First bit of code necessary for version 3 addresses

This commit is contained in:
Jonathan Warren 2013-04-24 15:48:46 -04:00
parent 6737a21d1c
commit 9bac0b5311
2 changed files with 232 additions and 56 deletions

View File

@ -162,7 +162,7 @@ def decodeAddress(address):
#print 'addressVersionNumber', addressVersionNumber #print 'addressVersionNumber', addressVersionNumber
#print 'bytesUsedByVersionNumber', bytesUsedByVersionNumber #print 'bytesUsedByVersionNumber', bytesUsedByVersionNumber
if addressVersionNumber > 2: if addressVersionNumber > 3:
print 'cannot decode address version numbers this high' print 'cannot decode address version numbers this high'
status = 'versiontoohigh' status = 'versiontoohigh'
return status,0,0,0 return status,0,0,0
@ -176,7 +176,7 @@ def decodeAddress(address):
status = 'success' status = 'success'
if addressVersionNumber == 1: if addressVersionNumber == 1:
return status,addressVersionNumber,streamNumber,data[-24:-4] return status,addressVersionNumber,streamNumber,data[-24:-4]
elif addressVersionNumber == 2: elif addressVersionNumber == 2 or addressVersionNumber == 3:
if len(data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4]) == 19: if len(data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4]) == 19:
return status,addressVersionNumber,streamNumber,'\x00'+data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4] return status,addressVersionNumber,streamNumber,'\x00'+data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4]
elif len(data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4]) == 20: elif len(data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4]) == 20:

View File

@ -409,8 +409,8 @@ class receiveDataThread(QThread):
def isProofOfWorkSufficient(self,data): def isProofOfWorkSufficient(self,data):
POW, = unpack('>Q',hashlib.sha512(hashlib.sha512(data[:8]+ hashlib.sha512(data[8:]).digest()).digest()).digest()[0:8]) POW, = unpack('>Q',hashlib.sha512(hashlib.sha512(data[:8]+ hashlib.sha512(data[8:]).digest()).digest()).digest()[0:8])
#print 'POW:', POW #print 'POW:', POW
#Notice that I have divided the averageProofOfWorkNonceTrialsPerByte by two. This makes the POW requirement easier. This gives us wiggle-room: if we decide that we want to make the POW easier, the change won't obsolete old clients because they already expect a lower POW. If we decide that the current work done by clients feels approperate then we can remove this division by 2 and make the requirement match what is actually done by a sending node. If we want to raise the POW requirement then old nodes will HAVE to upgrade no matter what. #Notice that I have divided the networkDefaultAverageProofOfWorkNonceTrialsPerByte by two. This makes the POW requirement easier. This gives us wiggle-room: if we decide that we want to make the POW easier, the change won't obsolete old clients because they already expect a lower POW. If we decide that the current work done by clients feels approperate then we can remove this division by 2 and make the requirement match what is actually done by a sending node. If we want to raise the POW requirement then old nodes will HAVE to upgrade no matter what.
return POW <= 2**64 / ((len(data)+payloadLengthExtraBytes) * (averageProofOfWorkNonceTrialsPerByte/2)) return POW <= 2**64 / ((len(data)+networkDefaultPayloadLengthExtraBytes) * (networkDefaultAverageProofOfWorkNonceTrialsPerByte/2))
def sendpong(self): def sendpong(self):
print 'Sending pong' print 'Sending pong'
@ -814,7 +814,7 @@ class receiveDataThread(QThread):
if sendersAddressVersionNumber == 0: if sendersAddressVersionNumber == 0:
print 'Cannot understand sendersAddressVersionNumber = 0. Ignoring message.' print 'Cannot understand sendersAddressVersionNumber = 0. Ignoring message.'
return return
if sendersAddressVersionNumber >= 3: if sendersAddressVersionNumber >= 4:
print 'Sender\'s address version number', sendersAddressVersionNumber, ' not yet supported. Ignoring message.' print 'Sender\'s address version number', sendersAddressVersionNumber, ' not yet supported. Ignoring message.'
return return
if len(unencryptedData) < 170: if len(unencryptedData) < 170:
@ -831,6 +831,13 @@ class receiveDataThread(QThread):
readPosition += 64 readPosition += 64
pubEncryptionKey = '\x04' + unencryptedData[readPosition:readPosition+64] pubEncryptionKey = '\x04' + unencryptedData[readPosition:readPosition+64]
readPosition += 64 readPosition += 64
if sendersAddressVersionNumber >= 3:
requiredAverageProofOfWorkNonceTrialsPerByte, varintLength = decodeVarint(unencryptedData[readPosition:readPosition+10])
readPosition += varintLength
print 'sender\'s requiredAverageProofOfWorkNonceTrialsPerByte is', requiredAverageProofOfWorkNonceTrialsPerByte
requiredPayloadLengthExtraBytes, varintLength = decodeVarint(unencryptedData[readPosition:readPosition+10])
readPosition += varintLength
print 'sender\'s requiredPayloadLengthExtraBytes is', requiredPayloadLengthExtraBytes
endOfThePublicKeyPosition = readPosition #needed for when we store the pubkey in our database of pubkeys for later use. endOfThePublicKeyPosition = readPosition #needed for when we store the pubkey in our database of pubkeys for later use.
if toRipe != unencryptedData[readPosition:readPosition+20]: if toRipe != unencryptedData[readPosition:readPosition+20]:
printLock.acquire() printLock.acquire()
@ -1096,7 +1103,7 @@ class receiveDataThread(QThread):
if addressVersion == 0: if addressVersion == 0:
print '(Within processpubkey) addressVersion of 0 doesn\'t make sense.' print '(Within processpubkey) addressVersion of 0 doesn\'t make sense.'
return return
if addressVersion >= 3 or addressVersion == 1: if addressVersion >= 4 or addressVersion == 1:
printLock.acquire() printLock.acquire()
print 'This version of Bitmessage cannot handle version', addressVersion,'addresses.' print 'This version of Bitmessage cannot handle version', addressVersion,'addresses.'
printLock.release() printLock.release()
@ -1155,6 +1162,74 @@ class receiveDataThread(QThread):
printLock.acquire() printLock.acquire()
printLock.release() printLock.release()
workerQueue.put(('newpubkey',(addressVersion,streamNumber,ripe))) workerQueue.put(('newpubkey',(addressVersion,streamNumber,ripe)))
if addressVersion == 3:
if len(data) < 170: #sanity check.
print '(within processpubkey) payloadLength less than 170. Sanity check failed.'
return
bitfieldBehaviors = data[readPosition:readPosition+4]
readPosition += 4
publicSigningKey = '\x04'+data[readPosition:readPosition+64]
#Is it possible for a public key to be invalid such that trying to encrypt or sign with it will cause an error? If it is, we should probably test these keys here.
readPosition += 64
publicEncryptionKey = '\x04'+data[readPosition:readPosition+64]
"""if len(publicEncryptionKey) < 64:
print 'publicEncryptionKey length less than 64. Sanity check failed.'
return"""
readPosition += 64
specifiedNonceTrialsPerByte, specifiedNonceTrialsPerByteLength = decodeVarint(data[readPosition:readPosition+10])
readPosition += specifiedNonceTrialsPerByteLength
specifiedPayloadLengthExtraBytes, specifiedPayloadLengthExtraBytesLength = decodeVarint(data[readPosition:readPosition+10])
readPosition += specifiedPayloadLengthExtraBytesLength
signatureLength, signatureLengthLength = decodeVarint(data[readPosition:readPosition+10])
signature = data[readPosition:readPosition+signatureLengthLength]
try:
highlevelcrypto.verify(data[8:readPosition],signature,publicSigningKey.encode('hex'))
print 'ECDSA verify passed (within processpubkey)'
except Exception, err:
print 'ECDSA verify failed (within processpubkey)', err
return
sha = hashlib.new('sha512')
sha.update(publicSigningKey+publicEncryptionKey)
ripeHasher = hashlib.new('ripemd160')
ripeHasher.update(sha.digest())
ripe = ripeHasher.digest()
printLock.acquire()
print 'within recpubkey, addressVersion:', addressVersion, ', streamNumber:', streamNumber
print 'ripe', ripe.encode('hex')
print 'publicSigningKey in hex:', publicSigningKey.encode('hex')
print 'publicEncryptionKey in hex:', publicEncryptionKey.encode('hex')
printLock.release()
t = (ripe,)
sqlLock.acquire()
sqlSubmitQueue.put('''SELECT usedpersonally FROM pubkeys WHERE hash=? AND usedpersonally='yes' ''')
sqlSubmitQueue.put(t)
queryreturn = sqlReturnQueue.get()
sqlLock.release()
if queryreturn != []: #if this pubkey is already in our database and if we have used it personally:
print 'We HAVE used this pubkey personally. Updating time.'
t = (ripe,True,data,embeddedTime,'yes')
sqlLock.acquire()
sqlSubmitQueue.put('''INSERT INTO pubkeys VALUES (?,?,?,?,?)''')
sqlSubmitQueue.put(t)
sqlReturnQueue.get()
sqlSubmitQueue.put('commit')
sqlLock.release()
workerQueue.put(('newpubkey',(addressVersion,streamNumber,ripe)))
else:
print 'We have NOT used this pubkey personally. Inserting in database.'
t = (ripe,True,data,embeddedTime,'no') #This will also update the embeddedTime.
sqlLock.acquire()
sqlSubmitQueue.put('''INSERT INTO pubkeys VALUES (?,?,?,?,?)''')
sqlSubmitQueue.put(t)
sqlReturnQueue.get()
sqlSubmitQueue.put('commit')
sqlLock.release()
printLock.acquire()
printLock.release()
workerQueue.put(('newpubkey',(addressVersion,streamNumber,ripe)))
#We have received a getpubkey message #We have received a getpubkey message
@ -1181,7 +1256,7 @@ class receiveDataThread(QThread):
if embeddedTime < int(time.time())-maximumAgeOfAnObjectThatIAmWillingToAccept: if embeddedTime < int(time.time())-maximumAgeOfAnObjectThatIAmWillingToAccept:
print 'The time in this getpubkey message is too old. Ignoring it. Time:', embeddedTime print 'The time in this getpubkey message is too old. Ignoring it. Time:', embeddedTime
return return
addressVersionNumber, addressVersionLength = decodeVarint(data[readPosition:readPosition+10]) requestedAddressVersionNumber, addressVersionLength = decodeVarint(data[readPosition:readPosition+10])
readPosition += addressVersionLength readPosition += addressVersionLength
streamNumber, streamNumberLength = decodeVarint(data[readPosition:readPosition+10]) streamNumber, streamNumberLength = decodeVarint(data[readPosition:readPosition+10])
if streamNumber <> self.streamNumber: if streamNumber <> self.streamNumber:
@ -1206,14 +1281,14 @@ class receiveDataThread(QThread):
#This getpubkey request is valid so far. Forward to peers. #This getpubkey request is valid so far. Forward to peers.
self.broadcastinv(inventoryHash) self.broadcastinv(inventoryHash)
if addressVersionNumber == 0: if requestedAddressVersionNumber == 0:
print 'The addressVersionNumber of the pubkey request is zero. That doesn\'t make any sense. Ignoring it.' print 'The requestedAddressVersionNumber of the pubkey request is zero. That doesn\'t make any sense. Ignoring it.'
return return
elif addressVersionNumber == 1: elif requestedAddressVersionNumber == 1:
print 'The addressVersionNumber of the pubkey request is 1 which isn\'t supported anymore. Ignoring it.' print 'The requestedAddressVersionNumber of the pubkey request is 1 which isn\'t supported anymore. Ignoring it.'
return return
elif addressVersionNumber > 2: elif requestedAddressVersionNumber > 3:
print 'The addressVersionNumber of the pubkey request is too high. Can\'t understand. Ignoring it.' print 'The requestedAddressVersionNumber of the pubkey request is too high. Can\'t understand. Ignoring it.'
return return
requestedHash = data[readPosition:readPosition+20] requestedHash = data[readPosition:readPosition+20]
@ -1240,16 +1315,27 @@ class receiveDataThread(QThread):
self.broadcastinv(inventoryHash)""" 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.) #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 myAddressesByHash: #if this address hash is one of mine if requestedHash in myAddressesByHash: #if this address hash is one of mine
if decodeAddress(myAddressesByHash[requestedHash])[1] != requestedAddressVersionNumber:
printLock.acquire()
sys.stderr.write('(Within the recgetpubkey function) Someone requested one of my pubkeys but the requestedAddressVersionNumber doesn\'t match my actual address version number. That shouldn\'t have happened. Ignoring.\n')
printLock.release()
return
try: try:
lastPubkeySendTime = int(config.get(myAddressesByHash[requestedHash],'lastpubkeysendtime')) lastPubkeySendTime = int(config.get(myAddressesByHash[requestedHash],'lastpubkeysendtime'))
except: except:
lastPubkeySendTime = 0 lastPubkeySendTime = 0
print 'lastPubkeySendTime is',lastPubkeySendTime
if lastPubkeySendTime < time.time()-lengthOfTimeToHoldOnToAllPubkeys: #If the last time we sent our pubkey was 28 days ago if lastPubkeySendTime < time.time()-lengthOfTimeToHoldOnToAllPubkeys: #If the last time we sent our pubkey was 28 days ago
printLock.acquire() printLock.acquire()
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.' 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.'
printLock.release() printLock.release()
if requestedAddressVersionNumber == 2:
workerQueue.put(('doPOWForMyV2Pubkey',requestedHash)) workerQueue.put(('doPOWForMyV2Pubkey',requestedHash))
elif requestedAddressVersionNumber == 3:
workerQueue.put(('doPOWForMyV3Pubkey',requestedHash))
else:
printLock.acquire()
print 'Found getpubkey-requested-hash in my list of EC hashes BUT we already sent it recently. Ignoring request. The lastPubkeySendTime is:',lastPubkeySendTime
printLock.release()
else: else:
printLock.acquire() printLock.acquire()
print 'This getpubkey request is not for any of my keys.' print 'This getpubkey request is not for any of my keys.'
@ -1934,13 +2020,13 @@ def reloadMyAddressHashes():
isEnabled = config.getboolean(addressInKeysFile, 'enabled') isEnabled = config.getboolean(addressInKeysFile, 'enabled')
if isEnabled: if isEnabled:
status,addressVersionNumber,streamNumber,hash = decodeAddress(addressInKeysFile) status,addressVersionNumber,streamNumber,hash = decodeAddress(addressInKeysFile)
if addressVersionNumber == 2: if addressVersionNumber == 2 or addressVersionNumber == 3:
privEncryptionKey = decodeWalletImportFormat(config.get(addressInKeysFile, 'privencryptionkey')).encode('hex') #returns a simple 32 bytes of information encoded in 64 Hex characters, or null if there was an error privEncryptionKey = decodeWalletImportFormat(config.get(addressInKeysFile, 'privencryptionkey')).encode('hex') #returns a simple 32 bytes of information encoded in 64 Hex characters, or null if there was an error
if len(privEncryptionKey) == 64:#It is 32 bytes encoded as 64 hex characters if len(privEncryptionKey) == 64:#It is 32 bytes encoded as 64 hex characters
myECCryptorObjects[hash] = highlevelcrypto.makeCryptor(privEncryptionKey) myECCryptorObjects[hash] = highlevelcrypto.makeCryptor(privEncryptionKey)
myAddressesByHash[hash] = addressInKeysFile myAddressesByHash[hash] = addressInKeysFile
else: else:
sys.stderr.write('Error in reloadMyAddressHashes: Can\'t handle address versions other than 2.\n') sys.stderr.write('Error in reloadMyAddressHashes: Can\'t handle address versions other than 2 or 3.\n')
#This function expects that pubkey begin with \x04 #This function expects that pubkey begin with \x04
def calculateBitcoinAddressFromPubkey(pubkey): def calculateBitcoinAddressFromPubkey(pubkey):
@ -2307,6 +2393,8 @@ class singleWorker(QThread):
self.sendBroadcast() self.sendBroadcast()
elif command == 'doPOWForMyV2Pubkey': elif command == 'doPOWForMyV2Pubkey':
self.doPOWForMyV2Pubkey(data) self.doPOWForMyV2Pubkey(data)
elif command == 'doPOWForMyV3Pubkey':
self.doPOWForMyV3Pubkey(data)
elif command == 'newpubkey': elif command == 'newpubkey':
toAddressVersion,toStreamNumber,toRipe = data toAddressVersion,toStreamNumber,toRipe = data
if toRipe in neededPubkeys: if toRipe in neededPubkeys:
@ -2361,7 +2449,80 @@ class singleWorker(QThread):
#Do the POW for this pubkey message #Do the POW for this pubkey message
nonce = 0 nonce = 0
trialValue = 99999999999999999999 trialValue = 99999999999999999999
target = 2**64 / ((len(payload)+payloadLengthExtraBytes+8) * averageProofOfWorkNonceTrialsPerByte) target = 2**64 / ((len(payload)+networkDefaultPayloadLengthExtraBytes+8) * networkDefaultAverageProofOfWorkNonceTrialsPerByte)
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])
print '(For pubkey message) Found proof of work', trialValue, 'Nonce:', nonce
payload = pack('>Q',nonce) + payload
"""t = (hash,True,payload,embeddedTime,'no')
sqlLock.acquire()
sqlSubmitQueue.put('''INSERT INTO pubkeys VALUES (?,?,?,?,?)''')
sqlSubmitQueue.put(t)
queryreturn = sqlReturnQueue.get()
sqlSubmitQueue.put('commit')
sqlLock.release()"""
inventoryHash = calculateInventoryHash(payload)
objectType = 'pubkey'
inventory[inventoryHash] = (objectType, streamNumber, payload, embeddedTime)
printLock.acquire()
print 'broadcasting inv with hash:', inventoryHash.encode('hex')
printLock.release()
broadcastToSendDataQueues((streamNumber, 'sendinv', inventoryHash))
self.emit(SIGNAL("updateStatusBar(PyQt_PyObject)"),"")
config.set(myAddress,'lastpubkeysendtime',str(int(time.time())))
with open(appdata + 'keys.dat', 'wb') as configfile:
config.write(configfile)
def doPOWForMyV3Pubkey(self,hash): #This function also broadcasts out the pubkey message once it is done with the POW
#Look up my stream number based on my address hash
"""configSections = config.sections()
for addressInKeysFile in configSections:
if addressInKeysFile <> 'bitmessagesettings':
status,addressVersionNumber,streamNumber,hashFromThisParticularAddress = decodeAddress(addressInKeysFile)
if hash == hashFromThisParticularAddress:
myAddress = addressInKeysFile
break"""
myAddress = myAddressesByHash[hash]
status,addressVersionNumber,streamNumber,hash = decodeAddress(myAddress)
embeddedTime = int(time.time()+random.randrange(-300, 300)) #the current time plus or minus five minutes
payload = pack('>I',(embeddedTime))
payload += encodeVarint(addressVersionNumber) #Address version number
payload += encodeVarint(streamNumber)
payload += '\x00\x00\x00\x01' #bitfield of features supported by me (see the wiki).
try:
privSigningKeyBase58 = config.get(myAddress, 'privsigningkey')
privEncryptionKeyBase58 = config.get(myAddress, 'privencryptionkey')
except Exception, err:
printLock.acquire()
sys.stderr.write('Error within doPOWForMyV3Pubkey. Could not read the keys from the keys.dat file for a requested address. %s\n' % err)
printLock.release()
return
privSigningKeyHex = decodeWalletImportFormat(privSigningKeyBase58).encode('hex')
privEncryptionKeyHex = decodeWalletImportFormat(privEncryptionKeyBase58).encode('hex')
pubSigningKey = highlevelcrypto.privToPub(privSigningKeyHex).decode('hex')
pubEncryptionKey = highlevelcrypto.privToPub(privEncryptionKeyHex).decode('hex')
payload += pubSigningKey[1:]
payload += pubEncryptionKey[1:]
payload += encodeVarint(networkDefaultAverageProofOfWorkNonceTrialsPerByte) #this is where we would multiply networkDefaultAverageProofOfWorkNonceTrialsPerByte by some difficulty set by the user.
payload += encodeVarint(networkDefaultPayloadLengthExtraBytes) #this is where we would multiply networkDefaultPayloadLengthExtraBytes by some difficulty set by the user.
signature = highlevelcrypto.sign(payload,privSigningKeyHex)
payload += encodeVarint(len(signature))
payload += signature
#Do the POW for this pubkey message
nonce = 0
trialValue = 99999999999999999999
target = 2**64 / ((len(payload)+networkDefaultPayloadLengthExtraBytes+8) * networkDefaultAverageProofOfWorkNonceTrialsPerByte)
print '(For pubkey message) Doing proof of work...' print '(For pubkey message) Doing proof of work...'
initialHash = hashlib.sha512(payload).digest() initialHash = hashlib.sha512(payload).digest()
while trialValue > target: while trialValue > target:
@ -2434,7 +2595,7 @@ class singleWorker(QThread):
nonce = 0 nonce = 0
trialValue = 99999999999999999999 trialValue = 99999999999999999999
target = 2**64 / ((len(payload)+payloadLengthExtraBytes+8) * averageProofOfWorkNonceTrialsPerByte) target = 2**64 / ((len(payload)+networkDefaultPayloadLengthExtraBytes+8) * networkDefaultAverageProofOfWorkNonceTrialsPerByte)
print '(For broadcast message) Doing proof of work...' print '(For broadcast message) Doing proof of work...'
self.emit(SIGNAL("updateSentItemStatusByAckdata(PyQt_PyObject,PyQt_PyObject)"),ackdata,'Doing work necessary to send broadcast...') self.emit(SIGNAL("updateSentItemStatusByAckdata(PyQt_PyObject,PyQt_PyObject)"),ackdata,'Doing work necessary to send broadcast...')
initialHash = hashlib.sha512(payload).digest() initialHash = hashlib.sha512(payload).digest()
@ -2525,44 +2686,47 @@ class singleWorker(QThread):
payload += encodeVarint(len(signature)) payload += encodeVarint(len(signature))
payload += signature payload += signature
"""elif fromAddressVersionNumber == 1: #This code is for old version 1 (RSA) addresses. It will soon be removed. if fromAddressVersionNumber == 3:
payload = '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' #this run of nulls allows the true message receiver to identify his message payload = '\x01' #Message version.
payload += '\x01' #Message version.
payload += '\x00\x00\x00\x01'
payload += encodeVarint(fromAddressVersionNumber) payload += encodeVarint(fromAddressVersionNumber)
payload += encodeVarint(fromStreamNumber) payload += encodeVarint(fromStreamNumber)
payload += '\x00\x00\x00\x01' #Bitfield of features and behaviors that can be expected from me. (See https://bitmessage.org/wiki/Protocol_specification#Pubkey_bitfield_features )
#We need to convert our private keys to public keys in order to include them.
try: try:
sendersN = convertIntToString(config.getint(fromaddress, 'n')) privSigningKeyBase58 = config.get(fromaddress, 'privsigningkey')
privEncryptionKeyBase58 = config.get(fromaddress, 'privencryptionkey')
except: except:
printLock.acquire() self.emit(SIGNAL("updateSentItemStatusByAckdata(PyQt_PyObject,PyQt_PyObject)"),ackdata,'Error! Could not find sender address (your address) in the keys.dat file.')
print 'Error: Could not find', fromaddress, 'in our keys.dat file. You must have deleted it. Aborting the send.' continue
printLock.release()
return
payload += encodeVarint(len(sendersN))
payload += sendersN
sendersE = convertIntToString(config.getint(fromaddress, 'e')) privSigningKeyHex = decodeWalletImportFormat(privSigningKeyBase58).encode('hex')
payload += encodeVarint(len(sendersE)) privEncryptionKeyHex = decodeWalletImportFormat(privEncryptionKeyBase58).encode('hex')
payload += sendersE
payload += '\x02' #Type 2 is simple UTF-8 message encoding. pubSigningKey = highlevelcrypto.privToPub(privSigningKeyHex).decode('hex')
pubEncryptionKey = highlevelcrypto.privToPub(privEncryptionKeyHex).decode('hex')
payload += pubSigningKey[1:] #The \x04 on the beginning of the public keys are not sent. This way there is only one acceptable way to encode and send a public key.
payload += pubEncryptionKey[1:]
payload += encodeVarint(networkDefaultAverageProofOfWorkNonceTrialsPerByte) #this is where we would multiply networkDefaultAverageProofOfWorkNonceTrialsPerByte by some difficulty we demand others meet.
payload += encodeVarint(networkDefaultPayloadLengthExtraBytes) #this is where we would multiply networkDefaultPayloadLengthExtraBytes by some difficulty set by some difficulty we demand others meet.
payload += toHash #This hash will be checked by the receiver of the message to verify that toHash belongs to them. This prevents a Surreptitious Forwarding Attack.
payload += '\x02' #Type 2 is simple UTF-8 message encoding as specified on the Protocol Specification on the Bitmessage Wiki.
messageToTransmit = 'Subject:' + subject + '\n' + 'Body:' + message messageToTransmit = 'Subject:' + subject + '\n' + 'Body:' + message
payload += encodeVarint(len(messageToTransmit)) payload += encodeVarint(len(messageToTransmit))
payload += messageToTransmit payload += messageToTransmit
fullAckPayload = self.generateFullAckMessage(ackdata,toStreamNumber,embeddedTime)#The fullAckPayload is a normal msg protocol message with the proof of work already completed that the receiver of this message can easily send out.
#Later, if anyone impliments clients that don't send the ack_data, then we should probably check here to make sure that the receiver will make use of this ack_data and not attach it if not.
fullAckPayload = self.generateFullAckMessage(ackdata,toStreamNumber,embeddedTime)
payload += encodeVarint(len(fullAckPayload)) payload += encodeVarint(len(fullAckPayload))
payload += fullAckPayload payload += fullAckPayload
sendersPrivKey = rsa.PrivateKey(config.getint(fromaddress, 'n'),config.getint(fromaddress, 'e'),config.getint(fromaddress, 'd'),config.getint(fromaddress, 'p'),config.getint(fromaddress, 'q')) signature = highlevelcrypto.sign(payload,privSigningKeyHex)
payload += encodeVarint(len(signature))
payload += signature
payload += rsa.sign(payload,sendersPrivKey,'SHA-512')"""
#We have assembled the data that will be encrypted. Now let us fetch the recipient's public key out of our database and do the encryption. #We have assembled the data that will be encrypted. Now let us fetch the recipient's public key out of our database and do the encryption.
if toAddressVersionNumber == 2: if toAddressVersionNumber == 2 or toAddressVersionNumber == 3:
sqlLock.acquire() sqlLock.acquire()
sqlSubmitQueue.put('SELECT transmitdata FROM pubkeys WHERE hash=?') sqlSubmitQueue.put('SELECT transmitdata FROM pubkeys WHERE hash=?')
sqlSubmitQueue.put((toRipe,)) sqlSubmitQueue.put((toRipe,))
@ -2588,6 +2752,18 @@ class singleWorker(QThread):
readPosition += 64 readPosition += 64
pubEncryptionKeyBase256 = pubkeyPayload[readPosition:readPosition+64] pubEncryptionKeyBase256 = pubkeyPayload[readPosition:readPosition+64]
readPosition += 64 readPosition += 64
if toAddressVersionNumber == 2:
requiredAverageProofOfWorkNonceTrialsPerByte = networkDefaultAverageProofOfWorkNonceTrialsPerByte
requiredPayloadLengthExtraBytes = networkDefaultPayloadLengthExtraBytes
elif toAddressVersionNumber == 3:
requiredAverageProofOfWorkNonceTrialsPerByte, varintLength = decodeVarint(pubkeyPayload[readPosition:readPosition+10])
readPosition += varintLength
requiredPayloadLengthExtraBytes, varintLength = decodeVarint(pubkeyPayload[readPosition:readPosition+10])
readPosition += varintLength
if requiredAverageProofOfWorkNonceTrialsPerByte < networkDefaultAverageProofOfWorkNonceTrialsPerByte: #We still have to meet a minimum POW difficulty regardless of what they say is allowed in order to get our message to propagate through the network.
requiredAverageProofOfWorkNonceTrialsPerByte = networkDefaultAverageProofOfWorkNonceTrialsPerByte
if requiredPayloadLengthExtraBytes < networkDefaultPayloadLengthExtraBytes:
requiredPayloadLengthExtraBytes = networkDefaultPayloadLengthExtraBytes
encrypted = highlevelcrypto.encrypt(payload,"04"+pubEncryptionKeyBase256.encode('hex')) encrypted = highlevelcrypto.encrypt(payload,"04"+pubEncryptionKeyBase256.encode('hex'))
nonce = 0 nonce = 0
@ -2596,7 +2772,7 @@ class singleWorker(QThread):
encodedStreamNumber = encodeVarint(toStreamNumber) encodedStreamNumber = encodeVarint(toStreamNumber)
#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. #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.
payload = embeddedTime + encodedStreamNumber + encrypted payload = embeddedTime + encodedStreamNumber + encrypted
target = 2**64 / ((len(payload)+payloadLengthExtraBytes+8) * averageProofOfWorkNonceTrialsPerByte) target = 2**64 / ((len(payload)+requiredPayloadLengthExtraBytes+8) * requiredAverageProofOfWorkNonceTrialsPerByte)
print '(For msg message) Doing proof of work. Target:', target print '(For msg message) Doing proof of work. Target:', target
powStartTime = time.time() powStartTime = time.time()
initialHash = hashlib.sha512(payload).digest() initialHash = hashlib.sha512(payload).digest()
@ -2647,7 +2823,7 @@ class singleWorker(QThread):
self.emit(SIGNAL("updateStatusBar(PyQt_PyObject)"),statusbar) self.emit(SIGNAL("updateStatusBar(PyQt_PyObject)"),statusbar)
self.emit(SIGNAL("updateSentItemStatusByHash(PyQt_PyObject,PyQt_PyObject)"),ripe,'Doing work necessary to request public key.') self.emit(SIGNAL("updateSentItemStatusByHash(PyQt_PyObject,PyQt_PyObject)"),ripe,'Doing work necessary to request public key.')
print 'Doing proof-of-work necessary to send getpubkey message.' print 'Doing proof-of-work necessary to send getpubkey message.'
target = 2**64 / ((len(payload)+payloadLengthExtraBytes+8) * averageProofOfWorkNonceTrialsPerByte) target = 2**64 / ((len(payload)+networkDefaultPayloadLengthExtraBytes+8) * networkDefaultAverageProofOfWorkNonceTrialsPerByte)
initialHash = hashlib.sha512(payload).digest() initialHash = hashlib.sha512(payload).digest()
while trialValue > target: while trialValue > target:
nonce += 1 nonce += 1
@ -2671,7 +2847,7 @@ class singleWorker(QThread):
trialValue = 99999999999999999999 trialValue = 99999999999999999999
encodedStreamNumber = encodeVarint(toStreamNumber) encodedStreamNumber = encodeVarint(toStreamNumber)
payload = embeddedTime + encodedStreamNumber + ackdata payload = embeddedTime + encodedStreamNumber + ackdata
target = 2**64 / ((len(payload)+payloadLengthExtraBytes+8) * averageProofOfWorkNonceTrialsPerByte) target = 2**64 / ((len(payload)+networkDefaultPayloadLengthExtraBytes+8) * networkDefaultAverageProofOfWorkNonceTrialsPerByte)
printLock.acquire() printLock.acquire()
print '(For ack message) Doing proof of work...' print '(For ack message) Doing proof of work...'
printLock.release() printLock.release()
@ -2707,7 +2883,7 @@ class addressGenerator(QThread):
self.eighteenByteRipe = eighteenByteRipe self.eighteenByteRipe = eighteenByteRipe
def run(self): def run(self):
if self.addressVersionNumber == 2: if self.addressVersionNumber == 3:
if self.deterministicPassphrase == "": if self.deterministicPassphrase == "":
statusbar = 'Generating one new address' statusbar = 'Generating one new address'
@ -2738,7 +2914,7 @@ class addressGenerator(QThread):
break break
print 'Generated address with ripe digest:', ripe.digest().encode('hex') print 'Generated address with ripe digest:', ripe.digest().encode('hex')
print 'Address generator calculated', numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix, 'addresses at', numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix/(time.time()-startTime),'addresses per second before finding one with the correct ripe-prefix.' print 'Address generator calculated', numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix, 'addresses at', numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix/(time.time()-startTime),'addresses per second before finding one with the correct ripe-prefix.'
address = encodeAddress(2,self.streamNumber,ripe.digest()) address = encodeAddress(3,self.streamNumber,ripe.digest())
#self.emit(SIGNAL("updateStatusBar(PyQt_PyObject)"),'Finished generating address. Writing to keys.dat') #self.emit(SIGNAL("updateStatusBar(PyQt_PyObject)"),'Finished generating address. Writing to keys.dat')
#An excellent way for us to store our keys is in Wallet Import Format. Let us convert now. #An excellent way for us to store our keys is in Wallet Import Format. Let us convert now.
@ -2768,7 +2944,7 @@ class addressGenerator(QThread):
self.emit(SIGNAL("updateStatusBar(PyQt_PyObject)"),'Done generating address. Doing work necessary to broadcast it...') self.emit(SIGNAL("updateStatusBar(PyQt_PyObject)"),'Done generating address. Doing work necessary to broadcast it...')
self.emit(SIGNAL("writeNewAddressToTable(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"),self.label,address,str(self.streamNumber)) self.emit(SIGNAL("writeNewAddressToTable(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"),self.label,address,str(self.streamNumber))
reloadMyAddressHashes() reloadMyAddressHashes()
workerQueue.put(('doPOWForMyV2Pubkey',ripe.digest())) workerQueue.put(('doPOWForMyV3Pubkey',ripe.digest()))
else: #There is something in the deterministicPassphrase variable thus we are going to do this deterministically. else: #There is something in the deterministicPassphrase variable thus we are going to do this deterministically.
statusbar = 'Generating '+str(self.numberOfAddressesToMake) + ' new addresses.' statusbar = 'Generating '+str(self.numberOfAddressesToMake) + ' new addresses.'
@ -2807,7 +2983,7 @@ class addressGenerator(QThread):
print 'ripe.digest', ripe.digest().encode('hex') print 'ripe.digest', ripe.digest().encode('hex')
print 'Address generator calculated', numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix, 'addresses at', numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix/(time.time()-startTime),'keys per second.' print 'Address generator calculated', numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix, 'addresses at', numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix/(time.time()-startTime),'keys per second.'
address = encodeAddress(2,self.streamNumber,ripe.digest()) address = encodeAddress(3,self.streamNumber,ripe.digest())
#self.emit(SIGNAL("updateStatusBar(PyQt_PyObject)"),'Finished generating address. Writing to keys.dat') #self.emit(SIGNAL("updateStatusBar(PyQt_PyObject)"),'Finished generating address. Writing to keys.dat')
#An excellent way for us to store our keys is in Wallet Import Format. Let us convert now. #An excellent way for us to store our keys is in Wallet Import Format. Let us convert now.
@ -3219,7 +3395,7 @@ class singleAPISignalHandler(QThread):
label, eighteenByteRipe = data label, eighteenByteRipe = data
streamNumberForAddress = 1 streamNumberForAddress = 1
self.addressGenerator = addressGenerator() self.addressGenerator = addressGenerator()
self.addressGenerator.setup(2,streamNumberForAddress,label,1,"",eighteenByteRipe) self.addressGenerator.setup(3,streamNumberForAddress,label,1,"",eighteenByteRipe)
self.emit(SIGNAL("passAddressGeneratorObjectThrough(PyQt_PyObject)"),self.addressGenerator) self.emit(SIGNAL("passAddressGeneratorObjectThrough(PyQt_PyObject)"),self.addressGenerator)
self.addressGenerator.start() self.addressGenerator.start()
elif command == 'createDeterministicAddresses': elif command == 'createDeterministicAddresses':
@ -4082,7 +4258,7 @@ class MyForm(QtGui.QMainWindow):
continue continue
except: except:
pass pass
if addressVersionNumber > 2 or addressVersionNumber == 0: if addressVersionNumber > 3 or addressVersionNumber == 0:
QMessageBox.about(self, "Address version number", "Concerning the address "+toAddress+", Bitmessage cannot understand address version numbers of "+str(addressVersionNumber)+". Perhaps upgrade Bitmessage to the latest version.") QMessageBox.about(self, "Address version number", "Concerning the address "+toAddress+", Bitmessage cannot understand address version numbers of "+str(addressVersionNumber)+". Perhaps upgrade Bitmessage to the latest version.")
continue continue
if streamNumber > 1 or streamNumber == 0: if streamNumber > 1 or streamNumber == 0:
@ -4638,7 +4814,7 @@ class MyForm(QtGui.QMainWindow):
streamNumberForAddress = addressStream(self.dialog.ui.comboBoxExisting.currentText()) streamNumberForAddress = addressStream(self.dialog.ui.comboBoxExisting.currentText())
self.addressGenerator = addressGenerator() self.addressGenerator = addressGenerator()
self.addressGenerator.setup(2,streamNumberForAddress,str(self.dialog.ui.newaddresslabel.text().toUtf8()),1,"",self.dialog.ui.checkBoxEighteenByteRipe.isChecked()) self.addressGenerator.setup(3,streamNumberForAddress,str(self.dialog.ui.newaddresslabel.text().toUtf8()),1,"",self.dialog.ui.checkBoxEighteenByteRipe.isChecked())
QtCore.QObject.connect(self.addressGenerator, SIGNAL("writeNewAddressToTable(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"), self.writeNewAddressToTable) QtCore.QObject.connect(self.addressGenerator, SIGNAL("writeNewAddressToTable(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"), self.writeNewAddressToTable)
QtCore.QObject.connect(self.addressGenerator, QtCore.SIGNAL("updateStatusBar(PyQt_PyObject)"), self.updateStatusBar) QtCore.QObject.connect(self.addressGenerator, QtCore.SIGNAL("updateStatusBar(PyQt_PyObject)"), self.updateStatusBar)
self.addressGenerator.start() self.addressGenerator.start()
@ -4650,7 +4826,7 @@ class MyForm(QtGui.QMainWindow):
else: else:
streamNumberForAddress = 1 #this will eventually have to be replaced by logic to determine the most available stream number. streamNumberForAddress = 1 #this will eventually have to be replaced by logic to determine the most available stream number.
self.addressGenerator = addressGenerator() self.addressGenerator = addressGenerator()
self.addressGenerator.setup(2,streamNumberForAddress,"unused address",self.dialog.ui.spinBoxNumberOfAddressesToMake.value(),self.dialog.ui.lineEditPassphrase.text().toUtf8(),self.dialog.ui.checkBoxEighteenByteRipe.isChecked()) self.addressGenerator.setup(3,streamNumberForAddress,"unused address",self.dialog.ui.spinBoxNumberOfAddressesToMake.value(),self.dialog.ui.lineEditPassphrase.text().toUtf8(),self.dialog.ui.checkBoxEighteenByteRipe.isChecked())
QtCore.QObject.connect(self.addressGenerator, SIGNAL("writeNewAddressToTable(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"), self.writeNewAddressToTable) QtCore.QObject.connect(self.addressGenerator, SIGNAL("writeNewAddressToTable(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"), self.writeNewAddressToTable)
QtCore.QObject.connect(self.addressGenerator, QtCore.SIGNAL("updateStatusBar(PyQt_PyObject)"), self.updateStatusBar) QtCore.QObject.connect(self.addressGenerator, QtCore.SIGNAL("updateStatusBar(PyQt_PyObject)"), self.updateStatusBar)
self.addressGenerator.start() self.addressGenerator.start()
@ -5158,12 +5334,12 @@ apiAddressGeneratorReturnQueue = Queue.Queue() #The address generator thread use
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. 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.
#These constants are not at the top because if changed they will cause particularly unexpected behavior: You won't be able to either send or receive messages because the proof of work you do (or demand) won't match that done or demanded by others. Don't change them! #These constants are not at the top because if changed they will cause particularly unexpected behavior: You won't be able to either send or receive messages because the proof of work you do (or demand) won't match that done or demanded by others. Don't change them!
averageProofOfWorkNonceTrialsPerByte = 320 #The amount of work that should be performed (and demanded) per byte of the payload. Double this number to double the work. networkDefaultAverageProofOfWorkNonceTrialsPerByte = 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. networkDefaultPayloadLengthExtraBytes = 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 useVeryEasyProofOfWorkForTesting: if useVeryEasyProofOfWorkForTesting:
averageProofOfWorkNonceTrialsPerByte = averageProofOfWorkNonceTrialsPerByte / 16 networkDefaultAverageProofOfWorkNonceTrialsPerByte = networkDefaultAverageProofOfWorkNonceTrialsPerByte / 16
payloadLengthExtraBytes = payloadLengthExtraBytes / 7000 networkDefaultPayloadLengthExtraBytes = networkDefaultPayloadLengthExtraBytes / 7000
if __name__ == "__main__": if __name__ == "__main__":
# Check the Major version, the first element in the array # Check the Major version, the first element in the array