Compare commits

...

31 Commits

Author SHA1 Message Date
durch f838ddffe2 Remove mixnode modifications 2023-03-27 14:21:44 +02:00
durch 7f7c33d10b Bundle libcpucycles 2023-03-27 14:11:03 +02:00
durch e7fdd3d076 mixnode feature 2023-03-27 09:57:28 +02:00
durch 4dc89bd65f Rename 2023-03-27 09:13:20 +02:00
durch 9aa3b9507d cpu cycle ffi 2023-03-15 11:21:48 +00:00
durch 61e88f304b Checkpoint 2023-03-14 14:38:34 +00:00
Pierre Dommerc 7ff043d8df Feature/bonding signature UI (#3157)
* wip

* updated gateways 'sign' command

* in-wallet verification of mix bonding signature

* changed signature of vesting contract trait method

* updated wallet bonding endpoints

* tauri commands for generating signing payloads

* renamed signer to sender

* verifying new signatures in the contract

* fixed existing mixnet unit tests

* unit tests for invalid signatures

* fixed other usages of MessageSignature + FromStr

* using base58-encoded serialization

* removed owner-signature from details response

* added ability to construct bonding payloads via nym-cli

* removed signature from bonding payload args

* moved 'message_type' from 'ContractMessageContent' to 'SignableMessage'

* refactor(wallet-rust): rename owner_signature args

* feat(wallet-bonding): handle user signature

* feat(wallet-bonding): fix bonding

* feat(wallet-bonding): fix lint issue

* feat(wallet-bonding): ui adjustment

* make the location field mandatory for payload signing

* feat(wallet-bonding): remove ownersignature field, remove dead code

---------

Co-authored-by: Jędrzej Stuczyński <jedrzej.stuczynski@gmail.com>
Co-authored-by: Tommy Verrall <tommyvez@protonmail.com>
2023-03-10 15:53:21 +01:00
Jon Häggblad c904d245d2 Remove old erc20 bandwidth-claim-contract (#3162) 2023-03-08 13:57:11 +01:00
Jon Häggblad d315a2a91b Add nym prefix to coconut contracts and crates (#3160)
* Add nym prefix to coconut-dkg

* Add nym prefix to coconut-bandwidth

* Add nym prefix to bandwidth-claim-contract

* Add nym prefix to coconut-bandwidth-contract-common

* Add nym prefix to coconut-dkg-common

* Add nym prefix to group-contract-common

* Add nym prefix to multisig-contract-common

* Add nym prefix to coconut-interface

* Add nym prefix to credential-storage

* rustfmt

* Mark coconut-test crate as private

* Fix build errors

* rustfmt
2023-03-08 13:17:09 +01:00
Tommy Verrall 0741d05ab3 Merge pull request #3140 from nymtech/feature/mock-nym-api
Add mock Nym API Typescript package
2023-03-08 14:06:54 +02:00
Jon Häggblad 61aa920362 Fix unnecessary parentheses around index expression (#3159) 2023-03-08 12:21:24 +01:00
Jon Häggblad 24b9b17e64 Add a few more nym- crate prefixes (#3158)
* Add nym- to socks5-prefixes crate

* Update imports

* rustfmt

* Add nym-socks5 prefix to proxy-helpers crate

* rustfmt

* Add nym prefix to ordered-buffer crate

* rustfmt

* Add nym prefix to service-providers-common crate

* rustfmt

* Add nym prefix to dkg crate

* Add nym prefix to credentials crate

* rustfmt

* fix build fail in tests
2023-03-08 11:56:29 +01:00
Jon Häggblad 5e3e633c4b Merge branch 'jon/fix/docs-rs-build' into develop 2023-03-08 09:37:54 +01:00
Jon Häggblad f3cff902ba Bump nym-vesting-contract to 1.2.0-pre.1 2023-03-08 09:21:27 +01:00
Jon Häggblad 4fe1b4c26f Fix docs.rs build for nym-vesting-contract and nym-bin-common 2023-03-08 09:14:07 +01:00
farbanas cb24a08b06 Merge branch 'master' into develop 2023-03-07 15:22:49 +01:00
farbanas 597a53d11a chore: bump mixnet and vesting contracts to prerelease version, update cargo locks 2023-03-07 15:17:07 +01:00
farbanas 639deeb502 Merge branch 'release/v1.1.12' 2023-03-07 14:32:34 +01:00
Jon Häggblad e263a4a21f wallet_storage: fix clippy useless-conversion (#3148) 2023-03-06 17:24:20 +01:00
Jędrzej Stuczyński 17d258d094 renamed serialized GetCW2ContractVersion to 'get_cw2_contract_version' instead of 'get_c_w2_contract_version' 2023-03-03 15:29:10 +00:00
Drazen Urch 8288d38257 Audit fixes (#2922)
* oak-2

* oak-8

* oak-13

* oak-15

* oak-18

* Minor clippy nit

* 2023-01-13-OAK-6

* 2023-01-13-OAK-3

* 2023-01-13-OAK-13

Implemented via direct dependency on cw2 and calling the appropriate code on migration

* Removed few instances of password being unecessarily copied

* 2023-01-13-OAK-10

* 2023-01-13-OAK-12

* 2021-09-13-JP-S-NYM-02

* 2021-09-13-JP-S-NYM-03

* Removed further instances of needlessly copying the mnemonic

* 2021-09-13-JP-O-PROT-03

* 2021-09-13-JP-S-NYM-01*

*: we still have one vulnerability on 'time' pulled from chrono via sqlx. However, apparently its usage is fine... Having said that, I'd still recommend removing all dependencies on chrono, but this will require some database migrations...

* 2023-01-13-OAK-11 (#3009)

* wip

* Introducing the concept of starting epoch transition in `nym-api`

* split epoch operations into multiple files

* epoch operation failure recovery

* sending rewarding transactions in correct order

* tests and fixes due to epoch state progression

* lint

* missed rebasing import changes

* Setting cw2 contract version during first migration run

* calling 'reconcile_epoch_events' at least once

* Made message to BeginEpochTransition more consistent with other variants

* Merge layer assignment updates

---------

Co-authored-by: Jędrzej Stuczyński <jedrzej.stuczynski@gmail.com>
2023-03-03 14:15:38 +00:00
Jędrzej Stuczyński 1cd560c85e fixed standalone build of 'client-core' 2023-03-03 11:39:59 +00:00
Mark Sinclair 9018642992 Add mock Nym API Typescript package 2023-03-03 11:09:35 +00:00
Jędrzej Stuczyński ad35c4006e Fixed logged hours value when determining staleness of persisted surb data (#2690) 2023-03-01 14:30:49 +00:00
Pierre Dommerc 2ec790e851 fix(explorer): fix layout responsiveness in mixnode details view (#3130) 2023-03-01 15:16:40 +01:00
Jędrzej Stuczyński 8f8cec0785 Fixed the build badge 2023-03-01 10:37:44 +00:00
farbanas 4a80cd301b fix: add step to install wasm-opt to our CI for building and uploading binaries 2023-03-01 11:06:40 +01:00
farbanas 63524eceff fix: update versions of the latest release in changelogs from 1.0.11 to 1.1.11 2023-03-01 11:06:40 +01:00
Fouad a477b007e1 TS Validator Client updates (#3085)
* Squashed clients/validator content from old branch

* fix up tests

fix up linting

* add bundle script

* add build prod script to package json

update tests

* update readme + copy to dist output

update global types

update types and tests!

* update package build

* move types and tests into src

* Squashed clients/validator content from old branch

fix up tests

fix up linting

* add bundle script

* add build prod script to package json

update tests

* update readme + copy to dist output

update global types

update types and tests!

update package build

* move types and tests into src

* build to sub-dir

* Fixing the few broken tests

---------

Co-authored-by: Jon Häggblad <jon.haggblad@gmail.com>
Co-authored-by: benedettadavico <benedetta.davico@gmail.com>
2023-03-01 10:22:35 +01:00
Jędrzej Stuczyński 5b81510325 added additional makefile targets that skip mobile-related steps (#3127) 2023-03-01 09:12:47 +00:00
farbanas 3bef973326 fix: update versions of the latest release in changelogs from 1.0.11 to 1.1.11 2023-02-28 17:27:58 +01:00
390 changed files with 15074 additions and 4704 deletions
+1
View File
@@ -43,3 +43,4 @@ Cargo.lock
nym-connect/Cargo.lock
.parcel-cache
**/.DS_Store
cpu-cycles/libcpucycles/build
Generated
+230 -341
View File
File diff suppressed because it is too large Load Diff
+1 -2
View File
@@ -22,7 +22,6 @@ members = [
"clients/native",
"clients/native/websocket-requests",
"clients/socks5",
"common/bandwidth-claim-contract",
"common/bin-common",
"common/client-libs/gateway-client",
"common/client-libs/mixnet-client",
@@ -107,7 +106,7 @@ license = "Apache-2.0"
[workspace.dependencies]
async-trait = "0.1.64"
cfg-if = "1.0.0"
dotenv = "0.15.0"
dotenvy = "0.15.6"
lazy_static = "1.4.0"
log = "0.4"
once_cell = "1.7.2"
+14 -5
View File
@@ -1,13 +1,22 @@
test: clippy-all cargo-test wasm fmt
test-no-mobile: clippy-all-no-mobile cargo-test-no-mobile wasm fmt-no-mobile
test-all: test cargo-test-expensive
test-all-no-mobile: test-no-mobile cargo-test-expensive
no-clippy: build cargo-test wasm fmt
no-clippy-no-mobile: build-no-mobile cargo-test-no-mobile wasm fmt-no-mobile
happy: fmt clippy-happy test
clippy-all: clippy-main clippy-main-examples clippy-all-contracts clippy-all-wallet clippy-all-connect clippy-all-connect-mobile clippy-all-wasm-client
clippy-happy: clippy-happy-main clippy-happy-contracts clippy-happy-wallet clippy-happy-connect clippy-happy-connect-mobile
cargo-test: test-main test-contracts test-wallet test-connect test-connect-mobile
happy-no-mobile: fmt-no-mobile clippy-happy-no-mobile test-no-mobile
clippy-all: clippy-all-no-mobile clippy-all-connect-mobile
clippy-all-no-mobile: clippy-main clippy-main-examples clippy-all-contracts clippy-all-wallet clippy-all-connect clippy-all-wasm-client
clippy-happy: clippy-happy-no-mobile clippy-happy-connect-mobile
clippy-happy-no-mobile: clippy-happy-main clippy-happy-contracts clippy-happy-wallet clippy-happy-connect
cargo-test: cargo-test-no-mobile test-connect-mobile
cargo-test-no-mobile: test-main test-contracts test-wallet test-connect
cargo-test-expensive: test-main-expensive test-contracts-expensive test-wallet-expensive test-connect-expensive
build: build-contracts build-wallet build-main build-main-examples build-connect build-connect-mobile build-wasm-client
fmt: fmt-main fmt-contracts fmt-wallet fmt-connect fmt-connect-mobile fmt-wasm-client
build: build-no-mobile build-connect-mobile
build-no-mobile: build-contracts build-wallet build-main build-main-examples build-connect build-wasm-client
fmt: fmt-no-mobile fmt-connect-mobile
fmt-no-mobile: fmt-main fmt-contracts fmt-wallet fmt-connect fmt-wasm-client
clippy-happy-main:
cargo clippy
+1 -1
View File
@@ -16,7 +16,7 @@ The platform is composed of multiple Rust crates. Top-level executable binary cr
* nym-wallet - a desktop wallet implemented using the [Tauri](https://tauri.studio/en/docs/about/intro) framework.
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg?style=for-the-badge)](https://opensource.org/licenses/Apache-2.0)
[![Build Status](https://img.shields.io/github/workflow/status/nymtech/nym/Continuous%20integration/develop?style=for-the-badge&logo=github-actions)](https://github.com/nymtech/nym/actions?query=branch%3Adevelop)
[![Build Status](https://img.shields.io/github/actions/workflow/status/nymtech/nym/build.yml?branch=develop&style=for-the-badge&logo=github-actions)](https://github.com/nymtech/nym/actions?query=branch%3Adevelop)
### Building
+4
View File
@@ -37,6 +37,10 @@ nym-topology = { path = "../../common/topology" }
validator-client = { path = "../../common/client-libs/validator-client", default-features = false }
nym-task = { path = "../../common/task" }
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.validator-client]
path = "../../common/client-libs/validator-client"
features = ["nyxd-client"]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-stream]
version = "0.1.11"
features = ["time"]
@@ -155,18 +155,21 @@ impl Backend {
// (assuming no key rotation has happened)
// but the way it's currently coded, everyone will purge old data
let since_last_flush = OffsetDateTime::now_utc() - last_flush;
if since_last_flush.whole_days() > 0 {
info!("it's been over {} days and {} hours since we last used our data store. our reply surbs are already outdated - we're going to purge them now.", since_last_flush.whole_days(), since_last_flush.whole_hours());
let days = since_last_flush.whole_days();
let hours = since_last_flush.whole_hours() % 24;
if days > 0 {
info!("it's been over {days} days and {hours} hours since we last used our data store. our reply surbs are already outdated - we're going to purge them now.");
manager.delete_all_reply_surb_data().await?;
}
if since_last_flush.whole_days() > 1 {
info!("it's been over {} days and {} hours since we last used our data store. our reply keys are already outdated - we're going to purge them now.", since_last_flush.whole_days(), since_last_flush.whole_hours());
if days > 1 {
info!("it's been over {days} days and {hours} hours since we last used our data store. our reply keys are already outdated - we're going to purge them now.");
manager.delete_all_reply_keys().await?;
}
if since_last_flush.whole_days() > 2 {
info!("it's been over {} days and {} hours since we last used our data store. our used sender tags are already outdated - we're going to purge them now.", since_last_flush.whole_days(), since_last_flush.whole_hours());
if days > 2 {
info!("it's been over {days} days and {hours} hours since we last used our data store. our used sender tags are already outdated - we're going to purge them now.");
manager.delete_all_tags().await?;
}
+3 -3
View File
@@ -15,10 +15,10 @@ thiserror = "1.0"
url = "2.2"
tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal", "macros"] } # async runtime
coconut-interface = { path = "../../common/coconut-interface" }
nym-coconut-interface = { path = "../../common/coconut-interface" }
nym-config = { path = "../../common/config" }
credentials = { path = "../../common/credentials" }
credential-storage = { path = "../../common/credential-storage" }
nym-credentials = { path = "../../common/credentials" }
nym-credential-storage = { path = "../../common/credential-storage" }
nym-crypto = { path = "../../common/crypto", features = ["rand", "asymmetric", "symmetric", "aes", "hashing"] }
nym-bin-common = { path = "../../common/bin-common"}
nym-network-defaults = { path = "../../common/network-defaults" }
+5 -5
View File
@@ -7,11 +7,11 @@ use nym_bin_common::completions::ArgShell;
use rand::rngs::OsRng;
use std::str::FromStr;
use coconut_interface::{Base58, Parameters};
use credential_storage::storage::Storage;
use credential_storage::PersistentStorage;
use credentials::coconut::bandwidth::{BandwidthVoucher, TOTAL_ATTRIBUTES};
use credentials::coconut::utils::obtain_aggregate_signature;
use nym_coconut_interface::{Base58, Parameters};
use nym_credential_storage::storage::Storage;
use nym_credential_storage::PersistentStorage;
use nym_credentials::coconut::bandwidth::{BandwidthVoucher, TOTAL_ATTRIBUTES};
use nym_credentials::coconut::utils::obtain_aggregate_signature;
use nym_crypto::asymmetric::{encryption, identity};
use nym_network_defaults::VOUCHER_INFO;
use validator_client::nyxd::traits::DkgQueryClient;
+2 -2
View File
@@ -4,8 +4,8 @@
use std::time::SystemTimeError;
use thiserror::Error;
use credential_storage::error::StorageError;
use credentials::error::Error as CredentialError;
use nym_credential_storage::error::StorageError;
use nym_credentials::error::Error as CredentialError;
use nym_crypto::asymmetric::encryption::KeyRecoveryError;
use nym_crypto::asymmetric::identity::Ed25519RecoveryError;
use validator_client::nyxd::error::NyxdError;
+1 -1
View File
@@ -77,7 +77,7 @@ async fn main() -> Result<()> {
.client_home_directory
.join(DATA_DIR)
.join(CRED_DB_FILE_NAME);
let shared_storage = credential_storage::initialise_storage(db_path).await;
let shared_storage = nym_credential_storage::initialise_storage(db_path).await;
let recovery_storage = recovery_storage::RecoveryStorage::new(r.recovery_dir)?;
let network_details = NymNetworkDetails::new_from_env();
+1 -1
View File
@@ -1,7 +1,7 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use credentials::coconut::bandwidth::BandwidthVoucher;
use nym_credentials::coconut::bandwidth::BandwidthVoucher;
use std::fs::{create_dir_all, read_dir, File};
use std::io::{Read, Write};
use std::path::PathBuf;
+2 -2
View File
@@ -1,8 +1,8 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use coconut_interface::Parameters;
use credentials::coconut::bandwidth::BandwidthVoucher;
use nym_coconut_interface::Parameters;
use nym_credentials::coconut::bandwidth::BandwidthVoucher;
use nym_crypto::asymmetric::{encryption, identity};
+3 -3
View File
@@ -36,10 +36,10 @@ tokio-tungstenite = "0.14" # websocket
## internal
nym-bin-common = { path = "../../common/bin-common" }
client-core = { path = "../client-core", features = ["fs-surb-storage"] }
coconut-interface = { path = "../../common/coconut-interface" }
nym-coconut-interface = { path = "../../common/coconut-interface" }
nym-config = { path = "../../common/config" }
credential-storage = { path = "../../common/credential-storage" }
credentials = { path = "../../common/credentials" }
nym-credential-storage = { path = "../../common/credential-storage" }
nym-credentials = { path = "../../common/credentials" }
nym-crypto = { path = "../../common/crypto" }
gateway-client = { path = "../../common/client-libs/gateway-client" }
gateway-requests = { path = "../../gateway/gateway-requests" }
+1 -1
View File
@@ -74,7 +74,7 @@ impl SocketClient {
let client = validator_client::Client::new_query(client_config)
.expect("Could not construct query client");
BandwidthController::new(
credential_storage::initialise_storage(config.get_base().get_database_path()).await,
nym_credential_storage::initialise_storage(config.get_base().get_database_path()).await,
client,
)
}
+8 -8
View File
@@ -29,26 +29,26 @@ url = "2.2"
# internal
nym-bin-common = { path = "../../common/bin-common" }
client-core = { path = "../client-core", features = ["fs-surb-storage"] }
coconut-interface = { path = "../../common/coconut-interface" }
nym-coconut-interface = { path = "../../common/coconut-interface" }
nym-config = { path = "../../common/config" }
credential-storage = { path = "../../common/credential-storage", optional = true }
nym-credential-storage = { path = "../../common/credential-storage", optional = true }
mobile-storage = { path = "../../common/mobile-storage", optional = true }
credentials = { path = "../../common/credentials" }
nym-credentials = { path = "../../common/credentials" }
nym-crypto = { path = "../../common/crypto" }
gateway-client = { path = "../../common/client-libs/gateway-client" }
gateway-requests = { path = "../../gateway/gateway-requests" }
nym-network-defaults = { path = "../../common/network-defaults" }
nym-sphinx = { path = "../../common/nymsphinx" }
ordered-buffer = { path = "../../common/socks5/ordered-buffer" }
nym-ordered-buffer = { path = "../../common/socks5/ordered-buffer" }
nym-pemstore = { path = "../../common/pemstore" }
proxy-helpers = { path = "../../common/socks5/proxy-helpers" }
service-providers-common = { path = "../../service-providers/common" }
socks5-requests = { path = "../../common/socks5/requests" }
nym-socks5-proxy-helpers = { path = "../../common/socks5/proxy-helpers" }
nym-service-providers-common = { path = "../../service-providers/common" }
nym-socks5-requests = { path = "../../common/socks5/requests" }
nym-task = { path = "../../common/task" }
nym-topology = { path = "../../common/topology" }
validator-client = { path = "../../common/client-libs/validator-client", features = ["nyxd-client"] }
[features]
default = ["credential-storage"]
default = ["nym-credential-storage"]
eth = []
mobile = ["mobile-storage", "gateway-client/mobile"]
+2 -2
View File
@@ -7,10 +7,10 @@ pub use client_core::config::MISSING_VALUE;
use client_core::config::{ClientCoreConfigTrait, DebugConfig};
use nym_config::defaults::DEFAULT_SOCKS5_LISTENING_PORT;
use nym_config::{NymConfig, OptionalSet};
use nym_service_providers_common::interface::ProviderInterfaceVersion;
use nym_socks5_requests::Socks5ProtocolVersion;
use nym_sphinx::addressing::clients::Recipient;
use serde::{Deserialize, Serialize};
use service_providers_common::interface::ProviderInterfaceVersion;
use socks5_requests::Socks5ProtocolVersion;
use std::fmt::Debug;
use std::path::PathBuf;
use std::str::FromStr;
+1 -1
View File
@@ -92,7 +92,7 @@ impl NymClient {
#[cfg(not(feature = "mobile"))]
let storage =
credential_storage::initialise_storage(config.get_base().get_database_path()).await;
nym_credential_storage::initialise_storage(config.get_base().get_database_path()).await;
#[cfg(feature = "mobile")]
let storage = mobile_storage::PersistentStorage {};
+1 -1
View File
@@ -1,6 +1,6 @@
use crate::socks::types::SocksProxyError;
use client_core::error::ClientCoreError;
use socks5_requests::{ConnectionError, ConnectionId};
use nym_socks5_requests::{ConnectionError, ConnectionId};
#[derive(thiserror::Error, Debug)]
pub enum Socks5ClientError {
+8 -8
View File
@@ -8,19 +8,19 @@ use client_core::client::inbound_messages::{InputMessage, InputMessageSender};
use futures::channel::mpsc;
use futures::task::{Context, Poll};
use log::*;
use nym_service_providers_common::interface::{ProviderInterfaceVersion, RequestVersion};
use nym_socks5_proxy_helpers::connection_controller::{
ConnectionReceiver, ControllerCommand, ControllerSender,
};
use nym_socks5_proxy_helpers::proxy_runner::ProxyRunner;
use nym_socks5_requests::{
ConnectionId, RemoteAddress, Socks5ProtocolVersion, Socks5ProviderRequest, Socks5Request,
};
use nym_sphinx::addressing::clients::Recipient;
use nym_task::connections::{LaneQueueLengths, TransmissionLane};
use nym_task::TaskClient;
use pin_project::pin_project;
use proxy_helpers::connection_controller::{
ConnectionReceiver, ControllerCommand, ControllerSender,
};
use proxy_helpers::proxy_runner::ProxyRunner;
use rand::RngCore;
use service_providers_common::interface::{ProviderInterfaceVersion, RequestVersion};
use socks5_requests::{
ConnectionId, RemoteAddress, Socks5ProtocolVersion, Socks5ProviderRequest, Socks5Request,
};
use std::io;
use std::net::SocketAddr;
use std::pin::Pin;
+3 -3
View File
@@ -4,11 +4,11 @@ use log::*;
use client_core::client::received_buffer::ReconstructedMessagesReceiver;
use client_core::client::received_buffer::{ReceivedBufferMessage, ReceivedBufferRequestSender};
use nym_service_providers_common::interface::{ControlResponse, ResponseContent};
use nym_socks5_proxy_helpers::connection_controller::ControllerSender;
use nym_socks5_requests::{Socks5ProviderResponse, Socks5Response, Socks5ResponseContent};
use nym_sphinx::receiver::ReconstructedMessage;
use nym_task::TaskClient;
use proxy_helpers::connection_controller::ControllerSender;
use service_providers_common::interface::{ControlResponse, ResponseContent};
use socks5_requests::{Socks5ProviderResponse, Socks5Response, Socks5ResponseContent};
use crate::error::Socks5ClientError;
+1 -1
View File
@@ -8,10 +8,10 @@ use client_core::client::{
inbound_messages::InputMessageSender, received_buffer::ReceivedBufferRequestSender,
};
use log::*;
use nym_socks5_proxy_helpers::connection_controller::Controller;
use nym_sphinx::addressing::clients::Recipient;
use nym_task::connections::{ConnectionCommandSender, LaneQueueLengths};
use nym_task::TaskClient;
use proxy_helpers::connection_controller::Controller;
use std::net::SocketAddr;
use tap::TapFallible;
use tokio::net::TcpListener;
+1 -1
View File
@@ -1,4 +1,4 @@
use socks5_requests::Socks5RequestError;
use nym_socks5_requests::Socks5RequestError;
use std::string::FromUtf8Error;
use thiserror::Error;
+68 -80
View File
@@ -1,83 +1,71 @@
{
"root": true,
"env": {
"browser": true,
"es6": true,
"node": true
},
"parserOptions": {
"ecmaVersion": 2019,
"sourceType": "module"
},
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"plugins": ["prettier", "mocha"],
"extends": [
"airbnb-base",
"airbnb-typescript/base",
"prettier"],
"rules": {
"prettier/prettier": "error",
"import/prefer-default-export": "off",
"import/no-extraneous-dependencies": [
"error",
{
"devDependencies": [
"**/*.test.[jt]s",
"**/*.spec.[jt]s"
]
}
],
"import/extensions": [
"error",
"ignorePackages",
{
"ts": "never",
"js": "never"
}
]
},
"overrides": [
{
"files": "**/*.ts",
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "./tsconfig.json"
},
"plugins": ["@typescript-eslint/eslint-plugin"],
"extends": [
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"prettier"
],
"rules": {
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-var-requires": "off",
"no-use-before-define": [0],
"@typescript-eslint/no-use-before-define": [1],
"import/no-unresolved": 0,
"import/no-extraneous-dependencies": [
"error",
{
"devDependencies": [
"**/*.test.ts",
"**/*.spec.ts"
]
}
],
"quotes": "off",
"@typescript-eslint/quotes": [
2,
"single",
{
"avoidEscape": true
}
],
"@typescript-eslint/no-unused-vars": [2, { "argsIgnorePattern": "^_" }]
}
}
"root": true,
"env": {
"browser": true,
"es6": true,
"node": true
},
"parserOptions": {
"ecmaVersion": 2019,
"sourceType": "module"
},
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"plugins": ["prettier", "mocha"],
"extends": ["airbnb-base", "airbnb-typescript/base", "prettier"],
"rules": {
"prettier/prettier": "error",
"import/prefer-default-export": "off",
"import/no-extraneous-dependencies": [
"error",
{
"devDependencies": ["**/*.test.[jt]s", "**/*.spec.[jt]s"]
}
],
"import/extensions": [
"error",
"ignorePackages",
{
"ts": "never",
"js": "never"
}
]
},
"overrides": [
{
"files": "**/*.ts",
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "./tsconfig.json"
},
"plugins": ["@typescript-eslint/eslint-plugin"],
"extends": ["plugin:@typescript-eslint/eslint-recommended", "plugin:@typescript-eslint/recommended", "prettier"],
"rules": {
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-var-requires": "off",
"no-use-before-define": [0],
"@typescript-eslint/no-use-before-define": [1],
"import/no-unresolved": 0,
"import/no-extraneous-dependencies": [
"error",
{
"devDependencies": ["**/*.test.ts", "**/*.spec.ts"]
}
],
"quotes": "off",
"@typescript-eslint/quotes": [
2,
"single",
{
"avoidEscape": true
}
],
"@typescript-eslint/no-unused-vars": [2, { "argsIgnorePattern": "^_" }]
}
}
],
"ignorePatterns": ["tsconfig.json", "*.d.ts", "dist/**/*", "dist", "node_modules"]
}
+2 -1
View File
@@ -4,4 +4,5 @@ coverage
dist
docs
examples/accounts
node_modules
node_modules
.env
+3 -1
View File
@@ -1,3 +1,5 @@
coverage
node_modules
tests
tests
src
type
+24 -25
View File
@@ -1,40 +1,39 @@
Nym Validator Client
====================
# Nym Validator Client (Typescript)
A TypeScript client for interacting with CosmWasm smart contracts in Nym validators.
A TypeScript client for interacting with CosmWasm smart contracts in Nym validators.
Running examples
-----------------
With the code checked out, `cd examples`. This folder contains runnable example code that will set up a blockchain and allow you to interact with it through the client.
Running tests
-------------
Include the Nym Validator in your project:
```
npm test
yarn add @nymproject/nym-validator-client
```
You can also trigger test execution with a test watcher. I don't have the centuries of life left to me that are needed to fight through the arcana of wiring up a working TypeScript mocha triggered execution setup, so for now my Cargo-based hack is:
Connect to validator and make queries
```
cargo watch -s "cd clients/validator && npm test"
import Validator from '@nymproject/nym-validator-client'
const main = async () => {
const client = await Validator.connectForQuery(rpcAddress, validatorAddress, prefix, mixnetContractAddress, vestingContractAddress, denom)
client.getBalance(address)
}
```
It's ugly but works fine if you have Cargo installed. TypeScript setup help happily accepted here.
Connect to validator for performing actions
Generating Documentation
------------------------
```
import Validator from '@nymproject/nym-validator-client'
You can generate docs by running `npm run docs`. Generated output will appear in the `docs` directory.
const main = async () => {
Packaging
------------------------
const client = await Validator.connect(mnemonic, rpcAddress, validatorAddress, prefix, mixnetContractAddress, vestingContractAddress, denom)
If you're a Nym platform developer who's made changes to the client and wants to re-publish the package to NPM, here's how you do it:
const res = await client.send(address, [{ amount: '10000000', denom: 'unym' }]);
1. Bump the version number (use SemVer)
1. `npm run build`
1. `npm login` (if you haven't already)
1. `npm publish`
}
```
+34 -18
View File
@@ -4,15 +4,23 @@
"description": "A TypeScript client for interacting with smart contracts in Nym validators",
"repository": "https://github.com/nymtech/nym",
"main": "./dist/index.js",
"types": "dist/index.d.ts",
"types": "./dist/index.d.ts",
"scripts": {
"build": "tsc",
"test": "ts-mocha tests/**/*.test.ts",
"build": "rollup -c ./rollup.config.mjs",
"build:types": "rollup-type-bundler --dist ./dist/nym-validator-client",
"build:prod": "sh ./scripts/build-prod.sh",
"test": "ts-mocha -p ./tsconfig.test.json ./src/tests/**/*.test.ts",
"testmock": "ts-mocha -p ./tsconfig.test.json ./src/tests/mock/*.test.ts",
"coverage": "nyc npm test",
"lint": "eslint src",
"lint:fix": "eslint src --fix",
"clean": "rm -rf ./dist",
"lint": "eslint",
"lint:fix": "eslint --fix",
"lint:tsc": "tsc --noEmit",
"docs": "typedoc --out docs src/index.ts"
},
"files": [
"./dist/*"
],
"keywords": [],
"author": "Nym Technologies SA (https://nymtech.net)",
"contributors": [
@@ -21,6 +29,14 @@
],
"license": "Apache-2.0",
"devDependencies": {
"@favware/rollup-type-bundler": "^2.0.0",
"@nymproject/types": "^1.0.0",
"@rollup/plugin-commonjs": "^24.0.1",
"@rollup/plugin-json": "^6.0.0",
"@rollup/plugin-typescript": "^11.0.0",
"@rollup/plugin-node-resolve": "^15.0.1",
"rollup": "^3.17.2",
"rollup-plugin-dts": "^5.2.0",
"@typescript-eslint/eslint-plugin": "^5.7.0",
"@typescript-eslint/parser": "^5.7.0",
"eslint": "^7.18.0",
@@ -31,21 +47,21 @@
"eslint-plugin-import": "^2.25.4",
"eslint-plugin-mocha": "^10.0.3",
"eslint-plugin-prettier": "^4.0.0",
"expect": "^28.1.3",
"mocha": "^10.0.0",
"prettier": "^2.5.1",
"typedoc": "^0.22.13",
"ts-mocha": "^10.0.0",
"typescript": "^4.6.2"
},
"dependencies": {
"@cosmjs/cosmwasm-stargate": "^0.28.0",
"@cosmjs/crypto": "^0.28.0",
"@cosmjs/math": "^0.28.0",
"@cosmjs/proto-signing": "^0.28.0",
"@cosmjs/stargate": "^0.28.0",
"@cosmjs/tendermint-rpc": "^0.28.0",
"axios": "^0.26.1",
"cosmjs-types": "^0.4.1"
"typedoc": "^0.22.13",
"typescript": "^4.6.2",
"cosmjs-types": "^0.4.1",
"dotenv": "^16.0.3",
"expect": "^28.1.3",
"moq.ts": "^7.3.4",
"@cosmjs/cosmwasm-stargate": "^0.29.5",
"@cosmjs/crypto": "^0.29.5",
"@cosmjs/math": "^0.29.5",
"@cosmjs/proto-signing": "^0.29.5",
"@cosmjs/stargate": "^0.29.5",
"@cosmjs/tendermint-rpc": "^0.29.5",
"axios": "^1.3.3"
}
}
+15
View File
@@ -0,0 +1,15 @@
import typescript from '@rollup/plugin-typescript';
import resolve from '@rollup/plugin-node-resolve';
import json from '@rollup/plugin-json';
import commonjs from '@rollup/plugin-commonjs';
export default [
{
input: './src/index.ts',
output: {
dir: 'dist/nym-validator-client',
format: 'cjs',
},
plugins: [resolve(), typescript(), commonjs(), json()],
},
];
+31
View File
@@ -0,0 +1,31 @@
#!/usr/bin/env bash
set -o errexit
set -o nounset
rm -rf ./dist || true
rm -rf ../../dist || true
# Bundle application
yarn build
# Bundle types
yarn build:types
# Build package.json for bundle
node ./scripts/buildPackageJson.mjs
# Copy README
cp README.md dist/nym-validator-client
# move the output outside of the yarn/npm workspaces
mv ./dist ../../
echo "Output can be found in:"
realpath ../../dist
@@ -0,0 +1,20 @@
import * as fs from 'fs';
// parse the package.json from the SDK, so we can keep fields like the name and version
const json = JSON.parse(fs.readFileSync('./package.json').toString());
// defaults (NB: these are in the output file locations)
const main = 'index.js';
const types = 'index.d.ts';
// make a package.json for the bundle
const packageJson = {
name: json.name,
version: json.version,
license: json.license,
author: json.author,
main,
types,
};
fs.writeFileSync('./dist/nym-validator-client/package.json', JSON.stringify(packageJson, null, 2));
+1 -9
View File
@@ -1,5 +1,6 @@
import { Decimal } from '@cosmjs/math';
import { Coin } from '@cosmjs/stargate';
import { CoinMap } from './types/shared';
// NARROW NO-BREAK SPACE (U+202F)
const thinSpace = '\u202F';
@@ -34,15 +35,6 @@ export function nativeToPrintable(nativeValue: string): string {
return Decimal.fromAtomics(nativeValue, 6).toString();
}
export interface MappedCoin {
readonly denom: string;
readonly fractionalDigits: number;
}
export interface CoinMap {
readonly [key: string]: MappedCoin;
}
export function nativeCoinToDisplay(coin: Coin, coinMap: CoinMap): Coin {
if (!coinMap) return coin;
+133 -75
View File
@@ -1,6 +1,3 @@
import { Bip39, Random } from '@cosmjs/crypto';
import { DirectSecp256k1HdWallet, EncodeObject } from '@cosmjs/proto-signing';
import { coin as cosmosCoin, Coin, DeliverTxResponse, isDeliverTxFailure, StdFee } from '@cosmjs/stargate';
import {
ExecuteResult,
InstantiateOptions,
@@ -8,45 +5,37 @@ import {
MigrateResult,
UploadResult,
} from '@cosmjs/cosmwasm-stargate';
import SigningClient, { ISigningClient } from './signing-client';
import { Bip39, Random } from '@cosmjs/crypto';
import { DirectSecp256k1HdWallet, EncodeObject } from '@cosmjs/proto-signing';
import { Coin, coin as cosmosCoin, DeliverTxResponse, isDeliverTxFailure, StdFee } from '@cosmjs/stargate';
import {
ContractStateParams,
Delegation,
Gateway,
GatewayBond,
GatewayOwnershipResponse,
LayerDistribution,
MixnetContractVersion,
MixNode,
MixNodeBond,
MixNodeCostParams,
MixNodeDetails,
MixNodeRewarding,
MixOwnershipResponse,
PagedAllDelegationsResponse,
PagedDelegatorDelegationsResponse,
PagedGatewayResponse,
PagedMixDelegationsResponse,
PagedMixnodeResponse,
} from './types';
import {
CoinMap,
displayAmountToNative,
MappedCoin,
nativeCoinToDisplay,
nativeToPrintable,
printableBalance,
printableCoin,
} from './currency';
PagedMixNodeBondResponse,
PagedMixNodeDetailsResponse,
PagedUnbondedMixnodesResponse,
RewardingParams,
StakeSaturationResponse,
UnbondedMixnodeResponse,
} from '@nymproject/types';
import QueryClient from './query-client';
import { nymGasPrice } from './stargate-helper';
export { coins, coin } from '@cosmjs/stargate';
export { Coin };
export {
displayAmountToNative,
nativeCoinToDisplay,
printableCoin,
printableBalance,
nativeToPrintable,
MappedCoin,
CoinMap,
};
export { nymGasPrice };
import SigningClient, { ISigningClient } from './signing-client';
import { ContractState } from './types/shared';
export interface INymClient {
readonly mixnetContract: string;
@@ -147,7 +136,7 @@ export default class ValidatorClient implements INymClient {
return DirectSecp256k1HdWallet.fromMnemonic(mnemonic, signerOptions);
}
getBalance(address: string): Promise<Coin> {
async getBalance(address: string): Promise<Coin> {
return this.client.getBalance(address, this.denom);
}
@@ -159,15 +148,39 @@ export default class ValidatorClient implements INymClient {
return this.client.getCachedMixnodes();
}
async getActiveMixnodes(): Promise<MixNodeBond[]> {
async getStakeSaturation(mixId: number): Promise<StakeSaturationResponse> {
return this.client.getStakeSaturation(this.mixnetContract, mixId);
}
async getActiveMixnodes(): Promise<MixNodeDetails[]> {
return this.client.getActiveMixnodes();
}
async getUnbondedMixNodeInformation(mixId: number): Promise<UnbondedMixnodeResponse> {
return this.client.getUnbondedMixNodeInformation(this.mixnetContract, mixId);
}
async getRewardedMixnodes(): Promise<MixNodeBond[]> {
return this.client.getRewardedMixnodes();
}
public async getMixnetContractSettings(): Promise<ContractStateParams> {
async getMixnodeRewardingDetails(mixId: number): Promise<MixNodeRewarding> {
return this.client.getMixnodeRewardingDetails(this.mixnetContract, mixId);
}
async getOwnedMixnode(address: string): Promise<MixOwnershipResponse> {
return this.client.getOwnedMixnode(this.mixnetContract, address);
}
async ownsGateway(address: string): Promise<GatewayOwnershipResponse> {
return this.client.ownsGateway(this.mixnetContract, address);
}
async getLayerDistribution(): Promise<LayerDistribution> {
return this.client.getLayerDistribution(this.mixnetContract);
}
public async getMixnetContractSettings(): Promise<ContractState> {
return this.client.getStateParams(this.mixnetContract);
}
@@ -175,29 +188,74 @@ export default class ValidatorClient implements INymClient {
return this.client.getContractVersion(this.mixnetContract);
}
public async getRewardPool(): Promise<string> {
return this.client.getRewardPool(this.mixnetContract);
public async getVestingContractVersion(): Promise<MixnetContractVersion> {
return this.client.getContractVersion(this.vestingContract);
}
public async getCirculatingSupply(): Promise<string> {
return this.client.getCirculatingSupply(this.mixnetContract);
public async getSpendableCoins(vestingAccountAddress: string): Promise<MixnetContractVersion> {
return this.client.getSpendableCoins(this.vestingContract, vestingAccountAddress);
}
public async getSybilResistancePercent(): Promise<number> {
return this.client.getSybilResistancePercent(this.mixnetContract);
public async getRewardParams(): Promise<RewardingParams> {
return this.client.getRewardParams(this.mixnetContract);
}
public async getIntervalRewardPercent(): Promise<number> {
return this.client.getIntervalRewardPercent(this.mixnetContract);
async getUnbondedMixNodes(): Promise<UnbondedMixnodeResponse[]> {
let mixNodes: UnbondedMixnodeResponse[] = [];
const limit = 50;
let startAfter;
for (;;) {
// eslint-disable-next-line no-await-in-loop
const pagedResponse: PagedUnbondedMixnodesResponse = await this.client.getUnbondedMixNodes(
this.mixnetContract,
limit,
startAfter,
);
mixNodes = mixNodes.concat(pagedResponse.nodes);
startAfter = pagedResponse.start_next_after;
// if `start_next_after` is not set, we're done
if (!startAfter) {
break;
}
}
return mixNodes;
}
public async getAllNyxdMixnodes(): Promise<MixNodeBond[]> {
public async getMixNodeBonds(): Promise<MixNodeBond[]> {
let mixNodes: MixNodeBond[] = [];
const limit = 50;
let startAfter;
for (; ;) {
for (;;) {
// eslint-disable-next-line no-await-in-loop
const pagedResponse: PagedMixnodeResponse = await this.client.getMixNodesPaged(this.mixnetContract, limit);
const pagedResponse: PagedMixNodeBondResponse = await this.client.getMixNodeBonds(
this.mixnetContract,
limit,
startAfter,
);
mixNodes = mixNodes.concat(pagedResponse.nodes);
startAfter = pagedResponse.start_next_after;
// if `start_next_after` is not set, we're done
if (!startAfter) {
break;
}
}
return mixNodes;
}
public async getMixNodesDetailed(): Promise<MixNodeDetails[]> {
let mixNodes: MixNodeDetails[] = [];
const limit = 50;
let startAfter;
for (;;) {
// eslint-disable-next-line no-await-in-loop
const pagedResponse: PagedMixNodeDetailsResponse = await this.client.getMixNodesDetailed(
this.mixnetContract,
limit,
startAfter,
);
mixNodes = mixNodes.concat(pagedResponse.nodes);
startAfter = pagedResponse.start_next_after;
// if `start_next_after` is not set, we're done
@@ -210,37 +268,24 @@ export default class ValidatorClient implements INymClient {
}
public async getAllNyxdGateways(): Promise<GatewayBond[]> {
let gateways: GatewayBond[] = [];
const limit = 50;
let startAfter;
for (; ;) {
// eslint-disable-next-line no-await-in-loop
const pagedResponse: PagedGatewayResponse = await this.client.getGatewaysPaged(this.mixnetContract, limit);
gateways = gateways.concat(pagedResponse.nodes);
startAfter = pagedResponse.start_next_after;
// if `start_next_after` is not set, we're done
if (!startAfter) {
break;
}
}
return gateways;
const pagedResponse: PagedGatewayResponse = await this.client.getGatewaysPaged(this.mixnetContract);
return pagedResponse.nodes;
}
/**
* Gets list of all delegations towards particular mixnode.
*
* @param mixIdentity identity of the node to which the delegation was sent
* @param mix_id identity of the node to which the delegation was sent
*/
public async getAllNyxdSingleMixnodeDelegations(mixIdentity: string): Promise<Delegation[]> {
public async getAllNyxdSingleMixnodeDelegations(mix_id: number): Promise<Delegation[]> {
let delegations: Delegation[] = [];
const limit = 250;
let startAfter;
for (; ;) {
for (;;) {
// eslint-disable-next-line no-await-in-loop
const pagedResponse: PagedMixDelegationsResponse = await this.client.getMixNodeDelegationsPaged(
this.mixnetContract,
mixIdentity,
mix_id,
limit,
startAfter,
);
@@ -259,7 +304,7 @@ export default class ValidatorClient implements INymClient {
let delegations: Delegation[] = [];
const limit = 250;
let startAfter;
for (; ;) {
for (;;) {
// eslint-disable-next-line no-await-in-loop
const pagedResponse: PagedDelegatorDelegationsResponse = await this.client.getDelegatorDelegationsPaged(
this.mixnetContract,
@@ -278,13 +323,13 @@ export default class ValidatorClient implements INymClient {
return delegations;
}
public async getAllNyxdNetworkDelegations(): Promise<Delegation[]> {
public async getAllNyxdDelegations(): Promise<Delegation[]> {
let delegations: Delegation[] = [];
const limit = 250;
let startAfter;
for (; ;) {
for (;;) {
// eslint-disable-next-line no-await-in-loop
const pagedResponse: PagedAllDelegationsResponse = await this.client.getAllNetworkDelegationsPaged(
const pagedResponse: PagedAllDelegationsResponse = await this.client.getAllDelegationsPaged(
this.mixnetContract,
limit,
startAfter,
@@ -300,6 +345,10 @@ export default class ValidatorClient implements INymClient {
return delegations;
}
public async getDelegationDetails(mix_id: number, delegator: string): Promise<Delegation> {
return this.client.getDelegationDetails(this.mixnetContract, mix_id, delegator);
}
/**
* Generate a minimum gateway bond required to create a fresh mixnode.
*
@@ -308,7 +357,7 @@ export default class ValidatorClient implements INymClient {
public async minimumMixnodePledge(): Promise<Coin> {
const stateParams = await this.getMixnetContractSettings();
// we trust the contract to return a valid number
return cosmosCoin(stateParams.minimum_mixnode_pledge, this.prefix);
return cosmosCoin(stateParams.params.minimum_mixnode_pledge, this.prefix);
}
/**
@@ -319,7 +368,7 @@ export default class ValidatorClient implements INymClient {
public async minimumGatewayPledge(): Promise<Coin> {
const stateParams = await this.getMixnetContractSettings();
// we trust the contract to return a valid number
return cosmosCoin(stateParams.minimum_gateway_pledge, this.prefix);
return cosmosCoin(stateParams.params.minimum_gateway_pledge, this.prefix);
}
public async send(
@@ -393,12 +442,21 @@ export default class ValidatorClient implements INymClient {
public async bondMixNode(
mixNode: MixNode,
ownerSignature: string,
costParams: MixNodeCostParams,
pledge: Coin,
fee?: StdFee | 'auto' | number,
memo?: string,
): Promise<ExecuteResult> {
this.assertSigning();
return (this.client as ISigningClient).bondMixNode(this.mixnetContract, mixNode, ownerSignature, pledge, fee, memo);
return (this.client as ISigningClient).bondMixNode(
this.mixnetContract,
mixNode,
costParams,
ownerSignature,
pledge,
fee,
memo,
);
}
public async unbondMixNode(fee?: StdFee | 'auto' | number, memo?: string): Promise<ExecuteResult> {
@@ -423,29 +481,29 @@ export default class ValidatorClient implements INymClient {
}
public async delegateToMixNode(
mixIdentity: string,
mixId: number,
amount: Coin,
fee?: StdFee | 'auto' | number,
memo?: string,
): Promise<ExecuteResult> {
this.assertSigning();
return (this.client as ISigningClient).delegateToMixNode(this.mixnetContract, mixIdentity, amount, fee, memo);
return (this.client as ISigningClient).delegateToMixNode(this.mixnetContract, mixId, amount, fee, memo);
}
public async undelegateFromMixNode(
mixIdentity: string,
mixId: number,
fee?: StdFee | 'auto' | number,
memo?: string,
): Promise<ExecuteResult> {
return (this.client as ISigningClient).undelegateFromMixNode(this.mixnetContract, mixIdentity, fee, memo);
return (this.client as ISigningClient).undelegateFromMixNode(this.mixnetContract, mixId, fee, memo);
}
public async updateMixnodeConfig(
mixIdentity: string,
mixId: number,
fee: StdFee | 'auto' | number,
profitPercentage: number,
): Promise<ExecuteResult> {
return (this.client as ISigningClient).updateMixnodeConfig(this.mixnetContract, mixIdentity, profitPercentage, fee);
return (this.client as ISigningClient).updateMixnodeConfig(this.mixnetContract, mixId, profitPercentage, fee);
}
public async updateContractStateParams(
+3 -4
View File
@@ -2,9 +2,8 @@
* Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
* SPDX-License-Identifier: Apache-2.0
*/
import axios from 'axios';
import { GatewayBond, MixNodeBond } from './types';
import { GatewayBond, MixNodeBond, MixNodeDetails } from '@nymproject/types';
export const NYM_API_VERSION = '/v1';
export const NYM_API_GATEWAYS_PATH = `${NYM_API_VERSION}/gateways`;
@@ -17,7 +16,7 @@ export interface INymApiQuery {
getCachedGateways(): Promise<GatewayBond[]>;
getActiveMixnodes(): Promise<MixNodeBond[]>;
getActiveMixnodes(): Promise<MixNodeDetails[]>;
getRewardedMixnodes(): Promise<MixNodeBond[]>;
}
@@ -51,7 +50,7 @@ export default class NymApiQuerier implements INymApiQuery {
throw new Error('None of the provided validator APIs seem to be alive');
}
async getActiveMixnodes(): Promise<MixNodeBond[]> {
async getActiveMixnodes(): Promise<MixNodeDetails[]> {
const url = new URL(this.nymApiUrl);
url.pathname += NYM_API_ACTIVE_MIXNODES_PATH;
+72 -56
View File
@@ -2,28 +2,24 @@
* Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
* SPDX-License-Identifier: Apache-2.0
*/
import { JsonObject } from '@cosmjs/cosmwasm-stargate';
// eslint-disable-next-line import/no-cycle
import { INyxdQuery } from './query-client';
import { Delegation, RewardingParams, StakeSaturationResponse } from '@nymproject/types';
import {
ContractStateParams,
Delegation,
UnbondedMixnodeResponse,
GatewayOwnershipResponse,
LayerDistribution,
MixnetContractVersion,
MixOwnershipResponse,
PagedAllDelegationsResponse,
PagedDelegatorDelegationsResponse,
PagedGatewayResponse,
PagedMixDelegationsResponse,
PagedMixnodeResponse,
RewardingStatus,
} from './types';
interface SmartContractQuery {
queryContractSmart(address: string, queryMsg: Record<string, unknown>): Promise<JsonObject>;
}
PagedMixNodeBondResponse,
PagedMixNodeDetailsResponse,
PagedUnbondedMixnodesResponse,
LayerDistribution,
} from '@nymproject/types';
import { ContractState, SmartContractQuery } from './types/shared';
export default class NyxdQuerier implements INyxdQuery {
client: SmartContractQuery;
@@ -38,15 +34,44 @@ export default class NyxdQuerier implements INyxdQuery {
});
}
getMixNodesPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedMixnodeResponse> {
getMixNodeBonds(
mixnetContractAddress: string,
limit?: number,
startAfter?: string,
): Promise<PagedMixNodeBondResponse> {
return this.client.queryContractSmart(mixnetContractAddress, {
get_mix_nodes: {
get_mix_node_bonds: {
limit,
start_after: startAfter,
},
});
}
getMixNodesDetailed(
mixnetContractAddress: string,
limit?: number,
startAfter?: string,
): Promise<PagedMixNodeDetailsResponse> {
return this.client.queryContractSmart(mixnetContractAddress, {
get_mix_nodes_detailed: {
limit,
start_after: startAfter,
},
});
}
getStakeSaturation(mixnetContractAddress: string, mixId: number): Promise<StakeSaturationResponse> {
return this.client.queryContractSmart(mixnetContractAddress, {
get_stake_saturation: { mix_id: mixId },
});
}
getMixnodeRewardingDetails(mixnetContractAddress: string, mixId: number): Promise<any> {
return this.client.queryContractSmart(mixnetContractAddress, {
get_mixnode_rewarding_details: { mix_id: mixId },
});
}
getGatewaysPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedGatewayResponse> {
return this.client.queryContractSmart(mixnetContractAddress, {
get_gateways: {
@@ -56,35 +81,51 @@ export default class NyxdQuerier implements INyxdQuery {
});
}
ownsMixNode(mixnetContractAddress: string, address: string): Promise<MixOwnershipResponse> {
getOwnedMixnode(mixnetContractAddress: string, address: string): Promise<MixOwnershipResponse> {
return this.client.queryContractSmart(mixnetContractAddress, {
owns_mixnode: {
get_owned_mixnode: {
address,
},
});
}
getUnbondedMixNodes(
mixnetContractAddress: string,
limit?: number,
startAfter?: string,
): Promise<PagedUnbondedMixnodesResponse> {
return this.client.queryContractSmart(mixnetContractAddress, {
get_unbonded_mix_nodes: { limit, start_after: startAfter },
});
}
getUnbondedMixNodeInformation(mixnetContractAddress: string, mixId: number): Promise<UnbondedMixnodeResponse> {
return this.client.queryContractSmart(mixnetContractAddress, {
get_unbonded_mix_node_information: { mix_id: mixId },
});
}
ownsGateway(mixnetContractAddress: string, address: string): Promise<GatewayOwnershipResponse> {
return this.client.queryContractSmart(mixnetContractAddress, {
owns_gateway: {
get_owned_gateway: {
address,
},
});
}
getStateParams(mixnetContractAddress: string): Promise<ContractStateParams> {
getStateParams(mixnetContractAddress: string): Promise<ContractState> {
return this.client.queryContractSmart(mixnetContractAddress, {
state_params: {},
get_state: {},
});
}
getAllNetworkDelegationsPaged(
getAllDelegationsPaged(
mixnetContractAddress: string,
limit?: number,
startAfter?: [string, string],
): Promise<PagedAllDelegationsResponse> {
return this.client.queryContractSmart(mixnetContractAddress, {
get_all_network_delegations: {
get_all_delegations: {
start_after: startAfter,
limit,
},
@@ -93,13 +134,13 @@ export default class NyxdQuerier implements INyxdQuery {
getMixNodeDelegationsPaged(
mixnetContractAddress: string,
mixIdentity: string,
mix_id: number,
limit?: number,
startAfter?: string,
): Promise<PagedMixDelegationsResponse> {
return this.client.queryContractSmart(mixnetContractAddress, {
get_mixnode_delegations: {
mix_identity: mixIdentity,
mix_id: mix_id,
start_after: startAfter,
limit,
},
@@ -121,10 +162,10 @@ export default class NyxdQuerier implements INyxdQuery {
});
}
getDelegationDetails(mixnetContractAddress: string, mixIdentity: string, delegator: string): Promise<Delegation> {
getDelegationDetails(mixnetContractAddress: string, mix_id: number, delegator: string): Promise<Delegation> {
return this.client.queryContractSmart(mixnetContractAddress, {
get_delegation_details: {
mix_identity: mixIdentity,
mix_id: mix_id,
delegator,
},
});
@@ -132,44 +173,19 @@ export default class NyxdQuerier implements INyxdQuery {
getLayerDistribution(mixnetContractAddress: string): Promise<LayerDistribution> {
return this.client.queryContractSmart(mixnetContractAddress, {
layer_distribution: {},
get_layer_distribution: {},
});
}
getRewardPool(mixnetContractAddress: string): Promise<string> {
getRewardParams(mixnetContractAddress: string): Promise<RewardingParams> {
return this.client.queryContractSmart(mixnetContractAddress, {
get_reward_pool: {},
get_rewarding_params: {},
});
}
getCirculatingSupply(mixnetContractAddress: string): Promise<string> {
return this.client.queryContractSmart(mixnetContractAddress, {
get_circulating_supply: {},
});
}
getIntervalRewardPercent(mixnetContractAddress: string): Promise<number> {
return this.client.queryContractSmart(mixnetContractAddress, {
get_interval_reward_percent: {},
});
}
getSybilResistancePercent(mixnetContractAddress: string): Promise<number> {
return this.client.queryContractSmart(mixnetContractAddress, {
get_sybil_resistance_percent: {},
});
}
getRewardingStatus(
mixnetContractAddress: string,
mixIdentity: string,
rewardingIntervalNonce: number,
): Promise<RewardingStatus> {
return this.client.queryContractSmart(mixnetContractAddress, {
get_rewarding_status: {
mix_identity: mixIdentity,
rewarding_interval_nonce: rewardingIntervalNonce,
},
getSpendableCoins(vestingContractAddress: string, vestingAccountAddress: string): Promise<any> {
return this.client.queryContractSmart(vestingContractAddress, {
vesting_account_address: vestingAccountAddress,
});
}
}
+84 -93
View File
@@ -1,75 +1,56 @@
import { CosmWasmClient, JsonObject } from '@cosmjs/cosmwasm-stargate';
import { Tendermint34Client } from '@cosmjs/tendermint-rpc';
import {
Account,
Block,
Coin,
DeliverTxResponse,
IndexedTx,
SearchTxFilter,
SearchTxQuery,
SequenceResponse,
} from '@cosmjs/stargate';
import { Code, CodeDetails, Contract, ContractCodeHistoryEntry } from '@cosmjs/cosmwasm-stargate/build/cosmwasmclient';
import { CosmWasmClient } from '@cosmjs/cosmwasm-stargate';
// eslint-disable-next-line import/no-cycle
import NyxdQuerier from './nyxd-querier';
import {
ContractStateParams,
Delegation,
GatewayBond,
GatewayOwnershipResponse,
LayerDistribution,
MixnetContractVersion,
MixNodeBond,
MixNodeDetails,
MixOwnershipResponse,
PagedAllDelegationsResponse,
PagedDelegatorDelegationsResponse,
PagedGatewayResponse,
PagedMixDelegationsResponse,
PagedMixnodeResponse,
RewardingStatus,
} from './types';
import NymApiQuerier, { INymApiQuery as INymApiQuery } from './nym-api-querier';
export interface ICosmWasmQuery {
// methods exposed by `CosmWasmClient`
getChainId(): Promise<string>;
getHeight(): Promise<number>;
getAccount(searchAddress: string): Promise<Account | null>;
getSequence(address: string): Promise<SequenceResponse>;
getBlock(height?: number): Promise<Block>;
getBalance(address: string, searchDenom: string): Promise<Coin>;
getTx(id: string): Promise<IndexedTx | null>;
searchTx(query: SearchTxQuery, filter?: SearchTxFilter): Promise<readonly IndexedTx[]>;
disconnect(): void;
broadcastTx(tx: Uint8Array, timeoutMs?: number, pollIntervalMs?: number): Promise<DeliverTxResponse>;
getCodes(): Promise<readonly Code[]>;
getCodeDetails(codeId: number): Promise<CodeDetails>;
getContracts(codeId: number): Promise<readonly string[]>;
getContract(address: string): Promise<Contract>;
getContractCodeHistory(address: string): Promise<readonly ContractCodeHistoryEntry[]>;
queryContractRaw(address: string, key: Uint8Array): Promise<Uint8Array | null>;
queryContractSmart(address: string, queryMsg: Record<string, unknown>): Promise<JsonObject>;
}
PagedMixNodeBondResponse,
PagedMixNodeDetailsResponse,
PagedUnbondedMixnodesResponse,
StakeSaturationResponse,
UnbondedMixnodeResponse,
MixNodeBond,
MixNodeRewarding,
} from '@nymproject/types';
import NymApiQuerier, { INymApiQuery } from './nym-api-querier';
import { ContractState, ICosmWasmQuery } from './types/shared';
import { RewardingParams } from '@nymproject/types';
import { Tendermint34Client } from '@cosmjs/tendermint-rpc';
export interface INyxdQuery {
// nym-specific implemented inside NymQuerier
getContractVersion(mixnetContractAddress: string): Promise<MixnetContractVersion>;
getMixNodesPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedMixnodeResponse>;
getMixNodeBonds(
mixnetContractAddress: string,
limit?: number,
startAfter?: string,
): Promise<PagedMixNodeBondResponse>;
getMixNodesDetailed(
mixnetContractAddress: string,
limit?: number,
startAfter?: string,
): Promise<PagedMixNodeDetailsResponse>;
getGatewaysPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedGatewayResponse>;
ownsMixNode(mixnetContractAddress: string, address: string): Promise<MixOwnershipResponse>;
getOwnedMixnode(mixnetContractAddress: string, address: string): Promise<MixOwnershipResponse>;
ownsGateway(mixnetContractAddress: string, address: string): Promise<GatewayOwnershipResponse>;
getStateParams(mixnetContractAddress: string): Promise<ContractStateParams>;
getAllNetworkDelegationsPaged(
getStateParams(mixnetContractAddress: string): Promise<ContractState>;
getAllDelegationsPaged(
mixnetContractAddress: string,
limit?: number,
startAfter?: [string, string],
): Promise<PagedAllDelegationsResponse>;
getMixNodeDelegationsPaged(
mixnetContractAddress: string,
mixIdentity: string,
mix_id: number,
limit?: number,
startAfter?: string,
): Promise<PagedMixDelegationsResponse>;
@@ -79,21 +60,15 @@ export interface INyxdQuery {
limit?: number,
startAfter?: string,
): Promise<PagedDelegatorDelegationsResponse>;
getDelegationDetails(mixnetContractAddress: string, mixIdentity: string, delegator: string): Promise<Delegation>;
getDelegationDetails(mixnetContractAddress: string, mix_id: number, delegator: string): Promise<Delegation>;
getLayerDistribution(mixnetContractAddress: string): Promise<LayerDistribution>;
getRewardPool(mixnetContractAddress: string): Promise<string>;
getCirculatingSupply(mixnetContractAddress: string): Promise<string>;
getIntervalRewardPercent(mixnetContractAddress: string): Promise<number>;
getSybilResistancePercent(mixnetContractAddress: string): Promise<number>;
getRewardingStatus(
mixnetContractAddress: string,
mixIdentity: string,
rewardingIntervalNonce: number,
): Promise<RewardingStatus>;
getRewardParams(mixnetContractAddress: string): Promise<RewardingParams>;
getStakeSaturation(mixnetContractAddress: string, mixId: number): Promise<StakeSaturationResponse>;
getUnbondedMixNodeInformation(mixnetContractAddress: string, mixId: number): Promise<UnbondedMixnodeResponse>;
getMixnodeRewardingDetails(mixnetContractAddress: string, mixId: number): Promise<MixNodeRewarding>;
}
export interface IQueryClient extends ICosmWasmQuery, INyxdQuery, INymApiQuery { }
export interface IQueryClient extends ICosmWasmQuery, INyxdQuery, INymApiQuery {}
export default class QueryClient extends CosmWasmClient implements IQueryClient {
private nyxdQuerier: NyxdQuerier;
@@ -115,41 +90,73 @@ export default class QueryClient extends CosmWasmClient implements IQueryClient
return this.nyxdQuerier.getContractVersion(mixnetContractAddress);
}
getMixNodesPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedMixnodeResponse> {
return this.nyxdQuerier.getMixNodesPaged(mixnetContractAddress, limit, startAfter);
getMixNodeBonds(
mixnetContractAddress: string,
limit?: number,
startAfter?: string,
): Promise<PagedMixNodeBondResponse> {
return this.nyxdQuerier.getMixNodeBonds(mixnetContractAddress, limit, startAfter);
}
getMixNodesDetailed(
mixnetContractAddress: string,
limit?: number,
startAfter?: string,
): Promise<PagedMixNodeDetailsResponse> {
return this.nyxdQuerier.getMixNodesDetailed(mixnetContractAddress, limit, startAfter);
}
getStakeSaturation(mixnetContractAddress: string, mixId: number): Promise<StakeSaturationResponse> {
return this.nyxdQuerier.getStakeSaturation(mixnetContractAddress, mixId);
}
getMixnodeRewardingDetails(mixnetContractAddress: string, mixId: number): Promise<MixNodeRewarding> {
return this.nyxdQuerier.getMixnodeRewardingDetails(mixnetContractAddress, mixId);
}
getGatewaysPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedGatewayResponse> {
return this.nyxdQuerier.getGatewaysPaged(mixnetContractAddress, limit, startAfter);
}
ownsMixNode(mixnetContractAddress: string, address: string): Promise<MixOwnershipResponse> {
return this.nyxdQuerier.ownsMixNode(mixnetContractAddress, address);
getOwnedMixnode(mixnetContractAddress: string, address: string): Promise<MixOwnershipResponse> {
return this.nyxdQuerier.getOwnedMixnode(mixnetContractAddress, address);
}
ownsGateway(mixnetContractAddress: string, address: string): Promise<GatewayOwnershipResponse> {
return this.nyxdQuerier.ownsGateway(mixnetContractAddress, address);
}
getStateParams(mixnetContractAddress: string): Promise<ContractStateParams> {
getUnbondedMixNodes(
mixnetContractAddress: string,
limit?: number,
startAfter?: string,
): Promise<PagedUnbondedMixnodesResponse> {
return this.nyxdQuerier.getUnbondedMixNodes(mixnetContractAddress, limit, startAfter);
}
getUnbondedMixNodeInformation(mixnetContractAddress: string, mixId: number): Promise<UnbondedMixnodeResponse> {
return this.nyxdQuerier.getUnbondedMixNodeInformation(mixnetContractAddress, mixId);
}
getStateParams(mixnetContractAddress: string): Promise<ContractState> {
return this.nyxdQuerier.getStateParams(mixnetContractAddress);
}
getAllNetworkDelegationsPaged(
getAllDelegationsPaged(
mixnetContractAddress: string,
limit?: number,
startAfter?: [string, string],
): Promise<PagedAllDelegationsResponse> {
return this.nyxdQuerier.getAllNetworkDelegationsPaged(mixnetContractAddress, limit, startAfter);
return this.nyxdQuerier.getAllDelegationsPaged(mixnetContractAddress, limit, startAfter);
}
getMixNodeDelegationsPaged(
mixnetContractAddress: string,
mixIdentity: string,
mix_id: number,
limit?: number,
startAfter?: string,
): Promise<PagedMixDelegationsResponse> {
return this.nyxdQuerier.getMixNodeDelegationsPaged(mixnetContractAddress, mixIdentity, limit, startAfter);
return this.nyxdQuerier.getMixNodeDelegationsPaged(mixnetContractAddress, mix_id, limit, startAfter);
}
getDelegatorDelegationsPaged(
@@ -161,36 +168,16 @@ export default class QueryClient extends CosmWasmClient implements IQueryClient
return this.nyxdQuerier.getDelegatorDelegationsPaged(mixnetContractAddress, delegator, limit, startAfter);
}
getDelegationDetails(mixnetContractAddress: string, mixIdentity: string, delegator: string): Promise<Delegation> {
return this.nyxdQuerier.getDelegationDetails(mixnetContractAddress, mixIdentity, delegator);
getDelegationDetails(mixnetContractAddress: string, mix_id: number, delegator: string): Promise<Delegation> {
return this.nyxdQuerier.getDelegationDetails(mixnetContractAddress, mix_id, delegator);
}
getLayerDistribution(mixnetContractAddress: string): Promise<LayerDistribution> {
return this.nyxdQuerier.getLayerDistribution(mixnetContractAddress);
}
getRewardPool(mixnetContractAddress: string): Promise<string> {
return this.nyxdQuerier.getRewardPool(mixnetContractAddress);
}
getCirculatingSupply(mixnetContractAddress: string): Promise<string> {
return this.nyxdQuerier.getCirculatingSupply(mixnetContractAddress);
}
getIntervalRewardPercent(mixnetContractAddress: string): Promise<number> {
return this.nyxdQuerier.getIntervalRewardPercent(mixnetContractAddress);
}
getSybilResistancePercent(mixnetContractAddress: string): Promise<number> {
return this.nyxdQuerier.getSybilResistancePercent(mixnetContractAddress);
}
getRewardingStatus(
mixnetContractAddress: string,
mixIdentity: string,
rewardingIntervalNonce: number,
): Promise<RewardingStatus> {
return this.nyxdQuerier.getRewardingStatus(mixnetContractAddress, mixIdentity, rewardingIntervalNonce);
getRewardParams(mixnetContractAddress: string): Promise<RewardingParams> {
return this.nyxdQuerier.getRewardParams(mixnetContractAddress);
}
getCachedGateways(): Promise<GatewayBond[]> {
@@ -201,11 +188,15 @@ export default class QueryClient extends CosmWasmClient implements IQueryClient
return this.nymApiQuerier.getCachedMixnodes();
}
getActiveMixnodes(): Promise<MixNodeBond[]> {
getActiveMixnodes(): Promise<MixNodeDetails[]> {
return this.nymApiQuerier.getActiveMixnodes();
}
getRewardedMixnodes(): Promise<MixNodeBond[]> {
return this.nymApiQuerier.getRewardedMixnodes();
}
getSpendableCoins(vestingContractAddress: string, vestingAccountAddress: string): Promise<any> {
return this.nyxdQuerier.getSpendableCoins(vestingContractAddress, vestingAccountAddress);
}
}
+73 -47
View File
@@ -25,15 +25,22 @@ import {
MixnetContractVersion,
MixNode,
MixNodeBond,
MixNodeCostParams,
MixNodeDetails,
MixNodeRewarding,
MixOwnershipResponse,
PagedAllDelegationsResponse,
PagedDelegatorDelegationsResponse,
PagedGatewayResponse,
PagedMixDelegationsResponse,
PagedMixnodeResponse,
RewardingStatus,
} from './types';
PagedMixNodeBondResponse,
PagedMixNodeDetailsResponse,
PagedUnbondedMixnodesResponse,
RewardingParams,
UnbondedMixnodeResponse,
} from '@nymproject/types';
import NymApiQuerier from './nym-api-querier';
import { ContractState } from './types/shared';
// methods exposed by `SigningCosmWasmClient`
export interface ICosmWasmSigning {
@@ -143,6 +150,7 @@ export interface ISigningClient extends IQueryClient, ICosmWasmSigning, INymSign
bondMixNode(
mixnetContractAddress: string,
mixNode: MixNode,
costParams: MixNodeCostParams,
ownerSignature: string,
pledge: Coin,
fee?: StdFee | 'auto' | number,
@@ -164,7 +172,7 @@ export interface ISigningClient extends IQueryClient, ICosmWasmSigning, INymSign
delegateToMixNode(
mixnetContractAddress: string,
mixIdentity: string,
mixId: number,
amount: Coin,
fee?: StdFee | 'auto' | number,
memo?: string,
@@ -172,14 +180,14 @@ export interface ISigningClient extends IQueryClient, ICosmWasmSigning, INymSign
undelegateFromMixNode(
mixnetContractAddress: string,
mixIdentity: string,
mixId: number,
fee?: StdFee | 'auto' | number,
memo?: string,
): Promise<ExecuteResult>;
updateMixnodeConfig(
mixnetContractAddress: string,
mixIdentity: string,
mixId: number,
profitMarginPercent: number,
fee: StdFee | 'auto' | number,
): Promise<ExecuteResult>;
@@ -235,44 +243,76 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
// query related:
getContractVersion(mixnetContractAddress: string): Promise<MixnetContractVersion> {
return this.nyxdQuerier.getContractVersion(mixnetContractAddress);
return this.getContractVersion(mixnetContractAddress);
}
getMixNodesPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedMixnodeResponse> {
return this.nyxdQuerier.getMixNodesPaged(mixnetContractAddress, limit, startAfter);
getMixNodeBonds(
mixnetContractAddress: string,
limit?: number,
startAfter?: string,
): Promise<PagedMixNodeBondResponse> {
return this.nyxdQuerier.getMixNodeBonds(mixnetContractAddress, limit, startAfter);
}
getMixNodesDetailed(
mixnetContractAddress: string,
limit?: number,
startAfter?: string,
): Promise<PagedMixNodeDetailsResponse> {
return this.nyxdQuerier.getMixNodesDetailed(mixnetContractAddress, limit, startAfter);
}
getStakeSaturation(mixnetContractAddress: string, mixId: number) {
return this.nyxdQuerier.getStakeSaturation(mixnetContractAddress, mixId);
}
getUnbondedMixNodeInformation(mixnetContractAddress: string, mixId: number): Promise<UnbondedMixnodeResponse> {
return this.nyxdQuerier.getUnbondedMixNodeInformation(mixnetContractAddress, mixId);
}
getMixnodeRewardingDetails(mixnetContractAddress: string, mixId: number): Promise<MixNodeRewarding> {
return this.nyxdQuerier.getMixnodeRewardingDetails(mixnetContractAddress, mixId);
}
getGatewaysPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedGatewayResponse> {
return this.nyxdQuerier.getGatewaysPaged(mixnetContractAddress, limit, startAfter);
}
ownsMixNode(mixnetContractAddress: string, address: string): Promise<MixOwnershipResponse> {
return this.nyxdQuerier.ownsMixNode(mixnetContractAddress, address);
getOwnedMixnode(mixnetContractAddress: string, address: string): Promise<MixOwnershipResponse> {
return this.nyxdQuerier.getOwnedMixnode(mixnetContractAddress, address);
}
ownsGateway(mixnetContractAddress: string, address: string): Promise<GatewayOwnershipResponse> {
return this.nyxdQuerier.ownsGateway(mixnetContractAddress, address);
}
getStateParams(mixnetContractAddress: string): Promise<ContractStateParams> {
getStateParams(mixnetContractAddress: string): Promise<ContractState> {
return this.nyxdQuerier.getStateParams(mixnetContractAddress);
}
getAllNetworkDelegationsPaged(
getAllDelegationsPaged(
mixnetContractAddress: string,
limit?: number,
startAfter?: [string, string],
): Promise<PagedAllDelegationsResponse> {
return this.nyxdQuerier.getAllNetworkDelegationsPaged(mixnetContractAddress, limit, startAfter);
return this.getAllDelegationsPaged(mixnetContractAddress, limit, startAfter);
}
getUnbondedMixNodes(
mixnetContractAddress: string,
limit?: number,
startAfter?: string,
): Promise<PagedUnbondedMixnodesResponse> {
return this.nyxdQuerier.getUnbondedMixNodes(mixnetContractAddress, limit, startAfter);
}
getMixNodeDelegationsPaged(
mixnetContractAddress: string,
mixIdentity: string,
mix_id: number,
limit?: number,
startAfter?: string,
): Promise<PagedMixDelegationsResponse> {
return this.nyxdQuerier.getMixNodeDelegationsPaged(mixnetContractAddress, mixIdentity, limit, startAfter);
return this.nyxdQuerier.getMixNodeDelegationsPaged(mixnetContractAddress, mix_id, limit, startAfter);
}
getDelegatorDelegationsPaged(
@@ -284,36 +324,16 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
return this.nyxdQuerier.getDelegatorDelegationsPaged(mixnetContractAddress, delegator, limit, startAfter);
}
getDelegationDetails(mixnetContractAddress: string, mixIdentity: string, delegator: string): Promise<Delegation> {
return this.nyxdQuerier.getDelegationDetails(mixnetContractAddress, mixIdentity, delegator);
getDelegationDetails(mixnetContractAddress: string, mix_id: number, delegator: string): Promise<Delegation> {
return this.nyxdQuerier.getDelegationDetails(mixnetContractAddress, mix_id, delegator);
}
getLayerDistribution(mixnetContractAddress: string): Promise<LayerDistribution> {
return this.nyxdQuerier.getLayerDistribution(mixnetContractAddress);
}
getRewardPool(mixnetContractAddress: string): Promise<string> {
return this.nyxdQuerier.getRewardPool(mixnetContractAddress);
}
getCirculatingSupply(mixnetContractAddress: string): Promise<string> {
return this.nyxdQuerier.getCirculatingSupply(mixnetContractAddress);
}
getIntervalRewardPercent(mixnetContractAddress: string): Promise<number> {
return this.nyxdQuerier.getIntervalRewardPercent(mixnetContractAddress);
}
getSybilResistancePercent(mixnetContractAddress: string): Promise<number> {
return this.nyxdQuerier.getSybilResistancePercent(mixnetContractAddress);
}
getRewardingStatus(
mixnetContractAddress: string,
mixIdentity: string,
rewardingIntervalNonce: number,
): Promise<RewardingStatus> {
return this.nyxdQuerier.getRewardingStatus(mixnetContractAddress, mixIdentity, rewardingIntervalNonce);
getRewardParams(mixnetContractAddress: string): Promise<RewardingParams> {
return this.nyxdQuerier.getRewardParams(mixnetContractAddress);
}
getCachedGateways(): Promise<GatewayBond[]> {
@@ -324,7 +344,7 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
return this.nymApiQuerier.getCachedMixnodes();
}
getActiveMixnodes(): Promise<MixNodeBond[]> {
getActiveMixnodes(): Promise<MixNodeDetails[]> {
return this.nymApiQuerier.getActiveMixnodes();
}
@@ -332,11 +352,16 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
return this.nymApiQuerier.getRewardedMixnodes();
}
getSpendableCoins(vestingContractAddress: string, vestingAccountAddress: string): Promise<any> {
return this.nyxdQuerier.getSpendableCoins(vestingContractAddress, vestingAccountAddress);
}
// signing related:
bondMixNode(
mixnetContractAddress: string,
mixNode: MixNode,
costParams: MixNodeCostParams,
ownerSignature: string,
pledge: Coin,
fee: StdFee | 'auto' | number = 'auto',
@@ -348,6 +373,7 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
{
bond_mixnode: {
mix_node: mixNode,
cost_params: costParams,
owner_signature: ownerSignature,
},
},
@@ -414,7 +440,7 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
delegateToMixNode(
mixnetContractAddress: string,
mixIdentity: string,
mixId: number,
amount: Coin,
fee: StdFee | 'auto' | number = 'auto',
memo = 'Default MixNode Delegation from Typescript',
@@ -424,7 +450,7 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
mixnetContractAddress,
{
delegate_to_mixnode: {
mix_identity: mixIdentity,
mix_id: mixId,
},
},
fee,
@@ -435,7 +461,7 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
undelegateFromMixNode(
mixnetContractAddress: string,
mixIdentity: string,
mixId: number,
fee: StdFee | 'auto' | number = 'auto',
memo = 'Default MixNode Undelegation from Typescript',
): Promise<ExecuteResult> {
@@ -444,7 +470,7 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
mixnetContractAddress,
{
undelegate_from_mixnode: {
mix_identity: mixIdentity,
mix_id: mixId,
},
},
fee,
@@ -454,14 +480,14 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
updateMixnodeConfig(
mixnetContractAddress: string,
mixIdentity: string,
mixId: number,
profitMarginPercent: number,
fee: StdFee | 'auto' | number,
): Promise<ExecuteResult> {
return this.execute(
this.clientAddress,
mixnetContractAddress,
{ update_mixnode_config: { profit_margin_percent: profitMarginPercent, mix_identity: mixIdentity } },
{ update_mixnode_config: { profit_margin_percent: profitMarginPercent, mix_id: mixId } },
fee,
);
}
@@ -0,0 +1,178 @@
import expect from 'expect';
export const amountDemon = {
amount: expect.any(String),
denom: expect.any(String)
}
export const delegation = {
owner: expect.any(String),
mix_id: expect.any(Number),
cumulative_reward_ratio: expect.any(String),
amount: amountDemon,
height: expect.any(Number || BigInt),
proxy: expect.any(String || null)
}
export const detailedDelegation = {
delegation: delegation,
mixnode_still_bonded: expect.any(Boolean)
}
export const gateway = {
pledge_amount: amountDemon,
owner: expect.any(String),
block_height: expect.any(Number || BigInt),
gateway: {
host: expect.any(String),
mix_port: expect.any(Number),
clients_port: expect.any(Number),
location: expect.any(String),
sphinx_key: expect.any(String),
identity_key: expect.any(String),
version: expect.any(String),
},
proxy: expect.any(String || null)
}
export const pagedGateway = {
nodes: gateway,
per_page: expect.any(Number),
start_next_after: expect.any(Number)
}
export const ownGateway = {
address: expect.any(String),
gateway: gateway
}
export const rewardingdetails = {
cost_params: {
profit_margin_percent: expect.any(String),
interval_operating_cost: {
denom: expect.any(String),
amount: expect.any(String)
}
},
operator: expect.any(String),
delegates: expect.any(String),
total_unit_reward: expect.any(String),
unit_delegation: expect.any(String),
last_rewarded_epoch: expect.any(Number),
unique_delegations: expect.any(Number)
}
export const mix_node = {
host: expect.any(String),
mix_port: expect.any(Number),
verloc_port: expect.any(Number),
http_api_port: expect.any(Number),
sphinx_key: expect.any(String),
identity_key: expect.any(String),
version: expect.any(String)
}
export const mixnodebond = {
mix_id: expect.any(Number),
owner: expect.any(String),
original_pledge: {
denom: expect.any(String),
amount: expect.any(String)
},
layer: expect.any(String),
mix_node: mix_node,
proxy: expect.any(String) || null,
bonding_height: expect.any(Number || BigInt),
is_unbonding: expect.any(Boolean)
}
export const mixnode = {
bond_information: mixnodebond,
rewarding_details: rewardingdetails
}
export const ownedNode = {
address: expect.any(String),
mixnode_details: {
bond_information: mixnodebond,
rewarding_details: rewardingdetails
}
}
export const saturation = {
mix_id: expect.any(Number),
current_saturation: expect.any(String),
uncapped_saturation: expect.any(String)
}
export const contractVersion = {
build_timestamp: expect.any(String),
build_version: expect.any(String),
commit_sha: expect.any(String),
commit_timestamp: expect.any(String),
commit_branch: expect.any(String),
rustc_version: expect.any(String)
};
export const stateParams = {
minimum_gateway_pledge: amountDemon,
minimum_mixnode_pledge: expect.any(String),
mixnode_rewarded_set_size: expect.any(Number),
mixnode_active_set_size: expect.any(Number)
}
export const contract = {
owner: expect.any(Number),
rewarding_validator_address: expect.any(Number),
vesting_contract_address: expect.any(Number),
rewarding_denom: expect.any(String),
params: stateParams
}
export const rewardingnode = {
mix_id: expect.any(Number),
rewarding_details: rewardingdetails
}
export const unbondednode = {
mix_id: expect.any(Number),
unbonded_info: {
identity_key: expect.any(String),
owner: expect.any(String),
proxy: expect.any(String) || null,
unbonding_height: expect.any(Number)
}
}
export const allunbondednodes = [
expect.any(Number), {
identity_key: expect.any(String),
owner: expect.any(String),
proxy: expect.any(String) || null,
unbonding_height: expect.any(Number)
}
]
export const layerDistribution = {
layer1: expect.any(Number),
layer2: expect.any(Number),
layer3: expect.any(Number)
}
export const intervalRewardParams = {
reward_pool: expect.any(Number),
staking_supply: expect.any(Number),
staking_supply_scale_factor: expect.any(Number),
epoch_reward_budget: expect.any(Number),
stake_saturation_point: expect.any(Number),
sybil_resistance: expect.any(Number),
active_set_work_factor: expect.any(Number),
interval_pool_emission: expect.any(Number)
}
export const rewardingParams = {
interval: intervalRewardParams,
rewarded_set_size: expect.any(Number),
active_set_size: expect.any(Number)
}
@@ -0,0 +1,19 @@
import { Mock, Times } from 'moq.ts';
import expect from 'expect';
import { INyxdQuery } from '../../query-client';
export class TestHelper {
buildMethod = async <T>(methodName: string, args: any[], expectedResult: any): Promise<T> => {
const client = new Mock<INyxdQuery>()
.setup((nym) => nym[methodName](...args))
.returns(Promise.resolve(expectedResult));
const obj = client.object();
const actualDetails = await obj[methodName](...args);
client.verify((nym) => nym[methodName](...args), Times.Exactly(1));
expect(Object.keys([actualDetails])).toEqual(Object.keys(expectedResult));
expect(actualDetails).toBeDefined();
return actualDetails;
};
}
@@ -0,0 +1,40 @@
import expect from 'expect';
import { Delegation } from '@nymproject/types';
import { PagedAllDelegationsResponse, PagedDelegatorDelegationsResponse } from '../../types/shared-types';
import { TestHelper } from './client';
import { mixnet, mixnodeowneraddress, mix_id } from './testData';
describe('Delegation mock tests', () => {
const testHelper = new TestHelper();
it('get Delegation Details', () => {
const execute = testHelper.buildMethod('getDelegationDetails', [mixnet, mix_id, mixnodeowneraddress], <Delegation>{
owner: mixnodeowneraddress,
mix_id: 0,
cumulative_reward_ratio: '',
amount: {
denom: 'nym',
amount: '10',
},
height: 1314134144132n,
proxy: 'null',
});
expect(execute).toBeTruthy();
});
it('get All Delegations Paged', () => {
const execute = testHelper.buildMethod('getAllDelegationsPaged', [mixnet], <PagedAllDelegationsResponse>{
delegations: [],
});
expect(execute).toBeTruthy();
});
it('get Delegator Delegations Paged', () => {
const execute = testHelper.buildMethod('getDelegatorDelegationsPaged', [mixnet, mixnodeowneraddress], <
PagedDelegatorDelegationsResponse
>{
delegations: [],
});
expect(execute).toBeTruthy();
});
});
@@ -0,0 +1,24 @@
import expect from 'expect';
import { GatewayOwnershipResponse, PagedGatewayResponse } from '../../types/shared-types';
import { TestHelper } from './client';
import { gatewayowneraddress, mixnet } from './testData';
describe('Gateway mock tests', () => {
const testHelper = new TestHelper();
it('get Gateways Paged', () => {
const execute = testHelper.buildMethod('getGatewaysPaged', [mixnet], <PagedGatewayResponse>{
nodes: [],
per_page: 25,
});
expect(execute).toBeTruthy();
});
it('owns Gateway', () => {
const execute = testHelper.buildMethod('ownsGateway', [mixnet, gatewayowneraddress], <GatewayOwnershipResponse>{
address: gatewayowneraddress,
gateway: {},
});
expect(execute).toBeTruthy();
});
});
@@ -0,0 +1,65 @@
import expect from 'expect';
import { LayerDistribution, MixnetContractVersion, StakeSaturationResponse } from '@nymproject/types';
import { TestHelper } from './client';
import { mixnet, mix_id } from './testData';
import { RewardingParams } from '@nymproject/types';
import { ContractState } from '../../types/shared';
describe('Mixnet mock tests', () => {
const testHelper = new TestHelper();
it('get Layer Distribution', () => {
const execute = testHelper.buildMethod('getLayerDistribution', [mixnet], <LayerDistribution>{
layer1: 2,
layer2: 2,
layer3: 5,
});
expect(execute).toBeTruthy();
});
it('get Reward Params', () => {
const execute = testHelper.buildMethod('getRewardParams', [mixnet], <RewardingParams>{
interval: {},
rewarded_set_size: 0,
active_set_size: 0,
});
expect(execute).toBeTruthy();
});
it('get Stake Saturation', () => {
const execute = testHelper.buildMethod('getStakeSaturation', [mixnet, mix_id], <StakeSaturationResponse>{
mix_id: 0,
current_saturation: '',
uncapped_saturation: '',
});
expect(execute).toBeTruthy();
});
it('get State Params', () => {
const execute = testHelper.buildMethod('getStateParams', [mixnet], <ContractState>{
owner: '',
rewarding_validator_address: '',
vesting_contract_address: '',
rewarding_denom: 'unym',
params: {
minimum_mixnode_pledge: '',
minimum_gateway_pledge: '',
mixnode_rewarded_set_size: 240,
mixnode_active_set_size: 240,
},
});
expect(execute).toBeTruthy();
});
it('get Contract Version', () => {
const execute = testHelper.buildMethod('getContractVersion', [mixnet], <MixnetContractVersion>{
build_timestamp: 'test',
commit_branch: 'test',
build_version: 'test',
rustc_version: 'test',
commit_sha: 'test',
commit_timestamp: 'test',
});
expect(execute).toBeTruthy();
});
});
@@ -0,0 +1,71 @@
import expect from 'expect';
import {
MixNodeRewarding,
MixOwnershipResponse,
PagedMixDelegationsResponse,
PagedMixNodeBondResponse,
PagedMixNodeDetailsResponse,
UnbondedMixnodeResponse,
} from '@nymproject/types';
import { TestHelper } from './client';
import { mixnet, mixnodeowneraddress, mix_id } from './testData';
describe('Mixnode mock tests', () => {
const testHelper = new TestHelper();
it('get Mixnode Bonds', () => {
const execute = testHelper.buildMethod('getMixNodeBonds', [mixnet], <PagedMixNodeBondResponse>{
nodes: [],
per_page: 25,
});
expect(execute).toBeTruthy();
});
it('get Mixnode Delegations Paged', () => {
const execute = testHelper.buildMethod('getMixNodeDelegationsPaged', [mixnet, mix_id], <
PagedMixDelegationsResponse
>{
delegations: [],
per_page: 25,
});
expect(execute).toBeTruthy();
});
it('get Mixnodes Detailed', () => {
const execute = testHelper.buildMethod('getMixNodesDetailed', [mixnet], <PagedMixNodeDetailsResponse>{
nodes: [],
per_page: 25,
});
expect(execute).toBeTruthy();
});
it('get Mixnode Rewarding Details', () => {
const execute = testHelper.buildMethod('getMixnodeRewardingDetails', [mixnet, mix_id], <MixNodeRewarding>{
cost_params: {},
operator: '',
delegates: '',
total_unit_reward: '',
unit_delegation: '',
last_rewarded_epoch: 1,
unique_delegations: 1,
});
expect(execute).toBeTruthy();
});
it('get Owned Mixnode', () => {
const execute = testHelper.buildMethod('getOwnedMixnode', [mixnet, mixnodeowneraddress], <MixOwnershipResponse>{
address: '',
mixnode: {},
});
expect(execute).toBeTruthy();
});
it('get Unbonded Mixnode Information', () => {
const execute = testHelper.buildMethod(
'getUnbondedMixNodeInformation',
[mixnet, mix_id],
<UnbondedMixnodeResponse>{},
);
expect(execute).toBeTruthy();
});
});
@@ -0,0 +1,8 @@
export const mixnet = 'n14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sjyvg3g';
export const gatewayowneraddress = 'n16evnn8glr0sham3matj8rg2s24m6x56ayk87ts';
// export const mixId = 436207616;
export const mix_id = 26;
export const nodeIdentityKey = 'ATmVJknZarDF6Yj53M7h8NS9LLCUvWuToXpk3pDvYUH1';
export const mixnodeowneraddress = 'n1fzv4jc7fanl9s0qj02ge2ezk3kts545kjtek47';
export const delegatorAddress = 'n1lemst75va9700tsrxq58adzujrh6h9s5x60h9h';
export const rewardingIntervalNonce = 1;
@@ -0,0 +1,180 @@
import expect from 'expect';
import ValidatorClient from '../../index';
import {
allunbondednodes,
contract,
delegation,
gateway,
layerDistribution,
mixnode,
mixnodebond,
ownedNode,
ownGateway,
rewardingnode,
saturation,
unbondednode,
} from '../expectedResponses';
import { delegatorAddress, gatewayowneraddress } from '../mock/testData';
const dotenv = require('dotenv');
dotenv.config();
describe('Mixnet queries', () => {
let client: ValidatorClient;
beforeEach(async () => {
client = await ValidatorClient.connectForQuery(
process.env.rpcAddress || '',
process.env.validatorAddress || '',
process.env.prefix || '',
process.env.mixnetContractAddress || '',
process.env.vestingContractAddress || '',
process.env.denom || '',
);
});
//
// CONTRACT
//
it('can query for an account balance', async () => {
const balance = await client.getBalance('n1ptg680vnmef2cd8l0s9uyc4f0hgf3x8sed6w77');
expect(Number.parseFloat(balance.amount)).toBeGreaterThan(0);
});
it('can query for stake saturation', async () => {
const stakeSaturation = await client.getStakeSaturation(7);
expect(Object.keys(stakeSaturation)).toEqual(Object.keys(saturation));
expect(stakeSaturation).toBeTruthy();
expect(stakeSaturation?.current_saturation).toBeTruthy();
});
it('can query for contract version', async () => {
const contractV = await client.getMixnetContractVersion();
expect(contractV).toBeTruthy();
});
it('can query for mixnet contract settings', async () => {
const settings = await client.getMixnetContractSettings();
expect(Object.keys(settings)).toEqual(Object.keys(contract));
expect(settings).toBeTruthy();
});
it('can query for reward pool', async () => {
const rewardPool = await client.getRewardParams();
// TODO add velidation here
expect(rewardPool).toBeTruthy();
});
it('can query for layer distribution', async () => {
const layer = await client.getLayerDistribution();
expect(Object.keys(layer)).toEqual(Object.keys(layerDistribution));
expect(layer).toBeTruthy();
});
//
// MIXNODES
//
it('can query for unbonded mixnodes', async () => {
const unbondedNodes = await client.getUnbondedMixNodes();
for (let i = 0; i < unbondedNodes.length; i++) {
expect(Object.keys(unbondedNodes[0])).toEqual(Object.keys(allunbondednodes));
expect(unbondedNodes).toBeTruthy();
}
});
it('can query for unbonded mixnode information', async () => {
const unbondedMixnodeInfo = await client.getUnbondedMixNodeInformation(1);
expect(Object.keys(unbondedMixnodeInfo)).toEqual(Object.keys(unbondednode));
expect(unbondedMixnodeInfo).toBeTruthy();
});
it('can query for mixnode rewarding details', async () => {
const rewardingDetails = await client.getMixnodeRewardingDetails(1);
expect(Object.keys(rewardingDetails)).toEqual(Object.keys(rewardingnode));
expect(rewardingDetails).toBeTruthy();
});
it('can query for owned mixnode', async () => {
const ownedMixnode = await client.getOwnedMixnode('n1ptg680vnmef2cd8l0s9uyc4f0hgf3x8sed6w77');
expect(Object.keys(ownedMixnode)).toEqual(Object.keys(ownedNode));
expect(ownedMixnode).toBeTruthy();
});
it('can query for all mixnode bonds', async () => {
const mixnodeBonds = await client.getMixNodeBonds();
expect(Object.keys(mixnodeBonds[0])).toEqual(Object.keys(mixnodebond));
expect(mixnodeBonds).toBeTruthy();
expect(Array.isArray(mixnodeBonds)).toBeTruthy();
});
it('can query for all mixnode details', async () => {
const mixnodeDetails = await client.getMixNodesDetailed();
expect(Object.keys(mixnodeDetails[0])).toEqual(Object.keys(mixnode));
expect(mixnodeDetails).toBeTruthy();
expect(Array.isArray(mixnodeDetails)).toBeTruthy();
});
it('can query for all active mixnodes', async () => {
const activeNodes = await client.getActiveMixnodes();
expect(Object.keys(activeNodes[0])).toEqual(Object.keys(mixnode));
expect(activeNodes).toBeTruthy();
expect(Array.isArray(activeNodes)).toBeTruthy();
});
it('can query for rewarded mixnodes', async () => {
const rewardNodes = await client.getRewardedMixnodes();
expect(Object.keys(rewardNodes[0])).toEqual(Object.keys(mixnode));
expect(rewardNodes).toBeTruthy();
});
//
// DELEGATIONS
//
it('can query for account delegations', async () => {
const delegations = await client.getAllNyxdDelegatorDelegations('n1fzv4jc7fanl9s0qj02ge2ezk3kts545kjtek47');
expect(Object.keys(delegations[0])).toEqual(Object.keys(delegation));
expect(delegations).toBeTruthy();
expect(Array.isArray(delegations)).toBeTruthy();
});
it('can query for all delegations', async () => {
const allDelegations = await client.getAllNyxdDelegations();
expect(Object.keys(allDelegations[0])).toEqual(Object.keys(delegation));
expect(allDelegations).toBeTruthy();
expect(Array.isArray(allDelegations)).toBeTruthy();
});
it('can query for all delegations on a node', async () => {
const mixnodeDelegations = await client.getAllNyxdSingleMixnodeDelegations(1);
expect(Object.keys(mixnodeDelegations[0])).toEqual(Object.keys(delegation));
expect(mixnodeDelegations).toBeTruthy();
});
it('can query for detailed delegations', async () => {
const detailedDelegation = await client.getDelegationDetails(7, delegatorAddress);
expect(Object.keys(detailedDelegation)).toEqual(Object.keys(detailedDelegation));
expect(detailedDelegation).toBeTruthy();
});
//
// GATEWAYS
//
it('can query for all gateways', async () => {
const gateways = await client.getAllNyxdGateways();
expect(Object.keys(gateways[0])).toEqual(Object.keys(gateway));
expect(gateways).toBeTruthy();
expect(Array.isArray(gateways)).toBeTruthy();
}).timeout(10000);
it('can query for owned gateway', async () => {
const gateway = await client.ownsGateway(gatewayowneraddress);
expect(Object.keys(gateway)).toEqual(Object.keys(ownGateway));
expect(gateway).toBeTruthy();
}).timeout(10000);
});
@@ -0,0 +1,28 @@
import expect from 'expect';
import ValidatorClient from '../../index';
const dotenv = require('dotenv');
dotenv.config();
describe('Vesting queries', () => {
let client: ValidatorClient;
beforeEach(async () => {
client = await ValidatorClient.connectForQuery(
process.env.rpcAddress || '',
process.env.validatorAddress || '',
process.env.prefix || '',
process.env.mixnetContractAddress || '',
process.env.vestingContractAddress || '',
process.env.denom || '',
);
});
it('can query for contract version', async () => {
const contract = await client.getVestingContractVersion();
expect(contract).toBeTruthy();
});
it('can get the balance on the account', () => {});
});
@@ -0,0 +1,107 @@
import expect from 'expect';
import ValidatorClient from '../../';
const dotenv = require('dotenv');
dotenv.config();
// TODO: implement for QA with .env for mnemonics
describe('Mixnet actions', () => {
let client: ValidatorClient;
beforeEach(async () => {
client = await ValidatorClient.connect(
process.env.mnemonic || '',
process.env.rpcAddress || '',
process.env.validatorAddress || '',
process.env.prefix || '',
process.env.mixnetContractAddress || '',
process.env.vestingContractAddress || '',
process.env.denom || '',
);
});
it('can send tokens', async () => {
const res = await client.send(client.address, [{ amount: '10000000', denom: 'unym' }]);
expect(res.transactionHash).toBeDefined();
}).timeout(10000);
it.skip('can delegate tokens', async () => {
const [_, secondMixnode] = await client.getActiveMixnodes();
if (secondMixnode) {
const res = await client.delegateToMixNode(
secondMixnode.bond_information.mix_id,
{
amount: '15000000',
denom: 'unym',
},
{ gas: '1000000', amount: [{ amount: '100000', denom: 'unym' }] },
);
expect(res.transactionHash).toBeDefined();
}
}).timeout(10000);
// Need to provide a mix id that can be undelegated from
it.skip('can undelegate from a mixnode', async () => {
const mixId = 8;
const res = await client.undelegateFromMixNode(mixId);
expect(res.transactionHash).toBeDefined();
});
it.skip('Can unbond a mixnode', async () => {
const res = await client.unbondMixNode();
expect(res.transactionHash).toBeDefined();
}).timeout(10000);
it.skip('Can bond a mixnode', async () => {
const res = await client.bondMixNode(
{
identity_key: '3P6pAcF2p3pYMqWdpHqhbavu3ifyaBs5Qw5UmmCGwimx',
sphinx_key: 'GQMQKwUThaggatA6oZteSWTsCQoUfmLtamJ7o9YkP9aE',
host: '109.74.195.67',
mix_port: 1789,
verloc_port: 1790,
http_api_port: 8000,
version: '1.1.4',
},
'3rXWCQBUj5JQB3UBUkZcXhCk9Zh3cjduMF8aFHPTG7KTkkhZzDJTNmE2p2Ph1g6iQW5vRGTpQzjXF33WDwvhzHk6',
{ profit_margin_percent: '0.1', interval_operating_cost: { amount: '40', denom: 'nym' } },
{ amount: '100_000_000', denom: 'unym' },
{ gas: '1000000', amount: [{ amount: '100000', denom: 'unym' }] },
);
expect(res.transactionHash).toBeDefined();
}).timeout(10000);
it.skip('can unbond a gateway', async () => {
const res = await client.unbondGateway();
expect(res.transactionHash).toBeDefined();
});
it.skip('can bond a gateway', async () => {
const res = await client.bondGateway(
{
identity_key: '36vfvEyBzo5cWEFbnP7fqgY39kFw9PQhvwzbispeNaxL',
sphinx_key: 'G65Fwc2JNAotuHQFqmDKhQNQL5rn3r9pupUdmxMygNUZ',
host: '151.236.220.82',
version: '1.1.4',
mix_port: 1789,
clients_port: 9000,
location: 'Cuba',
},
'3ipSJksWHehZm1YfuH5Ahtg7b22NnrP9hEs6iEDXfUS5uiUhpWmCjGR3b3NDHuxeGjpZYJNYJ52D8WCPK5ZR7Szj',
{ amount: '100_000_000', denom: 'unym' },
);
expect(res.transactionHash).toBeDefined();
});
it.skip('can update contract state params', async () => {
const res = await client.updateContractStateParams({
minimum_mixnode_pledge: '',
minimum_gateway_pledge: '',
mixnode_rewarded_set_size: 2,
mixnode_active_set_size: 2,
});
expect(res.transactionHash).toBeDefined();
});
});
-15
View File
@@ -1,15 +0,0 @@
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"esModuleInterop": true,
"strict": true,
"declaration": true,
"outDir": "./dist"
},
"exclude": [
"tests",
"dist",
"node_modules"
]
}
-157
View File
@@ -1,157 +0,0 @@
import { Coin } from '@cosmjs/stargate';
// TODO: ideally we'd have re-exported those using that fancy crate that builds ts types from rust
export type MixnetContractVersion = {
build_timestamp: string;
build_version: string;
commit_sha: string;
commit_timestamp: string;
commit_branch: string;
rustc_version: string;
};
export type PagedMixnodeResponse = {
nodes: MixNodeBond[];
per_page: number;
start_next_after?: string;
};
export type PagedGatewayResponse = {
nodes: GatewayBond[];
per_page: number;
start_next_after?: string;
};
export type MixOwnershipResponse = {
address: string;
mixnode?: MixNodeBond;
};
export type GatewayOwnershipResponse = {
address: string;
gateway?: GatewayBond;
};
export type ContractStateParams = {
// ideally I'd want to define those as `number` rather than `string`, but
// rust-side they are defined as Uint128 and that don't have
// native javascript representations and therefore are interpreted as strings after deserialization
minimum_mixnode_pledge: string;
minimum_gateway_pledge: string;
mixnode_rewarded_set_size: number;
mixnode_active_set_size: number;
};
export type LayerDistribution = {
gateways: number;
layer1: number;
layer2: number;
layer3: number;
};
export type Delegation = {
owner: string;
node_identity: string;
amount: Coin;
block_height: number;
proxy?: string;
};
export type PagedMixDelegationsResponse = {
delegations: Delegation[];
start_next_after?: string;
};
export type PagedDelegatorDelegationsResponse = {
delegations: Delegation[];
start_next_after?: string;
};
export type PagedAllDelegationsResponse = {
delegations: Delegation[];
start_next_after?: [string, string];
};
export type RewardingResult = {
operator_reward: string;
total_delegator_reward: string;
};
export type NodeRewardParams = {
period_reward_pool: string;
k: string;
reward_blockstamp: number;
circulating_supply: string;
uptime: string;
sybil_resistance_percent: number;
};
export type DelegatorRewardParams = {
node_reward_params: NodeRewardParams;
sigma: number;
profit_margin: number;
node_profit: number;
};
export type PendingDelegatorRewarding = {
running_results: RewardingResult;
next_start: string;
rewarding_params: DelegatorRewardParams;
};
export type RewardingStatus = { Complete: RewardingResult } | { PendingNextDelegatorPage: PendingDelegatorRewarding };
export type MixnodeRewardingStatusResponse = {
status?: RewardingStatus;
};
export enum Layer {
Gateway,
One,
Two,
Three,
}
export type MixNodeBond = {
owner: string;
mix_node: MixNode;
layer: Layer;
bond_amount: Coin;
total_delegation: Coin;
};
export type MixNode = {
host: string;
mix_port: number;
verloc_port: number;
http_api_port: number;
sphinx_key: string;
identity_key: string;
version: string;
profit_margin_percent: number;
};
export type GatewayBond = {
owner: string;
gateway: Gateway;
bond_amount: Coin;
total_delegation: Coin;
};
export type Gateway = {
host: string;
mix_port: number;
clients_port: number;
location: string;
sphinx_key: string;
identity_key: string;
version: string;
};
export type SendRequest = {
senderAddress: string;
recipientAddress: string;
transferAmount: readonly Coin[];
};
+11
View File
@@ -0,0 +1,11 @@
declare namespace NodeJS {
interface ProcessEnv {
rpcAddress: string;
validatorAddress: string;
prefix: string;
mixnetContractAddress: string;
vestingContractAddress: string;
denom: string;
mnemonic: string;
}
}
+259
View File
@@ -0,0 +1,259 @@
import { JsonObject } from '@cosmjs/cosmwasm-stargate';
import { Code, CodeDetails, Contract, ContractCodeHistoryEntry } from '@cosmjs/cosmwasm-stargate/build/cosmwasmclient';
import {
Account,
Block,
Coin,
DeliverTxResponse,
IndexedTx,
SearchTxFilter,
SearchTxQuery,
SequenceResponse,
} from '@cosmjs/stargate';
import {
MixNodeRewarding,
PagedMixNodeBondResponse,
PagedMixNodeDetailsResponse,
StakeSaturationResponse,
UnbondedMixnodeResponse,
} from '@nymproject/types';
export type MixnetContractVersion = {
build_timestamp: string;
build_version: string;
commit_sha: string;
commit_timestamp: string;
commit_branch: string;
rustc_version: string;
};
export type PagedMixnodeResponse = {
nodes: MixNodeBond[];
per_page: number;
start_next_after?: string;
};
export type PagedGatewayResponse = {
nodes: GatewayBond[];
per_page: number;
start_next_after?: string;
};
export type MixOwnershipResponse = {
address: string;
mixnode?: MixNodeBond;
};
export type GatewayOwnershipResponse = {
address: string;
gateway?: GatewayBond;
};
export type ContractStateParams = {
// ideally I'd want to define those as `number` rather than `string`, but
// rust-side they are defined as Uint128 and that don't have
// native javascript representations and therefore are interpreted as strings after deserialization
minimum_mixnode_pledge: string;
minimum_gateway_pledge: string;
mixnode_rewarded_set_size: number;
mixnode_active_set_size: number;
};
export type LayerDistribution = {
gateways: number;
layer1: number;
layer2: number;
layer3: number;
};
export type Delegation = {
owner: string;
node_identity: string;
amount: Coin;
block_height: number;
proxy?: string;
};
export type PagedMixDelegationsResponse = {
delegations: Delegation[];
start_next_after?: string;
};
export type PagedDelegatorDelegationsResponse = {
delegations: Delegation[];
start_next_after?: string;
};
export type PagedAllDelegationsResponse = {
delegations: Delegation[];
start_next_after?: [string, string];
};
export type RewardingResult = {
operator_reward: string;
total_delegator_reward: string;
};
export type NodeRewardParams = {
period_reward_pool: string;
k: string;
reward_blockstamp: number;
circulating_supply: string;
uptime: string;
sybil_resistance_percent: number;
};
export type DelegatorRewardParams = {
node_reward_params: NodeRewardParams;
sigma: number;
profit_margin: number;
node_profit: number;
};
export type PendingDelegatorRewarding = {
running_results: RewardingResult;
next_start: string;
rewarding_params: DelegatorRewardParams;
};
export type RewardingStatus = { Complete: RewardingResult } | { PendingNextDelegatorPage: PendingDelegatorRewarding };
export type MixnodeRewardingStatusResponse = {
status?: RewardingStatus;
};
export enum Layer {
Gateway,
One,
Two,
Three,
}
export type MixNodeBond = {
owner: string;
mix_node: MixNode;
layer: Layer;
bond_amount: Coin;
total_delegation: Coin;
};
export type MixNode = {
host: string;
mix_port: number;
verloc_port: number;
http_api_port: number;
sphinx_key: string;
identity_key: string;
version: string;
profit_margin_percent: number;
};
export type GatewayBond = {
owner: string;
gateway: Gateway;
bond_amount: Coin;
total_delegation: Coin;
};
export type Gateway = {
host: string;
mix_port: number;
clients_port: number;
location: string;
sphinx_key: string;
identity_key: string;
version: string;
};
export type SendRequest = {
senderAddress: string;
recipientAddress: string;
transferAmount: readonly Coin[];
};
export interface SmartContractQuery {
queryContractSmart(address: string, queryMsg: Record<string, unknown>): Promise<JsonObject>;
}
export interface ICosmWasmQuery {
// methods exposed by `CosmWasmClient`
getChainId(): Promise<string>;
getHeight(): Promise<number>;
getAccount(searchAddress: string): Promise<Account | null>;
getSequence(address: string): Promise<SequenceResponse>;
getBlock(height?: number): Promise<Block>;
getBalance(address: string, searchDenom: string): Promise<Coin>;
getTx(id: string): Promise<IndexedTx | null>;
searchTx(query: SearchTxQuery, filter?: SearchTxFilter): Promise<readonly IndexedTx[]>;
disconnect(): void;
broadcastTx(tx: Uint8Array, timeoutMs?: number, pollIntervalMs?: number): Promise<DeliverTxResponse>;
getCodes(): Promise<readonly Code[]>;
getCodeDetails(codeId: number): Promise<CodeDetails>;
getContracts(codeId: number): Promise<readonly string[]>;
getContract(address: string): Promise<Contract>;
getContractCodeHistory(address: string): Promise<readonly ContractCodeHistoryEntry[]>;
queryContractRaw(address: string, key: Uint8Array): Promise<Uint8Array | null>;
queryContractSmart(address: string, queryMsg: Record<string, unknown>): Promise<JsonObject>;
}
export interface INymdQuery {
// nym-specific implemented inside NymQuerier
getContractVersion(mixnetContractAddress: string): Promise<MixnetContractVersion>;
getMixNodeBonds(
mixnetContractAddress: string,
limit?: number,
startAfter?: string,
): Promise<PagedMixNodeBondResponse>;
getMixNodesDetailed(
mixnetContractAddress: string,
limit?: number,
startAfter?: string,
): Promise<PagedMixNodeDetailsResponse>;
getGatewaysPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedGatewayResponse>;
getOwnedMixnode(mixnetContractAddress: string, address: string): Promise<MixOwnershipResponse>;
ownsGateway(mixnetContractAddress: string, address: string): Promise<GatewayOwnershipResponse>;
getStateParams(mixnetContractAddress: string): Promise<ContractState>;
getAllNetworkDelegationsPaged(
mixnetContractAddress: string,
limit?: number,
startAfter?: [string, string],
): Promise<PagedAllDelegationsResponse>;
getMixNodeDelegationsPaged(
mixnetContractAddress: string,
mix_id: number,
limit?: number,
startAfter?: string,
): Promise<PagedMixDelegationsResponse>;
getDelegatorDelegationsPaged(
mixnetContractAddress: string,
delegator: string,
limit?: number,
startAfter?: string,
): Promise<PagedDelegatorDelegationsResponse>;
getDelegationDetails(mixnetContractAddress: string, mix_id: number, delegator: string): Promise<Delegation>;
getLayerDistribution(mixnetContractAddress: string): Promise<LayerDistribution>;
getStakeSaturation(mixnetContractAddress: string, mixId: number): Promise<StakeSaturationResponse>;
getUnbondedMixNodeInformation(mixnetContractAddress: string, mixId: number): Promise<UnbondedMixnodeResponse>;
getMixnodeRewardingDetails(mixnetContractAddress: string, mixId: number): Promise<MixNodeRewarding>;
}
export interface IVestingQuerier {
getVestingContractVersion(mixnetContractAddress: string): Promise<MixnetContractVersion>;
}
export interface MappedCoin {
readonly denom: string;
readonly fractionalDigits: number;
}
export interface CoinMap {
readonly [key: string]: MappedCoin;
}
export interface ContractState {
owner: string;
rewarding_validator_address: string;
vesting_contract_address: string;
rewarding_denom: string;
params: ContractStateParams;
}
@@ -1,11 +0,0 @@
import ValidatorClient from '../../validator/index';
import expect from 'expect';
describe('Query: balances', () => {
it('can query for an account balance', async () => {
const client = await ValidatorClient.connectForQuery(
'https://rpc.nymtech.net/', 'https://validator.nymtech.net/api/', 'n', 'n14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sjyvg3g', 'n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw', 'nym');
const balance = await client.getBalance('n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy');
expect(Number.parseFloat(balance.amount)).toBeGreaterThan(0);
}).timeout(5000);
})
-14
View File
@@ -1,14 +0,0 @@
import ValidatorClient from '../../dist';
import expect from 'expect';
// TODO: implement for QA with .env for mnemonics
// describe('Sign: send', () => {
// it('can send tokens', async () => {
// const client = await ValidatorClient.connect(
// '<ADD MNEMONIC HERE>',
// 'https://rpc.nyx.nodes.guru/', 'https://validator.nymtech.net/api/', 'n', 'n14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sjyvg3g', 'n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw', 'nym');
// await client.send('<ADD ADDRESS HERE>')
// const balance = await client.getBalance('n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy');
// expect(Number.parseFloat(balance.amount)).toBeGreaterThan(0);
// }).timeout(5000);
// })
+21 -22
View File
@@ -1,23 +1,22 @@
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"esModuleInterop": true,
"strict": true,
"declaration": true,
"outDir": "./dist",
"skipLibCheck": true
},
"typedocOptions": {
"entryPoints": [
"src/index.ts"
],
"out": "docs"
},
"exclude": [
"dist",
"examples",
"tests",
"node_modules"
]
}
"compilerOptions": {
"outDir": "./dist/nym-validator-client",
"module": "ES2020",
"target": "es2021",
"allowJs": false,
"strict": true,
"lib": ["es2021", "dom", "dom.iterable", "esnext"],
"moduleResolution": "node",
"skipDefaultLibCheck": true,
"esModuleInterop": true,
"declaration": true,
"skipLibCheck": true,
"noImplicitAny": true,
"typeRoots": ["./src/types/global.d.ts"]
},
"typedocOptions": {
"entryPoints": ["./src/index.ts"],
"out": "docs"
},
"exclude": ["dist", "./src/tests/**/*", "node_module"]
}
+6
View File
@@ -0,0 +1,6 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "CommonJS"
}
}
+2 -2
View File
@@ -31,8 +31,8 @@ wasm-bindgen-futures = "0.4"
# internal
client-core = { path = "../client-core", default-features = false, features = ["wasm"] }
coconut-interface = { path = "../../common/coconut-interface" }
credentials = { path = "../../common/credentials" }
nym-coconut-interface = { path = "../../common/coconut-interface" }
nym-credentials = { path = "../../common/credentials" }
nym-crypto = { path = "../../common/crypto" }
nym-sphinx = { path = "../../common/nymsphinx" }
gateway-client = { path = "../../common/client-libs/gateway-client", default-features = false, features = ["wasm"] }
@@ -1,10 +0,0 @@
[package]
name = "bandwidth-claim-contract"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
schemars = "0.8"
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
@@ -1,45 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
// Serializable structures for what we find in common/crypto
#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct PublicKey([u8; 32]);
impl PublicKey {
pub fn new(bytes: [u8; 32]) -> Self {
PublicKey(bytes)
}
pub fn to_bytes(&self) -> [u8; 32] {
self.0
}
}
impl AsRef<[u8]> for PublicKey {
#[inline]
fn as_ref(&self) -> &[u8] {
&self.0
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct Signature([u8; 32], [u8; 32]);
impl Signature {
pub fn new(bytes: [u8; 64]) -> Self {
let mut sig1 = [0u8; 32];
let mut sig2 = [0u8; 32];
sig1.copy_from_slice(&bytes[..32]);
sig2.copy_from_slice(&bytes[32..]);
Signature(sig1, sig2)
}
pub fn to_bytes(&self) -> [u8; 64] {
let mut res = [0u8; 64];
res[..32].copy_from_slice(&self.0);
res[32..].copy_from_slice(&self.1);
res
}
}
@@ -1,3 +0,0 @@
pub mod keys;
pub mod msg;
pub mod payment;
@@ -1,30 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::keys::PublicKey;
use crate::payment::LinkPaymentData;
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct InstantiateMsg {}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum ExecuteMsg {
LinkPayment { data: LinkPaymentData },
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum QueryMsg {
GetPayments {
limit: Option<u32>,
start_after: Option<PublicKey>,
},
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub struct MigrateMsg {}
@@ -1,73 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::keys::{PublicKey, Signature};
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
pub struct Payment {
verification_key: PublicKey,
gateway_identity: PublicKey,
bandwidth: u64,
}
impl Payment {
pub fn new(verification_key: PublicKey, gateway_identity: PublicKey, bandwidth: u64) -> Self {
Payment {
verification_key,
gateway_identity,
bandwidth,
}
}
pub fn verification_key(&self) -> PublicKey {
self.verification_key
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct LinkPaymentData {
pub verification_key: PublicKey,
pub gateway_identity: PublicKey,
pub bandwidth: u64,
pub signature: Signature,
}
impl LinkPaymentData {
pub fn new(
verification_key: [u8; 32],
gateway_identity: [u8; 32],
bandwidth: u64,
signature: [u8; 64],
) -> Self {
LinkPaymentData {
verification_key: PublicKey::new(verification_key),
gateway_identity: PublicKey::new(gateway_identity),
bandwidth,
signature: Signature::new(signature),
}
}
}
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
pub struct PagedPaymentResponse {
pub payments: Vec<Payment>,
pub per_page: usize,
pub start_next_after: Option<PublicKey>,
}
impl PagedPaymentResponse {
pub fn new(
payments: Vec<Payment>,
per_page: usize,
start_next_after: Option<PublicKey>,
) -> Self {
PagedPaymentResponse {
payments,
per_page,
start_next_after,
}
}
}
+1 -1
View File
@@ -17,7 +17,7 @@ semver = "0.11"
serde = { workspace = true, features = ["derive"], optional = true }
[build-dependencies]
vergen = { version = "7", default-features = false, features = ["build", "git", "rustc", "cargo"] }
vergen = { version = "=7.4.3", default-features = false, features = ["build", "git", "rustc", "cargo"] }
[features]
default = []
+6 -1
View File
@@ -4,5 +4,10 @@
use vergen::{vergen, Config};
fn main() {
vergen(Config::default()).expect("failed to extract build metadata")
let mut config = Config::default();
if std::env::var("DOCS_RS").is_ok() {
// If we don't have access to git information, such as in a docs.rs build, don't error
*config.git_mut().skip_if_error_mut() = true;
}
vergen(config).expect("failed to extract build metadata");
}
View File
+3 -3
View File
@@ -18,8 +18,8 @@ async-trait = { workspace = true }
tokio = { version = "1.24.1", features = ["macros"] }
# internal
coconut-interface = { path = "../../coconut-interface" }
credentials = { path = "../../credentials" }
nym-coconut-interface = { path = "../../coconut-interface" }
nym-credentials = { path = "../../credentials" }
nym-crypto = { path = "../../crypto" }
gateway-requests = { path = "../../../gateway/gateway-requests" }
nym-network-defaults = { path = "../../network-defaults" }
@@ -47,7 +47,7 @@ features = ["net", "sync", "time"]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-tungstenite]
version = "0.14"
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.credential-storage]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.nym-credential-storage]
path = "../../credential-storage"
# wasm-only dependencies
@@ -7,7 +7,7 @@ use crate::error::GatewayClientError;
use crate::wasm_mockups::Storage;
#[cfg(not(target_arch = "wasm32"))]
#[cfg(not(feature = "mobile"))]
use credential_storage::storage::Storage;
use nym_credential_storage::storage::Storage;
#[cfg(not(target_arch = "wasm32"))]
#[cfg(feature = "mobile")]
@@ -22,7 +22,7 @@ use crate::wasm_mockups::StorageError;
#[cfg(not(target_arch = "wasm32"))]
#[cfg(not(feature = "mobile"))]
use credential_storage::error::StorageError;
use nym_credential_storage::error::StorageError;
#[cfg(target_arch = "wasm32")]
use crate::wasm_mockups::{Client, CosmWasmClient};
@@ -30,8 +30,8 @@ use std::str::FromStr;
#[cfg(not(target_arch = "wasm32"))]
use validator_client::{nyxd::CosmWasmClient, Client};
use {
coconut_interface::Base58,
credentials::coconut::{
nym_coconut_interface::Base58,
nym_credentials::coconut::{
bandwidth::prepare_for_spending, utils::obtain_aggregate_verification_key,
},
};
@@ -42,7 +42,7 @@ use crate::wasm_mockups::PersistentStorage;
#[cfg(not(target_arch = "wasm32"))]
#[cfg(not(feature = "mobile"))]
use credential_storage::PersistentStorage;
use nym_credential_storage::PersistentStorage;
#[cfg(not(target_arch = "wasm32"))]
#[cfg(feature = "mobile")]
@@ -69,17 +69,17 @@ where
pub async fn prepare_coconut_credential(
&self,
) -> Result<(coconut_interface::Credential, i64), GatewayClientError> {
) -> Result<(nym_coconut_interface::Credential, i64), GatewayClientError> {
let bandwidth_credential = self.storage.get_next_coconut_credential().await?;
let voucher_value = u64::from_str(&bandwidth_credential.voucher_value)
.map_err(|_| StorageError::InconsistentData)?;
let voucher_info = bandwidth_credential.voucher_info.clone();
let serial_number =
coconut_interface::Attribute::try_from_bs58(bandwidth_credential.serial_number)?;
nym_coconut_interface::Attribute::try_from_bs58(bandwidth_credential.serial_number)?;
let binding_number =
coconut_interface::Attribute::try_from_bs58(bandwidth_credential.binding_number)?;
nym_coconut_interface::Attribute::try_from_bs58(bandwidth_credential.binding_number)?;
let signature =
coconut_interface::Signature::try_from_bs58(bandwidth_credential.signature)?;
nym_coconut_interface::Signature::try_from_bs58(bandwidth_credential.signature)?;
let epoch_id = u64::from_str(&bandwidth_credential.epoch_id)
.map_err(|_| StorageError::InconsistentData)?;
@@ -9,13 +9,13 @@ pub use crate::packet_router::{
};
use crate::socket_state::{PartiallyDelegated, SocketState};
use crate::{cleanup_socket_message, try_decrypt_binary_message};
use coconut_interface::Credential;
use futures::{SinkExt, StreamExt};
use gateway_requests::authentication::encrypted_address::EncryptedAddressBytes;
use gateway_requests::iv::IV;
use gateway_requests::registration::handshake::{client_handshake, SharedKeys};
use gateway_requests::{BinaryRequest, ClientControlRequest, ServerResponse, PROTOCOL_VERSION};
use log::*;
use nym_coconut_interface::Credential;
use nym_crypto::asymmetric::identity;
use nym_network_defaults::{REMAINING_BANDWIDTH_THRESHOLD, TOKENS_TO_BURN};
use nym_sphinx::forwarding::packet::MixPacket;
@@ -33,7 +33,7 @@ use validator_client::nyxd::CosmWasmClient;
#[cfg(not(target_arch = "wasm32"))]
#[cfg(not(feature = "mobile"))]
use credential_storage::PersistentStorage;
use nym_credential_storage::PersistentStorage;
#[cfg(not(target_arch = "wasm32"))]
#[cfg(feature = "mobile")]
@@ -3,13 +3,13 @@
#[cfg(target_arch = "wasm32")]
use crate::wasm_mockups::StorageError;
#[cfg(not(feature = "mobile"))]
#[cfg(not(target_arch = "wasm32"))]
use credential_storage::error::StorageError;
use gateway_requests::registration::handshake::error::HandshakeError;
#[cfg(feature = "mobile")]
#[cfg(not(target_arch = "wasm32"))]
use mobile_storage::StorageError;
#[cfg(not(feature = "mobile"))]
#[cfg(not(target_arch = "wasm32"))]
use nym_credential_storage::error::StorageError;
use std::io;
use thiserror::Error;
use tungstenite::Error as WsError;
@@ -31,7 +31,7 @@ pub enum GatewayClientError {
CredentialStorageError(#[from] StorageError),
#[error("Coconut error - {0}")]
CoconutError(#[from] coconut_interface::CoconutError),
CoconutError(#[from] nym_coconut_interface::CoconutError),
// TODO: see if `JsValue` is a reasonable type for this
#[cfg(target_arch = "wasm32")]
@@ -48,7 +48,7 @@ pub enum GatewayClientError {
NoBandwidthControllerAvailable,
#[error("Credential error - {0}")]
CredentialError(#[from] credentials::error::Error),
CredentialError(#[from] nym_credentials::error::Error),
#[error("Connection was abruptly closed")]
ConnectionAbruptlyClosed,
@@ -11,13 +11,13 @@ rust-version = "1.56"
base64 = "0.13"
colored = "2.0"
coconut-dkg-common = { path = "../../cosmwasm-smart-contracts/coconut-dkg" }
nym-coconut-dkg-common = { path = "../../cosmwasm-smart-contracts/coconut-dkg" }
nym-contracts-common = { path = "../../cosmwasm-smart-contracts/contracts-common" }
nym-mixnet-contract-common = { path= "../../cosmwasm-smart-contracts/mixnet-contract" }
nym-vesting-contract-common = { path= "../../cosmwasm-smart-contracts/vesting-contract" }
coconut-bandwidth-contract-common = { path= "../../cosmwasm-smart-contracts/coconut-bandwidth-contract" }
multisig-contract-common = { path = "../../cosmwasm-smart-contracts/multisig-contract" }
group-contract-common = { path = "../../cosmwasm-smart-contracts/group-contract" }
nym-coconut-bandwidth-contract-common = { path= "../../cosmwasm-smart-contracts/coconut-bandwidth-contract" }
nym-multisig-contract-common = { path = "../../cosmwasm-smart-contracts/multisig-contract" }
nym-group-contract-common = { path = "../../cosmwasm-smart-contracts/group-contract" }
nym-vesting-contract = { path = "../../../contracts/vesting" }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
@@ -28,9 +28,10 @@ url = { version = "2.2", features = ["serde"] }
tokio = { version = "1.24.1", features = ["sync", "time"] }
futures = "0.3"
coconut-interface = { path = "../../coconut-interface" }
nym-coconut-interface = { path = "../../coconut-interface" }
nym-network-defaults = { path = "../../network-defaults" }
nym-api-requests = { path = "../../../nym-api/nym-api-requests" }
nym-execute = { path = "../../execute" }
# required for nyxd-client
# at some point it might be possible to make it wasm-compatible
@@ -46,7 +47,7 @@ flate2 = { version = "1.0.20", optional = true }
sha2 = { version = "0.9.5", optional = true }
itertools = { version = "0.10", optional = true }
cosmwasm-std = { version = "1.0.0", optional = true }
nym-execute = { path = "../../execute" }
zeroize = { version = "1.5.7", optional = true }
[dev-dependencies]
ts-rs = "6.1.2"
@@ -64,6 +65,7 @@ nyxd-client = [
"sha2",
"itertools",
"cosmwasm-std",
"zeroize"
]
generate-ts = []
@@ -2,8 +2,6 @@
// SPDX-License-Identifier: Apache-2.0
use crate::{nym_api, ValidatorClientError};
use coconut_dkg_common::types::NodeIndex;
use coconut_interface::VerificationKey;
use nym_api_requests::coconut::{
BlindSignRequestBody, BlindedSignatureResponse, VerifyCredentialBody, VerifyCredentialResponse,
};
@@ -11,6 +9,8 @@ use nym_api_requests::models::{
GatewayCoreStatusResponse, MixnodeCoreStatusResponse, MixnodeStatusResponse,
RewardEstimationResponse, StakeSaturationResponse,
};
use nym_coconut_dkg_common::types::NodeIndex;
use nym_coconut_interface::VerificationKey;
pub use nym_mixnet_contract_common::{mixnode::MixNodeDetails, GatewayBond, IdentityKeyRef, MixId};
#[cfg(feature = "nyxd-client")]
@@ -18,17 +18,17 @@ use crate::nyxd::traits::{DkgQueryClient, MixnetQueryClient, MultisigQueryClient
#[cfg(feature = "nyxd-client")]
use crate::nyxd::{self, CosmWasmClient, NyxdClient, QueryNyxdClient, SigningNyxdClient};
#[cfg(feature = "nyxd-client")]
use coconut_dkg_common::{
use cw3::ProposalResponse;
#[cfg(feature = "nyxd-client")]
use nym_api_requests::models::MixNodeBondAnnotated;
#[cfg(feature = "nyxd-client")]
use nym_coconut_dkg_common::{
dealer::ContractDealing,
types::{DealerDetails, EpochId},
verification_key::ContractVKShare,
};
#[cfg(feature = "nyxd-client")]
use coconut_interface::Base58;
#[cfg(feature = "nyxd-client")]
use cw3::ProposalResponse;
#[cfg(feature = "nyxd-client")]
use nym_api_requests::models::MixNodeBondAnnotated;
use nym_coconut_interface::Base58;
#[cfg(feature = "nyxd-client")]
use nym_mixnet_contract_common::{
families::{Family, FamilyHead},
@@ -130,10 +130,9 @@ impl Config {
#[cfg(feature = "nyxd-client")]
#[derive(Clone)]
pub struct Client<C: Clone> {
// TODO: we really shouldn't be storing a mnemonic here, but removing it would be
// non-trivial amount of work and it's out of scope of the current branch
mnemonic: Option<bip39::Mnemonic>,
// // TODO: we really shouldn't be storing a mnemonic here, but removing it would be
// // non-trivial amount of work and it's out of scope of the current branch
// mnemonic: Option<bip39::Mnemonic>,
mixnode_page_limit: Option<u32>,
gateway_page_limit: Option<u32>,
mixnode_delegations_page_limit: Option<u32>,
@@ -157,12 +156,11 @@ impl Client<SigningNyxdClient> {
let nyxd_client = NyxdClient::connect_with_mnemonic(
config.nyxd_config.clone(),
config.nyxd_url.as_str(),
mnemonic.clone(),
mnemonic,
None,
)?;
Ok(Client {
mnemonic: Some(mnemonic),
mixnode_page_limit: config.mixnode_page_limit,
gateway_page_limit: config.gateway_page_limit,
mixnode_delegations_page_limit: config.mixnode_delegations_page_limit,
@@ -176,12 +174,7 @@ impl Client<SigningNyxdClient> {
}
pub fn change_nyxd(&mut self, new_endpoint: Url) -> Result<(), ValidatorClientError> {
self.nyxd = NyxdClient::connect_with_mnemonic(
self.nyxd.current_config().clone(),
new_endpoint.as_ref(),
self.mnemonic.clone().unwrap(),
None,
)?;
self.nyxd.change_endpoint(new_endpoint.as_ref())?;
Ok(())
}
@@ -198,7 +191,6 @@ impl Client<QueryNyxdClient> {
NyxdClient::connect(config.nyxd_config.clone(), config.nyxd_url.as_str())?;
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,
@@ -6,8 +6,8 @@ use cosmrs::tendermint::abci;
use itertools::Itertools;
use serde::{Deserialize, Serialize};
pub use coconut_bandwidth_contract_common::event_attributes::*;
pub use coconut_dkg_common::event_attributes::*;
pub use nym_coconut_bandwidth_contract_common::event_attributes::*;
pub use nym_coconut_dkg_common::event_attributes::*;
// it seems that currently validators just emit stringified events (which are also returned as part of deliverTx response)
// as theirs logs
@@ -756,6 +756,19 @@ impl Client {
})
}
pub fn change_endpoint<U>(&mut self, new_endpoint: U) -> Result<(), NyxdError>
where
U: TryInto<HttpClientUrl, Error = TendermintRpcError>,
{
let new_rpc_client = HttpClient::new(new_endpoint)?;
self.rpc_client = new_rpc_client;
Ok(())
}
pub fn into_signer(self) -> DirectSecp256k1HdWallet {
self.signer
}
pub fn set_broadcast_polling_rate(&mut self, broadcast_polling_rate: Duration) {
self.broadcast_polling_rate = broadcast_polling_rate
}
@@ -140,7 +140,7 @@ pub enum NyxdError {
CosmwasmStdError(#[from] cosmwasm_std::StdError),
#[error("Coconut interface error: {0}")]
CoconutInterfaceError(#[from] coconut_interface::error::CoconutInterfaceError),
CoconutInterfaceError(#[from] nym_coconut_interface::error::CoconutInterfaceError),
#[error("Account had an unexpected bech32 prefix. Expected: {expected}, got: {got}")]
UnexpectedBech32Prefix { got: String, expected: String },
@@ -43,6 +43,7 @@ pub use cosmrs::tendermint::Time as TendermintTime;
pub use cosmrs::tx::{self, Gas};
pub use cosmrs::Coin as CosmosCoin;
pub use cosmrs::{bip32, AccountId, Decimal, Denom};
use cosmwasm_std::Addr;
pub use cosmwasm_std::Coin as CosmWasmCoin;
pub use fee::{gas_price::GasPrice, GasAdjustable, GasAdjustment};
pub use signing_client::Client as SigningNyxdClient;
@@ -197,7 +198,7 @@ impl NyxdClient<SigningNyxdClient> {
{
let prefix = &config.chain_details.bech32_account_prefix;
let denom = &config.chain_details.mix_denom.base;
let wallet = DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic)?;
let wallet = DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic);
let client_address = wallet
.try_derive_accounts()?
.into_iter()
@@ -212,6 +213,17 @@ impl NyxdClient<SigningNyxdClient> {
simulated_gas_multiplier: DEFAULT_SIMULATED_GAS_MULTIPLIER,
})
}
pub fn change_endpoint<U>(&mut self, new_endpoint: U) -> Result<(), NyxdError>
where
U: TryInto<HttpClientUrl, Error = TendermintRpcError>,
{
self.client.change_endpoint(new_endpoint)
}
pub fn into_signer(self) -> DirectSecp256k1HdWallet {
self.client.into_signer()
}
}
impl<C> NyxdClient<C>
@@ -358,6 +370,15 @@ where
&self.client_address.as_ref().unwrap()[0]
}
pub fn cw_address(&self) -> Addr
where
C: SigningCosmWasmClient,
{
// the call to unchecked is fine here as we're converting directly from `AccountId`
// which must have been a valid bech32 address
Addr::unchecked(self.address().as_ref())
}
pub fn signer(&self) -> &DirectSecp256k1HdWallet
where
C: SigningCosmWasmClient,
@@ -4,8 +4,8 @@
use crate::nyxd::error::NyxdError;
use crate::nyxd::{CosmWasmClient, NyxdClient};
use coconut_bandwidth_contract_common::msg::QueryMsg;
use coconut_bandwidth_contract_common::spend_credential::SpendCredentialResponse;
use nym_coconut_bandwidth_contract_common::msg::QueryMsg;
use nym_coconut_bandwidth_contract_common::spend_credential::SpendCredentialResponse;
use async_trait::async_trait;
@@ -5,8 +5,8 @@ pub use crate::nyxd::cosmwasm_client::signing_client::SigningCosmWasmClient;
use crate::nyxd::cosmwasm_client::types::ExecuteResult;
use crate::nyxd::error::NyxdError;
use crate::nyxd::{Coin, Fee, NyxdClient};
use coconut_bandwidth_contract_common::spend_credential::SpendCredentialData;
use coconut_bandwidth_contract_common::{deposit::DepositData, msg::ExecuteMsg};
use nym_coconut_bandwidth_contract_common::spend_credential::SpendCredentialData;
use nym_coconut_bandwidth_contract_common::{deposit::DepositData, msg::ExecuteMsg};
use async_trait::async_trait;
@@ -4,13 +4,13 @@
use crate::nyxd::error::NyxdError;
use crate::nyxd::{CosmWasmClient, NyxdClient};
use async_trait::async_trait;
use coconut_dkg_common::dealer::{
use cosmrs::AccountId;
use nym_coconut_dkg_common::dealer::{
DealerDetailsResponse, PagedDealerResponse, PagedDealingsResponse,
};
use coconut_dkg_common::msg::QueryMsg as DkgQueryMsg;
use coconut_dkg_common::types::{Epoch, EpochId, InitialReplacementData};
use coconut_dkg_common::verification_key::PagedVKSharesResponse;
use cosmrs::AccountId;
use nym_coconut_dkg_common::msg::QueryMsg as DkgQueryMsg;
use nym_coconut_dkg_common::types::{Epoch, EpochId, InitialReplacementData};
use nym_coconut_dkg_common::verification_key::PagedVKSharesResponse;
#[async_trait]
pub trait DkgQueryClient {
@@ -5,9 +5,9 @@ use crate::nyxd::cosmwasm_client::types::ExecuteResult;
use crate::nyxd::error::NyxdError;
use crate::nyxd::{Fee, NyxdClient, SigningCosmWasmClient};
use async_trait::async_trait;
use coconut_dkg_common::msg::ExecuteMsg as DkgExecuteMsg;
use coconut_dkg_common::types::EncodedBTEPublicKeyWithProof;
use coconut_dkg_common::verification_key::VerificationKeyShare;
use nym_coconut_dkg_common::msg::ExecuteMsg as DkgExecuteMsg;
use nym_coconut_dkg_common::types::EncodedBTEPublicKeyWithProof;
use nym_coconut_dkg_common::verification_key::VerificationKeyShare;
use nym_contracts_common::dealings::ContractSafeBytes;
#[async_trait]
@@ -4,7 +4,7 @@
use crate::nyxd::error::NyxdError;
use crate::nyxd::{CosmWasmClient, NyxdClient};
use group_contract_common::msg::QueryMsg;
use nym_group_contract_common::msg::QueryMsg;
use async_trait::async_trait;
use cw4::MemberResponse;
@@ -6,6 +6,7 @@ use crate::nyxd::error::NyxdError;
use crate::nyxd::NyxdClient;
use async_trait::async_trait;
use cosmrs::AccountId;
use nym_contracts_common::signing::Nonce;
use nym_mixnet_contract_common::delegation::{MixNodeDelegationResponse, OwnerProxySubKey};
use nym_mixnet_contract_common::families::Family;
use nym_mixnet_contract_common::mixnode::{
@@ -18,12 +19,13 @@ use nym_mixnet_contract_common::rewarding::{
};
use nym_mixnet_contract_common::{
delegation, ContractBuildInformation, ContractState, ContractStateParams,
CurrentIntervalResponse, EpochEventId, GatewayBondResponse, GatewayOwnershipResponse,
IdentityKey, IntervalEventId, LayerDistribution, MixId, MixOwnershipResponse,
MixnodeDetailsResponse, PagedAllDelegationsResponse, PagedDelegatorDelegationsResponse,
PagedFamiliesResponse, PagedGatewayResponse, PagedMembersResponse,
PagedMixNodeDelegationsResponse, PagedMixnodeBondsResponse, PagedRewardedSetResponse,
PendingEpochEventsResponse, PendingIntervalEventsResponse, QueryMsg as MixnetQueryMsg,
CurrentIntervalResponse, EpochEventId, EpochStatus, GatewayBondResponse,
GatewayOwnershipResponse, IdentityKey, IntervalEventId, LayerDistribution, MixId,
MixOwnershipResponse, MixnodeDetailsResponse, NumberOfPendingEventsResponse,
PagedAllDelegationsResponse, PagedDelegatorDelegationsResponse, PagedFamiliesResponse,
PagedGatewayResponse, PagedMembersResponse, PagedMixNodeDelegationsResponse,
PagedMixnodeBondsResponse, PagedRewardedSetResponse, PendingEpochEventsResponse,
PendingIntervalEventsResponse, QueryMsg as MixnetQueryMsg,
};
use serde::Deserialize;
@@ -60,6 +62,11 @@ pub trait MixnetQueryClient {
.await
}
async fn get_current_epoch_status(&self) -> Result<EpochStatus, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetEpochStatus {})
.await
}
async fn get_current_interval_details(&self) -> Result<CurrentIntervalResponse, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetCurrentIntervalDetails {})
.await
@@ -377,6 +384,20 @@ pub trait MixnetQueryClient {
.await
}
async fn get_number_of_pending_events(
&self,
) -> Result<NumberOfPendingEventsResponse, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetNumberOfPendingEvents {})
.await
}
async fn get_signing_nonce(&self, address: &AccountId) -> Result<Nonce, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetSigningNonce {
address: address.to_string(),
})
.await
}
async fn get_node_family_by_label(&self, label: &str) -> Result<Option<Family>, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetFamilyByLabel {
label: label.to_string(),
@@ -8,6 +8,7 @@ use crate::nyxd::error::NyxdError;
use crate::nyxd::{Fee, NyxdClient, SigningCosmWasmClient};
use async_trait::async_trait;
use cosmrs::AccountId;
use nym_contracts_common::signing::MessageSignature;
use nym_mixnet_contract_common::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
use nym_mixnet_contract_common::reward_params::{IntervalRewardingParamsUpdate, Performance};
use nym_mixnet_contract_common::{
@@ -106,6 +107,11 @@ pub trait MixnetSigningClient {
.await
}
async fn begin_epoch_transition(&self, fee: Option<Fee>) -> Result<ExecuteResult, NyxdError> {
self.execute_mixnet_contract(fee, MixnetExecuteMsg::BeginEpochTransition {}, vec![])
.await
}
async fn advance_current_epoch(
&self,
new_rewarded_set: Vec<LayerAssignment>,
@@ -193,7 +199,8 @@ pub trait MixnetSigningClient {
async fn join_family_on_behalf(
&self,
member_address: String,
signature: String,
node_identity_signature: String,
family_signature: String,
family_head: String,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
@@ -201,7 +208,8 @@ pub trait MixnetSigningClient {
fee,
MixnetExecuteMsg::JoinFamilyOnBehalf {
member_address,
signature,
node_identity_signature,
family_signature,
family_head,
},
vec![],
@@ -229,7 +237,7 @@ pub trait MixnetSigningClient {
async fn leave_family_on_behalf(
&self,
member_address: String,
signature: String,
node_identity_signature: String,
family_head: String,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
@@ -237,7 +245,7 @@ pub trait MixnetSigningClient {
fee,
MixnetExecuteMsg::LeaveFamilyOnBehalf {
member_address,
signature,
node_identity_signature,
family_head,
},
vec![],
@@ -283,7 +291,7 @@ pub trait MixnetSigningClient {
&self,
mix_node: MixNode,
cost_params: MixNodeCostParams,
owner_signature: String,
owner_signature: MessageSignature,
pledge: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
@@ -304,7 +312,7 @@ pub trait MixnetSigningClient {
owner: AccountId,
mix_node: MixNode,
cost_params: MixNodeCostParams,
owner_signature: String,
owner_signature: MessageSignature,
pledge: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
@@ -435,7 +443,7 @@ pub trait MixnetSigningClient {
async fn bond_gateway(
&self,
gateway: Gateway,
owner_signature: String,
owner_signature: MessageSignature,
pledge: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
@@ -454,7 +462,7 @@ pub trait MixnetSigningClient {
&self,
owner: AccountId,
gateway: Gateway,
owner_signature: String,
owner_signature: MessageSignature,
pledge: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
@@ -5,7 +5,7 @@ use crate::nyxd::error::NyxdError;
use crate::nyxd::{CosmWasmClient, NyxdClient};
use cw3::{ProposalListResponse, ProposalResponse};
use multisig_contract_common::msg::QueryMsg;
use nym_multisig_contract_common::msg::QueryMsg;
use async_trait::async_trait;
@@ -6,9 +6,9 @@ use crate::nyxd::cosmwasm_client::types::ExecuteResult;
use crate::nyxd::error::NyxdError;
use crate::nyxd::{Fee, NyxdClient};
use coconut_bandwidth_contract_common::msg::ExecuteMsg as CoconutBandwidthExecuteMsg;
use cw3::Vote;
use multisig_contract_common::msg::ExecuteMsg;
use nym_coconut_bandwidth_contract_common::msg::ExecuteMsg as CoconutBandwidthExecuteMsg;
use nym_multisig_contract_common::msg::ExecuteMsg;
use async_trait::async_trait;
use cosmwasm_std::{to_binary, Coin, CosmosMsg, WasmMsg};
@@ -6,6 +6,7 @@ use crate::nyxd::cosmwasm_client::types::ExecuteResult;
use crate::nyxd::error::NyxdError;
use crate::nyxd::{Coin, Fee, NyxdClient};
use async_trait::async_trait;
use nym_contracts_common::signing::MessageSignature;
use nym_mixnet_contract_common::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
use nym_mixnet_contract_common::{Gateway, MixId, MixNode};
use nym_vesting_contract_common::messages::{
@@ -43,7 +44,7 @@ pub trait VestingSigningClient {
async fn vesting_bond_gateway(
&self,
gateway: Gateway,
owner_signature: &str,
owner_signature: MessageSignature,
pledge: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError>;
@@ -61,7 +62,7 @@ pub trait VestingSigningClient {
&self,
mix_node: MixNode,
cost_params: MixNodeCostParams,
owner_signature: &str,
owner_signature: MessageSignature,
pledge: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError>;
@@ -208,14 +209,14 @@ impl<C: SigningCosmWasmClient + Sync + Send + Clone> VestingSigningClient for Ny
async fn vesting_bond_gateway(
&self,
gateway: Gateway,
owner_signature: &str,
owner_signature: MessageSignature,
pledge: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
let req = VestingExecuteMsg::BondGateway {
gateway,
owner_signature: owner_signature.to_string(),
owner_signature,
amount: pledge.into(),
};
self.client
@@ -272,7 +273,7 @@ impl<C: SigningCosmWasmClient + Sync + Send + Clone> VestingSigningClient for Ny
&self,
mix_node: MixNode,
cost_params: MixNodeCostParams,
owner_signature: &str,
owner_signature: MessageSignature,
pledge: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
@@ -281,7 +282,7 @@ impl<C: SigningCosmWasmClient + Sync + Send + Clone> VestingSigningClient for Ny
VestingExecuteMsg::BondMixnode {
mix_node,
cost_params,
owner_signature: owner_signature.to_string(),
owner_signature,
amount: pledge.into(),
},
vec![],
@@ -8,6 +8,7 @@ use cosmrs::crypto::PublicKey;
use cosmrs::tx::SignDoc;
use cosmrs::{tx, AccountId};
use nym_config::defaults;
use zeroize::Zeroize;
/// Derivation information required to derive a keypair and an address from a mnemonic.
#[derive(Debug, Clone)]
@@ -56,19 +57,40 @@ pub struct DirectSecp256k1HdWallet {
accounts: Vec<Secp256k1Derivation>,
}
impl Zeroize for DirectSecp256k1HdWallet {
fn zeroize(&mut self) {
// in ideal world, Mnemonic would have had zeroize defined on it (there's an almost year old PR that introduces it)
// and the memory would have been filled with zeroes.
//
// we really don't want to keep our real mnemonic in memory, so let's do the semi-nasty thing
// of overwriting it with a fresh mnemonic that was never used before
//
// note: this function can only fail on an invalid word count, which clearly is not the case here
self.secret = bip39::Mnemonic::generate(self.secret.word_count()).unwrap();
self.seed.zeroize();
// there's nothing secret about derivation paths
}
}
impl Drop for DirectSecp256k1HdWallet {
fn drop(&mut self) {
self.zeroize()
}
}
impl DirectSecp256k1HdWallet {
pub fn builder(prefix: &str) -> DirectSecp256k1HdWalletBuilder {
DirectSecp256k1HdWalletBuilder::new(prefix)
}
/// Restores a wallet from the given BIP39 mnemonic using default options.
pub fn from_mnemonic(prefix: &str, mnemonic: bip39::Mnemonic) -> Result<Self, NyxdError> {
pub fn from_mnemonic(prefix: &str, mnemonic: bip39::Mnemonic) -> Self {
DirectSecp256k1HdWalletBuilder::new(prefix).build(mnemonic)
}
pub fn generate(prefix: &str, word_count: usize) -> Result<Self, NyxdError> {
let mneomonic = bip39::Mnemonic::generate(word_count)?;
Self::from_mnemonic(prefix, mneomonic)
Ok(Self::from_mnemonic(prefix, mneomonic))
}
fn derive_keypair(&self, hd_path: &DerivationPath) -> Result<Secp256k1Keypair, NyxdError> {
@@ -145,11 +167,14 @@ impl DirectSecp256k1HdWallet {
}
#[must_use]
#[derive(Zeroize)]
#[zeroize(drop)]
pub struct DirectSecp256k1HdWalletBuilder {
/// The password to use when deriving a BIP39 seed from a mnemonic.
bip39_password: String,
/// The BIP-32/SLIP-10 derivation paths
#[zeroize(skip)]
hd_paths: Vec<DerivationPath>,
/// The bech32 address prefix (human readable part)
@@ -185,23 +210,23 @@ impl DirectSecp256k1HdWalletBuilder {
self
}
pub fn build(self, mnemonic: bip39::Mnemonic) -> Result<DirectSecp256k1HdWallet, NyxdError> {
pub fn build(self, mnemonic: bip39::Mnemonic) -> DirectSecp256k1HdWallet {
let seed = mnemonic.to_seed(&self.bip39_password);
let prefix = self.prefix;
let prefix = self.prefix.clone();
let accounts = self
.hd_paths
.into_iter()
.iter()
.map(|hd_path| Secp256k1Derivation {
hd_path,
hd_path: hd_path.clone(),
prefix: prefix.clone(),
})
.collect();
Ok(DirectSecp256k1HdWallet {
DirectSecp256k1HdWallet {
accounts,
seed,
secret: mnemonic,
})
}
}
}
@@ -229,8 +254,7 @@ mod tests {
"n17n9flp6jflljg6fp05dsy07wcprf2uuu8g40rf",
];
for (idx, mnemonic) in mnemonics.iter().enumerate() {
let wallet =
DirectSecp256k1HdWallet::from_mnemonic(&prefix, mnemonic.parse().unwrap()).unwrap();
let wallet = DirectSecp256k1HdWallet::from_mnemonic(&prefix, mnemonic.parse().unwrap());
assert_eq!(
wallet.try_derive_accounts().unwrap()[0].address,
addrs[idx].parse().unwrap()
+1 -1
View File
@@ -1,5 +1,5 @@
[package]
name = "coconut-interface"
name = "nym-coconut-interface"
version = "0.1.0"
edition = "2021"
description = "Crutch library until there is proper SerDe support for coconut structs"
+4 -3
View File
@@ -30,8 +30,9 @@ cosmwasm-std = { version = "1.0.0" }
validator-client = { path = "../client-libs/validator-client", features = ["nyxd-client"] }
nym-network-defaults = { path = "../network-defaults" }
nym-contracts-common = { path = "../cosmwasm-smart-contracts/contracts-common" }
nym-mixnet-contract-common = { path = "../cosmwasm-smart-contracts/mixnet-contract" }
nym-vesting-contract-common = { path = "../cosmwasm-smart-contracts/vesting-contract" }
coconut-bandwidth-contract-common = { path = "../cosmwasm-smart-contracts/coconut-bandwidth-contract" }
coconut-dkg-common = { path = "../cosmwasm-smart-contracts/coconut-dkg" }
multisig-contract-common = { path = "../cosmwasm-smart-contracts/multisig-contract" }
nym-coconut-bandwidth-contract-common = { path = "../cosmwasm-smart-contracts/coconut-bandwidth-contract" }
nym-coconut-dkg-common = { path = "../cosmwasm-smart-contracts/coconut-dkg" }
nym-multisig-contract-common = { path = "../cosmwasm-smart-contracts/multisig-contract" }
+10 -3
View File
@@ -1,13 +1,20 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmrs::AccountId;
use cosmwasm_std::{Addr, Coin as CosmWasmCoin, Decimal};
use log::error;
use std::error::Error;
use std::fmt::Display;
use cosmwasm_std::{Coin as CosmWasmCoin, Decimal};
use log::error;
use validator_client::nyxd::Coin;
// TODO: perhaps it should be moved to some global common crate?
pub fn account_id_to_cw_addr(account_id: &AccountId) -> Addr {
// the call to unchecked is fine here as we're converting directly from `AccountId`
// which must have been a valid bech32 address
Addr::unchecked(account_id.as_ref())
}
pub fn pretty_coin(coin: &Coin) -> String {
let amount = Decimal::from_ratio(coin.amount, 1_000_000u128);
let denom = if coin.denom.starts_with('u') {
@@ -15,8 +15,7 @@ pub fn create_account(args: Args, prefix: &str) {
let word_count = args.word_count.unwrap_or(24);
let mnemonic = bip39::Mnemonic::generate(word_count).expect("failed to generate mnemonic!");
let wallet =
DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic).expect("failed to build wallet!");
let wallet = DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic);
// Output address and mnemonics into separate lines for easier parsing
println!("{}", wallet.mnemonic());
+11 -13
View File
@@ -51,21 +51,19 @@ pub async fn get_pubkey(
}
pub fn get_pubkey_from_mnemonic(address: AccountId, prefix: &str, mnemonic: bip39::Mnemonic) {
match DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic) {
Ok(wallet) => match wallet.try_derive_accounts() {
Ok(accounts) => match accounts.iter().find(|a| *a.address() == address) {
Some(account) => {
println!("{}", account.public_key().to_string());
}
None => {
error!("Could not derive key that matches {}", address)
}
},
Err(e) => {
error!("Failed to derive accounts. {}", e);
let wallet = DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic);
match wallet.try_derive_accounts() {
Ok(accounts) => match accounts.iter().find(|a| *a.address() == address) {
Some(account) => {
println!("{}", account.public_key().to_string());
}
None => {
error!("Could not derive key that matches {}", address)
}
},
Err(e) => show_error(e),
Err(e) => {
error!("Failed to derive accounts. {}", e);
}
}
}
@@ -6,7 +6,7 @@ use std::str::FromStr;
use clap::Parser;
use log::{debug, info};
use coconut_bandwidth_contract_common::msg::InstantiateMsg;
use nym_coconut_bandwidth_contract_common::msg::InstantiateMsg;
use validator_client::nyxd::AccountId;
#[derive(Debug, Parser)]
@@ -5,8 +5,8 @@ use clap::Parser;
use log::{debug, info};
use std::str::FromStr;
use coconut_dkg_common::msg::InstantiateMsg;
use coconut_dkg_common::types::TimeConfiguration;
use nym_coconut_dkg_common::msg::InstantiateMsg;
use nym_coconut_dkg_common::types::TimeConfiguration;
use validator_client::nyxd::AccountId;
#[derive(Debug, Parser)]
@@ -7,7 +7,7 @@ use clap::Parser;
use cosmwasm_std::Decimal;
use cw_utils::{Duration, Threshold};
use log::{debug, info};
use multisig_contract_common::msg::InstantiateMsg;
use nym_multisig_contract_common::msg::InstantiateMsg;
use validator_client::nyxd::AccountId;
#[derive(Debug, Parser)]
@@ -4,6 +4,7 @@
use crate::context::SigningClient;
use clap::Parser;
use log::{info, warn};
use nym_contracts_common::signing::MessageSignature;
use nym_mixnet_contract_common::Coin;
use nym_network_defaults::{DEFAULT_CLIENT_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT};
use validator_client::nyxd::traits::MixnetSigningClient;
@@ -14,7 +15,7 @@ pub struct Args {
pub host: String,
#[clap(long)]
pub signature: String,
pub signature: MessageSignature,
#[clap(long)]
pub mix_port: Option<u16>,
@@ -23,7 +24,7 @@ pub struct Args {
pub clients_port: Option<u16>,
#[clap(long)]
pub location: Option<String>,
pub location: String,
#[clap(long)]
pub sphinx_key: String,
@@ -59,9 +60,7 @@ pub async fn bond_gateway(args: Args, client: SigningClient) {
host: args.host,
mix_port: args.mix_port.unwrap_or(DEFAULT_MIX_LISTENING_PORT),
clients_port: args.clients_port.unwrap_or(DEFAULT_CLIENT_LISTENING_PORT),
location: args
.location
.unwrap_or_else(|| "secret gateway location".to_owned()),
location: args.location,
sphinx_key: args.sphinx_key,
identity_key: args.identity_key,
version: args.version,
@@ -0,0 +1,81 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use crate::utils::account_id_to_cw_addr;
use clap::Parser;
use cosmwasm_std::Coin;
use nym_mixnet_contract_common::construct_gateway_bonding_sign_payload;
use nym_network_defaults::{DEFAULT_CLIENT_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT};
use validator_client::nyxd::traits::MixnetQueryClient;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
pub host: String,
#[clap(long)]
pub mix_port: Option<u16>,
#[clap(long)]
pub clients_port: Option<u16>,
#[clap(long)]
pub location: String,
#[clap(long)]
pub sphinx_key: String,
#[clap(long)]
pub identity_key: String,
#[clap(long)]
pub version: String,
#[clap(
long,
help = "bonding amount in current DENOMINATION (so it would be 'unym', rather than 'nym')"
)]
pub amount: u128,
/// Indicates whether the gateway is going to get bonded via a vesting account
#[arg(long)]
pub with_vesting_account: bool,
}
pub async fn create_payload(args: Args, client: SigningClient) {
let denom = client.current_chain_details().mix_denom.base.as_str();
let gateway = nym_mixnet_contract_common::Gateway {
host: args.host,
mix_port: args.mix_port.unwrap_or(DEFAULT_MIX_LISTENING_PORT),
clients_port: args.clients_port.unwrap_or(DEFAULT_CLIENT_LISTENING_PORT),
location: args.location,
sphinx_key: args.sphinx_key,
identity_key: args.identity_key,
version: args.version,
};
let coin = Coin::new(args.amount, denom);
let nonce = match client.get_signing_nonce(client.address()).await {
Ok(nonce) => nonce,
Err(err) => {
eprint!(
"failed to query for the signing nonce of {}: {err}",
client.address()
);
return;
}
};
let address = account_id_to_cw_addr(client.address());
let proxy = if args.with_vesting_account {
Some(account_id_to_cw_addr(client.vesting_contract_address()))
} else {
None
};
let payload = construct_gateway_bonding_sign_payload(nonce, address, proxy, coin, gateway);
println!("{}", payload.to_base58_string().unwrap())
}
@@ -4,6 +4,7 @@
use clap::{Args, Subcommand};
pub mod bond_gateway;
pub mod gateway_bonding_sign_payload;
pub mod unbond_gateway;
pub mod vesting_bond_gateway;
pub mod vesting_unbond_gateway;
@@ -25,4 +26,6 @@ pub enum MixnetOperatorsGatewayCommands {
VestingBond(vesting_bond_gateway::Args),
/// Unbound from a gateway (when originally using locked tokens)
VestingUnbound(vesting_unbond_gateway::Args),
/// Create base58-encoded payload required for producing valid bonding signature.
CreateGatewayBondingSignPayload(gateway_bonding_sign_payload::Args),
}
@@ -4,6 +4,7 @@
use crate::context::SigningClient;
use clap::Parser;
use log::{info, warn};
use nym_contracts_common::signing::MessageSignature;
use nym_mixnet_contract_common::{Coin, Gateway};
use nym_network_defaults::{DEFAULT_CLIENT_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT};
use validator_client::nyxd::VestingSigningClient;
@@ -14,7 +15,7 @@ pub struct Args {
pub host: String,
#[clap(long)]
pub signature: String,
pub signature: MessageSignature,
#[clap(long)]
pub mix_port: Option<u16>,
@@ -23,7 +24,7 @@ pub struct Args {
pub clients_port: Option<u16>,
#[clap(long)]
pub location: Option<String>,
pub location: String,
#[clap(long)]
pub sphinx_key: String,
@@ -57,9 +58,7 @@ pub async fn vesting_bond_gateway(client: SigningClient, args: Args, denom: &str
host: args.host,
mix_port: args.mix_port.unwrap_or(DEFAULT_MIX_LISTENING_PORT),
clients_port: args.clients_port.unwrap_or(DEFAULT_CLIENT_LISTENING_PORT),
location: args
.location
.unwrap_or_else(|| "secret gateway location".to_owned()),
location: args.location,
sphinx_key: args.sphinx_key,
identity_key: args.identity_key,
version: args.version,
@@ -68,7 +67,7 @@ pub async fn vesting_bond_gateway(client: SigningClient, args: Args, denom: &str
let coin = Coin::new(args.amount, denom);
let res = client
.vesting_bond_gateway(gateway, &args.signature, coin.into(), None)
.vesting_bond_gateway(gateway, args.signature, coin.into(), None)
.await
.expect("failed to bond gateway!");
@@ -5,6 +5,7 @@ use clap::Parser;
use cosmwasm_std::Uint128;
use log::{info, warn};
use nym_contracts_common::signing::MessageSignature;
use nym_mixnet_contract_common::{Coin, MixNodeCostParams, Percent};
use nym_network_defaults::{
DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT,
@@ -20,7 +21,7 @@ pub struct Args {
pub host: String,
#[clap(long)]
pub signature: String,
pub signature: MessageSignature,
#[clap(long)]
pub mix_port: Option<u16>,
@@ -0,0 +1,108 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use crate::utils::account_id_to_cw_addr;
use clap::Parser;
use cosmwasm_std::{Coin, Uint128};
use nym_contracts_common::Percent;
use nym_mixnet_contract_common::{construct_mixnode_bonding_sign_payload, MixNodeCostParams};
use nym_network_defaults::{
DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT,
};
use validator_client::nyxd::traits::MixnetQueryClient;
use validator_client::nyxd::CosmWasmCoin;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
pub host: 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 sphinx_key: String,
#[clap(long)]
pub identity_key: String,
#[clap(long)]
pub version: String,
#[clap(long)]
pub profit_margin_percent: Option<u8>,
#[clap(
long,
help = "operating cost in current DENOMINATION (so it would be 'unym', rather than 'nym')"
)]
pub interval_operating_cost: Option<u128>,
#[clap(
long,
help = "bonding amount in current DENOMINATION (so it would be 'unym', rather than 'nym')"
)]
pub amount: u128,
/// Indicates whether the mixnode is going to get bonded via a vesting account
#[arg(long)]
pub with_vesting_account: bool,
}
pub async fn create_payload(args: Args, client: SigningClient) {
let denom = client.current_chain_details().mix_denom.base.as_str();
let mixnode = nym_mixnet_contract_common::MixNode {
host: args.host,
mix_port: args.mix_port.unwrap_or(DEFAULT_MIX_LISTENING_PORT),
verloc_port: args.verloc_port.unwrap_or(DEFAULT_VERLOC_LISTENING_PORT),
http_api_port: args
.http_api_port
.unwrap_or(DEFAULT_HTTP_API_LISTENING_PORT),
sphinx_key: args.sphinx_key,
identity_key: args.identity_key,
version: args.version,
};
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 nonce = match client.get_signing_nonce(client.address()).await {
Ok(nonce) => nonce,
Err(err) => {
eprint!(
"failed to query for the signing nonce of {}: {err}",
client.address()
);
return;
}
};
let address = account_id_to_cw_addr(client.address());
let proxy = if args.with_vesting_account {
Some(account_id_to_cw_addr(client.vesting_contract_address()))
} else {
None
};
let payload =
construct_mixnode_bonding_sign_payload(nonce, address, proxy, coin, mixnode, cost_params);
println!("{}", payload.to_base58_string().unwrap())
}
@@ -5,6 +5,7 @@ use clap::{Args, Subcommand};
pub mod bond_mixnode;
pub mod keys;
pub mod mixnode_bonding_sign_payload;
pub mod rewards;
pub mod settings;
pub mod unbond_mixnode;
@@ -34,4 +35,6 @@ pub enum MixnetOperatorsMixnodeCommands {
BondVesting(vesting_bond_mixnode::Args),
/// Unbound from a mixnode (when originally using locked tokens)
UnboundVesting(vesting_unbond_mixnode::Args),
/// Create base58-encoded payload required for producing valid bonding signature.
CreateMixnodeBondingSignPayload(mixnode_bonding_sign_payload::Args),
}

Some files were not shown because too many files have changed in this diff Show More