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#[derive(Clone, Debug)]
32#[allow(clippy::large_enum_variant)]
33pub enum DocumentOperation<'a> {
34 AddOperation {
36 owned_document_info: OwnedDocumentInfo<'a>,
38 override_document: bool,
40 },
41 UpdateOperation(UpdateOperationInfo<'a>),
43}
44
45#[derive(Clone, Debug)]
47pub struct DocumentOperationsForContractDocumentType<'a> {
48 pub operations: Vec<DocumentOperation<'a>>,
50 pub contract: &'a DataContract,
52 pub document_type: DocumentTypeRef<'a>,
54}
55
56#[derive(Clone, Debug)]
58#[allow(clippy::large_enum_variant)]
59pub enum DocumentOperationType<'a> {
60 AddDocument {
62 owned_document_info: OwnedDocumentInfo<'a>,
64 contract_info: DataContractInfo<'a>,
66 document_type_info: DocumentTypeInfo<'a>,
68 override_document: bool,
70 },
71 AddContestedDocument {
75 owned_document_info: OwnedDocumentInfo<'a>,
77 contested_document_resource_vote_poll: ContestedDocumentResourceVotePollWithContractInfo,
79 contract_info: DataContractInfo<'a>,
81 document_type_info: DocumentTypeInfo<'a>,
83 insert_without_check: bool,
85 also_insert_vote_poll_stored_info: Option<ContestedDocumentVotePollStoredInfo>,
87 },
88 UpdateDocument {
90 owned_document_info: OwnedDocumentInfo<'a>,
92 contract_info: DataContractInfo<'a>,
94 document_type_info: DocumentTypeInfo<'a>,
96 },
97 AddDocumentAndDeleteConsumed {
104 owned_document_info: OwnedDocumentInfo<'a>,
106 contract_info: DataContractInfo<'a>,
108 document_type_info: DocumentTypeInfo<'a>,
110 consumed_documents: Vec<(Identifier, String)>,
112 },
113 DeleteDocument {
115 document_id: Identifier,
117 contract_info: DataContractInfo<'a>,
119 document_type_info: DocumentTypeInfo<'a>,
121 },
122 ForceDeleteDocument {
131 document_id: Identifier,
133 contract_info: DataContractInfo<'a>,
135 document_type_info: DocumentTypeInfo<'a>,
137 },
138 DeleteIndexOnlyDocument {
143 document_id: Identifier,
145 owner_id: Identifier,
147 data: BTreeMap<String, Value>,
149 contract_info: DataContractInfo<'a>,
151 document_type_info: DocumentTypeInfo<'a>,
153 },
154 AddWithdrawalDocument {
156 owned_document_info: OwnedDocumentInfo<'a>,
158 },
159 MultipleDocumentOperationsForSameContractDocumentType {
161 document_operations: DocumentOperationsForContractDocumentType<'a>,
163 },
164 DocumentHistory {
168 source_data_contract_id: Identifier,
170 source_document_type_name: String,
172 source_document_id: Identifier,
174 owner_id: Identifier,
176 nonce: IdentityNonce,
178 event: DocumentEvent,
180 },
181}
182
183impl DriveLowLevelOperationConverter for DocumentOperationType<'_> {
184 fn into_low_level_drive_operations(
185 self,
186 drive: &Drive,
187 estimated_costs_only_with_layer_info: &mut Option<
188 HashMap<KeyInfoPath, EstimatedLayerInformation>,
189 >,
190 block_info: &BlockInfo,
191 transaction: TransactionArg,
192 platform_version: &PlatformVersion,
193 ) -> Result<Vec<LowLevelDriveOperation>, Error> {
194 if estimated_costs_only_with_layer_info.is_none() {
195 self.prepare_time_range_ttl(drive, block_info, transaction, platform_version)?;
196 }
197 self.into_low_level_drive_operations_after_ttl_drain(
198 drive,
199 estimated_costs_only_with_layer_info,
200 block_info,
201 transaction,
202 platform_version,
203 )
204 }
205}
206
207impl DocumentOperationType<'_> {
208 pub(crate) fn prepare_time_range_ttl(
210 &self,
211 drive: &Drive,
212 block_info: &BlockInfo,
213 transaction: TransactionArg,
214 platform_version: &PlatformVersion,
215 ) -> Result<(), Error> {
216 if platform_version
217 .system_limits
218 .min_time_range_ttl_drop_operations_per_write
219 .is_none()
220 {
221 return Ok(());
222 }
223 self.for_each_document_type(
226 drive,
227 block_info,
228 transaction,
229 |contract, document_type, _| {
230 drive.prepare_document_time_range_ttl(
231 contract,
232 document_type,
233 block_info.time_ms,
234 transaction,
235 platform_version,
236 )
237 },
238 platform_version,
239 )
240 }
241
242 pub(crate) fn for_each_document_type(
252 &self,
253 drive: &Drive,
254 block_info: &BlockInfo,
255 transaction: TransactionArg,
256 mut visit: impl FnMut(&DataContract, DocumentTypeRef, bool) -> Result<(), Error>,
257 platform_version: &PlatformVersion,
258 ) -> Result<(), Error> {
259 match self {
260 Self::AddDocument {
261 contract_info,
262 document_type_info,
263 ..
264 }
265 | Self::AddContestedDocument {
266 contract_info,
267 document_type_info,
268 ..
269 }
270 | Self::UpdateDocument {
271 contract_info,
272 document_type_info,
273 ..
274 }
275 | Self::DeleteDocument {
276 contract_info,
277 document_type_info,
278 ..
279 }
280 | Self::ForceDeleteDocument {
281 contract_info,
282 document_type_info,
283 ..
284 }
285 | Self::DeleteIndexOnlyDocument {
286 contract_info,
287 document_type_info,
288 ..
289 } => {
290 let resolved = contract_info.clone().resolve(
291 drive,
292 block_info,
293 transaction,
294 &mut vec![],
295 platform_version,
296 )?;
297 let contract = resolved.as_ref();
298 visit(
299 contract,
300 document_type_info.clone().resolve(contract)?,
301 matches!(self, Self::AddDocument { .. }),
302 )
303 }
304 Self::MultipleDocumentOperationsForSameContractDocumentType {
305 document_operations,
306 } => {
307 for operation in &document_operations.operations {
308 visit(
309 document_operations.contract,
310 document_operations.document_type,
311 matches!(operation, DocumentOperation::AddOperation { .. }),
312 )?;
313 }
314 Ok(())
315 }
316 Self::AddDocumentAndDeleteConsumed {
318 contract_info,
319 document_type_info,
320 consumed_documents,
321 ..
322 } => {
323 let resolved = contract_info.clone().resolve(
324 drive,
325 block_info,
326 transaction,
327 &mut vec![],
328 platform_version,
329 )?;
330 let contract = resolved.as_ref();
331 visit(
332 contract,
333 document_type_info.clone().resolve(contract)?,
334 true,
335 )?;
336 for (_, document_type_name) in consumed_documents {
337 visit(
338 contract,
339 DocumentTypeInfo::DocumentTypeName(document_type_name.clone())
340 .resolve(contract)?,
341 false,
342 )?;
343 }
344 Ok(())
345 }
346 Self::AddWithdrawalDocument { .. } | Self::DocumentHistory { .. } => Ok(()),
348 }
349 }
350
351 pub(crate) fn into_low_level_drive_operations_after_ttl_drain(
352 self,
353 drive: &Drive,
354 estimated_costs_only_with_layer_info: &mut Option<
355 HashMap<KeyInfoPath, EstimatedLayerInformation>,
356 >,
357 block_info: &BlockInfo,
358 transaction: TransactionArg,
359 platform_version: &PlatformVersion,
360 ) -> Result<Vec<LowLevelDriveOperation>, Error> {
361 match self {
362 DocumentOperationType::AddDocument {
363 owned_document_info,
364 contract_info,
365 document_type_info,
366 override_document,
367 } => {
368 let mut drive_operations: Vec<LowLevelDriveOperation> = vec![];
369 let contract_resolved_info = contract_info.resolve(
370 drive,
371 block_info,
372 transaction,
373 &mut drive_operations,
374 platform_version,
375 )?;
376 let contract = contract_resolved_info.as_ref();
377 let document_type = document_type_info.resolve(contract)?;
378
379 let document_and_contract_info = DocumentAndContractInfo {
380 owned_document_info,
381 contract,
382 document_type,
383 };
384 let mut operations = drive.add_document_for_contract_operations_without_ttl_drain(
385 document_and_contract_info,
386 override_document,
387 block_info,
388 &mut None,
389 estimated_costs_only_with_layer_info,
390 transaction,
391 platform_version,
392 )?;
393 drive_operations.append(&mut operations);
394 Ok(drive_operations)
395 }
396 DocumentOperationType::AddContestedDocument {
397 owned_document_info,
398 contested_document_resource_vote_poll,
399 contract_info,
400 document_type_info,
401 insert_without_check,
402 also_insert_vote_poll_stored_info,
403 } => {
404 let mut drive_operations: Vec<LowLevelDriveOperation> = vec![];
405 let contract_resolved_info = contract_info.resolve(
406 drive,
407 block_info,
408 transaction,
409 &mut drive_operations,
410 platform_version,
411 )?;
412 let contract = contract_resolved_info.as_ref();
413 let document_type = document_type_info.resolve(contract)?;
414
415 let document_and_contract_info = DocumentAndContractInfo {
416 owned_document_info,
417 contract,
418 document_type,
419 };
420 let mut operations = drive.add_contested_document_for_contract_operations(
421 document_and_contract_info,
422 contested_document_resource_vote_poll,
423 insert_without_check,
424 block_info,
425 also_insert_vote_poll_stored_info,
426 &mut None,
427 estimated_costs_only_with_layer_info,
428 transaction,
429 platform_version,
430 )?;
431 drive_operations.append(&mut operations);
432 Ok(drive_operations)
433 }
434 DocumentOperationType::AddWithdrawalDocument {
435 owned_document_info,
436 } => {
437 let contract = drive
438 .cache
439 .system_data_contracts
440 .load_withdrawals(platform_version)?;
441
442 let document_type = contract
443 .document_type_for_name(withdrawal::NAME)
444 .map_err(ProtocolError::DataContractError)?;
445
446 let document_and_contract_info = DocumentAndContractInfo {
447 owned_document_info,
448 contract: &contract,
449 document_type,
450 };
451 drive.add_document_for_contract_operations_without_ttl_drain(
452 document_and_contract_info,
453 false,
454 block_info,
455 &mut None,
456 estimated_costs_only_with_layer_info,
457 transaction,
458 platform_version,
459 )
460 }
461 DocumentOperationType::UpdateDocument {
462 owned_document_info,
463 contract_info,
464 document_type_info,
465 } => {
466 let mut drive_operations = vec![];
467 let contract_resolved_info = contract_info.resolve(
468 drive,
469 block_info,
470 transaction,
471 &mut drive_operations,
472 platform_version,
473 )?;
474 let contract = contract_resolved_info.as_ref();
475 let document_type = document_type_info.resolve(contract)?;
476
477 let document_and_contract_info = DocumentAndContractInfo {
478 owned_document_info,
479 contract,
480 document_type,
481 };
482 let mut operations = drive
483 .update_document_for_contract_operations_without_ttl_drain(
484 document_and_contract_info,
485 block_info,
486 &mut None,
487 estimated_costs_only_with_layer_info,
488 transaction,
489 platform_version,
490 )?;
491 drive_operations.append(&mut operations);
492 Ok(drive_operations)
493 }
494 DocumentOperationType::DocumentHistory {
495 source_data_contract_id,
496 source_document_type_name,
497 source_document_id,
498 owner_id,
499 nonce,
500 event,
501 } => {
502 let batch_operations = drive.add_document_history_operations(
503 source_data_contract_id,
504 source_document_type_name.as_str(),
505 source_document_id,
506 owner_id,
507 nonce,
508 event,
509 block_info,
510 estimated_costs_only_with_layer_info,
511 transaction,
512 platform_version,
513 )?;
514 Ok(batch_operations)
515 }
516 DocumentOperationType::AddDocumentAndDeleteConsumed {
517 owned_document_info,
518 contract_info,
519 document_type_info,
520 consumed_documents,
521 } => {
522 let mut drive_operations: Vec<LowLevelDriveOperation> = vec![];
523 let contract_resolved_info = contract_info.resolve(
524 drive,
525 block_info,
526 transaction,
527 &mut drive_operations,
528 platform_version,
529 )?;
530 let contract = contract_resolved_info.as_ref();
531 let document_type = document_type_info.resolve(contract)?;
532
533 let document_and_contract_info = DocumentAndContractInfo {
534 owned_document_info,
535 contract,
536 document_type,
537 };
538 let mut operations = drive.add_document_for_contract_operations_without_ttl_drain(
539 document_and_contract_info,
540 false,
541 block_info,
542 &mut None,
543 estimated_costs_only_with_layer_info,
544 transaction,
545 platform_version,
546 )?;
547 drive_operations.append(&mut operations);
548 for (document_id, document_type_name) in consumed_documents {
554 let consumed_document_type =
555 DocumentTypeInfo::DocumentTypeName(document_type_name).resolve(contract)?;
556 let mut operations = drive.force_delete_document_for_contract_operations(
557 document_id,
558 contract,
559 consumed_document_type,
560 Some(&mut drive_operations),
561 estimated_costs_only_with_layer_info,
562 block_info.time_ms,
563 transaction,
564 platform_version,
565 )?;
566 drive_operations.append(&mut operations);
567 }
568 Ok(drive_operations)
569 }
570 DocumentOperationType::DeleteDocument {
571 document_id,
572 contract_info,
573 document_type_info,
574 } => {
575 let mut drive_operations: Vec<LowLevelDriveOperation> = vec![];
576 let contract_resolved_info = contract_info.resolve(
577 drive,
578 block_info,
579 transaction,
580 &mut drive_operations,
581 platform_version,
582 )?;
583 let contract = contract_resolved_info.as_ref();
584 let document_type = document_type_info.resolve(contract)?;
585
586 drive.delete_document_for_contract_operations_without_ttl_drain(
587 document_id,
588 contract,
589 document_type,
590 None,
591 estimated_costs_only_with_layer_info,
592 block_info.time_ms,
593 transaction,
594 platform_version,
595 )
596 }
597 DocumentOperationType::ForceDeleteDocument {
598 document_id,
599 contract_info,
600 document_type_info,
601 } => {
602 let mut drive_operations: Vec<LowLevelDriveOperation> = vec![];
603 let contract_resolved_info = contract_info.resolve(
604 drive,
605 block_info,
606 transaction,
607 &mut drive_operations,
608 platform_version,
609 )?;
610 let contract = contract_resolved_info.as_ref();
611 let document_type = document_type_info.resolve(contract)?;
612
613 drive.force_delete_document_for_contract_operations(
616 document_id,
617 contract,
618 document_type,
619 None,
620 estimated_costs_only_with_layer_info,
621 block_info.time_ms,
622 transaction,
623 platform_version,
624 )
625 }
626 DocumentOperationType::DeleteIndexOnlyDocument {
627 document_id,
628 owner_id,
629 data,
630 contract_info,
631 document_type_info,
632 } => {
633 let mut drive_operations: Vec<LowLevelDriveOperation> = vec![];
634 let contract_resolved_info = contract_info.resolve(
635 drive,
636 block_info,
637 transaction,
638 &mut drive_operations,
639 platform_version,
640 )?;
641 let contract = contract_resolved_info.as_ref();
642 let document_type = document_type_info.resolve(contract)?;
643
644 let document = Drive::index_only_document_from_values(document_id, owner_id, data)?;
646
647 drive.delete_index_only_document_for_contract_operations_without_ttl_drain(
648 document,
649 contract,
650 document_type,
651 None,
652 estimated_costs_only_with_layer_info,
653 block_info.time_ms,
654 transaction,
655 platform_version,
656 )
657 }
658 DocumentOperationType::MultipleDocumentOperationsForSameContractDocumentType {
659 document_operations,
660 } => {
661 let DocumentOperationsForContractDocumentType {
662 operations,
663 contract,
664 document_type,
665 } = document_operations;
666
667 let mut drive_operations = vec![];
668 for document_operation in operations {
669 match document_operation {
670 DocumentOperation::AddOperation {
671 owned_document_info,
672 override_document,
673 } => {
674 let document_and_contract_info = DocumentAndContractInfo {
675 owned_document_info,
676 contract,
677 document_type,
678 };
679 let mut operations = drive
680 .add_document_for_contract_operations_without_ttl_drain(
681 document_and_contract_info,
682 override_document,
683 block_info,
684 &mut Some(&mut drive_operations),
685 estimated_costs_only_with_layer_info,
686 transaction,
687 platform_version,
688 )?;
689 drive_operations.append(&mut operations);
690 }
691 DocumentOperation::UpdateOperation(update_operation) => {
692 let UpdateOperationInfo {
693 document,
694 serialized_document,
695 owner_id,
696 storage_flags,
697 } = update_operation;
698
699 let document_info =
700 if let Some(serialized_document) = serialized_document {
701 DocumentRefAndSerialization((
702 document,
703 serialized_document,
704 storage_flags,
705 ))
706 } else {
707 DocumentRefInfo((document, storage_flags))
708 };
709 let document_and_contract_info = DocumentAndContractInfo {
710 owned_document_info: OwnedDocumentInfo {
711 document_info,
712 owner_id,
713 },
714 contract,
715 document_type,
716 };
717 let mut operations = drive
718 .update_document_for_contract_operations_without_ttl_drain(
719 document_and_contract_info,
720 block_info,
721 &mut Some(&mut drive_operations),
722 estimated_costs_only_with_layer_info,
723 transaction,
724 platform_version,
725 )?;
726 drive_operations.append(&mut operations);
727 }
728 }
729 }
730 Ok(drive_operations)
731 }
732 }
733 }
734}
735
736#[derive(Clone, Debug)]
738pub struct UpdateOperationInfo<'a> {
739 pub document: &'a Document,
741 pub serialized_document: Option<&'a [u8]>,
743 pub owner_id: Option<[u8; 32]>,
745 pub storage_flags: Option<Cow<'a, StorageFlags>>,
747}