Merge pull request #506 from Bitmessage/encrypted_pubkeys
Encrypted pubkeys
This commit is contained in:
commit
d261829efb
2
Makefile
2
Makefile
|
@ -1,5 +1,5 @@
|
|||
APP=pybitmessage
|
||||
VERSION=0.3.5
|
||||
VERSION=0.4.0
|
||||
RELEASE=1
|
||||
ARCH_TYPE=`uname -m`
|
||||
PREFIX?=/usr/local
|
||||
|
|
4
arch.sh
4
arch.sh
|
@ -2,8 +2,8 @@
|
|||
|
||||
GIT_APP=PyBitmessage
|
||||
APP=pybitmessage
|
||||
PREV_VERSION=0.3.5
|
||||
VERSION=0.3.5
|
||||
PREV_VERSION=0.4.0
|
||||
VERSION=0.4.0
|
||||
RELEASE=1
|
||||
ARCH_TYPE=any
|
||||
CURRDIR=`pwd`
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Maintainer: Bob Mottram (4096 bits) <bob@robotics.uk.to>
|
||||
pkgname=pybitmessage
|
||||
pkgver=0.3.5
|
||||
pkgver=0.4.0
|
||||
pkgrel=1
|
||||
pkgdesc="Bitmessage is a P2P communications protocol used to send encrypted messages to another person or to many subscribers. It is decentralized and trustless, meaning that you need-not inherently trust any entities like root certificate authorities. It uses strong authentication which means that the sender of a message cannot be spoofed, and it aims to hide "non-content" data, like the sender and receiver of messages, from passive eavesdroppers like those running warrantless wiretapping programs."
|
||||
arch=('any')
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#!/bin/bash
|
||||
|
||||
APP=pybitmessage
|
||||
PREV_VERSION=0.3.5
|
||||
VERSION=0.3.5
|
||||
PREV_VERSION=0.4.0
|
||||
VERSION=0.4.0
|
||||
RELEASE=1
|
||||
ARCH_TYPE=all
|
||||
DIR=${APP}-${VERSION}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#!/bin/bash
|
||||
|
||||
APP=pybitmessage
|
||||
PREV_VERSION=0.3.5
|
||||
VERSION=0.3.5
|
||||
PREV_VERSION=0.4.0
|
||||
VERSION=0.4.0
|
||||
RELEASE=1
|
||||
SOURCEDIR=.
|
||||
ARCH_TYPE=`uname -m`
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
rm -f Makefile rpmpackage/*.spec
|
||||
|
||||
packagemonkey -n "PyBitmessage" --version "0.3.5" --dir "." -l "mit" \
|
||||
packagemonkey -n "PyBitmessage" --version "0.4.0" --dir "." -l "mit" \
|
||||
-e "Bob Mottram (4096 bits) <bob@robotics.uk.to>" \
|
||||
--brief "Send encrypted messages" \
|
||||
--desc "Bitmessage is a P2P communications protocol used to send " \
|
||||
|
|
4
puppy.sh
4
puppy.sh
|
@ -1,8 +1,8 @@
|
|||
#!/bin/bash
|
||||
|
||||
APP=pybitmessage
|
||||
PREV_VERSION=0.3.5
|
||||
VERSION=0.3.5
|
||||
PREV_VERSION=0.4.0
|
||||
VERSION=0.4.0
|
||||
RELEASE=1
|
||||
BUILDDIR=~/petbuild
|
||||
CURRDIR=`pwd`
|
||||
|
|
4
rpm.sh
4
rpm.sh
|
@ -1,8 +1,8 @@
|
|||
#!/bin/bash
|
||||
|
||||
APP=pybitmessage
|
||||
PREV_VERSION=0.3.5
|
||||
VERSION=0.3.5
|
||||
PREV_VERSION=0.4.0
|
||||
VERSION=0.4.0
|
||||
RELEASE=1
|
||||
SOURCEDIR=.
|
||||
ARCH_TYPE=`uname -m`
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
Name: pybitmessage
|
||||
Version: 0.3.5
|
||||
Version: 0.4.0
|
||||
Release: 1%{?dist}
|
||||
Summary: Send encrypted messages
|
||||
License: MIT
|
||||
|
|
4
slack.sh
4
slack.sh
|
@ -1,8 +1,8 @@
|
|||
#!/bin/bash
|
||||
|
||||
APP=pybitmessage
|
||||
PREV_VERSION=0.3.5
|
||||
VERSION=0.3.5
|
||||
PREV_VERSION=0.4.0
|
||||
VERSION=0.4.0
|
||||
RELEASE=1
|
||||
ARCH_TYPE=`uname -m`
|
||||
BUILDDIR=~/slackbuild
|
||||
|
|
|
@ -204,7 +204,6 @@ def decodeAddress(address):
|
|||
else:
|
||||
x00string = '\x00' * (20 - len(data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4]))
|
||||
return status,addressVersionNumber,streamNumber,x00string+data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4]
|
||||
|
||||
|
||||
def addBMIfNotPresent(address):
|
||||
address = str(address).strip()
|
||||
|
@ -213,56 +212,6 @@ def addBMIfNotPresent(address):
|
|||
else:
|
||||
return address
|
||||
|
||||
def addressStream(address):
|
||||
#returns the stream number of an address or False if there is a problem with the address.
|
||||
|
||||
#check for the BM- at the front of the address. If it isn't there, this address might be for a different version of Bitmessage
|
||||
if address[:3] != 'BM-':
|
||||
status = 'missingbm'
|
||||
return False
|
||||
#here we take off the BM-
|
||||
integer = decodeBase58(address[3:])
|
||||
#after converting to hex, the string will be prepended with a 0x and appended with a L
|
||||
hexdata = hex(integer)[2:-1]
|
||||
|
||||
if len(hexdata) % 2 != 0:
|
||||
hexdata = '0' + hexdata
|
||||
|
||||
#print 'hexdata', hexdata
|
||||
|
||||
data = hexdata.decode('hex')
|
||||
checksum = data[-4:]
|
||||
|
||||
sha = hashlib.new('sha512')
|
||||
sha.update(data[:-4])
|
||||
currentHash = sha.digest()
|
||||
#print 'sha after first hashing: ', sha.hexdigest()
|
||||
sha = hashlib.new('sha512')
|
||||
sha.update(currentHash)
|
||||
#print 'sha after second hashing: ', sha.hexdigest()
|
||||
|
||||
if checksum != sha.digest()[0:4]:
|
||||
print 'checksum failed'
|
||||
status = 'checksumfailed'
|
||||
return False
|
||||
#else:
|
||||
# print 'checksum PASSED'
|
||||
|
||||
addressVersionNumber, bytesUsedByVersionNumber = decodeVarint(data[:9])
|
||||
#print 'addressVersionNumber', addressVersionNumber
|
||||
#print 'bytesUsedByVersionNumber', bytesUsedByVersionNumber
|
||||
|
||||
if addressVersionNumber < 1:
|
||||
print 'cannot decode version address version numbers this high'
|
||||
status = 'versiontoohigh'
|
||||
return False
|
||||
|
||||
streamNumber, bytesUsedByStreamNumber = decodeVarint(data[bytesUsedByVersionNumber:9+bytesUsedByVersionNumber])
|
||||
#print streamNumber
|
||||
status = 'success'
|
||||
return streamNumber
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print 'Let us make an address from scratch. Suppose we generate two random 32 byte values and call the first one the signing key and the second one the encryption key:'
|
||||
privateSigningKey = '93d0b61371a54b53df143b954035d612f8efa8a3ed1cf842c2186bfd8f876665'
|
||||
|
|
|
@ -31,12 +31,12 @@ print 'Uncomment the next two lines to create a new random address with a slight
|
|||
|
||||
print 'Uncomment these next four lines to create new deterministic addresses.'
|
||||
#passphrase = 'asdfasdfqwser'.encode('base64')
|
||||
#jsonDeterministicAddresses = api.createDeterministicAddresses(passphrase, 2, 3, 1, False)
|
||||
#jsonDeterministicAddresses = api.createDeterministicAddresses(passphrase, 2, 4, 1, False)
|
||||
#print jsonDeterministicAddresses
|
||||
#print json.loads(jsonDeterministicAddresses)
|
||||
|
||||
#print 'Uncomment this next line to print the first deterministic address that would be generated with the given passphrase. This will Not add it to the Bitmessage interface or the keys.dat file.'
|
||||
#print api.getDeterministicAddress('asdfasdfqwser'.encode('base64'),3,1)
|
||||
#print api.getDeterministicAddress('asdfasdfqwser'.encode('base64'),4,1)
|
||||
|
||||
#print 'Uncomment this line to subscribe to an address. (You must use your own address, this one is invalid).'
|
||||
#print api.addSubscription('2D94G5d8yp237GGqAheoecBYpdehdT3dha','test sub'.encode('base64'))
|
||||
|
|
|
@ -182,7 +182,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
|||
elif method == 'statusBar':
|
||||
message, = params
|
||||
shared.UISignalQueue.put(('updateStatusBar', message))
|
||||
elif method == 'listAddresses':
|
||||
elif method == 'listAddresses' or method == 'listAddresses2':
|
||||
data = '{"addresses":['
|
||||
configSections = shared.config.sections()
|
||||
for addressInKeysFile in configSections:
|
||||
|
@ -196,7 +196,10 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
|||
chan = shared.config.getboolean(addressInKeysFile, 'chan')
|
||||
else:
|
||||
chan = False
|
||||
data += json.dumps({'label': shared.config.get(addressInKeysFile, 'label'), 'address': addressInKeysFile, 'stream':
|
||||
label = shared.config.get(addressInKeysFile, 'label')
|
||||
if method == 'listAddresses2':
|
||||
label = label.encode('base64')
|
||||
data += json.dumps({'label': label, 'address': addressInKeysFile, 'stream':
|
||||
streamNumber, 'enabled': shared.config.getboolean(addressInKeysFile, 'enabled'), 'chan': chan}, indent=4, separators=(',', ': '))
|
||||
data += ']}'
|
||||
return data
|
||||
|
@ -276,7 +279,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
|||
shared.apiAddressGeneratorReturnQueue.queue.clear()
|
||||
streamNumberForAddress = 1
|
||||
shared.addressGeneratorQueue.put((
|
||||
'createRandomAddress', 3, streamNumberForAddress, label, 1, "", eighteenByteRipe, nonceTrialsPerByte, payloadLengthExtraBytes))
|
||||
'createRandomAddress', 4, streamNumberForAddress, label, 1, "", eighteenByteRipe, nonceTrialsPerByte, payloadLengthExtraBytes))
|
||||
return shared.apiAddressGeneratorReturnQueue.get()
|
||||
elif method == 'createDeterministicAddresses':
|
||||
if len(params) == 0:
|
||||
|
@ -341,9 +344,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
|||
raise APIError(23, 'Bool expected in eighteenByteRipe, saw %s instead' % type(eighteenByteRipe))
|
||||
passphrase = self._decode(passphrase, "base64")
|
||||
if addressVersionNumber == 0: # 0 means "just use the proper addressVersionNumber"
|
||||
addressVersionNumber = 3
|
||||
if addressVersionNumber != 3:
|
||||
raise APIError(2,'The address version number currently must be 3 (or 0 which means auto-select). ' + addressVersionNumber + ' isn\'t supported.')
|
||||
addressVersionNumber = 4
|
||||
if addressVersionNumber != 3 and addressVersionNumber != 4:
|
||||
raise APIError(2,'The address version number currently must be 3, 4, or 0 (which means auto-select). ' + addressVersionNumber + ' isn\'t supported.')
|
||||
if streamNumber == 0: # 0 means "just use the most available stream"
|
||||
streamNumber = 1
|
||||
if streamNumber != 1:
|
||||
|
@ -374,8 +377,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
|||
if len(passphrase) == 0:
|
||||
raise APIError(1, 'The specified passphrase is blank.')
|
||||
passphrase = self._decode(passphrase, "base64")
|
||||
if addressVersionNumber != 3:
|
||||
raise APIError(2, 'The address version number currently must be 3. ' + addressVersionNumber + ' isn\'t supported.')
|
||||
if addressVersionNumber != 3 and addressVersionNumber != 4:
|
||||
raise APIError(2, 'The address version number currently must be 3 or 4. ' + addressVersionNumber + ' isn\'t supported.')
|
||||
if streamNumber != 1:
|
||||
raise APIError(3, ' The stream number must be 1. Others aren\'t supported.')
|
||||
shared.apiAddressGeneratorReturnQueue.queue.clear()
|
||||
|
@ -712,7 +715,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
|||
inventoryHash = calculateInventoryHash(encryptedPayload)
|
||||
objectType = 'msg'
|
||||
shared.inventory[inventoryHash] = (
|
||||
objectType, toStreamNumber, encryptedPayload, int(time.time()))
|
||||
objectType, toStreamNumber, encryptedPayload, int(time.time()),'')
|
||||
shared.inventorySets[toStreamNumber].add(inventoryHash)
|
||||
with shared.printLock:
|
||||
print 'Broadcasting inv for msg(API disseminatePreEncryptedMsg command):', inventoryHash.encode('hex')
|
||||
|
@ -748,14 +751,15 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
|||
pubkeyStreamNumber = decodeVarint(payload[pubkeyReadPosition:pubkeyReadPosition+10])[0]
|
||||
inventoryHash = calculateInventoryHash(payload)
|
||||
objectType = 'pubkey'
|
||||
#todo: support v4 pubkeys
|
||||
shared.inventory[inventoryHash] = (
|
||||
objectType, pubkeyStreamNumber, payload, int(time.time()))
|
||||
objectType, pubkeyStreamNumber, payload, int(time.time()),'')
|
||||
shared.inventorySets[pubkeyStreamNumber].add(inventoryHash)
|
||||
with shared.printLock:
|
||||
print 'broadcasting inv within API command disseminatePubkey with hash:', inventoryHash.encode('hex')
|
||||
shared.broadcastToSendDataQueues((
|
||||
streamNumber, 'advertiseobject', inventoryHash))
|
||||
elif method == 'getMessageDataByDestinationHash':
|
||||
elif method == 'getMessageDataByDestinationHash' or method == 'getMessageDataByDestinationTag':
|
||||
# Method will eventually be used by a particular Android app to
|
||||
# select relevant messages. Do not yet add this to the api
|
||||
# doc.
|
||||
|
@ -763,24 +767,24 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
|||
if len(params) != 1:
|
||||
raise APIError(0, 'I need 1 parameter!')
|
||||
requestedHash, = params
|
||||
if len(requestedHash) != 40:
|
||||
raise APIError(19, 'The length of hash should be 20 bytes (encoded in hex thus 40 characters).')
|
||||
if len(requestedHash) != 32:
|
||||
raise APIError(19, 'The length of hash should be 32 bytes (encoded in hex thus 64 characters).')
|
||||
requestedHash = self._decode(requestedHash, "hex")
|
||||
|
||||
# This is not a particularly commonly used API function. Before we
|
||||
# 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 first20bytesofencryptedmessage = '' and objecttype = 'msg' ; ''')
|
||||
'''SELECT hash, payload FROM inventory WHERE tag = '' and objecttype = 'msg' ; ''')
|
||||
with SqlBulkExecute() as sql:
|
||||
for row in queryreturn:
|
||||
hash, payload = row
|
||||
readPosition = 16 # Nonce length + time length
|
||||
readPosition += decodeVarint(payload[readPosition:readPosition+10])[1] # Stream Number length
|
||||
t = (payload[readPosition:readPosition+20],hash)
|
||||
sql.execute('''UPDATE inventory SET first20bytesofencryptedmessage=? WHERE hash=?; ''', *t)
|
||||
t = (payload[readPosition:readPosition+32],hash)
|
||||
sql.execute('''UPDATE inventory SET tag=? WHERE hash=?; ''', *t)
|
||||
|
||||
queryreturn = sqlQuery('''SELECT payload FROM inventory WHERE first20bytesofencryptedmessage = ?''',
|
||||
queryreturn = sqlQuery('''SELECT payload FROM inventory WHERE tag = ?''',
|
||||
requestedHash)
|
||||
data = '{"receivedMessageDatas":['
|
||||
for row in queryreturn:
|
||||
|
@ -813,7 +817,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
|||
networkStatus = 'connectedButHaveNotReceivedIncomingConnections'
|
||||
else:
|
||||
networkStatus = 'connectedAndReceivingIncomingConnections'
|
||||
return json.dumps({'networkConnections':len(shared.connectedHostsList),'numberOfMessagesProcessed':shared.numberOfMessagesProcessed, 'numberOfBroadcastsProcessed':shared.numberOfBroadcastsProcessed, 'numberOfPubkeysProcessed':shared.numberOfPubkeysProcessed, 'networkStatus':networkStatus}, indent=4, separators=(',', ': '))
|
||||
return json.dumps({'networkConnections':len(shared.connectedHostsList),'numberOfMessagesProcessed':shared.numberOfMessagesProcessed, 'numberOfBroadcastsProcessed':shared.numberOfBroadcastsProcessed, 'numberOfPubkeysProcessed':shared.numberOfPubkeysProcessed, 'networkStatus':networkStatus, 'softwareName':'PyBitmessage','softwareVersion':shared.softwareVersion}, indent=4, separators=(',', ': '))
|
||||
else:
|
||||
raise APIError(20, 'Invalid method: %s' % method)
|
||||
|
||||
|
|
|
@ -334,7 +334,7 @@ class MyForm(QtGui.QMainWindow):
|
|||
newItem.setTextColor(QtGui.QColor(137, 04, 177)) # magenta
|
||||
self.ui.tableWidgetYourIdentities.setItem(0, 1, newItem)
|
||||
newItem = QtGui.QTableWidgetItem(str(
|
||||
addressStream(addressInKeysFile)))
|
||||
decodeAddress(addressInKeysFile)[2]))
|
||||
newItem.setFlags(
|
||||
QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
if not isEnabled:
|
||||
|
@ -1133,8 +1133,15 @@ class MyForm(QtGui.QMainWindow):
|
|||
else:
|
||||
streamNumberForAddress = int(
|
||||
self.regenerateAddressesDialogInstance.ui.lineEditStreamNumber.text())
|
||||
addressVersionNumber = int(
|
||||
self.regenerateAddressesDialogInstance.ui.lineEditAddressVersionNumber.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)
|
||||
|
@ -1153,7 +1160,7 @@ class MyForm(QtGui.QMainWindow):
|
|||
"MainWindow", "You didn't enter a chan name."))
|
||||
return
|
||||
shared.apiAddressGeneratorReturnQueue.queue.clear()
|
||||
shared.addressGeneratorQueue.put(('createChan', 3, 1, self.str_chan + ' ' + str(self.newChanDialogInstance.ui.lineEditChanNameCreate.text().toUtf8()), self.newChanDialogInstance.ui.lineEditChanNameCreate.text().toUtf8()))
|
||||
shared.addressGeneratorQueue.put(('createChan', 4, 1, self.str_chan + ' ' + str(self.newChanDialogInstance.ui.lineEditChanNameCreate.text().toUtf8()), self.newChanDialogInstance.ui.lineEditChanNameCreate.text().toUtf8()))
|
||||
addressGeneratorReturnValue = shared.apiAddressGeneratorReturnQueue.get()
|
||||
print 'addressGeneratorReturnValue', addressGeneratorReturnValue
|
||||
if len(addressGeneratorReturnValue) == 0:
|
||||
|
@ -1566,7 +1573,7 @@ class MyForm(QtGui.QMainWindow):
|
|||
continue
|
||||
except:
|
||||
pass
|
||||
if addressVersionNumber > 3 or addressVersionNumber <= 1:
|
||||
if addressVersionNumber > 4 or addressVersionNumber <= 1:
|
||||
QMessageBox.about(self, _translate("MainWindow", "Address version number"), _translate(
|
||||
"MainWindow", "Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version.").arg(toAddress).arg(str(addressVersionNumber)))
|
||||
continue
|
||||
|
@ -2200,9 +2207,9 @@ class MyForm(QtGui.QMainWindow):
|
|||
else:
|
||||
# User selected 'Use the same stream as an existing
|
||||
# address.'
|
||||
streamNumberForAddress = addressStream(
|
||||
self.dialog.ui.comboBoxExisting.currentText())
|
||||
shared.addressGeneratorQueue.put(('createRandomAddress', 3, streamNumberForAddress, str(
|
||||
streamNumberForAddress = decodeAddress(
|
||||
self.dialog.ui.comboBoxExisting.currentText())[2]
|
||||
shared.addressGeneratorQueue.put(('createRandomAddress', 4, streamNumberForAddress, str(
|
||||
self.dialog.ui.newaddresslabel.text().toUtf8()), 1, "", self.dialog.ui.checkBoxEighteenByteRipe.isChecked()))
|
||||
else:
|
||||
if self.dialog.ui.lineEditPassphrase.text() != self.dialog.ui.lineEditPassphraseAgain.text():
|
||||
|
@ -2213,7 +2220,7 @@ class MyForm(QtGui.QMainWindow):
|
|||
"MainWindow", "Choose a passphrase"), _translate("MainWindow", "You really do need a passphrase."))
|
||||
else:
|
||||
streamNumberForAddress = 1 # this will eventually have to be replaced by logic to determine the most available stream number.
|
||||
shared.addressGeneratorQueue.put(('createDeterministicAddresses', 3, streamNumberForAddress, "unused deterministic address", self.dialog.ui.spinBoxNumberOfAddressesToMake.value(
|
||||
shared.addressGeneratorQueue.put(('createDeterministicAddresses', 4, streamNumberForAddress, "unused deterministic address", self.dialog.ui.spinBoxNumberOfAddressesToMake.value(
|
||||
), self.dialog.ui.lineEditPassphrase.text().toUtf8(), self.dialog.ui.checkBoxEighteenByteRipe.isChecked()))
|
||||
else:
|
||||
print 'new address dialog box rejected'
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
# Form implementation generated from reading ui file 'newaddressdialog.ui'
|
||||
#
|
||||
# Created: Thu Jun 13 20:12:21 2013
|
||||
# by: PyQt4 UI code generator 4.10.1
|
||||
# Created: Sun Sep 15 23:53:31 2013
|
||||
# by: PyQt4 UI code generator 4.10.2
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
|
@ -178,7 +178,7 @@ class Ui_NewAddressDialog(object):
|
|||
self.radioButtonDeterministicAddress.setText(_translate("NewAddressDialog", "Use a passphrase to make addresses", None))
|
||||
self.checkBoxEighteenByteRipe.setText(_translate("NewAddressDialog", "Spend several minutes of extra computing time to make the address(es) 1 or 2 characters shorter", None))
|
||||
self.groupBoxDeterministic.setTitle(_translate("NewAddressDialog", "Make deterministic addresses", None))
|
||||
self.label_9.setText(_translate("NewAddressDialog", "Address version number: 3", None))
|
||||
self.label_9.setText(_translate("NewAddressDialog", "Address version number: 4", None))
|
||||
self.label_8.setText(_translate("NewAddressDialog", "In addition to your passphrase, you must remember these numbers:", None))
|
||||
self.label_6.setText(_translate("NewAddressDialog", "Passphrase", None))
|
||||
self.label_11.setText(_translate("NewAddressDialog", "Number of addresses to make based on your passphrase:", None))
|
||||
|
@ -191,13 +191,3 @@ class Ui_NewAddressDialog(object):
|
|||
self.radioButtonExisting.setText(_translate("NewAddressDialog", "Use the same stream as an existing address", None))
|
||||
self.label_4.setText(_translate("NewAddressDialog", "(saves you some bandwidth and processing power)", None))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
app = QtGui.QApplication(sys.argv)
|
||||
NewAddressDialog = QtGui.QDialog()
|
||||
ui = Ui_NewAddressDialog()
|
||||
ui.setupUi(NewAddressDialog)
|
||||
NewAddressDialog.show()
|
||||
sys.exit(app.exec_())
|
||||
|
||||
|
|
|
@ -99,7 +99,7 @@ The 'Random Number' option is selected by default but deterministic addresses ha
|
|||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="text">
|
||||
<string>Address version number: 3</string>
|
||||
<string>Address version number: 4</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
# Form implementation generated from reading ui file 'regenerateaddresses.ui'
|
||||
#
|
||||
# Created: Tue Apr 30 12:21:16 2013
|
||||
# by: PyQt4 UI code generator 4.9.4
|
||||
# Created: Sun Sep 15 23:50:23 2013
|
||||
# by: PyQt4 UI code generator 4.10.2
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
|
@ -12,7 +12,16 @@ from PyQt4 import QtCore, QtGui
|
|||
try:
|
||||
_fromUtf8 = QtCore.QString.fromUtf8
|
||||
except AttributeError:
|
||||
_fromUtf8 = lambda s: s
|
||||
def _fromUtf8(s):
|
||||
return s
|
||||
|
||||
try:
|
||||
_encoding = QtGui.QApplication.UnicodeUTF8
|
||||
def _translate(context, text, disambig):
|
||||
return QtGui.QApplication.translate(context, text, disambig, _encoding)
|
||||
except AttributeError:
|
||||
def _translate(context, text, disambig):
|
||||
return QtGui.QApplication.translate(context, text, disambig)
|
||||
|
||||
class Ui_regenerateAddressesDialog(object):
|
||||
def setupUi(self, regenerateAddressesDialog):
|
||||
|
@ -56,13 +65,14 @@ class Ui_regenerateAddressesDialog(object):
|
|||
self.label_2.setObjectName(_fromUtf8("label_2"))
|
||||
self.gridLayout.addWidget(self.label_2, 4, 0, 1, 1)
|
||||
self.lineEditAddressVersionNumber = QtGui.QLineEdit(self.groupBox)
|
||||
self.lineEditAddressVersionNumber.setEnabled(False)
|
||||
self.lineEditAddressVersionNumber.setEnabled(True)
|
||||
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.lineEditAddressVersionNumber.sizePolicy().hasHeightForWidth())
|
||||
self.lineEditAddressVersionNumber.setSizePolicy(sizePolicy)
|
||||
self.lineEditAddressVersionNumber.setMaximumSize(QtCore.QSize(31, 16777215))
|
||||
self.lineEditAddressVersionNumber.setText(_fromUtf8(""))
|
||||
self.lineEditAddressVersionNumber.setObjectName(_fromUtf8("lineEditAddressVersionNumber"))
|
||||
self.gridLayout.addWidget(self.lineEditAddressVersionNumber, 4, 1, 1, 1)
|
||||
spacerItem1 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
|
||||
|
@ -101,15 +111,14 @@ class Ui_regenerateAddressesDialog(object):
|
|||
QtCore.QMetaObject.connectSlotsByName(regenerateAddressesDialog)
|
||||
|
||||
def retranslateUi(self, regenerateAddressesDialog):
|
||||
regenerateAddressesDialog.setWindowTitle(QtGui.QApplication.translate("regenerateAddressesDialog", "Regenerate Existing Addresses", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.groupBox.setTitle(QtGui.QApplication.translate("regenerateAddressesDialog", "Regenerate existing addresses", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.label_6.setText(QtGui.QApplication.translate("regenerateAddressesDialog", "Passphrase", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.label_11.setText(QtGui.QApplication.translate("regenerateAddressesDialog", "Number of addresses to make based on your passphrase:", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.label_2.setText(QtGui.QApplication.translate("regenerateAddressesDialog", "Address version Number:", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.lineEditAddressVersionNumber.setText(QtGui.QApplication.translate("regenerateAddressesDialog", "3", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.label_3.setText(QtGui.QApplication.translate("regenerateAddressesDialog", "Stream number:", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.lineEditStreamNumber.setText(QtGui.QApplication.translate("regenerateAddressesDialog", "1", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.checkBoxEighteenByteRipe.setText(QtGui.QApplication.translate("regenerateAddressesDialog", "Spend several minutes of extra computing time to make the address(es) 1 or 2 characters shorter", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.label_4.setText(QtGui.QApplication.translate("regenerateAddressesDialog", "You must check (or not check) this box just like you did (or didn\'t) when you made your addresses the first time.", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.label.setText(QtGui.QApplication.translate("regenerateAddressesDialog", "If you have previously made deterministic addresses but lost them due to an accident (like hard drive failure), you can regenerate them here. If you used the random number generator to make your addresses then this form will be of no use to you.", None, QtGui.QApplication.UnicodeUTF8))
|
||||
regenerateAddressesDialog.setWindowTitle(_translate("regenerateAddressesDialog", "Regenerate Existing Addresses", None))
|
||||
self.groupBox.setTitle(_translate("regenerateAddressesDialog", "Regenerate existing addresses", None))
|
||||
self.label_6.setText(_translate("regenerateAddressesDialog", "Passphrase", None))
|
||||
self.label_11.setText(_translate("regenerateAddressesDialog", "Number of addresses to make based on your passphrase:", None))
|
||||
self.label_2.setText(_translate("regenerateAddressesDialog", "Address version number:", None))
|
||||
self.label_3.setText(_translate("regenerateAddressesDialog", "Stream number:", None))
|
||||
self.lineEditStreamNumber.setText(_translate("regenerateAddressesDialog", "1", None))
|
||||
self.checkBoxEighteenByteRipe.setText(_translate("regenerateAddressesDialog", "Spend several minutes of extra computing time to make the address(es) 1 or 2 characters shorter", None))
|
||||
self.label_4.setText(_translate("regenerateAddressesDialog", "You must check (or not check) this box just like you did (or didn\'t) when you made your addresses the first time.", None))
|
||||
self.label.setText(_translate("regenerateAddressesDialog", "If you have previously made deterministic addresses but lost them due to an accident (like hard drive failure), you can regenerate them here. If you used the random number generator to make your addresses then this form will be of no use to you.", None))
|
||||
|
||||
|
|
|
@ -86,14 +86,14 @@
|
|||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Address version Number:</string>
|
||||
<string>Address version number:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QLineEdit" name="lineEditAddressVersionNumber">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
|
@ -108,7 +108,7 @@
|
|||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>3</string>
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from setuptools import setup
|
||||
|
||||
name = "Bitmessage"
|
||||
version = "0.3.5"
|
||||
version = "0.4.0"
|
||||
mainscript = ["bitmessagemain.py"]
|
||||
|
||||
setup(
|
||||
|
|
|
@ -25,20 +25,38 @@ class addressGenerator(threading.Thread):
|
|||
command, addressVersionNumber, streamNumber, label, deterministicPassphrase = queueValue
|
||||
eighteenByteRipe = False
|
||||
numberOfAddressesToMake = 1
|
||||
numberOfNullBytesDemandedOnFrontOfRipeHash = 1
|
||||
elif queueValue[0] == 'joinChan':
|
||||
command, chanAddress, label, deterministicPassphrase = queueValue
|
||||
eighteenByteRipe = False
|
||||
addressVersionNumber = decodeAddress(chanAddress)[1]
|
||||
streamNumber = decodeAddress(chanAddress)[2]
|
||||
numberOfAddressesToMake = 1
|
||||
numberOfNullBytesDemandedOnFrontOfRipeHash = 1
|
||||
elif len(queueValue) == 7:
|
||||
command, addressVersionNumber, streamNumber, label, numberOfAddressesToMake, deterministicPassphrase, eighteenByteRipe = queueValue
|
||||
try:
|
||||
numberOfNullBytesDemandedOnFrontOfRipeHash = shared.config.getint(
|
||||
'bitmessagesettings', 'numberofnullbytesonaddress')
|
||||
except:
|
||||
if eighteenByteRipe:
|
||||
numberOfNullBytesDemandedOnFrontOfRipeHash = 2
|
||||
else:
|
||||
numberOfNullBytesDemandedOnFrontOfRipeHash = 1 # the default
|
||||
elif len(queueValue) == 9:
|
||||
command, addressVersionNumber, streamNumber, label, numberOfAddressesToMake, deterministicPassphrase, eighteenByteRipe, nonceTrialsPerByte, payloadLengthExtraBytes = queueValue
|
||||
try:
|
||||
numberOfNullBytesDemandedOnFrontOfRipeHash = shared.config.getint(
|
||||
'bitmessagesettings', 'numberofnullbytesonaddress')
|
||||
except:
|
||||
if eighteenByteRipe:
|
||||
numberOfNullBytesDemandedOnFrontOfRipeHash = 2
|
||||
else:
|
||||
numberOfNullBytesDemandedOnFrontOfRipeHash = 1 # the default
|
||||
else:
|
||||
sys.stderr.write(
|
||||
'Programming error: A structure with the wrong number of values was passed into the addressGeneratorQueue. Here is the queueValue: %s\n' % repr(queueValue))
|
||||
if addressVersionNumber < 3 or addressVersionNumber > 3:
|
||||
if addressVersionNumber < 3 or addressVersionNumber > 4:
|
||||
sys.stderr.write(
|
||||
'Program error: For some reason the address generator queue has been given a request to create at least one version %s address which it cannot do.\n' % addressVersionNumber)
|
||||
if nonceTrialsPerByte == 0:
|
||||
|
@ -51,26 +69,121 @@ class addressGenerator(threading.Thread):
|
|||
'bitmessagesettings', 'defaultpayloadlengthextrabytes')
|
||||
if payloadLengthExtraBytes < shared.networkDefaultPayloadLengthExtraBytes:
|
||||
payloadLengthExtraBytes = shared.networkDefaultPayloadLengthExtraBytes
|
||||
if addressVersionNumber == 3: # currently the only one supported.
|
||||
if command == 'createRandomAddress':
|
||||
if command == 'createRandomAddress':
|
||||
shared.UISignalQueue.put((
|
||||
'updateStatusBar', tr.translateText("MainWindow", "Generating one new address")))
|
||||
# This next section is a little bit strange. We're going to generate keys over and over until we
|
||||
# find one that starts with either \x00 or \x00\x00. Then when we pack them into a Bitmessage address,
|
||||
# we won't store the \x00 or \x00\x00 bytes thus making the
|
||||
# address shorter.
|
||||
startTime = time.time()
|
||||
numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix = 0
|
||||
potentialPrivSigningKey = OpenSSL.rand(32)
|
||||
potentialPubSigningKey = pointMult(potentialPrivSigningKey)
|
||||
while True:
|
||||
numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix += 1
|
||||
potentialPrivEncryptionKey = OpenSSL.rand(32)
|
||||
potentialPubEncryptionKey = pointMult(
|
||||
potentialPrivEncryptionKey)
|
||||
# print 'potentialPubSigningKey', potentialPubSigningKey.encode('hex')
|
||||
# print 'potentialPubEncryptionKey',
|
||||
# potentialPubEncryptionKey.encode('hex')
|
||||
ripe = hashlib.new('ripemd160')
|
||||
sha = hashlib.new('sha512')
|
||||
sha.update(
|
||||
potentialPubSigningKey + potentialPubEncryptionKey)
|
||||
ripe.update(sha.digest())
|
||||
# print 'potential ripe.digest',
|
||||
# ripe.digest().encode('hex')
|
||||
if ripe.digest()[:numberOfNullBytesDemandedOnFrontOfRipeHash] == '\x00' * numberOfNullBytesDemandedOnFrontOfRipeHash:
|
||||
break
|
||||
print 'Generated address with ripe digest:', ripe.digest().encode('hex')
|
||||
print 'Address generator calculated', numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix, 'addresses at', numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix / (time.time() - startTime), 'addresses per second before finding one with the correct ripe-prefix.'
|
||||
address = encodeAddress(addressVersionNumber, streamNumber, ripe.digest())
|
||||
|
||||
# An excellent way for us to store our keys is in Wallet Import Format. Let us convert now.
|
||||
# https://en.bitcoin.it/wiki/Wallet_import_format
|
||||
privSigningKey = '\x80' + potentialPrivSigningKey
|
||||
checksum = hashlib.sha256(hashlib.sha256(
|
||||
privSigningKey).digest()).digest()[0:4]
|
||||
privSigningKeyWIF = arithmetic.changebase(
|
||||
privSigningKey + checksum, 256, 58)
|
||||
# print 'privSigningKeyWIF',privSigningKeyWIF
|
||||
|
||||
privEncryptionKey = '\x80' + potentialPrivEncryptionKey
|
||||
checksum = hashlib.sha256(hashlib.sha256(
|
||||
privEncryptionKey).digest()).digest()[0:4]
|
||||
privEncryptionKeyWIF = arithmetic.changebase(
|
||||
privEncryptionKey + checksum, 256, 58)
|
||||
# print 'privEncryptionKeyWIF',privEncryptionKeyWIF
|
||||
|
||||
shared.config.add_section(address)
|
||||
shared.config.set(address, 'label', label)
|
||||
shared.config.set(address, 'enabled', 'true')
|
||||
shared.config.set(address, 'decoy', 'false')
|
||||
shared.config.set(address, 'noncetrialsperbyte', str(
|
||||
nonceTrialsPerByte))
|
||||
shared.config.set(address, 'payloadlengthextrabytes', str(
|
||||
payloadLengthExtraBytes))
|
||||
shared.config.set(
|
||||
address, 'privSigningKey', privSigningKeyWIF)
|
||||
shared.config.set(
|
||||
address, 'privEncryptionKey', privEncryptionKeyWIF)
|
||||
with open(shared.appdata + 'keys.dat', 'wb') as configfile:
|
||||
shared.config.write(configfile)
|
||||
|
||||
# The API and the join and create Chan functionality
|
||||
# both need information back from the address generator.
|
||||
shared.apiAddressGeneratorReturnQueue.put(address)
|
||||
|
||||
shared.UISignalQueue.put((
|
||||
'updateStatusBar', tr.translateText("MainWindow", "Done generating address. Doing work necessary to broadcast it...")))
|
||||
shared.UISignalQueue.put(('writeNewAddressToTable', (
|
||||
label, address, streamNumber)))
|
||||
shared.reloadMyAddressHashes()
|
||||
if addressVersionNumber == 3:
|
||||
shared.workerQueue.put((
|
||||
'sendOutOrStoreMyV3Pubkey', ripe.digest()))
|
||||
elif addressVersionNumber == 4:
|
||||
shared.workerQueue.put((
|
||||
'sendOutOrStoreMyV4Pubkey', address))
|
||||
|
||||
elif command == 'createDeterministicAddresses' or command == 'getDeterministicAddress' or command == 'createChan' or command == 'joinChan':
|
||||
if len(deterministicPassphrase) == 0:
|
||||
sys.stderr.write(
|
||||
'WARNING: You are creating deterministic address(es) using a blank passphrase. Bitmessage will do it but it is rather stupid.')
|
||||
if command == 'createDeterministicAddresses':
|
||||
statusbar = 'Generating ' + str(
|
||||
numberOfAddressesToMake) + ' new addresses.'
|
||||
shared.UISignalQueue.put((
|
||||
'updateStatusBar', tr.translateText("MainWindow", "Generating one new address")))
|
||||
'updateStatusBar', statusbar))
|
||||
signingKeyNonce = 0
|
||||
encryptionKeyNonce = 1
|
||||
listOfNewAddressesToSendOutThroughTheAPI = [
|
||||
] # We fill out this list no matter what although we only need it if we end up passing the info to the API.
|
||||
|
||||
for i in range(numberOfAddressesToMake):
|
||||
# This next section is a little bit strange. We're going to generate keys over and over until we
|
||||
# find one that starts with either \x00 or \x00\x00. Then when we pack them into a Bitmessage address,
|
||||
# we won't store the \x00 or \x00\x00 bytes thus making the
|
||||
# address shorter.
|
||||
# find one that has a RIPEMD hash that starts with either \x00 or \x00\x00. Then when we pack them
|
||||
# into a Bitmessage address, we won't store the \x00 or
|
||||
# \x00\x00 bytes thus making the address shorter.
|
||||
startTime = time.time()
|
||||
numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix = 0
|
||||
potentialPrivSigningKey = OpenSSL.rand(32)
|
||||
potentialPubSigningKey = pointMult(potentialPrivSigningKey)
|
||||
while True:
|
||||
numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix += 1
|
||||
potentialPrivEncryptionKey = OpenSSL.rand(32)
|
||||
potentialPrivSigningKey = hashlib.sha512(
|
||||
deterministicPassphrase + encodeVarint(signingKeyNonce)).digest()[:32]
|
||||
potentialPrivEncryptionKey = hashlib.sha512(
|
||||
deterministicPassphrase + encodeVarint(encryptionKeyNonce)).digest()[:32]
|
||||
potentialPubSigningKey = pointMult(
|
||||
potentialPrivSigningKey)
|
||||
potentialPubEncryptionKey = pointMult(
|
||||
potentialPrivEncryptionKey)
|
||||
# print 'potentialPubSigningKey', potentialPubSigningKey.encode('hex')
|
||||
# print 'potentialPubEncryptionKey',
|
||||
# potentialPubEncryptionKey.encode('hex')
|
||||
signingKeyNonce += 2
|
||||
encryptionKeyNonce += 2
|
||||
ripe = hashlib.new('ripemd160')
|
||||
sha = hashlib.new('sha512')
|
||||
sha.update(
|
||||
|
@ -78,193 +191,97 @@ class addressGenerator(threading.Thread):
|
|||
ripe.update(sha.digest())
|
||||
# print 'potential ripe.digest',
|
||||
# ripe.digest().encode('hex')
|
||||
if eighteenByteRipe:
|
||||
if ripe.digest()[:2] == '\x00\x00':
|
||||
break
|
||||
else:
|
||||
if ripe.digest()[:1] == '\x00':
|
||||
break
|
||||
print 'Generated address with ripe digest:', ripe.digest().encode('hex')
|
||||
print 'Address generator calculated', numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix, 'addresses at', numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix / (time.time() - startTime), 'addresses per second before finding one with the correct ripe-prefix.'
|
||||
address = encodeAddress(3, streamNumber, ripe.digest())
|
||||
if ripe.digest()[:numberOfNullBytesDemandedOnFrontOfRipeHash] == '\x00' * numberOfNullBytesDemandedOnFrontOfRipeHash:
|
||||
break
|
||||
|
||||
# An excellent way for us to store our keys is in Wallet Import Format. Let us convert now.
|
||||
# https://en.bitcoin.it/wiki/Wallet_import_format
|
||||
privSigningKey = '\x80' + potentialPrivSigningKey
|
||||
checksum = hashlib.sha256(hashlib.sha256(
|
||||
privSigningKey).digest()).digest()[0:4]
|
||||
privSigningKeyWIF = arithmetic.changebase(
|
||||
privSigningKey + checksum, 256, 58)
|
||||
# print 'privSigningKeyWIF',privSigningKeyWIF
|
||||
print 'ripe.digest', ripe.digest().encode('hex')
|
||||
print 'Address generator calculated', numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix, 'addresses at', numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix / (time.time() - startTime), 'keys per second.'
|
||||
address = encodeAddress(addressVersionNumber, streamNumber, ripe.digest())
|
||||
|
||||
privEncryptionKey = '\x80' + potentialPrivEncryptionKey
|
||||
checksum = hashlib.sha256(hashlib.sha256(
|
||||
privEncryptionKey).digest()).digest()[0:4]
|
||||
privEncryptionKeyWIF = arithmetic.changebase(
|
||||
privEncryptionKey + checksum, 256, 58)
|
||||
# print 'privEncryptionKeyWIF',privEncryptionKeyWIF
|
||||
|
||||
shared.config.add_section(address)
|
||||
shared.config.set(address, 'label', label)
|
||||
shared.config.set(address, 'enabled', 'true')
|
||||
shared.config.set(address, 'decoy', 'false')
|
||||
shared.config.set(address, 'noncetrialsperbyte', str(
|
||||
nonceTrialsPerByte))
|
||||
shared.config.set(address, 'payloadlengthextrabytes', str(
|
||||
payloadLengthExtraBytes))
|
||||
shared.config.set(
|
||||
address, 'privSigningKey', privSigningKeyWIF)
|
||||
shared.config.set(
|
||||
address, 'privEncryptionKey', privEncryptionKeyWIF)
|
||||
with open(shared.appdata + 'keys.dat', 'wb') as configfile:
|
||||
shared.config.write(configfile)
|
||||
|
||||
# The API and the join and create Chan functionality
|
||||
# both need information back from the address generator.
|
||||
shared.apiAddressGeneratorReturnQueue.put(address)
|
||||
|
||||
shared.UISignalQueue.put((
|
||||
'updateStatusBar', tr.translateText("MainWindow", "Done generating address. Doing work necessary to broadcast it...")))
|
||||
shared.UISignalQueue.put(('writeNewAddressToTable', (
|
||||
label, address, streamNumber)))
|
||||
shared.reloadMyAddressHashes()
|
||||
shared.workerQueue.put((
|
||||
'sendOutOrStoreMyV3Pubkey', ripe.digest()))
|
||||
|
||||
elif command == 'createDeterministicAddresses' or command == 'getDeterministicAddress' or command == 'createChan' or command == 'joinChan':
|
||||
if len(deterministicPassphrase) == 0:
|
||||
sys.stderr.write(
|
||||
'WARNING: You are creating deterministic address(es) using a blank passphrase. Bitmessage will do it but it is rather stupid.')
|
||||
if command == 'createDeterministicAddresses':
|
||||
statusbar = 'Generating ' + str(
|
||||
numberOfAddressesToMake) + ' new addresses.'
|
||||
shared.UISignalQueue.put((
|
||||
'updateStatusBar', statusbar))
|
||||
signingKeyNonce = 0
|
||||
encryptionKeyNonce = 1
|
||||
listOfNewAddressesToSendOutThroughTheAPI = [
|
||||
] # We fill out this list no matter what although we only need it if we end up passing the info to the API.
|
||||
|
||||
for i in range(numberOfAddressesToMake):
|
||||
# This next section is a little bit strange. We're going to generate keys over and over until we
|
||||
# find one that has a RIPEMD hash that starts with either \x00 or \x00\x00. Then when we pack them
|
||||
# into a Bitmessage address, we won't store the \x00 or
|
||||
# \x00\x00 bytes thus making the address shorter.
|
||||
startTime = time.time()
|
||||
numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix = 0
|
||||
while True:
|
||||
numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix += 1
|
||||
potentialPrivSigningKey = hashlib.sha512(
|
||||
deterministicPassphrase + encodeVarint(signingKeyNonce)).digest()[:32]
|
||||
potentialPrivEncryptionKey = hashlib.sha512(
|
||||
deterministicPassphrase + encodeVarint(encryptionKeyNonce)).digest()[:32]
|
||||
potentialPubSigningKey = pointMult(
|
||||
potentialPrivSigningKey)
|
||||
potentialPubEncryptionKey = pointMult(
|
||||
potentialPrivEncryptionKey)
|
||||
# print 'potentialPubSigningKey', potentialPubSigningKey.encode('hex')
|
||||
# print 'potentialPubEncryptionKey',
|
||||
# potentialPubEncryptionKey.encode('hex')
|
||||
signingKeyNonce += 2
|
||||
encryptionKeyNonce += 2
|
||||
ripe = hashlib.new('ripemd160')
|
||||
sha = hashlib.new('sha512')
|
||||
sha.update(
|
||||
potentialPubSigningKey + potentialPubEncryptionKey)
|
||||
ripe.update(sha.digest())
|
||||
# print 'potential ripe.digest',
|
||||
# ripe.digest().encode('hex')
|
||||
if eighteenByteRipe:
|
||||
if ripe.digest()[:2] == '\x00\x00':
|
||||
break
|
||||
else:
|
||||
if ripe.digest()[:1] == '\x00':
|
||||
break
|
||||
|
||||
print 'ripe.digest', ripe.digest().encode('hex')
|
||||
print 'Address generator calculated', numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix, 'addresses at', numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix / (time.time() - startTime), 'keys per second.'
|
||||
address = encodeAddress(3, streamNumber, ripe.digest())
|
||||
|
||||
saveAddressToDisk = True
|
||||
# If we are joining an existing chan, let us check to make sure it matches the provided Bitmessage address
|
||||
if command == 'joinChan':
|
||||
if address != chanAddress:
|
||||
shared.apiAddressGeneratorReturnQueue.put('chan name does not match address')
|
||||
saveAddressToDisk = False
|
||||
if command == 'getDeterministicAddress':
|
||||
saveAddressToDisk = True
|
||||
# If we are joining an existing chan, let us check to make sure it matches the provided Bitmessage address
|
||||
if command == 'joinChan':
|
||||
if address != chanAddress:
|
||||
shared.apiAddressGeneratorReturnQueue.put('chan name does not match address')
|
||||
saveAddressToDisk = False
|
||||
if command == 'getDeterministicAddress':
|
||||
saveAddressToDisk = False
|
||||
|
||||
if saveAddressToDisk:
|
||||
# An excellent way for us to store our keys is in Wallet Import Format. Let us convert now.
|
||||
# https://en.bitcoin.it/wiki/Wallet_import_format
|
||||
privSigningKey = '\x80' + potentialPrivSigningKey
|
||||
checksum = hashlib.sha256(hashlib.sha256(
|
||||
privSigningKey).digest()).digest()[0:4]
|
||||
privSigningKeyWIF = arithmetic.changebase(
|
||||
privSigningKey + checksum, 256, 58)
|
||||
if saveAddressToDisk:
|
||||
# An excellent way for us to store our keys is in Wallet Import Format. Let us convert now.
|
||||
# https://en.bitcoin.it/wiki/Wallet_import_format
|
||||
privSigningKey = '\x80' + potentialPrivSigningKey
|
||||
checksum = hashlib.sha256(hashlib.sha256(
|
||||
privSigningKey).digest()).digest()[0:4]
|
||||
privSigningKeyWIF = arithmetic.changebase(
|
||||
privSigningKey + checksum, 256, 58)
|
||||
|
||||
privEncryptionKey = '\x80' + \
|
||||
potentialPrivEncryptionKey
|
||||
checksum = hashlib.sha256(hashlib.sha256(
|
||||
privEncryptionKey).digest()).digest()[0:4]
|
||||
privEncryptionKeyWIF = arithmetic.changebase(
|
||||
privEncryptionKey + checksum, 256, 58)
|
||||
privEncryptionKey = '\x80' + \
|
||||
potentialPrivEncryptionKey
|
||||
checksum = hashlib.sha256(hashlib.sha256(
|
||||
privEncryptionKey).digest()).digest()[0:4]
|
||||
privEncryptionKeyWIF = arithmetic.changebase(
|
||||
privEncryptionKey + checksum, 256, 58)
|
||||
|
||||
addressAlreadyExists = False
|
||||
try:
|
||||
shared.config.add_section(address)
|
||||
except:
|
||||
print address, 'already exists. Not adding it again.'
|
||||
addressAlreadyExists = True
|
||||
if not addressAlreadyExists:
|
||||
print 'label:', label
|
||||
shared.config.set(address, 'label', label)
|
||||
shared.config.set(address, 'enabled', 'true')
|
||||
shared.config.set(address, 'decoy', 'false')
|
||||
if command == 'joinChan' or command == 'createChan':
|
||||
shared.config.set(address, 'chan', 'true')
|
||||
shared.config.set(address, 'noncetrialsperbyte', str(
|
||||
nonceTrialsPerByte))
|
||||
shared.config.set(address, 'payloadlengthextrabytes', str(
|
||||
payloadLengthExtraBytes))
|
||||
shared.config.set(
|
||||
address, 'privSigningKey', privSigningKeyWIF)
|
||||
shared.config.set(
|
||||
address, 'privEncryptionKey', privEncryptionKeyWIF)
|
||||
with open(shared.appdata + 'keys.dat', 'wb') as configfile:
|
||||
shared.config.write(configfile)
|
||||
addressAlreadyExists = False
|
||||
try:
|
||||
shared.config.add_section(address)
|
||||
except:
|
||||
print address, 'already exists. Not adding it again.'
|
||||
addressAlreadyExists = True
|
||||
if not addressAlreadyExists:
|
||||
print 'label:', label
|
||||
shared.config.set(address, 'label', label)
|
||||
shared.config.set(address, 'enabled', 'true')
|
||||
shared.config.set(address, 'decoy', 'false')
|
||||
if command == 'joinChan' or command == 'createChan':
|
||||
shared.config.set(address, 'chan', 'true')
|
||||
shared.config.set(address, 'noncetrialsperbyte', str(
|
||||
nonceTrialsPerByte))
|
||||
shared.config.set(address, 'payloadlengthextrabytes', str(
|
||||
payloadLengthExtraBytes))
|
||||
shared.config.set(
|
||||
address, 'privSigningKey', privSigningKeyWIF)
|
||||
shared.config.set(
|
||||
address, 'privEncryptionKey', privEncryptionKeyWIF)
|
||||
with open(shared.appdata + 'keys.dat', 'wb') as configfile:
|
||||
shared.config.write(configfile)
|
||||
|
||||
shared.UISignalQueue.put(('writeNewAddressToTable', (
|
||||
label, address, str(streamNumber))))
|
||||
listOfNewAddressesToSendOutThroughTheAPI.append(
|
||||
address)
|
||||
shared.myECCryptorObjects[ripe.digest()] = highlevelcrypto.makeCryptor(
|
||||
potentialPrivEncryptionKey.encode('hex'))
|
||||
shared.myAddressesByHash[
|
||||
ripe.digest()] = address
|
||||
shared.UISignalQueue.put(('writeNewAddressToTable', (
|
||||
label, address, str(streamNumber))))
|
||||
listOfNewAddressesToSendOutThroughTheAPI.append(
|
||||
address)
|
||||
shared.myECCryptorObjects[ripe.digest()] = highlevelcrypto.makeCryptor(
|
||||
potentialPrivEncryptionKey.encode('hex'))
|
||||
shared.myAddressesByHash[ripe.digest()] = address
|
||||
tag = hashlib.sha512(hashlib.sha512(encodeVarint(
|
||||
addressVersionNumber) + encodeVarint(streamNumber) + ripe.digest()).digest()).digest()[32:]
|
||||
shared.myAddressesByTag[tag] = address
|
||||
if addressVersionNumber == 3:
|
||||
shared.workerQueue.put((
|
||||
'sendOutOrStoreMyV3Pubkey', ripe.digest())) # If this is a chan address,
|
||||
# the worker thread won't send out the pubkey over the network.
|
||||
elif addressVersionNumber == 4:
|
||||
shared.workerQueue.put((
|
||||
'sendOutOrStoreMyV4Pubkey', address))
|
||||
|
||||
|
||||
# Done generating addresses.
|
||||
if command == 'createDeterministicAddresses' or command == 'joinChan' or command == 'createChan':
|
||||
shared.apiAddressGeneratorReturnQueue.put(
|
||||
listOfNewAddressesToSendOutThroughTheAPI)
|
||||
shared.UISignalQueue.put((
|
||||
'updateStatusBar', tr.translateText("MainWindow", "Done generating address")))
|
||||
# shared.reloadMyAddressHashes()
|
||||
elif command == 'getDeterministicAddress':
|
||||
shared.apiAddressGeneratorReturnQueue.put(address)
|
||||
#todo: return things to the API if createChan or joinChan assuming saveAddressToDisk
|
||||
else:
|
||||
raise Exception(
|
||||
"Error in the addressGenerator thread. Thread was given a command it could not understand: " + command)
|
||||
# Done generating addresses.
|
||||
if command == 'createDeterministicAddresses' or command == 'joinChan' or command == 'createChan':
|
||||
shared.apiAddressGeneratorReturnQueue.put(
|
||||
listOfNewAddressesToSendOutThroughTheAPI)
|
||||
shared.UISignalQueue.put((
|
||||
'updateStatusBar', tr.translateText("MainWindow", "Done generating address")))
|
||||
# shared.reloadMyAddressHashes()
|
||||
elif command == 'getDeterministicAddress':
|
||||
shared.apiAddressGeneratorReturnQueue.put(address)
|
||||
#todo: return things to the API if createChan or joinChan assuming saveAddressToDisk
|
||||
else:
|
||||
raise Exception(
|
||||
"Error in the addressGenerator thread. Thread was given a command it could not understand: " + command)
|
||||
|
||||
|
||||
# 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.
|
||||
|
|
|
@ -300,7 +300,7 @@ class receiveDataThread(threading.Thread):
|
|||
with shared.inventoryLock:
|
||||
for hash, storedValue in shared.inventory.items():
|
||||
if hash not in self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware:
|
||||
objectType, streamNumber, payload, receivedTime = storedValue
|
||||
objectType, streamNumber, payload, receivedTime, tag = storedValue
|
||||
if streamNumber == self.streamNumber and receivedTime > int(time.time()) - shared.maximumAgeOfObjectsThatIAdvertiseToOthers:
|
||||
bigInvList[hash] = 0
|
||||
numberOfObjectsInInvMessage = 0
|
||||
|
@ -391,7 +391,7 @@ class receiveDataThread(threading.Thread):
|
|||
# It is valid so far. Let's let our peers know about it.
|
||||
objectType = 'broadcast'
|
||||
shared.inventory[self.inventoryHash] = (
|
||||
objectType, self.streamNumber, data, embeddedTime)
|
||||
objectType, self.streamNumber, data, embeddedTime,'')
|
||||
shared.inventorySets[self.streamNumber].add(self.inventoryHash)
|
||||
shared.inventoryLock.release()
|
||||
self.broadcastinv(self.inventoryHash)
|
||||
|
@ -432,8 +432,8 @@ class receiveDataThread(threading.Thread):
|
|||
broadcastVersion, broadcastVersionLength = decodeVarint(
|
||||
data[readPosition:readPosition + 9])
|
||||
readPosition += broadcastVersionLength
|
||||
if broadcastVersion < 1 or broadcastVersion > 2:
|
||||
print 'Cannot decode incoming broadcast versions higher than 2. Assuming the sender isn\'t being silly, you should upgrade Bitmessage because this message shall be ignored.'
|
||||
if broadcastVersion < 1 or broadcastVersion > 3:
|
||||
print 'Cannot decode incoming broadcast versions higher than 3. Assuming the sender isn\'t 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
|
||||
|
@ -502,6 +502,10 @@ class receiveDataThread(threading.Thread):
|
|||
print 'ECDSA verify failed', err
|
||||
return
|
||||
# verify passed
|
||||
fromAddress = encodeAddress(
|
||||
sendersAddressVersion, sendersStream, ripe.digest())
|
||||
with shared.printLock:
|
||||
print 'fromAddress:', fromAddress
|
||||
|
||||
# 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
|
||||
|
@ -515,16 +519,11 @@ class receiveDataThread(threading.Thread):
|
|||
'\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF' + '\xFF\xFF\xFF\xFF' + data[beginningOfPubkeyPosition:endOfPubkeyPosition],
|
||||
int(time.time()),
|
||||
'yes')
|
||||
# shared.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.
|
||||
self.possibleNewPubkey(ripe.digest())
|
||||
self.possibleNewPubkey(ripe=ripe.digest())
|
||||
|
||||
fromAddress = encodeAddress(
|
||||
sendersAddressVersion, sendersStream, ripe.digest())
|
||||
with shared.printLock:
|
||||
print 'fromAddress:', fromAddress
|
||||
|
||||
if messageEncodingType == 2:
|
||||
subject, body = self.decodeType2Message(message)
|
||||
|
@ -665,7 +664,7 @@ class receiveDataThread(threading.Thread):
|
|||
# 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.
|
||||
self.possibleNewPubkey(ripe.digest())
|
||||
self.possibleNewPubkey(ripe=ripe.digest())
|
||||
|
||||
fromAddress = encodeAddress(
|
||||
sendersAddressVersion, sendersStream, ripe.digest())
|
||||
|
@ -709,6 +708,150 @@ class receiveDataThread(threading.Thread):
|
|||
with shared.printLock:
|
||||
print 'Time spent processing this interesting broadcast:', time.time() - self.messageProcessingStartTime
|
||||
|
||||
if broadcastVersion == 3:
|
||||
cleartextStreamNumber, cleartextStreamNumberLength = decodeVarint(
|
||||
data[readPosition:readPosition + 10])
|
||||
readPosition += cleartextStreamNumberLength
|
||||
embeddedTag = data[readPosition:readPosition+32]
|
||||
readPosition += 32
|
||||
if embeddedTag not in shared.MyECSubscriptionCryptorObjects:
|
||||
with shared.printLock:
|
||||
print 'We\'re not interested in this broadcast.'
|
||||
return
|
||||
# We are interested in this broadcast because of its tag.
|
||||
cryptorObject = shared.MyECSubscriptionCryptorObjects[embeddedTag]
|
||||
try:
|
||||
decryptedData = cryptorObject.decrypt(data[readPosition:])
|
||||
print 'EC decryption successful'
|
||||
except Exception as err:
|
||||
with shared.printLock:
|
||||
print 'Broadcast version 3 decryption Unsuccessful.'
|
||||
return
|
||||
|
||||
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 < 4:
|
||||
print 'Cannot decode senderAddressVersion less than 4 for broadcast version number 3. Assuming the sender isn\'t 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)
|
||||
ripeHasher = hashlib.new('ripemd160')
|
||||
ripeHasher.update(sha.digest())
|
||||
calculatedRipe = ripeHasher.digest()
|
||||
|
||||
calculatedTag = hashlib.sha512(hashlib.sha512(encodeVarint(
|
||||
sendersAddressVersion) + encodeVarint(sendersStream) + calculatedRipe).digest()).digest()[32:]
|
||||
if calculatedTag != embeddedTag:
|
||||
print 'The tag and encryption key used to encrypt this message doesn\'t match the keys inbedded in the message itself. Ignoring message.'
|
||||
return
|
||||
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:
|
||||
if not highlevelcrypto.verify(decryptedData[:readPositionAtBottomOfMessage], signature, sendersPubSigningKey.encode('hex')):
|
||||
print 'ECDSA verify failed'
|
||||
return
|
||||
print 'ECDSA verify passed'
|
||||
except Exception as err:
|
||||
print 'ECDSA verify failed', err
|
||||
return
|
||||
# verify passed
|
||||
|
||||
fromAddress = encodeAddress(
|
||||
sendersAddressVersion, sendersStream, calculatedRipe)
|
||||
with shared.printLock:
|
||||
print 'fromAddress:', fromAddress
|
||||
|
||||
# Let's store the public key in case we want to reply to this person.
|
||||
sqlExecute(
|
||||
'''INSERT INTO pubkeys VALUES (?,?,?,?)''',
|
||||
calculatedRipe,
|
||||
'\x00\x00\x00\x00\x00\x00\x00\x01' + decryptedData[beginningOfPubkeyPosition:endOfPubkeyPosition],
|
||||
int(time.time()),
|
||||
'yes')
|
||||
# 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.
|
||||
self.possibleNewPubkey(address = fromAddress)
|
||||
|
||||
if messageEncodingType == 2:
|
||||
subject, body = self.decodeType2Message(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:
|
||||
|
||||
t = (self.inventoryHash, toAddress, fromAddress, subject, int(
|
||||
time.time()), body, 'inbox', messageEncodingType, 0)
|
||||
helper_inbox.insert(t)
|
||||
|
||||
shared.UISignalQueue.put(('displayNewInboxMessage', (
|
||||
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 shared.safeConfigGetBoolean('bitmessagesettings', 'apienabled'):
|
||||