Skip to content

Commit 5e66f5a

Browse files
committed
session: implement "blockchain.outpoint.get_status" RPC
1 parent 7b6d260 commit 5e66f5a

1 file changed

Lines changed: 31 additions & 7 deletions

File tree

src/electrumx/server/session.py

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1285,13 +1285,13 @@ async def _notify_inner(
12851285
method = 'blockchain.outpoint.subscribe'
12861286
txo_to_status = {}
12871287
for prevout in touched_outpoints:
1288-
txo_to_status[prevout] = await self.txoutpoint_status(*prevout)
1288+
txo_to_status[prevout] = await self.txoutpoint_status_for_notif(*prevout)
12891289

12901290
# Check mempool TXOs - the status is a function of the confirmed state of
12911291
# other transactions. (this is to detect if height changed from -1 to 0)
12921292
mempool_txoutpoint_statuses = self.mempool_txoutpoint_statuses.copy()
12931293
for prevout, old_status in mempool_txoutpoint_statuses.items():
1294-
status = await self.txoutpoint_status(*prevout)
1294+
status = await self.txoutpoint_status_for_notif(*prevout)
12951295
if status != old_status:
12961296
txo_to_status[prevout] = status
12971297

@@ -1332,6 +1332,7 @@ async def address_status(self, hashX: bytes) -> Optional[str]:
13321332
'''Returns an address status.
13331333
13341334
Status is a hex string, but must be None if there is no history.
1335+
Side-effect: updates client-last-seen status, used by notifications.
13351336
'''
13361337
# Note both confirmed history and mempool history are ordered
13371338
# For mempool, height is -1 if it has unconfirmed inputs, otherwise 0
@@ -1353,6 +1354,7 @@ async def address_status(self, hashX: bytes) -> Optional[str]:
13531354
else:
13541355
status = None
13551356

1357+
# update status last sent to client
13561358
if mempool:
13571359
self.mempool_hashX_statuses[hashX] = status
13581360
else:
@@ -1369,7 +1371,7 @@ async def subscription_address_status(self, hashX):
13691371
self.unsubscribe_hashX(hashX)
13701372
return None
13711373

1372-
async def txoutpoint_status(self, prev_txhash: bytes, txout_idx: int) -> Dict[str, Any]:
1374+
async def _calc_txoutpoint_status(self, prev_txhash: bytes, txout_idx: int) -> Dict[str, Any]:
13731375
self.bump_cost(0.2)
13741376
spend_status = await self.db.spender_for_txo(prev_txhash, txout_idx)
13751377
if spend_status.spender_height is not None:
@@ -1393,14 +1395,20 @@ async def txoutpoint_status(self, prev_txhash: bytes, txout_idx: int) -> Dict[st
13931395
assert spend_status.spender_height is not None
13941396
status['spender_txhash'] = hash_to_hex_str(spend_status.spender_txhash)
13951397
status['spender_height'] = spend_status.spender_height
1398+
return status
13961399

1400+
async def txoutpoint_status_for_notif(self, prev_txhash: bytes, txout_idx: int) -> Dict[str, Any]:
1401+
"""Side-effect: updates client-last-seen status, used by notifications."""
1402+
status = await self._calc_txoutpoint_status(prev_txhash=prev_txhash, txout_idx=txout_idx)
1403+
# update status last sent to client
13971404
prevout = (prev_txhash, txout_idx)
1398-
if ((spend_status.prev_height is not None and spend_status.prev_height <= 0)
1399-
or (spend_status.spender_height is not None and spend_status.spender_height <= 0)):
1405+
prev_height = status.get('height') # type: Optional[int]
1406+
spender_height = status.get('spender_height') # type: Optional[int]
1407+
if ((prev_height is not None and prev_height <= 0)
1408+
or (spender_height is not None and spender_height <= 0)):
14001409
self.mempool_txoutpoint_statuses[prevout] = status
14011410
else:
14021411
self.mempool_txoutpoint_statuses.pop(prevout, None)
1403-
14041412
return status
14051413

14061414
async def hashX_listunspent(self, hashX):
@@ -1506,6 +1514,20 @@ def scriptpubkey_unsubscribe(self, spk: str):
15061514
scripthash = spk_to_scripthash(spk)
15071515
return self.scripthash_unsubscribe(scripthash)
15081516

1517+
async def txoutpoint_get_status(self, tx_hash, txout_idx, spk_hint=None):
1518+
'''Return the status of an outpoint, without subscribing.
1519+
1520+
spk_hint: scriptPubKey corresponding to the outpoint. Might be used by
1521+
other servers, but we don't need and hence ignore it.
1522+
'''
1523+
tx_hash = assert_tx_hash(tx_hash)
1524+
txout_idx = non_negative_integer(txout_idx)
1525+
if spk_hint is not None:
1526+
assert_hex_str(spk_hint)
1527+
# calc status (but do not side-effect client-last-seen status)
1528+
spend_status = await self._calc_txoutpoint_status(tx_hash, txout_idx)
1529+
return spend_status
1530+
15091531
async def txoutpoint_subscribe(self, tx_hash, txout_idx, spk_hint=None):
15101532
'''Subscribe to an outpoint.
15111533
@@ -1516,7 +1538,8 @@ async def txoutpoint_subscribe(self, tx_hash, txout_idx, spk_hint=None):
15161538
txout_idx = non_negative_integer(txout_idx)
15171539
if spk_hint is not None:
15181540
assert_hex_str(spk_hint)
1519-
spend_status = await self.txoutpoint_status(tx_hash, txout_idx)
1541+
# calc status, update client-last-seen status, and sub to outpoint
1542+
spend_status = await self.txoutpoint_status_for_notif(tx_hash, txout_idx)
15201543
self.txoutpoint_subs.add((tx_hash, txout_idx))
15211544
return spend_status
15221545

@@ -2026,6 +2049,7 @@ def set_request_handlers(self, ptuple):
20262049
# experimental:
20272050
if ptuple >= (1, 7):
20282051
handlers['blockchain.outpoint.subscribe'] = self.txoutpoint_subscribe
2052+
handlers['blockchain.outpoint.get_status'] = self.txoutpoint_get_status
20292053
handlers['blockchain.outpoint.unsubscribe'] = self.txoutpoint_unsubscribe
20302054
handlers['blockchain.scriptpubkey.get_balance'] = self.scriptpubkey_get_balance
20312055
handlers['blockchain.scriptpubkey.get_history'] = self.scriptpubkey_get_history

0 commit comments

Comments
 (0)