From e1ae33389c2cb91dfb8941a3461f4d0959714257 Mon Sep 17 00:00:00 2001 From: Lee Miller Date: Sun, 1 Jan 2023 22:45:54 +0200 Subject: [PATCH 1/5] Properly format all the keys samples --- src/tests/samples.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tests/samples.py b/src/tests/samples.py index a80a61ae..d96187ca 100644 --- a/src/tests/samples.py +++ b/src/tests/samples.py @@ -20,11 +20,11 @@ sample_addr_data = unhexlify( # These keys are from addresses test script sample_pubsigningkey = unhexlify( - '044a367f049ec16cb6b6118eb734a9962d10b8db59c890cd08f210c43ff08bdf09d' - '16f502ca26cd0713f38988a1237f1fc8fa07b15653c996dc4013af6d15505ce') + '044a367f049ec16cb6b6118eb734a9962d10b8db59c890cd08f210c43ff08bdf09' + 'd16f502ca26cd0713f38988a1237f1fc8fa07b15653c996dc4013af6d15505ce') sample_pubencryptionkey = unhexlify( - '044597d59177fc1d89555d38915f581b5ff2286b39d022ca0283d2bdd5c36be5d3c' - 'e7b9b97792327851a562752e4b79475d1f51f5a71352482b241227f45ed36a9') + '044597d59177fc1d89555d38915f581b5ff2286b39d022ca0283d2bdd5c36be5d3' + 'ce7b9b97792327851a562752e4b79475d1f51f5a71352482b241227f45ed36a9') sample_privsigningkey = \ b'93d0b61371a54b53df143b954035d612f8efa8a3ed1cf842c2186bfd8f876665' sample_privencryptionkey = \ -- 2.45.1 From 44a4a370a6ef17148bcac65ad796073110abac9f Mon Sep 17 00:00:00 2001 From: Lee Miller Date: Thu, 5 Jan 2023 05:23:53 +0200 Subject: [PATCH 2/5] Use defaults while doing PoW for a preencrypted msg --- src/api.py | 58 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/src/api.py b/src/api.py index 4bf316f2..90e3d07c 100644 --- a/src/api.py +++ b/src/api.py @@ -71,7 +71,6 @@ from struct import pack import six from six.moves import configparser, http_client, xmlrpc_server -import defaults import helper_inbox import helper_sent import protocol @@ -89,6 +88,9 @@ from addresses import ( ) from bmconfigparser import config from debug import logger +from defaults import ( + networkDefaultProofOfWorkNonceTrialsPerByte, + networkDefaultPayloadLengthExtraBytes) from helper_sql import ( SqlBulkExecute, sqlExecute, sqlQuery, sqlStoredProcedure, sql_ready) from highlevelcrypto import calculateInventoryHash @@ -657,13 +659,11 @@ class BMRPCDispatcher(object): nonceTrialsPerByte = self.config.get( 'bitmessagesettings', 'defaultnoncetrialsperbyte' ) if not totalDifficulty else int( - defaults.networkDefaultProofOfWorkNonceTrialsPerByte - * totalDifficulty) + networkDefaultProofOfWorkNonceTrialsPerByte * totalDifficulty) payloadLengthExtraBytes = self.config.get( 'bitmessagesettings', 'defaultpayloadlengthextrabytes' ) if not smallMessageDifficulty else int( - defaults.networkDefaultPayloadLengthExtraBytes - * smallMessageDifficulty) + networkDefaultPayloadLengthExtraBytes * smallMessageDifficulty) if not isinstance(eighteenByteRipe, bool): raise APIError( @@ -705,13 +705,11 @@ class BMRPCDispatcher(object): nonceTrialsPerByte = self.config.get( 'bitmessagesettings', 'defaultnoncetrialsperbyte' ) if not totalDifficulty else int( - defaults.networkDefaultProofOfWorkNonceTrialsPerByte - * totalDifficulty) + networkDefaultProofOfWorkNonceTrialsPerByte * totalDifficulty) payloadLengthExtraBytes = self.config.get( 'bitmessagesettings', 'defaultpayloadlengthextrabytes' ) if not smallMessageDifficulty else int( - defaults.networkDefaultPayloadLengthExtraBytes - * smallMessageDifficulty) + networkDefaultPayloadLengthExtraBytes * smallMessageDifficulty) if not passphrase: raise APIError(1, 'The specified passphrase is blank.') @@ -1286,15 +1284,21 @@ class BMRPCDispatcher(object): @command('disseminatePreEncryptedMsg') def HandleDisseminatePreEncryptedMsg( - self, encryptedPayload, requiredAverageProofOfWorkNonceTrialsPerByte, - requiredPayloadLengthExtraBytes): - """Handle a request to disseminate an encrypted message""" + self, encryptedPayload, + nonceTrialsPerByte=networkDefaultProofOfWorkNonceTrialsPerByte, + payloadLengthExtraBytes=networkDefaultPayloadLengthExtraBytes + ): + """ + Handle a request to disseminate an encrypted message. - # The device issuing this command to PyBitmessage supplies a msg - # object that has already been encrypted but which still needs the POW - # to be done. PyBitmessage accepts this msg object and sends it out - # to the rest of the Bitmessage network as if it had generated - # the message itself. Please do not yet add this to the api doc. + The device issuing this command to PyBitmessage supplies a msg object + that has already been encrypted but which still needs the PoW + to be done. PyBitmessage accepts this msg object and sends it out + to the rest of the Bitmessage network as if it had generated + the message itself. + + *encryptedPayload* is a hex encoded string + """ encryptedPayload = b'\x00' * 8 + self._decode(encryptedPayload, "hex") # compatibility stub ^, since disseminatePreEncryptedMsg # still expects the encryptedPayload without a nonce @@ -1304,12 +1308,10 @@ class BMRPCDispatcher(object): TTL = expiresTime - time.time() + 300 # a bit of extra padding # Let us do the POW and attach it to the front target = 2**64 / ( - requiredAverageProofOfWorkNonceTrialsPerByte * ( - len(encryptedPayload) + 8 - + requiredPayloadLengthExtraBytes + (( + nonceTrialsPerByte * ( + len(encryptedPayload) + 8 + payloadLengthExtraBytes + (( TTL * ( - len(encryptedPayload) + 8 - + requiredPayloadLengthExtraBytes + len(encryptedPayload) + 8 + payloadLengthExtraBytes )) / (2 ** 16)) )) logger.debug("expiresTime: %s", expiresTime) @@ -1318,10 +1320,10 @@ class BMRPCDispatcher(object): logger.info( '(For msg message via API) Doing proof of work. Total required' ' difficulty: %s\nRequired small message difficulty: %s', - float(requiredAverageProofOfWorkNonceTrialsPerByte) - / defaults.networkDefaultProofOfWorkNonceTrialsPerByte, - float(requiredPayloadLengthExtraBytes) - / defaults.networkDefaultPayloadLengthExtraBytes, + float(nonceTrialsPerByte) + / networkDefaultProofOfWorkNonceTrialsPerByte, + float(payloadLengthExtraBytes) + / networkDefaultPayloadLengthExtraBytes, ) powStartTime = time.time() initialHash = hashlib.sha512(encryptedPayload).digest() @@ -1365,8 +1367,8 @@ class BMRPCDispatcher(object): # Let us do the POW target = 2 ** 64 / (( - len(payload) + defaults.networkDefaultPayloadLengthExtraBytes + 8 - ) * defaults.networkDefaultProofOfWorkNonceTrialsPerByte) + len(payload) + networkDefaultPayloadLengthExtraBytes + 8 + ) * networkDefaultProofOfWorkNonceTrialsPerByte) logger.info('(For pubkey message via API) Doing proof of work...') initialHash = hashlib.sha512(payload).digest() trialValue, nonce = proofofwork.run(target, initialHash) -- 2.45.1 From 78365382905b218680a03164f553b564846e2790 Mon Sep 17 00:00:00 2001 From: Lee Miller Date: Thu, 5 Jan 2023 07:17:46 +0200 Subject: [PATCH 3/5] Make PoW optional in disseminatePreEncryptedMsg --- src/api.py | 78 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 41 insertions(+), 37 deletions(-) diff --git a/src/api.py b/src/api.py index 90e3d07c..f72863ce 100644 --- a/src/api.py +++ b/src/api.py @@ -66,7 +66,7 @@ import socket import subprocess # nosec B404 import time from binascii import hexlify, unhexlify -from struct import pack +from struct import pack, unpack import six from six.moves import configparser, http_client, xmlrpc_server @@ -1292,49 +1292,53 @@ class BMRPCDispatcher(object): Handle a request to disseminate an encrypted message. The device issuing this command to PyBitmessage supplies a msg object - that has already been encrypted but which still needs the PoW + that has already been encrypted but which may still need the PoW to be done. PyBitmessage accepts this msg object and sends it out to the rest of the Bitmessage network as if it had generated the message itself. - *encryptedPayload* is a hex encoded string + *encryptedPayload* is a hex encoded string starting with the nonce, + 8 zero bytes in case of no PoW done. """ - encryptedPayload = b'\x00' * 8 + self._decode(encryptedPayload, "hex") - # compatibility stub ^, since disseminatePreEncryptedMsg - # still expects the encryptedPayload without a nonce + encryptedPayload = self._decode(encryptedPayload, "hex") + + nonce, = unpack('>Q', encryptedPayload[:8]) objectType, toStreamNumber, expiresTime = \ protocol.decodeObjectParameters(encryptedPayload) - encryptedPayload = encryptedPayload[8:] - TTL = expiresTime - time.time() + 300 # a bit of extra padding - # Let us do the POW and attach it to the front - target = 2**64 / ( - nonceTrialsPerByte * ( - len(encryptedPayload) + 8 + payloadLengthExtraBytes + (( - TTL * ( - len(encryptedPayload) + 8 + payloadLengthExtraBytes - )) / (2 ** 16)) - )) - logger.debug("expiresTime: %s", expiresTime) - logger.debug("TTL: %s", TTL) - logger.debug("objectType: %s", objectType) - logger.info( - '(For msg message via API) Doing proof of work. Total required' - ' difficulty: %s\nRequired small message difficulty: %s', - float(nonceTrialsPerByte) - / networkDefaultProofOfWorkNonceTrialsPerByte, - float(payloadLengthExtraBytes) - / networkDefaultPayloadLengthExtraBytes, - ) - powStartTime = time.time() - initialHash = hashlib.sha512(encryptedPayload).digest() - trialValue, nonce = proofofwork.run(target, initialHash) - logger.info( - '(For msg message via API) Found proof of work %s\nNonce: %s\n' - 'POW took %s seconds. %s nonce trials per second.', - trialValue, nonce, int(time.time() - powStartTime), - nonce / (time.time() - powStartTime) - ) - encryptedPayload = pack('>Q', nonce) + encryptedPayload + + if nonce == 0: # Let us do the POW and attach it to the front + encryptedPayload = encryptedPayload[8:] + TTL = expiresTime - time.time() + 300 # a bit of extra padding + # Let us do the POW and attach it to the front + logger.debug("expiresTime: %s", expiresTime) + logger.debug("TTL: %s", TTL) + logger.debug("objectType: %s", objectType) + logger.info( + '(For msg message via API) Doing proof of work. Total required' + ' difficulty: %s\nRequired small message difficulty: %s', + float(nonceTrialsPerByte) + / networkDefaultProofOfWorkNonceTrialsPerByte, + float(payloadLengthExtraBytes) + / 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) + logger.info( + '(For msg message via API) Found proof of work %s\nNonce: %s\n' + 'POW took %s seconds. %s nonce trials per second.', + trialValue, nonce, int(time.time() - powStartTime), + nonce / (time.time() - powStartTime) + ) + encryptedPayload = pack('>Q', nonce) + encryptedPayload + inventoryHash = calculateInventoryHash(encryptedPayload) Inventory()[inventoryHash] = ( objectType, toStreamNumber, encryptedPayload, -- 2.45.1 From f6bd1546a763602af4bfd11b78d959833e4be65f Mon Sep 17 00:00:00 2001 From: Lee Miller Date: Mon, 9 Jan 2023 01:43:04 +0200 Subject: [PATCH 4/5] Alias disseminatePreEncryptedMsg as disseminatePreparedObject, edit docstring --- src/api.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/api.py b/src/api.py index f72863ce..e3acac29 100644 --- a/src/api.py +++ b/src/api.py @@ -1282,8 +1282,8 @@ class BMRPCDispatcher(object): }) return {'subscriptions': data} - @command('disseminatePreEncryptedMsg') - def HandleDisseminatePreEncryptedMsg( + @command('disseminatePreEncryptedMsg', 'disseminatePreparedObject') + def HandleDisseminatePreparedObject( self, encryptedPayload, nonceTrialsPerByte=networkDefaultProofOfWorkNonceTrialsPerByte, payloadLengthExtraBytes=networkDefaultPayloadLengthExtraBytes @@ -1291,9 +1291,9 @@ class BMRPCDispatcher(object): """ Handle a request to disseminate an encrypted message. - The device issuing this command to PyBitmessage supplies a msg object + The device issuing this command to PyBitmessage supplies an object that has already been encrypted but which may still need the PoW - to be done. PyBitmessage accepts this msg object and sends it out + to be done. PyBitmessage accepts this object and sends it out to the rest of the Bitmessage network as if it had generated the message itself. -- 2.45.1 From 629efb253e81ccde63514f401f81c1d3a262739d Mon Sep 17 00:00:00 2001 From: Lee Miller Date: Fri, 5 Apr 2024 00:20:16 +0300 Subject: [PATCH 5/5] Update the test for disseminatePreEncryptedMsg API command --- src/tests/test_api_thread.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/tests/test_api_thread.py b/src/tests/test_api_thread.py index 5b004066..5abc13bb 100644 --- a/src/tests/test_api_thread.py +++ b/src/tests/test_api_thread.py @@ -8,9 +8,7 @@ from struct import pack from six.moves import queue, xmlrpc_client from pybitmessage import protocol -from pybitmessage.defaults import ( - networkDefaultProofOfWorkNonceTrialsPerByte, - networkDefaultPayloadLengthExtraBytes) +from pybitmessage.highlevelcrypto import calculateInventoryHash from .partial import TestPartialRun from .samples import sample_statusbar_msg, sample_object_data @@ -80,12 +78,14 @@ class TestAPIThread(TestPartialRun): from inventory import Inventory proofofwork.init() - update_object = pack( + self.assertEqual( + unhexlify(self.api.disseminatePreparedObject( + hexlify(sample_object_data).decode())), + calculateInventoryHash(sample_object_data)) + update_object = b'\x00' * 8 + pack( '>Q', int(time.time() + 7200)) + sample_object_data[16:] invhash = unhexlify(self.api.disseminatePreEncryptedMsg( - hexlify(update_object).decode(), - networkDefaultProofOfWorkNonceTrialsPerByte, - networkDefaultPayloadLengthExtraBytes + hexlify(update_object).decode() )) obj_type, obj_stream, obj_data = Inventory()[invhash][:3] self.assertEqual(obj_type, 42) -- 2.45.1