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
50pub trait DriveLowLevelOperationConverter {
52 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
65pub 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#[allow(clippy::large_enum_variant)]
76#[derive(Clone, Debug)]
77pub enum DriveOperation<'a> {
78 DataContractOperation(DataContractOperationType<'a>),
80 DocumentOperation(DocumentOperationType<'a>),
82 TokenOperation(TokenOperationType),
84 WithdrawalOperation(WithdrawalOperationType),
86 IdentityOperation(IdentityOperationType),
88 PrefundedSpecializedBalanceOperation(PrefundedSpecializedBalanceOperationType),
90 SystemOperation(SystemOperationType),
92 GroupOperation(GroupOperationType),
94 AddressFundsOperation(AddressFundsOperationType),
96 ShieldedPoolOperation(ShieldedPoolOperationType),
98 GroveDBOperation(QualifiedGroveDbOp),
100 GroveDBOpBatch(GroveDbOpBatch),
102 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 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 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 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}