Skip to content

Signing: key-hash governance voters are never added to required signers #479

Description

@solidsnakedev

Summary

computeRequiredKeyHashesSync derives the signing keys from inputs, withdrawals, and certificates, but never inspects votingProcedures (body key 19) or proposalProcedures (key 20). The tx builder's Vote operation likewise only tracks redeemers for script voters and never adds a key-hash voter to requiredSigners. So a governance vote cast by a key-hash DRep, constitutional-committee member, or stake pool is not signed by the wallet, and the node rejects the under-signed transaction. Fail closed: no fund loss and no unauthorized action (a vkey witness covers the whole body, so nothing is signed that the signer didn't already authorize). The SDK can build a vote it cannot sign.

Affected

packages/evolution/src/sdk/client/internal/Signing.ts

  • computeRequiredKeyHashesSync (L47-184): checks requiredSigners, nativeScripts, inputs, collateralInputs, withdrawals, certificates; no votingProcedures / proposalProcedures branch

packages/evolution/src/sdk/builders/operations/Vote.ts

  • createVoteProgram: only tracks redeemers for script voters (isScriptVoter); key-hash voters are never added to state.requiredSigners

Fix

In computeRequiredKeyHashesSync, when tx.body.votingProcedures is present, iterate the voters and add the wallet credential on a match, mirroring the existing certificate logic:

  • DRepVoter with KeyHash drep -> match against stakeKhHex (same as RegDrepCert handling)
  • ConstitutionalCommitteeVoter with KeyHash credential -> match against the wallet's committee credential (same as AuthCommitteeHotCert handling)
  • StakePoolVoter -> match poolKeyHash against paymentKhHex (same as PoolRegistration/PoolRetirement handling)

Apply the equivalent for proposalProcedures.

Regression test

  • given: a transaction whose only wallet-relevant field is a votingProcedures entry for a key-hash DRep voter equal to the wallet's stake credential
  • before fix: signTx returns an empty witness set (voter key not added)
  • after fix: signTx adds the stake-key vkey witness
    Must FAIL on main today and PASS after the fix.

Reference

Reported informally (governance signing gap). The same report also claimed this enables "unauthorized voting"; that part does not hold, since a vkey witness signs the entire transaction body, so a vote riding along in a signed transaction is the ordinary review-before-signing consideration, not a separate escalation.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingexternal-reviewFrom external review batch

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions