Browse Source

Fix listunspent methods to remove mempool spends

Fixes #277
patch-1
Neil Booth 7 years ago
parent
commit
346385680e
  1. 20
      server/controller.py
  2. 38
      server/mempool.py

20
server/controller.py

@ -795,19 +795,27 @@ class Controller(ServerBase):
hashX = self.address_to_hashX(address)
raise RPCError('address.get_proof is not yet implemented')
async def hashX_listunspent(self, hashX):
'''Return the list of UTXOs of a script hash.
We should remove mempool spends from the in-DB UTXOs.'''
utxos = await self.get_utxos(hashX)
spends = await self.mempool.spends(hashX)
return [{'tx_hash': hash_to_str(utxo.tx_hash), 'tx_pos': utxo.tx_pos,
'height': utxo.height, 'value': utxo.value}
for utxo in sorted(utxos)
if (utxo.tx_hash, utxo.tx_pos) not in spends]
async def address_listunspent(self, address):
'''Return the list of UTXOs of an address.'''
hashX = self.address_to_hashX(address)
return [{'tx_hash': hash_to_str(utxo.tx_hash), 'tx_pos': utxo.tx_pos,
'height': utxo.height, 'value': utxo.value}
for utxo in sorted(await self.get_utxos(hashX))]
return await self.hashX_listunspent(hashX)
async def scripthash_listunspent(self, scripthash):
'''Return the list of UTXOs of a scripthash.'''
hashX = self.scripthash_to_hashX(scripthash)
return [{'tx_hash': hash_to_str(utxo.tx_hash), 'tx_pos': utxo.tx_pos,
'height': utxo.height, 'value': utxo.value}
for utxo in sorted(await self.get_utxos(hashX))]
return await self.hashX_listunspent(hashX)
def block_get_header(self, height):
'''The deserialized header at a given height.

38
server/mempool.py

@ -260,21 +260,30 @@ class MemPool(util.LoggedClass):
return result, deferred
async def transactions(self, hashX):
'''Generate (hex_hash, tx_fee, unconfirmed) tuples for mempool
entries for the hashX.
async def raw_transactions(self, hashX):
'''Returns an iterable of (hex_hash, raw_tx) pairs for all
transactions in the mempool that touch hashX.
unconfirmed is True if any txin is unconfirmed.
raw_tx can be None if the transaction has left the mempool.
'''
# hashXs is a defaultdict
if hashX not in self.hashXs:
return []
deserializer = self.coin.DESERIALIZER
hex_hashes = self.hashXs[hashX]
raw_txs = await self.daemon.getrawtransactions(hex_hashes)
return zip(hex_hashes, raw_txs)
async def transactions(self, hashX):
'''Generate (hex_hash, tx_fee, unconfirmed) tuples for mempool
entries for the hashX.
unconfirmed is True if any txin is unconfirmed.
'''
deserializer = self.coin.DESERIALIZER
pairs = await self.raw_transactions(hashX)
result = []
for hex_hash, raw_tx in zip(hex_hashes, raw_txs):
for hex_hash, raw_tx in pairs:
item = self.txs.get(hex_hash)
if not item or not raw_tx:
continue
@ -287,6 +296,23 @@ class MemPool(util.LoggedClass):
result.append((hex_hash, tx_fee, unconfirmed))
return result
async def spends(self, hashX):
'''Return a set of (prev_hash, prev_idx) pairs from mempool
transactions that touch hashX.
None, some or all of these may be spends of the hashX.
'''
deserializer = self.coin.DESERIALIZER
pairs = await self.raw_transactions(hashX)
spends = set()
for hex_hash, raw_tx in pairs:
if not raw_tx:
continue
tx, tx_hash = deserializer(raw_tx).read_tx()
for txin in tx.inputs:
spends.add((txin.prev_hash, txin.prev_idx))
return spends
def value(self, hashX):
'''Return the unconfirmed amount in the mempool for hashX.

Loading…
Cancel
Save