cometbft_rpc/
event.rs

1//! RPC subscription event-related data structures.
2
3use alloc::collections::BTreeMap as HashMap;
4
5use cometbft::{abci, block, Block};
6
7use crate::{prelude::*, query::EventType};
8
9/// An incoming event produced by a [`Subscription`].
10///
11/// [`Subscription`]: ../struct.Subscription.html
12#[derive(Debug, Clone, PartialEq, Eq)]
13pub struct Event {
14    /// The query that produced the event.
15    pub query: String,
16    /// The data associated with the event.
17    pub data: EventData,
18    /// Event type and attributes map.
19    pub events: Option<HashMap<String, Vec<String>>>,
20}
21
22impl Event {
23    /// Returns the type associated with this event, if we recognize it.
24    ///
25    /// Returns `None` if we don't yet support this event type.
26    pub fn event_type(&self) -> Option<EventType> {
27        match self.data {
28            EventData::NewBlock { .. } => Some(EventType::NewBlock),
29            EventData::Tx { .. } => Some(EventType::Tx),
30            _ => None,
31        }
32    }
33}
34
35#[derive(Debug, Clone, PartialEq, Eq)]
36pub enum EventData {
37    /// Data of the newly committed block.
38    ///
39    /// Used since CometBFT 0.38.
40    NewBlock {
41        block: Option<Box<Block>>,
42        block_id: block::Id,
43        result_finalize_block: Option<abci::response::FinalizeBlock>,
44    },
45    /// Data of the newly committed block.
46    ///
47    /// Used in CometBFT versions before 0.38.
48    LegacyNewBlock {
49        block: Option<Box<Block>>,
50        result_begin_block: Option<abci::response::BeginBlock>,
51        result_end_block: Option<abci::response::EndBlock>,
52    },
53    Tx {
54        tx_result: TxInfo,
55    },
56    GenericJsonEvent(serde_json::Value),
57}
58
59/// Transaction result info.
60#[derive(Debug, Clone, PartialEq, Eq)]
61pub struct TxInfo {
62    pub height: i64,
63    pub index: Option<i64>,
64    pub tx: Vec<u8>,
65    pub result: TxResult,
66}
67
68/// Transaction result.
69#[derive(Debug, Clone, PartialEq, Eq)]
70pub struct TxResult {
71    pub log: Option<String>,
72    pub gas_wanted: Option<String>,
73    pub gas_used: Option<String>,
74    pub events: Vec<abci::Event>,
75}
76
77/// Serialization helpers for CometBFT 0.34 RPC
78pub mod v0_34 {
79    use super::{Event, EventData, TxInfo, TxResult};
80    use crate::dialect::v0_34::Event as RpcEvent;
81    use crate::prelude::*;
82    use crate::{dialect, serializers, Response};
83    use alloc::collections::BTreeMap as HashMap;
84    use cometbft::Block;
85    use serde::{Deserialize, Serialize};
86
87    #[derive(Serialize, Deserialize, Debug)]
88    pub struct DialectEvent {
89        /// The query that produced the event.
90        pub query: String,
91        /// The data associated with the event.
92        pub data: DialectEventData,
93        /// Event type and attributes map.
94        pub events: Option<HashMap<String, Vec<String>>>,
95    }
96
97    pub type DeEvent = DialectEvent;
98    pub type SerEvent = DialectEvent;
99
100    impl Response for DialectEvent {}
101
102    impl From<DialectEvent> for Event {
103        fn from(msg: DialectEvent) -> Self {
104            Event {
105                query: msg.query,
106                data: msg.data.into(),
107                events: msg.events,
108            }
109        }
110    }
111
112    impl From<Event> for DialectEvent {
113        fn from(msg: Event) -> Self {
114            DialectEvent {
115                query: msg.query,
116                data: msg.data.into(),
117                events: msg.events,
118            }
119        }
120    }
121
122    #[derive(Serialize, Deserialize, Debug)]
123    #[serde(tag = "type", content = "value")]
124    #[allow(clippy::large_enum_variant)]
125    pub enum DialectEventData {
126        #[serde(alias = "tendermint/event/NewBlock")]
127        NewBlock {
128            block: Option<Box<Block>>,
129            result_begin_block: Option<dialect::BeginBlock<RpcEvent>>,
130            result_end_block: Option<dialect::EndBlock<RpcEvent>>,
131        },
132        #[serde(alias = "tendermint/event/Tx")]
133        Tx {
134            #[serde(rename = "TxResult")]
135            tx_result: DialectTxInfo,
136        },
137        GenericJsonEvent(serde_json::Value),
138    }
139
140    impl From<DialectEventData> for EventData {
141        fn from(msg: DialectEventData) -> Self {
142            match msg {
143                DialectEventData::NewBlock {
144                    block,
145                    result_begin_block,
146                    result_end_block,
147                } => EventData::LegacyNewBlock {
148                    block,
149                    result_begin_block: result_begin_block.map(Into::into),
150                    result_end_block: result_end_block.map(Into::into),
151                },
152                DialectEventData::Tx { tx_result } => EventData::Tx {
153                    tx_result: tx_result.into(),
154                },
155                DialectEventData::GenericJsonEvent(v) => EventData::GenericJsonEvent(v),
156            }
157        }
158    }
159
160    impl From<EventData> for DialectEventData {
161        fn from(msg: EventData) -> Self {
162            match msg {
163                EventData::LegacyNewBlock {
164                    block,
165                    result_begin_block,
166                    result_end_block,
167                } => DialectEventData::NewBlock {
168                    block,
169                    result_begin_block: result_begin_block.map(Into::into),
170                    result_end_block: result_end_block.map(Into::into),
171                },
172                // This variant should not be used with 0.34, but we're using
173                // this impl only for the mock server.
174                EventData::NewBlock {
175                    block,
176                    block_id: _,
177                    result_finalize_block: _,
178                } => DialectEventData::NewBlock {
179                    block,
180                    result_begin_block: None,
181                    result_end_block: None,
182                },
183                EventData::Tx { tx_result } => DialectEventData::Tx {
184                    tx_result: tx_result.into(),
185                },
186                EventData::GenericJsonEvent(v) => DialectEventData::GenericJsonEvent(v),
187            }
188        }
189    }
190
191    #[derive(Serialize, Deserialize, Debug)]
192    pub struct DialectTxInfo {
193        #[serde(with = "serializers::from_str")]
194        pub height: i64,
195        pub index: Option<i64>,
196        #[serde(with = "serializers::bytes::base64string")]
197        pub tx: Vec<u8>,
198        pub result: DialectTxResult,
199    }
200
201    impl From<DialectTxInfo> for TxInfo {
202        fn from(msg: DialectTxInfo) -> Self {
203            TxInfo {
204                height: msg.height,
205                index: msg.index,
206                tx: msg.tx,
207                result: msg.result.into(),
208            }
209        }
210    }
211
212    impl From<TxInfo> for DialectTxInfo {
213        fn from(msg: TxInfo) -> Self {
214            DialectTxInfo {
215                height: msg.height,
216                index: msg.index,
217                tx: msg.tx,
218                result: msg.result.into(),
219            }
220        }
221    }
222
223    #[derive(Serialize, Deserialize, Debug)]
224    pub struct DialectTxResult {
225        pub log: Option<String>,
226        pub gas_wanted: Option<String>,
227        pub gas_used: Option<String>,
228        pub events: Vec<RpcEvent>,
229    }
230
231    impl From<DialectTxResult> for TxResult {
232        fn from(msg: DialectTxResult) -> Self {
233            TxResult {
234                log: msg.log,
235                gas_wanted: msg.gas_wanted,
236                gas_used: msg.gas_used,
237                events: msg.events.into_iter().map(Into::into).collect(),
238            }
239        }
240    }
241
242    impl From<TxResult> for DialectTxResult {
243        fn from(msg: TxResult) -> Self {
244            DialectTxResult {
245                log: msg.log,
246                gas_wanted: msg.gas_wanted,
247                gas_used: msg.gas_used,
248                events: msg.events.into_iter().map(Into::into).collect(),
249            }
250        }
251    }
252}
253
254/// Shared serialization/deserialization helpers for the RPC protocol used since CometBFT 0.37
255pub mod v1 {
256    use super::{Event, EventData, TxInfo, TxResult};
257    use crate::prelude::*;
258    use crate::{serializers, Response};
259    use alloc::collections::BTreeMap as HashMap;
260    use cometbft::abci::Event as RpcEvent;
261    use cometbft::{abci, block, Block};
262    use serde::{Deserialize, Serialize};
263
264    #[derive(Deserialize, Debug)]
265    pub struct DeEvent {
266        /// The query that produced the event.
267        pub query: String,
268        /// The data associated with the event.
269        pub data: DeEventData,
270        /// Event type and attributes map.
271        pub events: Option<HashMap<String, Vec<String>>>,
272    }
273
274    impl Response for DeEvent {}
275
276    impl From<DeEvent> for Event {
277        fn from(msg: DeEvent) -> Self {
278            Event {
279                query: msg.query,
280                data: msg.data.into(),
281                events: msg.events,
282            }
283        }
284    }
285
286    /// Helper used to deserialize [`EventData`] for CometBFT RPC since 0.37
287    #[derive(Deserialize, Debug)]
288    #[serde(tag = "type", content = "value")]
289    #[allow(clippy::large_enum_variant)]
290    pub enum DeEventData {
291        #[serde(alias = "tendermint/event/NewBlock")]
292        NewBlock {
293            block: Option<Box<Block>>,
294            #[serde(default)]
295            result_begin_block: Option<abci::response::BeginBlock>,
296            #[serde(default)]
297            result_end_block: Option<abci::response::EndBlock>,
298            #[serde(default)]
299            block_id: Option<block::Id>,
300            #[serde(default)]
301            result_finalize_block: Option<abci::response::FinalizeBlock>,
302        },
303        #[serde(alias = "tendermint/event/Tx")]
304        Tx {
305            #[serde(rename = "TxResult")]
306            tx_result: DialectTxInfo,
307        },
308        GenericJsonEvent(serde_json::Value),
309    }
310
311    impl From<DeEventData> for EventData {
312        fn from(msg: DeEventData) -> Self {
313            match msg {
314                DeEventData::NewBlock {
315                    block,
316                    block_id: Some(block_id),
317                    result_finalize_block,
318                    result_begin_block: _,
319                    result_end_block: _,
320                } => EventData::NewBlock {
321                    block,
322                    block_id,
323                    result_finalize_block,
324                },
325                DeEventData::NewBlock {
326                    block,
327                    result_begin_block,
328                    result_end_block,
329                    block_id: None,
330                    result_finalize_block: _,
331                } => EventData::LegacyNewBlock {
332                    block,
333                    result_begin_block: result_begin_block.map(Into::into),
334                    result_end_block: result_end_block.map(Into::into),
335                },
336                DeEventData::Tx { tx_result } => EventData::Tx {
337                    tx_result: tx_result.into(),
338                },
339                DeEventData::GenericJsonEvent(v) => EventData::GenericJsonEvent(v),
340            }
341        }
342    }
343
344    #[derive(Serialize, Deserialize, Debug)]
345    pub struct DialectTxInfo {
346        #[serde(with = "serializers::from_str")]
347        pub height: i64,
348        pub index: Option<i64>,
349        #[serde(with = "serializers::bytes::base64string")]
350        pub tx: Vec<u8>,
351        pub result: DialectTxResult,
352    }
353
354    impl From<DialectTxInfo> for TxInfo {
355        fn from(msg: DialectTxInfo) -> Self {
356            TxInfo {
357                height: msg.height,
358                index: msg.index,
359                tx: msg.tx,
360                result: msg.result.into(),
361            }
362        }
363    }
364
365    impl From<TxInfo> for DialectTxInfo {
366        fn from(msg: TxInfo) -> Self {
367            DialectTxInfo {
368                height: msg.height,
369                index: msg.index,
370                tx: msg.tx,
371                result: msg.result.into(),
372            }
373        }
374    }
375
376    #[derive(Serialize, Deserialize, Debug)]
377    pub struct DialectTxResult {
378        pub log: Option<String>,
379        pub gas_wanted: Option<String>,
380        pub gas_used: Option<String>,
381        pub events: Vec<RpcEvent>,
382    }
383
384    impl From<DialectTxResult> for TxResult {
385        fn from(msg: DialectTxResult) -> Self {
386            TxResult {
387                log: msg.log,
388                gas_wanted: msg.gas_wanted,
389                gas_used: msg.gas_used,
390                events: msg.events.into_iter().map(Into::into).collect(),
391            }
392        }
393    }
394
395    impl From<TxResult> for DialectTxResult {
396        fn from(msg: TxResult) -> Self {
397            DialectTxResult {
398                log: msg.log,
399                gas_wanted: msg.gas_wanted,
400                gas_used: msg.gas_used,
401                events: msg.events.into_iter().map(Into::into).collect(),
402            }
403        }
404    }
405}
406
407/// Serialization helpers for the RPC protocol specific to CometBFT 0.37
408pub mod v0_37 {
409    use super::{Event, EventData};
410    use crate::prelude::*;
411    use alloc::collections::BTreeMap as HashMap;
412    use cometbft::{abci, Block};
413    use serde::Serialize;
414
415    pub use super::v1::*;
416
417    #[derive(Serialize, Debug)]
418    pub struct SerEvent {
419        /// The query that produced the event.
420        pub query: String,
421        /// The data associated with the event.
422        pub data: SerEventData,
423        /// Event type and attributes map.
424        pub events: Option<HashMap<String, Vec<String>>>,
425    }
426
427    impl From<Event> for SerEvent {
428        fn from(msg: Event) -> Self {
429            SerEvent {
430                query: msg.query,
431                data: msg.data.into(),
432                events: msg.events,
433            }
434        }
435    }
436
437    #[derive(Serialize, Debug)]
438    #[serde(tag = "type", content = "value")]
439    pub enum SerEventData {
440        #[serde(alias = "tendermint/event/NewBlock")]
441        NewBlock {
442            block: Option<Box<Block>>,
443            result_begin_block: Option<abci::response::BeginBlock>,
444            result_end_block: Option<abci::response::EndBlock>,
445        },
446        #[serde(alias = "tendermint/event/Tx")]
447        Tx {
448            #[serde(rename = "TxResult")]
449            tx_result: DialectTxInfo,
450        },
451        GenericJsonEvent(serde_json::Value),
452    }
453
454    impl From<EventData> for SerEventData {
455        fn from(msg: EventData) -> Self {
456            match msg {
457                // This variant should not be used in 0.37, but the conversion
458                // must be infallible.
459                EventData::NewBlock {
460                    block,
461                    block_id: _,
462                    result_finalize_block: _,
463                } => SerEventData::NewBlock {
464                    block,
465                    result_begin_block: None,
466                    result_end_block: None,
467                },
468                EventData::LegacyNewBlock {
469                    block,
470                    result_begin_block,
471                    result_end_block,
472                } => SerEventData::NewBlock {
473                    block,
474                    result_begin_block: result_begin_block.map(Into::into),
475                    result_end_block: result_end_block.map(Into::into),
476                },
477                EventData::Tx { tx_result } => SerEventData::Tx {
478                    tx_result: tx_result.into(),
479                },
480                EventData::GenericJsonEvent(v) => SerEventData::GenericJsonEvent(v),
481            }
482        }
483    }
484}
485
486/// Serialization helpers for the RPC protocol specific to CometBFT 0.38
487pub mod v0_38 {
488    use super::{Event, EventData};
489    use crate::prelude::*;
490    use alloc::collections::BTreeMap as HashMap;
491    use cometbft::{abci, block, Block};
492    use serde::Serialize;
493
494    pub use super::v1::*;
495
496    #[derive(Serialize, Debug)]
497    pub struct SerEvent {
498        /// The query that produced the event.
499        pub query: String,
500        /// The data associated with the event.
501        pub data: SerEventData,
502        /// Event type and attributes map.
503        pub events: Option<HashMap<String, Vec<String>>>,
504    }
505
506    impl From<Event> for SerEvent {
507        fn from(msg: Event) -> Self {
508            SerEvent {
509                query: msg.query,
510                data: msg.data.into(),
511                events: msg.events,
512            }
513        }
514    }
515
516    #[derive(Serialize, Debug)]
517    #[serde(tag = "type", content = "value")]
518    pub enum SerEventData {
519        #[serde(alias = "tendermint/event/NewBlock")]
520        NewBlock {
521            block: Option<Box<Block>>,
522            block_id: block::Id,
523            result_finalize_block: Option<abci::response::FinalizeBlock>,
524        },
525        #[serde(alias = "tendermint/event/Tx")]
526        Tx {
527            #[serde(rename = "TxResult")]
528            tx_result: DialectTxInfo,
529        },
530        GenericJsonEvent(serde_json::Value),
531    }
532
533    impl From<EventData> for SerEventData {
534        fn from(msg: EventData) -> Self {
535            match msg {
536                EventData::NewBlock {
537                    block,
538                    block_id,
539                    result_finalize_block,
540                } => SerEventData::NewBlock {
541                    block,
542                    block_id,
543                    result_finalize_block,
544                },
545                // This variant should not be used in 0.38, but the conversion
546                // must be infallible.
547                EventData::LegacyNewBlock {
548                    block,
549                    result_begin_block: _,
550                    result_end_block: _,
551                } => SerEventData::NewBlock {
552                    block,
553                    block_id: Default::default(),
554                    result_finalize_block: None,
555                },
556                EventData::Tx { tx_result } => SerEventData::Tx {
557                    tx_result: tx_result.into(),
558                },
559                EventData::GenericJsonEvent(v) => SerEventData::GenericJsonEvent(v),
560            }
561        }
562    }
563}