Merge pull request #727 from Bitmessage/ProtoV3

Proto v3
master
Jonathan Warren 9 years ago
commit 12b4015457

@ -68,23 +68,48 @@ def encodeVarint(integer):
if integer >= 18446744073709551616:
print 'varint cannot be >= 18446744073709551616'
raise SystemExit
class varintDecodeError(Exception):
pass
def decodeVarint(data):
"""
Decodes an encoded varint to an integer and returns it.
Per protocol v3, the encoded value must be encoded with
the minimum amount of data possible or else it is malformed.
Returns a tuple: (theEncodedValue, theSizeOfTheVarintInBytes)
"""
if len(data) == 0:
return (0,0)
firstByte, = unpack('>B',data[0:1])
if firstByte < 253:
# encodes 0 to 252
return (firstByte,1) #the 1 is the length of the varint
if firstByte == 253:
a, = unpack('>H',data[1:3])
return (a,3)
# encodes 253 to 65535
if len(data) < 3:
raise varintDecodeError('The first byte of this varint as an integer is %s but the total length is only %s. It needs to be at least 3.' % (firstByte, len(data)))
encodedValue, = unpack('>H',data[1:3])
if encodedValue < 253:
raise varintDecodeError('This varint does not encode the value with the lowest possible number of bytes.')
return (encodedValue,3)
if firstByte == 254:
a, = unpack('>I',data[1:5])
return (a,5)
# encodes 65536 to 4294967295
if len(data) < 5:
raise varintDecodeError('The first byte of this varint as an integer is %s but the total length is only %s. It needs to be at least 5.' % (firstByte, len(data)))
encodedValue, = unpack('>I',data[1:5])
if encodedValue < 65536:
raise varintDecodeError('This varint does not encode the value with the lowest possible number of bytes.')
return (encodedValue,5)
if firstByte == 255:
a, = unpack('>Q',data[1:9])
return (a,9)
# encodes 4294967296 to 18446744073709551615
if len(data) < 9:
raise varintDecodeError('The first byte of this varint as an integer is %s but the total length is only %s. It needs to be at least 9.' % (firstByte, len(data)))
encodedValue, = unpack('>Q',data[1:9])
if encodedValue < 4294967296:
raise varintDecodeError('This varint does not encode the value with the lowest possible number of bytes.')
return (encodedValue,9)
def calculateInventoryHash(data):
@ -107,23 +132,17 @@ def encodeAddress(version,stream,ripe):
raise Exception("Programming error in encodeAddress: The length of a given ripe hash was not 20.")
ripe = ripe.lstrip('\x00')
a = encodeVarint(version) + encodeVarint(stream) + ripe
storedBinaryData = encodeVarint(version) + encodeVarint(stream) + ripe
# Generate the checksum
sha = hashlib.new('sha512')
sha.update(a)
sha.update(storedBinaryData)
currentHash = sha.digest()
#print 'sha after first hashing: ', sha.hexdigest()
sha = hashlib.new('sha512')
sha.update(currentHash)
#print 'sha after second hashing: ', sha.hexdigest()
checksum = sha.digest()[0:4]
#print 'len(a) = ', len(a)
#print 'checksum = ', checksum.encode('hex')
#print 'len(checksum) = ', len(checksum)
asInt = int(a.encode('hex') + checksum.encode('hex'),16)
#asInt = int(checksum.encode('hex') + a.encode('hex'),16)
# print asInt
asInt = int(storedBinaryData.encode('hex') + checksum.encode('hex'),16)
return 'BM-'+ encodeBase58(asInt)
def decodeAddress(address):
@ -163,7 +182,12 @@ def decodeAddress(address):
#else:
# print 'checksum PASSED'
addressVersionNumber, bytesUsedByVersionNumber = decodeVarint(data[:9])
try:
addressVersionNumber, bytesUsedByVersionNumber = decodeVarint(data[:9])
except varintDecodeError as e:
print e
status = 'varintmalformed'
return status,0,0,""
#print 'addressVersionNumber', addressVersionNumber
#print 'bytesUsedByVersionNumber', bytesUsedByVersionNumber
@ -176,32 +200,42 @@ def decodeAddress(address):
status = 'versiontoohigh'
return status,0,0,""
streamNumber, bytesUsedByStreamNumber = decodeVarint(data[bytesUsedByVersionNumber:])
try:
streamNumber, bytesUsedByStreamNumber = decodeVarint(data[bytesUsedByVersionNumber:])
except varintDecodeError as e:
print e
status = 'varintmalformed'
return status,0,0,""
#print streamNumber
status = 'success'
if addressVersionNumber == 1:
return status,addressVersionNumber,streamNumber,data[-24:-4]
elif addressVersionNumber == 2 or addressVersionNumber == 3:
if len(data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4]) == 19:
return status,addressVersionNumber,streamNumber,'\x00'+data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4]
elif len(data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4]) == 20:
return status,addressVersionNumber,streamNumber,data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4]
elif len(data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4]) == 18:
return status,addressVersionNumber,streamNumber,'\x00\x00'+data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4]
elif len(data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4]) < 18:
embeddedRipeData = data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4]
if len(embeddedRipeData) == 19:
return status,addressVersionNumber,streamNumber,'\x00'+embeddedRipeData
elif len(embeddedRipeData) == 20:
return status,addressVersionNumber,streamNumber,embeddedRipeData
elif len(embeddedRipeData) == 18:
return status,addressVersionNumber,streamNumber,'\x00\x00'+embeddedRipeData
elif len(embeddedRipeData) < 18:
return 'ripetooshort',0,0,""
elif len(data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4]) > 20:
elif len(embeddedRipeData) > 20:
return 'ripetoolong',0,0,""
else:
return 'otherproblem',0,0,""
elif addressVersionNumber == 4:
if len(data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4]) > 20:
embeddedRipeData = data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4]
if embeddedRipeData[0:1] == '\x00':
# In order to enforce address non-malleability, encoded RIPE data must have NULL bytes removed from the front
return 'encodingproblem',0,0,""
elif len(embeddedRipeData) > 20:
return 'ripetoolong',0,0,""
elif len(data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4]) < 4:
elif len(embeddedRipeData) < 4:
return 'ripetooshort',0,0,""
else:
x00string = '\x00' * (20 - len(data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4]))
return status,addressVersionNumber,streamNumber,x00string+data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4]
x00string = '\x00' * (20 - len(embeddedRipeData))
return status,addressVersionNumber,streamNumber,x00string+embeddedRipeData
def addBMIfNotPresent(address):
address = str(address).strip()

@ -17,7 +17,7 @@ import json
import shared
import time
from addresses import decodeAddress,addBMIfNotPresent,decodeVarint,calculateInventoryHash
from addresses import decodeAddress,addBMIfNotPresent,decodeVarint,calculateInventoryHash,varintDecodeError
import helper_inbox
import helper_sent
import hashlib
@ -139,6 +139,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
raise APIError(9, 'Invalid characters in address: ' + address)
if status == 'versiontoohigh':
raise APIError(10, 'Address version number too high (or zero) in address: ' + address)
if status == 'varintmalformed':
raise APIError(26, 'Malformed varint in address: ' + address)
raise APIError(7, 'Could not decode address: ' + address + ' : ' + status)
if addressVersionNumber < 2 or addressVersionNumber > 4:
raise APIError(11, 'The address version number currently must be 2, 3 or 4. Others aren\'t supported. Check the address.')
@ -621,6 +623,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
raise APIError(6, 'The encoding type must be 2 because that is the only one this program currently supports.')
subject = self._decode(subject, "base64")
message = self._decode(message, "base64")
if len(subject + message) > (2 ** 18 - 500):
raise APIError(27, 'Message is too long.')
toAddress = addBMIfNotPresent(toAddress)
fromAddress = addBMIfNotPresent(fromAddress)
status, addressVersionNumber, streamNumber, toRipe = self._verifyAddress(toAddress)
@ -664,7 +668,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
raise APIError(6, 'The encoding type must be 2 because that is the only one this program currently supports.')
subject = self._decode(subject, "base64")
message = self._decode(message, "base64")
if len(subject + message) > (2 ** 18 - 500):
raise APIError(27, 'Message is too long.')
fromAddress = addBMIfNotPresent(fromAddress)
self._verifyAddress(fromAddress)
try:
@ -777,9 +782,10 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
encryptedPayload = pack('>Q', nonce) + encryptedPayload
toStreamNumber = decodeVarint(encryptedPayload[16:26])[0]
inventoryHash = calculateInventoryHash(encryptedPayload)
objectType = 'msg'
objectType = 2
TTL = 2.5 * 24 * 60 * 60
shared.inventory[inventoryHash] = (
objectType, toStreamNumber, encryptedPayload, int(time.time()),'')
objectType, toStreamNumber, encryptedPayload, int(time.time()) + TTL,'')
shared.inventorySets[toStreamNumber].add(inventoryHash)
with shared.printLock:
print 'Broadcasting inv for msg(API disseminatePreEncryptedMsg command):', inventoryHash.encode('hex')
@ -814,10 +820,11 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
pubkeyReadPosition += addressVersionLength
pubkeyStreamNumber = decodeVarint(payload[pubkeyReadPosition:pubkeyReadPosition+10])[0]
inventoryHash = calculateInventoryHash(payload)
objectType = 'pubkey'
objectType = 1
#todo: support v4 pubkeys
TTL = 28 * 24 * 60 * 60
shared.inventory[inventoryHash] = (
objectType, pubkeyStreamNumber, payload, int(time.time()),'')
objectType, pubkeyStreamNumber, payload, int(time.time()) + TTL,'')
shared.inventorySets[pubkeyStreamNumber].add(inventoryHash)
with shared.printLock:
print 'broadcasting inv within API command disseminatePubkey with hash:', inventoryHash.encode('hex')
@ -839,7 +846,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
# use it we'll need to fill out a field in our inventory database
# which is blank by default (first20bytesofencryptedmessage).
queryreturn = sqlQuery(
'''SELECT hash, payload FROM inventory WHERE tag = '' and objecttype = 'msg' ; ''')
'''SELECT hash, payload FROM inventory WHERE tag = '' and objecttype = 2 ; ''')
with SqlBulkExecute() as sql:
for row in queryreturn:
hash01, payload = row
@ -906,6 +913,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
return self._handle_request(method, params)
except APIError as e:
return str(e)
except varintDecodeError as e:
logger.error(e)
return "API Error 0026: Data contains a malformed varint. Some details: %s" % e
except Exception as e:
logger.exception(e)
return "API Error 0021: Unexpected API Failure - %s" % str(e)

@ -742,6 +742,8 @@ def sendMessage(sender="", recv="", broadcast=None, subject="", body="", reply=F
err += "Some data encoded in the address is too short. There might be something wrong with the software of your acquaintance."
elif status == "ripetoolong":
err += "Some data encoded in the address is too long. There might be something wrong with the software of your acquaintance."
elif status == "varintmalformed":
err += "Some data encoded in the address is malformed. There might be something wrong with the software of your acquaintance."
else:
err += "It is unknown what is wrong with the address."
d.scrollbox(unicode(err), exit_label="Continue")

@ -154,9 +154,9 @@ selfInitiatedConnections = {}
if shared.useVeryEasyProofOfWorkForTesting:
shared.networkDefaultProofOfWorkNonceTrialsPerByte = int(
shared.networkDefaultProofOfWorkNonceTrialsPerByte / 16)
shared.networkDefaultProofOfWorkNonceTrialsPerByte / 100)
shared.networkDefaultPayloadLengthExtraBytes = int(
shared.networkDefaultPayloadLengthExtraBytes / 7000)
shared.networkDefaultPayloadLengthExtraBytes / 100)
class Main:
def start(self, daemon=False):

@ -1346,26 +1346,23 @@ class MyForm(QtGui.QMainWindow):
if self.regenerateAddressesDialogInstance.ui.lineEditPassphrase.text() == "":
QMessageBox.about(self, _translate("MainWindow", "bad passphrase"), _translate(
"MainWindow", "You must type your passphrase. If you don\'t have one then this is not the form for you."))
else:
streamNumberForAddress = int(
self.regenerateAddressesDialogInstance.ui.lineEditStreamNumber.text())
try:
addressVersionNumber = int(
self.regenerateAddressesDialogInstance.ui.lineEditAddressVersionNumber.text())
except:
QMessageBox.about(self, _translate("MainWindow", "Bad address version number"), _translate(
"MainWindow", "Your address version number must be a number: either 3 or 4."))
if addressVersionNumber < 3 or addressVersionNumber > 4:
QMessageBox.about(self, _translate("MainWindow", "Bad address version number"), _translate(
"MainWindow", "Your address version number must be either 3 or 4."))
# self.addressGenerator = addressGenerator()
# self.addressGenerator.setup(addressVersionNumber,streamNumberForAddress,"unused address",self.regenerateAddressesDialogInstance.ui.spinBoxNumberOfAddressesToMake.value(),self.regenerateAddressesDialogInstance.ui.lineEditPassphrase.text().toUtf8(),self.regenerateAddressesDialogInstance.ui.checkBoxEighteenByteRipe.isChecked())
# 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)
# self.addressGenerator.start()
shared.addressGeneratorQueue.put(('createDeterministicAddresses', addressVersionNumber, streamNumberForAddress, "regenerated deterministic address", self.regenerateAddressesDialogInstance.ui.spinBoxNumberOfAddressesToMake.value(
), self.regenerateAddressesDialogInstance.ui.lineEditPassphrase.text().toUtf8(), self.regenerateAddressesDialogInstance.ui.checkBoxEighteenByteRipe.isChecked()))
self.ui.tabWidget.setCurrentIndex(3)
return
streamNumberForAddress = int(
self.regenerateAddressesDialogInstance.ui.lineEditStreamNumber.text())
try:
addressVersionNumber = int(
self.regenerateAddressesDialogInstance.ui.lineEditAddressVersionNumber.text())
except:
QMessageBox.about(self, _translate("MainWindow", "Bad address version number"), _translate(
"MainWindow", "Your address version number must be a number: either 3 or 4."))
return
if addressVersionNumber < 3 or addressVersionNumber > 4:
QMessageBox.about(self, _translate("MainWindow", "Bad address version number"), _translate(
"MainWindow", "Your address version number must be either 3 or 4."))
return
shared.addressGeneratorQueue.put(('createDeterministicAddresses', addressVersionNumber, streamNumberForAddress, "regenerated deterministic address", self.regenerateAddressesDialogInstance.ui.spinBoxNumberOfAddressesToMake.value(
), self.regenerateAddressesDialogInstance.ui.lineEditPassphrase.text().toUtf8(), self.regenerateAddressesDialogInstance.ui.checkBoxEighteenByteRipe.isChecked()))
self.ui.tabWidget.setCurrentIndex(3)
def click_actionJoinChan(self):
self.newChanDialogInstance = newChanDialog(self)
@ -1842,6 +1839,17 @@ class MyForm(QtGui.QMainWindow):
subject = str(self.ui.lineEditSubject.text().toUtf8())
message = str(
self.ui.textEditMessage.document().toPlainText().toUtf8())
"""
The whole network message must fit in 2^18 bytes. Let's assume 500
bytes of overhead. If someone wants to get that too an exact
number you are welcome to but I think that it would be a better
use of time to support message continuation so that users can
send messages of any length.
"""
if len(message) > (2 ** 18 - 500):
QMessageBox.about(self, _translate("MainWindow", "Message too long"), _translate(
"MainWindow", "The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending.").arg(len(message) - (2 ** 18 - 500)))
return
if self.ui.radioButtonSpecific.isChecked(): # To send a message to specific people (rather than broadcast)
toAddressesList = [s.strip()
for s in toAddresses.replace(',', ';').split(';')]
@ -1873,6 +1881,9 @@ class MyForm(QtGui.QMainWindow):
elif status == 'ripetoolong':
self.statusBar().showMessage(_translate(
"MainWindow", "Error: Some data encoded in the address %1 is too long. There might be something wrong with the software of your acquaintance.").arg(toAddress))
elif status == 'varintmalformed':
self.statusBar().showMessage(_translate(
"MainWindow", "Error: Some data encoded in the address %1 is malformed. There might be something wrong with the software of your acquaintance.").arg(toAddress))
else:
self.statusBar().showMessage(_translate(
"MainWindow", "Error: Something is wrong with the address %1.").arg(toAddress))
@ -2211,10 +2222,10 @@ class MyForm(QtGui.QMainWindow):
addressVersion) + encodeVarint(streamNumber) + ripe).digest()).digest()
tag = doubleHashOfAddressData[32:]
queryreturn = sqlQuery(
'''select payload from inventory where objecttype='broadcast' and tag=?''', tag)
'''select payload from inventory where objecttype=3 and tag=?''', tag)
for row in queryreturn:
payload, = row
objectType = 'broadcast'
objectType = 3
with shared.objectProcessorQueueSizeLock:
shared.objectProcessorQueueSize += len(payload)
shared.objectProcessorQueue.put((objectType,payload))
@ -2308,6 +2319,15 @@ class MyForm(QtGui.QMainWindow):
self.settingsDialogInstance.ui.lineEditSocksPassword.text()))
shared.config.set('bitmessagesettings', 'sockslisten', str(
self.settingsDialogInstance.ui.checkBoxSocksListen.isChecked()))
try:
# Rounding to integers just for aesthetics
shared.config.set('bitmessagesettings', 'maxdownloadrate', str(
int(float(self.settingsDialogInstance.ui.lineEditMaxDownloadRate.text()))))
shared.config.set('bitmessagesettings', 'maxuploadrate', str(
int(float(self.settingsDialogInstance.ui.lineEditMaxUploadRate.text()))))
except:
QMessageBox.about(self, _translate("MainWindow", "Number needed"), _translate(
"MainWindow", "Your maximum download and upload rate must be numbers. Ignoring what you typed."))
shared.config.set('bitmessagesettings', 'namecoinrpctype',
self.settingsDialogInstance.getNamecoinType())
@ -2319,19 +2339,39 @@ class MyForm(QtGui.QMainWindow):
self.settingsDialogInstance.ui.lineEditNamecoinUser.text()))
shared.config.set('bitmessagesettings', 'namecoinrpcpassword', str(
self.settingsDialogInstance.ui.lineEditNamecoinPassword.text()))
# Demanded difficulty tab
if float(self.settingsDialogInstance.ui.lineEditTotalDifficulty.text()) >= 1:
shared.config.set('bitmessagesettings', 'defaultnoncetrialsperbyte', str(int(float(
self.settingsDialogInstance.ui.lineEditTotalDifficulty.text()) * shared.networkDefaultProofOfWorkNonceTrialsPerByte)))
if float(self.settingsDialogInstance.ui.lineEditSmallMessageDifficulty.text()) >= 1:
shared.config.set('bitmessagesettings', 'defaultpayloadlengthextrabytes', str(int(float(
self.settingsDialogInstance.ui.lineEditSmallMessageDifficulty.text()) * shared.networkDefaultPayloadLengthExtraBytes)))
acceptableDifficultyChanged = False
if float(self.settingsDialogInstance.ui.lineEditMaxAcceptableTotalDifficulty.text()) >= 1 or float(self.settingsDialogInstance.ui.lineEditMaxAcceptableTotalDifficulty.text()) == 0:
shared.config.set('bitmessagesettings', 'maxacceptablenoncetrialsperbyte', str(int(float(
self.settingsDialogInstance.ui.lineEditMaxAcceptableTotalDifficulty.text()) * shared.networkDefaultProofOfWorkNonceTrialsPerByte)))
if shared.config.get('bitmessagesettings','maxacceptablenoncetrialsperbyte') != str(int(float(
self.settingsDialogInstance.ui.lineEditMaxAcceptableTotalDifficulty.text()) * shared.networkDefaultProofOfWorkNonceTrialsPerByte)):
# the user changed the max acceptable total difficulty
acceptableDifficultyChanged = True
shared.config.set('bitmessagesettings', 'maxacceptablenoncetrialsperbyte', str(int(float(
self.settingsDialogInstance.ui.lineEditMaxAcceptableTotalDifficulty.text()) * shared.networkDefaultProofOfWorkNonceTrialsPerByte)))
if float(self.settingsDialogInstance.ui.lineEditMaxAcceptableSmallMessageDifficulty.text()) >= 1 or float(self.settingsDialogInstance.ui.lineEditMaxAcceptableSmallMessageDifficulty.text()) == 0:
shared.config.set('bitmessagesettings', 'maxacceptablepayloadlengthextrabytes', str(int(float(
self.settingsDialogInstance.ui.lineEditMaxAcceptableSmallMessageDifficulty.text()) * shared.networkDefaultPayloadLengthExtraBytes)))
if shared.config.get('bitmessagesettings','maxacceptablepayloadlengthextrabytes') != str(int(float(
self.settingsDialogInstance.ui.lineEditMaxAcceptableSmallMessageDifficulty.text()) * shared.networkDefaultPayloadLengthExtraBytes)):
# the user changed the max acceptable small message difficulty
acceptableDifficultyChanged = True
shared.config.set('bitmessagesettings', 'maxacceptablepayloadlengthextrabytes', str(int(float(
self.settingsDialogInstance.ui.lineEditMaxAcceptableSmallMessageDifficulty.text()) * shared.networkDefaultPayloadLengthExtraBytes)))
if acceptableDifficultyChanged:
# It might now be possible to send msgs which were previously marked as toodifficult.
# Let us change them to 'msgqueued'. The singleWorker will try to send them and will again
# mark them as toodifficult if the receiver's required difficulty is still higher than
# we are willing to do.
sqlExecute('''UPDATE sent SET status='msgqueued' WHERE status='toodifficult' ''')
shared.workerQueue.put(('sendmessage', ''))
#start:UI setting to stop trying to send messages after X days/months
# I'm open to changing this UI to something else if someone has a better idea.
if ((self.settingsDialogInstance.ui.lineEditDays.text()=='') and (self.settingsDialogInstance.ui.lineEditMonths.text()=='')):#We need to handle this special case. Bitmessage has its default behavior. The input is blank/blank
@ -3429,7 +3469,12 @@ class settingsDialog(QtGui.QDialog):
shared.config.get('bitmessagesettings', 'sockspassword')))
QtCore.QObject.connect(self.ui.comboBoxProxyType, QtCore.SIGNAL(
"currentIndexChanged(int)"), self.comboBoxProxyTypeChanged)
self.ui.lineEditMaxDownloadRate.setText(str(
shared.config.get('bitmessagesettings', 'maxdownloadrate')))
self.ui.lineEditMaxUploadRate.setText(str(
shared.config.get('bitmessagesettings', 'maxuploadrate')))
# Demanded difficulty tab
self.ui.lineEditTotalDifficulty.setText(str((float(shared.config.getint(
'bitmessagesettings', 'defaultnoncetrialsperbyte')) / shared.networkDefaultProofOfWorkNonceTrialsPerByte)))
self.ui.lineEditSmallMessageDifficulty.setText(str((float(shared.config.getint(
@ -3620,6 +3665,9 @@ class AddAddressDialog(QtGui.QDialog):
elif status == 'ripetoolong':
self.ui.labelAddressCheck.setText(_translate(
"MainWindow", "Some data encoded in the address is too long."))
elif status == 'varintmalformed':
self.ui.labelAddressCheck.setText(_translate(
"MainWindow", "Some data encoded in the address is malformed."))
elif status == 'success':
self.ui.labelAddressCheck.setText(
_translate("MainWindow", "Address is valid."))
@ -3658,6 +3706,9 @@ class NewSubscriptionDialog(QtGui.QDialog):
elif status == 'ripetoolong':
self.ui.labelAddressCheck.setText(_translate(
"MainWindow", "Some data encoded in the address is too long."))
elif status == 'varintmalformed':
self.ui.labelAddressCheck.setText(_translate(
"MainWindow", "Some data encoded in the address is malformed."))
elif status == 'success':
self.ui.labelAddressCheck.setText(
_translate("MainWindow", "Address is valid."))
@ -3670,7 +3721,7 @@ class NewSubscriptionDialog(QtGui.QDialog):
addressVersion) + encodeVarint(streamNumber) + ripe).digest()).digest()
tag = doubleHashOfAddressData[32:]
queryreturn = sqlQuery(
'''select hash from inventory where objecttype='broadcast' and tag=?''', tag)
'''select hash from inventory where objecttype=3 and tag=?''', tag)
if len(queryreturn) == 0:
self.ui.checkBoxDisplayMessagesAlreadyInInventory.setText(
_translate("MainWindow", "There are no recent broadcasts from this address to display."))

@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'settings.ui'
#
# Created: Mon May 19 15:54:27 2014
# Created: Tue Sep 09 15:13:28 2014
# by: PyQt4 UI code generator 4.10.3
#
# WARNING! All changes made in this file will be lost!
@ -128,6 +128,37 @@ class Ui_settingsDialog(object):
self.lineEditTCPPort.setObjectName(_fromUtf8("lineEditTCPPort"))
self.gridLayout_3.addWidget(self.lineEditTCPPort, 0, 2, 1, 1)
self.gridLayout_4.addWidget(self.groupBox1, 0, 0, 1, 1)
self.groupBox_3 = QtGui.QGroupBox(self.tabNetworkSettings)
self.groupBox_3.setObjectName(_fromUtf8("groupBox_3"))
self.gridLayout_9 = QtGui.QGridLayout(self.groupBox_3)
self.gridLayout_9.setObjectName(_fromUtf8("gridLayout_9"))
spacerItem1 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_9.addItem(spacerItem1, 0, 0, 2, 1)
self.label_24 = QtGui.QLabel(self.groupBox_3)
self.label_24.setObjectName(_fromUtf8("label_24"))
self.gridLayout_9.addWidget(self.label_24, 0, 1, 1, 1)
self.lineEditMaxDownloadRate = QtGui.QLineEdit(self.groupBox_3)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.lineEditMaxDownloadRate.sizePolicy().hasHeightForWidth())
self.lineEditMaxDownloadRate.setSizePolicy(sizePolicy)
self.lineEditMaxDownloadRate.setMaximumSize(QtCore.QSize(60, 16777215))
self.lineEditMaxDownloadRate.setObjectName(_fromUtf8("lineEditMaxDownloadRate"))
self.gridLayout_9.addWidget(self.lineEditMaxDownloadRate, 0, 2, 1, 1)
self.label_25 = QtGui.QLabel(self.groupBox_3)
self.label_25.setObjectName(_fromUtf8("label_25"))
self.gridLayout_9.addWidget(self.label_25, 1, 1, 1, 1)
self.lineEditMaxUploadRate = QtGui.QLineEdit(self.groupBox_3)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.lineEditMaxUploadRate.sizePolicy().hasHeightForWidth())
self.lineEditMaxUploadRate.setSizePolicy(sizePolicy)
self.lineEditMaxUploadRate.setMaximumSize(QtCore.QSize(60, 16777215))
self.lineEditMaxUploadRate.setObjectName(_fromUtf8("lineEditMaxUploadRate"))
self.gridLayout_9.addWidget(self.lineEditMaxUploadRate, 1, 2, 1, 1)
self.gridLayout_4.addWidget(self.groupBox_3, 2, 0, 1, 1)
self.groupBox_2 = QtGui.QGroupBox(self.tabNetworkSettings)
self.groupBox_2.setObjectName(_fromUtf8("groupBox_2"))
self.gridLayout_2 = QtGui.QGridLayout(self.groupBox_2)
@ -176,8 +207,8 @@ class Ui_settingsDialog(object):
self.comboBoxProxyType.addItem(_fromUtf8(""))
self.gridLayout_2.addWidget(self.comboBoxProxyType, 0, 1, 1, 1)
self.gridLayout_4.addWidget(self.groupBox_2, 1, 0, 1, 1)
spacerItem1 = QtGui.QSpacerItem(20, 70, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
self.gridLayout_4.addItem(spacerItem1, 2, 0, 1, 1)
spacerItem2 = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
self.gridLayout_4.addItem(spacerItem2, 3, 0, 1, 1)
self.tabWidgetSettings.addTab(self.tabNetworkSettings, _fromUtf8(""))
self.tabDemandedDifficulty = QtGui.QWidget()
self.tabDemandedDifficulty.setObjectName(_fromUtf8("tabDemandedDifficulty"))
@ -199,8 +230,8 @@ class Ui_settingsDialog(object):
self.label_8.setWordWrap(True)
self.label_8.setObjectName(_fromUtf8("label_8"))
self.gridLayout_6.addWidget(self.label_8, 0, 0, 1, 3)
spacerItem2 = QtGui.QSpacerItem(203, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_6.addItem(spacerItem2, 1, 0, 1, 1)
spacerItem3 = QtGui.QSpacerItem(203, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_6.addItem(spacerItem3, 1, 0, 1, 1)
self.label_12 = QtGui.QLabel(self.tabDemandedDifficulty)
self.label_12.setWordWrap(True)
self.label_12.setObjectName(_fromUtf8("label_12"))
@ -223,10 +254,10 @@ class Ui_settingsDialog(object):
self.lineEditTotalDifficulty.setMaximumSize(QtCore.QSize(70, 16777215))
self.lineEditTotalDifficulty.setObjectName(_fromUtf8("lineEditTotalDifficulty"))
self.gridLayout_6.addWidget(self.lineEditTotalDifficulty, 1, 2, 1, 1)
spacerItem3 = QtGui.QSpacerItem(203, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_6.addItem(spacerItem3, 3, 0, 1, 1)
spacerItem4 = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
self.gridLayout_6.addItem(spacerItem4, 5, 0, 1, 1)
spacerItem4 = QtGui.QSpacerItem(203, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_6.addItem(spacerItem4, 3, 0, 1, 1)
spacerItem5 = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
self.gridLayout_6.addItem(spacerItem5, 5, 0, 1, 1)
self.tabWidgetSettings.addTab(self.tabDemandedDifficulty, _fromUtf8(""))
self.tabMaxAcceptableDifficulty = QtGui.QWidget()
self.tabMaxAcceptableDifficulty.setObjectName(_fromUtf8("tabMaxAcceptableDifficulty"))
@ -236,8 +267,8 @@ class Ui_settingsDialog(object):
self.label_15.setWordWrap(True)
self.label_15.setObjectName(_fromUtf8("label_15"))
self.gridLayout_7.addWidget(self.label_15, 0, 0, 1, 3)
spacerItem5 = QtGui.QSpacerItem(102, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_7.addItem(spacerItem5, 1, 0, 1, 1)
spacerItem6 = QtGui.QSpacerItem(102, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_7.addItem(spacerItem6, 1, 0, 1, 1)
self.label_13 = QtGui.QLabel(self.tabMaxAcceptableDifficulty)
self.label_13.setLayoutDirection(QtCore.Qt.LeftToRight)
self.label_13.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
@ -252,8 +283,8 @@ class Ui_settingsDialog(object):
self.lineEditMaxAcceptableTotalDifficulty.setMaximumSize(QtCore.QSize(70, 16777215))
self.lineEditMaxAcceptableTotalDifficulty.setObjectName(_fromUtf8("lineEditMaxAcceptableTotalDifficulty"))
self.gridLayout_7.addWidget(self.lineEditMaxAcceptableTotalDifficulty, 1, 2, 1, 1)
spacerItem6 = QtGui.QSpacerItem(102, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_7.addItem(spacerItem6, 2, 0, 1, 1)
spacerItem7 = QtGui.QSpacerItem(102, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_7.addItem(spacerItem7, 2, 0, 1, 1)
self.label_14 = QtGui.QLabel(self.tabMaxAcceptableDifficulty)
self.label_14.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.label_14.setObjectName(_fromUtf8("label_14"))
@ -267,15 +298,15 @@ class Ui_settingsDialog(object):
self.lineEditMaxAcceptableSmallMessageDifficulty.setMaximumSize(QtCore.QSize(70, 16777215))
self.lineEditMaxAcceptableSmallMessageDifficulty.setObjectName(_fromUtf8("lineEditMaxAcceptableSmallMessageDifficulty"))
self.gridLayout_7.addWidget(self.lineEditMaxAcceptableSmallMessageDifficulty, 2, 2, 1, 1)
spacerItem7 = QtGui.QSpacerItem(20, 147, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
self.gridLayout_7.addItem(spacerItem7, 3, 1, 1, 1)
spacerItem8 = QtGui.QSpacerItem(20, 147, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
self.gridLayout_7.addItem(spacerItem8, 3, 1, 1, 1)
self.tabWidgetSettings.addTab(self.tabMaxAcceptableDifficulty, _fromUtf8(""))
self.tabNamecoin = QtGui.QWidget()
self.tabNamecoin.setObjectName(_fromUtf8("tabNamecoin"))
self.gridLayout_8 = QtGui.QGridLayout(self.tabNamecoin)
self.gridLayout_8.setObjectName(_fromUtf8("gridLayout_8"))
spacerItem8 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_8.addItem(spacerItem8, 2, 0, 1, 1)
spacerItem9 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_8.addItem(spacerItem9, 2, 0, 1, 1)
self.label_16 = QtGui.QLabel(self.tabNamecoin)
self.label_16.setWordWrap(True)
self.label_16.setObjectName(_fromUtf8("label_16"))
@ -287,10 +318,10 @@ class Ui_settingsDialog(object):
self.lineEditNamecoinHost = QtGui.QLineEdit(self.tabNamecoin)
self.lineEditNamecoinHost.setObjectName(_fromUtf8("lineEditNamecoinHost"))
self.gridLayout_8.addWidget(self.lineEditNamecoinHost, 2, 2, 1, 1)
spacerItem9 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_8.addItem(spacerItem9, 3, 0, 1, 1)
spacerItem10 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_8.addItem(spacerItem10, 4, 0, 1, 1)
self.gridLayout_8.addItem(spacerItem10, 3, 0, 1, 1)
spacerItem11 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_8.addItem(spacerItem11, 4, 0, 1, 1)
self.label_18 = QtGui.QLabel(self.tabNamecoin)
self.label_18.setEnabled(True)
self.label_18.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
@ -299,8 +330,8 @@ class Ui_settingsDialog(object):
self.lineEditNamecoinPort = QtGui.QLineEdit(self.tabNamecoin)
self.lineEditNamecoinPort.setObjectName(_fromUtf8("lineEditNamecoinPort"))
self.gridLayout_8.addWidget(self.lineEditNamecoinPort, 3, 2, 1, 1)
spacerItem11 = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
self.gridLayout_8.addItem(spacerItem11, 8, 1, 1, 1)
spacerItem12 = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
self.gridLayout_8.addItem(spacerItem12, 8, 1, 1, 1)
self.labelNamecoinUser = QtGui.QLabel(self.tabNamecoin)
self.labelNamecoinUser.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.labelNamecoinUser.setObjectName(_fromUtf8("labelNamecoinUser"))
@ -308,8 +339,8 @@ class Ui_settingsDialog(object):
self.lineEditNamecoinUser = QtGui.QLineEdit(self.tabNamecoin)
self.lineEditNamecoinUser.setObjectName(_fromUtf8("lineEditNamecoinUser"))
self.gridLayout_8.addWidget(self.lineEditNamecoinUser, 4, 2, 1, 1)
spacerItem12 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_8.addItem(spacerItem12, 5, 0, 1, 1)
spacerItem13 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_8.addItem(spacerItem13, 5, 0, 1, 1)
self.labelNamecoinPassword = QtGui.QLabel(self.tabNamecoin)
self.labelNamecoinPassword.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.labelNamecoinPassword.setObjectName(_fromUtf8("labelNamecoinPassword"))
@ -347,8 +378,8 @@ class Ui_settingsDialog(object):
self.label_7.setWordWrap(True)
self.label_7.setObjectName(_fromUtf8("label_7"))
self.gridLayout_5.addWidget(self.label_7, 0, 0, 1, 3)
spacerItem13 = QtGui.QSpacerItem(212, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_5.addItem(spacerItem13, 1, 0, 1, 1)
spacerItem14 = QtGui.QSpacerItem(212, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_5.addItem(spacerItem14, 1, 0, 1, 1)
self.widget = QtGui.QWidget(self.tabResendsExpire)
self.widget.setMinimumSize(QtCore.QSize(231, 75))
self.widget.setObjectName(_fromUtf8("widget"))
@ -373,8 +404,8 @@ class Ui_settingsDialog(object):
self.label_23.setGeometry(QtCore.QRect(170, 41, 71, 16))
self.label_23.setObjectName(_fromUtf8("label_23"))
self.gridLayout_5.addWidget(self.widget, 1, 2, 1, 1)
spacerItem14 = QtGui.QSpacerItem(20, 129, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
self.gridLayout_5.addItem(spacerItem14, 2, 1, 1, 1)
spacerItem15 = QtGui.QSpacerItem(20, 129, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
self.gridLayout_5.addItem(spacerItem15, 2, 1, 1, 1)
self.tabWidgetSettings.addTab(self.tabResendsExpire, _fromUtf8(""))
self.gridLayout.addWidget(self.tabWidgetSettings, 0, 0, 1, 1)
@ -416,6 +447,9 @@ class Ui_settingsDialog(object):
self.tabWidgetSettings.setTabText(self.tabWidgetSettings.indexOf(self.tabUserInterface), _translate("settingsDialog", "User Interface", None))
self.groupBox1.setTitle(_translate("settingsDialog", "Listening port", None))
self.label.setText(_translate("settingsDialog", "Listen for connections on port:", None))
self.groupBox_3.setTitle(_translate("settingsDialog", "Bandwidth limit", None))
self.label_24.setText(_translate("settingsDialog", "Maximum download rate (kB/s): [0: unlimited]", None))
self.label_25.setText(_translate("settingsDialog", "Maximum upload rate (kB/s): [0: unlimited]", None))
self.groupBox_2.setTitle(_translate("settingsDialog", "Proxy server / Tor", None))
self.label_2.setText(_translate("settingsDialog", "Type:", None))
self.label_3.setText(_translate("settingsDialog", "Server hostname:", None))

@ -247,6 +247,74 @@
</layout>
</widget>
</item>
<item row="2" column="0">
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Bandwidth limit</string>
</property>
<layout class="QGridLayout" name="gridLayout_9">
<item row="0" column="0" rowspan="2">
<spacer name="horizontalSpacer_11">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_24">
<property name="text">
<string>Maximum download rate (kB/s): [0: unlimited]</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLineEdit" name="lineEditMaxDownloadRate">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>60</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_25">
<property name="text">
<string>Maximum upload rate (kB/s): [0: unlimited]</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLineEdit" name="lineEditMaxUploadRate">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>60</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="0">
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
@ -350,7 +418,7 @@
</layout>
</widget>
</item>
<item row="2" column="0">
<item row="3" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
@ -358,7 +426,7 @@
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>70</height>
<height>40</height>
</size>
</property>
</spacer>

@ -1,19 +1,19 @@
from setuptools import setup
name = "Bitmessage"
version = "0.4.3"
version = "0.4.4"
mainscript = ["bitmessagemain.py"]
setup(
name = name,
version = version,
app = mainscript,
setup_requires = ["py2app"],
options = dict(
py2app = dict(
resources = ["images", "translations"],
includes = ['sip', 'PyQt4._qt'],
iconfile = "images/bitmessage.icns"
)
)
name = name,
version = version,
app = mainscript,
setup_requires = ["py2app"],
options = dict(
py2app = dict(
resources = ["images", "translations"],
includes = ['sip', 'PyQt4._qt'],
iconfile = "images/bitmessage.icns"
)
)
)

@ -7,8 +7,9 @@ from struct import unpack, pack
import sys
import string
from subprocess import call # used when the API must execute an outside program
from pyelliptic.openssl import OpenSSL
import traceback
from pyelliptic.openssl import OpenSSL
import highlevelcrypto
from addresses import *
import helper_generic
@ -51,18 +52,23 @@ class objectProcessor(threading.Thread):
while True:
objectType, data = shared.objectProcessorQueue.get()
if objectType == 'getpubkey':
self.processgetpubkey(data)
elif objectType == 'pubkey':
self.processpubkey(data)
elif objectType == 'msg':
self.processmsg(data)
elif objectType == 'broadcast':
self.processbroadcast(data)
elif objectType == 'checkShutdownVariable': # is more of a command, not an object type. Is used to get this thread past the queue.get() so that it will check the shutdown variable.
pass
else:
logger.critical('Error! Bug! The class_objectProcessor was passed an object type it doesn\'t recognize: %s' % str(objectType))
try:
if objectType == 0: # getpubkey
self.processgetpubkey(data)
elif objectType == 1: #pubkey
self.processpubkey(data)
elif objectType == 2: #msg
self.processmsg(data)
elif objectType == 3: #broadcast
self.processbroadcast(data)
elif objectType == 'checkShutdownVariable': # is more of a command, not an object type. Is used to get this thread past the queue.get() so that it will check the shutdown variable.
pass
else:
logger.critical('Error! Bug! The class_objectProcessor was passed an object type it doesn\'t recognize: %s' % str(objectType))
except varintDecodeError as e:
logger.debug("There was a problem with a varint while processing an object. Some details: %s" % e)
except Exception as e:
logger.critical("Critical error within objectProcessorThread: \n%s" % traceback.format_exc())
with shared.objectProcessorQueueSizeLock:
shared.objectProcessorQueueSize -= len(data) # We maintain objectProcessorQueueSize so that we will slow down requesting objects if too much data accumulates in the queue.
@ -83,17 +89,7 @@ class objectProcessor(threading.Thread):
break
def processgetpubkey(self, data):
readPosition = 8 # bypass the nonce
embeddedTime, = unpack('>I', data[readPosition:readPosition + 4])
# This section is used for the transition from 32 bit time to 64 bit
# time in the protocol.
if embeddedTime == 0:
embeddedTime, = unpack('>Q', data[readPosition:readPosition + 8])
readPosition += 8
else:
readPosition += 4
readPosition = 20 # bypass the nonce, time, and object type
requestedAddressVersionNumber, addressVersionLength = decodeVarint(
data[readPosition:readPosition + 10])
readPosition += addressVersionLength
@ -147,7 +143,7 @@ class objectProcessor(threading.Thread):
myAddress, 'lastpubkeysendtime'))
except:
lastPubkeySendTime = 0
if lastPubkeySendTime > time.time() - shared.lengthOfTimeToHoldOnToAllPubkeys: # If the last time we sent our pubkey was more recent than 28 days ago...
if lastPubkeySendTime > time.time() - 2419200: # If the last time we sent our pubkey was more recent than 28 days ago...
logger.info('Found getpubkey-requested-item in my list of EC hashes BUT we already sent it recently. Ignoring request. The lastPubkeySendTime is: %s' % lastPubkeySendTime)
return
logger.info('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.')
@ -166,17 +162,8 @@ class objectProcessor(threading.Thread):
shared.numberOfPubkeysProcessed += 1
shared.UISignalQueue.put((
'updateNumberOfPubkeysProcessed', 'no data'))
readPosition = 8 # bypass the nonce
embeddedTime, = unpack('>I', data[readPosition:readPosition + 4])
# This section is used for the transition from 32 bit time to 64 bit
# time in the protocol.
if embeddedTime == 0:
embeddedTime, = unpack('>Q', data[readPosition:readPosition + 8])
readPosition += 8
else:
readPosition += 4
embeddedTime, = unpack('>Q', data[8:16])
readPosition = 20 # bypass the nonce, time, and object type
addressVersion, varintLength = decodeVarint(
data[readPosition:readPosition + 10])
readPosition += varintLength
@ -225,15 +212,25 @@ class objectProcessor(threading.Thread):
queryreturn = sqlQuery(
'''SELECT usedpersonally FROM pubkeys WHERE hash=? AND addressversion=? AND usedpersonally='yes' ''', ripe, addressVersion)
"""
With the changes in protocol v3, we have to be careful to store pubkey data
in the database the same way we did before to maintain backwards compatibility
with what is in people's databases already. This means that for v2 keys, we
must store the nonce, the time, and then everything else starting with the
address version.
"""
dataToStore = '\x00' * 8 # fake nonce
dataToStore += data[8:16] # the time
dataToStore += data[20:] # everything else
if queryreturn != []: # if this pubkey is already in our database and if we have used it personally:
logger.info('We HAVE used this pubkey personally. Updating time.')
t = (ripe, addressVersion, data, embeddedTime, 'yes')
t = (ripe, addressVersion, dataToStore, int(time.time()), 'yes')
else:
logger.info('We have NOT used this pubkey personally. Inserting in database.')
t = (ripe, addressVersion, data, embeddedTime, 'no')
# This will also update the embeddedTime.
t = (ripe, addressVersion, dataToStore, int(time.time()), 'no')
sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?,?)''', *t)
# shared.workerQueue.put(('newpubkey',(addressVersion,streamNumber,ripe)))
self.possibleNewPubkey(ripe = ripe)
if addressVersion == 3:
if len(data) < 170: # sanity check.
@ -242,9 +239,6 @@ class objectProcessor(threading.Thread):
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]
readPosition += 64
@ -259,14 +253,32 @@ class objectProcessor(threading.Thread):
data[readPosition:readPosition + 10])
readPosition += signatureLengthLength
signature = data[readPosition:readPosition + signatureLength]
try:
if not highlevelcrypto.verify(data[8:endOfSignedDataPosition], signature, publicSigningKey.encode('hex')):
logger.warning('ECDSA verify failed (within processpubkey)')
"""
With the changes in protocol v3, to maintain backwards compatibility, signatures will be sent
the 'old' way during an upgrade period and then a 'new' simpler way after that. We will therefore
check the sig both ways.
Old way:
signedData = timePubkeyWasSigned(4 bytes) + addressVersion through extra_bytes
New way:
signedData = all of the payload data, from the time down through the extra_bytes
The timePubkeyWasSigned will be calculated by subtracting 28 days form the embedded expiresTime.
"""
expiresTime, = unpack('>Q', data[8:16])
TTL = 28 * 24 * 60 * 60
signedData = pack('>I', (expiresTime - TTL)) # the time that the pubkey was signed. 4 bytes.
signedData += data[20:endOfSignedDataPosition] # the address version down through the payloadLengthExtraBytes
if highlevelcrypto.verify(signedData, signature, publicSigningKey.encode('hex')):
logger.info('ECDSA verify passed (within processpubkey, old method)')
else:
logger.warning('ECDSA verify failed (within processpubkey, old method)')
# let us try the newer signature method
if highlevelcrypto.verify(data[8:endOfSignedDataPosition], signature, publicSigningKey.encode('hex')):
logger.info('ECDSA verify passed (within processpubkey, new method)')
else:
logger.warning('ECDSA verify failed (within processpubkey, new method)')
return
logger.info('ECDSA verify passed (within processpubkey)')
except Exception as err:
logger.warning('ECDSA verify failed (within processpubkey) %s' % err)
return
sha = hashlib.new('sha512')
sha.update(publicSigningKey + publicEncryptionKey)
@ -286,109 +298,45 @@ class objectProcessor(threading.Thread):
)
)
"""
With the changes in protocol v3, we have to be careful to store pubkey data
in the database the same way we did before to maintain backwards compatibility
with what is in people's databases already. This means that for v3 keys, we
must store the nonce, the time, and then everything else starting with the
address version.
"""
dataToStore = '\x00' * 8 # fake nonce
dataToStore += data[8:16] # the time
dataToStore += data[20:] # everything else
queryreturn = sqlQuery('''SELECT usedpersonally FROM pubkeys WHERE hash=? AND addressversion=? AND usedpersonally='yes' ''', ripe, addressVersion)
if queryreturn != []: # if this pubkey is already in our database and if we have used it personally:
logger.info('We HAVE used this pubkey personally. Updating time.')
t = (ripe, addressVersion, data, embeddedTime, 'yes')
t = (ripe, addressVersion, dataToStore, int(time.time()), 'yes')
else:
logger.info('We have NOT used this pubkey personally. Inserting in database.')
t = (ripe, addressVersion, data, embeddedTime, 'no')
# This will also update the embeddedTime.
t = (ripe, addressVersion, dataToStore, int(time.time()), 'no')
sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?,?)''', *t)
self.possibleNewPubkey(ripe = ripe)
if addressVersion == 4:
"""
There exist a function: shared.decryptAndCheckPubkeyPayload which does something almost
the same as this section of code. There are differences, however; one being that
decryptAndCheckPubkeyPayload requires that a cryptor object be created each time it is
run which is an expensive operation. This, on the other hand, keeps them saved in
the shared.neededPubkeys dictionary so that if an attacker sends us many
incorrectly-tagged pubkeys, which would force us to try to decrypt them, this code
would run and handle that event quite quickly.
"""
if len(data) < 350: # sanity check.
logger.debug('(within processpubkey) payloadLength less than 350. Sanity check failed.')
return
signedData = data[8:readPosition] # Some of the signed data is not encrypted so let's keep it for now.
tag = data[readPosition:readPosition + 32]
readPosition += 32
encryptedData = data[readPosition:]
if tag not in shared.neededPubkeys:
logger.info('We don\'t need this v4 pubkey. We didn\'t ask for it.')
return
# Let us try to decrypt the pubkey
cryptorObject = shared.neededPubkeys[tag]
try:
decryptedData = cryptorObject.decrypt(encryptedData)
except:
# Someone must have encrypted some data with a different key
# but tagged it with a tag for which we are watching.
logger.info('Pubkey decryption was unsuccessful.')
return
readPosition = 0
bitfieldBehaviors = decryptedData[readPosition:readPosition + 4]
readPosition += 4
publicSigningKey = '\x04' + decryptedData[readPosition:readPosition + 64]
# Is it possible for a public key to be invalid such that trying to
# encrypt or check a sig with it will cause an error? If it is, we
# should probably test these keys here.
readPosition += 64
publicEncryptionKey = '\x04' + decryptedData[readPosition:readPosition + 64]
readPosition += 64
specifiedNonceTrialsPerByte, specifiedNonceTrialsPerByteLength = decodeVarint(
decryptedData[readPosition:readPosition + 10])
readPosition += specifiedNonceTrialsPerByteLength
specifiedPayloadLengthExtraBytes, specifiedPayloadLengthExtraBytesLength = decodeVarint(
decryptedData[readPosition:readPosition + 10])
readPosition += specifiedPayloadLengthExtraBytesLength
signedData += decryptedData[:readPosition]
signatureLength, signatureLengthLength = decodeVarint(
decryptedData[readPosition:readPosition + 10])
readPosition += signatureLengthLength
signature = decryptedData[readPosition:readPosition + signatureLength]
try:
if not highlevelcrypto.verify(signedData, signature, publicSigningKey.encode('hex')):
logger.info('ECDSA verify failed (within processpubkey)')
return
logger.info('ECDSA verify passed (within processpubkey)')
except Exception as err:
logger.info('ECDSA verify failed (within processpubkey) %s' % err)
return
sha = hashlib.new('sha512')
sha.update(publicSigningKey + publicEncryptionKey)
ripeHasher = hashlib.new('ripemd160')
ripeHasher.update(sha.digest())
ripe = ripeHasher.digest()
# We need to make sure that the tag on the outside of the encryption
# is the one generated from hashing these particular keys.
if tag != hashlib.sha512(hashlib.sha512(encodeVarint(addressVersion) + encodeVarint(streamNumber) + ripe).digest()).digest()[32:]:
logger.info('Someone was trying to act malicious: tag doesn\'t match the keys in this pubkey message. Ignoring it.')
return
logger.info('within recpubkey, addressVersion: %s, streamNumber: %s \n\
ripe %s\n\
publicSigningKey in hex: %s\n\
publicEncryptionKey in hex: %s' % (addressVersion,
streamNumber,
ripe.encode('hex'),
publicSigningKey.encode('hex'),
publicEncryptionKey.encode('hex')
)
)
t = (ripe, addressVersion, signedData, embeddedTime, 'yes')
sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?,?)''', *t)
fromAddress = encodeAddress(addressVersion, streamNumber, ripe)
# That this point we know that we have been waiting on this pubkey.
# This function will command the workerThread to start work on
# the messages that require it.
self.possibleNewPubkey(address = fromAddress)
# Let us try to decrypt the pubkey
toAddress, cryptorObject = shared.neededPubkeys[tag]
if shared.decryptAndCheckPubkeyPayload(data, toAddress) == 'successful':
# At this point we know that we have been waiting on this pubkey.
# This function will command the workerThread to start work on
# the messages that require it.
self.possibleNewPubkey(address=toAddress)
# Display timing data
timeRequiredToProcessPubkey = time.time(
@ -401,28 +349,28 @@ class objectProcessor(threading.Thread):
shared.numberOfMessagesProcessed += 1
shared.UISignalQueue.put((
'updateNumberOfMessagesProcessed', 'no data'))
readPosition = 8 # bypass the nonce
embeddedTime, = unpack('>I', data[readPosition:readPosition + 4])
# This section is used for the transition from 32 bit time to 64 bit
# time in the protocol.
if embeddedTime == 0:
embeddedTime, = unpack('>Q', data[readPosition:readPosition + 8])
readPosition += 8
else:
readPosition += 4
readPosition = 20 # bypass the nonce, time, and object type
"""
In protocol v2, the next byte(s) was the streamNumber. But starting after
the protocol v3 upgrade period, the next byte(s) will be a msg version
number followed by the streamNumber.
"""
#msgVersionOutsideEncryption, msgVersionOutsideEncryptionLength = decodeVarint(data[readPosition:readPosition + 9])
#readPosition += msgVersionOutsideEncryptionLength
streamNumberAsClaimedByMsg, streamNumberAsClaimedByMsgLength = decodeVarint(
data[readPosition:readPosition + 9])
readPosition += streamNumberAsClaimedByMsgLength
inventoryHash = calculateInventoryHash(data)
initialDecryptionSuccessful = False
# Let's check whether this is a message acknowledgement bound for us.
if data[readPosition:] in shared.ackdataForWhichImWatching:
if data[-32:] in shared.ackdataForWhichImWatching:
logger.info('This msg IS an acknowledgement bound for me.')
del shared.ackdataForWhichImWatching[data[readPosition:]]
del shared.ackdataForWhichImWatching[data[-32:]]
sqlExecute('UPDATE sent SET status=? WHERE ackdata=?',
'ackreceived', data[readPosition:])
shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (data[readPosition:], tr.translateText("MainWindow",'Acknowledgement of the message received. %1').arg(l10n.formatTimestamp()))))
'ackreceived', data[-32:])
shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (data[-32:], tr.translateText("MainWindow",'Acknowledgement of the message received. %1').arg(l10n.formatTimestamp()))))
return
else:
logger.info('This was NOT an acknowledgement bound for me.')
@ -430,17 +378,35 @@ class objectProcessor(threading.Thread):
# 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 can be simplified quite a bit after 1416175200: # Sun, 16 Nov 2014 22:00:00 GMT
for key, cryptorObject in shared.myECCryptorObjects.items():
try:
decryptedData = cryptorObject.decrypt(
data[readPosition:])
decryptedData = cryptorObject.decrypt(data[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.
initialDecryptionSuccessful = True
logger.info('EC decryption successful using key associated with ripe hash: %s' % key.encode('hex'))
logger.info('EC decryption successful using key associated with ripe hash: %s. msg did NOT specify version.' % key.encode('hex'))
# We didn't bypass a msg version above as it is commented out.
# But the decryption was successful. Which means that there
# wasn't a msg version byte include in this msg.
msgObjectContainedVersion = False
break
except Exception as err:
pass
# print 'cryptorObject.decrypt Exception:', err
# What if a client sent us a msg with
# a msg version included? We didn't bypass it above. So
# let's try to decrypt the msg assuming that it is present.
try:
decryptedData = cryptorObject.decrypt(data[readPosition+1:]) # notice that we offset by 1 byte compared to the attempt above.
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
logger.info('EC decryption successful using key associated with ripe hash: %s. msg DID specifiy version.' % key.encode('hex'))
# There IS a msg version byte include in this msg.
msgObjectContainedVersion = True
break
except Exception as err:
pass
if not initialDecryptionSuccessful:
# This is not a message bound for me.
logger.info('Length of time program spent failing to decrypt this message: %s seconds.' % (time.time() - messageProcessingStartTime,))
@ -450,12 +416,15 @@ class objectProcessor(threading.Thread):
toAddress = shared.myAddressesByHash[
toRipe] # Look up my address based on the RIPE hash.
readPosition = 0
messageVersion, messageVersionLength = decodeVarint(
decryptedData[readPosition:readPosition + 10])
readPosition += messageVersionLength
if messageVersion != 1:
logger.info('Cannot understand message versions other than one. Ignoring message.')
return
if not msgObjectContainedVersion: # by which I mean "if the msg object didn't have the msg version outside of the encryption". This confusingness will be removed after the protocol v3 upgrade period.
messageVersionWithinEncryption, messageVersionWithinEncryptionLength = decodeVarint(
decryptedData[readPosition:readPosition + 10])
readPosition += messageVersionWithinEncryptionLength
if messageVersionWithinEncryption != 1:
logger.info('Cannot understand message versions other than one. Ignoring message.')
return
else:
messageVersionWithinEncryptionLength = 0 # This variable can disappear after the protocol v3 upgrade period is complete.
sendersAddressVersionNumber, sendersAddressVersionNumberLength = decodeVarint(
decryptedData[readPosition:readPosition + 10])
readPosition += sendersAddressVersionNumberLength