Fixed Master

This commit is contained in:
Ahmed Bodiwala 2014-01-06 11:05:00 +00:00
commit 4f25f3c887
16 changed files with 152 additions and 100 deletions

2
.gitmodules vendored
View File

@ -6,7 +6,7 @@
url = https://github.com/Tydus/litecoin_scrypt.git
[submodule "externals/stratum"]
path = externals/stratum
url = https://github.com/slush0/stratum.git
url = https://github.com/ahmedbodi/stratum.git
[submodule "externals/quarkcoin-hash"]
path = externals/quarkcoin-hash
url = https://github.com/Neisklar/quarkcoin-hash-python

View File

@ -32,9 +32,9 @@ The goal is to make a reliable stratum mining server for scrypt based coins. Ove
* WDC: WeVFgZQsKSKXGak7NJPp9SrcUexghzTPGJ
* Doge: DLtBRYtNCzfiZfcpUeEr8KPvy5k1aR7jca
*
#Requirements
*stratum-mining* is built in python. I have been testing it with 2.7.3, but it should work with other versions. The requirements for running the software are below.
* Python 2.7+
* python-twisted
* stratum
@ -56,7 +56,8 @@ Other coins have been known to work with this implementation. I have tested with
* OpenSourceCoin
* TekCoin
* Franko
* Quark
* Securecoin
#Installation
The installation of this *stratum-mining* can be found in the Repo Wiki.
@ -64,10 +65,16 @@ The installation of this *stratum-mining* can be found in the Repo Wiki.
#Contact
I am available in the #MPOS, #crypto-expert, #digitalcoin, and #worldcoin channels on freenode.
Although i am willing to provide support through IRC please file issues on the repo.
<<<<<<< HEAD
Issues as a direct result of stratum will be helped with as much as possible
However issues related to a coin daemon's setup and other non stratum issues,
Please research and attempt to debug first.
=======
issues as a direct result of stratum will be helped with as much as possible
However issues related to a coin daemon's setup and other non stratum issues,
Please research and attempt to debug first.
#Credits
* Original version by Slush0 (original stratum code)
@ -76,7 +83,6 @@ Please research and attempt to debug first.
* PoS conversion done by TheSeven
* Multi Algo, Vardiff, DB and MPOS support done by Ahmed_Bodi and Obigal
#License
This software is provides AS-IS without any warranties of any kind. Please use at your own risk.

View File

@ -26,10 +26,7 @@ COINDAEMON_TRUSTED_PASSWORD = 'somepassword'
# For Coins which support TX Messages please enter yes in the TX selection
COINDAEMON_ALGO = 'scrypt'
COINDAEMON_Reward = 'POW'
COINDAEMON_TX_MSG = 'no'
# If you want a TX message in the block if the coin supports it, enter it below
Tx_Message = 'http://github.com/ahmedbodi/stratum-mining'
COINDAEMON_TX = 'no'
# ******************** BASIC SETTINGS ***************
# Backup Coin Daemon address's (consider having at least 1 backup)
# You can have up to 99
@ -45,10 +42,10 @@ Tx_Message = 'http://github.com/ahmedbodi/stratum-mining'
#COINDAEMON_TRUSTED_PASSWORD_2 = 'somepassword'
# ******************** GENERAL SETTINGS ***************
# Set process name of twistd, much more comfortable if you run multiple processes on one machine
STRATUM_MINING_PROCESS_NAME= 'twistd-stratum-mining'
# Enable some verbose debug (logging requests and responses).
DEBUG = False
@ -96,6 +93,7 @@ PASSWORD_SALT = 'some_crazy_string'
DATABASE_DRIVER = 'mysql' # Options: none, sqlite, postgresql or mysql
DATABASE_EXTEND = False # SQLite and PGSQL Only!
# SQLite
DB_SQLITE_FILE = 'pooldb.sqlite'
# Postgresql
@ -111,7 +109,6 @@ DB_MYSQL_USER = 'pooldb'
DB_MYSQL_PASS = '**empty**'
DB_MYSQL_PORT = 3306 # Default port for MySQL
# ******************** Adv. DB Settings *********************
# Don't change these unless you know what you are doing
@ -185,7 +182,11 @@ ENABLE_WORKER_BANNING = True # enable/disable temporary worker banning
WORKER_CACHE_TIME = 600 # How long the worker stats cache is good before we check and refresh
WORKER_BAN_TIME = 300 # How long we temporarily ban worker
INVALID_SHARES_PERCENT = 50 # Allow average invalid shares vary this % before we ban
#Pass scrypt hash to submit block check.
#Use if submit block is returning errors and marking submitted blocks invaild upstream, but the submitted blocks are being a accepted by the coin daemon into the block chain.
BLOCK_CHECK_SCRYPT_HASH = False
# ******************** E-Mail Notification Settings *********************
NOTIFY_EMAIL_TO = '' # Where to send Start/Found block notifications
NOTIFY_EMAIL_TO_DEADMINER = '' # Where to send dead miner notifications

View File

@ -45,7 +45,7 @@ class BitcoinRPC(object):
# Try submitblock if that fails, go to getblocktemplate
try:
log.debug("Submitting Block with Submit Block ")
log.info([block_hex,])
log.debug([block_hex,])
resp = (yield self._call('submitblock', [block_hex,]))
except Exception:
try:

View File

@ -6,6 +6,8 @@ import util
import merkletree
import halfnode
from coinbasetx import CoinbaseTransaction
import lib.logger
log = lib.logger.get_logger('block_template')
import lib.logger
log = lib.logger.get_logger('block_template')
@ -24,6 +26,7 @@ class BlockTemplate(halfnode.CBlock):
def __init__(self, timestamper, coinbaser, job_id):
log.debug("Got To Block_template.py")
log.debug("Got To Block_template.py")
super(BlockTemplate, self).__init__()
self.job_id = job_id

View File

@ -17,8 +17,8 @@ class BlockUpdater(object):
'''
def __init__(self, registry, bitcoin_rpc):
log.debug("Got to Block Updater")
self.bitcoin_rpc = bitcoin_rpc
log.debug("Got To Block Updater")
self.bitcoin_rpc = bitcoin_rpc
self.registry = registry
self.clock = None
self.schedule()

View File

@ -31,58 +31,52 @@ class SimpleCoinbaser(object):
d.addErrback(self._failure)
def _POW_address_check(self, result):
if result['isvalid'] == True:
log.debug("Is Valid = %s" % result['isvalid'])
log.debug("Address = %s " % result['address'])
log.debug("Is Script = %s" % result['isscript'])
log.debug("PubKey = %s " % result['pubkey'])
log.debug("Is Compressed = %s " % result['iscompressed'])
log.debug("Account = %s " % result['account'])
self.address = result['address']
if result['isvalid'] and result['ismine']:
self.is_valid = True
log.info("Wallet address '%s' is valid" % self.address)
if not self.on_load.called:
self.on_load.callback(True)
elif result['isvalid'] and settings.ALLOW_NONLOCAL_WALLET == True :
self.is_valid = True
log.warning("!!! Wallet address '%s' is valid BUT it is not local" % self.address)
if not self.on_load.called:
self.on_load.callback(True)
else:
self.is_valid = False
log.exception("Wallet address '%s' is NOT valid!" % self.address)
def _POS_address_check(self, result):
if result['isvalid'] == True:
log.debug("Is Valid = %s" % result['isvalid'])
log.debug("Address = %s " % result['address'])
log.debug("Is Script = %s" % result['isscript'])
log.debug("PubKey = %s " % result['pubkey'])
log.debug("Is Compressed = %s " % result['iscompressed'])
log.debug("Account = %s " % result['account'])
self.pubkey = result['pubkey']
if result['isvalid'] and result['ismine']:
self.is_valid = True
log.info("Wallet address '%s' is valid" % self.address)
if result['isvalid'] and result['ismine']:
self.is_valid = True
log.info("Coinbase address '%s' is valid" % self.address)
if result['isvalid'] == True:
log.debug("Is Valid = %s" % result['isvalid'])
log.debug("Address = %s " % result['address'])
log.debug("PubKey = %s " % result['pubkey'])
log.debug("Is Compressed = %s " % result['iscompressed'])
log.debug("Account = %s " % result['account'])
self.address = result['address']
if not self.on_load.called:
self.on_load.callback(True)
self.on_load.callback(True)
elif result['isvalid'] and settings.ALLOW_NONLOCAL_WALLET == True :
self.is_valid = True
log.warning("!!! Coinbase address '%s' is valid BUT it is not local" % self.address)
if not self.on_load.called:
self.on_load.callback(True)
else:
self.is_valid = False
log.error("Coinbase address '%s' is NOT valid!" % self.address)
def _POS_address_check(self, result):
if result['isvalid'] and result['ismine']:
self.is_valid = True
log.info("Coinbase address '%s' is valid" % self.address)
if result['isvalid'] == True:
log.debug("Is Valid = %s" % result['isvalid'])
log.debug("Address = %s " % result['address'])
log.debug("PubKey = %s " % result['pubkey'])
log.debug("Is Compressed = %s " % result['iscompressed'])
log.debug("Account = %s " % result['account'])
self.pubkey = result['pubkey']
if not self.on_load.called:
self.on_load.callback(True)
elif result['isvalid'] and settings.ALLOW_NONLOCAL_WALLET == True :
self.is_valid = True
log.warning("!!! Wallet address '%s' is valid BUT it is not local" % self.address)
if not self.on_load.called:
self.on_load.callback(True)
elif result['isvalid'] and settings.ALLOW_NONLOCAL_WALLET == True :
self.is_valid = True
log.warning("!!! Coinbase address '%s' is valid BUT it is not local" % self.address)
self.pubkey = result['pubkey']
if not self.on_load.called:
self.on_load.callback(True)
else:
self.is_valid = False
log.exception("Wallet address '%s' is NOT valid!" % self.address)
self.is_valid = False
#def on_new_block(self):
# pass

View File

@ -6,7 +6,6 @@ import settings
import lib.logger
log = lib.logger.get_logger('coinbasetx')
if settings.COINDAEMON_Reward == 'POW':
class CoinbaseTransaction(halfnode.CTransaction):
'''Construct special transaction used for coinbase tx.
@ -19,7 +18,7 @@ if settings.COINDAEMON_Reward == 'POW':
def __init__(self, timestamper, coinbaser, value, flags, height, data):
super(CoinbaseTransaction, self).__init__()
log.debug("Got to CoinBaseTX")
log.debug("Got to CoinBaseTX")
#self.extranonce = 0
if len(self.extranonce_placeholder) != self.extranonce_size:
@ -40,8 +39,8 @@ if settings.COINDAEMON_Reward == 'POW':
tx_out.nValue = value
tx_out.scriptPubKey = coinbaser.get_script_pubkey()
if settings.COINDAEMON_TX_MSG == 'yes':
self.strTxComment = settings.Tx_Message
if settings.COINDAEMON_TX == 'yes':
self.strTxComment = "http://github.com/ahmedbodi/stratum-mining"
self.vin.append(tx_in)
self.vout.append(tx_out)
@ -88,8 +87,8 @@ elif settings.COINDAEMON_Reward == 'POS':
tx_out.scriptPubKey = coinbaser.get_script_pubkey()
self.nTime = ntime
if settings.COINDAEMON_TX_MSG == 'yes':
self.strTxComment = settings.Tx_Message
if settings.COINDAEMON_SHA256_TX == 'yes':
self.strTxComment = "http://github.com/ahmedbodi/stratum-mining"
self.vin.append(tx_in)
self.vout.append(tx_out)
@ -114,7 +113,7 @@ else:
def __init__(self, timestamper, coinbaser, value, flags, height, data, ntime):
super(CoinbaseTransaction, self).__init__()
log.debug("Got to CoinBaseTX")
log.debug("Got to CoinBaseTX")
#self.extranonce = 0
if len(self.extranonce_placeholder) != self.extranonce_size:

View File

@ -2,7 +2,6 @@ import struct
import lib.logger
log = lib.logger.get_logger('extronance')
class ExtranonceCounter(object):
'''Implementation of a counter producing
unique extranonce across all pool instances.
@ -10,6 +9,7 @@ class ExtranonceCounter(object):
but it can be changed at any time without breaking anything.'''
def __init__(self, instance_id):
log.debug("Got to Extronance Counter")
if instance_id < 0 or instance_id > 31:
raise Exception("Current ExtranonceCounter implementation needs an instance_id in <0, 31>.")
log.debug("Got To Extronance")

View File

@ -28,7 +28,6 @@ elif settings.COINDAEMON_ALGO == 'quark':
import quark_hash
else:
log.debug("########################################### Loading SHA256 Support ######################################################")
pass
if settings.COINDAEMON_Reward == 'POS':
log.debug("########################################### Loading POS Support #########################################################")
@ -37,11 +36,11 @@ else:
log.debug("########################################### Loading POW Support ######################################################")
pass
if settings.COINDAEMON_TX_MSG == 'yes':
log.debug("########################################### Loading Transaction Message Support #########################################################")
log.info(settings.Tx_Message)
if settings.COINDAEMON_TX == 'yes':
log.debug("########################################### Loading SHA256 Transaction Message Support #########################################################")
pass
else:
log.debug("########################################### NOT Loading SHA256 Transaction Message Support ######################################################")
pass
@ -159,7 +158,7 @@ class CTransaction(object):
def __init__(self):
if settings.COINDAEMON_Reward == 'POW':
self.nVersion = 1
if settings.COINDAEMON_TX_MSG == 'yes':
if settings.COINDAEMON_TX == 'yes':
self.nVersion = 2
self.vin = []
self.vout = []
@ -167,15 +166,15 @@ class CTransaction(object):
self.sha256 = None
elif settings.COINDAEMON_Reward == 'POS':
self.nVersion = 1
if settings.COINDAEMON_TX_MSG == 'yes':
if settings.COINDAEMON_TX == 'yes':
self.nVersion = 2
self.nTime = 0
self.vin = []
self.vout = []
self.nLockTime = 0
self.sha256 = None
if settings.COINDAEMON_TX_MSG == 'yes':
self.strTxComment = settings.Tx_Message
if settings.COINDAEMON_TX == 'yes':
self.strTxComment = ""
def deserialize(self, f):
if settings.COINDAEMON_Reward == 'POW':
@ -191,7 +190,7 @@ class CTransaction(object):
self.vout = deser_vector(f, CTxOut)
self.nLockTime = struct.unpack("<I", f.read(4))[0]
self.sha256 = None
if settings.COINDAEMON_TX_MSG == 'yes':
if settings.COINDAEMON_TX == 'yes':
self.strTxComment = deser_string(f)
def serialize(self):
@ -208,7 +207,7 @@ class CTransaction(object):
r += ser_vector(self.vin)
r += ser_vector(self.vout)
r += struct.pack("<I", self.nLockTime)
if settings.COINDAEMON_TX_MSG == 'yes':
if settings.COINDAEMON_TX == 'yes':
r += ser_string(self.strTxComment)
return r

View File

@ -15,7 +15,7 @@ from lib.exceptions import SubmitException
import lib.logger
log = lib.logger.get_logger('template_registry')
log.debug("Got to Template Registry")
from mining.interfaces import Interfaces
from extranonce_counter import ExtranonceCounter
import lib.settings as settings
@ -69,7 +69,7 @@ class TemplateRegistry(object):
def get_last_broadcast_args(self):
'''Returns arguments for mining.notify
from last known template.'''
log.debug("Getting arguments needed for mining.notify")
log.debug("Getting Laat Template")
return self.last_block.broadcast_args
def add_template(self, block,block_height):
@ -268,23 +268,25 @@ class TemplateRegistry(object):
log.info("We found a block candidate! %s" % scrypt_hash_hex)
# Reverse the header and get the potential block hash (for scrypt only)
block_hash_bin = util.doublesha(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ]))
block_hash_hex = block_hash_bin[::-1].encode('hex_codec')
if settings.COINDAEMON_ALGO == 'scrypt' or settings.COINDAEMON_ALGO == 'sha256d':
if settings.COINDAEMON_Reward == 'POW':
block_hash_bin = util.doublesha(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ]))
block_hash_hex = block_hash_bin[::-1].encode('hex_codec')
else:
block_hash_hex = hash_bin[::-1].encode('hex_codec')
# 6. Finalize and serialize block object
job.finalize(merkle_root_int, extranonce1_bin, extranonce2_bin, int(ntime, 16), int(nonce, 16))
if not job.is_valid():
# Should not happen
log.info("Final job validation failed!")
log.exception("FINAL JOB VALIDATION FAILED!(Try enabling/disabling tx messages)")
# 7. Submit block to the network
serialized = binascii.hexlify(job.serialize())
if settings.BLOCK_CHECK_SCRYPT_HASH:
if settings.BLOCK_CHECK_SCRYPT_HASH:
on_submit = self.bitcoin_rpc.submitblock(serialized, scrypt_hash_hex)
else:
on_submit = self.bitcoin_rpc.submitblock(serialized, block_hash_hex)
if on_submit:
self.update_block()

View File

@ -73,6 +73,7 @@ class DB_Mysql():
log.debug(data)
for k, v in enumerate(data):
log.debug(v)
# for database compatibility we are converting our_worker to Y/N format
if v[5]:
v[5] = 'Y'
@ -87,16 +88,17 @@ class DB_Mysql():
VALUES
(FROM_UNIXTIME(%(time)s), %(host)s,
%(uname)s,
%(lres)s, 'N', %(reason)s, %(solution)s, %(difficulty)s)
%(lres)s, %(result)s, %(reason)s, %(solution)s, %(difficulty)s )
""",
{
"time": v[4],
"host": v[6],
"uname": v[0],
"lres": v[5],
"reason": v[9],
"solution": v[2],
"difficulty": v[3]
"time": data[4],
"host": data[6],
"uname": data[0],
"lres": data[5],
"result": data[5],
"reason": data[9],
"solution": data[2],
"difficulty": data[3]
}
)

View File

@ -46,13 +46,14 @@ class DB_Mysql_Vardiff(DB_Mysql.DB_Mysql):
VALUES
(FROM_UNIXTIME(%(time)s), %(host)s,
%(uname)s,
%(lres)s, 'N', %(reason)s, %(solution)s, %(difficulty)s)
%(lres)s, %(result)s, %(reason)s, %(solution)s, %(difficulty)s)
""",
{
"time": v[4],
"host": v[6],
"uname": v[0],
"lres": v[5],
"result": data[5],
"reason": v[9],
"solution": v[2],
"difficulty": v[3]

View File

@ -105,7 +105,7 @@ class Interfaces(object):
share_limiter = None
timestamper = None
template_registry = None
@classmethod
def set_worker_manager(cls, manager):
cls.worker_manager = manager

View File

@ -8,7 +8,6 @@ from interfaces import Interfaces
from subscription import MiningSubscription
from lib.exceptions import SubmitException
import json
import lib.logger
log = lib.logger.get_logger('mining')
@ -45,7 +44,7 @@ class MiningService(GenericService):
log.debug("Server stats request: %s" % serialized)
return '%s' % serialized
@admin
def update_block(self):
'''Connect this RPC call to 'litecoind -blocknotify' for

46
scripts/refreshconfig.sh Normal file
View File

@ -0,0 +1,46 @@
#!/usr/bin/env python
# Send notification to Stratum mining instance add a new litecoind instance to the pool
import socket
import json
import sys
import argparse
import time
start = time.time()
parser = argparse.ArgumentParser(description='Refresh the config of the Stratum instance.')
parser.add_argument('--password', dest='password', type=str, help='use admin password from Stratum server config')
parser.add_argument('--host', dest='host', type=str, default='localhost', help='hostname of Stratum mining instance')
parser.add_argument('--port', dest='port', type=int, default=3333, help='port of Stratum mining instance')
args = parser.parse_args()
if args.password == None:
parser.print_help()
sys.exit()
message = {'id': 1, 'method': 'mining.refresh_config', 'params': []}
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((args.host, args.port))
s.sendall(json.dumps(message)+"\n")
data = s.recv(16000)
s.close()
except IOError:
print "Refresh Config: Cannot connect to the pool"
sys.exit()
for line in data.split("\n"):
if not line.strip():
# Skip last line which doesn't contain any message
continue
message = json.loads(line)
if message['id'] == 1:
if message['result'] == True:
print "Refresh Config: done in %.03f sec" % (time.time() - start)
else:
print "Refresh Config: Error during request:", message['error'][1]
else:
print "Refresh Config: Unexpected message from the server:", message