use multi_get for fetching transactions #25

Merged
jackrobison merged 5 commits from multiget-transactions into master 2022-04-16 17:19:13 +02:00
2 changed files with 38 additions and 17 deletions
Showing only changes of commit 1bf079e177 - Show all commits

View file

@ -1020,35 +1020,36 @@ class HubDB:
if needed_confirmed: if needed_confirmed:
needed_heights = set() needed_heights = set()
tx_heights_and_positions = {} tx_heights_and_positions = defaultdict(list)
for (tx_hash_bytes, tx_num), tx in zip(needed_confirmed, await run_in_executor( for (tx_hash_bytes, tx_num), tx in zip(needed_confirmed, await run_in_executor(
self._executor, self.prefix_db.tx.multi_get, [(tx_hash,) for tx_hash, _ in needed_confirmed], self._executor, self.prefix_db.tx.multi_get, [(tx_hash,) for tx_hash, _ in needed_confirmed],
True, False)): True, False)):
tx_height = bisect_right(self.tx_counts, tx_num) tx_height = bisect_right(self.tx_counts, tx_num)
needed_heights.add(tx_height) needed_heights.add(tx_height)
tx_pos = tx_num - self.tx_counts[tx_height - 1] tx_pos = tx_num - self.tx_counts[tx_height - 1]
tx_heights_and_positions[tx_hash_bytes] = (tx, tx_num, tx_height, tx_pos) tx_heights_and_positions[tx_height].append((tx_hash_bytes, tx, tx_num, tx_pos))
sorted_heights = list(sorted(needed_heights)) sorted_heights = list(sorted(needed_heights))
block_txs = await run_in_executor( block_txs = await run_in_executor(
self._executor, self.prefix_db.block_txs.multi_get, [(height,) for height in sorted_heights] self._executor, self.prefix_db.block_txs.multi_get, [(height,) for height in sorted_heights]
) )
block_txs = {height: v.tx_hashes for height, v in zip(sorted_heights, block_txs)} block_txs = {height: v.tx_hashes for height, v in zip(sorted_heights, block_txs)}
for tx_hash_bytes, (tx, tx_num, tx_height, tx_pos) in tx_heights_and_positions.items(): for tx_height, v in tx_heights_and_positions.items():
branch, root = self.merkle.branch_and_root( branches, root = self.merkle.branches_and_root(
block_txs[tx_height], tx_pos block_txs[tx_height], [tx_pos for (tx_hash_bytes, tx, tx_num, tx_pos) in v]
) )
merkle = { for (tx_hash_bytes, tx, tx_num, tx_pos) in v:
'block_height': tx_height, merkle = {
'merkle': [ 'block_height': tx_height,
hash_to_hex_str(_hash) 'merkle': [
for _hash in branch hash_to_hex_str(_hash)
], for _hash in branches[tx_pos]
'pos': tx_pos ],
} 'pos': tx_pos
tx_infos[tx_hash_bytes[::-1].hex()] = None if not tx else tx.hex(), merkle }
if tx_height > 0 and tx_height + 10 < self.db_height: tx_infos[tx_hash_bytes[::-1].hex()] = None if not tx else tx.hex(), merkle
self._tx_and_merkle_cache[tx_hash_bytes[::-1].hex()] = tx, merkle if tx_height > 0 and tx_height + 10 < self.db_height:
self._tx_and_merkle_cache[tx_hash_bytes[::-1].hex()] = tx, merkle
await asyncio.sleep(0) await asyncio.sleep(0)
if needed_mempool: if needed_mempool:
for tx_hash_bytes, tx in zip(needed_mempool, await run_in_executor( for tx_hash_bytes, tx in zip(needed_mempool, await run_in_executor(

View file

@ -25,7 +25,7 @@
# and warranty status of this software. # and warranty status of this software.
"""Merkle trees, branches, proofs and roots.""" """Merkle trees, branches, proofs and roots."""
import typing
from asyncio import Event from asyncio import Event
from math import ceil, log from math import ceil, log
@ -87,6 +87,26 @@ class Merkle:
return branch, hashes[0] return branch, hashes[0]
@staticmethod
def branches_and_root(block_tx_hashes: typing.List[bytes], tx_positions: typing.List[int]):
block_tx_hashes = list(block_tx_hashes)
positions = list(tx_positions)
length = ceil(log(len(block_tx_hashes), 2))
branches = [[] for _ in range(len(tx_positions))]
for _ in range(length):
if len(block_tx_hashes) & 1:
h = block_tx_hashes[-1]
block_tx_hashes.append(h)
for idx, tx_position in enumerate(tx_positions):
h = block_tx_hashes[tx_position ^ 1]
branches[idx].append(h)
tx_positions[idx] >>= 1
block_tx_hashes = [
double_sha256(block_tx_hashes[n] + block_tx_hashes[n + 1]) for n in
range(0, len(block_tx_hashes), 2)
]
return {tx_position: branch for tx_position, branch in zip(positions, branches)}, block_tx_hashes[0]
@staticmethod @staticmethod
def root(hashes, length=None): def root(hashes, length=None):
"""Return the merkle root of a non-empty iterable of binary hashes.""" """Return the merkle root of a non-empty iterable of binary hashes."""