diff --git a/tests/test_lnchannel.py b/tests/test_lnchannel.py index c933d5720..de149f5e5 100644 --- a/tests/test_lnchannel.py +++ b/tests/test_lnchannel.py @@ -35,6 +35,7 @@ from electrum import lnutil from electrum import bip32 as bip32_utils from electrum.crypto import privkey_to_pubkey from electrum.lnutil import SENT, LOCAL, REMOTE, RECEIVED, UpdateAddHtlc +from electrum.lnutil import effective_htlc_tx_weight from electrum.logging import console_stderr_handler from electrum.lnchannel import ChannelState from electrum.json_db import StoredDict @@ -42,6 +43,7 @@ from electrum.coinchooser import PRNG from . import ElectrumTestCase +TEST_ANCHOR_CHANNELS = True one_bitcoin_in_msat = bitcoin.COIN * 1000 @@ -49,10 +51,13 @@ one_bitcoin_in_msat = bitcoin.COIN * 1000 def create_channel_state(funding_txid, funding_index, funding_sat, is_initiator, local_amount, remote_amount, privkeys, other_pubkeys, seed, cur, nex, other_node_id, l_dust, r_dust, l_csv, - r_csv): + r_csv, anchor_outputs=TEST_ANCHOR_CHANNELS): #assert local_amount > 0 #assert remote_amount > 0 channel_id, _ = lnpeer.channel_id_from_funding_tx(funding_txid, funding_index) + channel_type = lnutil.ChannelType.OPTION_STATIC_REMOTEKEY + if anchor_outputs: + channel_type |= lnutil.ChannelType.OPTION_ANCHOR_OUTPUTS state = { "channel_id":channel_id.hex(), "short_channel_id":channel_id[:8], @@ -111,7 +116,7 @@ def create_channel_state(funding_txid, funding_index, funding_sat, is_initiator, 'log': {}, 'unfulfilled_htlcs': {}, 'revocation_store': {}, - 'channel_type': lnutil.ChannelType.OPTION_STATIC_REMOTEKEY + 'channel_type': channel_type, } return StoredDict(state, None, []) @@ -124,7 +129,8 @@ def bip32(sequence): def create_test_channels(*, feerate=6000, local_msat=None, remote_msat=None, alice_name="alice", bob_name="bob", - alice_pubkey=b"\x01"*33, bob_pubkey=b"\x02"*33, random_seed=None): + alice_pubkey=b"\x01"*33, bob_pubkey=b"\x02"*33, random_seed=None, + anchor_outputs=TEST_ANCHOR_CHANNELS): if random_seed is None: # needed for deterministic randomness random_seed = os.urandom(32) random_gen = PRNG(random_seed) @@ -156,7 +162,7 @@ def create_test_channels(*, feerate=6000, local_msat=None, remote_msat=None, funding_txid, funding_index, funding_sat, True, local_amount, remote_amount, alice_privkeys, bob_pubkeys, alice_seed, None, bob_first, other_node_id=bob_pubkey, l_dust=200, r_dust=1300, - l_csv=5, r_csv=4 + l_csv=5, r_csv=4, anchor_outputs=anchor_outputs ), name=f"{alice_name}->{bob_name}", initial_feerate=feerate), @@ -165,7 +171,7 @@ def create_test_channels(*, feerate=6000, local_msat=None, remote_msat=None, funding_txid, funding_index, funding_sat, False, remote_amount, local_amount, bob_privkeys, alice_pubkeys, bob_seed, None, alice_first, other_node_id=alice_pubkey, l_dust=1300, r_dust=200, - l_csv=4, r_csv=5 + l_csv=4, r_csv=5, anchor_outputs=anchor_outputs ), name=f"{bob_name}->{alice_name}", initial_feerate=feerate) @@ -210,8 +216,9 @@ class TestFee(ElectrumTestCase): def test_fee(self): alice_channel, bob_channel = create_test_channels(feerate=253, local_msat=10000000000, - remote_msat=5000000000) - self.assertIn(9999817, [x.value for x in alice_channel.get_latest_commitment(LOCAL).outputs()]) + remote_msat=5000000000, anchor_outputs=TEST_ANCHOR_CHANNELS) + expected_value = 9999056 if TEST_ANCHOR_CHANNELS else 9999817 + self.assertIn(expected_value, [x.value for x in alice_channel.get_latest_commitment(LOCAL).outputs()]) class TestChannel(ElectrumTestCase): maxDiff = 999 @@ -223,6 +230,9 @@ class TestChannel(ElectrumTestCase): else: self.assertFalse() + def assertNumberNonAnchorOutputs(self, number, tx): + self.assertEqual(number, len(tx.outputs()) - (2 if TEST_ANCHOR_CHANNELS else 0)) + @classmethod def setUpClass(cls): super().setUpClass() @@ -233,15 +243,15 @@ class TestChannel(ElectrumTestCase): # Create a test channel which will be used for the duration of this # unittest. The channel will be funded evenly with Alice having 5 BTC, # and Bob having 5 BTC. - self.alice_channel, self.bob_channel = create_test_channels() + self.alice_channel, self.bob_channel = create_test_channels(anchor_outputs=TEST_ANCHOR_CHANNELS) self.paymentPreimage = b"\x01" * 32 paymentHash = bitcoin.sha256(self.paymentPreimage) self.htlc_dict = { - 'payment_hash' : paymentHash, - 'amount_msat' : one_bitcoin_in_msat, - 'cltv_abs' : 5, - 'timestamp' : 0, + 'payment_hash': paymentHash, + 'amount_msat': one_bitcoin_in_msat, + 'cltv_abs': 5, + 'timestamp': 0, } # First Alice adds the outgoing HTLC to her local channel's state @@ -263,40 +273,60 @@ class TestChannel(ElectrumTestCase): self.bob_channel.add_htlc(self.htlc_dict) self.alice_channel.receive_htlc(self.htlc_dict) - self.assertEqual(len(self.alice_channel.get_latest_commitment(LOCAL).outputs()), 2) - self.assertEqual(len(self.alice_channel.get_next_commitment(LOCAL).outputs()), 3) - self.assertEqual(len(self.alice_channel.get_latest_commitment(REMOTE).outputs()), 2) - self.assertEqual(len(self.alice_channel.get_next_commitment(REMOTE).outputs()), 3) + self.assertNumberNonAnchorOutputs(2, self.alice_channel.get_latest_commitment(LOCAL)) + self.assertNumberNonAnchorOutputs(3, self.alice_channel.get_next_commitment(LOCAL)) + self.assertNumberNonAnchorOutputs(2, self.alice_channel.get_latest_commitment(REMOTE)) + self.assertNumberNonAnchorOutputs(3, self.alice_channel.get_next_commitment(REMOTE)) self.alice_channel.receive_new_commitment(*self.bob_channel.sign_next_commitment()) - self.assertEqual(len(self.alice_channel.get_latest_commitment(LOCAL).outputs()), 3) - self.assertEqual(len(self.alice_channel.get_next_commitment(LOCAL).outputs()), 3) - self.assertEqual(len(self.alice_channel.get_latest_commitment(REMOTE).outputs()), 2) - self.assertEqual(len(self.alice_channel.get_next_commitment(REMOTE).outputs()), 3) + self.assertNumberNonAnchorOutputs(3, self.alice_channel.get_latest_commitment(LOCAL)) + self.assertNumberNonAnchorOutputs(3, self.alice_channel.get_next_commitment(LOCAL)) + self.assertNumberNonAnchorOutputs(2, self.alice_channel.get_latest_commitment(REMOTE)) + self.assertNumberNonAnchorOutputs(3, self.alice_channel.get_next_commitment(REMOTE)) self.alice_channel.revoke_current_commitment() - self.assertEqual(len(self.alice_channel.get_latest_commitment(LOCAL).outputs()), 3) - self.assertEqual(len(self.alice_channel.get_next_commitment(LOCAL).outputs()), 3) - self.assertEqual(len(self.alice_channel.get_latest_commitment(REMOTE).outputs()), 2) - self.assertEqual(len(self.alice_channel.get_next_commitment(REMOTE).outputs()), 4) + self.assertNumberNonAnchorOutputs(3, self.alice_channel.get_latest_commitment(LOCAL)) + self.assertNumberNonAnchorOutputs(3, self.alice_channel.get_next_commitment(LOCAL)) + self.assertNumberNonAnchorOutputs(2, self.alice_channel.get_latest_commitment(REMOTE)) + self.assertNumberNonAnchorOutputs(4, self.alice_channel.get_next_commitment(REMOTE)) def test_SimpleAddSettleWorkflow(self): alice_channel, bob_channel = self.alice_channel, self.bob_channel htlc = self.htlc + # Starting point: alice has sent an update_add_htlc message to bob + # but the htlc is not yet committed to alice_out = alice_channel.get_latest_commitment(LOCAL).outputs() - short_idx, = [idx for idx, x in enumerate(alice_out) if len(x.address) == 42] - long_idx, = [idx for idx, x in enumerate(alice_out) if len(x.address) == 62] - self.assertLess(alice_out[long_idx].value, 5 * 10**8, alice_out) - self.assertEqual(alice_out[short_idx].value, 5 * 10**8, alice_out) + if not alice_channel.has_anchors(): + # ctx outputs are ordered by increasing amounts + low_amt_idx = 0 + assert len(alice_out[low_amt_idx].address) == 62 # p2wsh + high_amt_idx = 1 + assert len(alice_out[high_amt_idx].address) == 42 # p2wpkh + else: + # using anchor outputs, all outputs are p2wsh + low_amt_idx = 2 + assert len(alice_out[low_amt_idx].address) == 62 + high_amt_idx = 3 + assert len(alice_out[high_amt_idx].address) == 62 + self.assertLess(alice_out[low_amt_idx].value, 5 * 10**8, alice_out) + self.assertEqual(alice_out[high_amt_idx].value, 5 * 10**8, alice_out) alice_out = alice_channel.get_latest_commitment(REMOTE).outputs() - short_idx, = [idx for idx, x in enumerate(alice_out) if len(x.address) == 42] - long_idx, = [idx for idx, x in enumerate(alice_out) if len(x.address) == 62] - self.assertLess(alice_out[short_idx].value, 5 * 10**8) - self.assertEqual(alice_out[long_idx].value, 5 * 10**8) + if not alice_channel.has_anchors(): + low_amt_idx = 0 + assert len(alice_out[low_amt_idx].address) == 42 + high_amt_idx = 1 + assert len(alice_out[high_amt_idx].address) == 62 + else: + low_amt_idx = 2 + assert len(alice_out[low_amt_idx].address) == 62 + high_amt_idx = 3 + assert len(alice_out[high_amt_idx].address) == 62 + self.assertLess(alice_out[low_amt_idx].value, 5 * 10**8) + self.assertEqual(alice_out[high_amt_idx].value, 5 * 10**8) self.assertTrue(alice_channel.signature_fits(alice_channel.get_latest_commitment(LOCAL))) @@ -341,7 +371,7 @@ class TestChannel(ElectrumTestCase): self.assertTrue(bob_channel.signature_fits(bob_channel.get_latest_commitment(LOCAL))) self.assertEqual(bob_channel.get_oldest_unrevoked_ctn(REMOTE), 0) - self.assertEqual(bob_channel.included_htlcs(LOCAL, RECEIVED, 1), [htlc])# + self.assertEqual(bob_channel.included_htlcs(LOCAL, RECEIVED, 1), [htlc]) self.assertEqual(alice_channel.included_htlcs(REMOTE, RECEIVED, 0), []) self.assertEqual(alice_channel.included_htlcs(REMOTE, RECEIVED, 1), [htlc]) @@ -369,10 +399,10 @@ class TestChannel(ElectrumTestCase): self.assertTrue(alice_channel.signature_fits(alice_channel.get_latest_commitment(LOCAL))) # so far: Alice added htlc, Alice signed. - self.assertEqual(len(alice_channel.get_latest_commitment(LOCAL).outputs()), 2) - self.assertEqual(len(alice_channel.get_next_commitment(LOCAL).outputs()), 2) - self.assertEqual(len(alice_channel.get_oldest_unrevoked_commitment(REMOTE).outputs()), 2) - self.assertEqual(len(alice_channel.get_latest_commitment(REMOTE).outputs()), 3) + self.assertNumberNonAnchorOutputs(2, alice_channel.get_latest_commitment(LOCAL)) + self.assertNumberNonAnchorOutputs(2, alice_channel.get_next_commitment(LOCAL)) + self.assertNumberNonAnchorOutputs(2, alice_channel.get_oldest_unrevoked_commitment(REMOTE)) + self.assertNumberNonAnchorOutputs(3, alice_channel.get_latest_commitment(REMOTE)) # Alice then processes this revocation, sending her own revocation for # her prior commitment transaction. Alice shouldn't have any HTLCs to @@ -381,21 +411,21 @@ class TestChannel(ElectrumTestCase): self.assertTrue(alice_channel.signature_fits(alice_channel.get_latest_commitment(LOCAL))) - self.assertEqual(len(alice_channel.get_latest_commitment(LOCAL).outputs()), 2) - self.assertEqual(len(alice_channel.get_latest_commitment(REMOTE).outputs()), 3) - self.assertEqual(len(alice_channel.force_close_tx().outputs()), 2) + self.assertNumberNonAnchorOutputs(2, alice_channel.get_latest_commitment(LOCAL)) + self.assertNumberNonAnchorOutputs(3, alice_channel.get_latest_commitment(REMOTE)) + self.assertNumberNonAnchorOutputs(2, alice_channel.force_close_tx()) self.assertEqual(len(alice_channel.hm.log[LOCAL]['adds']), 1) self.assertEqual(alice_channel.get_next_commitment(LOCAL).outputs(), bob_channel.get_latest_commitment(REMOTE).outputs()) # Alice then processes bob's signature, and since she just received - # the revocation, she expect this signature to cover everything up to + # the revocation, she expects this signature to cover everything up to # the point where she sent her signature, including the HTLC. alice_channel.receive_new_commitment(bobSig, bobHtlcSigs) - self.assertEqual(len(alice_channel.get_latest_commitment(REMOTE).outputs()), 3) - self.assertEqual(len(alice_channel.force_close_tx().outputs()), 3) + self.assertNumberNonAnchorOutputs(3, alice_channel.get_latest_commitment(REMOTE)) + self.assertNumberNonAnchorOutputs(3, alice_channel.force_close_tx()) self.assertEqual(len(alice_channel.hm.log[LOCAL]['adds']), 1) @@ -433,8 +463,8 @@ class TestChannel(ElectrumTestCase): # them should be exactly the amount of the HTLC. alice_ctx = alice_channel.get_next_commitment(LOCAL) bob_ctx = bob_channel.get_next_commitment(LOCAL) - self.assertEqual(len(alice_ctx.outputs()), 3, "alice should have three commitment outputs, instead have %s"% len(alice_ctx.outputs())) - self.assertEqual(len(bob_ctx.outputs()), 3, "bob should have three commitment outputs, instead have %s"% len(bob_ctx.outputs())) + self.assertNumberNonAnchorOutputs(3, alice_ctx) + self.assertNumberNonAnchorOutputs(3, bob_ctx) self.assertOutputExistsByValue(alice_ctx, htlc.amount_msat // 1000) self.assertOutputExistsByValue(bob_ctx, htlc.amount_msat // 1000) @@ -482,7 +512,7 @@ class TestChannel(ElectrumTestCase): aliceRevocation2 = alice_channel.revoke_current_commitment() aliceSig2, aliceHtlcSigs2 = alice_channel.sign_next_commitment() self.assertEqual(aliceHtlcSigs2, [], "alice should generate no htlc signatures") - self.assertEqual(len(bob_channel.get_latest_commitment(LOCAL).outputs()), 3) + self.assertNumberNonAnchorOutputs(3, bob_channel.get_latest_commitment(LOCAL)) bob_channel.receive_revocation(aliceRevocation2) bob_channel.receive_new_commitment(aliceSig2, aliceHtlcSigs2) @@ -643,7 +673,7 @@ class TestChannel(ElectrumTestCase): class TestAvailableToSpend(ElectrumTestCase): def test_DesyncHTLCs(self): alice_channel, bob_channel = create_test_channels() - self.assertEqual(499986152000, alice_channel.available_to_spend(LOCAL)) + self.assertEqual(499986152000 if not alice_channel.has_anchors() else 499980692000, alice_channel.available_to_spend(LOCAL)) self.assertEqual(500000000000, bob_channel.available_to_spend(LOCAL)) paymentPreimage = b"\x01" * 32 @@ -657,13 +687,13 @@ class TestAvailableToSpend(ElectrumTestCase): alice_idx = alice_channel.add_htlc(htlc).htlc_id bob_idx = bob_channel.receive_htlc(htlc).htlc_id - self.assertEqual(89984088000, alice_channel.available_to_spend(LOCAL)) + self.assertEqual(89984088000 if not alice_channel.has_anchors() else 89978628000, alice_channel.available_to_spend(LOCAL)) self.assertEqual(500000000000, bob_channel.available_to_spend(LOCAL)) force_state_transition(alice_channel, bob_channel) bob_channel.fail_htlc(bob_idx) alice_channel.receive_fail_htlc(alice_idx, error_bytes=None) - self.assertEqual(89984088000, alice_channel.available_to_spend(LOCAL)) + self.assertEqual(89984088000 if not alice_channel.has_anchors() else 89978628000, alice_channel.available_to_spend(LOCAL)) self.assertEqual(500000000000, bob_channel.available_to_spend(LOCAL)) # Alice now has gotten all her original balance (5 BTC) back, however, # adding a new HTLC at this point SHOULD fail, since if she adds the @@ -683,7 +713,7 @@ class TestAvailableToSpend(ElectrumTestCase): # Now do a state transition, which will ACK the FailHTLC, making Alice # able to add the new HTLC. force_state_transition(alice_channel, bob_channel) - self.assertEqual(499986152000, alice_channel.available_to_spend(LOCAL)) + self.assertEqual(499986152000 if not alice_channel.has_anchors() else 499980692000, alice_channel.available_to_spend(LOCAL)) self.assertEqual(500000000000, bob_channel.available_to_spend(LOCAL)) alice_channel.add_htlc(htlc) @@ -720,10 +750,10 @@ class TestChanReserve(ElectrumTestCase): paymentPreimage = b"\x01" * 32 paymentHash = bitcoin.sha256(paymentPreimage) htlc_dict = { - 'payment_hash' : paymentHash, - 'amount_msat' : int(.5 * one_bitcoin_in_msat), - 'cltv_abs' : 5, - 'timestamp' : 0, + 'payment_hash': paymentHash, + 'amount_msat': int(.5 * one_bitcoin_in_msat), + 'cltv_abs': 5, + 'timestamp': 0, } self.alice_channel.add_htlc(htlc_dict) self.bob_channel.receive_htlc(htlc_dict) @@ -759,9 +789,9 @@ class TestChanReserve(ElectrumTestCase): # Alice: 1.5 # Bob: 9.5 htlc_dict = { - 'payment_hash' : paymentHash, - 'amount_msat' : int(3.5 * one_bitcoin_in_msat), - 'cltv_abs' : 5, + 'payment_hash': paymentHash, + 'amount_msat': int(3.5 * one_bitcoin_in_msat), + 'cltv_abs': 5, } self.alice_channel.add_htlc(htlc_dict) self.bob_channel.receive_htlc(htlc_dict) @@ -783,10 +813,10 @@ class TestChanReserve(ElectrumTestCase): paymentPreimage = b"\x01" * 32 paymentHash = bitcoin.sha256(paymentPreimage) htlc_dict = { - 'payment_hash' : paymentHash, - 'amount_msat' : int(2 * one_bitcoin_in_msat), - 'cltv_abs' : 5, - 'timestamp' : 0, + 'payment_hash': paymentHash, + 'amount_msat': int(2 * one_bitcoin_in_msat), + 'cltv_abs': 5, + 'timestamp': 0, } alice_idx = self.alice_channel.add_htlc(htlc_dict).htlc_id bob_idx = self.bob_channel.receive_htlc(htlc_dict).htlc_id @@ -819,38 +849,61 @@ class TestChanReserve(ElectrumTestCase): class TestDust(ElectrumTestCase): def test_DustLimit(self): + """Test that addition of an HTLC below the dust limit changes the balances.""" alice_channel, bob_channel = create_test_channels() + dust_limit_alice = alice_channel.config[LOCAL].dust_limit_sat + dust_limit_bob = bob_channel.config[LOCAL].dust_limit_sat + self.assertLess(dust_limit_alice, dust_limit_bob) + bob_ctx = bob_channel.get_latest_commitment(LOCAL) + bobs_original_outputs = [x.value for x in bob_ctx.outputs()] paymentPreimage = b"\x01" * 32 paymentHash = bitcoin.sha256(paymentPreimage) fee_per_kw = alice_channel.get_next_feerate(LOCAL) - self.assertEqual(fee_per_kw, 6000) - htlcAmt = 500 + lnutil.HTLC_TIMEOUT_WEIGHT * (fee_per_kw // 1000) - self.assertEqual(htlcAmt, 4478) + success_weight = effective_htlc_tx_weight(success=True, has_anchors=TEST_ANCHOR_CHANNELS) + # we put a single sat less into the htlc than bob can afford + # to pay for his htlc success transaction + below_dust_for_bob = dust_limit_bob - 1 + htlc_amt = below_dust_for_bob + success_weight * (fee_per_kw // 1000) htlc = { - 'payment_hash' : paymentHash, - 'amount_msat' : 1000 * htlcAmt, - 'cltv_abs' : 5, # also in create_test_channels - 'timestamp' : 0, + 'payment_hash': paymentHash, + 'amount_msat': 1000 * htlc_amt, + 'cltv_abs': 5, # consistent with channel policy + 'timestamp': 0, } - old_values = [x.value for x in bob_channel.get_latest_commitment(LOCAL).outputs()] - aliceHtlcIndex = alice_channel.add_htlc(htlc).htlc_id - bobHtlcIndex = bob_channel.receive_htlc(htlc).htlc_id + # add the htlc + alice_htlc_id = alice_channel.add_htlc(htlc).htlc_id + bob_htlc_id = bob_channel.receive_htlc(htlc).htlc_id force_state_transition(alice_channel, bob_channel) alice_ctx = alice_channel.get_latest_commitment(LOCAL) bob_ctx = bob_channel.get_latest_commitment(LOCAL) - new_values = [x.value for x in bob_ctx.outputs()] - self.assertNotEqual(old_values, new_values) - self.assertEqual(len(alice_ctx.outputs()), 3) - self.assertEqual(len(bob_ctx.outputs()), 2) - default_fee = calc_static_fee(0) - self.assertEqual(bob_channel.get_next_fee(LOCAL), default_fee + htlcAmt) - bob_channel.settle_htlc(paymentPreimage, bobHtlcIndex) - alice_channel.receive_htlc_settle(paymentPreimage, aliceHtlcIndex) + bobs_second_outputs = [x.value for x in bob_ctx.outputs()] + self.assertNotEqual(bobs_original_outputs, bobs_second_outputs) + # the htlc appears as an output in alice's ctx, as she has a lower + # dust limit (also because her timeout tx costs less) + self.assertEqual(3, len(alice_ctx.outputs()) - (2 if TEST_ANCHOR_CHANNELS else 0)) + # htlc in bob's case goes to miner fees + self.assertEqual(2, len(bob_ctx.outputs()) - (2 if TEST_ANCHOR_CHANNELS else 0)) + self.assertEqual(htlc_amt, sum(bobs_original_outputs) - sum(bobs_second_outputs)) + empty_ctx_fee = lnutil.calc_fees_for_commitment_tx( + num_htlcs=0, feerate=fee_per_kw, is_local_initiator=True, + round_to_sat=True, has_anchors=TEST_ANCHOR_CHANNELS)[LOCAL] // 1000 + self.assertEqual(empty_ctx_fee + htlc_amt, bob_channel.get_next_fee(LOCAL)) + + bob_channel.settle_htlc(paymentPreimage, bob_htlc_id) + alice_channel.receive_htlc_settle(paymentPreimage, alice_htlc_id) force_state_transition(bob_channel, alice_channel) - self.assertEqual(len(alice_channel.get_next_commitment(LOCAL).outputs()), 2) - self.assertEqual(alice_channel.total_msat(SENT) // 1000, htlcAmt) + bob_ctx = bob_channel.get_latest_commitment(LOCAL) + bobs_third_outputs = [x.value for x in bob_ctx.outputs()] + # htlc is added back into the balance + self.assertEqual(sum(bobs_original_outputs), sum(bobs_third_outputs)) + # balance shifts in bob's direction after settlement + self.assertEqual(htlc_amt, bobs_third_outputs[1 + (2 if TEST_ANCHOR_CHANNELS else 0)] - bobs_original_outputs[1 + (2 if TEST_ANCHOR_CHANNELS else 0)]) + self.assertEqual(2, len(alice_channel.get_next_commitment(LOCAL).outputs()) - (2 if TEST_ANCHOR_CHANNELS else 0)) + self.assertEqual(2, len(bob_channel.get_next_commitment(LOCAL).outputs()) - (2 if TEST_ANCHOR_CHANNELS else 0)) + self.assertEqual(htlc_amt, alice_channel.total_msat(SENT) // 1000) + def force_state_transition(chanA, chanB): chanB.receive_new_commitment(*chanA.sign_next_commitment()) @@ -859,12 +912,3 @@ def force_state_transition(chanA, chanB): chanA.receive_revocation(rev) chanA.receive_new_commitment(bob_sig, bob_htlc_sigs) chanB.receive_revocation(chanA.revoke_current_commitment()) - -# calcStaticFee calculates appropriate fees for commitment transactions. This -# function provides a simple way to allow test balance assertions to take fee -# calculations into account. -def calc_static_fee(numHTLCs): - commitWeight = 724 - htlcWeight = 172 - feePerKw = 24//4 * 1000 - return feePerKw * (commitWeight + htlcWeight*numHTLCs) // 1000 diff --git a/tests/test_lnpeer.py b/tests/test_lnpeer.py index 30cb27cd5..06db6ba8e 100644 --- a/tests/test_lnpeer.py +++ b/tests/test_lnpeer.py @@ -45,9 +45,16 @@ from electrum.invoices import PR_PAID, PR_UNPAID from electrum.interface import GracefulDisconnect from electrum.simple_config import SimpleConfig -from .test_lnchannel import create_test_channels +from .test_lnchannel import create_test_channels as create_test_channels_anchors from . import ElectrumTestCase +TEST_ANCHOR_CHANNELS = True + + +def create_test_channels(*args, **kwargs): + return create_test_channels_anchors(*args, **kwargs, anchor_outputs=TEST_ANCHOR_CHANNELS) + + def keypair(): priv = ECPrivkey.generate_random_key().get_secret_bytes() k1 = Keypair( @@ -169,6 +176,7 @@ class MockLNWallet(Logger, EventListener, NetworkRetryManager[LNPeerAddr]): self.features |= LnFeatures.OPTION_TRAMPOLINE_ROUTING_OPT_ELECTRUM self.features |= LnFeatures.OPTION_CHANNEL_TYPE_OPT self.features |= LnFeatures.OPTION_SCID_ALIAS_OPT + self.features |= LnFeatures.OPTION_STATIC_REMOTEKEY_OPT self.pending_payments = defaultdict(asyncio.Future) for chan in chans: chan.lnworker = self @@ -387,7 +395,7 @@ low_fee_channel = { } depleted_channel = { - 'local_balance_msat': 0, + 'local_balance_msat': 330 * 1000, # local pays anchors 'remote_balance_msat': 10 * bitcoin.COIN * 1000, 'local_base_fee_msat': 1_000, 'local_fee_rate_millionths': 1,