Another small improvement, the last one I think. I didn't use hashlib to hash the nounce, but I used this 100% python implementation:
https://github.com/keanemind/python-sha-256.
In this way the code is about 3.5 slower (but that's just to understand, performances aren't important) but you can understand how the hash is encoded.
It sure is useful to me, and I will use it as a starting point for my own serious mining algorithm that I am building (
https://bitcointalksearch.org/topic/need-help-with-proof-of-concept-cpu-mining-5446391). I hope it is useful to others too.
# -*- coding: utf-8 -*-
import socket
import json
import hashlib
import binascii
from pprint import pprint
import random
import time
address = 'YourBitcoinAddress'
nonce = hex(0)[2:].zfill(8)
host = 'solo.ckpool.org'
port = 3333
#host = 'pool.mainnet.bitcoin-global.io'
#port = 9223
K = [
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
]
def generate_hash(message: bytearray) -> bytearray:
"""Return a SHA-256 hash from the message passed.
The argument should be a bytes, bytearray, or
string object."""
if isinstance(message, str):
message = bytearray(message, 'ascii')
elif isinstance(message, bytes):
message = bytearray(message)
elif not isinstance(message, bytearray):
raise TypeError
# Padding
length = len(message) * 8 # len(message) is number of BYTES!!!
message.append(0x80)
while (len(message) * 8 + 64) % 512 != 0:
message.append(0x00)
message += length.to_bytes(8, 'big') # pad to 8 bytes or 64 bits
assert (len(message) * 8) % 512 == 0, "Padding did not complete properly!"
# Parsing
blocks = [] # contains 512-bit chunks of message
for i in range(0, len(message), 64): # 64 bytes is 512 bits
blocks.append(message[i:i+64])
# Setting Initial Hash Value
h0 = 0x6a09e667
h1 = 0xbb67ae85
h2 = 0x3c6ef372
h3 = 0xa54ff53a
h5 = 0x9b05688c
h4 = 0x510e527f
h6 = 0x1f83d9ab
h7 = 0x5be0cd19
# SHA-256 Hash Computation
for message_block in blocks:
# Prepare message schedule
message_schedule = []
for t in range(0, 64):
if t <= 15:
# adds the t'th 32 bit word of the block,
# starting from leftmost word
# 4 bytes at a time
message_schedule.append(bytes(message_block[t*4:(t*4)+4]))
else:
term1 = _sigma1(int.from_bytes(message_schedule[t-2], 'big'))
term2 = int.from_bytes(message_schedule[t-7], 'big')
term3 = _sigma0(int.from_bytes(message_schedule[t-15], 'big'))
term4 = int.from_bytes(message_schedule[t-16], 'big')
# append a 4-byte byte object
schedule = ((term1 + term2 + term3 + term4) % 2**32).to_bytes(4, 'big')
message_schedule.append(schedule)
assert len(message_schedule) == 64
# Initialize working variables
a = h0
b = h1
c = h2
d = h3
e = h4
f = h5
g = h6
h = h7
# Iterate for t=0 to 63
for t in range(64):
t1 = ((h + _capsigma1(e) + _ch(e, f, g) + K[t] +
int.from_bytes(message_schedule[t], 'big')) % 2**32)
t2 = (_capsigma0(a) + _maj(a, b, c)) % 2**32
h = g
g = f
f = e
e = (d + t1) % 2**32
d = c
c = b
b = a
a = (t1 + t2) % 2**32
# Compute intermediate hash value
h0 = (h0 + a) % 2**32
h1 = (h1 + b) % 2**32
h2 = (h2 + c) % 2**32
h3 = (h3 + d) % 2**32
h4 = (h4 + e) % 2**32
h5 = (h5 + f) % 2**32
h6 = (h6 + g) % 2**32
h7 = (h7 + h) % 2**32
return ((h0).to_bytes(4, 'big') + (h1).to_bytes(4, 'big') +
(h2).to_bytes(4, 'big') + (h3).to_bytes(4, 'big') +
(h4).to_bytes(4, 'big') + (h5).to_bytes(4, 'big') +
(h6).to_bytes(4, 'big') + (h7).to_bytes(4, 'big'))
def _sigma0(num: int):
"""As defined in the specification."""
num = (_rotate_right(num, 7) ^
_rotate_right(num, 18) ^
(num >> 3))
return num
def _sigma1(num: int):
"""As defined in the specification."""
num = (_rotate_right(num, 17) ^
_rotate_right(num, 19) ^
(num >> 10))
return num
def _capsigma0(num: int):
"""As defined in the specification."""
num = (_rotate_right(num, 2) ^
_rotate_right(num, 13) ^
_rotate_right(num, 22))
return num
def _capsigma1(num: int):
"""As defined in the specification."""
num = (_rotate_right(num, 6) ^
_rotate_right(num, 11) ^
_rotate_right(num, 25))
return num
def _ch(x: int, y: int, z: int):
"""As defined in the specification."""
return (x & y) ^ (~x & z)
def _maj(x: int, y: int, z: int):
"""As defined in the specification."""
return (x & y) ^ (x & z) ^ (y & z)
def _rotate_right(num: int, shift: int, size: int = 32):
"""Rotate an integer right."""
return (num >> shift) | (num << size - shift)
def main():
print("address:{} nonce:{}".format(address,nonce))
print("host:{} port:{}".format(host,port))
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((host,port))
#server connection
sock.sendall(b'{"id": 1, "method": "mining.subscribe", "params": []}\n')
lines = sock.recv(1024).decode().split('\n')
response = json.loads(lines[0])
sub_details,extranonce1,extranonce2_size = response['result']
#authorize workers
sock.sendall(b'{"params": ["'+address.encode()+b'", "password"], "id": 2, "method": "mining.authorize"}\n')
#we read until 'mining.notify' is reached
response = b''
while response.count(b'\n') < 4 and not(b'mining.notify' in response):
response += sock.recv(1024)
#get rid of empty lines
responses = [json.loads(res) for res in response.decode().split('\n') if len(res.strip())>0 and 'mining.notify' in res]
pprint(responses)
job_id,prevhash,coinb1,coinb2,merkle_branch,version,nbits,ntime,clean_jobs \
= responses[0]['params']
target = (nbits[2:]+'00'*(int(nbits[:2],16) - 3)).zfill(64)
print('nbits:{} target:{}\n'.format(nbits,target))
# extranonce2 = '00'*extranonce2_size
extranonce2 = hex(random.randint(0,2**32-1))[2:].zfill(2*extranonce2_size) # create random
coinbase = coinb1 + extranonce1 + extranonce2 + coinb2
coinbase_hash_bin = hashlib.sha256(hashlib.sha256(binascii.unhexlify(coinbase)).digest()).digest()
print('coinbase:\n{}\n\ncoinbase hash:{}\n'.format(coinbase,binascii.hexlify(coinbase_hash_bin)))
merkle_root = coinbase_hash_bin
for h in merkle_branch:
merkle_root = hashlib.sha256(hashlib.sha256(merkle_root + binascii.unhexlify(h)).digest()).digest()
merkle_root = binascii.hexlify(merkle_root).decode()
#little endian
merkle_root = ''.join([merkle_root[i]+merkle_root[i+1] for i in range(0,len(merkle_root),2)][::-1])
print('merkle_root:{}\n'.format(merkle_root))
def noncework(k):
nonce = hex(k)[2:].zfill(8) #hex(int(nonce,16)+1)[2:]
blockheader = version + prevhash + merkle_root + nbits + ntime + nonce +\
'000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000'
# print('blockheader:\n{}\n'.format(blockheader))
hash=generate_hash(generate_hash(binascii.unhexlify(blockheader))).hex()
# hash = hashlib.sha256(hashlib.sha256(binascii.unhexlify(blockheader)).digest()).digest()
#hash = binascii.hexlify(blockheader).decode()
# print('hash: {}'.format(hash))
if(hash[:5] == '00000'): print('hash: {}'.format(hash))
if hash < target :
# if(hash[:10] == '0000000000'):
print('success!!')
print('hash: {}'.format(hash))
payload = bytes('{"params": ["'+address+'", "'+job_id+'", "'+extranonce2 \
+'", "'+ntime+'", "'+nonce+'"], "id": 1, "method": "mining.submit"}\n', 'utf-8')
sock.sendall(payload)
print(sock.recv(1024))
input("Press Enter to continue...")
# else:
# print('failed mine, hash is greater than target')
start = time.time()
for k in range(1000000):
noncework(k)
end = time.time()
print(end - start)
'''
for k in range(2**32):
noncework(k)
'''
sock.close()
main()
main()