Skip to content

Commit 5b754af

Browse files
committed
[WIP] python bitcoind initialization
1 parent 8c745bc commit 5b754af

File tree

1 file changed

+101
-38
lines changed

1 file changed

+101
-38
lines changed

python/test/payjoin_integration_test.py

Lines changed: 101 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import os
44
import sys
55
import httpx
6+
import logging
67

78
from payjoin import *
89
from typing import Optional
@@ -15,6 +16,8 @@
1516
)
1617

1718
import hashlib
19+
import subprocess
20+
import shutil
1821
import unittest
1922
from pprint import *
2023
from bitcoin import SelectParams
@@ -34,7 +37,86 @@ def get_rpc_credentials_from_cookie(cookie_path):
3437
credentials = cookie_file.read().strip()
3538
return credentials.split(":")
3639

37-
# Function to create and load a wallet if it doesn't already exist
40+
from typing import Optional, Tuple
41+
import time
42+
43+
from pathlib import Path
44+
45+
BITCOIND_DATADIR = Path("/tmp/bitcoind-test")
46+
COOKIE_PATH = BITCOIND_DATADIR / "regtest" / ".cookie"
47+
RPC_URL = "http://127.0.0.1:18443"
48+
49+
def init_bitcoind():
50+
bitcoind_exe = os.getenv("BITCOIND_EXE") or shutil.which("bitcoind")
51+
if not bitcoind_exe:
52+
raise RuntimeError("bitcoind not found")
53+
54+
args = [
55+
bitcoind_exe,
56+
"-regtest",
57+
f"-datadir={BITCOIND_DATADIR}",
58+
"-server",
59+
"-fallbackfee=0.0002",
60+
"-rpcbind=127.0.0.1",
61+
"-rpcallowip=127.0.0.1",
62+
"-printtoconsole",
63+
"-txindex"
64+
]
65+
66+
BITCOIND_DATADIR.mkdir(parents=True, exist_ok=True)
67+
68+
process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
69+
70+
for _ in range(30):
71+
if COOKIE_PATH.exists():
72+
break
73+
time.sleep(1)
74+
else:
75+
process.terminate()
76+
raise RuntimeError("bitcoind did not create cookie file in time")
77+
78+
return process, BITCOIND_DATADIR
79+
80+
81+
def get_cookie_auth():
82+
for _ in range(30):
83+
if COOKIE_PATH.exists():
84+
with open(COOKIE_PATH) as f:
85+
cookie = f.read().strip()
86+
return f"http://{cookie}@127.0.0.1:18443"
87+
time.sleep(1)
88+
raise RuntimeError("bitcoind cookie not found after timeout")
89+
90+
def wait_for_bitcoind(rpc_url, timeout=30):
91+
client = Proxy(rpc_url)
92+
for _ in range(timeout):
93+
try:
94+
client._call("getblockchaininfo")
95+
return client
96+
except Exception as e:
97+
print(f"Wanting for bitcoind: {e}")
98+
time.sleep(1)
99+
raise RuntimeError("bitcoind RPC not ready after timeout")
100+
101+
def init_bitcoind_sender_receiver():
102+
process, datadir = init_bitcoind()
103+
rpc_url = get_cookie_auth()
104+
105+
wait_for_bitcoind(rpc_url)
106+
107+
wallet_names = ["receiver", "sender"]
108+
clients = []
109+
110+
for wallet_name in wallet_names:
111+
default_client = Proxy(rpc_url)
112+
create_and_load_wallet(default_client, wallet_name)
113+
wallet_client = Proxy(f"{rpc_url}/wallet/{wallet_name}")
114+
clients.append(wallet_client)
115+
wallet_client._call("generatetoaddress", 1, wallet_client._call("getnewaddress"))
116+
117+
receiver, sender = clients
118+
return process, receiver, sender, datadir
119+
38120
def create_and_load_wallet(rpc_connection, wallet_name):
39121
try:
40122
# Try to load the wallet using the _call method
@@ -44,22 +126,11 @@ def create_and_load_wallet(rpc_connection, wallet_name):
44126
# Check if the error code indicates the wallet does not exist
45127
if e.error["code"] == -18: # Wallet not found error code
46128
# Create the wallet since it does not exist using the _call method
47-
rpc_connection._call("createwallet", wallet_name)
48-
print(f"Wallet '{wallet_name}' created and loaded successfully.")
129+
rpc_connection._call("createwallet", wallet_name)
130+
print(f"Wallet '{wallet_name}' created and loaded successfully.")
49131
elif e.error["code"] == -35: # Wallet already loaded
50132
print(f"Wallet '{wallet_name}' created and loaded successfully.")
51133

52-
53-
# Set up RPC connections
54-
rpc_user = os.environ.get("RPC_USER", "admin1")
55-
rpc_password = os.environ.get("RPC_PASSWORD", "123")
56-
rpc_host = os.environ.get("RPC_HOST", "localhost")
57-
rpc_port = os.environ.get("RPC_PORT", "18443")
58-
#ensure this is where your access cookie is located
59-
rpc_data_dir = os.environ.get("RPC_DATA_DIR", "~/.bitcoin/regtest")
60-
cookie_path = os.path.expanduser(os.path.join(rpc_data_dir, ".cookie"))
61-
rpc_user, rpc_password = get_rpc_credentials_from_cookie(cookie_path)
62-
63134
class InMemoryReceiverPersister(ReceiverPersister):
64135
def __init__(self):
65136
super().__init__()
@@ -94,26 +165,22 @@ def load(self, token: SenderToken) -> Sender:
94165
class TestPayjoin(unittest.IsolatedAsyncioTestCase):
95166
@classmethod
96167
def setUpClass(cls):
97-
# Initialize wallets once before all tests
98-
sender_wallet_name = "sender"
99-
sender_rpc_url = f"http://{rpc_user}:{rpc_password}@{rpc_host}:{rpc_port}/wallet/{sender_wallet_name}"
100-
cls.sender = Proxy(service_url=sender_rpc_url)
101-
create_and_load_wallet(cls.sender, sender_wallet_name)
102-
cls.sender.generatetoaddress(1, cls.sender.getnewaddress())
103-
104-
receiver_wallet_name = "receiver"
105-
receiver_rpc_url = f"http://{rpc_user}:{rpc_password}@{rpc_host}:{rpc_port}/wallet/{receiver_wallet_name}"
106-
cls.receiver = Proxy(service_url=receiver_rpc_url)
107-
create_and_load_wallet(cls.receiver, receiver_wallet_name)
108-
cls.receiver.generatetoaddress(1, cls.receiver.getnewaddress())
168+
cls.process, cls.receiver, cls.sender, cls.data_dir = init_bitcoind_sender_receiver()
109169

170+
@classmethod
171+
def tearDownClass(cls):
172+
if hasattr(cls, "process") and cls.process.poll() is None:
173+
cls.process.terminate()
174+
cls.process.wait()
175+
176+
if BITCOIND_DATADIR.exists():
177+
shutil.rmtree(BITCOIND_DATADIR)
178+
110179
async def test_integration_v2_to_v2(self):
111180
try:
112181
receiver_address = bitcoinffi.Address(str(self.receiver.getnewaddress()), bitcoinffi.Network.REGTEST)
113182
init_tracing()
114183
self.receiver.generatetoaddress(101, bitcoinffi.Address("mz3QWUnL94q2RdRxFtPq747SngoBu5uQMi", bitcoinffi.Network.REGTEST))
115-
pre_payjoin_receiver_balance = self.receiver.getbalance()
116-
pre_payjoin_sender_balance = self.sender.getbalance()
117184
services = TestServices.initialize()
118185

119186
services.wait_for_services_ready()
@@ -145,8 +212,6 @@ async def test_integration_v2_to_v2(self):
145212
# Inside the Sender:
146213
# Create a funded PSBT (not broadcasted) to address with amount given in the pj_uri
147214
pj_uri = session.pj_uri()
148-
outputs = {}
149-
outputs[pj_uri.address()] = 0.0001
150215
psbt = build_sweep_psbt(self.sender, pj_uri)
151216
new_sender = SenderBuilder(psbt, pj_uri).build_recommended(1000)
152217
persister = InMemorySenderPersister()
@@ -201,16 +266,14 @@ async def test_integration_v2_to_v2(self):
201266
final_psbt = self.sender._call("finalizepsbt", payjoin_psbt, False)["psbt"]
202267
payjoin_tx = bitcoinffi.Psbt.deserialize_base64(final_psbt).extract_tx()
203268
self.sender.sendrawtransaction(payjoin_tx)
204-
# print(f"Tx sent: {payjoin_tx.compute_txid()}")
205-
self.receiver.generatetoaddress(1, bitcoinffi.Address("mz3QWUnL94q2RdRxFtPq747SngoBu5uQMi", bitcoinffi.Network.REGTEST))
206269

207270
# Check resulting transaction and balances
208-
network_fees = bitcoinffi.Psbt.deserialize_base64(final_psbt).fee().to_sat()
271+
network_fees = bitcoinffi.Psbt.deserialize_base64(final_psbt).fee().to_btc()
209272
# Sender sent the entire value of their utxo to receiver (minus fees)
210273
self.assertEqual(len(payjoin_tx.input()), 2);
211-
self.assertEqual(len(payjoin_tx.output()), 2);
212-
self.assertEqual(self.receiver.getbalance(), pre_payjoin_receiver_balance + 10000)
213-
self.assertEqual(self.sender.getbalance(), pre_payjoin_sender_balance - (10000 + network_fees))
274+
self.assertEqual(len(payjoin_tx.output()), 1);
275+
self.assertEqual(float(self.receiver._call("getbalances")["mine"]["untrusted_pending"]), 100 - network_fees)
276+
self.assertEqual(self.sender.getbalance(), 0)
214277
return
215278
except Exception as e:
216279
print("Caught:", e)
@@ -227,13 +290,13 @@ def handle_directory_payjoin_proposal(receiver: Proxy, proposal: UncheckedPropos
227290

228291
def build_sweep_psbt(sender: Proxy, pj_uri: PjUri) -> bitcoinffi.Psbt:
229292
outputs = {}
230-
outputs[pj_uri.address()] = 0.0001
293+
outputs[pj_uri.address()] = 49.99998318
231294
psbt = sender._call(
232295
"walletcreatefundedpsbt",
233296
[],
234297
outputs,
235298
0,
236-
{"lockUnspents": True, "fee_rate": 10, "subtract_fee_from_outputs": [0]},
299+
{"lockUnspents": True, "fee_rate": 10, "subtract_fee_from_outputs": [0]}
237300
)["psbt"]
238301
return sender._call("walletprocesspsbt", psbt, True, None, False)["psbt"]
239302

0 commit comments

Comments
 (0)