From 9db3e934d9330b1ec0b49ee50af572673da2fae0 Mon Sep 17 00:00:00 2001
From: Matt Whitlock <c-lightning@mattwhitlock.name>
Date: Tue, 26 Aug 2025 03:03:18 -0400
Subject: [PATCH] tests: allow python-bitcointx as alternative to
 python-bitcoinlib

python-bitcointx is a fork of python-bitcoinlib that slims down the API
but still retains the functionality that we need. It is a near drop-in
alternative for our purposes, so we can trivially support it.

Notably, the JoinMarket project has been relying on python-bitcointx, so
evidently it has withstood a trial by fire.

See: https://gist.github.com/dgpv/6607c7d0eff66c387d8a5eaeb378e787
---
 contrib/pyln-testing/pyln/testing/btcproxy.py | 12 +++++++++---
 contrib/pyln-testing/pyln/testing/utils.py    | 18 +++++++++++++-----
 tests/test_closing.py                         |  5 ++++-
 tests/test_misc.py                            | 15 ++++++++++++---
 tests/test_plugin.py                          | 11 +++++++++--
 tests/test_wallet.py                          |  5 ++++-
 6 files changed, 51 insertions(+), 15 deletions(-)

diff --git a/contrib/pyln-testing/pyln/testing/btcproxy.py b/contrib/pyln-testing/pyln/testing/btcproxy.py
index fc641ace9662970e597a76f5e91e93bfc9a5b851..65b1ba3838bd99c7d6d701cdb6336082b0c8ed3c 100644
--- a/contrib/pyln-testing/pyln/testing/btcproxy.py
+++ b/contrib/pyln-testing/pyln/testing/btcproxy.py
@@ -1,8 +1,12 @@
 """ A bitcoind proxy that allows instrumentation and canned responses
 """
 from flask import Flask, request  # type: ignore
-from bitcoin.rpc import JSONRPCError  # type: ignore
-from bitcoin.rpc import RawProxy as BitcoinProxy  # type: ignore
+try:
+    from bitcoin.rpc import RawProxy as BitcoinProxy, JSONRPCError  # type: ignore
+    _using_bitcoinlib = 'bitcoinlib'
+except ModuleNotFoundError:
+    from bitcointx.rpc import RPCCaller as BitcoinProxy, JSONRPCError  # type: ignore
+    _using_bitcoinlib = 'bitcointx'
 from cheroot.wsgi import Server  # type: ignore
 from cheroot.wsgi import PathInfoDispatcher  # type: ignore
 
@@ -35,7 +39,9 @@ class BitcoinRpcProxy(object):
         self.request_count = 0
 
     def _handle_request(self, r):
-        brpc = BitcoinProxy(btc_conf_file=self.bitcoind.conf_file)
+        brpc = BitcoinProxy(**{'btc_conf_file' if _using_bitcoinlib == 'bitcoinlib'
+                               else 'conf_file' if _using_bitcoinlib == 'bitcointx'
+                               else None: self.bitcoind.conf_file})
         method = r['method']
 
         # If we have set a mock for this method reply with that instead of
diff --git a/contrib/pyln-testing/pyln/testing/utils.py b/contrib/pyln-testing/pyln/testing/utils.py
index 245f9c6936b3eedf9643dbf5d432594e7647bb1d..0eb32615f3b6ac25dd7645b3b24ddbf7196b7c8f 100644
--- a/contrib/pyln-testing/pyln/testing/utils.py
+++ b/contrib/pyln-testing/pyln/testing/utils.py
@@ -1,6 +1,12 @@
-from bitcoin.core import COIN  # type: ignore
-from bitcoin.rpc import RawProxy as BitcoinProxy  # type: ignore
-from bitcoin.rpc import JSONRPCError
+try:
+    from bitcoin.core import COIN  # type: ignore
+    from bitcoin.rpc import RawProxy as BitcoinProxy, JSONRPCError  # type: ignore
+    _using_bitcoinlib = 'bitcoinlib'
+except ModuleNotFoundError:
+    from bitcointx.core import CoreCoinParams  # type: ignore
+    COIN = CoreCoinParams.COIN
+    from bitcointx.rpc import RPCCaller as BitcoinProxy, JSONRPCError  # type: ignore
+    _using_bitcoinlib = 'bitcointx'
 from contextlib import contextmanager
 from pathlib import Path
 from pyln.client import RpcError
@@ -564,8 +570,10 @@ class SimpleBitcoinProxy:
             raise AttributeError
 
         # Create a callable to do the actual call
-        proxy = BitcoinProxy(btc_conf_file=self.__btc_conf_file__,
-                             timeout=self.__timeout__)
+        proxy = BitcoinProxy(**{'btc_conf_file' if _using_bitcoinlib == 'bitcoinlib'
+                                else 'conf_file' if _using_bitcoinlib == 'bitcointx'
+                                else None: self.__btc_conf_file__,
+                                'timeout': self.__timeout__})
 
         def f(*args):
             logging.debug("Calling {name} with arguments {args}".format(
diff --git a/tests/test_closing.py b/tests/test_closing.py
index 97690f53447b2a49f4b75dc12ac7acfc12a1c0ee..6351ff5e4b4f32438467583f4ff6b2b70281a4ab 100644
--- a/tests/test_closing.py
+++ b/tests/test_closing.py
@@ -13,7 +13,10 @@ from utils import (
 
 from typing import List, Optional
 
-import bitcoin
+try:
+    import bitcoin
+except ModuleNotFoundError:
+    import bitcointx as bitcoin
 import os
 import queue
 import pytest
diff --git a/tests/test_misc.py b/tests/test_misc.py
index a87f956cdbb5b52c9d35af6b783d98222488ced8..eb9d6a9906c3d880b67bec0560acc9301037618e 100644
--- a/tests/test_misc.py
+++ b/tests/test_misc.py
@@ -1,5 +1,10 @@
 import base64
-from bitcoin.rpc import RawProxy
+try:
+    from bitcoin.rpc import RawProxy as BitcoinProxy
+    _using_bitcoinlib = 'bitcoinlib'
+except ModuleNotFoundError:
+    from bitcointx.rpc import RPCCaller as BitcoinProxy
+    _using_bitcoinlib = 'bitcointx'
 from decimal import Decimal
 from fixtures import *  # noqa: F401,F403
 from fixtures import LightningNode, TEST_NETWORK
@@ -174,7 +179,9 @@ def test_bitcoin_pruned(node_factory, bitcoind):
         nonlocal fetched_peerblock
         if fetched_peerblock:
             fetched_peerblock = False
-            brpc = RawProxy(btc_conf_file=bitcoind.conf_file)
+            brpc = BitcoinProxy(**{'btc_conf_file' if _using_bitcoinlib == 'bitcoinlib'
+                                   else 'conf_file' if _using_bitcoinlib == 'bitcointx'
+                                   else None: bitcoind.conf_file})
             return {
                 "result": brpc._call(r["method"], *r["params"]),
                 "error": None,
@@ -269,7 +276,9 @@ def test_lightningd_still_loading(node_factory, bitcoind, executor):
 
     # This is slow enough that we're going to notice.
     def mock_getblock(r):
-        brpc = RawProxy(btc_conf_file=bitcoind.conf_file)
+        brpc = BitcoinProxy(**{'btc_conf_file' if _using_bitcoinlib == 'bitcoinlib'
+                               else 'conf_file' if _using_bitcoinlib == 'bitcointx'
+                               else None: bitcoind.conf_file})
         if r['params'][0] == slow_blockid:
             mock_release.wait(TIMEOUT)
         return {
diff --git a/tests/test_plugin.py b/tests/test_plugin.py
index 31c253c134d4e15e2a9b76021887c31d142161ba..6d7e92ccd2c69fc672e1516ad19ed8136393658e 100644
--- a/tests/test_plugin.py
+++ b/tests/test_plugin.py
@@ -1,4 +1,9 @@
-from bitcoin.rpc import RawProxy
+try:
+    from bitcoin.rpc import RawProxy as BitcoinProxy
+    _using_bitcoinlib = 'bitcoinlib'
+except ModuleNotFoundError:
+    from bitcointx.rpc import RPCCaller as BitcoinProxy
+    _using_bitcoinlib = 'bitcointx'
 from collections import OrderedDict
 from datetime import datetime
 from fixtures import *  # noqa: F401,F403
@@ -2281,7 +2286,9 @@ def test_bcli_concurrent(node_factory, bitcoind, executor):
 
     def mock_getblock(r):
         if getblockfrompeer_count >= retry_count:
-            brpc = RawProxy(btc_conf_file=bitcoind.conf_file)
+            brpc = BitcoinProxy(**{'btc_conf_file' if _using_bitcoinlib == 'bitcoinlib'
+                                   else 'conf_file' if _using_bitcoinlib == 'bitcointx'
+                                   else None: bitcoind.conf_file})
             return {
                 "result": brpc._call(r["method"], *r["params"]),
                 "error": None,
diff --git a/tests/test_wallet.py b/tests/test_wallet.py
index a69ed03f41dc728f130733cb84bf05c04532a25a..744a7c3406b9877e650119de095c225e201e76dc 100644
--- a/tests/test_wallet.py
+++ b/tests/test_wallet.py
@@ -1,4 +1,7 @@
-from bitcoin.rpc import JSONRPCError
+try:
+    from bitcoin.rpc import JSONRPCError
+except ModuleNotFoundError:
+    from bitcointx.rpc import JSONRPCError
 from decimal import Decimal
 from fixtures import *  # noqa: F401,F403
 from fixtures import TEST_NETWORK
-- 
2.55.0

