Browse Source

Retain the prevouts with each MemPoolTx

patch-2
Neil Booth 7 years ago
parent
commit
7485d0aaf0
  1. 71
      electrumx/server/mempool.py

71
electrumx/server/mempool.py

@ -21,6 +21,7 @@ from electrumx.server.db import UTXO
@attr.s(slots=True)
class MemPoolTx(object):
prevouts = attr.ib()
in_pairs = attr.ib()
out_pairs = attr.ib()
fee = attr.ib()
@ -35,7 +36,7 @@ class MemPool(object):
To that end we maintain the following maps:
tx_hash -> MemPoolTx (in_paris
tx_hash -> MemPoolTx
hashX -> set of all tx hashes in which the hashX appears
A pair is a (hashX, value) tuple. tx hashes are binary not strings.
@ -95,18 +96,17 @@ class MemPool(object):
'''
hashXs = self.hashXs
txs = self.txs
init_count = len(utxo_map)
deferred = {}
unspent = set(utxo_map)
# Try to find all previns so we can accept the TX
# Try to find all prevouts so we can accept the TX
for hash, tx in tx_map.items():
in_pairs = []
try:
for previn in tx.in_pairs:
utxo = utxo_map.get(previn)
for prevout in tx.prevouts:
utxo = utxo_map.get(prevout)
if not utxo:
prev_hash, prev_index = previn
prev_hash, prev_index = prevout
# Raises KeyError if prev_hash is not in txs
utxo = txs[prev_hash].out_pairs[prev_index]
in_pairs.append(utxo)
@ -114,12 +114,11 @@ class MemPool(object):
deferred[hash] = tx
continue
# Spend the previns
unspent.difference_update(tx.in_pairs)
# Spend the prevouts
unspent.difference_update(tx.prevouts)
# Convert in_pairs and add the TX to
# Save the in_pairs, compute the fee, and accept the TX
tx.in_pairs = in_pairs
# Compute fee
tx.fee = (sum(v for hashX, v in tx.in_pairs) -
sum(v for hashX, v in tx.out_pairs))
txs[hash] = tx
@ -127,7 +126,7 @@ class MemPool(object):
touched.add(hashX)
hashXs[hashX].add(hash)
return deferred, {previn: utxo_map[previn] for previn in unspent}
return deferred, {prevout: utxo_map[prevout] for prevout in unspent}
async def _refresh_hashes(self, single_pass):
'''Return a (hash set, height) pair when we're sure which height they
@ -224,7 +223,8 @@ class MemPool(object):
txin_pairs = [(txin.prev_hash, txin.prev_idx)
for txin in tx.inputs]
txs[hash] = MemPoolTx(txin_pairs, txout_pairs, 0, tx_size)
txs[hash] = MemPoolTx(txin_pairs, None, txout_pairs,
0, tx_size)
return txs
# Thread this potentially slow operation so as not to block
@ -233,29 +233,15 @@ class MemPool(object):
# Determine all prevouts not in the mempool, and fetch the
# UTXO information from the database. Failed prevout lookups
# return None - concurrent database updates happen
prevouts = [tx_in for tx in tx_map.values()for tx_in in tx.in_pairs
if tx_in[0] not in all_hashes]
prevouts = [prevout for tx in tx_map.values()
for prevout in tx.prevouts
if prevout[0] not in all_hashes]
utxos = await self.lookup_utxos(prevouts)
utxo_map = {prevout: utxo for prevout, utxo in zip(prevouts, utxos)}
# Attempt to complete processing of txs
return self._accept_transactions(tx_map, utxo_map, touched)
async def _raw_transactions(self, hashX):
'''Returns an iterable of (hex_hash, raw_tx) pairs for all
transactions in the mempool that touch hashX.
raw_tx can be None if the transaction has left the mempool.
'''
# hashXs is a defaultdict
if hashX not in self.hashXs:
return []
hashes = self.hashXs[hashX]
hex_hashes = [hash_to_hex_str(hash) for hash in hashes]
raw_txs = await self.daemon.getrawtransactions(hex_hashes)
return zip(hashes, raw_txs)
# External interface
async def start_and_wait_for_sync(self):
'''Starts the mempool synchronizer.
@ -295,15 +281,10 @@ class MemPool(object):
None, some or all of these may be spends of the hashX.
'''
deserializer = self.coin.DESERIALIZER
pairs = await self._raw_transactions(hashX)
result = set()
for hash, raw_tx in pairs:
if not raw_tx:
continue
tx = deserializer(raw_tx).read_tx()
for txin in tx.inputs:
result.add((txin.prev_hash, txin.prev_idx))
for tx_hash in self.hashXs.get(hashX, []):
tx = self.txs[tx_hash]
result.update(tx.prevouts)
return result
async def transaction_summaries(self, hashX):
@ -312,17 +293,13 @@ class MemPool(object):
unconfirmed is True if any txin is unconfirmed.
'''
deserializer = self.coin.DESERIALIZER
pairs = await self._raw_transactions(hashX)
# hashXs is a defaultdict, so use get() to query
result = []
for tx_hash, raw_tx in pairs:
mempool_tx = self.txs.get(tx_hash)
if not mempool_tx or not raw_tx:
continue
tx = deserializer(raw_tx).read_tx()
unconfirmed = any(txin.prev_hash in self.txs
for txin in tx.inputs)
result.append((tx_hash, mempool_tx.fee, unconfirmed))
for tx_hash in self.hashXs.get(hashX, []):
tx = self.txs[tx_hash]
unconfirmed = any(prev_hash in self.txs
for prev_hash, prev_idx in tx.prevouts)
result.append((tx_hash, tx.fee, unconfirmed))
return result
async def unordered_UTXOs(self, hashX):

Loading…
Cancel
Save