Merge pull request #156 from ahmedbodi/issue-144

Issue 144
This commit is contained in:
ahmedbodi 2014-01-20 09:15:04 -08:00
commit daa024115b
10 changed files with 253 additions and 113 deletions

View File

@ -21,11 +21,8 @@ COINDAEMON_TRUSTED_PASSWORD = 'somepassword'
# The available options are: # The available options are:
# scrypt, sha256d, scrypt-jane and quark # scrypt, sha256d, scrypt-jane and quark
# If the option does not meet either of these criteria stratum defaults to scrypt # If the option does not meet either of these criteria stratum defaults to scrypt
# Until AutoReward Selecting Code has been implemented the below options are used to select the type of coin
# For Reward type there is POW and POS. please ensure you choose the currect type.
# For Coins which support TX Messages please enter yes in the TX selection # For Coins which support TX Messages please enter yes in the TX selection
COINDAEMON_ALGO = 'scrypt' COINDAEMON_ALGO = 'scrypt'
COINDAEMON_Reward = 'POW'
COINDAEMON_TX = 'no' COINDAEMON_TX = 'no'
# ******************** BASIC SETTINGS *************** # ******************** BASIC SETTINGS ***************
# Backup Coin Daemon address's (consider having at least 1 backup) # Backup Coin Daemon address's (consider having at least 1 backup)
@ -191,3 +188,11 @@ NOTIFY_EMAIL_SERVER = 'localhost' # E-Mail Sender
NOTIFY_EMAIL_USERNAME = '' # E-Mail server SMTP Logon NOTIFY_EMAIL_USERNAME = '' # E-Mail server SMTP Logon
NOTIFY_EMAIL_PASSWORD = '' NOTIFY_EMAIL_PASSWORD = ''
NOTIFY_EMAIL_USETLS = True NOTIFY_EMAIL_USETLS = True
#### Memcache ####
# Memcahce is a requirement. Enter the settings below
MEMCACHE_HOST = "localhost" # hostname or IP that runs memcached
MEMCACHE_PORT = 11211 # Port
MEMCACHE_TIMEOUT = 900 # Key timeout
MEMCACHE_PREFIX = "stratum_" # Prefix for keys

View File

@ -5,6 +5,8 @@ import struct
import util import util
import merkletree import merkletree
import halfnode import halfnode
from coinbasetx import CoinbaseTransactionPOW
from coinbasetx import CoinbaseTransactionPOS
from coinbasetx import CoinbaseTransaction from coinbasetx import CoinbaseTransaction
import lib.logger import lib.logger
log = lib.logger.get_logger('block_template') log = lib.logger.get_logger('block_template')
@ -55,10 +57,10 @@ class BlockTemplate(halfnode.CBlock):
txhashes = [None] + [ util.ser_uint256(int(t['hash'], 16)) for t in data['transactions'] ] txhashes = [None] + [ util.ser_uint256(int(t['hash'], 16)) for t in data['transactions'] ]
mt = merkletree.MerkleTree(txhashes) mt = merkletree.MerkleTree(txhashes)
if settings.COINDAEMON_Reward == 'POW': if settings.COINDAEMON_Reward == 'POW':
coinbase = self.coinbase_transaction_class(self.timestamper, self.coinbaser, data['coinbasevalue'], data['coinbaseaux']['flags'], data['height'], coinbase = CoinbaseTransactionPOW(self.timestamper, self.coinbaser, data['coinbasevalue'], data['coinbaseaux']['flags'], data['height'],
settings.COINBASE_EXTRAS) settings.COINBASE_EXTRAS)
else: else:
coinbase = self.coinbase_transaction_class(self.timestamper, self.coinbaser, data['coinbasevalue'], data['coinbaseaux']['flags'], data['height'], coinbase = CoinbaseTransactionPOS(self.timestamper, self.coinbaser, data['coinbasevalue'], data['coinbaseaux']['flags'], data['height'],
settings.COINBASE_EXTRAS, data['curtime']) settings.COINBASE_EXTRAS, data['curtime'])
self.height = data['height'] self.height = data['height']

View File

@ -6,8 +6,8 @@ import settings
import lib.logger import lib.logger
log = lib.logger.get_logger('coinbasetx') log = lib.logger.get_logger('coinbasetx')
if settings.COINDAEMON_Reward == 'POW': #if settings.COINDAEMON_Reward == 'POW':
class CoinbaseTransaction(halfnode.CTransaction): class CoinbaseTransactionPOW(halfnode.CTransaction):
'''Construct special transaction used for coinbase tx. '''Construct special transaction used for coinbase tx.
It also implements quick serialization using pre-cached It also implements quick serialization using pre-cached
scriptSig template.''' scriptSig template.'''
@ -17,8 +17,8 @@ if settings.COINDAEMON_Reward == 'POW':
extranonce_size = struct.calcsize(extranonce_type) extranonce_size = struct.calcsize(extranonce_type)
def __init__(self, timestamper, coinbaser, value, flags, height, data): def __init__(self, timestamper, coinbaser, value, flags, height, data):
super(CoinbaseTransaction, self).__init__() super(CoinbaseTransactionPOW, self).__init__()
log.debug("Got to CoinBaseTX") log.debug("Got to CoinBaseTX")
#self.extranonce = 0 #self.extranonce = 0
if len(self.extranonce_placeholder) != self.extranonce_size: if len(self.extranonce_placeholder) != self.extranonce_size:
@ -45,7 +45,7 @@ if settings.COINDAEMON_Reward == 'POW':
self.vout.append(tx_out) self.vout.append(tx_out)
# Two parts of serialized coinbase, just put part1 + extranonce + part2 to have final serialized tx # Two parts of serialized coinbase, just put part1 + extranonce + part2 to have final serialized tx
self._serialized = super(CoinbaseTransaction, self).serialize().split(self.extranonce_placeholder) self._serialized = super(CoinbaseTransactionPOW, self).serialize().split(self.extranonce_placeholder)
def set_extranonce(self, extranonce): def set_extranonce(self, extranonce):
if len(extranonce) != self.extranonce_size: if len(extranonce) != self.extranonce_size:
@ -53,8 +53,56 @@ if settings.COINDAEMON_Reward == 'POW':
(part1, part2) = self.vin[0]._scriptSig_template (part1, part2) = self.vin[0]._scriptSig_template
self.vin[0].scriptSig = part1 + extranonce + part2 self.vin[0].scriptSig = part1 + extranonce + part2
elif settings.COINDAEMON_Reward == 'POS': #elif settings.COINDAEMON_Reward == 'POS':
class CoinbaseTransaction(halfnode.CTransaction): class CoinbaseTransactionPOS(halfnode.CTransaction):
'''Construct special transaction used for coinbase tx.
It also implements quick serialization using pre-cached
scriptSig template.'''
extranonce_type = '>Q'
extranonce_placeholder = struct.pack(extranonce_type, int('f000000ff111111f', 16))
extranonce_size = struct.calcsize(extranonce_type)
def __init__(self, timestamper, coinbaser, value, flags, height, data, ntime):
super(CoinbaseTransactionPOS, self).__init__()
log.debug("Got to CoinBaseTX")
#self.extranonce = 0
if len(self.extranonce_placeholder) != self.extranonce_size:
raise Exception("Extranonce placeholder don't match expected length!")
tx_in = halfnode.CTxIn()
tx_in.prevout.hash = 0L
tx_in.prevout.n = 2**32-1
tx_in._scriptSig_template = (
util.ser_number(height) + binascii.unhexlify(flags) + util.ser_number(int(timestamper.time())) + \
chr(self.extranonce_size),
util.ser_string(coinbaser.get_coinbase_data() + data)
)
tx_in.scriptSig = tx_in._scriptSig_template[0] + self.extranonce_placeholder + tx_in._scriptSig_template[1]
tx_out = halfnode.CTxOut()
tx_out.nValue = value
tx_out.scriptPubKey = coinbaser.get_script_pubkey()
self.nTime = ntime
if settings.COINDAEMON_SHA256_TX == 'yes':
self.strTxComment = "http://github.com/ahmedbodi/stratum-mining"
self.vin.append(tx_in)
self.vout.append(tx_out)
# Two parts of serialized coinbase, just put part1 + extranonce + part2 to have final serialized tx
self._serialized = super(CoinbaseTransactionPOS, self).serialize().split(self.extranonce_placeholder)
def set_extranonce(self, extranonce):
if len(extranonce) != self.extranonce_size:
raise Exception("Incorrect extranonce size")
(part1, part2) = self.vin[0]._scriptSig_template
self.vin[0].scriptSig = part1 + extranonce + part2
#else:
class CoinbaseTransaction(halfnode.CTransaction):
'''Construct special transaction used for coinbase tx. '''Construct special transaction used for coinbase tx.
It also implements quick serialization using pre-cached It also implements quick serialization using pre-cached
scriptSig template.''' scriptSig template.'''
@ -86,54 +134,6 @@ elif settings.COINDAEMON_Reward == 'POS':
tx_out.nValue = value tx_out.nValue = value
tx_out.scriptPubKey = coinbaser.get_script_pubkey() tx_out.scriptPubKey = coinbaser.get_script_pubkey()
self.nTime = ntime
if settings.COINDAEMON_SHA256_TX == 'yes':
self.strTxComment = "http://github.com/ahmedbodi/stratum-mining"
self.vin.append(tx_in)
self.vout.append(tx_out)
# Two parts of serialized coinbase, just put part1 + extranonce + part2 to have final serialized tx
self._serialized = super(CoinbaseTransaction, self).serialize().split(self.extranonce_placeholder)
def set_extranonce(self, extranonce):
if len(extranonce) != self.extranonce_size:
raise Exception("Incorrect extranonce size")
(part1, part2) = self.vin[0]._scriptSig_template
self.vin[0].scriptSig = part1 + extranonce + part2
else:
class CoinbaseTransaction(halfnode.CTransaction):
'''Construct special transaction used for coinbase tx.
It also implements quick serialization using pre-cached
scriptSig template.'''
extranonce_type = '>Q'
extranonce_placeholder = struct.pack(extranonce_type, int('f000000ff111111f', 16))
extranonce_size = struct.calcsize(extranonce_type)
def __init__(self, timestamper, coinbaser, value, flags, height, data, ntime):
super(CoinbaseTransaction, self).__init__()
log.debug("Got to CoinBaseTX")
#self.extranonce = 0
if len(self.extranonce_placeholder) != self.extranonce_size:
raise Exception("Extranonce placeholder don't match expected length!")
tx_in = halfnode.CTxIn()
tx_in.prevout.hash = 0L
tx_in.prevout.n = 2**32-1
tx_in._scriptSig_template = (
util.ser_number(height) + binascii.unhexlify(flags) + util.ser_number(int(timestamper.time())) + \
chr(self.extranonce_size),
util.ser_string(coinbaser.get_coinbase_data() + data)
)
tx_in.scriptSig = tx_in._scriptSig_template[0] + self.extranonce_placeholder + tx_in._scriptSig_template[1]
tx_out = halfnode.CTxOut()
tx_out.nValue = value
tx_out.scriptPubKey = coinbaser.get_script_pubkey()
self.nTime = ntime self.nTime = ntime
self.vin.append(tx_in) self.vin.append(tx_in)
self.vout.append(tx_out) self.vout.append(tx_out)

View File

@ -21,27 +21,27 @@ log = lib.logger.get_logger('halfnode')
log.debug("Got to Halfnode") log.debug("Got to Halfnode")
if settings.COINDAEMON_ALGO == 'scrypt': if settings.COINDAEMON_ALGO == 'scrypt':
log.debug("########################################### Loading LTC Scrypt #########################################################") log.debug("########################################### Loading LTC Scrypt #########################################################")
import ltc_scrypt import ltc_scrypt
elif settings.COINDAEMON_ALGO == 'quark': elif settings.COINDAEMON_ALGO == 'quark':
log.debug("########################################### Loading Quark Support #########################################################") log.debug("########################################### Loading Quark Support #########################################################")
import quark_hash import quark_hash
else: else:
log.debug("########################################### Loading SHA256 Support ######################################################") log.debug("########################################### Loading SHA256 Support ######################################################")
if settings.COINDAEMON_Reward == 'POS': #if settings.COINDAEMON_Reward == 'POS':
log.debug("########################################### Loading POS Support #########################################################") # log.debug("########################################### Loading POS Support #########################################################")
pass # pass
else: #else:
log.debug("########################################### Loading POW Support ######################################################") # log.debug("########################################### Loading POW Support ######################################################")
pass # pass
if settings.COINDAEMON_TX == 'yes': if settings.COINDAEMON_TX == 'yes':
log.debug("########################################### Loading SHA256 Transaction Message Support #########################################################") log.debug("########################################### Loading SHA256 Transaction Message Support #########################################################")
pass pass
else: else:
log.debug("########################################### NOT Loading SHA256 Transaction Message Support ######################################################") log.debug("########################################### NOT Loading SHA256 Transaction Message Support ######################################################")
pass pass
MY_VERSION = 31402 MY_VERSION = 31402

View File

@ -211,15 +211,15 @@ def ser_number(n):
s.append(n) s.append(n)
return bytes(s) return bytes(s)
if settings.COINDAEMON_Reward == 'POW': #if settings.COINDAEMON_Reward == 'POW':
def script_to_address(addr): def script_to_address(addr):
d = address_to_pubkeyhash(addr) d = address_to_pubkeyhash(addr)
if not d: if not d:
raise ValueError('invalid address') raise ValueError('invalid address')
(ver, pubkeyhash) = d (ver, pubkeyhash) = d
return b'\x76\xa9\x14' + pubkeyhash + b'\x88\xac' return b'\x76\xa9\x14' + pubkeyhash + b'\x88\xac'
else: #else:
def script_to_pubkey(key): def script_to_pubkey(key):
if len(key) == 66: key = binascii.unhexlify(key) if len(key) == 66: key = binascii.unhexlify(key)
if len(key) != 33: raise Exception('Invalid Address') if len(key) != 33: raise Exception('Invalid Address')
return b'\x21' + key + b'\xac' return b'\x21' + key + b'\xac'

24
mining/Cache.py Normal file
View File

@ -0,0 +1,24 @@
''' A simple wrapper for pylibmc. It can be overwritten with simple hashing if necessary '''
import lib.settings as settings
import lib.logger
log = lib.logger.get_logger('Cache')
import pylibmc
class Cache():
def __init__(self):
# Open a new connection
self.mc = pylibmc.Client([settings.MEMCACHE_HOST + ":" + str(settings.MEMCACHE_PORT)], binary=True)
log.info("Caching initialized")
def set(self, key, value, time=settings.MEMCACHE_TIMEOUT):
return self.mc.set(settings.MEMCACHE_PREFIX + str(key), value, time)
def get(self, key):
return self.mc.get(settings.MEMCACHE_PREFIX + str(key))
def delete(self, key):
return self.mc.delete(settings.MEMCACHE_PREFIX + str(key))
def exists(self, key):
return str(key) in self.mc.get(settings.MEMCACHE_PREFIX + str(key))

View File

@ -3,6 +3,7 @@ import time
from datetime import datetime from datetime import datetime
import Queue import Queue
import signal import signal
import Cache
import lib.settings as settings import lib.settings as settings
@ -19,8 +20,7 @@ class DBInterface():
self.q = Queue.Queue() self.q = Queue.Queue()
self.queueclock = None self.queueclock = None
self.usercache = {} self.cache = Cache.Cache()
self.clearusercache()
self.nextStatsUpdate = 0 self.nextStatsUpdate = 0
@ -67,11 +67,6 @@ class DBInterface():
return DB_None.DB_None() return DB_None.DB_None()
def clearusercache(self):
log.debug("DBInterface.clearusercache called")
self.usercache = {}
self.usercacheclock = reactor.callLater(settings.DB_USERCACHE_TIME , self.clearusercache)
def scheduleImport(self): def scheduleImport(self):
# This schedule's the Import # This schedule's the Import
if settings.DATABASE_DRIVER == "sqlite": if settings.DATABASE_DRIVER == "sqlite":
@ -163,19 +158,16 @@ class DBInterface():
# Force username and password to be strings # Force username and password to be strings
username = str(username) username = str(username)
password = str(password) password = str(password)
wid = username + ":-:" + password if not settings.USERS_CHECK_PASSWORD and self.user_exists(username):
if wid in self.usercache:
return True return True
elif not settings.USERS_CHECK_PASSWORD and self.user_exists(username): elif self.cache.get(username) == password:
self.usercache[wid] = 1
return True return True
elif self.dbi.check_password(username, password): elif self.dbi.check_password(username, password):
self.usercache[wid] = 1 self.cache.set(username, password)
return True return True
elif settings.USERS_AUTOADD == True: elif settings.USERS_AUTOADD == True:
self.insert_user(username, password) self.insert_user(username, password)
self.usercache[wid] = 1 self.cache.set(username, password)
return True return True
log.info("Authentication for %s failed" % username) log.info("Authentication for %s failed" % username)
@ -188,6 +180,8 @@ class DBInterface():
return self.dbi.get_user(id) return self.dbi.get_user(id)
def user_exists(self, username): def user_exists(self, username):
if self.cache.get(username) is not None:
return True
user = self.dbi.get_user(username) user = self.dbi.get_user(username)
return user is not None return user is not None
@ -195,11 +189,13 @@ class DBInterface():
return self.dbi.insert_user(username, password) return self.dbi.insert_user(username, password)
def delete_user(self, username): def delete_user(self, username):
self.mc.delete(username)
self.usercache = {} self.usercache = {}
return self.dbi.delete_user(username) return self.dbi.delete_user(username)
def update_user(self, username, password): def update_user(self, username, password):
self.usercache = {} self.mc.delete(username)
self.mc.set(username, password)
return self.dbi.update_user(username, password) return self.dbi.update_user(username, password)
def update_worker_diff(self, username, diff): def update_worker_diff(self, username, diff):

View File

@ -60,6 +60,72 @@ class DB_Mysql_Vardiff(DB_Mysql.DB_Mysql):
self.dbh.commit() self.dbh.commit()
def found_block(self, data):
# for database compatibility we are converting our_worker to Y/N format
if data[5]:
data[5] = 'Y'
else:
data[5] = 'N'
# Check for the share in the database before updating it
# Note: We can't use DUPLICATE KEY because solution is not a key
self.execute(
"""
Select `id` from `shares`
WHERE `solution` = %(solution)s
LIMIT 1
""",
{
"solution": data[2]
}
)
shareid = self.dbc.fetchone()
if shareid[0] > 0:
# Note: difficulty = -1 here
self.execute(
"""
UPDATE `shares`
SET `upstream_result` = %(result)s
WHERE `solution` = %(solution)s
AND `id` = %(id)s
LIMIT 1
""",
{
"result": data[5],
"solution": data[2],
"id": shareid[0]
}
)
self.dbh.commit()
else:
self.execute(
"""
INSERT INTO `shares`
(time, rem_host, username, our_result,
upstream_result, reason, solution)
VALUES
(FROM_UNIXTIME(%(time)s), %(host)s,
%(uname)s,
%(lres)s, %(result)s, %(reason)s, %(solution)s)
""",
{
"time": v[4],
"host": v[6],
"uname": v[0],
"lres": v[5],
"result": v[5],
"reason": v[9],
"solution": v[2]
}
)
self.dbh.commit()
def update_worker_diff(self, username, diff): def update_worker_diff(self, username, diff):
log.debug("Setting difficulty for %s to %s", username, diff) log.debug("Setting difficulty for %s to %s", username, diff)

View File

@ -44,17 +44,16 @@ def setup(on_startup):
if isinstance(result, dict): if isinstance(result, dict):
# litecoind implements version 1 of getblocktemplate # litecoind implements version 1 of getblocktemplate
if result['version'] >= 1: if result['version'] >= 1:
result = (yield bitcoin_rpc.getinfo()) result = (yield bitcoin_rpc.getdifficulty())
if isinstance(result,dict): if isinstance(result,dict):
if 'stake' in result and settings.COINDAEMON_Reward == 'POS': if 'proof-of-stake' in result:
log.info("CoinD looks to be a POS Coin, Config for POS looks correct") settings.COINDAEMON_Reward = 'POS'
break log.info("Coin detected as POS")
elif 'stake' not in result and settings.COINDAEMON_Reward == 'POW': break;
log.info("CoinD looks to be a POW Coin, Config looks to be correct") else:
break settings.COINDAEMON_Reward = 'POW'
else: log.info("Coin detected as POW")
log.error("Wrong Algo Selected, Switch to appropriate POS/POW in config.py!") break;
reactor.stop()
else: else:
log.error("Block Version mismatch: %s" % result['version']) log.error("Block Version mismatch: %s" % result['version'])

48
requirements.txt Normal file
View File

@ -0,0 +1,48 @@
# This File is used to create a list of requirements needed for testing stratum-mining or to create a clone install
BeautifulSoup==3.2.1
#Brlapi==0.5.7
#GnuPGInterface==0.3.2
MySQL-python==1.2.3
#PAM==0.4.2
#Pyste==0.9.10
#SOAPpy==0.12.0
Twisted==12.0.0
#Twisted-Conch==12.0.0
#Twisted-Core==12.0.0
#Twisted-Lore==12.0.0
#Twisted-Mail==12.0.0
#Twisted-Names==12.0.0
#Twisted-News==12.0.0
#Twisted-Runner==12.0.0
#Twisted-Web==12.0.0
#Twisted-Words==12.0.0
#apt-xapian-index==0.45
argparse==1.2.1
autobahn==0.6.5
#chardet==2.0.1
defer==1.0.6
distribute==0.6.28
ecdsa==0.10
feedparser==5.1.2
fpconst==0.7.2
httplib2==0.7.4
#louis==2.4.1
#ltc-scrypt==1.0
#numpy==1.6.2
pyOpenSSL==0.13
pyasn1==0.1.3
pycrypto==2.6
#pycurl==7.19.0
pyserial==2.5
#python-apt==0.8.8.2
#python-debian==0.1.21
#python-debianbts==1.11
python-memcached==1.48
pyxdg==0.19
#reportbug==6.4.4
simplejson==2.5.2
#stratum==0.2.13
#uTidylib==0.2
#unattended-upgrades==0.1
#wsgiref==0.1.2
zope.interface==3.6.1