1#[cfg(feature = "verify")]
2use super::ContractLookupFn;
3use crate::drive::votes::paths::VotePollPaths;
4#[cfg(any(feature = "server", feature = "verify"))]
5use crate::drive::votes::resolved::vote_polls::contested_document_resource_vote_poll::resolve::ContestedDocumentResourceVotePollResolver;
6use crate::drive::votes::resolved::vote_polls::contested_document_resource_vote_poll::ContestedDocumentResourceVotePollWithContractInfoAllowBorrowed;
7#[cfg(feature = "server")]
8use crate::drive::Drive;
9use crate::error::Error;
10#[cfg(feature = "server")]
11use crate::fees::op::LowLevelDriveOperation;
12#[cfg(feature = "server")]
13use crate::query::GroveError;
14use crate::query::Query;
15use bincode::{Decode, Encode};
16#[cfg(feature = "server")]
17use dpp::block::block_info::BlockInfo;
18use dpp::identifier::Identifier;
19#[cfg(feature = "server")]
20use dpp::platform_value;
21use dpp::voting::vote_choices::resource_vote_choice::ResourceVoteChoice::TowardsIdentity;
22use dpp::voting::vote_polls::contested_document_resource_vote_poll::ContestedDocumentResourceVotePoll;
23#[cfg(feature = "server")]
24use grovedb::query_result_type::{QueryResultElements, QueryResultType};
25#[cfg(feature = "server")]
26use grovedb::TransactionArg;
27use grovedb::{PathQuery, SizedQuery};
28use platform_version::version::PlatformVersion;
29
30#[derive(Debug, PartialEq, Clone, Encode, Decode)]
32pub struct ContestedDocumentVotePollVotesDriveQuery {
33 pub vote_poll: ContestedDocumentResourceVotePoll,
35 pub contestant_id: Identifier,
37 pub offset: Option<u16>,
39 pub limit: Option<u16>,
41 pub start_at: Option<([u8; 32], bool)>,
43 pub order_ascending: bool,
45}
46
47impl ContestedDocumentVotePollVotesDriveQuery {
48 #[cfg(feature = "server")]
49 pub fn resolve(
70 &self,
71 drive: &Drive,
72 transaction: TransactionArg,
73 platform_version: &PlatformVersion,
74 ) -> Result<ResolvedContestedDocumentVotePollVotesDriveQuery<'_>, Error> {
75 let ContestedDocumentVotePollVotesDriveQuery {
76 vote_poll,
77 contestant_id,
78 offset,
79 limit,
80 start_at,
81 order_ascending,
82 } = self;
83 Ok(ResolvedContestedDocumentVotePollVotesDriveQuery {
84 vote_poll: vote_poll.resolve_allow_borrowed(drive, transaction, platform_version)?,
85 contestant_id: *contestant_id,
86 offset: *offset,
87 limit: *limit,
88 start_at: *start_at,
89 order_ascending: *order_ascending,
90 })
91 }
92
93 #[cfg(feature = "verify")]
97 pub fn resolve_with_known_contracts_provider<'a>(
98 &self,
99 known_contracts_provider: &ContractLookupFn,
100 ) -> Result<ResolvedContestedDocumentVotePollVotesDriveQuery<'a>, Error> {
101 let ContestedDocumentVotePollVotesDriveQuery {
102 vote_poll,
103 contestant_id,
104 offset,
105 limit,
106 start_at,
107 order_ascending,
108 } = self;
109 Ok(ResolvedContestedDocumentVotePollVotesDriveQuery {
110 vote_poll: vote_poll.resolve_with_known_contracts_provider(known_contracts_provider)?,
111 contestant_id: *contestant_id,
112 offset: *offset,
113 limit: *limit,
114 start_at: *start_at,
115 order_ascending: *order_ascending,
116 })
117 }
118
119 #[cfg(feature = "server")]
120 pub fn execute_with_proof(
122 self,
123 drive: &Drive,
124 block_info: Option<BlockInfo>,
125 transaction: TransactionArg,
126 platform_version: &PlatformVersion,
127 ) -> Result<(Vec<u8>, u64), Error> {
128 let mut drive_operations = vec![];
129 let items = self.execute_with_proof_internal(
130 drive,
131 transaction,
132 &mut drive_operations,
133 platform_version,
134 )?;
135 let cost = if let Some(block_info) = block_info {
136 let fee_result = Drive::calculate_fee(
137 None,
138 Some(drive_operations),
139 &block_info.epoch,
140 drive.config.epochs_per_era,
141 platform_version,
142 None,
143 )?;
144 fee_result.processing_fee
145 } else {
146 0
147 };
148 Ok((items, cost))
149 }
150
151 #[cfg(feature = "server")]
152 pub(crate) fn execute_with_proof_internal(
154 self,
155 drive: &Drive,
156 transaction: TransactionArg,
157 drive_operations: &mut Vec<LowLevelDriveOperation>,
158 platform_version: &PlatformVersion,
159 ) -> Result<Vec<u8>, Error> {
160 let resolved = self.resolve(drive, transaction, platform_version)?;
161 let path_query = resolved.construct_path_query(platform_version)?;
162 drive.grove_get_proved_path_query(
163 &path_query,
164 transaction,
165 drive_operations,
166 &platform_version.drive,
167 )
168 }
169
170 #[cfg(feature = "server")]
171 pub fn execute_no_proof_with_cost(
173 &self,
174 drive: &Drive,
175 block_info: Option<BlockInfo>,
176 transaction: TransactionArg,
177 platform_version: &PlatformVersion,
178 ) -> Result<(Vec<Identifier>, u64), Error> {
179 let mut drive_operations = vec![];
180 let result =
181 self.execute_no_proof(drive, transaction, &mut drive_operations, platform_version)?;
182 let cost = if let Some(block_info) = block_info {
183 let fee_result = Drive::calculate_fee(
184 None,
185 Some(drive_operations),
186 &block_info.epoch,
187 drive.config.epochs_per_era,
188 platform_version,
189 None,
190 )?;
191 fee_result.processing_fee
192 } else {
193 0
194 };
195 Ok((result, cost))
196 }
197
198 #[cfg(feature = "server")]
199 pub fn execute_no_proof(
201 &self,
202 drive: &Drive,
203 transaction: TransactionArg,
204 drive_operations: &mut Vec<LowLevelDriveOperation>,
205 platform_version: &PlatformVersion,
206 ) -> Result<Vec<Identifier>, Error> {
207 let resolved = self.resolve(drive, transaction, platform_version)?;
208 let path_query = resolved.construct_path_query(platform_version)?;
209 let query_result = drive.grove_get_path_query(
210 &path_query,
211 transaction,
212 QueryResultType::QueryPathKeyElementTrioResultType,
213 drive_operations,
214 &platform_version.drive,
215 );
216 match query_result {
217 Err(Error::GroveDB(e))
218 if matches!(
219 e.as_ref(),
220 GroveError::PathKeyNotFound(_)
221 | GroveError::PathNotFound(_)
222 | GroveError::PathParentLayerNotFound(_)
223 ) =>
224 {
225 Ok(vec![])
226 }
227 Err(e) => Err(e),
228 Ok((query_result_elements, _skipped)) => {
229 let voters = query_result_elements
230 .to_keys()
231 .into_iter()
232 .map(Identifier::try_from)
233 .collect::<Result<Vec<Identifier>, platform_value::Error>>()?;
234
235 Ok(voters)
236 }
237 }
238 }
239
240 #[cfg(feature = "server")]
241 #[allow(unused)]
242 pub(crate) fn execute_no_proof_internal(
244 &self,
245 drive: &Drive,
246 result_type: QueryResultType,
247 transaction: TransactionArg,
248 drive_operations: &mut Vec<LowLevelDriveOperation>,
249 platform_version: &PlatformVersion,
250 ) -> Result<(QueryResultElements, u16), Error> {
251 let resolved = self.resolve(drive, transaction, platform_version)?;
252 let path_query = resolved.construct_path_query(platform_version)?;
253 let query_result = drive.grove_get_path_query(
254 &path_query,
255 transaction,
256 result_type,
257 drive_operations,
258 &platform_version.drive,
259 );
260 match query_result {
261 Err(Error::GroveDB(e))
262 if matches!(
263 e.as_ref(),
264 GroveError::PathKeyNotFound(_)
265 | GroveError::PathNotFound(_)
266 | GroveError::PathParentLayerNotFound(_)
267 ) =>
268 {
269 Ok((QueryResultElements::new(), 0))
270 }
271 _ => {
272 let (data, skipped) = query_result?;
273 {
274 Ok((data, skipped))
275 }
276 }
277 }
278 }
279}
280#[derive(Debug, PartialEq, Clone)]
282pub struct ResolvedContestedDocumentVotePollVotesDriveQuery<'a> {
283 pub vote_poll: ContestedDocumentResourceVotePollWithContractInfoAllowBorrowed<'a>,
285 pub contestant_id: Identifier,
287 pub offset: Option<u16>,
289 pub limit: Option<u16>,
291 pub start_at: Option<([u8; 32], bool)>,
293 pub order_ascending: bool,
295}
296
297impl ResolvedContestedDocumentVotePollVotesDriveQuery<'_> {
298 pub fn construct_path_query(
300 &self,
301 platform_version: &PlatformVersion,
302 ) -> Result<PathQuery, Error> {
303 let path = self
304 .vote_poll
305 .contender_voting_path(&TowardsIdentity(self.contestant_id), platform_version)?;
306
307 let mut query = Query::new_with_direction(self.order_ascending);
308
309 match &self.start_at {
311 None => {
312 query.insert_all();
313 }
314 Some((starts_at_key_bytes, start_at_included)) => {
315 let starts_at_key = starts_at_key_bytes.to_vec();
316 match self.order_ascending {
317 true => match start_at_included {
318 true => query.insert_range_from(starts_at_key..),
319 false => query.insert_range_after(starts_at_key..),
320 },
321 false => match start_at_included {
322 true => query.insert_range_to_inclusive(..=starts_at_key),
323 false => query.insert_range_to(..starts_at_key),
324 },
325 }
326 }
327 }
328
329 Ok(PathQuery {
330 path,
331 query: SizedQuery {
332 query,
333 limit: self.limit,
334 offset: self.offset,
335 },
336 })
337 }
338}
339
340#[cfg(test)]
341mod tests {
342 use super::*;
343 use crate::drive::votes::resolved::vote_polls::contested_document_resource_vote_poll::ContestedDocumentResourceVotePollWithContractInfoAllowBorrowed;
344 use crate::util::object_size_info::DataContractResolvedInfo;
345 use dpp::tests::fixtures::get_dpns_data_contract_fixture;
346 use dpp::version::PlatformVersion;
347 use grovedb::QueryItem;
348
349 fn build_resolved_query(
352 contract: &dpp::data_contract::DataContract,
353 contestant_id: Identifier,
354 offset: Option<u16>,
355 limit: Option<u16>,
356 start_at: Option<([u8; 32], bool)>,
357 order_ascending: bool,
358 ) -> ResolvedContestedDocumentVotePollVotesDriveQuery<'_> {
359 let document_type_name = "domain".to_string();
360 let index_name = "parentNameAndLabel".to_string();
361
362 let parent_domain_value = dpp::platform_value::Value::Text("dash".to_string());
363 let label_value = dpp::platform_value::Value::Text("test-name".to_string());
364
365 let index_values = vec![parent_domain_value, label_value];
366
367 let vote_poll = ContestedDocumentResourceVotePollWithContractInfoAllowBorrowed {
368 contract: DataContractResolvedInfo::BorrowedDataContract(contract),
369 document_type_name,
370 index_name,
371 index_values,
372 };
373
374 ResolvedContestedDocumentVotePollVotesDriveQuery {
375 vote_poll,
376 contestant_id,
377 offset,
378 limit,
379 start_at,
380 order_ascending,
381 }
382 }
383
384 #[test]
389 fn construct_path_query_no_start_ascending() {
390 let platform_version = PlatformVersion::latest();
391 let dpns = get_dpns_data_contract_fixture(None, 0, platform_version.protocol_version);
392 let contract = dpns.data_contract_owned();
393
394 let contestant_id = Identifier::from([0xAA; 32]);
395 let query = build_resolved_query(
396 &contract,
397 contestant_id,
398 None, Some(10), None, true, );
403
404 let pq = query
405 .construct_path_query(platform_version)
406 .expect("should build path query");
407
408 assert!(!pq.path.is_empty());
410 assert_eq!(pq.query.limit, Some(10));
411 assert_eq!(pq.query.offset, None);
412 assert!(pq.query.query.left_to_right);
413
414 let items = &pq.query.query.items;
416 assert_eq!(items.len(), 1);
417 assert!(matches!(&items[0], QueryItem::RangeFull(..)));
418 }
419
420 #[test]
421 fn construct_path_query_no_start_descending() {
422 let platform_version = PlatformVersion::latest();
423 let dpns = get_dpns_data_contract_fixture(None, 0, platform_version.protocol_version);
424 let contract = dpns.data_contract_owned();
425
426 let contestant_id = Identifier::from([0xBB; 32]);
427 let query = build_resolved_query(&contract, contestant_id, None, None, None, false);
428
429 let pq = query
430 .construct_path_query(platform_version)
431 .expect("should build path query");
432
433 assert!(!pq.query.query.left_to_right);
434 assert_eq!(pq.query.limit, None);
435 }
436
437 #[test]
438 fn construct_path_query_start_at_included_ascending() {
439 let platform_version = PlatformVersion::latest();
440 let dpns = get_dpns_data_contract_fixture(None, 0, platform_version.protocol_version);
441 let contract = dpns.data_contract_owned();
442
443 let contestant_id = Identifier::from([0xCC; 32]);
444 let start_key = [0x42u8; 32];
445 let query = build_resolved_query(
446 &contract,
447 contestant_id,
448 None,
449 Some(5),
450 Some((start_key, true)),
451 true,
452 );
453
454 let pq = query
455 .construct_path_query(platform_version)
456 .expect("should build path query");
457
458 let items = &pq.query.query.items;
459 assert_eq!(items.len(), 1);
460 assert!(
461 matches!(&items[0], QueryItem::RangeFrom(r) if r.start == start_key.to_vec()),
462 "ascending + included = RangeFrom"
463 );
464 }
465
466 #[test]
467 fn construct_path_query_start_at_excluded_ascending() {
468 let platform_version = PlatformVersion::latest();
469 let dpns = get_dpns_data_contract_fixture(None, 0, platform_version.protocol_version);
470 let contract = dpns.data_contract_owned();
471
472 let contestant_id = Identifier::from([0xDD; 32]);
473 let start_key = [0x42u8; 32];
474 let query = build_resolved_query(
475 &contract,
476 contestant_id,
477 None,
478 Some(5),
479 Some((start_key, false)),
480 true,
481 );
482
483 let pq = query
484 .construct_path_query(platform_version)
485 .expect("should build path query");
486
487 let items = &pq.query.query.items;
488 assert_eq!(items.len(), 1);
489 assert!(
490 matches!(&items[0], QueryItem::RangeAfter(r) if r.start == start_key.to_vec()),
491 "ascending + excluded = RangeAfter"
492 );
493 }
494
495 #[test]
496 fn construct_path_query_start_at_included_descending() {
497 let platform_version = PlatformVersion::latest();
498 let dpns = get_dpns_data_contract_fixture(None, 0, platform_version.protocol_version);
499 let contract = dpns.data_contract_owned();
500
501 let contestant_id = Identifier::from([0xEE; 32]);
502 let start_key = [0x42u8; 32];
503 let query = build_resolved_query(
504 &contract,
505 contestant_id,
506 None,
507 Some(5),
508 Some((start_key, true)),
509 false,
510 );
511
512 let pq = query
513 .construct_path_query(platform_version)
514 .expect("should build path query");
515
516 let items = &pq.query.query.items;
517 assert_eq!(items.len(), 1);
518 assert!(
519 matches!(&items[0], QueryItem::RangeToInclusive(r) if r.end == start_key.to_vec()),
520 "descending + included = RangeToInclusive"
521 );
522 }
523
524 #[test]
525 fn construct_path_query_start_at_excluded_descending() {
526 let platform_version = PlatformVersion::latest();
527 let dpns = get_dpns_data_contract_fixture(None, 0, platform_version.protocol_version);
528 let contract = dpns.data_contract_owned();
529
530 let contestant_id = Identifier::from([0xFF; 32]);
531 let start_key = [0x42u8; 32];
532 let query = build_resolved_query(
533 &contract,
534 contestant_id,
535 None,
536 Some(5),
537 Some((start_key, false)),
538 false,
539 );
540
541 let pq = query
542 .construct_path_query(platform_version)
543 .expect("should build path query");
544
545 let items = &pq.query.query.items;
546 assert_eq!(items.len(), 1);
547 assert!(
548 matches!(&items[0], QueryItem::RangeTo(r) if r.end == start_key.to_vec()),
549 "descending + excluded = RangeTo"
550 );
551 }
552
553 #[test]
554 fn construct_path_query_with_offset_and_limit() {
555 let platform_version = PlatformVersion::latest();
556 let dpns = get_dpns_data_contract_fixture(None, 0, platform_version.protocol_version);
557 let contract = dpns.data_contract_owned();
558
559 let contestant_id = Identifier::from([0x11; 32]);
560 let query = build_resolved_query(
561 &contract,
562 contestant_id,
563 Some(3), Some(20), None,
566 true,
567 );
568
569 let pq = query
570 .construct_path_query(platform_version)
571 .expect("should build path query");
572
573 assert_eq!(pq.query.limit, Some(20));
574 assert_eq!(pq.query.offset, Some(3));
575 }
576}