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:
# scrypt, sha256d, scrypt-jane and quark
# 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
COINDAEMON_ALGO = 'scrypt'
COINDAEMON_Reward = 'POW'
COINDAEMON_TX = 'no'
# ******************** BASIC SETTINGS ***************
# 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_PASSWORD = ''
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 merkletree
import halfnode
from coinbasetx import CoinbaseTransactionPOW
from coinbasetx import CoinbaseTransactionPOS
from coinbasetx import CoinbaseTransaction
import lib.logger
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'] ]
mt = merkletree.MerkleTree(txhashes)
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)
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'])
self.height = data['height']

View File

@ -6,8 +6,8 @@ import settings
import lib.logger
log = lib.logger.get_logger('coinbasetx')
if settings.COINDAEMON_Reward == 'POW':
class CoinbaseTransaction(halfnode.CTransaction):
#if settings.COINDAEMON_Reward == 'POW':
class CoinbaseTransactionPOW(halfnode.CTransaction):
'''Construct special transaction used for coinbase tx.
It also implements quick serialization using pre-cached
scriptSig template.'''
@ -17,8 +17,8 @@ if settings.COINDAEMON_Reward == 'POW':
extranonce_size = struct.calcsize(extranonce_type)
def __init__(self, timestamper, coinbaser, value, flags, height, data):
super(CoinbaseTransaction, self).__init__()
log.debug("Got to CoinBaseTX")
super(CoinbaseTransactionPOW, self).__init__()
log.debug("Got to CoinBaseTX")
#self.extranonce = 0
if len(self.extranonce_placeholder) != self.extranonce_size:
@ -45,7 +45,7 @@ if settings.COINDAEMON_Reward == 'POW':
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)
self._serialized = super(CoinbaseTransactionPOW, self).serialize().split(self.extranonce_placeholder)
def set_extranonce(self, extranonce):
if len(extranonce) != self.extranonce_size:
@ -53,8 +53,56 @@ if settings.COINDAEMON_Reward == 'POW':
(part1, part2) = self.vin[0]._scriptSig_template
self.vin[0].scriptSig = part1 + extranonce + part2
elif settings.COINDAEMON_Reward == 'POS':
class CoinbaseTransaction(halfnode.CTransaction):
#elif settings.COINDAEMON_Reward == 'POS':
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.
It also implements quick serialization using pre-cached
scriptSig template.'''
@ -86,54 +134,6 @@ elif settings.COINDAEMON_Reward == 'POS':
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(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.vin.append(tx_in)
self.vout.append(tx_out)

View File

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

View File

@ -211,15 +211,15 @@ def ser_number(n):
s.append(n)
return bytes(s)
if settings.COINDAEMON_Reward == 'POW':
def script_to_address(addr):
d = address_to_pubkeyhash(addr)
if not d:
raise ValueError('invalid address')
(ver, pubkeyhash) = d
return b'\x76\xa9\x14' + pubkeyhash + b'\x88\xac'
else:
def script_to_pubkey(key):
if len(key) == 66: key = binascii.unhexlify(key)
if len(key) != 33: raise Exception('Invalid Address')
return b'\x21' + key + b'\xac'
#if settings.COINDAEMON_Reward == 'POW':
def script_to_address(addr):
d = address_to_pubkeyhash(addr)
if not d:
raise ValueError('invalid address')
(ver, pubkeyhash) = d
return b'\x76\xa9\x14' + pubkeyhash + b'\x88\xac'
#else:
def script_to_pubkey(key):
if len(key) == 66: key = binascii.unhexlify(key)
if len(key) != 33: raise Exception('Invalid Address')
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
import Queue
import signal
import Cache
import lib.settings as settings
@ -19,8 +20,7 @@ class DBInterface():
self.q = Queue.Queue()
self.queueclock = None
self.usercache = {}
self.clearusercache()
self.cache = Cache.Cache()
self.nextStatsUpdate = 0
@ -67,11 +67,6 @@ class DBInterface():
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):
# This schedule's the Import
if settings.DATABASE_DRIVER == "sqlite":
@ -163,19 +158,16 @@ class DBInterface():
# Force username and password to be strings
username = str(username)
password = str(password)
wid = username + ":-:" + password
if wid in self.usercache:
if not settings.USERS_CHECK_PASSWORD and self.user_exists(username):
return True
elif not settings.USERS_CHECK_PASSWORD and self.user_exists(username):
self.usercache[wid] = 1
elif self.cache.get(username) == password:
return True
elif self.dbi.check_password(username, password):
self.usercache[wid] = 1
self.cache.set(username, password)
return True
elif settings.USERS_AUTOADD == True:
self.insert_user(username, password)
self.usercache[wid] = 1
self.cache.set(username, password)
return True
log.info("Authentication for %s failed" % username)
@ -188,6 +180,8 @@ class DBInterface():
return self.dbi.get_user(id)
def user_exists(self, username):
if self.cache.get(username) is not None:
return True
user = self.dbi.get_user(username)
return user is not None
@ -195,11 +189,13 @@ class DBInterface():
return self.dbi.insert_user(username, password)
def delete_user(self, username):
self.mc.delete(username)
self.usercache = {}
return self.dbi.delete_user(username)
def update_user(self, username, password):
self.usercache = {}
self.mc.delete(username)
self.mc.set(username, password)
return self.dbi.update_user(username, password)
def update_worker_diff(self, username, diff):

View File

@ -59,6 +59,72 @@ class DB_Mysql_Vardiff(DB_Mysql.DB_Mysql):
)
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):
log.debug("Setting difficulty for %s to %s", username, diff)

View File

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