Mysql share reporting fixed. Update config template.
[stratum-mining.git] / mining / basic_share_limiter.py
diff --git a/mining/basic_share_limiter.py b/mining/basic_share_limiter.py
new file mode 100644 (file)
index 0000000..6893a12
--- /dev/null
@@ -0,0 +1,117 @@
+from stratum import settings
+
+import stratum.logger
+log = stratum.logger.get_logger('BasicShareLimiter')
+
+import DBInterface
+dbi = DBInterface.DBInterface()
+dbi.clear_worker_diff()
+
+from twisted.internet import defer
+from mining.interfaces import Interfaces
+import time
+
+''' This is just a customized ring buffer '''
+class SpeedBuffer:
+       def __init__(self, size_max):
+               self.max = size_max
+               self.data = []
+               self.cur = 0
+       def append(self, x):
+               self.data.append(x)
+               self.cur += 1
+               if len(self.data) == self.max:
+                       self.cur = 0
+                       self.__class__ = SpeedBufferFull
+       def avg(self):
+               return sum(self.data) / self.cur
+       def pos(self):
+               return self.cur
+       def clear(self):
+               self.data = []
+               self.cur = 0
+       def size(self):
+               return self.cur
+
+class SpeedBufferFull:
+       def __init__(self, n):
+               raise "you should use SpeedBuffer"
+       def append(self, x):            
+               self.data[self.cur] = x
+               self.cur = (self.cur + 1) % self.max
+       def avg(self):
+               return sum(self.data) / self.max
+       def pos(self):
+               return self.cur
+       def clear(self):
+               self.data = []
+               self.cur = 0
+               self.__class__ = SpeedBuffer
+       def size(self):
+               return self.max
+
+class BasicShareLimiter(object):
+    def __init__(self):
+       self.worker_stats = {}
+       self.target = settings.VDIFF_TARGET
+       self.retarget = settings.VDIFF_RETARGET
+       self.variance = self.target * (float(settings.VDIFF_VARIANCE_PERCENT) / float(100))
+       self.tmin = self.target - self.variance
+       self.tmax = self.target + self.variance
+       self.buffersize = self.retarget / self.target * 4
+       # TODO: trim the hash of inactive workers
+
+    def submit(self, connection_ref, job_id, current_difficulty, timestamp, worker_name):
+       ts = int(timestamp)
+       # Init the stats for this worker if it isn't set.       
+        if worker_name not in self.worker_stats or self.worker_stats[worker_name]['last_ts'] < ts - settings.DB_USERCACHE_TIME :
+            self.worker_stats[worker_name] = {'last_rtc': (ts - self.retarget / 2), 'last_ts': ts, 'buffer': SpeedBuffer(self.buffersize) }
+            dbi.update_worker_diff(worker_name, settings.POOL_TARGET)
+            return
+       
+       # Standard share update of data
+       self.worker_stats[worker_name]['buffer'].append(ts - self.worker_stats[worker_name]['last_ts'])
+       self.worker_stats[worker_name]['last_ts'] = ts
+       # Do We retarget? If not, we're done.
+       if ts - self.worker_stats[worker_name]['last_rtc'] < self.retarget and self.worker_stats[worker_name]['buffer'].size() > 0:
+           return
+
+       # Set up and log our check
+       self.worker_stats[worker_name]['last_rtc'] = ts
+       avg = self.worker_stats[worker_name]['buffer'].avg()
+       log.info("Checking Retarget for %s (%i) avg. %i target %i+-%i" % (worker_name, current_difficulty, avg,
+               self.target, self.variance))
+       if avg < 1:
+           log.info("Reseting avg = 1 since it's SOOO low")
+           avg = 1
+
+       # Figure out our Delta-Diff
+       ddiff = int((float(current_difficulty) * (float(self.target) / float(avg))) - current_difficulty)
+       if (avg > self.tmax and current_difficulty > settings.POOL_TARGET):
+           # For fractional -0.1 ddiff's just drop by 1
+           if ddiff > -1:
+               ddiff = -1
+           # Don't drop below POOL_TARGET
+           if (ddiff + current_difficulty) < settings.POOL_TARGET:
+               ddiff = settings.POOL_TARGET - current_difficulty
+       elif avg < self.tmin:
+           # For fractional 0.1 ddiff's just up by 1
+           if ddiff < 1:
+               ddiff = 1
+           # Don't go above BITCOIN_DIFF
+           # TODO
+       else:  # If we are here, then we should not be retargeting.
+           return
+
+       # At this point we are retargeting this worker
+       new_diff = current_difficulty + ddiff
+       log.info("Retarget for %s %i old: %i new: %i" % (worker_name, ddiff, current_difficulty, new_diff))
+
+       self.worker_stats[worker_name]['buffer'].clear()
+        session = connection_ref().get_session()
+       session['prev_diff'] = session['difficulty']
+       session['prev_jobid'] = job_id
+       session['difficulty'] = new_diff
+       connection_ref().rpc('mining.set_difficulty', [new_diff, ], is_notification=True)
+       dbi.update_worker_diff(worker_name, new_diff)
+