1+ import socket
2+ import json
3+ import hashlib
4+ import struct
5+ import time
6+
7+ def sha256d (data ):
8+ return hashlib .sha256 (hashlib .sha256 (data ).digest ()).digest ()
9+
10+ def connect_to_pool (pool_address , pool_port , timeout = 30 , retries = 5 ):
11+ for attempt in range (retries ):
12+ try :
13+ print (f"Attempting to connect to pool (Attempt { attempt + 1 } /{ retries } )..." )
14+ sock = socket .socket (socket .AF_INET , socket .SOCK_STREAM )
15+ sock .settimeout (timeout )
16+ sock .connect ((pool_address , pool_port ))
17+ print ("Connected to pool!" )
18+ return sock
19+ except socket .gaierror as e :
20+ print (f"Address-related error connecting to server: { e } " )
21+ except socket .timeout as e :
22+ print (f"Connection timed out: { e } " )
23+ except socket .error as e :
24+ print (f"Socket error: { e } " )
25+
26+ print (f"Retrying in 5 seconds..." )
27+ time .sleep (5 )
28+
29+ raise Exception ("Failed to connect to the pool after multiple attempts" )
30+
31+ def send_message (sock , message ):
32+ print (f"Sending message: { message } " )
33+ sock .sendall (json .dumps (message ).encode ('utf-8' ) + b'\n ' )
34+
35+ def receive_messages (sock , timeout = 30 ):
36+ buffer = b''
37+ sock .settimeout (timeout ) # Set the timeout for receive operation
38+ while True :
39+ try :
40+ chunk = sock .recv (1024 )
41+ if not chunk :
42+ break
43+ buffer += chunk
44+ while b'\n ' in buffer :
45+ line , buffer = buffer .split (b'\n ' , 1 )
46+ print (f"Received message: { line .decode ('utf-8' )} " )
47+ yield json .loads (line .decode ('utf-8' ))
48+ except socket .timeout :
49+ print ("Receive operation timed out. Retrying..." )
50+ continue
51+
52+ def subscribe (sock ):
53+ message = {
54+ "id" : 1 ,
55+ "method" : "mining.subscribe" ,
56+ "params" : []
57+ }
58+ send_message (sock , message )
59+ for response in receive_messages (sock ):
60+ if response ['id' ] == 1 :
61+ print (f"Subscribe response: { response } " )
62+ return response ['result' ]
63+
64+ def authorize (sock , username , password ):
65+ message = {
66+ "id" : 2 ,
67+ "method" : "mining.authorize" ,
68+ "params" : [username , password ]
69+ }
70+ send_message (sock , message )
71+ for response in receive_messages (sock ):
72+ if response ['id' ] == 2 :
73+ return response ['result' ]
74+
75+ def mine (sock , job , target , extranonce1 , extranonce2_size ):
76+ job_id , prevhash , coinb1 , coinb2 , merkle_branch , version , nbits , ntime , clean_jobs = job
77+
78+ # Ensure extranonce2_size is an integer
79+ extranonce2_size = int (extranonce2_size )
80+ extranonce2 = struct .pack ('<Q' , 0 )[:extranonce2_size ]
81+
82+ coinbase = (coinb1 + extranonce1 + extranonce2 .hex () + coinb2 ).encode ('utf-8' )
83+ coinbase_hash_bin = sha256d (coinbase )
84+
85+ merkle_root = coinbase_hash_bin
86+ for branch in merkle_branch :
87+ merkle_root = sha256d (merkle_root + bytes .fromhex (branch ))
88+
89+ block_header = (version + prevhash + merkle_root [::- 1 ].hex () + ntime + nbits ).encode ('utf-8' )
90+
91+ nonce = 0
92+ max_nonce = 2 ** 32
93+ target_bin = bytes .fromhex (target )[::- 1 ]
94+
95+ while nonce < max_nonce :
96+ nonce_bin = struct .pack ('<I' , nonce )
97+ hash_result = sha256d (sha256d (block_header + nonce_bin ))
98+
99+ if hash_result [::- 1 ] < target_bin :
100+ print (f"Nonce found: { nonce } " )
101+ print (f"Hash: { hash_result [::- 1 ].hex ()} " )
102+ submit_solution (sock , job_id , extranonce2 , ntime , nonce )
103+ return
104+
105+ nonce += 1
106+
107+ def submit_solution (sock , job_id , extranonce2 , ntime , nonce ):
108+ message = {
109+ "id" : 4 ,
110+ "method" : "mining.submit" ,
111+ "params" : [username , job_id , extranonce2 .hex (), ntime , struct .pack ('<I' , nonce ).hex ()]
112+ }
113+ send_message (sock , message )
114+ for response in receive_messages (sock ):
115+ if response ['id' ] == 4 :
116+ print ("Submission response:" , response )
117+ if response ['result' ] == False and response ['error' ]['code' ] == 23 :
118+ print (f"Low difficulty share: { response ['error' ]['message' ]} " )
119+ return
120+
121+ if __name__ == "__main__" :
122+ pool_address = "public-pool.io"
123+ pool_port = 21496
124+ username = "bc1qp84qrxsntmpyekp9vzdenlt8khnj0h4wqafeqe"
125+ password = "x"
126+
127+ # Ensure the pool address does not include protocol prefix
128+ if pool_address .startswith ("stratum+tcp://" ):
129+ pool_address = pool_address [len ("stratum+tcp://" ):]
130+
131+ while True :
132+ try :
133+ sock = connect_to_pool (pool_address , pool_port )
134+
135+ extranonce = subscribe (sock )
136+ extranonce1 , extranonce2_size = extranonce [1 ], extranonce [2 ] # Adjust indices based on the actual response structure
137+ authorize (sock , username , password )
138+
139+ while True :
140+ for response in receive_messages (sock ):
141+ if response ['method' ] == 'mining.notify' :
142+ job = response ['params' ]
143+ mine (sock , job , job [6 ], extranonce1 , extranonce2_size )
144+ except Exception as e :
145+ print (f"An error occurred: { e } . Reconnecting..." )
146+ time .sleep (5 )
0 commit comments