batched merkle proofs

This commit is contained in:
Jack Robison 2022-04-15 16:56:18 -04:00
parent b5f46af3b4
commit 1bf079e177
No known key found for this signature in database
GPG key ID: DF25C68FE0239BB2
2 changed files with 38 additions and 17 deletions

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."""