tlnsweep.py - electrum - Electrum Bitcoin wallet
HTML git clone https://git.parazyd.org/electrum
DIR Log
DIR Files
DIR Refs
DIR Submodules
---
tlnsweep.py (26747B)
---
1 # Copyright (C) 2018 The Electrum developers
2 # Distributed under the MIT software license, see the accompanying
3 # file LICENCE or http://www.opensource.org/licenses/mit-license.php
4
5 from typing import Optional, Dict, List, Tuple, TYPE_CHECKING, NamedTuple, Callable
6 from enum import Enum, auto
7
8 from .util import bfh, bh2u
9 from .bitcoin import redeem_script_to_address, dust_threshold, construct_witness
10 from . import ecc
11 from .lnutil import (make_commitment_output_to_remote_address, make_commitment_output_to_local_witness_script,
12 derive_privkey, derive_pubkey, derive_blinded_pubkey, derive_blinded_privkey,
13 make_htlc_tx_witness, make_htlc_tx_with_open_channel, UpdateAddHtlc,
14 LOCAL, REMOTE, make_htlc_output_witness_script,
15 get_ordered_channel_configs, privkey_to_pubkey, get_per_commitment_secret_from_seed,
16 RevocationStore, extract_ctn_from_tx_and_chan, UnableToDeriveSecret, SENT, RECEIVED,
17 map_htlcs_to_ctx_output_idxs, Direction)
18 from .transaction import (Transaction, TxOutput, PartialTransaction, PartialTxInput,
19 PartialTxOutput, TxOutpoint)
20 from .simple_config import SimpleConfig
21 from .logging import get_logger, Logger
22
23 if TYPE_CHECKING:
24 from .lnchannel import Channel, AbstractChannel
25
26
27 _logger = get_logger(__name__)
28
29
30 class SweepInfo(NamedTuple):
31 name: str
32 csv_delay: int
33 cltv_expiry: int
34 gen_tx: Callable[[], Optional[Transaction]]
35
36
37 def create_sweeptxs_for_watchtower(chan: 'Channel', ctx: Transaction, per_commitment_secret: bytes,
38 sweep_address: str) -> List[Transaction]:
39 """Presign sweeping transactions using the just received revoked pcs.
40 These will only be utilised if the remote breaches.
41 Sweep 'to_local', and all the HTLCs (two cases: directly from ctx, or from HTLC tx).
42 """
43 # prep
44 ctn = extract_ctn_from_tx_and_chan(ctx, chan)
45 pcp = ecc.ECPrivkey(per_commitment_secret).get_public_key_bytes(compressed=True)
46 this_conf, other_conf = get_ordered_channel_configs(chan=chan, for_us=False)
47 other_revocation_privkey = derive_blinded_privkey(other_conf.revocation_basepoint.privkey,
48 per_commitment_secret)
49 to_self_delay = other_conf.to_self_delay
50 this_delayed_pubkey = derive_pubkey(this_conf.delayed_basepoint.pubkey, pcp)
51 txs = []
52 # to_local
53 revocation_pubkey = ecc.ECPrivkey(other_revocation_privkey).get_public_key_bytes(compressed=True)
54 witness_script = bh2u(make_commitment_output_to_local_witness_script(
55 revocation_pubkey, to_self_delay, this_delayed_pubkey))
56 to_local_address = redeem_script_to_address('p2wsh', witness_script)
57 output_idxs = ctx.get_output_idxs_from_address(to_local_address)
58 if output_idxs:
59 output_idx = output_idxs.pop()
60 sweep_tx = create_sweeptx_ctx_to_local(
61 sweep_address=sweep_address,
62 ctx=ctx,
63 output_idx=output_idx,
64 witness_script=witness_script,
65 privkey=other_revocation_privkey,
66 is_revocation=True,
67 config=chan.lnworker.config)
68 if sweep_tx:
69 txs.append(sweep_tx)
70 # HTLCs
71 def create_sweeptx_for_htlc(*, htlc: 'UpdateAddHtlc', htlc_direction: Direction,
72 ctx_output_idx: int) -> Optional[Transaction]:
73 htlc_tx_witness_script, htlc_tx = make_htlc_tx_with_open_channel(
74 chan=chan,
75 pcp=pcp,
76 subject=REMOTE,
77 ctn=ctn,
78 htlc_direction=htlc_direction,
79 commit=ctx,
80 htlc=htlc,
81 ctx_output_idx=ctx_output_idx)
82 return create_sweeptx_that_spends_htlctx_that_spends_htlc_in_ctx(
83 to_self_delay=0,
84 htlc_tx=htlc_tx,
85 htlctx_witness_script=htlc_tx_witness_script,
86 sweep_address=sweep_address,
87 privkey=other_revocation_privkey,
88 is_revocation=True,
89 config=chan.lnworker.config)
90
91 htlc_to_ctx_output_idx_map = map_htlcs_to_ctx_output_idxs(
92 chan=chan,
93 ctx=ctx,
94 pcp=pcp,
95 subject=REMOTE,
96 ctn=ctn)
97 for (direction, htlc), (ctx_output_idx, htlc_relative_idx) in htlc_to_ctx_output_idx_map.items():
98 secondstage_sweep_tx = create_sweeptx_for_htlc(
99 htlc=htlc,
100 htlc_direction=direction,
101 ctx_output_idx=ctx_output_idx)
102 if secondstage_sweep_tx:
103 txs.append(secondstage_sweep_tx)
104 return txs
105
106
107 def create_sweeptx_for_their_revoked_ctx(
108 chan: 'Channel',
109 ctx: Transaction,
110 per_commitment_secret: bytes,
111 sweep_address: str) -> Optional[Callable[[], Optional[Transaction]]]:
112 # prep
113 pcp = ecc.ECPrivkey(per_commitment_secret).get_public_key_bytes(compressed=True)
114 this_conf, other_conf = get_ordered_channel_configs(chan=chan, for_us=False)
115 other_revocation_privkey = derive_blinded_privkey(other_conf.revocation_basepoint.privkey,
116 per_commitment_secret)
117 to_self_delay = other_conf.to_self_delay
118 this_delayed_pubkey = derive_pubkey(this_conf.delayed_basepoint.pubkey, pcp)
119 txs = []
120 # to_local
121 revocation_pubkey = ecc.ECPrivkey(other_revocation_privkey).get_public_key_bytes(compressed=True)
122 witness_script = bh2u(make_commitment_output_to_local_witness_script(
123 revocation_pubkey, to_self_delay, this_delayed_pubkey))
124 to_local_address = redeem_script_to_address('p2wsh', witness_script)
125 output_idxs = ctx.get_output_idxs_from_address(to_local_address)
126 if output_idxs:
127 output_idx = output_idxs.pop()
128 sweep_tx = lambda: create_sweeptx_ctx_to_local(
129 sweep_address=sweep_address,
130 ctx=ctx,
131 output_idx=output_idx,
132 witness_script=witness_script,
133 privkey=other_revocation_privkey,
134 is_revocation=True,
135 config=chan.lnworker.config)
136 return sweep_tx
137 return None
138
139
140 def create_sweeptx_for_their_revoked_htlc(
141 chan: 'Channel',
142 ctx: Transaction,
143 htlc_tx: Transaction,
144 sweep_address: str) -> Optional[SweepInfo]:
145
146 x = analyze_ctx(chan, ctx)
147 if not x:
148 return
149 ctn, their_pcp, is_revocation, per_commitment_secret = x
150 if not is_revocation:
151 return
152 # prep
153 pcp = ecc.ECPrivkey(per_commitment_secret).get_public_key_bytes(compressed=True)
154 this_conf, other_conf = get_ordered_channel_configs(chan=chan, for_us=False)
155 other_revocation_privkey = derive_blinded_privkey(
156 other_conf.revocation_basepoint.privkey,
157 per_commitment_secret)
158 to_self_delay = other_conf.to_self_delay
159 this_delayed_pubkey = derive_pubkey(this_conf.delayed_basepoint.pubkey, pcp)
160 # same witness script as to_local
161 revocation_pubkey = ecc.ECPrivkey(other_revocation_privkey).get_public_key_bytes(compressed=True)
162 witness_script = bh2u(make_commitment_output_to_local_witness_script(
163 revocation_pubkey, to_self_delay, this_delayed_pubkey))
164 htlc_address = redeem_script_to_address('p2wsh', witness_script)
165 # check that htlc_tx is a htlc
166 if htlc_tx.outputs()[0].address != htlc_address:
167 return
168 gen_tx = lambda: create_sweeptx_ctx_to_local(
169 sweep_address=sweep_address,
170 ctx=htlc_tx,
171 output_idx=0,
172 witness_script=witness_script,
173 privkey=other_revocation_privkey,
174 is_revocation=True,
175 config=chan.lnworker.config)
176 return SweepInfo(
177 name='redeem_htlc2',
178 csv_delay=0,
179 cltv_expiry=0,
180 gen_tx=gen_tx)
181
182
183 def create_sweeptxs_for_our_ctx(
184 *, chan: 'AbstractChannel',
185 ctx: Transaction,
186 sweep_address: str) -> Optional[Dict[str, SweepInfo]]:
187 """Handle the case where we force close unilaterally with our latest ctx.
188 Construct sweep txns for 'to_local', and for all HTLCs (2 txns each).
189 'to_local' can be swept even if this is a breach (by us),
190 but HTLCs cannot (old HTLCs are no longer stored).
191 """
192 ctn = extract_ctn_from_tx_and_chan(ctx, chan)
193 our_conf, their_conf = get_ordered_channel_configs(chan=chan, for_us=True)
194 our_per_commitment_secret = get_per_commitment_secret_from_seed(
195 our_conf.per_commitment_secret_seed, RevocationStore.START_INDEX - ctn)
196 our_pcp = ecc.ECPrivkey(our_per_commitment_secret).get_public_key_bytes(compressed=True)
197 our_delayed_bp_privkey = ecc.ECPrivkey(our_conf.delayed_basepoint.privkey)
198 our_localdelayed_privkey = derive_privkey(our_delayed_bp_privkey.secret_scalar, our_pcp)
199 our_localdelayed_privkey = ecc.ECPrivkey.from_secret_scalar(our_localdelayed_privkey)
200 their_revocation_pubkey = derive_blinded_pubkey(their_conf.revocation_basepoint.pubkey, our_pcp)
201 to_self_delay = their_conf.to_self_delay
202 our_htlc_privkey = derive_privkey(secret=int.from_bytes(our_conf.htlc_basepoint.privkey, 'big'),
203 per_commitment_point=our_pcp).to_bytes(32, 'big')
204 our_localdelayed_pubkey = our_localdelayed_privkey.get_public_key_bytes(compressed=True)
205 to_local_witness_script = bh2u(make_commitment_output_to_local_witness_script(
206 their_revocation_pubkey, to_self_delay, our_localdelayed_pubkey))
207 to_local_address = redeem_script_to_address('p2wsh', to_local_witness_script)
208 # to remote address
209 bpk = their_conf.payment_basepoint.pubkey
210 their_payment_pubkey = bpk if chan.is_static_remotekey_enabled() else derive_pubkey(their_conf.payment_basepoint.pubkey, our_pcp)
211 to_remote_address = make_commitment_output_to_remote_address(their_payment_pubkey)
212 # test ctx
213 _logger.debug(f'testing our ctx: {to_local_address} {to_remote_address}')
214 if not ctx.get_output_idxs_from_address(to_local_address) \
215 and not ctx.get_output_idxs_from_address(to_remote_address):
216 return
217 # we have to_local, to_remote.
218 # other outputs are htlcs
219 # if they are spent, we need to generate the script
220 # so, second-stage htlc sweep should not be returned here
221 txs = {} # type: Dict[str, SweepInfo]
222 # to_local
223 output_idxs = ctx.get_output_idxs_from_address(to_local_address)
224 if output_idxs:
225 output_idx = output_idxs.pop()
226 sweep_tx = lambda: create_sweeptx_ctx_to_local(
227 sweep_address=sweep_address,
228 ctx=ctx,
229 output_idx=output_idx,
230 witness_script=to_local_witness_script,
231 privkey=our_localdelayed_privkey.get_secret_bytes(),
232 is_revocation=False,
233 to_self_delay=to_self_delay,
234 config=chan.lnworker.config)
235 prevout = ctx.txid() + ':%d'%output_idx
236 txs[prevout] = SweepInfo(
237 name='our_ctx_to_local',
238 csv_delay=to_self_delay,
239 cltv_expiry=0,
240 gen_tx=sweep_tx)
241 we_breached = ctn < chan.get_oldest_unrevoked_ctn(LOCAL)
242 if we_breached:
243 _logger.info("we breached.")
244 # return only our_ctx_to_local, because we don't keep htlc_signatures for old states
245 return txs
246
247 # HTLCs
248 def create_txns_for_htlc(
249 *, htlc: 'UpdateAddHtlc', htlc_direction: Direction,
250 ctx_output_idx: int, htlc_relative_idx: int):
251 if htlc_direction == RECEIVED:
252 preimage = chan.lnworker.get_preimage(htlc.payment_hash)
253 else:
254 preimage = None
255 htlctx_witness_script, htlc_tx = create_htlctx_that_spends_from_our_ctx(
256 chan=chan,
257 our_pcp=our_pcp,
258 ctx=ctx,
259 htlc=htlc,
260 local_htlc_privkey=our_htlc_privkey,
261 preimage=preimage,
262 htlc_direction=htlc_direction,
263 ctx_output_idx=ctx_output_idx,
264 htlc_relative_idx=htlc_relative_idx)
265 sweep_tx = lambda: create_sweeptx_that_spends_htlctx_that_spends_htlc_in_ctx(
266 to_self_delay=to_self_delay,
267 htlc_tx=htlc_tx,
268 htlctx_witness_script=htlctx_witness_script,
269 sweep_address=sweep_address,
270 privkey=our_localdelayed_privkey.get_secret_bytes(),
271 is_revocation=False,
272 config=chan.lnworker.config)
273 # side effect
274 txs[htlc_tx.inputs()[0].prevout.to_str()] = SweepInfo(
275 name='first-stage-htlc',
276 csv_delay=0,
277 cltv_expiry=htlc_tx.locktime,
278 gen_tx=lambda: htlc_tx)
279 txs[htlc_tx.txid() + ':0'] = SweepInfo(
280 name='second-stage-htlc',
281 csv_delay=to_self_delay,
282 cltv_expiry=0,
283 gen_tx=sweep_tx)
284
285 # offered HTLCs, in our ctx --> "timeout"
286 # received HTLCs, in our ctx --> "success"
287 htlc_to_ctx_output_idx_map = map_htlcs_to_ctx_output_idxs(
288 chan=chan,
289 ctx=ctx,
290 pcp=our_pcp,
291 subject=LOCAL,
292 ctn=ctn)
293 for (direction, htlc), (ctx_output_idx, htlc_relative_idx) in htlc_to_ctx_output_idx_map.items():
294 create_txns_for_htlc(
295 htlc=htlc,
296 htlc_direction=direction,
297 ctx_output_idx=ctx_output_idx,
298 htlc_relative_idx=htlc_relative_idx)
299 return txs
300
301
302 def analyze_ctx(chan: 'Channel', ctx: Transaction):
303 # note: the remote sometimes has two valid non-revoked commitment transactions,
304 # either of which could be broadcast
305 our_conf, their_conf = get_ordered_channel_configs(chan=chan, for_us=True)
306 ctn = extract_ctn_from_tx_and_chan(ctx, chan)
307 per_commitment_secret = None
308 oldest_unrevoked_remote_ctn = chan.get_oldest_unrevoked_ctn(REMOTE)
309 if ctn == oldest_unrevoked_remote_ctn:
310 their_pcp = their_conf.current_per_commitment_point
311 is_revocation = False
312 elif ctn == oldest_unrevoked_remote_ctn + 1:
313 their_pcp = their_conf.next_per_commitment_point
314 is_revocation = False
315 elif ctn < oldest_unrevoked_remote_ctn: # breach
316 try:
317 per_commitment_secret = chan.revocation_store.retrieve_secret(RevocationStore.START_INDEX - ctn)
318 except UnableToDeriveSecret:
319 return
320 their_pcp = ecc.ECPrivkey(per_commitment_secret).get_public_key_bytes(compressed=True)
321 is_revocation = True
322 #_logger.info(f'tx for revoked: {list(txs.keys())}')
323 elif chan.get_data_loss_protect_remote_pcp(ctn):
324 their_pcp = chan.get_data_loss_protect_remote_pcp(ctn)
325 is_revocation = False
326 else:
327 return
328 return ctn, their_pcp, is_revocation, per_commitment_secret
329
330
331 def create_sweeptxs_for_their_ctx(
332 *, chan: 'Channel',
333 ctx: Transaction,
334 sweep_address: str) -> Optional[Dict[str,SweepInfo]]:
335 """Handle the case when the remote force-closes with their ctx.
336 Sweep outputs that do not have a CSV delay ('to_remote' and first-stage HTLCs).
337 Outputs with CSV delay ('to_local' and second-stage HTLCs) are redeemed by LNWatcher.
338 """
339 txs = {} # type: Dict[str, SweepInfo]
340 our_conf, their_conf = get_ordered_channel_configs(chan=chan, for_us=True)
341 x = analyze_ctx(chan, ctx)
342 if not x:
343 return
344 ctn, their_pcp, is_revocation, per_commitment_secret = x
345 # to_local and to_remote addresses
346 our_revocation_pubkey = derive_blinded_pubkey(our_conf.revocation_basepoint.pubkey, their_pcp)
347 their_delayed_pubkey = derive_pubkey(their_conf.delayed_basepoint.pubkey, their_pcp)
348 witness_script = bh2u(make_commitment_output_to_local_witness_script(
349 our_revocation_pubkey, our_conf.to_self_delay, their_delayed_pubkey))
350 to_local_address = redeem_script_to_address('p2wsh', witness_script)
351 # to remote address
352 bpk = our_conf.payment_basepoint.pubkey
353 our_payment_pubkey = bpk if chan.is_static_remotekey_enabled() else derive_pubkey(bpk, their_pcp)
354 to_remote_address = make_commitment_output_to_remote_address(our_payment_pubkey)
355 # test if this is their ctx
356 _logger.debug(f'testing their ctx: {to_local_address} {to_remote_address}')
357 if not ctx.get_output_idxs_from_address(to_local_address) \
358 and not ctx.get_output_idxs_from_address(to_remote_address):
359 return
360 if is_revocation:
361 our_revocation_privkey = derive_blinded_privkey(our_conf.revocation_basepoint.privkey, per_commitment_secret)
362 gen_tx = create_sweeptx_for_their_revoked_ctx(chan, ctx, per_commitment_secret, chan.sweep_address)
363 if gen_tx:
364 tx = gen_tx()
365 txs[tx.inputs()[0].prevout.to_str()] = SweepInfo(
366 name='to_local_for_revoked_ctx',
367 csv_delay=0,
368 cltv_expiry=0,
369 gen_tx=gen_tx)
370 # prep
371 our_htlc_privkey = derive_privkey(secret=int.from_bytes(our_conf.htlc_basepoint.privkey, 'big'), per_commitment_point=their_pcp)
372 our_htlc_privkey = ecc.ECPrivkey.from_secret_scalar(our_htlc_privkey)
373 their_htlc_pubkey = derive_pubkey(their_conf.htlc_basepoint.pubkey, their_pcp)
374 # to_local is handled by lnwatcher
375 # to_remote
376 if not chan.is_static_remotekey_enabled():
377 our_payment_bp_privkey = ecc.ECPrivkey(our_conf.payment_basepoint.privkey)
378 our_payment_privkey = derive_privkey(our_payment_bp_privkey.secret_scalar, their_pcp)
379 our_payment_privkey = ecc.ECPrivkey.from_secret_scalar(our_payment_privkey)
380 assert our_payment_pubkey == our_payment_privkey.get_public_key_bytes(compressed=True)
381 output_idxs = ctx.get_output_idxs_from_address(to_remote_address)
382 if output_idxs:
383 output_idx = output_idxs.pop()
384 prevout = ctx.txid() + ':%d'%output_idx
385 sweep_tx = lambda: create_sweeptx_their_ctx_to_remote(
386 sweep_address=sweep_address,
387 ctx=ctx,
388 output_idx=output_idx,
389 our_payment_privkey=our_payment_privkey,
390 config=chan.lnworker.config)
391 txs[prevout] = SweepInfo(
392 name='their_ctx_to_remote',
393 csv_delay=0,
394 cltv_expiry=0,
395 gen_tx=sweep_tx)
396 # HTLCs
397 def create_sweeptx_for_htlc(
398 htlc: 'UpdateAddHtlc', is_received_htlc: bool,
399 ctx_output_idx: int) -> None:
400 if not is_received_htlc and not is_revocation:
401 preimage = chan.lnworker.get_preimage(htlc.payment_hash)
402 else:
403 preimage = None
404 htlc_output_witness_script = make_htlc_output_witness_script(
405 is_received_htlc=is_received_htlc,
406 remote_revocation_pubkey=our_revocation_pubkey,
407 remote_htlc_pubkey=our_htlc_privkey.get_public_key_bytes(compressed=True),
408 local_htlc_pubkey=their_htlc_pubkey,
409 payment_hash=htlc.payment_hash,
410 cltv_expiry=htlc.cltv_expiry)
411
412 cltv_expiry = htlc.cltv_expiry if is_received_htlc and not is_revocation else 0
413 prevout = ctx.txid() + ':%d'%ctx_output_idx
414 sweep_tx = lambda: create_sweeptx_their_ctx_htlc(
415 ctx=ctx,
416 witness_script=htlc_output_witness_script,
417 sweep_address=sweep_address,
418 preimage=preimage,
419 output_idx=ctx_output_idx,
420 privkey=our_revocation_privkey if is_revocation else our_htlc_privkey.get_secret_bytes(),
421 is_revocation=is_revocation,
422 cltv_expiry=cltv_expiry,
423 config=chan.lnworker.config)
424 txs[prevout] = SweepInfo(
425 name=f'their_ctx_htlc_{ctx_output_idx}',
426 csv_delay=0,
427 cltv_expiry=cltv_expiry,
428 gen_tx=sweep_tx)
429 # received HTLCs, in their ctx --> "timeout"
430 # offered HTLCs, in their ctx --> "success"
431 htlc_to_ctx_output_idx_map = map_htlcs_to_ctx_output_idxs(
432 chan=chan,
433 ctx=ctx,
434 pcp=their_pcp,
435 subject=REMOTE,
436 ctn=ctn)
437 for (direction, htlc), (ctx_output_idx, htlc_relative_idx) in htlc_to_ctx_output_idx_map.items():
438 create_sweeptx_for_htlc(
439 htlc=htlc,
440 is_received_htlc=direction == RECEIVED,
441 ctx_output_idx=ctx_output_idx)
442 return txs
443
444
445 def create_htlctx_that_spends_from_our_ctx(
446 chan: 'Channel', our_pcp: bytes,
447 ctx: Transaction, htlc: 'UpdateAddHtlc',
448 local_htlc_privkey: bytes, preimage: Optional[bytes],
449 htlc_direction: Direction, htlc_relative_idx: int,
450 ctx_output_idx: int) -> Tuple[bytes, Transaction]:
451 assert (htlc_direction == RECEIVED) == bool(preimage), 'preimage is required iff htlc is received'
452 preimage = preimage or b''
453 ctn = extract_ctn_from_tx_and_chan(ctx, chan)
454 witness_script, htlc_tx = make_htlc_tx_with_open_channel(
455 chan=chan,
456 pcp=our_pcp,
457 subject=LOCAL,
458 ctn=ctn,
459 htlc_direction=htlc_direction,
460 commit=ctx,
461 htlc=htlc,
462 ctx_output_idx=ctx_output_idx,
463 name=f'our_ctx_{ctx_output_idx}_htlc_tx_{bh2u(htlc.payment_hash)}')
464 remote_htlc_sig = chan.get_remote_htlc_sig_for_htlc(htlc_relative_idx=htlc_relative_idx)
465 local_htlc_sig = bfh(htlc_tx.sign_txin(0, local_htlc_privkey))
466 txin = htlc_tx.inputs()[0]
467 witness_program = bfh(Transaction.get_preimage_script(txin))
468 txin.witness = make_htlc_tx_witness(remote_htlc_sig, local_htlc_sig, preimage, witness_program)
469 return witness_script, htlc_tx
470
471
472 def create_sweeptx_their_ctx_htlc(
473 ctx: Transaction, witness_script: bytes, sweep_address: str,
474 preimage: Optional[bytes], output_idx: int,
475 privkey: bytes, is_revocation: bool,
476 cltv_expiry: int, config: SimpleConfig) -> Optional[PartialTransaction]:
477 assert type(cltv_expiry) is int
478 preimage = preimage or b'' # preimage is required iff (not is_revocation and htlc is offered)
479 val = ctx.outputs()[output_idx].value
480 prevout = TxOutpoint(txid=bfh(ctx.txid()), out_idx=output_idx)
481 txin = PartialTxInput(prevout=prevout)
482 txin._trusted_value_sats = val
483 txin.witness_script = witness_script
484 txin.script_sig = b''
485 sweep_inputs = [txin]
486 tx_size_bytes = 200 # TODO (depends on offered/received and is_revocation)
487 fee = config.estimate_fee(tx_size_bytes, allow_fallback_to_static_rates=True)
488 outvalue = val - fee
489 if outvalue <= dust_threshold(): return None
490 sweep_outputs = [PartialTxOutput.from_address_and_value(sweep_address, outvalue)]
491 tx = PartialTransaction.from_io(sweep_inputs, sweep_outputs, version=2, locktime=cltv_expiry)
492 sig = bfh(tx.sign_txin(0, privkey))
493 if not is_revocation:
494 witness = construct_witness([sig, preimage, witness_script])
495 else:
496 revocation_pubkey = privkey_to_pubkey(privkey)
497 witness = construct_witness([sig, revocation_pubkey, witness_script])
498 tx.inputs()[0].witness = bfh(witness)
499 assert tx.is_complete()
500 return tx
501
502
503 def create_sweeptx_their_ctx_to_remote(
504 sweep_address: str, ctx: Transaction, output_idx: int,
505 our_payment_privkey: ecc.ECPrivkey,
506 config: SimpleConfig) -> Optional[PartialTransaction]:
507 our_payment_pubkey = our_payment_privkey.get_public_key_hex(compressed=True)
508 val = ctx.outputs()[output_idx].value
509 prevout = TxOutpoint(txid=bfh(ctx.txid()), out_idx=output_idx)
510 txin = PartialTxInput(prevout=prevout)
511 txin._trusted_value_sats = val
512 txin.script_type = 'p2wpkh'
513 txin.pubkeys = [bfh(our_payment_pubkey)]
514 txin.num_sig = 1
515 sweep_inputs = [txin]
516 tx_size_bytes = 110 # approx size of p2wpkh->p2wpkh
517 fee = config.estimate_fee(tx_size_bytes, allow_fallback_to_static_rates=True)
518 outvalue = val - fee
519 if outvalue <= dust_threshold(): return None
520 sweep_outputs = [PartialTxOutput.from_address_and_value(sweep_address, outvalue)]
521 sweep_tx = PartialTransaction.from_io(sweep_inputs, sweep_outputs)
522 sweep_tx.set_rbf(True)
523 sweep_tx.sign({our_payment_pubkey: (our_payment_privkey.get_secret_bytes(), True)})
524 if not sweep_tx.is_complete():
525 raise Exception('channel close sweep tx is not complete')
526 return sweep_tx
527
528
529 def create_sweeptx_ctx_to_local(
530 *, sweep_address: str, ctx: Transaction, output_idx: int, witness_script: str,
531 privkey: bytes, is_revocation: bool, config: SimpleConfig,
532 to_self_delay: int=None) -> Optional[PartialTransaction]:
533 """Create a txn that sweeps the 'to_local' output of a commitment
534 transaction into our wallet.
535
536 privkey: either revocation_privkey or localdelayed_privkey
537 is_revocation: tells us which ^
538 """
539 val = ctx.outputs()[output_idx].value
540 prevout = TxOutpoint(txid=bfh(ctx.txid()), out_idx=output_idx)
541 txin = PartialTxInput(prevout=prevout)
542 txin._trusted_value_sats = val
543 txin.script_sig = b''
544 txin.witness_script = bfh(witness_script)
545 sweep_inputs = [txin]
546 if not is_revocation:
547 assert isinstance(to_self_delay, int)
548 sweep_inputs[0].nsequence = to_self_delay
549 tx_size_bytes = 121 # approx size of to_local -> p2wpkh
550 fee = config.estimate_fee(tx_size_bytes, allow_fallback_to_static_rates=True)
551 outvalue = val - fee
552 if outvalue <= dust_threshold():
553 return None
554 sweep_outputs = [PartialTxOutput.from_address_and_value(sweep_address, outvalue)]
555 sweep_tx = PartialTransaction.from_io(sweep_inputs, sweep_outputs, version=2)
556 sig = sweep_tx.sign_txin(0, privkey)
557 witness = construct_witness([sig, int(is_revocation), witness_script])
558 sweep_tx.inputs()[0].witness = bfh(witness)
559 return sweep_tx
560
561
562 def create_sweeptx_that_spends_htlctx_that_spends_htlc_in_ctx(
563 *, htlc_tx: Transaction, htlctx_witness_script: bytes, sweep_address: str,
564 privkey: bytes, is_revocation: bool, to_self_delay: int,
565 config: SimpleConfig) -> Optional[PartialTransaction]:
566 val = htlc_tx.outputs()[0].value
567 prevout = TxOutpoint(txid=bfh(htlc_tx.txid()), out_idx=0)
568 txin = PartialTxInput(prevout=prevout)
569 txin._trusted_value_sats = val
570 txin.script_sig = b''
571 txin.witness_script = htlctx_witness_script
572 sweep_inputs = [txin]
573 if not is_revocation:
574 assert isinstance(to_self_delay, int)
575 sweep_inputs[0].nsequence = to_self_delay
576 tx_size_bytes = 200 # TODO
577 fee = config.estimate_fee(tx_size_bytes, allow_fallback_to_static_rates=True)
578 outvalue = val - fee
579 if outvalue <= dust_threshold(): return None
580 sweep_outputs = [PartialTxOutput.from_address_and_value(sweep_address, outvalue)]
581 tx = PartialTransaction.from_io(sweep_inputs, sweep_outputs, version=2)
582 sig = bfh(tx.sign_txin(0, privkey))
583 witness = construct_witness([sig, int(is_revocation), htlctx_witness_script])
584 tx.inputs()[0].witness = bfh(witness)
585 assert tx.is_complete()
586 return tx