Skip to content

Commit d993fc8

Browse files
author
Atrides
committed
- stop mining if no new jobs after timeout, at example because of network failure. will be continued with new job
- proxy connections state via browser - added pool servers in config: US2, HK, CN, SG, AU - removed default pool wallet - now 3 failover servers - new parameter "coin" for ETH or EXP - fixed some bugs with stratum connections - remove email from command line of miner - show hashrate of miners
1 parent 8a2e2b9 commit d993fc8

12 files changed

+207
-72
lines changed

README.md

+14-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#Description
22

3-
This is Stratum Proxy for Ethereum-pools (RPCv2) using asynchronous networking written in Python Twisted.
3+
This is Stratum Proxy for Ethereum based pools (RPCv2) using asynchronous networking written in Python Twisted.
44
Originally developed for DwarfPool http://dwarfpool.com/eth
55

66
**NOTE:** This fork is still in development. Some features may be broken. Please report any broken features or issues.
@@ -47,9 +47,21 @@ Originally developed for DwarfPool http://dwarfpool.com/eth
4747
* ./ethminer --farm-recheck 200 -G -F http://127.0.0.1:8080/rig1
4848

4949

50+
#External script to restart proxy (made by rain)
51+
52+
* https://paste.ubuntu.com/15327007/
53+
54+
55+
# Proxy working check
56+
57+
* To check that proxy works open in browser http://127.0.0.1:8080/ (or your changed ip and port from config)
58+
* If you see "Ethereum stratum proxy" and some infos about connections.
59+
* If not then mostly case that you have application running on this port, at example Antivirus.
60+
61+
5062
#Donations
5163

52-
* ETH: 0xb7302f5988cd483db920069a5c88f049a3707e2f
64+
* ETH: 0xea7263feb7d8a8ab0a11eedd8f1ce04412ab0820
5365

5466

5567
#Requirements

eth-proxy.conf

+31-11
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,45 @@
11
###
2-
# Command line for miners:
2+
# Examples of command line for miners:
33
#
4-
# ethminer.exe -G -F http://HOST:PORT/
5-
# ethminer.exe -G -F http://HOST:PORT/rig1
4+
# ethminer.exe --farm-recheck 200 -G -F http://HOST:PORT/
5+
# ethminer.exe --farm-recheck 300 -G -F http://HOST:PORT/rig1
66
#
77
# ethminer.exe -G -F http://127.0.0.1:8080/
8-
# ethminer.exe -G -F http://192.168.0.33:8080/rig1
8+
# ethminer.exe --farm-recheck 100 -G -F http://192.168.0.33:8080/rig1
9+
#
10+
# farm-recheck parameter is very individual. Just test different values.
911
#
1012
# You can submit shares without workername or
1113
# You can provide workername:
1214
# - with url like "/rig1"
1315
# - or use automatically numbering(integer) based on IP of miner
1416
#
1517
# Servers:
16-
# EU-Server: eth-eu.dwarfpool.com (France)
17-
# US-Server: eth-us.dwarfpool.com (Montreal,Canada)
18-
# RU-Server: eth-ru.dwarfpool.com (Moscow)
18+
# EU-Server: eth-eu.dwarfpool.com (France)
19+
# US-Server: eth-us.dwarfpool.com (EastCoast: Montreal,Canada)
20+
# US-Server: eth-us2.dwarfpool.com (WestCoast: Las Vegas)
21+
# RU-Server: eth-ru.dwarfpool.com (Moscow)
22+
# HK-Server: eth-hk.dwarfpool.com (Hong-Kong)
23+
# CN-Server: eth-cn.dwarfpool.com (Shanghai)
24+
# SG-Server: eth-sg.dwarfpool.com (Singapore)
25+
# AU-Server: eth-au.dwarfpool.com (Melbourne)
1926
#
2027
###
2128

29+
# Select Ethereum ETH or Expanse EXP
30+
COIN = "ETH"
31+
2232
# Host and port for your workers
2333
HOST = "0.0.0.0"
2434
PORT = 8080
2535

2636
# Coin address where money goes
27-
WALLET = "0x2a65aca4d5fc5b5c859090a6c34d164135398226"
37+
WALLET = "XXXXXX"
38+
39+
# To donate please use wallet "0xea7263feb7d8a8ab0a11eedd8f1ce04412ab0820"
2840

2941
# It's useful for individually monitoring and statistic
30-
ENABLE_WORKER_ID = True
42+
ENABLE_WORKER_ID = False
3143

3244
# On DwarfPool you have option to monitor your workers via email.
3345
# If WORKER_ID is enabled, you can monitor every worker/rig separately.
@@ -40,8 +52,16 @@ POOL_PORT = 8008
4052

4153
# Failover pool
4254
POOL_FAILOVER_ENABLE = True
43-
POOL_HOST_FAILOVER = "eth-us.dwarfpool.com"
44-
POOL_PORT_FAILOVER = 8008
55+
56+
POOL_HOST_FAILOVER1 = "eth-ru.dwarfpool.com"
57+
POOL_PORT_FAILOVER1 = 8008
58+
59+
POOL_HOST_FAILOVER2 = "eth-us.dwarfpool.com"
60+
POOL_PORT_FAILOVER2 = 8008
61+
62+
POOL_HOST_FAILOVER3 = "eth-hk.dwarfpool.com"
63+
POOL_PORT_FAILOVER3 = 8008
64+
4565

4666
# Logging
4767
LOG_TO_FILE = True

eth-proxy.py

+44-16
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import time
55
import os
6+
import sys
67
import socket
78

89
from stratum import settings
@@ -12,7 +13,7 @@
1213
if __name__ == '__main__':
1314
if len(settings.WALLET)!=42 and len(settings.WALLET)!=40:
1415
log.error("Wrong WALLET!")
15-
quit()
16+
sys.exit()
1617
settings.CUSTOM_EMAIL = settings.MONITORING_EMAIL if settings.MONITORING_EMAIL and settings.MONITORING else ""
1718

1819
from twisted.internet import reactor, defer, protocol
@@ -26,7 +27,6 @@
2627
from mining_libs import client_service
2728
from mining_libs import jobs
2829
from mining_libs import version
29-
from mining_libs.jobs import Job
3030

3131
def on_shutdown(f):
3232
'''Clean environment properly'''
@@ -42,7 +42,10 @@ def ping(f):
4242
return
4343
try:
4444
yield (f.rpc('eth_getWork', [], ''))
45-
reactor.callLater(60, ping, f)
45+
if f.is_failover:
46+
reactor.callLater(30, ping, f)
47+
else:
48+
reactor.callLater(5, ping, f)
4649
except Exception:
4750
pass
4851

@@ -53,18 +56,18 @@ def on_connect(f):
5356
f.is_connected = True
5457
f.remote_ip = f.client._get_ip()
5558
#reactor.callLater(30, f.client.transport.loseConnection)
56-
59+
5760
# Hook to on_connect again
5861
f.on_connect.addCallback(on_connect)
59-
62+
6063
# Get first job and user_id
6164
debug = "_debug" if settings.DEBUG else ""
6265
initial_job = (yield f.rpc('eth_submitLogin', [settings.WALLET, settings.CUSTOM_EMAIL], 'Proxy_'+version.VERSION+debug))
6366

6467
reactor.callLater(0, ping, f)
6568

6669
defer.returnValue(f)
67-
70+
6871
def on_disconnect(f):
6972
'''Callback when proxy get disconnected from the pool'''
7073
log.info("Disconnected from Stratum pool at %s:%d" % f.main_host)
@@ -82,28 +85,52 @@ def main():
8285
f = SocketTransportClientFactory(settings.POOL_HOST, settings.POOL_PORT,
8386
debug=settings.DEBUG, proxy=None,
8487
event_handler=client_service.ClientMiningService)
85-
f.is_failover = False
8688

87-
ff = None
89+
f1 = None
90+
f2 = None
91+
f3 = None
8892
if settings.POOL_FAILOVER_ENABLE:
89-
log.warning("Trying to connect to failover Stratum pool at %s:%d" % (settings.POOL_HOST_FAILOVER, settings.POOL_PORT_FAILOVER))
90-
ff = SocketTransportClientFactory(settings.POOL_HOST_FAILOVER, settings.POOL_PORT_FAILOVER,
93+
log.warning("Trying to connect to failover Stratum pool-1 at %s:%d" % (settings.POOL_HOST_FAILOVER1, settings.POOL_PORT_FAILOVER1))
94+
f1 = SocketTransportClientFactory(settings.POOL_HOST_FAILOVER1, settings.POOL_PORT_FAILOVER1,
9195
debug=settings.DEBUG, proxy=None,
9296
event_handler=client_service.ClientMiningService)
93-
ff.is_failover = True
97+
f1.is_failover = True
9498

95-
job_registry = jobs.JobRegistry(f,ff)
99+
log.warning("Trying to connect to failover Stratum pool-2 at %s:%d" % (settings.POOL_HOST_FAILOVER2, settings.POOL_PORT_FAILOVER2))
100+
f2 = SocketTransportClientFactory(settings.POOL_HOST_FAILOVER2, settings.POOL_PORT_FAILOVER2,
101+
debug=settings.DEBUG, proxy=None,
102+
event_handler=client_service.ClientMiningService)
103+
f2.is_failover = True
104+
105+
log.warning("Trying to connect to failover Stratum pool-3 at %s:%d" % (settings.POOL_HOST_FAILOVER3, settings.POOL_PORT_FAILOVER3))
106+
f3 = SocketTransportClientFactory(settings.POOL_HOST_FAILOVER3, settings.POOL_PORT_FAILOVER3,
107+
debug=settings.DEBUG, proxy=None,
108+
event_handler=client_service.ClientMiningService)
109+
f3.is_failover = True
110+
111+
job_registry = jobs.JobRegistry(f,f1,f2,f3)
96112
client_service.ClientMiningService.job_registry = job_registry
97113
client_service.ClientMiningService.reset_timeout()
98114

99115
f.on_connect.addCallback(on_connect)
100116
f.on_disconnect.addCallback(on_disconnect)
101117
# Cleanup properly on shutdown
102118
reactor.addSystemEventTrigger('before', 'shutdown', on_shutdown, f)
103-
if ff:
104-
ff.on_connect.addCallback(on_connect)
105-
ff.on_disconnect.addCallback(on_disconnect)
106-
reactor.addSystemEventTrigger('before', 'shutdown', on_shutdown, ff)
119+
if f1:
120+
f1.on_connect.addCallback(on_connect)
121+
f1.on_disconnect.addCallback(on_disconnect)
122+
reactor.addSystemEventTrigger('before', 'shutdown', on_shutdown, f1)
123+
124+
if f2:
125+
f2.on_connect.addCallback(on_connect)
126+
f2.on_disconnect.addCallback(on_disconnect)
127+
reactor.addSystemEventTrigger('before', 'shutdown', on_shutdown, f2)
128+
129+
if f3:
130+
f3.on_connect.addCallback(on_connect)
131+
f3.on_disconnect.addCallback(on_disconnect)
132+
reactor.addSystemEventTrigger('before', 'shutdown', on_shutdown, f3)
133+
107134

108135
# Block until proxy connect to the pool
109136
try:
@@ -112,6 +139,7 @@ def main():
112139
log.warning("First pool server must be online first time during start")
113140
return
114141

142+
115143
conn = reactor.listenTCP(settings.PORT, Site(getwork_listener.Root(job_registry, settings.ENABLE_WORKER_ID)), interface=settings.HOST)
116144

117145
try:

mining_libs/client_service.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,12 @@ def on_timeout(cls):
3131
cls.reset_timeout()
3232
if not cls.job_registry.f.is_connected:
3333
cls.job_registry.f.reconnect()
34-
if cls.job_registry.ff and not cls.job_registry.ff.is_connected:
35-
cls.job_registry.ff.reconnect()
34+
if cls.job_registry.f1 and not cls.job_registry.f1.is_connected:
35+
cls.job_registry.f1.reconnect()
36+
if cls.job_registry.f2 and not cls.job_registry.f2.is_connected:
37+
cls.job_registry.f2.reconnect()
38+
if cls.job_registry.f3 and not cls.job_registry.f3.is_connected:
39+
cls.job_registry.f3.reconnect()
3640

3741
def handle_event(self, method, params, connection_ref):
3842
'''Handle RPC calls and notifications from the pool'''

mining_libs/getwork_listener.py

+33-10
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,23 @@
1010

1111
class Root(Resource):
1212
isLeaf = True
13-
13+
1414
def __init__(self, job_registry, enable_worker_id):
1515
Resource.__init__(self)
1616
self.job_registry = job_registry
1717
self.isWorkerID = enable_worker_id
1818
self.submitHashrates = {}
19-
19+
self.getWorkCacheTimeout = {"work":"","time":0}
20+
2021
def json_response(self, msg_id, result):
2122
resp = json.dumps({'id': msg_id, 'jsonrpc': '2.0', 'result': result})
2223
return resp
23-
24+
2425
def json_error(self, msg_id, message):
2526
resp = json.dumps({'id': msg_id, 'jsonrpc': '2.0', 'result': False, 'error': message})
26-
return resp
27-
28-
def render_POST(self, request):
27+
return resp
28+
29+
def render_POST(self, request):
2930
request.setHeader('content-type', 'application/json')
3031
data = json.loads(request.content.read())
3132

@@ -37,11 +38,17 @@ def render_POST(self, request):
3738

3839
if not data.has_key('method'):
3940
response = self.json_error(data.get('id'), "Need methods")+'\n'
40-
if data['method'] == 'eth_getWork':
41-
response = self.json_response(data.get('id', 0), self.job_registry.jobs.params)
41+
elif data['method'] == 'eth_getWork':
42+
if self.getWorkCacheTimeout["work"]==self.job_registry.jobs.params[0] and int(time.time())-self.getWorkCacheTimeout["time"]>=self.job_registry.coinTimeout:
43+
log.warning('Job timeout. Proxy is waiting for an updated job. Please restart proxy!')
44+
response = self.json_error(data.get('id', 0), "Job timeout. Proxy is waiting for an updated job...")
45+
else:
46+
if self.getWorkCacheTimeout["work"]!=self.job_registry.jobs.params[0]:
47+
self.getWorkCacheTimeout = {"work":self.job_registry.jobs.params[0],"time":int(time.time())}
48+
response = self.json_response(data.get('id', 0), self.job_registry.jobs.params)
4249
elif data['method'] == 'eth_submitWork' or data['method'] == 'eth_submitHashrate':
4350
if self.isWorkerID:
44-
worker_name = request.uri[1:15]
51+
worker_name = request.uri[1:15].split("/")[0]
4552
if not worker_name:
4653
ip_temp = request.getClientIP().split('.')
4754
worker_name = str( int(ip_temp[0])*16777216 + int(ip_temp[1])*65536 + int(ip_temp[2])*256 + int(ip_temp[3]) )
@@ -51,6 +58,7 @@ def render_POST(self, request):
5158
if data['method'] == 'eth_submitHashrate':
5259
if worker_name and (not self.submitHashrates.has_key(worker_name) or int(time.time())-self.submitHashrates[worker_name]>=60):
5360
self.submitHashrates[worker_name] = int(time.time())
61+
log.info('Hashrate for %s is %s MHs' % (worker_name,int(data['params'][0],16)/1000000.0 ) )
5462
threads.deferToThread(self.job_registry.submit, data['method'], data['params'], worker_name)
5563
elif data['method'] == 'eth_submitWork':
5664
threads.deferToThread(self.job_registry.submit, data['method'], data['params'], worker_name)
@@ -66,4 +74,19 @@ def render_POST(self, request):
6674
return
6775

6876
def render_GET(self, request):
69-
return "Ethereum startum proxy"
77+
ret_text = "Ethereum stratum proxy<br>"
78+
if self.job_registry and self.job_registry.jobs and self.job_registry.jobs.params:
79+
ret_text += "DAG-file: %s<br><br>" % str(self.job_registry.jobs.params[1][2:18])
80+
if self.job_registry.f:
81+
connected = "connected" if (hasattr(self.job_registry.f, "is_connected") and self.job_registry.f.is_connected) else "disconnected"
82+
ret_text += "Main server %s:%s (%s) %s<br>" % (self.job_registry.f.main_host[0], self.job_registry.f.main_host[1], self.job_registry.f.remote_ip, connected)
83+
if self.job_registry.f1:
84+
connected = "connected" if (hasattr(self.job_registry.f1, "is_connected") and self.job_registry.f1.is_connected) else "disconnected"
85+
ret_text += "Failover server1 %s:%s (%s) %s<br>" % (self.job_registry.f1.main_host[0], self.job_registry.f1.main_host[1], self.job_registry.f1.remote_ip, connected)
86+
if self.job_registry.f2:
87+
connected = "connected" if (hasattr(self.job_registry.f2, "is_connected") and self.job_registry.f2.is_connected) else "disconnected"
88+
ret_text += "Failover server2 %s:%s (%s) %s<br>" % (self.job_registry.f2.main_host[0], self.job_registry.f2.main_host[1], self.job_registry.f2.remote_ip, connected)
89+
if self.job_registry.f3:
90+
connected = "connected" if (hasattr(self.job_registry.f3, "is_connected") and self.job_registry.f3.is_connected) else "disconnected"
91+
ret_text += "Failover server3 %s:%s (%s) %s<br>" % (self.job_registry.f3.main_host[0], self.job_registry.f3.main_host[1], self.job_registry.f3.remote_ip, connected)
92+
return ret_text

0 commit comments

Comments
 (0)