Title: Solo mining with Bitcoin Core + Python program Post by: eric9800 on January 28, 2025, 07:13:59 AM 저는 제 노트북에 Bitcoin Core를 설치하고 동기화하여 실행했습니다. 또한, RPC를 통해 Bitcoin Core와 연결하여 솔로 마이닝을 수행하는 Python 프로그램을 개발하였습니다.
이 프로그램에서는 다양한 nonce 값을 생성하여 마이닝 과정에 적용하는 알고리즘을 구현하였습니다. 비록 블록 채굴에 성공할 확률이 매우 낮다는 것을 알고 있지만, 제가 만든 이 솔로 마이닝 Python 프로그램이 실제 비트코인 네트워크에서 기술적으로 올바르게 동작하며 오류 없이 운영되는지를 확인받고 싶습니다. 제 프로그램을 검토해 주시고, 문제점이나 개선 사항이 있다면 알려주신다면 저에게 큰 도움이 될 것입니다. 이와 같은 요청에 시간을 내어 주시고 도움을 주셔서 진심으로 감사드립니다. import hashlib import requests import json import struct import time import random # RPC 설정 RPC_USER = "user" RPC_PASSWORD = "passwd" RPC_URL = "http://127.0.0.1:8332/" # 사용자 정의 주소 MINING_ADDRESS = "your_bitcoin_address" def is_valid_bitcoin_address(address): """비트코인 주소의 유효성을 검증""" try: alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" base58_decoded = 0 for char in address: base58_decoded = base58_decoded * 58 + alphabet.index(char) address_bytes = base58_decoded.to_bytes(25, byteorder="big") checksum = address_bytes[-4:] hash_checksum = hashlib.sha256(hashlib.sha256(address_bytes[:-4]).digest()).digest()[:4] return checksum == hash_checksum except Exception as e: print(f"주소 검증 실패: {e}") return False # MINING_ADDRESS 검증 if not is_valid_bitcoin_address(MINING_ADDRESS): raise ValueError(f"유효하지 않은 비트코인 주소입니다: {MINING_ADDRESS}") def rpc_request(method, params=None): """RPC 요청""" payload = json.dumps({"jsonrpc": "2.0", "id": "mining", "method": method, "params": params or []}) response = requests.post(RPC_URL, auth=(RPC_USER, RPC_PASSWORD), data=payload) try: response.raise_for_status() result = response.json() if "result" not in result: raise ValueError("RPC 응답에 'result' 필드가 없습니다.") return result["result"] except Exception as e: print(f"RPC 요청 실패: {e}") print(f"응답 내용: {response.text}") raise def address_to_script_pubkey(address): """비트코인 주소를 ScriptPubKey로 변환""" try: alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" base58_decoded = 0 for char in address: base58_decoded = base58_decoded * 58 + alphabet.index(char) address_bytes = base58_decoded.to_bytes(25, byteorder="big") print(f"Base58 디코딩 성공: {address_bytes.hex()}") pubkey_hash = address_bytes[1:-4] # 네트워크 바이트와 체크섬 제외 script_pubkey = b"\x76\xa9" + bytes([len(pubkey_hash)]) + pubkey_hash + b"\x88\xac" print(f"ScriptPubKey 생성 성공: {script_pubkey.hex()}") return script_pubkey except Exception as e: print(f"주소 변환 실패: {e}") raise def modify_coinbase_tx(block_template, extra_nonce): """코인베이스 트랜잭션 생성""" coinbase_value = block_template["coinbasevalue"] script_pubkey = address_to_script_pubkey(MINING_ADDRESS) extra_nonce_script = struct.pack("<Q", extra_nonce) script_pubkey = extra_nonce_script + script_pubkey coinbase_tx = ( b"\x01" b"\x00" b"\x01" + b"\x00" * 32 + b"\xff" * 4 + b"\x01" + struct.pack("<Q", coinbase_value) + struct.pack("B", len(script_pubkey)) + script_pubkey ) return coinbase_tx def calculate_merkle_root(transactions): """머클 루트 계산""" transaction_hashes = [hashlib.sha256(bytes.fromhex(tx["data"])).digest() for tx in transactions] while len(transaction_hashes) > 1: if len(transaction_hashes) % 2 == 1: # 홀수인 경우 마지막 해시 복사 transaction_hashes.append(transaction_hashes[-1]) transaction_hashes = [ hashlib.sha256(transaction_hashes + transaction_hashes[i + 1]).digest() for i in range(0, len(transaction_hashes), 2) ] return transaction_hashes[0][::-1] # Little-endian 반환 def calculate_block_hash(header): """블록 헤더를 해싱하여 블록 해시 생성""" hash1 = hashlib.sha256(header).digest() hash2 = hashlib.sha256(hash1).digest() return hash2[::-1] # Little-endian 반환 def mine(): """마이닝 루프""" print("비트코인 채굴을 시작합니다...") extra_nonce = 0 # Extra Nonce를 처음부터 적용 while True: try: block_template = rpc_request("getblocktemplate", [{"rules": ["segwit"]}]) previous_block = block_template["previousblockhash"] target = int(block_template["target"], 16) print(f"마이닝 타겟: {block_template['target']}") while True: # Extra Nonce 루프 coinbase_tx = modify_coinbase_tx(block_template, extra_nonce) transactions = [{"data": coinbase_tx.hex()}] + block_template["transactions"] merkle_root = calculate_merkle_root(transactions) version = struct.pack("<I", block_template["version"]) prev_block_hash = bytes.fromhex(previous_block)[::-1] ntime = struct.pack("<I", int(block_template["curtime"])) nbits = bytes.fromhex(block_template["bits"])[::-1] nonce = 0 while nonce < 2**32: # Nonce 값 범위 header = ( version + prev_block_hash + merkle_root + ntime + nbits + struct.pack("<I", nonce) ) block_hash = calculate_block_hash(header) if int(block_hash.hex(), 16) < target: print(f"마이닝 성공! 블록 해시: {block_hash.hex()}") print(f"Extra Nonce: {extra_nonce}, Nonce: {nonce}") result = rpc_request("submitblock", [header.hex()]) if result is None: print("블록 제출 성공!") else: print(f"블록 제출 실패: {result}") return # 마이닝 루프 종료 nonce += 1 # Nonce 소진 시 Extra Nonce 증가 print(f"Nonce 소진 - Extra Nonce 증가: {extra_nonce}") extra_nonce += 1 except Exception as e: print(f"에러 발생: {e}") time.sleep(5) if __name__ == "__main__": mine() |