mirror of
https://github.com/LORDBABUINO/stealth.git
synced 2026-06-09 22:13:29 -07:00
159 lines
4.8 KiB
Python
159 lines
4.8 KiB
Python
"""
|
||
bitcoin_rpc.py — Facade that selects regtest or testnet backend
|
||
based on config.ini [bitcoin] network setting.
|
||
|
||
Exposes:
|
||
NETWORK – "regtest" or "testnet"
|
||
IS_REGTEST – True when running on regtest
|
||
cli(...) – RPC call routed to the correct backend
|
||
mine_blocks, get_tx, get_utxos, get_balance, send_raw, ...
|
||
"""
|
||
|
||
import json
|
||
import time
|
||
import os
|
||
import configparser
|
||
|
||
# ── Load config ──────────────────────────────────────────────────────────────
|
||
|
||
def _load_config():
|
||
cfg = configparser.ConfigParser()
|
||
config_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "config.ini")
|
||
cfg.read(config_path)
|
||
return cfg["bitcoin"] if "bitcoin" in cfg else {}
|
||
|
||
_cfg = _load_config()
|
||
NETWORK = _cfg.get("network", "regtest").strip().lower()
|
||
IS_REGTEST = NETWORK == "regtest"
|
||
|
||
# ── Select the right cli backend ─────────────────────────────────────────────
|
||
|
||
if IS_REGTEST:
|
||
from lib.clis import cli_regtest as _cli_backend
|
||
else:
|
||
from lib.clis import cli_testnet as _cli_backend
|
||
|
||
|
||
def cli(*args, wallet=None):
|
||
"""Route RPC calls to the active backend."""
|
||
return _cli_backend(*args, wallet=wallet)
|
||
|
||
|
||
# ── Block mining (regtest only) ──────────────────────────────────────────────
|
||
|
||
def mine_blocks(n=1):
|
||
"""Mine n blocks. Only works on regtest; no-op on testnet."""
|
||
if not IS_REGTEST:
|
||
# On testnet we cannot mine — just wait for propagation
|
||
time.sleep(2)
|
||
return get_block_count()
|
||
miner_addr = cli("getnewaddress", "", "bech32", wallet="miner")
|
||
cli("generatetoaddress", n, miner_addr)
|
||
return int(cli("getblockcount"))
|
||
|
||
|
||
def fund_wallet(wallet_name, amount=1.0, from_wallet="miner"):
|
||
"""Send `amount` BTC from `from_wallet` to a new address in `wallet_name`."""
|
||
addr = cli("getnewaddress", "", "bech32", wallet=wallet_name)
|
||
txid = cli("sendtoaddress", addr, f"{amount:.8f}", wallet=from_wallet)
|
||
return txid, addr
|
||
|
||
|
||
def wait_for_mempool_empty(timeout=60):
|
||
"""Wait until mempool is empty (all txs mined)."""
|
||
for _ in range(timeout * 2):
|
||
info = cli("getmempoolinfo")
|
||
if info["size"] == 0:
|
||
return True
|
||
time.sleep(0.5)
|
||
return False
|
||
|
||
|
||
def get_tx(txid):
|
||
"""Get decoded transaction."""
|
||
return cli("getrawtransaction", txid, "true")
|
||
|
||
|
||
def get_utxos(wallet_name, min_conf=0):
|
||
"""List unspent outputs for a wallet."""
|
||
return cli("listunspent", min_conf, wallet=wallet_name)
|
||
|
||
|
||
def get_balance(wallet_name):
|
||
"""Get wallet balance."""
|
||
return float(cli("getbalance", wallet=wallet_name))
|
||
|
||
|
||
def send_raw(hex_tx):
|
||
"""Broadcast a raw transaction."""
|
||
return cli("sendrawtransaction", hex_tx)
|
||
|
||
|
||
def create_funded_psbt(wallet_name, inputs, outputs, options=None):
|
||
"""Create a funded PSBT."""
|
||
args = ["walletcreatefundedpsbt", json.dumps(inputs), json.dumps(outputs), 0]
|
||
if options:
|
||
args.append(json.dumps(options))
|
||
return cli(*args, wallet=wallet_name)
|
||
|
||
|
||
def process_psbt(wallet_name, psbt):
|
||
"""Sign a PSBT."""
|
||
return cli("walletprocesspsbt", psbt, wallet=wallet_name)
|
||
|
||
|
||
def finalize_psbt(psbt):
|
||
"""Finalize a PSBT."""
|
||
return cli("finalizepsbt", psbt)
|
||
|
||
|
||
def decode_psbt(psbt):
|
||
"""Decode a PSBT."""
|
||
return cli("decodepsbt", psbt)
|
||
|
||
|
||
def create_raw_tx(inputs, outputs):
|
||
"""Create a raw transaction."""
|
||
return cli("createrawtransaction", json.dumps(inputs), json.dumps(outputs))
|
||
|
||
|
||
def sign_raw_tx(wallet_name, hex_tx):
|
||
"""Sign a raw transaction."""
|
||
return cli("signrawtransactionwithwallet", hex_tx, wallet=wallet_name)
|
||
|
||
|
||
def decode_raw_tx(hex_tx):
|
||
"""Decode a raw transaction."""
|
||
return cli("decoderawtransaction", hex_tx)
|
||
|
||
|
||
def get_block_count():
|
||
"""Get current block height."""
|
||
return int(cli("getblockcount"))
|
||
|
||
|
||
def get_new_address(wallet_name, addr_type="bech32"):
|
||
"""Get a new address."""
|
||
return cli("getnewaddress", "", addr_type, wallet=wallet_name)
|
||
|
||
|
||
def send_to_address(wallet_name, address, amount):
|
||
"""Send BTC to an address."""
|
||
print(f"Sending {amount:.8f} BTC from {wallet_name} to {address}...")
|
||
return cli("sendtoaddress", address, f"{amount:.8f}", wallet=wallet_name)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
print(f"Network mode: {NETWORK} (IS_REGTEST={IS_REGTEST})")
|
||
print("Testing RPC connection...")
|
||
info = cli("getblockchaininfo")
|
||
print(f" Chain: {info['chain']}")
|
||
print(f" Blocks: {info['blocks']}")
|
||
|
||
for w in ["miner", "alice", "bob", "carol", "exchange", "risky"]:
|
||
try:
|
||
bal = get_balance(w)
|
||
print(f" {w}: {bal} BTC")
|
||
except Exception as e:
|
||
print(f" {w}: ERROR - {e}")
|