File size: 6,173 Bytes
7471272
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
import socket
import json
import hashlib
import struct
import time
import threading
import argparse

class Miner:
    def __init__(self, pool_url, pool_port, username, password, num_threads, debug=False):
        self.pool_url = pool_url
        self.pool_port = pool_port
        self.username = username
        self.password = password
        self.num_threads = 2
        self.socket = None
        self.total_hashes = 0
        self.hashes_per_second = 0
        self.found_hashes = 0
        self.lock = threading.Lock()
        self.debug = debug
        self.difficulty = 0  # Initialize difficulty

    def connect(self):
        print(f"Connecting to {self.pool_url}:{self.pool_port}")
        self.socket = socket.create_connection((self.pool_url, self.pool_port))
        self.socket_file = self.socket.makefile('r', encoding='utf-8')

    def send_message(self, message):
        self.socket.sendall(json.dumps(message).encode('utf-8') + b'\n')

    def receive_message(self):
        try:
            message = self.socket_file.readline().strip()
            if message:
                if self.debug:
                    print(f"Received message: {message}")
                return json.loads(message)
        except socket.timeout:
            if self.debug:
                print("Socket timed out waiting for a message.")
            return None
        return None

    def authenticate(self):
        auth_message = {
            "jsonrpc": "2.0",
            "method": "mining.authorize",
            "params": [self.username, self.password],
            "id": 1
        }
        self.send_message(auth_message)
        response = self.receive_message()

        if response is not None:
            if 'result' in response:
                print("Authenticated successfully." if response['result'] else "Authentication failed.")
            else:
                print("Authentication response received, but 'result' key is missing. Response: ", response)
        else:
            print("No response received during authentication.")

    def mine(self):
        while True:
            work = self.receive_message()
            if work:
                if 'method' in work:
                    if work['method'] == 'mining.notify':
                        self.handle_mining_notify(work['params'])
                    elif work['method'] == 'mining.set_difficulty':
                        self.handle_difficulty_set(work['params'])

    def handle_difficulty_set(self, params):
        self.difficulty = params[0]  # Update difficulty
        if self.debug:
            print(f"Difficulty set to: {self.difficulty}")

    def handle_mining_notify(self, params):
        if self.debug:
            print("Received new work.")
        
        job_id, prevhash, coinb1, coinb2, merkle_branch, version, nbits, ntime, clean_jobs = params
        threads = []

        for _ in range(self.num_threads):
            thread = threading.Thread(target=self.threaded_mining, args=(job_id, prevhash, coinb1, coinb2, merkle_branch, version, nbits, ntime))
            threads.append(thread)
            thread.start()

        for thread in threads:
            thread.join()  # Wait for all threads to finish

    def threaded_mining(self, job_id, prevhash, coinb1, coinb2, merkle_branch, version, nbits, ntime):
        target = int(nbits, 16)
        coinbase = coinb1 + self.username.encode('utf-8').hex() + coinb2
        coinbase_hash_bin = hashlib.sha256(hashlib.sha256(bytes.fromhex(coinbase)).digest()).digest()
        merkle_root_bin = coinbase_hash_bin

        for branch in merkle_branch:
            merkle_root_bin = hashlib.sha256(hashlib.sha256(merkle_root_bin + bytes.fromhex(branch)).digest()).digest()

        header_hex = version + prevhash + merkle_root_bin.hex() + ntime + nbits + "00000000"

        nonce_limit = 4294967295  # Limit for debugging
        nonce_range = nonce_limit // self.num_threads
        start_nonce = nonce_range * (threading.current_thread().ident % self.num_threads)
        end_nonce = start_nonce + nonce_range

        for nonce in range(start_nonce, end_nonce):
            nonce_bin = struct.pack("<I", nonce)
            block_header_bin = bytes.fromhex(header_hex) + nonce_bin
            block_hash = hashlib.sha256(hashlib.sha256(block_header_bin).digest()).digest()[::-1].hex()

            with self.lock:
                self.total_hashes += 1

            if int(block_hash, 16) < target:
                print(f"Valid share found! Nonce: {nonce}, Hash: {block_hash}")
                with self.lock:
                    self.found_hashes += 1
                self.submit_share(job_id, nonce, block_hash)
                break

    def update_hash_rate(self):
        while True:
            time.sleep(1)
            with self.lock:
                self.hashes_per_second = self.total_hashes
                print(f"\rHash Rate: {self.hashes_per_second:.2f} H/s | Total: {self.total_hashes} | Found: {self.found_hashes}", end='', flush=True)
                self.total_hashes = 0  # Reset total hashes for the next interval

    def submit_share(self, job_id, nonce, hash_hex):
        self.send_message({
            'id': 3,
            'method': 'mining.submit',
            'params': [self.username, job_id, nonce, hash_hex]
        })
        response = self.receive_message()
        print("Share submission result:", response)

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Simple Mining Client')
    parser.add_argument('-d', '--debug', action='store_true', help='Enable debug output')
    parser.add_argument('-t', '--threads', type=int, default=1, help='Number of threads to use for mining')
    args = parser.parse_args()

    pool_url = 'btc.luckymonster.pro'
    pool_port = 7112
    username = 'bc1qzw7s79eea5ywnmmr4m5c0as2yjtnz8htk0fm5v'
    password = "r1"

    miner = Miner(pool_url, pool_port, username, password, args.threads, debug=args.debug)
    miner.connect()
    miner.authenticate()

    hash_rate_thread = threading.Thread(target=miner.update_hash_rate)
    hash_rate_thread.daemon = True
    hash_rate_thread.start()

    miner.mine()