Compare commits
48 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 81e31c0550 | |||
| 1c771df941 | |||
| 17c2aecd99 | |||
| 85074f3ac8 | |||
| c7020da81c | |||
| cfef9e96e6 | |||
| 018e4d6079 | |||
| ebadd9799f | |||
| 268fa02b83 | |||
| 4d6d2f0d33 | |||
| c7f8b05604 | |||
| 2878e9be9d | |||
| 7b419c2b12 | |||
| 0049126a91 | |||
| 80c5194d8b | |||
| 27a6b99453 | |||
| 61982de511 | |||
| efd9883197 | |||
| ce4ae8d90c | |||
| 1d2722f994 | |||
| 7e109e7f2d | |||
| f7a0b305df | |||
| 746ec71a0d | |||
| 41a63a0985 | |||
| 5a149c5492 | |||
| cdfa5ee540 | |||
| 71853f69f3 | |||
| bedff1f258 | |||
| 71a10a9a8b | |||
| 605aed6f20 | |||
| 5aee4b1660 | |||
| 68a7bb67de | |||
| 31e93428cf | |||
| 5f56b3eeea | |||
| e69b05693a | |||
| 8ae2451340 | |||
| b3b3279345 | |||
| 94a451c79b | |||
| ec7b959028 | |||
| 7061beea6e | |||
| e408162e26 | |||
| 25ae3895cb | |||
| 60296f2a41 | |||
| 3b97844310 | |||
| 8e6d3c34e2 | |||
| b1fb8bb18c | |||
| 4f59678ded | |||
| 8a1d2af3cf |
@@ -80,7 +80,7 @@ jobs:
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Install wasm-opt
|
||||
run: cargo install wasm-opt
|
||||
run: cargo install --version 0.110.0 wasm-opt
|
||||
|
||||
- name: Build release contracts
|
||||
run: make wasm
|
||||
@@ -99,9 +99,14 @@ jobs:
|
||||
cp target/release/nym-network-statistics $OUTPUT_DIR
|
||||
cp target/release/nym-cli $OUTPUT_DIR
|
||||
cp target/release/credential $OUTPUT_DIR
|
||||
cp target/release/explorer-api $OUTPUT_DIR
|
||||
|
||||
cp contracts/target/wasm32-unknown-unknown/release/mixnet_contract.wasm $OUTPUT_DIR
|
||||
cp contracts/target/wasm32-unknown-unknown/release/vesting_contract.wasm $OUTPUT_DIR
|
||||
cp contracts/target/wasm32-unknown-unknown/release/nym_coconut_bandwidth.wasm $OUTPUT_DIR
|
||||
cp contracts/target/wasm32-unknown-unknown/release/nym_coconut_dkg.wasm $OUTPUT_DIR
|
||||
cp contracts/target/wasm32-unknown-unknown/release/cw3_flex_multisig.wasm $OUTPUT_DIR
|
||||
cp contracts/target/wasm32-unknown-unknown/release/cw4_group.wasm $OUTPUT_DIR
|
||||
|
||||
- name: Deploy branch to CI www
|
||||
continue-on-error: true
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Install wasm-opt
|
||||
run: cargo install wasm-opt
|
||||
run: cargo install --version 0.110.0 wasm-opt
|
||||
|
||||
- name: Build release contracts
|
||||
run: make wasm
|
||||
|
||||
@@ -39,7 +39,5 @@ validator-api-config.toml
|
||||
dist
|
||||
storybook-static
|
||||
envs/qwerty.env
|
||||
Cargo.lock
|
||||
nym-connect/Cargo.lock
|
||||
.parcel-cache
|
||||
**/.DS_Store
|
||||
|
||||
@@ -4,6 +4,30 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
- nym-network-statistics properly handles signals ([#3209])
|
||||
|
||||
[#3209]: https://github.com/nymtech/nym/issues/3209
|
||||
|
||||
## [v1.1.13] (2023-03-15)
|
||||
|
||||
- NE - instead of throwing a "Mixnode/Gateway not found" error for blacklisted nodes due to bad performance, show their history but tag them as "Having poor performance" ([#2979])
|
||||
- NE - Upgrade Sandbox and make below changes: ([#2332])
|
||||
- Explorer - Updates ([#3168])
|
||||
- Fix contracts and nym-api audit findings ([#3026])
|
||||
- Website v2 - deploy infrastructure for strapi and CI ([#2213])
|
||||
- add blockstream green to sp list ([#3180])
|
||||
- mock-nym-api: fix .storybook lint error ([#3178])
|
||||
- Validating new interval config parameters to prevent division by zero ([#3153])
|
||||
|
||||
[#2979]: https://github.com/nymtech/nym/issues/2979
|
||||
[#2332]: https://github.com/nymtech/nym/issues/2332
|
||||
[#3168]: https://github.com/nymtech/nym/issues/3168
|
||||
[#3026]: https://github.com/nymtech/nym/issues/3026
|
||||
[#2213]: https://github.com/nymtech/nym/issues/2213
|
||||
[#3180]: https://github.com/nymtech/nym/pull/3180
|
||||
[#3178]: https://github.com/nymtech/nym/pull/3178
|
||||
[#3153]: https://github.com/nymtech/nym/pull/3153
|
||||
|
||||
## [v1.1.12] (2023-03-07)
|
||||
|
||||
- Fix generated docs for mixnet and vesting contract on docs.rs ([#3093])
|
||||
|
||||
Generated
+399
-442
File diff suppressed because it is too large
Load Diff
+10
@@ -107,6 +107,16 @@ license = "Apache-2.0"
|
||||
async-trait = "0.1.64"
|
||||
bip39 = { version = "2.0.0", features = ["zeroize"] }
|
||||
cfg-if = "1.0.0"
|
||||
cosmwasm-derive = "=1.0.0"
|
||||
cosmwasm-schema = "=1.0.0"
|
||||
cosmwasm-std = "=1.0.0"
|
||||
cosmwasm-storage = "=1.0.0"
|
||||
cw-utils = "=0.13.4"
|
||||
cw-storage-plus = "=0.13.4"
|
||||
cw2 = { version = "=0.13.4" }
|
||||
cw3 = { version = "=0.13.4" }
|
||||
cw3-fixed-multisig = { version = "=0.13.4" }
|
||||
cw4 = { version = "=0.13.4" }
|
||||
dotenvy = "0.15.6"
|
||||
lazy_static = "1.4.0"
|
||||
log = "0.4"
|
||||
|
||||
@@ -1,143 +1,90 @@
|
||||
# Default target
|
||||
all: test
|
||||
|
||||
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
|
||||
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-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
|
||||
# -----------------------------------------------------------------------------
|
||||
# Define targets for a given workspace
|
||||
# $(1): name
|
||||
# $(2): path to workspace
|
||||
# $(3): extra arguments to cargo
|
||||
# -----------------------------------------------------------------------------
|
||||
define add_cargo_workspace
|
||||
|
||||
clippy-happy-contracts:
|
||||
cargo clippy --manifest-path contracts/Cargo.toml --target wasm32-unknown-unknown
|
||||
clippy-happy-$(1):
|
||||
cargo clippy --manifest-path $(2)/Cargo.toml $(3)
|
||||
|
||||
clippy-happy-wallet:
|
||||
cargo clippy --manifest-path nym-wallet/Cargo.toml
|
||||
clippy-$(1):
|
||||
cargo clippy --manifest-path $(2)/Cargo.toml --workspace $(3) -- -D warnings
|
||||
|
||||
clippy-happy-connect:
|
||||
cargo clippy --manifest-path nym-connect/desktop/Cargo.toml
|
||||
clippy-$(1)-examples:
|
||||
cargo clippy --manifest-path $(2)/Cargo.toml --workspace --examples -- -D warnings
|
||||
|
||||
clippy-happy-connect-mobile:
|
||||
cargo clippy --manifest-path nym-connect/mobile/src-tauri/Cargo.toml
|
||||
test-$(1):
|
||||
cargo test --manifest-path $(2)/Cargo.toml --workspace
|
||||
|
||||
clippy-main:
|
||||
cargo clippy --workspace -- -D warnings
|
||||
test-$(1)-expensive:
|
||||
cargo test --manifest-path $(2)/Cargo.toml --workspace -- --ignored
|
||||
|
||||
clippy-main-examples:
|
||||
cargo clippy --workspace --examples -- -D warnings
|
||||
build-$(1):
|
||||
cargo build --manifest-path $(2)/Cargo.toml --workspace $(3)
|
||||
|
||||
clippy-wasm:
|
||||
cargo clippy --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown --workspace -- -D warnings
|
||||
build-$(1)-examples:
|
||||
cargo build --manifest-path $(2)/Cargo.toml --workspace --examples
|
||||
|
||||
fmt-$(1):
|
||||
cargo fmt --manifest-path $(2)/Cargo.toml --all
|
||||
|
||||
clippy-all-contracts:
|
||||
cargo clippy --workspace --manifest-path contracts/Cargo.toml --all-features --target wasm32-unknown-unknown -- -D warnings
|
||||
clippy-happy: clippy-happy-$(1)
|
||||
clippy-all: clippy-$(1) clippy-$(1)-examples
|
||||
cargo-test: test-$(1)
|
||||
cargo-test-expensive: test-$(1)-expensive
|
||||
build: build-$(1) build-$(1)-examples
|
||||
fmt: fmt-$(1)
|
||||
|
||||
clippy-all-wallet:
|
||||
cargo clippy --workspace --manifest-path nym-wallet/Cargo.toml --all-features -- -D warnings
|
||||
endef
|
||||
|
||||
clippy-all-connect:
|
||||
cargo clippy --workspace --manifest-path nym-connect/desktop/Cargo.toml --all-features -- -D warnings
|
||||
# -----------------------------------------------------------------------------
|
||||
# Rust workspaces
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
clippy-all-connect-mobile:
|
||||
cargo clippy --workspace --manifest-path nym-connect/mobile/src-tauri/Cargo.toml --all-features -- -D warnings
|
||||
# Generate targets for the various cargo workspaces
|
||||
|
||||
clippy-all-wasm-client:
|
||||
cargo clippy --workspace --manifest-path clients/webassembly/Cargo.toml --all-features --target wasm32-unknown-unknown -- -D warnings
|
||||
$(eval $(call add_cargo_workspace,main,.))
|
||||
$(eval $(call add_cargo_workspace,contracts,contracts,--target wasm32-unknown-unknown))
|
||||
$(eval $(call add_cargo_workspace,wasm-client,clients/webassembly,--target wasm32-unknown-unknown))
|
||||
$(eval $(call add_cargo_workspace,wallet,nym-wallet,))
|
||||
$(eval $(call add_cargo_workspace,connect,nym-connect/desktop))
|
||||
ifndef NYM_NO_MOBILE
|
||||
$(eval $(call add_cargo_workspace,connect-mobile,nym-connect/mobile/src-tauri))
|
||||
endif
|
||||
|
||||
test-main:
|
||||
cargo test --workspace
|
||||
|
||||
test-main-expensive:
|
||||
cargo test --workspace -- --ignored
|
||||
|
||||
test-contracts:
|
||||
cargo test --manifest-path contracts/Cargo.toml --all-features
|
||||
|
||||
test-contracts-expensive:
|
||||
cargo test --manifest-path contracts/Cargo.toml --all-features -- --ignored
|
||||
|
||||
test-wallet:
|
||||
cargo test --manifest-path nym-wallet/Cargo.toml --all-features
|
||||
|
||||
test-wallet-expensive:
|
||||
cargo test --manifest-path nym-wallet/Cargo.toml --all-features -- --ignored
|
||||
|
||||
test-connect:
|
||||
cargo test --manifest-path nym-connect/desktop/Cargo.toml --all-features
|
||||
|
||||
test-connect-expensive:
|
||||
cargo test --manifest-path nym-connect/desktop/Cargo.toml --all-features -- --ignored
|
||||
|
||||
test-connect-mobile:
|
||||
cargo test --manifest-path nym-connect/mobile/src-tauri/Cargo.toml --all-features
|
||||
|
||||
test-connect-mobile-expensive:
|
||||
cargo test --manifest-path nym-connect/mobile/src-tauri/Cargo.toml --all-features -- --ignored
|
||||
|
||||
build-main:
|
||||
cargo build --workspace
|
||||
|
||||
build-main-examples:
|
||||
cargo build --workspace --examples
|
||||
|
||||
build-contracts:
|
||||
cargo build --manifest-path contracts/Cargo.toml --workspace
|
||||
|
||||
build-wallet:
|
||||
cargo build --manifest-path nym-wallet/Cargo.toml --workspace
|
||||
|
||||
build-connect:
|
||||
cargo build --manifest-path nym-connect/desktop/Cargo.toml --workspace
|
||||
|
||||
build-connect-mobile:
|
||||
cargo build --manifest-path nym-connect/mobile/src-tauri/Cargo.toml --workspace
|
||||
# -----------------------------------------------------------------------------
|
||||
# Convenience targets for crates that are already part of the main workspace
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
build-explorer-api:
|
||||
cargo build --manifest-path explorer-api/Cargo.toml --workspace
|
||||
|
||||
build-wasm-client:
|
||||
cargo build --manifest-path clients/webassembly/Cargo.toml --workspace --target wasm32-unknown-unknown
|
||||
cargo build -p explorer-api
|
||||
|
||||
build-nym-cli:
|
||||
cargo build --release --manifest-path tools/nym-cli/Cargo.toml
|
||||
cargo build -p nym-cli --release
|
||||
|
||||
fmt-main:
|
||||
cargo fmt --all
|
||||
|
||||
fmt-contracts:
|
||||
cargo fmt --manifest-path contracts/Cargo.toml --all
|
||||
|
||||
fmt-wallet:
|
||||
cargo fmt --manifest-path nym-wallet/Cargo.toml --all
|
||||
|
||||
fmt-connect:
|
||||
cargo fmt --manifest-path nym-connect/desktop/Cargo.toml --all
|
||||
|
||||
fmt-connect-mobile:
|
||||
cargo fmt --manifest-path nym-connect/mobile/src-tauri/Cargo.toml --all
|
||||
|
||||
fmt-wasm-client:
|
||||
cargo fmt --manifest-path clients/webassembly/Cargo.toml --all
|
||||
# -----------------------------------------------------------------------------
|
||||
# Misc
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
wasm:
|
||||
RUSTFLAGS='-C link-arg=-s' cargo build --manifest-path contracts/Cargo.toml --release --target wasm32-unknown-unknown
|
||||
wasm-opt -Os contracts/target/wasm32-unknown-unknown/release/vesting_contract.wasm -o contracts/target/wasm32-unknown-unknown/release/vesting_contract.wasm
|
||||
wasm-opt -Os contracts/target/wasm32-unknown-unknown/release/mixnet_contract.wasm -o contracts/target/wasm32-unknown-unknown/release/mixnet_contract.wasm
|
||||
|
||||
# NOTE: this seems deprecated an not needed anymore?
|
||||
mixnet-opt: wasm
|
||||
cd contracts/mixnet && make opt
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "client-core"
|
||||
version = "1.1.12"
|
||||
version = "1.1.13"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.66"
|
||||
|
||||
@@ -35,7 +35,7 @@ use nym_crypto::asymmetric::{encryption, identity};
|
||||
use nym_sphinx::acknowledgements::AckKey;
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
use nym_sphinx::addressing::nodes::NodeIdentity;
|
||||
use nym_sphinx::receiver::ReconstructedMessage;
|
||||
use nym_sphinx::receiver::{ReconstructedMessage, SphinxMessageReceiver};
|
||||
use nym_task::connections::{ConnectionCommandReceiver, ConnectionCommandSender, LaneQueueLengths};
|
||||
use nym_task::{TaskClient, TaskManager};
|
||||
use nym_topology::provider_trait::TopologyProvider;
|
||||
@@ -294,14 +294,15 @@ where
|
||||
shutdown: TaskClient,
|
||||
) {
|
||||
info!("Starting received messages buffer controller...");
|
||||
ReceivedMessagesBufferController::new(
|
||||
local_encryption_keypair,
|
||||
query_receiver,
|
||||
mixnet_receiver,
|
||||
reply_key_storage,
|
||||
reply_controller_sender,
|
||||
)
|
||||
.start_with_shutdown(shutdown)
|
||||
let controller: ReceivedMessagesBufferController<SphinxMessageReceiver> =
|
||||
ReceivedMessagesBufferController::new(
|
||||
local_encryption_keypair,
|
||||
query_receiver,
|
||||
mixnet_receiver,
|
||||
reply_key_storage,
|
||||
reply_controller_sender,
|
||||
);
|
||||
controller.start_with_shutdown(shutdown)
|
||||
}
|
||||
|
||||
async fn start_gateway_client(
|
||||
|
||||
@@ -30,13 +30,13 @@ pub type ReceivedBufferRequestReceiver = mpsc::UnboundedReceiver<ReceivedBufferM
|
||||
pub type ReconstructedMessagesSender = mpsc::UnboundedSender<Vec<ReconstructedMessage>>;
|
||||
pub type ReconstructedMessagesReceiver = mpsc::UnboundedReceiver<Vec<ReconstructedMessage>>;
|
||||
|
||||
struct ReceivedMessagesBufferInner {
|
||||
struct ReceivedMessagesBufferInner<R: MessageReceiver> {
|
||||
messages: Vec<ReconstructedMessage>,
|
||||
local_encryption_keypair: Arc<encryption::KeyPair>,
|
||||
|
||||
// TODO: looking how it 'looks' here, perhaps `MessageReceiver` should be renamed to something
|
||||
// else instead.
|
||||
message_receiver: MessageReceiver,
|
||||
message_receiver: R,
|
||||
message_sender: Option<ReconstructedMessagesSender>,
|
||||
|
||||
// TODO: this will get cleared upon re-running the client
|
||||
@@ -45,7 +45,7 @@ struct ReceivedMessagesBufferInner {
|
||||
recently_reconstructed: HashSet<i32>,
|
||||
}
|
||||
|
||||
impl ReceivedMessagesBufferInner {
|
||||
impl<R: MessageReceiver> ReceivedMessagesBufferInner<R> {
|
||||
fn recover_from_fragment(&mut self, fragment_data: &[u8]) -> Option<NymMessage> {
|
||||
if nym_sphinx::cover::is_cover(fragment_data) {
|
||||
trace!("The message was a loop cover message! Skipping it");
|
||||
@@ -102,13 +102,13 @@ impl ReceivedMessagesBufferInner {
|
||||
&mut self,
|
||||
reply_ciphertext: &mut [u8],
|
||||
reply_key: SurbEncryptionKey,
|
||||
) -> Option<NymMessage> {
|
||||
) -> Result<Option<NymMessage>, MessageRecoveryError> {
|
||||
// note: this performs decryption IN PLACE without extra allocation
|
||||
self.message_receiver
|
||||
.recover_plaintext_from_reply(reply_ciphertext, reply_key);
|
||||
.recover_plaintext_from_reply(reply_ciphertext, reply_key)?;
|
||||
let fragment_data = reply_ciphertext;
|
||||
|
||||
self.recover_from_fragment(fragment_data)
|
||||
Ok(self.recover_from_fragment(fragment_data))
|
||||
}
|
||||
|
||||
fn process_received_regular_packet(&mut self, mut raw_fragment: Vec<u8>) -> Option<NymMessage> {
|
||||
@@ -130,13 +130,13 @@ impl ReceivedMessagesBufferInner {
|
||||
#[derive(Debug, Clone)]
|
||||
// Note: you should NEVER create more than a single instance of this using 'new()'.
|
||||
// You should always use .clone() to create additional instances
|
||||
struct ReceivedMessagesBuffer {
|
||||
inner: Arc<Mutex<ReceivedMessagesBufferInner>>,
|
||||
struct ReceivedMessagesBuffer<R: MessageReceiver> {
|
||||
inner: Arc<Mutex<ReceivedMessagesBufferInner<R>>>,
|
||||
reply_key_storage: SentReplyKeys,
|
||||
reply_controller_sender: ReplyControllerSender,
|
||||
}
|
||||
|
||||
impl ReceivedMessagesBuffer {
|
||||
impl<R: MessageReceiver> ReceivedMessagesBuffer<R> {
|
||||
fn new(
|
||||
local_encryption_keypair: Arc<encryption::KeyPair>,
|
||||
reply_key_storage: SentReplyKeys,
|
||||
@@ -146,7 +146,7 @@ impl ReceivedMessagesBuffer {
|
||||
inner: Arc::new(Mutex::new(ReceivedMessagesBufferInner {
|
||||
messages: Vec::new(),
|
||||
local_encryption_keypair,
|
||||
message_receiver: MessageReceiver::new(),
|
||||
message_receiver: R::new(),
|
||||
message_sender: None,
|
||||
recently_reconstructed: HashSet::new(),
|
||||
})),
|
||||
@@ -328,7 +328,10 @@ impl ReceivedMessagesBuffer {
|
||||
})
|
||||
}
|
||||
|
||||
async fn handle_new_received(&mut self, msgs: Vec<Vec<u8>>) {
|
||||
async fn handle_new_received(
|
||||
&mut self,
|
||||
msgs: Vec<Vec<u8>>,
|
||||
) -> Result<(), MessageRecoveryError> {
|
||||
trace!(
|
||||
"Processing {:?} new message that might get added to the buffer!",
|
||||
msgs.len()
|
||||
@@ -344,7 +347,7 @@ impl ReceivedMessagesBuffer {
|
||||
// if yes - this is a reply message
|
||||
let completed_message =
|
||||
if let Some((reply_key, reply_message)) = self.get_reply_key(&mut msg) {
|
||||
inner_guard.process_received_reply(reply_message, reply_key)
|
||||
inner_guard.process_received_reply(reply_message, reply_key)?
|
||||
} else {
|
||||
inner_guard.process_received_regular_packet(msg)
|
||||
};
|
||||
@@ -360,6 +363,7 @@ impl ReceivedMessagesBuffer {
|
||||
if !completed_messages.is_empty() {
|
||||
self.handle_reconstructed_messages(completed_messages).await
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -372,14 +376,14 @@ pub enum ReceivedBufferMessage {
|
||||
ReceiverDisconnect,
|
||||
}
|
||||
|
||||
struct RequestReceiver {
|
||||
received_buffer: ReceivedMessagesBuffer,
|
||||
struct RequestReceiver<R: MessageReceiver> {
|
||||
received_buffer: ReceivedMessagesBuffer<R>,
|
||||
query_receiver: ReceivedBufferRequestReceiver,
|
||||
}
|
||||
|
||||
impl RequestReceiver {
|
||||
impl<R: MessageReceiver> RequestReceiver<R> {
|
||||
fn new(
|
||||
received_buffer: ReceivedMessagesBuffer,
|
||||
received_buffer: ReceivedMessagesBuffer<R>,
|
||||
query_receiver: ReceivedBufferRequestReceiver,
|
||||
) -> Self {
|
||||
RequestReceiver {
|
||||
@@ -422,14 +426,14 @@ impl RequestReceiver {
|
||||
}
|
||||
}
|
||||
|
||||
struct FragmentedMessageReceiver {
|
||||
received_buffer: ReceivedMessagesBuffer,
|
||||
struct FragmentedMessageReceiver<R: MessageReceiver> {
|
||||
received_buffer: ReceivedMessagesBuffer<R>,
|
||||
mixnet_packet_receiver: MixnetMessageReceiver,
|
||||
}
|
||||
|
||||
impl FragmentedMessageReceiver {
|
||||
impl<R: MessageReceiver> FragmentedMessageReceiver<R> {
|
||||
fn new(
|
||||
received_buffer: ReceivedMessagesBuffer,
|
||||
received_buffer: ReceivedMessagesBuffer<R>,
|
||||
mixnet_packet_receiver: MixnetMessageReceiver,
|
||||
) -> Self {
|
||||
FragmentedMessageReceiver {
|
||||
@@ -438,13 +442,16 @@ impl FragmentedMessageReceiver {
|
||||
}
|
||||
}
|
||||
|
||||
async fn run_with_shutdown(&mut self, mut shutdown: nym_task::TaskClient) {
|
||||
async fn run_with_shutdown(
|
||||
&mut self,
|
||||
mut shutdown: nym_task::TaskClient,
|
||||
) -> Result<(), MessageRecoveryError> {
|
||||
debug!("Started FragmentedMessageReceiver with graceful shutdown support");
|
||||
while !shutdown.is_shutdown() {
|
||||
tokio::select! {
|
||||
new_messages = self.mixnet_packet_receiver.next() => {
|
||||
if let Some(new_messages) = new_messages {
|
||||
self.received_buffer.handle_new_received(new_messages).await;
|
||||
self.received_buffer.handle_new_received(new_messages).await?;
|
||||
} else {
|
||||
log::trace!("FragmentedMessageReceiver: Stopping since channel closed");
|
||||
break;
|
||||
@@ -457,15 +464,16 @@ impl FragmentedMessageReceiver {
|
||||
}
|
||||
shutdown.recv_timeout().await;
|
||||
log::debug!("FragmentedMessageReceiver: Exiting");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct ReceivedMessagesBufferController {
|
||||
fragmented_message_receiver: FragmentedMessageReceiver,
|
||||
request_receiver: RequestReceiver,
|
||||
pub(crate) struct ReceivedMessagesBufferController<R: MessageReceiver> {
|
||||
fragmented_message_receiver: FragmentedMessageReceiver<R>,
|
||||
request_receiver: RequestReceiver<R>,
|
||||
}
|
||||
|
||||
impl ReceivedMessagesBufferController {
|
||||
impl<R: MessageReceiver + Clone + Send + 'static> ReceivedMessagesBufferController<R> {
|
||||
pub(crate) fn new(
|
||||
local_encryption_keypair: Arc<encryption::KeyPair>,
|
||||
query_receiver: ReceivedBufferRequestReceiver,
|
||||
@@ -494,9 +502,13 @@ impl ReceivedMessagesBufferController {
|
||||
|
||||
let shutdown_handle = shutdown.clone();
|
||||
spawn_future(async move {
|
||||
fragmented_message_receiver
|
||||
match fragmented_message_receiver
|
||||
.run_with_shutdown(shutdown_handle)
|
||||
.await;
|
||||
.await
|
||||
{
|
||||
Ok(_) => {}
|
||||
Err(e) => error!("{e}"),
|
||||
}
|
||||
});
|
||||
spawn_future(async move {
|
||||
request_receiver.run_with_shutdown(shutdown).await;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-client"
|
||||
version = "1.1.12"
|
||||
version = "1.1.13"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
description = "Implementation of the Nym Client"
|
||||
edition = "2021"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.12"
|
||||
version = "1.1.13"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
|
||||
edition = "2021"
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
MixNodeDetails,
|
||||
MixNodeRewarding,
|
||||
MixOwnershipResponse,
|
||||
OriginalVestingResponse,
|
||||
PagedAllDelegationsResponse,
|
||||
PagedDelegatorDelegationsResponse,
|
||||
PagedGatewayResponse,
|
||||
@@ -32,10 +33,11 @@ import {
|
||||
RewardingParams,
|
||||
StakeSaturationResponse,
|
||||
UnbondedMixnodeResponse,
|
||||
VestingAccountInfo,
|
||||
ContractState, VestingAccountsCoinPaged, VestingAccountsPaged, DelegationTimes, Delegations, Period, VestingAccountNode
|
||||
} from '@nymproject/types';
|
||||
import QueryClient from './query-client';
|
||||
import SigningClient, { ISigningClient } from './signing-client';
|
||||
import { ContractState } from './types/shared';
|
||||
|
||||
export interface INymClient {
|
||||
readonly mixnetContract: string;
|
||||
@@ -204,7 +206,7 @@ export default class ValidatorClient implements INymClient {
|
||||
let mixNodes: UnbondedMixnodeResponse[] = [];
|
||||
const limit = 50;
|
||||
let startAfter;
|
||||
for (;;) {
|
||||
for (; ;) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const pagedResponse: PagedUnbondedMixnodesResponse = await this.client.getUnbondedMixNodes(
|
||||
this.mixnetContract,
|
||||
@@ -227,7 +229,7 @@ export default class ValidatorClient implements INymClient {
|
||||
let mixNodes: MixNodeBond[] = [];
|
||||
const limit = 50;
|
||||
let startAfter;
|
||||
for (;;) {
|
||||
for (; ;) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const pagedResponse: PagedMixNodeBondResponse = await this.client.getMixNodeBonds(
|
||||
this.mixnetContract,
|
||||
@@ -249,7 +251,7 @@ export default class ValidatorClient implements INymClient {
|
||||
let mixNodes: MixNodeDetails[] = [];
|
||||
const limit = 50;
|
||||
let startAfter;
|
||||
for (;;) {
|
||||
for (; ;) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const pagedResponse: PagedMixNodeDetailsResponse = await this.client.getMixNodesDetailed(
|
||||
this.mixnetContract,
|
||||
@@ -281,7 +283,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: PagedMixDelegationsResponse = await this.client.getMixNodeDelegationsPaged(
|
||||
this.mixnetContract,
|
||||
@@ -304,7 +306,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,
|
||||
@@ -327,7 +329,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: PagedAllDelegationsResponse = await this.client.getAllDelegationsPaged(
|
||||
this.mixnetContract,
|
||||
@@ -514,4 +516,97 @@ export default class ValidatorClient implements INymClient {
|
||||
this.assertSigning();
|
||||
return (this.client as ISigningClient).updateContractStateParams(this.mixnetContract, newParams, fee, memo);
|
||||
}
|
||||
|
||||
|
||||
// VESTING
|
||||
// TODO - MOVE TO A DIFFERENT FILE
|
||||
|
||||
|
||||
public async getVestingAccountsPaged(): Promise<VestingAccountsPaged> {
|
||||
return this.client.getVestingAccountsPaged(this.vestingContract);
|
||||
}
|
||||
|
||||
public async getVestingAmountsAccountsPaged(): Promise<VestingAccountsCoinPaged> {
|
||||
return this.client.getVestingAmountsAccountsPaged(this.vestingContract);
|
||||
}
|
||||
|
||||
public async getLockedTokens(vestingAccountAddress: string): Promise<Coin> {
|
||||
return this.client.getLockedTokens(this.vestingContract, vestingAccountAddress);
|
||||
}
|
||||
|
||||
public async getSpendableTokens(vestingAccountAddress: string): Promise<Coin> {
|
||||
return this.client.getSpendableTokens(this.vestingContract, vestingAccountAddress);
|
||||
}
|
||||
|
||||
public async getVestedTokens(vestingAccountAddress: string): Promise<Coin> {
|
||||
return this.client.getVestedTokens(this.vestingContract, vestingAccountAddress);
|
||||
}
|
||||
|
||||
public async getVestingTokens(vestingAccountAddress: string): Promise<Coin> {
|
||||
return this.client.getVestingTokens(this.vestingContract, vestingAccountAddress);
|
||||
}
|
||||
|
||||
public async getSpendableVestedTokens(vestingAccountAddress: string): Promise<Coin> {
|
||||
return this.client.getSpendableVestedTokens(this.vestingContract, vestingAccountAddress);
|
||||
}
|
||||
|
||||
public async getSpendableRewards(vestingAccountAddress: string): Promise<Coin> {
|
||||
return this.client.getSpendableRewards(this.vestingContract, vestingAccountAddress);
|
||||
}
|
||||
|
||||
public async getDelegatedCoins(vestingAccountAddress: string): Promise<Coin> {
|
||||
return this.client.getDelegatedCoins(this.vestingContract, vestingAccountAddress);
|
||||
}
|
||||
|
||||
public async getPledgedCoins(vestingAccountAddress: string): Promise<Coin> {
|
||||
return this.client.getPledgedCoins(this.vestingContract, vestingAccountAddress);
|
||||
}
|
||||
|
||||
public async getStakedCoins(vestingAccountAddress: string): Promise<Coin> {
|
||||
return this.client.getStakedCoins(this.vestingContract, vestingAccountAddress);
|
||||
}
|
||||
|
||||
public async getWithdrawnCoins(vestingAccountAddress: string): Promise<Coin> {
|
||||
return this.client.getWithdrawnCoins(this.vestingContract, vestingAccountAddress);
|
||||
}
|
||||
|
||||
public async getStartTime(vestingAccountAddress: string): Promise<string> {
|
||||
return this.client.getStartTime(this.vestingContract, vestingAccountAddress);
|
||||
}
|
||||
|
||||
public async getEndTime(vestingAccountAddress: string): Promise<string> {
|
||||
return this.client.getEndTime(this.vestingContract, vestingAccountAddress);
|
||||
}
|
||||
|
||||
public async getOriginalVestingDetails(vestingAccountAddress: string): Promise<OriginalVestingResponse> {
|
||||
return this.client.getOriginalVestingDetails(this.vestingContract, vestingAccountAddress);
|
||||
}
|
||||
|
||||
public async getHistoricStakingRewards(vestingAccountAddress: string): Promise<Coin> {
|
||||
return this.client.getHistoricStakingRewards(this.vestingContract, vestingAccountAddress);
|
||||
}
|
||||
|
||||
public async getAccountDetails(address: string): Promise<VestingAccountInfo> {
|
||||
return this.client.getAccountDetails(this.vestingContract, address);
|
||||
}
|
||||
|
||||
public async getMixnode(address: string): Promise<VestingAccountNode> {
|
||||
return this.client.getMixnode(this.vestingContract, address);
|
||||
}
|
||||
|
||||
public async getGateway(address: string): Promise<VestingAccountNode> {
|
||||
return this.client.getGateway(this.vestingContract, address);
|
||||
}
|
||||
|
||||
public async getDelegationTimes(mix_id: number, delegatorAddress: string): Promise<DelegationTimes> {
|
||||
return this.client.getDelegationTimes(this.vestingContract, mix_id, delegatorAddress);
|
||||
}
|
||||
|
||||
public async getAllDelegations(): Promise<Delegations> {
|
||||
return this.client.getAllDelegations(this.vestingContract);
|
||||
}
|
||||
|
||||
public async getCurrentVestingPeriod(address: string): Promise<Period> {
|
||||
return this.client.getCurrentVestingPeriod(this.vestingContract, address);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
*/
|
||||
// eslint-disable-next-line import/no-cycle
|
||||
import { INyxdQuery } from './query-client';
|
||||
import { Delegation, RewardingParams, StakeSaturationResponse } from '@nymproject/types';
|
||||
import {
|
||||
Delegation, OriginalVestingResponse, RewardingParams, StakeSaturationResponse, VestingAccountInfo,
|
||||
UnbondedMixnodeResponse,
|
||||
GatewayOwnershipResponse,
|
||||
MixnetContractVersion,
|
||||
@@ -18,8 +18,10 @@ import {
|
||||
PagedMixNodeDetailsResponse,
|
||||
PagedUnbondedMixnodesResponse,
|
||||
LayerDistribution,
|
||||
ContractState, VestingAccountsCoinPaged, VestingAccountsPaged, DelegationTimes, Delegations, Period, VestingAccountNode
|
||||
} from '@nymproject/types';
|
||||
import { ContractState, SmartContractQuery } from './types/shared';
|
||||
import { SmartContractQuery } from './types/shared';
|
||||
import { Coin } from 'cosmjs-types/cosmos/base/v1beta1/coin';
|
||||
|
||||
export default class NyxdQuerier implements INyxdQuery {
|
||||
client: SmartContractQuery;
|
||||
@@ -188,4 +190,136 @@ export default class NyxdQuerier implements INyxdQuery {
|
||||
vesting_account_address: vestingAccountAddress,
|
||||
});
|
||||
}
|
||||
|
||||
getVestingAccountsPaged(vestingContractAddress: string): Promise<VestingAccountsPaged> {
|
||||
return this.client.queryContractSmart(vestingContractAddress, {
|
||||
get_accounts_paged: {}
|
||||
});
|
||||
}
|
||||
|
||||
getVestingAmountsAccountsPaged(vestingContractAddress: string): Promise<VestingAccountsCoinPaged> {
|
||||
return this.client.queryContractSmart(vestingContractAddress, {
|
||||
get_accounts_vesting_coins_paged: {}
|
||||
});
|
||||
}
|
||||
|
||||
getLockedTokens(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
|
||||
return this.client.queryContractSmart(vestingContractAddress, {
|
||||
locked_coins: { vesting_account_address: vestingAccountAddress }
|
||||
});
|
||||
}
|
||||
|
||||
getSpendableTokens(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
|
||||
return this.client.queryContractSmart(vestingContractAddress, {
|
||||
spendable_coins: { vesting_account_address: vestingAccountAddress }
|
||||
});
|
||||
}
|
||||
|
||||
getVestedTokens(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
|
||||
return this.client.queryContractSmart(vestingContractAddress, {
|
||||
get_vested_coins: { vesting_account_address: vestingAccountAddress }
|
||||
});
|
||||
}
|
||||
|
||||
getVestingTokens(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
|
||||
return this.client.queryContractSmart(vestingContractAddress, {
|
||||
get_vesting_coins: { vesting_account_address: vestingAccountAddress }
|
||||
});
|
||||
}
|
||||
|
||||
getSpendableVestedTokens(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
|
||||
return this.client.queryContractSmart(vestingContractAddress, {
|
||||
get_spendable_vested_coins: { vesting_account_address: vestingAccountAddress }
|
||||
});
|
||||
}
|
||||
|
||||
getSpendableRewards(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
|
||||
return this.client.queryContractSmart(vestingContractAddress, {
|
||||
get_spendable_reward_coins: { vesting_account_address: vestingAccountAddress }
|
||||
});
|
||||
}
|
||||
|
||||
getDelegatedCoins(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
|
||||
return this.client.queryContractSmart(vestingContractAddress, {
|
||||
get_delegated_coins: { vesting_account_address: vestingAccountAddress }
|
||||
});
|
||||
}
|
||||
|
||||
getPledgedCoins(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
|
||||
return this.client.queryContractSmart(vestingContractAddress, {
|
||||
get_pledged_coins: { vesting_account_address: vestingAccountAddress }
|
||||
});
|
||||
}
|
||||
|
||||
getStakedCoins(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
|
||||
return this.client.queryContractSmart(vestingContractAddress, {
|
||||
get_staked_coins: { vesting_account_address: vestingAccountAddress }
|
||||
});
|
||||
}
|
||||
|
||||
getWithdrawnCoins(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
|
||||
return this.client.queryContractSmart(vestingContractAddress, {
|
||||
get_withdrawn_coins: { vesting_account_address: vestingAccountAddress }
|
||||
});
|
||||
}
|
||||
|
||||
getStartTime(vestingContractAddress: string, vestingAccountAddress: string): Promise<string> {
|
||||
return this.client.queryContractSmart(vestingContractAddress, {
|
||||
get_start_time: { vesting_account_address: vestingAccountAddress }
|
||||
});
|
||||
}
|
||||
|
||||
getEndTime(vestingContractAddress: string, vestingAccountAddress: string): Promise<string> {
|
||||
return this.client.queryContractSmart(vestingContractAddress, {
|
||||
get_end_time: { vesting_account_address: vestingAccountAddress }
|
||||
});
|
||||
}
|
||||
|
||||
getOriginalVestingDetails(vestingContractAddress: string, vestingAccountAddress: string): Promise<OriginalVestingResponse> {
|
||||
return this.client.queryContractSmart(vestingContractAddress, {
|
||||
get_original_vesting: { vesting_account_address: vestingAccountAddress }
|
||||
});
|
||||
}
|
||||
|
||||
getHistoricStakingRewards(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
|
||||
return this.client.queryContractSmart(vestingContractAddress, {
|
||||
get_historical_vesting_staking_reward: { vesting_account_address: vestingAccountAddress }
|
||||
});
|
||||
}
|
||||
|
||||
getAccountDetails(vestingContractAddress: string, address: string): Promise<VestingAccountInfo> {
|
||||
return this.client.queryContractSmart(vestingContractAddress, {
|
||||
get_account: { address: address }
|
||||
});
|
||||
}
|
||||
|
||||
getMixnode(vestingContractAddress: string, address: string): Promise<VestingAccountNode> {
|
||||
return this.client.queryContractSmart(vestingContractAddress, {
|
||||
get_mixnode: { address: address }
|
||||
});
|
||||
}
|
||||
|
||||
getGateway(vestingContractAddress: string, address: string): Promise<VestingAccountNode> {
|
||||
return this.client.queryContractSmart(vestingContractAddress, {
|
||||
get_gateway: { address: address }
|
||||
});
|
||||
}
|
||||
|
||||
getDelegationTimes(vestingContractAddress: string, mix_id: number, address: string): Promise<DelegationTimes> {
|
||||
return this.client.queryContractSmart(vestingContractAddress, {
|
||||
get_delegation_times: { mix_id: mix_id, address: address }
|
||||
});
|
||||
}
|
||||
|
||||
getAllDelegations(vestingContractAddress: string): Promise<Delegations> {
|
||||
return this.client.queryContractSmart(vestingContractAddress, {
|
||||
get_all_delegations: {}
|
||||
});
|
||||
}
|
||||
|
||||
getCurrentVestingPeriod(vestingContractAddress: string, address: string): Promise<Period> {
|
||||
return this.client.queryContractSmart(vestingContractAddress, {
|
||||
get_current_vesting_period: { address: address }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,11 +20,15 @@ import {
|
||||
UnbondedMixnodeResponse,
|
||||
MixNodeBond,
|
||||
MixNodeRewarding,
|
||||
OriginalVestingResponse,
|
||||
VestingAccountInfo,
|
||||
ContractState, VestingAccountsCoinPaged, VestingAccountsPaged, DelegationTimes, Delegations, Period, VestingAccountNode
|
||||
} from '@nymproject/types';
|
||||
import NymApiQuerier, { INymApiQuery } from './nym-api-querier';
|
||||
import { ContractState, ICosmWasmQuery } from './types/shared';
|
||||
import { ICosmWasmQuery } from './types/shared';
|
||||
import { RewardingParams } from '@nymproject/types';
|
||||
import { Tendermint34Client } from '@cosmjs/tendermint-rpc';
|
||||
import { Coin } from 'cosmjs-types/cosmos/base/v1beta1/coin';
|
||||
|
||||
export interface INyxdQuery {
|
||||
// nym-specific implemented inside NymQuerier
|
||||
@@ -68,7 +72,7 @@ export interface INyxdQuery {
|
||||
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;
|
||||
@@ -199,4 +203,92 @@ export default class QueryClient extends CosmWasmClient implements IQueryClient
|
||||
getSpendableCoins(vestingContractAddress: string, vestingAccountAddress: string): Promise<any> {
|
||||
return this.nyxdQuerier.getSpendableCoins(vestingContractAddress, vestingAccountAddress);
|
||||
}
|
||||
|
||||
getVestingAccountsPaged(vestingContractAddress: string): Promise<VestingAccountsPaged> {
|
||||
return this.nyxdQuerier.getVestingAccountsPaged(vestingContractAddress);
|
||||
}
|
||||
|
||||
getVestingAmountsAccountsPaged(vestingContractAddress: string): Promise<VestingAccountsCoinPaged> {
|
||||
return this.nyxdQuerier.getVestingAmountsAccountsPaged(vestingContractAddress);
|
||||
}
|
||||
|
||||
getLockedTokens(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
|
||||
return this.nyxdQuerier.getLockedTokens(vestingContractAddress, vestingAccountAddress);
|
||||
}
|
||||
|
||||
getSpendableTokens(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
|
||||
return this.nyxdQuerier.getSpendableTokens(vestingContractAddress, vestingAccountAddress);
|
||||
}
|
||||
|
||||
getVestedTokens(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
|
||||
return this.nyxdQuerier.getVestedTokens(vestingContractAddress, vestingAccountAddress);
|
||||
}
|
||||
|
||||
getVestingTokens(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
|
||||
return this.nyxdQuerier.getVestingTokens(vestingContractAddress, vestingAccountAddress);
|
||||
}
|
||||
|
||||
getSpendableVestedTokens(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
|
||||
return this.nyxdQuerier.getSpendableVestedTokens(vestingContractAddress, vestingAccountAddress);
|
||||
}
|
||||
|
||||
getSpendableRewards(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
|
||||
return this.nyxdQuerier.getSpendableRewards(vestingContractAddress, vestingAccountAddress);
|
||||
}
|
||||
|
||||
getDelegatedCoins(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
|
||||
return this.nyxdQuerier.getDelegatedCoins(vestingContractAddress, vestingAccountAddress);
|
||||
}
|
||||
|
||||
getPledgedCoins(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
|
||||
return this.nyxdQuerier.getPledgedCoins(vestingContractAddress, vestingAccountAddress);
|
||||
}
|
||||
|
||||
getStakedCoins(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
|
||||
return this.nyxdQuerier.getStakedCoins(vestingContractAddress, vestingAccountAddress);
|
||||
}
|
||||
|
||||
getWithdrawnCoins(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
|
||||
return this.nyxdQuerier.getWithdrawnCoins(vestingContractAddress, vestingAccountAddress);
|
||||
}
|
||||
|
||||
getStartTime(vestingContractAddress: string, vestingAccountAddress: string): Promise<string> {
|
||||
return this.nyxdQuerier.getStartTime(vestingContractAddress, vestingAccountAddress);
|
||||
}
|
||||
|
||||
getEndTime(vestingContractAddress: string, vestingAccountAddress: string): Promise<string> {
|
||||
return this.nyxdQuerier.getEndTime(vestingContractAddress, vestingAccountAddress);
|
||||
}
|
||||
|
||||
getOriginalVestingDetails(vestingContractAddress: string, vestingAccountAddress: string): Promise<OriginalVestingResponse> {
|
||||
return this.nyxdQuerier.getOriginalVestingDetails(vestingContractAddress, vestingAccountAddress);
|
||||
}
|
||||
|
||||
getHistoricStakingRewards(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
|
||||
return this.nyxdQuerier.getHistoricStakingRewards(vestingContractAddress, vestingAccountAddress);
|
||||
}
|
||||
|
||||
getAccountDetails(vestingContractAddress: string, address: string): Promise<VestingAccountInfo> {
|
||||
return this.nyxdQuerier.getAccountDetails(vestingContractAddress, address);
|
||||
}
|
||||
|
||||
getMixnode(vestingContractAddress: string, address: string): Promise<VestingAccountNode> {
|
||||
return this.nyxdQuerier.getMixnode(vestingContractAddress, address);
|
||||
}
|
||||
|
||||
getGateway(vestingContractAddress: string, address: string): Promise<VestingAccountNode> {
|
||||
return this.nyxdQuerier.getGateway(vestingContractAddress, address);
|
||||
}
|
||||
|
||||
getDelegationTimes(vestingContractAddress: string, mix_id: number, delegatorAddress: string): Promise<DelegationTimes> {
|
||||
return this.nyxdQuerier.getDelegationTimes(vestingContractAddress, mix_id, delegatorAddress);
|
||||
}
|
||||
|
||||
getAllDelegations(vestingContractAddress: string): Promise<Delegations> {
|
||||
return this.nyxdQuerier.getAllDelegations(vestingContractAddress);
|
||||
}
|
||||
|
||||
getCurrentVestingPeriod(vestingContractAddress: string, address: string): Promise<Period> {
|
||||
return this.nyxdQuerier.getCurrentVestingPeriod(vestingContractAddress, address);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import {
|
||||
MixNodeDetails,
|
||||
MixNodeRewarding,
|
||||
MixOwnershipResponse,
|
||||
OriginalVestingResponse,
|
||||
PagedAllDelegationsResponse,
|
||||
PagedDelegatorDelegationsResponse,
|
||||
PagedGatewayResponse,
|
||||
@@ -38,9 +39,10 @@ import {
|
||||
PagedUnbondedMixnodesResponse,
|
||||
RewardingParams,
|
||||
UnbondedMixnodeResponse,
|
||||
VestingAccountInfo,
|
||||
ContractState, VestingAccountsCoinPaged, VestingAccountsPaged, DelegationTimes, Delegations, Period, VestingAccountNode
|
||||
} from '@nymproject/types';
|
||||
import NymApiQuerier from './nym-api-querier';
|
||||
import { ContractState } from './types/shared';
|
||||
|
||||
// methods exposed by `SigningCosmWasmClient`
|
||||
export interface ICosmWasmSigning {
|
||||
@@ -243,7 +245,7 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
|
||||
// query related:
|
||||
|
||||
getContractVersion(mixnetContractAddress: string): Promise<MixnetContractVersion> {
|
||||
return this.getContractVersion(mixnetContractAddress);
|
||||
return this.nyxdQuerier.getContractVersion(mixnetContractAddress);
|
||||
}
|
||||
|
||||
getMixNodeBonds(
|
||||
@@ -508,4 +510,94 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
|
||||
memo,
|
||||
);
|
||||
}
|
||||
|
||||
// vesting related
|
||||
|
||||
getVestingAccountsPaged(vestingContractAddress: string): Promise<VestingAccountsPaged> {
|
||||
return this.nyxdQuerier.getVestingAccountsPaged(vestingContractAddress);
|
||||
};
|
||||
|
||||
getVestingAmountsAccountsPaged(vestingContractAddress: string): Promise<VestingAccountsCoinPaged> {
|
||||
return this.nyxdQuerier.getVestingAmountsAccountsPaged(vestingContractAddress);
|
||||
}
|
||||
|
||||
getLockedTokens(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
|
||||
return this.nyxdQuerier.getLockedTokens(vestingContractAddress, vestingAccountAddress);
|
||||
}
|
||||
|
||||
getSpendableTokens(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
|
||||
return this.nyxdQuerier.getSpendableTokens(vestingContractAddress, vestingAccountAddress);
|
||||
}
|
||||
|
||||
getVestedTokens(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
|
||||
return this.nyxdQuerier.getVestedTokens(vestingContractAddress, vestingAccountAddress);
|
||||
}
|
||||
|
||||
getVestingTokens(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
|
||||
return this.nyxdQuerier.getVestingTokens(vestingContractAddress, vestingAccountAddress);
|
||||
}
|
||||
|
||||
getSpendableVestedTokens(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
|
||||
return this.nyxdQuerier.getSpendableVestedTokens(vestingContractAddress, vestingAccountAddress);
|
||||
}
|
||||
|
||||
getSpendableRewards(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
|
||||
return this.nyxdQuerier.getSpendableRewards(vestingContractAddress, vestingAccountAddress);
|
||||
}
|
||||
|
||||
getDelegatedCoins(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
|
||||
return this.nyxdQuerier.getDelegatedCoins(vestingContractAddress, vestingAccountAddress);
|
||||
}
|
||||
|
||||
getPledgedCoins(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
|
||||
return this.nyxdQuerier.getPledgedCoins(vestingContractAddress, vestingAccountAddress);
|
||||
}
|
||||
|
||||
getStakedCoins(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
|
||||
return this.nyxdQuerier.getStakedCoins(vestingContractAddress, vestingAccountAddress);
|
||||
}
|
||||
|
||||
getWithdrawnCoins(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
|
||||
return this.nyxdQuerier.getWithdrawnCoins(vestingContractAddress, vestingAccountAddress);
|
||||
}
|
||||
|
||||
getStartTime(vestingContractAddress: string, vestingAccountAddress: string): Promise<string> {
|
||||
return this.nyxdQuerier.getStartTime(vestingContractAddress, vestingAccountAddress);
|
||||
}
|
||||
|
||||
getEndTime(vestingContractAddress: string, vestingAccountAddress: string): Promise<string> {
|
||||
return this.nyxdQuerier.getEndTime(vestingContractAddress, vestingAccountAddress);
|
||||
}
|
||||
|
||||
getOriginalVestingDetails(vestingContractAddress: string, vestingAccountAddress: string): Promise<OriginalVestingResponse> {
|
||||
return this.nyxdQuerier.getOriginalVestingDetails(vestingContractAddress, vestingAccountAddress);
|
||||
}
|
||||
|
||||
getHistoricStakingRewards(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
|
||||
return this.nyxdQuerier.getHistoricStakingRewards(vestingContractAddress, vestingAccountAddress);
|
||||
}
|
||||
|
||||
getAccountDetails(vestingContractAddress: string, address: string): Promise<VestingAccountInfo> {
|
||||
return this.nyxdQuerier.getAccountDetails(vestingContractAddress, address);
|
||||
}
|
||||
|
||||
getMixnode(vestingContractAddress: string, address: string): Promise<VestingAccountNode> {
|
||||
return this.nyxdQuerier.getMixnode(vestingContractAddress, address);
|
||||
}
|
||||
|
||||
getGateway(vestingContractAddress: string, address: string): Promise<VestingAccountNode> {
|
||||
return this.nyxdQuerier.getGateway(vestingContractAddress, address);
|
||||
}
|
||||
|
||||
getDelegationTimes(vestingContractAddress: string, mix_id: number, delegatorAddress: string): Promise<DelegationTimes> {
|
||||
return this.nyxdQuerier.getDelegationTimes(vestingContractAddress, mix_id, delegatorAddress);
|
||||
}
|
||||
|
||||
getAllDelegations(vestingContractAddress: string): Promise<Delegations> {
|
||||
return this.nyxdQuerier.getAllDelegations(vestingContractAddress);
|
||||
}
|
||||
|
||||
getCurrentVestingPeriod(vestingContractAddress: string, address: string): Promise<Period> {
|
||||
return this.nyxdQuerier.getCurrentVestingPeriod(vestingContractAddress, address);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { Coin } from 'cosmjs-types/cosmos/base/v1beta1/coin';
|
||||
import expect from 'expect';
|
||||
|
||||
export const amountDemon = {
|
||||
amount: expect.any(String),
|
||||
denom: expect.any(String)
|
||||
denom: expect.any(String),
|
||||
amount: expect.any(String)
|
||||
}
|
||||
|
||||
export const delegation = {
|
||||
@@ -159,7 +160,6 @@ export const layerDistribution = {
|
||||
layer3: expect.any(Number)
|
||||
}
|
||||
|
||||
|
||||
export const intervalRewardParams = {
|
||||
reward_pool: expect.any(Number),
|
||||
staking_supply: expect.any(Number),
|
||||
@@ -176,3 +176,79 @@ export const rewardingParams = {
|
||||
rewarded_set_size: expect.any(Number),
|
||||
active_set_size: expect.any(Number)
|
||||
}
|
||||
|
||||
export const VestAccounts = [{
|
||||
account_id: expect.any(String),
|
||||
owner: expect.any(String)
|
||||
}]
|
||||
|
||||
export const VestAccountCoin = [{
|
||||
account_id: expect.any(String),
|
||||
owner: expect.any(String),
|
||||
still_vesting: Coin
|
||||
}]
|
||||
|
||||
export const vestingAccountsPaged = {
|
||||
accounts: VestAccounts,
|
||||
start_next_after: expect.any(String)
|
||||
}
|
||||
|
||||
export const VestingCoinAccounts = {
|
||||
accounts: VestAccountCoin,
|
||||
start_next_after: expect.any(String)
|
||||
}
|
||||
|
||||
export const OriginalVestingDetails = {
|
||||
amount: Coin,
|
||||
number_of_periods: expect.any(Number),
|
||||
period_duration: expect.any(Number)
|
||||
}
|
||||
|
||||
export const PledgeCap = {
|
||||
percent: expect.any(String) || null,
|
||||
};
|
||||
|
||||
export const Periods = [{
|
||||
period_seconds: expect.any(Number),
|
||||
start_time: expect.any(Number),
|
||||
}]
|
||||
|
||||
export const VestingAccountDetails = {
|
||||
owner_address: expect.any(String),
|
||||
staking_address: expect.any(String) || null,
|
||||
start_time: expect.any(String),
|
||||
periods: Periods,
|
||||
coin: Coin,
|
||||
storage_key: expect.any(Number),
|
||||
pledge_cap: PledgeCap
|
||||
}
|
||||
|
||||
export const Node = {
|
||||
amount: Coin,
|
||||
block_time: expect.any(String)
|
||||
}
|
||||
|
||||
export type VestingPeriod = 'Before' | { In: number } | 'After';
|
||||
|
||||
export const DelegationTimestamps = [
|
||||
expect.any(Number)
|
||||
]
|
||||
|
||||
export const DelegatorTimes = {
|
||||
owner: expect.any(String),
|
||||
account_id: expect.any(Number),
|
||||
mix_id: expect.any(Number),
|
||||
delegation_timestamps: DelegationTimestamps
|
||||
}
|
||||
|
||||
export const DelegationBlock = [{
|
||||
account_id: expect.any(Number),
|
||||
amount: expect.any(String),
|
||||
block_timestamp: expect.any(Number),
|
||||
mix_id: expect.any(Number)
|
||||
}]
|
||||
|
||||
export const Delegations = {
|
||||
delegations: DelegationBlock,
|
||||
start_next_after: expect.any(String) || null
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import expect from 'expect';
|
||||
import ValidatorClient from '../../index';
|
||||
import { amountDemon, Delegations, DelegatorTimes, Node, OriginalVestingDetails, VestingAccountDetails, vestingAccountsPaged, VestingCoinAccounts, VestingPeriod } from '../expectedResponses';
|
||||
|
||||
const dotenv = require('dotenv');
|
||||
|
||||
@@ -19,10 +20,149 @@ describe('Vesting queries', () => {
|
||||
);
|
||||
});
|
||||
|
||||
const vesting_account_address = 'n14juvj7llvx8eppypnqj6xlrgwss9wfrcuy0nkv';
|
||||
const mixnodeowner = 'n1z93z44vf8ssvdhujjvxcj4rd5e3lz0l60wdk70';
|
||||
const gatewayowner = 'n1un9cuvw9e3xqratmde4j55ucksev0dkeruq800';
|
||||
const mix_id = 79;
|
||||
|
||||
it('can query for contract version', async () => {
|
||||
const contract = await client.getVestingContractVersion();
|
||||
expect(contract).toBeTruthy();
|
||||
});
|
||||
|
||||
it('can get the balance on the account', () => {});
|
||||
// TODO see if we can use AccountEntry type here instead
|
||||
it('can get all accounts paged', async () => {
|
||||
const accounts = await client.getVestingAccountsPaged();
|
||||
expect(Object.keys(accounts)).toEqual(Object.keys(vestingAccountsPaged));
|
||||
expect(accounts).toBeTruthy();
|
||||
});
|
||||
|
||||
it('can get coins for all accounts paged', async () => {
|
||||
const accounts = await client.getVestingAmountsAccountsPaged();
|
||||
expect(Object.keys(accounts)).toEqual(Object.keys(VestingCoinAccounts));
|
||||
expect(accounts).toBeTruthy();
|
||||
});
|
||||
|
||||
it('can get locked tokens for an account', async () => {
|
||||
const locked = await client.getLockedTokens(vesting_account_address);
|
||||
expect(Object.keys(locked)).toEqual(Object.keys(amountDemon));
|
||||
expect(locked).toBeTruthy();
|
||||
});
|
||||
|
||||
it('can get spendable tokens for an account', async () => {
|
||||
const spendable = await client.getSpendableTokens(vesting_account_address);
|
||||
expect(Object.keys(spendable)).toEqual(Object.keys(amountDemon));
|
||||
expect(spendable).toBeTruthy();
|
||||
});
|
||||
|
||||
it('can get vested tokens for an account', async () => {
|
||||
const vested = await client.getVestedTokens(vesting_account_address);
|
||||
expect(Object.keys(vested)).toEqual(Object.keys(amountDemon));
|
||||
expect(vested).toBeTruthy();
|
||||
});
|
||||
|
||||
it('can get vesting tokens for an account', async () => {
|
||||
const vesting = await client.getVestingTokens(vesting_account_address);
|
||||
expect(Object.keys(vesting)).toEqual(Object.keys(amountDemon));
|
||||
expect(vesting).toBeTruthy();
|
||||
});
|
||||
|
||||
it('can get spendable vested tokens for an account', async () => {
|
||||
const spendable = await client.getSpendableVestedTokens(vesting_account_address);
|
||||
expect(Object.keys(spendable)).toEqual(Object.keys(amountDemon));
|
||||
expect(spendable).toBeTruthy();
|
||||
});
|
||||
|
||||
it('can get spendable rewards for an account', async () => {
|
||||
const rewards = await client.getSpendableRewards(vesting_account_address);
|
||||
expect(Object.keys(rewards)).toEqual(Object.keys(amountDemon));
|
||||
expect(rewards).toBeTruthy();
|
||||
});
|
||||
|
||||
it('can get delegated coins', async () => {
|
||||
const delegated = await client.getDelegatedCoins(vesting_account_address);
|
||||
expect(Object.keys(delegated)).toEqual(Object.keys(amountDemon));
|
||||
expect(delegated).toBeTruthy();
|
||||
});
|
||||
|
||||
it('can get pledged coins', async () => {
|
||||
const pledged = await client.getPledgedCoins(vesting_account_address);
|
||||
expect(Object.keys(pledged)).toEqual(Object.keys(amountDemon));
|
||||
expect(pledged).toBeTruthy();
|
||||
});
|
||||
|
||||
it('can get staked coins', async () => {
|
||||
const staked = await client.getStakedCoins(vesting_account_address);
|
||||
expect(Object.keys(staked)).toEqual(Object.keys(amountDemon));
|
||||
expect(staked).toBeTruthy();
|
||||
});
|
||||
|
||||
it('can get withdrawn coins', async () => {
|
||||
const withdrawn = await client.getWithdrawnCoins(vesting_account_address);
|
||||
expect(Object.keys(withdrawn)).toEqual(Object.keys(amountDemon));
|
||||
expect(withdrawn).toBeTruthy();
|
||||
});
|
||||
|
||||
it('can get start time of an account', async () => {
|
||||
const time = await client.getStartTime(vesting_account_address);
|
||||
expect(typeof time).toBe("string");
|
||||
expect(time).toBeTruthy();
|
||||
});
|
||||
|
||||
it('can get end time of an account', async () => {
|
||||
const time = await client.getEndTime(vesting_account_address);
|
||||
expect(typeof time).toBe("string");
|
||||
expect(time).toBeTruthy();
|
||||
});
|
||||
|
||||
it('can get account original vesting details', async () => {
|
||||
const original = await client.getOriginalVestingDetails(vesting_account_address);
|
||||
expect(Object.keys(original)).toEqual(Object.keys(OriginalVestingDetails));
|
||||
expect(original).toBeTruthy();
|
||||
});
|
||||
|
||||
it('can get historic vesting staking rewards', async () => {
|
||||
const rewards = await client.getHistoricStakingRewards(vesting_account_address);
|
||||
expect(Object.keys(rewards)).toEqual(Object.keys(amountDemon));
|
||||
expect(rewards).toBeTruthy();
|
||||
});
|
||||
|
||||
// TODO see if we can use "VestingAccountInfo" type here instead
|
||||
it('can get account details', async () => {
|
||||
const account = await client.getAccountDetails(vesting_account_address);
|
||||
expect(Object.keys(account)).toEqual(Object.keys(VestingAccountDetails));
|
||||
expect(account).toBeTruthy();
|
||||
});
|
||||
|
||||
// TODO add option for if account has no mixnode and expected is null
|
||||
it('can get mixnode', async () => {
|
||||
const mixnode = await client.getMixnode(mixnodeowner);
|
||||
expect(Object.keys(mixnode)).toEqual(Object.keys(Node));
|
||||
expect(mixnode).toBeTruthy();
|
||||
});
|
||||
|
||||
// TODO add option for if account has no gateway and expected is null
|
||||
it.skip('can get gateway', async () => {
|
||||
const gateway = await client.getGateway(gatewayowner);
|
||||
expect(Object.keys(gateway)).toEqual(Object.keys(Node));
|
||||
expect(gateway).toBeTruthy();
|
||||
});
|
||||
|
||||
it('can get delegations times', async () => {
|
||||
const delegation = await client.getDelegationTimes(mix_id, mixnodeowner);
|
||||
expect(Object.keys(delegation)).toEqual(Object.keys(DelegatorTimes));
|
||||
expect(delegation).toBeTruthy();
|
||||
});
|
||||
|
||||
it('can get all delegations', async () => {
|
||||
const delegation = await client.getAllDelegations();
|
||||
expect(Object.keys(delegation)).toEqual(Object.keys(Delegations));
|
||||
expect(delegation).toBeTruthy();
|
||||
});
|
||||
|
||||
it('can get current vesting period', async () => {
|
||||
const period = await client.getCurrentVestingPeriod(gatewayowner);
|
||||
expect(period).toEqual(expect.anything() as unknown as VestingPeriod);
|
||||
expect(period).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-bin-common"
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
description = "Common code for nym binaries"
|
||||
edition = { workspace = true }
|
||||
authors = { workspace = true }
|
||||
|
||||
@@ -40,14 +40,14 @@ async-trait = { workspace = true, optional = true }
|
||||
bip39 = { workspace = true, features = ["rand"], optional = true }
|
||||
nym-config = { path = "../../config", optional = true }
|
||||
cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support", features = ["rpc", "bip32", "cosmwasm"], optional = true}
|
||||
cw3 = { version = "0.13.4", optional = true }
|
||||
cw4 = { version = "0.13.4", optional = true }
|
||||
cw3 = { workspace = true, optional = true }
|
||||
cw4 = { workspace = true, optional = true }
|
||||
prost = { version = "0.10", default-features = false, optional = true }
|
||||
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 }
|
||||
zeroize = { version = "1.5.7", optional = true, features = ["zeroize_derive"] }
|
||||
cosmwasm-std = { workspace = true, optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
ts-rs = "6.1.2"
|
||||
|
||||
@@ -749,6 +749,12 @@ where
|
||||
Ok(self.nym_api.get_mixnodes_detailed().await?)
|
||||
}
|
||||
|
||||
pub async fn get_cached_mixnodes_detailed_unfiltered(
|
||||
&self,
|
||||
) -> Result<Vec<MixNodeBondAnnotated>, ValidatorClientError> {
|
||||
Ok(self.nym_api.get_mixnodes_detailed_unfiltered().await?)
|
||||
}
|
||||
|
||||
pub async fn get_cached_rewarded_mixnodes(
|
||||
&self,
|
||||
) -> Result<Vec<MixNodeDetails>, ValidatorClientError> {
|
||||
|
||||
@@ -144,6 +144,21 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_mixnodes_detailed_unfiltered(
|
||||
&self,
|
||||
) -> Result<Vec<MixNodeBondAnnotated>, NymAPIError> {
|
||||
self.query_nym_api(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS,
|
||||
routes::MIXNODES,
|
||||
routes::DETAILED_UNFILTERED,
|
||||
],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_gateways(&self) -> Result<Vec<GatewayBond>, NymAPIError> {
|
||||
self.query_nym_api(&[routes::API_VERSION, routes::GATEWAYS], NO_PARAMS)
|
||||
.await
|
||||
|
||||
@@ -8,6 +8,7 @@ pub const MIXNODES: &str = "mixnodes";
|
||||
pub const GATEWAYS: &str = "gateways";
|
||||
|
||||
pub const DETAILED: &str = "detailed";
|
||||
pub const DETAILED_UNFILTERED: &str = "detailed-unfiltered";
|
||||
pub const ACTIVE: &str = "active";
|
||||
pub const REWARDED: &str = "rewarded";
|
||||
pub const COCONUT_ROUTES: &str = "coconut";
|
||||
|
||||
@@ -11,7 +11,7 @@ bs58 = "0.4"
|
||||
comfy-table = "6.0.0"
|
||||
cfg-if = "1.0.0"
|
||||
clap = { version = "4.0", features = ["derive"] }
|
||||
cw-utils = { version = "0.13.4" }
|
||||
cw-utils = { workspace = true }
|
||||
handlebars = "3.0.1"
|
||||
humantime-serde = "1.0"
|
||||
k256 = { version = "0.10", features = ["ecdsa", "sha256"] }
|
||||
@@ -26,7 +26,7 @@ url = "2.2"
|
||||
tap = "1"
|
||||
|
||||
cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support" }
|
||||
cosmwasm-std = { version = "1.0.0" }
|
||||
cosmwasm-std = { workspace = true }
|
||||
|
||||
validator-client = { path = "../client-libs/validator-client", features = ["nyxd-client"] }
|
||||
nym-network-defaults = { path = "../network-defaults" }
|
||||
|
||||
@@ -6,7 +6,7 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
cosmwasm-std = "1.0.0"
|
||||
cosmwasm-std = { workspace = true }
|
||||
schemars = "0.8"
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
nym-multisig-contract-common = { path = "../multisig-contract" }
|
||||
|
||||
@@ -6,8 +6,8 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
cosmwasm-std = "1.0.0"
|
||||
cw-utils = "0.13.4"
|
||||
cosmwasm-std = { workspace = true }
|
||||
cw-utils = { workspace = true }
|
||||
schemars = "0.8"
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-contracts-common"
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
description = "Common library for Nym cosmwasm contracts"
|
||||
edition = { workspace = true }
|
||||
authors = { workspace = true }
|
||||
@@ -9,7 +9,7 @@ repository = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
bs58 = "0.4.0"
|
||||
cosmwasm-std = "1.0.0"
|
||||
cosmwasm-std = { workspace = true }
|
||||
schemars = "0.8"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
thiserror = "1"
|
||||
|
||||
@@ -6,6 +6,6 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
cw4 = { version = "0.13.4" }
|
||||
cw4 = { workspace = true }
|
||||
schemars = "0.8"
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-mixnet-contract-common"
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
description = "Common library for the Nym mixnet contract"
|
||||
rust-version = "1.62"
|
||||
edition = { workspace = true }
|
||||
@@ -10,12 +10,12 @@ repository = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
bs58 = "0.4.0"
|
||||
cosmwasm-std = "=1.0.0"
|
||||
cosmwasm-std = { workspace = true }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_repr = "0.1"
|
||||
schemars = "0.8"
|
||||
thiserror = "1.0"
|
||||
contracts-common = { path = "../contracts-common", package = "nym-contracts-common", version = "0.2.0" }
|
||||
contracts-common = { path = "../contracts-common", package = "nym-contracts-common", version = "0.3.0" }
|
||||
# use 0.4.1 as that's the version used by cosmwasm-std 1.0.0
|
||||
# (and ideally we don't want to pull the same dependency twice)
|
||||
serde-json-wasm = "=0.4.1"
|
||||
|
||||
@@ -6,11 +6,11 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
cw-utils = { version = "0.13.4" }
|
||||
cw3 = { version = "0.13.4" }
|
||||
cw3-fixed-multisig = { version = "0.13.4", features = ["library"] }
|
||||
cw4 = { version = "0.13.4" }
|
||||
cosmwasm-std = "1.0.0"
|
||||
cw-utils = { workspace = true }
|
||||
cw3 = { workspace = true }
|
||||
cw3-fixed-multisig = { workspace = true, features = ["library"] }
|
||||
cw4 = { workspace= true }
|
||||
cosmwasm-std = { workspace = true }
|
||||
schemars = "0.8"
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
thiserror = { version = "1.0.23" }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-vesting-contract-common"
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
description = "Common library for the Nym vesting contract"
|
||||
edition = { workspace = true }
|
||||
authors = { workspace = true }
|
||||
@@ -8,9 +8,9 @@ license = { workspace = true }
|
||||
repository = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
cosmwasm-std = "1.0.0"
|
||||
mixnet-contract-common = { path = "../mixnet-contract", package = "nym-mixnet-contract-common", version = "0.2.0" }
|
||||
contracts-common = { path = "../contracts-common", package = "nym-contracts-common", version = "0.2.0" }
|
||||
cosmwasm-std = { workspace = true }
|
||||
mixnet-contract-common = { path = "../mixnet-contract", package = "nym-mixnet-contract-common", version = "0.3.0" }
|
||||
contracts-common = { path = "../contracts-common", package = "nym-contracts-common", version = "0.3.0" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
schemars = "0.8"
|
||||
ts-rs = {version = "6.1.2", optional = true}
|
||||
|
||||
@@ -48,12 +48,12 @@ impl CoconutCredentialManager {
|
||||
/// Tries to retrieve one of the stored, unused credentials.
|
||||
pub(crate) async fn get_next_coconut_credential(
|
||||
&self,
|
||||
) -> Result<CoconutCredential, sqlx::Error> {
|
||||
) -> Result<Option<CoconutCredential>, sqlx::Error> {
|
||||
sqlx::query_as!(
|
||||
CoconutCredential,
|
||||
"SELECT * FROM coconut_credentials WHERE NOT consumed"
|
||||
)
|
||||
.fetch_one(&self.connection_pool)
|
||||
.fetch_optional(&self.connection_pool)
|
||||
.await
|
||||
}
|
||||
|
||||
|
||||
@@ -13,4 +13,7 @@ pub enum StorageError {
|
||||
|
||||
#[error("Inconsistent data in database")]
|
||||
InconsistentData,
|
||||
|
||||
#[error("No unused credential in database. You need to buy at least one")]
|
||||
NoCredential,
|
||||
}
|
||||
|
||||
@@ -90,7 +90,8 @@ impl Storage for PersistentStorage {
|
||||
let credential = self
|
||||
.coconut_credential_manager
|
||||
.get_next_coconut_credential()
|
||||
.await?;
|
||||
.await?
|
||||
.ok_or(StorageError::NoCredential)?;
|
||||
|
||||
Ok(credential)
|
||||
}
|
||||
|
||||
@@ -26,6 +26,9 @@ nym-sphinx-types = { path = "types" }
|
||||
nym-crypto = { path = "../crypto", version = "0.2.0" }
|
||||
nym-topology = { path = "../topology" }
|
||||
|
||||
# outfox
|
||||
nym-outfox = { path = "../../nym-outfox" }
|
||||
|
||||
[dev-dependencies]
|
||||
nym-mixnet-contract-common = { path = "../cosmwasm-smart-contracts/mixnet-contract" }
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ use nym_crypto::asymmetric::encryption;
|
||||
use nym_crypto::shared_key::recompute_shared_key;
|
||||
use nym_crypto::symmetric::stream_cipher;
|
||||
use nym_crypto::symmetric::stream_cipher::CipherKey;
|
||||
use nym_outfox::error::OutfoxError;
|
||||
use nym_outfox::lion::lion_transform_decrypt;
|
||||
use nym_sphinx_anonymous_replies::requests::AnonymousSenderTag;
|
||||
use nym_sphinx_anonymous_replies::SurbEncryptionKey;
|
||||
use nym_sphinx_chunking::fragment::Fragment;
|
||||
@@ -74,54 +76,76 @@ pub enum MessageRecoveryError {
|
||||
|
||||
#[error("Failed to recover message fragment - {0}")]
|
||||
FragmentRecoveryError(#[from] ChunkingError),
|
||||
|
||||
#[error("Outfox: {source}")]
|
||||
OutfoxRecoveryError {
|
||||
#[from]
|
||||
source: OutfoxError,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct MessageReceiver {
|
||||
/// High level public structure used to buffer all received data [`Fragment`]s and eventually
|
||||
/// returning original messages that they encapsulate.
|
||||
#[derive(Default)]
|
||||
pub struct OutfoxMessageReceiver {
|
||||
reconstructor: MessageReconstructor,
|
||||
|
||||
/// Number of mix hops each packet ('real' message, ack, reply) is expected to take.
|
||||
/// Note that it does not include gateway hops.
|
||||
num_mix_hops: u8,
|
||||
}
|
||||
|
||||
impl MessageReceiver {
|
||||
impl OutfoxMessageReceiver {
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Allows setting non-default number of expected mix hops in the network.
|
||||
#[must_use]
|
||||
pub fn with_mix_hops(mut self, hops: u8) -> Self {
|
||||
self.num_mix_hops = hops;
|
||||
self
|
||||
impl MessageReceiver for OutfoxMessageReceiver {
|
||||
fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn decrypt_raw_message<C>(&self, message: &mut [u8], key: &CipherKey<C>)
|
||||
fn reconstructor(&self) -> MessageReconstructor {
|
||||
self.reconstructor.clone()
|
||||
}
|
||||
|
||||
fn num_mix_hops(&self) -> u8 {
|
||||
DEFAULT_NUM_MIX_HOPS
|
||||
}
|
||||
|
||||
fn decrypt_raw_message<C>(
|
||||
&self,
|
||||
message: &mut [u8],
|
||||
key: &CipherKey<C>,
|
||||
) -> Result<(), MessageRecoveryError>
|
||||
where
|
||||
C: StreamCipher + KeyIvInit,
|
||||
{
|
||||
let zero_iv = stream_cipher::zero_iv::<C>();
|
||||
stream_cipher::decrypt_in_place::<C>(key, &zero_iv, message)
|
||||
lion_transform_decrypt(message, key)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Given raw fragment data, **WITH KEY DIGEST PREFIX ALREADY REMOVED!!**, uses looked up
|
||||
/// key to decrypt fragment data
|
||||
pub fn recover_plaintext_from_reply(
|
||||
pub trait MessageReceiver {
|
||||
fn new() -> Self;
|
||||
fn reconstructor(&self) -> MessageReconstructor;
|
||||
fn num_mix_hops(&self) -> u8;
|
||||
|
||||
fn decrypt_raw_message<C>(
|
||||
&self,
|
||||
message: &mut [u8],
|
||||
key: &CipherKey<C>,
|
||||
) -> Result<(), MessageRecoveryError>
|
||||
where
|
||||
C: StreamCipher + KeyIvInit;
|
||||
|
||||
fn recover_plaintext_from_reply(
|
||||
&self,
|
||||
reply_ciphertext: &mut [u8],
|
||||
reply_key: SurbEncryptionKey,
|
||||
) {
|
||||
) -> Result<(), MessageRecoveryError> {
|
||||
self.decrypt_raw_message::<ReplySurbEncryptionAlgorithm>(
|
||||
reply_ciphertext,
|
||||
reply_key.inner(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Given raw fragment data, recovers the remote ephemeral key, recomputes shared secret,
|
||||
/// uses it to decrypt fragment data
|
||||
pub fn recover_plaintext_from_regular_packet<'a>(
|
||||
fn recover_plaintext_from_regular_packet<'a>(
|
||||
&self,
|
||||
local_key: &encryption::PrivateKey,
|
||||
raw_enc_frag: &'a mut [u8],
|
||||
@@ -146,30 +170,25 @@ impl MessageReceiver {
|
||||
// 3. decrypt fragment data
|
||||
let fragment_ciphertext = &mut raw_enc_frag[encryption::PUBLIC_KEY_SIZE..];
|
||||
|
||||
self.decrypt_raw_message::<PacketEncryptionAlgorithm>(fragment_ciphertext, &encryption_key);
|
||||
self.decrypt_raw_message::<PacketEncryptionAlgorithm>(
|
||||
fragment_ciphertext,
|
||||
&encryption_key,
|
||||
)?;
|
||||
let fragment_data = fragment_ciphertext;
|
||||
|
||||
Ok(fragment_data)
|
||||
}
|
||||
|
||||
/// Given fragment data recovers [`Fragment`] itself.
|
||||
pub fn recover_fragment(&self, frag_data: &[u8]) -> Result<Fragment, MessageRecoveryError> {
|
||||
fn recover_fragment(&self, frag_data: &[u8]) -> Result<Fragment, MessageRecoveryError> {
|
||||
Ok(Fragment::try_from_bytes(frag_data)?)
|
||||
}
|
||||
|
||||
/// Inserts given [`Fragment`] into the reconstructor.
|
||||
/// If it was last remaining [`Fragment`] for the original message, the message is reconstructed
|
||||
/// and returned alongside all (if applicable) set ids used in the message.
|
||||
///
|
||||
/// # Returns:
|
||||
/// - The reconstructed message alongside optional reply SURB,
|
||||
/// - List of ids of all the [`Set`]s used during reconstruction to detect stale retransmissions.
|
||||
pub fn insert_new_fragment(
|
||||
fn insert_new_fragment(
|
||||
&mut self,
|
||||
fragment: Fragment,
|
||||
) -> Result<Option<(NymMessage, Vec<i32>)>, MessageRecoveryError> {
|
||||
if let Some((message, used_sets)) = self.reconstructor.insert_new_fragment(fragment) {
|
||||
match PaddedMessage::new_reconstructed(message).remove_padding(self.num_mix_hops) {
|
||||
if let Some((message, used_sets)) = self.reconstructor().insert_new_fragment(fragment) {
|
||||
match PaddedMessage::new_reconstructed(message).remove_padding(self.num_mix_hops()) {
|
||||
Ok(message) => Ok(Some((message, used_sets))),
|
||||
Err(err) => Err(MessageRecoveryError::MalformedReconstructedMessage {
|
||||
source: err,
|
||||
@@ -182,9 +201,56 @@ impl MessageReceiver {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for MessageReceiver {
|
||||
#[derive(Clone)]
|
||||
pub struct SphinxMessageReceiver {
|
||||
/// High level public structure used to buffer all received data [`Fragment`]s and eventually
|
||||
/// returning original messages that they encapsulate.
|
||||
reconstructor: MessageReconstructor,
|
||||
|
||||
/// Number of mix hops each packet ('real' message, ack, reply) is expected to take.
|
||||
/// Note that it does not include gateway hops.
|
||||
num_mix_hops: u8,
|
||||
}
|
||||
|
||||
impl SphinxMessageReceiver {
|
||||
/// Allows setting non-default number of expected mix hops in the network.
|
||||
#[must_use]
|
||||
pub fn with_mix_hops(mut self, hops: u8) -> Self {
|
||||
self.num_mix_hops = hops;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl MessageReceiver for SphinxMessageReceiver {
|
||||
fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn decrypt_raw_message<C>(
|
||||
&self,
|
||||
message: &mut [u8],
|
||||
key: &CipherKey<C>,
|
||||
) -> Result<(), MessageRecoveryError>
|
||||
where
|
||||
C: StreamCipher + KeyIvInit,
|
||||
{
|
||||
let zero_iv = stream_cipher::zero_iv::<C>();
|
||||
stream_cipher::decrypt_in_place::<C>(key, &zero_iv, message);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn reconstructor(&self) -> MessageReconstructor {
|
||||
self.reconstructor.clone()
|
||||
}
|
||||
|
||||
fn num_mix_hops(&self) -> u8 {
|
||||
self.num_mix_hops
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SphinxMessageReceiver {
|
||||
fn default() -> Self {
|
||||
MessageReceiver {
|
||||
SphinxMessageReceiver {
|
||||
reconstructor: Default::default(),
|
||||
num_mix_hops: DEFAULT_NUM_MIX_HOPS,
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ thiserror = "1.0"
|
||||
url = "2.2"
|
||||
ts-rs = "6.1.2"
|
||||
|
||||
cosmwasm-std = "1.0.0"
|
||||
cosmwasm-std = { workspace = true }
|
||||
cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support" }
|
||||
|
||||
validator-client = { path = "../../common/client-libs/validator-client", features = [
|
||||
|
||||
Generated
+2028
File diff suppressed because it is too large
Load Diff
@@ -27,3 +27,18 @@ codegen-units = 1
|
||||
panic = 'abort'
|
||||
incremental = false
|
||||
overflow-checks = true
|
||||
|
||||
[workspace.dependencies]
|
||||
cosmwasm-crypto = "=1.0.0"
|
||||
cosmwasm-derive = "=1.0.0"
|
||||
cosmwasm-schema = "=1.0.0"
|
||||
cosmwasm-std = "=1.0.0"
|
||||
cosmwasm-storage = "=1.0.0"
|
||||
cw-controllers = "=0.13.4"
|
||||
cw-multi-test = "=0.13.4"
|
||||
cw-storage-plus = "=0.13.4"
|
||||
cw-utils = "=0.13.4"
|
||||
cw2 = "=0.13.4"
|
||||
cw3 = "=0.13.4"
|
||||
cw3-fixed-multisig = "=0.13.4"
|
||||
cw4 = "=0.13.4"
|
||||
|
||||
@@ -12,10 +12,10 @@ crate-type = ["cdylib", "rlib"]
|
||||
nym-coconut-bandwidth-contract-common = { path = "../../common/cosmwasm-smart-contracts/coconut-bandwidth-contract" }
|
||||
nym-multisig-contract-common = { path = "../../common/cosmwasm-smart-contracts/multisig-contract" }
|
||||
|
||||
cosmwasm-std = "1.0.0"
|
||||
cosmwasm-storage = "1.0.0"
|
||||
cw-storage-plus = "0.13.4"
|
||||
cw-controllers = "0.13.4"
|
||||
cosmwasm-std = { workspace = true }
|
||||
cosmwasm-storage = { workspace = true }
|
||||
cw-storage-plus = { workspace = true }
|
||||
cw-controllers = { workspace = true }
|
||||
|
||||
schemars = "0.8"
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
|
||||
@@ -11,18 +11,18 @@ crate-type = ["cdylib", "rlib"]
|
||||
[dependencies]
|
||||
nym-coconut-dkg-common = { path = "../../common/cosmwasm-smart-contracts/coconut-dkg" }
|
||||
|
||||
cosmwasm-std = "1.0.0"
|
||||
cosmwasm-storage = "1.0.0"
|
||||
cw-storage-plus = "0.13.4"
|
||||
cw-controllers = "0.13.4"
|
||||
cw4 = { version = "0.13.4" }
|
||||
cosmwasm-std = { workspace = true }
|
||||
cosmwasm-storage = { workspace = true }
|
||||
cw-storage-plus = { workspace = true }
|
||||
cw-controllers = { workspace = true }
|
||||
cw4 = { workspace = true }
|
||||
|
||||
schemars = "0.8"
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
thiserror = "1.0.23"
|
||||
|
||||
[dev-dependencies]
|
||||
cw-multi-test = { version = "0.13.4" }
|
||||
cw-multi-test = { workspace = true }
|
||||
cw4-group = { path = "../multisig/cw4-group" }
|
||||
nym-group-contract-common = { path = "../../common/cosmwasm-smart-contracts/group-contract" }
|
||||
lazy_static = "1.4"
|
||||
|
||||
@@ -12,13 +12,13 @@ nym-coconut-dkg-common = { path = "../../common/cosmwasm-smart-contracts/coconut
|
||||
nym-multisig-contract-common = { path = "../../common/cosmwasm-smart-contracts/multisig-contract" }
|
||||
nym-group-contract-common = { path = "../../common/cosmwasm-smart-contracts/group-contract" }
|
||||
|
||||
cosmwasm-std = "1.0.0"
|
||||
cosmwasm-storage = "1.0.0"
|
||||
cw3 = "0.13.4"
|
||||
cw4 = "0.13.4"
|
||||
cw-storage-plus = "0.13.4"
|
||||
cw-controllers = "0.13.4"
|
||||
cw-utils = "0.13.4"
|
||||
cosmwasm-std = { workspace = true }
|
||||
cosmwasm-storage = { workspace = true }
|
||||
cw3 = { workspace = true }
|
||||
cw4 = { workspace = true }
|
||||
cw-storage-plus = { workspace = true }
|
||||
cw-controllers = { workspace = true }
|
||||
cw-utils = { workspace = true }
|
||||
|
||||
schemars = "0.8"
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
@@ -26,7 +26,7 @@ thiserror = "1.0.23"
|
||||
|
||||
nym-coconut-bandwidth = { path = "../coconut-bandwidth" }
|
||||
nym-coconut-dkg = { path = "../coconut-dkg" }
|
||||
cw-multi-test = { version = "0.13.4" }
|
||||
cw-multi-test = { workspace = true }
|
||||
cw3-flex-multisig = { path = "../multisig/cw3-flex-multisig" }
|
||||
cw4-group = { path = "../multisig/cw4-group" }
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-mixnet-contract"
|
||||
version = "1.2.0-pre.0"
|
||||
version = "1.2.0"
|
||||
description = "Nym mixnet contract"
|
||||
edition = { workspace = true }
|
||||
authors = { workspace = true }
|
||||
@@ -22,14 +22,15 @@ name = "mixnet_contract"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract", package = "nym-mixnet-contract-common", version = "0.2.0" }
|
||||
vesting-contract-common = { path = "../../common/cosmwasm-smart-contracts/vesting-contract", package = "nym-vesting-contract-common", version = "0.2.0" }
|
||||
nym-contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts-common", version = "0.2.0" }
|
||||
mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract", package = "nym-mixnet-contract-common", version = "0.3.0" }
|
||||
vesting-contract-common = { path = "../../common/cosmwasm-smart-contracts/vesting-contract", package = "nym-vesting-contract-common", version = "0.3.0" }
|
||||
nym-contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts-common", version = "0.3.0" }
|
||||
|
||||
cosmwasm-std = "1.0.0"
|
||||
cosmwasm-storage = "1.0.0"
|
||||
cw2 = { version = "0.13.4" }
|
||||
cw-storage-plus = "0.13.4"
|
||||
cosmwasm-std = { workspace = true }
|
||||
cosmwasm-storage = { workspace = true }
|
||||
cosmwasm-derive = { workspace = true }
|
||||
cw2 = { workspace = true }
|
||||
cw-storage-plus = { workspace = true }
|
||||
|
||||
bs58 = "0.4.0"
|
||||
schemars = "0.8"
|
||||
@@ -39,7 +40,7 @@ time = { version = "0.3", features = ["macros"] }
|
||||
semver = { version = "1.0.16", default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
cosmwasm-schema = "1.0.0"
|
||||
cosmwasm-schema = { workspace = true }
|
||||
rand_chacha = "0.2"
|
||||
nym-crypto = { path = "../../common/crypto", features = ["asymmetric", "rand"] }
|
||||
|
||||
|
||||
@@ -327,6 +327,14 @@ pub(crate) fn try_update_interval_config(
|
||||
) -> Result<Response, MixnetContractError> {
|
||||
ensure_is_owner(info.sender, deps.storage)?;
|
||||
|
||||
if epochs_in_interval == 0 {
|
||||
return Err(MixnetContractError::EpochsInIntervalZero);
|
||||
}
|
||||
|
||||
if epoch_duration_secs == 0 {
|
||||
return Err(MixnetContractError::EpochDurationZero);
|
||||
}
|
||||
|
||||
let interval = storage::current_interval(deps.storage)?;
|
||||
if force_immediately || interval.is_current_interval_over(&env) {
|
||||
change_interval_config(
|
||||
|
||||
@@ -17,13 +17,13 @@ crate-type = ["cdylib", "rlib"]
|
||||
library = []
|
||||
|
||||
[dependencies]
|
||||
cw-utils = { version = "0.13.4" }
|
||||
cw2 = { version = "0.13.4" }
|
||||
cw3 = { version = "0.13.4" }
|
||||
cw3-fixed-multisig = { version = "0.13.4", features = ["library"] }
|
||||
cw4 = { version = "0.13.4" }
|
||||
cw-storage-plus = { version = "0.13.4" }
|
||||
cosmwasm-std = { version = "1.0.0" }
|
||||
cw-utils = { workspace = true }
|
||||
cw2 = { workspace = true }
|
||||
cw3 = { workspace = true }
|
||||
cw3-fixed-multisig = { workspace = true, features = ["library"] }
|
||||
cw4 = { workspace = true }
|
||||
cw-storage-plus = { workspace = true }
|
||||
cosmwasm-std = { workspace = true }
|
||||
schemars = "0.8.1"
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
|
||||
@@ -33,4 +33,4 @@ nym-multisig-contract-common = { path= "../../../common/cosmwasm-smart-contracts
|
||||
[dev-dependencies]
|
||||
cosmwasm-schema = { version = "1.0.0" }
|
||||
cw4-group = { path = "../cw4-group", version = "0.13.4" }
|
||||
cw-multi-test = { version = "0.13.4" }
|
||||
cw-multi-test = { workspace = true }
|
||||
|
||||
@@ -26,15 +26,15 @@ library = []
|
||||
[dependencies]
|
||||
nym-group-contract-common = { path = "../../../common/cosmwasm-smart-contracts/group-contract" }
|
||||
|
||||
cw-utils = { version = "0.13.4" }
|
||||
cw2 = { version = "0.13.4" }
|
||||
cw4 = { version = "0.13.4" }
|
||||
cw-controllers = { version = "0.13.4" }
|
||||
cw-storage-plus = { version = "0.13.4" }
|
||||
cosmwasm-std = { version = "1.0.0" }
|
||||
cw-utils = { workspace = true }
|
||||
cw2 = { workspace = true }
|
||||
cw4 = { workspace = true }
|
||||
cw-controllers = { workspace = true }
|
||||
cw-storage-plus = { workspace = true }
|
||||
cosmwasm-std = { workspace = true }
|
||||
schemars = "0.8.1"
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
thiserror = { version = "1.0.23" }
|
||||
|
||||
[dev-dependencies]
|
||||
cosmwasm-schema = { version = "1.0.0" }
|
||||
cosmwasm-schema = { workspace = true }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-vesting-contract"
|
||||
version = "1.2.0-pre.1"
|
||||
version = "1.2.0"
|
||||
description = "Nym vesting contract"
|
||||
edition = { workspace = true }
|
||||
authors = { workspace = true }
|
||||
@@ -20,13 +20,14 @@ name = "vesting_contract"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract", package = "nym-mixnet-contract-common", version = "0.2.0" }
|
||||
contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts-common", package = "nym-contracts-common", version = "0.2.0" }
|
||||
vesting-contract-common = { path = "../../common/cosmwasm-smart-contracts/vesting-contract", package = "nym-vesting-contract-common", version = "0.2.0" }
|
||||
mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract", package = "nym-mixnet-contract-common", version = "0.3.0" }
|
||||
contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts-common", package = "nym-contracts-common", version = "0.3.0" }
|
||||
vesting-contract-common = { path = "../../common/cosmwasm-smart-contracts/vesting-contract", package = "nym-vesting-contract-common", version = "0.3.0" }
|
||||
|
||||
cosmwasm-std = { version = "1.0.0 "}
|
||||
cw2 = { version = "0.13.4" }
|
||||
cw-storage-plus = { version = "0.13.4", features = ["iterator"] }
|
||||
cosmwasm-std = { workspace = true }
|
||||
cosmwasm-derive = { workspace = true }
|
||||
cw2 = { workspace = true }
|
||||
cw-storage-plus = { workspace = true, features = ["iterator"] }
|
||||
|
||||
schemars = "0.8"
|
||||
serde = { version = "1.0", default-features = false, features = ["derive"] }
|
||||
@@ -38,6 +39,7 @@ rand_chacha = "0.3.1"
|
||||
base64 = "0.21.0"
|
||||
hex = "0.4.3"
|
||||
serde_json = "1.0.66"
|
||||
cosmwasm-crypto = { workspace = true }
|
||||
|
||||
[build-dependencies]
|
||||
vergen = { version = "=7.4.3", default-features = false, features = ["build", "git", "rustc"] }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "explorer-api"
|
||||
version = "1.1.12"
|
||||
version = "1.1.13"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@@ -42,6 +42,7 @@ pub(crate) struct PrettyDetailedMixNodeBond {
|
||||
pub operating_cost: Coin,
|
||||
pub profit_margin_percent: Percent,
|
||||
pub family_id: Option<u16>,
|
||||
pub blacklisted: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, JsonSchema)]
|
||||
|
||||
@@ -162,6 +162,7 @@ impl ThreadsafeMixNodesCache {
|
||||
operating_cost: rewarding_info.cost_params.interval_operating_cost.clone(),
|
||||
profit_margin_percent: rewarding_info.cost_params.profit_margin_percent,
|
||||
family_id,
|
||||
blacklisted: node.blacklisted,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ impl ExplorerApiTasks {
|
||||
|
||||
async fn retrieve_all_mixnodes(&self) -> Vec<MixNodeBondAnnotated> {
|
||||
info!("About to retrieve all mixnode bonds...");
|
||||
self.retrieve_mixnodes(validator_client::Client::get_cached_mixnodes_detailed)
|
||||
self.retrieve_mixnodes(validator_client::Client::get_cached_mixnodes_detailed_unfiltered)
|
||||
.await
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
API_BASE_URL,
|
||||
BLOCK_API,
|
||||
COUNTRY_DATA_API,
|
||||
GATEWAYS_API,
|
||||
@@ -27,6 +28,7 @@ import {
|
||||
StatusResponse,
|
||||
SummaryOverviewResponse,
|
||||
ValidatorsResponse,
|
||||
Environment,
|
||||
GatewayBondAnnotated,
|
||||
GatewayBond,
|
||||
DirectoryService,
|
||||
@@ -157,3 +159,8 @@ export class Api {
|
||||
return json;
|
||||
};
|
||||
}
|
||||
|
||||
export const getEnvironment = (): Environment => {
|
||||
const matchEnv = (env: Environment) => API_BASE_URL?.toLocaleLowerCase().includes(env) && env;
|
||||
return matchEnv('sandbox') || matchEnv('qa') || 'mainnet';
|
||||
};
|
||||
|
||||
@@ -36,16 +36,16 @@ export const EconomicsInfoColumns: ColumnsType[] = [
|
||||
tooltipInfo:
|
||||
'Monthly operational cost of running this node. This cost is set by the operator and it influences how the rewards are split between the operator and delegators.',
|
||||
},
|
||||
{
|
||||
field: 'avgUptime',
|
||||
title: 'Avg. Score',
|
||||
width: '10%',
|
||||
tooltipInfo: "Mixnode's average routing score in the last 24 hour",
|
||||
},
|
||||
{
|
||||
field: 'nodePerformance',
|
||||
title: 'Routing Score',
|
||||
width: '10%',
|
||||
tooltipInfo:
|
||||
"Mixnode's most recent score (measured in the last 15 minutes). Routing score is relative to that of the network. Each time a gateway is tested, the test packets have to go through the full path of the network (gateway + 3 nodes). If a node in the path drop packets it will affect the score of the gateway and other nodes in the test.",
|
||||
},
|
||||
{
|
||||
field: 'avgUptime',
|
||||
title: 'Avg. Score',
|
||||
tooltipInfo: "Mixnode's average routing score in the last 24 hour",
|
||||
},
|
||||
];
|
||||
|
||||
@@ -53,7 +53,6 @@ export const DelegatorsInfoTable: FCWithChildren<UniversalTableProps<EconomicsIn
|
||||
{columnsData?.map((_, index: number) => {
|
||||
const { field } = columnsData[index];
|
||||
const value: EconomicsRowsType = (eachRow as any)[field];
|
||||
console.log(value);
|
||||
return (
|
||||
<TableCell
|
||||
key={_.title}
|
||||
|
||||
@@ -20,6 +20,7 @@ export type MixnodeRowType = {
|
||||
stake_saturation: React.ReactNode;
|
||||
operating_cost: string;
|
||||
node_performance: NodePerformance['most_recent'];
|
||||
blacklisted: boolean;
|
||||
};
|
||||
|
||||
export function mixnodeToGridRow(arrayOfMixnodes?: MixNodeResponse): MixnodeRowType[] {
|
||||
@@ -51,5 +52,6 @@ export function mixNodeResponseItemToMixnodeRowType(item: MixNodeResponseItem):
|
||||
stake_saturation: uncappedSaturation.toFixed(2),
|
||||
operating_cost: `${unymToNym(item.operating_cost?.amount, 6)} NYM`,
|
||||
node_performance: `${toPercentIntegerString(item.node_performance.most_recent)}%`,
|
||||
blacklisted: item.blacklisted,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -27,11 +27,18 @@ import { DarkLightSwitchMobile } from './Switch';
|
||||
|
||||
export const MobileNav: FCWithChildren = ({ children }) => {
|
||||
const theme = useTheme();
|
||||
const { navState, updateNavState } = useMainContext();
|
||||
const { navState, updateNavState, environment } = useMainContext();
|
||||
const [drawerOpen, setDrawerOpen] = React.useState(false);
|
||||
// Set maintenance banner to false by default to don't display it
|
||||
const [openMaintenance, setOpenMaintenance] = React.useState(false);
|
||||
|
||||
const explorerName =
|
||||
`${environment && environment.charAt(0).toUpperCase() + environment.slice(1)} Explorer` || 'Mainnet Explorer';
|
||||
|
||||
const switchNetworkText = environment === 'mainnet' ? 'Switch to Testnet' : 'Switch to Mainnet';
|
||||
const switchNetworkLink =
|
||||
environment === 'mainnet' ? 'https://sandbox-explorer.nymtech.net' : 'https://explorer.nymtech.net';
|
||||
|
||||
const toggleDrawer = () => {
|
||||
setDrawerOpen(!drawerOpen);
|
||||
};
|
||||
@@ -59,6 +66,7 @@ export const MobileNav: FCWithChildren = ({ children }) => {
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
@@ -72,7 +80,7 @@ export const MobileNav: FCWithChildren = ({ children }) => {
|
||||
}}
|
||||
>
|
||||
<IconButton component="a" href={NYM_WEBSITE} target="_blank">
|
||||
<NymLogo height="40px" width="40px" />
|
||||
<NymLogo height="24px" width="24px" />
|
||||
</IconButton>
|
||||
<Typography
|
||||
variant="h6"
|
||||
@@ -81,16 +89,24 @@ export const MobileNav: FCWithChildren = ({ children }) => {
|
||||
color: theme.palette.nym.networkExplorer.nav.text,
|
||||
fontSize: '18px',
|
||||
fontWeight: 600,
|
||||
ml: 2,
|
||||
}}
|
||||
>
|
||||
<MuiLink component={Link} to="/overview" underline="none" color="inherit">
|
||||
Network Explorer
|
||||
<MuiLink component={Link} to="/overview" underline="none" color="inherit" fontSize={14} fontWeight={700}>
|
||||
{explorerName}
|
||||
</MuiLink>
|
||||
<Button
|
||||
size="small"
|
||||
variant="outlined"
|
||||
color="inherit"
|
||||
href={switchNetworkLink}
|
||||
sx={{ textTransform: 'none', width: 114, fontSize: '12px', fontWeight: 600, ml: 1 }}
|
||||
>
|
||||
{switchNetworkText}
|
||||
</Button>
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box>
|
||||
<Box sx={{ mr: 1 }}>
|
||||
<DarkLightSwitchMobile />
|
||||
<Button onClick={toggleDrawer}>
|
||||
<Menu sx={{ color: theme.palette.primary.contrastText }} />
|
||||
|
||||
@@ -2,6 +2,7 @@ import * as React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { ExpandLess, ExpandMore, Menu } from '@mui/icons-material';
|
||||
import { CSSObject, styled, Theme, useTheme } from '@mui/material/styles';
|
||||
import Button from '@mui/material/Button';
|
||||
import MuiLink from '@mui/material/Link';
|
||||
import Box from '@mui/material/Box';
|
||||
import ListItem from '@mui/material/ListItem';
|
||||
@@ -231,13 +232,20 @@ ExpandableButton.defaultProps = {
|
||||
};
|
||||
|
||||
export const Nav: FCWithChildren = ({ children }) => {
|
||||
const { updateNavState, navState } = useMainContext();
|
||||
const { updateNavState, navState, environment } = useMainContext();
|
||||
const [drawerIsOpen, setDrawerToOpen] = React.useState(false);
|
||||
const [fixedOpen, setFixedOpen] = React.useState(false);
|
||||
// Set maintenance banner to false by default to don't display it
|
||||
const [openMaintenance, setOpenMaintenance] = React.useState(false);
|
||||
const theme = useTheme();
|
||||
|
||||
const explorerName =
|
||||
`${environment && environment.charAt(0).toUpperCase() + environment.slice(1)} Explorer` || 'Mainnet Explorer';
|
||||
|
||||
const switchNetworkText = environment === 'mainnet' ? 'Switch to Testnet' : 'Switch to Mainnet';
|
||||
const switchNetworkLink =
|
||||
environment === 'mainnet' ? 'https://sandbox-explorer.nymtech.net' : 'https://explorer.nymtech.net';
|
||||
|
||||
const setToActive = (id: number) => {
|
||||
updateNavState(id);
|
||||
};
|
||||
@@ -302,8 +310,17 @@ export const Nav: FCWithChildren = ({ children }) => {
|
||||
}}
|
||||
>
|
||||
<MuiLink component={Link} to="/" underline="none" color="inherit">
|
||||
Network Explorer
|
||||
{explorerName}
|
||||
</MuiLink>
|
||||
<Button
|
||||
size="small"
|
||||
variant="outlined"
|
||||
color="inherit"
|
||||
href={switchNetworkLink}
|
||||
sx={{ borderRadius: 2, textTransform: 'none', width: 150, ml: 4, fontSize: 14, fontWeight: 600 }}
|
||||
>
|
||||
{switchNetworkText}
|
||||
</Button>
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box
|
||||
|
||||
@@ -54,7 +54,7 @@ export const DarkLightSwitch = styled(Switch)(({ theme }) => ({
|
||||
export const DarkLightSwitchMobile: FCWithChildren = () => {
|
||||
const { toggleMode } = useMainContext();
|
||||
return (
|
||||
<Button onClick={() => toggleMode()} data-testid="switch-button">
|
||||
<Button onClick={() => toggleMode()} data-testid="switch-button" sx={{ p: 0, minWidth: 0 }}>
|
||||
<LightSwitchSVG />
|
||||
</Button>
|
||||
);
|
||||
|
||||
@@ -10,9 +10,10 @@ import {
|
||||
MixnodeStatus,
|
||||
SummaryOverviewResponse,
|
||||
ValidatorsResponse,
|
||||
Environment,
|
||||
} from '../typeDefs/explorer-api';
|
||||
import { EnumFilterKey } from '../typeDefs/filters';
|
||||
import { Api } from '../api';
|
||||
import { Api, getEnvironment } from '../api';
|
||||
import { NavOptionType, originalNavOptions } from './nav';
|
||||
|
||||
interface StateData {
|
||||
@@ -25,6 +26,7 @@ interface StateData {
|
||||
mode: PaletteMode;
|
||||
navState: NavOptionType[];
|
||||
validators?: ApiState<ValidatorsResponse>;
|
||||
environment?: Environment;
|
||||
serviceProviders?: ApiState<DirectoryService>;
|
||||
}
|
||||
|
||||
@@ -49,6 +51,9 @@ export const MainContext = React.createContext<State>({
|
||||
export const useMainContext = (): React.ContextType<typeof MainContext> => React.useContext<State>(MainContext);
|
||||
|
||||
export const MainContextProvider: FCWithChildren = ({ children }) => {
|
||||
// network explorer environment
|
||||
const [environment, setEnvironment] = React.useState<Environment>('mainnet');
|
||||
|
||||
// light/dark mode
|
||||
const [mode, setMode] = React.useState<PaletteMode>('dark');
|
||||
|
||||
@@ -190,10 +195,12 @@ export const MainContextProvider: FCWithChildren = ({ children }) => {
|
||||
fetchCountryData(),
|
||||
fetchServiceProviders(),
|
||||
]);
|
||||
setEnvironment(getEnvironment());
|
||||
}, []);
|
||||
|
||||
const state = React.useMemo<State>(
|
||||
() => ({
|
||||
environment,
|
||||
block,
|
||||
countryData,
|
||||
fetchMixnodes,
|
||||
@@ -210,6 +217,7 @@ export const MainContextProvider: FCWithChildren = ({ children }) => {
|
||||
serviceProviders,
|
||||
}),
|
||||
[
|
||||
environment,
|
||||
block,
|
||||
countryData,
|
||||
gateways,
|
||||
|
||||
@@ -111,7 +111,12 @@ const PageGatewayDetailsWithState = ({ selectedGateway }: { selectedGateway: Gat
|
||||
{uptimeStory && (
|
||||
<ContentCard title="Routing Score">
|
||||
{uptimeStory.error && <ComponentError text="There was a problem retrieving routing score." />}
|
||||
<UptimeChart loading={uptimeStory.isLoading} xLabel="date" uptimeStory={uptimeStory} />
|
||||
<UptimeChart
|
||||
loading={uptimeStory.isLoading}
|
||||
xLabel="Date"
|
||||
yLabel="Daily average"
|
||||
uptimeStory={uptimeStory}
|
||||
/>
|
||||
</ContentCard>
|
||||
)}
|
||||
</Grid>
|
||||
|
||||
@@ -153,9 +153,9 @@ export const PageGateways: FCWithChildren = () => {
|
||||
renderCell: (params: GridRenderCellParams) => (
|
||||
<MuiLink
|
||||
sx={{ ...cellStyles }}
|
||||
href={`${NYM_BIG_DIPPER}/account/${params.value}`}
|
||||
target="_blank"
|
||||
data-testid="owner"
|
||||
component={RRDLink}
|
||||
to={`/network-components/gateway/${params.row.identity_key}`}
|
||||
data-testid="version"
|
||||
>
|
||||
{params.value}
|
||||
</MuiLink>
|
||||
|
||||
@@ -12,6 +12,7 @@ import { WorldMap } from '../../components/WorldMap';
|
||||
import { MixNodeDetailSection } from '../../components/MixNodes/DetailSection';
|
||||
import { MixnodeContextProvider, useMixnodeContext } from '../../context/mixnode';
|
||||
import { Title } from '../../components/Title';
|
||||
import { useIsMobile } from '../../hooks/useIsMobile';
|
||||
|
||||
const columns: ColumnsType[] = [
|
||||
{
|
||||
@@ -41,6 +42,7 @@ const columns: ColumnsType[] = [
|
||||
field: 'self_percentage',
|
||||
width: '10%',
|
||||
title: 'Bond %',
|
||||
tooltipInfo: "Percentage of the operator's bond to the total stake on the node",
|
||||
},
|
||||
|
||||
{
|
||||
@@ -64,7 +66,7 @@ const columns: ColumnsType[] = [
|
||||
*/
|
||||
const PageMixnodeDetailWithState: FCWithChildren = () => {
|
||||
const { mixNode, mixNodeRow, description, stats, status, uptimeStory, uniqDelegations } = useMixnodeContext();
|
||||
console.log(mixNodeRow);
|
||||
const isMobile = useIsMobile();
|
||||
return (
|
||||
<Box component="main">
|
||||
<Title text="Mixnode Detail" />
|
||||
@@ -73,6 +75,11 @@ const PageMixnodeDetailWithState: FCWithChildren = () => {
|
||||
{mixNodeRow && description?.data && (
|
||||
<MixNodeDetailSection mixNodeRow={mixNodeRow} mixnodeDescription={description.data} />
|
||||
)}
|
||||
{mixNodeRow?.blacklisted && (
|
||||
<Typography textAlign={isMobile ? 'left' : 'right'} fontSize="smaller" sx={{ color: 'error.main' }}>
|
||||
This node is having a poor performance
|
||||
</Typography>
|
||||
)}
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid container>
|
||||
@@ -134,7 +141,12 @@ const PageMixnodeDetailWithState: FCWithChildren = () => {
|
||||
{uptimeStory && (
|
||||
<ContentCard title="Routing Score">
|
||||
{uptimeStory.error && <ComponentError text="There was a problem retrieving routing score." />}
|
||||
<UptimeChart loading={uptimeStory.isLoading} xLabel="date" uptimeStory={uptimeStory} />
|
||||
<UptimeChart
|
||||
loading={uptimeStory.isLoading}
|
||||
xLabel="Date"
|
||||
yLabel="Daily average"
|
||||
uptimeStory={uptimeStory}
|
||||
/>
|
||||
</ContentCard>
|
||||
)}
|
||||
</Grid>
|
||||
|
||||
@@ -22,9 +22,10 @@ const SupportedApps = () => {
|
||||
<FormControl size="small">
|
||||
<Select value={selected} onChange={handleChange} displayEmpty sx={{ mr: 2 }}>
|
||||
<MenuItem value="">Supported Apps</MenuItem>
|
||||
<MenuItem sx={{ opacity: 1 }}>Keybase</MenuItem>
|
||||
<MenuItem>Keybase</MenuItem>
|
||||
<MenuItem>Telegram</MenuItem>
|
||||
<MenuItem>Electrum</MenuItem>
|
||||
<MenuItem>Blockstream Green</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
);
|
||||
|
||||
@@ -89,6 +89,7 @@ export interface MixNodeResponseItem {
|
||||
uncapped_saturation: number;
|
||||
operating_cost: Amount;
|
||||
profit_margin_percent: string;
|
||||
blacklisted: boolean;
|
||||
}
|
||||
|
||||
export type MixNodeResponse = MixNodeResponseItem[];
|
||||
@@ -238,6 +239,8 @@ export type MixNodeEconomicDynamicsStatsResponse = {
|
||||
current_interval_uptime: number;
|
||||
};
|
||||
|
||||
export type Environment = 'mainnet' | 'sandbox' | 'qa';
|
||||
|
||||
export type DirectoryServiceProvider = {
|
||||
id: string;
|
||||
description: string;
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
|
||||
[package]
|
||||
name = "nym-gateway"
|
||||
version = "1.1.12"
|
||||
version = "1.1.13"
|
||||
authors = [
|
||||
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
|
||||
"Jędrzej Stuczyński <andrew@nymtech.net>",
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
|
||||
[package]
|
||||
name = "nym-mixnode"
|
||||
version = "1.1.13"
|
||||
version = "1.1.14"
|
||||
authors = [
|
||||
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
|
||||
"Jędrzej Stuczyński <andrew@nymtech.net>",
|
||||
|
||||
+6
-6
@@ -3,7 +3,7 @@
|
||||
|
||||
[package]
|
||||
name = "nym-api"
|
||||
version = "1.1.13"
|
||||
version = "1.1.14"
|
||||
authors = [
|
||||
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
|
||||
"Jędrzej Stuczyński <andrew@nymtech.net>",
|
||||
@@ -68,12 +68,12 @@ nym-coconut-bandwidth-contract-common = { path = "../common/cosmwasm-smart-contr
|
||||
nym-coconut-dkg-common = { path = "../common/cosmwasm-smart-contracts/coconut-dkg" }
|
||||
nym-coconut-interface = { path = "../common/coconut-interface" }
|
||||
nym-config = { path = "../common/config" }
|
||||
cosmwasm-std = "1.0.0"
|
||||
cosmwasm-std = { workspace = true }
|
||||
nym-credential-storage = { path = "../common/credential-storage" }
|
||||
nym-credentials = { path = "../common/credentials" }
|
||||
nym-crypto = { path = "../common/crypto" }
|
||||
cw3 = { version = "0.13.4" }
|
||||
cw4 = { version = "0.13.4" }
|
||||
cw3 = { workspace = true }
|
||||
cw4 = { workspace = true }
|
||||
nym-dkg = { path = "../common/dkg", features = ["cw-types"] }
|
||||
gateway-client = { path = "../common/client-libs/gateway-client" }
|
||||
nym-inclusion-probability = { path = "../common/inclusion-probability" }
|
||||
@@ -106,5 +106,5 @@ sqlx = { version = "0.6.2", features = [
|
||||
] }
|
||||
|
||||
[dev-dependencies]
|
||||
cw3 = "0.13.4"
|
||||
cw-utils = "0.13.4"
|
||||
cw3 = { workspace = true }
|
||||
cw-utils = { workspace = true }
|
||||
|
||||
@@ -8,7 +8,7 @@ edition = "2021"
|
||||
[dependencies]
|
||||
bs58 = "0.4.0"
|
||||
cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support" }
|
||||
cosmwasm-std = { version = "1.0.0", default-features = false }
|
||||
cosmwasm-std = { workspace = true, default-features = false }
|
||||
getset = "0.1.1"
|
||||
schemars = { version = "0.8", features = ["preserve_order"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
||||
@@ -111,6 +111,7 @@ pub struct MixNodeBondAnnotated {
|
||||
pub estimated_operator_apy: Decimal,
|
||||
pub estimated_delegators_apy: Decimal,
|
||||
pub family: Option<FamilyHead>,
|
||||
pub blacklisted: bool,
|
||||
}
|
||||
|
||||
impl MixNodeBondAnnotated {
|
||||
@@ -137,6 +138,7 @@ pub struct GatewayBondAnnotated {
|
||||
// NOTE: the performance field is deprecated in favour of node_performance
|
||||
pub performance: Performance,
|
||||
pub node_performance: NodePerformance,
|
||||
pub blacklisted: bool,
|
||||
}
|
||||
|
||||
impl GatewayBondAnnotated {
|
||||
|
||||
@@ -102,7 +102,7 @@ impl RewardedSetUpdater {
|
||||
|
||||
let epoch_end = interval.current_epoch_end();
|
||||
|
||||
let all_mixnodes = self.nym_contract_cache.mixnodes().await;
|
||||
let all_mixnodes = self.nym_contract_cache.mixnodes_filtered().await;
|
||||
if all_mixnodes.is_empty() {
|
||||
// that's a bit weird, but
|
||||
log::warn!("there don't seem to be any mixnodes on the network!")
|
||||
|
||||
+2
-1
@@ -21,6 +21,7 @@ use node_status_api::NodeStatusCache;
|
||||
use nym_bin_common::logging::setup_logging;
|
||||
use nym_config::NymConfig;
|
||||
use nym_contract_cache::cache::NymContractCache;
|
||||
use nym_sphinx::receiver::SphinxMessageReceiver;
|
||||
use nym_task::TaskManager;
|
||||
use rand::rngs::OsRng;
|
||||
use std::error::Error;
|
||||
@@ -127,7 +128,7 @@ async fn start_nym_api_tasks(
|
||||
// if network monitor is enabled, the storage MUST BE available
|
||||
let storage = maybe_storage.unwrap();
|
||||
|
||||
network_monitor::start(
|
||||
network_monitor::start::<SphinxMessageReceiver>(
|
||||
&config,
|
||||
nym_contract_cache_state,
|
||||
storage,
|
||||
|
||||
@@ -20,6 +20,7 @@ use futures::channel::mpsc;
|
||||
use gateway_client::bandwidth::BandwidthController;
|
||||
use nym_credential_storage::PersistentStorage;
|
||||
use nym_crypto::asymmetric::{encryption, identity};
|
||||
use nym_sphinx::receiver::MessageReceiver;
|
||||
use nym_task::TaskManager;
|
||||
use std::sync::Arc;
|
||||
use validator_client::nyxd::SigningNyxdClient;
|
||||
@@ -32,7 +33,7 @@ pub(crate) mod test_route;
|
||||
|
||||
pub(crate) const ROUTE_TESTING_TEST_NONCE: u64 = 0;
|
||||
|
||||
pub(crate) fn setup<'a>(
|
||||
pub(crate) fn setup<'a, R: MessageReceiver>(
|
||||
config: &'a Config,
|
||||
nym_contract_cache_state: &NymContractCache,
|
||||
storage: &NymApiStorage,
|
||||
@@ -73,7 +74,9 @@ impl<'a> NetworkMonitorBuilder<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn build(self) -> NetworkMonitorRunnables {
|
||||
pub(crate) async fn build<R: MessageReceiver + Send + 'static>(
|
||||
self,
|
||||
) -> NetworkMonitorRunnables<R> {
|
||||
// TODO: those keys change constant throughout the whole execution of the monitor.
|
||||
// and on top of that, they are used with ALL the gateways -> presumably this should change
|
||||
// in the future
|
||||
@@ -140,12 +143,12 @@ impl<'a> NetworkMonitorBuilder<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct NetworkMonitorRunnables {
|
||||
monitor: Monitor,
|
||||
pub(crate) struct NetworkMonitorRunnables<R: MessageReceiver + Send + 'static> {
|
||||
monitor: Monitor<R>,
|
||||
packet_receiver: PacketReceiver,
|
||||
}
|
||||
|
||||
impl NetworkMonitorRunnables {
|
||||
impl<R: MessageReceiver + Send + 'static> NetworkMonitorRunnables<R> {
|
||||
// TODO: note, that is not exactly doing what we want, because when
|
||||
// `ReceivedProcessor` is constructed, it already spawns a future
|
||||
// this needs to be refactored!
|
||||
@@ -195,10 +198,10 @@ fn new_packet_sender(
|
||||
)
|
||||
}
|
||||
|
||||
fn new_received_processor(
|
||||
fn new_received_processor<R: MessageReceiver + Send + 'static>(
|
||||
packets_receiver: ReceivedProcessorReceiver,
|
||||
client_encryption_keypair: Arc<encryption::KeyPair>,
|
||||
) -> ReceivedProcessor {
|
||||
) -> ReceivedProcessor<R> {
|
||||
ReceivedProcessor::new(packets_receiver, client_encryption_keypair)
|
||||
}
|
||||
|
||||
@@ -217,7 +220,7 @@ fn new_packet_receiver(
|
||||
|
||||
// TODO: 1) does it still have to have separate builder or could we get rid of it now?
|
||||
// TODO: 2) how do we make it non-async as other 'start' methods?
|
||||
pub(crate) async fn start(
|
||||
pub(crate) async fn start<R: MessageReceiver + Send + 'static>(
|
||||
config: &Config,
|
||||
nym_contract_cache_state: &NymContractCache,
|
||||
storage: &NymApiStorage,
|
||||
@@ -225,7 +228,7 @@ pub(crate) async fn start(
|
||||
system_version: &str,
|
||||
shutdown: &TaskManager,
|
||||
) {
|
||||
let monitor_builder = network_monitor::setup(
|
||||
let monitor_builder = network_monitor::setup::<R>(
|
||||
config,
|
||||
nym_contract_cache_state,
|
||||
storage,
|
||||
@@ -233,6 +236,6 @@ pub(crate) async fn start(
|
||||
system_version,
|
||||
);
|
||||
info!("Starting network monitor...");
|
||||
let runnables = monitor_builder.build().await;
|
||||
let runnables: NetworkMonitorRunnables<R> = monitor_builder.build().await;
|
||||
runnables.spawn_tasks(shutdown);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ use crate::network_monitor::test_route::TestRoute;
|
||||
use crate::storage::NymApiStorage;
|
||||
use crate::support::config::Config;
|
||||
use log::{debug, error, info};
|
||||
use nym_sphinx::receiver::MessageReceiver;
|
||||
use nym_task::TaskClient;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::process;
|
||||
@@ -23,11 +24,11 @@ pub(crate) mod receiver;
|
||||
pub(crate) mod sender;
|
||||
pub(crate) mod summary_producer;
|
||||
|
||||
pub(super) struct Monitor {
|
||||
pub(super) struct Monitor<R: MessageReceiver + Send + 'static> {
|
||||
test_nonce: u64,
|
||||
packet_preparer: PacketPreparer,
|
||||
packet_sender: PacketSender,
|
||||
received_processor: ReceivedProcessor,
|
||||
received_processor: ReceivedProcessor<R>,
|
||||
summary_producer: SummaryProducer,
|
||||
node_status_storage: NymApiStorage,
|
||||
run_interval: Duration,
|
||||
@@ -45,12 +46,12 @@ pub(super) struct Monitor {
|
||||
minimum_test_routes: usize,
|
||||
}
|
||||
|
||||
impl Monitor {
|
||||
impl<R: MessageReceiver + Send> Monitor<R> {
|
||||
pub(super) fn new(
|
||||
config: &Config,
|
||||
packet_preparer: PacketPreparer,
|
||||
packet_sender: PacketSender,
|
||||
received_processor: ReceivedProcessor,
|
||||
received_processor: ReceivedProcessor<R>,
|
||||
summary_producer: SummaryProducer,
|
||||
node_status_storage: NymApiStorage,
|
||||
) -> Self {
|
||||
|
||||
@@ -43,7 +43,7 @@ enum LockPermit {
|
||||
Free,
|
||||
}
|
||||
|
||||
struct ReceivedProcessorInner {
|
||||
struct ReceivedProcessorInner<R: MessageReceiver> {
|
||||
/// Nonce of the current test run indicating which packets should get rejected.
|
||||
test_nonce: Option<u64>,
|
||||
|
||||
@@ -55,13 +55,13 @@ struct ReceivedProcessorInner {
|
||||
client_encryption_keypair: Arc<encryption::KeyPair>,
|
||||
|
||||
/// Structure responsible for decrypting and recovering plaintext message from received ciphertexts.
|
||||
message_receiver: MessageReceiver,
|
||||
message_receiver: R,
|
||||
|
||||
/// Vector containing all received (and decrypted) packets in the current test run.
|
||||
received_packets: Vec<TestPacket>,
|
||||
}
|
||||
|
||||
impl ReceivedProcessorInner {
|
||||
impl<R: MessageReceiver> ReceivedProcessorInner<R> {
|
||||
fn on_message(&mut self, mut message: Vec<u8>) -> Result<(), ProcessingError> {
|
||||
// if the nonce is none it means the packet was received during the 'waiting' for the
|
||||
// next test run
|
||||
@@ -101,22 +101,22 @@ impl ReceivedProcessorInner {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct ReceivedProcessor {
|
||||
pub(crate) struct ReceivedProcessor<R: MessageReceiver> {
|
||||
permit_changer: Option<mpsc::Sender<LockPermit>>,
|
||||
inner: Arc<Mutex<ReceivedProcessorInner>>,
|
||||
inner: Arc<Mutex<ReceivedProcessorInner<R>>>,
|
||||
}
|
||||
|
||||
impl ReceivedProcessor {
|
||||
impl<R: MessageReceiver + Send + 'static> ReceivedProcessor<R> {
|
||||
pub(crate) fn new(
|
||||
packets_receiver: ReceivedProcessorReceiver,
|
||||
client_encryption_keypair: Arc<encryption::KeyPair>,
|
||||
) -> Self {
|
||||
let inner: Arc<Mutex<ReceivedProcessorInner>> =
|
||||
let inner: Arc<Mutex<ReceivedProcessorInner<R>>> =
|
||||
Arc::new(Mutex::new(ReceivedProcessorInner {
|
||||
test_nonce: None,
|
||||
packets_receiver,
|
||||
client_encryption_keypair,
|
||||
message_receiver: MessageReceiver::new(),
|
||||
message_receiver: R::new(),
|
||||
received_packets: Vec::new(),
|
||||
}));
|
||||
|
||||
@@ -138,9 +138,9 @@ impl ReceivedProcessor {
|
||||
receive_or_release_permit(&mut permit_receiver, permit).await;
|
||||
}
|
||||
|
||||
async fn receive_or_release_permit(
|
||||
async fn receive_or_release_permit<Q: MessageReceiver>(
|
||||
permit_receiver: &mut mpsc::Receiver<LockPermit>,
|
||||
mut inner: MutexGuard<'_, ReceivedProcessorInner>,
|
||||
mut inner: MutexGuard<'_, ReceivedProcessorInner<Q>>,
|
||||
) {
|
||||
loop {
|
||||
tokio::select! {
|
||||
@@ -166,10 +166,10 @@ impl ReceivedProcessor {
|
||||
// // this lint really looks like a false positive because when lifetimes are elided,
|
||||
// // the compiler can't figure out appropriate lifetime bounds
|
||||
// #[allow(clippy::needless_lifetimes)]
|
||||
async fn wait_for_permit<'a: 'b, 'b>(
|
||||
async fn wait_for_permit<'a: 'b, 'b, P: MessageReceiver>(
|
||||
permit_receiver: &'b mut mpsc::Receiver<LockPermit>,
|
||||
inner: &'a Mutex<ReceivedProcessorInner>,
|
||||
) -> Option<MutexGuard<'a, ReceivedProcessorInner>> {
|
||||
inner: &'a Mutex<ReceivedProcessorInner<P>>,
|
||||
) -> Option<MutexGuard<'a, ReceivedProcessorInner<P>>> {
|
||||
loop {
|
||||
match permit_receiver.next().await {
|
||||
// we should only ever get this on the very first run
|
||||
|
||||
+13
-3
@@ -89,10 +89,15 @@ impl NodeStatusCache {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn mixnodes_annotated(&self) -> Option<Cache<Vec<MixNodeBondAnnotated>>> {
|
||||
pub(crate) async fn mixnodes_annotated_full(&self) -> Option<Cache<Vec<MixNodeBondAnnotated>>> {
|
||||
self.get(|c| c.mixnodes_annotated.clone()).await
|
||||
}
|
||||
|
||||
pub(crate) async fn mixnodes_annotated_filtered(&self) -> Option<Vec<MixNodeBondAnnotated>> {
|
||||
let full = self.mixnodes_annotated_full().await?;
|
||||
Some(full.value.into_iter().filter(|m| !m.blacklisted).collect())
|
||||
}
|
||||
|
||||
pub(crate) async fn rewarded_set_annotated(&self) -> Option<Cache<Vec<MixNodeBondAnnotated>>> {
|
||||
self.get(|c| c.rewarded_set_annotated.clone()).await
|
||||
}
|
||||
@@ -101,10 +106,15 @@ impl NodeStatusCache {
|
||||
self.get(|c| c.active_set_annotated.clone()).await
|
||||
}
|
||||
|
||||
pub(crate) async fn gateways_annotated(&self) -> Option<Cache<Vec<GatewayBondAnnotated>>> {
|
||||
pub(crate) async fn gateways_annotated_full(&self) -> Option<Cache<Vec<GatewayBondAnnotated>>> {
|
||||
self.get(|c| c.gateways_annotated.clone()).await
|
||||
}
|
||||
|
||||
pub(crate) async fn gateways_annotated_filtered(&self) -> Option<Vec<GatewayBondAnnotated>> {
|
||||
let full = self.gateways_annotated_full().await?;
|
||||
Some(full.value.into_iter().filter(|m| !m.blacklisted).collect())
|
||||
}
|
||||
|
||||
pub(crate) async fn inclusion_probabilities(&self) -> Option<Cache<InclusionProbabilities>> {
|
||||
self.get(|c| c.inclusion_probabilities.clone()).await
|
||||
}
|
||||
@@ -126,7 +136,7 @@ impl NodeStatusCache {
|
||||
return (Some(bond.clone()), MixnodeStatus::Standby);
|
||||
}
|
||||
|
||||
let all_bonded = &self.mixnodes_annotated().await.unwrap().into_inner();
|
||||
let all_bonded = &self.mixnodes_annotated_filtered().await.unwrap();
|
||||
if let Some(bond) = all_bonded.iter().find(|mix| mix.mix_id() == mix_id) {
|
||||
(Some(bond.clone()), MixnodeStatus::Inactive)
|
||||
} else {
|
||||
|
||||
+5
-1
@@ -6,7 +6,7 @@ use nym_mixnet_contract_common::{reward_params::Performance, Interval, MixId};
|
||||
use nym_mixnet_contract_common::{
|
||||
GatewayBond, IdentityKey, MixNodeDetails, RewardedSetNodeStatus, RewardingParams,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
pub(super) fn to_rewarded_set_node_status(
|
||||
rewarded_set: &[MixNodeDetails],
|
||||
@@ -84,6 +84,7 @@ pub(super) async fn annotate_nodes_with_details(
|
||||
current_interval: Interval,
|
||||
rewarded_set: &HashMap<MixId, RewardedSetNodeStatus>,
|
||||
mix_to_family: Vec<(IdentityKey, FamilyHead)>,
|
||||
blacklist: &HashSet<MixId>,
|
||||
) -> Vec<MixNodeBondAnnotated> {
|
||||
let mix_to_family = mix_to_family
|
||||
.into_iter()
|
||||
@@ -135,6 +136,7 @@ pub(super) async fn annotate_nodes_with_details(
|
||||
.cloned();
|
||||
|
||||
annotated.push(MixNodeBondAnnotated {
|
||||
blacklisted: blacklist.contains(&mixnode.mix_id()),
|
||||
mixnode_details: mixnode,
|
||||
stake_saturation,
|
||||
uncapped_stake_saturation,
|
||||
@@ -152,6 +154,7 @@ pub(crate) async fn annotate_gateways_with_details(
|
||||
storage: &Option<NymApiStorage>,
|
||||
gateway_bonds: Vec<GatewayBond>,
|
||||
current_interval: Interval,
|
||||
blacklist: &HashSet<IdentityKey>,
|
||||
) -> Vec<GatewayBondAnnotated> {
|
||||
let mut annotated = Vec::new();
|
||||
for gateway_bond in gateway_bonds {
|
||||
@@ -175,6 +178,7 @@ pub(crate) async fn annotate_gateways_with_details(
|
||||
.unwrap_or_default();
|
||||
|
||||
annotated.push(GatewayBondAnnotated {
|
||||
blacklisted: blacklist.contains(&gateway_bond.gateway.identity_key),
|
||||
gateway_bond,
|
||||
performance,
|
||||
node_performance,
|
||||
|
||||
+14
-4
@@ -109,13 +109,17 @@ impl NodeStatusCacheRefresher {
|
||||
log::info!("Updating node status cache");
|
||||
|
||||
// Fetch contract cache data to work with
|
||||
let mixnode_details = self.contract_cache.mixnodes().await;
|
||||
let mixnode_details = self.contract_cache.mixnodes_all().await;
|
||||
let interval_reward_params = self.contract_cache.interval_reward_params().await;
|
||||
let current_interval = self.contract_cache.current_interval().await;
|
||||
let rewarded_set = self.contract_cache.rewarded_set().await;
|
||||
let active_set = self.contract_cache.active_set().await;
|
||||
let mix_to_family = self.contract_cache.mix_to_family().await;
|
||||
let gateway_bonds = self.contract_cache.gateways().await;
|
||||
let gateway_bonds = self.contract_cache.gateways_all().await;
|
||||
|
||||
// get blacklists
|
||||
let mixnodes_blacklist = self.contract_cache.mixnodes_blacklist().await;
|
||||
let gateways_blacklist = self.contract_cache.gateways_blacklist().await;
|
||||
|
||||
let interval_reward_params =
|
||||
interval_reward_params.ok_or(NodeStatusCacheError::SourceDataMissing)?;
|
||||
@@ -140,6 +144,7 @@ impl NodeStatusCacheRefresher {
|
||||
current_interval,
|
||||
&rewarded_set_node_status,
|
||||
mix_to_family.to_vec(),
|
||||
&mixnodes_blacklist,
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -147,8 +152,13 @@ impl NodeStatusCacheRefresher {
|
||||
let (rewarded_set, active_set) =
|
||||
split_into_active_and_rewarded_set(&mixnodes_annotated, &rewarded_set_node_status);
|
||||
|
||||
let gateways_annotated =
|
||||
annotate_gateways_with_details(&self.storage, gateway_bonds, current_interval).await;
|
||||
let gateways_annotated = annotate_gateways_with_details(
|
||||
&self.storage,
|
||||
gateway_bonds,
|
||||
current_interval,
|
||||
&gateways_blacklist,
|
||||
)
|
||||
.await;
|
||||
|
||||
// Update the cache
|
||||
self.cache
|
||||
|
||||
@@ -24,17 +24,19 @@ async fn get_gateway_bond_annotated(
|
||||
cache: &NodeStatusCache,
|
||||
identity: &str,
|
||||
) -> Result<GatewayBondAnnotated, ErrorResponse> {
|
||||
let gateways = cache.gateways_annotated().await.ok_or(ErrorResponse::new(
|
||||
"no data available",
|
||||
Status::ServiceUnavailable,
|
||||
))?;
|
||||
let gateways = cache
|
||||
.gateways_annotated_filtered()
|
||||
.await
|
||||
.ok_or(ErrorResponse::new(
|
||||
"no data available",
|
||||
Status::ServiceUnavailable,
|
||||
))?;
|
||||
|
||||
gateways
|
||||
.into_inner()
|
||||
.into_iter()
|
||||
.find(|gateway| gateway.identity() == identity)
|
||||
.ok_or(ErrorResponse::new(
|
||||
"mixnode bond not found",
|
||||
"gateway bond not found",
|
||||
Status::NotFound,
|
||||
))
|
||||
}
|
||||
@@ -43,13 +45,15 @@ async fn get_mixnode_bond_annotated(
|
||||
cache: &NodeStatusCache,
|
||||
mix_id: MixId,
|
||||
) -> Result<MixNodeBondAnnotated, ErrorResponse> {
|
||||
let mixnodes = cache.mixnodes_annotated().await.ok_or(ErrorResponse::new(
|
||||
"no data available",
|
||||
Status::ServiceUnavailable,
|
||||
))?;
|
||||
let mixnodes = cache
|
||||
.mixnodes_annotated_filtered()
|
||||
.await
|
||||
.ok_or(ErrorResponse::new(
|
||||
"no data available",
|
||||
Status::ServiceUnavailable,
|
||||
))?;
|
||||
|
||||
mixnodes
|
||||
.into_inner()
|
||||
.into_iter()
|
||||
.find(|mixnode| mixnode.mix_id() == mix_id)
|
||||
.ok_or(ErrorResponse::new(
|
||||
@@ -374,7 +378,16 @@ pub(crate) async fn _get_mixnode_inclusion_probabilities(
|
||||
|
||||
pub(crate) async fn _get_mixnodes_detailed(cache: &NodeStatusCache) -> Vec<MixNodeBondAnnotated> {
|
||||
cache
|
||||
.mixnodes_annotated()
|
||||
.mixnodes_annotated_filtered()
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub(crate) async fn _get_mixnodes_detailed_unfiltered(
|
||||
cache: &NodeStatusCache,
|
||||
) -> Vec<MixNodeBondAnnotated> {
|
||||
cache
|
||||
.mixnodes_annotated_full()
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
.into_inner()
|
||||
@@ -400,7 +413,16 @@ pub(crate) async fn _get_active_set_detailed(cache: &NodeStatusCache) -> Vec<Mix
|
||||
|
||||
pub(crate) async fn _get_gateways_detailed(cache: &NodeStatusCache) -> Vec<GatewayBondAnnotated> {
|
||||
cache
|
||||
.gateways_annotated()
|
||||
.gateways_annotated_filtered()
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub(crate) async fn _get_gateways_detailed_unfiltered(
|
||||
cache: &NodeStatusCache,
|
||||
) -> Vec<GatewayBondAnnotated> {
|
||||
cache
|
||||
.gateways_annotated_full()
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
.into_inner()
|
||||
|
||||
@@ -48,9 +48,11 @@ pub(crate) fn node_status_routes(
|
||||
routes::get_gateway_avg_uptime,
|
||||
routes::get_mixnode_inclusion_probabilities,
|
||||
routes::get_mixnodes_detailed,
|
||||
routes::get_mixnodes_detailed_unfiltered,
|
||||
routes::get_rewarded_set_detailed,
|
||||
routes::get_active_set_detailed,
|
||||
routes::get_gateways_detailed,
|
||||
routes::get_gateways_detailed_unfiltered,
|
||||
]
|
||||
} else {
|
||||
// in the minimal variant we would not have access to endpoints relying on existence
|
||||
|
||||
@@ -6,11 +6,11 @@ use super::NodeStatusCache;
|
||||
use crate::node_status_api::helpers::{
|
||||
_compute_mixnode_reward_estimation, _gateway_core_status_count, _gateway_report,
|
||||
_gateway_uptime_history, _get_active_set_detailed, _get_gateway_avg_uptime,
|
||||
_get_mixnode_avg_uptime, _get_mixnode_inclusion_probabilities,
|
||||
_get_mixnode_inclusion_probability, _get_mixnode_reward_estimation,
|
||||
_get_mixnode_stake_saturation, _get_mixnode_status, _get_mixnodes_detailed,
|
||||
_get_rewarded_set_detailed, _mixnode_core_status_count, _mixnode_report,
|
||||
_mixnode_uptime_history,
|
||||
_get_gateways_detailed_unfiltered, _get_mixnode_avg_uptime,
|
||||
_get_mixnode_inclusion_probabilities, _get_mixnode_inclusion_probability,
|
||||
_get_mixnode_reward_estimation, _get_mixnode_stake_saturation, _get_mixnode_status,
|
||||
_get_mixnodes_detailed, _get_mixnodes_detailed_unfiltered, _get_rewarded_set_detailed,
|
||||
_mixnode_core_status_count, _mixnode_report, _mixnode_uptime_history,
|
||||
};
|
||||
use crate::node_status_api::models::ErrorResponse;
|
||||
use crate::storage::NymApiStorage;
|
||||
@@ -188,6 +188,14 @@ pub async fn get_mixnodes_detailed(
|
||||
Json(_get_mixnodes_detailed(cache).await)
|
||||
}
|
||||
|
||||
#[openapi(tag = "status")]
|
||||
#[get("/mixnodes/detailed-unfiltered")]
|
||||
pub async fn get_mixnodes_detailed_unfiltered(
|
||||
cache: &State<NodeStatusCache>,
|
||||
) -> Json<Vec<MixNodeBondAnnotated>> {
|
||||
Json(_get_mixnodes_detailed_unfiltered(cache).await)
|
||||
}
|
||||
|
||||
#[openapi(tag = "status")]
|
||||
#[get("/mixnodes/rewarded/detailed")]
|
||||
pub async fn get_rewarded_set_detailed(
|
||||
@@ -211,3 +219,11 @@ pub async fn get_gateways_detailed(
|
||||
) -> Json<Vec<GatewayBondAnnotated>> {
|
||||
Json(_get_gateways_detailed(cache).await)
|
||||
}
|
||||
|
||||
#[openapi(tag = "status")]
|
||||
#[get("/gateways/detailed-unfiltered")]
|
||||
pub async fn get_gateways_detailed_unfiltered(
|
||||
cache: &State<NodeStatusCache>,
|
||||
) -> Json<Vec<GatewayBondAnnotated>> {
|
||||
Json(_get_gateways_detailed_unfiltered(cache).await)
|
||||
}
|
||||
|
||||
+69
-72
@@ -67,50 +67,48 @@ impl NymContractCache {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn mixnodes_blacklist(&self) -> Option<Cache<HashSet<MixId>>> {
|
||||
pub async fn mixnodes_blacklist(&self) -> Cache<HashSet<MixId>> {
|
||||
match time::timeout(Duration::from_millis(100), self.inner.read()).await {
|
||||
Ok(cache) => Some(cache.mixnodes_blacklist.clone()),
|
||||
Ok(cache) => cache.mixnodes_blacklist.clone(),
|
||||
Err(err) => {
|
||||
error!("{err}");
|
||||
None
|
||||
Cache::new(HashSet::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn gateways_blacklist(&self) -> Option<Cache<HashSet<IdentityKey>>> {
|
||||
pub async fn gateways_blacklist(&self) -> Cache<HashSet<IdentityKey>> {
|
||||
match time::timeout(Duration::from_millis(100), self.inner.read()).await {
|
||||
Ok(cache) => Some(cache.gateways_blacklist.clone()),
|
||||
Ok(cache) => cache.gateways_blacklist.clone(),
|
||||
Err(err) => {
|
||||
error!("{err}");
|
||||
None
|
||||
Cache::new(HashSet::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn update_mixnodes_blacklist(&self, add: HashSet<MixId>, remove: HashSet<MixId>) {
|
||||
let blacklist = self.mixnodes_blacklist().await;
|
||||
if let Some(blacklist) = blacklist {
|
||||
let mut blacklist = blacklist
|
||||
.value
|
||||
.union(&add)
|
||||
.cloned()
|
||||
.collect::<HashSet<MixId>>();
|
||||
let to_remove = blacklist
|
||||
.intersection(&remove)
|
||||
.cloned()
|
||||
.collect::<HashSet<MixId>>();
|
||||
for key in to_remove {
|
||||
blacklist.remove(&key);
|
||||
let mut blacklist = blacklist
|
||||
.value
|
||||
.union(&add)
|
||||
.cloned()
|
||||
.collect::<HashSet<MixId>>();
|
||||
let to_remove = blacklist
|
||||
.intersection(&remove)
|
||||
.cloned()
|
||||
.collect::<HashSet<MixId>>();
|
||||
for key in to_remove {
|
||||
blacklist.remove(&key);
|
||||
}
|
||||
match time::timeout(Duration::from_millis(100), self.inner.write()).await {
|
||||
Ok(mut cache) => {
|
||||
cache.mixnodes_blacklist.update(blacklist);
|
||||
}
|
||||
match time::timeout(Duration::from_millis(100), self.inner.write()).await {
|
||||
Ok(mut cache) => {
|
||||
cache.mixnodes_blacklist.update(blacklist);
|
||||
return;
|
||||
}
|
||||
Err(err) => error!("{err}"),
|
||||
Err(err) => {
|
||||
error!("Failed to update mixnodes blacklist: {err}");
|
||||
}
|
||||
}
|
||||
error!("Failed to update mixnodes blacklist");
|
||||
}
|
||||
|
||||
pub async fn update_gateways_blacklist(
|
||||
@@ -119,49 +117,52 @@ impl NymContractCache {
|
||||
remove: HashSet<IdentityKey>,
|
||||
) {
|
||||
let blacklist = self.gateways_blacklist().await;
|
||||
if let Some(blacklist) = blacklist {
|
||||
let mut blacklist = blacklist
|
||||
.value
|
||||
.union(&add)
|
||||
.cloned()
|
||||
.collect::<HashSet<IdentityKey>>();
|
||||
let to_remove = blacklist
|
||||
.intersection(&remove)
|
||||
.cloned()
|
||||
.collect::<HashSet<IdentityKey>>();
|
||||
for key in to_remove {
|
||||
blacklist.remove(&key);
|
||||
let mut blacklist = blacklist
|
||||
.value
|
||||
.union(&add)
|
||||
.cloned()
|
||||
.collect::<HashSet<IdentityKey>>();
|
||||
let to_remove = blacklist
|
||||
.intersection(&remove)
|
||||
.cloned()
|
||||
.collect::<HashSet<IdentityKey>>();
|
||||
for key in to_remove {
|
||||
blacklist.remove(&key);
|
||||
}
|
||||
match time::timeout(Duration::from_millis(100), self.inner.write()).await {
|
||||
Ok(mut cache) => {
|
||||
cache.gateways_blacklist.update(blacklist);
|
||||
}
|
||||
match time::timeout(Duration::from_millis(100), self.inner.write()).await {
|
||||
Ok(mut cache) => {
|
||||
cache.gateways_blacklist.update(blacklist);
|
||||
return;
|
||||
}
|
||||
Err(err) => error!("{err}"),
|
||||
Err(err) => {
|
||||
error!("Failed to update gateways blacklist: {err}");
|
||||
}
|
||||
}
|
||||
error!("Failed to update gateways blacklist");
|
||||
}
|
||||
|
||||
pub async fn mixnodes(&self) -> Vec<MixNodeDetails> {
|
||||
pub async fn mixnodes_filtered(&self) -> Vec<MixNodeDetails> {
|
||||
let mixnodes = self.mixnodes_all().await;
|
||||
if mixnodes.is_empty() {
|
||||
return Vec::new();
|
||||
}
|
||||
let blacklist = self.mixnodes_blacklist().await;
|
||||
let mixnodes = match time::timeout(Duration::from_millis(100), self.inner.read()).await {
|
||||
Ok(cache) => cache.mixnodes.clone(),
|
||||
Err(err) => {
|
||||
error!("{err}");
|
||||
return Vec::new();
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(blacklist) = blacklist {
|
||||
if !blacklist.is_empty() {
|
||||
mixnodes
|
||||
.value
|
||||
.iter()
|
||||
.into_iter()
|
||||
.filter(|mix| !blacklist.value.contains(&mix.mix_id()))
|
||||
.cloned()
|
||||
.collect()
|
||||
} else {
|
||||
mixnodes.value
|
||||
mixnodes
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn mixnodes_all(&self) -> Vec<MixNodeDetails> {
|
||||
match time::timeout(Duration::from_millis(100), self.inner.read()).await {
|
||||
Ok(cache) => cache.mixnodes.clone().value,
|
||||
Err(err) => {
|
||||
error!("{err}");
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,25 +182,21 @@ impl NymContractCache {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn gateways(&self) -> Vec<GatewayBond> {
|
||||
let blacklist = self.gateways_blacklist().await;
|
||||
let gateways = match time::timeout(Duration::from_millis(100), self.inner.read()).await {
|
||||
Ok(cache) => cache.gateways.clone(),
|
||||
Err(err) => {
|
||||
error!("{err}");
|
||||
return Vec::new();
|
||||
}
|
||||
};
|
||||
pub async fn gateways_filtered(&self) -> Vec<GatewayBond> {
|
||||
let gateways = self.gateways_all().await;
|
||||
if gateways.is_empty() {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
if let Some(blacklist) = blacklist {
|
||||
let blacklist = self.gateways_blacklist().await;
|
||||
|
||||
if !blacklist.is_empty() {
|
||||
gateways
|
||||
.value
|
||||
.iter()
|
||||
.into_iter()
|
||||
.filter(|mix| !blacklist.value.contains(mix.identity()))
|
||||
.cloned()
|
||||
.collect()
|
||||
} else {
|
||||
gateways.value
|
||||
gateways
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,7 +274,7 @@ impl NymContractCache {
|
||||
return (Some(bond.clone()), MixnodeStatus::Standby);
|
||||
}
|
||||
|
||||
let all_bonded = &self.mixnodes().await;
|
||||
let all_bonded = &self.mixnodes_filtered().await;
|
||||
if let Some(bond) = all_bonded.iter().find(|mix| mix.mix_id() == mix_id) {
|
||||
(Some(bond.clone()), MixnodeStatus::Inactive)
|
||||
} else {
|
||||
|
||||
@@ -20,7 +20,7 @@ use std::collections::HashSet;
|
||||
#[openapi(tag = "contract-cache")]
|
||||
#[get("/mixnodes")]
|
||||
pub async fn get_mixnodes(cache: &State<NymContractCache>) -> Json<Vec<MixNodeDetails>> {
|
||||
Json(cache.mixnodes().await)
|
||||
Json(cache.mixnodes_filtered().await)
|
||||
}
|
||||
|
||||
// DEPRECATED: this endpoint now lives in `node_status_api`. Once all consumers are updated,
|
||||
@@ -41,7 +41,7 @@ pub async fn get_mixnodes_detailed(
|
||||
#[openapi(tag = "contract-cache")]
|
||||
#[get("/gateways")]
|
||||
pub async fn get_gateways(cache: &State<NymContractCache>) -> Json<Vec<GatewayBond>> {
|
||||
Json(cache.gateways().await)
|
||||
Json(cache.gateways_filtered().await)
|
||||
}
|
||||
|
||||
#[openapi(tag = "contract-cache")]
|
||||
@@ -91,7 +91,12 @@ pub async fn get_active_set_detailed(
|
||||
pub async fn get_blacklisted_mixnodes(
|
||||
cache: &State<NymContractCache>,
|
||||
) -> Json<Option<HashSet<MixId>>> {
|
||||
Json(cache.mixnodes_blacklist().await.map(|c| c.value))
|
||||
let blacklist = cache.mixnodes_blacklist().await.value;
|
||||
if blacklist.is_empty() {
|
||||
Json(None)
|
||||
} else {
|
||||
Json(Some(blacklist))
|
||||
}
|
||||
}
|
||||
|
||||
#[openapi(tag = "contract-cache")]
|
||||
@@ -99,7 +104,12 @@ pub async fn get_blacklisted_mixnodes(
|
||||
pub async fn get_blacklisted_gateways(
|
||||
cache: &State<NymContractCache>,
|
||||
) -> Json<Option<HashSet<String>>> {
|
||||
Json(cache.gateways_blacklist().await.map(|c| c.value))
|
||||
let blacklist = cache.gateways_blacklist().await.value;
|
||||
if blacklist.is_empty() {
|
||||
Json(None)
|
||||
} else {
|
||||
Json(Some(blacklist))
|
||||
}
|
||||
}
|
||||
|
||||
#[openapi(tag = "contract-cache")]
|
||||
|
||||
Generated
+7346
File diff suppressed because it is too large
Load Diff
@@ -115,7 +115,6 @@
|
||||
"webpack": "^5.75.0",
|
||||
"webpack-cli": "^4.8.0",
|
||||
"webpack-dev-server": "^4.5.0",
|
||||
"webpack-favicons": "^1.3.8",
|
||||
"webpack-merge": "^5.8.0",
|
||||
"yaml-loader": "^0.8.0"
|
||||
}
|
||||
|
||||
Generated
+581
-577
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "nym-connect"
|
||||
version = "1.1.9"
|
||||
description = "nym-connect for Mobile"
|
||||
version = "1.1.12"
|
||||
description = "nym-connect mobile"
|
||||
authors = ["Nym Technologies SA"]
|
||||
license = ""
|
||||
repository = ""
|
||||
@@ -26,7 +26,6 @@ tauri-mobile = { git = "https://github.com/tauri-apps/tauri-mobile", branch = "
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
bip39 = { version = "2.0.0", features = ["zeroize"] }
|
||||
chrono = "0.4"
|
||||
dirs = "4.0"
|
||||
eyre = "0.6.5"
|
||||
fix-path-env = { git = "https://github.com/tauri-apps/fix-path-env-rs", branch = "release"}
|
||||
@@ -42,10 +41,11 @@ serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
serde_repr = "0.1"
|
||||
tap = "1.0.1"
|
||||
# tauri = { git = "https://github.com/tauri-apps/tauri", branch = "next", features = ["clipboard-write-text", "native-tls-vendored", "notification-all", "shell-open", "system-tray", "window-close", "window-minimize", "window-start-dragging"] }
|
||||
tauri = { version = "2.0.0-alpha.3", features = ["clipboard-write-text", "native-tls-vendored", "notification-all", "shell-open", "system-tray", "window-close", "window-minimize", "window-start-dragging"] }
|
||||
# tauri = { git = "https://github.com/tauri-apps/tauri", branch = "next", features = ["clipboard-write-text", "native-tls-vendored", "notification-all", "shell-open"] }
|
||||
tauri = { version = "2.0.0-alpha.3", features = ["clipboard-write-text", "native-tls-vendored", "notification-all", "shell-open"] }
|
||||
tendermint-rpc = "0.23.0"
|
||||
thiserror = "1.0"
|
||||
time = { version = "0.3.17", features = ["local-offset"] }
|
||||
tokio = { version = "1.24.1", features = ["sync", "time"] }
|
||||
url = "2.2"
|
||||
yaml-rust = "0.4"
|
||||
|
||||
@@ -16,11 +16,6 @@ pub mod operations;
|
||||
mod state;
|
||||
mod tasks;
|
||||
|
||||
#[cfg(desktop)]
|
||||
mod menu;
|
||||
#[cfg(desktop)]
|
||||
mod window;
|
||||
|
||||
pub use state::State;
|
||||
|
||||
#[cfg(mobile)]
|
||||
|
||||
@@ -1,9 +1,26 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use fern::colors::{Color, ColoredLevelConfig};
|
||||
use serde::Serialize;
|
||||
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||
use std::str::FromStr;
|
||||
use tauri::Manager;
|
||||
use time::{format_description, OffsetDateTime};
|
||||
|
||||
fn formatted_time() -> String {
|
||||
// if we fail to obtain local time according to local offset, fallback to utc
|
||||
let _now = OffsetDateTime::now_local().unwrap_or_else(|_| OffsetDateTime::now_utc());
|
||||
|
||||
// if we're running it in the unit test, use the hardcoded value
|
||||
#[cfg(test)]
|
||||
let _now = OffsetDateTime::from_unix_timestamp(1666666666).unwrap();
|
||||
|
||||
// the unwraps are fine as we know this description is correct
|
||||
// note: the reason for this very particular format is a very simple one
|
||||
// it's what we've always been using since we copied it from the example,
|
||||
// so feel free to update it to whatever
|
||||
let format =
|
||||
format_description::parse("[[[year]-[month]-[day]][[[hour]:[minute]:[second]]").unwrap();
|
||||
_now.format(&format).unwrap()
|
||||
}
|
||||
|
||||
pub fn setup_logging(app_handle: tauri::AppHandle) -> Result<(), log::SetLoggerError> {
|
||||
let colors = ColoredLevelConfig::new()
|
||||
@@ -21,7 +38,7 @@ pub fn setup_logging(app_handle: tauri::AppHandle) -> Result<(), log::SetLoggerE
|
||||
.format(move |out, message, record| {
|
||||
out.finish(format_args!(
|
||||
"{}[{}][{}] {}",
|
||||
chrono::Local::now().format("[%Y-%m-%d][%H:%M:%S]"),
|
||||
formatted_time(),
|
||||
record.target(),
|
||||
colors.color(record.level()),
|
||||
message,
|
||||
@@ -33,7 +50,7 @@ pub fn setup_logging(app_handle: tauri::AppHandle) -> Result<(), log::SetLoggerE
|
||||
.format(move |out, message, record| {
|
||||
out.finish(format_args!(
|
||||
"{}[{}] {}",
|
||||
chrono::Local::now().format("[%Y-%m-%d][%H:%M:%S]"),
|
||||
formatted_time(),
|
||||
record.target(),
|
||||
message,
|
||||
))
|
||||
@@ -116,3 +133,15 @@ impl From<log::Level> for LogLevel {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn log_formatting() {
|
||||
let expected_chrono_formated = "[2022-10-25][02:57:46]".to_string();
|
||||
let new_time_based = formatted_time();
|
||||
assert_eq!(new_time_based, expected_chrono_formated)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,11 +6,5 @@
|
||||
use nym_connect::AppBuilder;
|
||||
|
||||
fn main() {
|
||||
// As per breaking change description here
|
||||
// https://github.com/tauri-apps/tauri/blob/feac1d193c6d618e49916ad0707201f43d5cdd36/tooling/bundler/CHANGELOG.md
|
||||
//if let Err(error) = fix_path_env::fix() {
|
||||
// log::warn!("Failed to fix PATH: {error}");
|
||||
//}
|
||||
|
||||
AppBuilder::new().run();
|
||||
}
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
use tauri::{
|
||||
AppHandle, CustomMenuItem, Menu, Submenu, SystemTray, SystemTrayEvent, SystemTrayMenu,
|
||||
SystemTrayMenuItem, Wry,
|
||||
};
|
||||
|
||||
use crate::window::window_toggle;
|
||||
|
||||
pub const SHOW_LOG_WINDOW: &str = "show_log_window";
|
||||
pub const CLEAR_STORAGE: &str = "clear_storage";
|
||||
|
||||
pub trait AddDefaultSubmenus {
|
||||
fn add_default_app_submenus(self) -> Self;
|
||||
}
|
||||
|
||||
impl AddDefaultSubmenus for Menu {
|
||||
fn add_default_app_submenus(self) -> Self {
|
||||
let submenu = Submenu::new(
|
||||
"Help",
|
||||
Menu::new().add_item(CustomMenuItem::new(SHOW_LOG_WINDOW, "Show logs")),
|
||||
);
|
||||
self.add_submenu(submenu)
|
||||
}
|
||||
}
|
||||
|
||||
pub const TRAY_MENU_QUIT: &str = "quit";
|
||||
pub const TRAY_MENU_SHOW_HIDE: &str = "show-hide";
|
||||
pub const TRAY_MENU_CONNECTION: &str = "connection";
|
||||
|
||||
pub(crate) fn create_tray_menu() -> SystemTray {
|
||||
let quit = CustomMenuItem::new(TRAY_MENU_QUIT, "Quit");
|
||||
let hide = CustomMenuItem::new(TRAY_MENU_SHOW_HIDE, "Hide");
|
||||
let connection = CustomMenuItem::new(TRAY_MENU_CONNECTION, "Connect");
|
||||
let tray_menu = SystemTrayMenu::new()
|
||||
.add_item(hide)
|
||||
.add_item(connection)
|
||||
.add_native_item(SystemTrayMenuItem::Separator)
|
||||
.add_item(quit);
|
||||
|
||||
SystemTray::new().with_menu(tray_menu)
|
||||
}
|
||||
|
||||
pub(crate) fn tray_menu_event_handler(app: &AppHandle<Wry>, event: SystemTrayEvent) {
|
||||
match event {
|
||||
SystemTrayEvent::LeftClick { position, size, .. } => {
|
||||
println!("Event {position:?} {size:?}");
|
||||
}
|
||||
SystemTrayEvent::MenuItemClick { id, .. } => {
|
||||
println!("Event {id}");
|
||||
match id.as_str() {
|
||||
TRAY_MENU_SHOW_HIDE => {
|
||||
window_toggle(app);
|
||||
}
|
||||
TRAY_MENU_QUIT => {
|
||||
// TODO: add disconnecting first
|
||||
app.exit(0);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -3,5 +3,3 @@ pub mod directory;
|
||||
pub mod export;
|
||||
pub mod help;
|
||||
pub mod http;
|
||||
#[cfg(desktop)]
|
||||
pub mod window;
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
use crate::window::window_hide;
|
||||
use tauri::{AppHandle, Wry};
|
||||
|
||||
#[tauri::command]
|
||||
pub fn hide_window(app: AppHandle<Wry>) {
|
||||
window_hide(&app);
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
use crate::menu::TRAY_MENU_SHOW_HIDE;
|
||||
use tauri::{AppHandle, Manager};
|
||||
|
||||
pub(crate) fn window_hide(app: &AppHandle<tauri::Wry>) {
|
||||
let window = app.get_window("main").unwrap();
|
||||
let item_handle = app.tray_handle().get_item(TRAY_MENU_SHOW_HIDE);
|
||||
if window.is_visible().unwrap() {
|
||||
window.hide().unwrap();
|
||||
item_handle.set_title("Show").unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn window_show(app: &AppHandle<tauri::Wry>) {
|
||||
let window = app.get_window("main").unwrap();
|
||||
let item_handle = app.tray_handle().get_item(TRAY_MENU_SHOW_HIDE);
|
||||
if !window.is_visible().unwrap() {
|
||||
window.show().unwrap();
|
||||
item_handle.set_title("Hide").unwrap();
|
||||
window.set_focus().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn window_toggle(app: &AppHandle<tauri::Wry>) {
|
||||
let window = app.get_window("main").unwrap();
|
||||
if window.is_visible().unwrap() {
|
||||
window_hide(app);
|
||||
} else {
|
||||
window_show(app);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"package": {
|
||||
"productName": "nym-connect",
|
||||
"version": "1.1.9"
|
||||
"version": "1.1.12"
|
||||
},
|
||||
"build": {
|
||||
"distDir": "../dist",
|
||||
@@ -14,7 +14,13 @@
|
||||
"active": true,
|
||||
"targets": "all",
|
||||
"identifier": "net.nymtech.connect",
|
||||
"icon": ["icons/32x32.png", "icons/128x128.png", "icons/128x128@2x.png", "icons/icon.icns", "icons/icon.ico"],
|
||||
"icon": [
|
||||
"icons/32x32.png",
|
||||
"icons/128x128.png",
|
||||
"icons/128x128@2x.png",
|
||||
"icons/icon.icns",
|
||||
"icons/icon.ico"
|
||||
],
|
||||
"resources": [],
|
||||
"externalBin": [],
|
||||
"copyright": "Copyright © 2021-2022 Nym Technologies SA",
|
||||
@@ -37,10 +43,6 @@
|
||||
"timestampUrl": ""
|
||||
}
|
||||
},
|
||||
"systemTray": {
|
||||
"iconPath": "icons/tray_icon.png",
|
||||
"iconAsTemplate": true
|
||||
},
|
||||
"updater": {
|
||||
"active": false
|
||||
},
|
||||
@@ -51,11 +53,6 @@
|
||||
"clipboard": {
|
||||
"writeText": true
|
||||
},
|
||||
"window": {
|
||||
"startDragging": true,
|
||||
"close": true,
|
||||
"minimize": true
|
||||
},
|
||||
"notification": {
|
||||
"all": true
|
||||
}
|
||||
|
||||
@@ -6,12 +6,13 @@ import { CustomTitleBar } from './CustomTitleBar';
|
||||
|
||||
export const AppWindowFrame: FCWithChildren = ({ children }) => {
|
||||
const location = useLocation();
|
||||
const { userDefinedGateway, setUserDefinedGateway } = useClientContext();
|
||||
const { userDefinedGateway, setUserDefinedGateway, userDefinedSPAddress, setUserDefinedSPAddress } =
|
||||
useClientContext();
|
||||
|
||||
// defined functions to be used when moving away from pages
|
||||
const onBack = () => {
|
||||
switch (location.pathname) {
|
||||
case '/menu/settings':
|
||||
case '/menu/settings/gateway':
|
||||
return () => {
|
||||
// when the user moves away from the settings page and the gateway is not valid
|
||||
// set isActive to false
|
||||
@@ -19,6 +20,14 @@ export const AppWindowFrame: FCWithChildren = ({ children }) => {
|
||||
setUserDefinedGateway((current) => ({ ...current, isActive: false }));
|
||||
}
|
||||
};
|
||||
case '/menu/settings/service-provider':
|
||||
return () => {
|
||||
// when the user moves away from the settings page and the sp is not valid
|
||||
// set isActive to false
|
||||
if (!userDefinedSPAddress?.address) {
|
||||
setUserDefinedSPAddress((current) => ({ ...current, isActive: false }));
|
||||
}
|
||||
};
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
@@ -33,7 +42,7 @@ export const AppWindowFrame: FCWithChildren = ({ children }) => {
|
||||
}}
|
||||
>
|
||||
<CustomTitleBar path={location.pathname} onBack={onBack()} />
|
||||
<Box style={{ padding: '16px', paddingTop: '24px' }}>{children}</Box>
|
||||
<Box style={{ padding: '16px' }}>{children}</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -38,7 +38,8 @@ const ArrowBackIcon = ({ onBack }: { onBack?: () => void }) => {
|
||||
return <CustomButton Icon={ArrowBack} onClick={handleBack} />;
|
||||
};
|
||||
|
||||
const getTitleIcon = (path: string) => {
|
||||
const getTitle = (path: string) => {
|
||||
if (path.includes('settings')) return 'Settings';
|
||||
if (path !== '/') {
|
||||
const title = path.split('/').slice(-1);
|
||||
return (
|
||||
@@ -60,7 +61,7 @@ export const CustomTitleBar = ({ path = '/', onBack }: { path?: string; onBack?:
|
||||
<Box style={customTitleBarStyles.titlebar}>
|
||||
{/* set width to keep logo centered */}
|
||||
<Box sx={{ width: '40px' }}>{path === '/' ? <MenuIcon /> : <ArrowBackIcon onBack={onBack} />}</Box>
|
||||
{getTitleIcon(path)}
|
||||
{path !== '/' && <Box sx={{ width: '40px' }} />}
|
||||
{getTitle(path)}
|
||||
<Box sx={{ width: '40px' }} />
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -4,6 +4,10 @@ import { ServiceProvider } from 'src/types/directory';
|
||||
|
||||
export const ServiceProviderInfo = ({ serviceProvider }: { serviceProvider: ServiceProvider }) => (
|
||||
<Stack gap={1} sx={{ wordWrap: 'break-word', maxWidth: 200, p: 1 }}>
|
||||
<Typography variant="body2" fontWeight="bold">
|
||||
Connection info
|
||||
</Typography>
|
||||
<Divider />
|
||||
<Typography variant="caption" fontWeight="bold">
|
||||
Gateway <Typography variant="caption">{serviceProvider.gateway}</Typography>
|
||||
</Typography>
|
||||
@@ -16,6 +20,10 @@ export const ServiceProviderInfo = ({ serviceProvider }: { serviceProvider: Serv
|
||||
|
||||
export const GatwayWarningInfo = () => (
|
||||
<Stack gap={1} sx={{ wordWrap: 'break-word', maxWidth: 200, p: 1 }}>
|
||||
<Typography variant="body2" fontWeight="bold" color="warning.main">
|
||||
Connection issue
|
||||
</Typography>
|
||||
<Divider />
|
||||
<Typography variant="caption">Try disconnecting and connecting again</Typography>
|
||||
</Stack>
|
||||
);
|
||||
|
||||
@@ -4,13 +4,14 @@ import { invoke } from '@tauri-apps/api';
|
||||
import { Error } from 'src/types/error';
|
||||
import { getVersion } from '@tauri-apps/api/app';
|
||||
import { useEvents } from 'src/hooks/events';
|
||||
import { UserDefinedGateway } from 'src/types/gateway';
|
||||
import { forage } from '@tauri-apps/tauri-forage';
|
||||
import { UserDefinedGateway, UserDefinedSPAddress } from 'src/types/service-provider';
|
||||
import { getItemFromStorage, setItemInStorage } from 'src/utils';
|
||||
import { ConnectionStatusKind, GatewayPerformance } from '../types';
|
||||
import { ConnectionStatsItem } from '../components/ConnectionStats';
|
||||
import { ServiceProvider } from '../types/directory';
|
||||
|
||||
const FORAGE_KEY = 'nym-connect-user-gateway';
|
||||
const FORAGE_GATEWAY_KEY = 'nym-connect-user-gateway';
|
||||
const FORAGE_SP_KEY = 'nym-connect-user-sp';
|
||||
|
||||
type ModeType = 'light' | 'dark';
|
||||
|
||||
@@ -25,16 +26,19 @@ export type TClientContext = {
|
||||
selectedProvider?: ServiceProvider;
|
||||
showInfoModal: boolean;
|
||||
userDefinedGateway?: UserDefinedGateway;
|
||||
userDefinedSPAddress: UserDefinedSPAddress;
|
||||
serviceProviders?: ServiceProvider[];
|
||||
setMode: (mode: ModeType) => void;
|
||||
clearError: () => void;
|
||||
setConnectionStatus: (connectionStatus: ConnectionStatusKind) => void;
|
||||
setConnectionStats: (connectionStats: ConnectionStatsItem[] | undefined) => void;
|
||||
setConnectedSince: (connectedSince: DateTime | undefined) => void;
|
||||
setShowInfoModal: (show: boolean) => void;
|
||||
setRandomSerivceProvider: () => void;
|
||||
setSerivceProvider: () => void;
|
||||
startConnecting: () => Promise<void>;
|
||||
startDisconnecting: () => Promise<void>;
|
||||
setUserDefinedGateway: React.Dispatch<React.SetStateAction<UserDefinedGateway>>;
|
||||
setUserDefinedSPAddress: React.Dispatch<React.SetStateAction<UserDefinedSPAddress>>;
|
||||
};
|
||||
|
||||
export const ClientContext = createContext({} as TClientContext);
|
||||
@@ -50,43 +54,39 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => {
|
||||
const [appVersion, setAppVersion] = useState<string>();
|
||||
const [gatewayPerformance, setGatewayPerformance] = useState<GatewayPerformance>('Good');
|
||||
const [showInfoModal, setShowInfoModal] = useState(false);
|
||||
const [userDefinedGateway, setUserDefinedGateway] = useState<UserDefinedGateway>({ isActive: false, gateway: '' });
|
||||
const [userDefinedGateway, setUserDefinedGateway] = useState<UserDefinedGateway>({
|
||||
isActive: false,
|
||||
gateway: undefined,
|
||||
});
|
||||
const [userDefinedSPAddress, setUserDefinedSPAddress] = useState<UserDefinedSPAddress>({
|
||||
isActive: false,
|
||||
address: undefined,
|
||||
});
|
||||
|
||||
const getAppVersion = async () => {
|
||||
const version = await getVersion();
|
||||
return version;
|
||||
};
|
||||
|
||||
const setUserGatewayInStorage = async (gateway: UserDefinedGateway) => {
|
||||
try {
|
||||
await forage.setItem({
|
||||
key: FORAGE_KEY,
|
||||
value: gateway,
|
||||
} as any)();
|
||||
} catch (e) {
|
||||
console.warn(e);
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
useEffect(() => {
|
||||
setItemInStorage({ key: FORAGE_GATEWAY_KEY, value: userDefinedGateway });
|
||||
}, [userDefinedGateway]);
|
||||
|
||||
const getUserGatewayFromStorage = async (): Promise<UserDefinedGateway | undefined> => {
|
||||
try {
|
||||
const gatewayFromStorage = await forage.getItem({ key: FORAGE_KEY })();
|
||||
return gatewayFromStorage;
|
||||
} catch (e) {
|
||||
console.warn(e);
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
useEffect(() => {
|
||||
setItemInStorage({ key: FORAGE_SP_KEY, value: userDefinedSPAddress });
|
||||
}, [userDefinedSPAddress]);
|
||||
|
||||
const initialiseApp = async () => {
|
||||
const services = await invoke('get_services');
|
||||
const AppVersion = await getAppVersion();
|
||||
const storedUserDefinedGateway = await getUserGatewayFromStorage();
|
||||
const storedUserDefinedGateway = await getItemFromStorage({ key: FORAGE_GATEWAY_KEY });
|
||||
const storedUserDefinedSP = await getItemFromStorage({ key: FORAGE_SP_KEY });
|
||||
|
||||
setAppVersion(AppVersion);
|
||||
setServiceProviders(services as ServiceProvider[]);
|
||||
|
||||
if (storedUserDefinedGateway) setUserDefinedGateway(storedUserDefinedGateway);
|
||||
if (storedUserDefinedSP) setUserDefinedSPAddress(storedUserDefinedSP);
|
||||
};
|
||||
|
||||
useEvents({
|
||||
@@ -125,27 +125,37 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => {
|
||||
}, []);
|
||||
|
||||
const shouldUseUserGateway = !!userDefinedGateway.gateway && userDefinedGateway.isActive;
|
||||
const shouldUseUserSP = !!userDefinedSPAddress.address && userDefinedSPAddress.isActive;
|
||||
|
||||
const setServiceProvider = async (newServiceProvider: ServiceProvider) => {
|
||||
await invoke('set_gateway', {
|
||||
gateway: newServiceProvider.gateway,
|
||||
gateway: shouldUseUserGateway ? userDefinedGateway.gateway : newServiceProvider.gateway,
|
||||
});
|
||||
await invoke('set_service_provider', {
|
||||
serviceProvider: shouldUseUserSP ? userDefinedSPAddress.address : newServiceProvider.address,
|
||||
});
|
||||
await invoke('set_service_provider', { serviceProvider: newServiceProvider.address });
|
||||
};
|
||||
|
||||
const getRandomSPFromList = (services: ServiceProvider[]) => {
|
||||
const randomSelection = services[Math.floor(Math.random() * services.length)];
|
||||
|
||||
if (shouldUseUserGateway) return { ...randomSelection, gateway: userDefinedGateway.gateway } as ServiceProvider;
|
||||
return randomSelection;
|
||||
};
|
||||
|
||||
const setRandomSerivceProvider = async () => {
|
||||
const buildServiceProvider = async (serviceProvider: ServiceProvider) => {
|
||||
const sp = { ...serviceProvider };
|
||||
|
||||
if (shouldUseUserGateway) sp.gateway = userDefinedGateway.gateway as string;
|
||||
if (shouldUseUserSP) sp.address = userDefinedSPAddress.address as string;
|
||||
|
||||
return sp;
|
||||
};
|
||||
|
||||
const setSerivceProvider = async () => {
|
||||
if (serviceProviders) {
|
||||
const randomServiceProvider = getRandomSPFromList(serviceProviders);
|
||||
await setServiceProvider(randomServiceProvider);
|
||||
await setUserGatewayInStorage(userDefinedGateway);
|
||||
setSelectedProvider(randomServiceProvider);
|
||||
const withUserDefinitions = await buildServiceProvider(randomServiceProvider);
|
||||
await setServiceProvider(withUserDefinitions);
|
||||
setSelectedProvider(withUserDefinitions);
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
@@ -165,21 +175,25 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => {
|
||||
showInfoModal,
|
||||
setConnectionStats,
|
||||
selectedProvider,
|
||||
serviceProviders,
|
||||
connectedSince,
|
||||
setConnectedSince,
|
||||
setRandomSerivceProvider,
|
||||
setSerivceProvider,
|
||||
startConnecting,
|
||||
startDisconnecting,
|
||||
gatewayPerformance,
|
||||
setShowInfoModal,
|
||||
userDefinedSPAddress,
|
||||
userDefinedGateway,
|
||||
setUserDefinedGateway,
|
||||
setUserDefinedSPAddress,
|
||||
}),
|
||||
[
|
||||
mode,
|
||||
appVersion,
|
||||
error,
|
||||
showInfoModal,
|
||||
serviceProviders,
|
||||
connectedSince,
|
||||
connectionStatus,
|
||||
connectionStats,
|
||||
@@ -187,6 +201,7 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => {
|
||||
gatewayPerformance,
|
||||
selectedProvider,
|
||||
userDefinedGateway,
|
||||
userDefinedSPAddress,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ const mockValues: TClientContext = {
|
||||
gatewayPerformance: 'Good',
|
||||
showInfoModal: false,
|
||||
userDefinedGateway: { isActive: false, gateway: '' },
|
||||
userDefinedSPAddress: { isActive: false, address: '' },
|
||||
setShowInfoModal: () => {},
|
||||
setMode: () => {},
|
||||
clearError: () => {},
|
||||
@@ -18,8 +19,9 @@ const mockValues: TClientContext = {
|
||||
setConnectionStatus: () => {},
|
||||
startConnecting: async () => {},
|
||||
startDisconnecting: async () => {},
|
||||
setRandomSerivceProvider: () => {},
|
||||
setSerivceProvider: () => {},
|
||||
setUserDefinedGateway: () => {},
|
||||
setUserDefinedSPAddress: () => {},
|
||||
};
|
||||
|
||||
export const MockProvider: FCWithChildren<{
|
||||
|
||||
@@ -29,7 +29,7 @@ export const ConnectionPage = () => {
|
||||
// eslint-disable-next-line default-case
|
||||
switch (currentStatus) {
|
||||
case 'disconnected':
|
||||
await context.setRandomSerivceProvider();
|
||||
await context.setSerivceProvider();
|
||||
await context.startConnecting();
|
||||
context.setConnectedSince(DateTime.now());
|
||||
context.setShowInfoModal(true);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user