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)
|
@attr.s(slots=True)
|
||||||
class MemPoolTx(object):
|
class MemPoolTx(object):
|
||||||
|
prevouts = attr.ib()
|
||||||
in_pairs = attr.ib()
|
in_pairs = attr.ib()
|
||||||
out_pairs = attr.ib()
|
out_pairs = attr.ib()
|
||||||
fee = attr.ib()
|
fee = attr.ib()
|
||||||
@ -35,7 +36,7 @@ class MemPool(object):
|
|||||||
|
|
||||||
To that end we maintain the following maps:
|
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
|
hashX -> set of all tx hashes in which the hashX appears
|
||||||
|
|
||||||
A pair is a (hashX, value) tuple. tx hashes are binary not strings.
|
A pair is a (hashX, value) tuple. tx hashes are binary not strings.
|
||||||
@ -95,18 +96,17 @@ class MemPool(object):
|
|||||||
'''
|
'''
|
||||||
hashXs = self.hashXs
|
hashXs = self.hashXs
|
||||||
txs = self.txs
|
txs = self.txs
|
||||||
init_count = len(utxo_map)
|
|
||||||
|
|
||||||
deferred = {}
|
deferred = {}
|
||||||
unspent = set(utxo_map)
|
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():
|
for hash, tx in tx_map.items():
|
||||||
in_pairs = []
|
in_pairs = []
|
||||||
try:
|
try:
|
||||||
for previn in tx.in_pairs:
|
for prevout in tx.prevouts:
|
||||||
utxo = utxo_map.get(previn)
|
utxo = utxo_map.get(prevout)
|
||||||
if not utxo:
|
if not utxo:
|
||||||
prev_hash, prev_index = previn
|
prev_hash, prev_index = prevout
|
||||||
# Raises KeyError if prev_hash is not in txs
|
# Raises KeyError if prev_hash is not in txs
|
||||||
utxo = txs[prev_hash].out_pairs[prev_index]
|
utxo = txs[prev_hash].out_pairs[prev_index]
|
||||||
in_pairs.append(utxo)
|
in_pairs.append(utxo)
|
||||||
@ -114,12 +114,11 @@ class MemPool(object):
|
|||||||
deferred[hash] = tx
|
deferred[hash] = tx
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Spend the previns
|
# Spend the prevouts
|
||||||
unspent.difference_update(tx.in_pairs)
|
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
|
tx.in_pairs = in_pairs
|
||||||
# Compute fee
|
|
||||||
tx.fee = (sum(v for hashX, v in tx.in_pairs) -
|
tx.fee = (sum(v for hashX, v in tx.in_pairs) -
|
||||||
sum(v for hashX, v in tx.out_pairs))
|
sum(v for hashX, v in tx.out_pairs))
|
||||||
txs[hash] = tx
|
txs[hash] = tx
|
||||||
@ -127,7 +126,7 @@ class MemPool(object):
|
|||||||
touched.add(hashX)
|
touched.add(hashX)
|
||||||
hashXs[hashX].add(hash)
|
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):
|
async def _refresh_hashes(self, single_pass):
|
||||||
'''Return a (hash set, height) pair when we're sure which height they
|
'''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)
|
txin_pairs = [(txin.prev_hash, txin.prev_idx)
|
||||||
for txin in tx.inputs]
|
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
|
return txs
|
||||||
|
|
||||||
# Thread this potentially slow operation so as not to block
|
# 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
|
# Determine all prevouts not in the mempool, and fetch the
|
||||||
# UTXO information from the database. Failed prevout lookups
|
# UTXO information from the database. Failed prevout lookups
|
||||||
# return None - concurrent database updates happen
|
# return None - concurrent database updates happen
|
||||||
prevouts = [tx_in for tx in tx_map.values()for tx_in in tx.in_pairs
|
prevouts = [prevout for tx in tx_map.values()
|
||||||
if tx_in[0] not in all_hashes]
|
for prevout in tx.prevouts
|
||||||
|
if prevout[0] not in all_hashes]
|
||||||
utxos = await self.lookup_utxos(prevouts)
|
utxos = await self.lookup_utxos(prevouts)
|
||||||
utxo_map = {prevout: utxo for prevout, utxo in zip(prevouts, utxos)}
|
utxo_map = {prevout: utxo for prevout, utxo in zip(prevouts, utxos)}
|
||||||
|
|
||||||
# Attempt to complete processing of txs
|
# Attempt to complete processing of txs
|
||||||
return self._accept_transactions(tx_map, utxo_map, touched)
|
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
|
# External interface
|
||||||
async def start_and_wait_for_sync(self):
|
async def start_and_wait_for_sync(self):
|
||||||
'''Starts the mempool synchronizer.
|
'''Starts the mempool synchronizer.
|
||||||
@ -295,15 +281,10 @@ class MemPool(object):
|
|||||||
|
|
||||||
None, some or all of these may be spends of the hashX.
|
None, some or all of these may be spends of the hashX.
|
||||||
'''
|
'''
|
||||||
deserializer = self.coin.DESERIALIZER
|
|
||||||
pairs = await self._raw_transactions(hashX)
|
|
||||||
result = set()
|
result = set()
|
||||||
for hash, raw_tx in pairs:
|
for tx_hash in self.hashXs.get(hashX, []):
|
||||||
if not raw_tx:
|
tx = self.txs[tx_hash]
|
||||||
continue
|
result.update(tx.prevouts)
|
||||||
tx = deserializer(raw_tx).read_tx()
|
|
||||||
for txin in tx.inputs:
|
|
||||||
result.add((txin.prev_hash, txin.prev_idx))
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
async def transaction_summaries(self, hashX):
|
async def transaction_summaries(self, hashX):
|
||||||
@ -312,17 +293,13 @@ class MemPool(object):
|
|||||||
|
|
||||||
unconfirmed is True if any txin is unconfirmed.
|
unconfirmed is True if any txin is unconfirmed.
|
||||||
'''
|
'''
|
||||||
deserializer = self.coin.DESERIALIZER
|
# hashXs is a defaultdict, so use get() to query
|
||||||
pairs = await self._raw_transactions(hashX)
|
|
||||||
result = []
|
result = []
|
||||||
for tx_hash, raw_tx in pairs:
|
for tx_hash in self.hashXs.get(hashX, []):
|
||||||
mempool_tx = self.txs.get(tx_hash)
|
tx = self.txs[tx_hash]
|
||||||
if not mempool_tx or not raw_tx:
|
unconfirmed = any(prev_hash in self.txs
|
||||||
continue
|
for prev_hash, prev_idx in tx.prevouts)
|
||||||
tx = deserializer(raw_tx).read_tx()
|
result.append((tx_hash, tx.fee, unconfirmed))
|
||||||
unconfirmed = any(txin.prev_hash in self.txs
|
|
||||||
for txin in tx.inputs)
|
|
||||||
result.append((tx_hash, mempool_tx.fee, unconfirmed))
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
async def unordered_UTXOs(self, hashX):
|
async def unordered_UTXOs(self, hashX):
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user