Retain the prevouts with each MemPoolTx
This commit is contained in:
parent
45ccf8a64e
commit
7485d0aaf0
@ -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…
Reference in New Issue
Block a user