remove scribe-hub getrawtransaction calls, improve notifications
This commit is contained in:
parent
32a2e92993
commit
4936bd43b6
4 changed files with 53 additions and 56 deletions
|
@ -853,6 +853,15 @@ class HubDB:
|
||||||
return self.total_transactions[tx_num]
|
return self.total_transactions[tx_num]
|
||||||
return self.prefix_db.tx_hash.get(tx_num, deserialize_value=False)
|
return self.prefix_db.tx_hash.get(tx_num, deserialize_value=False)
|
||||||
|
|
||||||
|
def get_raw_mempool_tx(self, tx_hash: bytes) -> Optional[bytes]:
|
||||||
|
return self.prefix_db.mempool_tx.get(tx_hash, deserialize_value=False)
|
||||||
|
|
||||||
|
def get_raw_confirmed_tx(self, tx_hash: bytes) -> Optional[bytes]:
|
||||||
|
return self.prefix_db.tx.get(tx_hash, deserialize_value=False)
|
||||||
|
|
||||||
|
def get_raw_tx(self, tx_hash: bytes) -> Optional[bytes]:
|
||||||
|
return self.get_raw_mempool_tx(tx_hash) or self.get_raw_confirmed_tx(tx_hash)
|
||||||
|
|
||||||
def get_tx_num(self, tx_hash: bytes) -> int:
|
def get_tx_num(self, tx_hash: bytes) -> int:
|
||||||
if self._cache_all_tx_hashes:
|
if self._cache_all_tx_hashes:
|
||||||
return self.tx_num_mapping[tx_hash]
|
return self.tx_num_mapping[tx_hash]
|
||||||
|
@ -973,13 +982,14 @@ class HubDB:
|
||||||
if self._cache_all_claim_txos:
|
if self._cache_all_claim_txos:
|
||||||
fill_cache = tx_num in self.txo_to_claim and len(self.txo_to_claim[tx_num]) > 0
|
fill_cache = tx_num in self.txo_to_claim and len(self.txo_to_claim[tx_num]) > 0
|
||||||
else:
|
else:
|
||||||
fill_cache = False
|
fill_cache = True
|
||||||
tx_height = bisect_right(self.tx_counts, tx_num)
|
tx_height = bisect_right(self.tx_counts, tx_num)
|
||||||
tx = self.prefix_db.tx.get(tx_hash_bytes, fill_cache=fill_cache, deserialize_value=False)
|
tx = self.prefix_db.tx.get(tx_hash_bytes, fill_cache=fill_cache, deserialize_value=False)
|
||||||
if tx_height == -1:
|
if tx_height == -1:
|
||||||
merkle = {
|
merkle = {
|
||||||
'block_height': -1
|
'block_height': -1
|
||||||
}
|
}
|
||||||
|
tx = self.prefix_db.mempool_tx.get(tx_hash_bytes, deserialize_value=False)
|
||||||
else:
|
else:
|
||||||
tx_pos = tx_num - self.tx_counts[tx_height - 1]
|
tx_pos = tx_num - self.tx_counts[tx_height - 1]
|
||||||
branch, root = self.merkle.branch_and_root(
|
branch, root = self.merkle.branch_and_root(
|
||||||
|
|
|
@ -52,6 +52,7 @@ class MemPool:
|
||||||
self.refresh_secs = refresh_secs
|
self.refresh_secs = refresh_secs
|
||||||
self.mempool_process_time_metric = mempool_process_time_metric
|
self.mempool_process_time_metric = mempool_process_time_metric
|
||||||
self.session_manager: typing.Optional['SessionManager'] = None
|
self.session_manager: typing.Optional['SessionManager'] = None
|
||||||
|
self._notification_q = asyncio.Queue()
|
||||||
|
|
||||||
def refresh(self) -> typing.Set[bytes]: # returns list of new touched hashXs
|
def refresh(self) -> typing.Set[bytes]: # returns list of new touched hashXs
|
||||||
prefix_db = self._db.prefix_db
|
prefix_db = self._db.prefix_db
|
||||||
|
@ -211,6 +212,20 @@ class MemPool:
|
||||||
async def on_block(self, touched, height):
|
async def on_block(self, touched, height):
|
||||||
await self._notify_sessions(height, touched, set())
|
await self._notify_sessions(height, touched, set())
|
||||||
|
|
||||||
|
async def send_notifications_forever(self, started):
|
||||||
|
started.set()
|
||||||
|
while True:
|
||||||
|
(session_id, height_changed, hashXes) = await self._notification_q.get()
|
||||||
|
session = self.session_manager.sessions.get(session_id)
|
||||||
|
if session:
|
||||||
|
if session.subscribe_headers and height_changed:
|
||||||
|
asyncio.create_task(
|
||||||
|
session.send_notification('blockchain.headers.subscribe',
|
||||||
|
(self.session_manager.hsub_results[session.subscribe_headers_raw],))
|
||||||
|
)
|
||||||
|
if hashXes:
|
||||||
|
asyncio.create_task(session.send_history_notifications(*hashXes))
|
||||||
|
|
||||||
async def _notify_sessions(self, height, touched, new_touched):
|
async def _notify_sessions(self, height, touched, new_touched):
|
||||||
"""Notify sessions about height changes and touched addresses."""
|
"""Notify sessions about height changes and touched addresses."""
|
||||||
height_changed = height != self.session_manager.notified_height
|
height_changed = height != self.session_manager.notified_height
|
||||||
|
@ -221,18 +236,6 @@ class MemPool:
|
||||||
return
|
return
|
||||||
|
|
||||||
if height_changed:
|
if height_changed:
|
||||||
notified = 0
|
|
||||||
for session in self.session_manager.sessions.values():
|
|
||||||
if session.subscribe_headers:
|
|
||||||
notified += 1
|
|
||||||
asyncio.create_task(
|
|
||||||
session.send_notification('blockchain.headers.subscribe',
|
|
||||||
(self.session_manager.hsub_results[session.subscribe_headers_raw], ))
|
|
||||||
)
|
|
||||||
if notified % 10 == 0:
|
|
||||||
await asyncio.sleep(0) # break up the loop somewhat, there can be many headers notifications
|
|
||||||
if notified:
|
|
||||||
self.logger.info(f'queued notify {notified} sessions of new header')
|
|
||||||
for hashX in touched.intersection(self.session_manager.mempool_statuses.keys()):
|
for hashX in touched.intersection(self.session_manager.mempool_statuses.keys()):
|
||||||
self.session_manager.mempool_statuses.pop(hashX, None)
|
self.session_manager.mempool_statuses.pop(hashX, None)
|
||||||
# self.bp._chain_executor
|
# self.bp._chain_executor
|
||||||
|
@ -240,9 +243,11 @@ class MemPool:
|
||||||
self._db._executor, touched.intersection_update, self.session_manager.hashx_subscriptions_by_session.keys()
|
self._db._executor, touched.intersection_update, self.session_manager.hashx_subscriptions_by_session.keys()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
session_hashxes_to_notify = defaultdict(list)
|
||||||
|
notified_hashxs = 0
|
||||||
|
sent_headers = 0
|
||||||
|
|
||||||
if touched or new_touched or (height_changed and self.session_manager.mempool_statuses):
|
if touched or new_touched or (height_changed and self.session_manager.mempool_statuses):
|
||||||
notified_hashxs = 0
|
|
||||||
session_hashxes_to_notify = defaultdict(list)
|
|
||||||
to_notify = touched if height_changed else new_touched
|
to_notify = touched if height_changed else new_touched
|
||||||
|
|
||||||
for hashX in to_notify:
|
for hashX in to_notify:
|
||||||
|
@ -251,7 +256,18 @@ class MemPool:
|
||||||
for session_id in self.session_manager.hashx_subscriptions_by_session[hashX]:
|
for session_id in self.session_manager.hashx_subscriptions_by_session[hashX]:
|
||||||
session_hashxes_to_notify[session_id].append(hashX)
|
session_hashxes_to_notify[session_id].append(hashX)
|
||||||
notified_hashxs += 1
|
notified_hashxs += 1
|
||||||
for session_id, hashXes in session_hashxes_to_notify.items():
|
|
||||||
asyncio.create_task(self.session_manager.sessions[session_id].send_history_notifications(*hashXes))
|
for session_id, session in self.session_manager.sessions.items():
|
||||||
if session_hashxes_to_notify:
|
hashXes = None
|
||||||
self.logger.info(f'notified {len(session_hashxes_to_notify)} sessions/{notified_hashxs:,d} touched addresses')
|
if session_id in session_hashxes_to_notify:
|
||||||
|
hashXes = session_hashxes_to_notify[session_id]
|
||||||
|
elif not session.subscribe_headers:
|
||||||
|
continue
|
||||||
|
if session.subscribe_headers and height_changed:
|
||||||
|
sent_headers += 1
|
||||||
|
self._notification_q.put_nowait((session_id, height_changed, hashXes))
|
||||||
|
|
||||||
|
if sent_headers:
|
||||||
|
self.logger.info(f'notified {sent_headers} sessions of new block header')
|
||||||
|
if session_hashxes_to_notify:
|
||||||
|
self.logger.info(f'notified {len(session_hashxes_to_notify)} sessions/{notified_hashxs:,d} touched addresses')
|
||||||
|
|
|
@ -94,6 +94,7 @@ class HubServerService(BlockchainReaderService):
|
||||||
def _iter_start_tasks(self):
|
def _iter_start_tasks(self):
|
||||||
yield self.start_status_server()
|
yield self.start_status_server()
|
||||||
yield self.start_cancellable(self.es_notification_client.maintain_connection)
|
yield self.start_cancellable(self.es_notification_client.maintain_connection)
|
||||||
|
yield self.start_cancellable(self.mempool.send_notifications_forever)
|
||||||
yield self.start_cancellable(self.refresh_blocks_forever)
|
yield self.start_cancellable(self.refresh_blocks_forever)
|
||||||
yield self.finished_initial_catch_up.wait()
|
yield self.finished_initial_catch_up.wait()
|
||||||
self.block_count_metric.set(self.last_state.height)
|
self.block_count_metric.set(self.last_state.height)
|
||||||
|
|
|
@ -1666,53 +1666,23 @@ class LBRYElectrumX(asyncio.Protocol):
|
||||||
for tx_hash in tx_hashes:
|
for tx_hash in tx_hashes:
|
||||||
assert_tx_hash(tx_hash)
|
assert_tx_hash(tx_hash)
|
||||||
batch_result = await self.db.get_transactions_and_merkles(tx_hashes)
|
batch_result = await self.db.get_transactions_and_merkles(tx_hashes)
|
||||||
needed_merkles = {}
|
|
||||||
|
|
||||||
for tx_hash in tx_hashes:
|
|
||||||
if tx_hash in batch_result and batch_result[tx_hash][0]:
|
|
||||||
continue
|
|
||||||
tx_hash_bytes = bytes.fromhex(tx_hash)[::-1]
|
|
||||||
mempool_tx = self.mempool.txs.get(tx_hash_bytes, None)
|
|
||||||
if mempool_tx:
|
|
||||||
raw_tx, block_hash = mempool_tx.raw_tx.hex(), None
|
|
||||||
else:
|
|
||||||
tx_info = await self.daemon_request('getrawtransaction', tx_hash, 1)
|
|
||||||
raw_tx = tx_info['hex']
|
|
||||||
block_hash = tx_info.get('blockhash')
|
|
||||||
if block_hash:
|
|
||||||
block = await self.daemon.deserialised_block(block_hash)
|
|
||||||
height = block['height']
|
|
||||||
try:
|
|
||||||
pos = block['tx'].index(tx_hash)
|
|
||||||
except ValueError:
|
|
||||||
raise RPCError(BAD_REQUEST, f'tx hash {tx_hash} not in '
|
|
||||||
f'block {block_hash} at height {height:,d}')
|
|
||||||
needed_merkles[tx_hash] = raw_tx, block['tx'], pos, height
|
|
||||||
else:
|
|
||||||
batch_result[tx_hash] = [raw_tx, {'block_height': -1}]
|
|
||||||
|
|
||||||
if needed_merkles:
|
|
||||||
for tx_hash, (raw_tx, block_txs, pos, block_height) in needed_merkles.items():
|
|
||||||
batch_result[tx_hash] = raw_tx, {
|
|
||||||
'merkle': self._get_merkle_branch(block_txs, pos),
|
|
||||||
'pos': pos,
|
|
||||||
'block_height': block_height
|
|
||||||
}
|
|
||||||
await asyncio.sleep(0) # heavy call, give other tasks a chance
|
|
||||||
self.session_manager.tx_replied_count_metric.inc(len(tx_hashes))
|
self.session_manager.tx_replied_count_metric.inc(len(tx_hashes))
|
||||||
return batch_result
|
return batch_result
|
||||||
|
|
||||||
async def transaction_get(self, tx_hash, verbose=False):
|
async def transaction_get(self, txid: str, verbose=False):
|
||||||
"""Return the serialized raw transaction given its hash
|
"""Return the serialized raw transaction given its hash
|
||||||
|
|
||||||
tx_hash: the transaction hash as a hexadecimal string
|
txid: the transaction hash as a hexadecimal string
|
||||||
verbose: passed on to the daemon
|
verbose: passed on to the daemon
|
||||||
"""
|
"""
|
||||||
assert_tx_hash(tx_hash)
|
assert_tx_hash(txid)
|
||||||
if verbose not in (True, False):
|
if verbose not in (True, False):
|
||||||
raise RPCError(BAD_REQUEST, f'"verbose" must be a boolean')
|
raise RPCError(BAD_REQUEST, f'"verbose" must be a boolean')
|
||||||
|
tx_hash_bytes = bytes.fromhex(txid)[::-1]
|
||||||
|
|
||||||
return await self.daemon_request('getrawtransaction', tx_hash, int(verbose))
|
raw_tx = await asyncio.get_event_loop().run_in_executor(None, self.db.get_raw_tx, tx_hash_bytes)
|
||||||
|
if raw_tx:
|
||||||
|
return raw_tx.hex()
|
||||||
|
|
||||||
def _get_merkle_branch(self, tx_hashes, tx_pos):
|
def _get_merkle_branch(self, tx_hashes, tx_pos):
|
||||||
"""Return a merkle branch to a transaction.
|
"""Return a merkle branch to a transaction.
|
||||||
|
|
Loading…
Reference in a new issue