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