drive/util/batch/drive_op_batch/
document.rs

1use crate::drive::Drive;
2use crate::error::Error;
3use crate::fees::op::LowLevelDriveOperation;
4use crate::util::batch::drive_op_batch::DriveLowLevelOperationConverter;
5use crate::util::object_size_info::DocumentInfo::{DocumentRefAndSerialization, DocumentRefInfo};
6use crate::util::object_size_info::{
7    DataContractInfo, DocumentAndContractInfo, DocumentTypeInfo, OwnedDocumentInfo,
8};
9use crate::util::storage_flags::StorageFlags;
10use dpp::block::block_info::BlockInfo;
11use dpp::data_contract::accessors::v0::DataContractV0Getters;
12use dpp::data_contract::document_type::DocumentTypeRef;
13use dpp::data_contract::DataContract;
14use dpp::document::Document;
15use dpp::prelude::Identifier;
16
17use dpp::system_data_contracts::withdrawals_contract::v1::document_types::withdrawal;
18
19use crate::drive::votes::resolved::vote_polls::contested_document_resource_vote_poll::ContestedDocumentResourceVotePollWithContractInfo;
20use dpp::version::PlatformVersion;
21use dpp::voting::vote_info_storage::contested_document_vote_poll_stored_info::ContestedDocumentVotePollStoredInfo;
22use dpp::ProtocolError;
23use grovedb::batch::KeyInfoPath;
24use grovedb::{EstimatedLayerInformation, TransactionArg};
25use std::borrow::Cow;
26use std::collections::HashMap;
27
28/// A wrapper for a document operation
29#[derive(Clone, Debug)]
30#[allow(clippy::large_enum_variant)]
31pub enum DocumentOperation<'a> {
32    /// An add operation
33    AddOperation {
34        /// Document info with maybe the owner id
35        owned_document_info: OwnedDocumentInfo<'a>,
36        /// Should we override the document if one already exists?
37        override_document: bool,
38    },
39    /// An update operation
40    UpdateOperation(UpdateOperationInfo<'a>),
41}
42
43/// Document and contract info
44#[derive(Clone, Debug)]
45pub struct DocumentOperationsForContractDocumentType<'a> {
46    /// Document info
47    pub operations: Vec<DocumentOperation<'a>>,
48    ///DataContract
49    pub contract: &'a DataContract,
50    /// Document type
51    pub document_type: DocumentTypeRef<'a>,
52}
53
54/// Operations on Documents
55#[derive(Clone, Debug)]
56#[allow(clippy::large_enum_variant)]
57pub enum DocumentOperationType<'a> {
58    /// Adds a document to a contract matching the desired info.
59    AddDocument {
60        /// The document and contract info, also may contain the owner_id
61        owned_document_info: OwnedDocumentInfo<'a>,
62        /// Data Contract info to potentially be resolved if needed
63        contract_info: DataContractInfo<'a>,
64        /// Document type
65        document_type_info: DocumentTypeInfo<'a>,
66        /// Should we override the document if one already exists?
67        override_document: bool,
68    },
69    /// Adds a contested document to a contract matching the desired info.
70    /// A contested document is a document that is trying to a acquire a
71    /// unique index that has a conflict resolution mechanism
72    AddContestedDocument {
73        /// The document and contract info, also may contain the owner_id
74        owned_document_info: OwnedDocumentInfo<'a>,
75        /// The vote poll in question that will should be created
76        contested_document_resource_vote_poll: ContestedDocumentResourceVotePollWithContractInfo,
77        /// Data Contract info to potentially be resolved if needed
78        contract_info: DataContractInfo<'a>,
79        /// Document type
80        document_type_info: DocumentTypeInfo<'a>,
81        /// Should we insert without verifying first that the document doesn't already exist
82        insert_without_check: bool,
83        /// Should we also insert the vote poll stored info
84        also_insert_vote_poll_stored_info: Option<ContestedDocumentVotePollStoredInfo>,
85    },
86    /// Updates a document and returns the associated fee.
87    UpdateDocument {
88        /// The document and contract info, also may contain the owner_id
89        owned_document_info: OwnedDocumentInfo<'a>,
90        /// Data Contract info to potentially be resolved if needed
91        contract_info: DataContractInfo<'a>,
92        /// Document type
93        document_type_info: DocumentTypeInfo<'a>,
94    },
95    /// Deletes a document
96    DeleteDocument {
97        /// The document id
98        document_id: Identifier,
99        /// Data Contract info to potentially be resolved if needed
100        contract_info: DataContractInfo<'a>,
101        /// Document type
102        document_type_info: DocumentTypeInfo<'a>,
103    },
104    /// Convenience method to add a withdrawal document.
105    AddWithdrawalDocument {
106        /// The document and contract info, also may contain the owner_id
107        owned_document_info: OwnedDocumentInfo<'a>,
108    },
109    /// Adds a document to a contract.
110    MultipleDocumentOperationsForSameContractDocumentType {
111        /// The document operations
112        document_operations: DocumentOperationsForContractDocumentType<'a>,
113    },
114}
115
116impl DriveLowLevelOperationConverter for DocumentOperationType<'_> {
117    fn into_low_level_drive_operations(
118        self,
119        drive: &Drive,
120        estimated_costs_only_with_layer_info: &mut Option<
121            HashMap<KeyInfoPath, EstimatedLayerInformation>,
122        >,
123        block_info: &BlockInfo,
124        transaction: TransactionArg,
125        platform_version: &PlatformVersion,
126    ) -> Result<Vec<LowLevelDriveOperation>, Error> {
127        match self {
128            DocumentOperationType::AddDocument {
129                owned_document_info,
130                contract_info,
131                document_type_info,
132                override_document,
133            } => {
134                let mut drive_operations: Vec<LowLevelDriveOperation> = vec![];
135                let contract_resolved_info = contract_info.resolve(
136                    drive,
137                    block_info,
138                    transaction,
139                    &mut drive_operations,
140                    platform_version,
141                )?;
142                let contract = contract_resolved_info.as_ref();
143                let document_type = document_type_info.resolve(contract)?;
144
145                let document_and_contract_info = DocumentAndContractInfo {
146                    owned_document_info,
147                    contract,
148                    document_type,
149                };
150                let mut operations = drive.add_document_for_contract_operations(
151                    document_and_contract_info,
152                    override_document,
153                    block_info,
154                    &mut None,
155                    estimated_costs_only_with_layer_info,
156                    transaction,
157                    platform_version,
158                )?;
159                drive_operations.append(&mut operations);
160                Ok(drive_operations)
161            }
162            DocumentOperationType::AddContestedDocument {
163                owned_document_info,
164                contested_document_resource_vote_poll,
165                contract_info,
166                document_type_info,
167                insert_without_check,
168                also_insert_vote_poll_stored_info,
169            } => {
170                let mut drive_operations: Vec<LowLevelDriveOperation> = vec![];
171                let contract_resolved_info = contract_info.resolve(
172                    drive,
173                    block_info,
174                    transaction,
175                    &mut drive_operations,
176                    platform_version,
177                )?;
178                let contract = contract_resolved_info.as_ref();
179                let document_type = document_type_info.resolve(contract)?;
180
181                let document_and_contract_info = DocumentAndContractInfo {
182                    owned_document_info,
183                    contract,
184                    document_type,
185                };
186                let mut operations = drive.add_contested_document_for_contract_operations(
187                    document_and_contract_info,
188                    contested_document_resource_vote_poll,
189                    insert_without_check,
190                    block_info,
191                    also_insert_vote_poll_stored_info,
192                    &mut None,
193                    estimated_costs_only_with_layer_info,
194                    transaction,
195                    platform_version,
196                )?;
197                drive_operations.append(&mut operations);
198                Ok(drive_operations)
199            }
200            DocumentOperationType::AddWithdrawalDocument {
201                owned_document_info,
202            } => {
203                let contract = drive.cache.system_data_contracts.load_withdrawals();
204
205                let document_type = contract
206                    .document_type_for_name(withdrawal::NAME)
207                    .map_err(ProtocolError::DataContractError)?;
208
209                let document_and_contract_info = DocumentAndContractInfo {
210                    owned_document_info,
211                    contract: &contract,
212                    document_type,
213                };
214                drive.add_document_for_contract_operations(
215                    document_and_contract_info,
216                    false,
217                    block_info,
218                    &mut None,
219                    estimated_costs_only_with_layer_info,
220                    transaction,
221                    platform_version,
222                )
223            }
224            DocumentOperationType::UpdateDocument {
225                owned_document_info,
226                contract_info,
227                document_type_info,
228            } => {
229                let mut drive_operations = vec![];
230                let contract_resolved_info = contract_info.resolve(
231                    drive,
232                    block_info,
233                    transaction,
234                    &mut drive_operations,
235                    platform_version,
236                )?;
237                let contract = contract_resolved_info.as_ref();
238                let document_type = document_type_info.resolve(contract)?;
239
240                let document_and_contract_info = DocumentAndContractInfo {
241                    owned_document_info,
242                    contract,
243                    document_type,
244                };
245                let mut operations = drive.update_document_for_contract_operations(
246                    document_and_contract_info,
247                    block_info,
248                    &mut None,
249                    estimated_costs_only_with_layer_info,
250                    transaction,
251                    platform_version,
252                )?;
253                drive_operations.append(&mut operations);
254                Ok(drive_operations)
255            }
256            DocumentOperationType::DeleteDocument {
257                document_id,
258                contract_info,
259                document_type_info,
260            } => {
261                let mut drive_operations: Vec<LowLevelDriveOperation> = vec![];
262                let contract_resolved_info = contract_info.resolve(
263                    drive,
264                    block_info,
265                    transaction,
266                    &mut drive_operations,
267                    platform_version,
268                )?;
269                let contract = contract_resolved_info.as_ref();
270                let document_type = document_type_info.resolve(contract)?;
271
272                drive.delete_document_for_contract_operations(
273                    document_id,
274                    contract,
275                    document_type,
276                    None,
277                    estimated_costs_only_with_layer_info,
278                    transaction,
279                    platform_version,
280                )
281            }
282            DocumentOperationType::MultipleDocumentOperationsForSameContractDocumentType {
283                document_operations,
284            } => {
285                let DocumentOperationsForContractDocumentType {
286                    operations,
287                    contract,
288                    document_type,
289                } = document_operations;
290
291                let mut drive_operations = vec![];
292                for document_operation in operations {
293                    match document_operation {
294                        DocumentOperation::AddOperation {
295                            owned_document_info,
296                            override_document,
297                        } => {
298                            let document_and_contract_info = DocumentAndContractInfo {
299                                owned_document_info,
300                                contract,
301                                document_type,
302                            };
303                            let mut operations = drive.add_document_for_contract_operations(
304                                document_and_contract_info,
305                                override_document,
306                                block_info,
307                                &mut Some(&mut drive_operations),
308                                estimated_costs_only_with_layer_info,
309                                transaction,
310                                platform_version,
311                            )?;
312                            drive_operations.append(&mut operations);
313                        }
314                        DocumentOperation::UpdateOperation(update_operation) => {
315                            let UpdateOperationInfo {
316                                document,
317                                serialized_document,
318                                owner_id,
319                                storage_flags,
320                            } = update_operation;
321
322                            let document_info =
323                                if let Some(serialized_document) = serialized_document {
324                                    DocumentRefAndSerialization((
325                                        document,
326                                        serialized_document,
327                                        storage_flags,
328                                    ))
329                                } else {
330                                    DocumentRefInfo((document, storage_flags))
331                                };
332                            let document_and_contract_info = DocumentAndContractInfo {
333                                owned_document_info: OwnedDocumentInfo {
334                                    document_info,
335                                    owner_id,
336                                },
337                                contract,
338                                document_type,
339                            };
340                            let mut operations = drive.update_document_for_contract_operations(
341                                document_and_contract_info,
342                                block_info,
343                                &mut Some(&mut drive_operations),
344                                estimated_costs_only_with_layer_info,
345                                transaction,
346                                platform_version,
347                            )?;
348                            drive_operations.append(&mut operations);
349                        }
350                    }
351                }
352                Ok(drive_operations)
353            }
354        }
355    }
356}
357
358/// A wrapper for an update operation
359#[derive(Clone, Debug)]
360pub struct UpdateOperationInfo<'a> {
361    /// The document to update
362    pub document: &'a Document,
363    /// The document in pre-serialized form
364    pub serialized_document: Option<&'a [u8]>,
365    /// The owner id, if none is specified will try to recover from serialized document
366    pub owner_id: Option<[u8; 32]>,
367    /// Add storage flags (like epoch, owner id, etc)
368    pub storage_flags: Option<Cow<'a, StorageFlags>>,
369}