Browse Source

Create a MemPoolTx object and use it

patch-2
Neil Booth 7 years ago
parent
commit
21604cad15
  1. 83
      electrumx/server/mempool.py
  2. 3
      setup.py

83
electrumx/server/mempool.py

@ -1,4 +1,4 @@
# Copyright (c) 2016, Neil Booth
# Copyright (c) 2016-2018, Neil Booth
#
# All rights reserved.
#
@ -12,11 +12,22 @@ import itertools
import time
from collections import defaultdict
import attr
from electrumx.lib.hash import hash_to_hex_str, hex_str_to_hash
from electrumx.lib.util import class_logger
from electrumx.server.db import UTXO
@attr.s(slots=True)
class MemPoolTx(object):
hash = attr.ib()
in_pairs = attr.ib()
out_pairs = attr.ib()
fee = attr.ib()
size = attr.ib()
class MemPool(object):
'''Representation of the daemon's mempool.
@ -25,7 +36,7 @@ class MemPool(object):
To that end we maintain the following maps:
tx_hash -> (txin_pairs, txout_pairs, tx_fee, tx_size)
tx_hash -> MemPoolTx
hashX -> set of all tx hashes in which the hashX appears
A pair is a (hashX, value) tuple. tx hashes are hex strings.
@ -119,15 +130,14 @@ class MemPool(object):
for hex_hash in gone:
unfetched.discard(hex_hash)
unprocessed.pop(hex_hash, None)
item = txs.pop(hex_hash)
if item:
txin_pairs, txout_pairs, tx_fee, tx_size = item
fee_rate = tx_fee // tx_size
fee_hist[fee_rate] -= tx_size
tx = txs.pop(hex_hash)
if tx:
fee_rate = tx.fee // tx.size
fee_hist[fee_rate] -= tx.size
if fee_hist[fee_rate] == 0:
fee_hist.pop(fee_rate)
tx_hashXs = set(hashX for hashX, value in txin_pairs)
tx_hashXs.update(hashX for hashX, value in txout_pairs)
tx_hashXs = set(hashX for hashX, value in tx.in_pairs)
tx_hashXs.update(hashX for hashX, value in tx.out_pairs)
for hashX in tx_hashXs:
hashXs[hashX].remove(hex_hash)
if not hashXs[hashX]:
@ -191,7 +201,8 @@ class MemPool(object):
txin_pairs = [(hash_to_hex_str(txin.prev_hash), txin.prev_idx)
for txin in tx.inputs]
pending.append((tx_hash, txin_pairs, txout_pairs, tx_size))
pending.append(MemPoolTx(tx_hash, txin_pairs, txout_pairs,
0, tx_size))
# Do this potentially slow operation in a thread so as not to
# block
@ -202,8 +213,8 @@ class MemPool(object):
# otherwise presumably in the DB.
txs = self.txs
db_prevouts = [(hex_str_to_hash(prev_hash), prev_idx)
for item in pending
for (prev_hash, prev_idx) in item[1]
for tx in pending
for (prev_hash, prev_idx) in tx.in_pairs
if prev_hash not in txs]
# If a lookup fails, it returns a None entry
@ -216,33 +227,33 @@ class MemPool(object):
hashXs = self.hashXs
fee_hist = self.fee_histogram
for item in pending:
tx_hash, previns, txout_pairs, tx_size = item
if tx_hash not in txs:
for tx in pending:
if tx.hash not in txs:
continue
txin_pairs = []
in_pairs = []
try:
for previn in previns:
for previn in tx.in_pairs:
utxo = db_utxo_map.get(previn)
if not utxo:
prev_hash, prev_index = previn
# This can raise a KeyError or TypeError
utxo = txs[prev_hash][1][prev_index]
txin_pairs.append(utxo)
in_pairs.append(utxo)
except (KeyError, TypeError):
deferred.append(item)
deferred.append(tx)
continue
tx.in_pairs = in_pairs
# Compute fee
tx_fee = (sum(v for hashX, v in txin_pairs) -
sum(v for hashX, v in txout_pairs))
fee_rate = tx_fee // tx_size
fee_hist[fee_rate] += tx_size
txs[tx_hash] = (txin_pairs, txout_pairs, tx_fee, tx_size)
for hashX, value in itertools.chain(txin_pairs, txout_pairs):
tx_fee = (sum(v for hashX, v in tx.in_pairs) -
sum(v for hashX, v in tx.out_pairs))
fee_rate = tx.fee // tx.size
fee_hist[fee_rate] += tx.size
txs[tx.hash] = tx
for hashX, value in itertools.chain(tx.in_pairs, tx.out_pairs):
touched.add(hashX)
hashXs[hashX].add(tx_hash)
hashXs[hashX].add(tx.hash)
return deferred
@ -302,9 +313,9 @@ class MemPool(object):
# hashXs is a defaultdict
if hashX in self.hashXs:
for hex_hash in self.hashXs[hashX]:
txin_pairs, txout_pairs, tx_fee, tx_size = self.txs[hex_hash]
value -= sum(v for h168, v in txin_pairs if h168 == hashX)
value += sum(v for h168, v in txout_pairs if h168 == hashX)
tx = self.txs[hex_hash]
value -= sum(v for h168, v in tx.in_pairs if h168 == hashX)
value += sum(v for h168, v in tx.out_pairs if h168 == hashX)
return value
async def compact_fee_histogram(self):
@ -342,14 +353,13 @@ class MemPool(object):
pairs = await self._raw_transactions(hashX)
result = []
for hex_hash, raw_tx in pairs:
item = self.txs.get(hex_hash)
if not item or not raw_tx:
mempool_tx = self.txs.get(hex_hash)
if not mempool_tx or not raw_tx:
continue
tx_fee = item[2]
tx = deserializer(raw_tx).read_tx()
unconfirmed = any(hash_to_hex_str(txin.prev_hash) in self.txs
for txin in tx.inputs)
result.append((hex_hash, tx_fee, unconfirmed))
result.append((hex_hash, mempool_tx.fee, unconfirmed))
return result
async def unordered_UTXOs(self, hashX):
@ -362,11 +372,10 @@ class MemPool(object):
utxos = []
# hashXs is a defaultdict, so use get() to query
for hex_hash in self.hashXs.get(hashX, []):
item = self.txs.get(hex_hash)
if not item:
tx = self.txs.get(hex_hash)
if not tx:
continue
txout_pairs = item[1]
for pos, (hX, value) in enumerate(txout_pairs):
for pos, (hX, value) in enumerate(tx.out_pairs):
if hX == hashX:
# Unfortunately UTXO holds a binary hash
utxos.append(UTXO(-1, pos, hex_str_to_hash(hex_hash),

3
setup.py

@ -11,7 +11,8 @@ setuptools.setup(
# "tribus_hash" package is required to sync Denarius network.
# "blake256" package is required to sync Decred network.
# "xevan_hash" package is required to sync Xuez network.
install_requires=['aiorpcX >= 0.5.6', 'plyvel', 'pylru', 'aiohttp >= 2'],
install_requires=['aiorpcX >= 0.5.6', 'attrs>=15',
'plyvel', 'pylru', 'aiohttp >= 2'],
packages=setuptools.find_packages(include=('electrumx*',)),
description='ElectrumX Server',
author='Neil Booth',

Loading…
Cancel
Save