Skip to main content

drive/util/batch/drive_op_batch/
mod.rs

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