stratus/eth/primitives/
log_filter_input.rs1use std::ops::Deref;
2use std::sync::Arc;
3
4use display_json::DebugAsJson;
5use serde_with::DefaultOnNull;
6use serde_with::OneOrMany;
7use serde_with::PickFirst;
8use serde_with::formats::PreferMany;
9use serde_with::serde_as;
10
11use crate::eth::primitives::Address;
12use crate::eth::primitives::BlockFilter;
13use crate::eth::primitives::Hash;
14use crate::eth::primitives::LogFilter;
15use crate::eth::primitives::LogTopic;
16use crate::eth::primitives::PointInTime;
17use crate::eth::storage::StratusStorage;
18
19#[serde_as]
21#[derive(DebugAsJson, Clone, Default, serde::Deserialize, serde::Serialize, PartialEq, Eq, Hash)]
22#[cfg_attr(test, derive(fake::Dummy))]
23pub struct LogFilterInput {
24 #[serde(rename = "fromBlock", default)]
25 pub from_block: Option<BlockFilter>,
26
27 #[serde(rename = "toBlock", default)]
28 pub to_block: Option<BlockFilter>,
29
30 #[serde(rename = "blockHash", default)]
31 pub block_hash: Option<Hash>,
32
33 #[serde(rename = "address", default)]
34 #[serde_as(deserialize_as = "PickFirst<(DefaultOnNull, OneOrMany<_, PreferMany>)>")]
35 pub address: Vec<Address>,
36
37 #[serde(rename = "topics", default)]
39 #[serde_as(deserialize_as = "DefaultOnNull")]
40 pub topics: Vec<LogFilterInputTopic>,
41}
42
43impl LogFilterInput {
44 pub fn parse(self, storage: &Arc<StratusStorage>) -> anyhow::Result<LogFilter> {
46 let original_input = self.clone();
47
48 let (from, to) = match self.block_hash {
50 Some(hash) => {
51 let from_to = storage.translate_to_point_in_time(BlockFilter::Hash(hash))?;
52 (from_to, from_to)
53 }
54 None => {
55 let from = storage.translate_to_point_in_time(self.from_block.unwrap_or(BlockFilter::Latest))?;
56 let to = storage.translate_to_point_in_time(self.to_block.unwrap_or(BlockFilter::Latest))?;
57 (from, to)
58 }
59 };
60
61 let from = match from {
63 PointInTime::Pending => storage.read_pending_block_header().0.number,
64 PointInTime::Mined => storage.read_mined_block_number(),
65 PointInTime::MinedPast(number) => number,
66 };
67 let to = match to {
68 PointInTime::Pending => None,
69 PointInTime::Mined => None,
70 PointInTime::MinedPast(number) => Some(number),
71 };
72
73 Ok(LogFilter {
74 from_block: from,
75 to_block: to,
76 addresses: self.address,
77 original_input,
78 })
79 }
80}
81
82#[serde_as]
83#[derive(DebugAsJson, Clone, Default, serde::Deserialize, serde::Serialize, PartialEq, Eq, Hash)]
84#[cfg_attr(test, derive(fake::Dummy))]
85pub struct LogFilterInputTopic(#[serde_as(deserialize_as = "OneOrMany<_, PreferMany>")] pub Vec<Option<LogTopic>>);
87
88impl Deref for LogFilterInputTopic {
89 type Target = Vec<Option<LogTopic>>;
90 fn deref(&self) -> &Self::Target {
91 &self.0
92 }
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98
99 #[test]
100 fn deserialize_log_filter_input_with_topics() {
101 use serde_plain::from_str as deser;
102
103 let input = r#"{
104 "fromBlock": "0x3F5DBCA",
105 "toBlock": "latest",
106 "address": [
107 "0xea86da4a617b32b081a4af12e6c13ae7edf8dfc9"
108 ],
109 "topics": [
110 [
111 "0x712a5b346bb553ab14a2a2b44106991a5b94e4d44890d9aaa0f8e6b3268c502c",
112 "0xc9de12e35626948d49833bbe7ac6ebe7e7d96e2d2a2e01e1eaca07830c0bf03d"
113 ],
114 null,
115 "0x000000000000000000000000c23f832f3d9dd9492df35197f3ec0caa1cb23ce1",
116 "0x453138313839353437323032343036323031373434307a495331324d4b446f36"
117 ]
118 }"#;
119
120 let result: LogFilterInput = serde_json::from_str(input).unwrap();
121
122 let expected = LogFilterInput {
123 from_block: Some(deser("0x3F5DBCA").unwrap()),
124 to_block: Some(deser("latest").unwrap()),
125 block_hash: None,
126 address: vec![deser("0xea86da4a617b32b081a4af12e6c13ae7edf8dfc9").unwrap()],
127 topics: vec![
128 LogFilterInputTopic(vec![
129 Some(deser("0x712a5b346bb553ab14a2a2b44106991a5b94e4d44890d9aaa0f8e6b3268c502c").unwrap()),
130 Some(deser("0xc9de12e35626948d49833bbe7ac6ebe7e7d96e2d2a2e01e1eaca07830c0bf03d").unwrap()),
131 ]),
132 LogFilterInputTopic(vec![None]),
133 LogFilterInputTopic(vec![Some(deser("0x000000000000000000000000c23f832f3d9dd9492df35197f3ec0caa1cb23ce1").unwrap())]),
134 LogFilterInputTopic(vec![Some(deser("0x453138313839353437323032343036323031373434307a495331324d4b446f36").unwrap())]),
135 ],
136 };
137
138 assert_eq!(result, expected);
139 }
140
141 #[test]
142 fn deserialize_log_filter_input_empty() {
143 let input = "{}";
144 let result: LogFilterInput = serde_json::from_str(input).unwrap();
145 let expected = LogFilterInput::default();
146 assert_eq!(result, expected);
147 }
148}