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 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
/// A full [*Cosmos SDK* event].
///
/// This version uses string attributes (similar to [*Cosmos SDK* StringEvent]),
/// which then get magically converted to bytes for Tendermint somewhere between
/// the Rust-Go interface, JSON deserialization and the `NewEvent` call in Cosmos SDK.
///
/// [*Cosmos SDK* event]: https://docs.cosmos.network/main/core/events.html
/// [*Cosmos SDK* StringEvent]: https://github.com/cosmos/cosmos-sdk/blob/v0.42.5/proto/cosmos/base/abci/v1beta1/abci.proto#L56-L70
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
#[non_exhaustive]
pub struct Event {
/// The event type. This is renamed to "ty" because "type" is reserved in Rust. This sucks, we know.
#[serde(rename = "type")]
pub ty: String,
/// The attributes to be included in the event.
///
/// You can learn more about these from [*Cosmos SDK* docs].
///
/// [*Cosmos SDK* docs]: https://docs.cosmos.network/main/core/events.html
pub attributes: Vec<Attribute>,
}
impl Event {
/// Create a new event with the given type and an empty list of attributes.
pub fn new(ty: impl Into<String>) -> Self {
Event {
ty: ty.into(),
attributes: Vec::with_capacity(10),
}
}
/// Add an ENCRYPTED attribute to the event.
/// Only the transaction sender can decrypt it.
pub fn add_attribute(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.attributes.push(Attribute {
key: key.into(),
value: value.into(),
encrypted: true,
});
self
}
/// Add a NON-ENCRYPTED attribute to the event.
/// This key-value pair will be a public on-chain output for the transaction.
/// This is a good way of indexing public contract data on-chain.
pub fn add_attribute_plaintext(
mut self,
key: impl Into<String>,
value: impl Into<String>,
) -> Self {
self.attributes.push(Attribute {
key: key.into(),
value: value.into(),
encrypted: false,
});
self
}
/// Bulk add attributes to the event.
///
/// Anything that can be turned into an iterator and yields something
/// that can be converted into an `Attribute` is accepted.
pub fn add_attributes<A: Into<Attribute>>(
mut self,
attrs: impl IntoIterator<Item = A>,
) -> Self {
self.attributes.extend(attrs.into_iter().map(A::into));
self
}
}
/// Return true
///
/// Only used for serde annotations
fn bool_true() -> bool {
true
}
/// An key value pair that is used in the context of event attributes in logs
#[derive(Serialize, Deserialize, Clone, Default, Debug, PartialEq, Eq, JsonSchema)]
pub struct Attribute {
pub key: String,
pub value: String,
/// nonstandard late addition, thus optional and only used in deserialization.
/// The contracts may return this in newer versions that support distinguishing
/// encrypted and plaintext logs. We naturally default to encrypted logs.
#[serde(default = "bool_true")]
pub encrypted: bool,
}
impl Attribute {
/// Creates a new ENCRYPTED Attribute. `attr` is just an alias for this.
pub fn new(key: impl Into<String>, value: impl Into<String>) -> Self {
let key = key.into();
#[cfg(debug_assertions)]
if key.starts_with('_') {
panic!(
"attribute key `{}` is invalid - keys starting with an underscore are reserved",
key
);
}
Self {
key,
value: value.into(),
encrypted: true,
}
}
/// Creates a new NON-ENCRYPTED Attribute. `attr_plaintext` is just an alias for this.
pub fn new_plaintext(key: impl Into<String>, value: impl Into<String>) -> Self {
let key = key.into();
#[cfg(debug_assertions)]
if key.starts_with('_') {
panic!(
"attribute key `{}` is invalid - keys starting with an underscore are reserved",
key
);
}
Self {
key,
value: value.into(),
encrypted: false,
}
}
}
impl<K: Into<String>, V: Into<String>> From<(K, V)> for Attribute {
fn from((k, v): (K, V)) -> Self {
Attribute::new(k, v)
}
}
impl<K: AsRef<str>, V: AsRef<str>> PartialEq<(K, V)> for Attribute {
fn eq(&self, (k, v): &(K, V)) -> bool {
(self.key.as_str(), self.value.as_str()) == (k.as_ref(), v.as_ref())
}
}
impl<K: AsRef<str>, V: AsRef<str>> PartialEq<Attribute> for (K, V) {
fn eq(&self, attr: &Attribute) -> bool {
attr == self
}
}
impl<K: AsRef<str>, V: AsRef<str>> PartialEq<(K, V)> for &Attribute {
fn eq(&self, (k, v): &(K, V)) -> bool {
(self.key.as_str(), self.value.as_str()) == (k.as_ref(), v.as_ref())
}
}
impl<K: AsRef<str>, V: AsRef<str>> PartialEq<&Attribute> for (K, V) {
fn eq(&self, attr: &&Attribute) -> bool {
attr == self
}
}
impl PartialEq<Attribute> for &Attribute {
fn eq(&self, rhs: &Attribute) -> bool {
*self == rhs
}
}
impl PartialEq<&Attribute> for Attribute {
fn eq(&self, rhs: &&Attribute) -> bool {
self == *rhs
}
}
/// Creates a new ENCRYPTED Attribute. `Attribute::new` is an alias for this.
#[inline]
pub fn attr(key: impl Into<String>, value: impl Into<String>) -> Attribute {
Attribute::new(key, value)
}
/// Creates a new plaintext Attribute. `Attribute::new_plaintext` is an alias for this.
#[inline]
pub fn attr_plaintext(key: impl Into<String>, value: impl Into<String>) -> Attribute {
Attribute::new_plaintext(key, value)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Uint128;
#[test]
fn event_construction() {
let event_direct = Event {
ty: "test".to_string(),
attributes: vec![attr("foo", "bar"), attr("bar", "baz")],
};
let event_builder = Event::new("test").add_attributes(vec![("foo", "bar"), ("bar", "baz")]);
assert_eq!(event_direct, event_builder);
}
#[test]
#[should_panic]
fn attribute_new_reserved_key_panicks() {
Attribute::new("_invalid", "value");
}
#[test]
#[should_panic]
fn attribute_new_reserved_key_panicks2() {
Attribute::new("_", "value");
}
#[test]
fn attr_works_for_different_types() {
let expected = ("foo", "42");
assert_eq!(attr("foo", "42"), expected);
assert_eq!(attr("foo", "42"), expected);
assert_eq!(attr("foo", "42"), expected);
assert_eq!(attr("foo", Uint128::new(42)), expected);
}
}