1
0

interface: check server response for some methods

some basic sanity checks

Previously if the server sent back a malformed response, it could partially corrupt a wallet file.
(as sometimes the response would get persisted, and issues would only arise later when the values were used)
This commit is contained in:
SomberNight
2020-07-02 15:31:35 +02:00
parent 3393ff757e
commit d19ff43266
4 changed files with 200 additions and 43 deletions

View File

@@ -816,7 +816,13 @@ class Network(Logger, NetworkRetryManager[ServerAddr]):
if success_fut.exception():
try:
raise success_fut.exception()
except (RequestTimedOut, RequestCorrupted):
except RequestTimedOut:
await iface.close()
await iface.got_disconnected
continue # try again
except RequestCorrupted as e:
# TODO ban server?
iface.logger.exception(f"RequestCorrupted: {e}")
await iface.close()
await iface.got_disconnected
continue # try again
@@ -836,11 +842,7 @@ 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 not is_hash256_str(tx_hash):
raise Exception(f"{repr(tx_hash)} is not a txid")
if not is_non_negative_integer(tx_height):
raise Exception(f"{repr(tx_height)} is not a block height")
return await self.interface.session.send_request('blockchain.transaction.get_merkle', [tx_hash, tx_height])
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:
@@ -1012,54 +1014,32 @@ 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 not is_non_negative_integer(height):
raise Exception(f"{repr(height)} is not a block height")
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 not is_hash256_str(tx_hash):
raise Exception(f"{repr(tx_hash)} is not a txid")
iface = self.interface
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
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 not is_hash256_str(sh):
raise Exception(f"{repr(sh)} is not a scripthash")
return await self.interface.session.send_request('blockchain.scripthash.get_history', [sh])
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 not is_hash256_str(sh):
raise Exception(f"{repr(sh)} is not a scripthash")
return await self.interface.session.send_request('blockchain.scripthash.listunspent', [sh])
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 not is_hash256_str(sh):
raise Exception(f"{repr(sh)} is not a scripthash")
return await self.interface.session.send_request('blockchain.scripthash.get_balance', [sh])
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):
command = 'blockchain.transaction.id_from_pos'
return await self.interface.session.send_request(command, [tx_height, tx_pos, merkle])
return await self.interface.get_txid_from_txpos(tx_height, tx_pos, merkle)
def blockchain(self) -> Blockchain:
interface = self.interface