1use std::iter::Peekable;
3use std::ops::RangeInclusive;
4
5use crate::activity::AgentActivityResponse;
6use crate::activity::ChainItems;
7use crate::warrant::WarrantOp;
8use holo_hash::ActionHash;
9use holo_hash::AgentPubKey;
10use holo_hash::HasHash;
11use holochain_serialized_bytes::prelude::*;
12use holochain_zome_types::prelude::*;
13
14#[cfg(all(test, feature = "test_utils"))]
15mod test;
16
17mod chain_item;
18pub use chain_item::*;
19
20pub trait AgentActivityExt {
22 fn empty<T>(agent: &AgentPubKey) -> AgentActivityResponse {
24 AgentActivityResponse {
25 agent: agent.clone(),
26 valid_activity: ChainItems::NotRequested,
27 rejected_activity: ChainItems::NotRequested,
28 status: ChainStatus::Empty,
29 highest_observed: None,
31 warrants: vec![],
32 }
33 }
34}
35
36impl AgentActivityExt for AgentActivityResponse {}
37
38#[must_use = "Iterator doesn't do anything unless consumed."]
39#[derive(Debug)]
40pub struct ChainFilterIter<I: AsRef<A>, A: ChainItem = SignedActionHashed> {
50 filter: ChainFilter<A::Hash>,
51 iter: Peekable<std::vec::IntoIter<I>>,
52 end: bool,
53}
54
55#[derive(Debug, Clone, PartialEq, Eq)]
56pub struct ChainFilterRange {
59 filter: ChainFilter,
61 range: RangeInclusive<u32>,
66 chain_bottom_type: ChainBottomType,
68}
69
70#[derive(Debug, Clone, PartialEq, Eq)]
71enum ChainBottomType {
73 Genesis,
75 Take,
78 Until,
81}
82
83#[derive(Debug, Clone, PartialEq, Eq)]
84pub enum Sequences {
86 Found(ChainFilterRange),
88 ChainTopNotFound(ActionHash),
90 EmptyRange,
92}
93
94#[derive(Debug, Clone, PartialEq, Eq, SerializedBytes, Serialize, Deserialize)]
95pub enum MustGetAgentActivityResponse {
98 Activity {
100 activity: Vec<RegisterAgentActivity>,
102 warrants: Vec<WarrantOp>,
104 },
105 IncompleteChain,
107 ChainTopNotFound(ActionHash),
109 EmptyRange,
111}
112
113impl MustGetAgentActivityResponse {
114 #[cfg(feature = "test_utils")]
116 pub fn activity(activity: Vec<RegisterAgentActivity>) -> Self {
117 Self::Activity {
118 activity,
119 warrants: vec![],
120 }
121 }
122}
123
124#[derive(Debug, Clone, PartialEq, Eq)]
129pub enum BoundedMustGetAgentActivityResponse {
130 Activity {
132 activity: Vec<RegisterAgentActivity>,
134 warrants: Vec<WarrantOp>,
136 filter: ChainFilterRange,
138 },
139 IncompleteChain,
141 ChainTopNotFound(ActionHash),
143 EmptyRange,
145}
146
147impl BoundedMustGetAgentActivityResponse {
148 pub fn normalize(&mut self) {
151 if let Self::Activity { activity, .. } = self {
152 activity.sort_unstable_by_key(|a| a.action.action().action_seq());
153 activity.dedup_by_key(|a| a.action.as_hash().clone());
154 }
155 }
156
157 #[cfg(feature = "test_utils")]
159 pub fn activity(actions: Vec<RegisterAgentActivity>, filter: ChainFilterRange) -> Self {
160 Self::Activity {
161 activity: actions,
162 filter,
163 warrants: vec![],
164 }
165 }
166}
167
168pub fn merge_bounded_agent_activity_responses(
175 acc: BoundedMustGetAgentActivityResponse,
176 next: &BoundedMustGetAgentActivityResponse,
177) -> BoundedMustGetAgentActivityResponse {
178 match (&acc, next) {
179 (
182 BoundedMustGetAgentActivityResponse::Activity {
183 activity: responses,
184 filter: chain_filter,
185 warrants,
186 },
187 BoundedMustGetAgentActivityResponse::Activity {
188 activity: more_responses,
189 filter: other_chain_filter,
190 warrants: more_warrants,
191 },
192 ) => {
193 if chain_filter == other_chain_filter {
194 let mut merged_responses = responses.clone();
195 merged_responses.extend(more_responses.to_owned());
196 let mut merged_warrants = warrants.clone();
197 merged_warrants.extend(more_warrants.to_owned());
198 let mut merged_activity = BoundedMustGetAgentActivityResponse::Activity {
199 activity: merged_responses,
200 filter: chain_filter.clone(),
201 warrants: merged_warrants,
202 };
203 merged_activity.normalize();
204 merged_activity
205 }
206 else {
209 BoundedMustGetAgentActivityResponse::IncompleteChain
210 }
211 }
212 (BoundedMustGetAgentActivityResponse::Activity { .. }, _) => acc,
215 (_, BoundedMustGetAgentActivityResponse::Activity { .. }) => next.clone(),
218 _ => acc,
220 }
221}
222
223impl<I: AsRef<A>, A: ChainItem> ChainFilterIter<I, A> {
224 pub fn new(filter: ChainFilter<A::Hash>, mut chain: Vec<I>) -> Self {
231 chain.sort_unstable_by_key(|a| u32::MAX - a.as_ref().seq());
233 let mut iter = chain.into_iter().peekable();
235
236 let i = iter.by_ref();
238 while let Some(op) = i.peek() {
239 if *op.as_ref().get_hash() == filter.chain_top {
240 break;
241 }
242 i.next();
243 }
244
245 Self {
246 filter,
247 iter,
248 end: false,
249 }
250 }
251}
252
253impl<I: AsRef<A>, A: ChainItem> Iterator for ChainFilterIter<I, A> {
254 type Item = I;
255
256 fn next(&mut self) -> Option<Self::Item> {
257 if self.end {
258 return None;
259 }
260
261 let op = self.iter.next()?;
262 let op = loop {
263 let parent = self.iter.peek();
264
265 match parent {
267 Some(parent) => {
268 let child_seq = op.as_ref().seq();
269 let parent_seq = parent.as_ref().seq();
270 match (child_seq.cmp(&parent_seq), op.as_ref().prev_hash()) {
271 (std::cmp::Ordering::Less, _) => {
272 self.end = true;
274 break op;
275 }
276 (std::cmp::Ordering::Equal, _) => {
277 self.iter.next();
280 continue;
282 }
283 (std::cmp::Ordering::Greater, None) => {
284 return None;
292 }
293 (std::cmp::Ordering::Greater, _)
294 if parent_seq.checked_add(1)? != child_seq =>
295 {
296 self.end = true;
298 break op;
299 }
300 (std::cmp::Ordering::Greater, Some(prev_hash))
301 if prev_hash != parent.as_ref().get_hash() =>
302 {
303 self.iter.next();
306 continue;
308 }
309 (std::cmp::Ordering::Greater, Some(_)) => {
310 break op;
312 }
313 }
314 }
315 None => break op,
316 }
317 };
318
319 match &mut self.filter.filters {
320 ChainFilters::Take(n) => *n = n.checked_sub(1)?,
322 ChainFilters::Until(until_hashes) => {
324 if until_hashes.contains(op.as_ref().get_hash()) {
325 self.end = true;
327 }
328 }
329 ChainFilters::ToGenesis => (),
331 ChainFilters::Both(n, until_hashes) => {
333 *n = n.checked_sub(1)?;
334
335 if until_hashes.contains(op.as_ref().get_hash()) {
336 self.end = true;
337 }
338 }
339 }
340 Some(op)
341 }
342}
343
344impl Sequences {
345 pub fn find_sequences<F, E>(filter: ChainFilter, mut get_seq: F) -> Result<Self, E>
347 where
348 F: FnMut(&ActionHash) -> Result<Option<u32>, E>,
349 {
350 let chain_top = match get_seq(&filter.chain_top)? {
354 Some(seq) => seq,
355 None => return Ok(Self::ChainTopNotFound(filter.chain_top)),
356 };
357
358 let mut chain_bottom_type = ChainBottomType::Genesis;
360
361 let distance = match filter.get_until() {
365 Some(until_hashes) => {
366 let max = until_hashes
368 .iter()
369 .filter_map(|hash| {
370 match get_seq(hash) {
371 Ok(seq) => {
372 let seq = seq?;
374 (seq <= chain_top).then(|| Ok(seq))
376 }
377 Err(e) => Some(Err(e)),
378 }
379 })
380 .try_fold(0, |max, result| {
381 let seq = result?;
382 Ok(max.max(seq))
383 })?;
384
385 if max != 0 {
386 chain_bottom_type = ChainBottomType::Until;
389 }
390
391 chain_top - max
394 }
395 None => chain_top,
398 };
399
400 let start = match filter.get_take() {
403 Some(take) => {
404 if take == 0 {
406 return Ok(Self::EmptyRange);
407 } else if take <= distance {
408 chain_bottom_type = ChainBottomType::Take;
410 chain_top.saturating_sub(take).saturating_add(1)
413 } else {
414 chain_top - distance
417 }
418 }
419 None => chain_top - distance,
422 };
423 Ok(Self::Found(ChainFilterRange {
424 filter,
425 range: start..=chain_top,
426 chain_bottom_type,
427 }))
428 }
429}
430
431impl ChainFilterRange {
432 pub fn range(&self) -> &RangeInclusive<u32> {
434 &self.range
435 }
436 pub fn filter_then_check(
438 self,
439 chain: Vec<RegisterAgentActivity>,
440 warrants: Vec<WarrantOp>,
441 ) -> MustGetAgentActivityResponse {
442 let until_hashes = self.filter.get_until().cloned();
443
444 let actions: Vec<_> = ChainFilterIter::new(self.filter, chain).collect();
446
447 match actions.last().zip(actions.first()) {
449 Some((lowest, highest))
451 if (lowest.action.action().action_seq()..=highest.action.action().action_seq())
452 == self.range =>
453 {
454 if let Some(hashes) = until_hashes {
457 if matches!(self.chain_bottom_type, ChainBottomType::Until)
458 && !hashes.contains(lowest.action.action_address())
459 {
460 return MustGetAgentActivityResponse::IncompleteChain;
461 }
462 }
463
464 MustGetAgentActivityResponse::Activity {
466 activity: actions,
467 warrants,
468 }
469 }
470 _ => MustGetAgentActivityResponse::IncompleteChain,
472 }
473 }
474}
475
476#[cfg(test)]
477mod tests {
478 use super::BoundedMustGetAgentActivityResponse;
479 use super::ChainBottomType;
480 use super::ChainFilter;
481 use super::ChainFilterRange;
482 use holochain_types::prelude::*;
483 use test_case::test_case;
484
485 #[test_case(
487 BoundedMustGetAgentActivityResponse::EmptyRange,
488 BoundedMustGetAgentActivityResponse::EmptyRange
489 => BoundedMustGetAgentActivityResponse::EmptyRange
490 )]
491 #[test_case(
492 BoundedMustGetAgentActivityResponse::EmptyRange,
493 BoundedMustGetAgentActivityResponse::IncompleteChain
494 => BoundedMustGetAgentActivityResponse::EmptyRange
495 )]
496 #[test_case(
497 BoundedMustGetAgentActivityResponse::EmptyRange,
498 BoundedMustGetAgentActivityResponse::ChainTopNotFound(ActionHash::from_raw_36(vec![0; 36]))
499 => BoundedMustGetAgentActivityResponse::EmptyRange
500 )]
501 #[test_case(
502 BoundedMustGetAgentActivityResponse::IncompleteChain,
503 BoundedMustGetAgentActivityResponse::IncompleteChain
504 => BoundedMustGetAgentActivityResponse::IncompleteChain
505 )]
506 #[test_case(
507 BoundedMustGetAgentActivityResponse::IncompleteChain,
508 BoundedMustGetAgentActivityResponse::EmptyRange
509 => BoundedMustGetAgentActivityResponse::IncompleteChain
510 )]
511 #[test_case(
512 BoundedMustGetAgentActivityResponse::IncompleteChain,
513 BoundedMustGetAgentActivityResponse::ChainTopNotFound(ActionHash::from_raw_36(vec![0; 36]))
514 => BoundedMustGetAgentActivityResponse::IncompleteChain
515 )]
516 #[test_case(
517 BoundedMustGetAgentActivityResponse::ChainTopNotFound(ActionHash::from_raw_36(vec![0; 36])),
518 BoundedMustGetAgentActivityResponse::ChainTopNotFound(ActionHash::from_raw_36(vec![1; 36]))
519 => BoundedMustGetAgentActivityResponse::ChainTopNotFound(ActionHash::from_raw_36(vec![0; 36]))
520 )]
521 #[test_case(
522 BoundedMustGetAgentActivityResponse::ChainTopNotFound(ActionHash::from_raw_36(vec![0; 36])),
523 BoundedMustGetAgentActivityResponse::ChainTopNotFound(ActionHash::from_raw_36(vec![0; 36]))
524 => BoundedMustGetAgentActivityResponse::ChainTopNotFound(ActionHash::from_raw_36(vec![0; 36]))
525 )]
526 #[test_case(
527 BoundedMustGetAgentActivityResponse::ChainTopNotFound(ActionHash::from_raw_36(vec![0; 36])),
528 BoundedMustGetAgentActivityResponse::EmptyRange
529 => BoundedMustGetAgentActivityResponse::ChainTopNotFound(ActionHash::from_raw_36(vec![0; 36]))
530 )]
531 #[test_case(
532 BoundedMustGetAgentActivityResponse::ChainTopNotFound(ActionHash::from_raw_36(vec![0; 36])),
533 BoundedMustGetAgentActivityResponse::IncompleteChain
534 => BoundedMustGetAgentActivityResponse::ChainTopNotFound(ActionHash::from_raw_36(vec![0; 36]))
535 )]
536 #[test_case(
538 BoundedMustGetAgentActivityResponse::activity(vec![], ChainFilterRange {
539 filter: ChainFilter::new(ActionHash::from_raw_36(vec![0; 36])),
540 range: 0..=0,
541 chain_bottom_type: ChainBottomType::Genesis,
542 }),
543 BoundedMustGetAgentActivityResponse::EmptyRange
544 => BoundedMustGetAgentActivityResponse::activity(vec![], ChainFilterRange {
545 filter: ChainFilter::new(ActionHash::from_raw_36(vec![0; 36])),
546 range: 0..=0,
547 chain_bottom_type: ChainBottomType::Genesis,
548 })
549 )]
550 #[test_case(
551 BoundedMustGetAgentActivityResponse::activity(vec![], ChainFilterRange {
552 filter: ChainFilter::new(ActionHash::from_raw_36(vec![0; 36])),
553 range: 0..=0,
554 chain_bottom_type: ChainBottomType::Genesis,
555 }),
556 BoundedMustGetAgentActivityResponse::IncompleteChain
557 => BoundedMustGetAgentActivityResponse::activity(vec![], ChainFilterRange {
558 filter: ChainFilter::new(ActionHash::from_raw_36(vec![0; 36])),
559 range: 0..=0,
560 chain_bottom_type: ChainBottomType::Genesis,
561 })
562 )]
563 #[test_case(
564 BoundedMustGetAgentActivityResponse::activity(vec![], ChainFilterRange {
565 filter: ChainFilter::new(ActionHash::from_raw_36(vec![0; 36])),
566 range: 0..=0,
567 chain_bottom_type: ChainBottomType::Genesis,
568 }),
569 BoundedMustGetAgentActivityResponse::ChainTopNotFound(ActionHash::from_raw_36(vec![0; 36]))
570 => BoundedMustGetAgentActivityResponse::activity(vec![], ChainFilterRange {
571 filter: ChainFilter::new(ActionHash::from_raw_36(vec![0; 36])),
572 range: 0..=0,
573 chain_bottom_type: ChainBottomType::Genesis,
574 })
575 )]
576 #[test_case(
577 BoundedMustGetAgentActivityResponse::EmptyRange,
578 BoundedMustGetAgentActivityResponse::activity(vec![], ChainFilterRange {
579 filter: ChainFilter::new(ActionHash::from_raw_36(vec![0; 36])),
580 range: 0..=0,
581 chain_bottom_type: ChainBottomType::Genesis,
582 })
583 => BoundedMustGetAgentActivityResponse::activity(vec![], ChainFilterRange {
584 filter: ChainFilter::new(ActionHash::from_raw_36(vec![0; 36])),
585 range: 0..=0,
586 chain_bottom_type: ChainBottomType::Genesis,
587 })
588 )]
589 #[test_case(
590 BoundedMustGetAgentActivityResponse::IncompleteChain,
591 BoundedMustGetAgentActivityResponse::activity(vec![], ChainFilterRange {
592 filter: ChainFilter::new(ActionHash::from_raw_36(vec![0; 36])),
593 range: 0..=0,
594 chain_bottom_type: ChainBottomType::Genesis,
595 })
596 => BoundedMustGetAgentActivityResponse::activity(vec![], ChainFilterRange {
597 filter: ChainFilter::new(ActionHash::from_raw_36(vec![0; 36])),
598 range: 0..=0,
599 chain_bottom_type: ChainBottomType::Genesis,
600 })
601 )]
602 #[test_case(
603 BoundedMustGetAgentActivityResponse::ChainTopNotFound(ActionHash::from_raw_36(vec![0; 36])),
604 BoundedMustGetAgentActivityResponse::activity(vec![], ChainFilterRange {
605 filter: ChainFilter::new(ActionHash::from_raw_36(vec![0; 36])),
606 range: 0..=0,
607 chain_bottom_type: ChainBottomType::Genesis,
608 })
609 => BoundedMustGetAgentActivityResponse::activity(vec![], ChainFilterRange {
610 filter: ChainFilter::new(ActionHash::from_raw_36(vec![0; 36])),
611 range: 0..=0,
612 chain_bottom_type: ChainBottomType::Genesis,
613 })
614 )]
615 #[test_case(
617 BoundedMustGetAgentActivityResponse::activity(vec![], ChainFilterRange {
618 filter: ChainFilter::new(ActionHash::from_raw_36(vec![0; 36])),
619 range: 0..=0,
620 chain_bottom_type: ChainBottomType::Genesis,
621 }),
622 BoundedMustGetAgentActivityResponse::activity(vec![], ChainFilterRange {
623 filter: ChainFilter::new(ActionHash::from_raw_36(vec![0; 36])),
624 range: 0..=0,
625 chain_bottom_type: ChainBottomType::Genesis,
626 })
627 => BoundedMustGetAgentActivityResponse::activity(vec![], ChainFilterRange {
628 filter: ChainFilter::new(ActionHash::from_raw_36(vec![0; 36])),
629 range: 0..=0,
630 chain_bottom_type: ChainBottomType::Genesis,
631 })
632 )]
633 #[test_case(
634 BoundedMustGetAgentActivityResponse::activity(vec![], ChainFilterRange {
635 filter: ChainFilter::new(ActionHash::from_raw_36(vec![0; 36])),
636 range: 0..=0,
637 chain_bottom_type: ChainBottomType::Genesis,
638 }),
639 BoundedMustGetAgentActivityResponse::activity(vec![RegisterAgentActivity{
640 action: SignedActionHashed::with_presigned(
641 ActionHashed::from_content_sync(Action::Dna(Dna {
642 author: AgentPubKey::from_raw_36(vec![0; 36]),
643 timestamp: Timestamp(0),
644 hash: DnaHash::from_raw_36(vec![0; 36]),
645 })),
646 Signature([0; SIGNATURE_BYTES]),
647 ),
648 cached_entry: None,
649 }], ChainFilterRange {
650 filter: ChainFilter::new(ActionHash::from_raw_36(vec![0; 36])),
651 range: 0..=0,
652 chain_bottom_type: ChainBottomType::Genesis,
653 })
654 => BoundedMustGetAgentActivityResponse::activity(vec![RegisterAgentActivity{
655 action: SignedActionHashed::with_presigned(
656 ActionHashed::from_content_sync(Action::Dna(Dna {
657 author: AgentPubKey::from_raw_36(vec![0; 36]),
658 timestamp: Timestamp(0),
659 hash: DnaHash::from_raw_36(vec![0; 36]),
660 })),
661 Signature([0; SIGNATURE_BYTES]),
662 ),
663 cached_entry: None
664 }], ChainFilterRange {
665 filter: ChainFilter::new(ActionHash::from_raw_36(vec![0; 36])),
666 range: 0..=0,
667 chain_bottom_type: ChainBottomType::Genesis,
668 })
669 )]
670
671 fn test_merge_bounded_agent_activity_responses(
672 acc: BoundedMustGetAgentActivityResponse,
673 next: BoundedMustGetAgentActivityResponse,
674 ) -> BoundedMustGetAgentActivityResponse {
675 super::merge_bounded_agent_activity_responses(acc, &next)
676 }
677}