Compare commits

...

38 Commits

Author SHA1 Message Date
Biryuzovye Kleshni 7ad1725235 Applying solution I don't understand 2018-08-02 15:00:23 +00:00
Biryuzovye Kleshni 192b083d58 Readability 2018-07-30 03:10:48 +00:00
Biryuzovye Kleshni 0aaad8b896 Fixed big-endian decoding 2018-07-30 02:58:39 +00:00
Biryuzovye Kleshni 8e1259d06c More old code deleted 2018-07-29 12:47:44 +00:00
Biryuzovye Kleshni a398354700 Deleted old module 2018-07-29 12:06:55 +00:00
Biryuzovye Kleshni c22c5c2208 Removed broken API methods 2018-07-29 11:46:02 +00:00
Biryuzovye Kleshni 02ece2d491 Added more API for raw objects 2018-07-29 11:06:41 +00:00
Biryuzovye Kleshni 00c4ee881f Added API for raw objects 2018-07-29 09:44:15 +00:00
Biryuzovye Kleshni 373157db45 Added POW settings to settings window 2018-07-29 04:38:05 +00:00
Biryuzovye Kleshni 2c7d677ccf Added estimated POW time tooltip 2018-07-23 08:01:22 +00:00
Biryuzovye Kleshni a27b5e9055
Merge branch 'v0.6' into POW 2018-07-23 07:21:32 +00:00
Biryuzovye Kleshni f6415d6be1 Added "Edit and resend" option 2018-07-23 07:15:36 +00:00
Biryuzovye Kleshni 174fb38c7d Forgotten litter 2018-07-23 06:38:58 +00:00
Biryuzovye Kleshni ce4fe3a66b Added sending cancellation 2018-07-23 06:23:15 +00:00
Biryuzovye Kleshni c66156aeea Added POW speed indicator 2018-07-22 08:51:23 +00:00
Biryuzovye Kleshni b2441c7830 Deleted generated code for main window 2018-07-22 07:46:09 +00:00
Biryuzovye Kleshni d1a5c604e6 Deleted generated code for settings window 2018-07-20 00:02:17 +00:00
Biryuzovye Kleshni e94bdf4642 Fix "maxcores" setting 2018-07-15 20:03:29 +00:00
Biryuzovye Kleshni b24edfdc22 Merge branch 'v0.6' into POW 2018-07-15 19:24:13 +00:00
Biryuzovye Kleshni 8e0bff7d52 Connect WorkProver to SingleWorker 2018-07-15 18:37:23 +00:00
Biryuzovye Kleshni 3c0e23574d Signal handling 2018-06-30 09:55:53 +00:00
Biryuzovye Kleshni 54291918bf Fix for running after installation 2018-06-30 09:21:39 +00:00
Biryuzovye Kleshni 0c3ce79ea4 Wrap "bitmessagemain.py" for use with "multiprocessing" 2018-06-30 08:57:31 +00:00
Biryuzovye Kleshni 6fb637d1df
Merge branch 'v0.6' into POW 2018-06-28 14:20:56 +00:00
Biryuzovye Kleshni c28a4f6174 Don't load heavy modules until needed 2018-06-28 14:19:50 +00:00
Biryuzovye Kleshni 90ae95d9cb Added WorkProver to executable builders 2018-06-28 09:47:36 +00:00
Biryuzovye Kleshni 799933086a Downgrade OpenSSL version 2018-06-27 11:13:49 +00:00
Biryuzovye Kleshni 4c0b0c708f
Merge branch 'v0.6' into POW 2018-06-27 10:16:00 +00:00
Biryuzovye Kleshni cac0237ae0 OS X compatibility 2018-06-25 07:51:39 +00:00
Biryuzovye Kleshni 4f9274a5cc More build system integration for WorkProver 2018-06-25 07:50:32 +00:00
Biryuzovye Kleshni 721ad8c61a Add "workprover" to packages list 2018-06-23 15:54:32 +00:00
Biryuzovye Kleshni 30e7df6094 Skip OpenCL test if unavailable 2018-06-23 15:53:06 +00:00
Biryuzovye Kleshni 43f7e1e7a1 Improved unit tests loading 2018-06-23 13:19:59 +00:00
Biryuzovye Kleshni 2eeb58a43e Fixed indentation 2018-06-23 12:33:36 +00:00
Biryuzovye Kleshni 917b55d463 Sorted imports 2018-06-23 11:00:24 +00:00
Biryuzovye Kleshni 46da7e81bb Fixed indentation 2018-06-23 10:42:01 +00:00
Biryuzovye Kleshni 079326bc03 More precise exceptions 2018-06-23 10:32:05 +00:00
Biryuzovye Kleshni a181da3632 New POW calculation module 2018-06-23 08:57:34 +00:00
55 changed files with 5223 additions and 6491 deletions

View File

@ -11,3 +11,4 @@ install:
script:
- python checkdeps.py
- pybitmessage -t
- python setup.py test

View File

@ -88,25 +88,7 @@ def testCompiler():
# silent, we can't test without setuptools
return True
bitmsghash = Extension(
'bitmsghash',
sources=['src/bitmsghash/bitmsghash.cpp'],
libraries=['pthread', 'crypto'],
)
dist = Distribution()
dist.ext_modules = [bitmsghash]
cmd = build_ext(dist)
cmd.initialize_options()
cmd.finalize_options()
cmd.force = True
try:
cmd.run()
except CompileError:
return False
else:
fullPath = os.path.join(cmd.build_lib, cmd.get_ext_filename("bitmsghash"))
return os.path.isfile(fullPath)
return True
prereqs = detectPrereqs()

View File

@ -52,8 +52,8 @@ else:
arch=64
a.binaries += [('libeay32.dll', openSSLPath + 'libeay32.dll', 'BINARY'),
(os.path.join('bitmsghash', 'bitmsghash%i.dll' % (arch)), os.path.join(srcPath, 'bitmsghash', 'bitmsghash%i.dll' % (arch)), 'BINARY'),
(os.path.join('bitmsghash', 'bitmsghash.cl'), os.path.join(srcPath, 'bitmsghash', 'bitmsghash.cl'), 'BINARY'),
("workprover/fastsolver/libfastsolver-{}.dll".format(arch), os.path.join(srcPath, "workprover/fastsolver/libfastsolver-{}.dll".format(arch)), "BINARY"),
("workprover/gpusolver.cl", os.path.join(srcPath, "workprover/gpusolver.cl"), "BINARY"),
(os.path.join('sslkeys', 'cert.pem'), os.path.join(srcPath, 'sslkeys', 'cert.pem'), 'BINARY'),
(os.path.join('sslkeys', 'key.pem'), os.path.join(srcPath, 'sslkeys', 'key.pem'), 'BINARY')
]

View File

@ -12,7 +12,7 @@ from src.version import softwareVersion
EXTRAS_REQUIRE = {
'gir': ['pygobject'],
'notify2': ['notify2'],
'pyopencl': ['pyopencl'],
'pyopencl': ['pyopencl', 'numpy'],
'prctl': ['python_prctl'], # Named threads
'qrcode': ['qrcode'],
'sound;platform_system=="Windows"': ['winsound'],
@ -50,12 +50,6 @@ if __name__ == "__main__":
with open(os.path.join(here, 'README.md')) as f:
README = f.read()
bitmsghash = Extension(
'pybitmessage.bitmsghash.bitmsghash',
sources=['src/bitmsghash/bitmsghash.cpp'],
libraries=['pthread', 'crypto'],
)
installRequires = []
packages = [
'pybitmessage',
@ -66,7 +60,8 @@ if __name__ == "__main__":
'pybitmessage.pyelliptic',
'pybitmessage.socks',
'pybitmessage.storage',
'pybitmessage.plugins'
'pybitmessage.plugins',
'pybitmessage.workprover'
]
# this will silently accept alternative providers of msgpack
@ -108,8 +103,9 @@ if __name__ == "__main__":
package_dir={'pybitmessage': 'src'},
packages=packages,
package_data={'': [
'bitmessageqt/*.ui', 'bitmsghash/*.cl', 'sslkeys/*.pem',
'translations/*.ts', 'translations/*.qm',
'bitmessageqt/*.ui', 'translations/*.ts', 'translations/*.qm',
'workprover/*.cl', 'workprover/fastsolver/*',
'sslkeys/*.pem',
'images/*.png', 'images/*.ico', 'images/*.icns'
]},
data_files=[
@ -120,7 +116,6 @@ if __name__ == "__main__":
('share/icons/hicolor/24x24/apps/',
['desktop/icons/24x24/pybitmessage.png'])
],
ext_modules=[bitmsghash],
zip_safe=False,
entry_points={
'bitmessage.gui.menu': [

View File

@ -10,10 +10,15 @@ This is not what you run to run the Bitmessage API. Instead, enable the API
import base64
import hashlib
import json
import socket
import threading
import time
from binascii import hexlify, unhexlify
from random import randint
from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler, SimpleXMLRPCServer
from struct import pack
import errno
import Queue
import shared
from addresses import (
@ -23,11 +28,14 @@ from bmconfigparser import BMConfigParser
import defaults
import helper_inbox
import helper_sent
import helper_threading
import helper_random
import state
import queues
import shutdown
import network.stats
import protocol
# Classes
from helper_sql import sqlQuery, sqlExecute, SqlBulkExecute, sqlStoredProcedure
@ -36,11 +44,9 @@ from debug import logger
from inventory import Inventory
from version import softwareVersion
# Helper Functions
import proofofwork
str_chan = '[chan]'
queuedRawObjects = {}
class APIError(Exception):
def __init__(self, error_number, error_message):
@ -1031,45 +1037,6 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
})
return json.dumps(data, indent=4, separators=(',', ': '))
def HandleDisseminatePreEncryptedMsg(self, params):
# The device issuing this command to PyBitmessage supplies a msg
# object that has already been encrypted but which still needs the POW
# to be done. PyBitmessage accepts this msg object and sends it out
# to the rest of the Bitmessage network as if it had generated
# the message itself. Please do not yet add this to the api doc.
if len(params) != 3:
raise APIError(0, 'I need 3 parameter!')
encryptedPayload, requiredAverageProofOfWorkNonceTrialsPerByte, \
requiredPayloadLengthExtraBytes = params
encryptedPayload = self._decode(encryptedPayload, "hex")
# Let us do the POW and attach it to the front
target = 2**64 / (
(len(encryptedPayload) + requiredPayloadLengthExtraBytes + 8)
* requiredAverageProofOfWorkNonceTrialsPerByte)
with shared.printLock:
print '(For msg message via API) Doing proof of work. Total required difficulty:', float(requiredAverageProofOfWorkNonceTrialsPerByte) / defaults.networkDefaultProofOfWorkNonceTrialsPerByte, 'Required small message difficulty:', float(requiredPayloadLengthExtraBytes) / defaults.networkDefaultPayloadLengthExtraBytes
powStartTime = time.time()
initialHash = hashlib.sha512(encryptedPayload).digest()
trialValue, nonce = proofofwork.run(target, initialHash)
with shared.printLock:
print '(For msg message via API) 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
encryptedPayload = pack('>Q', nonce) + encryptedPayload
toStreamNumber = decodeVarint(encryptedPayload[16:26])[0]
inventoryHash = calculateInventoryHash(encryptedPayload)
objectType = 2
TTL = 2.5 * 24 * 60 * 60
Inventory()[inventoryHash] = (
objectType, toStreamNumber, encryptedPayload,
int(time.time()) + TTL, ''
)
with shared.printLock:
print 'Broadcasting inv for msg(API disseminatePreEncryptedMsg command):', hexlify(inventoryHash)
queues.invQueue.put((toStreamNumber, inventoryHash))
def HandleTrashSentMessageByAckDAta(self, params):
# This API method should only be used when msgid is not available
if len(params) == 0:
@ -1078,88 +1045,132 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
sqlExecute("UPDATE sent SET folder='trash' WHERE ackdata=?", ackdata)
return 'Trashed sent message (assuming message existed).'
def HandleDissimatePubKey(self, params):
# The device issuing this command to PyBitmessage supplies a pubkey
# object to be disseminated to the rest of the Bitmessage network.
# PyBitmessage accepts this pubkey object and sends it out to the rest
# of the Bitmessage network as if it had generated the pubkey object
# itself. Please do not yet add this to the api doc.
if len(params) != 1:
raise APIError(0, 'I need 1 parameter!')
payload, = params
payload = self._decode(payload, "hex")
def HandleDisseminateRawObject(self, arguments):
if len(arguments) != 1:
raise APIError(0, "1 argument needed")
# Let us do the POW
target = 2 ** 64 / (
(len(payload) + defaults.networkDefaultPayloadLengthExtraBytes
+ 8) * defaults.networkDefaultProofOfWorkNonceTrialsPerByte)
print '(For pubkey message via API) Doing proof of work...'
initialHash = hashlib.sha512(payload).digest()
trialValue, nonce = proofofwork.run(target, initialHash)
print '(For pubkey message via API) Found proof of work', trialValue, 'Nonce:', nonce
payload = pack('>Q', nonce) + payload
payload = self._decode(arguments[0], "hex")
pubkeyReadPosition = 8 # bypass the nonce
if payload[pubkeyReadPosition:pubkeyReadPosition+4] == \
'\x00\x00\x00\x00': # if this pubkey uses 8 byte time
pubkeyReadPosition += 8
inventoryHash = protocol.checkAndShareObjectWithPeers(payload)
if inventoryHash is None:
raise APIError(30, "Invalid object or insufficient POW")
else:
pubkeyReadPosition += 4
addressVersion, addressVersionLength = decodeVarint(
payload[pubkeyReadPosition:pubkeyReadPosition+10])
pubkeyReadPosition += addressVersionLength
pubkeyStreamNumber = decodeVarint(
payload[pubkeyReadPosition:pubkeyReadPosition+10])[0]
inventoryHash = calculateInventoryHash(payload)
objectType = 1 # TODO: support v4 pubkeys
TTL = 28 * 24 * 60 * 60
Inventory()[inventoryHash] = (
objectType, pubkeyStreamNumber, payload, int(time.time()) + TTL, ''
)
with shared.printLock:
print 'broadcasting inv within API command disseminatePubkey with hash:', hexlify(inventoryHash)
queues.invQueue.put((pubkeyStreamNumber, inventoryHash))
return hexlify(inventoryHash)
def HandleGetMessageDataByDestinationHash(self, params):
# Method will eventually be used by a particular Android app to
# select relevant messages. Do not yet add this to the api
# doc.
if len(params) != 1:
raise APIError(0, 'I need 1 parameter!')
requestedHash, = params
if len(requestedHash) != 32:
raise APIError(
19, 'The length of hash should be 32 bytes (encoded in hex'
' thus 64 characters).')
requestedHash = self._decode(requestedHash, "hex")
def HandleGetRawObject(self, arguments):
if len(arguments) != 1:
raise APIError(0, "1 argument needed")
# This is not a particularly commonly used API function. Before we
# use it we'll need to fill out a field in our inventory database
# which is blank by default (first20bytesofencryptedmessage).
queryreturn = sqlQuery(
"SELECT hash, payload FROM inventory WHERE tag = ''"
" and objecttype = 2")
with SqlBulkExecute() as sql:
for row in queryreturn:
hash01, payload = row
readPosition = 16 # Nonce length + time length
# Stream Number length
readPosition += decodeVarint(
payload[readPosition:readPosition+10])[1]
t = (payload[readPosition:readPosition+32], hash01)
sql.execute("UPDATE inventory SET tag=? WHERE hash=?", *t)
inventoryHash, = arguments
queryreturn = sqlQuery(
"SELECT payload FROM inventory WHERE tag = ?", requestedHash)
data = '{"receivedMessageDatas":['
for row in queryreturn:
payload, = row
if len(data) > 25:
data += ','
data += json.dumps(
{'data': hexlify(payload)}, indent=4, separators=(',', ': '))
data += ']}'
return data
if len(inventoryHash) != 64:
raise APIError(19, "The length of hash should be 32 bytes (encoded in hex thus 64 characters)")
inventoryHash = self._decode(inventoryHash, "hex")
try:
inventoryItem = Inventory()[inventoryHash]
except KeyError:
raise APIError(31, "Object not found")
return json.dumps({
"hash": hexlify(inventoryHash),
"expiryTime": inventoryItem.expires,
"objectType": inventoryItem.type,
"stream": inventoryItem.stream,
"tag": hexlify(inventoryItem.tag),
"payload": hexlify(inventoryItem.payload)
}, indent = 4, separators = (",", ": "))
def HandleListRawObjects(self, arguments):
if len(arguments) != 3:
raise APIError(0, "3 arguments needed")
objectType, stream, tag = arguments
if tag is not None:
tag = buffer(self._decode(tag, "hex"))
result = []
inventory = Inventory()
for i in inventory:
inventoryItem = inventory[str(i)]
if objectType is not None and inventoryItem.type != objectType:
continue
if stream is not None and inventoryItem.stream != stream:
continue
if tag is not None and inventoryItem.tag != tag:
continue
result.append(hexlify(i))
return json.dumps(result, indent = 4, separators = (",", ": "))
def HandleQueueRawObject(self, arguments):
if len(arguments) != 2:
raise APIError(0, "2 arguments needed")
TTL, headlessPayload = arguments
headlessPayload = self._decode(headlessPayload, "hex")
if type(TTL) is not int or TTL < 300 or TTL > 28 * 24 * 60 * 60:
raise APIError(33, "TTL must be an integer between 300 and 28 * 24 * 60 * 60 seconds")
ID = helper_random.randomBytes(32)
queues.workerQueue.put(("sendRawObject", ID, TTL, headlessPayload))
queuedRawObjects[ID] = "queued",
return hexlify(ID)
def HandleCancelQueuedRawObject(self, arguments):
if len(arguments) != 1:
raise APIError(0, "1 argument needed")
ID, = arguments
if len(ID) != 64:
raise APIError(19, "The length of queue item ID should be 32 bytes (encoded in hex thus 64 characters)")
ID = self._decode(ID, 'hex')
queues.workerQueue.put(("cancelRawObject", ID))
def HandleCheckQueuedRawObject(self, arguments):
if len(arguments) != 1:
raise APIError(0, "1 argument needed")
ID, = arguments
if len(ID) != 64:
raise APIError(19, "The length of queue item ID should be 32 bytes (encoded in hex thus 64 characters)")
ID = self._decode(ID, 'hex')
while True:
try:
queueItem = queues.processedRawObjectsQueue.get_nowait()
command, randomID, arguments = queueItem[0], queueItem[1], queueItem[2: ]
if command == "sent":
queuedRawObjects[randomID] = command, hexlify(arguments[0])
else:
queuedRawObjects[randomID] = (command, ) + arguments
except Queue.Empty:
break
status = queuedRawObjects.get(ID, ("notfound", ))
if status[0] in ["failed", "sent", "canceled"]:
del queuedRawObjects[ID]
return status
def HandleClientStatus(self, params):
if len(network.stats.connectedHostsList()) == 0:
@ -1262,12 +1273,12 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
handlers['addSubscription'] = HandleAddSubscription
handlers['deleteSubscription'] = HandleDeleteSubscription
handlers['listSubscriptions'] = ListSubscriptions
handlers['disseminatePreEncryptedMsg'] = HandleDisseminatePreEncryptedMsg
handlers['disseminatePubkey'] = HandleDissimatePubKey
handlers['getMessageDataByDestinationHash'] = \
HandleGetMessageDataByDestinationHash
handlers['getMessageDataByDestinationTag'] = \
HandleGetMessageDataByDestinationHash
handlers["disseminateRawObject"] = HandleDisseminateRawObject
handlers["getRawObject"] = HandleGetRawObject
handlers["listRawObjects"] = HandleListRawObjects
handlers["queueRawObject"] = HandleQueueRawObject
handlers["cancelQueuedRawObject"] = HandleCancelQueuedRawObject
handlers["checkQueuedRawObject"] = HandleCheckQueuedRawObject
handlers['clientStatus'] = HandleClientStatus
handlers['decodeAddress'] = HandleDecodeAddress
handlers['deleteAndVacuum'] = HandleDeleteAndVacuum
@ -1297,3 +1308,49 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
logger.exception(e)
return "API Error 0021: Unexpected API Failure - %s" % e
# This thread, of which there is only one, runs the API.
class singleAPI(threading.Thread, helper_threading.StoppableThread):
def __init__(self):
threading.Thread.__init__(self, name="singleAPI")
self.initStop()
def stopThread(self):
super(singleAPI, self).stopThread()
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.connect((
BMConfigParser().get('bitmessagesettings', 'apiinterface'),
BMConfigParser().getint('bitmessagesettings', 'apiport')
))
s.shutdown(socket.SHUT_RDWR)
s.close()
except:
pass
def run(self):
port = BMConfigParser().getint('bitmessagesettings', 'apiport')
try:
from errno import WSAEADDRINUSE
except (ImportError, AttributeError):
errno.WSAEADDRINUSE = errno.EADDRINUSE
for attempt in range(50):
try:
if attempt > 0:
port = randint(32767, 65535)
se = StoppableXMLRPCServer(
(BMConfigParser().get(
'bitmessagesettings', 'apiinterface'),
port),
MySimpleXMLRPCRequestHandler, True, True)
except socket.error as e:
if e.errno in (errno.EADDRINUSE, errno.WSAEADDRINUSE):
continue
else:
if attempt > 0:
BMConfigParser().set(
"bitmessagesettings", "apiport", str(port))
BMConfigParser().save()
break
se.register_introspection_functions()
se.serve_forever()

View File

@ -918,7 +918,7 @@ def loadSent():
# Set status string
if status == "awaitingpubkey":
statstr = "Waiting for their public key. Will request it again soon"
elif status == "doingpowforpubkey":
elif status == "doingpubkeypow":
statstr = "Encryption key request queued"
elif status == "msgqueued":
statstr = "Message queued"

View File

@ -9,66 +9,6 @@
# The software version variable is now held in shared.py
import os
import sys
app_dir = os.path.dirname(os.path.abspath(__file__))
os.chdir(app_dir)
sys.path.insert(0, app_dir)
import depends
depends.check_dependencies()
# Used to capture a Ctrl-C keypress so that Bitmessage can shutdown gracefully.
import signal
# The next 3 are used for the API
from singleinstance import singleinstance
import errno
import socket
import ctypes
from struct import pack
from subprocess import call
from time import sleep
from random import randint
import getopt
from api import MySimpleXMLRPCRequestHandler, StoppableXMLRPCServer
from helper_startup import (
isOurOperatingSystemLimitedToHavingVeryFewHalfOpenConnections
)
import defaults
import shared
import knownnodes
import state
import shutdown
import threading
# Classes
from class_sqlThread import sqlThread
from class_singleCleaner import singleCleaner
from class_objectProcessor import objectProcessor
from class_singleWorker import singleWorker
from class_addressGenerator import addressGenerator
from bmconfigparser import BMConfigParser
from inventory import Inventory
from network.connectionpool import BMConnectionPool
from network.dandelion import Dandelion
from network.networkthread import BMNetworkThread
from network.receivequeuethread import ReceiveQueueThread
from network.announcethread import AnnounceThread
from network.invthread import InvThread
from network.addrthread import AddrThread
from network.downloadthread import DownloadThread
# Helper Functions
import helper_bootstrap
import helper_generic
import helper_threading
def connectToStream(streamNumber):
state.streamsInWhichIAmParticipating.append(streamNumber)
selfInitiatedConnections[streamNumber] = {}
@ -155,63 +95,6 @@ def _fixSocket():
socket.IPV6_V6ONLY = 27
# This thread, of which there is only one, runs the API.
class singleAPI(threading.Thread, helper_threading.StoppableThread):
def __init__(self):
threading.Thread.__init__(self, name="singleAPI")
self.initStop()
def stopThread(self):
super(singleAPI, self).stopThread()
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.connect((
BMConfigParser().get('bitmessagesettings', 'apiinterface'),
BMConfigParser().getint('bitmessagesettings', 'apiport')
))
s.shutdown(socket.SHUT_RDWR)
s.close()
except:
pass
def run(self):
port = BMConfigParser().getint('bitmessagesettings', 'apiport')
try:
from errno import WSAEADDRINUSE
except (ImportError, AttributeError):
errno.WSAEADDRINUSE = errno.EADDRINUSE
for attempt in range(50):
try:
if attempt > 0:
port = randint(32767, 65535)
se = StoppableXMLRPCServer(
(BMConfigParser().get(
'bitmessagesettings', 'apiinterface'),
port),
MySimpleXMLRPCRequestHandler, True, True)
except socket.error as e:
if e.errno in (errno.EADDRINUSE, errno.WSAEADDRINUSE):
continue
else:
if attempt > 0:
BMConfigParser().set(
"bitmessagesettings", "apiport", str(port))
BMConfigParser().save()
break
se.register_introspection_functions()
se.serve_forever()
# This is a list of current connections (the thread pointers at least)
selfInitiatedConnections = {}
if shared.useVeryEasyProofOfWorkForTesting:
defaults.networkDefaultProofOfWorkNonceTrialsPerByte = int(
defaults.networkDefaultProofOfWorkNonceTrialsPerByte / 100)
defaults.networkDefaultPayloadLengthExtraBytes = int(
defaults.networkDefaultPayloadLengthExtraBytes / 100)
class Main:
def start(self):
_fixSocket()
@ -318,7 +201,6 @@ class Main:
smtpServerThread = smtpServer()
smtpServerThread.start()
# Start the thread that calculates POWs
objectProcessorThread = objectProcessor()
# DON'T close the main program even the thread remains.
# This thread checks the shutdown variable after processing
@ -502,10 +384,84 @@ def main():
mainprogram = Main()
mainprogram.start()
# See "workprover/Readme.md"
import os
import sys
app_dir = os.path.dirname(os.path.abspath(__file__))
os.chdir(app_dir)
sys.path.insert(0, app_dir)
import pyelliptic.openssl
import workprover.dumbsolver
workprover.dumbsolver.libcrypto = pyelliptic.openssl.OpenSSL._lib
if __name__ == "__main__":
main()
import multiprocessing
multiprocessing.freeze_support()
import depends
depends.check_dependencies()
# Used to capture a Ctrl-C keypress so that Bitmessage can shutdown gracefully.
import signal
# The next 3 are used for the API
from singleinstance import singleinstance
import socket
import ctypes
from struct import pack
from subprocess import call
from time import sleep
import getopt
from api import singleAPI
from helper_startup import (
isOurOperatingSystemLimitedToHavingVeryFewHalfOpenConnections
)
import defaults
import shared
import knownnodes
import state
import shutdown
# Classes
from class_sqlThread import sqlThread
from class_singleCleaner import singleCleaner
from class_objectProcessor import objectProcessor
from singleworker import singleWorker
from class_addressGenerator import addressGenerator
from bmconfigparser import BMConfigParser
from inventory import Inventory
from network.connectionpool import BMConnectionPool
from network.dandelion import Dandelion
from network.networkthread import BMNetworkThread
from network.receivequeuethread import ReceiveQueueThread
from network.announcethread import AnnounceThread
from network.invthread import InvThread
from network.addrthread import AddrThread
from network.downloadthread import DownloadThread
# Helper Functions
import helper_bootstrap
import helper_generic
import helper_threading
# This is a list of current connections (the thread pointers at least)
selfInitiatedConnections = {}
if shared.useVeryEasyProofOfWorkForTesting:
defaults.networkDefaultProofOfWorkNonceTrialsPerByte = int(
defaults.networkDefaultProofOfWorkNonceTrialsPerByte / 100)
defaults.networkDefaultPayloadLengthExtraBytes = int(
defaults.networkDefaultPayloadLengthExtraBytes / 100)
main()
# So far, the creation of and management of the Bitmessage protocol and this
# client is a one-man operation. Bitcoin tips are quite appreciated.

View File

@ -20,17 +20,15 @@ from debug import logger
from tr import _translate
from addresses import decodeAddress, addBMIfNotPresent
import shared
from bitmessageui import Ui_MainWindow
from bmconfigparser import BMConfigParser
import defaults
import namecoin
from messageview import MessageView
from migrationwizard import Ui_MigrationWizard
from foldertree import (
AccountMixin, Ui_FolderWidget, Ui_AddressWidget, Ui_SubscriptionWidget,
AccountMixin, AddressBookCompleter, Ui_FolderWidget, Ui_AddressWidget, Ui_SubscriptionWidget,
MessageList_AddressWidget, MessageList_SubjectWidget,
Ui_AddressBookWidgetItemLabel, Ui_AddressBookWidgetItemAddress)
from settings import Ui_settingsDialog
import settingsmixin
import support
import debug
@ -38,24 +36,24 @@ from helper_ackPayload import genAckPayload
from helper_sql import sqlQuery, sqlExecute, sqlExecuteChunked, sqlStoredProcedure
import helper_search
import l10n
import openclpow
from utils import str_broadcast_subscribers, avatarize
from account import (
getSortedAccounts, getSortedSubscriptions, accountClass, BMAccount,
GatewayAccount, MailchuckAccount, AccountColor)
import dialogs
from helper_generic import powQueueSize
from network.stats import pendingDownload, pendingUpload
from uisignaler import UISignaler
import knownnodes
import paths
from proofofwork import getPowType
import queues
import shutdown
import state
from statusbar import BMStatusBar
from network.asyncore_pollchoose import set_rates
import sound
import re
import bitmessage_icons_rc # Loads icon resources
import workprover.utils
import singleworker
try:
@ -371,13 +369,19 @@ class MyForm(settingsmixin.SMainWindow):
# Popup menu for the Sent page
self.ui.sentContextMenuToolbar = QtGui.QToolBar()
# Actions
self.actionTrashSentMessage = self.ui.sentContextMenuToolbar.addAction(
_translate(
"MainWindow", "Move to Trash"), self.on_action_SentTrash)
self.actionSentClipboard = self.ui.sentContextMenuToolbar.addAction(
_translate(
"MainWindow", "Copy destination address to clipboard"),
self.on_action_SentClipboard)
self.actionEditAndResend = self.ui.sentContextMenuToolbar.addAction(
_translate(
"MainWindow", "Edit and resend"), self.on_action_EditAndResend)
self.actionTrashSentMessage = self.ui.sentContextMenuToolbar.addAction(
_translate(
"MainWindow", "Move to Trash"), self.on_action_SentTrash)
self.actionCancelSending = self.ui.sentContextMenuToolbar.addAction(
_translate(
"MainWindow", "Cancel sending"), self.on_action_CancelSending)
self.actionForceSend = self.ui.sentContextMenuToolbar.addAction(
_translate(
"MainWindow", "Force send"), self.on_action_ForceSend)
@ -585,9 +589,54 @@ class MyForm(settingsmixin.SMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_MainWindow()
self.UI = widgets.loadType("bitmessageui.ui")[0]
self.ui = self.UI()
self.ui.setupUi(self)
self.ui.blackwhitelist.retranslateUi()
self.ui.networkstatus.retranslateUi()
addressBookCompleter = AddressBookCompleter()
addressBookCompleter.setCompletionMode(QtGui.QCompleter.PopupCompletion)
addressBookCompleter.setCaseSensitivity(QtCore.Qt.CaseInsensitive)
addressBookCompleter.setModel(QtGui.QStringListModel())
self.ui.lineEditTo.setCompleter(addressBookCompleter)
self.ui.pushButtonAddAddressBook.resize(200, self.ui.pushButtonAddAddressBook.height())
self.ui.pushButtonAddChan.resize(200, self.ui.pushButtonAddChan.height())
self.ui.pushButtonAddSubscription.resize(200, self.ui.pushButtonAddSubscription.height())
self.ui.pushButtonFetchNamecoinID.resize(200, self.ui.pushButtonFetchNamecoinID.height())
self.ui.pushButtonNewAddress.resize(200, self.ui.pushButtonNewAddress.height())
self.ui.tableWidgetAddressBook.resize(200, self.ui.tableWidgetAddressBook.height())
self.ui.treeWidgetChans.resize(200, self.ui.treeWidgetChans.height())
self.ui.treeWidgetSubscriptions.resize(200, self.ui.treeWidgetSubscriptions.height())
self.ui.treeWidgetYourIdentities.resize(200, self.ui.treeWidgetYourIdentities.height())
# TODO: why we need splitters here if they are disabled?
self.ui.horizontalSplitter_2.handle(1).setEnabled(False)
self.ui.horizontalSplitter_6.handle(1).setEnabled(False)
self.ui.horizontalSplitterSearch.handle(1).setEnabled(False)
self.ui.verticalSplitter.handle(1).setEnabled(False)
self.ui.verticalSplitter_2.handle(1).setEnabled(False)
self.ui.verticalSplitter_2.handle(2).setEnabled(False)
self.ui.verticalSplitter_3.handle(1).setEnabled(False)
self.ui.verticalSplitter_4.handle(1).setEnabled(False)
self.ui.verticalSplitter_5.handle(1).setEnabled(False)
self.ui.verticalSplitter_6.handle(1).setEnabled(False)
self.ui.verticalSplitter_7.handle(1).setEnabled(False)
self.ui.verticalSplitter_8.handle(1).setEnabled(False)
self.ui.verticalSplitter_12.handle(1).setEnabled(False)
self.ui.verticalSplitter_17.handle(1).setEnabled(False)
self.ui.horizontalSliderTTL.setMaximumSize(QtCore.QSize(105, self.ui.pushButtonSend.height()))
self.statusbar = self.statusBar()
self.statusbar.insertPermanentWidget(0, self.ui.rightStatusBar)
# Ask the user if we may delete their old version 1 addresses if they
# have any.
for addressInKeysFile in getSortedAccounts():
@ -704,18 +753,16 @@ class MyForm(settingsmixin.SMainWindow):
self.tabWidgetCurrentChanged
)
# Put the colored icon on the status bar
# self.pushButtonStatusIcon.setIcon(QIcon(":/newPrefix/images/yellowicon.png"))
self.setStatusBar(BMStatusBar())
self.statusbar = self.statusBar()
# Initialize the blacklist or whitelist
self.pushButtonStatusIcon = QtGui.QPushButton(self)
self.pushButtonStatusIcon.setText('')
self.pushButtonStatusIcon.setIcon(
QtGui.QIcon(':/newPrefix/images/redicon.png'))
self.pushButtonStatusIcon.setFlat(True)
self.statusbar.insertPermanentWidget(0, self.pushButtonStatusIcon)
QtCore.QObject.connect(self.pushButtonStatusIcon, QtCore.SIGNAL(
if BMConfigParser().get("bitmessagesettings", "blackwhitelist") == "white":
self.ui.blackwhitelist.radioButtonWhitelist.click()
self.ui.blackwhitelist.rerenderBlackWhiteList()
# The coloured icon on the status bar
QtCore.QObject.connect(self.ui.pushButtonStatusIcon, QtCore.SIGNAL(
"clicked()"), self.click_pushButtonStatusIcon)
self.numberOfMessagesProcessed = 0
@ -737,13 +784,15 @@ class MyForm(settingsmixin.SMainWindow):
QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
"updateStatusBar(PyQt_PyObject)"), self.updateStatusBar)
QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
"updateSentItemStatusByToAddress(PyQt_PyObject,PyQt_PyObject)"), self.updateSentItemStatusByToAddress)
"updateSentItemStatusByToAddress(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"), self.updateSentItemStatusByToAddress)
QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
"updateSentItemStatusByAckdata(PyQt_PyObject,PyQt_PyObject)"), self.updateSentItemStatusByAckdata)
"updateSentItemStatusByAckdata(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"), self.updateSentItemStatusByAckdata)
QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
"displayNewInboxMessage(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"), self.displayNewInboxMessage)
QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
"displayNewSentMessage(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"), self.displayNewSentMessage)
QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
"deleteSentItemByAckData(PyQt_PyObject)"), self.deleteSentItemByAckData)
QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
"setStatusIcon(PyQt_PyObject)"), self.setStatusIcon)
QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
@ -762,6 +811,8 @@ class MyForm(settingsmixin.SMainWindow):
"newVersionAvailable(PyQt_PyObject)"), self.newVersionAvailable)
QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
"displayAlert(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"), self.displayAlert)
QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
"updateWorkProverStatus(PyQt_PyObject)"), self.updateWorkProverStatus)
self.UISignalThread.start()
# Key press in tree view
@ -812,6 +863,8 @@ class MyForm(settingsmixin.SMainWindow):
' Fetch Namecoin ID button')
self.ui.pushButtonFetchNamecoinID.hide()
self.POWTasksCount = 0
def updateTTL(self, sliderPosition):
TTL = int(sliderPosition ** 3.199 + 3600)
self.updateHumanFriendlyTTLDescription(TTL)
@ -1039,7 +1092,7 @@ class MyForm(settingsmixin.SMainWindow):
if status == 'awaitingpubkey':
statusText = _translate(
"MainWindow", "Waiting for their encryption key. Will request it again soon.")
elif status == 'doingpowforpubkey':
elif status == 'doingpubkeypow':
statusText = _translate(
"MainWindow", "Doing work necessary to request encryption key.")
elif status == 'msgqueued':
@ -1057,6 +1110,8 @@ class MyForm(settingsmixin.SMainWindow):
elif status == 'ackreceived':
statusText = _translate("MainWindow", "Acknowledgement of the message received %1").arg(
l10n.formatTimestamp(lastactiontime))
elif status == "msgcanceled":
statusText = _translate("MainWindow", "Message canceled.")
elif status == 'broadcastqueued':
statusText = _translate(
"MainWindow", "Broadcast queued.")
@ -1066,6 +1121,8 @@ class MyForm(settingsmixin.SMainWindow):
elif status == 'broadcastsent':
statusText = _translate("MainWindow", "Broadcast on %1").arg(
l10n.formatTimestamp(lastactiontime))
elif status == "broadcastcanceled":
statusText = _translate("MainWindow", "Broadcast canceled.")
elif status == 'toodifficult':
statusText = _translate("MainWindow", "Problem: The work demanded by the recipient is more difficult than you are willing to do. %1").arg(
l10n.formatTimestamp(lastactiontime))
@ -1566,6 +1623,9 @@ class MyForm(settingsmixin.SMainWindow):
def changeEvent(self, event):
if event.type() == QtCore.QEvent.LanguageChange:
self.ui.retranslateUi(self)
self.ui.blackwhitelist.retranslateUi()
self.ui.networkstatus.retranslateUi()
self.updateHumanFriendlyTTLDescription(BMConfigParser().getint("bitmessagesettings", "ttl"))
self.init_inbox_popup_menu(False)
self.init_identities_popup_menu(False)
self.init_chan_popup_menu(False)
@ -1596,7 +1656,7 @@ class MyForm(settingsmixin.SMainWindow):
_notifications_enabled = not BMConfigParser().getboolean(
'bitmessagesettings', 'hidetrayconnectionnotifications')
if color == 'red':
self.pushButtonStatusIcon.setIcon(
self.ui.pushButtonStatusIcon.setIcon(
QtGui.QIcon(":/newPrefix/images/redicon.png"))
shared.statusIconColor = 'red'
# if the connection is lost then show a notification
@ -1622,7 +1682,7 @@ class MyForm(settingsmixin.SMainWindow):
if color == 'yellow':
if self.statusbar.currentMessage() == 'Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won\'t send until you connect.':
self.statusbar.clearMessage()
self.pushButtonStatusIcon.setIcon(
self.ui.pushButtonStatusIcon.setIcon(
QtGui.QIcon(":/newPrefix/images/yellowicon.png"))
shared.statusIconColor = 'yellow'
# if a new connection has been established then show a notification
@ -1640,7 +1700,7 @@ class MyForm(settingsmixin.SMainWindow):
if color == 'green':
if self.statusbar.currentMessage() == 'Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won\'t send until you connect.':
self.statusbar.clearMessage()
self.pushButtonStatusIcon.setIcon(
self.ui.pushButtonStatusIcon.setIcon(
QtGui.QIcon(":/newPrefix/images/greenicon.png"))
shared.statusIconColor = 'green'
if not self.connected and _notifications_enabled:
@ -1716,7 +1776,7 @@ class MyForm(settingsmixin.SMainWindow):
self.unreadCount = count
return self.unreadCount
def updateSentItemStatusByToAddress(self, toAddress, textToDisplay):
def updateSentItemStatusByToAddress(self, status, toAddress, textToDisplay):
for sent in [self.ui.tableWidgetInbox, self.ui.tableWidgetInboxSubscriptions, self.ui.tableWidgetInboxChans]:
treeWidget = self.widgetConvert(sent)
if self.getCurrentFolder(treeWidget) != "sent":
@ -1727,6 +1787,16 @@ class MyForm(settingsmixin.SMainWindow):
for i in range(sent.rowCount()):
rowAddress = sent.item(i, 0).data(QtCore.Qt.UserRole)
if toAddress == rowAddress:
tableAckData = str(sent.item(i, 3).data(QtCore.Qt.UserRole).toPyObject())
count = sqlQuery("""
SELECT COUNT(*) FROM "sent"
WHERE "ackdata" == ? AND "status" == ?;
""", tableAckData, status)[0][0]
if count == 0:
continue
sent.item(i, 3).setToolTip(textToDisplay)
try:
newlinePosition = textToDisplay.indexOf('\n')
@ -1738,7 +1808,15 @@ class MyForm(settingsmixin.SMainWindow):
else:
sent.item(i, 3).setText(textToDisplay)
def updateSentItemStatusByAckdata(self, ackdata, textToDisplay):
def updateSentItemStatusByAckdata(self, status, ackdata, textToDisplay):
count = sqlQuery("""
SELECT COUNT(*) FROM "sent"
WHERE "ackdata" == ? AND "status" == ?;
""", ackdata, status)[0][0]
if count == 0:
return
if type(ackdata) is str:
ackdata = QtCore.QByteArray(ackdata)
for sent in [self.ui.tableWidgetInbox, self.ui.tableWidgetInboxSubscriptions, self.ui.tableWidgetInboxChans]:
@ -1793,6 +1871,22 @@ class MyForm(settingsmixin.SMainWindow):
if exitAfterUserClicksOk:
os._exit(0)
def updateWorkProverStatus(self, status):
self.POWTasksCount = status.tasksCount
if status.speed == 0:
self.ui.workProverSpeed.setText("")
self.ui.workProverSpeed.setToolTip("")
else:
self.ui.workProverSpeed.setText(
_translate("MainWindow", "%1 kiH / s").arg("{:.1f}".format(status.speed / 1024))
)
self.ui.workProverSpeed.setToolTip("Difficulty: {}, 95 % completion time: {:.1f} s".format(
status.difficulty,
workprover.utils.estimateMaximumIterationsCount(status.difficulty, .95) / status.speed
))
def rerenderMessagelistFromLabels(self):
for messagelist in (self.ui.tableWidgetInbox, self.ui.tableWidgetInboxChans, self.ui.tableWidgetInboxSubscriptions):
for i in range(messagelist.rowCount()):
@ -2298,6 +2392,37 @@ class MyForm(settingsmixin.SMainWindow):
dialogs.EmailGatewayDialog(
self, BMConfigParser(), acct).exec_()
def deleteSentItemByAckData(self, ackData):
ackData = QtCore.QByteArray(ackData)
for tableWidget in [
self.ui.tableWidgetInbox,
self.ui.tableWidgetInboxSubscriptions,
self.ui.tableWidgetInboxChans
]:
treeWidget = self.widgetConvert(tableWidget)
if self.getCurrentFolder(treeWidget) != "sent":
continue
i = 0
while i < tableWidget.rowCount():
tableAckData = tableWidget.item(i, 3).data(QtCore.Qt.UserRole).toPyObject()
if ackData == tableAckData:
textEdit = self.getCurrentMessageTextedit()
if textEdit is not False:
textEdit.setPlainText("")
tableWidget.removeRow(i)
tableWidget.selectRow(max(0, i - 1))
self.updateStatusBar(_translate("MainWindow", "Moved items to trash."))
else:
i += 1
def click_pushButtonAddAddressBook(self, dialog=None):
if not dialog:
dialog = dialogs.AddAddressDialog(self)
@ -2490,9 +2615,34 @@ class MyForm(settingsmixin.SMainWindow):
BMConfigParser().set('bitmessagesettings', 'defaultpayloadlengthextrabytes', str(int(float(
self.settingsDialogInstance.ui.lineEditSmallMessageDifficulty.text()) * defaults.networkDefaultPayloadLengthExtraBytes)))
if self.settingsDialogInstance.ui.comboBoxOpenCL.currentText().toUtf8() != BMConfigParser().safeGet("bitmessagesettings", "opencl"):
BMConfigParser().set('bitmessagesettings', 'opencl', str(self.settingsDialogInstance.ui.comboBoxOpenCL.currentText()))
queues.workerQueue.put(('resetPoW', ''))
if self.settingsDialogInstance.ui.radioButtonDumbSolver.isChecked():
BMConfigParser().set("bitmessagesettings", "powsolver", "dumb")
elif self.settingsDialogInstance.ui.radioButtonForkingSolver.isChecked():
BMConfigParser().set("bitmessagesettings", "powsolver", "forking")
BMConfigParser().set(
"bitmessagesettings",
"processes",
str(self.settingsDialogInstance.ui.spinBoxForkingSolverParallelism.value())
)
elif self.settingsDialogInstance.ui.radioButtonFastSolver.isChecked():
BMConfigParser().set("bitmessagesettings", "powsolver", "fast")
BMConfigParser().set(
"bitmessagesettings",
"threads",
str(self.settingsDialogInstance.ui.spinBoxFastSolverParallelism.value())
)
elif self.settingsDialogInstance.ui.radioButtonGPUSolver.isChecked():
BMConfigParser().set("bitmessagesettings", "powsolver", "gpu")
BMConfigParser().set(
"bitmessagesettings",
"opencl",
str(self.settingsDialogInstance.ui.comboBoxGPUVendor.currentText().toUtf8())
)
singleworker.setBestSolver()
acceptableDifficultyChanged = False
@ -2681,6 +2831,14 @@ class MyForm(settingsmixin.SMainWindow):
def click_NewAddressDialog(self):
dialogs.NewAddressDialog(self)
def updateNetworkSwitchMenuLabel(self, dontconnect = None):
if dontconnect is None:
dontconnect = BMConfigParser().safeGetBoolean("bitmessagesettings", "dontconnect")
self.ui.actionNetworkSwitch.setText(
_translate("MainWindow", "Go online" if dontconnect else "Go offline", None)
)
def network_switch(self):
dontconnect_option = not BMConfigParser().safeGetBoolean(
'bitmessagesettings', 'dontconnect')
@ -2700,7 +2858,7 @@ class MyForm(settingsmixin.SMainWindow):
BMConfigParser().set(
'bitmessagesettings', 'dontconnect', str(dontconnect_option))
BMConfigParser().save()
self.ui.updateNetworkSwitchMenuLabel(dontconnect_option)
self.updateNetworkSwitchMenuLabel(dontconnect_option)
self.ui.pushButtonFetchNamecoinID.setHidden(
dontconnect_option or self.namecoin.test()[0] == 'failed'
@ -2728,9 +2886,9 @@ class MyForm(settingsmixin.SMainWindow):
waitForSync = False
# C PoW currently doesn't support interrupting and OpenCL is untested
if getPowType() == "python" and (powQueueSize() > 0 or pendingUpload() > 0):
if self.POWTasksCount > 0 or pendingUpload() > 0:
reply = QtGui.QMessageBox.question(self, _translate("MainWindow", "Proof of work pending"),
_translate("MainWindow", "%n object(s) pending proof of work", None, QtCore.QCoreApplication.CodecForTr, powQueueSize()) + ", " +
_translate("MainWindow", "%n object(s) pending proof of work", None, QtCore.QCoreApplication.CodecForTr, self.POWTasksCount) + ", " +
_translate("MainWindow", "%n object(s) waiting to be distributed", None, QtCore.QCoreApplication.CodecForTr, pendingUpload()) + "\n\n" +
_translate("MainWindow", "Wait until these tasks finish?"),
QtGui.QMessageBox.Yes|QtGui.QMessageBox.No|QtGui.QMessageBox.Cancel, QtGui.QMessageBox.Cancel)
@ -2786,10 +2944,10 @@ class MyForm(settingsmixin.SMainWindow):
if waitForPow:
# check if PoW queue empty
maxWorkerQueue = 0
curWorkerQueue = powQueueSize()
curWorkerQueue = self.POWTasksCount
while curWorkerQueue > 0:
# worker queue size
curWorkerQueue = powQueueSize()
curWorkerQueue = self.POWTasksCount
if curWorkerQueue > maxWorkerQueue:
maxWorkerQueue = curWorkerQueue
if curWorkerQueue > 0:
@ -3233,47 +3391,159 @@ class MyForm(settingsmixin.SMainWindow):
logger.exception('Message not saved', exc_info=True)
self.updateStatusBar(_translate("MainWindow", "Write error."))
# Send item on the Sent tab to trash
def on_action_SentTrash(self):
currentRow = 0
unread = False
def on_action_EditAndResend(self):
tableWidget = self.getCurrentMessagelist()
if not tableWidget:
return
folder = self.getCurrentFolder()
shifted = (QtGui.QApplication.queryKeyboardModifiers() & QtCore.Qt.ShiftModifier) > 0
while tableWidget.selectedIndexes() != []:
currentRow = tableWidget.selectedIndexes()[0].row()
ackdataToTrash = str(tableWidget.item(
currentRow, 3).data(QtCore.Qt.UserRole).toPyObject())
if folder == "trash" or shifted:
sqlExecute('''DELETE FROM sent WHERE ackdata=?''', ackdataToTrash)
else:
sqlExecute('''UPDATE sent SET folder='trash' WHERE ackdata=?''', ackdataToTrash)
if tableWidget.item(currentRow, 0).unread:
self.propagateUnreadCount(tableWidget.item(currentRow, 1 if tableWidget.item(currentRow, 1).type == AccountMixin.SUBSCRIPTION else 0).data(QtCore.Qt.UserRole), folder, self.getCurrentTreeWidget(), -1)
self.getCurrentMessageTextedit().setPlainText("")
tableWidget.removeRow(currentRow)
self.updateStatusBar(_translate(
"MainWindow", "Moved items to trash."))
self.ui.tableWidgetInbox.selectRow(
currentRow if currentRow == 0 else currentRow - 1)
shiftPressed = QtGui.QApplication.queryKeyboardModifiers() & QtCore.Qt.ShiftModifier != 0
folder = self.getCurrentFolder()
trash = not (folder == "trash" or shiftPressed)
for i in tableWidget.selectedIndexes():
if i.column() != 3:
continue
ackData = str(i.data(QtCore.Qt.UserRole).toPyObject())
queryReturn = sqlQuery("""
SELECT "status", "toaddress", "fromaddress", "subject", "message", "TTL" FROM "sent"
WHERE "ackdata" == ?;
""", ackData)
if len(queryReturn) == 0:
continue
status, destination, address, subject, body, TTL = queryReturn[0]
if status in ["broadcastqueued", "doingbroadcastpow", "broadcastsent", "broadcastcanceled"]:
self.ui.comboBoxSendFromBroadcast.setCurrentIndex(0)
for j in range(self.ui.comboBoxSendFromBroadcast.count()):
currentAddress = str(self.ui.comboBoxSendFromBroadcast.itemData(j).toPyObject())
if currentAddress == address:
self.ui.comboBoxSendFromBroadcast.setCurrentIndex(j)
break
self.ui.lineEditSubjectBroadcast.setText(subject)
self.ui.textEditMessageBroadcast.setText(body)
self.ui.tabWidgetSend.setCurrentIndex(self.ui.tabWidgetSend.indexOf(self.ui.sendBroadcast))
else:
self.ui.comboBoxSendFrom.setCurrentIndex(0)
for j in range(self.ui.comboBoxSendFrom.count()):
currentAddress = str(self.ui.comboBoxSendFrom.itemData(j).toPyObject())
if currentAddress == address:
self.ui.comboBoxSendFrom.setCurrentIndex(j)
break
self.ui.lineEditTo.setText(destination)
self.ui.lineEditSubject.setText(subject)
self.ui.textEditMessage.setText(body)
self.ui.tabWidgetSend.setCurrentIndex(self.ui.tabWidgetSend.indexOf(self.ui.sendDirect))
# TODO: 3.192 fits better
self.ui.horizontalSliderTTL.setSliderPosition((TTL - 3600) ** (1 / 3.199))
self.ui.tabWidget.setCurrentIndex(self.ui.tabWidget.indexOf(self.ui.send))
break
def on_action_SentTrash(self):
tableWidget = self.getCurrentMessagelist()
if not tableWidget:
return
shiftPressed = QtGui.QApplication.queryKeyboardModifiers() & QtCore.Qt.ShiftModifier != 0
folder = self.getCurrentFolder()
trash = not (folder == "trash" or shiftPressed)
for i in tableWidget.selectedIndexes():
if i.column() != 3:
continue
ackData = str(i.data(QtCore.Qt.UserRole).toPyObject())
queryReturn = sqlQuery("""SELECT "status" FROM "sent" WHERE "ackdata" == ?;""", ackData)
if len(queryReturn) == 0:
continue
status = queryReturn[0][0]
if status in ["broadcastqueued", "doingbroadcastpow", "broadcastsent", "broadcastcanceled"]:
queues.workerQueue.put(("cancelBroadcast", ackData, True, trash))
else:
queues.workerQueue.put(("cancelMessage", ackData, True, trash))
def on_action_CancelSending(self):
tableWidget = self.getCurrentMessagelist()
if not tableWidget:
return
for i in tableWidget.selectedIndexes():
if i.column() != 3:
continue
ackData = str(i.data(QtCore.Qt.UserRole).toPyObject())
queryReturn = sqlQuery("""SELECT "status" FROM "sent" WHERE "ackdata" == ?;""", ackData)
if len(queryReturn) == 0:
continue
status = queryReturn[0][0]
if status in ["broadcastqueued", "doingbroadcastpow"]:
queues.workerQueue.put(("cancelBroadcast", ackData, False, None))
elif status not in ["ackreceived", "msgsentnoackexpected", "badkey", "msgcanceled"]:
queues.workerQueue.put(("cancelMessage", ackData, False, None))
def on_action_ForceSend(self):
currentRow = self.ui.tableWidgetInbox.currentRow()
addressAtCurrentRow = self.ui.tableWidgetInbox.item(
currentRow, 0).data(QtCore.Qt.UserRole)
toRipe = decodeAddress(addressAtCurrentRow)[3]
sqlExecute(
'''UPDATE sent SET status='forcepow' WHERE toripe=? AND status='toodifficult' and folder='sent' ''',
toRipe)
queryreturn = sqlQuery('''select ackdata FROM sent WHERE status='forcepow' ''')
for row in queryreturn:
ackdata, = row
queues.UISignalQueue.put(('updateSentItemStatusByAckdata', (
ackdata, 'Overriding maximum-difficulty setting. Work queued.')))
queues.workerQueue.put(('sendmessage', ''))
tableWidget = self.getCurrentMessagelist()
if not tableWidget:
return
resendMessages = False
for i in tableWidget.selectedIndexes():
if i.column() != 3:
continue
ackData = str(i.data(QtCore.Qt.UserRole).toPyObject())
queryReturn = sqlQuery("""SELECT "status" FROM "sent" WHERE "ackdata" == ?;""", ackData)
if len(queryReturn) == 0:
continue
if queryReturn[0][0] == "toodifficult":
sqlExecute("""
UPDATE "sent" SET "status" = 'forcepow'
WHERE "ackdata" == ? AND "status" == 'toodifficult' AND "folder" = 'sent';
""", ackData)
queues.UISignalQueue.put(("updateSentItemStatusByAckdata", (
"forcepow",
ackData,
"Overriding maximum-difficulty setting. Work queued." # TODO: add translation?
)))
resendMessages = True
if resendMessages:
queues.workerQueue.put(("sendmessage", ))
def on_action_SentClipboard(self):
currentRow = self.ui.tableWidgetInbox.currentRow()
@ -3935,23 +4205,56 @@ class MyForm(settingsmixin.SMainWindow):
self.popMenuInbox.exec_(tableWidget.mapToGlobal(point))
def on_context_menuSent(self, point):
self.popMenuSent = QtGui.QMenu(self)
self.popMenuSent.addAction(self.actionSentClipboard)
self.popMenuSent.addAction(self.actionTrashSentMessage)
tableWidget = self.getCurrentMessagelist()
# Check to see if this item is toodifficult and display an additional
# menu option (Force Send) if it is.
currentRow = self.ui.tableWidgetInbox.currentRow()
if currentRow >= 0:
ackData = str(self.ui.tableWidgetInbox.item(
currentRow, 3).data(QtCore.Qt.UserRole).toPyObject())
queryreturn = sqlQuery('''SELECT status FROM sent where ackdata=?''', ackData)
for row in queryreturn:
status, = row
if status == 'toodifficult':
if not tableWidget:
return
showMenu = False
cancelSending = False
forceSend = False
for i in tableWidget.selectedIndexes():
if i.column() != 3:
continue
ackData = str(i.data(QtCore.Qt.UserRole).toPyObject())
queryReturn = sqlQuery("""SELECT "status" FROM "sent" WHERE "ackdata" == ?;""", ackData)
if len(queryReturn) == 0:
continue
status = queryReturn[0][0]
if status not in [
"ackreceived", "msgsentnoackexpected", "badkey", "msgcanceled",
"broadcastsent", "broadcastcanceled"
]:
cancelSending = True
if status == "toodifficult":
forceSend = True
showMenu = True
if showMenu:
self.popMenuSent = QtGui.QMenu(self)
self.popMenuSent.addAction(self.actionSentClipboard)
self.popMenuSent.addSeparator()
self.popMenuSent.addAction(self.actionEditAndResend)
if forceSend:
self.popMenuSent.addAction(self.actionForceSend)
self.popMenuSent.exec_(self.ui.tableWidgetInbox.mapToGlobal(point))
self.popMenuSent.addSeparator()
if cancelSending:
self.popMenuSent.addAction(self.actionCancelSending)
self.popMenuSent.addAction(self.actionTrashSentMessage)
self.popMenuSent.exec_(tableWidget.mapToGlobal(point))
def inboxSearchLineEditUpdated(self, text):
# dynamic search for too short text is slow
@ -4129,8 +4432,11 @@ class settingsDialog(QtGui.QDialog):
def __init__(self, parent):
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_settingsDialog()
self.UI = widgets.loadType("settings.ui")[0]
self.ui = self.UI()
self.ui.setupUi(self)
self.parent = parent
self.ui.checkBoxStartOnLogon.setChecked(
BMConfigParser().getboolean('bitmessagesettings', 'startonlogon'))
@ -4176,6 +4482,41 @@ class settingsDialog(QtGui.QDialog):
self.ui.checkBoxStartOnLogon.setDisabled(True)
self.ui.checkBoxStartOnLogon.setText(_translate(
"MainWindow", "Start-on-login not yet supported on your OS."))
self.ui.languageComboBox.addItem(
QtGui.QApplication.translate("settingsDialog", "System Settings", "system"),
"system"
)
localeRegexp = re.compile("bitmessage_(.*).qm$")
localeNames = {"eo": "Esperanto", "en_pirate": "Pirate English"}
for i in sorted(os.listdir(os.path.join(paths.codePath(), "translations"))):
match = localeRegexp.match(i)
if match is None:
continue
localeCode = match.group(1)
if localeCode in localeNames:
localeName = localeNames[localeCode]
else:
localeName = QtCore.QLocale(QtCore.QString(localeCode)).nativeLanguageName()
if localeName == "":
localeName = localeCode
self.ui.languageComboBox.addItem(localeName, localeCode)
configuredLocale = BMConfigParser().safeGet("bitmessagesettings", "userlocale", "system")
for i in range(self.ui.languageComboBox.count()):
if self.ui.languageComboBox.itemData(i) == configuredLocale:
self.ui.languageComboBox.setCurrentIndex(i)
break
# On the Network settings tab:
self.ui.lineEditTCPPort.setText(str(
BMConfigParser().get('bitmessagesettings', 'port')))
@ -4212,6 +4553,9 @@ class settingsDialog(QtGui.QDialog):
BMConfigParser().get('bitmessagesettings', 'maxdownloadrate')))
self.ui.lineEditMaxUploadRate.setText(str(
BMConfigParser().get('bitmessagesettings', 'maxuploadrate')))
self.ui.lineEditMaxOutboundConnections.setValidator(
QtGui.QIntValidator(0, 8, self.ui.lineEditMaxOutboundConnections)
)
self.ui.lineEditMaxOutboundConnections.setText(str(
BMConfigParser().get('bitmessagesettings', 'maxoutboundconnections')))
@ -4227,18 +4571,46 @@ class settingsDialog(QtGui.QDialog):
self.ui.lineEditMaxAcceptableSmallMessageDifficulty.setText(str((float(BMConfigParser().getint(
'bitmessagesettings', 'maxacceptablepayloadlengthextrabytes')) / defaults.networkDefaultPayloadLengthExtraBytes)))
# OpenCL
if openclpow.openclAvailable():
self.ui.comboBoxOpenCL.setEnabled(True)
else:
self.ui.comboBoxOpenCL.setEnabled(False)
self.ui.comboBoxOpenCL.clear()
self.ui.comboBoxOpenCL.addItem("None")
self.ui.comboBoxOpenCL.addItems(openclpow.vendors)
self.ui.comboBoxOpenCL.setCurrentIndex(0)
for i in range(self.ui.comboBoxOpenCL.count()):
if self.ui.comboBoxOpenCL.itemText(i) == BMConfigParser().safeGet('bitmessagesettings', 'opencl'):
self.ui.comboBoxOpenCL.setCurrentIndex(i)
if "forking" not in singleworker.workProver.availableSolvers:
self.ui.radioButtonForkingSolver.setEnabled(False)
if "fast" not in singleworker.workProver.availableSolvers:
self.ui.radioButtonFastSolver.setEnabled(False)
if "gpu" not in singleworker.workProver.availableSolvers:
self.ui.radioButtonGPUSolver.setEnabled(False)
solverName = BMConfigParser().safeGet("bitmessagesettings", "powsolver", "gpu")
forkingSolverParallelism = BMConfigParser().safeGetInt("bitmessagesettings", "processes")
fastSolverParallelism = BMConfigParser().safeGetInt("bitmessagesettings", "threads")
GPUVendor = BMConfigParser().safeGet("bitmessagesettings", "opencl")
if solverName == "dumb":
self.ui.radioButtonDumbSolver.setChecked(True)
elif solverName == "forking":
self.ui.radioButtonForkingSolver.setChecked(True)
elif solverName == "fast":
self.ui.radioButtonFastSolver.setChecked(True)
elif solverName == "gpu":
self.ui.radioButtonGPUSolver.setChecked(True)
self.ui.spinBoxForkingSolverParallelism.setValue(forkingSolverParallelism)
self.ui.spinBoxFastSolverParallelism.setValue(fastSolverParallelism)
vendors = set()
if GPUVendor is not None:
vendors.add(GPUVendor)
if "gpu" in singleworker.workProver.availableSolvers:
vendors |= set(singleworker.workProver.availableSolvers["gpu"].vendors)
self.ui.comboBoxGPUVendor.clear()
self.ui.comboBoxGPUVendor.addItems(list(vendors))
self.ui.comboBoxGPUVendor.setCurrentIndex(0)
for i in range(self.ui.comboBoxGPUVendor.count()):
if self.ui.comboBoxGPUVendor.itemText(i) == GPUVendor:
self.ui.comboBoxGPUVendor.setCurrentIndex(i)
break
# Namecoin integration tab
@ -4444,7 +4816,7 @@ def run():
'bitmessagesettings', 'dontconnect')
if myapp._firstrun:
myapp.showConnectDialog() # ask the user if we may connect
myapp.ui.updateNetworkSwitchMenuLabel()
myapp.updateNetworkSwitchMenuLabel()
# try:
# if BMConfigParser().get('bitmessagesettings', 'mailchuck') < 1:

View File

@ -1,777 +0,0 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'bitmessageui.ui'
#
# Created: Mon Mar 23 22:18:07 2015
# by: PyQt4 UI code generator 4.10.4
#
# WARNING! All changes made in this file will be lost!
from PyQt4 import QtCore, QtGui
from bmconfigparser import BMConfigParser
from foldertree import AddressBookCompleter
from messageview import MessageView
from messagecompose import MessageCompose
import settingsmixin
from networkstatus import NetworkStatus
from blacklist import Blacklist
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
def _fromUtf8(s):
return s
try:
_encoding = QtGui.QApplication.UnicodeUTF8
def _translate(context, text, disambig, encoding = QtCore.QCoreApplication.CodecForTr, n = None):
if n is None:
return QtGui.QApplication.translate(context, text, disambig, _encoding)
else:
return QtGui.QApplication.translate(context, text, disambig, _encoding, n)
except AttributeError:
def _translate(context, text, disambig, encoding = QtCore.QCoreApplication.CodecForTr, n = None):
if n is None:
return QtGui.QApplication.translate(context, text, disambig)
else:
return QtGui.QApplication.translate(context, text, disambig, QtCore.QCoreApplication.CodecForTr, n)
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName(_fromUtf8("MainWindow"))
MainWindow.resize(885, 580)
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/can-icon-24px.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
MainWindow.setWindowIcon(icon)
MainWindow.setTabShape(QtGui.QTabWidget.Rounded)
self.centralwidget = QtGui.QWidget(MainWindow)
self.centralwidget.setObjectName(_fromUtf8("centralwidget"))
self.gridLayout_10 = QtGui.QGridLayout(self.centralwidget)
self.gridLayout_10.setObjectName(_fromUtf8("gridLayout_10"))
self.tabWidget = QtGui.QTabWidget(self.centralwidget)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.tabWidget.sizePolicy().hasHeightForWidth())
self.tabWidget.setSizePolicy(sizePolicy)
self.tabWidget.setMinimumSize(QtCore.QSize(0, 0))
self.tabWidget.setBaseSize(QtCore.QSize(0, 0))
font = QtGui.QFont()
font.setPointSize(9)
self.tabWidget.setFont(font)
self.tabWidget.setTabPosition(QtGui.QTabWidget.North)
self.tabWidget.setTabShape(QtGui.QTabWidget.Rounded)
self.tabWidget.setObjectName(_fromUtf8("tabWidget"))
self.inbox = QtGui.QWidget()
self.inbox.setObjectName(_fromUtf8("inbox"))
self.gridLayout = QtGui.QGridLayout(self.inbox)
self.gridLayout.setObjectName(_fromUtf8("gridLayout"))
self.horizontalSplitter_3 = settingsmixin.SSplitter()
self.horizontalSplitter_3.setObjectName(_fromUtf8("horizontalSplitter_3"))
self.verticalSplitter_12 = settingsmixin.SSplitter()
self.verticalSplitter_12.setObjectName(_fromUtf8("verticalSplitter_12"))
self.verticalSplitter_12.setOrientation(QtCore.Qt.Vertical)
self.treeWidgetYourIdentities = settingsmixin.STreeWidget(self.inbox)
self.treeWidgetYourIdentities.setObjectName(_fromUtf8("treeWidgetYourIdentities"))
self.treeWidgetYourIdentities.resize(200, self.treeWidgetYourIdentities.height())
icon1 = QtGui.QIcon()
icon1.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/identities.png")), QtGui.QIcon.Selected, QtGui.QIcon.Off)
self.treeWidgetYourIdentities.headerItem().setIcon(0, icon1)
self.verticalSplitter_12.addWidget(self.treeWidgetYourIdentities)
self.pushButtonNewAddress = QtGui.QPushButton(self.inbox)
self.pushButtonNewAddress.setObjectName(_fromUtf8("pushButtonNewAddress"))
self.pushButtonNewAddress.resize(200, self.pushButtonNewAddress.height())
self.verticalSplitter_12.addWidget(self.pushButtonNewAddress)
self.verticalSplitter_12.setStretchFactor(0, 1)
self.verticalSplitter_12.setStretchFactor(1, 0)
self.verticalSplitter_12.setCollapsible(0, False)
self.verticalSplitter_12.setCollapsible(1, False)
self.verticalSplitter_12.handle(1).setEnabled(False)
self.horizontalSplitter_3.addWidget(self.verticalSplitter_12)
self.verticalSplitter_7 = settingsmixin.SSplitter()
self.verticalSplitter_7.setObjectName(_fromUtf8("verticalSplitter_7"))
self.verticalSplitter_7.setOrientation(QtCore.Qt.Vertical)
self.horizontalSplitterSearch = QtGui.QSplitter()
self.horizontalSplitterSearch.setObjectName(_fromUtf8("horizontalSplitterSearch"))
self.inboxSearchLineEdit = QtGui.QLineEdit(self.inbox)
self.inboxSearchLineEdit.setObjectName(_fromUtf8("inboxSearchLineEdit"))
self.horizontalSplitterSearch.addWidget(self.inboxSearchLineEdit)
self.inboxSearchOption = QtGui.QComboBox(self.inbox)
self.inboxSearchOption.setObjectName(_fromUtf8("inboxSearchOption"))
self.inboxSearchOption.addItem(_fromUtf8(""))
self.inboxSearchOption.addItem(_fromUtf8(""))
self.inboxSearchOption.addItem(_fromUtf8(""))
self.inboxSearchOption.addItem(_fromUtf8(""))
self.inboxSearchOption.addItem(_fromUtf8(""))
self.inboxSearchOption.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToContents)
self.horizontalSplitterSearch.addWidget(self.inboxSearchOption)
self.horizontalSplitterSearch.handle(1).setEnabled(False)
self.horizontalSplitterSearch.setStretchFactor(0, 1)
self.horizontalSplitterSearch.setStretchFactor(1, 0)
self.verticalSplitter_7.addWidget(self.horizontalSplitterSearch)
self.tableWidgetInbox = settingsmixin.STableWidget(self.inbox)
self.tableWidgetInbox.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
self.tableWidgetInbox.setAlternatingRowColors(True)
self.tableWidgetInbox.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
self.tableWidgetInbox.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
self.tableWidgetInbox.setWordWrap(False)
self.tableWidgetInbox.setObjectName(_fromUtf8("tableWidgetInbox"))
self.tableWidgetInbox.setColumnCount(4)
self.tableWidgetInbox.setRowCount(0)
item = QtGui.QTableWidgetItem()
self.tableWidgetInbox.setHorizontalHeaderItem(0, item)
item = QtGui.QTableWidgetItem()
self.tableWidgetInbox.setHorizontalHeaderItem(1, item)
item = QtGui.QTableWidgetItem()
self.tableWidgetInbox.setHorizontalHeaderItem(2, item)
item = QtGui.QTableWidgetItem()
self.tableWidgetInbox.setHorizontalHeaderItem(3, item)
self.tableWidgetInbox.horizontalHeader().setCascadingSectionResizes(True)
self.tableWidgetInbox.horizontalHeader().setDefaultSectionSize(200)
self.tableWidgetInbox.horizontalHeader().setHighlightSections(False)
self.tableWidgetInbox.horizontalHeader().setMinimumSectionSize(27)
self.tableWidgetInbox.horizontalHeader().setSortIndicatorShown(False)
self.tableWidgetInbox.horizontalHeader().setStretchLastSection(True)
self.tableWidgetInbox.verticalHeader().setVisible(False)
self.tableWidgetInbox.verticalHeader().setDefaultSectionSize(26)
self.verticalSplitter_7.addWidget(self.tableWidgetInbox)
self.textEditInboxMessage = MessageView(self.inbox)
self.textEditInboxMessage.setBaseSize(QtCore.QSize(0, 500))
self.textEditInboxMessage.setReadOnly(True)
self.textEditInboxMessage.setObjectName(_fromUtf8("textEditInboxMessage"))
self.verticalSplitter_7.addWidget(self.textEditInboxMessage)
self.verticalSplitter_7.setStretchFactor(0, 0)
self.verticalSplitter_7.setStretchFactor(1, 1)
self.verticalSplitter_7.setStretchFactor(2, 2)
self.verticalSplitter_7.setCollapsible(0, False)
self.verticalSplitter_7.setCollapsible(1, False)
self.verticalSplitter_7.setCollapsible(2, False)
self.verticalSplitter_7.handle(1).setEnabled(False)
self.horizontalSplitter_3.addWidget(self.verticalSplitter_7)
self.horizontalSplitter_3.setStretchFactor(0, 0)
self.horizontalSplitter_3.setStretchFactor(1, 1)
self.horizontalSplitter_3.setCollapsible(0, False)
self.horizontalSplitter_3.setCollapsible(1, False)
self.gridLayout.addWidget(self.horizontalSplitter_3)
icon2 = QtGui.QIcon()
icon2.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/inbox.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.tabWidget.addTab(self.inbox, icon2, _fromUtf8(""))
self.send = QtGui.QWidget()
self.send.setObjectName(_fromUtf8("send"))
self.gridLayout_7 = QtGui.QGridLayout(self.send)
self.gridLayout_7.setObjectName(_fromUtf8("gridLayout_7"))
self.horizontalSplitter = settingsmixin.SSplitter()
self.horizontalSplitter.setObjectName(_fromUtf8("horizontalSplitter"))
self.verticalSplitter_2 = settingsmixin.SSplitter()
self.verticalSplitter_2.setObjectName(_fromUtf8("verticalSplitter_2"))
self.verticalSplitter_2.setOrientation(QtCore.Qt.Vertical)
self.tableWidgetAddressBook = settingsmixin.STableWidget(self.send)
self.tableWidgetAddressBook.setAlternatingRowColors(True)
self.tableWidgetAddressBook.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
self.tableWidgetAddressBook.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
self.tableWidgetAddressBook.setObjectName(_fromUtf8("tableWidgetAddressBook"))
self.tableWidgetAddressBook.setColumnCount(2)
self.tableWidgetAddressBook.setRowCount(0)
self.tableWidgetAddressBook.resize(200, self.tableWidgetAddressBook.height())
item = QtGui.QTableWidgetItem()
icon3 = QtGui.QIcon()
icon3.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/addressbook.png")), QtGui.QIcon.Selected, QtGui.QIcon.Off)
item.setIcon(icon3)
self.tableWidgetAddressBook.setHorizontalHeaderItem(0, item)
item = QtGui.QTableWidgetItem()
self.tableWidgetAddressBook.setHorizontalHeaderItem(1, item)
self.tableWidgetAddressBook.horizontalHeader().setCascadingSectionResizes(True)
self.tableWidgetAddressBook.horizontalHeader().setDefaultSectionSize(200)
self.tableWidgetAddressBook.horizontalHeader().setHighlightSections(False)
self.tableWidgetAddressBook.horizontalHeader().setStretchLastSection(True)
self.tableWidgetAddressBook.verticalHeader().setVisible(False)
self.verticalSplitter_2.addWidget(self.tableWidgetAddressBook)
self.addressBookCompleter = AddressBookCompleter()
self.addressBookCompleter.setCompletionMode(QtGui.QCompleter.PopupCompletion)
self.addressBookCompleter.setCaseSensitivity(QtCore.Qt.CaseInsensitive)
self.addressBookCompleterModel = QtGui.QStringListModel()
self.addressBookCompleter.setModel(self.addressBookCompleterModel)
self.pushButtonAddAddressBook = QtGui.QPushButton(self.send)
self.pushButtonAddAddressBook.setObjectName(_fromUtf8("pushButtonAddAddressBook"))
self.pushButtonAddAddressBook.resize(200, self.pushButtonAddAddressBook.height())
self.verticalSplitter_2.addWidget(self.pushButtonAddAddressBook)
self.pushButtonFetchNamecoinID = QtGui.QPushButton(self.send)
self.pushButtonFetchNamecoinID.resize(200, self.pushButtonFetchNamecoinID.height())
self.pushButtonFetchNamecoinID.setObjectName(_fromUtf8("pushButtonFetchNamecoinID"))
self.verticalSplitter_2.addWidget(self.pushButtonFetchNamecoinID)
self.verticalSplitter_2.setStretchFactor(0, 1)
self.verticalSplitter_2.setStretchFactor(1, 0)
self.verticalSplitter_2.setStretchFactor(2, 0)
self.verticalSplitter_2.setCollapsible(0, False)
self.verticalSplitter_2.setCollapsible(1, False)
self.verticalSplitter_2.setCollapsible(2, False)
self.verticalSplitter_2.handle(1).setEnabled(False)
self.verticalSplitter_2.handle(2).setEnabled(False)
self.horizontalSplitter.addWidget(self.verticalSplitter_2)
self.verticalSplitter = settingsmixin.SSplitter()
self.verticalSplitter.setObjectName(_fromUtf8("verticalSplitter"))
self.verticalSplitter.setOrientation(QtCore.Qt.Vertical)
self.tabWidgetSend = QtGui.QTabWidget(self.send)
self.tabWidgetSend.setObjectName(_fromUtf8("tabWidgetSend"))
self.sendDirect = QtGui.QWidget()
self.sendDirect.setObjectName(_fromUtf8("sendDirect"))
self.gridLayout_8 = QtGui.QGridLayout(self.sendDirect)
self.gridLayout_8.setObjectName(_fromUtf8("gridLayout_8"))
self.verticalSplitter_5 = settingsmixin.SSplitter()
self.verticalSplitter_5.setObjectName(_fromUtf8("verticalSplitter_5"))
self.verticalSplitter_5.setOrientation(QtCore.Qt.Vertical)
self.gridLayout_2 = QtGui.QGridLayout()
self.gridLayout_2.setObjectName(_fromUtf8("gridLayout_2"))
self.label_3 = QtGui.QLabel(self.sendDirect)
self.label_3.setObjectName(_fromUtf8("label_3"))
self.gridLayout_2.addWidget(self.label_3, 2, 0, 1, 1)
self.label_2 = QtGui.QLabel(self.sendDirect)
self.label_2.setObjectName(_fromUtf8("label_2"))
self.gridLayout_2.addWidget(self.label_2, 0, 0, 1, 1)
self.lineEditSubject = QtGui.QLineEdit(self.sendDirect)
self.lineEditSubject.setText(_fromUtf8(""))
self.lineEditSubject.setObjectName(_fromUtf8("lineEditSubject"))
self.gridLayout_2.addWidget(self.lineEditSubject, 2, 1, 1, 1)
self.label = QtGui.QLabel(self.sendDirect)
self.label.setObjectName(_fromUtf8("label"))
self.gridLayout_2.addWidget(self.label, 1, 0, 1, 1)
self.comboBoxSendFrom = QtGui.QComboBox(self.sendDirect)
self.comboBoxSendFrom.setMinimumSize(QtCore.QSize(300, 0))
self.comboBoxSendFrom.setObjectName(_fromUtf8("comboBoxSendFrom"))
self.gridLayout_2.addWidget(self.comboBoxSendFrom, 0, 1, 1, 1)
self.lineEditTo = QtGui.QLineEdit(self.sendDirect)
self.lineEditTo.setObjectName(_fromUtf8("lineEditTo"))
self.gridLayout_2.addWidget(self.lineEditTo, 1, 1, 1, 1)
self.lineEditTo.setCompleter(self.addressBookCompleter)
self.gridLayout_2_Widget = QtGui.QWidget()
self.gridLayout_2_Widget.setLayout(self.gridLayout_2)
self.verticalSplitter_5.addWidget(self.gridLayout_2_Widget)
self.textEditMessage = MessageCompose(self.sendDirect)
self.textEditMessage.setObjectName(_fromUtf8("textEditMessage"))
self.verticalSplitter_5.addWidget(self.textEditMessage)
self.verticalSplitter_5.setStretchFactor(0, 0)
self.verticalSplitter_5.setStretchFactor(1, 1)
self.verticalSplitter_5.setCollapsible(0, False)
self.verticalSplitter_5.setCollapsible(1, False)
self.verticalSplitter_5.handle(1).setEnabled(False)
self.gridLayout_8.addWidget(self.verticalSplitter_5, 0, 0, 1, 1)
self.tabWidgetSend.addTab(self.sendDirect, _fromUtf8(""))
self.sendBroadcast = QtGui.QWidget()
self.sendBroadcast.setObjectName(_fromUtf8("sendBroadcast"))
self.gridLayout_9 = QtGui.QGridLayout(self.sendBroadcast)
self.gridLayout_9.setObjectName(_fromUtf8("gridLayout_9"))
self.verticalSplitter_6 = settingsmixin.SSplitter()
self.verticalSplitter_6.setObjectName(_fromUtf8("verticalSplitter_6"))
self.verticalSplitter_6.setOrientation(QtCore.Qt.Vertical)
self.gridLayout_5 = QtGui.QGridLayout()
self.gridLayout_5.setObjectName(_fromUtf8("gridLayout_5"))
self.label_8 = QtGui.QLabel(self.sendBroadcast)
self.label_8.setObjectName(_fromUtf8("label_8"))
self.gridLayout_5.addWidget(self.label_8, 0, 0, 1, 1)
self.lineEditSubjectBroadcast = QtGui.QLineEdit(self.sendBroadcast)
self.lineEditSubjectBroadcast.setText(_fromUtf8(""))
self.lineEditSubjectBroadcast.setObjectName(_fromUtf8("lineEditSubjectBroadcast"))
self.gridLayout_5.addWidget(self.lineEditSubjectBroadcast, 1, 1, 1, 1)
self.label_7 = QtGui.QLabel(self.sendBroadcast)
self.label_7.setObjectName(_fromUtf8("label_7"))
self.gridLayout_5.addWidget(self.label_7, 1, 0, 1, 1)
self.comboBoxSendFromBroadcast = QtGui.QComboBox(self.sendBroadcast)
self.comboBoxSendFromBroadcast.setMinimumSize(QtCore.QSize(300, 0))
self.comboBoxSendFromBroadcast.setObjectName(_fromUtf8("comboBoxSendFromBroadcast"))
self.gridLayout_5.addWidget(self.comboBoxSendFromBroadcast, 0, 1, 1, 1)
self.gridLayout_5_Widget = QtGui.QWidget()
self.gridLayout_5_Widget.setLayout(self.gridLayout_5)
self.verticalSplitter_6.addWidget(self.gridLayout_5_Widget)
self.textEditMessageBroadcast = MessageCompose(self.sendBroadcast)
self.textEditMessageBroadcast.setObjectName(_fromUtf8("textEditMessageBroadcast"))
self.verticalSplitter_6.addWidget(self.textEditMessageBroadcast)
self.verticalSplitter_6.setStretchFactor(0, 0)
self.verticalSplitter_6.setStretchFactor(1, 1)
self.verticalSplitter_6.setCollapsible(0, False)
self.verticalSplitter_6.setCollapsible(1, False)
self.verticalSplitter_6.handle(1).setEnabled(False)
self.gridLayout_9.addWidget(self.verticalSplitter_6, 0, 0, 1, 1)
self.tabWidgetSend.addTab(self.sendBroadcast, _fromUtf8(""))
self.verticalSplitter.addWidget(self.tabWidgetSend)
self.tTLContainer = QtGui.QWidget()
self.tTLContainer.setSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Fixed)
self.horizontalLayout_5 = QtGui.QHBoxLayout()
self.tTLContainer.setLayout(self.horizontalLayout_5)
self.horizontalLayout_5.setObjectName(_fromUtf8("horizontalLayout_5"))
self.pushButtonTTL = QtGui.QPushButton(self.send)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.pushButtonTTL.sizePolicy().hasHeightForWidth())
self.pushButtonTTL.setSizePolicy(sizePolicy)
palette = QtGui.QPalette()
brush = QtGui.QBrush(QtGui.QColor(0, 0, 255))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.ButtonText, brush)
brush = QtGui.QBrush(QtGui.QColor(0, 0, 255))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.ButtonText, brush)
brush = QtGui.QBrush(QtGui.QColor(120, 120, 120))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.ButtonText, brush)
self.pushButtonTTL.setPalette(palette)
font = QtGui.QFont()
font.setUnderline(True)
self.pushButtonTTL.setFont(font)
self.pushButtonTTL.setFlat(True)
self.pushButtonTTL.setObjectName(_fromUtf8("pushButtonTTL"))
self.horizontalLayout_5.addWidget(self.pushButtonTTL, 0, QtCore.Qt.AlignRight)
self.horizontalSliderTTL = QtGui.QSlider(self.send)
self.horizontalSliderTTL.setMinimumSize(QtCore.QSize(70, 0))
self.horizontalSliderTTL.setOrientation(QtCore.Qt.Horizontal)
self.horizontalSliderTTL.setInvertedAppearance(False)
self.horizontalSliderTTL.setInvertedControls(False)
self.horizontalSliderTTL.setObjectName(_fromUtf8("horizontalSliderTTL"))
self.horizontalLayout_5.addWidget(self.horizontalSliderTTL, 0, QtCore.Qt.AlignLeft)
self.labelHumanFriendlyTTLDescription = QtGui.QLabel(self.send)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.labelHumanFriendlyTTLDescription.sizePolicy().hasHeightForWidth())
self.labelHumanFriendlyTTLDescription.setSizePolicy(sizePolicy)
self.labelHumanFriendlyTTLDescription.setMinimumSize(QtCore.QSize(45, 0))
self.labelHumanFriendlyTTLDescription.setObjectName(_fromUtf8("labelHumanFriendlyTTLDescription"))
self.horizontalLayout_5.addWidget(self.labelHumanFriendlyTTLDescription, 1, QtCore.Qt.AlignLeft)
self.pushButtonClear = QtGui.QPushButton(self.send)
self.pushButtonClear.setObjectName(_fromUtf8("pushButtonClear"))
self.horizontalLayout_5.addWidget(self.pushButtonClear, 0, QtCore.Qt.AlignRight)
self.pushButtonSend = QtGui.QPushButton(self.send)
self.pushButtonSend.setObjectName(_fromUtf8("pushButtonSend"))
self.horizontalLayout_5.addWidget(self.pushButtonSend, 0, QtCore.Qt.AlignRight)
self.horizontalSliderTTL.setMaximumSize(QtCore.QSize(105, self.pushButtonSend.height()))
self.verticalSplitter.addWidget(self.tTLContainer)
self.tTLContainer.adjustSize()
self.verticalSplitter.setStretchFactor(1, 0)
self.verticalSplitter.setStretchFactor(0, 1)
self.verticalSplitter.setCollapsible(0, False)
self.verticalSplitter.setCollapsible(1, False)
self.verticalSplitter.handle(1).setEnabled(False)
self.horizontalSplitter.addWidget(self.verticalSplitter)
self.horizontalSplitter.setStretchFactor(0, 0)
self.horizontalSplitter.setStretchFactor(1, 1)
self.horizontalSplitter.setCollapsible(0, False)
self.horizontalSplitter.setCollapsible(1, False)
self.gridLayout_7.addWidget(self.horizontalSplitter, 0, 0, 1, 1)
icon4 = QtGui.QIcon()
icon4.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/send.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.tabWidget.addTab(self.send, icon4, _fromUtf8(""))
self.subscriptions = QtGui.QWidget()
self.subscriptions.setObjectName(_fromUtf8("subscriptions"))
self.gridLayout_3 = QtGui.QGridLayout(self.subscriptions)
self.gridLayout_3.setObjectName(_fromUtf8("gridLayout_3"))
self.horizontalSplitter_4 = settingsmixin.SSplitter()
self.horizontalSplitter_4.setObjectName(_fromUtf8("horizontalSplitter_4"))
self.verticalSplitter_3 = settingsmixin.SSplitter()
self.verticalSplitter_3.setObjectName(_fromUtf8("verticalSplitter_3"))
self.verticalSplitter_3.setOrientation(QtCore.Qt.Vertical)
self.treeWidgetSubscriptions = settingsmixin.STreeWidget(self.subscriptions)
self.treeWidgetSubscriptions.setAlternatingRowColors(True)
self.treeWidgetSubscriptions.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
self.treeWidgetSubscriptions.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
self.treeWidgetSubscriptions.setObjectName(_fromUtf8("treeWidgetSubscriptions"))
self.treeWidgetSubscriptions.resize(200, self.treeWidgetSubscriptions.height())
icon5 = QtGui.QIcon()
icon5.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/subscriptions.png")), QtGui.QIcon.Selected, QtGui.QIcon.Off)
self.treeWidgetSubscriptions.headerItem().setIcon(0, icon5)
self.verticalSplitter_3.addWidget(self.treeWidgetSubscriptions)
self.pushButtonAddSubscription = QtGui.QPushButton(self.subscriptions)
self.pushButtonAddSubscription.setObjectName(_fromUtf8("pushButtonAddSubscription"))
self.pushButtonAddSubscription.resize(200, self.pushButtonAddSubscription.height())
self.verticalSplitter_3.addWidget(self.pushButtonAddSubscription)
self.verticalSplitter_3.setStretchFactor(0, 1)
self.verticalSplitter_3.setStretchFactor(1, 0)
self.verticalSplitter_3.setCollapsible(0, False)
self.verticalSplitter_3.setCollapsible(1, False)
self.verticalSplitter_3.handle(1).setEnabled(False)
self.horizontalSplitter_4.addWidget(self.verticalSplitter_3)
self.verticalSplitter_4 = settingsmixin.SSplitter()
self.verticalSplitter_4.setObjectName(_fromUtf8("verticalSplitter_4"))
self.verticalSplitter_4.setOrientation(QtCore.Qt.Vertical)
self.horizontalSplitter_2 = QtGui.QSplitter()
self.horizontalSplitter_2.setObjectName(_fromUtf8("horizontalSplitter_2"))
self.inboxSearchLineEditSubscriptions = QtGui.QLineEdit(self.subscriptions)
self.inboxSearchLineEditSubscriptions.setObjectName(_fromUtf8("inboxSearchLineEditSubscriptions"))
self.horizontalSplitter_2.addWidget(self.inboxSearchLineEditSubscriptions)
self.inboxSearchOptionSubscriptions = QtGui.QComboBox(self.subscriptions)
self.inboxSearchOptionSubscriptions.setObjectName(_fromUtf8("inboxSearchOptionSubscriptions"))
self.inboxSearchOptionSubscriptions.addItem(_fromUtf8(""))
self.inboxSearchOptionSubscriptions.addItem(_fromUtf8(""))
self.inboxSearchOptionSubscriptions.addItem(_fromUtf8(""))
self.inboxSearchOptionSubscriptions.addItem(_fromUtf8(""))
self.inboxSearchOptionSubscriptions.addItem(_fromUtf8(""))
self.inboxSearchOptionSubscriptions.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToContents)
self.horizontalSplitter_2.addWidget(self.inboxSearchOptionSubscriptions)
self.horizontalSplitter_2.handle(1).setEnabled(False)
self.horizontalSplitter_2.setStretchFactor(0, 1)
self.horizontalSplitter_2.setStretchFactor(1, 0)
self.verticalSplitter_4.addWidget(self.horizontalSplitter_2)
self.tableWidgetInboxSubscriptions = settingsmixin.STableWidget(self.subscriptions)
self.tableWidgetInboxSubscriptions.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
self.tableWidgetInboxSubscriptions.setAlternatingRowColors(True)
self.tableWidgetInboxSubscriptions.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
self.tableWidgetInboxSubscriptions.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
self.tableWidgetInboxSubscriptions.setWordWrap(False)
self.tableWidgetInboxSubscriptions.setObjectName(_fromUtf8("tableWidgetInboxSubscriptions"))
self.tableWidgetInboxSubscriptions.setColumnCount(4)
self.tableWidgetInboxSubscriptions.setRowCount(0)
item = QtGui.QTableWidgetItem()
self.tableWidgetInboxSubscriptions.setHorizontalHeaderItem(0, item)
item = QtGui.QTableWidgetItem()
self.tableWidgetInboxSubscriptions.setHorizontalHeaderItem(1, item)
item = QtGui.QTableWidgetItem()
self.tableWidgetInboxSubscriptions.setHorizontalHeaderItem(2, item)
item = QtGui.QTableWidgetItem()
self.tableWidgetInboxSubscriptions.setHorizontalHeaderItem(3, item)
self.tableWidgetInboxSubscriptions.horizontalHeader().setCascadingSectionResizes(True)
self.tableWidgetInboxSubscriptions.horizontalHeader().setDefaultSectionSize(200)
self.tableWidgetInboxSubscriptions.horizontalHeader().setHighlightSections(False)
self.tableWidgetInboxSubscriptions.horizontalHeader().setMinimumSectionSize(27)
self.tableWidgetInboxSubscriptions.horizontalHeader().setSortIndicatorShown(False)
self.tableWidgetInboxSubscriptions.horizontalHeader().setStretchLastSection(True)
self.tableWidgetInboxSubscriptions.verticalHeader().setVisible(False)
self.tableWidgetInboxSubscriptions.verticalHeader().setDefaultSectionSize(26)
self.verticalSplitter_4.addWidget(self.tableWidgetInboxSubscriptions)
self.textEditInboxMessageSubscriptions = MessageView(self.subscriptions)
self.textEditInboxMessageSubscriptions.setBaseSize(QtCore.QSize(0, 500))
self.textEditInboxMessageSubscriptions.setReadOnly(True)
self.textEditInboxMessageSubscriptions.setObjectName(_fromUtf8("textEditInboxMessageSubscriptions"))
self.verticalSplitter_4.addWidget(self.textEditInboxMessageSubscriptions)
self.verticalSplitter_4.setStretchFactor(0, 0)
self.verticalSplitter_4.setStretchFactor(1, 1)
self.verticalSplitter_4.setStretchFactor(2, 2)
self.verticalSplitter_4.setCollapsible(0, False)
self.verticalSplitter_4.setCollapsible(1, False)
self.verticalSplitter_4.setCollapsible(2, False)
self.verticalSplitter_4.handle(1).setEnabled(False)
self.horizontalSplitter_4.addWidget(self.verticalSplitter_4)
self.horizontalSplitter_4.setStretchFactor(0, 0)
self.horizontalSplitter_4.setStretchFactor(1, 1)
self.horizontalSplitter_4.setCollapsible(0, False)
self.horizontalSplitter_4.setCollapsible(1, False)
self.gridLayout_3.addWidget(self.horizontalSplitter_4, 0, 0, 1, 1)
icon6 = QtGui.QIcon()
icon6.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/subscriptions.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.tabWidget.addTab(self.subscriptions, icon6, _fromUtf8(""))
self.chans = QtGui.QWidget()
self.chans.setObjectName(_fromUtf8("chans"))
self.gridLayout_4 = QtGui.QGridLayout(self.chans)
self.gridLayout_4.setObjectName(_fromUtf8("gridLayout_4"))
self.horizontalSplitter_7 = settingsmixin.SSplitter()
self.horizontalSplitter_7.setObjectName(_fromUtf8("horizontalSplitter_7"))
self.verticalSplitter_17 = settingsmixin.SSplitter()
self.verticalSplitter_17.setObjectName(_fromUtf8("verticalSplitter_17"))
self.verticalSplitter_17.setOrientation(QtCore.Qt.Vertical)
self.treeWidgetChans = settingsmixin.STreeWidget(self.chans)
self.treeWidgetChans.setFrameShadow(QtGui.QFrame.Sunken)
self.treeWidgetChans.setLineWidth(1)
self.treeWidgetChans.setAlternatingRowColors(True)
self.treeWidgetChans.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
self.treeWidgetChans.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
self.treeWidgetChans.setObjectName(_fromUtf8("treeWidgetChans"))
self.treeWidgetChans.resize(200, self.treeWidgetChans.height())
icon7 = QtGui.QIcon()
icon7.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/can-icon-16px.png")), QtGui.QIcon.Selected, QtGui.QIcon.Off)
self.treeWidgetChans.headerItem().setIcon(0, icon7)
self.verticalSplitter_17.addWidget(self.treeWidgetChans)
self.pushButtonAddChan = QtGui.QPushButton(self.chans)
self.pushButtonAddChan.setObjectName(_fromUtf8("pushButtonAddChan"))
self.pushButtonAddChan.resize(200, self.pushButtonAddChan.height())
self.verticalSplitter_17.addWidget(self.pushButtonAddChan)
self.verticalSplitter_17.setStretchFactor(0, 1)
self.verticalSplitter_17.setStretchFactor(1, 0)
self.verticalSplitter_17.setCollapsible(0, False)
self.verticalSplitter_17.setCollapsible(1, False)
self.verticalSplitter_17.handle(1).setEnabled(False)
self.horizontalSplitter_7.addWidget(self.verticalSplitter_17)
self.verticalSplitter_8 = settingsmixin.SSplitter()
self.verticalSplitter_8.setObjectName(_fromUtf8("verticalSplitter_8"))
self.verticalSplitter_8.setOrientation(QtCore.Qt.Vertical)
self.horizontalSplitter_6 = QtGui.QSplitter()
self.horizontalSplitter_6.setObjectName(_fromUtf8("horizontalSplitter_6"))
self.inboxSearchLineEditChans = QtGui.QLineEdit(self.chans)
self.inboxSearchLineEditChans.setObjectName(_fromUtf8("inboxSearchLineEditChans"))
self.horizontalSplitter_6.addWidget(self.inboxSearchLineEditChans)
self.inboxSearchOptionChans = QtGui.QComboBox(self.chans)
self.inboxSearchOptionChans.setObjectName(_fromUtf8("inboxSearchOptionChans"))
self.inboxSearchOptionChans.addItem(_fromUtf8(""))
self.inboxSearchOptionChans.addItem(_fromUtf8(""))
self.inboxSearchOptionChans.addItem(_fromUtf8(""))
self.inboxSearchOptionChans.addItem(_fromUtf8(""))
self.inboxSearchOptionChans.addItem(_fromUtf8(""))
self.inboxSearchOptionChans.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToContents)
self.horizontalSplitter_6.addWidget(self.inboxSearchOptionChans)
self.horizontalSplitter_6.handle(1).setEnabled(False)
self.horizontalSplitter_6.setStretchFactor(0, 1)
self.horizontalSplitter_6.setStretchFactor(1, 0)
self.verticalSplitter_8.addWidget(self.horizontalSplitter_6)
self.tableWidgetInboxChans = settingsmixin.STableWidget(self.chans)
self.tableWidgetInboxChans.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
self.tableWidgetInboxChans.setAlternatingRowColors(True)
self.tableWidgetInboxChans.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
self.tableWidgetInboxChans.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
self.tableWidgetInboxChans.setWordWrap(False)
self.tableWidgetInboxChans.setObjectName(_fromUtf8("tableWidgetInboxChans"))
self.tableWidgetInboxChans.setColumnCount(4)
self.tableWidgetInboxChans.setRowCount(0)
item = QtGui.QTableWidgetItem()
self.tableWidgetInboxChans.setHorizontalHeaderItem(0, item)
item = QtGui.QTableWidgetItem()
self.tableWidgetInboxChans.setHorizontalHeaderItem(1, item)
item = QtGui.QTableWidgetItem()
self.tableWidgetInboxChans.setHorizontalHeaderItem(2, item)
item = QtGui.QTableWidgetItem()
self.tableWidgetInboxChans.setHorizontalHeaderItem(3, item)
self.tableWidgetInboxChans.horizontalHeader().setCascadingSectionResizes(True)
self.tableWidgetInboxChans.horizontalHeader().setDefaultSectionSize(200)
self.tableWidgetInboxChans.horizontalHeader().setHighlightSections(False)
self.tableWidgetInboxChans.horizontalHeader().setMinimumSectionSize(27)
self.tableWidgetInboxChans.horizontalHeader().setSortIndicatorShown(False)
self.tableWidgetInboxChans.horizontalHeader().setStretchLastSection(True)
self.tableWidgetInboxChans.verticalHeader().setVisible(False)
self.tableWidgetInboxChans.verticalHeader().setDefaultSectionSize(26)
self.verticalSplitter_8.addWidget(self.tableWidgetInboxChans)
self.textEditInboxMessageChans = MessageView(self.chans)
self.textEditInboxMessageChans.setBaseSize(QtCore.QSize(0, 500))
self.textEditInboxMessageChans.setReadOnly(True)
self.textEditInboxMessageChans.setObjectName(_fromUtf8("textEditInboxMessageChans"))
self.verticalSplitter_8.addWidget(self.textEditInboxMessageChans)
self.verticalSplitter_8.setStretchFactor(0, 0)
self.verticalSplitter_8.setStretchFactor(1, 1)
self.verticalSplitter_8.setStretchFactor(2, 2)
self.verticalSplitter_8.setCollapsible(0, False)
self.verticalSplitter_8.setCollapsible(1, False)
self.verticalSplitter_8.setCollapsible(2, False)
self.verticalSplitter_8.handle(1).setEnabled(False)
self.horizontalSplitter_7.addWidget(self.verticalSplitter_8)
self.horizontalSplitter_7.setStretchFactor(0, 0)
self.horizontalSplitter_7.setStretchFactor(1, 1)
self.horizontalSplitter_7.setCollapsible(0, False)
self.horizontalSplitter_7.setCollapsible(1, False)
self.gridLayout_4.addWidget(self.horizontalSplitter_7, 0, 0, 1, 1)
icon8 = QtGui.QIcon()
icon8.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/can-icon-16px.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.tabWidget.addTab(self.chans, icon8, _fromUtf8(""))
self.blackwhitelist = Blacklist()
self.tabWidget.addTab(self.blackwhitelist, QtGui.QIcon(":/newPrefix/images/blacklist.png"), "")
# Initialize the Blacklist or Whitelist
if BMConfigParser().get('bitmessagesettings', 'blackwhitelist') == 'white':
self.blackwhitelist.radioButtonWhitelist.click()
self.blackwhitelist.rerenderBlackWhiteList()
self.networkstatus = NetworkStatus()
self.tabWidget.addTab(self.networkstatus, QtGui.QIcon(":/newPrefix/images/networkstatus.png"), "")
self.gridLayout_10.addWidget(self.tabWidget, 0, 0, 1, 1)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtGui.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 885, 27))
self.menubar.setObjectName(_fromUtf8("menubar"))
self.menuFile = QtGui.QMenu(self.menubar)
self.menuFile.setObjectName(_fromUtf8("menuFile"))
self.menuSettings = QtGui.QMenu(self.menubar)
self.menuSettings.setObjectName(_fromUtf8("menuSettings"))
self.menuHelp = QtGui.QMenu(self.menubar)
self.menuHelp.setObjectName(_fromUtf8("menuHelp"))
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtGui.QStatusBar(MainWindow)
self.statusbar.setMaximumSize(QtCore.QSize(16777215, 22))
self.statusbar.setObjectName(_fromUtf8("statusbar"))
MainWindow.setStatusBar(self.statusbar)
self.actionImport_keys = QtGui.QAction(MainWindow)
self.actionImport_keys.setObjectName(_fromUtf8("actionImport_keys"))
self.actionManageKeys = QtGui.QAction(MainWindow)
self.actionManageKeys.setCheckable(False)
self.actionManageKeys.setEnabled(True)
icon = QtGui.QIcon.fromTheme(_fromUtf8("dialog-password"))
self.actionManageKeys.setIcon(icon)
self.actionManageKeys.setObjectName(_fromUtf8("actionManageKeys"))
self.actionNetworkSwitch = QtGui.QAction(MainWindow)
self.actionNetworkSwitch.setObjectName(_fromUtf8("actionNetworkSwitch"))
self.actionExit = QtGui.QAction(MainWindow)
icon = QtGui.QIcon.fromTheme(_fromUtf8("application-exit"))
self.actionExit.setIcon(icon)
self.actionExit.setObjectName(_fromUtf8("actionExit"))
self.actionHelp = QtGui.QAction(MainWindow)
icon = QtGui.QIcon.fromTheme(_fromUtf8("help-contents"))
self.actionHelp.setIcon(icon)
self.actionHelp.setObjectName(_fromUtf8("actionHelp"))
self.actionSupport = QtGui.QAction(MainWindow)
icon = QtGui.QIcon.fromTheme(_fromUtf8("help-support"))
self.actionSupport.setIcon(icon)
self.actionSupport.setObjectName(_fromUtf8("actionSupport"))
self.actionAbout = QtGui.QAction(MainWindow)
icon = QtGui.QIcon.fromTheme(_fromUtf8("help-about"))
self.actionAbout.setIcon(icon)
self.actionAbout.setObjectName(_fromUtf8("actionAbout"))
self.actionSettings = QtGui.QAction(MainWindow)
icon = QtGui.QIcon.fromTheme(_fromUtf8("document-properties"))
self.actionSettings.setIcon(icon)
self.actionSettings.setObjectName(_fromUtf8("actionSettings"))
self.actionRegenerateDeterministicAddresses = QtGui.QAction(MainWindow)
icon = QtGui.QIcon.fromTheme(_fromUtf8("view-refresh"))
self.actionRegenerateDeterministicAddresses.setIcon(icon)
self.actionRegenerateDeterministicAddresses.setObjectName(_fromUtf8("actionRegenerateDeterministicAddresses"))
self.actionDeleteAllTrashedMessages = QtGui.QAction(MainWindow)
icon = QtGui.QIcon.fromTheme(_fromUtf8("user-trash"))
self.actionDeleteAllTrashedMessages.setIcon(icon)
self.actionDeleteAllTrashedMessages.setObjectName(_fromUtf8("actionDeleteAllTrashedMessages"))
self.actionJoinChan = QtGui.QAction(MainWindow)
icon = QtGui.QIcon.fromTheme(_fromUtf8("contact-new"))
self.actionJoinChan.setIcon(icon)
self.actionJoinChan.setObjectName(_fromUtf8("actionJoinChan"))
self.menuFile.addAction(self.actionManageKeys)
self.menuFile.addAction(self.actionDeleteAllTrashedMessages)
self.menuFile.addAction(self.actionRegenerateDeterministicAddresses)
self.menuFile.addAction(self.actionNetworkSwitch)
self.menuFile.addAction(self.actionExit)
self.menuSettings.addAction(self.actionSettings)
self.menuHelp.addAction(self.actionHelp)
self.menuHelp.addAction(self.actionSupport)
self.menuHelp.addAction(self.actionAbout)
self.menubar.addAction(self.menuFile.menuAction())
self.menubar.addAction(self.menuSettings.menuAction())
self.menubar.addAction(self.menuHelp.menuAction())
self.retranslateUi(MainWindow)
self.tabWidget.setCurrentIndex(
self.tabWidget.indexOf(self.inbox)
)
self.tabWidgetSend.setCurrentIndex(
self.tabWidgetSend.indexOf(self.sendDirect)
)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
MainWindow.setTabOrder(self.tableWidgetInbox, self.textEditInboxMessage)
MainWindow.setTabOrder(self.textEditInboxMessage, self.comboBoxSendFrom)
MainWindow.setTabOrder(self.comboBoxSendFrom, self.lineEditTo)
MainWindow.setTabOrder(self.lineEditTo, self.lineEditSubject)
MainWindow.setTabOrder(self.lineEditSubject, self.textEditMessage)
MainWindow.setTabOrder(self.textEditMessage, self.pushButtonAddSubscription)
def updateNetworkSwitchMenuLabel(self, dontconnect=None):
if dontconnect is None:
dontconnect = BMConfigParser().safeGetBoolean(
'bitmessagesettings', 'dontconnect')
self.actionNetworkSwitch.setText(
_translate("MainWindow", "Go online", None)
if dontconnect else
_translate("MainWindow", "Go offline", None)
)
def retranslateUi(self, MainWindow):
MainWindow.setWindowTitle(_translate("MainWindow", "Bitmessage", None))
self.treeWidgetYourIdentities.headerItem().setText(0, _translate("MainWindow", "Identities", None))
self.pushButtonNewAddress.setText(_translate("MainWindow", "New Identity", None))
self.inboxSearchLineEdit.setPlaceholderText(_translate("MainWindow", "Search", None))
self.inboxSearchOption.setItemText(0, _translate("MainWindow", "All", None))
self.inboxSearchOption.setItemText(1, _translate("MainWindow", "To", None))
self.inboxSearchOption.setItemText(2, _translate("MainWindow", "From", None))
self.inboxSearchOption.setItemText(3, _translate("MainWindow", "Subject", None))
self.inboxSearchOption.setItemText(4, _translate("MainWindow", "Message", None))
self.tableWidgetInbox.setSortingEnabled(True)
item = self.tableWidgetInbox.horizontalHeaderItem(0)
item.setText(_translate("MainWindow", "To", None))
item = self.tableWidgetInbox.horizontalHeaderItem(1)
item.setText(_translate("MainWindow", "From", None))
item = self.tableWidgetInbox.horizontalHeaderItem(2)
item.setText(_translate("MainWindow", "Subject", None))
item = self.tableWidgetInbox.horizontalHeaderItem(3)
item.setText(_translate("MainWindow", "Received", None))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.inbox), _translate("MainWindow", "Messages", None))
self.tableWidgetAddressBook.setSortingEnabled(True)
item = self.tableWidgetAddressBook.horizontalHeaderItem(0)
item.setText(_translate("MainWindow", "Address book", None))
item = self.tableWidgetAddressBook.horizontalHeaderItem(1)
item.setText(_translate("MainWindow", "Address", None))
self.pushButtonAddAddressBook.setText(_translate("MainWindow", "Add Contact", None))
self.pushButtonFetchNamecoinID.setText(_translate("MainWindow", "Fetch Namecoin ID", None))
self.label_3.setText(_translate("MainWindow", "Subject:", None))
self.label_2.setText(_translate("MainWindow", "From:", None))
self.label.setText(_translate("MainWindow", "To:", None))
#self.textEditMessage.setHtml("")
self.tabWidgetSend.setTabText(self.tabWidgetSend.indexOf(self.sendDirect), _translate("MainWindow", "Send ordinary Message", None))
self.label_8.setText(_translate("MainWindow", "From:", None))
self.label_7.setText(_translate("MainWindow", "Subject:", None))
#self.textEditMessageBroadcast.setHtml("")
self.tabWidgetSend.setTabText(self.tabWidgetSend.indexOf(self.sendBroadcast), _translate("MainWindow", "Send Message to your Subscribers", None))
self.pushButtonTTL.setText(_translate("MainWindow", "TTL:", None))
hours = 48
try:
hours = int(BMConfigParser().getint('bitmessagesettings', 'ttl')/60/60)
except:
pass
self.labelHumanFriendlyTTLDescription.setText(_translate("MainWindow", "%n hour(s)", None, QtCore.QCoreApplication.CodecForTr, hours))
self.pushButtonClear.setText(_translate("MainWindow", "Clear", None))
self.pushButtonSend.setText(_translate("MainWindow", "Send", None))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.send), _translate("MainWindow", "Send", None))
self.treeWidgetSubscriptions.headerItem().setText(0, _translate("MainWindow", "Subscriptions", None))
self.pushButtonAddSubscription.setText(_translate("MainWindow", "Add new Subscription", None))
self.inboxSearchLineEditSubscriptions.setPlaceholderText(_translate("MainWindow", "Search", None))
self.inboxSearchOptionSubscriptions.setItemText(0, _translate("MainWindow", "All", None))
self.inboxSearchOptionSubscriptions.setItemText(1, _translate("MainWindow", "To", None))
self.inboxSearchOptionSubscriptions.setItemText(2, _translate("MainWindow", "From", None))
self.inboxSearchOptionSubscriptions.setItemText(3, _translate("MainWindow", "Subject", None))
self.inboxSearchOptionSubscriptions.setItemText(4, _translate("MainWindow", "Message", None))
self.tableWidgetInboxSubscriptions.setSortingEnabled(True)
item = self.tableWidgetInboxSubscriptions.horizontalHeaderItem(0)
item.setText(_translate("MainWindow", "To", None))
item = self.tableWidgetInboxSubscriptions.horizontalHeaderItem(1)
item.setText(_translate("MainWindow", "From", None))
item = self.tableWidgetInboxSubscriptions.horizontalHeaderItem(2)
item.setText(_translate("MainWindow", "Subject", None))
item = self.tableWidgetInboxSubscriptions.horizontalHeaderItem(3)
item.setText(_translate("MainWindow", "Received", None))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.subscriptions), _translate("MainWindow", "Subscriptions", None))
self.treeWidgetChans.headerItem().setText(0, _translate("MainWindow", "Chans", None))
self.pushButtonAddChan.setText(_translate("MainWindow", "Add Chan", None))
self.inboxSearchLineEditChans.setPlaceholderText(_translate("MainWindow", "Search", None))
self.inboxSearchOptionChans.setItemText(0, _translate("MainWindow", "All", None))
self.inboxSearchOptionChans.setItemText(1, _translate("MainWindow", "To", None))
self.inboxSearchOptionChans.setItemText(2, _translate("MainWindow", "From", None))
self.inboxSearchOptionChans.setItemText(3, _translate("MainWindow", "Subject", None))
self.inboxSearchOptionChans.setItemText(4, _translate("MainWindow", "Message", None))
self.tableWidgetInboxChans.setSortingEnabled(True)
item = self.tableWidgetInboxChans.horizontalHeaderItem(0)
item.setText(_translate("MainWindow", "To", None))
item = self.tableWidgetInboxChans.horizontalHeaderItem(1)
item.setText(_translate("MainWindow", "From", None))
item = self.tableWidgetInboxChans.horizontalHeaderItem(2)
item.setText(_translate("MainWindow", "Subject", None))
item = self.tableWidgetInboxChans.horizontalHeaderItem(3)
item.setText(_translate("MainWindow", "Received", None))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.chans), _translate("MainWindow", "Chans", None))
self.blackwhitelist.retranslateUi()
self.tabWidget.setTabText(self.tabWidget.indexOf(self.blackwhitelist), _translate("blacklist", "Blacklist", None))
self.networkstatus.retranslateUi()
self.tabWidget.setTabText(self.tabWidget.indexOf(self.networkstatus), _translate("networkstatus", "Network Status", None))
self.menuFile.setTitle(_translate("MainWindow", "File", None))
self.menuSettings.setTitle(_translate("MainWindow", "Settings", None))
self.menuHelp.setTitle(_translate("MainWindow", "Help", None))
self.actionImport_keys.setText(_translate("MainWindow", "Import keys", None))
self.actionManageKeys.setText(_translate("MainWindow", "Manage keys", None))
self.actionExit.setText(_translate("MainWindow", "Quit", None))
self.actionExit.setShortcut(_translate("MainWindow", "Ctrl+Q", None))
self.actionHelp.setText(_translate("MainWindow", "Help", None))
self.actionHelp.setShortcut(_translate("MainWindow", "F1", None))
self.actionSupport.setText(_translate("MainWindow", "Contact support", None))
self.actionAbout.setText(_translate("MainWindow", "About", None))
self.actionSettings.setText(_translate("MainWindow", "Settings", None))
self.actionRegenerateDeterministicAddresses.setText(_translate("MainWindow", "Regenerate deterministic addresses", None))
self.actionDeleteAllTrashedMessages.setText(_translate("MainWindow", "Delete all trashed messages", None))
self.actionJoinChan.setText(_translate("MainWindow", "Join / Create chan", None))
import bitmessage_icons_rc
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
MainWindow = settingsmixin.SMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())

View File

@ -14,7 +14,7 @@
<string>Bitmessage</string>
</property>
<property name="windowIcon">
<iconset resource="bitmessage_icons.qrc">
<iconset>
<normaloff>:/newPrefix/images/can-icon-24px.png</normaloff>:/newPrefix/images/can-icon-24px.png</iconset>
</property>
<property name="tabShape">
@ -22,6 +22,42 @@
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout_10">
<item row="1" column="0">
<widget class="QWidget" name="rightStatusBar" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="workProverSpeed">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButtonStatusIcon">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>:/newPrefix/images/redicon.png</normaloff>:/newPrefix/images/redicon.png</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="0" column="0">
<widget class="QTabWidget" name="tabWidget">
<property name="sizePolicy">
@ -58,7 +94,7 @@
</property>
<widget class="QWidget" name="inbox">
<attribute name="icon">
<iconset resource="bitmessage_icons.qrc">
<iconset>
<normaloff>:/newPrefix/images/inbox.png</normaloff>:/newPrefix/images/inbox.png</iconset>
</attribute>
<attribute name="title">
@ -66,177 +102,198 @@
</attribute>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<layout class="QVBoxLayout" name="verticalLayout_12">
<item>
<widget class="QTreeWidget" name="treeWidgetYourIdentities">
<property name="maximumSize">
<size>
<width>200</width>
<height>16777215</height>
</size>
</property>
<column>
<property name="text">
<string>Identities</string>
</property>
<property name="icon">
<iconset>
<selectedoff>:/newPrefix/images/identities.png</selectedoff>
</iconset>
</property>
</column>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButtonNewAddress">
<property name="maximumSize">
<size>
<width>200</width>
<height>16777215</height>
</size>
</property>
<widget class="SSplitter" name="horizontalSplitter_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="childrenCollapsible">
<bool>false</bool>
</property>
<widget class="SSplitter" name="verticalSplitter_12">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="childrenCollapsible">
<bool>false</bool>
</property>
<widget class="STreeWidget" name="treeWidgetYourIdentities">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<column>
<property name="text">
<string>Identities</string>
</property>
<property name="icon">
<iconset>
<selectedoff>:/newPrefix/images/identities.png</selectedoff>
</iconset>
</property>
</column>
</widget>
<widget class="QPushButton" name="pushButtonNewAddress">
<property name="text">
<string>New Identity</string>
</property>
</widget>
</widget>
<widget class="SSplitter" name="verticalSplitter_7">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="childrenCollapsible">
<bool>false</bool>
</property>
<widget class="QSplitter" name="horizontalSplitterSearch">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QLineEdit" name="inboxSearchLineEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="placeholderText">
<string>Search</string>
</property>
</widget>
<widget class="QComboBox" name="inboxSearchOption">
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
<item>
<property name="text">
<string>New Indentitiy</string>
<string>All</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_7">
<item>
<layout class="QHBoxLayout" name="horizontalLayoutSearch">
<property name="topMargin">
<number>0</number>
</item>
<item>
<property name="text">
<string>To</string>
</property>
<item>
<widget class="QLineEdit" name="inboxSearchLineEdit">
<property name="placeholderText">
<string>Search</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="inboxSearchOption">
<item>
<property name="text">
<string>All</string>
</property>
</item>
<item>
<property name="text">
<string>To</string>
</property>
</item>
<item>
<property name="text">
<string>From</string>
</property>
</item>
<item>
<property name="text">
<string>Subject</string>
</property>
</item>
<item>
<property name="text">
<string>Message</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTableWidget" name="tableWidgetInbox">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</item>
<item>
<property name="text">
<string>From</string>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</item>
<item>
<property name="text">
<string>Subject</string>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</item>
<item>
<property name="text">
<string>Message</string>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
<attribute name="horizontalHeaderCascadingSectionResizes">
<bool>true</bool>
</attribute>
<attribute name="horizontalHeaderDefaultSectionSize">
<number>200</number>
</attribute>
<attribute name="horizontalHeaderHighlightSections">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderMinimumSectionSize">
<number>27</number>
</attribute>
<attribute name="horizontalHeaderShowSortIndicator" stdset="0">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderDefaultSectionSize">
<number>26</number>
</attribute>
<column>
<property name="text">
<string>To</string>
</property>
</column>
<column>
<property name="text">
<string>From</string>
</property>
</column>
<column>
<property name="text">
<string>Subject</string>
</property>
</column>
<column>
<property name="text">
<string>Received</string>
</property>
</column>
</widget>
</item>
<item>
<widget class="QTextEdit" name="textEditInboxMessage">
<property name="baseSize">
<size>
<width>0</width>
<height>500</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</widget>
</widget>
<widget class="STableWidget" name="tableWidgetInbox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
<attribute name="horizontalHeaderCascadingSectionResizes">
<bool>true</bool>
</attribute>
<attribute name="horizontalHeaderDefaultSectionSize">
<number>200</number>
</attribute>
<attribute name="horizontalHeaderHighlightSections">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderMinimumSectionSize">
<number>27</number>
</attribute>
<attribute name="horizontalHeaderShowSortIndicator" stdset="0">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderDefaultSectionSize">
<number>26</number>
</attribute>
<column>
<property name="text">
<string>To</string>
</property>
</column>
<column>
<property name="text">
<string>From</string>
</property>
</column>
<column>
<property name="text">
<string>Subject</string>
</property>
</column>
<column>
<property name="text">
<string>Received</string>
</property>
</column>
</widget>
<widget class="MessageView" name="textEditInboxMessage">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>2</verstretch>
</sizepolicy>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>500</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="send">
<attribute name="icon">
<iconset resource="bitmessage_icons.qrc">
<iconset>
<normaloff>:/newPrefix/images/send.png</normaloff>:/newPrefix/images/send.png</iconset>
</attribute>
<attribute name="title">
@ -244,368 +301,363 @@
</attribute>
<layout class="QGridLayout" name="gridLayout_7">
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QTableWidget" name="tableWidgetAddressBook">
<property name="maximumSize">
<size>
<width>200</width>
<height>16777215</height>
</size>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<attribute name="horizontalHeaderCascadingSectionResizes">
<bool>true</bool>
</attribute>
<attribute name="horizontalHeaderDefaultSectionSize">
<number>200</number>
</attribute>
<attribute name="horizontalHeaderHighlightSections">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string>Address book</string>
</property>
<property name="icon">
<iconset>
<selectedoff>:/newPrefix/images/addressbook.png</selectedoff>
</iconset>
</property>
</column>
<column>
<property name="text">
<string>Address</string>
</property>
</column>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButtonAddAddressBook">
<property name="maximumSize">
<size>
<width>200</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Add Contact</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButtonFetchNamecoinID">
<property name="maximumSize">
<size>
<width>200</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
<property name="text">
<string>Fetch Namecoin ID</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTabWidget" name="tabWidgetSend">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Send ordinary Message</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_8">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<layout class="QGridLayout" name="gridLayout_2">
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Subject:</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>From:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="lineEditSubject">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>To:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="comboBoxSendFrom">
<property name="minimumSize">
<size>
<width>300</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="lineEditTo"/>
</item>
</layout>
</item>
<item>
<widget class="QTextEdit" name="textEditMessage">
<property name="html">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Droid Sans'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2';&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>Send Message to your Subscribers</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_9">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<layout class="QGridLayout" name="gridLayout_5">
<item row="0" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>From:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="lineEditSubjectBroadcast">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Subject:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="comboBoxSendFromBroadcast">
<property name="minimumSize">
<size>
<width>300</width>
<height>0</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTextEdit" name="textEditMessageBroadcast">
<property name="html">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Droid Sans'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2';&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QPushButton" name="pushButtonTTL">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>32</width>
<height>16777215</height>
</size>
</property>
<property name="palette">
<palette>
<active>
<colorrole role="ButtonText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>0</red>
<green>0</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
</active>
<inactive>
<colorrole role="ButtonText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>0</red>
<green>0</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
</inactive>
<disabled>
<colorrole role="ButtonText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>120</red>
<green>120</green>
<blue>120</blue>
</color>
</brush>
</colorrole>
</disabled>
</palette>
</property>
<property name="font">
<font>
<underline>true</underline>
</font>
</property>
<property name="text">
<string>TTL:</string>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="horizontalSliderTTL">
<property name="minimumSize">
<size>
<width>35</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>70</width>
<height>16777215</height>
</size>
</property>
<widget class="SSplitter" name="horizontalSplitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="childrenCollapsible">
<bool>false</bool>
</property>
<widget class="SSplitter" name="verticalSplitter_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="childrenCollapsible">
<bool>false</bool>
</property>
<widget class="STableWidget" name="tableWidgetAddressBook">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<attribute name="horizontalHeaderCascadingSectionResizes">
<bool>true</bool>
</attribute>
<attribute name="horizontalHeaderDefaultSectionSize">
<number>200</number>
</attribute>
<attribute name="horizontalHeaderHighlightSections">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string>Address book</string>
</property>
<property name="icon">
<iconset>
<selectedoff>:/newPrefix/images/addressbook.png</selectedoff>
</iconset>
</property>
</column>
<column>
<property name="text">
<string>Address</string>
</property>
</column>
</widget>
<widget class="QPushButton" name="pushButtonAddAddressBook">
<property name="text">
<string>Add Contact</string>
</property>
</widget>
<widget class="QPushButton" name="pushButtonFetchNamecoinID">
<property name="text">
<string>Fetch Namecoin ID</string>
</property>
</widget>
</widget>
<widget class="SSplitter" name="verticalSplitter">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="childrenCollapsible">
<bool>false</bool>
</property>
<widget class="QTabWidget" name="tabWidgetSend">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="sendDirect">
<attribute name="title">
<string>Send ordinary Message</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_8">
<item row="0" column="0">
<widget class="SSplitter" name="verticalSplitter_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Vertical</enum>
</property>
<property name="invertedAppearance">
<property name="childrenCollapsible">
<bool>false</bool>
</property>
<property name="invertedControls">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelHumanFriendlyTTLDescription">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>45</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>45</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>X days</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButtonSend">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Send</string>
</property>
<widget class="QWidget" name="layoutWidget">
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>From:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="comboBoxSendFrom">
<property name="minimumSize">
<size>
<width>300</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>To:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="lineEditTo"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Subject:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="lineEditSubject">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="MessageCompose" name="textEditMessage">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
</widget>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="sendBroadcast">
<attribute name="title">
<string>Send Message to your Subscribers</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_9">
<item row="0" column="0">
<widget class="SSplitter" name="verticalSplitter_6">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="childrenCollapsible">
<bool>false</bool>
</property>
<widget class="QWidget" name="layoutWidget">
<layout class="QGridLayout" name="gridLayout_5">
<item row="0" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>From:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="lineEditSubjectBroadcast">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Subject:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="comboBoxSendFromBroadcast">
<property name="minimumSize">
<size>
<width>300</width>
<height>0</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="MessageCompose" name="textEditMessageBroadcast">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
</widget>
</widget>
</item>
</layout>
</widget>
</widget>
<widget class="QWidget" name="tTLContainer" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QPushButton" name="pushButtonTTL">
<property name="palette">
<palette>
<active>
<colorrole role="ButtonText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>0</red>
<green>0</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
</active>
<inactive>
<colorrole role="ButtonText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>0</red>
<green>0</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
</inactive>
<disabled>
<colorrole role="ButtonText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>120</red>
<green>120</green>
<blue>120</blue>
</color>
</brush>
</colorrole>
</disabled>
</palette>
</property>
<property name="font">
<font>
<underline>true</underline>
</font>
</property>
<property name="text">
<string>TTL:</string>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="horizontalSliderTTL">
<property name="minimumSize">
<size>
<width>70</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>105</width>
<height>16777215</height>
</size>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="invertedAppearance">
<bool>false</bool>
</property>
<property name="invertedControls">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelHumanFriendlyTTLDescription">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>45</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>%n hour(s)</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButtonClear">
<property name="text">
<string>Clear</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButtonSend">
<property name="text">
<string>Send</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</widget>
</item>
</layout>
<zorder></zorder>
</widget>
<widget class="QWidget" name="subscriptions">
<attribute name="icon">
<iconset resource="bitmessage_icons.qrc">
<iconset>
<normaloff>:/newPrefix/images/subscriptions.png</normaloff>:/newPrefix/images/subscriptions.png</iconset>
</attribute>
<attribute name="title">
@ -613,183 +665,207 @@ p, li { white-space: pre-wrap; }
</attribute>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QTreeWidget" name="treeWidgetSubscriptions">
<property name="maximumSize">
<size>
<width>200</width>
<height>16777215</height>
</size>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<column>
<property name="text">
<string>Subscriptions</string>
</property>
<property name="icon">
<iconset>
<selectedoff>:/newPrefix/images/subscriptions.png</selectedoff>
</iconset>
</property>
</column>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButtonAddSubscription">
<property name="maximumSize">
<size>
<width>200</width>
<height>16777215</height>
</size>
</property>
<widget class="SSplitter" name="horizontalSplitter_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="childrenCollapsible">
<bool>false</bool>
</property>
<widget class="SSplitter" name="verticalSplitter_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="childrenCollapsible">
<bool>false</bool>
</property>
<widget class="STreeWidget" name="treeWidgetSubscriptions">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<column>
<property name="text">
<string>Subscriptions</string>
</property>
<property name="icon">
<iconset>
<selectedoff>:/newPrefix/images/subscriptions.png</selectedoff>
</iconset>
</property>
</column>
</widget>
<widget class="QPushButton" name="pushButtonAddSubscription">
<property name="text">
<string>Add new Subscription</string>
</property>
</widget>
</widget>
<widget class="SSplitter" name="verticalSplitter_4">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="childrenCollapsible">
<bool>false</bool>
</property>
<widget class="QSplitter" name="horizontalSplitter_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QLineEdit" name="inboxSearchLineEditSubscriptions">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="placeholderText">
<string>Search</string>
</property>
</widget>
<widget class="QComboBox" name="inboxSearchOptionSubscriptions">
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
<item>
<property name="text">
<string>Add new Subscription</string>
<string>All</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLineEdit" name="inboxSearchLineEditSubscriptions">
<property name="placeholderText">
<string>Search</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="inboxSearchOptionSubscriptions">
<item>
<property name="text">
<string>All</string>
</property>
</item>
<item>
<property name="text">
<string>To</string>
</property>
</item>
<item>
<property name="text">
<string>From</string>
</property>
</item>
<item>
<property name="text">
<string>Subject</string>
</property>
</item>
<item>
<property name="text">
<string>Message</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTableWidget" name="tableWidgetInboxSubscriptions">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</item>
<item>
<property name="text">
<string>To</string>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</item>
<item>
<property name="text">
<string>From</string>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</item>
<item>
<property name="text">
<string>Subject</string>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</item>
<item>
<property name="text">
<string>Message</string>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
<attribute name="horizontalHeaderCascadingSectionResizes">
<bool>true</bool>
</attribute>
<attribute name="horizontalHeaderDefaultSectionSize">
<number>200</number>
</attribute>
<attribute name="horizontalHeaderHighlightSections">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderMinimumSectionSize">
<number>27</number>
</attribute>
<attribute name="horizontalHeaderShowSortIndicator" stdset="0">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderDefaultSectionSize">
<number>26</number>
</attribute>
<column>
<property name="text">
<string>To</string>
</property>
</column>
<column>
<property name="text">
<string>From</string>
</property>
</column>
<column>
<property name="text">
<string>Subject</string>
</property>
</column>
<column>
<property name="text">
<string>Received</string>
</property>
</column>
</widget>
</item>
<item>
<widget class="QTextEdit" name="textEditInboxMessageSubscriptions">
<property name="baseSize">
<size>
<width>0</width>
<height>500</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</widget>
</widget>
<widget class="STableWidget" name="tableWidgetInboxSubscriptions">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
<attribute name="horizontalHeaderCascadingSectionResizes">
<bool>true</bool>
</attribute>
<attribute name="horizontalHeaderDefaultSectionSize">
<number>200</number>
</attribute>
<attribute name="horizontalHeaderHighlightSections">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderMinimumSectionSize">
<number>27</number>
</attribute>
<attribute name="horizontalHeaderShowSortIndicator" stdset="0">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderDefaultSectionSize">
<number>26</number>
</attribute>
<column>
<property name="text">
<string>To</string>
</property>
</column>
<column>
<property name="text">
<string>From</string>
</property>
</column>
<column>
<property name="text">
<string>Subject</string>
</property>
</column>
<column>
<property name="text">
<string>Received</string>
</property>
</column>
</widget>
<widget class="MessageView" name="textEditInboxMessageSubscriptions">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>2</verstretch>
</sizepolicy>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>500</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_3">
<widget class="QWidget" name="chans">
<attribute name="icon">
<iconset resource="bitmessage_icons.qrc">
<iconset>
<normaloff>:/newPrefix/images/can-icon-16px.png</normaloff>:/newPrefix/images/can-icon-16px.png</iconset>
</attribute>
<attribute name="title">
@ -797,494 +873,227 @@ p, li { white-space: pre-wrap; }
</attribute>
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<layout class="QVBoxLayout" name="verticalLayout_17">
<item>
<widget class="QTreeWidget" name="treeWidgetChans">
<property name="maximumSize">
<size>
<width>200</width>
<height>16777215</height>
</size>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<property name="lineWidth">
<number>1</number>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<column>
<property name="text">
<string>Chans</string>
</property>
<property name="icon">
<iconset>
<selectedoff>:/newPrefix/images/can-icon-16px.png</selectedoff>
</iconset>
</property>
</column>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButtonAddChan">
<property name="maximumSize">
<size>
<width>200</width>
<height>16777215</height>
</size>
</property>
<widget class="SSplitter" name="horizontalSplitter_7">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="childrenCollapsible">
<bool>false</bool>
</property>
<widget class="SSplitter" name="verticalSplitter_17">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="childrenCollapsible">
<bool>false</bool>
</property>
<widget class="STreeWidget" name="treeWidgetChans">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<property name="lineWidth">
<number>1</number>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<column>
<property name="text">
<string>Chans</string>
</property>
<property name="icon">
<iconset>
<selectedoff>:/newPrefix/images/can-icon-16px.png</selectedoff>
</iconset>
</property>
</column>
</widget>
<widget class="QPushButton" name="pushButtonAddChan">
<property name="text">
<string>Add Chan</string>
</property>
</widget>
</widget>
<widget class="SSplitter" name="verticalSplitter_8">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="childrenCollapsible">
<bool>false</bool>
</property>
<widget class="QSplitter" name="horizontalSplitter_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QLineEdit" name="inboxSearchLineEditChans">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="placeholderText">
<string>Search</string>
</property>
</widget>
<widget class="QComboBox" name="inboxSearchOptionChans">
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
<item>
<property name="text">
<string>Add Chan</string>
<string>All</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_8">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QLineEdit" name="inboxSearchLineEditChans">
<property name="placeholderText">
<string>Search</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="inboxSearchOptionChans">
<item>
<property name="text">
<string>All</string>
</property>
</item>
<item>
<property name="text">
<string>To</string>
</property>
</item>
<item>
<property name="text">
<string>From</string>
</property>
</item>
<item>
<property name="text">
<string>Subject</string>
</property>
</item>
<item>
<property name="text">
<string>Message</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTableWidget" name="tableWidgetInboxChans">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</item>
<item>
<property name="text">
<string>To</string>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</item>
<item>
<property name="text">
<string>From</string>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</item>
<item>
<property name="text">
<string>Subject</string>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</item>
<item>
<property name="text">
<string>Message</string>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
<attribute name="horizontalHeaderCascadingSectionResizes">
<bool>true</bool>
</attribute>
<attribute name="horizontalHeaderDefaultSectionSize">
<number>200</number>
</attribute>
<attribute name="horizontalHeaderHighlightSections">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderMinimumSectionSize">
<number>27</number>
</attribute>
<attribute name="horizontalHeaderShowSortIndicator" stdset="0">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderDefaultSectionSize">
<number>26</number>
</attribute>
<column>
<property name="text">
<string>To</string>
</property>
</column>
<column>
<property name="text">
<string>From</string>
</property>
</column>
<column>
<property name="text">
<string>Subject</string>
</property>
</column>
<column>
<property name="text">
<string>Received</string>
</property>
</column>
</widget>
</item>
<item>
<widget class="QTextEdit" name="textEditInboxMessageChans">
<property name="baseSize">
<size>
<width>0</width>
<height>500</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</widget>
</widget>
<widget class="STableWidget" name="tableWidgetInboxChans">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
<attribute name="horizontalHeaderCascadingSectionResizes">
<bool>true</bool>
</attribute>
<attribute name="horizontalHeaderDefaultSectionSize">
<number>200</number>
</attribute>
<attribute name="horizontalHeaderHighlightSections">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderMinimumSectionSize">
<number>27</number>
</attribute>
<attribute name="horizontalHeaderShowSortIndicator" stdset="0">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderDefaultSectionSize">
<number>26</number>
</attribute>
<column>
<property name="text">
<string>To</string>
</property>
</column>
<column>
<property name="text">
<string>From</string>
</property>
</column>
<column>
<property name="text">
<string>Subject</string>
</property>
</column>
<column>
<property name="text">
<string>Received</string>
</property>
</column>
</widget>
<widget class="MessageView" name="textEditInboxMessageChans">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>2</verstretch>
</sizepolicy>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>500</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="blackwhitelist">
<widget class="Blacklist" name="blackwhitelist">
<attribute name="icon">
<iconset resource="bitmessage_icons.qrc">
<iconset>
<normaloff>:/newPrefix/images/blacklist.png</normaloff>:/newPrefix/images/blacklist.png</iconset>
</attribute>
<attribute name="title">
<string>Blacklist</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_6">
<item row="0" column="0" colspan="2">
<widget class="QRadioButton" name="radioButtonBlacklist">
<property name="text">
<string>Use a Blacklist (Allow all incoming messages except those on the Blacklist)</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QRadioButton" name="radioButtonWhitelist">
<property name="text">
<string>Use a Whitelist (Block all incoming messages except those on the Whitelist)</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QPushButton" name="pushButtonAddBlacklist">
<property name="text">
<string>Add new entry</string>
</property>
</widget>
</item>
<item row="2" column="1">
<spacer name="horizontalSpacer_8">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>689</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="0" colspan="2">
<widget class="QTableWidget" name="tableWidgetBlacklist">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<attribute name="horizontalHeaderCascadingSectionResizes">
<bool>true</bool>
</attribute>
<attribute name="horizontalHeaderDefaultSectionSize">
<number>400</number>
</attribute>
<attribute name="horizontalHeaderHighlightSections">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderShowSortIndicator" stdset="0">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string>Name or Label</string>
</property>
</column>
<column>
<property name="text">
<string>Address</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="networkstatus">
<widget class="NetworkStatus" name="networkstatus">
<attribute name="icon">
<iconset resource="bitmessage_icons.qrc">
<iconset>
<normaloff>:/newPrefix/images/networkstatus.png</normaloff>:/newPrefix/images/networkstatus.png</iconset>
</attribute>
<attribute name="title">
<string>Network Status</string>
</attribute>
<widget class="QPushButton" name="pushButtonStatusIcon">
<property name="geometry">
<rect>
<x>680</x>
<y>440</y>
<width>21</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="bitmessage_icons.qrc">
<normaloff>:/newPrefix/images/redicon.png</normaloff>:/newPrefix/images/redicon.png</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
<widget class="QTableWidget" name="tableWidgetConnectionCount">
<property name="geometry">
<rect>
<x>20</x>
<y>70</y>
<width>241</width>
<height>241</height>
</rect>
</property>
<property name="palette">
<palette>
<active>
<colorrole role="Base">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>212</red>
<green>208</green>
<blue>200</blue>
</color>
</brush>
</colorrole>
</active>
<inactive>
<colorrole role="Base">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>212</red>
<green>208</green>
<blue>200</blue>
</color>
</brush>
</colorrole>
</inactive>
<disabled>
<colorrole role="Base">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>212</red>
<green>208</green>
<blue>200</blue>
</color>
</brush>
</colorrole>
</disabled>
</palette>
</property>
<property name="frameShape">
<enum>QFrame::Box</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="showDropIndicator" stdset="0">
<bool>false</bool>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::NoSelection</enum>
</property>
<attribute name="horizontalHeaderCascadingSectionResizes">
<bool>true</bool>
</attribute>
<attribute name="horizontalHeaderHighlightSections">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string>Stream #</string>
</property>
</column>
<column>
<property name="text">
<string>Connections</string>
</property>
</column>
</widget>
<widget class="QLabel" name="labelTotalConnections">
<property name="geometry">
<rect>
<x>20</x>
<y>30</y>
<width>401</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>Total connections:</string>
</property>
</widget>
<widget class="QLabel" name="labelStartupTime">
<property name="geometry">
<rect>
<x>320</x>
<y>110</y>
<width>331</width>
<height>20</height>
</rect>
</property>
<property name="text">
<string>Since startup:</string>
</property>
</widget>
<widget class="QLabel" name="labelMessageCount">
<property name="geometry">
<rect>
<x>350</x>
<y>130</y>
<width>361</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>Processed 0 person-to-person messages.</string>
</property>
</widget>
<widget class="QLabel" name="labelPubkeyCount">
<property name="geometry">
<rect>
<x>350</x>
<y>170</y>
<width>331</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>Processed 0 public keys.</string>
</property>
</widget>
<widget class="QLabel" name="labelBroadcastCount">
<property name="geometry">
<rect>
<x>350</x>
<y>150</y>
<width>351</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>Processed 0 broadcasts.</string>
</property>
</widget>
<widget class="QLabel" name="labelLookupsPerSecond">
<property name="geometry">
<rect>
<x>320</x>
<y>250</y>
<width>291</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>Inventory lookups per second: 0</string>
</property>
</widget>
<widget class="QLabel" name="labelBytesRecvCount">
<property name="geometry">
<rect>
<x>350</x>
<y>210</y>
<width>251</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>Down: 0 KB/s</string>
</property>
</widget>
<widget class="QLabel" name="labelBytesSentCount">
<property name="geometry">
<rect>
<x>350</x>
<y>230</y>
<width>251</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>Up: 0 KB/s</string>
</property>
</widget>
</widget>
</widget>
</item>
@ -1296,7 +1105,7 @@ p, li { white-space: pre-wrap; }
<x>0</x>
<y>0</y>
<width>885</width>
<height>27</height>
<height>30</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
@ -1306,6 +1115,7 @@ p, li { white-space: pre-wrap; }
<addaction name="actionManageKeys"/>
<addaction name="actionDeleteAllTrashedMessages"/>
<addaction name="actionRegenerateDeterministicAddresses"/>
<addaction name="actionNetworkSwitch"/>
<addaction name="actionExit"/>
</widget>
<widget class="QMenu" name="menuSettings">
@ -1319,20 +1129,14 @@ p, li { white-space: pre-wrap; }
<string>Help</string>
</property>
<addaction name="actionHelp"/>
<addaction name="actionSupport"/>
<addaction name="actionAbout"/>
</widget>
<addaction name="menuFile"/>
<addaction name="menuSettings"/>
<addaction name="menuHelp"/>
</widget>
<widget class="QStatusBar" name="statusbar">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>22</height>
</size>
</property>
</widget>
<widget class="BMStatusBar" name="statusbar"/>
<action name="actionImport_keys">
<property name="text">
<string>Import keys</string>
@ -1354,6 +1158,11 @@ p, li { white-space: pre-wrap; }
<string>Manage keys</string>
</property>
</action>
<action name="actionNetworkSwitch">
<property name="text">
<string>Go offline</string>
</property>
</action>
<action name="actionExit">
<property name="icon">
<iconset theme="application-exit">
@ -1380,6 +1189,16 @@ p, li { white-space: pre-wrap; }
<string>F1</string>
</property>
</action>
<action name="actionSupport">
<property name="icon">
<iconset theme="help-support">
<normaloff/>
</iconset>
</property>
<property name="text">
<string>Contact support</string>
</property>
</action>
<action name="actionAbout">
<property name="icon">
<iconset theme="help-about">
@ -1431,6 +1250,51 @@ p, li { white-space: pre-wrap; }
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>SSplitter</class>
<extends>QSplitter</extends>
<header>bitmessageqt.settingsmixin.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>STreeWidget</class>
<extends>QTreeWidget</extends>
<header>bitmessageqt.settingsmixin.h</header>
</customwidget>
<customwidget>
<class>STableWidget</class>
<extends>QTableWidget</extends>
<header>bitmessageqt.settingsmixin.h</header>
</customwidget>
<customwidget>
<class>MessageView</class>
<extends>QTextEdit</extends>
<header>bitmessageqt.messageview.h</header>
</customwidget>
<customwidget>
<class>MessageCompose</class>
<extends>QTextEdit</extends>
<header>bitmessageqt.messagecompose.h</header>
</customwidget>
<customwidget>
<class>Blacklist</class>
<extends>QWidget</extends>
<header>bitmessageqt.blacklist.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>NetworkStatus</class>
<extends>QWidget</extends>
<header>bitmessageqt.networkstatus.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>BMStatusBar</class>
<extends>QStatusBar</extends>
<header>bitmessageqt.statusbar.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>tableWidgetInbox</tabstop>
<tabstop>textEditInboxMessage</tabstop>
@ -1438,17 +1302,8 @@ p, li { white-space: pre-wrap; }
<tabstop>lineEditTo</tabstop>
<tabstop>lineEditSubject</tabstop>
<tabstop>textEditMessage</tabstop>
<tabstop>pushButtonSend</tabstop>
<tabstop>pushButtonAddSubscription</tabstop>
<tabstop>radioButtonBlacklist</tabstop>
<tabstop>radioButtonWhitelist</tabstop>
<tabstop>pushButtonAddBlacklist</tabstop>
<tabstop>tableWidgetBlacklist</tabstop>
<tabstop>tableWidgetConnectionCount</tabstop>
<tabstop>pushButtonStatusIcon</tabstop>
</tabstops>
<resources>
<include location="bitmessage_icons.qrc"/>
</resources>
<resources/>
<connections/>
</ui>

View File

@ -240,3 +240,13 @@ class Blacklist(QtGui.QWidget, RetranslateMixin):
def on_action_BlacklistSetAvatar(self):
self.window().on_action_SetAvatar(self.tableWidgetBlacklist)
def retranslateUi(self):
super(self.__class__, self).retranslateUi()
tabs = self.parent().parent()
if BMConfigParser().get("bitmessagesettings", "blackwhitelist") == "black":
tabs.setTabText(tabs.indexOf(self), _translate("blacklist", "Blacklist"))
else:
tabs.setTabText(tabs.indexOf(self), _translate("blacklist", "Whitelist"))

View File

@ -1,36 +0,0 @@
import glob
import os
from PyQt4 import QtCore, QtGui
from bmconfigparser import BMConfigParser
import paths
class LanguageBox(QtGui.QComboBox):
languageName = {"system": "System Settings", "eo": "Esperanto", "en_pirate": "Pirate English"}
def __init__(self, parent = None):
super(QtGui.QComboBox, self).__init__(parent)
self.populate()
def populate(self):
self.clear()
localesPath = os.path.join (paths.codePath(), 'translations')
self.addItem(QtGui.QApplication.translate("settingsDialog", "System Settings", "system"), "system")
self.setCurrentIndex(0)
self.setInsertPolicy(QtGui.QComboBox.InsertAlphabetically)
for translationFile in sorted(glob.glob(os.path.join(localesPath, "bitmessage_*.qm"))):
localeShort = os.path.split(translationFile)[1].split("_", 1)[1][:-3]
locale = QtCore.QLocale(QtCore.QString(localeShort))
if localeShort in LanguageBox.languageName:
self.addItem(LanguageBox.languageName[localeShort], localeShort)
elif locale.nativeLanguageName() == "":
self.addItem(localeShort, localeShort)
else:
self.addItem(locale.nativeLanguageName(), localeShort)
configuredLocale = BMConfigParser().safeGet(
'bitmessagesettings', 'userlocale', "system")
for i in range(self.count()):
if self.itemData(i) == configuredLocale:
self.setCurrentIndex(i)
break

View File

@ -168,5 +168,9 @@ class NetworkStatus(QtGui.QWidget, RetranslateMixin):
def retranslateUi(self):
super(NetworkStatus, self).retranslateUi()
tabs = self.parent().parent()
tabs.setTabText(tabs.indexOf(self), _translate("networkstatus", "Network Status"))
self.labelStartupTime.setText(_translate("networkstatus", "Since startup on %1").arg(
l10n.formatTimestamp(self.startup)))

View File

@ -1,630 +0,0 @@
# -*- coding: utf-8 -*-
# pylint: disable=too-many-instance-attributes,too-many-locals,too-many-statements,attribute-defined-outside-init
"""
Form implementation generated from reading ui file 'settings.ui'
Created: Thu Dec 25 23:21:20 2014
by: PyQt4 UI code generator 4.10.3
WARNING! All changes made in this file will be lost!
"""
from __future__ import absolute_import
from sys import platform
from PyQt4 import QtCore, QtGui
from . import bitmessage_icons_rc # pylint: disable=unused-import
from .languagebox import LanguageBox
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
def _fromUtf8(s):
return s
try:
_encoding = QtGui.QApplication.UnicodeUTF8
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig)
class Ui_settingsDialog(object):
"""Encapsulate a UI settings dialog object"""
def setupUi(self, settingsDialog):
"""Set up the UI"""
settingsDialog.setObjectName(_fromUtf8("settingsDialog"))
settingsDialog.resize(521, 413)
self.gridLayout = QtGui.QGridLayout(settingsDialog)
self.gridLayout.setObjectName(_fromUtf8("gridLayout"))
self.buttonBox = QtGui.QDialogButtonBox(settingsDialog)
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel | QtGui.QDialogButtonBox.Ok)
self.buttonBox.setObjectName(_fromUtf8("buttonBox"))
self.gridLayout.addWidget(self.buttonBox, 1, 0, 1, 1)
self.tabWidgetSettings = QtGui.QTabWidget(settingsDialog)
self.tabWidgetSettings.setObjectName(_fromUtf8("tabWidgetSettings"))
self.tabUserInterface = QtGui.QWidget()
self.tabUserInterface.setEnabled(True)
self.tabUserInterface.setObjectName(_fromUtf8("tabUserInterface"))
self.formLayout = QtGui.QFormLayout(self.tabUserInterface)
self.formLayout.setObjectName(_fromUtf8("formLayout"))
self.checkBoxStartOnLogon = QtGui.QCheckBox(self.tabUserInterface)
self.checkBoxStartOnLogon.setObjectName(_fromUtf8("checkBoxStartOnLogon"))
self.formLayout.setWidget(0, QtGui.QFormLayout.LabelRole, self.checkBoxStartOnLogon)
self.groupBoxTray = QtGui.QGroupBox(self.tabUserInterface)
self.groupBoxTray.setObjectName(_fromUtf8("groupBoxTray"))
self.formLayoutTray = QtGui.QFormLayout(self.groupBoxTray)
self.formLayoutTray.setObjectName(_fromUtf8("formLayoutTray"))
self.checkBoxStartInTray = QtGui.QCheckBox(self.groupBoxTray)
self.checkBoxStartInTray.setObjectName(_fromUtf8("checkBoxStartInTray"))
self.formLayoutTray.setWidget(0, QtGui.QFormLayout.SpanningRole, self.checkBoxStartInTray)
self.checkBoxMinimizeToTray = QtGui.QCheckBox(self.groupBoxTray)
self.checkBoxMinimizeToTray.setChecked(True)
self.checkBoxMinimizeToTray.setObjectName(_fromUtf8("checkBoxMinimizeToTray"))
self.formLayoutTray.setWidget(1, QtGui.QFormLayout.LabelRole, self.checkBoxMinimizeToTray)
self.checkBoxTrayOnClose = QtGui.QCheckBox(self.groupBoxTray)
self.checkBoxTrayOnClose.setChecked(True)
self.checkBoxTrayOnClose.setObjectName(_fromUtf8("checkBoxTrayOnClose"))
self.formLayoutTray.setWidget(2, QtGui.QFormLayout.LabelRole, self.checkBoxTrayOnClose)
self.formLayout.setWidget(1, QtGui.QFormLayout.SpanningRole, self.groupBoxTray)
self.checkBoxHideTrayConnectionNotifications = QtGui.QCheckBox(self.tabUserInterface)
self.checkBoxHideTrayConnectionNotifications.setChecked(False)
self.checkBoxHideTrayConnectionNotifications.setObjectName(
_fromUtf8("checkBoxHideTrayConnectionNotifications"))
self.formLayout.setWidget(2, QtGui.QFormLayout.LabelRole, self.checkBoxHideTrayConnectionNotifications)
self.checkBoxShowTrayNotifications = QtGui.QCheckBox(self.tabUserInterface)
self.checkBoxShowTrayNotifications.setObjectName(_fromUtf8("checkBoxShowTrayNotifications"))
self.formLayout.setWidget(3, QtGui.QFormLayout.LabelRole, self.checkBoxShowTrayNotifications)
self.checkBoxPortableMode = QtGui.QCheckBox(self.tabUserInterface)
self.checkBoxPortableMode.setObjectName(_fromUtf8("checkBoxPortableMode"))
self.formLayout.setWidget(4, QtGui.QFormLayout.LabelRole, self.checkBoxPortableMode)
self.PortableModeDescription = QtGui.QLabel(self.tabUserInterface)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.PortableModeDescription.sizePolicy().hasHeightForWidth())
self.PortableModeDescription.setSizePolicy(sizePolicy)
self.PortableModeDescription.setWordWrap(True)
self.PortableModeDescription.setObjectName(_fromUtf8("PortableModeDescription"))
self.formLayout.setWidget(5, QtGui.QFormLayout.SpanningRole, self.PortableModeDescription)
self.checkBoxWillinglySendToMobile = QtGui.QCheckBox(self.tabUserInterface)
self.checkBoxWillinglySendToMobile.setObjectName(_fromUtf8("checkBoxWillinglySendToMobile"))
self.formLayout.setWidget(6, QtGui.QFormLayout.SpanningRole, self.checkBoxWillinglySendToMobile)
self.checkBoxUseIdenticons = QtGui.QCheckBox(self.tabUserInterface)
self.checkBoxUseIdenticons.setObjectName(_fromUtf8("checkBoxUseIdenticons"))
self.formLayout.setWidget(7, QtGui.QFormLayout.LabelRole, self.checkBoxUseIdenticons)
self.checkBoxReplyBelow = QtGui.QCheckBox(self.tabUserInterface)
self.checkBoxReplyBelow.setObjectName(_fromUtf8("checkBoxReplyBelow"))
self.formLayout.setWidget(8, QtGui.QFormLayout.LabelRole, self.checkBoxReplyBelow)
self.groupBox = QtGui.QGroupBox(self.tabUserInterface)
self.groupBox.setObjectName(_fromUtf8("groupBox"))
self.formLayout_2 = QtGui.QFormLayout(self.groupBox)
self.formLayout_2.setObjectName(_fromUtf8("formLayout_2"))
self.languageComboBox = LanguageBox(self.groupBox)
self.languageComboBox.setMinimumSize(QtCore.QSize(100, 0))
self.languageComboBox.setObjectName(_fromUtf8("languageComboBox")) # pylint: disable=not-callable
self.formLayout_2.setWidget(0, QtGui.QFormLayout.LabelRole, self.languageComboBox)
self.formLayout.setWidget(9, QtGui.QFormLayout.FieldRole, self.groupBox)
self.tabWidgetSettings.addTab(self.tabUserInterface, _fromUtf8(""))
self.tabNetworkSettings = QtGui.QWidget()
self.tabNetworkSettings.setObjectName(_fromUtf8("tabNetworkSettings"))
self.gridLayout_4 = QtGui.QGridLayout(self.tabNetworkSettings)
self.gridLayout_4.setObjectName(_fromUtf8("gridLayout_4"))
self.groupBox1 = QtGui.QGroupBox(self.tabNetworkSettings)
self.groupBox1.setObjectName(_fromUtf8("groupBox1"))
self.gridLayout_3 = QtGui.QGridLayout(self.groupBox1)
self.gridLayout_3.setObjectName(_fromUtf8("gridLayout_3"))
self.label = QtGui.QLabel(self.groupBox1)
self.label.setObjectName(_fromUtf8("label"))
self.gridLayout_3.addWidget(self.label, 0, 0, 1, 1, QtCore.Qt.AlignRight)
self.lineEditTCPPort = QtGui.QLineEdit(self.groupBox1)
self.lineEditTCPPort.setMaximumSize(QtCore.QSize(70, 16777215))
self.lineEditTCPPort.setObjectName(_fromUtf8("lineEditTCPPort"))
self.gridLayout_3.addWidget(self.lineEditTCPPort, 0, 1, 1, 1, QtCore.Qt.AlignLeft)
self.labelUPnP = QtGui.QLabel(self.groupBox1)
self.labelUPnP.setObjectName(_fromUtf8("labelUPnP"))
self.gridLayout_3.addWidget(self.labelUPnP, 0, 2, 1, 1, QtCore.Qt.AlignRight)
self.checkBoxUPnP = QtGui.QCheckBox(self.groupBox1)
self.checkBoxUPnP.setObjectName(_fromUtf8("checkBoxUPnP"))
self.gridLayout_3.addWidget(self.checkBoxUPnP, 0, 3, 1, 1, QtCore.Qt.AlignLeft)
self.gridLayout_4.addWidget(self.groupBox1, 0, 0, 1, 1)
self.groupBox_3 = QtGui.QGroupBox(self.tabNetworkSettings)
self.groupBox_3.setObjectName(_fromUtf8("groupBox_3"))
self.gridLayout_9 = QtGui.QGridLayout(self.groupBox_3)
self.gridLayout_9.setObjectName(_fromUtf8("gridLayout_9"))
spacerItem1 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_9.addItem(spacerItem1, 0, 0, 2, 1)
self.label_24 = QtGui.QLabel(self.groupBox_3)
self.label_24.setObjectName(_fromUtf8("label_24"))
self.gridLayout_9.addWidget(self.label_24, 0, 1, 1, 1)
self.lineEditMaxDownloadRate = QtGui.QLineEdit(self.groupBox_3)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.lineEditMaxDownloadRate.sizePolicy().hasHeightForWidth())
self.lineEditMaxDownloadRate.setSizePolicy(sizePolicy)
self.lineEditMaxDownloadRate.setMaximumSize(QtCore.QSize(60, 16777215))
self.lineEditMaxDownloadRate.setObjectName(_fromUtf8("lineEditMaxDownloadRate"))
self.gridLayout_9.addWidget(self.lineEditMaxDownloadRate, 0, 2, 1, 1)
self.label_25 = QtGui.QLabel(self.groupBox_3)
self.label_25.setObjectName(_fromUtf8("label_25"))
self.gridLayout_9.addWidget(self.label_25, 1, 1, 1, 1)
self.lineEditMaxUploadRate = QtGui.QLineEdit(self.groupBox_3)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.lineEditMaxUploadRate.sizePolicy().hasHeightForWidth())
self.lineEditMaxUploadRate.setSizePolicy(sizePolicy)
self.lineEditMaxUploadRate.setMaximumSize(QtCore.QSize(60, 16777215))
self.lineEditMaxUploadRate.setObjectName(_fromUtf8("lineEditMaxUploadRate"))
self.gridLayout_9.addWidget(self.lineEditMaxUploadRate, 1, 2, 1, 1)
self.label_26 = QtGui.QLabel(self.groupBox_3)
self.label_26.setObjectName(_fromUtf8("label_26"))
self.gridLayout_9.addWidget(self.label_26, 2, 1, 1, 1)
self.lineEditMaxOutboundConnections = QtGui.QLineEdit(self.groupBox_3)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.lineEditMaxOutboundConnections.sizePolicy().hasHeightForWidth())
self.lineEditMaxOutboundConnections.setSizePolicy(sizePolicy)
self.lineEditMaxOutboundConnections.setMaximumSize(QtCore.QSize(60, 16777215))
self.lineEditMaxOutboundConnections.setObjectName(_fromUtf8("lineEditMaxOutboundConnections"))
self.lineEditMaxOutboundConnections.setValidator(
QtGui.QIntValidator(0, 8, self.lineEditMaxOutboundConnections))
self.gridLayout_9.addWidget(self.lineEditMaxOutboundConnections, 2, 2, 1, 1)
self.gridLayout_4.addWidget(self.groupBox_3, 2, 0, 1, 1)
self.groupBox_2 = QtGui.QGroupBox(self.tabNetworkSettings)
self.groupBox_2.setObjectName(_fromUtf8("groupBox_2"))
self.gridLayout_2 = QtGui.QGridLayout(self.groupBox_2)
self.gridLayout_2.setObjectName(_fromUtf8("gridLayout_2"))
self.label_2 = QtGui.QLabel(self.groupBox_2)
self.label_2.setObjectName(_fromUtf8("label_2"))
self.gridLayout_2.addWidget(self.label_2, 0, 0, 1, 1)
self.label_3 = QtGui.QLabel(self.groupBox_2)
self.label_3.setObjectName(_fromUtf8("label_3"))
self.gridLayout_2.addWidget(self.label_3, 1, 1, 1, 1)
self.lineEditSocksHostname = QtGui.QLineEdit(self.groupBox_2)
self.lineEditSocksHostname.setObjectName(_fromUtf8("lineEditSocksHostname"))
self.lineEditSocksHostname.setPlaceholderText(_fromUtf8("127.0.0.1"))
self.gridLayout_2.addWidget(self.lineEditSocksHostname, 1, 2, 1, 2)
self.label_4 = QtGui.QLabel(self.groupBox_2)
self.label_4.setObjectName(_fromUtf8("label_4"))
self.gridLayout_2.addWidget(self.label_4, 1, 4, 1, 1)
self.lineEditSocksPort = QtGui.QLineEdit(self.groupBox_2)
self.lineEditSocksPort.setObjectName(_fromUtf8("lineEditSocksPort"))
if platform in ['darwin', 'win32', 'win64']:
self.lineEditSocksPort.setPlaceholderText(_fromUtf8("9150"))
else:
self.lineEditSocksPort.setPlaceholderText(_fromUtf8("9050"))
self.gridLayout_2.addWidget(self.lineEditSocksPort, 1, 5, 1, 1)
self.checkBoxAuthentication = QtGui.QCheckBox(self.groupBox_2)
self.checkBoxAuthentication.setObjectName(_fromUtf8("checkBoxAuthentication"))
self.gridLayout_2.addWidget(self.checkBoxAuthentication, 2, 1, 1, 1)
self.label_5 = QtGui.QLabel(self.groupBox_2)
self.label_5.setObjectName(_fromUtf8("label_5"))
self.gridLayout_2.addWidget(self.label_5, 2, 2, 1, 1)
self.lineEditSocksUsername = QtGui.QLineEdit(self.groupBox_2)
self.lineEditSocksUsername.setEnabled(False)
self.lineEditSocksUsername.setObjectName(_fromUtf8("lineEditSocksUsername"))
self.gridLayout_2.addWidget(self.lineEditSocksUsername, 2, 3, 1, 1)
self.label_6 = QtGui.QLabel(self.groupBox_2)
self.label_6.setObjectName(_fromUtf8("label_6"))
self.gridLayout_2.addWidget(self.label_6, 2, 4, 1, 1)
self.lineEditSocksPassword = QtGui.QLineEdit(self.groupBox_2)
self.lineEditSocksPassword.setEnabled(False)
self.lineEditSocksPassword.setInputMethodHints(
QtCore.Qt.ImhHiddenText | QtCore.Qt.ImhNoAutoUppercase | QtCore.Qt.ImhNoPredictiveText)
self.lineEditSocksPassword.setEchoMode(QtGui.QLineEdit.Password)
self.lineEditSocksPassword.setObjectName(_fromUtf8("lineEditSocksPassword"))
self.gridLayout_2.addWidget(self.lineEditSocksPassword, 2, 5, 1, 1)
self.checkBoxSocksListen = QtGui.QCheckBox(self.groupBox_2)
self.checkBoxSocksListen.setObjectName(_fromUtf8("checkBoxSocksListen"))
self.gridLayout_2.addWidget(self.checkBoxSocksListen, 3, 1, 1, 4)
self.comboBoxProxyType = QtGui.QComboBox(self.groupBox_2)
self.comboBoxProxyType.setObjectName(_fromUtf8("comboBoxProxyType")) # pylint: disable=not-callable
self.comboBoxProxyType.addItem(_fromUtf8(""))
self.comboBoxProxyType.addItem(_fromUtf8(""))
self.comboBoxProxyType.addItem(_fromUtf8(""))
self.gridLayout_2.addWidget(self.comboBoxProxyType, 0, 1, 1, 1)
self.gridLayout_4.addWidget(self.groupBox_2, 1, 0, 1, 1)
spacerItem2 = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
self.gridLayout_4.addItem(spacerItem2, 3, 0, 1, 1)
self.tabWidgetSettings.addTab(self.tabNetworkSettings, _fromUtf8(""))
self.tabDemandedDifficulty = QtGui.QWidget()
self.tabDemandedDifficulty.setObjectName(_fromUtf8("tabDemandedDifficulty"))
self.gridLayout_6 = QtGui.QGridLayout(self.tabDemandedDifficulty)
self.gridLayout_6.setObjectName(_fromUtf8("gridLayout_6"))
self.label_9 = QtGui.QLabel(self.tabDemandedDifficulty)
self.label_9.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter)
self.label_9.setObjectName(_fromUtf8("label_9"))
self.gridLayout_6.addWidget(self.label_9, 1, 1, 1, 1)
self.label_10 = QtGui.QLabel(self.tabDemandedDifficulty)
self.label_10.setWordWrap(True)
self.label_10.setObjectName(_fromUtf8("label_10"))
self.gridLayout_6.addWidget(self.label_10, 2, 0, 1, 3)
self.label_11 = QtGui.QLabel(self.tabDemandedDifficulty)
self.label_11.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter)
self.label_11.setObjectName(_fromUtf8("label_11"))
self.gridLayout_6.addWidget(self.label_11, 3, 1, 1, 1)
self.label_8 = QtGui.QLabel(self.tabDemandedDifficulty)
self.label_8.setWordWrap(True)
self.label_8.setObjectName(_fromUtf8("label_8"))
self.gridLayout_6.addWidget(self.label_8, 0, 0, 1, 3)
spacerItem3 = QtGui.QSpacerItem(203, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_6.addItem(spacerItem3, 1, 0, 1, 1)
self.label_12 = QtGui.QLabel(self.tabDemandedDifficulty)
self.label_12.setWordWrap(True)
self.label_12.setObjectName(_fromUtf8("label_12"))
self.gridLayout_6.addWidget(self.label_12, 4, 0, 1, 3)
self.lineEditSmallMessageDifficulty = QtGui.QLineEdit(self.tabDemandedDifficulty)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.lineEditSmallMessageDifficulty.sizePolicy().hasHeightForWidth())
self.lineEditSmallMessageDifficulty.setSizePolicy(sizePolicy)
self.lineEditSmallMessageDifficulty.setMaximumSize(QtCore.QSize(70, 16777215))
self.lineEditSmallMessageDifficulty.setObjectName(_fromUtf8("lineEditSmallMessageDifficulty"))
self.gridLayout_6.addWidget(self.lineEditSmallMessageDifficulty, 3, 2, 1, 1)
self.lineEditTotalDifficulty = QtGui.QLineEdit(self.tabDemandedDifficulty)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.lineEditTotalDifficulty.sizePolicy().hasHeightForWidth())
self.lineEditTotalDifficulty.setSizePolicy(sizePolicy)
self.lineEditTotalDifficulty.setMaximumSize(QtCore.QSize(70, 16777215))
self.lineEditTotalDifficulty.setObjectName(_fromUtf8("lineEditTotalDifficulty"))
self.gridLayout_6.addWidget(self.lineEditTotalDifficulty, 1, 2, 1, 1)
spacerItem4 = QtGui.QSpacerItem(203, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_6.addItem(spacerItem4, 3, 0, 1, 1)
spacerItem5 = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
self.gridLayout_6.addItem(spacerItem5, 5, 0, 1, 1)
self.tabWidgetSettings.addTab(self.tabDemandedDifficulty, _fromUtf8(""))
self.tabMaxAcceptableDifficulty = QtGui.QWidget()
self.tabMaxAcceptableDifficulty.setObjectName(_fromUtf8("tabMaxAcceptableDifficulty"))
self.gridLayout_7 = QtGui.QGridLayout(self.tabMaxAcceptableDifficulty)
self.gridLayout_7.setObjectName(_fromUtf8("gridLayout_7"))
self.label_15 = QtGui.QLabel(self.tabMaxAcceptableDifficulty)
self.label_15.setWordWrap(True)
self.label_15.setObjectName(_fromUtf8("label_15"))
self.gridLayout_7.addWidget(self.label_15, 0, 0, 1, 3)
spacerItem6 = QtGui.QSpacerItem(102, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_7.addItem(spacerItem6, 1, 0, 1, 1)
self.label_13 = QtGui.QLabel(self.tabMaxAcceptableDifficulty)
self.label_13.setLayoutDirection(QtCore.Qt.LeftToRight)
self.label_13.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter)
self.label_13.setObjectName(_fromUtf8("label_13"))
self.gridLayout_7.addWidget(self.label_13, 1, 1, 1, 1)
self.lineEditMaxAcceptableTotalDifficulty = QtGui.QLineEdit(self.tabMaxAcceptableDifficulty)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.lineEditMaxAcceptableTotalDifficulty.sizePolicy().hasHeightForWidth())
self.lineEditMaxAcceptableTotalDifficulty.setSizePolicy(sizePolicy)
self.lineEditMaxAcceptableTotalDifficulty.setMaximumSize(QtCore.QSize(70, 16777215))
self.lineEditMaxAcceptableTotalDifficulty.setObjectName(_fromUtf8("lineEditMaxAcceptableTotalDifficulty"))
self.gridLayout_7.addWidget(self.lineEditMaxAcceptableTotalDifficulty, 1, 2, 1, 1)
spacerItem7 = QtGui.QSpacerItem(102, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_7.addItem(spacerItem7, 2, 0, 1, 1)
self.label_14 = QtGui.QLabel(self.tabMaxAcceptableDifficulty)
self.label_14.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter)
self.label_14.setObjectName(_fromUtf8("label_14"))
self.gridLayout_7.addWidget(self.label_14, 2, 1, 1, 1)
self.lineEditMaxAcceptableSmallMessageDifficulty = QtGui.QLineEdit(self.tabMaxAcceptableDifficulty)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.lineEditMaxAcceptableSmallMessageDifficulty.sizePolicy().hasHeightForWidth())
self.lineEditMaxAcceptableSmallMessageDifficulty.setSizePolicy(sizePolicy)
self.lineEditMaxAcceptableSmallMessageDifficulty.setMaximumSize(QtCore.QSize(70, 16777215))
self.lineEditMaxAcceptableSmallMessageDifficulty.setObjectName(
_fromUtf8("lineEditMaxAcceptableSmallMessageDifficulty"))
self.gridLayout_7.addWidget(self.lineEditMaxAcceptableSmallMessageDifficulty, 2, 2, 1, 1)
spacerItem8 = QtGui.QSpacerItem(20, 147, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
self.gridLayout_7.addItem(spacerItem8, 3, 1, 1, 1)
self.labelOpenCL = QtGui.QLabel(self.tabMaxAcceptableDifficulty)
self.labelOpenCL.setObjectName(_fromUtf8("labelOpenCL"))
self.gridLayout_7.addWidget(self.labelOpenCL, 4, 0, 1, 1)
self.comboBoxOpenCL = QtGui.QComboBox(self.tabMaxAcceptableDifficulty)
self.comboBoxOpenCL.setObjectName = (_fromUtf8("comboBoxOpenCL"))
self.gridLayout_7.addWidget(self.comboBoxOpenCL, 4, 1, 1, 1)
self.tabWidgetSettings.addTab(self.tabMaxAcceptableDifficulty, _fromUtf8(""))
self.tabNamecoin = QtGui.QWidget()
self.tabNamecoin.setObjectName(_fromUtf8("tabNamecoin"))
self.gridLayout_8 = QtGui.QGridLayout(self.tabNamecoin)
self.gridLayout_8.setObjectName(_fromUtf8("gridLayout_8"))
spacerItem9 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_8.addItem(spacerItem9, 2, 0, 1, 1)
self.label_16 = QtGui.QLabel(self.tabNamecoin)
self.label_16.setWordWrap(True)
self.label_16.setObjectName(_fromUtf8("label_16"))
self.gridLayout_8.addWidget(self.label_16, 0, 0, 1, 3)
self.label_17 = QtGui.QLabel(self.tabNamecoin)
self.label_17.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter)
self.label_17.setObjectName(_fromUtf8("label_17"))
self.gridLayout_8.addWidget(self.label_17, 2, 1, 1, 1)
self.lineEditNamecoinHost = QtGui.QLineEdit(self.tabNamecoin)
self.lineEditNamecoinHost.setObjectName(_fromUtf8("lineEditNamecoinHost"))
self.gridLayout_8.addWidget(self.lineEditNamecoinHost, 2, 2, 1, 1)
spacerItem10 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_8.addItem(spacerItem10, 3, 0, 1, 1)
spacerItem11 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_8.addItem(spacerItem11, 4, 0, 1, 1)
self.label_18 = QtGui.QLabel(self.tabNamecoin)
self.label_18.setEnabled(True)
self.label_18.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter)
self.label_18.setObjectName(_fromUtf8("label_18"))
self.gridLayout_8.addWidget(self.label_18, 3, 1, 1, 1)
self.lineEditNamecoinPort = QtGui.QLineEdit(self.tabNamecoin)
self.lineEditNamecoinPort.setObjectName(_fromUtf8("lineEditNamecoinPort"))
self.gridLayout_8.addWidget(self.lineEditNamecoinPort, 3, 2, 1, 1)
spacerItem12 = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
self.gridLayout_8.addItem(spacerItem12, 8, 1, 1, 1)
self.labelNamecoinUser = QtGui.QLabel(self.tabNamecoin)
self.labelNamecoinUser.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter)
self.labelNamecoinUser.setObjectName(_fromUtf8("labelNamecoinUser"))
self.gridLayout_8.addWidget(self.labelNamecoinUser, 4, 1, 1, 1)
self.lineEditNamecoinUser = QtGui.QLineEdit(self.tabNamecoin)
self.lineEditNamecoinUser.setObjectName(_fromUtf8("lineEditNamecoinUser"))
self.gridLayout_8.addWidget(self.lineEditNamecoinUser, 4, 2, 1, 1)
spacerItem13 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_8.addItem(spacerItem13, 5, 0, 1, 1)
self.labelNamecoinPassword = QtGui.QLabel(self.tabNamecoin)
self.labelNamecoinPassword.setAlignment(
QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter)
self.labelNamecoinPassword.setObjectName(_fromUtf8("labelNamecoinPassword"))
self.gridLayout_8.addWidget(self.labelNamecoinPassword, 5, 1, 1, 1)
self.lineEditNamecoinPassword = QtGui.QLineEdit(self.tabNamecoin)
self.lineEditNamecoinPassword.setInputMethodHints(
QtCore.Qt.ImhHiddenText | QtCore.Qt.ImhNoAutoUppercase | QtCore.Qt.ImhNoPredictiveText)
self.lineEditNamecoinPassword.setEchoMode(QtGui.QLineEdit.Password)
self.lineEditNamecoinPassword.setObjectName(_fromUtf8("lineEditNamecoinPassword"))
self.gridLayout_8.addWidget(self.lineEditNamecoinPassword, 5, 2, 1, 1)
self.labelNamecoinTestResult = QtGui.QLabel(self.tabNamecoin)
self.labelNamecoinTestResult.setText(_fromUtf8(""))
self.labelNamecoinTestResult.setObjectName(_fromUtf8("labelNamecoinTestResult"))
self.gridLayout_8.addWidget(self.labelNamecoinTestResult, 7, 0, 1, 2)
self.pushButtonNamecoinTest = QtGui.QPushButton(self.tabNamecoin)
self.pushButtonNamecoinTest.setObjectName(_fromUtf8("pushButtonNamecoinTest"))
self.gridLayout_8.addWidget(self.pushButtonNamecoinTest, 7, 2, 1, 1)
self.horizontalLayout = QtGui.QHBoxLayout()
self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout"))
self.label_21 = QtGui.QLabel(self.tabNamecoin)
self.label_21.setObjectName(_fromUtf8("label_21"))
self.horizontalLayout.addWidget(self.label_21)
self.radioButtonNamecoinNamecoind = QtGui.QRadioButton(self.tabNamecoin)
self.radioButtonNamecoinNamecoind.setObjectName(_fromUtf8("radioButtonNamecoinNamecoind"))
self.horizontalLayout.addWidget(self.radioButtonNamecoinNamecoind)
self.radioButtonNamecoinNmcontrol = QtGui.QRadioButton(self.tabNamecoin)
self.radioButtonNamecoinNmcontrol.setObjectName(_fromUtf8("radioButtonNamecoinNmcontrol"))
self.horizontalLayout.addWidget(self.radioButtonNamecoinNmcontrol)
self.gridLayout_8.addLayout(self.horizontalLayout, 1, 0, 1, 3)
self.tabWidgetSettings.addTab(self.tabNamecoin, _fromUtf8(""))
self.tabResendsExpire = QtGui.QWidget()
self.tabResendsExpire.setObjectName(_fromUtf8("tabResendsExpire"))
self.gridLayout_5 = QtGui.QGridLayout(self.tabResendsExpire)
self.gridLayout_5.setObjectName(_fromUtf8("gridLayout_5"))
self.label_7 = QtGui.QLabel(self.tabResendsExpire)
self.label_7.setWordWrap(True)
self.label_7.setObjectName(_fromUtf8("label_7"))
self.gridLayout_5.addWidget(self.label_7, 0, 0, 1, 3)
spacerItem14 = QtGui.QSpacerItem(212, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_5.addItem(spacerItem14, 1, 0, 1, 1)
self.widget = QtGui.QWidget(self.tabResendsExpire)
self.widget.setMinimumSize(QtCore.QSize(231, 75))
self.widget.setObjectName(_fromUtf8("widget"))
self.label_19 = QtGui.QLabel(self.widget)
self.label_19.setGeometry(QtCore.QRect(10, 20, 101, 20))
self.label_19.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter)
self.label_19.setObjectName(_fromUtf8("label_19"))
self.label_20 = QtGui.QLabel(self.widget)
self.label_20.setGeometry(QtCore.QRect(30, 40, 80, 16))
self.label_20.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter)
self.label_20.setObjectName(_fromUtf8("label_20"))
self.lineEditDays = QtGui.QLineEdit(self.widget)
self.lineEditDays.setGeometry(QtCore.QRect(113, 20, 51, 20))
self.lineEditDays.setObjectName(_fromUtf8("lineEditDays"))
self.lineEditMonths = QtGui.QLineEdit(self.widget)
self.lineEditMonths.setGeometry(QtCore.QRect(113, 40, 51, 20))
self.lineEditMonths.setObjectName(_fromUtf8("lineEditMonths"))
self.label_22 = QtGui.QLabel(self.widget)
self.label_22.setGeometry(QtCore.QRect(169, 23, 61, 16))
self.label_22.setObjectName(_fromUtf8("label_22"))
self.label_23 = QtGui.QLabel(self.widget)
self.label_23.setGeometry(QtCore.QRect(170, 41, 71, 16))
self.label_23.setObjectName(_fromUtf8("label_23"))
self.gridLayout_5.addWidget(self.widget, 1, 2, 1, 1)
spacerItem15 = QtGui.QSpacerItem(20, 129, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
self.gridLayout_5.addItem(spacerItem15, 2, 1, 1, 1)
self.tabWidgetSettings.addTab(self.tabResendsExpire, _fromUtf8(""))
self.gridLayout.addWidget(self.tabWidgetSettings, 0, 0, 1, 1)
self.retranslateUi(settingsDialog)
self.tabWidgetSettings.setCurrentIndex(0)
QtCore.QObject.connect( # pylint: disable=no-member
self.buttonBox, QtCore.SIGNAL(_fromUtf8("accepted()")), settingsDialog.accept)
QtCore.QObject.connect( # pylint: disable=no-member
self.buttonBox, QtCore.SIGNAL(_fromUtf8("rejected()")), settingsDialog.reject)
QtCore.QObject.connect( # pylint: disable=no-member
self.checkBoxAuthentication,
QtCore.SIGNAL(
_fromUtf8("toggled(bool)")),
self.lineEditSocksUsername.setEnabled)
QtCore.QObject.connect( # pylint: disable=no-member
self.checkBoxAuthentication,
QtCore.SIGNAL(
_fromUtf8("toggled(bool)")),
self.lineEditSocksPassword.setEnabled)
QtCore.QMetaObject.connectSlotsByName(settingsDialog)
settingsDialog.setTabOrder(self.tabWidgetSettings, self.checkBoxStartOnLogon)
settingsDialog.setTabOrder(self.checkBoxStartOnLogon, self.checkBoxStartInTray)
settingsDialog.setTabOrder(self.checkBoxStartInTray, self.checkBoxMinimizeToTray)
settingsDialog.setTabOrder(self.checkBoxMinimizeToTray, self.lineEditTCPPort)
settingsDialog.setTabOrder(self.lineEditTCPPort, self.comboBoxProxyType)
settingsDialog.setTabOrder(self.comboBoxProxyType, self.lineEditSocksHostname)
settingsDialog.setTabOrder(self.lineEditSocksHostname, self.lineEditSocksPort)
settingsDialog.setTabOrder(self.lineEditSocksPort, self.checkBoxAuthentication)
settingsDialog.setTabOrder(self.checkBoxAuthentication, self.lineEditSocksUsername)
settingsDialog.setTabOrder(self.lineEditSocksUsername, self.lineEditSocksPassword)
settingsDialog.setTabOrder(self.lineEditSocksPassword, self.checkBoxSocksListen)
settingsDialog.setTabOrder(self.checkBoxSocksListen, self.buttonBox)
def retranslateUi(self, settingsDialog):
"""Re-translate the UI into the supported languages"""
settingsDialog.setWindowTitle(_translate("settingsDialog", "Settings", None))
self.checkBoxStartOnLogon.setText(_translate("settingsDialog", "Start Bitmessage on user login", None))
self.groupBoxTray.setTitle(_translate("settingsDialog", "Tray", None))
self.checkBoxStartInTray.setText(
_translate(
"settingsDialog",
"Start Bitmessage in the tray (don\'t show main window)",
None))
self.checkBoxMinimizeToTray.setText(_translate("settingsDialog", "Minimize to tray", None))
self.checkBoxTrayOnClose.setText(_translate("settingsDialog", "Close to tray", None))
self.checkBoxHideTrayConnectionNotifications.setText(
_translate("settingsDialog", "Hide connection notifications", None))
self.checkBoxShowTrayNotifications.setText(
_translate(
"settingsDialog",
"Show notification when message received",
None))
self.checkBoxPortableMode.setText(_translate("settingsDialog", "Run in Portable Mode", None))
self.PortableModeDescription.setText(
_translate(
"settingsDialog",
"In Portable Mode, messages and config files are stored in the same directory as the"
" program rather than the normal application-data folder. This makes it convenient to"
" run Bitmessage from a USB thumb drive.",
None))
self.checkBoxWillinglySendToMobile.setText(
_translate(
"settingsDialog",
"Willingly include unencrypted destination address when sending to a mobile device",
None))
self.checkBoxUseIdenticons.setText(_translate("settingsDialog", "Use Identicons", None))
self.checkBoxReplyBelow.setText(_translate("settingsDialog", "Reply below Quote", None))
self.groupBox.setTitle(_translate("settingsDialog", "Interface Language", None))
self.languageComboBox.setItemText(0, _translate("settingsDialog", "System Settings", "system"))
self.tabWidgetSettings.setTabText(
self.tabWidgetSettings.indexOf(
self.tabUserInterface),
_translate(
"settingsDialog", "User Interface", None))
self.groupBox1.setTitle(_translate("settingsDialog", "Listening port", None))
self.label.setText(_translate("settingsDialog", "Listen for connections on port:", None))
self.labelUPnP.setText(_translate("settingsDialog", "UPnP:", None))
self.groupBox_3.setTitle(_translate("settingsDialog", "Bandwidth limit", None))
self.label_24.setText(_translate("settingsDialog", "Maximum download rate (kB/s): [0: unlimited]", None))
self.label_25.setText(_translate("settingsDialog", "Maximum upload rate (kB/s): [0: unlimited]", None))
self.label_26.setText(_translate("settingsDialog", "Maximum outbound connections: [0: none]", None))
self.groupBox_2.setTitle(_translate("settingsDialog", "Proxy server / Tor", None))
self.label_2.setText(_translate("settingsDialog", "Type:", None))
self.label_3.setText(_translate("settingsDialog", "Server hostname:", None))
self.label_4.setText(_translate("settingsDialog", "Port:", None))
self.checkBoxAuthentication.setText(_translate("settingsDialog", "Authentication", None))
self.label_5.setText(_translate("settingsDialog", "Username:", None))
self.label_6.setText(_translate("settingsDialog", "Pass:", None))
self.checkBoxSocksListen.setText(
_translate(
"settingsDialog",
"Listen for incoming connections when using proxy",
None))
self.comboBoxProxyType.setItemText(0, _translate("settingsDialog", "none", None))
self.comboBoxProxyType.setItemText(1, _translate("settingsDialog", "SOCKS4a", None))
self.comboBoxProxyType.setItemText(2, _translate("settingsDialog", "SOCKS5", None))
self.tabWidgetSettings.setTabText(
self.tabWidgetSettings.indexOf(
self.tabNetworkSettings),
_translate(
"settingsDialog", "Network Settings", None))
self.label_9.setText(_translate("settingsDialog", "Total difficulty:", None))
self.label_10.setText(
_translate(
"settingsDialog",
"The \'Total difficulty\' affects the absolute amount of work the sender must complete."
" Doubling this value doubles the amount of work.",
None))
self.label_11.setText(_translate("settingsDialog", "Small message difficulty:", None))
self.label_8.setText(_translate(
"settingsDialog",
"When someone sends you a message, their computer must first complete some work. The difficulty of this"
" work, by default, is 1. You may raise this default for new addresses you create by changing the values"
" here. Any new addresses you create will require senders to meet the higher difficulty. There is one"
" exception: if you add a friend or acquaintance to your address book, Bitmessage will automatically"
" notify them when you next send a message that they need only complete the minimum amount of"
" work: difficulty 1. ",
None))
self.label_12.setText(
_translate(
"settingsDialog",
"The \'Small message difficulty\' mostly only affects the difficulty of sending small messages."
" Doubling this value makes it almost twice as difficult to send a small message but doesn\'t really"
" affect large messages.",
None))
self.tabWidgetSettings.setTabText(
self.tabWidgetSettings.indexOf(
self.tabDemandedDifficulty),
_translate(
"settingsDialog", "Demanded difficulty", None))
self.label_15.setText(
_translate(
"settingsDialog",
"Here you may set the maximum amount of work you are willing to do to send a message to another"
" person. Setting these values to 0 means that any value is acceptable.",
None))
self.label_13.setText(_translate("settingsDialog", "Maximum acceptable total difficulty:", None))
self.label_14.setText(_translate("settingsDialog", "Maximum acceptable small message difficulty:", None))
self.tabWidgetSettings.setTabText(
self.tabWidgetSettings.indexOf(
self.tabMaxAcceptableDifficulty),
_translate(
"settingsDialog", "Max acceptable difficulty", None))
self.labelOpenCL.setText(_translate("settingsDialog", "Hardware GPU acceleration (OpenCL):", None))
self.label_16.setText(_translate(
"settingsDialog",
"<html><head/><body><p>Bitmessage can utilize a different Bitcoin-based program called Namecoin to make"
" addresses human-friendly. For example, instead of having to tell your friend your long Bitmessage"
" address, you can simply tell him to send a message to <span style=\" font-style:italic;\">test."
" </span></p><p>(Getting your own Bitmessage address into Namecoin is still rather difficult).</p>"
"<p>Bitmessage can use either namecoind directly or a running nmcontrol instance.</p></body></html>",
None))
self.label_17.setText(_translate("settingsDialog", "Host:", None))
self.label_18.setText(_translate("settingsDialog", "Port:", None))
self.labelNamecoinUser.setText(_translate("settingsDialog", "Username:", None))
self.labelNamecoinPassword.setText(_translate("settingsDialog", "Password:", None))
self.pushButtonNamecoinTest.setText(_translate("settingsDialog", "Test", None))
self.label_21.setText(_translate("settingsDialog", "Connect to:", None))
self.radioButtonNamecoinNamecoind.setText(_translate("settingsDialog", "Namecoind", None))
self.radioButtonNamecoinNmcontrol.setText(_translate("settingsDialog", "NMControl", None))
self.tabWidgetSettings.setTabText(
self.tabWidgetSettings.indexOf(
self.tabNamecoin),
_translate(
"settingsDialog", "Namecoin integration", None))
self.label_7.setText(_translate(
"settingsDialog",
"<html><head/><body><p>By default, if you send a message to someone and he is offline for more than two"
" days, Bitmessage will send the message again after an additional two days. This will be continued with"
" exponential backoff forever; messages will be resent after 5, 10, 20 days ect. until the receiver"
" acknowledges them. Here you may change that behavior by having Bitmessage give up after a certain"
" number of days or months.</p><p>Leave these input fields blank for the default behavior."
" </p></body></html>",
None))
self.label_19.setText(_translate("settingsDialog", "Give up after", None))
self.label_20.setText(_translate("settingsDialog", "and", None))
self.label_22.setText(_translate("settingsDialog", "days", None))
self.label_23.setText(_translate("settingsDialog", "months.", None))
self.tabWidgetSettings.setTabText(
self.tabWidgetSettings.indexOf(
self.tabResendsExpire),
_translate(
"settingsDialog", "Resends Expire", None))

View File

@ -6,24 +6,14 @@
<rect>
<x>0</x>
<y>0</y>
<width>521</width>
<height>413</height>
<width>616</width>
<height>592</height>
</rect>
</property>
<property name="windowTitle">
<string>Settings</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QTabWidget" name="tabWidgetSettings">
<property name="currentIndex">
@ -44,23 +34,6 @@
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QCheckBox" name="checkBoxStartInTray">
<property name="text">
<string>Start Bitmessage in the tray (don't show main window)</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="checkBoxMinimizeToTray">
<property name="text">
<string>Minimize to tray</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="checkBoxShowTrayNotifications">
<property name="text">
@ -126,81 +99,52 @@
<height>0</height>
</size>
</property>
<item>
<property name="text">
<string comment="system">System Settings</string>
</property>
</item>
<item>
<property name="text">
<string notr="true" comment="en">English</string>
</property>
</item>
<item>
<property name="text">
<string notr="true" comment="eo">Esperanto</string>
</property>
</item>
<item>
<property name="text">
<string notr="true" comment="fr">Français</string>
</property>
</item>
<item>
<property name="text">
<string notr="true" comment="de">Deutsch</string>
</property>
</item>
<item>
<property name="text">
<string notr="true" comment="es">Español</string>
</property>
</item>
<item>
<property name="text">
<string notr="true" comment="ru">русский</string>
</property>
</item>
<item>
<property name="text">
<string notr="true" comment="no">Norsk</string>
</property>
</item>
<item>
<property name="text">
<string notr="true" comment="ar">العربية</string>
</property>
</item>
<item>
<property name="text">
<string notr="true" comment="zh_cn">简体中文</string>
</property>
</item>
<item>
<property name="text">
<string notr="true" comment="ja">日本語</string>
</property>
</item>
<item>
<property name="text">
<string notr="true" comment="nl">Nederlands</string>
</property>
</item>
<item>
<property name="text">
<string notr="true" comment="cs">Česky</string>
</property>
</item>
<item>
<property name="text">
<string comment="en_pirate">Pirate English</string>
</property>
</item>
<item>
<property name="text">
<string comment="other">Other (set in keys.dat)</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="checkBoxHideTrayConnectionNotifications">
<property name="text">
<string>Hide connection notifications</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QGroupBox" name="groupBoxTray">
<property name="title">
<string>Tray</string>
</property>
<layout class="QFormLayout" name="formLayoutTray">
<item row="0" column="0" colspan="2">
<widget class="QCheckBox" name="checkBoxStartInTray">
<property name="text">
<string>Start Bitmessage in the tray (don't show main window)</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="checkBoxMinimizeToTray">
<property name="text">
<string>Minimize to tray</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="checkBoxTrayOnClose">
<property name="text">
<string>Close to tray</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
@ -213,33 +157,33 @@
<string>Network Settings</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_4">
<item row="3" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Listening port</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>125</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="1">
<item row="0" column="0" alignment="Qt::AlignRight">
<widget class="QLabel" name="label">
<property name="text">
<string>Listen for connections on port:</string>
</property>
</widget>
</item>
<item row="0" column="2">
<item row="0" column="1" alignment="Qt::AlignLeft">
<widget class="QLineEdit" name="lineEditTCPPort">
<property name="maximumSize">
<size>
@ -249,94 +193,13 @@
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="2" column="0">
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Bandwidth limit</string>
</property>
<layout class="QGridLayout" name="gridLayout_9">
<item row="0" column="0" rowspan="2">
<spacer name="horizontalSpacer_11">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
<item row="0" column="3" alignment="Qt::AlignLeft">
<widget class="QCheckBox" name="checkBoxUPnP"/>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_24">
<item row="0" column="2" alignment="Qt::AlignRight">
<widget class="QLabel" name="labelUPnP">
<property name="text">
<string>Maximum download rate (kB/s): [0: unlimited]</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLineEdit" name="lineEditMaxDownloadRate">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>60</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_25">
<property name="text">
<string>Maximum upload rate (kB/s): [0: unlimited]</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLineEdit" name="lineEditMaxUploadRate">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>60</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="label_26">
<property name="text">
<string>Maximum outbound connections: [0: none]</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLineEdit" name="lineEditMaxOutboundConnections">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>60</width>
<height>16777215</height>
</size>
<string>UPnP:</string>
</property>
</widget>
</item>
@ -364,7 +227,11 @@
</widget>
</item>
<item row="1" column="2" colspan="2">
<widget class="QLineEdit" name="lineEditSocksHostname"/>
<widget class="QLineEdit" name="lineEditSocksHostname">
<property name="placeholderText">
<string notr="true">127.0.0.1</string>
</property>
</widget>
</item>
<item row="1" column="4">
<widget class="QLabel" name="label_4">
@ -446,18 +313,96 @@
</layout>
</widget>
</item>
<item row="3" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
<item row="2" column="0">
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Bandwidth limit</string>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
<layout class="QGridLayout" name="gridLayout_9">
<item row="2" column="2">
<widget class="QLineEdit" name="lineEditMaxOutboundConnections">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>60</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_25">
<property name="text">
<string>Maximum upload rate (kB/s): [0: unlimited]</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLineEdit" name="lineEditMaxUploadRate">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>60</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLineEdit" name="lineEditMaxDownloadRate">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>60</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="label_26">
<property name="text">
<string>Maximum outbound connections: [0: none]</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_24">
<property name="text">
<string>Maximum download rate (kB/s): [0: unlimited]</string>
</property>
</widget>
</item>
<item row="0" column="0" rowspan="2">
<spacer name="horizontalSpacer_11">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
@ -594,13 +539,13 @@
<string>Max acceptable difficulty</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_7">
<item row="0" column="0" colspan="3">
<widget class="QLabel" name="label_15">
<item row="2" column="1">
<widget class="QLabel" name="label_14">
<property name="text">
<string>Here you may set the maximum amount of work you are willing to do to send a message to another person. Setting these values to 0 means that any value is acceptable.</string>
<string>Maximum acceptable small message difficulty:</string>
</property>
<property name="wordWrap">
<bool>true</bool>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
@ -630,6 +575,32 @@
</property>
</widget>
</item>
<item row="0" column="0" colspan="3">
<widget class="QLabel" name="label_15">
<property name="text">
<string>Here you may set the maximum amount of work you are willing to do to send a message to another person. Setting these values to 0 means that any value is acceptable.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLineEdit" name="lineEditMaxAcceptableSmallMessageDifficulty">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>70</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLineEdit" name="lineEditMaxAcceptableTotalDifficulty">
<property name="sizePolicy">
@ -659,33 +630,142 @@
</property>
</spacer>
</item>
<item row="2" column="1">
<widget class="QLabel" name="label_14">
<property name="text">
<string>Maximum acceptable small message difficulty:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
<item row="3" column="0" colspan="3">
<widget class="QGroupBox" name="groupBoxSolvers">
<property name="title">
<string>Proof of work solver</string>
</property>
<layout class="QGridLayout" name="gridLayout_10">
<item row="0" column="0">
<widget class="QLabel" name="labelSolvers">
<property name="text">
<string>There are several worker modules to solve POW:</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayoutForkingSolver">
<item>
<widget class="QRadioButton" name="radioButtonForkingSolver">
<property name="text">
<string>Forking solver using multiple processes:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinBoxForkingSolverParallelism">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>4096</number>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacerForkingSolver">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="4" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayoutFastSolver">
<item>
<widget class="QRadioButton" name="radioButtonFastSolver">
<property name="text">
<string>Fast solver in C with multiple threads:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinBoxFastSolverParallelism">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>4096</number>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacerFastSolver">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="1" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayoutDumbSolver">
<item>
<widget class="QRadioButton" name="radioButtonDumbSolver">
<property name="text">
<string>Dumb solver</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacerDumbSolver">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="5" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayoutGPUSolver">
<item>
<widget class="QRadioButton" name="radioButtonGPUSolver">
<property name="text">
<string>GPU solver:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboBoxGPUVendor"/>
</item>
<item>
<spacer name="horizontalSpacerGPUSolver">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item row="2" column="2">
<widget class="QLineEdit" name="lineEditMaxAcceptableSmallMessageDifficulty">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>70</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
<item row="3" column="1">
<item row="5" column="0" colspan="3">
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
@ -741,7 +821,7 @@
<item row="2" column="2">
<widget class="QLineEdit" name="lineEditNamecoinHost"/>
</item>
<item row="3" column="0">
<item row="4" column="0">
<spacer name="horizontalSpacer_7">
<property name="orientation">
<enum>Qt::Horizontal</enum>
@ -754,7 +834,7 @@
</property>
</spacer>
</item>
<item row="4" column="0">
<item row="3" column="0">
<spacer name="horizontalSpacer_8">
<property name="orientation">
<enum>Qt::Horizontal</enum>
@ -1016,6 +1096,16 @@
</widget>
</widget>
</item>
<item row="1" column="0">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
@ -1033,9 +1123,7 @@
<tabstop>checkBoxSocksListen</tabstop>
<tabstop>buttonBox</tabstop>
</tabstops>
<resources>
<include location="bitmessage_icons.qrc"/>
</resources>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>

View File

@ -11,14 +11,13 @@ import defaults
from foldertree import AccountMixin
from helper_sql import *
from l10n import getTranslationLanguage
from openclpow import openclAvailable, openclEnabled
import paths
import proofofwork
from pyelliptic.openssl import OpenSSL
import queues
import network.stats
import state
from version import softwareVersion
import singleworker
# this is BM support address going to Peter Surda
OLD_SUPPORT_ADDRESS = 'BM-2cTkCtMYkrSPwFTpgcBrMrf5d8oZwvMZWK'
@ -113,10 +112,10 @@ def createSupportMessage(myapp):
if paths.frozen:
frozen = paths.frozen
portablemode = "True" if state.appdata == paths.lookupExeFolder() else "False"
cpow = "True" if proofofwork.bmpow else "False"
cpow = "True" if "fast" in singleworker.workProver.availableSolvers else "False"
openclpow = str(
BMConfigParser().safeGet('bitmessagesettings', 'opencl')
) if openclEnabled() else "None"
) if BMConfigParser().safeGetBoolean("bitmessagesettings", "powsolver") == "gpu" else "None"
locale = getTranslationLanguage()
socks = BMConfigParser().safeGet(
'bitmessagesettings', 'socksproxytype', "N/A")

View File

@ -26,14 +26,13 @@ class UISignaler(QThread):
"writeNewAddressToTable(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"), label, address, str(streamNumber))
elif command == 'updateStatusBar':
self.emit(SIGNAL("updateStatusBar(PyQt_PyObject)"), data)
elif command == 'updateSentItemStatusByToAddress':
toAddress, message = data
elif command == "updateSentItemStatusByToAddress":
self.emit(SIGNAL(
"updateSentItemStatusByToAddress(PyQt_PyObject,PyQt_PyObject)"), toAddress, message)
elif command == 'updateSentItemStatusByAckdata':
ackData, message = data
"updateSentItemStatusByToAddress(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"), *data)
elif command == "updateSentItemStatusByAckdata":
status, address, message = data
self.emit(SIGNAL(
"updateSentItemStatusByAckdata(PyQt_PyObject,PyQt_PyObject)"), ackData, message)
"updateSentItemStatusByAckdata(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"), *data)
elif command == 'displayNewInboxMessage':
inventoryHash, toAddress, fromAddress, subject, body = data
self.emit(SIGNAL(
@ -44,6 +43,8 @@ class UISignaler(QThread):
self.emit(SIGNAL(
"displayNewSentMessage(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"),
toAddress, fromLabel, fromAddress, subject, message, ackdata)
elif command == "deleteSentItemByAckData":
self.emit(SIGNAL("deleteSentItemByAckData(PyQt_PyObject)"), data)
elif command == 'updateNetworkStatusTab':
outbound, add, destination = data
self.emit(SIGNAL("updateNetworkStatusTab(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"), outbound, add, destination)
@ -74,6 +75,8 @@ class UISignaler(QThread):
elif command == 'alert':
title, text, exitAfterUserClicksOk = data
self.emit(SIGNAL("displayAlert(PyQt_PyObject, PyQt_PyObject, PyQt_PyObject)"), title, text, exitAfterUserClicksOk)
elif command == "updateWorkProverStatus":
self.emit(SIGNAL("updateWorkProverStatus(PyQt_PyObject)"), data)
else:
sys.stderr.write(
'Command sent to UISignaler not recognized: %s\n' % command)

View File

@ -11,3 +11,6 @@ def resource_path(resFile):
def load(resFile, widget):
uic.loadUi(resource_path(resFile), widget)
def loadType(resFile):
return uic.loadUiType(resource_path(resFile))

View File

@ -1,25 +0,0 @@
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Darwin)
CCFLAGS += -I/usr/local/Cellar/openssl/1.0.2d_1/include
LDFLAGS += -L/usr/local/Cellar/openssl/1.0.2d_1/lib
else ifeq ($(UNAME_S),MINGW32_NT-6.1)
CCFLAGS += -IC:\OpenSSL-1.0.2j-mingw\include -D_WIN32 -march=native
LDFLAGS += -static-libgcc -LC:\OpenSSL-1.0.2j-mingw\lib -lwsock32 -o bitmsghash32.dll -Wl,--out-implib,bitmsghash.a
else
LDFLAGS += -lpthread -o bitmsghash.so
endif
all: bitmsghash.so
powtest:
./testpow.py
bitmsghash.so: bitmsghash.o
${CXX} bitmsghash.o -shared -fPIC -lcrypto $(LDFLAGS)
bitmsghash.o:
${CXX} -Wall -O3 -march=native -fPIC $(CCFLAGS) -c bitmsghash.cpp
clean:
rm -f bitmsghash.o bitmsghash.so bitmsghash*.dll

View File

@ -1,14 +0,0 @@
all: bitmsghash.so
powtest:
./testpow.py
bitmsghash.so: bitmsghash.o
${CXX} bitmsghash.o -shared -fPIC -lpthread -lcrypto $(LDFLAGS) -o bitmsghash.so
bitmsghash.o:
${CXX} -Wall -O3 -march=native -fPIC $(CCFLAGS) -c bitmsghash.cpp
clean:
rm -f bitmsghash.o bitmsghash.so

View File

@ -1,2 +0,0 @@
all:
cl /I C:\OpenSSL-1.0.2j\include /INCREMENTAL bitmsghash.cpp /MT /link /DLL /OUT:bitmsghash32.dll /LIBPATH:C:\OpenSSL-1.0.2j\lib\ libeay32.lib ws2_32.lib

View File

@ -1,276 +0,0 @@
/*
* This is based on the John The Ripper SHA512 code, modified for double SHA512 and for use as a miner in Bitmessage.
* This software is originally Copyright (c) 2012 Myrice <qqlddg at gmail dot com>
* and it is hereby released to the general public under the following terms:
* Redistribution and use in source and binary forms, with or without modification, are permitted.
*/
#ifdef cl_khr_byte_addressable_store
#pragma OPENCL EXTENSION cl_khr_byte_addressable_store : disable
#endif
#define uint8_t unsigned char
#define uint32_t unsigned int
#define uint64_t unsigned long
#define SALT_SIZE 0
#define BINARY_SIZE 8
#define FULL_BINARY_SIZE 64
#define PLAINTEXT_LENGTH 72
#define CIPHERTEXT_LENGTH 128
/// Warning: This version of SWAP64(n) is slow and avoid bugs on AMD GPUs(7970)
// #define SWAP64(n) as_ulong(as_uchar8(n).s76543210)
#define SWAP64(n) \
(((n) << 56) \
| (((n) & 0xff00) << 40) \
| (((n) & 0xff0000) << 24) \
| (((n) & 0xff000000) << 8) \
| (((n) >> 8) & 0xff000000) \
| (((n) >> 24) & 0xff0000) \
| (((n) >> 40) & 0xff00) \
| ((n) >> 56))
#define rol(x,n) ((x << n) | (x >> (64-n)))
#define ror(x,n) ((x >> n) | (x << (64-n)))
#define Ch(x,y,z) ((x & y) ^ ( (~x) & z))
#define Maj(x,y,z) ((x & y) ^ (x & z) ^ (y & z))
#define Sigma0(x) ((ror(x,28)) ^ (ror(x,34)) ^ (ror(x,39)))
#define Sigma1(x) ((ror(x,14)) ^ (ror(x,18)) ^ (ror(x,41)))
#define sigma0(x) ((ror(x,1)) ^ (ror(x,8)) ^(x>>7))
#define sigma1(x) ((ror(x,19)) ^ (ror(x,61)) ^(x>>6))
typedef struct { // notice memory align problem
uint64_t H[8];
uint32_t buffer[32]; //1024 bits
uint32_t buflen;
} sha512_ctx;
typedef struct {
uint64_t target;
char v[PLAINTEXT_LENGTH+1];
} sha512_key;
/* Macros for reading/writing chars from int32's */
#define PUTCHAR(buf, index, val) (buf)[(index)>>2] = ((buf)[(index)>>2] & ~(0xffU << (((index) & 3) << 3))) + ((val) << (((index) & 3) << 3))
__constant uint64_t k[] = {
0x428a2f98d728ae22UL, 0x7137449123ef65cdUL, 0xb5c0fbcfec4d3b2fUL,
0xe9b5dba58189dbbcUL,
0x3956c25bf348b538UL, 0x59f111f1b605d019UL, 0x923f82a4af194f9bUL,
0xab1c5ed5da6d8118UL,
0xd807aa98a3030242UL, 0x12835b0145706fbeUL, 0x243185be4ee4b28cUL,
0x550c7dc3d5ffb4e2UL,
0x72be5d74f27b896fUL, 0x80deb1fe3b1696b1UL, 0x9bdc06a725c71235UL,
0xc19bf174cf692694UL,
0xe49b69c19ef14ad2UL, 0xefbe4786384f25e3UL, 0x0fc19dc68b8cd5b5UL,
0x240ca1cc77ac9c65UL,
0x2de92c6f592b0275UL, 0x4a7484aa6ea6e483UL, 0x5cb0a9dcbd41fbd4UL,
0x76f988da831153b5UL,
0x983e5152ee66dfabUL, 0xa831c66d2db43210UL, 0xb00327c898fb213fUL,
0xbf597fc7beef0ee4UL,
0xc6e00bf33da88fc2UL, 0xd5a79147930aa725UL, 0x06ca6351e003826fUL,
0x142929670a0e6e70UL,
0x27b70a8546d22ffcUL, 0x2e1b21385c26c926UL, 0x4d2c6dfc5ac42aedUL,
0x53380d139d95b3dfUL,
0x650a73548baf63deUL, 0x766a0abb3c77b2a8UL, 0x81c2c92e47edaee6UL,
0x92722c851482353bUL,
0xa2bfe8a14cf10364UL, 0xa81a664bbc423001UL, 0xc24b8b70d0f89791UL,
0xc76c51a30654be30UL,
0xd192e819d6ef5218UL, 0xd69906245565a910UL, 0xf40e35855771202aUL,
0x106aa07032bbd1b8UL,
0x19a4c116b8d2d0c8UL, 0x1e376c085141ab53UL, 0x2748774cdf8eeb99UL,
0x34b0bcb5e19b48a8UL,
0x391c0cb3c5c95a63UL, 0x4ed8aa4ae3418acbUL, 0x5b9cca4f7763e373UL,
0x682e6ff3d6b2b8a3UL,
0x748f82ee5defb2fcUL, 0x78a5636f43172f60UL, 0x84c87814a1f0ab72UL,
0x8cc702081a6439ecUL,
0x90befffa23631e28UL, 0xa4506cebde82bde9UL, 0xbef9a3f7b2c67915UL,
0xc67178f2e372532bUL,
0xca273eceea26619cUL, 0xd186b8c721c0c207UL, 0xeada7dd6cde0eb1eUL,
0xf57d4f7fee6ed178UL,
0x06f067aa72176fbaUL, 0x0a637dc5a2c898a6UL, 0x113f9804bef90daeUL,
0x1b710b35131c471bUL,
0x28db77f523047d84UL, 0x32caab7b40c72493UL, 0x3c9ebe0a15c9bebcUL,
0x431d67c49c100d4cUL,
0x4cc5d4becb3e42b6UL, 0x597f299cfc657e2aUL, 0x5fcb6fab3ad6faecUL,
0x6c44198c4a475817UL,
};
static void setup_ctx(sha512_ctx* ctx, const char * password, uint8_t pass_len)
{
uint32_t* b32 = ctx->buffer;
//set password to buffer
for (uint32_t i = 0; i < pass_len; i++) {
PUTCHAR(b32,i,password[i]);
}
ctx->buflen = pass_len;
//append 1 to ctx buffer
uint32_t length = ctx->buflen;
PUTCHAR(b32, length, 0x80);
while((++length & 3) != 0) {
PUTCHAR(b32, length, 0);
}
uint32_t* buffer32 = b32+(length>>2);
for(uint32_t i = length; i < 128; i+=4) {// append 0 to 128
*buffer32++=0;
}
//append length to buffer
uint64_t *buffer64 = (uint64_t *)ctx->buffer;
buffer64[15] = SWAP64(((uint64_t) ctx->buflen) * 8);
}
inline uint64_t sha512(char* password)
{
__private sha512_ctx ctx;
setup_ctx(&ctx, password, 72);
// sha512 main`
int i;
uint64_t a = 0x6a09e667f3bcc908UL;
uint64_t b = 0xbb67ae8584caa73bUL;
uint64_t c = 0x3c6ef372fe94f82bUL;
uint64_t d = 0xa54ff53a5f1d36f1UL;
uint64_t e = 0x510e527fade682d1UL;
uint64_t f = 0x9b05688c2b3e6c1fUL;
uint64_t g = 0x1f83d9abfb41bd6bUL;
uint64_t h = 0x5be0cd19137e2179UL;
__private uint64_t w[16];
uint64_t *data = (uint64_t *) ctx.buffer;
for (i = 0; i < 16; i++)
w[i] = SWAP64(data[i]);
uint64_t t1, t2;
for (i = 0; i < 16; i++) {
t1 = k[i] + w[i] + h + Sigma1(e) + Ch(e, f, g);
t2 = Maj(a, b, c) + Sigma0(a);
h = g;
g = f;
f = e;
e = d + t1;
d = c;
c = b;
b = a;
a = t1 + t2;
}
for (i = 16; i < 80; i++) {
w[i & 15] =sigma1(w[(i - 2) & 15]) + sigma0(w[(i - 15) & 15]) + w[(i -16) & 15] + w[(i - 7) & 15];
t1 = k[i] + w[i & 15] + h + Sigma1(e) + Ch(e, f, g);
t2 = Maj(a, b, c) + Sigma0(a);
h = g;
g = f;
f = e;
e = d + t1;
d = c;
c = b;
b = a;
a = t1 + t2;
}
uint64_t finalhash[8];
finalhash[0] = SWAP64(a + 0x6a09e667f3bcc908UL);
finalhash[1] = SWAP64(b + 0xbb67ae8584caa73bUL);
finalhash[2] = SWAP64(c + 0x3c6ef372fe94f82bUL);
finalhash[3] = SWAP64(d + 0xa54ff53a5f1d36f1UL);
finalhash[4] = SWAP64(e + 0x510e527fade682d1UL);
finalhash[5] = SWAP64(f + 0x9b05688c2b3e6c1fUL);
finalhash[6] = SWAP64(g + 0x1f83d9abfb41bd6bUL);
finalhash[7] = SWAP64(h + 0x5be0cd19137e2179UL);
setup_ctx(&ctx, (char*) finalhash, 64);
a = 0x6a09e667f3bcc908UL;
b = 0xbb67ae8584caa73bUL;
c = 0x3c6ef372fe94f82bUL;
d = 0xa54ff53a5f1d36f1UL;
e = 0x510e527fade682d1UL;
f = 0x9b05688c2b3e6c1fUL;
g = 0x1f83d9abfb41bd6bUL;
h = 0x5be0cd19137e2179UL;
data = (uint64_t *) ctx.buffer;
//((uint64_t*)ctx.buffer)[8] = SWAP64((uint64_t)0x80);
for (i = 0; i < 16; i++)
w[i] = SWAP64(data[i]);
for (i = 0; i < 16; i++) {
t1 = k[i] + w[i] + h + Sigma1(e) + Ch(e, f, g);
t2 = Maj(a, b, c) + Sigma0(a);
h = g;
g = f;
f = e;
e = d + t1;
d = c;
c = b;
b = a;
a = t1 + t2;
}
for (i = 16; i < 80; i++) {
w[i & 15] =sigma1(w[(i - 2) & 15]) + sigma0(w[(i - 15) & 15]) + w[(i -16) & 15] + w[(i - 7) & 15];
t1 = k[i] + w[i & 15] + h + Sigma1(e) + Ch(e, f, g);
t2 = Maj(a, b, c) + Sigma0(a);
h = g;
g = f;
f = e;
e = d + t1;
d = c;
c = b;
b = a;
a = t1 + t2;
}
return SWAP64(a + 0x6a09e667f3bcc908UL);
}
__kernel void kernel_sha512(__global const sha512_key *password,__global uint64_t *hash, uint64_t start)
{
uint64_t idx = get_global_id(0);
if (idx == 0 && start == 0) {
*hash = 0;
}
uint64_t winval;
uint64_t junk[9];
__global uint64_t * source = (__global uint64_t*) password->v;
for (int i = 1; i < 9; i++) {
junk[i] = source[i];
}
junk[0] = SWAP64(idx + (start));
winval = sha512((char*)junk);
if (SWAP64(winval) < password->target) {
*hash = SWAP64(junk[0]);
}
}

View File

@ -1,165 +0,0 @@
// bitmessage cracker, build with g++ or MSVS to a shared library, use included python code for usage under bitmessage
#ifdef _WIN32
#include "Winsock.h"
#include "Windows.h"
#define uint64_t unsigned __int64
#else
#include <arpa/inet.h>
#include <pthread.h>
#include <stdint.h>
#endif
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#if defined(__APPLE__) || defined(__FreeBSD__) || defined (__DragonFly__) || defined (__OpenBSD__) || defined (__NetBSD__)
#include <sys/types.h>
#include <sys/sysctl.h>
#endif
#include "openssl/sha.h"
#define HASH_SIZE 64
#define BUFLEN 16384
#if defined(__GNUC__)
#define EXPORT __attribute__ ((__visibility__("default")))
#elif defined(_WIN32)
#define EXPORT __declspec(dllexport)
#endif
#ifndef __APPLE__
#define ntohll(x) ( ( (uint64_t)(ntohl( (unsigned int)((x << 32) >> 32) )) << 32) | ntohl( ((unsigned int)(x >> 32)) ) )
#endif
unsigned long long max_val;
unsigned char *initialHash;
unsigned long long successval = 0;
unsigned int numthreads = 0;
#ifdef _WIN32
DWORD WINAPI threadfunc(LPVOID param) {
#else
void * threadfunc(void* param) {
#endif
unsigned int incamt = *((unsigned int*)param);
SHA512_CTX sha;
unsigned char buf[HASH_SIZE + sizeof(uint64_t)] = { 0 };
unsigned char output[HASH_SIZE] = { 0 };
memcpy(buf + sizeof(uint64_t), initialHash, HASH_SIZE);
unsigned long long tmpnonce = incamt;
unsigned long long * nonce = (unsigned long long *)buf;
unsigned long long * hash = (unsigned long long *)output;
while (successval == 0) {
tmpnonce += numthreads;
(*nonce) = ntohll(tmpnonce); /* increment nonce */
SHA512_Init(&sha);
SHA512_Update(&sha, buf, HASH_SIZE + sizeof(uint64_t));
SHA512_Final(output, &sha);
SHA512_Init(&sha);
SHA512_Update(&sha, output, HASH_SIZE);
SHA512_Final(output, &sha);
if (ntohll(*hash) < max_val) {
successval = tmpnonce;
}
}
#ifdef _WIN32
return 0;
#else
return NULL;
#endif
}
void getnumthreads()
{
#ifdef _WIN32
DWORD_PTR dwProcessAffinity, dwSystemAffinity;
#elif __linux__
cpu_set_t dwProcessAffinity;
#elif __OpenBSD__
int mib[2], core_count = 0;
int dwProcessAffinity = 0;
size_t len2;
#else
int dwProcessAffinity = 0;
int32_t core_count = 0;
#endif
size_t len = sizeof(dwProcessAffinity);
if (numthreads > 0)
return;
#ifdef _WIN32
GetProcessAffinityMask(GetCurrentProcess(), &dwProcessAffinity, &dwSystemAffinity);
#elif __linux__
sched_getaffinity(0, len, &dwProcessAffinity);
#elif __OpenBSD__
len2 = sizeof(core_count);
mib[0] = CTL_HW;
mib[1] = HW_NCPU;
if (sysctl(mib, 2, &core_count, &len2, 0, 0) == 0)
numthreads = core_count;
#else
if (sysctlbyname("hw.logicalcpu", &core_count, &len, 0, 0) == 0)
numthreads = core_count;
else if (sysctlbyname("hw.ncpu", &core_count, &len, 0, 0) == 0)
numthreads = core_count;
#endif
for (unsigned int i = 0; i < len * 8; i++)
#if defined(_WIN32)
#if defined(_MSC_VER)
if (dwProcessAffinity & (1i64 << i))
#else // CYGWIN/MINGW
if (dwProcessAffinity & (1ULL << i))
#endif
#elif defined __linux__
if (CPU_ISSET(i, &dwProcessAffinity))
#else
if (dwProcessAffinity & (1 << i))
#endif
numthreads++;
if (numthreads == 0) // something failed
numthreads = 1;
printf("Number of threads: %i\n", (int)numthreads);
}
extern "C" EXPORT unsigned long long BitmessagePOW(unsigned char * starthash, unsigned long long target)
{
successval = 0;
max_val = target;
getnumthreads();
initialHash = (unsigned char *)starthash;
# ifdef _WIN32
HANDLE* threads = (HANDLE*)calloc(sizeof(HANDLE), numthreads);
# else
pthread_t* threads = (pthread_t*)calloc(sizeof(pthread_t), numthreads);
struct sched_param schparam;
schparam.sched_priority = 0;
# endif
unsigned int *threaddata = (unsigned int *)calloc(sizeof(unsigned int), numthreads);
for (unsigned int i = 0; i < numthreads; i++) {
threaddata[i] = i;
# ifdef _WIN32
threads[i] = CreateThread(NULL, 0, threadfunc, (LPVOID)&threaddata[i], 0, NULL);
SetThreadPriority(threads[i], THREAD_PRIORITY_IDLE);
# else
pthread_create(&threads[i], NULL, threadfunc, (void*)&threaddata[i]);
# ifdef __linux__
pthread_setschedparam(threads[i], SCHED_IDLE, &schparam);
# else
pthread_setschedparam(threads[i], SCHED_RR, &schparam);
# endif
# endif
}
# ifdef _WIN32
WaitForMultipleObjects(numthreads, threads, TRUE, INFINITE);
# else
for (unsigned int i = 0; i < numthreads; i++) {
pthread_join(threads[i], NULL);
}
# endif
free(threads);
free(threaddata);
return successval;
}

View File

@ -9,7 +9,8 @@ mainscript = ["bitmessagemain.py"]
DATA_FILES = [
('', ['sslkeys', 'images']),
('bitmsghash', ['bitmsghash/bitmsghash.cl', 'bitmsghash/bitmsghash.so']),
("workprover", ["gpusolver.cl"]),
("workprover/fastsolver", ["libfastsolver.so"]),
('translations', glob('translations/*.qm')),
('ui', glob('bitmessageqt/*.ui')),
('translations', glob(str(QtCore.QLibraryInfo.location(QtCore.QLibraryInfo.TranslationsPath)) + '/qt_??.qm')),

View File

@ -201,12 +201,10 @@ class addressGenerator(threading.Thread, StoppableThread):
queues.UISignalQueue.put(('writeNewAddressToTable', (
label, address, streamNumber)))
shared.reloadMyAddressHashes()
if addressVersionNumber == 3:
queues.workerQueue.put((
'sendOutOrStoreMyV3Pubkey', ripe.digest()))
elif addressVersionNumber == 4:
queues.workerQueue.put((
'sendOutOrStoreMyV4Pubkey', address))
# If this is a chan address, the worker thread won't send out the pubkey over the network
queues.workerQueue.put(("sendMyPubkey", address))
elif command == 'createDeterministicAddresses' \
or command == 'getDeterministicAddress' \
@ -366,15 +364,11 @@ class addressGenerator(threading.Thread, StoppableThread):
encodeVarint(streamNumber) + ripe.digest()
).digest()).digest()[32:]
shared.myAddressesByTag[tag] = address
if addressVersionNumber == 3:
# If this is a chan address,
# the worker thread won't send out
# the pubkey over the network.
queues.workerQueue.put((
'sendOutOrStoreMyV3Pubkey', ripe.digest()))
elif addressVersionNumber == 4:
queues.workerQueue.put((
'sendOutOrStoreMyV4Pubkey', address))
# If this is a chan address, the worker thread won't send out the pubkey over the network
queues.workerQueue.put(("sendMyPubkey", address))
queues.UISignalQueue.put((
'updateStatusBar',
tr._translate(

View File

@ -25,6 +25,7 @@ import state
import tr
from debug import logger
import l10n
import workprover.utils
class objectProcessor(threading.Thread):
@ -114,30 +115,30 @@ class objectProcessor(threading.Thread):
break
def checkackdata(self, data):
# Let's check whether this is a message acknowledgement bound for us.
if len(data) < 32:
ackData = data[16: ]
if len(ackData) < 16 or ackData not in state.watchedAckData:
logger.debug("This object is not an acknowledgement bound for us")
return
# bypass nonce and time, retain object type/version/stream + body
readPosition = 16
logger.info("This object is an acknowledgement bound for us")
if data[readPosition:] in shared.ackdataForWhichImWatching:
logger.info('This object is an acknowledgement bound for me.')
del shared.ackdataForWhichImWatching[data[readPosition:]]
sqlExecute(
'UPDATE sent SET status=?, lastactiontime=?'
' WHERE ackdata=?',
'ackreceived', int(time.time()), data[readPosition:])
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata',
(data[readPosition:],
tr._translate(
"MainWindow",
"Acknowledgement of the message received %1"
).arg(l10n.formatTimestamp()))
))
else:
logger.debug('This object is not an acknowledgement bound for me.')
state.watchedAckData -= {ackData}
sqlExecute("""
UPDATE "sent" SET "status" = 'ackreceived', "lastactiontime" = ?
WHERE "status" IN ('doingmsgpow', 'msgsent') AND "ackdata" == ?;
""", int(time.time()), ackData)
queues.UISignalQueue.put(("updateSentItemStatusByAckdata", (
"ackreceived",
ackData,
tr._translate(
"MainWindow",
"Acknowledgement of the message received %1"
).arg(l10n.formatTimestamp())
)))
def processgetpubkey(self, data):
if len(data) > 200:
@ -237,12 +238,8 @@ class objectProcessor(threading.Thread):
'Found getpubkey-requested-hash in my list of EC hashes.'
' Telling Worker thread to do the POW for a pubkey message'
' and send it out.')
if requestedAddressVersionNumber == 2:
queues.workerQueue.put(('doPOWForMyV2Pubkey', requestedHash))
elif requestedAddressVersionNumber == 3:
queues.workerQueue.put(('sendOutOrStoreMyV3Pubkey', requestedHash))
elif requestedAddressVersionNumber == 4:
queues.workerQueue.put(('sendOutOrStoreMyV4Pubkey', myAddress))
queues.workerQueue.put(("sendMyPubkey", myAddress))
def processpubkey(self, data):
pubkeyProcessingStartTime = time.time()
@ -396,20 +393,23 @@ class objectProcessor(threading.Thread):
' Sanity check failed.')
return
tag = data[readPosition:readPosition + 32]
if tag not in state.neededPubkeys:
logger.info(
'We don\'t need this v4 pubkey. We didn\'t ask for it.')
return
tag = data[readPosition: readPosition + 32]
attributes = state.neededPubkeys.get(tag, None)
# Let us try to decrypt the pubkey
toAddress, _ = state.neededPubkeys[tag]
if shared.decryptAndCheckPubkeyPayload(data, toAddress) == \
'successful':
# At this point we know that we have been waiting on this
# pubkey. This function will command the workerThread
# to start work on the messages that require it.
self.possibleNewPubkey(toAddress)
if attributes is None:
logger.info("We don't need this v4 pubkey. We didn't ask for it")
else:
address, cryptor = attributes
storedData = protocol.decryptAndCheckV4Pubkey(data, address, cryptor)
if storedData is not None:
sqlExecute("""
INSERT INTO "pubkeys" ("address", "addressversion", "transmitdata", "time", "usedpersonally")
VALUES (?, 4, ?, ?, 'yes');
""", address, storedData, int(time.time()))
self.possibleNewPubkey(address)
# Display timing data
timeRequiredToProcessPubkey = time.time(
@ -608,16 +608,16 @@ class objectProcessor(threading.Thread):
# If the toAddress version number is 3 or higher and not one of
# my chan addresses:
if decodeAddress(toAddress)[1] >= 3 \
and not BMConfigParser().safeGetBoolean(toAddress, 'chan'):
and not BMConfigParser().has_section(toAddress):
# If I'm not friendly with this person:
if not shared.isAddressInMyAddressBookSubscriptionsListOrWhitelist(fromAddress):
requiredNonceTrialsPerByte = BMConfigParser().getint(
toAddress, 'noncetrialsperbyte')
requiredPayloadLengthExtraBytes = BMConfigParser().getint(
toAddress, 'payloadlengthextrabytes')
if not protocol.isProofOfWorkSufficient(
data, requiredNonceTrialsPerByte,
requiredPayloadLengthExtraBytes):
byteDifficulty = BMConfigParser().getint(toAddress, "noncetrialsperbyte")
lengthExtension = BMConfigParser().getint(toAddress, "payloadlengthextrabytes")
byteDifficulty = max(defaults.networkDefaultProofOfWorkNonceTrialsPerByte, byteDifficulty)
lengthExtension = max(defaults.networkDefaultPayloadLengthExtraBytes, lengthExtension)
if not workprover.utils.checkWorkSufficient(data, byteDifficulty, lengthExtension):
logger.info(
'Proof of work in msg is insufficient only because'
' it does not meet our higher requirement.')
@ -748,7 +748,7 @@ class objectProcessor(threading.Thread):
not BMConfigParser().safeGetBoolean(toAddress, 'dontsendack')
and not BMConfigParser().safeGetBoolean(toAddress, 'chan')
):
shared.checkAndShareObjectWithPeers(ackData[24:])
protocol.checkAndShareObjectWithPeers(ackData[24:])
# Display timing data
timeRequiredToAttemptToDecryptMessage = time.time(
@ -1015,42 +1015,43 @@ class objectProcessor(threading.Thread):
have been waiting for. Let's check.
"""
# For address versions <= 3, we wait on a key with the correct
# address version, stream number and RIPE hash.
_, addressVersion, streamNumber, ripe = decodeAddress(address)
if addressVersion <= 3:
if address in state.neededPubkeys:
del state.neededPubkeys[address]
self.sendMessages(address)
else:
logger.debug(
'We don\'t need this pub key. We didn\'t ask for it.'
' For address: %s', address)
# For address versions >= 4, we wait on a pubkey with the correct tag.
# Let us create the tag from the address and see if we were waiting
# for it.
elif addressVersion >= 4:
tag = hashlib.sha512(hashlib.sha512(
encodeVarint(addressVersion) + encodeVarint(streamNumber)
+ ripe).digest()
).digest()[32:]
if tag in state.neededPubkeys:
del state.neededPubkeys[tag]
self.sendMessages(address)
status, version, stream, ripe = decodeAddress(address)
def sendMessages(self, address):
"""
This function is called by the possibleNewPubkey function when
that function sees that we now have the necessary pubkey
to send one or more messages.
"""
logger.info('We have been awaiting the arrival of this pubkey.')
sqlExecute(
"UPDATE sent SET status='doingmsgpow', retrynumber=0"
" WHERE toaddress=?"
" AND (status='awaitingpubkey' OR status='doingpubkeypow')"
" AND folder='sent'", address)
queues.workerQueue.put(('sendmessage', ''))
if version <= 3:
needed = address in state.neededPubkeys
state.neededPubkeys.pop(address, None)
elif version == 4:
secretEncryptionKey, tag = protocol.calculateAddressTag(version, stream, ripe)
needed = tag in state.neededPubkeys
state.neededPubkeys.pop(tag, None)
if needed:
logger.info("We have been awaiting the arrival of this pubkey")
sqlExecute("""
UPDATE "sent" SET "status" = 'msgqueued'
WHERE "status" IN ('doingpubkeypow', 'awaitingpubkey') AND "toaddress" == ? AND "folder" == 'sent';
""", address)
queues.workerQueue.put(("sendmessage", ))
queued = sqlQuery("""
SELECT "ackdata" FROM "sent"
WHERE "status" == 'msgqueued' AND "toaddress" == ? AND "folder" == 'sent';
""", address)
for i, in queued:
queues.UISignalQueue.put(("updateSentItemStatusByAckdata", (
"msgqueued",
i,
tr._translate(
"MainWindow",
"Queued."
)
)))
else:
logger.debug("We don't need this pubkey, we didn't ask for it: %s", address)
def ackDataHasAValidHeader(self, ackData):
if len(ackData) < protocol.Header.size:
@ -1075,7 +1076,7 @@ class objectProcessor(threading.Thread):
# The largest message should be either an inv or a getdata
# message at 1.6 MB in size.
# That doesn't mean that the object may be that big. The
# shared.checkAndShareObjectWithPeers function will verify
# protocol.checkAndShareObjectWithPeers function will verify
# that it is no larger than 2^18 bytes.
return False
# test the checksum in the message.

View File

@ -35,6 +35,45 @@ import knownnodes
import queues
import state
def resendStaleMessages():
staleMessages = sqlQuery("""
SELECT "toaddress", "ackdata", "status" FROM "sent"
WHERE "status" IN ('awaitingpubkey', 'msgsent') AND "sleeptill" < ? AND "senttime" > ? AND "folder" == 'sent';
""", int(time.time()), int(time.time()) - shared.maximumLengthOfTimeToBotherResendingMessages)
resendMessages = False
for destination, ackData, status in staleMessages:
if status == "awaitingpubkey":
logger.info("Retrying getpubkey request for %s", destination)
sqlExecute("""
UPDATE "sent" SET "status" = 'msgqueued'
WHERE "status" == 'awaitingpubkey' AND "ackdata" == ?;
""", ackData)
elif status == "msgsent":
state.watchedAckData -= {ackData}
sqlExecute("""
UPDATE "sent" SET "status" = 'msgqueued'
WHERE "status" == 'msgsent' AND "ackdata" == ?;
""", ackData)
queues.UISignalQueue.put(("updateSentItemStatusByAckdata", (
"msgqueued",
ackData,
tr._translate(
"MainWindow",
"Queued."
)
)))
resendMessages = True
if resendMessages:
logger.info("Resending old messages with undelivered acks or unknown pubkeys")
queues.workerQueue.put(("sendmessage", ))
class singleCleaner(threading.Thread, StoppableThread):
cycleLength = 300
@ -92,28 +131,8 @@ class singleCleaner(threading.Thread, StoppableThread):
# Let us resend getpubkey objects if we have not yet heard
# a pubkey, and also msg objects if we have not yet heard
# an acknowledgement
queryreturn = sqlQuery(
"SELECT toaddress, ackdata, status FROM sent"
" WHERE ((status='awaitingpubkey' OR status='msgsent')"
" AND folder='sent' AND sleeptill<? AND senttime>?)",
int(time.time()),
int(time.time())
- shared.maximumLengthOfTimeToBotherResendingMessages
)
for row in queryreturn:
if len(row) < 2:
logger.error(
'Something went wrong in the singleCleaner thread:'
' a query did not return the requested fields. %r',
row
)
self.stop.wait(3)
break
toAddress, ackData, status = row
if status == 'awaitingpubkey':
resendPubkeyRequest(toAddress)
elif status == 'msgsent':
resendMsg(ackData)
resendStaleMessages()
# cleanup old nodes
now = int(time.time())
@ -189,41 +208,3 @@ class singleCleaner(threading.Thread, StoppableThread):
if state.shutdown == 0:
self.stop.wait(singleCleaner.cycleLength)
def resendPubkeyRequest(address):
logger.debug(
'It has been a long time and we haven\'t heard a response to our'
' getpubkey request. Sending again.'
)
try:
# We need to take this entry out of the neededPubkeys structure
# because the queues.workerQueue checks to see whether the entry
# is already present and will not do the POW and send the message
# because it assumes that it has already done it recently.
del state.neededPubkeys[address]
except:
pass
queues.UISignalQueue.put((
'updateStatusBar',
'Doing work necessary to again attempt to request a public key...'))
sqlExecute(
'''UPDATE sent SET status='msgqueued' WHERE toaddress=?''',
address)
queues.workerQueue.put(('sendmessage', ''))
def resendMsg(ackdata):
logger.debug(
'It has been a long time and we haven\'t heard an acknowledgement'
' to our msg. Sending again.'
)
sqlExecute(
'''UPDATE sent SET status='msgqueued' WHERE ackdata=?''',
ackdata)
queues.workerQueue.put(('sendmessage', ''))
queues.UISignalQueue.put((
'updateStatusBar',
'Doing work necessary to again attempt to deliver a message...'
))

View File

@ -1,1418 +0,0 @@
from __future__ import division
import time
import threading
import hashlib
from struct import pack
# used when the API must execute an outside program
from subprocess import call # nosec
from binascii import hexlify, unhexlify
import tr
import l10n
import protocol
import queues
import state
import shared
import defaults
import highlevelcrypto
import proofofwork
import helper_inbox
import helper_random
import helper_msgcoding
from bmconfigparser import BMConfigParser
from debug import logger
from inventory import Inventory
from addresses import (
decodeAddress, encodeVarint, decodeVarint, calculateInventoryHash
)
# from helper_generic import addDataPadding
from helper_threading import StoppableThread
from helper_sql import sqlQuery, sqlExecute
# This thread, of which there is only one, does the heavy lifting:
# calculating POWs.
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)
class singleWorker(threading.Thread, StoppableThread):
def __init__(self):
# QThread.__init__(self, parent)
threading.Thread.__init__(self, name="singleWorker")
self.initStop()
proofofwork.init()
def stopThread(self):
try:
queues.workerQueue.put(("stopThread", "data"))
except:
pass
super(singleWorker, self).stopThread()
def run(self):
while not state.sqlReady and state.shutdown == 0:
self.stop.wait(2)
if state.shutdown > 0:
return
# Initialize the neededPubkeys dictionary.
queryreturn = sqlQuery(
'''SELECT DISTINCT toaddress FROM sent'''
''' WHERE (status='awaitingpubkey' AND folder='sent')''')
for row in queryreturn:
toAddress, = row
# toStatus
_, toAddressVersionNumber, toStreamNumber, toRipe = \
decodeAddress(toAddress)
if toAddressVersionNumber <= 3:
state.neededPubkeys[toAddress] = 0
elif toAddressVersionNumber >= 4:
doubleHashOfAddressData = hashlib.sha512(hashlib.sha512(
encodeVarint(toAddressVersionNumber) +
encodeVarint(toStreamNumber) + toRipe
).digest()).digest()
# Note that this is the first half of the sha512 hash.
privEncryptionKey = doubleHashOfAddressData[:32]
tag = doubleHashOfAddressData[32:]
# We'll need this for when we receive a pubkey reply:
# it will be encrypted and we'll need to decrypt it.
state.neededPubkeys[tag] = (
toAddress,
highlevelcrypto.makeCryptor(
hexlify(privEncryptionKey))
)
# Initialize the shared.ackdataForWhichImWatching data structure
queryreturn = sqlQuery(
'''SELECT ackdata FROM sent WHERE status = 'msgsent' ''')
for row in queryreturn:
ackdata, = row
logger.info('Watching for ackdata ' + hexlify(ackdata))
shared.ackdataForWhichImWatching[ackdata] = 0
# Fix legacy (headerless) watched ackdata to include header
for oldack in shared.ackdataForWhichImWatching.keys():
if (len(oldack) == 32):
# attach legacy header, always constant (msg/1/1)
newack = '\x00\x00\x00\x02\x01\x01' + oldack
shared.ackdataForWhichImWatching[newack] = 0
sqlExecute(
'UPDATE sent SET ackdata=? WHERE ackdata=?',
newack, oldack
)
del shared.ackdataForWhichImWatching[oldack]
# give some time for the GUI to start
# before we start on existing POW tasks.
self.stop.wait(10)
if state.shutdown == 0:
# just in case there are any pending tasks for msg
# messages that have yet to be sent.
queues.workerQueue.put(('sendmessage', ''))
# just in case there are any tasks for Broadcasts
# that have yet to be sent.
queues.workerQueue.put(('sendbroadcast', ''))
while state.shutdown == 0:
self.busy = 0
command, data = queues.workerQueue.get()
self.busy = 1
if command == 'sendmessage':
try:
self.sendMsg()
except:
pass
elif command == 'sendbroadcast':
try:
self.sendBroadcast()
except:
pass
elif command == 'doPOWForMyV2Pubkey':
try:
self.doPOWForMyV2Pubkey(data)
except:
pass
elif command == 'sendOutOrStoreMyV3Pubkey':
try:
self.sendOutOrStoreMyV3Pubkey(data)
except:
pass
elif command == 'sendOutOrStoreMyV4Pubkey':
try:
self.sendOutOrStoreMyV4Pubkey(data)
except:
pass
elif command == 'resetPoW':
try:
proofofwork.resetPoW()
except:
pass
elif command == 'stopThread':
self.busy = 0
return
else:
logger.error(
'Probable programming error: The command sent'
' to the workerThread is weird. It is: %s\n',
command
)
queues.workerQueue.task_done()
logger.info("Quitting...")
def _getKeysForAddress(self, address):
privSigningKeyBase58 = BMConfigParser().get(
address, 'privsigningkey')
privEncryptionKeyBase58 = BMConfigParser().get(
address, 'privencryptionkey')
privSigningKeyHex = hexlify(shared.decodeWalletImportFormat(
privSigningKeyBase58))
privEncryptionKeyHex = hexlify(shared.decodeWalletImportFormat(
privEncryptionKeyBase58))
# 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.
pubSigningKey = unhexlify(highlevelcrypto.privToPub(
privSigningKeyHex))[1:]
pubEncryptionKey = unhexlify(highlevelcrypto.privToPub(
privEncryptionKeyHex))[1:]
return privSigningKeyHex, privEncryptionKeyHex, \
pubSigningKey, pubEncryptionKey
def _doPOWDefaults(self, payload, TTL,
log_prefix='',
log_time=False):
target = 2 ** 64 / (
defaults.networkDefaultProofOfWorkNonceTrialsPerByte * (
len(payload) + 8 +
defaults.networkDefaultPayloadLengthExtraBytes + ((
TTL * (
len(payload) + 8 +
defaults.networkDefaultPayloadLengthExtraBytes
)) / (2 ** 16))
))
initialHash = hashlib.sha512(payload).digest()
logger.info(
'%s Doing proof of work... TTL set to %s', log_prefix, TTL)
if log_time:
start_time = time.time()
trialValue, nonce = proofofwork.run(target, initialHash)
logger.info(
'%s Found proof of work %s Nonce: %s',
log_prefix, trialValue, nonce
)
try:
delta = time.time() - start_time
logger.info(
'PoW took %.1f seconds, speed %s.',
delta, sizeof_fmt(nonce / delta)
)
except: # NameError
pass
payload = pack('>Q', nonce) + payload
# inventoryHash = calculateInventoryHash(payload)
return payload
# This function also broadcasts out the pubkey message
# once it is done with the POW
def doPOWForMyV2Pubkey(self, adressHash):
# Look up my stream number based on my address hash
"""configSections = shared.config.addresses()
for addressInKeysFile in configSections:
if addressInKeysFile != 'bitmessagesettings':
status, addressVersionNumber, streamNumber, \
hashFromThisParticularAddress = \
decodeAddress(addressInKeysFile)
if hash == hashFromThisParticularAddress:
myAddress = addressInKeysFile
break"""
myAddress = shared.myAddressesByHash[adressHash]
# status
_, addressVersionNumber, streamNumber, adressHash = decodeAddress(myAddress)
# 28 days from now plus or minus five minutes
TTL = int(28 * 24 * 60 * 60 + helper_random.randomrandrange(-300, 300))
embeddedTime = int(time.time() + TTL)
payload = pack('>Q', (embeddedTime))
payload += '\x00\x00\x00\x01' # object type: pubkey
payload += encodeVarint(addressVersionNumber) # Address version number
payload += encodeVarint(streamNumber)
# bitfield of features supported by me (see the wiki).
payload += protocol.getBitfield(myAddress)
try:
# privSigningKeyHex, privEncryptionKeyHex
_, _, pubSigningKey, pubEncryptionKey = \
self._getKeysForAddress(myAddress)
except Exception as err:
logger.error(
'Error within doPOWForMyV2Pubkey. Could not read'
' the keys from the keys.dat file for a requested'
' address. %s\n', err
)
return
payload += pubSigningKey + pubEncryptionKey
# Do the POW for this pubkey message
payload = self._doPOWDefaults(
payload, TTL, log_prefix='(For pubkey message)')
inventoryHash = calculateInventoryHash(payload)
objectType = 1
Inventory()[inventoryHash] = (
objectType, streamNumber, payload, embeddedTime, '')
logger.info('broadcasting inv with hash: %s', hexlify(inventoryHash))
queues.invQueue.put((streamNumber, inventoryHash))
queues.UISignalQueue.put(('updateStatusBar', ''))
try:
BMConfigParser().set(
myAddress, 'lastpubkeysendtime', str(int(time.time())))
BMConfigParser().save()
except:
# The user deleted the address out of the keys.dat file
# before this finished.
pass
# 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, adressHash):
try:
myAddress = shared.myAddressesByHash[adressHash]
except:
# The address has been deleted.
return
if BMConfigParser().safeGetBoolean(myAddress, 'chan'):
logger.info('This is a chan address. Not sending pubkey.')
return
_, addressVersionNumber, streamNumber, adressHash = decodeAddress(
myAddress)
# 28 days from now plus or minus five minutes
TTL = int(28 * 24 * 60 * 60 + helper_random.randomrandrange(-300, 300))
embeddedTime = int(time.time() + TTL)
# 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
payload += encodeVarint(addressVersionNumber) # Address version number
payload += encodeVarint(streamNumber)
# bitfield of features supported by me (see the wiki).
payload += protocol.getBitfield(myAddress)
try:
# , privEncryptionKeyHex
privSigningKeyHex, _, pubSigningKey, pubEncryptionKey = \
self._getKeysForAddress(myAddress)
except Exception as err:
logger.error(
'Error within sendOutOrStoreMyV3Pubkey. Could not read'
' the keys from the keys.dat file for a requested'
' address. %s\n', err
)
return
payload += pubSigningKey + pubEncryptionKey
payload += encodeVarint(BMConfigParser().getint(
myAddress, 'noncetrialsperbyte'))
payload += encodeVarint(BMConfigParser().getint(
myAddress, 'payloadlengthextrabytes'))
signature = highlevelcrypto.sign(payload, privSigningKeyHex)
payload += encodeVarint(len(signature))
payload += signature
# Do the POW for this pubkey message
payload = self._doPOWDefaults(
payload, TTL, log_prefix='(For pubkey message)')
inventoryHash = calculateInventoryHash(payload)
objectType = 1
Inventory()[inventoryHash] = (
objectType, streamNumber, payload, embeddedTime, '')
logger.info('broadcasting inv with hash: ' + hexlify(inventoryHash))
queues.invQueue.put((streamNumber, inventoryHash))
queues.UISignalQueue.put(('updateStatusBar', ''))
try:
BMConfigParser().set(
myAddress, 'lastpubkeysendtime', str(int(time.time())))
BMConfigParser().save()
except:
# The user deleted the address out of the keys.dat file
# before this finished.
pass
# If this isn't a chan address, this function assembles
# the pubkey data, does the necessary POW and sends it out.
def sendOutOrStoreMyV4Pubkey(self, myAddress):
if not BMConfigParser().has_section(myAddress):
# The address has been deleted.
return
if shared.BMConfigParser().safeGetBoolean(myAddress, 'chan'):
logger.info('This is a chan address. Not sending pubkey.')
return
_, addressVersionNumber, streamNumber, addressHash = decodeAddress(
myAddress)
# 28 days from now plus or minus five minutes
TTL = int(28 * 24 * 60 * 60 + helper_random.randomrandrange(-300, 300))
embeddedTime = int(time.time() + TTL)
payload = pack('>Q', (embeddedTime))
payload += '\x00\x00\x00\x01' # object type: pubkey
payload += encodeVarint(addressVersionNumber) # Address version number
payload += encodeVarint(streamNumber)
dataToEncrypt = protocol.getBitfield(myAddress)
try:
# , privEncryptionKeyHex
privSigningKeyHex, _, pubSigningKey, pubEncryptionKey = \
self._getKeysForAddress(myAddress)
except Exception as err:
logger.error(
'Error within sendOutOrStoreMyV4Pubkey. Could not read'
' the keys from the keys.dat file for a requested'
' address. %s\n', err
)
return
dataToEncrypt += pubSigningKey + pubEncryptionKey
dataToEncrypt += encodeVarint(BMConfigParser().getint(
myAddress, 'noncetrialsperbyte'))
dataToEncrypt += encodeVarint(BMConfigParser().getint(
myAddress, 'payloadlengthextrabytes'))
# When we encrypt, we'll 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) + addressHash
).digest()).digest()
payload += doubleHashOfAddressData[32:] # the tag
signature = highlevelcrypto.sign(
payload + dataToEncrypt, privSigningKeyHex
)
dataToEncrypt += encodeVarint(len(signature))
dataToEncrypt += signature
privEncryptionKey = doubleHashOfAddressData[:32]
pubEncryptionKey = highlevelcrypto.pointMult(privEncryptionKey)
payload += highlevelcrypto.encrypt(
dataToEncrypt, hexlify(pubEncryptionKey))
# Do the POW for this pubkey message
payload = self._doPOWDefaults(
payload, TTL, log_prefix='(For pubkey message)')
inventoryHash = calculateInventoryHash(payload)
objectType = 1
Inventory()[inventoryHash] = (
objectType, streamNumber, payload, embeddedTime,
doubleHashOfAddressData[32:]
)
logger.info('broadcasting inv with hash: ' + hexlify(inventoryHash))
queues.invQueue.put((streamNumber, inventoryHash))
queues.UISignalQueue.put(('updateStatusBar', ''))
try:
BMConfigParser().set(
myAddress, 'lastpubkeysendtime', str(int(time.time())))
BMConfigParser().save()
except Exception as err:
logger.error(
'Error: Couldn\'t add the lastpubkeysendtime'
' to the keys.dat file. Error message: %s', err
)
def sendBroadcast(self):
# Reset just in case
sqlExecute(
'''UPDATE sent SET status='broadcastqueued' '''
'''WHERE status = 'doingbroadcastpow' ''')
queryreturn = sqlQuery(
'''SELECT fromaddress, subject, message, '''
''' ackdata, ttl, encodingtype FROM sent '''
''' WHERE status=? and folder='sent' ''', 'broadcastqueued')
for row in queryreturn:
fromaddress, subject, body, ackdata, TTL, encoding = row
# status
_, addressVersionNumber, streamNumber, ripe = \
decodeAddress(fromaddress)
if addressVersionNumber <= 1:
logger.error(
'Error: In the singleWorker thread, the '
' sendBroadcast function doesn\'t understand'
' the address version.\n')
return
# We need to convert our private keys to public keys in order
# to include them.
try:
# , privEncryptionKeyHex
privSigningKeyHex, _, pubSigningKey, pubEncryptionKey = \
self._getKeysForAddress(fromaddress)
except:
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata,
tr._translate(
"MainWindow",
"Error! Could not find sender address"
" (your address) in the keys.dat file."))
))
continue
sqlExecute(
'''UPDATE sent SET status='doingbroadcastpow' '''
''' WHERE ackdata=? AND status='broadcastqueued' ''',
ackdata)
# 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.
# pubSigningKey = \
# highlevelcrypto.privToPub(privSigningKeyHex).decode('hex')
if TTL > 28 * 24 * 60 * 60:
TTL = 28 * 24 * 60 * 60
if TTL < 60 * 60:
TTL = 60 * 60
# add some randomness to the TTL
TTL = int(TTL + helper_random.randomrandrange(-300, 300))
embeddedTime = int(time.time() + TTL)
payload = pack('>Q', embeddedTime)
payload += '\x00\x00\x00\x03' # object type: broadcast
if addressVersionNumber <= 3:
payload += encodeVarint(4) # broadcast version
else:
payload += encodeVarint(5) # broadcast version
payload += encodeVarint(streamNumber)
if addressVersionNumber >= 4:
doubleHashOfAddressData = hashlib.sha512(hashlib.sha512(
encodeVarint(addressVersionNumber) +
encodeVarint(streamNumber) + ripe
).digest()).digest()
tag = doubleHashOfAddressData[32:]
payload += tag
else:
tag = ''
dataToEncrypt = encodeVarint(addressVersionNumber)
dataToEncrypt += encodeVarint(streamNumber)
# behavior bitfield
dataToEncrypt += protocol.getBitfield(fromaddress)
dataToEncrypt += pubSigningKey + pubEncryptionKey
if addressVersionNumber >= 3:
dataToEncrypt += encodeVarint(BMConfigParser().getint(
fromaddress, 'noncetrialsperbyte'))
dataToEncrypt += encodeVarint(BMConfigParser().getint(
fromaddress, 'payloadlengthextrabytes'))
# message encoding type
dataToEncrypt += encodeVarint(encoding)
encodedMessage = helper_msgcoding.MsgEncode(
{"subject": subject, "body": body}, encoding)
dataToEncrypt += encodeVarint(encodedMessage.length)
dataToEncrypt += encodedMessage.data
dataToSign = payload + dataToEncrypt
signature = highlevelcrypto.sign(
dataToSign, 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.
if addressVersionNumber <= 3:
privEncryptionKey = hashlib.sha512(
encodeVarint(addressVersionNumber) +
encodeVarint(streamNumber) + ripe
).digest()[:32]
else:
privEncryptionKey = doubleHashOfAddressData[:32]
pubEncryptionKey = highlevelcrypto.pointMult(privEncryptionKey)
payload += highlevelcrypto.encrypt(
dataToEncrypt, hexlify(pubEncryptionKey))
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata,
tr._translate(
"MainWindow",
"Doing work necessary to send broadcast..."))
))
payload = self._doPOWDefaults(
payload, TTL, log_prefix='(For broadcast message)')
# 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
inventoryHash = calculateInventoryHash(payload)
objectType = 3
Inventory()[inventoryHash] = (
objectType, streamNumber, payload, embeddedTime, tag)
logger.info(
'sending inv (within sendBroadcast function)'
' for object: %s',
hexlify(inventoryHash)
)
queues.invQueue.put((streamNumber, inventoryHash))
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata,
tr._translate(
"MainWindow",
"Broadcast sent on %1"
).arg(l10n.formatTimestamp()))
))
# Update the status of the message in the 'sent' table to have
# a 'broadcastsent' status
sqlExecute(
'UPDATE sent SET msgid=?, status=?, lastactiontime=?'
' WHERE ackdata=?',
inventoryHash, 'broadcastsent', int(time.time()), ackdata
)
def sendMsg(self):
# Reset just in case
sqlExecute(
'''UPDATE sent SET status='msgqueued' '''
''' WHERE status IN ('doingpubkeypow', 'doingmsgpow')''')
queryreturn = sqlQuery(
'''SELECT toaddress, fromaddress, subject, message, '''
''' ackdata, status, ttl, retrynumber, encodingtype FROM '''
''' sent WHERE (status='msgqueued' or status='forcepow') '''
''' and folder='sent' ''')
# while we have a msg that needs some work
for row in queryreturn:
toaddress, fromaddress, subject, message, \
ackdata, status, TTL, retryNumber, encoding = row
# toStatus
_, toAddressVersionNumber, toStreamNumber, toRipe = \
decodeAddress(toaddress)
# fromStatus, , ,fromRipe
_, fromAddressVersionNumber, fromStreamNumber, _ = \
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
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
# 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 BMConfigParser().has_section(toaddress):
sqlExecute(
'''UPDATE sent SET status='doingmsgpow' '''
''' WHERE toaddress=? AND status='msgqueued' ''',
toaddress
)
status = 'doingmsgpow'
elif status == 'msgqueued':
# Let's see if we already have the pubkey in our pubkeys table
queryreturn = sqlQuery(
'''SELECT address FROM pubkeys WHERE address=?''',
toaddress
)
# If we have the needed pubkey in the pubkey table already,
if queryreturn != []:
# set the status of this msg to doingmsgpow
sqlExecute(
'''UPDATE sent SET status='doingmsgpow' '''
''' WHERE toaddress=? AND status='msgqueued' ''',
toaddress
)
status = 'doingmsgpow'
# 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.
sqlExecute(
'''UPDATE pubkeys SET usedpersonally='yes' '''
''' WHERE address=?''',
toaddress
)
# We don't have the needed pubkey in the pubkeys table already.
else:
if toAddressVersionNumber <= 3:
toTag = ''
else:
toTag = hashlib.sha512(hashlib.sha512(
encodeVarint(toAddressVersionNumber) +
encodeVarint(toStreamNumber) + toRipe
).digest()).digest()[32:]
if toaddress in state.neededPubkeys or \
toTag in state.neededPubkeys:
# We already sent a request for the pubkey
sqlExecute(
'''UPDATE sent SET status='awaitingpubkey', '''
''' sleeptill=? WHERE toaddress=? '''
''' AND status='msgqueued' ''',
int(time.time()) + 2.5 * 24 * 60 * 60,
toaddress
)
queues.UISignalQueue.put((
'updateSentItemStatusByToAddress', (
toaddress,
tr._translate(
"MainWindow",
"Encryption key was requested earlier."))
))
# on with the next msg on which we can do some work
continue
else:
# We have not yet sent a request for the pubkey
needToRequestPubkey = True
# 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.
if toAddressVersionNumber >= 4:
doubleHashOfToAddressData = hashlib.sha512(
hashlib.sha512(encodeVarint(
toAddressVersionNumber) +
encodeVarint(toStreamNumber) +
toRipe
).digest()
).digest()
# The first half of the sha512 hash.
privEncryptionKey = doubleHashOfToAddressData[:32]
# The second half of the sha512 hash.
tag = doubleHashOfToAddressData[32:]
state.neededPubkeys[tag] = (
toaddress,
highlevelcrypto.makeCryptor(
hexlify(privEncryptionKey))
)
for value in Inventory().by_type_and_tag(1, toTag):
# if valid, this function also puts it
# in the pubkeys table.
if shared.decryptAndCheckPubkeyPayload(
value.payload, toaddress
) == 'successful':
needToRequestPubkey = False
sqlExecute(
'''UPDATE sent SET '''
''' status='doingmsgpow', '''
''' retrynumber=0 WHERE '''
''' toaddress=? AND '''
''' (status='msgqueued' or '''
''' status='awaitingpubkey' or '''
''' status='doingpubkeypow')''',
toaddress)
del state.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.
if needToRequestPubkey:
sqlExecute(
'''UPDATE sent SET '''
''' status='doingpubkeypow' WHERE '''
''' toaddress=? AND status='msgqueued' ''',
toaddress
)
queues.UISignalQueue.put((
'updateSentItemStatusByToAddress', (
toaddress,
tr._translate(
"MainWindow",
"Sending a request for the"
" recipient\'s encryption key."))
))
self.requestPubKey(toaddress)
# on with the next msg on which we can do some work
continue
# At this point we know that we have the necessary pubkey
# in the pubkeys table.
TTL *= 2**retryNumber
if TTL > 28 * 24 * 60 * 60:
TTL = 28 * 24 * 60 * 60
# add some randomness to the TTL
TTL = int(TTL + helper_random.randomrandrange(-300, 300))
embeddedTime = int(time.time() + TTL)
# if we aren't sending this to ourselves or a chan
if not BMConfigParser().has_section(toaddress):
shared.ackdataForWhichImWatching[ackdata] = 0
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata,
tr._translate(
"MainWindow",
"Looking up the receiver\'s public key"))
))
logger.info('Sending a message.')
logger.debug(
'First 150 characters of message: %s',
repr(message[:150])
)
# 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 address=?',
toaddress)
for row in queryreturn:
pubkeyPayload, = row
# 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)
# to bypass the address version whose length is definitely 1
readPosition = 1
_, streamNumberLength = decodeVarint(
pubkeyPayload[readPosition:readPosition + 10])
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 receiver is a mobile device who expects that their
# address RIPE is included unencrypted on the front of
# the message..
if shared.isBitSetWithinBitfield(behaviorBitfield, 30):
# if we are Not willing to include the receiver's
# RIPE hash on the message..
if not shared.BMConfigParser().safeGetBoolean(
'bitmessagesettings', 'willinglysendtomobile'
):
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.'
)
queues.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()))
))
# 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
# We don't use this key for anything here.
# pubSigningKeyBase256 =
# pubkeyPayload[readPosition:readPosition+64]
readPosition += 64
pubEncryptionKeyBase256 = pubkeyPayload[
readPosition:readPosition + 64]
readPosition += 64
# Let us fetch the amount of work required by the recipient.
if toAddressVersionNumber == 2:
requiredAverageProofOfWorkNonceTrialsPerByte = \
defaults.networkDefaultProofOfWorkNonceTrialsPerByte
requiredPayloadLengthExtraBytes = \
defaults.networkDefaultPayloadLengthExtraBytes
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata,
tr._translate(
"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
# 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.
if requiredAverageProofOfWorkNonceTrialsPerByte < \
defaults.networkDefaultProofOfWorkNonceTrialsPerByte:
requiredAverageProofOfWorkNonceTrialsPerByte = \
defaults.networkDefaultProofOfWorkNonceTrialsPerByte
if requiredPayloadLengthExtraBytes < \
defaults.networkDefaultPayloadLengthExtraBytes:
requiredPayloadLengthExtraBytes = \
defaults.networkDefaultPayloadLengthExtraBytes
logger.debug(
'Using averageProofOfWorkNonceTrialsPerByte: %s'
' and payloadLengthExtraBytes: %s.',
requiredAverageProofOfWorkNonceTrialsPerByte,
requiredPayloadLengthExtraBytes
)
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata,
tr._translate(
"MainWindow",
"Doing work necessary to send message.\n"
"Receiver\'s required difficulty: %1"
" and %2"
).arg(str(float(
requiredAverageProofOfWorkNonceTrialsPerByte) /
defaults.networkDefaultProofOfWorkNonceTrialsPerByte
)).arg(str(float(
requiredPayloadLengthExtraBytes) /
defaults.networkDefaultPayloadLengthExtraBytes
)))))
if status != 'forcepow':
if (requiredAverageProofOfWorkNonceTrialsPerByte
> BMConfigParser().getint(
'bitmessagesettings',
'maxacceptablenoncetrialsperbyte'
) and
BMConfigParser().getint(
'bitmessagesettings',
'maxacceptablenoncetrialsperbyte'
) != 0) or (
requiredPayloadLengthExtraBytes
> BMConfigParser().getint(
'bitmessagesettings',
'maxacceptablepayloadlengthextrabytes'
) and
BMConfigParser().getint(
'bitmessagesettings',
'maxacceptablepayloadlengthextrabytes'
) != 0):
# The demanded difficulty is more than
# we are willing to do.
sqlExecute(
'''UPDATE sent SET status='toodifficult' '''
''' WHERE ackdata=? ''',
ackdata)
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata,
tr._translate(
"MainWindow",
"Problem: The work demanded by"
" the recipient (%1 and %2) is"
" more difficult than you are"
" willing to do. %3"
).arg(str(float(
requiredAverageProofOfWorkNonceTrialsPerByte)
/ defaults.networkDefaultProofOfWorkNonceTrialsPerByte
)).arg(str(float(
requiredPayloadLengthExtraBytes)
/ defaults.networkDefaultPayloadLengthExtraBytes
)).arg(l10n.formatTimestamp()))
))
continue
else: # if we are sending a message to ourselves or a chan..
logger.info('Sending a message.')
logger.debug(
'First 150 characters of message: %r', message[:150])
behaviorBitfield = protocol.getBitfield(fromaddress)
try:
privEncryptionKeyBase58 = BMConfigParser().get(
toaddress, 'privencryptionkey')
except Exception as err:
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata,
tr._translate(
"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(l10n.formatTimestamp()))
))
logger.error(
'Error within sendMsg. Could not read the keys'
' from the keys.dat file for our own address. %s\n',
err)
continue
privEncryptionKeyHex = hexlify(shared.decodeWalletImportFormat(
privEncryptionKeyBase58))
pubEncryptionKeyBase256 = unhexlify(highlevelcrypto.privToPub(
privEncryptionKeyHex))[1:]
requiredAverageProofOfWorkNonceTrialsPerByte = \
defaults.networkDefaultProofOfWorkNonceTrialsPerByte
requiredPayloadLengthExtraBytes = \
defaults.networkDefaultPayloadLengthExtraBytes
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata,
tr._translate(
"MainWindow",
"Doing work necessary to send message."))
))
# Now we can start to assemble our message.
payload = encodeVarint(fromAddressVersionNumber)
payload += encodeVarint(fromStreamNumber)
# Bitfield of features and behaviors
# that can be expected from me. (See
# https://bitmessage.org/wiki/Protocol_specification#Pubkey_bitfield_features)
payload += protocol.getBitfield(fromaddress)
# We need to convert our private keys to public keys in order
# to include them.
try:
privSigningKeyHex, privEncryptionKeyHex, \
pubSigningKey, pubEncryptionKey = self._getKeysForAddress(
fromaddress)
except:
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata,
tr._translate(
"MainWindow",
"Error! Could not find sender address"
" (your address) in the keys.dat file."))
))
continue
payload += pubSigningKey + pubEncryptionKey
if fromAddressVersionNumber >= 3:
# 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(
defaults.networkDefaultProofOfWorkNonceTrialsPerByte)
payload += encodeVarint(
defaults.networkDefaultPayloadLengthExtraBytes)
else:
payload += encodeVarint(BMConfigParser().getint(
fromaddress, 'noncetrialsperbyte'))
payload += encodeVarint(BMConfigParser().getint(
fromaddress, 'payloadlengthextrabytes'))
# This hash will be checked by the receiver of the message
# to verify that toRipe belongs to them. This prevents
# a Surreptitious Forwarding Attack.
payload += toRipe
payload += encodeVarint(encoding) # message encoding type
encodedMessage = helper_msgcoding.MsgEncode(
{"subject": subject, "body": message}, encoding
)
payload += encodeVarint(encodedMessage.length)
payload += encodedMessage.data
if BMConfigParser().has_section(toaddress):
logger.info(
'Not bothering to include ackdata because we are'
' sending to ourselves or a chan.'
)
fullAckPayload = ''
elif not protocol.checkBitfield(
behaviorBitfield, protocol.BITFIELD_DOESACK):
logger.info(
'Not bothering to include ackdata because'
' the receiver said that they won\'t relay it anyway.'
)
fullAckPayload = ''
else:
# 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.
fullAckPayload = self.generateFullAckMessage(
ackdata, toStreamNumber, TTL)
payload += encodeVarint(len(fullAckPayload))
payload += fullAckPayload
dataToSign = pack('>Q', embeddedTime) + '\x00\x00\x00\x02' + \
encodeVarint(1) + encodeVarint(toStreamNumber) + payload
signature = highlevelcrypto.sign(dataToSign, privSigningKeyHex)
payload += encodeVarint(len(signature))
payload += signature
# We have assembled the data that will be encrypted.
try:
encrypted = highlevelcrypto.encrypt(
payload, "04" + hexlify(pubEncryptionKeyBase256)
)
except:
sqlExecute(
'''UPDATE sent SET status='badkey' WHERE ackdata=?''',
ackdata
)
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata,
tr._translate(
"MainWindow",
"Problem: The recipient\'s encryption key is"
" no good. Could not encrypt message. %1"
).arg(l10n.formatTimestamp()))
))
continue
encryptedPayload = pack('>Q', embeddedTime)
encryptedPayload += '\x00\x00\x00\x02' # object type: msg
encryptedPayload += encodeVarint(1) # msg version
encryptedPayload += encodeVarint(toStreamNumber) + encrypted
target = 2 ** 64 / (
requiredAverageProofOfWorkNonceTrialsPerByte * (
len(encryptedPayload) + 8 +
requiredPayloadLengthExtraBytes + ((
TTL * (
len(encryptedPayload) + 8 +
requiredPayloadLengthExtraBytes
)) / (2 ** 16))
))
logger.info(
'(For msg message) Doing proof of work. Total required'
' difficulty: %f. Required small message difficulty: %f.',
float(requiredAverageProofOfWorkNonceTrialsPerByte) /
defaults.networkDefaultProofOfWorkNonceTrialsPerByte,
float(requiredPayloadLengthExtraBytes) /
defaults.networkDefaultPayloadLengthExtraBytes
)
powStartTime = time.time()
initialHash = hashlib.sha512(encryptedPayload).digest()
trialValue, nonce = proofofwork.run(target, initialHash)
logger.info(
'(For msg message) Found proof of work %s Nonce: %s',
trialValue, nonce
)
try:
logger.info(
'PoW took %.1f seconds, speed %s.',
time.time() - powStartTime,
sizeof_fmt(nonce / (time.time() - powStartTime))
)
except:
pass
encryptedPayload = pack('>Q', nonce) + encryptedPayload
# Sanity check. The encryptedPayload 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(encryptedPayload) > 2 ** 18: # 256 KiB
logger.critical(
'This msg object is too large to send. This should'
' never happen. Object size: %i',
len(encryptedPayload)
)
continue
inventoryHash = calculateInventoryHash(encryptedPayload)
objectType = 2
Inventory()[inventoryHash] = (
objectType, toStreamNumber, encryptedPayload, embeddedTime, '')
if BMConfigParser().has_section(toaddress) or \
not protocol.checkBitfield(
behaviorBitfield, protocol.BITFIELD_DOESACK):
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata,
tr._translate(
"MainWindow",
"Message sent. Sent at %1"
).arg(l10n.formatTimestamp()))
))
else:
# not sending to a chan or one of my addresses
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata,
tr._translate(
"MainWindow",
"Message sent. Waiting for acknowledgement."
" Sent on %1"
).arg(l10n.formatTimestamp()))
))
logger.info(
'Broadcasting inv for my msg(within sendmsg function): %s',
hexlify(inventoryHash)
)
queues.invQueue.put((toStreamNumber, inventoryHash))
# Update the sent message in the sent table with the
# necessary information.
if BMConfigParser().has_section(toaddress) or \
not protocol.checkBitfield(
behaviorBitfield, protocol.BITFIELD_DOESACK):
newStatus = 'msgsentnoackexpected'
else:
newStatus = 'msgsent'
# wait 10% past expiration
sleepTill = int(time.time() + TTL * 1.1)
sqlExecute(
'''UPDATE sent SET msgid=?, status=?, retrynumber=?, '''
''' sleeptill=?, lastactiontime=? WHERE ackdata=?''',
inventoryHash, newStatus, retryNumber + 1,
sleepTill, int(time.time()), ackdata
)
# If we are sending to ourselves or a chan, let's put
# the message in our own inbox.
if BMConfigParser().has_section(toaddress):
# Used to detect and ignore duplicate messages in our inbox
sigHash = hashlib.sha512(hashlib.sha512(
signature).digest()).digest()[32:]
t = (inventoryHash, toaddress, fromaddress, subject, int(
time.time()), message, 'inbox', encoding, 0, sigHash)
helper_inbox.insert(t)
queues.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 BMConfigParser().safeGetBoolean(
'bitmessagesettings', 'apienabled'):
try:
apiNotifyPath = BMConfigParser().get(
'bitmessagesettings', 'apinotifypath')
except:
apiNotifyPath = ''
if apiNotifyPath != '':
call([apiNotifyPath, "newMessage"])
def requestPubKey(self, toAddress):
toStatus, addressVersionNumber, streamNumber, ripe = decodeAddress(
toAddress)
if toStatus != 'success':
logger.error(
'Very abnormal error occurred in requestPubKey.'
' toAddress is: %r. Please report this error to Atheros.',
toAddress
)
return
queryReturn = sqlQuery(
'''SELECT retrynumber FROM sent WHERE toaddress=? '''
''' AND (status='doingpubkeypow' OR status='awaitingpubkey') '''
''' LIMIT 1''',
toAddress
)
if len(queryReturn) == 0:
logger.critical(
'BUG: Why are we requesting the pubkey for %s'
' if there are no messages in the sent folder'
' to that address?', toAddress
)
return
retryNumber = queryReturn[0][0]
if addressVersionNumber <= 3:
state.neededPubkeys[toAddress] = 0
elif addressVersionNumber >= 4:
# If the user just clicked 'send' then the tag
# (and other information) will already be in the
# neededPubkeys dictionary. But if we are recovering
# from a restart of the client then we have to put it in now.
# Note that this is the first half of the sha512 hash.
privEncryptionKey = hashlib.sha512(hashlib.sha512(
encodeVarint(addressVersionNumber) +
encodeVarint(streamNumber) + ripe
).digest()).digest()[:32]
# Note that this is the second half of the sha512 hash.
tag = hashlib.sha512(hashlib.sha512(
encodeVarint(addressVersionNumber) +
encodeVarint(streamNumber) + ripe
).digest()).digest()[32:]
if tag not in state.neededPubkeys:
# We'll need this for when we receive a pubkey reply:
# it will be encrypted and we'll need to decrypt it.
state.neededPubkeys[tag] = (
toAddress,
highlevelcrypto.makeCryptor(hexlify(privEncryptionKey))
)
# 2.5 days. This was chosen fairly arbitrarily.
TTL = 2.5 * 24 * 60 * 60
TTL *= 2 ** retryNumber
if TTL > 28 * 24 * 60 * 60:
TTL = 28 * 24 * 60 * 60
# add some randomness to the TTL
TTL = TTL + helper_random.randomrandrange(-300, 300)
embeddedTime = int(time.time() + TTL)
payload = pack('>Q', embeddedTime)
payload += '\x00\x00\x00\x00' # object type: getpubkey
payload += encodeVarint(addressVersionNumber)
payload += encodeVarint(streamNumber)
if addressVersionNumber <= 3:
payload += ripe
logger.info(
'making request for pubkey with ripe: %s', hexlify(ripe))
else:
payload += tag
logger.info(
'making request for v4 pubkey with tag: %s', hexlify(tag))
# print 'trial value', trialValue
statusbar = 'Doing the computations necessary to request' +\
' the recipient\'s public key.'
queues.UISignalQueue.put(('updateStatusBar', statusbar))
queues.UISignalQueue.put((
'updateSentItemStatusByToAddress', (
toAddress,
tr._translate(
"MainWindow",
"Doing work necessary to request encryption key."))
))
payload = self._doPOWDefaults(payload, TTL)
inventoryHash = calculateInventoryHash(payload)
objectType = 1
Inventory()[inventoryHash] = (
objectType, streamNumber, payload, embeddedTime, '')
logger.info('sending inv (for the getpubkey message)')
queues.invQueue.put((streamNumber, inventoryHash))
# wait 10% past expiration
sleeptill = int(time.time() + TTL * 1.1)
sqlExecute(
'''UPDATE sent SET lastactiontime=?, '''
''' status='awaitingpubkey', retrynumber=?, sleeptill=? '''
''' WHERE toaddress=? AND (status='doingpubkeypow' OR '''
''' status='awaitingpubkey') ''',
int(time.time()), retryNumber + 1, sleeptill, toAddress)
queues.UISignalQueue.put((
'updateStatusBar',
tr._translate(
"MainWindow",
"Broadcasting the public key request. This program will"
" auto-retry if they are offline.")
))
queues.UISignalQueue.put((
'updateSentItemStatusByToAddress', (
toAddress,
tr._translate(
"MainWindow",
"Sending public key request. Waiting for reply."
" Requested at %1"
).arg(l10n.formatTimestamp()))
))
def generateFullAckMessage(self, ackdata, toStreamNumber, TTL):
# It might be perfectly fine to just use the same TTL for
# the ackdata that we use for the message. But I would rather
# it be more difficult for attackers to associate ackData with
# the associated msg object. However, users would want the TTL
# of the acknowledgement to be about the same as they set
# for the message itself. So let's set the TTL of the
# acknowledgement to be in one of three 'buckets': 1 hour, 7
# days, or 28 days, whichever is relatively close to what the
# user specified.
if TTL < 24 * 60 * 60: # 1 day
TTL = 24 * 60 * 60 # 1 day
elif TTL < 7 * 24 * 60 * 60: # 1 week
TTL = 7 * 24 * 60 * 60 # 1 week
else:
TTL = 28 * 24 * 60 * 60 # 4 weeks
# Add some randomness to the TTL
TTL = int(TTL + helper_random.randomrandrange(-300, 300))
embeddedTime = int(time.time() + TTL)
# type/version/stream already included
payload = pack('>Q', (embeddedTime)) + ackdata
payload = self._doPOWDefaults(
payload, TTL, log_prefix='(For ack message)', log_time=True)
return protocol.CreatePacket('object', payload)

View File

@ -42,10 +42,10 @@ class smtpDeliver(threading.Thread, StoppableThread):
elif command == 'updateStatusBar':
pass
elif command == 'updateSentItemStatusByToAddress':
toAddress, message = data
status, toAddress, message = data
pass
elif command == 'updateSentItemStatusByAckdata':
ackData, message = data
status, ackData, message = data
pass
elif command == 'displayNewInboxMessage':
inventoryHash, toAddress, fromAddress, subject, body = data

View File

@ -1,4 +1,6 @@
import threading
import addresses
import binascii
from bmconfigparser import BMConfigParser
import sqlite3
import time
@ -148,16 +150,55 @@ class sqlThread(threading.Thread):
logger.debug('Vacuuming message.dat. You might notice that the file size gets much smaller.')
self.cur.execute( ''' VACUUM ''')
# After code refactoring, the possible status values for sent messages
# have changed.
self.cur.execute(
'''update sent set status='doingmsgpow' where status='doingpow' ''')
self.cur.execute(
'''update sent set status='msgsent' where status='sentmessage' ''')
self.cur.execute(
'''update sent set status='doingpubkeypow' where status='findingpubkey' ''')
self.cur.execute(
'''update sent set status='broadcastqueued' where status='broadcastpending' ''')
# Retry number now has no meaning for getpubkey messages
self.cur.execute("""
UPDATE "sent" SET "retrynumber" = 0
WHERE "status" IN (
'doingpubkeypow', 'awaitingpubkey',
'findingpubkey' -- Old name
);
""")
# Reset temporary message status values
# TODO: stop writing them to the database, they should exist only in memory
self.cur.execute("""
UPDATE "sent" SET "status" = 'broadcastqueued'
WHERE "status" == 'doingbroadcastpow';
""")
self.cur.execute("""
UPDATE "sent" SET "status" = 'msgqueued'
WHERE "status" IN (
'doingmsgpow', 'doingpubkeypow', 'awaitingpubkey',
'doingpow', 'findingpubkey' -- Old names
);
""")
# Update status values inherited from old versions
self.cur.execute("""UPDATE "sent" SET "status" = 'msgsent' WHERE "status" == 'sentmessage';""")
self.cur.execute("""UPDATE "sent" SET "status" = 'broadcastqueued' WHERE "status" == 'broadcastpending';""")
# Add tags to all objects
self.cur.execute("""SELECT "hash", "payload" FROM "inventory" WHERE "tag" == '';""")
updatingCursor = self.conn.cursor()
for ID, payload in self.cur:
readPosition = 20
version, readLength = addresses.decodeVarint(payload[readPosition: readPosition + 9])
readPosition += readLength
stream, readLength = addresses.decodeVarint(payload[readPosition: readPosition + 9])
readPosition += readLength
tag = buffer(payload[readPosition: readPosition + 32]) # May be shorter than 32 bytes for getpubkeys
updatingCursor.execute("""UPDATE "inventory" SET "tag" = ? WHERE "hash" == ?;""", (tag, ID))
self.conn.commit()
# Let's get rid of the first20bytesofencryptedmessage field in
@ -391,6 +432,24 @@ class sqlThread(threading.Thread):
parameters = (int(time.time()),)
self.cur.execute(item, parameters)
self.cur.execute("""SELECT "ackdata" FROM "sent" WHERE "status" == 'msgsent';""")
legacyAckData = []
for ackData, in self.cur:
logger.info("Watching for ack data: %s", binascii.hexlify(ackData))
if len(ackData) == 32:
legacyAckData.append(ackData)
else:
state.watchedAckData.add(ackData)
for i in legacyAckData:
ackData = "\x00\x00\x00\x02\x01\x01" + i
self.cur.execute("""UPDATE "sent" SET "ackdata" = ? WHERE "ackdata" == ?;""", (ackData, i))
state.watchedAckData.add(ackData)
state.sqlReady = True
while True:

View File

@ -18,18 +18,6 @@ import queues
import shutdown
from debug import logger
def powQueueSize():
curWorkerQueue = queues.workerQueue.qsize()
for thread in threading.enumerate():
try:
if thread.name == "singleWorker":
curWorkerQueue += thread.busy
except Exception as err:
logger.info('Thread error %s', err)
return curWorkerQueue
def convertIntToString(n):
a = __builtins__.hex(n)
if a[-1:] == 'L':
@ -68,8 +56,8 @@ def signal_handler(signal, frame):
# on Windows this isn't triggered, but it's fine,
# it has its own process termination thing
raise SystemExit
if "PoolWorker" in process.name:
raise SystemExit
if process.name == "ForkingSolver":
return
if threading.current_thread().name not in ("PyBitmessage", "MainThread"):
return
logger.error("Got signal %i", signal)

View File

@ -7,6 +7,8 @@ from inventory import Inventory
from network.dandelion import Dandelion
import protocol
import state
import workprover.utils
import defaults
class BMObjectInsufficientPOWError(Exception):
errorCodes = ("Insufficient proof of work")
@ -47,11 +49,16 @@ class BMObject(object):
self.inventoryHash = calculateInventoryHash(data)
# copy to avoid memory issues
self.data = bytearray(data)
# Doesn't matter if the payload is shorter, old version "getpubkey" objects must have 20-byte tags
self.tag = self.data[payloadOffset:payloadOffset+32]
def checkProofOfWorkSufficient(self):
# Let us check to make sure that the proof of work is sufficient.
if not protocol.isProofOfWorkSufficient(self.data):
if not workprover.utils.checkWorkSufficient(
self.data,
defaults.networkDefaultProofOfWorkNonceTrialsPerByte,
defaults.networkDefaultPayloadLengthExtraBytes
):
logger.info('Proof of work is insufficient.')
raise BMObjectInsufficientPOWError()

View File

@ -353,7 +353,6 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
try:
self.object.checkObjectByType()
objectProcessorQueue.put((self.object.objectType, buffer(self.object.data)))
except BMObjectInvalidError as e:
BMProto.stopDownloadingObject(self.object.inventoryHash, True)
else:
@ -369,6 +368,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
self.object.objectType, self.object.streamNumber, buffer(self.payload[objectOffset:]), self.object.expiresTime, buffer(self.object.tag))
self.handleReceivedObject(self.object.streamNumber, self.object.inventoryHash)
invQueue.put((self.object.streamNumber, self.object.inventoryHash, self.destination))
objectProcessorQueue.put((self.object.objectType, buffer(self.object.data)))
return True
def _decode_addr(self):

View File

@ -1,112 +0,0 @@
#!/usr/bin/env python2.7
from struct import pack, unpack
import time
import hashlib
import random
import os
from bmconfigparser import BMConfigParser
import paths
from state import shutdown
from debug import logger
libAvailable = True
ctx = False
queue = False
program = False
gpus = []
enabledGpus = []
vendors = []
hash_dt = None
try:
import pyopencl as cl
import numpy
except ImportError:
libAvailable = False
def initCL():
global ctx, queue, program, hash_dt, libAvailable
if libAvailable is False:
return
del enabledGpus[:]
del vendors[:]
del gpus[:]
ctx = False
try:
hash_dt = numpy.dtype([('target', numpy.uint64), ('v', numpy.str_, 73)])
try:
for platform in cl.get_platforms():
gpus.extend(platform.get_devices(device_type=cl.device_type.GPU))
if BMConfigParser().safeGet("bitmessagesettings", "opencl") == platform.vendor:
enabledGpus.extend(platform.get_devices(device_type=cl.device_type.GPU))
if platform.vendor not in vendors:
vendors.append(platform.vendor)
except:
pass
if (len(enabledGpus) > 0):
ctx = cl.Context(devices=enabledGpus)
queue = cl.CommandQueue(ctx)
f = open(os.path.join(paths.codePath(), "bitmsghash", 'bitmsghash.cl'), 'r')
fstr = ''.join(f.readlines())
program = cl.Program(ctx, fstr).build(options="")
logger.info("Loaded OpenCL kernel")
else:
logger.info("No OpenCL GPUs found")
del enabledGpus[:]
except Exception as e:
logger.error("OpenCL fail: ", exc_info=True)
del enabledGpus[:]
def openclAvailable():
return (len(gpus) > 0)
def openclEnabled():
return (len(enabledGpus) > 0)
def do_opencl_pow(hash, target):
output = numpy.zeros(1, dtype=[('v', numpy.uint64, 1)])
if (len(enabledGpus) == 0):
return output[0][0]
data = numpy.zeros(1, dtype=hash_dt, order='C')
data[0]['v'] = ("0000000000000000" + hash).decode("hex")
data[0]['target'] = target
hash_buf = cl.Buffer(ctx, cl.mem_flags.READ_ONLY | cl.mem_flags.COPY_HOST_PTR, hostbuf=data)
dest_buf = cl.Buffer(ctx, cl.mem_flags.WRITE_ONLY, output.nbytes)
kernel = program.kernel_sha512
worksize = kernel.get_work_group_info(cl.kernel_work_group_info.WORK_GROUP_SIZE, enabledGpus[0])
kernel.set_arg(0, hash_buf)
kernel.set_arg(1, dest_buf)
start = time.time()
progress = 0
globamt = worksize*2000
while output[0][0] == 0 and shutdown == 0:
kernel.set_arg(2, pack("<Q", progress))
cl.enqueue_nd_range_kernel(queue, kernel, (globamt,), (worksize,))
cl.enqueue_read_buffer(queue, dest_buf, output)
queue.finish()
progress += globamt
sofar = time.time() - start
# logger.debug("Working for %.3fs, %.2f Mh/s", sofar, (progress / sofar) / 1000000)
if shutdown != 0:
raise Exception ("Interrupted")
taken = time.time() - start
# logger.debug("Took %d tries.", progress)
return output[0][0]
#initCL()
if __name__ == "__main__":
target = 54227212183L
initialHash = "3758f55b5a8d902fd3597e4ce6a2d3f23daff735f65d9698c270987f4e67ad590b93f3ffeba0ef2fd08a8dc2f87b68ae5a0dc819ab57f22ad2c4c9c8618a43b3".decode("hex")
nonce = do_opencl_pow(initialHash.encode("hex"), target)
trialValue, = unpack('>Q',hashlib.sha512(hashlib.sha512(pack('>Q',nonce) + initialHash).digest()).digest()[0:8])
print "{} - value {} < {}".format(nonce, trialValue, target)

View File

@ -1,295 +0,0 @@
#import shared
#import time
#from multiprocessing import Pool, cpu_count
import hashlib
from struct import unpack, pack
from subprocess import call
import sys
import time
from bmconfigparser import BMConfigParser
from debug import logger
import paths
import openclpow
import queues
import tr
import os
import ctypes
import state
bitmsglib = 'bitmsghash.so'
bmpow = None
def _set_idle():
if 'linux' in sys.platform:
os.nice(20)
else:
try:
sys.getwindowsversion()
import win32api,win32process,win32con # @UnresolvedImport
pid = win32api.GetCurrentProcessId()
handle = win32api.OpenProcess(win32con.PROCESS_ALL_ACCESS, True, pid)
win32process.SetPriorityClass(handle, win32process.IDLE_PRIORITY_CLASS)
except:
#Windows 64-bit
pass
def _pool_worker(nonce, initialHash, target, pool_size):
_set_idle()
trialValue = float('inf')
while trialValue > target:
nonce += pool_size
trialValue, = unpack('>Q',hashlib.sha512(hashlib.sha512(pack('>Q',nonce) + initialHash).digest()).digest()[0:8])
return [trialValue, nonce]
def _doSafePoW(target, initialHash):
logger.debug("Safe PoW start")
nonce = 0
trialValue = float('inf')
while trialValue > target and state.shutdown == 0:
nonce += 1
trialValue, = unpack('>Q',hashlib.sha512(hashlib.sha512(pack('>Q',nonce) + initialHash).digest()).digest()[0:8])
if state.shutdown != 0:
raise StopIteration("Interrupted")
logger.debug("Safe PoW done")
return [trialValue, nonce]
def _doFastPoW(target, initialHash):
logger.debug("Fast PoW start")
from multiprocessing import Pool, cpu_count
try:
pool_size = cpu_count()
except:
pool_size = 4
try:
maxCores = BMConfigParser().getint('bitmessagesettings', 'maxcores')
except:
maxCores = 99999
if pool_size > maxCores:
pool_size = maxCores
pool = Pool(processes=pool_size)
result = []
for i in range(pool_size):
result.append(pool.apply_async(_pool_worker, args=(i, initialHash, target, pool_size)))
while True:
if state.shutdown > 0:
try:
pool.terminate()
pool.join()
except:
pass
raise StopIteration("Interrupted")
for i in range(pool_size):
if result[i].ready():
try:
result[i].successful()
except AssertionError:
pool.terminate()
pool.join()
raise StopIteration("Interrupted")
result = result[i].get()
pool.terminate()
pool.join()
logger.debug("Fast PoW done")
return result[0], result[1]
time.sleep(0.2)
def _doCPoW(target, initialHash):
h = initialHash
m = target
out_h = ctypes.pointer(ctypes.create_string_buffer(h, 64))
out_m = ctypes.c_ulonglong(m)
logger.debug("C PoW start")
nonce = bmpow(out_h, out_m)
trialValue, = unpack('>Q',hashlib.sha512(hashlib.sha512(pack('>Q',nonce) + initialHash).digest()).digest()[0:8])
if state.shutdown != 0:
raise StopIteration("Interrupted")
logger.debug("C PoW done")
return [trialValue, nonce]
def _doGPUPoW(target, initialHash):
logger.debug("GPU PoW start")
nonce = openclpow.do_opencl_pow(initialHash.encode("hex"), target)
trialValue, = unpack('>Q',hashlib.sha512(hashlib.sha512(pack('>Q',nonce) + initialHash).digest()).digest()[0:8])
#print "{} - value {} < {}".format(nonce, trialValue, target)
if trialValue > target:
deviceNames = ", ".join(gpu.name for gpu in openclpow.enabledGpus)
queues.UISignalQueue.put(('updateStatusBar', (tr._translate("MainWindow",'Your GPU(s) did not calculate correctly, disabling OpenCL. Please report to the developers.'), 1)))
logger.error("Your GPUs (%s) did not calculate correctly, disabling OpenCL. Please report to the developers.", deviceNames)
openclpow.enabledGpus = []
raise Exception("GPU did not calculate correctly.")
if state.shutdown != 0:
raise StopIteration("Interrupted")
logger.debug("GPU PoW done")
return [trialValue, nonce]
def estimate(difficulty, format = False):
ret = difficulty / 10
if ret < 1:
ret = 1
if format:
out = str(int(ret)) + " seconds"
if ret > 60:
ret /= 60
out = str(int(ret)) + " minutes"
if ret > 60:
ret /= 60
out = str(int(ret)) + " hours"
if ret > 24:
ret /= 24
out = str(int(ret)) + " days"
if ret > 7:
out = str(int(ret)) + " weeks"
if ret > 31:
out = str(int(ret)) + " months"
if ret > 366:
ret /= 366
out = str(int(ret)) + " years"
else:
return ret
def getPowType():
if openclpow.openclEnabled():
return "OpenCL"
if bmpow:
return "C"
return "python"
def notifyBuild(tried=False):
if bmpow:
queues.UISignalQueue.put(('updateStatusBar', (tr._translate("proofofwork", "C PoW module built successfully."), 1)))
elif tried:
queues.UISignalQueue.put(('updateStatusBar', (tr._translate("proofofwork", "Failed to build C PoW module. Please build it manually."), 1)))
else:
queues.UISignalQueue.put(('updateStatusBar', (tr._translate("proofofwork", "C PoW module unavailable. Please build it."), 1)))
def buildCPoW():
if bmpow is not None:
return
if paths.frozen is not None:
notifyBuild(False)
return
if sys.platform in ["win32", "win64"]:
notifyBuild(False)
return
try:
if "bsd" in sys.platform:
# BSD make
call(["make", "-C", os.path.join(paths.codePath(), "bitmsghash"), '-f', 'Makefile.bsd'])
else:
# GNU make
call(["make", "-C", os.path.join(paths.codePath(), "bitmsghash")])
if os.path.exists(os.path.join(paths.codePath(), "bitmsghash", "bitmsghash.so")):
init()
notifyBuild(True)
else:
notifyBuild(True)
except:
notifyBuild(True)
def run(target, initialHash):
if state.shutdown != 0:
raise
target = int(target)
if openclpow.openclEnabled():
# trialvalue1, nonce1 = _doGPUPoW(target, initialHash)
# trialvalue, nonce = _doFastPoW(target, initialHash)
# print "GPU: %s, %s" % (trialvalue1, nonce1)
# print "Fast: %s, %s" % (trialvalue, nonce)
# return [trialvalue, nonce]
try:
return _doGPUPoW(target, initialHash)
except StopIteration:
raise
except:
pass # fallback
if bmpow:
try:
return _doCPoW(target, initialHash)
except StopIteration:
raise
except:
pass # fallback
if paths.frozen == "macosx_app" or not paths.frozen:
# on my (Peter Surda) Windows 10, Windows Defender
# does not like this and fights with PyBitmessage
# over CPU, resulting in very slow PoW
# added on 2015-11-29: multiprocesing.freeze_support() doesn't help
try:
return _doFastPoW(target, initialHash)
except StopIteration:
logger.error("Fast PoW got StopIteration")
raise
except:
logger.error("Fast PoW got exception:", exc_info=True)
pass #fallback
try:
return _doSafePoW(target, initialHash)
except StopIteration:
raise
except:
pass #fallback
def resetPoW():
openclpow.initCL()
# init
def init():
global bitmsglib, bso, bmpow
openclpow.initCL()
if "win32" == sys.platform:
if ctypes.sizeof(ctypes.c_voidp) == 4:
bitmsglib = 'bitmsghash32.dll'
else:
bitmsglib = 'bitmsghash64.dll'
try:
# MSVS
bso = ctypes.WinDLL(os.path.join(paths.codePath(), "bitmsghash", bitmsglib))
logger.info("Loaded C PoW DLL (stdcall) %s", bitmsglib)
bmpow = bso.BitmessagePOW
bmpow.restype = ctypes.c_ulonglong
_doCPoW(2**63, "")
logger.info("Successfully tested C PoW DLL (stdcall) %s", bitmsglib)
except:
logger.error("C PoW test fail.", exc_info=True)
try:
# MinGW
bso = ctypes.CDLL(os.path.join(paths.codePath(), "bitmsghash", bitmsglib))
logger.info("Loaded C PoW DLL (cdecl) %s", bitmsglib)
bmpow = bso.BitmessagePOW
bmpow.restype = ctypes.c_ulonglong
_doCPoW(2**63, "")
logger.info("Successfully tested C PoW DLL (cdecl) %s", bitmsglib)
except:
logger.error("C PoW test fail.", exc_info=True)
bso = None
else:
try:
bso = ctypes.CDLL(os.path.join(paths.codePath(), "bitmsghash", bitmsglib))
except OSError:
import glob
try:
bso = ctypes.CDLL(glob.glob(os.path.join(
paths.codePath(), "bitmsghash", "bitmsghash*.so"
))[0])
except (OSError, IndexError):
bso = None
except:
bso = None
else:
logger.info("Loaded C PoW DLL %s", bitmsglib)
if bso:
try:
bmpow = bso.BitmessagePOW
bmpow.restype = ctypes.c_ulonglong
except:
bmpow = None
else:
bmpow = None
if bmpow is None:
buildCPoW()

View File

@ -27,9 +27,10 @@ from addresses import calculateInventoryHash, encodeVarint, decodeVarint, decode
from bmconfigparser import BMConfigParser
from debug import logger
from helper_sql import sqlExecute
from inventory import Inventory
from queues import objectProcessorQueue
from version import softwareVersion
import inventory
import queues
import workprover.utils
# Service flags
@ -62,32 +63,20 @@ Header = Struct('!L12sL4s')
VersionPacket = Struct('>LqQ20s4s36sH')
# Bitfield
def calculateDoubleHash(data):
return hashlib.sha512(hashlib.sha512(data).digest()).digest()
def calculateRipeHash(data):
return hashlib.new("ripemd160", hashlib.sha512(data).digest()).digest()
def getBitfield(address):
"""Get a bitfield from an address"""
# bitfield of features supported by me (see the wiki).
bitfield = 0
# send ack
if not BMConfigParser().safeGetBoolean(address, 'dontsendack'):
bitfield |= BITFIELD_DOESACK
return pack('>I', bitfield)
def checkBitfield(bitfieldBinary, flags):
"""Check if a bitfield matches the given flags"""
bitfield, = unpack('>I', bitfieldBinary)
return (bitfield & flags) == flags
def isBitSetWithinBitfield(fourByteString, n):
"""Check if a particular bit is set in a bitfeld"""
# Uses MSB 0 bit numbering across 4 bytes of data
n = 31 - n
x, = unpack('>L', fourByteString)
return x & 2**n != 0
def calculateAddressTag(version, stream, ripe):
doubleHash = calculateDoubleHash(
encodeVarint(version) +
encodeVarint(stream) +
ripe
)
return doubleHash[: 32], doubleHash[32: ]
# ip addresses
@ -200,36 +189,6 @@ def checkSocksIP(host):
return state.socksIP == host
def isProofOfWorkSufficient(data,
nonceTrialsPerByte=0,
payloadLengthExtraBytes=0,
recvTime=0):
"""
Validate an object's Proof of Work using method described in:
https://bitmessage.org/wiki/Proof_of_work
Arguments:
int nonceTrialsPerByte (default: from default.py)
int payloadLengthExtraBytes (default: from default.py)
float recvTime (optional) UNIX epoch time when object was
received from the network (default: current system time)
Returns:
True if PoW valid and sufficient, False in all other cases
"""
if nonceTrialsPerByte < defaults.networkDefaultProofOfWorkNonceTrialsPerByte:
nonceTrialsPerByte = defaults.networkDefaultProofOfWorkNonceTrialsPerByte
if payloadLengthExtraBytes < defaults.networkDefaultPayloadLengthExtraBytes:
payloadLengthExtraBytes = defaults.networkDefaultPayloadLengthExtraBytes
endOfLifeTime, = unpack('>Q', data[8:16])
TTL = endOfLifeTime - (int(recvTime) if recvTime else int(time.time()))
if TTL < 300:
TTL = 300
POW, = unpack('>Q', hashlib.sha512(hashlib.sha512(data[
:8] + hashlib.sha512(data[8:]).digest()).digest()).digest()[0:8])
return POW <= 2 ** 64 / (nonceTrialsPerByte *
(len(data) + payloadLengthExtraBytes +
((TTL * (len(data) + payloadLengthExtraBytes)) / (2 ** 16))))
# Packet creation
@ -320,344 +279,186 @@ def assembleErrorMessage(fatal=0, banTime=0, inventoryVector='', errorText=''):
# Packet decoding
def decryptAndCheckV4Pubkey(payload, address, cryptor):
status, version, stream, ripe = decodeAddress(address)
readPosition = 20
def decryptAndCheckPubkeyPayload(data, address):
"""
Version 4 pubkeys are encrypted. This function is run when we already have the
address to which we want to try to send a message. The 'data' may come either
off of the wire or we might have had it already in our inventory when we tried
to send a msg to this particular address.
"""
# pylint: disable=unused-variable
try:
status, addressVersion, streamNumber, ripe = decodeAddress(address)
embeddedVersion, readLength = decodeVarint(payload[readPosition: readPosition + 9])
readPosition += readLength
readPosition = 20 # bypass the nonce, time, and object type
embeddedAddressVersion, varintLength = decodeVarint(data[readPosition:readPosition + 10])
readPosition += varintLength
embeddedStreamNumber, varintLength = decodeVarint(data[readPosition:readPosition + 10])
readPosition += varintLength
# We'll store the address version and stream number (and some more) in the pubkeys table.
storedData = data[20:readPosition]
embeddedStream, readLength = decodeVarint(payload[readPosition: readPosition + 9])
readPosition += readLength
except:
return None
if addressVersion != embeddedAddressVersion:
logger.info('Pubkey decryption was UNsuccessful due to address version mismatch.')
return 'failed'
if streamNumber != embeddedStreamNumber:
logger.info('Pubkey decryption was UNsuccessful due to stream number mismatch.')
return 'failed'
if embeddedVersion != 4:
logger.info("Pubkey decryption failed due to address version mismatch")
tag = data[readPosition:readPosition + 32]
readPosition += 32
# the time through the tag. More data is appended onto signedData below after the decryption.
signedData = data[8:readPosition]
encryptedData = data[readPosition:]
return None
# Let us try to decrypt the pubkey
toAddress, cryptorObject = state.neededPubkeys[tag]
if toAddress != address:
logger.critical(
'decryptAndCheckPubkeyPayload failed due to toAddress mismatch.'
' This is very peculiar. toAddress: %s, address %s',
toAddress,
address)
# the only way I can think that this could happen is if someone encodes their address data two different
# ways. That sort of address-malleability should have been caught by the UI or API and an error given to
# the user.
return 'failed'
try:
decryptedData = cryptorObject.decrypt(encryptedData)
except:
# Someone must have encrypted some data with a different key
# but tagged it with a tag for which we are watching.
logger.info('Pubkey decryption was unsuccessful.')
return 'failed'
if embeddedStream != stream:
logger.info("Pubkey decryption failed due to stream number mismatch")
readPosition = 0
bitfieldBehaviors = decryptedData[readPosition:readPosition + 4]
return None
result = payload[20: readPosition]
tag = payload[readPosition: readPosition + 32]
readPosition += 32
if len(tag) < 32:
return None
signedData = payload[8: readPosition]
ciphertext = payload[readPosition: ]
try:
plaintext = cryptor.decrypt(ciphertext)
except:
logger.info("Pubkey decryption failed")
return None
readPosition = 0
try:
bitfield = unpack(">I", plaintext[readPosition: readPosition + 4])
readPosition += 4
publicSigningKey = '\x04' + decryptedData[readPosition:readPosition + 64]
readPosition += 64
publicEncryptionKey = '\x04' + decryptedData[readPosition:readPosition + 64]
readPosition += 64
specifiedNonceTrialsPerByte, specifiedNonceTrialsPerByteLength = decodeVarint(
decryptedData[readPosition:readPosition + 10])
readPosition += specifiedNonceTrialsPerByteLength
specifiedPayloadLengthExtraBytes, specifiedPayloadLengthExtraBytesLength = decodeVarint(
decryptedData[readPosition:readPosition + 10])
readPosition += specifiedPayloadLengthExtraBytesLength
storedData += decryptedData[:readPosition]
signedData += decryptedData[:readPosition]
signatureLength, signatureLengthLength = decodeVarint(
decryptedData[readPosition:readPosition + 10])
readPosition += signatureLengthLength
signature = decryptedData[readPosition:readPosition + signatureLength]
except:
return None
if highlevelcrypto.verify(signedData, signature, hexlify(publicSigningKey)):
logger.info('ECDSA verify passed (within decryptAndCheckPubkeyPayload)')
else:
logger.info('ECDSA verify failed (within decryptAndCheckPubkeyPayload)')
return 'failed'
publicSigningKey = "\x04" + plaintext[readPosition: readPosition + 64]
readPosition += 64
sha = hashlib.new('sha512')
sha.update(publicSigningKey + publicEncryptionKey)
ripeHasher = hashlib.new('ripemd160')
ripeHasher.update(sha.digest())
embeddedRipe = ripeHasher.digest()
publicEncryptionKey = "\x04" + plaintext[readPosition: readPosition + 64]
readPosition += 64
if embeddedRipe != ripe:
# Although this pubkey object had the tag were were looking for and was
# encrypted with the correct encryption key, it doesn't contain the
# correct pubkeys. Someone is either being malicious or using buggy software.
logger.info('Pubkey decryption was UNsuccessful due to RIPE mismatch.')
return 'failed'
if len(publicSigningKey) != 65 or len(publicEncryptionKey) != 65:
return None
# Everything checked out. Insert it into the pubkeys table.
embeddedRipe = calculateRipeHash(publicSigningKey + publicEncryptionKey)
logger.info(
os.linesep.join([
'within decryptAndCheckPubkeyPayload,'
' addressVersion: %s, streamNumber: %s' % addressVersion, streamNumber,
'ripe %s' % hexlify(ripe),
'publicSigningKey in hex: %s' % hexlify(publicSigningKey),
'publicEncryptionKey in hex: %s' % hexlify(publicEncryptionKey),
])
)
if embeddedRipe != ripe:
logger.info("Pubkey decryption failed due to RIPE mismatch")
t = (address, addressVersion, storedData, int(time.time()), 'yes')
sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?,?)''', *t)
return 'successful'
except varintDecodeError:
logger.info('Pubkey decryption was UNsuccessful due to a malformed varint.')
return 'failed'
except Exception:
logger.critical(
'Pubkey decryption was UNsuccessful because of an unhandled exception! This is definitely a bug! \n%s',
traceback.format_exc())
return 'failed'
return None
def checkAndShareObjectWithPeers(data):
"""
This function is called after either receiving an object off of the wire
or after receiving one as ackdata.
Returns the length of time that we should reserve to process this message
if we are receiving it off of the wire.
"""
if len(data) > 2 ** 18:
logger.info('The payload length of this object is too large (%s bytes). Ignoring it.', len(data))
return 0
# Let us check to make sure that the proof of work is sufficient.
if not isProofOfWorkSufficient(data):
logger.info('Proof of work is insufficient.')
return 0
endOfLifeTime, = unpack('>Q', data[8:16])
# The TTL may not be larger than 28 days + 3 hours of wiggle room
if endOfLifeTime - int(time.time()) > 28 * 24 * 60 * 60 + 10800:
logger.info('This object\'s End of Life time is too far in the future. Ignoring it. Time is %s', endOfLifeTime)
return 0
if endOfLifeTime - int(time.time()) < - 3600: # The EOL time was more than an hour ago. That's too much.
logger.info(
'This object\'s End of Life time was more than an hour ago. Ignoring the object. Time is %s',
endOfLifeTime)
return 0
intObjectType, = unpack('>I', data[16:20])
try:
if intObjectType == 0:
_checkAndShareGetpubkeyWithPeers(data)
return 0.1
elif intObjectType == 1:
_checkAndSharePubkeyWithPeers(data)
return 0.1
elif intObjectType == 2:
_checkAndShareMsgWithPeers(data)
return 0.6
elif intObjectType == 3:
_checkAndShareBroadcastWithPeers(data)
return 0.6
_checkAndShareUndefinedObjectWithPeers(data)
return 0.6
except varintDecodeError as err:
logger.debug(
"There was a problem with a varint while checking to see whether it was appropriate to share an object"
" with peers. Some details: %s", err
)
except Exception:
logger.critical(
'There was a problem while checking to see whether it was appropriate to share an object with peers.'
' This is definitely a bug! %s%s' % os.linesep, traceback.format_exc()
)
return 0
byteDifficulty, readLength = decodeVarint(plaintext[readPosition: readPosition + 9])
readPosition += readLength
lengthExtension, readLength = decodeVarint(plaintext[readPosition: readPosition + 9])
readPosition += readLength
except:
return None
def _checkAndShareUndefinedObjectWithPeers(data):
# pylint: disable=unused-variable
embeddedTime, = unpack('>Q', data[8:16])
readPosition = 20 # bypass nonce, time, and object type
objectVersion, objectVersionLength = decodeVarint(
data[readPosition:readPosition + 9])
readPosition += objectVersionLength
streamNumber, streamNumberLength = decodeVarint(
data[readPosition:readPosition + 9])
if streamNumber not in state.streamsInWhichIAmParticipating:
logger.debug('The streamNumber %s isn\'t one we are interested in.', streamNumber)
return
result += plaintext[: readPosition]
signedData += plaintext[: readPosition]
inventoryHash = calculateInventoryHash(data)
if inventoryHash in Inventory():
logger.debug('We have already received this undefined object. Ignoring.')
return
objectType, = unpack('>I', data[16:20])
Inventory()[inventoryHash] = (
objectType, streamNumber, data, embeddedTime, '')
logger.debug('advertising inv with hash: %s', hexlify(inventoryHash))
broadcastToSendDataQueues((streamNumber, 'advertiseobject', inventoryHash))
signatureLength, readLength = decodeVarint(plaintext[readPosition: readPosition + 9])
readPosition += readLength
signature = plaintext[readPosition: readPosition + signatureLength]
def _checkAndShareMsgWithPeers(data):
embeddedTime, = unpack('>Q', data[8:16])
readPosition = 20 # bypass nonce, time, and object type
objectVersion, objectVersionLength = decodeVarint( # pylint: disable=unused-variable
data[readPosition:readPosition + 9])
readPosition += objectVersionLength
streamNumber, streamNumberLength = decodeVarint(
data[readPosition:readPosition + 9])
if streamNumber not in state.streamsInWhichIAmParticipating:
logger.debug('The streamNumber %s isn\'t one we are interested in.', streamNumber)
return
readPosition += streamNumberLength
inventoryHash = calculateInventoryHash(data)
if inventoryHash in Inventory():
logger.debug('We have already received this msg message. Ignoring.')
return
# This msg message is valid. Let's let our peers know about it.
objectType = 2
Inventory()[inventoryHash] = (
objectType, streamNumber, data, embeddedTime, '')
logger.debug('advertising inv with hash: %s', hexlify(inventoryHash))
broadcastToSendDataQueues((streamNumber, 'advertiseobject', inventoryHash))
if len(signature) != signatureLength:
return None
# Now let's enqueue it to be processed ourselves.
objectProcessorQueue.put((objectType, data))
if not highlevelcrypto.verify(signedData, signature, hexlify(publicSigningKey)):
logger.info("Invalid signature on a pubkey")
return None
def _checkAndShareGetpubkeyWithPeers(data):
# pylint: disable=unused-variable
if len(data) < 42:
logger.info('getpubkey message doesn\'t contain enough data. Ignoring.')
return
if len(data) > 200:
logger.info('getpubkey is abnormally long. Sanity check failed. Ignoring object.')
embeddedTime, = unpack('>Q', data[8:16])
readPosition = 20 # bypass the nonce, time, and object type
requestedAddressVersionNumber, addressVersionLength = decodeVarint(
data[readPosition:readPosition + 10])
readPosition += addressVersionLength
streamNumber, streamNumberLength = decodeVarint(
data[readPosition:readPosition + 10])
if streamNumber not in state.streamsInWhichIAmParticipating:
logger.debug('The streamNumber %s isn\'t one we are interested in.', streamNumber)
return
readPosition += streamNumberLength
return result
inventoryHash = calculateInventoryHash(data)
if inventoryHash in Inventory():
logger.debug('We have already received this getpubkey request. Ignoring it.')
return
def checkAndShareObjectWithPeers(payload):
if len(payload) > 2 ** 18:
logger.info("The payload length of this object is too large (%i bytes)", len(payload))
objectType = 0
Inventory()[inventoryHash] = (
objectType, streamNumber, data, embeddedTime, '')
# This getpubkey request is valid. Forward to peers.
logger.debug('advertising inv with hash: %s', hexlify(inventoryHash))
broadcastToSendDataQueues((streamNumber, 'advertiseobject', inventoryHash))
return None
# Now let's queue it to be processed ourselves.
objectProcessorQueue.put((objectType, data))
if not workprover.utils.checkWorkSufficient(
payload,
defaults.networkDefaultProofOfWorkNonceTrialsPerByte,
defaults.networkDefaultPayloadLengthExtraBytes
):
logger.info("Proof of work is insufficient")
return None
def _checkAndSharePubkeyWithPeers(data):
if len(data) < 146 or len(data) > 440: # sanity check
return
embeddedTime, = unpack('>Q', data[8:16])
readPosition = 20 # bypass the nonce, time, and object type
addressVersion, varintLength = decodeVarint(
data[readPosition:readPosition + 10])
readPosition += varintLength
streamNumber, varintLength = decodeVarint(
data[readPosition:readPosition + 10])
readPosition += varintLength
if streamNumber not in state.streamsInWhichIAmParticipating:
logger.debug('The streamNumber %s isn\'t one we are interested in.', streamNumber)
return
if addressVersion >= 4:
tag = data[readPosition:readPosition + 32]
logger.debug('tag in received pubkey is: %s', hexlify(tag))
readPosition = 8
try:
expiryTime, objectType = unpack(">QI", payload[readPosition: readPosition + 12])
readPosition += 12
version, readLength = decodeVarint(payload[readPosition: readPosition + 9])
readPosition += readLength
stream, readLength = decodeVarint(payload[readPosition: readPosition + 9])
readPosition += readLength
except:
logger.info("Error parsing object header")
return None
tag = payload[readPosition: readPosition + 32]
TTL = expiryTime - int(time.time())
# TTL may not be lesser than -1 hour or larger than 28 days + 3 hours of wiggle room
if TTL < -3600:
logger.info("This object\'s expiry time was more than an hour ago: %s", expiryTime)
return None
elif TTL > 28 * 24 * 60 * 60 + 10800:
logger.info("This object\'s expiry time is too far in the future: %s", expiryTime)
return None
if stream not in state.streamsInWhichIAmParticipating:
logger.info("The stream number %i isn\'t one we are interested in", stream)
return None
if objectType == 0:
if len(payload) < 42:
logger.info("Too short \"getpubkey\" message")
return None
elif objectType == 1:
if len(payload) < 146 or len(payload) > 440:
logger.info("Invalid length \"pubkey\"")
return None
elif objectType == 3:
if len(payload) < 180:
logger.info("Too short \"broadcast\" message")
return None
if version == 1:
logger.info("Obsolete \"broadcast\" message version")
return None
inventoryHash = calculateDoubleHash(payload)[: 32]
if inventoryHash in inventory.Inventory():
logger.info("We already have this object")
return inventoryHash
else:
tag = ''
inventory.Inventory()[inventoryHash] = objectType, stream, payload, expiryTime, buffer(tag)
queues.invQueue.put((stream, inventoryHash))
inventoryHash = calculateInventoryHash(data)
if inventoryHash in Inventory():
logger.debug('We have already received this pubkey. Ignoring it.')
return
objectType = 1
Inventory()[inventoryHash] = (
objectType, streamNumber, data, embeddedTime, tag)
# This object is valid. Forward it to peers.
logger.debug('advertising inv with hash: %s', hexlify(inventoryHash))
broadcastToSendDataQueues((streamNumber, 'advertiseobject', inventoryHash))
logger.info("Broadcasting inventory object with hash: %s", hexlify(inventoryHash))
# Now let's queue it to be processed ourselves.
objectProcessorQueue.put((objectType, data))
def _checkAndShareBroadcastWithPeers(data):
if len(data) < 180:
logger.debug(
'The payload length of this broadcast packet is unreasonably low. '
'Someone is probably trying funny business. Ignoring message.')
return
embeddedTime, = unpack('>Q', data[8:16])
readPosition = 20 # bypass the nonce, time, and object type
broadcastVersion, broadcastVersionLength = decodeVarint(
data[readPosition:readPosition + 10])
readPosition += broadcastVersionLength
if broadcastVersion >= 2:
streamNumber, streamNumberLength = decodeVarint(data[readPosition:readPosition + 10])
readPosition += streamNumberLength
if streamNumber not in state.streamsInWhichIAmParticipating:
logger.debug('The streamNumber %s isn\'t one we are interested in.', streamNumber)
return
if broadcastVersion >= 3:
tag = data[readPosition:readPosition + 32]
else:
tag = ''
inventoryHash = calculateInventoryHash(data)
if inventoryHash in Inventory():
logger.debug('We have already received this broadcast object. Ignoring.')
return
# It is valid. Let's let our peers know about it.
objectType = 3
Inventory()[inventoryHash] = (
objectType, streamNumber, data, embeddedTime, tag)
# This object is valid. Forward it to peers.
logger.debug('advertising inv with hash: %s', hexlify(inventoryHash))
broadcastToSendDataQueues((streamNumber, 'advertiseobject', inventoryHash))
# Now let's queue it to be processed ourselves.
objectProcessorQueue.put((objectType, data))
def broadcastToSendDataQueues(data):
"""
If you want to command all of the sendDataThreads to do something, like shutdown or send some data, this
function puts your data into the queues for each of the sendDataThreads. The sendDataThreads are
responsible for putting their queue into (and out of) the sendDataQueues list.
"""
for q in state.sendDataQueues:
q.put(data)
queues.objectProcessorQueue.put((objectType, payload))
return inventoryHash
# sslProtocolVersion
if sys.version_info >= (2, 7, 13):

View File

@ -14,3 +14,4 @@ portCheckerQueue = Queue.Queue()
receiveDataQueue = Queue.Queue()
apiAddressGeneratorReturnQueue = Queue.Queue(
) # The address generator thread uses this queue to get information back to the API thread.
processedRawObjectsQueue = Queue.Queue()

View File

@ -24,8 +24,6 @@ from addresses import (
calculateInventoryHash
)
from helper_sql import sqlQuery, sqlExecute
from inventory import Inventory
from queues import objectProcessorQueue
verbose = 1
@ -68,7 +66,6 @@ alreadyAttemptedConnectionsListLock = threading.Lock()
alreadyAttemptedConnectionsListResetTime = int(time.time())
# A list of the amounts of time it took to successfully decrypt msg messages
successfullyDecryptMessageTimings = []
ackdataForWhichImWatching = {}
# used by API command clientStatus
clientHasReceivedIncomingConnections = False
numberOfMessagesProcessed = 0
@ -287,392 +284,6 @@ def fixSensitiveFilePermissions(filename, hasEnabledKeys):
logger.exception('Keyfile permissions could not be fixed.')
raise
def isBitSetWithinBitfield(fourByteString, n):
# Uses MSB 0 bit numbering across 4 bytes of data
n = 31 - n
x, = unpack('>L', fourByteString)
return x & 2**n != 0
def decryptAndCheckPubkeyPayload(data, address):
"""
Version 4 pubkeys are encrypted. This function is run when we
already have the address to which we want to try to send a message.
The 'data' may come either off of the wire or we might have had it
already in our inventory when we tried to send a msg to this
particular address.
"""
try:
# status
_, addressVersion, streamNumber, ripe = decodeAddress(address)
readPosition = 20 # bypass the nonce, time, and object type
embeddedAddressVersion, varintLength = \
decodeVarint(data[readPosition:readPosition + 10])
readPosition += varintLength
embeddedStreamNumber, varintLength = \
decodeVarint(data[readPosition:readPosition + 10])
readPosition += varintLength
# We'll store the address version and stream number
# (and some more) in the pubkeys table.
storedData = data[20:readPosition]
if addressVersion != embeddedAddressVersion:
logger.info(
'Pubkey decryption was UNsuccessful'
' due to address version mismatch.')
return 'failed'
if streamNumber != embeddedStreamNumber:
logger.info(
'Pubkey decryption was UNsuccessful'
' due to stream number mismatch.')
return 'failed'
tag = data[readPosition:readPosition + 32]
readPosition += 32
# the time through the tag. More data is appended onto
# signedData below after the decryption.
signedData = data[8:readPosition]
encryptedData = data[readPosition:]
# Let us try to decrypt the pubkey
toAddress, cryptorObject = state.neededPubkeys[tag]
if toAddress != address:
logger.critical(
'decryptAndCheckPubkeyPayload failed due to toAddress'
' mismatch. This is very peculiar.'
' toAddress: %s, address %s',
toAddress, address
)
# the only way I can think that this could happen
# is if someone encodes their address data two different ways.
# That sort of address-malleability should have been caught
# by the UI or API and an error given to the user.
return 'failed'
try:
decryptedData = cryptorObject.decrypt(encryptedData)
except:
# Someone must have encrypted some data with a different key
# but tagged it with a tag for which we are watching.
logger.info('Pubkey decryption was unsuccessful.')
return 'failed'
readPosition = 0
# bitfieldBehaviors = decryptedData[readPosition:readPosition + 4]
readPosition += 4
publicSigningKey = \
'\x04' + decryptedData[readPosition:readPosition + 64]
readPosition += 64
publicEncryptionKey = \
'\x04' + decryptedData[readPosition:readPosition + 64]
readPosition += 64
specifiedNonceTrialsPerByte, specifiedNonceTrialsPerByteLength = \
decodeVarint(decryptedData[readPosition:readPosition + 10])
readPosition += specifiedNonceTrialsPerByteLength
specifiedPayloadLengthExtraBytes, \
specifiedPayloadLengthExtraBytesLength = \
decodeVarint(decryptedData[readPosition:readPosition + 10])
readPosition += specifiedPayloadLengthExtraBytesLength
storedData += decryptedData[:readPosition]
signedData += decryptedData[:readPosition]
signatureLength, signatureLengthLength = \
decodeVarint(decryptedData[readPosition:readPosition + 10])
readPosition += signatureLengthLength
signature = decryptedData[readPosition:readPosition + signatureLength]
if not highlevelcrypto.verify(
signedData, signature, hexlify(publicSigningKey)):
logger.info(
'ECDSA verify failed (within decryptAndCheckPubkeyPayload)')
return 'failed'
logger.info(
'ECDSA verify passed (within decryptAndCheckPubkeyPayload)')
sha = hashlib.new('sha512')
sha.update(publicSigningKey + publicEncryptionKey)
ripeHasher = hashlib.new('ripemd160')
ripeHasher.update(sha.digest())
embeddedRipe = ripeHasher.digest()
if embeddedRipe != ripe:
# Although this pubkey object had the tag were were looking for
# and was encrypted with the correct encryption key,
# it doesn't contain the correct pubkeys. Someone is
# either being malicious or using buggy software.
logger.info(
'Pubkey decryption was UNsuccessful due to RIPE mismatch.')
return 'failed'
# Everything checked out. Insert it into the pubkeys table.
logger.info(
'within decryptAndCheckPubkeyPayload, '
'addressVersion: %s, streamNumber: %s\nripe %s\n'
'publicSigningKey in hex: %s\npublicEncryptionKey in hex: %s',
addressVersion, streamNumber, hexlify(ripe),
hexlify(publicSigningKey), hexlify(publicEncryptionKey)
)
t = (address, addressVersion, storedData, int(time.time()), 'yes')
sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?,?)''', *t)
return 'successful'
except varintDecodeError:
logger.info(
'Pubkey decryption was UNsuccessful due to a malformed varint.')
return 'failed'
except Exception:
logger.critical(
'Pubkey decryption was UNsuccessful because of'
' an unhandled exception! This is definitely a bug! \n%s' %
traceback.format_exc()
)
return 'failed'
def checkAndShareObjectWithPeers(data):
"""
This function is called after either receiving an object
off of the wire or after receiving one as ackdata.
Returns the length of time that we should reserve to process
this message if we are receiving it off of the wire.
"""
if len(data) > 2 ** 18:
logger.info(
'The payload length of this object is too large (%i bytes).'
' Ignoring it.', len(data)
)
return 0
# Let us check to make sure that the proof of work is sufficient.
if not protocol.isProofOfWorkSufficient(data):
logger.info('Proof of work is insufficient.')
return 0
endOfLifeTime, = unpack('>Q', data[8:16])
# The TTL may not be larger than 28 days + 3 hours of wiggle room
if endOfLifeTime - int(time.time()) > 28 * 24 * 60 * 60 + 10800:
logger.info(
'This object\'s End of Life time is too far in the future.'
' Ignoring it. Time is %s', endOfLifeTime
)
return 0
# The EOL time was more than an hour ago. That's too much.
if endOfLifeTime - int(time.time()) < -3600:
logger.info(
'This object\'s End of Life time was more than an hour ago.'
' Ignoring the object. Time is %s' % endOfLifeTime
)
return 0
intObjectType, = unpack('>I', data[16:20])
try:
if intObjectType == 0:
_checkAndShareGetpubkeyWithPeers(data)
return 0.1
elif intObjectType == 1:
_checkAndSharePubkeyWithPeers(data)
return 0.1
elif intObjectType == 2:
_checkAndShareMsgWithPeers(data)
return 0.6
elif intObjectType == 3:
_checkAndShareBroadcastWithPeers(data)
return 0.6
else:
_checkAndShareUndefinedObjectWithPeers(data)
return 0.6
except varintDecodeError as e:
logger.debug(
'There was a problem with a varint while checking'
' to see whether it was appropriate to share an object'
' with peers. Some details: %s' % e)
except Exception:
logger.critical(
'There was a problem while checking to see whether it was'
' appropriate to share an object with peers. This is'
' definitely a bug! \n%s' % traceback.format_exc())
return 0
def _checkAndShareUndefinedObjectWithPeers(data):
embeddedTime, = unpack('>Q', data[8:16])
readPosition = 20 # bypass nonce, time, and object type
objectVersion, objectVersionLength = decodeVarint(
data[readPosition:readPosition + 9])
readPosition += objectVersionLength
streamNumber, streamNumberLength = decodeVarint(
data[readPosition:readPosition + 9])
if streamNumber not in state.streamsInWhichIAmParticipating:
logger.debug(
'The streamNumber %i isn\'t one we are interested in.',
streamNumber
)
return
inventoryHash = calculateInventoryHash(data)
if inventoryHash in Inventory():
logger.debug(
'We have already received this undefined object. Ignoring.')
return
objectType, = unpack('>I', data[16:20])
Inventory()[inventoryHash] = (
objectType, streamNumber, data, embeddedTime, '')
logger.debug('advertising inv with hash: %s', hexlify(inventoryHash))
protocol.broadcastToSendDataQueues(
(streamNumber, 'advertiseobject', inventoryHash))
def _checkAndShareMsgWithPeers(data):
embeddedTime, = unpack('>Q', data[8:16])
readPosition = 20 # bypass nonce, time, and object type
objectVersion, objectVersionLength = \
decodeVarint(data[readPosition:readPosition + 9])
readPosition += objectVersionLength
streamNumber, streamNumberLength = \
decodeVarint(data[readPosition:readPosition + 9])
if streamNumber not in state.streamsInWhichIAmParticipating:
logger.debug(
'The streamNumber %i isn\'t one we are interested in.',
streamNumber
)
return
readPosition += streamNumberLength
inventoryHash = calculateInventoryHash(data)
if inventoryHash in Inventory():
logger.debug('We have already received this msg message. Ignoring.')
return
# This msg message is valid. Let's let our peers know about it.
objectType = 2
Inventory()[inventoryHash] = (
objectType, streamNumber, data, embeddedTime, '')
logger.debug('advertising inv with hash: %s', hexlify(inventoryHash))
protocol.broadcastToSendDataQueues(
(streamNumber, 'advertiseobject', inventoryHash))
# Now let's enqueue it to be processed ourselves.
objectProcessorQueue.put((objectType, data))
def _checkAndShareGetpubkeyWithPeers(data):
if len(data) < 42:
logger.info(
'getpubkey message doesn\'t contain enough data. Ignoring.')
return
embeddedTime, = unpack('>Q', data[8:16])
readPosition = 20 # bypass the nonce, time, and object type
requestedAddressVersionNumber, addressVersionLength = \
decodeVarint(data[readPosition:readPosition + 10])
readPosition += addressVersionLength
streamNumber, streamNumberLength = \
decodeVarint(data[readPosition:readPosition + 10])
if streamNumber not in state.streamsInWhichIAmParticipating:
logger.debug(
'The streamNumber %i isn\'t one we are interested in.',
streamNumber
)
return
readPosition += streamNumberLength
inventoryHash = calculateInventoryHash(data)
if inventoryHash in Inventory():
logger.debug(
'We have already received this getpubkey request. Ignoring it.')
return
objectType = 0
Inventory()[inventoryHash] = (
objectType, streamNumber, data, embeddedTime, '')
# This getpubkey request is valid. Forward to peers.
logger.debug('advertising inv with hash: %s', hexlify(inventoryHash))
protocol.broadcastToSendDataQueues(
(streamNumber, 'advertiseobject', inventoryHash))
# Now let's queue it to be processed ourselves.
objectProcessorQueue.put((objectType, data))
def _checkAndSharePubkeyWithPeers(data):
if len(data) < 146 or len(data) > 440: # sanity check
return
embeddedTime, = unpack('>Q', data[8:16])
readPosition = 20 # bypass the nonce, time, and object type
addressVersion, varintLength = \
decodeVarint(data[readPosition:readPosition + 10])
readPosition += varintLength
streamNumber, varintLength = \
decodeVarint(data[readPosition:readPosition + 10])
readPosition += varintLength
if streamNumber not in state.streamsInWhichIAmParticipating:
logger.debug(
'The streamNumber %i isn\'t one we are interested in.',
streamNumber
)
return
if addressVersion >= 4:
tag = data[readPosition:readPosition + 32]
logger.debug('tag in received pubkey is: %s', hexlify(tag))
else:
tag = ''
inventoryHash = calculateInventoryHash(data)
if inventoryHash in Inventory():
logger.debug('We have already received this pubkey. Ignoring it.')
return
objectType = 1
Inventory()[inventoryHash] = (
objectType, streamNumber, data, embeddedTime, tag)
# This object is valid. Forward it to peers.
logger.debug('advertising inv with hash: %s', hexlify(inventoryHash))
protocol.broadcastToSendDataQueues(
(streamNumber, 'advertiseobject', inventoryHash))
# Now let's queue it to be processed ourselves.
objectProcessorQueue.put((objectType, data))
def _checkAndShareBroadcastWithPeers(data):
if len(data) < 180:
logger.debug(
'The payload length of this broadcast packet is unreasonably low.'
' Someone is probably trying funny business. Ignoring message.')
return
embeddedTime, = unpack('>Q', data[8:16])
readPosition = 20 # bypass the nonce, time, and object type
broadcastVersion, broadcastVersionLength = \
decodeVarint(data[readPosition:readPosition + 10])
readPosition += broadcastVersionLength
if broadcastVersion >= 2:
streamNumber, streamNumberLength = \
decodeVarint(data[readPosition:readPosition + 10])
readPosition += streamNumberLength
if streamNumber not in state.streamsInWhichIAmParticipating:
logger.debug(
'The streamNumber %i isn\'t one we are interested in.',
streamNumber
)
return
if broadcastVersion >= 3:
tag = data[readPosition:readPosition+32]
else:
tag = ''
inventoryHash = calculateInventoryHash(data)
if inventoryHash in Inventory():
logger.debug(
'We have already received this broadcast object. Ignoring.')
return
# It is valid. Let's let our peers know about it.
objectType = 3
Inventory()[inventoryHash] = (
objectType, streamNumber, data, embeddedTime, tag)
# This object is valid. Forward it to peers.
logger.debug('advertising inv with hash: %s', hexlify(inventoryHash))
protocol.broadcastToSendDataQueues(
(streamNumber, 'advertiseobject', inventoryHash))
# Now let's queue it to be processed ourselves.
objectProcessorQueue.put((objectType, data))
def openKeysFile():
if 'linux' in sys.platform:
subprocess.call(["xdg-open", state.appdata + 'keys.dat'])

1099
src/singleworker.py Normal file
View File

@ -0,0 +1,1099 @@
import binascii
import collections
import hashlib
import os.path
import struct
import threading
import time
import addresses
import bmconfigparser
import debug
import defaults
import helper_msgcoding
import helper_sql
import helper_random
import helper_threading
import highlevelcrypto
import inventory
import l10n
import paths
import protocol
import shared
import state
import tr
import queues
import workprover
# Message status flow:
#
# +----------------------------------------------------------------------------------------+
# v |
# +-> msgqueued -+---------------------------------------->+-+-> doingmsgpow -+-> msgsent -+-> ackreceived
# ^ | ^ | |
# | +-> awaitingpubkey -+-> doingpubkeypow -+ | | +-> msgsentnoackexpected
# | ^ v | | | |
# +--------------+-------------------+-------------------+ | | +-> badkey
# | |
# | +-> toodifficult --> forcepow -+
# | |
# +--------------------------------+
#
# Can also be "msgcanceled"
# Broadcast status flow:
#
# broadcastqueued --> doingbroadcastpow --> broadcastsent
#
# Can also be "broadcastcanceled"
# TODO: queued pubkey messages are not saved to the database, they disappear when the client is closed
AddressProperties = collections.namedtuple("AddressProperties", [
"version", "stream", "ripe",
"own", "chan", "bitfield", "byteDifficulty", "lengthExtension",
"secretSigningKey", "secretEncryptionKey", "publicSigningKey", "publicEncryptionKey"
])
def getMyAddressProperties(address, defaultDifficulty = False):
status, version, stream, ripe = addresses.decodeAddress(address)
if defaultDifficulty:
byteDifficulty = defaults.networkDefaultProofOfWorkNonceTrialsPerByte
lengthExtension = defaults.networkDefaultPayloadLengthExtraBytes
else:
byteDifficulty = bmconfigparser.BMConfigParser().safeGetInt(address, "noncetrialsperbyte", None)
lengthExtension = bmconfigparser.BMConfigParser().safeGetInt(address, "payloadlengthextrabytes", None)
chan = bmconfigparser.BMConfigParser().safeGetBoolean(address, "chan")
bitfield = 0
if not bmconfigparser.BMConfigParser().safeGetBoolean(address, "dontsendack"):
bitfield |= protocol.BITFIELD_DOESACK
secretSigningKeyBase58 = bmconfigparser.BMConfigParser().get(address, "privsigningkey")
secretEncryptionKeyBase58 = bmconfigparser.BMConfigParser().get(address, "privencryptionkey")
secretSigningKey = shared.decodeWalletImportFormat(secretSigningKeyBase58)
secretEncryptionKey = shared.decodeWalletImportFormat(secretEncryptionKeyBase58)
publicSigningKey = binascii.unhexlify(highlevelcrypto.privToPub(binascii.hexlify(secretSigningKey)))
publicEncryptionKey = binascii.unhexlify(highlevelcrypto.privToPub(binascii.hexlify(secretEncryptionKey)))
return AddressProperties(
version, stream, ripe,
True, chan, bitfield, byteDifficulty, lengthExtension,
secretSigningKey, secretEncryptionKey, publicSigningKey, publicEncryptionKey
)
def parsePubkeyMessage(encoded):
readPosition = 0
version, readLength = addresses.decodeVarint(encoded[readPosition: readPosition + 9])
readPosition += readLength
stream, readLength = addresses.decodeVarint(encoded[readPosition: readPosition + 9])
readPosition += readLength
bitfield, = struct.unpack(">I", encoded[readPosition: readPosition + 4])
readPosition += 4
publicSigningKey = "\x04" + encoded[readPosition: readPosition + 64]
readPosition += 64
publicEncryptionKey = "\x04" + encoded[readPosition: readPosition + 64]
readPosition += 64
ripe = protocol.calculateRipeHash(publicSigningKey + publicEncryptionKey)
if version < 3:
byteDifficulty = defaults.networkDefaultProofOfWorkNonceTrialsPerByte
lengthExtension = defaults.networkDefaultPayloadLengthExtraBytes
else:
byteDifficulty, readLength = addresses.decodeVarint(encoded[readPosition: readPosition + 9])
readPosition += readLength
lengthExtension, readLength = addresses.decodeVarint(encoded[readPosition: readPosition + 9])
readPosition += readLength
byteDifficulty = max(defaults.networkDefaultProofOfWorkNonceTrialsPerByte, byteDifficulty)
lengthExtension = max(defaults.networkDefaultPayloadLengthExtraBytes, lengthExtension)
return AddressProperties(
version, stream, ripe,
False, False, bitfield, byteDifficulty, lengthExtension,
None, None, publicSigningKey, publicEncryptionKey
)
def getDestinationAddressProperties(address):
# Search own and chan addresses
try:
return getMyAddressProperties(address, True)
except:
pass
# Search the "pubkeys" table in the database
status, version, stream, ripe = addresses.decodeAddress(address)
if version == 4:
secretEncryptionKey, tag = protocol.calculateAddressTag(version, stream, ripe)
cryptor = highlevelcrypto.makeCryptor(binascii.hexlify(secretEncryptionKey))
alreadyNeeded = tag in state.neededPubkeys
state.neededPubkeys[tag] = address, cryptor
else:
alreadyNeeded = address in state.neededPubkeys
state.neededPubkeys[address] = None
helper_sql.sqlExecute("""UPDATE "pubkeys" SET "usedpersonally" = 'yes' WHERE "address" == ?;""", address)
encodedPubkeys = helper_sql.sqlQuery("""SELECT "transmitdata" FROM "pubkeys" WHERE "address" == ?;""", address)
result = None
if len(encodedPubkeys) != 0:
result = parsePubkeyMessage(encodedPubkeys[-1][0])
# Search the inventory for encrypted keys
if result is None and version == 4:
for i in inventory.Inventory().by_type_and_tag(1, tag):
encodedPubkey = protocol.decryptAndCheckV4Pubkey(i.payload, address, cryptor)
if encodedPubkey is None:
continue
helper_sql.sqlExecute("""
INSERT INTO "pubkeys" ("address", "addressversion", "transmitdata", "time", "usedpersonally")
VALUES (?, 4, ?, ?, 'yes');
""", address, encodedPubkey, int(time.time()))
result = parsePubkeyMessage(encodedPubkey)
break
if result is not None:
if version == 4:
state.neededPubkeys.pop(tag, None)
else:
state.neededPubkeys.pop(address, None)
helper_sql.sqlExecute("""
UPDATE "sent" SET "status" = 'msgqueued'
WHERE "status" IN ('doingpubkeypow', 'awaitingpubkey') AND "toaddress" == ? AND "folder" == 'sent';
""", address)
if alreadyNeeded:
queues.workerQueue.put(("sendmessage", ))
queued = helper_sql.sqlQuery("""
SELECT "ackdata" FROM "sent"
WHERE "status" == 'msgqueued' AND "toaddress" == ? AND "folder" == 'sent';
""", address)
for i, in queued:
queues.UISignalQueue.put(("updateSentItemStatusByAckdata", (
"msgqueued",
i,
tr._translate(
"MainWindow",
"Queued."
)
)))
return result
return None
def randomizeTTL(TTL):
return TTL + helper_random.randomrandrange(-300, 300)
workProver = workprover.WorkProver(
os.path.join(paths.codePath(), "workprover"),
helper_random.randomBytes(32),
lambda status: queues.UISignalQueue.put(("updateWorkProverStatus", status)),
queues.workerQueue
)
debug.logger.info("Availabe solvers: %s", str(workProver.availableSolvers.keys()))
if "fast" not in workProver.availableSolvers:
queues.UISignalQueue.put(("updateStatusBar", (
tr._translate(
"proofofwork",
"C PoW module unavailable. Please build it."
), 1
)))
def setBestSolver():
solverName = bmconfigparser.BMConfigParser().safeGet("bitmessagesettings", "powsolver", "gpu")
forkingSolverParallelism = bmconfigparser.BMConfigParser().safeGetInt("bitmessagesettings", "processes")
fastSolverParallelism = bmconfigparser.BMConfigParser().safeGetInt("bitmessagesettings", "threads")
GPUVendor = bmconfigparser.BMConfigParser().safeGet("bitmessagesettings", "opencl")
if forkingSolverParallelism < 1:
forkingSolverParallelism = workProver.defaultParallelism
if fastSolverParallelism < 1:
fastSolverParallelism = workProver.defaultParallelism
maxcores = bmconfigparser.BMConfigParser().safeGetInt("bitmessagesettings", "maxcores", None)
if maxcores is not None:
forkingSolverParallelism = min(maxcores, forkingSolverParallelism)
fastSolverParallelism = min(maxcores, fastSolverParallelism)
if solverName == "gpu" and GPUVendor is None:
solverName = "fast"
while solverName not in workProver.availableSolvers:
if solverName == "gpu":
solverName = "fast"
elif solverName == "fast":
solverName = "forking"
elif solverName == "forking":
solverName = "dumb"
bmconfigparser.BMConfigParser().set("bitmessagesettings", "powsolver", solverName)
bmconfigparser.BMConfigParser().set("bitmessagesettings", "processes", str(forkingSolverParallelism))
bmconfigparser.BMConfigParser().set("bitmessagesettings", "threads", str(fastSolverParallelism))
bmconfigparser.BMConfigParser().save()
if solverName in ["dumb", "gpu"]:
workProver.commandsQueue.put(("setSolver", solverName, None))
elif solverName == "forking":
workProver.commandsQueue.put(("setSolver", "forking", forkingSolverParallelism))
elif solverName == "fast":
workProver.commandsQueue.put(("setSolver", "fast", fastSolverParallelism))
setBestSolver()
class singleWorker(threading.Thread, helper_threading.StoppableThread):
def __init__(self):
super(self.__class__, self).__init__(name = "singleWorker")
self.initStop()
def stopThread(self):
queues.workerQueue.put(("stopThread", "data"))
workProver.commandsQueue.put(("shutdown", ))
super(self.__class__, self).stopThread()
def run(self):
workProver.start()
self.startedWorks = {}
# Give some time for the GUI to start
# TODO: use a condition variable
self.stop.wait(10)
queues.workerQueue.put(("sendmessage", ))
queues.workerQueue.put(("sendbroadcast", ))
while state.shutdown == 0:
queueItem = queues.workerQueue.get()
command, arguments = queueItem[0], queueItem[1: ]
if command == "sendmessage":
self.sendMessages()
elif command == "cancelMessage":
self.cancelMessage(*arguments)
elif command == "sendbroadcast":
self.sendBroadcasts()
elif command == "cancelBroadcast":
self.cancelBroadcast(*arguments)
elif command == "sendMyPubkey":
self.sendMyPubkey(*arguments)
elif command == "requestPubkey":
self.requestPubkey(*arguments)
elif command == "sendRawObject":
self.sendRawObject(*arguments)
elif command == "cancelRawObject":
self.cancelRawObject(*arguments)
elif command == "GPUError":
self.handleGPUError(*arguments)
elif command == "taskDone":
self.workDone(*arguments)
elif command == "stopThread":
workProver.commandsQueue.put(("shutdown", ))
workProver.join()
break
debug.logger.info("Quitting...")
def handleGPUError(self):
bmconfigparser.BMConfigParser().set("bitmessagesettings", "powsolver", "dumb")
workProver.commandsQueue.put(("setSolver", "dumb", None))
debug.logger.error(
"Your GPU(s) did not calculate correctly, disabling OpenCL. Please report to the developers"
)
queues.UISignalQueue.put(("updateStatusBar", (
tr._translate(
"MainWindow",
"Your GPU(s) did not calculate correctly, disabling OpenCL. Please report to the developers."
), 1
)))
def startWork(self, ID, headlessPayload, TTL, expiryTime, byteDifficulty, lengthExtension, logPrefix, callback):
debug.logger.info(
"%s Starting work %s, payload length = %s, TTL = %s",
logPrefix, ID, 8 + 8 + len(headlessPayload), TTL
)
self.startedWorks[ID] = callback
workProver.commandsQueue.put((
"addTask", ID, headlessPayload, TTL, expiryTime,
byteDifficulty, lengthExtension
))
def workDone(self, ID, nonce, expiryTime):
debug.logger.info("Found proof of work %s", ID)
if ID in self.startedWorks:
self.startedWorks[ID](nonce + struct.pack(">Q", expiryTime))
del self.startedWorks[ID]
def sendMyPubkey(self, address):
ID = "pubkey", address
if ID in self.startedWorks:
return
try:
addressProperties = getMyAddressProperties(address)
except Exception as exception:
debug.logger.error("Could not get the properties of a requested address %s\n", exception)
return
if addressProperties.chan:
debug.logger.info("This is a chan address. Not sending pubkey")
return
if addressProperties.version == 4:
secretEncryptionKey, tag = protocol.calculateAddressTag(
addressProperties.version,
addressProperties.stream,
addressProperties.ripe
)
publicEncryptionKey = highlevelcrypto.pointMult(secretEncryptionKey)
else:
tag = ""
debug.logger.info("Sending pubkey of %s", address)
TTL = randomizeTTL(28 * 24 * 60 * 60)
expiryTime = int(time.time() + TTL)
headlessPayload = struct.pack(">I", 1)
headlessPayload += addresses.encodeVarint(addressProperties.version)
headlessPayload += addresses.encodeVarint(addressProperties.stream)
headlessPayload += tag
if addressProperties.version == 4:
plaintext = struct.pack(">I", addressProperties.bitfield)
plaintext += addressProperties.publicSigningKey[1: ]
plaintext += addressProperties.publicEncryptionKey[1: ]
plaintext += addresses.encodeVarint(addressProperties.byteDifficulty)
plaintext += addresses.encodeVarint(addressProperties.lengthExtension)
signature = highlevelcrypto.sign(
struct.pack(">Q", expiryTime) + headlessPayload + plaintext,
binascii.hexlify(addressProperties.secretSigningKey)
)
plaintext += addresses.encodeVarint(len(signature))
plaintext += signature
headlessPayload += highlevelcrypto.encrypt(plaintext, binascii.hexlify(publicEncryptionKey))
else:
headlessPayload += struct.pack(">I", addressProperties.bitfield)
headlessPayload += addressProperties.publicSigningKey[1: ]
headlessPayload += addressProperties.publicEncryptionKey[1: ]
if addressProperties.version == 3:
headlessPayload += addresses.encodeVarint(addressProperties.byteDifficulty)
headlessPayload += addresses.encodeVarint(addressProperties.lengthExtension)
signature = highlevelcrypto.sign(
struct.pack(">Q", expiryTime) + headlessPayload,
binascii.hexlify(addressProperties.secretSigningKey)
)
headlessPayload += addresses.encodeVarint(len(signature))
headlessPayload += signature
def workDone(head):
protocol.checkAndShareObjectWithPeers(head + headlessPayload)
# TODO: not atomic with the addition to the inventory, the "lastpubkeysendtime" property should be removed
# Instead check if the pubkey is present in the inventory
try:
bmconfigparser.BMConfigParser().set(address, "lastpubkeysendtime", str(int(time.time())))
bmconfigparser.BMConfigParser().save()
except:
pass
queues.UISignalQueue.put(("updateStatusBar", ""))
self.startWork(
ID, headlessPayload, TTL, expiryTime,
defaults.networkDefaultProofOfWorkNonceTrialsPerByte,
defaults.networkDefaultPayloadLengthExtraBytes,
"(For pubkey version {} message)".format(addressProperties.version),
workDone
)
def processBroadcast(self, address, subject, body, ackData, TTL, encoding):
ID = "broadcast", ackData
try:
addressProperties = getMyAddressProperties(address)
except:
queues.UISignalQueue.put(("updateSentItemStatusByAckdata", (
"broadcastqueued",
ackData,
tr._translate(
"MainWindow",
"Error! Could not find sender address (your address) in the keys.dat file."
)
)))
return
if addressProperties.version < 2:
debug.logger.error("Address version unsupported for broadcasts")
return
debug.logger.info("Sending broadcast from %s", address)
if addressProperties.version == 4:
secretEncryptionKey, tag = protocol.calculateAddressTag(
addressProperties.version,
addressProperties.stream,
addressProperties.ripe
)
else:
secretEncryptionKey = hashlib.sha512(
addresses.encodeVarint(addressProperties.version) +
addresses.encodeVarint(addressProperties.stream) +
addressProperties.ripe
).digest()[: 32]
tag = ""
publicEncryptionKey = highlevelcrypto.pointMult(secretEncryptionKey)
TTL = min(28 * 24 * 60 * 60, TTL)
TTL = max(60 * 60, TTL)
TTL = randomizeTTL(TTL)
expiryTime = int(time.time() + TTL)
headlessPayload = struct.pack(">I", 3)
if addressProperties.version == 4:
headlessPayload += addresses.encodeVarint(5)
else:
headlessPayload += addresses.encodeVarint(4)
headlessPayload += addresses.encodeVarint(addressProperties.stream)
headlessPayload += tag
plaintext = addresses.encodeVarint(addressProperties.version)
plaintext += addresses.encodeVarint(addressProperties.stream)
plaintext += struct.pack(">I", addressProperties.bitfield)
plaintext += addressProperties.publicSigningKey[1: ]
plaintext += addressProperties.publicEncryptionKey[1: ]
if addressProperties.version >= 3:
plaintext += addresses.encodeVarint(addressProperties.byteDifficulty)
plaintext += addresses.encodeVarint(addressProperties.lengthExtension)
encodedMessage = helper_msgcoding.MsgEncode({"subject": subject, "body": body}, encoding)
plaintext += addresses.encodeVarint(encoding)
plaintext += addresses.encodeVarint(encodedMessage.length)
plaintext += encodedMessage.data
signature = highlevelcrypto.sign(
struct.pack(">Q", expiryTime) + headlessPayload + plaintext,
binascii.hexlify(addressProperties.secretSigningKey)
)
plaintext += addresses.encodeVarint(len(signature))
plaintext += signature
headlessPayload += highlevelcrypto.encrypt(plaintext, binascii.hexlify(publicEncryptionKey))
if len(headlessPayload) > 2 ** 18 - (8 + 8): # 256 kiB
debug.logger.critical(
"This broadcast object is too large to send. This should never happen. Object size: %s",
len(headlessPayload)
)
return
def workDone(head):
# TODO: adding to the inventory, adding to inbox and setting the sent status should be within a single SQL transaction
inventoryHash = protocol.checkAndShareObjectWithPeers(head + headlessPayload)
helper_sql.sqlExecute("""
UPDATE "sent" SET "msgid" = ?, "status" = 'broadcastsent', "lastactiontime" = ?
WHERE "ackdata" == ?;
""", inventoryHash, int(time.time()), ackData)
queues.UISignalQueue.put(("updateSentItemStatusByAckdata", (
"broadcastsent",
ackData,
tr._translate("MainWindow", "Broadcast sent on %1").arg(l10n.formatTimestamp())
)))
helper_sql.sqlExecute("""UPDATE "sent" SET "status" = 'doingbroadcastpow' WHERE "ackdata" == ?;""", ackData)
queues.UISignalQueue.put(("updateSentItemStatusByAckdata", (
"doingbroadcastpow",
ackData,
tr._translate(
"MainWindow",
"Doing work necessary to send broadcast."
)
)))
self.startWork(
ID, headlessPayload, TTL, expiryTime,
defaults.networkDefaultProofOfWorkNonceTrialsPerByte,
defaults.networkDefaultPayloadLengthExtraBytes,
"(For broadcast message)",
workDone
)
def sendBroadcasts(self):
queued = helper_sql.sqlQuery("""
SELECT "fromaddress", "subject", "message", "ackdata", "ttl", "encodingtype" FROM "sent"
WHERE "status" == 'broadcastqueued' AND "folder" == 'sent';
""")
for i in queued:
# Must be in a separate function because of the nested callback
self.processBroadcast(*i)
def cancelBroadcast(self, ackData, delete, trash):
ID = "broadcast", ackData
if ID in self.startedWorks:
del self.startedWorks[ID]
workProver.commandsQueue.put(("cancelTask", ID))
helper_sql.sqlExecute("""
UPDATE "sent" SET "status" = 'broadcastcanceled'
WHERE "ackdata" == ? AND "status" != 'broadcastsent';
""", ackData)
if delete:
if trash:
helper_sql.sqlExecute("""UPDATE "sent" SET "folder" = 'trash' WHERE "ackdata" == ?;""", ackData)
else:
helper_sql.sqlExecute("""DELETE FROM "sent" WHERE "ackdata" == ?""", ackData)
queues.UISignalQueue.put(("deleteSentItemByAckData", ackData))
else:
queues.UISignalQueue.put(("updateSentItemStatusByAckdata", (
"broadcastcanceled",
ackData,
tr._translate(
"MainWindow",
"Broadcast canceled."
)
)))
def generateAckMessage(self, ackData, stream, TTL, callback):
ID = "ack", ackData
# It might be perfectly fine to just use the same TTL for
# the ackdata that we use for the message. But I would rather
# it be more difficult for attackers to associate ackData with
# the associated msg object. However, users would want the TTL
# of the acknowledgement to be about the same as they set
# for the message itself. So let's set the TTL of the
# acknowledgement to be in one of three 'buckets': 1 hour, 7
# days, or 28 days, whichever is relatively close to what the
# user specified.
if TTL < 24 * 60 * 60:
TTL = 24 * 60 * 60
elif TTL < 7 * 24 * 60 * 60:
TTL = 7 * 24 * 60 * 60
else:
TTL = 28 * 24 * 60 * 60
TTL = randomizeTTL(TTL)
expiryTime = int(time.time() + TTL)
def workDone(head):
callback(protocol.CreatePacket("object", head + ackData))
self.startWork(
ID, ackData, TTL, expiryTime,
defaults.networkDefaultProofOfWorkNonceTrialsPerByte,
defaults.networkDefaultPayloadLengthExtraBytes,
"(For ack message)",
workDone
)
def processMessage(self, status, destination, source, subject, body, ackData, TTL, retryNumber, encoding):
ID = "message", ackData
helper_sql.sqlExecute("""UPDATE "sent" SET "status" = 'awaitingpubkey' WHERE "ackdata" == ?;""", ackData)
destinationProperties = getDestinationAddressProperties(destination)
if destinationProperties is None:
queues.workerQueue.put(("requestPubkey", destination))
queues.UISignalQueue.put(("updateSentItemStatusByAckdata", (
"awaitingpubkey",
ackData,
tr._translate(
"MainWindow",
"Waiting for their encryption key. Will request it again soon."
)
)))
return
try:
defaultDifficulty = shared.isAddressInMyAddressBookSubscriptionsListOrWhitelist(destination)
if destinationProperties.own:
defaultDifficulty = True
sourceProperties = getMyAddressProperties(source, defaultDifficulty)
except:
queues.UISignalQueue.put(("updateSentItemStatusByAckdata", (
"msgqueued",
ackData,
tr._translate(
"MainWindow",
"Error! Could not find sender address (your address) in the keys.dat file."
)
)))
return
relativeByteDifficulty = (
float(destinationProperties.byteDifficulty) /
defaults.networkDefaultProofOfWorkNonceTrialsPerByte
)
relativeLengthExtension = (
float(destinationProperties.lengthExtension) /
defaults.networkDefaultPayloadLengthExtraBytes
)
if status != "forcepow":
maximumByteDifficulty = bmconfigparser.BMConfigParser().getint(
"bitmessagesettings", "maxacceptablenoncetrialsperbyte"
)
maximumLengthExtension = bmconfigparser.BMConfigParser().getint(
"bitmessagesettings", "maxacceptablepayloadlengthextrabytes"
)
if (
maximumByteDifficulty != 0 and destinationProperties.byteDifficulty > maximumLengthExtension or
maximumLengthExtension != 0 and destinationProperties.lengthExtension > maximumLengthExtension
):
helper_sql.sqlExecute("""UPDATE "sent" SET "status" = 'toodifficult' WHERE "ackdata" == ?;""", ackData)
queues.UISignalQueue.put(("updateSentItemStatusByAckdata", (
"toodifficult",
ackData,
tr._translate(
"MainWindow",
"Problem: The work demanded by the recipient (%1 and %2) is "
"more difficult than you are willing to do. %3"
).arg(str(relativeByteDifficulty)).arg(str(relativeLengthExtension)).arg(l10n.formatTimestamp())
)))
return
debug.logger.info("Sending message from %s to %s", source, destination)
TTL *= 2 ** retryNumber
TTL = min(28 * 24 * 60 * 60, TTL)
TTL = max(60 * 60, TTL)
TTL = randomizeTTL(TTL)
expiryTime = int(time.time() + TTL)
def ackMessageGenerated(ackMessage):
headlessPayload = struct.pack(">I", 2)
headlessPayload += addresses.encodeVarint(1)
headlessPayload += addresses.encodeVarint(destinationProperties.stream)
plaintext = addresses.encodeVarint(sourceProperties.version)
plaintext += addresses.encodeVarint(sourceProperties.stream)
plaintext += struct.pack(">I", sourceProperties.bitfield)
plaintext += sourceProperties.publicSigningKey[1: ]
plaintext += sourceProperties.publicEncryptionKey[1: ]
if sourceProperties.version >= 3:
plaintext += addresses.encodeVarint(sourceProperties.byteDifficulty)
plaintext += addresses.encodeVarint(sourceProperties.lengthExtension)
plaintext += destinationProperties.ripe # To prevent resending a signed message to a different reciever
encodedMessage = helper_msgcoding.MsgEncode({"subject": subject, "body": body}, encoding)
plaintext += addresses.encodeVarint(encoding)
plaintext += addresses.encodeVarint(encodedMessage.length)
plaintext += encodedMessage.data
if ackMessage is None:
plaintext += addresses.encodeVarint(0)
else:
plaintext += addresses.encodeVarint(len(ackMessage))
plaintext += ackMessage
signature = highlevelcrypto.sign(
struct.pack(">Q", expiryTime) + headlessPayload + plaintext,
binascii.hexlify(sourceProperties.secretSigningKey)
)
plaintext += addresses.encodeVarint(len(signature))
plaintext += signature
try:
ciphertext = highlevelcrypto.encrypt(
plaintext,
binascii.hexlify(destinationProperties.publicEncryptionKey)
)
except:
helper_sql.sqlExecute("""UPDATE "sent" SET "status" = 'badkey' WHERE "ackdata" == ?;""", ackData)
queues.UISignalQueue.put(("updateSentItemStatusByAckdata", (
"badkey",
ackData,
tr._translate(
"MainWindow",
"Problem: The recipient's encryption key is no good. Could not encrypt message. %1"
).arg(l10n.formatTimestamp())
)))
return
headlessPayload += ciphertext
if len(headlessPayload) > 2 ** 18 - (8 + 8): # 256 kiB
debug.logger.critical(
"This message object is too large to send. This should never happen. Object size: %s",
len(headlessPayload)
)
return
def workDone(head):
if ackMessage is not None:
state.watchedAckData.add(ackData)
#TODO: adding to the inventory, adding to inbox and setting the sent status should be within a single SQL transaction
inventoryHash = protocol.checkAndShareObjectWithPeers(head + headlessPayload)
if ackMessage is None:
newStatus = "msgsentnoackexpected"
sleepTill = 0
else:
newStatus = "msgsent"
sleepTill = int(time.time() + TTL * 1.1)
helper_sql.sqlExecute("""
UPDATE "sent" SET "msgid" = ?, "status" = ?, "retrynumber" = ?,
"sleeptill" = ?, "lastactiontime" = ?
WHERE "status" == 'doingmsgpow' AND "ackdata" == ?;
""", inventoryHash, newStatus, retryNumber + 1, sleepTill, int(time.time()), ackData)
if ackMessage is None:
queues.UISignalQueue.put(("updateSentItemStatusByAckdata", (
"msgsentnoackexpected",
ackData,
tr._translate(
"MainWindow",
"Message sent. Sent at %1"
).arg(l10n.formatTimestamp())
)))
else:
queues.UISignalQueue.put(("updateSentItemStatusByAckdata", (
"msgsent",
ackData,
tr._translate(
"MainWindow",
"Message sent. Waiting for acknowledgement. Sent on %1"
).arg(l10n.formatTimestamp())
)))
self.startWork(
ID, headlessPayload, TTL, expiryTime,
destinationProperties.byteDifficulty,
destinationProperties.lengthExtension,
"(For message)",
workDone
)
helper_sql.sqlExecute("""UPDATE "sent" SET "status" = 'doingmsgpow' WHERE "ackdata" == ?;""", ackData)
if relativeByteDifficulty != 1 or relativeLengthExtension != 1:
queues.UISignalQueue.put(("updateSentItemStatusByAckdata", (
"doingmsgpow",
ackData,
tr._translate(
"MainWindow",
"Doing work necessary to send message.\nReceiver's required difficulty: %1 and %2"
).arg(str(relativeByteDifficulty)).arg(str(relativeLengthExtension))
)))
else:
queues.UISignalQueue.put(("updateSentItemStatusByAckdata", (
"doingmsgpow",
ackData,
tr._translate(
"MainWindow",
"Doing work necessary to send message."
)
)))
if destinationProperties.own:
debug.logger.info("Not bothering to include ack data because we are sending to ourselves or a chan")
ackMessageGenerated(None)
elif destinationProperties.bitfield & protocol.BITFIELD_DOESACK == 0:
debug.logger.info("Not bothering to include ack data because the receiver said that they won't relay it anyway")
ackMessageGenerated(None)
else:
self.generateAckMessage(ackData, destinationProperties.stream, TTL, ackMessageGenerated)
def sendMessages(self):
queued = helper_sql.sqlQuery("""
SELECT "status", "toaddress", "fromaddress", "subject", "message",
"ackdata", "ttl", "retrynumber", "encodingtype" FROM "sent"
WHERE "status" IN ('msgqueued', 'forcepow') AND "folder" == 'sent';
""")
for i in queued:
# Must be in a separate function because of the nested callback
self.processMessage(*i)
def cancelMessage(self, ackData, delete, trash):
ID = "ack", ackData
if ID in self.startedWorks:
del self.startedWorks[ID]
workProver.commandsQueue.put(("cancelTask", ID))
ID = "message", ackData
if ID in self.startedWorks:
del self.startedWorks[ID]
workProver.commandsQueue.put(("cancelTask", ID))
state.watchedAckData -= {ackData}
queryReturn = helper_sql.sqlQuery("""SELECT "toaddress" FROM "sent" WHERE "ackdata" == ?;""", ackData)
if len(queryReturn) != 0:
destination = queryReturn[0][0]
count = helper_sql.sqlQuery("""
SELECT COUNT(*) FROM "sent"
WHERE "status" IN ('doingpubkeypow', 'awaitingpubkey') AND "toaddress" == ? AND "ackdata" != ?;
""", destination, ackData)[0][0]
if count == 0:
ID = "getpubkey", destination
if ID in self.startedWorks:
del self.startedWorks[ID]
workProver.commandsQueue.put(("cancelTask", ID))
status, version, stream, ripe = addresses.decodeAddress(destination)
if version == 4:
secretEncryptionKey, tag = protocol.calculateAddressTag(version, stream, ripe)
state.neededPubkeys.pop(tag, None)
else:
state.neededPubkeys.pop(destination, None)
helper_sql.sqlExecute("""
UPDATE "sent" SET "status" = 'msgcanceled'
WHERE "ackdata" == ? AND "status" NOT IN ('ackreceived', 'msgsentnoackexpected', 'badkey');
""", ackData)
if delete:
if trash:
helper_sql.sqlExecute("""UPDATE "sent" SET "folder" = 'trash' WHERE "ackdata" == ?;""", ackData)
else:
helper_sql.sqlExecute("""DELETE FROM "sent" WHERE "ackdata" == ?""", ackData)
queues.UISignalQueue.put(("deleteSentItemByAckData", ackData))
else:
queues.UISignalQueue.put(("updateSentItemStatusByAckdata", (
"msgcanceled",
ackData,
tr._translate(
"MainWindow",
"Message canceled."
)
)))
def requestPubkey(self, address):
ID = "getpubkey", address
if ID in self.startedWorks:
return
status, version, stream, ripe = addresses.decodeAddress(address)
# Check if a request is already in the inventory
if version == 4:
secretEncryptionKey, tag = protocol.calculateAddressTag(version, stream, ripe)
else:
tag = ripe
currentExpiryTime = None
for i in inventory.Inventory().by_type_and_tag(0, tag):
if currentExpiryTime is None:
currentExpiryTime = i.expires
else:
currentExpiryTime = max(currentExpiryTime, i.expires)
if currentExpiryTime is not None:
helper_sql.sqlExecute("""
UPDATE "sent" SET "status" = 'awaitingpubkey', "sleeptill" = ?
WHERE "status" IN ('doingpubkeypow', 'awaitingpubkey') AND "toaddress" == ? AND "folder" == 'sent';
""", currentExpiryTime, address)
queues.UISignalQueue.put(("updateSentItemStatusByToAddress", (
"awaitingpubkey",
address,
tr._translate(
"MainWindow",
"Waiting for their encryption key. Will request it again soon."
)
)))
return
debug.logger.info("Making request for version %s pubkey with tag: %s", version, binascii.hexlify(tag))
TTL = randomizeTTL(28 * 24 * 60 * 60)
expiryTime = int(time.time() + TTL)
headlessPayload = struct.pack(">I", 0)
headlessPayload += addresses.encodeVarint(version)
headlessPayload += addresses.encodeVarint(stream)
headlessPayload += tag
def workDone(head):
# TODO: adding to the inventory and setting the sent status should be within a single SQL transaction
protocol.checkAndShareObjectWithPeers(head + headlessPayload)
sleepTill = int(time.time() + TTL * 1.1)
helper_sql.sqlExecute("""
UPDATE "sent" SET "status" = 'awaitingpubkey', "sleeptill" = ?, "lastactiontime" = ?
WHERE "status" IN ('doingpubkeypow', 'awaitingpubkey') AND "toaddress" == ? AND "folder" == 'sent';
""", sleepTill, int(time.time()), address)
queues.UISignalQueue.put(("updateSentItemStatusByToAddress", (
"awaitingpubkey",
address,
tr._translate(
"MainWindow",
"Sending public key request. Waiting for reply. Requested at %1"
).arg(l10n.formatTimestamp())
)))
helper_sql.sqlExecute("""
UPDATE "sent" SET "status" = 'doingpubkeypow'
WHERE "status" == 'awaitingpubkey' AND "toaddress" == ? AND "folder" == 'sent';
""", address)
queues.UISignalQueue.put(("updateSentItemStatusByToAddress", (
"doingpubkeypow",
address,
tr._translate(
"MainWindow",
"Doing work necessary to request encryption key."
)
)))
self.startWork(
ID, headlessPayload, TTL, expiryTime,
defaults.networkDefaultProofOfWorkNonceTrialsPerByte,
defaults.networkDefaultPayloadLengthExtraBytes,
"(For getpubkey message)".format(version),
workDone
)
def sendRawObject(self, randomID, TTL, headlessPayload):
ID = "raw", randomID
debug.logger.info("Sending raw object")
expiryTime = int(time.time() + TTL)
def workDone(head):
inventoryHash = protocol.checkAndShareObjectWithPeers(head + headlessPayload)
if inventoryHash is None:
queues.processedRawObjectsQueue.put(("failed", randomID))
else:
queues.processedRawObjectsQueue.put(("sent", randomID, inventoryHash))
queues.processedRawObjectsQueue.put(("doingwork", randomID))
self.startWork(
ID, headlessPayload, TTL, expiryTime,
defaults.networkDefaultProofOfWorkNonceTrialsPerByte,
defaults.networkDefaultPayloadLengthExtraBytes,
"(For raw object)",
workDone
)
def cancelRawObject(self, randomID):
ID = "raw", randomID
if ID in self.startedWorks:
del self.startedWorks[ID]
workProver.commandsQueue.put(("cancelTask", ID))
queues.processedRawObjectsQueue.put(("canceled", randomID))

View File

@ -1,8 +1,12 @@
import collections
# Single worker assumes, that object processor checks this dict only after a pubkey is added to the inventory or the "pubkeys" table
# TODO: add locking?
neededPubkeys = {}
watchedAckData = set()
streamsInWhichIAmParticipating = []
sendDataQueues = [] # each sendData thread puts its queue in this list.
# For UPnP
extPort = None

42
src/workprover/Readme.md Normal file
View File

@ -0,0 +1,42 @@
Please keep this module independent from the outside code, so that it can be reused in other applications.
If you are going to use it, you should wrap your program's main file in this:
```python
import workprover.dumbsolver
workprover.dumbsolver.libcrypto = ...
if __name__ == "__main__":
import multiprocessing
multiprocessing.freeze_support()
...
```
See the `multiprocessing` module documentation for explaination.
Build fast solver
-----------------
On Linux, BSDs or MacOS: `make -C fastsolver`.
On Windows:
- Install OpenSSL. Build it yourself or install [third-party](https://wiki.openssl.org/index.php/Binaries) prebuilt binaries.
- Install MSVC as part of Visual Studio or standalone. Official offline installer: https://aka.ms/vcpython27.
- Open its command line and go to the `fastsolver` directory.
- Add OpenSSL paths to environment variables:
```bat
set INCLUDE=C:\OpenSSL-Win64\include;%INCLUDE%
set LIB=C:\OpenSSL-Win64\lib;%LIB%
```
- Do `cl @options.txt`.
- Append the `-32` or `-64` suffix to the DLL file name.

262
src/workprover/__init__.py Normal file
View File

@ -0,0 +1,262 @@
import Queue
import collections
import multiprocessing
import struct
import sys
import threading
import time
import dumbsolver
import fastsolver
import forkingsolver
import gpusolver
import utils
timeout = .5
class Stop(Exception):
pass
class Task(object):
previous = None
next = None
def __init__(self, headlessPayload, TTL, expiryTime, target, difficulty):
self.headlessPayload = headlessPayload
self.TTL = TTL
self.expiryTime = expiryTime
self.target = target
self.difficulty = difficulty
Status = collections.namedtuple("Status", ["solverName", "solverStatus", "speed", "tasksCount", "difficulty"])
# Only one instance allowed
class WorkProver(threading.Thread):
# Seed must be 32 bytes
def __init__(self, codePath, seed, statusUpdated, resultsQueue):
super(self.__class__, self).__init__()
self.availableSolvers = {
"dumb": dumbsolver.DumbSolver(codePath)
}
# Comment from the previous version:
# on my (Peter Surda) Windows 10, Windows Defender
# does not like this and fights with PyBitmessage
# over CPU, resulting in very slow PoW
# added on 2015-11-29: multiprocesing.freeze_support() doesn't help
if not hasattr(sys, "frozen") or sys.frozen == "macosx_app":
self.availableSolvers["forking"] = forkingsolver.ForkingSolver(codePath)
try:
self.availableSolvers["fast"] = fastsolver.FastSolver(codePath)
except fastsolver.FastSolverError:
pass
try:
self.availableSolvers["gpu"] = gpusolver.GPUSolver(codePath)
except gpusolver.GPUSolverError:
pass
try:
self.defaultParallelism = multiprocessing.cpu_count()
except NotImplementedError:
self.defaultParallelism = 1
self.seed = seed
self.roundsCounter = 0
self.statusUpdated = statusUpdated
self.commandsQueue = Queue.Queue()
if resultsQueue is None:
self.resultsQueue = Queue.Queue()
else:
self.resultsQueue = resultsQueue
self.solverName = None
self.solver = None
self.lastTime = utils.getTimePoint()
self.timedIntervals = collections.deque()
self.speed = 0
self.totalDifficulty = 0
self.tasks = {}
self.currentTaskID = None
def notifyStatus(self):
if self.statusUpdated is None:
return
status = None
if self.solver is not None:
status = self.solver.status
self.statusUpdated(Status(self.solverName, status, self.speed, len(self.tasks), self.totalDifficulty))
def setSolver(self, name, configuration):
try:
if name is None and self.solverName is None:
pass
elif name == self.solverName:
self.solver.setConfiguration(configuration)
else:
if self.solver is not None:
self.solver.setConfiguration(None)
self.solverName = None
self.solver = None
if name is not None:
if name not in self.availableSolvers:
name, configuration = "dumb", None
self.solverName = name
self.solver = self.availableSolvers[name]
self.solver.setConfiguration(configuration)
except gpusolver.GPUSolverError:
self.solverName = None
self.solver = None
self.resultsQueue.put(("GPUError", ))
self.notifyStatus()
def updateSpeed(self, iterationsCount):
currentTime = utils.getTimePoint()
duration = currentTime - self.lastTime
self.lastTime = currentTime
self.timedIntervals.append((currentTime, iterationsCount, duration))
for i in xrange(len(self.timedIntervals)):
time, iterationsCount, duration = self.timedIntervals[0]
if time + duration < currentTime - 3:
self.timedIntervals.popleft()
totalDuration = 0
totalIterationsCount = 0
for time, iterationsCount, duration in self.timedIntervals:
totalIterationsCount += iterationsCount
totalDuration += duration
if totalDuration < .25:
self.speed = 0
else:
self.speed = totalIterationsCount / totalDuration
self.notifyStatus()
def addTask(self, ID, headlessPayload, TTL, expiryTime, byteDifficulty, lengthExtension):
target, difficulty = utils.calculateTarget(8 + 8 + len(headlessPayload), TTL, byteDifficulty, lengthExtension)
task = Task(headlessPayload, TTL, expiryTime, target, difficulty)
self.tasks[ID] = task
self.totalDifficulty += difficulty
if self.currentTaskID is None:
task.previous = ID
task.next = ID
self.currentTaskID = ID
else:
task.previous = self.currentTaskID
task.next = self.tasks[self.currentTaskID].next
self.tasks[task.previous].next = ID
self.tasks[task.next].previous = ID
self.notifyStatus()
def cancelTask(self, ID):
if ID not in self.tasks:
return
task = self.tasks.pop(ID)
self.totalDifficulty -= task.difficulty
if len(self.tasks) == 0:
self.currentTaskID = None
else:
self.tasks[task.previous].next = task.next
self.tasks[task.next].previous = task.previous
if self.currentTaskID == ID:
self.currentTaskID = task.next
self.notifyStatus()
def nextTask(self):
self.currentTaskID = self.tasks[self.currentTaskID].next
def shutdown(self):
self.setSolver(None, None)
for i in self.tasks.keys():
self.cancelTask(i)
raise Stop()
def processCommand(self, command, *arguments):
getattr(self, command)(*arguments)
def round(self):
while True:
try:
self.processCommand(*self.commandsQueue.get_nowait())
except Queue.Empty:
break
while self.solver is None or self.currentTaskID is None:
try:
self.processCommand(*self.commandsQueue.get(True, timeout))
except Queue.Empty:
self.updateSpeed(0)
task = self.tasks[self.currentTaskID]
if task.expiryTime is None:
expiryTime = int(time.time() + task.TTL)
else:
expiryTime = task.expiryTime
initialPayload = struct.pack(">Q", expiryTime) + task.headlessPayload
initialHash = utils.calculateInitialHash(initialPayload)
appendedSeed = self.seed + struct.pack(">Q", self.roundsCounter)
self.roundsCounter += 1
try:
nonce, iterationsCount = self.solver.search(initialHash, task.target, appendedSeed, timeout)
except gpusolver.GPUSolverError:
self.setSolver(None, None)
self.resultsQueue.put(("GPUError", ))
nonce, iterationsCount = None, 0
self.updateSpeed(iterationsCount)
if nonce is None:
self.nextTask()
else:
self.resultsQueue.put(("taskDone", self.currentTaskID, nonce, expiryTime))
self.cancelTask(self.currentTaskID)
def run(self):
try:
while True:
self.round()
except Stop:
return
except Exception as exception:
self.resultsQueue.put(exception)

View File

@ -0,0 +1,70 @@
import ctypes
import hashlib
import struct
import utils
libcrypto = None
class DumbSolver(object):
def __init__(self, codePath):
libcrypto.SHA512.restype = ctypes.c_void_p
self.prefixes = [chr(i) for i in xrange(256)]
if ctypes.c_size_t is ctypes.c_uint:
self.proofLength = 8 + 64
self.hashLength = 64
else:
# Using the wrapper instead of a clear number slows the work down, but otherwise seems to be unsafe
self.proofLength = ctypes.c_size_t(8 + 64)
self.hashLength = ctypes.c_size_t(64)
self.firstHash = ctypes.create_string_buffer(64)
self.secondHash = ctypes.create_string_buffer(64)
self.status = None
def search(self, initialHash, target, seed, timeout):
startTime = utils.getTimePoint()
sha512 = libcrypto.SHA512
prefixes = self.prefixes
proofLength = self.proofLength
hashLength = self.hashLength
firstHash = self.firstHash
secondHash = self.secondHash
encodedTarget = struct.pack(">Q", target)
solutions = []
i = 0
while True:
randomness = hashlib.sha512(seed + struct.pack(">Q", i)).digest()
i += 1
suffix = randomness[: 7] + initialHash
for j in prefixes:
proof = j + suffix
sha512(j + suffix, proofLength, firstHash)
sha512(firstHash, hashLength, secondHash)
if secondHash[: 8] <= encodedTarget:
solutions.append(proof[: 8])
if len(solutions) != 0:
index, = struct.unpack(">Q", randomness[7: 15])
nonce = solutions[index % len(solutions)]
return nonce, 256 * i
if utils.getTimePoint() - startTime >= timeout:
return None, 256 * i
def setConfiguration(self, configuration):
pass

View File

@ -0,0 +1,88 @@
import ctypes
import os.path
import platform
import subprocess
import sys
class FastSolverError(Exception):
pass
def loadFastSolver(codePath):
if hasattr(sys, "winver"):
suffix = "-32"
if platform.architecture()[0] == "64bit":
suffix = "-64"
path = os.path.join(codePath, "fastsolver/libfastsolver{}.dll".format(suffix))
try:
return ctypes.WinDLL(path)
except:
raise FastSolverError()
makePath = os.path.join(codePath, "fastsolver")
path = os.path.join(codePath, "fastsolver/libfastsolver.so")
try:
return ctypes.CDLL(path)
except:
if hasattr(sys, "frozen"):
raise FastSolverError()
try:
subprocess.call(["make", "-C", makePath])
return ctypes.CDLL(path)
except:
raise FastSolverError()
# Only one instance allowed
class FastSolver(object):
def __init__(self, codePath):
self.libfastsolver = loadFastSolver(codePath)
self.libfastsolver.fastsolver_add.restype = ctypes.c_size_t
self.libfastsolver.fastsolver_add.argtypes = []
self.libfastsolver.fastsolver_remove.restype = ctypes.c_size_t
self.libfastsolver.fastsolver_remove.argtypes = [ctypes.c_size_t]
self.libfastsolver.fastsolver_search.restype = ctypes.c_int
self.libfastsolver.fastsolver_search.argtypes = [
ctypes.c_void_p, ctypes.c_void_p,
ctypes.c_void_p, ctypes.c_ulonglong, ctypes.c_void_p, ctypes.c_ulonglong
]
self.nonce = ctypes.create_string_buffer(8)
self.iterationsCount = ctypes.c_ulonglong()
self.parallelism = 0
self.status = 0
def search(self, initialHash, target, seed, timeout):
found = self.libfastsolver.fastsolver_search(
self.nonce, ctypes.byref(self.iterationsCount),
initialHash, target, seed, long(1000000000 * timeout)
)
if found == 1:
return self.nonce.raw, self.iterationsCount.value
else:
return None, self.iterationsCount.value
def setConfiguration(self, configuration):
if configuration is None:
parallelism = 0
else:
parallelism = min(4096, configuration)
for i in xrange(self.parallelism, parallelism):
self.parallelism = self.libfastsolver.fastsolver_add()
if parallelism < self.parallelism:
self.parallelism = self.libfastsolver.fastsolver_remove(self.parallelism - parallelism)
self.status = parallelism

View File

@ -0,0 +1,100 @@
#include <string.h>
#include <openssl/sha.h>
#include "common.h"
volatile int run;
const char *initial_hash;
unsigned long long target;
const char *seed;
static void encode_big_endian(char *result, unsigned long long number) {
result[0] = number >> 56;
result[1] = number >> 48 & 0xff;
result[2] = number >> 40 & 0xff;
result[3] = number >> 32 & 0xff;
result[4] = number >> 24 & 0xff;
result[5] = number >> 16 & 0xff;
result[6] = number >> 8 & 0xff;
result[7] = number & 0xff;
}
static unsigned long long decode_big_endian(const char *encoded) {
return (
(encoded[0] & 0xffull) << 56 |
(encoded[1] & 0xffull) << 48 |
(encoded[2] & 0xffull) << 40 |
(encoded[3] & 0xffull) << 32 |
(encoded[4] & 0xffull) << 24 |
(encoded[5] & 0xffull) << 16 |
(encoded[6] & 0xffull) << 8 |
(encoded[7] & 0xffull)
);
}
int work(char *nonce, unsigned long long *iterations_count, size_t thread_number) {
unsigned long long i;
char proof[8 + 64];
char appended_seed[SEED_LENGTH + 8 + 8];
memcpy(proof + 8, initial_hash, 64);
memcpy(appended_seed, seed, SEED_LENGTH);
encode_big_endian(appended_seed + SEED_LENGTH, thread_number);
for (i = 0; run; ++i) {
char randomness[64];
size_t solutions_count = 0;
char solutions[256];
size_t j;
encode_big_endian(appended_seed + SEED_LENGTH + 8, i);
SHA512((unsigned char *) appended_seed, SEED_LENGTH + 8 + 8, (unsigned char *) randomness);
memcpy(proof + 1, randomness, 7);
for (j = 0; j < 256; ++j) {
unsigned long long trial;
SHA512_CTX context;
char first_hash[64];
char second_hash[64];
proof[0] = j;
SHA512_Init(&context);
SHA512_Update(&context, (unsigned char *) proof, 8 + 64);
SHA512_Final((unsigned char *) first_hash, &context);
SHA512_Init(&context);
SHA512_Update(&context, (unsigned char *) first_hash, 64);
SHA512_Final((unsigned char *) second_hash, &context);
trial = decode_big_endian(second_hash);
if (trial <= target) {
solutions[solutions_count] = j;
++solutions_count;
}
++*iterations_count;
}
if (solutions_count != 0) {
unsigned long long index = decode_big_endian(randomness + 7);
nonce[0] = solutions[index % solutions_count];
memcpy(nonce + 1, proof + 1, 7);
return 1;
}
}
return 0;
}

View File

@ -0,0 +1,19 @@
#ifndef COMMON_H
#define COMMON_H
#ifdef _WIN32
#define EXPORT __declspec(dllexport)
#else
#define EXPORT __attribute__ ((visibility("default")))
#endif
extern volatile int run;
#define SEED_LENGTH (32 + 8)
extern const char *initial_hash;
extern unsigned long long target;
extern const char *seed;
int work(char *nonce, unsigned long long *iterations_count, size_t thread_number);
#endif

View File

@ -0,0 +1,12 @@
CFLAGS += -std=gnu99 -Wall -Wextra -pedantic -O3 -fPIC -fvisibility=hidden
LDFLAGS += -shared
LDLIBS = -lpthread -lcrypto
libfastsolver.so: common.o pthread.o
$(CC) $(LDFLAGS) -o $@ common.o pthread.o $(LDLIBS)
common.o: common.h common.c
pthread.o: common.h pthread.c
clean:
rm -f common.o pthread.o libfastsolver.so

View File

@ -0,0 +1 @@
/Ox /MD common.c winapi.c /link /DLL /OUT:libfastsolver.dll libeay32.lib

View File

@ -0,0 +1,214 @@
#include <string.h>
#include <time.h>
#include <pthread.h>
#include "common.h"
static int initialized;
#define MAXIMUM_THREADS_COUNT 4096
static size_t threads_count;
static pthread_t threads[MAXIMUM_THREADS_COUNT];
static pthread_mutex_t lock;
static pthread_cond_t start;
static pthread_cond_t done;
static size_t running_threads_count;
static int found;
static char best_nonce[8];
static unsigned long long total_iterations_count;
static void *thread_function(void *argument) {
size_t thread_number = (pthread_t *) argument - threads;
while (1) {
char nonce[8];
unsigned long long iterations_count = 0;
int result;
pthread_mutex_lock(&lock);
while (!run && threads_count > thread_number) {
pthread_cond_wait(&start, &lock);
}
if (threads_count <= thread_number) {
pthread_mutex_unlock(&lock);
return NULL;
}
++running_threads_count;
pthread_mutex_unlock(&lock);
result = work(nonce, &iterations_count, thread_number);
pthread_mutex_lock(&lock);
if (result == 1) {
found = 1;
memcpy(best_nonce, nonce, 8);
}
total_iterations_count += iterations_count;
run = 0;
--running_threads_count;
pthread_cond_signal(&done);
pthread_mutex_unlock(&lock);
}
}
static int initialize(void) {
pthread_condattr_t done_attributes;
if (initialized == 1) {
return 1;
}
if (pthread_mutex_init(&lock, NULL) != 0) {
goto error_lock;
}
if (pthread_cond_init(&start, NULL) != 0) {
goto error_start;
}
if (pthread_condattr_init(&done_attributes) != 0) {
goto error_done_attributes;
}
#ifndef __APPLE__
pthread_condattr_setclock(&done_attributes, CLOCK_MONOTONIC);
#endif
if (pthread_cond_init(&done, &done_attributes) != 0) {
goto error_done;
}
pthread_condattr_destroy(&done_attributes);
initialized = 1;
return 1;
error_done: pthread_condattr_destroy(&done_attributes);
error_done_attributes: pthread_cond_destroy(&start);
error_start: pthread_mutex_destroy(&lock);
error_lock: return 0;
}
EXPORT size_t fastsolver_add(void) {
#ifdef SCHED_IDLE
int policy = SCHED_IDLE;
#else
int policy = SCHED_OTHER;
#endif
struct sched_param parameters;
if (initialize() == 0) {
return threads_count;
}
pthread_mutex_lock(&lock);
if (pthread_create(&threads[threads_count], NULL, thread_function, &threads[threads_count]) != 0) {
pthread_mutex_unlock(&lock);
return threads_count;
}
parameters.sched_priority = sched_get_priority_min(policy);
pthread_setschedparam(threads[threads_count], policy, &parameters);
++threads_count;
pthread_mutex_unlock(&lock);
return threads_count;
}
EXPORT size_t fastsolver_remove(size_t count) {
size_t i;
pthread_mutex_lock(&lock);
threads_count -= count;
pthread_cond_broadcast(&start);
pthread_mutex_unlock(&lock);
for (i = 0; i < count; ++i) {
void *result;
pthread_join(threads[threads_count + i], &result);
}
return threads_count;
}
EXPORT int fastsolver_search(
char *local_nonce,
unsigned long long *local_iterations_count,
const char *local_initial_hash,
unsigned long long local_target,
const char *local_seed,
unsigned long long timeout
) {
struct timespec wait_time;
unsigned long long nanoseconds;
initial_hash = local_initial_hash;
target = local_target;
seed = local_seed;
found = 0;
total_iterations_count = 0;
#ifdef __APPLE__
wait_time.tv_sec = 0;
wait_time.tv_nsec = 0;
#else
clock_gettime(CLOCK_MONOTONIC, &wait_time);
#endif
nanoseconds = wait_time.tv_nsec + timeout;
wait_time.tv_sec += nanoseconds / 1000000000;
wait_time.tv_nsec = nanoseconds % 1000000000;
pthread_mutex_lock(&lock);
run = 1;
pthread_cond_broadcast(&start);
#ifdef __APPLE__
pthread_cond_timedwait_relative_np(&done, &lock, &wait_time);
#else
pthread_cond_timedwait(&done, &lock, &wait_time);
#endif
run = 0;
while (running_threads_count != 0) {
pthread_cond_wait(&done, &lock);
}
pthread_mutex_unlock(&lock);
if (found) {
memcpy(local_nonce, best_nonce, 8);
}
*local_iterations_count = total_iterations_count;
return found;
}

View File

@ -0,0 +1,160 @@
#include <string.h>
#include <Windows.h>
#include "common.h"
static int initialized;
#define MAXIMUM_THREADS_COUNT 4096
static size_t threads_count;
static HANDLE threads[MAXIMUM_THREADS_COUNT];
static CRITICAL_SECTION lock;
static CONDITION_VARIABLE start = CONDITION_VARIABLE_INIT;
static CONDITION_VARIABLE done = CONDITION_VARIABLE_INIT;
static size_t running_threads_count;
static int found;
static char best_nonce[8];
static unsigned long long total_iterations_count;
DWORD WINAPI thread_function(LPVOID argument) {
size_t thread_number = (HANDLE *) argument - threads;
while (1) {
char nonce[8];
unsigned long long iterations_count = 0;
int result;
EnterCriticalSection(&lock);
while (!run && threads_count > thread_number) {
SleepConditionVariableCS(&start, &lock, INFINITE);
}
if (threads_count <= thread_number) {
LeaveCriticalSection(&lock);
return 0;
}
++running_threads_count;
LeaveCriticalSection(&lock);
result = work(nonce, &iterations_count, thread_number);
EnterCriticalSection(&lock);
if (result == 1) {
found = 1;
memcpy(best_nonce, nonce, 8);
}
total_iterations_count += iterations_count;
run = 0;
--running_threads_count;
WakeConditionVariable(&done);
LeaveCriticalSection(&lock);
}
}
static int initialize(void) {
if (initialized == 1) {
return 1;
}
InitializeCriticalSection(&lock);
initialized = 1;
return 1;
}
EXPORT size_t fastsolver_add(void) {
if (initialize() == 0) {
return threads_count;
}
EnterCriticalSection(&lock);
threads[threads_count] = CreateThread(NULL, 0, thread_function, &threads[threads_count], 0, NULL);
if (threads[threads_count] == NULL) {
LeaveCriticalSection(&lock);
return threads_count;
}
SetThreadPriority(threads[threads_count], THREAD_PRIORITY_IDLE);
++threads_count;
LeaveCriticalSection(&lock);
return threads_count;
}
EXPORT size_t fastsolver_remove(size_t count) {
size_t i;
EnterCriticalSection(&lock);
threads_count -= count;
WakeAllConditionVariable(&start);
LeaveCriticalSection(&lock);
WaitForMultipleObjects(count, threads + threads_count, TRUE, INFINITE);
for (i = 0; i < count; ++i) {
CloseHandle(threads[threads_count + i]);
}
return threads_count;
}
EXPORT int fastsolver_search(
char *local_nonce,
unsigned long long *local_iterations_count,
const char *local_initial_hash,
unsigned long long local_target,
const char *local_seed,
unsigned long long timeout
) {
initial_hash = local_initial_hash;
target = local_target;
seed = local_seed;
found = 0;
total_iterations_count = 0;
EnterCriticalSection(&lock);
run = 1;
WakeAllConditionVariable(&start);
SleepConditionVariableCS(&done, &lock, timeout / 1000);
run = 0;
while (running_threads_count != 0) {
SleepConditionVariableCS(&done, &lock, INFINITE);
}
LeaveCriticalSection(&lock);
if (found) {
memcpy(local_nonce, best_nonce, 8);
}
*local_iterations_count = total_iterations_count;
return found;
}

View File

@ -0,0 +1,117 @@
import multiprocessing
import os
import struct
import dumbsolver
def setIdle():
if hasattr(os, "nice"):
os.nice(40)
return
try:
import psutil
psutil.Process().nice(psutil.IDLE_PRIORITY_CLASS)
return
except:
pass
try:
import win32api
import win32con
import win32process
PID = win32api.GetCurrentProcessId()
handle = win32api.OpenProcess(win32con.PROCESS_ALL_ACCESS, True, PID)
win32process.SetPriorityClass(handle, win32process.IDLE_PRIORITY_CLASS)
except:
pass
def threadFunction(local, remote, codePath, threadNumber):
remote.close()
setIdle()
solver = dumbsolver.DumbSolver(codePath)
while True:
try:
received = local.recv()
command = received[0]
arguments = received[1: ]
if command == "search":
initialHash, target, seed, timeout = arguments
appendedSeed = seed + struct.pack(">Q", threadNumber)
nonce, iterationsCount = solver.search(initialHash, target, appendedSeed, timeout)
local.send(("done", nonce, iterationsCount))
elif command == "shutdown":
local.close()
return
except (EOFError, IOError):
return
class ForkingSolver(object):
def __init__(self, codePath):
self.pipes = []
self.processes = []
self.status = 0
self.codePath = codePath
def search(self, initialHash, target, seed, timeout):
for i in self.pipes:
i.send(("search", initialHash, target, seed, timeout))
bestNonce, totalIterationsCount = None, 0
for i in self.pipes:
event, nonce, iterationsCount = i.recv()
if nonce is not None:
bestNonce = nonce
totalIterationsCount += iterationsCount
return bestNonce, totalIterationsCount
def setConfiguration(self, configuration):
if configuration is None:
parallelism = 0
else:
parallelism = min(4096, configuration)
for i in xrange(len(self.processes), parallelism):
local, remote = multiprocessing.Pipe()
process = multiprocessing.Process(
target = threadFunction,
args = (remote, local, self.codePath, i),
name = "ForkingSolver"
)
process.start()
remote.close()
self.pipes.append(local)
self.processes.append(process)
for i in xrange(parallelism, len(self.processes)):
pipe = self.pipes.pop()
pipe.send(("shutdown", ))
pipe.close()
for i in xrange(parallelism, len(self.processes)):
self.processes.pop().join()
self.status = parallelism

136
src/workprover/gpusolver.cl Normal file
View File

@ -0,0 +1,136 @@
constant ulong k[80] = {
0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc,
0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118,
0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2,
0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, 0xc19bf174cf692694,
0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65,
0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5,
0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4,
0xc6e00bf33da88fc2, 0xd5a79147930aa725, 0x06ca6351e003826f, 0x142929670a0e6e70,
0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df,
0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b,
0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30,
0xd192e819d6ef5218, 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8,
0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8,
0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3,
0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec,
0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b,
0xca273eceea26619c, 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178,
0x06f067aa72176fba, 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b,
0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c,
0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817
};
constant ulong h[8] = {
0x6a09e667f3bcc908, 0xbb67ae8584caa73b, 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1,
0x510e527fade682d1, 0x9b05688c2b3e6c1f, 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179
};
#define ROTATE(x, n) ((x) >> (n) | (x) << 64 - (n))
#define C(x, y, z) ((x) & (y) ^ ~(x) & (z))
#define M(x, y, z) ((x) & (y) ^ (x) & (z) ^ (y) & (z))
#define S0(x) (ROTATE((x), 28) ^ ROTATE((x), 34) ^ ROTATE((x), 39))
#define S1(x) (ROTATE((x), 14) ^ ROTATE((x), 18) ^ ROTATE((x), 41))
#define s0(x) (ROTATE((x), 1) ^ ROTATE((x), 8) ^ (x) >> 7)
#define s1(x) (ROTATE((x), 19) ^ ROTATE((x), 61) ^ (x) >> 6)
void sha512_process_block(ulong *state, ulong *block) {
ulong a = state[0];
ulong b = state[1];
ulong c = state[2];
ulong d = state[3];
ulong e = state[4];
ulong f = state[5];
ulong g = state[6];
ulong h = state[7];
ulong *w = block;
#pragma unroll
for (size_t i = 0; i < 16; i++) {
ulong t = k[i] + w[i & 15] + h + S1(e) + C(e, f, g);
h = g;
g = f;
f = e;
e = d + t;
t += M(a, b, c) + S0(a);
d = c;
c = b;
b = a;
a = t;
}
#pragma unroll 16
for (size_t i = 16; i < 80; i++) {
w[i & 15] += s0(w[i + 1 & 15]) + s1(w[i + 14 & 15]) + w[i + 9 & 15];
ulong t = k[i] + w[i & 15] + h + S1(e) + C(e, f, g);
h = g;
g = f;
f = e;
e = d + t;
t += M(a, b, c) + S0(a);
d = c;
c = b;
b = a;
a = t;
}
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
state[4] += e;
state[5] += f;
state[6] += g;
state[7] += h;
}
ulong compute_trial(ulong nonce, global const ulong *initial_hash) {
ulong fisrt_block[16] = {
nonce,
initial_hash[0], initial_hash[1], initial_hash[2], initial_hash[3],
initial_hash[4], initial_hash[5], initial_hash[6], initial_hash[7],
0x8000000000000000, 0, 0, 0, 0, 0, 8 * (8 + 64)
};
ulong second_block[16] = {
h[0], h[1], h[2], h[3],
h[4], h[5], h[6], h[7],
0x8000000000000000, 0, 0, 0, 0, 0, 0, 8 * 64
};
ulong double_hash[8] = {
h[0], h[1], h[2], h[3],
h[4], h[5], h[6], h[7]
};
sha512_process_block(second_block, fisrt_block);
sha512_process_block(double_hash, second_block);
return double_hash[0];
}
kernel void search(global unsigned int *output, global ulong *input) {
size_t thread_number = get_global_id(0);
global unsigned int *solutions_count = output;
global unsigned int *solutions = output + 1;
global ulong *nonce = input;
global ulong *initial_hash = input + 1;
global ulong *target = input + 9;
ulong trial = compute_trial(*nonce + thread_number, initial_hash);
if (trial <= *target) {
unsigned int index = atom_inc(solutions_count);
solutions[index] = thread_number;
}
}

116
src/workprover/gpusolver.py Normal file
View File

@ -0,0 +1,116 @@
import hashlib
import os.path
import struct
import utils
pyopencl = None
numpy = None
class GPUSolverError(Exception):
pass
class GPUSolver(object):
def __init__(self, codePath):
global pyopencl
try:
import pyopencl
except ImportError:
raise GPUSolverError()
self.vendors = {}
for i in pyopencl.get_platforms():
devices = i.get_devices(device_type = pyopencl.device_type.GPU)
if len(devices) != 0:
self.vendors[i.vendor] = devices[0]
if len(self.vendors) == 0:
raise GPUSolverError()
with open(os.path.join(codePath, "gpusolver.cl")) as file:
self.source = file.read()
self.status = None
def search(self, initialHash, target, seed, timeout):
startTime = utils.getTimePoint()
self.hostOutput[0] = 0
for i in xrange(8):
self.hostInput[1 + i], = struct.unpack(">Q", initialHash[8 * i: 8 * (i + 1)])
self.hostInput[9] = target
pyopencl.enqueue_copy(self.queue, self.output, self.hostOutput[: 1])
i = 0
while True:
randomness = hashlib.sha512(seed + struct.pack(">Q", i)).digest()
i += 1
self.hostInput[0], = struct.unpack(">Q", randomness[: 8])
pyopencl.enqueue_copy(self.queue, self.input, self.hostInput)
pyopencl.enqueue_nd_range_kernel(self.queue, self.kernel, (self.batchSize, ), None)
self.queue.finish()
pyopencl.enqueue_copy(self.queue, self.hostOutput[: 1], self.output)
solutionsCount = long(self.hostOutput[0])
if solutionsCount != 0:
pyopencl.enqueue_copy(self.queue, self.hostOutput[0: 1 + solutionsCount], self.output)
index, = struct.unpack(">Q", randomness[8: 16])
threadNumber = self.hostOutput[1 + index % solutionsCount]
nonce = struct.pack(">Q", long(self.hostInput[0]) + threadNumber)
if not utils.checkProof(nonce, initialHash, target):
raise GPUSolverError()
return nonce, self.batchSize * i
if utils.getTimePoint() - startTime >= timeout:
return None, self.batchSize * i
def setConfiguration(self, configuration):
global numpy
if numpy is not None:
return
import numpy
if configuration is None:
configuration = self.vendors.keys()[0]
if configuration not in self.vendors:
raise GPUSolverError()
device = self.vendors[configuration]
context = pyopencl.Context(devices = [device])
computeUnitsCount = device.get_info(pyopencl.device_info.MAX_COMPUTE_UNITS)
workGroupSize = device.get_info(pyopencl.device_info.MAX_WORK_GROUP_SIZE)
self.batchSize = workGroupSize * computeUnitsCount * 256
self.queue = pyopencl.CommandQueue(context, device)
program = pyopencl.Program(context, self.source).build()
self.hostOutput = numpy.zeros(1 + self.batchSize, numpy.uint32)
self.hostInput = numpy.zeros(1 + 8 + 1, numpy.uint64)
self.output = pyopencl.Buffer(context, pyopencl.mem_flags.READ_WRITE, 4 * (1 + self.batchSize))
self.input = pyopencl.Buffer(context, pyopencl.mem_flags.READ_ONLY, 8 * (1 + 8 + 1))
self.kernel = program.search
self.kernel.set_args(self.output, self.input)
self.status = self.batchSize

220
src/workprover/test.py Executable file
View File

@ -0,0 +1,220 @@
#!/usr/bin/env python2.7
import binascii
import ctypes
import ctypes.util
import os.path
import struct
import sys
import unittest
import __init__
import dumbsolver
import fastsolver
import forkingsolver
import gpusolver
import utils
codePath = os.path.dirname(__file__)
if hasattr(sys, "winver"):
dumbsolver.libcrypto = ctypes.WinDLL("libeay32.dll")
else:
dumbsolver.libcrypto = ctypes.CDLL(ctypes.util.find_library("crypto"))
nonce = binascii.unhexlify("9ca6790a249679f8")
expiryTime = 1525845600
headlessPayload = binascii.unhexlify("000000000001")
initialPayload = struct.pack(">Q", expiryTime) + headlessPayload
payload = nonce + initialPayload
initialHash = binascii.unhexlify(
"1e87a288a10454dea0d3a9b606cc538db1b8b47fe8a21a37b8e57da3db6928eb"
"d854fd22aed3e1849c4a1c596fe0bfec266c05900a862c5b356a6b7e51a4b510"
)
doubleHash = binascii.unhexlify(
"16cdf04b739412bea1bf58d6c5a53ec92e7d4aab180213405bf10d615354d417"
"00f8b1510d0844a4b7c7b7434e6c115b52fcec5c591e96c31f4b8769ee683552"
)
TTL = 3600
byteDifficulty = 1000
lengthExtension = 1000
target = 0x00000f903320b7f6
difficulty = 1078000
seed = binascii.unhexlify("3941c24a1256660a8f65d962954c406dab7bc449317fa087c4a3f1a3ca7d95fd")
timeout = .5
class TestUtils(unittest.TestCase):
def testCalculateInitialHash(self):
self.assertEqual(utils.calculateInitialHash(initialPayload), initialHash)
def testCalculateDoubleHash(self):
self.assertEqual(utils.calculateDoubleHash(payload), doubleHash)
def testCalculateTarget(self):
self.assertEqual(utils.calculateTarget(1000, 1015, 1000, 1000), (0x00000843bf57fed2, 2030000))
self.assertEqual(utils.calculateTarget(1000, 1016, 1000, 1000), (0x00000842b4a960c2, 2031000))
def testCheckProof(self):
self.assertFalse(utils.checkProof(nonce, initialHash, 0x000002fe91eba355))
self.assertTrue(utils.checkProof(nonce, initialHash, 0x000002fe91eba356))
def testCheckWorkSufficient(self):
self.assertFalse(utils.checkWorkSufficient(payload, byteDifficulty, lengthExtension, expiryTime - 293757.5))
self.assertTrue(utils.checkWorkSufficient(payload, byteDifficulty, lengthExtension, expiryTime - 293757))
originalTime = utils.time.time
utils.time.time = lambda: expiryTime - 293757.5
self.assertFalse(utils.checkWorkSufficient(payload, byteDifficulty, lengthExtension))
utils.time.time = lambda: expiryTime - 293757
self.assertTrue(utils.checkWorkSufficient(payload, byteDifficulty, lengthExtension))
utils.time.time = originalTime
def testEstimateMaximumIterationsCount(self):
self.assertEqual(utils.estimateMaximumIterationsCount(4096, .1), 512)
self.assertEqual(utils.estimateMaximumIterationsCount(difficulty, .8), 1735168)
class TestSolver(unittest.TestCase):
def setUp(self):
try:
self.solver = self.Solver(codePath)
except gpusolver.GPUSolverError:
self.skipTest("OpenCL unavailable")
self.solver.setConfiguration(self.configuration)
def testSearch(self):
nonce = None
i = 0
while nonce is None:
appendedSeed = seed + struct.pack(">Q", i)
i += 1
nonce, iterationsCount = self.solver.search(initialHash, target, appendedSeed, timeout)
self.assertTrue(utils.checkProof(nonce, initialHash, target))
def tearDown(self):
self.solver.setConfiguration(None)
class TestDumbSolver(TestSolver):
Solver = dumbsolver.DumbSolver
configuration = None
class TestForkingSolver(TestSolver):
Solver = forkingsolver.ForkingSolver
configuration = 3
class TestFastSolver(TestSolver):
Solver = fastsolver.FastSolver
configuration = 3
class TestGPUSolver(TestSolver):
Solver = gpusolver.GPUSolver
configuration = None
class TestWorkProver(unittest.TestCase):
def setUp(self):
self.thread = __init__.WorkProver(codePath, seed, None, None)
self.thread.start()
def checkTaskLinks(self):
IDs = set(self.thread.tasks.keys())
if len(IDs) == 0:
return
self.assertIn(self.thread.currentTaskID, IDs)
linkID = next(iter(IDs))
for i in xrange(len(IDs)):
self.assertGreaterEqual(self.thread.totalDifficulty, 0)
self.assertIn(linkID, IDs)
IDs.remove(linkID)
nextLinkID = self.thread.tasks[linkID].next
self.assertEqual(self.thread.tasks[nextLinkID].previous, linkID)
linkID = nextLinkID
def testTasks(self):
self.thread.addTask(0, headlessPayload, TTL, None, byteDifficulty, lengthExtension)
self.checkTaskLinks()
self.thread.addTask(1, headlessPayload, TTL, None, byteDifficulty, lengthExtension)
self.thread.addTask(2, headlessPayload, TTL, None, byteDifficulty, lengthExtension)
self.checkTaskLinks()
self.thread.cancelTask(self.thread.currentTaskID)
self.thread.nextTask()
self.thread.nextTask()
self.thread.nextTask()
self.thread.addTask(3, headlessPayload, TTL, None, byteDifficulty, lengthExtension)
self.checkTaskLinks()
def testSearch(self):
self.thread.commandsQueue.put((
"addTask", 0,
headlessPayload, TTL, None, byteDifficulty, lengthExtension
))
self.thread.commandsQueue.put((
"addTask", 1,
headlessPayload, TTL, None, byteDifficulty, lengthExtension
))
self.thread.commandsQueue.put((
"addTask", 2,
headlessPayload, TTL * 100, expiryTime, byteDifficulty, lengthExtension
))
self.thread.commandsQueue.put(("setSolver", "dumb", 1))
for i in xrange(3):
event, ID, nonce, localExpiryTime = self.thread.resultsQueue.get()
initialPayload = struct.pack(">Q", localExpiryTime) + headlessPayload
initialHash = utils.calculateInitialHash(initialPayload)
self.assertTrue(utils.checkProof(nonce, initialHash, target))
def tearDown(self):
self.thread.commandsQueue.put(("shutdown", ))
self.thread.join()
def load_tests(loader, tests, pattern):
return unittest.TestSuite([
loader.loadTestsFromTestCase(TestUtils),
loader.loadTestsFromTestCase(TestDumbSolver),
loader.loadTestsFromTestCase(TestForkingSolver),
loader.loadTestsFromTestCase(TestFastSolver),
loader.loadTestsFromTestCase(TestGPUSolver),
loader.loadTestsFromTestCase(TestWorkProver)
])
if __name__ == "__main__":
import multiprocessing
multiprocessing.freeze_support()
loader = unittest.TestLoader()
runner = unittest.TextTestRunner()
runner.run(load_tests(loader, [], None))

53
src/workprover/utils.py Normal file
View File

@ -0,0 +1,53 @@
import hashlib
import math
import os
import struct
import sys
import time
def calculateInitialHash(initialPayload):
return hashlib.sha512(initialPayload).digest()
def calculateDoubleHash(data):
return hashlib.sha512(hashlib.sha512(data).digest()).digest()
# Length including nonce
def calculateTarget(length, TTL, byteDifficulty, lengthExtension):
adjustedLength = length + lengthExtension
timeEquivalent = TTL * adjustedLength / 2 ** 16
difficulty = byteDifficulty * (adjustedLength + timeEquivalent)
return 2 ** 64 / difficulty, difficulty
def checkProof(nonce, initialHash, target):
proof = nonce + initialHash
trial, = struct.unpack(">Q", calculateDoubleHash(proof)[: 8])
return trial <= target
def checkWorkSufficient(payload, byteDifficulty, lengthExtension, receivedTime = None):
if receivedTime is None:
receivedTime = int(time.time())
expiryTime, = struct.unpack(">Q", payload[8: 16])
minimumTTL = max(300, expiryTime - receivedTime)
nonce = payload[: 8]
initialHash = calculateInitialHash(payload[8: ])
target, difficulty = calculateTarget(len(payload), minimumTTL, byteDifficulty, lengthExtension)
return checkProof(nonce, initialHash, target)
def estimateMaximumIterationsCount(difficulty, probability):
coefficient = -math.log(1 - probability)
return int(coefficient * difficulty + 255) / 256 * 256
if hasattr(sys, "winver"):
getTimePoint = time.clock
else:
def getTimePoint():
return os.times()[4]