Skip to content

Commit ce14515

Browse files
committed
Merge pull request slush0#20 from obigal/vardiff-bug-patch
Vardiff bug patch
2 parents cdac4eb + e2b06d2 commit ce14515

8 files changed

Lines changed: 80 additions & 26 deletions

File tree

conf/config_sample.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,6 @@
158158
VDIFF_TARGET_TIME = 15 # Target time per share (i.e. try to get 1 share per this many seconds)
159159
VDIFF_RETARGET_TIME = 120 # Check to see if we should retarget this often
160160
VDIFF_VARIANCE_PERCENT = 30 # Allow average time to very this % from target without retarget
161-
VDIFF_RETARGET_DELAY = 25 # Wait this many seconds before applying new variable difficulty target
162-
VDIFF_RETARGET_REJECT_TIME = 60 # Wait this many seconds before rejecting old difficulty shares
163161

164162
# Allow external setting of worker difficulty, checks pool_worker table datarow[6] position for target difficulty
165163
# if present or else defaults to pool target, over rides all other difficulty settings, no checks are made

lib/template_registry.py

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ def get_job(self, job_id):
176176
return j
177177

178178
def submit_share(self, job_id, worker_name, session, extranonce1_bin, extranonce2, ntime, nonce,
179-
difficulty, submit_time):
179+
difficulty):
180180
'''Check parameters and finalize block template. If it leads
181181
to valid block candidate, asynchronously submits the block
182182
back to the bitcoin network.
@@ -249,16 +249,10 @@ def submit_share(self, job_id, worker_name, session, extranonce1_bin, extranonce
249249
header_hex = header_hex+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
250250
else: pass
251251

252-
target_user = self.diff_to_target(difficulty)
253-
if hash_int > target_user and \
254-
( 'prev_jobid' not in session or session['prev_jobid'] < job_id \
255-
or 'prev_diff' not in session or hash_int > self.diff_to_target(session['prev_diff']) ):
252+
target_user = self.diff_to_target(difficulty)
253+
if hash_int > target_user:
256254
raise SubmitException("Share is above target")
257255

258-
if hash_int > target_user and 'prev_ts' in session \
259-
and (submit_time - session['prev_ts']) > settings.VDIFF_RETARGET_REJECT_TIME:
260-
raise SubmitException("Stale-share above target")
261-
262256
# Mostly for debugging purposes
263257
target_info = self.diff_to_target(100000)
264258
if hash_int <= target_info:
@@ -267,7 +261,6 @@ def submit_share(self, job_id, worker_name, session, extranonce1_bin, extranonce
267261
# Algebra tells us the diff_to_target is the same as hash_to_diff
268262
share_diff = int(self.diff_to_target(hash_int))
269263

270-
271264
# 5. Compare hash with target of the network
272265
if hash_int <= job.target:
273266
# Yay! It is block candidate!

mining/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import time
66
import simplejson as json
77
from twisted.internet import reactor
8+
import threading
9+
from mining.work_log_pruner import WorkLogPruner
810

911
@defer.inlineCallbacks
1012
def setup(on_startup):
@@ -86,6 +88,10 @@ def setup(on_startup):
8688
# This is just failsafe solution when -blocknotify
8789
# mechanism is not working properly
8890
BlockUpdater(registry, bitcoin_rpc)
91+
92+
prune_thr = threading.Thread(target=WorkLogPruner, args=(Interfaces.worker_manager.job_log,))
93+
prune_thr.daemon = True
94+
prune_thr.start()
8995

9096
log.info("MINING SERVICE IS READY")
9197
on_startup.callback(True)

mining/basic_share_limiter.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,10 +167,13 @@ def submit(self, connection_ref, job_id, current_difficulty, timestamp, worker_n
167167

168168
self.worker_stats[worker_name]['buffer'].clear()
169169
session = connection_ref().get_session()
170+
171+
(job_id, prevhash, coinb1, coinb2, merkle_branch, version, nbits, ntime, _) = \
172+
Interfaces.template_registry.get_last_broadcast_args()
173+
work_id = Interfaces.worker_manager.register_work(worker_name, job_id, new_diff)
170174

171-
session['prev_diff'] = session['difficulty']
172-
session['prev_jobid'] = job_id
173175
session['difficulty'] = new_diff
174176
connection_ref().rpc('mining.set_difficulty', [new_diff, ], is_notification=True)
177+
connection_ref().rpc('mining.notify', [work_id, prevhash, coinb1, coinb2, merkle_branch, version, nbits, ntime, False, ], is_notification=True)
175178
dbi.update_worker_diff(worker_name, new_diff)
176179

mining/interfaces.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ class WorkerManagerInterface(object):
1919
def __init__(self):
2020
self.worker_log = {}
2121
self.worker_log.setdefault('authorized', {})
22+
self.job_log = {}
23+
self.job_log.setdefault('None', {})
2224
return
2325

2426
def authorize(self, worker_name, worker_password):
@@ -33,6 +35,22 @@ def get_user_difficulty(self, worker_name):
3335
else:
3436
return (False, settings.POOL_TARGET)
3537

38+
def register_work(self, worker_name, job_id, difficulty):
39+
now = Interfaces.timestamper.time()
40+
work_id = WorkIdGenerator.get_new_id()
41+
self.job_log.setdefault(worker_name, {})[work_id] = (job_id, difficulty, now)
42+
return work_id
43+
44+
class WorkIdGenerator(object):
45+
counter = 1000
46+
47+
@classmethod
48+
def get_new_id(cls):
49+
cls.counter += 1
50+
if cls.counter % 0xffff == 0:
51+
cls.counter = 1
52+
return "%x" % cls.counter
53+
3654
class ShareLimiterInterface(object):
3755
'''Implement difficulty adjustments here'''
3856

mining/service.py

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ def subscribe(self, *args):
8080
session['difficulty'] = settings.POOL_TARGET # Following protocol specs, default diff is 1
8181
return Pubsub.subscribe(self.connection_ref(), MiningSubscription()) + (extranonce1_hex, extranonce2_size)
8282

83-
def submit(self, worker_name, job_id, extranonce2, ntime, nonce):
83+
def submit(self, worker_name, work_id, extranonce2, ntime, nonce):
8484
'''Try to solve block candidate using given parameters.'''
8585

8686
session = self.connection_ref().get_session()
@@ -96,8 +96,16 @@ def submit(self, worker_name, job_id, extranonce2, ntime, nonce):
9696
if not extranonce1_bin:
9797
raise SubmitException("Connection is not subscribed for mining")
9898

99+
# Get current block job_id
99100
difficulty = session['difficulty']
100-
s_difficulty = difficulty
101+
if worker_name in Interfaces.worker_manager.job_log and work_id in Interfaces.worker_manager.job_log[worker_name]:
102+
(job_id, difficulty, job_ts) = Interfaces.worker_manager.job_log[worker_name][work_id]
103+
else:
104+
job_ts = Interfaces.timestamper.time()
105+
Interfaces.worker_manager.job_log.setdefault(worker_name, {})[work_id] = (work_id, difficulty, job_ts)
106+
job_id = work_id
107+
#log.debug("worker_job_log: %s" % repr(Interfaces.worker_manager.job_log))
108+
101109
submit_time = Interfaces.timestamper.time()
102110
ip = self.connection_ref()._get_ip()
103111
(valid, invalid, is_banned, diff, is_ext_diff, last_ts) = Interfaces.worker_manager.worker_log['authorized'][worker_name]
@@ -119,23 +127,19 @@ def submit(self, worker_name, job_id, extranonce2, ntime, nonce):
119127
log.debug("Clearing worker stats for: %s" % worker_name)
120128
(valid, invalid, is_banned, last_ts) = (0, 0, is_banned, Interfaces.timestamper.time())
121129

122-
if 'prev_ts' in session and (submit_time - session['prev_ts']) < settings.VDIFF_RETARGET_DELAY \
123-
and not is_ext_diff:
124-
difficulty = session['prev_diff'] or session['difficulty'] or settings.POOL_TARGET
125-
diff = difficulty
126-
log.debug("%s (%d, %d, %s, %s, %d) %0.2f%% diff(%f)" % (worker_name, valid, invalid, is_banned, is_ext_diff, last_ts, percent, diff))
130+
log.debug("%s (%d, %d, %s, %s, %d) %0.2f%% work_id(%s) job_id(%s) diff(%f)" % (worker_name, valid, invalid, is_banned, is_ext_diff, last_ts, percent, work_id, job_id, difficulty))
127131
if not is_ext_diff:
128132
Interfaces.share_limiter.submit(self.connection_ref, job_id, difficulty, submit_time, worker_name)
129133

130134
# This checks if submitted share meet all requirements
131135
# and it is valid proof of work.
132136
try:
133137
(block_header, block_hash, share_diff, on_submit) = Interfaces.template_registry.submit_share(job_id,
134-
worker_name, session, extranonce1_bin, extranonce2, ntime, nonce, s_difficulty, submit_time)
138+
worker_name, session, extranonce1_bin, extranonce2, ntime, nonce, difficulty)
135139
except SubmitException as e:
136140
# block_header and block_hash are None when submitted data are corrupted
137141
invalid += 1
138-
Interfaces.worker_manager.worker_log['authorized'][worker_name] = (valid, invalid, is_banned, diff, is_ext_diff, last_ts)
142+
Interfaces.worker_manager.worker_log['authorized'][worker_name] = (valid, invalid, is_banned, difficulty, is_ext_diff, last_ts)
139143

140144
if is_banned:
141145
raise SubmitException("Worker is temporarily banned")
@@ -145,7 +149,7 @@ def submit(self, worker_name, job_id, extranonce2, ntime, nonce):
145149
raise
146150

147151
valid += 1
148-
Interfaces.worker_manager.worker_log['authorized'][worker_name] = (valid, invalid, is_banned, diff, is_ext_diff, last_ts)
152+
Interfaces.worker_manager.worker_log['authorized'][worker_name] = (valid, invalid, is_banned, difficulty, is_ext_diff, last_ts)
149153

150154
if is_banned:
151155
raise SubmitException("Worker is temporarily banned")

mining/subscription.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,18 @@ def on_template(cls, is_new_block):
2121

2222
(job_id, prevhash, coinb1, coinb2, merkle_branch, version, nbits, ntime, _) = \
2323
Interfaces.template_registry.get_last_broadcast_args()
24-
24+
2525
# Push new job to subscribed clients
26-
cls.emit(job_id, prevhash, coinb1, coinb2, merkle_branch, version, nbits, ntime, clean_jobs)
26+
for subscription in Pubsub.iterate_subscribers(cls.event):
27+
session = subscription.connection_ref().get_session()
28+
session.setdefault('authorized', {})
29+
if session['authorized'].keys():
30+
worker_name = session['authorized'].keys()[0]
31+
difficulty = session['difficulty']
32+
work_id = Interfaces.worker_manager.register_work(worker_name, job_id, difficulty)
33+
subscription.emit_single(work_id, prevhash, coinb1, coinb2, merkle_branch, version, nbits, ntime, clean_jobs)
34+
else:
35+
subscription.emit_single(job_id, prevhash, coinb1, coinb2, merkle_branch, version, nbits, ntime, clean_jobs)
2736

2837
cnt = Pubsub.get_subscription_count(cls.event)
2938
log.info("BROADCASTED to %d connections in %.03f sec" % (cnt, (Interfaces.timestamper.time() - start)))

mining/work_log_pruner.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from time import sleep, time
2+
import traceback
3+
import lib.logger
4+
log = lib.logger.get_logger('work_log_pruner')
5+
6+
def _WorkLogPruner_I(wl):
7+
now = time()
8+
pruned = 0
9+
for username in wl:
10+
userwork = wl[username]
11+
for wli in tuple(userwork.keys()):
12+
if now > userwork[wli][2] + 120:
13+
del userwork[wli]
14+
pruned += 1
15+
log.debug('Pruned %d jobs' % (pruned,))
16+
17+
def WorkLogPruner(wl):
18+
while True:
19+
try:
20+
sleep(60)
21+
_WorkLogPruner_I(wl)
22+
except:
23+
log.debug(traceback.format_exc())

0 commit comments

Comments
 (0)