• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

spesmilo / electrum / 5735552722403328

16 May 2025 10:28AM UTC coverage: 59.722% (+0.002%) from 59.72%
5735552722403328

Pull #9833

CirrusCI

f321x
make lightning dns seed fetching async
Pull Request #9833: dns: use async dnspython interface

22 of 50 new or added lines in 7 files covered. (44.0%)

1107 existing lines in 11 files now uncovered.

21549 of 36082 relevant lines covered (59.72%)

2.39 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

65.57
/electrum/lnchannel.py
1
# Copyright (C) 2018 The Electrum developers
2
#
3
# Permission is hereby granted, free of charge, to any person obtaining a copy
4
# of this software and associated documentation files (the "Software"), to deal
5
# in the Software without restriction, including without limitation the rights
6
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
# copies of the Software, and to permit persons to whom the Software is
8
# furnished to do so, subject to the following conditions:
9
#
10
# The above copyright notice and this permission notice shall be included in
11
# all copies or substantial portions of the Software.
12
#
13
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
# THE SOFTWARE.
20
import enum
4✔
21
import os
4✔
22
from collections import namedtuple, defaultdict
4✔
23
import binascii
4✔
24
import json
4✔
25
from enum import IntEnum, Enum
4✔
26
from typing import (Optional, Dict, List, Tuple, NamedTuple, Set, Callable,
4✔
27
                    Iterable, Sequence, TYPE_CHECKING, Iterator, Union, Mapping)
28
import time
4✔
29
import threading
4✔
30
from abc import ABC, abstractmethod
4✔
31
import itertools
4✔
32

33
from aiorpcx import NetAddress
4✔
34
import attr
4✔
35

36
import electrum_ecc as ecc
4✔
37
from electrum_ecc import ECPubkey
4✔
38

39
from . import constants, util
4✔
40
from .util import bfh, chunks, TxMinedInfo
4✔
41
from .invoices import PR_PAID
4✔
42
from .bitcoin import redeem_script_to_address
4✔
43
from .crypto import sha256, sha256d
4✔
44
from .transaction import Transaction, PartialTransaction, TxInput, Sighash
4✔
45
from .logging import Logger
4✔
46
from .lntransport import LNPeerAddr
4✔
47
from .lnonion import OnionRoutingFailure
4✔
48
from . import lnutil
4✔
49
from .lnutil import (Outpoint, LocalConfig, RemoteConfig, Keypair, OnlyPubkeyKeypair, ChannelConstraints,
4✔
50
                     get_per_commitment_secret_from_seed, secret_to_pubkey, derive_privkey, make_closing_tx,
51
                     sign_and_get_sig_string, RevocationStore, derive_blinded_pubkey, Direction, derive_pubkey,
52
                     make_htlc_tx_with_open_channel, make_commitment, UpdateAddHtlc,
53
                     funding_output_script, SENT, RECEIVED, LOCAL, REMOTE, HTLCOwner, make_commitment_outputs,
54
                     ScriptHtlc, PaymentFailure, calc_fees_for_commitment_tx, RemoteMisbehaving, make_htlc_output_witness_script,
55
                     ShortChannelID, map_htlcs_to_ctx_output_idxs,
56
                     fee_for_htlc_output, offered_htlc_trim_threshold_sat,
57
                     received_htlc_trim_threshold_sat, make_commitment_output_to_remote_address, FIXED_ANCHOR_SAT,
58
                     ChannelType, LNProtocolWarning, ZEROCONF_TIMEOUT)
59
from .lnsweep import sweep_our_ctx, sweep_their_ctx
4✔
60
from .lnsweep import sweep_their_htlctx_justice, sweep_our_htlctx, SweepInfo
4✔
61
from .lnsweep import sweep_their_ctx_to_remote_backup
4✔
62
from .lnhtlc import HTLCManager
4✔
63
from .lnmsg import encode_msg, decode_msg
4✔
64
from .address_synchronizer import TX_HEIGHT_LOCAL
4✔
65
from .lnutil import CHANNEL_OPENING_TIMEOUT
4✔
66
from .lnutil import ChannelBackupStorage, ImportedChannelBackupStorage, OnchainChannelBackupStorage
4✔
67
from .lnutil import format_short_channel_id
4✔
68
from .fee_policy import FEERATE_PER_KW_MIN_RELAY_LIGHTNING
4✔
69

70
if TYPE_CHECKING:
4✔
71
    from .lnworker import LNWallet
×
72
    from .json_db import StoredDict
×
73
    from .lnrouter import RouteEdge
×
74

75
# channel flags
76
CF_ANNOUNCE_CHANNEL = 0x01
4✔
77

78
# lightning channel states
79
# Note: these states are persisted by name (for a given channel) in the wallet file,
80
#       so consider doing a wallet db upgrade when changing them.
81
class ChannelState(IntEnum):
4✔
82
    PREOPENING      = 0  # Initial negotiation. Channel will not be reestablished
4✔
83
    OPENING         = 1  # Channel will be reestablished. (per BOLT2)
4✔
84
                         #  - Funding node: has received funding_signed (can broadcast the funding tx)
85
                         #  - Non-funding node: has sent the funding_signed message.
86
    FUNDED          = 2  # Funding tx was mined (requires min_depth and tx verification)
4✔
87
    OPEN            = 3  # both parties have sent funding_locked
4✔
88
    SHUTDOWN        = 4  # shutdown has been sent.
4✔
89
    CLOSING         = 5  # closing negotiation done. we have a fully signed tx.
4✔
90
    FORCE_CLOSING   = 6  # *we* force-closed, and closing tx is unconfirmed. Note that if the
4✔
91
                         # remote force-closes then we remain OPEN until it gets mined -
92
                         # the server could be lying to us with a fake tx.
93
    REQUESTED_FCLOSE = 7   # Chan is open, but we have tried to request the *remote* to force-close
4✔
94
    WE_ARE_TOXIC     = 8   # Chan is open, but we have lost state and the remote proved this.
4✔
95
                           # The remote must force-close, it is *not* safe for us to do so.
96
    CLOSED           = 9   # closing tx has been mined
4✔
97
    REDEEMED         = 10  # we can stop watching
4✔
98

99

100
class PeerState(IntEnum):
4✔
101
    DISCONNECTED   = 0
4✔
102
    REESTABLISHING = 1
4✔
103
    GOOD           = 2
4✔
104
    BAD            = 3
4✔
105

106

107
cs = ChannelState
4✔
108
state_transitions = [
4✔
109
    (cs.PREOPENING, cs.OPENING),
110
    (cs.OPENING, cs.FUNDED),
111
    (cs.FUNDED, cs.OPEN),
112
    (cs.OPENING, cs.SHUTDOWN),
113
    (cs.FUNDED, cs.SHUTDOWN),
114
    (cs.OPEN, cs.SHUTDOWN),
115
    (cs.SHUTDOWN, cs.SHUTDOWN),  # if we reestablish
116
    (cs.SHUTDOWN, cs.CLOSING),
117
    (cs.CLOSING, cs.CLOSING),
118
    # we can force close almost any time
119
    (cs.OPENING,  cs.FORCE_CLOSING),
120
    (cs.FUNDED,   cs.FORCE_CLOSING),
121
    (cs.OPEN,     cs.FORCE_CLOSING),
122
    (cs.SHUTDOWN, cs.FORCE_CLOSING),
123
    (cs.CLOSING,  cs.FORCE_CLOSING),
124
    (cs.REQUESTED_FCLOSE, cs.FORCE_CLOSING),
125
    # we can request a force-close almost any time
126
    (cs.OPENING,  cs.REQUESTED_FCLOSE),
127
    (cs.FUNDED,   cs.REQUESTED_FCLOSE),
128
    (cs.OPEN,     cs.REQUESTED_FCLOSE),
129
    (cs.SHUTDOWN, cs.REQUESTED_FCLOSE),
130
    (cs.CLOSING,  cs.REQUESTED_FCLOSE),
131
    (cs.REQUESTED_FCLOSE,  cs.REQUESTED_FCLOSE),
132
    # we can get force closed almost any time
133
    (cs.OPENING,  cs.CLOSED),
134
    (cs.FUNDED,   cs.CLOSED),
135
    (cs.OPEN,     cs.CLOSED),
136
    (cs.SHUTDOWN, cs.CLOSED),
137
    (cs.CLOSING,  cs.CLOSED),
138
    (cs.REQUESTED_FCLOSE, cs.CLOSED),
139
    (cs.WE_ARE_TOXIC,          cs.CLOSED),
140
    # during channel_reestablish, we might realise we have lost state
141
    (cs.OPENING,  cs.WE_ARE_TOXIC),
142
    (cs.FUNDED,   cs.WE_ARE_TOXIC),
143
    (cs.OPEN,     cs.WE_ARE_TOXIC),
144
    (cs.SHUTDOWN, cs.WE_ARE_TOXIC),
145
    (cs.REQUESTED_FCLOSE, cs.WE_ARE_TOXIC),
146
    (cs.WE_ARE_TOXIC, cs.WE_ARE_TOXIC),
147
    #
148
    (cs.FORCE_CLOSING, cs.FORCE_CLOSING),  # allow multiple attempts
149
    (cs.FORCE_CLOSING, cs.CLOSED),
150
    (cs.FORCE_CLOSING, cs.REDEEMED),
151
    (cs.CLOSED, cs.REDEEMED),
152
    (cs.OPENING, cs.REDEEMED),  # channel never funded (dropped from mempool)
153
    (cs.PREOPENING, cs.REDEEMED),  # channel never funded
154
]
155
del cs  # delete as name is ambiguous without context
4✔
156

157

158
class ChanCloseOption(Enum):
4✔
159
    COOP_CLOSE = enum.auto()
4✔
160
    LOCAL_FCLOSE = enum.auto()
4✔
161
    REQUEST_REMOTE_FCLOSE = enum.auto()
4✔
162

163

164
class RevokeAndAck(NamedTuple):
4✔
165
    per_commitment_secret: bytes
4✔
166
    next_per_commitment_point: bytes
4✔
167

168

169
class RemoteCtnTooFarInFuture(Exception): pass
4✔
170

171

172
def htlcsum(htlcs: Iterable[UpdateAddHtlc]):
4✔
173
    return sum([x.amount_msat for x in htlcs])
4✔
174

175
def now():
4✔
176
    return int(time.time())
4✔
177

178
class HTLCWithStatus(NamedTuple):
4✔
179
    channel_id: bytes
4✔
180
    htlc: UpdateAddHtlc
4✔
181
    direction: Direction
4✔
182
    status: str
4✔
183

184

185
class AbstractChannel(Logger, ABC):
4✔
186
    storage: Union['StoredDict', dict]
4✔
187
    config: Dict[HTLCOwner, Union[LocalConfig, RemoteConfig]]
4✔
188
    _sweep_info: Dict[str, Dict[str, 'SweepInfo']]
4✔
189
    lnworker: Optional['LNWallet']
4✔
190
    channel_id: bytes
4✔
191
    short_channel_id: Optional[ShortChannelID] = None
4✔
192
    funding_outpoint: Outpoint
4✔
193
    node_id: bytes  # note that it might not be the full 33 bytes; for OCB it is only the prefix
4✔
194
    should_request_force_close: bool = False
4✔
195
    _state: ChannelState
4✔
196

197
    def set_short_channel_id(self, short_id: ShortChannelID) -> None:
4✔
198
        self.short_channel_id = short_id
×
199
        self.storage["short_channel_id"] = short_id
×
200

201
    def get_id_for_log(self) -> str:
4✔
202
        scid = self.short_channel_id
4✔
203
        if scid:
4✔
204
            return str(scid)
4✔
205
        return self.channel_id.hex()
×
206

207
    def short_id_for_GUI(self) -> str:
4✔
208
        return format_short_channel_id(self.short_channel_id)
×
209

210
    def diagnostic_name(self):
4✔
211
        return self.get_id_for_log()
4✔
212

213
    def set_state(self, state: ChannelState, *, force: bool = False) -> None:
4✔
214
        """Set on-chain state.
215
        `force` can be set while debugging from the console to allow illegal transitions.
216
        """
217
        old_state = self._state
4✔
218
        if not force and (old_state, state) not in state_transitions:
4✔
219
            raise Exception(f"Transition not allowed: {old_state.name} -> {state.name}")
×
220
        self.logger.debug(f'Setting channel state: {old_state.name} -> {state.name}')
4✔
221
        self._state = state
4✔
222
        self.storage['state'] = self._state.name
4✔
223
        if self.lnworker:
4✔
224
            self.lnworker.channel_state_changed(self)
4✔
225

226
    def get_state(self) -> ChannelState:
4✔
227
        return self._state
4✔
228

229
    def is_funded(self) -> bool:
4✔
230
        return self.get_state() >= ChannelState.FUNDED
4✔
231

232
    def is_open(self) -> bool:
4✔
233
        return self.get_state() == ChannelState.OPEN
4✔
234

235
    def is_closed(self) -> bool:
4✔
236
        # the closing txid has been saved
237
        return self.get_state() >= ChannelState.CLOSING
4✔
238

239
    def is_closed_or_closing(self):
4✔
240
        # related: self.get_state_for_GUI
241
        return self.is_closed() or self.unconfirmed_closing_txid is not None
×
242

243
    def is_redeemed(self) -> bool:
4✔
244
        return self.get_state() == ChannelState.REDEEMED
4✔
245

246
    def need_to_subscribe(self) -> bool:
4✔
247
        """Whether lnwatcher/synchronizer need to be watching this channel."""
248
        if not self.is_redeemed():
4✔
249
            return True
4✔
250
        # Chan already deeply closed. Still, if some txs are missing, we should sub.
251
        # check we have funding tx
252
        # note: tx might not be directly related to the wallet, e.g. chan opened by remote
253
        if (funding_item := self.get_funding_height()) is None:
4✔
254
            return True
×
255
        if self.lnworker:
4✔
256
            funding_txid, funding_height, funding_timestamp = funding_item
4✔
257
            if self.lnworker.wallet.adb.get_transaction(funding_txid) is None:
4✔
258
                return True
×
259
        # check we have closing tx
260
        # note: tx might not be directly related to the wallet, e.g. local-fclose
261
        if (closing_item := self.get_closing_height()) is None:
4✔
262
            return True
×
263
        if self.lnworker:
4✔
264
            closing_txid, closing_height, closing_timestamp = closing_item
4✔
265
            if self.lnworker.wallet.adb.get_transaction(closing_txid) is None:
4✔
266
                return True
×
267
        return False
4✔
268

269
    @abstractmethod
4✔
270
    def get_close_options(self) -> Sequence[ChanCloseOption]:
4✔
271
        pass
×
272

273
    def save_funding_height(self, *, txid: str, height: int, timestamp: Optional[int]) -> None:
4✔
274
        self.storage['funding_height'] = txid, height, timestamp
×
275

276
    def get_funding_height(self) -> Optional[Tuple[str, int, Optional[int]]]:
4✔
277
        return self.storage.get('funding_height')
4✔
278

279
    def delete_funding_height(self):
4✔
280
        self.storage.pop('funding_height', None)
×
281

282
    def save_closing_height(self, *, txid: str, height: int, timestamp: Optional[int]) -> None:
4✔
283
        self.storage['closing_height'] = txid, height, timestamp
×
284

285
    def get_closing_height(self) -> Optional[Tuple[str, int, Optional[int]]]:
4✔
286
        return self.storage.get('closing_height')
4✔
287

288
    def delete_closing_height(self):
4✔
289
        self.storage.pop('closing_height', None)
×
290

291
    def create_sweeptxs_for_our_ctx(self, ctx: Transaction) -> Dict[str, SweepInfo]:
4✔
292
        return sweep_our_ctx(chan=self, ctx=ctx)
×
293

294
    def create_sweeptxs_for_their_ctx(self, ctx: Transaction) -> Dict[str, SweepInfo]:
4✔
295
        return sweep_their_ctx(chan=self, ctx=ctx)
×
296

297
    def is_backup(self) -> bool:
4✔
298
        return False
×
299

300
    def get_local_scid_alias(self, *, create_new_if_needed: bool = False) -> Optional[bytes]:
4✔
301
        return None
×
302

303
    def get_remote_scid_alias(self) -> Optional[bytes]:
4✔
304
        return None
×
305

306
    def get_ctx_sweep_info(self, ctx: Transaction) -> Tuple[bool, Dict[str, SweepInfo]]:
4✔
307
        txid = ctx.txid()
×
308
        is_local = False
×
309
        if self._sweep_info.get(txid) is None:
×
310
            our_sweep_info = self.create_sweeptxs_for_our_ctx(ctx)
×
311
            their_sweep_info = self.create_sweeptxs_for_their_ctx(ctx)
×
312
            if our_sweep_info:
×
313
                is_local = True
×
314
                self._sweep_info[txid] = our_sweep_info
×
UNCOV
315
                self.logger.info(f'we (local) force closed')
×
316
            elif their_sweep_info:
×
317
                self._sweep_info[txid] = their_sweep_info
×
318
                self.logger.info(f'they (remote) force closed.')
×
319
            else:
320
                self._sweep_info[txid] = {}
×
321
                self.logger.info(f'not sure who closed.')
×
322
        return is_local, self._sweep_info[txid]
×
323

324
    def maybe_sweep_htlcs(self, ctx: Transaction, htlc_tx: Transaction) -> Dict[str, SweepInfo]:
4✔
325
        return {}
×
326

327
    def extract_preimage_from_htlc_txin(self, txin: TxInput, *, is_deeply_mined: bool) -> None:
4✔
UNCOV
328
        return
×
329

330
    def update_onchain_state(self, *, funding_txid: str, funding_height: TxMinedInfo,
4✔
331
                             closing_txid: str, closing_height: TxMinedInfo, keep_watching: bool) -> None:
332
        # note: state transitions are irreversible, but
333
        # save_funding_height, save_closing_height are reversible
UNCOV
334
        if funding_height.height == TX_HEIGHT_LOCAL:
×
UNCOV
335
            self.update_unfunded_state()
×
UNCOV
336
        elif closing_height.height == TX_HEIGHT_LOCAL:
×
UNCOV
337
            self.update_funded_state(
×
338
                funding_txid=funding_txid,
339
                funding_height=funding_height)
340
        else:
341
            self.update_closed_state(
×
342
                funding_txid=funding_txid,
343
                funding_height=funding_height,
344
                closing_txid=closing_txid,
345
                closing_height=closing_height,
346
                keep_watching=keep_watching)
347

348
    def update_unfunded_state(self) -> None:
4✔
UNCOV
349
        self.delete_funding_height()
×
UNCOV
350
        self.delete_closing_height()
×
UNCOV
351
        if not self.lnworker:
×
UNCOV
352
            return
×
UNCOV
353
        chan_age = now() - self.storage.get('init_timestamp', 0)
×
354
        state = self.get_state()
×
355
        if state in [ChannelState.PREOPENING, ChannelState.OPENING, ChannelState.FORCE_CLOSING]:
×
356
            if self.is_initiator():
×
357
                # set channel state to REDEEMED so that it can be removed manually
358
                # to protect ourselves against a server lying by omission,
359
                # we check that funding_inputs have been double spent and deeply mined
360
                inputs = self.storage.get('funding_inputs', [])
×
361
                if not inputs:
×
UNCOV
362
                    self.logger.info(f'channel funding inputs are not provided')
×
UNCOV
363
                    self.set_state(ChannelState.REDEEMED)
×
UNCOV
364
                for i in inputs:
×
365
                    spender_txid = self.lnworker.wallet.db.get_spent_outpoint(*i)
×
366
                    if spender_txid is None:
×
367
                        continue
×
368
                    if spender_txid != self.funding_outpoint.txid:
×
369
                        tx_mined_height = self.lnworker.wallet.adb.get_tx_height(spender_txid)
×
370
                        if tx_mined_height.conf > lnutil.REDEEM_AFTER_DOUBLE_SPENT_DELAY:
×
371
                            self.logger.info(f'channel is double spent {inputs}')
×
372
                            self.set_state(ChannelState.REDEEMED)
×
373
                            break
×
374
            else:
375
                if chan_age > CHANNEL_OPENING_TIMEOUT:
×
376
                    self.lnworker.remove_channel(self.channel_id)
×
377
        elif self.is_zeroconf() and state in [ChannelState.OPEN, ChannelState.CLOSING, ChannelState.FORCE_CLOSING]:
×
378
            assert self.storage.get('init_timestamp') is not None, "init_timestamp not set for zeroconf channel"
×
379
            # handling zeroconf channels with no funding tx, can happen if broadcasting fails on LSP side
380
            # or if the LSP did double spent the funding tx/never published it intentionally
381
            # only remove a timed out OPEN channel if we are connected to the network to prevent removing it if we went
382
            # offline before seeing the funding tx
383
            if state != ChannelState.OPEN or chan_age > ZEROCONF_TIMEOUT and self.lnworker.network.is_connected():
×
384
                # we delete the channel if its in closing state (either initiated manually by client or by LSP on failure)
385
                # or if the channel is not seeing any funding tx after 10 minutes to prevent further usage (limit damage)
UNCOV
386
                self.set_state(ChannelState.REDEEMED, force=True)
×
UNCOV
387
                local_balance_sat = int(self.balance(LOCAL) // 1000)
×
388
                if local_balance_sat > 0:
×
UNCOV
389
                    self.logger.warning(
×
390
                        f"we may have been scammed out of {local_balance_sat} sat by our "
391
                        f"JIT provider: {self.lnworker.config.ZEROCONF_TRUSTED_NODE} or he didn't use our preimage")
392
                    self.lnworker.config.ZEROCONF_TRUSTED_NODE = ''
×
393
                self.lnworker.lnwatcher.unwatch_channel(self.get_funding_address(), self.funding_outpoint.to_str())
×
394
                # remove remaining local transactions from the wallet, this will also remove child transactions (closing tx)
UNCOV
395
                self.lnworker.lnwatcher.adb.remove_transaction(self.funding_outpoint.txid)
×
UNCOV
396
                self.lnworker.remove_channel(self.channel_id)
×
397

398
    def update_funded_state(self, *, funding_txid: str, funding_height: TxMinedInfo) -> None:
4✔
UNCOV
399
        self.save_funding_height(txid=funding_txid, height=funding_height.height, timestamp=funding_height.timestamp)
×
400
        self.delete_closing_height()
×
401
        if funding_height.conf>0:
×
UNCOV
402
            self.set_short_channel_id(ShortChannelID.from_components(
×
403
                funding_height.height, funding_height.txpos, self.funding_outpoint.output_index))
404
        if self.get_state() == ChannelState.OPENING:
×
405
            if self.is_funding_tx_mined(funding_height):
×
406
                self.set_state(ChannelState.FUNDED)
×
407
        elif self.is_zeroconf() and funding_height.conf >= 3 and not self.should_request_force_close:
×
UNCOV
408
            if not self.is_funding_tx_mined(funding_height):
×
409
                # funding tx is invalid (invalid amount or address) we need to get rid of the channel again
410
                self.should_request_force_close = True
×
411
                if self.lnworker and self.node_id in self.lnworker.peers:
×
412
                    # reconnect to trigger force close request
413
                    self.lnworker.peers[self.node_id].close_and_cleanup()
×
414
            else:
415
                # remove zeroconf flag as we are now confirmed, this is to prevent an electrum server causing
416
                # us to remove a channel later in update_unfunded_state by omitting its funding tx
UNCOV
417
                self.remove_zeroconf_flag()
×
418

419
    def update_closed_state(self, *, funding_txid: str, funding_height: TxMinedInfo,
4✔
420
                            closing_txid: str, closing_height: TxMinedInfo, keep_watching: bool) -> None:
UNCOV
421
        self.save_funding_height(txid=funding_txid, height=funding_height.height, timestamp=funding_height.timestamp)
×
422
        self.save_closing_height(txid=closing_txid, height=closing_height.height, timestamp=closing_height.timestamp)
×
UNCOV
423
        if funding_height.conf>0:
×
UNCOV
424
            self.set_short_channel_id(ShortChannelID.from_components(
×
425
                funding_height.height, funding_height.txpos, self.funding_outpoint.output_index))
426
        if self.get_state() < ChannelState.CLOSED:
×
427
            conf = closing_height.conf
×
428
            if conf > 0:
×
429
                self.set_state(ChannelState.CLOSED)
×
UNCOV
430
                if self.lnworker:
×
431
                    self.lnworker.wallet.txbatcher.set_password_future(None)
×
432
            else:
433
                # we must not trust the server with unconfirmed transactions,
434
                # because the state transition is irreversible. if the remote
435
                # force closed, we remain OPEN until the closing tx is confirmed
436
                self.unconfirmed_closing_txid = closing_txid
×
UNCOV
437
                if self.lnworker:
×
UNCOV
438
                    util.trigger_callback('channel', self.lnworker.wallet, self)
×
439

UNCOV
440
        if self.get_state() == ChannelState.CLOSED and not keep_watching:
×
441
            self.set_state(ChannelState.REDEEMED)
×
442
            if self.lnworker and self.is_backup():
×
443
                # auto-remove redeemed backups
UNCOV
444
                self.lnworker.remove_channel_backup(self.channel_id)
×
445

446
    @abstractmethod
4✔
447
    def is_initiator(self) -> bool:
4✔
UNCOV
448
        pass
×
449

450
    @abstractmethod
4✔
451
    def is_public(self) -> bool:
4✔
UNCOV
452
        pass
×
453

454
    @abstractmethod
4✔
455
    def is_zeroconf(self) -> bool:
4✔
UNCOV
456
        pass
×
457

458
    @abstractmethod
4✔
459
    def remove_zeroconf_flag(self) -> None:
4✔
UNCOV
460
        pass
×
461

462
    @abstractmethod
4✔
463
    def is_funding_tx_mined(self, funding_height: TxMinedInfo) -> bool:
4✔
UNCOV
464
        pass
×
465

466
    @abstractmethod
4✔
467
    def get_funding_address(self) -> str:
4✔
UNCOV
468
        pass
×
469

470
    def get_funding_tx(self) -> Optional[Transaction]:
4✔
UNCOV
471
        funding_txid = self.funding_outpoint.txid
×
UNCOV
472
        return self.lnworker.lnwatcher.adb.get_transaction(funding_txid)
×
473

474
    @abstractmethod
4✔
475
    def get_sweep_address(self) -> str:
4✔
476
        """Returns a wallet address we can use to sweep coins to.
477
        It could be something static to the channel (fixed for its lifecycle),
478
        or it might just ask the wallet now for an unused address.
479
        """
UNCOV
480
        pass
×
481

482
    def get_state_for_GUI(self) -> str:
4✔
UNCOV
483
        cs = self.get_state()
×
UNCOV
484
        if cs <= ChannelState.OPEN and self.unconfirmed_closing_txid:
×
485
            return 'FORCE-CLOSING'
×
UNCOV
486
        return cs.name
×
487

488
    @abstractmethod
4✔
489
    def get_oldest_unrevoked_ctn(self, subject: HTLCOwner) -> int:
4✔
490
        pass
×
491

492
    @abstractmethod
4✔
493
    def included_htlcs(self, subject: HTLCOwner, direction: Direction, ctn: int = None) -> Sequence[UpdateAddHtlc]:
4✔
UNCOV
494
        pass
×
495

496
    @abstractmethod
4✔
497
    def funding_txn_minimum_depth(self) -> int:
4✔
UNCOV
498
        pass
×
499

500
    @abstractmethod
4✔
501
    def balance(self, whose: HTLCOwner, *, ctx_owner=HTLCOwner.LOCAL, ctn: int = None) -> int:
4✔
502
        """This balance (in msat) only considers HTLCs that have been settled by ctn.
503
        It disregards reserve, fees, and pending HTLCs (in both directions).
504
        """
UNCOV
505
        pass
×
506

507
    @abstractmethod
4✔
508
    def balance_minus_outgoing_htlcs(self, whose: HTLCOwner, *,
4✔
509
                                     ctx_owner: HTLCOwner = HTLCOwner.LOCAL,
510
                                     ctn: int = None) -> int:
511
        """This balance (in msat), which includes the value of
512
        pending outgoing HTLCs, is used in the UI.
513
        """
UNCOV
514
        pass
×
515

516
    @abstractmethod
4✔
517
    def is_frozen_for_sending(self) -> bool:
4✔
518
        """Whether the user has marked this channel as frozen for sending.
519
        Frozen channels are not supposed to be used for new outgoing payments.
520
        (note that payment-forwarding ignores this option)
521
        """
UNCOV
522
        pass
×
523

524
    @abstractmethod
4✔
525
    def is_frozen_for_receiving(self) -> bool:
4✔
526
        """Whether the user has marked this channel as frozen for receiving.
527
        Frozen channels are not supposed to be used for new incoming payments.
528
        (note that payment-forwarding ignores this option)
529
        """
UNCOV
530
        pass
×
531

532
    @abstractmethod
4✔
533
    def get_local_pubkey(self) -> bytes:
4✔
534
        """Returns our node ID."""
535
        pass
×
536

537
    @abstractmethod
4✔
538
    def get_capacity(self) -> Optional[int]:
4✔
539
        """Returns channel capacity in satoshis, or None if unknown."""
540
        pass
×
541

542
    @abstractmethod
4✔
543
    def can_be_deleted(self) -> bool:
4✔
UNCOV
544
        pass
×
545

546
    @abstractmethod
4✔
547
    def get_wallet_addresses_channel_might_want_reserved(self) -> Sequence[str]:
4✔
548
        """Returns a list of addrs that the wallet should not use, to avoid address-reuse.
549
        Typically, these addresses are wallet.is_mine, but that is not guaranteed,
550
        in which case the wallet can just ignore those.
551
        """
UNCOV
552
        pass
×
553

554
    def has_anchors(self) -> bool:
4✔
UNCOV
555
        pass
×
556

557

558
class ChannelBackup(AbstractChannel):
4✔
559
    """
560
    current capabilities:
561
      - detect force close
562
      - request force close
563
      - sweep my ctx to_local
564
    future:
565
      - will need to sweep their ctx to_remote
566
    """
567

568
    def __init__(self, cb: ChannelBackupStorage, *, lnworker=None):
4✔
UNCOV
569
        self.name = None
×
UNCOV
570
        self.cb = cb
×
UNCOV
571
        self.is_imported = isinstance(self.cb, ImportedChannelBackupStorage)
×
UNCOV
572
        self._sweep_info = {}
×
UNCOV
573
        self.storage = {} # dummy storage
×
574
        self._state = ChannelState.OPENING
×
575
        self.node_id = cb.node_id if self.is_imported else cb.node_id_prefix
×
576
        self.channel_id = cb.channel_id()
×
577
        self.funding_outpoint = cb.funding_outpoint()
×
578
        self.lnworker = lnworker
×
579
        self.short_channel_id = None
×
580
        Logger.__init__(self)
×
581
        self.config = {}
×
582
        if self.is_imported:
×
583
            assert isinstance(cb, ImportedChannelBackupStorage)
×
584
            self.init_config(cb)
×
585
        self.unconfirmed_closing_txid = None # not a state, only for GUI
×
586

587
    def init_config(self, cb: ImportedChannelBackupStorage):
4✔
588
        local_payment_pubkey = cb.local_payment_pubkey
×
589
        if local_payment_pubkey is None:
×
UNCOV
590
            self.logger.warning(
×
591
                f"local_payment_pubkey missing from (old-type) channel backup. "
592
                f"You should export and re-import a newer backup.")
593
        multisig_funding_keypair = None
×
594
        if multisig_funding_secret := cb.multisig_funding_privkey:
×
UNCOV
595
            multisig_funding_keypair = Keypair(
×
596
                privkey=multisig_funding_secret,
597
                pubkey=ecc.ECPrivkey(multisig_funding_secret).get_public_key_bytes(),
598
            )
599
        self.config[LOCAL] = LocalConfig.from_seed(
×
600
            channel_seed=cb.channel_seed,
601
            to_self_delay=cb.local_delay,
602
            # there are three cases of backups:
603
            # 1. legacy: payment_basepoint will be derived
604
            # 2. static_remotekey: to_remote sweep not necessary due to wallet address
605
            # 3. anchor outputs: sweep to_remote by deriving the key from the funding pubkeys
606
            static_remotekey=local_payment_pubkey,
607
            multisig_key=multisig_funding_keypair,
608
            # dummy values
609
            static_payment_key=None,
610
            dust_limit_sat=None,
611
            max_htlc_value_in_flight_msat=None,
612
            max_accepted_htlcs=None,
613
            initial_msat=None,
614
            reserve_sat=None,
615
            funding_locked_received=False,
616
            current_commitment_signature=None,
617
            current_htlc_signatures=b'',
618
            htlc_minimum_msat=1,
619
            upfront_shutdown_script='',
620
            announcement_node_sig=b'',
621
            announcement_bitcoin_sig=b'',
622
        )
UNCOV
623
        self.config[REMOTE] = RemoteConfig(
×
624
            # payment_basepoint needed to deobfuscate ctn in our_ctx
625
            payment_basepoint=OnlyPubkeyKeypair(cb.remote_payment_pubkey),
626
            # revocation_basepoint is used to claim to_local in our ctx
627
            revocation_basepoint=OnlyPubkeyKeypair(cb.remote_revocation_pubkey),
628
            to_self_delay=cb.remote_delay,
629
            # dummy values
630
            multisig_key=OnlyPubkeyKeypair(None),
631
            htlc_basepoint=OnlyPubkeyKeypair(None),
632
            delayed_basepoint=OnlyPubkeyKeypair(None),
633
            dust_limit_sat=None,
634
            max_htlc_value_in_flight_msat=None,
635
            max_accepted_htlcs=None,
636
            initial_msat = None,
637
            reserve_sat = None,
638
            htlc_minimum_msat=None,
639
            next_per_commitment_point=None,
640
            current_per_commitment_point=None,
641
            upfront_shutdown_script='',
642
            announcement_node_sig=b'',
643
            announcement_bitcoin_sig=b'',
644
        )
645

646
    def can_be_deleted(self):
4✔
UNCOV
647
        return self.is_imported or self.is_redeemed()
×
648

649
    def get_capacity(self):
4✔
UNCOV
650
        lnwatcher = self.lnworker.lnwatcher
×
651
        if lnwatcher:
×
652
            # fixme: we should probably not call that method here
UNCOV
653
            return lnwatcher.adb.get_tx_delta(self.funding_outpoint.txid, self.cb.funding_address)
×
654
        return None
×
655

656
    def is_backup(self):
4✔
657
        return True
×
658

659
    def create_sweeptxs_for_their_ctx(self, ctx):
4✔
UNCOV
660
        funding_tx = self.get_funding_tx()
×
661
        assert funding_tx
×
UNCOV
662
        return sweep_their_ctx_to_remote_backup(chan=self, ctx=ctx, funding_tx=funding_tx)
×
663

664
    def create_sweeptxs_for_our_ctx(self, ctx):
4✔
665
        if self.is_imported:
×
666
            return sweep_our_ctx(chan=self, ctx=ctx)
×
667
        else:
UNCOV
668
            return {}
×
669

670
    def maybe_sweep_htlcs(self, ctx: Transaction, htlc_tx: Transaction) -> Dict[str, SweepInfo]:
4✔
UNCOV
671
        return {}
×
672

673
    def extract_preimage_from_htlc_txin(self, txin: TxInput, *, is_deeply_mined: bool) -> None:
4✔
UNCOV
674
        return None
×
675

676
    def get_funding_address(self):
4✔
UNCOV
677
        return self.cb.funding_address
×
678

679
    def is_initiator(self):
4✔
UNCOV
680
        return self.cb.is_initiator
×
681

682
    def is_public(self):
4✔
UNCOV
683
        return False
×
684

685
    def get_oldest_unrevoked_ctn(self, who):
4✔
UNCOV
686
        return -1
×
687

688
    def included_htlcs(self, subject, direction, ctn=None):
4✔
UNCOV
689
        return []
×
690

691
    def funding_txn_minimum_depth(self):
4✔
UNCOV
692
        return 1
×
693

694
    def is_funding_tx_mined(self, funding_height):
4✔
UNCOV
695
        return funding_height.conf > 1
×
696

697
    def balance_minus_outgoing_htlcs(self, whose: HTLCOwner, *, ctx_owner: HTLCOwner = HTLCOwner.LOCAL, ctn: int = None):
4✔
UNCOV
698
        return 0
×
699

700
    def balance(self, whose: HTLCOwner, *, ctx_owner=HTLCOwner.LOCAL, ctn: int = None) -> int:
4✔
UNCOV
701
        return 0
×
702

703
    def is_frozen_for_sending(self) -> bool:
4✔
UNCOV
704
        return False
×
705

706
    def is_frozen_for_receiving(self) -> bool:
4✔
UNCOV
707
        return False
×
708

709
    def get_sweep_address(self) -> str:
4✔
UNCOV
710
        return self.lnworker.wallet.get_new_sweep_address_for_channel()
×
711

712
    def has_anchors(self) -> Optional[bool]:
4✔
UNCOV
713
        return None
×
714

715
    def is_zeroconf(self) -> bool:
4✔
UNCOV
716
        return False
×
717

718
    def remove_zeroconf_flag(self) -> None:
4✔
UNCOV
719
        pass
×
720

721
    def get_local_pubkey(self) -> bytes:
4✔
UNCOV
722
        cb = self.cb
×
723
        assert isinstance(cb, ChannelBackupStorage)
×
UNCOV
724
        if isinstance(cb, ImportedChannelBackupStorage):
×
UNCOV
725
            return ecc.ECPrivkey(cb.privkey).get_public_key_bytes(compressed=True)
×
726
        if isinstance(cb, OnchainChannelBackupStorage):
×
727
            return self.lnworker.node_keypair.pubkey
×
728
        raise NotImplementedError(f"unexpected cb type: {type(cb)}")
×
729

730
    def get_close_options(self) -> Sequence[ChanCloseOption]:
4✔
731
        ret = []
×
732
        if self.get_state() == ChannelState.FUNDED:
×
UNCOV
733
            ret.append(ChanCloseOption.REQUEST_REMOTE_FCLOSE)
×
UNCOV
734
        return ret
×
735

736
    def get_wallet_addresses_channel_might_want_reserved(self) -> Sequence[str]:
4✔
737
        if self.is_imported:
×
738
            # For v1 imported cbs, we have the local_payment_pubkey, which is
739
            # directly used as p2wpkh() of static_remotekey channels.
740
            # (for v0 imported cbs, the correct local_payment_pubkey is missing, and so
741
            #  we might calculate a different address here, which might not be wallet.is_mine,
742
            #  but that should be harmless)
UNCOV
743
            our_payment_pubkey = self.config[LOCAL].payment_basepoint.pubkey
×
UNCOV
744
            to_remote_address = make_commitment_output_to_remote_address(our_payment_pubkey, has_anchors=self.has_anchors())
×
UNCOV
745
            return [to_remote_address]
×
746
        else:  # on-chain backup
747
            return []
×
748

749

750
class Channel(AbstractChannel):
4✔
751
    # note: try to avoid naming ctns/ctxs/etc as "current" and "pending".
752
    #       they are ambiguous. Use "oldest_unrevoked" or "latest" or "next".
753
    #       TODO enforce this ^
754

755
    # our forwarding parameters for forwarding HTLCs through this channel
756
    forwarding_cltv_delta = 144
4✔
757
    forwarding_fee_base_msat = 1000
4✔
758
    forwarding_fee_proportional_millionths = 1
4✔
759

760
    def __repr__(self):
4✔
UNCOV
761
        return "Channel(%s)"%self.get_id_for_log()
×
762

763
    def __init__(self, state: 'StoredDict', *, name=None, lnworker=None, initial_feerate=None, opening_fee=None):
4✔
764
        self.opening_fee = opening_fee
4✔
765
        self.name = name
4✔
766
        self.channel_id = bfh(state["channel_id"])
4✔
767
        self.short_channel_id = ShortChannelID.normalize(state["short_channel_id"])
4✔
768
        Logger.__init__(self)  # should be after short_channel_id is set
4✔
769
        self.lnworker = lnworker
4✔
770
        self.storage = state
4✔
771
        self.db_lock = self.storage.db.lock if self.storage.db else threading.RLock()
4✔
772
        self.config = {}
4✔
773
        self.config[LOCAL] = state["local_config"]
4✔
774
        self.config[REMOTE] = state["remote_config"]
4✔
775
        self.constraints = state["constraints"]  # type: ChannelConstraints
4✔
776
        self.funding_outpoint = state["funding_outpoint"]
4✔
777
        self.node_id = bfh(state["node_id"])
4✔
778
        self.onion_keys = state['onion_keys']  # type: Dict[int, bytes]
4✔
779
        self.data_loss_protect_remote_pcp = state['data_loss_protect_remote_pcp']
4✔
780
        self.hm = HTLCManager(log=state['log'], initial_feerate=initial_feerate)
4✔
781
        self.unfulfilled_htlcs = state["unfulfilled_htlcs"]  # type: Dict[int, Tuple[str, Optional[str]]]
4✔
782
        # ^ htlc_id -> onion_packet_hex, forwarding_key
783
        self._state = ChannelState[state['state']]
4✔
784
        self.peer_state = PeerState.DISCONNECTED
4✔
785
        self._sweep_info = {}
4✔
786
        self._outgoing_channel_update = None  # type: Optional[bytes]
4✔
787
        self.revocation_store = RevocationStore(state["revocation_store"])
4✔
788
        self._can_send_ctx_updates = True  # type: bool
4✔
789
        self._receive_fail_reasons = {}  # type: Dict[int, (bytes, OnionRoutingFailure)]
4✔
790
        self.unconfirmed_closing_txid = None # not a state, only for GUI
4✔
791
        self.sent_channel_ready = False # no need to persist this, because channel_ready is re-sent in channel_reestablish
4✔
792
        self.sent_announcement_signatures = False
4✔
793
        self.htlc_settle_time = {}
4✔
794

795
    def get_local_scid_alias(self, *, create_new_if_needed: bool = False) -> Optional[bytes]:
4✔
796
        """Get scid_alias to be used for *outgoing* HTLCs.
797
        (called local as we choose the value)
798
        """
799
        if alias := self.storage.get('local_scid_alias'):
4✔
UNCOV
800
            return bytes.fromhex(alias)
×
801
        elif create_new_if_needed:
4✔
802
            # deterministic, same secrecy level as wallet master pubkey
803
            wallet_fingerprint = bytes(self.lnworker.wallet.get_fingerprint(), "utf8")
4✔
804
            alias = sha256(wallet_fingerprint + self.channel_id)[0:8]
4✔
805
            self.storage['local_scid_alias'] = alias.hex()
4✔
806
            return alias
4✔
807
        return None
4✔
808

809
    def save_remote_scid_alias(self, alias: bytes):
4✔
810
        self.storage['alias'] = alias.hex()
4✔
811

812
    def get_remote_scid_alias(self) -> Optional[bytes]:
4✔
813
        """Get scid_alias to be used for *incoming* HTLCs.
814
        (called remote as the remote chooses the value)
815
        """
816
        alias = self.storage.get('alias')
4✔
817
        return bytes.fromhex(alias) if alias else None
4✔
818

819
    def get_scid_or_local_alias(self):
4✔
820
        return self.short_channel_id or self.get_local_scid_alias()
4✔
821

822
    def has_onchain_backup(self):
4✔
UNCOV
823
        return self.storage.get('has_onchain_backup', False)
×
824

825
    def can_be_deleted(self):
4✔
826
        return self.is_redeemed()
×
827

828
    def get_capacity(self):
4✔
829
        return self.constraints.capacity
×
830

831
    def is_public(self):
4✔
832
        return bool(self.constraints.flags & CF_ANNOUNCE_CHANNEL)
4✔
833

834
    def is_initiator(self):
4✔
835
        return self.constraints.is_initiator
4✔
836

837
    def is_active(self):
4✔
838
        return self.get_state() == ChannelState.OPEN and self.peer_state == PeerState.GOOD
4✔
839

840
    def funding_txn_minimum_depth(self):
4✔
UNCOV
841
        return self.constraints.funding_txn_minimum_depth
×
842

843
    def diagnostic_name(self):
4✔
844
        if self.name:
4✔
845
            return str(self.name)
4✔
846
        return super().diagnostic_name()
4✔
847

848
    def set_onion_key(self, key: int, value: bytes):
4✔
849
        self.onion_keys[key] = value
4✔
850

851
    def pop_onion_key(self, key: int) -> bytes:
4✔
852
        return self.onion_keys.pop(key)
4✔
853

854
    def set_data_loss_protect_remote_pcp(self, key, value):
4✔
855
        self.data_loss_protect_remote_pcp[key] = value
4✔
856

857
    def get_data_loss_protect_remote_pcp(self, key):
4✔
UNCOV
858
        return self.data_loss_protect_remote_pcp.get(key)
×
859

860
    def get_local_pubkey(self) -> bytes:
4✔
861
        if not self.lnworker:
4✔
UNCOV
862
            raise Exception('lnworker not set for channel!')
×
863
        return self.lnworker.node_keypair.pubkey
4✔
864

865
    def set_remote_update(self, payload: dict) -> None:
4✔
866
        """Save the ChannelUpdate message for the incoming direction of this channel.
867
        This message contains info we need to populate private route hints when
868
        creating invoices.
869
        """
870
        assert payload['short_channel_id'] in [self.short_channel_id, self.get_local_scid_alias()]
4✔
871
        from .channel_db import ChannelDB
4✔
872
        ChannelDB.verify_channel_update(payload, start_node=self.node_id)
4✔
873
        raw = payload['raw']
4✔
874
        self.storage['remote_update'] = raw.hex()
4✔
875

876
    def get_remote_update(self) -> Optional[bytes]:
4✔
877
        return bfh(self.storage.get('remote_update')) if self.storage.get('remote_update') else None
4✔
878

879
    def add_or_update_peer_addr(self, peer: LNPeerAddr) -> None:
4✔
UNCOV
880
        if 'peer_network_addresses' not in self.storage:
×
UNCOV
881
            self.storage['peer_network_addresses'] = {}
×
UNCOV
882
        self.storage['peer_network_addresses'][peer.net_addr_str()] = now()
×
883

884
    def get_peer_addresses(self) -> Iterator[LNPeerAddr]:
4✔
885
        # sort by timestamp: most recent first
UNCOV
886
        addrs = sorted(self.storage.get('peer_network_addresses', {}).items(),
×
887
                       key=lambda x: x[1], reverse=True)
UNCOV
888
        for net_addr_str, ts in addrs:
×
889
            net_addr = NetAddress.from_string(net_addr_str)
×
UNCOV
890
            yield LNPeerAddr(host=str(net_addr.host), port=net_addr.port, pubkey=self.node_id)
×
891

892
    def get_outgoing_gossip_channel_update(self, *, scid: ShortChannelID = None) -> bytes:
4✔
893
        """
894
        scid: to be put into the channel_update message instead of the real scid, as this might be an scid alias
895
        """
896
        if self._outgoing_channel_update is not None and scid is None:
4✔
897
            return self._outgoing_channel_update
4✔
898
        if not self.lnworker:
4✔
UNCOV
899
            raise Exception('lnworker not set for channel!')
×
900
        if scid is None:
4✔
901
            scid = self.short_channel_id
4✔
902
        sorted_node_ids = list(sorted([self.node_id, self.get_local_pubkey()]))
4✔
903
        channel_flags = b'\x00' if sorted_node_ids[0] == self.get_local_pubkey() else b'\x01'
4✔
904
        htlc_maximum_msat = min(self.config[REMOTE].max_htlc_value_in_flight_msat, 1000 * self.constraints.capacity)
4✔
905

906
        chan_upd = encode_msg(
4✔
907
            "channel_update",
908
            short_channel_id=scid,
909
            channel_flags=channel_flags,
910
            message_flags=b'\x01',
911
            cltv_expiry_delta=self.forwarding_cltv_delta,
912
            htlc_minimum_msat=self.config[REMOTE].htlc_minimum_msat,
913
            htlc_maximum_msat=htlc_maximum_msat,
914
            fee_base_msat=self.forwarding_fee_base_msat,
915
            fee_proportional_millionths=self.forwarding_fee_proportional_millionths,
916
            chain_hash=constants.net.rev_genesis_bytes(),
917
            timestamp=now(),
918
        )
919
        sighash = sha256d(chan_upd[2 + 64:])
4✔
920
        sig = ecc.ECPrivkey(self.lnworker.node_keypair.privkey).ecdsa_sign(sighash, sigencode=ecc.ecdsa_sig64_from_r_and_s)
4✔
921
        message_type, payload = decode_msg(chan_upd)
4✔
922
        payload['signature'] = sig
4✔
923
        chan_upd = encode_msg(message_type, **payload)
4✔
924

925
        self._outgoing_channel_update = chan_upd
4✔
926
        return chan_upd
4✔
927

928
    def construct_channel_announcement_without_sigs(self) -> Tuple[bytes, bool]:
4✔
929
        bitcoin_keys = [
4✔
930
            self.config[REMOTE].multisig_key.pubkey,
931
            self.config[LOCAL].multisig_key.pubkey]
932
        node_ids = [self.node_id, self.get_local_pubkey()]
4✔
933
        is_reverse = node_ids[0] > node_ids[1]
4✔
934
        if is_reverse:
4✔
935
            node_ids.reverse()
4✔
936
            bitcoin_keys.reverse()
4✔
937
        chan_ann = encode_msg(
4✔
938
            "channel_announcement",
939
            len=0,
940
            features=b'',
941
            chain_hash=constants.net.rev_genesis_bytes(),
942
            short_channel_id=self.short_channel_id,
943
            node_id_1=node_ids[0],
944
            node_id_2=node_ids[1],
945
            bitcoin_key_1=bitcoin_keys[0],
946
            bitcoin_key_2=bitcoin_keys[1],
947
        )
948
        return chan_ann, is_reverse
4✔
949

950
    def get_channel_announcement_hash(self):
4✔
UNCOV
951
        chan_ann, _ = self.construct_channel_announcement_without_sigs()
×
UNCOV
952
        return sha256d(chan_ann[256+2:])
×
953

954
    def is_static_remotekey_enabled(self) -> bool:
4✔
955
        channel_type = ChannelType(self.storage.get('channel_type'))
4✔
956
        return bool(channel_type & ChannelType.OPTION_STATIC_REMOTEKEY)
4✔
957

958
    def is_zeroconf(self) -> bool:
4✔
UNCOV
959
        channel_type = ChannelType(self.storage.get('channel_type'))
×
UNCOV
960
        return bool(channel_type & ChannelType.OPTION_ZEROCONF)
×
961

962
    def remove_zeroconf_flag(self) -> None:
4✔
963
        if not self.is_zeroconf():
×
UNCOV
964
            return
×
UNCOV
965
        channel_type = ChannelType(self.storage.get('channel_type'))
×
966
        self.storage['channel_type'] = channel_type & ~ChannelType.OPTION_ZEROCONF
×
967

968
    def get_sweep_address(self) -> str:
4✔
969
        # TODO: in case of unilateral close with pending HTLCs, this address will be reused
970
        if self.has_anchors():
4✔
UNCOV
971
            addr = self.lnworker.wallet.get_new_sweep_address_for_channel()
×
972
        elif self.is_static_remotekey_enabled():
4✔
973
            our_payment_pubkey = self.config[LOCAL].payment_basepoint.pubkey
4✔
974
            addr = make_commitment_output_to_remote_address(our_payment_pubkey, has_anchors=self.has_anchors())
4✔
975
        if self.lnworker:
4✔
976
            assert self.lnworker.wallet.is_mine(addr)
4✔
977
        return addr
4✔
978

979
    def has_anchors(self) -> bool:
4✔
980
        channel_type = ChannelType(self.storage.get('channel_type'))
4✔
981
        return bool(channel_type & ChannelType.OPTION_ANCHORS_ZERO_FEE_HTLC_TX)
4✔
982

983
    def get_wallet_addresses_channel_might_want_reserved(self) -> Sequence[str]:
4✔
984
        assert self.is_static_remotekey_enabled()
4✔
985
        our_payment_pubkey = self.config[LOCAL].payment_basepoint.pubkey
4✔
986
        to_remote_address = make_commitment_output_to_remote_address(our_payment_pubkey, has_anchors=self.has_anchors())
4✔
987
        return [to_remote_address]
4✔
988

989
    def get_feerate(self, subject: HTLCOwner, *, ctn: int) -> int:
4✔
990
        # returns feerate in sat/kw
991
        return self.hm.get_feerate(subject, ctn)
4✔
992

993
    def get_oldest_unrevoked_feerate(self, subject: HTLCOwner) -> int:
4✔
994
        return self.hm.get_feerate_in_oldest_unrevoked_ctx(subject)
4✔
995

996
    def get_latest_feerate(self, subject: HTLCOwner) -> int:
4✔
997
        return self.hm.get_feerate_in_latest_ctx(subject)
4✔
998

999
    def get_next_feerate(self, subject: HTLCOwner) -> int:
4✔
1000
        return self.hm.get_feerate_in_next_ctx(subject)
4✔
1001

1002
    def get_payments(self, status=None) -> Mapping[bytes, List[HTLCWithStatus]]:
4✔
1003
        out = defaultdict(list)
4✔
1004
        for direction, htlc in self.hm.all_htlcs_ever():
4✔
1005
            htlc_proposer = LOCAL if direction is SENT else REMOTE
4✔
1006
            if self.hm.was_htlc_failed(htlc_id=htlc.htlc_id, htlc_proposer=htlc_proposer):
4✔
1007
                _status = 'failed'
4✔
1008
            elif self.hm.was_htlc_preimage_released(htlc_id=htlc.htlc_id, htlc_proposer=htlc_proposer):
4✔
1009
                _status = 'settled'
4✔
1010
            else:
1011
                _status = 'inflight'
4✔
1012
            if status and status != _status:
4✔
1013
                continue
4✔
1014
            htlc_with_status = HTLCWithStatus(
4✔
1015
                channel_id=self.channel_id, htlc=htlc, direction=direction, status=_status)
1016
            out[htlc.payment_hash].append(htlc_with_status)
4✔
1017
        return out
4✔
1018

1019
    def open_with_first_pcp(self, remote_pcp: bytes, remote_sig: bytes) -> None:
4✔
1020
        with self.db_lock:
4✔
1021
            self.config[REMOTE].current_per_commitment_point = remote_pcp
4✔
1022
            self.config[REMOTE].next_per_commitment_point = None
4✔
1023
            self.config[LOCAL].current_commitment_signature = remote_sig
4✔
1024
            self.hm.channel_open_finished()
4✔
1025
            self.peer_state = PeerState.GOOD
4✔
1026

1027
    def get_state_for_GUI(self):
4✔
UNCOV
1028
        cs_name = super().get_state_for_GUI()
×
UNCOV
1029
        if self.is_closed() or self.unconfirmed_closing_txid:
×
UNCOV
1030
            return cs_name
×
1031
        ps = self.peer_state
×
1032
        if ps != PeerState.GOOD:
×
1033
            return ps.name
×
1034
        return cs_name
×
1035

1036
    def set_can_send_ctx_updates(self, b: bool) -> None:
4✔
1037
        self._can_send_ctx_updates = b
4✔
1038

1039
    def can_send_ctx_updates(self) -> bool:
4✔
1040
        """Whether we can send update_fee, update_*_htlc changes to the remote."""
1041
        if self.get_state() not in (ChannelState.OPEN, ChannelState.SHUTDOWN):
4✔
1042
            return False
4✔
1043
        if self.peer_state != PeerState.GOOD:
4✔
UNCOV
1044
            return False
×
1045
        if not self._can_send_ctx_updates:
4✔
1046
            return False
2✔
1047
        return True
4✔
1048

1049
    def can_send_update_add_htlc(self) -> bool:
4✔
1050
        return self.can_send_ctx_updates() and self.is_open()
4✔
1051

1052
    def is_frozen_for_sending(self) -> bool:
4✔
1053
        if self.lnworker and self.lnworker.uses_trampoline() and not self.lnworker.is_trampoline_peer(self.node_id):
4✔
UNCOV
1054
            return True
×
1055
        return self.storage.get('frozen_for_sending', False)
4✔
1056

1057
    def set_frozen_for_sending(self, b: bool) -> None:
4✔
UNCOV
1058
        self.storage['frozen_for_sending'] = bool(b)
×
UNCOV
1059
        util.trigger_callback('channel', self.lnworker.wallet, self)
×
1060

1061
    def is_frozen_for_receiving(self) -> bool:
4✔
1062
        if self.lnworker and self.lnworker.uses_trampoline() and not self.lnworker.is_trampoline_peer(self.node_id):
4✔
UNCOV
1063
            return True
×
1064
        return self.storage.get('frozen_for_receiving', False)
4✔
1065

1066
    def set_frozen_for_receiving(self, b: bool) -> None:
4✔
UNCOV
1067
        self.storage['frozen_for_receiving'] = bool(b)
×
UNCOV
1068
        util.trigger_callback('channel', self.lnworker.wallet, self)
×
1069

1070
    def _assert_can_add_htlc(self, *, htlc_proposer: HTLCOwner, amount_msat: int,
4✔
1071
                             ignore_min_htlc_value: bool = False) -> None:
1072
        """Raises PaymentFailure if the htlc_proposer cannot add this new HTLC.
1073
        (this is relevant both for forwarding and endpoint)
1074
        """
1075
        htlc_receiver = htlc_proposer.inverted()
4✔
1076
        # note: all these tests are about the *receiver's* *next* commitment transaction,
1077
        #       and the constraints are the ones imposed by their config
1078
        ctn = self.get_next_ctn(htlc_receiver)
4✔
1079
        chan_config = self.config[htlc_receiver]
4✔
1080
        if self.get_state() != ChannelState.OPEN:
4✔
UNCOV
1081
            raise PaymentFailure('Channel not open', self.get_state())
×
1082
        if htlc_proposer == LOCAL:
4✔
1083
            if not self.can_send_ctx_updates():
4✔
1084
                raise PaymentFailure('Channel cannot send ctx updates')
×
1085
            if not self.can_send_update_add_htlc():
4✔
UNCOV
1086
                raise PaymentFailure('Channel cannot add htlc')
×
1087

1088
        # If proposer is LOCAL we apply stricter checks as that is behaviour we can control.
1089
        # This should lead to fewer disagreements (i.e. channels failing).
1090
        strict = (htlc_proposer == LOCAL)
4✔
1091

1092
        # check htlc raw value
1093
        if not ignore_min_htlc_value:
4✔
1094
            if amount_msat <= 0:
4✔
UNCOV
1095
                raise PaymentFailure("HTLC value must be positive")
×
1096
            if amount_msat < chan_config.htlc_minimum_msat:
4✔
UNCOV
1097
                raise PaymentFailure(f'HTLC value too small: {amount_msat} msat')
×
1098

1099
        # check proposer can afford htlc
1100
        max_can_send_msat = self.available_to_spend(htlc_proposer, strict=strict)
4✔
1101
        if max_can_send_msat < amount_msat:
4✔
1102
            raise PaymentFailure(f'Not enough balance. can send: {max_can_send_msat}, tried: {amount_msat}')
4✔
1103

1104
        # check "max_accepted_htlcs"
1105
        # this is the loose check BOLT-02 specifies:
1106
        if len(self.hm.htlcs_by_direction(htlc_receiver, direction=RECEIVED, ctn=ctn)) + 1 > chan_config.max_accepted_htlcs:
4✔
UNCOV
1107
            raise PaymentFailure('Too many HTLCs already in channel')
×
1108
        # however, c-lightning is a lot stricter, so extra checks:
1109
        # https://github.com/ElementsProject/lightning/blob/4dcd4ca1556b13b6964a10040ba1d5ef82de4788/channeld/full_channel.c#L581
1110
        if strict:
4✔
1111
            max_concurrent_htlcs = min(self.config[htlc_proposer].max_accepted_htlcs,
4✔
1112
                                       self.config[htlc_receiver].max_accepted_htlcs)
1113
            if len(self.hm.htlcs(htlc_receiver, ctn=ctn)) + 1 > max_concurrent_htlcs:
4✔
UNCOV
1114
                raise PaymentFailure('Too many HTLCs already in channel')
×
1115

1116
        # check "max_htlc_value_in_flight_msat"
1117
        current_htlc_sum = htlcsum(self.hm.htlcs_by_direction(htlc_receiver, direction=RECEIVED, ctn=ctn).values())
4✔
1118
        if current_htlc_sum + amount_msat > chan_config.max_htlc_value_in_flight_msat:
4✔
UNCOV
1119
            raise PaymentFailure(f'HTLC value sum (sum of pending htlcs: {current_htlc_sum/1000} sat '
×
1120
                                 f'plus new htlc: {amount_msat/1000} sat) '
1121
                                 f'would exceed max allowed: {chan_config.max_htlc_value_in_flight_msat/1000} sat')
1122

1123
    def can_pay(self, amount_msat: int, *, check_frozen=False) -> bool:
4✔
1124
        """Returns whether we can add an HTLC of given value."""
1125
        if check_frozen and self.is_frozen_for_sending():
4✔
UNCOV
1126
            return False
×
1127
        try:
4✔
1128
            self._assert_can_add_htlc(htlc_proposer=LOCAL, amount_msat=amount_msat)
4✔
1129
        except PaymentFailure:
4✔
1130
            return False
4✔
1131
        return True
4✔
1132

1133
    def can_receive(self, amount_msat: int, *, check_frozen=False,
4✔
1134
                    ignore_min_htlc_value: bool = False) -> bool:
1135
        """Returns whether the remote can add an HTLC of given value."""
UNCOV
1136
        if check_frozen and self.is_frozen_for_receiving():
×
UNCOV
1137
            return False
×
UNCOV
1138
        try:
×
1139
            self._assert_can_add_htlc(htlc_proposer=REMOTE,
×
1140
                                      amount_msat=amount_msat,
1141
                                      ignore_min_htlc_value=ignore_min_htlc_value)
1142
        except PaymentFailure:
×
UNCOV
1143
            return False
×
UNCOV
1144
        return True
×
1145

1146
    def should_try_to_reestablish_peer(self) -> bool:
4✔
1147
        if self.peer_state != PeerState.DISCONNECTED:
×
UNCOV
1148
            return False
×
UNCOV
1149
        if self.should_request_force_close:
×
1150
            return True
×
1151
        return ChannelState.PREOPENING < self._state < ChannelState.CLOSING
×
1152

1153
    def get_funding_address(self):
4✔
1154
        script = funding_output_script(self.config[LOCAL], self.config[REMOTE])
4✔
1155
        return redeem_script_to_address('p2wsh', script)
4✔
1156

1157
    def add_htlc(self, htlc: UpdateAddHtlc) -> UpdateAddHtlc:
4✔
1158
        """Adds a new LOCAL HTLC to the channel.
1159
        Action must be initiated by LOCAL.
1160
        """
1161
        if isinstance(htlc, dict):  # legacy conversion  # FIXME remove
4✔
1162
            htlc = UpdateAddHtlc(**htlc)
4✔
1163
        assert isinstance(htlc, UpdateAddHtlc)
4✔
1164
        self._assert_can_add_htlc(htlc_proposer=LOCAL, amount_msat=htlc.amount_msat)
4✔
1165
        if htlc.htlc_id is None:
4✔
1166
            htlc = attr.evolve(htlc, htlc_id=self.hm.get_next_htlc_id(LOCAL))
4✔
1167
        with self.db_lock:
4✔
1168
            self.hm.send_htlc(htlc)
4✔
1169
        self.logger.info("add_htlc")
4✔
1170
        return htlc
4✔
1171

1172
    def receive_htlc(self, htlc: UpdateAddHtlc, onion_packet:bytes = None) -> UpdateAddHtlc:
4✔
1173
        """Adds a new REMOTE HTLC to the channel.
1174
        Action must be initiated by REMOTE.
1175
        """
1176
        if isinstance(htlc, dict):  # legacy conversion  # FIXME remove
4✔
1177
            htlc = UpdateAddHtlc(**htlc)
4✔
1178
        assert isinstance(htlc, UpdateAddHtlc)
4✔
1179
        try:
4✔
1180
            self._assert_can_add_htlc(htlc_proposer=REMOTE, amount_msat=htlc.amount_msat)
4✔
UNCOV
1181
        except PaymentFailure as e:
×
UNCOV
1182
            raise RemoteMisbehaving(e) from e
×
1183
        if htlc.htlc_id is None:  # used in unit tests
4✔
1184
            htlc = attr.evolve(htlc, htlc_id=self.hm.get_next_htlc_id(REMOTE))
4✔
1185
        with self.db_lock:
4✔
1186
            self.hm.recv_htlc(htlc)
4✔
1187
            if onion_packet:
4✔
1188
                self.unfulfilled_htlcs[htlc.htlc_id] = onion_packet.hex(), None
4✔
1189

1190
        self.logger.info("receive_htlc")
4✔
1191
        return htlc
4✔
1192

1193
    def sign_next_commitment(self) -> Tuple[bytes, Sequence[bytes]]:
4✔
1194
        """Returns signatures for our next remote commitment tx.
1195
        Action must be initiated by LOCAL.
1196
        Finally, the next remote ctx becomes the latest remote ctx.
1197
        """
1198
        # TODO: when more channel types are supported, this method should depend on channel type
1199
        next_remote_ctn = self.get_next_ctn(REMOTE)
4✔
1200
        self.logger.info(f"sign_next_commitment. ctn={next_remote_ctn}")
4✔
1201

1202
        pending_remote_commitment = self.get_next_commitment(REMOTE)
4✔
1203
        sig_64 = sign_and_get_sig_string(pending_remote_commitment, self.config[LOCAL], self.config[REMOTE])
4✔
1204
        self.logger.debug(f"sign_next_commitment. {pending_remote_commitment.serialize()=}. {sig_64.hex()=}")
4✔
1205

1206
        their_remote_htlc_privkey_number = derive_privkey(
4✔
1207
            int.from_bytes(self.config[LOCAL].htlc_basepoint.privkey, 'big'),
1208
            self.config[REMOTE].next_per_commitment_point)
1209
        their_remote_htlc_privkey = their_remote_htlc_privkey_number.to_bytes(32, 'big')
4✔
1210

1211
        htlcsigs = []
4✔
1212
        htlc_to_ctx_output_idx_map = map_htlcs_to_ctx_output_idxs(chan=self,
4✔
1213
                                                                  ctx=pending_remote_commitment,
1214
                                                                  pcp=self.config[REMOTE].next_per_commitment_point,
1215
                                                                  subject=REMOTE,
1216
                                                                  ctn=next_remote_ctn)
1217
        for (direction, htlc), (ctx_output_idx, htlc_relative_idx) in htlc_to_ctx_output_idx_map.items():
4✔
1218
            _script, htlc_tx = make_htlc_tx_with_open_channel(chan=self,
4✔
1219
                                                              pcp=self.config[REMOTE].next_per_commitment_point,
1220
                                                              subject=REMOTE,
1221
                                                              ctn=next_remote_ctn,
1222
                                                              htlc_direction=direction,
1223
                                                              commit=pending_remote_commitment,
1224
                                                              ctx_output_idx=ctx_output_idx,
1225
                                                              htlc=htlc)
1226
            if self.has_anchors():
4✔
1227
                # we send a signature with the following sighash flags
1228
                # for the peer to be able to replace inputs and outputs
1229
                htlc_tx.inputs()[0].sighash = Sighash.ANYONECANPAY | Sighash.SINGLE
4✔
1230
            sig = htlc_tx.sign_txin(0, their_remote_htlc_privkey)
4✔
1231
            htlc_sig = ecc.ecdsa_sig64_from_der_sig(sig[:-1])
4✔
1232
            htlcsigs.append((ctx_output_idx, htlc_sig))
4✔
1233
        htlcsigs.sort()
4✔
1234
        htlcsigs = [x[1] for x in htlcsigs]
4✔
1235
        with self.db_lock:
4✔
1236
            self.hm.send_ctx()
4✔
1237
        return sig_64, htlcsigs
4✔
1238

1239
    def receive_new_commitment(self, sig: bytes, htlc_sigs: Sequence[bytes]) -> None:
4✔
1240
        """Processes signatures for our next local commitment tx, sent by the REMOTE.
1241
        Action must be initiated by REMOTE.
1242
        If all checks pass, the next local ctx becomes the latest local ctx.
1243
        """
1244
        # TODO in many failure cases below, we should "fail" the channel (force-close)
1245
        # TODO: when more channel types are supported, this method should depend on channel type
1246
        next_local_ctn = self.get_next_ctn(LOCAL)
4✔
1247
        self.logger.info(f"receive_new_commitment. ctn={next_local_ctn}, len(htlc_sigs)={len(htlc_sigs)}")
4✔
1248

1249
        assert len(htlc_sigs) == 0 or type(htlc_sigs[0]) is bytes
4✔
1250

1251
        pending_local_commitment = self.get_next_commitment(LOCAL)
4✔
1252
        pre_hash = pending_local_commitment.serialize_preimage(0)
4✔
1253
        msg_hash = sha256d(pre_hash)
4✔
1254
        if not ECPubkey(self.config[REMOTE].multisig_key.pubkey).ecdsa_verify(sig, msg_hash):
4✔
UNCOV
1255
            raise LNProtocolWarning(
×
1256
                f'failed verifying signature for our updated commitment transaction. '
1257
                f'sig={sig.hex()}. '
1258
                f'msg_hash={msg_hash.hex()}. '
1259
                f'pubkey={self.config[REMOTE].multisig_key.pubkey}. '
1260
                f'ctx={pending_local_commitment.serialize()} '
1261
            )
1262

1263
        htlc_sigs_string = b''.join(htlc_sigs)
4✔
1264

1265
        _secret, pcp = self.get_secret_and_point(subject=LOCAL, ctn=next_local_ctn)
4✔
1266

1267
        htlc_to_ctx_output_idx_map = map_htlcs_to_ctx_output_idxs(chan=self,
4✔
1268
                                                                  ctx=pending_local_commitment,
1269
                                                                  pcp=pcp,
1270
                                                                  subject=LOCAL,
1271
                                                                  ctn=next_local_ctn)
1272
        if len(htlc_to_ctx_output_idx_map) != len(htlc_sigs):
4✔
UNCOV
1273
            raise LNProtocolWarning(f'htlc sigs failure. recv {len(htlc_sigs)} sigs, expected {len(htlc_to_ctx_output_idx_map)}')
×
1274
        for (direction, htlc), (ctx_output_idx, htlc_relative_idx) in htlc_to_ctx_output_idx_map.items():
4✔
1275
            htlc_sig = htlc_sigs[htlc_relative_idx]
4✔
1276
            self._verify_htlc_sig(htlc=htlc,
4✔
1277
                                  htlc_sig=htlc_sig,
1278
                                  htlc_direction=direction,
1279
                                  pcp=pcp,
1280
                                  ctx=pending_local_commitment,
1281
                                  ctx_output_idx=ctx_output_idx,
1282
                                  ctn=next_local_ctn)
1283
        with self.db_lock:
4✔
1284
            self.hm.recv_ctx()
4✔
1285
            self.config[LOCAL].current_commitment_signature=sig
4✔
1286
            self.config[LOCAL].current_htlc_signatures=htlc_sigs_string
4✔
1287

1288
    def _verify_htlc_sig(self, *, htlc: UpdateAddHtlc, htlc_sig: bytes, htlc_direction: Direction,
4✔
1289
                         pcp: bytes, ctx: Transaction, ctx_output_idx: int, ctn: int) -> None:
1290
        _script, htlc_tx = make_htlc_tx_with_open_channel(chan=self,
4✔
1291
                                                          pcp=pcp,
1292
                                                          subject=LOCAL,
1293
                                                          ctn=ctn,
1294
                                                          htlc_direction=htlc_direction,
1295
                                                          commit=ctx,
1296
                                                          ctx_output_idx=ctx_output_idx,
1297
                                                          htlc=htlc)
1298
        if self.has_anchors():
4✔
1299
            # peer sent us a signature for our ctx using anchor sighash flags
1300
            htlc_tx.inputs()[0].sighash = Sighash.ANYONECANPAY | Sighash.SINGLE
4✔
1301
        pre_hash = htlc_tx.serialize_preimage(0)
4✔
1302
        msg_hash = sha256d(pre_hash)
4✔
1303
        remote_htlc_pubkey = derive_pubkey(self.config[REMOTE].htlc_basepoint.pubkey, pcp)
4✔
1304
        if not ECPubkey(remote_htlc_pubkey).ecdsa_verify(htlc_sig, msg_hash):
4✔
UNCOV
1305
            raise LNProtocolWarning(
×
1306
                f'failed verifying HTLC signatures: {htlc=}, {htlc_direction=}. '
1307
                f'htlc_tx={htlc_tx.serialize()}. '
1308
                f'htlc_sig={htlc_sig.hex()}. '
1309
                f'remote_htlc_pubkey={remote_htlc_pubkey.hex()}. '
1310
                f'msg_hash={msg_hash.hex()}. '
1311
                f'ctx={ctx.serialize()}. '
1312
                f'ctx_output_idx={ctx_output_idx}. '
1313
                f'ctn={ctn}. '
1314
            )
1315

1316
    def get_remote_htlc_sig_for_htlc(self, *, htlc_relative_idx: int) -> bytes:
4✔
UNCOV
1317
        data = self.config[LOCAL].current_htlc_signatures
×
UNCOV
1318
        htlc_sigs = list(chunks(data, 64))
×
UNCOV
1319
        htlc_sig = htlc_sigs[htlc_relative_idx]
×
1320
        remote_sighash = Sighash.ALL if not self.has_anchors() else Sighash.ANYONECANPAY | Sighash.SINGLE
×
1321
        remote_htlc_sig = ecc.ecdsa_der_sig_from_ecdsa_sig64(htlc_sig) + Sighash.to_sigbytes(remote_sighash)
×
1322
        return remote_htlc_sig
×
1323

1324
    def revoke_current_commitment(self):
4✔
1325
        self.logger.info("revoke_current_commitment")
4✔
1326
        new_ctn = self.get_latest_ctn(LOCAL)
4✔
1327
        new_ctx = self.get_latest_commitment(LOCAL)
4✔
1328
        if not self.signature_fits(new_ctx):
4✔
1329
            # this should never fail; as receive_new_commitment already did this test
UNCOV
1330
            raise Exception("refusing to revoke as remote sig does not fit")
×
1331
        with self.db_lock:
4✔
1332
            self.hm.send_rev()
4✔
1333
        last_secret, last_point = self.get_secret_and_point(LOCAL, new_ctn - 1)
4✔
1334
        next_secret, next_point = self.get_secret_and_point(LOCAL, new_ctn + 1)
4✔
1335
        return RevokeAndAck(last_secret, next_point)
4✔
1336

1337
    def receive_revocation(self, revocation: RevokeAndAck):
4✔
1338
        self.logger.info("receive_revocation")
4✔
1339
        new_ctn = self.get_latest_ctn(REMOTE)
4✔
1340
        cur_point = self.config[REMOTE].current_per_commitment_point
4✔
1341
        derived_point = ecc.ECPrivkey(revocation.per_commitment_secret).get_public_key_bytes(compressed=True)
4✔
1342
        if cur_point != derived_point:
4✔
UNCOV
1343
            raise Exception('revoked secret not for current point')
×
1344
        with self.db_lock:
4✔
1345
            self.revocation_store.add_next_entry(revocation.per_commitment_secret)
4✔
1346
            ##### start applying fee/htlc changes
1347
            self.hm.recv_rev()
4✔
1348
            self.config[REMOTE].current_per_commitment_point=self.config[REMOTE].next_per_commitment_point
4✔
1349
            self.config[REMOTE].next_per_commitment_point=revocation.next_per_commitment_point
4✔
1350
        assert new_ctn == self.get_oldest_unrevoked_ctn(REMOTE)
4✔
1351
        # lnworker callbacks
1352
        if self.lnworker:
4✔
1353
            sent = self.hm.sent_in_ctn(new_ctn)
4✔
1354
            for htlc in sent:
4✔
1355
                self.lnworker.htlc_fulfilled(self, htlc.payment_hash, htlc.htlc_id)
4✔
1356
            failed = self.hm.failed_in_ctn(new_ctn)
4✔
1357
            for htlc in failed:
4✔
1358
                try:
4✔
1359
                    error_bytes, failure_message = self._receive_fail_reasons.pop(htlc.htlc_id)
4✔
UNCOV
1360
                except KeyError:
×
UNCOV
1361
                    error_bytes, failure_message = None, None
×
1362
                self.lnworker.htlc_failed(self, htlc.payment_hash, htlc.htlc_id, error_bytes, failure_message)
4✔
1363

1364
    def extract_preimage_from_htlc_txin(self, txin: TxInput, *, is_deeply_mined: bool) -> None:
4✔
UNCOV
1365
        from . import lnutil
×
UNCOV
1366
        from .crypto import ripemd
×
UNCOV
1367
        from .transaction import match_script_against_template, script_GetOp
×
1368
        from .lnonion import OnionRoutingFailure, OnionFailureCode
×
1369
        witness = txin.witness_elements()
×
1370
        witness_script = witness[-1]
×
1371
        script_ops = [x for x in script_GetOp(witness_script)]
×
1372
        if match_script_against_template(witness_script, lnutil.WITNESS_TEMPLATE_OFFERED_HTLC, debug=False) \
×
1373
           or match_script_against_template(witness_script, lnutil.WITNESS_TEMPLATE_OFFERED_HTLC_ANCHORS, debug=False):
1374
            ripemd_payment_hash = script_ops[21][1]
×
1375
        elif match_script_against_template(witness_script, lnutil.WITNESS_TEMPLATE_RECEIVED_HTLC, debug=False) \
×
1376
           or match_script_against_template(witness_script, lnutil.WITNESS_TEMPLATE_RECEIVED_HTLC_ANCHORS, debug=False):
1377
            ripemd_payment_hash = script_ops[14][1]
×
1378
        else:
UNCOV
1379
            return
×
1380
        found = {}
×
UNCOV
1381
        for direction, htlc in itertools.chain(
×
1382
                self.hm.get_htlcs_in_oldest_unrevoked_ctx(REMOTE),
1383
                self.hm.get_htlcs_in_latest_ctx(REMOTE)):
1384
            if ripemd(htlc.payment_hash) == ripemd_payment_hash:
×
UNCOV
1385
                is_sent = direction == RECEIVED
×
UNCOV
1386
                found[htlc.htlc_id] = (htlc, is_sent)
×
1387
        for direction, htlc in itertools.chain(
×
1388
                self.hm.get_htlcs_in_oldest_unrevoked_ctx(LOCAL),
1389
                self.hm.get_htlcs_in_latest_ctx(LOCAL)):
1390
            if ripemd(htlc.payment_hash) == ripemd_payment_hash:
×
UNCOV
1391
                is_sent = direction == SENT
×
UNCOV
1392
                found[htlc.htlc_id] = (htlc, is_sent)
×
1393
        if not found:
×
1394
            return
×
1395
        if len(witness) == 5:    # HTLC success tx
×
1396
            preimage = witness[3]
×
1397
        elif len(witness) == 3:  # spending offered HTLC directly from ctx
×
1398
            preimage = witness[1]
×
1399
        else:
1400
            preimage = None      # HTLC timeout tx
×
1401
        if preimage:
×
UNCOV
1402
            assert ripemd(sha256(preimage)) == ripemd_payment_hash
×
1403
            payment_hash = sha256(preimage)
×
1404
            if self.lnworker.get_preimage(payment_hash) is not None:
×
1405
                return
×
1406
            # ^ note: log message text grepped for in regtests
1407
            self.logger.info(f"found preimage in witness of length {len(witness)}, for {payment_hash.hex()}")
×
1408

1409
        # Mark the htlc as fulfilled or failed.
1410
        # If we forwarded this, this ensures that the success/failure is propagated back on the incoming channel.
1411
        # FIXME we only look at outgoing htlcs that have a corresponding output in the commitment tx,
1412
        #       however we should also look at those that do not. E.g. a small value htlc might not create an output
1413
        #       but we should still propagate back success or failure on the incoming link. And it is not just about
1414
        #       small value htlcs: even a large htlc might not appear in the outgoing channel's ctx, e.g. maybe it was
1415
        #       not committed yet - we should still make sure it gets removed on the incoming channel. (see #9631)
UNCOV
1416
        if preimage:
×
UNCOV
1417
            self.lnworker.save_preimage(payment_hash, preimage)
×
UNCOV
1418
            for htlc, is_sent in found.values():
×
1419
                if is_sent:
×
1420
                    self.lnworker.htlc_fulfilled(self, payment_hash, htlc.htlc_id)
×
1421
        else:
1422
            # htlc timeout tx
1423
            if not is_deeply_mined:
×
UNCOV
1424
                return
×
UNCOV
1425
            failure = OnionRoutingFailure(code=OnionFailureCode.PERMANENT_CHANNEL_FAILURE, data=b'')
×
1426
            for htlc, is_sent in found.values():
×
1427
                if is_sent:
×
1428
                    self.logger.info(f'htlc timeout tx: failing htlc {is_sent}')
×
1429
                    self.lnworker.htlc_failed(
×
1430
                        self,
1431
                        payment_hash=htlc.payment_hash,
1432
                        htlc_id=htlc.htlc_id,
1433
                        error_bytes=None,
1434
                        failure_message=failure)
1435

1436
    def balance(self, whose: HTLCOwner, *, ctx_owner=HTLCOwner.LOCAL, ctn: int = None) -> int:
4✔
1437
        assert type(whose) is HTLCOwner
4✔
1438
        initial = self.config[whose].initial_msat
4✔
1439
        return self.hm.get_balance_msat(whose=whose,
4✔
1440
                                        ctx_owner=ctx_owner,
1441
                                        ctn=ctn,
1442
                                        initial_balance_msat=initial)
1443

1444
    def balance_minus_outgoing_htlcs(self, whose: HTLCOwner, *, ctx_owner: HTLCOwner = HTLCOwner.LOCAL,
4✔
1445
                                     ctn: int = None) -> int:
1446
        assert type(whose) is HTLCOwner
4✔
1447
        if ctn is None:
4✔
1448
            ctn = self.get_next_ctn(ctx_owner)
4✔
1449
        committed_balance = self.balance(whose, ctx_owner=ctx_owner, ctn=ctn)
4✔
1450
        direction = RECEIVED if whose != ctx_owner else SENT
4✔
1451
        balance_in_htlcs = self.balance_tied_up_in_htlcs_by_direction(ctx_owner, ctn=ctn, direction=direction)
4✔
1452
        return committed_balance - balance_in_htlcs
4✔
1453

1454
    def balance_tied_up_in_htlcs_by_direction(self, ctx_owner: HTLCOwner = LOCAL, *, ctn: int = None,
4✔
1455
                                              direction: Direction):
1456
        # in msat
1457
        if ctn is None:
4✔
UNCOV
1458
            ctn = self.get_next_ctn(ctx_owner)
×
1459
        return htlcsum(self.hm.htlcs_by_direction(ctx_owner, direction, ctn).values())
4✔
1460

1461
    def has_unsettled_htlcs(self) -> bool:
4✔
1462
        return len(self.hm.htlcs(LOCAL)) + len(self.hm.htlcs(REMOTE)) > 0
4✔
1463

1464
    def available_to_spend(self, subject: HTLCOwner, *, strict: bool = True) -> int:
4✔
1465
        """The usable balance of 'subject' in msat, after taking reserve and fees (and anchors) into
1466
        consideration. Note that fees (and hence the result) fluctuate even without user interaction.
1467
        """
1468
        assert type(subject) is HTLCOwner
4✔
1469
        sender = subject
4✔
1470
        receiver = subject.inverted()
4✔
1471
        initiator = LOCAL if self.constraints.is_initiator else REMOTE  # the initiator/funder pays on-chain fees
4✔
1472

1473
        def consider_ctx(*, ctx_owner: HTLCOwner, is_htlc_dust: bool) -> int:
4✔
1474
            ctn = self.get_next_ctn(ctx_owner)
4✔
1475
            sender_balance_msat = self.balance_minus_outgoing_htlcs(whose=sender, ctx_owner=ctx_owner, ctn=ctn)
4✔
1476
            receiver_balance_msat = self.balance_minus_outgoing_htlcs(whose=receiver, ctx_owner=ctx_owner, ctn=ctn)
4✔
1477
            sender_reserve_msat = self.config[receiver].reserve_sat * 1000
4✔
1478
            receiver_reserve_msat = self.config[sender].reserve_sat * 1000
4✔
1479
            num_htlcs_in_ctx = len(self.included_htlcs(ctx_owner, SENT, ctn=ctn) + self.included_htlcs(ctx_owner, RECEIVED, ctn=ctn))
4✔
1480
            feerate = self.get_feerate(ctx_owner, ctn=ctn)
4✔
1481
            ctx_fees_msat = calc_fees_for_commitment_tx(
4✔
1482
                num_htlcs=num_htlcs_in_ctx,
1483
                feerate=feerate,
1484
                is_local_initiator=self.constraints.is_initiator,
1485
                round_to_sat=False,
1486
                has_anchors=self.has_anchors()
1487
            )
1488
            htlc_fee_msat = fee_for_htlc_output(feerate=feerate)
4✔
1489
            htlc_trim_func = received_htlc_trim_threshold_sat if ctx_owner == receiver else offered_htlc_trim_threshold_sat
4✔
1490
            htlc_trim_threshold_msat = htlc_trim_func(dust_limit_sat=self.config[ctx_owner].dust_limit_sat, feerate=feerate, has_anchors=self.has_anchors()) * 1000
4✔
1491

1492
            # the sender cannot spend below its reserve
1493
            max_send_msat = sender_balance_msat - sender_reserve_msat
4✔
1494

1495
            # reserve a fee spike buffer
1496
            # see https://github.com/lightningnetwork/lightning-rfc/pull/740
1497
            if sender == initiator == LOCAL:
4✔
1498
                fee_spike_buffer = calc_fees_for_commitment_tx(
4✔
1499
                    num_htlcs=num_htlcs_in_ctx + int(not is_htlc_dust) + 1,
1500
                    feerate=2 * feerate,
1501
                    is_local_initiator=self.constraints.is_initiator,
1502
                    round_to_sat=False,
1503
                    has_anchors=self.has_anchors())[sender]
1504
                max_send_msat -= fee_spike_buffer
4✔
1505
            # we can't enforce the fee spike buffer on the remote party
1506
            elif sender == initiator == REMOTE:
4✔
1507
                max_send_msat -= ctx_fees_msat[sender]
4✔
1508

1509
            # initiator pays for anchor outputs
1510
            if sender == initiator and self.has_anchors():
4✔
1511
                max_send_msat -= 2 * FIXED_ANCHOR_SAT * 1000
4✔
1512

1513
            # handle the transaction fees for the HTLC transaction
1514
            if is_htlc_dust:
4✔
1515
                # nobody pays additional HTLC transaction fees
1516
                return min(max_send_msat, htlc_trim_threshold_msat - 1)
4✔
1517
            else:
1518
                # somebody has to pay for the additonal HTLC transaction fees
1519
                if sender == initiator:
4✔
1520
                    return max_send_msat - htlc_fee_msat
4✔
1521
                else:
1522
                    # check if the receiver can afford to pay for the HTLC transaction fees
1523
                    new_receiver_balance = receiver_balance_msat - receiver_reserve_msat - ctx_fees_msat[receiver] - htlc_fee_msat
4✔
1524
                    if self.has_anchors():
4✔
1525
                        new_receiver_balance -= 2 * FIXED_ANCHOR_SAT * 1000
4✔
1526
                    if new_receiver_balance < 0:
4✔
1527
                        return 0
4✔
1528
                    return max_send_msat
4✔
1529

1530
        max_send_msat = min(
4✔
1531
                            max(
1532
                                consider_ctx(ctx_owner=receiver, is_htlc_dust=True),
1533
                                consider_ctx(ctx_owner=receiver, is_htlc_dust=False),
1534
                            ),
1535
                            max(
1536
                                consider_ctx(ctx_owner=sender, is_htlc_dust=True),
1537
                                consider_ctx(ctx_owner=sender, is_htlc_dust=False),
1538
                            ),
1539
        )
1540
        max_send_msat = max(max_send_msat, 0)
4✔
1541
        return max_send_msat
4✔
1542

1543

1544
    def included_htlcs(self, subject: HTLCOwner, direction: Direction, ctn: int = None, *,
4✔
1545
                       feerate: int = None) -> List[UpdateAddHtlc]:
1546
        """Returns list of non-dust HTLCs for subject's commitment tx at ctn,
1547
        filtered by direction (of HTLCs).
1548
        """
1549
        assert type(subject) is HTLCOwner
4✔
1550
        assert type(direction) is Direction
4✔
1551
        if ctn is None:
4✔
UNCOV
1552
            ctn = self.get_oldest_unrevoked_ctn(subject)
×
1553
        if feerate is None:
4✔
1554
            feerate = self.get_feerate(subject, ctn=ctn)
4✔
1555
        conf = self.config[subject]
4✔
1556
        if direction == RECEIVED:
4✔
1557
            threshold_sat = received_htlc_trim_threshold_sat(dust_limit_sat=conf.dust_limit_sat, feerate=feerate, has_anchors=self.has_anchors())
4✔
1558
        else:
1559
            threshold_sat = offered_htlc_trim_threshold_sat(dust_limit_sat=conf.dust_limit_sat, feerate=feerate, has_anchors=self.has_anchors())
4✔
1560
        htlcs = self.hm.htlcs_by_direction(subject, direction, ctn=ctn).values()
4✔
1561
        return list(filter(lambda htlc: htlc.amount_msat // 1000 >= threshold_sat, htlcs))
4✔
1562

1563
    def get_secret_and_point(self, subject: HTLCOwner, ctn: int) -> Tuple[Optional[bytes], bytes]:
4✔
1564
        assert type(subject) is HTLCOwner
4✔
1565
        assert ctn >= 0, ctn
4✔
1566
        offset = ctn - self.get_oldest_unrevoked_ctn(subject)
4✔
1567
        if subject == REMOTE:
4✔
1568
            if offset > 1:
4✔
UNCOV
1569
                raise RemoteCtnTooFarInFuture(f"offset: {offset}")
×
1570
            conf = self.config[REMOTE]
4✔
1571
            if offset == 1:
4✔
1572
                secret = None
4✔
1573
                point = conf.next_per_commitment_point
4✔
1574
            elif offset == 0:
4✔
1575
                secret = None
4✔
1576
                point = conf.current_per_commitment_point
4✔
1577
            else:
UNCOV
1578
                secret = self.revocation_store.retrieve_secret(RevocationStore.START_INDEX - ctn)
×
UNCOV
1579
                point = secret_to_pubkey(int.from_bytes(secret, 'big'))
×
1580
        else:
1581
            secret = get_per_commitment_secret_from_seed(self.config[LOCAL].per_commitment_secret_seed, RevocationStore.START_INDEX - ctn)
4✔
1582
            point = secret_to_pubkey(int.from_bytes(secret, 'big'))
4✔
1583
        return secret, point
4✔
1584

1585
    def get_secret_and_commitment(self, subject: HTLCOwner, *, ctn: int) -> Tuple[Optional[bytes], PartialTransaction]:
4✔
1586
        secret, point = self.get_secret_and_point(subject, ctn)
4✔
1587
        ctx = self.make_commitment(subject, point, ctn)
4✔
1588
        return secret, ctx
4✔
1589

1590
    def get_commitment(self, subject: HTLCOwner, *, ctn: int) -> PartialTransaction:
4✔
1591
        secret, ctx = self.get_secret_and_commitment(subject, ctn=ctn)
4✔
1592
        return ctx
4✔
1593

1594
    def get_next_commitment(self, subject: HTLCOwner) -> PartialTransaction:
4✔
1595
        ctn = self.get_next_ctn(subject)
4✔
1596
        return self.get_commitment(subject, ctn=ctn)
4✔
1597

1598
    def get_latest_commitment(self, subject: HTLCOwner) -> PartialTransaction:
4✔
1599
        ctn = self.get_latest_ctn(subject)
4✔
1600
        return self.get_commitment(subject, ctn=ctn)
4✔
1601

1602
    def get_oldest_unrevoked_commitment(self, subject: HTLCOwner) -> PartialTransaction:
4✔
1603
        ctn = self.get_oldest_unrevoked_ctn(subject)
4✔
1604
        return self.get_commitment(subject, ctn=ctn)
4✔
1605

1606
    def create_sweeptxs_for_watchtower(self, ctn: int) -> List[Transaction]:
4✔
UNCOV
1607
        from .lnsweep import sweep_their_ctx_watchtower
×
UNCOV
1608
        from .fee_policy import FeePolicy
×
UNCOV
1609
        from .transaction import PartialTxOutput, PartialTransaction
×
1610
        secret, ctx = self.get_secret_and_commitment(REMOTE, ctn=ctn)
×
1611
        txs = []
×
1612
        txins = sweep_their_ctx_watchtower(self, ctx, secret)
×
1613
        fee_policy = FeePolicy('eta:2')
×
1614
        for txin in txins:
×
1615
            output_idx = txin.prevout.out_idx
×
1616
            value = ctx.outputs()[output_idx].value
×
1617
            tx_size_bytes = 121
×
1618
            fee = fee_policy.estimate_fee(tx_size_bytes, network=self.lnworker.network, allow_fallback_to_static_rates=True)
×
1619
            outvalue = value - fee
×
1620
            sweep_outputs = [PartialTxOutput.from_address_and_value(self.get_sweep_address(), outvalue)]
×
1621
            sweep_tx = PartialTransaction.from_io([txin], sweep_outputs, version=2)
×
1622
            sig = sweep_tx.sign_txin(0, txin.privkey)
×
1623
            txin.witness = txin.make_witness(sig)
×
1624
            txs.append(sweep_tx)
×
1625
        return txs
×
1626

1627
    def get_oldest_unrevoked_ctn(self, subject: HTLCOwner) -> int:
4✔
1628
        return self.hm.ctn_oldest_unrevoked(subject)
4✔
1629

1630
    def get_latest_ctn(self, subject: HTLCOwner) -> int:
4✔
1631
        return self.hm.ctn_latest(subject)
4✔
1632

1633
    def get_next_ctn(self, subject: HTLCOwner) -> int:
4✔
1634
        return self.hm.ctn_latest(subject) + 1
4✔
1635

1636
    def total_msat(self, direction: Direction) -> int:
4✔
1637
        """Return the cumulative total msat amount received/sent so far."""
1638
        assert type(direction) is Direction
4✔
1639
        return htlcsum(self.hm.all_settled_htlcs_ever_by_direction(LOCAL, direction))
4✔
1640

1641
    def settle_htlc(self, preimage: bytes, htlc_id: int) -> None:
4✔
1642
        """Settle/fulfill a pending received HTLC.
1643
        Action must be initiated by LOCAL.
1644
        """
1645
        self.logger.info("settle_htlc")
4✔
1646
        assert self.can_send_ctx_updates(), f"cannot update channel. {self.get_state()!r} {self.peer_state!r}"
4✔
1647
        htlc = self.hm.get_htlc_by_id(REMOTE, htlc_id)
4✔
1648
        if htlc.payment_hash != sha256(preimage):
4✔
UNCOV
1649
            raise Exception("incorrect preimage for HTLC")
×
1650
        assert htlc_id not in self.hm.log[REMOTE]['settles']
4✔
1651
        self.hm.send_settle(htlc_id)
4✔
1652
        self.htlc_settle_time[htlc_id] = now()
4✔
1653

1654
    def get_payment_hash(self, htlc_id: int) -> bytes:
4✔
UNCOV
1655
        htlc = self.hm.get_htlc_by_id(LOCAL, htlc_id)
×
UNCOV
1656
        return htlc.payment_hash
×
1657

1658
    def receive_htlc_settle(self, preimage: bytes, htlc_id: int) -> None:
4✔
1659
        """Settle/fulfill a pending offered HTLC.
1660
        Action must be initiated by REMOTE.
1661
        """
1662
        self.logger.info("receive_htlc_settle")
4✔
1663
        htlc = self.hm.get_htlc_by_id(LOCAL, htlc_id)
4✔
1664
        if htlc.payment_hash != sha256(preimage):
4✔
UNCOV
1665
            raise RemoteMisbehaving("received incorrect preimage for HTLC")
×
1666
        assert htlc_id not in self.hm.log[LOCAL]['settles']
4✔
1667
        with self.db_lock:
4✔
1668
            self.hm.recv_settle(htlc_id)
4✔
1669

1670
    def fail_htlc(self, htlc_id: int) -> None:
4✔
1671
        """Fail a pending received HTLC.
1672
        Action must be initiated by LOCAL.
1673
        """
1674
        self.logger.info("fail_htlc")
4✔
1675
        assert self.can_send_ctx_updates(), f"cannot update channel. {self.get_state()!r} {self.peer_state!r}"
4✔
1676
        with self.db_lock:
4✔
1677
            self.hm.send_fail(htlc_id)
4✔
1678

1679
    def receive_fail_htlc(self, htlc_id: int, *,
4✔
1680
                          error_bytes: Optional[bytes],
1681
                          reason: Optional[OnionRoutingFailure] = None) -> None:
1682
        """Fail a pending offered HTLC.
1683
        Action must be initiated by REMOTE.
1684
        """
1685
        self.logger.info("receive_fail_htlc")
4✔
1686
        with self.db_lock:
4✔
1687
            self.hm.recv_fail(htlc_id)
4✔
1688
        self._receive_fail_reasons[htlc_id] = (error_bytes, reason)
4✔
1689

1690
    def get_next_fee(self, subject: HTLCOwner) -> int:
4✔
1691
        return self.constraints.capacity - sum(x.value for x in self.get_next_commitment(subject).outputs())
4✔
1692

1693
    def get_latest_fee(self, subject: HTLCOwner) -> int:
4✔
1694
        return self.constraints.capacity - sum(x.value for x in self.get_latest_commitment(subject).outputs())
4✔
1695

1696
    def update_fee(self, feerate: int, from_us: bool) -> None:
4✔
1697
        # feerate uses sat/kw
1698
        if self.constraints.is_initiator != from_us:
4✔
UNCOV
1699
            raise Exception(f"Cannot update_fee: wrong initiator. us: {from_us}")
×
1700
        if feerate < FEERATE_PER_KW_MIN_RELAY_LIGHTNING:
4✔
UNCOV
1701
            raise Exception(f"Cannot update_fee: feerate lower than min relay fee. {feerate} sat/kw. us: {from_us}")
×
1702
        sender = LOCAL if from_us else REMOTE
4✔
1703
        ctx_owner = -sender
4✔
1704
        ctn = self.get_next_ctn(ctx_owner)
4✔
1705
        sender_balance_msat = self.balance_minus_outgoing_htlcs(whose=sender, ctx_owner=ctx_owner, ctn=ctn)
4✔
1706
        sender_reserve_msat = self.config[-sender].reserve_sat * 1000
4✔
1707
        num_htlcs_in_ctx = len(self.included_htlcs(ctx_owner, SENT, ctn=ctn, feerate=feerate) +
4✔
1708
                               self.included_htlcs(ctx_owner, RECEIVED, ctn=ctn, feerate=feerate))
1709
        ctx_fees_msat = calc_fees_for_commitment_tx(
4✔
1710
            num_htlcs=num_htlcs_in_ctx,
1711
            feerate=feerate,
1712
            is_local_initiator=self.constraints.is_initiator,
1713
            has_anchors=self.has_anchors()
1714
        )
1715
        remainder = sender_balance_msat - sender_reserve_msat - ctx_fees_msat[sender]
4✔
1716
        if remainder < 0:
4✔
UNCOV
1717
            raise Exception(f"Cannot update_fee. {sender} tried to update fee but they cannot afford it. "
×
1718
                            f"Their balance would go below reserve: {remainder} msat missing.")
1719
        with self.db_lock:
4✔
1720
            if from_us:
4✔
1721
                assert self.can_send_ctx_updates(), f"cannot update channel. {self.get_state()!r} {self.peer_state!r}"
4✔
1722
                self.hm.send_update_fee(feerate)
4✔
1723
            else:
1724
                self.hm.recv_update_fee(feerate)
4✔
1725

1726
    def make_commitment(self, subject: HTLCOwner, this_point: bytes, ctn: int) -> PartialTransaction:
4✔
1727
        assert type(subject) is HTLCOwner
4✔
1728
        feerate = self.get_feerate(subject, ctn=ctn)
4✔
1729
        other = subject.inverted()
4✔
1730
        local_msat = self.balance(subject, ctx_owner=subject, ctn=ctn)
4✔
1731
        remote_msat = self.balance(other, ctx_owner=subject, ctn=ctn)
4✔
1732
        received_htlcs = self.hm.htlcs_by_direction(subject, RECEIVED, ctn).values()
4✔
1733
        sent_htlcs = self.hm.htlcs_by_direction(subject, SENT, ctn).values()
4✔
1734
        remote_msat -= htlcsum(received_htlcs)
4✔
1735
        local_msat -= htlcsum(sent_htlcs)
4✔
1736
        assert remote_msat >= 0
4✔
1737
        assert local_msat >= 0
4✔
1738
        # same htlcs as before, but now without dust.
1739
        received_htlcs = self.included_htlcs(subject, RECEIVED, ctn)
4✔
1740
        sent_htlcs = self.included_htlcs(subject, SENT, ctn)
4✔
1741

1742
        this_config = self.config[subject]
4✔
1743
        other_config = self.config[-subject]
4✔
1744
        other_htlc_pubkey = derive_pubkey(other_config.htlc_basepoint.pubkey, this_point)
4✔
1745
        this_htlc_pubkey = derive_pubkey(this_config.htlc_basepoint.pubkey, this_point)
4✔
1746
        other_revocation_pubkey = derive_blinded_pubkey(other_config.revocation_basepoint.pubkey, this_point)
4✔
1747
        htlcs = []  # type: List[ScriptHtlc]
4✔
1748
        for is_received_htlc, htlc_list in zip((True, False), (received_htlcs, sent_htlcs)):
4✔
1749
            for htlc in htlc_list:
4✔
1750
                htlcs.append(ScriptHtlc(make_htlc_output_witness_script(
4✔
1751
                    is_received_htlc=is_received_htlc,
1752
                    remote_revocation_pubkey=other_revocation_pubkey,
1753
                    remote_htlc_pubkey=other_htlc_pubkey,
1754
                    local_htlc_pubkey=this_htlc_pubkey,
1755
                    payment_hash=htlc.payment_hash,
1756
                    cltv_abs=htlc.cltv_abs,
1757
                    has_anchors=self.has_anchors()), htlc))
1758
        # note: maybe flip initiator here for fee purposes, we want LOCAL and REMOTE
1759
        #       in the resulting dict to correspond to the to_local and to_remote *outputs* of the ctx
1760
        onchain_fees = calc_fees_for_commitment_tx(
4✔
1761
            num_htlcs=len(htlcs),
1762
            feerate=feerate,
1763
            is_local_initiator=self.constraints.is_initiator == (subject == LOCAL),
1764
            has_anchors=self.has_anchors(),
1765
        )
1766
        assert self.is_static_remotekey_enabled()
4✔
1767
        payment_pubkey = other_config.payment_basepoint.pubkey
4✔
1768
        return make_commitment(
4✔
1769
            ctn=ctn,
1770
            local_funding_pubkey=this_config.multisig_key.pubkey,
1771
            remote_funding_pubkey=other_config.multisig_key.pubkey,
1772
            remote_payment_pubkey=payment_pubkey,
1773
            funder_payment_basepoint=self.config[LOCAL if     self.constraints.is_initiator else REMOTE].payment_basepoint.pubkey,
1774
            fundee_payment_basepoint=self.config[LOCAL if not self.constraints.is_initiator else REMOTE].payment_basepoint.pubkey,
1775
            revocation_pubkey=other_revocation_pubkey,
1776
            delayed_pubkey=derive_pubkey(this_config.delayed_basepoint.pubkey, this_point),
1777
            to_self_delay=other_config.to_self_delay,
1778
            funding_txid=self.funding_outpoint.txid,
1779
            funding_pos=self.funding_outpoint.output_index,
1780
            funding_sat=self.constraints.capacity,
1781
            local_amount=local_msat,
1782
            remote_amount=remote_msat,
1783
            dust_limit_sat=this_config.dust_limit_sat,
1784
            fees_per_participant=onchain_fees,
1785
            htlcs=htlcs,
1786
            has_anchors=self.has_anchors()
1787
        )
1788

1789
    def make_closing_tx(self, local_script: bytes, remote_script: bytes,
4✔
1790
                        fee_sat: int, *, drop_remote = False) -> Tuple[bytes, PartialTransaction]:
1791
        """ cooperative close """
1792
        _, outputs = make_commitment_outputs(
4✔
1793
            fees_per_participant={
1794
                LOCAL: fee_sat * 1000 if self.constraints.is_initiator else 0,
1795
                REMOTE: fee_sat * 1000 if not self.constraints.is_initiator else 0,
1796
            },
1797
            local_amount_msat=self.balance(LOCAL),
1798
            remote_amount_msat=self.balance(REMOTE) if not drop_remote else 0,
1799
            local_script=local_script,
1800
            remote_script=remote_script,
1801
            htlcs=[],
1802
            dust_limit_sat=self.config[LOCAL].dust_limit_sat,
1803
            has_anchors=self.has_anchors(),
1804
            local_anchor_script=None,
1805
            remote_anchor_script=None,
1806
        )
1807

1808
        closing_tx = make_closing_tx(self.config[LOCAL].multisig_key.pubkey,
4✔
1809
                                     self.config[REMOTE].multisig_key.pubkey,
1810
                                     funding_txid=self.funding_outpoint.txid,
1811
                                     funding_pos=self.funding_outpoint.output_index,
1812
                                     funding_sat=self.constraints.capacity,
1813
                                     outputs=outputs)
1814

1815
        der_sig = closing_tx.sign_txin(0, self.config[LOCAL].multisig_key.privkey)
4✔
1816
        sig = ecc.ecdsa_sig64_from_der_sig(der_sig[:-1])
4✔
1817
        return sig, closing_tx
4✔
1818

1819
    def signature_fits(self, tx: PartialTransaction) -> bool:
4✔
1820
        remote_sig = self.config[LOCAL].current_commitment_signature
4✔
1821
        pre_hash = tx.serialize_preimage(0)
4✔
1822
        msg_hash = sha256d(pre_hash)
4✔
1823
        assert remote_sig
4✔
1824
        res = ECPubkey(self.config[REMOTE].multisig_key.pubkey).ecdsa_verify(remote_sig, msg_hash)
4✔
1825
        return res
4✔
1826

1827
    def force_close_tx(self) -> PartialTransaction:
4✔
1828
        tx = self.get_latest_commitment(LOCAL)
4✔
1829
        assert self.signature_fits(tx)
4✔
1830
        tx.sign({self.config[LOCAL].multisig_key.pubkey: self.config[LOCAL].multisig_key.privkey})
4✔
1831
        remote_sig = self.config[LOCAL].current_commitment_signature
4✔
1832
        remote_sig = ecc.ecdsa_der_sig_from_ecdsa_sig64(remote_sig) + Sighash.to_sigbytes(Sighash.ALL)
4✔
1833
        tx.add_signature_to_txin(txin_idx=0,
4✔
1834
                                 signing_pubkey=self.config[REMOTE].multisig_key.pubkey,
1835
                                 sig=remote_sig)
1836
        assert tx.is_complete()
4✔
1837
        return tx
4✔
1838

1839
    def get_close_options(self) -> Sequence[ChanCloseOption]:
4✔
1840
        # This method is used both in the GUI, and in lnpeer.schedule_force_closing
1841
        # in the latter case, the result does not depend on peer_state
1842
        ret = []
4✔
1843
        if not self.is_closed() and self.peer_state == PeerState.GOOD:
4✔
1844
            # If there are unsettled HTLCs, although is possible to cooperatively close,
1845
            # we choose not to expose that option in the GUI, because it is very likely
1846
            # that HTLCs will take a long time to settle (submarine swap, or stuck payment),
1847
            # and the close dialog would be taking a very long time to finish
1848
            if not self.has_unsettled_htlcs():
4✔
1849
                ret.append(ChanCloseOption.COOP_CLOSE)
4✔
1850
                ret.append(ChanCloseOption.REQUEST_REMOTE_FCLOSE)
4✔
1851
        if self.get_state() == ChannelState.WE_ARE_TOXIC:
4✔
UNCOV
1852
            ret.append(ChanCloseOption.REQUEST_REMOTE_FCLOSE)
×
1853
        if not self.is_closed() or self.get_state() == ChannelState.REQUESTED_FCLOSE:
4✔
1854
            ret.append(ChanCloseOption.LOCAL_FCLOSE)
4✔
1855
        assert not (self.get_state() == ChannelState.WE_ARE_TOXIC and ChanCloseOption.LOCAL_FCLOSE in ret), "local force-close unsafe if we are toxic"
4✔
1856
        return ret
4✔
1857

1858
    def maybe_sweep_htlcs(self, ctx: Transaction, htlc_tx: Transaction) -> Dict[str, SweepInfo]:
4✔
1859
        # look at the output address, check if it matches
UNCOV
1860
        d = sweep_their_htlctx_justice(self, ctx, htlc_tx)
×
UNCOV
1861
        d2 = sweep_our_htlctx(self, ctx, htlc_tx)
×
UNCOV
1862
        d.update(d2)
×
1863
        return d
×
1864

1865
    def has_pending_changes(self, subject: HTLCOwner) -> bool:
4✔
1866
        next_htlcs = self.hm.get_htlcs_in_next_ctx(subject)
4✔
1867
        latest_htlcs = self.hm.get_htlcs_in_latest_ctx(subject)
4✔
1868
        return not (next_htlcs == latest_htlcs and self.get_next_feerate(subject) == self.get_latest_feerate(subject))
4✔
1869

1870
    def should_be_closed_due_to_expiring_htlcs(self, local_height: int) -> bool:
4✔
UNCOV
1871
        htlcs_we_could_reclaim = {}  # type: Dict[Tuple[Direction, int], UpdateAddHtlc]
×
1872
        # If there is a received HTLC for which we already released the preimage
1873
        # but the remote did not revoke yet, and the CLTV of this HTLC is dangerously close
1874
        # to the present, then unilaterally close channel
UNCOV
1875
        recv_htlc_deadline_delta = lnutil.NBLOCK_DEADLINE_DELTA_BEFORE_EXPIRY_FOR_RECEIVED_HTLCS
×
UNCOV
1876
        for sub, dir, ctn in ((LOCAL, RECEIVED, self.get_latest_ctn(LOCAL)),
×
1877
                              (REMOTE, SENT, self.get_oldest_unrevoked_ctn(REMOTE)),
1878
                              (REMOTE, SENT, self.get_latest_ctn(REMOTE)),):
1879
            for htlc_id, htlc in self.hm.htlcs_by_direction(subject=sub, direction=dir, ctn=ctn).items():
×
UNCOV
1880
                if not self.hm.was_htlc_preimage_released(htlc_id=htlc_id, htlc_proposer=REMOTE):
×
UNCOV
1881
                    continue
×
1882
                if htlc.cltv_abs - recv_htlc_deadline_delta > local_height:
×
1883
                    continue
×
1884
                # Do not force-close if we just sent fulfill_htlc and have not received revack yet
1885
                if htlc_id in self.htlc_settle_time and now() - self.htlc_settle_time[htlc_id] < 30:
×
1886
                    continue
×
UNCOV
1887
                htlcs_we_could_reclaim[(RECEIVED, htlc_id)] = htlc
×
1888
        # If there is an offered HTLC which has already expired (+ some grace period after), we
1889
        # will unilaterally close the channel and time out the HTLC
1890
        offered_htlc_deadline_delta = lnutil.NBLOCK_DEADLINE_DELTA_AFTER_EXPIRY_FOR_OFFERED_HTLCS
×
UNCOV
1891
        for sub, dir, ctn in ((LOCAL, SENT, self.get_latest_ctn(LOCAL)),
×
1892
                              (REMOTE, RECEIVED, self.get_oldest_unrevoked_ctn(REMOTE)),
1893
                              (REMOTE, RECEIVED, self.get_latest_ctn(REMOTE)),):
1894
            for htlc_id, htlc in self.hm.htlcs_by_direction(subject=sub, direction=dir, ctn=ctn).items():
×
UNCOV
1895
                if htlc.cltv_abs + offered_htlc_deadline_delta > local_height:
×
UNCOV
1896
                    continue
×
1897
                htlcs_we_could_reclaim[(SENT, htlc_id)] = htlc
×
1898
        # Note: previously we used a threshold concept, "min_value_worth_closing_channel_over_sat", and
1899
        #       only force-closed the channel if the total value of these expiring htlcs was large enough.
1900
        #       However, if we are forwarding, and an outgoing htlc expires, we should always close
1901
        #       the outgoing channel (regardless of htlc value), so that we can propagate back the
1902
        #       removal of the htlc in the incoming channel.
UNCOV
1903
        return len(htlcs_we_could_reclaim) > 0
×
1904

1905
    def is_funding_tx_mined(self, funding_height):
4✔
1906
        funding_txid = self.funding_outpoint.txid
×
UNCOV
1907
        funding_idx = self.funding_outpoint.output_index
×
UNCOV
1908
        conf = funding_height.conf
×
1909
        if conf < self.funding_txn_minimum_depth():
×
1910
            #self.logger.info(f"funding tx is still not at sufficient depth. actual depth: {conf}")
1911
            return False
×
1912
        assert conf > 0 or self.is_zeroconf()
×
1913
        # check funding_tx amount and script
1914
        funding_tx = self.lnworker.lnwatcher.adb.get_transaction(funding_txid)
×
1915
        if not funding_tx:
×
UNCOV
1916
            self.logger.info(f"no funding_tx {funding_txid}")
×
1917
            return False
×
1918
        outp = funding_tx.outputs()[funding_idx]
×
1919
        redeem_script = funding_output_script(self.config[REMOTE], self.config[LOCAL])
×
1920
        funding_address = redeem_script_to_address('p2wsh', redeem_script)
×
1921
        funding_sat = self.constraints.capacity
×
1922
        if not (outp.address == funding_address and outp.value == funding_sat):
×
1923
            self.logger.info('funding outpoint mismatch')
×
1924
            return False
×
1925
        return True
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc