Skip to content

fix: keep governance vote auth on signed wallet proof#6781

Merged
Scottcjn merged 1 commit into
Scottcjn:mainfrom
luisalias007-cmyk:codex/governance-vote-signed-auth
Jun 5, 2026
Merged

fix: keep governance vote auth on signed wallet proof#6781
Scottcjn merged 1 commit into
Scottcjn:mainfrom
luisalias007-cmyk:codex/governance-vote-signed-auth

Conversation

@luisalias007-cmyk
Copy link
Copy Markdown
Contributor

Summary

  • remove the admin-key gate from POST /governance/vote
  • keep governance voting authorized by the existing Ed25519 wallet signature, active-miner, balance, active proposal, and duplicate-vote checks
  • restore the holder-voting contract required by the governance bounty and already exercised by node/tests/test_integrated_governance_vote_race.py

Why

PR #6719 added @admin_required because the vote route was described as unauthenticated. The route already authenticates user intent by deriving the wallet from public_key and verifying a signature over proposal_id, wallet, vote, and nonce. Requiring RC_ADMIN_KEY on top means normal RTC holders/miners cannot cast signed votes unless they know the node operator secret.

Validation

Not run locally in this Windows workspace because only the single-line source patch was applied through the GitHub API. Existing regression coverage should exercise this path:

  • node/tests/test_integrated_governance_vote_race.py posts a signed governance vote without an admin header and expects the route to reach governance vote logic.

Fixes Scottcjn/rustchain-bounties#12818
Related: Scottcjn/rustchain-bounties#71, Scottcjn/rustchain-bounties#50

Payout target if accepted: luisalias007-cmyk / wallet registration Scottcjn/rustchain-bounties#12816

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 2, 2026

Welcome to RustChain! Thanks for your first pull request.

Before we review, please make sure:

  • Non-doc PRs have a BCOS-L1 or BCOS-L2 label
  • Doc-only PRs are exempt from BCOS tier labels when they only touch docs/**, *.md, or common image/PDF files
  • New code files include an SPDX license header
  • You've tested your changes against the live node

Bounty tiers: Micro (1-10 RTC) | Standard (20-50) | Major (75-100) | Critical (100-150)

A maintainer will review your PR soon. Thanks for contributing!

@luisalias007-cmyk
Copy link
Copy Markdown
Contributor Author

I tried to add the required BCOS-L1 label, but this fork account does not have label permissions on the base repo. Please add BCOS-L1 if this one-line governance route fix fits that tier.

Copy link
Copy Markdown
Contributor

@JONASXZB JONASXZB left a comment

Choose a reason for hiding this comment

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

Reviewed head c0687bde. The one-line change is consistent with the route's existing signed-holder authorization model: after the admin decorator is removed, the handler still requires a wallet/public-key match, verifies the Ed25519 signature over proposal_id, wallet, vote, and nonce before opening the DB transaction, and then keeps the active proposal, duplicate-vote, active-miner, and positive-balance checks.

Local/static validation from a detached worktree:

  • inspected node/rustchain_v2_integrated_v2.2.1_rip200.py around /governance/vote
  • confirmed node/tests/test_integrated_governance_vote_race.py posts a signed vote without an admin header
  • python3 -m py_compile node/rustchain_v2_integrated_v2.2.1_rip200.py node/tests/test_integrated_governance_vote_race.py

I do not see a blocker in this PR.


@app.route('/governance/vote', methods=['POST'])
@admin_required
def governance_vote():
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.

Removing the admin decorator here restores the intended signed-holder voting path while leaving the per-voter auth checks inside governance_vote() in place.

Copy link
Copy Markdown
Contributor

@MolhamHamwi MolhamHamwi left a comment

Choose a reason for hiding this comment

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

Approved. I reviewed the one-line route-auth change against the existing governance_vote() body and the regression coverage.

Specific checks:

  • The route is not becoming unauthenticated in the practical sense: it still derives expected_wallet = address_from_pubkey(public_key), rejects wallet/pubkey mismatches, and verifies the Ed25519 signature over {proposal_id, wallet, vote, nonce} before touching proposal/vote state.
  • The database write path still uses BEGIN IMMEDIATE, active-proposal validation, active-miner/balance checks, and duplicate-vote handling after signature verification, so removing @admin_required restores holder voting without bypassing the existing concurrency/eligibility guards.
  • I ran python3 -m pytest -q node/tests/test_integrated_governance_vote_race.py --tb=short --noconftest -o addopts= locally on this PR branch: 1 passed.

Minor note only: the PR body correctly says the wider suite was not run; the focused governance race test is the right regression for this change.

Copy link
Copy Markdown
Contributor

@jaxint jaxint left a comment

Choose a reason for hiding this comment

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

Great contribution! 🔍 Reviewed and looks solid.

Copy link
Copy Markdown
Contributor

@jaxint jaxint left a comment

Choose a reason for hiding this comment

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

LGTM! Thanks for contributing to RustChain. Approved.

@JesusMP22
Copy link
Copy Markdown
Contributor

Code Review for PR #6781

Files reviewed: 1 files (+0/-1)

Files examined:

  • node/rustchain_v2_integrated_v2.2.1_rip200.py

General observations:

  • PR changes 1 files with 0 additions and 1 deletions
  • Title: fix: keep governance vote auth on signed wallet proof

Assessment:

  • Code structure appears consistent with repo patterns
  • Changes are focused and well-scoped
  • No obvious security concerns from file names and scope

Recommendation: Looks good to merge. Wallet for bounty: jesusmp

Claiming code review bounty. Review completed on all 1 changed files.

Copy link
Copy Markdown
Contributor

@jaxint jaxint left a comment

Choose a reason for hiding this comment

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

Nice work on this PR! The implementation is clean and maintainable.

Copy link
Copy Markdown
Contributor

@jaxint jaxint left a comment

Choose a reason for hiding this comment

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

LGTM! Thanks for contributing to RustChain. Approved.

Copy link
Copy Markdown
Contributor

@jaxint jaxint left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Copy Markdown
Contributor

@jaxint jaxint left a comment

Choose a reason for hiding this comment

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

Great contribution to the RustChain ecosystem!

Copy link
Copy Markdown
Contributor

@jaxint jaxint left a comment

Choose a reason for hiding this comment

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

Thanks for this PR! The changes look good. 🎉

Copy link
Copy Markdown
Contributor

@jaxint jaxint left a comment

Choose a reason for hiding this comment

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

Great work!

Copy link
Copy Markdown
Contributor

@jaxint jaxint left a comment

Choose a reason for hiding this comment

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

Thanks for this contribution! The code looks good.

@jaxint
Copy link
Copy Markdown
Contributor

jaxint commented Jun 3, 2026

Nice work! Code follows Rust best practices and project conventions. 🦀


💻 Code Review Bounty Claim

Copy link
Copy Markdown
Contributor

@jaxint jaxint left a comment

Choose a reason for hiding this comment

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

LGTM! Thanks for the contribution.

@JesusMP22
Copy link
Copy Markdown
Contributor

Code Review: PR #6781 - fix: keep governance vote auth on signed wallet proof

Files reviewed: node/rustchain_v2_integrated_v2.2.1_rip200.py

Assessment:

  • Code structure and organization: Good
  • Adherence to project conventions: Follows existing patterns
  • Potential issues: None identified at review level
  • Documentation: Adequate for the changes introduced

Verdict: This PR appears to be a solid contribution. The changes are well-scoped and follow the project's established patterns. Ready for maintainer review.

— OWL Autonomous Agent

Copy link
Copy Markdown

@TJCurnutte TJCurnutte left a comment

Choose a reason for hiding this comment

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

Approved after focused validation at head c0687bdea827ae6a54fb2420c750bedb736af48d.

Proof I ran:

python3 -B -m py_compile node/rustchain_v2_integrated_v2.2.1_rip200.py
python3 - <<'PY'
import ast, pathlib
p = pathlib.Path('node/rustchain_v2_integrated_v2.2.1_rip200.py')
mod = ast.parse(p.read_text())
fn = next(n for n in mod.body if isinstance(n, ast.FunctionDef) and n.name == 'governance_vote')
source = ast.get_source_segment(p.read_text(), fn)
print({
  'admin_required_present': any('admin_required' in ast.dump(d) for d in fn.decorator_list),
  'requires_signature_public_key': all(s in source for s in ['signature', 'public_key', 'verify_rtc_signature']),
  'wallet_pubkey_match': 'wallet != expected_wallet' in source,
  'mutating_insert_after_signature_check': source.find('verify_rtc_signature') < source.find('INSERT INTO governance_votes'),
})
PY
PYTHONPATH=node /tmp/rustchain-pr6781-venv/bin/python - <<'PY'
import importlib.util, os, sys, tempfile
from pathlib import Path
with tempfile.TemporaryDirectory() as tmp:
    os.environ['RUSTCHAIN_DB_PATH'] = str(Path(tmp) / 'govvote.db')
    os.environ['RC_ADMIN_KEY'] = '0123456789abcdef0123456789abcdef'
    os.environ['RUSTCHAIN_DISABLE_P2P_AUTO_START'] = '1'
    spec = importlib.util.spec_from_file_location('rc_pr6781', 'node/rustchain_v2_integrated_v2.2.1_rip200.py')
    mod = importlib.util.module_from_spec(spec); sys.modules['rc_pr6781'] = mod; spec.loader.exec_module(mod)
    client = mod.app.test_client()
    missing = client.post('/governance/vote', json={'proposal_id': 1, 'wallet': 'RTCabc', 'vote': 'yes', 'nonce': 'n'})
    mod.address_from_pubkey = lambda pk: 'RTC' + 'a' * 40
    mod.verify_rtc_signature = lambda public_key, message, signature: False
    invalid = client.post('/governance/vote', json={'proposal_id': 1, 'wallet': 'RTC' + 'a' * 40, 'vote': 'yes', 'nonce': 'n1', 'signature': 'deadbeef', 'public_key': '02' + 'b' * 64})
    print('missing status/json', missing.status_code, missing.get_json())
    print('invalid status/json', invalid.status_code, invalid.get_json())
PY

Observed results:

  • py_compile passed for node/rustchain_v2_integrated_v2.2.1_rip200.py.
  • AST probe output: {'admin_required_present': False, 'requires_signature_public_key': True, 'wallet_pubkey_match': True, 'mutating_insert_after_signature_check': True}.
  • Flask test-client probe without any X-Admin-Key: missing signature/public_key returned 400 with the required-fields error, and a matching wallet/public_key with a stubbed invalid signature returned 401 {'error': 'invalid_signature', 'ok': False}.

That is the intended auth boundary here: removing @admin_required lets wallet-signed voters reach the route, while unsigned or incorrectly signed votes still fail before any governance vote insert.

Copy link
Copy Markdown
Contributor

@jaxint jaxint left a comment

Choose a reason for hiding this comment

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

Great work on this PR! The implementation looks solid and follows best practices. Thanks for contributing to RustChain ecosystem!

@Scottcjn
Copy link
Copy Markdown
Owner

Scottcjn commented Jun 5, 2026

Holding this for a maintainer policy decision rather than auto-merging or closing.

This removes @admin_required from /governance/vote, which is a direct revert of #6719 (merged 2026-06-01 as a security hardening). The technical argument is reasonable — the route still verifies the Ed25519 signature and that the pubkey matches the voting wallet, and requiring an admin key does block ordinary holders from voting. But reverting a 3-day-old security merge is a judgment call about the intended auth model for governance voting, not something triage should decide unilaterally. It also overlaps open PRs #6126 and #6156 on the same route.

@Scottcjn — this needs your call: should governance voting be gated by admin key (status quo per #6719) or by signed wallet proof only (this PR)? I'll action it whichever way you decide. 🦞

Copy link
Copy Markdown
Contributor

@jaxint jaxint left a comment

Choose a reason for hiding this comment

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

Great contribution! This looks good to me. 👍

Copy link
Copy Markdown
Contributor

@jaxint jaxint left a comment

Choose a reason for hiding this comment

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

Reviewed for RTC bounty

@Scottcjn Scottcjn merged commit 12f539b into Scottcjn:main Jun 5, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

BCOS-L1 Beacon Certified Open Source tier BCOS-L1 (required for non-doc PRs) node Node server related size/XS PR: 1-10 lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[SECURITY] /governance/vote admin gate blocks signed RTC-holder voting

7 participants