|
|
@ -326,19 +326,14 @@ class SessionManager(object): |
|
|
|
]) |
|
|
|
return result |
|
|
|
|
|
|
|
async def _electrum_and_raw_headers(self, height): |
|
|
|
raw_header = await self.raw_header(height) |
|
|
|
electrum_header = self.env.coin.electrum_header(raw_header, height) |
|
|
|
return electrum_header, raw_header |
|
|
|
|
|
|
|
async def _refresh_hsub_results(self, height): |
|
|
|
'''Refresh the cached header subscription responses to be for height, |
|
|
|
and record that as notified_height. |
|
|
|
''' |
|
|
|
# Paranoia: a reorg could race and leave db_height lower |
|
|
|
height = min(height, self.db.db_height) |
|
|
|
electrum, raw = await self._electrum_and_raw_headers(height) |
|
|
|
self.hsub_results = (electrum, {'hex': raw.hex(), 'height': height}) |
|
|
|
raw = await self.raw_header(height) |
|
|
|
self.hsub_results = {'hex': raw.hex(), 'height': height} |
|
|
|
self.notified_height = height |
|
|
|
|
|
|
|
# --- LocalRPC command handlers |
|
|
@ -547,11 +542,6 @@ class SessionManager(object): |
|
|
|
raise RPCError(BAD_REQUEST, f'height {height:,d} ' |
|
|
|
'out of range') from None |
|
|
|
|
|
|
|
async def electrum_header(self, height): |
|
|
|
'''Return the deserialized header at the given height.''' |
|
|
|
electrum_header, _ = await self._electrum_and_raw_headers(height) |
|
|
|
return electrum_header |
|
|
|
|
|
|
|
async def broadcast_transaction(self, raw_tx): |
|
|
|
hex_hash = await self.daemon.broadcast_transaction(raw_tx) |
|
|
|
self.txs_sent += 1 |
|
|
@ -728,13 +718,12 @@ class SessionBase(RPCSession): |
|
|
|
class ElectrumX(SessionBase): |
|
|
|
'''A TCP server that handles incoming Electrum connections.''' |
|
|
|
|
|
|
|
PROTOCOL_MIN = (1, 2) |
|
|
|
PROTOCOL_MIN = (1, 4) |
|
|
|
PROTOCOL_MAX = (1, 4, 1) |
|
|
|
|
|
|
|
def __init__(self, *args, **kwargs): |
|
|
|
super().__init__(*args, **kwargs) |
|
|
|
self.subscribe_headers = False |
|
|
|
self.subscribe_headers_raw = False |
|
|
|
self.connection.max_response_size = self.env.max_send |
|
|
|
self.hashX_subs = {} |
|
|
|
self.sv_seen = False |
|
|
@ -821,26 +810,13 @@ class ElectrumX(SessionBase): |
|
|
|
|
|
|
|
async def subscribe_headers_result(self): |
|
|
|
'''The result of a header subscription or notification.''' |
|
|
|
return self.session_mgr.hsub_results[self.subscribe_headers_raw] |
|
|
|
|
|
|
|
async def _headers_subscribe(self, raw): |
|
|
|
'''Subscribe to get headers of new blocks.''' |
|
|
|
self.bump_cost(0.25) |
|
|
|
self.subscribe_headers_raw = assert_boolean(raw) |
|
|
|
self.subscribe_headers = True |
|
|
|
return await self.subscribe_headers_result() |
|
|
|
return self.session_mgr.hsub_results |
|
|
|
|
|
|
|
async def headers_subscribe(self): |
|
|
|
'''Subscribe to get raw headers of new blocks.''' |
|
|
|
return await self._headers_subscribe(True) |
|
|
|
|
|
|
|
async def headers_subscribe_True(self, raw=True): |
|
|
|
'''Subscribe to get headers of new blocks.''' |
|
|
|
return await self._headers_subscribe(raw) |
|
|
|
|
|
|
|
async def headers_subscribe_False(self, raw=False): |
|
|
|
'''Subscribe to get headers of new blocks.''' |
|
|
|
return await self._headers_subscribe(raw) |
|
|
|
self.subscribe_headers = True |
|
|
|
self.bump_cost(0.25) |
|
|
|
return await self.subscribe_headers_result() |
|
|
|
|
|
|
|
async def add_peer(self, features): |
|
|
|
'''Add a peer (but only if the peer resolves to the source).''' |
|
|
@ -903,40 +879,6 @@ class ElectrumX(SessionBase): |
|
|
|
self.hashX_subs[hashX] = alias |
|
|
|
return await self.address_status(hashX) |
|
|
|
|
|
|
|
def address_to_hashX(self, address): |
|
|
|
try: |
|
|
|
return self.coin.address_to_hashX(address) |
|
|
|
except Exception: |
|
|
|
pass |
|
|
|
raise RPCError(BAD_REQUEST, f'{address} is not a valid address') |
|
|
|
|
|
|
|
async def address_get_balance(self, address): |
|
|
|
'''Return the confirmed and unconfirmed balance of an address.''' |
|
|
|
hashX = self.address_to_hashX(address) |
|
|
|
return await self.get_balance(hashX) |
|
|
|
|
|
|
|
async def address_get_history(self, address): |
|
|
|
'''Return the confirmed and unconfirmed history of an address.''' |
|
|
|
hashX = self.address_to_hashX(address) |
|
|
|
return await self.confirmed_and_unconfirmed_history(hashX) |
|
|
|
|
|
|
|
async def address_get_mempool(self, address): |
|
|
|
'''Return the mempool transactions touching an address.''' |
|
|
|
hashX = self.address_to_hashX(address) |
|
|
|
return await self.unconfirmed_history(hashX) |
|
|
|
|
|
|
|
async def address_listunspent(self, address): |
|
|
|
'''Return the list of UTXOs of an address.''' |
|
|
|
hashX = self.address_to_hashX(address) |
|
|
|
return await self.hashX_listunspent(hashX) |
|
|
|
|
|
|
|
async def address_subscribe(self, address): |
|
|
|
'''Subscribe to an address. |
|
|
|
|
|
|
|
address: the address to subscribe to''' |
|
|
|
hashX = self.address_to_hashX(address) |
|
|
|
return await self.hashX_subscribe(hashX, address) |
|
|
|
|
|
|
|
async def get_balance(self, hashX): |
|
|
|
utxos = await self.db.all_utxos(hashX) |
|
|
|
confirmed = sum(utxo.value for utxo in utxos) |
|
|
@ -1016,12 +958,6 @@ class ElectrumX(SessionBase): |
|
|
|
result.update(await self._merkle_proof(cp_height, height)) |
|
|
|
return result |
|
|
|
|
|
|
|
async def block_header_13(self, height): |
|
|
|
'''Return a raw block header as a hexadecimal string. |
|
|
|
|
|
|
|
height: the header's height''' |
|
|
|
return await self.block_header(height) |
|
|
|
|
|
|
|
async def block_headers(self, start_height, count, cp_height=0): |
|
|
|
'''Return count concatenated block headers as hex for the main chain; |
|
|
|
starting at start_height. |
|
|
@ -1045,28 +981,6 @@ class ElectrumX(SessionBase): |
|
|
|
self.bump_cost(cost) |
|
|
|
return result |
|
|
|
|
|
|
|
async def block_headers_12(self, start_height, count): |
|
|
|
return await self.block_headers(start_height, count) |
|
|
|
|
|
|
|
async def block_get_chunk(self, index): |
|
|
|
'''Return a chunk of block headers as a hexadecimal string. |
|
|
|
|
|
|
|
index: the chunk index''' |
|
|
|
index = non_negative_integer(index) |
|
|
|
size = self.coin.CHUNK_SIZE |
|
|
|
start_height = index * size |
|
|
|
headers, _ = await self.db.read_headers(start_height, size) |
|
|
|
self.bump_cost(2016 / 50) |
|
|
|
return headers.hex() |
|
|
|
|
|
|
|
async def block_get_header(self, height): |
|
|
|
'''The deserialized header at a given height. |
|
|
|
|
|
|
|
height: the header's height''' |
|
|
|
height = non_negative_integer(height) |
|
|
|
self.bump_cost(0.25) |
|
|
|
return await self.session_mgr.electrum_header(height) |
|
|
|
|
|
|
|
def is_tor(self): |
|
|
|
'''Try to detect if the connection is to a tor hidden service we are |
|
|
|
running.''' |
|
|
@ -1295,10 +1209,10 @@ class ElectrumX(SessionBase): |
|
|
|
self.protocol_tuple = ptuple |
|
|
|
|
|
|
|
handlers = { |
|
|
|
'blockchain.block.get_chunk': self.block_get_chunk, |
|
|
|
'blockchain.block.get_header': self.block_get_header, |
|
|
|
'blockchain.block.headers': self.block_headers_12, |
|
|
|
'blockchain.block.header': self.block_header, |
|
|
|
'blockchain.block.headers': self.block_headers, |
|
|
|
'blockchain.estimatefee': self.estimatefee, |
|
|
|
'blockchain.headers.subscribe': self.headers_subscribe, |
|
|
|
'blockchain.relayfee': self.relayfee, |
|
|
|
'blockchain.scripthash.get_balance': self.scripthash_get_balance, |
|
|
|
'blockchain.scripthash.get_history': self.scripthash_get_history, |
|
|
@ -1308,6 +1222,7 @@ class ElectrumX(SessionBase): |
|
|
|
'blockchain.transaction.broadcast': self.transaction_broadcast, |
|
|
|
'blockchain.transaction.get': self.transaction_get, |
|
|
|
'blockchain.transaction.get_merkle': self.transaction_merkle, |
|
|
|
'blockchain.transaction.id_from_pos': self.transaction_id_from_pos, |
|
|
|
'mempool.get_fee_histogram': self.mempool.compact_fee_histogram, |
|
|
|
'server.add_peer': self.add_peer, |
|
|
|
'server.banner': self.banner, |
|
|
@ -1318,29 +1233,6 @@ class ElectrumX(SessionBase): |
|
|
|
'server.version': self.server_version, |
|
|
|
} |
|
|
|
|
|
|
|
if ptuple >= (1, 4): |
|
|
|
handlers.update({ |
|
|
|
'blockchain.block.header': self.block_header, |
|
|
|
'blockchain.block.headers': self.block_headers, |
|
|
|
'blockchain.headers.subscribe': self.headers_subscribe, |
|
|
|
'blockchain.transaction.id_from_pos': |
|
|
|
self.transaction_id_from_pos, |
|
|
|
}) |
|
|
|
elif ptuple >= (1, 3): |
|
|
|
handlers.update({ |
|
|
|
'blockchain.block.header': self.block_header_13, |
|
|
|
'blockchain.headers.subscribe': self.headers_subscribe_True, |
|
|
|
}) |
|
|
|
else: |
|
|
|
handlers.update({ |
|
|
|
'blockchain.headers.subscribe': self.headers_subscribe_False, |
|
|
|
'blockchain.address.get_balance': self.address_get_balance, |
|
|
|
'blockchain.address.get_history': self.address_get_history, |
|
|
|
'blockchain.address.get_mempool': self.address_get_mempool, |
|
|
|
'blockchain.address.listunspent': self.address_listunspent, |
|
|
|
'blockchain.address.subscribe': self.address_subscribe, |
|
|
|
}) |
|
|
|
|
|
|
|
self.request_handlers = handlers |
|
|
|
|
|
|
|
|
|
|
|