diff --git a/setup.cfg b/setup.cfg index 28ceaede..c735e0a8 100644 --- a/setup.cfg +++ b/setup.cfg @@ -20,6 +20,8 @@ ignore = E722,F841,W503 disable=invalid-name,bare-except,broad-except # invalid-name: needs fixing during a large, project-wide refactor # bare-except,broad-except: Need fixing once thorough testing is easier +max-args = 8 +max-attributes = 8 [MASTER] init-hook = import sys;sys.path.append('src') diff --git a/src/api.py b/src/api.py index afd21779..9f61f40b 100644 --- a/src/api.py +++ b/src/api.py @@ -1410,15 +1410,10 @@ class BMRPCDispatcher(object): / networkDefaultPayloadLengthExtraBytes, ) powStartTime = time.time() - target = 2**64 / ( - nonceTrialsPerByte * ( - len(encryptedPayload) + 8 + payloadLengthExtraBytes + (( - TTL * ( - len(encryptedPayload) + 8 + payloadLengthExtraBytes - )) / (2 ** 16)) - )) - initialHash = hashlib.sha512(encryptedPayload).digest() - trialValue, nonce = proofofwork.run(target, initialHash) + trialValue, nonce = proofofwork.calculate( + encryptedPayload, TTL, + nonceTrialsPerByte, payloadLengthExtraBytes + ) logger.info( '(For msg message via API) Found proof of work %s\nNonce: %s\n' 'POW took %s seconds. %s nonce trials per second.', @@ -1429,9 +1424,7 @@ class BMRPCDispatcher(object): inventoryHash = calculateInventoryHash(encryptedPayload) state.Inventory[inventoryHash] = ( - objectType, toStreamNumber, encryptedPayload, - expiresTime, b'' - ) + objectType, toStreamNumber, encryptedPayload, expiresTime, b'') logger.info( 'Broadcasting inv for msg(API disseminatePreEncryptedMsg' ' command): %s', hexlify(inventoryHash)) diff --git a/src/class_singleWorker.py b/src/class_singleWorker.py index f79d9240..83009c37 100644 --- a/src/class_singleWorker.py +++ b/src/class_singleWorker.py @@ -217,36 +217,36 @@ class singleWorker(StoppableThread): return privSigningKeyHex, privEncryptionKeyHex, \ pubSigningKey, pubEncryptionKey - def _doPOWDefaults(self, payload, TTL, - log_prefix='', - log_time=False): - target = 2 ** 64 / ( - defaults.networkDefaultProofOfWorkNonceTrialsPerByte * ( - len(payload) + 8 - + defaults.networkDefaultPayloadLengthExtraBytes + (( - TTL * ( - len(payload) + 8 - + defaults.networkDefaultPayloadLengthExtraBytes - )) / (2 ** 16)) - )) - initialHash = hashlib.sha512(payload).digest() - self.logger.info( + @classmethod + def _doPOWDefaults( + cls, payload, TTL, + nonceTrialsPerByte=None, payloadLengthExtraBytes=None, + log_prefix='', log_time=False + ): + if not nonceTrialsPerByte: + nonceTrialsPerByte = \ + defaults.networkDefaultProofOfWorkNonceTrialsPerByte + if not payloadLengthExtraBytes: + payloadLengthExtraBytes = \ + defaults.networkDefaultPayloadLengthExtraBytes + cls.logger.info( '%s Doing proof of work... TTL set to %s', log_prefix, TTL) if log_time: start_time = time.time() - trialValue, nonce = proofofwork.run(target, initialHash) - self.logger.info( + trialValue, nonce = proofofwork.calculate( + payload, TTL, nonceTrialsPerByte, payloadLengthExtraBytes) + cls.logger.info( '%s Found proof of work %s Nonce: %s', log_prefix, trialValue, nonce ) try: delta = time.time() - start_time - self.logger.info( + cls.logger.info( 'PoW took %.1f seconds, speed %s.', delta, sizeof_fmt(nonce / delta) ) - except: # noqa:E722 # NameError - self.logger.warning("Proof of Work exception") + except NameError: # no start_time - no logging + pass payload = pack('>Q', nonce) + payload return payload @@ -1250,41 +1250,13 @@ class singleWorker(StoppableThread): encryptedPayload += '\x00\x00\x00\x02' # object type: msg encryptedPayload += encodeVarint(1) # msg version encryptedPayload += encodeVarint(toStreamNumber) + encrypted - target = 2 ** 64 / ( - requiredAverageProofOfWorkNonceTrialsPerByte * ( - len(encryptedPayload) + 8 - + requiredPayloadLengthExtraBytes + (( - TTL * ( - len(encryptedPayload) + 8 - + requiredPayloadLengthExtraBytes - )) / (2 ** 16)) - )) - self.logger.info( - '(For msg message) Doing proof of work. Total required' - ' difficulty: %f. Required small message difficulty: %f.', - float(requiredAverageProofOfWorkNonceTrialsPerByte) - / defaults.networkDefaultProofOfWorkNonceTrialsPerByte, - float(requiredPayloadLengthExtraBytes) - / defaults.networkDefaultPayloadLengthExtraBytes - ) - powStartTime = time.time() - initialHash = hashlib.sha512(encryptedPayload).digest() - trialValue, nonce = proofofwork.run(target, initialHash) - self.logger.info( - '(For msg message) Found proof of work %s Nonce: %s', - trialValue, nonce + encryptedPayload = self._doPOWDefaults( + encryptedPayload, TTL, + requiredAverageProofOfWorkNonceTrialsPerByte, + requiredPayloadLengthExtraBytes, + log_prefix='(For msg message)', log_time=True ) - try: - self.logger.info( - 'PoW took %.1f seconds, speed %s.', - time.time() - powStartTime, - sizeof_fmt(nonce / (time.time() - powStartTime)) - ) - except: # noqa:E722 - self.logger.warning("Proof of Work exception") - - encryptedPayload = pack('>Q', nonce) + encryptedPayload # Sanity check. The encryptedPayload size should never be # larger than 256 KiB. There should be checks elsewhere diff --git a/src/proofofwork.py b/src/proofofwork.py index 5e157db9..539db710 100644 --- a/src/proofofwork.py +++ b/src/proofofwork.py @@ -1,9 +1,10 @@ -# pylint: disable=too-many-branches,too-many-statements,protected-access """ Proof of work calculation """ +# pylint: disable=import-outside-toplevel import ctypes +import hashlib import os import subprocess # nosec B404 import sys @@ -16,9 +17,13 @@ import openclpow import paths import queues import state -import tr from bmconfigparser import config from debug import logger +from defaults import ( + networkDefaultProofOfWorkNonceTrialsPerByte, + networkDefaultPayloadLengthExtraBytes) +from tr import _translate + bitmsglib = 'bitmsghash.so' bmpow = None @@ -79,10 +84,13 @@ def _set_idle(): import win32api import win32process import win32con - pid = win32api.GetCurrentProcessId() - handle = win32api.OpenProcess(win32con.PROCESS_ALL_ACCESS, True, pid) - win32process.SetPriorityClass(handle, win32process.IDLE_PRIORITY_CLASS) - except: # nosec B110 # noqa:E722 # pylint:disable=bare-except + + handle = win32api.OpenProcess( + win32con.PROCESS_ALL_ACCESS, True, + win32api.GetCurrentProcessId()) + win32process.SetPriorityClass( + handle, win32process.IDLE_PRIORITY_CLASS) + except: # nosec B110 # noqa:E722 pylint:disable=bare-except # Windows 64-bit pass @@ -101,11 +109,11 @@ def _pool_worker(nonce, initialHash, target, pool_size): while trialValue > target: nonce += pool_size trialValue = trial_value(nonce, initialHash) - return [trialValue, nonce] + return trialValue, nonce def _doSafePoW(target, initialHash): - logger.debug("Safe PoW start") + logger.debug('Safe PoW start') nonce = 0 trialValue = float('inf') while trialValue > target and state.shutdown == 0: @@ -113,35 +121,33 @@ def _doSafePoW(target, initialHash): trialValue = trial_value(nonce, initialHash) if state.shutdown != 0: raise StopIteration("Interrupted") - logger.debug("Safe PoW done") - return [trialValue, nonce] + logger.debug('Safe PoW done') + return trialValue, nonce def _doFastPoW(target, initialHash): - logger.debug("Fast PoW start") + # pylint:disable=bare-except + logger.debug('Fast PoW start') from multiprocessing import Pool, cpu_count try: pool_size = cpu_count() except: # noqa:E722 pool_size = 4 - try: - maxCores = config.getint('bitmessagesettings', 'maxcores') - except: # noqa:E722 - maxCores = 99999 - if pool_size > maxCores: - pool_size = maxCores + maxCores = config.safeGetInt('bitmessagesettings', 'maxcores', 99999) + pool_size = min(pool_size, maxCores) pool = Pool(processes=pool_size) result = [] for i in range(pool_size): - result.append(pool.apply_async(_pool_worker, args=(i, initialHash, target, pool_size))) + result.append(pool.apply_async( + _pool_worker, args=(i, initialHash, target, pool_size))) while True: - if state.shutdown > 0: + if state.shutdown != 0: try: pool.terminate() pool.join() - except: # nosec B110 # noqa:E722 # pylint:disable=bare-except + except: # nosec B110 # noqa:E722 pass raise StopIteration("Interrupted") for i in range(pool_size): @@ -155,7 +161,7 @@ def _doFastPoW(target, initialHash): result = result[i].get() pool.terminate() pool.join() - logger.debug("Fast PoW done") + logger.debug('Fast PoW done') return result[0], result[1] time.sleep(0.2) @@ -166,70 +172,67 @@ def _doCPoW(target, initialHash): m = target out_h = ctypes.pointer(ctypes.create_string_buffer(h, 64)) out_m = ctypes.c_ulonglong(m) - logger.debug("C PoW start") + logger.debug('C PoW start') nonce = bmpow(out_h, out_m) trialValue = trial_value(nonce, initialHash) if state.shutdown != 0: raise StopIteration("Interrupted") - logger.debug("C PoW done") - return [trialValue, nonce] + logger.debug('C PoW done') + return trialValue, nonce def _doGPUPoW(target, initialHash): - logger.debug("GPU PoW start") + logger.debug('GPU PoW start') nonce = openclpow.do_opencl_pow(initialHash.encode("hex"), target) trialValue = trial_value(nonce, initialHash) if trialValue > target: deviceNames = ", ".join(gpu.name for gpu in openclpow.enabledGpus) queues.UISignalQueue.put(( 'updateStatusBar', ( - tr._translate( + _translate( "MainWindow", - 'Your GPU(s) did not calculate correctly, disabling OpenCL. Please report to the developers.' - ), - 1))) + "Your GPU(s) did not calculate correctly," + " disabling OpenCL. Please report to the developers." + ), 1) + )) logger.error( - "Your GPUs (%s) did not calculate correctly, disabling OpenCL. Please report to the developers.", - deviceNames) + 'Your GPUs (%s) did not calculate correctly, disabling OpenCL.' + ' Please report to the developers.', deviceNames) openclpow.enabledGpus = [] raise Exception("GPU did not calculate correctly.") if state.shutdown != 0: raise StopIteration("Interrupted") - logger.debug("GPU PoW done") - return [trialValue, nonce] + logger.debug('GPU PoW done') + return trialValue, nonce -def estimate(difficulty, format=False): # pylint: disable=redefined-builtin - """ - .. todo: fix unused variable - """ - ret = difficulty / 10 - if ret < 1: - ret = 1 - - if format: - # pylint: disable=unused-variable - out = str(int(ret)) + " seconds" - if ret > 60: - ret /= 60 - out = str(int(ret)) + " minutes" - if ret > 60: - ret /= 60 - out = str(int(ret)) + " hours" - if ret > 24: - ret /= 24 - out = str(int(ret)) + " days" - if ret > 7: - out = str(int(ret)) + " weeks" - if ret > 31: - out = str(int(ret)) + " months" - if ret > 366: - ret /= 366 - out = str(int(ret)) + " years" - ret = None # Ensure legacy behaviour - - return ret +# def estimate(difficulty, fmt=False): +# ret = difficulty / 10 +# if ret < 1: +# ret = 1 +# +# if fmt: +# out = str(int(ret)) + " seconds" +# if ret > 60: +# ret /= 60 +# out = str(int(ret)) + " minutes" +# if ret > 60: +# ret /= 60 +# out = str(int(ret)) + " hours" +# if ret > 24: +# ret /= 24 +# out = str(int(ret)) + " days" +# if ret > 7: +# out = str(int(ret)) + " weeks" +# if ret > 31: +# out = str(int(ret)) + " months" +# if ret > 366: +# ret /= 366 +# out = str(int(ret)) + " years" +# ret = None # Ensure legacy behaviour +# +# return ret def getPowType(): @@ -243,25 +246,19 @@ def getPowType(): def notifyBuild(tried=False): - """Notify the user of the success or otherwise of building the PoW C module""" + """ + Notify the user of the success or otherwise of building the PoW C module + """ if bmpow: - queues.UISignalQueue.put(('updateStatusBar', (tr._translate( + queues.UISignalQueue.put(('updateStatusBar', (_translate( "proofofwork", "C PoW module built successfully."), 1))) elif tried: - queues.UISignalQueue.put( - ( - 'updateStatusBar', ( - tr._translate( - "proofofwork", - "Failed to build C PoW module. Please build it manually." - ), - 1 - ) - ) - ) + queues.UISignalQueue.put(('updateStatusBar', (_translate( + "proofofwork", + "Failed to build C PoW module. Please build it manually."), 1))) else: - queues.UISignalQueue.put(('updateStatusBar', (tr._translate( + queues.UISignalQueue.put(('updateStatusBar', (_translate( "proofofwork", "C PoW module unavailable. Please build it."), 1))) @@ -269,76 +266,71 @@ def buildCPoW(): """Attempt to build the PoW C module""" if bmpow is not None: return - if paths.frozen is not None: - notifyBuild(False) - return - if sys.platform in ["win32", "win64"]: + if paths.frozen or sys.platform.startswith('win'): notifyBuild(False) return + try: + # GNU make + make_cmd = ['make', '-C', os.path.join(paths.codePath(), 'bitmsghash')] if "bsd" in sys.platform: # BSD make - subprocess.check_call([ # nosec B607, B603 - "make", "-C", os.path.join(paths.codePath(), "bitmsghash"), - '-f', 'Makefile.bsd']) - else: - # GNU make - subprocess.check_call([ # nosec B607, B603 - "make", "-C", os.path.join(paths.codePath(), "bitmsghash")]) + make_cmd += ['-f', 'Makefile.bsd'] + + subprocess.check_call(make_cmd) # nosec B603 if os.path.exists( - os.path.join(paths.codePath(), "bitmsghash", "bitmsghash.so") + os.path.join(paths.codePath(), 'bitmsghash', 'bitmsghash.so') ): init() - notifyBuild(True) - else: - notifyBuild(True) except (OSError, subprocess.CalledProcessError): - notifyBuild(True) + pass except: # noqa:E722 logger.warning( 'Unexpected exception rised when tried to build bitmsghash lib', exc_info=True) - notifyBuild(True) + notifyBuild(True) def run(target, initialHash): - """Run the proof of work thread""" + """Run the proof of work calculation""" if state.shutdown != 0: - raise # pylint: disable=misplaced-bare-raise + raise StopIteration("Interrupted") target = int(target) if openclpow.openclEnabled(): - try: - return _doGPUPoW(target, initialHash) - except StopIteration: - raise - except: # nosec B110 # noqa:E722 # pylint:disable=bare-except - pass # fallback + return _doGPUPoW(target, initialHash) if bmpow: - try: - return _doCPoW(target, initialHash) - except StopIteration: - raise - except: # nosec B110 # noqa:E722 # pylint:disable=bare-except - pass # fallback + return _doCPoW(target, initialHash) if paths.frozen == "macosx_app" or not paths.frozen: # on my (Peter Surda) Windows 10, Windows Defender # does not like this and fights with PyBitmessage # over CPU, resulting in very slow PoW # added on 2015-11-29: multiprocesing.freeze_support() doesn't help - try: - return _doFastPoW(target, initialHash) - except StopIteration: - logger.error("Fast PoW got StopIteration") - raise - except: # noqa:E722 # pylint:disable=bare-except - logger.error("Fast PoW got exception:", exc_info=True) - try: - return _doSafePoW(target, initialHash) - except StopIteration: - raise - except: # nosec B110 # noqa:E722 # pylint:disable=bare-except - pass # fallback + return _doFastPoW(target, initialHash) + + return _doSafePoW(target, initialHash) + + +def getTarget(payloadLength, ttl, nonceTrialsPerByte, payloadLengthExtraBytes): + """Get PoW target for given length, ttl and difficulty params""" + return 2 ** 64 / ( + nonceTrialsPerByte * ( + payloadLength + 8 + payloadLengthExtraBytes + (( + ttl * ( + payloadLength + 8 + payloadLengthExtraBytes + )) / (2 ** 16)) + )) + + +def calculate( + payload, ttl, + nonceTrialsPerByte=networkDefaultProofOfWorkNonceTrialsPerByte, + payloadLengthExtraBytes=networkDefaultPayloadLengthExtraBytes +): + """Do the PoW for the payload and TTL with optional difficulty params""" + return run(getTarget( + len(payload), ttl, nonceTrialsPerByte, payloadLengthExtraBytes), + hashlib.sha512(payload).digest()) def resetPoW(): @@ -351,60 +343,63 @@ def resetPoW(): def init(): """Initialise PoW""" - # pylint: disable=global-statement + # pylint: disable=broad-exception-caught,global-statement global bitmsglib, bmpow openclpow.initCL() - if sys.platform == "win32": - if ctypes.sizeof(ctypes.c_voidp) == 4: - bitmsglib = 'bitmsghash32.dll' - else: - bitmsglib = 'bitmsghash64.dll' + if sys.platform.startswith('win'): + bitmsglib = ( + 'bitmsghash32.dll' if ctypes.sizeof(ctypes.c_voidp) == 4 else + 'bitmsghash64.dll') + libfile = os.path.join(paths.codePath(), 'bitmsghash', bitmsglib) try: # MSVS - bso = ctypes.WinDLL(os.path.join(paths.codePath(), "bitmsghash", bitmsglib)) - logger.info("Loaded C PoW DLL (stdcall) %s", bitmsglib) + bso = ctypes.WinDLL( + os.path.join(paths.codePath(), 'bitmsghash', bitmsglib)) + logger.info('Loaded C PoW DLL (stdcall) %s', bitmsglib) bmpow = bso.BitmessagePOW bmpow.restype = ctypes.c_ulonglong _doCPoW(2**63, "") - logger.info("Successfully tested C PoW DLL (stdcall) %s", bitmsglib) + logger.info( + 'Successfully tested C PoW DLL (stdcall) %s', bitmsglib) except ValueError: try: # MinGW - bso = ctypes.CDLL(os.path.join(paths.codePath(), "bitmsghash", bitmsglib)) - logger.info("Loaded C PoW DLL (cdecl) %s", bitmsglib) + bso = ctypes.CDLL(libfile) + logger.info('Loaded C PoW DLL (cdecl) %s', bitmsglib) bmpow = bso.BitmessagePOW bmpow.restype = ctypes.c_ulonglong _doCPoW(2**63, "") - logger.info("Successfully tested C PoW DLL (cdecl) %s", bitmsglib) + logger.info( + 'Successfully tested C PoW DLL (cdecl) %s', bitmsglib) except Exception as e: - logger.error("Error: %s", e, exc_info=True) - bso = None + logger.error('Error: %s', e, exc_info=True) except Exception as e: - logger.error("Error: %s", e, exc_info=True) - bso = None + logger.error('Error: %s', e, exc_info=True) else: try: - bso = ctypes.CDLL(os.path.join(paths.codePath(), "bitmsghash", bitmsglib)) + bso = ctypes.CDLL( + os.path.join(paths.codePath(), 'bitmsghash', bitmsglib)) except OSError: import glob try: bso = ctypes.CDLL(glob.glob(os.path.join( - paths.codePath(), "bitmsghash", "bitmsghash*.so" + paths.codePath(), 'bitmsghash', 'bitmsghash*.so' ))[0]) except (OSError, IndexError): bso = None - except: # noqa:E722 + except Exception: bso = None else: - logger.info("Loaded C PoW DLL %s", bitmsglib) - if bso: - try: - bmpow = bso.BitmessagePOW - bmpow.restype = ctypes.c_ulonglong - except: # noqa:E722 - bmpow = None - else: - bmpow = None + logger.info('Loaded C PoW DLL %s', bitmsglib) + if bso: + try: + bmpow = bso.BitmessagePOW + bmpow.restype = ctypes.c_ulonglong + except Exception: + logger.warning( + 'Failed to setup bmpow lib %s', bso, exc_info=True) + return + if bmpow is None: buildCPoW() diff --git a/src/tests/partial.py b/src/tests/partial.py index 870f6626..f97dc414 100644 --- a/src/tests/partial.py +++ b/src/tests/partial.py @@ -2,6 +2,7 @@ import os import sys +import time import unittest from pybitmessage import pathmagic @@ -39,3 +40,5 @@ class TestPartialRun(unittest.TestCase): # deactivate pathmagic os.chdir(cls.dirs[0]) sys.path.remove(cls.dirs[1]) + time.sleep(5) + cls.state.shutdown = 0 diff --git a/src/tests/samples.py b/src/tests/samples.py index dd862318..55debea3 100644 --- a/src/tests/samples.py +++ b/src/tests/samples.py @@ -91,3 +91,12 @@ sample_privsigningkey_wif = \ b'5K42shDERM5g7Kbi3JT5vsAWpXMqRhWZpX835M2pdSoqQQpJMYm' sample_privencryptionkey_wif = \ b'5HwugVWm31gnxtoYcvcK7oywH2ezYTh6Y4tzRxsndAeMi6NHqpA' + + +# PoW + +sample_pow_target = 54227212183 +sample_pow_initial_hash = unhexlify( + '3758f55b5a8d902fd3597e4ce6a2d3f23daff735f65d9698c270987f4e67ad590' + 'b93f3ffeba0ef2fd08a8dc2f87b68ae5a0dc819ab57f22ad2c4c9c8618a43b3' +) diff --git a/src/tests/test_openclpow.py b/src/tests/test_openclpow.py index 4770072e..d9ccbe2e 100644 --- a/src/tests/test_openclpow.py +++ b/src/tests/test_openclpow.py @@ -3,9 +3,12 @@ Tests for openclpow module """ import unittest +from binascii import hexlify from pybitmessage import openclpow, proofofwork +from .samples import sample_pow_target, sample_pow_initial_hash + class TestOpenClPow(unittest.TestCase): """ @@ -19,11 +22,8 @@ class TestOpenClPow(unittest.TestCase): @unittest.skipUnless(openclpow.enabledGpus, "No GPUs found / enabled") def test_openclpow(self): """Check the working of openclpow module""" - target_ = 54227212183 - initialHash = ( - "3758f55b5a8d902fd3597e4ce6a2d3f23daff735f65d9698c270987f4e67ad590" - "b93f3ffeba0ef2fd08a8dc2f87b68ae5a0dc819ab57f22ad2c4c9c8618a43b3" - ).decode("hex") - nonce = openclpow.do_opencl_pow(initialHash.encode("hex"), target_) + nonce = openclpow.do_opencl_pow( + hexlify(sample_pow_initial_hash), sample_pow_target) self.assertLess( - nonce - proofofwork.trial_value(nonce, initialHash), target_) + nonce - proofofwork.trial_value(nonce, sample_pow_initial_hash), + sample_pow_target) diff --git a/src/tests/test_proofofwork.py b/src/tests/test_proofofwork.py new file mode 100644 index 00000000..16ff649d --- /dev/null +++ b/src/tests/test_proofofwork.py @@ -0,0 +1,120 @@ +""" +Tests for proofofwork module +""" +# pylint: disable=protected-access + +import hashlib +import os +import time +import unittest +from struct import pack, unpack + +from pybitmessage import proofofwork, protocol +from pybitmessage.defaults import ( + networkDefaultProofOfWorkNonceTrialsPerByte, + networkDefaultPayloadLengthExtraBytes) + +from .partial import TestPartialRun +from .samples import sample_pow_target, sample_pow_initial_hash + +default_ttl = 7200 + + +class TestProofofworkBase(TestPartialRun): + """Basic test case for proofofwork""" + + @classmethod + def setUpClass(cls): + proofofwork.init() + super(TestProofofworkBase, cls).setUpClass() + + def setUp(self): + self.state.shutdown = 0 + + @staticmethod + def _make_sample_payload(TTL=default_ttl): + return pack('>Q', int(time.time() + TTL)) + os.urandom(166) + + def test_calculate(self): + """Ensure a calculated nonce has sufficient work for the protocol""" + payload = self._make_sample_payload() + nonce = proofofwork.calculate(payload, default_ttl)[1] + self.assertTrue( + protocol.isProofOfWorkSufficient(pack('>Q', nonce) + payload)) + + # pylint: disable=import-outside-toplevel + from class_singleWorker import singleWorker + + self.assertTrue(protocol.isProofOfWorkSufficient( + singleWorker._doPOWDefaults(payload, default_ttl))) + + +@unittest.skipUnless( + os.getenv('BITMESSAGE_TEST_POW'), "BITMESSAGE_TEST_POW is not set") +class TestProofofwork(TestProofofworkBase): + """The main test case for proofofwork""" + + def _make_sample_data(self): + payload = self._make_sample_payload() + return payload, proofofwork.getTarget( + len(payload), default_ttl, + networkDefaultProofOfWorkNonceTrialsPerByte, + networkDefaultPayloadLengthExtraBytes + ), hashlib.sha512(payload).digest() + + def test_calculate(self): + """Extended test for the main proofofwork call""" + # raise difficulty and TTL + TTL = 24 * 60 * 60 + payload = self._make_sample_payload(TTL) + nonce = proofofwork.calculate(payload, TTL, 2000, 2000)[1] + self.assertTrue( + protocol.isProofOfWorkSufficient( + pack('>Q', nonce) + payload, 2000, 2000, + int(time.time()) + TTL - 3600)) + + # pylint: disable=import-outside-toplevel + from class_singleWorker import singleWorker + + # pylint: disable=no-member + with self.assertLogs('default') as cm: + self.assertTrue(protocol.isProofOfWorkSufficient( + singleWorker._doPOWDefaults(payload, TTL, log_prefix='+'))) + self.assertEqual( + cm.output[0], + 'INFO:default:+ Doing proof of work... TTL set to %s' % TTL) + self.assertEqual( + cm.output[1][:34], 'INFO:default:+ Found proof of work') + + with self.assertLogs('default') as cm: + self.assertTrue(protocol.isProofOfWorkSufficient( + singleWorker._doPOWDefaults(payload, TTL, log_time=True))) + self.assertEqual(cm.output[2][:22], 'INFO:default:PoW took ') + + with self.assertRaises(StopIteration): + self.state.shutdown = 1 + proofofwork.calculate(payload, TTL) + + def test_CPoW(self): + """Do PoW with parameters from test_openclpow and check the result""" + nonce = proofofwork._doCPoW( + sample_pow_target, sample_pow_initial_hash)[0] + trial_value, = unpack( + '>Q', hashlib.sha512(hashlib.sha512( + pack('>Q', nonce) + sample_pow_initial_hash + ).digest()).digest()[0:8]) + self.assertLess((nonce - trial_value), sample_pow_target) + + def test_SafePoW(self): + """Do python PoW for a sample payload and check by protocol""" + payload, target, initial_hash = self._make_sample_data() + nonce = proofofwork._doSafePoW(target, initial_hash)[1] + self.assertTrue( + protocol.isProofOfWorkSufficient(pack('>Q', nonce) + payload)) + + def test_FastPoW(self): + """Do python multiprocessing PoW for a sample payload and check""" + payload, target, initial_hash = self._make_sample_data() + nonce = proofofwork._doFastPoW(target, initial_hash)[1] + self.assertTrue( + protocol.isProofOfWorkSufficient(pack('>Q', nonce) + payload)) diff --git a/tox.ini b/tox.ini index 3524bb57..2acf7a86 100644 --- a/tox.ini +++ b/tox.ini @@ -50,6 +50,11 @@ commands = python pybitmessage/bitmessagemain.py -t [testenv:py35] skip_install = true +[testenv:py36] +setenv = + BITMESSAGE_TEST_POW = true + {[testenv]setenv} + [testenv:reset] skip_install = true deps = coverage