1use crate::drive::Drive;
2use crate::error::Error;
3use crate::fees::op::LowLevelDriveOperation;
4use crate::util::batch::drive_op_batch::DriveLowLevelOperationConverter;
5use crate::util::object_size_info::DocumentInfo::{DocumentRefAndSerialization, DocumentRefInfo};
6use crate::util::object_size_info::{
7 DataContractInfo, DocumentAndContractInfo, DocumentTypeInfo, OwnedDocumentInfo,
8};
9use crate::util::storage_flags::StorageFlags;
10use dpp::block::block_info::BlockInfo;
11use dpp::data_contract::accessors::v0::DataContractV0Getters;
12use dpp::data_contract::document_type::DocumentTypeRef;
13use dpp::data_contract::DataContract;
14use dpp::document::document_event::DocumentEvent;
15use dpp::document::Document;
16use dpp::prelude::{Identifier, IdentityNonce};
17
18use dpp::system_data_contracts::withdrawals_contract::v1::document_types::withdrawal;
19
20use crate::drive::votes::resolved::vote_polls::contested_document_resource_vote_poll::ContestedDocumentResourceVotePollWithContractInfo;
21use dpp::platform_value::Value;
22use dpp::version::PlatformVersion;
23use dpp::voting::vote_info_storage::contested_document_vote_poll_stored_info::ContestedDocumentVotePollStoredInfo;
24use dpp::ProtocolError;
25use grovedb::batch::KeyInfoPath;
26use grovedb::{EstimatedLayerInformation, TransactionArg};
27use std::borrow::Cow;
28use std::collections::{BTreeMap, HashMap};
29
30#[derive(Clone, Debug)]
32#[allow(clippy::large_enum_variant)]
33pub enum DocumentOperation<'a> {
34 AddOperation {
36 owned_document_info: OwnedDocumentInfo<'a>,
38 override_document: bool,
40 },
41 UpdateOperation(UpdateOperationInfo<'a>),
43}
44
45#[derive(Clone, Debug)]
47pub struct DocumentOperationsForContractDocumentType<'a> {
48 pub operations: Vec<DocumentOperation<'a>>,
50 pub contract: &'a DataContract,
52 pub document_type: DocumentTypeRef<'a>,
54}
55
56#[derive(Clone, Debug)]
58#[allow(clippy::large_enum_variant)]
59pub enum DocumentOperationType<'a> {
60 AddDocument {
62 owned_document_info: OwnedDocumentInfo<'a>,
64 contract_info: DataContractInfo<'a>,
66 document_type_info: DocumentTypeInfo<'a>,
68 override_document: bool,
70 },
71 AddContestedDocument {
75 owned_document_info: OwnedDocumentInfo<'a>,
77 contested_document_resource_vote_poll: ContestedDocumentResourceVotePollWithContractInfo,
79 contract_info: DataContractInfo<'a>,
81 document_type_info: DocumentTypeInfo<'a>,
83 insert_without_check: bool,
85 also_insert_vote_poll_stored_info: Option<ContestedDocumentVotePollStoredInfo>,
87 },
88 UpdateDocument {
90 owned_document_info: OwnedDocumentInfo<'a>,
92 contract_info: DataContractInfo<'a>,
94 document_type_info: DocumentTypeInfo<'a>,
96 },
97 DeleteDocument {
99 document_id: Identifier,
101 contract_info: DataContractInfo<'a>,
103 document_type_info: DocumentTypeInfo<'a>,
105 },
106 DeleteIndexOnlyDocument {
111 document_id: Identifier,
113 owner_id: Identifier,
115 data: BTreeMap<String, Value>,
117 contract_info: DataContractInfo<'a>,
119 document_type_info: DocumentTypeInfo<'a>,
121 },
122 AddWithdrawalDocument {
124 owned_document_info: OwnedDocumentInfo<'a>,
126 },
127 MultipleDocumentOperationsForSameContractDocumentType {
129 document_operations: DocumentOperationsForContractDocumentType<'a>,
131 },
132 DocumentHistory {
136 source_data_contract_id: Identifier,
138 source_document_type_name: String,
140 source_document_id: Identifier,
142 owner_id: Identifier,
144 nonce: IdentityNonce,
146 event: DocumentEvent,
148 },
149}
150
151impl DriveLowLevelOperationConverter for DocumentOperationType<'_> {
152 fn into_low_level_drive_operations(
153 self,
154 drive: &Drive,
155 estimated_costs_only_with_layer_info: &mut Option<
156 HashMap<KeyInfoPath, EstimatedLayerInformation>,
157 >,
158 block_info: &BlockInfo,
159 transaction: TransactionArg,
160 platform_version: &PlatformVersion,
161 ) -> Result<Vec<LowLevelDriveOperation>, Error> {
162 if estimated_costs_only_with_layer_info.is_none() {
163 self.prepare_time_range_ttl(drive, block_info, transaction, platform_version)?;
164 }
165 self.into_low_level_drive_operations_after_ttl_drain(
166 drive,
167 estimated_costs_only_with_layer_info,
168 block_info,
169 transaction,
170 platform_version,
171 )
172 }
173}
174
175impl DocumentOperationType<'_> {
176 pub(crate) fn prepare_time_range_ttl(
178 &self,
179 drive: &Drive,
180 block_info: &BlockInfo,
181 transaction: TransactionArg,
182 platform_version: &PlatformVersion,
183 ) -> Result<(), Error> {
184 if platform_version
185 .system_limits
186 .min_time_range_ttl_drop_operations_per_write
187 .is_none()
188 {
189 return Ok(());
190 }
191 match self {
192 Self::AddDocument {
193 contract_info,
194 document_type_info,
195 ..
196 }
197 | Self::AddContestedDocument {
198 contract_info,
199 document_type_info,
200 ..
201 }
202 | Self::UpdateDocument {
203 contract_info,
204 document_type_info,
205 ..
206 }
207 | Self::DeleteDocument {
208 contract_info,
209 document_type_info,
210 ..
211 }
212 | Self::DeleteIndexOnlyDocument {
213 contract_info,
214 document_type_info,
215 ..
216 } => {
217 let resolved = contract_info.clone().resolve(
220 drive,
221 block_info,
222 transaction,
223 &mut vec![],
224 platform_version,
225 )?;
226 let contract = resolved.as_ref();
227 let document_type = document_type_info.clone().resolve(contract)?;
228 drive.prepare_document_time_range_ttl(
229 contract,
230 document_type,
231 block_info.time_ms,
232 transaction,
233 platform_version,
234 )
235 }
236 Self::MultipleDocumentOperationsForSameContractDocumentType {
237 document_operations,
238 } => {
239 for _ in &document_operations.operations {
242 drive.prepare_document_time_range_ttl(
243 document_operations.contract,
244 document_operations.document_type,
245 block_info.time_ms,
246 transaction,
247 platform_version,
248 )?;
249 }
250 Ok(())
251 }
252 Self::AddWithdrawalDocument { .. } | Self::DocumentHistory { .. } => Ok(()),
254 }
255 }
256
257 pub(crate) fn into_low_level_drive_operations_after_ttl_drain(
258 self,
259 drive: &Drive,
260 estimated_costs_only_with_layer_info: &mut Option<
261 HashMap<KeyInfoPath, EstimatedLayerInformation>,
262 >,
263 block_info: &BlockInfo,
264 transaction: TransactionArg,
265 platform_version: &PlatformVersion,
266 ) -> Result<Vec<LowLevelDriveOperation>, Error> {
267 match self {
268 DocumentOperationType::AddDocument {
269 owned_document_info,
270 contract_info,
271 document_type_info,
272 override_document,
273 } => {
274 let mut drive_operations: Vec<LowLevelDriveOperation> = vec![];
275 let contract_resolved_info = contract_info.resolve(
276 drive,
277 block_info,
278 transaction,
279 &mut drive_operations,
280 platform_version,
281 )?;
282 let contract = contract_resolved_info.as_ref();
283 let document_type = document_type_info.resolve(contract)?;
284
285 let document_and_contract_info = DocumentAndContractInfo {
286 owned_document_info,
287 contract,
288 document_type,
289 };
290 let mut operations = drive.add_document_for_contract_operations_without_ttl_drain(
291 document_and_contract_info,
292 override_document,
293 block_info,
294 &mut None,
295 estimated_costs_only_with_layer_info,
296 transaction,
297 platform_version,
298 )?;
299 drive_operations.append(&mut operations);
300 Ok(drive_operations)
301 }
302 DocumentOperationType::AddContestedDocument {
303 owned_document_info,
304 contested_document_resource_vote_poll,
305 contract_info,
306 document_type_info,
307 insert_without_check,
308 also_insert_vote_poll_stored_info,
309 } => {
310 let mut drive_operations: Vec<LowLevelDriveOperation> = vec![];
311 let contract_resolved_info = contract_info.resolve(
312 drive,
313 block_info,
314 transaction,
315 &mut drive_operations,
316 platform_version,
317 )?;
318 let contract = contract_resolved_info.as_ref();
319 let document_type = document_type_info.resolve(contract)?;
320
321 let document_and_contract_info = DocumentAndContractInfo {
322 owned_document_info,
323 contract,
324 document_type,
325 };
326 let mut operations = drive.add_contested_document_for_contract_operations(
327 document_and_contract_info,
328 contested_document_resource_vote_poll,
329 insert_without_check,
330 block_info,
331 also_insert_vote_poll_stored_info,
332 &mut None,
333 estimated_costs_only_with_layer_info,
334 transaction,
335 platform_version,
336 )?;
337 drive_operations.append(&mut operations);
338 Ok(drive_operations)
339 }
340 DocumentOperationType::AddWithdrawalDocument {
341 owned_document_info,
342 } => {
343 let contract = drive
344 .cache
345 .system_data_contracts
346 .load_withdrawals(platform_version)?;
347
348 let document_type = contract
349 .document_type_for_name(withdrawal::NAME)
350 .map_err(ProtocolError::DataContractError)?;
351
352 let document_and_contract_info = DocumentAndContractInfo {
353 owned_document_info,
354 contract: &contract,
355 document_type,
356 };
357 drive.add_document_for_contract_operations_without_ttl_drain(
358 document_and_contract_info,
359 false,
360 block_info,
361 &mut None,
362 estimated_costs_only_with_layer_info,
363 transaction,
364 platform_version,
365 )
366 }
367 DocumentOperationType::UpdateDocument {
368 owned_document_info,
369 contract_info,
370 document_type_info,
371 } => {
372 let mut drive_operations = vec![];
373 let contract_resolved_info = contract_info.resolve(
374 drive,
375 block_info,
376 transaction,
377 &mut drive_operations,
378 platform_version,
379 )?;
380 let contract = contract_resolved_info.as_ref();
381 let document_type = document_type_info.resolve(contract)?;
382
383 let document_and_contract_info = DocumentAndContractInfo {
384 owned_document_info,
385 contract,
386 document_type,
387 };
388 let mut operations = drive
389 .update_document_for_contract_operations_without_ttl_drain(
390 document_and_contract_info,
391 block_info,
392 &mut None,
393 estimated_costs_only_with_layer_info,
394 transaction,
395 platform_version,
396 )?;
397 drive_operations.append(&mut operations);
398 Ok(drive_operations)
399 }
400 DocumentOperationType::DocumentHistory {
401 source_data_contract_id,
402 source_document_type_name,
403 source_document_id,
404 owner_id,
405 nonce,
406 event,
407 } => {
408 let batch_operations = drive.add_document_history_operations(
409 source_data_contract_id,
410 source_document_type_name.as_str(),
411 source_document_id,
412 owner_id,
413 nonce,
414 event,
415 block_info,
416 estimated_costs_only_with_layer_info,
417 transaction,
418 platform_version,
419 )?;
420 Ok(batch_operations)
421 }
422 DocumentOperationType::DeleteDocument {
423 document_id,
424 contract_info,
425 document_type_info,
426 } => {
427 let mut drive_operations: Vec<LowLevelDriveOperation> = vec![];
428 let contract_resolved_info = contract_info.resolve(
429 drive,
430 block_info,
431 transaction,
432 &mut drive_operations,
433 platform_version,
434 )?;
435 let contract = contract_resolved_info.as_ref();
436 let document_type = document_type_info.resolve(contract)?;
437
438 drive.delete_document_for_contract_operations_without_ttl_drain(
439 document_id,
440 contract,
441 document_type,
442 None,
443 estimated_costs_only_with_layer_info,
444 block_info.time_ms,
445 transaction,
446 platform_version,
447 )
448 }
449 DocumentOperationType::DeleteIndexOnlyDocument {
450 document_id,
451 owner_id,
452 data,
453 contract_info,
454 document_type_info,
455 } => {
456 let mut drive_operations: Vec<LowLevelDriveOperation> = vec![];
457 let contract_resolved_info = contract_info.resolve(
458 drive,
459 block_info,
460 transaction,
461 &mut drive_operations,
462 platform_version,
463 )?;
464 let contract = contract_resolved_info.as_ref();
465 let document_type = document_type_info.resolve(contract)?;
466
467 let document = Drive::index_only_document_from_values(document_id, owner_id, data)?;
469
470 drive.delete_index_only_document_for_contract_operations_without_ttl_drain(
471 document,
472 contract,
473 document_type,
474 None,
475 estimated_costs_only_with_layer_info,
476 block_info.time_ms,
477 transaction,
478 platform_version,
479 )
480 }
481 DocumentOperationType::MultipleDocumentOperationsForSameContractDocumentType {
482 document_operations,
483 } => {
484 let DocumentOperationsForContractDocumentType {
485 operations,
486 contract,
487 document_type,
488 } = document_operations;
489
490 let mut drive_operations = vec![];
491 for document_operation in operations {
492 match document_operation {
493 DocumentOperation::AddOperation {
494 owned_document_info,
495 override_document,
496 } => {
497 let document_and_contract_info = DocumentAndContractInfo {
498 owned_document_info,
499 contract,
500 document_type,
501 };
502 let mut operations = drive
503 .add_document_for_contract_operations_without_ttl_drain(
504 document_and_contract_info,
505 override_document,
506 block_info,
507 &mut Some(&mut drive_operations),
508 estimated_costs_only_with_layer_info,
509 transaction,
510 platform_version,
511 )?;
512 drive_operations.append(&mut operations);
513 }
514 DocumentOperation::UpdateOperation(update_operation) => {
515 let UpdateOperationInfo {
516 document,
517 serialized_document,
518 owner_id,
519 storage_flags,
520 } = update_operation;
521
522 let document_info =
523 if let Some(serialized_document) = serialized_document {
524 DocumentRefAndSerialization((
525 document,
526 serialized_document,
527 storage_flags,
528 ))
529 } else {
530 DocumentRefInfo((document, storage_flags))
531 };
532 let document_and_contract_info = DocumentAndContractInfo {
533 owned_document_info: OwnedDocumentInfo {
534 document_info,
535 owner_id,
536 },
537 contract,
538 document_type,
539 };
540 let mut operations = drive
541 .update_document_for_contract_operations_without_ttl_drain(
542 document_and_contract_info,
543 block_info,
544 &mut Some(&mut drive_operations),
545 estimated_costs_only_with_layer_info,
546 transaction,
547 platform_version,
548 )?;
549 drive_operations.append(&mut operations);
550 }
551 }
552 }
553 Ok(drive_operations)
554 }
555 }
556 }
557}
558
559#[derive(Clone, Debug)]
561pub struct UpdateOperationInfo<'a> {
562 pub document: &'a Document,
564 pub serialized_document: Option<&'a [u8]>,
566 pub owner_id: Option<[u8; 32]>,
568 pub storage_flags: Option<Cow<'a, StorageFlags>>,
570}