Skip to main content

dpp/voting/contender_structs/
mod.rs

1mod contender;
2
3use crate::data_contract::document_type::DocumentTypeRef;
4use crate::document::serialization_traits::DocumentPlatformConversionMethodsV0;
5use crate::document::Document;
6use crate::voting::vote_choices::resource_vote_choice::ResourceVoteChoice;
7use crate::ProtocolError;
8use bincode::{Decode, Encode};
9use platform_value::Identifier;
10use platform_version::version::PlatformVersion;
11use std::fmt;
12
13pub use contender::v0::{ContenderV0, ContenderWithSerializedDocumentV0};
14pub use contender::{Contender, ContenderWithSerializedDocument};
15
16/// Represents a finalized contender in the contested document vote poll.
17/// This is for internal use where the document is in serialized form
18///
19/// This struct holds the identity ID of the contender, the serialized document,
20/// and the vote tally.
21#[derive(Debug, PartialEq, Eq, Clone, Default)]
22pub struct FinalizedContenderWithSerializedDocument {
23    /// The identity ID of the contender.
24    pub identity_id: Identifier,
25    /// The serialized document associated with the contender.
26    pub serialized_document: Vec<u8>,
27    /// The vote tally for the contender.
28    pub final_vote_tally: u32,
29}
30
31/// Represents a finalized contender in the contested document vote poll.
32/// This is for keeping information about previous vote polls
33///
34/// This struct holds the identity ID of the contender, the serialized document,
35/// and the vote tally.
36#[derive(Debug, PartialEq, Eq, Clone, Default, Encode, Decode)]
37pub struct FinalizedResourceVoteChoicesWithVoterInfo {
38    /// The resource vote choice.
39    pub resource_vote_choice: ResourceVoteChoice,
40    /// The pro_tx_hashes of the voters for this contender along with their strength
41    pub voters: Vec<(Identifier, u8)>,
42}
43impl fmt::Display for FinalizedResourceVoteChoicesWithVoterInfo {
44    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45        let voters_str: Vec<String> = self
46            .voters
47            .iter()
48            .map(|(id, strength)| format!("{}:{}", id, strength))
49            .collect();
50        write!(
51            f,
52            "FinalizedResourceVoteChoicesWithVoterInfo {{ resource_vote_choice: {}, voters: [{}] }}",
53            self.resource_vote_choice,
54            voters_str.join(", ")
55        )
56    }
57}
58
59/// Represents a finalized contender in the contested document vote poll.
60/// This is for internal use where the document is in serialized form
61///
62/// This struct holds the identity ID of the contender, the document,
63/// and the vote tally.
64#[derive(Debug, PartialEq, Clone)]
65pub struct FinalizedContender {
66    /// The identity ID of the contender.
67    pub identity_id: Identifier,
68    /// The document associated with the contender.
69    pub document: Document,
70    /// The still serialized document
71    pub serialized_document: Vec<u8>,
72    /// The vote tally for the contender.
73    pub final_vote_tally: u32,
74}
75
76impl FinalizedContender {
77    /// Try to get the finalized contender from a finalized contender with a serialized document
78    pub fn try_from_contender_with_serialized_document(
79        value: FinalizedContenderWithSerializedDocument,
80        document_type: DocumentTypeRef,
81        platform_version: &PlatformVersion,
82    ) -> Result<Self, ProtocolError> {
83        let FinalizedContenderWithSerializedDocument {
84            identity_id,
85            serialized_document,
86            final_vote_tally,
87        } = value;
88
89        Ok(FinalizedContender {
90            identity_id,
91            document: Document::from_bytes(&serialized_document, document_type, platform_version)?,
92            serialized_document,
93            final_vote_tally,
94        })
95    }
96}
97
98impl TryFrom<ContenderWithSerializedDocument> for FinalizedContenderWithSerializedDocument {
99    type Error = ProtocolError;
100
101    fn try_from(value: ContenderWithSerializedDocument) -> Result<Self, Self::Error> {
102        let (identity_id, serialized_document, vote_tally) = match value {
103            ContenderWithSerializedDocument::V0(v0) => {
104                let ContenderWithSerializedDocumentV0 {
105                    identity_id,
106                    serialized_document,
107                    vote_tally,
108                } = v0;
109                (identity_id, serialized_document, vote_tally)
110            }
111        };
112
113        Ok(FinalizedContenderWithSerializedDocument {
114            identity_id,
115            serialized_document: serialized_document.ok_or(
116                ProtocolError::CorruptedCodeExecution("expected serialized document".to_string()),
117            )?,
118            final_vote_tally: vote_tally.ok_or(ProtocolError::CorruptedCodeExecution(
119                "expected vote tally".to_string(),
120            ))?,
121        })
122    }
123}
124
125#[cfg(test)]
126mod tests {
127    use super::*;
128    use crate::voting::contender_structs::contender::v0::ContenderWithSerializedDocumentV0;
129    use platform_value::Identifier;
130
131    mod finalized_resource_vote_choices_display {
132        use super::*;
133
134        #[test]
135        fn display_with_no_voters() {
136            let item = FinalizedResourceVoteChoicesWithVoterInfo {
137                resource_vote_choice: ResourceVoteChoice::Abstain,
138                voters: vec![],
139            };
140            let s = item.to_string();
141            assert!(s.contains("resource_vote_choice"));
142            assert!(s.contains("voters: []"));
143        }
144
145        #[test]
146        fn display_with_multiple_voters() {
147            let id1 = Identifier::new([1u8; 32]);
148            let id2 = Identifier::new([2u8; 32]);
149            let item = FinalizedResourceVoteChoicesWithVoterInfo {
150                resource_vote_choice: ResourceVoteChoice::Lock,
151                voters: vec![(id1, 1), (id2, 4)],
152            };
153            let s = item.to_string();
154            // Each voter rendered as id:strength and comma separated
155            assert!(s.contains(&format!("{}:1", id1)));
156            assert!(s.contains(&format!("{}:4", id2)));
157            assert!(s.contains(", "));
158        }
159    }
160
161    mod finalized_resource_vote_choices_encoding {
162        use super::*;
163        use bincode::config;
164
165        #[test]
166        fn roundtrip_preserves_data() {
167            let id = Identifier::new([7u8; 32]);
168            let original = FinalizedResourceVoteChoicesWithVoterInfo {
169                resource_vote_choice: ResourceVoteChoice::TowardsIdentity(id),
170                voters: vec![(id, 3)],
171            };
172            let cfg = config::standard();
173            let bytes = bincode::encode_to_vec(&original, cfg).expect("encode");
174            let (decoded, _): (FinalizedResourceVoteChoicesWithVoterInfo, _) =
175                bincode::decode_from_slice(&bytes, cfg).expect("decode");
176            assert_eq!(decoded, original);
177        }
178    }
179
180    mod finalized_contender_with_serialized_document_default {
181        use super::*;
182
183        #[test]
184        fn default_has_zero_fields() {
185            let fc = FinalizedContenderWithSerializedDocument::default();
186            assert_eq!(fc.identity_id, Identifier::default());
187            assert!(fc.serialized_document.is_empty());
188            assert_eq!(fc.final_vote_tally, 0);
189        }
190    }
191
192    mod try_from_contender_with_serialized_document {
193        use super::*;
194
195        #[test]
196        fn err_when_serialized_document_missing() {
197            let csd = ContenderWithSerializedDocument::V0(ContenderWithSerializedDocumentV0 {
198                identity_id: Identifier::new([1u8; 32]),
199                serialized_document: None,
200                vote_tally: Some(10),
201            });
202            let result = FinalizedContenderWithSerializedDocument::try_from(csd);
203            match result {
204                Err(ProtocolError::CorruptedCodeExecution(msg)) => {
205                    assert!(msg.contains("expected serialized document"));
206                }
207                _ => panic!("expected CorruptedCodeExecution error for missing document"),
208            }
209        }
210
211        #[test]
212        fn err_when_vote_tally_missing() {
213            let csd = ContenderWithSerializedDocument::V0(ContenderWithSerializedDocumentV0 {
214                identity_id: Identifier::new([1u8; 32]),
215                serialized_document: Some(vec![1, 2, 3]),
216                vote_tally: None,
217            });
218            let result = FinalizedContenderWithSerializedDocument::try_from(csd);
219            match result {
220                Err(ProtocolError::CorruptedCodeExecution(msg)) => {
221                    assert!(msg.contains("expected vote tally"));
222                }
223                _ => panic!("expected CorruptedCodeExecution error for missing vote tally"),
224            }
225        }
226
227        #[test]
228        fn ok_when_all_fields_present() {
229            let id = Identifier::new([9u8; 32]);
230            let doc = vec![0xAA, 0xBB, 0xCC];
231            let csd = ContenderWithSerializedDocument::V0(ContenderWithSerializedDocumentV0 {
232                identity_id: id,
233                serialized_document: Some(doc.clone()),
234                vote_tally: Some(42),
235            });
236            let finalized =
237                FinalizedContenderWithSerializedDocument::try_from(csd).expect("should succeed");
238            assert_eq!(finalized.identity_id, id);
239            assert_eq!(finalized.serialized_document, doc);
240            assert_eq!(finalized.final_vote_tally, 42);
241        }
242    }
243
244    mod finalized_contender_with_serialized_document_equality {
245        use super::*;
246
247        #[test]
248        fn equal_structs_compare_equal() {
249            let id = Identifier::new([3u8; 32]);
250            let a = FinalizedContenderWithSerializedDocument {
251                identity_id: id,
252                serialized_document: vec![1, 2, 3],
253                final_vote_tally: 100,
254            };
255            let b = FinalizedContenderWithSerializedDocument {
256                identity_id: id,
257                serialized_document: vec![1, 2, 3],
258                final_vote_tally: 100,
259            };
260            assert_eq!(a, b);
261            // Clone preserves fields
262            let c = a.clone();
263            assert_eq!(a, c);
264        }
265
266        #[test]
267        fn different_tally_not_equal() {
268            let id = Identifier::new([3u8; 32]);
269            let a = FinalizedContenderWithSerializedDocument {
270                identity_id: id,
271                serialized_document: vec![1, 2, 3],
272                final_vote_tally: 100,
273            };
274            let b = FinalizedContenderWithSerializedDocument {
275                identity_id: id,
276                serialized_document: vec![1, 2, 3],
277                final_vote_tally: 101,
278            };
279            assert_ne!(a, b);
280        }
281    }
282}