2015-01-21 18:38:25 +01:00
from __future__ import division
2013-06-21 23:32:22 +02:00
import threading
import shared
import time
2013-06-24 22:25:31 +02:00
from time import strftime , localtime , gmtime
2013-06-21 23:32:22 +02:00
import random
2013-09-30 05:01:56 +02:00
from subprocess import call # used when the API must execute an outside program
2013-06-21 23:32:22 +02:00
from addresses import *
import highlevelcrypto
import proofofwork
2013-06-23 21:52:39 +02:00
import sys
2013-06-24 21:51:01 +02:00
import tr
2013-08-08 21:37:48 +02:00
from debug import logger
2013-08-29 13:27:30 +02:00
from helper_sql import *
2013-09-30 05:01:56 +02:00
import helper_inbox
2014-05-02 16:46:36 +02:00
from helper_generic import addDataPadding
2015-11-24 01:55:17 +01:00
from helper_threading import *
2014-08-06 04:01:01 +02:00
import l10n
2015-12-15 20:30:32 +01:00
from protocol import *
2016-03-23 23:26:57 +01:00
from binascii import hexlify , unhexlify
2013-06-21 23:32:22 +02:00
# This thread, of which there is only one, does the heavy lifting:
# calculating POWs.
2015-11-26 02:38:55 +01:00
def sizeof_fmt ( num , suffix = ' h/s ' ) :
for unit in [ ' ' , ' k ' , ' M ' , ' G ' , ' T ' , ' P ' , ' E ' , ' Z ' ] :
if abs ( num ) < 1000.0 :
return " %3.1f %s %s " % ( num , unit , suffix )
num / = 1024.0
return " %.1f %s %s " % ( num , ' Yi ' , suffix )
2013-06-21 23:32:22 +02:00
2015-11-24 01:55:17 +01:00
class singleWorker ( threading . Thread , StoppableThread ) :
2013-06-21 23:32:22 +02:00
def __init__ ( self ) :
# QThread.__init__(self, parent)
2015-11-18 16:22:17 +01:00
threading . Thread . __init__ ( self , name = " singleWorker " )
2015-11-24 01:55:17 +01:00
self . initStop ( )
def stopThread ( self ) :
try :
shared . workerQueue . put ( ( " stopThread " , " data " ) )
except :
pass
super ( singleWorker , self ) . stopThread ( )
2013-06-21 23:32:22 +02:00
def run ( self ) :
2015-03-09 07:35:32 +01:00
# Initialize the neededPubkeys dictionary.
2013-08-29 13:27:30 +02:00
queryreturn = sqlQuery (
2014-08-27 09:14:32 +02:00
''' SELECT DISTINCT toaddress FROM sent WHERE (status= ' awaitingpubkey ' AND folder= ' sent ' ) ''' )
2013-06-21 23:32:22 +02:00
for row in queryreturn :
2014-08-27 09:14:32 +02:00
toAddress , = row
toStatus , toAddressVersionNumber , toStreamNumber , toRipe = decodeAddress ( toAddress )
2013-09-13 06:27:34 +02:00
if toAddressVersionNumber < = 3 :
2015-03-09 07:35:32 +01:00
shared . neededPubkeys [ toAddress ] = 0
2013-09-13 06:27:34 +02:00
elif toAddressVersionNumber > = 4 :
2013-09-15 03:06:26 +02:00
doubleHashOfAddressData = hashlib . sha512 ( hashlib . sha512 ( encodeVarint (
toAddressVersionNumber ) + encodeVarint ( toStreamNumber ) + toRipe ) . digest ( ) ) . digest ( )
privEncryptionKey = doubleHashOfAddressData [ : 32 ] # Note that this is the first half of the sha512 hash.
tag = doubleHashOfAddressData [ 32 : ]
2016-03-23 23:26:57 +01:00
shared . neededPubkeys [ tag ] = ( toAddress , highlevelcrypto . makeCryptor ( hexlify ( privEncryptionKey ) ) ) # We'll need this for when we receive a pubkey reply: it will be encrypted and we'll need to decrypt it.
2013-06-21 23:32:22 +02:00
2014-08-27 09:14:32 +02:00
# Initialize the shared.ackdataForWhichImWatching data structure
2013-08-29 13:27:30 +02:00
queryreturn = sqlQuery (
2013-06-21 23:32:22 +02:00
''' SELECT ackdata FROM sent where (status= ' msgsent ' OR status= ' doingmsgpow ' ) ''' )
for row in queryreturn :
ackdata , = row
2016-03-23 23:26:57 +01:00
logger . info ( ' Watching for ackdata ' + hexlify ( ackdata ) )
2013-06-24 21:51:01 +02:00
shared . ackdataForWhichImWatching [ ackdata ] = 0
2013-06-21 23:32:22 +02:00
2015-11-24 01:55:17 +01:00
self . stop . wait (
2015-03-09 07:35:32 +01:00
10 ) # give some time for the GUI to start before we start on existing POW tasks.
2016-01-22 14:47:26 +01:00
if shared . shutdown == 0 :
queryreturn = sqlQuery (
''' SELECT DISTINCT toaddress FROM sent WHERE (status= ' doingpubkeypow ' AND folder= ' sent ' ) ''' )
for row in queryreturn :
toaddress , = row
logger . debug ( " c: %s " , shared . shutdown )
self . requestPubKey ( toaddress )
# just in case there are any pending tasks for msg
# messages that have yet to be sent.
self . sendMsg ( )
# just in case there are any tasks for Broadcasts
# that have yet to be sent.
self . sendBroadcast ( )
2013-06-21 23:32:22 +02:00
2015-11-24 01:55:17 +01:00
while shared . shutdown == 0 :
2016-04-20 15:33:01 +02:00
self . busy = 0
2013-06-21 23:32:22 +02:00
command , data = shared . workerQueue . get ( )
2016-04-20 15:33:01 +02:00
self . busy = 1
2013-06-21 23:32:22 +02:00
if command == ' sendmessage ' :
2016-04-17 20:31:25 +02:00
try :
self . sendMsg ( )
except :
pass
2013-06-21 23:32:22 +02:00
elif command == ' sendbroadcast ' :
2016-04-17 20:31:25 +02:00
try :
self . sendBroadcast ( )
except :
pass
2013-06-21 23:32:22 +02:00
elif command == ' doPOWForMyV2Pubkey ' :
2016-04-17 20:31:25 +02:00
try :
self . doPOWForMyV2Pubkey ( data )
except :
pass
2013-07-22 07:10:22 +02:00
elif command == ' sendOutOrStoreMyV3Pubkey ' :
2016-04-17 20:31:25 +02:00
try :
self . sendOutOrStoreMyV3Pubkey ( data )
except :
pass
2013-09-13 06:27:34 +02:00
elif command == ' sendOutOrStoreMyV4Pubkey ' :
2016-04-17 20:31:25 +02:00
try :
self . sendOutOrStoreMyV4Pubkey ( data )
except :
pass
2015-11-24 01:55:17 +01:00
elif command == ' stopThread ' :
2016-04-20 15:33:01 +02:00
self . busy = 0
2015-11-24 01:55:17 +01:00
return
2013-06-21 23:32:22 +02:00
else :
2015-11-18 16:22:17 +01:00
logger . error ( ' Probable programming error: The command sent to the workerThread is weird. It is: %s \n ' % command )
2013-06-29 19:29:35 +02:00
2013-06-21 23:32:22 +02:00
shared . workerQueue . task_done ( )
def doPOWForMyV2Pubkey ( self , hash ) : # This function also broadcasts out the pubkey message once it is done with the POW
# Look up my stream number based on my address hash
""" configSections = shared.config.sections()
for addressInKeysFile in configSections :
if addressInKeysFile < > ' bitmessagesettings ' :
status , addressVersionNumber , streamNumber , hashFromThisParticularAddress = decodeAddress ( addressInKeysFile )
if hash == hashFromThisParticularAddress :
myAddress = addressInKeysFile
break """
myAddress = shared . myAddressesByHash [ hash ]
status , addressVersionNumber , streamNumber , hash = decodeAddress (
myAddress )
2014-08-27 09:14:32 +02:00
2014-11-13 22:32:31 +01:00
TTL = int ( 28 * 24 * 60 * 60 + random . randrange ( - 300 , 300 ) ) # 28 days from now plus or minus five minutes
embeddedTime = int ( time . time ( ) + TTL )
2014-08-27 09:14:32 +02:00
payload = pack ( ' >Q ' , ( embeddedTime ) )
payload + = ' \x00 \x00 \x00 \x01 ' # object type: pubkey
2013-06-21 23:32:22 +02:00
payload + = encodeVarint ( addressVersionNumber ) # Address version number
payload + = encodeVarint ( streamNumber )
2015-12-15 20:30:32 +01:00
payload + = getBitfield ( myAddress ) # bitfield of features supported by me (see the wiki).
2013-06-21 23:32:22 +02:00
try :
privSigningKeyBase58 = shared . config . get (
myAddress , ' privsigningkey ' )
privEncryptionKeyBase58 = shared . config . get (
myAddress , ' privencryptionkey ' )
except Exception as err :
2015-11-18 16:22:17 +01:00
logger . error ( ' Error within doPOWForMyV2Pubkey. Could not read the keys from the keys.dat file for a requested address. %s \n ' % err )
2013-06-21 23:32:22 +02:00
return
2016-03-23 23:26:57 +01:00
privSigningKeyHex = hexlify ( shared . decodeWalletImportFormat (
privSigningKeyBase58 ) )
privEncryptionKeyHex = hexlify ( shared . decodeWalletImportFormat (
privEncryptionKeyBase58 ) )
pubSigningKey = unhexlify ( highlevelcrypto . privToPub (
privSigningKeyHex ) )
pubEncryptionKey = unhexlify ( highlevelcrypto . privToPub (
privEncryptionKeyHex ) )
2013-06-21 23:32:22 +02:00
payload + = pubSigningKey [ 1 : ]
payload + = pubEncryptionKey [ 1 : ]
# Do the POW for this pubkey message
2014-08-27 09:14:32 +02:00
target = 2 * * 64 / ( shared . networkDefaultProofOfWorkNonceTrialsPerByte * ( len ( payload ) + 8 + shared . networkDefaultPayloadLengthExtraBytes + ( ( TTL * ( len ( payload ) + 8 + shared . networkDefaultPayloadLengthExtraBytes ) ) / ( 2 * * 16 ) ) ) )
2015-11-18 16:22:17 +01:00
logger . info ( ' (For pubkey message) Doing proof of work... ' )
2013-06-21 23:32:22 +02:00
initialHash = hashlib . sha512 ( payload ) . digest ( )
trialValue , nonce = proofofwork . run ( target , initialHash )
2015-11-18 16:22:17 +01:00
logger . info ( ' (For pubkey message) Found proof of work ' + str ( trialValue ) , ' Nonce: ' , str ( nonce ) )
2013-06-21 23:32:22 +02:00
payload = pack ( ' >Q ' , nonce ) + payload
inventoryHash = calculateInventoryHash ( payload )
2014-08-27 09:14:32 +02:00
objectType = 1
2013-06-21 23:32:22 +02:00
shared . inventory [ inventoryHash ] = (
2013-09-13 06:27:34 +02:00
objectType , streamNumber , payload , embeddedTime , ' ' )
2013-06-21 23:32:22 +02:00
2016-03-23 23:26:57 +01:00
logger . info ( ' broadcasting inv with hash: ' + hexlify ( inventoryHash ) )
2013-06-29 19:29:35 +02:00
2013-06-21 23:32:22 +02:00
shared . broadcastToSendDataQueues ( (
2013-09-07 00:55:12 +02:00
streamNumber , ' advertiseobject ' , inventoryHash ) )
2013-06-21 23:32:22 +02:00
shared . UISignalQueue . put ( ( ' updateStatusBar ' , ' ' ) )
2013-11-07 05:38:19 +01:00
try :
shared . config . set (
myAddress , ' lastpubkeysendtime ' , str ( int ( time . time ( ) ) ) )
2014-09-15 08:34:33 +02:00
shared . writeKeysFile ( )
2013-11-07 05:38:19 +01:00
except :
# The user deleted the address out of the keys.dat file before this
# finished.
pass
2013-06-21 23:32:22 +02:00
2013-07-22 07:10:22 +02:00
# If this isn't a chan address, this function assembles the pubkey data,
# does the necessary POW and sends it out. If it *is* a chan then it
# assembles the pubkey and stores is in the pubkey table so that we can
# send messages to "ourselves".
def sendOutOrStoreMyV3Pubkey ( self , hash ) :
2013-11-07 05:38:19 +01:00
try :
myAddress = shared . myAddressesByHash [ hash ]
except :
#The address has been deleted.
return
2013-09-30 01:24:27 +02:00
if shared . safeConfigGetBoolean ( myAddress , ' chan ' ) :
2015-11-18 16:22:17 +01:00
logger . info ( ' This is a chan address. Not sending pubkey. ' )
2013-09-30 01:24:27 +02:00
return
2013-06-21 23:32:22 +02:00
status , addressVersionNumber , streamNumber , hash = decodeAddress (
myAddress )
2014-08-27 09:14:32 +02:00
2014-11-13 22:32:31 +01:00
TTL = int ( 28 * 24 * 60 * 60 + random . randrange ( - 300 , 300 ) ) # 28 days from now plus or minus five minutes
embeddedTime = int ( time . time ( ) + TTL )
2014-08-27 09:14:32 +02:00
signedTimeForProtocolV2 = embeddedTime - TTL
"""
According to the protocol specification , the expiresTime along with the pubkey information is
signed . But to be backwards compatible during the upgrade period , we shall sign not the
expiresTime but rather the current time . There must be precisely a 28 day difference
between the two . After the upgrade period we ' ll switch to signing the whole payload with the
expiresTime time .
"""
payload = pack ( ' >Q ' , ( embeddedTime ) )
payload + = ' \x00 \x00 \x00 \x01 ' # object type: pubkey
2013-06-21 23:32:22 +02:00
payload + = encodeVarint ( addressVersionNumber ) # Address version number
payload + = encodeVarint ( streamNumber )
2015-12-15 20:30:32 +01:00
payload + = getBitfield ( myAddress ) # bitfield of features supported by me (see the wiki).
2013-06-21 23:32:22 +02:00
try :
privSigningKeyBase58 = shared . config . get (
myAddress , ' privsigningkey ' )
privEncryptionKeyBase58 = shared . config . get (
myAddress , ' privencryptionkey ' )
except Exception as err :
2015-11-18 16:22:17 +01:00
logger . error ( ' Error within sendOutOrStoreMyV3Pubkey. Could not read the keys from the keys.dat file for a requested address. %s \n ' % err )
2013-06-29 19:29:35 +02:00
2013-06-21 23:32:22 +02:00
return
2016-03-23 23:26:57 +01:00
privSigningKeyHex = hexlify ( shared . decodeWalletImportFormat (
privSigningKeyBase58 ) )
privEncryptionKeyHex = hexlify ( shared . decodeWalletImportFormat (
privEncryptionKeyBase58 ) )
pubSigningKey = unhexlify ( highlevelcrypto . privToPub (
privSigningKeyHex ) )
pubEncryptionKey = unhexlify ( highlevelcrypto . privToPub (
privEncryptionKeyHex ) )
2013-06-21 23:32:22 +02:00
payload + = pubSigningKey [ 1 : ]
payload + = pubEncryptionKey [ 1 : ]
payload + = encodeVarint ( shared . config . getint (
myAddress , ' noncetrialsperbyte ' ) )
payload + = encodeVarint ( shared . config . getint (
myAddress , ' payloadlengthextrabytes ' ) )
2014-08-27 09:14:32 +02:00
2014-12-25 09:57:34 +01:00
signature = highlevelcrypto . sign ( payload , privSigningKeyHex )
2013-06-21 23:32:22 +02:00
payload + = encodeVarint ( len ( signature ) )
payload + = signature
2013-09-30 01:24:27 +02:00
# Do the POW for this pubkey message
2014-08-27 09:14:32 +02:00
target = 2 * * 64 / ( shared . networkDefaultProofOfWorkNonceTrialsPerByte * ( len ( payload ) + 8 + shared . networkDefaultPayloadLengthExtraBytes + ( ( TTL * ( len ( payload ) + 8 + shared . networkDefaultPayloadLengthExtraBytes ) ) / ( 2 * * 16 ) ) ) )
2015-11-18 16:22:17 +01:00
logger . info ( ' (For pubkey message) Doing proof of work... ' )
2013-09-30 01:24:27 +02:00
initialHash = hashlib . sha512 ( payload ) . digest ( )
trialValue , nonce = proofofwork . run ( target , initialHash )
2015-11-18 16:22:17 +01:00
logger . info ( ' (For pubkey message) Found proof of work. Nonce: ' + str ( nonce ) )
2013-06-21 23:32:22 +02:00
2013-09-30 01:24:27 +02:00
payload = pack ( ' >Q ' , nonce ) + payload
inventoryHash = calculateInventoryHash ( payload )
2014-08-27 09:14:32 +02:00
objectType = 1
2013-09-30 01:24:27 +02:00
shared . inventory [ inventoryHash ] = (
objectType , streamNumber , payload , embeddedTime , ' ' )
2013-06-21 23:32:22 +02:00
2016-03-23 23:26:57 +01:00
logger . info ( ' broadcasting inv with hash: ' + hexlify ( inventoryHash ) )
2013-06-29 19:29:35 +02:00
2013-09-30 01:24:27 +02:00
shared . broadcastToSendDataQueues ( (
streamNumber , ' advertiseobject ' , inventoryHash ) )
shared . UISignalQueue . put ( ( ' updateStatusBar ' , ' ' ) )
2013-11-07 05:38:19 +01:00
try :
shared . config . set (
myAddress , ' lastpubkeysendtime ' , str ( int ( time . time ( ) ) ) )
2014-09-15 08:34:33 +02:00
shared . writeKeysFile ( )
2013-11-07 05:38:19 +01:00
except :
# The user deleted the address out of the keys.dat file before this
# finished.
pass
2013-06-21 23:32:22 +02:00
2013-09-13 06:27:34 +02:00
# If this isn't a chan address, this function assembles the pubkey data,
2013-09-30 01:24:27 +02:00
# does the necessary POW and sends it out.
2013-09-13 06:27:34 +02:00
def sendOutOrStoreMyV4Pubkey ( self , myAddress ) :
2013-11-07 05:38:19 +01:00
if not shared . config . has_section ( myAddress ) :
#The address has been deleted.
return
2013-09-30 01:24:27 +02:00
if shared . safeConfigGetBoolean ( myAddress , ' chan ' ) :
2015-11-18 16:22:17 +01:00
logger . info ( ' This is a chan address. Not sending pubkey. ' )
2013-09-30 01:24:27 +02:00
return
2013-09-13 06:27:34 +02:00
status , addressVersionNumber , streamNumber , hash = decodeAddress (
myAddress )
2014-08-27 09:14:32 +02:00
2014-11-13 22:32:31 +01:00
TTL = int ( 28 * 24 * 60 * 60 + random . randrange ( - 300 , 300 ) ) # 28 days from now plus or minus five minutes
embeddedTime = int ( time . time ( ) + TTL )
2013-09-13 06:27:34 +02:00
payload = pack ( ' >Q ' , ( embeddedTime ) )
2014-08-27 09:14:32 +02:00
payload + = ' \x00 \x00 \x00 \x01 ' # object type: pubkey
2013-09-13 06:27:34 +02:00
payload + = encodeVarint ( addressVersionNumber ) # Address version number
payload + = encodeVarint ( streamNumber )
2015-12-15 20:30:32 +01:00
dataToEncrypt = getBitfield ( myAddress )
2013-09-13 06:27:34 +02:00
try :
privSigningKeyBase58 = shared . config . get (
myAddress , ' privsigningkey ' )
privEncryptionKeyBase58 = shared . config . get (
myAddress , ' privencryptionkey ' )
except Exception as err :
2015-11-18 16:22:17 +01:00
logger . error ( ' Error within sendOutOrStoreMyV4Pubkey. Could not read the keys from the keys.dat file for a requested address. %s \n ' % err )
2013-09-13 06:27:34 +02:00
return
2016-03-23 23:26:57 +01:00
privSigningKeyHex = hexlify ( shared . decodeWalletImportFormat (
privSigningKeyBase58 ) )
privEncryptionKeyHex = hexlify ( shared . decodeWalletImportFormat (
privEncryptionKeyBase58 ) )
pubSigningKey = unhexlify ( highlevelcrypto . privToPub (
privSigningKeyHex ) )
pubEncryptionKey = unhexlify ( highlevelcrypto . privToPub (
privEncryptionKeyHex ) )
2013-09-13 06:27:34 +02:00
dataToEncrypt + = pubSigningKey [ 1 : ]
dataToEncrypt + = pubEncryptionKey [ 1 : ]
dataToEncrypt + = encodeVarint ( shared . config . getint (
myAddress , ' noncetrialsperbyte ' ) )
dataToEncrypt + = encodeVarint ( shared . config . getint (
myAddress , ' payloadlengthextrabytes ' ) )
2014-08-27 09:14:32 +02:00
# When we encrypt, we'll use a hash of the data
2013-09-30 01:24:27 +02:00
# contained in an address as a decryption key. This way in order to
# read the public keys in a pubkey message, a node must know the address
# first. We'll also tag, unencrypted, the pubkey with part of the hash
# so that nodes know which pubkey object to try to decrypt when they
# want to send a message.
doubleHashOfAddressData = hashlib . sha512 ( hashlib . sha512 ( encodeVarint (
addressVersionNumber ) + encodeVarint ( streamNumber ) + hash ) . digest ( ) ) . digest ( )
payload + = doubleHashOfAddressData [ 32 : ] # the tag
2014-12-25 09:57:34 +01:00
signature = highlevelcrypto . sign ( payload + dataToEncrypt , privSigningKeyHex )
2014-08-27 09:14:32 +02:00
dataToEncrypt + = encodeVarint ( len ( signature ) )
dataToEncrypt + = signature
2013-09-30 01:24:27 +02:00
privEncryptionKey = doubleHashOfAddressData [ : 32 ]
2014-05-21 12:15:07 +02:00
pubEncryptionKey = highlevelcrypto . pointMult ( privEncryptionKey )
2013-09-30 01:24:27 +02:00
payload + = highlevelcrypto . encrypt (
2016-03-23 23:26:57 +01:00
dataToEncrypt , hexlify ( pubEncryptionKey ) )
2013-09-18 06:04:01 +02:00
2013-09-30 01:24:27 +02:00
# Do the POW for this pubkey message
2014-08-27 09:14:32 +02:00
target = 2 * * 64 / ( shared . networkDefaultProofOfWorkNonceTrialsPerByte * ( len ( payload ) + 8 + shared . networkDefaultPayloadLengthExtraBytes + ( ( TTL * ( len ( payload ) + 8 + shared . networkDefaultPayloadLengthExtraBytes ) ) / ( 2 * * 16 ) ) ) )
2015-11-18 16:22:17 +01:00
logger . info ( ' (For pubkey message) Doing proof of work... ' )
2013-09-30 01:24:27 +02:00
initialHash = hashlib . sha512 ( payload ) . digest ( )
trialValue , nonce = proofofwork . run ( target , initialHash )
2015-11-18 16:22:17 +01:00
logger . info ( ' (For pubkey message) Found proof of work ' + str ( trialValue ) + ' Nonce: ' + str ( nonce ) )
2013-09-13 06:27:34 +02:00
2013-09-30 01:24:27 +02:00
payload = pack ( ' >Q ' , nonce ) + payload
inventoryHash = calculateInventoryHash ( payload )
2014-08-27 09:14:32 +02:00
objectType = 1
2013-09-30 01:24:27 +02:00
shared . inventory [ inventoryHash ] = (
objectType , streamNumber , payload , embeddedTime , doubleHashOfAddressData [ 32 : ] )
2013-09-13 06:27:34 +02:00
2016-03-23 23:26:57 +01:00
logger . info ( ' broadcasting inv with hash: ' + hexlify ( inventoryHash ) )
2013-09-18 06:04:01 +02:00
2013-09-30 01:24:27 +02:00
shared . broadcastToSendDataQueues ( (
streamNumber , ' advertiseobject ' , inventoryHash ) )
shared . UISignalQueue . put ( ( ' updateStatusBar ' , ' ' ) )
2013-10-26 01:35:59 +02:00
try :
shared . config . set (
myAddress , ' lastpubkeysendtime ' , str ( int ( time . time ( ) ) ) )
2014-09-15 08:34:33 +02:00
shared . writeKeysFile ( )
2013-12-06 07:52:19 +01:00
except Exception as err :
logger . error ( ' Error: Couldn \' t add the lastpubkeysendtime to the keys.dat file. Error message: %s ' % err )
2013-09-13 06:27:34 +02:00
2013-06-21 23:32:22 +02:00
def sendBroadcast ( self ) :
2013-08-29 13:27:30 +02:00
queryreturn = sqlQuery (
2015-03-09 07:35:32 +01:00
''' SELECT fromaddress, subject, message, ackdata, ttl FROM sent WHERE status=? and folder= ' sent ' ''' , ' broadcastqueued ' )
2013-06-21 23:32:22 +02:00
for row in queryreturn :
2015-03-09 07:35:32 +01:00
fromaddress , subject , body , ackdata , TTL = row
2013-06-21 23:32:22 +02:00
status , addressVersionNumber , streamNumber , ripe = decodeAddress (
fromaddress )
2013-07-31 18:36:51 +02:00
if addressVersionNumber < = 1 :
2015-11-18 16:22:17 +01:00
logger . error ( ' Error: In the singleWorker thread, the sendBroadcast function doesn \' t understand the address version. \n ' )
2013-07-31 18:36:51 +02:00
return
# We need to convert our private keys to public keys in order
# to include them.
try :
privSigningKeyBase58 = shared . config . get (
fromaddress , ' privsigningkey ' )
privEncryptionKeyBase58 = shared . config . get (
fromaddress , ' privencryptionkey ' )
except :
shared . UISignalQueue . put ( ( ' updateSentItemStatusByAckdata ' , (
2016-04-30 11:07:27 +02:00
ackdata , tr . _translate ( " MainWindow " , " Error! Could not find sender address (your address) in the keys.dat file. " ) ) ) )
2013-07-31 18:36:51 +02:00
continue
2016-03-23 23:26:57 +01:00
privSigningKeyHex = hexlify ( shared . decodeWalletImportFormat (
privSigningKeyBase58 ) )
privEncryptionKeyHex = hexlify ( shared . decodeWalletImportFormat (
privEncryptionKeyBase58 ) )
2013-07-31 18:36:51 +02:00
pubSigningKey = highlevelcrypto . privToPub ( privSigningKeyHex ) . decode (
' hex ' ) # At this time these pubkeys are 65 bytes long because they include the encoding byte which we won't be sending in the broadcast message.
2016-03-23 23:26:57 +01:00
pubEncryptionKey = unhexlify ( highlevelcrypto . privToPub (
privEncryptionKeyHex ) )
2013-07-31 18:36:51 +02:00
2015-03-09 07:35:32 +01:00
if TTL > 28 * 24 * 60 * 60 :
TTL = 28 * 24 * 60 * 60
if TTL < 60 * 60 :
TTL = 60 * 60
TTL = int ( TTL + random . randrange ( - 300 , 300 ) ) # add some randomness to the TTL
2014-11-13 22:32:31 +01:00
embeddedTime = int ( time . time ( ) + TTL )
2014-08-27 09:14:32 +02:00
payload = pack ( ' >Q ' , embeddedTime )
payload + = ' \x00 \x00 \x00 \x03 ' # object type: broadcast
2014-12-25 09:57:34 +01:00
if addressVersionNumber < = 3 :
payload + = encodeVarint ( 4 ) # broadcast version
else :
payload + = encodeVarint ( 5 ) # broadcast version
2014-08-27 09:14:32 +02:00
2013-07-31 18:36:51 +02:00
payload + = encodeVarint ( streamNumber )
2013-09-15 03:06:26 +02:00
if addressVersionNumber > = 4 :
doubleHashOfAddressData = hashlib . sha512 ( hashlib . sha512 ( encodeVarint (
addressVersionNumber ) + encodeVarint ( streamNumber ) + ripe ) . digest ( ) ) . digest ( )
2013-12-01 06:45:37 +01:00
tag = doubleHashOfAddressData [ 32 : ]
payload + = tag
else :
tag = ' '
2013-07-31 18:36:51 +02:00
2014-12-25 09:57:34 +01:00
dataToEncrypt = encodeVarint ( addressVersionNumber )
2013-07-31 18:36:51 +02:00
dataToEncrypt + = encodeVarint ( streamNumber )
2015-12-15 20:30:32 +01:00
dataToEncrypt + = getBitfield ( fromaddress ) # behavior bitfield
2013-07-31 18:36:51 +02:00
dataToEncrypt + = pubSigningKey [ 1 : ]
dataToEncrypt + = pubEncryptionKey [ 1 : ]
if addressVersionNumber > = 3 :
dataToEncrypt + = encodeVarint ( shared . config . getint ( fromaddress , ' noncetrialsperbyte ' ) )
dataToEncrypt + = encodeVarint ( shared . config . getint ( fromaddress , ' payloadlengthextrabytes ' ) )
dataToEncrypt + = ' \x02 ' # message encoding type
dataToEncrypt + = encodeVarint ( len ( ' Subject: ' + subject + ' \n ' + ' Body: ' + body ) ) #Type 2 is simple UTF-8 message encoding per the documentation on the wiki.
dataToEncrypt + = ' Subject: ' + subject + ' \n ' + ' Body: ' + body
2014-12-25 09:57:34 +01:00
dataToSign = payload + dataToEncrypt
2014-08-27 09:14:32 +02:00
2013-07-31 18:36:51 +02:00
signature = highlevelcrypto . sign (
2014-08-27 09:14:32 +02:00
dataToSign , privSigningKeyHex )
2013-07-31 18:36:51 +02:00
dataToEncrypt + = encodeVarint ( len ( signature ) )
dataToEncrypt + = signature
2014-11-13 22:32:31 +01:00
# Encrypt the broadcast with the information contained in the broadcaster's address.
# Anyone who knows the address can generate the private encryption key to decrypt
# the broadcast. This provides virtually no privacy; its purpose is to keep
# questionable and illegal content from flowing through the Internet connections
# and being stored on the disk of 3rd parties.
2013-09-15 03:06:26 +02:00
if addressVersionNumber < = 3 :
privEncryptionKey = hashlib . sha512 ( encodeVarint (
addressVersionNumber ) + encodeVarint ( streamNumber ) + ripe ) . digest ( ) [ : 32 ]
else :
privEncryptionKey = doubleHashOfAddressData [ : 32 ]
2013-12-01 06:45:37 +01:00
2014-05-21 12:15:07 +02:00
pubEncryptionKey = highlevelcrypto . pointMult ( privEncryptionKey )
2013-07-31 18:36:51 +02:00
payload + = highlevelcrypto . encrypt (
2016-03-23 23:26:57 +01:00
dataToEncrypt , hexlify ( pubEncryptionKey ) )
2013-07-31 18:36:51 +02:00
2014-08-27 09:14:32 +02:00
target = 2 * * 64 / ( shared . networkDefaultProofOfWorkNonceTrialsPerByte * ( len ( payload ) + 8 + shared . networkDefaultPayloadLengthExtraBytes + ( ( TTL * ( len ( payload ) + 8 + shared . networkDefaultPayloadLengthExtraBytes ) ) / ( 2 * * 16 ) ) ) )
2015-11-18 16:22:17 +01:00
logger . info ( ' (For broadcast message) Doing proof of work... ' )
2013-07-31 18:36:51 +02:00
shared . UISignalQueue . put ( ( ' updateSentItemStatusByAckdata ' , (
2016-04-30 11:07:27 +02:00
ackdata , tr . _translate ( " MainWindow " , " Doing work necessary to send broadcast... " ) ) ) )
2013-07-31 18:36:51 +02:00
initialHash = hashlib . sha512 ( payload ) . digest ( )
trialValue , nonce = proofofwork . run ( target , initialHash )
2015-11-18 16:22:17 +01:00
logger . info ( ' (For broadcast message) Found proof of work ' + str ( trialValue ) + ' Nonce: ' + str ( nonce ) )
2013-07-31 18:36:51 +02:00
payload = pack ( ' >Q ' , nonce ) + payload
2014-08-27 09:14:32 +02:00
# Sanity check. The payload size should never be larger than 256 KiB. There should
# be checks elsewhere in the code to not let the user try to send a message this large
# until we implement message continuation.
if len ( payload ) > 2 * * 18 : # 256 KiB
logger . critical ( ' This broadcast object is too large to send. This should never happen. Object size: %s ' % len ( payload ) )
continue
2013-06-29 19:29:35 +02:00
2013-07-31 18:36:51 +02:00
inventoryHash = calculateInventoryHash ( payload )
2014-08-27 09:14:32 +02:00
objectType = 3
2013-07-31 18:36:51 +02:00
shared . inventory [ inventoryHash ] = (
2014-08-27 09:14:32 +02:00
objectType , streamNumber , payload , embeddedTime , tag )
2016-03-23 23:26:57 +01:00
logger . info ( ' sending inv (within sendBroadcast function) for object: ' + hexlify ( inventoryHash ) )
2013-07-31 18:36:51 +02:00
shared . broadcastToSendDataQueues ( (
2013-09-07 00:55:12 +02:00
streamNumber , ' advertiseobject ' , inventoryHash ) )
2013-07-31 18:36:51 +02:00
2016-04-30 11:07:27 +02:00
shared . UISignalQueue . put ( ( ' updateSentItemStatusByAckdata ' , ( ackdata , tr . _translate ( " MainWindow " , " Broadcast sent on % 1 " ) . arg ( l10n . formatTimestamp ( ) ) ) ) )
2013-07-31 18:36:51 +02:00
# Update the status of the message in the 'sent' table to have
# a 'broadcastsent' status
2013-08-29 13:27:30 +02:00
sqlExecute (
' UPDATE sent SET msgid=?, status=?, lastactiontime=? WHERE ackdata=? ' ,
inventoryHash ,
' broadcastsent ' ,
int ( time . time ( ) ) ,
ackdata )
2013-07-31 18:36:51 +02:00
2013-06-21 23:32:22 +02:00
def sendMsg ( self ) :
2014-08-27 09:14:32 +02:00
while True : # while we have a msg that needs some work
2014-10-15 23:16:27 +02:00
# Select just one msg that needs work.
2013-08-29 13:27:30 +02:00
queryreturn = sqlQuery (
2015-03-09 07:35:32 +01:00
''' SELECT toaddress, fromaddress, subject, message, ackdata, status, ttl, retrynumber FROM sent WHERE (status= ' msgqueued ' or status= ' doingmsgpow ' or status= ' forcepow ' ) and folder= ' sent ' LIMIT 1 ''' )
2014-08-27 09:14:32 +02:00
if len ( queryreturn ) == 0 : # if there is no work to do then
break # break out of this sendMsg loop and
# wait for something to get put in the shared.workerQueue.
row = queryreturn [ 0 ]
2015-03-09 07:35:32 +01:00
toaddress , fromaddress , subject , message , ackdata , status , TTL , retryNumber = row
2014-08-27 09:14:32 +02:00
toStatus , toAddressVersionNumber , toStreamNumber , toRipe = decodeAddress (
toaddress )
fromStatus , fromAddressVersionNumber , fromStreamNumber , fromRipe = decodeAddress (
fromaddress )
# We may or may not already have the pubkey for this toAddress. Let's check.
if status == ' forcepow ' :
# if the status of this msg is 'forcepow' then clearly we have the pubkey already
# because the user could not have overridden the message about the POW being
# too difficult without knowing the required difficulty.
pass
2015-03-09 07:35:32 +01:00
elif status == ' doingmsgpow ' :
# We wouldn't have set the status to doingmsgpow if we didn't already have the pubkey
# so let's assume that we have it.
pass
2014-08-27 09:14:32 +02:00
# If we are sending a message to ourselves or a chan then we won't need an entry in the pubkeys table; we can calculate the needed pubkey using the private keys in our keys.dat file.
elif shared . config . has_section ( toaddress ) :
2013-08-29 13:27:30 +02:00
sqlExecute (
''' UPDATE sent SET status= ' doingmsgpow ' WHERE toaddress=? AND status= ' msgqueued ' ''' ,
toaddress )
2014-08-27 09:14:32 +02:00
status = ' doingmsgpow '
2015-03-09 07:35:32 +01:00
elif status == ' msgqueued ' :
2014-08-27 09:14:32 +02:00
# Let's see if we already have the pubkey in our pubkeys table
queryreturn = sqlQuery (
2015-03-09 07:35:32 +01:00
''' SELECT address FROM pubkeys WHERE address=? ''' , toaddress )
2014-08-27 09:14:32 +02:00
if queryreturn != [ ] : # If we have the needed pubkey in the pubkey table already,
# set the status of this msg to doingmsgpow
2013-08-29 13:27:30 +02:00
sqlExecute (
2014-08-27 09:14:32 +02:00
''' UPDATE sent SET status= ' doingmsgpow ' WHERE toaddress=? AND status= ' msgqueued ' ''' ,
toaddress )
status = ' doingmsgpow '
2015-03-09 07:35:32 +01:00
# mark the pubkey as 'usedpersonally' so that we don't delete it later. If the pubkey version
# is >= 4 then usedpersonally will already be set to yes because we'll only ever have
# usedpersonally v4 pubkeys in the pubkeys table.
2014-08-27 09:14:32 +02:00
sqlExecute (
2015-03-09 07:35:32 +01:00
''' UPDATE pubkeys SET usedpersonally= ' yes ' WHERE address=? ''' ,
toaddress )
2014-08-27 09:14:32 +02:00
else : # We don't have the needed pubkey in the pubkeys table already.
if toAddressVersionNumber < = 3 :
toTag = ' '
else :
toTag = hashlib . sha512 ( hashlib . sha512 ( encodeVarint ( toAddressVersionNumber ) + encodeVarint ( toStreamNumber ) + toRipe ) . digest ( ) ) . digest ( ) [ 32 : ]
2015-03-09 07:35:32 +01:00
if toaddress in shared . neededPubkeys or toTag in shared . neededPubkeys :
2014-08-27 09:14:32 +02:00
# We already sent a request for the pubkey
2013-09-15 03:06:26 +02:00
sqlExecute (
2015-03-09 07:35:32 +01:00
''' UPDATE sent SET status= ' awaitingpubkey ' , sleeptill=? WHERE toaddress=? AND status= ' msgqueued ' ''' ,
int ( time . time ( ) ) + 2.5 * 24 * 60 * 60 ,
toaddress )
shared . UISignalQueue . put ( ( ' updateSentItemStatusByToAddress ' , (
2016-04-30 11:07:27 +02:00
toaddress , tr . _translate ( " MainWindow " , ' Encryption key was requested earlier. ' ) ) ) )
2014-08-27 09:14:32 +02:00
continue #on with the next msg on which we can do some work
else :
# We have not yet sent a request for the pubkey
needToRequestPubkey = True
if toAddressVersionNumber > = 4 : # If we are trying to send to address version >= 4 then
# the needed pubkey might be encrypted in the inventory.
# If we have it we'll need to decrypt it and put it in
# the pubkeys table.
# The decryptAndCheckPubkeyPayload function expects that the shared.neededPubkeys
# dictionary already contains the toAddress and cryptor object associated with
# the tag for this toAddress.
doubleHashOfToAddressData = hashlib . sha512 ( hashlib . sha512 ( encodeVarint (
toAddressVersionNumber ) + encodeVarint ( toStreamNumber ) + toRipe ) . digest ( ) ) . digest ( )
privEncryptionKey = doubleHashOfToAddressData [ : 32 ] # The first half of the sha512 hash.
tag = doubleHashOfToAddressData [ 32 : ] # The second half of the sha512 hash.
2016-03-23 23:26:57 +01:00
shared . neededPubkeys [ tag ] = ( toaddress , highlevelcrypto . makeCryptor ( hexlify ( privEncryptionKey ) ) )
2016-03-18 02:01:59 +01:00
for value in shared . inventory . by_type_and_tag ( 1 , toTag ) :
if shared . decryptAndCheckPubkeyPayload ( value . payload , toaddress ) == ' successful ' : #if valid, this function also puts it in the pubkeys table.
needToRequestPubkey = False
sqlExecute (
''' UPDATE sent SET status= ' doingmsgpow ' , retrynumber=0 WHERE toaddress=? AND (status= ' msgqueued ' or status= ' awaitingpubkey ' or status= ' doingpubkeypow ' ) ''' ,
toaddress )
del shared . neededPubkeys [ tag ]
break
#else: # There was something wrong with this pubkey object even
# though it had the correct tag- almost certainly because
# of malicious behavior or a badly programmed client. If
# there are any other pubkeys in our inventory with the correct
# tag then we'll try to decrypt those.
2014-08-27 09:14:32 +02:00
if needToRequestPubkey :
sqlExecute (
''' UPDATE sent SET status= ' doingpubkeypow ' WHERE toaddress=? AND status= ' msgqueued ' ''' ,
toaddress )
2015-03-09 07:35:32 +01:00
shared . UISignalQueue . put ( ( ' updateSentItemStatusByToAddress ' , (
2016-04-30 11:07:27 +02:00
toaddress , tr . _translate ( " MainWindow " , ' Sending a request for the recipient \' s encryption key. ' ) ) ) )
2014-08-27 09:14:32 +02:00
self . requestPubKey ( toaddress )
continue #on with the next msg on which we can do some work
# At this point we know that we have the necessary pubkey in the pubkeys table.
2015-03-09 07:35:32 +01:00
if retryNumber == 0 :
if TTL > 28 * 24 * 60 * 60 :
TTL = 28 * 24 * 60 * 60
else :
TTL = 28 * 24 * 60 * 60
TTL = int ( TTL + random . randrange ( - 300 , 300 ) ) # add some randomness to the TTL
2014-11-13 22:32:31 +01:00
embeddedTime = int ( time . time ( ) + TTL )
2014-08-27 09:14:32 +02:00
if not shared . config . has_section ( toaddress ) : # if we aren't sending this to ourselves or a chan
2013-09-30 01:24:27 +02:00
shared . ackdataForWhichImWatching [ ackdata ] = 0
shared . UISignalQueue . put ( ( ' updateSentItemStatusByAckdata ' , (
2016-04-30 11:07:27 +02:00
ackdata , tr . _translate ( " MainWindow " , " Looking up the receiver \' s public key " ) ) ) )
2015-11-18 16:22:17 +01:00
logger . info ( ' Sending a message. ' )
logger . debug ( ' First 150 characters of message: ' + repr ( message [ : 150 ] ) )
2013-06-29 19:29:35 +02:00
2013-09-30 01:24:27 +02:00
# Let us fetch the recipient's public key out of our database. If
# the required proof of work difficulty is too hard then we'll
# abort.
queryreturn = sqlQuery (
2015-03-09 07:35:32 +01:00
' SELECT transmitdata FROM pubkeys WHERE address=? ' ,
toaddress )
2013-09-30 01:24:27 +02:00
for row in queryreturn :
pubkeyPayload , = row
2014-12-25 09:57:34 +01:00
# The pubkey message is stored with the following items all appended:
# -address version
# -stream number
# -behavior bitfield
# -pub signing key
# -pub encryption key
# -nonce trials per byte (if address version is >= 3)
# -length extra bytes (if address version is >= 3)
readPosition = 1 # to bypass the address version whose length is definitely 1
2013-09-30 01:24:27 +02:00
streamNumber , streamNumberLength = decodeVarint (
2013-06-21 23:32:22 +02:00
pubkeyPayload [ readPosition : readPosition + 10 ] )
2013-09-30 01:24:27 +02:00
readPosition + = streamNumberLength
behaviorBitfield = pubkeyPayload [ readPosition : readPosition + 4 ]
# Mobile users may ask us to include their address's RIPE hash on a message
# unencrypted. Before we actually do it the sending human must check a box
# in the settings menu to allow it.
if shared . isBitSetWithinBitfield ( behaviorBitfield , 30 ) : # if receiver is a mobile device who expects that their address RIPE is included unencrypted on the front of the message..
if not shared . safeConfigGetBoolean ( ' bitmessagesettings ' , ' willinglysendtomobile ' ) : # if we are Not willing to include the receiver's RIPE hash on the message..
logger . info ( ' The receiver is a mobile user but the sender (you) has not selected that you are willing to send to mobiles. Aborting send. ' )
2016-04-30 11:07:27 +02:00
shared . UISignalQueue . put ( ( ' updateSentItemStatusByAckdata ' , ( ackdata , tr . _translate ( " MainWindow " , ' Problem: Destination is a mobile device who requests that the destination be included in the message but this is disallowed in your settings. % 1 ' ) . arg ( l10n . formatTimestamp ( ) ) ) ) )
2013-09-30 01:24:27 +02:00
# if the human changes their setting and then sends another message or restarts their client, this one will send at that time.
continue