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()