Skip to content

Commit 09aa14d

Browse files
committed
synchronizer: outpoint subs: always send spk_hint to server
1 parent 7578e1b commit 09aa14d

5 files changed

Lines changed: 39 additions & 25 deletions

File tree

electrum/address_synchronizer.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ def __init__(self, db: 'WalletDB', config: 'SimpleConfig', *, name: str = None):
102102

103103
self._get_balance_cache = {}
104104
self._get_utxos_cache = {}
105-
self._subscribed_outpoints = set()
105+
self._subscribed_outpoints = {} # type: dict[str, str] # outpoint->spk_hint
106106

107107
self.load_and_cleanup()
108108

@@ -239,13 +239,13 @@ def add_address(self, address: str) -> None:
239239
self.synchronizer.add_address(address)
240240
self.up_to_date_changed()
241241

242-
def subscribe_to_outpoint(self, outpoint):
242+
def subscribe_to_outpoint(self, outpoint: str, *, spk_hint: str) -> None:
243243
if outpoint in self._subscribed_outpoints:
244244
return
245-
self._subscribed_outpoints.add(outpoint)
245+
self._subscribed_outpoints[outpoint] = spk_hint
246246
if self.synchronizer:
247247
# fixme: launch polling task in synchronizer
248-
self.synchronizer.add_outpoint(outpoint)
248+
self.synchronizer.add_outpoint(outpoint, spk_hint=spk_hint)
249249
self.up_to_date_changed()
250250

251251
@with_lock
@@ -1079,7 +1079,7 @@ def subscribe_to_tx_outpoints(self, spender_txid: str):
10791079
spender_tx = self.get_transaction(spender_txid)
10801080
for i, o in enumerate(spender_tx.outputs()):
10811081
txo = spender_txid + ':%d'%i
1082-
self.subscribe_to_outpoint(txo)
1082+
self.subscribe_to_outpoint(txo, spk_hint=o.scriptpubkey.hex())
10831083

10841084
def get_tx_mined_depth(self, txid: str):
10851085
if not txid:

electrum/interface.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -238,17 +238,21 @@ def set_default_timeout(self, timeout):
238238
async def subscribe(self, method: str, params: List, queue: asyncio.Queue):
239239
# note: until the cache is written for the first time,
240240
# each 'subscribe' call might make a request on the network.
241-
params2 = params[:]
241+
notif_params = params[:]
242242
if method == 'blockchain.scriptpubkey.subscribe':
243-
params2[0] = script_to_scripthash(bytes.fromhex(params2[0]))
244-
key = self.get_hashable_key_for_rpc_call(method, params2)
243+
# params: spk
244+
notif_params[0] = script_to_scripthash(bytes.fromhex(params[0])) # convert spk->sh
245+
elif method == 'blockchain.outpoint.subscribe':
246+
# params: tx_hash, txout_idx, spk_hint
247+
notif_params = notif_params[0:2] # drop spk_hint
248+
key = self.get_hashable_key_for_rpc_call(method, notif_params)
245249
self.subscriptions[key].append(queue)
246250
if key in self.cache:
247251
result = self.cache[key]
248252
else:
249253
result = await self.send_request(method, params)
250254
self.cache[key] = result
251-
await queue.put(params2 + [result])
255+
await queue.put(notif_params + [result])
252256

253257
def unsubscribe(self, queue):
254258
"""Unsubscribe a callback to free object references to enable GC."""

electrum/lnchannel.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
from electrum_ecc import ECPubkey
3737

3838
from . import constants, util
39+
from . import bitcoin
3940
from .util import bfh, chunks, TxMinedInfo, error_text_bytes_to_safe_str
4041
from .bitcoin import redeem_script_to_address
4142
from .crypto import sha256, sha256d
@@ -481,6 +482,9 @@ def is_funding_tx_mined(self, funding_height: TxMinedInfo) -> bool:
481482
def get_funding_address(self) -> str:
482483
pass
483484

485+
def get_funding_scriptpubkey(self) -> bytes:
486+
return bitcoin.address_to_script(self.get_funding_address())
487+
484488
def get_funding_tx(self) -> Optional[Transaction]:
485489
funding_txid = self.funding_outpoint.txid
486490
return self.lnworker.lnwatcher.adb.get_transaction(funding_txid)

electrum/lnwatcher.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ def __init__(self, lnworker: 'LNWallet'):
2626
Logger.__init__(self)
2727
self.adb = lnworker.wallet.adb
2828
self.config = lnworker.config
29-
self.callbacks = {} # type: Dict[str, Callable[[], Awaitable[None]]] # address -> lambda function
29+
self.callbacks = {} # type: Dict[str, Callable[[], Awaitable[None]]] # address/txo -> lambda function
3030
self.network = None
3131
self.register_callbacks()
3232
self._pending_force_closes = set()
@@ -62,10 +62,11 @@ def add_outpoint_callback(
6262
outpoint: str,
6363
callback: Callable[[], Awaitable[None]],
6464
*,
65+
spk_hint: str,
6566
subscribe: bool = True,
6667
) -> None:
6768
if subscribe:
68-
self.adb.subscribe_to_outpoint(outpoint)
69+
self.adb.subscribe_to_outpoint(outpoint, spk_hint=spk_hint)
6970
self.callbacks[outpoint] = callback
7071

7172
async def trigger_callbacks(self, *, requires_synchronizer: bool = True):
@@ -107,7 +108,11 @@ async def on_event_adb_set_up_to_date(self, adb):
107108
def add_channel(self, chan: 'AbstractChannel') -> None:
108109
outpoint = chan.funding_outpoint.to_str()
109110
callback = lambda: self.check_onchain_situation(outpoint)
110-
self.add_outpoint_callback(outpoint, callback, subscribe=chan.need_to_subscribe())
111+
self.add_outpoint_callback(
112+
outpoint, callback,
113+
spk_hint=chan.get_funding_scriptpubkey().hex(),
114+
subscribe=chan.need_to_subscribe(),
115+
)
111116

112117
@ignore_exceptions
113118
@log_exceptions
@@ -121,7 +126,7 @@ async def check_onchain_situation(self, funding_outpoint: str) -> None:
121126
closing_txid = self.adb.get_spender(funding_outpoint)
122127
closing_height = self.adb.get_tx_height(closing_txid)
123128
if closing_txid:
124-
self.adb.subscribe_to_tx_outpoints(closing_txid)
129+
self.adb.subscribe_to_tx_outpoints(closing_txid) # FIXME this assumes we alrdy have closing_tx
125130
closing_tx = self.adb.get_transaction(closing_txid)
126131
if closing_tx:
127132
keep_watching = await self.sweep_commitment_transaction(funding_outpoint, closing_tx)

electrum/synchronizer.py

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ def __init__(self, network: 'Network'):
6666
def _reset(self):
6767
super()._reset()
6868
self._adding_addrs = set()
69-
self._adding_outpoints = set()
69+
self._adding_outpoints = {} # type: dict[str, str] # outpoint->spk_hint
7070
self.requested_addrs = set()
7171
self.requested_outpoints = set()
7272
self._handling_addr_statuses = set()
@@ -93,8 +93,9 @@ def add_address(self, addr: str) -> None:
9393
if not is_address(addr): raise ValueError(f"invalid bitcoin address {neuter_bitcoin_address(addr)}")
9494
self._adding_addrs.add(addr) # this lets is_up_to_date already know about addr
9595

96-
def add_outpoint(self, outpoint: str) -> None:
97-
self._adding_outpoints.add(outpoint) # this lets is_up_to_date already know about outpoint
96+
def add_outpoint(self, outpoint: str, *, spk_hint: str) -> None:
97+
# this lets is_up_to_date already know about outpoint
98+
self._adding_outpoints[outpoint] = spk_hint
9899

99100
async def _add_address(self, addr: str):
100101
try:
@@ -105,13 +106,13 @@ async def _add_address(self, addr: str):
105106
finally:
106107
self._adding_addrs.discard(addr) # ok for addr not to be present
107108

108-
async def _add_outpoint(self, outpoint: str):
109+
async def _add_outpoint(self, outpoint: str, *, spk_hint: str):
109110
try:
110111
if outpoint in self.requested_outpoints: return
111112
self.requested_outpoints.add(outpoint)
112-
await self.taskgroup.spawn(self._subscribe_to_outpoint, outpoint)
113+
await self.taskgroup.spawn(self._subscribe_to_outpoint(outpoint, spk_hint=spk_hint))
113114
finally:
114-
self._adding_outpoints.discard(outpoint) # ok for addr not to be present
115+
self._adding_outpoints.pop(outpoint, None) # ok for outpoint not to be present
115116

116117
async def _on_address_status(self, addr: str, status: Optional[str]):
117118
"""Handle the change of the status of an address.
@@ -137,14 +138,14 @@ async def _subscribe_to_address(self, addr):
137138
raise
138139
self._requests_answered += 1
139140

140-
async def _subscribe_to_outpoint(self, outpoint):
141+
async def _subscribe_to_outpoint(self, outpoint: str, *, spk_hint: str) -> None:
141142
self._requests_sent += 1
142143
txhash, idx = outpoint.split(':')
143144
idx = int(idx)
144145
self.logger.info(f'subscribe to outpoint: {txhash}:{idx}')
145146
try:
146147
async with self._network_request_semaphore:
147-
await self.session.subscribe('blockchain.outpoint.subscribe', [txhash, idx], self.outpoint_status_queue)
148+
await self.session.subscribe('blockchain.outpoint.subscribe', [txhash, idx, spk_hint], self.outpoint_status_queue)
148149
except RPCError as e:
149150
raise
150151
self._requests_answered += 1
@@ -365,17 +366,17 @@ async def main(self):
365366
for addr in random_shuffled_copy(self.adb.get_addresses()):
366367
await self._add_address(addr)
367368
# add outpoints to bootstrap (race)
368-
for outpoint in self.adb._subscribed_outpoints:
369-
await self._add_outpoint(outpoint)
369+
for outpoint, spk_hint in self.adb._subscribed_outpoints.items():
370+
await self._add_outpoint(outpoint, spk_hint=spk_hint)
370371
# main loop
371372
self._init_done = True
372373
prev_uptodate = False
373374
while True:
374375
await asyncio.sleep(0.1)
375376
for addr in self._adding_addrs.copy(): # copy set to ensure iterator stability
376377
await self._add_address(addr)
377-
for outpoint in list(self._adding_outpoints):
378-
await self._add_outpoint(outpoint)
378+
for outpoint, spk_hint in self._adding_outpoints.copy().items():
379+
await self._add_outpoint(outpoint, spk_hint=spk_hint)
379380
up_to_date = self.adb.is_up_to_date()
380381
# see if status changed
381382
if (up_to_date != prev_uptodate

0 commit comments

Comments
 (0)