Skip to main content

drive/util/batch/drive_op_batch/
mod.rs

1mod address_funds;
2mod contract;
3mod document;
4mod drive_methods;
5pub(crate) mod finalize_task;
6mod group;
7mod identity;
8mod prefunded_specialized_balance;
9mod shielded;
10mod system;
11mod token;
12mod withdrawals;
13
14use crate::util::batch::GroveDbOpBatch;
15
16use crate::drive::Drive;
17use crate::error::Error;
18use crate::fees::op::LowLevelDriveOperation;
19use dpp::block::block_info::BlockInfo;
20use dpp::fee::Credits;
21
22pub use address_funds::AddressFundsOperationType;
23pub use contract::DataContractOperationType;
24pub use document::DocumentOperation;
25pub use document::DocumentOperationType;
26pub use document::DocumentOperationsForContractDocumentType;
27pub use document::UpdateOperationInfo;
28pub use group::GroupOperationType;
29pub use identity::IdentityOperationType;
30pub use prefunded_specialized_balance::PrefundedSpecializedBalanceOperationType;
31pub use shielded::ShieldedPoolOperationType;
32pub use system::SystemOperationType;
33pub use token::TokenOperationType;
34pub use withdrawals::WithdrawalOperationType;
35
36use grovedb::{EstimatedLayerInformation, TransactionArg};
37
38use crate::fees::op::LowLevelDriveOperation::GroveOperation;
39
40use dpp::version::PlatformVersion;
41use grovedb::batch::{KeyInfoPath, QualifiedGroveDbOp};
42
43use crate::error::drive::DriveError;
44use crate::util::batch::drive_op_batch::finalize_task::{
45    DriveOperationFinalizationTasks, DriveOperationFinalizeTask,
46};
47
48use std::collections::{BTreeMap, HashMap};
49
50/// A converter that will get Drive Operations from High Level Operations
51pub trait DriveLowLevelOperationConverter {
52    /// This will get a list of atomic drive operations from a high level operations
53    fn into_low_level_drive_operations(
54        self,
55        drive: &Drive,
56        estimated_costs_only_with_layer_info: &mut Option<
57            HashMap<KeyInfoPath, EstimatedLayerInformation>,
58        >,
59        block_info: &BlockInfo,
60        transaction: TransactionArg,
61        platform_version: &PlatformVersion,
62    ) -> Result<Vec<LowLevelDriveOperation>, Error>;
63}
64
65/// The drive operation context keeps track of changes that might affect other operations
66/// Notably Identity balance changes are kept track of
67pub struct DriveOperationContext {
68    #[allow(dead_code)]
69    #[deprecated(note = "This function is marked as unused.")]
70    #[allow(deprecated)]
71    identity_balance_changes: BTreeMap<[u8; 32], i64>,
72}
73
74/// All types of Drive Operations
75#[allow(clippy::large_enum_variant)]
76#[derive(Clone, Debug)]
77pub enum DriveOperation<'a> {
78    /// A contract operation
79    DataContractOperation(DataContractOperationType<'a>),
80    /// A document operation
81    DocumentOperation(DocumentOperationType<'a>),
82    /// A token operation
83    TokenOperation(TokenOperationType),
84    /// Withdrawal operation
85    WithdrawalOperation(WithdrawalOperationType),
86    /// An identity operation
87    IdentityOperation(IdentityOperationType),
88    /// An operation on prefunded balances
89    PrefundedSpecializedBalanceOperation(PrefundedSpecializedBalanceOperationType),
90    /// A system operation
91    SystemOperation(SystemOperationType),
92    /// A group operation
93    GroupOperation(GroupOperationType),
94    /// An address funds operation
95    AddressFundsOperation(AddressFundsOperationType),
96    /// A shielded pool operation
97    ShieldedPoolOperation(ShieldedPoolOperationType),
98    /// A single low level groveDB operation
99    GroveDBOperation(QualifiedGroveDbOp),
100    /// Multiple low level groveDB operations
101    GroveDBOpBatch(GroveDbOpBatch),
102    /// An operation that only produces finalization tasks (no low-level ops)
103    FinalizeOperation(DriveOperationFinalizeTask),
104}
105
106impl DriveLowLevelOperationConverter for DriveOperation<'_> {
107    fn into_low_level_drive_operations(
108        self,
109        drive: &Drive,
110        estimated_costs_only_with_layer_info: &mut Option<
111            HashMap<KeyInfoPath, EstimatedLayerInformation>,
112        >,
113        block_info: &BlockInfo,
114        transaction: TransactionArg,
115        platform_version: &PlatformVersion,
116    ) -> Result<Vec<LowLevelDriveOperation>, Error> {
117        match self {
118            DriveOperation::DataContractOperation(contract_operation_type) => {
119                contract_operation_type.into_low_level_drive_operations(
120                    drive,
121                    estimated_costs_only_with_layer_info,
122                    block_info,
123                    transaction,
124                    platform_version,
125                )
126            }
127            DriveOperation::DocumentOperation(document_operation_type) => document_operation_type
128                .into_low_level_drive_operations(
129                    drive,
130                    estimated_costs_only_with_layer_info,
131                    block_info,
132                    transaction,
133                    platform_version,
134                ),
135            DriveOperation::WithdrawalOperation(withdrawal_operation_type) => {
136                withdrawal_operation_type.into_low_level_drive_operations(
137                    drive,
138                    estimated_costs_only_with_layer_info,
139                    block_info,
140                    transaction,
141                    platform_version,
142                )
143            }
144            DriveOperation::IdentityOperation(identity_operation_type) => identity_operation_type
145                .into_low_level_drive_operations(
146                    drive,
147                    estimated_costs_only_with_layer_info,
148                    block_info,
149                    transaction,
150                    platform_version,
151                ),
152            DriveOperation::PrefundedSpecializedBalanceOperation(
153                prefunded_balance_operation_type,
154            ) => prefunded_balance_operation_type.into_low_level_drive_operations(
155                drive,
156                estimated_costs_only_with_layer_info,
157                block_info,
158                transaction,
159                platform_version,
160            ),
161            DriveOperation::SystemOperation(system_operation_type) => system_operation_type
162                .into_low_level_drive_operations(
163                    drive,
164                    estimated_costs_only_with_layer_info,
165                    block_info,
166                    transaction,
167                    platform_version,
168                ),
169            DriveOperation::ShieldedPoolOperation(shielded_pool_operation_type) => {
170                shielded_pool_operation_type.into_low_level_drive_operations(
171                    drive,
172                    estimated_costs_only_with_layer_info,
173                    block_info,
174                    transaction,
175                    platform_version,
176                )
177            }
178            DriveOperation::GroveDBOperation(op) => Ok(vec![GroveOperation(op)]),
179            DriveOperation::GroveDBOpBatch(operations) => Ok(operations
180                .operations
181                .into_iter()
182                .map(GroveOperation)
183                .collect()),
184            DriveOperation::TokenOperation(token_operation_type) => token_operation_type
185                .into_low_level_drive_operations(
186                    drive,
187                    estimated_costs_only_with_layer_info,
188                    block_info,
189                    transaction,
190                    platform_version,
191                ),
192            DriveOperation::GroupOperation(group_operation_type) => group_operation_type
193                .into_low_level_drive_operations(
194                    drive,
195                    estimated_costs_only_with_layer_info,
196                    block_info,
197                    transaction,
198                    platform_version,
199                ),
200            DriveOperation::AddressFundsOperation(address_funds_operation_type) => {
201                address_funds_operation_type.into_low_level_drive_operations(
202                    drive,
203                    estimated_costs_only_with_layer_info,
204                    block_info,
205                    transaction,
206                    platform_version,
207                )
208            }
209            DriveOperation::FinalizeOperation(_) => Ok(vec![]),
210        }
211    }
212}
213
214impl DriveOperationFinalizationTasks for DriveOperation<'_> {
215    fn finalization_tasks(
216        &self,
217        platform_version: &PlatformVersion,
218    ) -> Result<Option<Vec<DriveOperationFinalizeTask>>, Error> {
219        match platform_version
220            .drive
221            .methods
222            .state_transitions
223            .operations
224            .finalization_tasks
225        {
226            0 => self.finalization_tasks_v0(platform_version),
227            version => Err(Error::Drive(DriveError::UnknownVersionMismatch {
228                method: "DriveOperation.finalization_tasks".to_string(),
229                known_versions: vec![0],
230                received: version,
231            })),
232        }
233    }
234}
235
236impl DriveOperation<'_> {
237    fn finalization_tasks_v0(
238        &self,
239        platform_version: &PlatformVersion,
240    ) -> Result<Option<Vec<DriveOperationFinalizeTask>>, Error> {
241        match self {
242            DriveOperation::DataContractOperation(o) => o.finalization_tasks(platform_version),
243            DriveOperation::FinalizeOperation(task) => Ok(Some(vec![task.clone()])),
244            _ => Ok(None),
245        }
246    }
247
248    /// Sums the credits the batch mints into Platform (its `AddToSystemCredits` operations,
249    /// saturating). This is the gross inflow of the batch — the net rule of the daily
250    /// withdrawal limit records it per block, and netting against removals here instead would
251    /// let a same-block deposit and withdrawal hide the inflow.
252    pub fn credit_mints(operations: &[DriveOperation]) -> Credits {
253        operations
254            .iter()
255            .filter_map(|operation| match operation {
256                DriveOperation::SystemOperation(SystemOperationType::AddToSystemCredits {
257                    amount,
258                }) => Some(*amount),
259                _ => None,
260            })
261            .fold(0u64, |total, amount| total.saturating_add(amount))
262    }
263}
264
265#[cfg(feature = "server")]
266#[cfg(test)]
267mod tests {
268    use grovedb::Element;
269    use std::borrow::Cow;
270    use std::option::Option::None;
271
272    use super::*;
273
274    use crate::util::test_helpers::setup_contract;
275    use dpp::block::block_info::BlockInfo;
276    use dpp::data_contract::accessors::v0::DataContractV0Getters;
277    use dpp::data_contract::DataContract;
278    use dpp::serialization::PlatformSerializableWithPlatformVersion;
279    use dpp::tests::json_document::{json_document_to_contract, json_document_to_document};
280    use dpp::util::cbor_serializer;
281    use rand::Rng;
282    use serde_json::json;
283
284    use crate::util::batch::drive_op_batch::document::DocumentOperation::{
285        AddOperation, UpdateOperation,
286    };
287    use crate::util::batch::drive_op_batch::document::DocumentOperationType::MultipleDocumentOperationsForSameContractDocumentType;
288    use crate::util::batch::drive_op_batch::document::{
289        DocumentOperationsForContractDocumentType, UpdateOperationInfo,
290    };
291    use crate::util::batch::DataContractOperationType::ApplyContract;
292    use crate::util::batch::DocumentOperationType::AddDocument;
293    use crate::util::batch::DriveOperation::{DataContractOperation, DocumentOperation};
294
295    use crate::drive::contract::paths::contract_root_path;
296    use crate::drive::Drive;
297    use crate::util::object_size_info::DocumentInfo::DocumentRefInfo;
298    use crate::util::object_size_info::{DataContractInfo, DocumentTypeInfo, OwnedDocumentInfo};
299    use crate::util::storage_flags::StorageFlags;
300    use crate::util::test_helpers::setup::setup_drive_with_initial_state_structure;
301
302    #[test]
303    fn test_add_dashpay_documents() {
304        let drive: Drive = setup_drive_with_initial_state_structure(None);
305        let platform_version = PlatformVersion::latest();
306
307        let mut drive_operations = vec![];
308        let db_transaction = drive.grove.start_transaction();
309
310        let contract = json_document_to_contract(
311            "tests/supporting_files/contract/dashpay/dashpay-contract-all-mutable.json",
312            false,
313            platform_version,
314        )
315        .expect("expected to get contract");
316
317        let _document_type = contract
318            .document_type_for_name("contactRequest")
319            .expect("expected to get document type");
320
321        drive_operations.push(DataContractOperation(ApplyContract {
322            contract: Cow::Borrowed(&contract),
323            storage_flags: None,
324        }));
325
326        let random_owner_id = rand::thread_rng().gen::<[u8; 32]>();
327
328        let document_type = contract
329            .document_type_for_name("contactRequest")
330            .expect("expected to get document type");
331
332        let dashpay_cr_document = json_document_to_document(
333            "tests/supporting_files/contract/dashpay/contact-request0.json",
334            Some(random_owner_id.into()),
335            document_type,
336            platform_version,
337        )
338        .expect("expected to get document");
339
340        drive_operations.push(DocumentOperation(AddDocument {
341            owned_document_info: OwnedDocumentInfo {
342                document_info: DocumentRefInfo((
343                    &dashpay_cr_document,
344                    StorageFlags::optional_default_as_cow(),
345                )),
346                owner_id: None,
347            },
348            contract_info: DataContractInfo::BorrowedDataContract(&contract),
349            document_type_info: DocumentTypeInfo::DocumentTypeRef(document_type),
350            override_document: false,
351        }));
352
353        drive
354            .apply_drive_operations(
355                drive_operations,
356                true,
357                &BlockInfo::default(),
358                Some(&db_transaction),
359                platform_version,
360                None,
361            )
362            .expect("expected to insert contract and document");
363
364        let element = drive
365            .grove
366            .get(
367                &contract_root_path(&contract.id().to_buffer()),
368                &[0],
369                Some(&db_transaction),
370                &platform_version.drive.grove_version,
371            )
372            .unwrap()
373            .expect("expected to get contract back");
374
375        assert_eq!(
376            element,
377            Element::Item(
378                contract
379                    .serialize_to_bytes_with_platform_version(platform_version)
380                    .expect("expected to serialize contract"),
381                None
382            )
383        );
384
385        let query_value = json!({
386            "where": [
387            ],
388            "limit": 100,
389            "orderBy": [
390                ["$ownerId", "asc"],
391            ]
392        });
393        let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
394            .expect("expected to serialize to cbor");
395
396        let (docs, _, _) = drive
397            .query_documents_cbor_from_contract(
398                &contract,
399                document_type,
400                where_cbor.as_slice(),
401                None,
402                Some(&db_transaction),
403                Some(platform_version.protocol_version),
404            )
405            .expect("expected to query");
406        assert_eq!(docs.len(), 1);
407    }
408
409    #[test]
410    fn test_add_multiple_dashpay_documents_individually_should_succeed() {
411        let drive = setup_drive_with_initial_state_structure(None);
412
413        let platform_version = PlatformVersion::latest();
414
415        let mut drive_operations = vec![];
416        let db_transaction = drive.grove.start_transaction();
417
418        let contract = json_document_to_contract(
419            "tests/supporting_files/contract/dashpay/dashpay-contract-all-mutable.json",
420            false,
421            platform_version,
422        )
423        .expect("expected to get contract");
424
425        let document_type = contract
426            .document_type_for_name("contactRequest")
427            .expect("expected to get document type");
428
429        drive_operations.push(DataContractOperation(ApplyContract {
430            contract: Cow::Borrowed(&contract),
431            storage_flags: None,
432        }));
433        let random_owner_id = rand::thread_rng().gen::<[u8; 32]>();
434
435        let dashpay_cr_document = json_document_to_document(
436            "tests/supporting_files/contract/dashpay/contact-request0.json",
437            Some(random_owner_id.into()),
438            document_type,
439            platform_version,
440        )
441        .expect("expected to get contract");
442
443        drive_operations.push(DocumentOperation(AddDocument {
444            owned_document_info: OwnedDocumentInfo {
445                document_info: DocumentRefInfo((&dashpay_cr_document, None)),
446                owner_id: None,
447            },
448            contract_info: DataContractInfo::BorrowedDataContract(&contract),
449            document_type_info: DocumentTypeInfo::DocumentTypeNameAsStr("contactRequest"),
450            override_document: false,
451        }));
452
453        let random_owner_id = rand::thread_rng().gen::<[u8; 32]>();
454
455        let dashpay_cr_1_document = json_document_to_document(
456            "tests/supporting_files/contract/dashpay/contact-request1.json",
457            Some(random_owner_id.into()),
458            document_type,
459            platform_version,
460        )
461        .expect("expected to get contract");
462
463        drive_operations.push(DocumentOperation(AddDocument {
464            owned_document_info: OwnedDocumentInfo {
465                document_info: DocumentRefInfo((&dashpay_cr_1_document, None)),
466                owner_id: None,
467            },
468            contract_info: DataContractInfo::BorrowedDataContract(&contract),
469            document_type_info: DocumentTypeInfo::DocumentTypeNameAsStr("contactRequest"),
470            override_document: false,
471        }));
472
473        drive
474            .apply_drive_operations(
475                drive_operations,
476                true,
477                &BlockInfo::default(),
478                Some(&db_transaction),
479                platform_version,
480                None,
481            )
482            .expect("expected to be able to insert documents");
483
484        let query_value = json!({
485            "where": [
486            ],
487            "limit": 100,
488            "orderBy": [
489                ["$ownerId", "asc"],
490            ]
491        });
492        let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
493            .expect("expected to serialize to cbor");
494
495        let (docs, _, _) = drive
496            .query_documents_cbor_from_contract(
497                &contract,
498                document_type,
499                where_cbor.as_slice(),
500                None,
501                Some(&db_transaction),
502                Some(platform_version.protocol_version),
503            )
504            .expect("expected to query");
505        assert_eq!(docs.len(), 2);
506    }
507
508    #[test]
509    fn test_add_multiple_dashpay_documents() {
510        let drive: Drive = setup_drive_with_initial_state_structure(None);
511
512        let platform_version = PlatformVersion::latest();
513
514        let mut drive_operations = vec![];
515        let db_transaction = drive.grove.start_transaction();
516
517        let contract = json_document_to_contract(
518            "tests/supporting_files/contract/dashpay/dashpay-contract-all-mutable.json",
519            false,
520            platform_version,
521        )
522        .expect("expected to get contract");
523
524        let document_type = contract
525            .document_type_for_name("contactRequest")
526            .expect("expected to get document type");
527
528        drive_operations.push(DataContractOperation(ApplyContract {
529            contract: Cow::Borrowed(&contract),
530            storage_flags: None,
531        }));
532
533        let random_owner_id = rand::thread_rng().gen::<[u8; 32]>();
534
535        let document0 = json_document_to_document(
536            "tests/supporting_files/contract/dashpay/contact-request0.json",
537            Some(random_owner_id.into()),
538            document_type,
539            platform_version,
540        )
541        .expect("expected to get document 0");
542
543        let document1 = json_document_to_document(
544            "tests/supporting_files/contract/dashpay/contact-request1.json",
545            Some(random_owner_id.into()),
546            document_type,
547            platform_version,
548        )
549        .expect("expected to get document 1");
550
551        let operations = vec![
552            AddOperation {
553                owned_document_info: OwnedDocumentInfo {
554                    document_info: DocumentRefInfo((
555                        &document0,
556                        StorageFlags::optional_default_as_cow(),
557                    )),
558                    owner_id: Some(random_owner_id),
559                },
560                override_document: false,
561            },
562            AddOperation {
563                owned_document_info: OwnedDocumentInfo {
564                    document_info: DocumentRefInfo((
565                        &document1,
566                        StorageFlags::optional_default_as_cow(),
567                    )),
568                    owner_id: Some(random_owner_id),
569                },
570                override_document: false,
571            },
572        ];
573
574        drive_operations.push(DocumentOperation(
575            MultipleDocumentOperationsForSameContractDocumentType {
576                document_operations: DocumentOperationsForContractDocumentType {
577                    operations,
578                    contract: &contract,
579                    document_type,
580                },
581            },
582        ));
583
584        drive
585            .apply_drive_operations(
586                drive_operations,
587                true,
588                &BlockInfo::default(),
589                Some(&db_transaction),
590                platform_version,
591                None,
592            )
593            .expect("expected to be able to insert documents");
594
595        let element = drive
596            .grove
597            .get(
598                &contract_root_path(&contract.id().to_buffer()),
599                &[0],
600                Some(&db_transaction),
601                &platform_version.drive.grove_version,
602            )
603            .unwrap()
604            .expect("expected to get contract back");
605
606        assert_eq!(
607            element,
608            Element::Item(
609                contract
610                    .serialize_to_bytes_with_platform_version(platform_version)
611                    .expect("expected to serialize contract"),
612                None
613            )
614        );
615
616        let query_value = json!({
617            "where": [
618            ],
619            "limit": 100,
620            "orderBy": [
621                ["$ownerId", "asc"],
622            ]
623        });
624        let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
625            .expect("expected to serialize to cbor");
626
627        let (docs, _, _) = drive
628            .query_documents_cbor_from_contract(
629                &contract,
630                document_type,
631                where_cbor.as_slice(),
632                None,
633                Some(&db_transaction),
634                Some(platform_version.protocol_version),
635            )
636            .expect("expected to query");
637        assert_eq!(docs.len(), 2);
638    }
639
640    #[test]
641    fn test_add_multiple_family_documents() {
642        let drive: Drive = setup_drive_with_initial_state_structure(None);
643
644        let platform_version = PlatformVersion::latest();
645
646        let mut drive_operations = vec![];
647        let db_transaction = drive.grove.start_transaction();
648
649        let contract = setup_contract(
650            &drive,
651            "tests/supporting_files/contract/family/family-contract.json",
652            None,
653            None,
654            None::<fn(&mut DataContract)>,
655            Some(&db_transaction),
656            None,
657        );
658
659        let document_type = contract
660            .document_type_for_name("person")
661            .expect("expected to get document type");
662
663        let random_owner_id0 = rand::thread_rng().gen::<[u8; 32]>();
664
665        let person_document0 = json_document_to_document(
666            "tests/supporting_files/contract/family/person0.json",
667            Some(random_owner_id0.into()),
668            document_type,
669            platform_version,
670        )
671        .expect("expected to get document");
672
673        let random_owner_id1 = rand::thread_rng().gen::<[u8; 32]>();
674
675        let person_document1 = json_document_to_document(
676            "tests/supporting_files/contract/family/person3.json",
677            Some(random_owner_id1.into()),
678            document_type,
679            platform_version,
680        )
681        .expect("expected to get document");
682
683        let mut operations = vec![];
684
685        operations.push(AddOperation {
686            owned_document_info: OwnedDocumentInfo {
687                document_info: DocumentRefInfo((
688                    &person_document0,
689                    StorageFlags::optional_default_as_cow(),
690                )),
691                owner_id: Some(random_owner_id0),
692            },
693            override_document: false,
694        });
695
696        let random_owner_id1 = rand::thread_rng().gen::<[u8; 32]>();
697
698        operations.push(AddOperation {
699            owned_document_info: OwnedDocumentInfo {
700                document_info: DocumentRefInfo((
701                    &person_document1,
702                    StorageFlags::optional_default_as_cow(),
703                )),
704                owner_id: Some(random_owner_id1),
705            },
706            override_document: false,
707        });
708
709        drive_operations.push(DocumentOperation(
710            MultipleDocumentOperationsForSameContractDocumentType {
711                document_operations: DocumentOperationsForContractDocumentType {
712                    operations,
713                    contract: &contract,
714                    document_type,
715                },
716            },
717        ));
718
719        drive
720            .apply_drive_operations(
721                drive_operations,
722                true,
723                &BlockInfo::default(),
724                Some(&db_transaction),
725                platform_version,
726                None,
727            )
728            .expect("expected to be able to insert documents");
729
730        let query_value = json!({
731            "where": [
732            ],
733            "limit": 100,
734            "orderBy": [
735                ["$ownerId", "asc"],
736            ]
737        });
738        let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
739            .expect("expected to serialize to cbor");
740
741        let (docs, _, _) = drive
742            .query_documents_cbor_from_contract(
743                &contract,
744                document_type,
745                where_cbor.as_slice(),
746                None,
747                Some(&db_transaction),
748                Some(platform_version.protocol_version),
749            )
750            .expect("expected to query");
751        assert_eq!(docs.len(), 2);
752    }
753
754    #[test]
755    fn test_update_multiple_family_documents() {
756        let drive: Drive = setup_drive_with_initial_state_structure(None);
757
758        let platform_version = PlatformVersion::latest();
759
760        let mut drive_operations = vec![];
761        let db_transaction = drive.grove.start_transaction();
762
763        let contract = setup_contract(
764            &drive,
765            "tests/supporting_files/contract/family/family-contract-only-age-index.json",
766            None,
767            None,
768            None::<fn(&mut DataContract)>,
769            Some(&db_transaction),
770            None,
771        );
772
773        let document_type = contract
774            .document_type_for_name("person")
775            .expect("expected to get document type");
776
777        let random_owner_id0 = rand::thread_rng().gen::<[u8; 32]>();
778
779        let person_document0 = json_document_to_document(
780            "tests/supporting_files/contract/family/person0.json",
781            Some(random_owner_id0.into()),
782            document_type,
783            platform_version,
784        )
785        .expect("expected to get document");
786
787        let random_owner_id1 = rand::thread_rng().gen::<[u8; 32]>();
788
789        let person_document1 = json_document_to_document(
790            "tests/supporting_files/contract/family/person3.json",
791            Some(random_owner_id1.into()),
792            document_type,
793            platform_version,
794        )
795        .expect("expected to get document");
796
797        let operations = vec![
798            AddOperation {
799                owned_document_info: OwnedDocumentInfo {
800                    document_info: DocumentRefInfo((
801                        &person_document0,
802                        StorageFlags::optional_default_as_cow(),
803                    )),
804                    owner_id: Some(random_owner_id0),
805                },
806                override_document: false,
807            },
808            AddOperation {
809                owned_document_info: OwnedDocumentInfo {
810                    document_info: DocumentRefInfo((
811                        &person_document1,
812                        StorageFlags::optional_default_as_cow(),
813                    )),
814                    owner_id: Some(random_owner_id1),
815                },
816                override_document: false,
817            },
818        ];
819
820        drive_operations.push(DocumentOperation(
821            MultipleDocumentOperationsForSameContractDocumentType {
822                document_operations: DocumentOperationsForContractDocumentType {
823                    operations,
824                    contract: &contract,
825                    document_type,
826                },
827            },
828        ));
829
830        drive
831            .apply_drive_operations(
832                drive_operations,
833                true,
834                &BlockInfo::default(),
835                Some(&db_transaction),
836                platform_version,
837                None,
838            )
839            .expect("expected to be able to insert documents");
840
841        // This was the setup now let's do the update
842
843        drive_operations = vec![];
844
845        let random_owner_id0 = rand::thread_rng().gen::<[u8; 32]>();
846
847        let person_document0 = json_document_to_document(
848            "tests/supporting_files/contract/family/person0-older.json",
849            Some(random_owner_id0.into()),
850            document_type,
851            platform_version,
852        )
853        .expect("expected to get document");
854
855        let random_owner_id1 = rand::thread_rng().gen::<[u8; 32]>();
856
857        let person_document1 = json_document_to_document(
858            "tests/supporting_files/contract/family/person3-older.json",
859            Some(random_owner_id1.into()),
860            document_type,
861            platform_version,
862        )
863        .expect("expected to get document");
864
865        let operations = vec![
866            UpdateOperation(UpdateOperationInfo {
867                document: &person_document0,
868                serialized_document: None,
869                owner_id: Some(random_owner_id0),
870                storage_flags: None,
871            }),
872            UpdateOperation(UpdateOperationInfo {
873                document: &person_document1,
874                serialized_document: None,
875                owner_id: Some(random_owner_id1),
876                storage_flags: None,
877            }),
878        ];
879
880        drive_operations.push(DocumentOperation(
881            MultipleDocumentOperationsForSameContractDocumentType {
882                document_operations: DocumentOperationsForContractDocumentType {
883                    operations,
884                    contract: &contract,
885                    document_type,
886                },
887            },
888        ));
889
890        drive
891            .apply_drive_operations(
892                drive_operations,
893                true,
894                &BlockInfo::default(),
895                Some(&db_transaction),
896                platform_version,
897                None,
898            )
899            .expect("expected to be able to update documents");
900
901        let query_value = json!({
902            "where": [
903            ],
904            "limit": 100,
905            "orderBy": [
906                ["age", "asc"],
907            ]
908        });
909        let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
910            .expect("expected to serialize to cbor");
911
912        let (docs, _, _) = drive
913            .query_documents_cbor_from_contract(
914                &contract,
915                document_type,
916                where_cbor.as_slice(),
917                None,
918                Some(&db_transaction),
919                Some(platform_version.protocol_version),
920            )
921            .expect("expected to query");
922        assert_eq!(docs.len(), 2);
923
924        let query_value = json!({
925            "where": [
926                ["age", "==", 35]
927            ],
928            "limit": 100,
929            "orderBy": [
930                ["age", "asc"],
931            ]
932        });
933        let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
934            .expect("expected to serialize to cbor");
935
936        let (docs, _, _) = drive
937            .query_documents_cbor_from_contract(
938                &contract,
939                document_type,
940                where_cbor.as_slice(),
941                None,
942                Some(&db_transaction),
943                Some(platform_version.protocol_version),
944            )
945            .expect("expected to query");
946        assert_eq!(docs.len(), 0);
947
948        let query_value = json!({
949            "where": [
950                ["age", "==", 36]
951            ],
952            "limit": 100,
953            "orderBy": [
954                ["age", "asc"],
955            ]
956        });
957        let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
958            .expect("expected to serialize to cbor");
959
960        let (docs, _, _) = drive
961            .query_documents_cbor_from_contract(
962                &contract,
963                document_type,
964                where_cbor.as_slice(),
965                None,
966                Some(&db_transaction),
967                Some(platform_version.protocol_version),
968            )
969            .expect("expected to query");
970        assert_eq!(docs.len(), 2);
971    }
972
973    #[test]
974    fn test_update_multiple_family_documents_with_index_being_removed_and_added() {
975        let drive: Drive = setup_drive_with_initial_state_structure(None);
976
977        let platform_version = PlatformVersion::latest();
978
979        let db_transaction = drive.grove.start_transaction();
980
981        let contract = setup_contract(
982            &drive,
983            "tests/supporting_files/contract/family/family-contract-only-age-index.json",
984            None,
985            None,
986            None::<fn(&mut DataContract)>,
987            Some(&db_transaction),
988            None,
989        );
990
991        let document_type = contract
992            .document_type_for_name("person")
993            .expect("expected to get document type");
994
995        let random_owner_id0 = rand::thread_rng().gen::<[u8; 32]>();
996
997        let person_document0 = json_document_to_document(
998            "tests/supporting_files/contract/family/person0.json",
999            Some(random_owner_id0.into()),
1000            document_type,
1001            platform_version,
1002        )
1003        .expect("expected to get document");
1004
1005        let random_owner_id1 = rand::thread_rng().gen::<[u8; 32]>();
1006
1007        let person_document1 = json_document_to_document(
1008            "tests/supporting_files/contract/family/person3-older.json",
1009            Some(random_owner_id1.into()),
1010            document_type,
1011            platform_version,
1012        )
1013        .expect("expected to get document");
1014
1015        let operations = vec![
1016            AddOperation {
1017                owned_document_info: OwnedDocumentInfo {
1018                    document_info: DocumentRefInfo((
1019                        &person_document0,
1020                        StorageFlags::optional_default_as_cow(),
1021                    )),
1022                    owner_id: Some(random_owner_id0),
1023                },
1024                override_document: false,
1025            },
1026            AddOperation {
1027                owned_document_info: OwnedDocumentInfo {
1028                    document_info: DocumentRefInfo((
1029                        &person_document1,
1030                        StorageFlags::optional_default_as_cow(),
1031                    )),
1032                    owner_id: Some(random_owner_id1),
1033                },
1034                override_document: false,
1035            },
1036        ];
1037        let drive_operations = vec![DocumentOperation(
1038            MultipleDocumentOperationsForSameContractDocumentType {
1039                document_operations: DocumentOperationsForContractDocumentType {
1040                    operations,
1041                    contract: &contract,
1042                    document_type,
1043                },
1044            },
1045        )];
1046
1047        drive
1048            .apply_drive_operations(
1049                drive_operations,
1050                true,
1051                &BlockInfo::default(),
1052                Some(&db_transaction),
1053                platform_version,
1054                None,
1055            )
1056            .expect("expected to be able to insert documents");
1057
1058        // This was the setup now let's do the update
1059
1060        let person_document0 = json_document_to_document(
1061            "tests/supporting_files/contract/family/person0-older.json",
1062            Some(random_owner_id0.into()),
1063            document_type,
1064            platform_version,
1065        )
1066        .expect("expected to get document");
1067
1068        let person_document1 = json_document_to_document(
1069            "tests/supporting_files/contract/family/person3.json",
1070            Some(random_owner_id1.into()),
1071            document_type,
1072            platform_version,
1073        )
1074        .expect("expected to get document");
1075
1076        let operations = vec![
1077            UpdateOperation(UpdateOperationInfo {
1078                document: &person_document0,
1079                serialized_document: None,
1080                owner_id: Some(random_owner_id0),
1081                storage_flags: None,
1082            }),
1083            UpdateOperation(UpdateOperationInfo {
1084                document: &person_document1,
1085                serialized_document: None,
1086                owner_id: Some(random_owner_id1),
1087                storage_flags: None,
1088            }),
1089        ];
1090
1091        let drive_operations = vec![DocumentOperation(
1092            MultipleDocumentOperationsForSameContractDocumentType {
1093                document_operations: DocumentOperationsForContractDocumentType {
1094                    operations,
1095                    contract: &contract,
1096                    document_type,
1097                },
1098            },
1099        )];
1100
1101        drive
1102            .apply_drive_operations(
1103                drive_operations,
1104                true,
1105                &BlockInfo::default(),
1106                Some(&db_transaction),
1107                platform_version,
1108                None,
1109            )
1110            .expect("expected to be able to update documents");
1111
1112        let query_value = json!({
1113            "where": [
1114                ["age", ">=", 5]
1115            ],
1116            "limit": 100,
1117            "orderBy": [
1118                ["age", "asc"],
1119            ]
1120        });
1121        let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
1122            .expect("expected to serialize to cbor");
1123
1124        let (docs, _, _) = drive
1125            .query_documents_cbor_from_contract(
1126                &contract,
1127                document_type,
1128                where_cbor.as_slice(),
1129                None,
1130                Some(&db_transaction),
1131                Some(platform_version.protocol_version),
1132            )
1133            .expect("expected to query");
1134        assert_eq!(docs.len(), 2);
1135
1136        let query_value = json!({
1137            "where": [
1138                ["age", "==", 35]
1139            ],
1140            "limit": 100,
1141            "orderBy": [
1142                ["age", "asc"],
1143            ]
1144        });
1145        let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
1146            .expect("expected to serialize to cbor");
1147
1148        let (docs, _, _) = drive
1149            .query_documents_cbor_from_contract(
1150                &contract,
1151                document_type,
1152                where_cbor.as_slice(),
1153                None,
1154                Some(&db_transaction),
1155                Some(platform_version.protocol_version),
1156            )
1157            .expect("expected to query");
1158        assert_eq!(docs.len(), 1);
1159
1160        let query_value = json!({
1161            "where": [
1162                ["age", "==", 36]
1163            ],
1164            "limit": 100,
1165            "orderBy": [
1166                ["age", "asc"],
1167            ]
1168        });
1169        let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
1170            .expect("expected to serialize to cbor");
1171
1172        let (docs, _, _) = drive
1173            .query_documents_cbor_from_contract(
1174                &contract,
1175                document_type,
1176                where_cbor.as_slice(),
1177                None,
1178                Some(&db_transaction),
1179                Some(platform_version.protocol_version),
1180            )
1181            .expect("expected to query");
1182        assert_eq!(docs.len(), 1);
1183    }
1184}