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
// Copyright (c) 2022 MASSA LABS <info@massa.net>

//! This module represents an event store allowing to store, search and retrieve
//! a config-limited number of execution-generated events

use massa_models::execution::EventFilter;
use massa_models::output_event::SCOutputEvent;
use serde::Serialize;
use std::collections::VecDeque;

/// Store for events emitted by smart contracts
#[derive(Default, Debug, Clone, Serialize)]
pub struct EventStore(pub VecDeque<SCOutputEvent>);

impl EventStore {
    /// Push a new smart contract event to the store
    pub fn push(&mut self, event: SCOutputEvent) {
        self.0.push_back(event);
    }

    /// Take the event store
    pub fn take(&mut self) -> VecDeque<SCOutputEvent> {
        std::mem::take(&mut self.0)
    }

    /// Clear the event store
    pub fn clear(&mut self) {
        self.0.clear()
    }

    /// Prune the event store if its size is over the given limit
    pub fn prune(&mut self, max_events: usize) {
        while self.0.len() > max_events {
            self.0.pop_front();
        }
    }

    /// Extend the event store with another store
    pub fn extend(&mut self, other: EventStore) {
        self.0.extend(other.0);
    }

    /// Set the events of this store as final
    pub fn finalize(&mut self) {
        for output in self.0.iter_mut() {
            output.context.is_final = true;
        }
    }

    /// Get events optionally filtered by:
    /// * start slot
    /// * end slot
    /// * emitter address
    /// * original caller address
    /// * operation id
    /// * is final
    pub fn get_filtered_sc_output_events(&self, filter: &EventFilter) -> VecDeque<SCOutputEvent> {
        self.get_filtered_sc_output_events_iter(filter)
            .cloned()
            .collect()
    }

    /// Get events iterator optionally filtered by given EventFilter
    pub fn get_filtered_sc_output_events_iter<'b, 'a: 'b>(
        &'a self,
        filter: &'b EventFilter,
    ) -> impl Iterator<Item = &'a SCOutputEvent> + 'b {
        // Note on lifetimes:
        // 'a -> is the lifetime for self -> because the iterator returns items from self
        // 'b -> is the lifetime for filter -> because the returning iterator captures filter
        // , and we have lifetime 'a > 'b because filter can live less than self

        self.0.iter().filter(|x| {
            if let Some(start) = filter.start {
                if x.context.slot < start {
                    return false;
                }
            }
            if let Some(end) = filter.end {
                if x.context.slot >= end {
                    return false;
                }
            }
            if let Some(is_final) = filter.is_final {
                if x.context.is_final != is_final {
                    return false;
                }
            }
            if let Some(is_error) = filter.is_error {
                if x.context.is_error != is_error {
                    return false;
                }
            }

            match (filter.original_caller_address, x.context.call_stack.front()) {
                (Some(addr1), Some(addr2)) if addr1 != *addr2 => return false,
                (Some(_), None) => return false,
                _ => (),
            }
            match (filter.emitter_address, x.context.call_stack.back()) {
                (Some(addr1), Some(addr2)) if addr1 != *addr2 => return false,
                (Some(_), None) => return false,
                _ => (),
            }
            match (filter.original_operation_id, x.context.origin_operation_id) {
                (Some(addr1), Some(addr2)) if addr1 != addr2 => return false,
                (Some(_), None) => return false,
                _ => (),
            }

            true
        })
    }
}

#[test]
fn test_prune() {
    use massa_models::output_event::{EventExecutionContext, SCOutputEvent};
    use massa_models::slot::Slot;

    let mut store = EventStore(VecDeque::new());
    for i in 0..10 {
        store.push(SCOutputEvent {
            context: EventExecutionContext {
                slot: Slot::new(i, 0),
                block: None,
                read_only: false,
                index_in_slot: 1,
                call_stack: VecDeque::new(),
                origin_operation_id: None,
                is_final: false,
                is_error: false,
            },
            data: i.to_string(),
        });
    }
    assert_eq!(store.0.len(), 10);
    store.prune(3);
    assert_eq!(store.0.len(), 3);
    assert_eq!(store.0[2].data, "9");
    assert_eq!(store.0[1].data, "8");
    assert_eq!(store.0[0].data, "7");
}