1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
//! # Source Chain Filtering
//! Types for filtering the source chain.
use crate::MigrationTarget;
use holo_hash::ActionHash;
use holo_hash::AgentPubKey;
use holochain_serialized_bytes::prelude::*;
use holochain_timestamp::Timestamp;
#[cfg(test)]
mod test;
/// Filter the contiguous chain of actions from an agent's source chain.
///
/// Starting from a specified `chain_top` [`ActionHash`], the filter walks backwards through
/// the linear chain path, towards genesis, excluding any forked actions, and bounded by
/// the specified [`LimitConditions`].
#[derive(Serialize, Deserialize, SerializedBytes, Debug, PartialEq, Eq, Hash, Clone)]
pub struct ChainFilter<H: Eq + Ord + std::hash::Hash = ActionHash> {
/// The starting position of the filter.
pub chain_top: H,
/// The limit conditions used by this filter.
/// Defaults to [`LimitConditions::ToGenesis`].
pub limit_conditions: LimitConditions<H>,
/// Should the query return any entries that are
/// cached at the agent activity to save network hops.
pub include_cached_entries: bool,
}
/// Specify when to stop walking down the chain from the `chain_top` action.
///
/// All variants walk down the chain from the `chain_top` action,
/// following the linear chain path and excluding any forked actions.
#[derive(Serialize, Deserialize, Debug, Eq, Clone, Default)]
pub enum LimitConditions<H: Eq + Ord + std::hash::Hash = ActionHash> {
/// Include all actions to the start of the chain.
#[default]
ToGenesis,
/// Include exactly the specified number of actions in the chain, or all actions in the chain,
/// whichever is fewer.
///
/// A value of `0` is considered invalid and will cause an error.
Take(u32),
/// Include all actions in the chain with timestamps greater than or equal to the given
/// timestamp.
///
/// To receive a success response, the query must retrieve an action with a timestamp *older*
/// than the given timestamp, *or* retrieve all actions until genesis. Without this we would
/// not know if there are additional actions after the timestamp, and so the response would
/// not be deterministic.
///
/// A timestamp value that is greater than the `chain_top` timestamp is considered invalid
/// and will return `MustGetAgentActivityResponse::UntilTimestampGreaterThanChainHead`.
UntilTimestamp(Timestamp),
/// Include all actions in the chain down to and including the action matching the given hash.
///
/// To receive a success response, the query must retrieve an action matching the given hash.
///
/// A hash value that maps to an action with a sequence number greater than the `chain_top`
/// sequence number is considered invalid and will return
/// `MustGetAgentActivityResponse::UntilHashAfterChainHead`.
UntilHash(H),
}
/// Create a deterministic hash to compare [LimitConditions].
impl<H: Eq + Ord + std::hash::Hash> core::hash::Hash for LimitConditions<H> {
fn hash<HH: std::hash::Hasher>(&self, state: &mut HH) {
core::mem::discriminant(self).hash(state);
match self {
LimitConditions::ToGenesis => (),
LimitConditions::Take(t) => t.hash(state),
LimitConditions::UntilTimestamp(ts) => ts.hash(state),
LimitConditions::UntilHash(u) => u.hash(state),
}
}
}
/// Implement a deterministic partial eq to compare [LimitConditions].
impl<H: Eq + Ord + std::hash::Hash> core::cmp::PartialEq for LimitConditions<H> {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Take(l0), Self::Take(r0)) => l0 == r0,
(Self::UntilTimestamp(l0), Self::UntilTimestamp(r0)) => l0 == r0,
(Self::UntilHash(l0), Self::UntilHash(r0)) => l0 == r0,
_ => core::mem::discriminant(self) == core::mem::discriminant(other),
}
}
}
/// Input to the `must_get_agent_activity` call.
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct MustGetAgentActivityInput {
/// The author of the chain that you are requesting
/// activity from.
pub author: AgentPubKey,
/// The filter on the chain's activity.
pub chain_filter: ChainFilter,
}
impl<H: Eq + Ord + std::hash::Hash> ChainFilter<H> {
/// Create a new filter using this [`ActionHash`] as
/// the starting position and walking the chain
/// towards the genesis [`Action`](crate::action::Action).
pub fn new(chain_top: H) -> Self {
Self {
chain_top,
limit_conditions: Default::default(),
include_cached_entries: false,
}
}
/// Create a filter with a take limit.
pub fn take(chain_top: H, n: u32) -> Self {
Self {
chain_top,
limit_conditions: LimitConditions::Take(n),
include_cached_entries: false,
}
}
/// Set this filter to include any cached entries
/// at the agent activity authority.
pub fn include_cached_entries(mut self) -> Self {
self.include_cached_entries = true;
self
}
/// Create a filter with an until-timestamp limit.
pub fn until_timestamp(chain_top: H, timestamp: Timestamp) -> Self {
Self {
chain_top,
limit_conditions: LimitConditions::UntilTimestamp(timestamp),
include_cached_entries: false,
}
}
/// Create a filter with an until-hash limit.
pub fn until_hash(chain_top: H, action_hash: H) -> Self {
Self {
chain_top,
limit_conditions: LimitConditions::UntilHash(action_hash),
include_cached_entries: false,
}
}
/// Get the until hash if there is one.
pub fn get_until_hash(&self) -> Option<&H> {
match &self.limit_conditions {
LimitConditions::UntilHash(u) => Some(u),
_ => None,
}
}
/// Get the take number if there is one.
pub fn get_take(&self) -> Option<u32> {
match self.limit_conditions {
LimitConditions::Take(n) => Some(n),
_ => None,
}
}
/// Get the take number if there is one.
pub fn get_until_timestamp(&self) -> Option<Timestamp> {
match self.limit_conditions {
LimitConditions::UntilTimestamp(ts) => Some(ts),
_ => None,
}
}
}
/// Input to close a chain.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, SerializedBytes)]
pub struct CloseChainInput {
/// The target identifier for the chain that will be migrated to.
pub new_target: Option<MigrationTarget>,
}
/// Input to open a chain.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, SerializedBytes)]
pub struct OpenChainInput {
/// The identifier for the chain that was migrated from.
pub prev_target: MigrationTarget,
/// Hash of the corresponding CloseChain action
pub close_hash: ActionHash,
}