From 1a3cf0d74d26b0b306e0e7a9085449153283a4d3 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Tue, 22 Jan 2013 14:29:49 -0500 Subject: [PATCH] moved pubkey POW responsibility from the receiveData thread to the singleWorker thread --- bitmessagemain.py | 216 ++++++++++++++++++-------------------------- newaddressdialog.ui | 13 +++ 2 files changed, 101 insertions(+), 128 deletions(-) diff --git a/bitmessagemain.py b/bitmessagemain.py index f2652b25..bf97d224 100644 --- a/bitmessagemain.py +++ b/bitmessagemain.py @@ -12,7 +12,7 @@ lengthOfTimeToLeaveObjectsInInventory = 237600 #Equals two days and 18 hours. Th maximumAgeOfObjectsThatIAdvertiseToOthers = 216000 #Equals two days and 12 hours maximumAgeOfNodesThatIAdvertiseToOthers = 10800 #Equals three hours storeConfigFilesInSameDirectoryAsProgram = False -userVeryEasyProofOfWorkForTesting = True #If you set this to True while on the normal network, you won't be able to send or sometimes receive messages. +useVeryEasyProofOfWorkForTesting = False #If you set this to True while on the normal network, you won't be able to send or sometimes receive messages. import sys try: @@ -1231,91 +1231,38 @@ class receiveDataThread(QThread): print 'The addressVersionNumber of the pubkey request is too high. Can\'t understand. Ignoring it.' return print 'the hash requested in this getpubkey request is:', self.data[36+addressVersionLength+streamNumberLength:56+addressVersionLength+streamNumberLength].encode('hex') - if self.data[36+addressVersionLength+streamNumberLength:56+addressVersionLength+streamNumberLength] in myECAddressHashes: #if this address hash is one of mine - print 'Found getpubkey requested hash in my list of EC hashes.' - #check to see whether we have already calculated the nonce and transmitted this key before - sqlLock.acquire()#released at the bottom of this payload generation section - t = (self.data[36+addressVersionLength+streamNumberLength:56+addressVersionLength+streamNumberLength],) #this prevents SQL injection - sqlSubmitQueue.put('SELECT * FROM pubkeys WHERE hash=?') - sqlSubmitQueue.put(t) - queryreturn = sqlReturnQueue.get() - #print 'queryreturn', queryreturn - if queryreturn == []: - print 'pubkey request is for me but the pubkey is not in our database of pubkeys. Making it.' - payload = pack('>I',(int(time.time())+random.randrange(-300, 300))) #the current time plus or minus five minutes - payload += encodeVarint(2) #Address version number - payload += encodeVarint(streamNumber) - payload += '\x00\x00\x00\x01' #bitfield of features supported by me (see the wiki). - privSigningKeyBase58 = config.get(encodeAddress(addressVersionNumber,streamNumber,self.data[36+addressVersionLength+streamNumberLength:56+addressVersionLength+streamNumberLength]), 'privsigningkey') - privEncryptionKeyBase58 = config.get(encodeAddress(addressVersionNumber,streamNumber,self.data[36+addressVersionLength+streamNumberLength:56+addressVersionLength+streamNumberLength]), 'privencryptionkey') - - privSigningKeyHex = decodeWalletImportFormat(privSigningKeyBase58).encode('hex') - privEncryptionKeyHex = decodeWalletImportFormat(privEncryptionKeyBase58).encode('hex') - pubSigningKey = highlevelcrypto.privToPub(privSigningKeyHex).decode('hex') - pubEncryptionKey = highlevelcrypto.privToPub(privEncryptionKeyHex).decode('hex') - - #print 'within recgetpubkey' - #print 'pubSigningKey in hex:', pubSigningKey.encode('hex') - #print 'pubEncryptionKey in hex:', pubEncryptionKey.encode('hex') - - payload += pubSigningKey[1:] - payload += pubEncryptionKey[1:] - - #Time to do the POW for this pubkey message - nonce = 0 - trialValue = 99999999999999999999 - target = 2**64 / ((len(payload)+payloadLengthExtraBytes+8) * averageProofOfWorkNonceTrialsPerByte) - print '(For pubkey message) Doing proof of work...' - initialHash = hashlib.sha512(payload).digest() - while trialValue > target: - nonce += 1 - trialValue, = unpack('>Q',hashlib.sha512(hashlib.sha512(pack('>Q',nonce) + initialHash).digest()).digest()[0:8]) - #trialValue, = unpack('>Q',hashlib.sha512(hashlib.sha512(pack('>Q',nonce) + payload).digest()).digest()[4:12]) - print '(For pubkey message) Found proof of work', trialValue, 'Nonce:', nonce - - payload = pack('>Q',nonce) + payload - t = (self.data[36+addressVersionLength+streamNumberLength:56+addressVersionLength+streamNumberLength],True,payload,int(time.time())+1209600) #after two weeks (1,209,600 seconds), we may remove our own pub key from our database. It will be regenerated and put back in the database if it is requested. - sqlSubmitQueue.put('''INSERT INTO pubkeys VALUES (?,?,?,?)''') - sqlSubmitQueue.put(t) - queryreturn = sqlReturnQueue.get() - - #Now that we have the full pubkey message ready either from making it just now or making it earlier, we can send it out. - t = (self.data[36+addressVersionLength+streamNumberLength:56+addressVersionLength+streamNumberLength],) #this prevents SQL injection - sqlSubmitQueue.put('''SELECT * FROM pubkeys WHERE hash=? AND havecorrectnonce=1''') - sqlSubmitQueue.put(t) - queryreturn = sqlReturnQueue.get() - if queryreturn == []: - sys.stderr.write('Error: pubkey which we just put in our pubkey database suddenly is not there. Is the database malfunctioning?') - sqlLock.release() - return + sqlLock.acquire() + t = (self.data[36+addressVersionLength+streamNumberLength:56+addressVersionLength+streamNumberLength],) #this prevents SQL injection + sqlSubmitQueue.put('''SELECT hash, transmitdata, time FROM pubkeys WHERE hash=? AND havecorrectnonce=1''') + sqlSubmitQueue.put(t) + queryreturn = sqlReturnQueue.get() + sqlLock.release() + if queryreturn != []: for row in queryreturn: - hash, havecorrectnonce, payload, timeLastRequested = row + hash, payload, timeLastRequested = row if timeLastRequested < int(time.time())+604800: #if the last time anyone asked about this hash was this week, extend the time. + sqlLock.acquire() t = (int(time.time())+604800,hash) sqlSubmitQueue.put('''UPDATE pubkeys set time=? WHERE hash=?''') sqlSubmitQueue.put(t) queryreturn = sqlReturnQueue.get() - - sqlLock.release() + sqlLock.release() inventoryHash = calculateInventoryHash(payload) objectType = 'pubkey' inventory[inventoryHash] = (objectType, self.streamNumber, payload, int(time.time())) - self.broadcastinv(inventoryHash) - - elif self.data[36+addressVersionLength+streamNumberLength:56+addressVersionLength+streamNumberLength] in myRSAAddressHashes: - print 'Found getpubkey requested hash in my list of RSA hashes.' - #check to see whether we have already calculated the nonce and transmitted this key before - sqlLock.acquire()#released at the bottom of this payload generation section - t = (self.data[36+addressVersionLength+streamNumberLength:56+addressVersionLength+streamNumberLength],) #this prevents SQL injection - sqlSubmitQueue.put('SELECT * FROM pubkeys WHERE hash=?') - sqlSubmitQueue.put(t) - queryreturn = sqlReturnQueue.get() - #print 'queryreturn', queryreturn + 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 queryreturn == []: - print 'pubkey request is for me but the pubkey is not in our database of pubkeys. Making it.' + if self.data[36+addressVersionLength+streamNumberLength:56+addressVersionLength+streamNumberLength] in myECAddressHashes: #if this address hash is one of mine + print 'Found getpubkey-requested-hash in my list of EC hashes. Telling Worker thread to do the POW for a pubkey message and send it out.' + myAddress = encodeAddress(addressVersionNumber,streamNumber,self.data[36+addressVersionLength+streamNumberLength:56+addressVersionLength+streamNumberLength]) + workerQueue.put(('doPOWForMyV2Pubkey',myAddress)) + + + elif self.data[36+addressVersionLength+streamNumberLength:56+addressVersionLength+streamNumberLength] in myRSAAddressHashes: + print 'Found getpubkey requested hash in my list of RSA hashes.' payload = '\x00\x00\x00\x01' #bitfield of features supported by me (see the wiki). payload += self.data[36:36+addressVersionLength+streamNumberLength] #print int(config.get(encodeAddress(addressVersionNumber,streamNumber,self.data[36+addressVersionLength+streamNumberLength:56+addressVersionLength+streamNumberLength]), 'n')) @@ -1338,61 +1285,20 @@ class receiveDataThread(QThread): payload = pack('>Q',nonce) + payload t = (self.data[36+addressVersionLength+streamNumberLength:56+addressVersionLength+streamNumberLength],True,payload,int(time.time())+1209600) #after two weeks (1,209,600 seconds), we may remove our own pub key from our database. It will be regenerated and put back in the database if it is requested. + sqlLock.acquire() sqlSubmitQueue.put('''INSERT INTO pubkeys VALUES (?,?,?,?)''') sqlSubmitQueue.put(t) queryreturn = sqlReturnQueue.get() - - #Now that we have the full pubkey message ready either from making it just now or making it earlier, we can send it out. - t = (self.data[36+addressVersionLength+streamNumberLength:56+addressVersionLength+streamNumberLength],) #this prevents SQL injection - sqlSubmitQueue.put('''SELECT * FROM pubkeys WHERE hash=? AND havecorrectnonce=1''') - sqlSubmitQueue.put(t) - queryreturn = sqlReturnQueue.get() - if queryreturn == []: - sys.stderr.write('Error: pubkey which we just put in our pubkey database suddenly is not there. Is the database malfunctioning?') sqlLock.release() - return - for row in queryreturn: - hash, havecorrectnonce, payload, timeLastRequested = row - if timeLastRequested < int(time.time())+604800: #if the last time anyone asked about this hash was this week, extend the time. - t = (int(time.time())+604800,hash) - sqlSubmitQueue.put('''UPDATE pubkeys set time=? WHERE hash=?''') - sqlSubmitQueue.put(t) - queryreturn = sqlReturnQueue.get() - sqlLock.release() - - inventoryHash = calculateInventoryHash(payload) - objectType = 'pubkey' - inventory[inventoryHash] = (objectType, self.streamNumber, payload, int(time.time())) - self.broadcastinv(inventoryHash) - - else: - - #This section hasn't been tested yet. Criteria for success: Alice sends Bob a message. Three days later, Charlie who is completely new to Bitmessage runs the client for the first time then sends a message to Bob and accomplishes this without Bob having to redo the POW for a pubkey message. - - print 'Hash in getpubkey request is not for any of my keys.' - #..but lets see if we have it stored from when it came in from someone else. - t = (self.data[36+addressVersionLength+streamNumberLength:56+addressVersionLength+streamNumberLength],) #this prevents SQL injection - sqlLock.acquire() - sqlSubmitQueue.put('''SELECT hash, transmitdata, time FROM pubkeys WHERE hash=? AND havecorrectnonce=1''') - sqlSubmitQueue.put(t) - queryreturn = sqlReturnQueue.get() - sqlLock.release() - print 'queryreturn', queryreturn - if queryreturn <> []: - print '...but we have the public key from when it came in from someone else. sending it.' - #We have it. Let's send it. - for row in queryreturn: - hash, transmitdata, timeLastRequested = row - if timeLastRequested < int(time.time())+604800: #if the last time anyone asked about this hash was this week, extend the time. - t = (int(time.time())+604800,hash) - sqlSubmitQueue.put('''UPDATE pubkeys set time=? WHERE hash=? ''') - sqlSubmitQueue.put(t) - queryreturn = sqlReturnQueue.get() - inventoryHash = calculateInventoryHash(transmitdata) + inventoryHash = calculateInventoryHash(payload) objectType = 'pubkey' - inventory[inventoryHash] = (objectType, self.streamNumber, transmitdata, int(time.time())) + inventory[inventoryHash] = (objectType, self.streamNumber, payload, int(time.time())) self.broadcastinv(inventoryHash) + else: + printLock.acquire() + print 'This getpubkey request is not for any of my keys.' + printLock.release() #We have received an inv message @@ -2216,7 +2122,8 @@ class singleWorker(QThread): print 'Within WorkerThread, processing sendbroadcast command.' fromAddress,subject,message = data self.sendBroadcast() - + elif command == 'doPOWForMyV2Pubkey': + self.doPOWForMyV2Pubkey(data) elif command == 'newpubkey': toAddressVersion,toStreamNumber,toRipe = data if toRipe in neededPubkeys: @@ -2228,6 +2135,62 @@ class singleWorker(QThread): workerQueue.task_done() + def doPOWForMyV2Pubkey(self,myAddress): #This function also broadcasts out the pubkey message once it is done with the POW + status,addressVersionNumber,streamNumber,hash = decodeAddress(myAddress) + payload = pack('>I',(int(time.time())+random.randrange(-300, 300))) #the current time plus or minus five minutes + payload += encodeVarint(2) #Address version number + payload += encodeVarint(streamNumber) + payload += '\x00\x00\x00\x01' #bitfield of features supported by me (see the wiki). + + privSigningKeyBase58 = config.get(myAddress, 'privsigningkey') + privEncryptionKeyBase58 = config.get(myAddress, 'privencryptionkey') + + privSigningKeyHex = decodeWalletImportFormat(privSigningKeyBase58).encode('hex') + privEncryptionKeyHex = decodeWalletImportFormat(privEncryptionKeyBase58).encode('hex') + pubSigningKey = highlevelcrypto.privToPub(privSigningKeyHex).decode('hex') + pubEncryptionKey = highlevelcrypto.privToPub(privEncryptionKeyHex).decode('hex') + + #print 'within recgetpubkey' + #print 'pubSigningKey in hex:', pubSigningKey.encode('hex') + #print 'pubEncryptionKey in hex:', pubEncryptionKey.encode('hex') + + payload += pubSigningKey[1:] + payload += pubEncryptionKey[1:] + + #Time to do the POW for this pubkey message + nonce = 0 + trialValue = 99999999999999999999 + target = 2**64 / ((len(payload)+payloadLengthExtraBytes+8) * averageProofOfWorkNonceTrialsPerByte) + print '(For pubkey message) Doing proof of work...' + initialHash = hashlib.sha512(payload).digest() + while trialValue > target: + nonce += 1 + trialValue, = unpack('>Q',hashlib.sha512(hashlib.sha512(pack('>Q',nonce) + initialHash).digest()).digest()[0:8]) + #trialValue, = unpack('>Q',hashlib.sha512(hashlib.sha512(pack('>Q',nonce) + payload).digest()).digest()[4:12]) + print '(For pubkey message) Found proof of work', trialValue, 'Nonce:', nonce + + payload = pack('>Q',nonce) + payload + t = (hash,True,payload,int(time.time())+1209600) #after two weeks (1,209,600 seconds), we may remove our own pub key from our database. It will be regenerated and put back in the database if it is requested. + sqlLock.acquire() + sqlSubmitQueue.put('''INSERT INTO pubkeys VALUES (?,?,?,?)''') + sqlSubmitQueue.put(t) + queryreturn = sqlReturnQueue.get() + sqlLock.release() + + inventoryHash = calculateInventoryHash(payload) + objectType = 'pubkey' + inventory[inventoryHash] = (objectType, streamNumber, payload, int(time.time())) + + payload = '\x01' + inventoryHash + headerData = '\xe9\xbe\xb4\xd9' #magic bits, slighly different from Bitcoin's magic bits. + headerData = headerData + 'inv\x00\x00\x00\x00\x00\x00\x00\x00\x00' + headerData = headerData + pack('>L',len(payload)) + headerData = headerData + hashlib.sha512(payload).digest()[:4] + printLock.acquire() + print 'broadcasting inv with hash:', hash.encode('hex') + printLock.release() + broadcastToSendDataQueues((streamNumber, 'send', headerData + payload)) + def sendBroadcast(self): sqlLock.acquire() t = ('broadcastpending',) @@ -3449,10 +3412,7 @@ class MyForm(QtGui.QMainWindow): self.statusBar().showMessage('') if connectionsCount[streamNumber] == 0: self.statusBar().showMessage('Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won\'t send until you connect.') - ackdata = '' - for i in range(4): #This will make 32 bytes of random data. - random.seed() - ackdata += pack('>Q',random.randrange(1, 18446744073709551615)) + ackdata = OpenSSL.rand(32) sqlLock.acquire() t = ('',toAddress,ripe,fromAddress,subject,message,ackdata,int(time.time()),'findingpubkey',1,1,'sent') sqlSubmitQueue.put('''INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?)''') @@ -4212,7 +4172,7 @@ neededPubkeys = {} averageProofOfWorkNonceTrialsPerByte = 320 #The amount of work that should be performed (and demanded) per byte of the payload. Double this number to double the work. payloadLengthExtraBytes = 14000 #To make sending short messages a little more difficult, this value is added to the payload length for use in calculating the proof of work target. -if userVeryEasyProofOfWorkForTesting: +if useVeryEasyProofOfWorkForTesting: averageProofOfWorkNonceTrialsPerByte = averageProofOfWorkNonceTrialsPerByte / 10 payloadLengthExtraBytes = payloadLengthExtraBytes / 10 diff --git a/newaddressdialog.ui b/newaddressdialog.ui index e6d1b290..4f8dd4af 100644 --- a/newaddressdialog.ui +++ b/newaddressdialog.ui @@ -302,6 +302,19 @@ The 'Random Number' option is selected by default but deterministic addresses ha + + radioButtonRandomAddress + radioButtonDeterministicAddress + newaddresslabel + radioButtonMostAvailable + radioButtonExisting + comboBoxExisting + lineEditPassphrase + lineEditPassphraseAgain + spinBoxNumberOfAddressesToMake + checkBoxEighteenByteRipe + buttonBox +