Skip to main content

drive/query/
single_document_drive_query.rs

1use crate::drive::document::paths::contract_document_type_path_vec;
2use crate::util::common::encode::encode_u64;
3
4use crate::drive::votes;
5use crate::error::query::QuerySyntaxError;
6use crate::error::Error;
7use crate::query::Query;
8use grovedb::{PathQuery, SizedQuery};
9use platform_version::version::PlatformVersion;
10use platform_version::TryFromPlatformVersioned;
11
12/// The expected contested status of a document
13/// Drives stores the document in either the not contested location (most of the time)
14/// Or a temporary contested area while the contest is ongoing
15#[derive(Debug, PartialEq, Clone)]
16#[repr(u8)]
17pub enum SingleDocumentDriveQueryContestedStatus {
18    /// The document was not contested by the system.
19    NotContested = 0,
20    /// We don't know if the document was contested by the system, or we are not sure if the contest
21    /// is already over or not.
22    MaybeContested = 1,
23    /// We know that the document was contested by the system and the contest is not over.
24    Contested = 2,
25}
26
27impl TryFrom<i32> for SingleDocumentDriveQueryContestedStatus {
28    type Error = Error;
29
30    fn try_from(value: i32) -> Result<Self, Self::Error> {
31        match value {
32            0 => Ok(SingleDocumentDriveQueryContestedStatus::NotContested),
33            1 => Ok(SingleDocumentDriveQueryContestedStatus::MaybeContested),
34            2 => Ok(SingleDocumentDriveQueryContestedStatus::Contested),
35            n => Err(Error::Query(QuerySyntaxError::Unsupported(format!(
36                "unsupported contested status {}, only 0, 1 and 2 are supported",
37                n
38            )))),
39        }
40    }
41}
42
43/// Drive query struct
44#[derive(Debug, PartialEq, Clone)]
45pub struct SingleDocumentDriveQuery {
46    ///DataContract
47    pub contract_id: [u8; 32],
48    /// Document type
49    pub document_type_name: String,
50    /// Document type keeps history
51    pub document_type_keeps_history: bool,
52    /// Document
53    pub document_id: [u8; 32],
54    /// Block time
55    pub block_time_ms: Option<u64>,
56    /// True if the document might have gone to a contested resolution
57    pub contested_status: SingleDocumentDriveQueryContestedStatus,
58}
59
60impl SingleDocumentDriveQuery {
61    /// The query of document `document_id` of `document_type_name` on `contract_id` as it
62    /// stands now, outside any contest: the query a moderator's deletion without a record and
63    /// a moderator's field change are proved by. The prover and the verifier both build it
64    /// here, so the two can not drift apart.
65    pub fn latest_not_contested(
66        contract_id: [u8; 32],
67        document_type_name: String,
68        document_type_keeps_history: bool,
69        document_id: [u8; 32],
70    ) -> Self {
71        SingleDocumentDriveQuery {
72            contract_id,
73            document_type_name,
74            document_type_keeps_history,
75            document_id,
76            block_time_ms: None,
77            contested_status: SingleDocumentDriveQueryContestedStatus::NotContested,
78        }
79    }
80
81    /// Operations to construct a path query.
82    pub fn construct_path_query(
83        &self,
84        platform_version: &PlatformVersion,
85    ) -> Result<PathQuery, Error> {
86        match self.contested_status {
87            SingleDocumentDriveQueryContestedStatus::NotContested => {
88                Ok(self.construct_non_contested_path_query(true))
89            }
90            SingleDocumentDriveQueryContestedStatus::MaybeContested => {
91                let non_contested = self.construct_non_contested_path_query(true);
92                let contested = self.construct_contested_path_query(true);
93                PathQuery::merge(
94                    vec![&non_contested, &contested],
95                    &platform_version.drive.grove_version,
96                )
97                .map_err(Error::from)
98            }
99            SingleDocumentDriveQueryContestedStatus::Contested => {
100                Ok(self.construct_contested_path_query(true))
101            }
102        }
103    }
104
105    /// Operations to construct the normal path query.
106    fn construct_non_contested_path_query(&self, with_limit_1: bool) -> PathQuery {
107        // First we should get the overall document_type_path
108        let mut path =
109            contract_document_type_path_vec(&self.contract_id, self.document_type_name.as_str());
110
111        path.push(vec![0]);
112
113        let mut query = Query::new();
114        query.insert_key(self.document_id.to_vec());
115
116        if self.document_type_keeps_history {
117            // if the documents keep history then we should insert a subquery
118            if let Some(block_time) = self.block_time_ms {
119                let encoded_block_time = encode_u64(block_time);
120                let mut sub_query = Query::new_with_direction(false);
121                sub_query.insert_range_to_inclusive(..=encoded_block_time);
122                query.set_subquery(sub_query);
123            } else {
124                query.set_subquery_key(vec![0]);
125            }
126        }
127
128        let limit = if with_limit_1 { Some(1) } else { None };
129
130        PathQuery::new(path, SizedQuery::new(query, limit, None))
131    }
132
133    /// Operations to construct the contested path query.
134    fn construct_contested_path_query(&self, with_limit_1: bool) -> PathQuery {
135        // First we should get the overall document_type_path
136        let path = votes::paths::vote_contested_resource_contract_documents_storage_path_vec(
137            &self.contract_id,
138            self.document_type_name.as_str(),
139        );
140
141        let mut query = Query::new();
142        query.insert_key(self.document_id.to_vec());
143
144        let limit = if with_limit_1 { Some(1) } else { None };
145
146        PathQuery::new(path, SizedQuery::new(query, limit, None))
147    }
148}
149
150impl TryFromPlatformVersioned<SingleDocumentDriveQuery> for PathQuery {
151    type Error = Error;
152    fn try_from_platform_versioned(
153        value: SingleDocumentDriveQuery,
154        platform_version: &PlatformVersion,
155    ) -> Result<Self, Self::Error> {
156        value.construct_path_query(platform_version)
157    }
158}