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
from class_addressGenerator import pointMult
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
2013-06-21 23:32:22 +02:00
# This thread, of which there is only one, does the heavy lifting:
# calculating POWs.
class singleWorker ( threading . Thread ) :
def __init__ ( self ) :
# QThread.__init__(self, parent)
threading . Thread . __init__ ( self )
def run ( self ) :
2013-08-29 13:27:30 +02:00
queryreturn = sqlQuery (
2013-09-13 06:27:34 +02:00
''' SELECT toripe, toaddress FROM sent WHERE ((status= ' awaitingpubkey ' OR status= ' doingpubkeypow ' ) AND folder= ' sent ' ) ''' )
2013-06-21 23:32:22 +02:00
for row in queryreturn :
2013-09-13 06:27:34 +02:00
toripe , toaddress = row
2013-09-15 03:06:26 +02:00
toStatus , toAddressVersionNumber , toStreamNumber , toRipe = decodeAddress ( toaddress )
2013-09-13 06:27:34 +02:00
if toAddressVersionNumber < = 3 :
shared . neededPubkeys [ toripe ] = 0
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 : ]
shared . neededPubkeys [ tag ] = highlevelcrypto . makeCryptor ( privEncryptionKey . encode ( ' hex ' ) ) # 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
2013-06-24 21:51:01 +02:00
# Initialize the shared.ackdataForWhichImWatching data structure using data
2013-06-21 23:32:22 +02:00
# from the sql database.
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
print ' Watching for ackdata ' , ackdata . encode ( ' hex ' )
2013-06-24 21:51:01 +02:00
shared . ackdataForWhichImWatching [ ackdata ] = 0
2013-06-21 23:32:22 +02:00
2013-08-29 13:27:30 +02:00
queryreturn = sqlQuery (
2013-06-21 23:32:22 +02:00
''' SELECT DISTINCT toaddress FROM sent WHERE (status= ' doingpubkeypow ' AND folder= ' sent ' ) ''' )
for row in queryreturn :
toaddress , = row
self . requestPubKey ( toaddress )
time . sleep (
10 ) # give some time for the GUI to start before we start on existing POW tasks.
self . sendMsg ( )
# just in case there are any pending tasks for msg
# messages that have yet to be sent.
self . sendBroadcast ( )
# just in case there are any tasks for Broadcasts
# that have yet to be sent.
while True :
command , data = shared . workerQueue . get ( )
if command == ' sendmessage ' :
self . sendMsg ( )
elif command == ' sendbroadcast ' :
self . sendBroadcast ( )
elif command == ' doPOWForMyV2Pubkey ' :
self . doPOWForMyV2Pubkey ( data )
2013-07-22 07:10:22 +02:00
elif command == ' sendOutOrStoreMyV3Pubkey ' :
self . sendOutOrStoreMyV3Pubkey ( data )
2013-09-13 06:27:34 +02:00
elif command == ' sendOutOrStoreMyV4Pubkey ' :
self . sendOutOrStoreMyV4Pubkey ( data )
2013-06-21 23:32:22 +02:00
else :
2013-06-29 19:29:35 +02:00
with shared . printLock :
sys . stderr . write (
' Probable programming error: The command sent to the workerThread is weird. It is: %s \n ' % command )
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 )
embeddedTime = int ( time . time ( ) + random . randrange (
- 300 , 300 ) ) # the current time plus or minus five minutes
payload = pack ( ' >I ' , ( embeddedTime ) )
payload + = encodeVarint ( addressVersionNumber ) # Address version number
payload + = encodeVarint ( streamNumber )
payload + = ' \x00 \x00 \x00 \x01 ' # bitfield of features supported by me (see the wiki).
try :
privSigningKeyBase58 = shared . config . get (
myAddress , ' privsigningkey ' )
privEncryptionKeyBase58 = shared . config . get (
myAddress , ' privencryptionkey ' )
except Exception as err :
2013-06-29 19:29:35 +02:00
with shared . printLock :
sys . stderr . write (
' 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
privSigningKeyHex = shared . decodeWalletImportFormat (
privSigningKeyBase58 ) . encode ( ' hex ' )
privEncryptionKeyHex = shared . decodeWalletImportFormat (
privEncryptionKeyBase58 ) . encode ( ' hex ' )
pubSigningKey = highlevelcrypto . privToPub (
privSigningKeyHex ) . decode ( ' hex ' )
pubEncryptionKey = highlevelcrypto . privToPub (
privEncryptionKeyHex ) . decode ( ' hex ' )
payload + = pubSigningKey [ 1 : ]
payload + = pubEncryptionKey [ 1 : ]
# Do the POW for this pubkey message
target = 2 * * 64 / ( ( len ( payload ) + shared . networkDefaultPayloadLengthExtraBytes +
8 ) * shared . networkDefaultProofOfWorkNonceTrialsPerByte )
print ' (For pubkey message) Doing proof of work... '
initialHash = hashlib . sha512 ( payload ) . digest ( )
trialValue , nonce = proofofwork . run ( target , initialHash )
print ' (For pubkey message) Found proof of work ' , trialValue , ' Nonce: ' , nonce
payload = pack ( ' >Q ' , nonce ) + payload
inventoryHash = calculateInventoryHash ( payload )
objectType = ' pubkey '
shared . inventory [ inventoryHash ] = (
2013-09-13 06:27:34 +02:00
objectType , streamNumber , payload , embeddedTime , ' ' )
2013-09-07 00:55:12 +02:00
shared . inventorySets [ streamNumber ] . add ( inventoryHash )
2013-06-21 23:32:22 +02:00
2013-06-29 19:29:35 +02:00
with shared . printLock :
print ' broadcasting inv with hash: ' , inventoryHash . encode ( ' hex ' )
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 ( ) ) ) )
with open ( shared . appdata + ' keys.dat ' , ' wb ' ) as configfile :
shared . config . write ( configfile )
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 ' ) :
with shared . printLock :
print ' This is a chan address. Not sending pubkey. '
return
2013-06-21 23:32:22 +02:00
status , addressVersionNumber , streamNumber , hash = decodeAddress (
myAddress )
embeddedTime = int ( time . time ( ) + random . randrange (
- 300 , 300 ) ) # the current time plus or minus five minutes
payload = pack ( ' >I ' , ( embeddedTime ) )
payload + = encodeVarint ( addressVersionNumber ) # Address version number
payload + = encodeVarint ( streamNumber )
payload + = ' \x00 \x00 \x00 \x01 ' # bitfield of features supported by me (see the wiki).
try :
privSigningKeyBase58 = shared . config . get (
myAddress , ' privsigningkey ' )
privEncryptionKeyBase58 = shared . config . get (
myAddress , ' privencryptionkey ' )
except Exception as err :
2013-06-29 19:29:35 +02:00
with shared . printLock :
sys . stderr . write (
2013-07-22 07:10:22 +02:00
' 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
privSigningKeyHex = shared . decodeWalletImportFormat (
privSigningKeyBase58 ) . encode ( ' hex ' )
privEncryptionKeyHex = shared . decodeWalletImportFormat (
privEncryptionKeyBase58 ) . encode ( ' hex ' )
pubSigningKey = highlevelcrypto . privToPub (
privSigningKeyHex ) . decode ( ' hex ' )
pubEncryptionKey = highlevelcrypto . privToPub (
privEncryptionKeyHex ) . decode ( ' hex ' )
payload + = pubSigningKey [ 1 : ]
payload + = pubEncryptionKey [ 1 : ]
payload + = encodeVarint ( shared . config . getint (
myAddress , ' noncetrialsperbyte ' ) )
payload + = encodeVarint ( shared . config . getint (
myAddress , ' payloadlengthextrabytes ' ) )
signature = highlevelcrypto . sign ( payload , privSigningKeyHex )
payload + = encodeVarint ( len ( signature ) )
payload + = signature
2013-09-30 01:24:27 +02:00
# Do the POW for this pubkey message
target = 2 * * 64 / ( ( len ( payload ) + shared . networkDefaultPayloadLengthExtraBytes +
8 ) * shared . networkDefaultProofOfWorkNonceTrialsPerByte )
print ' (For pubkey message) Doing proof of work... '
initialHash = hashlib . sha512 ( payload ) . digest ( )
trialValue , nonce = proofofwork . run ( target , initialHash )
print ' (For pubkey message) Found proof of work ' , trialValue , ' Nonce: ' , 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 )
objectType = ' pubkey '
shared . inventory [ inventoryHash ] = (
objectType , streamNumber , payload , embeddedTime , ' ' )
shared . inventorySets [ streamNumber ] . add ( inventoryHash )
2013-06-21 23:32:22 +02:00
2013-09-30 01:24:27 +02:00
with shared . printLock :
print ' broadcasting inv with hash: ' , inventoryHash . encode ( ' hex ' )
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 ( ) ) ) )
with open ( shared . appdata + ' keys.dat ' , ' wb ' ) as configfile :
shared . config . write ( configfile )
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 ' ) :
with shared . printLock :
print ' This is a chan address. Not sending pubkey. '
return
2013-09-13 06:27:34 +02:00
status , addressVersionNumber , streamNumber , hash = decodeAddress (
myAddress )
embeddedTime = int ( time . time ( ) + random . randrange (
- 300 , 300 ) ) # the current time plus or minus five minutes
payload = pack ( ' >Q ' , ( embeddedTime ) )
payload + = encodeVarint ( addressVersionNumber ) # Address version number
payload + = encodeVarint ( streamNumber )
dataToEncrypt = ' \x00 \x00 \x00 \x01 ' # bitfield of features supported by me (see the wiki).
try :
privSigningKeyBase58 = shared . config . get (
myAddress , ' privsigningkey ' )
privEncryptionKeyBase58 = shared . config . get (
myAddress , ' privencryptionkey ' )
except Exception as err :
with shared . printLock :
sys . stderr . write (
' Error within sendOutOrStoreMyV4Pubkey. Could not read the keys from the keys.dat file for a requested address. %s \n ' % err )
return
privSigningKeyHex = shared . decodeWalletImportFormat (
privSigningKeyBase58 ) . encode ( ' hex ' )
privEncryptionKeyHex = shared . decodeWalletImportFormat (
privEncryptionKeyBase58 ) . encode ( ' hex ' )
pubSigningKey = highlevelcrypto . privToPub (
privSigningKeyHex ) . decode ( ' hex ' )
pubEncryptionKey = highlevelcrypto . privToPub (
privEncryptionKeyHex ) . decode ( ' hex ' )
dataToEncrypt + = pubSigningKey [ 1 : ]
dataToEncrypt + = pubEncryptionKey [ 1 : ]
dataToEncrypt + = encodeVarint ( shared . config . getint (
myAddress , ' noncetrialsperbyte ' ) )
dataToEncrypt + = encodeVarint ( shared . config . getint (
myAddress , ' payloadlengthextrabytes ' ) )
2013-09-18 06:04:01 +02:00
2013-09-13 06:27:34 +02:00
signature = highlevelcrypto . sign ( payload + dataToEncrypt , privSigningKeyHex )
dataToEncrypt + = encodeVarint ( len ( signature ) )
dataToEncrypt + = signature
2013-09-30 01:24:27 +02:00
# Let us encrypt the necessary data. We will use a hash of the data
# 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
privEncryptionKey = doubleHashOfAddressData [ : 32 ]
pubEncryptionKey = pointMult ( privEncryptionKey )
payload + = highlevelcrypto . encrypt (
dataToEncrypt , pubEncryptionKey . encode ( ' hex ' ) )
2013-09-18 06:04:01 +02:00
2013-09-30 01:24:27 +02:00
# Do the POW for this pubkey message
target = 2 * * 64 / ( ( len ( payload ) + shared . networkDefaultPayloadLengthExtraBytes +
8 ) * shared . networkDefaultProofOfWorkNonceTrialsPerByte )
print ' (For pubkey message) Doing proof of work... '
initialHash = hashlib . sha512 ( payload ) . digest ( )
trialValue , nonce = proofofwork . run ( target , initialHash )
print ' (For pubkey message) Found proof of work ' , trialValue , ' Nonce: ' , 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 )
objectType = ' pubkey '
shared . inventory [ inventoryHash ] = (
objectType , streamNumber , payload , embeddedTime , doubleHashOfAddressData [ 32 : ] )
shared . inventorySets [ streamNumber ] . add ( inventoryHash )
2013-09-13 06:27:34 +02:00
2013-09-30 01:24:27 +02:00
with shared . printLock :
print ' broadcasting inv with hash: ' , inventoryHash . encode ( ' hex ' )
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 ( ) ) ) )
with open ( shared . appdata + ' keys.dat ' , ' wb ' ) as configfile :
shared . config . write ( configfile )
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 (
''' SELECT fromaddress, subject, message, ackdata FROM sent WHERE status=? and folder= ' sent ' ''' , ' broadcastqueued ' )
2013-06-21 23:32:22 +02:00
for row in queryreturn :
fromaddress , subject , body , ackdata = row
status , addressVersionNumber , streamNumber , ripe = decodeAddress (
fromaddress )
2013-07-31 18:36:51 +02:00
if addressVersionNumber < = 1 :
2013-06-29 19:29:35 +02:00
with shared . printLock :
sys . stderr . write (
' 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 ' , (
ackdata , tr . translateText ( " MainWindow " , " Error! Could not find sender address (your address) in the keys.dat file. " ) ) ) )
continue
privSigningKeyHex = shared . decodeWalletImportFormat (
privSigningKeyBase58 ) . encode ( ' hex ' )
privEncryptionKeyHex = shared . decodeWalletImportFormat (
privEncryptionKeyBase58 ) . encode ( ' hex ' )
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.
pubEncryptionKey = highlevelcrypto . privToPub (
privEncryptionKeyHex ) . decode ( ' hex ' )
payload = pack ( ' >Q ' , ( int ( time . time ( ) ) + random . randrange (
- 300 , 300 ) ) ) # the current time plus or minus five minutes
2013-09-18 06:04:01 +02:00
if addressVersionNumber < = 3 :
payload + = encodeVarint ( 2 ) # broadcast version
else :
payload + = encodeVarint ( 3 ) # broadcast version
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
2013-09-15 03:06:26 +02:00
if addressVersionNumber < = 3 :
dataToEncrypt = encodeVarint ( 2 ) # broadcast version
else :
dataToEncrypt = encodeVarint ( 3 ) # broadcast version
2013-07-31 18:36:51 +02:00
dataToEncrypt + = encodeVarint ( addressVersionNumber )
dataToEncrypt + = encodeVarint ( streamNumber )
dataToEncrypt + = ' \x00 \x00 \x00 \x01 ' # behavior bitfield
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
signature = highlevelcrypto . sign (
dataToEncrypt , privSigningKeyHex )
dataToEncrypt + = encodeVarint ( len ( signature ) )
dataToEncrypt + = signature
# 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
2013-07-31 18:36:51 +02:00
pubEncryptionKey = pointMult ( privEncryptionKey )
payload + = highlevelcrypto . encrypt (
dataToEncrypt , pubEncryptionKey . encode ( ' hex ' ) )
target = 2 * * 64 / ( ( len (
payload ) + shared . networkDefaultPayloadLengthExtraBytes + 8 ) * shared . networkDefaultProofOfWorkNonceTrialsPerByte )
print ' (For broadcast message) Doing proof of work... '
shared . UISignalQueue . put ( ( ' updateSentItemStatusByAckdata ' , (
ackdata , tr . translateText ( " MainWindow " , " Doing work necessary to send broadcast... " ) ) ) )
initialHash = hashlib . sha512 ( payload ) . digest ( )
trialValue , nonce = proofofwork . run ( target , initialHash )
print ' (For broadcast message) Found proof of work ' , trialValue , ' Nonce: ' , nonce
payload = pack ( ' >Q ' , nonce ) + payload
2013-06-29 19:29:35 +02:00
2013-07-31 18:36:51 +02:00
inventoryHash = calculateInventoryHash ( payload )
objectType = ' broadcast '
shared . inventory [ inventoryHash ] = (
2013-12-01 06:45:37 +01:00
objectType , streamNumber , payload , int ( time . time ( ) ) , tag )
2013-09-07 00:55:12 +02:00
shared . inventorySets [ streamNumber ] . add ( inventoryHash )
2013-07-31 18:36:51 +02:00
with shared . printLock :
print ' sending inv (within sendBroadcast function) for object: ' , inventoryHash . encode ( ' hex ' )
shared . broadcastToSendDataQueues ( (
2013-09-07 00:55:12 +02:00
streamNumber , ' advertiseobject ' , inventoryHash ) )
2013-07-31 18:36:51 +02:00
shared . UISignalQueue . put ( ( ' updateSentItemStatusByAckdata ' , ( ackdata , tr . translateText ( " MainWindow " , " Broadcast sent on % 1 " ) . arg ( unicode (
strftime ( shared . config . get ( ' bitmessagesettings ' , ' timeformat ' ) , localtime ( int ( time . time ( ) ) ) ) , ' utf-8 ' ) ) ) ) )
# 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 ) :
# Check to see if there are any messages queued to be sent
2013-08-29 13:27:30 +02:00
queryreturn = sqlQuery (
2013-06-21 23:32:22 +02:00
''' SELECT DISTINCT toaddress FROM sent WHERE (status= ' msgqueued ' AND folder= ' sent ' ) ''' )
for row in queryreturn : # For each address to which we need to send a message, check to see if we have its pubkey already.
toaddress , = row
2013-09-15 03:06:26 +02:00
status , toAddressVersion , toStreamNumber , toRipe = decodeAddress ( toaddress )
2013-09-30 01:24:27 +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.
if shared . config . has_section ( toaddress ) :
2013-09-30 05:01:56 +02:00
sqlExecute (
''' UPDATE sent SET status= ' doingmsgpow ' WHERE toaddress=? AND status= ' msgqueued ' ''' ,
toaddress )
2013-09-30 01:24:27 +02:00
continue
2013-08-29 13:27:30 +02:00
queryreturn = sqlQuery (
2013-09-30 01:24:27 +02:00
''' SELECT hash FROM pubkeys WHERE hash=? AND addressversion=? ''' , toRipe , toAddressVersion )
2013-09-15 03:06:26 +02:00
if queryreturn != [ ] : # If we have the needed pubkey in the pubkey table already, set the status to doingmsgpow (we'll do it further down)
2013-08-29 13:27:30 +02:00
sqlExecute (
''' UPDATE sent SET status= ' doingmsgpow ' WHERE toaddress=? AND status= ' msgqueued ' ''' ,
toaddress )
2013-09-15 03:06:26 +02:00
else : # We don't have the needed pubkey in the pubkey table already.
if toAddressVersion < = 3 :
toTag = ' '
else :
toTag = hashlib . sha512 ( hashlib . sha512 ( encodeVarint ( toAddressVersion ) + encodeVarint ( toStreamNumber ) + toRipe ) . digest ( ) ) . digest ( ) [ 32 : ]
if toRipe in shared . neededPubkeys or toTag in shared . neededPubkeys :
2013-06-21 23:32:22 +02:00
# We already sent a request for the pubkey
2013-08-29 13:27:30 +02:00
sqlExecute (
''' UPDATE sent SET status= ' awaitingpubkey ' WHERE toaddress=? AND status= ' msgqueued ' ''' , toaddress )
2013-06-21 23:32:22 +02:00
shared . UISignalQueue . put ( ( ' updateSentItemStatusByHash ' , (
2013-09-15 03:06:26 +02:00
toRipe , tr . translateText ( " MainWindow " , ' Encryption key was requested earlier. ' ) ) ) )
2013-06-21 23:32:22 +02:00
else :
# We have not yet sent a request for the pubkey
2013-09-15 03:06:26 +02:00
needToRequestPubkey = True
2013-09-18 06:04:01 +02:00
if toAddressVersion > = 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.
2013-09-15 03:06:26 +02:00
queryreturn = sqlQuery (
''' SELECT payload FROM inventory WHERE objecttype= ' pubkey ' and tag=? ''' , toTag )
if queryreturn != [ ] : # if there was a pubkey in our inventory with the correct tag, we need to try to decrypt it.
for row in queryreturn :
data , = row
2013-11-20 07:29:37 +01:00
if shared . decryptAndCheckPubkeyPayload ( data , toaddress ) == ' successful ' :
2013-09-18 06:04:01 +02:00
needToRequestPubkey = False
print ' debug. successfully decrypted and checked pubkey from sql inventory. ' #testing
sqlExecute (
''' UPDATE sent SET status= ' doingmsgpow ' WHERE toaddress=? AND status= ' msgqueued ' ''' ,
toaddress )
break
else : # There was something wrong with this pubkey even though it had the correct tag- almost certainly because of malicious behavior or a badly programmed client.
2013-09-15 03:06:26 +02:00
continue
2013-09-18 06:04:01 +02:00
if needToRequestPubkey : # Obviously we had no success looking in the sql inventory. Let's look through the memory inventory.
with shared . inventoryLock :
for hash , storedValue in shared . inventory . items ( ) :
objectType , streamNumber , payload , receivedTime , tag = storedValue
if objectType == ' pubkey ' and tag == toTag :
2013-11-20 07:29:37 +01:00
result = shared . decryptAndCheckPubkeyPayload ( payload , toaddress ) #if valid, this function also puts it in the pubkeys table.
2013-09-18 06:04:01 +02:00
if result == ' successful ' :
print ' debug. successfully decrypted and checked pubkey from memory inventory. '
needToRequestPubkey = False
sqlExecute (
''' UPDATE sent SET status= ' doingmsgpow ' WHERE toaddress=? AND status= ' msgqueued ' ''' ,
toaddress )
break
2013-09-15 03:06:26 +02:00
if needToRequestPubkey :
sqlExecute (
''' UPDATE sent SET status= ' doingpubkeypow ' WHERE toaddress=? AND status= ' msgqueued ' ''' ,
toaddress )
shared . UISignalQueue . put ( ( ' updateSentItemStatusByHash ' , (
toRipe , tr . translateText ( " MainWindow " , ' Sending a request for the recipient \' s encryption key. ' ) ) ) )
self . requestPubKey ( toaddress )
2013-06-21 23:32:22 +02:00
# Get all messages that are ready to be sent, and also all messages
# which we have sent in the last 28 days which were previously marked
# as 'toodifficult'. If the user as raised the maximum acceptable
# difficulty then those messages may now be sendable.
2013-08-29 13:27:30 +02:00
queryreturn = sqlQuery (
''' SELECT toaddress, toripe, fromaddress, subject, message, ackdata, status FROM sent WHERE (status= ' doingmsgpow ' or status= ' forcepow ' or (status= ' toodifficult ' and lastactiontime>?)) and folder= ' sent ' ''' ,
int ( time . time ( ) ) - 2419200 )
2013-06-21 23:32:22 +02:00
for row in queryreturn : # For each message we need to send..
toaddress , toripe , fromaddress , subject , message , ackdata , status = row
toStatus , toAddressVersionNumber , toStreamNumber , toHash = decodeAddress (
toaddress )
fromStatus , fromAddressVersionNumber , fromStreamNumber , fromHash = decodeAddress (
fromaddress )
2013-06-29 19:29:35 +02:00
2013-09-30 01:24:27 +02:00
if not shared . config . has_section ( toaddress ) :
# There is a remote possibility that we may no longer have the
# recipient's pubkey. Let us make sure we still have it or else the
# sendMsg function will appear to freeze. This can happen if the
# user sends a message but doesn't let the POW function finish,
# then leaves their client off for a long time which could cause
# the needed pubkey to expire and be deleted.
queryreturn = sqlQuery (
''' SELECT hash FROM pubkeys WHERE hash=? AND addressversion=? ''' ,
toripe ,
toAddressVersionNumber )
if queryreturn == [ ] and toripe not in shared . neededPubkeys :
# We no longer have the needed pubkey and we haven't requested
# it.
with shared . printLock :
sys . stderr . write (
' For some reason, the status of a message in our outbox is \' doingmsgpow \' even though we lack the pubkey. Here is the RIPE hash of the needed pubkey: %s \n ' % toripe . encode ( ' hex ' ) )
sqlExecute (
2013-09-30 05:01:56 +02:00
''' UPDATE sent SET status= ' doingpubkeypow ' WHERE toaddress=? AND status= ' doingmsgpow ' ''' , toaddress )
2013-09-30 01:24:27 +02:00
shared . UISignalQueue . put ( ( ' updateSentItemStatusByHash ' , (
toripe , tr . translateText ( " MainWindow " , ' Sending a request for the recipient \' s encryption key. ' ) ) ) )
self . requestPubKey ( toaddress )
continue
shared . ackdataForWhichImWatching [ ackdata ] = 0
shared . UISignalQueue . put ( ( ' updateSentItemStatusByAckdata ' , (
ackdata , tr . translateText ( " MainWindow " , " Looking up the receiver \' s public key " ) ) ) )
2013-06-29 19:29:35 +02:00
with shared . printLock :
2013-09-30 01:24:27 +02:00
print ' Sending a message. First 150 characters of message: ' , repr ( message [ : 150 ] )
2013-06-29 19:29:35 +02:00
2013-09-30 01:24:27 +02:00
# mark the pubkey as 'usedpersonally' so that we don't ever delete
# it.
sqlExecute (
''' UPDATE pubkeys SET usedpersonally= ' yes ' WHERE hash=? and addressversion=? ''' ,
toripe ,
toAddressVersionNumber )
# 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 (
' SELECT transmitdata FROM pubkeys WHERE hash=? and addressversion=? ' ,
toripe ,
toAddressVersionNumber )
if queryreturn == [ ] :
with shared . printLock :
sys . stderr . write (
' (within sendMsg) The needed pubkey was not found. This should never happen. Aborting send. \n ' )
return
for row in queryreturn :
pubkeyPayload , = row
# The pubkey message is stored the way we originally received it
# which means that we need to read beyond things like the nonce and
# time to get to the actual public keys.
if toAddressVersionNumber < = 3 :
readPosition = 8 # to bypass the nonce
elif toAddressVersionNumber > = 4 :
readPosition = 0 # the nonce is not included here so we don't need to skip over it.
2013-06-21 23:32:22 +02:00
pubkeyEmbeddedTime , = unpack (
2013-09-30 01:24:27 +02:00
' >I ' , pubkeyPayload [ readPosition : readPosition + 4 ] )
# This section is used for the transition from 32 bit time to 64
# bit time in the protocol.
if pubkeyEmbeddedTime == 0 :
pubkeyEmbeddedTime , = unpack (
' >Q ' , pubkeyPayload [ readPosition : readPosition + 8 ] )
readPosition + = 8
else :
readPosition + = 4
readPosition + = 1 # to bypass the address version whose length is definitely 1
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. ' )
shared . UISignalQueue . put ( ( ' updateSentItemStatusByAckdata ' , ( ackdata , tr . translateText ( " 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 ( unicode ( strftime ( shared . config . get ( ' bitmessagesettings ' , ' timeformat ' ) , localtime ( int ( time . time ( ) ) ) ) , ' utf-8 ' ) ) ) ) )
# if the human changes their setting and then sends another message or restarts their client, this one will send at that time.
continue
readPosition + = 4 # to bypass the bitfield of behaviors
# pubSigningKeyBase256 =
# pubkeyPayload[readPosition:readPosition+64] #We don't use this
# key for anything here.
readPosition + = 64
pubEncryptionKeyBase256 = pubkeyPayload [
readPosition : readPosition + 64 ]
readPosition + = 64
# Let us fetch the amount of work required by the recipient.
if toAddressVersionNumber == 2 :
2013-06-21 23:32:22 +02:00
requiredAverageProofOfWorkNonceTrialsPerByte = shared . networkDefaultProofOfWorkNonceTrialsPerByte
requiredPayloadLengthExtraBytes = shared . networkDefaultPayloadLengthExtraBytes
2013-09-30 01:24:27 +02:00
shared . UISignalQueue . put ( ( ' updateSentItemStatusByAckdata ' , (
ackdata , tr . translateText ( " MainWindow " , " Doing work necessary to send message. \n There is no required difficulty for version 2 addresses like this. " ) ) ) )
elif toAddressVersionNumber > = 3 :
requiredAverageProofOfWorkNonceTrialsPerByte , varintLength = decodeVarint (
pubkeyPayload [ readPosition : readPosition + 10 ] )
readPosition + = varintLength
requiredPayloadLengthExtraBytes , varintLength = decodeVarint (
pubkeyPayload [ readPosition : readPosition + 10 ] )
readPosition + = varintLength
if requiredAverageProofOfWorkNonceTrialsPerByte < shared . networkDefaultProofOfWorkNonceTrialsPerByte : # We still have to meet a minimum POW difficulty regardless of what they say is allowed in order to get our message to propagate through the network.
requiredAverageProofOfWorkNonceTrialsPerByte = shared . networkDefaultProofOfWorkNonceTrialsPerByte
if requiredPayloadLengthExtraBytes < shared . networkDefaultPayloadLengthExtraBytes :
requiredPayloadLengthExtraBytes = shared . networkDefaultPayloadLengthExtraBytes
shared . UISignalQueue . put ( ( ' updateSentItemStatusByAckdata ' , ( ackdata , tr . translateText ( " MainWindow " , " Doing work necessary to send message. \n Receiver \' s required difficulty: % 1 and % 2 " ) . arg ( str ( float (
requiredAverageProofOfWorkNonceTrialsPerByte ) / shared . networkDefaultProofOfWorkNonceTrialsPerByte ) ) . arg ( str ( float ( requiredPayloadLengthExtraBytes ) / shared . networkDefaultPayloadLengthExtraBytes ) ) ) ) )
if status != ' forcepow ' :
if ( requiredAverageProofOfWorkNonceTrialsPerByte > shared . config . getint ( ' bitmessagesettings ' , ' maxacceptablenoncetrialsperbyte ' ) and shared . config . getint ( ' bitmessagesettings ' , ' maxacceptablenoncetrialsperbyte ' ) != 0 ) or ( requiredPayloadLengthExtraBytes > shared . config . getint ( ' bitmessagesettings ' , ' maxacceptablepayloadlengthextrabytes ' ) and shared . config . getint ( ' bitmessagesettings ' , ' maxacceptablepayloadlengthextrabytes ' ) != 0 ) :
# The demanded difficulty is more than we are willing
# to do.
sqlExecute (
''' UPDATE sent SET status= ' toodifficult ' WHERE ackdata=? ''' ,
ackdata )
shared . UISignalQueue . put ( ( ' updateSentItemStatusByAckdata ' , ( ackdata , tr . translateText ( " MainWindow " , " Problem: The work demanded by the recipient ( % 1 and % 2) is more difficult than you are willing to do. " ) . arg ( str ( float ( requiredAverageProofOfWorkNonceTrialsPerByte ) / shared . networkDefaultProofOfWorkNonceTrialsPerByte ) ) . arg ( str ( float (
requiredPayloadLengthExtraBytes ) / shared . networkDefaultPayloadLengthExtraBytes ) ) . arg ( unicode ( strftime ( shared . config . get ( ' bitmessagesettings ' , ' timeformat ' ) , localtime ( int ( time . time ( ) ) ) ) , ' utf-8 ' ) ) ) ) )
continue
else : # if we are sending a message to ourselves or a chan..
with shared . printLock :
print ' Sending a message. First 150 characters of message: ' , repr ( message [ : 150 ] )
behaviorBitfield = ' \x00 \x00 \x00 \x01 '
2013-06-21 23:32:22 +02:00
2013-09-30 01:24:27 +02:00
try :
privEncryptionKeyBase58 = shared . config . get (
toaddress , ' privencryptionkey ' )
except Exception as err :
shared . UISignalQueue . put ( ( ' updateSentItemStatusByAckdata ' , ( ackdata , tr . translateText ( " MainWindow " , ' Problem: You are trying to send a message to yourself or a chan but your encryption key could not be found in the keys.dat file. Could not encrypt message. % 1 ' ) . arg ( unicode ( strftime ( shared . config . get ( ' bitmessagesettings ' , ' timeformat ' ) , localtime ( int ( time . time ( ) ) ) ) , ' utf-8 ' ) ) ) ) )
with shared . printLock :
sys . stderr . write (
' Error within sendMsg. Could not read the keys from the keys.dat file for our own address. %s \n ' % err )
continue
privEncryptionKeyHex = shared . decodeWalletImportFormat (
privEncryptionKeyBase58 ) . encode ( ' hex ' )
pubEncryptionKeyBase256 = highlevelcrypto . privToPub (
privEncryptionKeyHex ) . decode ( ' hex ' ) [ 1 : ]
requiredAverageProofOfWorkNonceTrialsPerByte = shared . networkDefaultProofOfWorkNonceTrialsPerByte
requiredPayloadLengthExtraBytes = shared . networkDefaultPayloadLengthExtraBytes
shared . UISignalQueue . put ( ( ' updateSentItemStatusByAckdata ' , (
ackdata , tr . translateText ( " MainWindow " , " Doing work necessary to send message. " ) ) ) )
2013-08-26 21:23:12 +02:00
2013-06-21 23:32:22 +02:00
embeddedTime = pack ( ' >Q ' , ( int ( time . time ( ) ) + random . randrange (
2013-12-06 07:52:19 +01:00
- 300 , 300 ) ) ) # the current time plus or minus five minutes.
2013-06-21 23:32:22 +02:00
if fromAddressVersionNumber == 2 :
payload = ' \x01 ' # Message version.
payload + = encodeVarint ( fromAddressVersionNumber )
payload + = encodeVarint ( fromStreamNumber )
payload + = ' \x00 \x00 \x00 \x01 ' # Bitfield of features and behaviors that can be expected from me. (See https://bitmessage.org/wiki/Protocol_specification#Pubkey_bitfield_features )
# 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 ' , (
2013-06-24 21:51:01 +02:00
ackdata , tr . translateText ( " MainWindow " , " Error! Could not find sender address (your address) in the keys.dat file. " ) ) ) )
2013-06-21 23:32:22 +02:00
continue
privSigningKeyHex = shared . decodeWalletImportFormat (
privSigningKeyBase58 ) . encode ( ' hex ' )
privEncryptionKeyHex = shared . decodeWalletImportFormat (
privEncryptionKeyBase58 ) . encode ( ' hex ' )
pubSigningKey = highlevelcrypto . privToPub (
privSigningKeyHex ) . decode ( ' hex ' )
pubEncryptionKey = highlevelcrypto . privToPub (
privEncryptionKeyHex ) . decode ( ' hex ' )
payload + = pubSigningKey [
1 : ] # The \x04 on the beginning of the public keys are not sent. This way there is only one acceptable way to encode and send a public key.
payload + = pubEncryptionKey [ 1 : ]
payload + = toHash # This hash will be checked by the receiver of the message to verify that toHash belongs to them. This prevents a Surreptitious Forwarding Attack.
payload + = ' \x02 ' # Type 2 is simple UTF-8 message encoding as specified on the Protocol Specification on the Bitmessage Wiki.
messageToTransmit = ' Subject: ' + \
subject + ' \n ' + ' Body: ' + message
payload + = encodeVarint ( len ( messageToTransmit ) )
payload + = messageToTransmit
fullAckPayload = self . generateFullAckMessage (
2013-12-06 07:52:19 +01:00
ackdata , toStreamNumber ) # The fullAckPayload is a normal msg protocol message with the proof of work already completed that the receiver of this message can easily send out.
2013-06-21 23:32:22 +02:00
payload + = encodeVarint ( len ( fullAckPayload ) )
payload + = fullAckPayload
signature = highlevelcrypto . sign ( payload , privSigningKeyHex )
payload + = encodeVarint ( len ( signature ) )
payload + = signature
2013-09-15 03:06:26 +02:00
if fromAddressVersionNumber > = 3 :
2013-06-21 23:32:22 +02:00
payload = ' \x01 ' # Message version.
payload + = encodeVarint ( fromAddressVersionNumber )
payload + = encodeVarint ( fromStreamNumber )
payload + = ' \x00 \x00 \x00 \x01 ' # Bitfield of features and behaviors that can be expected from me. (See https://bitmessage.org/wiki/Protocol_specification#Pubkey_bitfield_features )
# 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 ' , (
2013-06-24 21:51:01 +02:00
ackdata , tr . translateText ( " MainWindow " , " Error! Could not find sender address (your address) in the keys.dat file. " ) ) ) )
2013-06-21 23:32:22 +02:00
continue
privSigningKeyHex = shared . decodeWalletImportFormat (
privSigningKeyBase58 ) . encode ( ' hex ' )
privEncryptionKeyHex = shared . decodeWalletImportFormat (
privEncryptionKeyBase58 ) . encode ( ' hex ' )
pubSigningKey = highlevelcrypto . privToPub (
privSigningKeyHex ) . decode ( ' hex ' )
pubEncryptionKey = highlevelcrypto . privToPub (
privEncryptionKeyHex ) . decode ( ' hex ' )
payload + = pubSigningKey [
1 : ] # The \x04 on the beginning of the public keys are not sent. This way there is only one acceptable way to encode and send a public key.
payload + = pubEncryptionKey [ 1 : ]
# If the receiver of our message is in our address book,
# subscriptions list, or whitelist then we will allow them to
# do the network-minimum proof of work. Let us check to see if
# the receiver is in any of those lists.
if shared . isAddressInMyAddressBookSubscriptionsListOrWhitelist ( toaddress ) :
payload + = encodeVarint (
shared . networkDefaultProofOfWorkNonceTrialsPerByte )
payload + = encodeVarint (
shared . networkDefaultPayloadLengthExtraBytes )
else :
payload + = encodeVarint ( shared . config . getint (
fromaddress , ' noncetrialsperbyte ' ) )
payload + = encodeVarint ( shared . config . getint (
fromaddress , ' payloadlengthextrabytes ' ) )
payload + = toHash # This hash will be checked by the receiver of the message to verify that toHash belongs to them. This prevents a Surreptitious Forwarding Attack.
payload + = ' \x02 ' # Type 2 is simple UTF-8 message encoding as specified on the Protocol Specification on the Bitmessage Wiki.
messageToTransmit = ' Subject: ' + \
subject + ' \n ' + ' Body: ' + message
payload + = encodeVarint ( len ( messageToTransmit ) )
payload + = messageToTransmit
2013-09-30 05:01:56 +02:00
if shared . config . has_section ( toaddress ) :
2013-07-22 07:10:22 +02:00
with shared . printLock :
2013-09-30 05:01:56 +02:00
print ' Not bothering to include ackdata because we are sending to ourselves or a chan. '
2013-07-22 07:10:22 +02:00
fullAckPayload = ' '
2013-08-08 21:37:48 +02:00
elif not shared . isBitSetWithinBitfield ( behaviorBitfield , 31 ) :
with shared . printLock :
2013-09-30 01:24:27 +02:00
print ' Not bothering to include ackdata because the receiver said that they won \' t relay it anyway. '
2013-08-08 21:37:48 +02:00
fullAckPayload = ' '
2013-07-22 07:10:22 +02:00
else :
fullAckPayload = self . generateFullAckMessage (
2013-12-06 07:52:19 +01:00
ackdata , toStreamNumber ) # The fullAckPayload is a normal msg protocol message with the proof of work already completed that the receiver of this message can easily send out.
2013-06-21 23:32:22 +02:00
payload + = encodeVarint ( len ( fullAckPayload ) )
payload + = fullAckPayload
signature = highlevelcrypto . sign ( payload , privSigningKeyHex )
payload + = encodeVarint ( len ( signature ) )
payload + = signature
2013-09-18 06:04:01 +02:00
print ' using pubEncryptionKey: ' , pubEncryptionKeyBase256 . encode ( ' hex ' )
2013-06-21 23:32:22 +02:00
# We have assembled the data that will be encrypted.
try :
encrypted = highlevelcrypto . encrypt ( payload , " 04 " + pubEncryptionKeyBase256 . encode ( ' hex ' ) )
except :
2013-08-29 13:27:30 +02:00
sqlExecute ( ''' UPDATE sent SET status= ' badkey ' WHERE ackdata=? ''' , ackdata )
2013-06-24 21:51:01 +02:00
shared . UISignalQueue . put ( ( ' updateSentItemStatusByAckdata ' , ( ackdata , tr . translateText ( " MainWindow " , ' Problem: The recipient \' s encryption key is no good. Could not encrypt message. % 1 ' ) . arg ( unicode ( strftime ( shared . config . get ( ' bitmessagesettings ' , ' timeformat ' ) , localtime ( int ( time . time ( ) ) ) ) , ' utf-8 ' ) ) ) ) )
2013-06-21 23:32:22 +02:00
continue
encryptedPayload = embeddedTime + encodeVarint ( toStreamNumber ) + encrypted
target = 2 * * 64 / ( ( len ( encryptedPayload ) + requiredPayloadLengthExtraBytes + 8 ) * requiredAverageProofOfWorkNonceTrialsPerByte )
2013-06-29 19:29:35 +02:00
with shared . printLock :
print ' (For msg message) Doing proof of work. Total required difficulty: ' , float ( requiredAverageProofOfWorkNonceTrialsPerByte ) / shared . networkDefaultProofOfWorkNonceTrialsPerByte , ' Required small message difficulty: ' , float ( requiredPayloadLengthExtraBytes ) / shared . networkDefaultPayloadLengthExtraBytes
2013-06-21 23:32:22 +02:00
powStartTime = time . time ( )
initialHash = hashlib . sha512 ( encryptedPayload ) . digest ( )
trialValue , nonce = proofofwork . run ( target , initialHash )
2013-06-29 19:29:35 +02:00
with shared . printLock :
print ' (For msg message) Found proof of work ' , trialValue , ' Nonce: ' , nonce
try :
print ' POW took ' , int ( time . time ( ) - powStartTime ) , ' seconds. ' , nonce / ( time . time ( ) - powStartTime ) , ' nonce trials per second. '
except :
pass
2013-06-21 23:32:22 +02:00
encryptedPayload = pack ( ' >Q ' , nonce ) + encryptedPayload
inventoryHash = calculateInventoryHash ( encryptedPayload )
objectType = ' msg '
shared . inventory [ inventoryHash ] = (
2013-09-13 06:27:34 +02:00
objectType , toStreamNumber , encryptedPayload , int ( time . time ( ) ) , ' ' )
2013-09-07 00:55:12 +02:00
shared . inventorySets [ toStreamNumber ] . add ( inventoryHash )
2013-09-30 01:24:27 +02:00
if shared . config . has_section ( toaddress ) :
2013-07-22 07:10:22 +02:00
shared . UISignalQueue . put ( ( ' updateSentItemStatusByAckdata ' , ( ackdata , tr . translateText ( " MainWindow " , " Message sent. Sent on % 1 " ) . arg ( unicode (
strftime ( shared . config . get ( ' bitmessagesettings ' , ' timeformat ' ) , localtime ( int ( time . time ( ) ) ) ) , ' utf-8 ' ) ) ) ) )
else :
2013-09-30 01:24:27 +02:00
# not sending to a chan or one of my addresses
2013-07-22 07:10:22 +02:00
shared . UISignalQueue . put ( ( ' updateSentItemStatusByAckdata ' , ( ackdata , tr . translateText ( " MainWindow " , " Message sent. Waiting on acknowledgement. Sent on % 1 " ) . arg ( unicode (
strftime ( shared . config . get ( ' bitmessagesettings ' , ' timeformat ' ) , localtime ( int ( time . time ( ) ) ) ) , ' utf-8 ' ) ) ) ) )
2013-06-21 23:32:22 +02:00
print ' Broadcasting inv for my msg(within sendmsg function): ' , inventoryHash . encode ( ' hex ' )
shared . broadcastToSendDataQueues ( (
2013-09-30 05:01:56 +02:00
toStreamNumber , ' advertiseobject ' , inventoryHash ) )
2013-06-21 23:32:22 +02:00
# Update the status of the message in the 'sent' table to have a
2013-07-22 07:10:22 +02:00
# 'msgsent' status or 'msgsentnoackexpected' status.
2013-10-02 04:44:00 +02:00
if shared . config . has_section ( toaddress ) :
2013-07-22 07:10:22 +02:00
newStatus = ' msgsentnoackexpected '
else :
newStatus = ' msgsent '
2013-08-29 13:27:30 +02:00
sqlExecute ( ''' UPDATE sent SET msgid=?, status=? WHERE ackdata=? ''' ,
inventoryHash , newStatus , ackdata )
2013-06-21 23:32:22 +02:00
2013-09-30 05:01:56 +02:00
# If we are sending to ourselves or a chan, let's put the message in
# our own inbox.
if shared . config . has_section ( toaddress ) :
t = ( inventoryHash , toaddress , fromaddress , subject , int (
time . time ( ) ) , message , ' inbox ' , 2 , 0 )
helper_inbox . insert ( t )
shared . UISignalQueue . put ( ( ' displayNewInboxMessage ' , (
inventoryHash , toaddress , fromaddress , subject , message ) ) )
# 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 ' ) :
try :
apiNotifyPath = shared . config . get (
' bitmessagesettings ' , ' apinotifypath ' )
except :
apiNotifyPath = ' '
if apiNotifyPath != ' ' :
call ( [ apiNotifyPath , " newMessage " ] )
2013-06-21 23:32:22 +02:00
def requestPubKey ( self , toAddress ) :
toStatus , addressVersionNumber , streamNumber , ripe = decodeAddress (
toAddress )
if toStatus != ' success ' :
2013-06-29 19:29:35 +02:00
with shared . printLock :
sys . stderr . write ( ' Very abnormal error occurred in requestPubKey. toAddress is: ' + repr (
toAddress ) + ' . Please report this error to Atheros. ' )
2013-06-21 23:32:22 +02:00
return
2013-09-13 06:27:34 +02:00
if addressVersionNumber < = 3 :
shared . neededPubkeys [ ripe ] = 0
elif addressVersionNumber > = 4 :
privEncryptionKey = hashlib . sha512 ( hashlib . sha512 ( encodeVarint ( addressVersionNumber ) + encodeVarint ( streamNumber ) + ripe ) . digest ( ) ) . digest ( ) [ : 32 ] # Note that this is the first half of the sha512 hash.
2013-09-15 03:06:26 +02:00
tag = hashlib . sha512 ( hashlib . sha512 ( encodeVarint ( addressVersionNumber ) + encodeVarint ( streamNumber ) + ripe ) . digest ( ) ) . digest ( ) [ 32 : ] # Note that this is the second half of the sha512 hash.
shared . neededPubkeys [ tag ] = highlevelcrypto . makeCryptor ( privEncryptionKey . encode ( ' hex ' ) ) # 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
payload = pack ( ' >Q ' , ( int ( time . time ( ) ) + random . randrange (
- 300 , 300 ) ) ) # the current time plus or minus five minutes.
payload + = encodeVarint ( addressVersionNumber )
payload + = encodeVarint ( streamNumber )
2013-09-13 06:27:34 +02:00
if addressVersionNumber < = 3 :
payload + = ripe
with shared . printLock :
print ' making request for pubkey with ripe: ' , ripe . encode ( ' hex ' )
else :
2013-09-15 03:06:26 +02:00
payload + = tag
2013-09-13 06:27:34 +02:00
with shared . printLock :
2013-09-15 03:06:26 +02:00
print ' making request for v4 pubkey with tag: ' , tag . encode ( ' hex ' )
2013-06-29 19:29:35 +02:00
2013-06-21 23:32:22 +02:00
# print 'trial value', trialValue
statusbar = ' Doing the computations necessary to request the recipient \' s public key. '
shared . UISignalQueue . put ( ( ' updateStatusBar ' , statusbar ) )
shared . UISignalQueue . put ( ( ' updateSentItemStatusByHash ' , (
2013-06-24 21:51:01 +02:00
ripe , tr . translateText ( " MainWindow " , ' Doing work necessary to request encryption key. ' ) ) ) )
2013-06-21 23:32:22 +02:00
target = 2 * * 64 / ( ( len ( payload ) + shared . networkDefaultPayloadLengthExtraBytes +
8 ) * shared . networkDefaultProofOfWorkNonceTrialsPerByte )
initialHash = hashlib . sha512 ( payload ) . digest ( )
trialValue , nonce = proofofwork . run ( target , initialHash )
2013-06-29 19:29:35 +02:00
with shared . printLock :
print ' Found proof of work ' , trialValue , ' Nonce: ' , nonce
2013-06-21 23:32:22 +02:00
payload = pack ( ' >Q ' , nonce ) + payload
inventoryHash = calculateInventoryHash ( payload )
objectType = ' getpubkey '
shared . inventory [ inventoryHash ] = (
2013-09-13 06:27:34 +02:00
objectType , streamNumber , payload , int ( time . time ( ) ) , ' ' )
2013-09-07 00:55:12 +02:00
shared . inventorySets [ streamNumber ] . add ( inventoryHash )
2013-06-21 23:32:22 +02:00
print ' sending inv (for the getpubkey message) '
shared . broadcastToSendDataQueues ( (
2013-09-07 00:55:12 +02:00
streamNumber , ' advertiseobject ' , inventoryHash ) )
2013-06-21 23:32:22 +02:00
2013-08-29 13:27:30 +02:00
sqlExecute (
''' UPDATE sent SET status= ' awaitingpubkey ' WHERE toaddress=? AND status= ' doingpubkeypow ' ''' ,
toAddress )
2013-06-21 23:32:22 +02:00
shared . UISignalQueue . put ( (
2013-06-24 21:51:01 +02:00
' updateStatusBar ' , tr . translateText ( " MainWindow " , ' Broacasting the public key request. This program will auto-retry if they are offline. ' ) ) )
shared . UISignalQueue . put ( ( ' updateSentItemStatusByHash ' , ( ripe , tr . translateText ( " MainWindow " , ' Sending public key request. Waiting for reply. Requested at % 1 ' ) . arg ( unicode (
2013-06-21 23:32:22 +02:00
strftime ( shared . config . get ( ' bitmessagesettings ' , ' timeformat ' ) , localtime ( int ( time . time ( ) ) ) ) , ' utf-8 ' ) ) ) ) )
2013-12-06 07:52:19 +01:00
def generateFullAckMessage ( self , ackdata , toStreamNumber ) :
embeddedTime = pack ( ' >Q ' , ( int ( time . time ( ) ) + random . randrange (
- 300 , 300 ) ) ) # the current time plus or minus five minutes.
2013-06-21 23:32:22 +02:00
payload = embeddedTime + encodeVarint ( toStreamNumber ) + ackdata
target = 2 * * 64 / ( ( len ( payload ) + shared . networkDefaultPayloadLengthExtraBytes +
8 ) * shared . networkDefaultProofOfWorkNonceTrialsPerByte )
2013-06-29 19:29:35 +02:00
with shared . printLock :
print ' (For ack message) Doing proof of work... '
2013-06-21 23:32:22 +02:00
powStartTime = time . time ( )
initialHash = hashlib . sha512 ( payload ) . digest ( )
trialValue , nonce = proofofwork . run ( target , initialHash )
2013-06-29 19:29:35 +02:00
with shared . printLock :
print ' (For ack message) Found proof of work ' , trialValue , ' Nonce: ' , nonce
try :
print ' POW took ' , int ( time . time ( ) - powStartTime ) , ' seconds. ' , nonce / ( time . time ( ) - powStartTime ) , ' nonce trials per second. '
except :
pass
2013-06-21 23:32:22 +02:00
payload = pack ( ' >Q ' , nonce ) + payload
headerData = ' \xe9 \xbe \xb4 \xd9 ' # magic bits, slighly different from Bitcoin's magic bits.
headerData + = ' msg \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 '
headerData + = pack ( ' >L ' , len ( payload ) )
headerData + = hashlib . sha512 ( payload ) . digest ( ) [ : 4 ]
return headerData + payload