Browse Source

network.get_transaction: move some response validation logic from Synchronizer

hard-fail-on-bad-server-string
SomberNight 5 years ago
parent
commit
0b0139c676
No known key found for this signature in database GPG Key ID: B33B5F232C6271E9
  1. 5
      electrum/interface.py
  2. 21
      electrum/network.py
  3. 9
      electrum/synchronizer.py

5
electrum/interface.py

@ -181,6 +181,8 @@ class RequestTimedOut(GracefulDisconnect):
return _("Network request timed out.") return _("Network request timed out.")
class RequestCorrupted(GracefulDisconnect): pass
class ErrorParsingSSLCert(Exception): pass class ErrorParsingSSLCert(Exception): pass
class ErrorGettingSSLCertFromServer(Exception): pass class ErrorGettingSSLCertFromServer(Exception): pass
class ConnectError(NetworkException): pass class ConnectError(NetworkException): pass
@ -258,6 +260,9 @@ class Interface(Logger):
def diagnostic_name(self): def diagnostic_name(self):
return str(NetAddress(self.host, self.port)) return str(NetAddress(self.host, self.port))
def __str__(self):
return f"<Interface {self.diagnostic_name()}>"
def _set_proxy(self, proxy: dict): def _set_proxy(self, proxy: dict):
if proxy: if proxy:
username, pw = proxy.get('user'), proxy.get('password') username, pw = proxy.get('user'), proxy.get('password')

21
electrum/network.py

@ -53,10 +53,11 @@ from .bitcoin import COIN
from . import constants from . import constants
from . import blockchain from . import blockchain
from . import bitcoin from . import bitcoin
from .transaction import Transaction
from .blockchain import Blockchain, HEADER_SIZE from .blockchain import Blockchain, HEADER_SIZE
from .interface import (Interface, serialize_server, deserialize_server, from .interface import (Interface, serialize_server, deserialize_server,
RequestTimedOut, NetworkTimeout, BUCKET_NAME_OF_ONION_SERVERS, RequestTimedOut, NetworkTimeout, BUCKET_NAME_OF_ONION_SERVERS,
NetworkException) NetworkException, RequestCorrupted)
from .version import PROTOCOL_VERSION from .version import PROTOCOL_VERSION
from .simple_config import SimpleConfig from .simple_config import SimpleConfig
from .i18n import _ from .i18n import _
@ -66,7 +67,6 @@ if TYPE_CHECKING:
from .channel_db import ChannelDB from .channel_db import ChannelDB
from .lnworker import LNGossip from .lnworker import LNGossip
from .lnwatcher import WatchTower from .lnwatcher import WatchTower
from .transaction import Transaction
from .daemon import Daemon from .daemon import Daemon
@ -871,7 +871,7 @@ class Network(Logger):
if success_fut.exception(): if success_fut.exception():
try: try:
raise success_fut.exception() raise success_fut.exception()
except RequestTimedOut: except (RequestTimedOut, RequestCorrupted):
await iface.close() await iface.close()
await iface.got_disconnected await iface.got_disconnected
continue # try again continue # try again
@ -1068,8 +1068,19 @@ class Network(Logger):
async def get_transaction(self, tx_hash: str, *, timeout=None) -> str: async def get_transaction(self, tx_hash: str, *, timeout=None) -> str:
if not is_hash256_str(tx_hash): if not is_hash256_str(tx_hash):
raise Exception(f"{repr(tx_hash)} is not a txid") raise Exception(f"{repr(tx_hash)} is not a txid")
return await self.interface.session.send_request('blockchain.transaction.get', [tx_hash], iface = self.interface
timeout=timeout) raw = await iface.session.send_request('blockchain.transaction.get', [tx_hash], timeout=timeout)
# validate response
tx = Transaction(raw)
try:
tx.deserialize() # see if raises
except Exception as e:
self.logger.warning(f"cannot deserialize received transaction (txid {tx_hash}). from {str(iface)}")
raise RequestCorrupted() from e # TODO ban server?
if tx.txid() != tx_hash:
self.logger.warning(f"received tx does not match expected txid {tx_hash} (got {tx.txid()}). from {str(iface)}")
raise RequestCorrupted() # TODO ban server?
return raw
@best_effort_reliable @best_effort_reliable
@catch_server_exceptions @catch_server_exceptions

9
electrum/synchronizer.py

@ -221,15 +221,6 @@ class Synchronizer(SynchronizerBase):
finally: finally:
self._requests_answered += 1 self._requests_answered += 1
tx = Transaction(raw_tx) tx = Transaction(raw_tx)
try:
tx.deserialize() # see if raises
except Exception as e:
# possible scenarios:
# 1: server is sending garbage
# 2: there is a bug in the deserialization code
# 3: there was a segwit-like upgrade that changed the tx structure
# that we don't know about
raise SynchronizerFailure(f"cannot deserialize transaction {tx_hash}") from e
if tx_hash != tx.txid(): if tx_hash != tx.txid():
raise SynchronizerFailure(f"received tx does not match expected txid ({tx_hash} != {tx.txid()})") raise SynchronizerFailure(f"received tx does not match expected txid ({tx_hash} != {tx.txid()})")
tx_height = self.requested_tx.pop(tx_hash) tx_height = self.requested_tx.pop(tx_hash)

Loading…
Cancel
Save