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
}
}