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
49pub trait DriveLowLevelOperationConverter {
51 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
64pub 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#[allow(clippy::large_enum_variant)]
75#[derive(Clone, Debug)]
76pub enum DriveOperation<'a> {
77 DataContractOperation(DataContractOperationType<'a>),
79 DocumentOperation(DocumentOperationType<'a>),
81 TokenOperation(TokenOperationType),
83 WithdrawalOperation(WithdrawalOperationType),
85 IdentityOperation(IdentityOperationType),
87 PrefundedSpecializedBalanceOperation(PrefundedSpecializedBalanceOperationType),
89 SystemOperation(SystemOperationType),
91 GroupOperation(GroupOperationType),
93 AddressFundsOperation(AddressFundsOperationType),
95 ShieldedPoolOperation(ShieldedPoolOperationType),
97 GroveDBOperation(QualifiedGroveDbOp),
99 GroveDBOpBatch(GroveDbOpBatch),
101 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 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 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}