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
This commit is contained in:
Neil Booth 2018-02-11 23:21:30 +08:00
parent 3e6ced6039
commit 76f4969a98
3 changed files with 40 additions and 17 deletions

View File

@ -272,14 +272,17 @@ Return an ordered list of UTXOs sent to a bitcoin address.
**Response** **Response**
A list of unspent outputs in blockchain order. Each transaction 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 *value* keys. *height* is the integer height of the block the
transaction was confirmed in; if unconfirmed then *height* is 0 if transaction was confirmed in, *tx_hash* the transaction hash in
all inputs are confirmed, and -1 otherwise. *tx_hash* the hexadecimal, *tx_pos* the zero-based index of the output in the
transaction hash in hexadecimal, *tx_pos* the zero-based index of transaction's list of outputs, and *value* its integer value in
the output in the transaction's list of outputs, and *value* its minimum coin units (satoshis in the case of Bitcoin).
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** **Response Example**

View File

@ -788,15 +788,16 @@ class Controller(ServerBase):
return await self.unconfirmed_history(hashX) return await self.unconfirmed_history(hashX)
async def hashX_listunspent(self, hashX): async def hashX_listunspent(self, hashX):
'''Return the list of UTXOs of a script hash. '''Return the list of UTXOs of a script hash, including mempool
effects.'''
We should remove mempool spends from the in-DB UTXOs.'''
utxos = await self.get_utxos(hashX) 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, return [{'tx_hash': hash_to_str(utxo.tx_hash), 'tx_pos': utxo.tx_pos,
'height': utxo.height, 'value': utxo.value} '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] if (utxo.tx_hash, utxo.tx_pos) not in spends]
async def address_listunspent(self, address): async def address_listunspent(self, address):

View File

@ -305,14 +305,33 @@ class MemPool(util.LoggedClass):
item = self.txs.get(hex_hash) item = self.txs.get(hex_hash)
if not item or not raw_tx: if not item or not raw_tx:
continue continue
txin_pairs, txout_pairs, tx_fee, tx_size = item tx_fee = item[2]
tx = deserializer(raw_tx).read_tx() tx = deserializer(raw_tx).read_tx()
unconfirmed = any(hash_to_str(txin.prev_hash) in self.txs unconfirmed = any(hash_to_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, tx_fee, unconfirmed))
return result 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 '''Return a set of (prev_hash, prev_idx) pairs from mempool
transactions that touch hashX. transactions that touch hashX.
@ -320,14 +339,14 @@ class MemPool(util.LoggedClass):
''' '''
deserializer = self.coin.DESERIALIZER deserializer = self.coin.DESERIALIZER
pairs = await self.raw_transactions(hashX) pairs = await self.raw_transactions(hashX)
spends = set() result = set()
for hex_hash, raw_tx in pairs: for hex_hash, raw_tx in pairs:
if not raw_tx: if not raw_tx:
continue continue
tx = deserializer(raw_tx).read_tx() tx = deserializer(raw_tx).read_tx()
for txin in tx.inputs: for txin in tx.inputs:
spends.add((txin.prev_hash, txin.prev_idx)) result.add((txin.prev_hash, txin.prev_idx))
return spends return result
def value(self, hashX): def value(self, hashX):
'''Return the unconfirmed amount in the mempool for hashX. '''Return the unconfirmed amount in the mempool for hashX.