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

Loading…
Cancel
Save