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