Skip to main content

diem_types/
contract_event.rs

1// Copyright (c) The Diem Core Contributors
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::{
5    account_config::{
6        AdminTransactionEvent, BaseUrlRotationEvent, BurnEvent, CancelBurnEvent,
7        ComplianceKeyRotationEvent, CreateAccountEvent, MintEvent, NewBlockEvent, NewEpochEvent,
8        PreburnEvent, ReceivedMintEvent, ReceivedPaymentEvent, SentPaymentEvent,
9        ToXDXExchangeRateUpdateEvent, VASPDomainEvent,
10    },
11    event::EventKey,
12    ledger_info::LedgerInfo,
13    proof::EventProof,
14    transaction::{TransactionInfoTrait, Version},
15};
16use anyhow::{ensure, Context, Error, Result};
17use diem_crypto::hash::CryptoHash;
18use diem_crypto_derive::{BCSCryptoHash, CryptoHasher};
19use move_core_types::{language_storage::TypeTag, move_resource::MoveStructType};
20
21#[cfg(any(test, feature = "fuzzing"))]
22use proptest_derive::Arbitrary;
23use serde::{Deserialize, Serialize};
24use std::{convert::TryFrom, ops::Deref};
25
26/// Support versioning of the data structure.
27#[derive(Hash, Clone, Eq, PartialEq, Serialize, Deserialize, CryptoHasher, BCSCryptoHash)]
28pub enum ContractEvent {
29    V0(ContractEventV0),
30}
31
32impl ContractEvent {
33    pub fn new(
34        key: EventKey,
35        sequence_number: u64,
36        type_tag: TypeTag,
37        event_data: Vec<u8>,
38    ) -> Self {
39        ContractEvent::V0(ContractEventV0::new(
40            key,
41            sequence_number,
42            type_tag,
43            event_data,
44        ))
45    }
46}
47
48// Temporary hack to avoid massive changes, it won't work when new variant comes and needs proper
49// dispatch at that time.
50impl Deref for ContractEvent {
51    type Target = ContractEventV0;
52
53    fn deref(&self) -> &Self::Target {
54        match self {
55            ContractEvent::V0(event) => event,
56        }
57    }
58}
59
60/// Entry produced via a call to the `emit_event` builtin.
61#[derive(Hash, Clone, Eq, PartialEq, Serialize, Deserialize, CryptoHasher)]
62pub struct ContractEventV0 {
63    /// The unique key that the event was emitted to
64    key: EventKey,
65    /// The number of messages that have been emitted to the path previously
66    sequence_number: u64,
67    /// The type of the data
68    type_tag: TypeTag,
69    /// The data payload of the event
70    #[serde(with = "serde_bytes")]
71    event_data: Vec<u8>,
72}
73
74impl ContractEventV0 {
75    pub fn new(
76        key: EventKey,
77        sequence_number: u64,
78        type_tag: TypeTag,
79        event_data: Vec<u8>,
80    ) -> Self {
81        Self {
82            key,
83            sequence_number,
84            type_tag,
85            event_data,
86        }
87    }
88
89    pub fn key(&self) -> &EventKey {
90        &self.key
91    }
92
93    pub fn sequence_number(&self) -> u64 {
94        self.sequence_number
95    }
96
97    pub fn event_data(&self) -> &[u8] {
98        &self.event_data
99    }
100
101    pub fn type_tag(&self) -> &TypeTag {
102        &self.type_tag
103    }
104}
105
106impl TryFrom<&ContractEvent> for SentPaymentEvent {
107    type Error = Error;
108
109    fn try_from(event: &ContractEvent) -> Result<Self> {
110        if event.type_tag != TypeTag::Struct(SentPaymentEvent::struct_tag()) {
111            anyhow::bail!("Expected Sent Payment")
112        }
113        Self::try_from_bytes(&event.event_data)
114    }
115}
116
117impl TryFrom<&ContractEvent> for ReceivedPaymentEvent {
118    type Error = Error;
119
120    fn try_from(event: &ContractEvent) -> Result<Self> {
121        if event.type_tag != TypeTag::Struct(ReceivedPaymentEvent::struct_tag()) {
122            anyhow::bail!("Expected Received Payment")
123        }
124        Self::try_from_bytes(&event.event_data)
125    }
126}
127
128impl TryFrom<&ContractEvent> for ToXDXExchangeRateUpdateEvent {
129    type Error = Error;
130
131    fn try_from(event: &ContractEvent) -> Result<Self> {
132        if event.type_tag != TypeTag::Struct(ToXDXExchangeRateUpdateEvent::struct_tag()) {
133            anyhow::bail!("Expected ToXDXExchangeRateUpdateEvent")
134        }
135        Self::try_from_bytes(&event.event_data)
136    }
137}
138
139impl TryFrom<&ContractEvent> for MintEvent {
140    type Error = Error;
141
142    fn try_from(event: &ContractEvent) -> Result<Self> {
143        if event.type_tag != TypeTag::Struct(MintEvent::struct_tag()) {
144            anyhow::bail!("Expected MintEvent")
145        }
146        Self::try_from_bytes(&event.event_data)
147    }
148}
149
150impl TryFrom<&ContractEvent> for ReceivedMintEvent {
151    type Error = Error;
152
153    fn try_from(event: &ContractEvent) -> Result<Self> {
154        if event.type_tag != TypeTag::Struct(ReceivedMintEvent::struct_tag()) {
155            anyhow::bail!("Expected ReceivedMintEvent")
156        }
157        Self::try_from_bytes(&event.event_data)
158    }
159}
160
161impl TryFrom<&ContractEvent> for BurnEvent {
162    type Error = Error;
163
164    fn try_from(event: &ContractEvent) -> Result<Self> {
165        if event.type_tag != TypeTag::Struct(BurnEvent::struct_tag()) {
166            anyhow::bail!("Expected BurnEvent")
167        }
168        Self::try_from_bytes(&event.event_data)
169    }
170}
171
172impl TryFrom<&ContractEvent> for PreburnEvent {
173    type Error = Error;
174
175    fn try_from(event: &ContractEvent) -> Result<Self> {
176        if event.type_tag != TypeTag::Struct(PreburnEvent::struct_tag()) {
177            anyhow::bail!("Expected PreburnEvent")
178        }
179        Self::try_from_bytes(&event.event_data)
180    }
181}
182
183impl TryFrom<&ContractEvent> for CancelBurnEvent {
184    type Error = Error;
185
186    fn try_from(event: &ContractEvent) -> Result<Self> {
187        if event.type_tag != TypeTag::Struct(CancelBurnEvent::struct_tag()) {
188            anyhow::bail!("Expected CancelBurnEvent")
189        }
190        Self::try_from_bytes(&event.event_data)
191    }
192}
193
194impl TryFrom<&ContractEvent> for AdminTransactionEvent {
195    type Error = Error;
196
197    fn try_from(event: &ContractEvent) -> Result<Self> {
198        if event.type_tag != TypeTag::Struct(Self::struct_tag()) {
199            anyhow::bail!("Expected AdminTransactionEvent")
200        }
201        Self::try_from_bytes(&event.event_data)
202    }
203}
204
205impl TryFrom<&ContractEvent> for NewBlockEvent {
206    type Error = Error;
207
208    fn try_from(event: &ContractEvent) -> Result<Self> {
209        if event.type_tag != TypeTag::Struct(Self::struct_tag()) {
210            anyhow::bail!("Expected NewBlockEvent")
211        }
212        Self::try_from_bytes(&event.event_data)
213    }
214}
215
216impl TryFrom<&ContractEvent> for NewEpochEvent {
217    type Error = Error;
218
219    fn try_from(event: &ContractEvent) -> Result<Self> {
220        if event.type_tag != TypeTag::Struct(Self::struct_tag()) {
221            anyhow::bail!("Expected NewEpochEvent")
222        }
223        Self::try_from_bytes(&event.event_data)
224    }
225}
226
227impl TryFrom<&ContractEvent> for ComplianceKeyRotationEvent {
228    type Error = Error;
229    fn try_from(event: &ContractEvent) -> Result<Self> {
230        if event.type_tag != TypeTag::Struct(Self::struct_tag()) {
231            anyhow::bail!("Expected ComplianceKeyRotationEvent")
232        }
233        Self::try_from_bytes(&event.event_data)
234    }
235}
236
237impl TryFrom<&ContractEvent> for BaseUrlRotationEvent {
238    type Error = Error;
239    fn try_from(event: &ContractEvent) -> Result<Self> {
240        if event.type_tag != TypeTag::Struct(Self::struct_tag()) {
241            anyhow::bail!("Expected BaseUrlRotationEvent")
242        }
243        Self::try_from_bytes(&event.event_data)
244    }
245}
246
247impl TryFrom<&ContractEvent> for CreateAccountEvent {
248    type Error = Error;
249    fn try_from(event: &ContractEvent) -> Result<Self> {
250        if event.type_tag != TypeTag::Struct(Self::struct_tag()) {
251            anyhow::bail!("Expected CreateAccountEvent")
252        }
253        Self::try_from_bytes(&event.event_data)
254    }
255}
256
257impl TryFrom<&ContractEvent> for VASPDomainEvent {
258    type Error = Error;
259    fn try_from(event: &ContractEvent) -> Result<Self> {
260        if event.type_tag != TypeTag::Struct(Self::struct_tag()) {
261            anyhow::bail!("Expected VASPDomainEvent")
262        }
263        Self::try_from_bytes(&event.event_data)
264    }
265}
266
267impl std::fmt::Debug for ContractEvent {
268    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
269        write!(
270            f,
271            "ContractEvent {{ key: {:?}, index: {:?}, type: {:?}, event_data: {:?} }}",
272            self.key,
273            self.sequence_number,
274            self.type_tag,
275            hex::encode(&self.event_data)
276        )
277    }
278}
279
280impl std::fmt::Display for ContractEvent {
281    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
282        if let Ok(payload) = SentPaymentEvent::try_from(self) {
283            write!(
284                f,
285                "ContractEvent {{ key: {}, index: {:?}, type: {:?}, event_data: {:?} }}",
286                self.key, self.sequence_number, self.type_tag, payload,
287            )
288        } else if let Ok(payload) = ReceivedPaymentEvent::try_from(self) {
289            write!(
290                f,
291                "ContractEvent {{ key: {}, index: {:?}, type: {:?}, event_data: {:?} }}",
292                self.key, self.sequence_number, self.type_tag, payload,
293            )
294        } else {
295            write!(f, "{:?}", self)
296        }
297    }
298}
299
300#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
301#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
302pub struct EventWithProof<T> {
303    pub transaction_version: u64, // Should be `Version`
304    pub event_index: u64,
305    pub event: ContractEvent,
306    pub proof: EventProof<T>,
307}
308
309impl<T: TransactionInfoTrait> std::fmt::Display for EventWithProof<T> {
310    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
311        write!(
312            f,
313            "EventWithProof {{ \n\ttransaction_version: {}, \n\tevent_index: {}, \
314             \n\tevent: {}, \n\tproof: {:?} \n}}",
315            self.transaction_version, self.event_index, self.event, self.proof
316        )
317    }
318}
319
320impl<T: TransactionInfoTrait> EventWithProof<T> {
321    /// Constructor.
322    pub fn new(
323        transaction_version: Version,
324        event_index: u64,
325        event: ContractEvent,
326        proof: EventProof<T>,
327    ) -> Self {
328        Self {
329            transaction_version,
330            event_index,
331            event,
332            proof,
333        }
334    }
335
336    /// Verifies the event with the proof, both carried by `self`.
337    ///
338    /// Two things are ensured if no error is raised:
339    ///   1. This event exists in the ledger represented by `ledger_info`.
340    ///   2. And this event has the same `event_key`, `sequence_number`, `transaction_version`,
341    /// and `event_index` as indicated in the parameter list. If any of these parameter is unknown
342    /// to the call site and is supposed to be informed by this struct, get it from the struct
343    /// itself, such as: `event_with_proof.event.access_path()`, `event_with_proof.event_index()`,
344    /// etc.
345    pub fn verify(
346        &self,
347        ledger_info: &LedgerInfo,
348        event_key: &EventKey,
349        sequence_number: u64,
350        transaction_version: Version,
351        event_index: u64,
352    ) -> Result<()> {
353        ensure!(
354            self.event.key() == event_key,
355            "Event key ({}) not expected ({}).",
356            self.event.key(),
357            *event_key,
358        );
359        ensure!(
360            self.event.sequence_number == sequence_number,
361            "Sequence number ({}) not expected ({}).",
362            self.event.sequence_number(),
363            sequence_number,
364        );
365        ensure!(
366            self.transaction_version == transaction_version,
367            "Transaction version ({}) not expected ({}).",
368            self.transaction_version,
369            transaction_version,
370        );
371        ensure!(
372            self.event_index == event_index,
373            "Event index ({}) not expected ({}).",
374            self.event_index,
375            event_index,
376        );
377
378        self.proof.verify(
379            ledger_info,
380            self.event.hash(),
381            transaction_version,
382            event_index,
383        )?;
384
385        Ok(())
386    }
387}
388
389/// The response type for `get_event_by_version_with_proof`, which contains lower
390/// and upper bound events surrounding the requested version along with proofs
391/// for each event.
392///
393/// ### Why do we need two events?
394///
395/// If we could always get the event count _at the requested event_version_, we
396/// could return only the lower bound event. With the event count we could verify
397/// that the returned event is actually the latest event at the historical ledger
398/// view just by checking that `event.sequence_number == event_count`.
399///
400/// Unfortunately, the event count is only (verifiably) accessible via the
401/// on-chain state. While we can easily acquire the event count near the chain
402/// HEAD, historical event counts (at versions below HEAD for more than the prune
403/// window) may be inaccessible after most non-archival fullnodes have pruned past
404/// that version.
405///
406/// ### Including the Upper Bound Event
407///
408/// In contrast, if we also return the upper bound event, then we can always
409/// verify the request even if the version is past the prune window and we don't
410/// know the event_count (at event_version). The upper bound event lets us prove
411/// that there is no untransmitted event that is actually closer to the requested
412/// event_version than the lower bound.
413///
414/// For example, consider the case where there are three events at versions 10,
415/// 20, and 30. A client asks for the latest event at or below version 25. If we
416/// just returned the lower bound event, then a malicious server could return
417/// the event at version 10; the client would not be able to distinguish this
418/// response from the correct response without the event count (2) at version 25.
419///
420/// If we also return the upper bound event (the event at version 30), the client
421/// can verify that the upper bound is the next event after the lower bound and
422/// that the upper bound comes after their requested version. This proves that
423/// the lower bound is actually the latest event at or below their requested
424/// version.
425#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
426#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
427pub struct EventByVersionWithProof<T> {
428    pub lower_bound_incl: Option<EventWithProof<T>>,
429    pub upper_bound_excl: Option<EventWithProof<T>>,
430}
431
432impl<T: TransactionInfoTrait> EventByVersionWithProof<T> {
433    pub fn new(
434        lower_bound_incl: Option<EventWithProof<T>>,
435        upper_bound_excl: Option<EventWithProof<T>>,
436    ) -> Self {
437        Self {
438            lower_bound_incl,
439            upper_bound_excl,
440        }
441    }
442
443    /// Verify that the `lower_bound_incl` [`EventWithProof`] is the latest event
444    /// at or below the requested `event_version`.
445    ///
446    /// The `ledger_info` is the client's latest know ledger info (will be near
447    /// chain HEAD if the client is synced).
448    ///
449    /// The `latest_event_count` is the event count at the `ledger_info` version
450    /// (not the `event_version`) and is needed to verify the empty event stream
451    /// and version after last event cases. In some select instances
452    /// (e.g. [`NewBlockEvent`]s) we can determine these cases more efficiently
453    /// and so this parameter is left optional.
454    pub fn verify(
455        &self,
456        ledger_info: &LedgerInfo,
457        event_key: &EventKey,
458        latest_event_count: Option<u64>,
459        event_version: Version,
460    ) -> Result<()> {
461        ensure!(
462            event_version <= ledger_info.version(),
463            "request event_version {} must be <= LedgerInfo version {}",
464            event_version,
465            ledger_info.version(),
466        );
467
468        // If the verifier didn't provide a latest_event_count, choose a value
469        // that will pass the checks in each case.
470        let latest_event_count = latest_event_count.unwrap_or_else(|| {
471            let upper = self.upper_bound_excl.as_ref();
472            let lower = self.lower_bound_incl.as_ref();
473            upper /* (None, Some), (Some, Some) */
474                .or(lower) /* (Some, None) */
475                .map(|proof| proof.event.sequence_number.saturating_add(1))
476                .unwrap_or(0) /* (None, None) */
477        });
478
479        match (&self.lower_bound_incl, &self.upper_bound_excl) {
480            // no events at all yet
481            (None, None) => {
482                ensure!(latest_event_count == 0);
483            }
484            // event_version comes before first ever event, so in the range: [0, event_0.version)
485            //
486            // event_version
487            //      v
488            // |---------|
489            //            (event_0)----->(event_1)---->
490            (None, Some(first_event)) => {
491                let txn_version = first_event.transaction_version;
492                let seq_num = first_event.event.sequence_number;
493                ensure!(event_version < txn_version);
494                ensure!(seq_num == 0);
495                ensure!(latest_event_count > 0);
496
497                first_event
498                    .verify(
499                        ledger_info,
500                        event_key,
501                        seq_num,
502                        first_event.transaction_version,
503                        first_event.event_index,
504                    )
505                    .context("failed to verify first event")?;
506            }
507            // event_version is between two events, specifically, it must be in
508            // the range: [event_i.version, event_{i+1}.version)
509            //
510            //              event_version
511            //                    v
512            //            |---------------|
513            //      ----->(event_{i})----->(event_{i+1})----->
514            (Some(lower_bound_incl), Some(upper_bound_excl)) => {
515                ensure!(lower_bound_incl.transaction_version <= event_version);
516                ensure!(event_version < upper_bound_excl.transaction_version);
517
518                let start_seq_num = lower_bound_incl.event.sequence_number;
519                let end_seq_num = upper_bound_excl.event.sequence_number;
520                ensure!(start_seq_num.saturating_add(1) == end_seq_num);
521                ensure!(latest_event_count > end_seq_num);
522
523                lower_bound_incl
524                    .verify(
525                        ledger_info,
526                        event_key,
527                        start_seq_num,
528                        lower_bound_incl.transaction_version,
529                        lower_bound_incl.event_index,
530                    )
531                    .context("failed to verify lower bound event")?;
532                upper_bound_excl
533                    .verify(
534                        ledger_info,
535                        event_key,
536                        end_seq_num,
537                        upper_bound_excl.transaction_version,
538                        upper_bound_excl.event_index,
539                    )
540                    .context("failed to verify upper bound event")?;
541            }
542            // event_version is after the latest event, meaning it's in the range:
543            // (event_N.version, ledger_version]
544            //
545            //                         event_version
546            //                               v
547            //                       |----------------->
548            //      ----->(event_{N})
549            (Some(latest_event), None) => {
550                let txn_version = latest_event.transaction_version;
551                let seq_num = latest_event.event.sequence_number;
552                ensure!(txn_version <= event_version);
553                ensure!(seq_num.saturating_add(1) == latest_event_count);
554                latest_event
555                    .verify(
556                        ledger_info,
557                        event_key,
558                        seq_num,
559                        txn_version,
560                        latest_event.event_index,
561                    )
562                    .context("failed to verify latest event")?;
563            }
564        }
565
566        Ok(())
567    }
568}
569
570pub mod default_protocol {
571    use crate::transaction::TransactionInfo;
572
573    pub type EventWithProof = super::EventWithProof<TransactionInfo>;
574    pub type EventByVersionWithProof = super::EventByVersionWithProof<TransactionInfo>;
575}