diff --git a/.github/workflows/publish-nym-binaries.yml b/.github/workflows/publish-nym-binaries.yml index 4e8bcb2966..618304e9ae 100644 --- a/.github/workflows/publish-nym-binaries.yml +++ b/.github/workflows/publish-nym-binaries.yml @@ -52,7 +52,7 @@ jobs: - name: Install Rust stable uses: actions-rs/toolchain@v1 with: - toolchain: stable + toolchain: 1.86.0 override: true - name: Build all binaries diff --git a/CHANGELOG.md b/CHANGELOG.md index 812ccd617b..d7b686a06f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,58 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https:// ## [Unreleased] +## [2025.10-brie] (2025-05-27) + +- Backport PR 5779 ([#5801]) +- Expanded Accept Encoding for `reqwest` ([#5779]) +- Teach HttpClientError how to report its status code and timeout ([#5770]) +- Skip refreshing the topology on startup as we already have an initial set ([#5768]) +- Fetch the topology from the nym-api concurrently ([#5767]) +- feat: use bincode by default in NymApiClient + remove feature-lock ([#5761]) +- Instrument create_request ([#5760]) +- Add node_bonded field to delegations ([#5759]) +- build(deps): bump mikefarah/yq from 4.45.1 to 4.45.4 ([#5758]) +- Raw route submissions ([#5756]) +- feat: expires header for `/active` nym-api responses ([#5755]) +- Decrease default average packet delay to 15 ms ([#5754]) +- build(deps): bump the patch-updates group across 1 directory with 12 updates ([#5753]) +- Remove pretty_env_logger and switch remaining crates to use tracing ([#5749]) +- Update pretty_env_logger to latest to not depend on unmaintained crate atty ([#5748]) +- Upgrade prometheus crate to fix security warning ([#5747]) +- Downgrade deranged crate to 0.4.0 ([#5746]) +- feat: nym-api bincode + yaml support ([#5745]) +- fix parallel feature in ecash crate with send + sync ([#5744]) +- Remove old test directory - Update validator docker ([#5743]) +- [Feature] `RememberMe` is the new don't `ForgetMe` ([#5742]) +- build(deps): bump ammonia from 4.0.0 to 4.1.0 ([#5739]) +- build(deps): bump base-x from 3.0.9 to 3.0.11 in /testnet-faucet ([#5737]) +- build(deps): bump http-proxy-middleware from 2.0.8 to 2.0.9 ([#5730]) + +[#5801]: https://github.com/nymtech/nym/pull/5801 +[#5779]: https://github.com/nymtech/nym/pull/5779 +[#5770]: https://github.com/nymtech/nym/pull/5770 +[#5768]: https://github.com/nymtech/nym/pull/5768 +[#5767]: https://github.com/nymtech/nym/pull/5767 +[#5761]: https://github.com/nymtech/nym/pull/5761 +[#5760]: https://github.com/nymtech/nym/pull/5760 +[#5759]: https://github.com/nymtech/nym/pull/5759 +[#5758]: https://github.com/nymtech/nym/pull/5758 +[#5756]: https://github.com/nymtech/nym/pull/5756 +[#5755]: https://github.com/nymtech/nym/pull/5755 +[#5754]: https://github.com/nymtech/nym/pull/5754 +[#5753]: https://github.com/nymtech/nym/pull/5753 +[#5749]: https://github.com/nymtech/nym/pull/5749 +[#5748]: https://github.com/nymtech/nym/pull/5748 +[#5747]: https://github.com/nymtech/nym/pull/5747 +[#5746]: https://github.com/nymtech/nym/pull/5746 +[#5745]: https://github.com/nymtech/nym/pull/5745 +[#5744]: https://github.com/nymtech/nym/pull/5744 +[#5743]: https://github.com/nymtech/nym/pull/5743 +[#5742]: https://github.com/nymtech/nym/pull/5742 +[#5739]: https://github.com/nymtech/nym/pull/5739 +[#5737]: https://github.com/nymtech/nym/pull/5737 +[#5730]: https://github.com/nymtech/nym/pull/5730 + ## [2025.9-appenzeller] (2025-05-13) - build(deps): bump clap from 4.5.36 to 4.5.37 in the patch-updates group ([#5722]) diff --git a/Cargo.lock b/Cargo.lock index a6b120e930..3f2747f1ca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4788,7 +4788,7 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] name = "nym-api" -version = "1.1.57" +version = "1.1.59" dependencies = [ "anyhow", "async-trait", @@ -5043,7 +5043,7 @@ dependencies = [ [[package]] name = "nym-cli" -version = "1.1.55" +version = "1.1.56" dependencies = [ "anyhow", "base64 0.22.1", @@ -5125,7 +5125,7 @@ dependencies = [ [[package]] name = "nym-client" -version = "1.1.55" +version = "1.1.56" dependencies = [ "bs58", "clap", @@ -6129,7 +6129,7 @@ dependencies = [ [[package]] name = "nym-network-requester" -version = "1.1.56" +version = "1.1.57" dependencies = [ "addr", "anyhow", @@ -6179,7 +6179,7 @@ dependencies = [ [[package]] name = "nym-node" -version = "1.11.0" +version = "1.12.0" dependencies = [ "anyhow", "arc-swap", @@ -6575,7 +6575,7 @@ dependencies = [ [[package]] name = "nym-socks5-client" -version = "1.1.55" +version = "1.1.56" dependencies = [ "bs58", "clap", @@ -7180,7 +7180,7 @@ dependencies = [ [[package]] name = "nymvisor" -version = "0.1.20" +version = "0.1.21" dependencies = [ "anyhow", "bytes", diff --git a/clients/native/Cargo.toml b/clients/native/Cargo.toml index e54e982e81..ba75b7a941 100644 --- a/clients/native/Cargo.toml +++ b/clients/native/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nym-client" -version = "1.1.55" +version = "1.1.56" authors = ["Dave Hrycyszyn ", "Jędrzej Stuczyński "] description = "Implementation of the Nym Client" edition = "2021" diff --git a/clients/socks5/Cargo.toml b/clients/socks5/Cargo.toml index 43b88ff3dc..8892199070 100644 --- a/clients/socks5/Cargo.toml +++ b/clients/socks5/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nym-socks5-client" -version = "1.1.55" +version = "1.1.56" authors = ["Dave Hrycyszyn "] description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address" edition = "2021" diff --git a/nym-api/Cargo.toml b/nym-api/Cargo.toml index f9aa37314d..ef8d255256 100644 --- a/nym-api/Cargo.toml +++ b/nym-api/Cargo.toml @@ -4,7 +4,7 @@ [package] name = "nym-api" license = "GPL-3.0" -version = "1.1.57" +version = "1.1.59" authors.workspace = true edition = "2021" rust-version.workspace = true diff --git a/nym-api/src/unstable_routes/account/cache.rs b/nym-api/src/unstable_routes/account/cache.rs index 261018962a..a60a13abb0 100644 --- a/nym-api/src/unstable_routes/account/cache.rs +++ b/nym-api/src/unstable_routes/account/cache.rs @@ -108,13 +108,13 @@ impl AddressInfoCache { address: account_id.to_string(), balance: balance.into(), delegations: delegation_data - .delegations() .into_iter() .map(|d| NyxAccountDelegationDetails { - delegated: d.amount, - height: d.height, - node_id: d.node_id, - proxy: d.proxy, + delegated: d.details().amount.clone(), + height: d.details().height, + node_id: d.details().node_id, + proxy: d.details().proxy.clone(), + node_bonded: d.is_node_bonded(), }) .collect(), accumulated_rewards, diff --git a/nym-api/src/unstable_routes/account/data_collector.rs b/nym-api/src/unstable_routes/account/data_collector.rs index f9829b29a8..d2c780d0fb 100644 --- a/nym-api/src/unstable_routes/account/data_collector.rs +++ b/nym-api/src/unstable_routes/account/data_collector.rs @@ -8,10 +8,9 @@ use crate::{ }; use cosmwasm_std::{Coin, Decimal}; use nym_mixnet_contract_common::NodeRewarding; -use nym_topology::NodeId; use nym_validator_client::nyxd::AccountId; use std::collections::{HashMap, HashSet}; -use tracing::warn; +use tracing::error; pub(crate) struct AddressDataCollector { nyxd_client: crate::nyxd::Client, @@ -56,18 +55,18 @@ impl AddressDataCollector { Ok(balance) } - pub(crate) async fn get_delegations(&mut self) -> AxumResult { + pub(crate) async fn get_delegations(&mut self) -> AxumResult> { let og_delegations = self .nyxd_client .get_all_delegator_delegations(&self.account_id) - .await?; + .await? + .into_iter() + .map(|delegation| (delegation.node_id, delegation)) + .collect::>(); - let delegated_to_nodes = og_delegations - .iter() - .map(|d| d.node_id) - .collect::>(); + let mut node_delegation_info = Vec::new(); - let nym_nodes = self + let delegated_to_nodes_bonded = self .nym_contract_cache .all_cached_nym_nodes() .await @@ -85,61 +84,75 @@ impl AddressDataCollector { // add to totals self.total_value += pending_operator_reward; } - if delegated_to_nodes.contains(&node_details.node_id()) { - Some(( - node_details.node_id(), - // avoid cloning node data which we don't need - ( - node_details.rewarding_details.clone(), - node_details.is_unbonding(), - ), - )) + if let Some(delegation) = og_delegations.get(&node_details.node_id()) { + node_delegation_info.push(AddressDelegationInfo { + details: delegation.clone(), + node_reward_info: NodeBondStatus::Bonded { + rewarding_info: node_details.rewarding_details.to_owned(), + unbonding: node_details.is_unbonding(), + }, + }); + + Some(node_details.node_id()) } else { None } }) - .collect::>(); + .collect::>(); - Ok(AddressDelegationInfo { - delegations: og_delegations, - delegated_to_nodes: nym_nodes, - }) + for (node_id, delegation) in og_delegations { + if !delegated_to_nodes_bonded.contains(&node_id) { + node_delegation_info.push(AddressDelegationInfo { + details: delegation.clone(), + node_reward_info: NodeBondStatus::UnBonded, + }); + } + } + + Ok(node_delegation_info) } pub(crate) async fn calculate_rewards( &mut self, - delegation_data: &AddressDelegationInfo, + delegation_data: &Vec, ) -> AxumResult> { let mut accumulated_rewards = Vec::new(); - for delegation in delegation_data.delegations.iter() { - let node_id = &delegation.node_id; + for delegation in delegation_data { + let node_id = delegation.details.node_id; - if let Some((rewarding_details, is_unbonding)) = - delegation_data.delegated_to_nodes.get(node_id) - { - match rewarding_details.determine_delegation_reward(delegation) { - Ok(delegation_reward) => { - let reward = NyxAccountDelegationRewardDetails { - node_id: delegation.node_id, - rewards: decimal_to_coin(delegation_reward, &self.base_denom), - amount_staked: delegation.amount.clone(), - node_still_fully_bonded: !is_unbonding, - }; - // 4. sum the rewards and delegations - self.total_delegations += delegation.amount.amount.u128(); - self.total_value += delegation.amount.amount.u128(); - self.total_value += reward.rewards.amount.u128(); - self.claimable_rewards += reward.rewards.amount.u128(); + match &delegation.node_reward_info { + NodeBondStatus::Bonded { + rewarding_info, + unbonding, + } => { + match rewarding_info.determine_delegation_reward(&delegation.details) { + Ok(delegation_reward) => { + let reward = NyxAccountDelegationRewardDetails { + node_id, + rewards: decimal_to_coin(delegation_reward, &self.base_denom), + amount_staked: delegation.details.amount.clone(), + node_still_fully_bonded: !unbonding, + }; + // 4. sum the rewards and delegations + self.total_delegations += delegation.details.amount.amount.u128(); + self.total_value += delegation.details.amount.amount.u128(); + self.total_value += reward.rewards.amount.u128(); + self.claimable_rewards += reward.rewards.amount.u128(); - accumulated_rewards.push(reward); - } - Err(err) => { - warn!( - "Couldn't determine delegations for {} on node {}: {}", - &self.account_id, node_id, err - ) + accumulated_rewards.push(reward); + } + Err(err) => { + error!( + "Couldn't determine delegations for {} on node {}: {}", + &self.account_id, node_id, err + ) + } } } + NodeBondStatus::UnBonded => { + // directory cache doesn't store node details required to + // calculate rewarding for unbonded nodes + } } } @@ -171,17 +184,27 @@ impl AddressDataCollector { } pub(crate) struct AddressDelegationInfo { - delegations: Vec, - delegated_to_nodes: HashMap, + details: nym_mixnet_contract_common::Delegation, + node_reward_info: NodeBondStatus, } impl AddressDelegationInfo { - pub(crate) fn delegations(self) -> Vec { - self.delegations + pub(crate) fn details(&self) -> &nym_mixnet_contract_common::Delegation { + &self.details + } + + pub(crate) fn is_node_bonded(&self) -> bool { + matches!(self.node_reward_info, NodeBondStatus::Bonded { .. }) } } -type RewardAndBondInfo = (NodeRewarding, bool); +pub(crate) enum NodeBondStatus { + Bonded { + rewarding_info: NodeRewarding, + unbonding: bool, + }, + UnBonded, +} fn decimal_to_coin(decimal: Decimal, denom: impl Into) -> Coin { Coin::new(decimal.to_uint_floor(), denom) @@ -189,6 +212,7 @@ fn decimal_to_coin(decimal: Decimal, denom: impl Into) -> Coin { #[cfg(test)] mod test { + use super::*; #[tokio::test] diff --git a/nym-api/src/unstable_routes/models.rs b/nym-api/src/unstable_routes/models.rs index 9864f19a42..1d802f8aa0 100644 --- a/nym-api/src/unstable_routes/models.rs +++ b/nym-api/src/unstable_routes/models.rs @@ -21,6 +21,7 @@ pub struct NyxAccountDelegationDetails { pub height: u64, #[schema(value_type = Option)] pub proxy: Option, + pub node_bonded: bool, } #[derive(Clone, Debug, Serialize, Deserialize, utoipa::ToSchema, utoipa::ToResponse)] @@ -41,6 +42,9 @@ pub struct NyxAccountDetails { #[schema(value_type = CoinSchema)] pub total_value: Coin, pub delegations: Vec, + /// Shows rewards from delegations to **currently** bonded nodes. + /// Rewards from nodes that user delegated to, but were later unbonded, + /// are claimable, but not shown here. pub accumulated_rewards: Vec, #[schema(value_type = String)] pub total_delegations: Coin, diff --git a/nym-node/Cargo.toml b/nym-node/Cargo.toml index 73f8c81425..da03b23705 100644 --- a/nym-node/Cargo.toml +++ b/nym-node/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "nym-node" -version = "1.11.0" +version = "1.12.0" authors.workspace = true repository.workspace = true homepage.workspace = true diff --git a/service-providers/network-requester/Cargo.toml b/service-providers/network-requester/Cargo.toml index 8c778f0a0f..11f529d789 100644 --- a/service-providers/network-requester/Cargo.toml +++ b/service-providers/network-requester/Cargo.toml @@ -4,7 +4,7 @@ [package] name = "nym-network-requester" license = "GPL-3.0" -version = "1.1.56" +version = "1.1.57" authors.workspace = true edition.workspace = true rust-version = "1.70" diff --git a/tools/nym-cli/Cargo.toml b/tools/nym-cli/Cargo.toml index ea3d84e2ce..1560b61cd6 100644 --- a/tools/nym-cli/Cargo.toml +++ b/tools/nym-cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nym-cli" -version = "1.1.55" +version = "1.1.56" authors.workspace = true edition = "2021" license.workspace = true diff --git a/tools/nymvisor/Cargo.toml b/tools/nymvisor/Cargo.toml index c71b4b2ed1..ec5377c1e4 100644 --- a/tools/nymvisor/Cargo.toml +++ b/tools/nymvisor/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nymvisor" -version = "0.1.20" +version = "0.1.21" authors.workspace = true repository.workspace = true homepage.workspace = true