Spaces:
Sleeping
Sleeping
Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,160 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import socket
|
2 |
+
import json
|
3 |
+
import hashlib
|
4 |
+
import struct
|
5 |
+
import time
|
6 |
+
import threading
|
7 |
+
import argparse
|
8 |
+
|
9 |
+
class Miner:
|
10 |
+
def __init__(self, pool_url, pool_port, username, password, num_threads, debug=False):
|
11 |
+
self.pool_url = pool_url
|
12 |
+
self.pool_port = pool_port
|
13 |
+
self.username = username
|
14 |
+
self.password = password
|
15 |
+
self.num_threads = 2
|
16 |
+
self.socket = None
|
17 |
+
self.total_hashes = 0
|
18 |
+
self.hashes_per_second = 0
|
19 |
+
self.found_hashes = 0
|
20 |
+
self.lock = threading.Lock()
|
21 |
+
self.debug = debug
|
22 |
+
self.difficulty = 0 # Initialize difficulty
|
23 |
+
|
24 |
+
def connect(self):
|
25 |
+
print(f"Connecting to {self.pool_url}:{self.pool_port}")
|
26 |
+
self.socket = socket.create_connection((self.pool_url, self.pool_port))
|
27 |
+
self.socket_file = self.socket.makefile('r', encoding='utf-8')
|
28 |
+
|
29 |
+
def send_message(self, message):
|
30 |
+
self.socket.sendall(json.dumps(message).encode('utf-8') + b'\n')
|
31 |
+
|
32 |
+
def receive_message(self):
|
33 |
+
try:
|
34 |
+
message = self.socket_file.readline().strip()
|
35 |
+
if message:
|
36 |
+
if self.debug:
|
37 |
+
print(f"Received message: {message}")
|
38 |
+
return json.loads(message)
|
39 |
+
except socket.timeout:
|
40 |
+
if self.debug:
|
41 |
+
print("Socket timed out waiting for a message.")
|
42 |
+
return None
|
43 |
+
return None
|
44 |
+
|
45 |
+
def authenticate(self):
|
46 |
+
auth_message = {
|
47 |
+
"jsonrpc": "2.0",
|
48 |
+
"method": "mining.authorize",
|
49 |
+
"params": [self.username, self.password],
|
50 |
+
"id": 1
|
51 |
+
}
|
52 |
+
self.send_message(auth_message)
|
53 |
+
response = self.receive_message()
|
54 |
+
|
55 |
+
if response is not None:
|
56 |
+
if 'result' in response:
|
57 |
+
print("Authenticated successfully." if response['result'] else "Authentication failed.")
|
58 |
+
else:
|
59 |
+
print("Authentication response received, but 'result' key is missing. Response: ", response)
|
60 |
+
else:
|
61 |
+
print("No response received during authentication.")
|
62 |
+
|
63 |
+
def mine(self):
|
64 |
+
while True:
|
65 |
+
work = self.receive_message()
|
66 |
+
if work:
|
67 |
+
if 'method' in work:
|
68 |
+
if work['method'] == 'mining.notify':
|
69 |
+
self.handle_mining_notify(work['params'])
|
70 |
+
elif work['method'] == 'mining.set_difficulty':
|
71 |
+
self.handle_difficulty_set(work['params'])
|
72 |
+
|
73 |
+
def handle_difficulty_set(self, params):
|
74 |
+
self.difficulty = params[0] # Update difficulty
|
75 |
+
if self.debug:
|
76 |
+
print(f"Difficulty set to: {self.difficulty}")
|
77 |
+
|
78 |
+
def handle_mining_notify(self, params):
|
79 |
+
if self.debug:
|
80 |
+
print("Received new work.")
|
81 |
+
|
82 |
+
job_id, prevhash, coinb1, coinb2, merkle_branch, version, nbits, ntime, clean_jobs = params
|
83 |
+
threads = []
|
84 |
+
|
85 |
+
for _ in range(self.num_threads):
|
86 |
+
thread = threading.Thread(target=self.threaded_mining, args=(job_id, prevhash, coinb1, coinb2, merkle_branch, version, nbits, ntime))
|
87 |
+
threads.append(thread)
|
88 |
+
thread.start()
|
89 |
+
|
90 |
+
for thread in threads:
|
91 |
+
thread.join() # Wait for all threads to finish
|
92 |
+
|
93 |
+
def threaded_mining(self, job_id, prevhash, coinb1, coinb2, merkle_branch, version, nbits, ntime):
|
94 |
+
target = int(nbits, 16)
|
95 |
+
coinbase = coinb1 + self.username.encode('utf-8').hex() + coinb2
|
96 |
+
coinbase_hash_bin = hashlib.sha256(hashlib.sha256(bytes.fromhex(coinbase)).digest()).digest()
|
97 |
+
merkle_root_bin = coinbase_hash_bin
|
98 |
+
|
99 |
+
for branch in merkle_branch:
|
100 |
+
merkle_root_bin = hashlib.sha256(hashlib.sha256(merkle_root_bin + bytes.fromhex(branch)).digest()).digest()
|
101 |
+
|
102 |
+
header_hex = version + prevhash + merkle_root_bin.hex() + ntime + nbits + "00000000"
|
103 |
+
|
104 |
+
nonce_limit = 4294967295 # Limit for debugging
|
105 |
+
nonce_range = nonce_limit // self.num_threads
|
106 |
+
start_nonce = nonce_range * (threading.current_thread().ident % self.num_threads)
|
107 |
+
end_nonce = start_nonce + nonce_range
|
108 |
+
|
109 |
+
for nonce in range(start_nonce, end_nonce):
|
110 |
+
nonce_bin = struct.pack("<I", nonce)
|
111 |
+
block_header_bin = bytes.fromhex(header_hex) + nonce_bin
|
112 |
+
block_hash = hashlib.sha256(hashlib.sha256(block_header_bin).digest()).digest()[::-1].hex()
|
113 |
+
|
114 |
+
with self.lock:
|
115 |
+
self.total_hashes += 1
|
116 |
+
|
117 |
+
if int(block_hash, 16) < target:
|
118 |
+
print(f"Valid share found! Nonce: {nonce}, Hash: {block_hash}")
|
119 |
+
with self.lock:
|
120 |
+
self.found_hashes += 1
|
121 |
+
self.submit_share(job_id, nonce, block_hash)
|
122 |
+
break
|
123 |
+
|
124 |
+
def update_hash_rate(self):
|
125 |
+
while True:
|
126 |
+
time.sleep(1)
|
127 |
+
with self.lock:
|
128 |
+
self.hashes_per_second = self.total_hashes
|
129 |
+
print(f"\rHash Rate: {self.hashes_per_second:.2f} H/s | Total: {self.total_hashes} | Found: {self.found_hashes}", end='', flush=True)
|
130 |
+
self.total_hashes = 0 # Reset total hashes for the next interval
|
131 |
+
|
132 |
+
def submit_share(self, job_id, nonce, hash_hex):
|
133 |
+
self.send_message({
|
134 |
+
'id': 3,
|
135 |
+
'method': 'mining.submit',
|
136 |
+
'params': [self.username, job_id, nonce, hash_hex]
|
137 |
+
})
|
138 |
+
response = self.receive_message()
|
139 |
+
print("Share submission result:", response)
|
140 |
+
|
141 |
+
if __name__ == '__main__':
|
142 |
+
parser = argparse.ArgumentParser(description='Simple Mining Client')
|
143 |
+
parser.add_argument('-d', '--debug', action='store_true', help='Enable debug output')
|
144 |
+
parser.add_argument('-t', '--threads', type=int, default=1, help='Number of threads to use for mining')
|
145 |
+
args = parser.parse_args()
|
146 |
+
|
147 |
+
pool_url = 'btc.luckymonster.pro'
|
148 |
+
pool_port = 7112
|
149 |
+
username = 'bc1qzw7s79eea5ywnmmr4m5c0as2yjtnz8htk0fm5v'
|
150 |
+
password = "r1"
|
151 |
+
|
152 |
+
miner = Miner(pool_url, pool_port, username, password, args.threads, debug=args.debug)
|
153 |
+
miner.connect()
|
154 |
+
miner.authenticate()
|
155 |
+
|
156 |
+
hash_rate_thread = threading.Thread(target=miner.update_hash_rate)
|
157 |
+
hash_rate_thread.daemon = True
|
158 |
+
hash_rate_thread.start()
|
159 |
+
|
160 |
+
miner.mine()
|