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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user