Compare commits
151 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 47cae50e68 | |||
| c644956576 | |||
| c329724f8c | |||
| 49b3a5aa90 | |||
| 879ce3f2d5 | |||
| 996f0bf732 | |||
| b289a3570a | |||
| 1b689edb43 | |||
| 95c5b70eb7 | |||
| a5b4504b0a | |||
| 995a61b7ea | |||
| 0adf4df094 | |||
| 8c877d64d6 | |||
| 9ae1f046c4 | |||
| 7dc776f98a | |||
| 9717bcbb17 | |||
| f401266d1b | |||
| 1878b50752 | |||
| 8bd7b69bf9 | |||
| cf2ede1040 | |||
| af1bf57f24 | |||
| 0de6a0f1ca | |||
| 978cbc4f00 | |||
| ebb06d4beb | |||
| 03974f9cb3 | |||
| d322f5e91b | |||
| 0dee6d9db7 | |||
| 05bd6d6a9a | |||
| c43dbf6f4d | |||
| 848ace1e0b | |||
| f98d9d89bc | |||
| b9015c1321 | |||
| 59b0fe2f94 | |||
| bf98c1b369 | |||
| d7e3b2c6f2 | |||
| 7302b64be7 | |||
| d5365a7602 | |||
| 524d563077 | |||
| e44ddc419c | |||
| be20e17ebb | |||
| 7b76beab76 | |||
| 9ed013b418 | |||
| 33ae43b86d | |||
| 2c1ad1388d | |||
| 136666d759 | |||
| 4648967e93 | |||
| 824e980647 | |||
| ecbf5296a5 | |||
| f3454409f8 | |||
| 7d2e90b69f | |||
| bb2732bcc6 | |||
| d196993993 | |||
| b4fe8af890 | |||
| 1c8ab2395d | |||
| 065fe812ae | |||
| 02e75ea5cd | |||
| 2aa18fb77c | |||
| 8a0e7fb9d6 | |||
| 13c2ca4a78 | |||
| 6af84535fa | |||
| c8699cbe8d | |||
| 974163da97 | |||
| 7290e479db | |||
| c7b728318c | |||
| 5c6d31bcb5 | |||
| 3823292ba8 | |||
| 3eeda4a421 | |||
| ebb3f6eebb | |||
| 2c15d22e1e | |||
| f1dca2c9a8 | |||
| d039c25b55 | |||
| 16ef1c547b | |||
| e804b014a8 | |||
| e406a05694 | |||
| 057b3456a7 | |||
| 5ac124e159 | |||
| f29c6a0550 | |||
| 242a6d13af | |||
| dee2c50b50 | |||
| a394c9b59a | |||
| 17768bab0b | |||
| 7816b4c839 | |||
| e7ed48e55e | |||
| 63855f6ca4 | |||
| 2f53e40355 | |||
| bb9753cda6 | |||
| 95d0afdeb6 | |||
| fbe02fa7fb | |||
| 46e206e8f0 | |||
| 55bdcecffb | |||
| 2ee4b8fec6 | |||
| 67900956f8 | |||
| 29166c1d6a | |||
| 27d566dd47 | |||
| bfa0144594 | |||
| 8924f9642f | |||
| db24170752 | |||
| 58541defad | |||
| c513014913 | |||
| 5985ba5182 | |||
| fc90d5a389 | |||
| af1a83fe83 | |||
| e12b69e58f | |||
| d38614b15c | |||
| 627d12239e | |||
| 1af1370f23 | |||
| ade15d3c60 | |||
| 1241a81514 | |||
| 08a190c1cb | |||
| 124103d51b | |||
| 6154b0c24c | |||
| 81f36e8da7 | |||
| f230229ce9 | |||
| 912fb4ab38 | |||
| 1ab6bce821 | |||
| 70624e9062 | |||
| 2407285121 | |||
| db3171ea09 | |||
| 2aaaa0deb7 | |||
| d410277d14 | |||
| 99ceabb0b0 | |||
| 25df7bcd4d | |||
| 1cdca7bec3 | |||
| c809c7733d | |||
| 7b53003edb | |||
| 831d9d2bf8 | |||
| cb7c51ba12 | |||
| 0310f0a8a9 | |||
| bb79d08f6d | |||
| 414c86b500 | |||
| 4304ffcf3c | |||
| 309b23e18a | |||
| 52703583f0 | |||
| 6473ef13c6 | |||
| 6de7d060e3 | |||
| 9a45f15ba4 | |||
| 66b5eb13b0 | |||
| 4b37d4f050 | |||
| 746795b7ce | |||
| 8b81247044 | |||
| c6cd787950 | |||
| f9ab20b10f | |||
| acffd496ed | |||
| 466ac1a1e0 | |||
| d53adcd17e | |||
| 36e82e831f | |||
| cbe0115f01 | |||
| 37d501f16d | |||
| 1e76169178 | |||
| 7406eeff14 | |||
| bdabe31fc9 |
@@ -16,7 +16,7 @@ jobs:
|
||||
RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
|
||||
steps:
|
||||
- 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 squashfs-tools
|
||||
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 out repository code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
continue-on-error: ${{ matrix.rust == 'nightly' || matrix.rust == 'beta' || matrix.rust == 'stable' }}
|
||||
steps:
|
||||
- name: Install Dependencies (Linux)
|
||||
run: sudo apt-get update && sudo apt-get install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev squashfs-tools
|
||||
run: sudo apt-get update && sudo apt-get install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
|
||||
- name: Check out repository code
|
||||
@@ -107,6 +107,12 @@ jobs:
|
||||
command: build
|
||||
args: --workspace --features=coconut
|
||||
|
||||
- name: Reclaim some disk space (because Windows is being annoying)
|
||||
uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.os == 'windows-latest' }}
|
||||
with:
|
||||
command: clean
|
||||
|
||||
- name: Run all tests with coconut enabled
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
|
||||
@@ -166,8 +166,8 @@ jobs:
|
||||
GIT_BRANCH: "${GITHUB_REF##*/}"
|
||||
KEYBASE_NYMBOT_USERNAME: "${{ secrets.KEYBASE_NYMBOT_USERNAME }}"
|
||||
KEYBASE_NYMBOT_PAPERKEY: "${{ secrets.KEYBASE_NYMBOT_PAPERKEY }}"
|
||||
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBOT_TEAM }}"
|
||||
KEYBASE_NYM_CHANNEL: "ci-nightly"
|
||||
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMTECH_TEAM }}"
|
||||
KEYBASE_NYM_CHANNEL: "${{ secrets.KEYBASE_CHANNEL_DEV_CORE_ID }}"
|
||||
IS_SUCCESS: "${{ env.WORKFLOW_CONCLUSION == 'success' }}"
|
||||
uses: docker://keybaseio/client:stable-node
|
||||
with:
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
name: Nightly builds on dispatch
|
||||
|
||||
on: workflow_dispatch
|
||||
jobs:
|
||||
notification:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v2
|
||||
- name: Keybase - Node Install
|
||||
run: npm install
|
||||
working-directory: .github/workflows/support-files
|
||||
- name: Keybase - Send Notification
|
||||
env:
|
||||
NYM_NOTIFICATION_KIND: nightly
|
||||
NYM_PROJECT_NAME: "Notification test"
|
||||
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_PAPERKEY: "${{ secrets.KEYBASE_NYMBOT_PAPERKEY }}"
|
||||
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBOT_TEAM }}"
|
||||
KEYBASE_NYM_CHANNEL: "ci-nightly"
|
||||
uses: docker://keybaseio/client:stable-node
|
||||
with:
|
||||
args: .github/workflows/support-files/notifications/entry_point.sh
|
||||
@@ -2,6 +2,12 @@ name: Publish Nym binaries
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
add_tokio_unstable:
|
||||
description: 'True to add RUSTFLAGS="--cfg tokio_unstable"'
|
||||
required: true
|
||||
default: false
|
||||
type: boolean
|
||||
release:
|
||||
types: [created]
|
||||
|
||||
@@ -25,6 +31,11 @@ jobs:
|
||||
with:
|
||||
script: |
|
||||
core.setFailed('Release tag did not start with nym-binaries-...')
|
||||
|
||||
- name: Sets env vars for tokio if set in manual dispatch inputs
|
||||
run: |
|
||||
echo 'RUSTFLAGS="--cfg tokio_unstable"' >> $GITHUB_ENV
|
||||
if: github.event_name == 'workflow_dispatch' && inputs.add_tokio_unstable == true
|
||||
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
|
||||
@@ -89,7 +89,7 @@ async function sendKeybaseMessage(messageBody) {
|
||||
});
|
||||
|
||||
const channel = {
|
||||
name: 'nymtech_bot',
|
||||
name: context.env.KEYBASE_NYMBOT_TEAM || 'nymtech_bot',
|
||||
membersType: 'team',
|
||||
topicName: context.keybase.channel,
|
||||
topic_type: 'CHAT',
|
||||
|
||||
@@ -8,16 +8,26 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
|
||||
- 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])
|
||||
|
||||
### Fixed
|
||||
|
||||
- validator-api, mixnode, gateway should now prefer values in config.toml over mainnet defaults ([#1645])
|
||||
|
||||
### 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])
|
||||
|
||||
[#1541]: https://github.com/nymtech/nym/pull/1541
|
||||
[#1558]: https://github.com/nymtech/nym/pull/1558
|
||||
[#1577]: https://github.com/nymtech/nym/pull/1577
|
||||
[#1585]: https://github.com/nymtech/nym/pull/1585
|
||||
[#1591]: https://github.com/nymtech/nym/pull/1591
|
||||
[#1640]: https://github.com/nymtech/nym/pull/1640
|
||||
[#1645]: https://github.com/nymtech/nym/pull/1645
|
||||
|
||||
|
||||
## [nym-binaries-1.0.2](https://github.com/nymtech/nym/tree/nym-binaries-1.0.2)
|
||||
|
||||
Generated
+738
-764
File diff suppressed because it is too large
Load Diff
+3
-1
@@ -40,6 +40,7 @@ members = [
|
||||
"common/crypto/dkg",
|
||||
"common/execute",
|
||||
"common/inclusion-probability",
|
||||
"common/ledger",
|
||||
"common/mixnode-common",
|
||||
"common/network-defaults",
|
||||
"common/nonexhaustive-delayqueue",
|
||||
@@ -62,6 +63,7 @@ members = [
|
||||
"common/topology",
|
||||
"common/types",
|
||||
"common/wasm-utils",
|
||||
"common/completions",
|
||||
"explorer-api",
|
||||
"gateway",
|
||||
"gateway/gateway-requests",
|
||||
@@ -85,4 +87,4 @@ default-members = [
|
||||
"explorer-api",
|
||||
]
|
||||
|
||||
exclude = ["explorer", "contracts", "tokenomics-py", "clients/webassembly", "nym-wallet", "nym-connect"]
|
||||
exclude = ["explorer", "contracts", "clients/webassembly", "nym-wallet", "nym-connect"]
|
||||
|
||||
@@ -2,10 +2,10 @@ test: clippy-all cargo-test wasm fmt
|
||||
test-all: test cargo-test-expensive
|
||||
no-clippy: build cargo-test wasm fmt
|
||||
happy: fmt clippy-happy test
|
||||
clippy-all: clippy-all-main clippy-all-contracts clippy-all-wallet clippy-all-connect
|
||||
clippy-all: clippy-main clippy-coconut clippy-all-contracts clippy-all-wallet clippy-all-connect
|
||||
clippy-happy: clippy-happy-main clippy-happy-contracts clippy-happy-wallet clippy-happy-connect
|
||||
cargo-test: test-main test-contracts test-wallet test-connect
|
||||
cargo-test-expensive: test-main-expensive test-contracts-expensive test-wallet-expensive test-connect-expensive
|
||||
cargo-test: test-main test-contracts test-wallet test-connect test-coconut
|
||||
cargo-test-expensive: test-main-expensive test-contracts-expensive test-wallet-expensive test-connect-expensive test-coconut-expensive
|
||||
build: build-contracts build-wallet build-main build-connect
|
||||
fmt: fmt-main fmt-contracts fmt-wallet fmt-connect
|
||||
|
||||
@@ -21,8 +21,15 @@ clippy-happy-wallet:
|
||||
clippy-happy-connect:
|
||||
cargo clippy --manifest-path nym-connect/Cargo.toml
|
||||
|
||||
clippy-all-main:
|
||||
cargo clippy --workspace --all-features -- -D warnings
|
||||
clippy-main:
|
||||
cargo clippy --workspace -- -D warnings
|
||||
|
||||
clippy-coconut:
|
||||
cargo clippy --workspace --features coconut -- -D warnings
|
||||
|
||||
clippy-wasm:
|
||||
cargo clippy --workspace --features wasm -- -D warnings
|
||||
|
||||
|
||||
clippy-all-contracts:
|
||||
cargo clippy --workspace --manifest-path contracts/Cargo.toml --all-features --target wasm32-unknown-unknown -- -D warnings
|
||||
@@ -34,10 +41,20 @@ clippy-all-connect:
|
||||
cargo clippy --workspace --manifest-path nym-connect/Cargo.toml --all-features -- -D warnings
|
||||
|
||||
test-main:
|
||||
cargo test --all-features --workspace
|
||||
cargo test --workspace
|
||||
|
||||
test-coconut:
|
||||
cargo test --workspace --features coconut
|
||||
|
||||
test-wasm:
|
||||
cargo test --workspace --features wasm
|
||||
|
||||
|
||||
test-main-expensive:
|
||||
cargo test --all-features --workspace -- --ignored
|
||||
cargo test --workspace -- --ignored
|
||||
|
||||
test-coconut-expensive:
|
||||
cargo test --workspace --features coconut -- --ignored
|
||||
|
||||
test-contracts:
|
||||
cargo test --manifest-path contracts/Cargo.toml --all-features
|
||||
|
||||
@@ -136,7 +136,7 @@ impl LoopCoverTrafficStream<OsRng> {
|
||||
let cover_message = generate_loop_cover_packet(
|
||||
&mut self.rng,
|
||||
topology_ref,
|
||||
&*self.ack_key,
|
||||
&self.ack_key,
|
||||
&self.our_full_destination,
|
||||
self.average_ack_delay,
|
||||
self.average_packet_delay,
|
||||
|
||||
@@ -228,7 +228,7 @@ where
|
||||
generate_loop_cover_packet(
|
||||
&mut self.rng,
|
||||
topology_ref,
|
||||
&*self.ack_key,
|
||||
&self.ack_key,
|
||||
&self.our_full_destination,
|
||||
self.config.average_ack_delay,
|
||||
self.config.average_packet_delay,
|
||||
|
||||
@@ -13,7 +13,7 @@ use std::time::Duration;
|
||||
use task::ShutdownListener;
|
||||
use tokio::sync::{RwLock, RwLockReadGuard};
|
||||
use tokio::task::JoinHandle;
|
||||
use topology::{nym_topology_from_bonds, NymTopology};
|
||||
use topology::{nym_topology_from_detailed, NymTopology};
|
||||
use url::Url;
|
||||
|
||||
// I'm extremely curious why compiler NEVER complained about lack of Debug here before
|
||||
@@ -266,8 +266,8 @@ impl TopologyRefresher {
|
||||
};
|
||||
|
||||
let mixnodes_count = mixnodes.len();
|
||||
let topology =
|
||||
nym_topology_from_bonds(mixnodes, gateways).filter_system_version(&self.client_version);
|
||||
let topology = nym_topology_from_detailed(mixnodes, gateways)
|
||||
.filter_system_version(&self.client_version);
|
||||
|
||||
if !self.check_layer_distribution(&topology, mixnodes_count) {
|
||||
warn!("The current filtered active topology has extremely skewed layer distribution. It cannot be used.");
|
||||
|
||||
@@ -121,16 +121,6 @@ impl<T: NymConfig> Config<T> {
|
||||
self.client.gateway_endpoint.gateway_id = id.into();
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
pub fn with_eth_private_key<S: Into<String>>(&mut self, eth_private_key: S) {
|
||||
self.client.eth_private_key = eth_private_key.into();
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
pub fn with_eth_endpoint<S: Into<String>>(&mut self, eth_endpoint: S) {
|
||||
self.client.eth_endpoint = eth_endpoint.into();
|
||||
}
|
||||
|
||||
pub fn set_custom_validator_apis(&mut self, validator_api_urls: Vec<Url>) {
|
||||
self.client.validator_api_urls = validator_api_urls;
|
||||
}
|
||||
@@ -209,16 +199,6 @@ impl<T: NymConfig> Config<T> {
|
||||
self.client.database_path.clone()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
pub fn get_eth_endpoint(&self) -> String {
|
||||
self.client.eth_endpoint.clone()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
pub fn get_eth_private_key(&self) -> String {
|
||||
self.client.eth_private_key.clone()
|
||||
}
|
||||
|
||||
// Debug getters
|
||||
pub fn get_average_packet_delay(&self) -> Duration {
|
||||
self.debug.average_packet_delay
|
||||
@@ -342,14 +322,6 @@ pub struct Client<T> {
|
||||
/// Path to the database containing bandwidth credentials of this client.
|
||||
database_path: PathBuf,
|
||||
|
||||
/// Ethereum private key.
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
eth_private_key: String,
|
||||
|
||||
/// Address to an Ethereum full node.
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
eth_endpoint: String,
|
||||
|
||||
/// nym_home_directory specifies absolute path to the home nym Clients directory.
|
||||
/// It is expected to use default value and hence .toml file should not redefine this field.
|
||||
nym_root_directory: PathBuf,
|
||||
@@ -375,10 +347,6 @@ impl<T: NymConfig> Default for Client<T> {
|
||||
reply_encryption_key_store_path: Default::default(),
|
||||
gateway_endpoint: Default::default(),
|
||||
database_path: Default::default(),
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
eth_private_key: "".to_string(),
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
eth_endpoint: "".to_string(),
|
||||
nym_root_directory: T::default_root_directory(),
|
||||
super_struct: Default::default(),
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ edition = "2021"
|
||||
async-trait = "0.1.52"
|
||||
bip39 = "1.0.1"
|
||||
cfg-if = "0.1"
|
||||
clap = { version = "3.0.10", features = ["cargo", "derive"] }
|
||||
clap = { version = "3.2", features = ["cargo", "derive"] }
|
||||
pickledb = "0.4.1"
|
||||
rand = "0.7.3"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
@@ -19,6 +19,7 @@ tokio = { version = "1.19.1", features = ["rt-multi-thread", "net", "signal", "m
|
||||
|
||||
coconut-interface = { path = "../../common/coconut-interface" }
|
||||
config = { path = "../../common/config" }
|
||||
completions = { path = "../../common/completions" }
|
||||
credentials = { path = "../../common/credentials" }
|
||||
credential-storage = { path = "../../common/credential-storage" }
|
||||
crypto = { path = "../../common/crypto", features = ["rand", "asymmetric", "symmetric", "aes", "hashing"] }
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
use async_trait::async_trait;
|
||||
use clap::{Args, Subcommand};
|
||||
use completions::ArgShell;
|
||||
use pickledb::PickleDb;
|
||||
use rand::rngs::OsRng;
|
||||
use std::str::FromStr;
|
||||
@@ -28,6 +29,12 @@ pub(crate) enum Commands {
|
||||
ListDeposits(ListDeposits),
|
||||
/// Get a credential for a given deposit
|
||||
GetCredential(GetCredential),
|
||||
|
||||
/// Generate shell completions
|
||||
Completions(ArgShell),
|
||||
|
||||
/// Generate Fig specification
|
||||
GenerateFigSpec,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
||||
@@ -12,6 +12,8 @@ cfg_if::cfg_if! {
|
||||
use commands::{Commands, Execute};
|
||||
use error::Result;
|
||||
use network_defaults::setup_env;
|
||||
use clap::CommandFactory;
|
||||
use completions::fig_generate;
|
||||
|
||||
use clap::Parser;
|
||||
use pickledb::{PickleDb, PickleDbDumpPolicy, SerializationMethod};
|
||||
@@ -52,10 +54,14 @@ cfg_if::cfg_if! {
|
||||
),
|
||||
};
|
||||
|
||||
let bin_name = "nym-credential-client";
|
||||
|
||||
match &args.command {
|
||||
Commands::Deposit(m) => m.execute(&mut db, shared_storage).await?,
|
||||
Commands::ListDeposits(m) => m.execute(&mut db, shared_storage).await?,
|
||||
Commands::GetCredential(m) => m.execute(&mut db, shared_storage).await?,
|
||||
Commands::Completions(s) => s.generate(&mut crate::Cli::into_app(), bin_name),
|
||||
Commands::GenerateFigSpec => fig_generate(&mut crate::Cli::into_app(), bin_name)
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -20,7 +20,7 @@ futures = "0.3" # bunch of futures stuff, however, now that I think about it, it
|
||||
# and the single instance of abortable we have should really be refactored anyway
|
||||
url = "2.2"
|
||||
|
||||
clap = { version = "3.2.8", features = ["cargo", "derive"] }
|
||||
clap = { version = "3.2", features = ["cargo", "derive"] }
|
||||
dirs = "4.0"
|
||||
log = "0.4" # self explanatory
|
||||
pretty_env_logger = "0.4" # for formatting log messages
|
||||
@@ -34,6 +34,7 @@ tokio-tungstenite = "0.14" # websocket
|
||||
client-core = { path = "../client-core" }
|
||||
coconut-interface = { path = "../../common/coconut-interface", optional = true }
|
||||
config = { path = "../../common/config" }
|
||||
completions = { path = "../../common/completions" }
|
||||
credential-storage = { path = "../../common/credential-storage" }
|
||||
credentials = { path = "../../common/credentials", optional = true }
|
||||
crypto = { path = "../../common/crypto" }
|
||||
@@ -50,7 +51,6 @@ websocket-requests = { path = "websocket-requests" }
|
||||
|
||||
[features]
|
||||
coconut = ["coconut-interface", "credentials", "credentials/coconut", "gateway-requests/coconut", "gateway-client/coconut", "client-core/coconut"]
|
||||
eth = []
|
||||
|
||||
[dev-dependencies]
|
||||
serde_json = "1.0" # for the "textsend" example
|
||||
|
||||
+103
-21
@@ -27,6 +27,58 @@
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/gen-mapping": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
|
||||
"integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
|
||||
"dependencies": {
|
||||
"@jridgewell/set-array": "^1.0.1",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/resolve-uri": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
|
||||
"integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/set-array": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
|
||||
"integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/source-map": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz",
|
||||
"integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==",
|
||||
"dependencies": {
|
||||
"@jridgewell/gen-mapping": "^0.3.0",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/sourcemap-codec": {
|
||||
"version": "1.4.14",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
|
||||
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw=="
|
||||
},
|
||||
"node_modules/@jridgewell/trace-mapping": {
|
||||
"version": "0.3.15",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz",
|
||||
"integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==",
|
||||
"dependencies": {
|
||||
"@jridgewell/resolve-uri": "^3.0.3",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
@@ -3529,13 +3581,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/terser": {
|
||||
"version": "5.12.1",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.12.1.tgz",
|
||||
"integrity": "sha512-NXbs+7nisos5E+yXwAD+y7zrcTkMqb0dEJxIGtSKPdCBzopf7ni4odPul2aechpV7EXNvOudYOX2bb5tln1jbQ==",
|
||||
"version": "5.15.0",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.15.0.tgz",
|
||||
"integrity": "sha512-L1BJiXVmheAQQy+as0oF3Pwtlo4s3Wi1X2zNZ2NxOB4wx9bdS9Vk67XQENLFdLYGCK/Z2di53mTj/hBafR+dTA==",
|
||||
"dependencies": {
|
||||
"@jridgewell/source-map": "^0.3.2",
|
||||
"acorn": "^8.5.0",
|
||||
"commander": "^2.20.0",
|
||||
"source-map": "~0.7.2",
|
||||
"source-map-support": "~0.5.20"
|
||||
},
|
||||
"bin": {
|
||||
@@ -3583,14 +3635,6 @@
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
|
||||
},
|
||||
"node_modules/terser/node_modules/source-map": {
|
||||
"version": "0.7.3",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
|
||||
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/thunky": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
|
||||
@@ -4330,6 +4374,49 @@
|
||||
"integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==",
|
||||
"dev": true
|
||||
},
|
||||
"@jridgewell/gen-mapping": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
|
||||
"integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
|
||||
"requires": {
|
||||
"@jridgewell/set-array": "^1.0.1",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
}
|
||||
},
|
||||
"@jridgewell/resolve-uri": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
|
||||
"integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w=="
|
||||
},
|
||||
"@jridgewell/set-array": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
|
||||
"integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw=="
|
||||
},
|
||||
"@jridgewell/source-map": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz",
|
||||
"integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==",
|
||||
"requires": {
|
||||
"@jridgewell/gen-mapping": "^0.3.0",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
}
|
||||
},
|
||||
"@jridgewell/sourcemap-codec": {
|
||||
"version": "1.4.14",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
|
||||
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw=="
|
||||
},
|
||||
"@jridgewell/trace-mapping": {
|
||||
"version": "0.3.15",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz",
|
||||
"integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==",
|
||||
"requires": {
|
||||
"@jridgewell/resolve-uri": "^3.0.3",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||
}
|
||||
},
|
||||
"@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
@@ -7058,13 +7145,13 @@
|
||||
"integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ=="
|
||||
},
|
||||
"terser": {
|
||||
"version": "5.12.1",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.12.1.tgz",
|
||||
"integrity": "sha512-NXbs+7nisos5E+yXwAD+y7zrcTkMqb0dEJxIGtSKPdCBzopf7ni4odPul2aechpV7EXNvOudYOX2bb5tln1jbQ==",
|
||||
"version": "5.15.0",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.15.0.tgz",
|
||||
"integrity": "sha512-L1BJiXVmheAQQy+as0oF3Pwtlo4s3Wi1X2zNZ2NxOB4wx9bdS9Vk67XQENLFdLYGCK/Z2di53mTj/hBafR+dTA==",
|
||||
"requires": {
|
||||
"@jridgewell/source-map": "^0.3.2",
|
||||
"acorn": "^8.5.0",
|
||||
"commander": "^2.20.0",
|
||||
"source-map": "~0.7.2",
|
||||
"source-map-support": "~0.5.20"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -7072,11 +7159,6 @@
|
||||
"version": "2.20.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.7.3",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
|
||||
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -49,12 +49,6 @@ reply_encryption_key_store_path = '{{ client.reply_encryption_key_store_path }}'
|
||||
# Path to the database containing bandwidth credentials
|
||||
database_path = '{{ client.database_path }}'
|
||||
|
||||
# Ethereum private key.
|
||||
eth_private_key = '{{ client.eth_private_key }}'
|
||||
|
||||
# Addess to an Ethereum full node.
|
||||
eth_endpoint = '{{ client.eth_endpoint }}'
|
||||
|
||||
##### additional client config options #####
|
||||
|
||||
# A gateway specific, optional, base58 stringified shared key used for
|
||||
|
||||
@@ -190,8 +190,6 @@ impl NymClient {
|
||||
let bandwidth_controller = BandwidthController::new(
|
||||
credential_storage::initialise_storage(self.config.get_base().get_database_path())
|
||||
.await,
|
||||
self.config.get_base().get_eth_endpoint(),
|
||||
self.config.get_base().get_eth_private_key(),
|
||||
)
|
||||
.expect("Could not create bandwidth controller");
|
||||
|
||||
|
||||
@@ -10,9 +10,6 @@ use crate::{
|
||||
commands::{override_config, OverrideConfig},
|
||||
};
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
use crate::commands::{DEFAULT_ETH_ENDPOINT, DEFAULT_ETH_PRIVATE_KEY};
|
||||
|
||||
#[derive(Args, Clone)]
|
||||
pub(crate) struct Init {
|
||||
/// Id of the nym-mixnet-client we want to create config for.
|
||||
@@ -47,27 +44,9 @@ pub(crate) struct Init {
|
||||
|
||||
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
|
||||
/// with bandwidth credential requirement.
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
#[cfg(feature = "coconut")]
|
||||
#[clap(long)]
|
||||
enabled_credentials_mode: bool,
|
||||
|
||||
/// URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20
|
||||
/// tokens. If you don't want to set this value, use --enabled-credentials-mode instead
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[clap(
|
||||
long,
|
||||
default_value_if("enabled-credentials-mode", None, Some(DEFAULT_ETH_ENDPOINT))
|
||||
)]
|
||||
eth_endpoint: String,
|
||||
|
||||
/// Ethereum private key used for obtaining bandwidth tokens from ERC20 tokens. If you don't
|
||||
/// want to set this value, use --enabled-credentials-mode instead")
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[clap(
|
||||
long,
|
||||
default_value_if("enabled-credentials-mode", None, Some(DEFAULT_ETH_PRIVATE_KEY))
|
||||
)]
|
||||
eth_private_key: String,
|
||||
}
|
||||
|
||||
impl From<Init> for OverrideConfig {
|
||||
@@ -78,14 +57,8 @@ impl From<Init> for OverrideConfig {
|
||||
port: init_config.port,
|
||||
fastmode: init_config.fastmode,
|
||||
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
#[cfg(feature = "coconut")]
|
||||
enabled_credentials_mode: init_config.enabled_credentials_mode,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
eth_private_key: Some(init_config.eth_private_key),
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
eth_endpoint: Some(init_config.eth_endpoint),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,14 +2,9 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::client::config::{Config, SocketType};
|
||||
use clap::CommandFactory;
|
||||
use clap::{Parser, Subcommand};
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
pub(crate) const DEFAULT_ETH_ENDPOINT: &str =
|
||||
"https://rinkeby.infura.io/v3/00000000000000000000000000000000";
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
pub(crate) const DEFAULT_ETH_PRIVATE_KEY: &str =
|
||||
"0000000000000000000000000000000000000000000000000000000000000001";
|
||||
use completions::{fig_generate, ArgShell};
|
||||
|
||||
pub(crate) mod init;
|
||||
pub(crate) mod run;
|
||||
@@ -69,6 +64,12 @@ pub(crate) enum Commands {
|
||||
Run(run::Run),
|
||||
/// Try to upgrade the client
|
||||
Upgrade(upgrade::Upgrade),
|
||||
|
||||
/// Generate shell completions
|
||||
Completions(ArgShell),
|
||||
|
||||
/// Generate Fig specification
|
||||
GenerateFigSpec,
|
||||
}
|
||||
|
||||
// Configuration that can be overridden.
|
||||
@@ -78,21 +79,19 @@ pub(crate) struct OverrideConfig {
|
||||
port: Option<u16>,
|
||||
fastmode: bool,
|
||||
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
#[cfg(feature = "coconut")]
|
||||
enabled_credentials_mode: bool,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
eth_private_key: Option<String>,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
eth_endpoint: Option<String>,
|
||||
}
|
||||
|
||||
pub(crate) async fn execute(args: &Cli) {
|
||||
let bin_name = "nym-native-client";
|
||||
|
||||
match &args.command {
|
||||
Commands::Init(m) => init::execute(m).await,
|
||||
Commands::Run(m) => run::execute(m).await,
|
||||
Commands::Upgrade(m) => upgrade::execute(m),
|
||||
Commands::Completions(s) => s.generate(&mut Cli::into_app(), bin_name),
|
||||
Commands::GenerateFigSpec => fig_generate(&mut Cli::into_app(), bin_name),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,32 +116,13 @@ pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Confi
|
||||
config = config.with_port(port);
|
||||
}
|
||||
|
||||
#[cfg(all(not(feature = "eth"), not(feature = "coconut")))]
|
||||
{
|
||||
config
|
||||
.get_base_mut()
|
||||
.with_eth_endpoint(DEFAULT_ETH_ENDPOINT.to_string());
|
||||
config
|
||||
.get_base_mut()
|
||||
.with_eth_private_key(DEFAULT_ETH_PRIVATE_KEY.to_string());
|
||||
}
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
#[cfg(feature = "coconut")]
|
||||
{
|
||||
if args.enabled_credentials_mode {
|
||||
config.get_base_mut().with_disabled_credentials(false)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
{
|
||||
if let Some(eth_endpoint) = args.eth_endpoint {
|
||||
config.get_base_mut().with_eth_endpoint(eth_endpoint);
|
||||
}
|
||||
if let Some(eth_private_key) = args.eth_private_key {
|
||||
config.get_base_mut().with_eth_private_key(eth_private_key);
|
||||
}
|
||||
}
|
||||
|
||||
if args.fastmode {
|
||||
config.get_base_mut().set_high_default_traffic_volume();
|
||||
}
|
||||
|
||||
@@ -36,21 +36,9 @@ pub(crate) struct Run {
|
||||
|
||||
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
|
||||
/// with bandwidth credential requirement.
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
#[cfg(feature = "coconut")]
|
||||
#[clap(long)]
|
||||
enabled_credentials_mode: bool,
|
||||
|
||||
/// URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20
|
||||
/// tokens. If you don't want to set this value, use --enabled-credentials-mode instead
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[clap(long)]
|
||||
eth_endpoint: Option<String>,
|
||||
|
||||
/// Ethereum private key used for obtaining bandwidth tokens from ERC20 tokens. If you don't
|
||||
/// want to set this value, use --enabled-credentials-mode instead")
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[clap(long)]
|
||||
eth_private_key: Option<String>,
|
||||
}
|
||||
|
||||
impl From<Run> for OverrideConfig {
|
||||
@@ -60,15 +48,8 @@ impl From<Run> for OverrideConfig {
|
||||
disable_socket: run_config.disable_socket,
|
||||
port: run_config.port,
|
||||
fastmode: false,
|
||||
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
#[cfg(feature = "coconut")]
|
||||
enabled_credentials_mode: run_config.enabled_credentials_mode,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
eth_private_key: run_config.eth_private_key,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
eth_endpoint: run_config.eth_endpoint,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ fn minor_0_12_upgrade(
|
||||
Version::new(0, 12, 0)
|
||||
};
|
||||
|
||||
print_start_upgrade(&config_version, &to_version);
|
||||
print_start_upgrade(config_version, &to_version);
|
||||
|
||||
config
|
||||
.get_base_mut()
|
||||
@@ -111,7 +111,7 @@ fn minor_0_12_upgrade(
|
||||
|
||||
config.save_to_file(None).unwrap_or_else(|err| {
|
||||
eprintln!("failed to overwrite config file! - {:?}", err);
|
||||
print_failed_upgrade(&config_version, &to_version);
|
||||
print_failed_upgrade(config_version, &to_version);
|
||||
process::exit(1);
|
||||
});
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ name = "nym_socks5"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "3.2.8", features = ["cargo", "derive"] }
|
||||
clap = { version = "3.2", features = ["cargo", "derive"] }
|
||||
dirs = "4.0"
|
||||
futures = "0.3"
|
||||
log = "0.4"
|
||||
@@ -27,6 +27,7 @@ url = "2.2"
|
||||
client-core = { path = "../client-core" }
|
||||
coconut-interface = { path = "../../common/coconut-interface", optional = true }
|
||||
config = { path = "../../common/config" }
|
||||
completions = { path = "../../common/completions" }
|
||||
credential-storage = { path = "../../common/credential-storage" }
|
||||
credentials = { path = "../../common/credentials", optional = true }
|
||||
crypto = { path = "../../common/crypto" }
|
||||
|
||||
@@ -49,12 +49,6 @@ reply_encryption_key_store_path = '{{ client.reply_encryption_key_store_path }}'
|
||||
# Path to the database containing bandwidth credentials
|
||||
database_path = '{{ client.database_path }}'
|
||||
|
||||
# Ethereum private key.
|
||||
eth_private_key = '{{ client.eth_private_key }}'
|
||||
|
||||
# Addess to an Ethereum full node.
|
||||
eth_endpoint = '{{ client.eth_endpoint }}'
|
||||
|
||||
##### additional client config options #####
|
||||
|
||||
# A gateway specific, optional, base58 stringified shared key used for
|
||||
|
||||
@@ -191,8 +191,6 @@ impl NymClient {
|
||||
let bandwidth_controller = BandwidthController::new(
|
||||
credential_storage::initialise_storage(self.config.get_base().get_database_path())
|
||||
.await,
|
||||
self.config.get_base().get_eth_endpoint(),
|
||||
self.config.get_base().get_eth_private_key(),
|
||||
)
|
||||
.expect("Could not create bandwidth controller");
|
||||
|
||||
|
||||
@@ -10,9 +10,6 @@ use crate::{
|
||||
commands::{override_config, OverrideConfig},
|
||||
};
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
use crate::commands::{DEFAULT_ETH_ENDPOINT, DEFAULT_ETH_PRIVATE_KEY};
|
||||
|
||||
#[derive(Args, Clone)]
|
||||
pub(crate) struct Init {
|
||||
/// Id of the nym-mixnet-client we want to create config for.
|
||||
@@ -47,27 +44,9 @@ pub(crate) struct Init {
|
||||
|
||||
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
|
||||
/// with bandwidth credential requirement.
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
#[cfg(feature = "coconut")]
|
||||
#[clap(long)]
|
||||
enabled_credentials_mode: bool,
|
||||
|
||||
/// URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20
|
||||
/// tokens. If you don't want to set this value, use --enabled-credentials-mode instead
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[clap(
|
||||
long,
|
||||
default_value_if("enabled-credentials-mode", None, Some(DEFAULT_ETH_ENDPOINT))
|
||||
)]
|
||||
eth_endpoint: String,
|
||||
|
||||
/// Ethereum private key used for obtaining bandwidth tokens from ERC20 tokens. If you don't
|
||||
/// want to set this value, use --enabled-credentials-mode instead")
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[clap(
|
||||
long,
|
||||
default_value_if("enabled-credentials-mode", None, Some(DEFAULT_ETH_PRIVATE_KEY))
|
||||
)]
|
||||
eth_private_key: String,
|
||||
}
|
||||
|
||||
impl From<Init> for OverrideConfig {
|
||||
@@ -76,15 +55,8 @@ impl From<Init> for OverrideConfig {
|
||||
validators: init_config.validators,
|
||||
port: init_config.port,
|
||||
fastmode: init_config.fastmode,
|
||||
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
#[cfg(feature = "coconut")]
|
||||
enabled_credentials_mode: init_config.enabled_credentials_mode,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
eth_private_key: Some(init_config.eth_private_key),
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
eth_endpoint: Some(init_config.eth_endpoint),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,20 +2,15 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::client::config::Config;
|
||||
use clap::CommandFactory;
|
||||
use clap::{Parser, Subcommand};
|
||||
use completions::{fig_generate, ArgShell};
|
||||
use config::parse_validators;
|
||||
|
||||
pub mod init;
|
||||
pub(crate) mod run;
|
||||
pub(crate) mod upgrade;
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
pub(crate) const DEFAULT_ETH_ENDPOINT: &str =
|
||||
"https://rinkeby.infura.io/v3/00000000000000000000000000000000";
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
pub(crate) const DEFAULT_ETH_PRIVATE_KEY: &str =
|
||||
"0000000000000000000000000000000000000000000000000000000000000001";
|
||||
|
||||
fn long_version() -> String {
|
||||
format!(
|
||||
r#"
|
||||
@@ -70,6 +65,12 @@ pub(crate) enum Commands {
|
||||
Run(run::Run),
|
||||
/// Try to upgrade the client
|
||||
Upgrade(upgrade::Upgrade),
|
||||
|
||||
/// Generate shell completions
|
||||
Completions(ArgShell),
|
||||
|
||||
/// Generate Fig specification
|
||||
GenerateFigSpec,
|
||||
}
|
||||
|
||||
// Configuration that can be overridden.
|
||||
@@ -78,21 +79,19 @@ pub(crate) struct OverrideConfig {
|
||||
port: Option<u16>,
|
||||
fastmode: bool,
|
||||
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
#[cfg(feature = "coconut")]
|
||||
enabled_credentials_mode: bool,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
eth_private_key: Option<String>,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
eth_endpoint: Option<String>,
|
||||
}
|
||||
|
||||
pub(crate) async fn execute(args: &Cli) {
|
||||
let bin_name = "nym-socks5-client";
|
||||
|
||||
match &args.command {
|
||||
Commands::Init(m) => init::execute(m).await,
|
||||
Commands::Run(m) => run::execute(m).await,
|
||||
Commands::Upgrade(m) => upgrade::execute(m),
|
||||
Commands::Completions(s) => s.generate(&mut Cli::into_app(), bin_name),
|
||||
Commands::GenerateFigSpec => fig_generate(&mut Cli::into_app(), bin_name),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,31 +110,12 @@ pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Confi
|
||||
config = config.with_port(port);
|
||||
}
|
||||
|
||||
#[cfg(all(not(feature = "eth"), not(feature = "coconut")))]
|
||||
{
|
||||
config
|
||||
.get_base_mut()
|
||||
.with_eth_endpoint(DEFAULT_ETH_ENDPOINT.to_string());
|
||||
config
|
||||
.get_base_mut()
|
||||
.with_eth_private_key(DEFAULT_ETH_PRIVATE_KEY.to_string());
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
#[cfg(feature = "coconut")]
|
||||
{
|
||||
if args.enabled_credentials_mode {
|
||||
config.get_base_mut().with_disabled_credentials(false)
|
||||
}
|
||||
}
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
{
|
||||
if let Some(eth_endpoint) = args.eth_endpoint {
|
||||
config.get_base_mut().with_eth_endpoint(eth_endpoint);
|
||||
}
|
||||
if let Some(eth_private_key) = args.eth_private_key {
|
||||
config.get_base_mut().with_eth_private_key(eth_private_key);
|
||||
}
|
||||
}
|
||||
|
||||
if args.fastmode {
|
||||
config.get_base_mut().set_high_default_traffic_volume();
|
||||
|
||||
@@ -40,21 +40,9 @@ pub(crate) struct Run {
|
||||
|
||||
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
|
||||
/// with bandwidth credential requirement.
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
#[cfg(feature = "coconut")]
|
||||
#[clap(long)]
|
||||
enabled_credentials_mode: bool,
|
||||
|
||||
/// URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20
|
||||
/// tokens. If you don't want to set this value, use --enabled-credentials-mode instead
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[clap(long)]
|
||||
eth_endpoint: Option<String>,
|
||||
|
||||
/// Ethereum private key used for obtaining bandwidth tokens from ERC20 tokens. If you don't
|
||||
/// want to set this value, use --enabled-credentials-mode instead
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[clap(long)]
|
||||
eth_private_key: Option<String>,
|
||||
}
|
||||
|
||||
impl From<Run> for OverrideConfig {
|
||||
@@ -64,14 +52,8 @@ impl From<Run> for OverrideConfig {
|
||||
port: run_config.port,
|
||||
fastmode: false,
|
||||
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
#[cfg(feature = "coconut")]
|
||||
enabled_credentials_mode: run_config.enabled_credentials_mode,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
eth_private_key: run_config.eth_private_key,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
eth_endpoint: run_config.eth_endpoint,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ fn minor_0_12_upgrade(
|
||||
Version::new(0, 12, 0)
|
||||
};
|
||||
|
||||
print_start_upgrade(&config_version, &to_version);
|
||||
print_start_upgrade(config_version, &to_version);
|
||||
|
||||
config
|
||||
.get_base_mut()
|
||||
@@ -110,7 +110,7 @@ fn minor_0_12_upgrade(
|
||||
|
||||
config.save_to_file(None).unwrap_or_else(|err| {
|
||||
eprintln!("failed to overwrite config file! - {:?}", err);
|
||||
print_failed_upgrade(&config_version, &to_version);
|
||||
print_failed_upgrade(config_version, &to_version);
|
||||
process::exit(1);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "nym-client-wasm"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jedrzej Stuczynski <andrew@nymtech.net>"]
|
||||
version = "1.0.1"
|
||||
version = "1.0.0"
|
||||
edition = "2021"
|
||||
keywords = ["nym", "sphinx", "wasm", "webassembly", "privacy", "client"]
|
||||
license = "Apache-2.0"
|
||||
@@ -32,7 +32,7 @@ credentials = { path = "../../common/credentials", optional = true }
|
||||
crypto = { path = "../../common/crypto" }
|
||||
nymsphinx = { path = "../../common/nymsphinx" }
|
||||
topology = { path = "../../common/topology" }
|
||||
gateway-client = { path = "../../common/client-libs/gateway-client", default-features = false, features = ["wasm"] }
|
||||
gateway-client = { path = "../../common/client-libs/gateway-client", default-features = false, features = ["wasm", "coconut"] }
|
||||
validator-client = { path = "../../common/client-libs/validator-client", default-features = false }
|
||||
wasm-utils = { path = "../../common/wasm-utils" }
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ This client is part of the [Nym](https://nymtech.net/docs) project. It's written
|
||||
## Security Status
|
||||
|
||||
From a security point of view, this module is not yet complete. Key missing features include, but are not limited to: cover traffic, sending packets with delay according to Poisson distribution.
|
||||
|
||||
They should be implemented soon. You can build your applications, but don't rely on it for strong anonymity yet if your application needs cover traffic.
|
||||
|
||||
## Using it
|
||||
@@ -37,6 +38,6 @@ To be clear, this is not something that most JS developers need to worry about,
|
||||
|
||||
If you're a Nym platform developer who's made changes to the Rust (or JS) files and wants to re-publish the package to NPM, here's how you do it:
|
||||
|
||||
1. `wasm-pack build --scope nymproject` builds the wasm binaries into the `pkg` directory (not in source control)
|
||||
2. bump version numbers as necessary for SemVer
|
||||
3. `wasm-pack publish --access=public` will publish your changed package to NPM
|
||||
1. bump version numbers as necessary for SemVer
|
||||
2. `wasm-pack build --scope nymproject` builds the wasm binaries into the `pkg` directory (not in source control)
|
||||
3. `cd pkg && npm publish --access=public` will publish your changed package to NPM
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
// limitations under the License.
|
||||
|
||||
import {
|
||||
NymClient,
|
||||
set_panic_hook
|
||||
NymClient,
|
||||
set_panic_hook
|
||||
} from "@nymproject/nym-client-wasm"
|
||||
|
||||
// current limitation of rust-wasm for async stuff : (
|
||||
@@ -25,7 +25,7 @@ async function main() {
|
||||
set_panic_hook();
|
||||
|
||||
// validator server we will use to get topology from
|
||||
const validator = "https://sandbox-validator.nymtech.net/api"; //"http://localhost:8081";
|
||||
const validator = "https://validator.nymtech.net/api";
|
||||
|
||||
client = new NymClient(validator);
|
||||
|
||||
@@ -69,7 +69,7 @@ async function sendMessageTo() {
|
||||
* @param {string} message
|
||||
*/
|
||||
function displaySend(message) {
|
||||
let timestamp = new Date().toISOString().substr(11, 12);
|
||||
let timestamp = new Date().toISOString().slice(11, 21);
|
||||
|
||||
let sendDiv = document.createElement("div")
|
||||
let paragraph = document.createElement("p")
|
||||
@@ -90,11 +90,11 @@ function displayReceived(message) {
|
||||
const content = message.message
|
||||
const replySurb = message.replySurb
|
||||
|
||||
let timestamp = new Date().toISOString().substr(11, 12);
|
||||
let timestamp = new Date().toISOString().slice(11, 21);
|
||||
let receivedDiv = document.createElement("div")
|
||||
let paragraph = document.createElement("p")
|
||||
paragraph.setAttribute('style', 'color: green')
|
||||
let paragraphContent = document.createTextNode(timestamp + " received >>> " + content + ((replySurb != null) ? "Reply SURB was attached here (but we can't do anything with it yet" : " (NO REPLY-SURB AVAILABLE)"))
|
||||
let paragraphContent = document.createTextNode(timestamp + " received >>> " + content)
|
||||
paragraph.appendChild(paragraphContent)
|
||||
receivedDiv.appendChild(paragraph)
|
||||
document.getElementById("output").appendChild(receivedDiv)
|
||||
|
||||
+115
-25
@@ -36,6 +36,64 @@
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/gen-mapping": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
|
||||
"integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/set-array": "^1.0.1",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/resolve-uri": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
|
||||
"integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/set-array": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
|
||||
"integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/source-map": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz",
|
||||
"integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/gen-mapping": "^0.3.0",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/sourcemap-codec": {
|
||||
"version": "1.4.14",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
|
||||
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@jridgewell/trace-mapping": {
|
||||
"version": "0.3.15",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz",
|
||||
"integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/resolve-uri": "^3.0.3",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
@@ -3272,14 +3330,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/terser": {
|
||||
"version": "5.12.1",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.12.1.tgz",
|
||||
"integrity": "sha512-NXbs+7nisos5E+yXwAD+y7zrcTkMqb0dEJxIGtSKPdCBzopf7ni4odPul2aechpV7EXNvOudYOX2bb5tln1jbQ==",
|
||||
"version": "5.15.0",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.15.0.tgz",
|
||||
"integrity": "sha512-L1BJiXVmheAQQy+as0oF3Pwtlo4s3Wi1X2zNZ2NxOB4wx9bdS9Vk67XQENLFdLYGCK/Z2di53mTj/hBafR+dTA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/source-map": "^0.3.2",
|
||||
"acorn": "^8.5.0",
|
||||
"commander": "^2.20.0",
|
||||
"source-map": "~0.7.2",
|
||||
"source-map-support": "~0.5.20"
|
||||
},
|
||||
"bin": {
|
||||
@@ -3372,15 +3430,6 @@
|
||||
"url": "https://opencollective.com/webpack"
|
||||
}
|
||||
},
|
||||
"node_modules/terser/node_modules/source-map": {
|
||||
"version": "0.7.3",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
|
||||
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/thunky": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
|
||||
@@ -3828,6 +3877,55 @@
|
||||
"integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==",
|
||||
"dev": true
|
||||
},
|
||||
"@jridgewell/gen-mapping": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
|
||||
"integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jridgewell/set-array": "^1.0.1",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
}
|
||||
},
|
||||
"@jridgewell/resolve-uri": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
|
||||
"integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
|
||||
"dev": true
|
||||
},
|
||||
"@jridgewell/set-array": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
|
||||
"integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
|
||||
"dev": true
|
||||
},
|
||||
"@jridgewell/source-map": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz",
|
||||
"integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jridgewell/gen-mapping": "^0.3.0",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
}
|
||||
},
|
||||
"@jridgewell/sourcemap-codec": {
|
||||
"version": "1.4.14",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
|
||||
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
|
||||
"dev": true
|
||||
},
|
||||
"@jridgewell/trace-mapping": {
|
||||
"version": "0.3.15",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz",
|
||||
"integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jridgewell/resolve-uri": "^3.0.3",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||
}
|
||||
},
|
||||
"@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
@@ -6334,23 +6432,15 @@
|
||||
"dev": true
|
||||
},
|
||||
"terser": {
|
||||
"version": "5.12.1",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.12.1.tgz",
|
||||
"integrity": "sha512-NXbs+7nisos5E+yXwAD+y7zrcTkMqb0dEJxIGtSKPdCBzopf7ni4odPul2aechpV7EXNvOudYOX2bb5tln1jbQ==",
|
||||
"version": "5.15.0",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.15.0.tgz",
|
||||
"integrity": "sha512-L1BJiXVmheAQQy+as0oF3Pwtlo4s3Wi1X2zNZ2NxOB4wx9bdS9Vk67XQENLFdLYGCK/Z2di53mTj/hBafR+dTA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jridgewell/source-map": "^0.3.2",
|
||||
"acorn": "^8.5.0",
|
||||
"commander": "^2.20.0",
|
||||
"source-map": "~0.7.2",
|
||||
"source-map-support": "~0.5.20"
|
||||
},
|
||||
"dependencies": {
|
||||
"source-map": {
|
||||
"version": "0.7.3",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
|
||||
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"terser-webpack-plugin": {
|
||||
|
||||
@@ -34,6 +34,6 @@
|
||||
"webpack-dev-server": "^4.7.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nymproject/nym-client-wasm": "file:../pkg"
|
||||
"@nymproject/nym-client-wasm": "1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ module.exports = {
|
||||
},
|
||||
mode: "development",
|
||||
plugins: [
|
||||
new CopyWebpackPlugin({patterns: ['index.html']})
|
||||
new CopyWebpackPlugin({ patterns: ['index.html'] })
|
||||
],
|
||||
experiments: { asyncWebAssembly: true }
|
||||
experiments: { syncWebAssembly: true }
|
||||
};
|
||||
|
||||
@@ -11,7 +11,7 @@ use rand::rngs::OsRng;
|
||||
use received_processor::ReceivedMessagesProcessor;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use topology::{gateway, nym_topology_from_bonds, NymTopology};
|
||||
use topology::{gateway, nym_topology_from_detailed, NymTopology};
|
||||
use url::Url;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen_futures::spawn_local;
|
||||
@@ -265,7 +265,7 @@ impl NymClient {
|
||||
Ok(gateways) => gateways,
|
||||
};
|
||||
|
||||
let topology = nym_topology_from_bonds(mixnodes, gateways);
|
||||
let topology = nym_topology_from_detailed(mixnodes, gateways);
|
||||
let version = env!("CARGO_PKG_VERSION");
|
||||
topology.filter_system_version(version)
|
||||
}
|
||||
|
||||
@@ -10,13 +10,12 @@ edition = "2021"
|
||||
# TODO: (for this and other crates), similarly to 'tokio', import only required "futures" modules rather than
|
||||
# the entire crate
|
||||
futures = "0.3"
|
||||
json = "0.12.4"
|
||||
log = "0.4"
|
||||
thiserror = "1.0"
|
||||
url = "2.2"
|
||||
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
|
||||
secp256k1 = "0.20.3"
|
||||
web3 = { version = "0.17.0", default-features = false }
|
||||
secp256k1 = { version = "0.20.3", optional = true }
|
||||
web3 = { version = "0.17.0", default-features = false, optional = true }
|
||||
async-trait = { version = "0.1.51" }
|
||||
|
||||
# internal
|
||||
@@ -80,5 +79,5 @@ features = ["js"]
|
||||
|
||||
[features]
|
||||
coconut = ["gateway-requests/coconut", "coconut-interface", "validator-client", "credentials/coconut"]
|
||||
wasm = ["web3/wasm", "web3/http", "web3/signing"]
|
||||
default = ["web3/default"]
|
||||
wasm = []
|
||||
default = ["web3/default", "secp256k1"]
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::GatewayClientError;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use crate::wasm_storage::Storage;
|
||||
@@ -15,6 +14,8 @@ use crate::wasm_storage::StorageError;
|
||||
#[cfg(all(not(target_arch = "wasm32"), feature = "coconut"))]
|
||||
use credential_storage::error::StorageError;
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
use std::str::FromStr;
|
||||
#[cfg(feature = "coconut")]
|
||||
use {
|
||||
coconut_interface::Base58,
|
||||
@@ -23,65 +24,12 @@ use {
|
||||
},
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
use {
|
||||
credentials::token::bandwidth::TokenCredential,
|
||||
crypto::asymmetric::identity,
|
||||
network_defaults::{
|
||||
eth_contract::ETH_ERC20_JSON_ABI, eth_contract::ETH_JSON_ABI, BANDWIDTH_VALUE,
|
||||
ETH_BURN_FUNCTION_NAME, ETH_CONTRACT_ADDRESS, ETH_ERC20_APPROVE_FUNCTION_NAME,
|
||||
ETH_ERC20_CONTRACT_ADDRESS, ETH_MIN_BLOCK_DEPTH, TOKENS_TO_BURN, UTOKENS_TO_BURN,
|
||||
},
|
||||
pemstore::traits::PemStorableKeyPair,
|
||||
rand::rngs::OsRng,
|
||||
secp256k1::SecretKey,
|
||||
web3::{
|
||||
contract::{Contract, Options},
|
||||
ethabi::Token,
|
||||
signing::{Key, SecretKeyRef},
|
||||
transports::Http,
|
||||
types::{Address, U256, U64},
|
||||
Web3,
|
||||
},
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
pub fn eth_contract(web3: Web3<Http>) -> Contract<Http> {
|
||||
Contract::from_json(
|
||||
web3.eth(),
|
||||
Address::from(ETH_CONTRACT_ADDRESS),
|
||||
json::parse(ETH_JSON_ABI)
|
||||
.expect("Invalid json abi")
|
||||
.dump()
|
||||
.as_bytes(),
|
||||
)
|
||||
.expect("Invalid json abi")
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
pub fn eth_erc20_contract(web3: Web3<Http>) -> Contract<Http> {
|
||||
Contract::from_json(
|
||||
web3.eth(),
|
||||
Address::from(ETH_ERC20_CONTRACT_ADDRESS),
|
||||
json::parse(ETH_ERC20_JSON_ABI)
|
||||
.expect("Invalid json abi")
|
||||
.dump()
|
||||
.as_bytes(),
|
||||
)
|
||||
.expect("Invalid json abi")
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct BandwidthController<St: Storage> {
|
||||
#[allow(dead_code)]
|
||||
storage: St,
|
||||
#[cfg(feature = "coconut")]
|
||||
validator_endpoints: Vec<url::Url>,
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
contract: Contract<Http>,
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
erc20_contract: Contract<Http>,
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
eth_private_key: SecretKey,
|
||||
}
|
||||
|
||||
impl<St> BandwidthController<St>
|
||||
@@ -97,60 +45,8 @@ where
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
pub fn new(
|
||||
storage: St,
|
||||
eth_endpoint: String,
|
||||
eth_private_key: String,
|
||||
) -> Result<Self, GatewayClientError> {
|
||||
// Fail early, on invalid url
|
||||
let transport =
|
||||
Http::new(ð_endpoint).map_err(|_| GatewayClientError::InvalidURL(eth_endpoint))?;
|
||||
let web3 = web3::Web3::new(transport);
|
||||
// Fail early, on invalid abi
|
||||
let contract = eth_contract(web3.clone());
|
||||
let erc20_contract = eth_erc20_contract(web3);
|
||||
let eth_private_key = secp256k1::SecretKey::from_str(ð_private_key)
|
||||
.map_err(|_| GatewayClientError::InvalidEthereumPrivateKey)?;
|
||||
|
||||
Ok(BandwidthController {
|
||||
storage,
|
||||
contract,
|
||||
erc20_contract,
|
||||
eth_private_key,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
async fn backup_keypair(&self, keypair: &identity::KeyPair) -> Result<(), GatewayClientError> {
|
||||
self.storage
|
||||
.insert_erc20_credential(
|
||||
keypair.public_key().to_base58_string(),
|
||||
keypair.private_key().to_base58_string(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
async fn restore_keypair(&self) -> Result<identity::KeyPair, GatewayClientError> {
|
||||
let data = self.storage.get_next_erc20_credential().await?;
|
||||
let public_key = identity::PublicKey::from_base58_string(data.public_key).unwrap();
|
||||
let private_key = identity::PrivateKey::from_base58_string(data.private_key).unwrap();
|
||||
|
||||
Ok(identity::KeyPair::from_keys(private_key, public_key))
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
async fn mark_keypair_as_spent(
|
||||
&self,
|
||||
keypair: &identity::KeyPair,
|
||||
) -> Result<(), GatewayClientError> {
|
||||
self.storage
|
||||
.consume_erc20_credential(keypair.public_key().to_base58_string())
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
pub fn new(storage: St) -> Result<Self, GatewayClientError> {
|
||||
Ok(BandwidthController { storage })
|
||||
}
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
@@ -179,206 +75,4 @@ where
|
||||
&verification_key,
|
||||
)?)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
pub async fn prepare_token_credential(
|
||||
&self,
|
||||
gateway_identity: identity::PublicKey,
|
||||
gateway_owner: String,
|
||||
) -> Result<TokenCredential, GatewayClientError> {
|
||||
let kp = match self.restore_keypair().await {
|
||||
Ok(kp) => kp,
|
||||
Err(_) => {
|
||||
let mut rng = OsRng;
|
||||
let kp = identity::KeyPair::new(&mut rng);
|
||||
self.backup_keypair(&kp).await?;
|
||||
kp
|
||||
}
|
||||
};
|
||||
|
||||
let verification_key = *kp.public_key();
|
||||
let signed_verification_key = kp.private_key().sign(&verification_key.to_bytes());
|
||||
self.buy_token_credential(verification_key, signed_verification_key, gateway_owner)
|
||||
.await?;
|
||||
|
||||
self.mark_keypair_as_spent(&kp).await?;
|
||||
|
||||
let message: Vec<u8> = verification_key
|
||||
.to_bytes()
|
||||
.iter()
|
||||
.chain(gateway_identity.to_bytes().iter())
|
||||
.copied()
|
||||
.collect();
|
||||
let signature = kp.private_key().sign(&message);
|
||||
|
||||
Ok(TokenCredential::new(
|
||||
verification_key,
|
||||
gateway_identity,
|
||||
BANDWIDTH_VALUE,
|
||||
signature,
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
pub async fn buy_token_credential(
|
||||
&self,
|
||||
verification_key: identity::PublicKey,
|
||||
signed_verification_key: identity::Signature,
|
||||
gateway_owner: String,
|
||||
) -> Result<(), GatewayClientError> {
|
||||
let confirmations = if cfg!(debug_assertions) {
|
||||
1
|
||||
} else {
|
||||
ETH_MIN_BLOCK_DEPTH
|
||||
};
|
||||
// 15 seconds per confirmation block + 10 seconds of network overhead + 20 seconds of wait for kill
|
||||
log::info!(
|
||||
"Waiting for Ethereum transaction. This should take about {} seconds",
|
||||
(confirmations + 1) * 15 + 30
|
||||
);
|
||||
let mut options = Options::default();
|
||||
let estimation = self
|
||||
.erc20_contract
|
||||
.estimate_gas(
|
||||
ETH_ERC20_APPROVE_FUNCTION_NAME,
|
||||
(
|
||||
Token::Address(Address::from(ETH_CONTRACT_ADDRESS)),
|
||||
Token::Uint(U256::from(UTOKENS_TO_BURN)),
|
||||
),
|
||||
SecretKeyRef::from(&self.eth_private_key).address(),
|
||||
options.clone(),
|
||||
)
|
||||
.await?;
|
||||
options.gas = Some(estimation);
|
||||
log::info!("Calling ERC20 approve in 10 seconds with an estimated gas of {}. Kill the process if you want to abort", estimation);
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
tokio::time::sleep(tokio::time::Duration::from_secs(10)).await;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
if let Err(err) = fluvio_wasm_timer::Delay::new(std::time::Duration::from_secs(10)).await {
|
||||
log::error!(
|
||||
"the timer has gone away while waiting for possible kill! - {}",
|
||||
err
|
||||
);
|
||||
}
|
||||
let recipt = self
|
||||
.erc20_contract
|
||||
.signed_call_with_confirmations(
|
||||
ETH_ERC20_APPROVE_FUNCTION_NAME,
|
||||
(
|
||||
Token::Address(Address::from(ETH_CONTRACT_ADDRESS)),
|
||||
Token::Uint(U256::from(UTOKENS_TO_BURN)),
|
||||
),
|
||||
options,
|
||||
1, // One confirmation is enough, as we'll be consuming the approved token next anyway
|
||||
&self.eth_private_key,
|
||||
)
|
||||
.await?;
|
||||
if Some(U64::from(0u64)) == recipt.status {
|
||||
return Err(GatewayClientError::BurnTokenError(
|
||||
web3::Error::InvalidResponse(format!(
|
||||
"Approve transaction status is 0 (failure): {:?}",
|
||||
recipt.logs,
|
||||
)),
|
||||
));
|
||||
} else {
|
||||
log::info!(
|
||||
"Approved {} tokens for bandwidth use on Ethereum",
|
||||
TOKENS_TO_BURN
|
||||
);
|
||||
}
|
||||
|
||||
let mut options = Options::default();
|
||||
let estimation = self
|
||||
.contract
|
||||
.estimate_gas(
|
||||
ETH_BURN_FUNCTION_NAME,
|
||||
(
|
||||
Token::Uint(U256::from(UTOKENS_TO_BURN)),
|
||||
Token::Uint(U256::from(&verification_key.to_bytes())),
|
||||
Token::Bytes(signed_verification_key.to_bytes().to_vec()),
|
||||
Token::String(gateway_owner.clone()),
|
||||
),
|
||||
SecretKeyRef::from(&self.eth_private_key).address(),
|
||||
options.clone(),
|
||||
)
|
||||
.await?;
|
||||
options.gas = Some(estimation);
|
||||
log::info!("Generating bandwidth on ETH contract in 10 seconds with an estimated gas of {}. \
|
||||
Kill the process if you want to abort. Keep in mind that if you abort now, you'll still have \
|
||||
some tokens approved for bandwidth spending from the previous action. \
|
||||
If you don't want that, you'll need to manually decreaseAllowance to revert the approval.", estimation);
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
tokio::time::sleep(tokio::time::Duration::from_secs(10)).await;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
if let Err(err) = fluvio_wasm_timer::Delay::new(std::time::Duration::from_secs(10)).await {
|
||||
log::error!(
|
||||
"the timer has gone away while waiting for possible kill! - {}",
|
||||
err
|
||||
);
|
||||
}
|
||||
let recipt = self
|
||||
.contract
|
||||
.signed_call_with_confirmations(
|
||||
ETH_BURN_FUNCTION_NAME,
|
||||
(
|
||||
Token::Uint(U256::from(UTOKENS_TO_BURN)),
|
||||
Token::Uint(U256::from(&verification_key.to_bytes())),
|
||||
Token::Bytes(signed_verification_key.to_bytes().to_vec()),
|
||||
Token::String(gateway_owner),
|
||||
),
|
||||
options,
|
||||
confirmations,
|
||||
&self.eth_private_key,
|
||||
)
|
||||
.await?;
|
||||
if Some(U64::from(0u64)) == recipt.status {
|
||||
Err(GatewayClientError::BurnTokenError(
|
||||
web3::Error::InvalidResponse(format!(
|
||||
"Transaction status is 0 (failure): {:?}",
|
||||
recipt.logs,
|
||||
)),
|
||||
))
|
||||
} else {
|
||||
log::info!(
|
||||
"Bought bandwidth on Ethereum: {} MB",
|
||||
BANDWIDTH_VALUE / 1024 / 1024
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use network_defaults::ETH_EVENT_NAME;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn parse_contract() {
|
||||
let transport =
|
||||
Http::new("https://rinkeby.infura.io/v3/00000000000000000000000000000000").unwrap();
|
||||
let web3 = web3::Web3::new(transport);
|
||||
// test no panic occurs
|
||||
eth_contract(web3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_erc20_contract() {
|
||||
let transport =
|
||||
Http::new("https://rinkeby.infura.io/v3/00000000000000000000000000000000").unwrap();
|
||||
let web3 = web3::Web3::new(transport);
|
||||
// test no panic occurs
|
||||
eth_erc20_contract(web3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_event_name_constant_against_abi() {
|
||||
let transport =
|
||||
Http::new("https://rinkeby.infura.io/v3/00000000000000000000000000000000").unwrap();
|
||||
let web3 = web3::Web3::new(transport);
|
||||
let contract = eth_contract(web3);
|
||||
assert!(contract.abi().event(ETH_EVENT_NAME).is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,6 @@ use crate::wasm_storage::PersistentStorage;
|
||||
use coconut_interface::Credential;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use credential_storage::PersistentStorage;
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
use credentials::token::bandwidth::TokenCredential;
|
||||
use crypto::asymmetric::identity;
|
||||
use futures::{FutureExt, SinkExt, StreamExt};
|
||||
use gateway_requests::authentication::encrypted_address::EncryptedAddressBytes;
|
||||
@@ -309,6 +307,8 @@ impl GatewayClient {
|
||||
async {
|
||||
if let Some(mut s) = m_shutdown {
|
||||
s.recv().await
|
||||
} else {
|
||||
std::future::pending::<()>().await
|
||||
}
|
||||
}
|
||||
.fuse()
|
||||
@@ -317,7 +317,7 @@ impl GatewayClient {
|
||||
tokio::pin!(shutdown);
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let mut shutdown = Box::pin(async {}.fuse());
|
||||
let mut shutdown = std::future::pending::<()>().fuse();
|
||||
|
||||
loop {
|
||||
futures::select! {
|
||||
@@ -552,30 +552,6 @@ impl GatewayClient {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
async fn claim_token_bandwidth(
|
||||
&mut self,
|
||||
credential: TokenCredential,
|
||||
) -> Result<(), GatewayClientError> {
|
||||
let mut rng = OsRng;
|
||||
|
||||
let iv = IV::new_random(&mut rng);
|
||||
|
||||
let msg = ClientControlRequest::new_enc_token_bandwidth_credential(
|
||||
&credential,
|
||||
self.shared_key.as_ref().unwrap(),
|
||||
iv,
|
||||
)
|
||||
.into();
|
||||
self.bandwidth_remaining = match self.send_websocket_message(msg).await? {
|
||||
ServerResponse::Bandwidth { available_total } => Ok(available_total),
|
||||
ServerResponse::Error { message } => Err(GatewayClientError::GatewayError(message)),
|
||||
_ => Err(GatewayClientError::UnexpectedResponse),
|
||||
}?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn try_claim_testnet_bandwidth(&mut self) -> Result<(), GatewayClientError> {
|
||||
let msg = ClientControlRequest::ClaimFreeTestnetBandwidth.into();
|
||||
self.bandwidth_remaining = match self.send_websocket_message(msg).await? {
|
||||
@@ -614,17 +590,10 @@ impl GatewayClient {
|
||||
.prepare_coconut_credential()
|
||||
.await?;
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
let credential = self
|
||||
.bandwidth_controller
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.prepare_token_credential(self.gateway_identity, _gateway_owner)
|
||||
.await?;
|
||||
return self.try_claim_testnet_bandwidth().await;
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
return self.claim_coconut_bandwidth(credential).await;
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
return self.claim_token_bandwidth(credential).await;
|
||||
}
|
||||
|
||||
fn estimate_required_bandwidth(&self, packets: &[MixPacket]) -> i64 {
|
||||
|
||||
@@ -113,6 +113,8 @@ impl PartiallyDelegated {
|
||||
async {
|
||||
if let Some(mut s) = m_shutdown {
|
||||
s.recv().await
|
||||
} else {
|
||||
std::future::pending::<()>().await
|
||||
}
|
||||
}
|
||||
.fuse()
|
||||
@@ -121,7 +123,7 @@ impl PartiallyDelegated {
|
||||
tokio::pin!(shutdown);
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let mut shutdown = Box::pin(async {}.fuse());
|
||||
let mut shutdown = std::future::pending::<()>().fuse();
|
||||
|
||||
let ret_err = loop {
|
||||
futures::select! {
|
||||
|
||||
@@ -26,13 +26,6 @@ pub struct CoconutCredential {
|
||||
pub signature: String,
|
||||
}
|
||||
|
||||
pub struct ERC20Credential {
|
||||
pub id: i64,
|
||||
pub public_key: String,
|
||||
pub private_key: String,
|
||||
pub consumed: bool,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait Storage: Send + Sync {
|
||||
async fn insert_coconut_credential(
|
||||
@@ -47,16 +40,6 @@ pub trait Storage: Send + Sync {
|
||||
async fn get_next_coconut_credential(&self) -> Result<CoconutCredential, StorageError>;
|
||||
|
||||
async fn remove_coconut_credential(&self, id: i64) -> Result<(), StorageError>;
|
||||
|
||||
async fn insert_erc20_credential(
|
||||
&self,
|
||||
public_key: String,
|
||||
private_key: String,
|
||||
) -> Result<(), StorageError>;
|
||||
|
||||
async fn get_next_erc20_credential(&self) -> Result<ERC20Credential, StorageError>;
|
||||
|
||||
async fn consume_erc20_credential(&self, public_key: String) -> Result<(), StorageError>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@@ -79,20 +62,4 @@ impl Storage for PersistentStorage {
|
||||
async fn remove_coconut_credential(&self, _id: i64) -> Result<(), StorageError> {
|
||||
Err(StorageError::WasmNotSupported)
|
||||
}
|
||||
|
||||
async fn insert_erc20_credential(
|
||||
&self,
|
||||
_public_key: String,
|
||||
_private_key: String,
|
||||
) -> Result<(), StorageError> {
|
||||
Err(StorageError::WasmNotSupported)
|
||||
}
|
||||
|
||||
async fn get_next_erc20_credential(&self) -> Result<ERC20Credential, StorageError> {
|
||||
Err(StorageError::WasmNotSupported)
|
||||
}
|
||||
|
||||
async fn consume_erc20_credential(&self, _public_key: String) -> Result<(), StorageError> {
|
||||
Err(StorageError::WasmNotSupported)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,7 +186,7 @@ impl Client {
|
||||
address.into(),
|
||||
receiver,
|
||||
initial_connection_timeout,
|
||||
&*current_reconnection_attempt,
|
||||
¤t_reconnection_attempt,
|
||||
)
|
||||
.await
|
||||
});
|
||||
|
||||
@@ -2,35 +2,33 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{validator_api, ValidatorClientError};
|
||||
use mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixNodeBond};
|
||||
use mixnet_contract_common::mixnode::MixNodeDetails;
|
||||
use mixnet_contract_common::NodeId;
|
||||
use mixnet_contract_common::{GatewayBond, IdentityKeyRef};
|
||||
use url::Url;
|
||||
use validator_api_requests::coconut::{
|
||||
BlindSignRequestBody, BlindedSignatureResponse, CosmosAddressResponse, VerificationKeyResponse,
|
||||
VerifyCredentialBody, VerifyCredentialResponse,
|
||||
};
|
||||
use validator_api_requests::models::{
|
||||
CoreNodeStatusResponse, MixnodeStatusResponse, RewardEstimationResponse,
|
||||
StakeSaturationResponse,
|
||||
GatewayCoreStatusResponse, MixnodeCoreStatusResponse, MixnodeStatusResponse,
|
||||
RewardEstimationResponse, StakeSaturationResponse,
|
||||
};
|
||||
|
||||
#[cfg(feature = "nymd-client")]
|
||||
use validator_api_requests::models::{MixNodeBondAnnotated, UptimeResponse};
|
||||
|
||||
use crate::nymd::traits::MixnetQueryClient;
|
||||
#[cfg(feature = "nymd-client")]
|
||||
use crate::nymd::{
|
||||
self, error::NymdError, CosmWasmClient, NymdClient, QueryNymdClient, SigningNymdClient,
|
||||
};
|
||||
|
||||
use crate::nymd::{self, CosmWasmClient, NymdClient, QueryNymdClient, SigningNymdClient};
|
||||
#[cfg(feature = "nymd-client")]
|
||||
use mixnet_contract_common::{
|
||||
mixnode::DelegationEvent, ContractStateParams, Delegation, IdentityKey, Interval,
|
||||
MixnetContractVersion, MixnodeRewardingStatusResponse, RewardedSetNodeStatus,
|
||||
RewardedSetUpdateDetails,
|
||||
mixnode::MixNodeBond,
|
||||
pending_events::{PendingEpochEvent, PendingIntervalEvent},
|
||||
Delegation, RewardedSetNodeStatus, UnbondedMixnode,
|
||||
};
|
||||
#[cfg(feature = "nymd-client")]
|
||||
use network_defaults::NymNetworkDetails;
|
||||
#[cfg(feature = "nymd-client")]
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use validator_api_requests::models::MixNodeBondAnnotated;
|
||||
|
||||
#[cfg(feature = "nymd-client")]
|
||||
#[must_use]
|
||||
@@ -191,12 +189,9 @@ impl Client<QueryNymdClient> {
|
||||
}
|
||||
}
|
||||
|
||||
// nymd wrappers
|
||||
#[cfg(feature = "nymd-client")]
|
||||
impl<C> Client<C> {
|
||||
pub fn change_validator_api(&mut self, new_endpoint: Url) {
|
||||
self.validator_api.change_url(new_endpoint)
|
||||
}
|
||||
|
||||
// use case: somebody initialised client without a contract in order to upload and initialise one
|
||||
// and now they want to actually use it without making new client
|
||||
pub fn set_mixnet_contract_address(&mut self, mixnet_contract_address: cosmrs::AccountId) {
|
||||
@@ -208,203 +203,22 @@ impl<C> Client<C> {
|
||||
self.nymd.mixnet_contract_address().clone()
|
||||
}
|
||||
|
||||
pub async fn get_cached_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorClientError> {
|
||||
Ok(self.validator_api.get_mixnodes().await?)
|
||||
}
|
||||
|
||||
pub async fn get_cached_mixnodes_detailed(
|
||||
&self,
|
||||
) -> Result<Vec<MixNodeBondAnnotated>, ValidatorClientError> {
|
||||
Ok(self.validator_api.get_mixnodes_detailed().await?)
|
||||
}
|
||||
|
||||
pub async fn get_cached_rewarded_mixnodes(
|
||||
&self,
|
||||
) -> Result<Vec<MixNodeBond>, ValidatorClientError> {
|
||||
Ok(self.validator_api.get_rewarded_mixnodes().await?)
|
||||
}
|
||||
|
||||
pub async fn get_cached_rewarded_mixnodes_detailed(
|
||||
&self,
|
||||
) -> Result<Vec<MixNodeBondAnnotated>, ValidatorClientError> {
|
||||
Ok(self.validator_api.get_rewarded_mixnodes_detailed().await?)
|
||||
}
|
||||
|
||||
pub async fn get_cached_active_mixnodes(
|
||||
&self,
|
||||
) -> Result<Vec<MixNodeBond>, ValidatorClientError> {
|
||||
Ok(self.validator_api.get_active_mixnodes().await?)
|
||||
}
|
||||
|
||||
pub async fn get_cached_active_mixnodes_detailed(
|
||||
&self,
|
||||
) -> Result<Vec<MixNodeBondAnnotated>, ValidatorClientError> {
|
||||
Ok(self.validator_api.get_active_mixnodes_detailed().await?)
|
||||
}
|
||||
|
||||
pub async fn get_cached_gateways(&self) -> Result<Vec<GatewayBond>, ValidatorClientError> {
|
||||
Ok(self.validator_api.get_gateways().await?)
|
||||
}
|
||||
|
||||
pub async fn get_contract_settings(&self) -> Result<ContractStateParams, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
Ok(self.nymd.get_contract_settings().await?)
|
||||
}
|
||||
|
||||
pub async fn get_operator_rewards(&self, address: String) -> Result<u128, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
Ok(self.nymd.get_operator_rewards(address).await?.u128())
|
||||
}
|
||||
|
||||
pub async fn get_delegator_rewards(
|
||||
&self,
|
||||
address: String,
|
||||
mix_identity: IdentityKey,
|
||||
proxy: Option<String>,
|
||||
) -> Result<u128, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
Ok(self
|
||||
.nymd
|
||||
.get_delegator_rewards(address, mix_identity, proxy)
|
||||
.await?
|
||||
.u128())
|
||||
}
|
||||
|
||||
pub async fn get_pending_delegation_events(
|
||||
&self,
|
||||
owner_address: String,
|
||||
proxy_address: Option<String>,
|
||||
) -> Result<Vec<DelegationEvent>, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
Ok(self
|
||||
.nymd
|
||||
.get_pending_delegation_events(owner_address, proxy_address)
|
||||
.await?)
|
||||
}
|
||||
|
||||
pub async fn get_current_epoch(&self) -> Result<Interval, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
Ok(self.nymd.get_current_epoch().await?)
|
||||
}
|
||||
|
||||
pub async fn get_current_operator_cost(&self) -> Result<u64, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
Ok(self.nymd.get_current_operator_cost().await?)
|
||||
}
|
||||
|
||||
pub async fn get_mixnet_contract_version(&self) -> Result<MixnetContractVersion, NymdError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
self.nymd.get_mixnet_contract_version().await
|
||||
}
|
||||
|
||||
pub async fn get_rewarding_status(
|
||||
&self,
|
||||
mix_identity: mixnet_contract_common::IdentityKey,
|
||||
rewarding_interval_nonce: u32,
|
||||
) -> Result<MixnodeRewardingStatusResponse, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
Ok(self
|
||||
.nymd
|
||||
.get_rewarding_status(mix_identity, rewarding_interval_nonce)
|
||||
.await?)
|
||||
}
|
||||
|
||||
pub async fn get_reward_pool(&self) -> Result<u128, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
Ok(self.nymd.get_reward_pool().await?.u128())
|
||||
}
|
||||
|
||||
pub async fn get_circulating_supply(&self) -> Result<u128, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
Ok(self.nymd.get_circulating_supply().await?.u128())
|
||||
}
|
||||
|
||||
pub async fn get_sybil_resistance_percent(&self) -> Result<u8, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
Ok(self.nymd.get_sybil_resistance_percent().await?)
|
||||
}
|
||||
|
||||
pub async fn get_active_set_work_factor(&self) -> Result<u8, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
Ok(self.nymd.get_active_set_work_factor().await?)
|
||||
}
|
||||
|
||||
pub async fn get_epochs_in_interval(&self) -> Result<u64, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
Ok(self.nymd.get_epochs_in_interval().await?)
|
||||
}
|
||||
|
||||
pub async fn get_interval_reward_percent(&self) -> Result<u8, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
Ok(self.nymd.get_interval_reward_percent().await?)
|
||||
}
|
||||
|
||||
pub async fn get_current_rewarded_set_update_details(
|
||||
&self,
|
||||
) -> Result<RewardedSetUpdateDetails, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
Ok(self
|
||||
.nymd
|
||||
.query_current_rewarded_set_update_details()
|
||||
.await?)
|
||||
}
|
||||
|
||||
// basically handles paging for us
|
||||
pub async fn get_all_nymd_rewarded_set_mixnode_identities(
|
||||
pub async fn get_all_nymd_rewarded_set_mixnodes(
|
||||
&self,
|
||||
) -> Result<Vec<(IdentityKey, RewardedSetNodeStatus)>, ValidatorClientError>
|
||||
) -> Result<Vec<(NodeId, RewardedSetNodeStatus)>, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
C: CosmWasmClient + Sync + Send,
|
||||
{
|
||||
let mut identities = Vec::new();
|
||||
let mut start_after = None;
|
||||
let mut height = None;
|
||||
|
||||
loop {
|
||||
let mut paged_response = self
|
||||
.nymd
|
||||
.get_rewarded_set_identities_paged(
|
||||
start_after.take(),
|
||||
self.rewarded_set_page_limit,
|
||||
height,
|
||||
)
|
||||
.get_rewarded_set_paged(start_after.take(), self.rewarded_set_page_limit)
|
||||
.await?;
|
||||
identities.append(&mut paged_response.identities);
|
||||
|
||||
if height.is_none() {
|
||||
// keep using the same height (the first query happened at the most recent height)
|
||||
height = Some(paged_response.at_height)
|
||||
}
|
||||
identities.append(&mut paged_response.nodes);
|
||||
|
||||
if let Some(start_after_res) = paged_response.start_next_after {
|
||||
start_after = Some(start_after_res)
|
||||
@@ -416,83 +230,122 @@ impl<C> Client<C> {
|
||||
Ok(identities)
|
||||
}
|
||||
|
||||
pub async fn get_nymd_rewarded_and_active_sets(
|
||||
&self,
|
||||
) -> Result<Vec<(MixNodeBond, RewardedSetNodeStatus)>, ValidatorClientError>
|
||||
pub async fn get_all_nymd_mixnode_bonds(&self) -> Result<Vec<MixNodeBond>, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
let all_mixnodes = self.get_all_nymd_mixnodes().await?;
|
||||
let rewarded_set_identities = self
|
||||
.get_all_nymd_rewarded_set_mixnode_identities()
|
||||
.await?
|
||||
.into_iter()
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
Ok(all_mixnodes
|
||||
.into_iter()
|
||||
.filter_map(|node| {
|
||||
rewarded_set_identities
|
||||
.get(node.identity())
|
||||
.map(|status| (node, *status))
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
/// If you need both rewarded and the active set, consider using [Self::get_nymd_rewarded_and_active_sets] instead
|
||||
pub async fn get_nymd_rewarded_set(&self) -> Result<Vec<MixNodeBond>, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
let all_mixnodes = self.get_all_nymd_mixnodes().await?;
|
||||
let rewarded_set_identities = self
|
||||
.get_all_nymd_rewarded_set_mixnode_identities()
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|(identity, _status)| identity)
|
||||
.collect::<HashSet<_>>();
|
||||
|
||||
Ok(all_mixnodes
|
||||
.into_iter()
|
||||
.filter(|node| rewarded_set_identities.contains(node.identity()))
|
||||
.collect())
|
||||
}
|
||||
|
||||
/// If you need both rewarded and the active set, consider using [Self::get_nymd_rewarded_and_active_sets] instead
|
||||
pub async fn get_nymd_active_set(&self) -> Result<Vec<MixNodeBond>, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
let all_mixnodes = self.get_all_nymd_mixnodes().await?;
|
||||
let active_set_identities = self
|
||||
.get_all_nymd_rewarded_set_mixnode_identities()
|
||||
.await?
|
||||
.into_iter()
|
||||
.filter_map(|(identity, status)| {
|
||||
if status.is_active() {
|
||||
Some(identity)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<HashSet<_>>();
|
||||
|
||||
Ok(all_mixnodes
|
||||
.into_iter()
|
||||
.filter(|node| active_set_identities.contains(node.identity()))
|
||||
.collect())
|
||||
}
|
||||
|
||||
pub async fn get_all_nymd_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
C: CosmWasmClient + Sync + Send,
|
||||
{
|
||||
let mut mixnodes = Vec::new();
|
||||
let mut start_after = None;
|
||||
loop {
|
||||
let mut paged_response = self
|
||||
.nymd
|
||||
.get_mixnodes_paged(start_after.take(), self.mixnode_page_limit)
|
||||
.get_mixnode_bonds_paged(self.mixnode_page_limit, start_after.take())
|
||||
.await?;
|
||||
mixnodes.append(&mut paged_response.nodes);
|
||||
|
||||
if let Some(start_after_res) = paged_response.start_next_after {
|
||||
start_after = Some(start_after_res)
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(mixnodes)
|
||||
}
|
||||
|
||||
pub async fn get_all_nymd_mixnodes_detailed(
|
||||
&self,
|
||||
) -> Result<Vec<MixNodeDetails>, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync + Send,
|
||||
{
|
||||
let mut mixnodes = Vec::new();
|
||||
let mut start_after = None;
|
||||
loop {
|
||||
let mut paged_response = self
|
||||
.nymd
|
||||
.get_mixnodes_detailed_paged(self.mixnode_page_limit, start_after.take())
|
||||
.await?;
|
||||
mixnodes.append(&mut paged_response.nodes);
|
||||
|
||||
if let Some(start_after_res) = paged_response.start_next_after {
|
||||
start_after = Some(start_after_res)
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(mixnodes)
|
||||
}
|
||||
|
||||
pub async fn get_all_nymd_unbonded_mixnodes(
|
||||
&self,
|
||||
) -> Result<Vec<(NodeId, UnbondedMixnode)>, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync + Send,
|
||||
{
|
||||
let mut mixnodes = Vec::new();
|
||||
let mut start_after = None;
|
||||
loop {
|
||||
let mut paged_response = self
|
||||
.nymd
|
||||
.get_unbonded_paged(self.mixnode_page_limit, start_after.take())
|
||||
.await?;
|
||||
mixnodes.append(&mut paged_response.nodes);
|
||||
|
||||
if let Some(start_after_res) = paged_response.start_next_after {
|
||||
start_after = Some(start_after_res)
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(mixnodes)
|
||||
}
|
||||
|
||||
pub async fn get_all_nymd_unbonded_mixnodes_by_owner(
|
||||
&self,
|
||||
owner: &cosmrs::AccountId,
|
||||
) -> Result<Vec<(NodeId, UnbondedMixnode)>, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync + Send,
|
||||
{
|
||||
let mut mixnodes = Vec::new();
|
||||
let mut start_after = None;
|
||||
loop {
|
||||
let mut paged_response = self
|
||||
.nymd
|
||||
.get_unbonded_by_owner_paged(owner, self.mixnode_page_limit, start_after.take())
|
||||
.await?;
|
||||
mixnodes.append(&mut paged_response.nodes);
|
||||
|
||||
if let Some(start_after_res) = paged_response.start_next_after {
|
||||
start_after = Some(start_after_res)
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(mixnodes)
|
||||
}
|
||||
|
||||
pub async fn get_all_nymd_unbonded_mixnodes_by_identity(
|
||||
&self,
|
||||
identity_key: String,
|
||||
) -> Result<Vec<(NodeId, UnbondedMixnode)>, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync + Send,
|
||||
{
|
||||
let mut mixnodes = Vec::new();
|
||||
let mut start_after = None;
|
||||
loop {
|
||||
let mut paged_response = self
|
||||
.nymd
|
||||
.get_unbonded_by_identity_paged(
|
||||
identity_key.clone(),
|
||||
self.mixnode_page_limit,
|
||||
start_after.take(),
|
||||
)
|
||||
.await?;
|
||||
mixnodes.append(&mut paged_response.nodes);
|
||||
|
||||
@@ -508,7 +361,7 @@ impl<C> Client<C> {
|
||||
|
||||
pub async fn get_all_nymd_gateways(&self) -> Result<Vec<GatewayBond>, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
C: CosmWasmClient + Sync + Send,
|
||||
{
|
||||
let mut gateways = Vec::new();
|
||||
let mut start_after = None;
|
||||
@@ -531,18 +384,18 @@ impl<C> Client<C> {
|
||||
|
||||
pub async fn get_all_nymd_single_mixnode_delegations(
|
||||
&self,
|
||||
identity: IdentityKey,
|
||||
mix_id: NodeId,
|
||||
) -> Result<Vec<Delegation>, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
C: CosmWasmClient + Sync + Send,
|
||||
{
|
||||
let mut delegations = Vec::new();
|
||||
let mut start_after = None;
|
||||
loop {
|
||||
let mut paged_response = self
|
||||
.nymd
|
||||
.get_mix_delegations_paged(
|
||||
identity.clone(),
|
||||
.get_mixnode_delegations_paged(
|
||||
mix_id,
|
||||
start_after.take(),
|
||||
self.mixnode_delegations_page_limit,
|
||||
)
|
||||
@@ -564,7 +417,7 @@ impl<C> Client<C> {
|
||||
delegation_owner: &cosmrs::AccountId,
|
||||
) -> Result<Vec<Delegation>, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
C: CosmWasmClient + Sync + Send,
|
||||
{
|
||||
let mut delegations = Vec::new();
|
||||
let mut start_after = None;
|
||||
@@ -589,10 +442,128 @@ impl<C> Client<C> {
|
||||
Ok(delegations)
|
||||
}
|
||||
|
||||
pub async fn get_mixnode_avg_uptimes(
|
||||
pub async fn get_all_network_delegations(&self) -> Result<Vec<Delegation>, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync + Send,
|
||||
{
|
||||
let mut delegations = Vec::new();
|
||||
let mut start_after = None;
|
||||
loop {
|
||||
let mut paged_response = self
|
||||
.nymd
|
||||
.get_all_network_delegations_paged(
|
||||
start_after.take(),
|
||||
self.mixnode_delegations_page_limit,
|
||||
)
|
||||
.await?;
|
||||
delegations.append(&mut paged_response.delegations);
|
||||
|
||||
if let Some(start_after_res) = paged_response.start_next_after {
|
||||
start_after = Some(start_after_res)
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(delegations)
|
||||
}
|
||||
|
||||
pub async fn get_all_nymd_pending_epoch_events(
|
||||
&self,
|
||||
) -> Result<Vec<UptimeResponse>, ValidatorClientError> {
|
||||
Ok(self.validator_api.get_mixnode_avg_uptimes().await?)
|
||||
) -> Result<Vec<PendingEpochEvent>, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync + Send,
|
||||
{
|
||||
let mut events = Vec::new();
|
||||
let mut start_after = None;
|
||||
|
||||
loop {
|
||||
let mut paged_response = self
|
||||
.nymd
|
||||
.get_pending_epoch_events_paged(start_after.take(), self.rewarded_set_page_limit)
|
||||
.await?;
|
||||
events.append(&mut paged_response.events);
|
||||
|
||||
if let Some(start_after_res) = paged_response.start_next_after {
|
||||
start_after = Some(start_after_res)
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(events)
|
||||
}
|
||||
|
||||
pub async fn get_all_nymd_pending_interval_events(
|
||||
&self,
|
||||
) -> Result<Vec<PendingIntervalEvent>, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync + Send,
|
||||
{
|
||||
let mut events = Vec::new();
|
||||
let mut start_after = None;
|
||||
|
||||
loop {
|
||||
let mut paged_response = self
|
||||
.nymd
|
||||
.get_pending_interval_events_paged(start_after.take(), self.rewarded_set_page_limit)
|
||||
.await?;
|
||||
events.append(&mut paged_response.events);
|
||||
|
||||
if let Some(start_after_res) = paged_response.start_next_after {
|
||||
start_after = Some(start_after_res)
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(events)
|
||||
}
|
||||
}
|
||||
|
||||
// validator-api wrappers
|
||||
#[cfg(feature = "nymd-client")]
|
||||
impl<C> Client<C> {
|
||||
pub fn change_validator_api(&mut self, new_endpoint: Url) {
|
||||
self.validator_api.change_url(new_endpoint)
|
||||
}
|
||||
|
||||
pub async fn get_cached_mixnodes(&self) -> Result<Vec<MixNodeDetails>, ValidatorClientError> {
|
||||
Ok(self.validator_api.get_mixnodes().await?)
|
||||
}
|
||||
|
||||
pub async fn get_cached_mixnodes_detailed(
|
||||
&self,
|
||||
) -> Result<Vec<MixNodeBondAnnotated>, ValidatorClientError> {
|
||||
Ok(self.validator_api.get_mixnodes_detailed().await?)
|
||||
}
|
||||
|
||||
pub async fn get_cached_rewarded_mixnodes(
|
||||
&self,
|
||||
) -> Result<Vec<MixNodeDetails>, ValidatorClientError> {
|
||||
Ok(self.validator_api.get_rewarded_mixnodes().await?)
|
||||
}
|
||||
|
||||
pub async fn get_cached_rewarded_mixnodes_detailed(
|
||||
&self,
|
||||
) -> Result<Vec<MixNodeBondAnnotated>, ValidatorClientError> {
|
||||
Ok(self.validator_api.get_rewarded_mixnodes_detailed().await?)
|
||||
}
|
||||
|
||||
pub async fn get_cached_active_mixnodes(
|
||||
&self,
|
||||
) -> Result<Vec<MixNodeDetails>, ValidatorClientError> {
|
||||
Ok(self.validator_api.get_active_mixnodes().await?)
|
||||
}
|
||||
|
||||
pub async fn get_cached_active_mixnodes_detailed(
|
||||
&self,
|
||||
) -> Result<Vec<MixNodeBondAnnotated>, ValidatorClientError> {
|
||||
Ok(self.validator_api.get_active_mixnodes_detailed().await?)
|
||||
}
|
||||
|
||||
pub async fn get_cached_gateways(&self) -> Result<Vec<GatewayBond>, ValidatorClientError> {
|
||||
Ok(self.validator_api.get_gateways().await?)
|
||||
}
|
||||
|
||||
pub async fn blind_sign(
|
||||
@@ -630,17 +601,17 @@ impl ApiClient {
|
||||
|
||||
pub async fn get_cached_active_mixnodes(
|
||||
&self,
|
||||
) -> Result<Vec<MixNodeBond>, ValidatorClientError> {
|
||||
) -> Result<Vec<MixNodeDetails>, ValidatorClientError> {
|
||||
Ok(self.validator_api.get_active_mixnodes().await?)
|
||||
}
|
||||
|
||||
pub async fn get_cached_rewarded_mixnodes(
|
||||
&self,
|
||||
) -> Result<Vec<MixNodeBond>, ValidatorClientError> {
|
||||
) -> Result<Vec<MixNodeDetails>, ValidatorClientError> {
|
||||
Ok(self.validator_api.get_rewarded_mixnodes().await?)
|
||||
}
|
||||
|
||||
pub async fn get_cached_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorClientError> {
|
||||
pub async fn get_cached_mixnodes(&self) -> Result<Vec<MixNodeDetails>, ValidatorClientError> {
|
||||
Ok(self.validator_api.get_mixnodes().await?)
|
||||
}
|
||||
|
||||
@@ -652,7 +623,7 @@ impl ApiClient {
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
since: Option<i64>,
|
||||
) -> Result<CoreNodeStatusResponse, ValidatorClientError> {
|
||||
) -> Result<GatewayCoreStatusResponse, ValidatorClientError> {
|
||||
Ok(self
|
||||
.validator_api
|
||||
.get_gateway_core_status_count(identity, since)
|
||||
@@ -661,39 +632,39 @@ impl ApiClient {
|
||||
|
||||
pub async fn get_mixnode_core_status_count(
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
mix_id: NodeId,
|
||||
since: Option<i64>,
|
||||
) -> Result<CoreNodeStatusResponse, ValidatorClientError> {
|
||||
) -> Result<MixnodeCoreStatusResponse, ValidatorClientError> {
|
||||
Ok(self
|
||||
.validator_api
|
||||
.get_mixnode_core_status_count(identity, since)
|
||||
.get_mixnode_core_status_count(mix_id, since)
|
||||
.await?)
|
||||
}
|
||||
|
||||
pub async fn get_mixnode_status(
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
mix_id: NodeId,
|
||||
) -> Result<MixnodeStatusResponse, ValidatorClientError> {
|
||||
Ok(self.validator_api.get_mixnode_status(identity).await?)
|
||||
Ok(self.validator_api.get_mixnode_status(mix_id).await?)
|
||||
}
|
||||
|
||||
pub async fn get_mixnode_reward_estimation(
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
mix_id: NodeId,
|
||||
) -> Result<RewardEstimationResponse, ValidatorClientError> {
|
||||
Ok(self
|
||||
.validator_api
|
||||
.get_mixnode_reward_estimation(identity)
|
||||
.get_mixnode_reward_estimation(mix_id)
|
||||
.await?)
|
||||
}
|
||||
|
||||
pub async fn get_mixnode_stake_saturation(
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
mix_id: NodeId,
|
||||
) -> Result<StakeSaturationResponse, ValidatorClientError> {
|
||||
Ok(self
|
||||
.validator_api
|
||||
.get_mixnode_stake_saturation(identity)
|
||||
.get_mixnode_stake_saturation(mix_id)
|
||||
.await?)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
use crate::nymd::error::NymdError;
|
||||
use crate::nymd::{Config as ClientConfig, NymdClient, QueryNymdClient};
|
||||
use crate::ApiClient;
|
||||
use network_defaults::all::Network;
|
||||
|
||||
use crate::nymd::traits::MixnetQueryClient;
|
||||
use colored::Colorize;
|
||||
use core::fmt;
|
||||
use itertools::Itertools;
|
||||
use network_defaults::NymNetworkDetails;
|
||||
use std::collections::HashMap;
|
||||
use std::hash::BuildHasher;
|
||||
use std::time::Duration;
|
||||
@@ -17,12 +18,12 @@ const CONNECTION_TEST_TIMEOUT_SEC: u64 = 2;
|
||||
|
||||
// Run connection tests for all specified nymd and api urls. These are all run concurrently.
|
||||
pub async fn run_validator_connection_test<H: BuildHasher + 'static>(
|
||||
nymd_urls: impl Iterator<Item = (Network, Url)>,
|
||||
api_urls: impl Iterator<Item = (Network, Url)>,
|
||||
mixnet_contract_address: HashMap<Network, cosmrs::AccountId, H>,
|
||||
nymd_urls: impl Iterator<Item = (NymNetworkDetails, Url)>,
|
||||
api_urls: impl Iterator<Item = (NymNetworkDetails, Url)>,
|
||||
mixnet_contract_address: HashMap<NymNetworkDetails, cosmrs::AccountId, H>,
|
||||
) -> (
|
||||
HashMap<Network, Vec<(Url, bool)>>,
|
||||
HashMap<Network, Vec<(Url, bool)>>,
|
||||
HashMap<NymNetworkDetails, Vec<(Url, bool)>>,
|
||||
HashMap<NymNetworkDetails, Vec<(Url, bool)>>,
|
||||
) {
|
||||
// Setup all the clients for the connection tests
|
||||
let connection_test_clients =
|
||||
@@ -45,16 +46,16 @@ pub async fn run_validator_connection_test<H: BuildHasher + 'static>(
|
||||
}
|
||||
|
||||
fn setup_connection_tests<H: BuildHasher + 'static>(
|
||||
nymd_urls: impl Iterator<Item = (Network, Url)>,
|
||||
api_urls: impl Iterator<Item = (Network, Url)>,
|
||||
mixnet_contract_address: HashMap<Network, cosmrs::AccountId, H>,
|
||||
nymd_urls: impl Iterator<Item = (NymNetworkDetails, Url)>,
|
||||
api_urls: impl Iterator<Item = (NymNetworkDetails, Url)>,
|
||||
mixnet_contract_address: HashMap<NymNetworkDetails, cosmrs::AccountId, H>,
|
||||
) -> impl Iterator<Item = ClientForConnectionTest> {
|
||||
let nymd_connection_test_clients = nymd_urls.filter_map(move |(network, url)| {
|
||||
let address = mixnet_contract_address
|
||||
.get(&network)
|
||||
.expect("No configured contract address")
|
||||
.clone();
|
||||
let config = ClientConfig::try_from_nym_network_details(&network.details())
|
||||
let config = ClientConfig::try_from_nym_network_details(&network)
|
||||
.expect("failed to create valid nymd client config");
|
||||
|
||||
if let Ok(mut client) = NymdClient::<QueryNymdClient>::connect(config, url.as_str()) {
|
||||
@@ -80,7 +81,7 @@ fn setup_connection_tests<H: BuildHasher + 'static>(
|
||||
fn extract_and_collect_results_into_map(
|
||||
connection_results: &[ConnectionResult],
|
||||
url_type: &UrlType,
|
||||
) -> HashMap<Network, Vec<(Url, bool)>> {
|
||||
) -> HashMap<NymNetworkDetails, Vec<(Url, bool)>> {
|
||||
connection_results
|
||||
.iter()
|
||||
.filter(|c| &c.url_type() == url_type)
|
||||
@@ -92,7 +93,7 @@ fn extract_and_collect_results_into_map(
|
||||
}
|
||||
|
||||
async fn test_nymd_connection(
|
||||
network: Network,
|
||||
network: NymNetworkDetails,
|
||||
url: &Url,
|
||||
client: &NymdClient<QueryNymdClient>,
|
||||
) -> ConnectionResult {
|
||||
@@ -104,56 +105,47 @@ async fn test_nymd_connection(
|
||||
{
|
||||
Ok(Err(NymdError::TendermintError(e))) => {
|
||||
// If we get a tendermint-rpc error, we classify the node as not contactable
|
||||
log::debug!(
|
||||
"Checking: nymd_url: {network}: {url}: {}: {}",
|
||||
"failed".red(),
|
||||
e
|
||||
);
|
||||
log::debug!("Checking: nymd_url: {url}: {}: {}", "failed".red(), e);
|
||||
false
|
||||
}
|
||||
Ok(Err(NymdError::AbciError(code, log))) => {
|
||||
// We accept the mixnet contract not found as ok from a connection standpoint. This happens
|
||||
// for example on a pre-launch network.
|
||||
log::debug!(
|
||||
"Checking: nymd_url: {network}: {url}: {}, but with abci error: {code}: {log}",
|
||||
"Checking: nymd_url: {url}: {}, but with abci error: {code}: {log}",
|
||||
"success".green()
|
||||
);
|
||||
code == 18
|
||||
}
|
||||
Ok(Err(error @ NymdError::NoContractAddressAvailable)) => {
|
||||
log::debug!(
|
||||
"Checking: nymd_url: {network}: {url}: {}: {error}",
|
||||
"failed".red()
|
||||
);
|
||||
log::debug!("Checking: nymd_url: {url}: {}: {error}", "failed".red());
|
||||
false
|
||||
}
|
||||
Ok(Err(e)) => {
|
||||
// For any other error, we're optimistic and just try anyway.
|
||||
log::debug!(
|
||||
"Checking: nymd_url: {network}: {url}: {}, but with error: {e}",
|
||||
"Checking: nymd_url: {url}: {}, but with error: {e}",
|
||||
"success".green()
|
||||
);
|
||||
true
|
||||
}
|
||||
Ok(Ok(_)) => {
|
||||
log::debug!(
|
||||
"Checking: nymd_url: {network}: {url}: {}",
|
||||
"success".green()
|
||||
);
|
||||
log::debug!("Checking: nymd_url: {url}: {}", "success".green());
|
||||
true
|
||||
}
|
||||
Err(e) => {
|
||||
log::debug!(
|
||||
"Checking: nymd_url: {network}: {url}: {}: {e}",
|
||||
"failed".red()
|
||||
);
|
||||
log::debug!("Checking: nymd_url: {url}: {}: {e}", "failed".red());
|
||||
false
|
||||
}
|
||||
};
|
||||
ConnectionResult::Nymd(network, url.clone(), result)
|
||||
}
|
||||
|
||||
async fn test_api_connection(network: Network, url: &Url, client: &ApiClient) -> ConnectionResult {
|
||||
async fn test_api_connection(
|
||||
network: NymNetworkDetails,
|
||||
url: &Url,
|
||||
client: &ApiClient,
|
||||
) -> ConnectionResult {
|
||||
let result = match timeout(
|
||||
Duration::from_secs(CONNECTION_TEST_TIMEOUT_SEC),
|
||||
client.get_cached_mixnodes(),
|
||||
@@ -161,21 +153,15 @@ async fn test_api_connection(network: Network, url: &Url, client: &ApiClient) ->
|
||||
.await
|
||||
{
|
||||
Ok(Ok(_)) => {
|
||||
log::debug!("Checking: api_url: {network}: {url}: {}", "success".green());
|
||||
log::debug!("Checking: api_url: {url}: {}", "success".green());
|
||||
true
|
||||
}
|
||||
Ok(Err(e)) => {
|
||||
log::debug!(
|
||||
"Checking: api_url: {network}: {url}: {}: {e}",
|
||||
"failed".red()
|
||||
);
|
||||
log::debug!("Checking: api_url: {url}: {}: {e}", "failed".red());
|
||||
false
|
||||
}
|
||||
Err(e) => {
|
||||
log::debug!(
|
||||
"Checking: api_url: {network}: {url}: {}: {e}",
|
||||
"failed".red()
|
||||
);
|
||||
log::debug!("Checking: api_url: {url}: {}: {e}", "failed".red());
|
||||
false
|
||||
}
|
||||
};
|
||||
@@ -183,8 +169,8 @@ async fn test_api_connection(network: Network, url: &Url, client: &ApiClient) ->
|
||||
}
|
||||
|
||||
enum ClientForConnectionTest {
|
||||
Nymd(Network, Url, Box<NymdClient<QueryNymdClient>>),
|
||||
Api(Network, Url, ApiClient),
|
||||
Nymd(NymNetworkDetails, Url, Box<NymdClient<QueryNymdClient>>),
|
||||
Api(NymNetworkDetails, Url, ApiClient),
|
||||
}
|
||||
|
||||
impl ClientForConnectionTest {
|
||||
@@ -217,12 +203,12 @@ impl fmt::Display for UrlType {
|
||||
|
||||
#[derive(Debug)]
|
||||
enum ConnectionResult {
|
||||
Nymd(Network, Url, bool),
|
||||
Api(Network, Url, bool),
|
||||
Nymd(NymNetworkDetails, Url, bool),
|
||||
Api(NymNetworkDetails, Url, bool),
|
||||
}
|
||||
|
||||
impl ConnectionResult {
|
||||
fn result(&self) -> (&Network, &Url, &bool) {
|
||||
fn result(&self) -> (&NymNetworkDetails, &Url, &bool) {
|
||||
match self {
|
||||
ConnectionResult::Nymd(network, url, result)
|
||||
| ConnectionResult::Api(network, url, result) => (network, url, result),
|
||||
@@ -239,11 +225,8 @@ impl ConnectionResult {
|
||||
|
||||
impl fmt::Display for ConnectionResult {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let (network, url, result) = self.result();
|
||||
let (_network, url, result) = self.result();
|
||||
let url_type = self.url_type();
|
||||
write!(
|
||||
f,
|
||||
"{network}: {url}: {url_type}: connection is successful: {result}"
|
||||
)
|
||||
write!(f, "{url}: {url_type}: connection is successful: {result}")
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,387 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub use crate::nymd::cosmwasm_client::client::CosmWasmClient;
|
||||
use crate::nymd::error::NymdError;
|
||||
use crate::nymd::NymdClient;
|
||||
use async_trait::async_trait;
|
||||
use cosmrs::AccountId;
|
||||
use mixnet_contract_common::delegation::{MixNodeDelegationResponse, OwnerProxySubKey};
|
||||
use mixnet_contract_common::mixnode::{
|
||||
MixNodeDetails, MixnodeRewardingDetailsResponse, PagedMixnodesDetailsResponse,
|
||||
PagedUnbondedMixnodesResponse, StakeSaturationResponse, UnbondedMixnodeResponse,
|
||||
};
|
||||
use mixnet_contract_common::reward_params::{Performance, RewardingParams};
|
||||
use mixnet_contract_common::rewarding::{
|
||||
EstimatedCurrentEpochRewardResponse, PendingRewardResponse,
|
||||
};
|
||||
use mixnet_contract_common::{
|
||||
delegation, ContractBuildInformation, ContractState, ContractStateParams,
|
||||
CurrentIntervalResponse, EpochEventId, GatewayBondResponse, GatewayOwnershipResponse,
|
||||
IdentityKey, IntervalEventId, LayerDistribution, MixOwnershipResponse, MixnodeDetailsResponse,
|
||||
NodeId, PagedAllDelegationsResponse, PagedDelegatorDelegationsResponse, PagedGatewayResponse,
|
||||
PagedMixNodeDelegationsResponse, PagedMixnodeBondsResponse, PagedRewardedSetResponse,
|
||||
PendingEpochEventsResponse, PendingIntervalEventsResponse, QueryMsg as MixnetQueryMsg,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
|
||||
#[async_trait]
|
||||
pub trait MixnetQueryClient {
|
||||
async fn query_mixnet_contract<T>(&self, query: MixnetQueryMsg) -> Result<T, NymdError>
|
||||
where
|
||||
for<'a> T: Deserialize<'a>;
|
||||
|
||||
// state/sys-params-related
|
||||
|
||||
async fn get_mixnet_contract_version(&self) -> Result<ContractBuildInformation, NymdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetContractVersion {})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_rewarding_validator_address(&self) -> Result<AccountId, NymdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetRewardingValidatorAddress {})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_mixnet_contract_settings(&self) -> Result<ContractStateParams, NymdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetStateParams {})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_mixnet_contract_state(&self) -> Result<ContractState, NymdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetState {})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_rewarding_parameters(&self) -> Result<RewardingParams, NymdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetRewardingParams {})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_current_interval_details(&self) -> Result<CurrentIntervalResponse, NymdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetCurrentIntervalDetails {})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_rewarded_set_paged(
|
||||
&self,
|
||||
start_after: Option<NodeId>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<PagedRewardedSetResponse, NymdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetRewardedSet { limit, start_after })
|
||||
.await
|
||||
}
|
||||
|
||||
// mixnode-related:
|
||||
|
||||
async fn get_mixnode_bonds_paged(
|
||||
&self,
|
||||
limit: Option<u32>,
|
||||
start_after: Option<NodeId>,
|
||||
) -> Result<PagedMixnodeBondsResponse, NymdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetMixNodeBonds { limit, start_after })
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_mixnodes_detailed_paged(
|
||||
&self,
|
||||
limit: Option<u32>,
|
||||
start_after: Option<NodeId>,
|
||||
) -> Result<PagedMixnodesDetailsResponse, NymdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetMixNodesDetailed { limit, start_after })
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_unbonded_paged(
|
||||
&self,
|
||||
limit: Option<u32>,
|
||||
start_after: Option<NodeId>,
|
||||
) -> Result<PagedUnbondedMixnodesResponse, NymdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetUnbondedMixNodes { limit, start_after })
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_unbonded_by_owner_paged(
|
||||
&self,
|
||||
owner: &AccountId,
|
||||
limit: Option<u32>,
|
||||
start_after: Option<NodeId>,
|
||||
) -> Result<PagedUnbondedMixnodesResponse, NymdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetUnbondedMixNodesByOwner {
|
||||
owner: owner.to_string(),
|
||||
limit,
|
||||
start_after,
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_unbonded_by_identity_paged(
|
||||
&self,
|
||||
identity_key: String,
|
||||
limit: Option<u32>,
|
||||
start_after: Option<NodeId>,
|
||||
) -> Result<PagedUnbondedMixnodesResponse, NymdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetUnbondedMixNodesByIdentityKey {
|
||||
identity_key,
|
||||
limit,
|
||||
start_after,
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_owned_mixnode(
|
||||
&self,
|
||||
address: &AccountId,
|
||||
) -> Result<MixOwnershipResponse, NymdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetOwnedMixnode {
|
||||
address: address.to_string(),
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_mixnode_details(
|
||||
&self,
|
||||
mix_id: NodeId,
|
||||
) -> Result<MixnodeDetailsResponse, NymdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetMixnodeDetails { mix_id })
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_mixnode_rewarding_details(
|
||||
&self,
|
||||
mix_id: NodeId,
|
||||
) -> Result<MixnodeRewardingDetailsResponse, NymdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetMixnodeRewardingDetails { mix_id })
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_mixnode_stake_saturation(
|
||||
&self,
|
||||
mix_id: NodeId,
|
||||
) -> Result<StakeSaturationResponse, NymdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetStakeSaturation { mix_id })
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_unbonded_mixnode_information(
|
||||
&self,
|
||||
mix_id: NodeId,
|
||||
) -> Result<UnbondedMixnodeResponse, NymdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetUnbondedMixNodeInformation { mix_id })
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_layer_distribution(&self) -> Result<LayerDistribution, NymdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetLayerDistribution {})
|
||||
.await
|
||||
}
|
||||
|
||||
// gateway-related:
|
||||
|
||||
async fn get_gateways_paged(
|
||||
&self,
|
||||
start_after: Option<IdentityKey>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<PagedGatewayResponse, NymdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetGateways { start_after, limit })
|
||||
.await
|
||||
}
|
||||
|
||||
/// Checks whether there is a bonded gateway associated with the provided identity key
|
||||
async fn get_gateway_bond(
|
||||
&self,
|
||||
identity: IdentityKey,
|
||||
) -> Result<GatewayBondResponse, NymdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetGatewayBond { identity })
|
||||
.await
|
||||
}
|
||||
|
||||
/// Checks whether there is a bonded gateway associated with the provided client's address
|
||||
async fn get_owned_gateway(
|
||||
&self,
|
||||
address: &AccountId,
|
||||
) -> Result<GatewayOwnershipResponse, NymdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetOwnedGateway {
|
||||
address: address.to_string(),
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
// delegation-related:
|
||||
|
||||
/// Gets list of all delegations towards particular mixnode on particular page.
|
||||
async fn get_mixnode_delegations_paged(
|
||||
&self,
|
||||
mix_id: NodeId,
|
||||
start_after: Option<String>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<PagedMixNodeDelegationsResponse, NymdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetMixnodeDelegations {
|
||||
mix_id,
|
||||
start_after,
|
||||
limit,
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
/// Gets list of all the mixnodes to which a particular address delegated.
|
||||
async fn get_delegator_delegations_paged(
|
||||
&self,
|
||||
delegator: String,
|
||||
start_after: Option<(NodeId, OwnerProxySubKey)>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<PagedDelegatorDelegationsResponse, NymdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetDelegatorDelegations {
|
||||
delegator,
|
||||
start_after,
|
||||
limit,
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
/// Checks value of delegation of given client towards particular mixnode.
|
||||
async fn get_delegation_details(
|
||||
&self,
|
||||
mix_id: NodeId,
|
||||
delegator: &AccountId,
|
||||
proxy: Option<String>,
|
||||
) -> Result<MixNodeDelegationResponse, NymdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetDelegationDetails {
|
||||
mix_id,
|
||||
delegator: delegator.to_string(),
|
||||
proxy,
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
/// Gets all the delegations on the entire network
|
||||
async fn get_all_network_delegations_paged(
|
||||
&self,
|
||||
start_after: Option<delegation::StorageKey>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<PagedAllDelegationsResponse, NymdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetAllDelegations { start_after, limit })
|
||||
.await
|
||||
}
|
||||
|
||||
// rewards related
|
||||
async fn get_pending_operator_reward(
|
||||
&self,
|
||||
operator: &AccountId,
|
||||
) -> Result<PendingRewardResponse, NymdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetPendingOperatorReward {
|
||||
address: operator.to_string(),
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_pending_mixnode_operator_reward(
|
||||
&self,
|
||||
mix_id: NodeId,
|
||||
) -> Result<PendingRewardResponse, NymdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetPendingMixNodeOperatorReward { mix_id })
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_pending_delegator_reward(
|
||||
&self,
|
||||
delegator: &AccountId,
|
||||
mix_id: NodeId,
|
||||
proxy: Option<String>,
|
||||
) -> Result<PendingRewardResponse, NymdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetPendingDelegatorReward {
|
||||
address: delegator.to_string(),
|
||||
mix_id,
|
||||
proxy,
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
// given the provided performance, estimate the reward at the end of the current epoch
|
||||
async fn get_estimated_current_epoch_operator_reward(
|
||||
&self,
|
||||
mix_id: NodeId,
|
||||
estimated_performance: Performance,
|
||||
) -> Result<EstimatedCurrentEpochRewardResponse, NymdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetEstimatedCurrentEpochOperatorReward {
|
||||
mix_id,
|
||||
estimated_performance,
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
// given the provided performance, estimate the reward at the end of the current epoch
|
||||
async fn get_estimated_current_epoch_delegator_reward(
|
||||
&self,
|
||||
delegator: &AccountId,
|
||||
mix_id: NodeId,
|
||||
proxy: Option<String>,
|
||||
estimated_performance: Performance,
|
||||
) -> Result<EstimatedCurrentEpochRewardResponse, NymdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetEstimatedCurrentEpochDelegatorReward {
|
||||
address: delegator.to_string(),
|
||||
mix_id,
|
||||
proxy,
|
||||
estimated_performance,
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
// interval-related
|
||||
|
||||
async fn get_pending_epoch_events_paged(
|
||||
&self,
|
||||
start_after: Option<EpochEventId>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<PendingEpochEventsResponse, NymdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetPendingEpochEvents { start_after, limit })
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_pending_interval_events_paged(
|
||||
&self,
|
||||
start_after: Option<IntervalEventId>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<PendingIntervalEventsResponse, NymdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetPendingIntervalEvents { start_after, limit })
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_mixnode_details_by_identity(
|
||||
&self,
|
||||
mix_identity: IdentityKey,
|
||||
) -> Result<Option<MixNodeDetails>, NymdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetBondedMixnodeDetailsByIdentity {
|
||||
mix_identity,
|
||||
})
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<C> MixnetQueryClient for NymdClient<C>
|
||||
where
|
||||
C: CosmWasmClient + Sync + Send,
|
||||
{
|
||||
async fn query_mixnet_contract<T>(&self, query: MixnetQueryMsg) -> Result<T, NymdError>
|
||||
where
|
||||
for<'a> T: Deserialize<'a>,
|
||||
{
|
||||
self.client
|
||||
.query_contract_smart(self.mixnet_contract_address(), &query)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<C> MixnetQueryClient for crate::Client<C>
|
||||
where
|
||||
C: CosmWasmClient + Sync + Send,
|
||||
{
|
||||
async fn query_mixnet_contract<T>(&self, query: MixnetQueryMsg) -> Result<T, NymdError>
|
||||
where
|
||||
for<'a> T: Deserialize<'a>,
|
||||
{
|
||||
self.nymd.query_mixnet_contract(query).await
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,496 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::nymd::coin::Coin;
|
||||
pub use crate::nymd::cosmwasm_client::client::CosmWasmClient;
|
||||
use crate::nymd::cosmwasm_client::types::ExecuteResult;
|
||||
use crate::nymd::error::NymdError;
|
||||
use crate::nymd::{Fee, NymdClient, SigningCosmWasmClient};
|
||||
use async_trait::async_trait;
|
||||
use cosmrs::AccountId;
|
||||
use mixnet_contract_common::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
|
||||
use mixnet_contract_common::reward_params::{IntervalRewardingParamsUpdate, Performance};
|
||||
use mixnet_contract_common::{
|
||||
ContractStateParams, ExecuteMsg as MixnetExecuteMsg, Gateway, MixNode, NodeId,
|
||||
};
|
||||
|
||||
#[async_trait]
|
||||
pub trait MixnetSigningClient {
|
||||
async fn execute_mixnet_contract(
|
||||
&self,
|
||||
fee: Option<Fee>,
|
||||
msg: MixnetExecuteMsg,
|
||||
funds: Vec<Coin>,
|
||||
) -> Result<ExecuteResult, NymdError>;
|
||||
|
||||
// state/sys-params-related
|
||||
|
||||
async fn update_rewarding_validator_address(
|
||||
&self,
|
||||
address: AccountId,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::UpdateRewardingValidatorAddress {
|
||||
address: address.to_string(),
|
||||
},
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn update_contract_state_params(
|
||||
&self,
|
||||
updated_parameters: ContractStateParams,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::UpdateContractStateParams { updated_parameters },
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn update_active_set_size(
|
||||
&self,
|
||||
active_set_size: u32,
|
||||
force_immediately: bool,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::UpdateActiveSetSize {
|
||||
active_set_size,
|
||||
force_immediately,
|
||||
},
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn update_rewarding_parameters(
|
||||
&self,
|
||||
updated_params: IntervalRewardingParamsUpdate,
|
||||
force_immediately: bool,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::UpdateRewardingParams {
|
||||
updated_params,
|
||||
force_immediately,
|
||||
},
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn update_interval_config(
|
||||
&self,
|
||||
epochs_in_interval: u32,
|
||||
epoch_duration_secs: u64,
|
||||
force_immediately: bool,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::UpdateIntervalConfig {
|
||||
epochs_in_interval,
|
||||
epoch_duration_secs,
|
||||
force_immediately,
|
||||
},
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn advance_current_epoch(
|
||||
&self,
|
||||
new_rewarded_set: Vec<NodeId>,
|
||||
expected_active_set_size: u32,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::AdvanceCurrentEpoch {
|
||||
new_rewarded_set,
|
||||
expected_active_set_size,
|
||||
},
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn reconcile_epoch_events(
|
||||
&self,
|
||||
limit: Option<u32>,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::ReconcileEpochEvents { limit },
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
// mixnode-related:
|
||||
|
||||
async fn bond_mixnode(
|
||||
&self,
|
||||
mix_node: MixNode,
|
||||
cost_params: MixNodeCostParams,
|
||||
owner_signature: String,
|
||||
pledge: Coin,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::BondMixnode {
|
||||
mix_node,
|
||||
cost_params,
|
||||
owner_signature,
|
||||
},
|
||||
vec![pledge],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn bond_mixnode_on_behalf(
|
||||
&self,
|
||||
owner: AccountId,
|
||||
mix_node: MixNode,
|
||||
cost_params: MixNodeCostParams,
|
||||
owner_signature: String,
|
||||
pledge: Coin,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::BondMixnodeOnBehalf {
|
||||
mix_node,
|
||||
cost_params,
|
||||
owner_signature,
|
||||
owner: owner.to_string(),
|
||||
},
|
||||
vec![pledge],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn unbond_mixnode(&self, fee: Option<Fee>) -> Result<ExecuteResult, NymdError> {
|
||||
self.execute_mixnet_contract(fee, MixnetExecuteMsg::UnbondMixnode {}, vec![])
|
||||
.await
|
||||
}
|
||||
|
||||
async fn unbond_mixnode_on_behalf(
|
||||
&self,
|
||||
owner: AccountId,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::UnbondMixnodeOnBehalf {
|
||||
owner: owner.to_string(),
|
||||
},
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn update_mixnode_cost_params(
|
||||
&self,
|
||||
new_costs: MixNodeCostParams,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::UpdateMixnodeCostParams { new_costs },
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn update_mixnode_cost_params_on_behalf(
|
||||
&self,
|
||||
owner: AccountId,
|
||||
new_costs: MixNodeCostParams,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::UpdateMixnodeCostParamsOnBehalf {
|
||||
new_costs,
|
||||
owner: owner.to_string(),
|
||||
},
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn update_mixnode_config(
|
||||
&self,
|
||||
new_config: MixNodeConfigUpdate,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::UpdateMixnodeConfig { new_config },
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn update_mixnode_config_on_behalf(
|
||||
&self,
|
||||
owner: AccountId,
|
||||
new_config: MixNodeConfigUpdate,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::UpdateMixnodeConfigOnBehalf {
|
||||
new_config,
|
||||
owner: owner.to_string(),
|
||||
},
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
// gateway-related:
|
||||
|
||||
async fn bond_gateway(
|
||||
&self,
|
||||
gateway: Gateway,
|
||||
owner_signature: String,
|
||||
pledge: Coin,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::BondGateway {
|
||||
gateway,
|
||||
owner_signature,
|
||||
},
|
||||
vec![pledge],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn bond_gateway_on_behalf(
|
||||
&self,
|
||||
owner: AccountId,
|
||||
gateway: Gateway,
|
||||
owner_signature: String,
|
||||
pledge: Coin,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::BondGatewayOnBehalf {
|
||||
gateway,
|
||||
owner_signature,
|
||||
owner: owner.to_string(),
|
||||
},
|
||||
vec![pledge],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn unbond_gateway(&self, fee: Option<Fee>) -> Result<ExecuteResult, NymdError> {
|
||||
self.execute_mixnet_contract(fee, MixnetExecuteMsg::UnbondGateway {}, vec![])
|
||||
.await
|
||||
}
|
||||
|
||||
async fn unbond_gateway_on_behalf(
|
||||
&self,
|
||||
owner: AccountId,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::UnbondGatewayOnBehalf {
|
||||
owner: owner.to_string(),
|
||||
},
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
// delegation-related:
|
||||
|
||||
async fn delegate_to_mixnode(
|
||||
&self,
|
||||
mix_id: NodeId,
|
||||
amount: Coin,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::DelegateToMixnode { mix_id },
|
||||
vec![amount],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn delegate_to_mixnode_on_behalf(
|
||||
&self,
|
||||
delegate: AccountId,
|
||||
mix_id: NodeId,
|
||||
amount: Coin,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::DelegateToMixnodeOnBehalf {
|
||||
mix_id,
|
||||
delegate: delegate.to_string(),
|
||||
},
|
||||
vec![amount],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn undelegate_from_mixnode(
|
||||
&self,
|
||||
mix_id: NodeId,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::UndelegateFromMixnode { mix_id },
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn undelegate_to_mixnode_on_behalf(
|
||||
&self,
|
||||
delegate: AccountId,
|
||||
mix_id: NodeId,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::UndelegateFromMixnodeOnBehalf {
|
||||
mix_id,
|
||||
delegate: delegate.to_string(),
|
||||
},
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
// reward-related
|
||||
|
||||
async fn reward_mixnode(
|
||||
&self,
|
||||
mix_id: NodeId,
|
||||
performance: Performance,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::RewardMixnode {
|
||||
mix_id,
|
||||
performance,
|
||||
},
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn withdraw_operator_reward(&self, fee: Option<Fee>) -> Result<ExecuteResult, NymdError> {
|
||||
self.execute_mixnet_contract(fee, MixnetExecuteMsg::WithdrawOperatorReward {}, vec![])
|
||||
.await
|
||||
}
|
||||
|
||||
async fn withdraw_operator_reward_on_behalf(
|
||||
&self,
|
||||
owner: AccountId,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::WithdrawOperatorRewardOnBehalf {
|
||||
owner: owner.to_string(),
|
||||
},
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn withdraw_delegator_reward(
|
||||
&self,
|
||||
mix_id: NodeId,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::WithdrawDelegatorReward { mix_id },
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn withdraw_delegator_reward_on_behalf(
|
||||
&self,
|
||||
owner: AccountId,
|
||||
mix_id: NodeId,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::WithdrawDelegatorRewardOnBehalf {
|
||||
mix_id,
|
||||
owner: owner.to_string(),
|
||||
},
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<C> MixnetSigningClient for NymdClient<C>
|
||||
where
|
||||
C: SigningCosmWasmClient + Sync + Send,
|
||||
{
|
||||
async fn execute_mixnet_contract(
|
||||
&self,
|
||||
fee: Option<Fee>,
|
||||
msg: MixnetExecuteMsg,
|
||||
funds: Vec<Coin>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
||||
let memo = msg.default_memo();
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.mixnet_contract_address(),
|
||||
&msg,
|
||||
fee,
|
||||
memo,
|
||||
funds,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<C> MixnetSigningClient for crate::Client<C>
|
||||
where
|
||||
C: SigningCosmWasmClient + Sync + Send,
|
||||
{
|
||||
async fn execute_mixnet_contract(
|
||||
&self,
|
||||
fee: Option<Fee>,
|
||||
msg: MixnetExecuteMsg,
|
||||
funds: Vec<Coin>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
self.nymd.execute_mixnet_contract(fee, msg, funds).await
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
mod coconut_bandwidth_query_client;
|
||||
mod coconut_bandwidth_signing_client;
|
||||
mod mixnet_query_client;
|
||||
mod mixnet_signing_client;
|
||||
mod multisig_query_client;
|
||||
mod multisig_signing_client;
|
||||
mod vesting_query_client;
|
||||
@@ -10,6 +12,8 @@ mod vesting_signing_client;
|
||||
|
||||
pub use coconut_bandwidth_query_client::CoconutBandwidthQueryClient;
|
||||
pub use coconut_bandwidth_signing_client::CoconutBandwidthSigningClient;
|
||||
pub use mixnet_query_client::MixnetQueryClient;
|
||||
pub use mixnet_signing_client::MixnetSigningClient;
|
||||
pub use multisig_query_client::MultisigQueryClient;
|
||||
pub use multisig_signing_client::MultisigSigningClient;
|
||||
pub use vesting_query_client::VestingQueryClient;
|
||||
|
||||
@@ -7,7 +7,7 @@ use crate::nymd::error::NymdError;
|
||||
use crate::nymd::NymdClient;
|
||||
use async_trait::async_trait;
|
||||
use cosmwasm_std::{Coin as CosmWasmCoin, Timestamp};
|
||||
use mixnet_contract_common::IdentityKey;
|
||||
use mixnet_contract_common::NodeId;
|
||||
use vesting_contract::vesting::Account;
|
||||
use vesting_contract_common::{
|
||||
messages::QueryMsg as VestingQueryMsg, AllDelegationsResponse, DelegationTimesResponse,
|
||||
@@ -76,12 +76,12 @@ pub trait VestingQueryClient {
|
||||
async fn get_delegation_timestamps(
|
||||
&self,
|
||||
address: &str,
|
||||
mix_identity: String,
|
||||
mix_id: NodeId,
|
||||
) -> Result<DelegationTimesResponse, NymdError>;
|
||||
|
||||
async fn get_all_vesting_delegations_paged(
|
||||
&self,
|
||||
start_after: Option<(u32, IdentityKey, u64)>,
|
||||
start_after: Option<(u32, NodeId, u64)>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<AllDelegationsResponse, NymdError>;
|
||||
|
||||
@@ -269,11 +269,11 @@ impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NymdClient<C> {
|
||||
async fn get_delegation_timestamps(
|
||||
&self,
|
||||
address: &str,
|
||||
mix_identity: String,
|
||||
mix_id: NodeId,
|
||||
) -> Result<DelegationTimesResponse, NymdError> {
|
||||
let request = VestingQueryMsg::GetDelegationTimes {
|
||||
address: address.to_string(),
|
||||
mix_identity,
|
||||
mix_id,
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart(self.vesting_contract_address(), &request)
|
||||
@@ -282,7 +282,7 @@ impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NymdClient<C> {
|
||||
|
||||
async fn get_all_vesting_delegations_paged(
|
||||
&self,
|
||||
start_after: Option<(u32, IdentityKey, u64)>,
|
||||
start_after: Option<(u32, NodeId, u64)>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<AllDelegationsResponse, NymdError> {
|
||||
let request = VestingQueryMsg::GetAllDelegations { start_after, limit };
|
||||
|
||||
@@ -6,14 +6,28 @@ use crate::nymd::cosmwasm_client::types::ExecuteResult;
|
||||
use crate::nymd::error::NymdError;
|
||||
use crate::nymd::{Coin, Fee, NymdClient};
|
||||
use async_trait::async_trait;
|
||||
use mixnet_contract_common::{Gateway, IdentityKey, IdentityKeyRef, MixNode};
|
||||
use mixnet_contract_common::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
|
||||
use mixnet_contract_common::{Gateway, MixNode, NodeId};
|
||||
use vesting_contract_common::messages::{ExecuteMsg as VestingExecuteMsg, VestingSpecification};
|
||||
|
||||
#[async_trait]
|
||||
pub trait VestingSigningClient {
|
||||
async fn execute_vesting_contract(
|
||||
&self,
|
||||
fee: Option<Fee>,
|
||||
msg: VestingExecuteMsg,
|
||||
funds: Vec<Coin>,
|
||||
) -> Result<ExecuteResult, NymdError>;
|
||||
|
||||
async fn vesting_update_mixnode_cost_params(
|
||||
&self,
|
||||
new_costs: MixNodeCostParams,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError>;
|
||||
|
||||
async fn vesting_update_mixnode_config(
|
||||
&self,
|
||||
profix_margin_percent: u8,
|
||||
new_config: MixNodeConfigUpdate,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError>;
|
||||
|
||||
@@ -43,10 +57,12 @@ pub trait VestingSigningClient {
|
||||
async fn vesting_bond_mixnode(
|
||||
&self,
|
||||
mix_node: MixNode,
|
||||
cost_params: MixNodeCostParams,
|
||||
owner_signature: &str,
|
||||
pledge: Coin,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError>;
|
||||
|
||||
async fn vesting_unbond_mixnode(&self, fee: Option<Fee>) -> Result<ExecuteResult, NymdError>;
|
||||
|
||||
async fn vesting_track_unbond_mixnode(
|
||||
@@ -65,21 +81,21 @@ pub trait VestingSigningClient {
|
||||
async fn vesting_track_undelegation(
|
||||
&self,
|
||||
address: &str,
|
||||
mix_identity: IdentityKey,
|
||||
mix_id: NodeId,
|
||||
amount: Coin,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError>;
|
||||
|
||||
async fn vesting_delegate_to_mixnode<'a>(
|
||||
async fn vesting_delegate_to_mixnode(
|
||||
&self,
|
||||
mix_identity: IdentityKeyRef<'a>,
|
||||
mix_id: NodeId,
|
||||
amount: Coin,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError>;
|
||||
|
||||
async fn vesting_undelegate_from_mixnode<'a>(
|
||||
async fn vesting_undelegate_from_mixnode(
|
||||
&self,
|
||||
mix_identity: IdentityKeyRef<'a>,
|
||||
mix_id: NodeId,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError>;
|
||||
|
||||
@@ -95,15 +111,46 @@ pub trait VestingSigningClient {
|
||||
|
||||
#[async_trait]
|
||||
impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient<C> {
|
||||
async fn execute_vesting_contract(
|
||||
&self,
|
||||
fee: Option<Fee>,
|
||||
msg: VestingExecuteMsg,
|
||||
funds: Vec<Coin>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
||||
let memo = msg.name().to_string();
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.vesting_contract_address(),
|
||||
&msg,
|
||||
fee,
|
||||
memo,
|
||||
funds,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn vesting_update_mixnode_cost_params(
|
||||
&self,
|
||||
new_costs: MixNodeCostParams,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
self.execute_vesting_contract(
|
||||
fee,
|
||||
VestingExecuteMsg::UpdateMixnodeCostParams { new_costs },
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn vesting_update_mixnode_config(
|
||||
&self,
|
||||
profit_margin_percent: u8,
|
||||
new_config: MixNodeConfigUpdate,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
||||
let req = VestingExecuteMsg::UpdateMixnodeConfig {
|
||||
profit_margin_percent,
|
||||
};
|
||||
let req = VestingExecuteMsg::UpdateMixnodeConfig { new_config };
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
@@ -203,26 +250,22 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
|
||||
async fn vesting_bond_mixnode(
|
||||
&self,
|
||||
mix_node: MixNode,
|
||||
cost_params: MixNodeCostParams,
|
||||
owner_signature: &str,
|
||||
pledge: Coin,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
||||
let req = VestingExecuteMsg::BondMixnode {
|
||||
mix_node,
|
||||
owner_signature: owner_signature.to_string(),
|
||||
amount: pledge.into(),
|
||||
};
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.vesting_contract_address(),
|
||||
&req,
|
||||
fee,
|
||||
"VestingContract::BondMixnode",
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
self.execute_vesting_contract(
|
||||
fee,
|
||||
VestingExecuteMsg::BondMixnode {
|
||||
mix_node,
|
||||
cost_params,
|
||||
owner_signature: owner_signature.to_string(),
|
||||
amount: pledge.into(),
|
||||
},
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn vesting_unbond_mixnode(&self, fee: Option<Fee>) -> Result<ExecuteResult, NymdError> {
|
||||
@@ -262,6 +305,7 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn withdraw_vested_coins(
|
||||
&self,
|
||||
amount: Coin,
|
||||
@@ -282,72 +326,54 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn vesting_track_undelegation(
|
||||
&self,
|
||||
address: &str,
|
||||
mix_identity: IdentityKey,
|
||||
mix_id: NodeId,
|
||||
amount: Coin,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
||||
let req = VestingExecuteMsg::TrackUndelegation {
|
||||
owner: address.to_string(),
|
||||
mix_identity,
|
||||
amount: amount.into(),
|
||||
};
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.vesting_contract_address(),
|
||||
&req,
|
||||
fee,
|
||||
"VestingContract::TrackUndelegation",
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
async fn vesting_delegate_to_mixnode<'a>(
|
||||
&self,
|
||||
mix_identity: IdentityKeyRef<'a>,
|
||||
amount: Coin,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
||||
let req = VestingExecuteMsg::DelegateToMixnode {
|
||||
mix_identity: mix_identity.into(),
|
||||
amount: amount.into(),
|
||||
};
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.vesting_contract_address(),
|
||||
&req,
|
||||
fee,
|
||||
"VestingContract::DelegateToMixnode",
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
self.execute_vesting_contract(
|
||||
fee,
|
||||
VestingExecuteMsg::TrackUndelegation {
|
||||
owner: address.to_string(),
|
||||
mix_id,
|
||||
amount: amount.into(),
|
||||
},
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn vesting_undelegate_from_mixnode<'a>(
|
||||
async fn vesting_delegate_to_mixnode(
|
||||
&self,
|
||||
mix_identity: IdentityKeyRef<'a>,
|
||||
mix_id: NodeId,
|
||||
amount: Coin,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
||||
let req = VestingExecuteMsg::UndelegateFromMixnode {
|
||||
mix_identity: mix_identity.into(),
|
||||
};
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.vesting_contract_address(),
|
||||
&req,
|
||||
fee,
|
||||
"VestingContract::UndelegateFromMixnode",
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
self.execute_vesting_contract(
|
||||
fee,
|
||||
VestingExecuteMsg::DelegateToMixnode {
|
||||
mix_id,
|
||||
amount: amount.into(),
|
||||
},
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn vesting_undelegate_from_mixnode(
|
||||
&self,
|
||||
mix_id: NodeId,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
self.execute_vesting_contract(
|
||||
fee,
|
||||
VestingExecuteMsg::UndelegateFromMixnode { mix_id },
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn create_periodic_vesting_account(
|
||||
|
||||
@@ -72,7 +72,7 @@ impl DirectSecp256k1HdWallet {
|
||||
}
|
||||
|
||||
fn derive_keypair(&self, hd_path: &DerivationPath) -> Result<Secp256k1Keypair, NymdError> {
|
||||
let extended_private_key = XPrv::derive_from_path(&self.seed, hd_path)?;
|
||||
let extended_private_key = XPrv::derive_from_path(self.seed, hd_path)?;
|
||||
|
||||
let private_key: SigningKey = extended_private_key.into();
|
||||
let public_key = private_key.public_key();
|
||||
@@ -207,8 +207,9 @@ impl DirectSecp256k1HdWalletBuilder {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use network_defaults::NymNetworkDetails;
|
||||
|
||||
use super::*;
|
||||
use network_defaults::all::Network::*;
|
||||
|
||||
#[test]
|
||||
fn generating_account_addresses() {
|
||||
@@ -218,7 +219,9 @@ mod tests {
|
||||
"acquire rebel spot skin gun such erupt pull swear must define ill chief turtle today flower chunk truth battle claw rigid detail gym feel",
|
||||
"step income throw wheat mobile ship wave drink pool sudden upset jaguar bar globe rifle spice frost bless glimpse size regular carry aspect ball"
|
||||
];
|
||||
let prefix = MAINNET.bech32_prefix();
|
||||
let prefix = NymNetworkDetails::new_mainnet()
|
||||
.chain_details
|
||||
.bech32_account_prefix;
|
||||
|
||||
let addrs = vec![
|
||||
"n1jw6mp7d5xqc7w6xm79lha27glmd0vdt3l9artf",
|
||||
|
||||
@@ -3,17 +3,18 @@
|
||||
|
||||
use crate::validator_api::error::ValidatorAPIError;
|
||||
use crate::validator_api::routes::{CORE_STATUS_COUNT, SINCE_ARG};
|
||||
use mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixNodeBond};
|
||||
use mixnet_contract_common::mixnode::MixNodeDetails;
|
||||
use mixnet_contract_common::{GatewayBond, IdentityKeyRef, NodeId};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use url::Url;
|
||||
use validator_api_requests::coconut::{
|
||||
BlindSignRequestBody, BlindedSignatureResponse, CosmosAddressResponse, VerificationKeyResponse,
|
||||
VerifyCredentialBody, VerifyCredentialResponse,
|
||||
};
|
||||
use validator_api_requests::models::{
|
||||
CoreNodeStatusResponse, InclusionProbabilityResponse, MixNodeBondAnnotated,
|
||||
MixnodeStatusResponse, RewardEstimationResponse, StakeSaturationResponse, UptimeResponse,
|
||||
GatewayCoreStatusResponse, InclusionProbabilityResponse, MixNodeBondAnnotated,
|
||||
MixnodeCoreStatusResponse, MixnodeStatusResponse, RewardEstimationResponse,
|
||||
StakeSaturationResponse, UptimeResponse,
|
||||
};
|
||||
|
||||
pub mod error;
|
||||
@@ -57,6 +58,7 @@ impl Client {
|
||||
V: AsRef<str>,
|
||||
{
|
||||
let url = create_api_url(&self.url, path, params);
|
||||
log::trace!("url: {:?}", url.as_str());
|
||||
Ok(self.reqwest_client.get(url).send().await?.json().await?)
|
||||
}
|
||||
|
||||
@@ -83,7 +85,7 @@ impl Client {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorAPIError> {
|
||||
pub async fn get_mixnodes(&self) -> Result<Vec<MixNodeDetails>, ValidatorAPIError> {
|
||||
self.query_validator_api(&[routes::API_VERSION, routes::MIXNODES], NO_PARAMS)
|
||||
.await
|
||||
}
|
||||
@@ -103,7 +105,7 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_active_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorAPIError> {
|
||||
pub async fn get_active_mixnodes(&self) -> Result<Vec<MixNodeDetails>, ValidatorAPIError> {
|
||||
self.query_validator_api(
|
||||
&[routes::API_VERSION, routes::MIXNODES, routes::ACTIVE],
|
||||
NO_PARAMS,
|
||||
@@ -126,7 +128,7 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_rewarded_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorAPIError> {
|
||||
pub async fn get_rewarded_mixnodes(&self) -> Result<Vec<MixNodeDetails>, ValidatorAPIError> {
|
||||
self.query_validator_api(
|
||||
&[routes::API_VERSION, routes::MIXNODES, routes::REWARDED],
|
||||
NO_PARAMS,
|
||||
@@ -149,28 +151,11 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_probs_mixnode_rewarded(
|
||||
&self,
|
||||
mixnode_id: &str,
|
||||
) -> Result<HashMap<String, f32>, ValidatorAPIError> {
|
||||
self.query_validator_api(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::MIXNODES,
|
||||
routes::REWARDED,
|
||||
routes::INCLUSION_CHANCE,
|
||||
mixnode_id,
|
||||
],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_gateway_core_status_count(
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
since: Option<i64>,
|
||||
) -> Result<CoreNodeStatusResponse, ValidatorAPIError> {
|
||||
) -> Result<GatewayCoreStatusResponse, ValidatorAPIError> {
|
||||
if let Some(since) = since {
|
||||
self.query_validator_api(
|
||||
&[
|
||||
@@ -199,16 +184,16 @@ impl Client {
|
||||
|
||||
pub async fn get_mixnode_core_status_count(
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
mix_id: NodeId,
|
||||
since: Option<i64>,
|
||||
) -> Result<CoreNodeStatusResponse, ValidatorAPIError> {
|
||||
) -> Result<MixnodeCoreStatusResponse, ValidatorAPIError> {
|
||||
if let Some(since) = since {
|
||||
self.query_validator_api(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS_ROUTES,
|
||||
routes::MIXNODE,
|
||||
identity,
|
||||
&mix_id.to_string(),
|
||||
CORE_STATUS_COUNT,
|
||||
],
|
||||
&[(SINCE_ARG, since.to_string())],
|
||||
@@ -220,7 +205,8 @@ impl Client {
|
||||
routes::API_VERSION,
|
||||
routes::STATUS_ROUTES,
|
||||
routes::MIXNODE,
|
||||
identity,
|
||||
&mix_id.to_string(),
|
||||
CORE_STATUS_COUNT,
|
||||
],
|
||||
NO_PARAMS,
|
||||
)
|
||||
@@ -230,14 +216,14 @@ impl Client {
|
||||
|
||||
pub async fn get_mixnode_status(
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
mix_id: NodeId,
|
||||
) -> Result<MixnodeStatusResponse, ValidatorAPIError> {
|
||||
self.query_validator_api(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS_ROUTES,
|
||||
routes::MIXNODE,
|
||||
identity,
|
||||
&mix_id.to_string(),
|
||||
routes::STATUS,
|
||||
],
|
||||
NO_PARAMS,
|
||||
@@ -247,14 +233,14 @@ impl Client {
|
||||
|
||||
pub async fn get_mixnode_reward_estimation(
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
mix_id: NodeId,
|
||||
) -> Result<RewardEstimationResponse, ValidatorAPIError> {
|
||||
self.query_validator_api(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS_ROUTES,
|
||||
routes::MIXNODE,
|
||||
identity,
|
||||
&mix_id.to_string(),
|
||||
routes::REWARD_ESTIMATION,
|
||||
],
|
||||
NO_PARAMS,
|
||||
@@ -264,14 +250,14 @@ impl Client {
|
||||
|
||||
pub async fn get_mixnode_stake_saturation(
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
mix_id: NodeId,
|
||||
) -> Result<StakeSaturationResponse, ValidatorAPIError> {
|
||||
self.query_validator_api(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS_ROUTES,
|
||||
routes::MIXNODE,
|
||||
identity,
|
||||
&mix_id.to_string(),
|
||||
routes::STAKE_SATURATION,
|
||||
],
|
||||
NO_PARAMS,
|
||||
@@ -281,14 +267,14 @@ impl Client {
|
||||
|
||||
pub async fn get_mixnode_inclusion_probability(
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
mix_id: NodeId,
|
||||
) -> Result<InclusionProbabilityResponse, ValidatorAPIError> {
|
||||
self.query_validator_api(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS_ROUTES,
|
||||
routes::MIXNODE,
|
||||
identity,
|
||||
&mix_id.to_string(),
|
||||
routes::INCLUSION_CHANCE,
|
||||
],
|
||||
NO_PARAMS,
|
||||
@@ -298,27 +284,14 @@ impl Client {
|
||||
|
||||
pub async fn get_mixnode_avg_uptime(
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
mix_id: NodeId,
|
||||
) -> Result<UptimeResponse, ValidatorAPIError> {
|
||||
self.query_validator_api(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS_ROUTES,
|
||||
routes::MIXNODE,
|
||||
identity,
|
||||
routes::AVG_UPTIME,
|
||||
],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_mixnode_avg_uptimes(&self) -> Result<Vec<UptimeResponse>, ValidatorAPIError> {
|
||||
self.query_validator_api(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS_ROUTES,
|
||||
routes::MIXNODES,
|
||||
&mix_id.to_string(),
|
||||
routes::AVG_UPTIME,
|
||||
],
|
||||
NO_PARAMS,
|
||||
|
||||
@@ -28,6 +28,11 @@ pub fn pretty_cosmwasm_coin(coin: &CosmWasmCoin) -> String {
|
||||
format!("{} {}", amount, denom)
|
||||
}
|
||||
|
||||
pub fn pretty_decimal_with_denom(value: Decimal, denom: &str) -> String {
|
||||
// TODO: we might have to truncate the value here (that's why I moved it to separate function)
|
||||
format!("{} {}", value, denom)
|
||||
}
|
||||
|
||||
pub fn show_error<E>(e: E)
|
||||
where
|
||||
E: Display,
|
||||
|
||||
@@ -51,7 +51,7 @@ pub async fn query_balance(
|
||||
return;
|
||||
}
|
||||
|
||||
let denom = args.denom.unwrap_or_else(|| "".to_string());
|
||||
let denom = args.denom.unwrap_or_default();
|
||||
|
||||
for coin in coins {
|
||||
if denom.is_empty() || denom.eq_ignore_ascii_case(&coin.denom) {
|
||||
|
||||
@@ -4,12 +4,16 @@
|
||||
use crate::context::SigningClient;
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
use mixnet_contract_common::Coin;
|
||||
use mixnet_contract_common::{Coin, NodeId};
|
||||
use validator_client::nymd::traits::{MixnetQueryClient, MixnetSigningClient};
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
#[clap(long)]
|
||||
pub identity_key: String,
|
||||
pub mix_id: Option<NodeId>,
|
||||
|
||||
#[clap(long)]
|
||||
pub identity_key: Option<String>,
|
||||
|
||||
#[clap(long)]
|
||||
pub amount: u128,
|
||||
@@ -20,10 +24,25 @@ pub async fn delegate_to_mixnode(args: Args, client: SigningClient) {
|
||||
|
||||
info!("Starting delegation to mixnode");
|
||||
|
||||
let mix_id = match args.mix_id {
|
||||
Some(mix_id) => mix_id,
|
||||
None => {
|
||||
let identity_key = args
|
||||
.identity_key
|
||||
.expect("either mix_id or mix_identity has to be specified");
|
||||
let node_details = client
|
||||
.get_mixnode_details_by_identity(identity_key)
|
||||
.await
|
||||
.expect("contract query failed")
|
||||
.expect("mixnode with the specified identity doesnt exist");
|
||||
node_details.mix_id()
|
||||
}
|
||||
};
|
||||
|
||||
let coin = Coin::new(args.amount, denom);
|
||||
|
||||
let res = client
|
||||
.delegate_to_mixnode(&*args.identity_key, coin.into(), None)
|
||||
.delegate_to_mixnode(mix_id, coin.into(), None)
|
||||
.await
|
||||
.expect("failed to delegate to mixnode!");
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ use crate::context::SigningClientWithValidatorAPI;
|
||||
use crate::utils::{pretty_cosmwasm_coin, show_error_passthrough};
|
||||
|
||||
use comfy_table::Table;
|
||||
use mixnet_contract_common::mixnode::DelegationEvent;
|
||||
use mixnet_contract_common::Delegation;
|
||||
use cosmwasm_std::Addr;
|
||||
use mixnet_contract_common::{Delegation, PendingEpochEvent, PendingEpochEventData};
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {}
|
||||
@@ -26,19 +26,7 @@ pub async fn execute(_args: Args, client: SigningClientWithValidatorAPI) {
|
||||
.map_err(show_error_passthrough);
|
||||
|
||||
let mixnet_contract_events = client
|
||||
.nymd
|
||||
.get_pending_delegation_events(client.nymd.address().to_string(), None)
|
||||
.await
|
||||
.map_err(show_error_passthrough);
|
||||
|
||||
let vesting_contract = client.nymd.vesting_contract_address();
|
||||
|
||||
let vesting_contract_events = client
|
||||
.nymd
|
||||
.get_pending_delegation_events(
|
||||
client.nymd.address().to_string(),
|
||||
Some(vesting_contract.to_string()),
|
||||
)
|
||||
.get_all_nymd_pending_epoch_events()
|
||||
.await
|
||||
.map_err(show_error_passthrough);
|
||||
|
||||
@@ -58,13 +46,6 @@ pub async fn execute(_args: Args, client: SigningClientWithValidatorAPI) {
|
||||
print_delegation_events(res, &client).await;
|
||||
}
|
||||
}
|
||||
if let Ok(res) = vesting_contract_events {
|
||||
if !res.is_empty() {
|
||||
println!();
|
||||
println!("Pending delegations (locked tokens):");
|
||||
print_delegation_events(res, &client).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn to_iso_timestamp(block_height: u32, client: &SigningClientWithValidatorAPI) -> String {
|
||||
@@ -77,14 +58,17 @@ async fn to_iso_timestamp(block_height: u32, client: &SigningClientWithValidator
|
||||
async fn print_delegations(delegations: Vec<Delegation>, client: &SigningClientWithValidatorAPI) {
|
||||
let mut table = Table::new();
|
||||
|
||||
table.set_header(vec!["Timestamp", "Identity Key", "Delegation", "Proxy"]);
|
||||
table.set_header(vec!["Timestamp", "Mix Id", "Delegation", "Proxy"]);
|
||||
|
||||
for delegation in delegations {
|
||||
table.add_row(vec![
|
||||
to_iso_timestamp(delegation.block_height as u32, client).await,
|
||||
delegation.node_identity.to_string(),
|
||||
to_iso_timestamp(delegation.height as u32, client).await,
|
||||
delegation.mix_id.to_string(),
|
||||
pretty_cosmwasm_coin(&delegation.amount),
|
||||
format!("{:?}", delegation.proxy),
|
||||
delegation
|
||||
.proxy
|
||||
.map(Addr::into_string)
|
||||
.unwrap_or_else(|| "-".into()),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -92,36 +76,53 @@ async fn print_delegations(delegations: Vec<Delegation>, client: &SigningClientW
|
||||
}
|
||||
|
||||
async fn print_delegation_events(
|
||||
events: Vec<DelegationEvent>,
|
||||
events: Vec<PendingEpochEvent>,
|
||||
client: &SigningClientWithValidatorAPI,
|
||||
) {
|
||||
let mut table = Table::new();
|
||||
|
||||
table.set_header(vec![
|
||||
"Timestamp",
|
||||
"Identity Key",
|
||||
"Mix id",
|
||||
"Delegation",
|
||||
"Event Type",
|
||||
"Proxy",
|
||||
]);
|
||||
|
||||
for event in events {
|
||||
match event {
|
||||
DelegationEvent::Delegate(delegation) => {
|
||||
table.add_row(vec![
|
||||
to_iso_timestamp(delegation.block_height as u32, client).await,
|
||||
delegation.node_identity.to_string(),
|
||||
pretty_cosmwasm_coin(&delegation.amount),
|
||||
"Delegate".to_string(),
|
||||
]);
|
||||
match event.event {
|
||||
PendingEpochEventData::Delegate {
|
||||
owner,
|
||||
mix_id,
|
||||
amount,
|
||||
proxy,
|
||||
} => {
|
||||
if owner.as_str() == client.nymd.address().as_ref() {
|
||||
table.add_row(vec![
|
||||
"not-sure-if-applicable".into(),
|
||||
mix_id.to_string(),
|
||||
pretty_cosmwasm_coin(&amount),
|
||||
"Delegate".to_string(),
|
||||
proxy.map(Addr::into_string).unwrap_or_else(|| "-".into()),
|
||||
]);
|
||||
}
|
||||
}
|
||||
DelegationEvent::Undelegate(undelegate) => {
|
||||
table.add_row(vec![
|
||||
to_iso_timestamp(undelegate.block_height() as u32, client).await,
|
||||
undelegate.mix_identity().to_string(),
|
||||
"-".to_string(),
|
||||
"Undelegate".to_string(),
|
||||
]);
|
||||
PendingEpochEventData::Undelegate {
|
||||
owner,
|
||||
mix_id,
|
||||
proxy,
|
||||
} => {
|
||||
if owner.as_str() == client.nymd.address().as_ref() {
|
||||
table.add_row(vec![
|
||||
"not-sure-if-applicable".into(),
|
||||
mix_id.to_string(),
|
||||
"-".to_string(),
|
||||
"Undelegate".to_string(),
|
||||
proxy.map(Addr::into_string).unwrap_or_else(|| "-".into()),
|
||||
]);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,18 +4,38 @@
|
||||
use crate::context::SigningClient;
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
use mixnet_contract_common::NodeId;
|
||||
use validator_client::nymd::traits::{MixnetQueryClient, MixnetSigningClient};
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
#[clap(long)]
|
||||
pub identity_key: String,
|
||||
pub mix_id: Option<NodeId>,
|
||||
|
||||
#[clap(long)]
|
||||
pub identity_key: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn claim_delegator_reward(args: Args, client: SigningClient) {
|
||||
info!("Claim delegator reward");
|
||||
|
||||
let mix_id = match args.mix_id {
|
||||
Some(mix_id) => mix_id,
|
||||
None => {
|
||||
let identity_key = args
|
||||
.identity_key
|
||||
.expect("either mix_id or mix_identity has to be specified");
|
||||
let node_details = client
|
||||
.get_mixnode_details_by_identity(identity_key)
|
||||
.await
|
||||
.expect("contract query failed")
|
||||
.expect("mixnode with the specified identity doesnt exist");
|
||||
node_details.mix_id()
|
||||
}
|
||||
};
|
||||
|
||||
let res = client
|
||||
.execute_claim_delegator_reward(args.identity_key, None)
|
||||
.withdraw_delegator_reward(mix_id, None)
|
||||
.await
|
||||
.expect("failed to claim delegator-reward");
|
||||
|
||||
|
||||
+22
-2
@@ -4,18 +4,38 @@
|
||||
use crate::context::SigningClient;
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
use mixnet_contract_common::NodeId;
|
||||
use validator_client::nymd::traits::{MixnetQueryClient, MixnetSigningClient};
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
#[clap(long)]
|
||||
pub identity: String,
|
||||
pub mix_id: Option<NodeId>,
|
||||
|
||||
#[clap(long)]
|
||||
pub identity_key: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn vesting_claim_delegator_reward(args: Args, client: SigningClient) {
|
||||
info!("Claim vesting delegator reward");
|
||||
|
||||
let mix_id = match args.mix_id {
|
||||
Some(mix_id) => mix_id,
|
||||
None => {
|
||||
let identity_key = args
|
||||
.identity_key
|
||||
.expect("either mix_id or mix_identity has to be specified");
|
||||
let node_details = client
|
||||
.get_mixnode_details_by_identity(identity_key)
|
||||
.await
|
||||
.expect("contract query failed")
|
||||
.expect("mixnode with the specified identity doesnt exist");
|
||||
node_details.mix_id()
|
||||
}
|
||||
};
|
||||
|
||||
let res = client
|
||||
.execute_vesting_claim_delegator_reward(args.identity, None)
|
||||
.withdraw_delegator_reward_on_behalf(client.address().clone(), mix_id, None)
|
||||
.await
|
||||
.expect("failed to claim vesting delegator-reward");
|
||||
|
||||
|
||||
@@ -4,18 +4,38 @@
|
||||
use crate::context::SigningClient;
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
use mixnet_contract_common::NodeId;
|
||||
use validator_client::nymd::traits::{MixnetQueryClient, MixnetSigningClient};
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
#[clap(long)]
|
||||
pub identity_key: String,
|
||||
pub mix_id: Option<NodeId>,
|
||||
|
||||
#[clap(long)]
|
||||
pub identity_key: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn undelegate_from_mixnode(args: Args, client: SigningClient) {
|
||||
info!("removing stake from mix-node");
|
||||
|
||||
let mix_id = match args.mix_id {
|
||||
Some(mix_id) => mix_id,
|
||||
None => {
|
||||
let identity_key = args
|
||||
.identity_key
|
||||
.expect("either mix_id or mix_identity has to be specified");
|
||||
let node_details = client
|
||||
.get_mixnode_details_by_identity(identity_key)
|
||||
.await
|
||||
.expect("contract query failed")
|
||||
.expect("mixnode with the specified identity doesnt exist");
|
||||
node_details.mix_id()
|
||||
}
|
||||
};
|
||||
|
||||
let res = client
|
||||
.remove_mixnode_delegation(&*args.identity_key, None)
|
||||
.undelegate_from_mixnode(mix_id, None)
|
||||
.await
|
||||
.expect("failed to remove stake from mixnode!");
|
||||
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
|
||||
use mixnet_contract_common::Coin;
|
||||
use mixnet_contract_common::{Coin, NodeId};
|
||||
use validator_client::nymd::traits::MixnetQueryClient;
|
||||
use validator_client::nymd::VestingSigningClient;
|
||||
|
||||
use crate::context::SigningClient;
|
||||
@@ -12,7 +13,10 @@ use crate::context::SigningClient;
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
#[clap(long)]
|
||||
pub identity_key: String,
|
||||
pub mix_id: Option<NodeId>,
|
||||
|
||||
#[clap(long)]
|
||||
pub identity_key: Option<String>,
|
||||
|
||||
#[clap(long)]
|
||||
pub amount: u128,
|
||||
@@ -23,10 +27,25 @@ pub async fn vesting_delegate_to_mixnode(args: Args, client: SigningClient) {
|
||||
|
||||
info!("Starting vesting delegation to mixnode");
|
||||
|
||||
let mix_id = match args.mix_id {
|
||||
Some(mix_id) => mix_id,
|
||||
None => {
|
||||
let identity_key = args
|
||||
.identity_key
|
||||
.expect("either mix_id or mix_identity has to be specified");
|
||||
let node_details = client
|
||||
.get_mixnode_details_by_identity(identity_key)
|
||||
.await
|
||||
.expect("contract query failed")
|
||||
.expect("mixnode with the specified identity doesnt exist");
|
||||
node_details.mix_id()
|
||||
}
|
||||
};
|
||||
|
||||
let coin = Coin::new(args.amount, denom);
|
||||
|
||||
let res = client
|
||||
.vesting_delegate_to_mixnode(&*args.identity_key, coin.into(), None)
|
||||
.vesting_delegate_to_mixnode(mix_id, coin.into(), None)
|
||||
.await
|
||||
.expect("failed to delegate to mixnode!");
|
||||
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
|
||||
use mixnet_contract_common::NodeId;
|
||||
use validator_client::nymd::traits::MixnetQueryClient;
|
||||
use validator_client::nymd::VestingSigningClient;
|
||||
|
||||
use crate::context::SigningClient;
|
||||
@@ -11,14 +12,32 @@ use crate::context::SigningClient;
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
#[clap(long)]
|
||||
pub identity_key: String,
|
||||
pub mix_id: Option<NodeId>,
|
||||
|
||||
#[clap(long)]
|
||||
pub identity_key: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn vesting_undelegate_from_mixnode(args: Args, client: SigningClient) {
|
||||
info!("removing stake from vesting mix-node");
|
||||
|
||||
let mix_id = match args.mix_id {
|
||||
Some(mix_id) => mix_id,
|
||||
None => {
|
||||
let identity_key = args
|
||||
.identity_key
|
||||
.expect("either mix_id or mix_identity has to be specified");
|
||||
let node_details = client
|
||||
.get_mixnode_details_by_identity(identity_key)
|
||||
.await
|
||||
.expect("contract query failed")
|
||||
.expect("mixnode with the specified identity doesnt exist");
|
||||
node_details.mix_id()
|
||||
}
|
||||
};
|
||||
|
||||
let res = client
|
||||
.vesting_undelegate_from_mixnode(&*args.identity_key, None)
|
||||
.vesting_undelegate_from_mixnode(mix_id, None)
|
||||
.await
|
||||
.expect("failed to remove stake from vesting account on mixnode!");
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ use clap::Parser;
|
||||
use log::{info, warn};
|
||||
use mixnet_contract_common::Coin;
|
||||
use network_defaults::{DEFAULT_CLIENT_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT};
|
||||
use validator_client::nymd::traits::MixnetSigningClient;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
use crate::context::SigningClient;
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
use validator_client::nymd::traits::MixnetSigningClient;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {}
|
||||
|
||||
@@ -68,7 +68,7 @@ pub async fn vesting_bond_gateway(client: SigningClient, args: Args, denom: &str
|
||||
let coin = Coin::new(args.amount, denom);
|
||||
|
||||
let res = client
|
||||
.vesting_bond_gateway(gateway, &*args.signature, coin.into(), None)
|
||||
.vesting_bond_gateway(gateway, &args.signature, coin.into(), None)
|
||||
.await
|
||||
.expect("failed to bond gateway!");
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
use crate::context::SigningClient;
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
use validator_client::nymd::traits::MixnetSigningClient;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {}
|
||||
|
||||
@@ -2,12 +2,15 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use clap::Parser;
|
||||
use cosmwasm_std::Uint128;
|
||||
use log::{info, warn};
|
||||
|
||||
use mixnet_contract_common::Coin;
|
||||
use mixnet_contract_common::{Coin, MixNodeCostParams, Percent};
|
||||
use network_defaults::{
|
||||
DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT,
|
||||
};
|
||||
use validator_client::nymd::traits::MixnetSigningClient;
|
||||
use validator_client::nymd::CosmWasmCoin;
|
||||
|
||||
use crate::context::SigningClient;
|
||||
|
||||
@@ -40,6 +43,12 @@ pub struct Args {
|
||||
#[clap(long)]
|
||||
pub profit_margin_percent: Option<u8>,
|
||||
|
||||
#[clap(
|
||||
long,
|
||||
help = "operating cost in current DENOMINATION (so it would be 'unym', rather than 'nym')"
|
||||
)]
|
||||
pub interval_operating_cost: Option<u128>,
|
||||
|
||||
#[clap(
|
||||
long,
|
||||
help = "bonding amount in current DENOMINATION (so it would be 'unym', rather than 'nym')"
|
||||
@@ -71,13 +80,23 @@ pub async fn bond_mixnode(args: Args, client: SigningClient) {
|
||||
sphinx_key: args.sphinx_key,
|
||||
identity_key: args.identity_key,
|
||||
version: args.version,
|
||||
profit_margin_percent: args.profit_margin_percent.unwrap_or(10),
|
||||
};
|
||||
|
||||
let coin = Coin::new(args.amount, denom);
|
||||
|
||||
let cost_params = MixNodeCostParams {
|
||||
profit_margin_percent: Percent::from_percentage_value(
|
||||
args.profit_margin_percent.unwrap_or(10) as u64,
|
||||
)
|
||||
.unwrap(),
|
||||
interval_operating_cost: CosmWasmCoin {
|
||||
denom: denom.into(),
|
||||
amount: Uint128::new(args.interval_operating_cost.unwrap_or(40_000_000)),
|
||||
},
|
||||
};
|
||||
|
||||
let res = client
|
||||
.bond_mixnode(mixnode, args.signature, coin.into(), None)
|
||||
.bond_mixnode(mixnode, cost_params, args.signature, coin.into(), None)
|
||||
.await
|
||||
.expect("failed to bond mixnode!");
|
||||
|
||||
|
||||
+2
-1
@@ -4,6 +4,7 @@
|
||||
use crate::context::SigningClient;
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
use validator_client::nymd::traits::MixnetSigningClient;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {}
|
||||
@@ -12,7 +13,7 @@ pub async fn claim_operator_reward(_args: Args, client: SigningClient) {
|
||||
info!("Claim operator reward");
|
||||
|
||||
let res = client
|
||||
.execute_claim_operator_reward(None)
|
||||
.withdraw_operator_reward(None)
|
||||
.await
|
||||
.expect("failed to claim operator reward");
|
||||
|
||||
|
||||
+2
-1
@@ -4,6 +4,7 @@
|
||||
use crate::context::SigningClient;
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
use validator_client::nymd::traits::MixnetSigningClient;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
@@ -15,7 +16,7 @@ pub async fn vesting_claim_operator_reward(client: SigningClient) {
|
||||
info!("Claim vesting operator reward");
|
||||
|
||||
let res = client
|
||||
.execute_vesting_claim_operator_reward(None)
|
||||
.withdraw_operator_reward_on_behalf(client.address().clone(), None)
|
||||
.await
|
||||
.expect("failed to claim vesting operator reward");
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
|
||||
use clap::{Args, Subcommand};
|
||||
|
||||
pub mod update_profit_percent;
|
||||
pub mod vesting_update_profit_percent;
|
||||
pub mod update_config;
|
||||
pub mod vesting_update_config;
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
|
||||
@@ -15,8 +15,12 @@ pub struct MixnetOperatorsMixnodeSettings {
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum MixnetOperatorsMixnodeSettingsCommands {
|
||||
/// Update profit percentage
|
||||
UpdateProfitPercentage(update_profit_percent::Args),
|
||||
/// Update profit percentage for a mixnode bonded with locked tokens
|
||||
VestingUpdateProfitPercentage(vesting_update_profit_percent::Args),
|
||||
/// Update mixnode configuration
|
||||
UpdateConfig(update_config::Args),
|
||||
/// Update mixnode configuration for a mixnode bonded with locked tokens
|
||||
VestingUpdateConfig(vesting_update_config::Args),
|
||||
/// Update mixnode cost parameters
|
||||
UpdateCostParameters,
|
||||
/// Update mixnode cost parameters for a mixnode bonded with locked tokens
|
||||
VestingUpdateCostParameters,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::context::SigningClient;
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
use mixnet_contract_common::MixNodeConfigUpdate;
|
||||
use validator_client::nymd::traits::{MixnetQueryClient, MixnetSigningClient};
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
#[clap(long)]
|
||||
pub host: Option<String>,
|
||||
|
||||
#[clap(long)]
|
||||
pub mix_port: Option<u16>,
|
||||
|
||||
#[clap(long)]
|
||||
pub verloc_port: Option<u16>,
|
||||
|
||||
#[clap(long)]
|
||||
pub http_api_port: Option<u16>,
|
||||
|
||||
#[clap(long)]
|
||||
pub version: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn update_config(args: Args, client: SigningClient) {
|
||||
info!("Update mix node config!");
|
||||
|
||||
let current_details = match client
|
||||
.get_owned_mixnode(client.address())
|
||||
.await
|
||||
.expect("failed to query the chain for mixnode details")
|
||||
.mixnode_details
|
||||
{
|
||||
Some(details) => details,
|
||||
None => {
|
||||
log::warn!("this operator does not own a mixnode to update");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let update = MixNodeConfigUpdate {
|
||||
host: args
|
||||
.host
|
||||
.unwrap_or(current_details.bond_information.mix_node.host),
|
||||
mix_port: args
|
||||
.mix_port
|
||||
.unwrap_or(current_details.bond_information.mix_node.mix_port),
|
||||
verloc_port: args
|
||||
.verloc_port
|
||||
.unwrap_or(current_details.bond_information.mix_node.verloc_port),
|
||||
http_api_port: args
|
||||
.http_api_port
|
||||
.unwrap_or(current_details.bond_information.mix_node.http_api_port),
|
||||
version: args
|
||||
.version
|
||||
.unwrap_or(current_details.bond_information.mix_node.version),
|
||||
};
|
||||
|
||||
let res = client
|
||||
.update_mixnode_config(update, None)
|
||||
.await
|
||||
.expect("updating mix-node config");
|
||||
|
||||
info!("mixnode config updated: {:?}", res)
|
||||
}
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::context::SigningClient;
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
#[clap(long)]
|
||||
pub profit_percent: u8,
|
||||
}
|
||||
|
||||
pub async fn update_profit_percent(args: Args, client: SigningClient) {
|
||||
info!("Update mix node profit percent - get those rewards!");
|
||||
|
||||
//profit percent between 1-100
|
||||
let res = client
|
||||
.update_mixnode_config(args.profit_percent, None)
|
||||
.await
|
||||
.expect("updating mix-node profit percent");
|
||||
|
||||
info!("profit percentage updated: {:?}", res)
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::context::SigningClient;
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
use mixnet_contract_common::MixNodeConfigUpdate;
|
||||
use validator_client::nymd::traits::MixnetQueryClient;
|
||||
use validator_client::nymd::VestingSigningClient;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
#[clap(long)]
|
||||
pub host: Option<String>,
|
||||
|
||||
#[clap(long)]
|
||||
pub mix_port: Option<u16>,
|
||||
|
||||
#[clap(long)]
|
||||
pub verloc_port: Option<u16>,
|
||||
|
||||
#[clap(long)]
|
||||
pub http_api_port: Option<u16>,
|
||||
|
||||
#[clap(long)]
|
||||
pub version: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn vesting_update_config(client: SigningClient, args: Args) {
|
||||
info!("Update vesting mix node config!");
|
||||
|
||||
let current_details = match client
|
||||
.get_owned_mixnode(client.address())
|
||||
.await
|
||||
.expect("failed to query the chain for mixnode details")
|
||||
.mixnode_details
|
||||
{
|
||||
Some(details) => details,
|
||||
None => {
|
||||
log::warn!("this operator does not own a mixnode to update");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let update = MixNodeConfigUpdate {
|
||||
host: args
|
||||
.host
|
||||
.unwrap_or(current_details.bond_information.mix_node.host),
|
||||
mix_port: args
|
||||
.mix_port
|
||||
.unwrap_or(current_details.bond_information.mix_node.mix_port),
|
||||
verloc_port: args
|
||||
.verloc_port
|
||||
.unwrap_or(current_details.bond_information.mix_node.verloc_port),
|
||||
http_api_port: args
|
||||
.http_api_port
|
||||
.unwrap_or(current_details.bond_information.mix_node.http_api_port),
|
||||
version: args
|
||||
.version
|
||||
.unwrap_or(current_details.bond_information.mix_node.version),
|
||||
};
|
||||
|
||||
let res = client
|
||||
.vesting_update_mixnode_config(update, None)
|
||||
.await
|
||||
.expect("updating vesting mix-node config");
|
||||
|
||||
info!("mixnode config updated: {:?}", res)
|
||||
}
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::context::SigningClient;
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
use validator_client::nymd::VestingSigningClient;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
#[clap(long)]
|
||||
pub profit_percent: u8,
|
||||
|
||||
#[clap(long)]
|
||||
pub gas: Option<u64>,
|
||||
}
|
||||
|
||||
pub async fn vesting_update_profit_percent(client: SigningClient, args: Args) {
|
||||
info!("Update vesting mix node profit percent - get those rewards!");
|
||||
|
||||
//profit percent between 1-100
|
||||
let res = client
|
||||
.vesting_update_mixnode_config(args.profit_percent, None)
|
||||
.await
|
||||
.expect("updating vesting mix-node profit percent");
|
||||
|
||||
info!("profit percentage updated: {:?}", res)
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
use validator_client::nymd::traits::MixnetSigningClient;
|
||||
|
||||
use crate::context::SigningClient;
|
||||
|
||||
|
||||
@@ -3,13 +3,14 @@
|
||||
|
||||
use crate::context::SigningClient;
|
||||
use clap::Parser;
|
||||
use cosmwasm_std::Uint128;
|
||||
use log::{info, warn};
|
||||
use mixnet_contract_common::Coin;
|
||||
use mixnet_contract_common::MixNode;
|
||||
use mixnet_contract_common::{Coin, MixNodeCostParams};
|
||||
use mixnet_contract_common::{MixNode, Percent};
|
||||
use network_defaults::{
|
||||
DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT,
|
||||
};
|
||||
use validator_client::nymd::VestingSigningClient;
|
||||
use validator_client::nymd::{CosmWasmCoin, VestingSigningClient};
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
@@ -40,6 +41,12 @@ pub struct Args {
|
||||
#[clap(long)]
|
||||
pub profit_margin_percent: Option<u8>,
|
||||
|
||||
#[clap(
|
||||
long,
|
||||
help = "operating cost in current DENOMINATION (so it would be 'unym', rather than 'nym')"
|
||||
)]
|
||||
pub interval_operating_cost: Option<u128>,
|
||||
|
||||
#[clap(
|
||||
long,
|
||||
help = "bonding amount in current DENOMINATION (so it would be 'unym', rather than 'nym')"
|
||||
@@ -72,13 +79,23 @@ pub async fn vesting_bond_mixnode(client: SigningClient, args: Args, denom: &str
|
||||
sphinx_key: args.sphinx_key,
|
||||
identity_key: args.identity_key,
|
||||
version: args.version,
|
||||
profit_margin_percent: args.profit_margin_percent.unwrap_or(10),
|
||||
};
|
||||
|
||||
let coin = Coin::new(args.amount, denom);
|
||||
|
||||
let cost_params = MixNodeCostParams {
|
||||
profit_margin_percent: Percent::from_percentage_value(
|
||||
args.profit_margin_percent.unwrap_or(10) as u64,
|
||||
)
|
||||
.unwrap(),
|
||||
interval_operating_cost: CosmWasmCoin {
|
||||
denom: denom.into(),
|
||||
amount: Uint128::new(args.interval_operating_cost.unwrap_or(40_000_000)),
|
||||
},
|
||||
};
|
||||
|
||||
let res = client
|
||||
.vesting_bond_mixnode(mixnode, &*args.signature, coin.into(), None)
|
||||
.vesting_bond_mixnode(mixnode, cost_params, &args.signature, coin.into(), None)
|
||||
.await
|
||||
.expect("failed to bond vesting mixnode!");
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ use clap::Parser;
|
||||
use comfy_table::Table;
|
||||
|
||||
use crate::context::QueryClientWithValidatorAPI;
|
||||
use crate::utils::{pretty_cosmwasm_coin, show_error};
|
||||
use crate::utils::{pretty_decimal_with_denom, show_error};
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
@@ -19,7 +19,8 @@ pub async fn query(args: Args, client: &QueryClientWithValidatorAPI) {
|
||||
Ok(res) => match args.identity_key {
|
||||
Some(identity_key) => {
|
||||
let node = res.iter().find(|node| {
|
||||
node.mix_node
|
||||
node.bond_information
|
||||
.mix_node
|
||||
.identity_key
|
||||
.to_string()
|
||||
.eq_ignore_ascii_case(&identity_key)
|
||||
@@ -33,6 +34,7 @@ pub async fn query(args: Args, client: &QueryClientWithValidatorAPI) {
|
||||
let mut table = Table::new();
|
||||
|
||||
table.set_header(vec![
|
||||
"Mix id",
|
||||
"Identity Key",
|
||||
"Owner",
|
||||
"Host",
|
||||
@@ -41,13 +43,15 @@ pub async fn query(args: Args, client: &QueryClientWithValidatorAPI) {
|
||||
"Version",
|
||||
]);
|
||||
for node in res {
|
||||
let denom = &node.bond_information.original_pledge().denom;
|
||||
table.add_row(vec![
|
||||
node.mix_node.identity_key.to_string(),
|
||||
node.owner.to_string(),
|
||||
node.mix_node.host.to_string(),
|
||||
pretty_cosmwasm_coin(&node.pledge_amount),
|
||||
pretty_cosmwasm_coin(&node.total_delegation()),
|
||||
node.mix_node.version,
|
||||
node.mix_id().to_string(),
|
||||
node.bond_information.mix_node.identity_key.clone(),
|
||||
node.bond_information.owner.clone().into_string(),
|
||||
node.bond_information.mix_node.host.clone(),
|
||||
pretty_decimal_with_denom(node.rewarding_details.operator, denom),
|
||||
pretty_decimal_with_denom(node.rewarding_details.delegates, denom),
|
||||
node.bond_information.mix_node.version,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ pub async fn create(args: Args, client: SigningClient, network_details: &NymNetw
|
||||
|
||||
let res = client
|
||||
.create_periodic_vesting_account(
|
||||
&*args.address,
|
||||
&args.address,
|
||||
args.staking_address,
|
||||
Some(vesting),
|
||||
coin.into(),
|
||||
@@ -70,7 +70,7 @@ pub async fn create(args: Args, client: SigningClient, network_details: &NymNetw
|
||||
|
||||
let send_coin_response = client
|
||||
.send(
|
||||
&AccountId::from_str(&*args.address).unwrap(),
|
||||
&AccountId::from_str(&args.address).unwrap(),
|
||||
vec![coin.into()],
|
||||
"payment made :)",
|
||||
None,
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "completions"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "3.2", features = ["derive"] }
|
||||
clap_complete = "3.2"
|
||||
clap_complete_fig = "3.2"
|
||||
@@ -0,0 +1,58 @@
|
||||
use clap::builder::Command;
|
||||
use clap::clap_derive::ArgEnum;
|
||||
use clap::Args;
|
||||
use clap_complete::generator::generate;
|
||||
use clap_complete::Shell as ClapShell;
|
||||
use std::io;
|
||||
|
||||
pub fn fig_generate(command: &mut Command, name: &str) {
|
||||
clap_complete::generate(
|
||||
clap_complete_fig::Fig,
|
||||
command,
|
||||
name,
|
||||
&mut std::io::stdout(),
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(ArgEnum, Copy, Clone)]
|
||||
pub enum Shell {
|
||||
Bash,
|
||||
Elvish,
|
||||
Fish,
|
||||
PowerShell,
|
||||
Zsh,
|
||||
}
|
||||
|
||||
#[derive(Args, Copy, Clone)]
|
||||
pub struct ArgShell {
|
||||
#[clap(arg_enum, value_name = "SHELL")]
|
||||
shell: Shell,
|
||||
}
|
||||
|
||||
impl ArgShell {
|
||||
pub fn generate(&self, command: &mut Command, name: &str) {
|
||||
self.shell.generate(command, name)
|
||||
}
|
||||
}
|
||||
|
||||
impl Shell {
|
||||
pub fn generate(&self, command: &mut Command, name: &str) {
|
||||
match &self {
|
||||
Self::Bash => {
|
||||
generate(ClapShell::Bash, command, name, &mut io::stdout());
|
||||
}
|
||||
Self::Elvish => {
|
||||
generate(ClapShell::Elvish, command, name, &mut io::stdout());
|
||||
}
|
||||
Self::Fish => {
|
||||
generate(ClapShell::Fish, command, name, &mut io::stdout());
|
||||
}
|
||||
Self::PowerShell => {
|
||||
generate(ClapShell::PowerShell, command, name, &mut io::stdout());
|
||||
}
|
||||
Self::Zsh => {
|
||||
generate(ClapShell::Zsh, command, name, &mut io::stdout());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,6 @@ edition = "2021"
|
||||
[dependencies]
|
||||
cfg-if = "1.0.0"
|
||||
handlebars = "3.0.1"
|
||||
humantime-serde = "1.0"
|
||||
log = "0.4"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
toml = "0.5.6"
|
||||
|
||||
@@ -88,6 +88,7 @@ pub trait NymConfig: Default + Serialize + DeserializeOwned {
|
||||
|
||||
let location = custom_location
|
||||
.unwrap_or_else(|| self.config_directory().join(Self::config_file_name()));
|
||||
log::info!("Configuration file will be saved to {:?}", location);
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(unix)] {
|
||||
|
||||
@@ -8,3 +8,4 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
cosmwasm-std = "1.0.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
||||
@@ -28,3 +28,26 @@ pub fn may_find_attribute(event: &Event, key: &str) -> Option<String> {
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub trait OptionallyAddAttribute {
|
||||
fn add_optional_attribute(
|
||||
self,
|
||||
key: impl Into<String>,
|
||||
value: Option<impl Into<String>>,
|
||||
) -> Self;
|
||||
}
|
||||
|
||||
impl OptionallyAddAttribute for Event {
|
||||
fn add_optional_attribute(
|
||||
self,
|
||||
key: impl Into<String>,
|
||||
value: Option<impl Into<String>>,
|
||||
) -> Self {
|
||||
if let Some(value) = value {
|
||||
self.add_attribute(key, value)
|
||||
} else {
|
||||
// TODO: perhaps if value doesn't exist, we should emit explicit 'null'?
|
||||
self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,3 +2,6 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod events;
|
||||
pub mod types;
|
||||
|
||||
pub use types::*;
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
// TODO: there's no reason this couldn't be used for proper binaries, but in that case
|
||||
// perhaps the struct should get renamed and moved to a "more" common crate
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct ContractBuildInformation {
|
||||
// VERGEN_BUILD_TIMESTAMP
|
||||
/// Provides the build timestamp, for example `2021-02-23T20:14:46.558472672+00:00`.
|
||||
pub build_timestamp: String,
|
||||
|
||||
// VERGEN_BUILD_SEMVER
|
||||
/// Provides the build version, for example `0.1.0-9-g46f83e1`.
|
||||
pub build_version: String,
|
||||
|
||||
// VERGEN_GIT_SHA
|
||||
/// Provides the hash of the commit that was used for the build, for example `46f83e112520533338245862d366f6a02cef07d4`.
|
||||
pub commit_sha: String,
|
||||
|
||||
// VERGEN_GIT_COMMIT_TIMESTAMP
|
||||
/// Provides the timestamp of the commit that was used for the build, for example `2021-02-23T08:08:02-05:00`.
|
||||
pub commit_timestamp: String,
|
||||
|
||||
// VERGEN_GIT_BRANCH
|
||||
/// Provides the name of the git branch that was used for the build, for example `master`.
|
||||
pub commit_branch: String,
|
||||
|
||||
// VERGEN_RUSTC_SEMVER
|
||||
/// Provides the rustc version that was used for the build, for example `1.52.0-nightly`.
|
||||
pub rustc_version: String,
|
||||
}
|
||||
@@ -8,23 +8,24 @@ rust-version = "1.62"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
bs58 = "0.4.0"
|
||||
cosmwasm-std = "1.0.0"
|
||||
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_repr = "0.1"
|
||||
schemars = "0.8"
|
||||
thiserror = "1.0"
|
||||
fixed = { version = "1.1", features = ["serde"] }
|
||||
az = "1.1"
|
||||
contracts-common = { path = "../contracts-common" }
|
||||
serde_json = "1.0.0"
|
||||
|
||||
# TO CHECK WHETHER STILL NEEDED:
|
||||
log = "0.4.14"
|
||||
time = { version = "0.3.6", features = ["parsing", "formatting"] }
|
||||
ts-rs = "6.1.2"
|
||||
|
||||
contracts-common = { path = "../contracts-common" }
|
||||
ts-rs = { version = "6.1.2", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
rand_chacha = "0.3"
|
||||
time = { version = "0.3.5", features = ["serde", "macros"] }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
generate-ts = []
|
||||
generate-ts = ['ts-rs']
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use cosmwasm_std::Decimal;
|
||||
|
||||
// I'm still not 100% sure how to feel about existence of this file
|
||||
// This is equivalent of representing our display coin with 6 decimal places.
|
||||
// I'm using this one as opposed to "Decimal::one()", as this provides us with higher accuracy
|
||||
// whilst providing no noticable drawbacks.
|
||||
pub const UNIT_DELEGATION_BASE: Decimal =
|
||||
Decimal::raw(1_000_000_000 * 1_000_000_000_000_000_000u128);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn unit_delegation_didnt_change() {
|
||||
// a sanity check test to make sure Decimal's `DECIMAL_FRACTIONAL` internal implementation hasn't changed
|
||||
assert_eq!(
|
||||
UNIT_DELEGATION_BASE,
|
||||
Decimal::one() * Decimal::from_atomics(1_000_000_000u32, 0).unwrap()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,151 +1,135 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// due to code generated by JsonSchema
|
||||
#![allow(clippy::field_reassign_with_default)]
|
||||
|
||||
use crate::{Addr, IdentityKey};
|
||||
use cosmwasm_std::{Coin, Uint128};
|
||||
use crate::{Addr, NodeId};
|
||||
use cosmwasm_std::{Coin, Decimal};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Display;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
type OwnerAddressBytes = Vec<u8>;
|
||||
type BlockHeight = u64;
|
||||
// just use a string representation of those so that we wouldn't need to bother with decoding bytes
|
||||
// and trying to figure out whether they're valid, etc
|
||||
pub type OwnerProxySubKey = String;
|
||||
pub type StorageKey = (NodeId, OwnerProxySubKey);
|
||||
|
||||
pub fn generate_storage_key(address: &Addr, proxy: Option<&Addr>) -> Vec<u8> {
|
||||
pub fn generate_owner_storage_subkey(address: &Addr, proxy: Option<&Addr>) -> String {
|
||||
if let Some(proxy) = &proxy {
|
||||
address
|
||||
let key_bytes = address
|
||||
.as_bytes()
|
||||
.iter()
|
||||
.zip(proxy.as_bytes())
|
||||
.map(|(x, y)| x ^ y)
|
||||
.collect()
|
||||
.collect::<Vec<_>>();
|
||||
bs58::encode(key_bytes).into_string()
|
||||
} else {
|
||||
address.as_bytes().to_vec()
|
||||
address.clone().into_string()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, JsonSchema)]
|
||||
pub struct Delegation {
|
||||
/// Address of the owner of this delegation.
|
||||
pub owner: Addr,
|
||||
pub node_identity: IdentityKey,
|
||||
|
||||
/// Id of the MixNode that this delegation was performed against.
|
||||
#[serde(alias = "node_id")]
|
||||
pub mix_id: NodeId,
|
||||
|
||||
// Note to UI/UX devs: there's absolutely no point in displaying this value to the users,
|
||||
// it would serve them no purpose. It's only used for calculating rewards
|
||||
/// Value of the "unit delegation" associated with the mixnode at the time of delegation.
|
||||
#[serde(alias = "crr")]
|
||||
pub cumulative_reward_ratio: Decimal,
|
||||
|
||||
/// Original delegation amount. Note that it is never mutated as delegation accumulates rewards.
|
||||
pub amount: Coin,
|
||||
pub block_height: u64,
|
||||
pub proxy: Option<Addr>, // proxy address used to delegate the funds on behalf of another address
|
||||
}
|
||||
|
||||
impl Eq for Delegation {}
|
||||
/// Block height where this delegation occurred.
|
||||
pub height: u64,
|
||||
|
||||
#[allow(clippy::derive_hash_xor_eq)]
|
||||
impl Hash for Delegation {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.owner.hash(state);
|
||||
self.node_identity.hash(state);
|
||||
self.block_height.hash(state);
|
||||
self.proxy.hash(state);
|
||||
}
|
||||
/// Proxy address used to delegate the funds on behalf of another address
|
||||
pub proxy: Option<Addr>,
|
||||
}
|
||||
|
||||
impl Delegation {
|
||||
pub fn new(
|
||||
owner: Addr,
|
||||
node_identity: IdentityKey,
|
||||
mix_id: NodeId,
|
||||
cumulative_reward_ratio: Decimal,
|
||||
amount: Coin,
|
||||
block_height: BlockHeight,
|
||||
height: u64,
|
||||
proxy: Option<Addr>,
|
||||
) -> Self {
|
||||
Delegation {
|
||||
owner,
|
||||
node_identity,
|
||||
mix_id,
|
||||
cumulative_reward_ratio,
|
||||
amount,
|
||||
block_height,
|
||||
height,
|
||||
proxy,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn storage_key(&self) -> (IdentityKey, OwnerAddressBytes, BlockHeight) {
|
||||
(
|
||||
self.node_identity(),
|
||||
self.proxy_storage_key(),
|
||||
self.block_height(),
|
||||
)
|
||||
pub fn generate_storage_key(
|
||||
mix_id: NodeId,
|
||||
owner_address: &Addr,
|
||||
proxy: Option<&Addr>,
|
||||
) -> StorageKey {
|
||||
(mix_id, generate_owner_storage_subkey(owner_address, proxy))
|
||||
}
|
||||
|
||||
pub fn event_storage_key(&self) -> (OwnerAddressBytes, BlockHeight, IdentityKey) {
|
||||
(
|
||||
self.proxy_storage_key(),
|
||||
self.block_height(),
|
||||
self.node_identity(),
|
||||
)
|
||||
// this function might seem a bit redundant, but I'd rather explicitly keep it around in case
|
||||
// some types change in the future
|
||||
pub fn generate_storage_key_with_subkey(
|
||||
mix_id: NodeId,
|
||||
owner_proxy_subkey: OwnerProxySubKey,
|
||||
) -> StorageKey {
|
||||
(mix_id, owner_proxy_subkey)
|
||||
}
|
||||
|
||||
pub fn proxy_storage_key(&self) -> OwnerAddressBytes {
|
||||
generate_storage_key(&self.owner, self.proxy.as_ref())
|
||||
pub fn dec_amount(&self) -> 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(&self) -> Option<&Addr> {
|
||||
self.proxy.as_ref()
|
||||
pub fn proxy_storage_key(&self) -> OwnerProxySubKey {
|
||||
generate_owner_storage_subkey(&self.owner, self.proxy.as_ref())
|
||||
}
|
||||
|
||||
pub fn increment_amount(&mut self, amount: Uint128, at_height: Option<u64>) {
|
||||
self.amount.amount += amount;
|
||||
if let Some(at_height) = at_height {
|
||||
self.block_height = at_height;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn amount(&self) -> &Coin {
|
||||
&self.amount
|
||||
}
|
||||
|
||||
pub fn node_identity(&self) -> IdentityKey {
|
||||
self.node_identity.clone()
|
||||
}
|
||||
|
||||
pub fn owner(&self) -> Addr {
|
||||
self.owner.clone()
|
||||
}
|
||||
|
||||
pub fn block_height(&self) -> u64 {
|
||||
self.block_height
|
||||
pub fn storage_key(&self) -> StorageKey {
|
||||
Self::generate_storage_key(self.mix_id, &self.owner, self.proxy.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Delegation {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{} delegated towards {} by {} at block {}",
|
||||
self.amount, self.node_identity, self.owner, self.block_height
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||
pub struct PagedMixDelegationsResponse {
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
pub struct PagedMixNodeDelegationsResponse {
|
||||
pub delegations: Vec<Delegation>,
|
||||
pub start_next_after: Option<(String, u64)>,
|
||||
pub start_next_after: Option<OwnerProxySubKey>,
|
||||
}
|
||||
|
||||
impl PagedMixDelegationsResponse {
|
||||
pub fn new(delegations: Vec<Delegation>, start_next_after: Option<(String, u64)>) -> Self {
|
||||
PagedMixDelegationsResponse {
|
||||
impl PagedMixNodeDelegationsResponse {
|
||||
pub fn new(delegations: Vec<Delegation>, start_next_after: Option<OwnerProxySubKey>) -> Self {
|
||||
PagedMixNodeDelegationsResponse {
|
||||
delegations,
|
||||
start_next_after,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
pub struct PagedDelegatorDelegationsResponse {
|
||||
pub delegations: Vec<Delegation>,
|
||||
pub start_next_after: Option<IdentityKey>,
|
||||
pub start_next_after: Option<(NodeId, OwnerProxySubKey)>,
|
||||
}
|
||||
|
||||
impl PagedDelegatorDelegationsResponse {
|
||||
pub fn new(delegations: Vec<Delegation>, start_next_after: Option<IdentityKey>) -> Self {
|
||||
pub fn new(
|
||||
delegations: Vec<Delegation>,
|
||||
start_next_after: Option<(NodeId, OwnerProxySubKey)>,
|
||||
) -> Self {
|
||||
PagedDelegatorDelegationsResponse {
|
||||
delegations,
|
||||
start_next_after,
|
||||
@@ -153,20 +137,32 @@ impl PagedDelegatorDelegationsResponse {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||
pub struct PagedAllDelegationsResponse {
|
||||
pub delegations: Vec<Delegation>,
|
||||
pub start_next_after: Option<(IdentityKey, Vec<u8>, u64)>,
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
pub struct MixNodeDelegationResponse {
|
||||
pub delegation: Option<Delegation>,
|
||||
pub mixnode_still_bonded: bool,
|
||||
}
|
||||
|
||||
impl PagedAllDelegationsResponse {
|
||||
pub fn new(
|
||||
delegations: Vec<Delegation>,
|
||||
start_next_after: Option<(IdentityKey, Vec<u8>, u64)>,
|
||||
) -> Self {
|
||||
PagedAllDelegationsResponse {
|
||||
delegations,
|
||||
start_next_after: start_next_after.map(|(id, addr, height)| (id, addr, height)),
|
||||
impl MixNodeDelegationResponse {
|
||||
pub fn new(delegation: Option<Delegation>, mixnode_still_bonded: bool) -> Self {
|
||||
MixNodeDelegationResponse {
|
||||
delegation,
|
||||
mixnode_still_bonded,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
pub struct PagedAllDelegationsResponse {
|
||||
pub delegations: Vec<Delegation>,
|
||||
pub start_next_after: Option<StorageKey>,
|
||||
}
|
||||
|
||||
impl PagedAllDelegationsResponse {
|
||||
pub fn new(delegations: Vec<Delegation>, start_next_after: Option<StorageKey>) -> Self {
|
||||
PagedAllDelegationsResponse {
|
||||
delegations,
|
||||
start_next_after,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,139 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::NodeId;
|
||||
use cosmwasm_std::{Addr, Coin, Decimal};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug, PartialEq)]
|
||||
pub enum MixnetContractError {
|
||||
#[error("Overflow Error")]
|
||||
OverflowError(#[from] cosmwasm_std::OverflowError),
|
||||
#[error("reward_blockstamp field not set, set_reward_blockstamp must be called before attempting to issue rewards")]
|
||||
BlockstampNotSet,
|
||||
#[error("{source}")]
|
||||
TryFromIntError {
|
||||
#[from]
|
||||
source: std::num::TryFromIntError,
|
||||
},
|
||||
#[error("Error casting from U128")]
|
||||
CastError,
|
||||
#[error("{source}")]
|
||||
StdErr {
|
||||
#[from]
|
||||
source: cosmwasm_std::StdError,
|
||||
},
|
||||
#[error("Division by zero at {}", line!())]
|
||||
DivisionByZero,
|
||||
|
||||
#[error("Provided percent value is greater than 100%")]
|
||||
InvalidPercent,
|
||||
|
||||
#[error("Attempted to subtract decimals with overflow ({minuend}.sub({subtrahend}))")]
|
||||
OverflowDecimalSubtraction {
|
||||
minuend: Decimal,
|
||||
subtrahend: Decimal,
|
||||
},
|
||||
|
||||
#[error("Attempted to subtract with overflow ({minuend}.sub({subtrahend}))")]
|
||||
OverflowSubtraction { minuend: u64, subtrahend: u64 },
|
||||
|
||||
#[error("Not enough funds sent for node pledge. (received {received}, minimum {minimum})")]
|
||||
InsufficientPledge { received: Coin, minimum: Coin },
|
||||
|
||||
#[error("Not enough funds sent for node delegation. (received {received}, minimum {minimum})")]
|
||||
InsufficientDelegation { received: Coin, minimum: Coin },
|
||||
|
||||
#[error("Mixnode ({mix_id}) does not exist")]
|
||||
MixNodeBondNotFound { mix_id: NodeId },
|
||||
|
||||
#[error("{owner} does not seem to own any mixnodes")]
|
||||
NoAssociatedMixNodeBond { owner: Addr },
|
||||
|
||||
#[error("{owner} does not seem to own any gateways")]
|
||||
NoAssociatedGatewayBond { owner: Addr },
|
||||
|
||||
#[error("This address has already bonded a mixnode")]
|
||||
AlreadyOwnsMixnode,
|
||||
|
||||
#[error("This address has already bonded a gateway")]
|
||||
AlreadyOwnsGateway,
|
||||
|
||||
#[error("Gateway with this identity already exists. Its owner is {owner}")]
|
||||
DuplicateGateway { owner: Addr },
|
||||
|
||||
#[error("Unauthorized")]
|
||||
Unauthorized,
|
||||
|
||||
#[error("No tokens were sent for the bonding")]
|
||||
NoBondFound,
|
||||
|
||||
#[error("No funds were provided for the delegation")]
|
||||
EmptyDelegation,
|
||||
|
||||
#[error("Wrong coin denomination. Received: {received}, expected: {expected}")]
|
||||
WrongDenom { received: String, expected: String },
|
||||
|
||||
#[error("Received multiple coin types during staking")]
|
||||
MultipleDenoms,
|
||||
|
||||
#[error("Proxy address mismatch, expected {existing}, got {incoming}")]
|
||||
ProxyMismatch { existing: String, incoming: String },
|
||||
|
||||
#[error("Failed to recover ed25519 public key from its base58 representation - {0}")]
|
||||
MalformedEd25519IdentityKey(String),
|
||||
|
||||
#[error("Failed to recover ed25519 signature from its base58 representation - {0}")]
|
||||
MalformedEd25519Signature(String),
|
||||
|
||||
#[error("Provided ed25519 signature did not verify correctly")]
|
||||
InvalidEd25519Signature,
|
||||
|
||||
#[error("Can't perform the specified action as the current epoch is still progress. It started at {epoch_start} and finishes at {epoch_end}, while the current block time is {current_block_time}")]
|
||||
EpochInProgress {
|
||||
current_block_time: u64,
|
||||
epoch_start: i64,
|
||||
epoch_end: i64,
|
||||
},
|
||||
|
||||
#[error("Mixnode {mix_id} has already been rewarded during the current rewarding epoch ({absolute_epoch_id})")]
|
||||
MixnodeAlreadyRewarded {
|
||||
mix_id: NodeId,
|
||||
absolute_epoch_id: u32,
|
||||
},
|
||||
|
||||
#[error("Mixnode {mix_id} hasn't been selected to the rewarding set in this epoch ({absolute_epoch_id})")]
|
||||
MixnodeNotInRewardedSet {
|
||||
mix_id: NodeId,
|
||||
absolute_epoch_id: u32,
|
||||
},
|
||||
|
||||
#[error("Mixnode {mix_id} is currently in the process of unbonding")]
|
||||
MixnodeIsUnbonding { mix_id: NodeId },
|
||||
|
||||
#[error("Mixnode {mix_id} has already unbonded")]
|
||||
MixnodeHasUnbonded { mix_id: NodeId },
|
||||
|
||||
#[error("The contract has ended up in a state that was deemed impossible: {comment}")]
|
||||
InconsistentState { comment: String },
|
||||
|
||||
#[error(
|
||||
"Could not find any delegation information associated with mixnode {mix_id} for {address} (proxy: {proxy:?})"
|
||||
)]
|
||||
NoMixnodeDelegationFound {
|
||||
mix_id: NodeId,
|
||||
address: String,
|
||||
proxy: Option<String>,
|
||||
},
|
||||
|
||||
#[error("Provided message to update rewarding params did not contain any updates")]
|
||||
EmptyParamsChangeMsg,
|
||||
|
||||
#[error("Provided active set size is bigger than the rewarded set")]
|
||||
InvalidActiveSetSize,
|
||||
|
||||
#[error("Provided rewarded set size is smaller than the active set")]
|
||||
InvalidRewardedSetSize,
|
||||
|
||||
#[error("Provided active set size is zero")]
|
||||
ZeroActiveSet,
|
||||
|
||||
#[error("Provided rewarded set size is zero")]
|
||||
ZeroRewardedSet,
|
||||
|
||||
#[error("Received unexpected value for the active set. Got: {received}, expected: {expected}")]
|
||||
UnexpectedActiveSetSize { received: u32, expected: u32 },
|
||||
|
||||
#[error("Received unexpected value for the rewarded set. Got: {received}, expected at most: {expected}")]
|
||||
UnexpectedRewardedSetSize { received: u32, expected: u32 },
|
||||
|
||||
#[error("Mixnode {mix_id} appears multiple times in the provided rewarded set update!")]
|
||||
DuplicateRewardedSetNode { mix_id: NodeId },
|
||||
}
|
||||
|
||||
@@ -1,32 +1,90 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
use crate::mixnode::NodeRewardResult;
|
||||
use crate::{ContractStateParams, IdentityKeyRef, Interval, Layer};
|
||||
use cosmwasm_std::{Addr, Coin, Event, Uint128};
|
||||
|
||||
use crate::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
|
||||
use crate::reward_params::{IntervalRewardParams, IntervalRewardingParamsUpdate};
|
||||
use crate::rewarding::RewardDistribution;
|
||||
use crate::{ContractStateParams, IdentityKeyRef, Interval, Layer, NodeId};
|
||||
pub use contracts_common::events::*;
|
||||
// FIXME: This should becoma an Enum
|
||||
// event types
|
||||
pub const DELEGATION_EVENT_TYPE: &str = "delegation";
|
||||
pub const PENDING_DELEGATION_EVENT_TYPE: &str = "pending_delegation";
|
||||
pub const RECONCILE_DELEGATION_EVENT_TYPE: &str = "reconcile_delegation";
|
||||
pub const UNDELEGATION_EVENT_TYPE: &str = "undelegation";
|
||||
pub const PENDING_UNDELEGATION_EVENT_TYPE: &str = "pending_undelegation";
|
||||
pub const GATEWAY_BONDING_EVENT_TYPE: &str = "gateway_bonding";
|
||||
pub const GATEWAY_UNBONDING_EVENT_TYPE: &str = "gateway_unbonding";
|
||||
pub const MIXNODE_BONDING_EVENT_TYPE: &str = "mixnode_bonding";
|
||||
pub const MIXNODE_UNBONDING_EVENT_TYPE: &str = "mixnode_unbonding";
|
||||
pub const SETTINGS_UPDATE_EVENT_TYPE: &str = "settings_update";
|
||||
pub const OPERATOR_REWARDING_EVENT_TYPE: &str = "mix_rewarding";
|
||||
pub const MIX_DELEGATORS_REWARDING_EVENT_TYPE: &str = "mix_delegators_rewarding";
|
||||
pub const CHANGE_REWARDED_SET_EVENT_TYPE: &str = "change_rewarded_set";
|
||||
pub const ADVANCE_INTERVAL_EVENT_TYPE: &str = "advance_interval";
|
||||
pub const ADVANCE_EPOCH_EVENT_TYPE: &str = "advance_epoch";
|
||||
pub const COMPOUND_DELEGATOR_REWARD_EVENT_TYPE: &str = "compound_delegator_reward";
|
||||
pub const CLAIM_DELEGATOR_REWARD_EVENT_TYPE: &str = "claim_delegator_reward";
|
||||
pub const COMPOUND_OPERATOR_REWARD_EVENT_TYPE: &str = "compound_operator_reward";
|
||||
pub const CLAIM_OPERATOR_REWARD_EVENT_TYPE: &str = "claim_operator_reward";
|
||||
pub const SNAPSHOT_MIXNODES_EVENT: &str = "snapshot_mixnodes";
|
||||
use cosmwasm_std::{Addr, Coin, Decimal, Event};
|
||||
|
||||
pub const EVENT_VERSION_PREFIX: &str = "v2_";
|
||||
|
||||
pub enum MixnetEventType {
|
||||
MixnodeBonding,
|
||||
GatewayBonding,
|
||||
GatewayUnbonding,
|
||||
PendingMixnodeUnbonding,
|
||||
MixnodeUnbonding,
|
||||
MixnodeConfigUpdate,
|
||||
PendingMixnodeCostParamsUpdate,
|
||||
MixnodeCostParamsUpdate,
|
||||
MixnodeRewarding,
|
||||
WithdrawDelegatorReward,
|
||||
WithdrawOperatorReward,
|
||||
PendingActiveSetUpdate,
|
||||
ActiveSetUpdate,
|
||||
PendingIntervalRewardingParamsUpdate,
|
||||
IntervalRewardingParamsUpdate,
|
||||
PendingDelegation,
|
||||
PendingUndelegation,
|
||||
Delegation,
|
||||
DelegationOnUnbonding,
|
||||
Undelegation,
|
||||
ContractSettingsUpdate,
|
||||
RewardingValidatorUpdate,
|
||||
AdvanceEpoch,
|
||||
ExecutePendingEpochEvents,
|
||||
ExecutePendingIntervalEvents,
|
||||
ReconcilePendingEvents,
|
||||
PendingIntervalConfigUpdate,
|
||||
IntervalConfigUpdate,
|
||||
}
|
||||
|
||||
impl From<MixnetEventType> for String {
|
||||
fn from(typ: MixnetEventType) -> Self {
|
||||
typ.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for MixnetEventType {
|
||||
fn to_string(&self) -> String {
|
||||
let event_name = match self {
|
||||
MixnetEventType::MixnodeBonding => "mixnode_bonding",
|
||||
MixnetEventType::GatewayBonding => "gateway_bonding",
|
||||
MixnetEventType::GatewayUnbonding => "gateway_unbonding",
|
||||
MixnetEventType::PendingMixnodeUnbonding => "pending_mixnode_unbonding",
|
||||
MixnetEventType::MixnodeConfigUpdate => "mixnode_config_update",
|
||||
MixnetEventType::MixnodeUnbonding => "mixnode_unbonding",
|
||||
MixnetEventType::PendingMixnodeCostParamsUpdate => "pending_mixnode_cost_params_update",
|
||||
MixnetEventType::MixnodeCostParamsUpdate => "mixnode_cost_params_update",
|
||||
MixnetEventType::MixnodeRewarding => "mix_rewarding",
|
||||
MixnetEventType::WithdrawDelegatorReward => "withdraw_delegator_reward",
|
||||
MixnetEventType::WithdrawOperatorReward => "withdraw_operator_reward",
|
||||
MixnetEventType::PendingActiveSetUpdate => "pending_active_set_update",
|
||||
MixnetEventType::ActiveSetUpdate => "active_set_update",
|
||||
MixnetEventType::PendingIntervalRewardingParamsUpdate => {
|
||||
"pending_interval_rewarding_params_update"
|
||||
}
|
||||
MixnetEventType::IntervalRewardingParamsUpdate => "interval_rewarding_params_update",
|
||||
MixnetEventType::PendingDelegation => "pending_delegation",
|
||||
MixnetEventType::PendingUndelegation => "pending_undelegation",
|
||||
MixnetEventType::Delegation => "delegation",
|
||||
MixnetEventType::Undelegation => "undelegation",
|
||||
MixnetEventType::ContractSettingsUpdate => "settings_update",
|
||||
MixnetEventType::RewardingValidatorUpdate => "rewarding_validator_address_update",
|
||||
MixnetEventType::AdvanceEpoch => "advance_epoch",
|
||||
MixnetEventType::ExecutePendingEpochEvents => "execute_pending_epoch_events",
|
||||
MixnetEventType::ExecutePendingIntervalEvents => "execute_pending_interval_events",
|
||||
MixnetEventType::ReconcilePendingEvents => "reconcile_pending_events",
|
||||
MixnetEventType::PendingIntervalConfigUpdate => "pending_interval_config_update",
|
||||
MixnetEventType::IntervalConfigUpdate => "interval_config_update",
|
||||
MixnetEventType::DelegationOnUnbonding => "delegation_on_unbonding_node",
|
||||
};
|
||||
|
||||
format!("{}{}", EVENT_VERSION_PREFIX, event_name)
|
||||
}
|
||||
}
|
||||
|
||||
// attributes that are used in multiple places
|
||||
pub const OWNER_KEY: &str = "owner";
|
||||
@@ -41,35 +99,44 @@ pub const DELEGATION_TARGET_KEY: &str = "delegation_target";
|
||||
pub const DELEGATION_HEIGHT_KEY: &str = "delegation_latest_block_height";
|
||||
|
||||
// bonding/unbonding
|
||||
pub const MIX_ID_KEY: &str = "mix_id";
|
||||
pub const NODE_IDENTITY_KEY: &str = "identity";
|
||||
pub const ASSIGNED_LAYER_KEY: &str = "assigned_layer";
|
||||
|
||||
// settings change
|
||||
pub const OLD_MINIMUM_MIXNODE_PLEDGE_KEY: &str = "old_minimum_mixnode_pledge";
|
||||
pub const OLD_MINIMUM_GATEWAY_PLEDGE_KEY: &str = "old_minimum_gateway_pledge";
|
||||
pub const OLD_MIXNODE_REWARDED_SET_SIZE_KEY: &str = "old_mixnode_rewarded_set_size";
|
||||
pub const OLD_MIXNODE_ACTIVE_SET_SIZE_KEY: &str = "old_mixnode_active_set_size";
|
||||
pub const OLD_ACTIVE_SET_WORK_FACTOR_KEY: &str = "old_active_set_work_factor";
|
||||
pub const OLD_MINIMUM_DELEGATION_KEY: &str = "old_minimum_delegation";
|
||||
|
||||
pub const NEW_MINIMUM_MIXNODE_PLEDGE_KEY: &str = "new_minimum_mixnode_pledge";
|
||||
pub const NEW_MINIMUM_GATEWAY_PLEDGE_KEY: &str = "new_minimum_gateway_pledge";
|
||||
pub const NEW_MIXNODE_REWARDED_SET_SIZE_KEY: &str = "new_mixnode_rewarded_set_size";
|
||||
pub const NEW_MIXNODE_ACTIVE_SET_SIZE_KEY: &str = "new_mixnode_active_set_size";
|
||||
pub const NEW_MINIMUM_DELEGATION_KEY: &str = "new_minimum_delegation";
|
||||
|
||||
pub const OLD_REWARDING_VALIDATOR_ADDRESS_KEY: &str = "old_rewarding_validator_address";
|
||||
pub const NEW_REWARDING_VALIDATOR_ADDRESS_KEY: &str = "new_rewarding_validator_address";
|
||||
|
||||
pub const UPDATED_MIXNODE_CONFIG_KEY: &str = "updated_mixnode_config";
|
||||
pub const UPDATED_MIXNODE_COST_PARAMS_KEY: &str = "updated_mixnode_cost_params";
|
||||
|
||||
// rewarding
|
||||
pub const INTERVAL_ID_KEY: &str = "interval_id";
|
||||
pub const INTERVAL_KEY: &str = "interval_details";
|
||||
pub const TOTAL_MIXNODE_REWARD_KEY: &str = "total_node_reward";
|
||||
pub const OPERATOR_REWARD_KEY: &str = "operator_reward";
|
||||
pub const TOTAL_PLEDGE_KEY: &str = "pledge";
|
||||
pub const TOTAL_DELEGATIONS_KEY: &str = "delegated";
|
||||
pub const LAMBDA_KEY: &str = "lambda";
|
||||
pub const SIGMA_KEY: &str = "sigma";
|
||||
pub const OPERATOR_REWARD_KEY: &str = "operator_reward";
|
||||
pub const DELEGATES_REWARD_KEY: &str = "delegates_reward";
|
||||
pub const APPROXIMATE_TIME_LEFT_SECS_KEY: &str = "approximate_time_left_secs";
|
||||
pub const INTERVAL_REWARDING_PARAMS_UPDATE_KEY: &str = "interval_rewarding_params_update";
|
||||
pub const UPDATED_INTERVAL_REWARDING_PARAMS_KEY: &str = "updated_interval_rewarding_params";
|
||||
pub const PRIOR_DELEGATES_KEY: &str = "prior_delegates";
|
||||
pub const PRIOR_UNIT_DELEGATION_KEY: &str = "prior_unit_delegation";
|
||||
|
||||
pub const DISTRIBUTED_DELEGATION_REWARDS_KEY: &str = "distributed_delegation_rewards";
|
||||
pub const FURTHER_DELEGATIONS_TO_REWARD_KEY: &str = "further_delegations";
|
||||
pub const NO_REWARD_REASON_KEY: &str = "no_reward_reason";
|
||||
pub const BOND_NOT_FOUND_VALUE: &str = "bond_not_found";
|
||||
pub const BOND_TOO_FRESH_VALUE: &str = "bond_too_fresh";
|
||||
pub const ZERO_UPTIME_VALUE: &str = "zero_uptime";
|
||||
pub const ZERO_PERFORMANCE_VALUE: &str = "zero_performance";
|
||||
|
||||
// rewarded set update
|
||||
pub const ACTIVE_SET_SIZE_KEY: &str = "active_set_size";
|
||||
@@ -80,156 +147,140 @@ pub const CURRENT_INTERVAL_ID_KEY: &str = "current_interval";
|
||||
pub const NEW_CURRENT_INTERVAL_KEY: &str = "new_current_interval";
|
||||
pub const NEW_CURRENT_EPOCH_KEY: &str = "new_current_epoch";
|
||||
pub const BLOCK_HEIGHT_KEY: &str = "block_height";
|
||||
pub const CHECKPOINT_MIXNODES_EVENT: &str = "checkpoint_mixnodes";
|
||||
pub const RECONCILIATION_ERROR_EVENT: &str = "reconciliation_error";
|
||||
|
||||
pub fn new_checkpoint_mixnodes_event(block_height: u64) -> Event {
|
||||
Event::new(CHECKPOINT_MIXNODES_EVENT)
|
||||
.add_attribute(BLOCK_HEIGHT_KEY, format!("{}", block_height))
|
||||
}
|
||||
|
||||
pub fn new_error_event(err: String) -> Event {
|
||||
Event::new(RECONCILIATION_ERROR_EVENT).add_attribute("error", err)
|
||||
}
|
||||
// interval
|
||||
pub const EVENTS_EXECUTED_KEY: &str = "number_of_events_executed";
|
||||
pub const REWARDED_SET_NODES_KEY: &str = "rewarded_set_nodes";
|
||||
pub const NEW_EPOCHS_DURATION_SECS_KEY: &str = "new_epoch_durations_secs";
|
||||
pub const NEW_EPOCHS_IN_INTERVAL: &str = "new_epochs_in_interval";
|
||||
|
||||
pub fn new_delegation_event(
|
||||
delegator: &Addr,
|
||||
proxy: &Option<Addr>,
|
||||
amount: &Coin,
|
||||
mix_identity: IdentityKeyRef<'_>,
|
||||
mix_id: NodeId,
|
||||
) -> Event {
|
||||
let mut event = Event::new(DELEGATION_EVENT_TYPE).add_attribute(DELEGATOR_KEY, delegator);
|
||||
|
||||
if let Some(proxy) = proxy {
|
||||
event = event.add_attribute(PROXY_KEY, proxy)
|
||||
}
|
||||
|
||||
// coin implements Display trait and we use that implementation here
|
||||
event
|
||||
Event::new(MixnetEventType::Delegation)
|
||||
.add_attribute(DELEGATOR_KEY, delegator)
|
||||
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
|
||||
.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
.add_attribute(DELEGATION_TARGET_KEY, mix_identity)
|
||||
.add_attribute(DELEGATION_TARGET_KEY, mix_id.to_string())
|
||||
}
|
||||
|
||||
pub fn new_delegation_on_unbonded_node_event(
|
||||
delegator: &Addr,
|
||||
proxy: &Option<Addr>,
|
||||
mix_id: NodeId,
|
||||
) -> Event {
|
||||
Event::new(MixnetEventType::Delegation)
|
||||
.add_attribute(DELEGATOR_KEY, delegator)
|
||||
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
|
||||
.add_attribute(DELEGATION_TARGET_KEY, mix_id.to_string())
|
||||
}
|
||||
|
||||
pub fn new_pending_delegation_event(
|
||||
delegator: &Addr,
|
||||
proxy: &Option<Addr>,
|
||||
amount: &Coin,
|
||||
mix_identity: IdentityKeyRef<'_>,
|
||||
mix_id: NodeId,
|
||||
) -> Event {
|
||||
let mut event =
|
||||
Event::new(PENDING_DELEGATION_EVENT_TYPE).add_attribute(DELEGATOR_KEY, delegator);
|
||||
|
||||
if let Some(proxy) = proxy {
|
||||
event = event.add_attribute(PROXY_KEY, proxy)
|
||||
}
|
||||
|
||||
// coin implements Display trait and we use that implementation here
|
||||
event
|
||||
.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
.add_attribute(DELEGATION_TARGET_KEY, mix_identity)
|
||||
}
|
||||
|
||||
pub fn new_reconcile_delegation_event(
|
||||
delegator: &Addr,
|
||||
proxy: &Option<Addr>,
|
||||
amount: &Coin,
|
||||
mix_identity: IdentityKeyRef<'_>,
|
||||
) -> Event {
|
||||
let mut event =
|
||||
Event::new(RECONCILE_DELEGATION_EVENT_TYPE).add_attribute(DELEGATOR_KEY, delegator);
|
||||
|
||||
if let Some(proxy) = proxy {
|
||||
event = event.add_attribute(PROXY_KEY, proxy)
|
||||
}
|
||||
|
||||
// coin implements Display trait and we use that implementation here
|
||||
event
|
||||
.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
.add_attribute(DELEGATION_TARGET_KEY, mix_identity)
|
||||
}
|
||||
|
||||
pub fn new_compound_operator_reward_event(owner: &Addr, amount: Uint128) -> Event {
|
||||
let event = Event::new(COMPOUND_OPERATOR_REWARD_EVENT_TYPE).add_attribute(OWNER_KEY, owner);
|
||||
event.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
}
|
||||
|
||||
pub fn new_claim_operator_reward_event(owner: &Addr, amount: Uint128) -> Event {
|
||||
let event = Event::new(CLAIM_OPERATOR_REWARD_EVENT_TYPE).add_attribute(OWNER_KEY, owner);
|
||||
event.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
}
|
||||
|
||||
pub fn new_compound_delegator_reward_event(
|
||||
delegator: &Addr,
|
||||
proxy: &Option<Addr>,
|
||||
amount: Uint128,
|
||||
mix_identity: IdentityKeyRef<'_>,
|
||||
) -> Event {
|
||||
let mut event =
|
||||
Event::new(COMPOUND_DELEGATOR_REWARD_EVENT_TYPE).add_attribute(DELEGATOR_KEY, delegator);
|
||||
|
||||
if let Some(proxy) = proxy {
|
||||
event = event.add_attribute(PROXY_KEY, proxy)
|
||||
}
|
||||
|
||||
// coin implements Display trait and we use that implementation here
|
||||
event
|
||||
.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
.add_attribute(DELEGATION_TARGET_KEY, mix_identity)
|
||||
Event::new(MixnetEventType::PendingDelegation)
|
||||
.add_attribute(DELEGATOR_KEY, delegator)
|
||||
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
|
||||
.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
.add_attribute(DELEGATION_TARGET_KEY, mix_id.to_string())
|
||||
}
|
||||
|
||||
pub fn new_claim_delegator_reward_event(
|
||||
pub fn new_withdraw_operator_reward_event(
|
||||
owner: &Addr,
|
||||
proxy: &Option<Addr>,
|
||||
amount: Coin,
|
||||
mix_id: NodeId,
|
||||
) -> Event {
|
||||
Event::new(MixnetEventType::WithdrawOperatorReward)
|
||||
.add_attribute(OWNER_KEY, owner.as_str())
|
||||
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
|
||||
.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
.add_attribute(MIX_ID_KEY, mix_id.to_string())
|
||||
}
|
||||
|
||||
pub fn new_withdraw_delegator_reward_event(
|
||||
delegator: &Addr,
|
||||
proxy: &Option<Addr>,
|
||||
amount: Uint128,
|
||||
mix_identity: IdentityKeyRef<'_>,
|
||||
amount: Coin,
|
||||
mix_id: NodeId,
|
||||
) -> Event {
|
||||
let mut event =
|
||||
Event::new(CLAIM_DELEGATOR_REWARD_EVENT_TYPE).add_attribute(DELEGATOR_KEY, delegator);
|
||||
|
||||
if let Some(proxy) = proxy {
|
||||
event = event.add_attribute(PROXY_KEY, proxy)
|
||||
}
|
||||
|
||||
// coin implements Display trait and we use that implementation here
|
||||
event
|
||||
.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
.add_attribute(DELEGATION_TARGET_KEY, mix_identity)
|
||||
Event::new(MixnetEventType::WithdrawDelegatorReward)
|
||||
.add_attribute(DELEGATOR_KEY, delegator)
|
||||
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
|
||||
.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
.add_attribute(DELEGATION_TARGET_KEY, mix_id.to_string())
|
||||
}
|
||||
|
||||
pub fn new_undelegation_event(
|
||||
delegator: &Addr,
|
||||
proxy: &Option<Addr>,
|
||||
mix_identity: IdentityKeyRef<'_>,
|
||||
amount: Uint128,
|
||||
pub fn new_active_set_update_event(new_size: u32) -> Event {
|
||||
Event::new(MixnetEventType::ActiveSetUpdate)
|
||||
.add_attribute(ACTIVE_SET_SIZE_KEY, new_size.to_string())
|
||||
}
|
||||
|
||||
pub fn new_pending_active_set_update_event(
|
||||
new_size: u32,
|
||||
approximate_time_remaining_secs: i64,
|
||||
) -> Event {
|
||||
let mut event = Event::new(UNDELEGATION_EVENT_TYPE).add_attribute(DELEGATOR_KEY, delegator);
|
||||
Event::new(MixnetEventType::PendingActiveSetUpdate)
|
||||
.add_attribute(ACTIVE_SET_SIZE_KEY, new_size.to_string())
|
||||
.add_attribute(
|
||||
APPROXIMATE_TIME_LEFT_SECS_KEY,
|
||||
approximate_time_remaining_secs.to_string(),
|
||||
)
|
||||
}
|
||||
|
||||
if let Some(proxy) = proxy {
|
||||
event = event.add_attribute(PROXY_KEY, proxy)
|
||||
}
|
||||
pub fn new_rewarding_params_update_event(
|
||||
update: IntervalRewardingParamsUpdate,
|
||||
updated: IntervalRewardParams,
|
||||
) -> Event {
|
||||
Event::new(MixnetEventType::IntervalRewardingParamsUpdate)
|
||||
.add_attribute(
|
||||
INTERVAL_REWARDING_PARAMS_UPDATE_KEY,
|
||||
update.to_inline_json(),
|
||||
)
|
||||
.add_attribute(
|
||||
UPDATED_INTERVAL_REWARDING_PARAMS_KEY,
|
||||
updated.to_inline_json(),
|
||||
)
|
||||
}
|
||||
|
||||
// coin implements Display trait and we use that implementation here
|
||||
event
|
||||
.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
.add_attribute(DELEGATION_TARGET_KEY, mix_identity)
|
||||
pub fn new_pending_rewarding_params_update_event(
|
||||
update: IntervalRewardingParamsUpdate,
|
||||
approximate_time_remaining_secs: i64,
|
||||
) -> Event {
|
||||
Event::new(MixnetEventType::PendingIntervalRewardingParamsUpdate)
|
||||
.add_attribute(
|
||||
INTERVAL_REWARDING_PARAMS_UPDATE_KEY,
|
||||
update.to_inline_json(),
|
||||
)
|
||||
.add_attribute(
|
||||
APPROXIMATE_TIME_LEFT_SECS_KEY,
|
||||
approximate_time_remaining_secs.to_string(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_undelegation_event(delegator: &Addr, proxy: &Option<Addr>, mix_id: NodeId) -> Event {
|
||||
Event::new(MixnetEventType::Undelegation)
|
||||
.add_attribute(DELEGATOR_KEY, delegator)
|
||||
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
|
||||
.add_attribute(MIX_ID_KEY, mix_id.to_string())
|
||||
}
|
||||
|
||||
pub fn new_pending_undelegation_event(
|
||||
delegator: &Addr,
|
||||
proxy: &Option<Addr>,
|
||||
mix_identity: IdentityKeyRef<'_>,
|
||||
mix_id: NodeId,
|
||||
) -> Event {
|
||||
let mut event =
|
||||
Event::new(PENDING_UNDELEGATION_EVENT_TYPE).add_attribute(DELEGATOR_KEY, delegator);
|
||||
|
||||
if let Some(proxy) = proxy {
|
||||
event = event.add_attribute(PROXY_KEY, proxy)
|
||||
}
|
||||
|
||||
// coin implements Display trait and we use that implementation here
|
||||
event.add_attribute(DELEGATION_TARGET_KEY, mix_identity)
|
||||
Event::new(MixnetEventType::PendingUndelegation)
|
||||
.add_attribute(DELEGATOR_KEY, delegator)
|
||||
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
|
||||
.add_attribute(MIX_ID_KEY, mix_id.to_string())
|
||||
}
|
||||
|
||||
pub fn new_gateway_bonding_event(
|
||||
@@ -238,16 +289,11 @@ pub fn new_gateway_bonding_event(
|
||||
amount: &Coin,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
) -> Event {
|
||||
let mut event = Event::new(GATEWAY_BONDING_EVENT_TYPE)
|
||||
Event::new(MixnetEventType::GatewayBonding)
|
||||
.add_attribute(OWNER_KEY, owner)
|
||||
.add_attribute(NODE_IDENTITY_KEY, identity);
|
||||
|
||||
if let Some(proxy) = proxy {
|
||||
event = event.add_attribute(PROXY_KEY, proxy)
|
||||
}
|
||||
|
||||
// coin implements Display trait and we use that implementation here
|
||||
event.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
.add_attribute(NODE_IDENTITY_KEY, identity)
|
||||
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
|
||||
.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
}
|
||||
|
||||
pub fn new_gateway_unbonding_event(
|
||||
@@ -256,16 +302,11 @@ pub fn new_gateway_unbonding_event(
|
||||
amount: &Coin,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
) -> Event {
|
||||
let mut event = Event::new(GATEWAY_UNBONDING_EVENT_TYPE)
|
||||
Event::new(MixnetEventType::GatewayUnbonding)
|
||||
.add_attribute(OWNER_KEY, owner)
|
||||
.add_attribute(NODE_IDENTITY_KEY, identity);
|
||||
|
||||
if let Some(proxy) = proxy {
|
||||
event = event.add_attribute(PROXY_KEY, proxy)
|
||||
}
|
||||
|
||||
// coin implements Display trait and we use that implementation here
|
||||
event.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
.add_attribute(NODE_IDENTITY_KEY, identity)
|
||||
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
|
||||
.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
}
|
||||
|
||||
pub fn new_mixnode_bonding_event(
|
||||
@@ -273,55 +314,92 @@ pub fn new_mixnode_bonding_event(
|
||||
proxy: &Option<Addr>,
|
||||
amount: &Coin,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
mix_id: NodeId,
|
||||
assigned_layer: Layer,
|
||||
) -> Event {
|
||||
let mut event = Event::new(MIXNODE_BONDING_EVENT_TYPE)
|
||||
.add_attribute(OWNER_KEY, owner)
|
||||
.add_attribute(NODE_IDENTITY_KEY, identity);
|
||||
|
||||
if let Some(proxy) = proxy {
|
||||
event = event.add_attribute(PROXY_KEY, proxy)
|
||||
}
|
||||
|
||||
// coin implements Display trait and we use that implementation here
|
||||
event
|
||||
Event::new(MixnetEventType::MixnodeBonding)
|
||||
.add_attribute(MIX_ID_KEY, mix_id.to_string())
|
||||
.add_attribute(NODE_IDENTITY_KEY, identity)
|
||||
.add_attribute(OWNER_KEY, owner)
|
||||
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
|
||||
.add_attribute(ASSIGNED_LAYER_KEY, assigned_layer)
|
||||
.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
}
|
||||
|
||||
pub fn new_mixnode_unbonding_event(
|
||||
pub fn new_mixnode_unbonding_event(mix_id: NodeId) -> Event {
|
||||
Event::new(MixnetEventType::MixnodeUnbonding).add_attribute(MIX_ID_KEY, mix_id.to_string())
|
||||
}
|
||||
|
||||
pub fn new_pending_mixnode_unbonding_event(
|
||||
owner: &Addr,
|
||||
proxy: &Option<Addr>,
|
||||
amount: &Coin,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
mix_id: NodeId,
|
||||
) -> Event {
|
||||
let mut event = Event::new(MIXNODE_UNBONDING_EVENT_TYPE)
|
||||
Event::new(MixnetEventType::PendingMixnodeUnbonding)
|
||||
.add_attribute(MIX_ID_KEY, mix_id.to_string())
|
||||
.add_attribute(NODE_IDENTITY_KEY, identity)
|
||||
.add_attribute(OWNER_KEY, owner)
|
||||
.add_attribute(NODE_IDENTITY_KEY, identity);
|
||||
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
|
||||
}
|
||||
|
||||
if let Some(proxy) = proxy {
|
||||
event = event.add_attribute(PROXY_KEY, proxy)
|
||||
}
|
||||
pub fn new_mixnode_config_update_event(
|
||||
mix_id: NodeId,
|
||||
owner: &Addr,
|
||||
proxy: &Option<Addr>,
|
||||
update: &MixNodeConfigUpdate,
|
||||
) -> Event {
|
||||
Event::new(MixnetEventType::MixnodeConfigUpdate)
|
||||
.add_attribute(MIX_ID_KEY, mix_id.to_string())
|
||||
.add_attribute(OWNER_KEY, owner)
|
||||
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
|
||||
.add_attribute(UPDATED_MIXNODE_CONFIG_KEY, update.to_inline_json())
|
||||
}
|
||||
|
||||
// coin implements Display trait and we use that implementation here
|
||||
event.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
pub fn new_mixnode_pending_cost_params_update_event(
|
||||
mix_id: NodeId,
|
||||
owner: &Addr,
|
||||
proxy: &Option<Addr>,
|
||||
new_costs: &MixNodeCostParams,
|
||||
) -> Event {
|
||||
Event::new(MixnetEventType::PendingMixnodeCostParamsUpdate)
|
||||
.add_attribute(MIX_ID_KEY, mix_id.to_string())
|
||||
.add_attribute(OWNER_KEY, owner)
|
||||
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
|
||||
.add_attribute(UPDATED_MIXNODE_COST_PARAMS_KEY, new_costs.to_inline_json())
|
||||
}
|
||||
|
||||
pub fn new_mixnode_cost_params_update_event(
|
||||
mix_id: NodeId,
|
||||
new_costs: &MixNodeCostParams,
|
||||
) -> Event {
|
||||
Event::new(MixnetEventType::MixnodeCostParamsUpdate)
|
||||
.add_attribute(MIX_ID_KEY, mix_id.to_string())
|
||||
.add_attribute(UPDATED_MIXNODE_COST_PARAMS_KEY, new_costs.to_inline_json())
|
||||
}
|
||||
|
||||
pub fn new_rewarding_validator_address_update_event(old: Addr, new: Addr) -> Event {
|
||||
Event::new(MixnetEventType::RewardingValidatorUpdate)
|
||||
.add_attribute(OLD_REWARDING_VALIDATOR_ADDRESS_KEY, old)
|
||||
.add_attribute(NEW_REWARDING_VALIDATOR_ADDRESS_KEY, new)
|
||||
}
|
||||
|
||||
pub fn new_settings_update_event(
|
||||
old_params: &ContractStateParams,
|
||||
new_params: &ContractStateParams,
|
||||
) -> Event {
|
||||
let mut event = Event::new(SETTINGS_UPDATE_EVENT_TYPE);
|
||||
let mut event = Event::new(MixnetEventType::ContractSettingsUpdate);
|
||||
|
||||
if old_params.minimum_mixnode_pledge != new_params.minimum_mixnode_pledge {
|
||||
event = event
|
||||
.add_attribute(
|
||||
OLD_MINIMUM_MIXNODE_PLEDGE_KEY,
|
||||
old_params.minimum_mixnode_pledge,
|
||||
old_params.minimum_mixnode_pledge.to_string(),
|
||||
)
|
||||
.add_attribute(
|
||||
NEW_MINIMUM_MIXNODE_PLEDGE_KEY,
|
||||
new_params.minimum_mixnode_pledge,
|
||||
new_params.minimum_mixnode_pledge.to_string(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -329,128 +407,128 @@ pub fn new_settings_update_event(
|
||||
event = event
|
||||
.add_attribute(
|
||||
OLD_MINIMUM_GATEWAY_PLEDGE_KEY,
|
||||
old_params.minimum_gateway_pledge,
|
||||
old_params.minimum_gateway_pledge.to_string(),
|
||||
)
|
||||
.add_attribute(
|
||||
NEW_MINIMUM_GATEWAY_PLEDGE_KEY,
|
||||
new_params.minimum_gateway_pledge,
|
||||
new_params.minimum_gateway_pledge.to_string(),
|
||||
)
|
||||
}
|
||||
|
||||
if old_params.mixnode_rewarded_set_size != new_params.mixnode_rewarded_set_size {
|
||||
event = event
|
||||
.add_attribute(
|
||||
OLD_MIXNODE_REWARDED_SET_SIZE_KEY,
|
||||
old_params.mixnode_rewarded_set_size.to_string(),
|
||||
)
|
||||
.add_attribute(
|
||||
NEW_MIXNODE_REWARDED_SET_SIZE_KEY,
|
||||
new_params.mixnode_rewarded_set_size.to_string(),
|
||||
)
|
||||
}
|
||||
|
||||
if old_params.mixnode_active_set_size != new_params.mixnode_active_set_size {
|
||||
event = event
|
||||
.add_attribute(
|
||||
OLD_MIXNODE_ACTIVE_SET_SIZE_KEY,
|
||||
old_params.mixnode_active_set_size.to_string(),
|
||||
)
|
||||
.add_attribute(
|
||||
NEW_MIXNODE_ACTIVE_SET_SIZE_KEY,
|
||||
new_params.mixnode_active_set_size.to_string(),
|
||||
)
|
||||
if old_params.minimum_mixnode_delegation != new_params.minimum_mixnode_delegation {
|
||||
if let Some(ref old) = old_params.minimum_mixnode_delegation {
|
||||
event = event.add_attribute(OLD_MINIMUM_DELEGATION_KEY, old.to_string())
|
||||
} else {
|
||||
event = event.add_attribute(OLD_MINIMUM_DELEGATION_KEY, "None")
|
||||
}
|
||||
if let Some(ref new) = new_params.minimum_mixnode_delegation {
|
||||
event = event.add_attribute(NEW_MINIMUM_DELEGATION_KEY, new.to_string())
|
||||
} else {
|
||||
event = event.add_attribute(NEW_MINIMUM_DELEGATION_KEY, "None")
|
||||
}
|
||||
}
|
||||
|
||||
event
|
||||
}
|
||||
|
||||
pub fn new_not_found_mix_operator_rewarding_event(
|
||||
interval_id: u32,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
) -> Event {
|
||||
Event::new(OPERATOR_REWARDING_EVENT_TYPE)
|
||||
.add_attribute(INTERVAL_ID_KEY, interval_id.to_string())
|
||||
.add_attribute(NODE_IDENTITY_KEY, identity)
|
||||
pub fn new_not_found_mix_operator_rewarding_event(interval: Interval, mix_id: NodeId) -> Event {
|
||||
Event::new(MixnetEventType::MixnodeRewarding)
|
||||
.add_attribute(
|
||||
INTERVAL_KEY,
|
||||
interval.current_epoch_absolute_id().to_string(),
|
||||
)
|
||||
.add_attribute(MIX_ID_KEY, mix_id.to_string())
|
||||
.add_attribute(NO_REWARD_REASON_KEY, BOND_NOT_FOUND_VALUE)
|
||||
}
|
||||
|
||||
pub fn new_too_fresh_bond_mix_operator_rewarding_event(
|
||||
interval_id: u32,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
) -> Event {
|
||||
Event::new(OPERATOR_REWARDING_EVENT_TYPE)
|
||||
.add_attribute(INTERVAL_ID_KEY, interval_id.to_string())
|
||||
.add_attribute(NODE_IDENTITY_KEY, identity)
|
||||
.add_attribute(NO_REWARD_REASON_KEY, BOND_TOO_FRESH_VALUE)
|
||||
}
|
||||
|
||||
pub fn new_zero_uptime_mix_operator_rewarding_event(
|
||||
interval_id: u32,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
) -> Event {
|
||||
Event::new(OPERATOR_REWARDING_EVENT_TYPE)
|
||||
.add_attribute(INTERVAL_ID_KEY, interval_id.to_string())
|
||||
.add_attribute(NODE_IDENTITY_KEY, identity)
|
||||
.add_attribute(NO_REWARD_REASON_KEY, ZERO_UPTIME_VALUE)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new_mix_operator_rewarding_event(
|
||||
interval_id: u32,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
node_reward_result: NodeRewardResult,
|
||||
node_pledge: Uint128,
|
||||
node_delegation: Uint128,
|
||||
) -> Event {
|
||||
Event::new(OPERATOR_REWARDING_EVENT_TYPE)
|
||||
.add_attribute(INTERVAL_ID_KEY, interval_id.to_string())
|
||||
.add_attribute(NODE_IDENTITY_KEY, identity)
|
||||
.add_attribute(TOTAL_PLEDGE_KEY, node_pledge)
|
||||
.add_attribute(TOTAL_DELEGATIONS_KEY, node_delegation)
|
||||
pub fn new_zero_uptime_mix_operator_rewarding_event(interval: Interval, mix_id: NodeId) -> Event {
|
||||
Event::new(MixnetEventType::MixnodeRewarding)
|
||||
.add_attribute(
|
||||
TOTAL_MIXNODE_REWARD_KEY,
|
||||
node_reward_result.reward().to_string(),
|
||||
INTERVAL_KEY,
|
||||
interval.current_epoch_absolute_id().to_string(),
|
||||
)
|
||||
.add_attribute(LAMBDA_KEY, node_reward_result.lambda().to_string())
|
||||
.add_attribute(SIGMA_KEY, node_reward_result.sigma().to_string())
|
||||
.add_attribute(MIX_ID_KEY, mix_id.to_string())
|
||||
.add_attribute(NO_REWARD_REASON_KEY, ZERO_PERFORMANCE_VALUE)
|
||||
}
|
||||
|
||||
pub fn new_mix_delegators_rewarding_event(
|
||||
interval_id: u32,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
delegation_rewards_distributed: Uint128,
|
||||
further_delegations: bool,
|
||||
pub fn new_mix_rewarding_event(
|
||||
interval: Interval,
|
||||
mix_id: NodeId,
|
||||
reward_distribution: RewardDistribution,
|
||||
prior_delegates: Decimal,
|
||||
prior_unit_delegation: Decimal,
|
||||
) -> Event {
|
||||
Event::new(MIX_DELEGATORS_REWARDING_EVENT_TYPE)
|
||||
.add_attribute(INTERVAL_ID_KEY, interval_id.to_string())
|
||||
.add_attribute(NODE_IDENTITY_KEY, identity)
|
||||
Event::new(MixnetEventType::MixnodeRewarding)
|
||||
.add_attribute(
|
||||
DISTRIBUTED_DELEGATION_REWARDS_KEY,
|
||||
delegation_rewards_distributed,
|
||||
INTERVAL_KEY,
|
||||
interval.current_epoch_absolute_id().to_string(),
|
||||
)
|
||||
.add_attribute(PRIOR_DELEGATES_KEY, prior_delegates.to_string())
|
||||
.add_attribute(PRIOR_UNIT_DELEGATION_KEY, prior_unit_delegation.to_string())
|
||||
.add_attribute(MIX_ID_KEY, mix_id.to_string())
|
||||
.add_attribute(
|
||||
OPERATOR_REWARD_KEY,
|
||||
reward_distribution.operator.to_string(),
|
||||
)
|
||||
.add_attribute(
|
||||
FURTHER_DELEGATIONS_TO_REWARD_KEY,
|
||||
further_delegations.to_string(),
|
||||
DELEGATES_REWARD_KEY,
|
||||
reward_distribution.delegates.to_string(),
|
||||
)
|
||||
}
|
||||
|
||||
// note that when this event is emitted, we'll know the current block height
|
||||
pub fn new_change_rewarded_set_event(
|
||||
active_set_size: u32,
|
||||
rewarded_set_size: u32,
|
||||
nodes_in_rewarded_set: u32,
|
||||
pub fn new_advance_epoch_event(interval: Interval, rewarded_nodes: u32) -> Event {
|
||||
Event::new(MixnetEventType::AdvanceEpoch)
|
||||
.add_attribute(
|
||||
NEW_CURRENT_EPOCH_KEY,
|
||||
interval.current_epoch_absolute_id().to_string(),
|
||||
)
|
||||
.add_attribute(REWARDED_SET_NODES_KEY, rewarded_nodes.to_string())
|
||||
}
|
||||
|
||||
pub fn new_pending_epoch_events_execution_event(executed: u32) -> Event {
|
||||
Event::new(MixnetEventType::ExecutePendingEpochEvents)
|
||||
.add_attribute(EVENTS_EXECUTED_KEY, executed.to_string())
|
||||
}
|
||||
|
||||
pub fn new_pending_interval_events_execution_event(executed: u32) -> Event {
|
||||
Event::new(MixnetEventType::ExecutePendingIntervalEvents)
|
||||
.add_attribute(EVENTS_EXECUTED_KEY, executed.to_string())
|
||||
}
|
||||
|
||||
pub fn new_reconcile_pending_events() -> Event {
|
||||
Event::new(MixnetEventType::ReconcilePendingEvents)
|
||||
}
|
||||
|
||||
pub fn new_interval_config_update_event(
|
||||
epochs_in_interval: u32,
|
||||
epoch_duration_secs: u64,
|
||||
updated_rewarding_params: IntervalRewardParams,
|
||||
) -> Event {
|
||||
Event::new(CHANGE_REWARDED_SET_EVENT_TYPE)
|
||||
.add_attribute(ACTIVE_SET_SIZE_KEY, active_set_size.to_string())
|
||||
.add_attribute(REWARDED_SET_SIZE_KEY, rewarded_set_size.to_string())
|
||||
.add_attribute(NODES_IN_REWARDED_SET_KEY, nodes_in_rewarded_set.to_string())
|
||||
Event::new(MixnetEventType::IntervalConfigUpdate)
|
||||
.add_attribute(
|
||||
NEW_EPOCHS_DURATION_SECS_KEY,
|
||||
epoch_duration_secs.to_string(),
|
||||
)
|
||||
.add_attribute(NEW_EPOCHS_IN_INTERVAL, epochs_in_interval.to_string())
|
||||
.add_attribute(
|
||||
UPDATED_INTERVAL_REWARDING_PARAMS_KEY,
|
||||
updated_rewarding_params.to_inline_json(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_advance_interval_event(interval: Interval) -> Event {
|
||||
Event::new(ADVANCE_INTERVAL_EVENT_TYPE)
|
||||
.add_attribute(NEW_CURRENT_INTERVAL_KEY, interval.to_string())
|
||||
}
|
||||
|
||||
pub fn new_advance_epoch_event(interval: Interval) -> Event {
|
||||
Event::new(ADVANCE_EPOCH_EVENT_TYPE).add_attribute(NEW_CURRENT_EPOCH_KEY, interval.to_string())
|
||||
pub fn new_pending_interval_config_update_event(
|
||||
epochs_in_interval: u32,
|
||||
epoch_duration_secs: u64,
|
||||
approximate_time_remaining_secs: i64,
|
||||
) -> Event {
|
||||
Event::new(MixnetEventType::PendingIntervalConfigUpdate)
|
||||
.add_attribute(
|
||||
NEW_EPOCHS_DURATION_SECS_KEY,
|
||||
epoch_duration_secs.to_string(),
|
||||
)
|
||||
.add_attribute(NEW_EPOCHS_IN_INTERVAL, epochs_in_interval.to_string())
|
||||
.add_attribute(
|
||||
APPROXIMATE_TIME_LEFT_SECS_KEY,
|
||||
approximate_time_remaining_secs.to_string(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// due to code generated by JsonSchema
|
||||
#![allow(clippy::field_reassign_with_default)]
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::pending_events::{PendingEpochEvent, PendingIntervalEvent};
|
||||
use crate::{EpochId, IntervalId};
|
||||
use cosmwasm_std::Env;
|
||||
use schemars::gen::SchemaGenerator;
|
||||
use schemars::schema::{InstanceType, Schema, SchemaObject};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::convert::TryInto;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::time::Duration;
|
||||
use time::OffsetDateTime;
|
||||
@@ -59,12 +60,16 @@ pub(crate) mod string_rfc3339_offset_date_time {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, PartialOrd, Serialize)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, Serialize)]
|
||||
pub struct Interval {
|
||||
id: u32,
|
||||
id: IntervalId,
|
||||
epochs_in_interval: u32,
|
||||
|
||||
#[serde(with = "string_rfc3339_offset_date_time")]
|
||||
start: OffsetDateTime,
|
||||
length: Duration,
|
||||
current_epoch_start: OffsetDateTime,
|
||||
current_epoch_id: EpochId,
|
||||
epoch_length: Duration,
|
||||
total_elapsed_epochs: EpochId,
|
||||
}
|
||||
|
||||
impl JsonSchema for Interval {
|
||||
@@ -81,20 +86,46 @@ impl JsonSchema for Interval {
|
||||
let object_validation = schema_object.object();
|
||||
object_validation
|
||||
.properties
|
||||
.insert("id".to_owned(), gen.subschema_for::<u32>());
|
||||
.insert("id".to_owned(), gen.subschema_for::<IntervalId>());
|
||||
object_validation.required.insert("id".to_owned());
|
||||
|
||||
object_validation
|
||||
.properties
|
||||
.insert("epochs_in_interval".to_owned(), gen.subschema_for::<u32>());
|
||||
object_validation
|
||||
.required
|
||||
.insert("epochs_in_interval".to_owned());
|
||||
|
||||
// PrimitiveDateTime does not implement JsonSchema. However it has a custom
|
||||
// serialization to string, so we just specify the schema to be String.
|
||||
object_validation.properties.insert(
|
||||
"current_epoch_start".to_owned(),
|
||||
gen.subschema_for::<String>(),
|
||||
);
|
||||
object_validation
|
||||
.properties
|
||||
.insert("start".to_owned(), gen.subschema_for::<String>());
|
||||
object_validation.required.insert("start".to_owned());
|
||||
.required
|
||||
.insert("current_epoch_start".to_owned());
|
||||
|
||||
object_validation.properties.insert(
|
||||
"current_epoch_id".to_owned(),
|
||||
gen.subschema_for::<EpochId>(),
|
||||
);
|
||||
object_validation
|
||||
.required
|
||||
.insert("current_epoch_id".to_owned());
|
||||
|
||||
object_validation
|
||||
.properties
|
||||
.insert("length".to_owned(), gen.subschema_for::<Duration>());
|
||||
object_validation.required.insert("length".to_owned());
|
||||
.insert("epoch_length".to_owned(), gen.subschema_for::<Duration>());
|
||||
object_validation.required.insert("epoch_length".to_owned());
|
||||
|
||||
object_validation.properties.insert(
|
||||
"total_elapsed_epochs".to_owned(),
|
||||
gen.subschema_for::<EpochId>(),
|
||||
);
|
||||
object_validation
|
||||
.required
|
||||
.insert("total_elapsed_epochs".to_owned());
|
||||
|
||||
Schema::Object(schema_object)
|
||||
}
|
||||
@@ -102,328 +133,451 @@ impl JsonSchema for Interval {
|
||||
|
||||
impl Interval {
|
||||
/// Initialize epoch in the contract with default values.
|
||||
pub fn init_epoch(env: Env) -> Self {
|
||||
pub fn init_interval(epochs_in_interval: u32, epoch_length: Duration, env: &Env) -> Self {
|
||||
Interval {
|
||||
id: 0,
|
||||
epochs_in_interval,
|
||||
// I really don't see a way for this to fail, unless the blockchain is lying to us
|
||||
start: OffsetDateTime::from_unix_timestamp(env.block.time.seconds() as i64)
|
||||
.expect("Invalid timestamp from env.block.time"),
|
||||
length: Duration::from_secs(3600),
|
||||
current_epoch_start: OffsetDateTime::from_unix_timestamp(
|
||||
env.block.time.seconds() as i64
|
||||
)
|
||||
.expect("Invalid timestamp from env.block.time"),
|
||||
current_epoch_id: 0,
|
||||
epoch_length,
|
||||
total_elapsed_epochs: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_over(&self, env: Env) -> bool {
|
||||
self.end_unix_timestamp() <= env.block.time.seconds() as i64
|
||||
pub const fn current_epoch_id(&self) -> EpochId {
|
||||
self.current_epoch_id
|
||||
}
|
||||
|
||||
pub fn in_progress(&self, env: Env) -> bool {
|
||||
let block_time = env.block.time.seconds() as i64;
|
||||
self.start_unix_timestamp() <= block_time && block_time < self.end_unix_timestamp()
|
||||
}
|
||||
|
||||
pub fn update_duration(&mut self, secs: u64) {
|
||||
self.length = Duration::from_secs(secs);
|
||||
}
|
||||
|
||||
pub const fn length_secs(&self) -> u64 {
|
||||
self.length.as_secs()
|
||||
}
|
||||
|
||||
/// Returns the next interval.
|
||||
#[must_use]
|
||||
pub fn next(&self) -> Self {
|
||||
Interval {
|
||||
id: self.id + 1,
|
||||
start: self.end(),
|
||||
length: self.length,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_on_chain(&self, env: Env) -> Self {
|
||||
let start = self
|
||||
.end()
|
||||
.max(OffsetDateTime::from_unix_timestamp(env.block.time.seconds() as i64).unwrap());
|
||||
Interval {
|
||||
id: self.id + 1,
|
||||
start,
|
||||
length: self.length,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the last interval.
|
||||
pub fn previous(&self) -> Option<Self> {
|
||||
if self.id > 0 {
|
||||
Some(Interval {
|
||||
id: self.id - 1,
|
||||
start: self.start - self.length,
|
||||
length: self.length,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Determines whether the provided datetime is contained within the interval
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `datetime`: specified datetime
|
||||
pub fn contains(&self, datetime: OffsetDateTime) -> bool {
|
||||
self.start <= datetime && datetime <= self.end()
|
||||
}
|
||||
|
||||
/// Determines whether the provided unix timestamp is contained within the interval
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `timestamp`: specified timestamp
|
||||
pub fn contains_timestamp(&self, timestamp: i64) -> bool {
|
||||
self.start_unix_timestamp() <= timestamp && timestamp <= self.end_unix_timestamp()
|
||||
}
|
||||
|
||||
/// Returns new instance of [Interval] such that the provided datetime would be within
|
||||
/// its duration.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `now`: current datetime
|
||||
pub fn current(&self, now: OffsetDateTime) -> Option<Self> {
|
||||
let mut candidate = *self;
|
||||
|
||||
if now > self.start {
|
||||
loop {
|
||||
if candidate.contains(now) {
|
||||
return Some(candidate);
|
||||
}
|
||||
candidate = candidate.next();
|
||||
}
|
||||
} else {
|
||||
loop {
|
||||
if candidate.contains(now) {
|
||||
return Some(candidate);
|
||||
}
|
||||
candidate = candidate.previous()?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns new instance of [Interval] such that the provided unix timestamp would be within
|
||||
/// its duration.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `now_unix`: current unix time
|
||||
pub fn current_with_timestamp(&self, now_unix: i64) -> Option<Self> {
|
||||
let mut candidate = *self;
|
||||
|
||||
if now_unix > self.start_unix_timestamp() {
|
||||
loop {
|
||||
if candidate.contains_timestamp(now_unix) {
|
||||
return Some(candidate);
|
||||
}
|
||||
candidate = candidate.next();
|
||||
}
|
||||
} else {
|
||||
loop {
|
||||
if candidate.contains_timestamp(now_unix) {
|
||||
return Some(candidate);
|
||||
}
|
||||
candidate = candidate.previous()?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks whether this interval has already finished
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `now`: current datetime
|
||||
pub fn has_elapsed(&self, now: OffsetDateTime) -> bool {
|
||||
self.end() < now
|
||||
}
|
||||
|
||||
/// Returns id of this interval
|
||||
pub const fn id(&self) -> u32 {
|
||||
pub const fn current_interval_id(&self) -> IntervalId {
|
||||
self.id
|
||||
}
|
||||
|
||||
/// Determines amount of time left until this interval finishes.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `now`: current datetime
|
||||
pub fn until_end(&self, now: OffsetDateTime) -> Option<Duration> {
|
||||
let remaining = self.end() - now;
|
||||
if remaining.is_negative() {
|
||||
None
|
||||
pub const fn epochs_in_interval(&self) -> u32 {
|
||||
self.epochs_in_interval
|
||||
}
|
||||
|
||||
pub fn force_change_epochs_in_interval(&mut self, epochs_in_interval: u32) {
|
||||
self.epochs_in_interval = epochs_in_interval;
|
||||
if self.current_epoch_id >= epochs_in_interval {
|
||||
// we have to go to the next interval as we can't
|
||||
// have the same (interval, epoch) combo as we had in the past
|
||||
self.id += self.current_epoch_id / epochs_in_interval;
|
||||
self.current_epoch_id %= epochs_in_interval;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn change_epoch_length(&mut self, epoch_length: Duration) {
|
||||
self.epoch_length = epoch_length
|
||||
}
|
||||
|
||||
pub const fn total_elapsed_epochs(&self) -> u32 {
|
||||
self.total_elapsed_epochs
|
||||
}
|
||||
|
||||
pub const fn current_epoch_absolute_id(&self) -> u32 {
|
||||
// since we count epochs starting from 0, if n epochs have elapsed, the current one has absolute id of n
|
||||
self.total_elapsed_epochs
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_current_epoch_over(&self, env: &Env) -> bool {
|
||||
self.current_epoch_end_unix_timestamp() <= env.block.time.seconds() as i64
|
||||
}
|
||||
|
||||
pub fn secs_until_current_epoch_end(&self, env: &Env) -> i64 {
|
||||
if self.is_current_epoch_over(env) {
|
||||
0
|
||||
} else {
|
||||
remaining.try_into().ok()
|
||||
self.current_epoch_end_unix_timestamp() - env.block.time.seconds() as i64
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_current_interval_over(&self, env: &Env) -> bool {
|
||||
self.current_interval_end_unix_timestamp() <= env.block.time.seconds() as i64
|
||||
}
|
||||
|
||||
pub fn secs_until_current_interval_end(&self, env: &Env) -> i64 {
|
||||
if self.is_current_interval_over(env) {
|
||||
0
|
||||
} else {
|
||||
self.current_interval_end_unix_timestamp() - env.block.time.seconds() as i64
|
||||
}
|
||||
}
|
||||
|
||||
pub fn current_epoch_in_progress(&self, env: &Env) -> bool {
|
||||
let block_time = env.block.time.seconds() as i64;
|
||||
self.current_epoch_start_unix_timestamp() <= block_time
|
||||
&& block_time < self.current_epoch_end_unix_timestamp()
|
||||
}
|
||||
|
||||
pub fn update_epoch_duration(&mut self, secs: u64) {
|
||||
self.epoch_length = Duration::from_secs(secs);
|
||||
}
|
||||
|
||||
pub const fn epoch_length_secs(&self) -> u64 {
|
||||
self.epoch_length.as_secs()
|
||||
}
|
||||
|
||||
/// Returns the next epoch. If if would result in advancing the interval,
|
||||
/// the relevant changes are applied.
|
||||
#[must_use]
|
||||
pub fn advance_epoch(&self) -> Self {
|
||||
// remember we start from 0th epoch, so if we're supposed to have 100 epochs in interval,
|
||||
// epoch 99 is going to be the last one
|
||||
if self.current_epoch_id == self.epochs_in_interval - 1 {
|
||||
Interval {
|
||||
id: self.id + 1,
|
||||
epochs_in_interval: self.epochs_in_interval,
|
||||
current_epoch_start: self.current_epoch_end(),
|
||||
current_epoch_id: 0,
|
||||
epoch_length: self.epoch_length,
|
||||
total_elapsed_epochs: self.total_elapsed_epochs + 1,
|
||||
}
|
||||
} else {
|
||||
Interval {
|
||||
id: self.id,
|
||||
epochs_in_interval: self.epochs_in_interval,
|
||||
current_epoch_start: self.current_epoch_end(),
|
||||
current_epoch_id: self.current_epoch_id + 1,
|
||||
epoch_length: self.epoch_length,
|
||||
total_elapsed_epochs: self.total_elapsed_epochs + 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the starting datetime of this interval.
|
||||
pub const fn start(&self) -> OffsetDateTime {
|
||||
self.start
|
||||
pub const fn current_epoch_start(&self) -> OffsetDateTime {
|
||||
self.current_epoch_start
|
||||
}
|
||||
|
||||
/// Returns the length of this interval.
|
||||
pub const fn length(&self) -> Duration {
|
||||
self.length
|
||||
pub const fn epoch_length(&self) -> Duration {
|
||||
self.epoch_length
|
||||
}
|
||||
|
||||
/// Returns the ending datetime of this interval.
|
||||
pub fn end(&self) -> OffsetDateTime {
|
||||
self.start + self.length
|
||||
/// Returns the ending datetime of the current epoch.
|
||||
pub fn current_epoch_end(&self) -> OffsetDateTime {
|
||||
self.current_epoch_start + self.epoch_length
|
||||
}
|
||||
|
||||
/// Returns the unix timestamp of the start of this interval.
|
||||
pub const fn start_unix_timestamp(&self) -> i64 {
|
||||
self.start().unix_timestamp()
|
||||
pub fn epochs_until_interval_end(&self) -> u32 {
|
||||
self.epochs_in_interval - self.current_epoch_id
|
||||
}
|
||||
|
||||
/// Returns the unix timestamp of the end of this interval.
|
||||
pub fn end_unix_timestamp(&self) -> i64 {
|
||||
self.end().unix_timestamp()
|
||||
/// Returns the ending datetime of the current interval.
|
||||
pub fn current_interval_end(&self) -> OffsetDateTime {
|
||||
self.current_epoch_start + self.epochs_until_interval_end() * self.epoch_length
|
||||
}
|
||||
|
||||
/// Returns the unix timestamp of the start of the current epoch.
|
||||
pub const fn current_epoch_start_unix_timestamp(&self) -> i64 {
|
||||
self.current_epoch_start().unix_timestamp()
|
||||
}
|
||||
|
||||
/// Returns the unix timestamp of the end of the current epoch.
|
||||
#[inline]
|
||||
pub fn current_epoch_end_unix_timestamp(&self) -> i64 {
|
||||
self.current_epoch_end().unix_timestamp()
|
||||
}
|
||||
|
||||
/// Returns the unix timestamp of the end of the current interval.
|
||||
#[inline]
|
||||
pub fn current_interval_end_unix_timestamp(&self) -> i64 {
|
||||
self.current_interval_end().unix_timestamp()
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Interval {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
let length = self.length().as_secs();
|
||||
let length = self.epoch_length_secs();
|
||||
let full_hours = length / 3600;
|
||||
let rem = length % 3600;
|
||||
write!(
|
||||
f,
|
||||
"Interval {}: {} - {} ({}h {}s)",
|
||||
"Interval {}: epoch {}/{} (current epoch begun at: {}; epoch lengths: {}h {}s)",
|
||||
self.id,
|
||||
self.start(),
|
||||
self.end(),
|
||||
self.current_epoch_id + 1,
|
||||
self.epochs_in_interval,
|
||||
self.current_epoch_start,
|
||||
full_hours,
|
||||
rem
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, Serialize)]
|
||||
pub struct CurrentIntervalResponse {
|
||||
pub interval: Interval,
|
||||
pub current_blocktime: u64,
|
||||
pub is_current_interval_over: bool,
|
||||
pub is_current_epoch_over: bool,
|
||||
}
|
||||
|
||||
impl CurrentIntervalResponse {
|
||||
pub fn new(interval: Interval, env: Env) -> Self {
|
||||
CurrentIntervalResponse {
|
||||
interval,
|
||||
current_blocktime: env.block.time.seconds(),
|
||||
is_current_interval_over: interval.is_current_interval_over(&env),
|
||||
is_current_epoch_over: interval.is_current_epoch_over(&env),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn time_until_current_epoch_end(&self) -> Duration {
|
||||
if self.is_current_epoch_over {
|
||||
Duration::from_secs(0)
|
||||
} else {
|
||||
let remaining_secs =
|
||||
self.interval.current_epoch_end_unix_timestamp() - self.current_blocktime as i64;
|
||||
// this should never be negative, but better safe than sorry and guard ourselves against that case
|
||||
if remaining_secs <= 0 {
|
||||
Duration::from_secs(0)
|
||||
} else {
|
||||
Duration::from_secs(remaining_secs as u64)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
pub struct PendingEpochEventsResponse {
|
||||
pub seconds_until_executable: i64,
|
||||
pub events: Vec<PendingEpochEvent>,
|
||||
pub start_next_after: Option<u32>,
|
||||
}
|
||||
|
||||
impl PendingEpochEventsResponse {
|
||||
pub fn new(
|
||||
seconds_until_executable: i64,
|
||||
events: Vec<PendingEpochEvent>,
|
||||
start_next_after: Option<u32>,
|
||||
) -> Self {
|
||||
PendingEpochEventsResponse {
|
||||
seconds_until_executable,
|
||||
events,
|
||||
start_next_after,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
pub struct PendingIntervalEventsResponse {
|
||||
pub seconds_until_executable: i64,
|
||||
pub events: Vec<PendingIntervalEvent>,
|
||||
pub start_next_after: Option<u32>,
|
||||
}
|
||||
|
||||
impl PendingIntervalEventsResponse {
|
||||
pub fn new(
|
||||
seconds_until_executable: i64,
|
||||
events: Vec<PendingIntervalEvent>,
|
||||
start_next_after: Option<u32>,
|
||||
) -> Self {
|
||||
PendingIntervalEventsResponse {
|
||||
seconds_until_executable,
|
||||
events,
|
||||
start_next_after,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use cosmwasm_std::testing::mock_env;
|
||||
use rand_chacha::rand_core::{RngCore, SeedableRng};
|
||||
|
||||
#[test]
|
||||
fn previous() {
|
||||
fn advancing_epoch() {
|
||||
// just advancing epoch
|
||||
let interval = Interval {
|
||||
id: 1,
|
||||
start: time::macros::datetime!(2021-08-23 12:00 UTC),
|
||||
length: Duration::from_secs(24 * 60 * 60),
|
||||
id: 0,
|
||||
epochs_in_interval: 100,
|
||||
current_epoch_start: time::macros::datetime!(2021-08-23 12:00 UTC),
|
||||
current_epoch_id: 23,
|
||||
epoch_length: Duration::from_secs(60 * 60),
|
||||
total_elapsed_epochs: 0,
|
||||
};
|
||||
let expected = Interval {
|
||||
id: 0,
|
||||
start: time::macros::datetime!(2021-08-22 12:00 UTC),
|
||||
length: Duration::from_secs(24 * 60 * 60),
|
||||
epochs_in_interval: 100,
|
||||
current_epoch_start: time::macros::datetime!(2021-08-23 13:00 UTC),
|
||||
current_epoch_id: 24,
|
||||
epoch_length: Duration::from_secs(60 * 60),
|
||||
total_elapsed_epochs: 1,
|
||||
};
|
||||
assert_eq!(expected, interval.previous().unwrap());
|
||||
assert_eq!(expected, interval.advance_epoch());
|
||||
|
||||
let genesis_interval = Interval {
|
||||
id: 0,
|
||||
start: time::macros::datetime!(2021-08-23 12:00 UTC),
|
||||
length: Duration::from_secs(24 * 60 * 60),
|
||||
};
|
||||
assert!(genesis_interval.previous().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn next() {
|
||||
// results in advancing interval
|
||||
let interval = Interval {
|
||||
id: 0,
|
||||
start: time::macros::datetime!(2021-08-23 12:00 UTC),
|
||||
length: Duration::from_secs(24 * 60 * 60),
|
||||
epochs_in_interval: 100,
|
||||
current_epoch_start: time::macros::datetime!(2021-08-23 12:00 UTC),
|
||||
current_epoch_id: 99,
|
||||
epoch_length: Duration::from_secs(60 * 60),
|
||||
total_elapsed_epochs: 42,
|
||||
};
|
||||
let expected = Interval {
|
||||
id: 1,
|
||||
start: time::macros::datetime!(2021-08-24 12:00 UTC),
|
||||
length: Duration::from_secs(24 * 60 * 60),
|
||||
epochs_in_interval: 100,
|
||||
current_epoch_start: time::macros::datetime!(2021-08-23 13:00 UTC),
|
||||
current_epoch_id: 0,
|
||||
epoch_length: Duration::from_secs(60 * 60),
|
||||
total_elapsed_epochs: 43,
|
||||
};
|
||||
|
||||
assert_eq!(expected, interval.next())
|
||||
assert_eq!(expected, interval.advance_epoch())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn checking_for_datetime_inclusion() {
|
||||
fn checking_for_epoch_ends() {
|
||||
let env = mock_env();
|
||||
|
||||
// epoch just begun
|
||||
let interval = Interval {
|
||||
id: 100,
|
||||
start: time::macros::datetime!(2021-08-23 12:00 UTC),
|
||||
length: Duration::from_secs(24 * 60 * 60),
|
||||
id: 0,
|
||||
epochs_in_interval: 100,
|
||||
current_epoch_start: OffsetDateTime::from_unix_timestamp(
|
||||
env.block.time.seconds() as i64 - 100,
|
||||
)
|
||||
.unwrap(),
|
||||
current_epoch_id: 23,
|
||||
epoch_length: Duration::from_secs(60 * 60),
|
||||
total_elapsed_epochs: 0,
|
||||
};
|
||||
assert!(!interval.is_current_epoch_over(&env));
|
||||
|
||||
// it must contain its own boundaries
|
||||
assert!(interval.contains(interval.start));
|
||||
assert!(interval.contains(interval.end()));
|
||||
// current time == current epoch start
|
||||
let mut interval = interval;
|
||||
interval.current_epoch_start =
|
||||
OffsetDateTime::from_unix_timestamp(env.block.time.seconds() as i64).unwrap();
|
||||
assert!(!interval.is_current_epoch_over(&env));
|
||||
|
||||
let in_the_midle = interval.start + Duration::from_secs(interval.length.as_secs() / 2);
|
||||
assert!(interval.contains(in_the_midle));
|
||||
// epoch HASN'T yet begun (weird edge case, but can happen if we decide to manually adjust things)
|
||||
let mut interval = interval;
|
||||
interval.current_epoch_start =
|
||||
OffsetDateTime::from_unix_timestamp(env.block.time.seconds() as i64 + 100).unwrap();
|
||||
assert!(!interval.is_current_epoch_over(&env));
|
||||
|
||||
assert!(!interval.contains(interval.next().end()));
|
||||
assert!(!interval.contains(interval.previous().unwrap().start()));
|
||||
// current_time = EXACTLY end of the epoch
|
||||
let mut interval = interval;
|
||||
interval.current_epoch_start =
|
||||
OffsetDateTime::from_unix_timestamp(env.block.time.seconds() as i64).unwrap()
|
||||
- interval.epoch_length;
|
||||
assert!(interval.is_current_epoch_over(&env));
|
||||
|
||||
// revert time a bit more
|
||||
interval.current_epoch_start -= Duration::from_secs(42);
|
||||
assert!(interval.is_current_epoch_over(&env));
|
||||
|
||||
// revert by A LOT -> epoch still should be in finished state
|
||||
interval.current_epoch_start -= Duration::from_secs(5 * 31 * 60 * 60);
|
||||
assert!(interval.is_current_epoch_over(&env));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn determining_current_interval() {
|
||||
let first_interval = Interval {
|
||||
id: 100,
|
||||
start: time::macros::datetime!(2021-08-23 12:00 UTC),
|
||||
length: Duration::from_secs(24 * 60 * 60),
|
||||
fn interval_end() {
|
||||
let mut interval = Interval {
|
||||
id: 0,
|
||||
epochs_in_interval: 100,
|
||||
current_epoch_start: time::macros::datetime!(2021-08-23 12:00 UTC),
|
||||
current_epoch_id: 99,
|
||||
epoch_length: Duration::from_secs(60 * 60),
|
||||
total_elapsed_epochs: 0,
|
||||
};
|
||||
|
||||
// interval just before
|
||||
let fake_now = first_interval.start - Duration::from_secs(123);
|
||||
assert_eq!(first_interval.previous(), first_interval.current(fake_now));
|
||||
|
||||
// this interval (start boundary)
|
||||
assert_eq!(
|
||||
first_interval,
|
||||
first_interval.current(first_interval.start).unwrap()
|
||||
interval.current_epoch_start + interval.epoch_length,
|
||||
interval.current_interval_end()
|
||||
);
|
||||
|
||||
// this interval (in the middle)
|
||||
let fake_now = first_interval.start + Duration::from_secs(123);
|
||||
assert_eq!(first_interval, first_interval.current(fake_now).unwrap());
|
||||
|
||||
// this interval (end boundary)
|
||||
interval.current_epoch_id -= 1;
|
||||
assert_eq!(
|
||||
first_interval,
|
||||
first_interval.current(first_interval.end()).unwrap()
|
||||
interval.current_epoch_start + 2 * interval.epoch_length,
|
||||
interval.current_interval_end()
|
||||
);
|
||||
|
||||
// next interval
|
||||
let fake_now = first_interval.end() + Duration::from_secs(123);
|
||||
interval.current_epoch_id -= 10;
|
||||
assert_eq!(
|
||||
first_interval.next(),
|
||||
first_interval.current(fake_now).unwrap()
|
||||
interval.current_epoch_start + 12 * interval.epoch_length,
|
||||
interval.current_interval_end()
|
||||
);
|
||||
|
||||
// few intervals in the past
|
||||
let fake_now = first_interval.start()
|
||||
- first_interval.length
|
||||
- first_interval.length
|
||||
- first_interval.length;
|
||||
interval.current_epoch_id = 0;
|
||||
assert_eq!(
|
||||
first_interval
|
||||
.previous()
|
||||
.unwrap()
|
||||
.previous()
|
||||
.unwrap()
|
||||
.previous()
|
||||
.unwrap(),
|
||||
first_interval.current(fake_now).unwrap()
|
||||
interval.current_epoch_start + interval.epochs_in_interval * interval.epoch_length,
|
||||
interval.current_interval_end()
|
||||
);
|
||||
}
|
||||
|
||||
// few intervals in the future
|
||||
let fake_now = first_interval.end()
|
||||
+ first_interval.length
|
||||
+ first_interval.length
|
||||
+ first_interval.length;
|
||||
assert_eq!(
|
||||
first_interval.next().next().next(),
|
||||
first_interval.current(fake_now).unwrap()
|
||||
);
|
||||
#[test]
|
||||
fn checking_for_interval_ends() {
|
||||
let env = mock_env();
|
||||
|
||||
let epoch_length = Duration::from_secs(60 * 60);
|
||||
|
||||
let mut interval = Interval {
|
||||
id: 0,
|
||||
epochs_in_interval: 100,
|
||||
current_epoch_start: OffsetDateTime::from_unix_timestamp(
|
||||
env.block.time.seconds() as i64
|
||||
)
|
||||
.unwrap(),
|
||||
current_epoch_id: 98,
|
||||
epoch_length,
|
||||
total_elapsed_epochs: 0,
|
||||
};
|
||||
|
||||
// current epoch just started (we still have to finish 2 epochs)
|
||||
assert!(!interval.is_current_interval_over(&env));
|
||||
|
||||
// still need to finish the 99th epoch
|
||||
interval.current_epoch_start -= epoch_length;
|
||||
assert!(!interval.is_current_interval_over(&env));
|
||||
|
||||
// it JUST finished
|
||||
interval.current_epoch_start -= epoch_length;
|
||||
assert!(interval.is_current_interval_over(&env));
|
||||
|
||||
// nobody updated the interval data, but the current one should still be in finished state
|
||||
interval.current_epoch_start -= 10 * epoch_length;
|
||||
assert!(interval.is_current_interval_over(&env));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn getting_current_full_epoch_id() {
|
||||
let env = mock_env();
|
||||
let dummy_seed = [42u8; 32];
|
||||
let mut rng = rand_chacha::ChaCha20Rng::from_seed(dummy_seed);
|
||||
|
||||
let epoch_length = Duration::from_secs(60 * 60);
|
||||
|
||||
let mut interval = Interval::init_interval(100, epoch_length, &env);
|
||||
|
||||
// normal situation
|
||||
for i in 0u32..2000 {
|
||||
assert_eq!(interval.current_epoch_absolute_id(), i);
|
||||
interval = interval.advance_epoch();
|
||||
}
|
||||
|
||||
let mut interval = Interval::init_interval(100, epoch_length, &env);
|
||||
|
||||
for i in 0u32..2000 {
|
||||
// every few epochs decide to change epochs in interval
|
||||
if i % 7 == 0 {
|
||||
let new_epochs_in_interval = (rng.next_u32() % 200) + 42;
|
||||
interval.force_change_epochs_in_interval(new_epochs_in_interval)
|
||||
}
|
||||
|
||||
// make sure full epoch id is always monotonically increasing
|
||||
assert_eq!(interval.current_epoch_absolute_id(), i);
|
||||
|
||||
interval = interval.advance_epoch();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
mod constants;
|
||||
pub mod delegation;
|
||||
pub mod error;
|
||||
pub mod events;
|
||||
@@ -8,29 +9,31 @@ pub mod gateway;
|
||||
mod interval;
|
||||
pub mod mixnode;
|
||||
mod msg;
|
||||
pub mod pending_events;
|
||||
pub mod reward_params;
|
||||
pub mod rewarding;
|
||||
mod types;
|
||||
|
||||
pub const MIXNODE_DELEGATORS_PAGE_LIMIT: usize = 250;
|
||||
|
||||
pub use cosmwasm_std::{Addr, Coin};
|
||||
pub use contracts_common::types::*;
|
||||
pub use cosmwasm_std::{Addr, Coin, Decimal, Fraction};
|
||||
pub use delegation::{
|
||||
Delegation, PagedAllDelegationsResponse, PagedDelegatorDelegationsResponse,
|
||||
PagedMixDelegationsResponse,
|
||||
PagedMixNodeDelegationsResponse,
|
||||
};
|
||||
pub use gateway::{
|
||||
Gateway, GatewayBond, GatewayBondResponse, GatewayOwnershipResponse, PagedGatewayResponse,
|
||||
};
|
||||
pub use interval::Interval;
|
||||
pub use interval::{
|
||||
CurrentIntervalResponse, Interval, PendingEpochEventsResponse, PendingIntervalEventsResponse,
|
||||
};
|
||||
pub use mixnode::{
|
||||
Layer, MixNode, MixNodeBond, MixOwnershipResponse, MixnodeBondResponse, PagedMixnodeResponse,
|
||||
RewardedSetNodeStatus,
|
||||
Layer, MixNode, MixNodeBond, MixNodeConfigUpdate, MixNodeCostParams, MixNodeDetails,
|
||||
MixNodeRewarding, MixOwnershipResponse, MixnodeDetailsResponse, PagedMixnodeBondsResponse,
|
||||
RewardedSetNodeStatus, UnbondedMixnode,
|
||||
};
|
||||
pub use msg::*;
|
||||
pub use pending_events::{
|
||||
PendingEpochEvent, PendingEpochEventData, PendingIntervalEvent, PendingIntervalEventData,
|
||||
};
|
||||
pub use reward_params::{IntervalRewardParams, IntervalRewardingParamsUpdate, RewardingParams};
|
||||
pub use types::*;
|
||||
|
||||
pub type U128 = fixed::types::U75F53;
|
||||
|
||||
fixed::const_fixed_from_int! {
|
||||
const ONE: U128 = 1;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,160 +1,342 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::reward_params::NodeRewardParams;
|
||||
use crate::ContractStateParams;
|
||||
use crate::delegation::OwnerProxySubKey;
|
||||
use crate::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
|
||||
use crate::reward_params::{
|
||||
IntervalRewardParams, IntervalRewardingParamsUpdate, Performance, RewardingParams,
|
||||
};
|
||||
use crate::{delegation, ContractStateParams, NodeId, Percent};
|
||||
use crate::{Gateway, IdentityKey, MixNode};
|
||||
use cosmwasm_std::Decimal;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::time::Duration;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct InstantiateMsg {
|
||||
pub rewarding_validator_address: String,
|
||||
pub mixnet_denom: String,
|
||||
pub vesting_contract_address: String,
|
||||
|
||||
pub rewarding_denom: String,
|
||||
pub epochs_in_interval: u32,
|
||||
pub epoch_duration: Duration,
|
||||
pub initial_rewarding_params: InitialRewardingParams,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct InitialRewardingParams {
|
||||
pub initial_reward_pool: Decimal,
|
||||
pub initial_staking_supply: Decimal,
|
||||
|
||||
pub sybil_resistance: Percent,
|
||||
pub active_set_work_factor: Decimal,
|
||||
pub interval_pool_emission: Percent,
|
||||
|
||||
pub rewarded_set_size: u32,
|
||||
pub active_set_size: u32,
|
||||
}
|
||||
|
||||
impl InitialRewardingParams {
|
||||
pub fn into_rewarding_params(self, epochs_in_interval: u32) -> RewardingParams {
|
||||
let epoch_reward_budget = self.initial_reward_pool
|
||||
/ Decimal::from_atomics(epochs_in_interval, 0).unwrap()
|
||||
* self.interval_pool_emission;
|
||||
let stake_saturation_point =
|
||||
self.initial_staking_supply / Decimal::from_atomics(self.rewarded_set_size, 0).unwrap();
|
||||
|
||||
RewardingParams {
|
||||
interval: IntervalRewardParams {
|
||||
reward_pool: self.initial_reward_pool,
|
||||
staking_supply: self.initial_staking_supply,
|
||||
epoch_reward_budget,
|
||||
stake_saturation_point,
|
||||
sybil_resistance: self.sybil_resistance,
|
||||
active_set_work_factor: self.active_set_work_factor,
|
||||
interval_pool_emission: self.interval_pool_emission,
|
||||
},
|
||||
rewarded_set_size: self.rewarded_set_size,
|
||||
active_set_size: self.active_set_size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ExecuteMsg {
|
||||
// state/sys-params-related
|
||||
UpdateRewardingValidatorAddress {
|
||||
address: String,
|
||||
},
|
||||
InitEpoch {},
|
||||
ReconcileDelegations {},
|
||||
CheckpointMixnodes {},
|
||||
CompoundOperatorRewardOnBehalf {
|
||||
owner: String,
|
||||
UpdateContractStateParams {
|
||||
updated_parameters: ContractStateParams,
|
||||
},
|
||||
CompoundDelegatorRewardOnBehalf {
|
||||
owner: String,
|
||||
mix_identity: IdentityKey,
|
||||
UpdateActiveSetSize {
|
||||
active_set_size: u32,
|
||||
force_immediately: bool,
|
||||
},
|
||||
CompoundOperatorReward {},
|
||||
CompoundDelegatorReward {
|
||||
mix_identity: IdentityKey,
|
||||
UpdateRewardingParams {
|
||||
updated_params: IntervalRewardingParamsUpdate,
|
||||
force_immediately: bool,
|
||||
},
|
||||
CompoundReward {
|
||||
operator: Option<String>,
|
||||
delegator: Option<String>,
|
||||
mix_identity: Option<IdentityKey>,
|
||||
proxy: Option<String>,
|
||||
UpdateIntervalConfig {
|
||||
epochs_in_interval: u32,
|
||||
epoch_duration_secs: u64,
|
||||
force_immediately: bool,
|
||||
},
|
||||
AdvanceCurrentEpoch {
|
||||
new_rewarded_set: Vec<NodeId>,
|
||||
expected_active_set_size: u32,
|
||||
},
|
||||
ReconcileEpochEvents {
|
||||
limit: Option<u32>,
|
||||
},
|
||||
|
||||
// mixnode-related:
|
||||
BondMixnode {
|
||||
mix_node: MixNode,
|
||||
cost_params: MixNodeCostParams,
|
||||
owner_signature: String,
|
||||
},
|
||||
UnbondMixnode {},
|
||||
UpdateMixnodeConfig {
|
||||
profit_margin_percent: u8,
|
||||
},
|
||||
UpdateMixnodeConfigOnBehalf {
|
||||
profit_margin_percent: u8,
|
||||
owner: String,
|
||||
},
|
||||
BondGateway {
|
||||
gateway: Gateway,
|
||||
owner_signature: String,
|
||||
},
|
||||
UnbondGateway {},
|
||||
UpdateContractStateParams(ContractStateParams),
|
||||
|
||||
DelegateToMixnode {
|
||||
mix_identity: IdentityKey,
|
||||
},
|
||||
|
||||
UndelegateFromMixnode {
|
||||
mix_identity: IdentityKey,
|
||||
},
|
||||
|
||||
RewardMixnode {
|
||||
identity: IdentityKey,
|
||||
// percentage value in range 0-100
|
||||
params: NodeRewardParams,
|
||||
},
|
||||
// RewardNextMixDelegators {
|
||||
// mix_identity: IdentityKey,
|
||||
// // id of the current rewarding interval
|
||||
// interval_id: u32,
|
||||
// },
|
||||
DelegateToMixnodeOnBehalf {
|
||||
mix_identity: IdentityKey,
|
||||
delegate: String,
|
||||
},
|
||||
UndelegateFromMixnodeOnBehalf {
|
||||
mix_identity: IdentityKey,
|
||||
delegate: String,
|
||||
},
|
||||
BondMixnodeOnBehalf {
|
||||
mix_node: MixNode,
|
||||
owner: String,
|
||||
cost_params: MixNodeCostParams,
|
||||
owner_signature: String,
|
||||
owner: String,
|
||||
},
|
||||
UnbondMixnode {},
|
||||
UnbondMixnodeOnBehalf {
|
||||
owner: String,
|
||||
},
|
||||
UpdateMixnodeCostParams {
|
||||
new_costs: MixNodeCostParams,
|
||||
},
|
||||
UpdateMixnodeCostParamsOnBehalf {
|
||||
new_costs: MixNodeCostParams,
|
||||
owner: String,
|
||||
},
|
||||
UpdateMixnodeConfig {
|
||||
new_config: MixNodeConfigUpdate,
|
||||
},
|
||||
UpdateMixnodeConfigOnBehalf {
|
||||
new_config: MixNodeConfigUpdate,
|
||||
owner: String,
|
||||
},
|
||||
|
||||
// gateway-related:
|
||||
BondGateway {
|
||||
gateway: Gateway,
|
||||
owner_signature: String,
|
||||
},
|
||||
BondGatewayOnBehalf {
|
||||
gateway: Gateway,
|
||||
owner: String,
|
||||
owner_signature: String,
|
||||
},
|
||||
UnbondGateway {},
|
||||
UnbondGatewayOnBehalf {
|
||||
owner: String,
|
||||
},
|
||||
WriteRewardedSet {
|
||||
rewarded_set: Vec<IdentityKey>,
|
||||
expected_active_set_size: u32,
|
||||
|
||||
// delegation-related:
|
||||
DelegateToMixnode {
|
||||
mix_id: NodeId,
|
||||
},
|
||||
// AdvanceCurrentInterval {},
|
||||
AdvanceCurrentEpoch {},
|
||||
ClaimOperatorReward {},
|
||||
ClaimOperatorRewardOnBehalf {
|
||||
DelegateToMixnodeOnBehalf {
|
||||
mix_id: NodeId,
|
||||
delegate: String,
|
||||
},
|
||||
UndelegateFromMixnode {
|
||||
mix_id: NodeId,
|
||||
},
|
||||
UndelegateFromMixnodeOnBehalf {
|
||||
mix_id: NodeId,
|
||||
delegate: String,
|
||||
},
|
||||
|
||||
// reward-related
|
||||
RewardMixnode {
|
||||
mix_id: NodeId,
|
||||
performance: Performance,
|
||||
},
|
||||
WithdrawOperatorReward {},
|
||||
WithdrawOperatorRewardOnBehalf {
|
||||
owner: String,
|
||||
},
|
||||
ClaimDelegatorReward {
|
||||
mix_identity: IdentityKey,
|
||||
WithdrawDelegatorReward {
|
||||
mix_id: NodeId,
|
||||
},
|
||||
ClaimDelegatorRewardOnBehalf {
|
||||
mix_identity: IdentityKey,
|
||||
WithdrawDelegatorRewardOnBehalf {
|
||||
mix_id: NodeId,
|
||||
owner: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl ExecuteMsg {
|
||||
pub fn default_memo(&self) -> String {
|
||||
match self {
|
||||
ExecuteMsg::UpdateRewardingValidatorAddress { address } => {
|
||||
format!("updating rewarding validator to {}", address)
|
||||
}
|
||||
ExecuteMsg::UpdateContractStateParams { .. } => {
|
||||
"updating mixnet state parameters".into()
|
||||
}
|
||||
ExecuteMsg::UpdateActiveSetSize {
|
||||
active_set_size,
|
||||
force_immediately,
|
||||
} => format!(
|
||||
"updating active set size to {}. forced: {}",
|
||||
active_set_size, force_immediately
|
||||
),
|
||||
ExecuteMsg::UpdateRewardingParams {
|
||||
force_immediately, ..
|
||||
} => format!(
|
||||
"updating mixnet rewarding parameters. forced: {}",
|
||||
force_immediately
|
||||
),
|
||||
ExecuteMsg::UpdateIntervalConfig {
|
||||
force_immediately, ..
|
||||
} => format!(
|
||||
"updating mixnet interval configuration. forced: {}",
|
||||
force_immediately
|
||||
),
|
||||
ExecuteMsg::AdvanceCurrentEpoch { .. } => "advancing current epoch".into(),
|
||||
ExecuteMsg::ReconcileEpochEvents { .. } => "reconciling epoch events".into(),
|
||||
ExecuteMsg::BondMixnode { mix_node, .. } => {
|
||||
format!("bonding mixnode {}", mix_node.identity_key)
|
||||
}
|
||||
ExecuteMsg::BondMixnodeOnBehalf { mix_node, .. } => {
|
||||
format!("bonding mixnode {} on behalf", mix_node.identity_key)
|
||||
}
|
||||
ExecuteMsg::UnbondMixnode { .. } => "unbonding mixnode".into(),
|
||||
ExecuteMsg::UnbondMixnodeOnBehalf { .. } => "unbonding mixnode on behalf".into(),
|
||||
ExecuteMsg::UpdateMixnodeCostParams { .. } => "updating mixnode cost parameters".into(),
|
||||
ExecuteMsg::UpdateMixnodeCostParamsOnBehalf { .. } => {
|
||||
"updating mixnode cost parameters on behalf".into()
|
||||
}
|
||||
ExecuteMsg::UpdateMixnodeConfig { .. } => "updating mixnode configuration".into(),
|
||||
ExecuteMsg::UpdateMixnodeConfigOnBehalf { .. } => {
|
||||
"updating mixnode configuration on behalf".into()
|
||||
}
|
||||
ExecuteMsg::BondGateway { gateway, .. } => {
|
||||
format!("bonding gateway {}", gateway.identity_key)
|
||||
}
|
||||
ExecuteMsg::BondGatewayOnBehalf { gateway, .. } => {
|
||||
format!("bonding gateway {} on behalf", gateway.identity_key)
|
||||
}
|
||||
ExecuteMsg::UnbondGateway { .. } => "unbonding gateway".into(),
|
||||
ExecuteMsg::UnbondGatewayOnBehalf { .. } => "unbonding gateway on behalf".into(),
|
||||
ExecuteMsg::DelegateToMixnode { mix_id } => format!("delegating to mixnode {}", mix_id),
|
||||
ExecuteMsg::DelegateToMixnodeOnBehalf { mix_id, .. } => {
|
||||
format!("delegating to mixnode {} on behalf", mix_id)
|
||||
}
|
||||
ExecuteMsg::UndelegateFromMixnode { mix_id } => {
|
||||
format!("removing delegation from mixnode {}", mix_id)
|
||||
}
|
||||
ExecuteMsg::UndelegateFromMixnodeOnBehalf { mix_id, .. } => {
|
||||
format!("removing delegation from mixnode {} on behalf", mix_id)
|
||||
}
|
||||
ExecuteMsg::RewardMixnode {
|
||||
mix_id,
|
||||
performance,
|
||||
} => format!(
|
||||
"rewarding mixnode {} for performance {}",
|
||||
mix_id, performance
|
||||
),
|
||||
ExecuteMsg::WithdrawOperatorReward { .. } => "withdrawing operator reward".into(),
|
||||
ExecuteMsg::WithdrawOperatorRewardOnBehalf { .. } => {
|
||||
"withdrawing operator reward on behalf".into()
|
||||
}
|
||||
ExecuteMsg::WithdrawDelegatorReward { mix_id } => {
|
||||
format!("withdrawing delegator reward from mixnode {}", mix_id)
|
||||
}
|
||||
ExecuteMsg::WithdrawDelegatorRewardOnBehalf { mix_id, .. } => format!(
|
||||
"withdrawing delegator reward from mixnode {} on behalf",
|
||||
mix_id
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum QueryMsg {
|
||||
GetBlacklistedNodes {},
|
||||
GetCurrentOperatorCost {},
|
||||
GetRewardingValidatorAddress {},
|
||||
GetAllDelegationKeys {},
|
||||
DebugGetAllDelegationValues {},
|
||||
// state/sys-params-related
|
||||
GetContractVersion {},
|
||||
GetMixNodes {
|
||||
GetRewardingValidatorAddress {},
|
||||
GetStateParams {},
|
||||
GetState {},
|
||||
GetRewardingParams {},
|
||||
GetCurrentIntervalDetails {},
|
||||
GetRewardedSet {
|
||||
limit: Option<u32>,
|
||||
start_after: Option<IdentityKey>,
|
||||
start_after: Option<NodeId>,
|
||||
},
|
||||
|
||||
// mixnode-related:
|
||||
GetMixNodeBonds {
|
||||
limit: Option<u32>,
|
||||
start_after: Option<NodeId>,
|
||||
},
|
||||
GetMixNodesDetailed {
|
||||
limit: Option<u32>,
|
||||
start_after: Option<NodeId>,
|
||||
},
|
||||
GetUnbondedMixNodes {
|
||||
limit: Option<u32>,
|
||||
start_after: Option<NodeId>,
|
||||
},
|
||||
GetUnbondedMixNodesByOwner {
|
||||
owner: String,
|
||||
limit: Option<u32>,
|
||||
start_after: Option<NodeId>,
|
||||
},
|
||||
GetUnbondedMixNodesByIdentityKey {
|
||||
identity_key: String,
|
||||
limit: Option<u32>,
|
||||
start_after: Option<NodeId>,
|
||||
},
|
||||
GetOwnedMixnode {
|
||||
address: String,
|
||||
},
|
||||
GetMixnodeDetails {
|
||||
mix_id: NodeId,
|
||||
},
|
||||
GetMixnodeRewardingDetails {
|
||||
mix_id: NodeId,
|
||||
},
|
||||
GetStakeSaturation {
|
||||
mix_id: NodeId,
|
||||
},
|
||||
GetUnbondedMixNodeInformation {
|
||||
mix_id: NodeId,
|
||||
},
|
||||
GetBondedMixnodeDetailsByIdentity {
|
||||
mix_identity: IdentityKey,
|
||||
},
|
||||
GetLayerDistribution {},
|
||||
|
||||
// gateway-related:
|
||||
GetGateways {
|
||||
start_after: Option<IdentityKey>,
|
||||
limit: Option<u32>,
|
||||
},
|
||||
OwnsMixnode {
|
||||
address: String,
|
||||
},
|
||||
OwnsGateway {
|
||||
address: String,
|
||||
},
|
||||
GetMixnodeBond {
|
||||
identity: IdentityKey,
|
||||
},
|
||||
GetGatewayBond {
|
||||
identity: IdentityKey,
|
||||
},
|
||||
StateParams {},
|
||||
GetOwnedGateway {
|
||||
address: String,
|
||||
},
|
||||
|
||||
// delegation-related:
|
||||
// gets all [paged] delegations associated with particular mixnode
|
||||
GetMixnodeDelegations {
|
||||
mix_identity: IdentityKey,
|
||||
mix_id: NodeId,
|
||||
// since `start_after` is user-provided input, we can't use `Addr` as we
|
||||
// can't guarantee it's validated.
|
||||
start_after: Option<(String, u64)>,
|
||||
start_after: Option<String>,
|
||||
limit: Option<u32>,
|
||||
},
|
||||
// gets all [paged] delegations associated with particular delegator
|
||||
@@ -162,81 +344,56 @@ pub enum QueryMsg {
|
||||
// since `delegator` is user-provided input, we can't use `Addr` as we
|
||||
// can't guarantee it's validated.
|
||||
delegator: String,
|
||||
start_after: Option<IdentityKey>,
|
||||
start_after: Option<(NodeId, OwnerProxySubKey)>,
|
||||
limit: Option<u32>,
|
||||
},
|
||||
// gets delegation associated with particular mixnode, delegator pair
|
||||
GetDelegationDetails {
|
||||
mix_identity: IdentityKey,
|
||||
mix_id: NodeId,
|
||||
delegator: String,
|
||||
proxy: Option<String>,
|
||||
},
|
||||
LayerDistribution {},
|
||||
GetRewardPool {},
|
||||
GetCirculatingSupply {},
|
||||
GetStakingSupply {},
|
||||
GetIntervalRewardPercent {},
|
||||
GetSybilResistancePercent {},
|
||||
GetActiveSetWorkFactor {},
|
||||
GetRewardingStatus {
|
||||
mix_identity: IdentityKey,
|
||||
interval_id: u32,
|
||||
},
|
||||
GetRewardedSet {
|
||||
height: Option<u64>,
|
||||
start_after: Option<IdentityKey>,
|
||||
// gets all delegations in the system
|
||||
GetAllDelegations {
|
||||
start_after: Option<delegation::StorageKey>,
|
||||
limit: Option<u32>,
|
||||
},
|
||||
GetRewardedSetUpdateDetails {},
|
||||
GetCurrentRewardedSetHeight {},
|
||||
GetRewardedSetRefreshBlocks {},
|
||||
GetCurrentEpoch {},
|
||||
GetEpochsInInterval {},
|
||||
QueryOperatorReward {
|
||||
|
||||
// rewards related
|
||||
GetPendingOperatorReward {
|
||||
address: String,
|
||||
},
|
||||
QueryDelegatorReward {
|
||||
GetPendingMixNodeOperatorReward {
|
||||
mix_id: NodeId,
|
||||
},
|
||||
GetPendingDelegatorReward {
|
||||
address: String,
|
||||
mix_identity: IdentityKey,
|
||||
mix_id: NodeId,
|
||||
proxy: Option<String>,
|
||||
},
|
||||
GetPendingDelegationEvents {
|
||||
owner_address: String,
|
||||
proxy_address: Option<String>,
|
||||
// given the provided performance, estimate the reward at the end of the current epoch
|
||||
GetEstimatedCurrentEpochOperatorReward {
|
||||
mix_id: NodeId,
|
||||
estimated_performance: Performance,
|
||||
},
|
||||
GetCheckpointsForMixnode {
|
||||
mix_identity: IdentityKey,
|
||||
GetEstimatedCurrentEpochDelegatorReward {
|
||||
address: String,
|
||||
mix_id: NodeId,
|
||||
proxy: Option<String>,
|
||||
estimated_performance: Performance,
|
||||
},
|
||||
GetMixnodeAtHeight {
|
||||
mix_identity: IdentityKey,
|
||||
height: u64,
|
||||
|
||||
// interval-related
|
||||
GetPendingEpochEvents {
|
||||
limit: Option<u32>,
|
||||
start_after: Option<u32>,
|
||||
},
|
||||
GetPendingIntervalEvents {
|
||||
limit: Option<u32>,
|
||||
start_after: Option<u32>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct MigrateMsg {
|
||||
pub nodes_to_remove: Option<Vec<NodeToRemove>>,
|
||||
}
|
||||
|
||||
impl MigrateMsg {
|
||||
pub fn nodes_to_remove(&self) -> Vec<NodeToRemove> {
|
||||
self.nodes_to_remove.clone().unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
pub struct NodeToRemove {
|
||||
owner: String,
|
||||
proxy: Option<String>,
|
||||
}
|
||||
|
||||
impl NodeToRemove {
|
||||
pub fn owner(&self) -> &str {
|
||||
&self.owner
|
||||
}
|
||||
|
||||
pub fn proxy(&self) -> Option<&String> {
|
||||
self.proxy.as_ref()
|
||||
}
|
||||
}
|
||||
pub struct MigrateMsg {}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::mixnode::MixNodeCostParams;
|
||||
use crate::reward_params::IntervalRewardingParamsUpdate;
|
||||
use crate::{EpochEventId, IntervalEventId, NodeId};
|
||||
use cosmwasm_std::{Addr, Coin};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct PendingEpochEvent {
|
||||
pub id: EpochEventId,
|
||||
pub event: PendingEpochEventData,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub enum PendingEpochEventData {
|
||||
// can't just pass the `Delegation` struct here as it's impossible to determine
|
||||
// `cumulative_reward_ratio` ahead of time
|
||||
Delegate {
|
||||
owner: Addr,
|
||||
mix_id: NodeId,
|
||||
amount: Coin,
|
||||
proxy: Option<Addr>,
|
||||
},
|
||||
Undelegate {
|
||||
owner: Addr,
|
||||
mix_id: NodeId,
|
||||
proxy: Option<Addr>,
|
||||
},
|
||||
UnbondMixnode {
|
||||
mix_id: NodeId,
|
||||
},
|
||||
UpdateActiveSetSize {
|
||||
new_size: u32,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<(EpochEventId, PendingEpochEventData)> for PendingEpochEvent {
|
||||
fn from(data: (EpochEventId, PendingEpochEventData)) -> Self {
|
||||
PendingEpochEvent {
|
||||
id: data.0,
|
||||
event: data.1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct PendingIntervalEvent {
|
||||
pub id: EpochEventId,
|
||||
pub event: PendingIntervalEventData,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub enum PendingIntervalEventData {
|
||||
ChangeMixCostParams {
|
||||
mix_id: NodeId,
|
||||
new_costs: MixNodeCostParams,
|
||||
},
|
||||
|
||||
UpdateRewardingParams {
|
||||
update: IntervalRewardingParamsUpdate,
|
||||
},
|
||||
UpdateIntervalConfig {
|
||||
epochs_in_interval: u32,
|
||||
epoch_duration_secs: u64,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<(IntervalEventId, PendingIntervalEventData)> for PendingIntervalEvent {
|
||||
fn from(data: (IntervalEventId, PendingIntervalEventData)) -> Self {
|
||||
PendingIntervalEvent {
|
||||
id: data.0,
|
||||
event: data.1,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,288 +1,264 @@
|
||||
use crate::{error::MixnetContractError, mixnode::StoredNodeRewardResult, ONE, U128};
|
||||
use az::CheckedCast;
|
||||
use cosmwasm_std::Uint128;
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{error::MixnetContractError, Percent};
|
||||
use cosmwasm_std::Decimal;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, JsonSchema, PartialEq, Eq, Serialize, Deserialize, Copy)]
|
||||
pub struct NodeEpochRewards {
|
||||
params: NodeRewardParams,
|
||||
result: StoredNodeRewardResult,
|
||||
epoch_id: u32,
|
||||
pub type Performance = Percent;
|
||||
|
||||
/// Parameters required by the mix-mining reward distribution that do not change during an interval.
|
||||
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
|
||||
#[cfg_attr(
|
||||
feature = "generate-ts",
|
||||
ts(export_to = "ts-packages/types/src/types/rust/IntervalRewardParams.ts")
|
||||
)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, PartialOrd, Serialize, JsonSchema)]
|
||||
pub struct IntervalRewardParams {
|
||||
/// Current value of the rewarding pool.
|
||||
/// It is expected to be constant throughout the interval.
|
||||
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
|
||||
pub reward_pool: Decimal,
|
||||
|
||||
/// Current value of the staking supply.
|
||||
/// It is expected to be constant throughout the interval.
|
||||
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
|
||||
pub staking_supply: Decimal,
|
||||
|
||||
// computed values
|
||||
/// Current value of the computed reward budget per epoch, per node.
|
||||
/// It is expected to be constant throughout the interval.
|
||||
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
|
||||
pub epoch_reward_budget: Decimal,
|
||||
|
||||
/// Current value of the stake saturation point.
|
||||
/// It is expected to be constant throughout the interval.
|
||||
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
|
||||
pub stake_saturation_point: Decimal,
|
||||
|
||||
// constants(-ish)
|
||||
// default: 30%
|
||||
/// Current value of the sybil resistance percent (`alpha`).
|
||||
/// It is not really expected to be changing very often.
|
||||
/// As a matter of fact, unless there's a very specific reason, it should remain constant.
|
||||
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
|
||||
pub sybil_resistance: Percent,
|
||||
|
||||
// default: 10
|
||||
/// Current active set work factor.
|
||||
/// It is not really expected to be changing very often.
|
||||
/// As a matter of fact, unless there's a very specific reason, it should remain constant.
|
||||
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
|
||||
pub active_set_work_factor: Decimal,
|
||||
|
||||
// default: 2%
|
||||
/// Current maximum interval pool emission.
|
||||
/// Assuming all nodes in the rewarded set are fully saturated and have 100% performance,
|
||||
/// this % of the reward pool would get distributed in rewards to all operators and its delegators.
|
||||
/// It is not really expected to be changing very often.
|
||||
/// As a matter of fact, unless there's a very specific reason, it should remain constant.
|
||||
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
|
||||
pub interval_pool_emission: Percent,
|
||||
}
|
||||
|
||||
impl NodeEpochRewards {
|
||||
pub fn new(params: NodeRewardParams, result: StoredNodeRewardResult, epoch_id: u32) -> Self {
|
||||
Self {
|
||||
params,
|
||||
result,
|
||||
epoch_id,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn epoch_id(&self) -> u32 {
|
||||
self.epoch_id
|
||||
}
|
||||
|
||||
pub fn sigma(&self) -> U128 {
|
||||
self.result.sigma()
|
||||
}
|
||||
|
||||
pub fn lambda(&self) -> U128 {
|
||||
self.result.lambda()
|
||||
}
|
||||
|
||||
pub fn params(&self) -> NodeRewardParams {
|
||||
self.params
|
||||
}
|
||||
|
||||
pub fn reward(&self) -> Uint128 {
|
||||
self.result.reward()
|
||||
}
|
||||
|
||||
pub fn operator_cost(&self, base_operator_cost: u64) -> U128 {
|
||||
self.params.operator_cost(base_operator_cost)
|
||||
}
|
||||
|
||||
pub fn node_profit(&self, base_operator_cost: u64) -> U128 {
|
||||
let reward = U128::from_num(self.reward().u128());
|
||||
// if operating cost is higher then the reward node profit is 0
|
||||
reward.saturating_sub(self.operator_cost(base_operator_cost))
|
||||
}
|
||||
|
||||
pub fn operator_reward(
|
||||
&self,
|
||||
profit_margin: U128,
|
||||
base_operator_cost: u64,
|
||||
) -> Result<Uint128, MixnetContractError> {
|
||||
let reward = self.node_profit(base_operator_cost);
|
||||
let operator_base_reward = reward.min(self.operator_cost(base_operator_cost));
|
||||
let div_by_zero_check = if let Some(value) = self.lambda().checked_div(self.sigma()) {
|
||||
value
|
||||
} else {
|
||||
return Err(MixnetContractError::DivisionByZero);
|
||||
};
|
||||
let operator_reward = (profit_margin + (ONE - profit_margin) * div_by_zero_check) * reward;
|
||||
|
||||
let reward = (operator_reward + operator_base_reward).max(U128::from_num(0u128));
|
||||
|
||||
if let Some(int_reward) = reward.checked_cast() {
|
||||
Ok(Uint128::new(int_reward))
|
||||
} else {
|
||||
Err(MixnetContractError::CastError)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn delegation_reward(
|
||||
&self,
|
||||
delegation_amount: Uint128,
|
||||
profit_margin: U128,
|
||||
base_operator_cost: u64,
|
||||
epoch_reward_params: EpochRewardParams,
|
||||
) -> Result<Uint128, MixnetContractError> {
|
||||
// change all values into their fixed representations
|
||||
let delegation_amount = U128::from_num(delegation_amount.u128());
|
||||
let staking_supply = U128::from_num(epoch_reward_params.staking_supply());
|
||||
|
||||
let scaled_delegation_amount = delegation_amount / staking_supply;
|
||||
|
||||
let check_div_by_zero =
|
||||
if let Some(value) = scaled_delegation_amount.checked_div(self.sigma()) {
|
||||
value
|
||||
} else {
|
||||
return Err(MixnetContractError::DivisionByZero);
|
||||
};
|
||||
|
||||
let delegator_reward =
|
||||
(ONE - profit_margin) * check_div_by_zero * self.node_profit(base_operator_cost);
|
||||
|
||||
let reward = delegator_reward.max(U128::ZERO);
|
||||
if let Some(int_reward) = reward.checked_cast() {
|
||||
Ok(Uint128::new(int_reward))
|
||||
} else {
|
||||
Err(MixnetContractError::CastError)
|
||||
}
|
||||
impl IntervalRewardParams {
|
||||
pub fn to_inline_json(&self) -> String {
|
||||
serde_json::to_string(self).unwrap_or_else(|_| "serialisation failure".into())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, JsonSchema, PartialEq, Eq, Serialize, Deserialize, Copy)]
|
||||
pub struct EpochRewardParams {
|
||||
epoch_reward_pool: Uint128,
|
||||
rewarded_set_size: Uint128,
|
||||
active_set_size: Uint128,
|
||||
#[serde(alias = "circulating_supply")]
|
||||
staking_supply: Uint128,
|
||||
sybil_resistance_percent: u8,
|
||||
active_set_work_factor: u8,
|
||||
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
|
||||
#[cfg_attr(
|
||||
feature = "generate-ts",
|
||||
ts(export_to = "ts-packages/types/src/types/rust/RewardingParams.ts")
|
||||
)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, PartialOrd, Serialize, JsonSchema)]
|
||||
pub struct RewardingParams {
|
||||
/// Parameters that should remain unchanged throughout an interval.
|
||||
pub interval: IntervalRewardParams,
|
||||
|
||||
// while the active set size can change between epochs to accommodate for bandwidth demands,
|
||||
// the active set size should be unchanged between epochs and should only be adjusted between
|
||||
// intervals. However, it makes more sense to keep both of those values together as they're
|
||||
// very strongly related to each other.
|
||||
pub rewarded_set_size: u32,
|
||||
pub active_set_size: u32,
|
||||
}
|
||||
|
||||
impl EpochRewardParams {
|
||||
pub fn new(
|
||||
epoch_reward_pool: u128,
|
||||
rewarded_set_size: u128,
|
||||
active_set_size: u128,
|
||||
staking_supply: u128,
|
||||
sybil_resistance_percent: u8,
|
||||
active_set_work_factor: u8,
|
||||
) -> EpochRewardParams {
|
||||
EpochRewardParams {
|
||||
epoch_reward_pool: Uint128::new(epoch_reward_pool),
|
||||
rewarded_set_size: Uint128::new(rewarded_set_size),
|
||||
active_set_size: Uint128::new(active_set_size),
|
||||
staking_supply: Uint128::new(staking_supply),
|
||||
sybil_resistance_percent,
|
||||
active_set_work_factor,
|
||||
impl RewardingParams {
|
||||
pub fn active_node_work(&self) -> Decimal {
|
||||
self.interval.active_set_work_factor * self.standby_node_work()
|
||||
}
|
||||
|
||||
pub fn standby_node_work(&self) -> Decimal {
|
||||
let f = self.interval.active_set_work_factor;
|
||||
let k = self.dec_rewarded_set_size();
|
||||
let one = Decimal::one();
|
||||
|
||||
// nodes in reserve
|
||||
let k_r = self.dec_standby_set_size();
|
||||
|
||||
one / (f * k - (f - one) * k_r)
|
||||
}
|
||||
|
||||
pub fn dec_rewarded_set_size(&self) -> Decimal {
|
||||
// the unwrap here is fine as we're guaranteed an `u32` is going to fit in a Decimal
|
||||
// with 0 decimal places
|
||||
Decimal::from_atomics(self.rewarded_set_size, 0).unwrap()
|
||||
}
|
||||
|
||||
pub fn dec_active_set_size(&self) -> Decimal {
|
||||
// the unwrap here is fine as we're guaranteed an `u32` is going to fit in a Decimal
|
||||
// with 0 decimal places
|
||||
Decimal::from_atomics(self.active_set_size, 0).unwrap()
|
||||
}
|
||||
|
||||
fn dec_standby_set_size(&self) -> Decimal {
|
||||
// the unwrap here is fine as we're guaranteed an `u32` is going to fit in a Decimal
|
||||
// with 0 decimal places
|
||||
Decimal::from_atomics(self.rewarded_set_size - self.active_set_size, 0).unwrap()
|
||||
}
|
||||
|
||||
pub fn apply_epochs_in_interval_change(&mut self, new_epochs_in_interval: u32) {
|
||||
self.interval.epoch_reward_budget = self.interval.reward_pool
|
||||
/ Decimal::from_atomics(new_epochs_in_interval, 0).unwrap()
|
||||
* self.interval.interval_pool_emission;
|
||||
}
|
||||
|
||||
pub fn try_change_active_set_size(
|
||||
&mut self,
|
||||
new_active_set_size: u32,
|
||||
) -> Result<(), MixnetContractError> {
|
||||
if new_active_set_size == 0 {
|
||||
return Err(MixnetContractError::ZeroActiveSet);
|
||||
}
|
||||
}
|
||||
|
||||
// technically it's identical to what would have been derived with a Default implementation,
|
||||
// however, I prefer to be explicit about it, as a `Default::default` value makes no sense
|
||||
// apart from the `ValidatorCacheInner` context, where this value is not going to be touched anyway
|
||||
// (it's guarded behind an `initialised` flag)
|
||||
pub fn new_empty() -> Self {
|
||||
EpochRewardParams {
|
||||
epoch_reward_pool: Uint128::new(0),
|
||||
staking_supply: Uint128::new(0),
|
||||
sybil_resistance_percent: 0,
|
||||
rewarded_set_size: Uint128::new(0),
|
||||
active_set_size: Uint128::new(0),
|
||||
active_set_work_factor: 0,
|
||||
if new_active_set_size > self.rewarded_set_size {
|
||||
return Err(MixnetContractError::InvalidActiveSetSize);
|
||||
}
|
||||
|
||||
self.active_set_size = new_active_set_size;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn rewarded_set_size(&self) -> u128 {
|
||||
self.rewarded_set_size.u128()
|
||||
}
|
||||
pub fn try_apply_updates(
|
||||
&mut self,
|
||||
updates: IntervalRewardingParamsUpdate,
|
||||
epochs_in_interval: u32,
|
||||
) -> Result<(), MixnetContractError> {
|
||||
if !updates.contains_updates() {
|
||||
return Err(MixnetContractError::EmptyParamsChangeMsg);
|
||||
}
|
||||
|
||||
pub fn active_set_size(&self) -> u128 {
|
||||
self.active_set_size.u128()
|
||||
}
|
||||
let mut recompute_epoch_budget = false;
|
||||
let mut recompute_saturation_point = false;
|
||||
|
||||
pub fn staking_supply(&self) -> u128 {
|
||||
self.staking_supply.u128()
|
||||
}
|
||||
if let Some(reward_pool) = updates.reward_pool {
|
||||
recompute_epoch_budget = true;
|
||||
self.interval.reward_pool = reward_pool;
|
||||
}
|
||||
|
||||
pub fn epoch_reward_pool(&self) -> u128 {
|
||||
self.epoch_reward_pool.u128()
|
||||
}
|
||||
if let Some(staking_supply) = updates.staking_supply {
|
||||
recompute_saturation_point = true;
|
||||
self.interval.staking_supply = staking_supply;
|
||||
}
|
||||
|
||||
pub fn sybil_resistance_percent(&self) -> u8 {
|
||||
self.sybil_resistance_percent
|
||||
}
|
||||
if let Some(sybil_resistance_percent) = updates.sybil_resistance_percent {
|
||||
self.interval.sybil_resistance = sybil_resistance_percent;
|
||||
}
|
||||
|
||||
pub fn active_set_work_factor(&self) -> u8 {
|
||||
self.active_set_work_factor
|
||||
if let Some(active_set_work_factor) = updates.active_set_work_factor {
|
||||
self.interval.active_set_work_factor = active_set_work_factor;
|
||||
}
|
||||
|
||||
if let Some(interval_pool_emission) = updates.interval_pool_emission {
|
||||
recompute_epoch_budget = true;
|
||||
self.interval.interval_pool_emission = interval_pool_emission;
|
||||
}
|
||||
|
||||
if let Some(rewarded_set_size) = updates.rewarded_set_size {
|
||||
if rewarded_set_size == 0 {
|
||||
return Err(MixnetContractError::ZeroRewardedSet);
|
||||
}
|
||||
if rewarded_set_size < self.active_set_size {
|
||||
return Err(MixnetContractError::InvalidRewardedSetSize);
|
||||
}
|
||||
|
||||
recompute_saturation_point = true;
|
||||
self.rewarded_set_size = rewarded_set_size;
|
||||
}
|
||||
|
||||
if recompute_epoch_budget {
|
||||
self.interval.epoch_reward_budget = self.interval.reward_pool
|
||||
/ Decimal::from_atomics(epochs_in_interval, 0).unwrap()
|
||||
* self.interval.interval_pool_emission;
|
||||
}
|
||||
|
||||
if recompute_saturation_point {
|
||||
self.interval.stake_saturation_point = self.interval.staking_supply
|
||||
/ Decimal::from_atomics(self.rewarded_set_size, 0).unwrap();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, JsonSchema, PartialEq, Eq, Serialize, Deserialize, Copy)]
|
||||
// TODO: possibly refactor this
|
||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, PartialOrd, Serialize, JsonSchema)]
|
||||
pub struct NodeRewardParams {
|
||||
reward_blockstamp: u64,
|
||||
uptime: Uint128,
|
||||
in_active_set: bool,
|
||||
pub performance: Percent,
|
||||
pub in_active_set: bool,
|
||||
}
|
||||
|
||||
impl NodeRewardParams {
|
||||
pub fn new(reward_blockstamp: u64, uptime: u128, in_active_set: bool) -> NodeRewardParams {
|
||||
pub fn new(performance: Percent, in_active_set: bool) -> Self {
|
||||
NodeRewardParams {
|
||||
reward_blockstamp,
|
||||
uptime: Uint128::new(uptime),
|
||||
performance,
|
||||
in_active_set,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn operator_cost(&self, base_operator_cost: u64) -> U128 {
|
||||
self.performance() * U128::from_num(base_operator_cost)
|
||||
}
|
||||
|
||||
pub fn uptime(&self) -> Uint128 {
|
||||
self.uptime
|
||||
}
|
||||
|
||||
pub fn performance(&self) -> U128 {
|
||||
U128::from_num(self.uptime.u128()) / U128::from_num(100)
|
||||
}
|
||||
|
||||
pub fn set_reward_blockstamp(&mut self, blockstamp: u64) {
|
||||
self.reward_blockstamp = blockstamp;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, JsonSchema, PartialEq, Eq, Serialize, Deserialize, Copy)]
|
||||
pub struct RewardParams {
|
||||
pub epoch: EpochRewardParams,
|
||||
pub node: NodeRewardParams,
|
||||
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
|
||||
#[cfg_attr(
|
||||
feature = "generate-ts",
|
||||
ts(export_to = "ts-packages/types/src/types/rust/IntervalRewardingParamsUpdate.ts")
|
||||
)]
|
||||
#[derive(
|
||||
Clone, Copy, Debug, Default, Deserialize, PartialEq, Eq, PartialOrd, Serialize, JsonSchema,
|
||||
)]
|
||||
pub struct IntervalRewardingParamsUpdate {
|
||||
#[cfg_attr(feature = "generate-ts", ts(type = "string | null"))]
|
||||
pub reward_pool: Option<Decimal>,
|
||||
|
||||
#[cfg_attr(feature = "generate-ts", ts(type = "string | null"))]
|
||||
pub staking_supply: Option<Decimal>,
|
||||
|
||||
#[cfg_attr(feature = "generate-ts", ts(type = "string | null"))]
|
||||
pub sybil_resistance_percent: Option<Percent>,
|
||||
|
||||
#[cfg_attr(feature = "generate-ts", ts(type = "string | null"))]
|
||||
pub active_set_work_factor: Option<Decimal>,
|
||||
|
||||
#[cfg_attr(feature = "generate-ts", ts(type = "string | null"))]
|
||||
pub interval_pool_emission: Option<Percent>,
|
||||
|
||||
pub rewarded_set_size: Option<u32>,
|
||||
}
|
||||
|
||||
impl RewardParams {
|
||||
pub fn new(epoch: EpochRewardParams, node: NodeRewardParams) -> RewardParams {
|
||||
RewardParams { epoch, node }
|
||||
impl IntervalRewardingParamsUpdate {
|
||||
pub fn contains_updates(&self) -> bool {
|
||||
// essentially at least a single field has to be a `Some`
|
||||
self.reward_pool.is_some()
|
||||
|| self.staking_supply.is_some()
|
||||
|| self.sybil_resistance_percent.is_some()
|
||||
|| self.active_set_work_factor.is_some()
|
||||
|| self.interval_pool_emission.is_some()
|
||||
|| self.rewarded_set_size.is_some()
|
||||
}
|
||||
|
||||
pub fn omega(&self) -> U128 {
|
||||
// As per keybase://chat/nymtech#tokeneconomics/1179
|
||||
let denom = self.active_set_work_factor() * U128::from_num(self.rewarded_set_size())
|
||||
- (self.active_set_work_factor() - ONE) * U128::from_num(self.idle_nodes().u128());
|
||||
|
||||
if denom == 0 {
|
||||
return U128::ZERO;
|
||||
}
|
||||
|
||||
// Div by zero checked above
|
||||
if self.in_active_set() {
|
||||
// work_active = factor / (factor * self.network.k[month] - (factor - 1) * idle_nodes)
|
||||
self.active_set_work_factor() / denom * self.rewarded_set_size()
|
||||
} else {
|
||||
// work_idle = 1 / (factor * self.network.k[month] - (factor - 1) * idle_nodes)
|
||||
ONE / denom * self.rewarded_set_size()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn idle_nodes(&self) -> Uint128 {
|
||||
self.epoch.rewarded_set_size - self.epoch.active_set_size
|
||||
}
|
||||
|
||||
pub fn active_set_work_factor(&self) -> U128 {
|
||||
U128::from_num(self.epoch.active_set_work_factor)
|
||||
}
|
||||
|
||||
pub fn in_active_set(&self) -> bool {
|
||||
self.node.in_active_set
|
||||
}
|
||||
|
||||
pub fn performance(&self) -> U128 {
|
||||
self.node.performance()
|
||||
}
|
||||
|
||||
pub fn set_reward_blockstamp(&mut self, blockstamp: u64) {
|
||||
self.node.reward_blockstamp = blockstamp;
|
||||
}
|
||||
|
||||
pub fn epoch_reward_pool(&self) -> u128 {
|
||||
self.epoch.epoch_reward_pool.u128()
|
||||
}
|
||||
|
||||
pub fn rewarded_set_size(&self) -> u128 {
|
||||
self.epoch.rewarded_set_size.u128()
|
||||
}
|
||||
|
||||
pub fn staking_supply(&self) -> u128 {
|
||||
self.epoch.staking_supply.u128()
|
||||
}
|
||||
|
||||
pub fn reward_blockstamp(&self) -> u64 {
|
||||
self.node.reward_blockstamp
|
||||
}
|
||||
|
||||
pub fn uptime(&self) -> Uint128 {
|
||||
self.node.uptime()
|
||||
}
|
||||
|
||||
pub fn one_over_k(&self) -> U128 {
|
||||
ONE / U128::from_num(self.epoch.rewarded_set_size.u128())
|
||||
}
|
||||
|
||||
pub fn alpha(&self) -> U128 {
|
||||
U128::from_num(self.epoch.sybil_resistance_percent) / U128::from_num(100)
|
||||
pub fn to_inline_json(&self) -> String {
|
||||
serde_json::to_string(self).unwrap_or_else(|_| "serialisation failure".into())
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user