Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
db5cc32
feat(platform)!: yes/no masternode vote poll kind with supermajority …
QuantumExplorer Sep 23, 2026
d6c4092
fix(dpp): a resource vote naming a yes/no poll is inactive before pro…
QuantumExplorer Sep 23, 2026
44545be
Merge v4.2-dev (#4930, #4931) into the yes/no vote poll branch
QuantumExplorer Sep 23, 2026
d7f783e
fix(platform): yes/no poll review fixes: end-date clean-up, proof rou…
QuantumExplorer Sep 23, 2026
b8fb0a1
feat(platform)!: a yes/no poll's minimum is a fixed voting power or a…
QuantumExplorer Sep 23, 2026
49df27f
feat(dpp): round a yes/no poll's share minimum down plus one or up mi…
QuantumExplorer Sep 23, 2026
1285ffe
Merge v5.1-dev into the yes/no vote poll branch, moving it to protoco…
QuantumExplorer Oct 20, 2026
d6f5d96
fix(platform): apply the yes/no vote poll review findings
QuantumExplorer Oct 21, 2026
38e9563
fix(platform): apply the second yes/no vote poll review round
QuantumExplorer Oct 22, 2026
217cd14
fix(platform): apply the third yes/no vote poll review round
QuantumExplorer Oct 25, 2026
6ffb1e7
fix(platform): apply the fourth yes/no vote poll review round
QuantumExplorer Oct 27, 2026
af1ae5b
Merge v5.1-dev into the yes/no vote poll branch
QuantumExplorer Oct 27, 2026
e74343b
fix(platform): version the yes/no poll rules and refuse resource vote…
QuantumExplorer Oct 27, 2026
245f526
fix(platform): refuse yes/no polls in resource votes read from JSON a…
QuantumExplorer Oct 27, 2026
258d540
fix(platform): mark every yes/no poll method not active before protoc…
QuantumExplorer Oct 27, 2026
4a7f57f
fix(platform): read the yes/no vote weights from SystemLimits and bui…
QuantumExplorer Oct 28, 2026
00eb496
test(drive-abci): move the nullifier batch guard's latest-version roo…
QuantumExplorer Oct 28, 2026
88df31e
refactor(platform): name the yes/no vote poll activation version
QuantumExplorer Oct 28, 2026
b634524
Merge v5.1-dev into the yes/no vote poll branch
QuantumExplorer Oct 28, 2026
b30a5c0
fix(drive): refuse the yes/no poll state query before protocol versio…
QuantumExplorer Oct 28, 2026
0222808
fix(sdk): answer no current DPNS contests for a zero limit
QuantumExplorer Oct 28, 2026
1dace00
refactor(platform)!: select the yes/no vote poll id through the platf…
QuantumExplorer Oct 28, 2026
fe6482b
Merge v5.1-dev into the yes/no vote poll branch
QuantumExplorer Oct 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions book/src/data-model/contested-documents.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,13 +116,17 @@ which holds its contenders, stored result and tallies, is a count tree, so a joi
contenders there are in one fetch; a contest started before 14 keeps its plain tree, and a join
counts its contenders by reading their keys. The masternodes' vote references live under
`votes / contested_resource / identity_votes`, and the end dates under `votes / end_date_queries`,
one tree per end date holding an entry for each contest ending then.
one tree per end date holding an entry for each vote poll ending then. From protocol version 15
that index also lists yes/no vote polls (the masternode polls that ask "should this happen?",
stored under `votes / decisions`), so an end date can hold contests and yes/no polls side by side.
Once the contest ends, the winning document is awarded, the losers are removed, and the stored
result stays for the `getContestedResourceVoteState` query.

A block ends at most `maximum_vote_polls_to_process` contests (a drive-abci `event_constants`
value, two at protocol version 14), the earliest end date first, so contests due together may end
over several blocks. The cleanup removes each ended contest's
end-date entry, and removes an end date only once none of its contests remain under it; the rest
end in the next blocks. Before that read, a block removes any end date among the first due that
holds no contest, since each would take a place in the read and end nothing.
A block ends at most `maximum_vote_polls_to_process` vote polls (a drive-abci `event_constants`
value, two at protocol version 14), the earliest end date first, so polls due together may end
over several blocks; from protocol version 15 yes/no polls count toward the same limit, so a
yes/no poll due earlier can push a contest to the next block. The cleanup removes each ended
poll's end-date entry, and removes an end date only once none of its polls, of either kind,
remain under it; the rest end in the next blocks. Before that read, a block removes any end date
among the first due that holds no vote poll, since each would take a place in the read and end
nothing.
2 changes: 1 addition & 1 deletion book/src/error-handling/error-codes.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ The fee category currently has a single code. The 30000 range is reserved for fu
| 40000-40009 | Data Contract | `DataContractAlreadyPresentError` (40000), `DataContractIsReadonlyError` (40001), `DataContractNotFoundError` (40008) |
| 40100-40149 | Documents | `DocumentAlreadyPresentError` (40100), `DocumentNotFoundError` (40101), `DuplicateUniqueIndexError` (40105), `DocumentActionFeeAgreementNotSetError` (40132), `DocumentActionFeeAgreementMismatchError` (40133), `DocumentActionFeeMultiplierNotToleratedError` (40134), `DocumentActionFeeModeratorsShareMismatchError` (40139), `DocumentExpiredError` (40140), `DocumentContestMaximumContendersReachedError` (40141), `ReferencedDocumentRequirementNotMetError` (40142), `ReferencedDocumentTypeNotModeratedError` (40143), `ReferencedDocumentTypeModeratedError` (40144), `ReferencedDocumentRemovedError` (40145), `ReferencedDocumentTypeIndexOnlyError` (40146), `DocumentDeleteConstraintViolatedError` (40147), `ReferencedDocumentRuleReadInvalidError` (40148), `ReferencedDocumentPrecededError` (40149) |
| 40200-40217 | Identity | `IdentityAlreadyExistsError` (40200), `InvalidIdentityRevisionError` (40203), `IdentityInsufficientBalanceError` (40210) |
| 40300-40307 | Voting | `MasternodeNotFoundError` (40300), `MasternodeVoteAlreadyPresentError` (40304), `VoteChoiceNotAllowedForVotePollError` (40307) |
| 40300-40308 | Voting | `MasternodeNotFoundError` (40300), `MasternodeVoteAlreadyPresentError` (40304), `VoteChoiceNotAllowedForVotePollError` (40307), `YesNoVotePollNotAvailableForVotingError` (40308) |
| 40400-40401 | Prefunded Balances | `PrefundedSpecializedBalanceInsufficientError` (40400) |
| 40500-40502 | Data Triggers | `DataTriggerConditionError` (40500), `DataTriggerExecutionError` (40501) |
| 40600-40603 | Addresses | `AddressDoesNotExistError` (40600), `AddressNotEnoughFundsError` (40601) |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use crate::data_contract::document_type::DocumentPropertyType;
#[cfg(feature = "validation")]
use crate::data_contract::document_type::StringPropertySizes;
use crate::fee::Credits;
use crate::voting::vote_polls::VotePoll;
use crate::voting::vote_polls::contested_document_resource_vote_poll::ContestedDocumentResourceVotePoll;
use platform_value::btreemap_extensions::{
BTreeValueMapInsertionPathHelper, BTreeValueMapPathHelper,
};
Expand Down Expand Up @@ -1046,7 +1046,7 @@ pub trait DocumentTypeV0Methods: DocumentTypeV0Getters + DocumentTypeV0MethodsVe
&self,
document: &Document,
platform_version: &PlatformVersion,
) -> Result<Option<VotePoll>, ProtocolError> {
) -> Result<Option<ContestedDocumentResourceVotePoll>, ProtocolError> {
match platform_version
.dpp
.contract_versions
Expand All @@ -1067,7 +1067,7 @@ pub trait DocumentTypeV0Methods: DocumentTypeV0Getters + DocumentTypeV0MethodsVe
&self,
document_properties: &BTreeMap<String, Value>,
platform_version: &PlatformVersion,
) -> Result<Option<VotePoll>, ProtocolError> {
) -> Result<Option<ContestedDocumentResourceVotePoll>, ProtocolError> {
Comment thread
QuantumExplorer marked this conversation as resolved.
match platform_version
.dpp
.contract_versions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ use crate::validation::SimpleConsensusValidationResult;
use crate::voting::vote_polls::contested_document_resource_vote_poll::{
required_vote_resolution_fund, ContestedDocumentResourceVotePoll,
};
use crate::voting::vote_polls::VotePoll;
use crate::ProtocolError;
use chrono::Utc;
use itertools::Itertools;
Expand Down Expand Up @@ -373,19 +372,24 @@ pub trait DocumentTypeV0MethodsVersioned: DocumentTypeV0Getters + DocumentTypeBa
}

/// Figures out the prefunded voting balance (v0) for a document in a document type
// In place, type only: this v0 (every protocol version) returned the same poll wrapped in
// `VotePoll::ContestedDocumentResourceVotePoll`, the only variant it ever built. The poll
// and every check made on it are unchanged.
fn contested_vote_poll_for_document_v0(
&self,
document: &Document,
platform_version: &PlatformVersion,
) -> Result<Option<VotePoll>, ProtocolError> {
) -> Result<Option<ContestedDocumentResourceVotePoll>, ProtocolError> {
self.contested_vote_poll_for_document_properties_v0(document.properties(), platform_version)
}

// In place, type only, as for `contested_vote_poll_for_document_v0`: the poll built below is
// the one this v0 always built, no longer wrapped in `VotePoll`.
fn contested_vote_poll_for_document_properties_v0(
&self,
document_properties: &BTreeMap<String, Value>,
platform_version: &PlatformVersion,
) -> Result<Option<VotePoll>, ProtocolError> {
) -> Result<Option<ContestedDocumentResourceVotePoll>, ProtocolError> {
self.indexes()
.values()
.find(|index| {
Expand Down Expand Up @@ -417,14 +421,12 @@ pub trait DocumentTypeV0MethodsVersioned: DocumentTypeV0Getters + DocumentTypeBa
self.flattened_properties(),
platform_version,
)?;
Ok(VotePoll::ContestedDocumentResourceVotePoll(
ContestedDocumentResourceVotePoll {
contract_id: self.data_contract_id(),
document_type_name: self.name().clone(),
index_name: index.name.clone(),
index_values,
},
))
Ok(ContestedDocumentResourceVotePoll {
contract_id: self.data_contract_id(),
document_type_name: self.name().clone(),
index_name: index.name.clone(),
index_values,
})
})
.transpose()
}
Expand Down
1 change: 1 addition & 0 deletions packages/rs-dpp/src/errors/consensus/codes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,7 @@ impl ErrorWithCode for StateError {
Self::MasternodeIncorrectVotingAddressError(_) => 40305,
Self::MasternodeIncorrectVoterIdentityIdError(_) => 40306,
Self::VoteChoiceNotAllowedForVotePollError(_) => 40307,
Self::YesNoVotePollNotAvailableForVotingError(_) => 40308,

// Prefunded specialized balances Errors: 40400-40499
Self::PrefundedSpecializedBalanceInsufficientError(_) => 40400,
Expand Down
27 changes: 26 additions & 1 deletion packages/rs-dpp/src/errors/consensus/state/state_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ use crate::consensus::state::voting::masternode_voted_too_many_times::Masternode
use crate::consensus::state::voting::vote_poll_not_available_for_voting_error::VotePollNotAvailableForVotingError;
use crate::consensus::state::voting::vote_choice_not_allowed_for_vote_poll_error::VoteChoiceNotAllowedForVotePollError;
use crate::consensus::state::voting::vote_poll_not_found_error::VotePollNotFoundError;
use crate::consensus::state::voting::yes_no_vote_poll_not_available_for_voting_error::YesNoVotePollNotAvailableForVotingError;

use super::document::document_timestamps_are_equal_error::DocumentTimestampsAreEqualError;

Expand Down Expand Up @@ -745,6 +746,10 @@ pub enum StateError {
// `chooseFirst` groups by (protocol version 14).
#[error(transparent)]
ReferencedDocumentPrecededError(ReferencedDocumentPrecededError),

// Yes/no vote polls (protocol version 15).
#[error(transparent)]
YesNoVotePollNotAvailableForVotingError(YesNoVotePollNotAvailableForVotingError),
}

impl From<StateError> for ConsensusError {
Expand All @@ -756,6 +761,7 @@ impl From<StateError> for ConsensusError {
#[cfg(test)]
mod tests {
use super::*;
use crate::block::block_info::BlockInfo;
use crate::consensus::basic::document::PropertyConstraintViolation;
use crate::consensus::state::contract_moderation::ContractModerationCounterpartyRole;
use crate::consensus::state::identity::identity_public_key_limit_not_set_error::KeyLimit;
Expand All @@ -772,9 +778,11 @@ mod tests {
};
use crate::tokens::gas_fees_paid_by::GasFeesPaidBy;
use crate::voting::vote_choices::resource_vote_choice::ResourceVoteChoice;
use crate::voting::vote_info_storage::yes_no_vote_poll_stored_info::YesNoVotePollStatus;
use crate::voting::vote_polls::contested_document_resource_vote_poll::ContestedDocumentResourceVotePoll;
use crate::voting::vote_polls::yes_no_vote_poll::{YesNoMinimumVotingPower, YesNoVotePoll};
use crate::voting::vote_polls::VotePoll;
use platform_value::Identifier;
use platform_value::{BinaryData, Identifier};
use std::collections::BTreeMap;

/// `StateError` is encoded by variant position, so inserting a variant
Expand Down Expand Up @@ -1674,5 +1682,22 @@ mod tests {
)),
175
);
// Yes/no vote polls (protocol version 15): the tail of the enum.
assert_eq!(
discriminant_of(StateError::YesNoVotePollNotAvailableForVotingError(
YesNoVotePollNotAvailableForVotingError::new(
YesNoVotePoll {
resource_path: vec![BinaryData::new(vec![1; 32])],
supermajority_numerator: 2,
supermajority_denominator: 3,
minimum_voting_power: YesNoMinimumVotingPower::Absolute {
voting_power: 400
},
},
YesNoVotePollStatus::Started(BlockInfo::default())
)
)),
176
);
}
}
1 change: 1 addition & 0 deletions packages/rs-dpp/src/errors/consensus/state/voting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ pub mod masternode_voted_too_many_times;
pub mod vote_choice_not_allowed_for_vote_poll_error;
pub mod vote_poll_not_available_for_voting_error;
pub mod vote_poll_not_found_error;
pub mod yes_no_vote_poll_not_available_for_voting_error;
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
use crate::consensus::state::state_error::StateError;
use crate::consensus::ConsensusError;
use crate::errors::ProtocolError;
use crate::voting::vote_info_storage::yes_no_vote_poll_stored_info::YesNoVotePollStatus;
use crate::voting::vote_polls::yes_no_vote_poll::YesNoVotePoll;
use bincode::{Decode, DecodeUntrusted, Encode};
use platform_serialization_derive::{
PlatformDeserializeTrusted, PlatformDeserializeUntrusted, PlatformSerialize,
};
use thiserror::Error;

/// A vote on a yes/no poll that exists but is no longer taking votes (protocol version 15).
#[derive(
Error,
Debug,
Clone,
PartialEq,
Encode,
Decode,
PlatformSerialize,
PlatformDeserializeTrusted,
PlatformDeserializeUntrusted,
DecodeUntrusted,
)]
#[error("yes/no vote poll {vote_poll} is not available for voting: {status}")]
#[platform_serialize(unversioned)]
pub struct YesNoVotePollNotAvailableForVotingError {
/*
DO NOT CHANGE ORDER OF FIELDS WITHOUT INTRODUCING OF NEW VERSION
*/
vote_poll: YesNoVotePoll,
status: YesNoVotePollStatus,
}

impl YesNoVotePollNotAvailableForVotingError {
pub fn new(vote_poll: YesNoVotePoll, status: YesNoVotePollStatus) -> Self {
Self { vote_poll, status }
}

pub fn vote_poll(&self) -> &YesNoVotePoll {
&self.vote_poll
}

pub fn status(&self) -> &YesNoVotePollStatus {
&self.status
}
}

impl From<YesNoVotePollNotAvailableForVotingError> for ConsensusError {
fn from(err: YesNoVotePollNotAvailableForVotingError) -> Self {
Self::StateError(StateError::YesNoVotePollNotAvailableForVotingError(err))
}
}
5 changes: 5 additions & 0 deletions packages/rs-dpp/src/serialization/json/safe_fields.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,11 @@ impl JsonSafeFields for TokenTransition {}
#[cfg(feature = "state-transitions")]
impl JsonSafeFields for BatchedTransition {}
impl JsonSafeFields for crate::voting::vote_choices::resource_vote_choice::ResourceVoteChoice {}
impl JsonSafeFields
for crate::voting::vote_choices::yes_no_abstain_vote_choice::YesNoAbstainVoteChoice
{
}
impl JsonSafeFields for crate::voting::vote_polls::yes_no_vote_poll::YesNoMinimumVotingPower {}
impl JsonSafeFields for crate::group::action_event::GroupActionEvent {}
// TokenEvent contains u64 aliases (TokenAmount, Credits) in tuple variants that
// `#[json_safe_fields]` can't auto-annotate. Developer takes responsibility for
Expand Down
Loading
Loading