electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

test_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