From dac26c88e3976aa0816c10253ece4926795cab1a Mon Sep 17 00:00:00 2001 From: jp1ac4 <121959000+jp1ac4@users.noreply.github.com> Date: Thu, 22 Aug 2024 13:00:08 +0100 Subject: [PATCH] func test: allow to run using electrs backend --- tests/fixtures.py | 11 +++++ tests/test_chain.py | 6 +++ tests/test_framework/electrs.py | 72 +++++++++++++++++++++++++++++++++ tests/test_framework/utils.py | 10 +++++ tests/test_misc.py | 6 +++ tests/test_rpc.py | 3 ++ 6 files changed, 108 insertions(+) create mode 100644 tests/test_framework/electrs.py diff --git a/tests/fixtures.py b/tests/fixtures.py index fd42ab0c2..4c362c835 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -3,6 +3,7 @@ from bip380.descriptors import Descriptor from concurrent import futures from test_framework.bitcoind import Bitcoind +from test_framework.electrs import Electrs from test_framework.lianad import Lianad from test_framework.signer import SingleSigner, MultiSigner from test_framework.utils import ( @@ -126,6 +127,16 @@ def bitcoin_backend(directory, bitcoind): if BITCOIN_BACKEND_TYPE is BitcoinBackendType.Bitcoind: yield bitcoind bitcoind.cleanup() + elif BITCOIN_BACKEND_TYPE is BitcoinBackendType.Electrs: + electrs = Electrs( + electrs_dir=os.path.join(directory, "electrs"), + bitcoind_dir=bitcoind.bitcoin_dir, + bitcoind_rpcport=bitcoind.rpcport, + bitcoind_p2pport=bitcoind.p2pport, + ) + electrs.startup() + yield electrs + electrs.cleanup() else: raise NotImplementedError diff --git a/tests/test_chain.py b/tests/test_chain.py index 26487c999..68b531435 100644 --- a/tests/test_chain.py +++ b/tests/test_chain.py @@ -193,6 +193,9 @@ def test_reorg_status_recovery(lianad, bitcoind): assert new_coin_b == coin_b +@pytest.mark.skipif( + not BITCOIN_BACKEND_TYPE.has_rescan(), reason="Rescan not applicable." +) def test_rescan_edge_cases(lianad, bitcoind): """Test some specific cases that could arise when rescanning the chain.""" initial_tip = bitcoind.rpc.getblockheader(bitcoind.rpc.getbestblockhash()) @@ -329,6 +332,9 @@ def test_deposit_replacement(lianad, bitcoind): wait_for(lambda: len(lianad.rpc.listcoins()["coins"]) == 1) +@pytest.mark.skipif( + not BITCOIN_BACKEND_TYPE.has_rescan(), reason="Rescan not applicable." +) def test_rescan_and_recovery(lianad, bitcoind): """Test user recovery flow""" # Get initial_tip to use for rescan later diff --git a/tests/test_framework/electrs.py b/tests/test_framework/electrs.py new file mode 100644 index 000000000..58fac0cc1 --- /dev/null +++ b/tests/test_framework/electrs.py @@ -0,0 +1,72 @@ +import logging +import os + +from ephemeral_port_reserve import reserve +from test_framework.utils import BitcoinBackend, TailableProc, ELECTRS_PATH + + +class Electrs(BitcoinBackend): + def __init__( + self, + bitcoind_dir, + bitcoind_rpcport, + bitcoind_p2pport, + electrs_dir, + rpcport=None, + ): + TailableProc.__init__(self, electrs_dir, verbose=False) + + if rpcport is None: + rpcport = reserve() + + self.electrs_dir = electrs_dir + self.rpcport = rpcport + + regtestdir = os.path.join(electrs_dir, "regtest") + if not os.path.exists(regtestdir): + os.makedirs(regtestdir) + + self.cmd_line = [ + ELECTRS_PATH, + "--conf", + "{}/electrs.toml".format(regtestdir), + ] + electrs_conf = { + "daemon_dir": bitcoind_dir, + "cookie_file": os.path.join(bitcoind_dir, "regtest", ".cookie"), + "daemon_rpc_addr": f"127.0.0.1:{bitcoind_rpcport}", + "daemon_p2p_addr": f"127.0.0.1:{bitcoind_p2pport}", + "db_dir": electrs_dir, + "network": "regtest", + "electrum_rpc_addr": f"127.0.0.1:{self.rpcport}", + } + self.conf_file = os.path.join(regtestdir, "electrs.toml") + with open(self.conf_file, "w") as f: + for k, v in electrs_conf.items(): + f.write(f'{k} = "{v}"\n') + + def start(self): + TailableProc.start(self) + logging.info("Electrs started") + + def startup(self): + try: + self.start() + except Exception: + self.stop() + raise + + def stop(self): + return TailableProc.stop(self) + + def cleanup(self): + try: + self.stop() + except Exception: + self.proc.kill() + self.proc.wait() + + def append_to_lianad_conf(self, conf_file): + with open(conf_file, "a") as f: + f.write("[electrum_config]\n") + f.write(f"addr = '127.0.0.1:{self.rpcport}'\n") diff --git a/tests/test_framework/utils.py b/tests/test_framework/utils.py index 82b9d4552..c3fab464d 100644 --- a/tests/test_framework/utils.py +++ b/tests/test_framework/utils.py @@ -26,6 +26,14 @@ class BitcoinBackendType(str, enum.Enum): Bitcoind = "bitcoind" + Electrs = "electrs" + + def has_rescan(self): + if self is BitcoinBackendType.Bitcoind: + return True + if self is BitcoinBackendType.Electrs: + return False + raise NotImplementedError DEFAULT_BITCOIN_BACKEND_TYPE = "bitcoind" @@ -34,6 +42,8 @@ class BitcoinBackendType(str, enum.Enum): ) DEFAULT_BITCOIND_PATH = "bitcoind" BITCOIND_PATH = os.getenv("BITCOIND_PATH", DEFAULT_BITCOIND_PATH) +DEFAULT_ELECTRS_PATH = "electrs" +ELECTRS_PATH = os.getenv("ELECTRS_PATH", DEFAULT_ELECTRS_PATH) OLD_LIANAD_PATH = os.getenv("OLD_LIANAD_PATH", None) IS_NOT_BITCOIND_24 = bool(int(os.getenv("IS_NOT_BITCOIND_24", True))) USE_TAPROOT = bool( diff --git a/tests/test_misc.py b/tests/test_misc.py index fecad7c3d..b6759053b 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -7,6 +7,8 @@ from test_framework.authproxy import JSONRPCException from test_framework.serializations import PSBT from test_framework.utils import ( + BitcoinBackendType, + BITCOIN_BACKEND_TYPE, wait_for, RpcError, OLD_LIANAD_PATH, @@ -315,6 +317,10 @@ def bitcoind_wait_new_block(bitcoind): @pytest.mark.skipif( not IS_NOT_BITCOIND_24, reason="Need 'generateblock' with 'submit=False'" ) +# TODO: Should this test work for other backends? +@pytest.mark.skipif( + BITCOIN_BACKEND_TYPE is not BitcoinBackendType.Bitcoind, reason="FIXME" +) def test_retry_on_workqueue_exceeded(lianad, bitcoind, executor): """Make sure we retry requests to bitcoind if it is temporarily overloaded.""" # Start by reducing the work queue to a single slot. Note we need to stop lianad diff --git a/tests/test_rpc.py b/tests/test_rpc.py index cd697aae1..5fec6b1f3 100644 --- a/tests/test_rpc.py +++ b/tests/test_rpc.py @@ -605,6 +605,9 @@ def test_broadcast_spend(lianad, bitcoind): lianad.rpc.broadcastspend(txid) +@pytest.mark.skipif( + not BITCOIN_BACKEND_TYPE.has_rescan(), reason="Rescan not applicable." +) def test_start_rescan(lianad, bitcoind): """Test we successfully retrieve all our transactions after losing state by rescanning.""" initial_timestamp = int(time.time())