ethers_abi/
event.rs

1// Copyright 2015-2020 Parity Technologies
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9//! Contract event.
10
11use alloc::collections::BTreeMap;
12
13#[cfg(feature = "serde")]
14use serde::{Deserialize, Serialize};
15use sha3::{Digest, Keccak256};
16
17#[cfg(not(feature = "std"))]
18use crate::no_std_prelude::*;
19use crate::{
20    decode, decode_validate, encode, signature::long_signature, Error, EventParam, Hash, Log,
21    LogParam, ParamType, RawLog, RawTopicFilter, Result, Token, Topic, TopicFilter,
22};
23
24/// Contract event.
25#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
26#[derive(Clone, Debug, PartialEq)]
27pub struct Event {
28    /// Event name.
29    #[cfg_attr(
30        feature = "serde",
31        serde(deserialize_with = "crate::util::sanitize_name::deserialize")
32    )]
33    pub name: String,
34    /// Event input.
35    pub inputs: Vec<EventParam>,
36    /// If anonymous, event cannot be found using `from` filter.
37    pub anonymous: bool,
38}
39
40impl Event {
41    /// Returns names of all params.
42    fn params_names(&self) -> Vec<String> {
43        self.inputs.iter().map(|p| p.name.clone()).collect()
44    }
45
46    /// Returns types of all params.
47    fn param_types(&self) -> Vec<ParamType> {
48        self.inputs.iter().map(|p| p.kind.clone()).collect()
49    }
50
51    /// Returns all params of the event.
52    fn indexed_params(&self, indexed: bool) -> Vec<EventParam> {
53        self.inputs
54            .iter()
55            .filter(|p| p.indexed == indexed)
56            .cloned()
57            .collect()
58    }
59
60    /// Event signature
61    pub fn signature(&self) -> Hash {
62        long_signature(&self.name, &self.param_types())
63    }
64
65    /// Creates topic filter
66    pub fn filter(&self, raw: RawTopicFilter) -> Result<TopicFilter> {
67        fn convert_token(token: Token, kind: &ParamType) -> Result<Hash> {
68            if !token.type_check(kind) {
69                return Err(Error::InvalidData);
70            }
71            let encoded = encode(&[token]);
72            if encoded.len() == 32 {
73                let mut data = [0u8; 32];
74                data.copy_from_slice(&encoded);
75                Ok(data.into())
76            } else {
77                Ok(Hash::from_slice(Keccak256::digest(&encoded).as_slice()))
78            }
79        }
80
81        fn convert_topic(topic: Topic<Token>, kind: Option<&ParamType>) -> Result<Topic<Hash>> {
82            match topic {
83                Topic::None => Ok(Topic::None),
84                Topic::Any => Ok(Topic::Any),
85                Topic::OneOf(tokens) => match kind {
86                    None => Err(Error::InvalidData),
87                    Some(kind) => {
88                        let topics = tokens
89                            .into_iter()
90                            .map(|token| convert_token(token, kind))
91                            .collect::<Result<Vec<_>>>()?;
92                        Ok(Topic::OneOf(topics))
93                    }
94                },
95                Topic::This(token) => match kind {
96                    None => Err(Error::InvalidData),
97                    Some(kind) => Ok(Topic::This(convert_token(token, kind)?)),
98                },
99            }
100        }
101
102        let kinds: Vec<_> = self
103            .indexed_params(true)
104            .into_iter()
105            .map(|param| param.kind)
106            .collect();
107        let result = if self.anonymous {
108            TopicFilter {
109                topic0: convert_topic(raw.topic0, kinds.get(0))?,
110                topic1: convert_topic(raw.topic1, kinds.get(1))?,
111                topic2: convert_topic(raw.topic2, kinds.get(2))?,
112                topic3: Topic::Any,
113            }
114        } else {
115            TopicFilter {
116                topic0: Topic::This(self.signature()),
117                topic1: convert_topic(raw.topic0, kinds.get(0))?,
118                topic2: convert_topic(raw.topic1, kinds.get(1))?,
119                topic3: convert_topic(raw.topic2, kinds.get(2))?,
120            }
121        };
122
123        Ok(result)
124    }
125
126    // Converts param types for indexed parameters to bytes32 where appropriate
127    // This applies to strings, arrays, structs and bytes to follow the encoding of
128    // these indexed param types according to
129    // https://solidity.readthedocs.io/en/develop/abi-spec.html#encoding-of-indexed-event-parameters
130    fn convert_topic_param_type(&self, kind: &ParamType) -> ParamType {
131        match kind {
132            ParamType::String
133            | ParamType::Bytes
134            | ParamType::Array(_)
135            | ParamType::FixedArray(_, _)
136            | ParamType::Tuple(_) => ParamType::FixedBytes(32),
137            _ => kind.clone(),
138        }
139    }
140
141    fn parse_log_inner<F: Fn(&[ParamType], &[u8]) -> Result<Vec<Token>>>(
142        &self,
143        log: RawLog,
144        decode: F,
145    ) -> Result<Log> {
146        let topics = log.topics;
147        let data = log.data;
148        let topics_len = topics.len();
149        // obtains all params info
150        let topic_params = self.indexed_params(true);
151        let data_params = self.indexed_params(false);
152        // then take first topic if event is not anonymous
153        let to_skip = if self.anonymous {
154            0
155        } else {
156            // verify
157            let event_signature = topics.get(0).ok_or(Error::InvalidData)?;
158            if event_signature != &self.signature() {
159                return Err(Error::InvalidData);
160            }
161            1
162        };
163
164        let topic_types = topic_params
165            .iter()
166            .map(|p| self.convert_topic_param_type(&p.kind))
167            .collect::<Vec<ParamType>>();
168
169        let flat_topics = topics
170            .into_iter()
171            .skip(to_skip)
172            .flat_map(|t| t.as_ref().to_vec())
173            .collect::<Vec<u8>>();
174
175        let topic_tokens = decode(&topic_types, &flat_topics)?;
176
177        // topic may be only a 32 bytes encoded token
178        if topic_tokens.len() != topics_len - to_skip {
179            return Err(Error::InvalidData);
180        }
181
182        let topics_named_tokens = topic_params
183            .into_iter()
184            .map(|p| p.name)
185            .zip(topic_tokens.into_iter());
186
187        let data_types = data_params
188            .iter()
189            .map(|p| p.kind.clone())
190            .collect::<Vec<ParamType>>();
191
192        let data_tokens = decode(&data_types, &data)?;
193
194        let data_named_tokens = data_params
195            .into_iter()
196            .map(|p| p.name)
197            .zip(data_tokens.into_iter());
198
199        let named_tokens = topics_named_tokens
200            .chain(data_named_tokens)
201            .collect::<BTreeMap<String, Token>>();
202
203        let decoded_params = self
204            .params_names()
205            .into_iter()
206            .map(|name| LogParam {
207                name: name.clone(),
208                value: named_tokens[&name].clone(),
209            })
210            .collect();
211
212        let result = Log {
213            params: decoded_params,
214        };
215
216        Ok(result)
217    }
218
219    /// Parses `RawLog` and retrieves all log params from it.
220    /// Checks, that decoded data is exact as input provided
221    pub fn parse_log_validate(&self, log: RawLog) -> Result<Log> {
222        self.parse_log_inner(log, decode_validate)
223    }
224
225    /// Parses `RawLog` and retrieves all log params from it.
226    pub fn parse_log(&self, log: RawLog) -> Result<Log> {
227        self.parse_log_inner(log, decode)
228    }
229}
230
231#[cfg(test)]
232mod tests {
233    use hex_literal::hex;
234
235    #[cfg(not(feature = "std"))]
236    use crate::no_std_prelude::*;
237    use crate::{
238        log::{Log, RawLog},
239        signature::long_signature,
240        token::Token,
241        Event, EventParam, LogParam, ParamType,
242    };
243
244    #[test]
245    fn test_decoding_event() {
246        let event = Event {
247            name: "foo".to_owned(),
248            inputs: vec![
249                EventParam {
250                    name: "a".to_owned(),
251                    kind: ParamType::Int(256),
252                    indexed: false,
253                },
254                EventParam {
255                    name: "b".to_owned(),
256                    kind: ParamType::Int(256),
257                    indexed: true,
258                },
259                EventParam {
260                    name: "c".to_owned(),
261                    kind: ParamType::Address,
262                    indexed: false,
263                },
264                EventParam {
265                    name: "d".to_owned(),
266                    kind: ParamType::Address,
267                    indexed: true,
268                },
269                EventParam {
270                    name: "e".to_owned(),
271                    kind: ParamType::String,
272                    indexed: true,
273                },
274                EventParam {
275                    name: "f".to_owned(),
276                    kind: ParamType::Array(Box::new(ParamType::Int(256))),
277                    indexed: true,
278                },
279                EventParam {
280                    name: "g".to_owned(),
281                    kind: ParamType::FixedArray(Box::new(ParamType::Address), 5),
282                    indexed: true,
283                },
284            ],
285            anonymous: false,
286        };
287
288        let log = RawLog {
289            topics: vec![
290                long_signature(
291                    "foo",
292                    &[
293                        ParamType::Int(256),
294                        ParamType::Int(256),
295                        ParamType::Address,
296                        ParamType::Address,
297                        ParamType::String,
298                        ParamType::Array(Box::new(ParamType::Int(256))),
299                        ParamType::FixedArray(Box::new(ParamType::Address), 5),
300                    ],
301                ),
302                hex!("0000000000000000000000000000000000000000000000000000000000000002").into(),
303                hex!("0000000000000000000000001111111111111111111111111111111111111111").into(),
304                hex!("00000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").into(),
305                hex!("00000000000000000bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb").into(),
306                hex!("00000000000000000ccccccccccccccccccccccccccccccccccccccccccccccc").into(),
307            ],
308            data: hex!(
309                "
310				0000000000000000000000000000000000000000000000000000000000000003
311				0000000000000000000000002222222222222222222222222222222222222222
312			"
313            )
314            .into(),
315        };
316        let result = event.parse_log(log).unwrap();
317
318        assert_eq!(
319            result,
320            Log {
321                params: [
322                    (
323                        "a",
324                        Token::Int(
325                            hex!(
326                                "0000000000000000000000000000000000000000000000000000000000000003"
327                            )
328                            .into()
329                        ),
330                    ),
331                    (
332                        "b",
333                        Token::Int(
334                            hex!(
335                                "0000000000000000000000000000000000000000000000000000000000000002"
336                            )
337                            .into()
338                        ),
339                    ),
340                    (
341                        "c",
342                        Token::Address(hex!("2222222222222222222222222222222222222222").into())
343                    ),
344                    (
345                        "d",
346                        Token::Address(hex!("1111111111111111111111111111111111111111").into())
347                    ),
348                    (
349                        "e",
350                        Token::FixedBytes(
351                            hex!(
352                                "00000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
353                            )
354                            .into()
355                        )
356                    ),
357                    (
358                        "f",
359                        Token::FixedBytes(
360                            hex!(
361                                "00000000000000000bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
362                            )
363                            .into()
364                        )
365                    ),
366                    (
367                        "g",
368                        Token::FixedBytes(
369                            hex!(
370                                "00000000000000000ccccccccccccccccccccccccccccccccccccccccccccccc"
371                            )
372                            .into()
373                        )
374                    ),
375                ]
376                .iter()
377                .cloned()
378                .map(|(name, value)| LogParam {
379                    name: name.to_string(),
380                    value
381                })
382                .collect::<Vec<_>>()
383            }
384        );
385    }
386
387    #[test]
388    fn parse_log_whole() {
389        let correct_event = Event {
390            name: "Test".into(),
391            inputs: vec![
392                EventParam {
393                    name: "tuple".into(),
394                    kind: ParamType::Tuple(vec![ParamType::Address, ParamType::Address]),
395                    indexed: false,
396                },
397                EventParam {
398                    name: "addr".into(),
399                    kind: ParamType::Address,
400                    indexed: true,
401                },
402            ],
403            anonymous: false,
404        };
405        // swap indexed params
406        let mut wrong_event = correct_event.clone();
407        wrong_event.inputs[0].indexed = true;
408        wrong_event.inputs[1].indexed = false;
409
410        let log = RawLog {
411            topics: vec![
412                hex!("cf74b4e62f836eeedcd6f92120ffb5afea90e6fa490d36f8b81075e2a7de0cf7").into(),
413                hex!("0000000000000000000000000000000000000000000000000000000000012321").into(),
414            ],
415            data: hex!(
416                "
417			0000000000000000000000000000000000000000000000000000000000012345
418			0000000000000000000000000000000000000000000000000000000000054321
419			"
420            )
421            .into(),
422        };
423
424        assert!(wrong_event.parse_log(log.clone()).is_ok());
425        assert!(wrong_event.parse_log_validate(log.clone()).is_err());
426        assert!(correct_event.parse_log_validate(log).is_ok());
427    }
428}