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

spesmilo / electrum / 6606673856430080

15 Jul 2025 10:35PM UTC coverage: 59.8% (-0.003%) from 59.803%
6606673856430080

push

CirrusCI

SomberNight
verifier: fix off-by-one for max_checkpoint

if a wallet had a tx mined in the max_checkpoint block, in certain cases
we would leave it forever in the "unverified" state and remain stuck in "synchronizing..."

0 of 3 new or added lines in 2 files covered. (0.0%)

10 existing lines in 5 files now uncovered.

21985 of 36764 relevant lines covered (59.8%)

2.99 hits per line

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

65.71
/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
5✔
21
from collections import defaultdict
5✔
22
from enum import IntEnum, Enum
5✔
23
from typing import (
5✔
24
    Optional, Dict, List, Tuple, NamedTuple,
25
    Iterable, Sequence, TYPE_CHECKING, Iterator, Union, Mapping)
26
import time
5✔
27
import threading
5✔
28
from abc import ABC, abstractmethod
5✔
29
import itertools
5✔
30

31
from aiorpcx import NetAddress
5✔
32
import attr
5✔
33

34
import electrum_ecc as ecc
5✔
35
from electrum_ecc import ECPubkey
5✔
36

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

67
if TYPE_CHECKING:
5✔
68
    from .lnworker import LNWallet
×
69
    from .json_db import StoredDict
×
70

71

72
# channel flags
73
CF_ANNOUNCE_CHANNEL = 0x01
5✔
74

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

96

97
class PeerState(IntEnum):
5✔
98
    DISCONNECTED   = 0
5✔
99
    REESTABLISHING = 1
5✔
100
    GOOD           = 2
5✔
101
    BAD            = 3
5✔
102

103

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

154

155
class ChanCloseOption(Enum):
5✔
156
    COOP_CLOSE = enum.auto()
5✔
157
    LOCAL_FCLOSE = enum.auto()
5✔
158
    REQUEST_REMOTE_FCLOSE = enum.auto()
5✔
159

160

161
class RevokeAndAck(NamedTuple):
5✔
162
    per_commitment_secret: bytes
5✔
163
    next_per_commitment_point: bytes
5✔
164

165

166
class RemoteCtnTooFarInFuture(Exception): pass
5✔
167

168

169
def htlcsum(htlcs: Iterable[UpdateAddHtlc]):
5✔
170
    return sum([x.amount_msat for x in htlcs])
5✔
171

172
def now():
5✔
173
    return int(time.time())
5✔
174

175
class HTLCWithStatus(NamedTuple):
5✔
176
    channel_id: bytes
5✔
177
    htlc: UpdateAddHtlc
5✔
178
    direction: Direction
5✔
179
    status: str
5✔
180

181

182
class AbstractChannel(Logger, ABC):
5✔
183
    storage: Union['StoredDict', dict]
5✔
184
    config: Dict[HTLCOwner, Union[LocalConfig, RemoteConfig]]
5✔
185
    lnworker: Optional['LNWallet']
5✔
186
    channel_id: bytes
5✔
187
    short_channel_id: Optional[ShortChannelID] = None
5✔
188
    funding_outpoint: Outpoint
5✔
189
    node_id: bytes  # note that it might not be the full 33 bytes; for OCB it is only the prefix
5✔
190
    should_request_force_close: bool = False
5✔
191
    _state: ChannelState
5✔
192
    _who_closed: Optional[int] = None  # HTLCOwner (1 or -1).  0 means "unknown"
5✔
193

194
    def set_short_channel_id(self, short_id: ShortChannelID) -> None:
5✔
195
        self.short_channel_id = short_id
×
196
        self.storage["short_channel_id"] = short_id
×
197

198
    def get_id_for_log(self) -> str:
5✔
199
        scid = self.short_channel_id
5✔
200
        if scid:
5✔
201
            return str(scid)
5✔
202
        return self.channel_id.hex()
×
203

204
    def short_id_for_GUI(self) -> str:
5✔
205
        return format_short_channel_id(self.short_channel_id)
×
206

207
    def diagnostic_name(self):
5✔
208
        return self.get_id_for_log()
5✔
209

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

223
    def get_state(self) -> ChannelState:
5✔
224
        return self._state
5✔
225

226
    def is_funded(self) -> bool:
5✔
227
        return self.get_state() >= ChannelState.FUNDED
5✔
228

229
    def is_open(self) -> bool:
5✔
230
        return self.get_state() == ChannelState.OPEN
5✔
231

232
    def is_closed(self) -> bool:
5✔
233
        # the closing txid has been saved
234
        return self.get_state() >= ChannelState.CLOSING
5✔
235

236
    def is_closed_or_closing(self):
5✔
237
        # related: self.get_state_for_GUI
238
        return self.is_closed() or self.unconfirmed_closing_txid is not None
×
239

240
    def is_redeemed(self) -> bool:
5✔
241
        return self.get_state() == ChannelState.REDEEMED
5✔
242

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

266
    @abstractmethod
5✔
267
    def get_close_options(self) -> Sequence[ChanCloseOption]:
5✔
268
        pass
×
269

270
    def save_funding_height(self, *, txid: str, height: int, timestamp: Optional[int]) -> None:
5✔
271
        self.storage['funding_height'] = txid, height, timestamp
×
272

273
    def get_funding_height(self) -> Optional[Tuple[str, int, Optional[int]]]:
5✔
274
        return self.storage.get('funding_height')
5✔
275

276
    def delete_funding_height(self):
5✔
277
        self.storage.pop('funding_height', None)
×
278

279
    def save_closing_height(self, *, txid: str, height: int, timestamp: Optional[int]) -> None:
5✔
280
        self.storage['closing_height'] = txid, height, timestamp
×
281

282
    def get_closing_height(self) -> Optional[Tuple[str, int, Optional[int]]]:
5✔
283
        return self.storage.get('closing_height')
5✔
284

285
    def delete_closing_height(self):
5✔
286
        self.storage.pop('closing_height', None)
×
287

288
    def create_sweeptxs_for_our_ctx(self, ctx: Transaction) -> Dict[str, SweepInfo]:
5✔
289
        return sweep_our_ctx(chan=self, ctx=ctx)
×
290

291
    def create_sweeptxs_for_their_ctx(self, ctx: Transaction) -> Dict[str, SweepInfo]:
5✔
292
        return sweep_their_ctx(chan=self, ctx=ctx)
×
293

294
    def is_backup(self) -> bool:
5✔
295
        return False
×
296

297
    def get_local_scid_alias(self, *, create_new_if_needed: bool = False) -> Optional[bytes]:
5✔
298
        return None
×
299

300
    def get_remote_scid_alias(self) -> Optional[bytes]:
5✔
301
        return None
×
302

303
    def get_remote_peer_sent_error(self) -> Optional[str]:
5✔
304
        return None
×
305

306
    def get_ctx_sweep_info(self, ctx: Transaction) -> Tuple[bool, Dict[str, SweepInfo]]:
5✔
307
        our_sweep_info = self.create_sweeptxs_for_our_ctx(ctx)
×
308
        their_sweep_info = self.create_sweeptxs_for_their_ctx(ctx)
×
309
        if our_sweep_info:
×
310
            sweep_info = our_sweep_info
×
311
            who_closed = LOCAL
×
312
        elif their_sweep_info:
×
313
            sweep_info = their_sweep_info
×
314
            who_closed = REMOTE
×
315
        else:
316
            sweep_info = {}
×
317
            who_closed = 0
×
318
        if self._who_closed != who_closed:  # mostly here to limit log spam
×
319
            self._who_closed = who_closed
×
320
            if who_closed == LOCAL:
×
321
                self.logger.info(f'we (local) force closed')
×
322
            elif who_closed == REMOTE:
×
323
                self.logger.info(f'they (remote) force closed.')
×
324
            else:
325
                self.logger.info(f'not sure who closed. maybe co-op close?')
×
326
        is_local_ctx = who_closed == LOCAL
×
327
        return is_local_ctx, sweep_info
×
328

329
    def maybe_sweep_htlcs(self, ctx: Transaction, htlc_tx: Transaction) -> Dict[str, SweepInfo]:
5✔
330
        return {}
×
331

332
    def extract_preimage_from_htlc_txin(self, txin: TxInput, *, is_deeply_mined: bool) -> None:
5✔
333
        return
×
334

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

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

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

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

445
        if self.get_state() == ChannelState.CLOSED and not keep_watching:
×
446
            self.set_state(ChannelState.REDEEMED)
×
447
            if self.lnworker and self.is_backup():
×
448
                # auto-remove redeemed backups
449
                self.lnworker.remove_channel_backup(self.channel_id)
×
450

451
    @abstractmethod
5✔
452
    def is_initiator(self) -> bool:
5✔
453
        pass
×
454

455
    @abstractmethod
5✔
456
    def is_public(self) -> bool:
5✔
457
        pass
×
458

459
    @abstractmethod
5✔
460
    def is_zeroconf(self) -> bool:
5✔
461
        pass
×
462

463
    @abstractmethod
5✔
464
    def remove_zeroconf_flag(self) -> None:
5✔
465
        pass
×
466

467
    @abstractmethod
5✔
468
    def is_funding_tx_mined(self, funding_height: TxMinedInfo) -> bool:
5✔
469
        pass
×
470

471
    @abstractmethod
5✔
472
    def get_funding_address(self) -> str:
5✔
473
        pass
×
474

475
    def get_funding_tx(self) -> Optional[Transaction]:
5✔
476
        funding_txid = self.funding_outpoint.txid
×
477
        return self.lnworker.lnwatcher.adb.get_transaction(funding_txid)
×
478

479
    @abstractmethod
5✔
480
    def get_sweep_address(self) -> str:
5✔
481
        """Returns a wallet address we can use to sweep coins to.
482
        It could be something static to the channel (fixed for its lifecycle),
483
        or it might just ask the wallet now for an unused address.
484
        """
485
        pass
×
486

487
    def get_state_for_GUI(self) -> str:
5✔
488
        cs = self.get_state()
×
489
        if cs <= ChannelState.OPEN and self.unconfirmed_closing_txid:
×
490
            return 'FORCE-CLOSING'
×
491
        return cs.name
×
492

493
    @abstractmethod
5✔
494
    def get_oldest_unrevoked_ctn(self, subject: HTLCOwner) -> int:
5✔
495
        pass
×
496

497
    @abstractmethod
5✔
498
    def included_htlcs(self, subject: HTLCOwner, direction: Direction, ctn: int = None) -> Sequence[UpdateAddHtlc]:
5✔
499
        pass
×
500

501
    @abstractmethod
5✔
502
    def funding_txn_minimum_depth(self) -> int:
5✔
503
        pass
×
504

505
    @abstractmethod
5✔
506
    def balance(self, whose: HTLCOwner, *, ctx_owner=HTLCOwner.LOCAL, ctn: int = None) -> int:
5✔
507
        """This balance (in msat) only considers HTLCs that have been settled by ctn.
508
        It disregards reserve, fees, and pending HTLCs (in both directions).
509
        """
510
        pass
×
511

512
    @abstractmethod
5✔
513
    def balance_minus_outgoing_htlcs(self, whose: HTLCOwner, *,
5✔
514
                                     ctx_owner: HTLCOwner = HTLCOwner.LOCAL,
515
                                     ctn: int = None) -> int:
516
        """This balance (in msat), which includes the value of
517
        pending outgoing HTLCs, is used in the UI.
518
        """
519
        pass
×
520

521
    @abstractmethod
5✔
522
    def is_frozen_for_sending(self) -> bool:
5✔
523
        """Whether the user has marked this channel as frozen for sending.
524
        Frozen channels are not supposed to be used for new outgoing payments.
525
        (note that payment-forwarding ignores this option)
526
        """
527
        pass
×
528

529
    @abstractmethod
5✔
530
    def is_frozen_for_receiving(self) -> bool:
5✔
531
        """Whether the user has marked this channel as frozen for receiving.
532
        Frozen channels are not supposed to be used for new incoming payments.
533
        (note that payment-forwarding ignores this option)
534
        """
535
        pass
×
536

537
    @abstractmethod
5✔
538
    def get_local_pubkey(self) -> bytes:
5✔
539
        """Returns our node ID."""
540
        pass
×
541

542
    @abstractmethod
5✔
543
    def get_capacity(self) -> Optional[int]:
5✔
544
        """Returns channel capacity in satoshis, or None if unknown."""
545
        pass
×
546

547
    @abstractmethod
5✔
548
    def can_be_deleted(self) -> bool:
5✔
549
        pass
×
550

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

559
    def has_anchors(self) -> bool:
5✔
560
        pass
×
561

562

563
class ChannelBackup(AbstractChannel):
5✔
564
    """
565
    current capabilities:
566
      - detect force close
567
      - request force close
568
      - sweep my ctx to_local
569
    future:
570
      - will need to sweep their ctx to_remote
571
    """
572

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

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

650
    def can_be_deleted(self):
5✔
651
        return self.is_imported or self.is_redeemed()
×
652

653
    def get_capacity(self):
5✔
654
        lnwatcher = self.lnworker.lnwatcher
×
655
        if lnwatcher:
×
656
            # fixme: we should probably not call that method here
657
            return lnwatcher.adb.get_tx_delta(self.funding_outpoint.txid, self.cb.funding_address)
×
658
        return None
×
659

660
    def is_backup(self):
5✔
661
        return True
×
662

663
    def create_sweeptxs_for_their_ctx(self, ctx):
5✔
664
        funding_tx = self.get_funding_tx()
×
665
        assert funding_tx
×
666
        return sweep_their_ctx_to_remote_backup(chan=self, ctx=ctx, funding_tx=funding_tx)
×
667

668
    def create_sweeptxs_for_our_ctx(self, ctx):
5✔
669
        if self.is_imported:
×
670
            return sweep_our_ctx(chan=self, ctx=ctx)
×
671
        else:
672
            return {}
×
673

674
    def maybe_sweep_htlcs(self, ctx: Transaction, htlc_tx: Transaction) -> Dict[str, SweepInfo]:
5✔
675
        return {}
×
676

677
    def extract_preimage_from_htlc_txin(self, txin: TxInput, *, is_deeply_mined: bool) -> None:
5✔
678
        return None
×
679

680
    def get_funding_address(self):
5✔
681
        return self.cb.funding_address
×
682

683
    def is_initiator(self):
5✔
684
        return self.cb.is_initiator
×
685

686
    def is_public(self):
5✔
687
        return False
×
688

689
    def get_oldest_unrevoked_ctn(self, who):
5✔
690
        return -1
×
691

692
    def included_htlcs(self, subject, direction, ctn=None):
5✔
693
        return []
×
694

695
    def funding_txn_minimum_depth(self):
5✔
696
        return 1
×
697

698
    def is_funding_tx_mined(self, funding_height):
5✔
699
        return funding_height.conf > 1
×
700

701
    def balance_minus_outgoing_htlcs(self, whose: HTLCOwner, *, ctx_owner: HTLCOwner = HTLCOwner.LOCAL, ctn: int = None):
5✔
702
        return 0
×
703

704
    def balance(self, whose: HTLCOwner, *, ctx_owner=HTLCOwner.LOCAL, ctn: int = None) -> int:
5✔
705
        return 0
×
706

707
    def is_frozen_for_sending(self) -> bool:
5✔
708
        return False
×
709

710
    def is_frozen_for_receiving(self) -> bool:
5✔
711
        return False
×
712

713
    def get_sweep_address(self) -> str:
5✔
714
        return self.lnworker.wallet.get_new_sweep_address_for_channel()
×
715

716
    def has_anchors(self) -> Optional[bool]:
5✔
717
        return None
×
718

719
    def is_zeroconf(self) -> bool:
5✔
720
        return False
×
721

722
    def remove_zeroconf_flag(self) -> None:
5✔
723
        pass
×
724

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

734
    def get_close_options(self) -> Sequence[ChanCloseOption]:
5✔
735
        ret = []
×
736
        if self.get_state() == ChannelState.FUNDED:
×
737
            ret.append(ChanCloseOption.REQUEST_REMOTE_FCLOSE)
×
738
        return ret
×
739

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

753

754
class Channel(AbstractChannel):
5✔
755
    # note: try to avoid naming ctns/ctxs/etc as "current" and "pending".
756
    #       they are ambiguous. Use "oldest_unrevoked" or "latest" or "next".
757
    #       TODO enforce this ^
758

759
    # our forwarding parameters for forwarding HTLCs through this channel
760
    forwarding_cltv_delta = 144
5✔
761
    forwarding_fee_base_msat = 1000
5✔
762
    forwarding_fee_proportional_millionths = 1
5✔
763

764
    def __repr__(self):
5✔
765
        return "Channel(%s)"%self.get_id_for_log()
×
766

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

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

812
    def save_remote_scid_alias(self, alias: bytes):
5✔
813
        self.storage['alias'] = alias.hex()
5✔
814

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

822
    def get_scid_or_local_alias(self):
5✔
823
        return self.short_channel_id or self.get_local_scid_alias()
5✔
824

825
    def has_onchain_backup(self):
5✔
826
        return self.storage.get('has_onchain_backup', False)
×
827

828
    def can_be_deleted(self):
5✔
829
        return self.is_redeemed()
×
830

831
    def get_capacity(self):
5✔
832
        return self.constraints.capacity
×
833

834
    def is_public(self):
5✔
835
        return bool(self.constraints.flags & CF_ANNOUNCE_CHANNEL)
5✔
836

837
    def is_initiator(self):
5✔
838
        return self.constraints.is_initiator
5✔
839

840
    def is_active(self):
5✔
841
        return self.get_state() == ChannelState.OPEN and self.peer_state == PeerState.GOOD
5✔
842

843
    def funding_txn_minimum_depth(self):
5✔
844
        return self.constraints.funding_txn_minimum_depth
×
845

846
    def diagnostic_name(self):
5✔
847
        if self.name:
5✔
848
            return str(self.name)
5✔
849
        return super().diagnostic_name()
5✔
850

851
    def set_onion_key(self, key: int, value: bytes):
5✔
852
        self.onion_keys[key] = value
5✔
853

854
    def pop_onion_key(self, key: int) -> bytes:
5✔
855
        return self.onion_keys.pop(key)
5✔
856

857
    def set_data_loss_protect_remote_pcp(self, key, value):
5✔
858
        self.data_loss_protect_remote_pcp[key] = value
5✔
859

860
    def get_data_loss_protect_remote_pcp(self, key):
5✔
861
        return self.data_loss_protect_remote_pcp.get(key)
×
862

863
    def get_local_pubkey(self) -> bytes:
5✔
864
        if not self.lnworker:
5✔
865
            raise Exception('lnworker not set for channel!')
×
866
        return self.lnworker.node_keypair.pubkey
5✔
867

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

879
    def get_remote_update(self) -> Optional[bytes]:
5✔
880
        return bfh(self.storage.get('remote_update')) if self.storage.get('remote_update') else None
5✔
881

882
    def add_or_update_peer_addr(self, peer: LNPeerAddr) -> None:
5✔
883
        if 'peer_network_addresses' not in self.storage:
×
884
            self.storage['peer_network_addresses'] = {}
×
885
        self.storage['peer_network_addresses'][peer.net_addr_str()] = now()
×
886

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

895
    def save_remote_peer_sent_error(self, original_error: bytes):
5✔
896
        # We save the original arbitrary text(/bytes) error, as received.
897
        # The length is only implicitly limited by the BOLT-08 max msg size.
898
        # Receiving an error usually results in the channel getting closed, so
899
        # there is likely no need to store multiple errors. We only store one, and overwrite.
900
        self.storage['remote_peer_sent_error'] = original_error.hex()
5✔
901

902
    def get_remote_peer_sent_error(self) -> Optional[str]:
5✔
903
        original_error = self.storage.get('remote_peer_sent_error')
×
904
        if not original_error:
×
905
            return None
×
906
        err_bytes = bytes.fromhex(original_error)
×
907
        safe_str = error_text_bytes_to_safe_str(err_bytes)   # note: truncates
×
908
        return safe_str
×
909

910
    def get_outgoing_gossip_channel_update(self, *, scid: ShortChannelID = None) -> bytes:
5✔
911
        """
912
        scid: to be put into the channel_update message instead of the real scid, as this might be an scid alias
913
        """
914
        if self._outgoing_channel_update is not None and scid is None:
5✔
915
            return self._outgoing_channel_update
5✔
916
        if not self.lnworker:
5✔
917
            raise Exception('lnworker not set for channel!')
×
918
        if scid is None:
5✔
919
            scid = self.short_channel_id
5✔
920
        sorted_node_ids = list(sorted([self.node_id, self.get_local_pubkey()]))
5✔
921
        channel_flags = b'\x00' if sorted_node_ids[0] == self.get_local_pubkey() else b'\x01'
5✔
922
        htlc_maximum_msat = min(self.config[REMOTE].max_htlc_value_in_flight_msat, 1000 * self.constraints.capacity)
5✔
923

924
        chan_upd = encode_msg(
5✔
925
            "channel_update",
926
            short_channel_id=scid,
927
            channel_flags=channel_flags,
928
            message_flags=b'\x01',
929
            cltv_expiry_delta=self.forwarding_cltv_delta,
930
            htlc_minimum_msat=self.config[REMOTE].htlc_minimum_msat,
931
            htlc_maximum_msat=htlc_maximum_msat,
932
            fee_base_msat=self.forwarding_fee_base_msat,
933
            fee_proportional_millionths=self.forwarding_fee_proportional_millionths,
934
            chain_hash=constants.net.rev_genesis_bytes(),
935
            timestamp=now(),
936
        )
937
        sighash = sha256d(chan_upd[2 + 64:])
5✔
938
        sig = ecc.ECPrivkey(self.lnworker.node_keypair.privkey).ecdsa_sign(sighash, sigencode=ecc.ecdsa_sig64_from_r_and_s)
5✔
939
        message_type, payload = decode_msg(chan_upd)
5✔
940
        payload['signature'] = sig
5✔
941
        chan_upd = encode_msg(message_type, **payload)
5✔
942

943
        self._outgoing_channel_update = chan_upd
5✔
944
        return chan_upd
5✔
945

946
    def construct_channel_announcement_without_sigs(self) -> Tuple[bytes, bool]:
5✔
947
        bitcoin_keys = [
5✔
948
            self.config[REMOTE].multisig_key.pubkey,
949
            self.config[LOCAL].multisig_key.pubkey]
950
        node_ids = [self.node_id, self.get_local_pubkey()]
5✔
951
        is_reverse = node_ids[0] > node_ids[1]
5✔
952
        if is_reverse:
5✔
953
            node_ids.reverse()
5✔
954
            bitcoin_keys.reverse()
5✔
955
        chan_ann = encode_msg(
5✔
956
            "channel_announcement",
957
            len=0,
958
            features=b'',
959
            chain_hash=constants.net.rev_genesis_bytes(),
960
            short_channel_id=self.short_channel_id,
961
            node_id_1=node_ids[0],
962
            node_id_2=node_ids[1],
963
            bitcoin_key_1=bitcoin_keys[0],
964
            bitcoin_key_2=bitcoin_keys[1],
965
        )
966
        return chan_ann, is_reverse
5✔
967

968
    def get_channel_announcement_hash(self):
5✔
969
        chan_ann, _ = self.construct_channel_announcement_without_sigs()
×
970
        return sha256d(chan_ann[256+2:])
×
971

972
    def is_static_remotekey_enabled(self) -> bool:
5✔
973
        channel_type = ChannelType(self.storage.get('channel_type'))
5✔
974
        return bool(channel_type & ChannelType.OPTION_STATIC_REMOTEKEY)
5✔
975

976
    def is_zeroconf(self) -> bool:
5✔
977
        channel_type = ChannelType(self.storage.get('channel_type'))
×
978
        return bool(channel_type & ChannelType.OPTION_ZEROCONF)
×
979

980
    def remove_zeroconf_flag(self) -> None:
5✔
981
        if not self.is_zeroconf():
×
982
            return
×
983
        channel_type = ChannelType(self.storage.get('channel_type'))
×
984
        self.storage['channel_type'] = channel_type & ~ChannelType.OPTION_ZEROCONF
×
985

986
    def get_sweep_address(self) -> str:
5✔
987
        # TODO: in case of unilateral close with pending HTLCs, this address will be reused
988
        if self.has_anchors():
5✔
989
            addr = self.lnworker.wallet.get_new_sweep_address_for_channel()
×
990
        elif self.is_static_remotekey_enabled():
5✔
991
            our_payment_pubkey = self.config[LOCAL].payment_basepoint.pubkey
5✔
992
            addr = make_commitment_output_to_remote_address(our_payment_pubkey, has_anchors=self.has_anchors())
5✔
993
        if self.lnworker:
5✔
994
            assert self.lnworker.wallet.is_mine(addr)
5✔
995
        return addr
5✔
996

997
    def has_anchors(self) -> bool:
5✔
998
        channel_type = ChannelType(self.storage.get('channel_type'))
5✔
999
        return bool(channel_type & ChannelType.OPTION_ANCHORS_ZERO_FEE_HTLC_TX)
5✔
1000

1001
    def get_wallet_addresses_channel_might_want_reserved(self) -> Sequence[str]:
5✔
1002
        assert self.is_static_remotekey_enabled()
5✔
1003
        our_payment_pubkey = self.config[LOCAL].payment_basepoint.pubkey
5✔
1004
        to_remote_address = make_commitment_output_to_remote_address(our_payment_pubkey, has_anchors=self.has_anchors())
5✔
1005
        return [to_remote_address]
5✔
1006

1007
    def get_feerate(self, subject: HTLCOwner, *, ctn: int) -> int:
5✔
1008
        # returns feerate in sat/kw
1009
        return self.hm.get_feerate(subject, ctn)
5✔
1010

1011
    def get_oldest_unrevoked_feerate(self, subject: HTLCOwner) -> int:
5✔
1012
        return self.hm.get_feerate_in_oldest_unrevoked_ctx(subject)
5✔
1013

1014
    def get_latest_feerate(self, subject: HTLCOwner) -> int:
5✔
1015
        return self.hm.get_feerate_in_latest_ctx(subject)
5✔
1016

1017
    def get_next_feerate(self, subject: HTLCOwner) -> int:
5✔
1018
        return self.hm.get_feerate_in_next_ctx(subject)
5✔
1019

1020
    def get_payments(self, status=None) -> Mapping[bytes, List[HTLCWithStatus]]:
5✔
1021
        out = defaultdict(list)
5✔
1022
        for direction, htlc in self.hm.all_htlcs_ever():
5✔
1023
            htlc_proposer = LOCAL if direction is SENT else REMOTE
5✔
1024
            if self.hm.was_htlc_failed(htlc_id=htlc.htlc_id, htlc_proposer=htlc_proposer):
5✔
1025
                _status = 'failed'
5✔
1026
            elif self.hm.was_htlc_preimage_released(htlc_id=htlc.htlc_id, htlc_proposer=htlc_proposer):
5✔
1027
                _status = 'settled'
5✔
1028
            else:
1029
                _status = 'inflight'
5✔
1030
            if status and status != _status:
5✔
1031
                continue
5✔
1032
            htlc_with_status = HTLCWithStatus(
5✔
1033
                channel_id=self.channel_id, htlc=htlc, direction=direction, status=_status)
1034
            out[htlc.payment_hash].append(htlc_with_status)
5✔
1035
        return out
5✔
1036

1037
    def open_with_first_pcp(self, remote_pcp: bytes, remote_sig: bytes) -> None:
5✔
1038
        with self.db_lock:
5✔
1039
            self.config[REMOTE].current_per_commitment_point = remote_pcp
5✔
1040
            self.config[REMOTE].next_per_commitment_point = None
5✔
1041
            self.config[LOCAL].current_commitment_signature = remote_sig
5✔
1042
            self.hm.channel_open_finished()
5✔
1043
            self.peer_state = PeerState.GOOD
5✔
1044

1045
    def get_state_for_GUI(self):
5✔
1046
        cs_name = super().get_state_for_GUI()
×
1047
        if self.is_closed() or self.unconfirmed_closing_txid:
×
1048
            return cs_name
×
1049
        ps = self.peer_state
×
1050
        if ps != PeerState.GOOD:
×
1051
            return ps.name
×
1052
        return cs_name
×
1053

1054
    def set_can_send_ctx_updates(self, b: bool) -> None:
5✔
1055
        self._can_send_ctx_updates = b
5✔
1056

1057
    def can_update_ctx(self, *, proposer: HTLCOwner) -> bool:
5✔
1058
        """Whether proposer is allowed to send commitment_signed, revoke_and_ack,
1059
        and update_* messages.
1060
        """
1061
        if self.get_state() not in (ChannelState.OPEN, ChannelState.SHUTDOWN):
5✔
1062
            return False
5✔
1063
        if self.peer_state != PeerState.GOOD:
5✔
1064
            return False
×
1065
        if proposer == LOCAL:
5✔
1066
            if not self._can_send_ctx_updates:
5✔
UNCOV
1067
                return False
1✔
1068
        return True
5✔
1069

1070
    def can_send_update_add_htlc(self) -> bool:
5✔
1071
        return self.can_update_ctx(proposer=LOCAL) and self.is_open()
5✔
1072

1073
    def is_frozen_for_sending(self) -> bool:
5✔
1074
        if self.lnworker and self.lnworker.uses_trampoline() and not self.lnworker.is_trampoline_peer(self.node_id):
5✔
1075
            return True
×
1076
        return self.storage.get('frozen_for_sending', False)
5✔
1077

1078
    def set_frozen_for_sending(self, b: bool) -> None:
5✔
1079
        self.storage['frozen_for_sending'] = bool(b)
×
1080
        util.trigger_callback('channel', self.lnworker.wallet, self)
×
1081

1082
    def is_frozen_for_receiving(self) -> bool:
5✔
1083
        if self.lnworker and self.lnworker.uses_trampoline() and not self.lnworker.is_trampoline_peer(self.node_id):
5✔
1084
            return True
×
1085
        return self.storage.get('frozen_for_receiving', False)
5✔
1086

1087
    def set_frozen_for_receiving(self, b: bool) -> None:
5✔
1088
        self.storage['frozen_for_receiving'] = bool(b)
×
1089
        util.trigger_callback('channel', self.lnworker.wallet, self)
×
1090

1091
    def _assert_can_add_htlc(self, *, htlc_proposer: HTLCOwner, amount_msat: int,
5✔
1092
                             ignore_min_htlc_value: bool = False) -> None:
1093
        """Raises PaymentFailure if the htlc_proposer cannot add this new HTLC.
1094
        (this is relevant both for forwarding and endpoint)
1095
        """
1096
        htlc_receiver = htlc_proposer.inverted()
5✔
1097
        # note: all these tests are about the *receiver's* *next* commitment transaction,
1098
        #       and the constraints are the ones imposed by their config
1099
        ctn = self.get_next_ctn(htlc_receiver)
5✔
1100
        chan_config = self.config[htlc_receiver]
5✔
1101
        if self.get_state() != ChannelState.OPEN:
5✔
1102
            raise PaymentFailure(f"Channel not open. {self.get_state()!r}")
×
1103
        if not self.can_update_ctx(proposer=htlc_proposer):
5✔
1104
            raise PaymentFailure(f"cannot update channel. {self.get_state()!r} {self.peer_state!r}")
×
1105
        if htlc_proposer == LOCAL:
5✔
1106
            if not self.can_send_update_add_htlc():
5✔
1107
                raise PaymentFailure('Channel cannot add htlc')
×
1108

1109
        # check htlc raw value
1110
        if not ignore_min_htlc_value:
5✔
1111
            if amount_msat <= 0:
5✔
1112
                raise PaymentFailure("HTLC value must be positive")
×
1113
            if amount_msat < chan_config.htlc_minimum_msat:
5✔
1114
                raise PaymentFailure(f'HTLC value too small: {amount_msat} msat')
×
1115

1116
        if self.htlc_slots_left(htlc_proposer) == 0:
5✔
1117
            raise PaymentFailure('Too many HTLCs already in channel')
×
1118

1119
        if amount_msat > self.remaining_max_inflight(htlc_receiver, strict=False):
5✔
1120
            raise PaymentFailure(
5✔
1121
                f'HTLC value sum (sum of pending htlcs plus new htlc) '
1122
                f'would exceed max allowed: {chan_config.max_htlc_value_in_flight_msat/1000} sat')
1123

1124
        # check proposer can afford htlc
1125
        max_can_send_msat = self.available_to_spend(htlc_proposer)
5✔
1126
        if max_can_send_msat < amount_msat:
5✔
1127
            raise PaymentFailure(f'Not enough balance. can send: {max_can_send_msat}, tried: {amount_msat}')
5✔
1128

1129
    def htlc_slots_left(self, htlc_proposer: HTLCOwner) -> int:
5✔
1130
        # check "max_accepted_htlcs"
1131
        htlc_receiver = htlc_proposer.inverted()
5✔
1132
        ctn = self.get_next_ctn(htlc_receiver)
5✔
1133
        chan_config = self.config[htlc_receiver]
5✔
1134
        # If proposer is LOCAL we apply stricter checks as that is behaviour we can control.
1135
        # This should lead to fewer disagreements (i.e. channels failing).
1136
        strict = (htlc_proposer == LOCAL)
5✔
1137
        if not strict:
5✔
1138
            # this is the loose check BOLT-02 specifies:
1139
            return chan_config.max_accepted_htlcs - len(self.hm.htlcs_by_direction(htlc_receiver, direction=RECEIVED, ctn=ctn))
5✔
1140
        else:
1141
            # however, c-lightning is a lot stricter, so extra checks:
1142
            # https://github.com/ElementsProject/lightning/blob/4dcd4ca1556b13b6964a10040ba1d5ef82de4788/channeld/full_channel.c#L581
1143
            max_concurrent_htlcs = min(
5✔
1144
                self.config[htlc_proposer].max_accepted_htlcs,
1145
                self.config[htlc_receiver].max_accepted_htlcs)
1146
            return max_concurrent_htlcs - len(self.hm.htlcs(htlc_receiver, ctn=ctn))
5✔
1147

1148
    def remaining_max_inflight(self, htlc_receiver: HTLCOwner, *, strict: bool) -> int:
5✔
1149
        """
1150
        Checks max_htlc_value_in_flight_msat
1151
        strict = False -> how much we can accept according to BOLT2
1152
        strict = True -> how much the remote will accept to send to us (Eclair has stricter rules)
1153
        """
1154
        ctn = self.get_next_ctn(htlc_receiver)
5✔
1155
        current_htlc_sum = htlcsum(self.hm.htlcs_by_direction(htlc_receiver, direction=RECEIVED, ctn=ctn).values())
5✔
1156
        max_inflight = self.config[htlc_receiver].max_htlc_value_in_flight_msat
5✔
1157
        if strict and htlc_receiver == LOCAL:
5✔
1158
            # in order to send, eclair applies both local and remote max values
1159
            # https://github.com/ACINQ/eclair/blob/9b0c00a2a28d3ba6c7f3d01fbd2d8704ebbdc75d/eclair-core/src/main/scala/fr/acinq/eclair/channel/Commitments.scala#L503
1160
            max_inflight = min(
5✔
1161
                self.config[LOCAL].max_htlc_value_in_flight_msat,
1162
                self.config[REMOTE].max_htlc_value_in_flight_msat
1163
            )
1164
        return max_inflight - current_htlc_sum
5✔
1165

1166
    def can_pay(self, amount_msat: int, *, check_frozen=False) -> bool:
5✔
1167
        """Returns whether we can add an HTLC of given value."""
1168
        if check_frozen and self.is_frozen_for_sending():
5✔
1169
            return False
×
1170
        try:
5✔
1171
            self._assert_can_add_htlc(htlc_proposer=LOCAL, amount_msat=amount_msat)
5✔
1172
        except PaymentFailure:
5✔
1173
            return False
5✔
1174
        return True
5✔
1175

1176
    def can_receive(self, amount_msat: int, *, check_frozen=False,
5✔
1177
                    ignore_min_htlc_value: bool = False) -> bool:
1178
        """Returns whether the remote can add an HTLC of given value."""
1179
        if check_frozen and self.is_frozen_for_receiving():
×
1180
            return False
×
1181
        try:
×
1182
            self._assert_can_add_htlc(
×
1183
                htlc_proposer=REMOTE,
1184
                amount_msat=amount_msat,
1185
                ignore_min_htlc_value=ignore_min_htlc_value)
1186
        except PaymentFailure:
×
1187
            return False
×
1188
        return True
×
1189

1190
    def should_try_to_reestablish_peer(self) -> bool:
5✔
1191
        if self.peer_state != PeerState.DISCONNECTED:
×
1192
            return False
×
1193
        if self.should_request_force_close:
×
1194
            return True
×
1195
        return ChannelState.PREOPENING < self._state < ChannelState.CLOSING
×
1196

1197
    def get_funding_address(self):
5✔
1198
        script = funding_output_script(self.config[LOCAL], self.config[REMOTE])
5✔
1199
        return redeem_script_to_address('p2wsh', script)
5✔
1200

1201
    def add_htlc(self, htlc: UpdateAddHtlc) -> UpdateAddHtlc:
5✔
1202
        """Adds a new LOCAL HTLC to the channel.
1203
        Action must be initiated by LOCAL.
1204
        """
1205
        if isinstance(htlc, dict):  # legacy conversion  # FIXME remove
5✔
1206
            htlc = UpdateAddHtlc(**htlc)
5✔
1207
        assert isinstance(htlc, UpdateAddHtlc)
5✔
1208
        self._assert_can_add_htlc(htlc_proposer=LOCAL, amount_msat=htlc.amount_msat)
5✔
1209
        if htlc.htlc_id is None:
5✔
1210
            htlc = attr.evolve(htlc, htlc_id=self.hm.get_next_htlc_id(LOCAL))
5✔
1211
        with self.db_lock:
5✔
1212
            self.hm.send_htlc(htlc)
5✔
1213
        self.logger.info("add_htlc")
5✔
1214
        return htlc
5✔
1215

1216
    def receive_htlc(self, htlc: UpdateAddHtlc, onion_packet:bytes = None) -> UpdateAddHtlc:
5✔
1217
        """Adds a new REMOTE HTLC to the channel.
1218
        Action must be initiated by REMOTE.
1219
        """
1220
        if isinstance(htlc, dict):  # legacy conversion  # FIXME remove
5✔
1221
            htlc = UpdateAddHtlc(**htlc)
5✔
1222
        assert isinstance(htlc, UpdateAddHtlc)
5✔
1223
        try:
5✔
1224
            self._assert_can_add_htlc(htlc_proposer=REMOTE, amount_msat=htlc.amount_msat)
5✔
1225
        except PaymentFailure as e:
×
1226
            raise RemoteMisbehaving(e) from e
×
1227
        if htlc.htlc_id is None:  # used in unit tests
5✔
1228
            htlc = attr.evolve(htlc, htlc_id=self.hm.get_next_htlc_id(REMOTE))
5✔
1229
        with self.db_lock:
5✔
1230
            self.hm.recv_htlc(htlc)
5✔
1231
            if onion_packet:
5✔
1232
                self.unfulfilled_htlcs[htlc.htlc_id] = onion_packet.hex(), None
5✔
1233

1234
        self.logger.info("receive_htlc")
5✔
1235
        return htlc
5✔
1236

1237
    def sign_next_commitment(self) -> Tuple[bytes, Sequence[bytes]]:
5✔
1238
        """Returns signatures for our next remote commitment tx.
1239
        Action must be initiated by LOCAL.
1240
        Finally, the next remote ctx becomes the latest remote ctx.
1241
        """
1242
        # TODO: when more channel types are supported, this method should depend on channel type
1243
        next_remote_ctn = self.get_next_ctn(REMOTE)
5✔
1244
        self.logger.info(f"sign_next_commitment. ctn={next_remote_ctn}")
5✔
1245
        assert not self.is_closed(), self.get_state()
5✔
1246

1247
        pending_remote_commitment = self.get_next_commitment(REMOTE)
5✔
1248
        sig_64 = sign_and_get_sig_string(pending_remote_commitment, self.config[LOCAL], self.config[REMOTE])
5✔
1249
        self.logger.debug(f"sign_next_commitment. {pending_remote_commitment.serialize()=}. {sig_64.hex()=}")
5✔
1250

1251
        their_remote_htlc_privkey_number = derive_privkey(
5✔
1252
            int.from_bytes(self.config[LOCAL].htlc_basepoint.privkey, 'big'),
1253
            self.config[REMOTE].next_per_commitment_point)
1254
        their_remote_htlc_privkey = their_remote_htlc_privkey_number.to_bytes(32, 'big')
5✔
1255

1256
        htlcsigs = []
5✔
1257
        htlc_to_ctx_output_idx_map = map_htlcs_to_ctx_output_idxs(chan=self,
5✔
1258
                                                                  ctx=pending_remote_commitment,
1259
                                                                  pcp=self.config[REMOTE].next_per_commitment_point,
1260
                                                                  subject=REMOTE,
1261
                                                                  ctn=next_remote_ctn)
1262
        for (direction, htlc), (ctx_output_idx, htlc_relative_idx) in htlc_to_ctx_output_idx_map.items():
5✔
1263
            _script, htlc_tx = make_htlc_tx_with_open_channel(chan=self,
5✔
1264
                                                              pcp=self.config[REMOTE].next_per_commitment_point,
1265
                                                              subject=REMOTE,
1266
                                                              ctn=next_remote_ctn,
1267
                                                              htlc_direction=direction,
1268
                                                              commit=pending_remote_commitment,
1269
                                                              ctx_output_idx=ctx_output_idx,
1270
                                                              htlc=htlc)
1271
            if self.has_anchors():
5✔
1272
                # we send a signature with the following sighash flags
1273
                # for the peer to be able to replace inputs and outputs
1274
                htlc_tx.inputs()[0].sighash = Sighash.ANYONECANPAY | Sighash.SINGLE
5✔
1275
            sig = htlc_tx.sign_txin(0, their_remote_htlc_privkey)
5✔
1276
            htlc_sig = ecc.ecdsa_sig64_from_der_sig(sig[:-1])
5✔
1277
            htlcsigs.append((ctx_output_idx, htlc_sig))
5✔
1278
        htlcsigs.sort()
5✔
1279
        htlcsigs = [x[1] for x in htlcsigs]
5✔
1280
        with self.db_lock:
5✔
1281
            self.hm.send_ctx()
5✔
1282
        return sig_64, htlcsigs
5✔
1283

1284
    def receive_new_commitment(self, sig: bytes, htlc_sigs: Sequence[bytes]) -> None:
5✔
1285
        """Processes signatures for our next local commitment tx, sent by the REMOTE.
1286
        Action must be initiated by REMOTE.
1287
        If all checks pass, the next local ctx becomes the latest local ctx.
1288
        """
1289
        # TODO in many failure cases below, we should "fail" the channel (force-close)
1290
        # TODO: when more channel types are supported, this method should depend on channel type
1291
        next_local_ctn = self.get_next_ctn(LOCAL)
5✔
1292
        self.logger.info(f"receive_new_commitment. ctn={next_local_ctn}, len(htlc_sigs)={len(htlc_sigs)}")
5✔
1293
        assert not self.is_closed(), self.get_state()
5✔
1294

1295
        assert len(htlc_sigs) == 0 or type(htlc_sigs[0]) is bytes
5✔
1296

1297
        pending_local_commitment = self.get_next_commitment(LOCAL)
5✔
1298
        pre_hash = pending_local_commitment.serialize_preimage(0)
5✔
1299
        msg_hash = sha256d(pre_hash)
5✔
1300
        if not ECPubkey(self.config[REMOTE].multisig_key.pubkey).ecdsa_verify(sig, msg_hash):
5✔
1301
            raise LNProtocolWarning(
×
1302
                f'failed verifying signature for our updated commitment transaction. '
1303
                f'sig={sig.hex()}. '
1304
                f'msg_hash={msg_hash.hex()}. '
1305
                f'pubkey={self.config[REMOTE].multisig_key.pubkey}. '
1306
                f'ctx={pending_local_commitment.serialize()} '
1307
            )
1308

1309
        htlc_sigs_string = b''.join(htlc_sigs)
5✔
1310

1311
        _secret, pcp = self.get_secret_and_point(subject=LOCAL, ctn=next_local_ctn)
5✔
1312

1313
        htlc_to_ctx_output_idx_map = map_htlcs_to_ctx_output_idxs(chan=self,
5✔
1314
                                                                  ctx=pending_local_commitment,
1315
                                                                  pcp=pcp,
1316
                                                                  subject=LOCAL,
1317
                                                                  ctn=next_local_ctn)
1318
        if len(htlc_to_ctx_output_idx_map) != len(htlc_sigs):
5✔
1319
            raise LNProtocolWarning(f'htlc sigs failure. recv {len(htlc_sigs)} sigs, expected {len(htlc_to_ctx_output_idx_map)}')
×
1320
        for (direction, htlc), (ctx_output_idx, htlc_relative_idx) in htlc_to_ctx_output_idx_map.items():
5✔
1321
            htlc_sig = htlc_sigs[htlc_relative_idx]
5✔
1322
            self._verify_htlc_sig(htlc=htlc,
5✔
1323
                                  htlc_sig=htlc_sig,
1324
                                  htlc_direction=direction,
1325
                                  pcp=pcp,
1326
                                  ctx=pending_local_commitment,
1327
                                  ctx_output_idx=ctx_output_idx,
1328
                                  ctn=next_local_ctn)
1329
        with self.db_lock:
5✔
1330
            self.hm.recv_ctx()
5✔
1331
            self.config[LOCAL].current_commitment_signature=sig
5✔
1332
            self.config[LOCAL].current_htlc_signatures=htlc_sigs_string
5✔
1333

1334
    def _verify_htlc_sig(self, *, htlc: UpdateAddHtlc, htlc_sig: bytes, htlc_direction: Direction,
5✔
1335
                         pcp: bytes, ctx: Transaction, ctx_output_idx: int, ctn: int) -> None:
1336
        _script, htlc_tx = make_htlc_tx_with_open_channel(chan=self,
5✔
1337
                                                          pcp=pcp,
1338
                                                          subject=LOCAL,
1339
                                                          ctn=ctn,
1340
                                                          htlc_direction=htlc_direction,
1341
                                                          commit=ctx,
1342
                                                          ctx_output_idx=ctx_output_idx,
1343
                                                          htlc=htlc)
1344
        if self.has_anchors():
5✔
1345
            # peer sent us a signature for our ctx using anchor sighash flags
1346
            htlc_tx.inputs()[0].sighash = Sighash.ANYONECANPAY | Sighash.SINGLE
5✔
1347
        pre_hash = htlc_tx.serialize_preimage(0)
5✔
1348
        msg_hash = sha256d(pre_hash)
5✔
1349
        remote_htlc_pubkey = derive_pubkey(self.config[REMOTE].htlc_basepoint.pubkey, pcp)
5✔
1350
        if not ECPubkey(remote_htlc_pubkey).ecdsa_verify(htlc_sig, msg_hash):
5✔
1351
            raise LNProtocolWarning(
×
1352
                f'failed verifying HTLC signatures: {htlc=}, {htlc_direction=}. '
1353
                f'htlc_tx={htlc_tx.serialize()}. '
1354
                f'htlc_sig={htlc_sig.hex()}. '
1355
                f'remote_htlc_pubkey={remote_htlc_pubkey.hex()}. '
1356
                f'msg_hash={msg_hash.hex()}. '
1357
                f'ctx={ctx.serialize()}. '
1358
                f'ctx_output_idx={ctx_output_idx}. '
1359
                f'ctn={ctn}. '
1360
            )
1361

1362
    def get_remote_htlc_sig_for_htlc(self, *, htlc_relative_idx: int) -> bytes:
5✔
1363
        data = self.config[LOCAL].current_htlc_signatures
×
1364
        htlc_sigs = list(chunks(data, 64))
×
1365
        htlc_sig = htlc_sigs[htlc_relative_idx]
×
1366
        remote_sighash = Sighash.ALL if not self.has_anchors() else Sighash.ANYONECANPAY | Sighash.SINGLE
×
1367
        remote_htlc_sig = ecc.ecdsa_der_sig_from_ecdsa_sig64(htlc_sig) + Sighash.to_sigbytes(remote_sighash)
×
1368
        return remote_htlc_sig
×
1369

1370
    def revoke_current_commitment(self):
5✔
1371
        self.logger.info("revoke_current_commitment")
5✔
1372
        assert not self.is_closed(), self.get_state()
5✔
1373
        new_ctn = self.get_latest_ctn(LOCAL)
5✔
1374
        new_ctx = self.get_latest_commitment(LOCAL)
5✔
1375
        if not self.signature_fits(new_ctx):
5✔
1376
            # this should never fail; as receive_new_commitment already did this test
1377
            raise Exception("refusing to revoke as remote sig does not fit")
×
1378
        with self.db_lock:
5✔
1379
            self.hm.send_rev()
5✔
1380
        last_secret, last_point = self.get_secret_and_point(LOCAL, new_ctn - 1)
5✔
1381
        next_secret, next_point = self.get_secret_and_point(LOCAL, new_ctn + 1)
5✔
1382
        return RevokeAndAck(last_secret, next_point)
5✔
1383

1384
    def receive_revocation(self, revocation: RevokeAndAck):
5✔
1385
        self.logger.info("receive_revocation")
5✔
1386
        assert not self.is_closed(), self.get_state()
5✔
1387
        new_ctn = self.get_latest_ctn(REMOTE)
5✔
1388
        cur_point = self.config[REMOTE].current_per_commitment_point
5✔
1389
        derived_point = ecc.ECPrivkey(revocation.per_commitment_secret).get_public_key_bytes(compressed=True)
5✔
1390
        if cur_point != derived_point:
5✔
1391
            raise Exception('revoked secret not for current point')
×
1392
        with self.db_lock:
5✔
1393
            self.revocation_store.add_next_entry(revocation.per_commitment_secret)
5✔
1394
            ##### start applying fee/htlc changes
1395
            self.hm.recv_rev()
5✔
1396
            self.config[REMOTE].current_per_commitment_point=self.config[REMOTE].next_per_commitment_point
5✔
1397
            self.config[REMOTE].next_per_commitment_point=revocation.next_per_commitment_point
5✔
1398
        assert new_ctn == self.get_oldest_unrevoked_ctn(REMOTE)
5✔
1399
        # lnworker callbacks
1400
        if self.lnworker:
5✔
1401
            sent = self.hm.sent_in_ctn(new_ctn)
5✔
1402
            for htlc in sent:
5✔
1403
                self.lnworker.htlc_fulfilled(self, htlc.payment_hash, htlc.htlc_id)
5✔
1404
            failed = self.hm.failed_in_ctn(new_ctn)
5✔
1405
            for htlc in failed:
5✔
1406
                try:
5✔
1407
                    error_bytes, failure_message = self._receive_fail_reasons.pop(htlc.htlc_id)
5✔
1408
                except KeyError:
×
1409
                    error_bytes, failure_message = None, None
×
1410
                self.lnworker.htlc_failed(self, htlc.payment_hash, htlc.htlc_id, error_bytes, failure_message)
5✔
1411

1412
    def extract_preimage_from_htlc_txin(self, txin: TxInput, *, is_deeply_mined: bool) -> None:
5✔
1413
        from . import lnutil
×
1414
        from .crypto import ripemd
×
1415
        from .transaction import match_script_against_template, script_GetOp
×
1416
        from .lnonion import OnionRoutingFailure, OnionFailureCode
×
1417
        witness = txin.witness_elements()
×
1418
        witness_script = witness[-1]
×
1419
        script_ops = [x for x in script_GetOp(witness_script)]
×
1420
        if match_script_against_template(witness_script, lnutil.WITNESS_TEMPLATE_OFFERED_HTLC, debug=False) \
×
1421
           or match_script_against_template(witness_script, lnutil.WITNESS_TEMPLATE_OFFERED_HTLC_ANCHORS, debug=False):
1422
            ripemd_payment_hash = script_ops[21][1]
×
1423
        elif match_script_against_template(witness_script, lnutil.WITNESS_TEMPLATE_RECEIVED_HTLC, debug=False) \
×
1424
           or match_script_against_template(witness_script, lnutil.WITNESS_TEMPLATE_RECEIVED_HTLC_ANCHORS, debug=False):
1425
            ripemd_payment_hash = script_ops[14][1]
×
1426
        else:
1427
            return
×
1428
        found = {}
×
1429
        for direction, htlc in itertools.chain(
×
1430
                self.hm.get_htlcs_in_oldest_unrevoked_ctx(REMOTE),
1431
                self.hm.get_htlcs_in_latest_ctx(REMOTE)):
1432
            if ripemd(htlc.payment_hash) == ripemd_payment_hash:
×
1433
                is_sent = direction == RECEIVED
×
1434
                found[htlc.htlc_id] = (htlc, is_sent)
×
1435
        for direction, htlc in itertools.chain(
×
1436
                self.hm.get_htlcs_in_oldest_unrevoked_ctx(LOCAL),
1437
                self.hm.get_htlcs_in_latest_ctx(LOCAL)):
1438
            if ripemd(htlc.payment_hash) == ripemd_payment_hash:
×
1439
                is_sent = direction == SENT
×
1440
                found[htlc.htlc_id] = (htlc, is_sent)
×
1441
        if not found:
×
1442
            return
×
1443
        if len(witness) == 5:    # HTLC success tx
×
1444
            preimage = witness[3]
×
1445
        elif len(witness) == 3:  # spending offered HTLC directly from ctx
×
1446
            preimage = witness[1]
×
1447
        else:
1448
            preimage = None      # HTLC timeout tx
×
1449
        if preimage:
×
1450
            assert ripemd(sha256(preimage)) == ripemd_payment_hash
×
1451
            payment_hash = sha256(preimage)
×
1452
            if self.lnworker.get_preimage(payment_hash) is not None:
×
1453
                return
×
1454
            # ^ note: log message text grepped for in regtests
1455
            self.logger.info(f"found preimage in witness of length {len(witness)}, for {payment_hash.hex()}")
×
1456

1457
        # Mark the htlc as fulfilled or failed.
1458
        # If we forwarded this, this ensures that the success/failure is propagated back on the incoming channel.
1459
        # FIXME we only look at outgoing htlcs that have a corresponding output in the commitment tx,
1460
        #       however we should also look at those that do not. E.g. a small value htlc might not create an output
1461
        #       but we should still propagate back success or failure on the incoming link. And it is not just about
1462
        #       small value htlcs: even a large htlc might not appear in the outgoing channel's ctx, e.g. maybe it was
1463
        #       not committed yet - we should still make sure it gets removed on the incoming channel. (see #9631)
1464
        if preimage:
×
1465
            self.lnworker.save_preimage(payment_hash, preimage)
×
1466
            for htlc, is_sent in found.values():
×
1467
                if is_sent:
×
1468
                    self.lnworker.htlc_fulfilled(self, payment_hash, htlc.htlc_id)
×
1469
        else:
1470
            # htlc timeout tx
1471
            if not is_deeply_mined:
×
1472
                return
×
1473
            failure = OnionRoutingFailure(code=OnionFailureCode.PERMANENT_CHANNEL_FAILURE, data=b'')
×
1474
            for htlc, is_sent in found.values():
×
1475
                if is_sent:
×
1476
                    self.logger.info(f'htlc timeout tx: failing htlc {is_sent}')
×
1477
                    self.lnworker.htlc_failed(
×
1478
                        self,
1479
                        payment_hash=htlc.payment_hash,
1480
                        htlc_id=htlc.htlc_id,
1481
                        error_bytes=None,
1482
                        failure_message=failure)
1483

1484
    def balance(self, whose: HTLCOwner, *, ctx_owner=HTLCOwner.LOCAL, ctn: int = None) -> int:
5✔
1485
        assert type(whose) is HTLCOwner
5✔
1486
        initial = self.config[whose].initial_msat
5✔
1487
        return self.hm.get_balance_msat(whose=whose,
5✔
1488
                                        ctx_owner=ctx_owner,
1489
                                        ctn=ctn,
1490
                                        initial_balance_msat=initial)
1491

1492
    def balance_minus_outgoing_htlcs(self, whose: HTLCOwner, *, ctx_owner: HTLCOwner = HTLCOwner.LOCAL,
5✔
1493
                                     ctn: int = None) -> int:
1494
        assert type(whose) is HTLCOwner
5✔
1495
        if ctn is None:
5✔
1496
            ctn = self.get_next_ctn(ctx_owner)
5✔
1497
        committed_balance = self.balance(whose, ctx_owner=ctx_owner, ctn=ctn)
5✔
1498
        direction = RECEIVED if whose != ctx_owner else SENT
5✔
1499
        balance_in_htlcs = self.balance_tied_up_in_htlcs_by_direction(ctx_owner, ctn=ctn, direction=direction)
5✔
1500
        return committed_balance - balance_in_htlcs
5✔
1501

1502
    def balance_tied_up_in_htlcs_by_direction(self, ctx_owner: HTLCOwner = LOCAL, *, ctn: int = None,
5✔
1503
                                              direction: Direction):
1504
        # in msat
1505
        if ctn is None:
5✔
1506
            ctn = self.get_next_ctn(ctx_owner)
×
1507
        return htlcsum(self.hm.htlcs_by_direction(ctx_owner, direction, ctn).values())
5✔
1508

1509
    def has_unsettled_htlcs(self) -> bool:
5✔
1510
        return len(self.hm.htlcs(LOCAL)) + len(self.hm.htlcs(REMOTE)) > 0
5✔
1511

1512
    def available_to_spend(self, subject: HTLCOwner) -> int:
5✔
1513
        """The usable balance of 'subject' in msat, after taking reserve and fees (and anchors) into
1514
        consideration. Note that fees (and hence the result) fluctuate even without user interaction.
1515
        """
1516
        assert type(subject) is HTLCOwner
5✔
1517
        sender = subject
5✔
1518
        receiver = subject.inverted()
5✔
1519
        initiator = LOCAL if self.constraints.is_initiator else REMOTE  # the initiator/funder pays on-chain fees
5✔
1520

1521
        def consider_ctx(*, ctx_owner: HTLCOwner, is_htlc_dust: bool) -> int:
5✔
1522
            ctn = self.get_next_ctn(ctx_owner)
5✔
1523
            sender_balance_msat = self.balance_minus_outgoing_htlcs(whose=sender, ctx_owner=ctx_owner, ctn=ctn)
5✔
1524
            receiver_balance_msat = self.balance_minus_outgoing_htlcs(whose=receiver, ctx_owner=ctx_owner, ctn=ctn)
5✔
1525
            sender_reserve_msat = self.config[receiver].reserve_sat * 1000
5✔
1526
            receiver_reserve_msat = self.config[sender].reserve_sat * 1000
5✔
1527
            num_htlcs_in_ctx = len(self.included_htlcs(ctx_owner, SENT, ctn=ctn) + self.included_htlcs(ctx_owner, RECEIVED, ctn=ctn))
5✔
1528
            feerate = self.get_feerate(ctx_owner, ctn=ctn)
5✔
1529
            ctx_fees_msat = calc_fees_for_commitment_tx(
5✔
1530
                num_htlcs=num_htlcs_in_ctx,
1531
                feerate=feerate,
1532
                is_local_initiator=self.constraints.is_initiator,
1533
                round_to_sat=False,
1534
                has_anchors=self.has_anchors()
1535
            )
1536
            htlc_fee_msat = fee_for_htlc_output(feerate=feerate)
5✔
1537
            htlc_trim_func = received_htlc_trim_threshold_sat if ctx_owner == receiver else offered_htlc_trim_threshold_sat
5✔
1538
            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
5✔
1539

1540
            # the sender cannot spend below its reserve
1541
            max_send_msat = sender_balance_msat - sender_reserve_msat
5✔
1542

1543
            # reserve a fee spike buffer
1544
            # see https://github.com/lightningnetwork/lightning-rfc/pull/740
1545
            if sender == initiator == LOCAL:
5✔
1546
                fee_spike_buffer = calc_fees_for_commitment_tx(
5✔
1547
                    num_htlcs=num_htlcs_in_ctx + int(not is_htlc_dust) + 1,
1548
                    feerate=2 * feerate,
1549
                    is_local_initiator=self.constraints.is_initiator,
1550
                    round_to_sat=False,
1551
                    has_anchors=self.has_anchors())[sender]
1552
                max_send_msat -= fee_spike_buffer
5✔
1553
            # we can't enforce the fee spike buffer on the remote party
1554
            elif sender == initiator == REMOTE:
5✔
1555
                max_send_msat -= ctx_fees_msat[sender]
5✔
1556

1557
            # initiator pays for anchor outputs
1558
            if sender == initiator and self.has_anchors():
5✔
1559
                max_send_msat -= 2 * FIXED_ANCHOR_SAT * 1000
5✔
1560

1561
            # handle the transaction fees for the HTLC transaction
1562
            if is_htlc_dust:
5✔
1563
                # nobody pays additional HTLC transaction fees
1564
                return min(max_send_msat, htlc_trim_threshold_msat - 1)
5✔
1565
            else:
1566
                # somebody has to pay for the additional HTLC transaction fees
1567
                if sender == initiator:
5✔
1568
                    return max_send_msat - htlc_fee_msat
5✔
1569
                else:
1570
                    # check if the receiver can afford to pay for the HTLC transaction fees
1571
                    new_receiver_balance = receiver_balance_msat - receiver_reserve_msat - ctx_fees_msat[receiver] - htlc_fee_msat
5✔
1572
                    if self.has_anchors():
5✔
1573
                        new_receiver_balance -= 2 * FIXED_ANCHOR_SAT * 1000
5✔
1574
                    if new_receiver_balance < 0:
5✔
1575
                        return 0
5✔
1576
                    return max_send_msat
5✔
1577

1578
        max_send_msat = min(
5✔
1579
            max(
1580
                consider_ctx(ctx_owner=receiver, is_htlc_dust=True),
1581
                consider_ctx(ctx_owner=receiver, is_htlc_dust=False),
1582
            ),
1583
            max(
1584
                consider_ctx(ctx_owner=sender, is_htlc_dust=True),
1585
                consider_ctx(ctx_owner=sender, is_htlc_dust=False),
1586
            ),
1587
        )
1588

1589
        max_send_msat = min(max_send_msat, self.remaining_max_inflight(receiver, strict=True))
5✔
1590
        if self.htlc_slots_left(sender) == 0:
5✔
1591
            max_send_msat = 0
×
1592

1593
        max_send_msat = max(max_send_msat, 0)
5✔
1594
        return max_send_msat
5✔
1595

1596

1597
    def included_htlcs(self, subject: HTLCOwner, direction: Direction, ctn: int = None, *,
5✔
1598
                       feerate: int = None) -> List[UpdateAddHtlc]:
1599
        """Returns list of non-dust HTLCs for subject's commitment tx at ctn,
1600
        filtered by direction (of HTLCs).
1601
        """
1602
        assert type(subject) is HTLCOwner
5✔
1603
        assert type(direction) is Direction
5✔
1604
        if ctn is None:
5✔
1605
            ctn = self.get_oldest_unrevoked_ctn(subject)
×
1606
        if feerate is None:
5✔
1607
            feerate = self.get_feerate(subject, ctn=ctn)
5✔
1608
        conf = self.config[subject]
5✔
1609
        if direction == RECEIVED:
5✔
1610
            threshold_sat = received_htlc_trim_threshold_sat(dust_limit_sat=conf.dust_limit_sat, feerate=feerate, has_anchors=self.has_anchors())
5✔
1611
        else:
1612
            threshold_sat = offered_htlc_trim_threshold_sat(dust_limit_sat=conf.dust_limit_sat, feerate=feerate, has_anchors=self.has_anchors())
5✔
1613
        htlcs = self.hm.htlcs_by_direction(subject, direction, ctn=ctn).values()
5✔
1614
        return list(filter(lambda htlc: htlc.amount_msat // 1000 >= threshold_sat, htlcs))
5✔
1615

1616
    def get_secret_and_point(self, subject: HTLCOwner, ctn: int) -> Tuple[Optional[bytes], bytes]:
5✔
1617
        assert type(subject) is HTLCOwner
5✔
1618
        assert ctn >= 0, ctn
5✔
1619
        offset = ctn - self.get_oldest_unrevoked_ctn(subject)
5✔
1620
        if subject == REMOTE:
5✔
1621
            if offset > 1:
5✔
1622
                raise RemoteCtnTooFarInFuture(f"offset: {offset}")
×
1623
            conf = self.config[REMOTE]
5✔
1624
            if offset == 1:
5✔
1625
                secret = None
5✔
1626
                point = conf.next_per_commitment_point
5✔
1627
            elif offset == 0:
5✔
1628
                secret = None
5✔
1629
                point = conf.current_per_commitment_point
5✔
1630
            else:
1631
                secret = self.revocation_store.retrieve_secret(RevocationStore.START_INDEX - ctn)
×
1632
                point = secret_to_pubkey(int.from_bytes(secret, 'big'))
×
1633
        else:
1634
            secret = get_per_commitment_secret_from_seed(self.config[LOCAL].per_commitment_secret_seed, RevocationStore.START_INDEX - ctn)
5✔
1635
            point = secret_to_pubkey(int.from_bytes(secret, 'big'))
5✔
1636
        return secret, point
5✔
1637

1638
    def get_secret_and_commitment(self, subject: HTLCOwner, *, ctn: int) -> Tuple[Optional[bytes], PartialTransaction]:
5✔
1639
        secret, point = self.get_secret_and_point(subject, ctn)
5✔
1640
        ctx = self.make_commitment(subject, point, ctn)
5✔
1641
        return secret, ctx
5✔
1642

1643
    def get_commitment(self, subject: HTLCOwner, *, ctn: int) -> PartialTransaction:
5✔
1644
        secret, ctx = self.get_secret_and_commitment(subject, ctn=ctn)
5✔
1645
        return ctx
5✔
1646

1647
    def get_next_commitment(self, subject: HTLCOwner) -> PartialTransaction:
5✔
1648
        ctn = self.get_next_ctn(subject)
5✔
1649
        return self.get_commitment(subject, ctn=ctn)
5✔
1650

1651
    def get_latest_commitment(self, subject: HTLCOwner) -> PartialTransaction:
5✔
1652
        ctn = self.get_latest_ctn(subject)
5✔
1653
        return self.get_commitment(subject, ctn=ctn)
5✔
1654

1655
    def get_oldest_unrevoked_commitment(self, subject: HTLCOwner) -> PartialTransaction:
5✔
1656
        ctn = self.get_oldest_unrevoked_ctn(subject)
5✔
1657
        return self.get_commitment(subject, ctn=ctn)
5✔
1658

1659
    def create_sweeptxs_for_watchtower(self, ctn: int) -> List[Transaction]:
5✔
1660
        from .lnsweep import sweep_their_ctx_watchtower
×
1661
        from .fee_policy import FeePolicy
×
1662
        from .transaction import PartialTxOutput, PartialTransaction
×
1663
        secret, ctx = self.get_secret_and_commitment(REMOTE, ctn=ctn)
×
1664
        txs = []
×
1665
        txins = sweep_their_ctx_watchtower(self, ctx, secret)
×
1666
        fee_policy = FeePolicy('eta:2')
×
1667
        for txin in txins:
×
1668
            output_idx = txin.prevout.out_idx
×
1669
            value = ctx.outputs()[output_idx].value
×
1670
            tx_size_bytes = 121
×
1671
            fee = fee_policy.estimate_fee(tx_size_bytes, network=self.lnworker.network, allow_fallback_to_static_rates=True)
×
1672
            outvalue = value - fee
×
1673
            sweep_outputs = [PartialTxOutput.from_address_and_value(self.get_sweep_address(), outvalue)]
×
1674
            sweep_tx = PartialTransaction.from_io([txin], sweep_outputs, version=2)
×
1675
            sig = sweep_tx.sign_txin(0, txin.privkey)
×
1676
            txin.witness = txin.make_witness(sig)
×
1677
            txs.append(sweep_tx)
×
1678
        return txs
×
1679

1680
    def get_oldest_unrevoked_ctn(self, subject: HTLCOwner) -> int:
5✔
1681
        return self.hm.ctn_oldest_unrevoked(subject)
5✔
1682

1683
    def get_latest_ctn(self, subject: HTLCOwner) -> int:
5✔
1684
        return self.hm.ctn_latest(subject)
5✔
1685

1686
    def get_next_ctn(self, subject: HTLCOwner) -> int:
5✔
1687
        return self.hm.ctn_latest(subject) + 1
5✔
1688

1689
    def total_msat(self, direction: Direction) -> int:
5✔
1690
        """Return the cumulative total msat amount received/sent so far."""
1691
        assert type(direction) is Direction
5✔
1692
        return htlcsum(self.hm.all_settled_htlcs_ever_by_direction(LOCAL, direction))
5✔
1693

1694
    def settle_htlc(self, preimage: bytes, htlc_id: int) -> None:
5✔
1695
        """Settle/fulfill a pending received HTLC.
1696
        Action must be initiated by LOCAL.
1697
        """
1698
        self.logger.info("settle_htlc")
5✔
1699
        assert self.can_update_ctx(proposer=LOCAL), f"cannot update channel. {self.get_state()!r} {self.peer_state!r}"
5✔
1700
        htlc = self.hm.get_htlc_by_id(REMOTE, htlc_id)
5✔
1701
        if htlc.payment_hash != sha256(preimage):
5✔
1702
            raise Exception("incorrect preimage for HTLC")
×
1703
        assert htlc_id not in self.hm.log[REMOTE]['settles']
5✔
1704
        self.hm.send_settle(htlc_id)
5✔
1705
        self.htlc_settle_time[htlc_id] = now()
5✔
1706

1707
    def get_payment_hash(self, htlc_id: int) -> bytes:
5✔
1708
        htlc = self.hm.get_htlc_by_id(LOCAL, htlc_id)
×
1709
        return htlc.payment_hash
×
1710

1711
    def receive_htlc_settle(self, preimage: bytes, htlc_id: int) -> None:
5✔
1712
        """Settle/fulfill a pending offered HTLC.
1713
        Action must be initiated by REMOTE.
1714
        """
1715
        self.logger.info("receive_htlc_settle")
5✔
1716
        assert self.can_update_ctx(proposer=REMOTE), f"cannot update channel. {self.get_state()!r} {self.peer_state!r}"
5✔
1717
        htlc = self.hm.get_htlc_by_id(LOCAL, htlc_id)
5✔
1718
        if htlc.payment_hash != sha256(preimage):
5✔
1719
            raise RemoteMisbehaving("received incorrect preimage for HTLC")
×
1720
        assert htlc_id not in self.hm.log[LOCAL]['settles']
5✔
1721
        with self.db_lock:
5✔
1722
            self.hm.recv_settle(htlc_id)
5✔
1723

1724
    def fail_htlc(self, htlc_id: int) -> None:
5✔
1725
        """Fail a pending received HTLC.
1726
        Action must be initiated by LOCAL.
1727
        """
1728
        self.logger.info("fail_htlc")
5✔
1729
        assert self.can_update_ctx(proposer=LOCAL), f"cannot update channel. {self.get_state()!r} {self.peer_state!r}"
5✔
1730
        with self.db_lock:
5✔
1731
            self.hm.send_fail(htlc_id)
5✔
1732

1733
    def receive_fail_htlc(self, htlc_id: int, *,
5✔
1734
                          error_bytes: Optional[bytes],
1735
                          reason: Optional[OnionRoutingFailure] = None) -> None:
1736
        """Fail a pending offered HTLC.
1737
        Action must be initiated by REMOTE.
1738
        """
1739
        self.logger.info("receive_fail_htlc")
5✔
1740
        assert self.can_update_ctx(proposer=REMOTE), f"cannot update channel. {self.get_state()!r} {self.peer_state!r}"
5✔
1741
        with self.db_lock:
5✔
1742
            self.hm.recv_fail(htlc_id)
5✔
1743
        self._receive_fail_reasons[htlc_id] = (error_bytes, reason)
5✔
1744

1745
    def get_next_fee(self, subject: HTLCOwner) -> int:
5✔
1746
        return self.constraints.capacity - sum(x.value for x in self.get_next_commitment(subject).outputs())
5✔
1747

1748
    def get_latest_fee(self, subject: HTLCOwner) -> int:
5✔
1749
        return self.constraints.capacity - sum(x.value for x in self.get_latest_commitment(subject).outputs())
5✔
1750

1751
    def update_fee(self, feerate: int, from_us: bool) -> None:
5✔
1752
        # feerate uses sat/kw
1753
        if self.constraints.is_initiator != from_us:
5✔
1754
            raise Exception(f"Cannot update_fee: wrong initiator. us: {from_us}")
×
1755
        if feerate < FEERATE_PER_KW_MIN_RELAY_LIGHTNING:
5✔
1756
            raise Exception(f"Cannot update_fee: feerate lower than min relay fee. {feerate} sat/kw. us: {from_us}")
×
1757
        sender = LOCAL if from_us else REMOTE
5✔
1758
        ctx_owner = -sender
5✔
1759
        ctn = self.get_next_ctn(ctx_owner)
5✔
1760
        sender_balance_msat = self.balance_minus_outgoing_htlcs(whose=sender, ctx_owner=ctx_owner, ctn=ctn)
5✔
1761
        sender_reserve_msat = self.config[-sender].reserve_sat * 1000
5✔
1762
        num_htlcs_in_ctx = len(self.included_htlcs(ctx_owner, SENT, ctn=ctn, feerate=feerate) +
5✔
1763
                               self.included_htlcs(ctx_owner, RECEIVED, ctn=ctn, feerate=feerate))
1764
        ctx_fees_msat = calc_fees_for_commitment_tx(
5✔
1765
            num_htlcs=num_htlcs_in_ctx,
1766
            feerate=feerate,
1767
            is_local_initiator=self.constraints.is_initiator,
1768
            has_anchors=self.has_anchors()
1769
        )
1770
        remainder = sender_balance_msat - sender_reserve_msat - ctx_fees_msat[sender]
5✔
1771
        if remainder < 0:
5✔
1772
            raise Exception(f"Cannot update_fee. {sender} tried to update fee but they cannot afford it. "
×
1773
                            f"Their balance would go below reserve: {remainder} msat missing.")
1774
        assert self.can_update_ctx(proposer=LOCAL if from_us else REMOTE), f"cannot update channel. {self.get_state()!r} {self.peer_state!r}. {from_us=}"
5✔
1775
        with self.db_lock:
5✔
1776
            if from_us:
5✔
1777
                self.hm.send_update_fee(feerate)
5✔
1778
            else:
1779
                self.hm.recv_update_fee(feerate)
5✔
1780

1781
    def make_commitment(self, subject: HTLCOwner, this_point: bytes, ctn: int) -> PartialTransaction:
5✔
1782
        assert type(subject) is HTLCOwner
5✔
1783
        feerate = self.get_feerate(subject, ctn=ctn)
5✔
1784
        other = subject.inverted()
5✔
1785
        local_msat = self.balance(subject, ctx_owner=subject, ctn=ctn)
5✔
1786
        remote_msat = self.balance(other, ctx_owner=subject, ctn=ctn)
5✔
1787
        received_htlcs = self.hm.htlcs_by_direction(subject, RECEIVED, ctn).values()
5✔
1788
        sent_htlcs = self.hm.htlcs_by_direction(subject, SENT, ctn).values()
5✔
1789
        remote_msat -= htlcsum(received_htlcs)
5✔
1790
        local_msat -= htlcsum(sent_htlcs)
5✔
1791
        assert remote_msat >= 0
5✔
1792
        assert local_msat >= 0
5✔
1793
        # same htlcs as before, but now without dust.
1794
        received_htlcs = self.included_htlcs(subject, RECEIVED, ctn)
5✔
1795
        sent_htlcs = self.included_htlcs(subject, SENT, ctn)
5✔
1796

1797
        this_config = self.config[subject]
5✔
1798
        other_config = self.config[-subject]
5✔
1799
        other_htlc_pubkey = derive_pubkey(other_config.htlc_basepoint.pubkey, this_point)
5✔
1800
        this_htlc_pubkey = derive_pubkey(this_config.htlc_basepoint.pubkey, this_point)
5✔
1801
        other_revocation_pubkey = derive_blinded_pubkey(other_config.revocation_basepoint.pubkey, this_point)
5✔
1802
        htlcs = []  # type: List[ScriptHtlc]
5✔
1803
        for is_received_htlc, htlc_list in zip((True, False), (received_htlcs, sent_htlcs)):
5✔
1804
            for htlc in htlc_list:
5✔
1805
                htlcs.append(ScriptHtlc(make_htlc_output_witness_script(
5✔
1806
                    is_received_htlc=is_received_htlc,
1807
                    remote_revocation_pubkey=other_revocation_pubkey,
1808
                    remote_htlc_pubkey=other_htlc_pubkey,
1809
                    local_htlc_pubkey=this_htlc_pubkey,
1810
                    payment_hash=htlc.payment_hash,
1811
                    cltv_abs=htlc.cltv_abs,
1812
                    has_anchors=self.has_anchors()), htlc))
1813
        # note: maybe flip initiator here for fee purposes, we want LOCAL and REMOTE
1814
        #       in the resulting dict to correspond to the to_local and to_remote *outputs* of the ctx
1815
        onchain_fees = calc_fees_for_commitment_tx(
5✔
1816
            num_htlcs=len(htlcs),
1817
            feerate=feerate,
1818
            is_local_initiator=self.constraints.is_initiator == (subject == LOCAL),
1819
            has_anchors=self.has_anchors(),
1820
        )
1821
        assert self.is_static_remotekey_enabled()
5✔
1822
        payment_pubkey = other_config.payment_basepoint.pubkey
5✔
1823
        return make_commitment(
5✔
1824
            ctn=ctn,
1825
            local_funding_pubkey=this_config.multisig_key.pubkey,
1826
            remote_funding_pubkey=other_config.multisig_key.pubkey,
1827
            remote_payment_pubkey=payment_pubkey,
1828
            funder_payment_basepoint=self.config[LOCAL if     self.constraints.is_initiator else REMOTE].payment_basepoint.pubkey,
1829
            fundee_payment_basepoint=self.config[LOCAL if not self.constraints.is_initiator else REMOTE].payment_basepoint.pubkey,
1830
            revocation_pubkey=other_revocation_pubkey,
1831
            delayed_pubkey=derive_pubkey(this_config.delayed_basepoint.pubkey, this_point),
1832
            to_self_delay=other_config.to_self_delay,
1833
            funding_txid=self.funding_outpoint.txid,
1834
            funding_pos=self.funding_outpoint.output_index,
1835
            funding_sat=self.constraints.capacity,
1836
            local_amount=local_msat,
1837
            remote_amount=remote_msat,
1838
            dust_limit_sat=this_config.dust_limit_sat,
1839
            fees_per_participant=onchain_fees,
1840
            htlcs=htlcs,
1841
            has_anchors=self.has_anchors()
1842
        )
1843

1844
    def make_closing_tx(self, local_script: bytes, remote_script: bytes,
5✔
1845
                        fee_sat: int, *, drop_remote = False) -> Tuple[bytes, PartialTransaction]:
1846
        """ cooperative close """
1847
        _, outputs = make_commitment_outputs(
5✔
1848
            fees_per_participant={
1849
                LOCAL: fee_sat * 1000 if self.constraints.is_initiator else 0,
1850
                REMOTE: fee_sat * 1000 if not self.constraints.is_initiator else 0,
1851
            },
1852
            local_amount_msat=self.balance(LOCAL),
1853
            remote_amount_msat=self.balance(REMOTE) if not drop_remote else 0,
1854
            local_script=local_script,
1855
            remote_script=remote_script,
1856
            htlcs=[],
1857
            dust_limit_sat=self.config[LOCAL].dust_limit_sat,
1858
            has_anchors=self.has_anchors(),
1859
            local_anchor_script=None,
1860
            remote_anchor_script=None,
1861
        )
1862

1863
        closing_tx = make_closing_tx(self.config[LOCAL].multisig_key.pubkey,
5✔
1864
                                     self.config[REMOTE].multisig_key.pubkey,
1865
                                     funding_txid=self.funding_outpoint.txid,
1866
                                     funding_pos=self.funding_outpoint.output_index,
1867
                                     funding_sat=self.constraints.capacity,
1868
                                     outputs=outputs)
1869

1870
        der_sig = closing_tx.sign_txin(0, self.config[LOCAL].multisig_key.privkey)
5✔
1871
        sig = ecc.ecdsa_sig64_from_der_sig(der_sig[:-1])
5✔
1872
        return sig, closing_tx
5✔
1873

1874
    def signature_fits(self, tx: PartialTransaction) -> bool:
5✔
1875
        remote_sig = self.config[LOCAL].current_commitment_signature
5✔
1876
        pre_hash = tx.serialize_preimage(0)
5✔
1877
        msg_hash = sha256d(pre_hash)
5✔
1878
        assert remote_sig
5✔
1879
        res = ECPubkey(self.config[REMOTE].multisig_key.pubkey).ecdsa_verify(remote_sig, msg_hash)
5✔
1880
        return res
5✔
1881

1882
    def force_close_tx(self) -> PartialTransaction:
5✔
1883
        tx = self.get_latest_commitment(LOCAL)
5✔
1884
        assert self.signature_fits(tx)
5✔
1885
        tx.sign({self.config[LOCAL].multisig_key.pubkey: self.config[LOCAL].multisig_key.privkey})
5✔
1886
        remote_sig = self.config[LOCAL].current_commitment_signature
5✔
1887
        remote_sig = ecc.ecdsa_der_sig_from_ecdsa_sig64(remote_sig) + Sighash.to_sigbytes(Sighash.ALL)
5✔
1888
        tx.add_signature_to_txin(txin_idx=0,
5✔
1889
                                 signing_pubkey=self.config[REMOTE].multisig_key.pubkey,
1890
                                 sig=remote_sig)
1891
        assert tx.is_complete()
5✔
1892
        return tx
5✔
1893

1894
    def get_close_options(self) -> Sequence[ChanCloseOption]:
5✔
1895
        # This method is used both in the GUI, and in lnpeer.schedule_force_closing
1896
        # in the latter case, the result does not depend on peer_state
1897
        ret = []
5✔
1898
        if not self.is_closed() and self.peer_state == PeerState.GOOD:
5✔
1899
            # If there are unsettled HTLCs, although is possible to cooperatively close,
1900
            # we choose not to expose that option in the GUI, because it is very likely
1901
            # that HTLCs will take a long time to settle (submarine swap, or stuck payment),
1902
            # and the close dialog would be taking a very long time to finish
1903
            if not self.has_unsettled_htlcs():
5✔
1904
                ret.append(ChanCloseOption.COOP_CLOSE)
5✔
1905
                ret.append(ChanCloseOption.REQUEST_REMOTE_FCLOSE)
5✔
1906
        if self.get_state() == ChannelState.WE_ARE_TOXIC:
5✔
1907
            ret.append(ChanCloseOption.REQUEST_REMOTE_FCLOSE)
×
1908
        if not self.is_closed() or self.get_state() == ChannelState.REQUESTED_FCLOSE:
5✔
1909
            ret.append(ChanCloseOption.LOCAL_FCLOSE)
5✔
1910
        assert not (self.get_state() == ChannelState.WE_ARE_TOXIC and ChanCloseOption.LOCAL_FCLOSE in ret), "local force-close unsafe if we are toxic"
5✔
1911
        return ret
5✔
1912

1913
    def maybe_sweep_htlcs(self, ctx: Transaction, htlc_tx: Transaction) -> Dict[str, SweepInfo]:
5✔
1914
        # look at the output address, check if it matches
1915
        d = sweep_their_htlctx_justice(self, ctx, htlc_tx)
×
1916
        d2 = sweep_our_htlctx(self, ctx, htlc_tx)
×
1917
        d.update(d2)
×
1918
        return d
×
1919

1920
    def has_pending_changes(self, subject: HTLCOwner) -> bool:
5✔
1921
        next_htlcs = self.hm.get_htlcs_in_next_ctx(subject)
5✔
1922
        latest_htlcs = self.hm.get_htlcs_in_latest_ctx(subject)
5✔
1923
        return not (next_htlcs == latest_htlcs and self.get_next_feerate(subject) == self.get_latest_feerate(subject))
5✔
1924

1925
    def should_be_closed_due_to_expiring_htlcs(self, local_height: int) -> bool:
5✔
1926
        htlcs_we_could_reclaim = {}  # type: Dict[Tuple[Direction, int], UpdateAddHtlc]
×
1927
        # If there is a received HTLC for which we already released the preimage
1928
        # but the remote did not revoke yet, and the CLTV of this HTLC is dangerously close
1929
        # to the present, then unilaterally close channel
1930
        recv_htlc_deadline_delta = lnutil.NBLOCK_DEADLINE_DELTA_BEFORE_EXPIRY_FOR_RECEIVED_HTLCS
×
1931
        for sub, dir, ctn in ((LOCAL, RECEIVED, self.get_latest_ctn(LOCAL)),
×
1932
                              (REMOTE, SENT, self.get_oldest_unrevoked_ctn(REMOTE)),
1933
                              (REMOTE, SENT, self.get_latest_ctn(REMOTE)),):
1934
            for htlc_id, htlc in self.hm.htlcs_by_direction(subject=sub, direction=dir, ctn=ctn).items():
×
1935
                if not self.hm.was_htlc_preimage_released(htlc_id=htlc_id, htlc_proposer=REMOTE):
×
1936
                    continue
×
1937
                if htlc.cltv_abs - recv_htlc_deadline_delta > local_height:
×
1938
                    continue
×
1939
                # Do not force-close if we just sent fulfill_htlc and have not received revack yet
1940
                if htlc_id in self.htlc_settle_time and now() - self.htlc_settle_time[htlc_id] < 30:
×
1941
                    continue
×
1942
                htlcs_we_could_reclaim[(RECEIVED, htlc_id)] = htlc
×
1943
        # If there is an offered HTLC which has already expired (+ some grace period after), we
1944
        # will unilaterally close the channel and time out the HTLC
1945
        offered_htlc_deadline_delta = lnutil.NBLOCK_DEADLINE_DELTA_AFTER_EXPIRY_FOR_OFFERED_HTLCS
×
1946
        for sub, dir, ctn in ((LOCAL, SENT, self.get_latest_ctn(LOCAL)),
×
1947
                              (REMOTE, RECEIVED, self.get_oldest_unrevoked_ctn(REMOTE)),
1948
                              (REMOTE, RECEIVED, self.get_latest_ctn(REMOTE)),):
1949
            for htlc_id, htlc in self.hm.htlcs_by_direction(subject=sub, direction=dir, ctn=ctn).items():
×
1950
                if htlc.cltv_abs + offered_htlc_deadline_delta > local_height:
×
1951
                    continue
×
1952
                htlcs_we_could_reclaim[(SENT, htlc_id)] = htlc
×
1953
        # Note: previously we used a threshold concept, "min_value_worth_closing_channel_over_sat", and
1954
        #       only force-closed the channel if the total value of these expiring htlcs was large enough.
1955
        #       However, if we are forwarding, and an outgoing htlc expires, we should always close
1956
        #       the outgoing channel (regardless of htlc value), so that we can propagate back the
1957
        #       removal of the htlc in the incoming channel.
1958
        return len(htlcs_we_could_reclaim) > 0
×
1959

1960
    def is_funding_tx_mined(self, funding_height):
5✔
1961
        funding_txid = self.funding_outpoint.txid
×
1962
        funding_idx = self.funding_outpoint.output_index
×
1963
        conf = funding_height.conf
×
1964
        if conf < self.funding_txn_minimum_depth():
×
1965
            #self.logger.info(f"funding tx is still not at sufficient depth. actual depth: {conf}")
1966
            return False
×
1967
        assert conf > 0 or self.is_zeroconf()
×
1968
        # check funding_tx amount and script
1969
        funding_tx = self.lnworker.lnwatcher.adb.get_transaction(funding_txid)
×
1970
        if not funding_tx:
×
1971
            self.logger.info(f"no funding_tx {funding_txid}")
×
1972
            return False
×
1973
        outp = funding_tx.outputs()[funding_idx]
×
1974
        redeem_script = funding_output_script(self.config[REMOTE], self.config[LOCAL])
×
1975
        funding_address = redeem_script_to_address('p2wsh', redeem_script)
×
1976
        funding_sat = self.constraints.capacity
×
1977
        if not (outp.address == funding_address and outp.value == funding_sat):
×
1978
            self.logger.info('funding outpoint mismatch')
×
1979
            return False
×
1980
        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

© 2026 Coveralls, Inc