From 9fb3443fd1e88cbc347b2e7ee2ebcadda1683e69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Tue, 21 Oct 2025 10:59:17 +0100 Subject: [PATCH] bugfix: update stored epoch share when changing announce address (#6131) * bugfix: update stored epoch share when changing announce address * chore: remove placeholder legacy mixnode bonding test [mixnet contract] --- .github/workflows/ci-contracts.yml | 2 +- .../coconut-dkg/src/dealers/transactions.rs | 102 +++++++++++++++++- .../src/testable_dkg_contract/mod.rs | 16 +-- contracts/mixnet/src/testing/legacy.rs | 12 --- contracts/mixnet/src/testing/mod.rs | 4 - nym-wallet/Cargo.lock | 27 +---- 6 files changed, 111 insertions(+), 52 deletions(-) delete mode 100644 contracts/mixnet/src/testing/legacy.rs diff --git a/.github/workflows/ci-contracts.yml b/.github/workflows/ci-contracts.yml index 3448eb0834..4a958ecf73 100644 --- a/.github/workflows/ci-contracts.yml +++ b/.github/workflows/ci-contracts.yml @@ -54,7 +54,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: test - args: --lib --manifest-path contracts/Cargo.toml + args: --lib --manifest-path contracts/Cargo.toml --all-features - name: Check formatting uses: actions-rs/cargo@v1 diff --git a/contracts/coconut-dkg/src/dealers/transactions.rs b/contracts/coconut-dkg/src/dealers/transactions.rs index 5bd957dcdc..7977a439d8 100644 --- a/contracts/coconut-dkg/src/dealers/transactions.rs +++ b/contracts/coconut-dkg/src/dealers/transactions.rs @@ -9,6 +9,7 @@ use crate::epoch_state::storage::{load_current_epoch, save_epoch}; use crate::epoch_state::utils::check_epoch_state; use crate::error::ContractError; use crate::state::storage::STATE; +use crate::verification_key_shares::storage::vk_shares; use crate::Dealer; use cosmwasm_std::{Deps, DepsMut, Env, Event, MessageInfo, Response}; use nym_coconut_dkg_common::dealer::{DealerRegistrationDetails, OwnershipTransfer}; @@ -161,6 +162,14 @@ pub fn try_update_announce_address( details.announce_address = new_address.clone(); EPOCH_DEALERS_MAP.save(deps.storage, (epoch.epoch_id, &info.sender), &details)?; + let mut contract_share = vk_shares().load(deps.storage, (&info.sender, epoch.epoch_id))?; + contract_share.announce_address = new_address.clone(); + vk_shares().save( + deps.storage, + (&info.sender, epoch.epoch_id), + &contract_share, + )?; + Ok(Response::new().add_event( Event::new("dkg-announce-address-update") .add_attribute("dealer", info.sender) @@ -228,9 +237,14 @@ pub(crate) mod tests { #[cfg(feature = "testable-dkg-contract")] mod tests_with_mock { use super::*; - use crate::testable_dkg_contract::{init_contract_tester, DkgContractTesterExt}; + use crate::testable_dkg_contract::{ + init_contract_tester, init_contract_tester_with_group_members, DkgContractTesterExt, + }; + use anyhow::Context; use cosmwasm_std::testing::message_info; - use nym_contracts_common_testing::ContractOpts; + use nym_coconut_dkg_common::msg::QueryMsg; + use nym_coconut_dkg_common::verification_key::PagedVKSharesResponse; + use nym_contracts_common_testing::{ChainOpts, ContractOpts}; #[test] fn transferring_ownership() -> anyhow::Result<()> { @@ -436,9 +450,91 @@ mod tests_with_mock { assert_eq!(old_details1, new_details1); assert_eq!(old_details2, new_details2); - // most recent entry is updated + // most recent entry is updated assert_eq!(new_details3.announce_address, new_address); Ok(()) } + + #[test] + fn updating_announce_address_updates_vk_shares() -> anyhow::Result<()> { + let mut contract = init_contract_tester_with_group_members(3); + let group_member = contract.random_group_member(); + + contract.run_initial_dummy_dkg(); // => epoch 0 + contract.run_reset_dkg(); // => epoch 1 + + // LEAVE DKG MEMBERSHIP + contract.remove_group_member(group_member.clone()); + contract.run_reset_dkg(); // => epoch 2 + + // COME BACK + contract.add_group_member(group_member.clone()); + contract.run_reset_dkg(); // => epoch 3 + + let old_address = EPOCH_DEALERS_MAP + .load(&contract, (3, &group_member))? + .announce_address; + + let old_share0 = vk_shares().load(&contract, (&group_member, 0))?; + let old_share1 = vk_shares().load(&contract, (&group_member, 1))?; + let old_share2 = vk_shares().may_load(&contract, (&group_member, 2))?; + assert!(old_share2.is_none()); + let old_share3 = vk_shares().may_load(&contract, (&group_member, 3))?; + assert!(old_share3.is_some()); + + let new_address = "https://new-address.com".to_string(); + try_update_announce_address( + contract.deps_mut(), + message_info(&group_member, &[]), + new_address.clone(), + )?; + + let new_share0 = vk_shares().load(&contract, (&group_member, 0))?; + let new_share1 = vk_shares().load(&contract, (&group_member, 1))?; + let new_share2 = vk_shares().may_load(&contract, (&group_member, 2))?; + assert!(new_share2.is_none()); + let new_share3 = vk_shares().load(&contract, (&group_member, 3))?; + + // old epoch data is unchanged + assert_eq!(old_share0, new_share0); + assert_eq!(old_share1, new_share1); + assert_eq!(old_share2, new_share2); + + // most recent entry is updated + assert_eq!(new_share3.announce_address, new_address); + + // finally an integration check against query endpoint + let epoch0_shares: PagedVKSharesResponse = + contract.query(&QueryMsg::GetVerificationKeys { + epoch_id: 0, + limit: None, + start_after: None, + })?; + assert_eq!(epoch0_shares.shares.len(), 3); + + let member_share = epoch0_shares + .shares + .iter() + .find(|s| s.owner == group_member) + .context("failed to find member's share")?; + assert_eq!(member_share.announce_address, old_address); + + let epoch0_shares: PagedVKSharesResponse = + contract.query(&QueryMsg::GetVerificationKeys { + epoch_id: 3, + limit: None, + start_after: None, + })?; + assert_eq!(epoch0_shares.shares.len(), 3); + + let member_share = epoch0_shares + .shares + .iter() + .find(|s| s.owner == group_member) + .context("failed to find member's share")?; + assert_eq!(member_share.announce_address, new_address); + + Ok(()) + } } diff --git a/contracts/coconut-dkg/src/testable_dkg_contract/mod.rs b/contracts/coconut-dkg/src/testable_dkg_contract/mod.rs index 528498f600..2ceaee2965 100644 --- a/contracts/coconut-dkg/src/testable_dkg_contract/mod.rs +++ b/contracts/coconut-dkg/src/testable_dkg_contract/mod.rs @@ -62,12 +62,18 @@ impl TestableNymContract for DkgContract { where Self: Sized, { - init_contract_tester_with_group_members(DEFAULT_GROUP_MEMBERS) + init_contract_tester() } } pub fn init_contract_tester() -> ContractTester { - DkgContract::init().with_common_storage_key(CommonStorageKeys::Admin, "dkg-admin") + init_contract_tester_with_group_members(DEFAULT_GROUP_MEMBERS) +} + +pub fn init_contract_tester_with_group_members(members: usize) -> ContractTester { + prepare_contract_tester_builder_with_group_members(members) + .build() + .with_common_storage_key(CommonStorageKeys::Admin, "dkg-admin") } pub fn prepare_contract_tester_builder_with_group_members( @@ -137,12 +143,6 @@ where builder } -pub fn init_contract_tester_with_group_members(members: usize) -> ContractTester { - prepare_contract_tester_builder_with_group_members(members) - .build() - .with_common_storage_key(CommonStorageKeys::Admin, "dkg-admin") -} - pub trait DkgContractTesterExt: ContractOpts + ChainOpts diff --git a/contracts/mixnet/src/testing/legacy.rs b/contracts/mixnet/src/testing/legacy.rs deleted file mode 100644 index 38a919fd33..0000000000 --- a/contracts/mixnet/src/testing/legacy.rs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2024 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn legacy_mixnode_bonding() { - todo!() - } -} diff --git a/contracts/mixnet/src/testing/mod.rs b/contracts/mixnet/src/testing/mod.rs index ac99770b50..30de4b3136 100644 --- a/contracts/mixnet/src/testing/mod.rs +++ b/contracts/mixnet/src/testing/mod.rs @@ -2,7 +2,3 @@ // SPDX-License-Identifier: Apache-2.0 pub(crate) mod transactions; - -// the purpose of that module is to keep track of tests of legacy features that will eventually be phased out -// such as standalone mixnode/gateway bonding -pub(crate) mod legacy; diff --git a/nym-wallet/Cargo.lock b/nym-wallet/Cargo.lock index 4603c90e9b..546ee6409d 100644 --- a/nym-wallet/Cargo.lock +++ b/nym-wallet/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "NymWallet" @@ -773,7 +773,7 @@ dependencies = [ [[package]] name = "bls12_381" version = "0.8.0" -source = "git+https://github.com/jstuczyn/bls12_381?branch=temp/experimental-serdect-updated#9bf520059cb28323fc51469cae86868ef4fa6fbd" +source = "git+https://github.com/jstuczyn/bls12_381?branch=temp%2Fexperimental-serdect-updated#9bf520059cb28323fc51469cae86868ef4fa6fbd" dependencies = [ "digest 0.10.7", "ff", @@ -1723,15 +1723,6 @@ dependencies = [ "dirs-sys 0.3.7", ] -[[package]] -name = "dirs" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" -dependencies = [ - "dirs-sys 0.4.1", -] - [[package]] name = "dirs" version = "6.0.0" @@ -1752,18 +1743,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "dirs-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" -dependencies = [ - "libc", - "option-ext", - "redox_users 0.4.6", - "windows-sys 0.48.0", -] - [[package]] name = "dirs-sys" version = "0.5.0" @@ -4100,7 +4079,7 @@ dependencies = [ name = "nym-config" version = "0.1.0" dependencies = [ - "dirs 5.0.1", + "dirs 6.0.0", "handlebars", "log", "nym-network-defaults",