URI: 
       ttest_lnchannel.py - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
       ttest_lnchannel.py (43778B)
       ---
            1 # Copyright (C) 2018 The Electrum developers
            2 # Copyright (C) 2015-2018 The Lightning Network Developers
            3 #
            4 # Permission is hereby granted, free of charge, to any person obtaining a copy
            5 # of this software and associated documentation files (the "Software"), to deal
            6 # in the Software without restriction, including without limitation the rights
            7 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
            8 # copies of the Software, and to permit persons to whom the Software is
            9 # furnished to do so, subject to the following conditions:
           10 #
           11 # The above copyright notice and this permission notice shall be included in
           12 # all copies or substantial portions of the Software.
           13 #
           14 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
           15 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
           16 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
           17 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
           18 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
           19 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
           20 # THE SOFTWARE.
           21 #
           22 # Many of these unit tests are heavily based on unit tests in lnd
           23 # (around commit 42de4400bff5105352d0552155f73589166d162b).
           24 
           25 import unittest
           26 import os
           27 import binascii
           28 from pprint import pformat
           29 import logging
           30 
           31 from electrum import bitcoin
           32 from electrum import lnpeer
           33 from electrum import lnchannel
           34 from electrum import lnutil
           35 from electrum import bip32 as bip32_utils
           36 from electrum.lnutil import SENT, LOCAL, REMOTE, RECEIVED
           37 from electrum.logging import console_stderr_handler
           38 from electrum.lnchannel import ChannelState
           39 from electrum.json_db import StoredDict
           40 from electrum.coinchooser import PRNG
           41 
           42 from . import ElectrumTestCase
           43 
           44 
           45 one_bitcoin_in_msat = bitcoin.COIN * 1000
           46 
           47 
           48 def create_channel_state(funding_txid, funding_index, funding_sat, is_initiator,
           49                          local_amount, remote_amount, privkeys, other_pubkeys,
           50                          seed, cur, nex, other_node_id, l_dust, r_dust, l_csv,
           51                          r_csv):
           52     assert local_amount > 0
           53     assert remote_amount > 0
           54     channel_id, _ = lnpeer.channel_id_from_funding_tx(funding_txid, funding_index)
           55     state = {
           56             "channel_id":channel_id.hex(),
           57             "short_channel_id":channel_id[:8],
           58             "funding_outpoint":lnpeer.Outpoint(funding_txid, funding_index),
           59             "remote_config":lnpeer.RemoteConfig(
           60                 payment_basepoint=other_pubkeys[0],
           61                 multisig_key=other_pubkeys[1],
           62                 htlc_basepoint=other_pubkeys[2],
           63                 delayed_basepoint=other_pubkeys[3],
           64                 revocation_basepoint=other_pubkeys[4],
           65                 to_self_delay=r_csv,
           66                 dust_limit_sat=r_dust,
           67                 max_htlc_value_in_flight_msat=one_bitcoin_in_msat * 5,
           68                 max_accepted_htlcs=5,
           69                 initial_msat=remote_amount,
           70                 reserve_sat=0,
           71                 htlc_minimum_msat=1,
           72                 next_per_commitment_point=nex,
           73                 current_per_commitment_point=cur,
           74                 upfront_shutdown_script=b'',
           75             ),
           76             "local_config":lnpeer.LocalConfig(
           77                 channel_seed = None,
           78                 payment_basepoint=privkeys[0],
           79                 multisig_key=privkeys[1],
           80                 htlc_basepoint=privkeys[2],
           81                 delayed_basepoint=privkeys[3],
           82                 revocation_basepoint=privkeys[4],
           83                 to_self_delay=l_csv,
           84                 dust_limit_sat=l_dust,
           85                 max_htlc_value_in_flight_msat=one_bitcoin_in_msat * 5,
           86                 max_accepted_htlcs=5,
           87                 initial_msat=local_amount,
           88                 reserve_sat=0,
           89                 per_commitment_secret_seed=seed,
           90                 funding_locked_received=True,
           91                 was_announced=False,
           92                 current_commitment_signature=None,
           93                 current_htlc_signatures=None,
           94                 htlc_minimum_msat=1,
           95                 upfront_shutdown_script=b'',
           96             ),
           97             "constraints":lnpeer.ChannelConstraints(
           98                 capacity=funding_sat,
           99                 is_initiator=is_initiator,
          100                 funding_txn_minimum_depth=3,
          101             ),
          102             "node_id":other_node_id.hex(),
          103             'onion_keys': {},
          104             'data_loss_protect_remote_pcp': {},
          105             'state': 'PREOPENING',
          106             'log': {},
          107             'revocation_store': {},
          108     }
          109     return StoredDict(state, None, [])
          110 
          111 def bip32(sequence):
          112     node = bip32_utils.BIP32Node.from_rootseed(b"9dk", xtype='standard').subkey_at_private_derivation(sequence)
          113     k = node.eckey.get_secret_bytes()
          114     assert len(k) == 32
          115     assert type(k) is bytes
          116     return k
          117 
          118 def create_test_channels(*, feerate=6000, local_msat=None, remote_msat=None,
          119                          alice_name="alice", bob_name="bob",
          120                          alice_pubkey=b"\x01"*33, bob_pubkey=b"\x02"*33, random_seed=None):
          121     if random_seed is None:  # needed for deterministic randomness
          122         random_seed = os.urandom(32)
          123     random_gen = PRNG(random_seed)
          124     funding_txid = binascii.hexlify(random_gen.get_bytes(32)).decode("ascii")
          125     funding_index = 0
          126     funding_sat = ((local_msat + remote_msat) // 1000) if local_msat is not None and remote_msat is not None else (bitcoin.COIN * 10)
          127     local_amount = local_msat if local_msat is not None else (funding_sat * 1000 // 2)
          128     remote_amount = remote_msat if remote_msat is not None else (funding_sat * 1000 // 2)
          129     alice_raw = [ bip32("m/" + str(i)) for i in range(5) ]
          130     bob_raw = [ bip32("m/" + str(i)) for i in range(5,11) ]
          131     alice_privkeys = [lnutil.Keypair(lnutil.privkey_to_pubkey(x), x) for x in alice_raw]
          132     bob_privkeys = [lnutil.Keypair(lnutil.privkey_to_pubkey(x), x) for x in bob_raw]
          133     alice_pubkeys = [lnutil.OnlyPubkeyKeypair(x.pubkey) for x in alice_privkeys]
          134     bob_pubkeys = [lnutil.OnlyPubkeyKeypair(x.pubkey) for x in bob_privkeys]
          135 
          136     alice_seed = random_gen.get_bytes(32)
          137     bob_seed = random_gen.get_bytes(32)
          138 
          139     alice_first = lnutil.secret_to_pubkey(
          140         int.from_bytes(lnutil.get_per_commitment_secret_from_seed(
          141             alice_seed, lnutil.RevocationStore.START_INDEX), "big"))
          142     bob_first = lnutil.secret_to_pubkey(
          143         int.from_bytes(lnutil.get_per_commitment_secret_from_seed(
          144             bob_seed, lnutil.RevocationStore.START_INDEX), "big"))
          145 
          146     alice, bob = (
          147         lnchannel.Channel(
          148             create_channel_state(
          149                 funding_txid, funding_index, funding_sat, True, local_amount,
          150                 remote_amount, alice_privkeys, bob_pubkeys, alice_seed, None,
          151                 bob_first, other_node_id=bob_pubkey, l_dust=200, r_dust=1300,
          152                 l_csv=5, r_csv=4
          153             ),
          154             name=bob_name,
          155             initial_feerate=feerate),
          156         lnchannel.Channel(
          157             create_channel_state(
          158                 funding_txid, funding_index, funding_sat, False, remote_amount,
          159                 local_amount, bob_privkeys, alice_pubkeys, bob_seed, None,
          160                 alice_first, other_node_id=alice_pubkey, l_dust=1300, r_dust=200,
          161                 l_csv=4, r_csv=5
          162             ),
          163             name=alice_name,
          164             initial_feerate=feerate)
          165     )
          166 
          167     alice.hm.log[LOCAL]['ctn'] = 0
          168     bob.hm.log[LOCAL]['ctn'] = 0
          169 
          170     alice._state = ChannelState.OPEN
          171     bob._state = ChannelState.OPEN
          172 
          173     a_out = alice.get_latest_commitment(LOCAL).outputs()
          174     b_out = bob.get_next_commitment(REMOTE).outputs()
          175     assert a_out == b_out, "\n" + pformat((a_out, b_out))
          176 
          177     sig_from_bob, a_htlc_sigs = bob.sign_next_commitment()
          178     sig_from_alice, b_htlc_sigs = alice.sign_next_commitment()
          179 
          180     assert len(a_htlc_sigs) == 0
          181     assert len(b_htlc_sigs) == 0
          182 
          183     alice.open_with_first_pcp(bob_first, sig_from_bob)
          184     bob.open_with_first_pcp(alice_first, sig_from_alice)
          185 
          186     alice_second = lnutil.secret_to_pubkey(int.from_bytes(lnutil.get_per_commitment_secret_from_seed(alice_seed, lnutil.RevocationStore.START_INDEX - 1), "big"))
          187     bob_second = lnutil.secret_to_pubkey(int.from_bytes(lnutil.get_per_commitment_secret_from_seed(bob_seed, lnutil.RevocationStore.START_INDEX - 1), "big"))
          188 
          189     # from funding_locked:
          190     alice.config[REMOTE].next_per_commitment_point = bob_second
          191     bob.config[REMOTE].next_per_commitment_point = alice_second
          192 
          193     alice._fallback_sweep_address = bitcoin.pubkey_to_address('p2wpkh', alice.config[LOCAL].payment_basepoint.pubkey.hex())
          194     bob._fallback_sweep_address = bitcoin.pubkey_to_address('p2wpkh', bob.config[LOCAL].payment_basepoint.pubkey.hex())
          195 
          196     alice._ignore_max_htlc_value = True
          197     bob._ignore_max_htlc_value = True
          198 
          199     return alice, bob
          200 
          201 class TestFee(ElectrumTestCase):
          202     """
          203     test
          204     https://github.com/lightningnetwork/lightning-rfc/blob/e0c436bd7a3ed6a028e1cb472908224658a14eca/03-transactions.md#requirements-2
          205     """
          206     def test_fee(self):
          207         alice_channel, bob_channel = create_test_channels(feerate=253,
          208                                                           local_msat=10000000000,
          209                                                           remote_msat=5000000000)
          210         self.assertIn(9999817, [x.value for x in alice_channel.get_latest_commitment(LOCAL).outputs()])
          211 
          212 class TestChannel(ElectrumTestCase):
          213     maxDiff = 999
          214 
          215     def assertOutputExistsByValue(self, tx, amt_sat):
          216         for o in tx.outputs():
          217             if o.value == amt_sat:
          218                 break
          219         else:
          220             self.assertFalse()
          221 
          222     @classmethod
          223     def setUpClass(cls):
          224         super().setUpClass()
          225         console_stderr_handler.setLevel(logging.DEBUG)
          226 
          227     def setUp(self):
          228         super().setUp()
          229         # Create a test channel which will be used for the duration of this
          230         # unittest. The channel will be funded evenly with Alice having 5 BTC,
          231         # and Bob having 5 BTC.
          232         self.alice_channel, self.bob_channel = create_test_channels()
          233 
          234         self.paymentPreimage = b"\x01" * 32
          235         paymentHash = bitcoin.sha256(self.paymentPreimage)
          236         self.htlc_dict = {
          237             'payment_hash' : paymentHash,
          238             'amount_msat' :  one_bitcoin_in_msat,
          239             'cltv_expiry' :  5,
          240             'timestamp'   :  0,
          241         }
          242 
          243         # First Alice adds the outgoing HTLC to her local channel's state
          244         # update log. Then Alice sends this wire message over to Bob who adds
          245         # this htlc to his remote state update log.
          246         self.aliceHtlcIndex = self.alice_channel.add_htlc(self.htlc_dict).htlc_id
          247         self.assertNotEqual(list(self.alice_channel.hm.htlcs_by_direction(REMOTE, RECEIVED, 1).values()), [])
          248 
          249         before = self.bob_channel.balance_minus_outgoing_htlcs(REMOTE)
          250         beforeLocal = self.bob_channel.balance_minus_outgoing_htlcs(LOCAL)
          251 
          252         self.bobHtlcIndex = self.bob_channel.receive_htlc(self.htlc_dict).htlc_id
          253 
          254         self.htlc = self.bob_channel.hm.log[REMOTE]['adds'][0]
          255 
          256     def test_concurrent_reversed_payment(self):
          257         self.htlc_dict['payment_hash'] = bitcoin.sha256(32 * b'\x02')
          258         self.htlc_dict['amount_msat'] += 1000
          259         self.bob_channel.add_htlc(self.htlc_dict)
          260         self.alice_channel.receive_htlc(self.htlc_dict)
          261 
          262         self.assertEqual(len(self.alice_channel.get_latest_commitment(LOCAL).outputs()), 2)
          263         self.assertEqual(len(self.alice_channel.get_next_commitment(LOCAL).outputs()), 3)
          264         self.assertEqual(len(self.alice_channel.get_latest_commitment(REMOTE).outputs()), 2)
          265         self.assertEqual(len(self.alice_channel.get_next_commitment(REMOTE).outputs()), 3)
          266 
          267         self.alice_channel.receive_new_commitment(*self.bob_channel.sign_next_commitment())
          268 
          269         self.assertEqual(len(self.alice_channel.get_latest_commitment(LOCAL).outputs()), 3)
          270         self.assertEqual(len(self.alice_channel.get_next_commitment(LOCAL).outputs()), 3)
          271         self.assertEqual(len(self.alice_channel.get_latest_commitment(REMOTE).outputs()), 2)
          272         self.assertEqual(len(self.alice_channel.get_next_commitment(REMOTE).outputs()), 3)
          273 
          274         self.alice_channel.revoke_current_commitment()
          275 
          276         self.assertEqual(len(self.alice_channel.get_latest_commitment(LOCAL).outputs()), 3)
          277         self.assertEqual(len(self.alice_channel.get_next_commitment(LOCAL).outputs()), 3)
          278         self.assertEqual(len(self.alice_channel.get_latest_commitment(REMOTE).outputs()), 2)
          279         self.assertEqual(len(self.alice_channel.get_next_commitment(REMOTE).outputs()), 4)
          280 
          281     def test_SimpleAddSettleWorkflow(self):
          282         alice_channel, bob_channel = self.alice_channel, self.bob_channel
          283         htlc = self.htlc
          284 
          285         alice_out = alice_channel.get_latest_commitment(LOCAL).outputs()
          286         short_idx, = [idx for idx, x in enumerate(alice_out) if len(x.address) == 42]
          287         long_idx,  = [idx for idx, x in enumerate(alice_out) if len(x.address) == 62]
          288         self.assertLess(alice_out[long_idx].value, 5 * 10**8, alice_out)
          289         self.assertEqual(alice_out[short_idx].value, 5 * 10**8, alice_out)
          290 
          291         alice_out = alice_channel.get_latest_commitment(REMOTE).outputs()
          292         short_idx, = [idx for idx, x in enumerate(alice_out) if len(x.address) == 42]
          293         long_idx,  = [idx for idx, x in enumerate(alice_out) if len(x.address) == 62]
          294         self.assertLess(alice_out[short_idx].value, 5 * 10**8)
          295         self.assertEqual(alice_out[long_idx].value, 5 * 10**8)
          296 
          297         self.assertTrue(alice_channel.signature_fits(alice_channel.get_latest_commitment(LOCAL)))
          298 
          299         self.assertNotEqual(alice_channel.included_htlcs(REMOTE, RECEIVED, 1), [])
          300 
          301         self.assertEqual(alice_channel.included_htlcs(REMOTE, RECEIVED, 0), [])
          302         self.assertEqual(alice_channel.included_htlcs(REMOTE, RECEIVED, 1), [htlc])
          303 
          304         self.assertEqual(bob_channel.included_htlcs(REMOTE, SENT, 0), [])
          305         self.assertEqual(bob_channel.included_htlcs(REMOTE, SENT, 1), [])
          306 
          307         self.assertEqual(alice_channel.included_htlcs(REMOTE, SENT, 0), [])
          308         self.assertEqual(alice_channel.included_htlcs(REMOTE, SENT, 1), [])
          309 
          310         self.assertEqual(bob_channel.included_htlcs(REMOTE, RECEIVED, 0), [])
          311         self.assertEqual(bob_channel.included_htlcs(REMOTE, RECEIVED, 1), [])
          312 
          313         from electrum.lnutil import extract_ctn_from_tx_and_chan
          314         tx0 = str(alice_channel.force_close_tx())
          315         self.assertEqual(alice_channel.get_oldest_unrevoked_ctn(LOCAL), 0)
          316         self.assertEqual(extract_ctn_from_tx_and_chan(alice_channel.force_close_tx(), alice_channel), 0)
          317         self.assertTrue(alice_channel.signature_fits(alice_channel.get_latest_commitment(LOCAL)))
          318 
          319         # Next alice commits this change by sending a signature message. Since
          320         # we expect the messages to be ordered, Bob will receive the HTLC we
          321         # just sent before he receives this signature, so the signature will
          322         # cover the HTLC.
          323         aliceSig, aliceHtlcSigs = alice_channel.sign_next_commitment()
          324         self.assertEqual(len(aliceHtlcSigs), 1, "alice should generate one htlc signature")
          325 
          326         self.assertTrue(alice_channel.signature_fits(alice_channel.get_latest_commitment(LOCAL)))
          327 
          328         self.assertEqual(next(iter(alice_channel.hm.get_htlcs_in_next_ctx(REMOTE)))[0], RECEIVED)
          329         self.assertEqual(alice_channel.hm.get_htlcs_in_next_ctx(REMOTE), bob_channel.hm.get_htlcs_in_next_ctx(LOCAL))
          330         self.assertEqual(alice_channel.get_latest_commitment(REMOTE).outputs(), bob_channel.get_next_commitment(LOCAL).outputs())
          331 
          332         # Bob receives this signature message, and checks that this covers the
          333         # state he has in his remote log. This includes the HTLC just sent
          334         # from Alice.
          335         self.assertTrue(bob_channel.signature_fits(bob_channel.get_latest_commitment(LOCAL)))
          336         bob_channel.receive_new_commitment(aliceSig, aliceHtlcSigs)
          337         self.assertTrue(bob_channel.signature_fits(bob_channel.get_latest_commitment(LOCAL)))
          338 
          339         self.assertEqual(bob_channel.get_oldest_unrevoked_ctn(REMOTE), 0)
          340         self.assertEqual(bob_channel.included_htlcs(LOCAL, RECEIVED, 1), [htlc])#
          341 
          342         self.assertEqual(alice_channel.included_htlcs(REMOTE, RECEIVED, 0), [])
          343         self.assertEqual(alice_channel.included_htlcs(REMOTE, RECEIVED, 1), [htlc])
          344 
          345         self.assertEqual(alice_channel.included_htlcs(REMOTE, SENT, 0), [])
          346         self.assertEqual(alice_channel.included_htlcs(REMOTE, SENT, 1), [])
          347 
          348         self.assertEqual(bob_channel.included_htlcs(REMOTE, RECEIVED, 0), [])
          349         self.assertEqual(bob_channel.included_htlcs(REMOTE, RECEIVED, 1), [])
          350 
          351         # Bob revokes his prior commitment given to him by Alice, since he now
          352         # has a valid signature for a newer commitment.
          353         bobRevocation = bob_channel.revoke_current_commitment()
          354         self.assertTrue(bob_channel.signature_fits(bob_channel.get_latest_commitment(LOCAL)))
          355 
          356         # Bob finally sends a signature for Alice's commitment transaction.
          357         # This signature will cover the HTLC, since Bob will first send the
          358         # revocation just created. The revocation also acks every received
          359         # HTLC up to the point where Alice sent her signature.
          360         bobSig, bobHtlcSigs = bob_channel.sign_next_commitment()
          361         self.assertTrue(bob_channel.signature_fits(bob_channel.get_latest_commitment(LOCAL)))
          362 
          363         self.assertEqual(len(bobHtlcSigs), 1)
          364 
          365         self.assertTrue(alice_channel.signature_fits(alice_channel.get_latest_commitment(LOCAL)))
          366 
          367         # so far: Alice added htlc, Alice signed.
          368         self.assertEqual(len(alice_channel.get_latest_commitment(LOCAL).outputs()), 2)
          369         self.assertEqual(len(alice_channel.get_next_commitment(LOCAL).outputs()), 2)
          370         self.assertEqual(len(alice_channel.get_oldest_unrevoked_commitment(REMOTE).outputs()), 2)
          371         self.assertEqual(len(alice_channel.get_latest_commitment(REMOTE).outputs()), 3)
          372 
          373         # Alice then processes this revocation, sending her own revocation for
          374         # her prior commitment transaction. Alice shouldn't have any HTLCs to
          375         # forward since she's sending an outgoing HTLC.
          376         alice_channel.receive_revocation(bobRevocation)
          377 
          378         self.assertTrue(alice_channel.signature_fits(alice_channel.get_latest_commitment(LOCAL)))
          379 
          380         self.assertEqual(len(alice_channel.get_latest_commitment(LOCAL).outputs()), 2)
          381         self.assertEqual(len(alice_channel.get_latest_commitment(REMOTE).outputs()), 3)
          382         self.assertEqual(len(alice_channel.force_close_tx().outputs()), 2)
          383 
          384         self.assertEqual(len(alice_channel.hm.log[LOCAL]['adds']), 1)
          385         self.assertEqual(alice_channel.get_next_commitment(LOCAL).outputs(),
          386                          bob_channel.get_latest_commitment(REMOTE).outputs())
          387 
          388         # Alice then processes bob's signature, and since she just received
          389         # the revocation, she expect this signature to cover everything up to
          390         # the point where she sent her signature, including the HTLC.
          391         alice_channel.receive_new_commitment(bobSig, bobHtlcSigs)
          392 
          393         self.assertEqual(len(alice_channel.get_latest_commitment(REMOTE).outputs()), 3)
          394         self.assertEqual(len(alice_channel.force_close_tx().outputs()), 3)
          395 
          396         self.assertEqual(len(alice_channel.hm.log[LOCAL]['adds']), 1)
          397 
          398         tx1 = str(alice_channel.force_close_tx())
          399         self.assertNotEqual(tx0, tx1)
          400 
          401         # Alice then generates a revocation for bob.
          402         aliceRevocation = alice_channel.revoke_current_commitment()
          403 
          404         tx2 = str(alice_channel.force_close_tx())
          405         # since alice already has the signature for the next one, it doesn't change her force close tx (it was already the newer one)
          406         self.assertEqual(tx1, tx2)
          407 
          408         # Finally Bob processes Alice's revocation, at this point the new HTLC
          409         # is fully locked in within both commitment transactions. Bob should
          410         # also be able to forward an HTLC now that the HTLC has been locked
          411         # into both commitment transactions.
          412         self.assertTrue(bob_channel.signature_fits(bob_channel.get_latest_commitment(LOCAL)))
          413         bob_channel.receive_revocation(aliceRevocation)
          414 
          415         # At this point, both sides should have the proper number of satoshis
          416         # sent, and commitment height updated within their local channel
          417         # state.
          418         aliceSent = 0
          419         bobSent = 0
          420 
          421         self.assertEqual(alice_channel.total_msat(SENT), aliceSent, "alice has incorrect milli-satoshis sent")
          422         self.assertEqual(alice_channel.total_msat(RECEIVED), bobSent, "alice has incorrect milli-satoshis received")
          423         self.assertEqual(bob_channel.total_msat(SENT), bobSent, "bob has incorrect milli-satoshis sent")
          424         self.assertEqual(bob_channel.total_msat(RECEIVED), aliceSent, "bob has incorrect milli-satoshis received")
          425         self.assertEqual(bob_channel.get_oldest_unrevoked_ctn(LOCAL), 1, "bob has incorrect commitment height")
          426         self.assertEqual(alice_channel.get_oldest_unrevoked_ctn(LOCAL), 1, "alice has incorrect commitment height")
          427 
          428         # Both commitment transactions should have three outputs, and one of
          429         # them should be exactly the amount of the HTLC.
          430         alice_ctx = alice_channel.get_next_commitment(LOCAL)
          431         bob_ctx = bob_channel.get_next_commitment(LOCAL)
          432         self.assertEqual(len(alice_ctx.outputs()), 3, "alice should have three commitment outputs, instead have %s"% len(alice_ctx.outputs()))
          433         self.assertEqual(len(bob_ctx.outputs()), 3, "bob should have three commitment outputs, instead have %s"% len(bob_ctx.outputs()))
          434         self.assertOutputExistsByValue(alice_ctx, htlc.amount_msat // 1000)
          435         self.assertOutputExistsByValue(bob_ctx, htlc.amount_msat // 1000)
          436 
          437         # Now we'll repeat a similar exchange, this time with Bob settling the
          438         # HTLC once he learns of the preimage.
          439         preimage = self.paymentPreimage
          440         bob_channel.settle_htlc(preimage, self.bobHtlcIndex)
          441 
          442         alice_channel.receive_htlc_settle(preimage, self.aliceHtlcIndex)
          443 
          444         tx3 = str(alice_channel.force_close_tx())
          445         # just settling a htlc does not change her force close tx
          446         self.assertEqual(tx2, tx3)
          447 
          448         bobSig2, bobHtlcSigs2 = bob_channel.sign_next_commitment()
          449         self.assertEqual(len(bobHtlcSigs2), 0)
          450 
          451         self.assertEqual(list(alice_channel.hm.htlcs_by_direction(REMOTE, RECEIVED).values()), [htlc])
          452         self.assertEqual(alice_channel.included_htlcs(REMOTE, RECEIVED, alice_channel.get_oldest_unrevoked_ctn(REMOTE)), [htlc])
          453 
          454         self.assertEqual(alice_channel.included_htlcs(REMOTE, RECEIVED, 1), [htlc])
          455         self.assertEqual(alice_channel.included_htlcs(REMOTE, RECEIVED, 2), [htlc])
          456 
          457         self.assertEqual(bob_channel.included_htlcs(REMOTE, SENT, 1), [htlc])
          458         self.assertEqual(bob_channel.included_htlcs(REMOTE, SENT, 2), [])
          459 
          460         self.assertEqual(alice_channel.included_htlcs(REMOTE, SENT, 1), [])
          461         self.assertEqual(alice_channel.included_htlcs(REMOTE, SENT, 2), [])
          462 
          463         self.assertEqual(bob_channel.included_htlcs(REMOTE, RECEIVED, 1), [])
          464         self.assertEqual(bob_channel.included_htlcs(REMOTE, RECEIVED, 2), [])
          465 
          466         alice_ctx_bob_version = bob_channel.get_latest_commitment(REMOTE).outputs()
          467         alice_ctx_alice_version = alice_channel.get_next_commitment(LOCAL).outputs()
          468         self.assertEqual(alice_ctx_alice_version, alice_ctx_bob_version)
          469 
          470         alice_channel.receive_new_commitment(bobSig2, bobHtlcSigs2)
          471 
          472         tx4 = str(alice_channel.force_close_tx())
          473         self.assertNotEqual(tx3, tx4)
          474 
          475         self.assertEqual(alice_channel.balance(LOCAL), 500000000000)
          476         self.assertEqual(1, alice_channel.get_oldest_unrevoked_ctn(LOCAL))
          477         self.assertEqual(len(alice_channel.included_htlcs(LOCAL, RECEIVED, ctn=2)), 0)
          478         aliceRevocation2 = alice_channel.revoke_current_commitment()
          479         aliceSig2, aliceHtlcSigs2 = alice_channel.sign_next_commitment()
          480         self.assertEqual(aliceHtlcSigs2, [], "alice should generate no htlc signatures")
          481         self.assertEqual(len(bob_channel.get_latest_commitment(LOCAL).outputs()), 3)
          482         bob_channel.receive_revocation(aliceRevocation2)
          483 
          484         bob_channel.receive_new_commitment(aliceSig2, aliceHtlcSigs2)
          485 
          486         bobRevocation2 = bob_channel.revoke_current_commitment()
          487         received = lnchannel.htlcsum(bob_channel.hm.received_in_ctn(bob_channel.get_latest_ctn(LOCAL)))
          488         self.assertEqual(one_bitcoin_in_msat, received)
          489         alice_channel.receive_revocation(bobRevocation2)
          490 
          491         # At this point, Bob should have 6 BTC settled, with Alice still having
          492         # 4 BTC. Alice's channel should show 1 BTC sent and Bob's channel
          493         # should show 1 BTC received. They should also be at commitment height
          494         # two, with the revocation window extended by 1 (5).
          495         mSatTransferred = one_bitcoin_in_msat
          496         self.assertEqual(alice_channel.total_msat(SENT), mSatTransferred, "alice satoshis sent incorrect")
          497         self.assertEqual(alice_channel.total_msat(RECEIVED), 0, "alice satoshis received incorrect")
          498         self.assertEqual(bob_channel.total_msat(RECEIVED), mSatTransferred, "bob satoshis received incorrect")
          499         self.assertEqual(bob_channel.total_msat(SENT), 0, "bob satoshis sent incorrect")
          500         self.assertEqual(bob_channel.get_latest_ctn(LOCAL), 2, "bob has incorrect commitment height")
          501         self.assertEqual(alice_channel.get_latest_ctn(LOCAL), 2, "alice has incorrect commitment height")
          502 
          503         alice_channel.update_fee(100000, True)
          504         alice_outputs = alice_channel.get_next_commitment(REMOTE).outputs()
          505         old_outputs = bob_channel.get_next_commitment(LOCAL).outputs()
          506         bob_channel.update_fee(100000, False)
          507         new_outputs = bob_channel.get_next_commitment(LOCAL).outputs()
          508         self.assertNotEqual(old_outputs, new_outputs)
          509         self.assertEqual(alice_outputs, new_outputs)
          510 
          511         tx5 = str(alice_channel.force_close_tx())
          512         # sending a fee update does not change her force close tx
          513         self.assertEqual(tx4, tx5)
          514 
          515         force_state_transition(alice_channel, bob_channel)
          516 
          517         tx6 = str(alice_channel.force_close_tx())
          518         self.assertNotEqual(tx5, tx6)
          519 
          520         self.htlc_dict['amount_msat'] *= 5
          521         bob_index = bob_channel.add_htlc(self.htlc_dict).htlc_id
          522         alice_index = alice_channel.receive_htlc(self.htlc_dict).htlc_id
          523 
          524         force_state_transition(bob_channel, alice_channel)
          525 
          526         alice_channel.settle_htlc(self.paymentPreimage, alice_index)
          527         bob_channel.receive_htlc_settle(self.paymentPreimage, bob_index)
          528 
          529         force_state_transition(alice_channel, bob_channel)
          530         self.assertEqual(alice_channel.total_msat(SENT), one_bitcoin_in_msat, "alice satoshis sent incorrect")
          531         self.assertEqual(alice_channel.total_msat(RECEIVED), 5 * one_bitcoin_in_msat, "alice satoshis received incorrect")
          532         self.assertEqual(bob_channel.total_msat(RECEIVED), one_bitcoin_in_msat, "bob satoshis received incorrect")
          533         self.assertEqual(bob_channel.total_msat(SENT), 5 * one_bitcoin_in_msat, "bob satoshis sent incorrect")
          534 
          535 
          536     def alice_to_bob_fee_update(self, fee=111):
          537         aoldctx = self.alice_channel.get_next_commitment(REMOTE).outputs()
          538         self.alice_channel.update_fee(fee, True)
          539         anewctx = self.alice_channel.get_next_commitment(REMOTE).outputs()
          540         self.assertNotEqual(aoldctx, anewctx)
          541         boldctx = self.bob_channel.get_next_commitment(LOCAL).outputs()
          542         self.bob_channel.update_fee(fee, False)
          543         bnewctx = self.bob_channel.get_next_commitment(LOCAL).outputs()
          544         self.assertNotEqual(boldctx, bnewctx)
          545         self.assertEqual(anewctx, bnewctx)
          546         return fee
          547 
          548     def test_UpdateFeeSenderCommits(self):
          549         alice_channel, bob_channel = self.alice_channel, self.bob_channel
          550 
          551         old_feerate = alice_channel.get_next_feerate(LOCAL)
          552 
          553         fee = self.alice_to_bob_fee_update()
          554         self.assertEqual(alice_channel.get_next_feerate(LOCAL), old_feerate)
          555 
          556         alice_sig, alice_htlc_sigs = alice_channel.sign_next_commitment()
          557         #self.assertEqual(alice_channel.get_next_feerate(LOCAL), old_feerate)
          558 
          559         bob_channel.receive_new_commitment(alice_sig, alice_htlc_sigs)
          560 
          561         self.assertNotEqual(fee, bob_channel.get_oldest_unrevoked_feerate(LOCAL))
          562         self.assertEqual(fee, bob_channel.get_latest_feerate(LOCAL))
          563         rev = bob_channel.revoke_current_commitment()
          564         self.assertEqual(fee, bob_channel.get_oldest_unrevoked_feerate(LOCAL))
          565 
          566         alice_channel.receive_revocation(rev)
          567 
          568 
          569         bob_sig, bob_htlc_sigs = bob_channel.sign_next_commitment()
          570         alice_channel.receive_new_commitment(bob_sig, bob_htlc_sigs)
          571 
          572         self.assertNotEqual(fee, alice_channel.get_oldest_unrevoked_feerate(LOCAL))
          573         self.assertEqual(fee, alice_channel.get_latest_feerate(LOCAL))
          574         rev = alice_channel.revoke_current_commitment()
          575         self.assertEqual(fee, alice_channel.get_oldest_unrevoked_feerate(LOCAL))
          576 
          577         bob_channel.receive_revocation(rev)
          578         self.assertEqual(fee, bob_channel.get_oldest_unrevoked_feerate(LOCAL))
          579         self.assertEqual(fee, bob_channel.get_latest_feerate(LOCAL))
          580 
          581 
          582     def test_UpdateFeeReceiverCommits(self):
          583         fee = self.alice_to_bob_fee_update()
          584 
          585         alice_channel, bob_channel = self.alice_channel, self.bob_channel
          586 
          587         bob_sig, bob_htlc_sigs = bob_channel.sign_next_commitment()
          588         alice_channel.receive_new_commitment(bob_sig, bob_htlc_sigs)
          589 
          590         alice_revocation = alice_channel.revoke_current_commitment()
          591         bob_channel.receive_revocation(alice_revocation)
          592         alice_sig, alice_htlc_sigs = alice_channel.sign_next_commitment()
          593         bob_channel.receive_new_commitment(alice_sig, alice_htlc_sigs)
          594 
          595         self.assertNotEqual(fee, bob_channel.get_oldest_unrevoked_feerate(LOCAL))
          596         self.assertEqual(fee, bob_channel.get_latest_feerate(LOCAL))
          597         bob_revocation = bob_channel.revoke_current_commitment()
          598         self.assertEqual(fee, bob_channel.get_oldest_unrevoked_feerate(LOCAL))
          599 
          600         bob_sig, bob_htlc_sigs = bob_channel.sign_next_commitment()
          601         alice_channel.receive_revocation(bob_revocation)
          602         alice_channel.receive_new_commitment(bob_sig, bob_htlc_sigs)
          603 
          604         self.assertNotEqual(fee, alice_channel.get_oldest_unrevoked_feerate(LOCAL))
          605         self.assertEqual(fee, alice_channel.get_latest_feerate(LOCAL))
          606         alice_revocation = alice_channel.revoke_current_commitment()
          607         self.assertEqual(fee, alice_channel.get_oldest_unrevoked_feerate(LOCAL))
          608 
          609         bob_channel.receive_revocation(alice_revocation)
          610         self.assertEqual(fee, bob_channel.get_oldest_unrevoked_feerate(LOCAL))
          611         self.assertEqual(fee, bob_channel.get_latest_feerate(LOCAL))
          612 
          613     @unittest.skip("broken probably because we havn't implemented detecting when we come out of a situation where we violate reserve")
          614     def test_AddHTLCNegativeBalance(self):
          615         # the test in lnd doesn't set the fee to zero.
          616         # probably lnd subtracts commitment fee after deciding weather
          617         # an htlc can be added. so we set the fee to zero so that
          618         # the test can work.
          619         self.alice_to_bob_fee_update(0)
          620         force_state_transition(self.alice_channel, self.bob_channel)
          621 
          622         self.htlc_dict['payment_hash'] = bitcoin.sha256(32 * b'\x02')
          623         self.alice_channel.add_htlc(self.htlc_dict)
          624         self.htlc_dict['payment_hash'] = bitcoin.sha256(32 * b'\x03')
          625         self.alice_channel.add_htlc(self.htlc_dict)
          626         # now there are three htlcs (one was in setUp)
          627 
          628         # Alice now has an available balance of 2 BTC. We'll add a new HTLC of
          629         # value 2 BTC, which should make Alice's balance negative (since she
          630         # has to pay a commitment fee).
          631         new = dict(self.htlc_dict)
          632         new['amount_msat'] *= 2.5
          633         new['payment_hash'] = bitcoin.sha256(32 * b'\x04')
          634         with self.assertRaises(lnutil.PaymentFailure) as cm:
          635             self.alice_channel.add_htlc(new)
          636         self.assertIn('Not enough local balance', cm.exception.args[0])
          637 
          638 
          639 class TestAvailableToSpend(ElectrumTestCase):
          640     def test_DesyncHTLCs(self):
          641         alice_channel, bob_channel = create_test_channels()
          642         self.assertEqual(499986152000, alice_channel.available_to_spend(LOCAL))
          643         self.assertEqual(500000000000, bob_channel.available_to_spend(LOCAL))
          644 
          645         paymentPreimage = b"\x01" * 32
          646         paymentHash = bitcoin.sha256(paymentPreimage)
          647         htlc_dict = {
          648             'payment_hash' : paymentHash,
          649             'amount_msat' :  one_bitcoin_in_msat * 41 // 10,
          650             'cltv_expiry' :  5,
          651             'timestamp'   :  0,
          652         }
          653 
          654         alice_idx = alice_channel.add_htlc(htlc_dict).htlc_id
          655         bob_idx = bob_channel.receive_htlc(htlc_dict).htlc_id
          656         self.assertEqual(89984088000, alice_channel.available_to_spend(LOCAL))
          657         self.assertEqual(500000000000, bob_channel.available_to_spend(LOCAL))
          658 
          659         force_state_transition(alice_channel, bob_channel)
          660         bob_channel.fail_htlc(bob_idx)
          661         alice_channel.receive_fail_htlc(alice_idx, error_bytes=None)
          662         self.assertEqual(89984088000, alice_channel.available_to_spend(LOCAL))
          663         self.assertEqual(500000000000, bob_channel.available_to_spend(LOCAL))
          664         # Alice now has gotten all her original balance (5 BTC) back, however,
          665         # adding a new HTLC at this point SHOULD fail, since if she adds the
          666         # HTLC and signs the next state, Bob cannot assume she received the
          667         # FailHTLC, and must assume she doesn't have the necessary balance
          668         # available.
          669         # We try adding an HTLC of value 1 BTC, which should fail because the
          670         # balance is unavailable.
          671         htlc_dict = {
          672             'payment_hash' : paymentHash,
          673             'amount_msat' :  one_bitcoin_in_msat,
          674             'cltv_expiry' :  5,
          675             'timestamp'   :  0,
          676         }
          677         with self.assertRaises(lnutil.PaymentFailure):
          678             alice_channel.add_htlc(htlc_dict)
          679         # Now do a state transition, which will ACK the FailHTLC, making Alice
          680         # able to add the new HTLC.
          681         force_state_transition(alice_channel, bob_channel)
          682         self.assertEqual(499986152000, alice_channel.available_to_spend(LOCAL))
          683         self.assertEqual(500000000000, bob_channel.available_to_spend(LOCAL))
          684         alice_channel.add_htlc(htlc_dict)
          685 
          686     def test_max_htlc_value(self):
          687         alice_channel, bob_channel = create_test_channels()
          688         paymentPreimage = b"\x01" * 32
          689         paymentHash = bitcoin.sha256(paymentPreimage)
          690         htlc_dict = {
          691             'payment_hash' : paymentHash,
          692             'amount_msat' :  one_bitcoin_in_msat * 41 // 10,
          693             'cltv_expiry' :  5,
          694             'timestamp'   :  0,
          695         }
          696 
          697         alice_channel._ignore_max_htlc_value = False
          698         bob_channel._ignore_max_htlc_value = False
          699         with self.assertRaises(lnutil.PaymentFailure):
          700             alice_channel.add_htlc(htlc_dict)
          701         with self.assertRaises(lnutil.RemoteMisbehaving):
          702             bob_channel.receive_htlc(htlc_dict)
          703 
          704         alice_channel._ignore_max_htlc_value = True
          705         bob_channel._ignore_max_htlc_value = True
          706         alice_channel.add_htlc(htlc_dict)
          707         bob_channel.receive_htlc(htlc_dict)
          708 
          709 
          710 class TestChanReserve(ElectrumTestCase):
          711     def setUp(self):
          712         alice_channel, bob_channel = create_test_channels()
          713         alice_min_reserve = int(.5 * one_bitcoin_in_msat // 1000)
          714         # We set Bob's channel reserve to a value that is larger than
          715         # his current balance in the channel. This will ensure that
          716         # after a channel is first opened, Bob can still receive HTLCs
          717         # even though his balance is less than his channel reserve.
          718         bob_min_reserve = 6 * one_bitcoin_in_msat // 1000
          719         # bob min reserve was decided by alice, but applies to bob
          720 
          721         alice_channel.config[LOCAL].reserve_sat = bob_min_reserve
          722         alice_channel.config[REMOTE].reserve_sat = alice_min_reserve
          723 
          724         bob_channel.config[LOCAL].reserve_sat = alice_min_reserve
          725         bob_channel.config[REMOTE].reserve_sat = bob_min_reserve
          726 
          727         self.alice_channel = alice_channel
          728         self.bob_channel = bob_channel
          729 
          730     @unittest.skip("broken probably because we havn't implemented detecting when we come out of a situation where we violate reserve")
          731     def test_part1(self):
          732         # Add an HTLC that will increase Bob's balance. This should succeed,
          733         # since Alice stays above her channel reserve, and Bob increases his
          734         # balance (while still being below his channel reserve).
          735         #
          736         # Resulting balances:
          737         #        Alice:        4.5
          738         #        Bob:        5.0
          739         paymentPreimage = b"\x01" * 32
          740         paymentHash = bitcoin.sha256(paymentPreimage)
          741         htlc_dict = {
          742             'payment_hash' : paymentHash,
          743             'amount_msat' :  int(.5 * one_bitcoin_in_msat),
          744             'cltv_expiry' :  5,
          745             'timestamp'   :  0,
          746         }
          747         self.alice_channel.add_htlc(htlc_dict)
          748         self.bob_channel.receive_htlc(htlc_dict)
          749         # Force a state transition, making sure this HTLC is considered valid
          750         # even though the channel reserves are not met.
          751         force_state_transition(self.alice_channel, self.bob_channel)
          752 
          753         aliceSelfBalance = self.alice_channel.balance(LOCAL)\
          754                 - lnchannel.htlcsum(self.alice_channel.hm.htlcs_by_direction(LOCAL, SENT).values())
          755         bobBalance = self.bob_channel.balance(REMOTE)\
          756                 - lnchannel.htlcsum(self.alice_channel.hm.htlcs_by_direction(REMOTE, SENT).values())
          757         self.assertEqual(aliceSelfBalance, one_bitcoin_in_msat*4.5)
          758         self.assertEqual(bobBalance, one_bitcoin_in_msat*5)
          759         # Now let Bob try to add an HTLC. This should fail, since it will
          760         # decrease his balance, which is already below the channel reserve.
          761         #
          762         # Resulting balances:
          763         #        Alice:        4.5
          764         #        Bob:        5.0
          765         with self.assertRaises(lnutil.PaymentFailure):
          766             htlc_dict['payment_hash'] = bitcoin.sha256(32 * b'\x02')
          767             self.bob_channel.add_htlc(htlc_dict)
          768         with self.assertRaises(lnutil.RemoteMisbehaving):
          769             self.alice_channel.receive_htlc(htlc_dict)
          770 
          771     def part2(self):
          772         paymentPreimage = b"\x01" * 32
          773         paymentHash = bitcoin.sha256(paymentPreimage)
          774         # Now we'll add HTLC of 3.5 BTC to Alice's commitment, this should put
          775         # Alice's balance at 1.5 BTC.
          776         #
          777         # Resulting balances:
          778         #        Alice:        1.5
          779         #        Bob:        9.5
          780         htlc_dict = {
          781             'payment_hash' : paymentHash,
          782             'amount_msat' :  int(3.5 * one_bitcoin_in_msat),
          783             'cltv_expiry' :  5,
          784         }
          785         self.alice_channel.add_htlc(htlc_dict)
          786         self.bob_channel.receive_htlc(htlc_dict)
          787         # Add a second HTLC of 1 BTC. This should fail because it will take
          788         # Alice's balance all the way down to her channel reserve, but since
          789         # she is the initiator the additional transaction fee makes her
          790         # balance dip below.
          791         htlc_dict['amount_msat'] = one_bitcoin_in_msat
          792         with self.assertRaises(lnutil.PaymentFailure):
          793             self.alice_channel.add_htlc(htlc_dict)
          794         with self.assertRaises(lnutil.RemoteMisbehaving):
          795             self.bob_channel.receive_htlc(htlc_dict)
          796 
          797     def part3(self):
          798         # Add a HTLC of 2 BTC to Alice, and the settle it.
          799         # Resulting balances:
          800         #        Alice:        3.0
          801         #        Bob:        7.0
          802         paymentPreimage = b"\x01" * 32
          803         paymentHash = bitcoin.sha256(paymentPreimage)
          804         htlc_dict = {
          805             'payment_hash' : paymentHash,
          806             'amount_msat' :  int(2 * one_bitcoin_in_msat),
          807             'cltv_expiry' :  5,
          808             'timestamp'   :  0,
          809         }
          810         alice_idx = self.alice_channel.add_htlc(htlc_dict).htlc_id
          811         bob_idx = self.bob_channel.receive_htlc(htlc_dict).htlc_id
          812         force_state_transition(self.alice_channel, self.bob_channel)
          813         self.check_bals(one_bitcoin_in_msat * 3
          814                         - self.alice_channel.get_next_fee(LOCAL),
          815                         one_bitcoin_in_msat * 5)
          816         self.bob_channel.settle_htlc(paymentPreimage, bob_idx)
          817         self.alice_channel.receive_htlc_settle(paymentPreimage, alice_idx)
          818         force_state_transition(self.alice_channel, self.bob_channel)
          819         self.check_bals(one_bitcoin_in_msat * 3
          820                         - self.alice_channel.get_next_fee(LOCAL),
          821                         one_bitcoin_in_msat * 7)
          822         # And now let Bob add an HTLC of 1 BTC. This will take Bob's balance
          823         # all the way down to his channel reserve, but since he is not paying
          824         # the fee this is okay.
          825         htlc_dict['amount_msat'] = one_bitcoin_in_msat
          826         self.bob_channel.add_htlc(htlc_dict)
          827         self.alice_channel.receive_htlc(htlc_dict)
          828         force_state_transition(self.alice_channel, self.bob_channel)
          829         self.check_bals(one_bitcoin_in_msat * 3 \
          830                         - self.alice_channel.get_next_fee(LOCAL),
          831                         one_bitcoin_in_msat * 6)
          832 
          833     def check_bals(self, amt1, amt2):
          834         self.assertEqual(self.alice_channel.available_to_spend(LOCAL), amt1)
          835         self.assertEqual(self.bob_channel.available_to_spend(REMOTE), amt1)
          836         self.assertEqual(self.alice_channel.available_to_spend(REMOTE), amt2)
          837         self.assertEqual(self.bob_channel.available_to_spend(LOCAL), amt2)
          838 
          839 class TestDust(ElectrumTestCase):
          840     def test_DustLimit(self):
          841         alice_channel, bob_channel = create_test_channels()
          842 
          843         paymentPreimage = b"\x01" * 32
          844         paymentHash = bitcoin.sha256(paymentPreimage)
          845         fee_per_kw = alice_channel.get_next_feerate(LOCAL)
          846         self.assertEqual(fee_per_kw, 6000)
          847         htlcAmt = 500 + lnutil.HTLC_TIMEOUT_WEIGHT * (fee_per_kw // 1000)
          848         self.assertEqual(htlcAmt, 4478)
          849         htlc = {
          850             'payment_hash' : paymentHash,
          851             'amount_msat' :  1000 * htlcAmt,
          852             'cltv_expiry' :  5, # also in create_test_channels
          853             'timestamp'   :  0,
          854         }
          855 
          856         old_values = [x.value for x in bob_channel.get_latest_commitment(LOCAL).outputs() ]
          857         aliceHtlcIndex = alice_channel.add_htlc(htlc).htlc_id
          858         bobHtlcIndex = bob_channel.receive_htlc(htlc).htlc_id
          859         force_state_transition(alice_channel, bob_channel)
          860         alice_ctx = alice_channel.get_latest_commitment(LOCAL)
          861         bob_ctx = bob_channel.get_latest_commitment(LOCAL)
          862         new_values = [x.value for x in bob_ctx.outputs() ]
          863         self.assertNotEqual(old_values, new_values)
          864         self.assertEqual(len(alice_ctx.outputs()), 3)
          865         self.assertEqual(len(bob_ctx.outputs()), 2)
          866         default_fee = calc_static_fee(0)
          867         self.assertEqual(bob_channel.get_next_fee(LOCAL), default_fee + htlcAmt)
          868         bob_channel.settle_htlc(paymentPreimage, bobHtlcIndex)
          869         alice_channel.receive_htlc_settle(paymentPreimage, aliceHtlcIndex)
          870         force_state_transition(bob_channel, alice_channel)
          871         self.assertEqual(len(alice_channel.get_next_commitment(LOCAL).outputs()), 2)
          872         self.assertEqual(alice_channel.total_msat(SENT) // 1000, htlcAmt)
          873 
          874 def force_state_transition(chanA, chanB):
          875     chanB.receive_new_commitment(*chanA.sign_next_commitment())
          876     rev = chanB.revoke_current_commitment()
          877     bob_sig, bob_htlc_sigs = chanB.sign_next_commitment()
          878     chanA.receive_revocation(rev)
          879     chanA.receive_new_commitment(bob_sig, bob_htlc_sigs)
          880     chanB.receive_revocation(chanA.revoke_current_commitment())
          881 
          882 # calcStaticFee calculates appropriate fees for commitment transactions.  This
          883 # function provides a simple way to allow test balance assertions to take fee
          884 # calculations into account.
          885 def calc_static_fee(numHTLCs):
          886     commitWeight = 724
          887     htlcWeight   = 172
          888     feePerKw     = 24//4 * 1000
          889     return feePerKw * (commitWeight + htlcWeight*numHTLCs) // 1000