/ src / proofofwork.py.bak
proofofwork.py.bak
  1  """
  2  Proof of work calculation
  3  """
  4  # pylint: disable=import-outside-toplevel
  5  
  6  import ctypes
  7  import hashlib
  8  import os
  9  import subprocess  # nosec B404
 10  import sys
 11  import tempfile
 12  import time
 13  from struct import pack, unpack
 14  
 15  import highlevelcrypto
 16  import openclpow
 17  import paths
 18  import queues
 19  import state
 20  from bmconfigparser import config
 21  from debug import logger
 22  from defaults import (
 23      networkDefaultProofOfWorkNonceTrialsPerByte,
 24      networkDefaultPayloadLengthExtraBytes)
 25  from tr import _translate
 26  
 27  
 28  bitmsglib = 'bitmsghash.so'
 29  bmpow = None
 30  
 31  
 32  class LogOutput(object):  # pylint: disable=too-few-public-methods
 33      """
 34      A context manager that block stdout for its scope
 35      and appends it's content to log before exit. Usage::
 36  
 37      with LogOutput():
 38          os.system('ls -l')
 39  
 40      https://stackoverflow.com/questions/5081657
 41      """
 42  
 43      def __init__(self, prefix='PoW'):
 44          self.prefix = prefix
 45          try:
 46              sys.stdout.flush()
 47              self._stdout = sys.stdout
 48              self._stdout_fno = os.dup(sys.stdout.fileno())
 49          except AttributeError:
 50              # NullWriter instance has no attribute 'fileno' on Windows
 51              self._stdout = None
 52          else:
 53              self._dst, self._filepath = tempfile.mkstemp()
 54  
 55      def __enter__(self):
 56          if not self._stdout:
 57              return
 58          stdout = os.dup(1)
 59          os.dup2(self._dst, 1)
 60          os.close(self._dst)
 61          sys.stdout = os.fdopen(stdout, 'w')
 62  
 63      def __exit__(self, exc_type, exc_val, exc_tb):
 64          if not self._stdout:
 65              return
 66          sys.stdout.close()
 67          sys.stdout = self._stdout
 68          sys.stdout.flush()
 69          os.dup2(self._stdout_fno, 1)
 70  
 71          with open(self._filepath) as out:
 72              for line in out:
 73                  logger.info('%s: %s', self.prefix, line)
 74          os.remove(self._filepath)
 75  
 76  
 77  def _set_idle():
 78      if 'linux' in sys.platform:
 79          os.nice(20)
 80      else:
 81          try:
 82              # pylint: disable=no-member,import-error
 83              sys.getwindowsversion()
 84              import win32api
 85              import win32process
 86              import win32con
 87  
 88              handle = win32api.OpenProcess(
 89                  win32con.PROCESS_ALL_ACCESS, True,
 90                  win32api.GetCurrentProcessId())
 91              win32process.SetPriorityClass(
 92                  handle, win32process.IDLE_PRIORITY_CLASS)
 93          except:  # nosec B110 # noqa:E722 pylint:disable=bare-except
 94              # Windows 64-bit
 95              pass
 96  
 97  
 98  def trial_value(nonce, initialHash):
 99      """Calculate PoW trial value"""
100      trialValue, = unpack(
101          '>Q', highlevelcrypto.double_sha512(
102              pack('>Q', nonce) + initialHash)[0:8])
103      return trialValue
104  
105  
106  def _pool_worker(nonce, initialHash, target, pool_size):
107      _set_idle()
108      trialValue = float('inf')
109      while trialValue > target:
110          nonce += pool_size
111          trialValue = trial_value(nonce, initialHash)
112      return trialValue, nonce
113  
114  
115  def _doSafePoW(target, initialHash):
116      logger.debug('Safe PoW start')
117      nonce = 0
118      trialValue = float('inf')
119      while trialValue > target and state.shutdown == 0:
120          nonce += 1
121          trialValue = trial_value(nonce, initialHash)
122      if state.shutdown != 0:
123          raise StopIteration("Interrupted")
124      logger.debug('Safe PoW done')
125      return trialValue, nonce
126  
127  
128  def _doFastPoW(target, initialHash):
129      # pylint:disable=bare-except
130      logger.debug('Fast PoW start')
131      from multiprocessing import Pool, cpu_count
132      try:
133          pool_size = cpu_count()
134      except:  # noqa:E722
135          pool_size = 4
136      maxCores = config.safeGetInt('bitmessagesettings', 'maxcores', 99999)
137      pool_size = min(pool_size, maxCores)
138  
139      pool = Pool(processes=pool_size)
140      result = []
141      for i in range(pool_size):
142          result.append(pool.apply_async(
143              _pool_worker, args=(i, initialHash, target, pool_size)))
144  
145      while True:
146          if state.shutdown != 0:
147              try:
148                  pool.terminate()
149                  pool.join()
150              except:  # nosec B110 # noqa:E722
151                  pass
152              raise StopIteration("Interrupted")
153          for i in range(pool_size):
154              if result[i].ready():
155                  try:
156                      result[i].successful()
157                  except AssertionError:
158                      pool.terminate()
159                      pool.join()
160                      raise StopIteration("Interrupted")
161                  result = result[i].get()
162                  pool.terminate()
163                  pool.join()
164                  logger.debug('Fast PoW done')
165                  return result[0], result[1]
166          time.sleep(0.2)
167  
168  
169  def _doCPoW(target, initialHash):
170      with LogOutput():
171          h = initialHash
172          m = target
173          out_h = ctypes.pointer(ctypes.create_string_buffer(h, 64))
174          out_m = ctypes.c_ulonglong(m)
175          logger.debug('C PoW start')
176          nonce = bmpow(out_h, out_m)
177  
178      trialValue = trial_value(nonce, initialHash)
179      if state.shutdown != 0:
180          raise StopIteration("Interrupted")
181      logger.debug('C PoW done')
182      return trialValue, nonce
183  
184  
185  def _doGPUPoW(target, initialHash):
186      logger.debug('GPU PoW start')
187      nonce = openclpow.do_opencl_pow(initialHash.encode("hex"), target)
188      trialValue = trial_value(nonce, initialHash)
189      if trialValue > target:
190          deviceNames = ", ".join(gpu.name for gpu in openclpow.enabledGpus)
191          queues.UISignalQueue.put((
192              'updateStatusBar', (
193                  _translate(
194                      "MainWindow",
195                      "Your GPU(s) did not calculate correctly,"
196                      " disabling OpenCL. Please report to the developers."
197                  ), 1)
198          ))
199          logger.error(
200              'Your GPUs (%s) did not calculate correctly, disabling OpenCL.'
201              ' Please report to the developers.', deviceNames)
202          openclpow.enabledGpus = []
203          raise Exception("GPU did not calculate correctly.")
204      if state.shutdown != 0:
205          raise StopIteration("Interrupted")
206      logger.debug('GPU PoW done')
207      return trialValue, nonce
208  
209  
210  # def estimate(difficulty, fmt=False):
211  #     ret = difficulty / 10
212  #     if ret < 1:
213  #         ret = 1
214  #
215  #     if fmt:
216  #         out = str(int(ret)) + " seconds"
217  #         if ret > 60:
218  #             ret /= 60
219  #             out = str(int(ret)) + " minutes"
220  #         if ret > 60:
221  #             ret /= 60
222  #             out = str(int(ret)) + " hours"
223  #         if ret > 24:
224  #             ret /= 24
225  #             out = str(int(ret)) + " days"
226  #         if ret > 7:
227  #             out = str(int(ret)) + " weeks"
228  #         if ret > 31:
229  #             out = str(int(ret)) + " months"
230  #         if ret > 366:
231  #             ret /= 366
232  #             out = str(int(ret)) + " years"
233  #         ret = None  # Ensure legacy behaviour
234  #
235  #     return ret
236  
237  
238  def getPowType():
239      """Get the proof of work implementation"""
240  
241      if openclpow.openclEnabled():
242          return "OpenCL"
243      if bmpow:
244          return "C"
245      return "python"
246  
247  
248  def notifyBuild(tried=False):
249      """
250      Notify the user of the success or otherwise of building the PoW C module
251      """
252  
253      if bmpow:
254          queues.UISignalQueue.put(('updateStatusBar', (_translate(
255              "proofofwork", "C PoW module built successfully."), 1)))
256      elif tried:
257          queues.UISignalQueue.put(('updateStatusBar', (_translate(
258              "proofofwork",
259              "Failed to build C PoW module. Please build it manually."), 1)))
260      else:
261          queues.UISignalQueue.put(('updateStatusBar', (_translate(
262              "proofofwork", "C PoW module unavailable. Please build it."), 1)))
263  
264  
265  def buildCPoW():
266      """Attempt to build the PoW C module"""
267      if bmpow is not None:
268          return
269      if paths.frozen or sys.platform.startswith('win'):
270          notifyBuild(False)
271          return
272  
273      try:
274          # GNU make
275          make_cmd = ['make', '-C', os.path.join(paths.codePath(), 'bitmsghash')]
276          if "bsd" in sys.platform:
277              # BSD make
278              make_cmd += ['-f', 'Makefile.bsd']
279  
280          subprocess.check_call(make_cmd)  # nosec B603
281          if os.path.exists(
282              os.path.join(paths.codePath(), 'bitmsghash', 'bitmsghash.so')
283          ):
284              init()
285      except (OSError, subprocess.CalledProcessError):
286          pass
287      except:  # noqa:E722
288          logger.warning(
289              'Unexpected exception rised when tried to build bitmsghash lib',
290              exc_info=True)
291      notifyBuild(True)
292  
293  
294  def run(target, initialHash):
295      """Run the proof of work calculation"""
296  
297      if state.shutdown != 0:
298          raise StopIteration("Interrupted")
299      target = int(target)
300      if openclpow.openclEnabled():
301          return _doGPUPoW(target, initialHash)
302      if bmpow:
303          return _doCPoW(target, initialHash)
304      if paths.frozen == "macosx_app" or not paths.frozen:
305          # on my (Peter Surda) Windows 10, Windows Defender
306          # does not like this and fights with PyBitmessage
307          # over CPU, resulting in very slow PoW
308          # added on 2015-11-29: multiprocesing.freeze_support() doesn't help
309          return _doFastPoW(target, initialHash)
310  
311      return _doSafePoW(target, initialHash)
312  
313  
314  def getTarget(payloadLength, ttl, nonceTrialsPerByte, payloadLengthExtraBytes):
315      """Get PoW target for given length, ttl and difficulty params"""
316      return 2 ** 64 / (
317          nonceTrialsPerByte * (
318              payloadLength + 8 + payloadLengthExtraBytes + ((
319                  ttl * (
320                      payloadLength + 8 + payloadLengthExtraBytes
321                  )) / (2 ** 16))
322          ))
323  
324  
325  def calculate(
326      payload, ttl,
327      nonceTrialsPerByte=networkDefaultProofOfWorkNonceTrialsPerByte,
328      payloadLengthExtraBytes=networkDefaultPayloadLengthExtraBytes
329  ):
330      """Do the PoW for the payload and TTL with optional difficulty params"""
331      return run(getTarget(
332          len(payload), ttl, nonceTrialsPerByte, payloadLengthExtraBytes),
333          hashlib.sha512(payload).digest())
334  
335  
336  def resetPoW():
337      """Initialise the OpenCL PoW"""
338      openclpow.initCL()
339  
340  
341  # init
342  
343  
344  def init():
345      """Initialise PoW"""
346      # pylint: disable=broad-exception-caught,global-statement
347      global bitmsglib, bmpow
348  
349      openclpow.initCL()
350      if sys.platform.startswith('win'):
351          bitmsglib = (
352              'bitmsghash32.dll' if ctypes.sizeof(ctypes.c_voidp) == 4 else
353              'bitmsghash64.dll')
354          libfile = os.path.join(paths.codePath(), 'bitmsghash', bitmsglib)
355          try:
356              # MSVS
357              bso = ctypes.WinDLL(
358                  os.path.join(paths.codePath(), 'bitmsghash', bitmsglib))
359              logger.info('Loaded C PoW DLL (stdcall) %s', bitmsglib)
360              bmpow = bso.BitmessagePOW
361              bmpow.restype = ctypes.c_ulonglong
362              _doCPoW(2**63, "")
363              logger.info(
364                  'Successfully tested C PoW DLL (stdcall) %s', bitmsglib)
365          except ValueError:
366              try:
367                  # MinGW
368                  bso = ctypes.CDLL(libfile)
369                  logger.info('Loaded C PoW DLL (cdecl) %s', bitmsglib)
370                  bmpow = bso.BitmessagePOW
371                  bmpow.restype = ctypes.c_ulonglong
372                  _doCPoW(2**63, "")
373                  logger.info(
374                      'Successfully tested C PoW DLL (cdecl) %s', bitmsglib)
375              except Exception as e:
376                  logger.error('Error: %s', e, exc_info=True)
377          except Exception as e:
378              logger.error('Error: %s', e, exc_info=True)
379      else:
380          try:
381              bso = ctypes.CDLL(
382                  os.path.join(paths.codePath(), 'bitmsghash', bitmsglib))
383          except OSError:
384              import glob
385              try:
386                  bso = ctypes.CDLL(glob.glob(os.path.join(
387                      paths.codePath(), 'bitmsghash', 'bitmsghash*.so'
388                  ))[0])
389              except (OSError, IndexError):
390                  bso = None
391          except Exception:
392              bso = None
393          else:
394              logger.info('Loaded C PoW DLL %s', bitmsglib)
395          if bso:
396              try:
397                  bmpow = bso.BitmessagePOW
398                  bmpow.restype = ctypes.c_ulonglong
399              except Exception:
400                  logger.warning(
401                      'Failed to setup bmpow lib %s', bso, exc_info=True)
402                  return
403  
404      if bmpow is None:
405          buildCPoW()