This repository has been archived on 2024-12-26. You can view files and clone it, but cannot push or open issues or pull requests.
PyBitmessage-2024-12-26/src/class_addressGenerator.py

381 lines
18 KiB
Python
Raw Permalink Normal View History

2019-11-04 15:45:18 +01:00
"""
A thread for creating addresses
"""
import hashlib
import time
2017-09-21 17:24:51 +02:00
from binascii import hexlify
from six.moves import configparser, queue
2017-09-21 17:24:51 +02:00
import defaults
import highlevelcrypto
import queues
import shared
import state
2017-09-21 17:24:51 +02:00
from addresses import decodeAddress, encodeAddress, encodeVarint
from bmconfigparser import config
from fallback import RIPEMD160Hash
from network import StoppableThread
from tr import _translate
2017-09-21 17:24:51 +02:00
class AddressGeneratorException(Exception):
'''Generic AddressGenerator exception'''
pass
class addressGenerator(StoppableThread):
2019-11-04 15:45:18 +01:00
"""A thread for creating addresses"""
name = "addressGenerator"
2017-09-21 17:24:51 +02:00
def stopThread(self):
"""Tell the thread to stop putting a special command to it's queue"""
try:
queues.addressGeneratorQueue.put(("stopThread", "data"))
except queue.Full:
self.logger.error('addressGeneratorQueue is Full')
super(addressGenerator, self).stopThread()
def run(self):
"""
Process the requests for addresses generation
from `.queues.addressGeneratorQueue`
"""
# pylint: disable=too-many-locals,too-many-branches,too-many-statements
# pylint: disable=too-many-nested-blocks
while state.shutdown == 0:
queueValue = queues.addressGeneratorQueue.get()
nonceTrialsPerByte = 0
payloadLengthExtraBytes = 0
live = True
2013-06-26 17:55:33 +02:00
if queueValue[0] == 'createChan':
2017-09-21 17:24:51 +02:00
command, addressVersionNumber, streamNumber, label, \
deterministicPassphrase, live = queueValue
2013-06-26 17:55:33 +02:00
eighteenByteRipe = False
numberOfAddressesToMake = 1
2013-09-18 06:04:01 +02:00
numberOfNullBytesDemandedOnFrontOfRipeHash = 1
2013-06-26 17:55:33 +02:00
elif queueValue[0] == 'joinChan':
2017-09-21 17:24:51 +02:00
command, chanAddress, label, deterministicPassphrase, \
live = queueValue
2013-06-26 17:55:33 +02:00
eighteenByteRipe = False
2013-07-22 07:10:22 +02:00
addressVersionNumber = decodeAddress(chanAddress)[1]
streamNumber = decodeAddress(chanAddress)[2]
2013-06-26 17:55:33 +02:00
numberOfAddressesToMake = 1
2013-09-18 06:04:01 +02:00
numberOfNullBytesDemandedOnFrontOfRipeHash = 1
2013-06-26 17:55:33 +02:00
elif len(queueValue) == 7:
2017-09-21 17:24:51 +02:00
command, addressVersionNumber, streamNumber, label, \
numberOfAddressesToMake, deterministicPassphrase, \
eighteenByteRipe = queueValue
numberOfNullBytesDemandedOnFrontOfRipeHash = \
config.safeGetInt(
'bitmessagesettings',
'numberofnullbytesonaddress',
2 if eighteenByteRipe else 1
)
elif len(queueValue) == 9:
2017-09-21 17:24:51 +02:00
command, addressVersionNumber, streamNumber, label, \
numberOfAddressesToMake, deterministicPassphrase, \
eighteenByteRipe, nonceTrialsPerByte, \
payloadLengthExtraBytes = queueValue
numberOfNullBytesDemandedOnFrontOfRipeHash = \
config.safeGetInt(
'bitmessagesettings',
'numberofnullbytesonaddress',
2 if eighteenByteRipe else 1
)
elif queueValue[0] == 'stopThread':
break
else:
self.logger.error(
2017-09-21 17:24:51 +02:00
'Programming error: A structure with the wrong number'
' of values was passed into the addressGeneratorQueue.'
' Here is the queueValue: %r\n', queueValue)
if addressVersionNumber < 3 or addressVersionNumber > 4:
self.logger.error(
2017-09-21 17:24:51 +02:00
'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:
nonceTrialsPerByte = config.getint(
'bitmessagesettings', 'defaultnoncetrialsperbyte')
2017-09-21 17:24:51 +02:00
if nonceTrialsPerByte < \
defaults.networkDefaultProofOfWorkNonceTrialsPerByte:
nonceTrialsPerByte = \
defaults.networkDefaultProofOfWorkNonceTrialsPerByte
if payloadLengthExtraBytes == 0:
payloadLengthExtraBytes = config.getint(
'bitmessagesettings', 'defaultpayloadlengthextrabytes')
2017-09-21 17:24:51 +02:00
if payloadLengthExtraBytes < \
defaults.networkDefaultPayloadLengthExtraBytes:
payloadLengthExtraBytes = \
defaults.networkDefaultPayloadLengthExtraBytes
if command == 'createRandomAddress':
queues.UISignalQueue.put((
2017-09-21 17:24:51 +02:00
'updateStatusBar',
_translate(
2017-09-21 17:24:51 +02:00
"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
privSigningKey, pubSigningKey = highlevelcrypto.random_keys()
while True:
numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix += 1
potentialPrivEncryptionKey, potentialPubEncryptionKey = \
highlevelcrypto.random_keys()
sha = hashlib.new('sha512')
sha.update(pubSigningKey + potentialPubEncryptionKey)
ripe = RIPEMD160Hash(sha.digest()).digest()
if (
ripe[:numberOfNullBytesDemandedOnFrontOfRipeHash]
== b'\x00' * numberOfNullBytesDemandedOnFrontOfRipeHash
):
2013-09-18 06:04:01 +02:00
break
self.logger.info(
'Generated address with ripe digest: %s', hexlify(ripe))
2015-01-08 23:11:30 +01:00
try:
self.logger.info(
2017-09-21 17:24:51 +02:00
'Address generator calculated %s addresses at %s'
' addresses per second before finding one with'
' the correct ripe-prefix.',
numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix,
numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix
/ (time.time() - startTime))
2015-01-08 23:11:30 +01:00
except ZeroDivisionError:
2017-09-21 17:24:51 +02:00
# The user must have a pretty fast computer.
# time.time() - startTime equaled zero.
2015-01-08 23:11:30 +01:00
pass
2017-09-21 17:24:51 +02:00
address = encodeAddress(
addressVersionNumber, streamNumber, ripe)
privSigningKeyWIF = highlevelcrypto.encodeWalletImportFormat(
privSigningKey)
privEncryptionKeyWIF = highlevelcrypto.encodeWalletImportFormat(
potentialPrivEncryptionKey)
config.add_section(address)
config.set(address, 'label', label)
config.set(address, 'enabled', 'true')
config.set(address, 'decoy', 'false')
config.set(address, 'noncetrialsperbyte', str(
nonceTrialsPerByte))
config.set(address, 'payloadlengthextrabytes', str(
payloadLengthExtraBytes))
config.set(
2022-07-04 17:33:42 +02:00
address, 'privsigningkey', privSigningKeyWIF.decode())
config.set(
2022-07-04 17:33:42 +02:00
address, 'privencryptionkey',
privEncryptionKeyWIF.decode())
config.save()
# The API and the join and create Chan functionality
# both need information back from the address generator.
queues.apiAddressGeneratorReturnQueue.put(address)
queues.UISignalQueue.put((
2017-09-21 17:24:51 +02:00
'updateStatusBar',
_translate(
2017-09-21 17:24:51 +02:00
"MainWindow",
"Done generating address. Doing work necessary"
" to broadcast it...")
))
queues.UISignalQueue.put(('writeNewAddressToTable', (
label, address, streamNumber)))
shared.reloadMyAddressHashes()
if addressVersionNumber == 3:
queues.workerQueue.put((
'sendOutOrStoreMyV3Pubkey', ripe))
elif addressVersionNumber == 4:
queues.workerQueue.put((
'sendOutOrStoreMyV4Pubkey', address))
elif command in (
'createDeterministicAddresses', 'createChan',
'getDeterministicAddress', 'joinChan'
):
2019-11-04 15:45:18 +01:00
if not deterministicPassphrase:
self.logger.warning(
2017-09-21 17:24:51 +02:00
'You are creating deterministic'
' address(es) using a blank passphrase.'
' Bitmessage will do it but it is rather stupid.')
if command == 'createDeterministicAddresses':
queues.UISignalQueue.put((
2017-09-21 17:24:51 +02:00
'updateStatusBar',
_translate(
2017-09-21 17:24:51 +02:00
"MainWindow",
"Generating %1 new addresses."
).arg(str(numberOfAddressesToMake))
))
signingKeyNonce = 0
encryptionKeyNonce = 1
2017-09-21 17:24:51 +02:00
# We fill out this list no matter what although we only
# need it if we end up passing the info to the API.
listOfNewAddressesToSendOutThroughTheAPI = []
2018-05-02 17:29:55 +02:00
for _ in range(numberOfAddressesToMake):
2017-09-21 17:24:51 +02:00
# 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, potentialPubSigningKey = \
highlevelcrypto.deterministic_keys(
deterministicPassphrase,
encodeVarint(signingKeyNonce))
potentialPrivEncryptionKey, potentialPubEncryptionKey = \
highlevelcrypto.deterministic_keys(
deterministicPassphrase,
encodeVarint(encryptionKeyNonce))
signingKeyNonce += 2
encryptionKeyNonce += 2
sha = hashlib.new('sha512')
sha.update(
potentialPubSigningKey + potentialPubEncryptionKey)
ripe = RIPEMD160Hash(sha.digest()).digest()
if (
ripe[:numberOfNullBytesDemandedOnFrontOfRipeHash]
== b'\x00' * numberOfNullBytesDemandedOnFrontOfRipeHash
):
2013-09-18 06:04:01 +02:00
break
self.logger.info(
'Generated address with ripe digest: %s', hexlify(ripe))
2015-01-08 23:11:30 +01:00
try:
self.logger.info(
2017-09-21 17:24:51 +02:00
'Address generator calculated %s addresses'
' at %s addresses per second before finding'
' one with the correct ripe-prefix.',
numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix,
numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix
/ (time.time() - startTime)
2017-09-21 17:24:51 +02:00
)
2015-01-08 23:11:30 +01:00
except ZeroDivisionError:
2017-09-21 17:24:51 +02:00
# The user must have a pretty fast computer.
# time.time() - startTime equaled zero.
2015-01-08 23:11:30 +01:00
pass
2017-09-21 17:24:51 +02:00
address = encodeAddress(
addressVersionNumber, streamNumber, ripe)
saveAddressToDisk = True
2017-09-21 17:24:51 +02:00
# 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:
2017-09-21 17:24:51 +02:00
listOfNewAddressesToSendOutThroughTheAPI.append(
'chan name does not match address')
2013-06-26 17:55:33 +02:00
saveAddressToDisk = False
if command == 'getDeterministicAddress':
saveAddressToDisk = False
2013-06-26 17:55:33 +02:00
if saveAddressToDisk and live:
privSigningKeyWIF = \
highlevelcrypto.encodeWalletImportFormat(
potentialPrivSigningKey)
privEncryptionKeyWIF = \
highlevelcrypto.encodeWalletImportFormat(
potentialPrivEncryptionKey)
try:
config.add_section(address)
2015-01-08 23:11:30 +01:00
addressAlreadyExists = False
except configparser.DuplicateSectionError:
addressAlreadyExists = True
2017-09-21 17:24:51 +02:00
2015-01-08 23:11:30 +01:00
if addressAlreadyExists:
self.logger.info(
2017-09-21 17:24:51 +02:00
'%s already exists. Not adding it again.',
address
)
queues.UISignalQueue.put((
2017-09-21 17:24:51 +02:00
'updateStatusBar',
_translate(
2017-09-21 17:24:51 +02:00
"MainWindow",
"%1 is already in 'Your Identities'."
" Not adding it again."
).arg(address)
))
2015-01-08 23:11:30 +01:00
else:
self.logger.debug('label: %s', label)
config.set(address, 'label', label)
config.set(address, 'enabled', 'true')
config.set(address, 'decoy', 'false')
if command in ('createChan', 'joinChan'):
config.set(address, 'chan', 'true')
config.set(
2017-09-21 17:24:51 +02:00
address, 'noncetrialsperbyte',
str(nonceTrialsPerByte))
config.set(
2017-09-21 17:24:51 +02:00
address, 'payloadlengthextrabytes',
str(payloadLengthExtraBytes))
config.set(
2022-07-04 17:33:42 +02:00
address, 'privsigningkey',
privSigningKeyWIF.decode())
config.set(
2022-07-04 17:33:42 +02:00
address, 'privencryptionkey',
privEncryptionKeyWIF.decode())
config.save()
2017-09-21 17:24:51 +02:00
queues.UISignalQueue.put((
'writeNewAddressToTable',
(label, address, str(streamNumber))
))
listOfNewAddressesToSendOutThroughTheAPI.append(
address)
shared.myECCryptorObjects[ripe] = \
2017-09-21 17:24:51 +02:00
highlevelcrypto.makeCryptor(
2019-11-04 15:45:18 +01:00
hexlify(potentialPrivEncryptionKey))
shared.myAddressesByHash[ripe] = address
2021-07-29 21:16:37 +02:00
tag = highlevelcrypto.double_sha512(
encodeVarint(addressVersionNumber)
+ encodeVarint(streamNumber) + ripe
2021-07-29 21:16:37 +02:00
)[32:]
2013-09-15 03:06:26 +02:00
shared.myAddressesByTag[tag] = address
if addressVersionNumber == 3:
2017-09-21 17:24:51 +02:00
# If this is a chan address,
# the worker thread won't send out
# the pubkey over the network.
queues.workerQueue.put((
'sendOutOrStoreMyV3Pubkey', ripe))
elif addressVersionNumber == 4:
queues.workerQueue.put((
'sendOutOrStoreMyV4Pubkey', address))
queues.UISignalQueue.put((
2017-09-21 17:24:51 +02:00
'updateStatusBar',
_translate(
2017-09-21 17:24:51 +02:00
"MainWindow", "Done generating address")
))
elif saveAddressToDisk and not live \
and not config.has_section(address):
2017-09-21 17:24:51 +02:00
listOfNewAddressesToSendOutThroughTheAPI.append(
address)
# Done generating addresses.
if command in (
'createDeterministicAddresses', 'createChan', 'joinChan'
):
queues.apiAddressGeneratorReturnQueue.put(
listOfNewAddressesToSendOutThroughTheAPI)
elif command == 'getDeterministicAddress':
queues.apiAddressGeneratorReturnQueue.put(address)
else:
raise AddressGeneratorException(
"Error in the addressGenerator thread. Thread was"
+ " given a command it could not understand: " + command)
queues.addressGeneratorQueue.task_done()