Skip to content

Commit 1f5fa2b

Browse files
committed
tests + fixes
1 parent dbd9b4d commit 1f5fa2b

1 file changed

Lines changed: 237 additions & 0 deletions

File tree

src/did/src/logs.rs

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use ethers_core::types::Bytes;
22
use serde::{Deserialize, Serialize};
3+
use serde_json::Value;
34
use serde_with::formats::PreferOne;
45
use serde_with::{serde_as, OneOrMany};
56

@@ -36,6 +37,40 @@ pub struct LogFilter {
3637
pub topics: Option<Vec<Option<LogTopicFilter>>>,
3738
}
3839

40+
impl TryFrom<Value> for LogFilter {
41+
type Error = jsonrpc_core::Error;
42+
43+
fn try_from(value: Value) -> Result<Self, Self::Error> {
44+
if let Value::Object(ref map) = value {
45+
// According to documentation if `blockHash` property is specified then `fromBlock` and `toBlock` shouldn't be specified
46+
if map.contains_key("blockHash")
47+
&& (map.contains_key("fromBlock") || map.contains_key("toBlock"))
48+
{
49+
Err(Self::Error::invalid_params(
50+
"'blockHash' property cannot be used with 'fromBlock' or 'toBlock'",
51+
))
52+
} else {
53+
let mut filter: LogFilter =
54+
serde_json::from_value(value).map_err(|_| Self::Error::parse_error())?;
55+
56+
// Empty block filter can be serialized as `block_filter: BlockFilter::Bounded(from_block: None, to_block:None)`
57+
// That could be OK for us because it is equivalent to `block_filter: None`, but it's better to disambiguate things
58+
if let Some(BlockFilter::Bounded {
59+
from_block: None,
60+
to_block: None,
61+
}) = filter.block_filter
62+
{
63+
filter.block_filter = None;
64+
}
65+
66+
Ok(filter)
67+
}
68+
} else {
69+
Err(Self::Error::invalid_params("invalid json value"))
70+
}
71+
}
72+
}
73+
3974
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
4075
#[serde(rename_all = "camelCase")]
4176
/// Transaction's log entry.
@@ -57,3 +92,205 @@ pub struct TransactionLog {
5792
/// Log's Topics.
5893
pub topics: Vec<H256>,
5994
}
95+
96+
#[cfg(test)]
97+
mod tests {
98+
use serde_json::json;
99+
100+
use super::*;
101+
102+
const BLOCK_HASH_1: &str = "f43869e67c02c57d1f9a07bb897b54bec1cfa1feb704d91a2ee087566de5df2c";
103+
const TOPIC_1: &str = "cc6a069bf885d8cf2fb456ca33db48ab7d5e3df1e6504a18e7899a16d604f5c6";
104+
const TOPIC_2: &str = "e4058f2da8dda0b1ffb454bb7d121c1498dcfc4446a3d86b7c03e27b34e29345";
105+
const ADDRESS: &str = "7fafd954cbcfd683304cd9be0a85848cbbb1c13d";
106+
107+
fn get_block_hash_1_str() -> String {
108+
format!("0x{BLOCK_HASH_1}")
109+
}
110+
111+
fn get_block_hash_1() -> H256 {
112+
H256::from_hex_str(BLOCK_HASH_1).unwrap()
113+
}
114+
115+
fn get_topic_1() -> H256 {
116+
H256::from_hex_str(TOPIC_1).unwrap()
117+
}
118+
119+
fn get_topic_1_str() -> String {
120+
format!("0x{TOPIC_1}")
121+
}
122+
123+
fn get_topic_2() -> H256 {
124+
H256::from_hex_str(TOPIC_2).unwrap()
125+
}
126+
127+
fn get_topic_2_str() -> String {
128+
format!("0x{TOPIC_2}")
129+
}
130+
131+
fn get_address_str() -> String {
132+
format!("0x{ADDRESS}")
133+
}
134+
135+
fn get_address_1() -> H160 {
136+
H160::from_hex_str(ADDRESS).unwrap()
137+
}
138+
139+
#[test]
140+
fn test_log_filter_deserialization_fail() {
141+
assert!(LogFilter::try_from(json!([])).is_err());
142+
assert!(LogFilter::try_from(json!("str")).is_err());
143+
assert!(LogFilter::try_from(json!(42)).is_err());
144+
assert!(LogFilter::try_from(
145+
json!({"blockHash": get_block_hash_1_str(), "fromBlock": "earliest"})
146+
)
147+
.is_err());
148+
assert!(LogFilter::try_from(
149+
json!({"blockHash": get_block_hash_1_str(), "toBlock": "0x01"})
150+
)
151+
.is_err());
152+
}
153+
154+
#[test]
155+
fn test_log_filter_deserialization_empty() {
156+
let filter = LogFilter::try_from(json!({})).unwrap();
157+
let expected_filter = Default::default();
158+
assert_eq!(filter, expected_filter);
159+
}
160+
161+
#[test]
162+
fn test_log_filter_deserialization_block_filter() {
163+
let filter =
164+
LogFilter::try_from(json!({"fromBlock": "earliest", "toBlock": "0x01"})).unwrap();
165+
166+
let expected_filter = LogFilter {
167+
block_filter: Some(BlockFilter::Bounded {
168+
from_block: Some(BlockNumber::Earliest),
169+
to_block: Some(BlockNumber::Number(U64::one())),
170+
}),
171+
..Default::default()
172+
};
173+
assert_eq!(filter, expected_filter);
174+
175+
let filter = LogFilter::try_from(json!({ "blockHash": get_block_hash_1_str() })).unwrap();
176+
let expected_filter = LogFilter {
177+
block_filter: Some(BlockFilter::Exact {
178+
block_hash: get_block_hash_1(),
179+
}),
180+
..Default::default()
181+
};
182+
assert_eq!(filter, expected_filter);
183+
}
184+
185+
#[test]
186+
fn test_log_filter_deserialization_address() {
187+
let filter = LogFilter::try_from(json!({
188+
"address": [],
189+
}))
190+
.unwrap();
191+
let expected_filter = LogFilter {
192+
address: Some(LogAddressFilter(vec![])),
193+
..Default::default()
194+
};
195+
assert_eq!(filter, expected_filter);
196+
197+
let filter = LogFilter::try_from(json!({
198+
"address": [get_address_str()],
199+
}))
200+
.unwrap();
201+
let expected_filter = LogFilter {
202+
address: Some(LogAddressFilter(vec![get_address_1()])),
203+
..Default::default()
204+
};
205+
assert_eq!(filter, expected_filter);
206+
207+
let filter = LogFilter::try_from(json!({
208+
"address": [get_address_str(), get_address_str()],
209+
}))
210+
.unwrap();
211+
let expected_filter = LogFilter {
212+
address: Some(LogAddressFilter(vec![get_address_1(), get_address_1()])),
213+
..Default::default()
214+
};
215+
assert_eq!(filter, expected_filter);
216+
}
217+
218+
#[test]
219+
fn test_log_filter_deserialization_topics() {
220+
let filter = LogFilter::try_from(json!({
221+
"topics": [],
222+
}))
223+
.unwrap();
224+
let expected_filter = LogFilter {
225+
topics: Some(vec![]),
226+
..Default::default()
227+
};
228+
assert_eq!(filter, expected_filter);
229+
230+
let filter = LogFilter::try_from(json!({
231+
"topics": [null],
232+
}))
233+
.unwrap();
234+
let expected_filter = LogFilter {
235+
topics: Some(vec![None]),
236+
..Default::default()
237+
};
238+
assert_eq!(filter, expected_filter);
239+
240+
let filter = LogFilter::try_from(json!({
241+
"topics": [[get_topic_1_str()]],
242+
}))
243+
.unwrap();
244+
let expected_filter = LogFilter {
245+
topics: Some(vec![Some(LogTopicFilter(vec![get_topic_1()]))]),
246+
..Default::default()
247+
};
248+
assert_eq!(filter, expected_filter);
249+
250+
let filter = LogFilter::try_from(json!({
251+
"topics": [[get_topic_1_str()], null],
252+
}))
253+
.unwrap();
254+
let expected_filter = LogFilter {
255+
topics: Some(vec![Some(LogTopicFilter(vec![get_topic_1()])), None]),
256+
..Default::default()
257+
};
258+
assert_eq!(filter, expected_filter);
259+
260+
let filter = LogFilter::try_from(json!({
261+
"topics": [[get_topic_1_str()], null, [get_topic_1_str(), get_topic_2_str()]],
262+
}))
263+
.unwrap();
264+
let expected_filter = LogFilter {
265+
topics: Some(vec![
266+
Some(LogTopicFilter(vec![get_topic_1()])),
267+
None,
268+
Some(LogTopicFilter(vec![get_topic_1(), get_topic_2()])),
269+
]),
270+
..Default::default()
271+
};
272+
assert_eq!(filter, expected_filter);
273+
}
274+
275+
#[test]
276+
fn test_log_filter_deserialization_combine() {
277+
let filter = LogFilter::try_from(json!({
278+
"blockHash": get_block_hash_1_str(),
279+
"address": [get_address_str()],
280+
"topics": [null, [get_topic_1_str()], [get_topic_1_str(), get_topic_2_str()]],
281+
}))
282+
.unwrap();
283+
let expected_filter = LogFilter {
284+
block_filter: Some(BlockFilter::Exact {
285+
block_hash: get_block_hash_1(),
286+
}),
287+
address: Some(LogAddressFilter(vec![get_address_1()])),
288+
topics: Some(vec![
289+
None,
290+
Some(LogTopicFilter(vec![get_topic_1()])),
291+
Some(LogTopicFilter(vec![get_topic_1(), get_topic_2()])),
292+
]),
293+
};
294+
assert_eq!(filter, expected_filter);
295+
}
296+
}

0 commit comments

Comments
 (0)