massa_execution_exports/
event_store.rs

1// Copyright (c) 2022 MASSA LABS <info@massa.net>
2
3//! This module represents an event store allowing to store, search and retrieve
4//! a config-limited number of execution-generated events
5
6use massa_models::execution::EventFilter;
7use massa_models::output_event::SCOutputEvent;
8use serde::Serialize;
9use std::collections::VecDeque;
10
11/// Store for events emitted by smart contracts
12#[derive(Default, Debug, Clone, Serialize)]
13pub struct EventStore(pub VecDeque<SCOutputEvent>);
14
15impl EventStore {
16    /// Push a new smart contract event to the store
17    pub fn push(&mut self, event: SCOutputEvent) {
18        self.0.push_back(event);
19    }
20
21    /// Take the event store
22    pub fn take(&mut self) -> VecDeque<SCOutputEvent> {
23        std::mem::take(&mut self.0)
24    }
25
26    /// Clear the event store
27    pub fn clear(&mut self) {
28        self.0.clear()
29    }
30
31    /// Prune the event store if its size is over the given limit
32    pub fn prune(&mut self, max_events: usize) {
33        while self.0.len() > max_events {
34            self.0.pop_front();
35        }
36    }
37
38    /// Extend the event store with another store
39    pub fn extend(&mut self, other: EventStore) {
40        self.0.extend(other.0);
41    }
42
43    /// Set the events of this store as final
44    pub fn finalize(&mut self) {
45        for output in self.0.iter_mut() {
46            output.context.is_final = true;
47        }
48    }
49
50    /// Get events optionally filtered by:
51    /// * start slot
52    /// * end slot
53    /// * emitter address
54    /// * original caller address
55    /// * operation id
56    /// * is final
57    pub fn get_filtered_sc_output_events(&self, filter: &EventFilter) -> VecDeque<SCOutputEvent> {
58        self.get_filtered_sc_output_events_iter(filter)
59            .cloned()
60            .collect()
61    }
62
63    /// Get events iterator optionally filtered by given EventFilter
64    pub fn get_filtered_sc_output_events_iter<'b, 'a: 'b>(
65        &'a self,
66        filter: &'b EventFilter,
67    ) -> impl Iterator<Item = &'a SCOutputEvent> + 'b {
68        // Note on lifetimes:
69        // 'a -> is the lifetime for self -> because the iterator returns items from self
70        // 'b -> is the lifetime for filter -> because the returning iterator captures filter
71        // , and we have lifetime 'a > 'b because filter can live less than self
72
73        self.0.iter().filter(|x| {
74            if let Some(start) = filter.start {
75                if x.context.slot < start {
76                    return false;
77                }
78            }
79            if let Some(end) = filter.end {
80                if x.context.slot >= end {
81                    return false;
82                }
83            }
84            if let Some(is_final) = filter.is_final {
85                if x.context.is_final != is_final {
86                    return false;
87                }
88            }
89            if let Some(is_error) = filter.is_error {
90                if x.context.is_error != is_error {
91                    return false;
92                }
93            }
94
95            match (filter.original_caller_address, x.context.call_stack.front()) {
96                (Some(addr1), Some(addr2)) if addr1 != *addr2 => return false,
97                (Some(_), None) => return false,
98                _ => (),
99            }
100            match (filter.emitter_address, x.context.call_stack.back()) {
101                (Some(addr1), Some(addr2)) if addr1 != *addr2 => return false,
102                (Some(_), None) => return false,
103                _ => (),
104            }
105            match (filter.original_operation_id, x.context.origin_operation_id) {
106                (Some(addr1), Some(addr2)) if addr1 != addr2 => return false,
107                (Some(_), None) => return false,
108                _ => (),
109            }
110
111            true
112        })
113    }
114}
115
116#[test]
117fn test_prune() {
118    use massa_models::output_event::{EventExecutionContext, SCOutputEvent};
119    use massa_models::slot::Slot;
120
121    let mut store = EventStore(VecDeque::new());
122    for i in 0..10 {
123        store.push(SCOutputEvent {
124            context: EventExecutionContext {
125                slot: Slot::new(i, 0),
126                block: None,
127                read_only: false,
128                index_in_slot: 1,
129                call_stack: VecDeque::new(),
130                origin_operation_id: None,
131                is_final: false,
132                is_error: false,
133                deferred_call_id: None,
134                async_msg_id: None,
135            },
136            data: i.to_string(),
137        });
138    }
139    assert_eq!(store.0.len(), 10);
140    store.prune(3);
141    assert_eq!(store.0.len(), 3);
142    assert_eq!(store.0[2].data, "9");
143    assert_eq!(store.0[1].data, "8");
144    assert_eq!(store.0[0].data, "7");
145}