URI: 
       tlnhtlc: save settled htlc amounts separately - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 699368b0b783dd5ade7740a0bffd11ba0fd056f0
   DIR parent 18d06dd6b4f5bcb21eb78690a51680664ce259d4
  HTML Author: Janus <ysangkok@gmail.com>
       Date:   Wed, 10 Oct 2018 16:58:31 +0200
       
       lnhtlc: save settled htlc amounts separately
       
       Diffstat:
         M electrum/lnhtlc.py                  |      42 +++++++++++--------------------
         M electrum/tests/test_lnhtlc.py       |      18 +++++++++---------
       
       2 files changed, 24 insertions(+), 36 deletions(-)
       ---
   DIR diff --git a/electrum/lnhtlc.py b/electrum/lnhtlc.py
       t@@ -63,7 +63,7 @@ class FeeUpdate:
                    return self.rate
                # implicit return None
        
       -class UpdateAddHtlc(namedtuple('UpdateAddHtlc', ['amount_msat', 'payment_hash', 'cltv_expiry', 'settled', 'locked_in', 'htlc_id'])):
       +class UpdateAddHtlc(namedtuple('UpdateAddHtlc', ['amount_msat', 'payment_hash', 'cltv_expiry', 'locked_in', 'htlc_id'])):
            __slots__ = ()
            def __new__(cls, *args, **kwargs):
                if len(args) > 0:
       t@@ -71,7 +71,6 @@ class UpdateAddHtlc(namedtuple('UpdateAddHtlc', ['amount_msat', 'payment_hash', 
                    if type(args[1]) is str:
                        args[1] = bfh(args[1])
                    args[3] = {HTLCOwner(int(x)): y for x,y in args[3].items()}
       -            args[4] = {HTLCOwner(int(x)): y for x,y in args[4].items()}
                    return super().__new__(cls, *args)
                if type(kwargs['payment_hash']) is str:
                    kwargs['payment_hash'] = bfh(kwargs['payment_hash'])
       t@@ -79,10 +78,6 @@ class UpdateAddHtlc(namedtuple('UpdateAddHtlc', ['amount_msat', 'payment_hash', 
                    kwargs['locked_in'] = {LOCAL: None, REMOTE: None}
                else:
                    kwargs['locked_in'] = {HTLCOwner(int(x)): y for x,y in kwargs['locked_in']}
       -        if 'settled' not in kwargs:
       -            kwargs['settled'] = {LOCAL: None, REMOTE: None}
       -        else:
       -            kwargs['settled'] = {HTLCOwner(int(x)): y for x,y in kwargs['settled']}
                return super().__new__(cls, **kwargs)
        
        is_key = lambda k: k.endswith("_basepoint") or k.endswith("_key")
       t@@ -176,6 +171,8 @@ class HTLCStateMachine(PrintError):
        
                self.lnwatcher = None
        
       +        self.settled = {LOCAL: state.get('settled_local', []), REMOTE: state.get('settled_remote', [])}
       +
            def set_state(self, state: str):
                self._state = state
        
       t@@ -429,10 +426,14 @@ class HTLCStateMachine(PrintError):
                    """
                    old_amount = self.htlcsum(self.gen_htlc_indices(subject, False))
        
       +            removed = []
                    for x in self.log[-subject]:
                        if type(x) is not SettleHtlc: continue
                        htlc = self.lookup_htlc(self.log[subject], x.htlc_id)
       -                htlc.settled[subject] = self.current_height[subject]
       +                self.settled[subject].append(htlc.amount_msat)
       +                self.log[subject].remove(htlc)
       +                removed.append(x)
       +            for x in removed: self.log[-subject].remove(x)
        
                    return old_amount - self.htlcsum(self.gen_htlc_indices(subject, False))
        
       t@@ -465,15 +466,8 @@ class HTLCStateMachine(PrintError):
            def balance(self, subject):
                initial = self.local_config.initial_msat if subject == LOCAL else self.remote_config.initial_msat
        
       -        for direction in (SENT, RECEIVED):
       -           for x in self.log[-direction]:
       -               if type(x) is not SettleHtlc: continue
       -               htlc = self.lookup_htlc(self.log[direction], x.htlc_id)
       -               htlc_height = htlc.settled[direction]
       -               if htlc_height is not None and htlc_height <= self.current_height[direction]:
       -                   # so we will subtract when direction == subject.
       -                   # example subject=LOCAL, direction=SENT: we subtract
       -                   initial -= htlc.amount_msat * subject * direction
       +        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)
                return initial
       t@@ -528,11 +522,10 @@ class HTLCStateMachine(PrintError):
                _, this_point, _ = self.points
                return self.make_commitment(LOCAL, this_point)
        
       -    @property
       -    def total_msat(self):
       -        return {LOCAL: self.htlcsum(self.gen_htlc_indices(LOCAL, False, True)), REMOTE: self.htlcsum(self.gen_htlc_indices(REMOTE, False, True))}
       +    def total_msat(self, sub):
       +        return sum(self.settled[sub])
        
       -    def gen_htlc_indices(self, subject, only_pending, include_settled=False):
       +    def gen_htlc_indices(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
       t@@ -543,17 +536,10 @@ class HTLCStateMachine(PrintError):
                for htlc in update_log:
                    if type(htlc) is not UpdateAddHtlc:
                        continue
       -            height = self.current_height[-subject]
                    locked_in = htlc.locked_in[subject]
        
                    if locked_in is None or only_pending == (SettleHtlc(htlc.htlc_id) in other_log):
                        continue
       -
       -            settled_cutoff = self.local_state.ctn if subject == LOCAL else self.remote_state.ctn
       -
       -            if not include_settled and htlc.settled[subject] is not None and settled_cutoff >= htlc.settled[subject]:
       -                continue
       -
                    res.append(htlc)
                return res
        
       t@@ -651,6 +637,8 @@ class HTLCStateMachine(PrintError):
                        "remote_log": [(type(x).__name__, x) for x in remote_filtered],
                        "local_log": [(type(x).__name__, x) for x in local_filtered],
                        "onion_keys": {str(k): bh2u(v) for k, v in self.onion_keys.items()},
       +                "settled_local": self.settled[LOCAL],
       +                "settled_remote": self.settled[REMOTE],
                }
        
                # htlcs number must be monotonically increasing,
   DIR diff --git a/electrum/tests/test_lnhtlc.py b/electrum/tests/test_lnhtlc.py
       t@@ -201,10 +201,10 @@ class TestLNBaseHTLCStateMachine(unittest.TestCase):
                aliceSent = 0
                bobSent = 0
        
       -        self.assertEqual(alice_channel.total_msat[SENT], aliceSent, "alice has incorrect milli-satoshis sent")
       -        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(alice_channel.total_msat(SENT), aliceSent, "alice has incorrect milli-satoshis sent")
       +        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")
        
       t@@ -242,10 +242,10 @@ class TestLNBaseHTLCStateMachine(unittest.TestCase):
                # should show 1 BTC received. They should also be at commitment height
                # two, with the revocation window extended by 1 (5).
                mSatTransferred = one_bitcoin_in_msat
       -        self.assertEqual(alice_channel.total_msat[SENT], mSatTransferred, "alice satoshis sent incorrect")
       -        self.assertEqual(alice_channel.total_msat[RECEIVED], 0, "alice satoshis received incorrect")
       -        self.assertEqual(bob_channel.total_msat[RECEIVED], mSatTransferred, "bob satoshis received incorrect")
       -        self.assertEqual(bob_channel.total_msat[SENT], 0, "bob satoshis sent incorrect")
       +        self.assertEqual(alice_channel.total_msat(SENT), mSatTransferred, "alice satoshis sent incorrect")
       +        self.assertEqual(alice_channel.total_msat(RECEIVED), 0, "alice satoshis received incorrect")
       +        self.assertEqual(bob_channel.total_msat(RECEIVED), mSatTransferred, "bob satoshis received incorrect")
       +        self.assertEqual(bob_channel.total_msat(SENT), 0, "bob satoshis sent incorrect")
                self.assertEqual(bob_channel.current_height[LOCAL], 2, "bob has incorrect commitment height")
                self.assertEqual(alice_channel.current_height[LOCAL], 2, "alice has incorrect commitment height")
        
       t@@ -348,7 +348,7 @@ class TestLNHTLCDust(unittest.TestCase):
                alice_channel.receive_htlc_settle(paymentPreimage, aliceHtlcIndex)
                force_state_transition(bob_channel, alice_channel)
                self.assertEqual(len(alice_channel.local_commitment.outputs()), 2)
       -        self.assertEqual(alice_channel.total_msat[SENT] // 1000, htlcAmt)
       +        self.assertEqual(alice_channel.total_msat(SENT) // 1000, htlcAmt)
        
        def force_state_transition(chanA, chanB):
            chanB.receive_new_commitment(*chanA.sign_next_commitment())