URI: 
       tlnhtlc: merge config and state, remove unnecessary properties - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit e8471e483b303e6b65f9a2979b9eb9ea530b0490
   DIR parent 1d4c113a3524cf012d5a7c40acdc7a61e16b19ed
  HTML Author: Janus <ysangkok@gmail.com>
       Date:   Wed, 10 Oct 2018 22:54:30 +0200
       
       lnhtlc: merge config and state, remove unnecessary properties
       
       Diffstat:
         M electrum/lnbase.py                  |     188 +++++++++++++++----------------
         M electrum/lnhtlc.py                  |     258 ++++++++++++++-----------------
         M electrum/lnutil.py                  |      48 ++++++++++++++++++++++++-------
         M electrum/lnworker.py                |      12 ++++++------
         M electrum/tests/test_lnhtlc.py       |      90 +++++++++++++++----------------
       
       5 files changed, 292 insertions(+), 304 deletions(-)
       ---
   DIR diff --git a/electrum/lnbase.py b/electrum/lnbase.py
       t@@ -27,9 +27,9 @@ from .util import PrintError, bh2u, print_error, bfh, log_exceptions
        from .transaction import Transaction, TxOutput
        from .lnonion import new_onion_packet, OnionHopsDataSingle, OnionPerHop, decode_onion_error, OnionFailureCode
        from .lnaddr import lndecode
       -from .lnhtlc import HTLCStateMachine, RevokeAndAck
       -from .lnutil import (Outpoint, ChannelConfig, LocalState,
       -                     RemoteState, OnlyPubkeyKeypair, ChannelConstraints, RevocationStore,
       +from .lnhtlc import HTLCStateMachine, RevokeAndAck, htlcsum
       +from .lnutil import (Outpoint, LocalConfig, ChannelConfig,
       +                     RemoteConfig, OnlyPubkeyKeypair, ChannelConstraints, RevocationStore,
                             funding_output_script, get_ecdh, get_per_commitment_secret_from_seed,
                             secret_to_pubkey, LNPeerAddr, PaymentFailure, LnLocalFeatures,
                             LOCAL, REMOTE, HTLCOwner, generate_keypair, LnKeyFamily,
       t@@ -480,7 +480,7 @@ class Peer(PrintError):
            def on_announcement_signatures(self, payload):
                channel_id = payload['channel_id']
                chan = self.channels[payload['channel_id']]
       -        if chan.local_state.was_announced:
       +        if chan.config[LOCAL].was_announced:
                    h, local_node_sig, local_bitcoin_sig = self.send_announcement_signatures(chan)
                else:
                    self.announcement_signatures[channel_id].put_nowait(payload)
       t@@ -530,7 +530,7 @@ class Peer(PrintError):
                    chan.set_state('DISCONNECTED')
                    self.network.trigger_callback('channel', chan)
        
       -    def make_local_config(self, funding_sat, push_msat, initiator: HTLCOwner):
       +    def make_local_config(self, funding_sat, push_msat, initiator: HTLCOwner, feerate):
                # key derivation
                channel_counter = self.lnworker.get_and_inc_counter_for_channel_keys()
                keypair_generator = lambda family: generate_keypair(self.lnworker.ln_keystore, family, channel_counter)
       t@@ -549,6 +549,10 @@ class Peer(PrintError):
                    max_htlc_value_in_flight_msat=0xffffffffffffffff,
                    max_accepted_htlcs=5,
                    initial_msat=initial_msat,
       +            ctn=-1,
       +            next_htlc_id=0,
       +            amount_msat=initial_msat,
       +            feerate=feerate,
                )
                per_commitment_secret_seed = keypair_generator(LnKeyFamily.REVOCATION_ROOT).privkey
                return local_config, per_commitment_secret_seed
       t@@ -556,9 +560,8 @@ class Peer(PrintError):
            @log_exceptions
            async def channel_establishment_flow(self, password, funding_sat, push_msat, temp_channel_id):
                await self.initialized
       -        local_config, per_commitment_secret_seed = self.make_local_config(funding_sat, push_msat, LOCAL)
       -        # amounts
       -        local_feerate = self.current_feerate_per_kw()
       +        feerate = self.current_feerate_per_kw()
       +        local_config, per_commitment_secret_seed = self.make_local_config(funding_sat, push_msat, LOCAL, feerate)
                # for the first commitment transaction
                per_commitment_secret_first = get_per_commitment_secret_from_seed(per_commitment_secret_seed, RevocationStore.START_INDEX)
                per_commitment_point_first = secret_to_pubkey(int.from_bytes(per_commitment_secret_first, 'big'))
       t@@ -569,7 +572,7 @@ class Peer(PrintError):
                    funding_satoshis=funding_sat,
                    push_msat=push_msat,
                    dust_limit_satoshis=local_config.dust_limit_sat,
       -            feerate_per_kw=local_feerate,
       +            feerate_per_kw=feerate,
                    max_accepted_htlcs=local_config.max_accepted_htlcs,
                    funding_pubkey=local_config.multisig_key.pubkey,
                    revocation_basepoint=local_config.revocation_basepoint.pubkey,
       t@@ -587,24 +590,33 @@ class Peer(PrintError):
                if payload.get('error'):
                    raise Exception(payload.get('error'))
                remote_per_commitment_point = payload['first_per_commitment_point']
       -        remote_config=ChannelConfig(
       +        funding_txn_minimum_depth = int.from_bytes(payload['minimum_depth'], 'big')
       +        remote_dust_limit_sat = int.from_bytes(payload['dust_limit_satoshis'], byteorder='big')
       +        assert remote_dust_limit_sat < 600, remote_dust_limit_sat
       +        assert int.from_bytes(payload['htlc_minimum_msat'], 'big') < 600 * 1000
       +        remote_max = int.from_bytes(payload['max_htlc_value_in_flight_msat'], 'big')
       +        assert remote_max >= 198 * 1000 * 1000, remote_max
       +        their_revocation_store = RevocationStore()
       +        remote_config = RemoteConfig(
                    payment_basepoint=OnlyPubkeyKeypair(payload['payment_basepoint']),
                    multisig_key=OnlyPubkeyKeypair(payload["funding_pubkey"]),
                    htlc_basepoint=OnlyPubkeyKeypair(payload['htlc_basepoint']),
                    delayed_basepoint=OnlyPubkeyKeypair(payload['delayed_payment_basepoint']),
                    revocation_basepoint=OnlyPubkeyKeypair(payload['revocation_basepoint']),
                    to_self_delay=int.from_bytes(payload['to_self_delay'], 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'),
       +            dust_limit_sat=remote_dust_limit_sat,
       +            max_htlc_value_in_flight_msat=remote_max,
                    max_accepted_htlcs=int.from_bytes(payload["max_accepted_htlcs"], 'big'),
       -            initial_msat=push_msat
       +            initial_msat=push_msat,
       +            ctn = -1,
       +            amount_msat=push_msat,
       +            next_htlc_id = 0,
       +            feerate=feerate,
       +
       +            next_per_commitment_point=remote_per_commitment_point,
       +            current_per_commitment_point=None,
       +            revocation_store=their_revocation_store,
                )
       -        funding_txn_minimum_depth = int.from_bytes(payload['minimum_depth'], 'big')
       -        assert remote_config.dust_limit_sat < 600
       -        assert int.from_bytes(payload['htlc_minimum_msat'], 'big') < 600 * 1000
       -        assert remote_config.max_htlc_value_in_flight_msat >= 198 * 1000 * 1000, remote_config.max_htlc_value_in_flight_msat
       -        self.print_error('remote delay', remote_config.to_self_delay)
       -        self.print_error('funding_txn_minimum_depth', funding_txn_minimum_depth)
                # create funding tx
                redeem_script = funding_output_script(local_config, remote_config)
                funding_address = bitcoin.redeem_script_to_address('p2wsh', redeem_script)
       t@@ -612,38 +624,21 @@ class Peer(PrintError):
                funding_tx = self.lnworker.wallet.mktx([funding_output], password, self.lnworker.config, 1000)
                funding_txid = funding_tx.txid()
                funding_index = funding_tx.outputs().index(funding_output)
       -        # compute amounts
       -        local_amount = funding_sat*1000 - push_msat
       -        remote_amount = push_msat
                # remote commitment transaction
                channel_id, funding_txid_bytes = channel_id_from_funding_tx(funding_txid, funding_index)
       -        their_revocation_store = RevocationStore()
                chan = {
                        "node_id": self.pubkey,
                        "channel_id": channel_id,
                        "short_channel_id": None,
                        "funding_outpoint": Outpoint(funding_txid, funding_index),
       -                "local_config": local_config,
                        "remote_config": remote_config,
       -                "remote_state": RemoteState(
       -                    ctn = -1,
       -                    next_per_commitment_point=remote_per_commitment_point,
       -                    current_per_commitment_point=None,
       -                    amount_msat=remote_amount,
       -                    revocation_store=their_revocation_store,
       -                    next_htlc_id = 0,
       -                    feerate=local_feerate
       -                ),
       -                "local_state": LocalState(
       -                    ctn = -1,
       +                "local_config": LocalConfig(
       +                    **local_config._asdict(),
                            per_commitment_secret_seed=per_commitment_secret_seed,
       -                    amount_msat=local_amount,
       -                    next_htlc_id = 0,
                            funding_locked_received = False,
                            was_announced = False,
                            current_commitment_signature = None,
                            current_htlc_signatures = None,
       -                    feerate=local_feerate
                        ),
                        "constraints": ChannelConstraints(capacity=funding_sat, is_initiator=True, funding_txn_minimum_depth=funding_txn_minimum_depth),
                        "remote_commitment_to_be_revoked": None,
       t@@ -665,8 +660,8 @@ class Peer(PrintError):
                success, _txid = await self.network.broadcast_transaction(funding_tx)
                assert success, success
                m.remote_commitment_to_be_revoked = m.pending_remote_commitment
       -        m.remote_state = m.remote_state._replace(ctn=0)
       -        m.local_state = m.local_state._replace(ctn=0, current_commitment_signature=remote_sig)
       +        m.config[REMOTE] = m.config[REMOTE]._replace(ctn=0)
       +        m.config[LOCAL] = m.config[LOCAL]._replace(ctn=0, current_commitment_signature=remote_sig)
                m.set_state('OPENING')
                return m
        
       t@@ -677,21 +672,10 @@ class Peer(PrintError):
                    raise Exception('wrong chain_hash')
                funding_sat = int.from_bytes(payload['funding_satoshis'], 'big')
                push_msat = int.from_bytes(payload['push_msat'], 'big')
       +        feerate = int.from_bytes(payload['feerate_per_kw'], 'big')
        
       -        remote_config = ChannelConfig(
       -            payment_basepoint=OnlyPubkeyKeypair(payload['payment_basepoint']),
       -            multisig_key=OnlyPubkeyKeypair(payload['funding_pubkey']),
       -            htlc_basepoint=OnlyPubkeyKeypair(payload['htlc_basepoint']),
       -            delayed_basepoint=OnlyPubkeyKeypair(payload['delayed_payment_basepoint']),
       -            revocation_basepoint=OnlyPubkeyKeypair(payload['revocation_basepoint']),
       -            to_self_delay=int.from_bytes(payload['to_self_delay'], 'big'),
       -            dust_limit_sat=int.from_bytes(payload['dust_limit_satoshis'], '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'),
       -            initial_msat=funding_sat * 1000 - push_msat,
       -        )
                temp_chan_id = payload['temporary_channel_id']
       -        local_config, per_commitment_secret_seed = self.make_local_config(funding_sat * 1000, push_msat, REMOTE)
       +        local_config, per_commitment_secret_seed = self.make_local_config(funding_sat * 1000, push_msat, REMOTE, feerate)
        
                # for the first commitment transaction
                per_commitment_secret_first = get_per_commitment_secret_from_seed(per_commitment_secret_seed, RevocationStore.START_INDEX)
       t@@ -719,33 +703,39 @@ class Peer(PrintError):
                funding_txid = bh2u(funding_created['funding_txid'][::-1])
                channel_id, funding_txid_bytes = channel_id_from_funding_tx(funding_txid, funding_idx)
                their_revocation_store = RevocationStore()
       -        local_feerate = int.from_bytes(payload['feerate_per_kw'], 'big')
       +        remote_balance_sat = funding_sat * 1000 - push_msat
                chan = {
                        "node_id": self.pubkey,
                        "channel_id": channel_id,
                        "short_channel_id": None,
                        "funding_outpoint": Outpoint(funding_txid, funding_idx),
       -                "local_config": local_config,
       -                "remote_config": remote_config,
       -                "remote_state": RemoteState(
       +                "remote_config": RemoteConfig(
       +                    payment_basepoint=OnlyPubkeyKeypair(payload['payment_basepoint']),
       +                    multisig_key=OnlyPubkeyKeypair(payload['funding_pubkey']),
       +                    htlc_basepoint=OnlyPubkeyKeypair(payload['htlc_basepoint']),
       +                    delayed_basepoint=OnlyPubkeyKeypair(payload['delayed_payment_basepoint']),
       +                    revocation_basepoint=OnlyPubkeyKeypair(payload['revocation_basepoint']),
       +                    to_self_delay=int.from_bytes(payload['to_self_delay'], 'big'),
       +                    dust_limit_sat=int.from_bytes(payload['dust_limit_satoshis'], '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'),
       +                    initial_msat=remote_balance_sat,
                            ctn = -1,
       +                    amount_msat=remote_balance_sat,
       +                    next_htlc_id = 0,
       +                    feerate=feerate,
       +
                            next_per_commitment_point=payload['first_per_commitment_point'],
                            current_per_commitment_point=None,
       -                    amount_msat=remote_config.initial_msat,
                            revocation_store=their_revocation_store,
       -                    next_htlc_id = 0,
       -                    feerate=local_feerate
                        ),
       -                "local_state": LocalState(
       -                    ctn = -1,
       +                "local_config": LocalConfig(
       +                    **local_config._asdict(),
                            per_commitment_secret_seed=per_commitment_secret_seed,
       -                    amount_msat=local_config.initial_msat,
       -                    next_htlc_id = 0,
                            funding_locked_received = False,
                            was_announced = False,
                            current_commitment_signature = None,
                            current_htlc_signatures = None,
       -                    feerate=local_feerate
                        ),
                        "constraints": ChannelConstraints(capacity=funding_sat, is_initiator=False, funding_txn_minimum_depth=min_depth),
                        "remote_commitment_to_be_revoked": None,
       t@@ -762,8 +752,8 @@ class Peer(PrintError):
                ))
                m.set_state('OPENING')
                m.remote_commitment_to_be_revoked = m.pending_remote_commitment
       -        m.remote_state = m.remote_state._replace(ctn=0)
       -        m.local_state = m.local_state._replace(ctn=0, current_commitment_signature=remote_sig)
       +        m.config[REMOTE] = m.config[REMOTE]._replace(ctn=0)
       +        m.config[LOCAL] = m.config[LOCAL]._replace(ctn=0, current_commitment_signature=remote_sig)
                self.lnworker.save_channel(m)
                self.lnwatcher.watch_channel(m.get_funding_address(), m.funding_outpoint.to_str())
                self.lnworker.on_channels_updated()
       t@@ -776,7 +766,7 @@ class Peer(PrintError):
                    else:
                        break
                outp = funding_tx.outputs()[funding_idx]
       -        redeem_script = funding_output_script(remote_config, local_config)
       +        redeem_script = funding_output_script(m.config[REMOTE], m.config[LOCAL])
                funding_address = bitcoin.redeem_script_to_address('p2wsh', redeem_script)
                if outp != TxOutput(bitcoin.TYPE_ADDRESS, funding_address, funding_sat):
                    m.set_state('DISCONNECTED')
       t@@ -794,12 +784,12 @@ class Peer(PrintError):
                self.network.trigger_callback('channel', chan)
                self.send_message(gen_msg("channel_reestablish",
                    channel_id=chan_id,
       -            next_local_commitment_number=chan.local_state.ctn+1,
       -            next_remote_revocation_number=chan.remote_state.ctn
       +            next_local_commitment_number=chan.config[LOCAL].ctn+1,
       +            next_remote_revocation_number=chan.config[REMOTE].ctn
                ))
                await self.channel_reestablished[chan_id]
                chan.set_state('OPENING')
       -        if chan.local_state.funding_locked_received and chan.short_channel_id:
       +        if chan.config[LOCAL].funding_locked_received and chan.short_channel_id:
                    self.mark_open(chan)
                self.network.trigger_callback('channel', chan)
        
       t@@ -822,24 +812,24 @@ class Peer(PrintError):
                channel_reestablish_msg = payload
                # compare remote ctns
                remote_ctn = int.from_bytes(channel_reestablish_msg["next_local_commitment_number"], 'big')
       -        if remote_ctn != chan.remote_state.ctn + 1:
       -            self.print_error("expected remote ctn {}, got {}".format(chan.remote_state.ctn + 1, remote_ctn))
       +        if remote_ctn != chan.config[REMOTE].ctn + 1:
       +            self.print_error("expected remote ctn {}, got {}".format(chan.config[REMOTE].ctn + 1, remote_ctn))
                    # TODO iff their ctn is lower than ours, we should force close instead
                    try_to_get_remote_to_force_close_with_their_latest()
                    return
                # compare local ctns
                local_ctn = int.from_bytes(channel_reestablish_msg["next_remote_revocation_number"], 'big')
       -        if local_ctn != chan.local_state.ctn:
       -            self.print_error("expected local ctn {}, got {}".format(chan.local_state.ctn, local_ctn))
       +        if local_ctn != chan.config[LOCAL].ctn:
       +            self.print_error("expected local ctn {}, got {}".format(chan.config[LOCAL].ctn, local_ctn))
                    # TODO iff their ctn is lower than ours, we should force close instead
                    try_to_get_remote_to_force_close_with_their_latest()
                    return
                # compare per commitment points (needs data_protect option)
                their_pcp = channel_reestablish_msg.get("my_current_per_commitment_point", None)
                if their_pcp is not None:
       -            our_pcp = chan.remote_state.current_per_commitment_point
       +            our_pcp = chan.config[REMOTE].current_per_commitment_point
                    if our_pcp is None:
       -                our_pcp = chan.remote_state.next_per_commitment_point
       +                our_pcp = chan.config[REMOTE].next_per_commitment_point
                    if our_pcp != their_pcp:
                        self.print_error("Remote PCP mismatch: {} {}".format(bh2u(our_pcp), bh2u(their_pcp)))
                        # FIXME ...what now?
       t@@ -852,10 +842,10 @@ class Peer(PrintError):
                channel_id = chan.channel_id
                per_commitment_secret_index = RevocationStore.START_INDEX - 1
                per_commitment_point_second = secret_to_pubkey(int.from_bytes(
       -            get_per_commitment_secret_from_seed(chan.local_state.per_commitment_secret_seed, per_commitment_secret_index), 'big'))
       +            get_per_commitment_secret_from_seed(chan.config[LOCAL].per_commitment_secret_seed, per_commitment_secret_index), 'big'))
                # note: if funding_locked was not yet received, we might send it multiple times
                self.send_message(gen_msg("funding_locked", channel_id=channel_id, next_per_commitment_point=per_commitment_point_second))
       -        if chan.local_state.funding_locked_received:
       +        if chan.config[LOCAL].funding_locked_received:
                    self.mark_open(chan)
        
            def on_funding_locked(self, payload):
       t@@ -864,13 +854,13 @@ class Peer(PrintError):
                if not chan:
                    print(self.channels)
                    raise Exception("Got unknown funding_locked", channel_id)
       -        if not chan.local_state.funding_locked_received:
       -            our_next_point = chan.remote_state.next_per_commitment_point
       +        if not chan.config[LOCAL].funding_locked_received:
       +            our_next_point = chan.config[REMOTE].next_per_commitment_point
                    their_next_point = payload["next_per_commitment_point"]
       -            new_remote_state = chan.remote_state._replace(next_per_commitment_point=their_next_point, current_per_commitment_point=our_next_point)
       -            new_local_state = chan.local_state._replace(funding_locked_received = True)
       -            chan.remote_state=new_remote_state
       -            chan.local_state=new_local_state
       +            new_remote_state = chan.config[REMOTE]._replace(next_per_commitment_point=their_next_point, current_per_commitment_point=our_next_point)
       +            new_local_state = chan.config[LOCAL]._replace(funding_locked_received = True)
       +            chan.config[REMOTE]=new_remote_state
       +            chan.config[LOCAL]=new_local_state
                    self.lnworker.save_channel(chan)
                if chan.short_channel_id:
                    self.mark_open(chan)
       t@@ -881,11 +871,11 @@ class Peer(PrintError):
        
                Runs on the Network thread.
                """
       -        if not chan.local_state.was_announced and funding_tx_depth >= 6:
       +        if not chan.config[LOCAL].was_announced and funding_tx_depth >= 6:
                    # don't announce our channels
                    # FIXME should this be a field in chan.local_state maybe?
                    return
       -            chan.local_state=chan.local_state._replace(was_announced=True)
       +            chan.config[LOCAL]=chan.config[LOCAL]._replace(was_announced=True)
                    coro = self.handle_announcements(chan)
                    self.lnworker.save_channel(chan)
                    asyncio.run_coroutine_threadsafe(coro, self.network.asyncio_loop)
       t@@ -896,7 +886,7 @@ class Peer(PrintError):
                announcement_signatures_msg = await self.announcement_signatures[chan.channel_id].get()
                remote_node_sig = announcement_signatures_msg["node_signature"]
                remote_bitcoin_sig = announcement_signatures_msg["bitcoin_signature"]
       -        if not ecc.verify_signature(chan.remote_config.multisig_key.pubkey, remote_bitcoin_sig, h):
       +        if not ecc.verify_signature(chan.config[REMOTE].multisig_key.pubkey, remote_bitcoin_sig, h):
                    raise Exception("bitcoin_sig invalid in announcement_signatures")
                if not ecc.verify_signature(self.pubkey, remote_node_sig, h):
                    raise Exception("node_sig invalid in announcement_signatures")
       t@@ -904,7 +894,7 @@ class Peer(PrintError):
                node_sigs = [local_node_sig, remote_node_sig]
                bitcoin_sigs = [local_bitcoin_sig, remote_bitcoin_sig]
                node_ids = [privkey_to_pubkey(self.privkey), self.pubkey]
       -        bitcoin_keys = [chan.local_config.multisig_key.pubkey, chan.remote_config.multisig_key.pubkey]
       +        bitcoin_keys = [chan.config[LOCAL].multisig_key.pubkey, chan.config[REMOTE].multisig_key.pubkey]
        
                if node_ids[0] > node_ids[1]:
                    node_sigs.reverse()
       t@@ -935,14 +925,14 @@ class Peer(PrintError):
                if chan.get_state() == "OPEN":
                    return
                # NOTE: even closed channels will be temporarily marked "OPEN"
       -        assert chan.local_state.funding_locked_received
       +        assert chan.config[LOCAL].funding_locked_received
                chan.set_state("OPEN")
                self.network.trigger_callback('channel', chan)
                # add channel to database
                pubkey_ours = self.lnworker.node_keypair.pubkey
                pubkey_theirs = self.pubkey
                node_ids = [pubkey_theirs, pubkey_ours]
       -        bitcoin_keys = [chan.local_config.multisig_key.pubkey, chan.remote_config.multisig_key.pubkey]
       +        bitcoin_keys = [chan.config[LOCAL].multisig_key.pubkey, chan.config[REMOTE].multisig_key.pubkey]
                sorted_node_ids = list(sorted(node_ids))
                if sorted_node_ids != node_ids:
                    node_ids = sorted_node_ids
       t@@ -977,8 +967,8 @@ class Peer(PrintError):
        
            def send_announcement_signatures(self, chan):
        
       -        bitcoin_keys = [chan.local_config.multisig_key.pubkey,
       -                        chan.remote_config.multisig_key.pubkey]
       +        bitcoin_keys = [chan.config[LOCAL].multisig_key.pubkey,
       +                        chan.config[REMOTE].multisig_key.pubkey]
        
                node_ids = [privkey_to_pubkey(self.privkey),
                            self.pubkey]
       t@@ -1000,7 +990,7 @@ class Peer(PrintError):
                )
                to_hash = chan_ann[256+2:]
                h = bitcoin.Hash(to_hash)
       -        bitcoin_signature = ecc.ECPrivkey(chan.local_config.multisig_key.privkey).sign(h, sig_string_from_r_and_s, get_r_and_s_from_sig_string)
       +        bitcoin_signature = ecc.ECPrivkey(chan.config[LOCAL].multisig_key.privkey).sign(h, sig_string_from_r_and_s, get_r_and_s_from_sig_string)
                node_signature = ecc.ECPrivkey(self.privkey).sign(h, sig_string_from_r_and_s, get_r_and_s_from_sig_string)
                self.send_message(gen_msg("announcement_signatures",
                    channel_id=chan.channel_id,
       t@@ -1105,10 +1095,10 @@ class Peer(PrintError):
                # then no other payment can use this channel either.
                # we need finer blacklisting -- e.g. a blacklist for just this "payment session"?
                # or blacklist entries could store an msat value and also expire
       -        if len(chan.htlcs_in_local) + 1 > chan.remote_config.max_accepted_htlcs:
       +        if len(chan.htlcs(LOCAL, only_pending=True)) + 1 > chan.config[REMOTE].max_accepted_htlcs:
                    raise PaymentFailure('too many HTLCs already in channel')
       -        if chan.htlcsum(chan.htlcs_in_local) + amount_msat > chan.remote_config.max_htlc_value_in_flight_msat:
       -            raise PaymentFailure('HTLC value sum would exceed max allowed: {} msat'.format(chan.remote_config.max_htlc_value_in_flight_msat))
       +        if htlcsum(chan.htlcs(LOCAL, only_pending=True)) + amount_msat > chan.config[REMOTE].max_htlc_value_in_flight_msat:
       +            raise PaymentFailure('HTLC value sum would exceed max allowed: {} msat'.format(chan.config[REMOTE].max_htlc_value_in_flight_msat))
                if msat_local < 0:
                    # FIXME what about channel_reserve_satoshis? will the remote fail the channel if we go below? test.
                    raise PaymentFailure('not enough local balance')
       t@@ -1144,7 +1134,7 @@ class Peer(PrintError):
                channel_id = chan.channel_id
                expected_received_msat = int(decoded.amount * bitcoin.COIN * 1000)
                htlc_id = int.from_bytes(htlc["id"], 'big')
       -        assert htlc_id == chan.remote_state.next_htlc_id, (htlc_id, chan.remote_state.next_htlc_id)
       +        assert htlc_id == chan.config[REMOTE].next_htlc_id, (htlc_id, chan.config[REMOTE].next_htlc_id)
                assert chan.get_state() == "OPEN"
                cltv_expiry = int.from_bytes(htlc["cltv_expiry"], 'big')
                # TODO verify sanity of their cltv expiry
       t@@ -1166,7 +1156,7 @@ class Peer(PrintError):
                self.print_error("commitment_signed", payload)
                channel_id = payload['channel_id']
                chan = self.channels[channel_id]
       -        chan.local_state=chan.local_state._replace(
       +        chan.config[LOCAL]=chan.config[LOCAL]._replace(
                    current_commitment_signature=payload['signature'],
                    current_htlc_signatures=payload['htlc_signature'])
                self.lnworker.save_channel(chan)
   DIR diff --git a/electrum/lnhtlc.py b/electrum/lnhtlc.py
       t@@ -10,7 +10,7 @@ from .bitcoin import Hash, TYPE_SCRIPT, TYPE_ADDRESS
        from .bitcoin import redeem_script_to_address
        from .crypto import sha256
        from . import ecc
       -from .lnutil import Outpoint, ChannelConfig, LocalState, RemoteState, Keypair, OnlyPubkeyKeypair, ChannelConstraints, RevocationStore, EncumberedTransaction
       +from .lnutil import Outpoint, LocalConfig, RemoteConfig, Keypair, OnlyPubkeyKeypair, ChannelConstraints, RevocationStore, EncumberedTransaction
        from .lnutil import get_per_commitment_secret_from_seed
        from .lnutil import make_commitment_output_to_remote_address, make_commitment_output_to_local_witness_script
        from .lnutil import secret_to_pubkey, derive_privkey, derive_pubkey, derive_blinded_pubkey, derive_blinded_privkey
       t@@ -75,59 +75,50 @@ class UpdateAddHtlc(namedtuple('UpdateAddHtlc', ['amount_msat', 'payment_hash', 
                if 'locked_in' not in kwargs:
                    kwargs['locked_in'] = {LOCAL: None, REMOTE: None}
                else:
       -            kwargs['locked_in'] = {HTLCOwner(int(x)): y for x,y in kwargs['locked_in']}
       +            kwargs['locked_in'] = {HTLCOwner(int(x)): y for x,y in kwargs['locked_in'].items()}
                return super().__new__(cls, **kwargs)
        
       -is_key = lambda k: k.endswith("_basepoint") or k.endswith("_key")
       -
       -def maybeDecode(k, v):
       -    assert type(v) is not list
       -    if 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:
       -        return binascii.unhexlify(v)
       -    return v
       -
       -def decodeAll(v):
       -    return {i: maybeDecode(i, j) for i, j in v.items()} if isinstance(v, dict) else v
       -
       -def typeWrap(k, v, local):
       -    if is_key(k):
       -        if local:
       -            return Keypair(**v)
       +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))
                else:
       -            return OnlyPubkeyKeypair(**v)
       -    return v
       +            yield (k, v)
       +
       +def htlcsum(htlcs):
       +    return sum([x.amount_msat for x in htlcs])
        
        class HTLCStateMachine(PrintError):
            def diagnostic_name(self):
                return str(self.name)
        
            def __init__(self, state, name = None):
       -        self.local_config = state["local_config"]
       -        if type(self.local_config) is not ChannelConfig:
       -            new_local_config = {k: typeWrap(k, decodeAll(v), True) for k, v in self.local_config.items()}
       -            self.local_config = ChannelConfig(**new_local_config)
       -
       -        self.remote_config = state["remote_config"]
       -        if type(self.remote_config) is not ChannelConfig:
       -            new_remote_config = {k: typeWrap(k, decodeAll(v), False) for k, v in self.remote_config.items()}
       -            self.remote_config = ChannelConfig(**new_remote_config)
       -
       -        self.local_state = state["local_state"]
       -        if type(self.local_state) is not LocalState:
       -            self.local_state = LocalState(**decodeAll(self.local_state))
       -
       -        self.remote_state = state["remote_state"]
       -        if type(self.remote_state) is not RemoteState:
       -            self.remote_state = RemoteState(**decodeAll(self.remote_state))
       -
       -        if type(self.remote_state.revocation_store) is not RevocationStore:
       -            self.remote_state = self.remote_state._replace(revocation_store = RevocationStore.from_json_obj(self.remote_state.revocation_store))
       -
       -        self.channel_id = maybeDecode("channel_id", state["channel_id"]) if type(state["channel_id"]) is not bytes else state["channel_id"]
       -        self.constraints = ChannelConstraints(**decodeAll(state["constraints"])) if type(state["constraints"]) is not ChannelConstraints else state["constraints"]
       -        self.funding_outpoint = Outpoint(**decodeAll(state["funding_outpoint"])) if type(state["funding_outpoint"]) is not Outpoint else state["funding_outpoint"]
       -        self.node_id = maybeDecode("node_id", state["node_id"]) if type(state["node_id"]) is not bytes else state["node_id"]
       -        self.short_channel_id = maybeDecode("short_channel_id", state["short_channel_id"]) if type(state["short_channel_id"]) is not bytes else state["short_channel_id"]
       +        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"]
                self.short_channel_id_predicted = self.short_channel_id
                self.onion_keys = {int(k): bfh(v) for k,v in state['onion_keys'].items()} if 'onion_keys' in state else {}
        
       t@@ -141,7 +132,7 @@ class HTLCStateMachine(PrintError):
                for strname, subject in [('remote_log', REMOTE), ('local_log', LOCAL)]:
                    if strname not in state: continue
                    for y in state[strname]:
       -                htlc = UpdateAddHtlc(*decodeAll(y))
       +                htlc = UpdateAddHtlc(**y)
                        self.log[subject]['adds'][htlc.htlc_id] = htlc
        
                self.name = name
       t@@ -172,7 +163,7 @@ class HTLCStateMachine(PrintError):
                return self._is_funding_txo_spent is False and self._state == 'DISCONNECTED'
        
            def get_funding_address(self):
       -        script = funding_output_script(self.local_config, self.remote_config)
       +        script = funding_output_script(self.config[LOCAL], self.config[REMOTE])
                return redeem_script_to_address('p2wsh', script)
        
            def add_htlc(self, htlc):
       t@@ -181,10 +172,10 @@ class HTLCStateMachine(PrintError):
                should be called when preparing to send an outgoing HTLC.
                """
                assert type(htlc) is dict
       -        htlc = UpdateAddHtlc(**htlc, htlc_id=self.local_state.next_htlc_id)
       +        htlc = UpdateAddHtlc(**htlc, htlc_id=self.config[LOCAL].next_htlc_id)
                self.log[LOCAL]['adds'][htlc.htlc_id] = htlc
                self.print_error("add_htlc")
       -        self.local_state=self.local_state._replace(next_htlc_id=htlc.htlc_id + 1)
       +        self.config[LOCAL]=self.config[LOCAL]._replace(next_htlc_id=htlc.htlc_id + 1)
                return htlc.htlc_id
        
            def receive_htlc(self, htlc):
       t@@ -194,10 +185,10 @@ class HTLCStateMachine(PrintError):
                party.
                """
                assert type(htlc) is dict
       -        htlc = UpdateAddHtlc(**htlc, htlc_id = self.remote_state.next_htlc_id)
       +        htlc = UpdateAddHtlc(**htlc, htlc_id = self.config[REMOTE].next_htlc_id)
                self.log[REMOTE]['adds'][htlc.htlc_id] = htlc
                self.print_error("receive_htlc")
       -        self.remote_state=self.remote_state._replace(next_htlc_id=htlc.htlc_id + 1)
       +        self.config[REMOTE]=self.config[REMOTE]._replace(next_htlc_id=htlc.htlc_id + 1)
                return htlc.htlc_id
        
            def sign_next_commitment(self):
       t@@ -215,15 +206,15 @@ class HTLCStateMachine(PrintError):
                """
                for htlc in self.log[LOCAL]['adds'].values():
                    if htlc.locked_in[LOCAL] is None:
       -                htlc.locked_in[LOCAL] = self.local_state.ctn
       +                htlc.locked_in[LOCAL] = self.config[LOCAL].ctn
                self.print_error("sign_next_commitment")
        
                pending_remote_commitment = self.pending_remote_commitment
       -        sig_64 = sign_and_get_sig_string(pending_remote_commitment, self.local_config, self.remote_config)
       +        sig_64 = sign_and_get_sig_string(pending_remote_commitment, self.config[LOCAL], self.config[REMOTE])
        
                their_remote_htlc_privkey_number = derive_privkey(
       -            int.from_bytes(self.local_config.htlc_basepoint.privkey, 'big'),
       -            self.remote_state.next_per_commitment_point)
       +            int.from_bytes(self.config[LOCAL].htlc_basepoint.privkey, 'big'),
       +            self.config[REMOTE].next_per_commitment_point)
                their_remote_htlc_privkey = their_remote_htlc_privkey_number.to_bytes(32, 'big')
        
                for_us = False
       t@@ -231,7 +222,7 @@ class HTLCStateMachine(PrintError):
                htlcsigs = []
                for we_receive, htlcs in zip([True, False], [self.included_htlcs(REMOTE, REMOTE), self.included_htlcs(REMOTE, LOCAL)]):
                    for htlc in htlcs:
       -                args = [self.remote_state.next_per_commitment_point, for_us, we_receive, pending_remote_commitment, htlc]
       +                args = [self.config[REMOTE].next_per_commitment_point, for_us, we_receive, pending_remote_commitment, htlc]
                        htlc_tx = make_htlc_tx_with_open_channel(self, *args)
                        sig = bfh(htlc_tx.sign_txin(0, their_remote_htlc_privkey))
                        htlc_sig = ecc.sig_string_from_der_sig(sig[:-1])
       t@@ -265,13 +256,13 @@ class HTLCStateMachine(PrintError):
                self.print_error("receive_new_commitment")
                for htlc in self.log[REMOTE]['adds'].values():
                    if htlc.locked_in[REMOTE] is None:
       -                htlc.locked_in[REMOTE] = self.remote_state.ctn
       +                htlc.locked_in[REMOTE] = self.config[REMOTE].ctn
                assert len(htlc_sigs) == 0 or type(htlc_sigs[0]) is bytes
        
                pending_local_commitment = self.pending_local_commitment
                preimage_hex = pending_local_commitment.serialize_preimage(0)
                pre_hash = Hash(bfh(preimage_hex))
       -        if not ecc.verify_signature(self.remote_config.multisig_key.pubkey, sig, pre_hash):
       +        if not ecc.verify_signature(self.config[REMOTE].multisig_key.pubkey, sig, pre_hash):
                    raise Exception('failed verifying signature of our updated commitment transaction: ' + bh2u(sig) + ' preimage is ' + preimage_hex)
        
                _, this_point, _ = self.points
       t@@ -280,7 +271,7 @@ class HTLCStateMachine(PrintError):
                    for htlc in htlcs:
                        htlc_tx = make_htlc_tx_with_open_channel(self, this_point, True, we_receive, pending_local_commitment, htlc)
                        pre_hash = Hash(bfh(htlc_tx.serialize_preimage(0)))
       -                remote_htlc_pubkey = derive_pubkey(self.remote_config.htlc_basepoint.pubkey, this_point)
       +                remote_htlc_pubkey = derive_pubkey(self.config[REMOTE].htlc_basepoint.pubkey, this_point)
                        for idx, sig in enumerate(htlc_sigs):
                            if ecc.verify_signature(remote_htlc_pubkey, sig, pre_hash):
                                del htlc_sigs[idx]
       t@@ -314,8 +305,8 @@ class HTLCStateMachine(PrintError):
        
                last_secret, this_point, next_point = self.points
        
       -        new_local_feerate = self.local_state.feerate
       -        new_remote_feerate = self.remote_state.feerate
       +        new_local_feerate = self.config[LOCAL].feerate
       +        new_remote_feerate = self.config[REMOTE].feerate
        
                for pending_fee in self.fee_mgr[:]:
                    if not self.constraints.is_initiator and pending_fee.had(FUNDEE_SIGNED):
       t@@ -327,11 +318,11 @@ class HTLCStateMachine(PrintError):
                        self.fee_mgr.remove(pending_fee)
                        print("FEERATE CHANGE COMPLETE (initiator)")
        
       -        self.local_state=self.local_state._replace(
       -            ctn=self.local_state.ctn + 1,
       +        self.config[LOCAL]=self.config[LOCAL]._replace(
       +            ctn=self.config[LOCAL].ctn + 1,
                    feerate=new_local_feerate
                )
       -        self.remote_state=self.remote_state._replace(
       +        self.config[REMOTE]=self.config[REMOTE]._replace(
                    feerate=new_remote_feerate
                )
        
       t@@ -341,13 +332,13 @@ class HTLCStateMachine(PrintError):
        
            @property
            def points(self):
       -        last_small_num = self.local_state.ctn
       +        last_small_num = self.config[LOCAL].ctn
                this_small_num = last_small_num + 1
                next_small_num = last_small_num + 2
       -        last_secret = get_per_commitment_secret_from_seed(self.local_state.per_commitment_secret_seed, RevocationStore.START_INDEX - last_small_num)
       -        this_secret = get_per_commitment_secret_from_seed(self.local_state.per_commitment_secret_seed, RevocationStore.START_INDEX - this_small_num)
       +        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)
                this_point = secret_to_pubkey(int.from_bytes(this_secret, 'big'))
       -        next_secret = get_per_commitment_secret_from_seed(self.local_state.per_commitment_secret_seed, RevocationStore.START_INDEX - next_small_num)
       +        next_secret = get_per_commitment_secret_from_seed(self.config[LOCAL].per_commitment_secret_seed, RevocationStore.START_INDEX - next_small_num)
                next_point = secret_to_pubkey(int.from_bytes(next_secret, 'big'))
                return last_secret, this_point, next_point
        
       t@@ -358,13 +349,13 @@ class HTLCStateMachine(PrintError):
                    return
                outpoint = self.funding_outpoint.to_str()
                if ours:
       -            ctn = self.local_state.ctn + 1
       +            ctn = self.config[LOCAL].ctn + 1
                    our_per_commitment_secret = get_per_commitment_secret_from_seed(
       -                self.local_state.per_commitment_secret_seed, RevocationStore.START_INDEX - ctn)
       +                self.config[LOCAL].per_commitment_secret_seed, RevocationStore.START_INDEX - ctn)
                    our_cur_pcp = ecc.ECPrivkey(our_per_commitment_secret).get_public_key_bytes(compressed=True)
                    encumbered_sweeptx = maybe_create_sweeptx_for_our_ctx_to_local(self, ctx, our_cur_pcp, self.sweep_address)
                else:
       -            their_cur_pcp = self.remote_state.next_per_commitment_point
       +            their_cur_pcp = self.config[REMOTE].next_per_commitment_point
                    encumbered_sweeptx = maybe_create_sweeptx_for_their_ctx_to_remote(self, ctx, their_cur_pcp, self.sweep_address)
                self.lnwatcher.add_sweep_tx(outpoint, ctx.txid(), encumbered_sweeptx)
        
       t@@ -390,7 +381,7 @@ class HTLCStateMachine(PrintError):
                """
                self.print_error("receive_revocation")
        
       -        cur_point = self.remote_state.current_per_commitment_point
       +        cur_point = self.config[REMOTE].current_per_commitment_point
                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')
       t@@ -400,14 +391,14 @@ class HTLCStateMachine(PrintError):
                # this might break
                prev_remote_commitment = self.pending_remote_commitment
        
       -        self.remote_state.revocation_store.add_next_entry(revocation.per_commitment_secret)
       +        self.config[REMOTE].revocation_store.add_next_entry(revocation.per_commitment_secret)
                self.process_new_revocation_secret(revocation.per_commitment_secret)
        
                def mark_settled(subject):
                    """
                    find pending settlements for subject (LOCAL or REMOTE) and mark them settled, return value of settled htlcs
                    """
       -            old_amount = self.htlcsum(self.gen_htlc_indices(subject, False))
       +            old_amount = htlcsum(self.htlcs(subject, False))
        
                    for htlc_id in self.log[-subject]['settles']:
                        adds = self.log[subject]['adds']
       t@@ -415,23 +406,23 @@ class HTLCStateMachine(PrintError):
                        self.settled[subject].append(htlc.amount_msat)
                    self.log[-subject]['settles'].clear()
        
       -            return old_amount - self.htlcsum(self.gen_htlc_indices(subject, False))
       +            return old_amount - htlcsum(self.htlcs(subject, False))
        
                sent_this_batch = mark_settled(LOCAL)
                received_this_batch = mark_settled(REMOTE)
        
       -        next_point = self.remote_state.next_per_commitment_point
       +        next_point = self.config[REMOTE].next_per_commitment_point
        
                print("RECEIVED", received_this_batch)
                print("SENT", sent_this_batch)
       -        self.remote_state=self.remote_state._replace(
       -            ctn=self.remote_state.ctn + 1,
       +        self.config[REMOTE]=self.config[REMOTE]._replace(
       +            ctn=self.config[REMOTE].ctn + 1,
                    current_per_commitment_point=next_point,
                    next_per_commitment_point=revocation.next_per_commitment_point,
       -            amount_msat=self.remote_state.amount_msat + (sent_this_batch - received_this_batch)
       +            amount_msat=self.config[REMOTE].amount_msat + (sent_this_batch - received_this_batch)
                )
       -        self.local_state=self.local_state._replace(
       -            amount_msat = self.local_state.amount_msat + (received_this_batch - sent_this_batch)
       +        self.config[LOCAL]=self.config[LOCAL]._replace(
       +            amount_msat = self.config[LOCAL].amount_msat + (received_this_batch - sent_this_batch)
                )
        
                for pending_fee in self.fee_mgr:
       t@@ -444,43 +435,39 @@ class HTLCStateMachine(PrintError):
                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
       +        initial = self.config[subject].initial_msat
        
                initial -= sum(self.settled[subject])
                initial += sum(self.settled[-subject])
        
       -        assert initial == (self.local_state.amount_msat if subject == LOCAL else self.remote_state.amount_msat)
       +        assert initial == self.config[subject].amount_msat
                return initial
        
       -    @staticmethod
       -    def htlcsum(htlcs):
       -        amount_unsettled = 0
       -        for x in htlcs:
       -            amount_unsettled += x.amount_msat
       -        return amount_unsettled
       -
            def amounts(self):
       -        remote_settled= self.htlcsum(self.gen_htlc_indices(REMOTE, False))
       -        local_settled= self.htlcsum(self.gen_htlc_indices(LOCAL, False))
       -        unsettled_local = self.htlcsum(self.gen_htlc_indices(LOCAL, True))
       -        unsettled_remote = self.htlcsum(self.gen_htlc_indices(REMOTE, True))
       -        remote_msat = self.remote_state.amount_msat -\
       +        remote_settled= htlcsum(self.htlcs(REMOTE, False))
       +        local_settled= htlcsum(self.htlcs(LOCAL, False))
       +        unsettled_local = htlcsum(self.htlcs(LOCAL, True))
       +        unsettled_remote = htlcsum(self.htlcs(REMOTE, True))
       +        remote_msat = self.config[REMOTE].amount_msat -\
                  unsettled_remote + local_settled - remote_settled
       -        local_msat = self.local_state.amount_msat -\
       +        local_msat = self.config[LOCAL].amount_msat -\
                  unsettled_local + remote_settled - local_settled
                return remote_msat, local_msat
        
            def included_htlcs(self, subject, htlc_initiator):
       +        """
       +        return filter of non-dust htlcs for subjects commitment transaction, initiated by given party
       +        """
                feerate = self.pending_feerate(subject)
       -        conf = self.remote_config if subject == REMOTE else self.local_config
       +        conf = self.config[subject]
                weight = HTLC_SUCCESS_WEIGHT if subject != htlc_initiator else HTLC_TIMEOUT_WEIGHT
       -        htlcs = self.htlcs_in_local if htlc_initiator == LOCAL else self.htlcs_in_remote
       +        htlcs = self.htlcs(htlc_initiator, only_pending=True)
                fee_for_htlc = lambda htlc: htlc.amount_msat // 1000 - (weight * feerate // 1000)
                return filter(lambda htlc: fee_for_htlc(htlc) >= conf.dust_limit_sat, htlcs)
        
            @property
            def pending_remote_commitment(self):
       -        this_point = self.remote_state.next_per_commitment_point
       +        this_point = self.config[REMOTE].next_per_commitment_point
                return self.make_commitment(REMOTE, this_point)
        
            def pending_feerate(self, subject):
       t@@ -495,7 +482,7 @@ class HTLCStateMachine(PrintError):
        
            @property
            def _committed_feerate(self):
       -        return {LOCAL: self.local_state.feerate, REMOTE: self.remote_state.feerate}
       +        return {LOCAL: self.config[LOCAL].feerate, REMOTE: self.config[REMOTE].feerate}
        
            @property
            def pending_local_commitment(self):
       t@@ -505,10 +492,9 @@ class HTLCStateMachine(PrintError):
            def total_msat(self, sub):
                return sum(self.settled[sub])
        
       -    def gen_htlc_indices(self, subject, only_pending):
       +    def htlcs(self, subject, only_pending):
                """
                only_pending: require the htlc's settlement to be pending (needs additional signatures/acks)
       -        include_settled: include settled (totally done with) htlcs
                """
                update_log = self.log[subject]
                other_log = self.log[-subject]
       t@@ -521,16 +507,6 @@ class HTLCStateMachine(PrintError):
                    res.append(htlc)
                return res
        
       -    @property
       -    def htlcs_in_local(self):
       -        """in the local log. 'offered by us'"""
       -        return self.gen_htlc_indices(LOCAL, True)
       -
       -    @property
       -    def htlcs_in_remote(self):
       -        """in the remote log. 'offered by them'"""
       -        return self.gen_htlc_indices(REMOTE, True)
       -
            def settle_htlc(self, preimage, htlc_id):
                """
                SettleHTLC attempts to settle an existing outstanding received HTLC.
       t@@ -552,7 +528,7 @@ class HTLCStateMachine(PrintError):
        
            @property
            def current_height(self):
       -        return {LOCAL: self.local_state.ctn, REMOTE: self.remote_state.ctn}
       +        return {LOCAL: self.config[LOCAL].ctn, REMOTE: self.config[REMOTE].ctn}
        
            @property
            def pending_local_fee(self):
       t@@ -581,7 +557,7 @@ class HTLCStateMachine(PrintError):
                for i in self.log[subject]['adds'].values():
                    locked_in = i.locked_in[LOCAL] is not None or i.locked_in[REMOTE] is not None
                    if locked_in:
       -                htlcs.append(i)
       +                htlcs.append(i._asdict())
                    else:
                        removed.append(i.htlc_id)
                return htlcs, removed
       t@@ -593,10 +569,8 @@ class HTLCStateMachine(PrintError):
                remote_filtered, remote_removed = self.remove_uncommitted_htlcs_from_log(REMOTE)
                local_filtered, local_removed = self.remove_uncommitted_htlcs_from_log(LOCAL)
                to_save = {
       -                "local_config": self.local_config,
       -                "remote_config": self.remote_config,
       -                "local_state": self.local_state,
       -                "remote_state": self.remote_state,
       +                "local_config": self.config[LOCAL],
       +                "remote_config": self.config[REMOTE],
                        "channel_id": self.channel_id,
                        "short_channel_id": self.short_channel_id,
                        "constraints": self.constraints,
       t@@ -613,12 +587,12 @@ class HTLCStateMachine(PrintError):
                # htlcs number must be monotonically increasing,
                # so we have to decrease the counter
                if len(remote_removed) != 0:
       -            assert min(remote_removed) < to_save['remote_state'].next_htlc_id
       -            to_save['remote_state'] = to_save['remote_state']._replace(next_htlc_id = min(remote_removed))
       +            assert min(remote_removed) < to_save['remote_config'].next_htlc_id
       +            to_save['remote_config'] = to_save['remote_config']._replace(next_htlc_id = min(remote_removed))
        
                if len(local_removed) != 0:
       -            assert min(local_removed) < to_save['local_state'].next_htlc_id
       -            to_save['local_state'] = to_save['local_state']._replace(next_htlc_id = min(local_removed))
       +            assert min(local_removed) < to_save['local_config'].next_htlc_id
       +            to_save['local_config'] = to_save['local_config']._replace(next_htlc_id = min(local_removed))
        
                return to_save
        
       t@@ -652,8 +626,8 @@ class HTLCStateMachine(PrintError):
                remote_msat, local_msat = self.amounts()
                assert local_msat >= 0
                assert remote_msat >= 0
       -        this_config = self.remote_config if subject != LOCAL else self.local_config
       -        other_config = self.remote_config if subject == LOCAL else self.local_config
       +        this_config = self.config[subject]
       +        other_config = self.config[-subject]
                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)
       t@@ -676,12 +650,12 @@ class HTLCStateMachine(PrintError):
                    remote_msat, local_msat = local_msat, remote_msat
                payment_pubkey = derive_pubkey(other_config.payment_basepoint.pubkey, this_point)
                return make_commitment(
       -            (self.local_state.ctn if subject == LOCAL else self.remote_state.ctn) + 1,
       +            self.config[subject].ctn + 1,
                    this_config.multisig_key.pubkey,
                    other_config.multisig_key.pubkey,
                    payment_pubkey,
       -            self.local_config.payment_basepoint.pubkey,
       -            self.remote_config.payment_basepoint.pubkey,
       +            self.config[LOCAL].payment_basepoint.pubkey,
       +            self.config[REMOTE].payment_basepoint.pubkey,
                    other_revocation_pubkey,
                    derive_pubkey(this_config.delayed_basepoint.pubkey, this_point),
                    other_config.to_self_delay,
       t@@ -700,28 +674,28 @@ class HTLCStateMachine(PrintError):
                    fee_sat = self.pending_local_fee
        
                _, outputs = make_outputs(fee_sat * 1000, True,
       -                self.local_state.amount_msat,
       -                self.remote_state.amount_msat,
       +                self.config[LOCAL].amount_msat,
       +                self.config[REMOTE].amount_msat,
                        (TYPE_SCRIPT, bh2u(local_script)),
                        (TYPE_SCRIPT, bh2u(remote_script)),
       -                [], self.local_config.dust_limit_sat)
       +                [], self.config[LOCAL].dust_limit_sat)
        
       -        closing_tx = make_closing_tx(self.local_config.multisig_key.pubkey,
       -                self.remote_config.multisig_key.pubkey,
       -                self.local_config.payment_basepoint.pubkey,
       -                self.remote_config.payment_basepoint.pubkey,
       +        closing_tx = make_closing_tx(self.config[LOCAL].multisig_key.pubkey,
       +                self.config[REMOTE].multisig_key.pubkey,
       +                self.config[LOCAL].payment_basepoint.pubkey,
       +                self.config[REMOTE].payment_basepoint.pubkey,
                        # TODO hardcoded we_are_initiator:
                        True, *self.funding_outpoint, self.constraints.capacity,
                        outputs)
        
       -        der_sig = bfh(closing_tx.sign_txin(0, self.local_config.multisig_key.privkey))
       +        der_sig = bfh(closing_tx.sign_txin(0, self.config[LOCAL].multisig_key.privkey))
                sig = ecc.sig_string_from_der_sig(der_sig[:-1])
                return sig, fee_sat
        
        def maybe_create_sweeptx_for_their_ctx_to_remote(chan, ctx, their_pcp: bytes,
                                                         sweep_address) -> Optional[EncumberedTransaction]:
            assert isinstance(their_pcp, bytes)
       -    payment_bp_privkey = ecc.ECPrivkey(chan.local_config.payment_basepoint.privkey)
       +    payment_bp_privkey = ecc.ECPrivkey(chan.config[LOCAL].payment_basepoint.privkey)
            our_payment_privkey = derive_privkey(payment_bp_privkey.secret_scalar, their_pcp)
            our_payment_privkey = ecc.ECPrivkey.from_secret_scalar(our_payment_privkey)
            our_payment_pubkey = our_payment_privkey.get_public_key_bytes(compressed=True)
       t@@ -742,11 +716,11 @@ def maybe_create_sweeptx_for_their_ctx_to_local(chan, ctx, per_commitment_secret
                                                        sweep_address) -> Optional[EncumberedTransaction]:
            assert isinstance(per_commitment_secret, bytes)
            per_commitment_point = ecc.ECPrivkey(per_commitment_secret).get_public_key_bytes(compressed=True)
       -    revocation_privkey = derive_blinded_privkey(chan.local_config.revocation_basepoint.privkey,
       +    revocation_privkey = derive_blinded_privkey(chan.config[LOCAL].revocation_basepoint.privkey,
                                                        per_commitment_secret)
            revocation_pubkey = ecc.ECPrivkey(revocation_privkey).get_public_key_bytes(compressed=True)
       -    to_self_delay = chan.local_config.to_self_delay
       -    delayed_pubkey = derive_pubkey(chan.remote_config.delayed_basepoint.pubkey,
       +    to_self_delay = chan.config[LOCAL].to_self_delay
       +    delayed_pubkey = derive_pubkey(chan.config[REMOTE].delayed_basepoint.pubkey,
                                           per_commitment_point)
            witness_script = bh2u(make_commitment_output_to_local_witness_script(
                revocation_pubkey, to_self_delay, delayed_pubkey))
       t@@ -768,13 +742,13 @@ def maybe_create_sweeptx_for_their_ctx_to_local(chan, ctx, per_commitment_secret
        def maybe_create_sweeptx_for_our_ctx_to_local(chan, ctx, our_pcp: bytes,
                                                      sweep_address) -> Optional[EncumberedTransaction]:
            assert isinstance(our_pcp, bytes)
       -    delayed_bp_privkey = ecc.ECPrivkey(chan.local_config.delayed_basepoint.privkey)
       +    delayed_bp_privkey = ecc.ECPrivkey(chan.config[LOCAL].delayed_basepoint.privkey)
            our_localdelayed_privkey = derive_privkey(delayed_bp_privkey.secret_scalar, our_pcp)
            our_localdelayed_privkey = ecc.ECPrivkey.from_secret_scalar(our_localdelayed_privkey)
            our_localdelayed_pubkey = our_localdelayed_privkey.get_public_key_bytes(compressed=True)
       -    revocation_pubkey = derive_blinded_pubkey(chan.remote_config.revocation_basepoint.pubkey,
       +    revocation_pubkey = derive_blinded_pubkey(chan.config[REMOTE].revocation_basepoint.pubkey,
                                                      our_pcp)
       -    to_self_delay = chan.remote_config.to_self_delay
       +    to_self_delay = chan.config[REMOTE].to_self_delay
            witness_script = bh2u(make_commitment_output_to_local_witness_script(
                revocation_pubkey, to_self_delay, our_localdelayed_pubkey))
            to_local_address = redeem_script_to_address('p2wsh', witness_script)
   DIR diff --git a/electrum/lnutil.py b/electrum/lnutil.py
       t@@ -20,14 +20,36 @@ HTLC_TIMEOUT_WEIGHT = 663
        HTLC_SUCCESS_WEIGHT = 703
        
        Keypair = namedtuple("Keypair", ["pubkey", "privkey"])
       -ChannelConfig = namedtuple("ChannelConfig", [
       -    "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", "initial_msat"])
        OnlyPubkeyKeypair = namedtuple("OnlyPubkeyKeypair", ["pubkey"])
       -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"])
       +
       +common = [
       +    ('ctn' , int),
       +    ('amount_msat' , int),
       +    ('next_htlc_id' , int),
       +    ('feerate' , int),
       +    ('payment_basepoint' , Keypair),
       +    ('multisig_key' , Keypair),
       +    ('htlc_basepoint' , Keypair),
       +    ('delayed_basepoint' , Keypair),
       +    ('revocation_basepoint' , Keypair),
       +    ('to_self_delay' , int),
       +    ('dust_limit_sat' , int),
       +    ('max_htlc_value_in_flight_msat' , int),
       +    ('max_accepted_htlcs' , int),
       +    ('initial_msat' , int),
       +]
       +
       +ChannelConfig = NamedTuple('ChannelConfig', common)
       +
       +LocalConfig = NamedTuple('LocalConfig', common + [
       +    ('per_commitment_secret_seed', bytes),
       +    ('funding_locked_received', bool),
       +    ('was_announced', bool),
       +    ('current_commitment_signature', bytes),
       +    ('current_htlc_signatures', List[bytes]),
       +])
       +
        ChannelConstraints = namedtuple("ChannelConstraints", ["capacity", "is_initiator", "funding_txn_minimum_depth"])
       -#OpenChannel = namedtuple("OpenChannel", ["channel_id", "short_channel_id", "funding_outpoint", "local_config", "remote_config", "remote_state", "local_state", "constraints", "node_id"])
        
        ScriptHtlc = namedtuple('ScriptHtlc', ['redeem_script', 'htlc'])
        
       t@@ -88,6 +110,12 @@ class RevocationStore:
            def __hash__(self):
                return hash(json.dumps(self.serialize(), sort_keys=True))
        
       +RemoteConfig = NamedTuple('RemoteConfig', common + [
       +    ('next_per_commitment_point' , bytes),
       +    ('revocation_store' , RevocationStore),
       +    ('current_per_commitment_point' , bytes),
       +])
       +
        def count_trailing_zeros(index):
            """ BOLT-03 (where_to_put_secret) """
            try:
       t@@ -243,8 +271,8 @@ def make_received_htlc(revocation_pubkey, remote_htlcpubkey, local_htlcpubkey, p
        
        def make_htlc_tx_with_open_channel(chan, pcp, for_us, we_receive, commit, htlc):
            amount_msat, cltv_expiry, payment_hash = htlc.amount_msat, htlc.cltv_expiry, htlc.payment_hash
       -    conf = chan.local_config if for_us else chan.remote_config
       -    other_conf = chan.local_config if not for_us else chan.remote_config
       +    conf =       chan.config[LOCAL] if     for_us else chan.config[REMOTE]
       +    other_conf = chan.config[LOCAL] if not for_us else chan.config[REMOTE]
        
            revocation_pubkey = derive_blinded_pubkey(other_conf.revocation_basepoint.pubkey, pcp)
            delayedpubkey = derive_pubkey(conf.delayed_basepoint.pubkey, pcp)
       t@@ -412,8 +440,8 @@ def extract_ctn_from_tx(tx, txin_index: int, funder_payment_basepoint: bytes,
            return get_obscured_ctn(obs, funder_payment_basepoint, fundee_payment_basepoint)
        
        def extract_ctn_from_tx_and_chan(tx, chan) -> int:
       -    funder_conf = chan.local_config if     chan.constraints.is_initiator else chan.remote_config
       -    fundee_conf = chan.local_config if not chan.constraints.is_initiator else chan.remote_config
       +    funder_conf = chan.config[LOCAL] if     chan.constraints.is_initiator else chan.config[REMOTE]
       +    fundee_conf = chan.config[LOCAL] if not chan.constraints.is_initiator else chan.config[REMOTE]
            return extract_ctn_from_tx(tx, txin_index=0,
                                       funder_payment_basepoint=funder_conf.payment_basepoint.pubkey,
                                       fundee_payment_basepoint=fundee_conf.payment_basepoint.pubkey)
   DIR diff --git a/electrum/lnworker.py b/electrum/lnworker.py
       t@@ -116,7 +116,7 @@ class LNWorker(PrintError):
        
            def save_channel(self, openchannel):
                assert type(openchannel) is HTLCStateMachine
       -        if openchannel.remote_state.next_per_commitment_point == openchannel.remote_state.current_per_commitment_point:
       +        if openchannel.config[REMOTE].next_per_commitment_point == openchannel.config[REMOTE].current_per_commitment_point:
                    raise Exception("Tried to save channel with next_point == current_point, this should not happen")
                with self.lock:
                    self.channels[openchannel.channel_id] = openchannel
       t@@ -350,12 +350,12 @@ class LNWorker(PrintError):
                chan = self.channels[chan_id]
                # local_commitment always gives back the next expected local_commitment,
                # but in this case, we want the current one. So substract one ctn number
       -        old_local_state = chan.local_state
       -        chan.local_state=chan.local_state._replace(ctn=chan.local_state.ctn - 1)
       +        old_local_state = chan.config[LOCAL]
       +        chan.config[LOCAL]=chan.config[LOCAL]._replace(ctn=chan.config[LOCAL].ctn - 1)
                tx = chan.pending_local_commitment
       -        chan.local_state = old_local_state
       -        tx.sign({bh2u(chan.local_config.multisig_key.pubkey): (chan.local_config.multisig_key.privkey, True)})
       -        remote_sig = chan.local_state.current_commitment_signature
       +        chan.config[LOCAL] = old_local_state
       +        tx.sign({bh2u(chan.config[LOCAL].multisig_key.pubkey): (chan.config[LOCAL].multisig_key.privkey, True)})
       +        remote_sig = chan.config[LOCAL].current_commitment_signature
                remote_sig = der_sig_from_sig_string(remote_sig) + b"\x01"
                none_idx = tx._inputs[0]["signatures"].index(None)
                tx.add_signature_to_txin(0, none_idx, bh2u(remote_sig))
   DIR diff --git a/electrum/tests/test_lnhtlc.py b/electrum/tests/test_lnhtlc.py
       t@@ -16,56 +16,52 @@ def create_channel_state(funding_txid, funding_index, funding_sat, local_feerate
            assert remote_amount > 0
            channel_id, _ = lnbase.channel_id_from_funding_tx(funding_txid, funding_index)
            their_revocation_store = lnbase.RevocationStore()
       -    local_config=lnbase.ChannelConfig(
       -        payment_basepoint=privkeys[0],
       -        multisig_key=privkeys[1],
       -        htlc_basepoint=privkeys[2],
       -        delayed_basepoint=privkeys[3],
       -        revocation_basepoint=privkeys[4],
       -        to_self_delay=l_csv,
       -        dust_limit_sat=l_dust,
       -        max_htlc_value_in_flight_msat=500000 * 1000,
       -        max_accepted_htlcs=5,
       -        initial_msat=local_amount,
       -    )
       -    remote_config=lnbase.ChannelConfig(
       -        payment_basepoint=other_pubkeys[0],
       -        multisig_key=other_pubkeys[1],
       -        htlc_basepoint=other_pubkeys[2],
       -        delayed_basepoint=other_pubkeys[3],
       -        revocation_basepoint=other_pubkeys[4],
       -        to_self_delay=r_csv,
       -        dust_limit_sat=r_dust,
       -        max_htlc_value_in_flight_msat=500000 * 1000,
       -        max_accepted_htlcs=5,
       -        initial_msat=remote_amount,
       -    )
        
            return {
                    "channel_id":channel_id,
                    "short_channel_id":channel_id[:8],
                    "funding_outpoint":lnbase.Outpoint(funding_txid, funding_index),
       -            "local_config":local_config,
       -            "remote_config":remote_config,
       -            "remote_state":lnbase.RemoteState(
       +            "remote_config":lnbase.RemoteConfig(
       +                payment_basepoint=other_pubkeys[0],
       +                multisig_key=other_pubkeys[1],
       +                htlc_basepoint=other_pubkeys[2],
       +                delayed_basepoint=other_pubkeys[3],
       +                revocation_basepoint=other_pubkeys[4],
       +                to_self_delay=r_csv,
       +                dust_limit_sat=r_dust,
       +                max_htlc_value_in_flight_msat=500000 * 1000,
       +                max_accepted_htlcs=5,
       +                initial_msat=remote_amount,
                        ctn = 0,
       +                next_htlc_id = 0,
       +                feerate=local_feerate,
       +                amount_msat=remote_amount,
       +
                        next_per_commitment_point=nex,
                        current_per_commitment_point=cur,
       -                amount_msat=remote_amount,
                        revocation_store=their_revocation_store,
       -                next_htlc_id = 0,
       -                feerate=local_feerate
                    ),
       -            "local_state":lnbase.LocalState(
       +            "local_config":lnbase.LocalConfig(
       +                payment_basepoint=privkeys[0],
       +                multisig_key=privkeys[1],
       +                htlc_basepoint=privkeys[2],
       +                delayed_basepoint=privkeys[3],
       +                revocation_basepoint=privkeys[4],
       +                to_self_delay=l_csv,
       +                dust_limit_sat=l_dust,
       +                max_htlc_value_in_flight_msat=500000 * 1000,
       +                max_accepted_htlcs=5,
       +                initial_msat=local_amount,
                        ctn = 0,
       -                per_commitment_secret_seed=seed,
       -                amount_msat=local_amount,
                        next_htlc_id = 0,
       +                feerate=local_feerate,
       +                amount_msat=local_amount,
       +
       +                per_commitment_secret_seed=seed,
                        funding_locked_received=True,
                        was_announced=False,
                        current_commitment_signature=None,
                        current_htlc_signatures=None,
       -                feerate=local_feerate
                    ),
                    "constraints":lnbase.ChannelConstraints(capacity=funding_sat, is_initiator=is_initiator, funding_txn_minimum_depth=3),
                    "node_id":other_node_id,
       t@@ -205,8 +201,8 @@ class TestLNBaseHTLCStateMachine(unittest.TestCase):
                self.assertEqual(alice_channel.total_msat(RECEIVED), bobSent, "alice has incorrect milli-satoshis received")
                self.assertEqual(bob_channel.total_msat(SENT), bobSent, "bob has incorrect milli-satoshis sent")
                self.assertEqual(bob_channel.total_msat(RECEIVED), aliceSent, "bob has incorrect milli-satoshis received")
       -        self.assertEqual(bob_channel.local_state.ctn, 1, "bob has incorrect commitment height")
       -        self.assertEqual(alice_channel.local_state.ctn, 1, "alice has incorrect commitment height")
       +        self.assertEqual(bob_channel.config[LOCAL].ctn, 1, "bob has incorrect commitment height")
       +        self.assertEqual(alice_channel.config[LOCAL].ctn, 1, "alice has incorrect commitment height")
        
                # Both commitment transactions should have three outputs, and one of
                # them should be exactly the amount of the HTLC.
       t@@ -275,20 +271,20 @@ class TestLNBaseHTLCStateMachine(unittest.TestCase):
        
                bob_channel.receive_new_commitment(alice_sig, alice_htlc_sigs)
        
       -        self.assertNotEqual(fee, bob_channel.local_state.feerate)
       +        self.assertNotEqual(fee, bob_channel.config[LOCAL].feerate)
                rev, _ = bob_channel.revoke_current_commitment()
       -        self.assertEqual(fee, bob_channel.local_state.feerate)
       +        self.assertEqual(fee, bob_channel.config[LOCAL].feerate)
        
                bob_sig, bob_htlc_sigs = bob_channel.sign_next_commitment()
                alice_channel.receive_revocation(rev)
                alice_channel.receive_new_commitment(bob_sig, bob_htlc_sigs)
        
       -        self.assertNotEqual(fee, alice_channel.local_state.feerate)
       +        self.assertNotEqual(fee, alice_channel.config[LOCAL].feerate)
                rev, _ = alice_channel.revoke_current_commitment()
       -        self.assertEqual(fee, alice_channel.local_state.feerate)
       +        self.assertEqual(fee, alice_channel.config[LOCAL].feerate)
        
                bob_channel.receive_revocation(rev)
       -        self.assertEqual(fee, bob_channel.remote_state.feerate)
       +        self.assertEqual(fee, bob_channel.config[REMOTE].feerate)
        
        
            def test_UpdateFeeReceiverCommits(self):
       t@@ -304,20 +300,20 @@ class TestLNBaseHTLCStateMachine(unittest.TestCase):
                alice_sig, alice_htlc_sigs = alice_channel.sign_next_commitment()
                bob_channel.receive_new_commitment(alice_sig, alice_htlc_sigs)
        
       -        self.assertNotEqual(fee, bob_channel.local_state.feerate)
       +        self.assertNotEqual(fee, bob_channel.config[LOCAL].feerate)
                bob_revocation, _ = bob_channel.revoke_current_commitment()
       -        self.assertEqual(fee, bob_channel.local_state.feerate)
       +        self.assertEqual(fee, bob_channel.config[LOCAL].feerate)
        
                bob_sig, bob_htlc_sigs = bob_channel.sign_next_commitment()
                alice_channel.receive_revocation(bob_revocation)
                alice_channel.receive_new_commitment(bob_sig, bob_htlc_sigs)
        
       -        self.assertNotEqual(fee, alice_channel.local_state.feerate)
       +        self.assertNotEqual(fee, alice_channel.config[LOCAL].feerate)
                alice_revocation, _ = alice_channel.revoke_current_commitment()
       -        self.assertEqual(fee, alice_channel.local_state.feerate)
       +        self.assertEqual(fee, alice_channel.config[LOCAL].feerate)
        
                bob_channel.receive_revocation(alice_revocation)
       -        self.assertEqual(fee, bob_channel.remote_state.feerate)
       +        self.assertEqual(fee, bob_channel.config[REMOTE].feerate)
        
        
        
       t@@ -327,7 +323,7 @@ class TestLNHTLCDust(unittest.TestCase):
        
                paymentPreimage = b"\x01" * 32
                paymentHash = bitcoin.sha256(paymentPreimage)
       -        fee_per_kw = alice_channel.local_state.feerate
       +        fee_per_kw = alice_channel.config[LOCAL].feerate
                self.assertEqual(fee_per_kw, 6000)
                htlcAmt = 500 + lnutil.HTLC_TIMEOUT_WEIGHT * (fee_per_kw // 1000)
                self.assertEqual(htlcAmt, 4478)