Compare commits
38 Commits
v0.6
...
Kleshni/PO
Author | SHA1 | Date | |
---|---|---|---|
|
7ad1725235 | ||
|
192b083d58 | ||
|
0aaad8b896 | ||
|
8e1259d06c | ||
|
a398354700 | ||
|
c22c5c2208 | ||
|
02ece2d491 | ||
|
00c4ee881f | ||
|
373157db45 | ||
|
2c7d677ccf | ||
|
a27b5e9055 | ||
|
f6415d6be1 | ||
|
174fb38c7d | ||
|
ce4fe3a66b | ||
|
c66156aeea | ||
|
b2441c7830 | ||
|
d1a5c604e6 | ||
|
e94bdf4642 | ||
|
b24edfdc22 | ||
|
8e0bff7d52 | ||
|
3c0e23574d | ||
|
54291918bf | ||
|
0c3ce79ea4 | ||
|
6fb637d1df | ||
|
c28a4f6174 | ||
|
90ae95d9cb | ||
|
799933086a | ||
|
4c0b0c708f | ||
|
cac0237ae0 | ||
|
4f9274a5cc | ||
|
721ad8c61a | ||
|
30e7df6094 | ||
|
43f7e1e7a1 | ||
|
2eeb58a43e | ||
|
917b55d463 | ||
|
46da7e81bb | ||
|
079326bc03 | ||
|
a181da3632 |
|
@ -11,3 +11,4 @@ install:
|
|||
script:
|
||||
- python checkdeps.py
|
||||
- pybitmessage -t
|
||||
- python setup.py test
|
||||
|
|
20
checkdeps.py
20
checkdeps.py
|
@ -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()
|
||||
|
|
|
@ -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')
|
||||
]
|
||||
|
|
17
setup.py
17
setup.py
|
@ -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': [
|
||||
|
|
305
src/api.py
305
src/api.py
|
@ -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()
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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):
|
||||
tableWidget = self.getCurrentMessagelist()
|
||||
|
||||
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.addAction(self.actionTrashSentMessage)
|
||||
self.popMenuSent.addSeparator()
|
||||
self.popMenuSent.addAction(self.actionEditAndResend)
|
||||
|
||||
# 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 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:
|
||||
|
|
|
@ -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_())
|
||||
|
|
@ -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,16 +102,26 @@
|
|||
</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>
|
||||
<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">
|
||||
|
@ -88,38 +134,44 @@
|
|||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButtonNewAddress">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>New Indentitiy</string>
|
||||
<string>New Identity</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>
|
||||
</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>
|
||||
<item>
|
||||
<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>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="inboxSearchOption">
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QComboBox::AdjustToContents</enum>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>All</string>
|
||||
|
@ -146,11 +198,14 @@
|
|||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTableWidget" name="tableWidgetInbox">
|
||||
</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>
|
||||
|
@ -214,9 +269,13 @@
|
|||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTextEdit" name="textEditInboxMessage">
|
||||
<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>
|
||||
|
@ -227,16 +286,14 @@
|
|||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</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,16 +301,26 @@
|
|||
</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>
|
||||
<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>
|
||||
|
@ -298,63 +365,55 @@
|
|||
</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>
|
||||
<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="tab">
|
||||
<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">
|
||||
<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>
|
||||
<widget class="SSplitter" name="verticalSplitter_5">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<property name="childrenCollapsible">
|
||||
<bool>false</bool>
|
||||
</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">
|
||||
|
@ -362,20 +421,6 @@
|
|||
</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">
|
||||
|
@ -386,34 +431,58 @@
|
|||
</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>
|
||||
</layout>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Subject:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTextEdit" name="textEditMessage">
|
||||
<property name="html">
|
||||
<string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||
p, li { white-space: pre-wrap; }
|
||||
</style></head><body style=" font-family:'Droid Sans'; font-size:9pt; font-weight:400; font-style:normal;">
|
||||
<p style="-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';"><br /></p></body></html></string>
|
||||
<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>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_2">
|
||||
<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">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
<item>
|
||||
<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">
|
||||
|
@ -447,40 +516,30 @@ p, li { white-space: pre-wrap; }
|
|||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTextEdit" name="textEditMessageBroadcast">
|
||||
<property name="html">
|
||||
<string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||
p, li { white-space: pre-wrap; }
|
||||
</style></head><body style=" font-family:'Droid Sans'; font-size:9pt; font-weight:400; font-style:normal;">
|
||||
<p style="-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';"><br /></p></body></html></string>
|
||||
</widget>
|
||||
<widget class="MessageCompose" name="textEditMessageBroadcast">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>1</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButtonTTL">
|
||||
<widget class="QWidget" name="tTLContainer" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>32</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButtonTTL">
|
||||
<property name="palette">
|
||||
<palette>
|
||||
<active>
|
||||
|
@ -535,13 +594,13 @@ p, li { white-space: pre-wrap; }
|
|||
<widget class="QSlider" name="horizontalSliderTTL">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>35</width>
|
||||
<width>70</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>70</width>
|
||||
<width>105</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
|
@ -559,8 +618,8 @@ p, li { white-space: pre-wrap; }
|
|||
<item>
|
||||
<widget class="QLabel" name="labelHumanFriendlyTTLDescription">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
|
@ -570,42 +629,35 @@ p, li { white-space: pre-wrap; }
|
|||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>45</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>X days</string>
|
||||
<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="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Send</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</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,16 +665,26 @@ 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>
|
||||
<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>
|
||||
|
@ -644,35 +706,44 @@ p, li { white-space: pre-wrap; }
|
|||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButtonAddSubscription">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Add new Subscription</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
</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>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="inboxSearchOptionSubscriptions">
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QComboBox::AdjustToContents</enum>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>All</string>
|
||||
|
@ -699,11 +770,14 @@ p, li { white-space: pre-wrap; }
|
|||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTableWidget" name="tableWidgetInboxSubscriptions">
|
||||
</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>
|
||||
|
@ -767,9 +841,13 @@ p, li { white-space: pre-wrap; }
|
|||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTextEdit" name="textEditInboxMessageSubscriptions">
|
||||
<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>
|
||||
|
@ -780,16 +858,14 @@ p, li { white-space: pre-wrap; }
|
|||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</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,16 +873,26 @@ 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>
|
||||
<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>
|
||||
|
@ -834,35 +920,44 @@ p, li { white-space: pre-wrap; }
|
|||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButtonAddChan">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Add Chan</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_8">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||
<item>
|
||||
</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>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="inboxSearchOptionChans">
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QComboBox::AdjustToContents</enum>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>All</string>
|
||||
|
@ -889,11 +984,14 @@ p, li { white-space: pre-wrap; }
|
|||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTableWidget" name="tableWidgetInboxChans">
|
||||
</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>
|
||||
|
@ -957,9 +1055,13 @@ p, li { white-space: pre-wrap; }
|
|||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTextEdit" name="textEditInboxMessageChans">
|
||||
<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>
|
||||
|
@ -970,321 +1072,28 @@ p, li { white-space: pre-wrap; }
|
|||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</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>
|
||||
|
|
|
@ -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"))
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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)))
|
||||
|
|
|
@ -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))
|
|
@ -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>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true" comment="en">English</string>
|
||||
</property>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="checkBoxHideTrayConnectionNotifications">
|
||||
<property name="text">
|
||||
<string notr="true" comment="eo">Esperanto</string>
|
||||
<string>Hide connection notifications</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<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 notr="true" comment="fr">Français</string>
|
||||
<string>Start Bitmessage in the tray (don't show main window)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="checkBoxMinimizeToTray">
|
||||
<property name="text">
|
||||
<string notr="true" comment="de">Deutsch</string>
|
||||
<string>Minimize to tray</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="checkBoxTrayOnClose">
|
||||
<property name="text">
|
||||
<string notr="true" comment="es">Español</string>
|
||||
<string>Close to tray</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true" comment="ru">русский</string>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</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>
|
||||
|
@ -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 row="0" column="3" alignment="Qt::AlignLeft">
|
||||
<widget class="QCheckBox" name="checkBoxUPnP"/>
|
||||
</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>
|
||||
<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,21 +313,99 @@
|
|||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<spacer name="verticalSpacer">
|
||||
<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="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::Vertical</enum>
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tabDemandedDifficulty">
|
||||
<attribute name="title">
|
||||
<string>Demanded difficulty</string>
|
||||
|
@ -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>
|
||||
<item row="3" column="0" colspan="3">
|
||||
<widget class="QGroupBox" name="groupBoxSolvers">
|
||||
<property name="title">
|
||||
<string>Proof of work solver</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
<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="2">
|
||||
<widget class="QLineEdit" name="lineEditMaxAcceptableSmallMessageDifficulty">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
<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>
|
||||
<property name="maximumSize">
|
||||
</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>70</width>
|
||||
<height>16777215</height>
|
||||
<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 row="3" column="1">
|
||||
<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="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>
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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')),
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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:],
|
||||
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()))
|
||||
))
|
||||
else:
|
||||
logger.debug('This object is not an acknowledgement bound for me.')
|
||||
).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.
|
||||
|
|
|
@ -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...'
|
||||
))
|
||||
|
|
|
@ -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)
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
112
src/openclpow.py
112
src/openclpow.py
|
@ -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)
|
||||
|
|
@ -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()
|
525
src/protocol.py
525
src/protocol.py
|
@ -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]
|
||||
|
||||
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)
|
||||
embeddedStream, readLength = decodeVarint(payload[readPosition: readPosition + 9])
|
||||
readPosition += readLength
|
||||
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'
|
||||
return None
|
||||
|
||||
if embeddedVersion != 4:
|
||||
logger.info("Pubkey decryption failed due to address version mismatch")
|
||||
|
||||
return None
|
||||
|
||||
if embeddedStream != stream:
|
||||
logger.info("Pubkey decryption failed due to stream number mismatch")
|
||||
|
||||
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
|
||||
bitfieldBehaviors = decryptedData[readPosition:readPosition + 4]
|
||||
|
||||
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 len(publicSigningKey) != 65 or len(publicEncryptionKey) != 65:
|
||||
return None
|
||||
|
||||
embeddedRipe = calculateRipeHash(publicSigningKey + publicEncryptionKey)
|
||||
|
||||
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'
|
||||
logger.info("Pubkey decryption failed due to RIPE mismatch")
|
||||
|
||||
# Everything checked out. Insert it into the pubkeys table.
|
||||
return None
|
||||
|
||||
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),
|
||||
])
|
||||
)
|
||||
|
||||
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 (%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):
|
||||
|
|
|
@ -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()
|
||||
|
|
389
src/shared.py
389
src/shared.py
|
@ -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
1099
src/singleworker.py
Normal 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))
|
|
@ -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
42
src/workprover/Readme.md
Normal 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
262
src/workprover/__init__.py
Normal 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)
|
70
src/workprover/dumbsolver.py
Normal file
70
src/workprover/dumbsolver.py
Normal 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
|
88
src/workprover/fastsolver.py
Normal file
88
src/workprover/fastsolver.py
Normal 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
|
100
src/workprover/fastsolver/common.c
Normal file
100
src/workprover/fastsolver/common.c
Normal 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;
|
||||
}
|
19
src/workprover/fastsolver/common.h
Normal file
19
src/workprover/fastsolver/common.h
Normal 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
|
12
src/workprover/fastsolver/makefile
Normal file
12
src/workprover/fastsolver/makefile
Normal 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
|
1
src/workprover/fastsolver/options.txt
Normal file
1
src/workprover/fastsolver/options.txt
Normal file
|
@ -0,0 +1 @@
|
|||
/Ox /MD common.c winapi.c /link /DLL /OUT:libfastsolver.dll libeay32.lib
|
214
src/workprover/fastsolver/pthread.c
Normal file
214
src/workprover/fastsolver/pthread.c
Normal 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, ¶meters);
|
||||
|
||||
++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;
|
||||
}
|
160
src/workprover/fastsolver/winapi.c
Normal file
160
src/workprover/fastsolver/winapi.c
Normal 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;
|
||||
}
|
117
src/workprover/forkingsolver.py
Normal file
117
src/workprover/forkingsolver.py
Normal 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
136
src/workprover/gpusolver.cl
Normal 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
116
src/workprover/gpusolver.py
Normal 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
220
src/workprover/test.py
Executable 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
53
src/workprover/utils.py
Normal 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]
|
Loading…
Reference in New Issue
Block a user