1
0
mirror of git://projects.qi-hardware.com/openwrt-packages.git synced 2024-11-18 19:31:53 +02:00
openwrt-packages/icarus-miner/data/root/queue_ver/miner.py

324 lines
10 KiB
Python
Raw Normal View History

#!/usr/bin/env python
# by teknohog
# Python wrapper for my serial port FPGA Bitcoin miners
from jsonrpc import ServiceProxy
from time import ctime, sleep, time
from serial import Serial
from threading import Thread, Event, Lock
from Queue import Queue
from optparse import OptionParser
def stats(count, starttime):
# 2**32 hashes per share (difficulty 1)
mhshare = 4294.967296
s = sum(count)
tdelta = time() - starttime
rate = s * mhshare / tdelta
# This is only a rough estimate of the true hash rate,
# particularly when the number of events is low. However, since
# the events follow a Poisson distribution, we can estimate the
# standard deviation (sqrt(n) for n events). Thus we get some idea
# on how rough an estimate this is.
# s should always be positive when this function is called, but
# checking for robustness anyway
if s > 0:
stddev = rate / s**0.5
else:
stddev = 0
return "[%i accepted, %i failed, %.2f +/- %.2f Mhash/s]" % (count[0], count[1], rate, stddev)
class Reader(Thread):
def __init__(self):
Thread.__init__(self)
self.daemon = True
# flush the input buffer
#ser.read(1000)
def run(self):
while True:
nonce = ser.read(4)
if len(nonce) == 4:
# Keep this order, because writer.block will be
# updated due to the golden event.
submitter = Submitter(writer.block, nonce)
submitter.start()
if options.debug:
print("raise golden event\n")
golden.set()
class Writer(Thread):
def __init__(self):
Thread.__init__(self)
# Keep something sensible available while waiting for the
# first getwork
#self.block = "0" * 256
#self.midstate = "0" * 64
# This will produce nonce 063c5e01 -> debug by using a bogus URL
self.block = "0000000120c8222d0497a7ab44a1a2c7bf39de941c9970b1dc7cdc400000079700000000e88aabe1f353238c668d8a4df9318e614c10c474f8cdf8bc5f6397b946c33d7c4e7242c31a098ea500000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
self.midstate = "33c5bf5751ec7f7e056443b5aee3800331432c83f404d9de38b94ecbf907b92d"
self.daemon = True
def run(self):
while True:
result =0
#try:
# work = bitcoin.getwork()
# self.block = work['data']
# self.midstate = work['midstate']
#except:
# print("RPC getwork error")
# In this case, keep crunching with the old data. It will get
# stale at some point, but it's better than doing nothing.
# Just a reminder of how Python slices work in reverse
#rdata = self.block.decode('hex')[::-1]
#rdata2 = rdata[32:64]
work = wq.read_work_queue()
self.block = work['data']
self.midstate = work['midstate']
#print("push work to miner")
rdata2 = self.block.decode('hex')[95:63:-1]
rmid = self.midstate.decode('hex')[::-1]
payload = rmid + rdata2
ser.write(payload)
result = golden.wait(options.askrate)
if result:
golden.clear()
if options.debug:
print("clear golden event")
class WorkQueue:
def __init__(self, max_num):
self.max_num = max_num+1
self.ptr = 0
self.ptr_tobe = 0;
self.tail = 0
self.in_wr = 0;
self.work = {}
self.work_queue = []
for i in range(self.max_num):
self.work_queue.append({})
def get_from_server(self):
get_success = 0
while get_success != 1 :
try:
self.work_queue[self.ptr_tobe] = bitcoin.getwork()
get_success = 1
except:
print("RPC getwork error")
def is_full(self):
ptr_mutex.acquire()
full = (self.ptr + 1) % self.max_num == self.tail
ptr_mutex.release()
return full
def write_work_queue(self):
#print("update work queue")
ptr_mutex.acquire()
if (self.ptr + 1) % self.max_num == self.tail:
if options.debug:
print("Queue is full")
self.tail = (self.tail + 1) % self.max_num
self.ptr_tobe = (self.ptr + 1) % self.max_num
#print("write0:tail=", self.tail, "ptr_tobe=", self.ptr_tobe, "ptr="+self.ptr)
if options.debug:
print "write0:tail=%d, ptr_tobe=%d, ptr=%d" % (self.tail, self.ptr_tobe, self.ptr)
ptr_mutex.release()
#self.work_queue[self.ptr] = bitcoin.getwork()
write_queue_mutex.acquire()
self.get_from_server()
write_queue_mutex.release()
ptr_mutex.acquire()
if (self.ptr + 1) % self.max_num != self.ptr_tobe:
self.work_queue[self.ptr] = self.work_queue[self.ptr_tobe]
else:
self.ptr = self.ptr_tobe
if options.debug:
print "write1:tail=%d, ptr_tobe=%d, ptr=%d" % (self.tail, self.ptr_tobe, self.ptr)
#print("write1:tail="+self.tail+"ptr_tobe="+self.ptr_tobe+"ptr="+self.ptr)
ptr_mutex.release()
#print("1update work queue")
def read_work_queue(self):
ptr_mutex.acquire()
#print("read from queue")
if options.debug:
print"read0:tail=%d, ptr=%d" % (self.tail, self.ptr)
if self.ptr == self.tail:
ptr_mutex.release()
#print("Queue is empty")
#print("1read from queue")
write_queue_mutex.acquire()
ptr_mutex.acquire()
if self.ptr == self.tail:
print("reader get queue")
self.ptr_tobe = (self.ptr + 1) % self.max_num
self.get_from_server()
self.ptr = self.ptr_tobe
write_queue_mutex.release()
self.work = self.work_queue[self.ptr]
if self.ptr == 0:
self.ptr = self.max_num - 1
else:
self.ptr = self.ptr - 1
#print("read0:tail="+self.tail+"ptr="+self.ptr)
if options.debug:
print"read1:tail=%d, ptr=%d" % (self.tail, self.ptr)
ptr_mutex.release()
return self.work
class GetWorkQueue(Thread):
def __init__(self):
Thread.__init__(self)
self.daemon = True
self.delay = (options.askrate>>1)+1
def run(self):
while True:
if options.debug:
print("GetWorkQueue thread")
wq.write_work_queue()
#if (self.ptr + 1) % self.max_num == self.tail:
if(wq.is_full()):
sleep(4)
if options.debug:
print("queue is full, slow down request")
else:
if options.debug:
print("****\nfill the work queue at speed\n****")
#else:
# sleep(2)
class Submitter(Thread):
def __init__(self, block, nonce):
Thread.__init__(self)
self.block = block
self.nonce = nonce
def run(self):
# This thread will be created upon every submit, as they may
# come in sooner than the submits finish.
print("Block found on " + ctime() + "\n")
if stride > 0:
n = self.nonce.encode('hex')
print(n + " % " + str(stride) + " = " + str(int(n, 16) % stride))
elif options.debug:
print(self.nonce.encode('hex'))
hrnonce = self.nonce[::-1].encode('hex')
data = self.block[:152] + hrnonce + self.block[160:]
try:
result = bitcoin.getwork(data)
print("Upstream result: " + str(result))
print(self.nonce.encode('hex'))
except:
print("RPC send error")
print(self.nonce.encode('hex'))
# a sensible boolean for stats
result = False
results_queue.put(result)
class Display_stats(Thread):
def __init__(self):
Thread.__init__(self)
self.count = [0, 0]
self.starttime = time()
self.daemon = True
print("Miner started on " + ctime())
def run(self):
while True:
result = results_queue.get()
if result:
self.count[0] += 1
else:
self.count[1] += 1
print(stats(self.count, self.starttime))
results_queue.task_done()
parser = OptionParser()
parser.add_option("-a", "--askrate", dest="askrate", default=8, help="Seconds between getwork requests")
parser.add_option("-d", "--debug", dest="debug", default=False, action="store_true", help="Show each nonce result in hex")
parser.add_option("-m", "--miners", dest="miners", default=0, help="Show the nonce result remainder mod MINERS, to identify the node in a cluster")
parser.add_option("-u", "--url", dest="url", default="http://test_fpga_btc@hotmail.com:lzhjxntswc@pit.deepbit.net:8332/", help="URL for bitcoind or mining pool, typically http://user:password@host:8332/")
parser.add_option("-s", "--serial", dest="serial_port", default="com3", help="Serial port, e.g. /dev/ttyS0 on unix or COM1 in Windows")
(options, args) = parser.parse_args()
stride = int(options.miners)
golden = Event()
ptr_mutex = Lock();
write_queue_mutex = Lock();
bitcoin = ServiceProxy(options.url)
results_queue = Queue()
ser = Serial(options.serial_port, 115200, timeout=options.askrate)
wq = WorkQueue(5)
reader = Reader()
writer = Writer()
get_work_queue = GetWorkQueue()
disp = Display_stats()
get_work_queue.start()
reader.start()
writer.start()
disp.start()
try:
while True:
# Threads are generally hard to interrupt. So they are left
# running as daemons, and we do something simple here that can
# be easily terminated to bring down the entire script.
sleep(10000)
except KeyboardInterrupt:
print("Terminated")