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
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
//! Types for source chain queries

use std::collections::HashMap;
use std::collections::HashSet;

use crate::action::ActionType;
use crate::action::EntryType;
use crate::warrant::Warrant;
use crate::ActionHashed;
use crate::Record;
use holo_hash::ActionHash;
use holo_hash::EntryHash;
use holo_hash::HasHash;
pub use holochain_serialized_bytes::prelude::*;

/// Defines several ways that queries can be restricted to a range.
/// Notably hash bounded ranges disambiguate forks whereas sequence indexes do
/// not as the same position can be found in many forks.
/// The reason that this does NOT use native rust range traits is that the hash
/// bounded queries MUST be inclusive otherwise the integrity and fork
/// disambiguation logic is impossible. An exclusive range bound that does not
/// include the final action tells us nothing about which fork to select
/// between N forks of equal length that proceed it. With an inclusive hash
/// bounded range the final action always points unambiguously at the "correct"
/// fork that the range is over. Start hashes are not needed to provide this
/// property so ranges can be hash terminated with a length of preceeding
/// records to return only. Technically the seq bounded ranges do not imply
/// any fork disambiguation and so could be a range but for simplicity we left
/// the API symmetrical in boundedness across all enum variants.
/// @TODO It may be possible to provide/implement RangeBounds in the case that
/// a full sequence of records/actions is provided but it would need to be
/// handled as inclusive first, to enforce the integrity of the query, then the
/// exclusiveness achieved by simply removing the final record after the fact.
#[derive(serde::Serialize, serde::Deserialize, PartialEq, Clone, Debug)]
pub enum ChainQueryFilterRange {
    /// Do NOT apply any range filtering for this query.
    Unbounded,
    /// A range over source chain sequence numbers.
    /// This is ambiguous over forking histories and so should NOT be used in
    /// validation logic.
    /// Inclusive start, inclusive end.
    ActionSeqRange(u32, u32),
    /// A range over source chain action hashes.
    /// This CAN be used in validation logic as forks are disambiguated.
    /// Inclusive start and end (unlike std::ops::Range).
    ActionHashRange(ActionHash, ActionHash),
    /// The terminating action hash and N preceeding records.
    /// N = 0 returns only the record with this `ActionHash`.
    /// This CAN be used in validation logic as forks are not possible when
    /// "looking up" towards genesis from some `ActionHash`.
    ActionHashTerminated(ActionHash, u32),
}

impl Default for ChainQueryFilterRange {
    fn default() -> Self {
        Self::Unbounded
    }
}

/// Specifies arguments to a query of the source chain, including ordering and filtering.
///
/// This struct is used to construct an actual SQL query on the database, and also has methods
/// to allow filtering in-memory.
#[derive(
    serde::Serialize, serde::Deserialize, SerializedBytes, Default, PartialEq, Clone, Debug,
)]
// TODO: get feedback on whether it's OK to remove non_exhaustive
// #[non_exhaustive]
pub struct ChainQueryFilter {
    /// Limit the results to a range of records according to their actions.
    pub sequence_range: ChainQueryFilterRange,
    /// Filter by EntryType
    // NB: if this filter is set, you can't verify the results, so don't
    //     use this in validation
    pub entry_type: Option<EntryType>,
    /// Filter by a list of `EntryHash`.
    pub entry_hashes: Option<HashSet<EntryHash>>,
    /// Filter by ActionType
    // NB: if this filter is set, you can't verify the results, so don't
    //     use this in validation
    pub action_type: Option<ActionType>,
    /// Include the entries in the records
    pub include_entries: bool,
    /// The query should be ordered in descending order (default is ascending),
    /// when run as a database query. There is no provisioning for in-memory ordering.
    pub order_descending: bool,
}

#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, SerializedBytes)]
/// An agents chain records returned from a agent_activity_query
pub struct AgentActivity {
    /// Valid actions on this chain.
    pub valid_activity: Vec<(u32, ActionHash)>,
    /// Rejected actions on this chain.
    pub rejected_activity: Vec<(u32, ActionHash)>,
    /// The status of this chain.
    pub status: ChainStatus,
    /// The highest chain action that has
    /// been observed by this authority.
    pub highest_observed: Option<HighestObserved>,
    /// Warrants about this AgentActivity.
    /// Placeholder for future.
    pub warrants: Vec<Warrant>,
}

#[derive(Clone, Copy, Debug, PartialEq, serde::Serialize, serde::Deserialize, SerializedBytes)]
/// Get either the full activity or just the status of the chain
pub enum ActivityRequest {
    /// Just request the status of the chain
    Status,
    /// Request all the activity
    Full,
}

#[derive(Clone, Debug, PartialEq, Hash, Eq, serde::Serialize, serde::Deserialize)]
/// The highest action sequence observed by this authority.
/// This also includes the actions at this sequence.
/// If there is more then one then there is a fork.
///
/// This type is to prevent actions being hidden by
/// withholding the previous action.
///
/// The information is tracked at the edge of holochain before
/// validation (but after drop checks).
pub struct HighestObserved {
    /// The highest sequence number observed.
    pub action_seq: u32,
    /// Hashes of any actions claiming to be at this
    /// action sequence.
    pub hash: Vec<ActionHash>,
}

#[derive(Clone, Debug, Hash, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
/// Status of the agent activity chain
// TODO: In the future we will most likely be replaced
// by warrants instead of Forked / Invalid so we can provide
// evidence of why the chain has a status.
pub enum ChainStatus {
    /// This authority has no information on the chain.
    Empty,
    /// The chain is valid as at this action sequence and action hash.
    Valid(ChainHead),
    /// Chain is forked.
    Forked(ChainFork),
    /// Chain is invalid because of this action.
    Invalid(ChainHead),
}

impl Default for ChainStatus {
    fn default() -> Self {
        ChainStatus::Empty
    }
}

#[derive(Clone, Debug, Hash, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
/// The action at the head of the complete chain.
/// This is as far as this authority can see a
/// chain with no gaps.
pub struct ChainHead {
    /// Sequence number of this chain head.
    pub action_seq: u32,
    /// Hash of this chain head
    pub hash: ActionHash,
}

#[derive(Clone, Debug, Hash, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
/// The chain has been forked by these two actions
pub struct ChainFork {
    /// The point where the chain has forked.
    pub fork_seq: u32,
    /// The first action at this sequence position.
    pub first_action: ActionHash,
    /// The second action at this sequence position.
    pub second_action: ActionHash,
}

impl ChainQueryFilter {
    /// Create a no-op ChainQueryFilter which returns everything.
    pub fn new() -> Self {
        Self {
            include_entries: false,
            ..Self::default()
        }
    }

    /// Filter on sequence range.
    pub fn sequence_range(mut self, sequence_range: ChainQueryFilterRange) -> Self {
        self.sequence_range = sequence_range;
        self
    }

    /// Filter on entry type.
    pub fn entry_type(mut self, entry_type: EntryType) -> Self {
        self.entry_type = Some(entry_type);
        self
    }

    /// Filter on entry hashes.
    pub fn entry_hashes(mut self, entry_hashes: HashSet<EntryHash>) -> Self {
        self.entry_hashes = Some(entry_hashes);
        self
    }

    /// Filter on action type.
    pub fn action_type(mut self, action_type: ActionType) -> Self {
        self.action_type = Some(action_type);
        self
    }

    /// Include the entries in the RecordsVec that is returned.
    pub fn include_entries(mut self, include_entries: bool) -> Self {
        self.include_entries = include_entries;
        self
    }

    /// Set the order to ascending.
    pub fn ascending(mut self) -> Self {
        self.order_descending = false;
        self
    }

    /// Set the order to ascending.
    pub fn descending(mut self) -> Self {
        self.order_descending = true;
        self
    }

    /// If the sequence range supports fork disambiguation, apply it to remove
    /// actions that are not in the correct branch.
    /// Numerical range bounds do NOT support fork disambiguation, and neither
    /// does unbounded, but everything hash bounded does.
    pub fn disambiguate_forks(&self, actions: Vec<ActionHashed>) -> Vec<ActionHashed> {
        match &self.sequence_range {
            ChainQueryFilterRange::Unbounded => actions,
            ChainQueryFilterRange::ActionSeqRange(start, end) => actions
                .into_iter()
                .filter(|action| *start <= action.action_seq() && action.action_seq() <= *end)
                .collect(),
            ChainQueryFilterRange::ActionHashRange(start, end) => {
                let mut action_hashmap = actions
                    .into_iter()
                    .map(|action| (action.as_hash().clone(), action))
                    .collect::<HashMap<ActionHash, ActionHashed>>();
                let mut filtered_actions = Vec::new();
                let mut maybe_next_action = action_hashmap.remove(end);
                while let Some(next_action) = maybe_next_action {
                    maybe_next_action = next_action
                        .as_content()
                        .prev_action()
                        .and_then(|prev_action| action_hashmap.remove(prev_action));
                    let is_start = next_action.as_hash() == start;
                    filtered_actions.push(next_action);
                    // This comes after the push to make the range inclusive.
                    if is_start {
                        break;
                    }
                }
                filtered_actions
            }
            ChainQueryFilterRange::ActionHashTerminated(end, n) => {
                let mut action_hashmap = actions
                    .iter()
                    .map(|action| (action.as_hash().clone(), action))
                    .collect::<HashMap<ActionHash, &ActionHashed>>();
                let mut filtered_actions = Vec::new();
                let mut maybe_next_action = action_hashmap.remove(end);
                let mut i = 0;
                while let Some(next_action) = maybe_next_action {
                    maybe_next_action = next_action
                        .as_content()
                        .prev_action()
                        .and_then(|prev_action| action_hashmap.remove(prev_action));
                    filtered_actions.push(next_action.clone());
                    // This comes after the push to make the range inclusive.
                    if i == *n {
                        break;
                    }
                    i += 1;
                }
                filtered_actions
            }
        }
    }

    /// Filter a vector of hashed actions according to the query.
    pub fn filter_actions(&self, actions: Vec<ActionHashed>) -> Vec<ActionHashed> {
        self.disambiguate_forks(actions)
            .into_iter()
            .filter(|action| {
                self.action_type
                    .as_ref()
                    .map(|action_type| action.action_type() == *action_type)
                    .unwrap_or(true)
                    && self
                        .entry_type
                        .as_ref()
                        .map(|entry_type| action.entry_type() == Some(entry_type))
                        .unwrap_or(true)
                    && self
                        .entry_hashes
                        .as_ref()
                        .map(|entry_hashes| match action.entry_hash() {
                            Some(entry_hash) => entry_hashes.contains(entry_hash),
                            None => false,
                        })
                        .unwrap_or(true)
            })
            .collect()
    }

    /// Filter a vector of records according to the query.
    pub fn filter_records(&self, records: Vec<Record>) -> Vec<Record> {
        let actions = self.filter_actions(
            records
                .iter()
                .map(|record| record.action_hashed().clone())
                .collect(),
        );
        let action_hashset = actions
            .iter()
            .map(|action| action.as_hash().clone())
            .collect::<HashSet<ActionHash>>();
        records
            .into_iter()
            .filter(|record| action_hashset.contains(record.action_address()))
            .collect()
    }
}

#[cfg(test)]
#[cfg(feature = "fixturators")]
mod tests {
    use super::ChainQueryFilter;
    use crate::action::EntryType;
    use crate::fixt::AppEntryDefFixturator;
    use crate::fixt::*;
    use crate::ActionHashed;
    use crate::ChainQueryFilterRange;
    use ::fixt::prelude::*;
    use holo_hash::HasHash;

    /// Create three Actions with various properties.
    /// Also return the EntryTypes used to construct the first two actions.
    fn fixtures() -> [ActionHashed; 7] {
        let entry_type_1 = EntryType::App(fixt!(AppEntryDef));
        let entry_type_2 = EntryType::AgentPubKey;

        let entry_hash_0 = fixt!(EntryHash);

        let mut h0 = fixt!(Create);
        h0.entry_type = entry_type_1.clone();
        h0.action_seq = 0;
        h0.entry_hash = entry_hash_0.clone();
        let hh0 = ActionHashed::from_content_sync(h0.into());

        let mut h1 = fixt!(Update);
        h1.entry_type = entry_type_2.clone();
        h1.action_seq = 1;
        h1.prev_action = hh0.as_hash().clone();
        let hh1 = ActionHashed::from_content_sync(h1.into());

        let mut h2 = fixt!(CreateLink);
        h2.action_seq = 2;
        h2.prev_action = hh1.as_hash().clone();
        let hh2 = ActionHashed::from_content_sync(h2.into());

        let mut h3 = fixt!(Create);
        h3.entry_type = entry_type_2.clone();
        h3.action_seq = 3;
        h3.prev_action = hh2.as_hash().clone();
        let hh3 = ActionHashed::from_content_sync(h3.into());

        // Cheeky forker!
        let mut h3a = fixt!(Create);
        h3a.entry_type = entry_type_1.clone();
        h3a.action_seq = 3;
        h3a.prev_action = hh2.as_hash().clone();
        let hh3a = ActionHashed::from_content_sync(h3a.into());

        let mut h4 = fixt!(Update);
        h4.entry_type = entry_type_1.clone();
        // same entry content as h0
        h4.entry_hash = entry_hash_0;
        h4.action_seq = 4;
        h4.prev_action = hh3.as_hash().clone();
        let hh4 = ActionHashed::from_content_sync(h4.into());

        let mut h5 = fixt!(CreateLink);
        h5.action_seq = 5;
        h5.prev_action = hh4.as_hash().clone();
        let hh5 = ActionHashed::from_content_sync(h5.into());

        let actions = [hh0, hh1, hh2, hh3, hh3a, hh4, hh5];
        actions
    }

    fn map_query(query: &ChainQueryFilter, actions: &[ActionHashed]) -> Vec<bool> {
        let filtered = query.filter_actions(actions.to_vec());
        actions
            .iter()
            .map(|h| filtered.contains(h))
            .collect::<Vec<_>>()
    }

    #[test]
    fn filter_by_entry_type() {
        let actions = fixtures();

        let query_1 =
            ChainQueryFilter::new().entry_type(actions[0].entry_type().unwrap().to_owned());
        let query_2 =
            ChainQueryFilter::new().entry_type(actions[1].entry_type().unwrap().to_owned());

        assert_eq!(
            map_query(&query_1, &actions),
            [true, false, false, false, true, true, false].to_vec()
        );
        assert_eq!(
            map_query(&query_2, &actions),
            [false, true, false, true, false, false, false].to_vec()
        );
    }

    #[test]
    fn filter_by_entry_hash() {
        let actions = fixtures();

        let query = ChainQueryFilter::new().entry_hashes(
            vec![
                actions[3].entry_hash().unwrap().clone(),
                // actions[5] has same entry hash as actions[0]
                actions[5].entry_hash().unwrap().clone(),
            ]
            .into_iter()
            .collect(),
        );

        assert_eq!(
            map_query(&query, &actions),
            vec![true, false, false, true, false, true, false]
        );
    }

    #[test]
    fn filter_by_action_type() {
        let actions = fixtures();

        let query_1 = ChainQueryFilter::new().action_type(actions[0].action_type());
        let query_2 = ChainQueryFilter::new().action_type(actions[1].action_type());
        let query_3 = ChainQueryFilter::new().action_type(actions[2].action_type());

        assert_eq!(
            map_query(&query_1, &actions),
            [true, false, false, true, true, false, false].to_vec()
        );
        assert_eq!(
            map_query(&query_2, &actions),
            [false, true, false, false, false, true, false].to_vec()
        );
        assert_eq!(
            map_query(&query_3, &actions),
            [false, false, true, false, false, false, true].to_vec()
        );
    }

    #[test]
    fn filter_by_chain_sequence() {
        let actions = fixtures();

        for (sequence_range, expected, name) in vec![
            (
                ChainQueryFilterRange::Unbounded,
                vec![true, true, true, true, true, true, true],
                "unbounded",
            ),
            (
                ChainQueryFilterRange::ActionSeqRange(0, 0),
                vec![true, false, false, false, false, false, false],
                "first only",
            ),
            (
                ChainQueryFilterRange::ActionSeqRange(0, 1),
                vec![true, true, false, false, false, false, false],
                "several from start",
            ),
            (
                ChainQueryFilterRange::ActionSeqRange(1, 2),
                vec![false, true, true, false, false, false, false],
                "several not start",
            ),
            (
                ChainQueryFilterRange::ActionSeqRange(2, 999),
                vec![false, false, true, true, true, true, true],
                "exceeds chain length, not start",
            ),
            (
                ChainQueryFilterRange::ActionHashRange(
                    actions[2].as_hash().clone(),
                    actions[6].as_hash().clone(),
                ),
                vec![false, false, true, true, false, true, true],
                "hash bounded not 3a",
            ),
            (
                ChainQueryFilterRange::ActionHashRange(
                    actions[2].as_hash().clone(),
                    actions[4].as_hash().clone(),
                ),
                vec![false, false, true, false, true, false, false],
                "hash bounded 3a",
            ),
            (
                ChainQueryFilterRange::ActionHashTerminated(actions[2].as_hash().clone(), 1),
                vec![false, true, true, false, false, false, false],
                "hash terminated not start",
            ),
            (
                ChainQueryFilterRange::ActionHashTerminated(actions[2].as_hash().clone(), 0),
                vec![false, false, true, false, false, false, false],
                "hash terminated not start 0 prior",
            ),
            (
                ChainQueryFilterRange::ActionHashTerminated(actions[5].as_hash().clone(), 7),
                vec![true, true, true, true, false, true, false],
                "hash terminated main chain before chain start",
            ),
            (
                ChainQueryFilterRange::ActionHashTerminated(actions[4].as_hash().clone(), 7),
                vec![true, true, true, false, true, false, false],
                "hash terminated 3a chain before chain start",
            ),
        ] {
            assert_eq!(
                (
                    map_query(
                        &ChainQueryFilter::new().sequence_range(sequence_range),
                        &actions,
                    ),
                    name
                ),
                (expected, name),
            );
        }
    }

    #[test]
    fn filter_by_multi() {
        let actions = fixtures();

        assert_eq!(
            map_query(
                &ChainQueryFilter::new()
                    .action_type(actions[0].action_type())
                    .entry_type(actions[0].entry_type().unwrap().clone())
                    .sequence_range(ChainQueryFilterRange::ActionSeqRange(0, 0)),
                &actions
            ),
            [true, false, false, false, false, false, false].to_vec()
        );

        assert_eq!(
            map_query(
                &ChainQueryFilter::new()
                    .action_type(actions[1].action_type())
                    .entry_type(actions[0].entry_type().unwrap().clone())
                    .sequence_range(ChainQueryFilterRange::ActionSeqRange(0, 999)),
                &actions
            ),
            [false, false, false, false, false, true, false].to_vec()
        );

        assert_eq!(
            map_query(
                &ChainQueryFilter::new()
                    .entry_type(actions[0].entry_type().unwrap().clone())
                    .sequence_range(ChainQueryFilterRange::ActionSeqRange(0, 999)),
                &actions
            ),
            [true, false, false, false, true, true, false].to_vec()
        );
    }
}