From a8557e5ea4d74377999d26182e6a6b6b0839b0e3 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Thu, 2 Jul 2026 17:22:42 +0700 Subject: [PATCH 1/3] fix(wasm-sdk): protocol upgrade state misreported vote count as activation height ProtocolVersionVoteCount::fetch_many returns a map of protocol version to vote count, but getProtocolVersionUpgradeState assigned the count to activationHeight, left voteCount null, and reported thresholdReached whenever any vote existed. Populate voteCount from the fetched map, drop the activationHeight and isThresholdReached fields (neither is present in or derivable from this response), take currentProtocolVersion from the response metadata instead of the SDK's own configured version, and pick the future version with the most votes as nextProtocolVersion. Co-Authored-By: Claude Fable 5 --- packages/wasm-sdk/src/queries/protocol.rs | 103 ++++++------------ .../unit/conversion-simple-types.spec.ts | 13 +-- 2 files changed, 36 insertions(+), 80 deletions(-) diff --git a/packages/wasm-sdk/src/queries/protocol.rs b/packages/wasm-sdk/src/queries/protocol.rs index 8a7a0b60fba..5109cb75715 100644 --- a/packages/wasm-sdk/src/queries/protocol.rs +++ b/packages/wasm-sdk/src/queries/protocol.rs @@ -16,56 +16,59 @@ use wasm_dpp2::{ProTxHashLikeNullableJs, ProTxHashWasm}; pub struct ProtocolVersionUpgradeStateWasm { current_protocol_version: u32, next_protocol_version: Option, - activation_height: Option, - vote_count: Option, - #[serde(rename = "thresholdReached")] - is_threshold_reached: bool, + vote_count: Option, } impl ProtocolVersionUpgradeStateWasm { fn new( current_protocol_version: u32, next_protocol_version: Option, - activation_height: Option, - vote_count: Option, - is_threshold_reached: bool, + vote_count: Option, ) -> Self { Self { current_protocol_version, next_protocol_version, - activation_height, vote_count, - is_threshold_reached, } } } #[wasm_bindgen(js_class = ProtocolVersionUpgradeState)] impl ProtocolVersionUpgradeStateWasm { + /// Protocol version the chain was running when this response was produced. #[wasm_bindgen(getter = "currentProtocolVersion")] pub fn current_protocol_version(&self) -> u32 { self.current_protocol_version } + /// Candidate upgrade version: the version above the current one with the + /// most evonode votes, if any votes exist. #[wasm_bindgen(getter = "nextProtocolVersion")] pub fn next_protocol_version(&self) -> Option { self.next_protocol_version } - #[wasm_bindgen(getter = "activationHeight")] - pub fn activation_height(&self) -> Option { - self.activation_height - } - + /// Number of evonode votes cast for `nextProtocolVersion`. #[wasm_bindgen(getter = "voteCount")] - pub fn vote_count(&self) -> Option { + pub fn vote_count(&self) -> Option { self.vote_count } +} - #[wasm_bindgen(getter = "isThresholdReached")] - pub fn is_threshold_reached(&self) -> bool { - self.is_threshold_reached - } +/// Pick the candidate upgrade version from the vote counts: among versions +/// newer than `current_version`, the one with the most votes. +fn next_version_upgrade( + upgrades: &drive_proof_verifier::types::ProtocolVersionUpgrades, + current_version: u32, +) -> (Option, Option) { + upgrades + .iter() + .filter_map(|(version, votes)| votes.map(|votes| (*version, votes))) + .filter(|(version, _)| *version > current_version) + .max_by_key(|(_, votes)| *votes) + .map_or((None, None), |(version, votes)| { + (Some(version), Some(votes)) + }) } #[wasm_bindgen(js_name = "ProtocolVersionUpgradeVoteStatus")] @@ -108,36 +111,18 @@ impl WasmSdk { use dash_sdk::platform::FetchMany; use drive_proof_verifier::types::ProtocolVersionVoteCount; - let upgrade_result: drive_proof_verifier::types::ProtocolVersionUpgrades = - ProtocolVersionVoteCount::fetch_many(self.as_ref(), ()).await?; - - // Get the current protocol version from the SDK - let current_version = self.version(); - - // Find the next version with votes - let mut next_version = None; - let mut activation_height = None; - let mut vote_count = None; - let mut threshold_reached = false; - - // The result is an IndexMap> where u32 is version and Option is activation height - for (version, height_opt) in upgrade_result.iter() { - if *version > current_version { - next_version = Some(*version); - activation_height = *height_opt; - // TODO: Get actual vote count and threshold from platform - vote_count = None; - threshold_reached = height_opt.is_some(); - break; - } - } + let (upgrade_result, metadata): (drive_proof_verifier::types::ProtocolVersionUpgrades, _) = + ProtocolVersionVoteCount::fetch_many_with_metadata(self.as_ref(), (), None).await?; + + // The chain's protocol version at the time of the response + let current_version = metadata.protocol_version; + + let (next_version, vote_count) = next_version_upgrade(&upgrade_result, current_version); Ok(ProtocolVersionUpgradeStateWasm::new( current_version, next_version, - activation_height, vote_count, - threshold_reached, )) } @@ -194,32 +179,12 @@ impl WasmSdk { ) = ProtocolVersionVoteCount::fetch_many_with_metadata_and_proof(self.as_ref(), (), None) .await?; - // Get the current protocol version from the SDK - let current_version = self.version(); - - // Find the next version with votes - let mut next_version = None; - let mut activation_height = None; - let mut vote_count = None; - let mut threshold_reached = false; - - for (version, height_opt) in upgrade_result.iter() { - if *version > current_version { - next_version = Some(*version); - activation_height = *height_opt; - vote_count = None; - threshold_reached = height_opt.is_some(); - break; - } - } + // The chain's protocol version at the time of the response + let current_version = metadata.protocol_version; - let state = ProtocolVersionUpgradeStateWasm::new( - current_version, - next_version, - activation_height, - vote_count, - threshold_reached, - ); + let (next_version, vote_count) = next_version_upgrade(&upgrade_result, current_version); + + let state = ProtocolVersionUpgradeStateWasm::new(current_version, next_version, vote_count); Ok(ProofMetadataResponseWasm::from_sdk_parts( state, metadata, proof, diff --git a/packages/wasm-sdk/tests/unit/conversion-simple-types.spec.ts b/packages/wasm-sdk/tests/unit/conversion-simple-types.spec.ts index e3fb1eb7521..24eed82a766 100644 --- a/packages/wasm-sdk/tests/unit/conversion-simple-types.spec.ts +++ b/packages/wasm-sdk/tests/unit/conversion-simple-types.spec.ts @@ -52,17 +52,13 @@ describe('Simple Type Conversions', () => { const jsonFixture = { currentProtocolVersion: 7, nextProtocolVersion: 8, - activationHeight: 50000, voteCount: 100, - thresholdReached: false, }; const objectFixture = { currentProtocolVersion: 7, nextProtocolVersion: 8, - activationHeight: 50000n, - voteCount: 100, - thresholdReached: false, + voteCount: 100n, }; describe('toJSON()', () => { @@ -84,8 +80,7 @@ describe('Simple Type Conversions', () => { const result = sdk.ProtocolVersionUpgradeState.fromJSON(jsonFixture); expect(result.currentProtocolVersion).to.equal(7); expect(result.nextProtocolVersion).to.equal(8); - expect(result.voteCount).to.equal(100); - expect(result.isThresholdReached).to.equal(false); + expect(result.voteCount).to.equal(100n); }); }); @@ -94,17 +89,13 @@ describe('Simple Type Conversions', () => { const fixture = { currentProtocolVersion: 7, nextProtocolVersion: null, - activationHeight: null, voteCount: null, - thresholdReached: true, }; const result = sdk.ProtocolVersionUpgradeState.fromJSON(fixture); expect(result.currentProtocolVersion).to.equal(7); expect(result.nextProtocolVersion).to.be.undefined(); - expect(result.activationHeight).to.be.undefined(); expect(result.voteCount).to.be.undefined(); - expect(result.isThresholdReached).to.equal(true); }); }); }); From 1894166b5d3f25918a27a1b14549a7afd8d49d78 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Thu, 2 Jul 2026 17:29:24 +0700 Subject: [PATCH 2/3] fix(wasm-sdk): break equal-vote ties deterministically by highest version Co-Authored-By: Claude Fable 5 --- packages/wasm-sdk/src/queries/protocol.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/wasm-sdk/src/queries/protocol.rs b/packages/wasm-sdk/src/queries/protocol.rs index 5109cb75715..756c5b900f8 100644 --- a/packages/wasm-sdk/src/queries/protocol.rs +++ b/packages/wasm-sdk/src/queries/protocol.rs @@ -56,7 +56,8 @@ impl ProtocolVersionUpgradeStateWasm { } /// Pick the candidate upgrade version from the vote counts: among versions -/// newer than `current_version`, the one with the most votes. +/// newer than `current_version`, the one with the most votes; equal vote +/// counts resolve to the highest version. fn next_version_upgrade( upgrades: &drive_proof_verifier::types::ProtocolVersionUpgrades, current_version: u32, @@ -65,7 +66,7 @@ fn next_version_upgrade( .iter() .filter_map(|(version, votes)| votes.map(|votes| (*version, votes))) .filter(|(version, _)| *version > current_version) - .max_by_key(|(_, votes)| *votes) + .max_by_key(|&(version, votes)| (votes, version)) .map_or((None, None), |(version, votes)| { (Some(version), Some(votes)) }) From c96f19960a02303ef58d6a81706ceaf206c7ec90 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Thu, 2 Jul 2026 17:36:22 +0700 Subject: [PATCH 3/3] test(wasm-sdk): cover next_version_upgrade selection logic directly Co-Authored-By: Claude Fable 5 --- packages/wasm-sdk/src/queries/protocol.rs | 46 +++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/packages/wasm-sdk/src/queries/protocol.rs b/packages/wasm-sdk/src/queries/protocol.rs index 756c5b900f8..f6cfaf59017 100644 --- a/packages/wasm-sdk/src/queries/protocol.rs +++ b/packages/wasm-sdk/src/queries/protocol.rs @@ -72,6 +72,52 @@ fn next_version_upgrade( }) } +#[cfg(test)] +mod tests { + use super::next_version_upgrade; + use drive_proof_verifier::types::ProtocolVersionUpgrades; + + #[test] + fn empty_upgrades_yield_no_candidate() { + let upgrades = ProtocolVersionUpgrades::new(); + + assert_eq!(next_version_upgrade(&upgrades, 12), (None, None)); + } + + #[test] + fn none_vote_counts_are_skipped() { + let upgrades = ProtocolVersionUpgrades::from_iter([(13, None), (14, None)]); + + assert_eq!(next_version_upgrade(&upgrades, 12), (None, None)); + } + + #[test] + fn versions_at_or_below_current_are_excluded() { + let upgrades = ProtocolVersionUpgrades::from_iter([(11, Some(50)), (12, Some(80))]); + + assert_eq!(next_version_upgrade(&upgrades, 12), (None, None)); + } + + #[test] + fn picks_future_version_with_most_votes() { + let upgrades = ProtocolVersionUpgrades::from_iter([ + (11, Some(200)), + (13, Some(7)), + (14, Some(132)), + (15, Some(3)), + ]); + + assert_eq!(next_version_upgrade(&upgrades, 12), (Some(14), Some(132))); + } + + #[test] + fn equal_votes_resolve_to_highest_version() { + let upgrades = ProtocolVersionUpgrades::from_iter([(13, Some(9)), (14, Some(9))]); + + assert_eq!(next_version_upgrade(&upgrades, 12), (Some(14), Some(9))); + } +} + #[wasm_bindgen(js_name = "ProtocolVersionUpgradeVoteStatus")] #[derive(Clone)] pub struct ProtocolVersionUpgradeVoteStatusWasm {