2018-10-25 19:16:55 +02:00
# Copyright (C) 2018 The Electrum developers
# Copyright (C) 2015-2018 The Lightning Network Developers
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
2018-10-26 15:00:43 +02:00
# API (method signatures and docstrings) partially copied from lnd
2018-10-25 19:16:55 +02:00
# 42de4400bff5105352d0552155f73589166d162b
2019-01-23 15:10:11 +01:00
import os
2018-10-11 17:06:37 +02:00
from collections import namedtuple , defaultdict
2018-06-27 20:23:03 +02:00
import binascii
import json
2019-10-29 08:02:14 +01:00
from enum import IntEnum
2020-03-06 03:37:00 +01:00
from typing import Optional , Dict , List , Tuple , NamedTuple , Set , Callable , Iterable , Sequence , TYPE_CHECKING , Iterator
2019-02-26 21:28:11 +01:00
import time
2020-01-30 18:09:32 +01:00
import threading
2018-09-12 16:17:10 +02:00
2020-03-06 03:37:00 +01:00
from aiorpcx import NetAddress
2019-01-21 21:27:27 +01:00
from . import ecc
2020-02-17 20:38:41 +01:00
from . import constants
2019-10-11 10:11:41 +02:00
from . util import bfh , bh2u
2018-07-19 10:39:29 +02:00
from . bitcoin import redeem_script_to_address
2018-10-25 23:30:36 +02:00
from . crypto import sha256 , sha256d
2019-10-23 17:09:41 +02:00
from . transaction import Transaction , PartialTransaction
2019-05-02 17:59:11 +02:00
from . logging import Logger
2019-10-09 19:23:09 +02:00
from . lnonion import decode_onion_error
2020-02-17 20:38:41 +01:00
from . import lnutil
2019-01-21 21:27:27 +01:00
from . lnutil import ( Outpoint , LocalConfig , RemoteConfig , Keypair , OnlyPubkeyKeypair , ChannelConstraints ,
get_per_commitment_secret_from_seed , secret_to_pubkey , derive_privkey , make_closing_tx ,
sign_and_get_sig_string , RevocationStore , derive_blinded_pubkey , Direction , derive_pubkey ,
make_htlc_tx_with_open_channel , make_commitment , make_received_htlc , make_offered_htlc ,
HTLC_TIMEOUT_WEIGHT , HTLC_SUCCESS_WEIGHT , extract_ctn_from_tx_and_chan , UpdateAddHtlc ,
funding_output_script , SENT , RECEIVED , LOCAL , REMOTE , HTLCOwner , make_commitment_outputs ,
2019-09-06 18:09:05 +02:00
ScriptHtlc , PaymentFailure , calc_onchain_fees , RemoteMisbehaving , make_htlc_output_witness_script ,
2020-03-06 03:37:00 +01:00
ShortChannelID , map_htlcs_to_ctx_output_idxs , LNPeerAddr )
2019-06-24 11:13:18 +02:00
from . lnsweep import create_sweeptxs_for_our_ctx , create_sweeptxs_for_their_ctx
2019-09-09 19:38:35 +02:00
from . lnsweep import create_sweeptx_for_their_revoked_htlc , SweepInfo
2019-01-21 21:27:27 +01:00
from . lnhtlc import HTLCManager
2020-02-17 20:38:41 +01:00
from . lnmsg import encode_msg , decode_msg
2018-10-23 16:44:39 +02:00
2019-09-22 20:46:01 +02:00
if TYPE_CHECKING :
from . lnworker import LNWallet
2020-02-04 13:35:58 +01:00
from . json_db import StoredDict
2019-09-22 20:46:01 +02:00
2018-09-12 16:17:10 +02:00
2019-10-29 08:02:14 +01:00
# lightning channel states
2020-02-14 16:15:25 +01:00
# Note: these states are persisted by name (for a given channel) in the wallet file,
# so consider doing a wallet db upgrade when changing them.
2019-10-29 08:02:14 +01:00
class channel_states ( IntEnum ) :
2020-02-23 14:54:04 +01:00
PREOPENING = 0 # Initial negotiation. Channel will not be reestablished
OPENING = 1 # Channel will be reestablished. (per BOLT2)
2020-02-27 15:22:22 +01:00
# - Funding node: has received funding_signed (can broadcast the funding tx)
2020-02-23 14:54:04 +01:00
# - Non-funding node: has sent the funding_signed message.
FUNDED = 2 # Funding tx was mined (requires min_depth and tx verification)
2019-10-29 08:02:14 +01:00
OPEN = 3 # both parties have sent funding_locked
2020-03-06 11:25:34 +01:00
CLOSING = 4 # shutdown has been sent.
FORCE_CLOSING = 5 # force-close tx has been broadcast
2019-10-29 08:02:14 +01:00
CLOSED = 6 # funding txo has been spent
REDEEMED = 7 # we can stop watching
class peer_states ( IntEnum ) :
DISCONNECTED = 0
REESTABLISHING = 1
GOOD = 2
2020-02-11 20:55:52 +01:00
BAD = 3
2019-10-29 08:02:14 +01:00
cs = channel_states
state_transitions = [
( cs . PREOPENING , cs . OPENING ) ,
( cs . OPENING , cs . FUNDED ) ,
( cs . FUNDED , cs . OPEN ) ,
( cs . OPENING , cs . CLOSING ) ,
( cs . FUNDED , cs . CLOSING ) ,
( cs . OPEN , cs . CLOSING ) ,
( cs . OPENING , cs . FORCE_CLOSING ) ,
( cs . FUNDED , cs . FORCE_CLOSING ) ,
( cs . OPEN , cs . FORCE_CLOSING ) ,
( cs . CLOSING , cs . FORCE_CLOSING ) ,
( cs . OPENING , cs . CLOSED ) ,
( cs . FUNDED , cs . CLOSED ) ,
( cs . OPEN , cs . CLOSED ) ,
2020-02-26 09:04:54 +01:00
( cs . CLOSING , cs . CLOSING ) , # if we reestablish
2019-10-29 08:02:14 +01:00
( cs . CLOSING , cs . CLOSED ) ,
2020-03-06 12:17:26 +01:00
( cs . FORCE_CLOSING , cs . FORCE_CLOSING ) , # allow multiple attempts
2019-10-29 08:02:14 +01:00
( cs . FORCE_CLOSING , cs . CLOSED ) ,
2020-03-06 11:40:08 +01:00
( cs . FORCE_CLOSING , cs . REDEEMED ) ,
2019-10-29 08:02:14 +01:00
( cs . CLOSED , cs . REDEEMED ) ,
2020-02-24 13:02:11 +01:00
( cs . OPENING , cs . REDEEMED ) , # channel never funded (dropped from mempool)
2020-02-24 12:01:54 +01:00
( cs . PREOPENING , cs . REDEEMED ) , # channel never funded
2019-10-29 08:02:14 +01:00
]
2020-02-14 16:15:25 +01:00
del cs # delete as name is ambiguous without context
2019-10-29 08:02:14 +01:00
2018-05-29 11:51:48 +02:00
2018-06-14 15:34:51 +02:00
RevokeAndAck = namedtuple ( " RevokeAndAck " , [ " per_commitment_secret " , " next_per_commitment_point " ] )
2018-05-29 11:51:48 +02:00
2019-07-23 19:23:39 +02:00
2019-08-02 18:04:13 +02:00
class RemoteCtnTooFarInFuture ( Exception ) : pass
2018-07-02 17:51:57 +02:00
2018-10-10 22:54:30 +02:00
def htlcsum ( htlcs ) :
return sum ( [ x . amount_msat for x in htlcs ] )
2018-05-29 11:51:48 +02:00
2019-07-23 19:23:39 +02:00
2019-05-02 17:59:11 +02:00
class Channel ( Logger ) :
2019-07-27 00:59:51 +02:00
# note: try to avoid naming ctns/ctxs/etc as "current" and "pending".
# they are ambiguous. Use "oldest_unrevoked" or "latest" or "next".
# TODO enforce this ^
2018-05-29 11:51:48 +02:00
def diagnostic_name ( self ) :
2018-10-22 15:05:58 +02:00
if self . name :
return str ( self . name )
try :
2019-02-09 10:29:33 +01:00
return f " lnchannel_ { bh2u ( self . channel_id [ - 4 : ] ) } "
2018-10-22 15:05:58 +02:00
except :
return super ( ) . diagnostic_name ( )
2018-05-29 11:51:48 +02:00
2020-02-04 13:35:58 +01:00
def __init__ ( self , state : ' StoredDict ' , * , sweep_address = None , name = None , lnworker = None , initial_feerate = None ) :
2019-07-23 19:23:39 +02:00
self . name = name
Logger . __init__ ( self )
2019-09-22 20:46:01 +02:00
self . lnworker = lnworker # type: Optional[LNWallet]
2018-12-15 11:38:46 +01:00
self . sweep_address = sweep_address
2020-02-04 13:35:58 +01:00
self . storage = state
self . db_lock = self . storage . db . lock if self . storage . db else threading . RLock ( )
2018-10-10 22:54:30 +02:00
self . config = { }
self . config [ LOCAL ] = state [ " local_config " ]
self . config [ REMOTE ] = state [ " remote_config " ]
2020-02-04 13:35:58 +01:00
self . channel_id = bfh ( state [ " channel_id " ] )
self . constraints = state [ " constraints " ]
self . funding_outpoint = state [ " funding_outpoint " ]
self . node_id = bfh ( state [ " node_id " ] )
2019-09-06 18:09:05 +02:00
self . short_channel_id = ShortChannelID . normalize ( state [ " short_channel_id " ] )
2020-02-04 13:35:58 +01:00
self . onion_keys = state [ ' onion_keys ' ]
self . data_loss_protect_remote_pcp = state [ ' data_loss_protect_remote_pcp ' ]
self . hm = HTLCManager ( log = state [ ' log ' ] , initial_feerate = initial_feerate )
2019-10-29 08:02:14 +01:00
self . _state = channel_states [ state [ ' state ' ] ]
self . peer_state = peer_states . DISCONNECTED
2019-09-09 19:38:35 +02:00
self . sweep_info = { } # type: Dict[str, Dict[str, SweepInfo]]
2019-08-16 22:35:25 +02:00
self . _outgoing_channel_update = None # type: Optional[bytes]
2020-02-17 20:38:41 +01:00
self . _chan_ann_without_sigs = None # type: Optional[bytes]
2020-01-30 18:09:32 +01:00
self . revocation_store = RevocationStore ( state [ " revocation_store " ] )
2020-02-26 20:35:46 +01:00
self . _can_send_ctx_updates = True # type: bool
2020-03-04 11:54:42 +01:00
self . _receive_fail_reasons = { }
2018-12-15 11:38:46 +01:00
2020-02-24 16:32:18 +01:00
def get_id_for_log ( self ) - > str :
scid = self . short_channel_id
if scid :
return str ( scid )
return self . channel_id . hex ( )
2020-02-04 13:35:58 +01:00
def set_onion_key ( self , key , value ) :
self . onion_keys [ key ] = value
def get_onion_key ( self , key ) :
return self . onion_keys . get ( key )
def set_data_loss_protect_remote_pcp ( self , key , value ) :
self . data_loss_protect_remote_pcp [ key ] = value
def get_data_loss_protect_remote_pcp ( self , key ) :
2020-02-12 14:19:31 +01:00
return self . data_loss_protect_remote_pcp . get ( key )
2020-02-04 13:35:58 +01:00
2020-02-17 20:38:41 +01:00
def get_local_pubkey ( self ) - > bytes :
if not self . lnworker :
raise Exception ( ' lnworker not set for channel! ' )
return self . lnworker . node_keypair . pubkey
def set_remote_update ( self , raw : bytes ) - > None :
2020-02-04 13:35:58 +01:00
self . storage [ ' remote_update ' ] = raw . hex ( )
2020-02-17 20:38:41 +01:00
def get_remote_update ( self ) - > Optional [ bytes ] :
2020-02-04 13:35:58 +01:00
return bfh ( self . storage . get ( ' remote_update ' ) ) if self . storage . get ( ' remote_update ' ) else None
2020-03-06 03:37:00 +01:00
def add_or_update_peer_addr ( self , peer : LNPeerAddr ) - > None :
if ' peer_network_addresses ' not in self . storage :
self . storage [ ' peer_network_addresses ' ] = { }
now = int ( time . time ( ) )
self . storage [ ' peer_network_addresses ' ] [ peer . net_addr_str ( ) ] = now
def get_peer_addresses ( self ) - > Iterator [ LNPeerAddr ] :
# sort by timestamp: most recent first
addrs = sorted ( self . storage . get ( ' peer_network_addresses ' , { } ) . items ( ) ,
key = lambda x : x [ 1 ] , reverse = True )
for net_addr_str , ts in addrs :
net_addr = NetAddress . from_string ( net_addr_str )
yield LNPeerAddr ( host = str ( net_addr . host ) , port = net_addr . port , pubkey = self . node_id )
2020-02-17 20:38:41 +01:00
def get_outgoing_gossip_channel_update ( self ) - > bytes :
if self . _outgoing_channel_update is not None :
return self . _outgoing_channel_update
if not self . lnworker :
raise Exception ( ' lnworker not set for channel! ' )
sorted_node_ids = list ( sorted ( [ self . node_id , self . get_local_pubkey ( ) ] ) )
channel_flags = b ' \x00 ' if sorted_node_ids [ 0 ] == self . get_local_pubkey ( ) else b ' \x01 '
now = int ( time . time ( ) )
htlc_maximum_msat = min ( self . config [ REMOTE ] . max_htlc_value_in_flight_msat , 1000 * self . constraints . capacity )
chan_upd = encode_msg (
" channel_update " ,
short_channel_id = self . short_channel_id ,
channel_flags = channel_flags ,
message_flags = b ' \x01 ' ,
cltv_expiry_delta = lnutil . NBLOCK_OUR_CLTV_EXPIRY_DELTA . to_bytes ( 2 , byteorder = " big " ) ,
htlc_minimum_msat = self . config [ REMOTE ] . htlc_minimum_msat . to_bytes ( 8 , byteorder = " big " ) ,
htlc_maximum_msat = htlc_maximum_msat . to_bytes ( 8 , byteorder = " big " ) ,
fee_base_msat = lnutil . OUR_FEE_BASE_MSAT . to_bytes ( 4 , byteorder = " big " ) ,
fee_proportional_millionths = lnutil . OUR_FEE_PROPORTIONAL_MILLIONTHS . to_bytes ( 4 , byteorder = " big " ) ,
chain_hash = constants . net . rev_genesis_bytes ( ) ,
timestamp = now . to_bytes ( 4 , byteorder = " big " ) ,
)
sighash = sha256d ( chan_upd [ 2 + 64 : ] )
sig = ecc . ECPrivkey ( self . lnworker . node_keypair . privkey ) . sign ( sighash , ecc . sig_string_from_r_and_s )
message_type , payload = decode_msg ( chan_upd )
payload [ ' signature ' ] = sig
chan_upd = encode_msg ( message_type , * * payload )
self . _outgoing_channel_update = chan_upd
return chan_upd
def construct_channel_announcement_without_sigs ( self ) - > bytes :
if self . _chan_ann_without_sigs is not None :
return self . _chan_ann_without_sigs
if not self . lnworker :
raise Exception ( ' lnworker not set for channel! ' )
bitcoin_keys = [ self . config [ REMOTE ] . multisig_key . pubkey ,
self . config [ LOCAL ] . multisig_key . pubkey ]
node_ids = [ self . node_id , self . get_local_pubkey ( ) ]
sorted_node_ids = list ( sorted ( node_ids ) )
if sorted_node_ids != node_ids :
node_ids = sorted_node_ids
bitcoin_keys . reverse ( )
chan_ann = encode_msg ( " channel_announcement " ,
len = 0 ,
features = b ' ' ,
chain_hash = constants . net . rev_genesis_bytes ( ) ,
short_channel_id = self . short_channel_id ,
node_id_1 = node_ids [ 0 ] ,
node_id_2 = node_ids [ 1 ] ,
bitcoin_key_1 = bitcoin_keys [ 0 ] ,
bitcoin_key_2 = bitcoin_keys [ 1 ]
)
self . _chan_ann_without_sigs = chan_ann
return chan_ann
2019-12-13 14:07:11 +01:00
def is_static_remotekey_enabled ( self ) :
return self . storage . get ( ' static_remotekey_enabled ' )
2020-02-04 13:35:58 +01:00
def set_short_channel_id ( self , short_id ) :
self . short_channel_id = short_id
self . storage [ " short_channel_id " ] = short_id
2019-07-27 01:05:37 +02:00
def get_feerate ( self , subject , ctn ) :
return self . hm . get_feerate ( subject , ctn )
def get_oldest_unrevoked_feerate ( self , subject ) :
return self . hm . get_feerate_in_oldest_unrevoked_ctx ( subject )
def get_latest_feerate ( self , subject ) :
return self . hm . get_feerate_in_latest_ctx ( subject )
2019-07-23 19:23:39 +02:00
def get_next_feerate ( self , subject ) :
2019-07-27 01:05:37 +02:00
return self . hm . get_feerate_in_next_ctx ( subject )
2019-07-23 19:23:39 +02:00
2020-02-18 17:40:13 +01:00
def get_payments ( self ) :
out = [ ]
for subject in LOCAL , REMOTE :
log = self . hm . log [ subject ]
for htlc_id , htlc in log . get ( ' adds ' , { } ) . items ( ) :
if htlc_id in log . get ( ' fails ' , { } ) :
status = ' failed '
elif htlc_id in log . get ( ' settles ' , { } ) :
status = ' settled '
else :
status = ' inflight '
direction = SENT if subject is LOCAL else RECEIVED
rhash = bh2u ( htlc . payment_hash )
out . append ( ( rhash , self . channel_id , htlc , direction , status ) )
return out
2020-02-17 12:19:18 +01:00
def get_settled_payments ( self ) :
2019-01-29 19:01:04 +01:00
out = { }
for subject in LOCAL , REMOTE :
log = self . hm . log [ subject ]
for htlc_id , htlc in log . get ( ' adds ' , { } ) . items ( ) :
2020-02-17 12:19:18 +01:00
if htlc_id in log . get ( ' settles ' , { } ) :
direction = SENT if subject is LOCAL else RECEIVED
rhash = bh2u ( htlc . payment_hash )
out [ rhash ] = ( self . channel_id , htlc , direction )
2019-01-29 19:01:04 +01:00
return out
2020-02-26 20:35:46 +01:00
def open_with_first_pcp ( self , remote_pcp : bytes , remote_sig : bytes ) - > None :
2020-01-30 18:09:32 +01:00
with self . db_lock :
2020-02-26 20:35:46 +01:00
self . config [ REMOTE ] . current_per_commitment_point = remote_pcp
self . config [ REMOTE ] . next_per_commitment_point = None
self . config [ LOCAL ] . current_commitment_signature = remote_sig
2020-01-30 18:09:32 +01:00
self . hm . channel_open_finished ( )
self . peer_state = peer_states . GOOD
2019-03-07 16:15:06 +01:00
2019-10-29 08:02:14 +01:00
def set_state ( self , state ) :
""" set on-chain state """
2019-11-22 21:37:15 +01:00
old_state = self . _state
if ( old_state , state ) not in state_transitions :
raise Exception ( f " Transition not allowed: { old_state . name } -> { state . name } " )
self . logger . debug ( f ' Setting channel state: { old_state . name } -> { state . name } ' )
2020-02-04 13:35:58 +01:00
self . _state = state
self . storage [ ' state ' ] = self . _state . name
2019-10-29 08:02:14 +01:00
if self . lnworker :
self . lnworker . save_channel ( self )
self . lnworker . network . trigger_callback ( ' channel ' , self )
2018-07-30 13:51:03 +02:00
def get_state ( self ) :
return self . _state
2020-02-19 16:03:21 +01:00
def is_open ( self ) :
return self . get_state ( ) == channel_states . OPEN
2020-02-14 13:25:04 +01:00
def is_closing ( self ) :
return self . get_state ( ) in [ channel_states . CLOSING , channel_states . FORCE_CLOSING ]
2019-01-30 19:40:20 +01:00
def is_closed ( self ) :
2020-02-14 13:25:04 +01:00
# the closing txid has been saved
return self . get_state ( ) > = channel_states . CLOSED
2019-01-30 19:40:20 +01:00
2020-02-26 20:35:46 +01:00
def set_can_send_ctx_updates ( self , b : bool ) - > None :
self . _can_send_ctx_updates = b
def can_send_ctx_updates ( self ) - > bool :
""" Whether we can send update_fee, update_*_htlc changes to the remote. """
2020-02-27 14:40:58 +01:00
if not ( self . is_open ( ) or self . is_closing ( ) ) :
2020-02-26 20:35:46 +01:00
return False
if self . peer_state != peer_states . GOOD :
return False
if not self . _can_send_ctx_updates :
return False
return True
2020-02-27 14:40:58 +01:00
def can_send_update_add_htlc ( self ) - > bool :
return self . can_send_ctx_updates ( ) and not self . is_closing ( )
2020-02-22 18:26:52 +01:00
def save_funding_height ( self , txid , height , timestamp ) :
self . storage [ ' funding_height ' ] = txid , height , timestamp
def get_funding_height ( self ) :
return self . storage . get ( ' funding_height ' )
def delete_funding_height ( self ) :
self . storage . pop ( ' funding_height ' , None )
def save_closing_height ( self , txid , height , timestamp ) :
self . storage [ ' closing_height ' ] = txid , height , timestamp
def get_closing_height ( self ) :
return self . storage . get ( ' closing_height ' )
2020-02-22 12:28:07 +01:00
2020-02-16 18:54:27 +01:00
def is_redeemed ( self ) :
return self . get_state ( ) == channel_states . REDEEMED
2018-10-23 20:32:18 +02:00
def _check_can_pay ( self , amount_msat : int ) - > None :
2019-07-27 00:59:51 +02:00
# TODO check if this method uses correct ctns (should use "latest" + 1)
2019-05-19 11:55:55 +02:00
if self . is_closed ( ) :
raise PaymentFailure ( ' Channel closed ' )
2019-10-29 08:02:14 +01:00
if self . get_state ( ) != channel_states . OPEN :
raise PaymentFailure ( ' Channel not open ' , self . get_state ( ) )
2020-02-26 20:35:46 +01:00
if not self . can_send_ctx_updates ( ) :
raise PaymentFailure ( ' Channel cannot send ctx updates ' )
2018-10-17 20:01:45 +02:00
if self . available_to_spend ( LOCAL ) < amount_msat :
2018-10-24 20:39:07 +02:00
raise PaymentFailure ( f ' Not enough local balance. Have: { self . available_to_spend ( LOCAL ) } , Need: { amount_msat } ' )
2019-01-21 21:27:27 +01:00
if len ( self . hm . htlcs ( LOCAL ) ) + 1 > self . config [ REMOTE ] . max_accepted_htlcs :
2018-10-15 12:45:07 +02:00
raise PaymentFailure ( ' Too many HTLCs already in channel ' )
2019-08-14 21:35:37 +02:00
current_htlc_sum = ( htlcsum ( self . hm . htlcs_by_direction ( LOCAL , SENT ) . values ( ) )
+ htlcsum ( self . hm . htlcs_by_direction ( LOCAL , RECEIVED ) . values ( ) ) )
2018-10-23 20:32:18 +02:00
if current_htlc_sum + amount_msat > self . config [ REMOTE ] . max_htlc_value_in_flight_msat :
raise PaymentFailure ( f ' HTLC value sum (sum of pending htlcs: { current_htlc_sum / 1000 } sat plus new htlc: { amount_msat / 1000 } sat) would exceed max allowed: { self . config [ REMOTE ] . max_htlc_value_in_flight_msat / 1000 } sat ' )
2019-01-28 20:13:09 +01:00
if amount_msat < self . config [ REMOTE ] . htlc_minimum_msat :
2018-10-22 20:39:44 +02:00
raise PaymentFailure ( f ' HTLC value too small: { amount_msat } msat ' )
2018-10-15 12:45:07 +02:00
def can_pay ( self , amount_msat ) :
try :
2018-10-23 20:32:18 +02:00
self . _check_can_pay ( amount_msat )
except PaymentFailure :
2018-10-15 12:45:07 +02:00
return False
return True
2018-07-30 13:51:03 +02:00
def should_try_to_reestablish_peer ( self ) - > bool :
2020-03-06 11:25:34 +01:00
return channel_states . PREOPENING < self . _state < channel_states . FORCE_CLOSING and self . peer_state == peer_states . DISCONNECTED
2018-07-16 16:51:32 +02:00
2018-07-19 13:10:41 +02:00
def get_funding_address ( self ) :
2018-10-10 22:54:30 +02:00
script = funding_output_script ( self . config [ LOCAL ] , self . config [ REMOTE ] )
2018-07-19 10:39:29 +02:00
return redeem_script_to_address ( ' p2wsh ' , script )
2019-02-14 17:53:09 +01:00
def add_htlc ( self , htlc : UpdateAddHtlc ) - > UpdateAddHtlc :
2018-05-29 11:51:48 +02:00
"""
AddHTLC adds an HTLC to the state machine ' s local update log. This method
should be called when preparing to send an outgoing HTLC .
2018-10-26 15:00:43 +02:00
This docstring is from LND .
2018-05-29 11:51:48 +02:00
"""
2020-02-26 20:35:46 +01:00
assert self . can_send_ctx_updates ( ) , f " cannot update channel. { self . get_state ( ) !r} { self . peer_state !r} "
2019-02-14 17:53:09 +01:00
if isinstance ( htlc , dict ) : # legacy conversion # FIXME remove
htlc = UpdateAddHtlc ( * * htlc )
assert isinstance ( htlc , UpdateAddHtlc )
self . _check_can_pay ( htlc . amount_msat )
2019-08-02 20:54:41 +02:00
if htlc . htlc_id is None :
htlc = htlc . _replace ( htlc_id = self . hm . get_next_htlc_id ( LOCAL ) )
2020-01-30 18:09:32 +01:00
with self . db_lock :
self . hm . send_htlc ( htlc )
2019-05-02 17:59:11 +02:00
self . logger . info ( " add_htlc " )
2019-02-14 17:53:09 +01:00
return htlc
2018-05-29 11:51:48 +02:00
2020-02-29 09:12:33 +01:00
def receive_htlc ( self , htlc : UpdateAddHtlc , onion_packet : bytes = None ) - > UpdateAddHtlc :
2018-05-29 11:51:48 +02:00
"""
ReceiveHTLC adds an HTLC to the state machine ' s remote update log. This
method should be called in response to receiving a new HTLC from the remote
party .
2018-10-26 15:00:43 +02:00
This docstring is from LND .
2018-05-29 11:51:48 +02:00
"""
2019-02-14 17:53:09 +01:00
if isinstance ( htlc , dict ) : # legacy conversion # FIXME remove
htlc = UpdateAddHtlc ( * * htlc )
assert isinstance ( htlc , UpdateAddHtlc )
2019-08-02 20:54:41 +02:00
if htlc . htlc_id is None : # used in unit tests
htlc = htlc . _replace ( htlc_id = self . hm . get_next_htlc_id ( REMOTE ) )
2019-02-14 21:42:37 +01:00
if 0 < = self . available_to_spend ( REMOTE ) < htlc . amount_msat :
2018-10-24 20:39:07 +02:00
raise RemoteMisbehaving ( ' Remote dipped below channel reserve. ' + \
f ' Available at remote: { self . available_to_spend ( REMOTE ) } , ' + \
f ' HTLC amount: { htlc . amount_msat } ' )
2020-01-30 18:09:32 +01:00
with self . db_lock :
self . hm . recv_htlc ( htlc )
2020-02-29 09:12:33 +01:00
local_ctn = self . get_latest_ctn ( LOCAL )
remote_ctn = self . get_latest_ctn ( REMOTE )
if onion_packet :
2020-03-02 11:05:09 +01:00
self . hm . log [ ' unfulfilled_htlcs ' ] [ htlc . htlc_id ] = local_ctn , remote_ctn , onion_packet . hex ( ) , False
2020-02-29 09:12:33 +01:00
2019-05-02 17:59:11 +02:00
self . logger . info ( " receive_htlc " )
2019-02-14 17:53:09 +01:00
return htlc
2018-05-29 11:51:48 +02:00
def sign_next_commitment ( self ) :
"""
SignNextCommitment signs a new commitment which includes any previous
unsettled HTLCs , any new HTLCs , and any modifications to prior HTLCs
2018-10-26 15:00:43 +02:00
committed in previous commitment updates .
2018-05-29 11:51:48 +02:00
The first return parameter is the signature for the commitment transaction
2018-10-26 15:00:43 +02:00
itself , while the second parameter is are all HTLC signatures concatenated .
2018-05-29 11:51:48 +02:00
any ) . The HTLC signatures are sorted according to the BIP 69 order of the
HTLC ' s on the commitment transaction.
2018-10-26 15:00:43 +02:00
This docstring was adapted from LND .
2018-05-29 11:51:48 +02:00
"""
2019-08-03 17:34:11 +02:00
next_remote_ctn = self . get_next_ctn ( REMOTE )
2019-05-02 17:59:11 +02:00
self . logger . info ( f " sign_next_commitment { next_remote_ctn } " )
2019-08-03 17:34:11 +02:00
pending_remote_commitment = self . get_next_commitment ( REMOTE )
2018-10-10 22:54:30 +02:00
sig_64 = sign_and_get_sig_string ( pending_remote_commitment , self . config [ LOCAL ] , self . config [ REMOTE ] )
2018-07-10 19:26:54 +02:00
their_remote_htlc_privkey_number = derive_privkey (
2018-10-10 22:54:30 +02:00
int . from_bytes ( self . config [ LOCAL ] . htlc_basepoint . privkey , ' big ' ) ,
self . config [ REMOTE ] . next_per_commitment_point )
2018-07-10 19:26:54 +02:00
their_remote_htlc_privkey = their_remote_htlc_privkey_number . to_bytes ( 32 , ' big ' )
htlcsigs = [ ]
2019-09-07 07:37:13 +02:00
htlc_to_ctx_output_idx_map = map_htlcs_to_ctx_output_idxs ( chan = self ,
ctx = pending_remote_commitment ,
2018-10-23 16:44:39 +02:00
pcp = self . config [ REMOTE ] . next_per_commitment_point ,
2019-09-07 07:37:13 +02:00
subject = REMOTE ,
ctn = next_remote_ctn )
for ( direction , htlc ) , ( ctx_output_idx , htlc_relative_idx ) in htlc_to_ctx_output_idx_map . items ( ) :
_script , htlc_tx = make_htlc_tx_with_open_channel ( chan = self ,
pcp = self . config [ REMOTE ] . next_per_commitment_point ,
subject = REMOTE ,
htlc_direction = direction ,
commit = pending_remote_commitment ,
ctx_output_idx = ctx_output_idx ,
htlc = htlc )
sig = bfh ( htlc_tx . sign_txin ( 0 , their_remote_htlc_privkey ) )
htlc_sig = ecc . sig_string_from_der_sig ( sig [ : - 1 ] )
htlcsigs . append ( ( ctx_output_idx , htlc_sig ) )
2018-09-25 17:08:46 +02:00
htlcsigs . sort ( )
2018-09-25 18:04:00 +02:00
htlcsigs = [ x [ 1 ] for x in htlcsigs ]
2020-01-30 18:09:32 +01:00
with self . db_lock :
self . hm . send_ctx ( )
2018-07-10 19:26:54 +02:00
return sig_64 , htlcsigs
2018-05-29 11:51:48 +02:00
def receive_new_commitment ( self , sig , htlc_sigs ) :
"""
ReceiveNewCommitment process a signature for a new commitment state sent by
the remote party . This method should be called in response to the
remote party initiating a new change , or when the remote party sends a
signature fully accepting a new state we ' ve initiated. If we are able to
successfully validate the signature , then the generated commitment is added
to our local commitment chain . Once we send a revocation for our prior
state , then this newly added commitment becomes our current accepted channel
state .
2018-10-26 15:00:43 +02:00
This docstring is from LND .
2018-05-29 11:51:48 +02:00
"""
2019-09-07 07:37:13 +02:00
# TODO in many failure cases below, we should "fail" the channel (force-close)
2019-08-03 17:34:11 +02:00
next_local_ctn = self . get_next_ctn ( LOCAL )
2019-09-07 07:37:13 +02:00
self . logger . info ( f " receive_new_commitment. ctn= { next_local_ctn } , len(htlc_sigs)= { len ( htlc_sigs ) } " )
2018-10-26 17:05:03 +02:00
2018-06-11 19:53:15 +02:00
assert len ( htlc_sigs ) == 0 or type ( htlc_sigs [ 0 ] ) is bytes
2019-08-03 17:34:11 +02:00
pending_local_commitment = self . get_next_commitment ( LOCAL )
2018-09-12 16:17:10 +02:00
preimage_hex = pending_local_commitment . serialize_preimage ( 0 )
2018-10-25 23:30:36 +02:00
pre_hash = sha256d ( bfh ( preimage_hex ) )
2018-10-10 22:54:30 +02:00
if not ecc . verify_signature ( self . config [ REMOTE ] . multisig_key . pubkey , sig , pre_hash ) :
2019-08-03 17:34:11 +02:00
raise Exception ( f ' failed verifying signature of our updated commitment transaction: { bh2u ( sig ) } preimage is { preimage_hex } ' )
2018-07-10 19:26:54 +02:00
2018-10-22 18:57:51 +02:00
htlc_sigs_string = b ' ' . join ( htlc_sigs )
2018-07-10 19:26:54 +02:00
2019-09-07 07:37:13 +02:00
_secret , pcp = self . get_secret_and_point ( subject = LOCAL , ctn = next_local_ctn )
htlc_to_ctx_output_idx_map = map_htlcs_to_ctx_output_idxs ( chan = self ,
ctx = pending_local_commitment ,
pcp = pcp ,
subject = LOCAL ,
ctn = next_local_ctn )
if len ( htlc_to_ctx_output_idx_map ) != len ( htlc_sigs ) :
raise Exception ( f ' htlc sigs failure. recv { len ( htlc_sigs ) } sigs, expected { len ( htlc_to_ctx_output_idx_map ) } ' )
for ( direction , htlc ) , ( ctx_output_idx , htlc_relative_idx ) in htlc_to_ctx_output_idx_map . items ( ) :
htlc_sig = htlc_sigs [ htlc_relative_idx ]
self . verify_htlc ( htlc = htlc ,
htlc_sig = htlc_sig ,
htlc_direction = direction ,
pcp = pcp ,
ctx = pending_local_commitment ,
ctx_output_idx = ctx_output_idx )
2020-01-30 18:09:32 +01:00
with self . db_lock :
self . hm . recv_ctx ( )
self . config [ LOCAL ] . current_commitment_signature = sig
self . config [ LOCAL ] . current_htlc_signatures = htlc_sigs_string
2018-11-21 17:33:27 +01:00
2019-09-07 07:37:13 +02:00
def verify_htlc ( self , * , htlc : UpdateAddHtlc , htlc_sig : bytes , htlc_direction : Direction ,
pcp : bytes , ctx : Transaction , ctx_output_idx : int ) - > None :
2018-10-23 16:44:39 +02:00
_script , htlc_tx = make_htlc_tx_with_open_channel ( chan = self ,
2019-09-07 07:37:13 +02:00
pcp = pcp ,
subject = LOCAL ,
htlc_direction = htlc_direction ,
2019-01-21 21:27:27 +01:00
commit = ctx ,
2019-09-07 07:37:13 +02:00
ctx_output_idx = ctx_output_idx ,
2018-10-23 16:44:39 +02:00
htlc = htlc )
2018-10-25 23:30:36 +02:00
pre_hash = sha256d ( bfh ( htlc_tx . serialize_preimage ( 0 ) ) )
2019-09-07 07:37:13 +02:00
remote_htlc_pubkey = derive_pubkey ( self . config [ REMOTE ] . htlc_basepoint . pubkey , pcp )
if not ecc . verify_signature ( remote_htlc_pubkey , htlc_sig , pre_hash ) :
raise Exception ( f ' failed verifying HTLC signatures: { htlc } { htlc_direction } ' )
2018-07-20 16:44:03 +02:00
2019-09-07 07:37:13 +02:00
def get_remote_htlc_sig_for_htlc ( self , * , htlc_relative_idx : int ) - > bytes :
2018-10-23 16:44:39 +02:00
data = self . config [ LOCAL ] . current_htlc_signatures
htlc_sigs = [ data [ i : i + 64 ] for i in range ( 0 , len ( data ) , 64 ) ]
2019-09-07 07:37:13 +02:00
htlc_sig = htlc_sigs [ htlc_relative_idx ]
remote_htlc_sig = ecc . der_sig_from_sig_string ( htlc_sig ) + b ' \x01 '
2018-10-23 16:44:39 +02:00
return remote_htlc_sig
2018-05-29 11:51:48 +02:00
def revoke_current_commitment ( self ) :
2019-05-02 17:59:11 +02:00
self . logger . info ( " revoke_current_commitment " )
2019-08-05 16:49:57 +02:00
new_ctn = self . get_latest_ctn ( LOCAL )
2019-08-03 17:34:11 +02:00
new_ctx = self . get_latest_commitment ( LOCAL )
if not self . signature_fits ( new_ctx ) :
# this should never fail; as receive_new_commitment already did this test
raise Exception ( " refusing to revoke as remote sig does not fit " )
2020-01-30 18:09:32 +01:00
with self . db_lock :
self . hm . send_rev ( )
2019-02-27 10:51:40 +01:00
if self . lnworker :
2020-03-04 18:09:43 +01:00
received = self . hm . received_in_ctn ( new_ctn )
2019-02-27 10:51:40 +01:00
for htlc in received :
2020-03-04 18:09:43 +01:00
self . lnworker . payment_received ( self , htlc . payment_hash )
2019-07-26 17:03:36 +02:00
last_secret , last_point = self . get_secret_and_point ( LOCAL , new_ctn - 1 )
next_secret , next_point = self . get_secret_and_point ( LOCAL , new_ctn + 1 )
2020-03-04 18:09:43 +01:00
return RevokeAndAck ( last_secret , next_point )
2018-06-11 19:53:15 +02:00
2019-02-26 20:56:48 +01:00
def receive_revocation ( self , revocation : RevokeAndAck ) :
2019-05-02 17:59:11 +02:00
self . logger . info ( " receive_revocation " )
2020-03-04 18:09:43 +01:00
new_ctn = self . get_latest_ctn ( REMOTE )
2018-10-10 22:54:30 +02:00
cur_point = self . config [ REMOTE ] . current_per_commitment_point
2018-09-12 16:17:10 +02:00
derived_point = ecc . ECPrivkey ( revocation . per_commitment_secret ) . get_public_key_bytes ( compressed = True )
if cur_point != derived_point :
raise Exception ( ' revoked secret not for current point ' )
2020-01-30 18:09:32 +01:00
with self . db_lock :
self . revocation_store . add_next_entry ( revocation . per_commitment_secret )
##### start applying fee/htlc changes
self . hm . recv_rev ( )
self . config [ REMOTE ] . current_per_commitment_point = self . config [ REMOTE ] . next_per_commitment_point
self . config [ REMOTE ] . next_per_commitment_point = revocation . next_per_commitment_point
2020-03-04 18:09:43 +01:00
# lnworker callbacks
if self . lnworker :
sent = self . hm . sent_in_ctn ( new_ctn )
for htlc in sent :
self . lnworker . payment_sent ( self , htlc . payment_hash )
failed = self . hm . failed_in_ctn ( new_ctn )
for htlc in failed :
reason = self . _receive_fail_reasons . get ( htlc . htlc_id )
self . lnworker . payment_failed ( self , htlc . payment_hash , reason )
2019-01-21 21:27:27 +01:00
2019-03-21 22:52:33 +01:00
def balance ( self , whose , * , ctx_owner = HTLCOwner . LOCAL , ctn = None ) :
2018-10-24 20:39:07 +02:00
"""
This balance in mSAT is not including reserve and fees .
2019-03-21 22:52:33 +01:00
So a node cannot actually use its whole balance .
2018-10-24 20:39:07 +02:00
But this number is simple , since it is derived simply
from the initial balance , and the value of settled HTLCs .
Note that it does not decrease once an HTLC is added ,
failed or fulfilled , since the balance change is only
2019-03-21 22:52:33 +01:00
committed to later when the respective commitment
transaction has been revoked .
2018-10-24 20:39:07 +02:00
"""
2019-03-21 22:52:33 +01:00
assert type ( whose ) is HTLCOwner
initial = self . config [ whose ] . initial_msat
2020-03-06 21:54:05 +01:00
# TODO slow. -- and 'balance' is called from a decent number of places (e.g. 'make_commitment')
2019-03-21 22:52:33 +01:00
for direction , htlc in self . hm . all_settled_htlcs_ever ( ctx_owner , ctn ) :
# note: could "simplify" to (whose * ctx_owner == direction * SENT)
if whose == ctx_owner :
if direction == SENT :
initial - = htlc . amount_msat
else :
initial + = htlc . amount_msat
2019-01-21 21:27:27 +01:00
else :
2019-03-21 22:52:33 +01:00
if direction == SENT :
initial + = htlc . amount_msat
else :
initial - = htlc . amount_msat
2018-09-18 18:38:57 +02:00
return initial
2019-08-05 16:49:57 +02:00
def balance_minus_outgoing_htlcs ( self , whose : HTLCOwner , * , ctx_owner : HTLCOwner = HTLCOwner . LOCAL ) :
2018-10-24 20:39:07 +02:00
"""
This balance in mSAT , which includes the value of
pending outgoing HTLCs , is used in the UI .
"""
2019-03-21 22:52:33 +01:00
assert type ( whose ) is HTLCOwner
2019-08-05 16:49:57 +02:00
ctn = self . get_next_ctn ( ctx_owner )
2020-03-06 09:59:43 +01:00
return self . balance ( whose , ctx_owner = ctx_owner , ctn = ctn ) - self . unsettled_sent_balance ( ctx_owner )
2020-03-06 09:57:37 +01:00
def unsettled_sent_balance ( self , subject : HTLCOwner = LOCAL ) :
ctn = self . get_next_ctn ( subject )
return htlcsum ( self . hm . htlcs_by_direction ( subject , SENT , ctn ) . values ( ) )
2018-10-24 20:39:07 +02:00
def available_to_spend ( self , subject ) :
"""
This balance in mSAT , while technically correct , can
not be used in the UI cause it fluctuates ( commit fee )
"""
2019-03-21 22:52:33 +01:00
# FIXME whose balance? whose ctx?
2019-07-27 00:59:51 +02:00
# FIXME confusing/mixing ctns (should probably use latest_ctn + 1; not oldest_unrevoked + 1)
2019-01-21 21:27:27 +01:00
assert type ( subject ) is HTLCOwner
2019-03-21 22:52:33 +01:00
return self . balance_minus_outgoing_htlcs ( subject , ctx_owner = subject ) \
2018-10-24 20:39:07 +02:00
- self . config [ - subject ] . reserve_sat * 1000 \
2018-10-23 20:32:18 +02:00
- calc_onchain_fees (
# TODO should we include a potential new htlc, when we are called from receive_htlc?
2019-01-21 21:27:27 +01:00
len ( self . included_htlcs ( subject , SENT ) + self . included_htlcs ( subject , RECEIVED ) ) ,
2019-07-27 01:05:37 +02:00
self . get_latest_feerate ( subject ) ,
2018-10-23 20:32:18 +02:00
self . constraints . is_initiator ,
) [ subject ]
2018-10-17 20:01:45 +02:00
2019-01-21 21:27:27 +01:00
def included_htlcs ( self , subject , direction , ctn = None ) :
2018-10-10 22:54:30 +02:00
"""
return filter of non - dust htlcs for subjects commitment transaction , initiated by given party
"""
2019-01-21 21:27:27 +01:00
assert type ( subject ) is HTLCOwner
assert type ( direction ) is Direction
if ctn is None :
2019-08-05 16:49:57 +02:00
ctn = self . get_oldest_unrevoked_ctn ( subject )
2019-07-23 19:23:39 +02:00
feerate = self . get_feerate ( subject , ctn )
2018-10-10 22:54:30 +02:00
conf = self . config [ subject ]
2019-01-21 21:27:27 +01:00
if ( subject , direction ) in [ ( REMOTE , RECEIVED ) , ( LOCAL , SENT ) ] :
weight = HTLC_SUCCESS_WEIGHT
else :
weight = HTLC_TIMEOUT_WEIGHT
2019-08-14 21:35:37 +02:00
htlcs = self . hm . htlcs_by_direction ( subject , direction , ctn = ctn ) . values ( )
2019-09-07 07:37:13 +02:00
htlc_value_after_fees = lambda htlc : htlc . amount_msat / / 1000 - ( weight * feerate / / 1000 )
return list ( filter ( lambda htlc : htlc_value_after_fees ( htlc ) > = conf . dust_limit_sat , htlcs ) )
2018-09-25 17:08:46 +02:00
2019-09-07 07:37:13 +02:00
def get_secret_and_point ( self , subject : HTLCOwner , ctn : int ) - > Tuple [ Optional [ bytes ] , bytes ] :
2019-01-21 21:27:27 +01:00
assert type ( subject ) is HTLCOwner
2019-08-05 17:43:06 +02:00
assert ctn > = 0 , ctn
2019-08-03 17:34:11 +02:00
offset = ctn - self . get_oldest_unrevoked_ctn ( subject )
2019-07-26 17:03:36 +02:00
if subject == REMOTE :
2019-08-02 18:04:13 +02:00
if offset > 1 :
raise RemoteCtnTooFarInFuture ( f " offset: { offset } " )
2019-07-26 17:03:36 +02:00
conf = self . config [ REMOTE ]
if offset == 1 :
secret = None
point = conf . next_per_commitment_point
elif offset == 0 :
secret = None
point = conf . current_per_commitment_point
else :
2020-01-30 18:09:32 +01:00
secret = self . revocation_store . retrieve_secret ( RevocationStore . START_INDEX - ctn )
2019-07-26 17:03:36 +02:00
point = secret_to_pubkey ( int . from_bytes ( secret , ' big ' ) )
else :
secret = get_per_commitment_secret_from_seed ( self . config [ LOCAL ] . per_commitment_secret_seed , RevocationStore . START_INDEX - ctn )
point = secret_to_pubkey ( int . from_bytes ( secret , ' big ' ) )
return secret , point
def get_secret_and_commitment ( self , subject , ctn ) :
secret , point = self . get_secret_and_point ( subject , ctn )
ctx = self . make_commitment ( subject , point , ctn )
return secret , ctx
2019-10-23 17:09:41 +02:00
def get_commitment ( self , subject , ctn ) - > PartialTransaction :
2019-07-26 17:03:36 +02:00
secret , ctx = self . get_secret_and_commitment ( subject , ctn )
return ctx
2019-10-23 17:09:41 +02:00
def get_next_commitment ( self , subject : HTLCOwner ) - > PartialTransaction :
2019-08-03 17:34:11 +02:00
ctn = self . get_next_ctn ( subject )
return self . get_commitment ( subject , ctn )
2019-10-23 17:09:41 +02:00
def get_latest_commitment ( self , subject : HTLCOwner ) - > PartialTransaction :
2019-08-03 17:34:11 +02:00
ctn = self . get_latest_ctn ( subject )
return self . get_commitment ( subject , ctn )
2018-05-29 11:51:48 +02:00
2019-10-23 17:09:41 +02:00
def get_oldest_unrevoked_commitment ( self , subject : HTLCOwner ) - > PartialTransaction :
2019-08-03 17:34:11 +02:00
ctn = self . get_oldest_unrevoked_ctn ( subject )
2019-07-26 17:03:36 +02:00
return self . get_commitment ( subject , ctn )
2018-09-12 23:37:45 +02:00
2019-09-07 07:37:13 +02:00
def create_sweeptxs ( self , ctn : int ) - > List [ Transaction ] :
2019-07-05 14:42:09 +02:00
from . lnsweep import create_sweeptxs_for_watchtower
2019-07-26 17:03:36 +02:00
secret , ctx = self . get_secret_and_commitment ( REMOTE , ctn )
2019-07-05 14:42:09 +02:00
return create_sweeptxs_for_watchtower ( self , ctx , secret , self . sweep_address )
2019-08-03 17:34:11 +02:00
def get_oldest_unrevoked_ctn ( self , subject : HTLCOwner ) - > int :
2019-08-05 16:49:57 +02:00
return self . hm . ctn_oldest_unrevoked ( subject )
2019-02-04 12:37:30 +01:00
2019-08-03 17:34:11 +02:00
def get_latest_ctn ( self , subject : HTLCOwner ) - > int :
return self . hm . ctn_latest ( subject )
def get_next_ctn ( self , subject : HTLCOwner ) - > int :
return self . hm . ctn_latest ( subject ) + 1
2019-01-21 21:27:27 +01:00
def total_msat ( self , direction ) :
2019-03-21 22:51:18 +01:00
""" Return the cumulative total msat amount received/sent so far. """
2019-01-21 21:27:27 +01:00
assert type ( direction ) is Direction
2019-03-21 22:51:18 +01:00
return htlcsum ( self . hm . all_settled_htlcs_ever_by_direction ( LOCAL , direction ) )
2018-05-29 11:51:48 +02:00
2018-06-15 16:35:29 +02:00
def settle_htlc ( self , preimage , htlc_id ) :
2018-05-29 11:51:48 +02:00
"""
SettleHTLC attempts to settle an existing outstanding received HTLC .
"""
2019-05-02 17:59:11 +02:00
self . logger . info ( " settle_htlc " )
2020-02-26 20:35:46 +01:00
assert self . can_send_ctx_updates ( ) , f " cannot update channel. { self . get_state ( ) !r} { self . peer_state !r} "
2019-01-21 21:27:27 +01:00
log = self . hm . log [ REMOTE ]
htlc = log [ ' adds ' ] [ htlc_id ]
2018-05-29 11:51:48 +02:00
assert htlc . payment_hash == sha256 ( preimage )
2019-01-21 21:27:27 +01:00
assert htlc_id not in log [ ' settles ' ]
self . hm . send_settle ( htlc_id )
2018-05-29 11:51:48 +02:00
2019-10-11 10:11:41 +02:00
def get_payment_hash ( self , htlc_id ) :
2019-10-11 12:54:00 +02:00
log = self . hm . log [ LOCAL ]
2019-10-11 10:11:41 +02:00
htlc = log [ ' adds ' ] [ htlc_id ]
return htlc . payment_hash
2019-10-09 19:23:09 +02:00
def decode_onion_error ( self , reason , route , htlc_id ) :
failure_msg , sender_idx = decode_onion_error (
reason ,
[ x . node_id for x in route ] ,
self . onion_keys [ htlc_id ] )
return failure_msg , sender_idx
2018-10-18 22:56:40 +02:00
def receive_htlc_settle ( self , preimage , htlc_id ) :
2019-05-02 17:59:11 +02:00
self . logger . info ( " receive_htlc_settle " )
2019-01-21 21:27:27 +01:00
log = self . hm . log [ LOCAL ]
htlc = log [ ' adds ' ] [ htlc_id ]
2018-05-29 11:51:48 +02:00
assert htlc . payment_hash == sha256 ( preimage )
2019-01-21 21:27:27 +01:00
assert htlc_id not in log [ ' settles ' ]
2020-01-30 18:09:32 +01:00
with self . db_lock :
self . hm . recv_settle ( htlc_id )
2018-06-18 19:46:25 +02:00
2018-10-18 22:56:40 +02:00
def fail_htlc ( self , htlc_id ) :
2019-05-02 17:59:11 +02:00
self . logger . info ( " fail_htlc " )
2020-02-26 20:35:46 +01:00
assert self . can_send_ctx_updates ( ) , f " cannot update channel. { self . get_state ( ) !r} { self . peer_state !r} "
2020-01-30 18:09:32 +01:00
with self . db_lock :
self . hm . send_fail ( htlc_id )
2018-10-18 22:56:40 +02:00
2020-03-04 11:54:42 +01:00
def receive_fail_htlc ( self , htlc_id , reason ) :
2019-05-02 17:59:11 +02:00
self . logger . info ( " receive_fail_htlc " )
2020-01-30 18:09:32 +01:00
with self . db_lock :
self . hm . recv_fail ( htlc_id )
2020-03-04 11:54:42 +01:00
self . _receive_fail_reasons [ htlc_id ] = reason
2018-06-25 21:06:50 +02:00
2018-07-20 16:44:03 +02:00
def pending_local_fee ( self ) :
2019-10-23 17:09:41 +02:00
return self . constraints . capacity - sum ( x . value for x in self . get_next_commitment ( LOCAL ) . outputs ( ) )
2018-06-27 18:33:55 +02:00
2020-02-25 12:35:07 +01:00
def get_latest_fee ( self , subject ) :
return self . constraints . capacity - sum ( x . value for x in self . get_latest_commitment ( subject ) . outputs ( ) )
2019-07-27 01:05:37 +02:00
def update_fee ( self , feerate : int , from_us : bool ) :
# feerate uses sat/kw
if self . constraints . is_initiator != from_us :
raise Exception ( f " Cannot update_fee: wrong initiator. us: { from_us } " )
2020-01-30 18:09:32 +01:00
with self . db_lock :
if from_us :
2020-02-26 20:35:46 +01:00
assert self . can_send_ctx_updates ( ) , f " cannot update channel. { self . get_state ( ) !r} { self . peer_state !r} "
2020-01-30 18:09:32 +01:00
self . hm . send_update_fee ( feerate )
else :
self . hm . recv_update_fee ( feerate )
2018-06-27 18:33:55 +02:00
2019-10-23 17:09:41 +02:00
def make_commitment ( self , subject , this_point , ctn ) - > PartialTransaction :
2019-01-21 21:27:27 +01:00
assert type ( subject ) is HTLCOwner
2019-07-26 17:03:36 +02:00
feerate = self . get_feerate ( subject , ctn )
2019-01-21 21:27:27 +01:00
other = REMOTE if LOCAL == subject else LOCAL
2019-03-21 22:52:33 +01:00
local_msat = self . balance ( subject , ctx_owner = subject , ctn = ctn )
remote_msat = self . balance ( other , ctx_owner = subject , ctn = ctn )
2019-08-14 21:35:37 +02:00
received_htlcs = self . hm . htlcs_by_direction ( subject , SENT if subject == LOCAL else RECEIVED , ctn ) . values ( )
sent_htlcs = self . hm . htlcs_by_direction ( subject , RECEIVED if subject == LOCAL else SENT , ctn ) . values ( )
2019-01-21 21:27:27 +01:00
if subject != LOCAL :
remote_msat - = htlcsum ( received_htlcs )
local_msat - = htlcsum ( sent_htlcs )
else :
remote_msat - = htlcsum ( sent_htlcs )
local_msat - = htlcsum ( received_htlcs )
assert remote_msat > = 0
assert local_msat > = 0
# same htlcs as before, but now without dust.
received_htlcs = self . included_htlcs ( subject , SENT if subject == LOCAL else RECEIVED , ctn )
sent_htlcs = self . included_htlcs ( subject , RECEIVED if subject == LOCAL else SENT , ctn )
2018-10-10 22:54:30 +02:00
this_config = self . config [ subject ]
other_config = self . config [ - subject ]
2018-09-28 16:40:32 +02:00
other_htlc_pubkey = derive_pubkey ( other_config . htlc_basepoint . pubkey , this_point )
this_htlc_pubkey = derive_pubkey ( this_config . htlc_basepoint . pubkey , this_point )
other_revocation_pubkey = derive_blinded_pubkey ( other_config . revocation_basepoint . pubkey , this_point )
2018-10-23 16:44:39 +02:00
htlcs = [ ] # type: List[ScriptHtlc]
2019-01-21 21:27:27 +01:00
for is_received_htlc , htlc_list in zip ( ( subject != LOCAL , subject == LOCAL ) , ( received_htlcs , sent_htlcs ) ) :
for htlc in htlc_list :
htlcs . append ( ScriptHtlc ( make_htlc_output_witness_script (
is_received_htlc = is_received_htlc ,
remote_revocation_pubkey = other_revocation_pubkey ,
remote_htlc_pubkey = other_htlc_pubkey ,
local_htlc_pubkey = this_htlc_pubkey ,
payment_hash = htlc . payment_hash ,
cltv_expiry = htlc . cltv_expiry ) , htlc ) )
onchain_fees = calc_onchain_fees (
len ( htlcs ) ,
feerate ,
self . constraints . is_initiator == ( subject == LOCAL ) ,
)
2019-12-13 14:07:11 +01:00
if self . is_static_remotekey_enabled ( ) :
payment_pubkey = other_config . payment_basepoint . pubkey
else :
payment_pubkey = derive_pubkey ( other_config . payment_basepoint . pubkey , this_point )
2018-06-28 15:50:45 +02:00
return make_commitment (
2019-01-21 21:27:27 +01:00
ctn ,
2018-09-28 16:40:32 +02:00
this_config . multisig_key . pubkey ,
other_config . multisig_key . pubkey ,
2018-06-28 15:50:45 +02:00
payment_pubkey ,
2018-10-15 18:36:13 +02:00
self . config [ LOCAL if self . constraints . is_initiator else REMOTE ] . payment_basepoint . pubkey ,
self . config [ LOCAL if not self . constraints . is_initiator else REMOTE ] . payment_basepoint . pubkey ,
2018-09-28 16:40:32 +02:00
other_revocation_pubkey ,
derive_pubkey ( this_config . delayed_basepoint . pubkey , this_point ) ,
other_config . to_self_delay ,
2020-02-01 16:30:02 +01:00
self . funding_outpoint . txid ,
self . funding_outpoint . output_index ,
2018-09-28 16:40:32 +02:00
self . constraints . capacity ,
2018-06-28 15:50:45 +02:00
local_msat ,
remote_msat ,
2018-09-28 16:40:32 +02:00
this_config . dust_limit_sat ,
2019-01-21 21:27:27 +01:00
onchain_fees ,
2018-07-06 22:54:26 +02:00
htlcs = htlcs )
2018-09-21 19:18:34 +02:00
2018-10-25 19:53:31 +02:00
def make_closing_tx ( self , local_script : bytes , remote_script : bytes ,
2020-02-26 11:01:53 +01:00
fee_sat : int , * , drop_remote = False ) - > Tuple [ bytes , PartialTransaction ] :
2018-11-05 17:23:49 +01:00
""" cooperative close """
2019-10-23 17:09:41 +02:00
_ , outputs = make_commitment_outputs (
fees_per_participant = {
2018-10-22 21:50:13 +02:00
LOCAL : fee_sat * 1000 if self . constraints . is_initiator else 0 ,
REMOTE : fee_sat * 1000 if not self . constraints . is_initiator else 0 ,
} ,
2019-10-23 17:09:41 +02:00
local_amount_msat = self . balance ( LOCAL ) ,
2020-02-26 11:01:53 +01:00
remote_amount_msat = self . balance ( REMOTE ) if not drop_remote else 0 ,
2019-10-23 17:09:41 +02:00
local_script = bh2u ( local_script ) ,
remote_script = bh2u ( remote_script ) ,
htlcs = [ ] ,
dust_limit_sat = self . config [ LOCAL ] . dust_limit_sat )
2018-09-21 19:18:34 +02:00
2018-10-10 22:54:30 +02:00
closing_tx = make_closing_tx ( self . config [ LOCAL ] . multisig_key . pubkey ,
2018-10-22 21:50:13 +02:00
self . config [ REMOTE ] . multisig_key . pubkey ,
funding_txid = self . funding_outpoint . txid ,
funding_pos = self . funding_outpoint . output_index ,
funding_sat = self . constraints . capacity ,
outputs = outputs )
2018-09-21 19:18:34 +02:00
2018-10-10 22:54:30 +02:00
der_sig = bfh ( closing_tx . sign_txin ( 0 , self . config [ LOCAL ] . multisig_key . privkey ) )
2018-09-21 19:18:34 +02:00
sig = ecc . sig_string_from_der_sig ( der_sig [ : - 1 ] )
2018-11-22 16:18:28 +01:00
return sig , closing_tx
2018-10-05 19:37:55 +02:00
2019-10-23 17:09:41 +02:00
def signature_fits ( self , tx : PartialTransaction ) :
2018-12-18 14:35:22 +01:00
remote_sig = self . config [ LOCAL ] . current_commitment_signature
2019-01-21 21:27:27 +01:00
preimage_hex = tx . serialize_preimage ( 0 )
2019-10-23 17:09:41 +02:00
msg_hash = sha256d ( bfh ( preimage_hex ) )
2019-01-21 21:27:27 +01:00
assert remote_sig
2019-10-23 17:09:41 +02:00
res = ecc . verify_signature ( self . config [ REMOTE ] . multisig_key . pubkey , remote_sig , msg_hash )
2019-01-21 21:27:27 +01:00
return res
2018-12-18 14:35:22 +01:00
2018-11-05 17:23:49 +01:00
def force_close_tx ( self ) :
2019-08-03 17:34:11 +02:00
tx = self . get_latest_commitment ( LOCAL )
2019-01-21 21:27:27 +01:00
assert self . signature_fits ( tx )
2018-11-05 17:23:49 +01:00
tx . sign ( { bh2u ( self . config [ LOCAL ] . multisig_key . pubkey ) : ( self . config [ LOCAL ] . multisig_key . privkey , True ) } )
remote_sig = self . config [ LOCAL ] . current_commitment_signature
2019-01-21 21:27:27 +01:00
remote_sig = ecc . der_sig_from_sig_string ( remote_sig ) + b " \x01 "
2019-10-23 17:09:41 +02:00
tx . add_signature_to_txin ( txin_idx = 0 ,
signing_pubkey = self . config [ REMOTE ] . multisig_key . pubkey . hex ( ) ,
sig = remote_sig . hex ( ) )
2019-01-21 21:27:27 +01:00
assert tx . is_complete ( )
2018-11-05 17:23:49 +01:00
return tx
2019-06-04 11:37:29 +02:00
2019-09-09 19:38:35 +02:00
def sweep_ctx ( self , ctx : Transaction ) - > Dict [ str , SweepInfo ] :
2019-06-24 11:13:18 +02:00
txid = ctx . txid ( )
if self . sweep_info . get ( txid ) is None :
2019-09-07 07:37:13 +02:00
our_sweep_info = create_sweeptxs_for_our_ctx ( chan = self , ctx = ctx , sweep_address = self . sweep_address )
their_sweep_info = create_sweeptxs_for_their_ctx ( chan = self , ctx = ctx , sweep_address = self . sweep_address )
2019-06-24 11:13:18 +02:00
if our_sweep_info is not None :
self . sweep_info [ txid ] = our_sweep_info
2019-06-04 11:37:29 +02:00
self . logger . info ( f ' we force closed. ' )
2019-06-24 11:13:18 +02:00
elif their_sweep_info is not None :
self . sweep_info [ txid ] = their_sweep_info
2019-06-04 11:37:29 +02:00
self . logger . info ( f ' they force closed. ' )
else :
2019-06-24 11:13:18 +02:00
self . sweep_info [ txid ] = { }
return self . sweep_info [ txid ]
def sweep_htlc ( self , ctx : Transaction , htlc_tx : Transaction ) :
# look at the output address, check if it matches
return create_sweeptx_for_their_revoked_htlc ( self , ctx , htlc_tx , self . sweep_address )
2020-02-26 19:08:48 +01:00
def has_pending_changes ( self , subject ) :
next_htlcs = self . hm . get_htlcs_in_next_ctx ( subject )
latest_htlcs = self . hm . get_htlcs_in_latest_ctx ( subject )
return not ( next_htlcs == latest_htlcs and self . get_next_feerate ( subject ) == self . get_latest_feerate ( subject ) )