import logging from bitcoinrpc.authproxy import AuthServiceProxy import config log = logging.getLogger(__name__) def connect_rpc() -> AuthServiceProxy: """Create an RPC connection to the Bitcoin node.""" return AuthServiceProxy( f"http://{config.RPC_USER}:{config.RPC_PASSWORD}@{config.RPC_HOST}:{config.RPC_PORT}" ) def test_rpc_connection() -> None: """Check the connection and show basic blockchain information.""" log.info("Checking RPC connection") try: info = connect_rpc().getblockchaininfo() log.info( "RPC connection successful - chain=%s, blocks=%d, difficulty=%s", info["chain"], info["blocks"], info["difficulty"], ) except Exception: log.exception("RPC connection error") raise def get_best_block_hash(rpc) -> str | None: """Fetch the most recent block hash.""" try: h = rpc.getbestblockhash() log.debug("Best block hash: %s", h) return h except Exception as e: log.error("RPC error getbestblockhash: %s", e) return None def get_block_template(rpc) -> dict | None: """Request a block template with SegWit support.""" try: tpl = rpc.getblocktemplate({"rules": ["segwit"]}) log.debug("Template received - height=%d, tx=%d", tpl["height"], len(tpl["transactions"])) return tpl except Exception as e: log.error("RPC error getblocktemplate: %s", e) return None def ensure_witness_data(rpc, template: dict) -> None: """ Enrich template transactions with full witness data. Use a single HTTP batch call to reduce latency versus N single requests. """ txs = template["transactions"] if not txs: return # JSON-RPC batch call: one HTTP request for all transactions try: batch = [["getrawtransaction", tx["txid"], False] for tx in txs] results = rpc._batch(batch) raw_map = { txs[r["id"]]["txid"]: r["result"] for r in results if r.get("result") is not None } except Exception as e: log.warning("RPC batch unavailable, falling back to single calls: %s", e) raw_map = {} for tx in txs: try: raw = rpc.getrawtransaction(tx["txid"], False) if raw: raw_map[tx["txid"]] = raw except Exception as e2: log.debug("Missing raw witness for %s: %s", tx["txid"], e2) template["transactions"] = [ {"hash": tx["txid"], "data": raw_map.get(tx["txid"], tx["data"])} for tx in txs ] def submit_block(rpc, serialized_block: str) -> None: """Submit the mined block to the Bitcoin node.""" log.info("Submitting serialized block (%d bytes) to node", len(serialized_block) // 2) if not serialized_block: log.error("Block not serialized correctly - submission canceled") return try: result = rpc.submitblock(serialized_block) if result is None: log.info("Block accepted into the blockchain") else: log.error("submitblock returned: %s", result) except Exception: log.exception("RPC error during submitblock")