Mysql share reporting fixed. Update config template.
[stratum-mining.git] / mining / basic_share_limiter.py
1 from stratum import settings
2
3 import stratum.logger
4 log = stratum.logger.get_logger('BasicShareLimiter')
5
6 import DBInterface
7 dbi = DBInterface.DBInterface()
8 dbi.clear_worker_diff()
9
10 from twisted.internet import defer
11 from mining.interfaces import Interfaces
12 import time
13
14 ''' This is just a customized ring buffer '''
15 class SpeedBuffer:
16         def __init__(self, size_max):
17                 self.max = size_max
18                 self.data = []
19                 self.cur = 0
20         def append(self, x):
21                 self.data.append(x)
22                 self.cur += 1
23                 if len(self.data) == self.max:
24                         self.cur = 0
25                         self.__class__ = SpeedBufferFull
26         def avg(self):
27                 return sum(self.data) / self.cur
28         def pos(self):
29                 return self.cur
30         def clear(self):
31                 self.data = []
32                 self.cur = 0
33         def size(self):
34                 return self.cur
35
36 class SpeedBufferFull:
37         def __init__(self, n):
38                 raise "you should use SpeedBuffer"
39         def append(self, x):            
40                 self.data[self.cur] = x
41                 self.cur = (self.cur + 1) % self.max
42         def avg(self):
43                 return sum(self.data) / self.max
44         def pos(self):
45                 return self.cur
46         def clear(self):
47                 self.data = []
48                 self.cur = 0
49                 self.__class__ = SpeedBuffer
50         def size(self):
51                 return self.max
52
53 class BasicShareLimiter(object):
54     def __init__(self):
55         self.worker_stats = {}
56         self.target = settings.VDIFF_TARGET
57         self.retarget = settings.VDIFF_RETARGET
58         self.variance = self.target * (float(settings.VDIFF_VARIANCE_PERCENT) / float(100))
59         self.tmin = self.target - self.variance
60         self.tmax = self.target + self.variance
61         self.buffersize = self.retarget / self.target * 4
62         # TODO: trim the hash of inactive workers
63
64     def submit(self, connection_ref, job_id, current_difficulty, timestamp, worker_name):
65         ts = int(timestamp)
66         # Init the stats for this worker if it isn't set.       
67         if worker_name not in self.worker_stats or self.worker_stats[worker_name]['last_ts'] < ts - settings.DB_USERCACHE_TIME :
68             self.worker_stats[worker_name] = {'last_rtc': (ts - self.retarget / 2), 'last_ts': ts, 'buffer': SpeedBuffer(self.buffersize) }
69             dbi.update_worker_diff(worker_name, settings.POOL_TARGET)
70             return
71         
72         # Standard share update of data
73         self.worker_stats[worker_name]['buffer'].append(ts - self.worker_stats[worker_name]['last_ts'])
74         self.worker_stats[worker_name]['last_ts'] = ts
75         # Do We retarget? If not, we're done.
76         if ts - self.worker_stats[worker_name]['last_rtc'] < self.retarget and self.worker_stats[worker_name]['buffer'].size() > 0:
77             return
78
79         # Set up and log our check
80         self.worker_stats[worker_name]['last_rtc'] = ts
81         avg = self.worker_stats[worker_name]['buffer'].avg()
82         log.info("Checking Retarget for %s (%i) avg. %i target %i+-%i" % (worker_name, current_difficulty, avg,
83                 self.target, self.variance))
84         if avg < 1:
85             log.info("Reseting avg = 1 since it's SOOO low")
86             avg = 1
87
88         # Figure out our Delta-Diff
89         ddiff = int((float(current_difficulty) * (float(self.target) / float(avg))) - current_difficulty)
90         if (avg > self.tmax and current_difficulty > settings.POOL_TARGET):
91             # For fractional -0.1 ddiff's just drop by 1
92             if ddiff > -1:
93                 ddiff = -1
94             # Don't drop below POOL_TARGET
95             if (ddiff + current_difficulty) < settings.POOL_TARGET:
96                 ddiff = settings.POOL_TARGET - current_difficulty
97         elif avg < self.tmin:
98             # For fractional 0.1 ddiff's just up by 1
99             if ddiff < 1:
100                 ddiff = 1
101             # Don't go above BITCOIN_DIFF
102             # TODO
103         else:  # If we are here, then we should not be retargeting.
104             return
105
106         # At this point we are retargeting this worker
107         new_diff = current_difficulty + ddiff
108         log.info("Retarget for %s %i old: %i new: %i" % (worker_name, ddiff, current_difficulty, new_diff))
109
110         self.worker_stats[worker_name]['buffer'].clear()
111         session = connection_ref().get_session()
112         session['prev_diff'] = session['difficulty']
113         session['prev_jobid'] = job_id
114         session['difficulty'] = new_diff
115         connection_ref().rpc('mining.set_difficulty', [new_diff, ], is_notification=True)
116         dbi.update_worker_diff(worker_name, new_diff)
117