diff --git a/lnprototest/backend/__init__.py b/lnprototest/backend/__init__.py new file mode 100644 index 0000000..5fab85c --- /dev/null +++ b/lnprototest/backend/__init__.py @@ -0,0 +1,7 @@ +from .backend import Backend +from .bitcoind import Bitcoind + +__all__ = [ + "Backend", + "Bitcoind" +] diff --git a/lnprototest/backend/backend.py b/lnprototest/backend/backend.py new file mode 100644 index 0000000..8c443b6 --- /dev/null +++ b/lnprototest/backend/backend.py @@ -0,0 +1,25 @@ +#! /usr/bin/python3 +# This script exercises the backend implementation + +# Released by Rusty Russell under CC0: +# https://creativecommons.org/publicdomain/zero/1.0/ +from abc import ABC, abstractmethod + +class Backend(ABC): + """ + Generic implementation of Bitcoin backend + This is useful when the LN node use different type + of bitcoin backend. + """ + + @abstractmethod + def start(self) -> None: + pass + + @abstractmethod + def stop(self) -> None: + pass + + @abstractmethod + def restart(self) -> None: + pass diff --git a/lnprototest/backend/bitcoind.py b/lnprototest/backend/bitcoind.py new file mode 100644 index 0000000..1b350ad --- /dev/null +++ b/lnprototest/backend/bitcoind.py @@ -0,0 +1,85 @@ +#! /usr/bin/python3 +# This script exercises the c-lightning implementation + +# Released by Rusty Russell under CC0: +# https://creativecommons.org/publicdomain/zero/1.0/ + +import os +import shutil +import subprocess +import logging + +from ephemeral_port_reserve import reserve +from pyln.testing.utils import wait_for, SimpleBitcoinProxy +from .backend import Backend + + +class Bitcoind(Backend): + """Starts regtest bitcoind on an ephemeral port, and returns the RPC proxy""" + def __init__(self, basedir: str): + self.bitcoin_dir = os.path.join(basedir, "bitcoind") + if not os.path.exists(self.bitcoin_dir): + os.makedirs(self.bitcoin_dir) + self.bitcoin_conf = os.path.join(self.bitcoin_dir, 'bitcoin.conf') + self.cmd_line = [ + 'bitcoind', + '-datadir={}'.format(self.bitcoin_dir), + '-server', + '-regtest', + '-logtimestamps', + '-nolisten'] + self.port = reserve() + self.btc_version = None + print("Port is {}, dir is {}".format(self.port, self.bitcoin_dir)) + # For after 0.16.1 (eg. 3f398d7a17f136cd4a67998406ca41a124ae2966), this + # needs its own [regtest] section. + with open(self.bitcoin_conf, 'w') as f: + f.write("regtest=1\n") + f.write("rpcuser=rpcuser\n") + f.write("rpcpassword=rpcpass\n") + f.write("[regtest]\n") + f.write("rpcport={}\n".format(self.port)) + self.rpc = SimpleBitcoinProxy(btc_conf_file=self.bitcoin_conf) + + def version_compatibility(self): + """ + This method try to manage the compatibility between + different version of Bitcoin Core implementation. + + This method could be useful sometimes when is necessary + run the test with different version of Bitcoin core. + """ + if self.rpc is None: + # Sanity check + raise Error("bitcoind not initialized") + + self.btc_version = self.rpc.getnetworkinfo()['version'] + logging.info("Bitcoin Core version {}".format(self.btc_version)) + if self.btc_version >= 210000: + # Maintains the compatibility between wallet + # different ln implementation can use the main wallet (?) + self.rpc.createwallet("main") # Automatically loads + + + def start(self) -> None: + self.proc = subprocess.Popen(self.cmd_line, stdout=subprocess.PIPE) + + # Wait for it to startup. + while b'Done loading' not in self.proc.stdout.readline(): + pass + + self.version_compatibility() + # Block #1. + self.rpc.submitblock('0000002006226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f69d715fba6edece89b2dee71f4fed52c7accd6cd62c328536e6233b72b14c5f5c8ba465fffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03510101ffffffff0200f2052a0100000016001419c70534cd905244cff88a594f0c16d4bbedc5e60000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000') + self.rpc.generatetoaddress(100, self.rpc.getnewaddress()) + + def stop(self) -> None: + self.proc.kill() + + def restart(self) -> None: + # Only restart if we have to. + if self.rpc.getblockcount() != 102 or self.rpc.getrawmempool() == []: + self.stop() + shutil.rmtree(os.path.join(self.bitcoin_dir, 'regtest')) + self.start() + diff --git a/lnprototest/clightning/clightning.py b/lnprototest/clightning/clightning.py index d9b2434..512a238 100644 --- a/lnprototest/clightning/clightning.py +++ b/lnprototest/clightning/clightning.py @@ -8,7 +8,6 @@ import pyln.client import pyln.proto.wire import os -import shutil import subprocess import tempfile import lnprototest @@ -17,67 +16,15 @@ from concurrent import futures from ephemeral_port_reserve import reserve -from pyln.testing.utils import wait_for, SimpleBitcoinProxy +from lnprototest.backend import Bitcoind from lnprototest import Event, EventError, SpecFileError, KeySet, Conn, namespace, MustNotMsg from typing import Dict, Any, Callable, List, Optional, cast +from pyln.testing.utils import wait_for TIMEOUT = int(os.getenv("TIMEOUT", "30")) LIGHTNING_SRC = os.path.join(os.getcwd(), os.getenv("LIGHTNING_SRC", '../lightning/')) -class Bitcoind(object): - """Starts regtest bitcoind on an ephemeral port, and returns the RPC proxy""" - def __init__(self, basedir: str): - self.bitcoin_dir = os.path.join(basedir, "bitcoind") - if not os.path.exists(self.bitcoin_dir): - os.makedirs(self.bitcoin_dir) - self.bitcoin_conf = os.path.join(self.bitcoin_dir, 'bitcoin.conf') - self.cmd_line = [ - 'bitcoind', - '-datadir={}'.format(self.bitcoin_dir), - '-server', - '-regtest', - '-logtimestamps', - '-nolisten'] - self.port = reserve() - print("Port is {}, dir is {}".format(self.port, self.bitcoin_dir)) - # For after 0.16.1 (eg. 3f398d7a17f136cd4a67998406ca41a124ae2966), this - # needs its own [regtest] section. - with open(self.bitcoin_conf, 'w') as f: - f.write("regtest=1\n") - f.write("rpcuser=rpcuser\n") - f.write("rpcpassword=rpcpass\n") - f.write("[regtest]\n") - f.write("rpcport={}\n".format(self.port)) - self.rpc = SimpleBitcoinProxy(btc_conf_file=self.bitcoin_conf) - - def start(self) -> None: - self.proc = subprocess.Popen(self.cmd_line, stdout=subprocess.PIPE) - - # Wait for it to startup. - while b'Done loading' not in self.proc.stdout.readline(): - pass - - if 'lnprototest' not in self.rpc.listwallets(): - self.rpc.createwallet("lnprototest") # Automatically loads - else: - self.rpc.loadwallet("lnprototest") - - # Block #1. - self.rpc.submitblock('0000002006226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f69d715fba6edece89b2dee71f4fed52c7accd6cd62c328536e6233b72b14c5f5c8ba465fffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03510101ffffffff0200f2052a0100000016001419c70534cd905244cff88a594f0c16d4bbedc5e60000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000') - self.rpc.generatetoaddress(100, self.rpc.getnewaddress()) - - def stop(self) -> None: - self.proc.kill() - - def restart(self) -> None: - # Only restart if we have to. - if self.rpc.getblockcount() != 102 or self.rpc.getrawmempool() == []: - self.stop() - shutil.rmtree(os.path.join(self.bitcoin_dir, 'regtest')) - self.start() - - class CLightningConn(lnprototest.Conn): def __init__(self, connprivkey: str, port: int): super().__init__(connprivkey)