From 3ee9b4ea6e828a99206c0cdb7d51dacf4be88ddd Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Tue, 28 May 2013 13:30:44 -0400 Subject: [PATCH 1/3] inv flooding attack mitigation --- src/bitmessagemain.py | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 93acfa19..be96fc6d 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -303,6 +303,10 @@ class receiveDataThread(threading.Thread): shared.printLock.acquire() print 'Could not delete', self.HOST, 'from shared.connectedHostsList.', err shared.printLock.release() + try: + del numberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHavePerPeer[self.HOST] + except: + pass shared.UISignalQueue.put(('updateNetworkStatusTab','no data')) shared.printLock.acquire() print 'The size of the connectedHostsList is now:', len(shared.connectedHostsList) @@ -386,15 +390,24 @@ class receiveDataThread(threading.Thread): shared.printLock.acquire() print '(concerning', self.HOST + ')', 'number of objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave is now', len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) shared.printLock.release() + try: + del numberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHavePerPeer[self.HOST] + except: + pass break if len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) == 0: shared.printLock.acquire() print '(concerning', self.HOST + ')', 'number of objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave is now', len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) shared.printLock.release() + try: + del numberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHavePerPeer[self.HOST] + except: + pass if len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) > 0: shared.printLock.acquire() print '(concerning', self.HOST + ')', 'number of objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave is now', len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) shared.printLock.release() + numberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHavePerPeer[self.HOST] = len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) if len(self.ackDataThatWeHaveYetToSend) > 0: self.data = self.ackDataThatWeHaveYetToSend.pop() self.processData() @@ -1489,11 +1502,26 @@ class receiveDataThread(threading.Thread): #We have received an inv message def recinv(self,data): + totalNumberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave = 0 + print 'number of keys(hosts) in numberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHavePerPeer:', len(numberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHavePerPeer) + for key, value in numberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHavePerPeer.items(): + totalNumberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave += value + print 'totalNumberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave = ', totalNumberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave + """if totalNumberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave > 200000: + shared.printLock.acquire() + print 'We already have', totalNumberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave, 'items yet to retrieve from peers. Ignoring this inv message.' + shared.printLock.release() + return""" numberOfItemsInInv, lengthOfVarint = decodeVarint(data[:10]) if len(data) < lengthOfVarint + (numberOfItemsInInv * 32): print 'inv message doesn\'t contain enough data. Ignoring.' return if numberOfItemsInInv == 1: #we'll just request this data from the person who advertised the object. + if totalNumberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave > 200000 and len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) > 1000: + shared.printLock.acquire() + print 'We already have', totalNumberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave, 'items yet to retrieve from peers and over 1000 from this node in particular. Ignoring this inv message.' + shared.printLock.release() + return self.objectsOfWhichThisRemoteNodeIsAlreadyAware[data[lengthOfVarint:32+lengthOfVarint]] = 0 if data[lengthOfVarint:32+lengthOfVarint] in shared.inventory: shared.printLock.acquire() @@ -1507,8 +1535,14 @@ class receiveDataThread(threading.Thread): print 'inv message lists', numberOfItemsInInv, 'objects.' for i in range(numberOfItemsInInv): #upon finishing dealing with an incoming message, the receiveDataThread will request a random object from the peer. This way if we get multiple inv messages from multiple peers which list mostly the same objects, we will make getdata requests for different random objects from the various peers. if len(data[lengthOfVarint+(32*i):32+lengthOfVarint+(32*i)]) == 32: #The length of an inventory hash should be 32. If it isn't 32 then the remote node is either badly programmed or behaving nefariously. + if totalNumberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave > 200000 and len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) > 1000: + shared.printLock.acquire() + print 'We already have', totalNumberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave, 'items yet to retrieve from peers and over',len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave),'from this node in particular. Ignoring the rest of this inv message.' + shared.printLock.release() + break self.objectsOfWhichThisRemoteNodeIsAlreadyAware[data[lengthOfVarint+(32*i):32+lengthOfVarint+(32*i)]] = 0 self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave[data[lengthOfVarint+(32*i):32+lengthOfVarint+(32*i)]] = 0 + numberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHavePerPeer[self.HOST] = len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) #Send a getdata message to our peer to request the object with the given hash @@ -2535,6 +2569,12 @@ class singleCleaner(threading.Thread): shared.sqlSubmitQueue.put(t) queryreturn = shared.sqlReturnQueue.get() for row in queryreturn: + if len(row) < 5: + shared.printLock.acquire() + sys.stderr.write('Something went wrong in the singleCleaner thread: a query did not return the requested fields. '+repr(row)) + time.sleep(3) + shared.printLock.release() + break toaddress, toripe, fromaddress, subject, message, ackdata, lastactiontime, status, pubkeyretrynumber, msgretrynumber = row if status == 'findingpubkey': if int(time.time()) - lastactiontime > (maximumAgeOfAnObjectThatIAmWillingToAccept * (2 ** (pubkeyretrynumber))): @@ -3880,6 +3920,7 @@ neededPubkeys = {} successfullyDecryptMessageTimings = [] #A list of the amounts of time it took to successfully decrypt msg messages apiAddressGeneratorReturnQueue = Queue.Queue() #The address generator thread uses this queue to get information back to the API thread. alreadyAttemptedConnectionsListResetTime = int(time.time()) #used to clear out the alreadyAttemptedConnectionsList periodically so that we will retry connecting to hosts to which we have already tried to connect. +numberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHavePerPeer = {} if useVeryEasyProofOfWorkForTesting: shared.networkDefaultProofOfWorkNonceTrialsPerByte = int(shared.networkDefaultProofOfWorkNonceTrialsPerByte / 16) From 3b9c5885ea500698b10e8566c794a89226b721a6 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Tue, 28 May 2013 16:50:09 -0400 Subject: [PATCH 2/3] Added menu option to delete all trashed messages --- src/bitmessagemain.py | 17 ++++++----------- src/bitmessageqt/__init__.py | 34 ++++++++++++++++------------------ src/bitmessageui.py | 6 +++++- src/bitmessageui.ui | 6 ++++++ src/messages.dat reader.py | 4 ++-- 5 files changed, 35 insertions(+), 32 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index be96fc6d..143510b5 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -391,7 +391,7 @@ class receiveDataThread(threading.Thread): print '(concerning', self.HOST + ')', 'number of objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave is now', len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) shared.printLock.release() try: - del numberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHavePerPeer[self.HOST] + del numberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHavePerPeer[self.HOST] #this data structure is maintained so that we can keep track of how many total objects, across all connections, are currently outstanding. If it goes too high it can indicate that we are under attack by multiple nodes working together. except: pass break @@ -400,14 +400,14 @@ class receiveDataThread(threading.Thread): print '(concerning', self.HOST + ')', 'number of objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave is now', len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) shared.printLock.release() try: - del numberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHavePerPeer[self.HOST] + del numberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHavePerPeer[self.HOST] #this data structure is maintained so that we can keep track of how many total objects, across all connections, are currently outstanding. If it goes too high it can indicate that we are under attack by multiple nodes working together. except: pass if len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) > 0: shared.printLock.acquire() print '(concerning', self.HOST + ')', 'number of objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave is now', len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) shared.printLock.release() - numberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHavePerPeer[self.HOST] = len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) + numberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHavePerPeer[self.HOST] = len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) #this data structure is maintained so that we can keep track of how many total objects, across all connections, are currently outstanding. If it goes too high it can indicate that we are under attack by multiple nodes working together. if len(self.ackDataThatWeHaveYetToSend) > 0: self.data = self.ackDataThatWeHaveYetToSend.pop() self.processData() @@ -1507,17 +1507,12 @@ class receiveDataThread(threading.Thread): for key, value in numberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHavePerPeer.items(): totalNumberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave += value print 'totalNumberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave = ', totalNumberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave - """if totalNumberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave > 200000: - shared.printLock.acquire() - print 'We already have', totalNumberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave, 'items yet to retrieve from peers. Ignoring this inv message.' - shared.printLock.release() - return""" numberOfItemsInInv, lengthOfVarint = decodeVarint(data[:10]) if len(data) < lengthOfVarint + (numberOfItemsInInv * 32): print 'inv message doesn\'t contain enough data. Ignoring.' return if numberOfItemsInInv == 1: #we'll just request this data from the person who advertised the object. - if totalNumberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave > 200000 and len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) > 1000: + if totalNumberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave > 200000 and len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) > 1000: #inv flooding attack mitigation shared.printLock.acquire() print 'We already have', totalNumberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave, 'items yet to retrieve from peers and over 1000 from this node in particular. Ignoring this inv message.' shared.printLock.release() @@ -1535,7 +1530,7 @@ class receiveDataThread(threading.Thread): print 'inv message lists', numberOfItemsInInv, 'objects.' for i in range(numberOfItemsInInv): #upon finishing dealing with an incoming message, the receiveDataThread will request a random object from the peer. This way if we get multiple inv messages from multiple peers which list mostly the same objects, we will make getdata requests for different random objects from the various peers. if len(data[lengthOfVarint+(32*i):32+lengthOfVarint+(32*i)]) == 32: #The length of an inventory hash should be 32. If it isn't 32 then the remote node is either badly programmed or behaving nefariously. - if totalNumberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave > 200000 and len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) > 1000: + if totalNumberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave > 200000 and len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) > 1000: #inv flooding attack mitigation shared.printLock.acquire() print 'We already have', totalNumberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave, 'items yet to retrieve from peers and over',len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave),'from this node in particular. Ignoring the rest of this inv message.' shared.printLock.release() @@ -3977,7 +3972,7 @@ if __name__ == "__main__": shared.config.set('bitmessagesettings','messagesencrypted','false') shared.config.set('bitmessagesettings','defaultnoncetrialsperbyte',str(shared.networkDefaultProofOfWorkNonceTrialsPerByte)) shared.config.set('bitmessagesettings','defaultpayloadlengthextrabytes',str(shared.networkDefaultPayloadLengthExtraBytes)) - shared.config.set('bitmessagesettings','minimizeonclose','true') + shared.config.set('bitmessagesettings','minimizeonclose','false') if storeConfigFilesInSameDirectoryAsProgramByDefault: #Just use the same directory as the program and forget about the appdata folder diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index dd42692c..20101bc1 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -87,6 +87,7 @@ class MyForm(QtGui.QMainWindow): #FILE MENU and other buttons QtCore.QObject.connect(self.ui.actionExit, QtCore.SIGNAL("triggered()"), self.quit) QtCore.QObject.connect(self.ui.actionManageKeys, QtCore.SIGNAL("triggered()"), self.click_actionManageKeys) + QtCore.QObject.connect(self.ui.actionDeleteAllTrashedMessages, QtCore.SIGNAL("triggered()"), self.click_actionDeleteAllTrashedMessages) QtCore.QObject.connect(self.ui.actionRegenerateDeterministicAddresses, QtCore.SIGNAL("triggered()"), self.click_actionRegenerateDeterministicAddresses) QtCore.QObject.connect(self.ui.pushButtonNewAddress, QtCore.SIGNAL("clicked()"), self.click_NewAddressDialog) QtCore.QObject.connect(self.ui.comboBoxSendFrom, QtCore.SIGNAL("activated(int)"),self.redrawLabelFrom) @@ -768,6 +769,21 @@ class MyForm(QtGui.QMainWindow): if reply == QtGui.QMessageBox.Yes: self.openKeysFile() + def click_actionDeleteAllTrashedMessages(self): + if QtGui.QMessageBox.question(self, 'Delete trash?',"Are you sure you want to delete all trashed messages?", QtGui.QMessageBox.Yes, QtGui.QMessageBox.No) == QtGui.QMessageBox.No: + return + shared.sqlLock.acquire() + shared.sqlSubmitQueue.put('''delete from inbox where folder='trash' ''') + shared.sqlSubmitQueue.put('') + shared.sqlReturnQueue.get() + shared.sqlSubmitQueue.put('''delete from sent where folder='trash' ''') + shared.sqlSubmitQueue.put('') + shared.sqlReturnQueue.get() + shared.sqlSubmitQueue.put('commit') #Commit takes no parameters + shared.sqlSubmitQueue.put('vacuum') + shared.sqlSubmitQueue.put('') + shared.sqlLock.release() + def click_actionRegenerateDeterministicAddresses(self): self.regenerateAddressesDialogInstance = regenerateAddressesDialog(self) if self.regenerateAddressesDialogInstance.exec_(): @@ -1206,24 +1222,6 @@ class MyForm(QtGui.QMainWindow): self.ui.comboBoxSendFrom.setCurrentIndex(0) - - """def connectObjectToSignals(self,object): - QtCore.QObject.connect(object, QtCore.SIGNAL("updateStatusBar(PyQt_PyObject)"), self.updateStatusBar) - QtCore.QObject.connect(object, QtCore.SIGNAL("displayNewInboxMessage(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"), self.displayNewInboxMessage) - QtCore.QObject.connect(object, QtCore.SIGNAL("displayNewSentMessage(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"), self.displayNewSentMessage) - QtCore.QObject.connect(object, QtCore.SIGNAL("updateSentItemStatusByHash(PyQt_PyObject,PyQt_PyObject)"), self.updateSentItemStatusByHash) - QtCore.QObject.connect(object, QtCore.SIGNAL("updateSentItemStatusByAckdata(PyQt_PyObject,PyQt_PyObject)"), self.updateSentItemStatusByAckdata) - QtCore.QObject.connect(object, QtCore.SIGNAL("updateNetworkStatusTab()"), self.updateNetworkStatusTab) - QtCore.QObject.connect(object, QtCore.SIGNAL("incrementNumberOfMessagesProcessed()"), self.incrementNumberOfMessagesProcessed) - QtCore.QObject.connect(object, QtCore.SIGNAL("incrementNumberOfPubkeysProcessed()"), self.incrementNumberOfPubkeysProcessed) - QtCore.QObject.connect(object, QtCore.SIGNAL("incrementNumberOfBroadcastsProcessed()"), self.incrementNumberOfBroadcastsProcessed) - QtCore.QObject.connect(object, QtCore.SIGNAL("setStatusIcon(PyQt_PyObject)"), self.setStatusIcon) - - #This function exists because of the API. The API thread starts an address generator thread and must somehow connect the address generator's signals to the QApplication thread. This function is used to connect the slots and signals. - def connectObjectToAddressGeneratorSignals(self,object): - QtCore.QObject.connect(object, SIGNAL("writeNewAddressToTable(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"), self.writeNewAddressToTable) - QtCore.QObject.connect(object, QtCore.SIGNAL("updateStatusBar(PyQt_PyObject)"), self.updateStatusBar)""" - #This function is called by the processmsg function when that function receives a message to an address that is acting as a pseudo-mailing-list. The message will be broadcast out. This function puts the message on the 'Sent' tab. def displayNewSentMessage(self,toAddress,toLabel,fromAddress,subject,message,ackdata): try: diff --git a/src/bitmessageui.py b/src/bitmessageui.py index c3253da1..ac4ae3b6 100644 --- a/src/bitmessageui.py +++ b/src/bitmessageui.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'bitmessageui.ui' # -# Created: Fri May 24 16:22:22 2013 +# Created: Tue May 28 16:22:12 2013 # by: PyQt4 UI code generator 4.9.4 # # WARNING! All changes made in this file will be lost! @@ -412,7 +412,10 @@ class Ui_MainWindow(object): self.actionSettings.setObjectName(_fromUtf8("actionSettings")) self.actionRegenerateDeterministicAddresses = QtGui.QAction(MainWindow) self.actionRegenerateDeterministicAddresses.setObjectName(_fromUtf8("actionRegenerateDeterministicAddresses")) + self.actionDeleteAllTrashedMessages = QtGui.QAction(MainWindow) + self.actionDeleteAllTrashedMessages.setObjectName(_fromUtf8("actionDeleteAllTrashedMessages")) self.menuFile.addAction(self.actionManageKeys) + self.menuFile.addAction(self.actionDeleteAllTrashedMessages) self.menuFile.addAction(self.actionRegenerateDeterministicAddresses) self.menuFile.addAction(self.actionExit) self.menuSettings.addAction(self.actionSettings) @@ -544,5 +547,6 @@ class Ui_MainWindow(object): self.actionAbout.setText(QtGui.QApplication.translate("MainWindow", "About", None, QtGui.QApplication.UnicodeUTF8)) self.actionSettings.setText(QtGui.QApplication.translate("MainWindow", "Settings", None, QtGui.QApplication.UnicodeUTF8)) self.actionRegenerateDeterministicAddresses.setText(QtGui.QApplication.translate("MainWindow", "Regenerate deterministic addresses", None, QtGui.QApplication.UnicodeUTF8)) + self.actionDeleteAllTrashedMessages.setText(QtGui.QApplication.translate("MainWindow", "Delete all trashed messages", None, QtGui.QApplication.UnicodeUTF8)) import bitmessage_icons_rc diff --git a/src/bitmessageui.ui b/src/bitmessageui.ui index 010db41e..bba6d3a5 100644 --- a/src/bitmessageui.ui +++ b/src/bitmessageui.ui @@ -933,6 +933,7 @@ p, li { white-space: pre-wrap; } File + @@ -1002,6 +1003,11 @@ p, li { white-space: pre-wrap; } Regenerate deterministic addresses + + + Delete all trashed messages + + tabWidget diff --git a/src/messages.dat reader.py b/src/messages.dat reader.py index cb94525e..286739e0 100644 --- a/src/messages.dat reader.py +++ b/src/messages.dat reader.py @@ -107,12 +107,12 @@ def vacuum(): #takeInboxMessagesOutOfTrash() #takeSentMessagesOutOfTrash() #markAllInboxMessagesAsUnread() -#readInbox() +readInbox() #readSent() #readPubkeys() #readSubscriptions() #readInventory() -vacuum() #will defragment and clean empty space from the messages.dat file. +#vacuum() #will defragment and clean empty space from the messages.dat file. From c762d7ed01b0a1b50953b1cd57359a7074759e54 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Tue, 28 May 2013 17:04:23 -0400 Subject: [PATCH 3/3] Added menu option to delete all trashed messages --- src/bitmessageqt/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index dd383bfe..add21421 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -777,9 +777,10 @@ class MyForm(QtGui.QMainWindow): shared.sqlSubmitQueue.put('''delete from sent where folder='trash' ''') shared.sqlSubmitQueue.put('') shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') #Commit takes no parameters + shared.sqlSubmitQueue.put('commit') #Commit takes no parameters and returns nothing shared.sqlSubmitQueue.put('vacuum') shared.sqlSubmitQueue.put('') + shared.sqlReturnQueue.get() shared.sqlLock.release() def click_actionRegenerateDeterministicAddresses(self):