Skip to content

Fix key witness count underestimation for votes in fee estimation - #1271

Open
carbolymer wants to merge 1 commit into
masterfrom
mgalazyn/fix/vote-key-witness-count
Open

Fix key witness count underestimation for votes in fee estimation#1271
carbolymer wants to merge 1 commit into
masterfrom
mgalazyn/fix/vote-key-witness-count

Conversation

@carbolymer

@carbolymer carbolymer commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Context

Closes #722.

Both variants of estimateTransactionKeyWitnessCount (experimental Cardano.Api.Experimental.Tx.Internal.Fee and legacy Cardano.Api.Tx.Internal.Fee) ignored txVotingProcedures entirely, so a transaction carrying votes from key-credentialed voters (key-hash DReps, CC hot keys, SPOs) had its key witness count underestimated.
This understates the fee and can lead to FeeTooSmallUTxO.

The experimental estimator now counts one key witness per AnyKeyWitnessPlaceholder entry in the vote witness map, and estimateTransactionKeyWitnessCount is newly exported from Cardano.Api.Experimental (it was previously unreachable outside the package).
The legacy TxVotingProcedures map is ScriptWitness-only, so its count is instead derived from the ledger L.VotingProcedures voter credentials: a CommitteeVoter/DRepVoter needs a key witness only when its credential is a KeyHashObj, and a StakePoolVoter always does.
Proposals are deliberately not counted, since the ledger requires no key witness for them.

Note that the type asymmetry the issue asks about (ScriptWitness vs Witness) is already resolved in the experimental API via AnyWitness; this PR closes the remaining fee-estimation gap in both APIs.

How to trust this PR

The legacy fix mirrors ledger's own voterWitnesses (Cardano.Ledger.Conway.UTxO), so the credential-matching logic is not new invention, it is the same rule the ledger itself applies when building the witness set.

Two hedgehog property tests cover the fix, each generating a mix of key-credentialed and script-credentialed voters and asserting the exact expected key witness count:

  • Test.Cardano.Api.Experimental.Fee: estimateTransactionKeyWitnessCount counts key witnesses required by key-credentialed voters.
  • Test.Cardano.Api.TxBody: vote key witness count, which isolates the vote contribution by comparing the estimate for a randomly generated tx body with and without the generated votes attached.

Both tests fail against the pre-fix code with a bare 0 === 1 (or similar), since the old record patterns simply never destructured txVotingProcedures.

Checklist

  • Commit sequence broadly makes sense and commits have useful messages
  • New tests are added if needed and existing tests are updated. See Running tests for more details
  • Self-reviewed the diff
  • Changelog fragment added in .changes/

@carbolymer carbolymer changed the title Fix witness counting for voting transaction Fix key witness count underestimation for votes in fee estimation Jul 28, 2026
@carbolymer
carbolymer force-pushed the mgalazyn/fix/vote-key-witness-count branch from 8fd6482 to eae5142 Compare July 29, 2026 12:00
@carbolymer
carbolymer force-pushed the mgalazyn/fix/vote-key-witness-count branch from eae5142 to bf26988 Compare July 29, 2026 12:07
@carbolymer carbolymer self-assigned this Jul 29, 2026
@carbolymer
carbolymer marked this pull request as ready for review July 29, 2026 12:07
@carbolymer
carbolymer requested a review from Jimbo4350 as a code owner July 29, 2026 12:07
Copilot AI review requested due to automatic review settings July 29, 2026 12:07

Copilot AI 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.

Pull request overview

Fixes fee underestimation for vote-carrying transactions by ensuring estimateTransactionKeyWitnessCount accounts for key-credentialed governance voters in both the legacy and experimental transaction-building APIs. This closes the remaining fee-estimation gap around txVotingProcedures, preventing underestimated fees that can lead to FeeTooSmallUTxO.

Changes:

  • Legacy fee estimator now counts key witnesses implied by txVotingProcedures based on ledger voter credential types (key-hash DReps, committee hot keys, and SPOs).
  • Experimental fee estimator now counts AnyKeyWitnessPlaceholder entries in the vote witness map, and estimateTransactionKeyWitnessCount is exported from Cardano.Api.Experimental.
  • Adds Hedgehog regression/property tests covering vote key-witness counting in both APIs, plus a Herald changelog fragment.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.

Show a summary per file
File Description
cardano-api/src/Cardano/Api/Tx/Internal/Fee.hs Includes txVotingProcedures in legacy key-witness estimation using ledger voter credential rules.
cardano-api/src/Cardano/Api/Experimental/Tx/Internal/Fee.hs Counts vote-related key witnesses via AnyKeyWitnessPlaceholder entries and exports the estimator from the internal module.
cardano-api/src/Cardano/Api/Experimental.hs Re-exports estimateTransactionKeyWitnessCount from the experimental top-level module.
cardano-api/test/cardano-api-test/Test/Cardano/Api/TxBody.hs Adds regression property test isolating vote contribution to legacy key-witness estimation.
cardano-api/test/cardano-api-test/Test/Cardano/Api/Experimental/Fee.hs Adds regression property test for experimental vote key-witness estimation and shared vote generators.
.changes/20260728_cardano_api_vote_key_witness_count.yml Documents the bugfix + compatible API change (new export) for Herald/CI.

@palas palas 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.

Nice catch! This is a net improvement, but I think there are two things that could be improved, one in the implementation, and one in the tests

Comment on lines +1428 to +1430
votingProcedures =
L.VotingProcedures $
Map.fromList [(voter, Map.singleton govActionId votingProcedure) | voter <- allVoters]

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.

We should try with several govActionId and also having the same voter vote for several action ids. The easiest way would be generating a relatively small pool of potential voters and reusing them to create collisions.

OMap.size m
Nothing -> 0
+ case txVotingProcedures of
Just (TxVotingProcedures _ voteWits) ->

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.

I think here it would be more reliable to look at the voting procedures (first field) instead of voteWits, like in the version of this for the traditional API. And that is what the ledger does here

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.

[FR] - TxVotingProcedures accepts ScriptWitness but not broader Witness type

3 participants