v0.3.0 #132

Merged
Atheros1 merged 34 commits from master into master 2013-05-06 07:03:38 +02:00
4 changed files with 398 additions and 182 deletions
Showing only changes of commit 63e698f562 - Show all commits

View File

@ -72,7 +72,7 @@ class outgoingSynSender(QThread):
time.sleep(1) time.sleep(1)
global alreadyAttemptedConnectionsListResetTime global alreadyAttemptedConnectionsListResetTime
while True: while True:
time.sleep(999999)#I sometimes use this to prevent connections for testing. #time.sleep(999999)#I sometimes use this to prevent connections for testing.
if len(selfInitiatedConnections[self.streamNumber]) < 8: #maximum number of outgoing connections = 8 if len(selfInitiatedConnections[self.streamNumber]) < 8: #maximum number of outgoing connections = 8
random.seed() random.seed()
HOST, = random.sample(knownNodes[self.streamNumber], 1) HOST, = random.sample(knownNodes[self.streamNumber], 1)
@ -409,11 +409,14 @@ class receiveDataThread(QThread):
print 'Checksum incorrect. Clearing this message.' print 'Checksum incorrect. Clearing this message.'
self.data = self.data[self.payloadLength+24:] self.data = self.data[self.payloadLength+24:]
def isProofOfWorkSufficient(self,data): def isProofOfWorkSufficient(self,data,nonceTrialsPerByte=0,payloadLengthExtraBytes=0):
if nonceTrialsPerByte < networkDefaultProofOfWorkNonceTrialsPerByte:
nonceTrialsPerByte = networkDefaultProofOfWorkNonceTrialsPerByte
if payloadLengthExtraBytes < networkDefaultPayloadLengthExtraBytes:
payloadLengthExtraBytes = networkDefaultPayloadLengthExtraBytes
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 networkDefaultProofOfWorkNonceTrialsPerByte 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) * (nonceTrialsPerByte))
return POW <= 2**64 / ((len(data)+networkDefaultPayloadLengthExtraBytes) * (networkDefaultProofOfWorkNonceTrialsPerByte/2))
def sendpong(self): def sendpong(self):
print 'Sending pong' print 'Sending pong'
@ -533,6 +536,14 @@ class receiveDataThread(QThread):
if len(data) < 180: if len(data) < 180:
print 'The payload length of this broadcast packet is unreasonably low. Someone is probably trying funny business. Ignoring message.' print 'The payload length of this broadcast packet is unreasonably low. Someone is probably trying funny business. Ignoring message.'
return return
#Let us check to make sure the stream number is correct (thus preventing an individual from sending broadcasts out on the wrong streams or all streams).
broadcastVersion, broadcastVersionLength = decodeVarint(data[readPosition:readPosition+10])
if broadcastVersion >= 2:
streamNumber = decodeVarint(data[readPosition+broadcastVersionLength:readPosition+broadcastVersionLength+10])
if streamNumber != self.streamNumber:
print 'The stream number encoded in this broadcast message (' + str(streamNumber) + ') does not match the stream number on which it was received. Ignoring it.'
return
inventoryLock.acquire() inventoryLock.acquire()
self.inventoryHash = calculateInventoryHash(data) self.inventoryHash = calculateInventoryHash(data)
if self.inventoryHash in inventory: if self.inventoryHash in inventory:
@ -577,10 +588,11 @@ class receiveDataThread(QThread):
#A broadcast message has a valid time and POW and requires processing. The recbroadcast function calls this one. #A broadcast message has a valid time and POW and requires processing. The recbroadcast function calls this one.
def processbroadcast(self,readPosition,data): def processbroadcast(self,readPosition,data):
broadcastVersion, broadcastVersionLength = decodeVarint(data[readPosition:readPosition+9]) broadcastVersion, broadcastVersionLength = decodeVarint(data[readPosition:readPosition+9])
if broadcastVersion <> 1:
#Cannot decode incoming broadcast versions higher than 1. Assuming the sender isn\' being silly, you should upgrade Bitmessage because this message shall be ignored.
return
readPosition += broadcastVersionLength readPosition += broadcastVersionLength
if broadcastVersion < 1 or broadcastVersion > 2:
#Cannot decode incoming broadcast versions higher than 2. Assuming the sender isn\' being silly, you should upgrade Bitmessage because this message shall be ignored.
return
if broadcastVersion == 1:
beginningOfPubkeyPosition = readPosition #used when we add the pubkey to our pubkey table beginningOfPubkeyPosition = readPosition #used when we add the pubkey to our pubkey table
sendersAddressVersion, sendersAddressVersionLength = decodeVarint(data[readPosition:readPosition+9]) sendersAddressVersion, sendersAddressVersionLength = decodeVarint(data[readPosition:readPosition+9])
if sendersAddressVersion <= 1 or sendersAddressVersion >=3: if sendersAddressVersion <= 1 or sendersAddressVersion >=3:
@ -589,8 +601,6 @@ class receiveDataThread(QThread):
readPosition += sendersAddressVersionLength readPosition += sendersAddressVersionLength
if sendersAddressVersion == 2: if sendersAddressVersion == 2:
sendersStream, sendersStreamLength = decodeVarint(data[readPosition:readPosition+9]) sendersStream, sendersStreamLength = decodeVarint(data[readPosition:readPosition+9])
if sendersStream <= 0 or sendersStream <> self.streamNumber:
return
readPosition += sendersStreamLength readPosition += sendersStreamLength
behaviorBitfield = data[readPosition:readPosition+4] behaviorBitfield = data[readPosition:readPosition+4]
readPosition += 4 readPosition += 4
@ -692,6 +702,135 @@ class receiveDataThread(QThread):
printLock.acquire() printLock.acquire()
print 'Time spent processing this interesting broadcast:', time.time()- self.messageProcessingStartTime print 'Time spent processing this interesting broadcast:', time.time()- self.messageProcessingStartTime
printLock.release() printLock.release()
if broadcastVersion == 2:
cleartextStreamNumber, cleartextStreamNumberLength = decodeVarint(data[readPosition:readPosition+10])
readPosition += streamNumberLength
initialDecryptionSuccessful = False
for key, cryptorObject in MyECSubscriptionCryptorObjects.items():
try:
decryptedData = cryptorObject.decrypt(data[readPosition:])
toRipe = key #This is the RIPE hash of the sender's pubkey. We need this below to compare to the RIPE hash of the sender's address to verify that it was encrypted by with their key rather than some other key.
initialDecryptionSuccessful = True
print 'EC decryption successful using key associated with ripe hash:', key.encode('hex')
break
except Exception, err:
pass
#print 'cryptorObject.decrypt Exception:', err
if not initialDecryptionSuccessful:
#This is not a message bound for me.
printLock.acquire()
print 'Length of time program spent failing to decrypt this broadcast:', time.time()- self.messageProcessingStartTime, 'seconds.'
printLock.release()
else:
#This is a broadcast I have decrypted and thus am interested in.
signedBroadcastVersion, readPosition = decodeVarint(decryptedData[:10])
beginningOfPubkeyPosition = readPosition #used when we add the pubkey to our pubkey table
sendersAddressVersion, sendersAddressVersionLength = decodeVarint(decryptedData[readPosition:readPosition+9])
if sendersAddressVersion < 2 or sendersAddressVersion > 3:
print 'Cannot decode senderAddressVersion other than 2 or 3. Assuming the sender isn\' being silly, you should upgrade Bitmessage because this message shall be ignored.'
return
readPosition += sendersAddressVersionLength
sendersStream, sendersStreamLength = decodeVarint(decryptedData[readPosition:readPosition+9])
if sendersStream != cleartextStreamNumber:
print 'The stream number outside of the encryption on which the POW was completed doesn\'t match the stream number inside the encryption. Ignoring broadcast.'
return
readPosition += sendersStreamLength
behaviorBitfield = decryptedData[readPosition:readPosition+4]
readPosition += 4
sendersPubSigningKey = '\x04' + decryptedData[readPosition:readPosition+64]
readPosition += 64
sendersPubEncryptionKey = '\x04' + decryptedData[readPosition:readPosition+64]
readPosition += 64
if sendersAddressVersion >= 3:
requiredAverageProofOfWorkNonceTrialsPerByte, varintLength = decodeVarint(decryptedData[readPosition:readPosition+10])
readPosition += varintLength
print 'sender\'s requiredAverageProofOfWorkNonceTrialsPerByte is', requiredAverageProofOfWorkNonceTrialsPerByte
requiredPayloadLengthExtraBytes, varintLength = decodeVarint(decryptedData[readPosition:readPosition+10])
readPosition += varintLength
print 'sender\'s requiredPayloadLengthExtraBytes is', requiredPayloadLengthExtraBytes
endOfPubkeyPosition = readPosition
sha = hashlib.new('sha512')
sha.update(sendersPubSigningKey+sendersPubEncryptionKey)
ripe = hashlib.new('ripemd160')
ripe.update(sha.digest())
messageEncodingType, messageEncodingTypeLength = decodeVarint(decryptedData[readPosition:readPosition+9])
if messageEncodingType == 0:
return
readPosition += messageEncodingTypeLength
messageLength, messageLengthLength = decodeVarint(decryptedData[readPosition:readPosition+9])
readPosition += messageLengthLength
message = decryptedData[readPosition:readPosition+messageLength]
readPosition += messageLength
readPositionAtBottomOfMessage = readPosition
signatureLength, signatureLengthLength = decodeVarint(decryptedData[readPosition:readPosition+9])
readPosition += signatureLengthLength
signature = decryptedData[readPosition:readPosition+signatureLength]
try:
highlevelcrypto.verify(decryptedData[:readPositionAtBottomOfMessage],signature,sendersPubSigningKey.encode('hex'))
print 'ECDSA verify passed'
except Exception, err:
print 'ECDSA verify failed', err
return
#verify passed
#Let's store the public key in case we want to reply to this person.
t = (ripe.digest(),'\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF'+'\xFF\xFF\xFF\xFF'+decryptedData[beginningOfPubkeyPosition:endOfPubkeyPosition],int(time.time()),'yes')
sqlLock.acquire()
sqlSubmitQueue.put('''INSERT INTO pubkeys VALUES (?,?,?,?)''')
sqlSubmitQueue.put(t)
sqlReturnQueue.get()
sqlSubmitQueue.put('commit')
sqlLock.release()
workerQueue.put(('newpubkey',(sendersAddressVersion,sendersStream,ripe.digest()))) #This will check to see whether we happen to be awaiting this pubkey in order to send a message. If we are, it will do the POW and send it.
fromAddress = encodeAddress(sendersAddressVersion,sendersStream,ripe.digest())
printLock.acquire()
print 'fromAddress:', fromAddress
printLock.release()
if messageEncodingType == 2:
bodyPositionIndex = string.find(message,'\nBody:')
if bodyPositionIndex > 1:
subject = message[8:bodyPositionIndex]
body = message[bodyPositionIndex+6:]
else:
subject = ''
body = message
elif messageEncodingType == 1:
body = message
subject = ''
elif messageEncodingType == 0:
print 'messageEncodingType == 0. Doing nothing with the message.'
else:
body = 'Unknown encoding type.\n\n' + repr(message)
subject = ''
toAddress = '[Broadcast subscribers]'
if messageEncodingType <> 0:
sqlLock.acquire()
t = (self.inventoryHash,toAddress,fromAddress,subject,int(time.time()),body,'inbox',messageEncodingType,0)
sqlSubmitQueue.put('''INSERT INTO inbox VALUES (?,?,?,?,?,?,?,?,?)''')
sqlSubmitQueue.put(t)
sqlReturnQueue.get()
sqlSubmitQueue.put('commit')
sqlLock.release()
self.emit(SIGNAL("displayNewInboxMessage(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"),self.inventoryHash,toAddress,fromAddress,subject,body)
#If we are behaving as an API then we might need to run an outside command to let some program know that a new message has arrived.
if safeConfigGetBoolean('bitmessagesettings','apienabled'):
try:
apiNotifyPath = config.get('bitmessagesettings','apinotifypath')
except:
apiNotifyPath = ''
if apiNotifyPath != '':
call([apiNotifyPath, "newBroadcast"])
#Display timing data
printLock.acquire()
print 'Time spent processing this interesting broadcast:', time.time()- self.messageProcessingStartTime
printLock.release()
#We have received a msg message. #We have received a msg message.
@ -791,7 +930,7 @@ class receiveDataThread(QThread):
#This is not an acknowledgement bound for me. See if it is a message bound for me by trying to decrypt it with my private keys. #This is not an acknowledgement bound for me. See if it is a message bound for me by trying to decrypt it with my private keys.
for key, cryptorObject in myECCryptorObjects.items(): for key, cryptorObject in myECCryptorObjects.items():
try: try:
unencryptedData = cryptorObject.decrypt(encryptedData[readPosition:]) decryptedData = cryptorObject.decrypt(encryptedData[readPosition:])
toRipe = key #This is the RIPE hash of my pubkeys. We need this below to compare to the destination_ripe included in the encrypted data. toRipe = key #This is the RIPE hash of my pubkeys. We need this below to compare to the destination_ripe included in the encrypted data.
initialDecryptionSuccessful = True initialDecryptionSuccessful = True
print 'EC decryption successful using key associated with ripe hash:', key.encode('hex') print 'EC decryption successful using key associated with ripe hash:', key.encode('hex')
@ -806,13 +945,22 @@ class receiveDataThread(QThread):
printLock.release() printLock.release()
else: else:
#This is a message bound for me. #This is a message bound for me.
#If this message is bound for one of my version 3 addresses (or higher), then we must check to make sure it meets our demanded proof of work requirement.
toAddress = myAddressesByHash[toRipe] #Look up my address based on the RIPE hash.
if decodeAddress(toAddress)[1] >= 3:#If the toAddress version number is 3 or higher:
requiredNonceTrialsPerByte = config.getint(toAddress,'noncetrialsperbyte')
requiredPayloadLengthExtraBytes = config.getint(toAddress,'payloadlengthextrabytes')
if not self.isProofOfWorkSufficient(encryptedData,requiredNonceTrialsPerByte,requiredPayloadLengthExtraBytes):
print 'Proof of work in msg message insufficient only because it does not meet our higher requirement.'
return
readPosition = 0 readPosition = 0
messageVersion, messageVersionLength = decodeVarint(unencryptedData[readPosition:readPosition+10]) messageVersion, messageVersionLength = decodeVarint(decryptedData[readPosition:readPosition+10])
readPosition += messageVersionLength readPosition += messageVersionLength
if messageVersion != 1: if messageVersion != 1:
print 'Cannot understand message versions other than one. Ignoring message.' print 'Cannot understand message versions other than one. Ignoring message.'
return return
sendersAddressVersionNumber, sendersAddressVersionNumberLength = decodeVarint(unencryptedData[readPosition:readPosition+10]) sendersAddressVersionNumber, sendersAddressVersionNumberLength = decodeVarint(decryptedData[readPosition:readPosition+10])
readPosition += sendersAddressVersionNumberLength readPosition += sendersAddressVersionNumberLength
if sendersAddressVersionNumber == 0: if sendersAddressVersionNumber == 0:
print 'Cannot understand sendersAddressVersionNumber = 0. Ignoring message.' print 'Cannot understand sendersAddressVersionNumber = 0. Ignoring message.'
@ -820,54 +968,54 @@ class receiveDataThread(QThread):
if sendersAddressVersionNumber >= 4: 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(decryptedData) < 170:
print 'Length of the unencrypted data is unreasonably short. Sanity check failed. Ignoring message.' print 'Length of the unencrypted data is unreasonably short. Sanity check failed. Ignoring message.'
return return
sendersStreamNumber, sendersStreamNumberLength = decodeVarint(unencryptedData[readPosition:readPosition+10]) sendersStreamNumber, sendersStreamNumberLength = decodeVarint(decryptedData[readPosition:readPosition+10])
if sendersStreamNumber == 0: if sendersStreamNumber == 0:
print 'sender\'s stream number is 0. Ignoring message.' print 'sender\'s stream number is 0. Ignoring message.'
return return
readPosition += sendersStreamNumberLength readPosition += sendersStreamNumberLength
behaviorBitfield = unencryptedData[readPosition:readPosition+4] behaviorBitfield = decryptedData[readPosition:readPosition+4]
readPosition += 4 readPosition += 4
pubSigningKey = '\x04' + unencryptedData[readPosition:readPosition+64] pubSigningKey = '\x04' + decryptedData[readPosition:readPosition+64]
readPosition += 64 readPosition += 64
pubEncryptionKey = '\x04' + unencryptedData[readPosition:readPosition+64] pubEncryptionKey = '\x04' + decryptedData[readPosition:readPosition+64]
readPosition += 64 readPosition += 64
if sendersAddressVersionNumber >= 3: if sendersAddressVersionNumber >= 3:
requiredAverageProofOfWorkNonceTrialsPerByte, varintLength = decodeVarint(unencryptedData[readPosition:readPosition+10]) requiredAverageProofOfWorkNonceTrialsPerByte, varintLength = decodeVarint(decryptedData[readPosition:readPosition+10])
readPosition += varintLength readPosition += varintLength
print 'sender\'s requiredAverageProofOfWorkNonceTrialsPerByte is', requiredAverageProofOfWorkNonceTrialsPerByte print 'sender\'s requiredAverageProofOfWorkNonceTrialsPerByte is', requiredAverageProofOfWorkNonceTrialsPerByte
requiredPayloadLengthExtraBytes, varintLength = decodeVarint(unencryptedData[readPosition:readPosition+10]) requiredPayloadLengthExtraBytes, varintLength = decodeVarint(decryptedData[readPosition:readPosition+10])
readPosition += varintLength readPosition += varintLength
print 'sender\'s requiredPayloadLengthExtraBytes is', requiredPayloadLengthExtraBytes 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 != decryptedData[readPosition:readPosition+20]:
printLock.acquire() printLock.acquire()
print 'The original sender of this message did not send it to you. Someone is attempting a Surreptitious Forwarding Attack.' print 'The original sender of this message did not send it to you. Someone is attempting a Surreptitious Forwarding Attack.'
print 'See: http://world.std.com/~dtd/sign_encrypt/sign_encrypt7.html' print 'See: http://world.std.com/~dtd/sign_encrypt/sign_encrypt7.html'
print 'your toRipe:', toRipe.encode('hex') print 'your toRipe:', toRipe.encode('hex')
print 'embedded destination toRipe:', unencryptedData[readPosition:readPosition+20].encode('hex') print 'embedded destination toRipe:', decryptedData[readPosition:readPosition+20].encode('hex')
printLock.release() printLock.release()
return return
readPosition += 20 readPosition += 20
messageEncodingType, messageEncodingTypeLength = decodeVarint(unencryptedData[readPosition:readPosition+10]) messageEncodingType, messageEncodingTypeLength = decodeVarint(decryptedData[readPosition:readPosition+10])
readPosition += messageEncodingTypeLength readPosition += messageEncodingTypeLength
messageLength, messageLengthLength = decodeVarint(unencryptedData[readPosition:readPosition+10]) messageLength, messageLengthLength = decodeVarint(decryptedData[readPosition:readPosition+10])
readPosition += messageLengthLength readPosition += messageLengthLength
message = unencryptedData[readPosition:readPosition+messageLength] message = decryptedData[readPosition:readPosition+messageLength]
#print 'First 150 characters of message:', repr(message[:150]) #print 'First 150 characters of message:', repr(message[:150])
readPosition += messageLength readPosition += messageLength
ackLength, ackLengthLength = decodeVarint(unencryptedData[readPosition:readPosition+10]) ackLength, ackLengthLength = decodeVarint(decryptedData[readPosition:readPosition+10])
readPosition += ackLengthLength readPosition += ackLengthLength
ackData = unencryptedData[readPosition:readPosition+ackLength] ackData = decryptedData[readPosition:readPosition+ackLength]
readPosition += ackLength readPosition += ackLength
positionOfBottomOfAckData = readPosition #needed to mark the end of what is covered by the signature positionOfBottomOfAckData = readPosition #needed to mark the end of what is covered by the signature
signatureLength, signatureLengthLength = decodeVarint(unencryptedData[readPosition:readPosition+10]) signatureLength, signatureLengthLength = decodeVarint(decryptedData[readPosition:readPosition+10])
readPosition += signatureLengthLength readPosition += signatureLengthLength
signature = unencryptedData[readPosition:readPosition+signatureLength] signature = decryptedData[readPosition:readPosition+signatureLength]
try: try:
highlevelcrypto.verify(unencryptedData[:positionOfBottomOfAckData],signature,pubSigningKey.encode('hex')) highlevelcrypto.verify(decryptedData[:positionOfBottomOfAckData],signature,pubSigningKey.encode('hex'))
print 'ECDSA verify passed' print 'ECDSA verify passed'
except Exception, err: except Exception, err:
print 'ECDSA verify failed', err print 'ECDSA verify failed', err
@ -881,8 +1029,7 @@ class receiveDataThread(QThread):
ripe = hashlib.new('ripemd160') ripe = hashlib.new('ripemd160')
ripe.update(sha.digest()) ripe.update(sha.digest())
#Let's store the public key in case we want to reply to this person. #Let's store the public key in case we want to reply to this person.
#We don't have the correct nonce or time (which would let us send out a pubkey message) so we'll just fill it with 1's. We won't be able to send this pubkey to others (without doing the proof of work ourselves, which this program is programmed to not do.) t = (ripe.digest(),'\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF'+'\xFF\xFF\xFF\xFF'+decryptedData[messageVersionLength:endOfThePublicKeyPosition],int(time.time()),'yes')
t = (ripe.digest(),'\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF'+'\xFF\xFF\xFF\xFF'+unencryptedData[messageVersionLength:endOfThePublicKeyPosition],int(time.time()),'yes')
sqlLock.acquire() sqlLock.acquire()
sqlSubmitQueue.put('''INSERT INTO pubkeys VALUES (?,?,?,?)''') sqlSubmitQueue.put('''INSERT INTO pubkeys VALUES (?,?,?,?)''')
sqlSubmitQueue.put(t) sqlSubmitQueue.put(t)
@ -925,7 +1072,6 @@ class receiveDataThread(QThread):
print 'fromAddress:', fromAddress print 'fromAddress:', fromAddress
print 'First 150 characters of message:', repr(message[:150]) print 'First 150 characters of message:', repr(message[:150])
toAddress = myAddressesByHash[toRipe] #Look up my address based on the RIPE hash.
toLabel = config.get(toAddress, 'label') toLabel = config.get(toAddress, 'label')
if toLabel == '': if toLabel == '':
toLabel = addressInKeysFile toLabel = addressInKeysFile
@ -2068,6 +2214,32 @@ def safeConfigGetBoolean(section,field):
except: except:
return False return False
#Does an EC point multiplication; turns a private key into a public key.
def pointMult(secret):
#ctx = OpenSSL.BN_CTX_new() #This value proved to cause Seg Faults on Linux. It turns out that it really didn't speed up EC_POINT_mul anyway.
k = OpenSSL.EC_KEY_new_by_curve_name(OpenSSL.get_curve('secp256k1'))
priv_key = OpenSSL.BN_bin2bn(secret, 32, 0)
group = OpenSSL.EC_KEY_get0_group(k)
pub_key = OpenSSL.EC_POINT_new(group)
OpenSSL.EC_POINT_mul(group, pub_key, priv_key, None, None, None)
OpenSSL.EC_KEY_set_private_key(k, priv_key)
OpenSSL.EC_KEY_set_public_key(k, pub_key)
#print 'priv_key',priv_key
#print 'pub_key',pub_key
size = OpenSSL.i2o_ECPublicKey(k, 0)
mb = ctypes.create_string_buffer(size)
OpenSSL.i2o_ECPublicKey(k, ctypes.byref(ctypes.pointer(mb)))
#print 'mb.raw', mb.raw.encode('hex'), 'length:', len(mb.raw)
#print 'mb.raw', mb.raw, 'length:', len(mb.raw)
OpenSSL.EC_POINT_free(pub_key)
#OpenSSL.BN_CTX_free(ctx)
OpenSSL.BN_free(priv_key)
OpenSSL.EC_KEY_free(k)
return mb.raw
def lookupAppdataFolder(): def lookupAppdataFolder():
APPNAME = "PyBitmessage" APPNAME = "PyBitmessage"
from os import path, environ from os import path, environ
@ -2577,7 +2749,7 @@ class singleWorker(QThread):
for row in queryreturn: for row in queryreturn:
fromaddress, subject, body, ackdata = row fromaddress, subject, body, ackdata = row
status,addressVersionNumber,streamNumber,ripe = decodeAddress(fromaddress) status,addressVersionNumber,streamNumber,ripe = decodeAddress(fromaddress)
if addressVersionNumber == 2: if addressVersionNumber == 2:#todo: and time is less than some date in the future:
#We need to convert our private keys to public keys in order to include them. #We need to convert our private keys to public keys in order to include them.
try: try:
privSigningKeyBase58 = config.get(fromaddress, 'privsigningkey') privSigningKeyBase58 = config.get(fromaddress, 'privsigningkey')
@ -2629,6 +2801,76 @@ class singleWorker(QThread):
self.emit(SIGNAL("updateSentItemStatusByAckdata(PyQt_PyObject,PyQt_PyObject)"),ackdata,'Broadcast sent on '+unicode(strftime(config.get('bitmessagesettings', 'timeformat'),localtime(int(time.time()))),'utf-8')) self.emit(SIGNAL("updateSentItemStatusByAckdata(PyQt_PyObject,PyQt_PyObject)"),ackdata,'Broadcast sent on '+unicode(strftime(config.get('bitmessagesettings', 'timeformat'),localtime(int(time.time()))),'utf-8'))
#Update the status of the message in the 'sent' table to have a 'broadcastsent' status
sqlLock.acquire()
t = ('broadcastsent',int(time.time()),fromaddress, subject, body,'broadcastpending')
sqlSubmitQueue.put('UPDATE sent SET status=?, lastactiontime=? WHERE fromaddress=? AND subject=? AND message=? AND status=?')
sqlSubmitQueue.put(t)
queryreturn = sqlReturnQueue.get()
sqlSubmitQueue.put('commit')
sqlLock.release()
elif addressVersionNumber == 3:#todo: or (addressVersionNumber == 2 and time is greater than than some date in the future):
#We need to convert our private keys to public keys in order to include them.
try:
privSigningKeyBase58 = config.get(fromaddress, 'privsigningkey')
privEncryptionKeyBase58 = config.get(fromaddress, 'privencryptionkey')
except:
self.emit(SIGNAL("updateSentItemStatusByAckdata(PyQt_PyObject,PyQt_PyObject)"),ackdata,'Error! Could not find sender address (your address) in the keys.dat file.')
continue
privSigningKeyHex = decodeWalletImportFormat(privSigningKeyBase58).encode('hex')
privEncryptionKeyHex = decodeWalletImportFormat(privEncryptionKeyBase58).encode('hex')
pubSigningKey = highlevelcrypto.privToPub(privSigningKeyHex).decode('hex') #At this time these pubkeys are 65 bytes long because they include the encoding byte which we won't be sending in the broadcast message.
pubEncryptionKey = highlevelcrypto.privToPub(privEncryptionKeyHex).decode('hex')
payload = pack('>I',(int(time.time())+random.randrange(-300, 300)))#the current time plus or minus five minutes
payload += encodeVarint(2) #broadcast version
payload += encodeVarint(streamNumber)
dataToEncrypt = encodeVarint(2) #broadcast version
dataToEncrypt += encodeVarint(addressVersionNumber)
dataToEncrypt += encodeVarint(streamNumber)
dataToEncrypt += '\x00\x00\x00\x01' #behavior bitfield
dataToEncrypt += pubSigningKey[1:]
dataToEncrypt += pubEncryptionKey[1:]
if addressVersionNumber >= 3:
dataToEncrypt += encodeVarint(config.getint(fromaddress,'noncetrialsperbyte'))
dataToEncrypt += encodeVarint(config.getint(fromaddress,'payloadlengthextrabytes'))
dataToEncrypt += '\x02' #message encoding type
dataToEncrypt += encodeVarint(len('Subject:' + subject + '\n' + 'Body:' + body)) #Type 2 is simple UTF-8 message encoding.
dataToEncrypt += 'Subject:' + subject + '\n' + 'Body:' + body
signature = highlevelcrypto.sign(payload,privSigningKeyHex)
dataToEncrypt += encodeVarint(len(signature))
dataToEncrypt += signature
print 'The string that we will hash to make the privEncryptionKey is', (encodeVarint(addressVersionNumber)+encodeVarint(streamNumber)+ripe).encode('hex')
privEncryptionKey = hashlib.sha512(encodeVarint(addressVersionNumber)+encodeVarint(streamNumber)+ripe).digest()[:32]
pubEncryptionKey = pointMult(privEncryptionKey)
print 'length of the pub encrypion key is', len(pubEncryptionKey), 'which should be 65.'
payload += highlevelcrypto.encrypt(dataToEncrypt,pubEncryptionKey.encode('hex'))
nonce = 0
trialValue = 99999999999999999999
target = 2**64 / ((len(payload)+networkDefaultPayloadLengthExtraBytes+8) * networkDefaultProofOfWorkNonceTrialsPerByte)
print '(For broadcast message) Doing proof of work...'
self.emit(SIGNAL("updateSentItemStatusByAckdata(PyQt_PyObject,PyQt_PyObject)"),ackdata,'Doing work necessary to send broadcast...')
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 broadcast message) Found proof of work', trialValue, 'Nonce:', nonce
payload = pack('>Q',nonce) + payload
inventoryHash = calculateInventoryHash(payload)
objectType = 'broadcast'
inventory[inventoryHash] = (objectType, streamNumber, payload, int(time.time()))
print 'sending inv (within sendBroadcast function)'
broadcastToSendDataQueues((streamNumber, 'sendinv', inventoryHash))
self.emit(SIGNAL("updateSentItemStatusByAckdata(PyQt_PyObject,PyQt_PyObject)"),ackdata,'Broadcast sent on '+unicode(strftime(config.get('bitmessagesettings', 'timeformat'),localtime(int(time.time()))),'utf-8'))
#Update the status of the message in the 'sent' table to have a 'broadcastsent' status #Update the status of the message in the 'sent' table to have a 'broadcastsent' status
sqlLock.acquire() sqlLock.acquire()
t = ('broadcastsent',int(time.time()),fromaddress, subject, body,'broadcastpending') t = ('broadcastsent',int(time.time()),fromaddress, subject, body,'broadcastpending')
@ -2792,8 +3034,8 @@ class singleWorker(QThread):
print '(For msg message) Doing proof of work. Target:', target print '(For msg message) Doing proof of work. Target:', target
print 'Using requiredAverageProofOfWorkNonceTrialsPerByte', requiredAverageProofOfWorkNonceTrialsPerByte print 'Using requiredAverageProofOfWorkNonceTrialsPerByte', requiredAverageProofOfWorkNonceTrialsPerByte
print 'Using requiredPayloadLengthExtraBytes =', requiredPayloadLengthExtraBytes print 'Using requiredPayloadLengthExtraBytes =', requiredPayloadLengthExtraBytes
print 'The required total difficulty is', requiredAverageProofOfWorkNonceTrialsPerByte/networkDefaultProofOfWorkNonceTrialsPerByte print 'The required total difficulty is', float(requiredAverageProofOfWorkNonceTrialsPerByte)/networkDefaultProofOfWorkNonceTrialsPerByte
print 'The required small message difficulty is', requiredPayloadLengthExtraBytes/networkDefaultPayloadLengthExtraBytes print 'The required small message difficulty is', float(requiredPayloadLengthExtraBytes)/networkDefaultPayloadLengthExtraBytes
printLock.release() printLock.release()
powStartTime = time.time() powStartTime = time.time()
initialHash = hashlib.sha512(payload).digest() initialHash = hashlib.sha512(payload).digest()
@ -2915,11 +3157,11 @@ class addressGenerator(QThread):
startTime = time.time() startTime = time.time()
numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix = 0 numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix = 0
potentialPrivSigningKey = OpenSSL.rand(32) potentialPrivSigningKey = OpenSSL.rand(32)
potentialPubSigningKey = self.pointMult(potentialPrivSigningKey) potentialPubSigningKey = pointMult(potentialPrivSigningKey)
while True: while True:
numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix += 1 numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix += 1
potentialPrivEncryptionKey = OpenSSL.rand(32) potentialPrivEncryptionKey = OpenSSL.rand(32)
potentialPubEncryptionKey = self.pointMult(potentialPrivEncryptionKey) potentialPubEncryptionKey = pointMult(potentialPrivEncryptionKey)
#print 'potentialPubSigningKey', potentialPubSigningKey.encode('hex') #print 'potentialPubSigningKey', potentialPubSigningKey.encode('hex')
#print 'potentialPubEncryptionKey', potentialPubEncryptionKey.encode('hex') #print 'potentialPubEncryptionKey', potentialPubEncryptionKey.encode('hex')
ripe = hashlib.new('ripemd160') ripe = hashlib.new('ripemd160')
@ -2986,8 +3228,8 @@ class addressGenerator(QThread):
numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix += 1 numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix += 1
potentialPrivSigningKey = hashlib.sha512(self.deterministicPassphrase + encodeVarint(signingKeyNonce)).digest()[:32] potentialPrivSigningKey = hashlib.sha512(self.deterministicPassphrase + encodeVarint(signingKeyNonce)).digest()[:32]
potentialPrivEncryptionKey = hashlib.sha512(self.deterministicPassphrase + encodeVarint(encryptionKeyNonce)).digest()[:32] potentialPrivEncryptionKey = hashlib.sha512(self.deterministicPassphrase + encodeVarint(encryptionKeyNonce)).digest()[:32]
potentialPubSigningKey = self.pointMult(potentialPrivSigningKey) potentialPubSigningKey = pointMult(potentialPrivSigningKey)
potentialPubEncryptionKey = self.pointMult(potentialPrivEncryptionKey) potentialPubEncryptionKey = pointMult(potentialPrivEncryptionKey)
#print 'potentialPubSigningKey', potentialPubSigningKey.encode('hex') #print 'potentialPubSigningKey', potentialPubSigningKey.encode('hex')
#print 'potentialPubEncryptionKey', potentialPubEncryptionKey.encode('hex') #print 'potentialPubEncryptionKey', potentialPubEncryptionKey.encode('hex')
signingKeyNonce += 2 signingKeyNonce += 2
@ -3043,31 +3285,6 @@ class addressGenerator(QThread):
self.emit(SIGNAL("updateStatusBar(PyQt_PyObject)"),'Done generating address') self.emit(SIGNAL("updateStatusBar(PyQt_PyObject)"),'Done generating address')
reloadMyAddressHashes() reloadMyAddressHashes()
#Does an EC point multiplication; turns a private key into a public key.
def pointMult(self,secret):
#ctx = OpenSSL.BN_CTX_new() #This value proved to cause Seg Faults on Linux. It turns out that it really didn't speed up EC_POINT_mul anyway.
k = OpenSSL.EC_KEY_new_by_curve_name(OpenSSL.get_curve('secp256k1'))
priv_key = OpenSSL.BN_bin2bn(secret, 32, 0)
group = OpenSSL.EC_KEY_get0_group(k)
pub_key = OpenSSL.EC_POINT_new(group)
OpenSSL.EC_POINT_mul(group, pub_key, priv_key, None, None, None)
OpenSSL.EC_KEY_set_private_key(k, priv_key)
OpenSSL.EC_KEY_set_public_key(k, pub_key)
#print 'priv_key',priv_key
#print 'pub_key',pub_key
size = OpenSSL.i2o_ECPublicKey(k, 0)
mb = ctypes.create_string_buffer(size)
OpenSSL.i2o_ECPublicKey(k, ctypes.byref(ctypes.pointer(mb)))
#print 'mb.raw', mb.raw.encode('hex'), 'length:', len(mb.raw)
#print 'mb.raw', mb.raw, 'length:', len(mb.raw)
OpenSSL.EC_POINT_free(pub_key)
#OpenSSL.BN_CTX_free(ctx)
OpenSSL.BN_free(priv_key)
OpenSSL.EC_KEY_free(k)
return mb.raw
#This is one of several classes that constitute the API #This is one of several classes that constitute the API
#This class was written by Vaibhav Bhatia. Modified by Jonathan Warren (Atheros). #This class was written by Vaibhav Bhatia. Modified by Jonathan Warren (Atheros).
@ -4694,7 +4911,9 @@ class MyForm(QtGui.QMainWindow):
config.set('bitmessagesettings', 'socksport', str(self.settingsDialogInstance.ui.lineEditSocksPort.text())) config.set('bitmessagesettings', 'socksport', str(self.settingsDialogInstance.ui.lineEditSocksPort.text()))
config.set('bitmessagesettings', 'socksusername', str(self.settingsDialogInstance.ui.lineEditSocksUsername.text())) config.set('bitmessagesettings', 'socksusername', str(self.settingsDialogInstance.ui.lineEditSocksUsername.text()))
config.set('bitmessagesettings', 'sockspassword', str(self.settingsDialogInstance.ui.lineEditSocksPassword.text())) config.set('bitmessagesettings', 'sockspassword', str(self.settingsDialogInstance.ui.lineEditSocksPassword.text()))
if float(self.settingsDialogInstance.ui.lineEditTotalDifficulty.text()) >= 1:
config.set('bitmessagesettings', 'defaultnoncetrialsperbyte',str(int(float(self.settingsDialogInstance.ui.lineEditTotalDifficulty.text())*networkDefaultProofOfWorkNonceTrialsPerByte))) config.set('bitmessagesettings', 'defaultnoncetrialsperbyte',str(int(float(self.settingsDialogInstance.ui.lineEditTotalDifficulty.text())*networkDefaultProofOfWorkNonceTrialsPerByte)))
if float(self.settingsDialogInstance.ui.lineEditSmallMessageDifficulty.text()) >= 1:
config.set('bitmessagesettings', 'defaultpayloadlengthextrabytes',str(int(float(self.settingsDialogInstance.ui.lineEditSmallMessageDifficulty.text())*networkDefaultPayloadLengthExtraBytes))) config.set('bitmessagesettings', 'defaultpayloadlengthextrabytes',str(int(float(self.settingsDialogInstance.ui.lineEditSmallMessageDifficulty.text())*networkDefaultPayloadLengthExtraBytes)))
with open(appdata + 'keys.dat', 'wb') as configfile: with open(appdata + 'keys.dat', 'wb') as configfile:
@ -5327,7 +5546,13 @@ class MyForm(QtGui.QMainWindow):
for row in queryreturn: for row in queryreturn:
address, = row address, = row
status,addressVersionNumber,streamNumber,hash = decodeAddress(address) status,addressVersionNumber,streamNumber,hash = decodeAddress(address)
if addressVersionNumber == 2:
broadcastSendersForWhichImWatching[hash] = 0 broadcastSendersForWhichImWatching[hash] = 0
#Now, for all addresses, even version 2 addresses, we should create Cryptor objects in a dictionary which we will use to attempt to decrypt encrypted broadcast messages.
print '(Within reloadBroadcastSendersForWhichImWatching) The string that we will hash to make the privEncryptionKey is', (encodeVarint(addressVersionNumber)+encodeVarint(streamNumber)+hash).encode('hex')
privEncryptionKey = hashlib.sha512(encodeVarint(addressVersionNumber)+encodeVarint(streamNumber)+hash).digest()[:32]
print 'length of privEncryptionKey is', len(privEncryptionKey)
MyECSubscriptionCryptorObjects[hash] = highlevelcrypto.makeCryptor(privEncryptionKey.encode('hex'))
#In order for the time columns on the Inbox and Sent tabs to be sorted correctly (rather than alphabetically), we need to overload the < operator and use this class instead of QTableWidgetItem. #In order for the time columns on the Inbox and Sent tabs to be sorted correctly (rather than alphabetically), we need to overload the < operator and use this class instead of QTableWidgetItem.
class myTableWidgetItem(QTableWidgetItem): class myTableWidgetItem(QTableWidgetItem):
@ -5339,6 +5564,7 @@ selfInitiatedConnections = {} #This is a list of current connections (the thread
alreadyAttemptedConnectionsList = {} #This is a list of nodes to which we have already attempted a connection alreadyAttemptedConnectionsList = {} #This is a list of nodes to which we have already attempted a connection
sendDataQueues = [] #each sendData thread puts its queue in this list. sendDataQueues = [] #each sendData thread puts its queue in this list.
myECCryptorObjects = {} myECCryptorObjects = {}
MyECSubscriptionCryptorObjects = {}
myAddressesByHash = {} #The key in this dictionary is the RIPE hash which is encoded in an address and value is the address itself. myAddressesByHash = {} #The key in this dictionary is the RIPE hash which is encoded in an address and value is the address itself.
inventory = {} #of objects (like msg payloads and pubkey payloads) Does not include protocol headers (the first 24 bytes of each packet). inventory = {} #of objects (like msg payloads and pubkey payloads) Does not include protocol headers (the first 24 bytes of each packet).
workerQueue = Queue.Queue() workerQueue = Queue.Queue()

View File

@ -104,16 +104,6 @@ def vacuum():
conn.commit() conn.commit()
print 'done' print 'done'
def temp():
item = '''SELECT name FROM sqlite_master WHERE type='table' AND name='sent';'''
parameters = ''
cur.execute(item, parameters)
output = cur.fetchall()
if output == []:
print 'no table'
else:
print 'table exists'
#takeInboxMessagesOutOfTrash() #takeInboxMessagesOutOfTrash()
#takeSentMessagesOutOfTrash() #takeSentMessagesOutOfTrash()
#markAllInboxMessagesAsUnread() #markAllInboxMessagesAsUnread()
@ -122,7 +112,7 @@ def temp():
#readPubkeys() #readPubkeys()
#readSubscriptions() #readSubscriptions()
#readInventory() #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.
temp()

View File

@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'settings.ui' # Form implementation generated from reading ui file 'settings.ui'
# #
# Created: Thu Apr 25 16:02:49 2013 # Created: Fri Apr 26 13:19:59 2013
# by: PyQt4 UI code generator 4.9.4 # by: PyQt4 UI code generator 4.9.4
# #
# WARNING! All changes made in this file will be lost! # WARNING! All changes made in this file will be lost!
@ -175,7 +175,7 @@ class Ui_settingsDialog(object):
self.gridLayout.addWidget(self.tabWidgetSettings, 0, 0, 1, 1) self.gridLayout.addWidget(self.tabWidgetSettings, 0, 0, 1, 1)
self.retranslateUi(settingsDialog) self.retranslateUi(settingsDialog)
self.tabWidgetSettings.setCurrentIndex(2) self.tabWidgetSettings.setCurrentIndex(0)
QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("accepted()")), settingsDialog.accept) QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("accepted()")), settingsDialog.accept)
QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("rejected()")), settingsDialog.reject) QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("rejected()")), settingsDialog.reject)
QtCore.QObject.connect(self.checkBoxAuthentication, QtCore.SIGNAL(_fromUtf8("toggled(bool)")), self.lineEditSocksUsername.setEnabled) QtCore.QObject.connect(self.checkBoxAuthentication, QtCore.SIGNAL(_fromUtf8("toggled(bool)")), self.lineEditSocksUsername.setEnabled)
@ -216,7 +216,7 @@ class Ui_settingsDialog(object):
self.label_5.setText(QtGui.QApplication.translate("settingsDialog", "Username:", None, QtGui.QApplication.UnicodeUTF8)) self.label_5.setText(QtGui.QApplication.translate("settingsDialog", "Username:", None, QtGui.QApplication.UnicodeUTF8))
self.label_6.setText(QtGui.QApplication.translate("settingsDialog", "Pass:", None, QtGui.QApplication.UnicodeUTF8)) self.label_6.setText(QtGui.QApplication.translate("settingsDialog", "Pass:", None, QtGui.QApplication.UnicodeUTF8))
self.tabWidgetSettings.setTabText(self.tabWidgetSettings.indexOf(self.tabNetworkSettings), QtGui.QApplication.translate("settingsDialog", "Network Settings", None, QtGui.QApplication.UnicodeUTF8)) self.tabWidgetSettings.setTabText(self.tabWidgetSettings.indexOf(self.tabNetworkSettings), QtGui.QApplication.translate("settingsDialog", "Network Settings", None, QtGui.QApplication.UnicodeUTF8))
self.label_8.setText(QtGui.QApplication.translate("settingsDialog", "When someone sends you a message, their computer must first complete some work. The difficulty of this work, by default, is 1. You may raise this default for new addresses you create by changing the values here. Any new addresses you create will require senders to meet the higher difficulty. There is one exeption: if you add a friend or aquaintance to your address book, Bitmessage will automatically notify them when you next send a message that they need only complete the minimum amount of work: difficulty 1. ", None, QtGui.QApplication.UnicodeUTF8)) self.label_8.setText(QtGui.QApplication.translate("settingsDialog", "When someone sends you a message, their computer must first complete some work. The difficulty of this work, by default, is 1. You may raise this default for new addresses you create by changing the values here. Any new addresses you create will require senders to meet the higher difficulty. There is one exception: if you add a friend or aquaintance to your address book, Bitmessage will automatically notify them when you next send a message that they need only complete the minimum amount of work: difficulty 1. ", None, QtGui.QApplication.UnicodeUTF8))
self.label_9.setText(QtGui.QApplication.translate("settingsDialog", "Total difficulty:", None, QtGui.QApplication.UnicodeUTF8)) self.label_9.setText(QtGui.QApplication.translate("settingsDialog", "Total difficulty:", None, QtGui.QApplication.UnicodeUTF8))
self.label_11.setText(QtGui.QApplication.translate("settingsDialog", "Small message difficulty:", None, QtGui.QApplication.UnicodeUTF8)) self.label_11.setText(QtGui.QApplication.translate("settingsDialog", "Small message difficulty:", None, QtGui.QApplication.UnicodeUTF8))
self.label_12.setText(QtGui.QApplication.translate("settingsDialog", "The \'Small message difficulty\' mostly only affects the difficulty of sending small messages. Doubling this value makes it almost twice as difficult to send a small message but doesn\'t really affect large messages.", None, QtGui.QApplication.UnicodeUTF8)) self.label_12.setText(QtGui.QApplication.translate("settingsDialog", "The \'Small message difficulty\' mostly only affects the difficulty of sending small messages. Doubling this value makes it almost twice as difficult to send a small message but doesn\'t really affect large messages.", None, QtGui.QApplication.UnicodeUTF8))

View File

@ -27,7 +27,7 @@
<item row="0" column="0"> <item row="0" column="0">
<widget class="QTabWidget" name="tabWidgetSettings"> <widget class="QTabWidget" name="tabWidgetSettings">
<property name="currentIndex"> <property name="currentIndex">
<number>2</number> <number>0</number>
</property> </property>
<widget class="QWidget" name="tabUserInterface"> <widget class="QWidget" name="tabUserInterface">
<property name="enabled"> <property name="enabled">
@ -266,7 +266,7 @@
<item row="0" column="0" colspan="3"> <item row="0" column="0" colspan="3">
<widget class="QLabel" name="label_8"> <widget class="QLabel" name="label_8">
<property name="text"> <property name="text">
<string>When someone sends you a message, their computer must first complete some work. The difficulty of this work, by default, is 1. You may raise this default for new addresses you create by changing the values here. Any new addresses you create will require senders to meet the higher difficulty. There is one exeption: if you add a friend or aquaintance to your address book, Bitmessage will automatically notify them when you next send a message that they need only complete the minimum amount of work: difficulty 1. </string> <string>When someone sends you a message, their computer must first complete some work. The difficulty of this work, by default, is 1. You may raise this default for new addresses you create by changing the values here. Any new addresses you create will require senders to meet the higher difficulty. There is one exception: if you add a friend or aquaintance to your address book, Bitmessage will automatically notify them when you next send a message that they need only complete the minimum amount of work: difficulty 1. </string>
</property> </property>
<property name="wordWrap"> <property name="wordWrap">
<bool>true</bool> <bool>true</bool>