Skip to content

feat(governance): add counter-vote support and get_proposals_by_proposer - #210

Merged
snowrugar-beep merged 30 commits into
VertexChainLabs:mainfrom
DammyAji:feat/governance-proposer-query-counter-votes
Jul 22, 2026
Merged

feat(governance): add counter-vote support and get_proposals_by_proposer#210
snowrugar-beep merged 30 commits into
VertexChainLabs:mainfrom
DammyAji:feat/governance-proposer-query-counter-votes

Conversation

@DammyAji

@DammyAji DammyAji commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Summary

Resolves issue #131 — upgrades the governance contract to support counter-voting and proposer-scoped proposal enumeration.

Closes #131


Problem

contracts/governance/src/lib.rs previously tracked only approvals. Voters had no way to signal "no", and proposers could not enumerate their own proposals for follow-up.

Changes

Vote storage upgrade

  • Replaced the bare bool vote value with VoteRecord { vote_power: u32, against: bool }
  • Stored under the same UserVote(proposal_id, voter) key — key schema unchanged
  • Added rejections: u32 field to Proposal struct

Counter-vote support

  • vote_proposal(voter, proposal_id, against: bool) now accepts direction
    • against = false → increments proposal.approvals
    • against = true → increments proposal.rejections
  • A voter may only cast one vote per proposal; flipping direction is also blocked (AlreadyVoted error)
  • voted event extended with against flag for off-chain indexers

get_proposals_by_proposer

  • New ProposalsByProposer(Address) persistent storage key maintains a Vec<u32> of proposal IDs per proposer
  • Index updated atomically inside create_proposal
  • get_proposals_by_proposer(addr) -> Vec<u32> exposed on the contract; returns empty Vec for unknown addresses

Migration path (acceptance criterion)

  • migrate_votes(admin, snapshot_votes: Vec<(u32, Address)>) — one-shot, admin-only function
  • Operator supplies a pre-upgrade snapshot of all UserVote keys (read off-chain before re-deploy)
  • Writes each entry as VoteRecord { vote_power: 1, against: false } (all pre-upgrade votes were approvals)
  • Protected by MigrationDone instance-storage flag; second call panics with MigrationAlreadyDone = 11
  • migration_done event emits count of migrated entries

Tests

15 tests total — 3 original preserved + 12 new:

Test Covers
test_vote_against_increments_rejections against=true increments rejections only
test_vote_for_increments_approvals_not_rejections against=false increments approvals only
test_mixed_votes_tallied_correctly mixed for/against tally
test_cannot_vote_twice_same_direction duplicate vote blocked
test_cannot_flip_vote_direction direction flip blocked
test_get_proposals_by_proposer_empty returns empty Vec
test_get_proposals_by_proposer_single single proposal indexed
test_get_proposals_by_proposer_multiple multiple proposals indexed in order
test_proposals_by_proposer_are_isolated_per_address no cross-proposer leakage
test_migrate_votes_runs_once migration writes VoteRecord entries
test_migrate_votes_cannot_run_twice MigrationAlreadyDone (#11) on second call
test_migrate_votes_requires_admin Unauthorized (#3) for non-admin

CI Verification

All CI steps pass locally against the exact same toolchain (dtolnay/rust-toolchain@stable):

cargo fmt --all -- --check       ✅
cargo clippy --workspace -- -D warnings  ✅  (0 warnings)
cargo check --workspace --all-targets    ✅
cargo test --workspace           ✅  27 tests, 0 failed
cargo build --target wasm32-unknown-unknown --release  ✅

Merge conflicts

None — branch is based on the current tip of main (33a67ab). git merge --no-ff origin/main → Already up to date.


Files changed: contracts/governance/src/lib.rs

Closes #131

@DammyAji

Copy link
Copy Markdown
Contributor Author

@snowrugar-beep please kindly review. Thank you

@snowrugar-beep

Copy link
Copy Markdown
Contributor

@DammyAji resolve confliicts.

@DammyAji

Copy link
Copy Markdown
Contributor Author

@snowrugar-beep i have resolved conflicts but i think i did something i was not supposed to do. Can you please point it out to me.

Copy link
Copy Markdown
Contributor

Heads-up: CI was green a moment ago but this PR is no longer marked MERGEABLE, so I didn't auto-merge it. Could you rebase against main and re-push? \xf0\x9f\x99\x8c

Copy link
Copy Markdown
Contributor

Hey \xf0\x9f\x91\x8b \xe2\x80\x94 small update: my very first note on this PR suggested the mergeable status was a transient hiccup. After more careful checking, the branch actually has merge conflicts against main now. Could you rebase against the latest main and re-push? Once that's clean we can merge right away. Thanks \xf0\x9f\x99\x8c, and apologies for the confusion in the earlier message!

@DammyAji
DammyAji force-pushed the feat/governance-proposer-query-counter-votes branch 2 times, most recently from 2794fc5 to 3f493be Compare July 19, 2026 22:05
@DammyAji

Copy link
Copy Markdown
Contributor Author

@snowrugar-beep please check now and review

@snowrugar-beep

Copy link
Copy Markdown
Contributor

@DammyAji resolve conflicts please.

BigJohn-dev and others added 21 commits July 21, 2026 11:22
…VertexChainLabs#186)

Expand SECURITY.md with coordinated-disclosure contact, attack-path flow diagrams, and tables for anonymous posting abuse, IPFS/cache injection, and Soroban replay threats — each with owner, mitigation, and linked implementation or PR.

Co-authored-by: Cursor <cursoragent@cursor.com>
VertexChainLabs#228)

* ci: add Trivy vulnerability scan workflow for Postgres and infrastructure

* fix(ci): update system packages in postgres.Dockerfile and force-pull latest in workflow to resolve Trivy CVE checks

* fix(ci): add .trivyignore to ignore Go CVE-2026-42504

* fix(ci): add CVE-2026-42499 to .trivyignore

* fix(ci): add CVE-2026-39836 to .trivyignore

* fix(ci): add all Go CVEs to .trivyignore to pass Postgres image scan

* fix(ci): overwrite vulnerable gosu with patched package and update .trivyignore

* fix(docker): resolve gosu CVE-2026-42504 via multi-stage build

* fix(docker): clone and compile gosu from source to fix builder tag issue
…hainLabs#226)

- Add stripUserContent() function to remove HTML and bidi controls
- Remove U+202A-U+202E (LRE, RLE, PDF, LRO, RLO) characters
- Remove U+2066-U+2069 (LRI, RLI, FSI, PDI) characters
- Preserve all printable Unicode, emoji, and ZWJ sequences
- Add 26 comprehensive tests covering bidi removal and content preservation
- Fix adversarial content vulnerability that breaks UI trust
* feat(observability): retire static pipeline-metrics.json and implement live /metrics endpoint

* docs: add initial 6 architecture decision records (ADRs)

* docs: add PR title template guidelines

* ci: allow PR title check to continue on error
)

- Add renovate.json5 at repo root with package grouping for all four
  workspaces: Frontend (Next.js), Backend (NestJS), analytics (Next.js),
  and contracts (Rust/Soroban via Cargo).
- Add .github/workflows/renovate.yml that runs renovatebot/github-action@v41
  on a daily schedule (03:00 UTC) and supports manual workflow_dispatch with
  an optional dry-run toggle.
- Package rules: workspace-scoped groups, NestJS and soroban-sdk pinned with
  stabilisation windows, patch auto-merge, GitHub Actions grouped and
  patch-auto-merged, major bumps labelled 'major-upgrade'.
- Vulnerability alerts enabled for immediate CVE remediation.

Closes VertexChainLabs#158
…VertexChainLabs#215)

* feat(gists): allow authors to correct a gist within a 60s edit window

* fix(contracts): pin Cargo.lock to fix CI build break

soroban-env-host 22.1.3 declares ed25519-dalek as ">=2.0.0" with no
upper bound. Without a committed lockfile, cargo re-resolved deps on
every CI run and picked up ed25519-dalek 3.0.0, whose CryptoRng trait
is incompatible with the rand_chacha-based RNG soroban-env-host uses
internally in its own testutils, breaking the build with E0277.

Pin ed25519-dalek to 2.2.0 (still satisfies the >=2.0.0 constraint)
and commit Cargo.lock so builds are reproducible instead of drifting
with upstream releases.
Replaces Dependabot version-update PRs with manual dependency management. Adds .github/dependabot.yml with version: 2 and an empty updates: list. Also disables dependabot_security_updates at the repo level via the GitHub API.
Dependabot version updates are disabled via .github/dependabot.yml (version: 2, updates: []), so the auto-merge workflow added by PR VertexChainLabs#197 has nothing left to act on and is misleading dead code. Removes .github/workflows/auto-merge.yml.
Removes preliminary architecture decision records 0001-0006 (Stellar-as-blockchain, geohash, mock-mode defaults, postgres extensions, postgis-only, hybrid on/off-chain storage). They captured early-stage opinions that have been superseded by current implementations. Preserved in git history for reference.
Adds two Node.js scripts under infrastructure/scripts/:
   - import-issues.mjs: parses docs/ISSUES_100.md into a structured JSON with one entry per issue (titles, bodies, labels). Validates that exactly 100 sequential VertexChainLabs#1..VertexChainLabs#100 issues are produced.
   - import-issues-create.mjs: consumes that JSON and uses `gh issue create` / `gh label create` to import them into VertexChainLabs/VertexChain. Idempotent (skips already-created issues by sourceNumber via a local JSONL log), handles 429 rate limits (sleeps on `try again in Ns`), uses stable label-color hex derived from label names, intentionally does NOT pass `--force` when creating labels so maintainer-set colors/descriptions are preserved, and supports --dry-run for a preview.
Bumps [caniuse-lite](https://github.com/browserslist/caniuse-lite) from 1.0.30001799 to 1.0.30001806.
- [Commits](browserslist/caniuse-lite@1.0.30001799...1.0.30001806)

---
updated-dependencies:
- dependency-name: caniuse-lite
  dependency-version: 1.0.30001806
  dependency-type: indirect
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
dependabot Bot and others added 8 commits July 21, 2026 11:22
…exChainLabs#329)

Bumps [globals](https://github.com/sindresorhus/globals) from 16.5.0 to 17.7.0.
- [Release notes](https://github.com/sindresorhus/globals/releases)
- [Commits](sindresorhus/globals@v16.5.0...v17.7.0)

---
updated-dependencies:
- dependency-name: globals
  dependency-version: 17.7.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [@nestjs/testing](https://github.com/nestjs/nest/tree/HEAD/packages/testing) from 11.1.27 to 11.1.28.
- [Release notes](https://github.com/nestjs/nest/releases)
- [Commits](https://github.com/nestjs/nest/commits/v11.1.28/packages/testing)

---
updated-dependencies:
- dependency-name: "@nestjs/testing"
  dependency-version: 11.1.28
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
VertexChainLabs#327)

Bumps [@types/supertest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/supertest) from 6.0.3 to 7.2.1.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/supertest)

---
updated-dependencies:
- dependency-name: "@types/supertest"
  dependency-version: 7.2.1
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…VertexChainLabs#326)

Bumps [libphonenumber-js](https://gitlab.com/catamphetamine/libphonenumber-js) from 1.13.7 to 1.13.9.
- [Changelog](https://gitlab.com/catamphetamine/libphonenumber-js/blob/master/CHANGELOG.md)
- [Commits](https://gitlab.com/catamphetamine/libphonenumber-js/commits/master)

---
updated-dependencies:
- dependency-name: libphonenumber-js
  dependency-version: 1.13.9
  dependency-type: indirect
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…inLabs#325)

Bumps [nanoid](https://github.com/ai/nanoid) from 3.3.14 to 3.3.16.
- [Release notes](https://github.com/ai/nanoid/releases)
- [Changelog](https://github.com/ai/nanoid/blob/main/CHANGELOG.md)
- [Commits](ai/nanoid@3.3.14...3.3.16)

---
updated-dependencies:
- dependency-name: nanoid
  dependency-version: 3.3.16
  dependency-type: indirect
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Deletes .github/dependabot.yml so Dependabot version updates are no longer configured for this repository. The Dependabot auto-merge workflow (.github/workflows/auto-merge.yml) was already removed in commit a9fb74d, and all current Dependabot PRs have been closed. Future contributors would need to actively re-add the config to bring Dependabot back.
- Upgrade vote storage from bare bool to VoteRecord { vote_power: u32, against: bool }
- Add rejections: u32 field to Proposal struct to track against-votes
- Update vote_proposal() to accept against: bool parameter;
  against=false increments approvals, against=true increments rejections
- Prevent vote flipping: AlreadyVoted error covers both duplicate and
  direction-change attempts (checked via storage key existence)
- Add ProposalsByProposer(Address) storage key maintaining Vec<u32> of
  proposal IDs per proposer, updated atomically in create_proposal()
- Expose get_proposals_by_proposer(addr) -> Vec<u32> on contract;
  returns empty Vec for addresses with no proposals
- Add migrate_votes(admin, snapshot_votes) one-shot migration function
  for existing on-chain bool votes pre-upgrade; guarded by MigrationDone
  flag (new error MigrationAlreadyDone = 11), admin-only
- Extend voted event to include against flag for off-chain indexers
- Add migration_done event emitting count of migrated entries
- Add 12 new tests covering counter-votes, proposer index, and migration;
  all 15 tests pass (cargo test --workspace: 27 total, 0 failed)
- cargo fmt, clippy -D warnings, cargo check, wasm32 release build all pass

Closes VertexChainLabs#131
…-tags

RUSTDOCFLAGS=-D warnings treats bare Vec<u32> in a doc comment as an
unclosed HTML tag <u32> (rustdoc::invalid-html-tags lint).  Wrapping
the type in backticks makes it a code span, not markup.

Also tighten adjacent doc-comment wording: backtick-quote VoteRecord,
migrate_votes and Address references for consistency, and use an
intra-doc link for VoteRecord in the migrate_votes module doc.
@DammyAji
DammyAji force-pushed the feat/governance-proposer-query-counter-votes branch from 77efa72 to 5a2916e Compare July 21, 2026 11:23
…poser-query-counter-votes

# Conflicts:
#	Backend/src/common/utils/sanitize.spec.ts
#	charts/vertexchain/values.yaml
#	contracts/governance/src/lib.rs
#	infrastructure/docker/postgres.Dockerfile
@DammyAji

Copy link
Copy Markdown
Contributor Author

hey @snowrugar-beep please check now

@snowrugar-beep snowrugar-beep 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.

LGTM

@snowrugar-beep
snowrugar-beep merged commit d8312c2 into VertexChainLabs:main Jul 22, 2026
20 checks passed
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.

Add get_proposals_by_proposer + counter-vote support to Governance