Expected Behavior
Vote::fetch(sdk, VoteQuery::new(voter_id, vote_poll_id)) should return the proved vote for that voter and poll when it exists, or None when it does not.
The proof decoder should validate the returned map key against the requested vote-poll ID.
Current Behavior
FromProof<GetContestedResourceIdentityVotesRequest> for Vote extracts identity_id from the request and compares it with the key returned by ResourceVotesByIdentity:
let id_in_request = Identifier::from_bytes(&v0.identity_id)?;
// ...
let (id, vote) = maybe_votes.into_iter().next()?;
if id != id_in_request {
return Err(Error::ResponseDecodeError { ... });
}
However, the proved ResourceVotesByIdentity map is keyed by vote-poll ID, not voter identity ID. Drive's proof verification inserts the poll's vote_id as the map key.
Consequently, an existing vote normally fails with a response-decode error equivalent to:
expected vote for identity <voter-id>, got vote for identity <vote-poll-id>
The identifiers are different by design.
Impact
This breaks the public single-vote fetch API whenever a vote exists.
It also affects PutVote::put_to_platform_and_wait_for_response. Its duplicate-transition recovery calls:
Vote::fetch(sdk, VoteQuery::new(voter_pro_tx_hash, vote_poll_id)).await?
Therefore the recovery path can fail while trying to verify that the intended vote already exists.
Clients can work around the bug with ResourceVote::fetch_many and an exact lookup by vote-poll ID, but the public Vote::fetch contract should work directly.
Source Locations
- Faulty comparison:
packages/rs-drive-proof-verifier/src/proof.rs, FromProof<GetContestedResourceIdentityVotesRequest> for Vote
- Query containing both voter and poll IDs:
packages/rs-sdk/src/platform/query.rs, VoteQuery
- Affected recovery caller:
packages/rs-sdk/src/platform/transition/vote.rs
- Proof map construction:
packages/rs-drive/src/verify/voting/verify_identity_votes_given_proof/v0/mod.rs
Suggested Fix
Extract the requested vote-poll ID from the request's start_at value and compare the returned map key with that ID. Keep identity_id only as the voter scope for the query.
Alternatively, preserve the expected poll ID explicitly in the query/request conversion so the proof decoder does not need to infer it from pagination fields.
Suggested Tests
Vote::fetch returns an existing vote when the proof map is keyed by the requested poll ID.
- The returned poll ID must match the requested poll ID.
- A different returned poll ID is rejected.
- No vote returns
None.
PutVote duplicate recovery succeeds when the proved current vote matches the submitted choice.
- The tests use distinct voter and poll identifiers so comparing the wrong fields cannot pass accidentally.
Context
Discovered while implementing authoritative DPNS vote-state reconciliation in Dash Evo Tool. DET will use the proved ResourceVote::fetch_many API as a temporary workaround.
This is separate from #4137, which concerns classification and retry behavior after a successful broadcast followed by a result-wait failure.
🤖 Co-authored by Claudius the Magnificent AI Agent
Expected Behavior
Vote::fetch(sdk, VoteQuery::new(voter_id, vote_poll_id))should return the proved vote for that voter and poll when it exists, orNonewhen it does not.The proof decoder should validate the returned map key against the requested vote-poll ID.
Current Behavior
FromProof<GetContestedResourceIdentityVotesRequest> for Voteextractsidentity_idfrom the request and compares it with the key returned byResourceVotesByIdentity:However, the proved
ResourceVotesByIdentitymap is keyed by vote-poll ID, not voter identity ID. Drive's proof verification inserts the poll'svote_idas the map key.Consequently, an existing vote normally fails with a response-decode error equivalent to:
The identifiers are different by design.
Impact
This breaks the public single-vote fetch API whenever a vote exists.
It also affects
PutVote::put_to_platform_and_wait_for_response. Its duplicate-transition recovery calls:Therefore the recovery path can fail while trying to verify that the intended vote already exists.
Clients can work around the bug with
ResourceVote::fetch_manyand an exact lookup by vote-poll ID, but the publicVote::fetchcontract should work directly.Source Locations
packages/rs-drive-proof-verifier/src/proof.rs,FromProof<GetContestedResourceIdentityVotesRequest> for Votepackages/rs-sdk/src/platform/query.rs,VoteQuerypackages/rs-sdk/src/platform/transition/vote.rspackages/rs-drive/src/verify/voting/verify_identity_votes_given_proof/v0/mod.rsSuggested Fix
Extract the requested vote-poll ID from the request's
start_atvalue and compare the returned map key with that ID. Keepidentity_idonly as the voter scope for the query.Alternatively, preserve the expected poll ID explicitly in the query/request conversion so the proof decoder does not need to infer it from pagination fields.
Suggested Tests
Vote::fetchreturns an existing vote when the proof map is keyed by the requested poll ID.None.PutVoteduplicate recovery succeeds when the proved current vote matches the submitted choice.Context
Discovered while implementing authoritative DPNS vote-state reconciliation in Dash Evo Tool. DET will use the proved
ResourceVote::fetch_manyAPI as a temporary workaround.This is separate from #4137, which concerns classification and retry behavior after a successful broadcast followed by a result-wait failure.
🤖 Co-authored by Claudius the Magnificent AI Agent