drive/query/
moderated_join.rs1use crate::drive::contract::moderation::types::{
22 ContractDocumentRemovalEntry, ContractDocumentRemovalsQuery, ContractDocumentRemovalsSelection,
23};
24use crate::drive::Drive;
25use crate::error::proof::ProofError;
26use crate::error::Error;
27#[cfg(feature = "server")]
28use crate::fees::op::LowLevelDriveOperation;
29#[cfg(feature = "server")]
30use crate::query::is_absent_path;
31use dpp::identifier::Identifier;
32#[cfg(feature = "server")]
33use dpp::version::PlatformVersion;
34#[cfg(feature = "server")]
35use grovedb::query_result_type::QueryResultType;
36#[cfg(feature = "server")]
37use grovedb::TransactionArg;
38use grovedb::{Element, PathQuery};
39use std::collections::BTreeMap;
40
41pub fn removals_path_query(
50 contract_id: Identifier,
51 document_type_name: &str,
52 document_ids: &[Identifier],
53 left_to_right: bool,
54) -> PathQuery {
55 let mut ids = document_ids.to_vec();
56 ids.sort();
57 ids.dedup();
58 let mut path_query = Drive::contract_document_removals_query(
59 contract_id.to_buffer(),
60 &ContractDocumentRemovalsQuery {
61 document_type_name: document_type_name.to_string(),
62 selection: ContractDocumentRemovalsSelection::DocumentIds(ids),
63 },
64 );
65 path_query.query.limit = None;
66 path_query.query.query.left_to_right = left_to_right;
67 path_query
68}
69
70pub fn decode_removals(
74 entries: impl IntoIterator<Item = (Vec<u8>, Element)>,
75) -> Result<BTreeMap<Identifier, ContractDocumentRemovalEntry>, Error> {
76 let mut removals = BTreeMap::new();
77 for (key, element) in entries {
78 let entry =
79 ContractDocumentRemovalEntry::from_key_element(&key, &element).map_err(|reason| {
80 Error::Proof(ProofError::CorruptedProof(format!(
81 "a joined document's removal record does not decode: {reason}"
82 )))
83 })?;
84 let document_id = entry.document_id;
85 if removals.insert(document_id, entry).is_some() {
86 return Err(Error::Proof(ProofError::CorruptedProof(format!(
87 "the removal record of joined document {document_id} is carried twice"
88 ))));
89 }
90 }
91 Ok(removals)
92}
93
94pub fn pair_missing_with_removals(
100 missing: &[Identifier],
101 removals: &BTreeMap<Identifier, ContractDocumentRemovalEntry>,
102) -> Result<Vec<ContractDocumentRemovalEntry>, Error> {
103 missing
104 .iter()
105 .map(|document_id| match removals.get(document_id) {
106 Some(entry) if !entry.removal.is_restored() => Ok(entry.clone()),
107 Some(_) => Err(Error::Proof(ProofError::CorruptedProof(format!(
108 "joined document {document_id} is missing although its removal record says a \
109 moderator restored it"
110 )))),
111 None => Err(Error::Proof(ProofError::CorruptedProof(format!(
112 "joined document {document_id} is missing without a removal record: a \
113 moderatedDocument reference resolves to its document or to the record of its \
114 removal"
115 )))),
116 })
117 .collect()
118}
119
120#[cfg(feature = "server")]
124pub(crate) fn fetch_removals(
125 drive: &Drive,
126 path_query: &PathQuery,
127 transaction: TransactionArg,
128 drive_operations: &mut Vec<LowLevelDriveOperation>,
129 platform_version: &PlatformVersion,
130) -> Result<BTreeMap<Identifier, ContractDocumentRemovalEntry>, Error> {
131 let results = match drive.grove_get_path_query(
132 path_query,
133 transaction,
134 QueryResultType::QueryKeyElementPairResultType,
135 drive_operations,
136 &platform_version.drive,
137 ) {
138 Err(error) if is_absent_path(&error) => {
139 return Ok(BTreeMap::new());
140 }
141 other => other?.0,
142 };
143 decode_removals(results.to_key_elements())
144}