From 9439261e42eb9efaa35db16735b8421ff17e7b0b Mon Sep 17 00:00:00 2001 From: SomberNight Date: Sat, 11 Mar 2023 18:32:38 +0000 Subject: [PATCH] network: fix bug in best_effort_reliable self.interface might get set to None after decorator checks it but before func gets scheduled: 125.04 | E | asyncio | Task exception was never retrieved future: .add_info_to_txin() done, defined at ...\electrum\electrum\transaction.py:976> exception=AttributeError("'NoneType' object has no attribute 'get_transaction'")> Traceback (most recent call last): File "...\electrum\electrum\transaction.py", line 980, in add_info_to_txin await txin.add_info_from_network(network=network, ignore_network_issues=ignore_network_issues) File "...\electrum\electrum\transaction.py", line 375, in add_info_from_network self.utxo = await fetch_from_network(txid=self.prevout.txid.hex()) File "...\electrum\electrum\transaction.py", line 362, in fetch_from_network raw_tx = await network.get_transaction(txid, timeout=10) File "...\electrum\electrum\network.py", line 866, in make_reliable_wrapper async with OldTaskGroup(wait=any) as group: File "...\aiorpcX\aiorpcx\curio.py", line 304, in __aexit__ await self.join() File "...\electrum\electrum\util.py", line 1410, in join self.completed.result() File "...\electrum\electrum\network.py", line 889, in wrapper return await func(self, *args, **kwargs) File "...\electrum\electrum\network.py", line 1114, in get_transaction return await self.interface.get_transaction(tx_hash=tx_hash, timeout=timeout) AttributeError: 'NoneType' object has no attribute 'get_transaction' --- electrum/network.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/electrum/network.py b/electrum/network.py index 5e531208d..bc2057062 100644 --- a/electrum/network.py +++ b/electrum/network.py @@ -894,10 +894,14 @@ class Network(Logger, NetworkRetryManager[ServerAddr]): @best_effort_reliable @catch_server_exceptions async def get_merkle_for_transaction(self, tx_hash: str, tx_height: int) -> dict: + if self.interface is None: # handled by best_effort_reliable + raise RequestTimedOut() return await self.interface.get_merkle_for_transaction(tx_hash=tx_hash, tx_height=tx_height) @best_effort_reliable async def broadcast_transaction(self, tx: 'Transaction', *, timeout=None) -> None: + if self.interface is None: # handled by best_effort_reliable + raise RequestTimedOut() if timeout is None: timeout = self.get_network_timeout_seconds(NetworkTimeout.Urgent) try: @@ -1106,31 +1110,43 @@ class Network(Logger, NetworkRetryManager[ServerAddr]): @best_effort_reliable @catch_server_exceptions async def request_chunk(self, height: int, tip=None, *, can_return_early=False): + if self.interface is None: # handled by best_effort_reliable + raise RequestTimedOut() return await self.interface.request_chunk(height, tip=tip, can_return_early=can_return_early) @best_effort_reliable @catch_server_exceptions async def get_transaction(self, tx_hash: str, *, timeout=None) -> str: + if self.interface is None: # handled by best_effort_reliable + raise RequestTimedOut() return await self.interface.get_transaction(tx_hash=tx_hash, timeout=timeout) @best_effort_reliable @catch_server_exceptions async def get_history_for_scripthash(self, sh: str) -> List[dict]: + if self.interface is None: # handled by best_effort_reliable + raise RequestTimedOut() return await self.interface.get_history_for_scripthash(sh) @best_effort_reliable @catch_server_exceptions async def listunspent_for_scripthash(self, sh: str) -> List[dict]: + if self.interface is None: # handled by best_effort_reliable + raise RequestTimedOut() return await self.interface.listunspent_for_scripthash(sh) @best_effort_reliable @catch_server_exceptions async def get_balance_for_scripthash(self, sh: str) -> dict: + if self.interface is None: # handled by best_effort_reliable + raise RequestTimedOut() return await self.interface.get_balance_for_scripthash(sh) @best_effort_reliable @catch_server_exceptions async def get_txid_from_txpos(self, tx_height, tx_pos, merkle): + if self.interface is None: # handled by best_effort_reliable + raise RequestTimedOut() return await self.interface.get_txid_from_txpos(tx_height, tx_pos, merkle) def blockchain(self) -> Blockchain: