ethabi_decode/
event.rs

1// Copyright 2015-2020 Parity Technologies
2// Copyright 2020 Snowfork
3//
4// SPDX-License-Identifier: Apache-2.0
5//
6// Licensed under the Apache License, Version 2.0 <LICENSE or
7// http://www.apache.org/licenses/LICENSE-2.0>. This file may not be
8// copied, modified, or distributed except according to those terms.
9
10//! Contract event.
11use crate::std::BTreeMap;
12use crate::std::Vec;
13use tiny_keccak::{Hasher, Keccak};
14
15use crate::{decode, Error, Param, ParamKind, Token, H256};
16
17
18/// Contract event.
19#[derive(Clone, Debug, PartialEq)]
20pub struct Event<'a> {
21	/// Event signature. Like "Foo(int32,bytes)".
22	pub signature: &'a str,
23	/// Event input.
24	pub inputs: &'a [Param],
25	/// If anonymous, event cannot be found using `from` filter.
26	pub anonymous: bool,
27}
28
29impl<'a> Event<'a> {
30	fn signature_keccak256(&self) -> H256 {
31		let mut result = [0u8; 32];
32		let mut sponge = Keccak::v256();
33		sponge.update(self.signature.as_ref());
34		sponge.finalize(&mut result);
35		result.into()
36	}
37
38	/// Returns all params of the event.
39	fn indexed_params(&self, indexed: bool) -> Vec<Param> {
40		self.inputs.iter().filter(|p| p.indexed == indexed).cloned().collect()
41	}
42
43	/// Returns indices of all params of the event
44	fn indices(&self, indexed: bool) -> Vec<usize> {
45		self.inputs.iter().enumerate().filter(|(_, p)| p.indexed == indexed).map(|(i, _)| i).collect()
46	}
47
48	// Converts param types for indexed parameters to bytes32 where appropriate
49	// This applies to strings, arrays, structs and bytes to follow the encoding of
50	// these indexed param types according to
51	// https://solidity.readthedocs.io/en/develop/abi-spec.html#encoding-of-indexed-event-parameters
52	fn convert_topic_param_type(&self, kind: &ParamKind) -> ParamKind {
53		match kind {
54			ParamKind::String
55			| ParamKind::Bytes
56			| ParamKind::Array(_)
57			| ParamKind::FixedArray(_, _)
58			| ParamKind::Tuple(_) => ParamKind::FixedBytes(32),
59			_ => kind.clone(),
60		}
61	}
62
63	pub fn decode(&self, topics: Vec<H256>, data: Vec<u8>) -> Result<Vec<Token>, Error> {
64
65		// Take first topic if event is not anonymous
66		let to_skip = if self.anonymous {
67			0
68		} else {
69			// verify
70			let event_signature = topics.get(0).ok_or(Error::InvalidData)?;
71			if event_signature != &self.signature_keccak256() {
72				return Err(Error::InvalidData.into());
73			}
74			1
75		};
76
77		let topics_len = topics.len();
78
79		// obtains all params info
80		let topic_params = self.indexed_params(true);
81		let data_params = self.indexed_params(false);
82		let topic_params_indices = self.indices(true);
83		let data_params_indices = self.indices(false);
84
85
86		let topic_types =
87			topic_params.iter().map(|p| self.convert_topic_param_type(&p.kind)).collect::<Vec<ParamKind>>();
88
89		let flat_topics = topics.into_iter().skip(to_skip).flat_map(|t| t.as_ref().to_vec()).collect::<Vec<u8>>();
90
91		let topic_tokens = decode(&topic_types, &flat_topics)?;
92
93		// topic may be only a 32 bytes encoded token
94		if topic_tokens.len() != topics_len - to_skip {
95			return Err(Error::InvalidData);
96		}
97
98		let topics_named_tokens = topic_params_indices.into_iter().zip(topic_tokens.into_iter());
99
100		let data_types = data_params.iter().map(|p| p.kind.clone()).collect::<Vec<ParamKind>>();
101		let data_tokens = decode(&data_types, &data)?;
102		let data_named_tokens = data_params_indices.into_iter().zip(data_tokens.into_iter());
103
104		let named_tokens = topics_named_tokens.chain(data_named_tokens).collect::<BTreeMap<usize, Token>>();
105
106		let tokens: Vec<Token> =
107			self.inputs.iter().enumerate().map(|t| t.0).map(|i| named_tokens[&i].clone()).collect();
108
109		Ok(tokens)
110	}
111}
112
113#[cfg(test)]
114mod tests {
115
116	use crate::{token::Token, Event, Param, ParamKind, H256};
117	use hex::FromHex;
118	use tiny_keccak::{Hasher, Keccak};
119
120	fn keccak256(data: &str) -> H256 {
121		let mut result = [0u8; 32];
122		let mut sponge = Keccak::v256();
123		sponge.update(data.as_ref());
124		sponge.finalize(&mut result);
125		result.into()
126	}
127
128	#[test]
129	fn test_decoding_event() {
130		let event = Event {
131			signature: "foo(int256,int256,address,address,string,int256[],address[5])",
132			inputs: &[Param { kind: ParamKind::Int(256), indexed: false },
133				Param { kind: ParamKind::Int(256), indexed: true },
134				Param { kind: ParamKind::Address, indexed: false },
135				Param { kind: ParamKind::Address, indexed: true },
136				Param { kind: ParamKind::String, indexed: true },
137				Param { kind: ParamKind::Array(Box::new(ParamKind::Int(256))), indexed: true },
138				Param { kind: ParamKind::FixedArray(Box::new(ParamKind::Address), 5), indexed: true },
139			],
140			anonymous: false,
141		};
142
143		let topics: Vec<H256> = vec![
144			keccak256("foo(int256,int256,address,address,string,int256[],address[5])"),
145			"0000000000000000000000000000000000000000000000000000000000000002".parse().unwrap(),
146			"0000000000000000000000001111111111111111111111111111111111111111".parse().unwrap(),
147			"00000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".parse().unwrap(),
148			"00000000000000000bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb".parse().unwrap(),
149			"00000000000000000ccccccccccccccccccccccccccccccccccccccccccccccc".parse().unwrap(),
150		];
151
152		let data = concat!(
153			"0000000000000000000000000000000000000000000000000000000000000003",
154			"0000000000000000000000002222222222222222222222222222222222222222"
155		)
156		.from_hex()
157		.unwrap();
158
159		let tokens = event.decode(topics, data).unwrap();
160
161		assert_eq!(
162			tokens,
163			vec![
164				Token::Int("0000000000000000000000000000000000000000000000000000000000000003".into()),
165				Token::Int("0000000000000000000000000000000000000000000000000000000000000002".into()),
166				Token::Address("2222222222222222222222222222222222222222".parse().unwrap()),
167				Token::Address("1111111111111111111111111111111111111111".parse().unwrap()),
168				Token::FixedBytes(
169					"00000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".from_hex().unwrap()
170				),
171				Token::FixedBytes(
172					"00000000000000000bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb".from_hex().unwrap()
173				),
174				Token::FixedBytes(
175					"00000000000000000ccccccccccccccccccccccccccccccccccccccccccccccc".from_hex().unwrap()
176				),
177			]
178		)
179	}
180}