re-add 'blockchain.address.listunspent and blockchain.address.getbalance to scribe-hub

This commit is contained in:
Jack Robison 2022-03-23 14:02:26 -04:00
parent 32b8afe154
commit ba1a93b9b0
No known key found for this signature in database
GPG key ID: DF25C68FE0239BB2
3 changed files with 73 additions and 30 deletions

View file

@ -1103,13 +1103,11 @@ class HubDB:
async def all_utxos(self, hashX): async def all_utxos(self, hashX):
"""Return all UTXOs for an address sorted in no particular order.""" """Return all UTXOs for an address sorted in no particular order."""
def read_utxos(): def read_utxos():
utxos = []
utxos_append = utxos.append
fs_tx_hash = self.fs_tx_hash fs_tx_hash = self.fs_tx_hash
for k, v in self.prefix_db.utxo.iterate(prefix=(hashX, )): utxo_info = [
tx_hash, height = fs_tx_hash(k.tx_num) (k.tx_num, k.nout, v.amount) for k, v in self.prefix_db.utxo.iterate(prefix=(hashX, ))
utxos_append(UTXO(k.tx_num, k.nout, tx_hash, height, v.amount)) ]
return utxos return [UTXO(tx_num, nout, *fs_tx_hash(tx_num), value=value) for (tx_num, nout, value) in utxo_info]
while True: while True:
utxos = await asyncio.get_event_loop().run_in_executor(self._executor, read_utxos) utxos = await asyncio.get_event_loop().run_in_executor(self._executor, read_utxos)
@ -1117,7 +1115,7 @@ class HubDB:
return utxos return utxos
self.logger.warning(f'all_utxos: tx hash not ' self.logger.warning(f'all_utxos: tx hash not '
f'found (reorg?), retrying...') f'found (reorg?), retrying...')
await sleep(0.25) await asyncio.sleep(0.25)
async def lookup_utxos(self, prevouts): async def lookup_utxos(self, prevouts):
def lookup_utxos(): def lookup_utxos():

View file

@ -9,6 +9,7 @@ from prometheus_client import Histogram
import rocksdb.errors import rocksdb.errors
from scribe import PROMETHEUS_NAMESPACE from scribe import PROMETHEUS_NAMESPACE
from scribe.common import HISTOGRAM_BUCKETS from scribe.common import HISTOGRAM_BUCKETS
from scribe.db.common import UTXO
from scribe.blockchain.transaction.deserializer import Deserializer from scribe.blockchain.transaction.deserializer import Deserializer
if typing.TYPE_CHECKING: if typing.TYPE_CHECKING:
@ -146,6 +147,44 @@ class MemPool:
result.append(MemPoolTxSummary(tx_hash, tx.fee, has_ui)) result.append(MemPoolTxSummary(tx_hash, tx.fee, has_ui))
return result return result
def unordered_UTXOs(self, hashX):
"""Return an unordered list of UTXO named tuples from mempool
transactions that pay to hashX.
This does not consider if any other mempool transactions spend
the outputs.
"""
utxos = []
for tx_hash in self.touched_hashXs.get(hashX, ()):
tx = self.txs.get(tx_hash)
for pos, (hX, value) in enumerate(tx.out_pairs):
if hX == hashX:
utxos.append(UTXO(-1, pos, tx_hash, 0, value))
return utxos
def potential_spends(self, hashX):
"""Return a set of (prev_hash, prev_idx) pairs from mempool
transactions that touch hashX.
None, some or all of these may be spends of the hashX, but all
actual spends of it (in the DB or mempool) will be included.
"""
result = set()
for tx_hash in self.touched_hashXs.get(hashX, ()):
tx = self.txs[tx_hash]
result.update(tx.prevouts)
return result
def balance_delta(self, hashX):
"""Return the unconfirmed amount in the mempool for hashX.
Can be positive or negative.
"""
value = 0
if hashX in self.touched_hashXs:
for h in self.touched_hashXs[hashX]:
tx = self.txs[h]
value -= sum(v for h168, v in tx.in_pairs if h168 == hashX)
value += sum(v for h168, v in tx.out_pairs if h168 == hashX)
return value
def get_mempool_height(self, tx_hash: bytes) -> int: def get_mempool_height(self, tx_hash: bytes) -> int:
# Height Progression # Height Progression
# -2: not broadcast # -2: not broadcast

View file

@ -652,7 +652,6 @@ class LBRYElectrumX(asyncio.Protocol):
MAX_CHUNK_SIZE = 40960 MAX_CHUNK_SIZE = 40960
session_counter = itertools.count() session_counter = itertools.count()
request_handlers: typing.Dict[str, typing.Callable] = {}
RESPONSE_TIMES = Histogram("response_time", "Response times", namespace=NAMESPACE, RESPONSE_TIMES = Histogram("response_time", "Response times", namespace=NAMESPACE,
labelnames=("method", "version"), buckets=HISTOGRAM_BUCKETS) labelnames=("method", "version"), buckets=HISTOGRAM_BUCKETS)
NOTIFICATION_COUNT = Counter("notification", "Number of notifications sent (for subscriptions)", NOTIFICATION_COUNT = Counter("notification", "Number of notifications sent (for subscriptions)",
@ -885,6 +884,10 @@ class LBRYElectrumX(asyncio.Protocol):
coro = self.address_subscribe coro = self.address_subscribe
elif method == 'blockchain.address.unsubscribe': elif method == 'blockchain.address.unsubscribe':
coro = self.address_unsubscribe coro = self.address_unsubscribe
elif method == 'blockchain.address.listunspent':
coro = self.address_listunspent
elif method == 'blockchain.address.getbalance':
coro = self.address_get_balance
elif method == 'blockchain.estimatefee': elif method == 'blockchain.estimatefee':
coro = self.estimatefee coro = self.estimatefee
elif method == 'blockchain.relayfee': elif method == 'blockchain.relayfee':
@ -1351,19 +1354,22 @@ class LBRYElectrumX(asyncio.Protocol):
self.session_manager.mempool_statuses.pop(hashX, None) self.session_manager.mempool_statuses.pop(hashX, None)
return status return status
# async def hashX_listunspent(self, hashX): async def hashX_listunspent(self, hashX: bytes):
# """Return the list of UTXOs of a script hash, including mempool """Return the list of UTXOs of a script hash, including mempool
# effects.""" effects."""
# utxos = await self.db.all_utxos(hashX) utxos = await self.db.all_utxos(hashX)
# utxos = sorted(utxos) utxos = sorted(utxos)
# utxos.extend(await self.mempool.unordered_UTXOs(hashX)) utxos.extend(self.mempool.unordered_UTXOs(hashX))
# spends = await self.mempool.potential_spends(hashX) spends = self.mempool.potential_spends(hashX)
#
# return [{'tx_hash': hash_to_hex_str(utxo.tx_hash), return [{'tx_hash': hash_to_hex_str(utxo.tx_hash),
# 'tx_pos': utxo.tx_pos, 'tx_pos': utxo.tx_pos,
# 'height': utxo.height, 'value': utxo.value} 'height': utxo.height, 'value': utxo.value}
# for utxo in utxos for utxo in utxos
# if (utxo.tx_hash, utxo.tx_pos) not in spends] if (utxo.tx_hash, utxo.tx_pos) not in spends]
async def address_listunspent(self, address: str):
return await self.hashX_listunspent(self.address_to_hashX(address))
async def hashX_subscribe(self, hashX, alias): async def hashX_subscribe(self, hashX, alias):
self.hashX_subs[hashX] = alias self.hashX_subs[hashX] = alias
@ -1386,10 +1392,10 @@ class LBRYElectrumX(asyncio.Protocol):
pass pass
raise RPCError(BAD_REQUEST, f'{address} is not a valid address') raise RPCError(BAD_REQUEST, f'{address} is not a valid address')
# async def address_get_balance(self, address): async def address_get_balance(self, address):
# """Return the confirmed and unconfirmed balance of an address.""" """Return the confirmed and unconfirmed balance of an address."""
# hashX = self.address_to_hashX(address) hashX = self.address_to_hashX(address)
# return await self.get_balance(hashX) return await self.get_balance(hashX)
async def address_get_history(self, address): async def address_get_history(self, address):
"""Return the confirmed and unconfirmed history of an address.""" """Return the confirmed and unconfirmed history of an address."""
@ -1425,11 +1431,11 @@ class LBRYElectrumX(asyncio.Protocol):
hashX = self.address_to_hashX(address) hashX = self.address_to_hashX(address)
return await self.hashX_unsubscribe(hashX, address) return await self.hashX_unsubscribe(hashX, address)
# async def get_balance(self, hashX): async def get_balance(self, hashX):
# utxos = await self.db.all_utxos(hashX) utxos = await self.db.all_utxos(hashX)
# confirmed = sum(utxo.value for utxo in utxos) confirmed = sum(utxo.value for utxo in utxos)
# unconfirmed = await self.mempool.balance_delta(hashX) unconfirmed = self.mempool.balance_delta(hashX)
# return {'confirmed': confirmed, 'unconfirmed': unconfirmed} return {'confirmed': confirmed, 'unconfirmed': unconfirmed}
# async def scripthash_get_balance(self, scripthash): # async def scripthash_get_balance(self, scripthash):
# """Return the confirmed and unconfirmed balance of a scripthash.""" # """Return the confirmed and unconfirmed balance of a scripthash."""