Browse Source

listunspent methods consider mempool receipts

- Update docs.  Height is 0 for mempool receipts
- Implement mempool.get_utxos() and use it
- Rename mempool.spends to mempool.potential_spends

Closes #365
patch-2
Neil Booth 7 years ago
parent
commit
76f4969a98
  1. 17
      docs/PROTOCOL.rst
  2. 11
      server/controller.py
  3. 29
      server/mempool.py

17
docs/PROTOCOL.rst

@ -272,14 +272,17 @@ Return an ordered list of UTXOs sent to a bitcoin address.
**Response**
A list of unspent outputs in blockchain order. Each transaction
is a dictionary with keys *height* , *tx_pos*, *tx_height* and
is a dictionary with keys *height* , *tx_pos*, *tx_hash* and
*value* keys. *height* is the integer height of the block the
transaction was confirmed in; if unconfirmed then *height* is 0 if
all inputs are confirmed, and -1 otherwise. *tx_hash* the
transaction hash in hexadecimal, *tx_pos* the zero-based index of
the output in the transaction's list of outputs, and *value* its
integer value in minimum coin units (satoshis in the case of
Bitcoin).
transaction was confirmed in, *tx_hash* the transaction hash in
hexadecimal, *tx_pos* the zero-based index of the output in the
transaction's list of outputs, and *value* its integer value in
minimum coin units (satoshis in the case of Bitcoin).
This function takes the mempool into account. Mempool
transactions paying to the address are included at the end of the
list in an undefined order, each with *tx_height* of zero. Any
output that is spent in the mempool does not appear.
**Response Example**

11
server/controller.py

@ -788,15 +788,16 @@ class Controller(ServerBase):
return await self.unconfirmed_history(hashX)
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.'''
'''Return the list of UTXOs of a script hash, including mempool
effects.'''
utxos = await self.get_utxos(hashX)
spends = await self.mempool.spends(hashX)
utxos = sorted(utxos)
utxos.extend(self.mempool.get_utxos(hashX))
spends = await self.mempool.potential_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)
for utxo in utxos
if (utxo.tx_hash, utxo.tx_pos) not in spends]
async def address_listunspent(self, address):

29
server/mempool.py

@ -305,14 +305,33 @@ class MemPool(util.LoggedClass):
item = self.txs.get(hex_hash)
if not item or not raw_tx:
continue
txin_pairs, txout_pairs, tx_fee, tx_size = item
tx_fee = item[2]
tx = deserializer(raw_tx).read_tx()
unconfirmed = any(hash_to_str(txin.prev_hash) in self.txs
for txin in tx.inputs)
result.append((hex_hash, tx_fee, unconfirmed))
return result
async def spends(self, hashX):
def get_utxos(self, hashX):
'''Return an unordered list of UTXO named tuples from mempool
transactions that pay to hashX.
This does not consider if any other mempool transactions spend
the outputs.
'''
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:
continue
txout_pairs = item[1]
for pos, (hX, value) in enumerate(txout_pairs):
if hX == hashX:
utxos.append(UTXO(-1, pos, hex_hash, 0, value))
return utxos
async def potential_spends(self, hashX):
'''Return a set of (prev_hash, prev_idx) pairs from mempool
transactions that touch hashX.
@ -320,14 +339,14 @@ class MemPool(util.LoggedClass):
'''
deserializer = self.coin.DESERIALIZER
pairs = await self.raw_transactions(hashX)
spends = set()
result = set()
for hex_hash, raw_tx in pairs:
if not raw_tx:
continue
tx = deserializer(raw_tx).read_tx()
for txin in tx.inputs:
spends.add((txin.prev_hash, txin.prev_idx))
return spends
result.add((txin.prev_hash, txin.prev_idx))
return result
def value(self, hashX):
'''Return the unconfirmed amount in the mempool for hashX.

Loading…
Cancel
Save