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
// 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.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
            })
            .cloned()
            .collect()
    }
}

#[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");
}