diff --git a/lnprototest/clightning/clightning.py b/lnprototest/clightning/clightning.py index edb53f1..5ad71f9 100644 --- a/lnprototest/clightning/clightning.py +++ b/lnprototest/clightning/clightning.py @@ -88,7 +88,7 @@ def __init__(self, config: Any): k, v = o.split("/") self.options[k] = v - def __init_sandbox_dir(self): + def __init_sandbox_dir(self) -> None: """Create the tmp directory for lnprotest and lightningd""" self.lightning_dir = os.path.join(self.directory, "lightningd") if not os.path.exists(self.lightning_dir): diff --git a/tests/helpers.py b/tests/helpers.py index 77d127e..c64f58d 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -1,3 +1,6 @@ +import logging +import traceback + import bitcoin.core import coincurve from typing import Tuple, Union, Sequence, List @@ -137,7 +140,7 @@ def pubkey_of(privkey: str) -> str: def gen_random_keyset(counter: int = 20) -> KeySet: - """Helper function to generate a random keyset with the possibility.""" + """Helper function to generate a random keyset.""" return KeySet( revocation_base_secret=f"{counter + 1}", payment_base_secret=f"{counter + 2}", @@ -147,6 +150,11 @@ def gen_random_keyset(counter: int = 20) -> KeySet: ) +def get_traceback(e): + lines = traceback.format_exception(type(e), e, e.__traceback__) + return "".join(lines) + + def run_runner(runner: Runner, test: Union[Sequence, List[Event], Event]) -> None: """ The pytest using the assertion as safe failure, and the exception it is only @@ -159,6 +167,7 @@ def run_runner(runner: Runner, test: Union[Sequence, List[Event], Event]) -> Non runner.run(test) except Exception as ex: runner.stop() + logging.error(get_traceback(ex)) assert False, ex diff --git a/tests/spec_helper.py b/tests/spec_helper.py index e8befc2..3b403db 100644 --- a/tests/spec_helper.py +++ b/tests/spec_helper.py @@ -15,33 +15,138 @@ Connect, Block, ExpectMsg, - RawMsg, Msg, Runner, - Funding, + regtest_hash, + remote_funding_pubkey, + remote_revocation_basepoint, + remote_payment_basepoint, + remote_delayed_payment_basepoint, + remote_htlc_basepoint, + remote_per_commitment_point, + remote_funding_privkey, + Commit, + Side, + msat, + CreateFunding, ) from helpers import ( utxo, + pubkey_of, + gen_random_keyset, + funding_amount_for_utxo, +) +from lnprototest.stash import ( + rcvd, + funding, + sent, + commitsig_to_recv, + channel_id, + commitsig_to_send, + funding_txid, + funding_tx, ) -def open_and_announce_channel_helper( - runner: Runner, tx_spendable: str, opts: dict +def connect_to_node_helper( + runner: Runner, + tx_spendable: str, + conn_privkey: str = "02", + global_features="", + features: str = "", ) -> List[Union[Block, Connect, ExpectMsg, TryAll]]: - funding, funding_tx = Funding.from_utxo( - *utxo(0), - local_node_privkey="02", - local_funding_privkey="10", - remote_node_privkey="03", - remote_funding_privkey="20" - ) - opts["funding"] = funding - opts["funding_tx"] = funding_tx + """Helper function to make a connection with the node""" return [ Block(blockheight=102, txs=[tx_spendable]), - Connect(connprivkey="03"), + Connect(connprivkey=conn_privkey), ExpectMsg("init"), - Msg("init", globalfeatures="", features=""), - Block(blockheight=103, number=6, txs=[funding_tx]), - RawMsg(funding.channel_announcement("103x1x0", "")), + Msg("init", globalfeatures=global_features, features=features), + ] + + +def open_and_announce_channel_helper( + runner: Runner, conn_privkey: str = "02", opts: dict = {} +) -> List[Union[Block, Connect, ExpectMsg, TryAll]]: + # Make up a channel between nodes 02 and 03, using bitcoin privkeys 10 and 20 + local_keyset = gen_random_keyset() + local_funding_privkey = "20" + + return [ + Msg( + "open_channel", + chain_hash=regtest_hash, + temporary_channel_id="00" * 32, + funding_satoshis=funding_amount_for_utxo(0), + push_msat=0, + dust_limit_satoshis=546, + max_htlc_value_in_flight_msat=4294967295, + channel_reserve_satoshis=9998, + htlc_minimum_msat=0, + feerate_per_kw=253, + # clightning uses to_self_delay=6; we use 5 to test differentiation + to_self_delay=5, + max_accepted_htlcs=483, + funding_pubkey=pubkey_of(local_funding_privkey), + revocation_basepoint=local_keyset.revocation_basepoint(), + payment_basepoint=local_keyset.payment_basepoint(), + delayed_payment_basepoint=local_keyset.delayed_payment_basepoint(), + htlc_basepoint=local_keyset.htlc_basepoint(), + first_per_commitment_point=local_keyset.per_commit_point(0), + channel_flags=1, + ), + ExpectMsg( + "accept_channel", + funding_pubkey=remote_funding_pubkey(), + revocation_basepoint=remote_revocation_basepoint(), + payment_basepoint=remote_payment_basepoint(), + delayed_payment_basepoint=remote_delayed_payment_basepoint(), + htlc_basepoint=remote_htlc_basepoint(), + first_per_commitment_point=remote_per_commitment_point(0), + minimum_depth=3, + channel_reserve_satoshis=9998, + ), + # Create and stash Funding object and FundingTx + CreateFunding( + *utxo(0), + local_node_privkey="02", + local_funding_privkey=local_funding_privkey, + remote_node_privkey=runner.get_node_privkey(), + remote_funding_privkey=remote_funding_privkey() + ), + Commit( + funding=funding(), + opener=Side.local, + local_keyset=local_keyset, + local_to_self_delay=rcvd("to_self_delay", int), + remote_to_self_delay=sent("to_self_delay", int), + local_amount=msat(sent("funding_satoshis", int)), + remote_amount=0, + local_dust_limit=546, + remote_dust_limit=546, + feerate=253, + local_features=sent("init.features"), + remote_features=rcvd("init.features"), + ), + Msg( + "funding_created", + temporary_channel_id=rcvd(), + funding_txid=funding_txid(), + funding_output_index=0, + signature=commitsig_to_send(), + ), + ExpectMsg( + "funding_signed", channel_id=channel_id(), signature=commitsig_to_recv() + ), + # Mine it and get it deep enough to confirm channel. + Block(blockheight=103, number=3, txs=[funding_tx()]), + ExpectMsg( + "funding_locked", + channel_id=channel_id(), + next_per_commitment_point=remote_per_commitment_point(1), + ), + Msg( + "funding_locked", + channel_id=channel_id(), + next_per_commitment_point=local_keyset.per_commit_point(1), + ), ] diff --git a/tests/test_bolt2-01-close_channel.py b/tests/test_bolt2-01-close_channel.py index 4e00e52..9b1bdcc 100644 --- a/tests/test_bolt2-01-close_channel.py +++ b/tests/test_bolt2-01-close_channel.py @@ -22,39 +22,64 @@ author: https://github.com/vincenzopalazzo """ +import hashlib +from bitcoin.core import CScript, Hash160 +from bitcoin.core.script import OP_0 +from bitcoin.wallet import CBitcoinSecret from lnprototest import ( ExpectMsg, Msg, Runner, + TryAll, ) -from helpers import run_runner, tx_spendable, merge_events_sequences -from spec_helper import open_and_announce_channel_helper +from helpers import run_runner, merge_events_sequences, tx_spendable +from lnprototest.stash import channel_id +from spec_helper import open_and_announce_channel_helper, connect_to_node_helper -def test_close_channel_shutdown_msg(runner: Runner) -> None: +def test_close_channel_shutdown_msg_normal_case(runner: Runner) -> None: """Close the channel with the other peer, and check if the shutdown message works in the expected way.""" # test preconditions. # the option that the helper method feel for us test_opts = {} + pre_events_conn = connect_to_node_helper( + runner, tx_spendable=tx_spendable, conn_privkey="03" + ) pre_events = open_and_announce_channel_helper( - runner=runner, tx_spendable=tx_spendable, opts=test_opts + runner, conn_privkey="03", opts=test_opts ) - funding = test_opts["funding"] + # merge the two events + pre_events = merge_events_sequences(pre_events_conn, pre_events) + channel_idx = channel_id() + + # TODO: refactoring this + h = hashlib.sha256(b"correct horse battery staple").digest() + seckey = CBitcoinSecret.from_secret_bytes(h) + # Create an address from that private key. + script = CScript([OP_0, Hash160(seckey.pub)]).hex() test = [ - Msg( - "shutdown", - channel_id=funding.channel_id(), - # TODO: The scriptpubkey should be a new one - # this should cause the failure - scriptpubkey="001473daa75958d5b2ddca87a6c279bb7cb307167037", + # runner sent shutdown message to the ln implementation + TryAll( + [ + Msg( + "shutdown", + channel_id=channel_idx, + scriptpubkey=script, + ), + # FIXME: it is possible to receive a sequence of channel_update + # this test os nondeterministic + TryAll([], ExpectMsg("channel_update")), + # Some times I receive a channel update, why? + ExpectMsg("shutdown", channel_id=channel_idx), + # FIXME: We should not be able to ping the node right? + ], + # ln implementation sent the shutdown message + # TODO: to be implemented. + [], ), - # FIXME: I received the message type 1, it should be a warning message, - # this should happen when there is a bad scriptpubkey - # BOLT2: https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#channel-close - ExpectMsg("shutdown", channel_id=funding.channel_id()), ] run_runner(runner, merge_events_sequences(pre=pre_events, post=test)) diff --git a/tests/test_bolt7-01-channel_announcement-success.py b/tests/test_bolt7-01-channel_announcement-success.py index d8efa2c..d7ce66b 100644 --- a/tests/test_bolt7-01-channel_announcement-success.py +++ b/tests/test_bolt7-01-channel_announcement-success.py @@ -34,7 +34,8 @@ def test_gossip(runner: Runner) -> None: Msg("init", globalfeatures="", features=""), Block(blockheight=103, number=6, txs=[funding_tx]), RawMsg(funding.channel_announcement("103x1x0", "")), - # New peer connects, asking for initial_routing_sync. We *won't* relay channel_announcement, as there is no channel_update. + # New peer connects, asking for initial_routing_sync. We *won't* relay channel_announcement, as there is no + # channel_update. Connect(connprivkey="05"), ExpectMsg("init"), Msg("init", globalfeatures="", features="08"), diff --git a/tests/test_bolt7-10-gossip-filter.py b/tests/test_bolt7-10-gossip-filter.py index 8088ebd..8ffcb57 100644 --- a/tests/test_bolt7-10-gossip-filter.py +++ b/tests/test_bolt7-10-gossip-filter.py @@ -55,7 +55,8 @@ def test_gossip_timestamp_filter(runner: Runner) -> None: Side.local, "", (1, 2, 3), "foobar", b"", timestamp1 ) ), - # New peer connects, asks for gossip_timestamp_filter=all. We *won't* relay channel_announcement, as there is no channel_update. + # New peer connects, asks for gossip_timestamp_filter=all. We *won't* relay channel_announcement, + # as there is no channel_update. Connect(connprivkey="05"), ExpectMsg("init"), # BOLT #9: