103 lines
3.4 KiB
Python
103 lines
3.4 KiB
Python
# Copyright (c) 2016-2018, Neil Booth
|
|
#
|
|
# All rights reserved.
|
|
#
|
|
# See the file "LICENCE" for information about the copyright
|
|
# and warranty status of this software.
|
|
|
|
|
|
from electrumx.lib.hash import hash_to_hex_str
|
|
|
|
|
|
class ChainState(object):
|
|
'''Used as an interface by servers to request information about
|
|
blocks, transaction history, UTXOs and the mempool.
|
|
'''
|
|
|
|
def __init__(self, env, db, daemon, bp):
|
|
self._env = env
|
|
self._db = db
|
|
self._daemon = daemon
|
|
|
|
# External interface pass-throughs for session.py
|
|
self.force_chain_reorg = bp.force_chain_reorg
|
|
self.tx_branch_and_root = db.merkle.branch_and_root
|
|
self.read_headers = db.read_headers
|
|
self.all_utxos = db.all_utxos
|
|
self.limited_history = db.limited_history
|
|
self.header_branch_and_root = db.header_branch_and_root
|
|
|
|
async def broadcast_transaction(self, raw_tx):
|
|
return await self._daemon.sendrawtransaction([raw_tx])
|
|
|
|
async def daemon_request(self, method, args=()):
|
|
return await getattr(self._daemon, method)(*args)
|
|
|
|
def db_height(self):
|
|
return self._db.db_height
|
|
|
|
def get_info(self):
|
|
'''Chain state info for LocalRPC and logs.'''
|
|
return {
|
|
'daemon': self._daemon.logged_url(),
|
|
'daemon_height': self._daemon.cached_height(),
|
|
'db_height': self.db_height(),
|
|
}
|
|
|
|
async def raw_header(self, height):
|
|
'''Return the binary header at the given height.'''
|
|
header, n = await self.read_headers(height, 1)
|
|
if n != 1:
|
|
raise IndexError(f'height {height:,d} out of range')
|
|
return header
|
|
|
|
def set_daemon_url(self, daemon_url):
|
|
self._daemon.set_urls(self._env.coin.daemon_urls(daemon_url))
|
|
return self._daemon.logged_url()
|
|
|
|
async def query(self, args, limit):
|
|
coin = self._env.coin
|
|
db = self._db
|
|
lines = []
|
|
|
|
def arg_to_hashX(arg):
|
|
try:
|
|
script = bytes.fromhex(arg)
|
|
lines.append(f'Script: {arg}')
|
|
return coin.hashX_from_script(script)
|
|
except ValueError:
|
|
pass
|
|
|
|
hashX = coin.address_to_hashX(arg)
|
|
lines.append(f'Address: {arg}')
|
|
return hashX
|
|
|
|
for arg in args:
|
|
hashX = arg_to_hashX(arg)
|
|
if not hashX:
|
|
continue
|
|
n = None
|
|
history = await db.limited_history(hashX, limit=limit)
|
|
for n, (tx_hash, height) in enumerate(history):
|
|
lines.append(f'History #{n:,d}: height {height:,d} '
|
|
f'tx_hash {hash_to_hex_str(tx_hash)}')
|
|
if n is None:
|
|
lines.append('No history found')
|
|
n = None
|
|
utxos = await db.all_utxos(hashX)
|
|
for n, utxo in enumerate(utxos, start=1):
|
|
lines.append(f'UTXO #{n:,d}: tx_hash '
|
|
f'{hash_to_hex_str(utxo.tx_hash)} '
|
|
f'tx_pos {utxo.tx_pos:,d} height '
|
|
f'{utxo.height:,d} value {utxo.value:,d}')
|
|
if n == limit:
|
|
break
|
|
if n is None:
|
|
lines.append('No UTXOs found')
|
|
|
|
balance = sum(utxo.value for utxo in utxos)
|
|
lines.append(f'Balance: {coin.decimal_value(balance):,f} '
|
|
f'{coin.SHORTNAME}')
|
|
|
|
return lines
|