Skip to content

Commit c4f14f6

Browse files
authored
Merge pull request #117 from bitfinity-network/logs_types_in_did
[EPROD-711] Logs types in did package
2 parents 969871e + 1f5fa2b commit c4f14f6

4 files changed

Lines changed: 299 additions & 0 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ rand = { version = "0.8", features = ["std_rng", "small_rng"] }
6060
reqwest = { version = "0.11", default-features = false }
6161
rlp = "0.5"
6262
serde = "1.0"
63+
serde_with = "3.3"
6364
serde_bytes = "0.11"
6465
serde_json = "1.0"
6566
sha2 = "0.10"

src/did/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ num = { workspace = true }
3030
once_cell = { workspace = true }
3131
rlp = { workspace = true }
3232
serde = { workspace = true }
33+
serde_with = { workspace = true }
3334
serde_json = { workspace = true }
3435
sha2 = { workspace = true }
3536
sha3 = { workspace = true }

src/did/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ pub mod ic;
1515
pub mod init;
1616
pub mod integer;
1717
pub mod keccak;
18+
pub mod logs;
1819
pub mod mint_order_exemption;
1920
pub mod notify;
2021
pub mod permission;

src/did/src/logs.rs

Lines changed: 296 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,296 @@
1+
use ethers_core::types::Bytes;
2+
use serde::{Deserialize, Serialize};
3+
use serde_json::Value;
4+
use serde_with::formats::PreferOne;
5+
use serde_with::{serde_as, OneOrMany};
6+
7+
use crate::{BlockNumber, H160, H256, U256, U64};
8+
9+
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
10+
#[serde(untagged)]
11+
pub enum BlockFilter {
12+
#[serde(rename_all = "camelCase")]
13+
Exact { block_hash: H256 },
14+
#[serde(rename_all = "camelCase")]
15+
Bounded {
16+
from_block: Option<BlockNumber>,
17+
to_block: Option<BlockNumber>,
18+
},
19+
}
20+
21+
#[serde_as]
22+
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
23+
#[serde(transparent)]
24+
pub struct LogAddressFilter(#[serde_as(deserialize_as = "OneOrMany<_, PreferOne>")] pub Vec<H160>);
25+
26+
#[serde_as]
27+
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
28+
#[serde(transparent)]
29+
pub struct LogTopicFilter(#[serde_as(deserialize_as = "OneOrMany<_, PreferOne>")] pub Vec<H256>);
30+
31+
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Default)]
32+
#[serde(rename_all = "camelCase")]
33+
pub struct LogFilter {
34+
#[serde(flatten)]
35+
pub block_filter: Option<BlockFilter>,
36+
pub address: Option<LogAddressFilter>,
37+
pub topics: Option<Vec<Option<LogTopicFilter>>>,
38+
}
39+
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+
74+
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
75+
#[serde(rename_all = "camelCase")]
76+
/// Transaction's log entry.
77+
pub struct TransactionLog {
78+
/// Log's index within transaction.
79+
pub log_index: U256,
80+
/// Transaction's index within block.
81+
pub transaction_index: U64,
82+
/// Transaction's hash.
83+
pub transaction_hash: H256,
84+
/// Block's hash, transaction is included in.
85+
pub block_hash: H256,
86+
/// Block number, transaction is included in.
87+
pub block_number: U64,
88+
/// Log's address.
89+
pub address: H160,
90+
/// Log's data.
91+
pub data: Bytes,
92+
/// Log's Topics.
93+
pub topics: Vec<H256>,
94+
}
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)