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
2018-09-13 19:59:12 +02:00
from enum import Enum , auto
2018-10-23 16:44:39 +02:00
from typing import Optional , Dict , List , Tuple , NamedTuple , Set , Callable , Iterable , Sequence
2018-09-12 16:17:10 +02:00
2019-01-21 21:27:27 +01:00
from . import ecc
2018-07-20 16:17:18 +02:00
from . util import bfh , PrintError , bh2u
2018-10-25 23:30:36 +02:00
from . bitcoin import TYPE_SCRIPT , TYPE_ADDRESS
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-01-21 21:27:27 +01:00
from . simple_config import get_config
2018-10-23 16:44:39 +02:00
from . transaction import Transaction
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 ,
ScriptHtlc , PaymentFailure , calc_onchain_fees , RemoteMisbehaving , make_htlc_output_witness_script )
2018-12-04 20:50:24 +01:00
from . lnsweep import create_sweeptxs_for_their_just_revoked_ctx
2018-12-15 11:38:46 +01:00
from . lnsweep import create_sweeptxs_for_our_latest_ctx , create_sweeptxs_for_their_latest_ctx
2019-01-21 21:27:27 +01:00
from . lnhtlc import HTLCManager
2018-10-23 16:44:39 +02:00
2018-09-12 16:17:10 +02:00
2018-10-25 00:22:42 +02:00
class ChannelJsonEncoder ( json . JSONEncoder ) :
def default ( self , o ) :
if isinstance ( o , bytes ) :
return binascii . hexlify ( o ) . decode ( " ascii " )
if isinstance ( o , RevocationStore ) :
return o . serialize ( )
2018-11-07 17:44:49 +01:00
if isinstance ( o , set ) :
return list ( o )
return super ( ) . default ( o )
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
2018-09-13 19:59:12 +02:00
class FeeUpdateProgress ( Enum ) :
FUNDEE_SIGNED = auto ( )
FUNDEE_ACKED = auto ( )
FUNDER_SIGNED = auto ( )
2018-09-12 23:37:45 +02:00
2018-09-13 19:59:12 +02:00
FUNDEE_SIGNED = FeeUpdateProgress . FUNDEE_SIGNED
FUNDEE_ACKED = FeeUpdateProgress . FUNDEE_ACKED
FUNDER_SIGNED = FeeUpdateProgress . FUNDER_SIGNED
2018-09-12 23:37:45 +02:00
2018-10-11 17:06:37 +02:00
class FeeUpdate ( defaultdict ) :
2018-10-04 16:05:23 +02:00
def __init__ ( self , chan , rate ) :
2018-10-11 17:06:37 +02:00
super ( ) . __init__ ( lambda : False )
2018-10-04 16:05:23 +02:00
self . rate = rate
2018-09-13 19:59:12 +02:00
self . chan = chan
2018-07-20 16:44:03 +02:00
2018-09-13 19:59:12 +02:00
def pending_feerate ( self , subject ) :
2018-10-11 17:06:37 +02:00
if self [ FUNDEE_ACKED ] :
2018-09-13 19:59:12 +02:00
return self . rate
if subject == REMOTE and self . chan . constraints . is_initiator :
return self . rate
if subject == LOCAL and not self . chan . constraints . is_initiator :
return self . rate
2018-09-26 15:08:57 +02:00
# implicit return None
2018-07-02 17:51:57 +02:00
2018-10-10 22:54:30 +02:00
def decodeAll ( d , local ) :
for k , v in d . items ( ) :
if k == ' revocation_store ' :
yield ( k , RevocationStore . from_json_obj ( v ) )
elif k . endswith ( " _basepoint " ) or k . endswith ( " _key " ) :
if local :
yield ( k , Keypair ( * * dict ( decodeAll ( v , local ) ) ) )
else :
yield ( k , OnlyPubkeyKeypair ( * * dict ( decodeAll ( v , local ) ) ) )
elif k in [ " node_id " , " channel_id " , " short_channel_id " , " pubkey " , " privkey " , " current_per_commitment_point " , " next_per_commitment_point " , " per_commitment_secret_seed " , " current_commitment_signature " , " current_htlc_signatures " ] and v is not None :
yield ( k , binascii . unhexlify ( v ) )
2018-06-27 20:23:03 +02:00
else :
2018-10-10 22:54:30 +02:00
yield ( k , v )
def htlcsum ( htlcs ) :
return sum ( [ x . amount_msat for x in htlcs ] )
2018-05-29 11:51:48 +02:00
2018-10-17 19:35:15 +02:00
# following two functions are used because json
# doesn't store int keys and byte string values
def str_bytes_dict_from_save ( x ) :
return { int ( k ) : bfh ( v ) for k , v in x . items ( ) }
def str_bytes_dict_to_save ( x ) :
return { str ( k ) : bh2u ( v ) for k , v in x . items ( ) }
2018-10-11 17:15:25 +02:00
class Channel ( PrintError ) :
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
2019-01-21 21:27:27 +01:00
def __init__ ( self , state , sweep_address = None , name = None , payment_completed : Optional [ Callable [ [ Direction , UpdateAddHtlc , bytes ] , None ] ] = None ) :
2018-11-07 17:44:49 +01:00
self . preimages = { }
if not payment_completed :
2018-11-19 18:09:43 +01:00
payment_completed = lambda this , x , y , z : None
2018-12-15 11:38:46 +01:00
self . sweep_address = sweep_address
2018-11-07 17:44:49 +01:00
self . payment_completed = payment_completed
2018-10-10 22:54:30 +02:00
assert ' local_state ' not in state
self . config = { }
self . config [ LOCAL ] = state [ " local_config " ]
if type ( self . config [ LOCAL ] ) is not LocalConfig :
conf = dict ( decodeAll ( self . config [ LOCAL ] , True ) )
self . config [ LOCAL ] = LocalConfig ( * * conf )
assert type ( self . config [ LOCAL ] . htlc_basepoint . privkey ) is bytes
self . config [ REMOTE ] = state [ " remote_config " ]
if type ( self . config [ REMOTE ] ) is not RemoteConfig :
conf = dict ( decodeAll ( self . config [ REMOTE ] , False ) )
self . config [ REMOTE ] = RemoteConfig ( * * conf )
assert type ( self . config [ REMOTE ] . htlc_basepoint . pubkey ) is bytes
self . channel_id = bfh ( state [ " channel_id " ] ) if type ( state [ " channel_id " ] ) not in ( bytes , type ( None ) ) else state [ " channel_id " ]
self . constraints = ChannelConstraints ( * * state [ " constraints " ] ) if type ( state [ " constraints " ] ) is not ChannelConstraints else state [ " constraints " ]
self . funding_outpoint = Outpoint ( * * dict ( decodeAll ( state [ " funding_outpoint " ] , False ) ) ) if type ( state [ " funding_outpoint " ] ) is not Outpoint else state [ " funding_outpoint " ]
self . node_id = bfh ( state [ " node_id " ] ) if type ( state [ " node_id " ] ) not in ( bytes , type ( None ) ) else state [ " node_id " ]
self . short_channel_id = bfh ( state [ " short_channel_id " ] ) if type ( state [ " short_channel_id " ] ) not in ( bytes , type ( None ) ) else state [ " short_channel_id " ]
2018-10-08 20:31:15 +02:00
self . short_channel_id_predicted = self . short_channel_id
2018-10-17 19:35:15 +02:00
self . onion_keys = str_bytes_dict_from_save ( state . get ( ' onion_keys ' , { } ) )
2018-06-27 20:23:03 +02:00
2018-09-12 16:17:10 +02:00
# FIXME this is a tx serialised in the custom electrum partial tx format.
# we should not persist txns in this format. we should persist htlcs, and be able to derive
# any past commitment transaction and use that instead; until then...
self . remote_commitment_to_be_revoked = Transaction ( state [ " remote_commitment_to_be_revoked " ] )
2019-01-21 21:27:27 +01:00
self . remote_commitment_to_be_revoked . deserialize ( True )
2018-09-12 16:17:10 +02:00
2019-01-21 21:27:27 +01:00
self . hm = HTLCManager ( state . get ( ' log ' ) )
2018-05-29 11:51:48 +02:00
self . name = name
2018-11-12 11:55:13 +01:00
self . pending_fee = None
2018-07-02 17:51:57 +02:00
2018-07-30 13:51:03 +02:00
self . _is_funding_txo_spent = None # "don't know"
2018-11-05 17:23:49 +01:00
self . _state = None
if state . get ( ' force_closed ' , False ) :
self . set_state ( ' FORCE_CLOSING ' )
else :
self . set_state ( ' DISCONNECTED ' )
2018-07-30 13:51:03 +02:00
2018-09-12 16:17:10 +02:00
self . lnwatcher = None
2019-01-21 21:27:27 +01:00
self . local_commitment = None
self . remote_commitment = None
2018-12-15 11:38:46 +01:00
2019-01-29 19:01:04 +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 ( ) :
rhash = bh2u ( htlc . payment_hash )
status = ' settled ' if htlc_id in log . get ( ' settles ' , { } ) else ' inflight '
direction = SENT if subject is LOCAL else RECEIVED
out [ rhash ] = ( self . channel_id , htlc , direction , status )
return out
2018-12-15 11:38:46 +01:00
def set_local_commitment ( self , ctx ) :
2019-01-21 21:27:27 +01:00
ctn = extract_ctn_from_tx_and_chan ( ctx , self )
2019-02-11 15:58:57 +01:00
assert self . signature_fits ( ctx ) , ( self . hm . log [ LOCAL ] )
2018-12-15 11:38:46 +01:00
self . local_commitment = ctx
if self . sweep_address is not None :
self . local_sweeptxs = create_sweeptxs_for_our_latest_ctx ( self , self . local_commitment , self . sweep_address )
2019-01-23 15:10:11 +01:00
initial = os . path . join ( get_config ( ) . electrum_path ( ) , ' initial_commitment_tx ' )
tx = self . force_close_tx ( ) . serialize_to_network ( )
if not os . path . exists ( initial ) :
with open ( initial , ' w ' ) as f :
f . write ( tx )
2018-12-15 11:38:46 +01:00
2019-01-21 21:27:27 +01:00
def set_remote_commitment ( self ) :
self . remote_commitment = self . current_commitment ( REMOTE )
2018-12-15 11:38:46 +01:00
if self . sweep_address is not None :
self . remote_sweeptxs = create_sweeptxs_for_their_latest_ctx ( self , self . remote_commitment , self . sweep_address )
2018-12-10 21:15:31 +01:00
2018-07-30 13:51:03 +02:00
def set_state ( self , state : str ) :
2018-11-05 17:23:49 +01:00
if self . _state == ' FORCE_CLOSING ' :
assert state == ' FORCE_CLOSING ' , ' new state was not FORCE_CLOSING: ' + state
2018-07-30 13:51:03 +02:00
self . _state = state
def get_state ( self ) :
return self . _state
2019-01-30 19:40:20 +01:00
def is_closed ( self ) :
return self . get_state ( ) in [ ' CLOSED ' , ' FORCE_CLOSING ' ]
2018-10-23 20:32:18 +02:00
def _check_can_pay ( self , amount_msat : int ) - > None :
2018-10-15 12:45:07 +02:00
if self . get_state ( ) != ' OPEN ' :
raise PaymentFailure ( ' Channel not open ' )
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-01-21 21:27:27 +01:00
current_htlc_sum = htlcsum ( self . hm . htlcs_by_direction ( LOCAL , SENT ) ) + htlcsum ( self . hm . htlcs_by_direction ( LOCAL , RECEIVED ) )
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 set_funding_txo_spentness ( self , is_spent : bool ) :
assert isinstance ( is_spent , bool )
self . _is_funding_txo_spent = is_spent
def should_try_to_reestablish_peer ( self ) - > bool :
return self . _is_funding_txo_spent is False and self . _state == ' 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 )
2018-05-29 11:51:48 +02:00
def add_htlc ( self , htlc ) :
"""
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
"""
2018-09-18 18:38:57 +02:00
assert type ( htlc ) is dict
2018-10-23 20:32:18 +02:00
self . _check_can_pay ( htlc [ ' amount_msat ' ] )
2018-10-10 22:54:30 +02:00
htlc = UpdateAddHtlc ( * * htlc , htlc_id = self . config [ LOCAL ] . next_htlc_id )
2019-01-21 21:27:27 +01:00
self . hm . send_htlc ( htlc )
2018-05-29 11:51:48 +02:00
self . print_error ( " add_htlc " )
2018-10-10 22:54:30 +02:00
self . config [ LOCAL ] = self . config [ LOCAL ] . _replace ( next_htlc_id = htlc . htlc_id + 1 )
2018-09-18 18:38:57 +02:00
return htlc . htlc_id
2018-05-29 11:51:48 +02:00
def receive_htlc ( self , htlc ) :
"""
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
"""
2018-09-18 18:38:57 +02:00
assert type ( htlc ) is dict
2018-10-10 22:54:30 +02:00
htlc = UpdateAddHtlc ( * * htlc , htlc_id = self . config [ REMOTE ] . next_htlc_id )
2018-10-23 20:32:18 +02:00
if 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 } ' )
2019-01-21 21:27:27 +01:00
self . hm . recv_htlc ( htlc )
2018-09-18 18:38:57 +02:00
self . print_error ( " receive_htlc " )
2018-10-10 22:54:30 +02:00
self . config [ REMOTE ] = self . config [ REMOTE ] . _replace ( next_htlc_id = htlc . htlc_id + 1 )
2018-09-18 18:38:57 +02:00
return htlc . htlc_id
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-02-12 11:27:01 +01:00
next_remote_ctn = self . get_current_ctn ( REMOTE ) + 1
self . print_error ( " sign_next_commitment " , next_remote_ctn )
2019-01-21 21:27:27 +01:00
self . hm . send_ctx ( )
2018-11-21 15:32:36 +01:00
pending_remote_commitment = self . pending_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 ' )
for_us = False
htlcsigs = [ ]
2019-01-21 21:27:27 +01:00
# they sent => we receive
2019-02-12 11:27:01 +01:00
for we_receive , htlcs in zip ( [ True , False ] , [ self . included_htlcs ( REMOTE , SENT , ctn = next_remote_ctn ) ,
self . included_htlcs ( REMOTE , RECEIVED , ctn = next_remote_ctn ) ] ) :
2018-07-10 19:26:54 +02:00
for htlc in htlcs :
2018-10-23 16:44:39 +02:00
_script , htlc_tx = make_htlc_tx_with_open_channel ( chan = self ,
pcp = self . config [ REMOTE ] . next_per_commitment_point ,
for_us = for_us ,
we_receive = we_receive ,
commit = pending_remote_commitment ,
htlc = htlc )
2018-07-10 19:26:54 +02:00
sig = bfh ( htlc_tx . sign_txin ( 0 , their_remote_htlc_privkey ) )
htlc_sig = ecc . sig_string_from_der_sig ( sig [ : - 1 ] )
2018-10-23 16:44:39 +02:00
htlc_output_idx = htlc_tx . inputs ( ) [ 0 ] [ ' prevout_n ' ]
htlcsigs . append ( ( htlc_output_idx , htlc_sig ) )
2018-09-12 16:17:10 +02:00
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 ]
2018-09-25 17:08:46 +02:00
2019-01-21 21:27:27 +01:00
# TODO should add remote_commitment here and handle
# both valid ctx'es in lnwatcher at the same time...
2018-10-26 17:05:03 +02:00
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
"""
self . print_error ( " receive_new_commitment " )
2018-10-26 17:05:03 +02:00
2019-01-21 21:27:27 +01:00
self . hm . recv_ctx ( )
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
2018-11-21 15:32:36 +01:00
pending_local_commitment = self . pending_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 ) :
2018-07-20 16:17:18 +02:00
raise Exception ( ' 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
2018-10-22 18:57:51 +02:00
htlc_sigs = htlc_sigs [ : ] # copy cause we will delete now
2019-02-12 11:27:01 +01:00
next_local_ctn = self . get_current_ctn ( LOCAL ) + 1
for htlcs , we_receive in [ ( self . included_htlcs ( LOCAL , SENT , ctn = next_local_ctn ) , False ) ,
( self . included_htlcs ( LOCAL , RECEIVED , ctn = next_local_ctn ) , True ) ] :
2018-09-25 17:08:46 +02:00
for htlc in htlcs :
2019-01-21 21:27:27 +01:00
idx = self . verify_htlc ( htlc , htlc_sigs , we_receive , pending_local_commitment )
2018-10-22 18:57:51 +02:00
del htlc_sigs [ idx ]
2018-09-25 17:08:46 +02:00
if len ( htlc_sigs ) != 0 : # all sigs should have been popped above
raise Exception ( ' failed verifying HTLC signatures: invalid amount of correct signatures ' )
2018-06-14 15:34:51 +02:00
2018-10-22 18:57:51 +02:00
self . config [ LOCAL ] = self . config [ LOCAL ] . _replace (
current_commitment_signature = sig ,
2019-01-21 21:27:27 +01:00
current_htlc_signatures = htlc_sigs_string ,
got_sig_for_next = True )
2018-10-22 18:57:51 +02:00
2018-11-12 12:25:21 +01:00
if self . pending_fee is not None :
2018-10-04 16:05:23 +02:00
if not self . constraints . is_initiator :
2018-11-12 11:55:13 +01:00
self . pending_fee [ FUNDEE_SIGNED ] = True
if self . constraints . is_initiator and self . pending_fee [ FUNDEE_ACKED ] :
self . pending_fee [ FUNDER_SIGNED ] = True
2018-07-20 16:44:03 +02:00
2019-01-21 21:27:27 +01:00
self . set_local_commitment ( pending_local_commitment )
2018-11-21 17:33:27 +01:00
2019-01-21 21:27:27 +01:00
def verify_htlc ( self , htlc : UpdateAddHtlc , htlc_sigs : Sequence [ bytes ] , we_receive : bool , ctx ) - > int :
2019-01-24 17:19:08 +01:00
ctn = extract_ctn_from_tx_and_chan ( ctx , self )
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 ' ) )
2018-10-23 16:44:39 +02:00
_script , htlc_tx = make_htlc_tx_with_open_channel ( chan = self ,
2019-01-24 17:19:08 +01:00
pcp = point ,
2018-10-23 16:44:39 +02:00
for_us = True ,
we_receive = we_receive ,
2019-01-21 21:27:27 +01:00
commit = ctx ,
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-01-24 17:19:08 +01:00
remote_htlc_pubkey = derive_pubkey ( self . config [ REMOTE ] . htlc_basepoint . pubkey , point )
2018-10-22 18:57:51 +02:00
for idx , sig in enumerate ( htlc_sigs ) :
if ecc . verify_signature ( remote_htlc_pubkey , sig , pre_hash ) :
return idx
else :
2019-01-21 21:27:27 +01:00
raise Exception ( f ' failed verifying HTLC signatures: { htlc } , sigs: { len ( htlc_sigs ) } , we_receive: { we_receive } ' )
2018-07-20 16:44:03 +02:00
2019-01-21 21:27:27 +01:00
def get_remote_htlc_sig_for_htlc ( self , htlc : UpdateAddHtlc , we_receive : bool , ctx ) - > 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-01-21 21:27:27 +01:00
idx = self . verify_htlc ( htlc , htlc_sigs , we_receive = we_receive , ctx = ctx )
2018-10-23 16:44:39 +02:00
remote_htlc_sig = ecc . der_sig_from_sig_string ( htlc_sigs [ idx ] ) + b ' \x01 '
return remote_htlc_sig
2018-05-29 11:51:48 +02:00
def revoke_current_commitment ( self ) :
self . print_error ( " revoke_current_commitment " )
2019-01-21 21:27:27 +01:00
last_secret , this_point , next_point , _ = self . points ( )
2018-06-11 19:53:15 +02:00
2018-10-11 17:06:37 +02:00
new_feerate = self . constraints . feerate
2018-07-20 16:44:03 +02:00
2018-11-12 12:25:21 +01:00
if self . pending_fee is not None :
2018-11-12 11:55:13 +01:00
if not self . constraints . is_initiator and self . pending_fee [ FUNDEE_SIGNED ] :
new_feerate = self . pending_fee . rate
self . pending_fee = None
2018-07-20 16:44:03 +02:00
print ( " FEERATE CHANGE COMPLETE (non-initiator) " )
2018-11-12 11:55:13 +01:00
if self . constraints . is_initiator and self . pending_fee [ FUNDER_SIGNED ] :
new_feerate = self . pending_fee . rate
self . pending_fee = None
2018-07-20 16:44:03 +02:00
print ( " FEERATE CHANGE COMPLETE (initiator) " )
2018-07-02 17:51:57 +02:00
2019-01-21 21:27:27 +01:00
assert self . config [ LOCAL ] . got_sig_for_next
2018-10-11 17:06:37 +02:00
self . constraints = self . constraints . _replace (
feerate = new_feerate
2018-07-23 20:16:03 +02:00
)
2019-01-21 21:27:27 +01:00
self . set_local_commitment ( self . pending_commitment ( LOCAL ) )
ctx = self . pending_commitment ( LOCAL )
self . hm . send_rev ( )
self . config [ LOCAL ] = self . config [ LOCAL ] . _replace (
ctn = self . config [ LOCAL ] . ctn + 1 ,
got_sig_for_next = False ,
)
assert self . signature_fits ( ctx )
2018-07-02 17:51:57 +02:00
2018-06-14 15:34:51 +02:00
return RevokeAndAck ( last_secret , next_point ) , " current htlcs "
2018-06-11 19:53:15 +02:00
def points ( self ) :
2018-10-10 22:54:30 +02:00
last_small_num = self . config [ LOCAL ] . ctn
2018-05-29 11:51:48 +02:00
this_small_num = last_small_num + 1
2018-07-09 00:15:55 +02:00
next_small_num = last_small_num + 2
2018-10-10 22:54:30 +02:00
last_secret = get_per_commitment_secret_from_seed ( self . config [ LOCAL ] . per_commitment_secret_seed , RevocationStore . START_INDEX - last_small_num )
this_secret = get_per_commitment_secret_from_seed ( self . config [ LOCAL ] . per_commitment_secret_seed , RevocationStore . START_INDEX - this_small_num )
2018-05-29 11:51:48 +02:00
this_point = secret_to_pubkey ( int . from_bytes ( this_secret , ' big ' ) )
2018-10-10 22:54:30 +02:00
next_secret = get_per_commitment_secret_from_seed ( self . config [ LOCAL ] . per_commitment_secret_seed , RevocationStore . START_INDEX - next_small_num )
2018-05-29 11:51:48 +02:00
next_point = secret_to_pubkey ( int . from_bytes ( next_secret , ' big ' ) )
2019-01-21 21:27:27 +01:00
last_point = secret_to_pubkey ( int . from_bytes ( last_secret , ' big ' ) )
return last_secret , this_point , next_point , last_point
2018-05-29 11:51:48 +02:00
2018-10-23 16:44:39 +02:00
def process_new_revocation_secret ( self , per_commitment_secret : bytes ) :
2018-10-08 17:20:31 +02:00
if not self . lnwatcher :
return
2018-10-05 19:37:55 +02:00
outpoint = self . funding_outpoint . to_str ( )
2018-10-23 16:44:39 +02:00
ctx = self . remote_commitment_to_be_revoked # FIXME can't we just reconstruct it?
2019-01-17 12:35:14 +01:00
sweeptxs = create_sweeptxs_for_their_just_revoked_ctx ( self , ctx , per_commitment_secret , self . sweep_address )
for prev_txid , tx in sweeptxs . items ( ) :
if tx is not None :
2019-01-17 12:56:18 +01:00
self . lnwatcher . add_sweep_tx ( outpoint , prev_txid , tx . as_dict ( ) )
2018-10-05 19:37:55 +02:00
2018-10-26 15:00:43 +02:00
def receive_revocation ( self , revocation ) - > Tuple [ int , int ] :
2018-05-29 11:51:48 +02:00
self . print_error ( " receive_revocation " )
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 ' )
# FIXME not sure this is correct... but it seems to work
# if there are update_add_htlc msgs between commitment_signed and rev_ack,
# this might break
2018-11-21 15:32:36 +01:00
prev_remote_commitment = self . pending_commitment ( REMOTE )
2018-09-12 16:17:10 +02:00
2018-10-10 22:54:30 +02:00
self . config [ REMOTE ] . revocation_store . add_next_entry ( revocation . per_commitment_secret )
2018-10-05 19:37:55 +02:00
self . process_new_revocation_secret ( revocation . per_commitment_secret )
2018-09-12 16:17:10 +02:00
2018-10-23 16:44:39 +02:00
##### start applying fee/htlc changes
if self . pending_fee is not None :
if not self . constraints . is_initiator :
self . pending_fee [ FUNDEE_SIGNED ] = True
if self . constraints . is_initiator and self . pending_fee [ FUNDEE_ACKED ] :
self . pending_fee [ FUNDER_SIGNED ] = True
2019-01-21 21:27:27 +01:00
received = self . hm . received_in_ctn ( self . config [ REMOTE ] . ctn + 1 )
sent = self . hm . sent_in_ctn ( self . config [ REMOTE ] . ctn + 1 )
for htlc in received :
self . payment_completed ( self , RECEIVED , htlc , None )
for htlc in sent :
preimage = self . preimages . pop ( htlc . htlc_id )
self . payment_completed ( self , SENT , htlc , preimage )
received_this_batch = htlcsum ( received )
sent_this_batch = htlcsum ( sent )
2018-06-25 21:06:50 +02:00
2018-10-10 22:54:30 +02:00
next_point = self . config [ REMOTE ] . next_per_commitment_point
2018-05-29 11:51:48 +02:00
2019-01-21 21:27:27 +01:00
self . hm . recv_rev ( )
2018-10-10 22:54:30 +02:00
self . config [ REMOTE ] = self . config [ REMOTE ] . _replace (
ctn = self . config [ REMOTE ] . ctn + 1 ,
2018-06-27 20:23:03 +02:00
current_per_commitment_point = next_point ,
next_per_commitment_point = revocation . next_per_commitment_point ,
2018-05-29 11:51:48 +02:00
)
2018-07-20 16:44:03 +02:00
2018-11-12 12:25:21 +01:00
if self . pending_fee is not None :
2018-10-04 16:05:23 +02:00
if self . constraints . is_initiator :
2018-11-12 11:55:13 +01:00
self . pending_fee [ FUNDEE_ACKED ] = True
2018-07-20 16:44:03 +02:00
2019-01-21 21:27:27 +01:00
self . set_remote_commitment ( )
2018-09-12 16:17:10 +02:00
self . remote_commitment_to_be_revoked = prev_remote_commitment
2019-01-21 21:27:27 +01:00
2018-09-12 21:01:00 +02:00
return received_this_batch , sent_this_batch
2018-05-29 11:51:48 +02:00
2019-01-21 21:27:27 +01:00
def balance ( self , subject , ctn = None ) :
2018-10-24 20:39:07 +02:00
"""
This balance in mSAT is not including reserve and fees .
So a node cannot actually use it ' s whole balance.
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
commited to later when the respective commitment
transaction as been revoked .
"""
2019-01-21 21:27:27 +01:00
assert type ( subject ) is HTLCOwner
2018-10-10 22:54:30 +02:00
initial = self . config [ subject ] . initial_msat
2018-09-18 18:38:57 +02:00
2019-01-21 21:27:27 +01:00
for direction , htlc in self . hm . settled_htlcs ( subject , ctn ) :
if direction == SENT :
initial - = htlc . amount_msat
else :
initial + = htlc . amount_msat
2018-09-18 18:38:57 +02:00
return initial
2018-10-24 20:39:07 +02:00
def balance_minus_outgoing_htlcs ( self , subject ) :
"""
This balance in mSAT , which includes the value of
pending outgoing HTLCs , is used in the UI .
"""
2019-01-21 21:27:27 +01:00
assert type ( subject ) is HTLCOwner
ctn = self . hm . log [ subject ] [ ' ctn ' ] + 1
return self . balance ( subject , ctn ) \
- htlcsum ( self . hm . htlcs_by_direction ( subject , SENT , ctn ) )
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-01-21 21:27:27 +01:00
assert type ( subject ) is HTLCOwner
2018-10-24 20:39:07 +02:00
return self . balance_minus_outgoing_htlcs ( subject ) \
- 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 ) ) ,
2018-10-23 20:32:18 +02:00
self . pending_feerate ( subject ) ,
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 :
ctn = self . config [ subject ] . ctn
2018-09-25 17:08:46 +02:00
feerate = self . pending_feerate ( subject )
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
htlcs = self . hm . htlcs_by_direction ( subject , direction , ctn = ctn )
lnhtlc: multiply weight by feerate before rounding
This resolves the error formerly manifested as:
Traceback (most recent call last):
File "/home/janus/Skrivebord/lightning-rfc/tools/electrum/packages/jsonrpclib/SimpleJSONRPCServer.py", line 376, in _dispatch
return func(*params)
File "/home/janus/Skrivebord/lightning-rfc/tools/electrum/electrum/daemon.py", line 292, in run_cmdline
result = func(*args, **kwargs)
File "/home/janus/Skrivebord/lightning-rfc/tools/electrum/electrum/commands.py", line 87, in func_wrapper
return func(*args, **kwargs)
File "/home/janus/Skrivebord/lightning-rfc/tools/electrum/electrum/commands.py", line 697, in lnpay
return f.result()
File "/usr/lib/python3.6/concurrent/futures/_base.py", line 432, in result
return self.__get_result()
File "/usr/lib/python3.6/concurrent/futures/_base.py", line 384, in __get_result
raise self._exception
File "/home/janus/Skrivebord/lightning-rfc/tools/electrum/electrum/lnbase.py", line 887, in pay
sig_64, htlc_sigs = chan.sign_next_commitment()
File "/home/janus/Skrivebord/lightning-rfc/tools/electrum/electrum/lnhtlc.py", line 281, in sign_next_commitment
htlc_tx = make_htlc_tx_with_open_channel(self, *args)
File "/home/janus/Skrivebord/lightning-rfc/tools/electrum/electrum/lnutil.py", line 262, in make_htlc_tx_with_open_channel
commit.txid(), commit.htlc_output_indices[original_htlc_output_index],
KeyError: 0
2018-10-02 17:13:45 +02:00
fee_for_htlc = lambda htlc : htlc . amount_msat / / 1000 - ( weight * feerate / / 1000 )
2019-01-21 21:27:27 +01:00
return list ( filter ( lambda htlc : fee_for_htlc ( htlc ) > = conf . dust_limit_sat , htlcs ) )
2018-09-25 17:08:46 +02:00
2018-09-13 19:59:12 +02:00
def pending_feerate ( self , subject ) :
2019-01-21 21:27:27 +01:00
assert type ( subject ) is HTLCOwner
2018-10-11 17:06:37 +02:00
candidate = self . constraints . feerate
2018-11-12 12:25:21 +01:00
if self . pending_fee is not None :
2018-11-12 11:55:13 +01:00
x = self . pending_fee . pending_feerate ( subject )
2018-09-13 19:59:12 +02:00
if x is not None :
candidate = x
2018-10-11 17:06:37 +02:00
return candidate
2018-09-13 19:59:12 +02:00
2018-11-21 15:32:36 +01:00
def pending_commitment ( self , subject ) :
2019-01-21 21:27:27 +01:00
assert type ( subject ) is HTLCOwner
2018-11-21 15:32:36 +01:00
this_point = self . config [ REMOTE ] . next_per_commitment_point if subject == REMOTE else self . points ( ) [ 1 ]
2019-01-21 21:27:27 +01:00
ctn = self . config [ subject ] . ctn + 1
feerate = self . pending_feerate ( subject )
return self . make_commitment ( subject , this_point , ctn , feerate , True )
2018-05-29 11:51:48 +02:00
2018-12-10 21:15:31 +01:00
def current_commitment ( self , subject ) :
2019-01-21 21:27:27 +01:00
assert type ( subject ) is HTLCOwner
this_point = self . config [ REMOTE ] . current_per_commitment_point if subject == REMOTE else self . points ( ) [ 3 ]
ctn = self . config [ subject ] . ctn
feerate = self . constraints . feerate
return self . make_commitment ( subject , this_point , ctn , feerate , False )
2018-09-12 23:37:45 +02:00
2019-02-04 12:37:30 +01:00
def get_current_ctn ( self , subject ) :
return self . config [ subject ] . ctn
2019-01-21 21:27:27 +01:00
def total_msat ( self , direction ) :
assert type ( direction ) is Direction
sub = LOCAL if direction == SENT else REMOTE
return htlcsum ( self . hm . settled_htlcs_by ( sub , self . config [ sub ] . ctn ) )
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 .
"""
2018-06-25 21:06:50 +02:00
self . print_error ( " settle_htlc " )
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-10-17 19:35:15 +02:00
# not saving preimage because it's already saved in LNWorker.invoices
2018-05-29 11:51:48 +02:00
2018-10-18 22:56:40 +02:00
def receive_htlc_settle ( self , preimage , htlc_id ) :
2018-06-25 21:06:50 +02:00
self . print_error ( " 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 ' ]
self . hm . recv_settle ( htlc_id )
2018-11-07 17:44:49 +01:00
self . preimages [ htlc_id ] = preimage
2018-10-17 19:35:15 +02:00
# we don't save the preimage because we don't need to forward it anyway
2018-06-18 19:46:25 +02:00
2018-10-18 22:56:40 +02:00
def fail_htlc ( self , htlc_id ) :
self . print_error ( " fail_htlc " )
2019-01-21 21:27:27 +01:00
self . hm . send_fail ( htlc_id )
2018-10-18 22:56:40 +02:00
2018-09-24 18:10:14 +02:00
def receive_fail_htlc ( self , htlc_id ) :
self . print_error ( " receive_fail_htlc " )
2019-01-21 21:27:27 +01:00
self . hm . recv_fail ( htlc_id )
2018-06-25 21:06:50 +02:00
@property
2018-09-12 23:37:45 +02:00
def current_height ( self ) :
2018-10-10 22:54:30 +02:00
return { LOCAL : self . config [ LOCAL ] . ctn , REMOTE : self . config [ REMOTE ] . ctn }
2018-06-26 19:18:56 +02:00
2018-07-20 16:44:03 +02:00
def pending_local_fee ( self ) :
2018-11-21 15:32:36 +01:00
return self . constraints . capacity - sum ( x [ 2 ] for x in self . pending_commitment ( LOCAL ) . outputs ( ) )
2018-06-27 18:33:55 +02:00
2018-11-12 11:55:13 +01:00
def update_fee ( self , feerate , initiator ) :
if self . constraints . is_initiator != initiator :
raise Exception ( " Cannot update_fee: wrong initiator " , initiator )
2018-11-12 12:25:21 +01:00
if self . pending_fee is not None :
2018-11-12 11:55:13 +01:00
raise Exception ( " a fee update is already in progress " )
self . pending_fee = FeeUpdate ( self , rate = feerate )
2018-06-27 18:33:55 +02:00
2018-06-27 20:23:03 +02:00
def to_save ( self ) :
2018-09-25 21:28:06 +02:00
to_save = {
2018-10-10 22:54:30 +02:00
" local_config " : self . config [ LOCAL ] ,
" remote_config " : self . config [ REMOTE ] ,
2018-06-27 20:23:03 +02:00
" channel_id " : self . channel_id ,
" short_channel_id " : self . short_channel_id ,
" constraints " : self . constraints ,
" funding_outpoint " : self . funding_outpoint ,
" node_id " : self . node_id ,
2018-09-12 16:17:10 +02:00
" remote_commitment_to_be_revoked " : str ( self . remote_commitment_to_be_revoked ) ,
2019-01-21 21:27:27 +01:00
" log " : self . hm . to_save ( ) ,
2018-10-17 19:35:15 +02:00
" onion_keys " : str_bytes_dict_to_save ( self . onion_keys ) ,
2018-11-05 17:23:49 +01:00
" force_closed " : self . get_state ( ) == ' FORCE_CLOSING ' ,
2018-06-27 20:23:03 +02:00
}
2018-09-25 21:28:06 +02:00
return to_save
2018-06-27 20:23:03 +02:00
def serialize ( self ) :
namedtuples_to_dict = lambda v : { i : j . _asdict ( ) if isinstance ( j , tuple ) else j for i , j in v . _asdict ( ) . items ( ) }
2018-10-26 18:46:33 +02:00
serialized_channel = { }
to_save_ref = self . to_save ( )
for k , v in to_save_ref . items ( ) :
if isinstance ( v , tuple ) :
serialized_channel [ k ] = namedtuples_to_dict ( v )
else :
serialized_channel [ k ] = v
2018-10-25 00:22:42 +02:00
dumped = ChannelJsonEncoder ( ) . encode ( serialized_channel )
2018-06-27 20:23:03 +02:00
roundtripped = json . loads ( dumped )
2018-12-18 15:07:07 +01:00
reconstructed = Channel ( roundtripped )
2018-10-26 18:46:33 +02:00
to_save_new = reconstructed . to_save ( )
if to_save_new != to_save_ref :
from pprint import PrettyPrinter
pp = PrettyPrinter ( indent = 168 )
2018-09-25 21:28:06 +02:00
try :
from deepdiff import DeepDiff
except ImportError :
2018-10-26 18:46:33 +02:00
raise Exception ( " Channels did not roundtrip serialization without changes: \n " + pp . pformat ( to_save_ref ) + " \n " + pp . pformat ( to_save_new ) )
2018-09-25 21:28:06 +02:00
else :
2018-10-26 18:46:33 +02:00
raise Exception ( " Channels did not roundtrip serialization without changes: \n " + pp . pformat ( DeepDiff ( to_save_ref , to_save_new ) ) )
2018-06-27 20:23:03 +02:00
return roundtripped
def __str__ ( self ) :
2018-10-12 16:27:12 +02:00
return str ( self . serialize ( ) )
2018-06-28 15:50:45 +02:00
2019-01-21 21:27:27 +01:00
def make_commitment ( self , subject , this_point , ctn , feerate , pending ) - > Transaction :
#if subject == REMOTE and not pending:
# ctn -= 1
assert type ( subject ) is HTLCOwner
other = REMOTE if LOCAL == subject else LOCAL
remote_msat , local_msat = self . balance ( other , ctn ) , self . balance ( subject , ctn )
received_htlcs = self . hm . htlcs_by_direction ( subject , SENT if subject == LOCAL else RECEIVED , ctn )
sent_htlcs = self . hm . htlcs_by_direction ( subject , RECEIVED if subject == LOCAL else SENT , ctn )
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 ) ,
)
2018-09-28 16:40:32 +02:00
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 ,
* self . funding_outpoint ,
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-11-22 16:18:28 +01:00
def get_local_index ( self ) :
return int ( self . config [ LOCAL ] . multisig_key . pubkey < self . config [ REMOTE ] . multisig_key . pubkey )
2018-10-25 19:53:31 +02:00
def make_closing_tx ( self , local_script : bytes , remote_script : bytes ,
2018-11-22 16:18:28 +01:00
fee_sat : int ) - > Tuple [ bytes , int , str ] :
2018-11-05 17:23:49 +01:00
""" cooperative close """
2018-10-22 21:50:13 +02:00
_ , outputs = make_commitment_outputs ( {
LOCAL : fee_sat * 1000 if self . constraints . is_initiator else 0 ,
REMOTE : fee_sat * 1000 if not self . constraints . is_initiator else 0 ,
} ,
2019-01-21 21:27:27 +01:00
self . balance ( LOCAL ) ,
self . balance ( REMOTE ) ,
2018-09-21 19:18:34 +02:00
( TYPE_SCRIPT , bh2u ( local_script ) ) ,
( TYPE_SCRIPT , bh2u ( remote_script ) ) ,
2018-10-10 22:54:30 +02:00
[ ] , 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-01-21 21:27:27 +01:00
def signature_fits ( self , tx ) :
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 )
pre_hash = sha256d ( bfh ( preimage_hex ) )
assert remote_sig
res = ecc . verify_signature ( self . config [ REMOTE ] . multisig_key . pubkey , remote_sig , pre_hash )
return res
2018-12-18 14:35:22 +01:00
2018-11-05 17:23:49 +01:00
def force_close_tx ( self ) :
2018-12-18 14:35:22 +01:00
tx = self . local_commitment
2019-01-21 21:27:27 +01:00
assert self . signature_fits ( tx )
2018-12-18 14:35:22 +01:00
tx = Transaction ( str ( tx ) )
tx . deserialize ( True )
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 "
sigs = tx . _inputs [ 0 ] [ " signatures " ]
none_idx = sigs . index ( None )
tx . add_signature_to_txin ( 0 , none_idx , bh2u ( remote_sig ) )
assert tx . is_complete ( )
2018-11-05 17:23:49 +01:00
return tx
2018-10-23 16:44:39 +02:00
def included_htlcs_in_their_latest_ctxs ( self , htlc_initiator ) - > Dict [ int , List [ UpdateAddHtlc ] ] :
2018-11-05 20:30:56 +01:00
""" A map from commitment number to list of HTLCs in
their latest two commitment transactions .
The oldest might have been revoked . """
2019-01-21 21:27:27 +01:00
assert type ( htlc_initiator ) is HTLCOwner
direction = RECEIVED if htlc_initiator == LOCAL else SENT
old_ctn = self . config [ REMOTE ] . ctn
old_htlcs = self . included_htlcs ( REMOTE , direction , ctn = old_ctn )
2018-11-05 20:30:56 +01:00
2019-01-21 21:27:27 +01:00
new_ctn = self . config [ REMOTE ] . ctn + 1
new_htlcs = self . included_htlcs ( REMOTE , direction , ctn = new_ctn )
2018-11-05 20:30:56 +01:00
2019-01-21 21:27:27 +01:00
return { old_ctn : old_htlcs ,
new_ctn : new_htlcs , }