Skip to main content

dpp/voting/votes/resource_vote/v0/
mod.rs

1#[cfg(feature = "json-conversion")]
2use crate::serialization::json_safe_fields;
3use crate::voting::vote_choices::resource_vote_choice::ResourceVoteChoice;
4use crate::voting::vote_polls::VotePoll;
5use crate::ProtocolError;
6use bincode::{Decode, DecodeUntrusted, Encode};
7use platform_serialization_derive::{
8    PlatformDeserializeTrusted, PlatformDeserializeUntrusted, PlatformSerialize,
9};
10use platform_value::Identifier;
11use platform_version::version::PlatformVersion;
12#[cfg(feature = "serde-conversion")]
13use serde::{Deserialize, Serialize};
14
15#[cfg_attr(feature = "json-conversion", json_safe_fields)]
16#[derive(
17    Debug,
18    Clone,
19    Encode,
20    Decode,
21    PlatformDeserializeTrusted,
22    PlatformDeserializeUntrusted,
23    PlatformSerialize,
24    PartialEq,
25    DecodeUntrusted,
26)]
27#[cfg_attr(
28    feature = "serde-conversion",
29    derive(Serialize, Deserialize),
30    serde(rename_all = "camelCase"),
31    serde(try_from = "ResourceVoteV0Inner")
32)]
33#[platform_serialize(unversioned)]
34pub struct ResourceVoteV0 {
35    pub vote_poll: VotePoll,
36    pub resource_vote_choice: ResourceVoteChoice,
37}
38
39/// The fields of a resource vote read from a plain object or JSON, built into one through
40/// [`ResourceVoteV0::try_new`] so those conversions refuse a yes/no poll as `try_new` does.
41#[cfg(feature = "serde-conversion")]
42#[derive(Deserialize)]
43#[serde(rename_all = "camelCase")]
44struct ResourceVoteV0Inner {
45    vote_poll: VotePoll,
46    resource_vote_choice: ResourceVoteChoice,
47}
48
49#[cfg(feature = "serde-conversion")]
50impl TryFrom<ResourceVoteV0Inner> for ResourceVoteV0 {
51    type Error = ProtocolError;
52
53    fn try_from(value: ResourceVoteV0Inner) -> Result<Self, Self::Error> {
54        ResourceVoteV0::try_new(value.vote_poll, value.resource_vote_choice)
55    }
56}
57
58impl Default for ResourceVoteV0 {
59    fn default() -> Self {
60        ResourceVoteV0 {
61            vote_poll: VotePoll::default(),
62            resource_vote_choice: ResourceVoteChoice::Abstain,
63        }
64    }
65}
66
67impl ResourceVoteV0 {
68    /// A resource vote on `vote_poll`, refused when the poll is a yes/no poll, which a yes/no
69    /// vote answers. The fields stay public for decoding; consensus refuses a resource vote
70    /// naming a yes/no poll however it was built.
71    pub fn try_new(
72        vote_poll: VotePoll,
73        resource_vote_choice: ResourceVoteChoice,
74    ) -> Result<Self, ProtocolError> {
75        match vote_poll {
76            VotePoll::ContestedDocumentResourceVotePoll(_) => Ok(ResourceVoteV0 {
77                vote_poll,
78                resource_vote_choice,
79            }),
80            VotePoll::YesNoVotePoll(_) => Err(ProtocolError::VoteError(
81                "a resource vote answers a contested document resource vote poll; a yes/no vote \
82                 poll is answered by a yes/no vote"
83                    .to_string(),
84            )),
85        }
86    }
87
88    /// The id of the poll this vote answers.
89    pub fn vote_poll_unique_id(
90        &self,
91        platform_version: &PlatformVersion,
92    ) -> Result<Identifier, ProtocolError> {
93        self.vote_poll.unique_id(platform_version)
94    }
95}
96
97#[cfg(test)]
98mod tests {
99    use super::*;
100    use crate::voting::vote_polls::contested_document_resource_vote_poll::ContestedDocumentResourceVotePoll;
101    use crate::voting::vote_polls::yes_no_vote_poll::{
102        YesNoMinimumVotingPower, YesNoVotePoll, YesNoVotePollPurpose,
103    };
104    use platform_value::Identifier;
105
106    #[test]
107    fn should_build_a_resource_vote_only_on_a_contested_poll() {
108        let contested_poll = VotePoll::ContestedDocumentResourceVotePoll(
109            ContestedDocumentResourceVotePoll::default(),
110        );
111        let vote = ResourceVoteV0::try_new(contested_poll.clone(), ResourceVoteChoice::Abstain)
112            .expect("expected a resource vote on a contested poll");
113        assert_eq!(vote.vote_poll, contested_poll);
114
115        let yes_no_poll = VotePoll::YesNoVotePoll(YesNoVotePoll {
116            purpose: YesNoVotePollPurpose::DecisionProposal {
117                document_id: Identifier::new([0xc1; 32]),
118            },
119            supermajority_numerator: 2,
120            supermajority_denominator: 3,
121            minimum_voting_power: YesNoMinimumVotingPower::Absolute { voting_power: 0 },
122        });
123        ResourceVoteV0::try_new(yes_no_poll, ResourceVoteChoice::Abstain)
124            .expect_err("expected a resource vote on a yes/no poll to be refused");
125    }
126}