func test: allow to run using electrs backend

This commit is contained in:
jp1ac4 2024-08-22 13:00:08 +01:00 committed by Michael Mallan
parent a85d4887e9
commit 371e31e3f3
No known key found for this signature in database
GPG Key ID: 5177CDCEDB0EABEB
7 changed files with 121 additions and 8 deletions

View File

@ -3,6 +3,7 @@ from bip32.utils import _pubkey_to_fingerprint
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

View File

@ -238,7 +238,8 @@ def test_rescan_edge_cases(lianad, bitcoind):
outpoints_before = set(c["outpoint"] for c in coins_before)
bitcoind.generate_block(1)
lianad.restart_fresh(bitcoind)
assert len(list_coins()) == 0
if BITCOIN_BACKEND_TYPE is BitcoinBackendType.Bitcoind:
assert len(list_coins()) == 0
# We can be stopped while we are rescanning
lianad.rpc.startrescan(initial_tip["time"])
@ -252,7 +253,8 @@ def test_rescan_edge_cases(lianad, bitcoind):
bitcoind.generate_block(1)
lianad.restart_fresh(bitcoind)
wait_for(lambda: lianad.rpc.getinfo()["rescan_progress"] is None)
assert len(list_coins()) == 0
if BITCOIN_BACKEND_TYPE is BitcoinBackendType.Bitcoind:
assert len(list_coins()) == 0
# There can be a reorg when we start rescanning
reorg_shift(initial_tip["height"], txs)
@ -271,7 +273,8 @@ def test_rescan_edge_cases(lianad, bitcoind):
lianad.restart_fresh(bitcoind)
wait_synced()
wait_for(lambda: lianad.rpc.getinfo()["rescan_progress"] is None)
assert len(list_coins()) == 0
if BITCOIN_BACKEND_TYPE is BitcoinBackendType.Bitcoind:
assert len(list_coins()) == 0
# We can be rescanning when a reorg happens
lianad.rpc.startrescan(initial_tip["time"])
@ -350,7 +353,8 @@ def test_rescan_and_recovery(lianad, bitcoind):
# Clear lianad state
lianad.restart_fresh(bitcoind)
assert len(lianad.rpc.listcoins()["coins"]) == 0
if BITCOIN_BACKEND_TYPE is BitcoinBackendType.Bitcoind:
assert len(lianad.rpc.listcoins()["coins"]) == 0
# Start rescan
lianad.rpc.startrescan(initial_tip["time"])

View File

@ -0,0 +1,79 @@
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()
# Prometheus metrics can't be deactivated in Electrs. Configure the port so it doesn't
# conflict with other instances when running tests in parallel.
monitoring_port = 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}",
"monitoring_addr": f"127.0.0.1:{monitoring_port}",
}
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')
self.env = {"RUST_LOG": "DEBUG"}
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")

View File

@ -5,6 +5,8 @@ import shutil
from bip380.descriptors import Descriptor
from bip380.miniscript import SatisfactionMaterial
from test_framework.utils import (
BITCOIN_BACKEND_TYPE,
BitcoinBackendType,
UnixDomainSocketRpc,
TailableProc,
VERBOSE,
@ -43,6 +45,7 @@ class Lianad(TailableProc):
self.cmd_line = [LIANAD_PATH, "--conf", f"{self.conf_file}"]
socket_path = os.path.join(os.path.join(datadir, "regtest"), "lianad_rpc")
self.rpc = UnixDomainSocketRpc(socket_path)
self.bitcoin_backend = bitcoin_backend
with open(self.conf_file, "w") as f:
f.write(f"data_dir = '{datadir}'\n")
@ -103,8 +106,9 @@ class Lianad(TailableProc):
self.stop()
dir_path = os.path.join(self.datadir, "regtest")
shutil.rmtree(dir_path)
wallet_path = os.path.join(dir_path, "lianad_watchonly_wallet")
bitcoind.node_rpc.unloadwallet(wallet_path)
if BITCOIN_BACKEND_TYPE is BitcoinBackendType.Bitcoind:
wallet_path = os.path.join(dir_path, "lianad_watchonly_wallet")
bitcoind.node_rpc.unloadwallet(wallet_path)
self.start()
wait_for(
lambda: self.rpc.getinfo()["block_height"] == bitcoind.rpc.getblockcount()

View File

@ -26,6 +26,7 @@ LIANAD_PATH = os.getenv("LIANAD_PATH", DEFAULT_MS_PATH)
class BitcoinBackendType(str, enum.Enum):
Bitcoind = "bitcoind"
Electrs = "electrs"
DEFAULT_BITCOIN_BACKEND_TYPE = "bitcoind"
@ -34,6 +35,8 @@ BITCOIN_BACKEND_TYPE = BitcoinBackendType(
)
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(

View File

@ -7,6 +7,8 @@ from fixtures import *
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,
@ -260,6 +262,10 @@ def test_coinbase_deposit(lianad, bitcoind):
OLD_LIANAD_PATH is None or USE_TAPROOT,
reason="Need the old lianad binary to create the datadir.",
)
@pytest.mark.skipif(
BITCOIN_BACKEND_TYPE is not BitcoinBackendType.Bitcoind,
reason="Only bitcoind backend was available for older lianad versions.",
)
def test_migration(lianad_multisig, bitcoind):
"""Test we can start a newer lianad on a datadir created by an older lianad."""
lianad = lianad_multisig
@ -315,6 +321,10 @@ def bitcoind_wait_new_block(bitcoind):
@pytest.mark.skipif(
not IS_NOT_BITCOIND_24, reason="Need 'generateblock' with 'submit=False'"
)
@pytest.mark.skipif(
BITCOIN_BACKEND_TYPE is not BitcoinBackendType.Bitcoind,
reason="Tests the retry logic specific to the bitcoind backend.",
)
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

View File

@ -686,11 +686,13 @@ def test_start_rescan(lianad, bitcoind):
# descriptor.
coins_before = sorted_coins()
lianad.restart_fresh(bitcoind)
assert len(list_coins()) == 0
if BITCOIN_BACKEND_TYPE is BitcoinBackendType.Bitcoind:
assert len(list_coins()) == 0
# The wallet isn't aware what derivation indexes were used. Necessarily it'll start
# from 0.
assert lianad.rpc.getnewaddress() == first_address
if BITCOIN_BACKEND_TYPE is BitcoinBackendType.Bitcoind:
assert lianad.rpc.getnewaddress() == first_address
# Once the rescan is done, we must have detected all previous transactions.
lianad.rpc.startrescan(initial_timestamp)