Compare commits

..

4 Commits

Author SHA1 Message Date
f9c1a7bc33
Sudden KeyError in addKnownNode() 2021-03-24 14:54:36 +02:00
345403631a
Log knownnodes summary before exit 2021-01-22 19:58:08 +02:00
d968a7b99b
Don't update expired node if have enough nodes in that stream 2021-01-22 19:58:08 +02:00
7558f74ab6
Introduce outages - a dict of all hosts
to track multiport configurations / port change events
2021-01-22 19:58:01 +02:00
65 changed files with 777 additions and 928 deletions

3
.gitattributes vendored
View File

@ -1,3 +0,0 @@
# Pickle files (for testing) should always have UNIX line endings.
# Windows issue like here https://stackoverflow.com/questions/556269
knownnodes.dat text eol=lf

3
.gitmodules vendored
View File

@ -1,3 +0,0 @@
[submodule "packages/flatpak/shared-modules"]
path = packages/flatpak/shared-modules
url = https://github.com/flathub/shared-modules.git

View File

@ -1,9 +1,6 @@
language: python
cache: pip
dist: bionic
python:
- "2.7_with_system_site_packages"
- "3.7"
- "2.7"
addons:
apt:
packages:
@ -14,9 +11,9 @@ addons:
- xvfb
install:
- pip install -r requirements.txt
- ln -s src pybitmessage # tests environment
- python setup.py install
- export PYTHONWARNINGS=all
script:
- python checkdeps.py
- xvfb-run src/bitmessagemain.py -t
- python -bm tests
- python setup.py test

View File

@ -1,64 +0,0 @@
FROM ubuntu:bionic AS pybm-travis-bionic
ENV DEBIAN_FRONTEND noninteractive
ENV TRAVIS_SKIP_APT_UPDATE 1
RUN apt-get update
RUN apt-get install -yq --no-install-suggests --no-install-recommends \
software-properties-common
RUN dpkg --add-architecture i386
RUN add-apt-repository ppa:deadsnakes/ppa
RUN apt-get -y install sudo
RUN apt-get install -yq --no-install-suggests --no-install-recommends \
# travis xenial bionic
python-setuptools libssl-dev libpq-dev python-prctl python-dev \
python-dev python-virtualenv python-pip virtualenv \
# dpkg
python-minimal python-setuptools python-all python openssl libssl-dev \
dh-apparmor debhelper dh-python python-msgpack python-qt4 python-stdeb \
python-all-dev python-crypto python-psutil \
fakeroot python-pytest \
# Code quality
pylint python-pycodestyle python3-pycodestyle pycodestyle python-flake8 \
python3-flake8 flake8 python-pyflakes python3-pyflakes pyflakes pyflakes3 \
curl \
# Wine
python python-pip wget wine-stable winetricks mingw-w64 wine32 wine64 xvfb \
# Buildbot
python3-dev libffi-dev python3-setuptools \
python3-pip \
# python 3.7
python3.7 python3.7-dev \
# .travis.yml
build-essential libcap-dev tor \
language-pack-en
# cleanup
RUN rm -rf /var/lib/apt/lists/*
RUN useradd -m -U builder
RUN echo 'builder ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
# travis2bash
RUN wget -O /usr/local/bin/travis2bash.sh https://git.bitmessage.org/Bitmessage/buildbot-scripts/raw/branch/master/travis2bash.sh
RUN chmod +x /usr/local/bin/travis2bash.sh
# copy sources
COPY . /home/builder/src
RUN chown -R builder.builder /home/builder/src
USER builder
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8
WORKDIR /home/builder/src
ENTRYPOINT /usr/local/bin/travis2bash.sh

43
android_instruction.rst Normal file
View File

@ -0,0 +1,43 @@
PyBitmessage(Android)
This sample aims to be as close to a real world example of a mobile. It has a more refined design and also provides a practical example of how a mobile app would interact and communicate with its addresses.
Steps for trying out this sample:
Compile and install the mobile app onto your mobile device or emulator.
Getting Started
This sample uses the kivy as Kivy is an open source, cross-platform Python framework for the development of applications that make use of innovative, multi-touch user interfaces. The aim is to allow for quick and easy interaction design and rapid prototyping whilst making your code reusable and deployable.
Kivy is written in Python and Cython, supports various input devices and has an extensive widget library. With the same codebase, you can target Windows, OS X, Linux, Android and iOS. All Kivy widgets are built with multitouch support.
Kivy in support take Buildozer which is a tool that automates the entire build process. It downloads and sets up all the prerequisite for python-for-android, including the android SDK and NDK, then builds an apk that can be automatically pushed to the device.
Buildozer currently works only in Linux, and is an alpha release, but it already works well and can significantly simplify the apk build.
To build this project, use the "Buildozer android release deploy run" command or use.
Buildozer ue=sed for creating application packages easily.The goal is to have one "buildozer.spec" file in your app directory, describing your application requirements and settings such as title, icon, included modules etc. Buildozer will use that spec to create a package for Android, iOS, Windows, OSX and/or Linux.
Installing Requirements
You can create a package for android using the python-for-android project as with using the Buildozer tool to automate the entire process. You can also see Packaging your application for the Kivy Launcher to run kivy programs without compiling them.
You can get buildozer at https://github.com/kivy/buildozer or you can directly install using pip install buildozer
This will install buildozer in your system. Afterwards, navigate to your project directory and run:
buildozer init
This creates a buildozer.spec file controlling your build configuration. You should edit it appropriately with your app name etc. You can set variables to control most or all of the parameters passed to python-for-android.
Install buildozers dependencies.
Finally, plug in your android device and run:
buildozer android debug deploy run >> To build, push and automatically run the apk on your device. Here we used debug as tested in debug mode for now.
Packaging your application for the Kivy Launcher

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python2
"""
Check dependencies and give recommendations about how to satisfy them

View File

@ -6,4 +6,4 @@ Comment=Send encrypted messages
Exec=pybitmessage %F
Icon=pybitmessage
Terminal=false
Categories=Office;Email;Network;
Categories=Office;Email;

View File

@ -1,57 +0,0 @@
{
"id": "org.bitmessage.BaseApp",
"branch": "19.08",
"runtime": "org.freedesktop.Platform",
"sdk": "org.freedesktop.Sdk",
"runtime-version": "19.08",
"separate-locales": false,
"modules": [
"shared-modules/python2.7/python-2.7.json",
"shared-modules/qt4/qt4-4.8.7-minimal.json",
{
"name": "python-sip",
"sources": [
{
"type": "archive",
"url": "https://www.riverbankcomputing.com/static/Downloads/sip/4.19.25/sip-4.19.25.tar.gz",
"sha256": "b39d93e937647807bac23579edbff25fe46d16213f708370072574ab1f1b4211"
}
],
"buildsystem": "simple",
"build-commands": [
"python configure.py --sip-module PyQt4.sip --no-dist-info",
"make",
"make install"
]
},
{
"name": "python-qt4",
"sources": [
{
"type": "archive",
"url": "http://sourceforge.net/projects/pyqt/files/PyQt4/PyQt-4.12.3/PyQt4_gpl_x11-4.12.3.tar.gz",
"sha256": "a00f5abef240a7b5852b7924fa5fdf5174569525dc076cd368a566619e56d472"
}
],
"buildsystem": "simple",
"build-commands": [
"python configure.py -w --confirm-license",
"make",
"make install"
]
},
{
"name" : "PyBitmessage-dependencies",
"buildsystem" : "simple",
"build-options": {
"build-args": [
"--share=network"
]
},
"build-commands": [
"pip --version",
"pip install setuptools msgpack"
]
}
]
}

View File

@ -1,48 +0,0 @@
{
"app-id": "org.bitmessage.PyBitmessage",
"runtime": "org.freedesktop.Platform",
"runtime-version": "19.08",
"branch": "stable",
"sdk": "org.freedesktop.Sdk",
"base": "org.bitmessage.BaseApp",
"command": "pybitmessage",
"base-version":"stable",
"finish-args" : [
"--share=network",
"--socket=x11",
"--share=ipc",
"--filesystem=xdg-config/PyBitmessage:create"
],
"modules": [
{
"name" : "PyBitmessage",
"buildsystem" : "simple",
"build-options": {
"build-args": [
"--share=network"
]
},
"build-commands": [
"python --version",
"pwd",
"ls",
"python checkdeps.py",
"python setup.py install --prefix=/app --exec-prefix=/app",
"sed -i 's~/usr/bin/~/app/bin/~' /app/bin/pybitmessage",
"cat /app/bin/pybitmessage",
"mv /app/share/applications/pybitmessage.desktop /app/share/applications/org.bitmessage.PyBitmessage.desktop",
"sed -i 's~Icon=pybitmessage~Icon=org.bitmessage.PyBitmessage~' /app/share/applications/org.bitmessage.PyBitmessage.desktop",
"mv /app/share/icons/hicolor/scalable/apps/pybitmessage.svg /app/share/icons/hicolor/scalable/apps/org.bitmessage.PyBitmessage.svg",
"mv /app/share/icons/hicolor/24x24/apps/pybitmessage.png /app/share/icons/hicolor/24x24/apps/org.bitmessage.PyBitmessage.png",
"which pybitmessage"
],
"sources" : [
{
"type" : "dir",
"path" : "../../"
}
]
}
]
}

@ -1 +0,0 @@
Subproject commit fd4d38328ccb078b88ad4a891807e593ae8de806

View File

@ -1,54 +1,52 @@
# -*- mode: python -*-
import ctypes
import os
import sys
import time
import sys
if ctypes.sizeof(ctypes.c_voidp) == 4:
arch=32
else:
arch=64
sslName = 'OpenSSL-Win%s' % ("32" if arch == 32 else "64")
site_root = os.path.abspath(HOMEPATH)
spec_root = os.path.abspath(SPECPATH)
arch = 32 if ctypes.sizeof(ctypes.c_voidp) == 4 else 64
cdrivePath = site_root[0:3]
srcPath = os.path.join(spec_root[:-20], "src")
sslName = 'OpenSSL-Win%i' % arch
qtBase = "PyQt4"
openSSLPath = os.path.join(cdrivePath, sslName)
msvcrDllPath = os.path.join(cdrivePath, "windows", "system32")
pythonDllPath = os.path.join(cdrivePath, "Python27")
outPath = os.path.join(spec_root, "bitmessagemain")
qtBase = "PyQt4"
sys.path.insert(0, srcPath)
os.chdir(srcPath)
importPath = srcPath
sys.path.insert(0,importPath)
os.chdir(sys.path[0])
from version import softwareVersion
today = time.strftime("%Y%m%d")
snapshot = False
os.rename(
os.path.join(srcPath, '__init__.py'),
os.path.join(srcPath, '__init__.py.backup'))
os.rename(os.path.join(srcPath, '__init__.py'), os.path.join(srcPath, '__init__.py.backup'))
# -*- mode: python -*-
a = Analysis(
[os.path.join(srcPath, 'bitmessagemain.py')],
pathex=[outPath],
hiddenimports=[
'bitmessageqt.languagebox', 'pyopencl', 'numpy', 'win32com',
'setuptools.msvc', '_cffi_backend'
],
hookspath=None,
runtime_hooks=None,
excludes=['bsddb', 'bz2', 'tcl', 'tk', 'Tkinter']
)
os.rename(
os.path.join(srcPath, '__init__.py.backup'),
os.path.join(srcPath, '__init__.py'))
[os.path.join(srcPath, 'bitmessagemain.py')],
pathex=[outPath],
hiddenimports=['bitmessageqt.languagebox', 'pyopencl','numpy', 'win32com' , 'setuptools.msvc' ,'_cffi_backend'],
hookspath=None,
runtime_hooks=None
)
os.rename(os.path.join(srcPath, '__init__.py.backup'), os.path.join(srcPath, '__init__.py'))
def addTranslations():
import os
extraDatas = []
for file_ in os.listdir(os.path.join(srcPath, 'translations')):
if file_[-3:] != ".qm":
continue
extraDatas.append((
os.path.join('translations', file_),
extraDatas.append((os.path.join('translations', file_),
os.path.join(srcPath, 'translations', file_), 'DATA'))
for libdir in sys.path:
qtdir = os.path.join(libdir, qtBase, 'translations')
@ -59,74 +57,57 @@ def addTranslations():
for file_ in os.listdir(qtdir):
if file_[0:3] != "qt_" or file_[5:8] != ".qm":
continue
extraDatas.append((
os.path.join('translations', file_),
extraDatas.append((os.path.join('translations', file_),
os.path.join(qtdir, file_), 'DATA'))
return extraDatas
dir_append = os.path.join(srcPath, 'bitmessageqt')
a.datas += [
(os.path.join('ui', file_), os.path.join(dir_append, file_), 'DATA')
for file_ in os.listdir(dir_append) if file_.endswith('.ui')
]
def addUIs():
import os
extraDatas = []
for file_ in os.listdir(os.path.join(srcPath, 'bitmessageqt')):
if file_[-3:] != ".ui":
continue
extraDatas.append((os.path.join('ui', file_), os.path.join(srcPath,
'bitmessageqt', file_), 'DATA'))
return extraDatas
# append the translations directory
a.datas += addTranslations()
a.datas += addUIs()
excluded_binaries = [
'QtOpenGL4.dll',
'QtSvg4.dll',
'QtXml4.dll',
]
a.binaries = TOC([x for x in a.binaries if x[0] not in excluded_binaries])
a.binaries += [
# No effect: libeay32.dll will be taken from PyQt if installed
('libeay32.dll', os.path.join(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'),
(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')
]
from version import softwareVersion
today = time.strftime("%Y%m%d")
fname = '%s_%%s_%s.exe' % (
('Bitmessagedev', today) if snapshot else ('Bitmessage', softwareVersion)
) % ("x86" if arch == 32 else "x64")
a.binaries += [('libeay32.dll', os.path.join(openSSLPath, 'libeay32.dll'), 'BINARY'),
('python27.dll', os.path.join(pythonDllPath, 'python27.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'),
(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')
]
fname = 'Bitmessage_%s_%s.exe' % ("x86" if arch == 32 else "x64", softwareVersion)
if snapshot:
fname = 'Bitmessagedev_%s_%s.exe' % ("x86" if arch == 32 else "x64", today)
pyz = PYZ(a.pure)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
name=fname,
debug=False,
strip=None,
upx=False,
console=False, icon=os.path.join(srcPath, 'images', 'can-icon.ico')
)
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
a.binaries,
[],
name=fname,
debug=False,
strip=None,
upx=False,
console=False, icon= os.path.join(srcPath, 'images', 'can-icon.ico'))
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=False,
name='main')
coll = COLLECT(
exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=False,
name='main'
)

View File

@ -1,4 +1,3 @@
coverage
python_prctl
psutil
pycrypto

View File

@ -1,4 +0,0 @@
#!/bin/bash
docker build -t pybm-travis-bionic -f Dockerfile.travis .
docker run pybm-travis-bionic

View File

@ -12,7 +12,6 @@ from src.version import softwareVersion
EXTRAS_REQUIRE = {
'docs': ['sphinx', 'sphinxcontrib-apidoc', 'm2r'],
'gir': ['pygobject'],
'json': ['jsonrpclib'],
'notify2': ['notify2'],
@ -21,8 +20,8 @@ EXTRAS_REQUIRE = {
'qrcode': ['qrcode'],
'sound;platform_system=="Windows"': ['winsound'],
'tor': ['stem'],
'xdg': ['pyxdg'],
'xml': ['defusedxml']
'xml': ['defusedxml'],
'docs': ['sphinx', 'sphinxcontrib-apidoc', 'm2r']
}
@ -95,14 +94,11 @@ if __name__ == "__main__":
['desktop/icons/24x24/pybitmessage.png'])
]
try:
if platform.dist()[0] in ('Debian', 'Ubuntu'):
data_files += [
("etc/apparmor.d/",
['packages/apparmor/pybitmessage'])
]
except AttributeError:
pass # FIXME: use distro for more recent python
if platform.dist()[0] in ('Debian', 'Ubuntu'):
data_files += [
("etc/apparmor.d/",
['packages/apparmor/pybitmessage'])
]
dist = setup(
name='pybitmessage',
@ -119,7 +115,6 @@ if __name__ == "__main__":
#keywords='',
install_requires=installRequires,
tests_require=requirements,
test_suite='tests.unittest_discover',
extras_require=EXTRAS_REQUIRE,
classifiers=[
"License :: OSI Approved :: MIT License"
@ -158,9 +153,6 @@ if __name__ == "__main__":
'libmessaging ='
'pybitmessage.plugins.indicator_libmessaging [gir]'
],
'bitmessage.desktop': [
'freedesktop = pybitmessage.plugins.desktop_xdg [xdg]'
],
'bitmessage.proxyconfig': [
'stem = pybitmessage.plugins.proxyconfig_stem [tor]'
],

View File

@ -3,12 +3,10 @@ Operations with addresses
"""
# pylint: disable=redefined-outer-name,inconsistent-return-statements
import hashlib
import logging
from binascii import hexlify, unhexlify
from struct import pack, unpack
logger = logging.getLogger('default')
from debug import logger
ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
@ -25,7 +23,8 @@ def encodeBase58(num, alphabet=ALPHABET):
arr = []
base = len(alphabet)
while num:
num, rem = divmod(num, base)
rem = num % base
num = num // base
arr.append(alphabet[rem])
arr.reverse()
return ''.join(arr)
@ -149,16 +148,16 @@ def encodeAddress(version, stream, ripe):
'Programming error in encodeAddress: The length of'
' a given ripe hash was not 20.'
)
if ripe[:2] == b'\x00\x00':
if ripe[:2] == '\x00\x00':
ripe = ripe[2:]
elif ripe[:1] == b'\x00':
elif ripe[:1] == '\x00':
ripe = ripe[1:]
elif version == 4:
if len(ripe) != 20:
raise Exception(
'Programming error in encodeAddress: The length of'
' a given ripe hash was not 20.')
ripe = ripe.lstrip(b'\x00')
ripe = ripe.lstrip('\x00')
storedBinaryData = encodeVarint(version) + encodeVarint(stream) + ripe
@ -192,8 +191,8 @@ def decodeAddress(address):
status = 'invalidcharacters'
return status, 0, 0, ''
# after converting to hex, the string will be prepended
# with a 0x and appended with a L in python2
hexdata = hex(integer)[2:].rstrip('L')
# with a 0x and appended with a L
hexdata = hex(integer)[2:-1]
if len(hexdata) % 2 != 0:
hexdata = '0' + hexdata
@ -243,13 +242,13 @@ def decodeAddress(address):
data[bytesUsedByVersionNumber + bytesUsedByStreamNumber:-4]
if len(embeddedRipeData) == 19:
return status, addressVersionNumber, streamNumber, \
b'\x00' + embeddedRipeData
'\x00' + embeddedRipeData
elif len(embeddedRipeData) == 20:
return status, addressVersionNumber, streamNumber, \
embeddedRipeData
elif len(embeddedRipeData) == 18:
return status, addressVersionNumber, streamNumber, \
b'\x00\x00' + embeddedRipeData
'\x00\x00' + embeddedRipeData
elif len(embeddedRipeData) < 18:
return 'ripetooshort', 0, 0, ''
elif len(embeddedRipeData) > 20:
@ -258,7 +257,7 @@ def decodeAddress(address):
elif addressVersionNumber == 4:
embeddedRipeData = \
data[bytesUsedByVersionNumber + bytesUsedByStreamNumber:-4]
if embeddedRipeData[0:1] == b'\x00':
if embeddedRipeData[0:1] == '\x00':
# In order to enforce address non-malleability, encoded
# RIPE data must have NULL bytes removed from the front
return 'encodingproblem', 0, 0, ''
@ -266,7 +265,7 @@ def decodeAddress(address):
return 'ripetoolong', 0, 0, ''
elif len(embeddedRipeData) < 4:
return 'ripetooshort', 0, 0, ''
x00string = b'\x00' * (20 - len(embeddedRipeData))
x00string = '\x00' * (20 - len(embeddedRipeData))
return status, addressVersionNumber, streamNumber, \
x00string + embeddedRipeData

View File

@ -983,7 +983,7 @@ def sendMessage(sender="", recv="", broadcast=None, subject="", body="", reply=F
def loadInbox():
"""Load the list of messages"""
sys.stdout = sys.__stdout__
print("Loading inbox messages...")
print "Loading inbox messages..."
sys.stdout = printlog
where = "toaddress || fromaddress || subject || message"
@ -1035,7 +1035,7 @@ def loadInbox():
def loadSent():
"""Load the messages that sent"""
sys.stdout = sys.__stdout__
print("Loading sent messages...")
print "Loading sent messages..."
sys.stdout = printlog
where = "toaddress || fromaddress || subject || message"
@ -1121,7 +1121,7 @@ def loadSent():
def loadAddrBook():
"""Load address book"""
sys.stdout = sys.__stdout__
print("Loading address book...")
print "Loading address book..."
sys.stdout = printlog
ret = sqlQuery("SELECT label, address FROM addressbook")
@ -1228,7 +1228,7 @@ def run(stdscr):
def doShutdown():
"""Shutting the app down"""
sys.stdout = sys.__stdout__
print("Shutting down...")
print "Shutting down..."
sys.stdout = printlog
shutdown.doCleanShutdown()
sys.stdout = sys.__stdout__

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/python2.7
"""
The PyBitmessage startup script
"""
@ -12,11 +12,10 @@ The PyBitmessage startup script
import os
import sys
try:
import pathmagic
except ImportError:
from pybitmessage import pathmagic
app_dir = pathmagic.setup()
app_dir = os.path.dirname(os.path.abspath(__file__))
os.chdir(app_dir)
sys.path.insert(0, app_dir)
import depends
depends.check_dependencies()
@ -321,10 +320,9 @@ class Main(object):
receiveQueueThread = ReceiveQueueThread(i)
receiveQueueThread.daemon = True
receiveQueueThread.start()
if config.safeGetBoolean('bitmessagesettings', 'udp'):
state.announceThread = AnnounceThread()
state.announceThread.daemon = True
state.announceThread.start()
announceThread = AnnounceThread()
announceThread.daemon = True
announceThread.start()
state.invThread = InvThread()
state.invThread.daemon = True
state.invThread.start()
@ -353,6 +351,10 @@ class Main(object):
print('Running with curses')
import bitmessagecurses
bitmessagecurses.runwrapper()
elif state.kivy:
config.remove_option('bitmessagesettings', 'dontconnect')
# from bitmessagekivy.mpybit import NavigateApp
# NavigateApp().run()
else:
import bitmessageqt
bitmessageqt.run()
@ -379,7 +381,11 @@ class Main(object):
test_core_result = test_core.run()
self.stop()
test_core.cleanup()
sys.exit(not test_core_result.wasSuccessful())
sys.exit(
'Core tests failed!'
if test_core_result.errors or test_core_result.failures
else 0
)
@staticmethod
def daemonize():

View File

@ -640,6 +640,8 @@ class MyForm(settingsmixin.SMainWindow):
BMConfigParser().remove_section(addressInKeysFile)
BMConfigParser().save()
self.updateStartOnLogon()
self.change_translation()
# e.g. for editing labels
@ -824,7 +826,6 @@ class MyForm(settingsmixin.SMainWindow):
self.sqlInit()
self.indicatorInit()
self.notifierInit()
self.updateStartOnLogon()
self.ui.updateNetworkSwitchMenuLabel()
@ -843,28 +844,26 @@ class MyForm(settingsmixin.SMainWindow):
self._contact_selected = None
def updateStartOnLogon(self):
"""
Configure Bitmessage to start on startup (or remove the
configuration) based on the setting in the keys.dat file
"""
startonlogon = BMConfigParser().safeGetBoolean(
'bitmessagesettings', 'startonlogon')
if sys.platform.startswith('win'): # Auto-startup for Windows
# Configure Bitmessage to start on startup (or remove the
# configuration) based on the setting in the keys.dat file
if 'win32' in sys.platform or 'win64' in sys.platform:
# Auto-startup for Windows
RUN_PATH = "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run"
settings = QtCore.QSettings(
self.settings = QtCore.QSettings(
RUN_PATH, QtCore.QSettings.NativeFormat)
# In case the user moves the program and the registry entry is
# no longer valid, this will delete the old registry entry.
if startonlogon:
settings.setValue("PyBitmessage", sys.argv[0])
else:
settings.remove("PyBitmessage")
else:
try: # get desktop plugin if any
self.desktop = get_plugin('desktop')()
self.desktop.adjust_startonlogon(startonlogon)
except (NameError, TypeError):
self.desktop = False
self.settings.remove("PyBitmessage")
if BMConfigParser().getboolean(
'bitmessagesettings', 'startonlogon'
):
self.settings.setValue("PyBitmessage", sys.argv[0])
elif 'darwin' in sys.platform:
# startup for mac
pass
elif 'linux' in sys.platform:
# startup for linux
pass
def updateTTL(self, sliderPosition):
TTL = int(sliderPosition ** 3.199 + 3600)
@ -1424,11 +1423,9 @@ class MyForm(settingsmixin.SMainWindow):
def sqlInit(self):
register_adapter(QtCore.QByteArray, str)
# Try init the distro specific appindicator,
# for example the Ubuntu MessagingMenu
def indicatorInit(self):
"""
Try init the distro specific appindicator,
for example the Ubuntu MessagingMenu
"""
def _noop_update(*args, **kwargs):
pass

View File

@ -19,7 +19,7 @@ import widgets
from bmconfigparser import BMConfigParser
from helper_sql import sqlExecute, sqlStoredProcedure
from helper_startup import start_proxyconfig
from network import knownnodes, AnnounceThread
from network import knownnodes
from network.asyncore_pollchoose import set_rates
from tr import _translate
@ -119,6 +119,9 @@ class SettingsDialog(QtGui.QDialog):
self.checkBoxPortableMode.setDisabled(True)
if 'darwin' in sys.platform:
self.checkBoxStartOnLogon.setDisabled(True)
self.checkBoxStartOnLogon.setText(_translate(
"MainWindow", "Start-on-login not yet supported on your OS."))
self.checkBoxMinimizeToTray.setDisabled(True)
self.checkBoxMinimizeToTray.setText(_translate(
"MainWindow",
@ -127,19 +130,15 @@ class SettingsDialog(QtGui.QDialog):
self.checkBoxShowTrayNotifications.setText(_translate(
"MainWindow",
"Tray notifications not yet supported on your OS."))
if 'win' not in sys.platform and not self.parent.desktop:
elif 'linux' in sys.platform:
self.checkBoxStartOnLogon.setDisabled(True)
self.checkBoxStartOnLogon.setText(_translate(
"MainWindow", "Start-on-login not yet supported on your OS."))
# On the Network settings tab:
self.lineEditTCPPort.setText(str(
config.get('bitmessagesettings', 'port')))
self.checkBoxUPnP.setChecked(
config.safeGetBoolean('bitmessagesettings', 'upnp'))
self.checkBoxUDP.setChecked(
config.safeGetBoolean('bitmessagesettings', 'udp'))
self.checkBoxAuthentication.setChecked(
config.getboolean('bitmessagesettings', 'socksauthentication'))
self.checkBoxSocksListen.setChecked(
@ -328,8 +327,7 @@ class SettingsDialog(QtGui.QDialog):
self.lineEditTCPPort.text()):
self.config.set(
'bitmessagesettings', 'port', str(self.lineEditTCPPort.text()))
if not self.config.safeGetBoolean(
'bitmessagesettings', 'dontconnect'):
if not self.config.safeGetBoolean('bitmessagesettings', 'dontconnect'):
self.net_restart_needed = True
if self.checkBoxUPnP.isChecked() != self.config.safeGetBoolean(
@ -342,26 +340,11 @@ class SettingsDialog(QtGui.QDialog):
upnpThread = upnp.uPnPThread()
upnpThread.start()
udp_enabled = self.checkBoxUDP.isChecked()
if udp_enabled != self.config.safeGetBoolean(
'bitmessagesettings', 'udp'):
self.config.set('bitmessagesettings', 'udp', str(udp_enabled))
if udp_enabled:
announceThread = AnnounceThread()
announceThread.daemon = True
announceThread.start()
else:
try:
state.announceThread.stopThread()
except AttributeError:
pass
proxytype_index = self.comboBoxProxyType.currentIndex()
if proxytype_index == 0:
if self._proxy_type and state.statusIconColor != 'red':
self.net_restart_needed = True
elif state.statusIconColor == 'red' and self.config.safeGetBoolean(
'bitmessagesettings', 'dontconnect'):
elif state.statusIconColor == 'red' and self.config.safeGetBoolean('bitmessagesettings', 'dontconnect'):
self.net_restart_needed = False
elif self.comboBoxProxyType.currentText() != self._proxy_type:
self.net_restart_needed = True
@ -387,11 +370,8 @@ class SettingsDialog(QtGui.QDialog):
self.lineEditSocksPassword.text()))
self.config.set('bitmessagesettings', 'sockslisten', str(
self.checkBoxSocksListen.isChecked()))
if (
self.checkBoxOnionOnly.isChecked()
and not self.config.safeGetBoolean(
'bitmessagesettings', 'onionservicesonly')
):
if self.checkBoxOnionOnly.isChecked() \
and not self.config.safeGetBoolean('bitmessagesettings', 'onionservicesonly'):
self.net_restart_needed = True
self.config.set('bitmessagesettings', 'onionservicesonly', str(
self.checkBoxOnionOnly.isChecked()))
@ -453,8 +433,8 @@ class SettingsDialog(QtGui.QDialog):
acceptableDifficultyChanged = False
if (
float(self.lineEditMaxAcceptableTotalDifficulty.text()) >= 1
or float(self.lineEditMaxAcceptableTotalDifficulty.text()) == 0
float(self.lineEditMaxAcceptableTotalDifficulty.text()) >= 1
or float(self.lineEditMaxAcceptableTotalDifficulty.text()) == 0
):
if self.config.get(
'bitmessagesettings', 'maxacceptablenoncetrialsperbyte'
@ -470,8 +450,8 @@ class SettingsDialog(QtGui.QDialog):
* defaults.networkDefaultProofOfWorkNonceTrialsPerByte))
)
if (
float(self.lineEditMaxAcceptableSmallMessageDifficulty.text()) >= 1
or float(self.lineEditMaxAcceptableSmallMessageDifficulty.text()) == 0
float(self.lineEditMaxAcceptableSmallMessageDifficulty.text()) >= 1
or float(self.lineEditMaxAcceptableSmallMessageDifficulty.text()) == 0
):
if self.config.get(
'bitmessagesettings', 'maxacceptablepayloadlengthextrabytes'
@ -562,8 +542,8 @@ class SettingsDialog(QtGui.QDialog):
self.parent.updateStartOnLogon()
if (
state.appdata != paths.lookupExeFolder()
and self.checkBoxPortableMode.isChecked()
state.appdata != paths.lookupExeFolder()
and self.checkBoxPortableMode.isChecked()
):
# If we are NOT using portable mode now but the user selected
# that we should...
@ -585,8 +565,8 @@ class SettingsDialog(QtGui.QDialog):
pass
if (
state.appdata == paths.lookupExeFolder()
and not self.checkBoxPortableMode.isChecked()
state.appdata == paths.lookupExeFolder()
and not self.checkBoxPortableMode.isChecked()
):
# If we ARE using portable mode now but the user selected
# that we shouldn't...

View File

@ -231,7 +231,7 @@
</layout>
</widget>
</item>
<item row="3" column="0">
<item row="2" column="0">
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Bandwidth limit</string>
@ -322,7 +322,7 @@
</layout>
</widget>
</item>
<item row="2" column="0">
<item row="1" column="0">
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Proxy server / Tor</string>
@ -432,14 +432,7 @@
</layout>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="checkBoxUDP">
<property name="text">
<string>Announce self by UDP</string>
</property>
</widget>
</item>
<item row="4" column="0">
<item row="3" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>

View File

@ -1,11 +1,6 @@
"""bitmessageqt tests"""
from addressbook import TestAddressbook
from main import TestMain, TestUISignaler
from settings import TestSettings
from support import TestSupport
__all__ = [
"TestAddressbook", "TestMain", "TestSettings", "TestSupport",
"TestUISignaler"
]
__all__ = ["TestMain", "TestSupport", "TestUISignaler"]

View File

@ -1,17 +0,0 @@
import helper_addressbook
from bitmessageqt.support import createAddressIfNeeded
from main import TestBase
class TestAddressbook(TestBase):
"""Test case for addressbook"""
def test_add_own_address_to_addressbook(self):
"""Checking own address adding in addressbook"""
try:
address = createAddressIfNeeded(self.window)
self.assertFalse(
helper_addressbook.insert(label='test', address=address))
except IndexError:
self.fail("Can't generate addresses")

View File

@ -1,34 +0,0 @@
import threading
import time
from main import TestBase
from bmconfigparser import BMConfigParser
from bitmessageqt import settings
class TestSettings(TestBase):
"""A test case for the "Settings" dialog"""
def setUp(self):
super(TestSettings, self).setUp()
self.dialog = settings.SettingsDialog(self.window)
def test_udp(self):
"""Test the effect of checkBoxUDP"""
udp_setting = BMConfigParser().safeGetBoolean(
'bitmessagesettings', 'udp')
self.assertEqual(udp_setting, self.dialog.checkBoxUDP.isChecked())
self.dialog.checkBoxUDP.setChecked(not udp_setting)
self.dialog.accept()
self.assertEqual(
not udp_setting,
BMConfigParser().safeGetBoolean('bitmessagesettings', 'udp'))
time.sleep(5)
for thread in threading.enumerate():
if thread.name == 'Announcer': # find Announcer thread
if udp_setting:
self.fail(
'Announcer thread is running while udp set to False')
break
else:
if not udp_setting:
self.fail('No Announcer thread found while udp set to True')

View File

@ -2,22 +2,13 @@
BMConfigParser class definition and default configuration settings
"""
import sys
if sys.version_info[0] == 3:
# python 3
import configparser as ConfigParser
SafeConfigParser = ConfigParser.ConfigParser
else:
# python 2
import ConfigParser
SafeConfigParser = ConfigParser.SafeConfigParser
import state
from singleton import Singleton
import ConfigParser
import os
import shutil
from datetime import datetime
import state
from singleton import Singleton
BMConfigDefaults = {
"bitmessagesettings": {
@ -28,32 +19,30 @@ BMConfigDefaults = {
"maxtotalconnections": 200,
"maxuploadrate": 0,
"apiinterface": "127.0.0.1",
"apiport": 8442,
"udp": "True"
"apiport": 8442
},
"threads": {
"receive": 3,
},
"network": {
"bind": "",
"bind": '',
"dandelion": 90,
},
"inventory": {
"storage": "sqlite",
"acceptmismatch": "False",
"acceptmismatch": False,
},
"knownnodes": {
"maxnodes": 20000,
},
"zlib": {
"maxsize": 1048576
'maxsize': 1048576
}
}
@Singleton
class BMConfigParser(SafeConfigParser):
class BMConfigParser(ConfigParser.SafeConfigParser):
"""
Singleton class inherited from :class:`ConfigParser.SafeConfigParser`
with additional methods specific to bitmessage config.
@ -70,47 +59,26 @@ class BMConfigParser(SafeConfigParser):
raise ValueError("Invalid value %s" % value)
return ConfigParser.ConfigParser.set(self, section, option, value)
def get(self, section, option, raw=False, vars=None):
if sys.version_info[0] == 3:
# pylint: disable=arguments-differ
try:
if section == "bitmessagesettings" and option == "timeformat":
return ConfigParser.ConfigParser.get(
self, section, option)
try:
return self._temp[section][option]
except KeyError:
pass
def get(self, section, option, raw=False, variables=None):
# pylint: disable=arguments-differ
try:
if section == "bitmessagesettings" and option == "timeformat":
return ConfigParser.ConfigParser.get(
self, section, option)
except ConfigParser.InterpolationError:
return ConfigParser.ConfigParser.get(
self, section, option)
except (ConfigParser.NoSectionError, ConfigParser.NoOptionError) as e:
try:
return BMConfigDefaults[section][option]
except (KeyError, ValueError, AttributeError):
raise e
else:
# pylint: disable=arguments-differ
self, section, option, raw, variables)
try:
if section == "bitmessagesettings" and option == "timeformat":
return ConfigParser.ConfigParser.get(
self, section, option, raw, vars)
try:
return self._temp[section][option]
except KeyError:
pass
return ConfigParser.ConfigParser.get(
self, section, option, True, vars)
except ConfigParser.InterpolationError:
return ConfigParser.ConfigParser.get(
self, section, option, True, vars)
except (ConfigParser.NoSectionError, ConfigParser.NoOptionError) as e:
try:
return BMConfigDefaults[section][option]
except (KeyError, ValueError, AttributeError):
raise e
return self._temp[section][option]
except KeyError:
pass
return ConfigParser.ConfigParser.get(
self, section, option, True, variables)
except ConfigParser.InterpolationError:
return ConfigParser.ConfigParser.get(
self, section, option, True, variables)
except (ConfigParser.NoSectionError, ConfigParser.NoOptionError) as e:
try:
return BMConfigDefaults[section][option]
except (KeyError, ValueError, AttributeError):
raise e
def setTemp(self, section, option, value=None):
"""Temporary set option to value, not saving."""
@ -222,4 +190,3 @@ class BMConfigParser(SafeConfigParser):
if value < 0 or value > 8:
return False
return True

275
src/buildozer.spec Normal file
View File

@ -0,0 +1,275 @@
[app]
# (str) Title of your application
title = PyBitmessage
# (str) Package name
package.name = PyBitmessage
# (str) Package domain (needed for android/ios packaging)
package.domain = org.test
# (str) Source code where the main.py live
source.dir = .
# (list) Source files to include (let empty to include all the files)
source.include_exts = py,png,jpg,kv,atlas
# (list) List of inclusions using pattern matching
#source.include_patterns = assets/*,images/*.png
# (list) Source files to exclude (let empty to not exclude anything)
#source.exclude_exts = spec
# (list) List of directory to exclude (let empty to not exclude anything)
#source.exclude_dirs = tests, bin
# (list) List of exclusions using pattern matching
#source.exclude_patterns = license,images/*/*.jpg
# (str) Application versioning (method 1)
version = 0.1
# (str) Application versioning (method 2)
# version.regex = __version__ = ['"](.*)['"]
# version.filename = %(source.dir)s/main.py
# (list) Application requirements
# comma seperated e.g. requirements = sqlite3,kivy
requirements = python2, sqlite3, kivy, openssl
# (str) Custom source folders for requirements
# Sets custom source for any requirements with recipes
# requirements.source.kivy = ../../kivy
#requirements.source.sqlite3 =
# (list) Garden requirements
#garden_requirements =
# (str) Presplash of the application
#presplash.filename = %(source.dir)s/data/presplash.png
# (str) Icon of the application
#icon.filename = %(source.dir)s/data/icon.png
# (str) Supported orientation (one of landscape, portrait or all)
orientation = portrait
# (list) List of service to declare
#services = NAME:ENTRYPOINT_TO_PY,NAME2:ENTRYPOINT2_TO_PY
#
# OSX Specific
#
#
# author = © Copyright Info
# change the major version of python used by the app
#osx.python_version = 2
# Kivy version to use
osx.kivy_version = 1.9.1
#
# Android specific
#
# (bool) Indicate if the application should be fullscreen or not
fullscreen = 0
# (string) Presplash background color (for new android toolchain)
# Supported formats are: #RRGGBB #AARRGGBB or one of the following names:
# red, blue, green, black, white, gray, cyan, magenta, yellow, lightgray,
# darkgray, grey, lightgrey, darkgrey, aqua, fuchsia, lime, maroon, navy,
# olive, purple, silver, teal.
#android.presplash_color = #FFFFFF
# (list) Permissions
android.permissions = INTERNET
# (int) Android API to use
#android.api = 19
# (int) Minimum API required
#android.minapi = 9
# (int) Android SDK version to use
#android.sdk = 20
# (str) Android NDK version to use
#android.ndk = 9c
# (bool) Use --private data storage (True) or --dir public storage (False)
#android.private_storage = True
# (str) Android NDK directory (if empty, it will be automatically downloaded.)
#android.ndk_path =
# (str) Android SDK directory (if empty, it will be automatically downloaded.)
#android.sdk_path =
# (str) ANT directory (if empty, it will be automatically downloaded.)
#android.ant_path =
# (bool) If True, then skip trying to update the Android sdk
# This can be useful to avoid excess Internet downloads or save time
# when an update is due and you just want to test/build your package
# android.skip_update = False
# (str) Android entry point, default is ok for Kivy-based app
#android.entrypoint = org.renpy.android.PythonActivity
# (list) Pattern to whitelist for the whole project
#android.whitelist =
android.whitelist = /usr/lib/komodo-edit/python/lib/python2.7/lib-dynload/_sqlite3.so
# (str) Path to a custom whitelist file
#android.whitelist_src =
# (str) Path to a custom blacklist file
#android.blacklist_src =
# (list) List of Java .jar files to add to the libs so that pyjnius can access
# their classes. Don't add jars that you do not need, since extra jars can slow
# down the build process. Allows wildcards matching, for example:
# OUYA-ODK/libs/*.jar
#android.add_jars = foo.jar,bar.jar,path/to/more/*.jar
# (list) List of Java files to add to the android project (can be java or a
# directory containing the files)
#android.add_src =
# (list) Android AAR archives to add (currently works only with sdl2_gradle
# bootstrap)
#android.add_aars =
# (list) Gradle dependencies to add (currently works only with sdl2_gradle
# bootstrap)
#android.gradle_dependencies =
, /home/cis/Downloads/libssl1.0.2_1.0.2l-2+deb9u2_amd64
# (str) python-for-android branch to use, defaults to stable
#p4a.branch = stable
# (str) OUYA Console category. Should be one of GAME or APP
# If you leave this blank, OUYA support will not be enabled
#android.ouya.category = GAME
# (str) Filename of OUYA Console icon. It must be a 732x412 png image.
#android.ouya.icon.filename = %(source.dir)s/data/ouya_icon.png
# (str) XML file to include as an intent filters in <activity> tag
#android.manifest.intent_filters =
# (list) Android additionnal libraries to copy into libs/armeabi
#android.add_libs_armeabi = libs/android/*.so
#android.add_libs_armeabi_v7a = libs/android-v7/*.so
#android.add_libs_x86 = libs/android-x86/*.so
#android.add_libs_mips = libs/android-mips/*.so
# (bool) Indicate whether the screen should stay on
# Don't forget to add the WAKE_LOCK permission if you set this to True
#android.wakelock = False
# (list) Android application meta-data to set (key=value format)
#android.meta_data =
# (list) Android library project to add (will be added in the
# project.properties automatically.)
#android.library_references =
# (str) Android logcat filters to use
#android.logcat_filters = *:S python:D
# (bool) Copy library instead of making a libpymodules.so
#android.copy_libs = 1
# (str) The Android arch to build for, choices: armeabi-v7a, arm64-v8a, x86
android.arch = armeabi-v7a
#
# Python for android (p4a) specific
#
# (str) python-for-android git clone directory (if empty, it will be automatically cloned from github)
#p4a.source_dir =
# (str) The directory in which python-for-android should look for your own build recipes (if any)
#p4a.local_recipes =
# (str) Filename to the hook for p4a
#p4a.hook =
# (str) Bootstrap to use for android builds
# p4a.bootstrap = sdl2
#
# iOS specific
#
# (str) Path to a custom kivy-ios folder
#ios.kivy_ios_dir = ../kivy-ios
# (str) Name of the certificate to use for signing the debug version
# Get a list of available identities: buildozer ios list_identities
#ios.codesign.debug = "iPhone Developer: <lastname> <firstname> (<hexstring>)"
# (str) Name of the certificate to use for signing the release version
#ios.codesign.release = %(ios.codesign.debug)s
[buildozer]
# (int) Log level (0 = error only, 1 = info, 2 = debug (with command output))
log_level = 2
# (int) Display warning if buildozer is run as root (0 = False, 1 = True)
warn_on_root = 1
# (str) Path to build artifact storage, absolute or relative to spec file
# build_dir = ./.buildozer
# (str) Path to build output (i.e. .apk, .ipa) storage
# bin_dir = ./bin
# -----------------------------------------------------------------------------
# List as sections
#
# You can define all the "list" as [section:key].
# Each line will be considered as a option to the list.
# Let's take [app] / source.exclude_patterns.
# Instead of doing:
#
#[app]
#source.exclude_patterns = license,data/audio/*.wav,data/images/original/*
#
# This can be translated into:
#
#[app:source.exclude_patterns]
#license
#data/audio/*.wav
#data/images/original/*
#
# -----------------------------------------------------------------------------
# Profiles
#
# You can extend section / key with a profile
# For example, you want to deploy a demo version of your application without
# HD content. You could first change the title to add "(demo)" in the name
# and extend the excluded directories to remove the HD content.
#
#[app@demo]
#title = My Application (demo)
#
#[app:source.exclude_patterns@demo]
#images/hd/*
#
# Then, invoke the command line with the "demo" profile:
#
#buildozer --profile demo android debug

View File

@ -421,8 +421,8 @@ def check_dependencies(verbose=False, optional=False):
if sys.hexversion >= 0x3000000:
logger.error(
'PyBitmessage does not support Python 3+. Python 2.7.4'
' or greater is required. Python 2.7.18 is recommended.')
sys.exit()
' or greater is required.')
has_all_dependencies = False
check_functions = [check_ripemd160, check_sqlite, check_openssl]
if optional:

View File

@ -0,0 +1,35 @@
"""
Create random address
"""
import time
import defaults
import queues
import state
from bmconfigparser import BMConfigParser
def checkHasNormalAddress():
"""method for checking address"""
for address in BMConfigParser().addresses():
if BMConfigParser().safeGetBoolean(address, 'enabled'):
return address
return False
def createAddressIfNeeded(label_text, streamNumberForAddress=1):
"""method for creating random address"""
if not checkHasNormalAddress():
queues.addressGeneratorQueue.put((
'createRandomAddress', 4, streamNumberForAddress,
label_text,
1, "", False,
defaults.networkDefaultProofOfWorkNonceTrialsPerByte,
defaults.networkDefaultPayloadLengthExtraBytes
))
start_time = time.time()
while int(time.time() - start_time) < 10 and state.shutdown == 0 and not checkHasNormalAddress():
time.sleep(.2)
return checkHasNormalAddress()

View File

@ -31,7 +31,6 @@ class AdvancedDispatcher(asyncore.dispatcher):
def __init__(self, sock=None):
if not hasattr(self, '_map'):
asyncore.dispatcher.__init__(self, sock)
self.close_reason = None
self.read_buf = bytearray()
self.write_buf = bytearray()
self.state = "init"

View File

@ -7,6 +7,7 @@ import state
from bmconfigparser import BMConfigParser
from network.assemble import assemble_addr
from network.connectionpool import BMConnectionPool
from network.udp import UDPSocket
from node import Peer
from threads import StoppableThread
@ -14,13 +15,12 @@ from threads import StoppableThread
class AnnounceThread(StoppableThread):
"""A thread to manage regular announcing of this node"""
name = "Announcer"
announceInterval = 60
def run(self):
lastSelfAnnounced = 0
while not self._stopped and state.shutdown == 0:
processed = 0
if lastSelfAnnounced < time.time() - self.announceInterval:
if lastSelfAnnounced < time.time() - UDPSocket.announceInterval:
self.announceSelf()
lastSelfAnnounced = time.time()
if processed == 0:

View File

@ -749,7 +749,7 @@ class dispatcher(object):
def log_info(self, message, log_type='info'):
"""Conditionally print a message"""
if log_type not in self.ignore_log_types:
print('%s: %s' % (log_type, message))
print '%s: %s' % (log_type, message)
def handle_read_event(self):
"""Handle a read event"""

View File

@ -437,14 +437,9 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
# pylint: disable=redefined-outer-name
addresses = self._decode_addr()
for seenTime, stream, _, ip, port in addresses:
ip = str(ip)
if (
stream not in state.streamsInWhichIAmParticipating
# FIXME: should check against complete list
or ip.startswith('bootstrap')
):
decodedIP = protocol.checkIPAddress(str(ip))
if stream not in state.streamsInWhichIAmParticipating:
continue
decodedIP = protocol.checkIPAddress(ip)
if (
decodedIP
and time.time() - seenTime > 0

View File

@ -18,19 +18,19 @@ class HttpConnection(AdvancedDispatcher):
self.destination = (host, 80)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.connect(self.destination)
print("connecting in background to %s:%i" % self.destination)
print "connecting in background to %s:%i" % (self.destination[0], self.destination[1])
def state_init(self):
self.append_write_buf(
"GET %s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n" % (
self.path, self.destination[0]))
print("Sending %ib" % len(self.write_buf))
print "Sending %ib" % (len(self.write_buf))
self.set_state("http_request_sent", 0)
return False
def state_http_request_sent(self):
if self.read_buf:
print("Received %ib" % len(self.read_buf))
print "Received %ib" % (len(self.read_buf))
self.read_buf = b""
if not self.connected:
self.set_state("close", 0)
@ -62,13 +62,13 @@ if __name__ == "__main__":
for host in ("bootstrap8080.bitmessage.org", "bootstrap8444.bitmessage.org"):
proxy = Socks5Resolver(host=host)
while asyncore.socket_map:
print("loop %s, len %i" % (proxy.state, len(asyncore.socket_map)))
print "loop %s, len %i" % (proxy.state, len(asyncore.socket_map))
asyncore.loop(timeout=1, count=1)
proxy.resolved()
proxy = Socks4aResolver(host=host)
while asyncore.socket_map:
print("loop %s, len %i" % (proxy.state, len(asyncore.socket_map)))
print "loop %s, len %i" % (proxy.state, len(asyncore.socket_map))
asyncore.loop(timeout=1, count=1)
proxy.resolved()

View File

@ -1,8 +1,6 @@
"""
Manipulations with knownNodes dictionary.
"""
# TODO: knownnodes object maybe?
# pylint: disable=global-statement
import json
import logging
@ -34,6 +32,9 @@ knownNodesForgetRating = -0.5
knownNodesActual = False
outages = {}
"""a dict with all hosts"""
logger = logging.getLogger('default')
DEFAULT_NODES = (
@ -67,11 +68,17 @@ def json_deserialize_knownnodes(source):
"""
Read JSON from source and make knownnodes dict
"""
global knownNodesActual
global knownNodesActual # pylint: disable=global-statement
for node in json.load(source):
peer = node['peer']
info = node['info']
peer = Peer(str(peer['host']), peer.get('port', 8444))
port = peer.get('port', 8444)
peer = Peer(str(peer['host']), port)
outages[peer.host] = {
'lastseen': info.get('lastseen', time.time()),
'port': port,
'stream': info.get('stream', 1)
}
knownNodes[node['stream']][peer] = info
if not (knownNodesActual
or info.get('self')) and peer not in DEFAULT_NODES:
@ -84,7 +91,7 @@ def pickle_deserialize_old_knownnodes(source):
the old format was {Peer:lastseen, ...}
the new format is {Peer:{"lastseen":i, "rating":f}}
"""
global knownNodes
global knownNodes # pylint: disable=global-statement
knownNodes = pickle.load(source)
for stream in knownNodes.keys():
for node, params in knownNodes[stream].iteritems():
@ -121,11 +128,18 @@ def addKnownNode(stream, peer, lastseen=None, is_self=False):
else:
lastseen = int(lastseen)
try:
prev = outages[peer.host]
if peer.port == prev['port']:
outages[peer.host]['lastseen'] = lastseen
info = knownNodes[stream].get(peer)
if lastseen > info['lastseen']:
info['lastseen'] = lastseen
except (KeyError, TypeError):
info['lastseen'] = lastseen
except KeyError:
pass
except TypeError:
# don't update expired node if have enough nodes in that stream
if len(knownNodes[stream]) > 64:
return
rating = -0.2
else:
return
@ -133,6 +147,25 @@ def addKnownNode(stream, peer, lastseen=None, is_self=False):
if len(knownNodes[stream]) > BMConfigParser().safeGetInt(
"knownnodes", "maxnodes"):
return
try:
prev = outages[peer.host]
except KeyError:
outages[peer.host] = {
'stream': stream,
'port': peer.port,
'lastseen': lastseen
}
else:
if stream == prev['stream']:
if lastseen - prev['lastseen'] > 3600 * 24:
# more than a day ago, this should be port change
try:
del knownNodes[stream][Peer(peer.host, prev['port'])]
except KeyError:
pass
outages[peer.host]['port'] = peer.port
else:
rating = -0.2
knownNodes[stream][peer] = {
'lastseen': lastseen,
@ -232,7 +265,6 @@ def cleanupKnownNodes():
"""
Cleanup knownnodes: remove old nodes and nodes with low rating
"""
global knownNodesActual
now = int(time.time())
needToWriteKnownNodesToDisk = False
@ -243,8 +275,6 @@ def cleanupKnownNodes():
keys = knownNodes[stream].keys()
for node in keys:
if len(knownNodes[stream]) <= 1: # leave at least one node
if stream == 1:
knownNodesActual = False
break
try:
age = now - knownNodes[stream][node]["lastseen"]

View File

@ -366,21 +366,16 @@ def bootstrap(connection_class):
"""
BMProto.bm_command_addr(self)
self._succeed = True
# pylint: disable=attribute-defined-outside-init
self.close_reason = "Thanks for bootstrapping!"
self.set_state("close")
def set_connection_fully_established(self):
"""Only send addr here"""
# pylint: disable=attribute-defined-outside-init
self.fullyEstablished = True
self.sendAddr()
def handle_close(self):
"""
After closing the connection switch knownnodes.knownNodesActual
back to False if the bootstrapper failed.
"""
BMProto.handle_close(self)
self._connection_base.handle_close(self)
if not self._succeed:
knownnodes.knownNodesActual = False

View File

@ -154,13 +154,12 @@ class TLSDispatcher(AdvancedDispatcher):
except AttributeError:
return AdvancedDispatcher.handle_read(self)
except ssl.SSLError as err:
self.close_reason = "SSL Error in handle_read"
if err.errno == ssl.SSL_ERROR_WANT_READ:
return
elif err.errno in _DISCONNECTED_SSL:
self.handle_close()
return
logger.info("SSL Error: %s", err)
logger.info("SSL Error: %s", str(err))
self.handle_close()
return
@ -185,13 +184,12 @@ class TLSDispatcher(AdvancedDispatcher):
except AttributeError:
return AdvancedDispatcher.handle_write(self)
except ssl.SSLError as err:
self.close_reason = "SSL Error in handle_write"
if err.errno == ssl.SSL_ERROR_WANT_WRITE:
return 0
elif err.errno in _DISCONNECTED_SSL:
self.handle_close()
return 0
logger.info("SSL Error: %s", err)
logger.info("SSL Error: %s", str(err))
self.handle_close()
return
@ -205,8 +203,8 @@ class TLSDispatcher(AdvancedDispatcher):
logger.debug("handshaking (internal)")
self.sslSocket.do_handshake()
except ssl.SSLError as err:
self.close_reason = "SSL Error in tls_handshake"
logger.info("%s:%i: handshake fail", *self.destination)
logger.debug(
'%s:%i: handshake fail', self.destination.host, self.destination.port)
self.want_read = self.want_write = False
if err.args[0] == ssl.SSL_ERROR_WANT_READ:
logger.debug("want read")
@ -219,7 +217,6 @@ class TLSDispatcher(AdvancedDispatcher):
except socket.error as err:
# pylint: disable=protected-access
if err.errno in asyncore._DISCONNECTED:
self.close_reason = "socket.error in tls_handshake"
self.handle_close()
else:
raise

View File

@ -8,7 +8,6 @@ import time
import protocol
import state
from bmproto import BMProto
from constants import MAX_TIME_OFFSET
from node import Peer
from objectracker import ObjectTracker
from queues import receiveDataQueue
@ -19,6 +18,7 @@ logger = logging.getLogger('default')
class UDPSocket(BMProto): # pylint: disable=too-many-instance-attributes
"""Bitmessage protocol over UDP (class)"""
port = 8444
announceInterval = 60
def __init__(self, host=None, sock=None, announcing=False):
# pylint: disable=bad-super-call
@ -82,8 +82,8 @@ class UDPSocket(BMProto): # pylint: disable=too-many-instance-attributes
decodedIP = protocol.checkIPAddress(str(ip))
if stream not in state.streamsInWhichIAmParticipating:
continue
if (seenTime < time.time() - MAX_TIME_OFFSET
or seenTime > time.time() + MAX_TIME_OFFSET):
if (seenTime < time.time() - self.maxTimeOffset
or seenTime > time.time() + self.maxTimeOffset):
continue
if decodedIP is False:
# if the address isn't local, interpret it as
@ -94,8 +94,9 @@ class UDPSocket(BMProto): # pylint: disable=too-many-instance-attributes
logger.debug(
"received peer discovery from %s:%i (port %i):",
self.destination.host, self.destination.port, remoteport)
state.discoveredPeers[Peer(self.destination.host, remoteport)] = \
time.time()
if self.local:
state.discoveredPeers[Peer(self.destination.host, remoteport)] = \
time.time()
return True
def bm_command_portcheck(self):
@ -124,9 +125,9 @@ class UDPSocket(BMProto): # pylint: disable=too-many-instance-attributes
def handle_read(self):
try:
recdata, addr = self.socket.recvfrom(self._buf_len)
except socket.error:
logger.error("socket error on recvfrom:", exc_info=True)
(recdata, addr) = self.socket.recvfrom(self._buf_len)
except socket.error as e:
logger.error("socket error: %s", e)
return
self.destination = Peer(*addr)
@ -142,7 +143,7 @@ class UDPSocket(BMProto): # pylint: disable=too-many-instance-attributes
try:
retval = self.socket.sendto(
self.write_buf, ('<broadcast>', self.port))
except socket.error:
logger.error("socket error on sendto:", exc_info=True)
except socket.error as e:
logger.error("socket error on sendto: %s", e)
retval = len(self.write_buf)
self.slice_write_buf(retval)

View File

@ -1,24 +1,16 @@
#!/usr/bin/env python2.7
"""
Module for Proof of Work using OpenCL
"""
import logging
import os
from struct import pack
import paths
from bmconfigparser import BMConfigParser
from debug import logger
from state import shutdown
try:
import numpy
import pyopencl as cl
libAvailable = True
except ImportError:
libAvailable = False
logger = logging.getLogger('default')
libAvailable = True
ctx = False
queue = False
program = False
@ -27,10 +19,17 @@ enabledGpus = []
vendors = []
hash_dt = None
try:
import pyopencl as cl
import numpy
except ImportError:
libAvailable = False
def initCL():
"""Initlialise OpenCL engine"""
global ctx, queue, program, hash_dt # pylint: disable=global-statement
# pylint: disable=global-statement
global ctx, queue, program, hash_dt, libAvailable
if libAvailable is False:
return
del enabledGpus[:]

View File

@ -1,10 +0,0 @@
import os
import sys
def setup():
"""Add path to this file to sys.path"""
app_dir = os.path.dirname(os.path.abspath(__file__))
os.chdir(app_dir)
sys.path.insert(0, app_dir)
return app_dir

View File

@ -1,31 +0,0 @@
# -*- coding: utf-8 -*-
import os
from xdg import BaseDirectory, Menu
class DesktopXDG(object):
"""pyxdg Freedesktop desktop implementation"""
def __init__(self):
menu_entry = Menu.parse().getMenu('Office').getMenuEntry(
'pybitmessage.desktop')
self.desktop = menu_entry.DesktopEntry if menu_entry else None
def adjust_startonlogon(self, autostart=False):
"""Configure autostart according to settings"""
if not self.desktop:
return
autostart_path = os.path.join(
BaseDirectory.xdg_config_home, 'autostart', 'pybitmessage.desktop')
if autostart:
self.desktop.write(autostart_path)
else:
try:
os.remove(autostart_path)
except OSError:
pass
connect_plugin = DesktopXDG

View File

@ -16,7 +16,7 @@ def inv(a, n):
lm, hm = 1, 0
low, high = a % n, n
while low > 1:
r = high // low
r = high / low
nm, new = hm - lm * r, high - low * r
lm, low, hm, high = nm, new, lm, low
return lm % n
@ -43,8 +43,8 @@ def encode(val, base, minlen=0):
code_string = get_code_string(base)
result = ""
while val > 0:
val, i = divmod(val, base)
result = code_string[i] + result
result = code_string[val % base] + result
val /= base
if len(result) < minlen:
result = code_string[0] * (minlen - len(result)) + result
return result
@ -101,11 +101,10 @@ def base10_multiply(a, n):
return G
if n == 1:
return a
n, m = divmod(n, 2)
if m == 0:
return base10_double(base10_multiply(a, n))
if m == 1:
return base10_add(base10_double(base10_multiply(a, n)), a)
if (n % 2) == 0:
return base10_double(base10_multiply(a, n / 2))
if (n % 2) == 1:
return base10_add(base10_double(base10_multiply(a, n / 2)), a)
return None

View File

@ -1,10 +1,12 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Symmetric Encryption
"""
# Copyright (C) 2011 Yann GUIBET <yannguibet@gmail.com>
# See LICENSE for details.
from .openssl import OpenSSL
from openssl import OpenSSL
# pylint: disable=redefined-builtin

View File

@ -1,3 +1,5 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Asymmetric cryptography using elliptic curves
"""
@ -8,9 +10,9 @@ Asymmetric cryptography using elliptic curves
from hashlib import sha512
from struct import pack, unpack
from .cipher import Cipher
from .hash import equals, hmac_sha256
from .openssl import OpenSSL
from cipher import Cipher
from hash import equals, hmac_sha256
from openssl import OpenSSL
class ECC(object):

View File

@ -1,3 +1,4 @@
#!/usr/bin/env python
"""
ECC blind signature functionality based on
"An Efficient Blind Signature Scheme
@ -150,7 +151,7 @@ class ECCBlind(object): # pylint: disable=too-many-instance-attributes
# padding manually
bx = OpenSSL.malloc(0, OpenSSL.BN_num_bytes(x))
OpenSSL.BN_bn2bin(x, bx)
out = bx.raw.rjust(l_, b'\x00')
out = bx.raw.rjust(l_, chr(0))
return pack(EC, y_byte, out)
finally:
@ -180,7 +181,7 @@ class ECCBlind(object): # pylint: disable=too-many-instance-attributes
except AttributeError:
o = OpenSSL.malloc(0, OpenSSL.BN_num_bytes(bn))
OpenSSL.BN_bn2bin(bn, o)
return o.raw.rjust(l_, b'\x00')
return o.raw.rjust(l_, chr(0))
def _bn_deserialize(self, data):
"""Make a BigNum out of string"""

View File

@ -4,7 +4,7 @@ Wrappers for hash functions from OpenSSL.
# Copyright (C) 2011 Yann GUIBET <yannguibet@gmail.com>
# See LICENSE for details.
from .openssl import OpenSSL
from openssl import OpenSSL
# For python3

View File

@ -83,7 +83,7 @@ class _OpenSSL(object):
"""
self._lib = ctypes.CDLL(library)
self._version, self._hexversion, self._cflags = get_version(self._lib)
self._libreSSL = self._version.startswith(b"LibreSSL")
self._libreSSL = self._version.startswith("LibreSSL")
self.pointer = ctypes.pointer
self.c_int = ctypes.c_int

View File

@ -8,8 +8,7 @@ import state
from debug import logger
from helper_sql import sqlQuery, sqlStoredProcedure
from inventory import Inventory
from network import StoppableThread
from network.knownnodes import saveKnownNodes
from network import knownnodes, StoppableThread
from queues import (
addressGeneratorQueue, objectProcessorQueue, UISignalQueue, workerQueue)
@ -29,7 +28,7 @@ def doCleanShutdown():
'updateStatusBar',
'Saving the knownNodes list of peers to disk...'))
logger.info('Saving knownNodes list of peers to disk')
saveKnownNodes()
knownnodes.saveKnownNodes()
logger.info('Done saving knownNodes list of peers to disk')
UISignalQueue.put((
'updateStatusBar',
@ -79,6 +78,37 @@ def doCleanShutdown():
except Queue.Empty:
break
# Log knownnodes/outages/multiport statistics
dupes = {}
nodes_counter = 0
for stream in knownnodes.knownNodes.itervalues():
for peer in stream:
nodes_counter += 1
dup = dupes.get(peer.host)
port = str(peer.port)
if dup:
dupes[peer.host].append(port)
else:
dupes[peer.host] = [port]
dup_counter = 0
max_ports = (1, '')
for dup, ports in dupes.iteritems():
ports_len = len(ports)
if ports_len > 1:
dup_counter += 1
if ports_len > max_ports[0]:
max_ports = (ports_len, dup)
logger.warning(
'Multiport host: %s, ports: %s',
dup, ', '.join(ports))
logger.warning(
'Knownnodes len: %s, outages: %s, multiport: %s',
nodes_counter, len(knownnodes.outages), dup_counter)
if max_ports[0] > 0:
logger.warning(
'Maximum number of ports %s - %s', *max_ports)
if state.thisapp.daemon or not state.enableGUI:
logger.info('Clean shutdown complete.')
state.thisapp.cleanup()

View File

@ -51,6 +51,10 @@ dandelion = 0
testmode = False
kivy = False
association = ''
clientHasReceivedIncomingConnections = False
"""used by API command clientStatus"""

View File

@ -7,7 +7,7 @@ when pybitmessage started in test mode.
import sys
import tempfile
from common import put_signal_file
from test_process import put_signal_file
if __name__ == '__main__':

View File

@ -1,7 +1,4 @@
import os
import sys
import time
import unittest
_files = (
@ -20,15 +17,3 @@ def cleanup(home=None, files=_files):
os.remove(os.path.join(home, pfile))
except OSError:
pass
def skip_python3():
"""Raise unittest.SkipTest() if detected python3"""
if sys.hexversion >= 0x3000000:
raise unittest.SkipTest('Module is not ported to python3')
def put_signal_file(path, filename):
"""Creates file, presence of which is a signal about some event."""
with open(os.path.join(path, filename), 'wb') as outfile:
outfile.write(b'%i' % time.time())

View File

@ -8,10 +8,8 @@ import pickle # nosec
import Queue
import random # nosec
import shutil
import socket
import string
import sys
import threading
import time
import unittest
@ -22,6 +20,7 @@ import helper_addressbook
from bmconfigparser import BMConfigParser
from helper_msgcoding import MsgEncode, MsgDecode
from helper_startup import start_proxyconfig
from helper_sql import sqlQuery
from network import asyncore_pollchoose as asyncore, knownnodes
from network.bmproto import BMProto
@ -34,10 +33,9 @@ from version import softwareVersion
from common import cleanup
try:
socket.socket().bind(('127.0.0.1', 9050))
tor_port_free = True
except (OSError, socket.error):
tor_port_free = False
import stem.version as stem_version
except ImportError:
stem_version = None
knownnodes_file = os.path.join(state.appdata, 'knownnodes.dat')
@ -62,13 +60,6 @@ class TestCore(unittest.TestCase):
"""Test case, which runs in main pybitmessage thread"""
addr = 'BM-2cVvkzJuQDsQHLqxRXc6HZGPLZnkBLzEZY'
def tearDown(self):
"""Reset possible unexpected settings after test"""
knownnodes.addKnownNode(1, Peer('127.0.0.1', 8444), is_self=True)
BMConfigParser().remove_option('bitmessagesettings', 'dontconnect')
BMConfigParser().remove_option('bitmessagesettings', 'onionservicesonly')
BMConfigParser().set('bitmessagesettings', 'socksproxytype', 'none')
def test_msgcoding(self):
"""test encoding and decoding (originally from helper_msgcoding)"""
msg_data = {
@ -167,17 +158,12 @@ class TestCore(unittest.TestCase):
def _initiate_bootstrap(self):
BMConfigParser().set('bitmessagesettings', 'dontconnect', 'true')
self._wipe_knownnodes()
self._outdate_knownnodes()
knownnodes.addKnownNode(1, Peer('127.0.0.1', 8444), is_self=True)
knownnodes.cleanupKnownNodes()
time.sleep(5)
time.sleep(2)
def _check_connection(self, full=False):
"""
Check if there is at least one outbound connection to remote host
with name not starting with "bootstrap" in 6 minutes at most,
fail otherwise.
"""
def _check_bootstrap(self):
_started = time.time()
BMConfigParser().remove_option('bitmessagesettings', 'dontconnect')
proxy_type = BMConfigParser().safeGet(
@ -188,126 +174,53 @@ class TestCore(unittest.TestCase):
connection_base = Socks4aBMConnection
else:
connection_base = TCPConnection
c = 360
while c > 0:
for _ in range(180):
time.sleep(1)
c -= 2
for peer, con in BMConnectionPool().outboundConnections.iteritems():
if (
peer.host.startswith('bootstrap')
or peer.host == 'quzwelsuziwqgpt2.onion'
):
if c < 60:
self.fail(
'Still connected to bootstrap node %s after % seconds' %
(peer, time.time() - _started))
c += 1
break
else:
if not peer.host.startswith('bootstrap'):
self.assertIsInstance(con, connection_base)
self.assertNotEqual(peer.host, '127.0.0.1')
if full and not con.fullyEstablished:
continue
return
self.fail(
'Failed to connect during %s sec' % (time.time() - _started))
def _check_knownnodes(self):
for stream in knownnodes.knownNodes.itervalues():
for peer in stream:
if peer.host.startswith('bootstrap'):
self.fail(
'Bootstrap server in knownnodes: %s' % peer.host)
def test_dontconnect(self):
"""all connections are closed 5 seconds after setting dontconnect"""
self._initiate_bootstrap()
self.assertEqual(len(BMConnectionPool().connections()), 0)
def test_connection(self):
"""test connection to bootstrap servers"""
self._initiate_bootstrap()
for port in [8080, 8444]:
for item in socket.getaddrinfo(
'bootstrap%s.bitmessage.org' % port, 80):
try:
addr = item[4][0]
socket.inet_aton(item[4][0])
except (TypeError, socket.error):
continue
else:
knownnodes.addKnownNode(1, Peer(addr, port))
self._check_connection(True)
def test_bootstrap(self):
"""test bootstrapping"""
BMConfigParser().set('bitmessagesettings', 'socksproxytype', 'none')
self._initiate_bootstrap()
self._check_connection()
self._check_knownnodes()
self._check_bootstrap()
@unittest.skipIf(tor_port_free, 'no running tor detected')
@unittest.skipUnless(stem_version, 'No stem, skipping tor dependent test')
def test_bootstrap_tor(self):
"""test bootstrapping with tor"""
BMConfigParser().set('bitmessagesettings', 'socksproxytype', 'SOCKS5')
self._initiate_bootstrap()
self._check_connection()
self._check_knownnodes()
BMConfigParser().set('bitmessagesettings', 'socksproxytype', 'stem')
start_proxyconfig()
self._check_bootstrap()
@unittest.skipIf(tor_port_free, 'no running tor detected')
def test_onionservicesonly(self):
"""ensure bitmessage doesn't try to connect to non-onion nodes
if onionservicesonly set, wait at least 3 onion nodes
@unittest.skipUnless(stem_version, 'No stem, skipping tor dependent test')
def test_onionservicesonly(self): # this should start after bootstrap
"""
set onionservicesonly, wait for 3 connections and check them all
are onions
"""
BMConfigParser().set('bitmessagesettings', 'socksproxytype', 'SOCKS5')
BMConfigParser().set('bitmessagesettings', 'onionservicesonly', 'true')
self._initiate_bootstrap()
BMConfigParser().remove_option('bitmessagesettings', 'dontconnect')
tried_hosts = set()
for _ in range(360):
time.sleep(1)
for peer in BMConnectionPool().outboundConnections:
if peer.host.endswith('.onion'):
tried_hosts.add(peer.host)
else:
if not peer.host.startswith('bootstrap'):
self.fail(
'Found non onion hostname %s in outbound'
'connections!' % peer.host)
if len(tried_hosts) > 2:
for n, peer in enumerate(BMConnectionPool().outboundConnections):
if n > 2:
return
if (
not peer.host.endswith('.onion')
and not peer.host.startswith('bootstrap')
):
self.fail(
'Found non onion hostname %s in outbound connections!'
% peer.host)
self.fail('Failed to connect to at least 3 nodes within 360 sec')
def test_udp(self):
"""check default udp setting and presence of Announcer thread"""
self.assertTrue(
BMConfigParser().safeGetBoolean('bitmessagesettings', 'udp'))
for thread in threading.enumerate():
if thread.name == 'Announcer': # find Announcer thread
break
else:
return self.fail('No Announcer thread found')
for _ in range(20): # wait for UDP socket
for sock in BMConnectionPool().udpSockets.values():
thread.announceSelf()
break
else:
time.sleep(1)
continue
break
else:
self.fail('UDP socket is not started')
for _ in range(20):
if state.discoveredPeers:
peer = state.discoveredPeers.keys()[0]
self.assertEqual(peer.port, 8444)
break
time.sleep(1)
else:
self.fail('No self in discovered peers')
@staticmethod
def _decode_msg(data, pattern):
proto = BMProto()
@ -381,6 +294,15 @@ class TestCore(unittest.TestCase):
self.assertEqual(queryreturn[0][0], 1)
self.delete_address_from_addressbook(self.addr)
def test_is_own_address_add_to_addressbook(self):
"""Checking own address adding in addressbook"""
from helper_addressGenerator import createAddressIfNeeded
try:
address = createAddressIfNeeded('test1')
self.assertFalse(helper_addressbook.insert(label='test', address=address))
except IndexError:
self.fail("Can't generate addresses")
def test_adding_two_same_case_sensitive_addresses(self):
"""Testing same case sensitive address store in addressbook"""
address1 = 'BM-2cVWtdUzPwF7UNGDrZftWuHWiJ6xxBpiSP'

View File

@ -5,13 +5,9 @@ Tests using API.
import base64
import json
import time
import xmlrpclib # nosec
try: # nosec
from xmlrpclib import ServerProxy, ProtocolError
except ImportError:
from xmlrpc.client import ServerProxy, ProtocolError
from .test_process import TestProcessProto, TestProcessShutdown
from test_process import TestProcessProto, TestProcessShutdown
class TestAPIProto(TestProcessProto):
@ -23,7 +19,7 @@ class TestAPIProto(TestProcessProto):
"""Setup XMLRPC proxy for pybitmessage API"""
super(TestAPIProto, cls).setUpClass()
cls.addresses = []
cls.api = ServerProxy(
cls.api = xmlrpclib.ServerProxy(
"http://username:password@127.0.0.1:8442/")
for _ in range(5):
if cls._get_readline('.api_started'):
@ -69,8 +65,8 @@ class TestAPI(TestAPIProto):
def test_user_password(self):
"""Trying to connect with wrong username/password"""
api_wrong = ServerProxy("http://test:wrong@127.0.0.1:8442/")
with self.assertRaises(ProtocolError):
api_wrong = xmlrpclib.ServerProxy("http://test:wrong@127.0.0.1:8442/")
with self.assertRaises(xmlrpclib.ProtocolError):
api_wrong.clientStatus()
def test_connection(self):

View File

@ -5,7 +5,9 @@ import os
import unittest
from hashlib import sha256
from pybitmessage.pyelliptic import ECCBlind, ECCBlindChain, OpenSSL
from pybitmessage.pyelliptic.eccblind import ECCBlind
from pybitmessage.pyelliptic.eccblindchain import ECCBlindChain
from pybitmessage.pyelliptic.openssl import OpenSSL
# pylint: disable=protected-access
@ -34,12 +36,12 @@ class TestBlindSig(unittest.TestCase):
# (3) Signature Generation
signature_blinded = signer_obj.blind_sign(msg_blinded)
assert isinstance(signature_blinded, bytes)
assert isinstance(signature_blinded, str)
self.assertEqual(len(signature_blinded), 32)
# (4) Extraction
signature = requester_obj.unblind(signature_blinded)
assert isinstance(signature, bytes)
assert isinstance(signature, str)
self.assertEqual(len(signature), 65)
self.assertNotEqual(signature, signature_blinded)
@ -161,7 +163,7 @@ class TestBlindSig(unittest.TestCase):
output.extend(pubkey)
output.extend(signature)
signer_obj = child_obj
verifychain = ECCBlindChain(ca=ca.pubkey(), chain=bytes(output))
verifychain = ECCBlindChain(ca=ca.pubkey(), chain=str(output))
self.assertTrue(verifychain.verify(msg=msg, value=1))
def test_blind_sig_chain_wrong_ca(self): # pylint: disable=too-many-locals

View File

@ -2,8 +2,12 @@
Various tests for config
"""
import os
import unittest
import tempfile
from pybitmessage.bmconfigparser import BMConfigParser
from test_process import TestProcessProto
class TestConfig(unittest.TestCase):
@ -34,3 +38,32 @@ class TestConfig(unittest.TestCase):
BMConfigParser().safeGetInt('nonexistent', 'nonexistent'), 0)
self.assertEqual(
BMConfigParser().safeGetInt('nonexistent', 'nonexistent', 42), 42)
class TestProcessConfig(TestProcessProto):
"""A test case for keys.dat"""
home = tempfile.mkdtemp()
def test_config_defaults(self):
"""Test settings in the generated config"""
self._stop_process()
self._kill_process()
config = BMConfigParser()
config.read(os.path.join(self.home, 'keys.dat'))
self.assertEqual(config.safeGetInt(
'bitmessagesettings', 'settingsversion'), 10)
self.assertEqual(config.safeGetInt(
'bitmessagesettings', 'port'), 8444)
# don't connect
self.assertTrue(config.safeGetBoolean(
'bitmessagesettings', 'dontconnect'))
# API disabled
self.assertFalse(config.safeGetBoolean(
'bitmessagesettings', 'apienabled'))
# extralowdifficulty is false
self.assertEqual(config.safeGetInt(
'bitmessagesettings', 'defaultnoncetrialsperbyte'), 1000)
self.assertEqual(config.safeGetInt(
'bitmessagesettings', 'defaultpayloadlengthextrabytes'), 1000)

View File

@ -1,38 +0,0 @@
"""
Various tests for config
"""
import os
import tempfile
from pybitmessage.bmconfigparser import BMConfigParser
from .test_process import TestProcessProto
class TestProcessConfig(TestProcessProto):
"""A test case for keys.dat"""
home = tempfile.mkdtemp()
def test_config_defaults(self):
"""Test settings in the generated config"""
config = BMConfigParser()
self._stop_process()
self._kill_process()
config.read(os.path.join(self.home, 'keys.dat'))
self.assertEqual(config.safeGetInt(
'bitmessagesettings', 'settingsversion'), 10)
self.assertEqual(config.safeGetInt(
'bitmessagesettings', 'port'), 8444)
# don't connect
self.assertTrue(config.safeGetBoolean(
'bitmessagesettings', 'dontconnect'))
# API disabled
self.assertFalse(config.safeGetBoolean(
'bitmessagesettings', 'apienabled'))
# extralowdifficulty is false
self.assertEqual(config.safeGetInt(
'bitmessagesettings', 'defaultnoncetrialsperbyte'), 1000)
self.assertEqual(config.safeGetInt(
'bitmessagesettings', 'defaultpayloadlengthextrabytes'), 1000)

View File

@ -6,10 +6,8 @@ import hashlib
import unittest
from abc import ABCMeta, abstractmethod
from binascii import hexlify, unhexlify
from pybitmessage.pyelliptic import arithmetic
try:
from Crypto.Hash import RIPEMD
except ImportError:
@ -23,21 +21,14 @@ sample_pubsigningkey = unhexlify(
sample_pubencryptionkey = unhexlify(
'044597d59177fc1d89555d38915f581b5ff2286b39d022ca0283d2bdd5c36be5d3c'
'e7b9b97792327851a562752e4b79475d1f51f5a71352482b241227f45ed36a9')
sample_privsigningkey = \
sample_privatesigningkey = \
'93d0b61371a54b53df143b954035d612f8efa8a3ed1cf842c2186bfd8f876665'
sample_privencryptionkey = \
sample_privateencryptionkey = \
'4b0b73a54e19b059dc274ab69df095fe699f43b17397bca26fdf40f4d7400a3a'
sample_ripe = b'003cd097eb7f35c87b5dc8b4538c22cb55312a9f'
sample_ripe = '003cd097eb7f35c87b5dc8b4538c22cb55312a9f'
# stream: 1, version: 2
sample_address = 'BM-onkVu1KKL2UaUss5Upg9vXmqd3esTmV79'
sample_factor = 66858749573256452658262553961707680376751171096153613379801854825275240965733
# G * sample_factor
sample_point = (
33567437183004486938355437500683826356288335339807546987348409590129959362313,
94730058721143827257669456336351159718085716196507891067256111928318063085006
)
_sha = hashlib.new('sha512')
_sha.update(sample_pubsigningkey + sample_pubencryptionkey)
@ -78,20 +69,14 @@ class TestCrypto(RIPEMD160TestCase, unittest.TestCase):
class TestAddresses(unittest.TestCase):
"""Test addresses manipulations"""
def test_base10_multiply(self):
"""Test arithmetic.base10_multiply"""
self.assertEqual(
sample_point,
arithmetic.base10_multiply(arithmetic.G, sample_factor))
def test_privtopub(self):
"""Generate public keys and check the result"""
self.assertEqual(
arithmetic.privtopub(sample_privsigningkey).encode(),
arithmetic.privtopub(sample_privatesigningkey),
hexlify(sample_pubsigningkey)
)
self.assertEqual(
arithmetic.privtopub(sample_privencryptionkey).encode(),
arithmetic.privtopub(sample_privateencryptionkey),
hexlify(sample_pubencryptionkey)
)

View File

@ -5,7 +5,7 @@ Testing the logger configuration
import os
import tempfile
from .test_process import TestProcessProto
from test_process import TestProcessProto
class TestLogger(TestProcessProto):

View File

@ -3,10 +3,6 @@ Test for network group
"""
import unittest
from .common import skip_python3
skip_python3()
class TestNetworkGroup(unittest.TestCase):
"""

View File

@ -3,16 +3,15 @@ Tests for openclpow module
"""
import hashlib
import unittest
import pybitmessage.openclpow as openclpow
from struct import pack, unpack
from .common import skip_python3
skip_python3() # noqa:E402
from pybitmessage import openclpow
from test_process import TestProcessProto
class TestOpenClPow(unittest.TestCase):
class TestOpenClPow(TestProcessProto):
"""
Main opencl test case
"""
@ -20,17 +19,16 @@ class TestOpenClPow(unittest.TestCase):
@classmethod
def setUpClass(cls):
openclpow.initCL()
super(TestOpenClPow, cls).setUpClass()
@unittest.skipUnless(openclpow.enabledGpus, "No GPUs found / enabled")
@unittest.skipIf(not openclpow.enabledGpus,
"No GPUs found / enabled")
def test_openclpow(self):
"""Check the working of openclpow module"""
target_ = 54227212183
initialHash = (
"3758f55b5a8d902fd3597e4ce6a2d3f23daff735f65d9698c270987f4e67ad590"
"b93f3ffeba0ef2fd08a8dc2f87b68ae5a0dc819ab57f22ad2c4c9c8618a43b3"
).decode("hex")
initialHash = ("3758f55b5a8d902fd3597e4ce6a2d3f23daff735f65d9698c270987f4e67ad590"
"b93f3ffeba0ef2fd08a8dc2f87b68ae5a0dc819ab57f22ad2c4c9c8618a43b3").decode("hex")
nonce = openclpow.do_opencl_pow(initialHash.encode("hex"), target_)
trialValue, = unpack(
'>Q', hashlib.sha512(hashlib.sha512(
pack('>Q', nonce) + initialHash).digest()).digest()[0:8])
'>Q', hashlib.sha512(hashlib.sha512(pack('>Q', nonce) + initialHash).digest()).digest()[0:8])
self.assertLess((nonce - trialValue), target_)

View File

@ -49,6 +49,6 @@ class TestOpenSSL(unittest.TestCase):
c = OpenSSL.malloc(0, OpenSSL.BN_num_bytes(a))
OpenSSL.BN_bn2binpad(a, b, OpenSSL.BN_num_bytes(n))
OpenSSL.BN_bn2bin(a, c)
if b.raw != c.raw.rjust(OpenSSL.BN_num_bytes(n), b'\x00'):
if b.raw != c.raw.rjust(OpenSSL.BN_num_bytes(n), chr(0)):
bad += 1
self.assertEqual(bad, 0)

View File

@ -12,10 +12,13 @@ import unittest
import psutil
from .common import cleanup, put_signal_file, skip_python3
from common import cleanup
skip_python3()
def put_signal_file(path, filename):
"""Creates file, presence of which is a signal about some event."""
with open(os.path.join(path, filename), 'wb') as outfile:
outfile.write(str(time.time()))
class TestProcessProto(unittest.TestCase):
@ -195,19 +198,6 @@ class TestProcess(TestProcessProto):
"""Check PyBitmessage process name"""
self.assertEqual(self.process.name(), 'PyBitmessage')
@unittest.skipIf(psutil.version_info < (4, 0), 'psutil is too old')
def test_home(self):
"""Ensure BITMESSAGE_HOME is used by process"""
self.assertEqual(
self.process.environ().get('BITMESSAGE_HOME'), self.home)
def test_listening(self):
"""Check that pybitmessage listens on port 8444"""
for c in self.process.connections():
if c.status == 'LISTEN':
self.assertEqual(c.laddr[1], 8444)
break
def test_files(self):
"""Check existence of PyBitmessage files"""
for pfile in self._files:

View File

@ -4,10 +4,6 @@ Tests for common protocol functions
import unittest
from .common import skip_python3
skip_python3()
class TestProtocol(unittest.TestCase):
"""Main protocol test case"""

View File

@ -15,7 +15,7 @@ class TestRandomTrackingDict(unittest.TestCase):
@staticmethod
def randString():
"""helper function for tests, generates a random string"""
retval = ''
retval = b''
for _ in range(32):
retval += chr(random.randint(0, 255))
return retval

View File

@ -6,4 +6,3 @@ Depends: openssl, python-setuptools
Recommends: apparmor, python-msgpack, python-qt4, python-stem, tor
Suggests: python-pyopencl, python-jsonrpclib, python-defusedxml, python-qrcode
Suite: bionic
Setup-Env-Vars: DEB_BUILD_OPTIONS=nocheck

View File

@ -1,22 +0,0 @@
#!/usr/bin/env python
"""Custom tests runner script for tox and python3"""
import random # noseq
import sys
import unittest
def unittest_discover():
"""Explicit test suite creation"""
if sys.hexversion >= 0x3000000:
from pybitmessage import pathmagic
pathmagic.setup()
loader = unittest.defaultTestLoader
# randomize the order of tests in test cases
loader.sortTestMethodsUsing = lambda a, b: random.randint(-1, 1)
# pybitmessage symlink may disappear on Windows
return loader.discover('src.tests')
if __name__ == "__main__":
result = unittest.TextTestRunner(verbosity=2).run(unittest_discover())
sys.exit(not result.wasSuccessful())

34
tox.ini
View File

@ -1,34 +0,0 @@
[tox]
envlist = reset,py{27,37,38},stats
skip_missing_interpreters = true
[testenv]
setenv =
BITMESSAGE_HOME = {envtmpdir}
PYTHONWARNINGS = all
deps = -rrequirements.txt
commands =
python checkdeps.py
coverage run -a src/bitmessagemain.py -t
coverage run -a -m tests
[testenv:reset]
commands = coverage erase
[testenv:stats]
commands =
coverage report
coverage xml
[coverage:run]
source = src
omit =
*/lib*
tests.py
*/tests/*
src/version.py
*/__init__.py
src/fallback/umsgpack/*
[coverage:report]
ignore_errors = true