Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4324925782 | ||
|
|
a98fbf6057 | ||
|
|
faebf8b2f7 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,4 +2,3 @@
|
||||
conf/config.py
|
||||
*.log
|
||||
LOG
|
||||
*.bak
|
||||
|
||||
@ -1,9 +0,0 @@
|
||||
language: python
|
||||
python:
|
||||
- "2.6"
|
||||
- "2.7"
|
||||
- "3.2"
|
||||
- "3.3"
|
||||
# command to install dependencies
|
||||
install:
|
||||
- "pip install -r requirements.txt"
|
||||
47
README.md
47
README.md
@ -1,18 +1,20 @@
|
||||
# Description
|
||||
[ ](https://www.codeship.io/projects/12274)
|
||||
#Description
|
||||
Stratum-mining is a pooled mining protocol. It is a replacement for *getwork* based pooling servers by allowing clients to generate work. The stratum protocol is described [here](http://mining.bitcoin.cz/stratum-mining) in full detail.
|
||||
|
||||
This is a implementation of stratum-mining for scrypt based coins. It is compatible with *MPOS* as it complies with the standards of *pushpool*. The end goal is to build on these standards to come up with a more stable solution.
|
||||
|
||||
The goal is to make a reliable stratum mining server for a wide range of coins unlike other forks where the code is limited to specific algorithm's. Over time I will develop this to be more feature rich and very stable. If you would like to see a feature please file a feature request.
|
||||
The goal is to make a reliable stratum mining server for scrypt based coins. Over time I will develop this to be more feature rich and very stable. If you would like to see a feature please file a feature request.
|
||||
|
||||
**NOTE:** This fork is still in development. Many features may be broken. Please report any broken features or issues.
|
||||
|
||||
# Features
|
||||
#Features
|
||||
|
||||
* Stratum Mining Pool
|
||||
* Solved Block Confirmation
|
||||
* Job Based Vardiff support
|
||||
* Solution Block Hash Support
|
||||
* *NEW* SHA256 and Scrypt Algo Support
|
||||
* Log Rotation
|
||||
* Initial low difficulty share confirmation
|
||||
* Multiple *coind* wallets
|
||||
@ -23,14 +25,25 @@ The goal is to make a reliable stratum mining server for a wide range of coins u
|
||||
* Proof Of Work and Proof of Stake Coin Support
|
||||
* Transaction Messaging Support
|
||||
|
||||
#Donations
|
||||
* BTC: 18Xg4qP6RUvpeajanKPt5PDvvcqvU2pP6d
|
||||
* BTE: 8UJLskr8eDYATvYzmaCBw3vbRmeNweT3rW
|
||||
* DGC: DSBb5KmGWYKMJjxk3rETtvpk9sPqgCCYAw
|
||||
* LTC: Lg4kXMqPsmMHrGr81LLe8oHpbsMiWiuMSB
|
||||
* WDC: WeVFgZQsKSKXGak7NJPp9SrcUexghzTPGJ
|
||||
* Doge: DLtBRYtNCzfiZfcpUeEr8KPvy5k1aR7jca
|
||||
* SRC: sMP2wHN5H2ik7FQDPjhSzFZUWux75BYZGe
|
||||
* ARG: AQvXPWVqGzcpH2j2XSRG7X5R9nA3y9D9aQ
|
||||
* CryptsyTradeKey: ec13d183e304326ebd41258d6ae7188e303866fe
|
||||
|
||||
# Requirements
|
||||
|
||||
#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
|
||||
* MySQL Server
|
||||
* CoinD's
|
||||
* SHA256 or Scrypt CoinDaemon
|
||||
|
||||
Other coins have been known to work with this implementation. I have tested with the following coins, but there may be many others that work.
|
||||
|
||||
@ -50,17 +63,25 @@ Other coins have been known to work with this implementation. I have tested with
|
||||
* Quark
|
||||
* Securecoin
|
||||
|
||||
# Installation
|
||||
#Installation
|
||||
|
||||
The installation of this *stratum-mining* can be found in the Repo Wiki.
|
||||
|
||||
# Credits
|
||||
#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.
|
||||
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.
|
||||
|
||||
* Original version by Slush0 and ArtForz (original stratum code)
|
||||
* More Features added by GeneralFault, Wadee Womersley, Viperaus, TheSeven and Moopless
|
||||
* Multi Algo, Vardiff, DB and MPOS support done by Ahmed_Bodi, penner42 and Obigal
|
||||
* Riecoin support implemented by gatra
|
||||
#Credits
|
||||
|
||||
# License
|
||||
This software is provided AS-IS without any warranties of any kind. Please use at your own risk.
|
||||
* Original version by Slush0 (original stratum code)
|
||||
* More Features added by GeneralFault, Wadee Womersley and Moopless
|
||||
* Scrypt conversion from work done by viperaus
|
||||
* 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.
|
||||
|
||||
|
||||
@ -9,33 +9,32 @@ You NEED to set the parameters in BASIC SETTINGS
|
||||
# ******************** BASIC SETTINGS ***************
|
||||
# These are the MUST BE SET parameters!
|
||||
|
||||
CENTRAL_WALLET = 'set_valid_addresss_in_config!' # Local coin address where money goes
|
||||
CENTRAL_WALLET = 'set_valid_addresss_in_config!' # local coin address where money goes
|
||||
|
||||
COINDAEMON_TRUSTED_HOST = 'localhost'
|
||||
COINDAEMON_TRUSTED_PORT = 28332
|
||||
COINDAEMON_TRUSTED_PORT = 8332
|
||||
COINDAEMON_TRUSTED_USER = 'user'
|
||||
COINDAEMON_TRUSTED_PASSWORD = 'somepassword'
|
||||
|
||||
# Coin algorithm is the option used to determine the algorithm used by stratum
|
||||
# Coin Algorithm is the option used to determine the algortithm used by stratum
|
||||
# This currently works with POW and POS coins
|
||||
# The available options are:
|
||||
# scrypt, sha256d, scrypt-jane, skeinhash, quark and riecoin
|
||||
# scrypt, sha256d, scrypt-jane and quark
|
||||
# If the option does not meet either of these criteria stratum defaults to scrypt
|
||||
# For Coins which support TX Messages please enter yes in the TX selection
|
||||
COINDAEMON_ALGO = 'riecoin'
|
||||
COINDAEMON_ALGO = 'scrypt'
|
||||
COINDAEMON_TX = 'no'
|
||||
|
||||
# ******************** BASIC SETTINGS ***************
|
||||
# Backup Coin Daemon address's (consider having at least 1 backup)
|
||||
# You can have up to 99
|
||||
|
||||
#COINDAEMON_TRUSTED_HOST_1 = 'localhost'
|
||||
#COINDAEMON_TRUSTED_PORT_1 = 28332
|
||||
#COINDAEMON_TRUSTED_PORT_1 = 8332
|
||||
#COINDAEMON_TRUSTED_USER_1 = 'user'
|
||||
#COINDAEMON_TRUSTED_PASSWORD_1 = 'somepassword'
|
||||
|
||||
#COINDAEMON_TRUSTED_HOST_2 = 'localhost'
|
||||
#COINDAEMON_TRUSTED_PORT_2 = 28332
|
||||
#COINDAEMON_TRUSTED_PORT_2 = 8332
|
||||
#COINDAEMON_TRUSTED_USER_2 = 'user'
|
||||
#COINDAEMON_TRUSTED_PASSWORD_2 = 'somepassword'
|
||||
|
||||
@ -55,7 +54,7 @@ LOGFILE = None # eg. 'stratum.log'
|
||||
LOGLEVEL = 'DEBUG'
|
||||
# Logging Rotation can be enabled with the following settings
|
||||
# It if not enabled here, you can set up logrotate to rotate the files.
|
||||
# For built in log rotation set LOG_ROTATION = True and configure the variables
|
||||
# For built in log rotation set LOG_ROTATION = True and configrue the variables
|
||||
LOG_ROTATION = True
|
||||
LOG_SIZE = 10485760 # Rotate every 10M
|
||||
LOG_RETENTION = 10 # Keep 10 Logs
|
||||
@ -66,6 +65,7 @@ LOG_RETENTION = 10 # Keep 10 Logs
|
||||
THREAD_POOL_SIZE = 300
|
||||
|
||||
# ******************** TRANSPORTS *********************
|
||||
|
||||
# Hostname or external IP to expose
|
||||
HOSTNAME = 'localhost'
|
||||
|
||||
@ -87,8 +87,9 @@ LISTEN_WSS_TRANSPORT = None
|
||||
PASSWORD_SALT = 'some_crazy_string'
|
||||
|
||||
# ******************** Database *********************
|
||||
DATABASE_DRIVER = 'mysql' # Options: none, sqlite, postgresql or mysql
|
||||
DATABASE_EXTEND = False # SQLite and PGSQL Only!
|
||||
|
||||
DATABASE_DRIVER = 'mysql' # Options: none, sqlite, postgresql or mysql
|
||||
DATABASE_EXTEND = False # SQLite and PGSQL Only!
|
||||
|
||||
# SQLite
|
||||
DB_SQLITE_FILE = 'pooldb.sqlite'
|
||||
@ -103,7 +104,7 @@ DB_MYSQL_HOST = 'localhost'
|
||||
DB_MYSQL_DBNAME = 'pooldb'
|
||||
DB_MYSQL_USER = 'pooldb'
|
||||
DB_MYSQL_PASS = '**empty**'
|
||||
DB_MYSQL_PORT = 3306 # Default port for MySQL
|
||||
DB_MYSQL_PORT = 3306 # Default port for MySQL
|
||||
|
||||
# ******************** Adv. DB Settings *********************
|
||||
# Don't change these unless you know what you are doing
|
||||
@ -119,7 +120,7 @@ DB_USERCACHE_TIME = 600 # How long the usercache is good for before we r
|
||||
# ******************** Pool Settings *********************
|
||||
|
||||
# User Auth Options
|
||||
USERS_AUTOADD = False # Automatically add users to database when they connect.
|
||||
USERS_AUTOADD = False # Automatically add users to db when they connect.
|
||||
# This basically disables User Auth for the pool.
|
||||
USERS_CHECK_PASSWORD = False # Check the workers password? (Many pools don't)
|
||||
|
||||
@ -128,69 +129,78 @@ COINBASE_EXTRAS = '/stratumPool/' # Extra Descriptive String to incorp
|
||||
ALLOW_NONLOCAL_WALLET = False # Allow valid, but NON-Local wallet's
|
||||
|
||||
# Coin Daemon communication polling settings (In Seconds)
|
||||
PREVHASH_REFRESH_INTERVAL = 5 # How often to check for new Blocks
|
||||
# If using the blocknotify script (recommended) set = to MERKLE_REFRESH_INTERVAL
|
||||
# (No reason to poll if we're getting pushed notifications)
|
||||
MERKLE_REFRESH_INTERVAL = 60 # How often check memorypool
|
||||
# This effectively resets the template and incorporates new transactions.
|
||||
# This should be "slow"
|
||||
PREVHASH_REFRESH_INTERVAL = 5 # How often to check for new Blocks
|
||||
# If using the blocknotify script (recommended) set = to MERKLE_REFRESH_INTERVAL
|
||||
# (No reason to poll if we're getting pushed notifications)
|
||||
MERKLE_REFRESH_INTERVAL = 60 # How often check memorypool
|
||||
# This effectively resets the template and incorporates new transactions.
|
||||
# This should be "slow"
|
||||
|
||||
INSTANCE_ID = 31 # Used for extranonce and needs to be 0-31
|
||||
INSTANCE_ID = 31 # Used for extranonce and needs to be 0-31
|
||||
|
||||
# ******************** Pool Difficulty Settings *********************
|
||||
VDIFF_X2_TYPE = True # Powers of 2 e.g. 2,4,8,16,32,64,128,256,512,1024
|
||||
VDIFF_FLOAT = False # Use float difficulty
|
||||
VDIFF_X2_TYPE = True # powers of 2 e.g. 2,4,8,16,32,64,128,256,512,1024
|
||||
VDIFF_FLOAT = False # Use float difficulty
|
||||
|
||||
# Pool Target (Base Difficulty)
|
||||
POOL_TARGET = 4 # Pool-wide difficulty target int >= 1
|
||||
POOL_TARGET = 32 # Pool-wide difficulty target int >= 1
|
||||
|
||||
# Variable Difficulty Enable
|
||||
VARIABLE_DIFF = True # Master variable difficulty enable
|
||||
VARIABLE_DIFF = True # Master variable difficulty enable
|
||||
|
||||
# Variable diff tuning variables
|
||||
#VARDIFF will start at the POOL_TARGET. It can go as low as the VDIFF_MIN and as high as min(VDIFF_MAX or coindaemons difficulty)
|
||||
USE_COINDAEMON_DIFF = False # Set the maximum difficulty to the coindaemon difficulty.
|
||||
DIFF_UPDATE_FREQUENCY = 86400 # Update the coindaemon difficulty once a day for the VARDIFF maximum
|
||||
VDIFF_MIN_TARGET = 16 # Minimum target difficulty
|
||||
VDIFF_MAX_TARGET = 1024 # Maximum target difficulty
|
||||
VDIFF_TARGET_TIME = 15 # Target time per share (i.e. try to get 1 share per this many seconds)
|
||||
VDIFF_RETARGET_TIME = 120 # Check to see if we should retarget this often
|
||||
VDIFF_VARIANCE_PERCENT = 30 # Allow average time to very this % from target without retarget
|
||||
#VARDIFF will start at the POOL_TARGET. It can go as low as the VDIFF_MIN and as high as min(VDIFF_MAX or Liteconin's difficulty)
|
||||
USE_COINDAEMON_DIFF = False # Set the maximum difficulty to the litecoin difficulty.
|
||||
DIFF_UPDATE_FREQUENCY = 86400 # Update the litecoin difficulty once a day for the VARDIFF maximum
|
||||
VDIFF_MIN_TARGET = 16 # Minimum Target difficulty
|
||||
VDIFF_MAX_TARGET = 1024 # Maximum Target difficulty
|
||||
VDIFF_TARGET_TIME = 15 # Target time per share (i.e. try to get 1 share per this many seconds)
|
||||
VDIFF_RETARGET_TIME = 120 # Check to see if we should retarget this often
|
||||
VDIFF_VARIANCE_PERCENT = 30 # Allow average time to very this % from target without retarget
|
||||
|
||||
# Allow external setting of worker difficulty, checks pool_worker table datarow[6] position for target difficulty
|
||||
# if present or else defaults to pool target, over rides all other difficulty settings, no checks are made
|
||||
# for min or max limits this should be done by your front end software
|
||||
#for min or max limits this sould be done by your front end software
|
||||
ALLOW_EXTERNAL_DIFFICULTY = False
|
||||
|
||||
#### Advanced Option #####
|
||||
# For backwards compatibility, we send the scrypt hash to the solutions column in the shares table
|
||||
# For block confirmation, we have an option to send the block hash in
|
||||
# Please make sure your front end is compatible with the block hash in the solutions table.
|
||||
# For People using the MPOS frontend enabling this is recommended. It allows the frontend to compare the block hash to the coin daemon reducing the likelihood of missing share error's for blocks
|
||||
SOLUTION_BLOCK_HASH = True # If enabled, enter the block hash. If false enter the scrypt/sha hash into the shares table
|
||||
# For People using the MPOS frontend enabling this is recommended. It allows the frontend to compare the block hash to the coin daemon reducing the liklihood of missing share error's for blocks
|
||||
SOLUTION_BLOCK_HASH = True # If enabled, enter the block hash. If false enter the scrypt/sha hash into the shares table
|
||||
|
||||
#Pass scrypt hash to submit block check.
|
||||
#Use if submit block is returning errors and marking submitted blocks invalid upstream, but the submitted blocks are being a accepted by the coin daemon into the block chain.
|
||||
#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
|
||||
|
||||
# ******************** Worker Ban Options *********************
|
||||
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
|
||||
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
|
||||
|
||||
# ******************** E-Mail Notification Settings *********************
|
||||
NOTIFY_EMAIL_TO = '' # Where to send Start/Found block notifications
|
||||
NOTIFY_EMAIL_TO_DEADMINER = '' # Where to send dead miner notifications
|
||||
NOTIFY_EMAIL_FROM = 'root@localhost' # Sender address
|
||||
NOTIFY_EMAIL_SERVER = 'localhost' # E-Mail sender
|
||||
NOTIFY_EMAIL_USERNAME = '' # E-Mail server SMTP logon
|
||||
NOTIFY_EMAIL_PASSWORD = ''
|
||||
NOTIFY_EMAIL_USETLS = True
|
||||
NOTIFY_ADMIN = 'admin@domain.com'
|
||||
NOTIFY_EMAIL_FROM = 'root@localhost' # Sender address
|
||||
NOTIFY_EMAIL_SERVER = 'localhost' # SMTP Server
|
||||
NOTIFY_EMAIL_SERVER_PORT = '587' # SMTP Port
|
||||
NOTIFY_EMAIL_USERNAME = '' # SMTP Login
|
||||
NOTIFY_EMAIL_PASSWORD = '' # SMTP Password
|
||||
NOTIFY_EMAIL_USETLS = True # WIP
|
||||
NOTIFY_DEADMINER_INTERVAL = 600 # Notify Dead Miners every x seconds
|
||||
NOTIFY_MAX_EMAILS = 10 # Max emails sent per user
|
||||
|
||||
# ******************** Memcache Settings *********************
|
||||
#### 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
|
||||
MEMCACHE_HOST = "localhost" # hostname or IP that runs memcached
|
||||
MEMCACHE_PORT = 11211 # Port
|
||||
MEMCACHE_TIMEOUT = 900 # Key timeout
|
||||
MEMCACHE_PREFIX = "stratum_" # Prefix for keys
|
||||
|
||||
######## IRC BOT ##################
|
||||
BOT_ENABLED = True
|
||||
BOT_NETWORK = "irc.freenode.net"
|
||||
BOT_PORT = 6667
|
||||
BOT_NICK = "StratumBot"
|
||||
BOT_CHANNEL = "#cryptopools"
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
# Add conf directory to python path.
|
||||
# Configuration file is standard python module.
|
||||
import os, sys
|
||||
sys.path = [os.path.join(os.getcwd(), 'conf'),os.path.join(os.getcwd(), '.'),os.path.join(os.getcwd(), 'externals', 'stratum-mining-proxy'),] + sys.path
|
||||
sys.path = [os.path.join(os.getcwd(), 'conf'),os.path.join(os.getcwd(), 'externals', 'stratum-mining-proxy'),] + sys.path
|
||||
|
||||
from twisted.internet import defer
|
||||
from twisted.application.service import Application, IProcess
|
||||
|
||||
@ -22,8 +22,7 @@ class BitcoinRPC(object):
|
||||
'Authorization': 'Basic %s' % self.credentials,
|
||||
}
|
||||
client.HTTPClientFactory.noisy = False
|
||||
self.has_submitblock = False
|
||||
|
||||
|
||||
def _call_raw(self, data):
|
||||
client.Headers
|
||||
return client.getPage(
|
||||
@ -40,84 +39,27 @@ class BitcoinRPC(object):
|
||||
'params': params,
|
||||
'id': '1',
|
||||
}))
|
||||
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def check_submitblock(self):
|
||||
def submitblock(self, block_hex, hash_hex):
|
||||
# Try submitblock if that fails, go to getblocktemplate
|
||||
try:
|
||||
log.info("Checking for submitblock")
|
||||
resp = (yield self._call('submitblock', []))
|
||||
self.has_submitblock = True
|
||||
except Exception as e:
|
||||
if (str(e) == "404 Not Found"):
|
||||
log.debug("No submitblock detected.")
|
||||
self.has_submitblock = False
|
||||
elif (str(e) == "500 Internal Server Error"):
|
||||
log.debug("submitblock detected.")
|
||||
self.has_submitblock = True
|
||||
else:
|
||||
log.debug("unknown submitblock check result.")
|
||||
self.has_submitblock = True
|
||||
finally:
|
||||
defer.returnValue(self.has_submitblock)
|
||||
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def submitblock(self, block_hex, hash_hex, scrypt_hex):
|
||||
#try 5 times? 500 Internal Server Error could mean random error or that TX messages setting is wrong
|
||||
attempts = 0
|
||||
while True:
|
||||
attempts += 1
|
||||
if self.has_submitblock == True:
|
||||
try:
|
||||
log.debug("Submitting Block with submitblock: attempt #"+str(attempts))
|
||||
log.debug([block_hex,])
|
||||
resp = (yield self._call('submitblock', [block_hex,]))
|
||||
log.debug("SUBMITBLOCK RESULT: %s", resp)
|
||||
break
|
||||
except Exception as e:
|
||||
if attempts > 4:
|
||||
log.exception("submitblock failed. Problem Submitting block %s" % str(e))
|
||||
log.exception("Try Enabling TX Messages in config.py!")
|
||||
raise
|
||||
else:
|
||||
continue
|
||||
elif self.has_submitblock == False:
|
||||
try:
|
||||
log.debug("Submitting Block with getblocktemplate submit: attempt #"+str(attempts))
|
||||
log.debug([block_hex,])
|
||||
resp = (yield self._call('getblocktemplate', [{'mode': 'submit', 'data': block_hex}]))
|
||||
break
|
||||
except Exception as e:
|
||||
if attempts > 4:
|
||||
log.exception("getblocktemplate submit failed. Problem Submitting block %s" % str(e))
|
||||
log.exception("Try Enabling TX Messages in config.py!")
|
||||
raise
|
||||
else:
|
||||
continue
|
||||
else: # self.has_submitblock = None; unable to detect submitblock, try both
|
||||
try:
|
||||
log.debug("Submitting Block with submitblock")
|
||||
log.debug([block_hex,])
|
||||
resp = (yield self._call('submitblock', [block_hex,]))
|
||||
break
|
||||
except Exception as e:
|
||||
try:
|
||||
log.exception("submitblock Failed, does the coind have submitblock?")
|
||||
log.exception("Trying GetBlockTemplate")
|
||||
resp = (yield self._call('getblocktemplate', [{'mode': 'submit', 'data': block_hex}]))
|
||||
break
|
||||
except Exception as e:
|
||||
if attempts > 4:
|
||||
log.exception("submitblock failed. Problem Submitting block %s" % str(e))
|
||||
log.exception("Try Enabling TX Messages in config.py!")
|
||||
raise
|
||||
else:
|
||||
continue
|
||||
|
||||
log.debug("Submitting Block with Submit Block ")
|
||||
log.debug([block_hex,])
|
||||
resp = (yield self._call('submitblock', [block_hex,]))
|
||||
except Exception:
|
||||
try:
|
||||
log.exception("Submit Block Failed, does the coind have submitblock?")
|
||||
log.exception("Trying GetBlockTemplate")
|
||||
resp = (yield self._call('getblocktemplate', [{'mode': 'submit', 'data': block_hex}]))
|
||||
except Exception as e:
|
||||
log.exception("Both SubmitBlock and GetBlockTemplate failed. Problem Submitting block %s" % str(e))
|
||||
log.exception("Try Enabling TX Messages in config.py!")
|
||||
raise
|
||||
|
||||
if json.loads(resp)['result'] == None:
|
||||
# make sure the block was created.
|
||||
log.info("CHECKING FOR BLOCK AFTER SUBMITBLOCK")
|
||||
defer.returnValue((yield self.blockexists(hash_hex, scrypt_hex)))
|
||||
# make sure the block was created.
|
||||
defer.returnValue((yield self.blockexists(hash_hex)))
|
||||
else:
|
||||
defer.returnValue(False)
|
||||
|
||||
@ -141,16 +83,12 @@ class BitcoinRPC(object):
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def prevhash(self):
|
||||
resp = (yield self._call('getwork', []))
|
||||
try:
|
||||
resp = (yield self._call('getblocktemplate', [{}]))
|
||||
defer.returnValue(json.loads(resp)['result']['previousblockhash'])
|
||||
defer.returnValue(json.loads(resp)['result']['data'][8:72])
|
||||
except Exception as e:
|
||||
if (str(e) == "500 Internal Server Error"):
|
||||
resp = (yield self._call('getblocktemplate', []))
|
||||
defer.returnValue(json.loads(resp)['result']['previousblockhash'])
|
||||
else:
|
||||
log.exception("Cannot decode prevhash %s" % str(e))
|
||||
raise
|
||||
log.exception("Cannot decode prevhash %s" % str(e))
|
||||
raise
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def validateaddress(self, address):
|
||||
@ -163,58 +101,10 @@ class BitcoinRPC(object):
|
||||
defer.returnValue(json.loads(resp)['result'])
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def blockexists(self, hash_hex, scrypt_hex):
|
||||
valid_hash = None
|
||||
blockheight = None
|
||||
# try both hash_hex and scrypt_hex to find block
|
||||
try:
|
||||
resp = (yield self._call('getblock', [hash_hex,]))
|
||||
result = json.loads(resp)['result']
|
||||
if "hash" in result and result['hash'] == hash_hex:
|
||||
log.debug("Block found: %s" % hash_hex)
|
||||
valid_hash = hash_hex
|
||||
if "height" in result:
|
||||
blockheight = result['height']
|
||||
else:
|
||||
defer.returnValue(True)
|
||||
else:
|
||||
log.info("Cannot find block for %s" % hash_hex)
|
||||
defer.returnValue(False)
|
||||
|
||||
except Exception as e:
|
||||
try:
|
||||
resp = (yield self._call('getblock', [scrypt_hex,]))
|
||||
result = json.loads(resp)['result']
|
||||
if "hash" in result and result['hash'] == scrypt_hex:
|
||||
valid_hash = scrypt_hex
|
||||
log.debug("Block found: %s" % scrypt_hex)
|
||||
if "height" in result:
|
||||
blockheight = result['height']
|
||||
else:
|
||||
defer.returnValue(True)
|
||||
else:
|
||||
log.info("Cannot find block for %s" % scrypt_hex)
|
||||
defer.returnValue(False)
|
||||
|
||||
except Exception as e:
|
||||
log.info("Cannot find block for hash_hex %s or scrypt_hex %s" % hash_hex, scrypt_hex)
|
||||
defer.returnValue(False)
|
||||
|
||||
#after we've found the block, check the block with that height in the blockchain to see if hashes match
|
||||
try:
|
||||
log.debug("checking block hash against hash of block height: %s", blockheight)
|
||||
resp = (yield self._call('getblockhash', [blockheight,]))
|
||||
hash = json.loads(resp)['result']
|
||||
log.debug("hash of block of height %s: %s", blockheight, hash)
|
||||
if hash == valid_hash:
|
||||
log.debug("Block confirmed: hash of block matches hash of blockheight")
|
||||
defer.returnValue(True)
|
||||
else:
|
||||
log.debug("Block invisible: hash of block does not match hash of blockheight")
|
||||
defer.returnValue(False)
|
||||
|
||||
except Exception as e:
|
||||
# cannot get blockhash from height; block was created, so return true
|
||||
def blockexists(self, hash_hex):
|
||||
resp = (yield self._call('getblock', [hash_hex,]))
|
||||
if "hash" in json.loads(resp)['result'] and json.loads(resp)['result']['hash'] == hash_hex:
|
||||
log.debug("Block Confirmed: %s" % hash_hex)
|
||||
defer.returnValue(True)
|
||||
else:
|
||||
log.info("Cannot find block for %s" % hash_hex)
|
||||
|
||||
@ -42,7 +42,6 @@ class BitcoinRPCManager(object):
|
||||
if len(self.conns) <= 1:
|
||||
log.error("Problem with Pool 0 -- NO ALTERNATE POOLS!!!")
|
||||
time.sleep(4)
|
||||
self.curr_conn = 0
|
||||
return
|
||||
log.error("Problem with Pool %i Switching to Next!" % (self.curr_conn) )
|
||||
self.curr_conn = self.curr_conn + 1
|
||||
@ -90,17 +89,11 @@ class BitcoinRPCManager(object):
|
||||
return self.conns[self.curr_conn]._call(method,params)
|
||||
except:
|
||||
self.next_connection()
|
||||
def check_submitblock(self):
|
||||
while True:
|
||||
try:
|
||||
return self.conns[self.curr_conn].check_submitblock()
|
||||
except:
|
||||
self.next_connection()
|
||||
|
||||
def submitblock(self, block_hex, hash_hex, scrypt_hex):
|
||||
def submitblock(self, block_hex, hash_hex):
|
||||
while True:
|
||||
try:
|
||||
return self.conns[self.curr_conn].submitblock(block_hex, hash_hex, scrypt_hex)
|
||||
return self.conns[self.curr_conn].submitblock(block_hex, hash_hex)
|
||||
except:
|
||||
self.next_connection()
|
||||
|
||||
|
||||
@ -69,7 +69,6 @@ class BlockTemplate(halfnode.CBlock):
|
||||
self.nVersion = data['version']
|
||||
self.hashPrevBlock = int(data['previousblockhash'], 16)
|
||||
self.nBits = int(data['bits'], 16)
|
||||
|
||||
self.hashMerkleRoot = 0
|
||||
self.nTime = 0
|
||||
self.nNonce = 0
|
||||
@ -140,12 +139,8 @@ class BlockTemplate(halfnode.CBlock):
|
||||
r = struct.pack(">i", self.nVersion)
|
||||
r += self.prevhash_bin
|
||||
r += util.ser_uint256_be(merkle_root_int)
|
||||
if settings.COINDAEMON_ALGO == 'riecoin':
|
||||
r += struct.pack(">I", self.nBits)
|
||||
r += ntime_bin
|
||||
else:
|
||||
r += ntime_bin
|
||||
r += struct.pack(">I", self.nBits)
|
||||
r += ntime_bin
|
||||
r += struct.pack(">I", self.nBits)
|
||||
r += nonce_bin
|
||||
return r
|
||||
|
||||
|
||||
@ -40,7 +40,7 @@ class CoinbaseTransactionPOW(halfnode.CTransaction):
|
||||
tx_out.scriptPubKey = coinbaser.get_script_pubkey()
|
||||
|
||||
if settings.COINDAEMON_TX == 'yes':
|
||||
self.strTxComment = "RanchiMall mining"
|
||||
self.strTxComment = "http://github.com/ahmedbodi/stratum-mining"
|
||||
self.vin.append(tx_in)
|
||||
self.vout.append(tx_out)
|
||||
|
||||
@ -88,7 +88,7 @@ class CoinbaseTransactionPOS(halfnode.CTransaction):
|
||||
|
||||
self.nTime = ntime
|
||||
if settings.COINDAEMON_SHA256_TX == 'yes':
|
||||
self.strTxComment = "RanchiMall mining"
|
||||
self.strTxComment = "http://github.com/ahmedbodi/stratum-mining"
|
||||
self.vin.append(tx_in)
|
||||
self.vout.append(tx_out)
|
||||
|
||||
|
||||
@ -109,7 +109,7 @@ COINDAEMON_TRUSTED_PASSWORD = '***somepassword***'
|
||||
# Until AutoReward Selecting Code has been implemented the below options are us$
|
||||
# For Reward type there is POW and POS. please ensure you choose the currect ty$
|
||||
# For SHA256 PoS Coins which support TX Messages please enter yes in the TX sel$
|
||||
COINDAEMON_ALGO = 'riecoin'
|
||||
COINDAEMON_ALGO = 'scrypt'
|
||||
COINDAEMON_Reward = 'POW'
|
||||
COINDAEMON_SHA256_TX = 'yes'
|
||||
|
||||
|
||||
@ -239,8 +239,6 @@ class CBlock(object):
|
||||
self.scrypt = None
|
||||
elif settings.COINDAEMON_ALGO == 'quark':
|
||||
self.quark = None
|
||||
elif settings.COINDAEMON_ALGO == 'riecoin':
|
||||
self.riecoin = None
|
||||
else: pass
|
||||
if settings.COINDAEMON_Reward == 'POS':
|
||||
self.signature = b""
|
||||
@ -250,14 +248,9 @@ class CBlock(object):
|
||||
self.nVersion = struct.unpack("<i", f.read(4))[0]
|
||||
self.hashPrevBlock = deser_uint256(f)
|
||||
self.hashMerkleRoot = deser_uint256(f)
|
||||
if settings.COINDAEMON_ALGO == 'riecoin':
|
||||
self.nBits = struct.unpack("<I", f.read(4))[0]
|
||||
self.nTime = struct.unpack("<II", f.read(8))[0]
|
||||
self.nNonce = struct.unpack("<IIIIIIII", f.read(32))[0]
|
||||
else:
|
||||
self.nTime = struct.unpack("<I", f.read(4))[0]
|
||||
self.nBits = struct.unpack("<I", f.read(4))[0]
|
||||
self.nNonce = struct.unpack("<I", f.read(4))[0]
|
||||
self.nTime = struct.unpack("<I", f.read(4))[0]
|
||||
self.nBits = struct.unpack("<I", f.read(4))[0]
|
||||
self.nNonce = struct.unpack("<I", f.read(4))[0]
|
||||
self.vtx = deser_vector(f, CTransaction)
|
||||
if settings.COINDAEMON_Reward == 'POS':
|
||||
self.signature = deser_string(f)
|
||||
@ -268,14 +261,9 @@ class CBlock(object):
|
||||
r.append(struct.pack("<i", self.nVersion))
|
||||
r.append(ser_uint256(self.hashPrevBlock))
|
||||
r.append(ser_uint256(self.hashMerkleRoot))
|
||||
if settings.COINDAEMON_ALGO == 'riecoin':
|
||||
r.append(struct.pack("<I", self.nBits))
|
||||
r.append(struct.pack("<Q", self.nTime))
|
||||
r.append(ser_uint256(self.nNonce))
|
||||
else:
|
||||
r.append(struct.pack("<I", self.nTime))
|
||||
r.append(struct.pack("<I", self.nBits))
|
||||
r.append(struct.pack("<I", self.nNonce))
|
||||
r.append(struct.pack("<I", self.nTime))
|
||||
r.append(struct.pack("<I", self.nBits))
|
||||
r.append(struct.pack("<I", self.nNonce))
|
||||
r.append(ser_vector(self.vtx))
|
||||
if settings.COINDAEMON_Reward == 'POS':
|
||||
r.append(ser_string(self.signature))
|
||||
@ -306,18 +294,6 @@ class CBlock(object):
|
||||
r.append(struct.pack("<I", self.nNonce))
|
||||
self.quark = uint256_from_str(quark_hash.getPoWHash(''.join(r)))
|
||||
return self.quark
|
||||
elif settings.COINDAEMON_ALGO == 'riecoin':
|
||||
def calc_riecoin(self):
|
||||
if self.riecoin is None:
|
||||
r = []
|
||||
r.append(struct.pack("<i", self.nVersion))
|
||||
r.append(ser_uint256(self.hashPrevBlock))
|
||||
r.append(ser_uint256(self.hashMerkleRoot))
|
||||
r.append(struct.pack("<I", self.nBits))
|
||||
r.append(struct.pack("<Q", self.nTime))
|
||||
sha256 = uint256_from_str(SHA256.new(SHA256.new(''.join(r)).digest()).digest())
|
||||
self.riecoin = riecoinPoW( sha256, uint256_from_compact(self.nBits), self.nNonce )
|
||||
return self.riecoin
|
||||
else:
|
||||
def calc_sha256(self):
|
||||
if self.sha256 is None:
|
||||
@ -333,23 +309,15 @@ class CBlock(object):
|
||||
|
||||
|
||||
def is_valid(self):
|
||||
if settings.COINDAEMON_ALGO == 'riecoin':
|
||||
self.calc_riecoin()
|
||||
elif settings.COINDAEMON_ALGO == 'scrypt':
|
||||
if settings.COINDAEMON_ALGO == 'scrypt':
|
||||
self.calc_scrypt()
|
||||
elif settings.COINDAEMON_ALGO == 'quark':
|
||||
self.calc_quark()
|
||||
else:
|
||||
self.calc_sha256()
|
||||
|
||||
if settings.COINDAEMON_ALGO == 'riecoin':
|
||||
target = settings.POOL_TARGET
|
||||
else:
|
||||
target = uint256_from_compact(self.nBits)
|
||||
target = uint256_from_compact(self.nBits)
|
||||
|
||||
if settings.COINDAEMON_ALGO == 'riecoin':
|
||||
if self.riecoin < target:
|
||||
return False
|
||||
if settings.COINDAEMON_ALGO == 'scrypt':
|
||||
if self.scrypt > target:
|
||||
return False
|
||||
|
||||
63
lib/ircbot.py
Normal file
63
lib/ircbot.py
Normal file
@ -0,0 +1,63 @@
|
||||
import base64
|
||||
import gc
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
import sys
|
||||
import time
|
||||
import signal
|
||||
import traceback
|
||||
import urlparse
|
||||
import twisted
|
||||
import settings
|
||||
#if settings.BOT_ENABLED:
|
||||
from twisted.words.protocols import irc
|
||||
class IRCClient(irc.IRCClient):
|
||||
nickname = settings.BOT_NICK
|
||||
channel = settings.BOT_CHANNEL
|
||||
|
||||
def lineReceived(self, line):
|
||||
log.debug(line)
|
||||
irc.IRCClient.lineReceived(self, line)
|
||||
|
||||
def signedOn(self):
|
||||
self.in_channel = False
|
||||
irc.IRCClient.signedOn(self)
|
||||
self.factory.resetDelay()
|
||||
self.join(self.channel)
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def new_share(share):
|
||||
if not self.in_channel:
|
||||
return
|
||||
if share.pow_hash <= share.header['bits'].target and abs(share.timestamp - time.time()) < 10*60:
|
||||
yield deferral.sleep(random.expovariate(1/60))
|
||||
message = '\x02%s BLOCK FOUND by %s! %s%064x' % (net.NAME.upper(), bitcoin_data.script2_to_address(share.new_script, net.PARENT), net.PARENT.BLOCK_EXPLORER_URL_PREFIX, share.header_hash)
|
||||
if all('%x' % (share.header_hash,) not in old_message for old_message in self.recent_messages):
|
||||
self.say(self.channel, message)
|
||||
self._remember_message(message)
|
||||
self.watch_id = node.tracker.verified.added.watch(new_share)
|
||||
self.recent_messages = []
|
||||
|
||||
def joined(self, channel):
|
||||
self.in_channel = True
|
||||
|
||||
def left(self, channel):
|
||||
self.in_channel = False
|
||||
|
||||
def _remember_message(self, message):
|
||||
self.recent_messages.append(message)
|
||||
while len(self.recent_messages) > 100:
|
||||
self.recent_messages.pop(0)
|
||||
|
||||
def privmsg(self, user, channel, message):
|
||||
if channel == self.channel:
|
||||
self._remember_message(message)
|
||||
|
||||
def connectionLost(self, reason):
|
||||
node.tracker.verified.added.unwatch(self.watch_id)
|
||||
print 'IRC connection lost:', reason.getErrorMessage()
|
||||
|
||||
class IRCClientFactory(protocol.ReconnectingClientFactory):
|
||||
protocol = IRCClient
|
||||
reactor.connectTCP(settings.BOT_NETWORK,settings.BOT_PORT, IRCClientFactory())
|
||||
@ -1,44 +0,0 @@
|
||||
|
||||
if args.irc_announce:
|
||||
from twisted.words.protocols import irc
|
||||
class IRCClient(irc.IRCClient):
|
||||
nickname = 'xpool%02i' % (random.randrange(100),)
|
||||
channel = net.ANNOUNCE_CHANNEL
|
||||
def lineReceived(self, line):
|
||||
if p2pool.DEBUG:
|
||||
print repr(line)
|
||||
irc.IRCClient.lineReceived(self, line)
|
||||
def signedOn(self):
|
||||
self.in_channel = False
|
||||
irc.IRCClient.signedOn(self)
|
||||
self.factory.resetDelay()
|
||||
self.join(self.channel)
|
||||
@defer.inlineCallbacks
|
||||
def new_share(share):
|
||||
if not self.in_channel:
|
||||
return
|
||||
if share.pow_hash <= share.header['bits'].target and abs(share.timestamp - time.time()) < 10*60:
|
||||
yield deferral.sleep(random.expovariate(1/60))
|
||||
message = '\x02%s BLOCK FOUND by %s! %s%064x' % (net.NAME.upper(), bitcoin_data.script2_to_address(share.new_script, net.PARENT), net.PARENT.BLOCK_EXPLORER_URL_PREFIX, share.header_hash)
|
||||
if all('%x' % (share.header_hash,) not in old_message for old_message in self.recent_messages):
|
||||
self.say(self.channel, message)
|
||||
self._remember_message(message)
|
||||
self.watch_id = node.tracker.verified.added.watch(new_share)
|
||||
self.recent_messages = []
|
||||
def joined(self, channel):
|
||||
self.in_channel = True
|
||||
def left(self, channel):
|
||||
self.in_channel = False
|
||||
def _remember_message(self, message):
|
||||
self.recent_messages.append(message)
|
||||
while len(self.recent_messages) > 100:
|
||||
self.recent_messages.pop(0)
|
||||
def privmsg(self, user, channel, message):
|
||||
if channel == self.channel:
|
||||
self._remember_message(message)
|
||||
def connectionLost(self, reason):
|
||||
node.tracker.verified.added.unwatch(self.watch_id)
|
||||
print 'IRC connection lost:', reason.getErrorMessage()
|
||||
class IRCClientFactory(protocol.ReconnectingClientFactory):
|
||||
protocol = IRCClient
|
||||
reactor.connectTCP("irc.freenode.net", 6667, IRCClientFactory())
|
||||
206
lib/skein.py
206
lib/skein.py
@ -1,206 +0,0 @@
|
||||
# /usr/bin/env python
|
||||
# coding=utf-8
|
||||
|
||||
# Copyright 2010 Jonathan Bowman
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied. See the License for the specific language governing
|
||||
# permissions and limitations under the License.
|
||||
|
||||
"""Pure Python implementation of the Skein 512-bit hashing algorithm"""
|
||||
|
||||
import array
|
||||
import binascii
|
||||
import os
|
||||
import struct
|
||||
|
||||
from threefish import (add64, bigint, bytes2words, Threefish512, words,
|
||||
words2bytes, words_format, xrange,
|
||||
zero_bytes, zero_words)
|
||||
|
||||
# An empty bytestring that behaves itself whether in Python 2 or 3
|
||||
empty_bytes = array.array('B').tostring()
|
||||
|
||||
class Skein512(object):
|
||||
"""Skein 512-bit hashing algorithm
|
||||
|
||||
The message to be hashed may be set as `msg` when initialized, or
|
||||
passed in later using the ``update`` method.
|
||||
|
||||
Use `key` (a bytestring with arbitrary length) for MAC
|
||||
functionality.
|
||||
|
||||
`block_type` will typically be "msg", but may also be one of:
|
||||
"key", "nonce", "cfg_final", or "out_final". These will affect the
|
||||
tweak value passed to the underlying Threefish block cipher. Again,
|
||||
if you don't know which one to choose, "msg" is probably what you
|
||||
want.
|
||||
|
||||
Example:
|
||||
|
||||
>>> Skein512("Hello, world!").hexdigest()
|
||||
'8449f597f1764274f8bf4a03ead22e0404ea2dc63c8737629e6e282303aebfd5dd96f07e21ae2e7a8b2bdfadd445bd1d71dfdd9745c95b0eb05dc01f289ad765'
|
||||
|
||||
"""
|
||||
block_size = 64
|
||||
block_bits = 512
|
||||
block_type = {'key': 0,
|
||||
'nonce': 0x5400000000000000,
|
||||
'msg': 0x7000000000000000,
|
||||
'cfg_final': 0xc400000000000000,
|
||||
'out_final': 0xff00000000000000}
|
||||
|
||||
def __init__(self, msg='', digest_bits=512, key=None,
|
||||
block_type='msg'):
|
||||
self.tf = Threefish512()
|
||||
if key:
|
||||
self.digest_bits = 512
|
||||
self._start_new_type('key')
|
||||
self.update(key)
|
||||
self.tf.key = bytes2words(self.final(False))
|
||||
self.digest_bits = digest_bits
|
||||
self.digest_size = (digest_bits + 7) >> 3
|
||||
self._start_new_type('cfg_final')
|
||||
b = words2bytes((0x133414853,digest_bits,0,0,0,0,0,0))
|
||||
self._process_block(b,32)
|
||||
self._start_new_type(block_type)
|
||||
if msg:
|
||||
self.update(msg)
|
||||
|
||||
def _start_new_type(self, block_type):
|
||||
"""Setup new tweak values and internal buffer.
|
||||
|
||||
Primarily for internal use.
|
||||
|
||||
"""
|
||||
self.buf = empty_bytes
|
||||
self.tf.tweak = words([0, self.block_type[block_type]])
|
||||
|
||||
def _process_block(self, block, byte_count_add):
|
||||
"""Encrypt internal state using Threefish.
|
||||
|
||||
Primarily for internal use.
|
||||
|
||||
"""
|
||||
block_len = len(block)
|
||||
for i in xrange(0,block_len,64):
|
||||
w = bytes2words(block[i:i+64])
|
||||
self.tf.tweak[0] = add64(self.tf.tweak[0], byte_count_add)
|
||||
self.tf.prepare_tweak()
|
||||
self.tf.prepare_key()
|
||||
self.tf.key = self.tf.encrypt_block(w)
|
||||
self.tf._feed_forward(self.tf.key, w)
|
||||
# set second tweak value to ~SKEIN_T1_FLAG_FIRST:
|
||||
self.tf.tweak[1] &= bigint(0xbfffffffffffffff)
|
||||
|
||||
def update(self, msg):
|
||||
"""Update internal state with new data to be hashed.
|
||||
|
||||
`msg` is a bytestring, and should be a bytes object in Python 3
|
||||
and up, or simply a string in Python 2.5 and 2.6.
|
||||
|
||||
"""
|
||||
self.buf += msg
|
||||
buflen = len(self.buf)
|
||||
if buflen > 64:
|
||||
end = -(buflen % 64) or (buflen-64)
|
||||
data = self.buf[0:end]
|
||||
self.buf = self.buf[end:]
|
||||
try:
|
||||
self._process_block(data, 64)
|
||||
except:
|
||||
print(len(data))
|
||||
print(binascii.b2a_hex(data))
|
||||
|
||||
def final(self, output=True):
|
||||
"""Return hashed data as bytestring.
|
||||
|
||||
`output` is primarily for internal use. It should only be False
|
||||
if you have a clear reason for doing so.
|
||||
|
||||
This function can be called as either ``final`` or ``digest``.
|
||||
|
||||
"""
|
||||
self.tf.tweak[1] |= bigint(0x8000000000000000) # SKEIN_T1_FLAG_FINAL
|
||||
buflen = len(self.buf)
|
||||
self.buf += zero_bytes[:64-buflen]
|
||||
|
||||
self._process_block(self.buf, buflen)
|
||||
|
||||
if not output:
|
||||
hash_val = words2bytes(self.tf.key)
|
||||
else:
|
||||
hash_val = empty_bytes
|
||||
self.buf = zero_bytes[:]
|
||||
key = self.tf.key[:] # temporary copy
|
||||
i=0
|
||||
while i*64 < self.digest_size:
|
||||
self.buf = words_format[1].pack(i) + self.buf[8:]
|
||||
self.tf.tweak = [0, self.block_type['out_final']]
|
||||
self._process_block(self.buf, 8)
|
||||
n = self.digest_size - i*64
|
||||
if n >= 64:
|
||||
n = 64
|
||||
hash_val += words2bytes(self.tf.key)[0:n]
|
||||
self.tf.key = key
|
||||
i+=1
|
||||
return hash_val
|
||||
|
||||
digest = final
|
||||
|
||||
def hexdigest(self):
|
||||
"""Return a hexadecimal representation of the hashed data"""
|
||||
return binascii.b2a_hex(self.digest())
|
||||
|
||||
class Skein512Random(Skein512):
|
||||
"""A Skein-based pseudo-random bytestring generator.
|
||||
|
||||
If `seed` is unspecified, ``os.urandom`` will be used to provide the
|
||||
seed.
|
||||
|
||||
In case you are using this as an iterator, rather than generating
|
||||
new data at each iteration, a pool of length `queue_size` is
|
||||
generated periodically.
|
||||
|
||||
"""
|
||||
def __init__(self, seed=None, queue_size=512):
|
||||
Skein512.__init__(self, block_type='nonce')
|
||||
self.queue = []
|
||||
self.queue_size = queue_size
|
||||
self.tf.key = zero_words[:]
|
||||
if not seed:
|
||||
seed = os.urandom(100)
|
||||
self.reseed(seed)
|
||||
|
||||
def reseed(self, seed):
|
||||
"""(Re)seed the generator."""
|
||||
self.digest_size = 64
|
||||
self.update(words2bytes(self.tf.key) + seed)
|
||||
self.tf.key = bytes2words(self.final())
|
||||
|
||||
def getbytes(self, request_bytes):
|
||||
"""Return random bytestring of length `request_bytes`."""
|
||||
self.digest_size = 64 + request_bytes
|
||||
self.update(words2bytes(self.tf.key))
|
||||
output = self.final()
|
||||
self.tf.key = bytes2words(output[0:64])
|
||||
return output[64:]
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def next(self):
|
||||
if not self.queue:
|
||||
self.queue = array.array('B', self.getbytes(self.queue_size))
|
||||
return self.queue.pop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
print Skein512('123').hexdigest()
|
||||
@ -1,20 +0,0 @@
|
||||
import hashlib
|
||||
import struct
|
||||
import skein
|
||||
|
||||
def skeinhash(msg):
|
||||
return hashlib.sha256(skein.Skein512(msg[:80]).digest()).digest()
|
||||
|
||||
def skeinhashmid(msg):
|
||||
s = skein.Skein512(msg[:64] + '\x00') # hack to force Skein512.update()
|
||||
return struct.pack('<8Q', *s.tf.key.tolist())
|
||||
|
||||
if __name__ == '__main__':
|
||||
mesg = "dissociative1234dissociative4567dissociative1234dissociative4567dissociative1234"
|
||||
h = skeinhashmid(mesg)
|
||||
print h.encode('hex')
|
||||
print 'ad0d423b18b47f57724e519c42c9d5623308feac3df37aca964f2aa869f170bdf23e97f644e81511df49c59c5962887d17e277e7e8513345137638334c8e59a4' == h.encode('hex')
|
||||
|
||||
h = skeinhash(mesg)
|
||||
print h.encode('hex')
|
||||
print '764da2e768811e91c6c0c649b052b7109a9bc786bce136a59c8d5a0547cddc54' == h.encode('hex')
|
||||
@ -9,8 +9,6 @@ elif settings.COINDAEMON_ALGO == 'scrypt-jane':
|
||||
import yac_scrypt
|
||||
elif settings.COINDAEMON_ALGO == 'quark':
|
||||
import quark_hash
|
||||
elif settings.COINDAEMON_ALGO == 'skeinhash':
|
||||
import skeinhash
|
||||
else: pass
|
||||
from twisted.internet import defer
|
||||
from lib.exceptions import SubmitException
|
||||
@ -65,7 +63,7 @@ class TemplateRegistry(object):
|
||||
def get_new_extranonce1(self):
|
||||
'''Generates unique extranonce1 (e.g. for newly
|
||||
subscribed connection.'''
|
||||
log.debug("Getting Unique Extranonce")
|
||||
log.debug("Getting Unique Extronance")
|
||||
return self.extranonce_counter.get_new_bin()
|
||||
|
||||
def get_last_broadcast_args(self):
|
||||
@ -150,14 +148,10 @@ class TemplateRegistry(object):
|
||||
|
||||
def diff_to_target(self, difficulty):
|
||||
'''Converts difficulty to target'''
|
||||
if settings.COINDAEMON_ALGO == 'scrypt':
|
||||
diff1 = 0x0000ffff00000000000000000000000000000000000000000000000000000000
|
||||
elif settings.COINDAEMON_ALGO == 'scrypt-jane':
|
||||
if settings.COINDAEMON_ALGO == 'scrypt' or 'scrypt-jane':
|
||||
diff1 = 0x0000ffff00000000000000000000000000000000000000000000000000000000
|
||||
elif settings.COINDAEMON_ALGO == 'quark':
|
||||
diff1 = 0x000000ffff000000000000000000000000000000000000000000000000000000
|
||||
elif settings.COINDAEMON_ALGO == 'riecoin':
|
||||
return difficulty
|
||||
else:
|
||||
diff1 = 0x00000000ffff0000000000000000000000000000000000000000000000000000
|
||||
|
||||
@ -206,23 +200,15 @@ class TemplateRegistry(object):
|
||||
raise SubmitException("Job '%s' not found" % job_id)
|
||||
|
||||
# Check if ntime looks correct
|
||||
if settings.COINDAEMON_ALGO == 'riecoin':
|
||||
if len(ntime) != 16:
|
||||
raise SubmitException("Incorrect size of ntime. Expected 16 chars")
|
||||
else:
|
||||
if len(ntime) != 8:
|
||||
raise SubmitException("Incorrect size of ntime. Expected 8 chars")
|
||||
if len(ntime) != 8:
|
||||
raise SubmitException("Incorrect size of ntime. Expected 8 chars")
|
||||
|
||||
if not job.check_ntime(int(ntime, 16)):
|
||||
raise SubmitException("Ntime out of range")
|
||||
|
||||
# Check nonce
|
||||
if settings.COINDAEMON_ALGO == 'riecoin':
|
||||
if len(nonce) != 64:
|
||||
raise SubmitException("Incorrect size of nonce. Expected 64 chars")
|
||||
else:
|
||||
if len(nonce) != 8:
|
||||
raise SubmitException("Incorrect size of nonce. Expected 8 chars")
|
||||
if len(nonce) != 8:
|
||||
raise SubmitException("Incorrect size of nonce. Expected 8 chars")
|
||||
|
||||
# Check for duplicated submit
|
||||
if not job.register_submit(extranonce1_bin, extranonce2, ntime, nonce):
|
||||
@ -237,9 +223,6 @@ class TemplateRegistry(object):
|
||||
extranonce2_bin = binascii.unhexlify(extranonce2)
|
||||
ntime_bin = binascii.unhexlify(ntime)
|
||||
nonce_bin = binascii.unhexlify(nonce)
|
||||
if settings.COINDAEMON_ALGO == 'riecoin':
|
||||
ntime_bin = (''.join([ ntime_bin[(1-i)*4:(1-i)*4+4] for i in range(0, 2) ]))
|
||||
nonce_bin = (''.join([ nonce_bin[(7-i)*4:(7-i)*4+4] for i in range(0, 8) ]))
|
||||
|
||||
# 1. Build coinbase
|
||||
coinbase_bin = job.serialize_coinbase(extranonce1_bin, extranonce2_bin)
|
||||
@ -259,72 +242,55 @@ class TemplateRegistry(object):
|
||||
hash_bin = yac_scrypt.getPoWHash(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ]), int(ntime, 16))
|
||||
elif settings.COINDAEMON_ALGO == 'quark':
|
||||
hash_bin = quark_hash.getPoWHash(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ]))
|
||||
elif settings.COINDAEMON_ALGO == 'skeinhash':
|
||||
hash_bin = skeinhash.skeinhash(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ]))
|
||||
else:
|
||||
hash_bin = util.doublesha(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ]))
|
||||
|
||||
hash_int = util.uint256_from_str(hash_bin)
|
||||
scrypt_hash_hex = "%064x" % hash_int
|
||||
|
||||
if settings.COINDAEMON_ALGO == 'riecoin':
|
||||
# this is kind of an ugly hack: we use hash_int to store the number of primes
|
||||
hash_int = util.riecoinPoW( hash_int, job.target, int(nonce, 16) )
|
||||
|
||||
header_hex = binascii.hexlify(header_bin)
|
||||
if settings.COINDAEMON_ALGO == 'scrypt' or settings.COINDAEMON_ALGO == 'scrypt-jane':
|
||||
header_hex = header_hex+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
|
||||
elif settings.COINDAEMON_ALGO == 'quark':
|
||||
header_hex = header_hex+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
|
||||
elif settings.COINDAEMON_ALGO == 'riecoin':
|
||||
header_hex = header_hex+"00000080000000000000000080030000"
|
||||
else: pass
|
||||
|
||||
target_user = self.diff_to_target(difficulty)
|
||||
if settings.COINDAEMON_ALGO == 'riecoin':
|
||||
if hash_int < target_user:
|
||||
raise SubmitException("Share does not meet target")
|
||||
else:
|
||||
if hash_int > target_user:
|
||||
raise SubmitException("Share is above target")
|
||||
# Mostly for debugging purposes
|
||||
target_info = self.diff_to_target(100000)
|
||||
if hash_int <= target_info:
|
||||
log.info("Yay, share with diff above 100000")
|
||||
if hash_int > target_user:
|
||||
raise SubmitException("Share is above target")
|
||||
|
||||
# Mostly for debugging purposes
|
||||
target_info = self.diff_to_target(100000)
|
||||
if hash_int <= target_info:
|
||||
log.info("Yay, share with diff above 100000")
|
||||
|
||||
# Algebra tells us the diff_to_target is the same as hash_to_diff
|
||||
share_diff = int(self.diff_to_target(hash_int))
|
||||
|
||||
# 5. Compare hash with target of the network
|
||||
isBlockCandidate = False
|
||||
if settings.COINDAEMON_ALGO == 'riecoin':
|
||||
if hash_int == 6:
|
||||
isBlockCandidate = True
|
||||
else:
|
||||
if hash_int <= job.target:
|
||||
isBlockCandidate = True
|
||||
|
||||
if isBlockCandidate == True:
|
||||
if hash_int <= job.target:
|
||||
# Yay! It is block candidate!
|
||||
log.info("We found a block candidate! %s" % scrypt_hash_hex)
|
||||
|
||||
# Reverse the header and get the potential block hash (for scrypt only)
|
||||
if settings.COINDAEMON_ALGO == 'riecoin':
|
||||
block_hash_bin = util.doublesha(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 28) ]))
|
||||
else:
|
||||
block_hash_bin = util.doublesha(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ]))
|
||||
#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')
|
||||
#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.exception("FINAL JOB VALIDATION FAILED!(Try enabling/disabling tx messages)")
|
||||
|
||||
# 7. Submit block to the network
|
||||
serialized = binascii.hexlify(job.serialize())
|
||||
on_submit = self.bitcoin_rpc.submitblock(serialized, block_hash_hex, scrypt_hash_hex)
|
||||
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()
|
||||
|
||||
@ -335,10 +301,7 @@ class TemplateRegistry(object):
|
||||
|
||||
if settings.SOLUTION_BLOCK_HASH:
|
||||
# Reverse the header and get the potential block hash (for scrypt only) only do this if we want to send in the block hash to the shares table
|
||||
if settings.COINDAEMON_ALGO == 'riecoin':
|
||||
block_hash_bin = util.doublesha(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 28) ]))
|
||||
else:
|
||||
block_hash_bin = util.doublesha(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ]))
|
||||
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')
|
||||
return (header_hex, block_hash_hex, share_diff, None)
|
||||
else:
|
||||
|
||||
288
lib/template_registry.py.save
Normal file
288
lib/template_registry.py.save
Normal file
@ -0,0 +1,288 @@
|
||||
import weakref
|
||||
import binascii
|
||||
import util
|
||||
import StringIO
|
||||
import settings
|
||||
if settings.COINDAEMON_ALGO == 'scrypt':
|
||||
import ltc_scrypt
|
||||
else: pass
|
||||
from twisted.internet import defer
|
||||
from lib.exceptions import SubmitException
|
||||
|
||||
import lib.logger
|
||||
log = lib.logger.get_logger('template_registry')
|
||||
|
||||
from mining.interfaces import Interfaces
|
||||
from extranonce_counter import ExtranonceCounter
|
||||
import lib.settings as settings
|
||||
|
||||
|
||||
class JobIdGenerator(object):
|
||||
'''Generate pseudo-unique job_id. It does not need to be absolutely unique,
|
||||
because pool sends "clean_jobs" flag to clients and they should drop all previous jobs.'''
|
||||
counter = 0
|
||||
|
||||
@classmethod
|
||||
def get_new_id(cls):
|
||||
cls.counter += 1
|
||||
if cls.counter % 0xffff == 0:
|
||||
cls.counter = 1
|
||||
return "%x" % cls.counter
|
||||
|
||||
class TemplateRegistry(object):
|
||||
'''Implements the main logic of the pool. Keep track
|
||||
on valid block templates, provide internal interface for stratum
|
||||
service and implements block validation and submits.'''
|
||||
|
||||
def __init__(self, block_template_class, coinbaser, bitcoin_rpc, instance_id,
|
||||
on_template_callback, on_block_callback):
|
||||
self.prevhashes = {}
|
||||
self.jobs = weakref.WeakValueDictionary()
|
||||
|
||||
self.extranonce_counter = ExtranonceCounter(instance_id)
|
||||
self.extranonce2_size = block_template_class.coinbase_transaction_class.extranonce_size \
|
||||
- self.extranonce_counter.get_size()
|
||||
|
||||
self.coinbaser = coinbaser
|
||||
self.block_template_class = block_template_class
|
||||
self.bitcoin_rpc = bitcoin_rpc
|
||||
self.on_block_callback = on_block_callback
|
||||
self.on_template_callback = on_template_callback
|
||||
|
||||
self.last_block = None
|
||||
self.update_in_progress = False
|
||||
self.last_update = None
|
||||
|
||||
# Create first block template on startup
|
||||
self.update_block()
|
||||
|
||||
def get_new_extranonce1(self):
|
||||
'''Generates unique extranonce1 (e.g. for newly
|
||||
subscribed connection.'''
|
||||
return self.extranonce_counter.get_new_bin()
|
||||
|
||||
def get_last_broadcast_args(self):
|
||||
'''Returns arguments for mining.notify
|
||||
from last known template.'''
|
||||
return self.last_block.broadcast_args
|
||||
|
||||
def add_template(self, block,block_height):
|
||||
'''Adds new template to the registry.
|
||||
It also clean up templates which should
|
||||
not be used anymore.'''
|
||||
|
||||
prevhash = block.prevhash_hex
|
||||
|
||||
if prevhash in self.prevhashes.keys():
|
||||
new_block = False
|
||||
else:
|
||||
new_block = True
|
||||
self.prevhashes[prevhash] = []
|
||||
|
||||
# Blocks sorted by prevhash, so it's easy to drop
|
||||
# them on blockchain update
|
||||
self.prevhashes[prevhash].append(block)
|
||||
|
||||
# Weak reference for fast lookup using job_id
|
||||
self.jobs[block.job_id] = block
|
||||
|
||||
# Use this template for every new request
|
||||
self.last_block = block
|
||||
|
||||
# Drop templates of obsolete blocks
|
||||
for ph in self.prevhashes.keys():
|
||||
if ph != prevhash:
|
||||
del self.prevhashes[ph]
|
||||
|
||||
log.info("New template for %s" % prevhash)
|
||||
|
||||
if new_block:
|
||||
# Tell the system about new block
|
||||
# It is mostly important for share manager
|
||||
self.on_block_callback(prevhash, block_height)
|
||||
|
||||
# Everything is ready, let's broadcast jobs!
|
||||
self.on_template_callback(new_block)
|
||||
|
||||
|
||||
#from twisted.internet import reactor
|
||||
#reactor.callLater(10, self.on_block_callback, new_block)
|
||||
|
||||
def update_block(self):
|
||||
'''Registry calls the getblocktemplate() RPC
|
||||
and build new block template.'''
|
||||
|
||||
if self.update_in_progress:
|
||||
# Block has been already detected
|
||||
return
|
||||
|
||||
self.update_in_progress = True
|
||||
self.last_update = Interfaces.timestamper.time()
|
||||
|
||||
d = self.bitcoin_rpc.getblocktemplate()
|
||||
d.addCallback(self._update_block)
|
||||
d.addErrback(self._update_block_failed)
|
||||
|
||||
def _update_block_failed(self, failure):
|
||||
log.error(str(failure))
|
||||
self.update_in_progress = False
|
||||
|
||||
def _update_block(self, data):
|
||||
start = Interfaces.timestamper.time()
|
||||
|
||||
template = self.block_template_class(Interfaces.timestamper, self.coinbaser, JobIdGenerator.get_new_id())
|
||||
print("hit template registry")
|
||||
template.fill_from_rpc(data) template.fill_from_rpc(data)
|
||||
self.add_template(template,data['height'])
|
||||
|
||||
log.info("Update finished, %.03f sec, %d txes" % \
|
||||
(Interfaces.timestamper.time() - start, len(template.vtx)))
|
||||
|
||||
self.update_in_progress = False
|
||||
return data
|
||||
|
||||
def diff_to_target(self, difficulty):
|
||||
'''Converts difficulty to target'''
|
||||
if settings.COINDAEMON_ALGO == 'scrypt':
|
||||
diff1 = 0x0000ffff00000000000000000000000000000000000000000000000000000000
|
||||
else: diff1 = 0x00000000ffff0000000000000000000000000000000000000000000000000000
|
||||
return diff1 / difficulty
|
||||
|
||||
def get_job(self, job_id):
|
||||
'''For given job_id returns BlockTemplate instance or None'''
|
||||
try:
|
||||
j = self.jobs[job_id]
|
||||
except:
|
||||
log.info("Job id '%s' not found" % job_id)
|
||||
return None
|
||||
|
||||
# Now we have to check if job is still valid.
|
||||
# Unfortunately weak references are not bulletproof and
|
||||
# old reference can be found until next run of garbage collector.
|
||||
if j.prevhash_hex not in self.prevhashes:
|
||||
log.info("Prevhash of job '%s' is unknown" % job_id)
|
||||
return None
|
||||
|
||||
if j not in self.prevhashes[j.prevhash_hex]:
|
||||
log.info("Job %s is unknown" % job_id)
|
||||
return None
|
||||
|
||||
return j
|
||||
|
||||
def submit_share(self, job_id, worker_name, session, extranonce1_bin, extranonce2, ntime, nonce,
|
||||
difficulty):
|
||||
'''Check parameters and finalize block template. If it leads
|
||||
to valid block candidate, asynchronously submits the block
|
||||
back to the bitcoin network.
|
||||
|
||||
- extranonce1_bin is binary. No checks performed, it should be from session data
|
||||
- job_id, extranonce2, ntime, nonce - in hex form sent by the client
|
||||
- difficulty - decimal number from session, again no checks performed
|
||||
- submitblock_callback - reference to method which receive result of submitblock()
|
||||
'''
|
||||
|
||||
# Check if extranonce2 looks correctly. extranonce2 is in hex form...
|
||||
if len(extranonce2) != self.extranonce2_size * 2:
|
||||
raise SubmitException("Incorrect size of extranonce2. Expected %d chars" % (self.extranonce2_size*2))
|
||||
|
||||
# Check for job
|
||||
job = self.get_job(job_id)
|
||||
if job == None:
|
||||
raise SubmitException("Job '%s' not found" % job_id)
|
||||
|
||||
# Check if ntime looks correct
|
||||
if len(ntime) != 8:
|
||||
raise SubmitException("Incorrect size of ntime. Expected 8 chars")
|
||||
|
||||
if not job.check_ntime(int(ntime, 16)):
|
||||
raise SubmitException("Ntime out of range")
|
||||
|
||||
# Check nonce
|
||||
if len(nonce) != 8:
|
||||
raise SubmitException("Incorrect size of nonce. Expected 8 chars")
|
||||
|
||||
# Check for duplicated submit
|
||||
if not job.register_submit(extranonce1_bin, extranonce2, ntime, nonce):
|
||||
log.info("Duplicate from %s, (%s %s %s %s)" % \
|
||||
(worker_name, binascii.hexlify(extranonce1_bin), extranonce2, ntime, nonce))
|
||||
raise SubmitException("Duplicate share")
|
||||
|
||||
# Now let's do the hard work!
|
||||
# ---------------------------
|
||||
|
||||
# 0. Some sugar
|
||||
extranonce2_bin = binascii.unhexlify(extranonce2)
|
||||
ntime_bin = binascii.unhexlify(ntime)
|
||||
nonce_bin = binascii.unhexlify(nonce)
|
||||
|
||||
# 1. Build coinbase
|
||||
coinbase_bin = job.serialize_coinbase(extranonce1_bin, extranonce2_bin)
|
||||
coinbase_hash = util.doublesha(coinbase_bin)
|
||||
|
||||
# 2. Calculate merkle root
|
||||
merkle_root_bin = job.merkletree.withFirst(coinbase_hash)
|
||||
merkle_root_int = util.uint256_from_str(merkle_root_bin)
|
||||
|
||||
# 3. Serialize header with given merkle, ntime and nonce
|
||||
header_bin = job.serialize_header(merkle_root_int, ntime_bin, nonce_bin)
|
||||
|
||||
# 4. Reverse header and compare it with target of the user
|
||||
if settings.COINDAEMON_ALGO == 'scrypt':
|
||||
hash_bin = ltc_scrypt.getPoWHash(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ]))
|
||||
else: hash_bin = util.doublesha(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ]))
|
||||
hash_int = util.uint256_from_str(hash_bin)
|
||||
scrypt_hash_hex = "%064x" % hash_int
|
||||
header_hex = binascii.hexlify(header_bin)
|
||||
if settings.COINDAEMON_ALGO == 'scrypt':
|
||||
header_hex = header_hex+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
|
||||
else: pass
|
||||
|
||||
target_user = self.diff_to_target(difficulty)
|
||||
if hash_int > target_user and \
|
||||
( 'prev_jobid' not in session or session['prev_jobid'] < job_id \
|
||||
or 'prev_diff' not in session or hash_int > self.diff_to_target(session['prev_diff']) ):
|
||||
raise SubmitException("Share is above target")
|
||||
|
||||
# Mostly for debugging purposes
|
||||
target_info = self.diff_to_target(100000)
|
||||
if hash_int <= target_info:
|
||||
log.info("Yay, share with diff above 100000")
|
||||
|
||||
# Algebra tells us the diff_to_target is the same as hash_to_diff
|
||||
share_diff = int(self.diff_to_target(hash_int))
|
||||
|
||||
|
||||
# 5. Compare hash with target of the network
|
||||
if hash_int <= job.target:
|
||||
# Yay! It is block candidate!
|
||||
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')
|
||||
|
||||
# 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.error("Final job validation failed!")
|
||||
|
||||
# 7. Submit block to the network
|
||||
serialized = binascii.hexlify(job.serialize())
|
||||
on_submit = self.bitcoin_rpc.submitblock(serialized, block_hash_hex)
|
||||
if on_submit:
|
||||
self.update_block()
|
||||
|
||||
if settings.SOLUTION_BLOCK_HASH:
|
||||
return (header_hex, block_hash_hex, share_diff, on_submit)
|
||||
else:
|
||||
return (header_hex, scrypt_hash_hex, share_diff, on_submit)
|
||||
|
||||
if settings.SOLUTION_BLOCK_HASH:
|
||||
# Reverse the header and get the potential block hash (for scrypt only) only do this if we want to send in the block hash to the shares table
|
||||
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')
|
||||
return (header_hex, block_hash_hex, share_diff, None)
|
||||
else:
|
||||
return (header_hex, scrypt_hash_hex, share_diff, None)
|
||||
147
lib/threefish.py
147
lib/threefish.py
@ -1,147 +0,0 @@
|
||||
# /usr/bin/env python
|
||||
# coding=utf-8
|
||||
|
||||
# Copyright 2010 Jonathan Bowman
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied. See the License for the specific language governing
|
||||
# permissions and limitations under the License.
|
||||
|
||||
"""Pure Python implementation of the Threefish block cipher
|
||||
|
||||
The core of the Skein 512-bit hashing algorithm
|
||||
|
||||
"""
|
||||
from util_numpy import add64, bigint, bytelist, bytes2words, imap, izip, sub64, \
|
||||
SKEIN_KS_PARITY, words, words2bytes, words_format, xrange, zero_bytes, zero_words, RotL_64, RotR_64, xor
|
||||
|
||||
from itertools import cycle
|
||||
|
||||
ROT = bytelist((46, 36, 19, 37,
|
||||
33, 27, 14, 42,
|
||||
17, 49, 36, 39,
|
||||
44, 9, 54, 56,
|
||||
39, 30, 34, 24,
|
||||
13, 50, 10, 17,
|
||||
25, 29, 39, 43,
|
||||
8, 35, 56, 22))
|
||||
|
||||
PERM = bytelist(((0,1),(2,3),(4,5),(6,7),
|
||||
(2,1),(4,7),(6,5),(0,3),
|
||||
(4,1),(6,3),(0,5),(2,7),
|
||||
(6,1),(0,7),(2,5),(4,3)))
|
||||
|
||||
class Threefish512(object):
|
||||
"""The Threefish 512-bit block cipher.
|
||||
|
||||
The key and tweak may be set when initialized (as
|
||||
bytestrings) or after initialization using the ``tweak`` or
|
||||
``key`` properties. When choosing the latter, be sure to call
|
||||
the ``prepare_key`` and ``prepare_tweak`` methods.
|
||||
|
||||
"""
|
||||
def __init__(self, key=None, tweak=None):
|
||||
"""Set key and tweak.
|
||||
|
||||
The key and the tweak will be lists of 8 64-bit words
|
||||
converted from `key` and `tweak` bytestrings, or all
|
||||
zeroes if not specified.
|
||||
|
||||
"""
|
||||
if key:
|
||||
self.key = bytes2words(key)
|
||||
self.prepare_key()
|
||||
else:
|
||||
self.key = words(zero_words[:] + [0])
|
||||
if tweak:
|
||||
self.tweak = bytes2words(tweak, 2)
|
||||
self.prepare_tweak()
|
||||
else:
|
||||
self.tweak = zero_words[:3]
|
||||
|
||||
def prepare_key(self):
|
||||
"""Compute key."""
|
||||
final = reduce(xor, self.key[:8]) ^ SKEIN_KS_PARITY
|
||||
try:
|
||||
self.key[8] = final
|
||||
except IndexError:
|
||||
#self.key.append(final)
|
||||
self.key = words(list(self.key) + [final])
|
||||
|
||||
def prepare_tweak(self):
|
||||
"""Compute tweak."""
|
||||
final = self.tweak[0] ^ self.tweak[1]
|
||||
try:
|
||||
self.tweak[2] = final
|
||||
except IndexError:
|
||||
#self.tweak.append(final)
|
||||
self.tweak = words(list(self.tweak) + [final])
|
||||
|
||||
def encrypt_block(self, plaintext):
|
||||
"""Return 8-word ciphertext, encrypted from plaintext.
|
||||
|
||||
`plaintext` must be a list of 8 64-bit words.
|
||||
|
||||
"""
|
||||
key = self.key
|
||||
tweak = self.tweak
|
||||
state = words(list(imap(add64, plaintext, key[:8])))
|
||||
state[5] = add64(state[5], tweak[0])
|
||||
state[6] = add64(state[6], tweak[1])
|
||||
|
||||
for r,s in izip(xrange(1,19),cycle((0,16))):
|
||||
for i in xrange(16):
|
||||
m,n = PERM[i]
|
||||
state[m] = add64(state[m], state[n])
|
||||
state[n] = RotL_64(state[n], ROT[i+s])
|
||||
state[n] = state[n] ^ state[m]
|
||||
for y in xrange(8):
|
||||
state[y] = add64(state[y], key[(r+y) % 9])
|
||||
state[5] = add64(state[5], tweak[r % 3])
|
||||
state[6] = add64(state[6], tweak[(r+1) % 3])
|
||||
state[7] = add64(state[7], r)
|
||||
|
||||
return state
|
||||
|
||||
def _feed_forward(self, state, plaintext):
|
||||
"""Compute additional step required when hashing.
|
||||
|
||||
Primarily for internal use.
|
||||
|
||||
"""
|
||||
state[:] = list(imap(xor, state, plaintext))
|
||||
|
||||
def decrypt_block(self, ciphertext):
|
||||
"""Return 8-word plaintext, decrypted from plaintext.
|
||||
|
||||
`ciphertext` must be a list of 8 64-bit words.
|
||||
|
||||
"""
|
||||
key = self.key
|
||||
tweak = self.tweak
|
||||
state = ciphertext[:]
|
||||
|
||||
for r,s in izip(xrange(18,0,-1),cycle((16,0))):
|
||||
for y in xrange(8):
|
||||
state[y] = sub64(state[y], key[(r+y) % 9])
|
||||
state[5] = sub64(state[5], tweak[r % 3])
|
||||
state[6] = sub64(state[6], tweak[(r+1) % 3])
|
||||
state[7] = sub64(state[7], r)
|
||||
|
||||
for i in xrange(15,-1,-1):
|
||||
m,n = PERM[i]
|
||||
state[n] = RotR_64(state[m] ^ state[n], ROT[i+s])
|
||||
state[m] = sub64(state[m], state[n])
|
||||
|
||||
result = list(imap(sub64, state, key))
|
||||
result[5] = sub64(result[5], tweak[0])
|
||||
result[6] = sub64(result[6], tweak[1])
|
||||
return result
|
||||
53
lib/util.py
53
lib/util.py
@ -56,10 +56,7 @@ def uint256_from_str_be(s):
|
||||
|
||||
def uint256_from_compact(c):
|
||||
nbytes = (c >> 24) & 0xFF
|
||||
if nbytes <= 3:
|
||||
v = (c & 0xFFFFFFL) >> (8 * (3 - nbytes))
|
||||
else:
|
||||
v = (c & 0xFFFFFFL) << (8 * (nbytes - 3))
|
||||
v = (c & 0xFFFFFFL) << (8 * (nbytes - 3))
|
||||
return v
|
||||
|
||||
def deser_vector(f, c):
|
||||
@ -214,54 +211,6 @@ def ser_number(n):
|
||||
s.append(n)
|
||||
return bytes(s)
|
||||
|
||||
|
||||
def isPrime( n ):
|
||||
if pow( 2, n-1, n ) == 1:
|
||||
return True
|
||||
return False
|
||||
|
||||
def riecoinPoW( hash_int, diff, nNonce ):
|
||||
base = 1 << 8
|
||||
for i in range(256):
|
||||
base = base << 1
|
||||
base = base | (hash_int & 1)
|
||||
hash_int = hash_int >> 1
|
||||
trailingZeros = diff - 1 - 8 - 256
|
||||
if trailingZeros < 16 or trailingZeros > 20000:
|
||||
return 0
|
||||
base = base << trailingZeros
|
||||
|
||||
base += nNonce
|
||||
|
||||
if (base % 210) != 97:
|
||||
return 0
|
||||
|
||||
if not isPrime( base ):
|
||||
return 0
|
||||
primes = 1
|
||||
|
||||
base += 4
|
||||
if isPrime( base ):
|
||||
primes+=1
|
||||
|
||||
base += 2
|
||||
if isPrime( base ):
|
||||
primes+=1
|
||||
|
||||
base += 4
|
||||
if isPrime( base ):
|
||||
primes+=1
|
||||
|
||||
base += 2
|
||||
if isPrime( base ):
|
||||
primes+=1
|
||||
|
||||
base += 4
|
||||
if isPrime( base ):
|
||||
primes+=1
|
||||
|
||||
return primes
|
||||
|
||||
#if settings.COINDAEMON_Reward == 'POW':
|
||||
def script_to_address(addr):
|
||||
d = address_to_pubkeyhash(addr)
|
||||
|
||||
@ -1,100 +0,0 @@
|
||||
# /usr/bin/env python
|
||||
# coding=utf-8
|
||||
|
||||
# Copyright 2010 Jonathan Bowman
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied. See the License for the specific language governing
|
||||
# permissions and limitations under the License.
|
||||
|
||||
"""Various helper functions for handling arrays, etc. using numpy"""
|
||||
|
||||
import struct
|
||||
from operator import xor, add as add64, sub as sub64
|
||||
import numpy as np
|
||||
|
||||
words = np.uint64
|
||||
bytelist = np.uint8
|
||||
bigint = np.uint64
|
||||
|
||||
# working out some differences between Python 2 and 3
|
||||
try:
|
||||
from itertools import imap, izip
|
||||
except ImportError:
|
||||
imap = map
|
||||
izip = zip
|
||||
try:
|
||||
xrange = xrange
|
||||
except:
|
||||
xrange = range
|
||||
|
||||
SKEIN_KS_PARITY = np.uint64(0x1BD11BDAA9FC1A22)
|
||||
|
||||
# zeroed out byte string and list for convenience and performance
|
||||
zero_bytes = struct.pack('64B', *[0] * 64)
|
||||
zero_words = np.zeros(8, dtype=np.uint64)
|
||||
|
||||
# Build structs for conversion appropriate to this system, favoring
|
||||
# native formats if possible for slight performance benefit
|
||||
words_format_tpl = "%dQ"
|
||||
if struct.pack('2B', 0, 1) == struct.pack('=H', 1): # big endian?
|
||||
words_format_tpl = "<" + words_format_tpl # force little endian
|
||||
else:
|
||||
try: # is 64-bit integer native?
|
||||
struct.unpack(words_format_tpl % 2, zero_bytes[:16])
|
||||
except(struct.error): # Use standard instead of native
|
||||
words_format_tpl = "=" + words_format_tpl
|
||||
|
||||
# build structs for one-, two- and eight-word sequences
|
||||
words_format = dict(
|
||||
(i,struct.Struct(words_format_tpl % i)) for i in (1,2,8))
|
||||
|
||||
def bytes2words(data, length=8):
|
||||
"""Return a list of `length` 64-bit words from `data`.
|
||||
|
||||
`data` must consist of `length` * 8 bytes.
|
||||
`length` must be 1, 2, or 8.
|
||||
|
||||
"""
|
||||
return(np.fromstring(data, dtype=np.uint64))
|
||||
|
||||
def words2bytes(data, length=8):
|
||||
"""Return a `length` * 8 byte string from `data`.
|
||||
|
||||
|
||||
`data` must be a list of `length` 64-bit words
|
||||
`length` must be 1, 2, or 8.
|
||||
|
||||
"""
|
||||
try:
|
||||
return(data.tostring())
|
||||
except AttributeError:
|
||||
return(np.uint64(data).tostring())
|
||||
|
||||
def RotL_64(x, N):
|
||||
"""Return `x` rotated left by `N`."""
|
||||
#return (x << np.uint64(N & 63)) | (x >> np.uint64((64-N) & 63))
|
||||
return(np.left_shift(x, (N & 63), dtype=np.uint64) |
|
||||
np.right_shift(x, ((64-N) & 63), dtype=np.uint64))
|
||||
|
||||
def RotR_64(x, N):
|
||||
"""Return `x` rotated right by `N`."""
|
||||
return(np.right_shift(x, (N & 63), dtype=np.uint64) |
|
||||
np.left_shift(x, ((64-N) & 63), dtype=np.uint64))
|
||||
|
||||
def add64(a,b):
|
||||
"""Return a 64-bit integer sum of `a` and `b`."""
|
||||
return(np.add(a, b, dtype=np.uint64))
|
||||
|
||||
def sub64(a,b):
|
||||
"""Return a 64-bit integer difference of `a` and `b`."""
|
||||
return(np.subtract(a, b, dtype=np.uint64))
|
||||
|
||||
@ -5,7 +5,7 @@ import Queue
|
||||
import signal
|
||||
import Cache
|
||||
from sets import Set
|
||||
|
||||
import notify_email
|
||||
import lib.settings as settings
|
||||
|
||||
import lib.logger
|
||||
@ -17,18 +17,17 @@ class DBInterface():
|
||||
|
||||
def init_main(self):
|
||||
self.dbi.check_tables()
|
||||
|
||||
self.q = Queue.Queue()
|
||||
self.queueclock = None
|
||||
|
||||
self.cache = Cache.Cache()
|
||||
|
||||
self.email = notify_email.NOTIFY_EMAIL()
|
||||
self.nextStatsUpdate = 0
|
||||
|
||||
self.scheduleImport()
|
||||
|
||||
self.next_force_import_time = time.time() + settings.DB_LOADER_FORCE_TIME
|
||||
|
||||
self.next_force_notify_time = time.time() + settings.NOTIFY_DEADMINER_INTERVAL
|
||||
signal.signal(signal.SIGINT, self.signal_handler)
|
||||
|
||||
def signal_handler(self, signal, frame):
|
||||
@ -179,13 +178,6 @@ class DBInterface():
|
||||
self.dbi.insert_worker(uid, username, password)
|
||||
self.cache.set(username, password)
|
||||
return True
|
||||
else:
|
||||
self.dbi.insert_user(username, password)
|
||||
if self.dbi.get_uid(username) != False:
|
||||
uid = self.dbi.get_uid(username)
|
||||
self.dbi.insert_worker(uid, username, password)
|
||||
self.cache.set(username, password)
|
||||
return True
|
||||
|
||||
log.info("Authentication for %s failed" % username)
|
||||
return False
|
||||
@ -194,10 +186,7 @@ class DBInterface():
|
||||
return self.dbi.list_users()
|
||||
|
||||
def get_user(self, id):
|
||||
if self.cache.get(id) is None:
|
||||
self.cache.set(id,self.dbi.get_user(id))
|
||||
return self.cache.get(id)
|
||||
|
||||
return self.dbi.get_user(id)
|
||||
|
||||
def user_exists(self, username):
|
||||
if self.cache.get(username) is not None:
|
||||
|
||||
@ -342,13 +342,28 @@ class DB_Mysql():
|
||||
}
|
||||
|
||||
return ret
|
||||
|
||||
def get_uid(self, id_or_username):
|
||||
log.debug("Finding user id of %s", id_or_username)
|
||||
uname = id_or_username.split(".", 1)[0]
|
||||
self.execute("SELECT `id` FROM `accounts` where username = %s", (uname))
|
||||
row = self.dbc.fetchone()
|
||||
|
||||
|
||||
if row is None:
|
||||
return False
|
||||
else:
|
||||
uid = row[0]
|
||||
return uid
|
||||
|
||||
|
||||
def insert_worker(self, account_id, username, password):
|
||||
log.debug("Adding new worker %s", username)
|
||||
query = "INSERT INTO pool_worker"
|
||||
self.execute(query + '(account_id, username, password) VALUES (%s, %s, %s);', (account_id, username, password))
|
||||
self.dbh.commit()
|
||||
return str(username)
|
||||
|
||||
|
||||
|
||||
def close(self):
|
||||
self.dbh.close()
|
||||
|
||||
@ -39,22 +39,6 @@ def setup(on_startup):
|
||||
# - we are not still downloading the blockchain (Sleep)
|
||||
log.info("Connecting to litecoind...")
|
||||
while True:
|
||||
try:
|
||||
result = (yield bitcoin_rpc.check_submitblock())
|
||||
if result == True:
|
||||
log.info("Found submitblock")
|
||||
elif result == False:
|
||||
log.info("Did not find submitblock")
|
||||
else:
|
||||
log.info("unknown submitblock result")
|
||||
except ConnectionRefusedError, e:
|
||||
log.error("Connection refused while trying to connect to the coind (are your COIND_* settings correct?)")
|
||||
reactor.stop()
|
||||
break
|
||||
|
||||
except Exception, e:
|
||||
log.debug(str(e))
|
||||
|
||||
try:
|
||||
result = (yield bitcoin_rpc.getblocktemplate())
|
||||
if isinstance(result, dict):
|
||||
@ -65,11 +49,11 @@ def setup(on_startup):
|
||||
if 'proof-of-stake' in result:
|
||||
settings.COINDAEMON_Reward = 'POS'
|
||||
log.info("Coin detected as POS")
|
||||
break
|
||||
break;
|
||||
else:
|
||||
settings.COINDAEMON_Reward = 'POW'
|
||||
log.info("Coin detected as POW")
|
||||
break
|
||||
break;
|
||||
else:
|
||||
log.error("Block Version mismatch: %s" % result['version'])
|
||||
|
||||
|
||||
@ -80,9 +80,12 @@ class ShareManagerInterface(object):
|
||||
dbi.queue_share([worker_name, block_header, block_hash, difficulty, timestamp, is_valid, ip, self.block_height, self.prev_hash,
|
||||
invalid_reason, share_diff ])
|
||||
|
||||
def on_submit_block(self, is_accepted, worker_name, block_header, block_hash, timestamp, ip, share_diff):
|
||||
def on_submit_block(self, on_submit, worker_name, block_header, block_hash, timestamp, ip, share_diff):
|
||||
(is_accepted, valid_hash) = on_submit
|
||||
if (settings.SOLUTION_BLOCK_HASH):
|
||||
block_hash = valid_hash
|
||||
|
||||
log.info("Block %s %s" % (block_hash, 'ACCEPTED' if is_accepted else 'REJECTED'))
|
||||
dbi.do_import(dbi, True)
|
||||
dbi.found_block([worker_name, block_header, block_hash, -1, timestamp, is_accepted, ip, self.block_height, self.prev_hash, share_diff ])
|
||||
|
||||
class TimestamperInterface(object):
|
||||
|
||||
55
mining/notify_email.py
Normal file
55
mining/notify_email.py
Normal file
@ -0,0 +1,55 @@
|
||||
import os
|
||||
import smtplib
|
||||
import time
|
||||
from email.mime.text import MIMEText
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from stratum import settings
|
||||
|
||||
import stratum.logger
|
||||
log = stratum.logger.get_logger('Notify_Email')
|
||||
|
||||
class NOTIFY_EMAIL():
|
||||
|
||||
def notify_start(self):
|
||||
subject = (' Stratum ALERT: Stratum started!')
|
||||
text = ('Stratum service has started!')
|
||||
message = MIMEText(text, 'plain')
|
||||
self.send_email(settings.NOTIFY_ADMIN,subject,message)
|
||||
|
||||
def notify_found_block(self,worker_name):
|
||||
subject = (' Stratum ALERT: Found Block by ' % worker_name)
|
||||
text = ('%s on Stratum server found a block!' % worker_name)
|
||||
message = MIMEText(text, 'plain')
|
||||
self.send_email(settings.NOTIFY_ADMIN,subject,message)
|
||||
|
||||
def notify_dead_coindaemon(self,worker_name):
|
||||
subject = (' Stratum ALERT: Stratum down!')
|
||||
text = ('Stratum is down!')
|
||||
message = MIMEText(text, 'plain')
|
||||
self.send_email(settings.NOTIFY_ADMIN,subject,message)
|
||||
|
||||
def notify_dead_miner(self,username,email):
|
||||
log.info("Attempting to send email to: %s" % username)
|
||||
subject = (' Stratum ALERT: ' + username + ' not authenticating properly!')
|
||||
text = (' Youre Miner is not authorising With Stratum correctly. please recheck youre worker details and retry')
|
||||
message = MIMEText(text, 'html')
|
||||
self.send_email(email,subject,message)
|
||||
log.info("Sent to %s" % email)
|
||||
|
||||
def send_email(self,to,subject,message):
|
||||
log.info("Send attempt to %s" % to)
|
||||
|
||||
msg = MIMEMultipart('alternative')
|
||||
msg['Subject'] = subject
|
||||
msg['From'] = settings.NOTIFY_EMAIL_FROM
|
||||
msg['To'] = to
|
||||
msg.attach(message)
|
||||
try:
|
||||
s = smtplib.SMTP(settings.NOTIFY_EMAIL_SERVER,settings.NOTIFY_EMAIL_SERVER_PORT)
|
||||
s.login(settings.NOTIFY_EMAIL_USERNAME,settings.NOTIFY_EMAIL_PASSWORD)
|
||||
s.sendmail(msg['From'], msg['To'], msg.as_string())
|
||||
s.quit()
|
||||
except smtplib.SMTPAuthenticationError as e:
|
||||
log.error('Error sending Email: %s' % e[1])
|
||||
except Exception as e:
|
||||
log.error('Error sending Email: %s' % e[0])
|
||||
@ -87,7 +87,7 @@ class MiningService(GenericService):
|
||||
else:
|
||||
session['difficulty'] = settings.POOL_TARGET
|
||||
# worker_log = (valid, invalid, is_banned, diff, is_ext_diff, timestamp)
|
||||
Interfaces.worker_manager.worker_log['authorized'][worker_name] = (0, 0, False, session['difficulty'], is_ext_diff, Interfaces.timestamper.time())
|
||||
Interfaces.worker_manager.worker_log['authorized'][worker_name] = (0, 0, False, session['difficulty'], is_ext_diff, Interfaces.timestamper.time())
|
||||
return True
|
||||
else:
|
||||
ip = self.connection_ref()._get_ip()
|
||||
|
||||
@ -1,34 +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
|
||||
Jinja2==2.7.2
|
||||
Magic-file-extensions==0.2
|
||||
MarkupSafe==0.18
|
||||
MySQL-python==1.2.5
|
||||
PyYAML==3.10
|
||||
Twisted==13.2.0
|
||||
ansible==1.4.5
|
||||
#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.8.4-3
|
||||
cffi==0.8.2
|
||||
cryptography==0.2.2
|
||||
autobahn==0.6.5
|
||||
#chardet==2.0.1
|
||||
defer==1.0.6
|
||||
distribute==0.6.28
|
||||
ecdsa==0.10
|
||||
feedparser==5.1.3
|
||||
feedparser==5.1.2
|
||||
fpconst==0.7.2
|
||||
httplib2==0.8
|
||||
numpy==1.7.1
|
||||
paramiko==1.12.1
|
||||
pyOpenSSL==0.14
|
||||
pyasn1==0.1.7
|
||||
pycparser==2.10
|
||||
pycrypto==2.6.1
|
||||
pylibmc==1.2.3
|
||||
pyserial==2.7
|
||||
python-dateutil==2.1
|
||||
python-memcached==1.53
|
||||
pyxdg==0.25
|
||||
requests==2.2.0
|
||||
simplejson==3.3.3
|
||||
six==1.4.1
|
||||
wsgiref==0.1.2
|
||||
zope.interface==4.1.0
|
||||
stratum==0.2.15
|
||||
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
|
||||
|
||||
@ -1,260 +0,0 @@
|
||||
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
|
||||
SET time_zone = "+00:00";
|
||||
|
||||
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
||||
/*!40101 SET NAMES utf8 */;
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `accounts` (
|
||||
`id` int(255) NOT NULL AUTO_INCREMENT,
|
||||
`is_admin` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`is_anonymous` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`no_fees` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`username` varchar(40) NOT NULL,
|
||||
`pass` varchar(255) NOT NULL,
|
||||
`email` varchar(255) DEFAULT NULL COMMENT 'Assocaited email: used for validating users, and re-setting passwords',
|
||||
`timezone` varchar(35) NOT NULL DEFAULT '415',
|
||||
`notify_email` VARCHAR( 255 ) NULL DEFAULT NULL,
|
||||
`loggedIp` varchar(255) DEFAULT NULL,
|
||||
`is_locked` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`failed_logins` int(5) unsigned DEFAULT '0',
|
||||
`failed_pins` int(5) unsigned DEFAULT '0',
|
||||
`signup_timestamp` int(10) DEFAULT '0',
|
||||
`last_login` int(10) DEFAULT NULL,
|
||||
`pin` varchar(255) NOT NULL COMMENT 'four digit pin to allow account changes',
|
||||
`api_key` varchar(255) DEFAULT NULL,
|
||||
`token` varchar(65) DEFAULT NULL,
|
||||
`donate_percent` float DEFAULT '0',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `username` (`username`),
|
||||
UNIQUE KEY `email` (`email`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `blocks` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`height` int(10) unsigned NOT NULL,
|
||||
`blockhash` char(65) NOT NULL,
|
||||
`confirmations` int(10) NOT NULL,
|
||||
`amount` double NOT NULL,
|
||||
`difficulty` double NOT NULL,
|
||||
`time` int(11) NOT NULL,
|
||||
`accounted` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`account_id` int(255) unsigned DEFAULT NULL,
|
||||
`worker_name` varchar(50) DEFAULT 'unknown',
|
||||
`shares` double unsigned DEFAULT NULL,
|
||||
`share_id` bigint(30) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `height` (`height`,`blockhash`),
|
||||
KEY `time` (`time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Discovered blocks persisted from Litecoin Service';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `coin_addresses` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`account_id` int(11) NOT NULL,
|
||||
`currency` varchar(5) NOT NULL,
|
||||
`coin_address` varchar(255) NOT NULL,
|
||||
`ap_threshold` float DEFAULT '0',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `coin_address` (`coin_address`),
|
||||
KEY `account_id` (`account_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `invitations` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`account_id` int(11) unsigned NOT NULL,
|
||||
`email` varchar(50) NOT NULL,
|
||||
`token_id` int(11) NOT NULL,
|
||||
`is_activated` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `monitoring` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(30) NOT NULL,
|
||||
`type` varchar(15) NOT NULL,
|
||||
`value` varchar(255) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `name` (`name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Monitoring events from cronjobs';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `news` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`account_id` int(10) unsigned NOT NULL,
|
||||
`header` varchar(255) NOT NULL,
|
||||
`content` text NOT NULL,
|
||||
`time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`active` tinyint(1) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `notifications` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`type` varchar(25) NOT NULL,
|
||||
`data` varchar(255) NOT NULL,
|
||||
`active` tinyint(1) NOT NULL DEFAULT '1',
|
||||
`time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`account_id` int(10) unsigned DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `active` (`active`),
|
||||
KEY `data` (`data`),
|
||||
KEY `account_id` (`account_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `notification_settings` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`type` varchar(15) NOT NULL,
|
||||
`account_id` int(11) NOT NULL,
|
||||
`active` tinyint(1) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `account_id` (`account_id`),
|
||||
UNIQUE KEY `account_id_type` (`account_id`,`type`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `payouts` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`account_id` int(11) NOT NULL,
|
||||
`time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`completed` tinyint(1) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `account_id` (`account_id`,`completed`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `pool_worker` (
|
||||
`id` int(255) NOT NULL AUTO_INCREMENT,
|
||||
`account_id` int(255) NOT NULL,
|
||||
`username` char(50) DEFAULT NULL,
|
||||
`password` char(255) DEFAULT NULL,
|
||||
`difficulty` float NOT NULL DEFAULT '0',
|
||||
`monitor` tinyint(1) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `username` (`username`),
|
||||
KEY `account_id` (`account_id`),
|
||||
KEY `pool_worker_username` (`username`(10))
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `settings` (
|
||||
`name` varchar(255) NOT NULL,
|
||||
`value` text DEFAULT NULL,
|
||||
PRIMARY KEY (`name`),
|
||||
UNIQUE KEY `setting` (`name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
INSERT INTO `settings` (`name`, `value`) VALUES ('DB_VERSION', '1.0.3');
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `shares` (
|
||||
`id` bigint(30) NOT NULL AUTO_INCREMENT,
|
||||
`rem_host` varchar(255) NOT NULL,
|
||||
`username` varchar(120) NOT NULL,
|
||||
`our_result` enum('Y','N') NOT NULL,
|
||||
`upstream_result` enum('Y','N') DEFAULT NULL,
|
||||
`reason` varchar(50) DEFAULT NULL,
|
||||
`solution` varchar(257) NOT NULL,
|
||||
`difficulty` float NOT NULL DEFAULT '0',
|
||||
`time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `time` (`time`),
|
||||
KEY `upstream_result` (`upstream_result`),
|
||||
KEY `our_result` (`our_result`),
|
||||
KEY `username` (`username`),
|
||||
KEY `shares_username` (`username`(10))
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `shares_archive` (
|
||||
`id` bigint(30) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`share_id` bigint(30) unsigned NOT NULL,
|
||||
`username` varchar(120) NOT NULL,
|
||||
`our_result` enum('Y','N') DEFAULT NULL,
|
||||
`upstream_result` enum('Y','N') DEFAULT NULL,
|
||||
`block_id` int(10) unsigned NOT NULL,
|
||||
`difficulty` float NOT NULL DEFAULT '0',
|
||||
`time` datetime NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `share_id` (`share_id`),
|
||||
KEY `time` (`time`),
|
||||
KEY `our_result` (`our_result`),
|
||||
KEY `username` (`username`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Archive shares for potential later debugging purposes';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `statistics_shares` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`account_id` int(10) unsigned NOT NULL,
|
||||
`block_id` int(10) unsigned NOT NULL,
|
||||
`valid` float unsigned NOT NULL DEFAULT '0',
|
||||
`invalid` float unsigned NOT NULL DEFAULT '0',
|
||||
`pplns_valid` float unsigned NOT NULL DEFAULT '0',
|
||||
`pplns_invalid` float unsigned NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `account_id` (`account_id`),
|
||||
KEY `block_id` (`block_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `tokens` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`account_id` int(11) NOT NULL,
|
||||
`token` varchar(65) NOT NULL,
|
||||
`type` tinyint(4) NOT NULL,
|
||||
`time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `token` (`token`),
|
||||
KEY `account_id` (`account_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `token_types` (
|
||||
`id` tinyint(4) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(25) NOT NULL,
|
||||
`expiration` INT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `name` (`name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
INSERT INTO `token_types` (`id`, `name`, `expiration`) VALUES
|
||||
(1, 'password_reset', 3600),
|
||||
(2, 'confirm_email', 0),
|
||||
(3, 'invitation', 0),
|
||||
(4, 'account_unlock', 0),
|
||||
(5, 'account_edit', 3600),
|
||||
(6, 'change_pw', 3600),
|
||||
(7, 'withdraw_funds', 3600);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `transactions` (
|
||||
`id` int(255) NOT NULL AUTO_INCREMENT,
|
||||
`account_id` int(255) unsigned NOT NULL,
|
||||
`type` varchar(25) DEFAULT NULL,
|
||||
`coin_address` varchar(255) DEFAULT NULL,
|
||||
`amount` decimal(50,30) DEFAULT '0',
|
||||
`block_id` int(255) DEFAULT NULL,
|
||||
`timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`txid` varchar(256) DEFAULT NULL,
|
||||
`archived` tinyint(1) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `block_id` (`block_id`),
|
||||
KEY `account_id` (`account_id`),
|
||||
KEY `type` (`type`),
|
||||
KEY `archived` (`archived`),
|
||||
KEY `account_id_archived` (`account_id`,`archived`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
CREATE TABLE `statistics_users` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`account_id` int(11) NOT NULL,
|
||||
`hashrate` bigint(20) unsigned NOT NULL,
|
||||
`workers` int(11) NOT NULL,
|
||||
`sharerate` float NOT NULL,
|
||||
`timestamp` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `account_id_timestamp` (`account_id`,`timestamp`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `user_settings` (
|
||||
`account_id` int(11) NOT NULL,
|
||||
`name` varchar(50) NOT NULL,
|
||||
`value` text DEFAULT NULL,
|
||||
PRIMARY KEY (`account_id`,`name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;
|
||||
|
||||
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
||||
Loading…
Reference in New Issue
Block a user