/ src / class_addressGenerator.py
class_addressGenerator.py
  1  """
  2  A thread for creating addresses
  3  """
  4  
  5  import time
  6  from binascii import hexlify
  7  
  8  from six.moves import configparser, queue
  9  
 10  from . import defaults
 11  from . import highlevelcrypto
 12  from . import queues
 13  from . import shared
 14  from . import state
 15  from .addresses import decodeAddress, encodeAddress, encodeVarint
 16  from .bmconfigparser import config
 17  from .network import StoppableThread
 18  from .tr import _translate
 19  
 20  
 21  class AddressGeneratorException(Exception):
 22      '''Generic AddressGenerator exception'''
 23      pass
 24  
 25  
 26  class addressGenerator(StoppableThread):
 27      """A thread for creating addresses"""
 28  
 29      name = "addressGenerator"
 30  
 31      def stopThread(self):
 32          """Tell the thread to stop putting a special command to it's queue"""
 33          try:
 34              queues.addressGeneratorQueue.put(("stopThread", "data"))
 35          except queue.Full:
 36              self.logger.error('addressGeneratorQueue is Full')
 37  
 38          super(addressGenerator, self).stopThread()
 39  
 40      def run(self):
 41          """
 42          Process the requests for addresses generation
 43          from `.queues.addressGeneratorQueue`
 44          """
 45          # pylint: disable=too-many-locals,too-many-branches,too-many-statements
 46          # pylint: disable=too-many-nested-blocks
 47  
 48          while state.shutdown == 0:
 49              queueValue = queues.addressGeneratorQueue.get()
 50              nonceTrialsPerByte = 0
 51              payloadLengthExtraBytes = 0
 52              live = True
 53              if queueValue[0] == 'createChan':
 54                  command, addressVersionNumber, streamNumber, label, \
 55                      deterministicPassphrase, live = queueValue
 56                  eighteenByteRipe = False
 57                  numberOfAddressesToMake = 1
 58                  numberOfNullBytesDemandedOnFrontOfRipeHash = 1
 59              elif queueValue[0] == 'joinChan':
 60                  command, chanAddress, label, deterministicPassphrase, \
 61                      live = queueValue
 62                  eighteenByteRipe = False
 63                  addressVersionNumber = decodeAddress(chanAddress)[1]
 64                  streamNumber = decodeAddress(chanAddress)[2]
 65                  numberOfAddressesToMake = 1
 66                  numberOfNullBytesDemandedOnFrontOfRipeHash = 1
 67              elif len(queueValue) == 7:
 68                  command, addressVersionNumber, streamNumber, label, \
 69                      numberOfAddressesToMake, deterministicPassphrase, \
 70                      eighteenByteRipe = queueValue
 71  
 72                  numberOfNullBytesDemandedOnFrontOfRipeHash = \
 73                      config.safeGetInt(
 74                          'bitmessagesettings',
 75                          'numberofnullbytesonaddress',
 76                          2 if eighteenByteRipe else 1
 77                      )
 78              elif len(queueValue) == 9:
 79                  command, addressVersionNumber, streamNumber, label, \
 80                      numberOfAddressesToMake, deterministicPassphrase, \
 81                      eighteenByteRipe, nonceTrialsPerByte, \
 82                      payloadLengthExtraBytes = queueValue
 83  
 84                  numberOfNullBytesDemandedOnFrontOfRipeHash = \
 85                      config.safeGetInt(
 86                          'bitmessagesettings',
 87                          'numberofnullbytesonaddress',
 88                          2 if eighteenByteRipe else 1
 89                      )
 90              elif queueValue[0] == 'stopThread':
 91                  break
 92              else:
 93                  self.logger.error(
 94                      'Programming error: A structure with the wrong number'
 95                      ' of values was passed into the addressGeneratorQueue.'
 96                      ' Here is the queueValue: %r\n', queueValue)
 97              if addressVersionNumber < 3 or addressVersionNumber > 4:
 98                  self.logger.error(
 99                      'Program error: For some reason the address generator'
100                      ' queue has been given a request to create at least'
101                      ' one version %s address which it cannot do.\n',
102                      addressVersionNumber)
103              if nonceTrialsPerByte == 0:
104                  nonceTrialsPerByte = config.getint(
105                      'bitmessagesettings', 'defaultnoncetrialsperbyte')
106              if nonceTrialsPerByte < \
107                      defaults.networkDefaultProofOfWorkNonceTrialsPerByte:
108                  nonceTrialsPerByte = \
109                      defaults.networkDefaultProofOfWorkNonceTrialsPerByte
110              if payloadLengthExtraBytes == 0:
111                  payloadLengthExtraBytes = config.getint(
112                      'bitmessagesettings', 'defaultpayloadlengthextrabytes')
113              if payloadLengthExtraBytes < \
114                      defaults.networkDefaultPayloadLengthExtraBytes:
115                  payloadLengthExtraBytes = \
116                      defaults.networkDefaultPayloadLengthExtraBytes
117              if command == 'createRandomAddress':
118                  queues.UISignalQueue.put((
119                      'updateStatusBar',
120                      _translate(
121                          "MainWindow", "Generating one new address")
122                  ))
123                  # This next section is a little bit strange. We're going
124                  # to generate keys over and over until we find one
125                  # that starts with either \x00 or \x00\x00. Then when
126                  # we pack them into a Bitmessage address, we won't store
127                  # the \x00 or \x00\x00 bytes thus making the address shorter.
128                  startTime = time.time()
129                  numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix = 0
130                  privSigningKey, pubSigningKey = highlevelcrypto.random_keys()
131                  while True:
132                      numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix += 1
133                      potentialPrivEncryptionKey, potentialPubEncryptionKey = \
134                          highlevelcrypto.random_keys()
135                      ripe = highlevelcrypto.to_ripe(
136                          pubSigningKey, potentialPubEncryptionKey)
137                      if (
138                          ripe[:numberOfNullBytesDemandedOnFrontOfRipeHash]
139                          == b'\x00' * numberOfNullBytesDemandedOnFrontOfRipeHash
140                      ):
141                          break
142                  self.logger.info(
143                      'Generated address with ripe digest: %s', hexlify(ripe))
144                  try:
145                      self.logger.info(
146                          'Address generator calculated %s addresses at %s'
147                          ' addresses per second before finding one with'
148                          ' the correct ripe-prefix.',
149                          numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix,
150                          numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix
151                          / (time.time() - startTime))
152                  except ZeroDivisionError:
153                      # The user must have a pretty fast computer.
154                      # time.time() - startTime equaled zero.
155                      pass
156                  address = encodeAddress(
157                      addressVersionNumber, streamNumber, ripe)
158  
159                  privSigningKeyWIF = highlevelcrypto.encodeWalletImportFormat(
160                      privSigningKey)
161                  privEncryptionKeyWIF = highlevelcrypto.encodeWalletImportFormat(
162                      potentialPrivEncryptionKey)
163  
164                  config.add_section(address)
165                  config.set(address, 'label', label)
166                  config.set(address, 'enabled', 'true')
167                  config.set(address, 'decoy', 'false')
168                  config.set(address, 'noncetrialsperbyte', str(
169                      nonceTrialsPerByte))
170                  config.set(address, 'payloadlengthextrabytes', str(
171                      payloadLengthExtraBytes))
172                  config.set(
173                      address, 'privsigningkey', privSigningKeyWIF.decode())
174                  config.set(
175                      address, 'privencryptionkey',
176                      privEncryptionKeyWIF.decode())
177                  config.save()
178  
179                  # The API and the join and create Chan functionality
180                  # both need information back from the address generator.
181                  queues.apiAddressGeneratorReturnQueue.put(address)
182  
183                  queues.UISignalQueue.put((
184                      'updateStatusBar',
185                      _translate(
186                          "MainWindow",
187                          "Done generating address. Doing work necessary"
188                          " to broadcast it...")
189                  ))
190                  queues.UISignalQueue.put(('writeNewAddressToTable', (
191                      label, address, streamNumber)))
192                  shared.reloadMyAddressHashes()
193                  if addressVersionNumber == 3:
194                      queues.workerQueue.put((
195                          'sendOutOrStoreMyV3Pubkey', ripe))
196                  elif addressVersionNumber == 4:
197                      queues.workerQueue.put((
198                          'sendOutOrStoreMyV4Pubkey', address))
199  
200              elif command in (
201                  'createDeterministicAddresses', 'createChan',
202                  'getDeterministicAddress', 'joinChan'
203              ):
204                  if not deterministicPassphrase:
205                      self.logger.warning(
206                          'You are creating deterministic'
207                          ' address(es) using a blank passphrase.'
208                          ' Bitmessage will do it but it is rather stupid.')
209                  if command == 'createDeterministicAddresses':
210                      queues.UISignalQueue.put((
211                          'updateStatusBar',
212                          _translate(
213                              "MainWindow",
214                              "Generating %1 new addresses."
215                          ).arg(str(numberOfAddressesToMake))
216                      ))
217                  signingKeyNonce = 0
218                  encryptionKeyNonce = 1
219                  # We fill out this list no matter what although we only
220                  # need it if we end up passing the info to the API.
221                  listOfNewAddressesToSendOutThroughTheAPI = []
222  
223                  for _ in range(numberOfAddressesToMake):
224                      # This next section is a little bit strange. We're
225                      # going to generate keys over and over until we find
226                      # one that has a RIPEMD hash that starts with either
227                      # \x00 or \x00\x00. Then when we pack them into a
228                      # Bitmessage address, we won't store the \x00 or
229                      # \x00\x00 bytes thus making the address shorter.
230                      startTime = time.time()
231                      numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix = 0
232                      while True:
233                          numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix += 1
234                          potentialPrivSigningKey, potentialPubSigningKey = \
235                              highlevelcrypto.deterministic_keys(
236                                  deterministicPassphrase,
237                                  encodeVarint(signingKeyNonce))
238                          potentialPrivEncryptionKey, potentialPubEncryptionKey = \
239                              highlevelcrypto.deterministic_keys(
240                                  deterministicPassphrase,
241                                  encodeVarint(encryptionKeyNonce))
242  
243                          signingKeyNonce += 2
244                          encryptionKeyNonce += 2
245                          ripe = highlevelcrypto.to_ripe(
246                              potentialPubSigningKey, potentialPubEncryptionKey)
247                          if (
248                              ripe[:numberOfNullBytesDemandedOnFrontOfRipeHash]
249                              == b'\x00' * numberOfNullBytesDemandedOnFrontOfRipeHash
250                          ):
251                              break
252  
253                      self.logger.info(
254                          'Generated address with ripe digest: %s', hexlify(ripe))
255                      try:
256                          self.logger.info(
257                              'Address generator calculated %s addresses'
258                              ' at %s addresses per second before finding'
259                              ' one with the correct ripe-prefix.',
260                              numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix,
261                              numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix
262                              / (time.time() - startTime)
263                          )
264                      except ZeroDivisionError:
265                          # The user must have a pretty fast computer.
266                          # time.time() - startTime equaled zero.
267                          pass
268                      address = encodeAddress(
269                          addressVersionNumber, streamNumber, ripe)
270  
271                      saveAddressToDisk = True
272                      # If we are joining an existing chan, let us check
273                      # to make sure it matches the provided Bitmessage address
274                      if command == 'joinChan':
275                          if address != chanAddress:
276                              listOfNewAddressesToSendOutThroughTheAPI.append(
277                                  'chan name does not match address')
278                              saveAddressToDisk = False
279                      if command == 'getDeterministicAddress':
280                          saveAddressToDisk = False
281  
282                      if saveAddressToDisk and live:
283                          privSigningKeyWIF = \
284                              highlevelcrypto.encodeWalletImportFormat(
285                                  potentialPrivSigningKey)
286                          privEncryptionKeyWIF = \
287                              highlevelcrypto.encodeWalletImportFormat(
288                                  potentialPrivEncryptionKey)
289  
290                          try:
291                              config.add_section(address)
292                              addressAlreadyExists = False
293                          except configparser.DuplicateSectionError:
294                              addressAlreadyExists = True
295  
296                          if addressAlreadyExists:
297                              self.logger.info(
298                                  '%s already exists. Not adding it again.',
299                                  address
300                              )
301                              queues.UISignalQueue.put((
302                                  'updateStatusBar',
303                                  _translate(
304                                      "MainWindow",
305                                      "%1 is already in 'Your Identities'."
306                                      " Not adding it again."
307                                  ).arg(address)
308                              ))
309                          else:
310                              self.logger.debug('label: %s', label)
311                              config.set(address, 'label', label)
312                              config.set(address, 'enabled', 'true')
313                              config.set(address, 'decoy', 'false')
314                              if command in ('createChan', 'joinChan'):
315                                  config.set(address, 'chan', 'true')
316                              config.set(
317                                  address, 'noncetrialsperbyte',
318                                  str(nonceTrialsPerByte))
319                              config.set(
320                                  address, 'payloadlengthextrabytes',
321                                  str(payloadLengthExtraBytes))
322                              config.set(
323                                  address, 'privsigningkey',
324                                  privSigningKeyWIF.decode())
325                              config.set(
326                                  address, 'privencryptionkey',
327                                  privEncryptionKeyWIF.decode())
328                              config.save()
329  
330                              queues.UISignalQueue.put((
331                                  'writeNewAddressToTable',
332                                  (label, address, str(streamNumber))
333                              ))
334                              listOfNewAddressesToSendOutThroughTheAPI.append(
335                                  address)
336                              shared.myECCryptorObjects[ripe] = \
337                                  highlevelcrypto.makeCryptor(
338                                      hexlify(potentialPrivEncryptionKey))
339                              shared.myAddressesByHash[ripe] = address
340                              tag = highlevelcrypto.double_sha512(
341                                  encodeVarint(addressVersionNumber)
342                                  + encodeVarint(streamNumber) + ripe
343                              )[32:]
344                              shared.myAddressesByTag[tag] = address
345                              if addressVersionNumber == 3:
346                                  # If this is a chan address,
347                                  # the worker thread won't send out
348                                  # the pubkey over the network.
349                                  queues.workerQueue.put((
350                                      'sendOutOrStoreMyV3Pubkey', ripe))
351                              elif addressVersionNumber == 4:
352                                  queues.workerQueue.put((
353                                      'sendOutOrStoreMyV4Pubkey', address))
354                              queues.UISignalQueue.put((
355                                  'updateStatusBar',
356                                  _translate(
357                                      "MainWindow", "Done generating address")
358                              ))
359                      elif saveAddressToDisk and not live \
360                              and not config.has_section(address):
361                          listOfNewAddressesToSendOutThroughTheAPI.append(
362                              address)
363  
364                  # Done generating addresses.
365                  if command in (
366                      'createDeterministicAddresses', 'createChan', 'joinChan'
367                  ):
368                      queues.apiAddressGeneratorReturnQueue.put(
369                          listOfNewAddressesToSendOutThroughTheAPI)
370                  elif command == 'getDeterministicAddress':
371                      queues.apiAddressGeneratorReturnQueue.put(address)
372              else:
373                  raise AddressGeneratorException(
374                      "Error in the addressGenerator thread. Thread was"
375                      + " given a command it could not understand: " + command)
376              queues.addressGeneratorQueue.task_done()