Browse Source

lnchannel: add more type hints

hard-fail-on-bad-server-string
SomberNight 5 years ago
parent
commit
79d57784c1
No known key found for this signature in database GPG Key ID: B33B5F232C6271E9
  1. 113
      electrum/lnchannel.py
  2. 30
      electrum/lnutil.py
  3. 3
      electrum/lnwatcher.py
  4. 20
      electrum/tests/test_lnchannel.py
  5. 97
      electrum/tests/test_lnutil.py

113
electrum/lnchannel.py

@ -23,7 +23,8 @@ from collections import namedtuple, defaultdict
import binascii import binascii
import json import json
from enum import IntEnum from enum import IntEnum
from typing import Optional, Dict, List, Tuple, NamedTuple, Set, Callable, Iterable, Sequence, TYPE_CHECKING, Iterator from typing import (Optional, Dict, List, Tuple, NamedTuple, Set, Callable,
Iterable, Sequence, TYPE_CHECKING, Iterator, Union)
import time import time
import threading import threading
@ -63,7 +64,7 @@ if TYPE_CHECKING:
# lightning channel states # lightning channel states
# Note: these states are persisted by name (for a given channel) in the wallet file, # 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. # so consider doing a wallet db upgrade when changing them.
class channel_states(IntEnum): class channel_states(IntEnum): # TODO rename to use CamelCase
PREOPENING = 0 # Initial negotiation. Channel will not be reestablished PREOPENING = 0 # Initial negotiation. Channel will not be reestablished
OPENING = 1 # Channel will be reestablished. (per BOLT2) OPENING = 1 # Channel will be reestablished. (per BOLT2)
# - Funding node: has received funding_signed (can broadcast the funding tx) # - Funding node: has received funding_signed (can broadcast the funding tx)
@ -75,7 +76,7 @@ class channel_states(IntEnum):
CLOSED = 6 # closing tx has been mined CLOSED = 6 # closing tx has been mined
REDEEMED = 7 # we can stop watching REDEEMED = 7 # we can stop watching
class peer_states(IntEnum): class peer_states(IntEnum): # TODO rename to use CamelCase
DISCONNECTED = 0 DISCONNECTED = 0
REESTABLISHING = 1 REESTABLISHING = 1
GOOD = 2 GOOD = 2
@ -138,15 +139,15 @@ class Channel(Logger):
self.sweep_address = sweep_address self.sweep_address = sweep_address
self.storage = state self.storage = state
self.db_lock = self.storage.db.lock if self.storage.db else threading.RLock() self.db_lock = self.storage.db.lock if self.storage.db else threading.RLock()
self.config = {} # type: Dict[HTLCOwner, lnutil.Config] self.config = {} # type: Dict[HTLCOwner, Union[LocalConfig, RemoteConfig]]
self.config[LOCAL] = state["local_config"] self.config[LOCAL] = state["local_config"]
self.config[REMOTE] = state["remote_config"] self.config[REMOTE] = state["remote_config"]
self.channel_id = bfh(state["channel_id"]) self.channel_id = bfh(state["channel_id"])
self.constraints = state["constraints"] self.constraints = state["constraints"] # type: ChannelConstraints
self.funding_outpoint = state["funding_outpoint"] self.funding_outpoint = state["funding_outpoint"] # type: Outpoint
self.node_id = bfh(state["node_id"]) self.node_id = bfh(state["node_id"])
self.short_channel_id = ShortChannelID.normalize(state["short_channel_id"]) self.short_channel_id = ShortChannelID.normalize(state["short_channel_id"])
self.onion_keys = state['onion_keys'] self.onion_keys = state['onion_keys'] # type: Dict[int, bytes]
self.data_loss_protect_remote_pcp = state['data_loss_protect_remote_pcp'] self.data_loss_protect_remote_pcp = state['data_loss_protect_remote_pcp']
self.hm = HTLCManager(log=state['log'], initial_feerate=initial_feerate) self.hm = HTLCManager(log=state['log'], initial_feerate=initial_feerate)
self._state = channel_states[state['state']] self._state = channel_states[state['state']]
@ -165,10 +166,10 @@ class Channel(Logger):
return str(scid) return str(scid)
return self.channel_id.hex() return self.channel_id.hex()
def set_onion_key(self, key, value): def set_onion_key(self, key: int, value: bytes):
self.onion_keys[key] = value self.onion_keys[key] = value
def get_onion_key(self, key): def get_onion_key(self, key: int) -> bytes:
return self.onion_keys.get(key) return self.onion_keys.get(key)
def set_data_loss_protect_remote_pcp(self, key, value): def set_data_loss_protect_remote_pcp(self, key, value):
@ -262,23 +263,24 @@ class Channel(Logger):
self._chan_ann_without_sigs = chan_ann self._chan_ann_without_sigs = chan_ann
return chan_ann return chan_ann
def is_static_remotekey_enabled(self): def is_static_remotekey_enabled(self) -> bool:
return self.storage.get('static_remotekey_enabled') return bool(self.storage.get('static_remotekey_enabled'))
def set_short_channel_id(self, short_id): def set_short_channel_id(self, short_id: ShortChannelID) -> None:
self.short_channel_id = short_id self.short_channel_id = short_id
self.storage["short_channel_id"] = short_id self.storage["short_channel_id"] = short_id
def get_feerate(self, subject, ctn): def get_feerate(self, subject: HTLCOwner, *, ctn: int) -> int:
# returns feerate in sat/kw
return self.hm.get_feerate(subject, ctn) return self.hm.get_feerate(subject, ctn)
def get_oldest_unrevoked_feerate(self, subject): def get_oldest_unrevoked_feerate(self, subject: HTLCOwner) -> int:
return self.hm.get_feerate_in_oldest_unrevoked_ctx(subject) return self.hm.get_feerate_in_oldest_unrevoked_ctx(subject)
def get_latest_feerate(self, subject): def get_latest_feerate(self, subject: HTLCOwner) -> int:
return self.hm.get_feerate_in_latest_ctx(subject) return self.hm.get_feerate_in_latest_ctx(subject)
def get_next_feerate(self, subject): def get_next_feerate(self, subject: HTLCOwner) -> int:
return self.hm.get_feerate_in_next_ctx(subject) return self.hm.get_feerate_in_next_ctx(subject)
def get_payments(self): def get_payments(self):
@ -316,7 +318,7 @@ class Channel(Logger):
self.hm.channel_open_finished() self.hm.channel_open_finished()
self.peer_state = peer_states.GOOD self.peer_state = peer_states.GOOD
def set_state(self, state): def set_state(self, state: channel_states) -> None:
""" set on-chain state """ """ set on-chain state """
old_state = self._state old_state = self._state
if (old_state, state) not in state_transitions: if (old_state, state) not in state_transitions:
@ -329,7 +331,7 @@ class Channel(Logger):
self.lnworker.save_channel(self) self.lnworker.save_channel(self)
self.lnworker.network.trigger_callback('channel', self) self.lnworker.network.trigger_callback('channel', self)
def get_state(self): def get_state(self) -> channel_states:
return self._state return self._state
def get_state_for_GUI(self): def get_state_for_GUI(self):
@ -767,7 +769,7 @@ class Channel(Logger):
assert type(direction) is Direction assert type(direction) is Direction
if ctn is None: if ctn is None:
ctn = self.get_oldest_unrevoked_ctn(subject) ctn = self.get_oldest_unrevoked_ctn(subject)
feerate = self.get_feerate(subject, ctn) feerate = self.get_feerate(subject, ctn=ctn)
conf = self.config[subject] conf = self.config[subject]
if direction == RECEIVED: if direction == RECEIVED:
threshold_sat = received_htlc_trim_threshold_sat(dust_limit_sat=conf.dust_limit_sat, feerate=feerate) threshold_sat = received_htlc_trim_threshold_sat(dust_limit_sat=conf.dust_limit_sat, feerate=feerate)
@ -798,30 +800,30 @@ class Channel(Logger):
point = secret_to_pubkey(int.from_bytes(secret, 'big')) point = secret_to_pubkey(int.from_bytes(secret, 'big'))
return secret, point return secret, point
def get_secret_and_commitment(self, subject, ctn): def get_secret_and_commitment(self, subject: HTLCOwner, *, ctn: int) -> Tuple[Optional[bytes], PartialTransaction]:
secret, point = self.get_secret_and_point(subject, ctn) secret, point = self.get_secret_and_point(subject, ctn)
ctx = self.make_commitment(subject, point, ctn) ctx = self.make_commitment(subject, point, ctn)
return secret, ctx return secret, ctx
def get_commitment(self, subject, ctn) -> PartialTransaction: def get_commitment(self, subject: HTLCOwner, *, ctn: int) -> PartialTransaction:
secret, ctx = self.get_secret_and_commitment(subject, ctn) secret, ctx = self.get_secret_and_commitment(subject, ctn=ctn)
return ctx return ctx
def get_next_commitment(self, subject: HTLCOwner) -> PartialTransaction: def get_next_commitment(self, subject: HTLCOwner) -> PartialTransaction:
ctn = self.get_next_ctn(subject) ctn = self.get_next_ctn(subject)
return self.get_commitment(subject, ctn) return self.get_commitment(subject, ctn=ctn)
def get_latest_commitment(self, subject: HTLCOwner) -> PartialTransaction: def get_latest_commitment(self, subject: HTLCOwner) -> PartialTransaction:
ctn = self.get_latest_ctn(subject) ctn = self.get_latest_ctn(subject)
return self.get_commitment(subject, ctn) return self.get_commitment(subject, ctn=ctn)
def get_oldest_unrevoked_commitment(self, subject: HTLCOwner) -> PartialTransaction: def get_oldest_unrevoked_commitment(self, subject: HTLCOwner) -> PartialTransaction:
ctn = self.get_oldest_unrevoked_ctn(subject) ctn = self.get_oldest_unrevoked_ctn(subject)
return self.get_commitment(subject, ctn) return self.get_commitment(subject, ctn=ctn)
def create_sweeptxs(self, ctn: int) -> List[Transaction]: def create_sweeptxs(self, ctn: int) -> List[Transaction]:
from .lnsweep import create_sweeptxs_for_watchtower from .lnsweep import create_sweeptxs_for_watchtower
secret, ctx = self.get_secret_and_commitment(REMOTE, ctn) secret, ctx = self.get_secret_and_commitment(REMOTE, ctn=ctn)
return create_sweeptxs_for_watchtower(self, ctx, secret, self.sweep_address) return create_sweeptxs_for_watchtower(self, ctx, secret, self.sweep_address)
def get_oldest_unrevoked_ctn(self, subject: HTLCOwner) -> int: def get_oldest_unrevoked_ctn(self, subject: HTLCOwner) -> int:
@ -850,9 +852,9 @@ class Channel(Logger):
assert htlc_id not in log['settles'] assert htlc_id not in log['settles']
self.hm.send_settle(htlc_id) self.hm.send_settle(htlc_id)
def get_payment_hash(self, htlc_id): def get_payment_hash(self, htlc_id: int) -> bytes:
log = self.hm.log[LOCAL] log = self.hm.log[LOCAL]
htlc = log['adds'][htlc_id] htlc = log['adds'][htlc_id] # type: UpdateAddHtlc
return htlc.payment_hash return htlc.payment_hash
def decode_onion_error(self, reason: bytes, route: Sequence['RouteEdge'], def decode_onion_error(self, reason: bytes, route: Sequence['RouteEdge'],
@ -898,13 +900,13 @@ class Channel(Logger):
error_bytes=error_bytes, error_bytes=error_bytes,
error_reason=reason) error_reason=reason)
def pending_local_fee(self): def get_next_fee(self, subject: HTLCOwner) -> int:
return self.constraints.capacity - sum(x.value for x in self.get_next_commitment(LOCAL).outputs()) return self.constraints.capacity - sum(x.value for x in self.get_next_commitment(subject).outputs())
def get_latest_fee(self, subject): def get_latest_fee(self, subject: HTLCOwner) -> int:
return self.constraints.capacity - sum(x.value for x in self.get_latest_commitment(subject).outputs()) return self.constraints.capacity - sum(x.value for x in self.get_latest_commitment(subject).outputs())
def update_fee(self, feerate: int, from_us: bool): def update_fee(self, feerate: int, from_us: bool) -> None:
# feerate uses sat/kw # feerate uses sat/kw
if self.constraints.is_initiator != from_us: if self.constraints.is_initiator != from_us:
raise Exception(f"Cannot update_fee: wrong initiator. us: {from_us}") raise Exception(f"Cannot update_fee: wrong initiator. us: {from_us}")
@ -917,9 +919,9 @@ class Channel(Logger):
else: else:
self.hm.recv_update_fee(feerate) self.hm.recv_update_fee(feerate)
def make_commitment(self, subject, this_point, ctn) -> PartialTransaction: def make_commitment(self, subject: HTLCOwner, this_point: bytes, ctn: int) -> PartialTransaction:
assert type(subject) is HTLCOwner assert type(subject) is HTLCOwner
feerate = self.get_feerate(subject, ctn) feerate = self.get_feerate(subject, ctn=ctn)
other = subject.inverted() other = subject.inverted()
local_msat = self.balance(subject, ctx_owner=subject, ctn=ctn) local_msat = self.balance(subject, ctx_owner=subject, ctn=ctn)
remote_msat = self.balance(other, ctx_owner=subject, ctn=ctn) remote_msat = self.balance(other, ctx_owner=subject, ctn=ctn)
@ -969,23 +971,24 @@ class Channel(Logger):
payment_pubkey = derive_pubkey(other_config.payment_basepoint.pubkey, this_point) payment_pubkey = derive_pubkey(other_config.payment_basepoint.pubkey, this_point)
return make_commitment( return make_commitment(
ctn, ctn=ctn,
this_config.multisig_key.pubkey, local_funding_pubkey=this_config.multisig_key.pubkey,
other_config.multisig_key.pubkey, remote_funding_pubkey=other_config.multisig_key.pubkey,
payment_pubkey, remote_payment_pubkey=payment_pubkey,
self.config[LOCAL if self.constraints.is_initiator else REMOTE].payment_basepoint.pubkey, funder_payment_basepoint=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, fundee_payment_basepoint=self.config[LOCAL if not self.constraints.is_initiator else REMOTE].payment_basepoint.pubkey,
other_revocation_pubkey, revocation_pubkey=other_revocation_pubkey,
derive_pubkey(this_config.delayed_basepoint.pubkey, this_point), delayed_pubkey=derive_pubkey(this_config.delayed_basepoint.pubkey, this_point),
other_config.to_self_delay, to_self_delay=other_config.to_self_delay,
self.funding_outpoint.txid, funding_txid=self.funding_outpoint.txid,
self.funding_outpoint.output_index, funding_pos=self.funding_outpoint.output_index,
self.constraints.capacity, funding_sat=self.constraints.capacity,
local_msat, local_amount=local_msat,
remote_msat, remote_amount=remote_msat,
this_config.dust_limit_sat, dust_limit_sat=this_config.dust_limit_sat,
onchain_fees, fees_per_participant=onchain_fees,
htlcs=htlcs) htlcs=htlcs,
)
def make_closing_tx(self, local_script: bytes, remote_script: bytes, def make_closing_tx(self, local_script: bytes, remote_script: bytes,
fee_sat: int, *, drop_remote = False) -> Tuple[bytes, PartialTransaction]: fee_sat: int, *, drop_remote = False) -> Tuple[bytes, PartialTransaction]:
@ -1013,7 +1016,7 @@ class Channel(Logger):
sig = ecc.sig_string_from_der_sig(der_sig[:-1]) sig = ecc.sig_string_from_der_sig(der_sig[:-1])
return sig, closing_tx return sig, closing_tx
def signature_fits(self, tx: PartialTransaction): def signature_fits(self, tx: PartialTransaction) -> bool:
remote_sig = self.config[LOCAL].current_commitment_signature remote_sig = self.config[LOCAL].current_commitment_signature
preimage_hex = tx.serialize_preimage(0) preimage_hex = tx.serialize_preimage(0)
msg_hash = sha256d(bfh(preimage_hex)) msg_hash = sha256d(bfh(preimage_hex))
@ -1021,7 +1024,7 @@ class Channel(Logger):
res = ecc.verify_signature(self.config[REMOTE].multisig_key.pubkey, remote_sig, msg_hash) res = ecc.verify_signature(self.config[REMOTE].multisig_key.pubkey, remote_sig, msg_hash)
return res return res
def force_close_tx(self): def force_close_tx(self) -> PartialTransaction:
tx = self.get_latest_commitment(LOCAL) tx = self.get_latest_commitment(LOCAL)
assert self.signature_fits(tx) assert self.signature_fits(tx)
tx.sign({bh2u(self.config[LOCAL].multisig_key.pubkey): (self.config[LOCAL].multisig_key.privkey, True)}) tx.sign({bh2u(self.config[LOCAL].multisig_key.pubkey): (self.config[LOCAL].multisig_key.privkey, True)})
@ -1048,11 +1051,11 @@ class Channel(Logger):
self.sweep_info[txid] = {} self.sweep_info[txid] = {}
return self.sweep_info[txid] return self.sweep_info[txid]
def sweep_htlc(self, ctx:Transaction, htlc_tx: Transaction): def sweep_htlc(self, ctx: Transaction, htlc_tx: Transaction) -> Optional[SweepInfo]:
# look at the output address, check if it matches # look at the output address, check if it matches
return create_sweeptx_for_their_revoked_htlc(self, ctx, htlc_tx, self.sweep_address) return create_sweeptx_for_their_revoked_htlc(self, ctx, htlc_tx, self.sweep_address)
def has_pending_changes(self, subject): def has_pending_changes(self, subject: HTLCOwner) -> bool:
next_htlcs = self.hm.get_htlcs_in_next_ctx(subject) next_htlcs = self.hm.get_htlcs_in_next_ctx(subject)
latest_htlcs = self.hm.get_htlcs_in_latest_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)) return not (next_htlcs == latest_htlcs and self.get_next_feerate(subject) == self.get_latest_feerate(subject))

30
electrum/lnutil.py

@ -599,13 +599,27 @@ def calc_fees_for_commitment_tx(*, num_htlcs: int, feerate: int,
REMOTE: fee if not is_local_initiator else 0, REMOTE: fee if not is_local_initiator else 0,
} }
def make_commitment(ctn, local_funding_pubkey, remote_funding_pubkey,
remote_payment_pubkey, funder_payment_basepoint, def make_commitment(
fundee_payment_basepoint, revocation_pubkey, *,
delayed_pubkey, to_self_delay, funding_txid, ctn: int,
funding_pos, funding_sat, local_amount, remote_amount, local_funding_pubkey: bytes,
dust_limit_sat, fees_per_participant, remote_funding_pubkey: bytes,
htlcs: List[ScriptHtlc]) -> PartialTransaction: remote_payment_pubkey: bytes,
funder_payment_basepoint: bytes,
fundee_payment_basepoint: bytes,
revocation_pubkey: bytes,
delayed_pubkey: bytes,
to_self_delay: int,
funding_txid: str,
funding_pos: int,
funding_sat: int,
local_amount: int,
remote_amount: int,
dust_limit_sat: int,
fees_per_participant: Mapping[HTLCOwner, int],
htlcs: List[ScriptHtlc]
) -> PartialTransaction:
c_input = make_funding_input(local_funding_pubkey, remote_funding_pubkey, c_input = make_funding_input(local_funding_pubkey, remote_funding_pubkey,
funding_pos, funding_txid, funding_sat) funding_pos, funding_txid, funding_sat)
obs = get_obscured_ctn(ctn, funder_payment_basepoint, fundee_payment_basepoint) obs = get_obscured_ctn(ctn, funder_payment_basepoint, fundee_payment_basepoint)
@ -618,7 +632,7 @@ def make_commitment(ctn, local_funding_pubkey, remote_funding_pubkey,
# commitment tx outputs # commitment tx outputs
local_address = make_commitment_output_to_local_address(revocation_pubkey, to_self_delay, delayed_pubkey) local_address = make_commitment_output_to_local_address(revocation_pubkey, to_self_delay, delayed_pubkey)
remote_address = make_commitment_output_to_remote_address(remote_payment_pubkey) remote_address = make_commitment_output_to_remote_address(remote_payment_pubkey)
# TODO trim htlc outputs here while also considering 2nd stage htlc transactions # note: it is assumed that the given 'htlcs' are all non-dust (dust htlcs already trimmed)
# BOLT-03: "Transaction Input and Output Ordering # BOLT-03: "Transaction Input and Output Ordering
# Lexicographic ordering: see BIP69. In the case of identical HTLC outputs, # Lexicographic ordering: see BIP69. In the case of identical HTLC outputs,

3
electrum/lnwatcher.py

@ -23,6 +23,7 @@ from .transaction import Transaction
if TYPE_CHECKING: if TYPE_CHECKING:
from .network import Network from .network import Network
from .lnsweep import SweepInfo from .lnsweep import SweepInfo
from .lnworker import LNWallet
class ListenerItem(NamedTuple): class ListenerItem(NamedTuple):
# this is triggered when the lnwatcher is all done with the outpoint used as index in LNWatcher.tx_progress # this is triggered when the lnwatcher is all done with the outpoint used as index in LNWatcher.tx_progress
@ -332,7 +333,7 @@ CHANNEL_OPENING_TIMEOUT = 24*60*60
class LNWalletWatcher(LNWatcher): class LNWalletWatcher(LNWatcher):
def __init__(self, lnworker, network): def __init__(self, lnworker: 'LNWallet', network: 'Network'):
LNWatcher.__init__(self, network) LNWatcher.__init__(self, network)
self.network = network self.network = network
self.lnworker = lnworker self.lnworker = lnworker

20
electrum/tests/test_lnchannel.py

@ -786,15 +786,15 @@ class TestChanReserve(ElectrumTestCase):
alice_idx = self.alice_channel.add_htlc(htlc_dict).htlc_id alice_idx = self.alice_channel.add_htlc(htlc_dict).htlc_id
bob_idx = self.bob_channel.receive_htlc(htlc_dict).htlc_id bob_idx = self.bob_channel.receive_htlc(htlc_dict).htlc_id
force_state_transition(self.alice_channel, self.bob_channel) force_state_transition(self.alice_channel, self.bob_channel)
self.check_bals(one_bitcoin_in_msat*3\ self.check_bals(one_bitcoin_in_msat * 3
- self.alice_channel.pending_local_fee(), - self.alice_channel.get_next_fee(LOCAL),
one_bitcoin_in_msat*5) one_bitcoin_in_msat * 5)
self.bob_channel.settle_htlc(paymentPreimage, bob_idx) self.bob_channel.settle_htlc(paymentPreimage, bob_idx)
self.alice_channel.receive_htlc_settle(paymentPreimage, alice_idx) self.alice_channel.receive_htlc_settle(paymentPreimage, alice_idx)
force_state_transition(self.alice_channel, self.bob_channel) force_state_transition(self.alice_channel, self.bob_channel)
self.check_bals(one_bitcoin_in_msat*3\ self.check_bals(one_bitcoin_in_msat * 3
- self.alice_channel.pending_local_fee(), - self.alice_channel.get_next_fee(LOCAL),
one_bitcoin_in_msat*7) one_bitcoin_in_msat * 7)
# And now let Bob add an HTLC of 1 BTC. This will take Bob's balance # And now let Bob add an HTLC of 1 BTC. This will take Bob's balance
# all the way down to his channel reserve, but since he is not paying # all the way down to his channel reserve, but since he is not paying
# the fee this is okay. # the fee this is okay.
@ -802,9 +802,9 @@ class TestChanReserve(ElectrumTestCase):
self.bob_channel.add_htlc(htlc_dict) self.bob_channel.add_htlc(htlc_dict)
self.alice_channel.receive_htlc(htlc_dict) self.alice_channel.receive_htlc(htlc_dict)
force_state_transition(self.alice_channel, self.bob_channel) force_state_transition(self.alice_channel, self.bob_channel)
self.check_bals(one_bitcoin_in_msat*3\ self.check_bals(one_bitcoin_in_msat * 3 \
- self.alice_channel.pending_local_fee(), - self.alice_channel.get_next_fee(LOCAL),
one_bitcoin_in_msat*6) one_bitcoin_in_msat * 6)
def check_bals(self, amt1, amt2): def check_bals(self, amt1, amt2):
self.assertEqual(self.alice_channel.available_to_spend(LOCAL), amt1) self.assertEqual(self.alice_channel.available_to_spend(LOCAL), amt1)
@ -840,7 +840,7 @@ class TestDust(ElectrumTestCase):
self.assertEqual(len(alice_ctx.outputs()), 3) self.assertEqual(len(alice_ctx.outputs()), 3)
self.assertEqual(len(bob_ctx.outputs()), 2) self.assertEqual(len(bob_ctx.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.get_next_fee(LOCAL), default_fee + htlcAmt)
bob_channel.settle_htlc(paymentPreimage, bobHtlcIndex) 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)

97
electrum/tests/test_lnutil.py

@ -510,13 +510,23 @@ class TestLNUtil(ElectrumTestCase):
htlcs = [ScriptHtlc(htlc[x], htlc_obj[x]) for x in range(5)] htlcs = [ScriptHtlc(htlc[x], htlc_obj[x]) for x in range(5)]
our_commit_tx = make_commitment( our_commit_tx = make_commitment(
commitment_number, ctn=commitment_number,
local_funding_pubkey, remote_funding_pubkey, remotepubkey, local_funding_pubkey=local_funding_pubkey,
local_payment_basepoint, remote_payment_basepoint, remote_funding_pubkey=remote_funding_pubkey,
local_revocation_pubkey, local_delayedpubkey, local_delay, remote_payment_pubkey=remotepubkey,
funding_tx_id, funding_output_index, funding_amount_satoshi, funder_payment_basepoint=local_payment_basepoint,
to_local_msat, to_remote_msat, local_dust_limit_satoshi, fundee_payment_basepoint=remote_payment_basepoint,
calc_fees_for_commitment_tx(num_htlcs=len(htlcs), feerate=local_feerate_per_kw, is_local_initiator=True), htlcs=htlcs) revocation_pubkey=local_revocation_pubkey,
delayed_pubkey=local_delayedpubkey,
to_self_delay=local_delay,
funding_txid=funding_tx_id,
funding_pos=funding_output_index,
funding_sat=funding_amount_satoshi,
local_amount=to_local_msat,
remote_amount=to_remote_msat,
dust_limit_sat=local_dust_limit_satoshi,
fees_per_participant=calc_fees_for_commitment_tx(num_htlcs=len(htlcs), feerate=local_feerate_per_kw, is_local_initiator=True),
htlcs=htlcs)
self.sign_and_insert_remote_sig(our_commit_tx, remote_funding_pubkey, remote_signature, local_funding_pubkey, local_funding_privkey) self.sign_and_insert_remote_sig(our_commit_tx, remote_funding_pubkey, remote_signature, local_funding_pubkey, local_funding_privkey)
self.assertEqual(str(our_commit_tx), output_commit_tx) self.assertEqual(str(our_commit_tx), output_commit_tx)
@ -587,13 +597,23 @@ class TestLNUtil(ElectrumTestCase):
output_commit_tx= "02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b820b584a488489000000000038b02b8001c0c62d0000000000160014ccf1af2f2aabee14bb40fa3851ab2301de8431100400473044022031a82b51bd014915fe68928d1abf4b9885353fb896cac10c3fdd88d7f9c7f2e00220716bda819641d2c63e65d3549b6120112e1aeaf1742eed94a471488e79e206b101473044022064901950be922e62cbe3f2ab93de2b99f37cff9fc473e73e394b27f88ef0731d02206d1dfa227527b4df44a07599289e207d6fd9cca60c0365682dcd3deaf739567e01475221023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb21030e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c152ae3e195220" output_commit_tx= "02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b820b584a488489000000000038b02b8001c0c62d0000000000160014ccf1af2f2aabee14bb40fa3851ab2301de8431100400473044022031a82b51bd014915fe68928d1abf4b9885353fb896cac10c3fdd88d7f9c7f2e00220716bda819641d2c63e65d3549b6120112e1aeaf1742eed94a471488e79e206b101473044022064901950be922e62cbe3f2ab93de2b99f37cff9fc473e73e394b27f88ef0731d02206d1dfa227527b4df44a07599289e207d6fd9cca60c0365682dcd3deaf739567e01475221023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb21030e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c152ae3e195220"
our_commit_tx = make_commitment( our_commit_tx = make_commitment(
commitment_number, ctn=commitment_number,
local_funding_pubkey, remote_funding_pubkey, remotepubkey, local_funding_pubkey=local_funding_pubkey,
local_payment_basepoint, remote_payment_basepoint, remote_funding_pubkey=remote_funding_pubkey,
local_revocation_pubkey, local_delayedpubkey, local_delay, remote_payment_pubkey=remotepubkey,
funding_tx_id, funding_output_index, funding_amount_satoshi, funder_payment_basepoint=local_payment_basepoint,
to_local_msat, to_remote_msat, local_dust_limit_satoshi, fundee_payment_basepoint=remote_payment_basepoint,
calc_fees_for_commitment_tx(num_htlcs=0, feerate=local_feerate_per_kw, is_local_initiator=True), htlcs=[]) revocation_pubkey=local_revocation_pubkey,
delayed_pubkey=local_delayedpubkey,
to_self_delay=local_delay,
funding_txid=funding_tx_id,
funding_pos=funding_output_index,
funding_sat=funding_amount_satoshi,
local_amount=to_local_msat,
remote_amount=to_remote_msat,
dust_limit_sat=local_dust_limit_satoshi,
fees_per_participant=calc_fees_for_commitment_tx(num_htlcs=0, feerate=local_feerate_per_kw, is_local_initiator=True),
htlcs=[])
self.sign_and_insert_remote_sig(our_commit_tx, remote_funding_pubkey, remote_signature, local_funding_pubkey, local_funding_privkey) self.sign_and_insert_remote_sig(our_commit_tx, remote_funding_pubkey, remote_signature, local_funding_pubkey, local_funding_privkey)
self.assertEqual(str(our_commit_tx), output_commit_tx) self.assertEqual(str(our_commit_tx), output_commit_tx)
@ -606,13 +626,23 @@ class TestLNUtil(ElectrumTestCase):
output_commit_tx= "02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b820b584a488489000000000038b02b8001c0c62d0000000000160014ccf1af2f2aabee14bb40fa3851ab2301de8431100400473044022031a82b51bd014915fe68928d1abf4b9885353fb896cac10c3fdd88d7f9c7f2e00220716bda819641d2c63e65d3549b6120112e1aeaf1742eed94a471488e79e206b101473044022064901950be922e62cbe3f2ab93de2b99f37cff9fc473e73e394b27f88ef0731d02206d1dfa227527b4df44a07599289e207d6fd9cca60c0365682dcd3deaf739567e01475221023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb21030e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c152ae3e195220" output_commit_tx= "02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b820b584a488489000000000038b02b8001c0c62d0000000000160014ccf1af2f2aabee14bb40fa3851ab2301de8431100400473044022031a82b51bd014915fe68928d1abf4b9885353fb896cac10c3fdd88d7f9c7f2e00220716bda819641d2c63e65d3549b6120112e1aeaf1742eed94a471488e79e206b101473044022064901950be922e62cbe3f2ab93de2b99f37cff9fc473e73e394b27f88ef0731d02206d1dfa227527b4df44a07599289e207d6fd9cca60c0365682dcd3deaf739567e01475221023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb21030e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c152ae3e195220"
our_commit_tx = make_commitment( our_commit_tx = make_commitment(
commitment_number, ctn=commitment_number,
local_funding_pubkey, remote_funding_pubkey, remotepubkey, local_funding_pubkey=local_funding_pubkey,
local_payment_basepoint, remote_payment_basepoint, remote_funding_pubkey=remote_funding_pubkey,
local_revocation_pubkey, local_delayedpubkey, local_delay, remote_payment_pubkey=remotepubkey,
funding_tx_id, funding_output_index, funding_amount_satoshi, funder_payment_basepoint=local_payment_basepoint,
to_local_msat, to_remote_msat, local_dust_limit_satoshi, fundee_payment_basepoint=remote_payment_basepoint,
calc_fees_for_commitment_tx(num_htlcs=0, feerate=local_feerate_per_kw, is_local_initiator=True), htlcs=[]) revocation_pubkey=local_revocation_pubkey,
delayed_pubkey=local_delayedpubkey,
to_self_delay=local_delay,
funding_txid=funding_tx_id,
funding_pos=funding_output_index,
funding_sat=funding_amount_satoshi,
local_amount=to_local_msat,
remote_amount=to_remote_msat,
dust_limit_sat=local_dust_limit_satoshi,
fees_per_participant=calc_fees_for_commitment_tx(num_htlcs=0, feerate=local_feerate_per_kw, is_local_initiator=True),
htlcs=[])
self.sign_and_insert_remote_sig(our_commit_tx, remote_funding_pubkey, remote_signature, local_funding_pubkey, local_funding_privkey) self.sign_and_insert_remote_sig(our_commit_tx, remote_funding_pubkey, remote_signature, local_funding_pubkey, local_funding_privkey)
self.assertEqual(str(our_commit_tx), output_commit_tx) self.assertEqual(str(our_commit_tx), output_commit_tx)
@ -662,15 +692,24 @@ class TestLNUtil(ElectrumTestCase):
# to_remote amount 3000000 P2WPKH(0394854aa6eab5b2a8122cc726e9dded053a2184d88256816826d6231c068d4a5b) # to_remote amount 3000000 P2WPKH(0394854aa6eab5b2a8122cc726e9dded053a2184d88256816826d6231c068d4a5b)
remote_signature = "3045022100f51d2e566a70ba740fc5d8c0f07b9b93d2ed741c3c0860c613173de7d39e7968022041376d520e9c0e1ad52248ddf4b22e12be8763007df977253ef45a4ca3bdb7c0" remote_signature = "3045022100f51d2e566a70ba740fc5d8c0f07b9b93d2ed741c3c0860c613173de7d39e7968022041376d520e9c0e1ad52248ddf4b22e12be8763007df977253ef45a4ca3bdb7c0"
# local_signature = 3044022051b75c73198c6deee1a875871c3961832909acd297c6b908d59e3319e5185a46022055c419379c5051a78d00dbbce11b5b664a0c22815fbcc6fcef6b1937c3836939 # local_signature = 3044022051b75c73198c6deee1a875871c3961832909acd297c6b908d59e3319e5185a46022055c419379c5051a78d00dbbce11b5b664a0c22815fbcc6fcef6b1937c3836939
htlcs=[]
our_commit_tx = make_commitment( our_commit_tx = make_commitment(
commitment_number, ctn=commitment_number,
local_funding_pubkey, remote_funding_pubkey, remotepubkey, local_funding_pubkey=local_funding_pubkey,
local_payment_basepoint, remote_payment_basepoint, remote_funding_pubkey=remote_funding_pubkey,
local_revocation_pubkey, local_delayedpubkey, local_delay, remote_payment_pubkey=remotepubkey,
funding_tx_id, funding_output_index, funding_amount_satoshi, funder_payment_basepoint=local_payment_basepoint,
to_local_msat, to_remote_msat, local_dust_limit_satoshi, fundee_payment_basepoint=remote_payment_basepoint,
calc_fees_for_commitment_tx(num_htlcs=0, feerate=local_feerate_per_kw, is_local_initiator=True), htlcs=[]) revocation_pubkey=local_revocation_pubkey,
delayed_pubkey=local_delayedpubkey,
to_self_delay=local_delay,
funding_txid=funding_tx_id,
funding_pos=funding_output_index,
funding_sat=funding_amount_satoshi,
local_amount=to_local_msat,
remote_amount=to_remote_msat,
dust_limit_sat=local_dust_limit_satoshi,
fees_per_participant=calc_fees_for_commitment_tx(num_htlcs=0, feerate=local_feerate_per_kw, is_local_initiator=True),
htlcs=[])
self.sign_and_insert_remote_sig(our_commit_tx, remote_funding_pubkey, remote_signature, local_funding_pubkey, local_funding_privkey) self.sign_and_insert_remote_sig(our_commit_tx, remote_funding_pubkey, remote_signature, local_funding_pubkey, local_funding_privkey)
ref_commit_tx_str = '02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b820b584a488489000000000038b02b8002c0c62d0000000000160014ccf1af2f2aabee14bb40fa3851ab2301de84311054a56a00000000002200204adb4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e0400473044022051b75c73198c6deee1a875871c3961832909acd297c6b908d59e3319e5185a46022055c419379c5051a78d00dbbce11b5b664a0c22815fbcc6fcef6b1937c383693901483045022100f51d2e566a70ba740fc5d8c0f07b9b93d2ed741c3c0860c613173de7d39e7968022041376d520e9c0e1ad52248ddf4b22e12be8763007df977253ef45a4ca3bdb7c001475221023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb21030e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c152ae3e195220' ref_commit_tx_str = '02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b820b584a488489000000000038b02b8002c0c62d0000000000160014ccf1af2f2aabee14bb40fa3851ab2301de84311054a56a00000000002200204adb4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e0400473044022051b75c73198c6deee1a875871c3961832909acd297c6b908d59e3319e5185a46022055c419379c5051a78d00dbbce11b5b664a0c22815fbcc6fcef6b1937c383693901483045022100f51d2e566a70ba740fc5d8c0f07b9b93d2ed741c3c0860c613173de7d39e7968022041376d520e9c0e1ad52248ddf4b22e12be8763007df977253ef45a4ca3bdb7c001475221023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb21030e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c152ae3e195220'
self.assertEqual(str(our_commit_tx), ref_commit_tx_str) self.assertEqual(str(our_commit_tx), ref_commit_tx_str)

Loading…
Cancel
Save