Skip to content

Commit

Permalink
tests: tests for both anchors and old ctx types
Browse files Browse the repository at this point in the history
* in test_lnutil, patch htlc weight to pass original anchor commitment
  test vectors
* activate tests for both commitment types
  • Loading branch information
bitromortac committed Nov 10, 2021
1 parent ecd035b commit 20b9e98
Show file tree
Hide file tree
Showing 6 changed files with 178 additions and 139 deletions.
1 change: 1 addition & 0 deletions electrum/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ def tearDown(self):

class ElectrumTestCase(SequentialTestCase):
"""Base class for our unit tests."""
TEST_ANCHOR_CHANNELS = False

def setUp(self):
super().setUp()
Expand Down
22 changes: 18 additions & 4 deletions electrum/tests/regtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,17 @@
import unittest
import subprocess

class TestLightning(unittest.TestCase):

@staticmethod
def run_shell(args, timeout=30):
process = subprocess.Popen(['electrum/tests/regtest/regtest.sh'] + args, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, universal_newlines=True)
class TestLightning(unittest.TestCase):
TEST_ANCHOR_CHANNELS = False

def run_shell(self, args, timeout=30):
process = subprocess.Popen(
['electrum/tests/regtest/regtest.sh'] + args,
stderr=subprocess.STDOUT, stdout=subprocess.PIPE,
universal_newlines=True,
env=os.environ.update({'TEST_ANCHOR_CHANNELS': str(self.TEST_ANCHOR_CHANNELS)}),
)
for line in iter(process.stdout.readline, ''):
sys.stdout.write(line)
sys.stdout.flush()
Expand Down Expand Up @@ -63,8 +69,16 @@ def test_breach_with_spent_htlc(self):
self.run_shell(['breach_with_spent_htlc'])


class TestLightningABAnchors(TestLightningAB):
TEST_ANCHOR_CHANNELS = True


class TestLightningABC(TestLightning):
agents = ['alice', 'bob', 'carol']

def test_watchtower(self):
self.run_shell(['watchtower'])


class TestLightningABCAnchors(TestLightningABC):
TEST_ANCHOR_CHANNELS = True
2 changes: 1 addition & 1 deletion electrum/tests/regtest/regtest.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
export HOME=~
set -eu

TEST_ANCHOR_CHANNELS=False
# alice -> bob -> carol

alice="./run_electrum --regtest -D /tmp/alice"
Expand Down Expand Up @@ -73,6 +72,7 @@ if [[ $1 == "new_block" ]]; then
fi

if [[ $1 == "init" ]]; then
echo "testing anchor channels: $TEST_ANCHOR_CHANNELS"
echo "initializing $2"
rm -rf /tmp/$2/
agent="./run_electrum --regtest -D /tmp/$2"
Expand Down
53 changes: 34 additions & 19 deletions electrum/tests/test_lnchannel.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,13 @@

from . import ElectrumTestCase

TEST_ANCHOR_CHANNELS = False

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, anchor_outputs=TEST_ANCHOR_CHANNELS):
r_csv, anchor_outputs):
assert local_amount > 0
assert remote_amount > 0
channel_id, _ = lnpeer.channel_id_from_funding_tx(funding_txid, funding_index)
Expand Down Expand Up @@ -122,7 +120,7 @@ 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,
anchor_outputs=TEST_ANCHOR_CHANNELS):
anchor_outputs=False):
if random_seed is None: # needed for deterministic randomness
random_seed = os.urandom(32)
random_gen = PRNG(random_seed)
Expand Down Expand Up @@ -208,10 +206,11 @@ class TestFee(ElectrumTestCase):
def test_fee(self):
alice_channel, bob_channel = create_test_channels(feerate=253,
local_msat=10000000000,
remote_msat=5000000000, anchor_outputs=TEST_ANCHOR_CHANNELS)
expected_value = 9999056 if TEST_ANCHOR_CHANNELS else 9999817
remote_msat=5000000000, anchor_outputs=self.TEST_ANCHOR_CHANNELS)
expected_value = 9999056 if self.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

Expand All @@ -223,7 +222,7 @@ def assertOutputExistsByValue(self, tx, amt_sat):
self.assertFalse()

def assertNumberNonAnchorOutputs(self, number, tx):
self.assertEqual(number, len(tx.outputs()) - (2 if TEST_ANCHOR_CHANNELS else 0))
self.assertEqual(number, len(tx.outputs()) - (2 if self.TEST_ANCHOR_CHANNELS else 0))

@classmethod
def setUpClass(cls):
Expand All @@ -235,7 +234,7 @@ def setUp(self):
# 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(anchor_outputs=TEST_ANCHOR_CHANNELS)
self.alice_channel, self.bob_channel = create_test_channels(anchor_outputs=self.TEST_ANCHOR_CHANNELS)

self.paymentPreimage = b"\x01" * 32
paymentHash = bitcoin.sha256(self.paymentPreimage)
Expand Down Expand Up @@ -558,7 +557,6 @@ def test_SimpleAddSettleWorkflow(self):
self.assertEqual(bob_channel.total_msat(RECEIVED), one_bitcoin_in_msat, "bob satoshis received incorrect")
self.assertEqual(bob_channel.total_msat(SENT), 5 * one_bitcoin_in_msat, "bob satoshis sent incorrect")


def alice_to_bob_fee_update(self, fee=1111):
aoldctx = self.alice_channel.get_next_commitment(REMOTE).outputs()
self.alice_channel.update_fee(fee, True)
Expand Down Expand Up @@ -662,9 +660,13 @@ def test_AddHTLCNegativeBalance(self):
self.assertIn('Not enough local balance', cm.exception.args[0])


class TestChannelAnchors(TestChannel):
TEST_ANCHOR_CHANNELS = True


class TestAvailableToSpend(ElectrumTestCase):
def test_DesyncHTLCs(self):
alice_channel, bob_channel = create_test_channels()
alice_channel, bob_channel = create_test_channels(anchor_outputs=self.TEST_ANCHOR_CHANNELS)
self.assertEqual(499986152000 if not alice_channel.has_anchors() else 499981351340, alice_channel.available_to_spend(LOCAL))
self.assertEqual(500000000000, bob_channel.available_to_spend(LOCAL))

Expand Down Expand Up @@ -710,9 +712,13 @@ def test_DesyncHTLCs(self):
alice_channel.add_htlc(htlc_dict)


class TestAvailableToSpendAnchors(TestAvailableToSpend):
TEST_ANCHOR_CHANNELS = True


class TestChanReserve(ElectrumTestCase):
def setUp(self):
alice_channel, bob_channel = create_test_channels()
alice_channel, bob_channel = create_test_channels(anchor_outputs=False)
alice_min_reserve = int(.5 * one_bitcoin_in_msat // 1000)
# We set Bob's channel reserve to a value that is larger than
# his current balance in the channel. This will ensure that
Expand Down Expand Up @@ -839,10 +845,15 @@ def check_bals(self, amt1, amt2):
self.assertEqual(self.alice_channel.available_to_spend(REMOTE), amt2)
self.assertEqual(self.bob_channel.available_to_spend(LOCAL), amt2)


class TestChanReserveAnchors(TestChanReserve):
TEST_ANCHOR_CHANNELS = True


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()
alice_channel, bob_channel = create_test_channels(anchor_outputs=self.TEST_ANCHOR_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)
Expand All @@ -852,7 +863,7 @@ def test_DustLimit(self):
paymentPreimage = b"\x01" * 32
paymentHash = bitcoin.sha256(paymentPreimage)
fee_per_kw = alice_channel.get_next_feerate(LOCAL)
success_weight = effective_htlc_tx_weight(success=True, has_anchors=TEST_ANCHOR_CHANNELS)
success_weight = effective_htlc_tx_weight(success=True, has_anchors=self.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
Expand All @@ -874,13 +885,13 @@ def test_DustLimit(self):
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))
self.assertEqual(3, len(alice_ctx.outputs()) - (2 if self.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(2, len(bob_ctx.outputs()) - (2 if self.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
round_to_sat=True, has_anchors=self.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)
Expand All @@ -891,12 +902,16 @@ def test_DustLimit(self):
# 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, bobs_third_outputs[1 + (2 if self.TEST_ANCHOR_CHANNELS else 0)] - bobs_original_outputs[1 + (2 if self.TEST_ANCHOR_CHANNELS else 0)])
self.assertEqual(2, len(alice_channel.get_next_commitment(LOCAL).outputs()) - (2 if self.TEST_ANCHOR_CHANNELS else 0))
self.assertEqual(2, len(bob_channel.get_next_commitment(LOCAL).outputs()) - (2 if self.TEST_ANCHOR_CHANNELS else 0))
self.assertEqual(htlc_amt, alice_channel.total_msat(SENT) // 1000)


class TestDustAnchors(TestDust):
TEST_ANCHOR_CHANNELS = True


def force_state_transition(chanA, chanB):
chanB.receive_new_commitment(*chanA.sign_next_commitment())
rev = chanB.revoke_current_commitment()
Expand Down
Loading

0 comments on commit 20b9e98

Please sign in to comment.