Skip to content

fix(wasm-sdk): protocol upgrade state misreported vote count as activation height#3979

Merged
shumkov merged 3 commits into
v4.0-devfrom
claude/gracious-dewdney-fa2854
Jul 2, 2026
Merged

fix(wasm-sdk): protocol upgrade state misreported vote count as activation height#3979
shumkov merged 3 commits into
v4.0-devfrom
claude/gracious-dewdney-fa2854

Conversation

@QuantumExplorer

@QuantumExplorer QuantumExplorer commented Jul 2, 2026

Copy link
Copy Markdown
Member

Issue being fixed or feature implemented

getProtocolVersionUpgradeState (and its WithProofInfo variant) misinterpreted the result of ProtocolVersionVoteCount::fetch_many. The returned IndexMap<u32, u64> maps protocol version → vote count (pub type ProtocolVersionVoteCount = u64), but the binding assigned the count to activationHeight, left voteCount as a None TODO, and set isThresholdReached whenever any vote existed. Verified live on mainnet: activationHeight returned 132 — exactly the number of PV12 votes — with thresholdReached: true from a single vote onward.

currentProtocolVersion was also taken from the SDK's own configured version (self.version()), which can be pinned or stale, rather than from chain state.

What was done?

  • Populate voteCount (now Option<u64>) from the fetched map.
  • Remove activationHeight and isThresholdReached from ProtocolVersionUpgradeState: the activation height is not present in this response, and the vote threshold cannot be derived without the active evonode count, for which the WASM SDK has no proven query. Reporting nothing is better than reporting wrong data.
  • Take currentProtocolVersion from the response metadata's protocol_version (chain state at response time); the plain variant switched to fetch_many_with_metadata, which costs no extra network round trip.
  • Pick nextProtocolVersion as the future version with the most votes (shared helper for both variants) instead of the first map entry.
  • Update unit-test fixtures for the new shape.

Not affected: rs-sdk-ffi's equivalent binding already interprets the map correctly as vote counts, and the js-evo-sdk protocol facade is a pass-through whose types come from the generated .d.ts.

How Has This Been Tested?

  • cargo check/cargo clippy -p wasm-sdk --target wasm32-unknown-unknown clean.
  • Full wasm-sdk unit suite (mocha + karma, 397 tests) passes against the rebuilt bundle.

Breaking Changes

None consensus-wise. SDK API surface: ProtocolVersionUpgradeState.activationHeight and .isThresholdReached are removed, and voteCount changes from always-null to a populated bigint — pre-release v4.0 API cleanup. Consumers reading the removed fields now get undefined instead of a wrong number.

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have added "!" to the title and described breaking changes in the corresponding section if my code contains any
  • I have made corresponding changes to the documentation if needed

For repository code-owners and collaborators only

  • I have assigned this pull request to a milestone

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Updated protocol upgrade state responses to return a simpler set of fields, removing unsupported activation-status details.
    • Improved vote count handling so large values are preserved accurately.
    • Refined upgrade-state selection to better identify the next available protocol version and its vote count.
  • Tests
    • Adjusted unit coverage to match the updated response shape and optional-value behavior.

…ation 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 <noreply@anthropic.com>
@QuantumExplorer QuantumExplorer requested a review from shumkov as a code owner July 2, 2026 10:23
@github-actions github-actions Bot added this to the v4.0.0 milestone Jul 2, 2026
@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@QuantumExplorer, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 46 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6b58482e-30d2-4a99-b3cb-83d95f547267

📥 Commits

Reviewing files that changed from the base of the PR and between a8557e5 and c96f199.

📒 Files selected for processing (1)
  • packages/wasm-sdk/src/queries/protocol.rs
📝 Walkthrough

Walkthrough

The ProtocolVersionUpgradeStateWasm struct is simplified to drop activation_height and is_threshold_reached fields, changing vote_count to Option. Callers now derive current version from fetch metadata and use a new next_version_upgrade helper. Unit tests are updated accordingly.

Changes

Protocol version upgrade state simplification

Layer / File(s) Summary
Struct and helper changes
packages/wasm-sdk/src/queries/protocol.rs
ProtocolVersionUpgradeStateWasm drops activation_height and is_threshold_reached, changes vote_count to Option<u64>, updates the constructor/getters, and adds a next_version_upgrade helper that selects the highest-vote candidate version newer than the current one.
Caller updates in WasmSdk query methods
packages/wasm-sdk/src/queries/protocol.rs
getProtocolVersionUpgradeState and getProtocolVersionUpgradeStateWithProofInfo fetch upgrades with metadata, derive current version from metadata.protocol_version, and use next_version_upgrade in place of the prior manual selection loops.
Unit test updates for reduced shape
packages/wasm-sdk/tests/unit/conversion-simple-types.spec.ts
Fixtures and assertions are updated so voteCount is expected as BigInt, and isThresholdReached/activationHeight expectations are removed.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant WasmSdk
  participant ProtocolVersionVoteCount
  participant next_version_upgrade
  participant ProtocolVersionUpgradeStateWasm

  WasmSdk->>ProtocolVersionVoteCount: fetch_many_with_metadata
  ProtocolVersionVoteCount-->>WasmSdk: metadata + upgrade_result
  WasmSdk->>next_version_upgrade: upgrade_result, current_version
  next_version_upgrade-->>WasmSdk: next_version, vote_count
  WasmSdk->>ProtocolVersionUpgradeStateWasm: new(current_version, next_version, vote_count)
Loading

Related issues: None specified.

Related PRs: None specified.

Suggested labels: wasm-sdk, breaking-change

Suggested reviewers: None specified.

🐰 A vote is cast, a height set free,
Just current, next, and count of three,
The struct grows lean, the tests align,
BigInt votes now fall in line.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main fix: protocol upgrade state was reporting vote count as activation height.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/gracious-dewdney-fa2854

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@thepastaclaw

thepastaclaw commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

✅ Review complete (commit c96f199)

@shumkov shumkov modified the milestones: v4.0.0, v4.0.x Jul 2, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/wasm-sdk/src/queries/protocol.rs (1)

64-70: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Make equal-vote selection deterministic.
ProtocolVersionUpgrades is built from a hash-map-like IntMap, so equal vote counts can resolve in arbitrary order. If the contract is “highest votes, then highest version,” key on (votes, version) here.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/wasm-sdk/src/queries/protocol.rs` around lines 64 - 70, The
equal-vote upgrade selection in ProtocolVersionUpgrades is nondeterministic
because max_by_key currently compares only vote counts from the upgrades
iterator. Update the selection logic in the ProtocolVersionUpgrades handling so
that it keys on both votes and version, choosing the highest vote count and then
the highest version as the tie-breaker. Keep the change localized to the
method/closure that filters upgrades and returns the selected version/votes
pair.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/wasm-sdk/src/queries/protocol.rs`:
- Around line 64-70: The equal-vote upgrade selection in ProtocolVersionUpgrades
is nondeterministic because max_by_key currently compares only vote counts from
the upgrades iterator. Update the selection logic in the ProtocolVersionUpgrades
handling so that it keys on both votes and version, choosing the highest vote
count and then the highest version as the tie-breaker. Keep the change localized
to the method/closure that filters upgrades and returns the selected
version/votes pair.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 62fa8cf2-3da0-46c9-928f-3bb117c1bb95

📥 Commits

Reviewing files that changed from the base of the PR and between b614938 and a8557e5.

📒 Files selected for processing (2)
  • packages/wasm-sdk/src/queries/protocol.rs
  • packages/wasm-sdk/tests/unit/conversion-simple-types.spec.ts

…sion

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@QuantumExplorer

Copy link
Copy Markdown
Member Author

Addressed the CodeRabbit nitpick in 1894166: next_version_upgrade now keys on (votes, version), so equal vote counts deterministically resolve to the highest version.

🤖 Addressed by Claude Code

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

Focused, correct bug fix for the wasm-sdk protocol-upgrade-state binding: vote counts are now read from the map values, unrepresentable fields are dropped, and currentProtocolVersion comes from ResponseMetadata. No consensus, security, or FFI concerns. Only in-scope observation is that the new next_version_upgrade selection helper has no direct unit test.

🟡 1 suggestion(s)

🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `packages/wasm-sdk/src/queries/protocol.rs`:
- [SUGGESTION] packages/wasm-sdk/src/queries/protocol.rs:60-72: Add a direct unit test for `next_version_upgrade`
  `next_version_upgrade` centralizes the non-trivial selection logic used by both `getProtocolVersionUpgradeState` and its `WithProofInfo` variant — skip `None` entries, exclude versions `<= current_version`, and pick the max-vote future version. The new `conversion-simple-types.spec.ts` cases only round-trip the WASM struct's JSON/Object shape with a single-candidate fixture; they never construct a `ProtocolVersionUpgrades` map with multiple future versions or a mix of `None`/`Some` entries. Given the original bug was precisely a misinterpretation of this map (activation height read as vote count, single vote read as threshold reached), a `#[cfg(test)]` covering (a) empty upgrades, (b) only-`None` values, (c) all entries `<= current_version`, and (d) max-vote selection among multiple future versions would cheaply protect against a regression in the exact spot most likely to break again.

Comment thread packages/wasm-sdk/src/queries/protocol.rs
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

PR #3979 remains a narrow, correct wasm-sdk fix for getProtocolVersionUpgradeState. The latest delta (a8557e5..1894166) makes the next_version_upgrade tie-breaker deterministic via max_by_key(|&(version, votes)| (votes, version)) and updates the docstring — this cleanly resolves a prior low-confidence tie-break concern. No new correctness, consensus, security, or FFI issues in the delta. Carried forward: the prior test-coverage suggestion (prior-1) for next_version_upgrade remains valid — no #[cfg(test)] was added, and the new tie-break behavior is not exercised by any test.

🟡 1 suggestion(s)

Carried-forward findings already raised (1)

These findings were not re-posted as new inline comments because an existing review thread already covers them.

  • [SUGGESTION] (deduped existing open thread) packages/wasm-sdk/src/queries/protocol.rs:61-73: Add a direct unit test for next_version_upgrade — Carried forward from the prior review (prior-1) and sharpened by this delta. next_version_upgrade centralizes the selection logic used by both getProtocolVersionUpgradeState and its WithProofInfo variant: skip None entries, exclude versions <= current_version, pick the max-vote future v...
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `packages/wasm-sdk/src/queries/protocol.rs`:
- [SUGGESTION] packages/wasm-sdk/src/queries/protocol.rs:61-73: Add a direct unit test for `next_version_upgrade`
  Carried forward from the prior review (prior-1) and sharpened by this delta. `next_version_upgrade` centralizes the selection logic used by both `getProtocolVersionUpgradeState` and its `WithProofInfo` variant: skip `None` entries, exclude versions `<= current_version`, pick the max-vote future version, and — new in this delta — break ties by highest version via the tuple key `(votes, version)`. The existing `conversion-simple-types.spec.ts` only round-trips the WASM struct's JSON/Object shape with a single-candidate fixture; it never constructs a multi-entry `ProtocolVersionUpgrades` map, so it cannot catch regressions in the selection or tie-break logic. Given the original bug was a misinterpretation of this same map, a small `#[cfg(test)]` covering (a) empty upgrades, (b) only-`None` values, (c) all entries `<= current_version`, (d) max-vote selection among multiple future versions, and (e) tie-break resolving to the higher version would cheaply protect the exact spot that has now been modified twice.

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

Latest delta (1894166..c96f199) is test-only: adds a #[cfg(test)] module in packages/wasm-sdk/src/queries/protocol.rs covering next_version_upgrade with five cases (empty map, all-None votes, versions <= current excluded, max-vote selection, equal-vote tie-break to highest version). This directly resolves the sole carried-forward prior finding (prior-1). No new correctness, consensus, security, or FFI issues in the delta or cumulative PR.

@shumkov shumkov merged commit 357aff1 into v4.0-dev Jul 2, 2026
22 checks passed
@shumkov shumkov deleted the claude/gracious-dewdney-fa2854 branch July 2, 2026 12:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants