network.get_transaction: move some response validation logic from Synchronizer

This commit is contained in:
SomberNight 2020-01-09 19:22:58 +01:00
parent 94888739d3
commit 0b0139c676
No known key found for this signature in database
GPG key ID: B33B5F232C6271E9
3 changed files with 21 additions and 14 deletions

View file

@ -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')

View file

@ -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

View file

@ -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)