Browse Source

lnhtlc: save logs and feeupdates

dependabot/pip/contrib/deterministic-build/ecdsa-0.13.3
Janus 6 years ago
committed by ThomasV
parent
commit
d5d9270d0c
  1. 5
      electrum/gui/qt/channels_list.py
  2. 25
      electrum/lnbase.py
  3. 134
      electrum/lnhtlc.py
  4. 2
      electrum/lnutil.py
  5. 31
      electrum/tests/test_lnhtlc.py

5
electrum/gui/qt/channels_list.py

@ -6,6 +6,7 @@ from electrum.util import inv_dict, bh2u, bfh
from electrum.i18n import _ from electrum.i18n import _
from electrum.lnhtlc import HTLCStateMachine from electrum.lnhtlc import HTLCStateMachine
from electrum.lnaddr import lndecode from electrum.lnaddr import lndecode
from electrum.lnutil import LOCAL, REMOTE
from .util import MyTreeWidget, SortableTreeWidgetItem, WindowModalDialog, Buttons, OkButton, CancelButton from .util import MyTreeWidget, SortableTreeWidgetItem, WindowModalDialog, Buttons, OkButton, CancelButton
from .amountedit import BTCAmountEdit from .amountedit import BTCAmountEdit
@ -24,8 +25,8 @@ class ChannelsList(MyTreeWidget):
def format_fields(self, chan): def format_fields(self, chan):
return [ return [
bh2u(chan.node_id), bh2u(chan.node_id),
self.parent.format_amount(chan.local_state.amount_msat//1000), self.parent.format_amount(chan.balance(LOCAL)//1000),
self.parent.format_amount(chan.remote_state.amount_msat//1000), self.parent.format_amount(chan.balance(REMOTE)//1000),
chan.get_state() chan.get_state()
] ]

25
electrum/lnbase.py

@ -8,6 +8,7 @@ from collections import namedtuple, defaultdict, OrderedDict, defaultdict
from .lnutil import Outpoint, ChannelConfig, LocalState, RemoteState, Keypair, OnlyPubkeyKeypair, ChannelConstraints, RevocationStore from .lnutil import Outpoint, ChannelConfig, LocalState, RemoteState, Keypair, OnlyPubkeyKeypair, ChannelConstraints, RevocationStore
from .lnutil import sign_and_get_sig_string, funding_output_script, get_ecdh, get_per_commitment_secret_from_seed from .lnutil import sign_and_get_sig_string, funding_output_script, get_ecdh, get_per_commitment_secret_from_seed
from .lnutil import secret_to_pubkey, LNPeerAddr, PaymentFailure from .lnutil import secret_to_pubkey, LNPeerAddr, PaymentFailure
from .lnutil import LOCAL, REMOTE
from .bitcoin import COIN from .bitcoin import COIN
from ecdsa.util import sigdecode_der, sigencode_string_canonize, sigdecode_string from ecdsa.util import sigdecode_der, sigencode_string_canonize, sigdecode_string
@ -33,7 +34,7 @@ from .util import PrintError, bh2u, print_error, bfh, aiosafe
from .transaction import opcodes, Transaction, TxOutput from .transaction import opcodes, Transaction, TxOutput
from .lnonion import new_onion_packet, OnionHopsDataSingle, OnionPerHop, decode_onion_error, ONION_FAILURE_CODE_MAP from .lnonion import new_onion_packet, OnionHopsDataSingle, OnionPerHop, decode_onion_error, ONION_FAILURE_CODE_MAP
from .lnaddr import lndecode from .lnaddr import lndecode
from .lnhtlc import UpdateAddHtlc, HTLCStateMachine, RevokeAndAck, SettleHtlc from .lnhtlc import HTLCStateMachine, RevokeAndAck
def channel_id_from_funding_tx(funding_txid, funding_index): def channel_id_from_funding_tx(funding_txid, funding_index):
funding_txid_bytes = bytes.fromhex(funding_txid)[::-1] funding_txid_bytes = bytes.fromhex(funding_txid)[::-1]
@ -496,7 +497,8 @@ class Peer(PrintError):
to_self_delay=143, to_self_delay=143,
dust_limit_sat=546, dust_limit_sat=546,
max_htlc_value_in_flight_msat=0xffffffffffffffff, max_htlc_value_in_flight_msat=0xffffffffffffffff,
max_accepted_htlcs=5 max_accepted_htlcs=5,
initial_msat=funding_sat * 1000 - push_msat,
) )
# TODO derive this? # TODO derive this?
per_commitment_secret_seed = 0x1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100.to_bytes(32, 'big') per_commitment_secret_seed = 0x1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100.to_bytes(32, 'big')
@ -536,7 +538,8 @@ class Peer(PrintError):
to_self_delay=int.from_bytes(payload['to_self_delay'], byteorder='big'), to_self_delay=int.from_bytes(payload['to_self_delay'], byteorder='big'),
dust_limit_sat=int.from_bytes(payload['dust_limit_satoshis'], byteorder='big'), dust_limit_sat=int.from_bytes(payload['dust_limit_satoshis'], byteorder='big'),
max_htlc_value_in_flight_msat=int.from_bytes(payload['max_htlc_value_in_flight_msat'], 'big'), max_htlc_value_in_flight_msat=int.from_bytes(payload['max_htlc_value_in_flight_msat'], 'big'),
max_accepted_htlcs=int.from_bytes(payload["max_accepted_htlcs"], 'big') max_accepted_htlcs=int.from_bytes(payload["max_accepted_htlcs"], 'big'),
initial_msat=push_msat
) )
funding_txn_minimum_depth = int.from_bytes(payload['minimum_depth'], 'big') funding_txn_minimum_depth = int.from_bytes(payload['minimum_depth'], 'big')
assert remote_config.dust_limit_sat < 600 assert remote_config.dust_limit_sat < 600
@ -844,9 +847,9 @@ class Peer(PrintError):
onion = new_onion_packet([x.node_id for x in route], self.secret_key, hops_data, associated_data) onion = new_onion_packet([x.node_id for x in route], self.secret_key, hops_data, associated_data)
amount_msat += total_fee amount_msat += total_fee
# FIXME this below will probably break with multiple HTLCs # FIXME this below will probably break with multiple HTLCs
msat_local = chan.local_state.amount_msat - amount_msat msat_local = chan.balance(LOCAL) - amount_msat
msat_remote = chan.remote_state.amount_msat + amount_msat msat_remote = chan.balance(REMOTE) + amount_msat
htlc = UpdateAddHtlc(amount_msat, payment_hash, final_cltv_expiry_with_deltas) htlc = {'amount_msat':amount_msat, 'payment_hash':payment_hash, 'cltv_expiry':final_cltv_expiry_with_deltas}
# FIXME if we raise here, this channel will not get blacklisted, and the payment can never succeed, # FIXME if we raise here, this channel will not get blacklisted, and the payment can never succeed,
# as we will just keep retrying this same path. using the current blacklisting is not a solution as # as we will just keep retrying this same path. using the current blacklisting is not a solution as
@ -861,10 +864,10 @@ class Peer(PrintError):
# FIXME what about channel_reserve_satoshis? will the remote fail the channel if we go below? test. # FIXME what about channel_reserve_satoshis? will the remote fail the channel if we go below? test.
raise PaymentFailure('not enough local balance') raise PaymentFailure('not enough local balance')
self.send_message(gen_msg("update_add_htlc", channel_id=chan.channel_id, id=chan.local_state.next_htlc_id, cltv_expiry=final_cltv_expiry_with_deltas, amount_msat=amount_msat, payment_hash=payment_hash, onion_routing_packet=onion.to_bytes())) htlc_id = chan.add_htlc(htlc)
self.send_message(gen_msg("update_add_htlc", channel_id=chan.channel_id, id=htlc_id, cltv_expiry=final_cltv_expiry_with_deltas, amount_msat=amount_msat, payment_hash=payment_hash, onion_routing_packet=onion.to_bytes()))
chan.add_htlc(htlc) self.attempted_route[(chan.channel_id, htlc_id)] = route
self.attempted_route[(chan.channel_id, htlc.htlc_id)] = route
sig_64, htlc_sigs = chan.sign_next_commitment() sig_64, htlc_sigs = chan.sign_next_commitment()
self.send_message(gen_msg("commitment_signed", channel_id=chan.channel_id, signature=sig_64, num_htlcs=len(htlc_sigs), htlc_signature=b"".join(htlc_sigs))) self.send_message(gen_msg("commitment_signed", channel_id=chan.channel_id, signature=sig_64, num_htlcs=len(htlc_sigs), htlc_signature=b"".join(htlc_sigs)))
@ -947,7 +950,7 @@ class Peer(PrintError):
assert amount_msat == expected_received_msat assert amount_msat == expected_received_msat
payment_hash = htlc["payment_hash"] payment_hash = htlc["payment_hash"]
htlc = UpdateAddHtlc(amount_msat, payment_hash, cltv_expiry) htlc = {'amount_msat': amount_msat, 'payment_hash':payment_hash, 'cltv_expiry':cltv_expiry}
chan.receive_htlc(htlc) chan.receive_htlc(htlc)
@ -967,7 +970,7 @@ class Peer(PrintError):
# remote commitment transaction without htlcs # remote commitment transaction without htlcs
# FIXME why is this not using the HTLC state machine? # FIXME why is this not using the HTLC state machine?
bare_ctx = chan.make_commitment(chan.remote_state.ctn + 1, False, chan.remote_state.next_per_commitment_point, bare_ctx = chan.make_commitment(chan.remote_state.ctn + 1, False, chan.remote_state.next_per_commitment_point,
chan.remote_state.amount_msat - expected_received_msat, chan.local_state.amount_msat + expected_received_msat) chan.balance(REMOTE) - expected_received_msat, chan.balance(LOCAL) + expected_received_msat)
self.lnwatcher.process_new_offchain_ctx(chan, bare_ctx, ours=False) self.lnwatcher.process_new_offchain_ctx(chan, bare_ctx, ours=False)
sig_64 = sign_and_get_sig_string(bare_ctx, chan.local_config, chan.remote_config) sig_64 = sign_and_get_sig_string(bare_ctx, chan.local_config, chan.remote_config)
self.send_message(gen_msg("commitment_signed", channel_id=channel_id, signature=sig_64, num_htlcs=0)) self.send_message(gen_msg("commitment_signed", channel_id=channel_id, signature=sig_64, num_htlcs=0))

134
electrum/lnhtlc.py

@ -16,7 +16,7 @@ from .lnutil import sign_and_get_sig_string
from .lnutil import make_htlc_tx_with_open_channel, make_commitment, make_received_htlc, make_offered_htlc from .lnutil import make_htlc_tx_with_open_channel, make_commitment, make_received_htlc, make_offered_htlc
from .lnutil import HTLC_TIMEOUT_WEIGHT, HTLC_SUCCESS_WEIGHT from .lnutil import HTLC_TIMEOUT_WEIGHT, HTLC_SUCCESS_WEIGHT
from .lnutil import funding_output_script, extract_ctn_from_tx_and_chan from .lnutil import funding_output_script, extract_ctn_from_tx_and_chan
from .lnutil import LOCAL, REMOTE, SENT, RECEIVED from .lnutil import LOCAL, REMOTE, SENT, RECEIVED, HTLCOwner
from .transaction import Transaction from .transaction import Transaction
@ -34,12 +34,22 @@ FUNDEE_ACKED = FeeUpdateProgress.FUNDEE_ACKED
FUNDER_SIGNED = FeeUpdateProgress.FUNDER_SIGNED FUNDER_SIGNED = FeeUpdateProgress.FUNDER_SIGNED
COMMITTED = FeeUpdateProgress.COMMITTED COMMITTED = FeeUpdateProgress.COMMITTED
class FeeUpdate: from collections import namedtuple
def __init__(self, chan, feerate): class FeeUpdate:
self.rate = feerate def __init__(self, chan, **kwargs):
if 'rate' in kwargs:
self.rate = kwargs['rate']
else:
assert False
if 'proposed' not in kwargs:
self.proposed = chan.remote_state.ctn if not chan.constraints.is_initiator else chan.local_state.ctn self.proposed = chan.remote_state.ctn if not chan.constraints.is_initiator else chan.local_state.ctn
else:
self.proposed = kwargs['proposed']
if 'progress' not in kwargs:
self.progress = {FUNDEE_SIGNED: None, FUNDEE_ACKED: None, FUNDER_SIGNED: None, COMMITTED: None} self.progress = {FUNDEE_SIGNED: None, FUNDEE_ACKED: None, FUNDER_SIGNED: None, COMMITTED: None}
else:
self.progress = {FeeUpdateProgress[x.partition('.')[2]]: y for x,y in kwargs['progress'].items()}
self.chan = chan self.chan = chan
@property @property
@ -65,30 +75,30 @@ class FeeUpdate:
if subject == LOCAL and not self.chan.constraints.is_initiator: if subject == LOCAL and not self.chan.constraints.is_initiator:
return self.rate return self.rate
class UpdateAddHtlc: def to_save(self):
def __init__(self, amount_msat, payment_hash, cltv_expiry): return {'rate': self.rate, 'proposed': self.proposed, 'progress': self.progress}
self.amount_msat = amount_msat
self.payment_hash = payment_hash class UpdateAddHtlc(namedtuple('UpdateAddHtlc', ['amount_msat', 'payment_hash', 'cltv_expiry', 'settled', 'locked_in', 'htlc_id'])):
self.cltv_expiry = cltv_expiry __slots__ = ()
def __new__(cls, *args, **kwargs):
# the height the htlc was locked in at, or None if len(args) > 0:
self.locked_in = {LOCAL: None, REMOTE: None} args = list(args)
if type(args[1]) is str:
self.settled = {LOCAL: None, REMOTE: None} args[1] = bfh(args[1])
args[3] = {HTLCOwner(int(x)): y for x,y in args[3].items()}
self.htlc_id = None args[4] = {HTLCOwner(int(x)): y for x,y in args[4].items()}
return super().__new__(cls, *args)
def as_tuple(self): if type(kwargs['payment_hash']) is str:
return (self.htlc_id, self.amount_msat, self.payment_hash, self.cltv_expiry, self.locked_in[REMOTE], self.locked_in[LOCAL], self.settled) kwargs['payment_hash'] = bfh(kwargs['payment_hash'])
if 'locked_in' not in kwargs:
def __hash__(self): kwargs['locked_in'] = {LOCAL: None, REMOTE: None}
return hash(self.as_tuple()) else:
kwargs['locked_in'] = {HTLCOwner(int(x)): y for x,y in kwargs['locked_in']}
def __eq__(self, o): if 'settled' not in kwargs:
return type(o) is UpdateAddHtlc and self.as_tuple() == o.as_tuple() kwargs['settled'] = {LOCAL: None, REMOTE: None}
else:
def __repr__(self): kwargs['settled'] = {HTLCOwner(int(x)): y for x,y in kwargs['settled']}
return "UpdateAddHtlc" + str(self.as_tuple()) return super().__new__(cls, **kwargs)
is_key = lambda k: k.endswith("_basepoint") or k.endswith("_key") is_key = lambda k: k.endswith("_basepoint") or k.endswith("_key")
@ -155,10 +165,22 @@ class HTLCStateMachine(PrintError):
self.remote_commitment_to_be_revoked = Transaction(state["remote_commitment_to_be_revoked"]) self.remote_commitment_to_be_revoked = Transaction(state["remote_commitment_to_be_revoked"])
self.log = {LOCAL: [], REMOTE: []} self.log = {LOCAL: [], REMOTE: []}
for strname, subject in [('remote_log', REMOTE), ('local_log', LOCAL)]:
if strname not in state: continue
for typ,y in state[strname]:
if typ == "UpdateAddHtlc":
self.log[subject].append(UpdateAddHtlc(*decodeAll(y)))
elif typ == "SettleHtlc":
self.log[subject].append(SettleHtlc(*decodeAll(y)))
else:
assert False
self.name = name self.name = name
self.fee_mgr = [] self.fee_mgr = []
if 'fee_updates' in state:
for y in state['fee_updates']:
self.fee_mgr.append(FeeUpdate(self, **y))
self.local_commitment = self.pending_local_commitment self.local_commitment = self.pending_local_commitment
self.remote_commitment = self.pending_remote_commitment self.remote_commitment = self.pending_remote_commitment
@ -190,13 +212,12 @@ class HTLCStateMachine(PrintError):
AddHTLC adds an HTLC to the state machine's local update log. This method AddHTLC adds an HTLC to the state machine's local update log. This method
should be called when preparing to send an outgoing HTLC. should be called when preparing to send an outgoing HTLC.
""" """
assert type(htlc) is UpdateAddHtlc assert type(htlc) is dict
htlc = UpdateAddHtlc(**htlc, htlc_id=self.local_state.next_htlc_id)
self.log[LOCAL].append(htlc) self.log[LOCAL].append(htlc)
self.print_error("add_htlc") self.print_error("add_htlc")
htlc_id = self.local_state.next_htlc_id self.local_state=self.local_state._replace(next_htlc_id=htlc.htlc_id + 1)
self.local_state=self.local_state._replace(next_htlc_id=htlc_id + 1) return htlc.htlc_id
htlc.htlc_id = htlc_id
return htlc_id
def receive_htlc(self, htlc): def receive_htlc(self, htlc):
""" """
@ -204,13 +225,12 @@ class HTLCStateMachine(PrintError):
method should be called in response to receiving a new HTLC from the remote method should be called in response to receiving a new HTLC from the remote
party. party.
""" """
self.print_error("receive_htlc") assert type(htlc) is dict
assert type(htlc) is UpdateAddHtlc htlc = UpdateAddHtlc(**htlc, htlc_id = self.remote_state.next_htlc_id)
self.log[REMOTE].append(htlc) self.log[REMOTE].append(htlc)
htlc_id = self.remote_state.next_htlc_id self.print_error("receive_htlc")
self.remote_state=self.remote_state._replace(next_htlc_id=htlc_id + 1) self.remote_state=self.remote_state._replace(next_htlc_id=htlc.htlc_id + 1)
htlc.htlc_id = htlc_id return htlc.htlc_id
return htlc_id
def sign_next_commitment(self): def sign_next_commitment(self):
""" """
@ -431,6 +451,9 @@ class HTLCStateMachine(PrintError):
amount_msat = self.local_state.amount_msat + (received_this_batch - sent_this_batch) amount_msat = self.local_state.amount_msat + (received_this_batch - sent_this_batch)
) )
self.balance(LOCAL)
self.balance(REMOTE)
for pending_fee in self.fee_mgr: for pending_fee in self.fee_mgr:
if pending_fee.is_proposed(): if pending_fee.is_proposed():
if self.constraints.is_initiator: if self.constraints.is_initiator:
@ -441,6 +464,26 @@ class HTLCStateMachine(PrintError):
self.remote_commitment_to_be_revoked = prev_remote_commitment self.remote_commitment_to_be_revoked = prev_remote_commitment
return received_this_batch, sent_this_batch return received_this_batch, sent_this_batch
def balance(self, subject):
initial = self.local_config.initial_msat if subject == LOCAL else self.remote_config.initial_msat
for x in self.log[-subject]:
if type(x) is not SettleHtlc: continue
htlc = self.lookup_htlc(self.log[subject], x.htlc_id)
htlc_height = htlc.settled[subject]
if htlc_height is not None and htlc_height <= self.current_height[subject]:
initial -= htlc.amount_msat
for x in self.log[subject]:
if type(x) is not SettleHtlc: continue
htlc = self.lookup_htlc(self.log[-subject], x.htlc_id)
htlc_height = htlc.settled[-subject]
if htlc_height is not None and htlc_height <= self.current_height[-subject]:
initial += htlc.amount_msat
assert initial == (self.local_state.amount_msat if subject == LOCAL else self.remote_state.amount_msat)
return initial
@staticmethod @staticmethod
def htlcsum(htlcs): def htlcsum(htlcs):
amount_unsettled = 0 amount_unsettled = 0
@ -611,13 +654,13 @@ class HTLCStateMachine(PrintError):
def update_fee(self, feerate): def update_fee(self, feerate):
if not self.constraints.is_initiator: if not self.constraints.is_initiator:
raise Exception("only initiator can update_fee, this counterparty is not initiator") raise Exception("only initiator can update_fee, this counterparty is not initiator")
pending_fee = FeeUpdate(self, feerate) pending_fee = FeeUpdate(self, rate=feerate)
self.fee_mgr.append(pending_fee) self.fee_mgr.append(pending_fee)
def receive_update_fee(self, feerate): def receive_update_fee(self, feerate):
if self.constraints.is_initiator: if self.constraints.is_initiator:
raise Exception("only the non-initiator can receive_update_fee, this counterparty is initiator") raise Exception("only the non-initiator can receive_update_fee, this counterparty is initiator")
pending_fee = FeeUpdate(self, feerate) pending_fee = FeeUpdate(self, rate=feerate)
self.fee_mgr.append(pending_fee) self.fee_mgr.append(pending_fee)
def to_save(self): def to_save(self):
@ -632,6 +675,9 @@ class HTLCStateMachine(PrintError):
"funding_outpoint": self.funding_outpoint, "funding_outpoint": self.funding_outpoint,
"node_id": self.node_id, "node_id": self.node_id,
"remote_commitment_to_be_revoked": str(self.remote_commitment_to_be_revoked), "remote_commitment_to_be_revoked": str(self.remote_commitment_to_be_revoked),
"remote_log": [(type(x).__name__, x) for x in self.log[REMOTE]],
"local_log": [(type(x).__name__, x) for x in self.log[LOCAL]],
"fee_updates": [x.to_save() for x in self.fee_mgr],
} }
def serialize(self): def serialize(self):
@ -643,7 +689,13 @@ class HTLCStateMachine(PrintError):
return binascii.hexlify(o).decode("ascii") return binascii.hexlify(o).decode("ascii")
if isinstance(o, RevocationStore): if isinstance(o, RevocationStore):
return o.serialize() return o.serialize()
if isinstance(o, SettleHtlc):
return json.dumps(('SettleHtlc', namedtuples_to_dict(o)))
if isinstance(o, UpdateAddHtlc):
return json.dumps(('UpdateAddHtlc', namedtuples_to_dict(o)))
return super(MyJsonEncoder, self) return super(MyJsonEncoder, self)
for fee_upd in serialized_channel['fee_updates']:
fee_upd['progress'] = {str(k): v for k,v in fee_upd['progress'].items()}
dumped = MyJsonEncoder().encode(serialized_channel) dumped = MyJsonEncoder().encode(serialized_channel)
roundtripped = json.loads(dumped) roundtripped = json.loads(dumped)
reconstructed = HTLCStateMachine(roundtripped) reconstructed = HTLCStateMachine(roundtripped)

2
electrum/lnutil.py

@ -18,7 +18,7 @@ HTLC_SUCCESS_WEIGHT = 703
Keypair = namedtuple("Keypair", ["pubkey", "privkey"]) Keypair = namedtuple("Keypair", ["pubkey", "privkey"])
ChannelConfig = namedtuple("ChannelConfig", [ ChannelConfig = namedtuple("ChannelConfig", [
"payment_basepoint", "multisig_key", "htlc_basepoint", "delayed_basepoint", "revocation_basepoint", "payment_basepoint", "multisig_key", "htlc_basepoint", "delayed_basepoint", "revocation_basepoint",
"to_self_delay", "dust_limit_sat", "max_htlc_value_in_flight_msat", "max_accepted_htlcs"]) "to_self_delay", "dust_limit_sat", "max_htlc_value_in_flight_msat", "max_accepted_htlcs", "initial_msat"])
OnlyPubkeyKeypair = namedtuple("OnlyPubkeyKeypair", ["pubkey"]) OnlyPubkeyKeypair = namedtuple("OnlyPubkeyKeypair", ["pubkey"])
RemoteState = namedtuple("RemoteState", ["ctn", "next_per_commitment_point", "amount_msat", "revocation_store", "current_per_commitment_point", "next_htlc_id", "feerate"]) RemoteState = namedtuple("RemoteState", ["ctn", "next_per_commitment_point", "amount_msat", "revocation_store", "current_per_commitment_point", "next_htlc_id", "feerate"])
LocalState = namedtuple("LocalState", ["ctn", "per_commitment_secret_seed", "amount_msat", "next_htlc_id", "funding_locked_received", "was_announced", "current_commitment_signature", "current_htlc_signatures", "feerate"]) LocalState = namedtuple("LocalState", ["ctn", "per_commitment_secret_seed", "amount_msat", "next_htlc_id", "funding_locked_received", "was_announced", "current_commitment_signature", "current_htlc_signatures", "feerate"])

31
electrum/tests/test_lnhtlc.py

@ -25,7 +25,8 @@ def create_channel_state(funding_txid, funding_index, funding_sat, local_feerate
to_self_delay=l_csv, to_self_delay=l_csv,
dust_limit_sat=l_dust, dust_limit_sat=l_dust,
max_htlc_value_in_flight_msat=500000 * 1000, max_htlc_value_in_flight_msat=500000 * 1000,
max_accepted_htlcs=5 max_accepted_htlcs=5,
initial_msat=local_amount,
) )
remote_config=lnbase.ChannelConfig( remote_config=lnbase.ChannelConfig(
payment_basepoint=other_pubkeys[0], payment_basepoint=other_pubkeys[0],
@ -36,7 +37,8 @@ def create_channel_state(funding_txid, funding_index, funding_sat, local_feerate
to_self_delay=r_csv, to_self_delay=r_csv,
dust_limit_sat=r_dust, dust_limit_sat=r_dust,
max_htlc_value_in_flight_msat=500000 * 1000, max_htlc_value_in_flight_msat=500000 * 1000,
max_accepted_htlcs=5 max_accepted_htlcs=5,
initial_msat=remote_amount,
) )
return { return {
@ -132,11 +134,11 @@ class TestLNBaseHTLCStateMachine(unittest.TestCase):
self.paymentPreimage = b"\x01" * 32 self.paymentPreimage = b"\x01" * 32
paymentHash = bitcoin.sha256(self.paymentPreimage) paymentHash = bitcoin.sha256(self.paymentPreimage)
self.htlc = lnhtlc.UpdateAddHtlc( self.htlc = {
payment_hash = paymentHash, 'payment_hash' : paymentHash,
amount_msat = one_bitcoin_in_msat, 'amount_msat' : one_bitcoin_in_msat,
cltv_expiry = 5, 'cltv_expiry' : 5,
) }
# First Alice adds the outgoing HTLC to her local channel's state # First Alice adds the outgoing HTLC to her local channel's state
# update log. Then Alice sends this wire message over to Bob who adds # update log. Then Alice sends this wire message over to Bob who adds
@ -144,6 +146,7 @@ class TestLNBaseHTLCStateMachine(unittest.TestCase):
self.aliceHtlcIndex = self.alice_channel.add_htlc(self.htlc) self.aliceHtlcIndex = self.alice_channel.add_htlc(self.htlc)
self.bobHtlcIndex = self.bob_channel.receive_htlc(self.htlc) self.bobHtlcIndex = self.bob_channel.receive_htlc(self.htlc)
self.htlc = self.bob_channel.log[lnutil.REMOTE][0]
def test_SimpleAddSettleWorkflow(self): def test_SimpleAddSettleWorkflow(self):
alice_channel, bob_channel = self.alice_channel, self.bob_channel alice_channel, bob_channel = self.alice_channel, self.bob_channel
@ -250,6 +253,8 @@ class TestLNBaseHTLCStateMachine(unittest.TestCase):
# revocation. # revocation.
#self.assertEqual(alice_channel.local_update_log, [], "alice's local not updated, should be empty, has %s entries instead"% len(alice_channel.local_update_log)) #self.assertEqual(alice_channel.local_update_log, [], "alice's local not updated, should be empty, has %s entries instead"% len(alice_channel.local_update_log))
#self.assertEqual(alice_channel.remote_update_log, [], "alice's remote not updated, should be empty, has %s entries instead"% len(alice_channel.remote_update_log)) #self.assertEqual(alice_channel.remote_update_log, [], "alice's remote not updated, should be empty, has %s entries instead"% len(alice_channel.remote_update_log))
alice_channel.update_fee(100000)
alice_channel.serialize()
def alice_to_bob_fee_update(self): def alice_to_bob_fee_update(self):
fee = 111 fee = 111
@ -325,11 +330,11 @@ class TestLNHTLCDust(unittest.TestCase):
self.assertEqual(fee_per_kw, 6000) self.assertEqual(fee_per_kw, 6000)
htlcAmt = 500 + lnutil.HTLC_TIMEOUT_WEIGHT * (fee_per_kw // 1000) htlcAmt = 500 + lnutil.HTLC_TIMEOUT_WEIGHT * (fee_per_kw // 1000)
self.assertEqual(htlcAmt, 4478) self.assertEqual(htlcAmt, 4478)
htlc = lnhtlc.UpdateAddHtlc( htlc = {
payment_hash = paymentHash, 'payment_hash' : paymentHash,
amount_msat = 1000 * htlcAmt, 'amount_msat' : 1000 * htlcAmt,
cltv_expiry = 5, # also in create_test_channels 'cltv_expiry' : 5, # also in create_test_channels
) }
aliceHtlcIndex = alice_channel.add_htlc(htlc) aliceHtlcIndex = alice_channel.add_htlc(htlc)
bobHtlcIndex = bob_channel.receive_htlc(htlc) bobHtlcIndex = bob_channel.receive_htlc(htlc)
@ -338,7 +343,7 @@ class TestLNHTLCDust(unittest.TestCase):
self.assertEqual(len(bob_channel.local_commitment.outputs()), 2) self.assertEqual(len(bob_channel.local_commitment.outputs()), 2)
default_fee = calc_static_fee(0) default_fee = calc_static_fee(0)
self.assertEqual(bob_channel.pending_local_fee, default_fee + htlcAmt) self.assertEqual(bob_channel.pending_local_fee, default_fee + htlcAmt)
bob_channel.settle_htlc(paymentPreimage, htlc.htlc_id) bob_channel.settle_htlc(paymentPreimage, bobHtlcIndex)
alice_channel.receive_htlc_settle(paymentPreimage, aliceHtlcIndex) alice_channel.receive_htlc_settle(paymentPreimage, aliceHtlcIndex)
force_state_transition(bob_channel, alice_channel) force_state_transition(bob_channel, alice_channel)
self.assertEqual(len(alice_channel.local_commitment.outputs()), 2) self.assertEqual(len(alice_channel.local_commitment.outputs()), 2)

Loading…
Cancel
Save