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'],
'pyopencl': ['pyopencl'],
'notify2': ['notify2'],
'sound:platform_system=="Windows"': ['winsound']
'sound;platform_system=="Windows"': ['winsound']
},
classifiers=[
"License :: OSI Approved :: MIT License"

View File

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

View File

@ -69,7 +69,7 @@ import queues
import shutdown
import state
from statusbar import BMStatusBar
import throttle
from network.asyncore_pollchoose import set_rates
from version import softwareVersion
import sound
@ -2287,16 +2287,16 @@ class MyForm(settingsmixin.SMainWindow):
int(float(self.settingsDialogInstance.ui.lineEditMaxDownloadRate.text()))))
BMConfigParser().set('bitmessagesettings', 'maxuploadrate', str(
int(float(self.settingsDialogInstance.ui.lineEditMaxUploadRate.text()))))
except:
except ValueError:
QMessageBox.about(self, _translate("MainWindow", "Number needed"), _translate(
"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(
int(float(self.settingsDialogInstance.ui.lineEditMaxOutboundConnections.text()))))
throttle.SendThrottle().resetLimit()
throttle.ReceiveThrottle().resetLimit()
BMConfigParser().set('bitmessagesettings', 'namecoinrpctype',
self.settingsDialogInstance.getNamecoinType())
BMConfigParser().set('bitmessagesettings', 'namecoinrpchost', str(

View File

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

View File

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

View File

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

View File

@ -8,23 +8,28 @@ from network.dandelion import Dandelion
import protocol
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):
pass
errorCodes = ("Already have this object")
class BMObject(object):

View File

@ -24,13 +24,16 @@ import shared
import state
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):
@ -494,6 +497,20 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
return False
except:
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):
self.append_write_buf(protocol.assembleErrorMessage(fatal=2,
errorText="I'm connected to myself. Closing connection."))

View File

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

View File

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

View File

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

View File

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

View File

@ -180,7 +180,7 @@ class TCPConnection(BMProto, TLSDispatcher):
payload += hash
objectCount += 1
if objectCount >= BMProto.maxObjectCount:
self.sendChunk()
sendChunk()
payload = b''
objectCount = 0
@ -292,7 +292,10 @@ class TCPServer(AdvancedDispatcher):
if len(network.connectionpool.BMConnectionPool().inboundConnections) + \
len(network.connectionpool.BMConnectionPool().outboundConnections) > \
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()
return
try:

View File

@ -9,14 +9,12 @@ from helper_sql import sqlQuery, sqlStoredProcedure
from helper_threading import StoppableThread
from knownnodes import saveKnownNodes
from inventory import Inventory
import protocol
from queues import addressGeneratorQueue, objectProcessorQueue, UISignalQueue, workerQueue
import shared
import state
def doCleanShutdown():
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'))
for thread in threading.enumerate():
if thread.isAlive() and isinstance(thread, StoppableThread):

View File

@ -78,6 +78,15 @@ class singleinstance:
return
if self.daemon and self.lockPid == os.getpid():
# 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
print "Cleaning up lockfile"
try:

View File

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