Skip to main content

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_event::DocumentEvent;
15use dpp::document::Document;
16use dpp::prelude::{Identifier, IdentityNonce};
17
18use dpp::system_data_contracts::withdrawals_contract::v1::document_types::withdrawal;
19
20use crate::drive::votes::resolved::vote_polls::contested_document_resource_vote_poll::ContestedDocumentResourceVotePollWithContractInfo;
21use dpp::platform_value::Value;
22use dpp::version::PlatformVersion;
23use dpp::voting::vote_info_storage::contested_document_vote_poll_stored_info::ContestedDocumentVotePollStoredInfo;
24use dpp::ProtocolError;
25use grovedb::batch::KeyInfoPath;
26use grovedb::{EstimatedLayerInformation, TransactionArg};
27use std::borrow::Cow;
28use std::collections::{BTreeMap, HashMap};
29
30/// A wrapper for a document operation
31#[derive(Clone, Debug)]
32#[allow(clippy::large_enum_variant)]
33pub enum DocumentOperation<'a> {
34    /// An add operation
35    AddOperation {
36        /// Document info with maybe the owner id
37        owned_document_info: OwnedDocumentInfo<'a>,
38        /// Should we override the document if one already exists?
39        override_document: bool,
40    },
41    /// An update operation
42    UpdateOperation(UpdateOperationInfo<'a>),
43}
44
45/// Document and contract info
46#[derive(Clone, Debug)]
47pub struct DocumentOperationsForContractDocumentType<'a> {
48    /// Document info
49    pub operations: Vec<DocumentOperation<'a>>,
50    ///DataContract
51    pub contract: &'a DataContract,
52    /// Document type
53    pub document_type: DocumentTypeRef<'a>,
54}
55
56/// Operations on Documents
57#[derive(Clone, Debug)]
58#[allow(clippy::large_enum_variant)]
59pub enum DocumentOperationType<'a> {
60    /// Adds a document to a contract matching the desired info.
61    AddDocument {
62        /// The document and contract info, also may contain the owner_id
63        owned_document_info: OwnedDocumentInfo<'a>,
64        /// Data Contract info to potentially be resolved if needed
65        contract_info: DataContractInfo<'a>,
66        /// Document type
67        document_type_info: DocumentTypeInfo<'a>,
68        /// Should we override the document if one already exists?
69        override_document: bool,
70    },
71    /// Adds a contested document to a contract matching the desired info.
72    /// A contested document is a document that is trying to a acquire a
73    /// unique index that has a conflict resolution mechanism
74    AddContestedDocument {
75        /// The document and contract info, also may contain the owner_id
76        owned_document_info: OwnedDocumentInfo<'a>,
77        /// The vote poll in question that will should be created
78        contested_document_resource_vote_poll: ContestedDocumentResourceVotePollWithContractInfo,
79        /// Data Contract info to potentially be resolved if needed
80        contract_info: DataContractInfo<'a>,
81        /// Document type
82        document_type_info: DocumentTypeInfo<'a>,
83        /// Should we insert without verifying first that the document doesn't already exist
84        insert_without_check: bool,
85        /// Should we also insert the vote poll stored info
86        also_insert_vote_poll_stored_info: Option<ContestedDocumentVotePollStoredInfo>,
87    },
88    /// Updates a document and returns the associated fee.
89    UpdateDocument {
90        /// The document and contract info, also may contain the owner_id
91        owned_document_info: OwnedDocumentInfo<'a>,
92        /// Data Contract info to potentially be resolved if needed
93        contract_info: DataContractInfo<'a>,
94        /// Document type
95        document_type_info: DocumentTypeInfo<'a>,
96    },
97    /// Deletes a document
98    DeleteDocument {
99        /// The document id
100        document_id: Identifier,
101        /// Data Contract info to potentially be resolved if needed
102        contract_info: DataContractInfo<'a>,
103        /// Document type
104        document_type_info: DocumentTypeInfo<'a>,
105    },
106    /// Deletes an indexOnly document from its property values — there is
107    /// no primary-storage row to fetch, so the values (plus the owner)
108    /// are what every index entry is recomputed from. `$createdAt` may
109    /// ride in `data` under its system key when the type indexes it.
110    DeleteIndexOnlyDocument {
111        /// The document id (deterministic; never stored)
112        document_id: Identifier,
113        /// The owner whose entries are being removed
114        owner_id: Identifier,
115        /// The document's property values
116        data: BTreeMap<String, Value>,
117        /// Data Contract info to potentially be resolved if needed
118        contract_info: DataContractInfo<'a>,
119        /// Document type
120        document_type_info: DocumentTypeInfo<'a>,
121    },
122    /// Convenience method to add a withdrawal document.
123    AddWithdrawalDocument {
124        /// The document and contract info, also may contain the owner_id
125        owned_document_info: OwnedDocumentInfo<'a>,
126    },
127    /// Adds a document to a contract.
128    MultipleDocumentOperationsForSameContractDocumentType {
129        /// The document operations
130        document_operations: DocumentOperationsForContractDocumentType<'a>,
131    },
132    /// Adds a historical document to the document history system contract,
133    /// recording a transfer, purchase, or price update of a document whose
134    /// document type subscribed to history.
135    DocumentHistory {
136        /// The data contract of the source document
137        source_data_contract_id: Identifier,
138        /// The document type name of the source document
139        source_document_type_name: String,
140        /// The source document
141        source_document_id: Identifier,
142        /// The identity making the event
143        owner_id: Identifier,
144        /// The nonce
145        nonce: IdentityNonce,
146        /// The document event
147        event: DocumentEvent,
148    },
149}
150
151impl DriveLowLevelOperationConverter for DocumentOperationType<'_> {
152    fn into_low_level_drive_operations(
153        self,
154        drive: &Drive,
155        estimated_costs_only_with_layer_info: &mut Option<
156            HashMap<KeyInfoPath, EstimatedLayerInformation>,
157        >,
158        block_info: &BlockInfo,
159        transaction: TransactionArg,
160        platform_version: &PlatformVersion,
161    ) -> Result<Vec<LowLevelDriveOperation>, Error> {
162        match self {
163            DocumentOperationType::AddDocument {
164                owned_document_info,
165                contract_info,
166                document_type_info,
167                override_document,
168            } => {
169                let mut drive_operations: Vec<LowLevelDriveOperation> = vec![];
170                let contract_resolved_info = contract_info.resolve(
171                    drive,
172                    block_info,
173                    transaction,
174                    &mut drive_operations,
175                    platform_version,
176                )?;
177                let contract = contract_resolved_info.as_ref();
178                let document_type = document_type_info.resolve(contract)?;
179
180                let document_and_contract_info = DocumentAndContractInfo {
181                    owned_document_info,
182                    contract,
183                    document_type,
184                };
185                let mut operations = drive.add_document_for_contract_operations(
186                    document_and_contract_info,
187                    override_document,
188                    block_info,
189                    &mut None,
190                    estimated_costs_only_with_layer_info,
191                    transaction,
192                    platform_version,
193                )?;
194                drive_operations.append(&mut operations);
195                Ok(drive_operations)
196            }
197            DocumentOperationType::AddContestedDocument {
198                owned_document_info,
199                contested_document_resource_vote_poll,
200                contract_info,
201                document_type_info,
202                insert_without_check,
203                also_insert_vote_poll_stored_info,
204            } => {
205                let mut drive_operations: Vec<LowLevelDriveOperation> = vec![];
206                let contract_resolved_info = contract_info.resolve(
207                    drive,
208                    block_info,
209                    transaction,
210                    &mut drive_operations,
211                    platform_version,
212                )?;
213                let contract = contract_resolved_info.as_ref();
214                let document_type = document_type_info.resolve(contract)?;
215
216                let document_and_contract_info = DocumentAndContractInfo {
217                    owned_document_info,
218                    contract,
219                    document_type,
220                };
221                let mut operations = drive.add_contested_document_for_contract_operations(
222                    document_and_contract_info,
223                    contested_document_resource_vote_poll,
224                    insert_without_check,
225                    block_info,
226                    also_insert_vote_poll_stored_info,
227                    &mut None,
228                    estimated_costs_only_with_layer_info,
229                    transaction,
230                    platform_version,
231                )?;
232                drive_operations.append(&mut operations);
233                Ok(drive_operations)
234            }
235            DocumentOperationType::AddWithdrawalDocument {
236                owned_document_info,
237            } => {
238                let contract = drive
239                    .cache
240                    .system_data_contracts
241                    .load_withdrawals(platform_version)?;
242
243                let document_type = contract
244                    .document_type_for_name(withdrawal::NAME)
245                    .map_err(ProtocolError::DataContractError)?;
246
247                let document_and_contract_info = DocumentAndContractInfo {
248                    owned_document_info,
249                    contract: &contract,
250                    document_type,
251                };
252                drive.add_document_for_contract_operations(
253                    document_and_contract_info,
254                    false,
255                    block_info,
256                    &mut None,
257                    estimated_costs_only_with_layer_info,
258                    transaction,
259                    platform_version,
260                )
261            }
262            DocumentOperationType::UpdateDocument {
263                owned_document_info,
264                contract_info,
265                document_type_info,
266            } => {
267                let mut drive_operations = vec![];
268                let contract_resolved_info = contract_info.resolve(
269                    drive,
270                    block_info,
271                    transaction,
272                    &mut drive_operations,
273                    platform_version,
274                )?;
275                let contract = contract_resolved_info.as_ref();
276                let document_type = document_type_info.resolve(contract)?;
277
278                let document_and_contract_info = DocumentAndContractInfo {
279                    owned_document_info,
280                    contract,
281                    document_type,
282                };
283                let mut operations = drive.update_document_for_contract_operations(
284                    document_and_contract_info,
285                    block_info,
286                    &mut None,
287                    estimated_costs_only_with_layer_info,
288                    transaction,
289                    platform_version,
290                )?;
291                drive_operations.append(&mut operations);
292                Ok(drive_operations)
293            }
294            DocumentOperationType::DocumentHistory {
295                source_data_contract_id,
296                source_document_type_name,
297                source_document_id,
298                owner_id,
299                nonce,
300                event,
301            } => {
302                let batch_operations = drive.add_document_history_operations(
303                    source_data_contract_id,
304                    source_document_type_name.as_str(),
305                    source_document_id,
306                    owner_id,
307                    nonce,
308                    event,
309                    block_info,
310                    estimated_costs_only_with_layer_info,
311                    transaction,
312                    platform_version,
313                )?;
314                Ok(batch_operations)
315            }
316            DocumentOperationType::DeleteDocument {
317                document_id,
318                contract_info,
319                document_type_info,
320            } => {
321                let mut drive_operations: Vec<LowLevelDriveOperation> = vec![];
322                let contract_resolved_info = contract_info.resolve(
323                    drive,
324                    block_info,
325                    transaction,
326                    &mut drive_operations,
327                    platform_version,
328                )?;
329                let contract = contract_resolved_info.as_ref();
330                let document_type = document_type_info.resolve(contract)?;
331
332                drive.delete_document_for_contract_operations(
333                    document_id,
334                    contract,
335                    document_type,
336                    None,
337                    estimated_costs_only_with_layer_info,
338                    transaction,
339                    platform_version,
340                )
341            }
342            DocumentOperationType::DeleteIndexOnlyDocument {
343                document_id,
344                owner_id,
345                data,
346                contract_info,
347                document_type_info,
348            } => {
349                let mut drive_operations: Vec<LowLevelDriveOperation> = vec![];
350                let contract_resolved_info = contract_info.resolve(
351                    drive,
352                    block_info,
353                    transaction,
354                    &mut drive_operations,
355                    platform_version,
356                )?;
357                let contract = contract_resolved_info.as_ref();
358                let document_type = document_type_info.resolve(contract)?;
359
360                // Reconstruct the document the entries were written from.
361                let document = Drive::index_only_document_from_values(document_id, owner_id, data)?;
362
363                drive.delete_index_only_document_for_contract_operations(
364                    document,
365                    contract,
366                    document_type,
367                    None,
368                    estimated_costs_only_with_layer_info,
369                    transaction,
370                    platform_version,
371                )
372            }
373            DocumentOperationType::MultipleDocumentOperationsForSameContractDocumentType {
374                document_operations,
375            } => {
376                let DocumentOperationsForContractDocumentType {
377                    operations,
378                    contract,
379                    document_type,
380                } = document_operations;
381
382                let mut drive_operations = vec![];
383                for document_operation in operations {
384                    match document_operation {
385                        DocumentOperation::AddOperation {
386                            owned_document_info,
387                            override_document,
388                        } => {
389                            let document_and_contract_info = DocumentAndContractInfo {
390                                owned_document_info,
391                                contract,
392                                document_type,
393                            };
394                            let mut operations = drive.add_document_for_contract_operations(
395                                document_and_contract_info,
396                                override_document,
397                                block_info,
398                                &mut Some(&mut drive_operations),
399                                estimated_costs_only_with_layer_info,
400                                transaction,
401                                platform_version,
402                            )?;
403                            drive_operations.append(&mut operations);
404                        }
405                        DocumentOperation::UpdateOperation(update_operation) => {
406                            let UpdateOperationInfo {
407                                document,
408                                serialized_document,
409                                owner_id,
410                                storage_flags,
411                            } = update_operation;
412
413                            let document_info =
414                                if let Some(serialized_document) = serialized_document {
415                                    DocumentRefAndSerialization((
416                                        document,
417                                        serialized_document,
418                                        storage_flags,
419                                    ))
420                                } else {
421                                    DocumentRefInfo((document, storage_flags))
422                                };
423                            let document_and_contract_info = DocumentAndContractInfo {
424                                owned_document_info: OwnedDocumentInfo {
425                                    document_info,
426                                    owner_id,
427                                },
428                                contract,
429                                document_type,
430                            };
431                            let mut operations = drive.update_document_for_contract_operations(
432                                document_and_contract_info,
433                                block_info,
434                                &mut Some(&mut drive_operations),
435                                estimated_costs_only_with_layer_info,
436                                transaction,
437                                platform_version,
438                            )?;
439                            drive_operations.append(&mut operations);
440                        }
441                    }
442                }
443                Ok(drive_operations)
444            }
445        }
446    }
447}
448
449/// A wrapper for an update operation
450#[derive(Clone, Debug)]
451pub struct UpdateOperationInfo<'a> {
452    /// The document to update
453    pub document: &'a Document,
454    /// The document in pre-serialized form
455    pub serialized_document: Option<&'a [u8]>,
456    /// The owner id, if none is specified will try to recover from serialized document
457    pub owner_id: Option<[u8; 32]>,
458    /// Add storage flags (like epoch, owner id, etc)
459    pub storage_flags: Option<Cow<'a, StorageFlags>>,
460}