From 7b508884e3c4b8b67009e887ca9ef3902ec4ad8c Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Mon, 10 Jun 2013 09:40:51 -0400 Subject: [PATCH] some initial work completed on MaxAcceptableDifficulty checking --- src/bitmessagemain.py | 71 ++++++++++++++++++++++-------------- src/bitmessageqt/__init__.py | 14 ++++--- 2 files changed, 53 insertions(+), 32 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index f9f0950c..0c85f339 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -1263,7 +1263,14 @@ class receiveDataThread(threading.Thread): def processpubkey(self,data): readPosition = 8 #for the nonce embeddedTime, = unpack('>I',data[readPosition:readPosition+4]) - readPosition += 4 #for the time + + #This section is used for the transition from 32 bit time to 64 bit time in the protocol. + if embeddedTime == 0: + embeddedTime, = unpack('>Q',data[readPosition:readPosition+8]) + readPosition += 8 + else: + readPosition += 4 + addressVersion, varintLength = decodeVarint(data[readPosition:readPosition+10]) readPosition += varintLength streamNumber, varintLength = decodeVarint(data[readPosition:readPosition+10]) @@ -1461,23 +1468,6 @@ class receiveDataThread(threading.Thread): return print 'the hash requested in this getpubkey request is:', requestedHash.encode('hex') - """shared.sqlLock.acquire() - t = (requestedHash,int(time.time())-lengthOfTimeToHoldOnToAllPubkeys) #this prevents SQL injection - shared.sqlSubmitQueue.put('''SELECT hash, transmitdata, time FROM pubkeys WHERE hash=? AND havecorrectnonce=1 AND time>?''') - shared.sqlSubmitQueue.put(t) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() - if queryreturn != []: - for row in queryreturn: - hash, payload, timeEncodedInPubkey = row - shared.printLock.acquire() - print 'We have the requested pubkey stored in our database of pubkeys. Sending it.' - shared.printLock.release() - inventoryHash = calculateInventoryHash(payload) - objectType = 'pubkey' - shared.inventory[inventoryHash] = (objectType, self.streamNumber, payload, timeEncodedInPubkey)#If the time embedded in this pubkey is more than 3 days old then this object isn't going to last very long in the inventory- the cleanerThread is going to come along and move it from the inventory in memory to the SQL inventory and then delete it from the SQL inventory. It should still find its way back to the original requestor if he is online however. - self.broadcastinv(inventoryHash)""" - #else: #the pubkey is not in our database of pubkeys. Let's check if the requested key is ours (which would mean we should do the POW, put it in the pubkey table, and broadcast out the pubkey.) if requestedHash in shared.myAddressesByHash: #if this address hash is one of mine if decodeAddress(shared.myAddressesByHash[requestedHash])[1] != requestedAddressVersionNumber: shared.printLock.acquire() @@ -2406,6 +2396,11 @@ class sqlThread(threading.Thread): shared.config.set('bitmessagesettings','defaultnoncetrialsperbyte',str(shared.networkDefaultProofOfWorkNonceTrialsPerByte)) shared.config.set('bitmessagesettings','defaultpayloadlengthextrabytes',str(shared.networkDefaultPayloadLengthExtraBytes)) shared.config.set('bitmessagesettings','settingsversion','5') + + if shared.config.getint('bitmessagesettings','settingsversion') == 5: + shared.config.set('bitmessagesettings','maxacceptablenoncetrialsperbyte','0') + shared.config.set('bitmessagesettings','maxacceptablepayloadlengthextrabytes','0') + shared.config.set('bitmessagesettings','settingsversion','6') with open(shared.appdata + 'keys.dat', 'wb') as configfile: shared.config.write(configfile) @@ -3018,8 +3013,8 @@ class singleWorker(threading.Thread): shared.sqlSubmitQueue.put((toripe,)) queryreturn = shared.sqlReturnQueue.get() shared.sqlLock.release() - if queryreturn == []: - #We no longer have the needed pubkey + if queryreturn == [] and toripe not in neededPubkeys: + #We no longer have the needed pubkey and we haven't requested it. shared.printLock.acquire() sys.stderr.write('For some reason, the status of a message in our outbox is \'doingmsgpow\' even though we lack the pubkey. Here is the RIPE hash of the needed pubkey: %s\n' % toripe.encode('hex')) shared.printLock.release() @@ -3032,14 +3027,14 @@ class singleWorker(threading.Thread): shared.sqlLock.release() shared.UISignalQueue.put(('updateSentItemStatusByHash',(toripe,'Sending a request for the recipient\'s encryption key.'))) self.requestPubKey(toaddress) - return + continue ackdataForWhichImWatching[ackdata] = 0 toStatus,toAddressVersionNumber,toStreamNumber,toHash = decodeAddress(toaddress) fromStatus,fromAddressVersionNumber,fromStreamNumber,fromHash = decodeAddress(fromaddress) shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,'Doing work necessary to send the message.'))) shared.printLock.acquire() print 'Found a message in our database that needs to be sent with this pubkey.' - print 'First 150 characters of message:', message[:150] + print 'First 150 characters of message:', repr(message[:150]) shared.printLock.release() embeddedTime = pack('>I',(int(time.time())+random.randrange(-300, 300)))#the current time plus or minus five minutes. We will use this time both for our message and for the ackdata packed within our message. if fromAddressVersionNumber == 2: @@ -3140,7 +3135,15 @@ class singleWorker(threading.Thread): #The pubkey is stored the way we originally received it which means that we need to read beyond things like the nonce and time to get to the public keys. readPosition = 8 #to bypass the nonce - readPosition += 4 #to bypass the embedded time + + pubkeyEmbeddedTime, = unpack('>I',pubkeyPayload[readPosition:readPosition+4]) + #This section is used for the transition from 32 bit time to 64 bit time in the protocol. + if pubkeyEmbeddedTime == 0: + pubkeyEmbeddedTime, = unpack('>Q',pubkeyPayload[readPosition:readPosition+8]) + readPosition += 8 + else: + readPosition += 4 + readPosition += 1 #to bypass the address version whose length is definitely 1 streamNumber, streamNumberLength = decodeVarint(pubkeyPayload[readPosition:readPosition+10]) readPosition += streamNumberLength @@ -3162,7 +3165,19 @@ class singleWorker(threading.Thread): requiredAverageProofOfWorkNonceTrialsPerByte = shared.networkDefaultProofOfWorkNonceTrialsPerByte if requiredPayloadLengthExtraBytes < shared.networkDefaultPayloadLengthExtraBytes: requiredPayloadLengthExtraBytes = shared.networkDefaultPayloadLengthExtraBytes - #todo: pull yet-to-be-added values out of config: maximumacceptabletotaldifficult and maximumacceptablesmallmessagedifficulty and compare. + if (requiredAverageProofOfWorkNonceTrialsPerByte > shared.config.getint('bitmessagesettings','maxacceptablenoncetrialsperbyte') and shared.config.getint('bitmessagesettings','maxacceptablenoncetrialsperbyte') != 0) or (requiredPayloadLengthExtraBytes > shared.config.getint('bitmessagesettings','maxacceptablepayloadlengthextrabytes') and shared.config.getint('bitmessagesettings','maxacceptablepayloadlengthextrabytes') != 0): + #The demanded difficulty is more than we are willing to do. + shared.sqlLock.acquire() + t = (ackdata,) + shared.sqlSubmitQueue.put('''UPDATE sent SET status='toodifficult' WHERE ackdata=? AND status='doingmsgpow' ''') + shared.sqlSubmitQueue.put(t) + shared.sqlReturnQueue.get() + shared.sqlSubmitQueue.put('commit') + shared.sqlLock.release() + shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,'Problem: The work demanded by the recipient (' + str(requiredAverageProofOfWorkNonceTrialsPerByte / shared.networkDefaultProofOfWorkNonceTrialsPerByte) + ' and ' + str(requiredPayloadLengthExtraBytes / shared.networkDefaultPayloadLengthExtraBytes) + ') is more difficult than you are willing to do. ' + unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(time.time()))),'utf-8')))) + continue + + encrypted = highlevelcrypto.encrypt(payload,"04"+pubEncryptionKeyBase256.encode('hex')) #We are now dropping the unencrypted data in payload since it has already been encrypted and replacing it with the encrypted payload that we will send out. @@ -3189,7 +3204,7 @@ class singleWorker(threading.Thread): print 'Broadcasting inv for my msg(within sendmsg function):', inventoryHash.encode('hex') shared.broadcastToSendDataQueues((streamNumber, 'sendinv', inventoryHash)) - #Update the status of the message in the 'sent' table to have a 'sent' status + #Update the status of the message in the 'sent' table to have a 'msgsent' status shared.sqlLock.acquire() t = ('msgsent',toaddress, fromaddress, subject, message,'doingmsgpow') shared.sqlSubmitQueue.put('UPDATE sent SET status=? WHERE toaddress=? AND fromaddress=? AND subject=? AND message=? AND status=?') @@ -3970,7 +3985,7 @@ if __name__ == "__main__": except: #This appears to be the first time running the program; there is no config file (or it cannot be accessed). Create config file. shared.config.add_section('bitmessagesettings') - shared.config.set('bitmessagesettings','settingsversion','5') + shared.config.set('bitmessagesettings','settingsversion','6') shared.config.set('bitmessagesettings','port','8444') shared.config.set('bitmessagesettings','timeformat','%%a, %%d %%b %%Y %%I:%%M %%p') shared.config.set('bitmessagesettings','blackwhitelist','black') @@ -3992,6 +4007,8 @@ if __name__ == "__main__": shared.config.set('bitmessagesettings','defaultnoncetrialsperbyte',str(shared.networkDefaultProofOfWorkNonceTrialsPerByte)) shared.config.set('bitmessagesettings','defaultpayloadlengthextrabytes',str(shared.networkDefaultPayloadLengthExtraBytes)) shared.config.set('bitmessagesettings','minimizeonclose','false') + shared.config.set('bitmessagesettings','maxacceptablenoncetrialsperbyte','0') + shared.config.set('bitmessagesettings','maxacceptablepayloadlengthextrabytes','0') if storeConfigFilesInSameDirectoryAsProgramByDefault: #Just use the same directory as the program and forget about the appdata folder @@ -4028,7 +4045,7 @@ if __name__ == "__main__": pickleFile = open(shared.appdata + 'knownnodes.dat', 'rb') shared.knownNodes = pickle.load(pickleFile) pickleFile.close() - if shared.config.getint('bitmessagesettings', 'settingsversion') > 5: + if shared.config.getint('bitmessagesettings', 'settingsversion') > 6: print 'Bitmessage cannot read future versions of the keys file (keys.dat). Run the newer version of Bitmessage.' raise SystemExit diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 6617da3d..94a525fb 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -363,8 +363,10 @@ class MyForm(QtGui.QMainWindow): newItem = myTableWidgetItem('Broadcast queued.') elif status == 'broadcastsent': newItem = myTableWidgetItem('Broadcast on ' + unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(lastactiontime))),'utf-8')) + elif status == 'toodifficult': + newItem = myTableWidgetItem('Problem: The work demanded by the recipient is more difficult than you are willing to do. ' + unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(lastactiontime))),'utf-8')) else: - newItem = myTableWidgetItem('Unknown status. ' + unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(lastactiontime))),'utf-8')) + newItem = myTableWidgetItem('Unknown status: ' + status + ' ' + unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(lastactiontime))),'utf-8')) newItem.setData(Qt.UserRole,QByteArray(ackdata)) newItem.setData(33,int(lastactiontime)) newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled ) @@ -1668,13 +1670,15 @@ class MyForm(QtGui.QMainWindow): currentInboxRow = self.ui.tableWidgetInbox.currentRow() toAddressAtCurrentInboxRow = str(self.ui.tableWidgetInbox.item(currentInboxRow,0).data(Qt.UserRole).toPyObject()) fromAddressAtCurrentInboxRow = str(self.ui.tableWidgetInbox.item(currentInboxRow,1).data(Qt.UserRole).toPyObject()) - if toAddressAtCurrentInboxRow == self.str_broadcast_subscribers: self.ui.labelFrom.setText('') + elif not shared.config.has_section(toAddressAtCurrentInboxRow): + QtGui.QMessageBox.information(self, 'Address is gone','Bitmessage cannot find your address ' + toAddressAtCurrentInboxRow + '. Perhaps you removed it?', QMessageBox.Ok) + self.ui.labelFrom.setText('') + elif not shared.config.getboolean(toAddressAtCurrentInboxRow,'enabled'): + QtGui.QMessageBox.information(self, 'Address disabled','Error: The address from which you are trying to send is disabled. You\'ll have to enable it on the \'Your Identities\' tab before using it.', QMessageBox.Ok) + self.ui.labelFrom.setText('') else: - if not shared.config.get(toAddressAtCurrentInboxRow,'enabled'): - self.statusBar().showMessage('Error: The address from which you are trying to send is disabled. Enable it from the \'Your Identities\' tab first.') - return self.ui.labelFrom.setText(toAddressAtCurrentInboxRow) self.ui.lineEditTo.setText(str(fromAddressAtCurrentInboxRow)) self.ui.comboBoxSendFrom.setCurrentIndex(0)