Skip to content

Commit b9ba322

Browse files
authored
Fixed BlockHandler with Call filter (#3087)
* Fixed BlockHanlder with Call filter The `EthereumBlockFilter.extend` was not properly extending the base structure with the extension struct. The new code reduces drasticly the cloning behavior that was happening way too much in the previous implementation. Fixes #2314 * Added more test for coverage Co-authored-by: Matthieu Vachon <[email protected]>
1 parent 22716fc commit b9ba322

File tree

2 files changed

+277
-18
lines changed

2 files changed

+277
-18
lines changed

chain/ethereum/src/adapter.rs

+151-18
Original file line numberDiff line numberDiff line change
@@ -692,24 +692,25 @@ impl EthereumBlockFilter {
692692
} = other;
693693

694694
self.trigger_every_block = self.trigger_every_block || trigger_every_block;
695-
self.contract_addresses = self.contract_addresses.iter().cloned().fold(
696-
HashSet::new(),
697-
|mut addresses, (start_block, address)| {
698-
match contract_addresses
699-
.iter()
700-
.cloned()
701-
.find(|(_, other_address)| &address == other_address)
702-
{
703-
Some((other_start_block, address)) => {
704-
addresses.insert((cmp::min(other_start_block, start_block), address));
705-
}
706-
None => {
707-
addresses.insert((start_block, address));
695+
696+
for other in contract_addresses {
697+
let (other_start_block, other_address) = other;
698+
699+
match self.find_contract_address(&other.1) {
700+
Some((current_start_block, current_address)) => {
701+
if other_start_block < current_start_block {
702+
self.contract_addresses
703+
.remove(&(current_start_block, current_address));
704+
self.contract_addresses
705+
.insert((other_start_block, other_address));
708706
}
709707
}
710-
addresses
711-
},
712-
);
708+
None => {
709+
self.contract_addresses
710+
.insert((other_start_block, other_address));
711+
}
712+
}
713+
}
713714
}
714715

715716
fn requires_traces(&self) -> bool {
@@ -725,6 +726,13 @@ impl EthereumBlockFilter {
725726

726727
self.contract_addresses.is_empty()
727728
}
729+
730+
fn find_contract_address(&self, candidate: &Address) -> Option<(i32, Address)> {
731+
self.contract_addresses
732+
.iter()
733+
.find(|(_, current_address)| candidate == current_address)
734+
.cloned()
735+
}
728736
}
729737

730738
pub enum ProviderStatus {
@@ -1188,8 +1196,6 @@ mod tests {
11881196

11891197
#[test]
11901198
fn matching_ethereum_call_filter() {
1191-
let address = |id: u64| Address::from_low_u64_be(id);
1192-
let bytes = |value: Vec<u8>| Bytes::from(value);
11931199
let call = |to: Address, input: Vec<u8>| EthereumCall {
11941200
to,
11951201
input: bytes(input),
@@ -1287,6 +1293,125 @@ mod tests {
12871293
);
12881294
}
12891295

1296+
#[test]
1297+
fn extending_ethereum_block_filter_no_found() {
1298+
let mut base = EthereumBlockFilter {
1299+
contract_addresses: HashSet::new(),
1300+
trigger_every_block: false,
1301+
};
1302+
1303+
let extension = EthereumBlockFilter {
1304+
contract_addresses: HashSet::from_iter(vec![(10, address(1))]),
1305+
trigger_every_block: false,
1306+
};
1307+
1308+
base.extend(extension);
1309+
1310+
assert_eq!(
1311+
HashSet::from_iter(vec![(10, address(1))]),
1312+
base.contract_addresses,
1313+
);
1314+
}
1315+
1316+
#[test]
1317+
fn extending_ethereum_block_filter_conflict_picks_lowest_block_from_ext() {
1318+
let mut base = EthereumBlockFilter {
1319+
contract_addresses: HashSet::from_iter(vec![(10, address(1))]),
1320+
trigger_every_block: false,
1321+
};
1322+
1323+
let extension = EthereumBlockFilter {
1324+
contract_addresses: HashSet::from_iter(vec![(2, address(1))]),
1325+
trigger_every_block: false,
1326+
};
1327+
1328+
base.extend(extension);
1329+
1330+
assert_eq!(
1331+
HashSet::from_iter(vec![(2, address(1))]),
1332+
base.contract_addresses,
1333+
);
1334+
}
1335+
1336+
#[test]
1337+
fn extending_ethereum_block_filter_conflict_picks_lowest_block_from_base() {
1338+
let mut base = EthereumBlockFilter {
1339+
contract_addresses: HashSet::from_iter(vec![(2, address(1))]),
1340+
trigger_every_block: false,
1341+
};
1342+
1343+
let extension = EthereumBlockFilter {
1344+
contract_addresses: HashSet::from_iter(vec![(10, address(1))]),
1345+
trigger_every_block: false,
1346+
};
1347+
1348+
base.extend(extension);
1349+
1350+
assert_eq!(
1351+
HashSet::from_iter(vec![(2, address(1))]),
1352+
base.contract_addresses,
1353+
);
1354+
}
1355+
1356+
#[test]
1357+
fn extending_ethereum_block_filter_every_block_in_ext() {
1358+
let mut base = EthereumBlockFilter {
1359+
contract_addresses: HashSet::default(),
1360+
trigger_every_block: false,
1361+
};
1362+
1363+
let extension = EthereumBlockFilter {
1364+
contract_addresses: HashSet::default(),
1365+
trigger_every_block: true,
1366+
};
1367+
1368+
base.extend(extension);
1369+
1370+
assert_eq!(true, base.trigger_every_block);
1371+
}
1372+
1373+
#[test]
1374+
fn extending_ethereum_block_filter_every_block_in_base_and_merge_contract_addresses() {
1375+
let mut base = EthereumBlockFilter {
1376+
contract_addresses: HashSet::from_iter(vec![(10, address(2))]),
1377+
trigger_every_block: true,
1378+
};
1379+
1380+
let extension = EthereumBlockFilter {
1381+
contract_addresses: HashSet::from_iter(vec![]),
1382+
trigger_every_block: false,
1383+
};
1384+
1385+
base.extend(extension);
1386+
1387+
assert_eq!(true, base.trigger_every_block);
1388+
assert_eq!(
1389+
HashSet::from_iter(vec![(10, address(2))]),
1390+
base.contract_addresses,
1391+
);
1392+
}
1393+
1394+
#[test]
1395+
fn extending_ethereum_block_filter_every_block_in_ext_and_merge_contract_addresses() {
1396+
let mut base = EthereumBlockFilter {
1397+
contract_addresses: HashSet::from_iter(vec![(10, address(2))]),
1398+
trigger_every_block: false,
1399+
};
1400+
1401+
let extension = EthereumBlockFilter {
1402+
contract_addresses: HashSet::from_iter(vec![(10, address(1))]),
1403+
trigger_every_block: true,
1404+
};
1405+
1406+
base.extend(extension);
1407+
1408+
assert_eq!(true, base.trigger_every_block);
1409+
assert_eq!(
1410+
HashSet::from_iter(vec![(10, address(2)), (10, address(1))]),
1411+
base.contract_addresses,
1412+
);
1413+
}
1414+
12901415
#[test]
12911416
fn extending_ethereum_call_filter() {
12921417
let mut base = EthereumCallFilter {
@@ -1333,6 +1458,14 @@ mod tests {
13331458
Some(&(1, HashSet::from_iter(vec![[1u8; 4]])))
13341459
);
13351460
}
1461+
1462+
fn address(id: u64) -> Address {
1463+
Address::from_low_u64_be(id)
1464+
}
1465+
1466+
fn bytes(value: Vec<u8>) -> Bytes {
1467+
Bytes::from(value)
1468+
}
13361469
}
13371470

13381471
// Tests `eth_get_logs_filters` in instances where all events are filtered on by all contracts.

chain/ethereum/src/ethereum_adapter.rs

+126
Original file line numberDiff line numberDiff line change
@@ -1987,3 +1987,129 @@ async fn get_transaction_receipts_for_transaction_hashes(
19871987

19881988
Ok(receipts_by_hash)
19891989
}
1990+
1991+
#[cfg(test)]
1992+
mod tests {
1993+
1994+
use crate::trigger::{EthereumBlockTriggerType, EthereumTrigger};
1995+
1996+
use super::{parse_block_triggers, EthereumBlock, EthereumBlockFilter, EthereumBlockWithCalls};
1997+
use graph::blockchain::BlockPtr;
1998+
use graph::prelude::ethabi::ethereum_types::U64;
1999+
use graph::prelude::web3::types::{Address, Block, Bytes, H256};
2000+
use graph::prelude::EthereumCall;
2001+
use std::collections::HashSet;
2002+
use std::iter::FromIterator;
2003+
use std::sync::Arc;
2004+
2005+
#[test]
2006+
fn parse_block_triggers_every_block() {
2007+
let block = EthereumBlockWithCalls {
2008+
ethereum_block: EthereumBlock {
2009+
block: Arc::new(Block {
2010+
hash: Some(hash(2)),
2011+
number: Some(U64::from(2)),
2012+
..Default::default()
2013+
}),
2014+
..Default::default()
2015+
},
2016+
calls: Some(vec![EthereumCall {
2017+
to: address(4),
2018+
input: bytes(vec![1; 36]),
2019+
..Default::default()
2020+
}]),
2021+
};
2022+
2023+
assert_eq!(
2024+
vec![EthereumTrigger::Block(
2025+
BlockPtr::from((hash(2), 2)),
2026+
EthereumBlockTriggerType::Every
2027+
)],
2028+
parse_block_triggers(
2029+
&EthereumBlockFilter {
2030+
contract_addresses: HashSet::from_iter(vec![(10, address(1))]),
2031+
trigger_every_block: true,
2032+
},
2033+
&block
2034+
),
2035+
"every block should generate a trigger even when address don't match"
2036+
);
2037+
}
2038+
2039+
#[test]
2040+
fn parse_block_triggers_specific_call_not_found() {
2041+
let block = EthereumBlockWithCalls {
2042+
ethereum_block: EthereumBlock {
2043+
block: Arc::new(Block {
2044+
hash: Some(hash(2)),
2045+
number: Some(U64::from(2)),
2046+
..Default::default()
2047+
}),
2048+
..Default::default()
2049+
},
2050+
calls: Some(vec![EthereumCall {
2051+
to: address(4),
2052+
input: bytes(vec![1; 36]),
2053+
..Default::default()
2054+
}]),
2055+
};
2056+
2057+
assert_eq!(
2058+
Vec::<EthereumTrigger>::new(),
2059+
parse_block_triggers(
2060+
&EthereumBlockFilter {
2061+
contract_addresses: HashSet::from_iter(vec![(1, address(1))]),
2062+
trigger_every_block: false,
2063+
},
2064+
&block
2065+
),
2066+
"block filter specifies address 1 but block does not contain any call to it"
2067+
);
2068+
}
2069+
2070+
#[test]
2071+
fn parse_block_triggers_specific_call_found() {
2072+
let block = EthereumBlockWithCalls {
2073+
ethereum_block: EthereumBlock {
2074+
block: Arc::new(Block {
2075+
hash: Some(hash(2)),
2076+
number: Some(U64::from(2)),
2077+
..Default::default()
2078+
}),
2079+
..Default::default()
2080+
},
2081+
calls: Some(vec![EthereumCall {
2082+
to: address(4),
2083+
input: bytes(vec![1; 36]),
2084+
..Default::default()
2085+
}]),
2086+
};
2087+
2088+
assert_eq!(
2089+
vec![EthereumTrigger::Block(
2090+
BlockPtr::from((hash(2), 2)),
2091+
EthereumBlockTriggerType::WithCallTo(address(4))
2092+
)],
2093+
parse_block_triggers(
2094+
&EthereumBlockFilter {
2095+
contract_addresses: HashSet::from_iter(vec![(1, address(4))]),
2096+
trigger_every_block: false,
2097+
},
2098+
&block
2099+
),
2100+
"block filter specifies address 4 and block has call to it"
2101+
);
2102+
}
2103+
2104+
fn address(id: u64) -> Address {
2105+
Address::from_low_u64_be(id)
2106+
}
2107+
2108+
fn hash(id: u8) -> H256 {
2109+
H256::from([id; 32])
2110+
}
2111+
2112+
fn bytes(value: Vec<u8>) -> Bytes {
2113+
Bytes::from(value)
2114+
}
2115+
}

0 commit comments

Comments
 (0)