Merge pull request #11 from Bitmessage/v0.6

V0.6
This commit is contained in:
sigoa 2018-01-22 09:54:02 +01:00 committed by GitHub
commit 98cdf287c4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 106 additions and 40 deletions

View File

@ -83,7 +83,7 @@ if __name__ == "__main__":
'qrcode': ['qrcode'], 'qrcode': ['qrcode'],
'pyopencl': ['pyopencl'], 'pyopencl': ['pyopencl'],
'notify2': ['notify2'], 'notify2': ['notify2'],
'sound:platform_system=="Windows"': ['winsound'] 'sound;platform_system=="Windows"': ['winsound']
}, },
classifiers=[ classifiers=[
"License :: OSI Approved :: MIT License" "License :: OSI Approved :: MIT License"

View File

@ -342,13 +342,21 @@ class Main:
sleep(1) sleep(1)
def daemonize(self): def daemonize(self):
grandfatherPid = os.getpid()
parentPid = None
try: try:
if os.fork(): if os.fork():
# unlock
shared.thisapp.cleanup()
# wait until grandchild ready
while True:
sleep(1)
os._exit(0) os._exit(0)
except AttributeError: except AttributeError:
# fork not implemented # fork not implemented
pass pass
else: else:
parentPid = os.getpid()
shared.thisapp.lock() # relock shared.thisapp.lock() # relock
os.umask(0) os.umask(0)
try: try:
@ -358,6 +366,11 @@ class Main:
pass pass
try: try:
if os.fork(): if os.fork():
# unlock
shared.thisapp.cleanup()
# wait until child ready
while True:
sleep(1)
os._exit(0) os._exit(0)
except AttributeError: except AttributeError:
# fork not implemented # fork not implemented
@ -374,6 +387,10 @@ class Main:
os.dup2(si.fileno(), sys.stdin.fileno()) os.dup2(si.fileno(), sys.stdin.fileno())
os.dup2(so.fileno(), sys.stdout.fileno()) os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno()) os.dup2(se.fileno(), sys.stderr.fileno())
if parentPid:
# signal ready
os.kill(parentPid, signal.SIGTERM)
os.kill(grandfatherPid, signal.SIGTERM)
def setSignalHandler(self): def setSignalHandler(self):
signal.signal(signal.SIGINT, helper_generic.signal_handler) signal.signal(signal.SIGINT, helper_generic.signal_handler)

View File

@ -69,7 +69,7 @@ import queues
import shutdown import shutdown
import state import state
from statusbar import BMStatusBar from statusbar import BMStatusBar
import throttle from network.asyncore_pollchoose import set_rates
from version import softwareVersion from version import softwareVersion
import sound import sound
@ -2287,16 +2287,16 @@ class MyForm(settingsmixin.SMainWindow):
int(float(self.settingsDialogInstance.ui.lineEditMaxDownloadRate.text())))) int(float(self.settingsDialogInstance.ui.lineEditMaxDownloadRate.text()))))
BMConfigParser().set('bitmessagesettings', 'maxuploadrate', str( BMConfigParser().set('bitmessagesettings', 'maxuploadrate', str(
int(float(self.settingsDialogInstance.ui.lineEditMaxUploadRate.text())))) int(float(self.settingsDialogInstance.ui.lineEditMaxUploadRate.text()))))
except: except ValueError:
QMessageBox.about(self, _translate("MainWindow", "Number needed"), _translate( QMessageBox.about(self, _translate("MainWindow", "Number needed"), _translate(
"MainWindow", "Your maximum download and upload rate must be numbers. Ignoring what you typed.")) "MainWindow", "Your maximum download and upload rate must be numbers. Ignoring what you typed."))
else:
set_rates(BMConfigParser().safeGetInt("bitmessagesettings", "maxdownloadrate"),
BMConfigParser().safeGetInt("bitmessagesettings", "maxuploadrate"))
BMConfigParser().set('bitmessagesettings', 'maxoutboundconnections', str( BMConfigParser().set('bitmessagesettings', 'maxoutboundconnections', str(
int(float(self.settingsDialogInstance.ui.lineEditMaxOutboundConnections.text())))) int(float(self.settingsDialogInstance.ui.lineEditMaxOutboundConnections.text()))))
throttle.SendThrottle().resetLimit()
throttle.ReceiveThrottle().resetLimit()
BMConfigParser().set('bitmessagesettings', 'namecoinrpctype', BMConfigParser().set('bitmessagesettings', 'namecoinrpctype',
self.settingsDialogInstance.getNamecoinType()) self.settingsDialogInstance.getNamecoinType())
BMConfigParser().set('bitmessagesettings', 'namecoinrpchost', str( BMConfigParser().set('bitmessagesettings', 'namecoinrpchost', str(

View File

@ -51,7 +51,7 @@ def signal_handler(signal, frame):
raise SystemExit raise SystemExit
if "PoolWorker" in current_process().name: if "PoolWorker" in current_process().name:
raise SystemExit raise SystemExit
if current_thread().name != "PyBitmessage": if current_thread().name not in ("PyBitmessage", "MainThread"):
return return
logger.error("Got signal %i", signal) logger.error("Got signal %i", signal)
if BMConfigParser().safeGetBoolean('bitmessagesettings', 'daemon'): if BMConfigParser().safeGetBoolean('bitmessagesettings', 'daemon'):

View File

@ -75,7 +75,7 @@ class AdvancedDispatcher(asyncore.dispatcher):
def writable(self): def writable(self):
self.uploadChunk = AdvancedDispatcher._buf_len self.uploadChunk = AdvancedDispatcher._buf_len
if asyncore.maxUploadRate > 0: if asyncore.maxUploadRate > 0:
self.uploadChunk = asyncore.uploadBucket self.uploadChunk = int(asyncore.uploadBucket)
self.uploadChunk = min(self.uploadChunk, len(self.write_buf)) self.uploadChunk = min(self.uploadChunk, len(self.write_buf))
return asyncore.dispatcher.writable(self) and \ return asyncore.dispatcher.writable(self) and \
(self.connecting or (self.connected and self.uploadChunk > 0)) (self.connecting or (self.connected and self.uploadChunk > 0))
@ -83,7 +83,7 @@ class AdvancedDispatcher(asyncore.dispatcher):
def readable(self): def readable(self):
self.downloadChunk = AdvancedDispatcher._buf_len self.downloadChunk = AdvancedDispatcher._buf_len
if asyncore.maxDownloadRate > 0: if asyncore.maxDownloadRate > 0:
self.downloadChunk = asyncore.downloadBucket self.downloadChunk = int(asyncore.downloadBucket)
try: try:
if self.expectBytes > 0 and not self.fullyEstablished: if self.expectBytes > 0 and not self.fullyEstablished:
self.downloadChunk = min(self.downloadChunk, self.expectBytes - len(self.read_buf)) self.downloadChunk = min(self.downloadChunk, self.expectBytes - len(self.read_buf))

View File

@ -112,6 +112,8 @@ uploadBucket = 0
sentBytes = 0 sentBytes = 0
def read(obj): def read(obj):
if not can_receive():
return
try: try:
obj.handle_read_event() obj.handle_read_event()
except _reraised_exceptions: except _reraised_exceptions:
@ -120,6 +122,8 @@ def read(obj):
obj.handle_error() obj.handle_error()
def write(obj): def write(obj):
if not can_send():
return
try: try:
obj.handle_write_event() obj.handle_write_event()
except _reraised_exceptions: except _reraised_exceptions:
@ -129,19 +133,25 @@ def write(obj):
def set_rates(download, upload): def set_rates(download, upload):
global maxDownloadRate, maxUploadRate, downloadBucket, uploadBucket, downloadTimestamp, uploadTimestamp global maxDownloadRate, maxUploadRate, downloadBucket, uploadBucket, downloadTimestamp, uploadTimestamp
maxDownloadRate = float(download) maxDownloadRate = float(download) * 1024
maxUploadRate = float(upload) maxUploadRate = float(upload) * 1024
downloadBucket = maxDownloadRate downloadBucket = maxDownloadRate
uploadBucket = maxUploadRate uploadBucket = maxUploadRate
downloadTimestamp = time.time() downloadTimestamp = time.time()
uploadTimestamp = time.time() uploadTimestamp = time.time()
def can_receive():
return maxDownloadRate == 0 or downloadBucket > 0
def can_send():
return maxUploadRate == 0 or uploadBucket > 0
def update_received(download=0): def update_received(download=0):
global receivedBytes, downloadBucket, downloadTimestamp global receivedBytes, downloadBucket, downloadTimestamp
currentTimestamp = time.time() currentTimestamp = time.time()
receivedBytes += download receivedBytes += download
if maxDownloadRate > 0: if maxDownloadRate > 0:
bucketIncrease = int(maxDownloadRate * (currentTimestamp - downloadTimestamp)) bucketIncrease = maxDownloadRate * (currentTimestamp - downloadTimestamp)
downloadBucket += bucketIncrease downloadBucket += bucketIncrease
if downloadBucket > maxDownloadRate: if downloadBucket > maxDownloadRate:
downloadBucket = int(maxDownloadRate) downloadBucket = int(maxDownloadRate)
@ -153,7 +163,7 @@ def update_sent(upload=0):
currentTimestamp = time.time() currentTimestamp = time.time()
sentBytes += upload sentBytes += upload
if maxUploadRate > 0: if maxUploadRate > 0:
bucketIncrease = int(maxUploadRate * (currentTimestamp - uploadTimestamp)) bucketIncrease = maxUploadRate * (currentTimestamp - uploadTimestamp)
uploadBucket += bucketIncrease uploadBucket += bucketIncrease
if uploadBucket > maxUploadRate: if uploadBucket > maxUploadRate:
uploadBucket = int(maxUploadRate) uploadBucket = int(maxUploadRate)
@ -170,9 +180,9 @@ def _exception(obj):
def readwrite(obj, flags): def readwrite(obj, flags):
try: try:
if flags & select.POLLIN: if flags & select.POLLIN and can_receive():
obj.handle_read_event() obj.handle_read_event()
if flags & select.POLLOUT: if flags & select.POLLOUT and can_send():
obj.handle_write_event() obj.handle_write_event()
if flags & select.POLLPRI: if flags & select.POLLPRI:
obj.handle_expt_event() obj.handle_expt_event()

View File

@ -8,23 +8,28 @@ from network.dandelion import Dandelion
import protocol import protocol
import state import state
class BMObjectInsufficientPOWError(Exception): pass class BMObjectInsufficientPOWError(Exception):
errorCodes = ("Insufficient proof of work")
class BMObjectInvalidDataError(Exception): pass class BMObjectInvalidDataError(Exception):
errorCodes = ("Data invalid")
class BMObjectExpiredError(Exception): pass class BMObjectExpiredError(Exception):
errorCodes = ("Object expired")
class BMObjectUnwantedStreamError(Exception): pass class BMObjectUnwantedStreamError(Exception):
errorCodes = ("Object in unwanted stream")
class BMObjectInvalidError(Exception): pass class BMObjectInvalidError(Exception):
errorCodes = ("Invalid object")
class BMObjectAlreadyHaveError(Exception): class BMObjectAlreadyHaveError(Exception):
pass errorCodes = ("Already have this object")
class BMObject(object): class BMObject(object):

View File

@ -24,13 +24,16 @@ import shared
import state import state
import protocol import protocol
class BMProtoError(ProxyError): pass class BMProtoError(ProxyError):
errorCodes = ("Protocol error")
class BMProtoInsufficientDataError(BMProtoError): pass class BMProtoInsufficientDataError(BMProtoError):
errorCodes = ("Insufficient data")
class BMProtoExcessiveDataError(BMProtoError): pass class BMProtoExcessiveDataError(BMProtoError):
errorCodes = ("Too much data")
class BMProto(AdvancedDispatcher, ObjectTracker): class BMProto(AdvancedDispatcher, ObjectTracker):
@ -494,6 +497,20 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
return False return False
except: except:
pass pass
if not self.isOutbound:
# incoming from a peer we're connected to as outbound, or server full
# report the same error to counter deanonymisation
if state.Peer(self.destination.host, self.peerNode.port) in \
network.connectionpool.BMConnectionPool().inboundConnections or \
len(network.connectionpool.BMConnectionPool().inboundConnections) + \
len(network.connectionpool.BMConnectionPool().outboundConnections) > \
BMConfigParser().safeGetInt("bitmessagesettings", "maxtotalconnections") + \
BMConfigParser().safeGetInt("bitmessagesettings", "maxbootstrapconnections"):
self.append_write_buf(protocol.assembleErrorMessage(fatal=2,
errorText="Server full, please try again later."))
logger.debug ("Closed connection to %s due to server full or duplicate inbound/outbound.",
str(self.destination))
return False
if network.connectionpool.BMConnectionPool().isAlreadyConnected(self.nonce): if network.connectionpool.BMConnectionPool().isAlreadyConnected(self.nonce):
self.append_write_buf(protocol.assembleErrorMessage(fatal=2, self.append_write_buf(protocol.assembleErrorMessage(fatal=2,
errorText="I'm connected to myself. Closing connection.")) errorText="I'm connected to myself. Closing connection."))

View File

@ -22,8 +22,8 @@ import state
class BMConnectionPool(object): class BMConnectionPool(object):
def __init__(self): def __init__(self):
asyncore.set_rates( asyncore.set_rates(
BMConfigParser().safeGetInt("bitmessagesettings", "maxdownloadrate") * 1024, BMConfigParser().safeGetInt("bitmessagesettings", "maxdownloadrate"),
BMConfigParser().safeGetInt("bitmessagesettings", "maxuploadrate") * 1024) BMConfigParser().safeGetInt("bitmessagesettings", "maxuploadrate"))
self.outboundConnections = {} self.outboundConnections = {}
self.inboundConnections = {} self.inboundConnections = {}
self.listeningSockets = {} self.listeningSockets = {}
@ -65,12 +65,18 @@ class BMConnectionPool(object):
def getConnectionByAddr(self, addr): def getConnectionByAddr(self, addr):
if addr in self.inboundConnections: if addr in self.inboundConnections:
return self.inboundConnections[addr] return self.inboundConnections[addr]
if addr.host in self.inboundConnections: try:
return self.inboundConnections[addr.host] if addr.host in self.inboundConnections:
return self.inboundConnections[addr.host]
except AttributeError:
pass
if addr in self.outboundConnections: if addr in self.outboundConnections:
return self.outboundConnections[addr] return self.outboundConnections[addr]
if addr.host in self.udpSockets: try:
return self.udpSockets[addr.host] if addr.host in self.udpSockets:
return self.udpSockets[addr.host]
except AttributeError:
pass
raise KeyError raise KeyError
def isAlreadyConnected(self, nodeid): def isAlreadyConnected(self, nodeid):

View File

@ -43,7 +43,7 @@ class DownloadThread(threading.Thread, StoppableThread):
connections = BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values() connections = BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values()
random.shuffle(connections) random.shuffle(connections)
try: try:
requestChunk = max(int(DownloadThread.maxRequestChunk / len(connections)), 1) requestChunk = max(int(min(DownloadThread.maxRequestChunk, len(missingObjects)) / len(connections)), 1)
except ZeroDivisionError: except ZeroDivisionError:
requestChunk = 1 requestChunk = 1
for i in connections: for i in connections:
@ -63,15 +63,14 @@ class DownloadThread(threading.Thread, StoppableThread):
except KeyError: except KeyError:
continue continue
random.shuffle(request) random.shuffle(request)
if len(request) > requestChunk - downloadPending:
request = request[:max(1, requestChunk - downloadPending)]
if not request: if not request:
continue continue
if len(request) > requestChunk - downloadPending:
request = request[:requestChunk - downloadPending]
# mark them as pending # mark them as pending
for k in request: for k in request:
i.objectsNewToMe[k] = False i.objectsNewToMe[k] = False
missingObjects[k] = now missingObjects[k] = now
payload = bytearray() payload = bytearray()
payload.extend(addresses.encodeVarint(len(request))) payload.extend(addresses.encodeVarint(len(request)))
for chunk in request: for chunk in request:

View File

@ -29,6 +29,7 @@ class ObjectTracker(object):
invInitialCapacity = 50000 invInitialCapacity = 50000
invErrorRate = 0.03 invErrorRate = 0.03
trackingExpires = 3600 trackingExpires = 3600
initialTimeOffset = 60
def __init__(self): def __init__(self):
self.objectsNewToMe = {} self.objectsNewToMe = {}
@ -87,7 +88,7 @@ class ObjectTracker(object):
if hashId in Dandelion().hashMap: if hashId in Dandelion().hashMap:
Dandelion().fluffTrigger(hashId) Dandelion().fluffTrigger(hashId)
if hashId not in missingObjects: if hashId not in missingObjects:
missingObjects[hashId] = time.time() missingObjects[hashId] = time.time() - ObjectTracker.initialTimeOffset
with self.objectsNewToMeLock: with self.objectsNewToMeLock:
self.objectsNewToMe[hashId] = True self.objectsNewToMe[hashId] = True

View File

@ -10,7 +10,7 @@ import state
class ProxyError(Exception): class ProxyError(Exception):
errorCodes = ("UnknownError") errorCodes = ("UnknownError")
def __init__(self, code): def __init__(self, code=-1):
self.code = code self.code = code
try: try:
self.message = self.__class__.errorCodes[self.code] self.message = self.__class__.errorCodes[self.code]

View File

@ -180,7 +180,7 @@ class TCPConnection(BMProto, TLSDispatcher):
payload += hash payload += hash
objectCount += 1 objectCount += 1
if objectCount >= BMProto.maxObjectCount: if objectCount >= BMProto.maxObjectCount:
self.sendChunk() sendChunk()
payload = b'' payload = b''
objectCount = 0 objectCount = 0
@ -292,7 +292,10 @@ class TCPServer(AdvancedDispatcher):
if len(network.connectionpool.BMConnectionPool().inboundConnections) + \ if len(network.connectionpool.BMConnectionPool().inboundConnections) + \
len(network.connectionpool.BMConnectionPool().outboundConnections) > \ len(network.connectionpool.BMConnectionPool().outboundConnections) > \
BMConfigParser().safeGetInt("bitmessagesettings", "maxtotalconnections") + \ BMConfigParser().safeGetInt("bitmessagesettings", "maxtotalconnections") + \
BMConfigParser().safeGetInt("bitmessagesettings", "maxbootstrapconnections"): BMConfigParser().safeGetInt("bitmessagesettings", "maxbootstrapconnections") + 10:
# 10 is a sort of buffer, in between it will go through the version handshake
# and return an error to the peer
logger.warning("Server full, dropping connection")
sock.close() sock.close()
return return
try: try:

View File

@ -9,14 +9,12 @@ from helper_sql import sqlQuery, sqlStoredProcedure
from helper_threading import StoppableThread from helper_threading import StoppableThread
from knownnodes import saveKnownNodes from knownnodes import saveKnownNodes
from inventory import Inventory from inventory import Inventory
import protocol
from queues import addressGeneratorQueue, objectProcessorQueue, UISignalQueue, workerQueue from queues import addressGeneratorQueue, objectProcessorQueue, UISignalQueue, workerQueue
import shared import shared
import state import state
def doCleanShutdown(): def doCleanShutdown():
state.shutdown = 1 #Used to tell proof of work worker threads and the objectProcessorThread to exit. state.shutdown = 1 #Used to tell proof of work worker threads and the objectProcessorThread to exit.
protocol.broadcastToSendDataQueues((0, 'shutdown', 'no data'))
objectProcessorQueue.put(('checkShutdownVariable', 'no data')) objectProcessorQueue.put(('checkShutdownVariable', 'no data'))
for thread in threading.enumerate(): for thread in threading.enumerate():
if thread.isAlive() and isinstance(thread, StoppableThread): if thread.isAlive() and isinstance(thread, StoppableThread):

View File

@ -78,6 +78,15 @@ class singleinstance:
return return
if self.daemon and self.lockPid == os.getpid(): if self.daemon and self.lockPid == os.getpid():
# these are the two initial forks while daemonizing # these are the two initial forks while daemonizing
try:
if sys.platform == 'win32':
if hasattr(self, 'fd'):
os.close(self.fd)
else:
fcntl.lockf(self.fp, fcntl.LOCK_UN)
except Exception, e:
pass
return return
print "Cleaning up lockfile" print "Cleaning up lockfile"
try: try:

View File

@ -1 +1,2 @@
softwareName = 'PyBitmessage'
softwareVersion = '0.6.2' softwareVersion = '0.6.2'