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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
//! Auxiliary information exposed by snapp smart contract execution.
//!
//! The format of this data is opaque to the Mina protocol, and is defined by each individual smart
//! contract. A smart contract may emit zero or more 'events', which are communicated with the
//! transaction when it is broadcast over the Mina network.
//!
//! These 'events' are collapsed into a [single hash](`EventsHash::of_events`) for the purposes of
//! the zero-knowledge proofs, and this hash is unused by the transaction logic.
//!
//! An event may be used to expose information that is useful or important to make publically
//! available, but without storing the data in accounts on-chain.
//!
//! Nodes may make this data available for transactions in their pool or from within blocks.
//! Archive nodes and similar services may make older historical event data available.
//!
//! # Example
//!
//! A snapp smart contract could use a [merkle tree](https://en.wikipedia.org/wiki/Merkle_tree) to
//! store more data than it can fit in its 8 general purpose registers. Usually, when a snap
//! account updates the root of its merkle tree, there will be no way to know what the new contents
//! of the merkle tree are; only the cryptographic 'root hash' is publically available.
//!
//! In order to update the merkle tree while making the new contents publically known, a snapp
//! developer can emit an event containing the new data and its position as an event, and any other
//! member of the network can look at this event to discover the new contents.
//!
//! Consider for example the tree containing 8 values `A, B, C, D, E, F, G, H`.
//! ```text
//!                             H7=Hash(H5, H6)
//!                            /               \
//!             H5=Hash(H1, H2)                 H6=Hash(H3, H4)
//!            /               \               /               \
//!   H1=Hash(A, B)     H2=Hash(C, D)     H3=Hash(E, F)     H4=Hash(G, H)
//!  /             \   /             \   /             \   /             \
//! A               B C               D E               F G               H
//! ```
//! `H7` is used as the 'root hash' of the tree.
//!
//! A snapp might update the tree at the 5th position (0-indexed, replacing `F` with `I`),
//! generating the resulting tree
//! ```text
//!                             H7'=Hash(H5, H6')
//!                            /               \
//!             H5=Hash(H1, H2)                 H6'=Hash(H3', H4)
//!            /               \               /               \
//!   H1=Hash(A, B)     H2=Hash(C, D)     H3'=Hash(E, I)     H4=Hash(G, H)
//!  /             \   /             \   /             \   /             \
//! A               B C               D E               I G               H
//! ```
//! and storing its new 'root hash' `H7'` in its `app_state`.
//!
//! The snapp can emit an event that encodes the new data to be placed in the tree, for example as
//! `{data: I, position: 5}`, and then any user who previously knew the contents of the tree can
//! update their local copy to match, and can continue to interact with the snapp's data by
//! querying/updating the new tree.
use crate::primitives::*;

/// A single event emitted by a snapp. See [`crate::event`] for more.
pub struct Event<'a>(pub &'a [Fp]);

impl<'a> Event<'a> {
    /// Compute the hash input of a single  event by calling [`HashInput::add_field`] for each
    /// field element in the event.
    /// ```rust
    /// let mut hash_input = HashInput::empty();
    /// for event_part in event.0.iter() {
    ///     HashInput::add_field(&mut hash_input, *event_part)
    /// }
    /// hash_input
    /// ```
    pub fn hash_input(event: &Event<'a>) -> HashInput {
        let mut hash_input = HashInput::empty();
        for event_part in event.0.iter() {
            HashInput::add_field(&mut hash_input, *event_part)
        }
        hash_input
    }

    /// Compute the hash of a single event, using [`Event::hash_input`] and
    /// [`HashPrefixState::event`].
    /// ```rust
    /// Hash::compute_hash(HashPrefixState::event(), Event::hash_input(event))
    /// ```
    pub fn hash(event: &Event<'a>) -> Hash {
        Hash::compute_hash(HashPrefixState::event(), Event::hash_input(event))
    }
}

/// A list of zero or more events, which may be emitted by a snapp. See [`crate::event`] for more.
pub type Events<'a> = [Event<'a>];

/// A hash representing a list of  events.
#[derive(Copy, Clone)]
pub struct EventsHash(pub Fp);

impl EventsHash {
    /// The hash representing an empty list of  events. Equivalent to
    /// [`Hash::empty_events`] wrapped by [`EventsHash`].
    pub fn empty() -> EventsHash {
        EventsHash(Hash::empty_events())
    }

    /// The hash formed by hash-consing `event_hash` with `events_hash`.
    ///
    /// Creates a [`HashInput`] formed by adding the `events_hash` and
    /// `events_hash` field elements in order, then [computing](`Hash::compute_hash`) the
    /// resulting hash with [`HashPrefixState::events_list`].
    /// ```rust
    /// let mut hash_input = HashInput::empty();
    /// HashInput::add_field(&mut hash_input, event_hash);
    /// HashInput::add_field(&mut hash_input, events_hash.0);
    ///
    /// EventsHash(Hash::compute_hash(
    ///     HashPrefixState::events_list(),
    ///     hash_input,
    /// ))
    /// ```
    pub fn push_event_hash(event_hash: Hash, events_hash: EventsHash) -> EventsHash {
        let mut hash_input = HashInput::empty();
        HashInput::add_field(&mut hash_input, event_hash);
        HashInput::add_field(&mut hash_input, events_hash.0);
        EventsHash(Hash::compute_hash(
            HashPrefixState::events_list(),
            hash_input,
        ))
    }

    /// The hash formed by hash-consing `event`'s hash with `events_hash`.
    ///
    /// Equivalent to calling [`EventsHash::push_event`] on the result of
    /// [`Event::hash`]:
    /// ```rust
    /// EventsHash::push_event_hash(Event::hash(event), events_hash)
    /// ```
    pub fn push_event(event: &Event, events_hash: EventsHash) -> EventsHash {
        EventsHash::push_event_hash(Event::hash(event), events_hash)
    }

    /// Compute the hash of `events` by starting with
    /// [`empty`](`EventsHash::empty`) and calling
    /// [`push_event`](`EventsHash::push_event`) on each event in turn.
    /// ```rust
    /// let mut events_hash = EventsHash::empty();
    /// for event in events.iter() {
    ///     events_hash =
    ///         EventsHash::push_event(event, events_hash)
    /// }
    /// events_hash
    /// ```
    pub fn of_events(events: &Events) -> EventsHash {
        let mut events_hash = EventsHash::empty();
        for event in events.iter() {
            events_hash = EventsHash::push_event(event, events_hash)
        }
        events_hash
    }
}