drive/query/
vote_poll_vote_state_query.rs

1use crate::drive::votes::paths::{
2    VotePollPaths, RESOURCE_ABSTAIN_VOTE_TREE_KEY_U8_32, RESOURCE_LOCK_VOTE_TREE_KEY_U8_32,
3    RESOURCE_STORED_INFO_KEY_U8_32,
4};
5use crate::drive::votes::resolved::vote_polls::contested_document_resource_vote_poll::resolve::ContestedDocumentResourceVotePollResolver;
6use crate::drive::votes::resolved::vote_polls::contested_document_resource_vote_poll::ContestedDocumentResourceVotePollWithContractInfoAllowBorrowed;
7#[cfg(feature = "server")]
8use crate::drive::Drive;
9use crate::error::drive::DriveError;
10use crate::error::query::QuerySyntaxError;
11use crate::error::Error;
12#[cfg(feature = "server")]
13use crate::fees::op::LowLevelDriveOperation;
14#[cfg(feature = "server")]
15use crate::query::GroveError;
16use bincode::{Decode, Encode};
17use dpp::block::block_info::BlockInfo;
18use dpp::data_contract::DataContract;
19use dpp::identifier::Identifier;
20#[cfg(feature = "server")]
21use dpp::serialization::PlatformDeserializable;
22#[cfg(feature = "server")]
23use dpp::voting::contender_structs::ContenderWithSerializedDocumentV0;
24use dpp::voting::contender_structs::{
25    ContenderWithSerializedDocument, FinalizedContenderWithSerializedDocument,
26};
27#[cfg(feature = "server")]
28use dpp::voting::vote_info_storage::contested_document_vote_poll_stored_info::ContestedDocumentVotePollStoredInfo;
29#[cfg(feature = "server")]
30use dpp::voting::vote_info_storage::contested_document_vote_poll_stored_info::ContestedDocumentVotePollStoredInfoV0Getters;
31use dpp::voting::vote_info_storage::contested_document_vote_poll_winner_info::ContestedDocumentVotePollWinnerInfo;
32use dpp::voting::vote_polls::contested_document_resource_vote_poll::ContestedDocumentResourceVotePoll;
33#[cfg(feature = "server")]
34use grovedb::query_result_type::QueryResultType;
35#[cfg(feature = "server")]
36use grovedb::{Element, TransactionArg};
37use grovedb::{PathQuery, Query, QueryItem, SizedQuery};
38use platform_version::version::PlatformVersion;
39
40/// Represents the types of results that can be obtained from a contested document vote poll query.
41///
42/// This enum defines the various types of results that can be returned when querying the drive
43/// for contested document vote poll information.
44#[derive(Debug, PartialEq, Clone, Copy, Encode, Decode)]
45pub enum ContestedDocumentVotePollDriveQueryResultType {
46    /// The documents associated with the vote poll are returned in the query result.
47    Documents,
48    /// The vote tally results are returned in the query result.
49    VoteTally,
50    /// Both the documents and the vote tally results are returned in the query result.
51    DocumentsAndVoteTally,
52    /// We are searching for a single document only.
53    SingleDocumentByContender(Identifier),
54}
55
56impl ContestedDocumentVotePollDriveQueryResultType {
57    /// Helper method to say if this result type should return vote tally
58    pub fn has_vote_tally(&self) -> bool {
59        match self {
60            ContestedDocumentVotePollDriveQueryResultType::Documents => false,
61            ContestedDocumentVotePollDriveQueryResultType::SingleDocumentByContender(_) => false,
62            ContestedDocumentVotePollDriveQueryResultType::VoteTally => true,
63            ContestedDocumentVotePollDriveQueryResultType::DocumentsAndVoteTally => true,
64        }
65    }
66
67    /// Helper method to say if this result type should return documents
68    pub fn has_documents(&self) -> bool {
69        match self {
70            ContestedDocumentVotePollDriveQueryResultType::Documents => true,
71            ContestedDocumentVotePollDriveQueryResultType::SingleDocumentByContender(_) => true,
72            ContestedDocumentVotePollDriveQueryResultType::VoteTally => false,
73            ContestedDocumentVotePollDriveQueryResultType::DocumentsAndVoteTally => true,
74        }
75    }
76}
77
78impl TryFrom<i32> for ContestedDocumentVotePollDriveQueryResultType {
79    type Error = Error;
80
81    fn try_from(value: i32) -> Result<Self, Self::Error> {
82        match value {
83            0 => Ok(ContestedDocumentVotePollDriveQueryResultType::Documents),
84            1 => Ok(ContestedDocumentVotePollDriveQueryResultType::VoteTally),
85            2 => Ok(ContestedDocumentVotePollDriveQueryResultType::DocumentsAndVoteTally),
86            3 => Err(Error::Query(QuerySyntaxError::Unsupported(
87                "unsupported to get SingleDocumentByContender query result type".to_string()
88            ))),
89            n => Err(Error::Query(QuerySyntaxError::Unsupported(format!(
90                "unsupported contested document vote poll drive query result type {}, only 0, 1, 2 and 3 are supported",
91                n
92            )))),
93        }
94    }
95}
96
97/// Vote Poll Drive Query struct
98#[derive(Debug, PartialEq, Clone, Encode, Decode)]
99pub struct ContestedDocumentVotePollDriveQuery {
100    /// What vote poll are we asking for?
101    pub vote_poll: ContestedDocumentResourceVotePoll,
102    /// What result type are we interested in
103    pub result_type: ContestedDocumentVotePollDriveQueryResultType,
104    /// Offset
105    pub offset: Option<u16>,
106    /// Limit for returned contestant info, including locked or abstaining votes does not change this
107    pub limit: Option<u16>,
108    /// Start at identity id
109    pub start_at: Option<([u8; 32], bool)>,
110    /// Include locked and abstaining vote tally
111    /// This is not automatic, it will just be at the beginning if the order is ascending
112    /// If the order is descending, we will get a value if we finish the query
113    pub allow_include_locked_and_abstaining_vote_tally: bool,
114}
115
116/// Represents the result of executing a contested document vote poll drive query.
117///
118/// This struct holds the list of contenders and the number of skipped items
119/// when an offset is given.
120#[derive(Debug, PartialEq, Eq, Clone, Default)]
121pub struct ContestedDocumentVotePollDriveQueryExecutionResult {
122    /// The list of contenders returned by the query.
123    pub contenders: Vec<ContenderWithSerializedDocument>,
124    /// Locked tally
125    pub locked_vote_tally: Option<u32>,
126    /// Abstaining tally
127    pub abstaining_vote_tally: Option<u32>,
128    /// Finalization info
129    pub winner: Option<(ContestedDocumentVotePollWinnerInfo, BlockInfo)>,
130    /// The number of skipped items when an offset is given.
131    pub skipped: u16,
132}
133
134/// Represents the result of executing a contested document vote poll drive query.
135///
136/// This struct holds the list of contenders and the number of skipped items
137/// when an offset is given.
138#[derive(Debug, PartialEq, Eq, Clone, Default)]
139pub struct FinalizedContestedDocumentVotePollDriveQueryExecutionResult {
140    /// The list of contenders returned by the query.
141    pub contenders: Vec<FinalizedContenderWithSerializedDocument>,
142    /// Locked tally
143    pub locked_vote_tally: u32,
144    /// Abstaining tally
145    pub abstaining_vote_tally: u32,
146}
147
148impl TryFrom<ContestedDocumentVotePollDriveQueryExecutionResult>
149    for FinalizedContestedDocumentVotePollDriveQueryExecutionResult
150{
151    type Error = Error;
152
153    fn try_from(
154        value: ContestedDocumentVotePollDriveQueryExecutionResult,
155    ) -> Result<Self, Self::Error> {
156        let ContestedDocumentVotePollDriveQueryExecutionResult {
157            contenders,
158            locked_vote_tally,
159            abstaining_vote_tally,
160            ..
161        } = value;
162
163        let finalized_contenders = contenders
164            .into_iter()
165            .map(|contender| {
166                let finalized: FinalizedContenderWithSerializedDocument = contender.try_into()?;
167                Ok(finalized)
168            })
169            .collect::<Result<Vec<_>, Error>>()?;
170
171        Ok(
172            FinalizedContestedDocumentVotePollDriveQueryExecutionResult {
173                contenders: finalized_contenders,
174                locked_vote_tally: locked_vote_tally.ok_or(Error::Drive(
175                    DriveError::CorruptedCodeExecution("expected a locked tally"),
176                ))?,
177                abstaining_vote_tally: abstaining_vote_tally.ok_or(Error::Drive(
178                    DriveError::CorruptedCodeExecution("expected an abstaining tally"),
179                ))?,
180            },
181        )
182    }
183}
184
185impl ContestedDocumentVotePollDriveQuery {
186    #[cfg(feature = "server")]
187    /// Resolves the contested document vote poll drive query.
188    ///
189    /// This method processes the query by interacting with the drive, using the provided
190    /// transaction and platform version to ensure consistency and compatibility.
191    ///
192    /// # Parameters
193    ///
194    /// * `drive`: A reference to the `Drive` object used for database interactions.
195    /// * `transaction`: The transaction argument used to ensure consistency during the resolve operation.
196    /// * `platform_version`: The platform version to ensure compatibility.
197    ///
198    /// # Returns
199    ///
200    /// * `Ok(ResolvedContestedDocumentVotePollDriveQuery)` - The resolved query information.
201    /// * `Err(Error)` - An error if the resolution process fails.
202    ///
203    /// # Errors
204    ///
205    /// This method returns an `Error` variant if there is an issue resolving the query.
206    /// The specific error depends on the underlying problem encountered during resolution.
207    pub fn resolve(
208        &self,
209        drive: &Drive,
210        transaction: TransactionArg,
211        platform_version: &PlatformVersion,
212    ) -> Result<ResolvedContestedDocumentVotePollDriveQuery<'_>, Error> {
213        let ContestedDocumentVotePollDriveQuery {
214            vote_poll,
215            result_type,
216            offset,
217            limit,
218            start_at,
219            allow_include_locked_and_abstaining_vote_tally,
220        } = self;
221        Ok(ResolvedContestedDocumentVotePollDriveQuery {
222            vote_poll: vote_poll.resolve_allow_borrowed(drive, transaction, platform_version)?,
223            result_type: *result_type,
224            offset: *offset,
225            limit: *limit,
226            start_at: *start_at,
227            allow_include_locked_and_abstaining_vote_tally:
228                *allow_include_locked_and_abstaining_vote_tally,
229        })
230    }
231
232    #[cfg(feature = "verify")]
233    /// Resolves with a known contract provider
234    pub fn resolve_with_known_contracts_provider<'a>(
235        &self,
236        known_contracts_provider_fn: &super::ContractLookupFn,
237    ) -> Result<ResolvedContestedDocumentVotePollDriveQuery<'a>, Error> {
238        let ContestedDocumentVotePollDriveQuery {
239            vote_poll,
240            result_type,
241            offset,
242            limit,
243            start_at,
244            allow_include_locked_and_abstaining_vote_tally,
245        } = self;
246        Ok(ResolvedContestedDocumentVotePollDriveQuery {
247            vote_poll: vote_poll
248                .resolve_with_known_contracts_provider(known_contracts_provider_fn)?,
249            result_type: *result_type,
250            offset: *offset,
251            limit: *limit,
252            start_at: *start_at,
253            allow_include_locked_and_abstaining_vote_tally:
254                *allow_include_locked_and_abstaining_vote_tally,
255        })
256    }
257
258    #[cfg(any(feature = "verify", feature = "server"))]
259    /// Resolves with a provided borrowed contract
260    pub fn resolve_with_provided_borrowed_contract<'a>(
261        &self,
262        data_contract: &'a DataContract,
263    ) -> Result<ResolvedContestedDocumentVotePollDriveQuery<'a>, Error> {
264        let ContestedDocumentVotePollDriveQuery {
265            vote_poll,
266            result_type,
267            offset,
268            limit,
269            start_at,
270            allow_include_locked_and_abstaining_vote_tally,
271        } = self;
272        Ok(ResolvedContestedDocumentVotePollDriveQuery {
273            vote_poll: vote_poll.resolve_with_provided_borrowed_contract(data_contract)?,
274            result_type: *result_type,
275            offset: *offset,
276            limit: *limit,
277            start_at: *start_at,
278            allow_include_locked_and_abstaining_vote_tally:
279                *allow_include_locked_and_abstaining_vote_tally,
280        })
281    }
282
283    #[cfg(feature = "server")]
284    /// Executes a query with proof and returns the items and fee.
285    pub fn execute_with_proof(
286        self,
287        drive: &Drive,
288        block_info: Option<BlockInfo>,
289        transaction: TransactionArg,
290        platform_version: &PlatformVersion,
291    ) -> Result<(Vec<u8>, u64), Error> {
292        let mut drive_operations = vec![];
293        let items = self.execute_with_proof_internal(
294            drive,
295            transaction,
296            &mut drive_operations,
297            platform_version,
298        )?;
299        let cost = if let Some(block_info) = block_info {
300            let fee_result = Drive::calculate_fee(
301                None,
302                Some(drive_operations),
303                &block_info.epoch,
304                drive.config.epochs_per_era,
305                platform_version,
306                None,
307            )?;
308            fee_result.processing_fee
309        } else {
310            0
311        };
312        Ok((items, cost))
313    }
314
315    #[cfg(feature = "server")]
316    /// Executes an internal query with proof and returns the items.
317    pub(crate) fn execute_with_proof_internal(
318        self,
319        drive: &Drive,
320        transaction: TransactionArg,
321        drive_operations: &mut Vec<LowLevelDriveOperation>,
322        platform_version: &PlatformVersion,
323    ) -> Result<Vec<u8>, Error> {
324        let resolved = self.resolve(drive, transaction, platform_version)?;
325        let path_query = resolved.construct_path_query(platform_version)?;
326        // println!("{:?}", &path_query);
327        drive.grove_get_proved_path_query(
328            &path_query,
329            transaction,
330            drive_operations,
331            &platform_version.drive,
332        )
333    }
334
335    #[cfg(feature = "server")]
336    /// Executes a query with no proof and returns the items, skipped items, and fee.
337    pub fn execute_no_proof_with_cost(
338        &self,
339        drive: &Drive,
340        block_info: Option<BlockInfo>,
341        transaction: TransactionArg,
342        platform_version: &PlatformVersion,
343    ) -> Result<(ContestedDocumentVotePollDriveQueryExecutionResult, u64), Error> {
344        let mut drive_operations = vec![];
345        let result =
346            self.execute_no_proof(drive, transaction, &mut drive_operations, platform_version)?;
347        let cost = if let Some(block_info) = block_info {
348            let fee_result = Drive::calculate_fee(
349                None,
350                Some(drive_operations),
351                &block_info.epoch,
352                drive.config.epochs_per_era,
353                platform_version,
354                None,
355            )?;
356            fee_result.processing_fee
357        } else {
358            0
359        };
360        Ok((result, cost))
361    }
362
363    #[cfg(feature = "server")]
364    /// Executes an internal query with no proof and returns the values and skipped items.
365    pub fn execute_no_proof(
366        &self,
367        drive: &Drive,
368        transaction: TransactionArg,
369        drive_operations: &mut Vec<LowLevelDriveOperation>,
370        platform_version: &PlatformVersion,
371    ) -> Result<ContestedDocumentVotePollDriveQueryExecutionResult, Error> {
372        let resolved = self.resolve(drive, transaction, platform_version)?;
373        resolved.execute(drive, transaction, drive_operations, platform_version)
374    }
375}
376
377/// Vote Poll Drive Query struct
378#[derive(Debug, PartialEq, Clone)]
379pub struct ResolvedContestedDocumentVotePollDriveQuery<'a> {
380    /// What vote poll are we asking for?
381    pub vote_poll: ContestedDocumentResourceVotePollWithContractInfoAllowBorrowed<'a>,
382    /// What result type are we interested in
383    pub result_type: ContestedDocumentVotePollDriveQueryResultType,
384    /// Offset
385    pub offset: Option<u16>,
386    /// Limit
387    pub limit: Option<u16>,
388    /// Start at identity id, the bool is if it is also included
389    pub start_at: Option<([u8; 32], bool)>,
390    /// Include locked and abstaining vote tally
391    pub allow_include_locked_and_abstaining_vote_tally: bool,
392}
393
394impl ResolvedContestedDocumentVotePollDriveQuery<'_> {
395    /// Operations to construct a path query.
396    pub fn construct_path_query(
397        &self,
398        platform_version: &PlatformVersion,
399    ) -> Result<PathQuery, Error> {
400        let path = self.vote_poll.contenders_path(platform_version)?;
401
402        let mut query = Query::new();
403
404        let allow_include_locked_and_abstaining_vote_tally = self
405            .allow_include_locked_and_abstaining_vote_tally
406            && self.result_type.has_vote_tally();
407
408        // We have the following
409        // Stored Info [[0;31],0] Abstain votes [[0;31],1] Lock Votes [[0;31],2]
410
411        // this is a range on all elements
412        let limit = match &self.start_at {
413            None => {
414                if allow_include_locked_and_abstaining_vote_tally {
415                    match &self.result_type {
416                            ContestedDocumentVotePollDriveQueryResultType::Documents => {
417                                // Documents don't care about the vote tallies
418                                query.insert_range_after(RESOURCE_LOCK_VOTE_TREE_KEY_U8_32.to_vec()..);
419                                self.limit
420                            }
421                            ContestedDocumentVotePollDriveQueryResultType::VoteTally => {
422                                query.insert_all();
423                                self.limit.map(|limit| limit.saturating_add(3))
424                            }
425                            ContestedDocumentVotePollDriveQueryResultType::DocumentsAndVoteTally => {
426                                query.insert_all();
427                                self.limit.map(|limit| limit.saturating_mul(2).saturating_add(3))
428                            }
429                            ContestedDocumentVotePollDriveQueryResultType::SingleDocumentByContender(contender_id) => {
430                                query.insert_key(contender_id.to_vec());
431                                self.limit
432                            }
433                        }
434                } else {
435                    match &self.result_type {
436                            ContestedDocumentVotePollDriveQueryResultType::Documents => {
437                                query.insert_range_after(RESOURCE_LOCK_VOTE_TREE_KEY_U8_32.to_vec()..);
438                                self.limit
439                            }
440                            ContestedDocumentVotePollDriveQueryResultType::SingleDocumentByContender(contender_id) => {
441                                query.insert_key(contender_id.to_vec());
442                                self.limit
443                            }
444                            ContestedDocumentVotePollDriveQueryResultType::VoteTally => {
445                                query.insert_key(RESOURCE_STORED_INFO_KEY_U8_32.to_vec());
446                                query.insert_range_after(RESOURCE_LOCK_VOTE_TREE_KEY_U8_32.to_vec()..);
447                                self.limit.map(|limit| limit.saturating_add(1))
448                            }
449                            ContestedDocumentVotePollDriveQueryResultType::DocumentsAndVoteTally => {
450                                query.insert_key(RESOURCE_STORED_INFO_KEY_U8_32.to_vec());
451                                query.insert_range_after(RESOURCE_LOCK_VOTE_TREE_KEY_U8_32.to_vec()..);
452                                self.limit.map(|limit| limit.saturating_mul(2).saturating_add(1))
453                            }
454                        }
455                }
456            }
457            Some((starts_at_key_bytes, start_at_included)) => {
458                let starts_at_key = starts_at_key_bytes.to_vec();
459                match start_at_included {
460                    true => query.insert_range_from(starts_at_key..),
461                    false => query.insert_range_after(starts_at_key..),
462                }
463                match &self.result_type {
464                    ContestedDocumentVotePollDriveQueryResultType::Documents
465                    | ContestedDocumentVotePollDriveQueryResultType::SingleDocumentByContender(_)
466                    | ContestedDocumentVotePollDriveQueryResultType::VoteTally => self.limit,
467                    ContestedDocumentVotePollDriveQueryResultType::DocumentsAndVoteTally => {
468                        self.limit.map(|limit| limit.saturating_mul(2))
469                    }
470                }
471            }
472        };
473
474        let (subquery_path, subquery) = match self.result_type {
475            ContestedDocumentVotePollDriveQueryResultType::Documents
476            | ContestedDocumentVotePollDriveQueryResultType::SingleDocumentByContender(_) => {
477                (Some(vec![vec![0]]), None)
478            }
479            ContestedDocumentVotePollDriveQueryResultType::VoteTally => (Some(vec![vec![1]]), None),
480            ContestedDocumentVotePollDriveQueryResultType::DocumentsAndVoteTally => {
481                let mut query = Query::new();
482                query.insert_keys(vec![vec![0], vec![1]]);
483                (None, Some(query.into()))
484            }
485        };
486
487        query.default_subquery_branch.subquery_path = subquery_path;
488        query.default_subquery_branch.subquery = subquery;
489
490        if allow_include_locked_and_abstaining_vote_tally {
491            query.add_conditional_subquery(
492                QueryItem::Key(RESOURCE_LOCK_VOTE_TREE_KEY_U8_32.to_vec()),
493                Some(vec![vec![1]]),
494                None,
495            );
496            query.add_conditional_subquery(
497                QueryItem::Key(RESOURCE_ABSTAIN_VOTE_TREE_KEY_U8_32.to_vec()),
498                Some(vec![vec![1]]),
499                None,
500            );
501        }
502
503        query.add_conditional_subquery(
504            QueryItem::Key(RESOURCE_STORED_INFO_KEY_U8_32.to_vec()),
505            None,
506            None,
507        );
508
509        Ok(PathQuery {
510            path,
511            query: SizedQuery {
512                query,
513                limit,
514                offset: self.offset,
515            },
516        })
517    }
518
519    #[cfg(feature = "server")]
520    /// Executes the query with no proof
521    pub fn execute(
522        &self,
523        drive: &Drive,
524        transaction: TransactionArg,
525        drive_operations: &mut Vec<LowLevelDriveOperation>,
526        platform_version: &PlatformVersion,
527    ) -> Result<ContestedDocumentVotePollDriveQueryExecutionResult, Error> {
528        let path_query = self.construct_path_query(platform_version)?;
529        // println!("path_query {:?}", &path_query);
530        let query_result = drive.grove_get_path_query(
531            &path_query,
532            transaction,
533            QueryResultType::QueryPathKeyElementTrioResultType,
534            drive_operations,
535            &platform_version.drive,
536        );
537        match query_result {
538            Err(Error::GroveDB(e))
539                if matches!(
540                    e.as_ref(),
541                    GroveError::PathKeyNotFound(_)
542                        | GroveError::PathNotFound(_)
543                        | GroveError::PathParentLayerNotFound(_)
544                ) =>
545            {
546                Ok(ContestedDocumentVotePollDriveQueryExecutionResult::default())
547            }
548            Err(e) => Err(e),
549            Ok((query_result_elements, skipped)) => {
550                match self.result_type {
551                    ContestedDocumentVotePollDriveQueryResultType::Documents
552                    | ContestedDocumentVotePollDriveQueryResultType::SingleDocumentByContender(_) =>
553                    {
554                        // with documents only we don't need to work about lock and abstaining tree
555                        let contenders = query_result_elements
556                            .to_path_key_elements()
557                            .into_iter()
558                            .map(|(mut path, _key, document)| {
559                                let identity_id = path.pop().ok_or(Error::Drive(
560                                    DriveError::CorruptedDriveState(
561                                        "the path must have a last element".to_string(),
562                                    ),
563                                ))?;
564                                Ok(ContenderWithSerializedDocumentV0 {
565                                    identity_id: Identifier::try_from(identity_id)?,
566                                    serialized_document: Some(document.into_item_bytes()?),
567                                    vote_tally: None,
568                                }
569                                .into())
570                            })
571                            .collect::<Result<Vec<ContenderWithSerializedDocument>, Error>>()?;
572
573                        Ok(ContestedDocumentVotePollDriveQueryExecutionResult {
574                            contenders,
575                            locked_vote_tally: None,
576                            abstaining_vote_tally: None,
577                            winner: None,
578                            skipped,
579                        })
580                    }
581                    ContestedDocumentVotePollDriveQueryResultType::VoteTally => {
582                        let mut contenders = Vec::new();
583                        let mut locked_vote_tally: Option<u32> = None;
584                        let mut abstaining_vote_tally: Option<u32> = None;
585                        let mut winner = None;
586
587                        for (path, first_key, element) in
588                            query_result_elements.to_path_key_elements().into_iter()
589                        {
590                            let Some(identity_bytes) = path.last() else {
591                                return Err(Error::Drive(DriveError::CorruptedDriveState(
592                                    "the path must have a last element".to_string(),
593                                )));
594                            };
595                            match element {
596                                Element::SumTree(_, sum_tree_value, _) => {
597                                    if sum_tree_value < 0 || sum_tree_value > u32::MAX as i64 {
598                                        return Err(Error::Drive(DriveError::CorruptedDriveState(format!(
599                                            "sum tree value for vote tally must be between 0 and u32::Max, received {} from state",
600                                            sum_tree_value
601                                        ))));
602                                    }
603
604                                    if identity_bytes.as_slice()
605                                        == RESOURCE_LOCK_VOTE_TREE_KEY_U8_32.as_slice()
606                                    {
607                                        locked_vote_tally = Some(sum_tree_value as u32);
608                                    } else if identity_bytes.as_slice()
609                                        == RESOURCE_ABSTAIN_VOTE_TREE_KEY_U8_32.as_slice()
610                                    {
611                                        abstaining_vote_tally = Some(sum_tree_value as u32);
612                                    } else {
613                                        contenders.push(
614                                            ContenderWithSerializedDocumentV0 {
615                                                identity_id: Identifier::try_from(identity_bytes)?,
616                                                serialized_document: None,
617                                                vote_tally: Some(sum_tree_value as u32),
618                                            }
619                                            .into(),
620                                        );
621                                    }
622                                }
623                                Element::Item(serialized_item_info, _) => {
624                                    if first_key.as_slice() == RESOURCE_STORED_INFO_KEY_U8_32 {
625                                        // this is the stored info, let's check to see if the vote is over
626                                        let finalized_contested_document_vote_poll_stored_info = ContestedDocumentVotePollStoredInfo::deserialize_from_bytes(&serialized_item_info)?;
627                                        if finalized_contested_document_vote_poll_stored_info
628                                            .vote_poll_status()
629                                            .awarded_or_locked()
630                                        {
631                                            locked_vote_tally = Some(
632                                                finalized_contested_document_vote_poll_stored_info
633                                                    .last_locked_votes()
634                                                    .ok_or(Error::Drive(
635                                                        DriveError::CorruptedDriveState(
636                                                            "we should have last locked votes"
637                                                                .to_string(),
638                                                        ),
639                                                    ))?,
640                                            );
641                                            abstaining_vote_tally = Some(
642                                                finalized_contested_document_vote_poll_stored_info
643                                                    .last_abstain_votes()
644                                                    .ok_or(Error::Drive(
645                                                        DriveError::CorruptedDriveState(
646                                                            "we should have last abstain votes"
647                                                                .to_string(),
648                                                        ),
649                                                    ))?,
650                                            );
651                                            winner = Some((
652                                                finalized_contested_document_vote_poll_stored_info.winner(),
653                                                finalized_contested_document_vote_poll_stored_info
654                                                    .last_finalization_block().ok_or(Error::Drive(DriveError::CorruptedDriveState(
655                                                    "we should have a last finalization block".to_string(),
656                                                )))?,
657                                            ));
658                                            contenders = finalized_contested_document_vote_poll_stored_info
659                                                .contender_votes_in_vec_of_contender_with_serialized_document().ok_or(Error::Drive(DriveError::CorruptedDriveState(
660                                                "we should have a last contender votes".to_string(),
661                                            )))?;
662                                        }
663                                    } else {
664                                        return Err(Error::Drive(
665                                            DriveError::CorruptedDriveState(
666                                                "the only item that should be returned should be stored info"
667                                                    .to_string(),
668                                            ),
669                                        ));
670                                    }
671                                }
672                                _ => {
673                                    return Err(Error::Drive(DriveError::CorruptedDriveState(
674                                        "unexpected element type in result".to_string(),
675                                    )));
676                                }
677                            }
678                        }
679                        Ok(ContestedDocumentVotePollDriveQueryExecutionResult {
680                            contenders,
681                            locked_vote_tally,
682                            abstaining_vote_tally,
683                            winner,
684                            skipped,
685                        })
686                    }
687                    ContestedDocumentVotePollDriveQueryResultType::DocumentsAndVoteTally => {
688                        let mut elements_iter =
689                            query_result_elements.to_path_key_elements().into_iter();
690                        let mut contenders = vec![];
691                        let mut locked_vote_tally: Option<u32> = None;
692                        let mut abstaining_vote_tally: Option<u32> = None;
693                        let mut winner = None;
694
695                        // Handle ascending order
696                        while let Some((path, first_key, element)) = elements_iter.next() {
697                            let Some(identity_bytes) = path.last() else {
698                                return Err(Error::Drive(DriveError::CorruptedDriveState(
699                                    "the path must have a last element".to_string(),
700                                )));
701                            };
702
703                            match element {
704                                Element::SumTree(_, sum_tree_value, _) => {
705                                    if sum_tree_value < 0 || sum_tree_value > u32::MAX as i64 {
706                                        return Err(Error::Drive(DriveError::CorruptedDriveState(format!(
707                                            "sum tree value for vote tally must be between 0 and u32::Max, received {} from state",
708                                            sum_tree_value
709                                        ))));
710                                    }
711
712                                    if identity_bytes.as_slice()
713                                        == RESOURCE_LOCK_VOTE_TREE_KEY_U8_32.as_slice()
714                                    {
715                                        locked_vote_tally = Some(sum_tree_value as u32);
716                                    } else if identity_bytes.as_slice()
717                                        == RESOURCE_ABSTAIN_VOTE_TREE_KEY_U8_32.as_slice()
718                                    {
719                                        abstaining_vote_tally = Some(sum_tree_value as u32);
720                                    } else {
721                                        return Err(Error::Drive(DriveError::CorruptedDriveState(
722                                            "unexpected key for sum tree value".to_string(),
723                                        )));
724                                    }
725                                }
726                                Element::Item(serialized_item_info, _) => {
727                                    if first_key.as_slice() == RESOURCE_STORED_INFO_KEY_U8_32 {
728                                        // this is the stored info, let's check to see if the vote is over
729                                        let finalized_contested_document_vote_poll_stored_info = ContestedDocumentVotePollStoredInfo::deserialize_from_bytes(&serialized_item_info)?;
730                                        if finalized_contested_document_vote_poll_stored_info
731                                            .vote_poll_status()
732                                            .awarded_or_locked()
733                                        {
734                                            locked_vote_tally = Some(
735                                                finalized_contested_document_vote_poll_stored_info
736                                                    .last_locked_votes()
737                                                    .ok_or(Error::Drive(
738                                                        DriveError::CorruptedDriveState(
739                                                            "we should have last locked votes"
740                                                                .to_string(),
741                                                        ),
742                                                    ))?,
743                                            );
744                                            abstaining_vote_tally = Some(
745                                                finalized_contested_document_vote_poll_stored_info
746                                                    .last_abstain_votes()
747                                                    .ok_or(Error::Drive(
748                                                        DriveError::CorruptedDriveState(
749                                                            "we should have last abstain votes"
750                                                                .to_string(),
751                                                        ),
752                                                    ))?,
753                                            );
754                                            winner = Some((
755                                                finalized_contested_document_vote_poll_stored_info.winner(),
756                                                finalized_contested_document_vote_poll_stored_info
757                                                    .last_finalization_block().ok_or(Error::Drive(DriveError::CorruptedDriveState(
758                                                    "we should have a last finalization block".to_string(),
759                                                )))?,
760                                            ));
761                                            contenders = finalized_contested_document_vote_poll_stored_info
762                                                .contender_votes_in_vec_of_contender_with_serialized_document().ok_or(Error::Drive(DriveError::CorruptedDriveState(
763                                                "we should have a last contender votes".to_string(),
764                                            )))?;
765                                        }
766                                    } else {
767                                        // We should find a sum tree paired with this document
768                                        if let Some((
769                                            path_tally,
770                                            second_key,
771                                            Element::SumTree(_, sum_tree_value, _),
772                                        )) = elements_iter.next()
773                                        {
774                                            if path != path_tally {
775                                                return Err(Error::Drive(DriveError::CorruptedDriveState(format!("the two results in a chunk when requesting documents and vote tally should both have the same path asc, got {}:{}, and {}:{}", path.iter().map(hex::encode).collect::<Vec<_>>().join("/"), hex::encode(first_key), path_tally.iter().map(hex::encode).collect::<Vec<_>>().join("/"), hex::encode(second_key)))));
776                                            }
777
778                                            if sum_tree_value < 0
779                                                || sum_tree_value > u32::MAX as i64
780                                            {
781                                                return Err(Error::Drive(DriveError::CorruptedDriveState(format!(
782                                                    "sum tree value for vote tally must be between 0 and u32::Max, received {} from state",
783                                                    sum_tree_value
784                                                ))));
785                                            }
786
787                                            let identity_id =
788                                                Identifier::from_bytes(identity_bytes)?;
789                                            let contender = ContenderWithSerializedDocumentV0 {
790                                                identity_id,
791                                                serialized_document: Some(serialized_item_info),
792                                                vote_tally: Some(sum_tree_value as u32),
793                                            }
794                                            .into();
795                                            contenders.push(contender);
796                                        } else {
797                                            return Err(Error::Drive(
798                                                DriveError::CorruptedDriveState(
799                                                    "we should have a sum item after a normal item"
800                                                        .to_string(),
801                                                ),
802                                            ));
803                                        }
804                                    }
805                                }
806                                _ => {
807                                    return Err(Error::Drive(DriveError::CorruptedDriveState(
808                                        "unexpected element type in result".to_string(),
809                                    )));
810                                }
811                            }
812                        }
813
814                        Ok(ContestedDocumentVotePollDriveQueryExecutionResult {
815                            contenders,
816                            locked_vote_tally,
817                            abstaining_vote_tally,
818                            winner,
819                            skipped,
820                        })
821                    }
822                }
823            }
824        }
825    }
826}