Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| dd83631be1 | |||
| 405ca63c7b | |||
| 7f17522666 | |||
| ba158f822d | |||
| 0f147750ae | |||
| 60d17389d0 | |||
| 94d70e47fc |
@@ -16,7 +16,7 @@ jobs:
|
|||||||
RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
|
RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
|
||||||
steps:
|
steps:
|
||||||
- name: Install Dependencies (Linux)
|
- name: Install Dependencies (Linux)
|
||||||
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools
|
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
|
||||||
|
|
||||||
- name: Check out repository code
|
- name: Check out repository code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ jobs:
|
|||||||
continue-on-error: ${{ matrix.rust == 'nightly' || matrix.rust == 'beta' || matrix.rust == 'stable' }}
|
continue-on-error: ${{ matrix.rust == 'nightly' || matrix.rust == 'beta' || matrix.rust == 'stable' }}
|
||||||
steps:
|
steps:
|
||||||
- name: Install Dependencies (Linux)
|
- 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 libudev-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 squashfs-tools
|
||||||
if: matrix.os == 'ubuntu-latest'
|
if: matrix.os == 'ubuntu-latest'
|
||||||
|
|
||||||
- name: Check out repository code
|
- name: Check out repository code
|
||||||
|
|||||||
@@ -2,12 +2,6 @@ name: Publish Nym binaries
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
|
||||||
add_tokio_unstable:
|
|
||||||
description: 'True to add RUSTFLAGS="--cfg tokio_unstable"'
|
|
||||||
required: true
|
|
||||||
default: false
|
|
||||||
type: boolean
|
|
||||||
release:
|
release:
|
||||||
types: [created]
|
types: [created]
|
||||||
|
|
||||||
@@ -31,11 +25,6 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
core.setFailed('Release tag did not start with nym-binaries-...')
|
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
|
- name: Install Rust stable
|
||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1
|
||||||
|
|||||||
@@ -9,11 +9,6 @@ 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])
|
- 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])
|
- 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))
|
- 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
|
### Changed
|
||||||
|
|
||||||
@@ -26,8 +21,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
|||||||
[#1577]: https://github.com/nymtech/nym/pull/1577
|
[#1577]: https://github.com/nymtech/nym/pull/1577
|
||||||
[#1585]: https://github.com/nymtech/nym/pull/1585
|
[#1585]: https://github.com/nymtech/nym/pull/1585
|
||||||
[#1591]: https://github.com/nymtech/nym/pull/1591
|
[#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)
|
## [nym-binaries-1.0.2](https://github.com/nymtech/nym/tree/nym-binaries-1.0.2)
|
||||||
|
|||||||
Generated
+696
-627
File diff suppressed because it is too large
Load Diff
@@ -63,7 +63,6 @@ members = [
|
|||||||
"common/topology",
|
"common/topology",
|
||||||
"common/types",
|
"common/types",
|
||||||
"common/wasm-utils",
|
"common/wasm-utils",
|
||||||
"common/completions",
|
|
||||||
"explorer-api",
|
"explorer-api",
|
||||||
"gateway",
|
"gateway",
|
||||||
"gateway/gateway-requests",
|
"gateway/gateway-requests",
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ impl LoopCoverTrafficStream<OsRng> {
|
|||||||
let cover_message = generate_loop_cover_packet(
|
let cover_message = generate_loop_cover_packet(
|
||||||
&mut self.rng,
|
&mut self.rng,
|
||||||
topology_ref,
|
topology_ref,
|
||||||
&self.ack_key,
|
&*self.ack_key,
|
||||||
&self.our_full_destination,
|
&self.our_full_destination,
|
||||||
self.average_ack_delay,
|
self.average_ack_delay,
|
||||||
self.average_packet_delay,
|
self.average_packet_delay,
|
||||||
|
|||||||
@@ -228,7 +228,7 @@ where
|
|||||||
generate_loop_cover_packet(
|
generate_loop_cover_packet(
|
||||||
&mut self.rng,
|
&mut self.rng,
|
||||||
topology_ref,
|
topology_ref,
|
||||||
&self.ack_key,
|
&*self.ack_key,
|
||||||
&self.our_full_destination,
|
&self.our_full_destination,
|
||||||
self.config.average_ack_delay,
|
self.config.average_ack_delay,
|
||||||
self.config.average_packet_delay,
|
self.config.average_packet_delay,
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ use std::time::Duration;
|
|||||||
use task::ShutdownListener;
|
use task::ShutdownListener;
|
||||||
use tokio::sync::{RwLock, RwLockReadGuard};
|
use tokio::sync::{RwLock, RwLockReadGuard};
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
use topology::{nym_topology_from_detailed, NymTopology};
|
use topology::{nym_topology_from_bonds, NymTopology};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
// I'm extremely curious why compiler NEVER complained about lack of Debug here before
|
// 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 mixnodes_count = mixnodes.len();
|
||||||
let topology = nym_topology_from_detailed(mixnodes, gateways)
|
let topology =
|
||||||
.filter_system_version(&self.client_version);
|
nym_topology_from_bonds(mixnodes, gateways).filter_system_version(&self.client_version);
|
||||||
|
|
||||||
if !self.check_layer_distribution(&topology, mixnodes_count) {
|
if !self.check_layer_distribution(&topology, mixnodes_count) {
|
||||||
warn!("The current filtered active topology has extremely skewed layer distribution. It cannot be used.");
|
warn!("The current filtered active topology has extremely skewed layer distribution. It cannot be used.");
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ edition = "2021"
|
|||||||
async-trait = "0.1.52"
|
async-trait = "0.1.52"
|
||||||
bip39 = "1.0.1"
|
bip39 = "1.0.1"
|
||||||
cfg-if = "0.1"
|
cfg-if = "0.1"
|
||||||
clap = { version = "3.2", features = ["cargo", "derive"] }
|
clap = { version = "3.0.10", features = ["cargo", "derive"] }
|
||||||
pickledb = "0.4.1"
|
pickledb = "0.4.1"
|
||||||
rand = "0.7.3"
|
rand = "0.7.3"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
@@ -19,7 +19,6 @@ tokio = { version = "1.19.1", features = ["rt-multi-thread", "net", "signal", "m
|
|||||||
|
|
||||||
coconut-interface = { path = "../../common/coconut-interface" }
|
coconut-interface = { path = "../../common/coconut-interface" }
|
||||||
config = { path = "../../common/config" }
|
config = { path = "../../common/config" }
|
||||||
completions = { path = "../../common/completions" }
|
|
||||||
credentials = { path = "../../common/credentials" }
|
credentials = { path = "../../common/credentials" }
|
||||||
credential-storage = { path = "../../common/credential-storage" }
|
credential-storage = { path = "../../common/credential-storage" }
|
||||||
crypto = { path = "../../common/crypto", features = ["rand", "asymmetric", "symmetric", "aes", "hashing"] }
|
crypto = { path = "../../common/crypto", features = ["rand", "asymmetric", "symmetric", "aes", "hashing"] }
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use clap::{Args, Subcommand};
|
use clap::{Args, Subcommand};
|
||||||
use completions::ArgShell;
|
|
||||||
use pickledb::PickleDb;
|
use pickledb::PickleDb;
|
||||||
use rand::rngs::OsRng;
|
use rand::rngs::OsRng;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
@@ -29,12 +28,6 @@ pub(crate) enum Commands {
|
|||||||
ListDeposits(ListDeposits),
|
ListDeposits(ListDeposits),
|
||||||
/// Get a credential for a given deposit
|
/// Get a credential for a given deposit
|
||||||
GetCredential(GetCredential),
|
GetCredential(GetCredential),
|
||||||
|
|
||||||
/// Generate shell completions
|
|
||||||
Completions(ArgShell),
|
|
||||||
|
|
||||||
/// Generate Fig specification
|
|
||||||
GenerateFigSpec,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
|||||||
@@ -12,8 +12,6 @@ cfg_if::cfg_if! {
|
|||||||
use commands::{Commands, Execute};
|
use commands::{Commands, Execute};
|
||||||
use error::Result;
|
use error::Result;
|
||||||
use network_defaults::setup_env;
|
use network_defaults::setup_env;
|
||||||
use clap::CommandFactory;
|
|
||||||
use completions::fig_generate;
|
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use pickledb::{PickleDb, PickleDbDumpPolicy, SerializationMethod};
|
use pickledb::{PickleDb, PickleDbDumpPolicy, SerializationMethod};
|
||||||
@@ -54,14 +52,10 @@ cfg_if::cfg_if! {
|
|||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
let bin_name = "nym-credential-client";
|
|
||||||
|
|
||||||
match &args.command {
|
match &args.command {
|
||||||
Commands::Deposit(m) => m.execute(&mut db, shared_storage).await?,
|
Commands::Deposit(m) => m.execute(&mut db, shared_storage).await?,
|
||||||
Commands::ListDeposits(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::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(())
|
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
|
# and the single instance of abortable we have should really be refactored anyway
|
||||||
url = "2.2"
|
url = "2.2"
|
||||||
|
|
||||||
clap = { version = "3.2", features = ["cargo", "derive"] }
|
clap = { version = "3.2.8", features = ["cargo", "derive"] }
|
||||||
dirs = "4.0"
|
dirs = "4.0"
|
||||||
log = "0.4" # self explanatory
|
log = "0.4" # self explanatory
|
||||||
pretty_env_logger = "0.4" # for formatting log messages
|
pretty_env_logger = "0.4" # for formatting log messages
|
||||||
@@ -34,7 +34,6 @@ tokio-tungstenite = "0.14" # websocket
|
|||||||
client-core = { path = "../client-core" }
|
client-core = { path = "../client-core" }
|
||||||
coconut-interface = { path = "../../common/coconut-interface", optional = true }
|
coconut-interface = { path = "../../common/coconut-interface", optional = true }
|
||||||
config = { path = "../../common/config" }
|
config = { path = "../../common/config" }
|
||||||
completions = { path = "../../common/completions" }
|
|
||||||
credential-storage = { path = "../../common/credential-storage" }
|
credential-storage = { path = "../../common/credential-storage" }
|
||||||
credentials = { path = "../../common/credentials", optional = true }
|
credentials = { path = "../../common/credentials", optional = true }
|
||||||
crypto = { path = "../../common/crypto" }
|
crypto = { path = "../../common/crypto" }
|
||||||
|
|||||||
@@ -2,9 +2,7 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::client::config::{Config, SocketType};
|
use crate::client::config::{Config, SocketType};
|
||||||
use clap::CommandFactory;
|
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use completions::{fig_generate, ArgShell};
|
|
||||||
|
|
||||||
pub(crate) mod init;
|
pub(crate) mod init;
|
||||||
pub(crate) mod run;
|
pub(crate) mod run;
|
||||||
@@ -64,12 +62,6 @@ pub(crate) enum Commands {
|
|||||||
Run(run::Run),
|
Run(run::Run),
|
||||||
/// Try to upgrade the client
|
/// Try to upgrade the client
|
||||||
Upgrade(upgrade::Upgrade),
|
Upgrade(upgrade::Upgrade),
|
||||||
|
|
||||||
/// Generate shell completions
|
|
||||||
Completions(ArgShell),
|
|
||||||
|
|
||||||
/// Generate Fig specification
|
|
||||||
GenerateFigSpec,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configuration that can be overridden.
|
// Configuration that can be overridden.
|
||||||
@@ -84,14 +76,10 @@ pub(crate) struct OverrideConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn execute(args: &Cli) {
|
pub(crate) async fn execute(args: &Cli) {
|
||||||
let bin_name = "nym-native-client";
|
|
||||||
|
|
||||||
match &args.command {
|
match &args.command {
|
||||||
Commands::Init(m) => init::execute(m).await,
|
Commands::Init(m) => init::execute(m).await,
|
||||||
Commands::Run(m) => run::execute(m).await,
|
Commands::Run(m) => run::execute(m).await,
|
||||||
Commands::Upgrade(m) => upgrade::execute(m),
|
Commands::Upgrade(m) => upgrade::execute(m),
|
||||||
Commands::Completions(s) => s.generate(&mut Cli::into_app(), bin_name),
|
|
||||||
Commands::GenerateFigSpec => fig_generate(&mut Cli::into_app(), bin_name),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ fn minor_0_12_upgrade(
|
|||||||
Version::new(0, 12, 0)
|
Version::new(0, 12, 0)
|
||||||
};
|
};
|
||||||
|
|
||||||
print_start_upgrade(config_version, &to_version);
|
print_start_upgrade(&config_version, &to_version);
|
||||||
|
|
||||||
config
|
config
|
||||||
.get_base_mut()
|
.get_base_mut()
|
||||||
@@ -111,7 +111,7 @@ fn minor_0_12_upgrade(
|
|||||||
|
|
||||||
config.save_to_file(None).unwrap_or_else(|err| {
|
config.save_to_file(None).unwrap_or_else(|err| {
|
||||||
eprintln!("failed to overwrite config file! - {:?}", 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);
|
process::exit(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ name = "nym_socks5"
|
|||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { version = "3.2", features = ["cargo", "derive"] }
|
clap = { version = "3.2.8", features = ["cargo", "derive"] }
|
||||||
dirs = "4.0"
|
dirs = "4.0"
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
@@ -27,7 +27,6 @@ url = "2.2"
|
|||||||
client-core = { path = "../client-core" }
|
client-core = { path = "../client-core" }
|
||||||
coconut-interface = { path = "../../common/coconut-interface", optional = true }
|
coconut-interface = { path = "../../common/coconut-interface", optional = true }
|
||||||
config = { path = "../../common/config" }
|
config = { path = "../../common/config" }
|
||||||
completions = { path = "../../common/completions" }
|
|
||||||
credential-storage = { path = "../../common/credential-storage" }
|
credential-storage = { path = "../../common/credential-storage" }
|
||||||
credentials = { path = "../../common/credentials", optional = true }
|
credentials = { path = "../../common/credentials", optional = true }
|
||||||
crypto = { path = "../../common/crypto" }
|
crypto = { path = "../../common/crypto" }
|
||||||
|
|||||||
@@ -2,9 +2,7 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::client::config::Config;
|
use crate::client::config::Config;
|
||||||
use clap::CommandFactory;
|
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use completions::{fig_generate, ArgShell};
|
|
||||||
use config::parse_validators;
|
use config::parse_validators;
|
||||||
|
|
||||||
pub mod init;
|
pub mod init;
|
||||||
@@ -65,12 +63,6 @@ pub(crate) enum Commands {
|
|||||||
Run(run::Run),
|
Run(run::Run),
|
||||||
/// Try to upgrade the client
|
/// Try to upgrade the client
|
||||||
Upgrade(upgrade::Upgrade),
|
Upgrade(upgrade::Upgrade),
|
||||||
|
|
||||||
/// Generate shell completions
|
|
||||||
Completions(ArgShell),
|
|
||||||
|
|
||||||
/// Generate Fig specification
|
|
||||||
GenerateFigSpec,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configuration that can be overridden.
|
// Configuration that can be overridden.
|
||||||
@@ -84,14 +76,10 @@ pub(crate) struct OverrideConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn execute(args: &Cli) {
|
pub(crate) async fn execute(args: &Cli) {
|
||||||
let bin_name = "nym-socks5-client";
|
|
||||||
|
|
||||||
match &args.command {
|
match &args.command {
|
||||||
Commands::Init(m) => init::execute(m).await,
|
Commands::Init(m) => init::execute(m).await,
|
||||||
Commands::Run(m) => run::execute(m).await,
|
Commands::Run(m) => run::execute(m).await,
|
||||||
Commands::Upgrade(m) => upgrade::execute(m),
|
Commands::Upgrade(m) => upgrade::execute(m),
|
||||||
Commands::Completions(s) => s.generate(&mut Cli::into_app(), bin_name),
|
|
||||||
Commands::GenerateFigSpec => fig_generate(&mut Cli::into_app(), bin_name),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ fn minor_0_12_upgrade(
|
|||||||
Version::new(0, 12, 0)
|
Version::new(0, 12, 0)
|
||||||
};
|
};
|
||||||
|
|
||||||
print_start_upgrade(config_version, &to_version);
|
print_start_upgrade(&config_version, &to_version);
|
||||||
|
|
||||||
config
|
config
|
||||||
.get_base_mut()
|
.get_base_mut()
|
||||||
@@ -110,7 +110,7 @@ fn minor_0_12_upgrade(
|
|||||||
|
|
||||||
config.save_to_file(None).unwrap_or_else(|err| {
|
config.save_to_file(None).unwrap_or_else(|err| {
|
||||||
eprintln!("failed to overwrite config file! - {:?}", 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);
|
process::exit(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ async function main() {
|
|||||||
set_panic_hook();
|
set_panic_hook();
|
||||||
|
|
||||||
// validator server we will use to get topology from
|
// validator server we will use to get topology from
|
||||||
const validator = "https://validator.nymtech.net/api";
|
const validator = "https://validator.nymtech.net/api"; //"http://localhost:8081";
|
||||||
|
|
||||||
client = new NymClient(validator);
|
client = new NymClient(validator);
|
||||||
|
|
||||||
@@ -69,7 +69,7 @@ async function sendMessageTo() {
|
|||||||
* @param {string} message
|
* @param {string} message
|
||||||
*/
|
*/
|
||||||
function displaySend(message) {
|
function displaySend(message) {
|
||||||
let timestamp = new Date().toISOString().slice(11, 21);
|
let timestamp = new Date().toISOString().substr(11, 12);
|
||||||
|
|
||||||
let sendDiv = document.createElement("div")
|
let sendDiv = document.createElement("div")
|
||||||
let paragraph = document.createElement("p")
|
let paragraph = document.createElement("p")
|
||||||
@@ -90,11 +90,11 @@ function displayReceived(message) {
|
|||||||
const content = message.message
|
const content = message.message
|
||||||
const replySurb = message.replySurb
|
const replySurb = message.replySurb
|
||||||
|
|
||||||
let timestamp = new Date().toISOString().slice(11, 21);
|
let timestamp = new Date().toISOString().substr(11, 12);
|
||||||
let receivedDiv = document.createElement("div")
|
let receivedDiv = document.createElement("div")
|
||||||
let paragraph = document.createElement("p")
|
let paragraph = document.createElement("p")
|
||||||
paragraph.setAttribute('style', 'color: green')
|
paragraph.setAttribute('style', 'color: green')
|
||||||
let paragraphContent = document.createTextNode(timestamp + " received >>> " + content)
|
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)"))
|
||||||
paragraph.appendChild(paragraphContent)
|
paragraph.appendChild(paragraphContent)
|
||||||
receivedDiv.appendChild(paragraph)
|
receivedDiv.appendChild(paragraph)
|
||||||
document.getElementById("output").appendChild(receivedDiv)
|
document.getElementById("output").appendChild(receivedDiv)
|
||||||
|
|||||||
@@ -34,6 +34,6 @@
|
|||||||
"webpack-dev-server": "^4.7.4"
|
"webpack-dev-server": "^4.7.4"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nymproject/nym-client-wasm": "1.0.0"
|
"@nymproject/nym-client-wasm": "file:../pkg"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ use rand::rngs::OsRng;
|
|||||||
use received_processor::ReceivedMessagesProcessor;
|
use received_processor::ReceivedMessagesProcessor;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use topology::{gateway, nym_topology_from_detailed, NymTopology};
|
use topology::{gateway, nym_topology_from_bonds, NymTopology};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
use wasm_bindgen_futures::spawn_local;
|
use wasm_bindgen_futures::spawn_local;
|
||||||
@@ -265,7 +265,7 @@ impl NymClient {
|
|||||||
Ok(gateways) => gateways,
|
Ok(gateways) => gateways,
|
||||||
};
|
};
|
||||||
|
|
||||||
let topology = nym_topology_from_detailed(mixnodes, gateways);
|
let topology = nym_topology_from_bonds(mixnodes, gateways);
|
||||||
let version = env!("CARGO_PKG_VERSION");
|
let version = env!("CARGO_PKG_VERSION");
|
||||||
topology.filter_system_version(version)
|
topology.filter_system_version(version)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -186,7 +186,7 @@ impl Client {
|
|||||||
address.into(),
|
address.into(),
|
||||||
receiver,
|
receiver,
|
||||||
initial_connection_timeout,
|
initial_connection_timeout,
|
||||||
¤t_reconnection_attempt,
|
&*current_reconnection_attempt,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ sha2 = { version = "0.9.5", optional = true }
|
|||||||
itertools = { version = "0.10", optional = true }
|
itertools = { version = "0.10", optional = true }
|
||||||
cosmwasm-std = { version = "1.0.0", optional = true }
|
cosmwasm-std = { version = "1.0.0", optional = true }
|
||||||
execute = { path = "../../execute" }
|
execute = { path = "../../execute" }
|
||||||
|
ledger = { path = "../../ledger" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
ts-rs = "6.1.2"
|
ts-rs = "6.1.2"
|
||||||
|
|||||||
@@ -2,33 +2,38 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::{validator_api, ValidatorClientError};
|
use crate::{validator_api, ValidatorClientError};
|
||||||
use mixnet_contract_common::mixnode::MixNodeDetails;
|
use mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixNodeBond};
|
||||||
use mixnet_contract_common::NodeId;
|
|
||||||
use mixnet_contract_common::{GatewayBond, IdentityKeyRef};
|
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use validator_api_requests::coconut::{
|
use validator_api_requests::coconut::{
|
||||||
BlindSignRequestBody, BlindedSignatureResponse, CosmosAddressResponse, VerificationKeyResponse,
|
BlindSignRequestBody, BlindedSignatureResponse, CosmosAddressResponse, VerificationKeyResponse,
|
||||||
VerifyCredentialBody, VerifyCredentialResponse,
|
VerifyCredentialBody, VerifyCredentialResponse,
|
||||||
};
|
};
|
||||||
use validator_api_requests::models::{
|
use validator_api_requests::models::{
|
||||||
GatewayCoreStatusResponse, MixnodeCoreStatusResponse, MixnodeStatusResponse,
|
CoreNodeStatusResponse, MixnodeStatusResponse, RewardEstimationResponse,
|
||||||
RewardEstimationResponse, StakeSaturationResponse,
|
StakeSaturationResponse,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "nymd-client")]
|
#[cfg(feature = "nymd-client")]
|
||||||
use crate::nymd::traits::MixnetQueryClient;
|
use validator_api_requests::models::{MixNodeBondAnnotated, UptimeResponse};
|
||||||
|
|
||||||
#[cfg(feature = "nymd-client")]
|
#[cfg(feature = "nymd-client")]
|
||||||
use crate::nymd::{self, CosmWasmClient, NymdClient, QueryNymdClient, SigningNymdClient};
|
use crate::nymd::{
|
||||||
|
self, error::NymdError, CosmWasmClient, NymdClient, QueryNymdClient, SigningNymdClient,
|
||||||
|
WalletNymdClient,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "nymd-client")]
|
||||||
|
use ledger::CosmosLedger;
|
||||||
#[cfg(feature = "nymd-client")]
|
#[cfg(feature = "nymd-client")]
|
||||||
use mixnet_contract_common::{
|
use mixnet_contract_common::{
|
||||||
mixnode::MixNodeBond,
|
mixnode::DelegationEvent, ContractStateParams, Delegation, IdentityKey, Interval,
|
||||||
pending_events::{PendingEpochEvent, PendingIntervalEvent},
|
MixnetContractVersion, MixnodeRewardingStatusResponse, RewardedSetNodeStatus,
|
||||||
Delegation, RewardedSetNodeStatus, UnbondedMixnode,
|
RewardedSetUpdateDetails,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "nymd-client")]
|
#[cfg(feature = "nymd-client")]
|
||||||
use network_defaults::NymNetworkDetails;
|
use network_defaults::{NymNetworkDetails, COSMOS_DERIVATION_PATH};
|
||||||
#[cfg(feature = "nymd-client")]
|
#[cfg(feature = "nymd-client")]
|
||||||
use validator_api_requests::models::MixNodeBondAnnotated;
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
#[cfg(feature = "nymd-client")]
|
#[cfg(feature = "nymd-client")]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@@ -165,6 +170,51 @@ impl Client<SigningNymdClient> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "nymd-client")]
|
||||||
|
impl Client<WalletNymdClient> {
|
||||||
|
pub fn new_ledger(config: Config) -> Result<Client<WalletNymdClient>, ValidatorClientError> {
|
||||||
|
let validator_api_client = validator_api::Client::new(config.api_url.clone());
|
||||||
|
let signer = CosmosLedger::new(
|
||||||
|
COSMOS_DERIVATION_PATH.parse().unwrap(),
|
||||||
|
config
|
||||||
|
.nymd_config
|
||||||
|
.chain_details
|
||||||
|
.bech32_account_prefix
|
||||||
|
.clone(),
|
||||||
|
)?;
|
||||||
|
let nymd_client = NymdClient::connect_with_ledger(
|
||||||
|
config.nymd_config.clone(),
|
||||||
|
config.nymd_url.as_str(),
|
||||||
|
signer,
|
||||||
|
None,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(Client {
|
||||||
|
mnemonic: None,
|
||||||
|
mixnode_page_limit: config.mixnode_page_limit,
|
||||||
|
gateway_page_limit: config.gateway_page_limit,
|
||||||
|
mixnode_delegations_page_limit: config.mixnode_delegations_page_limit,
|
||||||
|
rewarded_set_page_limit: config.rewarded_set_page_limit,
|
||||||
|
validator_api: validator_api_client,
|
||||||
|
nymd: nymd_client,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn change_nymd(&mut self, new_endpoint: Url) -> Result<(), ValidatorClientError> {
|
||||||
|
self.nymd = NymdClient::connect_with_ledger(
|
||||||
|
self.nymd.current_config().clone(),
|
||||||
|
new_endpoint.as_ref(),
|
||||||
|
self.nymd.ledger_signer(),
|
||||||
|
None,
|
||||||
|
)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_nymd_simulated_gas_multiplier(&mut self, multiplier: f32) {
|
||||||
|
self.nymd.set_simulated_gas_multiplier(multiplier)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "nymd-client")]
|
#[cfg(feature = "nymd-client")]
|
||||||
impl Client<QueryNymdClient> {
|
impl Client<QueryNymdClient> {
|
||||||
pub fn new_query(config: Config) -> Result<Client<QueryNymdClient>, ValidatorClientError> {
|
pub fn new_query(config: Config) -> Result<Client<QueryNymdClient>, ValidatorClientError> {
|
||||||
@@ -189,9 +239,12 @@ impl Client<QueryNymdClient> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// nymd wrappers
|
|
||||||
#[cfg(feature = "nymd-client")]
|
#[cfg(feature = "nymd-client")]
|
||||||
impl<C> Client<C> {
|
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
|
// 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
|
// 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) {
|
pub fn set_mixnet_contract_address(&mut self, mixnet_contract_address: cosmrs::AccountId) {
|
||||||
@@ -203,22 +256,203 @@ impl<C> Client<C> {
|
|||||||
self.nymd.mixnet_contract_address().clone()
|
self.nymd.mixnet_contract_address().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
// basically handles paging for us
|
pub async fn get_cached_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorClientError> {
|
||||||
pub async fn get_all_nymd_rewarded_set_mixnodes(
|
Ok(self.validator_api.get_mixnodes().await?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_cached_mixnodes_detailed(
|
||||||
&self,
|
&self,
|
||||||
) -> Result<Vec<(NodeId, RewardedSetNodeStatus)>, ValidatorClientError>
|
) -> 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
|
where
|
||||||
C: CosmWasmClient + Sync + Send,
|
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(
|
||||||
|
&self,
|
||||||
|
) -> Result<Vec<(IdentityKey, RewardedSetNodeStatus)>, ValidatorClientError>
|
||||||
|
where
|
||||||
|
C: CosmWasmClient + Sync,
|
||||||
{
|
{
|
||||||
let mut identities = Vec::new();
|
let mut identities = Vec::new();
|
||||||
let mut start_after = None;
|
let mut start_after = None;
|
||||||
|
let mut height = None;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let mut paged_response = self
|
let mut paged_response = self
|
||||||
.nymd
|
.nymd
|
||||||
.get_rewarded_set_paged(start_after.take(), self.rewarded_set_page_limit)
|
.get_rewarded_set_identities_paged(
|
||||||
|
start_after.take(),
|
||||||
|
self.rewarded_set_page_limit,
|
||||||
|
height,
|
||||||
|
)
|
||||||
.await?;
|
.await?;
|
||||||
identities.append(&mut paged_response.nodes);
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(start_after_res) = paged_response.start_next_after {
|
if let Some(start_after_res) = paged_response.start_next_after {
|
||||||
start_after = Some(start_after_res)
|
start_after = Some(start_after_res)
|
||||||
@@ -230,122 +464,83 @@ impl<C> Client<C> {
|
|||||||
Ok(identities)
|
Ok(identities)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_all_nymd_mixnode_bonds(&self) -> Result<Vec<MixNodeBond>, ValidatorClientError>
|
pub async fn get_nymd_rewarded_and_active_sets(
|
||||||
|
&self,
|
||||||
|
) -> Result<Vec<(MixNodeBond, RewardedSetNodeStatus)>, ValidatorClientError>
|
||||||
where
|
where
|
||||||
C: CosmWasmClient + Sync + Send,
|
C: CosmWasmClient + Sync,
|
||||||
{
|
{
|
||||||
let mut mixnodes = Vec::new();
|
let all_mixnodes = self.get_all_nymd_mixnodes().await?;
|
||||||
let mut start_after = None;
|
let rewarded_set_identities = self
|
||||||
loop {
|
.get_all_nymd_rewarded_set_mixnode_identities()
|
||||||
let mut paged_response = self
|
.await?
|
||||||
.nymd
|
.into_iter()
|
||||||
.get_mixnode_bonds_paged(self.mixnode_page_limit, start_after.take())
|
.collect::<HashMap<_, _>>();
|
||||||
.await?;
|
|
||||||
mixnodes.append(&mut paged_response.nodes);
|
|
||||||
|
|
||||||
if let Some(start_after_res) = paged_response.start_next_after {
|
Ok(all_mixnodes
|
||||||
start_after = Some(start_after_res)
|
.into_iter()
|
||||||
} else {
|
.filter_map(|node| {
|
||||||
break;
|
rewarded_set_identities
|
||||||
}
|
.get(node.identity())
|
||||||
}
|
.map(|status| (node, *status))
|
||||||
|
})
|
||||||
Ok(mixnodes)
|
.collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_all_nymd_mixnodes_detailed(
|
/// If you need both rewarded and the active set, consider using [Self::get_nymd_rewarded_and_active_sets] instead
|
||||||
&self,
|
pub async fn get_nymd_rewarded_set(&self) -> Result<Vec<MixNodeBond>, ValidatorClientError>
|
||||||
) -> Result<Vec<MixNodeDetails>, ValidatorClientError>
|
|
||||||
where
|
where
|
||||||
C: CosmWasmClient + Sync + Send,
|
C: CosmWasmClient + Sync,
|
||||||
{
|
{
|
||||||
let mut mixnodes = Vec::new();
|
let all_mixnodes = self.get_all_nymd_mixnodes().await?;
|
||||||
let mut start_after = None;
|
let rewarded_set_identities = self
|
||||||
loop {
|
.get_all_nymd_rewarded_set_mixnode_identities()
|
||||||
let mut paged_response = self
|
.await?
|
||||||
.nymd
|
.into_iter()
|
||||||
.get_mixnodes_detailed_paged(self.mixnode_page_limit, start_after.take())
|
.map(|(identity, _status)| identity)
|
||||||
.await?;
|
.collect::<HashSet<_>>();
|
||||||
mixnodes.append(&mut paged_response.nodes);
|
|
||||||
|
|
||||||
if let Some(start_after_res) = paged_response.start_next_after {
|
Ok(all_mixnodes
|
||||||
start_after = Some(start_after_res)
|
.into_iter()
|
||||||
} else {
|
.filter(|node| rewarded_set_identities.contains(node.identity()))
|
||||||
break;
|
.collect())
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(mixnodes)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_all_nymd_unbonded_mixnodes(
|
/// If you need both rewarded and the active set, consider using [Self::get_nymd_rewarded_and_active_sets] instead
|
||||||
&self,
|
pub async fn get_nymd_active_set(&self) -> Result<Vec<MixNodeBond>, ValidatorClientError>
|
||||||
) -> Result<Vec<(NodeId, UnbondedMixnode)>, ValidatorClientError>
|
|
||||||
where
|
where
|
||||||
C: CosmWasmClient + Sync + Send,
|
C: CosmWasmClient + Sync,
|
||||||
{
|
{
|
||||||
let mut mixnodes = Vec::new();
|
let all_mixnodes = self.get_all_nymd_mixnodes().await?;
|
||||||
let mut start_after = None;
|
let active_set_identities = self
|
||||||
loop {
|
.get_all_nymd_rewarded_set_mixnode_identities()
|
||||||
let mut paged_response = self
|
.await?
|
||||||
.nymd
|
.into_iter()
|
||||||
.get_unbonded_paged(self.mixnode_page_limit, start_after.take())
|
.filter_map(|(identity, status)| {
|
||||||
.await?;
|
if status.is_active() {
|
||||||
mixnodes.append(&mut paged_response.nodes);
|
Some(identity)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<HashSet<_>>();
|
||||||
|
|
||||||
if let Some(start_after_res) = paged_response.start_next_after {
|
Ok(all_mixnodes
|
||||||
start_after = Some(start_after_res)
|
.into_iter()
|
||||||
} else {
|
.filter(|node| active_set_identities.contains(node.identity()))
|
||||||
break;
|
.collect())
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(mixnodes)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_all_nymd_unbonded_mixnodes_by_owner(
|
pub async fn get_all_nymd_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorClientError>
|
||||||
&self,
|
|
||||||
owner: &cosmrs::AccountId,
|
|
||||||
) -> Result<Vec<(NodeId, UnbondedMixnode)>, ValidatorClientError>
|
|
||||||
where
|
where
|
||||||
C: CosmWasmClient + Sync + Send,
|
C: CosmWasmClient + Sync,
|
||||||
{
|
{
|
||||||
let mut mixnodes = Vec::new();
|
let mut mixnodes = Vec::new();
|
||||||
let mut start_after = None;
|
let mut start_after = None;
|
||||||
loop {
|
loop {
|
||||||
let mut paged_response = self
|
let mut paged_response = self
|
||||||
.nymd
|
.nymd
|
||||||
.get_unbonded_by_owner_paged(owner, self.mixnode_page_limit, start_after.take())
|
.get_mixnodes_paged(start_after.take(), self.mixnode_page_limit)
|
||||||
.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?;
|
.await?;
|
||||||
mixnodes.append(&mut paged_response.nodes);
|
mixnodes.append(&mut paged_response.nodes);
|
||||||
|
|
||||||
@@ -361,7 +556,7 @@ impl<C> Client<C> {
|
|||||||
|
|
||||||
pub async fn get_all_nymd_gateways(&self) -> Result<Vec<GatewayBond>, ValidatorClientError>
|
pub async fn get_all_nymd_gateways(&self) -> Result<Vec<GatewayBond>, ValidatorClientError>
|
||||||
where
|
where
|
||||||
C: CosmWasmClient + Sync + Send,
|
C: CosmWasmClient + Sync,
|
||||||
{
|
{
|
||||||
let mut gateways = Vec::new();
|
let mut gateways = Vec::new();
|
||||||
let mut start_after = None;
|
let mut start_after = None;
|
||||||
@@ -384,18 +579,18 @@ impl<C> Client<C> {
|
|||||||
|
|
||||||
pub async fn get_all_nymd_single_mixnode_delegations(
|
pub async fn get_all_nymd_single_mixnode_delegations(
|
||||||
&self,
|
&self,
|
||||||
mix_id: NodeId,
|
identity: IdentityKey,
|
||||||
) -> Result<Vec<Delegation>, ValidatorClientError>
|
) -> Result<Vec<Delegation>, ValidatorClientError>
|
||||||
where
|
where
|
||||||
C: CosmWasmClient + Sync + Send,
|
C: CosmWasmClient + Sync,
|
||||||
{
|
{
|
||||||
let mut delegations = Vec::new();
|
let mut delegations = Vec::new();
|
||||||
let mut start_after = None;
|
let mut start_after = None;
|
||||||
loop {
|
loop {
|
||||||
let mut paged_response = self
|
let mut paged_response = self
|
||||||
.nymd
|
.nymd
|
||||||
.get_mixnode_delegations_paged(
|
.get_mix_delegations_paged(
|
||||||
mix_id,
|
identity.clone(),
|
||||||
start_after.take(),
|
start_after.take(),
|
||||||
self.mixnode_delegations_page_limit,
|
self.mixnode_delegations_page_limit,
|
||||||
)
|
)
|
||||||
@@ -417,7 +612,7 @@ impl<C> Client<C> {
|
|||||||
delegation_owner: &cosmrs::AccountId,
|
delegation_owner: &cosmrs::AccountId,
|
||||||
) -> Result<Vec<Delegation>, ValidatorClientError>
|
) -> Result<Vec<Delegation>, ValidatorClientError>
|
||||||
where
|
where
|
||||||
C: CosmWasmClient + Sync + Send,
|
C: CosmWasmClient + Sync,
|
||||||
{
|
{
|
||||||
let mut delegations = Vec::new();
|
let mut delegations = Vec::new();
|
||||||
let mut start_after = None;
|
let mut start_after = None;
|
||||||
@@ -442,128 +637,10 @@ impl<C> Client<C> {
|
|||||||
Ok(delegations)
|
Ok(delegations)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_all_network_delegations(&self) -> Result<Vec<Delegation>, ValidatorClientError>
|
pub async fn get_mixnode_avg_uptimes(
|
||||||
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,
|
&self,
|
||||||
) -> Result<Vec<PendingEpochEvent>, ValidatorClientError>
|
) -> Result<Vec<UptimeResponse>, ValidatorClientError> {
|
||||||
where
|
Ok(self.validator_api.get_mixnode_avg_uptimes().await?)
|
||||||
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(
|
pub async fn blind_sign(
|
||||||
@@ -601,17 +678,17 @@ impl ApiClient {
|
|||||||
|
|
||||||
pub async fn get_cached_active_mixnodes(
|
pub async fn get_cached_active_mixnodes(
|
||||||
&self,
|
&self,
|
||||||
) -> Result<Vec<MixNodeDetails>, ValidatorClientError> {
|
) -> Result<Vec<MixNodeBond>, ValidatorClientError> {
|
||||||
Ok(self.validator_api.get_active_mixnodes().await?)
|
Ok(self.validator_api.get_active_mixnodes().await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_cached_rewarded_mixnodes(
|
pub async fn get_cached_rewarded_mixnodes(
|
||||||
&self,
|
&self,
|
||||||
) -> Result<Vec<MixNodeDetails>, ValidatorClientError> {
|
) -> Result<Vec<MixNodeBond>, ValidatorClientError> {
|
||||||
Ok(self.validator_api.get_rewarded_mixnodes().await?)
|
Ok(self.validator_api.get_rewarded_mixnodes().await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_cached_mixnodes(&self) -> Result<Vec<MixNodeDetails>, ValidatorClientError> {
|
pub async fn get_cached_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorClientError> {
|
||||||
Ok(self.validator_api.get_mixnodes().await?)
|
Ok(self.validator_api.get_mixnodes().await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -623,7 +700,7 @@ impl ApiClient {
|
|||||||
&self,
|
&self,
|
||||||
identity: IdentityKeyRef<'_>,
|
identity: IdentityKeyRef<'_>,
|
||||||
since: Option<i64>,
|
since: Option<i64>,
|
||||||
) -> Result<GatewayCoreStatusResponse, ValidatorClientError> {
|
) -> Result<CoreNodeStatusResponse, ValidatorClientError> {
|
||||||
Ok(self
|
Ok(self
|
||||||
.validator_api
|
.validator_api
|
||||||
.get_gateway_core_status_count(identity, since)
|
.get_gateway_core_status_count(identity, since)
|
||||||
@@ -632,39 +709,39 @@ impl ApiClient {
|
|||||||
|
|
||||||
pub async fn get_mixnode_core_status_count(
|
pub async fn get_mixnode_core_status_count(
|
||||||
&self,
|
&self,
|
||||||
mix_id: NodeId,
|
identity: IdentityKeyRef<'_>,
|
||||||
since: Option<i64>,
|
since: Option<i64>,
|
||||||
) -> Result<MixnodeCoreStatusResponse, ValidatorClientError> {
|
) -> Result<CoreNodeStatusResponse, ValidatorClientError> {
|
||||||
Ok(self
|
Ok(self
|
||||||
.validator_api
|
.validator_api
|
||||||
.get_mixnode_core_status_count(mix_id, since)
|
.get_mixnode_core_status_count(identity, since)
|
||||||
.await?)
|
.await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_mixnode_status(
|
pub async fn get_mixnode_status(
|
||||||
&self,
|
&self,
|
||||||
mix_id: NodeId,
|
identity: IdentityKeyRef<'_>,
|
||||||
) -> Result<MixnodeStatusResponse, ValidatorClientError> {
|
) -> Result<MixnodeStatusResponse, ValidatorClientError> {
|
||||||
Ok(self.validator_api.get_mixnode_status(mix_id).await?)
|
Ok(self.validator_api.get_mixnode_status(identity).await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_mixnode_reward_estimation(
|
pub async fn get_mixnode_reward_estimation(
|
||||||
&self,
|
&self,
|
||||||
mix_id: NodeId,
|
identity: IdentityKeyRef<'_>,
|
||||||
) -> Result<RewardEstimationResponse, ValidatorClientError> {
|
) -> Result<RewardEstimationResponse, ValidatorClientError> {
|
||||||
Ok(self
|
Ok(self
|
||||||
.validator_api
|
.validator_api
|
||||||
.get_mixnode_reward_estimation(mix_id)
|
.get_mixnode_reward_estimation(identity)
|
||||||
.await?)
|
.await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_mixnode_stake_saturation(
|
pub async fn get_mixnode_stake_saturation(
|
||||||
&self,
|
&self,
|
||||||
mix_id: NodeId,
|
identity: IdentityKeyRef<'_>,
|
||||||
) -> Result<StakeSaturationResponse, ValidatorClientError> {
|
) -> Result<StakeSaturationResponse, ValidatorClientError> {
|
||||||
Ok(self
|
Ok(self
|
||||||
.validator_api
|
.validator_api
|
||||||
.get_mixnode_stake_saturation(mix_id)
|
.get_mixnode_stake_saturation(identity)
|
||||||
.await?)
|
.await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
use crate::nymd::error::NymdError;
|
use crate::nymd::error::NymdError;
|
||||||
use crate::nymd::{Config as ClientConfig, NymdClient, QueryNymdClient};
|
use crate::nymd::{Config as ClientConfig, NymdClient, QueryNymdClient};
|
||||||
use crate::ApiClient;
|
use crate::ApiClient;
|
||||||
|
use network_defaults::all::Network;
|
||||||
|
|
||||||
use crate::nymd::traits::MixnetQueryClient;
|
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use network_defaults::NymNetworkDetails;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::hash::BuildHasher;
|
use std::hash::BuildHasher;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
@@ -18,12 +17,12 @@ const CONNECTION_TEST_TIMEOUT_SEC: u64 = 2;
|
|||||||
|
|
||||||
// Run connection tests for all specified nymd and api urls. These are all run concurrently.
|
// 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>(
|
pub async fn run_validator_connection_test<H: BuildHasher + 'static>(
|
||||||
nymd_urls: impl Iterator<Item = (NymNetworkDetails, Url)>,
|
nymd_urls: impl Iterator<Item = (Network, Url)>,
|
||||||
api_urls: impl Iterator<Item = (NymNetworkDetails, Url)>,
|
api_urls: impl Iterator<Item = (Network, Url)>,
|
||||||
mixnet_contract_address: HashMap<NymNetworkDetails, cosmrs::AccountId, H>,
|
mixnet_contract_address: HashMap<Network, cosmrs::AccountId, H>,
|
||||||
) -> (
|
) -> (
|
||||||
HashMap<NymNetworkDetails, Vec<(Url, bool)>>,
|
HashMap<Network, Vec<(Url, bool)>>,
|
||||||
HashMap<NymNetworkDetails, Vec<(Url, bool)>>,
|
HashMap<Network, Vec<(Url, bool)>>,
|
||||||
) {
|
) {
|
||||||
// Setup all the clients for the connection tests
|
// Setup all the clients for the connection tests
|
||||||
let connection_test_clients =
|
let connection_test_clients =
|
||||||
@@ -46,16 +45,16 @@ pub async fn run_validator_connection_test<H: BuildHasher + 'static>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn setup_connection_tests<H: BuildHasher + 'static>(
|
fn setup_connection_tests<H: BuildHasher + 'static>(
|
||||||
nymd_urls: impl Iterator<Item = (NymNetworkDetails, Url)>,
|
nymd_urls: impl Iterator<Item = (Network, Url)>,
|
||||||
api_urls: impl Iterator<Item = (NymNetworkDetails, Url)>,
|
api_urls: impl Iterator<Item = (Network, Url)>,
|
||||||
mixnet_contract_address: HashMap<NymNetworkDetails, cosmrs::AccountId, H>,
|
mixnet_contract_address: HashMap<Network, cosmrs::AccountId, H>,
|
||||||
) -> impl Iterator<Item = ClientForConnectionTest> {
|
) -> impl Iterator<Item = ClientForConnectionTest> {
|
||||||
let nymd_connection_test_clients = nymd_urls.filter_map(move |(network, url)| {
|
let nymd_connection_test_clients = nymd_urls.filter_map(move |(network, url)| {
|
||||||
let address = mixnet_contract_address
|
let address = mixnet_contract_address
|
||||||
.get(&network)
|
.get(&network)
|
||||||
.expect("No configured contract address")
|
.expect("No configured contract address")
|
||||||
.clone();
|
.clone();
|
||||||
let config = ClientConfig::try_from_nym_network_details(&network)
|
let config = ClientConfig::try_from_nym_network_details(&network.details())
|
||||||
.expect("failed to create valid nymd client config");
|
.expect("failed to create valid nymd client config");
|
||||||
|
|
||||||
if let Ok(mut client) = NymdClient::<QueryNymdClient>::connect(config, url.as_str()) {
|
if let Ok(mut client) = NymdClient::<QueryNymdClient>::connect(config, url.as_str()) {
|
||||||
@@ -81,7 +80,7 @@ fn setup_connection_tests<H: BuildHasher + 'static>(
|
|||||||
fn extract_and_collect_results_into_map(
|
fn extract_and_collect_results_into_map(
|
||||||
connection_results: &[ConnectionResult],
|
connection_results: &[ConnectionResult],
|
||||||
url_type: &UrlType,
|
url_type: &UrlType,
|
||||||
) -> HashMap<NymNetworkDetails, Vec<(Url, bool)>> {
|
) -> HashMap<Network, Vec<(Url, bool)>> {
|
||||||
connection_results
|
connection_results
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|c| &c.url_type() == url_type)
|
.filter(|c| &c.url_type() == url_type)
|
||||||
@@ -93,7 +92,7 @@ fn extract_and_collect_results_into_map(
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn test_nymd_connection(
|
async fn test_nymd_connection(
|
||||||
network: NymNetworkDetails,
|
network: Network,
|
||||||
url: &Url,
|
url: &Url,
|
||||||
client: &NymdClient<QueryNymdClient>,
|
client: &NymdClient<QueryNymdClient>,
|
||||||
) -> ConnectionResult {
|
) -> ConnectionResult {
|
||||||
@@ -105,47 +104,56 @@ async fn test_nymd_connection(
|
|||||||
{
|
{
|
||||||
Ok(Err(NymdError::TendermintError(e))) => {
|
Ok(Err(NymdError::TendermintError(e))) => {
|
||||||
// If we get a tendermint-rpc error, we classify the node as not contactable
|
// If we get a tendermint-rpc error, we classify the node as not contactable
|
||||||
log::debug!("Checking: nymd_url: {url}: {}: {}", "failed".red(), e);
|
log::debug!(
|
||||||
|
"Checking: nymd_url: {network}: {url}: {}: {}",
|
||||||
|
"failed".red(),
|
||||||
|
e
|
||||||
|
);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
Ok(Err(NymdError::AbciError(code, log))) => {
|
Ok(Err(NymdError::AbciError(code, log))) => {
|
||||||
// We accept the mixnet contract not found as ok from a connection standpoint. This happens
|
// We accept the mixnet contract not found as ok from a connection standpoint. This happens
|
||||||
// for example on a pre-launch network.
|
// for example on a pre-launch network.
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"Checking: nymd_url: {url}: {}, but with abci error: {code}: {log}",
|
"Checking: nymd_url: {network}: {url}: {}, but with abci error: {code}: {log}",
|
||||||
"success".green()
|
"success".green()
|
||||||
);
|
);
|
||||||
code == 18
|
code == 18
|
||||||
}
|
}
|
||||||
Ok(Err(error @ NymdError::NoContractAddressAvailable)) => {
|
Ok(Err(error @ NymdError::NoContractAddressAvailable)) => {
|
||||||
log::debug!("Checking: nymd_url: {url}: {}: {error}", "failed".red());
|
log::debug!(
|
||||||
|
"Checking: nymd_url: {network}: {url}: {}: {error}",
|
||||||
|
"failed".red()
|
||||||
|
);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
Ok(Err(e)) => {
|
Ok(Err(e)) => {
|
||||||
// For any other error, we're optimistic and just try anyway.
|
// For any other error, we're optimistic and just try anyway.
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"Checking: nymd_url: {url}: {}, but with error: {e}",
|
"Checking: nymd_url: {network}: {url}: {}, but with error: {e}",
|
||||||
"success".green()
|
"success".green()
|
||||||
);
|
);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
Ok(Ok(_)) => {
|
Ok(Ok(_)) => {
|
||||||
log::debug!("Checking: nymd_url: {url}: {}", "success".green());
|
log::debug!(
|
||||||
|
"Checking: nymd_url: {network}: {url}: {}",
|
||||||
|
"success".green()
|
||||||
|
);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::debug!("Checking: nymd_url: {url}: {}: {e}", "failed".red());
|
log::debug!(
|
||||||
|
"Checking: nymd_url: {network}: {url}: {}: {e}",
|
||||||
|
"failed".red()
|
||||||
|
);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ConnectionResult::Nymd(network, url.clone(), result)
|
ConnectionResult::Nymd(network, url.clone(), result)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn test_api_connection(
|
async fn test_api_connection(network: Network, url: &Url, client: &ApiClient) -> ConnectionResult {
|
||||||
network: NymNetworkDetails,
|
|
||||||
url: &Url,
|
|
||||||
client: &ApiClient,
|
|
||||||
) -> ConnectionResult {
|
|
||||||
let result = match timeout(
|
let result = match timeout(
|
||||||
Duration::from_secs(CONNECTION_TEST_TIMEOUT_SEC),
|
Duration::from_secs(CONNECTION_TEST_TIMEOUT_SEC),
|
||||||
client.get_cached_mixnodes(),
|
client.get_cached_mixnodes(),
|
||||||
@@ -153,15 +161,21 @@ async fn test_api_connection(
|
|||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(Ok(_)) => {
|
Ok(Ok(_)) => {
|
||||||
log::debug!("Checking: api_url: {url}: {}", "success".green());
|
log::debug!("Checking: api_url: {network}: {url}: {}", "success".green());
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
Ok(Err(e)) => {
|
Ok(Err(e)) => {
|
||||||
log::debug!("Checking: api_url: {url}: {}: {e}", "failed".red());
|
log::debug!(
|
||||||
|
"Checking: api_url: {network}: {url}: {}: {e}",
|
||||||
|
"failed".red()
|
||||||
|
);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::debug!("Checking: api_url: {url}: {}: {e}", "failed".red());
|
log::debug!(
|
||||||
|
"Checking: api_url: {network}: {url}: {}: {e}",
|
||||||
|
"failed".red()
|
||||||
|
);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -169,8 +183,8 @@ async fn test_api_connection(
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum ClientForConnectionTest {
|
enum ClientForConnectionTest {
|
||||||
Nymd(NymNetworkDetails, Url, Box<NymdClient<QueryNymdClient>>),
|
Nymd(Network, Url, Box<NymdClient<QueryNymdClient>>),
|
||||||
Api(NymNetworkDetails, Url, ApiClient),
|
Api(Network, Url, ApiClient),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClientForConnectionTest {
|
impl ClientForConnectionTest {
|
||||||
@@ -203,12 +217,12 @@ impl fmt::Display for UrlType {
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum ConnectionResult {
|
enum ConnectionResult {
|
||||||
Nymd(NymNetworkDetails, Url, bool),
|
Nymd(Network, Url, bool),
|
||||||
Api(NymNetworkDetails, Url, bool),
|
Api(Network, Url, bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConnectionResult {
|
impl ConnectionResult {
|
||||||
fn result(&self) -> (&NymNetworkDetails, &Url, &bool) {
|
fn result(&self) -> (&Network, &Url, &bool) {
|
||||||
match self {
|
match self {
|
||||||
ConnectionResult::Nymd(network, url, result)
|
ConnectionResult::Nymd(network, url, result)
|
||||||
| ConnectionResult::Api(network, url, result) => (network, url, result),
|
| ConnectionResult::Api(network, url, result) => (network, url, result),
|
||||||
@@ -225,8 +239,11 @@ impl ConnectionResult {
|
|||||||
|
|
||||||
impl fmt::Display for ConnectionResult {
|
impl fmt::Display for ConnectionResult {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
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();
|
let url_type = self.url_type();
|
||||||
write!(f, "{url}: {url_type}: connection is successful: {result}")
|
write!(
|
||||||
|
f,
|
||||||
|
"{network}: {url}: {url_type}: connection is successful: {result}"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,10 @@ pub enum ValidatorClientError {
|
|||||||
#[error("There was an issue with the Nymd client - {0}")]
|
#[error("There was an issue with the Nymd client - {0}")]
|
||||||
NymdError(#[from] crate::nymd::error::NymdError),
|
NymdError(#[from] crate::nymd::error::NymdError),
|
||||||
|
|
||||||
|
#[cfg(feature = "nymd-client")]
|
||||||
|
#[error("There was an issue with the Ledger client - {0}")]
|
||||||
|
LedgerError(#[from] ledger::error::LedgerError),
|
||||||
|
|
||||||
#[error("No validator API url has been provided")]
|
#[error("No validator API url has been provided")]
|
||||||
NoAPIUrlAvailable,
|
NoAPIUrlAvailable,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -262,6 +262,7 @@ pub trait CosmWasmClient: rpc::Client {
|
|||||||
let broadcasted = CosmWasmClient::broadcast_tx_sync(self, tx).await?;
|
let broadcasted = CosmWasmClient::broadcast_tx_sync(self, tx).await?;
|
||||||
|
|
||||||
if broadcasted.code.is_err() {
|
if broadcasted.code.is_err() {
|
||||||
|
println!("Error {:?}", broadcasted);
|
||||||
let code_val = broadcasted.code.value();
|
let code_val = broadcasted.code.value();
|
||||||
return Err(NymdError::BroadcastTxErrorDeliverTx {
|
return Err(NymdError::BroadcastTxErrorDeliverTx {
|
||||||
hash: broadcasted.hash,
|
hash: broadcasted.hash,
|
||||||
|
|||||||
@@ -5,13 +5,19 @@ use crate::nymd::error::NymdError;
|
|||||||
use crate::nymd::wallet::DirectSecp256k1HdWallet;
|
use crate::nymd::wallet::DirectSecp256k1HdWallet;
|
||||||
use crate::nymd::GasPrice;
|
use crate::nymd::GasPrice;
|
||||||
use cosmrs::rpc::{Error as TendermintRpcError, HttpClient, HttpClientUrl};
|
use cosmrs::rpc::{Error as TendermintRpcError, HttpClient, HttpClientUrl};
|
||||||
|
use ledger::CosmosLedger;
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
pub mod client;
|
pub mod client;
|
||||||
mod helpers;
|
mod helpers;
|
||||||
pub mod logs;
|
pub mod logs;
|
||||||
pub mod signing_client;
|
pub mod signing_client;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
pub mod wallet_client;
|
||||||
|
|
||||||
|
pub(crate) const DEFAULT_BROADCAST_POLLING_RATE: Duration = Duration::from_secs(4);
|
||||||
|
pub(crate) const DEFAULT_BROADCAST_TIMEOUT: Duration = Duration::from_secs(60);
|
||||||
|
|
||||||
pub fn connect<U>(endpoint: U) -> Result<HttpClient, NymdError>
|
pub fn connect<U>(endpoint: U) -> Result<HttpClient, NymdError>
|
||||||
where
|
where
|
||||||
@@ -31,3 +37,14 @@ where
|
|||||||
{
|
{
|
||||||
signing_client::Client::connect_with_signer(endpoint, signer, gas_price)
|
signing_client::Client::connect_with_signer(endpoint, signer, gas_price)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn connect_with_ledger<U: Clone>(
|
||||||
|
endpoint: U,
|
||||||
|
signer: CosmosLedger,
|
||||||
|
gas_price: GasPrice,
|
||||||
|
) -> Result<wallet_client::Client, NymdError>
|
||||||
|
where
|
||||||
|
U: TryInto<HttpClientUrl, Error = TendermintRpcError>,
|
||||||
|
{
|
||||||
|
wallet_client::Client::connect_with_ledger(endpoint, signer, gas_price)
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,22 +5,23 @@ use crate::nymd::cosmwasm_client::client::CosmWasmClient;
|
|||||||
use crate::nymd::cosmwasm_client::helpers::{compress_wasm_code, CheckResponse};
|
use crate::nymd::cosmwasm_client::helpers::{compress_wasm_code, CheckResponse};
|
||||||
use crate::nymd::cosmwasm_client::logs::{self, parse_raw_logs};
|
use crate::nymd::cosmwasm_client::logs::{self, parse_raw_logs};
|
||||||
use crate::nymd::cosmwasm_client::types::*;
|
use crate::nymd::cosmwasm_client::types::*;
|
||||||
|
use crate::nymd::cosmwasm_client::{DEFAULT_BROADCAST_POLLING_RATE, DEFAULT_BROADCAST_TIMEOUT};
|
||||||
use crate::nymd::error::NymdError;
|
use crate::nymd::error::NymdError;
|
||||||
use crate::nymd::fee::{Fee, DEFAULT_SIMULATED_GAS_MULTIPLIER};
|
use crate::nymd::fee::{Fee, DEFAULT_SIMULATED_GAS_MULTIPLIER};
|
||||||
use crate::nymd::wallet::DirectSecp256k1HdWallet;
|
use crate::nymd::wallet::DirectSecp256k1HdWallet;
|
||||||
use crate::nymd::{Coin, GasAdjustable, GasPrice, TxResponse};
|
use crate::nymd::{Coin, GasAdjustable, GasPrice, MinimalSigningCosmWasmClient, TxResponse};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use cosmrs::bank::MsgSend;
|
use cosmrs::bank::MsgSend;
|
||||||
use cosmrs::distribution::MsgWithdrawDelegatorReward;
|
use cosmrs::distribution::MsgWithdrawDelegatorReward;
|
||||||
use cosmrs::feegrant::{
|
use cosmrs::feegrant::{
|
||||||
AllowedMsgAllowance, BasicAllowance, MsgGrantAllowance, MsgRevokeAllowance,
|
AllowedMsgAllowance, BasicAllowance, MsgGrantAllowance, MsgRevokeAllowance,
|
||||||
};
|
};
|
||||||
use cosmrs::proto::cosmos::tx::signing::v1beta1::SignMode;
|
|
||||||
use cosmrs::rpc::endpoint::broadcast;
|
use cosmrs::rpc::endpoint::broadcast;
|
||||||
use cosmrs::rpc::{Error as TendermintRpcError, HttpClient, HttpClientUrl, SimpleRequest};
|
use cosmrs::rpc::{Error as TendermintRpcError, HttpClient, HttpClientUrl, SimpleRequest};
|
||||||
use cosmrs::staking::{MsgDelegate, MsgUndelegate};
|
use cosmrs::staking::{MsgDelegate, MsgUndelegate};
|
||||||
use cosmrs::tx::{self, Msg, SignDoc, SignerInfo};
|
use cosmrs::tx::{self, Msg, SignDoc, SignerInfo};
|
||||||
use cosmrs::{cosmwasm, rpc, AccountId, Any, Tx};
|
use cosmrs::{cosmwasm, rpc, AccountId, Any, Tx};
|
||||||
|
use ledger::CosmosLedger;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use sha2::Digest;
|
use sha2::Digest;
|
||||||
@@ -28,40 +29,16 @@ use sha2::Sha256;
|
|||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use std::time::{Duration, SystemTime};
|
use std::time::{Duration, SystemTime};
|
||||||
|
|
||||||
const DEFAULT_BROADCAST_POLLING_RATE: Duration = Duration::from_secs(4);
|
|
||||||
const DEFAULT_BROADCAST_TIMEOUT: Duration = Duration::from_secs(60);
|
|
||||||
|
|
||||||
fn empty_fee() -> tx::Fee {
|
|
||||||
tx::Fee {
|
|
||||||
amount: vec![],
|
|
||||||
gas_limit: Default::default(),
|
|
||||||
payer: None,
|
|
||||||
granter: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn single_unspecified_signer_auth(
|
|
||||||
public_key: Option<tx::SignerPublicKey>,
|
|
||||||
sequence_number: tx::SequenceNumber,
|
|
||||||
) -> tx::AuthInfo {
|
|
||||||
tx::SignerInfo {
|
|
||||||
public_key,
|
|
||||||
mode_info: tx::ModeInfo::Single(tx::mode_info::Single {
|
|
||||||
mode: SignMode::Unspecified,
|
|
||||||
}),
|
|
||||||
sequence: sequence_number,
|
|
||||||
}
|
|
||||||
.auth_info(empty_fee())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait SigningCosmWasmClient: CosmWasmClient {
|
pub trait SigningCosmWasmClient: MinimalSigningCosmWasmClient {
|
||||||
fn signer(&self) -> &DirectSecp256k1HdWallet;
|
fn signer(&self) -> &DirectSecp256k1HdWallet;
|
||||||
|
|
||||||
fn gas_price(&self) -> &GasPrice;
|
fn gas_price(&self) -> &GasPrice;
|
||||||
|
|
||||||
fn signer_public_key(&self, signer_address: &AccountId) -> Option<tx::SignerPublicKey> {
|
fn signer_public_key(&self, signer_address: &AccountId) -> Option<tx::SignerPublicKey> {
|
||||||
let signer_accounts = self.signer().try_derive_accounts().ok()?;
|
let signer_accounts = SigningCosmWasmClient::signer(self)
|
||||||
|
.try_derive_accounts()
|
||||||
|
.ok()?;
|
||||||
let account_from_signer = signer_accounts
|
let account_from_signer = signer_accounts
|
||||||
.iter()
|
.iter()
|
||||||
.find(|account| &account.address == signer_address)?;
|
.find(|account| &account.address == signer_address)?;
|
||||||
@@ -69,33 +46,6 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
|
|||||||
Some(public_key.into())
|
Some(public_key.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn simulate(
|
|
||||||
&self,
|
|
||||||
signer_address: &AccountId,
|
|
||||||
messages: Vec<Any>,
|
|
||||||
memo: impl Into<String> + Send + 'static,
|
|
||||||
) -> Result<SimulateResponse, NymdError> {
|
|
||||||
let public_key = self.signer_public_key(signer_address);
|
|
||||||
let sequence_response = self.get_sequence(signer_address).await?;
|
|
||||||
|
|
||||||
let partial_tx = Tx {
|
|
||||||
body: tx::Body::new(messages, memo, 0u32),
|
|
||||||
auth_info: single_unspecified_signer_auth(public_key, sequence_response.sequence),
|
|
||||||
signatures: vec![Vec::new()],
|
|
||||||
};
|
|
||||||
self.query_simulate(Some(partial_tx), Vec::new()).await
|
|
||||||
|
|
||||||
// for completion sake, once we're able to transition into using `tx_bytes`,
|
|
||||||
// we might want to use something like this instead:
|
|
||||||
// let tx_raw: tx::Raw = cosmrs::proto::cosmos::tx::v1beta1::TxRaw {
|
|
||||||
// body_bytes: partial_tx.body.into_bytes().unwrap(),
|
|
||||||
// auth_info_bytes: partial_tx.auth_info.into_bytes().unwrap(),
|
|
||||||
// signatures: partial_tx.signatures,
|
|
||||||
// }
|
|
||||||
// .into();
|
|
||||||
// self.query_simulate(None, tx_raw.to_bytes().unwrap()).await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn upload(
|
async fn upload(
|
||||||
&self,
|
&self,
|
||||||
sender_address: &AccountId,
|
sender_address: &AccountId,
|
||||||
@@ -541,50 +491,6 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
|
|||||||
.check_response()
|
.check_response()
|
||||||
}
|
}
|
||||||
|
|
||||||
// in this particular case we cannot generalise the argument to `&str` due to lifetime constraints
|
|
||||||
#[allow(clippy::ptr_arg)]
|
|
||||||
async fn determine_transaction_fee(
|
|
||||||
&self,
|
|
||||||
signer_address: &AccountId,
|
|
||||||
messages: &[Any],
|
|
||||||
fee: Fee,
|
|
||||||
memo: &String,
|
|
||||||
) -> Result<tx::Fee, NymdError> {
|
|
||||||
let auto_fee = |multiplier: Option<f32>| async move {
|
|
||||||
debug!("Trying to simulate gas costs...");
|
|
||||||
// from what I've seen in manual testing, gas estimation does not exist if transaction
|
|
||||||
// fails to get executed (for example if you send 'BondMixnode" with invalid signature)
|
|
||||||
let gas_estimation = self
|
|
||||||
.simulate(signer_address, messages.to_vec(), memo.clone())
|
|
||||||
.await?
|
|
||||||
.gas_info
|
|
||||||
.ok_or(NymdError::GasEstimationFailure)?
|
|
||||||
.gas_used;
|
|
||||||
|
|
||||||
let multiplier = multiplier.unwrap_or(DEFAULT_SIMULATED_GAS_MULTIPLIER);
|
|
||||||
let gas = gas_estimation.adjust_gas(multiplier);
|
|
||||||
|
|
||||||
debug!("Gas estimation: {}", gas_estimation);
|
|
||||||
debug!("Multiplying the estimation by {}", multiplier);
|
|
||||||
debug!("Final gas limit used: {}", gas);
|
|
||||||
|
|
||||||
let fee = self.gas_price() * gas;
|
|
||||||
Ok::<tx::Fee, NymdError>(tx::Fee::from_amount_and_gas(fee, gas))
|
|
||||||
};
|
|
||||||
let fee = match fee {
|
|
||||||
Fee::Manual(fee) => fee,
|
|
||||||
Fee::Auto(multiplier) => auto_fee(multiplier).await?,
|
|
||||||
Fee::PayerGranterAuto(auto_feegrant) => {
|
|
||||||
let mut fee = auto_fee(auto_feegrant.gas_adjustment).await?;
|
|
||||||
fee.payer = auto_feegrant.payer;
|
|
||||||
fee.granter = auto_feegrant.granter;
|
|
||||||
fee
|
|
||||||
}
|
|
||||||
};
|
|
||||||
debug!("Fee used for the transaction: {:?}", fee);
|
|
||||||
Ok(fee)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Broadcast a transaction, returning immediately.
|
/// Broadcast a transaction, returning immediately.
|
||||||
async fn sign_and_broadcast_async(
|
async fn sign_and_broadcast_async(
|
||||||
&self,
|
&self,
|
||||||
@@ -597,7 +503,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
|
|||||||
let fee = self
|
let fee = self
|
||||||
.determine_transaction_fee(signer_address, &messages, fee, &memo)
|
.determine_transaction_fee(signer_address, &messages, fee, &memo)
|
||||||
.await?;
|
.await?;
|
||||||
let tx_raw = self.sign(signer_address, messages, fee, memo).await?;
|
let tx_raw = SigningCosmWasmClient::sign(self, signer_address, messages, fee, memo).await?;
|
||||||
let tx_bytes = tx_raw
|
let tx_bytes = tx_raw
|
||||||
.to_bytes()
|
.to_bytes()
|
||||||
.map_err(|_| NymdError::SerializationError("Tx".to_owned()))?;
|
.map_err(|_| NymdError::SerializationError("Tx".to_owned()))?;
|
||||||
@@ -617,7 +523,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
|
|||||||
let fee = self
|
let fee = self
|
||||||
.determine_transaction_fee(signer_address, &messages, fee, &memo)
|
.determine_transaction_fee(signer_address, &messages, fee, &memo)
|
||||||
.await?;
|
.await?;
|
||||||
let tx_raw = self.sign(signer_address, messages, fee, memo).await?;
|
let tx_raw = SigningCosmWasmClient::sign(self, signer_address, messages, fee, memo).await?;
|
||||||
let tx_bytes = tx_raw
|
let tx_bytes = tx_raw
|
||||||
.to_bytes()
|
.to_bytes()
|
||||||
.map_err(|_| NymdError::SerializationError("Tx".to_owned()))?;
|
.map_err(|_| NymdError::SerializationError("Tx".to_owned()))?;
|
||||||
@@ -638,7 +544,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
|
|||||||
.determine_transaction_fee(signer_address, &messages, fee, &memo)
|
.determine_transaction_fee(signer_address, &messages, fee, &memo)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let tx_raw = self.sign(signer_address, messages, fee, memo).await?;
|
let tx_raw = SigningCosmWasmClient::sign(self, signer_address, messages, fee, memo).await?;
|
||||||
let tx_bytes = tx_raw
|
let tx_bytes = tx_raw
|
||||||
.to_bytes()
|
.to_bytes()
|
||||||
.map_err(|_| NymdError::SerializationError("Tx".to_owned()))?;
|
.map_err(|_| NymdError::SerializationError("Tx".to_owned()))?;
|
||||||
@@ -659,7 +565,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
|
|||||||
.determine_transaction_fee(signer_address, &messages, fee, &memo)
|
.determine_transaction_fee(signer_address, &messages, fee, &memo)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let tx_raw = self.sign(signer_address, messages, fee, memo).await?;
|
let tx_raw = SigningCosmWasmClient::sign(self, signer_address, messages, fee, memo).await?;
|
||||||
let tx_bytes = tx_raw
|
let tx_bytes = tx_raw
|
||||||
.to_bytes()
|
.to_bytes()
|
||||||
.map_err(|_| NymdError::SerializationError("Tx".to_owned()))?;
|
.map_err(|_| NymdError::SerializationError("Tx".to_owned()))?;
|
||||||
@@ -675,7 +581,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
|
|||||||
memo: impl Into<String> + Send + 'static,
|
memo: impl Into<String> + Send + 'static,
|
||||||
signer_data: SignerData,
|
signer_data: SignerData,
|
||||||
) -> Result<tx::Raw, NymdError> {
|
) -> Result<tx::Raw, NymdError> {
|
||||||
let signer_accounts = self.signer().try_derive_accounts()?;
|
let signer_accounts = SigningCosmWasmClient::signer(self).try_derive_accounts()?;
|
||||||
let account_from_signer = signer_accounts
|
let account_from_signer = signer_accounts
|
||||||
.iter()
|
.iter()
|
||||||
.find(|account| &account.address == signer_address)
|
.find(|account| &account.address == signer_address)
|
||||||
@@ -701,8 +607,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
|
|||||||
)
|
)
|
||||||
.map_err(|_| NymdError::SigningFailure)?;
|
.map_err(|_| NymdError::SigningFailure)?;
|
||||||
|
|
||||||
self.signer()
|
SigningCosmWasmClient::signer(self).sign_direct_with_account(account_from_signer, sign_doc)
|
||||||
.sign_direct_with_account(account_from_signer, sign_doc)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn sign(
|
async fn sign(
|
||||||
@@ -786,7 +691,16 @@ impl CosmWasmClient for Client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
impl MinimalSigningCosmWasmClient for Client {
|
||||||
|
fn signer(&self) -> CosmosLedger {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gas_price(&self) -> &GasPrice {
|
||||||
|
&self.gas_price
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl SigningCosmWasmClient for Client {
|
impl SigningCosmWasmClient for Client {
|
||||||
fn signer(&self) -> &DirectSecp256k1HdWallet {
|
fn signer(&self) -> &DirectSecp256k1HdWallet {
|
||||||
&self.signer
|
&self.signer
|
||||||
|
|||||||
@@ -0,0 +1,490 @@
|
|||||||
|
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use crate::nymd::cosmwasm_client::client::CosmWasmClient;
|
||||||
|
use crate::nymd::cosmwasm_client::helpers::CheckResponse;
|
||||||
|
use crate::nymd::cosmwasm_client::logs::{self, parse_raw_logs};
|
||||||
|
use crate::nymd::cosmwasm_client::types::*;
|
||||||
|
use crate::nymd::cosmwasm_client::HttpClient;
|
||||||
|
use crate::nymd::cosmwasm_client::{DEFAULT_BROADCAST_POLLING_RATE, DEFAULT_BROADCAST_TIMEOUT};
|
||||||
|
use crate::nymd::fee::GasAdjustable;
|
||||||
|
use crate::nymd::wallet_client::rpc::SimpleRequest;
|
||||||
|
use crate::nymd::DEFAULT_SIMULATED_GAS_MULTIPLIER;
|
||||||
|
use crate::nymd::{
|
||||||
|
DirectSecp256k1HdWallet, GasPrice, HttpClientUrl, NymdError, SigningCosmWasmClient,
|
||||||
|
SimulateResponse, TendermintRpcError, TxResponse,
|
||||||
|
};
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use cosmrs::proto::cosmos::tx::signing::v1beta1::SignMode;
|
||||||
|
use cosmrs::rpc;
|
||||||
|
use cosmrs::tx::mode_info::Single;
|
||||||
|
use cosmrs::tx::{self, Gas, ModeInfo, Msg, MsgProto, SignDoc, SignerInfo};
|
||||||
|
use cosmrs::{AccountId, Any};
|
||||||
|
use ledger::CosmosLedger;
|
||||||
|
use log::{debug, info};
|
||||||
|
use mixnet_contract_common::ExecuteMsg;
|
||||||
|
use serde::Serialize;
|
||||||
|
use std::str::FromStr;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
fn single_unspecified_signer_auth(
|
||||||
|
public_key: Option<tx::SignerPublicKey>,
|
||||||
|
sequence_number: tx::SequenceNumber,
|
||||||
|
) -> tx::AuthInfo {
|
||||||
|
tx::SignerInfo {
|
||||||
|
public_key,
|
||||||
|
mode_info: tx::ModeInfo::Single(tx::mode_info::Single {
|
||||||
|
mode: SignMode::Unspecified,
|
||||||
|
}),
|
||||||
|
sequence: sequence_number,
|
||||||
|
}
|
||||||
|
.auth_info(empty_fee())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn empty_fee() -> tx::Fee {
|
||||||
|
tx::Fee {
|
||||||
|
amount: vec![],
|
||||||
|
gas_limit: Default::default(),
|
||||||
|
payer: None,
|
||||||
|
granter: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Client {
|
||||||
|
rpc_client: HttpClient,
|
||||||
|
signer: CosmosLedger,
|
||||||
|
gas_price: GasPrice,
|
||||||
|
|
||||||
|
broadcast_polling_rate: Duration,
|
||||||
|
broadcast_timeout: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Client {
|
||||||
|
pub fn connect_with_ledger<U: Clone>(
|
||||||
|
endpoint: U,
|
||||||
|
signer: CosmosLedger,
|
||||||
|
gas_price: GasPrice,
|
||||||
|
) -> Result<Self, NymdError>
|
||||||
|
where
|
||||||
|
U: TryInto<HttpClientUrl, Error = TendermintRpcError>,
|
||||||
|
{
|
||||||
|
let rpc_client = HttpClient::new(endpoint)?;
|
||||||
|
Ok(Client {
|
||||||
|
rpc_client,
|
||||||
|
signer,
|
||||||
|
gas_price,
|
||||||
|
broadcast_polling_rate: DEFAULT_BROADCAST_POLLING_RATE,
|
||||||
|
broadcast_timeout: DEFAULT_BROADCAST_TIMEOUT,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_broadcast_polling_rate(&mut self, broadcast_polling_rate: Duration) {
|
||||||
|
self.broadcast_polling_rate = broadcast_polling_rate
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_broadcast_timeout(&mut self, broadcast_timeout: Duration) {
|
||||||
|
self.broadcast_timeout = broadcast_timeout
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn signer(&self) -> CosmosLedger {
|
||||||
|
self.signer.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl rpc::Client for Client {
|
||||||
|
async fn perform<R>(&self, request: R) -> Result<R::Response, rpc::Error>
|
||||||
|
where
|
||||||
|
R: SimpleRequest,
|
||||||
|
{
|
||||||
|
self.rpc_client.perform(request).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl CosmWasmClient for Client {
|
||||||
|
fn broadcast_polling_rate(&self) -> Duration {
|
||||||
|
self.broadcast_polling_rate
|
||||||
|
}
|
||||||
|
|
||||||
|
fn broadcast_timeout(&self) -> Duration {
|
||||||
|
self.broadcast_timeout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MinimalSigningCosmWasmClient for Client {
|
||||||
|
fn signer(&self) -> CosmosLedger {
|
||||||
|
self.signer.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gas_price(&self) -> &GasPrice {
|
||||||
|
&self.gas_price
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SigningCosmWasmClient for Client {
|
||||||
|
fn signer(&self) -> &DirectSecp256k1HdWallet {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gas_price(&self) -> &GasPrice {
|
||||||
|
&self.gas_price
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
struct Coin {
|
||||||
|
amount: String,
|
||||||
|
denom: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crate::nymd::Coin> for Coin {
|
||||||
|
fn from(coin: crate::nymd::Coin) -> Self {
|
||||||
|
Coin {
|
||||||
|
amount: coin.amount.to_string(),
|
||||||
|
denom: coin.denom.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<cosmrs::Coin> for Coin {
|
||||||
|
fn from(coin: cosmrs::Coin) -> Self {
|
||||||
|
let nymd_coin: crate::nymd::Coin = coin.into();
|
||||||
|
nymd_coin.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
struct Fee {
|
||||||
|
amount: Vec<Coin>,
|
||||||
|
gas: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
struct SendValue {
|
||||||
|
amount: Vec<Coin>,
|
||||||
|
from_address: String,
|
||||||
|
to_address: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
struct MsgSend {
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
type_url: String,
|
||||||
|
value: SendValue,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
struct SendTransaction {
|
||||||
|
account_number: String,
|
||||||
|
chain_id: String,
|
||||||
|
fee: Fee,
|
||||||
|
memo: String,
|
||||||
|
msgs: Vec<MsgSend>,
|
||||||
|
sequence: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct ExecuteContractValue {
|
||||||
|
contract: String,
|
||||||
|
funds: Vec<Coin>,
|
||||||
|
msg: ExecuteMsg,
|
||||||
|
sender: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct MsgExecuteContract {
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
type_url: String,
|
||||||
|
value: ExecuteContractValue,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct ExecuteContractTransaction {
|
||||||
|
account_number: String,
|
||||||
|
chain_id: String,
|
||||||
|
fee: Fee,
|
||||||
|
memo: String,
|
||||||
|
msgs: Vec<MsgExecuteContract>,
|
||||||
|
sequence: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait MinimalSigningCosmWasmClient: CosmWasmClient {
|
||||||
|
fn signer(&self) -> CosmosLedger;
|
||||||
|
|
||||||
|
fn gas_price(&self) -> &GasPrice;
|
||||||
|
|
||||||
|
fn signer_public_key(&self, signer_address: &AccountId) -> Option<tx::SignerPublicKey> {
|
||||||
|
let response = self.signer().get_addr_secp265k1(false).ok()?;
|
||||||
|
if response.address == signer_address.to_string() {
|
||||||
|
let verifying_key: cosmrs::crypto::secp256k1::VerifyingKey = response.public_key.into();
|
||||||
|
let cosmrs_public_key: cosmrs::crypto::PublicKey = verifying_key.into();
|
||||||
|
Some(cosmrs_public_key.into())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn simulate(
|
||||||
|
&self,
|
||||||
|
signer_address: &AccountId,
|
||||||
|
messages: Vec<Any>,
|
||||||
|
memo: impl Into<String> + Send + 'static,
|
||||||
|
) -> Result<SimulateResponse, NymdError> {
|
||||||
|
let public_key = self.signer_public_key(signer_address);
|
||||||
|
let sequence_response = self.get_sequence(signer_address).await?;
|
||||||
|
|
||||||
|
let partial_tx = cosmrs::Tx {
|
||||||
|
body: tx::Body::new(messages, memo, 0u32),
|
||||||
|
auth_info: single_unspecified_signer_auth(public_key, sequence_response.sequence),
|
||||||
|
signatures: vec![Vec::new()],
|
||||||
|
};
|
||||||
|
self.query_simulate(Some(partial_tx), Vec::new()).await
|
||||||
|
|
||||||
|
// for completion sake, once we're able to transition into using `tx_bytes`,
|
||||||
|
// we might want to use something like this instead:
|
||||||
|
// let tx_raw: tx::Raw = cosmrs::proto::cosmos::tx::v1beta1::TxRaw {
|
||||||
|
// body_bytes: partial_tx.body.into_bytes().unwrap(),
|
||||||
|
// auth_info_bytes: partial_tx.auth_info.into_bytes().unwrap(),
|
||||||
|
// signatures: partial_tx.signatures,
|
||||||
|
// }
|
||||||
|
// .into();
|
||||||
|
// self.query_simulate(None, tx_raw.to_bytes().unwrap()).await
|
||||||
|
}
|
||||||
|
|
||||||
|
// in this particular case we cannot generalise the argument to `&str` due to lifetime constraints
|
||||||
|
#[allow(clippy::ptr_arg)]
|
||||||
|
async fn determine_transaction_fee(
|
||||||
|
&self,
|
||||||
|
signer_address: &AccountId,
|
||||||
|
messages: &[Any],
|
||||||
|
fee: crate::nymd::fee::Fee,
|
||||||
|
memo: &String,
|
||||||
|
) -> Result<tx::Fee, NymdError> {
|
||||||
|
let auto_fee = |multiplier: Option<f32>| async move {
|
||||||
|
debug!("Trying to simulate gas costs...");
|
||||||
|
// from what I've seen in manual testing, gas estimation does not exist if transaction
|
||||||
|
// fails to get executed (for example if you send 'BondMixnode" with invalid signature)
|
||||||
|
let gas_estimation = self
|
||||||
|
.simulate(signer_address, messages.to_vec(), memo.clone())
|
||||||
|
.await?
|
||||||
|
.gas_info
|
||||||
|
.ok_or(NymdError::GasEstimationFailure)?
|
||||||
|
.gas_used;
|
||||||
|
|
||||||
|
let multiplier = multiplier.unwrap_or(DEFAULT_SIMULATED_GAS_MULTIPLIER);
|
||||||
|
let gas = gas_estimation.adjust_gas(multiplier);
|
||||||
|
|
||||||
|
debug!("Gas estimation: {}", gas_estimation);
|
||||||
|
debug!("Multiplying the estimation by {}", multiplier);
|
||||||
|
debug!("Final gas limit used: {}", gas);
|
||||||
|
|
||||||
|
let fee = self.gas_price() * gas;
|
||||||
|
Ok::<tx::Fee, NymdError>(tx::Fee::from_amount_and_gas(fee, gas))
|
||||||
|
};
|
||||||
|
let fee = match fee {
|
||||||
|
crate::nymd::fee::Fee::Manual(fee) => fee,
|
||||||
|
crate::nymd::fee::Fee::Auto(multiplier) => auto_fee(multiplier).await?,
|
||||||
|
crate::nymd::fee::Fee::PayerGranterAuto(auto_feegrant) => {
|
||||||
|
let mut fee = auto_fee(auto_feegrant.gas_adjustment).await?;
|
||||||
|
fee.payer = auto_feegrant.payer;
|
||||||
|
fee.granter = auto_feegrant.granter;
|
||||||
|
fee
|
||||||
|
}
|
||||||
|
};
|
||||||
|
debug!("Fee used for the transaction: {:?}", fee);
|
||||||
|
Ok(fee)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sign(
|
||||||
|
&self,
|
||||||
|
messages: String,
|
||||||
|
cosmrs_messages: Vec<Any>,
|
||||||
|
fee: tx::Fee,
|
||||||
|
memo: impl Into<String> + Send + 'static,
|
||||||
|
signer_data: SignerData,
|
||||||
|
) -> Result<tx::Raw, NymdError> {
|
||||||
|
let signature = self
|
||||||
|
.signer()
|
||||||
|
.sign_secp265k1(messages)
|
||||||
|
.expect("Could not sign")
|
||||||
|
.signature;
|
||||||
|
let response = self.signer().get_addr_secp265k1(false)?;
|
||||||
|
let verifying_key: cosmrs::crypto::secp256k1::VerifyingKey = response.public_key.into();
|
||||||
|
let cosmrs_public_key: cosmrs::crypto::PublicKey = verifying_key.into();
|
||||||
|
|
||||||
|
// TODO: WTF HOW IS TIMEOUT_HEIGHT SUPPOSED TO GET DETERMINED?
|
||||||
|
// IT DOESNT EXIST IN COSMJS!!
|
||||||
|
// try to set to 0
|
||||||
|
let timeout_height = 0u32;
|
||||||
|
|
||||||
|
let tx_body = tx::Body::new(cosmrs_messages, memo, timeout_height);
|
||||||
|
let mut signer_info =
|
||||||
|
SignerInfo::single_direct(Some(cosmrs_public_key), signer_data.sequence);
|
||||||
|
signer_info.mode_info = ModeInfo::Single(Single {
|
||||||
|
mode: SignMode::LegacyAminoJson,
|
||||||
|
});
|
||||||
|
let auth_info = signer_info.auth_info(fee);
|
||||||
|
|
||||||
|
// ideally I'd prefer to have the entire error put into the NymdError::SigningFailure
|
||||||
|
// but I'm super hesitant to trying to downcast the eyre::Report to cosmrs::error::Error
|
||||||
|
let sign_doc = SignDoc::new(
|
||||||
|
&tx_body,
|
||||||
|
&auth_info,
|
||||||
|
&signer_data.chain_id,
|
||||||
|
signer_data.account_number,
|
||||||
|
)
|
||||||
|
.map_err(|_| NymdError::SigningFailure)?;
|
||||||
|
|
||||||
|
Ok(cosmrs::proto::cosmos::tx::v1beta1::TxRaw {
|
||||||
|
body_bytes: sign_doc.body_bytes,
|
||||||
|
auth_info_bytes: sign_doc.auth_info_bytes,
|
||||||
|
signatures: vec![signature.as_ref().to_vec()],
|
||||||
|
}
|
||||||
|
.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn send_tokens(
|
||||||
|
&self,
|
||||||
|
sender_address: &AccountId,
|
||||||
|
recipient_address: &AccountId,
|
||||||
|
amount: Vec<crate::nymd::Coin>,
|
||||||
|
fee: crate::nymd::fee::Fee,
|
||||||
|
memo: impl Into<String> + Send + 'static,
|
||||||
|
) -> Result<TxResponse, NymdError> {
|
||||||
|
let cosmrs_msg = cosmrs::bank::MsgSend {
|
||||||
|
from_address: sender_address.clone(),
|
||||||
|
to_address: recipient_address.clone(),
|
||||||
|
amount: amount.iter().cloned().map(Into::into).collect(),
|
||||||
|
}
|
||||||
|
.to_any()
|
||||||
|
.map_err(|_| NymdError::SerializationError("MsgSend".to_owned()))?;
|
||||||
|
let memo = memo.into();
|
||||||
|
let fee = self
|
||||||
|
.determine_transaction_fee(sender_address, &vec![cosmrs_msg.clone()], fee, &memo)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let response = self.signer().get_addr_secp265k1(false)?;
|
||||||
|
let sequence_response = self
|
||||||
|
.get_sequence(&AccountId::from_str(&response.address).unwrap())
|
||||||
|
.await?;
|
||||||
|
let chain_id = self.get_chain_id().await?;
|
||||||
|
|
||||||
|
let signer_data = SignerData {
|
||||||
|
account_number: sequence_response.account_number,
|
||||||
|
sequence: sequence_response.sequence,
|
||||||
|
chain_id,
|
||||||
|
};
|
||||||
|
|
||||||
|
let send_msg = MsgSend {
|
||||||
|
type_url: String::from("cosmos-sdk/MsgSend"),
|
||||||
|
value: SendValue {
|
||||||
|
from_address: sender_address.to_string(),
|
||||||
|
to_address: recipient_address.to_string(),
|
||||||
|
amount: amount.iter().cloned().map(Into::into).collect(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let tx = SendTransaction {
|
||||||
|
account_number: signer_data.account_number.to_string(),
|
||||||
|
chain_id: signer_data.chain_id.to_string(),
|
||||||
|
fee: Fee {
|
||||||
|
amount: fee.amount.iter().cloned().map(Into::into).collect(),
|
||||||
|
gas: fee.gas_limit.to_string(),
|
||||||
|
},
|
||||||
|
memo: memo.clone(),
|
||||||
|
msgs: vec![send_msg],
|
||||||
|
sequence: signer_data.sequence.to_string(),
|
||||||
|
};
|
||||||
|
let tx_bytes = self
|
||||||
|
.sign(
|
||||||
|
serde_json::to_string(&tx).unwrap(),
|
||||||
|
vec![cosmrs_msg],
|
||||||
|
fee,
|
||||||
|
memo,
|
||||||
|
signer_data,
|
||||||
|
)?
|
||||||
|
.to_bytes()
|
||||||
|
.map_err(|_| NymdError::SerializationError("Tx".to_owned()))?;
|
||||||
|
|
||||||
|
self.broadcast_tx(tx_bytes.into()).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn wallet_execute(
|
||||||
|
&self,
|
||||||
|
sender_address: &AccountId,
|
||||||
|
contract_address: &AccountId,
|
||||||
|
msg: &ExecuteMsg,
|
||||||
|
fee: crate::nymd::fee::Fee,
|
||||||
|
memo: impl Into<String> + Send + 'static,
|
||||||
|
funds: Vec<crate::nymd::Coin>,
|
||||||
|
) -> Result<ExecuteResult, NymdError> {
|
||||||
|
let cosmrs_msg = cosmrs::cosmwasm::MsgExecuteContract {
|
||||||
|
sender: sender_address.clone(),
|
||||||
|
contract: contract_address.clone(),
|
||||||
|
msg: serde_json::to_vec(msg)?,
|
||||||
|
funds: funds.iter().cloned().map(Into::into).collect(),
|
||||||
|
}
|
||||||
|
.to_any()
|
||||||
|
.map_err(|_| NymdError::SerializationError("MsgExecuteContract".to_owned()))?;
|
||||||
|
let memo = memo.into();
|
||||||
|
let fee = self
|
||||||
|
.determine_transaction_fee(sender_address, &vec![cosmrs_msg.clone()], fee, &memo)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let response = self.signer().get_addr_secp265k1(false)?;
|
||||||
|
let sequence_response = self
|
||||||
|
.get_sequence(&AccountId::from_str(&response.address).unwrap())
|
||||||
|
.await?;
|
||||||
|
let chain_id = self.get_chain_id().await?;
|
||||||
|
|
||||||
|
let signer_data = SignerData {
|
||||||
|
account_number: sequence_response.account_number,
|
||||||
|
sequence: sequence_response.sequence,
|
||||||
|
chain_id,
|
||||||
|
};
|
||||||
|
|
||||||
|
let execute_contract_msg = MsgExecuteContract {
|
||||||
|
type_url: String::from("wasm/MsgExecuteContract"),
|
||||||
|
value: ExecuteContractValue {
|
||||||
|
contract: contract_address.to_string(),
|
||||||
|
funds: funds.iter().cloned().map(Into::into).collect(),
|
||||||
|
msg: msg.clone(),
|
||||||
|
sender: sender_address.to_string(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let tx = ExecuteContractTransaction {
|
||||||
|
account_number: signer_data.account_number.to_string(),
|
||||||
|
chain_id: signer_data.chain_id.to_string(),
|
||||||
|
fee: Fee {
|
||||||
|
amount: fee.amount.iter().cloned().map(Into::into).collect(),
|
||||||
|
gas: fee.gas_limit.to_string(),
|
||||||
|
},
|
||||||
|
memo: memo.clone(),
|
||||||
|
msgs: vec![execute_contract_msg],
|
||||||
|
sequence: signer_data.sequence.to_string(),
|
||||||
|
};
|
||||||
|
let tx_bytes = self
|
||||||
|
.sign(
|
||||||
|
serde_json::to_string(&tx).unwrap(),
|
||||||
|
vec![cosmrs_msg],
|
||||||
|
fee,
|
||||||
|
memo,
|
||||||
|
signer_data,
|
||||||
|
)?
|
||||||
|
.to_bytes()
|
||||||
|
.map_err(|_| NymdError::SerializationError("Tx".to_owned()))?;
|
||||||
|
|
||||||
|
let tx_res = self.broadcast_tx(tx_bytes.into()).await?.check_response()?;
|
||||||
|
|
||||||
|
let gas_info = GasInfo::new(tx_res.tx_result.gas_wanted, tx_res.tx_result.gas_used);
|
||||||
|
|
||||||
|
Ok(ExecuteResult {
|
||||||
|
logs: parse_raw_logs(tx_res.tx_result.log)?,
|
||||||
|
data: tx_res.tx_result.data,
|
||||||
|
transaction_hash: tx_res.hash,
|
||||||
|
gas_info,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -133,6 +133,9 @@ pub enum NymdError {
|
|||||||
|
|
||||||
#[error("Account had an unexpected bech32 prefix. Expected: {expected}, got: {got}")]
|
#[error("Account had an unexpected bech32 prefix. Expected: {expected}, got: {got}")]
|
||||||
UnexpectedBech32Prefix { got: String, expected: String },
|
UnexpectedBech32Prefix { got: String, expected: String },
|
||||||
|
|
||||||
|
#[error("Ledger error: {0}")]
|
||||||
|
LedgerError(#[from] ledger::error::LedgerError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NymdError {
|
impl NymdError {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,387 +0,0 @@
|
|||||||
// 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,496 +0,0 @@
|
|||||||
// 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,10 +1,8 @@
|
|||||||
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
mod coconut_bandwidth_query_client;
|
mod coconut_bandwidth_query_client;
|
||||||
mod coconut_bandwidth_signing_client;
|
mod coconut_bandwidth_signing_client;
|
||||||
mod mixnet_query_client;
|
|
||||||
mod mixnet_signing_client;
|
|
||||||
mod multisig_query_client;
|
mod multisig_query_client;
|
||||||
mod multisig_signing_client;
|
mod multisig_signing_client;
|
||||||
mod vesting_query_client;
|
mod vesting_query_client;
|
||||||
@@ -12,8 +10,6 @@ mod vesting_signing_client;
|
|||||||
|
|
||||||
pub use coconut_bandwidth_query_client::CoconutBandwidthQueryClient;
|
pub use coconut_bandwidth_query_client::CoconutBandwidthQueryClient;
|
||||||
pub use coconut_bandwidth_signing_client::CoconutBandwidthSigningClient;
|
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_query_client::MultisigQueryClient;
|
||||||
pub use multisig_signing_client::MultisigSigningClient;
|
pub use multisig_signing_client::MultisigSigningClient;
|
||||||
pub use vesting_query_client::VestingQueryClient;
|
pub use vesting_query_client::VestingQueryClient;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use crate::nymd::error::NymdError;
|
|||||||
use crate::nymd::NymdClient;
|
use crate::nymd::NymdClient;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use cosmwasm_std::{Coin as CosmWasmCoin, Timestamp};
|
use cosmwasm_std::{Coin as CosmWasmCoin, Timestamp};
|
||||||
use mixnet_contract_common::NodeId;
|
use mixnet_contract_common::IdentityKey;
|
||||||
use vesting_contract::vesting::Account;
|
use vesting_contract::vesting::Account;
|
||||||
use vesting_contract_common::{
|
use vesting_contract_common::{
|
||||||
messages::QueryMsg as VestingQueryMsg, AllDelegationsResponse, DelegationTimesResponse,
|
messages::QueryMsg as VestingQueryMsg, AllDelegationsResponse, DelegationTimesResponse,
|
||||||
@@ -76,12 +76,12 @@ pub trait VestingQueryClient {
|
|||||||
async fn get_delegation_timestamps(
|
async fn get_delegation_timestamps(
|
||||||
&self,
|
&self,
|
||||||
address: &str,
|
address: &str,
|
||||||
mix_id: NodeId,
|
mix_identity: String,
|
||||||
) -> Result<DelegationTimesResponse, NymdError>;
|
) -> Result<DelegationTimesResponse, NymdError>;
|
||||||
|
|
||||||
async fn get_all_vesting_delegations_paged(
|
async fn get_all_vesting_delegations_paged(
|
||||||
&self,
|
&self,
|
||||||
start_after: Option<(u32, NodeId, u64)>,
|
start_after: Option<(u32, IdentityKey, u64)>,
|
||||||
limit: Option<u32>,
|
limit: Option<u32>,
|
||||||
) -> Result<AllDelegationsResponse, NymdError>;
|
) -> Result<AllDelegationsResponse, NymdError>;
|
||||||
|
|
||||||
@@ -269,11 +269,11 @@ impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NymdClient<C> {
|
|||||||
async fn get_delegation_timestamps(
|
async fn get_delegation_timestamps(
|
||||||
&self,
|
&self,
|
||||||
address: &str,
|
address: &str,
|
||||||
mix_id: NodeId,
|
mix_identity: String,
|
||||||
) -> Result<DelegationTimesResponse, NymdError> {
|
) -> Result<DelegationTimesResponse, NymdError> {
|
||||||
let request = VestingQueryMsg::GetDelegationTimes {
|
let request = VestingQueryMsg::GetDelegationTimes {
|
||||||
address: address.to_string(),
|
address: address.to_string(),
|
||||||
mix_id,
|
mix_identity,
|
||||||
};
|
};
|
||||||
self.client
|
self.client
|
||||||
.query_contract_smart(self.vesting_contract_address(), &request)
|
.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(
|
async fn get_all_vesting_delegations_paged(
|
||||||
&self,
|
&self,
|
||||||
start_after: Option<(u32, NodeId, u64)>,
|
start_after: Option<(u32, IdentityKey, u64)>,
|
||||||
limit: Option<u32>,
|
limit: Option<u32>,
|
||||||
) -> Result<AllDelegationsResponse, NymdError> {
|
) -> Result<AllDelegationsResponse, NymdError> {
|
||||||
let request = VestingQueryMsg::GetAllDelegations { start_after, limit };
|
let request = VestingQueryMsg::GetAllDelegations { start_after, limit };
|
||||||
|
|||||||
@@ -6,28 +6,14 @@ use crate::nymd::cosmwasm_client::types::ExecuteResult;
|
|||||||
use crate::nymd::error::NymdError;
|
use crate::nymd::error::NymdError;
|
||||||
use crate::nymd::{Coin, Fee, NymdClient};
|
use crate::nymd::{Coin, Fee, NymdClient};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use mixnet_contract_common::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
|
use mixnet_contract_common::{Gateway, IdentityKey, IdentityKeyRef, MixNode};
|
||||||
use mixnet_contract_common::{Gateway, MixNode, NodeId};
|
|
||||||
use vesting_contract_common::messages::{ExecuteMsg as VestingExecuteMsg, VestingSpecification};
|
use vesting_contract_common::messages::{ExecuteMsg as VestingExecuteMsg, VestingSpecification};
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait VestingSigningClient {
|
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(
|
async fn vesting_update_mixnode_config(
|
||||||
&self,
|
&self,
|
||||||
new_config: MixNodeConfigUpdate,
|
profix_margin_percent: u8,
|
||||||
fee: Option<Fee>,
|
fee: Option<Fee>,
|
||||||
) -> Result<ExecuteResult, NymdError>;
|
) -> Result<ExecuteResult, NymdError>;
|
||||||
|
|
||||||
@@ -57,12 +43,10 @@ pub trait VestingSigningClient {
|
|||||||
async fn vesting_bond_mixnode(
|
async fn vesting_bond_mixnode(
|
||||||
&self,
|
&self,
|
||||||
mix_node: MixNode,
|
mix_node: MixNode,
|
||||||
cost_params: MixNodeCostParams,
|
|
||||||
owner_signature: &str,
|
owner_signature: &str,
|
||||||
pledge: Coin,
|
pledge: Coin,
|
||||||
fee: Option<Fee>,
|
fee: Option<Fee>,
|
||||||
) -> Result<ExecuteResult, NymdError>;
|
) -> Result<ExecuteResult, NymdError>;
|
||||||
|
|
||||||
async fn vesting_unbond_mixnode(&self, fee: Option<Fee>) -> Result<ExecuteResult, NymdError>;
|
async fn vesting_unbond_mixnode(&self, fee: Option<Fee>) -> Result<ExecuteResult, NymdError>;
|
||||||
|
|
||||||
async fn vesting_track_unbond_mixnode(
|
async fn vesting_track_unbond_mixnode(
|
||||||
@@ -81,21 +65,21 @@ pub trait VestingSigningClient {
|
|||||||
async fn vesting_track_undelegation(
|
async fn vesting_track_undelegation(
|
||||||
&self,
|
&self,
|
||||||
address: &str,
|
address: &str,
|
||||||
mix_id: NodeId,
|
mix_identity: IdentityKey,
|
||||||
amount: Coin,
|
amount: Coin,
|
||||||
fee: Option<Fee>,
|
fee: Option<Fee>,
|
||||||
) -> Result<ExecuteResult, NymdError>;
|
) -> Result<ExecuteResult, NymdError>;
|
||||||
|
|
||||||
async fn vesting_delegate_to_mixnode(
|
async fn vesting_delegate_to_mixnode<'a>(
|
||||||
&self,
|
&self,
|
||||||
mix_id: NodeId,
|
mix_identity: IdentityKeyRef<'a>,
|
||||||
amount: Coin,
|
amount: Coin,
|
||||||
fee: Option<Fee>,
|
fee: Option<Fee>,
|
||||||
) -> Result<ExecuteResult, NymdError>;
|
) -> Result<ExecuteResult, NymdError>;
|
||||||
|
|
||||||
async fn vesting_undelegate_from_mixnode(
|
async fn vesting_undelegate_from_mixnode<'a>(
|
||||||
&self,
|
&self,
|
||||||
mix_id: NodeId,
|
mix_identity: IdentityKeyRef<'a>,
|
||||||
fee: Option<Fee>,
|
fee: Option<Fee>,
|
||||||
) -> Result<ExecuteResult, NymdError>;
|
) -> Result<ExecuteResult, NymdError>;
|
||||||
|
|
||||||
@@ -111,46 +95,15 @@ pub trait VestingSigningClient {
|
|||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient<C> {
|
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(
|
async fn vesting_update_mixnode_config(
|
||||||
&self,
|
&self,
|
||||||
new_config: MixNodeConfigUpdate,
|
profit_margin_percent: u8,
|
||||||
fee: Option<Fee>,
|
fee: Option<Fee>,
|
||||||
) -> Result<ExecuteResult, NymdError> {
|
) -> Result<ExecuteResult, NymdError> {
|
||||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
||||||
let req = VestingExecuteMsg::UpdateMixnodeConfig { new_config };
|
let req = VestingExecuteMsg::UpdateMixnodeConfig {
|
||||||
|
profit_margin_percent,
|
||||||
|
};
|
||||||
self.client
|
self.client
|
||||||
.execute(
|
.execute(
|
||||||
self.address(),
|
self.address(),
|
||||||
@@ -250,22 +203,26 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
|
|||||||
async fn vesting_bond_mixnode(
|
async fn vesting_bond_mixnode(
|
||||||
&self,
|
&self,
|
||||||
mix_node: MixNode,
|
mix_node: MixNode,
|
||||||
cost_params: MixNodeCostParams,
|
|
||||||
owner_signature: &str,
|
owner_signature: &str,
|
||||||
pledge: Coin,
|
pledge: Coin,
|
||||||
fee: Option<Fee>,
|
fee: Option<Fee>,
|
||||||
) -> Result<ExecuteResult, NymdError> {
|
) -> Result<ExecuteResult, NymdError> {
|
||||||
self.execute_vesting_contract(
|
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
||||||
fee,
|
let req = VestingExecuteMsg::BondMixnode {
|
||||||
VestingExecuteMsg::BondMixnode {
|
mix_node,
|
||||||
mix_node,
|
owner_signature: owner_signature.to_string(),
|
||||||
cost_params,
|
amount: pledge.into(),
|
||||||
owner_signature: owner_signature.to_string(),
|
};
|
||||||
amount: pledge.into(),
|
self.client
|
||||||
},
|
.execute(
|
||||||
vec![],
|
self.address(),
|
||||||
)
|
self.vesting_contract_address(),
|
||||||
.await
|
&req,
|
||||||
|
fee,
|
||||||
|
"VestingContract::BondMixnode",
|
||||||
|
vec![],
|
||||||
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn vesting_unbond_mixnode(&self, fee: Option<Fee>) -> Result<ExecuteResult, NymdError> {
|
async fn vesting_unbond_mixnode(&self, fee: Option<Fee>) -> Result<ExecuteResult, NymdError> {
|
||||||
@@ -305,7 +262,6 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
|
|||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn withdraw_vested_coins(
|
async fn withdraw_vested_coins(
|
||||||
&self,
|
&self,
|
||||||
amount: Coin,
|
amount: Coin,
|
||||||
@@ -326,54 +282,72 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
|
|||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn vesting_track_undelegation(
|
async fn vesting_track_undelegation(
|
||||||
&self,
|
&self,
|
||||||
address: &str,
|
address: &str,
|
||||||
mix_id: NodeId,
|
mix_identity: IdentityKey,
|
||||||
amount: Coin,
|
amount: Coin,
|
||||||
fee: Option<Fee>,
|
fee: Option<Fee>,
|
||||||
) -> Result<ExecuteResult, NymdError> {
|
) -> Result<ExecuteResult, NymdError> {
|
||||||
self.execute_vesting_contract(
|
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
||||||
fee,
|
let req = VestingExecuteMsg::TrackUndelegation {
|
||||||
VestingExecuteMsg::TrackUndelegation {
|
owner: address.to_string(),
|
||||||
owner: address.to_string(),
|
mix_identity,
|
||||||
mix_id,
|
amount: amount.into(),
|
||||||
amount: amount.into(),
|
};
|
||||||
},
|
self.client
|
||||||
vec![],
|
.execute(
|
||||||
)
|
self.address(),
|
||||||
.await
|
self.vesting_contract_address(),
|
||||||
|
&req,
|
||||||
|
fee,
|
||||||
|
"VestingContract::TrackUndelegation",
|
||||||
|
vec![],
|
||||||
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
async fn vesting_delegate_to_mixnode<'a>(
|
||||||
async fn vesting_delegate_to_mixnode(
|
|
||||||
&self,
|
&self,
|
||||||
mix_id: NodeId,
|
mix_identity: IdentityKeyRef<'a>,
|
||||||
amount: Coin,
|
amount: Coin,
|
||||||
fee: Option<Fee>,
|
fee: Option<Fee>,
|
||||||
) -> Result<ExecuteResult, NymdError> {
|
) -> Result<ExecuteResult, NymdError> {
|
||||||
self.execute_vesting_contract(
|
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
||||||
fee,
|
let req = VestingExecuteMsg::DelegateToMixnode {
|
||||||
VestingExecuteMsg::DelegateToMixnode {
|
mix_identity: mix_identity.into(),
|
||||||
mix_id,
|
amount: amount.into(),
|
||||||
amount: amount.into(),
|
};
|
||||||
},
|
self.client
|
||||||
vec![],
|
.execute(
|
||||||
)
|
self.address(),
|
||||||
.await
|
self.vesting_contract_address(),
|
||||||
|
&req,
|
||||||
|
fee,
|
||||||
|
"VestingContract::DelegateToMixnode",
|
||||||
|
vec![],
|
||||||
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn vesting_undelegate_from_mixnode(
|
async fn vesting_undelegate_from_mixnode<'a>(
|
||||||
&self,
|
&self,
|
||||||
mix_id: NodeId,
|
mix_identity: IdentityKeyRef<'a>,
|
||||||
fee: Option<Fee>,
|
fee: Option<Fee>,
|
||||||
) -> Result<ExecuteResult, NymdError> {
|
) -> Result<ExecuteResult, NymdError> {
|
||||||
self.execute_vesting_contract(
|
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
||||||
fee,
|
let req = VestingExecuteMsg::UndelegateFromMixnode {
|
||||||
VestingExecuteMsg::UndelegateFromMixnode { mix_id },
|
mix_identity: mix_identity.into(),
|
||||||
vec![],
|
};
|
||||||
)
|
self.client
|
||||||
.await
|
.execute(
|
||||||
|
self.address(),
|
||||||
|
self.vesting_contract_address(),
|
||||||
|
&req,
|
||||||
|
fee,
|
||||||
|
"VestingContract::UndelegateFromMixnode",
|
||||||
|
vec![],
|
||||||
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn create_periodic_vesting_account(
|
async fn create_periodic_vesting_account(
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ impl DirectSecp256k1HdWallet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn derive_keypair(&self, hd_path: &DerivationPath) -> Result<Secp256k1Keypair, NymdError> {
|
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 private_key: SigningKey = extended_private_key.into();
|
||||||
let public_key = private_key.public_key();
|
let public_key = private_key.public_key();
|
||||||
@@ -207,9 +207,8 @@ impl DirectSecp256k1HdWalletBuilder {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use network_defaults::NymNetworkDetails;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use network_defaults::all::Network::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn generating_account_addresses() {
|
fn generating_account_addresses() {
|
||||||
@@ -219,9 +218,7 @@ 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",
|
"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"
|
"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 = NymNetworkDetails::new_mainnet()
|
let prefix = MAINNET.bech32_prefix();
|
||||||
.chain_details
|
|
||||||
.bech32_account_prefix;
|
|
||||||
|
|
||||||
let addrs = vec![
|
let addrs = vec![
|
||||||
"n1jw6mp7d5xqc7w6xm79lha27glmd0vdt3l9artf",
|
"n1jw6mp7d5xqc7w6xm79lha27glmd0vdt3l9artf",
|
||||||
|
|||||||
@@ -3,18 +3,17 @@
|
|||||||
|
|
||||||
use crate::validator_api::error::ValidatorAPIError;
|
use crate::validator_api::error::ValidatorAPIError;
|
||||||
use crate::validator_api::routes::{CORE_STATUS_COUNT, SINCE_ARG};
|
use crate::validator_api::routes::{CORE_STATUS_COUNT, SINCE_ARG};
|
||||||
use mixnet_contract_common::mixnode::MixNodeDetails;
|
use mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixNodeBond};
|
||||||
use mixnet_contract_common::{GatewayBond, IdentityKeyRef, NodeId};
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::collections::HashMap;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use validator_api_requests::coconut::{
|
use validator_api_requests::coconut::{
|
||||||
BlindSignRequestBody, BlindedSignatureResponse, CosmosAddressResponse, VerificationKeyResponse,
|
BlindSignRequestBody, BlindedSignatureResponse, CosmosAddressResponse, VerificationKeyResponse,
|
||||||
VerifyCredentialBody, VerifyCredentialResponse,
|
VerifyCredentialBody, VerifyCredentialResponse,
|
||||||
};
|
};
|
||||||
use validator_api_requests::models::{
|
use validator_api_requests::models::{
|
||||||
GatewayCoreStatusResponse, InclusionProbabilityResponse, MixNodeBondAnnotated,
|
CoreNodeStatusResponse, InclusionProbabilityResponse, MixNodeBondAnnotated,
|
||||||
MixnodeCoreStatusResponse, MixnodeStatusResponse, RewardEstimationResponse,
|
MixnodeStatusResponse, RewardEstimationResponse, StakeSaturationResponse, UptimeResponse,
|
||||||
StakeSaturationResponse, UptimeResponse,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
@@ -85,7 +84,7 @@ impl Client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_mixnodes(&self) -> Result<Vec<MixNodeDetails>, ValidatorAPIError> {
|
pub async fn get_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorAPIError> {
|
||||||
self.query_validator_api(&[routes::API_VERSION, routes::MIXNODES], NO_PARAMS)
|
self.query_validator_api(&[routes::API_VERSION, routes::MIXNODES], NO_PARAMS)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@@ -105,7 +104,7 @@ impl Client {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_active_mixnodes(&self) -> Result<Vec<MixNodeDetails>, ValidatorAPIError> {
|
pub async fn get_active_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorAPIError> {
|
||||||
self.query_validator_api(
|
self.query_validator_api(
|
||||||
&[routes::API_VERSION, routes::MIXNODES, routes::ACTIVE],
|
&[routes::API_VERSION, routes::MIXNODES, routes::ACTIVE],
|
||||||
NO_PARAMS,
|
NO_PARAMS,
|
||||||
@@ -128,7 +127,7 @@ impl Client {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_rewarded_mixnodes(&self) -> Result<Vec<MixNodeDetails>, ValidatorAPIError> {
|
pub async fn get_rewarded_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorAPIError> {
|
||||||
self.query_validator_api(
|
self.query_validator_api(
|
||||||
&[routes::API_VERSION, routes::MIXNODES, routes::REWARDED],
|
&[routes::API_VERSION, routes::MIXNODES, routes::REWARDED],
|
||||||
NO_PARAMS,
|
NO_PARAMS,
|
||||||
@@ -151,11 +150,28 @@ impl Client {
|
|||||||
.await
|
.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(
|
pub async fn get_gateway_core_status_count(
|
||||||
&self,
|
&self,
|
||||||
identity: IdentityKeyRef<'_>,
|
identity: IdentityKeyRef<'_>,
|
||||||
since: Option<i64>,
|
since: Option<i64>,
|
||||||
) -> Result<GatewayCoreStatusResponse, ValidatorAPIError> {
|
) -> Result<CoreNodeStatusResponse, ValidatorAPIError> {
|
||||||
if let Some(since) = since {
|
if let Some(since) = since {
|
||||||
self.query_validator_api(
|
self.query_validator_api(
|
||||||
&[
|
&[
|
||||||
@@ -184,16 +200,16 @@ impl Client {
|
|||||||
|
|
||||||
pub async fn get_mixnode_core_status_count(
|
pub async fn get_mixnode_core_status_count(
|
||||||
&self,
|
&self,
|
||||||
mix_id: NodeId,
|
identity: IdentityKeyRef<'_>,
|
||||||
since: Option<i64>,
|
since: Option<i64>,
|
||||||
) -> Result<MixnodeCoreStatusResponse, ValidatorAPIError> {
|
) -> Result<CoreNodeStatusResponse, ValidatorAPIError> {
|
||||||
if let Some(since) = since {
|
if let Some(since) = since {
|
||||||
self.query_validator_api(
|
self.query_validator_api(
|
||||||
&[
|
&[
|
||||||
routes::API_VERSION,
|
routes::API_VERSION,
|
||||||
routes::STATUS_ROUTES,
|
routes::STATUS_ROUTES,
|
||||||
routes::MIXNODE,
|
routes::MIXNODE,
|
||||||
&mix_id.to_string(),
|
identity,
|
||||||
CORE_STATUS_COUNT,
|
CORE_STATUS_COUNT,
|
||||||
],
|
],
|
||||||
&[(SINCE_ARG, since.to_string())],
|
&[(SINCE_ARG, since.to_string())],
|
||||||
@@ -205,8 +221,7 @@ impl Client {
|
|||||||
routes::API_VERSION,
|
routes::API_VERSION,
|
||||||
routes::STATUS_ROUTES,
|
routes::STATUS_ROUTES,
|
||||||
routes::MIXNODE,
|
routes::MIXNODE,
|
||||||
&mix_id.to_string(),
|
identity,
|
||||||
CORE_STATUS_COUNT,
|
|
||||||
],
|
],
|
||||||
NO_PARAMS,
|
NO_PARAMS,
|
||||||
)
|
)
|
||||||
@@ -216,14 +231,14 @@ impl Client {
|
|||||||
|
|
||||||
pub async fn get_mixnode_status(
|
pub async fn get_mixnode_status(
|
||||||
&self,
|
&self,
|
||||||
mix_id: NodeId,
|
identity: IdentityKeyRef<'_>,
|
||||||
) -> Result<MixnodeStatusResponse, ValidatorAPIError> {
|
) -> Result<MixnodeStatusResponse, ValidatorAPIError> {
|
||||||
self.query_validator_api(
|
self.query_validator_api(
|
||||||
&[
|
&[
|
||||||
routes::API_VERSION,
|
routes::API_VERSION,
|
||||||
routes::STATUS_ROUTES,
|
routes::STATUS_ROUTES,
|
||||||
routes::MIXNODE,
|
routes::MIXNODE,
|
||||||
&mix_id.to_string(),
|
identity,
|
||||||
routes::STATUS,
|
routes::STATUS,
|
||||||
],
|
],
|
||||||
NO_PARAMS,
|
NO_PARAMS,
|
||||||
@@ -233,14 +248,14 @@ impl Client {
|
|||||||
|
|
||||||
pub async fn get_mixnode_reward_estimation(
|
pub async fn get_mixnode_reward_estimation(
|
||||||
&self,
|
&self,
|
||||||
mix_id: NodeId,
|
identity: IdentityKeyRef<'_>,
|
||||||
) -> Result<RewardEstimationResponse, ValidatorAPIError> {
|
) -> Result<RewardEstimationResponse, ValidatorAPIError> {
|
||||||
self.query_validator_api(
|
self.query_validator_api(
|
||||||
&[
|
&[
|
||||||
routes::API_VERSION,
|
routes::API_VERSION,
|
||||||
routes::STATUS_ROUTES,
|
routes::STATUS_ROUTES,
|
||||||
routes::MIXNODE,
|
routes::MIXNODE,
|
||||||
&mix_id.to_string(),
|
identity,
|
||||||
routes::REWARD_ESTIMATION,
|
routes::REWARD_ESTIMATION,
|
||||||
],
|
],
|
||||||
NO_PARAMS,
|
NO_PARAMS,
|
||||||
@@ -250,14 +265,14 @@ impl Client {
|
|||||||
|
|
||||||
pub async fn get_mixnode_stake_saturation(
|
pub async fn get_mixnode_stake_saturation(
|
||||||
&self,
|
&self,
|
||||||
mix_id: NodeId,
|
identity: IdentityKeyRef<'_>,
|
||||||
) -> Result<StakeSaturationResponse, ValidatorAPIError> {
|
) -> Result<StakeSaturationResponse, ValidatorAPIError> {
|
||||||
self.query_validator_api(
|
self.query_validator_api(
|
||||||
&[
|
&[
|
||||||
routes::API_VERSION,
|
routes::API_VERSION,
|
||||||
routes::STATUS_ROUTES,
|
routes::STATUS_ROUTES,
|
||||||
routes::MIXNODE,
|
routes::MIXNODE,
|
||||||
&mix_id.to_string(),
|
identity,
|
||||||
routes::STAKE_SATURATION,
|
routes::STAKE_SATURATION,
|
||||||
],
|
],
|
||||||
NO_PARAMS,
|
NO_PARAMS,
|
||||||
@@ -267,14 +282,14 @@ impl Client {
|
|||||||
|
|
||||||
pub async fn get_mixnode_inclusion_probability(
|
pub async fn get_mixnode_inclusion_probability(
|
||||||
&self,
|
&self,
|
||||||
mix_id: NodeId,
|
identity: IdentityKeyRef<'_>,
|
||||||
) -> Result<InclusionProbabilityResponse, ValidatorAPIError> {
|
) -> Result<InclusionProbabilityResponse, ValidatorAPIError> {
|
||||||
self.query_validator_api(
|
self.query_validator_api(
|
||||||
&[
|
&[
|
||||||
routes::API_VERSION,
|
routes::API_VERSION,
|
||||||
routes::STATUS_ROUTES,
|
routes::STATUS_ROUTES,
|
||||||
routes::MIXNODE,
|
routes::MIXNODE,
|
||||||
&mix_id.to_string(),
|
identity,
|
||||||
routes::INCLUSION_CHANCE,
|
routes::INCLUSION_CHANCE,
|
||||||
],
|
],
|
||||||
NO_PARAMS,
|
NO_PARAMS,
|
||||||
@@ -284,14 +299,27 @@ impl Client {
|
|||||||
|
|
||||||
pub async fn get_mixnode_avg_uptime(
|
pub async fn get_mixnode_avg_uptime(
|
||||||
&self,
|
&self,
|
||||||
mix_id: NodeId,
|
identity: IdentityKeyRef<'_>,
|
||||||
) -> Result<UptimeResponse, ValidatorAPIError> {
|
) -> Result<UptimeResponse, ValidatorAPIError> {
|
||||||
self.query_validator_api(
|
self.query_validator_api(
|
||||||
&[
|
&[
|
||||||
routes::API_VERSION,
|
routes::API_VERSION,
|
||||||
routes::STATUS_ROUTES,
|
routes::STATUS_ROUTES,
|
||||||
routes::MIXNODE,
|
routes::MIXNODE,
|
||||||
&mix_id.to_string(),
|
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,
|
||||||
routes::AVG_UPTIME,
|
routes::AVG_UPTIME,
|
||||||
],
|
],
|
||||||
NO_PARAMS,
|
NO_PARAMS,
|
||||||
|
|||||||
@@ -28,11 +28,6 @@ pub fn pretty_cosmwasm_coin(coin: &CosmWasmCoin) -> String {
|
|||||||
format!("{} {}", amount, denom)
|
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)
|
pub fn show_error<E>(e: E)
|
||||||
where
|
where
|
||||||
E: Display,
|
E: Display,
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ pub async fn query_balance(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let denom = args.denom.unwrap_or_default();
|
let denom = args.denom.unwrap_or_else(|| "".to_string());
|
||||||
|
|
||||||
for coin in coins {
|
for coin in coins {
|
||||||
if denom.is_empty() || denom.eq_ignore_ascii_case(&coin.denom) {
|
if denom.is_empty() || denom.eq_ignore_ascii_case(&coin.denom) {
|
||||||
|
|||||||
@@ -4,16 +4,12 @@
|
|||||||
use crate::context::SigningClient;
|
use crate::context::SigningClient;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use log::info;
|
use log::info;
|
||||||
use mixnet_contract_common::{Coin, NodeId};
|
use mixnet_contract_common::Coin;
|
||||||
use validator_client::nymd::traits::{MixnetQueryClient, MixnetSigningClient};
|
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
pub struct Args {
|
pub struct Args {
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
pub mix_id: Option<NodeId>,
|
pub identity_key: String,
|
||||||
|
|
||||||
#[clap(long)]
|
|
||||||
pub identity_key: Option<String>,
|
|
||||||
|
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
pub amount: u128,
|
pub amount: u128,
|
||||||
@@ -24,25 +20,10 @@ pub async fn delegate_to_mixnode(args: Args, client: SigningClient) {
|
|||||||
|
|
||||||
info!("Starting delegation to mixnode");
|
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 coin = Coin::new(args.amount, denom);
|
||||||
|
|
||||||
let res = client
|
let res = client
|
||||||
.delegate_to_mixnode(mix_id, coin.into(), None)
|
.delegate_to_mixnode(&*args.identity_key, coin.into(), None)
|
||||||
.await
|
.await
|
||||||
.expect("failed to delegate to mixnode!");
|
.expect("failed to delegate to mixnode!");
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ use crate::context::SigningClientWithValidatorAPI;
|
|||||||
use crate::utils::{pretty_cosmwasm_coin, show_error_passthrough};
|
use crate::utils::{pretty_cosmwasm_coin, show_error_passthrough};
|
||||||
|
|
||||||
use comfy_table::Table;
|
use comfy_table::Table;
|
||||||
use cosmwasm_std::Addr;
|
use mixnet_contract_common::mixnode::DelegationEvent;
|
||||||
use mixnet_contract_common::{Delegation, PendingEpochEvent, PendingEpochEventData};
|
use mixnet_contract_common::Delegation;
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
pub struct Args {}
|
pub struct Args {}
|
||||||
@@ -26,7 +26,19 @@ pub async fn execute(_args: Args, client: SigningClientWithValidatorAPI) {
|
|||||||
.map_err(show_error_passthrough);
|
.map_err(show_error_passthrough);
|
||||||
|
|
||||||
let mixnet_contract_events = client
|
let mixnet_contract_events = client
|
||||||
.get_all_nymd_pending_epoch_events()
|
.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()),
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(show_error_passthrough);
|
.map_err(show_error_passthrough);
|
||||||
|
|
||||||
@@ -46,6 +58,13 @@ pub async fn execute(_args: Args, client: SigningClientWithValidatorAPI) {
|
|||||||
print_delegation_events(res, &client).await;
|
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 {
|
async fn to_iso_timestamp(block_height: u32, client: &SigningClientWithValidatorAPI) -> String {
|
||||||
@@ -58,17 +77,14 @@ async fn to_iso_timestamp(block_height: u32, client: &SigningClientWithValidator
|
|||||||
async fn print_delegations(delegations: Vec<Delegation>, client: &SigningClientWithValidatorAPI) {
|
async fn print_delegations(delegations: Vec<Delegation>, client: &SigningClientWithValidatorAPI) {
|
||||||
let mut table = Table::new();
|
let mut table = Table::new();
|
||||||
|
|
||||||
table.set_header(vec!["Timestamp", "Mix Id", "Delegation", "Proxy"]);
|
table.set_header(vec!["Timestamp", "Identity Key", "Delegation", "Proxy"]);
|
||||||
|
|
||||||
for delegation in delegations {
|
for delegation in delegations {
|
||||||
table.add_row(vec![
|
table.add_row(vec![
|
||||||
to_iso_timestamp(delegation.height as u32, client).await,
|
to_iso_timestamp(delegation.block_height as u32, client).await,
|
||||||
delegation.mix_id.to_string(),
|
delegation.node_identity.to_string(),
|
||||||
pretty_cosmwasm_coin(&delegation.amount),
|
pretty_cosmwasm_coin(&delegation.amount),
|
||||||
delegation
|
format!("{:?}", delegation.proxy),
|
||||||
.proxy
|
|
||||||
.map(Addr::into_string)
|
|
||||||
.unwrap_or_else(|| "-".into()),
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,53 +92,36 @@ async fn print_delegations(delegations: Vec<Delegation>, client: &SigningClientW
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn print_delegation_events(
|
async fn print_delegation_events(
|
||||||
events: Vec<PendingEpochEvent>,
|
events: Vec<DelegationEvent>,
|
||||||
client: &SigningClientWithValidatorAPI,
|
client: &SigningClientWithValidatorAPI,
|
||||||
) {
|
) {
|
||||||
let mut table = Table::new();
|
let mut table = Table::new();
|
||||||
|
|
||||||
table.set_header(vec![
|
table.set_header(vec![
|
||||||
"Timestamp",
|
"Timestamp",
|
||||||
"Mix id",
|
"Identity Key",
|
||||||
"Delegation",
|
"Delegation",
|
||||||
"Event Type",
|
"Event Type",
|
||||||
"Proxy",
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
for event in events {
|
for event in events {
|
||||||
match event.event {
|
match event {
|
||||||
PendingEpochEventData::Delegate {
|
DelegationEvent::Delegate(delegation) => {
|
||||||
owner,
|
table.add_row(vec![
|
||||||
mix_id,
|
to_iso_timestamp(delegation.block_height as u32, client).await,
|
||||||
amount,
|
delegation.node_identity.to_string(),
|
||||||
proxy,
|
pretty_cosmwasm_coin(&delegation.amount),
|
||||||
} => {
|
"Delegate".to_string(),
|
||||||
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()),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
PendingEpochEventData::Undelegate {
|
DelegationEvent::Undelegate(undelegate) => {
|
||||||
owner,
|
table.add_row(vec![
|
||||||
mix_id,
|
to_iso_timestamp(undelegate.block_height() as u32, client).await,
|
||||||
proxy,
|
undelegate.mix_identity().to_string(),
|
||||||
} => {
|
"-".to_string(),
|
||||||
if owner.as_str() == client.nymd.address().as_ref() {
|
"Undelegate".to_string(),
|
||||||
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,38 +4,18 @@
|
|||||||
use crate::context::SigningClient;
|
use crate::context::SigningClient;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use log::info;
|
use log::info;
|
||||||
use mixnet_contract_common::NodeId;
|
|
||||||
use validator_client::nymd::traits::{MixnetQueryClient, MixnetSigningClient};
|
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
pub struct Args {
|
pub struct Args {
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
pub mix_id: Option<NodeId>,
|
pub identity_key: String,
|
||||||
|
|
||||||
#[clap(long)]
|
|
||||||
pub identity_key: Option<String>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn claim_delegator_reward(args: Args, client: SigningClient) {
|
pub async fn claim_delegator_reward(args: Args, client: SigningClient) {
|
||||||
info!("Claim delegator reward");
|
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
|
let res = client
|
||||||
.withdraw_delegator_reward(mix_id, None)
|
.execute_claim_delegator_reward(args.identity_key, None)
|
||||||
.await
|
.await
|
||||||
.expect("failed to claim delegator-reward");
|
.expect("failed to claim delegator-reward");
|
||||||
|
|
||||||
|
|||||||
+2
-22
@@ -4,38 +4,18 @@
|
|||||||
use crate::context::SigningClient;
|
use crate::context::SigningClient;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use log::info;
|
use log::info;
|
||||||
use mixnet_contract_common::NodeId;
|
|
||||||
use validator_client::nymd::traits::{MixnetQueryClient, MixnetSigningClient};
|
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
pub struct Args {
|
pub struct Args {
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
pub mix_id: Option<NodeId>,
|
pub identity: String,
|
||||||
|
|
||||||
#[clap(long)]
|
|
||||||
pub identity_key: Option<String>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn vesting_claim_delegator_reward(args: Args, client: SigningClient) {
|
pub async fn vesting_claim_delegator_reward(args: Args, client: SigningClient) {
|
||||||
info!("Claim vesting delegator reward");
|
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
|
let res = client
|
||||||
.withdraw_delegator_reward_on_behalf(client.address().clone(), mix_id, None)
|
.execute_vesting_claim_delegator_reward(args.identity, None)
|
||||||
.await
|
.await
|
||||||
.expect("failed to claim vesting delegator-reward");
|
.expect("failed to claim vesting delegator-reward");
|
||||||
|
|
||||||
|
|||||||
@@ -4,38 +4,18 @@
|
|||||||
use crate::context::SigningClient;
|
use crate::context::SigningClient;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use log::info;
|
use log::info;
|
||||||
use mixnet_contract_common::NodeId;
|
|
||||||
use validator_client::nymd::traits::{MixnetQueryClient, MixnetSigningClient};
|
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
pub struct Args {
|
pub struct Args {
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
pub mix_id: Option<NodeId>,
|
pub identity_key: String,
|
||||||
|
|
||||||
#[clap(long)]
|
|
||||||
pub identity_key: Option<String>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn undelegate_from_mixnode(args: Args, client: SigningClient) {
|
pub async fn undelegate_from_mixnode(args: Args, client: SigningClient) {
|
||||||
info!("removing stake from mix-node");
|
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
|
let res = client
|
||||||
.undelegate_from_mixnode(mix_id, None)
|
.remove_mixnode_delegation(&*args.identity_key, None)
|
||||||
.await
|
.await
|
||||||
.expect("failed to remove stake from mixnode!");
|
.expect("failed to remove stake from mixnode!");
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,7 @@
|
|||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use log::info;
|
use log::info;
|
||||||
|
|
||||||
use mixnet_contract_common::{Coin, NodeId};
|
use mixnet_contract_common::Coin;
|
||||||
use validator_client::nymd::traits::MixnetQueryClient;
|
|
||||||
use validator_client::nymd::VestingSigningClient;
|
use validator_client::nymd::VestingSigningClient;
|
||||||
|
|
||||||
use crate::context::SigningClient;
|
use crate::context::SigningClient;
|
||||||
@@ -13,10 +12,7 @@ use crate::context::SigningClient;
|
|||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
pub struct Args {
|
pub struct Args {
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
pub mix_id: Option<NodeId>,
|
pub identity_key: String,
|
||||||
|
|
||||||
#[clap(long)]
|
|
||||||
pub identity_key: Option<String>,
|
|
||||||
|
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
pub amount: u128,
|
pub amount: u128,
|
||||||
@@ -27,25 +23,10 @@ pub async fn vesting_delegate_to_mixnode(args: Args, client: SigningClient) {
|
|||||||
|
|
||||||
info!("Starting vesting delegation to mixnode");
|
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 coin = Coin::new(args.amount, denom);
|
||||||
|
|
||||||
let res = client
|
let res = client
|
||||||
.vesting_delegate_to_mixnode(mix_id, coin.into(), None)
|
.vesting_delegate_to_mixnode(&*args.identity_key, coin.into(), None)
|
||||||
.await
|
.await
|
||||||
.expect("failed to delegate to mixnode!");
|
.expect("failed to delegate to mixnode!");
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,7 @@
|
|||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use log::info;
|
use log::info;
|
||||||
use mixnet_contract_common::NodeId;
|
|
||||||
use validator_client::nymd::traits::MixnetQueryClient;
|
|
||||||
use validator_client::nymd::VestingSigningClient;
|
use validator_client::nymd::VestingSigningClient;
|
||||||
|
|
||||||
use crate::context::SigningClient;
|
use crate::context::SigningClient;
|
||||||
@@ -12,32 +11,14 @@ use crate::context::SigningClient;
|
|||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
pub struct Args {
|
pub struct Args {
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
pub mix_id: Option<NodeId>,
|
pub identity_key: String,
|
||||||
|
|
||||||
#[clap(long)]
|
|
||||||
pub identity_key: Option<String>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn vesting_undelegate_from_mixnode(args: Args, client: SigningClient) {
|
pub async fn vesting_undelegate_from_mixnode(args: Args, client: SigningClient) {
|
||||||
info!("removing stake from vesting mix-node");
|
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
|
let res = client
|
||||||
.vesting_undelegate_from_mixnode(mix_id, None)
|
.vesting_undelegate_from_mixnode(&*args.identity_key, None)
|
||||||
.await
|
.await
|
||||||
.expect("failed to remove stake from vesting account on mixnode!");
|
.expect("failed to remove stake from vesting account on mixnode!");
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ use clap::Parser;
|
|||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
use mixnet_contract_common::Coin;
|
use mixnet_contract_common::Coin;
|
||||||
use network_defaults::{DEFAULT_CLIENT_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT};
|
use network_defaults::{DEFAULT_CLIENT_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT};
|
||||||
use validator_client::nymd::traits::MixnetSigningClient;
|
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
pub struct Args {
|
pub struct Args {
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
use crate::context::SigningClient;
|
use crate::context::SigningClient;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use log::info;
|
use log::info;
|
||||||
use validator_client::nymd::traits::MixnetSigningClient;
|
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
pub struct Args {}
|
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 coin = Coin::new(args.amount, denom);
|
||||||
|
|
||||||
let res = client
|
let res = client
|
||||||
.vesting_bond_gateway(gateway, &args.signature, coin.into(), None)
|
.vesting_bond_gateway(gateway, &*args.signature, coin.into(), None)
|
||||||
.await
|
.await
|
||||||
.expect("failed to bond gateway!");
|
.expect("failed to bond gateway!");
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
use crate::context::SigningClient;
|
use crate::context::SigningClient;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use log::info;
|
use log::info;
|
||||||
use validator_client::nymd::traits::MixnetSigningClient;
|
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
pub struct Args {}
|
pub struct Args {}
|
||||||
|
|||||||
@@ -2,15 +2,12 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use cosmwasm_std::Uint128;
|
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
|
|
||||||
use mixnet_contract_common::{Coin, MixNodeCostParams, Percent};
|
use mixnet_contract_common::Coin;
|
||||||
use network_defaults::{
|
use network_defaults::{
|
||||||
DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT,
|
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;
|
use crate::context::SigningClient;
|
||||||
|
|
||||||
@@ -43,12 +40,6 @@ pub struct Args {
|
|||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
pub profit_margin_percent: Option<u8>,
|
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(
|
#[clap(
|
||||||
long,
|
long,
|
||||||
help = "bonding amount in current DENOMINATION (so it would be 'unym', rather than 'nym')"
|
help = "bonding amount in current DENOMINATION (so it would be 'unym', rather than 'nym')"
|
||||||
@@ -80,23 +71,13 @@ pub async fn bond_mixnode(args: Args, client: SigningClient) {
|
|||||||
sphinx_key: args.sphinx_key,
|
sphinx_key: args.sphinx_key,
|
||||||
identity_key: args.identity_key,
|
identity_key: args.identity_key,
|
||||||
version: args.version,
|
version: args.version,
|
||||||
|
profit_margin_percent: args.profit_margin_percent.unwrap_or(10),
|
||||||
};
|
};
|
||||||
|
|
||||||
let coin = Coin::new(args.amount, denom);
|
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
|
let res = client
|
||||||
.bond_mixnode(mixnode, cost_params, args.signature, coin.into(), None)
|
.bond_mixnode(mixnode, args.signature, coin.into(), None)
|
||||||
.await
|
.await
|
||||||
.expect("failed to bond mixnode!");
|
.expect("failed to bond mixnode!");
|
||||||
|
|
||||||
|
|||||||
+1
-2
@@ -4,7 +4,6 @@
|
|||||||
use crate::context::SigningClient;
|
use crate::context::SigningClient;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use log::info;
|
use log::info;
|
||||||
use validator_client::nymd::traits::MixnetSigningClient;
|
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
pub struct Args {}
|
pub struct Args {}
|
||||||
@@ -13,7 +12,7 @@ pub async fn claim_operator_reward(_args: Args, client: SigningClient) {
|
|||||||
info!("Claim operator reward");
|
info!("Claim operator reward");
|
||||||
|
|
||||||
let res = client
|
let res = client
|
||||||
.withdraw_operator_reward(None)
|
.execute_claim_operator_reward(None)
|
||||||
.await
|
.await
|
||||||
.expect("failed to claim operator reward");
|
.expect("failed to claim operator reward");
|
||||||
|
|
||||||
|
|||||||
+1
-2
@@ -4,7 +4,6 @@
|
|||||||
use crate::context::SigningClient;
|
use crate::context::SigningClient;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use log::info;
|
use log::info;
|
||||||
use validator_client::nymd::traits::MixnetSigningClient;
|
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
pub struct Args {
|
pub struct Args {
|
||||||
@@ -16,7 +15,7 @@ pub async fn vesting_claim_operator_reward(client: SigningClient) {
|
|||||||
info!("Claim vesting operator reward");
|
info!("Claim vesting operator reward");
|
||||||
|
|
||||||
let res = client
|
let res = client
|
||||||
.withdraw_operator_reward_on_behalf(client.address().clone(), None)
|
.execute_vesting_claim_operator_reward(None)
|
||||||
.await
|
.await
|
||||||
.expect("failed to claim vesting operator reward");
|
.expect("failed to claim vesting operator reward");
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
|
|
||||||
use clap::{Args, Subcommand};
|
use clap::{Args, Subcommand};
|
||||||
|
|
||||||
pub mod update_config;
|
pub mod update_profit_percent;
|
||||||
pub mod vesting_update_config;
|
pub mod vesting_update_profit_percent;
|
||||||
|
|
||||||
#[derive(Debug, Args)]
|
#[derive(Debug, Args)]
|
||||||
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
|
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
|
||||||
@@ -15,12 +15,8 @@ pub struct MixnetOperatorsMixnodeSettings {
|
|||||||
|
|
||||||
#[derive(Debug, Subcommand)]
|
#[derive(Debug, Subcommand)]
|
||||||
pub enum MixnetOperatorsMixnodeSettingsCommands {
|
pub enum MixnetOperatorsMixnodeSettingsCommands {
|
||||||
/// Update mixnode configuration
|
/// Update profit percentage
|
||||||
UpdateConfig(update_config::Args),
|
UpdateProfitPercentage(update_profit_percent::Args),
|
||||||
/// Update mixnode configuration for a mixnode bonded with locked tokens
|
/// Update profit percentage for a mixnode bonded with locked tokens
|
||||||
VestingUpdateConfig(vesting_update_config::Args),
|
VestingUpdateProfitPercentage(vesting_update_profit_percent::Args),
|
||||||
/// Update mixnode cost parameters
|
|
||||||
UpdateCostParameters,
|
|
||||||
/// Update mixnode cost parameters for a mixnode bonded with locked tokens
|
|
||||||
VestingUpdateCostParameters,
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,68 +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 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
@@ -0,0 +1,24 @@
|
|||||||
|
// 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
@@ -1,69 +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 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
@@ -0,0 +1,28 @@
|
|||||||
|
// 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,7 +3,6 @@
|
|||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use log::info;
|
use log::info;
|
||||||
use validator_client::nymd::traits::MixnetSigningClient;
|
|
||||||
|
|
||||||
use crate::context::SigningClient;
|
use crate::context::SigningClient;
|
||||||
|
|
||||||
|
|||||||
@@ -3,14 +3,13 @@
|
|||||||
|
|
||||||
use crate::context::SigningClient;
|
use crate::context::SigningClient;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use cosmwasm_std::Uint128;
|
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
use mixnet_contract_common::{Coin, MixNodeCostParams};
|
use mixnet_contract_common::Coin;
|
||||||
use mixnet_contract_common::{MixNode, Percent};
|
use mixnet_contract_common::MixNode;
|
||||||
use network_defaults::{
|
use network_defaults::{
|
||||||
DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT,
|
DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT,
|
||||||
};
|
};
|
||||||
use validator_client::nymd::{CosmWasmCoin, VestingSigningClient};
|
use validator_client::nymd::VestingSigningClient;
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
pub struct Args {
|
pub struct Args {
|
||||||
@@ -41,12 +40,6 @@ pub struct Args {
|
|||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
pub profit_margin_percent: Option<u8>,
|
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(
|
#[clap(
|
||||||
long,
|
long,
|
||||||
help = "bonding amount in current DENOMINATION (so it would be 'unym', rather than 'nym')"
|
help = "bonding amount in current DENOMINATION (so it would be 'unym', rather than 'nym')"
|
||||||
@@ -79,23 +72,13 @@ pub async fn vesting_bond_mixnode(client: SigningClient, args: Args, denom: &str
|
|||||||
sphinx_key: args.sphinx_key,
|
sphinx_key: args.sphinx_key,
|
||||||
identity_key: args.identity_key,
|
identity_key: args.identity_key,
|
||||||
version: args.version,
|
version: args.version,
|
||||||
|
profit_margin_percent: args.profit_margin_percent.unwrap_or(10),
|
||||||
};
|
};
|
||||||
|
|
||||||
let coin = Coin::new(args.amount, denom);
|
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
|
let res = client
|
||||||
.vesting_bond_mixnode(mixnode, cost_params, &args.signature, coin.into(), None)
|
.vesting_bond_mixnode(mixnode, &*args.signature, coin.into(), None)
|
||||||
.await
|
.await
|
||||||
.expect("failed to bond vesting mixnode!");
|
.expect("failed to bond vesting mixnode!");
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use clap::Parser;
|
|||||||
use comfy_table::Table;
|
use comfy_table::Table;
|
||||||
|
|
||||||
use crate::context::QueryClientWithValidatorAPI;
|
use crate::context::QueryClientWithValidatorAPI;
|
||||||
use crate::utils::{pretty_decimal_with_denom, show_error};
|
use crate::utils::{pretty_cosmwasm_coin, show_error};
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
pub struct Args {
|
pub struct Args {
|
||||||
@@ -19,8 +19,7 @@ pub async fn query(args: Args, client: &QueryClientWithValidatorAPI) {
|
|||||||
Ok(res) => match args.identity_key {
|
Ok(res) => match args.identity_key {
|
||||||
Some(identity_key) => {
|
Some(identity_key) => {
|
||||||
let node = res.iter().find(|node| {
|
let node = res.iter().find(|node| {
|
||||||
node.bond_information
|
node.mix_node
|
||||||
.mix_node
|
|
||||||
.identity_key
|
.identity_key
|
||||||
.to_string()
|
.to_string()
|
||||||
.eq_ignore_ascii_case(&identity_key)
|
.eq_ignore_ascii_case(&identity_key)
|
||||||
@@ -34,7 +33,6 @@ pub async fn query(args: Args, client: &QueryClientWithValidatorAPI) {
|
|||||||
let mut table = Table::new();
|
let mut table = Table::new();
|
||||||
|
|
||||||
table.set_header(vec![
|
table.set_header(vec![
|
||||||
"Mix id",
|
|
||||||
"Identity Key",
|
"Identity Key",
|
||||||
"Owner",
|
"Owner",
|
||||||
"Host",
|
"Host",
|
||||||
@@ -43,15 +41,13 @@ pub async fn query(args: Args, client: &QueryClientWithValidatorAPI) {
|
|||||||
"Version",
|
"Version",
|
||||||
]);
|
]);
|
||||||
for node in res {
|
for node in res {
|
||||||
let denom = &node.bond_information.original_pledge().denom;
|
|
||||||
table.add_row(vec![
|
table.add_row(vec![
|
||||||
node.mix_id().to_string(),
|
node.mix_node.identity_key.to_string(),
|
||||||
node.bond_information.mix_node.identity_key.clone(),
|
node.owner.to_string(),
|
||||||
node.bond_information.owner.clone().into_string(),
|
node.mix_node.host.to_string(),
|
||||||
node.bond_information.mix_node.host.clone(),
|
pretty_cosmwasm_coin(&node.pledge_amount),
|
||||||
pretty_decimal_with_denom(node.rewarding_details.operator, denom),
|
pretty_cosmwasm_coin(&node.total_delegation()),
|
||||||
pretty_decimal_with_denom(node.rewarding_details.delegates, denom),
|
node.mix_node.version,
|
||||||
node.bond_information.mix_node.version,
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ pub async fn create(args: Args, client: SigningClient, network_details: &NymNetw
|
|||||||
|
|
||||||
let res = client
|
let res = client
|
||||||
.create_periodic_vesting_account(
|
.create_periodic_vesting_account(
|
||||||
&args.address,
|
&*args.address,
|
||||||
args.staking_address,
|
args.staking_address,
|
||||||
Some(vesting),
|
Some(vesting),
|
||||||
coin.into(),
|
coin.into(),
|
||||||
@@ -70,7 +70,7 @@ pub async fn create(args: Args, client: SigningClient, network_details: &NymNetw
|
|||||||
|
|
||||||
let send_coin_response = client
|
let send_coin_response = client
|
||||||
.send(
|
.send(
|
||||||
&AccountId::from_str(&args.address).unwrap(),
|
&AccountId::from_str(&*args.address).unwrap(),
|
||||||
vec![coin.into()],
|
vec![coin.into()],
|
||||||
"payment made :)",
|
"payment made :)",
|
||||||
None,
|
None,
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
[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"
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -88,7 +88,6 @@ pub trait NymConfig: Default + Serialize + DeserializeOwned {
|
|||||||
|
|
||||||
let location = custom_location
|
let location = custom_location
|
||||||
.unwrap_or_else(|| self.config_directory().join(Self::config_file_name()));
|
.unwrap_or_else(|| self.config_directory().join(Self::config_file_name()));
|
||||||
log::info!("Configuration file will be saved to {:?}", location);
|
|
||||||
|
|
||||||
cfg_if::cfg_if! {
|
cfg_if::cfg_if! {
|
||||||
if #[cfg(unix)] {
|
if #[cfg(unix)] {
|
||||||
|
|||||||
@@ -8,4 +8,3 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cosmwasm-std = "1.0.0"
|
cosmwasm-std = "1.0.0"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
|
||||||
|
|||||||
@@ -28,26 +28,3 @@ pub fn may_find_attribute(event: &Event, key: &str) -> Option<String> {
|
|||||||
}
|
}
|
||||||
None
|
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,6 +2,3 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
pub mod events;
|
pub mod events;
|
||||||
pub mod types;
|
|
||||||
|
|
||||||
pub use types::*;
|
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
// 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,22 +8,21 @@ rust-version = "1.62"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bs58 = "0.4.0"
|
|
||||||
cosmwasm-std = "1.0.0"
|
cosmwasm-std = "1.0.0"
|
||||||
|
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_repr = "0.1"
|
serde_repr = "0.1"
|
||||||
schemars = "0.8"
|
schemars = "0.8"
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
contracts-common = { path = "../contracts-common" }
|
fixed = { version = "1.1", features = ["serde"] }
|
||||||
serde_json = "1.0.0"
|
az = "1.1"
|
||||||
|
|
||||||
# TO CHECK WHETHER STILL NEEDED:
|
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
time = { version = "0.3.6", features = ["parsing", "formatting"] }
|
time = { version = "0.3.6", features = ["parsing", "formatting"] }
|
||||||
ts-rs = { version = "6.1.2", optional = true }
|
ts-rs = {version = "6.1.2", optional = true}
|
||||||
|
|
||||||
|
contracts-common = { path = "../contracts-common" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rand_chacha = "0.3"
|
|
||||||
time = { version = "0.3.5", features = ["serde", "macros"] }
|
time = { version = "0.3.5", features = ["serde", "macros"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
// 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,135 +1,151 @@
|
|||||||
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
// due to code generated by JsonSchema
|
// due to code generated by JsonSchema
|
||||||
#![allow(clippy::field_reassign_with_default)]
|
#![allow(clippy::field_reassign_with_default)]
|
||||||
|
|
||||||
use crate::{Addr, NodeId};
|
use crate::{Addr, IdentityKey};
|
||||||
use cosmwasm_std::{Coin, Decimal};
|
use cosmwasm_std::{Coin, Uint128};
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::fmt::Display;
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
// just use a string representation of those so that we wouldn't need to bother with decoding bytes
|
type OwnerAddressBytes = Vec<u8>;
|
||||||
// and trying to figure out whether they're valid, etc
|
type BlockHeight = u64;
|
||||||
pub type OwnerProxySubKey = String;
|
|
||||||
pub type StorageKey = (NodeId, OwnerProxySubKey);
|
|
||||||
|
|
||||||
pub fn generate_owner_storage_subkey(address: &Addr, proxy: Option<&Addr>) -> String {
|
pub fn generate_storage_key(address: &Addr, proxy: Option<&Addr>) -> Vec<u8> {
|
||||||
if let Some(proxy) = &proxy {
|
if let Some(proxy) = &proxy {
|
||||||
let key_bytes = address
|
address
|
||||||
.as_bytes()
|
.as_bytes()
|
||||||
.iter()
|
.iter()
|
||||||
.zip(proxy.as_bytes())
|
.zip(proxy.as_bytes())
|
||||||
.map(|(x, y)| x ^ y)
|
.map(|(x, y)| x ^ y)
|
||||||
.collect::<Vec<_>>();
|
.collect()
|
||||||
bs58::encode(key_bytes).into_string()
|
|
||||||
} else {
|
} else {
|
||||||
address.clone().into_string()
|
address.as_bytes().to_vec()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, JsonSchema)]
|
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, JsonSchema)]
|
||||||
pub struct Delegation {
|
pub struct Delegation {
|
||||||
/// Address of the owner of this delegation.
|
|
||||||
pub owner: Addr,
|
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 amount: Coin,
|
||||||
|
pub block_height: u64,
|
||||||
|
pub proxy: Option<Addr>, // proxy address used to delegate the funds on behalf of another address
|
||||||
|
}
|
||||||
|
|
||||||
/// Block height where this delegation occurred.
|
impl Eq for Delegation {}
|
||||||
pub height: u64,
|
|
||||||
|
|
||||||
/// Proxy address used to delegate the funds on behalf of another address
|
#[allow(clippy::derive_hash_xor_eq)]
|
||||||
pub proxy: Option<Addr>,
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Delegation {
|
impl Delegation {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
owner: Addr,
|
owner: Addr,
|
||||||
mix_id: NodeId,
|
node_identity: IdentityKey,
|
||||||
cumulative_reward_ratio: Decimal,
|
|
||||||
amount: Coin,
|
amount: Coin,
|
||||||
height: u64,
|
block_height: BlockHeight,
|
||||||
proxy: Option<Addr>,
|
proxy: Option<Addr>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Delegation {
|
Delegation {
|
||||||
owner,
|
owner,
|
||||||
mix_id,
|
node_identity,
|
||||||
cumulative_reward_ratio,
|
|
||||||
amount,
|
amount,
|
||||||
height,
|
block_height,
|
||||||
proxy,
|
proxy,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_storage_key(
|
pub fn storage_key(&self) -> (IdentityKey, OwnerAddressBytes, BlockHeight) {
|
||||||
mix_id: NodeId,
|
(
|
||||||
owner_address: &Addr,
|
self.node_identity(),
|
||||||
proxy: Option<&Addr>,
|
self.proxy_storage_key(),
|
||||||
) -> StorageKey {
|
self.block_height(),
|
||||||
(mix_id, generate_owner_storage_subkey(owner_address, proxy))
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// this function might seem a bit redundant, but I'd rather explicitly keep it around in case
|
pub fn event_storage_key(&self) -> (OwnerAddressBytes, BlockHeight, IdentityKey) {
|
||||||
// some types change in the future
|
(
|
||||||
pub fn generate_storage_key_with_subkey(
|
self.proxy_storage_key(),
|
||||||
mix_id: NodeId,
|
self.block_height(),
|
||||||
owner_proxy_subkey: OwnerProxySubKey,
|
self.node_identity(),
|
||||||
) -> StorageKey {
|
)
|
||||||
(mix_id, owner_proxy_subkey)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dec_amount(&self) -> Decimal {
|
pub fn proxy_storage_key(&self) -> OwnerAddressBytes {
|
||||||
// the unwrap here is fine as we're guaranteed our base coin amount is going to fit in a Decimal
|
generate_storage_key(&self.owner, self.proxy.as_ref())
|
||||||
// with 0 decimal places
|
|
||||||
Decimal::from_atomics(self.amount.amount, 0).unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn proxy_storage_key(&self) -> OwnerProxySubKey {
|
pub fn proxy(&self) -> Option<&Addr> {
|
||||||
generate_owner_storage_subkey(&self.owner, self.proxy.as_ref())
|
self.proxy.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn storage_key(&self) -> StorageKey {
|
pub fn increment_amount(&mut self, amount: Uint128, at_height: Option<u64>) {
|
||||||
Self::generate_storage_key(self.mix_id, &self.owner, self.proxy.as_ref())
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
impl Display for Delegation {
|
||||||
pub struct PagedMixNodeDelegationsResponse {
|
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 {
|
||||||
pub delegations: Vec<Delegation>,
|
pub delegations: Vec<Delegation>,
|
||||||
pub start_next_after: Option<OwnerProxySubKey>,
|
pub start_next_after: Option<(String, u64)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PagedMixNodeDelegationsResponse {
|
impl PagedMixDelegationsResponse {
|
||||||
pub fn new(delegations: Vec<Delegation>, start_next_after: Option<OwnerProxySubKey>) -> Self {
|
pub fn new(delegations: Vec<Delegation>, start_next_after: Option<(String, u64)>) -> Self {
|
||||||
PagedMixNodeDelegationsResponse {
|
PagedMixDelegationsResponse {
|
||||||
delegations,
|
delegations,
|
||||||
start_next_after,
|
start_next_after,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||||
pub struct PagedDelegatorDelegationsResponse {
|
pub struct PagedDelegatorDelegationsResponse {
|
||||||
pub delegations: Vec<Delegation>,
|
pub delegations: Vec<Delegation>,
|
||||||
pub start_next_after: Option<(NodeId, OwnerProxySubKey)>,
|
pub start_next_after: Option<IdentityKey>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PagedDelegatorDelegationsResponse {
|
impl PagedDelegatorDelegationsResponse {
|
||||||
pub fn new(
|
pub fn new(delegations: Vec<Delegation>, start_next_after: Option<IdentityKey>) -> Self {
|
||||||
delegations: Vec<Delegation>,
|
|
||||||
start_next_after: Option<(NodeId, OwnerProxySubKey)>,
|
|
||||||
) -> Self {
|
|
||||||
PagedDelegatorDelegationsResponse {
|
PagedDelegatorDelegationsResponse {
|
||||||
delegations,
|
delegations,
|
||||||
start_next_after,
|
start_next_after,
|
||||||
@@ -137,32 +153,20 @@ impl PagedDelegatorDelegationsResponse {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||||
pub struct MixNodeDelegationResponse {
|
|
||||||
pub delegation: Option<Delegation>,
|
|
||||||
pub mixnode_still_bonded: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
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 struct PagedAllDelegationsResponse {
|
||||||
pub delegations: Vec<Delegation>,
|
pub delegations: Vec<Delegation>,
|
||||||
pub start_next_after: Option<StorageKey>,
|
pub start_next_after: Option<(IdentityKey, Vec<u8>, u64)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PagedAllDelegationsResponse {
|
impl PagedAllDelegationsResponse {
|
||||||
pub fn new(delegations: Vec<Delegation>, start_next_after: Option<StorageKey>) -> Self {
|
pub fn new(
|
||||||
|
delegations: Vec<Delegation>,
|
||||||
|
start_next_after: Option<(IdentityKey, Vec<u8>, u64)>,
|
||||||
|
) -> Self {
|
||||||
PagedAllDelegationsResponse {
|
PagedAllDelegationsResponse {
|
||||||
delegations,
|
delegations,
|
||||||
start_next_after,
|
start_next_after: start_next_after.map(|(id, addr, height)| (id, addr, height)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,139 +1,23 @@
|
|||||||
// 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;
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Error, Debug, PartialEq)]
|
#[derive(Error, Debug, PartialEq)]
|
||||||
pub enum MixnetContractError {
|
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}")]
|
#[error("{source}")]
|
||||||
StdErr {
|
StdErr {
|
||||||
#[from]
|
#[from]
|
||||||
source: cosmwasm_std::StdError,
|
source: cosmwasm_std::StdError,
|
||||||
},
|
},
|
||||||
|
#[error("Division by zero at {}", line!())]
|
||||||
#[error("Provided percent value is greater than 100%")]
|
DivisionByZero,
|
||||||
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,90 +1,32 @@
|
|||||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
use crate::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::*;
|
pub use contracts_common::events::*;
|
||||||
use cosmwasm_std::{Addr, Coin, Decimal, Event};
|
// FIXME: This should becoma an Enum
|
||||||
|
// event types
|
||||||
pub const EVENT_VERSION_PREFIX: &str = "v2_";
|
pub const DELEGATION_EVENT_TYPE: &str = "delegation";
|
||||||
|
pub const PENDING_DELEGATION_EVENT_TYPE: &str = "pending_delegation";
|
||||||
pub enum MixnetEventType {
|
pub const RECONCILE_DELEGATION_EVENT_TYPE: &str = "reconcile_delegation";
|
||||||
MixnodeBonding,
|
pub const UNDELEGATION_EVENT_TYPE: &str = "undelegation";
|
||||||
GatewayBonding,
|
pub const PENDING_UNDELEGATION_EVENT_TYPE: &str = "pending_undelegation";
|
||||||
GatewayUnbonding,
|
pub const GATEWAY_BONDING_EVENT_TYPE: &str = "gateway_bonding";
|
||||||
PendingMixnodeUnbonding,
|
pub const GATEWAY_UNBONDING_EVENT_TYPE: &str = "gateway_unbonding";
|
||||||
MixnodeUnbonding,
|
pub const MIXNODE_BONDING_EVENT_TYPE: &str = "mixnode_bonding";
|
||||||
MixnodeConfigUpdate,
|
pub const MIXNODE_UNBONDING_EVENT_TYPE: &str = "mixnode_unbonding";
|
||||||
PendingMixnodeCostParamsUpdate,
|
pub const SETTINGS_UPDATE_EVENT_TYPE: &str = "settings_update";
|
||||||
MixnodeCostParamsUpdate,
|
pub const OPERATOR_REWARDING_EVENT_TYPE: &str = "mix_rewarding";
|
||||||
MixnodeRewarding,
|
pub const MIX_DELEGATORS_REWARDING_EVENT_TYPE: &str = "mix_delegators_rewarding";
|
||||||
WithdrawDelegatorReward,
|
pub const CHANGE_REWARDED_SET_EVENT_TYPE: &str = "change_rewarded_set";
|
||||||
WithdrawOperatorReward,
|
pub const ADVANCE_INTERVAL_EVENT_TYPE: &str = "advance_interval";
|
||||||
PendingActiveSetUpdate,
|
pub const ADVANCE_EPOCH_EVENT_TYPE: &str = "advance_epoch";
|
||||||
ActiveSetUpdate,
|
pub const COMPOUND_DELEGATOR_REWARD_EVENT_TYPE: &str = "compound_delegator_reward";
|
||||||
PendingIntervalRewardingParamsUpdate,
|
pub const CLAIM_DELEGATOR_REWARD_EVENT_TYPE: &str = "claim_delegator_reward";
|
||||||
IntervalRewardingParamsUpdate,
|
pub const COMPOUND_OPERATOR_REWARD_EVENT_TYPE: &str = "compound_operator_reward";
|
||||||
PendingDelegation,
|
pub const CLAIM_OPERATOR_REWARD_EVENT_TYPE: &str = "claim_operator_reward";
|
||||||
PendingUndelegation,
|
pub const SNAPSHOT_MIXNODES_EVENT: &str = "snapshot_mixnodes";
|
||||||
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
|
// attributes that are used in multiple places
|
||||||
pub const OWNER_KEY: &str = "owner";
|
pub const OWNER_KEY: &str = "owner";
|
||||||
@@ -99,44 +41,35 @@ pub const DELEGATION_TARGET_KEY: &str = "delegation_target";
|
|||||||
pub const DELEGATION_HEIGHT_KEY: &str = "delegation_latest_block_height";
|
pub const DELEGATION_HEIGHT_KEY: &str = "delegation_latest_block_height";
|
||||||
|
|
||||||
// bonding/unbonding
|
// bonding/unbonding
|
||||||
pub const MIX_ID_KEY: &str = "mix_id";
|
|
||||||
pub const NODE_IDENTITY_KEY: &str = "identity";
|
pub const NODE_IDENTITY_KEY: &str = "identity";
|
||||||
pub const ASSIGNED_LAYER_KEY: &str = "assigned_layer";
|
pub const ASSIGNED_LAYER_KEY: &str = "assigned_layer";
|
||||||
|
|
||||||
// settings change
|
// settings change
|
||||||
pub const OLD_MINIMUM_MIXNODE_PLEDGE_KEY: &str = "old_minimum_mixnode_pledge";
|
pub const OLD_MINIMUM_MIXNODE_PLEDGE_KEY: &str = "old_minimum_mixnode_pledge";
|
||||||
pub const OLD_MINIMUM_GATEWAY_PLEDGE_KEY: &str = "old_minimum_gateway_pledge";
|
pub const OLD_MINIMUM_GATEWAY_PLEDGE_KEY: &str = "old_minimum_gateway_pledge";
|
||||||
pub const OLD_MINIMUM_DELEGATION_KEY: &str = "old_minimum_delegation";
|
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 NEW_MINIMUM_MIXNODE_PLEDGE_KEY: &str = "new_minimum_mixnode_pledge";
|
pub const NEW_MINIMUM_MIXNODE_PLEDGE_KEY: &str = "new_minimum_mixnode_pledge";
|
||||||
pub const NEW_MINIMUM_GATEWAY_PLEDGE_KEY: &str = "new_minimum_gateway_pledge";
|
pub const NEW_MINIMUM_GATEWAY_PLEDGE_KEY: &str = "new_minimum_gateway_pledge";
|
||||||
pub const NEW_MINIMUM_DELEGATION_KEY: &str = "new_minimum_delegation";
|
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 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
|
// rewarding
|
||||||
pub const INTERVAL_KEY: &str = "interval_details";
|
pub const INTERVAL_ID_KEY: &str = "interval_id";
|
||||||
pub const TOTAL_MIXNODE_REWARD_KEY: &str = "total_node_reward";
|
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_PLEDGE_KEY: &str = "pledge";
|
||||||
pub const TOTAL_DELEGATIONS_KEY: &str = "delegated";
|
pub const TOTAL_DELEGATIONS_KEY: &str = "delegated";
|
||||||
pub const OPERATOR_REWARD_KEY: &str = "operator_reward";
|
pub const LAMBDA_KEY: &str = "lambda";
|
||||||
pub const DELEGATES_REWARD_KEY: &str = "delegates_reward";
|
pub const SIGMA_KEY: &str = "sigma";
|
||||||
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 DISTRIBUTED_DELEGATION_REWARDS_KEY: &str = "distributed_delegation_rewards";
|
||||||
pub const FURTHER_DELEGATIONS_TO_REWARD_KEY: &str = "further_delegations";
|
pub const FURTHER_DELEGATIONS_TO_REWARD_KEY: &str = "further_delegations";
|
||||||
pub const NO_REWARD_REASON_KEY: &str = "no_reward_reason";
|
pub const NO_REWARD_REASON_KEY: &str = "no_reward_reason";
|
||||||
pub const BOND_NOT_FOUND_VALUE: &str = "bond_not_found";
|
pub const BOND_NOT_FOUND_VALUE: &str = "bond_not_found";
|
||||||
pub const BOND_TOO_FRESH_VALUE: &str = "bond_too_fresh";
|
pub const BOND_TOO_FRESH_VALUE: &str = "bond_too_fresh";
|
||||||
pub const ZERO_PERFORMANCE_VALUE: &str = "zero_performance";
|
pub const ZERO_UPTIME_VALUE: &str = "zero_uptime";
|
||||||
|
|
||||||
// rewarded set update
|
// rewarded set update
|
||||||
pub const ACTIVE_SET_SIZE_KEY: &str = "active_set_size";
|
pub const ACTIVE_SET_SIZE_KEY: &str = "active_set_size";
|
||||||
@@ -147,140 +80,156 @@ pub const CURRENT_INTERVAL_ID_KEY: &str = "current_interval";
|
|||||||
pub const NEW_CURRENT_INTERVAL_KEY: &str = "new_current_interval";
|
pub const NEW_CURRENT_INTERVAL_KEY: &str = "new_current_interval";
|
||||||
pub const NEW_CURRENT_EPOCH_KEY: &str = "new_current_epoch";
|
pub const NEW_CURRENT_EPOCH_KEY: &str = "new_current_epoch";
|
||||||
pub const BLOCK_HEIGHT_KEY: &str = "block_height";
|
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 const RECONCILIATION_ERROR_EVENT: &str = "reconciliation_error";
|
||||||
|
|
||||||
// interval
|
pub fn new_checkpoint_mixnodes_event(block_height: u64) -> Event {
|
||||||
pub const EVENTS_EXECUTED_KEY: &str = "number_of_events_executed";
|
Event::new(CHECKPOINT_MIXNODES_EVENT)
|
||||||
pub const REWARDED_SET_NODES_KEY: &str = "rewarded_set_nodes";
|
.add_attribute(BLOCK_HEIGHT_KEY, format!("{}", block_height))
|
||||||
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_error_event(err: String) -> Event {
|
||||||
|
Event::new(RECONCILIATION_ERROR_EVENT).add_attribute("error", err)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn new_delegation_event(
|
pub fn new_delegation_event(
|
||||||
delegator: &Addr,
|
delegator: &Addr,
|
||||||
proxy: &Option<Addr>,
|
proxy: &Option<Addr>,
|
||||||
amount: &Coin,
|
amount: &Coin,
|
||||||
mix_id: NodeId,
|
mix_identity: IdentityKeyRef<'_>,
|
||||||
) -> Event {
|
) -> Event {
|
||||||
Event::new(MixnetEventType::Delegation)
|
let mut event = Event::new(DELEGATION_EVENT_TYPE).add_attribute(DELEGATOR_KEY, delegator);
|
||||||
.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_delegation_on_unbonded_node_event(
|
if let Some(proxy) = proxy {
|
||||||
delegator: &Addr,
|
event = event.add_attribute(PROXY_KEY, proxy)
|
||||||
proxy: &Option<Addr>,
|
}
|
||||||
mix_id: NodeId,
|
|
||||||
) -> Event {
|
// coin implements Display trait and we use that implementation here
|
||||||
Event::new(MixnetEventType::Delegation)
|
event
|
||||||
.add_attribute(DELEGATOR_KEY, delegator)
|
.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||||
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
|
.add_attribute(DELEGATION_TARGET_KEY, mix_identity)
|
||||||
.add_attribute(DELEGATION_TARGET_KEY, mix_id.to_string())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_pending_delegation_event(
|
pub fn new_pending_delegation_event(
|
||||||
delegator: &Addr,
|
delegator: &Addr,
|
||||||
proxy: &Option<Addr>,
|
proxy: &Option<Addr>,
|
||||||
amount: &Coin,
|
amount: &Coin,
|
||||||
mix_id: NodeId,
|
mix_identity: IdentityKeyRef<'_>,
|
||||||
) -> Event {
|
) -> Event {
|
||||||
Event::new(MixnetEventType::PendingDelegation)
|
let mut event =
|
||||||
.add_attribute(DELEGATOR_KEY, delegator)
|
Event::new(PENDING_DELEGATION_EVENT_TYPE).add_attribute(DELEGATOR_KEY, delegator);
|
||||||
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
|
|
||||||
|
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(AMOUNT_KEY, amount.to_string())
|
||||||
.add_attribute(DELEGATION_TARGET_KEY, mix_id.to_string())
|
.add_attribute(DELEGATION_TARGET_KEY, mix_identity)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_withdraw_operator_reward_event(
|
pub fn new_reconcile_delegation_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,
|
delegator: &Addr,
|
||||||
proxy: &Option<Addr>,
|
proxy: &Option<Addr>,
|
||||||
amount: Coin,
|
amount: &Coin,
|
||||||
mix_id: NodeId,
|
mix_identity: IdentityKeyRef<'_>,
|
||||||
) -> Event {
|
) -> Event {
|
||||||
Event::new(MixnetEventType::WithdrawDelegatorReward)
|
let mut event =
|
||||||
.add_attribute(DELEGATOR_KEY, delegator)
|
Event::new(RECONCILE_DELEGATION_EVENT_TYPE).add_attribute(DELEGATOR_KEY, delegator);
|
||||||
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
|
|
||||||
|
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(AMOUNT_KEY, amount.to_string())
|
||||||
.add_attribute(DELEGATION_TARGET_KEY, mix_id.to_string())
|
.add_attribute(DELEGATION_TARGET_KEY, mix_identity)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_active_set_update_event(new_size: u32) -> Event {
|
pub fn new_compound_operator_reward_event(owner: &Addr, amount: Uint128) -> Event {
|
||||||
Event::new(MixnetEventType::ActiveSetUpdate)
|
let event = Event::new(COMPOUND_OPERATOR_REWARD_EVENT_TYPE).add_attribute(OWNER_KEY, owner);
|
||||||
.add_attribute(ACTIVE_SET_SIZE_KEY, new_size.to_string())
|
event.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_pending_active_set_update_event(
|
pub fn new_claim_operator_reward_event(owner: &Addr, amount: Uint128) -> Event {
|
||||||
new_size: u32,
|
let event = Event::new(CLAIM_OPERATOR_REWARD_EVENT_TYPE).add_attribute(OWNER_KEY, owner);
|
||||||
approximate_time_remaining_secs: i64,
|
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 {
|
) -> Event {
|
||||||
Event::new(MixnetEventType::PendingActiveSetUpdate)
|
let mut event =
|
||||||
.add_attribute(ACTIVE_SET_SIZE_KEY, new_size.to_string())
|
Event::new(COMPOUND_DELEGATOR_REWARD_EVENT_TYPE).add_attribute(DELEGATOR_KEY, delegator);
|
||||||
.add_attribute(
|
|
||||||
APPROXIMATE_TIME_LEFT_SECS_KEY,
|
|
||||||
approximate_time_remaining_secs.to_string(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_rewarding_params_update_event(
|
if let Some(proxy) = proxy {
|
||||||
update: IntervalRewardingParamsUpdate,
|
event = event.add_attribute(PROXY_KEY, proxy)
|
||||||
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(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_pending_rewarding_params_update_event(
|
// coin implements Display trait and we use that implementation here
|
||||||
update: IntervalRewardingParamsUpdate,
|
event
|
||||||
approximate_time_remaining_secs: i64,
|
.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||||
) -> Event {
|
.add_attribute(DELEGATION_TARGET_KEY, mix_identity)
|
||||||
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_attribute(DELEGATOR_KEY, delegator)
|
||||||
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
|
}
|
||||||
.add_attribute(MIX_ID_KEY, mix_id.to_string())
|
|
||||||
|
pub fn new_claim_delegator_reward_event(
|
||||||
|
delegator: &Addr,
|
||||||
|
proxy: &Option<Addr>,
|
||||||
|
amount: Uint128,
|
||||||
|
mix_identity: IdentityKeyRef<'_>,
|
||||||
|
) -> 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)
|
||||||
|
.add_attribute(DELEGATOR_KEY, delegator)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_undelegation_event(
|
||||||
|
delegator: &Addr,
|
||||||
|
proxy: &Option<Addr>,
|
||||||
|
mix_identity: IdentityKeyRef<'_>,
|
||||||
|
amount: Uint128,
|
||||||
|
) -> Event {
|
||||||
|
let mut event = Event::new(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(AMOUNT_KEY, amount.to_string())
|
||||||
|
.add_attribute(DELEGATION_TARGET_KEY, mix_identity)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_pending_undelegation_event(
|
pub fn new_pending_undelegation_event(
|
||||||
delegator: &Addr,
|
delegator: &Addr,
|
||||||
proxy: &Option<Addr>,
|
proxy: &Option<Addr>,
|
||||||
mix_id: NodeId,
|
mix_identity: IdentityKeyRef<'_>,
|
||||||
) -> Event {
|
) -> Event {
|
||||||
Event::new(MixnetEventType::PendingUndelegation)
|
let mut event =
|
||||||
.add_attribute(DELEGATOR_KEY, delegator)
|
Event::new(PENDING_UNDELEGATION_EVENT_TYPE).add_attribute(DELEGATOR_KEY, delegator);
|
||||||
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
|
|
||||||
.add_attribute(MIX_ID_KEY, mix_id.to_string())
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_gateway_bonding_event(
|
pub fn new_gateway_bonding_event(
|
||||||
@@ -289,11 +238,16 @@ pub fn new_gateway_bonding_event(
|
|||||||
amount: &Coin,
|
amount: &Coin,
|
||||||
identity: IdentityKeyRef<'_>,
|
identity: IdentityKeyRef<'_>,
|
||||||
) -> Event {
|
) -> Event {
|
||||||
Event::new(MixnetEventType::GatewayBonding)
|
let mut event = Event::new(GATEWAY_BONDING_EVENT_TYPE)
|
||||||
.add_attribute(OWNER_KEY, owner)
|
.add_attribute(OWNER_KEY, owner)
|
||||||
.add_attribute(NODE_IDENTITY_KEY, identity)
|
.add_attribute(NODE_IDENTITY_KEY, identity);
|
||||||
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
|
|
||||||
.add_attribute(AMOUNT_KEY, amount.to_string())
|
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())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_gateway_unbonding_event(
|
pub fn new_gateway_unbonding_event(
|
||||||
@@ -302,11 +256,16 @@ pub fn new_gateway_unbonding_event(
|
|||||||
amount: &Coin,
|
amount: &Coin,
|
||||||
identity: IdentityKeyRef<'_>,
|
identity: IdentityKeyRef<'_>,
|
||||||
) -> Event {
|
) -> Event {
|
||||||
Event::new(MixnetEventType::GatewayUnbonding)
|
let mut event = Event::new(GATEWAY_UNBONDING_EVENT_TYPE)
|
||||||
.add_attribute(OWNER_KEY, owner)
|
.add_attribute(OWNER_KEY, owner)
|
||||||
.add_attribute(NODE_IDENTITY_KEY, identity)
|
.add_attribute(NODE_IDENTITY_KEY, identity);
|
||||||
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
|
|
||||||
.add_attribute(AMOUNT_KEY, amount.to_string())
|
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())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_mixnode_bonding_event(
|
pub fn new_mixnode_bonding_event(
|
||||||
@@ -314,92 +273,55 @@ pub fn new_mixnode_bonding_event(
|
|||||||
proxy: &Option<Addr>,
|
proxy: &Option<Addr>,
|
||||||
amount: &Coin,
|
amount: &Coin,
|
||||||
identity: IdentityKeyRef<'_>,
|
identity: IdentityKeyRef<'_>,
|
||||||
mix_id: NodeId,
|
|
||||||
assigned_layer: Layer,
|
assigned_layer: Layer,
|
||||||
) -> Event {
|
) -> Event {
|
||||||
// coin implements Display trait and we use that implementation here
|
let mut event = Event::new(MIXNODE_BONDING_EVENT_TYPE)
|
||||||
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_attribute(OWNER_KEY, owner)
|
||||||
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
|
.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(ASSIGNED_LAYER_KEY, assigned_layer)
|
.add_attribute(ASSIGNED_LAYER_KEY, assigned_layer)
|
||||||
.add_attribute(AMOUNT_KEY, amount.to_string())
|
.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_mixnode_unbonding_event(mix_id: NodeId) -> Event {
|
pub fn new_mixnode_unbonding_event(
|
||||||
Event::new(MixnetEventType::MixnodeUnbonding).add_attribute(MIX_ID_KEY, mix_id.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_pending_mixnode_unbonding_event(
|
|
||||||
owner: &Addr,
|
owner: &Addr,
|
||||||
proxy: &Option<Addr>,
|
proxy: &Option<Addr>,
|
||||||
|
amount: &Coin,
|
||||||
identity: IdentityKeyRef<'_>,
|
identity: IdentityKeyRef<'_>,
|
||||||
mix_id: NodeId,
|
|
||||||
) -> Event {
|
) -> Event {
|
||||||
Event::new(MixnetEventType::PendingMixnodeUnbonding)
|
let mut event = Event::new(MIXNODE_UNBONDING_EVENT_TYPE)
|
||||||
.add_attribute(MIX_ID_KEY, mix_id.to_string())
|
|
||||||
.add_attribute(NODE_IDENTITY_KEY, identity)
|
|
||||||
.add_attribute(OWNER_KEY, owner)
|
.add_attribute(OWNER_KEY, owner)
|
||||||
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
|
.add_attribute(NODE_IDENTITY_KEY, identity);
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_mixnode_config_update_event(
|
if let Some(proxy) = proxy {
|
||||||
mix_id: NodeId,
|
event = event.add_attribute(PROXY_KEY, proxy)
|
||||||
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())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_mixnode_pending_cost_params_update_event(
|
// coin implements Display trait and we use that implementation here
|
||||||
mix_id: NodeId,
|
event.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||||
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(
|
pub fn new_settings_update_event(
|
||||||
old_params: &ContractStateParams,
|
old_params: &ContractStateParams,
|
||||||
new_params: &ContractStateParams,
|
new_params: &ContractStateParams,
|
||||||
) -> Event {
|
) -> Event {
|
||||||
let mut event = Event::new(MixnetEventType::ContractSettingsUpdate);
|
let mut event = Event::new(SETTINGS_UPDATE_EVENT_TYPE);
|
||||||
|
|
||||||
if old_params.minimum_mixnode_pledge != new_params.minimum_mixnode_pledge {
|
if old_params.minimum_mixnode_pledge != new_params.minimum_mixnode_pledge {
|
||||||
event = event
|
event = event
|
||||||
.add_attribute(
|
.add_attribute(
|
||||||
OLD_MINIMUM_MIXNODE_PLEDGE_KEY,
|
OLD_MINIMUM_MIXNODE_PLEDGE_KEY,
|
||||||
old_params.minimum_mixnode_pledge.to_string(),
|
old_params.minimum_mixnode_pledge,
|
||||||
)
|
)
|
||||||
.add_attribute(
|
.add_attribute(
|
||||||
NEW_MINIMUM_MIXNODE_PLEDGE_KEY,
|
NEW_MINIMUM_MIXNODE_PLEDGE_KEY,
|
||||||
new_params.minimum_mixnode_pledge.to_string(),
|
new_params.minimum_mixnode_pledge,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -407,128 +329,128 @@ pub fn new_settings_update_event(
|
|||||||
event = event
|
event = event
|
||||||
.add_attribute(
|
.add_attribute(
|
||||||
OLD_MINIMUM_GATEWAY_PLEDGE_KEY,
|
OLD_MINIMUM_GATEWAY_PLEDGE_KEY,
|
||||||
old_params.minimum_gateway_pledge.to_string(),
|
old_params.minimum_gateway_pledge,
|
||||||
)
|
)
|
||||||
.add_attribute(
|
.add_attribute(
|
||||||
NEW_MINIMUM_GATEWAY_PLEDGE_KEY,
|
NEW_MINIMUM_GATEWAY_PLEDGE_KEY,
|
||||||
new_params.minimum_gateway_pledge.to_string(),
|
new_params.minimum_gateway_pledge,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if old_params.minimum_mixnode_delegation != new_params.minimum_mixnode_delegation {
|
if old_params.mixnode_rewarded_set_size != new_params.mixnode_rewarded_set_size {
|
||||||
if let Some(ref old) = old_params.minimum_mixnode_delegation {
|
event = event
|
||||||
event = event.add_attribute(OLD_MINIMUM_DELEGATION_KEY, old.to_string())
|
.add_attribute(
|
||||||
} else {
|
OLD_MIXNODE_REWARDED_SET_SIZE_KEY,
|
||||||
event = event.add_attribute(OLD_MINIMUM_DELEGATION_KEY, "None")
|
old_params.mixnode_rewarded_set_size.to_string(),
|
||||||
}
|
)
|
||||||
if let Some(ref new) = new_params.minimum_mixnode_delegation {
|
.add_attribute(
|
||||||
event = event.add_attribute(NEW_MINIMUM_DELEGATION_KEY, new.to_string())
|
NEW_MIXNODE_REWARDED_SET_SIZE_KEY,
|
||||||
} else {
|
new_params.mixnode_rewarded_set_size.to_string(),
|
||||||
event = event.add_attribute(NEW_MINIMUM_DELEGATION_KEY, "None")
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
event
|
event
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_not_found_mix_operator_rewarding_event(interval: Interval, mix_id: NodeId) -> Event {
|
pub fn new_not_found_mix_operator_rewarding_event(
|
||||||
Event::new(MixnetEventType::MixnodeRewarding)
|
interval_id: u32,
|
||||||
.add_attribute(
|
identity: IdentityKeyRef<'_>,
|
||||||
INTERVAL_KEY,
|
) -> Event {
|
||||||
interval.current_epoch_absolute_id().to_string(),
|
Event::new(OPERATOR_REWARDING_EVENT_TYPE)
|
||||||
)
|
.add_attribute(INTERVAL_ID_KEY, interval_id.to_string())
|
||||||
.add_attribute(MIX_ID_KEY, mix_id.to_string())
|
.add_attribute(NODE_IDENTITY_KEY, identity)
|
||||||
.add_attribute(NO_REWARD_REASON_KEY, BOND_NOT_FOUND_VALUE)
|
.add_attribute(NO_REWARD_REASON_KEY, BOND_NOT_FOUND_VALUE)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_zero_uptime_mix_operator_rewarding_event(interval: Interval, mix_id: NodeId) -> Event {
|
pub fn new_too_fresh_bond_mix_operator_rewarding_event(
|
||||||
Event::new(MixnetEventType::MixnodeRewarding)
|
interval_id: u32,
|
||||||
.add_attribute(
|
identity: IdentityKeyRef<'_>,
|
||||||
INTERVAL_KEY,
|
|
||||||
interval.current_epoch_absolute_id().to_string(),
|
|
||||||
)
|
|
||||||
.add_attribute(MIX_ID_KEY, mix_id.to_string())
|
|
||||||
.add_attribute(NO_REWARD_REASON_KEY, ZERO_PERFORMANCE_VALUE)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_mix_rewarding_event(
|
|
||||||
interval: Interval,
|
|
||||||
mix_id: NodeId,
|
|
||||||
reward_distribution: RewardDistribution,
|
|
||||||
prior_delegates: Decimal,
|
|
||||||
prior_unit_delegation: Decimal,
|
|
||||||
) -> Event {
|
) -> Event {
|
||||||
Event::new(MixnetEventType::MixnodeRewarding)
|
Event::new(OPERATOR_REWARDING_EVENT_TYPE)
|
||||||
.add_attribute(
|
.add_attribute(INTERVAL_ID_KEY, interval_id.to_string())
|
||||||
INTERVAL_KEY,
|
.add_attribute(NODE_IDENTITY_KEY, identity)
|
||||||
interval.current_epoch_absolute_id().to_string(),
|
.add_attribute(NO_REWARD_REASON_KEY, BOND_TOO_FRESH_VALUE)
|
||||||
)
|
|
||||||
.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(
|
|
||||||
DELEGATES_REWARD_KEY,
|
|
||||||
reward_distribution.delegates.to_string(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_advance_epoch_event(interval: Interval, rewarded_nodes: u32) -> Event {
|
pub fn new_zero_uptime_mix_operator_rewarding_event(
|
||||||
Event::new(MixnetEventType::AdvanceEpoch)
|
interval_id: u32,
|
||||||
.add_attribute(
|
identity: IdentityKeyRef<'_>,
|
||||||
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 {
|
||||||
Event::new(MixnetEventType::IntervalConfigUpdate)
|
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)
|
||||||
.add_attribute(
|
.add_attribute(
|
||||||
NEW_EPOCHS_DURATION_SECS_KEY,
|
TOTAL_MIXNODE_REWARD_KEY,
|
||||||
epoch_duration_secs.to_string(),
|
node_reward_result.reward().to_string(),
|
||||||
)
|
)
|
||||||
.add_attribute(NEW_EPOCHS_IN_INTERVAL, epochs_in_interval.to_string())
|
.add_attribute(LAMBDA_KEY, node_reward_result.lambda().to_string())
|
||||||
|
.add_attribute(SIGMA_KEY, node_reward_result.sigma().to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_mix_delegators_rewarding_event(
|
||||||
|
interval_id: u32,
|
||||||
|
identity: IdentityKeyRef<'_>,
|
||||||
|
delegation_rewards_distributed: Uint128,
|
||||||
|
further_delegations: bool,
|
||||||
|
) -> Event {
|
||||||
|
Event::new(MIX_DELEGATORS_REWARDING_EVENT_TYPE)
|
||||||
|
.add_attribute(INTERVAL_ID_KEY, interval_id.to_string())
|
||||||
|
.add_attribute(NODE_IDENTITY_KEY, identity)
|
||||||
.add_attribute(
|
.add_attribute(
|
||||||
UPDATED_INTERVAL_REWARDING_PARAMS_KEY,
|
DISTRIBUTED_DELEGATION_REWARDS_KEY,
|
||||||
updated_rewarding_params.to_inline_json(),
|
delegation_rewards_distributed,
|
||||||
|
)
|
||||||
|
.add_attribute(
|
||||||
|
FURTHER_DELEGATIONS_TO_REWARD_KEY,
|
||||||
|
further_delegations.to_string(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_pending_interval_config_update_event(
|
// note that when this event is emitted, we'll know the current block height
|
||||||
epochs_in_interval: u32,
|
pub fn new_change_rewarded_set_event(
|
||||||
epoch_duration_secs: u64,
|
active_set_size: u32,
|
||||||
approximate_time_remaining_secs: i64,
|
rewarded_set_size: u32,
|
||||||
|
nodes_in_rewarded_set: u32,
|
||||||
) -> Event {
|
) -> Event {
|
||||||
Event::new(MixnetEventType::PendingIntervalConfigUpdate)
|
Event::new(CHANGE_REWARDED_SET_EVENT_TYPE)
|
||||||
.add_attribute(
|
.add_attribute(ACTIVE_SET_SIZE_KEY, active_set_size.to_string())
|
||||||
NEW_EPOCHS_DURATION_SECS_KEY,
|
.add_attribute(REWARDED_SET_SIZE_KEY, rewarded_set_size.to_string())
|
||||||
epoch_duration_secs.to_string(),
|
.add_attribute(NODES_IN_REWARDED_SET_KEY, nodes_in_rewarded_set.to_string())
|
||||||
)
|
}
|
||||||
.add_attribute(NEW_EPOCHS_IN_INTERVAL, epochs_in_interval.to_string())
|
|
||||||
.add_attribute(
|
pub fn new_advance_interval_event(interval: Interval) -> Event {
|
||||||
APPROXIMATE_TIME_LEFT_SECS_KEY,
|
Event::new(ADVANCE_INTERVAL_EVENT_TYPE)
|
||||||
approximate_time_remaining_secs.to_string(),
|
.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())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
// due to code generated by JsonSchema
|
// due to code generated by JsonSchema
|
||||||
#![allow(clippy::field_reassign_with_default)]
|
#![allow(clippy::field_reassign_with_default)]
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::pending_events::{PendingEpochEvent, PendingIntervalEvent};
|
|
||||||
use crate::{EpochId, IntervalId};
|
|
||||||
use cosmwasm_std::Env;
|
use cosmwasm_std::Env;
|
||||||
use schemars::gen::SchemaGenerator;
|
use schemars::gen::SchemaGenerator;
|
||||||
use schemars::schema::{InstanceType, Schema, SchemaObject};
|
use schemars::schema::{InstanceType, Schema, SchemaObject};
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::convert::TryInto;
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
@@ -60,16 +59,12 @@ pub(crate) mod string_rfc3339_offset_date_time {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, Serialize)]
|
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, PartialOrd, Serialize)]
|
||||||
pub struct Interval {
|
pub struct Interval {
|
||||||
id: IntervalId,
|
id: u32,
|
||||||
epochs_in_interval: u32,
|
|
||||||
|
|
||||||
#[serde(with = "string_rfc3339_offset_date_time")]
|
#[serde(with = "string_rfc3339_offset_date_time")]
|
||||||
current_epoch_start: OffsetDateTime,
|
start: OffsetDateTime,
|
||||||
current_epoch_id: EpochId,
|
length: Duration,
|
||||||
epoch_length: Duration,
|
|
||||||
total_elapsed_epochs: EpochId,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JsonSchema for Interval {
|
impl JsonSchema for Interval {
|
||||||
@@ -86,46 +81,20 @@ impl JsonSchema for Interval {
|
|||||||
let object_validation = schema_object.object();
|
let object_validation = schema_object.object();
|
||||||
object_validation
|
object_validation
|
||||||
.properties
|
.properties
|
||||||
.insert("id".to_owned(), gen.subschema_for::<IntervalId>());
|
.insert("id".to_owned(), gen.subschema_for::<u32>());
|
||||||
object_validation.required.insert("id".to_owned());
|
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
|
// PrimitiveDateTime does not implement JsonSchema. However it has a custom
|
||||||
// serialization to string, so we just specify the schema to be String.
|
// 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
|
object_validation
|
||||||
.required
|
.properties
|
||||||
.insert("current_epoch_start".to_owned());
|
.insert("start".to_owned(), gen.subschema_for::<String>());
|
||||||
|
object_validation.required.insert("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
|
object_validation
|
||||||
.properties
|
.properties
|
||||||
.insert("epoch_length".to_owned(), gen.subschema_for::<Duration>());
|
.insert("length".to_owned(), gen.subschema_for::<Duration>());
|
||||||
object_validation.required.insert("epoch_length".to_owned());
|
object_validation.required.insert("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)
|
Schema::Object(schema_object)
|
||||||
}
|
}
|
||||||
@@ -133,451 +102,328 @@ impl JsonSchema for Interval {
|
|||||||
|
|
||||||
impl Interval {
|
impl Interval {
|
||||||
/// Initialize epoch in the contract with default values.
|
/// Initialize epoch in the contract with default values.
|
||||||
pub fn init_interval(epochs_in_interval: u32, epoch_length: Duration, env: &Env) -> Self {
|
pub fn init_epoch(env: Env) -> Self {
|
||||||
Interval {
|
Interval {
|
||||||
id: 0,
|
id: 0,
|
||||||
epochs_in_interval,
|
|
||||||
// I really don't see a way for this to fail, unless the blockchain is lying to us
|
// I really don't see a way for this to fail, unless the blockchain is lying to us
|
||||||
current_epoch_start: OffsetDateTime::from_unix_timestamp(
|
start: OffsetDateTime::from_unix_timestamp(env.block.time.seconds() as i64)
|
||||||
env.block.time.seconds() as i64
|
.expect("Invalid timestamp from env.block.time"),
|
||||||
)
|
length: Duration::from_secs(3600),
|
||||||
.expect("Invalid timestamp from env.block.time"),
|
|
||||||
current_epoch_id: 0,
|
|
||||||
epoch_length,
|
|
||||||
total_elapsed_epochs: 0,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn current_epoch_id(&self) -> EpochId {
|
pub fn is_over(&self, env: Env) -> bool {
|
||||||
self.current_epoch_id
|
self.end_unix_timestamp() <= env.block.time.seconds() as i64
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn current_interval_id(&self) -> IntervalId {
|
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 {
|
||||||
self.id
|
self.id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn epochs_in_interval(&self) -> u32 {
|
/// Determines amount of time left until this interval finishes.
|
||||||
self.epochs_in_interval
|
///
|
||||||
}
|
/// # Arguments
|
||||||
|
///
|
||||||
pub fn force_change_epochs_in_interval(&mut self, epochs_in_interval: u32) {
|
/// * `now`: current datetime
|
||||||
self.epochs_in_interval = epochs_in_interval;
|
pub fn until_end(&self, now: OffsetDateTime) -> Option<Duration> {
|
||||||
if self.current_epoch_id >= epochs_in_interval {
|
let remaining = self.end() - now;
|
||||||
// we have to go to the next interval as we can't
|
if remaining.is_negative() {
|
||||||
// have the same (interval, epoch) combo as we had in the past
|
None
|
||||||
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 {
|
} else {
|
||||||
self.current_epoch_end_unix_timestamp() - env.block.time.seconds() as i64
|
remaining.try_into().ok()
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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.
|
/// Returns the starting datetime of this interval.
|
||||||
pub const fn current_epoch_start(&self) -> OffsetDateTime {
|
pub const fn start(&self) -> OffsetDateTime {
|
||||||
self.current_epoch_start
|
self.start
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the length of this interval.
|
/// Returns the length of this interval.
|
||||||
pub const fn epoch_length(&self) -> Duration {
|
pub const fn length(&self) -> Duration {
|
||||||
self.epoch_length
|
self.length
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the ending datetime of the current epoch.
|
/// Returns the ending datetime of this interval.
|
||||||
pub fn current_epoch_end(&self) -> OffsetDateTime {
|
pub fn end(&self) -> OffsetDateTime {
|
||||||
self.current_epoch_start + self.epoch_length
|
self.start + self.length
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn epochs_until_interval_end(&self) -> u32 {
|
/// Returns the unix timestamp of the start of this interval.
|
||||||
self.epochs_in_interval - self.current_epoch_id
|
pub const fn start_unix_timestamp(&self) -> i64 {
|
||||||
|
self.start().unix_timestamp()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the ending datetime of the current interval.
|
/// Returns the unix timestamp of the end of this interval.
|
||||||
pub fn current_interval_end(&self) -> OffsetDateTime {
|
pub fn end_unix_timestamp(&self) -> i64 {
|
||||||
self.current_epoch_start + self.epochs_until_interval_end() * self.epoch_length
|
self.end().unix_timestamp()
|
||||||
}
|
|
||||||
|
|
||||||
/// 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 {
|
impl Display for Interval {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
let length = self.epoch_length_secs();
|
let length = self.length().as_secs();
|
||||||
let full_hours = length / 3600;
|
let full_hours = length / 3600;
|
||||||
let rem = length % 3600;
|
let rem = length % 3600;
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"Interval {}: epoch {}/{} (current epoch begun at: {}; epoch lengths: {}h {}s)",
|
"Interval {}: {} - {} ({}h {}s)",
|
||||||
self.id,
|
self.id,
|
||||||
self.current_epoch_id + 1,
|
self.start(),
|
||||||
self.epochs_in_interval,
|
self.end(),
|
||||||
self.current_epoch_start,
|
|
||||||
full_hours,
|
full_hours,
|
||||||
rem
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use cosmwasm_std::testing::mock_env;
|
|
||||||
use rand_chacha::rand_core::{RngCore, SeedableRng};
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn advancing_epoch() {
|
fn previous() {
|
||||||
// just advancing epoch
|
|
||||||
let interval = Interval {
|
let interval = Interval {
|
||||||
id: 0,
|
id: 1,
|
||||||
epochs_in_interval: 100,
|
start: time::macros::datetime!(2021-08-23 12:00 UTC),
|
||||||
current_epoch_start: time::macros::datetime!(2021-08-23 12:00 UTC),
|
length: Duration::from_secs(24 * 60 * 60),
|
||||||
current_epoch_id: 23,
|
|
||||||
epoch_length: Duration::from_secs(60 * 60),
|
|
||||||
total_elapsed_epochs: 0,
|
|
||||||
};
|
};
|
||||||
let expected = Interval {
|
let expected = Interval {
|
||||||
id: 0,
|
id: 0,
|
||||||
epochs_in_interval: 100,
|
start: time::macros::datetime!(2021-08-22 12:00 UTC),
|
||||||
current_epoch_start: time::macros::datetime!(2021-08-23 13:00 UTC),
|
length: Duration::from_secs(24 * 60 * 60),
|
||||||
current_epoch_id: 24,
|
|
||||||
epoch_length: Duration::from_secs(60 * 60),
|
|
||||||
total_elapsed_epochs: 1,
|
|
||||||
};
|
};
|
||||||
assert_eq!(expected, interval.advance_epoch());
|
assert_eq!(expected, interval.previous().unwrap());
|
||||||
|
|
||||||
// results in advancing interval
|
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() {
|
||||||
let interval = Interval {
|
let interval = Interval {
|
||||||
id: 0,
|
id: 0,
|
||||||
epochs_in_interval: 100,
|
start: time::macros::datetime!(2021-08-23 12:00 UTC),
|
||||||
current_epoch_start: time::macros::datetime!(2021-08-23 12:00 UTC),
|
length: Duration::from_secs(24 * 60 * 60),
|
||||||
current_epoch_id: 99,
|
|
||||||
epoch_length: Duration::from_secs(60 * 60),
|
|
||||||
total_elapsed_epochs: 42,
|
|
||||||
};
|
};
|
||||||
let expected = Interval {
|
let expected = Interval {
|
||||||
id: 1,
|
id: 1,
|
||||||
epochs_in_interval: 100,
|
start: time::macros::datetime!(2021-08-24 12:00 UTC),
|
||||||
current_epoch_start: time::macros::datetime!(2021-08-23 13:00 UTC),
|
length: Duration::from_secs(24 * 60 * 60),
|
||||||
current_epoch_id: 0,
|
|
||||||
epoch_length: Duration::from_secs(60 * 60),
|
|
||||||
total_elapsed_epochs: 43,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(expected, interval.advance_epoch())
|
assert_eq!(expected, interval.next())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn checking_for_epoch_ends() {
|
fn checking_for_datetime_inclusion() {
|
||||||
let env = mock_env();
|
|
||||||
|
|
||||||
// epoch just begun
|
|
||||||
let interval = Interval {
|
let interval = Interval {
|
||||||
id: 0,
|
id: 100,
|
||||||
epochs_in_interval: 100,
|
start: time::macros::datetime!(2021-08-23 12:00 UTC),
|
||||||
current_epoch_start: OffsetDateTime::from_unix_timestamp(
|
length: Duration::from_secs(24 * 60 * 60),
|
||||||
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));
|
|
||||||
|
|
||||||
// current time == current epoch start
|
// it must contain its own boundaries
|
||||||
let mut interval = interval;
|
assert!(interval.contains(interval.start));
|
||||||
interval.current_epoch_start =
|
assert!(interval.contains(interval.end()));
|
||||||
OffsetDateTime::from_unix_timestamp(env.block.time.seconds() as i64).unwrap();
|
|
||||||
assert!(!interval.is_current_epoch_over(&env));
|
|
||||||
|
|
||||||
// epoch HASN'T yet begun (weird edge case, but can happen if we decide to manually adjust things)
|
let in_the_midle = interval.start + Duration::from_secs(interval.length.as_secs() / 2);
|
||||||
let mut interval = interval;
|
assert!(interval.contains(in_the_midle));
|
||||||
interval.current_epoch_start =
|
|
||||||
OffsetDateTime::from_unix_timestamp(env.block.time.seconds() as i64 + 100).unwrap();
|
|
||||||
assert!(!interval.is_current_epoch_over(&env));
|
|
||||||
|
|
||||||
// current_time = EXACTLY end of the epoch
|
assert!(!interval.contains(interval.next().end()));
|
||||||
let mut interval = interval;
|
assert!(!interval.contains(interval.previous().unwrap().start()));
|
||||||
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]
|
#[test]
|
||||||
fn interval_end() {
|
fn determining_current_interval() {
|
||||||
let mut interval = Interval {
|
let first_interval = Interval {
|
||||||
id: 0,
|
id: 100,
|
||||||
epochs_in_interval: 100,
|
start: time::macros::datetime!(2021-08-23 12:00 UTC),
|
||||||
current_epoch_start: time::macros::datetime!(2021-08-23 12:00 UTC),
|
length: Duration::from_secs(24 * 60 * 60),
|
||||||
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!(
|
assert_eq!(
|
||||||
interval.current_epoch_start + interval.epoch_length,
|
first_interval,
|
||||||
interval.current_interval_end()
|
first_interval.current(first_interval.start).unwrap()
|
||||||
);
|
);
|
||||||
|
|
||||||
interval.current_epoch_id -= 1;
|
// 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)
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
interval.current_epoch_start + 2 * interval.epoch_length,
|
first_interval,
|
||||||
interval.current_interval_end()
|
first_interval.current(first_interval.end()).unwrap()
|
||||||
);
|
);
|
||||||
|
|
||||||
interval.current_epoch_id -= 10;
|
// next interval
|
||||||
|
let fake_now = first_interval.end() + Duration::from_secs(123);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
interval.current_epoch_start + 12 * interval.epoch_length,
|
first_interval.next(),
|
||||||
interval.current_interval_end()
|
first_interval.current(fake_now).unwrap()
|
||||||
);
|
);
|
||||||
|
|
||||||
interval.current_epoch_id = 0;
|
// few intervals in the past
|
||||||
|
let fake_now = first_interval.start()
|
||||||
|
- first_interval.length
|
||||||
|
- first_interval.length
|
||||||
|
- first_interval.length;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
interval.current_epoch_start + interval.epochs_in_interval * interval.epoch_length,
|
first_interval
|
||||||
interval.current_interval_end()
|
.previous()
|
||||||
|
.unwrap()
|
||||||
|
.previous()
|
||||||
|
.unwrap()
|
||||||
|
.previous()
|
||||||
|
.unwrap(),
|
||||||
|
first_interval.current(fake_now).unwrap()
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
// few intervals in the future
|
||||||
fn checking_for_interval_ends() {
|
let fake_now = first_interval.end()
|
||||||
let env = mock_env();
|
+ first_interval.length
|
||||||
|
+ first_interval.length
|
||||||
let epoch_length = Duration::from_secs(60 * 60);
|
+ first_interval.length;
|
||||||
|
assert_eq!(
|
||||||
let mut interval = Interval {
|
first_interval.next().next().next(),
|
||||||
id: 0,
|
first_interval.current(fake_now).unwrap()
|
||||||
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,7 +1,6 @@
|
|||||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
mod constants;
|
|
||||||
pub mod delegation;
|
pub mod delegation;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod events;
|
pub mod events;
|
||||||
@@ -9,31 +8,29 @@ pub mod gateway;
|
|||||||
mod interval;
|
mod interval;
|
||||||
pub mod mixnode;
|
pub mod mixnode;
|
||||||
mod msg;
|
mod msg;
|
||||||
pub mod pending_events;
|
|
||||||
pub mod reward_params;
|
pub mod reward_params;
|
||||||
pub mod rewarding;
|
|
||||||
mod types;
|
mod types;
|
||||||
|
|
||||||
pub use contracts_common::types::*;
|
pub const MIXNODE_DELEGATORS_PAGE_LIMIT: usize = 250;
|
||||||
pub use cosmwasm_std::{Addr, Coin, Decimal, Fraction};
|
|
||||||
|
pub use cosmwasm_std::{Addr, Coin};
|
||||||
pub use delegation::{
|
pub use delegation::{
|
||||||
Delegation, PagedAllDelegationsResponse, PagedDelegatorDelegationsResponse,
|
Delegation, PagedAllDelegationsResponse, PagedDelegatorDelegationsResponse,
|
||||||
PagedMixNodeDelegationsResponse,
|
PagedMixDelegationsResponse,
|
||||||
};
|
};
|
||||||
pub use gateway::{
|
pub use gateway::{
|
||||||
Gateway, GatewayBond, GatewayBondResponse, GatewayOwnershipResponse, PagedGatewayResponse,
|
Gateway, GatewayBond, GatewayBondResponse, GatewayOwnershipResponse, PagedGatewayResponse,
|
||||||
};
|
};
|
||||||
pub use interval::{
|
pub use interval::Interval;
|
||||||
CurrentIntervalResponse, Interval, PendingEpochEventsResponse, PendingIntervalEventsResponse,
|
|
||||||
};
|
|
||||||
pub use mixnode::{
|
pub use mixnode::{
|
||||||
Layer, MixNode, MixNodeBond, MixNodeConfigUpdate, MixNodeCostParams, MixNodeDetails,
|
Layer, MixNode, MixNodeBond, MixOwnershipResponse, MixnodeBondResponse, PagedMixnodeResponse,
|
||||||
MixNodeRewarding, MixOwnershipResponse, MixnodeDetailsResponse, PagedMixnodeBondsResponse,
|
RewardedSetNodeStatus,
|
||||||
RewardedSetNodeStatus, UnbondedMixnode,
|
|
||||||
};
|
};
|
||||||
pub use msg::*;
|
pub use msg::*;
|
||||||
pub use pending_events::{
|
|
||||||
PendingEpochEvent, PendingEpochEventData, PendingIntervalEvent, PendingIntervalEventData,
|
|
||||||
};
|
|
||||||
pub use reward_params::{IntervalRewardParams, IntervalRewardingParamsUpdate, RewardingParams};
|
|
||||||
pub use types::*;
|
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,342 +1,160 @@
|
|||||||
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::delegation::OwnerProxySubKey;
|
use crate::reward_params::NodeRewardParams;
|
||||||
use crate::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
|
use crate::ContractStateParams;
|
||||||
use crate::reward_params::{
|
|
||||||
IntervalRewardParams, IntervalRewardingParamsUpdate, Performance, RewardingParams,
|
|
||||||
};
|
|
||||||
use crate::{delegation, ContractStateParams, NodeId, Percent};
|
|
||||||
use crate::{Gateway, IdentityKey, MixNode};
|
use crate::{Gateway, IdentityKey, MixNode};
|
||||||
use cosmwasm_std::Decimal;
|
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||||
#[serde(rename_all = "snake_case")]
|
|
||||||
pub struct InstantiateMsg {
|
pub struct InstantiateMsg {
|
||||||
pub rewarding_validator_address: String,
|
pub rewarding_validator_address: String,
|
||||||
pub vesting_contract_address: String,
|
pub mixnet_denom: 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)]
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[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 {
|
pub enum ExecuteMsg {
|
||||||
// state/sys-params-related
|
|
||||||
UpdateRewardingValidatorAddress {
|
UpdateRewardingValidatorAddress {
|
||||||
address: String,
|
address: String,
|
||||||
},
|
},
|
||||||
UpdateContractStateParams {
|
InitEpoch {},
|
||||||
updated_parameters: ContractStateParams,
|
ReconcileDelegations {},
|
||||||
|
CheckpointMixnodes {},
|
||||||
|
CompoundOperatorRewardOnBehalf {
|
||||||
|
owner: String,
|
||||||
},
|
},
|
||||||
UpdateActiveSetSize {
|
CompoundDelegatorRewardOnBehalf {
|
||||||
active_set_size: u32,
|
owner: String,
|
||||||
force_immediately: bool,
|
mix_identity: IdentityKey,
|
||||||
},
|
},
|
||||||
UpdateRewardingParams {
|
CompoundOperatorReward {},
|
||||||
updated_params: IntervalRewardingParamsUpdate,
|
CompoundDelegatorReward {
|
||||||
force_immediately: bool,
|
mix_identity: IdentityKey,
|
||||||
},
|
},
|
||||||
UpdateIntervalConfig {
|
CompoundReward {
|
||||||
epochs_in_interval: u32,
|
operator: Option<String>,
|
||||||
epoch_duration_secs: u64,
|
delegator: Option<String>,
|
||||||
force_immediately: bool,
|
mix_identity: Option<IdentityKey>,
|
||||||
|
proxy: Option<String>,
|
||||||
},
|
},
|
||||||
AdvanceCurrentEpoch {
|
|
||||||
new_rewarded_set: Vec<NodeId>,
|
|
||||||
expected_active_set_size: u32,
|
|
||||||
},
|
|
||||||
ReconcileEpochEvents {
|
|
||||||
limit: Option<u32>,
|
|
||||||
},
|
|
||||||
|
|
||||||
// mixnode-related:
|
|
||||||
BondMixnode {
|
BondMixnode {
|
||||||
mix_node: MixNode,
|
mix_node: MixNode,
|
||||||
cost_params: MixNodeCostParams,
|
|
||||||
owner_signature: String,
|
owner_signature: String,
|
||||||
},
|
},
|
||||||
BondMixnodeOnBehalf {
|
|
||||||
mix_node: MixNode,
|
|
||||||
cost_params: MixNodeCostParams,
|
|
||||||
owner_signature: String,
|
|
||||||
owner: String,
|
|
||||||
},
|
|
||||||
UnbondMixnode {},
|
UnbondMixnode {},
|
||||||
UnbondMixnodeOnBehalf {
|
|
||||||
owner: String,
|
|
||||||
},
|
|
||||||
UpdateMixnodeCostParams {
|
|
||||||
new_costs: MixNodeCostParams,
|
|
||||||
},
|
|
||||||
UpdateMixnodeCostParamsOnBehalf {
|
|
||||||
new_costs: MixNodeCostParams,
|
|
||||||
owner: String,
|
|
||||||
},
|
|
||||||
UpdateMixnodeConfig {
|
UpdateMixnodeConfig {
|
||||||
new_config: MixNodeConfigUpdate,
|
profit_margin_percent: u8,
|
||||||
},
|
},
|
||||||
UpdateMixnodeConfigOnBehalf {
|
UpdateMixnodeConfigOnBehalf {
|
||||||
new_config: MixNodeConfigUpdate,
|
profit_margin_percent: u8,
|
||||||
owner: String,
|
owner: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
// gateway-related:
|
|
||||||
BondGateway {
|
BondGateway {
|
||||||
gateway: Gateway,
|
gateway: Gateway,
|
||||||
owner_signature: String,
|
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,
|
||||||
|
owner_signature: String,
|
||||||
|
},
|
||||||
|
UnbondMixnodeOnBehalf {
|
||||||
|
owner: String,
|
||||||
|
},
|
||||||
BondGatewayOnBehalf {
|
BondGatewayOnBehalf {
|
||||||
gateway: Gateway,
|
gateway: Gateway,
|
||||||
owner: String,
|
owner: String,
|
||||||
owner_signature: String,
|
owner_signature: String,
|
||||||
},
|
},
|
||||||
UnbondGateway {},
|
|
||||||
UnbondGatewayOnBehalf {
|
UnbondGatewayOnBehalf {
|
||||||
owner: String,
|
owner: String,
|
||||||
},
|
},
|
||||||
|
WriteRewardedSet {
|
||||||
// delegation-related:
|
rewarded_set: Vec<IdentityKey>,
|
||||||
DelegateToMixnode {
|
expected_active_set_size: u32,
|
||||||
mix_id: NodeId,
|
|
||||||
},
|
},
|
||||||
DelegateToMixnodeOnBehalf {
|
// AdvanceCurrentInterval {},
|
||||||
mix_id: NodeId,
|
AdvanceCurrentEpoch {},
|
||||||
delegate: String,
|
ClaimOperatorReward {},
|
||||||
},
|
ClaimOperatorRewardOnBehalf {
|
||||||
UndelegateFromMixnode {
|
|
||||||
mix_id: NodeId,
|
|
||||||
},
|
|
||||||
UndelegateFromMixnodeOnBehalf {
|
|
||||||
mix_id: NodeId,
|
|
||||||
delegate: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
// reward-related
|
|
||||||
RewardMixnode {
|
|
||||||
mix_id: NodeId,
|
|
||||||
performance: Performance,
|
|
||||||
},
|
|
||||||
WithdrawOperatorReward {},
|
|
||||||
WithdrawOperatorRewardOnBehalf {
|
|
||||||
owner: String,
|
owner: String,
|
||||||
},
|
},
|
||||||
WithdrawDelegatorReward {
|
ClaimDelegatorReward {
|
||||||
mix_id: NodeId,
|
mix_identity: IdentityKey,
|
||||||
},
|
},
|
||||||
WithdrawDelegatorRewardOnBehalf {
|
ClaimDelegatorRewardOnBehalf {
|
||||||
mix_id: NodeId,
|
mix_identity: IdentityKey,
|
||||||
owner: String,
|
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)]
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum QueryMsg {
|
pub enum QueryMsg {
|
||||||
// state/sys-params-related
|
GetBlacklistedNodes {},
|
||||||
GetContractVersion {},
|
GetCurrentOperatorCost {},
|
||||||
GetRewardingValidatorAddress {},
|
GetRewardingValidatorAddress {},
|
||||||
GetStateParams {},
|
GetAllDelegationKeys {},
|
||||||
GetState {},
|
DebugGetAllDelegationValues {},
|
||||||
GetRewardingParams {},
|
GetContractVersion {},
|
||||||
GetCurrentIntervalDetails {},
|
GetMixNodes {
|
||||||
GetRewardedSet {
|
|
||||||
limit: Option<u32>,
|
limit: Option<u32>,
|
||||||
start_after: Option<NodeId>,
|
start_after: Option<IdentityKey>,
|
||||||
},
|
},
|
||||||
|
|
||||||
// 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 {
|
GetGateways {
|
||||||
start_after: Option<IdentityKey>,
|
start_after: Option<IdentityKey>,
|
||||||
limit: Option<u32>,
|
limit: Option<u32>,
|
||||||
},
|
},
|
||||||
|
OwnsMixnode {
|
||||||
|
address: String,
|
||||||
|
},
|
||||||
|
OwnsGateway {
|
||||||
|
address: String,
|
||||||
|
},
|
||||||
|
GetMixnodeBond {
|
||||||
|
identity: IdentityKey,
|
||||||
|
},
|
||||||
GetGatewayBond {
|
GetGatewayBond {
|
||||||
identity: IdentityKey,
|
identity: IdentityKey,
|
||||||
},
|
},
|
||||||
GetOwnedGateway {
|
StateParams {},
|
||||||
address: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
// delegation-related:
|
|
||||||
// gets all [paged] delegations associated with particular mixnode
|
// gets all [paged] delegations associated with particular mixnode
|
||||||
GetMixnodeDelegations {
|
GetMixnodeDelegations {
|
||||||
mix_id: NodeId,
|
mix_identity: IdentityKey,
|
||||||
// since `start_after` is user-provided input, we can't use `Addr` as we
|
// since `start_after` is user-provided input, we can't use `Addr` as we
|
||||||
// can't guarantee it's validated.
|
// can't guarantee it's validated.
|
||||||
start_after: Option<String>,
|
start_after: Option<(String, u64)>,
|
||||||
limit: Option<u32>,
|
limit: Option<u32>,
|
||||||
},
|
},
|
||||||
// gets all [paged] delegations associated with particular delegator
|
// gets all [paged] delegations associated with particular delegator
|
||||||
@@ -344,56 +162,81 @@ pub enum QueryMsg {
|
|||||||
// since `delegator` is user-provided input, we can't use `Addr` as we
|
// since `delegator` is user-provided input, we can't use `Addr` as we
|
||||||
// can't guarantee it's validated.
|
// can't guarantee it's validated.
|
||||||
delegator: String,
|
delegator: String,
|
||||||
start_after: Option<(NodeId, OwnerProxySubKey)>,
|
start_after: Option<IdentityKey>,
|
||||||
limit: Option<u32>,
|
limit: Option<u32>,
|
||||||
},
|
},
|
||||||
// gets delegation associated with particular mixnode, delegator pair
|
// gets delegation associated with particular mixnode, delegator pair
|
||||||
GetDelegationDetails {
|
GetDelegationDetails {
|
||||||
mix_id: NodeId,
|
mix_identity: IdentityKey,
|
||||||
delegator: String,
|
delegator: String,
|
||||||
proxy: Option<String>,
|
proxy: Option<String>,
|
||||||
},
|
},
|
||||||
// gets all delegations in the system
|
LayerDistribution {},
|
||||||
GetAllDelegations {
|
GetRewardPool {},
|
||||||
start_after: Option<delegation::StorageKey>,
|
GetCirculatingSupply {},
|
||||||
|
GetStakingSupply {},
|
||||||
|
GetIntervalRewardPercent {},
|
||||||
|
GetSybilResistancePercent {},
|
||||||
|
GetActiveSetWorkFactor {},
|
||||||
|
GetRewardingStatus {
|
||||||
|
mix_identity: IdentityKey,
|
||||||
|
interval_id: u32,
|
||||||
|
},
|
||||||
|
GetRewardedSet {
|
||||||
|
height: Option<u64>,
|
||||||
|
start_after: Option<IdentityKey>,
|
||||||
limit: Option<u32>,
|
limit: Option<u32>,
|
||||||
},
|
},
|
||||||
|
GetRewardedSetUpdateDetails {},
|
||||||
// rewards related
|
GetCurrentRewardedSetHeight {},
|
||||||
GetPendingOperatorReward {
|
GetRewardedSetRefreshBlocks {},
|
||||||
|
GetCurrentEpoch {},
|
||||||
|
GetEpochsInInterval {},
|
||||||
|
QueryOperatorReward {
|
||||||
address: String,
|
address: String,
|
||||||
},
|
},
|
||||||
GetPendingMixNodeOperatorReward {
|
QueryDelegatorReward {
|
||||||
mix_id: NodeId,
|
|
||||||
},
|
|
||||||
GetPendingDelegatorReward {
|
|
||||||
address: String,
|
address: String,
|
||||||
mix_id: NodeId,
|
mix_identity: IdentityKey,
|
||||||
proxy: Option<String>,
|
proxy: Option<String>,
|
||||||
},
|
},
|
||||||
// given the provided performance, estimate the reward at the end of the current epoch
|
GetPendingDelegationEvents {
|
||||||
GetEstimatedCurrentEpochOperatorReward {
|
owner_address: String,
|
||||||
mix_id: NodeId,
|
proxy_address: Option<String>,
|
||||||
estimated_performance: Performance,
|
|
||||||
},
|
},
|
||||||
GetEstimatedCurrentEpochDelegatorReward {
|
GetCheckpointsForMixnode {
|
||||||
address: String,
|
mix_identity: IdentityKey,
|
||||||
mix_id: NodeId,
|
|
||||||
proxy: Option<String>,
|
|
||||||
estimated_performance: Performance,
|
|
||||||
},
|
},
|
||||||
|
GetMixnodeAtHeight {
|
||||||
// interval-related
|
mix_identity: IdentityKey,
|
||||||
GetPendingEpochEvents {
|
height: u64,
|
||||||
limit: Option<u32>,
|
|
||||||
start_after: Option<u32>,
|
|
||||||
},
|
|
||||||
GetPendingIntervalEvents {
|
|
||||||
limit: Option<u32>,
|
|
||||||
start_after: Option<u32>,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub struct MigrateMsg {}
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,77 +0,0 @@
|
|||||||
// 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,264 +1,288 @@
|
|||||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
use crate::{error::MixnetContractError, mixnode::StoredNodeRewardResult, ONE, U128};
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
use az::CheckedCast;
|
||||||
|
use cosmwasm_std::Uint128;
|
||||||
use crate::{error::MixnetContractError, Percent};
|
|
||||||
use cosmwasm_std::Decimal;
|
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
pub type Performance = Percent;
|
#[derive(Debug, Clone, JsonSchema, PartialEq, Eq, Serialize, Deserialize, Copy)]
|
||||||
|
pub struct NodeEpochRewards {
|
||||||
/// Parameters required by the mix-mining reward distribution that do not change during an interval.
|
params: NodeRewardParams,
|
||||||
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
|
result: StoredNodeRewardResult,
|
||||||
#[cfg_attr(
|
epoch_id: u32,
|
||||||
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 IntervalRewardParams {
|
impl NodeEpochRewards {
|
||||||
pub fn to_inline_json(&self) -> String {
|
pub fn new(params: NodeRewardParams, result: StoredNodeRewardResult, epoch_id: u32) -> Self {
|
||||||
serde_json::to_string(self).unwrap_or_else(|_| "serialisation failure".into())
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
|
#[derive(Debug, Clone, JsonSchema, PartialEq, Eq, Serialize, Deserialize, Copy)]
|
||||||
#[cfg_attr(
|
pub struct EpochRewardParams {
|
||||||
feature = "generate-ts",
|
epoch_reward_pool: Uint128,
|
||||||
ts(export_to = "ts-packages/types/src/types/rust/RewardingParams.ts")
|
rewarded_set_size: Uint128,
|
||||||
)]
|
active_set_size: Uint128,
|
||||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, PartialOrd, Serialize, JsonSchema)]
|
#[serde(alias = "circulating_supply")]
|
||||||
pub struct RewardingParams {
|
staking_supply: Uint128,
|
||||||
/// Parameters that should remain unchanged throughout an interval.
|
sybil_resistance_percent: u8,
|
||||||
pub interval: IntervalRewardParams,
|
active_set_work_factor: u8,
|
||||||
|
|
||||||
// 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 RewardingParams {
|
impl EpochRewardParams {
|
||||||
pub fn active_node_work(&self) -> Decimal {
|
pub fn new(
|
||||||
self.interval.active_set_work_factor * self.standby_node_work()
|
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,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn standby_node_work(&self) -> Decimal {
|
// technically it's identical to what would have been derived with a Default implementation,
|
||||||
let f = self.interval.active_set_work_factor;
|
// however, I prefer to be explicit about it, as a `Default::default` value makes no sense
|
||||||
let k = self.dec_rewarded_set_size();
|
// apart from the `ValidatorCacheInner` context, where this value is not going to be touched anyway
|
||||||
let one = Decimal::one();
|
// (it's guarded behind an `initialised` flag)
|
||||||
|
pub fn new_empty() -> Self {
|
||||||
// nodes in reserve
|
EpochRewardParams {
|
||||||
let k_r = self.dec_standby_set_size();
|
epoch_reward_pool: Uint128::new(0),
|
||||||
|
staking_supply: Uint128::new(0),
|
||||||
one / (f * k - (f - one) * k_r)
|
sybil_resistance_percent: 0,
|
||||||
|
rewarded_set_size: Uint128::new(0),
|
||||||
|
active_set_size: Uint128::new(0),
|
||||||
|
active_set_work_factor: 0,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dec_rewarded_set_size(&self) -> Decimal {
|
pub fn rewarded_set_size(&self) -> u128 {
|
||||||
// the unwrap here is fine as we're guaranteed an `u32` is going to fit in a Decimal
|
self.rewarded_set_size.u128()
|
||||||
// with 0 decimal places
|
|
||||||
Decimal::from_atomics(self.rewarded_set_size, 0).unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dec_active_set_size(&self) -> Decimal {
|
pub fn active_set_size(&self) -> u128 {
|
||||||
// the unwrap here is fine as we're guaranteed an `u32` is going to fit in a Decimal
|
self.active_set_size.u128()
|
||||||
// with 0 decimal places
|
|
||||||
Decimal::from_atomics(self.active_set_size, 0).unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dec_standby_set_size(&self) -> Decimal {
|
pub fn staking_supply(&self) -> u128 {
|
||||||
// the unwrap here is fine as we're guaranteed an `u32` is going to fit in a Decimal
|
self.staking_supply.u128()
|
||||||
// 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) {
|
pub fn epoch_reward_pool(&self) -> u128 {
|
||||||
self.interval.epoch_reward_budget = self.interval.reward_pool
|
self.epoch_reward_pool.u128()
|
||||||
/ Decimal::from_atomics(new_epochs_in_interval, 0).unwrap()
|
|
||||||
* self.interval.interval_pool_emission;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn try_change_active_set_size(
|
pub fn sybil_resistance_percent(&self) -> u8 {
|
||||||
&mut self,
|
self.sybil_resistance_percent
|
||||||
new_active_set_size: u32,
|
|
||||||
) -> Result<(), MixnetContractError> {
|
|
||||||
if new_active_set_size == 0 {
|
|
||||||
return Err(MixnetContractError::ZeroActiveSet);
|
|
||||||
}
|
|
||||||
|
|
||||||
if new_active_set_size > self.rewarded_set_size {
|
|
||||||
return Err(MixnetContractError::InvalidActiveSetSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.active_set_size = new_active_set_size;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn try_apply_updates(
|
pub fn active_set_work_factor(&self) -> u8 {
|
||||||
&mut self,
|
self.active_set_work_factor
|
||||||
updates: IntervalRewardingParamsUpdate,
|
|
||||||
epochs_in_interval: u32,
|
|
||||||
) -> Result<(), MixnetContractError> {
|
|
||||||
if !updates.contains_updates() {
|
|
||||||
return Err(MixnetContractError::EmptyParamsChangeMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut recompute_epoch_budget = false;
|
|
||||||
let mut recompute_saturation_point = false;
|
|
||||||
|
|
||||||
if let Some(reward_pool) = updates.reward_pool {
|
|
||||||
recompute_epoch_budget = true;
|
|
||||||
self.interval.reward_pool = reward_pool;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(staking_supply) = updates.staking_supply {
|
|
||||||
recompute_saturation_point = true;
|
|
||||||
self.interval.staking_supply = staking_supply;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(sybil_resistance_percent) = updates.sybil_resistance_percent {
|
|
||||||
self.interval.sybil_resistance = sybil_resistance_percent;
|
|
||||||
}
|
|
||||||
|
|
||||||
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(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: possibly refactor this
|
#[derive(Debug, Clone, JsonSchema, PartialEq, Eq, Serialize, Deserialize, Copy)]
|
||||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, PartialOrd, Serialize, JsonSchema)]
|
|
||||||
pub struct NodeRewardParams {
|
pub struct NodeRewardParams {
|
||||||
pub performance: Percent,
|
reward_blockstamp: u64,
|
||||||
pub in_active_set: bool,
|
uptime: Uint128,
|
||||||
|
in_active_set: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NodeRewardParams {
|
impl NodeRewardParams {
|
||||||
pub fn new(performance: Percent, in_active_set: bool) -> Self {
|
pub fn new(reward_blockstamp: u64, uptime: u128, in_active_set: bool) -> NodeRewardParams {
|
||||||
NodeRewardParams {
|
NodeRewardParams {
|
||||||
performance,
|
reward_blockstamp,
|
||||||
|
uptime: Uint128::new(uptime),
|
||||||
in_active_set,
|
in_active_set,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
|
pub fn operator_cost(&self, base_operator_cost: u64) -> U128 {
|
||||||
#[cfg_attr(
|
self.performance() * U128::from_num(base_operator_cost)
|
||||||
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 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 to_inline_json(&self) -> String {
|
pub fn uptime(&self) -> Uint128 {
|
||||||
serde_json::to_string(self).unwrap_or_else(|_| "serialisation failure".into())
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RewardParams {
|
||||||
|
pub fn new(epoch: EpochRewardParams, node: NodeRewardParams) -> RewardParams {
|
||||||
|
RewardParams { epoch, node }
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
use cosmwasm_std::{Coin, Decimal, Uint128};
|
|
||||||
|
|
||||||
/// Truncates all decimal points so that the reward would fit in a `Coin` and so that we would
|
|
||||||
/// never attempt to reward more than the owner is due
|
|
||||||
/// for example it truncates "23.9" into "23"
|
|
||||||
pub fn truncate_reward(reward: Decimal, denom: impl Into<String>) -> Coin {
|
|
||||||
let amount = truncate_reward_amount(reward);
|
|
||||||
Coin {
|
|
||||||
denom: denom.into(),
|
|
||||||
amount,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn truncate_reward_amount(reward: Decimal) -> Uint128 {
|
|
||||||
truncate_decimal(reward)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn truncate_decimal(amount: Decimal) -> Uint128 {
|
|
||||||
amount * Uint128::new(1)
|
|
||||||
}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
use cosmwasm_std::{Coin, Decimal};
|
|
||||||
use schemars::JsonSchema;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
pub mod helpers;
|
|
||||||
pub mod simulator;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, JsonSchema, PartialEq, Eq)]
|
|
||||||
pub struct RewardEstimate {
|
|
||||||
pub total_node_reward: Decimal,
|
|
||||||
|
|
||||||
// note that operator reward includes the operating_cost,
|
|
||||||
// i.e. say total_node_reward was `1nym` and operating_cost was `2nym`
|
|
||||||
// in that case the operator reward would still be `1nym` as opposed to 0
|
|
||||||
pub operator: Decimal,
|
|
||||||
pub delegates: Decimal,
|
|
||||||
pub operating_cost: Decimal,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq)]
|
|
||||||
pub struct RewardDistribution {
|
|
||||||
pub operator: Decimal,
|
|
||||||
pub delegates: Decimal,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Deserialize, Serialize, JsonSchema, PartialEq)]
|
|
||||||
pub struct PendingRewardResponse {
|
|
||||||
pub amount_staked: Option<Coin>,
|
|
||||||
pub amount_earned: Option<Coin>,
|
|
||||||
pub amount_earned_detailed: Option<Decimal>,
|
|
||||||
|
|
||||||
/// The associated mixnode is still fully bonded, meaning it is neither unbonded
|
|
||||||
/// nor in the process of unbonding that would have finished at the epoch transition.
|
|
||||||
pub mixnode_still_fully_bonded: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Deserialize, Serialize, JsonSchema, PartialEq)]
|
|
||||||
pub struct EstimatedCurrentEpochRewardResponse {
|
|
||||||
pub original_stake: Option<Coin>,
|
|
||||||
|
|
||||||
pub current_stake_value: Option<Coin>,
|
|
||||||
pub current_stake_value_detailed_amount: Option<Decimal>,
|
|
||||||
|
|
||||||
pub estimation: Option<Coin>,
|
|
||||||
pub detailed_estimation_amount: Option<Decimal>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EstimatedCurrentEpochRewardResponse {
|
|
||||||
pub fn empty_response() -> Self {
|
|
||||||
EstimatedCurrentEpochRewardResponse {
|
|
||||||
original_stake: None,
|
|
||||||
current_stake_value: None,
|
|
||||||
current_stake_value_detailed_amount: None,
|
|
||||||
estimation: None,
|
|
||||||
detailed_estimation_amount: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,465 +0,0 @@
|
|||||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
use crate::error::MixnetContractError;
|
|
||||||
use crate::mixnode::{MixNodeCostParams, MixNodeRewarding};
|
|
||||||
use crate::reward_params::{IntervalRewardParams, NodeRewardParams, RewardingParams};
|
|
||||||
use crate::rewarding::helpers::truncate_reward;
|
|
||||||
use crate::rewarding::RewardDistribution;
|
|
||||||
use crate::{Delegation, Interval, Percent};
|
|
||||||
use cosmwasm_std::{Addr, Coin, Decimal};
|
|
||||||
|
|
||||||
pub struct Simulator {
|
|
||||||
pub node_rewarding_details: MixNodeRewarding,
|
|
||||||
pub node_delegations: Vec<Delegation>,
|
|
||||||
pub system_rewarding_params: RewardingParams,
|
|
||||||
|
|
||||||
pub interval: Interval,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Simulator {
|
|
||||||
pub fn new(
|
|
||||||
profit_margin_percent: Percent,
|
|
||||||
interval_operating_cost: Coin,
|
|
||||||
system_rewarding_params: RewardingParams,
|
|
||||||
initial_pledge: Coin,
|
|
||||||
interval: Interval,
|
|
||||||
) -> Self {
|
|
||||||
let cost_params = MixNodeCostParams {
|
|
||||||
profit_margin_percent,
|
|
||||||
interval_operating_cost,
|
|
||||||
};
|
|
||||||
|
|
||||||
Simulator {
|
|
||||||
node_rewarding_details: MixNodeRewarding::initialise_new(
|
|
||||||
cost_params,
|
|
||||||
&initial_pledge,
|
|
||||||
Default::default(),
|
|
||||||
),
|
|
||||||
node_delegations: vec![],
|
|
||||||
system_rewarding_params,
|
|
||||||
interval,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn delegate(&mut self, amount: Coin) {
|
|
||||||
let cumulative_reward_ratio = self.node_rewarding_details.total_unit_reward;
|
|
||||||
// let record = self.node_rewarding_details.increment_period();
|
|
||||||
// self.node_historical_records.insert(period, record);
|
|
||||||
self.node_rewarding_details
|
|
||||||
.add_base_delegation(amount.amount);
|
|
||||||
|
|
||||||
// we don't care about the owner/node details here
|
|
||||||
self.node_delegations.push(Delegation::new(
|
|
||||||
Addr::unchecked("bob"),
|
|
||||||
42,
|
|
||||||
cumulative_reward_ratio,
|
|
||||||
amount,
|
|
||||||
123,
|
|
||||||
None,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn determine_delegation_reward(&self, delegation: &Delegation) -> Decimal {
|
|
||||||
self.node_rewarding_details
|
|
||||||
.determine_delegation_reward(delegation)
|
|
||||||
}
|
|
||||||
|
|
||||||
// since this is a simulator only, not something to be used in the production code, the unwraps are fine
|
|
||||||
// if user inputs are invalid
|
|
||||||
pub fn undelegate(
|
|
||||||
&mut self,
|
|
||||||
delegation_index: usize,
|
|
||||||
) -> Result<(Coin, Coin), MixnetContractError> {
|
|
||||||
let delegation = self.node_delegations.remove(delegation_index);
|
|
||||||
let reward = self.determine_delegation_reward(&delegation);
|
|
||||||
self.node_rewarding_details
|
|
||||||
.decrease_delegates_decimal(delegation.dec_amount() + reward)?;
|
|
||||||
self.node_rewarding_details.unique_delegations -= 1;
|
|
||||||
|
|
||||||
let reward_denom = &delegation.amount.denom;
|
|
||||||
let truncated_reward = truncate_reward(reward, reward_denom);
|
|
||||||
|
|
||||||
// if this was last delegation, move all leftover decimal tokens to the operator
|
|
||||||
// (this is literally in the order of a millionth of a micronym)
|
|
||||||
if self.node_delegations.is_empty() {
|
|
||||||
assert_eq!(self.node_rewarding_details.unique_delegations, 0);
|
|
||||||
self.node_rewarding_details.operator += self.node_rewarding_details.delegates;
|
|
||||||
self.node_rewarding_details.delegates = Decimal::zero();
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok((delegation.amount, truncated_reward))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn determine_total_delegation_reward(&self) -> Decimal {
|
|
||||||
let mut total = Decimal::zero();
|
|
||||||
for delegation in &self.node_delegations {
|
|
||||||
total += self.determine_delegation_reward(delegation)
|
|
||||||
}
|
|
||||||
total
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn simulate_epoch(&mut self, node_params: NodeRewardParams) -> RewardDistribution {
|
|
||||||
let reward_distribution = self.node_rewarding_details.calculate_epoch_reward(
|
|
||||||
&self.system_rewarding_params,
|
|
||||||
node_params,
|
|
||||||
self.interval.epochs_in_interval(),
|
|
||||||
);
|
|
||||||
self.node_rewarding_details.distribute_rewards(
|
|
||||||
reward_distribution,
|
|
||||||
self.interval.current_epoch_absolute_id(),
|
|
||||||
);
|
|
||||||
self.interval = self.interval.advance_epoch();
|
|
||||||
|
|
||||||
reward_distribution
|
|
||||||
}
|
|
||||||
|
|
||||||
// assume node state doesn't change in the interval (kinda unrealistic)
|
|
||||||
pub fn simulate_interval(&mut self, node_params: NodeRewardParams) {
|
|
||||||
assert_eq!(self.interval.current_epoch_id(), 0);
|
|
||||||
let id = self.interval.current_interval_id();
|
|
||||||
let mut distributed = Decimal::zero();
|
|
||||||
for _ in 0..self.interval.epochs_in_interval() {
|
|
||||||
let distr = self.simulate_epoch(node_params);
|
|
||||||
distributed += distr.operator;
|
|
||||||
distributed += distr.delegates;
|
|
||||||
}
|
|
||||||
assert_eq!(id + 1, self.interval.current_interval_id());
|
|
||||||
|
|
||||||
// update reward pool and all of that
|
|
||||||
let old = self.system_rewarding_params.interval;
|
|
||||||
let reward_pool = old.reward_pool - distributed;
|
|
||||||
let staking_supply = old.staking_supply + distributed;
|
|
||||||
let epoch_reward_budget = reward_pool
|
|
||||||
/ Decimal::from_atomics(self.interval.epochs_in_interval(), 0).unwrap()
|
|
||||||
* old.interval_pool_emission.value();
|
|
||||||
let stake_saturation_point = staking_supply
|
|
||||||
/ Decimal::from_atomics(self.system_rewarding_params.rewarded_set_size, 0).unwrap();
|
|
||||||
|
|
||||||
let updated_params = RewardingParams {
|
|
||||||
interval: IntervalRewardParams {
|
|
||||||
reward_pool,
|
|
||||||
staking_supply,
|
|
||||||
epoch_reward_budget,
|
|
||||||
stake_saturation_point,
|
|
||||||
sybil_resistance: old.sybil_resistance,
|
|
||||||
active_set_work_factor: old.active_set_work_factor,
|
|
||||||
interval_pool_emission: old.interval_pool_emission,
|
|
||||||
},
|
|
||||||
rewarded_set_size: self.system_rewarding_params.rewarded_set_size,
|
|
||||||
active_set_size: self.system_rewarding_params.active_set_size,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.system_rewarding_params = updated_params;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use crate::reward_params::IntervalRewardParams;
|
|
||||||
use crate::rewarding::helpers::truncate_reward_amount;
|
|
||||||
use cosmwasm_std::testing::mock_env;
|
|
||||||
use cosmwasm_std::{coin, Uint128};
|
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
fn base_simulator(initial_pledge: u128) -> Simulator {
|
|
||||||
let profit_margin = Percent::from_percentage_value(10).unwrap();
|
|
||||||
let interval_operating_cost = Coin::new(40_000_000, "unym");
|
|
||||||
let epochs_in_interval = 720u32;
|
|
||||||
let rewarded_set_size = 240;
|
|
||||||
let active_set_size = 100;
|
|
||||||
let interval_pool_emission = Percent::from_percentage_value(2).unwrap();
|
|
||||||
|
|
||||||
let reward_pool = 250_000_000_000_000u128;
|
|
||||||
let staking_supply = 100_000_000_000_000u128;
|
|
||||||
let epoch_reward_budget =
|
|
||||||
interval_pool_emission * Decimal::from_ratio(reward_pool, epochs_in_interval);
|
|
||||||
let stake_saturation_point = Decimal::from_ratio(staking_supply, rewarded_set_size);
|
|
||||||
|
|
||||||
let rewarding_params = RewardingParams {
|
|
||||||
interval: IntervalRewardParams {
|
|
||||||
reward_pool: Decimal::from_atomics(reward_pool, 0).unwrap(), // 250M * 1M (we're expressing it all in base tokens)
|
|
||||||
staking_supply: Decimal::from_atomics(staking_supply, 0).unwrap(), // 100M * 1M
|
|
||||||
epoch_reward_budget,
|
|
||||||
stake_saturation_point,
|
|
||||||
sybil_resistance: Percent::from_percentage_value(30).unwrap(),
|
|
||||||
active_set_work_factor: Decimal::percent(1000), // value '10'
|
|
||||||
interval_pool_emission,
|
|
||||||
},
|
|
||||||
rewarded_set_size,
|
|
||||||
active_set_size,
|
|
||||||
};
|
|
||||||
|
|
||||||
let interval = Interval::init_interval(
|
|
||||||
epochs_in_interval,
|
|
||||||
Duration::from_secs(60 * 60),
|
|
||||||
&mock_env(),
|
|
||||||
);
|
|
||||||
let initial_pledge = Coin::new(initial_pledge, "unym");
|
|
||||||
Simulator::new(
|
|
||||||
profit_margin,
|
|
||||||
interval_operating_cost,
|
|
||||||
rewarding_params,
|
|
||||||
initial_pledge,
|
|
||||||
interval,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn compare_decimals(a: Decimal, b: Decimal) {
|
|
||||||
let epsilon = Decimal::from_ratio(1u128, 100_000_000u128);
|
|
||||||
if a > b {
|
|
||||||
assert!(a - b < epsilon, "{} != {}", a, b)
|
|
||||||
} else {
|
|
||||||
assert!(b - a < epsilon, "{} != {}", a, b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// essentially our delegations + estimated rewards HAVE TO equal to what we actually determined
|
|
||||||
fn check_rewarding_invariant(simulator: &Simulator) {
|
|
||||||
let delegation_sum: Decimal = simulator
|
|
||||||
.node_delegations
|
|
||||||
.iter()
|
|
||||||
.map(|d| d.dec_amount())
|
|
||||||
.sum();
|
|
||||||
let reward_sum = simulator.determine_total_delegation_reward();
|
|
||||||
compare_decimals(
|
|
||||||
delegation_sum + reward_sum,
|
|
||||||
simulator.node_rewarding_details.delegates,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn simulator_returns_expected_values_for_base_case() {
|
|
||||||
let mut simulator = base_simulator(10000_000000);
|
|
||||||
|
|
||||||
let epoch_params =
|
|
||||||
NodeRewardParams::new(Percent::from_percentage_value(100).unwrap(), true);
|
|
||||||
let rewards = simulator.simulate_epoch(epoch_params);
|
|
||||||
|
|
||||||
assert_eq!(rewards.delegates, Decimal::zero());
|
|
||||||
compare_decimals(rewards.operator, "1128452.5416104363".parse().unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn single_delegation_at_genesis() {
|
|
||||||
let mut simulator = base_simulator(10000_000000);
|
|
||||||
simulator.delegate(Coin::new(18000_000000, "unym"));
|
|
||||||
|
|
||||||
let node_params = NodeRewardParams::new(Percent::from_percentage_value(100).unwrap(), true);
|
|
||||||
let rewards = simulator.simulate_epoch(node_params);
|
|
||||||
|
|
||||||
compare_decimals(rewards.delegates, "1795950.2602660495".parse().unwrap());
|
|
||||||
compare_decimals(rewards.operator, "1363716.856243172".parse().unwrap());
|
|
||||||
|
|
||||||
compare_decimals(
|
|
||||||
rewards.delegates,
|
|
||||||
simulator.determine_total_delegation_reward(),
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
simulator.node_rewarding_details.operator,
|
|
||||||
rewards.operator + Decimal::from_atomics(10000_000000u128, 0).unwrap()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
simulator.node_rewarding_details.delegates,
|
|
||||||
rewards.delegates + Decimal::from_atomics(18000_000000u128, 0).unwrap()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn delegation_and_undelegation() {
|
|
||||||
let mut simulator = base_simulator(10000_000000);
|
|
||||||
let node_params = NodeRewardParams::new(Percent::from_percentage_value(100).unwrap(), true);
|
|
||||||
|
|
||||||
let rewards1 = simulator.simulate_epoch(node_params);
|
|
||||||
let expected_operator1 = "1128452.5416104363".parse().unwrap();
|
|
||||||
assert_eq!(rewards1.delegates, Decimal::zero());
|
|
||||||
compare_decimals(rewards1.operator, expected_operator1);
|
|
||||||
|
|
||||||
simulator.delegate(Coin::new(18000_000000, "unym"));
|
|
||||||
|
|
||||||
let rewards2 = simulator.simulate_epoch(node_params);
|
|
||||||
let expected_operator2 = "1363843.413584609".parse().unwrap();
|
|
||||||
let expected_delegator_reward1 = "1795952.25874404".parse().unwrap();
|
|
||||||
compare_decimals(rewards2.delegates, expected_delegator_reward1);
|
|
||||||
compare_decimals(rewards2.operator, expected_operator2);
|
|
||||||
|
|
||||||
let rewards3 = simulator.simulate_epoch(node_params);
|
|
||||||
let expected_operator3 = "1364017.7824440491".parse().unwrap();
|
|
||||||
let expected_delegator_reward2 = "1796135.9269468693".parse().unwrap();
|
|
||||||
compare_decimals(rewards3.delegates, expected_delegator_reward2);
|
|
||||||
compare_decimals(rewards3.operator, expected_operator3);
|
|
||||||
|
|
||||||
let (delegation, reward) = simulator.undelegate(0).unwrap();
|
|
||||||
assert_eq!(delegation.amount.u128(), 18000_000000);
|
|
||||||
assert_eq!(
|
|
||||||
reward.amount,
|
|
||||||
(expected_delegator_reward1 + expected_delegator_reward2) * Uint128::new(1)
|
|
||||||
);
|
|
||||||
|
|
||||||
let base_op = Decimal::from_atomics(10000_000000u128, 0).unwrap();
|
|
||||||
compare_decimals(
|
|
||||||
simulator.node_rewarding_details.operator,
|
|
||||||
base_op + expected_operator1 + expected_operator2 + expected_operator3,
|
|
||||||
);
|
|
||||||
assert_eq!(Decimal::zero(), simulator.node_rewarding_details.delegates);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn withdrawing_operator_reward() {
|
|
||||||
// essentially all delegators' rewards (and the operator itself) are still correctly computed
|
|
||||||
let original_pledge = coin(10000_000000, "unym");
|
|
||||||
let mut simulator = base_simulator(original_pledge.amount.u128());
|
|
||||||
let node_params = NodeRewardParams::new(Percent::from_percentage_value(100).unwrap(), true);
|
|
||||||
|
|
||||||
// add 2 delegations at genesis (because it makes things easier and as shown with previous tests
|
|
||||||
// delegating at different times still work)
|
|
||||||
simulator.delegate(Coin::new(18000_000000, "unym"));
|
|
||||||
simulator.delegate(Coin::new(4000_000000, "unym"));
|
|
||||||
|
|
||||||
// "normal", sanity check rewarding
|
|
||||||
let rewards1 = simulator.simulate_epoch(node_params);
|
|
||||||
let expected_operator1 = "1411087.1007647323".parse().unwrap();
|
|
||||||
let expected_delegator_reward1 = "2199961.032388664".parse().unwrap();
|
|
||||||
compare_decimals(rewards1.delegates, expected_delegator_reward1);
|
|
||||||
compare_decimals(rewards1.operator, expected_operator1);
|
|
||||||
check_rewarding_invariant(&simulator);
|
|
||||||
|
|
||||||
let reward = simulator
|
|
||||||
.node_rewarding_details
|
|
||||||
.withdraw_operator_reward(&original_pledge);
|
|
||||||
assert_eq!(reward.amount, truncate_reward_amount(expected_operator1));
|
|
||||||
assert_eq!(
|
|
||||||
simulator.node_rewarding_details.operator,
|
|
||||||
Decimal::from_atomics(original_pledge.amount, 0).unwrap()
|
|
||||||
);
|
|
||||||
|
|
||||||
let rewards2 = simulator.simulate_epoch(node_params);
|
|
||||||
let expected_operator2 = "1411113.0004067947".parse().unwrap();
|
|
||||||
let expected_delegator_reward2 = "2200183.3879084454".parse().unwrap();
|
|
||||||
compare_decimals(rewards2.delegates, expected_delegator_reward2);
|
|
||||||
compare_decimals(rewards2.operator, expected_operator2);
|
|
||||||
check_rewarding_invariant(&simulator);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn withdrawing_delegator_reward() {
|
|
||||||
// essentially all delegators' rewards (and the operator itself) are still correctly computed
|
|
||||||
let mut simulator = base_simulator(10000_000000);
|
|
||||||
let node_params = NodeRewardParams::new(Percent::from_percentage_value(100).unwrap(), true);
|
|
||||||
|
|
||||||
// add 2 delegations at genesis (because it makes things easier and as shown with previous tests
|
|
||||||
// delegating at different times still work)
|
|
||||||
simulator.delegate(Coin::new(18000_000000, "unym"));
|
|
||||||
simulator.delegate(Coin::new(4000_000000, "unym"));
|
|
||||||
|
|
||||||
// "normal", sanity check rewarding
|
|
||||||
let rewards1 = simulator.simulate_epoch(node_params);
|
|
||||||
let expected_operator1 = "1411087.1007647323".parse().unwrap();
|
|
||||||
let expected_delegator_reward1 = "2199961.032388664".parse().unwrap();
|
|
||||||
compare_decimals(rewards1.delegates, expected_delegator_reward1);
|
|
||||||
compare_decimals(rewards1.operator, expected_operator1);
|
|
||||||
check_rewarding_invariant(&simulator);
|
|
||||||
|
|
||||||
// reference to our `18000_000000` delegation
|
|
||||||
let delegation1 = &mut simulator.node_delegations[0];
|
|
||||||
let reward = simulator
|
|
||||||
.node_rewarding_details
|
|
||||||
.withdraw_delegator_reward(delegation1)
|
|
||||||
.unwrap();
|
|
||||||
let expected_del1_reward = "1799968.1174089068".parse().unwrap();
|
|
||||||
assert_eq!(reward.amount, truncate_reward_amount(expected_del1_reward));
|
|
||||||
|
|
||||||
// new reward after withdrawal
|
|
||||||
let rewards2 = simulator.simulate_epoch(node_params);
|
|
||||||
let expected_operator2 = "1411250.1907492676".parse().unwrap();
|
|
||||||
let expected_delegator_reward2 = "2200004.051009689".parse().unwrap();
|
|
||||||
compare_decimals(rewards2.delegates, expected_delegator_reward2);
|
|
||||||
compare_decimals(rewards2.operator, expected_operator2);
|
|
||||||
check_rewarding_invariant(&simulator);
|
|
||||||
|
|
||||||
// check final values
|
|
||||||
let reward_del1 = simulator
|
|
||||||
.node_rewarding_details
|
|
||||||
.withdraw_delegator_reward(&mut simulator.node_delegations[0])
|
|
||||||
.unwrap();
|
|
||||||
let expected_del1_reward = "1799970.5883041779".parse().unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
reward_del1.amount,
|
|
||||||
truncate_reward_amount(expected_del1_reward)
|
|
||||||
);
|
|
||||||
|
|
||||||
let reward_del2 = simulator
|
|
||||||
.node_rewarding_details
|
|
||||||
.withdraw_delegator_reward(&mut simulator.node_delegations[1])
|
|
||||||
.unwrap();
|
|
||||||
let first: Decimal = "399992.91497975704".parse().unwrap();
|
|
||||||
let second: Decimal = "400033.4627055114".parse().unwrap();
|
|
||||||
let expected_del2_reward = first + second;
|
|
||||||
assert_eq!(
|
|
||||||
reward_del2.amount,
|
|
||||||
truncate_reward_amount(expected_del2_reward)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn simulating_multiple_epochs() {
|
|
||||||
let mut simulator = base_simulator(10000_000000);
|
|
||||||
|
|
||||||
let mut is_active = true;
|
|
||||||
let mut performance = Percent::from_percentage_value(100).unwrap();
|
|
||||||
for epoch in 0..720 {
|
|
||||||
if epoch == 0 {
|
|
||||||
simulator.delegate(Coin::new(18000_000000, "unym"))
|
|
||||||
}
|
|
||||||
if epoch == 42 {
|
|
||||||
simulator.delegate(Coin::new(2000_000000, "unym"))
|
|
||||||
}
|
|
||||||
if epoch == 89 {
|
|
||||||
is_active = false;
|
|
||||||
}
|
|
||||||
if epoch == 123 {
|
|
||||||
simulator.delegate(Coin::new(6666_000000, "unym"))
|
|
||||||
}
|
|
||||||
if epoch == 167 {
|
|
||||||
performance = Percent::from_percentage_value(90).unwrap();
|
|
||||||
}
|
|
||||||
if epoch == 245 {
|
|
||||||
simulator.delegate(Coin::new(2050_000000, "unym"))
|
|
||||||
}
|
|
||||||
if epoch == 264 {
|
|
||||||
let (delegation, _reward) = simulator.undelegate(1).unwrap();
|
|
||||||
// sanity check to make sure we undelegated what we wanted to undelegate : )
|
|
||||||
assert_eq!(delegation.amount.u128(), 2000_000000);
|
|
||||||
// TODO: figure out if there's a good way to verify whether `reward` is what we expect it to be
|
|
||||||
}
|
|
||||||
if epoch == 345 {
|
|
||||||
is_active = true;
|
|
||||||
}
|
|
||||||
if epoch == 358 {
|
|
||||||
performance = Percent::from_percentage_value(100).unwrap();
|
|
||||||
}
|
|
||||||
if epoch == 458 {
|
|
||||||
let (delegation, _reward) = simulator.undelegate(0).unwrap();
|
|
||||||
// sanity check to make sure we undelegated what we wanted to undelegate : )
|
|
||||||
assert_eq!(delegation.amount.u128(), 18000_000000);
|
|
||||||
// TODO: figure out if there's a good way to verify whether `reward` is what we expect it to be
|
|
||||||
}
|
|
||||||
if epoch == 545 {
|
|
||||||
simulator.delegate(Coin::new(5000_000000, "unym"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// this has to always hold
|
|
||||||
check_rewarding_invariant(&simulator);
|
|
||||||
let node_params = NodeRewardParams::new(performance, is_active);
|
|
||||||
simulator.simulate_epoch(node_params);
|
|
||||||
}
|
|
||||||
|
|
||||||
// after everyone undelegates, there should be nothing left in the delegates pool
|
|
||||||
let delegations = simulator.node_delegations.len();
|
|
||||||
for _ in 0..delegations {
|
|
||||||
simulator.undelegate(0).unwrap();
|
|
||||||
}
|
|
||||||
assert_eq!(Decimal::zero(), simulator.node_rewarding_details.delegates);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,111 +1,16 @@
|
|||||||
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::error::MixnetContractError;
|
use crate::mixnode::DelegatorRewardParams;
|
||||||
use crate::rewarding::helpers::truncate_decimal;
|
|
||||||
use crate::{Layer, RewardedSetNodeStatus};
|
use crate::{Layer, RewardedSetNodeStatus};
|
||||||
use cosmwasm_std::{Addr, Uint128};
|
use cosmwasm_std::{Addr, Uint128};
|
||||||
use cosmwasm_std::{Coin, Decimal};
|
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::de::Error;
|
use serde::{Deserialize, Serialize};
|
||||||
use serde::{Deserialize, Deserializer, Serialize};
|
|
||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
use std::ops::{Index, Mul};
|
|
||||||
|
|
||||||
// type aliases for better reasoning about available data
|
|
||||||
pub type IdentityKey = String;
|
|
||||||
pub type IdentityKeyRef<'a> = &'a str;
|
|
||||||
pub type SphinxKey = String;
|
|
||||||
pub type SphinxKeyRef<'a> = &'a str;
|
|
||||||
pub type EpochId = u32;
|
|
||||||
pub type IntervalId = u32;
|
|
||||||
pub type NodeId = u32;
|
|
||||||
pub type EpochEventId = u32;
|
|
||||||
pub type IntervalEventId = u32;
|
|
||||||
|
|
||||||
/// Percent represents a value between 0 and 100%
|
|
||||||
/// (i.e. between 0.0 and 1.0)
|
|
||||||
#[derive(
|
|
||||||
Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Serialize, Deserialize, JsonSchema,
|
|
||||||
)]
|
|
||||||
pub struct Percent(#[serde(deserialize_with = "de_decimal_percent")] Decimal);
|
|
||||||
|
|
||||||
impl Percent {
|
|
||||||
pub fn new(value: Decimal) -> Result<Self, MixnetContractError> {
|
|
||||||
if value > Decimal::one() {
|
|
||||||
Err(MixnetContractError::InvalidPercent)
|
|
||||||
} else {
|
|
||||||
Ok(Percent(value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_zero(&self) -> bool {
|
|
||||||
self.0 == Decimal::zero()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_percentage_value(value: u64) -> Result<Self, MixnetContractError> {
|
|
||||||
Percent::new(Decimal::percent(value))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn value(&self) -> Decimal {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn round_to_integer(&self) -> u8 {
|
|
||||||
let hundred = Decimal::from_ratio(100u32, 1u32);
|
|
||||||
// we know the cast from u128 to u8 is a safe one since the internal value must be within 0 - 1 range
|
|
||||||
truncate_decimal(hundred * self.0).u128() as u8
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Percent {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
||||||
let adjusted = Decimal::from_atomics(100u32, 0).unwrap() * self.0;
|
|
||||||
write!(f, "{}%", adjusted)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mul<Decimal> for Percent {
|
|
||||||
type Output = Decimal;
|
|
||||||
|
|
||||||
fn mul(self, rhs: Decimal) -> Self::Output {
|
|
||||||
self.0 * rhs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mul<Percent> for Decimal {
|
|
||||||
type Output = Decimal;
|
|
||||||
|
|
||||||
fn mul(self, rhs: Percent) -> Self::Output {
|
|
||||||
rhs * self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mul<Uint128> for Percent {
|
|
||||||
type Output = Uint128;
|
|
||||||
|
|
||||||
fn mul(self, rhs: Uint128) -> Self::Output {
|
|
||||||
self.0 * rhs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// implement custom Deserialize because we want to validate Percent has the correct range
|
|
||||||
fn de_decimal_percent<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
let v = Decimal::deserialize(deserializer)?;
|
|
||||||
if v > Decimal::one() {
|
|
||||||
Err(D::Error::custom(
|
|
||||||
"provided decimal percent is larger than 100%",
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
Ok(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Serialize, Deserialize, Copy, Clone, Eq, PartialEq)]
|
#[derive(Debug, Default, Serialize, Deserialize, Copy, Clone, Eq, PartialEq)]
|
||||||
pub struct LayerDistribution {
|
pub struct LayerDistribution {
|
||||||
|
pub gateways: u64,
|
||||||
pub layer1: u64,
|
pub layer1: u64,
|
||||||
pub layer2: u64,
|
pub layer2: u64,
|
||||||
pub layer3: u64,
|
pub layer3: u64,
|
||||||
@@ -120,133 +25,121 @@ impl LayerDistribution {
|
|||||||
];
|
];
|
||||||
layers.iter().min_by_key(|x| x.1).unwrap().0
|
layers.iter().min_by_key(|x| x.1).unwrap().0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn increment_layer_count(&mut self, layer: Layer) {
|
|
||||||
match layer {
|
|
||||||
Layer::One => self.layer1 += 1,
|
|
||||||
Layer::Two => self.layer2 += 1,
|
|
||||||
Layer::Three => self.layer3 += 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn decrement_layer_count(&mut self, layer: Layer) -> Result<(), MixnetContractError> {
|
|
||||||
match layer {
|
|
||||||
Layer::One => {
|
|
||||||
self.layer1 =
|
|
||||||
self.layer1
|
|
||||||
.checked_sub(1)
|
|
||||||
.ok_or(MixnetContractError::OverflowSubtraction {
|
|
||||||
minuend: self.layer1,
|
|
||||||
subtrahend: 1,
|
|
||||||
})?
|
|
||||||
}
|
|
||||||
Layer::Two => {
|
|
||||||
self.layer2 =
|
|
||||||
self.layer2
|
|
||||||
.checked_sub(1)
|
|
||||||
.ok_or(MixnetContractError::OverflowSubtraction {
|
|
||||||
minuend: self.layer2,
|
|
||||||
subtrahend: 1,
|
|
||||||
})?
|
|
||||||
}
|
|
||||||
Layer::Three => {
|
|
||||||
self.layer3 =
|
|
||||||
self.layer3
|
|
||||||
.checked_sub(1)
|
|
||||||
.ok_or(MixnetContractError::OverflowSubtraction {
|
|
||||||
minuend: self.layer3,
|
|
||||||
subtrahend: 1,
|
|
||||||
})?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Index<Layer> for LayerDistribution {
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||||
type Output = u64;
|
|
||||||
|
|
||||||
fn index(&self, index: Layer) -> &Self::Output {
|
|
||||||
match index {
|
|
||||||
Layer::One => &self.layer1,
|
|
||||||
Layer::Two => &self.layer2,
|
|
||||||
Layer::Three => &self.layer3,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
|
||||||
pub struct ContractState {
|
|
||||||
pub owner: Addr, // only the owner account can update state
|
|
||||||
pub rewarding_validator_address: Addr,
|
|
||||||
|
|
||||||
/// Address of the vesting contract to which the mixnet contract would be sending all
|
|
||||||
/// track-related messages.
|
|
||||||
pub vesting_contract_address: Addr,
|
|
||||||
pub rewarding_denom: String,
|
|
||||||
pub params: ContractStateParams,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
|
||||||
pub struct ContractStateParams {
|
pub struct ContractStateParams {
|
||||||
/// Minimum amount a delegator must stake in orders for his delegation to get accepted.
|
// so currently interval_length is being unused and validator API performs rewarding
|
||||||
pub minimum_mixnode_delegation: Option<Coin>,
|
// based on its own interval length config value. I guess that's fine for time being
|
||||||
|
// however, in the future, the contract constant should be controlling it instead.
|
||||||
|
// pub interval_length: u32, // length of a rewarding interval/interval, expressed in hours
|
||||||
|
pub minimum_mixnode_pledge: Uint128, // minimum amount a mixnode must pledge to get into the system
|
||||||
|
pub minimum_gateway_pledge: Uint128, // minimum amount a gateway must pledge to get into the system
|
||||||
|
|
||||||
/// Minimum amount a mixnode must pledge to get into the system.
|
// number of mixnode that are going to get rewarded during current rewarding interval (k_m)
|
||||||
pub minimum_mixnode_pledge: Coin,
|
// based on overall demand for private bandwidth-
|
||||||
|
pub mixnode_rewarded_set_size: u32,
|
||||||
|
|
||||||
/// Minimum amount a gateway must pledge to get into the system.
|
// subset of rewarded mixnodes that are actively receiving mix traffic
|
||||||
pub minimum_gateway_pledge: Coin,
|
// used to handle shorter-term (e.g. hourly) fluctuations of demand
|
||||||
|
pub mixnode_active_set_size: u32,
|
||||||
|
pub staking_supply: Uint128,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Display for ContractStateParams {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "Contract state parameters: [ ")?;
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"minimum mixnode pledge: {}; ",
|
||||||
|
self.minimum_mixnode_pledge
|
||||||
|
)?;
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"minimum gateway pledge: {}; ",
|
||||||
|
self.minimum_gateway_pledge
|
||||||
|
)?;
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"mixnode rewarded set size: {}",
|
||||||
|
self.mixnode_rewarded_set_size
|
||||||
|
)?;
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"mixnode active set size: {}",
|
||||||
|
self.mixnode_active_set_size
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
|
pub struct RewardingResult {
|
||||||
|
pub node_reward: Uint128,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct PendingDelegatorRewarding {
|
||||||
|
// keep track of the running rewarding results so we'd known how much was the operator and its delegators rewarded
|
||||||
|
pub running_results: RewardingResult,
|
||||||
|
|
||||||
|
pub next_start: Addr,
|
||||||
|
|
||||||
|
pub rewarding_params: DelegatorRewardParams,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub enum RewardingStatus {
|
||||||
|
Complete(RewardingResult),
|
||||||
|
PendingNextDelegatorPage(PendingDelegatorRewarding),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct MixnodeRewardingStatusResponse {
|
||||||
|
pub status: Option<RewardingStatus>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct MixnetContractVersion {
|
||||||
|
// VERGEN_BUILD_TIMESTAMP
|
||||||
|
pub build_timestamp: String,
|
||||||
|
|
||||||
|
// VERGEN_BUILD_SEMVER
|
||||||
|
pub build_version: String,
|
||||||
|
|
||||||
|
// VERGEN_GIT_SHA
|
||||||
|
pub commit_sha: String,
|
||||||
|
|
||||||
|
// VERGEN_GIT_COMMIT_TIMESTAMP
|
||||||
|
pub commit_timestamp: String,
|
||||||
|
|
||||||
|
// VERGEN_GIT_BRANCH
|
||||||
|
pub commit_branch: String,
|
||||||
|
|
||||||
|
// VERGEN_RUSTC_SEMVER
|
||||||
|
pub rustc_version: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
// type aliases for better reasoning about available data
|
||||||
|
pub type IdentityKey = String;
|
||||||
|
pub type IdentityKeyRef<'a> = &'a str;
|
||||||
|
pub type SphinxKey = String;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, JsonSchema)]
|
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, JsonSchema)]
|
||||||
pub struct PagedRewardedSetResponse {
|
pub struct PagedRewardedSetResponse {
|
||||||
pub nodes: Vec<(NodeId, RewardedSetNodeStatus)>,
|
pub identities: Vec<(IdentityKey, RewardedSetNodeStatus)>,
|
||||||
pub start_next_after: Option<NodeId>,
|
pub start_next_after: Option<IdentityKey>,
|
||||||
|
pub at_height: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||||
mod tests {
|
pub struct RewardedSetUpdateDetails {
|
||||||
use super::*;
|
pub refresh_rate_blocks: u64,
|
||||||
|
pub last_refreshed_block: u64,
|
||||||
#[test]
|
pub current_height: u64,
|
||||||
fn percent_serde() {
|
}
|
||||||
let valid_value = Percent::from_percentage_value(80).unwrap();
|
|
||||||
let serialized = serde_json::to_string(&valid_value).unwrap();
|
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||||
|
pub struct IntervalRewardedSetHeightsResponse {
|
||||||
println!("{}", serialized);
|
pub interval_id: u32,
|
||||||
let deserialized: Percent = serde_json::from_str(&serialized).unwrap();
|
pub heights: Vec<u64>,
|
||||||
assert_eq!(valid_value, deserialized);
|
|
||||||
|
|
||||||
let invalid_values = vec!["\"42\"", "\"1.1\"", "\"1.00000001\"", "\"foomp\"", "\"1a\""];
|
|
||||||
for invalid_value in invalid_values {
|
|
||||||
assert!(serde_json::from_str::<'_, Percent>(invalid_value).is_err())
|
|
||||||
}
|
|
||||||
assert_eq!(
|
|
||||||
serde_json::from_str::<'_, Percent>("\"0.95\"").unwrap(),
|
|
||||||
Percent::from_percentage_value(95).unwrap()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn percent_to_absolute_integer() {
|
|
||||||
let p = serde_json::from_str::<'_, Percent>("\"0.0001\"").unwrap();
|
|
||||||
assert_eq!(p.round_to_integer(), 0);
|
|
||||||
|
|
||||||
let p = serde_json::from_str::<'_, Percent>("\"0.0099\"").unwrap();
|
|
||||||
assert_eq!(p.round_to_integer(), 0);
|
|
||||||
|
|
||||||
let p = serde_json::from_str::<'_, Percent>("\"0.0199\"").unwrap();
|
|
||||||
assert_eq!(p.round_to_integer(), 1);
|
|
||||||
|
|
||||||
let p = serde_json::from_str::<'_, Percent>("\"0.45123\"").unwrap();
|
|
||||||
assert_eq!(p.round_to_integer(), 45);
|
|
||||||
|
|
||||||
let p = serde_json::from_str::<'_, Percent>("\"0.999999999\"").unwrap();
|
|
||||||
assert_eq!(p.round_to_integer(), 99);
|
|
||||||
|
|
||||||
let p = serde_json::from_str::<'_, Percent>("\"1.00\"").unwrap();
|
|
||||||
assert_eq!(p.round_to_integer(), 100);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,8 +16,6 @@ pub const VESTING_GATEWAY_UNBONDING_EVENT_TYPE: &str = "vesting_gateway_unbondin
|
|||||||
pub const VESTING_MIXNODE_BONDING_EVENT_TYPE: &str = "vesting_mixnode_bonding";
|
pub const VESTING_MIXNODE_BONDING_EVENT_TYPE: &str = "vesting_mixnode_bonding";
|
||||||
pub const VESTING_MIXNODE_UNBONDING_EVENT_TYPE: &str = "vesting_mixnode_unbonding";
|
pub const VESTING_MIXNODE_UNBONDING_EVENT_TYPE: &str = "vesting_mixnode_unbonding";
|
||||||
pub const VESTING_UPDATE_MIXNODE_CONFIG_EVENT_TYPE: &str = "vesting_update_mixnode_config";
|
pub const VESTING_UPDATE_MIXNODE_CONFIG_EVENT_TYPE: &str = "vesting_update_mixnode_config";
|
||||||
pub const VESTING_UPDATE_MIXNODE_COST_PARAMS_EVENT_TYPE: &str =
|
|
||||||
"vesting_update_mixnode_cost_params";
|
|
||||||
|
|
||||||
pub const TRACK_MIXNODE_UNBOND_EVENT_TYPE: &str = "track_mixnode_unbond";
|
pub const TRACK_MIXNODE_UNBOND_EVENT_TYPE: &str = "track_mixnode_unbond";
|
||||||
pub const TRACK_GATEWAY_UNBOND_EVENT_TYPE: &str = "track_gateway_unbond";
|
pub const TRACK_GATEWAY_UNBOND_EVENT_TYPE: &str = "track_gateway_unbond";
|
||||||
@@ -116,10 +114,6 @@ pub fn new_vesting_update_mixnode_config_event() -> Event {
|
|||||||
Event::new(VESTING_UPDATE_MIXNODE_CONFIG_EVENT_TYPE)
|
Event::new(VESTING_UPDATE_MIXNODE_CONFIG_EVENT_TYPE)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_vesting_update_mixnode_cost_params_event() -> Event {
|
|
||||||
Event::new(VESTING_UPDATE_MIXNODE_COST_PARAMS_EVENT_TYPE)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_vesting_mixnode_unbonding_event() -> Event {
|
pub fn new_vesting_mixnode_unbonding_event() -> Event {
|
||||||
Event::new(VESTING_MIXNODE_UNBONDING_EVENT_TYPE)
|
Event::new(VESTING_MIXNODE_UNBONDING_EVENT_TYPE)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,19 @@
|
|||||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
use cosmwasm_std::{Addr, Coin, Timestamp, Uint128};
|
use cosmwasm_std::{Addr, Coin, Timestamp, Uint128};
|
||||||
use mixnet_contract_common::NodeId;
|
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
pub use messages::{ExecuteMsg, InitMsg, MigrateMsg, QueryMsg};
|
pub use messages::{ExecuteMsg, InitMsg, MigrateMsg, QueryMsg};
|
||||||
|
use mixnet_contract_common::IdentityKey;
|
||||||
|
|
||||||
pub mod events;
|
pub mod events;
|
||||||
pub mod messages;
|
pub mod messages;
|
||||||
|
|
||||||
|
pub fn one_ucoin(denom: String) -> Coin {
|
||||||
|
Coin::new(1, denom)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
|
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "generate-ts",
|
feature = "generate-ts",
|
||||||
@@ -74,14 +78,18 @@ impl OriginalVestingResponse {
|
|||||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, JsonSchema)]
|
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, JsonSchema)]
|
||||||
pub struct VestingDelegation {
|
pub struct VestingDelegation {
|
||||||
pub account_id: u32,
|
pub account_id: u32,
|
||||||
pub mix_id: NodeId,
|
pub mix_identity: IdentityKey,
|
||||||
pub block_timestamp: u64,
|
pub block_timestamp: u64,
|
||||||
pub amount: Uint128,
|
pub amount: Uint128,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VestingDelegation {
|
impl VestingDelegation {
|
||||||
pub fn storage_key(&self) -> (u32, NodeId, u64) {
|
pub fn storage_key(&self) -> (u32, IdentityKey, u64) {
|
||||||
(self.account_id, self.mix_id, self.block_timestamp)
|
(
|
||||||
|
self.account_id,
|
||||||
|
self.mix_identity.clone(),
|
||||||
|
self.block_timestamp,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,12 +97,12 @@ impl VestingDelegation {
|
|||||||
pub struct DelegationTimesResponse {
|
pub struct DelegationTimesResponse {
|
||||||
pub owner: Addr,
|
pub owner: Addr,
|
||||||
pub account_id: u32,
|
pub account_id: u32,
|
||||||
pub mix_id: NodeId,
|
pub mix_identity: IdentityKey,
|
||||||
pub delegation_timestamps: Vec<u64>,
|
pub delegation_timestamps: Vec<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, JsonSchema)]
|
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, JsonSchema)]
|
||||||
pub struct AllDelegationsResponse {
|
pub struct AllDelegationsResponse {
|
||||||
pub delegations: Vec<VestingDelegation>,
|
pub delegations: Vec<VestingDelegation>,
|
||||||
pub start_next_after: Option<(u32, NodeId, u64)>,
|
pub start_next_after: Option<(u32, IdentityKey, u64)>,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
use cosmwasm_std::{Coin, Timestamp, Uint128};
|
use cosmwasm_std::{Coin, Timestamp, Uint128};
|
||||||
use mixnet_contract_common::{
|
use mixnet_contract_common::{Gateway, IdentityKey, MixNode};
|
||||||
mixnode::{MixNodeConfigUpdate, MixNodeCostParams},
|
|
||||||
Gateway, MixNode, NodeId,
|
|
||||||
};
|
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
@@ -15,9 +12,7 @@ pub struct InitMsg {
|
|||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub struct MigrateMsg {
|
pub struct MigrateMsg {}
|
||||||
pub v2_mixnet_contract_address: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema, Default)]
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema, Default)]
|
||||||
pub struct VestingSpecification {
|
pub struct VestingSpecification {
|
||||||
@@ -61,23 +56,24 @@ pub enum ExecuteMsg {
|
|||||||
},
|
},
|
||||||
ClaimOperatorReward {},
|
ClaimOperatorReward {},
|
||||||
ClaimDelegatorReward {
|
ClaimDelegatorReward {
|
||||||
mix_id: NodeId,
|
mix_identity: String,
|
||||||
},
|
},
|
||||||
UpdateMixnodeCostParams {
|
CompoundDelegatorReward {
|
||||||
new_costs: MixNodeCostParams,
|
mix_identity: String,
|
||||||
},
|
},
|
||||||
|
CompoundOperatorReward {},
|
||||||
UpdateMixnodeConfig {
|
UpdateMixnodeConfig {
|
||||||
new_config: MixNodeConfigUpdate,
|
profit_margin_percent: u8,
|
||||||
},
|
},
|
||||||
UpdateMixnetAddress {
|
UpdateMixnetAddress {
|
||||||
address: String,
|
address: String,
|
||||||
},
|
},
|
||||||
DelegateToMixnode {
|
DelegateToMixnode {
|
||||||
mix_id: NodeId,
|
mix_identity: IdentityKey,
|
||||||
amount: Coin,
|
amount: Coin,
|
||||||
},
|
},
|
||||||
UndelegateFromMixnode {
|
UndelegateFromMixnode {
|
||||||
mix_id: NodeId,
|
mix_identity: IdentityKey,
|
||||||
},
|
},
|
||||||
CreateAccount {
|
CreateAccount {
|
||||||
owner_address: String,
|
owner_address: String,
|
||||||
@@ -89,12 +85,11 @@ pub enum ExecuteMsg {
|
|||||||
},
|
},
|
||||||
TrackUndelegation {
|
TrackUndelegation {
|
||||||
owner: String,
|
owner: String,
|
||||||
mix_id: NodeId,
|
mix_identity: IdentityKey,
|
||||||
amount: Coin,
|
amount: Coin,
|
||||||
},
|
},
|
||||||
BondMixnode {
|
BondMixnode {
|
||||||
mix_node: MixNode,
|
mix_node: MixNode,
|
||||||
cost_params: MixNodeCostParams,
|
|
||||||
owner_signature: String,
|
owner_signature: String,
|
||||||
amount: Coin,
|
amount: Coin,
|
||||||
},
|
},
|
||||||
@@ -124,35 +119,6 @@ pub enum ExecuteMsg {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExecuteMsg {
|
|
||||||
pub fn name(&self) -> &str {
|
|
||||||
match self {
|
|
||||||
ExecuteMsg::TrackReward { .. } => "VestingExecuteMsg::TrackReward",
|
|
||||||
ExecuteMsg::ClaimOperatorReward { .. } => "VestingExecuteMsg::ClaimOperatorReward",
|
|
||||||
ExecuteMsg::ClaimDelegatorReward { .. } => "VestingExecuteMsg::ClaimDelegatorReward",
|
|
||||||
ExecuteMsg::UpdateMixnodeConfig { .. } => "VestingExecuteMsg::UpdateMixnodeConfig",
|
|
||||||
ExecuteMsg::UpdateMixnodeCostParams { .. } => {
|
|
||||||
"VestingExecuteMsg::UpdateMixnodeCostParams"
|
|
||||||
}
|
|
||||||
ExecuteMsg::UpdateMixnetAddress { .. } => "VestingExecuteMsg::UpdateMixnetAddress",
|
|
||||||
ExecuteMsg::DelegateToMixnode { .. } => "VestingExecuteMsg::DelegateToMixnode",
|
|
||||||
ExecuteMsg::UndelegateFromMixnode { .. } => "VestingExecuteMsg::UndelegateFromMixnode",
|
|
||||||
ExecuteMsg::CreateAccount { .. } => "VestingExecuteMsg::CreateAccount",
|
|
||||||
ExecuteMsg::WithdrawVestedCoins { .. } => "VestingExecuteMsg::WithdrawVestedCoins",
|
|
||||||
ExecuteMsg::TrackUndelegation { .. } => "VestingExecuteMsg::TrackUndelegation",
|
|
||||||
ExecuteMsg::BondMixnode { .. } => "VestingExecuteMsg::BondMixnode",
|
|
||||||
ExecuteMsg::UnbondMixnode { .. } => "VestingExecuteMsg::UnbondMixnode",
|
|
||||||
ExecuteMsg::TrackUnbondMixnode { .. } => "VestingExecuteMsg::TrackUnbondMixnode",
|
|
||||||
ExecuteMsg::BondGateway { .. } => "VestingExecuteMsg::BondGateway",
|
|
||||||
ExecuteMsg::UnbondGateway { .. } => "VestingExecuteMsg::UnbondGateway",
|
|
||||||
ExecuteMsg::TrackUnbondGateway { .. } => "VestingExecuteMsg::TrackUnbondGateway",
|
|
||||||
ExecuteMsg::TransferOwnership { .. } => "VestingExecuteMsg::TransferOwnership",
|
|
||||||
ExecuteMsg::UpdateStakingAddress { .. } => "VestingExecuteMsg::UpdateStakingAddress",
|
|
||||||
ExecuteMsg::UpdateLockedPledgeCap { .. } => "VestingExecuteMsg::UpdateLockedPledgeCap",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum QueryMsg {
|
pub enum QueryMsg {
|
||||||
@@ -204,10 +170,10 @@ pub enum QueryMsg {
|
|||||||
GetLockedPledgeCap {},
|
GetLockedPledgeCap {},
|
||||||
GetDelegationTimes {
|
GetDelegationTimes {
|
||||||
address: String,
|
address: String,
|
||||||
mix_id: NodeId,
|
mix_identity: IdentityKey,
|
||||||
},
|
},
|
||||||
GetAllDelegations {
|
GetAllDelegations {
|
||||||
start_after: Option<(u32, NodeId, u64)>,
|
start_after: Option<(u32, IdentityKey, u64)>,
|
||||||
limit: Option<u32>,
|
limit: Option<u32>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ async fn main() {
|
|||||||
let out_dir = env::var("OUT_DIR").unwrap();
|
let out_dir = env::var("OUT_DIR").unwrap();
|
||||||
let database_path = format!("{}/coconut-credential-example.sqlite", out_dir);
|
let database_path = format!("{}/coconut-credential-example.sqlite", out_dir);
|
||||||
|
|
||||||
let mut conn = SqliteConnection::connect(&format!("sqlite://{}?mode=rwc", database_path))
|
let mut conn = SqliteConnection::connect(&*format!("sqlite://{}?mode=rwc", database_path))
|
||||||
.await
|
.await
|
||||||
.expect("Failed to create SQLx database connection");
|
.expect("Failed to create SQLx database connection");
|
||||||
|
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ impl Tau {
|
|||||||
|
|
||||||
let tau_mem = self.0.as_raw_slice();
|
let tau_mem = self.0.as_raw_slice();
|
||||||
assert_eq!(tau_mem.len(), 1, "tau length invariant was broken");
|
assert_eq!(tau_mem.len(), 1, "tau length invariant was broken");
|
||||||
random_oracle_builder.update(tau_mem[0].to_be_bytes());
|
random_oracle_builder.update(&tau_mem[0].to_be_bytes());
|
||||||
|
|
||||||
let oracle_output = random_oracle_builder.finalize();
|
let oracle_output = random_oracle_builder.finalize();
|
||||||
debug_assert_eq!(oracle_output.len() * 8, HASH_SECURITY_PARAM);
|
debug_assert_eq!(oracle_output.len() * 8, HASH_SECURITY_PARAM);
|
||||||
|
|||||||
@@ -17,6 +17,11 @@ pub fn execute(attr: TokenStream, item: TokenStream) -> TokenStream {
|
|||||||
} else {
|
} else {
|
||||||
panic!("Only `mixnet` and `vesting` targets are supported!")
|
panic!("Only `mixnet` and `vesting` targets are supported!")
|
||||||
};
|
};
|
||||||
|
let exe = if target == "mixnet" {
|
||||||
|
quote!(wallet_execute)
|
||||||
|
} else {
|
||||||
|
quote!(execute)
|
||||||
|
};
|
||||||
let cl = proc_macro::TokenStream::from(cl);
|
let cl = proc_macro::TokenStream::from(cl);
|
||||||
let cl = parse_macro_input!(cl as ExprMethodCall);
|
let cl = parse_macro_input!(cl as ExprMethodCall);
|
||||||
|
|
||||||
@@ -85,7 +90,7 @@ pub fn execute(attr: TokenStream, item: TokenStream) -> TokenStream {
|
|||||||
let (req, fee) = self.#name(#(#execute_args),*);
|
let (req, fee) = self.#name(#(#execute_args),*);
|
||||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
||||||
self.client
|
self.client
|
||||||
.execute(
|
.#exe(
|
||||||
self.address(),
|
self.address(),
|
||||||
#cl,
|
#cl,
|
||||||
&req,
|
&req,
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ impl CosmosLedger {
|
|||||||
|
|
||||||
/// Get the SECP265K1 address of the device.
|
/// Get the SECP265K1 address of the device.
|
||||||
pub fn get_addr_secp265k1(&self, display: bool) -> Result<AddrSecp265k1Response> {
|
pub fn get_addr_secp265k1(&self, display: bool) -> Result<AddrSecp265k1Response> {
|
||||||
let display = u8::from(display);
|
let display = if display { 1 } else { 0 };
|
||||||
let components = path_bytes(self.path.clone())?;
|
let components = path_bytes(self.path.clone())?;
|
||||||
let data: Vec<u8> = vec![
|
let data: Vec<u8> = vec![
|
||||||
[self.prefix.len() as u8].as_slice(),
|
[self.prefix.len() as u8].as_slice(),
|
||||||
|
|||||||
@@ -321,18 +321,17 @@ impl VerlocMeasurer {
|
|||||||
let tested_nodes = all_mixes
|
let tested_nodes = all_mixes
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|node| {
|
.filter_map(|node| {
|
||||||
let mix_node = node.bond_information.mix_node;
|
|
||||||
// check if the node has sufficient version to be able to understand the packets
|
// check if the node has sufficient version to be able to understand the packets
|
||||||
let node_version = parse_version(&mix_node.version).ok()?;
|
let node_version = parse_version(&node.mix_node.version).ok()?;
|
||||||
if node_version < self.config.minimum_compatible_node_version {
|
if node_version < self.config.minimum_compatible_node_version {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
// try to parse the identity and host
|
// try to parse the identity and host
|
||||||
let node_identity =
|
let node_identity =
|
||||||
identity::PublicKey::from_base58_string(mix_node.identity_key).ok()?;
|
identity::PublicKey::from_base58_string(node.mix_node.identity_key).ok()?;
|
||||||
|
|
||||||
let verloc_host = (&*mix_node.host, mix_node.verloc_port)
|
let verloc_host = (&*node.mix_node.host, node.mix_node.verloc_port)
|
||||||
.to_socket_addrs()
|
.to_socket_addrs()
|
||||||
.ok()?
|
.ok()?
|
||||||
.next()?;
|
.next()?;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ MIX_DENOM_DISPLAY=nym
|
|||||||
STAKE_DENOM=unyx
|
STAKE_DENOM=unyx
|
||||||
STAKE_DENOM_DISPLAY=nyx
|
STAKE_DENOM_DISPLAY=nyx
|
||||||
DENOMS_EXPONENT=6
|
DENOMS_EXPONENT=6
|
||||||
MIXNET_CONTRACT_ADDRESS=n1rjzps6qrmdqmf0xz4cn4x4rcmqeqzq6hnzqg4wcvd0r2lyasdq5sepn5s8
|
MIXNET_CONTRACT_ADDRESS=n1suhgf5svhu4usrurvxzlgn54ksxmn8gljarjtxqnapv8kjnp4nrsd3qaep
|
||||||
VESTING_CONTRACT_ADDRESS=n1xr3rq8yvd7qplsw5yx90ftsr2zdhg4e9z60h5duusgxpv72hud3sjkxkav
|
VESTING_CONTRACT_ADDRESS=n1xr3rq8yvd7qplsw5yx90ftsr2zdhg4e9z60h5duusgxpv72hud3sjkxkav
|
||||||
BANDWIDTH_CLAIM_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0
|
BANDWIDTH_CLAIM_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0
|
||||||
COCONUT_BANDWIDTH_CONTRACT_ADDRESS=n1ghd753shjuwexxywmgs4xz7x2q732vcn7ty4yw
|
COCONUT_BANDWIDTH_CONTRACT_ADDRESS=n1ghd753shjuwexxywmgs4xz7x2q732vcn7ty4yw
|
||||||
|
|||||||
@@ -0,0 +1,251 @@
|
|||||||
|
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
DefaultNetworkDetails, DenomDetailsOwned, NymNetworkDetails, ValidatorDetails,
|
||||||
|
MAINNET_DEFAULTS, QA_DEFAULTS, SANDBOX_DEFAULTS,
|
||||||
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::{collections::HashMap, fmt, str::FromStr};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum NetworkDefaultsError {
|
||||||
|
#[error("The provided network was invalid")]
|
||||||
|
MalformedNetworkProvided(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
// the reason for allowing it is that this is just a temporary solution
|
||||||
|
#[allow(clippy::large_enum_variant)]
|
||||||
|
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||||
|
pub enum Network {
|
||||||
|
QA,
|
||||||
|
SANDBOX,
|
||||||
|
MAINNET,
|
||||||
|
CUSTOM { details: NymNetworkDetails },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Network {
|
||||||
|
pub fn new_custom(details: NymNetworkDetails) -> Self {
|
||||||
|
Network::CUSTOM { details }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn details(&self) -> NymNetworkDetails {
|
||||||
|
match self {
|
||||||
|
Self::QA => (&*QA_DEFAULTS).into(),
|
||||||
|
Self::SANDBOX => (&*SANDBOX_DEFAULTS).into(),
|
||||||
|
Self::MAINNET => (&*MAINNET_DEFAULTS).into(),
|
||||||
|
// I dislike the clone here, but for compatibility reasons we cannot define other networks with `NymNetworkDetails` directly yet
|
||||||
|
Self::CUSTOM { details } => details.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bech32_prefix(&self) -> String {
|
||||||
|
self.details().chain_details.bech32_account_prefix
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mix_denom(&self) -> DenomDetailsOwned {
|
||||||
|
self.details().chain_details.mix_denom
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stake_denom(&self) -> DenomDetailsOwned {
|
||||||
|
self.details().chain_details.stake_denom
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn base_mix_denom(&self) -> String {
|
||||||
|
self.details().chain_details.mix_denom.base
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn base_stake_denom(&self) -> String {
|
||||||
|
self.details().chain_details.stake_denom.base
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mixnet_contract_address(&self) -> Option<String> {
|
||||||
|
self.details().contracts.mixnet_contract_address
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn vesting_contract_address(&self) -> Option<String> {
|
||||||
|
self.details().contracts.vesting_contract_address
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bandwidth_claim_contract_address(&self) -> Option<String> {
|
||||||
|
self.details().contracts.bandwidth_claim_contract_address
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn coconut_bandwidth_contract_address(&self) -> Option<String> {
|
||||||
|
self.details().contracts.coconut_bandwidth_contract_address
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn multisig_contract_address(&self) -> Option<String> {
|
||||||
|
self.details().contracts.multisig_contract_address
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn validators(&self) -> Vec<ValidatorDetails> {
|
||||||
|
self.details().endpoints
|
||||||
|
}
|
||||||
|
|
||||||
|
// only used in mixnet contract tests, but I don't want to be messing with that code now
|
||||||
|
pub fn rewarding_validator_address(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
Network::QA => crate::qa::REWARDING_VALIDATOR_ADDRESS,
|
||||||
|
Network::SANDBOX => crate::sandbox::REWARDING_VALIDATOR_ADDRESS,
|
||||||
|
Network::MAINNET => crate::mainnet::REWARDING_VALIDATOR_ADDRESS,
|
||||||
|
Network::CUSTOM { .. } => {
|
||||||
|
panic!("rewarding validator address is unavailable for a custom network")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// this should be handled differently, but I don't want to break compatibility
|
||||||
|
pub fn statistics_service_url(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
Network::MAINNET => crate::mainnet::STATISTICS_SERVICE_DOMAIN_ADDRESS,
|
||||||
|
Network::SANDBOX => crate::mainnet::STATISTICS_SERVICE_DOMAIN_ADDRESS,
|
||||||
|
Network::QA => crate::mainnet::STATISTICS_SERVICE_DOMAIN_ADDRESS,
|
||||||
|
Network::CUSTOM { .. } => {
|
||||||
|
panic!("statistics service url is unavailable for a custom network")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Network {
|
||||||
|
type Err = NetworkDefaultsError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s.to_lowercase().as_str() {
|
||||||
|
"qa" => Ok(Network::QA),
|
||||||
|
"sandbox" => Ok(Network::SANDBOX),
|
||||||
|
"mainnet" => Ok(Network::MAINNET),
|
||||||
|
_ => Err(NetworkDefaultsError::MalformedNetworkProvided(
|
||||||
|
s.to_string(),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Network {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match *self {
|
||||||
|
Network::QA => f.write_str("QA"),
|
||||||
|
Network::SANDBOX => f.write_str("Sandbox"),
|
||||||
|
Network::MAINNET => f.write_str("Mainnet"),
|
||||||
|
Network::CUSTOM { .. } => f.write_str("Custom"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||||
|
pub struct NetworkDetails {
|
||||||
|
bech32_prefix: String,
|
||||||
|
mix_denom: DenomDetailsOwned,
|
||||||
|
stake_denom: DenomDetailsOwned,
|
||||||
|
mixnet_contract_address: String,
|
||||||
|
vesting_contract_address: String,
|
||||||
|
bandwidth_claim_contract_address: String,
|
||||||
|
statistics_service_url: String,
|
||||||
|
validators: Vec<ValidatorDetails>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&DefaultNetworkDetails> for NetworkDetails {
|
||||||
|
fn from(details: &DefaultNetworkDetails) -> Self {
|
||||||
|
NetworkDetails {
|
||||||
|
bech32_prefix: details.bech32_prefix.into(),
|
||||||
|
mix_denom: details.mix_denom.into(),
|
||||||
|
stake_denom: details.stake_denom.into(),
|
||||||
|
mixnet_contract_address: details.mixnet_contract_address.into(),
|
||||||
|
vesting_contract_address: details.vesting_contract_address.into(),
|
||||||
|
bandwidth_claim_contract_address: details.bandwidth_claim_contract_address.into(),
|
||||||
|
statistics_service_url: details.statistics_service_url.into(),
|
||||||
|
validators: details.validators.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// this also has to exist for compatibility reasons since I don't want to be touching the wallet now
|
||||||
|
impl From<NymNetworkDetails> for NetworkDetails {
|
||||||
|
fn from(details: NymNetworkDetails) -> Self {
|
||||||
|
NetworkDetails {
|
||||||
|
bech32_prefix: details.chain_details.bech32_account_prefix,
|
||||||
|
mix_denom: details.chain_details.mix_denom,
|
||||||
|
stake_denom: details.chain_details.stake_denom,
|
||||||
|
mixnet_contract_address: details
|
||||||
|
.contracts
|
||||||
|
.mixnet_contract_address
|
||||||
|
.unwrap_or_default(),
|
||||||
|
vesting_contract_address: details
|
||||||
|
.contracts
|
||||||
|
.vesting_contract_address
|
||||||
|
.unwrap_or_default(),
|
||||||
|
bandwidth_claim_contract_address: details
|
||||||
|
.contracts
|
||||||
|
.bandwidth_claim_contract_address
|
||||||
|
.unwrap_or_default(),
|
||||||
|
statistics_service_url: "".to_string(),
|
||||||
|
validators: details.endpoints,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetworkDetails {
|
||||||
|
pub fn base_mix_denom(&self) -> &str {
|
||||||
|
&self.mix_denom.base
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
|
||||||
|
pub struct SupportedNetworks {
|
||||||
|
networks: HashMap<Network, NetworkDetails>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SupportedNetworks {
|
||||||
|
pub fn new(support: Vec<Network>) -> Self {
|
||||||
|
SupportedNetworks {
|
||||||
|
networks: support
|
||||||
|
.into_iter()
|
||||||
|
.map(|n| {
|
||||||
|
let details = n.details().into();
|
||||||
|
(n, details)
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bech32_prefix(&self, network: Network) -> Option<&str> {
|
||||||
|
self.networks
|
||||||
|
.get(&network)
|
||||||
|
.map(|network_details| network_details.bech32_prefix.as_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn base_mix_denom(&self, network: Network) -> Option<&str> {
|
||||||
|
self.networks
|
||||||
|
.get(&network)
|
||||||
|
.map(|network_details| network_details.base_mix_denom())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mixnet_contract_address(&self, network: Network) -> Option<&str> {
|
||||||
|
self.networks
|
||||||
|
.get(&network)
|
||||||
|
.map(|network_details| network_details.mixnet_contract_address.as_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn vesting_contract_address(&self, network: Network) -> Option<&str> {
|
||||||
|
self.networks
|
||||||
|
.get(&network)
|
||||||
|
.map(|network_details| network_details.vesting_contract_address.as_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bandwidth_claim_contract_address(&self, network: Network) -> Option<&str> {
|
||||||
|
self.networks
|
||||||
|
.get(&network)
|
||||||
|
.map(|network_details| network_details.bandwidth_claim_contract_address.as_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn validators(&self, network: Network) -> impl Iterator<Item = &ValidatorDetails> {
|
||||||
|
self.networks
|
||||||
|
.get(&network)
|
||||||
|
.map(|network_details| &network_details.validators)
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,15 @@
|
|||||||
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{env::var, ops::Not, path::PathBuf};
|
use std::{env::var, path::PathBuf};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
pub mod all;
|
||||||
pub mod mainnet;
|
pub mod mainnet;
|
||||||
|
pub mod qa;
|
||||||
|
pub mod sandbox;
|
||||||
pub mod var_names;
|
pub mod var_names;
|
||||||
|
|
||||||
pub const ETH_CONTRACT_ADDRESS: [u8; 20] = mainnet::_ETH_CONTRACT_ADDRESS;
|
pub const ETH_CONTRACT_ADDRESS: [u8; 20] = mainnet::_ETH_CONTRACT_ADDRESS;
|
||||||
@@ -99,99 +103,159 @@ impl NymNetworkDetails {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_mainnet() -> Self {
|
pub fn new_mainnet() -> Self {
|
||||||
fn parse_optional_str(raw: &str) -> Option<String> {
|
(&*MAINNET_DEFAULTS).into()
|
||||||
raw.is_empty().not().then(|| raw.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Consider caching this process (lazy static)
|
|
||||||
NymNetworkDetails {
|
|
||||||
chain_details: ChainDetails {
|
|
||||||
bech32_account_prefix: mainnet::BECH32_PREFIX.into(),
|
|
||||||
mix_denom: mainnet::MIX_DENOM.into(),
|
|
||||||
stake_denom: mainnet::STAKE_DENOM.into(),
|
|
||||||
},
|
|
||||||
endpoints: mainnet::validators(),
|
|
||||||
contracts: NymContracts {
|
|
||||||
mixnet_contract_address: parse_optional_str(mainnet::MIXNET_CONTRACT_ADDRESS),
|
|
||||||
vesting_contract_address: parse_optional_str(mainnet::VESTING_CONTRACT_ADDRESS),
|
|
||||||
bandwidth_claim_contract_address: parse_optional_str(
|
|
||||||
mainnet::BANDWIDTH_CLAIM_CONTRACT_ADDRESS,
|
|
||||||
),
|
|
||||||
coconut_bandwidth_contract_address: parse_optional_str(
|
|
||||||
mainnet::COCONUT_BANDWIDTH_CONTRACT_ADDRESS,
|
|
||||||
),
|
|
||||||
multisig_contract_address: parse_optional_str(mainnet::MULTISIG_CONTRACT_ADDRESS),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn with_bech32_account_prefix<S: Into<String>>(mut self, prefix: S) -> Self {
|
pub fn with_bech32_account_prefix<S: Into<String>>(mut self, prefix: S) -> Self {
|
||||||
self.chain_details.bech32_account_prefix = prefix.into();
|
self.chain_details.bech32_account_prefix = prefix.into();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn with_mix_denom(mut self, mix_denom: DenomDetailsOwned) -> Self {
|
pub fn with_mix_denom(mut self, mix_denom: DenomDetailsOwned) -> Self {
|
||||||
self.chain_details.mix_denom = mix_denom;
|
self.chain_details.mix_denom = mix_denom;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn with_stake_denom(mut self, stake_denom: DenomDetailsOwned) -> Self {
|
pub fn with_stake_denom(mut self, stake_denom: DenomDetailsOwned) -> Self {
|
||||||
self.chain_details.stake_denom = stake_denom;
|
self.chain_details.stake_denom = stake_denom;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn with_base_mix_denom<S: Into<String>>(mut self, base_mix_denom: S) -> Self {
|
pub fn with_base_mix_denom<S: Into<String>>(mut self, base_mix_denom: S) -> Self {
|
||||||
self.chain_details.mix_denom = DenomDetailsOwned::base_only(base_mix_denom.into());
|
self.chain_details.mix_denom = DenomDetailsOwned::base_only(base_mix_denom.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn with_base_stake_denom<S: Into<String>>(mut self, base_stake_denom: S) -> Self {
|
pub fn with_base_stake_denom<S: Into<String>>(mut self, base_stake_denom: S) -> Self {
|
||||||
self.chain_details.stake_denom = DenomDetailsOwned::base_only(base_stake_denom.into());
|
self.chain_details.stake_denom = DenomDetailsOwned::base_only(base_stake_denom.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn with_validator_endpoint(mut self, endpoint: ValidatorDetails) -> Self {
|
pub fn with_validator_endpoint(mut self, endpoint: ValidatorDetails) -> Self {
|
||||||
self.endpoints.push(endpoint);
|
self.endpoints.push(endpoint);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn with_mixnet_contract<S: Into<String>>(mut self, contract: Option<S>) -> Self {
|
pub fn with_mixnet_contract<S: Into<String>>(mut self, contract: Option<S>) -> Self {
|
||||||
self.contracts.mixnet_contract_address = contract.map(Into::into);
|
self.contracts.mixnet_contract_address = contract.map(Into::into);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn with_vesting_contract<S: Into<String>>(mut self, contract: Option<S>) -> Self {
|
pub fn with_vesting_contract<S: Into<String>>(mut self, contract: Option<S>) -> Self {
|
||||||
self.contracts.vesting_contract_address = contract.map(Into::into);
|
self.contracts.vesting_contract_address = contract.map(Into::into);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn with_bandwidth_claim_contract<S: Into<String>>(mut self, contract: Option<S>) -> Self {
|
pub fn with_bandwidth_claim_contract<S: Into<String>>(mut self, contract: Option<S>) -> Self {
|
||||||
self.contracts.bandwidth_claim_contract_address = contract.map(Into::into);
|
self.contracts.bandwidth_claim_contract_address = contract.map(Into::into);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn with_coconut_bandwidth_contract<S: Into<String>>(mut self, contract: Option<S>) -> Self {
|
pub fn with_coconut_bandwidth_contract<S: Into<String>>(mut self, contract: Option<S>) -> Self {
|
||||||
self.contracts.coconut_bandwidth_contract_address = contract.map(Into::into);
|
self.contracts.coconut_bandwidth_contract_address = contract.map(Into::into);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn with_multisig_contract<S: Into<String>>(mut self, contract: Option<S>) -> Self {
|
pub fn with_multisig_contract<S: Into<String>>(mut self, contract: Option<S>) -> Self {
|
||||||
self.contracts.multisig_contract_address = contract.map(Into::into);
|
self.contracts.multisig_contract_address = contract.map(Into::into);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This conversion only exists for convenience reasons until
|
||||||
|
// we can completely phase out `DefaultNetworkDetails`
|
||||||
|
impl<'a> From<&'a DefaultNetworkDetails> for NymNetworkDetails {
|
||||||
|
fn from(details: &'a DefaultNetworkDetails) -> Self {
|
||||||
|
fn parse_optional_str(raw: &str) -> Option<String> {
|
||||||
|
if raw.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(raw.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NymNetworkDetails {
|
||||||
|
chain_details: ChainDetails {
|
||||||
|
bech32_account_prefix: details.bech32_prefix.into(),
|
||||||
|
mix_denom: details.mix_denom.into(),
|
||||||
|
stake_denom: details.stake_denom.into(),
|
||||||
|
},
|
||||||
|
endpoints: details.validators.clone(),
|
||||||
|
contracts: NymContracts {
|
||||||
|
mixnet_contract_address: parse_optional_str(details.mixnet_contract_address),
|
||||||
|
vesting_contract_address: parse_optional_str(details.vesting_contract_address),
|
||||||
|
bandwidth_claim_contract_address: parse_optional_str(
|
||||||
|
details.bandwidth_claim_contract_address,
|
||||||
|
),
|
||||||
|
coconut_bandwidth_contract_address: parse_optional_str(
|
||||||
|
details.coconut_bandwidth_contract_address,
|
||||||
|
),
|
||||||
|
multisig_contract_address: parse_optional_str(details.multisig_contract_address),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since these are lazily constructed, we can afford to switch some of them to stronger types in the
|
||||||
|
// future. If we do this, and also get rid of the references we could potentially unify with
|
||||||
|
// `NetworkDetails`.
|
||||||
|
pub struct DefaultNetworkDetails {
|
||||||
|
bech32_prefix: &'static str,
|
||||||
|
mix_denom: DenomDetails,
|
||||||
|
stake_denom: DenomDetails,
|
||||||
|
mixnet_contract_address: &'static str,
|
||||||
|
vesting_contract_address: &'static str,
|
||||||
|
bandwidth_claim_contract_address: &'static str,
|
||||||
|
coconut_bandwidth_contract_address: &'static str,
|
||||||
|
multisig_contract_address: &'static str,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
rewarding_validator_address: &'static str,
|
||||||
|
statistics_service_url: &'static str,
|
||||||
|
validators: Vec<ValidatorDetails>,
|
||||||
|
}
|
||||||
|
|
||||||
|
static MAINNET_DEFAULTS: Lazy<DefaultNetworkDetails> = Lazy::new(|| DefaultNetworkDetails {
|
||||||
|
bech32_prefix: mainnet::BECH32_PREFIX,
|
||||||
|
mix_denom: mainnet::MIX_DENOM,
|
||||||
|
stake_denom: mainnet::STAKE_DENOM,
|
||||||
|
mixnet_contract_address: mainnet::MIXNET_CONTRACT_ADDRESS,
|
||||||
|
vesting_contract_address: mainnet::VESTING_CONTRACT_ADDRESS,
|
||||||
|
bandwidth_claim_contract_address: mainnet::BANDWIDTH_CLAIM_CONTRACT_ADDRESS,
|
||||||
|
coconut_bandwidth_contract_address: mainnet::COCONUT_BANDWIDTH_CONTRACT_ADDRESS,
|
||||||
|
multisig_contract_address: mainnet::MULTISIG_CONTRACT_ADDRESS,
|
||||||
|
rewarding_validator_address: mainnet::REWARDING_VALIDATOR_ADDRESS,
|
||||||
|
statistics_service_url: mainnet::STATISTICS_SERVICE_DOMAIN_ADDRESS,
|
||||||
|
validators: mainnet::validators(),
|
||||||
|
});
|
||||||
|
|
||||||
|
static SANDBOX_DEFAULTS: Lazy<DefaultNetworkDetails> = Lazy::new(|| DefaultNetworkDetails {
|
||||||
|
bech32_prefix: sandbox::BECH32_PREFIX,
|
||||||
|
mix_denom: sandbox::MIX_DENOM,
|
||||||
|
stake_denom: sandbox::STAKE_DENOM,
|
||||||
|
mixnet_contract_address: sandbox::MIXNET_CONTRACT_ADDRESS,
|
||||||
|
vesting_contract_address: sandbox::VESTING_CONTRACT_ADDRESS,
|
||||||
|
bandwidth_claim_contract_address: sandbox::BANDWIDTH_CLAIM_CONTRACT_ADDRESS,
|
||||||
|
coconut_bandwidth_contract_address: sandbox::COCONUT_BANDWIDTH_CONTRACT_ADDRESS,
|
||||||
|
multisig_contract_address: sandbox::MULTISIG_CONTRACT_ADDRESS,
|
||||||
|
rewarding_validator_address: sandbox::REWARDING_VALIDATOR_ADDRESS,
|
||||||
|
statistics_service_url: sandbox::STATISTICS_SERVICE_DOMAIN_ADDRESS,
|
||||||
|
validators: sandbox::validators(),
|
||||||
|
});
|
||||||
|
|
||||||
|
static QA_DEFAULTS: Lazy<DefaultNetworkDetails> = Lazy::new(|| DefaultNetworkDetails {
|
||||||
|
bech32_prefix: qa::BECH32_PREFIX,
|
||||||
|
mix_denom: qa::MIX_DENOM,
|
||||||
|
stake_denom: qa::STAKE_DENOM,
|
||||||
|
mixnet_contract_address: qa::MIXNET_CONTRACT_ADDRESS,
|
||||||
|
vesting_contract_address: qa::VESTING_CONTRACT_ADDRESS,
|
||||||
|
bandwidth_claim_contract_address: qa::BANDWIDTH_CLAIM_CONTRACT_ADDRESS,
|
||||||
|
coconut_bandwidth_contract_address: qa::COCONUT_BANDWIDTH_CONTRACT_ADDRESS,
|
||||||
|
multisig_contract_address: qa::MULTISIG_CONTRACT_ADDRESS,
|
||||||
|
rewarding_validator_address: qa::REWARDING_VALIDATOR_ADDRESS,
|
||||||
|
statistics_service_url: qa::STATISTICS_SERVICE_DOMAIN_ADDRESS,
|
||||||
|
validators: qa::validators(),
|
||||||
|
});
|
||||||
|
|
||||||
#[derive(Debug, Copy, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||||
pub struct DenomDetails {
|
pub struct DenomDetails {
|
||||||
pub base: &'static str,
|
pub base: &'static str,
|
||||||
@@ -285,17 +349,12 @@ pub fn setup_env(config_env_file: Option<PathBuf>) {
|
|||||||
.expect("Invalid path to environment configuration file");
|
.expect("Invalid path to environment configuration file");
|
||||||
} else {
|
} else {
|
||||||
// if nothing is set, the use mainnet defaults
|
// if nothing is set, the use mainnet defaults
|
||||||
// if the user has not set `CONFIGURED`, then even if they set any of the env variables,
|
|
||||||
// overwrite them
|
|
||||||
crate::mainnet::export_to_env();
|
crate::mainnet::export_to_env();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(_) => crate::mainnet::export_to_env(),
|
Err(_) => crate::mainnet::export_to_env(),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we haven't explicitly defined any of the constants, fallback to defaults
|
|
||||||
crate::mainnet::export_to_env_if_not_set()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Name of the event triggered by the eth contract. If the event name is changed,
|
// Name of the event triggered by the eth contract. If the event name is changed,
|
||||||
|
|||||||
@@ -31,107 +31,42 @@ pub(crate) fn validators() -> Vec<ValidatorDetails> {
|
|||||||
vec![ValidatorDetails::new(NYMD_VALIDATOR, Some(API_VALIDATOR))]
|
vec![ValidatorDetails::new(NYMD_VALIDATOR, Some(API_VALIDATOR))]
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_SUFFIX: &str = "_MAINNET_DEFAULT";
|
|
||||||
|
|
||||||
fn set_var_to_default(var: &str, value: &str) {
|
|
||||||
std::env::set_var(var, value);
|
|
||||||
std::env::set_var(format!("{}{}", var, DEFAULT_SUFFIX), "1")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_var_conditionally_to_default(var: &str, value: &str) {
|
|
||||||
if std::env::var(var).is_err() {
|
|
||||||
set_var_to_default(var, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn uses_default(var: &str) -> bool {
|
|
||||||
std::env::var(format!("{}{}", var, DEFAULT_SUFFIX)).is_ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_var_if_not_default(var: &str) -> Option<String> {
|
|
||||||
if uses_default(var) {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
std::env::var(var).ok()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn export_to_env() {
|
pub fn export_to_env() {
|
||||||
set_var_to_default(var_names::CONFIGURED, "true");
|
std::env::set_var(var_names::CONFIGURED, "true");
|
||||||
set_var_to_default(var_names::BECH32_PREFIX, BECH32_PREFIX);
|
std::env::set_var(var_names::BECH32_PREFIX, BECH32_PREFIX);
|
||||||
set_var_to_default(var_names::MIX_DENOM, MIX_DENOM.base);
|
std::env::set_var(var_names::MIX_DENOM, MIX_DENOM.base);
|
||||||
set_var_to_default(var_names::MIX_DENOM_DISPLAY, MIX_DENOM.display);
|
std::env::set_var(var_names::MIX_DENOM_DISPLAY, MIX_DENOM.display);
|
||||||
set_var_to_default(var_names::STAKE_DENOM, STAKE_DENOM.base);
|
std::env::set_var(var_names::STAKE_DENOM, STAKE_DENOM.base);
|
||||||
set_var_to_default(var_names::STAKE_DENOM_DISPLAY, STAKE_DENOM.display);
|
std::env::set_var(var_names::STAKE_DENOM_DISPLAY, STAKE_DENOM.display);
|
||||||
set_var_to_default(
|
std::env::set_var(
|
||||||
var_names::DENOMS_EXPONENT,
|
var_names::DENOMS_EXPONENT,
|
||||||
&STAKE_DENOM.display_exponent.to_string(),
|
STAKE_DENOM.display_exponent.to_string(),
|
||||||
);
|
);
|
||||||
set_var_to_default(var_names::MIXNET_CONTRACT_ADDRESS, MIXNET_CONTRACT_ADDRESS);
|
std::env::set_var(var_names::MIXNET_CONTRACT_ADDRESS, MIXNET_CONTRACT_ADDRESS);
|
||||||
set_var_to_default(
|
std::env::set_var(
|
||||||
var_names::VESTING_CONTRACT_ADDRESS,
|
var_names::VESTING_CONTRACT_ADDRESS,
|
||||||
VESTING_CONTRACT_ADDRESS,
|
VESTING_CONTRACT_ADDRESS,
|
||||||
);
|
);
|
||||||
set_var_to_default(
|
std::env::set_var(
|
||||||
var_names::BANDWIDTH_CLAIM_CONTRACT_ADDRESS,
|
var_names::BANDWIDTH_CLAIM_CONTRACT_ADDRESS,
|
||||||
BANDWIDTH_CLAIM_CONTRACT_ADDRESS,
|
BANDWIDTH_CLAIM_CONTRACT_ADDRESS,
|
||||||
);
|
);
|
||||||
set_var_to_default(
|
std::env::set_var(
|
||||||
var_names::COCONUT_BANDWIDTH_CONTRACT_ADDRESS,
|
var_names::COCONUT_BANDWIDTH_CONTRACT_ADDRESS,
|
||||||
COCONUT_BANDWIDTH_CONTRACT_ADDRESS,
|
COCONUT_BANDWIDTH_CONTRACT_ADDRESS,
|
||||||
);
|
);
|
||||||
set_var_to_default(
|
std::env::set_var(
|
||||||
var_names::MULTISIG_CONTRACT_ADDRESS,
|
var_names::MULTISIG_CONTRACT_ADDRESS,
|
||||||
MULTISIG_CONTRACT_ADDRESS,
|
MULTISIG_CONTRACT_ADDRESS,
|
||||||
);
|
);
|
||||||
set_var_to_default(
|
std::env::set_var(
|
||||||
var_names::REWARDING_VALIDATOR_ADDRESS,
|
var_names::REWARDING_VALIDATOR_ADDRESS,
|
||||||
REWARDING_VALIDATOR_ADDRESS,
|
REWARDING_VALIDATOR_ADDRESS,
|
||||||
);
|
);
|
||||||
set_var_to_default(
|
std::env::set_var(
|
||||||
var_names::STATISTICS_SERVICE_DOMAIN_ADDRESS,
|
var_names::STATISTICS_SERVICE_DOMAIN_ADDRESS,
|
||||||
STATISTICS_SERVICE_DOMAIN_ADDRESS,
|
STATISTICS_SERVICE_DOMAIN_ADDRESS,
|
||||||
);
|
);
|
||||||
set_var_to_default(var_names::NYMD_VALIDATOR, NYMD_VALIDATOR);
|
std::env::set_var(var_names::NYMD_VALIDATOR, NYMD_VALIDATOR);
|
||||||
set_var_to_default(var_names::API_VALIDATOR, API_VALIDATOR);
|
std::env::set_var(var_names::API_VALIDATOR, API_VALIDATOR);
|
||||||
}
|
|
||||||
|
|
||||||
pub fn export_to_env_if_not_set() {
|
|
||||||
set_var_conditionally_to_default(var_names::CONFIGURED, "true");
|
|
||||||
set_var_conditionally_to_default(var_names::BECH32_PREFIX, BECH32_PREFIX);
|
|
||||||
set_var_conditionally_to_default(var_names::MIX_DENOM, MIX_DENOM.base);
|
|
||||||
set_var_conditionally_to_default(var_names::MIX_DENOM_DISPLAY, MIX_DENOM.display);
|
|
||||||
set_var_conditionally_to_default(var_names::STAKE_DENOM, STAKE_DENOM.base);
|
|
||||||
set_var_conditionally_to_default(var_names::STAKE_DENOM_DISPLAY, STAKE_DENOM.display);
|
|
||||||
set_var_conditionally_to_default(
|
|
||||||
var_names::DENOMS_EXPONENT,
|
|
||||||
&STAKE_DENOM.display_exponent.to_string(),
|
|
||||||
);
|
|
||||||
set_var_conditionally_to_default(var_names::MIXNET_CONTRACT_ADDRESS, MIXNET_CONTRACT_ADDRESS);
|
|
||||||
set_var_conditionally_to_default(
|
|
||||||
var_names::VESTING_CONTRACT_ADDRESS,
|
|
||||||
VESTING_CONTRACT_ADDRESS,
|
|
||||||
);
|
|
||||||
set_var_conditionally_to_default(
|
|
||||||
var_names::BANDWIDTH_CLAIM_CONTRACT_ADDRESS,
|
|
||||||
BANDWIDTH_CLAIM_CONTRACT_ADDRESS,
|
|
||||||
);
|
|
||||||
set_var_conditionally_to_default(
|
|
||||||
var_names::COCONUT_BANDWIDTH_CONTRACT_ADDRESS,
|
|
||||||
COCONUT_BANDWIDTH_CONTRACT_ADDRESS,
|
|
||||||
);
|
|
||||||
set_var_conditionally_to_default(
|
|
||||||
var_names::MULTISIG_CONTRACT_ADDRESS,
|
|
||||||
MULTISIG_CONTRACT_ADDRESS,
|
|
||||||
);
|
|
||||||
set_var_conditionally_to_default(
|
|
||||||
var_names::REWARDING_VALIDATOR_ADDRESS,
|
|
||||||
REWARDING_VALIDATOR_ADDRESS,
|
|
||||||
);
|
|
||||||
set_var_conditionally_to_default(
|
|
||||||
var_names::STATISTICS_SERVICE_DOMAIN_ADDRESS,
|
|
||||||
STATISTICS_SERVICE_DOMAIN_ADDRESS,
|
|
||||||
);
|
|
||||||
set_var_conditionally_to_default(var_names::NYMD_VALIDATOR, NYMD_VALIDATOR);
|
|
||||||
set_var_conditionally_to_default(var_names::API_VALIDATOR, API_VALIDATOR);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use crate::{DenomDetails, ValidatorDetails};
|
||||||
|
|
||||||
|
pub(crate) const BECH32_PREFIX: &str = "n";
|
||||||
|
|
||||||
|
pub const MIX_DENOM: DenomDetails = DenomDetails::new("unym", "nym", 6);
|
||||||
|
pub const STAKE_DENOM: DenomDetails = DenomDetails::new("unyx", "nyx", 6);
|
||||||
|
|
||||||
|
pub(crate) const MIXNET_CONTRACT_ADDRESS: &str =
|
||||||
|
"n1suhgf5svhu4usrurvxzlgn54ksxmn8gljarjtxqnapv8kjnp4nrsd3qaep";
|
||||||
|
pub(crate) const VESTING_CONTRACT_ADDRESS: &str =
|
||||||
|
"n1xr3rq8yvd7qplsw5yx90ftsr2zdhg4e9z60h5duusgxpv72hud3sjkxkav";
|
||||||
|
pub(crate) const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str =
|
||||||
|
"n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
|
||||||
|
pub(crate) const COCONUT_BANDWIDTH_CONTRACT_ADDRESS: &str =
|
||||||
|
"n1ghd753shjuwexxywmgs4xz7x2q732vcn7ty4yw";
|
||||||
|
pub(crate) const MULTISIG_CONTRACT_ADDRESS: &str = "n17p9rzwnnfxcjp32un9ug7yhhzgtkhvl988qccs";
|
||||||
|
pub(crate) const _ETH_CONTRACT_ADDRESS: [u8; 20] =
|
||||||
|
hex_literal::hex!("0000000000000000000000000000000000000000");
|
||||||
|
pub(crate) const _ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] =
|
||||||
|
hex_literal::hex!("0000000000000000000000000000000000000000");
|
||||||
|
pub(crate) const REWARDING_VALIDATOR_ADDRESS: &str = "n1tfzd4qz3a45u8p4mr5zmzv66457uwjgcl05jdq";
|
||||||
|
|
||||||
|
pub(crate) const STATISTICS_SERVICE_DOMAIN_ADDRESS: &str = "http://0.0.0.0";
|
||||||
|
pub(crate) fn validators() -> Vec<ValidatorDetails> {
|
||||||
|
vec![ValidatorDetails::new(
|
||||||
|
"https://qa-validator.nymtech.net",
|
||||||
|
Some("https://qa-validator-api.nymtech.net/api"),
|
||||||
|
)]
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user