Repository: raiden-network/raiden-services

Issue:
Optimize usage of `event_abi_to_log_topic`
Optimize usage of `event_abi_to_log_topic` because 5.77% of the time is spent here.

We currently recreate the log topic(s) each time we filter the blockchain for events. This could be cached instead.

Taken out of https://github.com/raiden-network/raiden-services/issues/672

Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/raiden-network/raiden-services/pull/795?src=pr&el=h1) Report
> Merging [#795](https://codecov.io/gh/raiden-network/raiden-services/pull/795?src=pr&el=desc) into [master](https://codecov.io/gh/raiden-network/raiden-services/commit/5e7bca16564ee3194357534209ad39a4a0597bb9&el=desc) will **increase** coverage by `0.06%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/raiden-network/raiden-services/pull/795/graphs/tree.svg?width=650&height=150&src=pr&token=H07qtpFA6p)](https://codecov.io/gh/raiden-network/raiden-services/pull/795?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #795      +/-   ##
==========================================
+ Coverage   89.98%   90.04%   +0.06%     
==========================================
  Files          35       35              
  Lines        2626     2633       +7     
  Branches      333      334       +1     
==========================================
+ Hits         2363     2371       +8     
+ Misses        189      188       -1     
  Partials       74       74              
```


| [Impacted Files](https://codecov.io/gh/raiden-network/raiden-services/pull/795?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/raiden\_libs/blockchain.py](https://codecov.io/gh/raiden-network/raiden-services/pull/795/diff?src=pr&el=tree#diff-c3JjL3JhaWRlbl9saWJzL2Jsb2NrY2hhaW4ucHk=) | `86.60% <100.00%> (+1.84%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/raiden-network/raiden-services/pull/795?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/raiden-network/raiden-services/pull/795?src=pr&el=footer). Last update [5e7bca1...b80ab6a](https://codecov.io/gh/raiden-network/raiden-services/pull/795?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/src/raiden_libs/blockchain.py b/src/raiden_libs/blockchain.py
index e38e715..92573d4 100644
--- a/src/raiden_libs/blockchain.py
+++ b/src/raiden_libs/blockchain.py
@@ -28,7 +28,6 @@ from raiden_contracts.constants import (
     ChannelEvent,
     MonitoringServiceEvent,
 )
-from raiden_contracts.contract_manager import ContractManager
 from raiden_libs.contract_info import CONTRACT_MANAGER
 from raiden_libs.events import (
     Event,
@@ -46,6 +45,27 @@ from raiden_libs.states import BlockchainState
 log = structlog.get_logger(__name__)
 
 
+def create_event_topic_to_abi_dict() -> Dict[bytes, ABIEvent]:
+    contract_names = [
+        CONTRACT_TOKEN_NETWORK_REGISTRY,
+        CONTRACT_TOKEN_NETWORK,
+        CONTRACT_MONITORING_SERVICE,
+    ]
+
+    event_abis = {}
+    for contract_name in contract_names:
+        events = filter_by_type("event", CONTRACT_MANAGER.get_contract_abi(contract_name))
+
+        for event_abi in events:
+            event_topic = event_abi_to_log_topic(event_abi)  # type: ignore
+            event_abis[event_topic] = event_abi
+
+    return event_abis  # type: ignore
+
+
+EVENT_TOPIC_TO_ABI = create_event_topic_to_abi_dict()
+
+
 def get_web3_provider_info(web3: Web3) -> str:
     """ Returns information about the provider
 
@@ -63,43 +83,27 @@ def get_web3_provider_info(web3: Web3) -> str:
     raise RuntimeError(f"Unsupported web3 provider {provider!r}")
 
 
-def decode_event(
-    abi_codec: ABICodec, topic_to_event_abi: Dict[bytes, ABIEvent], log_entry: LogReceipt
-) -> Dict:
+def decode_event(abi_codec: ABICodec, log_entry: LogReceipt) -> Dict:
     topic = log_entry["topics"][0]
-    event_abi = topic_to_event_abi[topic]
+    event_abi = EVENT_TOPIC_TO_ABI[topic]
 
     return get_event_data(abi_codec=abi_codec, event_abi=event_abi, log_entry=log_entry)
 
 
 def query_blockchain_events(
-    web3: Web3,
-    contract_manager: ContractManager,
-    contract_addresses: List[Address],
-    contract_name: str,
-    from_block: BlockNumber,
-    to_block: BlockNumber,
+    web3: Web3, contract_addresses: List[Address], from_block: BlockNumber, to_block: BlockNumber
 ) -> List[Dict]:
     """Returns events emmitted by a contract for a given event name, within a certain range.
 
     Args:
         web3: A Web3 instance
-        contract_manager: A contract manager
         contract_addresses: The address(es) of the contract(s) to be filtered
-        contract_name: The name of the contract
         from_block: The block to start search events
         to_block: The block to stop searching for events
 
     Returns:
         All matching events
     """
-    events_abi = filter_by_type("event", contract_manager.get_contract_abi(contract_name))
-
-    # eth-utils doesn't have strict ABI types yet
-    topic_to_event_abi: Dict[bytes, ABIEvent] = {
-        event_abi_to_log_topic(event_abi): event_abi for event_abi in events_abi  # type: ignore
-    }
-
     filter_params = FilterParams(
         {
             "fromBlock": from_block,
@@ -110,7 +114,7 @@ def query_blockchain_events(
 
     events = web3.eth.getLogs(filter_params)
 
-    return [decode_event(web3.codec, topic_to_event_abi, log_entry) for log_entry in events]
+    return [decode_event(web3.codec, log_entry) for log_entry in events]
 
 
 def parse_token_network_event(event: dict) -> Optional[Event]:
@@ -155,7 +159,6 @@ def parse_token_network_event(event: dict) -> Optional[Event]:
 
 def get_blockchain_events(
     web3: Web3,
-    contract_manager: ContractManager,
     token_network_addresses: List[TokenNetworkAddress],
     chain_state: BlockchainState,
     from_block: BlockNumber,
@@ -176,9 +179,7 @@ def get_blockchain_events(
     # first check for new token networks and add to state
     registry_events = query_blockchain_events(
         web3=web3,
-        contract_manager=contract_manager,
         contract_addresses=[chain_state.token_network_registry_address],
-        contract_name=CONTRACT_TOKEN_NETWORK_REGISTRY,
         from_block=from_block,
         to_block=to_block,
     )
@@ -202,9 +203,7 @@ def get_blockchain_events(
     # then check all token networks
     network_events = query_blockchain_events(
         web3=web3,
-        contract_manager=contract_manager,
         contract_addresses=token_network_addresses,  # type: ignore
-        contract_name=CONTRACT_TOKEN_NETWORK,
         from_block=from_block,
         to_block=to_block,
     )
@@ -218,7 +217,6 @@ def get_blockchain_events(
     # if the monitor contract address is set in chain_state
     monitoring_events = get_monitoring_blockchain_events(
         web3=web3,
-        contract_manager=contract_manager,
         monitor_contract_address=chain_state.monitor_contract_address,
         from_block=from_block,
         to_block=to_block,
@@ -233,7 +231,6 @@ def get_blockchain_events(
 
 def get_monitoring_blockchain_events(
     web3: Web3,
-    contract_manager: ContractManager,
     monitor_contract_address: Optional[Address],
     from_block: BlockNumber,
     to_block: BlockNumber,
@@ -243,9 +240,7 @@ def get_monitoring_blockchain_events(
 
     monitoring_service_events = query_blockchain_events(
         web3=web3,
-        contract_manager=contract_manager,
         contract_addresses=[monitor_contract_address],
-        contract_name=CONTRACT_MONITORING_SERVICE,
         from_block=from_block,
         to_block=to_block,
     )
@@ -330,7 +325,6 @@ def get_blockchain_events_adaptive(
         before_query = time.monotonic()
         events = get_blockchain_events(
             web3=web3,
-            contract_manager=CONTRACT_MANAGER,
             token_network_addresses=token_network_addresses,
             chain_state=blockchain_state,
             from_block=from_block,


Test patch:
diff --git a/tests/libs/test_blockchain.py b/tests/libs/test_blockchain.py
index b6511f1..6b4d1f6 100644
--- a/tests/libs/test_blockchain.py
+++ b/tests/libs/test_blockchain.py
@@ -9,8 +9,7 @@ from web3.contract import Contract
 
 from monitoring_service.constants import DEFAULT_FILTER_INTERVAL
 from raiden.utils.typing import Address, BlockNumber, ChainID
-from raiden_contracts.constants import CONTRACT_TOKEN_NETWORK_REGISTRY, EVENT_TOKEN_NETWORK_CREATED
-from raiden_contracts.contract_manager import ContractManager
+from raiden_contracts.constants import EVENT_TOKEN_NETWORK_CREATED
 from raiden_libs.blockchain import (
     get_blockchain_events,
     get_blockchain_events_adaptive,
@@ -20,15 +19,11 @@ from raiden_libs.blockchain import (
 from raiden_libs.states import BlockchainState
 
 
-def create_tnr_contract_events_query(
-    web3: Web3, contract_manager: ContractManager, contract_address: Address
-):
+def create_tnr_contract_events_query(web3: Web3, contract_address: Address):
     def query_callback():
         return query_blockchain_events(
             web3=web3,
-            contract_manager=contract_manager,
             contract_addresses=[contract_address],
-            contract_name=CONTRACT_TOKEN_NETWORK_REGISTRY,
             from_block=BlockNumber(0),
             to_block=web3.eth.blockNumber,
         )
@@ -38,14 +33,9 @@ def create_tnr_contract_events_query(
 
 @pytest.mark.usefixtures("token_network")
 def test_limit_inclusivity_in_query_blockchain_events(
-    web3: Web3,
-    wait_for_blocks,
-    contracts_manager: ContractManager,
-    token_network_registry_contract,
+    web3: Web3, wait_for_blocks, token_network_registry_contract,
 ):
-    query = create_tnr_contract_events_query(
-        web3, contracts_manager, token_network_registry_contract.address
-    )
+    query = create_tnr_contract_events_query(web3, token_network_registry_contract.address)
 
     # A new token network has been registered by the `token_network_registry_contract` fixture
     events = query()
@@ -57,9 +47,7 @@ def test_limit_inclusivity_in_query_blockchain_events(
     # test to_block is inclusive
     events = query_blockchain_events(
         web3=web3,
-        contract_manager=contracts_manager,
         contract_addresses=[token_network_registry_contract.address],
-        contract_name=CONTRACT_TOKEN_NETWORK_REGISTRY,
         from_block=BlockNumber(0),
         to_block=BlockNumber(registry_event_block - 1),
     )
@@ -67,9 +55,7 @@ def test_limit_inclusivity_in_query_blockchain_events(
 
     events = query_blockchain_events(
         web3=web3,
-        contract_manager=contracts_manager,
         contract_addresses=[token_network_registry_contract.address],
-        contract_name=CONTRACT_TOKEN_NETWORK_REGISTRY,
         from_block=BlockNumber(0),
         to_block=registry_event_block,
     )
@@ -83,9 +69,7 @@ def test_limit_inclusivity_in_query_blockchain_events(
     # test to_block is inclusive
     events = query_blockchain_events(
         web3=web3,
-        contract_manager=contracts_manager,
         contract_addresses=[token_network_registry_contract.address],
-        contract_name=CONTRACT_TOKEN_NETWORK_REGISTRY,
         from_block=BlockNumber(registry_event_block + 1),
         to_block=current_block_number,
     )
@@ -93,9 +77,7 @@ def test_limit_inclusivity_in_query_blockchain_events(
 
     events = query_blockchain_events(
         web3=web3,
-        contract_manager=contracts_manager,
         contract_addresses=[token_network_registry_contract.address],
-        contract_name=CONTRACT_TOKEN_NETWORK_REGISTRY,
         from_block=registry_event_block,
         to_block=current_block_number,
     )
@@ -104,9 +86,7 @@ def test_limit_inclusivity_in_query_blockchain_events(
     # test that querying just one block works
     events = query_blockchain_events(
         web3=web3,
-        contract_manager=contracts_manager,
         contract_addresses=[token_network_registry_contract.address],
-        contract_name=CONTRACT_TOKEN_NETWORK_REGISTRY,
         from_block=registry_event_block,
         to_block=registry_event_block,
     )
@@ -146,11 +126,10 @@ def test_get_pessimistic_udc_balance(user_deposit_contract, web3, deposit_to_udc
 
 
 def test_get_blockchain_events_returns_early_for_invalid_interval(
-    web3: Web3, contracts_manager: ContractManager, token_network_registry_contract: Contract
+    web3: Web3, token_network_registry_contract: Contract
 ):
     events = get_blockchain_events(
         web3=web3,
-        contract_manager=contracts_manager,
         token_network_addresses=[],
         chain_state=BlockchainState(
             chain_id=ChainID(1),
diff --git a/tests/monitoring/monitoring_service/test_end_to_end.py b/tests/monitoring/monitoring_service/test_end_to_end.py
index 3c633c9..9450570 100644
--- a/tests/monitoring/monitoring_service/test_end_to_end.py
+++ b/tests/monitoring/monitoring_service/test_end_to_end.py
@@ -15,24 +15,15 @@ from raiden.utils.typing import (
     TokenAmount,
     TokenNetworkAddress,
 )
-from raiden_contracts.constants import (
-    CONTRACT_MONITORING_SERVICE,
-    LOCKSROOT_OF_NO_LOCKS,
-    MonitoringServiceEvent,
-)
-from raiden_contracts.contract_manager import ContractManager
+from raiden_contracts.constants import LOCKSROOT_OF_NO_LOCKS, MonitoringServiceEvent
 from raiden_libs.blockchain import query_blockchain_events
 
 
-def create_ms_contract_events_query(
-    web3: Web3, contract_manager: ContractManager, contract_address: Address
-) -> Callable:
+def create_ms_contract_events_query(web3: Web3, contract_address: Address) -> Callable:
     def f():
         return query_blockchain_events(
             web3=web3,
-            contract_manager=contract_manager,
             contract_addresses=[contract_address],
-            contract_name=CONTRACT_MONITORING_SERVICE,
             from_block=BlockNumber(0),
             to_block=web3.eth.blockNumber,
         )
@@ -47,7 +38,6 @@ def test_first_allowed_monitoring(
     service_registry,
     monitoring_service: MonitoringService,
     request_collector: RequestCollector,
-    contracts_manager: ContractManager,
     deposit_to_udc,
     create_channel,
     token_network,
@@ -55,9 +45,7 @@ def test_first_allowed_monitoring(
     get_private_key,
 ):
     # pylint: disable=too-many-arguments,too-many-locals,protected-access
-    query = create_ms_contract_events_query(
-        web3, contracts_manager, monitoring_service_contract.address
-    )
+    query = create_ms_contract_events_query(web3, monitoring_service_contract.address)
     c1, c2 = get_accounts(2)
 
     # add deposit for c1
@@ -158,7 +146,6 @@ def test_e2e(  # pylint: disable=too-many-arguments,too-many-locals
     service_registry,
     monitoring_service: MonitoringService,
     request_collector: RequestCollector,
-    contracts_manager: ContractManager,
     deposit_to_udc,
     create_channel,
     token_network,
@@ -173,9 +160,7 @@ def test_e2e(  # pylint: disable=too-many-arguments,too-many-locals
         5) wait for channel settle
         6) MS claims the reward
     """
-    query = create_ms_contract_events_query(
-        web3, contracts_manager, monitoring_service_contract.address
-    )
+    query = create_ms_contract_events_query(web3, monitoring_service_contract.address)
     initial_balance = user_deposit_contract.functions.balances(monitoring_service.address).call()
     c1, c2 = get_accounts(2)