holochain_integrity_types/
chain.rs

1//! # Source Chain Filtering
2//! Types for filtering the source chain.
3
4use std::collections::HashSet;
5
6use holo_hash::ActionHash;
7use holo_hash::AgentPubKey;
8use holochain_serialized_bytes::prelude::*;
9
10use crate::MigrationTarget;
11
12#[cfg(test)]
13mod test;
14
15#[derive(Serialize, Deserialize, SerializedBytes, Debug, PartialEq, Eq, Hash, Clone)]
16/// Filter source chain items.
17/// Starting from some chain position given as an [`ActionHash`]
18/// the chain is walked backwards to genesis.
19/// The filter can stop early by specifying the number of
20/// chain items to take and / or an [`ActionHash`] to consume until.
21pub struct ChainFilter<H: Eq + Ord + std::hash::Hash = ActionHash> {
22    /// The starting position of the filter.
23    pub chain_top: H,
24    /// The filters that have been applied.
25    /// Defaults to [`ChainFilters::ToGenesis`].
26    pub filters: ChainFilters<H>,
27    /// Should the query return any entries that are
28    /// cached at the agent activity to save network hops.
29    pub include_cached_entries: bool,
30}
31
32#[derive(Serialize, Deserialize, Debug, Eq, Clone)]
33/// Specify which [`Action`](crate::action::Action)s to allow through
34/// this filter.
35pub enum ChainFilters<H: Eq + Ord + std::hash::Hash = ActionHash> {
36    /// Allow all up to genesis.
37    ToGenesis,
38    /// Take this many (inclusive of the starting position).
39    Take(u32),
40    /// Continue until one of these hashes is found.
41    Until(HashSet<H>),
42    /// Combination of both take and until.
43    /// Whichever is the smaller set.
44    Both(u32, HashSet<H>),
45}
46
47/// Create a deterministic hash to compare filters.
48impl<H: Eq + Ord + std::hash::Hash> core::hash::Hash for ChainFilters<H> {
49    fn hash<HH: std::hash::Hasher>(&self, state: &mut HH) {
50        core::mem::discriminant(self).hash(state);
51        match self {
52            ChainFilters::ToGenesis => (),
53            ChainFilters::Take(t) => t.hash(state),
54            ChainFilters::Until(u) => {
55                let mut u: Vec<_> = u.iter().collect();
56                u.sort_unstable();
57                u.hash(state);
58            }
59            ChainFilters::Both(t, u) => {
60                let mut u: Vec<_> = u.iter().collect();
61                u.sort_unstable();
62                u.hash(state);
63                t.hash(state);
64            }
65        }
66    }
67}
68
69/// Implement a deterministic partial eq to compare ChainFilters.
70impl<H: Eq + Ord + std::hash::Hash> core::cmp::PartialEq for ChainFilters<H> {
71    fn eq(&self, other: &Self) -> bool {
72        match (self, other) {
73            (Self::Take(l0), Self::Take(r0)) => l0 == r0,
74            (Self::Until(a), Self::Until(b)) => {
75                let mut a: Vec<_> = a.iter().collect();
76                let mut b: Vec<_> = b.iter().collect();
77                a.sort_unstable();
78                b.sort_unstable();
79                a == b
80            }
81            (Self::Both(l0, a), Self::Both(r0, b)) => {
82                let mut a: Vec<_> = a.iter().collect();
83                let mut b: Vec<_> = b.iter().collect();
84                a.sort_unstable();
85                b.sort_unstable();
86                l0 == r0 && a == b
87            }
88            _ => core::mem::discriminant(self) == core::mem::discriminant(other),
89        }
90    }
91}
92
93#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
94/// Input to the `must_get_agent_activity` call.
95pub struct MustGetAgentActivityInput {
96    /// The author of the chain that you are requesting
97    /// activity from.
98    pub author: AgentPubKey,
99    /// The filter on the chains activity.
100    pub chain_filter: ChainFilter,
101}
102
103impl<H: Eq + Ord + std::hash::Hash> ChainFilter<H> {
104    /// Create a new filter using this [`ActionHash`] as
105    /// the starting position and walking the chain
106    /// towards the genesis [`Action`](crate::action::Action).
107    pub fn new(chain_top: H) -> Self {
108        Self {
109            chain_top,
110            filters: Default::default(),
111            include_cached_entries: false,
112        }
113    }
114
115    /// Take up to `n` actions including the starting position.
116    /// This may return less then `n` actions.
117    pub fn take(mut self, n: u32) -> Self {
118        self.filters = match self.filters {
119            ChainFilters::ToGenesis => ChainFilters::Take(n),
120            ChainFilters::Take(old_n) => ChainFilters::Take(old_n.min(n)),
121            ChainFilters::Until(u) => ChainFilters::Both(n, u),
122            ChainFilters::Both(old_n, u) => ChainFilters::Both(old_n.min(n), u),
123        };
124        self
125    }
126
127    /// Set this filter to include any cached entries
128    /// at the agent activity authority.
129    pub fn include_cached_entries(mut self) -> Self {
130        self.include_cached_entries = true;
131        self
132    }
133
134    /// Take all actions until this action hash is found.
135    /// Note that all actions specified as `until` hashes must be
136    /// found so this filter can produce deterministic results.
137    /// It is invalid to specify an until hash that is on a different
138    /// fork then the starting position.
139    pub fn until(mut self, action_hash: H) -> Self {
140        self.filters = match self.filters {
141            ChainFilters::ToGenesis => ChainFilters::Until(Some(action_hash).into_iter().collect()),
142            ChainFilters::Take(n) => ChainFilters::Both(n, Some(action_hash).into_iter().collect()),
143            ChainFilters::Until(mut u) => {
144                u.insert(action_hash);
145                ChainFilters::Until(u)
146            }
147            ChainFilters::Both(n, mut u) => {
148                u.insert(action_hash);
149                ChainFilters::Both(n, u)
150            }
151        };
152        self
153    }
154
155    /// Get the until hashes if there are any.
156    pub fn get_until(&self) -> Option<&HashSet<H>> {
157        match &self.filters {
158            ChainFilters::Until(u) => Some(u),
159            ChainFilters::Both(_, u) => Some(u),
160            _ => None,
161        }
162    }
163
164    /// Get the take number if there is one.
165    pub fn get_take(&self) -> Option<u32> {
166        match &self.filters {
167            ChainFilters::Take(s) => Some(*s),
168            ChainFilters::Both(s, _) => Some(*s),
169            _ => None,
170        }
171    }
172}
173
174impl<H: Eq + Ord + std::hash::Hash> Default for ChainFilters<H> {
175    fn default() -> Self {
176        Self::ToGenesis
177    }
178}
179
180/// Input to close a chain.
181#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, SerializedBytes)]
182pub struct CloseChainInput {
183    /// The target identifier for the chain that will be migrated to.
184    pub new_target: Option<MigrationTarget>,
185}
186
187/// Input to open a chain.
188#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, SerializedBytes)]
189pub struct OpenChainInput {
190    /// The identifier for the chain that was migrated from.
191    pub prev_target: MigrationTarget,
192
193    /// Hash of the corresponding CloseChain action
194    pub close_hash: ActionHash,
195}