fix some CLI/RPC commands
This commit is contained in:
@@ -181,7 +181,7 @@ class Commands:
|
|||||||
walletless server query, results are not checked by SPV.
|
walletless server query, results are not checked by SPV.
|
||||||
"""
|
"""
|
||||||
sh = bitcoin.address_to_scripthash(address)
|
sh = bitcoin.address_to_scripthash(address)
|
||||||
return self.network.get_history_for_scripthash(sh)
|
return self.network.run_from_another_thread(self.network.get_history_for_scripthash(sh))
|
||||||
|
|
||||||
@command('w')
|
@command('w')
|
||||||
def listunspent(self):
|
def listunspent(self):
|
||||||
@@ -199,7 +199,7 @@ class Commands:
|
|||||||
is a walletless server query, results are not checked by SPV.
|
is a walletless server query, results are not checked by SPV.
|
||||||
"""
|
"""
|
||||||
sh = bitcoin.address_to_scripthash(address)
|
sh = bitcoin.address_to_scripthash(address)
|
||||||
return self.network.listunspent_for_scripthash(sh)
|
return self.network.run_from_another_thread(self.network.listunspent_for_scripthash(sh))
|
||||||
|
|
||||||
@command('')
|
@command('')
|
||||||
def serialize(self, jsontx):
|
def serialize(self, jsontx):
|
||||||
@@ -322,7 +322,7 @@ class Commands:
|
|||||||
server query, results are not checked by SPV.
|
server query, results are not checked by SPV.
|
||||||
"""
|
"""
|
||||||
sh = bitcoin.address_to_scripthash(address)
|
sh = bitcoin.address_to_scripthash(address)
|
||||||
out = self.network.get_balance_for_scripthash(sh)
|
out = self.network.run_from_another_thread(self.network.get_balance_for_scripthash(sh))
|
||||||
out["confirmed"] = str(Decimal(out["confirmed"])/COIN)
|
out["confirmed"] = str(Decimal(out["confirmed"])/COIN)
|
||||||
out["unconfirmed"] = str(Decimal(out["unconfirmed"])/COIN)
|
out["unconfirmed"] = str(Decimal(out["unconfirmed"])/COIN)
|
||||||
return out
|
return out
|
||||||
@@ -331,7 +331,7 @@ class Commands:
|
|||||||
def getmerkle(self, txid, height):
|
def getmerkle(self, txid, height):
|
||||||
"""Get Merkle branch of a transaction included in a block. Electrum
|
"""Get Merkle branch of a transaction included in a block. Electrum
|
||||||
uses this to verify transactions (Simple Payment Verification)."""
|
uses this to verify transactions (Simple Payment Verification)."""
|
||||||
return self.network.get_merkle_for_transaction(txid, int(height))
|
return self.network.run_from_another_thread(self.network.get_merkle_for_transaction(txid, int(height)))
|
||||||
|
|
||||||
@command('n')
|
@command('n')
|
||||||
def getservers(self):
|
def getservers(self):
|
||||||
@@ -517,7 +517,7 @@ class Commands:
|
|||||||
if self.wallet and txid in self.wallet.transactions:
|
if self.wallet and txid in self.wallet.transactions:
|
||||||
tx = self.wallet.transactions[txid]
|
tx = self.wallet.transactions[txid]
|
||||||
else:
|
else:
|
||||||
raw = self.network.get_transaction(txid)
|
raw = self.network.run_from_another_thread(self.network.get_transaction(txid))
|
||||||
if raw:
|
if raw:
|
||||||
tx = Transaction(raw)
|
tx = Transaction(raw)
|
||||||
else:
|
else:
|
||||||
@@ -637,6 +637,7 @@ class Commands:
|
|||||||
@command('n')
|
@command('n')
|
||||||
def notify(self, address, URL):
|
def notify(self, address, URL):
|
||||||
"""Watch an address. Every time the address changes, a http POST is sent to the URL."""
|
"""Watch an address. Every time the address changes, a http POST is sent to the URL."""
|
||||||
|
raise NotImplementedError() # TODO this method is currently broken
|
||||||
def callback(x):
|
def callback(x):
|
||||||
import urllib.request
|
import urllib.request
|
||||||
headers = {'content-type':'application/json'}
|
headers = {'content-type':'application/json'}
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ class NotificationSession(ClientSession):
|
|||||||
super().send_request(*args, **kwargs),
|
super().send_request(*args, **kwargs),
|
||||||
timeout)
|
timeout)
|
||||||
except asyncio.TimeoutError as e:
|
except asyncio.TimeoutError as e:
|
||||||
raise GracefulDisconnect('request timed out: {}'.format(args)) from e
|
raise RequestTimedOut('request timed out: {}'.format(args)) from e
|
||||||
|
|
||||||
async def subscribe(self, method, params, queue):
|
async def subscribe(self, method, params, queue):
|
||||||
# note: until the cache is written for the first time,
|
# note: until the cache is written for the first time,
|
||||||
@@ -105,6 +105,7 @@ class NotificationSession(ClientSession):
|
|||||||
|
|
||||||
|
|
||||||
class GracefulDisconnect(Exception): pass
|
class GracefulDisconnect(Exception): pass
|
||||||
|
class RequestTimedOut(GracefulDisconnect): pass
|
||||||
class ErrorParsingSSLCert(Exception): pass
|
class ErrorParsingSSLCert(Exception): pass
|
||||||
class ErrorGettingSSLCertFromServer(Exception): pass
|
class ErrorGettingSSLCertFromServer(Exception): pass
|
||||||
|
|
||||||
@@ -140,6 +141,7 @@ class Interface(PrintError):
|
|||||||
self._requested_chunks = set()
|
self._requested_chunks = set()
|
||||||
self.network = network
|
self.network = network
|
||||||
self._set_proxy(proxy)
|
self._set_proxy(proxy)
|
||||||
|
self.session = None
|
||||||
|
|
||||||
self.tip_header = None
|
self.tip_header = None
|
||||||
self.tip = 0
|
self.tip = 0
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ from .bitcoin import COIN
|
|||||||
from . import constants
|
from . import constants
|
||||||
from . import blockchain
|
from . import blockchain
|
||||||
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
|
||||||
from .version import PROTOCOL_VERSION
|
from .version import PROTOCOL_VERSION
|
||||||
from .simple_config import SimpleConfig
|
from .simple_config import SimpleConfig
|
||||||
|
|
||||||
@@ -638,13 +638,34 @@ class Network(PrintError):
|
|||||||
with b.lock:
|
with b.lock:
|
||||||
b.update_size()
|
b.update_size()
|
||||||
|
|
||||||
async def get_merkle_for_transaction(self, tx_hash, tx_height):
|
def best_effort_reliable(func):
|
||||||
|
async def make_reliable_wrapper(self, *args, **kwargs):
|
||||||
|
for i in range(10):
|
||||||
|
iface = self.interface
|
||||||
|
session = iface.session if iface else None
|
||||||
|
if not session:
|
||||||
|
# no main interface; try again
|
||||||
|
await asyncio.sleep(0.1)
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
return await func(self, *args, **kwargs)
|
||||||
|
except RequestTimedOut:
|
||||||
|
if self.interface != iface:
|
||||||
|
# main interface changed; try again
|
||||||
|
continue
|
||||||
|
raise
|
||||||
|
raise Exception('no interface to do request on... gave up.')
|
||||||
|
return make_reliable_wrapper
|
||||||
|
|
||||||
|
@best_effort_reliable
|
||||||
|
async def get_merkle_for_transaction(self, tx_hash: str, tx_height: int) -> dict:
|
||||||
return await self.interface.session.send_request('blockchain.transaction.get_merkle', [tx_hash, tx_height])
|
return await self.interface.session.send_request('blockchain.transaction.get_merkle', [tx_hash, tx_height])
|
||||||
|
|
||||||
|
@best_effort_reliable
|
||||||
async def broadcast_transaction(self, tx, timeout=10):
|
async def broadcast_transaction(self, tx, timeout=10):
|
||||||
try:
|
try:
|
||||||
out = await self.interface.session.send_request('blockchain.transaction.broadcast', [str(tx)], timeout=timeout)
|
out = await self.interface.session.send_request('blockchain.transaction.broadcast', [str(tx)], timeout=timeout)
|
||||||
except asyncio.TimeoutError as e:
|
except RequestTimedOut as e:
|
||||||
return False, "error: operation timed out"
|
return False, "error: operation timed out"
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return False, "error: " + str(e)
|
return False, "error: " + str(e)
|
||||||
@@ -653,10 +674,27 @@ class Network(PrintError):
|
|||||||
return False, "error: " + out
|
return False, "error: " + out
|
||||||
return True, out
|
return True, out
|
||||||
|
|
||||||
|
@best_effort_reliable
|
||||||
async def request_chunk(self, height, tip=None, *, can_return_early=False):
|
async def request_chunk(self, height, tip=None, *, can_return_early=False):
|
||||||
return await self.interface.request_chunk(height, tip=tip, can_return_early=can_return_early)
|
return await self.interface.request_chunk(height, tip=tip, can_return_early=can_return_early)
|
||||||
|
|
||||||
def blockchain(self):
|
@best_effort_reliable
|
||||||
|
async def get_transaction(self, tx_hash: str) -> str:
|
||||||
|
return await self.interface.session.send_request('blockchain.transaction.get', [tx_hash])
|
||||||
|
|
||||||
|
@best_effort_reliable
|
||||||
|
async def get_history_for_scripthash(self, sh: str) -> List[dict]:
|
||||||
|
return await self.interface.session.send_request('blockchain.scripthash.get_history', [sh])
|
||||||
|
|
||||||
|
@best_effort_reliable
|
||||||
|
async def listunspent_for_scripthash(self, sh: str) -> List[dict]:
|
||||||
|
return await self.interface.session.send_request('blockchain.scripthash.listunspent', [sh])
|
||||||
|
|
||||||
|
@best_effort_reliable
|
||||||
|
async def get_balance_for_scripthash(self, sh: str) -> dict:
|
||||||
|
return await self.interface.session.send_request('blockchain.scripthash.get_balance', [sh])
|
||||||
|
|
||||||
|
def blockchain(self) -> Blockchain:
|
||||||
interface = self.interface
|
interface = self.interface
|
||||||
if interface and interface.blockchain is not None:
|
if interface and interface.blockchain is not None:
|
||||||
self.blockchain_index = interface.blockchain.forkpoint
|
self.blockchain_index = interface.blockchain.forkpoint
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ class Synchronizer(PrintError):
|
|||||||
'''
|
'''
|
||||||
def __init__(self, wallet):
|
def __init__(self, wallet):
|
||||||
self.wallet = wallet
|
self.wallet = wallet
|
||||||
|
self.network = wallet.network
|
||||||
self.asyncio_loop = wallet.network.asyncio_loop
|
self.asyncio_loop = wallet.network.asyncio_loop
|
||||||
self.requested_tx = {}
|
self.requested_tx = {}
|
||||||
self.requested_histories = {}
|
self.requested_histories = {}
|
||||||
@@ -86,7 +87,7 @@ class Synchronizer(PrintError):
|
|||||||
# request address history
|
# request address history
|
||||||
self.requested_histories[addr] = status
|
self.requested_histories[addr] = status
|
||||||
h = address_to_scripthash(addr)
|
h = address_to_scripthash(addr)
|
||||||
result = await self.session.send_request("blockchain.scripthash.get_history", [h])
|
result = await self.network.get_history_for_scripthash(h)
|
||||||
self.print_error("receiving history", addr, len(result))
|
self.print_error("receiving history", addr, len(result))
|
||||||
hashes = set(map(lambda item: item['tx_hash'], result))
|
hashes = set(map(lambda item: item['tx_hash'], result))
|
||||||
hist = list(map(lambda item: (item['tx_hash'], item['height']), result))
|
hist = list(map(lambda item: (item['tx_hash'], item['height']), result))
|
||||||
@@ -125,7 +126,7 @@ class Synchronizer(PrintError):
|
|||||||
await group.spawn(self._get_transaction, tx_hash)
|
await group.spawn(self._get_transaction, tx_hash)
|
||||||
|
|
||||||
async def _get_transaction(self, tx_hash):
|
async def _get_transaction(self, tx_hash):
|
||||||
result = await self.session.send_request('blockchain.transaction.get', [tx_hash])
|
result = await self.network.get_transaction(tx_hash)
|
||||||
tx = Transaction(result)
|
tx = Transaction(result)
|
||||||
try:
|
try:
|
||||||
tx.deserialize()
|
tx.deserialize()
|
||||||
|
|||||||
Reference in New Issue
Block a user