Compare commits

...
This repository has been archived on 2024-12-09. You can view files and clone it, but cannot push or open issues or pull requests.

98 Commits

Author SHA1 Message Date
1ee3883898
Suppress flake8 error
- python3-only code
2024-10-20 10:33:37 +08:00
79ba94bc7f
supress pylint super complaint 2024-10-17 11:44:02 +05:30
3eec0da204
code-quality: Update code quality kivy/baseclass/inbox 2024-10-11 19:23:18 +05:30
41ce3ef1ba
code-quality: Update code quality kivy/baseclass/draft 2024-10-11 19:23:18 +05:30
db69b9885f
Suppress pylinit super complaint
- it's in python3-only code
2024-10-09 19:32:13 +08:00
4ad06d7c59
code-quality: Update code quality kivy/baseclass/allmail and related references 2024-10-08 11:35:02 +05:30
85c3720fcc
Don't put compiled bitmsghash.so into the dockerfiles 2024-09-21 17:07:03 +03:00
dc57f71353
Additionally quote vars in appimage/build.sh 2024-09-20 16:59:53 +03:00
efaa63c14c
Added zbarcam deps
- replaces libpng wth pypng
2024-08-22 10:41:28 +05:30
e6ece59e0d
fix kivy build - restrict setuptools < 71 2024-07-25 09:55:39 +05:30
cf3716950f
Use mock for android build 2024-07-24 21:55:38 +02:00
759c6228c3
Android build fixes
- updates requirements as per Play Store
- can build aab on release
2024-07-24 15:45:37 +02:00
d6823a6fb1
Fix python3 / kivy test 2024-07-07 21:26:29 +08:00
anand k
5b537eabe1
Moved portCheckerQueue and receiveDataQueue to network module 2024-07-07 19:42:41 +08:00
anand k
49e89ecdf2
Moved addrQueue to network module 2024-07-04 17:57:11 +05:30
anand k
5cd4ecb437
Moved invQueue to network module 2024-07-04 17:57:11 +05:30
anand k
6578b5b925
Moved multiqueue to network module 2024-07-04 17:57:11 +05:30
27e566140d
Add a check for default in TestProtocol.test_check_local() 2024-07-03 01:25:47 +03:00
65d2a37cb2
Extend environment markers for PyQt5 - doesn't install on arm 2024-06-30 18:30:59 +03:00
b68beaab20
Update and rename the script for testing with docker 2024-06-30 18:30:58 +03:00
2b3034a42f
Add lines for local testing into the buildbot docker files 2024-06-30 18:30:52 +03:00
anand k
e578759a3f
Reduced helper_randon dependency from network module 2024-06-21 07:36:48 +05:30
205e25337f
Use six to avoid obsolete unittest assertions 2024-06-17 00:42:07 +03:00
a69e4eebf8
Update the windows exe URL 2024-06-16 20:51:37 +03:00
8280e058c7
Added some notes on the appimages into the install doc 2024-06-16 17:08:07 +03:00
638b5a9b1a
Add the alpha siffix for non-merge appimage builds 2024-06-16 17:07:36 +03:00
de445d6bd9
Suppress pylint complaint 2024-06-16 11:44:18 +08:00
de11bc484c
Make Windows the default window style on windows, not GTK+ 2024-06-12 19:42:16 +03:00
c3c41902cf
Use QtTest and QTimer.singleShot() to check the font dialog 2024-06-12 19:41:56 +03:00
d46af0835a
A rather rudimentary test with basic checks for the style setting 2024-06-12 19:41:56 +03:00
0ccbf6832f
Adjust the tabWidget font size relative to the base app font 2024-06-12 19:41:55 +03:00
ec1b05ee90
Allow user to set a base Qt window style and the font (family and size only) 2024-06-12 19:41:32 +03:00
21bef1058d
Improve the base class for bitmessageqt test cases 2024-06-11 16:05:08 +03:00
anand k
a71b44e95c
moved dandelion_enabled from state to Dandelion class as enabled attr 2024-06-11 14:48:29 +05:30
anand k
a209d65a26
Moved dandelion runtime var from state to network->dandelion 2024-05-27 09:20:21 +05:30
3a04e351cc
Move RIPEMD160 hash to highlevelcrypto and export to_ripe() (closes: #1796) 2024-05-20 15:07:20 +03:00
41fd17b637
Try to avoid building the snaps from branches not having snap-related changes,
except for merges (when HEAD is the v0.6 tip).
2024-05-19 12:13:01 +03:00
aaaac0f034
Append commit abbreviation to the snap version for non-release builds 2024-05-18 15:44:35 +03:00
anand k
e571ba8a51
Replaced state.streamsInWhichIAmParticipating with pool.streams 2024-05-15 09:21:37 +05:30
anand k
657c1de16b
Remove state.shutdown or replaced with self._stopped from some network thread 2024-05-13 07:23:11 +05:30
3b36676793
Run the lint env in the jammy buildbot dir 2024-05-10 02:55:16 +03:00
de9ee28516
Add a new tox env for linting, currently running pylint,
and the pylint config for python3.
2024-05-10 02:55:16 +03:00
8ef9c4d839
Remove ancient travis configuration 2024-05-09 17:55:00 +03:00
anand k
28355d70c7
Made BMConnectionPool as global runtime variable in connectionpool from singleton 2024-05-09 19:53:54 +05:30
8fa9da4cb4
Fix cross-compilation in the appimage build script and simplify local building 2024-05-08 01:48:44 +03:00
f28935f8fe
Avoid recursion when trying to build bitmsghash lib
in environments where prebuilt one is unusable and make doesn't work.
2024-05-05 23:22:34 +03:00
06ed879fca
Downgrade devcontainer ubuntu
- just released noble doesn't seem to have python-2.7 anymore so we're
  downgrading to jammy
2024-04-29 11:05:13 +08:00
anand k
95af3a859b
Renamed dandelion flag to dandelion_enabled 2024-04-22 08:59:00 +05:30
anand k
1c8ae8fef3
moved Dandelion in state - global runtime variable from singleton 2024-04-21 22:40:19 +05:30
anand k
5faef8d40e
moved inventory in state - global runtime variable from singleton 2024-04-19 08:21:19 +05:30
anand k
d555a79200
Test cases for shared.py 2024-04-17 18:21:11 +05:30
0ed566500f
Use fallback.RIPEMD160Hash() in the test for deterministic keys 2024-04-16 20:18:37 +03:00
c51b2875df
Tests for keys generation
this implementation for deterministic keys requires a passphrase of type bytes
2024-04-16 20:18:37 +03:00
5c1dcc5645
Define functions for generating keys in the highlevelcrypto 2024-04-16 20:18:03 +03:00
d547a8be2f
Encode WIF string before passing to highlevelcrypto.decodeWalletImportFormat() 2024-04-15 05:27:45 +03:00
acab92c561
Add a test for random address generation 2024-04-15 05:27:45 +03:00
ae3ff8c07e
Fix outdated exception handlers for singleWorker._getKeysForAddress() 2024-04-14 04:58:26 +03:00
629efb253e
Update the test for disseminatePreEncryptedMsg API command 2024-04-13 17:16:46 +03:00
f6bd1546a7
Alias disseminatePreEncryptedMsg as disseminatePreparedObject, edit docstring 2024-04-13 06:34:30 +03:00
7836538290
Make PoW optional in disseminatePreEncryptedMsg 2024-04-13 06:34:30 +03:00
44a4a370a6
Use defaults while doing PoW for a preencrypted msg 2024-04-13 06:34:30 +03:00
e1ae33389c
Properly format all the keys samples 2024-04-13 06:33:59 +03:00
7348568c78
The test for double SHA512 2024-04-13 03:19:55 +03:00
3ed84a5863
Start adding hashes with double SHA512 2024-04-13 03:17:39 +03:00
1b9773f2cf
A dummy test for randomBytes 2024-04-12 15:47:06 +03:00
c7a3bfacfa
Move randomBytes to highlevelcrypto 2024-04-12 15:46:32 +03:00
feaee60632
Add a test for WIF decoding and encoding 2024-04-07 03:30:56 +03:00
c1ca7044d2
Moved decodeWalletImportFormat() from shared to highlevelcrypto,
not addresses, where it's supposed to be because it uses
pyelliptic.arithmetic, addresses.decodeBase58() returns int which needs
to be encoded. Defined encodeWalletImportFormat() and replaced all uses.
2024-04-07 03:30:50 +03:00
17a09a665b
Moved samples into the samples module 2024-04-06 03:47:51 +03:00
5a48ee0ad5
Add tests for base58 and WIF decoding using pyelliptic.arithmetic 2024-04-06 03:47:19 +03:00
799237c7ff
Don't use BMConfigParser in highlevelcrypto, instead use digestAlg kwarg,
both in .sign() and .verify(), extend TestHighlevelcrypto.test_signatures().
2024-04-05 03:10:24 +03:00
fd3567b3fa
Add a test for sign() and verify() 2024-04-05 02:44:28 +03:00
13d090e344
Use protocol.decodeObjectParameters() for sending ACK - put it in the inventory 2024-04-05 00:38:30 +03:00
2a93b04332
Fix api.HandleDisseminatePreEncryptedMsg() for python3 2024-04-05 00:38:30 +03:00
8fb6410097
Enable inventory item setting for python3 2024-04-05 00:38:30 +03:00
2030e08db3
A test for disseminatePreEncryptedMsg API command 2024-04-05 00:38:30 +03:00
1794384f01
Use decodeObjectParameters() in disseminatePreEncryptedMsg API command handler 2024-04-05 00:38:29 +03:00
9747f65884
Define a protocol function for decoding object parameters outside of network 2024-04-05 00:37:50 +03:00
anand k
a39fc2cff5
Test cases for helper_inbox.py 2024-03-29 07:46:20 +05:30
73ffd26094
Kivy dependencies downgrade
- also fixes video recording
- tests are flakey but sometimes pass
2024-03-28 21:02:02 +08:00
anand-skss
b8749c6d7c
Rename mockpb dir to mockbm 2024-03-22 08:54:15 +00:00
anand k
563e557b1b
Test cases for helper_sent 2024-03-22 11:53:12 +05:30
anand k
2e98a1eb2d
Test cases for helper_sql & Added mock library dependency in requirements.txt 2024-03-21 11:25:10 +05:30
anand k
6d8c7f3bbc
Renamed mock dir to mockpb 2024-03-20 07:27:38 +05:30
7dc91af6d8
Fix typo and formatting in tests.core 2024-03-14 21:26:43 +02:00
anand k
d8843791ab
Test cases for multiqueue 2024-03-08 20:07:05 +05:30
95659b6f6c
Fix bandit comments:
- suppress B301 in the single place where it appears,
  - fix placement of B607, B603 in proofofwork and B324 in bitmessagekivy.
2024-03-06 13:59:13 +02:00
698932f487
Reduce number of globally disabled bandit checks
and uncomment fail fast in the test script.
2024-03-06 13:58:58 +02:00
b211c795ad
Remove tox-xenial
- EOL, difficult to maintain test environment
2024-03-06 08:39:45 +08:00
anand-skss
6a33fe58e1
Code Quality 2024-03-04 15:37:03 +00:00
498b21864e
Update devcontainers
- setuptools support
- pre-compile PyBitmessage
2024-03-03 14:14:37 +08:00
19ec364b72
Code quality 2024-03-03 11:21:33 +08:00
anand-skss
d33959defc
Code Quality 2024-02-29 17:49:43 +00:00
13e6116d8e
Move sphinx_rtd_theme into the docs requirements 2024-02-29 12:30:54 +02:00
anand-skss
c734ac7b5f
Code Quality 2024-02-28 15:42:37 +00:00
b8e099539e
Restrict m2r version for py27 2024-02-28 09:54:08 +02:00
anand k
5af8b8d3f7
included defusedxml package for parsestring & cleaned upnp schema 2024-02-28 08:40:56 +05:30
anand k
dd64a7b507
Code Quality 2024-02-28 07:23:55 +05:30
140 changed files with 2057 additions and 1078 deletions

View File

@ -22,7 +22,10 @@ RUN apt-get -y update -qq \
RUN apt-get -y install -qq --no-install-recommends openjdk-17-jdk \ RUN apt-get -y install -qq --no-install-recommends openjdk-17-jdk \
&& apt-get -y autoremove && apt-get -y autoremove
RUN pip install pip install buildozer cython virtualenv # pyzbar dependencies
RUN apt-get -y install -qq --no-install-recommends libzbar0 libtool gettext
RUN pip install buildozer cython virtualenv
ENV ANDROID_NDK_HOME="${ANDROID_HOME}/android-ndk" ENV ANDROID_NDK_HOME="${ANDROID_HOME}/android-ndk"

View File

@ -1,9 +1,32 @@
#!/bin/bash #!/bin/bash
export LC_ALL=en_US.UTF-8 export LC_ALL=en_US.UTF-8
export LANG=en_US.UTF-8 export LANG=en_US.UTF-8
# buildozer OOM workaround
mkdir -p ~/.gradle
echo "org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8" \
> ~/.gradle/gradle.properties
# workaround for symlink
rm -rf src/pybitmessage
mkdir -p src/pybitmessage
cp src/*.py src/pybitmessage
cp -r src/bitmessagekivy src/backend src/mockbm src/images src/pybitmessage
pushd packages/android pushd packages/android
buildozer android debug || exit $?
BUILDMODE=debug
if [ "$BUILDBOT_JOBNAME" = "android" -a \
"$BUILDBOT_REPOSITORY" = "https://github.com/Bitmessage/PyBitmessage" -a \
"$BUILDBOT_BRANCH" = "v0.6" ]; then
sed -e 's/android.release_artifact *=.*/release_artifact = aab/' -i "" buildozer.spec
BUILDMODE=release
fi
buildozer android $BUILDMODE || exit $?
popd popd
mkdir -p ../out mkdir -p ../out
cp packages/android/bin/*.apk ../out RELEASE_ARTIFACT=$(grep release_artifact packages/android/buildozer.spec |cut -d= -f2|tr -Cd 'a-z')
cp packages/android/bin/*.${RELEASE_ARTIFACT} ../out

View File

@ -1,5 +1,11 @@
#!/bin/bash #!/bin/bash
RELEASE_ARTIFACT=$(grep release_artifact packages/android/buildozer.spec |cut -d= -f2|tr -Cd 'a-z')
if [ $RELEASE_ARTIFACT = "aab" ]; then
exit
fi
unzip -p packages/android/bin/*.apk assets/private.tar \ unzip -p packages/android/bin/*.apk assets/private.tar \
| tar --list -z > package.list | tar --list -z > package.list
cat package.list cat package.list

View File

@ -24,3 +24,5 @@ RUN wget -qO appimage-builder-x86_64.AppImage \
https://github.com/AppImageCrafters/appimage-builder/releases/download/v1.1.0/appimage-builder-1.1.0-x86_64.AppImage https://github.com/AppImageCrafters/appimage-builder/releases/download/v1.1.0/appimage-builder-1.1.0-x86_64.AppImage
ADD . . ADD . .
CMD .buildbot/tox-bionic/build.sh

View File

@ -4,7 +4,11 @@ export APPIMAGE_EXTRACT_AND_RUN=1
BUILDER=appimage-builder-x86_64.AppImage BUILDER=appimage-builder-x86_64.AppImage
RECIPE=packages/AppImage/AppImageBuilder.yml RECIPE=packages/AppImage/AppImageBuilder.yml
git remote add -f upstream https://github.com/Bitmessage/PyBitmessage.git
HEAD="$(git rev-parse HEAD)"
UPSTREAM="$(git merge-base --fork-point upstream/v0.6)"
export APP_VERSION=$(git describe --tags | cut -d- -f1,3 | tr -d v) export APP_VERSION=$(git describe --tags | cut -d- -f1,3 | tr -d v)
[ "$HEAD" != "$UPSTREAM" ] && APP_VERSION="${APP_VERSION}-alpha"
function set_sourceline { function set_sourceline {
if [ ${ARCH} == amd64 ]; then if [ ${ARCH} == amd64 ]; then
@ -14,36 +18,45 @@ function set_sourceline {
fi fi
} }
function build_appimage {
set_sourceline
./${BUILDER} --recipe ${RECIPE} || exit 1
rm -rf build
}
[ -f ${BUILDER} ] || wget -qO ${BUILDER} \ [ -f ${BUILDER} ] || wget -qO ${BUILDER} \
https://github.com/AppImageCrafters/appimage-builder/releases/download/v1.1.0/appimage-builder-1.1.0-x86_64.AppImage \ https://github.com/AppImageCrafters/appimage-builder/releases/download/v1.1.0/appimage-builder-1.1.0-x86_64.AppImage \
&& chmod +x ${BUILDER} && chmod +x ${BUILDER}
chmod 1777 /tmp
export ARCH=amd64 export ARCH=amd64
export APPIMAGE_ARCH=x86_64 export APPIMAGE_ARCH=x86_64
export RUNTIME=${APPIMAGE_ARCH} export RUNTIME=${APPIMAGE_ARCH}
set_sourceline
./${BUILDER} --recipe ${RECIPE} || exit 1 build_appimage
export ARCH=armhf export ARCH=armhf
export APPIMAGE_ARCH=${ARCH} export APPIMAGE_ARCH=${ARCH}
export RUNTIME=gnueabihf export RUNTIME=gnueabihf
export CC=arm-linux-gnueabihf-gcc export CC=arm-linux-gnueabihf-gcc
export CXX=${CC} export CXX=${CC}
set_sourceline
./${BUILDER} --recipe ${RECIPE} || exit 1 build_appimage
export ARCH=arm64 export ARCH=arm64
export APPIMAGE_ARCH=aarch64 export APPIMAGE_ARCH=aarch64
export RUNTIME=${APPIMAGE_ARCH} export RUNTIME=${APPIMAGE_ARCH}
export CC=aarch64-linux-gnu-gcc export CC=aarch64-linux-gnu-gcc
export CXX=${CC} export CXX=${CC}
set_sourceline
./${BUILDER} --recipe ${RECIPE} build_appimage
mkdir -p ../out EXISTING_OWNER=$(stat -c %u ../out) || mkdir -p ../out
sha256sum PyBitmessage*.AppImage > ../out/SHA256SUMS
sha256sum PyBitmessage*.AppImage >> ../out/SHA256SUMS
cp PyBitmessage*.AppImage ../out cp PyBitmessage*.AppImage ../out
if [ ${EXISTING_OWNER} ]; then
chown ${EXISTING_OWNER} ../out/PyBitmessage*.AppImage ../out/SHA256SUMS
fi

View File

@ -15,4 +15,4 @@ RUN apt-get install -yq \
RUN ln -sf /usr/bin/python3 /usr/bin/python RUN ln -sf /usr/bin/python3 /usr/bin/python
RUN pip3 install --upgrade setuptools pip RUN pip3 install --upgrade 'setuptools<71' pip

View File

@ -1,5 +1,12 @@
#!/bin/bash #!/bin/bash
git remote add -f upstream https://github.com/Bitmessage/PyBitmessage.git
HEAD="$(git rev-parse HEAD)"
UPSTREAM="$(git merge-base --fork-point upstream/v0.6)"
SNAP_DIFF="$(git diff upstream/v0.6 -- packages/snap .buildbot/snap)"
[ -z "${SNAP_DIFF}" ] && [ $HEAD != $UPSTREAM ] && exit 0
pushd packages && snapcraft || exit 1 pushd packages && snapcraft || exit 1
popd popd

View File

@ -20,3 +20,7 @@ RUN python3.8 -m pip install --upgrade pip tox virtualenv
ENV LANG en_US.UTF-8 ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8 ENV LC_ALL en_US.UTF-8
ADD . .
CMD .buildbot/tox-bionic/test.sh

View File

@ -1,4 +1,4 @@
#!/bin/sh #!/bin/sh
tox -e lint-basic # || exit 1 tox -e lint-basic || exit 1
tox tox

View File

@ -13,3 +13,5 @@ RUN apt-get install -yq --no-install-suggests --no-install-recommends \
RUN python3.9 -m pip install --upgrade pip tox virtualenv RUN python3.9 -m pip install --upgrade pip tox virtualenv
ADD . . ADD . .
CMD .buildbot/tox-focal/test.sh

View File

@ -10,3 +10,7 @@ RUN apt-get install -yq --no-install-suggests --no-install-recommends \
python3-dev python3-pip language-pack-en qt5dxcb-plugin tor xvfb python3-dev python3-pip language-pack-en qt5dxcb-plugin tor xvfb
RUN pip install tox RUN pip install tox
ADD . .
CMD .buildbot/tox-jammy/test.sh

View File

@ -1,4 +1,4 @@
#!/bin/sh #!/bin/sh
tox -e lint-basic # || exit 1 tox -e lint || exit 1
tox -e py310 tox -e py310

View File

@ -1,50 +0,0 @@
FROM ubuntu:xenial
RUN apt-get update
# Common apt packages
RUN apt-get install -yq --no-install-suggests --no-install-recommends \
software-properties-common build-essential libcap-dev libffi-dev \
libssl-dev python-all-dev python-pip python-setuptools python3-dev
# wget
RUN apt-get install -yq --no-install-suggests --no-install-recommends \
libgmp-dev m4 pkgconf wget
RUN wget "https://ftp.gnu.org/gnu/nettle/nettle-3.9.1.tar.gz" \
&& tar -zxf nettle-3.9.1.tar.gz
RUN cd nettle-3.9.1 \
&& ./configure --disable-openssl --enable-shared \
&& make && make install
RUN wget "https://www.gnupg.org/ftp/gcrypt/gnutls/v3.6/gnutls-3.6.16.tar.xz" \
&& tar -Jxf gnutls-3.6.16.tar.xz
RUN apt-get remove -yq libgnutls30
RUN cd gnutls-3.6.16 \
&& ./configure --prefix=/usr --without-p11-kit \
--with-included-libtasn1 --with-included-unistring --without-idn \
&& make && make install
RUN wget "https://ftp.gnu.org/gnu/wget/wget2-2.1.0.tar.gz" \
&& tar -zxf wget2-2.1.0.tar.gz
RUN apt-get remove -yq wget
RUN cd wget2-2.1.0 \
&& ./configure --without-libpsl --prefix=/usr \
GNUTLS_CFLAGS=-I/usr/include/gnutls/ GNUTLS_LIBS=-L/usr/lib \
&& make && make install \
&& mv /usr/bin/wget2 /usr/bin/wget
RUN wget -O /usr/local/bin/buildbot_entrypoint.sh http://git.bitmessage.org/Bitmessage/buildbot-scripts/raw/branch/master/docker/xenial/entrypoint.sh
RUN pip install --upgrade pip==20.0.1
RUN pip install --upgrade setuptools
RUN pip install tox
ADD . .
CMD .buildbot/tox-xenial/test.sh

View File

@ -1 +0,0 @@
../tox-bionic/test.sh

View File

@ -1,4 +1,4 @@
FROM ubuntu:latest FROM ubuntu:jammy
ARG USERNAME=user ARG USERNAME=user
ARG USER_UID=1000 ARG USER_UID=1000
@ -15,6 +15,9 @@ RUN DEBIAN_FRONTEND=noninteractive apt-get install -y \
libcap-dev \ libcap-dev \
libssl-dev \ libssl-dev \
pylint \ pylint \
python-setuptools \
python2.7 \
python2.7-dev \
python3 \ python3 \
python3-dev \ python3-dev \
python3-flake8 \ python3-flake8 \
@ -26,9 +29,6 @@ RUN DEBIAN_FRONTEND=noninteractive apt-get install -y \
RUN apt-add-repository ppa:deadsnakes/ppa RUN apt-add-repository ppa:deadsnakes/ppa
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y \
python2.7 python2.7-dev
RUN pip install 'tox<4' 'virtualenv<20.22.0' RUN pip install 'tox<4' 'virtualenv<20.22.0'
RUN groupadd --gid $USER_GID $USERNAME \ RUN groupadd --gid $USER_GID $USERNAME \

View File

@ -16,6 +16,7 @@
], ],
"dockerFile": "Dockerfile", "dockerFile": "Dockerfile",
"postCreateCommand": "pip3 install -r requirements.txt", "postCreateCommand": "pip3 install -r requirements.txt",
"updateContentCommand": "python2.7 setup.py install --user",
"remoteEnv": { "remoteEnv": {
"PATH": "${containerEnv:PATH}:/home/user/.local/bin" "PATH": "${containerEnv:PATH}:/home/user/.local/bin"
}, },

View File

@ -5,3 +5,4 @@ __pycache__
.buildozer .buildozer
.tox .tox
mprofile_* mprofile_*
**.so

View File

@ -1,23 +0,0 @@
language: python
cache: pip
dist: bionic
python:
- "2.7_with_system_site_packages"
- "3.7"
addons:
apt:
packages:
- build-essential
- libcap-dev
- python-qt4
- python-pyqt5
- tor
- xvfb
install:
- pip install -r requirements.txt
- python setup.py install
- export PYTHONWARNINGS=all
script:
- python checkdeps.py
- python src/bitmessagemain.py -t
- python -bm tests

View File

@ -1,12 +1,43 @@
# PyBitmessage Installation Instructions # PyBitmessage Installation Instructions
- Binary (64bit, no separate installation of dependencies required) - Binary (64bit, no separate installation of dependencies required)
- Windows: https://download.bitmessage.org/snapshots/ - Windows: https://artifacts.bitmessage.at/winebuild/
- Linux AppImages: https://artifacts.bitmessage.at/appimage/ - Linux AppImages: https://artifacts.bitmessage.at/appimage/
- Linux snaps: https://artifacts.bitmessage.at/snap/ - Linux snaps: https://artifacts.bitmessage.at/snap/
- Mac (not up to date): https://github.com/Bitmessage/PyBitmessage/releases/tag/v0.6.1 - Mac (not up to date): https://github.com/Bitmessage/PyBitmessage/releases/tag/v0.6.1
- Source - Source
`git clone git://github.com/Bitmessage/PyBitmessage.git` `git clone git://github.com/Bitmessage/PyBitmessage.git`
## Notes on the AppImages
The [AppImage](https://docs.appimage.org/introduction/index.html)
is a bundle, built by the
[appimage-builder](https://github.com/AppImageCrafters/appimage-builder) from
the Ubuntu Bionic deb files, the sources and `bitmsghash.so`, precompiled for
3 architectures, using the `packages/AppImage/AppImageBuilder.yml` recipe.
When you run the appimage the bundle is loop mounted to a location like
`/tmp/.mount_PyBitm97wj4K` with `squashfs-tools`.
The appimage name has several informational filds:
```
PyBitmessage-<VERSION>-g<COMMITHASH>[-alpha]-<ARCH>.AppImage
```
E.g. `PyBitmessage-0.6.3.2-ge571ba8a-x86_64.AppImage` is an appimage, built from
the `v0.6` for x86_64 and `PyBitmessage-0.6.3.2-g9de2aaf1-alpha-aarch64.AppImage`
is one, built from some development branch for arm64.
You can also build the appimage with local code. For that you need installed
docker:
```
$ docker build -t bm-appimage -f .buildbot/appimage/Dockerfile .
$ docker run -t --rm -v "$(pwd)"/dist:/out bm-appimage .buildbot/appimage/build.sh
```
The appimages should be in the dist dir.
## Helper Script for building from source ## Helper Script for building from source
Go to the directory with PyBitmessage source code and run: Go to the directory with PyBitmessage source code and run:
``` ```

11
docker-test.sh Executable file
View File

@ -0,0 +1,11 @@
#!/bin/sh
DOCKERFILE=.buildbot/tox-bionic/Dockerfile
docker build -t pybm/tox -f $DOCKERFILE .
if [ $? -gt 0 ]; then
docker build --no-cache -t pybm/tox -f $DOCKERFILE .
fi
docker run --rm -it pybm/tox

View File

@ -203,7 +203,7 @@ autodoc_mock_imports = [
'pybitmessage.bitmessagekivy', 'pybitmessage.bitmessagekivy',
'pybitmessage.bitmessageqt.foldertree', 'pybitmessage.bitmessageqt.foldertree',
'pybitmessage.helper_startup', 'pybitmessage.helper_startup',
'pybitmessage.mock', 'pybitmessage.mockbm',
'pybitmessage.network.httpd', 'pybitmessage.network.httpd',
'pybitmessage.network.https', 'pybitmessage.network.https',
'ctypes', 'ctypes',
@ -232,7 +232,7 @@ apidoc_excluded_paths = [
'bitmessageqt/addressvalidator.py', 'bitmessageqt/foldertree.py', 'bitmessageqt/addressvalidator.py', 'bitmessageqt/foldertree.py',
'bitmessageqt/migrationwizard.py', 'bitmessageqt/newaddresswizard.py', 'bitmessageqt/migrationwizard.py', 'bitmessageqt/newaddresswizard.py',
'helper_startup.py', 'helper_startup.py',
'kivymd', 'mock', 'main.py', 'navigationdrawer', 'network/http*', 'kivymd', 'mockbm', 'main.py', 'navigationdrawer', 'network/http*',
'src', 'tests', 'version.py' 'src', 'tests', 'version.py'
] ]
apidoc_module_first = True apidoc_module_first = True

View File

@ -1,4 +1,5 @@
mistune<=0.8.4 mistune<=0.8.4
m2r m2r<=0.2.1
sphinx_rtd_theme
sphinxcontrib-apidoc sphinxcontrib-apidoc
docutils<=0.17.1 docutils<=0.17.1

View File

@ -1,6 +1,11 @@
kivy-garden.qrcode kivy-garden.qrcode
kivymd==1.0.2 kivymd==1.0.2
kivy==2.1.0
opencv-python opencv-python
pyzbar pyzbar
git+https://github.com/tito/telenium@9b54ff1#egg=telenium git+https://github.com/tito/telenium@9b54ff1#egg=telenium
Pillow Pillow==9.4.0
jaraco.collections==3.8.0
jaraco.classes==3.2.3
pytz==2022.7.1
pydantic==1.10.6

View File

@ -1,19 +1,19 @@
[app] [app]
# (str) Title of your application # (str) Title of your application
title = mockone title = PyBitmessage Mock
# (str) Package name # (str) Package name
package.name = mock package.name = pybitmessagemock
# (str) Package domain (needed for android/ios packaging) # (str) Package domain (needed for android/ios packaging)
package.domain = org.mock package.domain = at.bitmessage
# (str) Source code where the main.py live # (str) Source code where the main.py live
source.dir = ../../src source.dir = ../../src
# (list) Source files to include (let empty to include all the files) # (list) Source files to include (let empty to include all the files)
source.include_exts = py,png,jpg,kv,atlas,tflite,sql source.include_exts = py,png,jpg,kv,atlas,tflite,sql,json
# (list) List of inclusions using pattern matching # (list) List of inclusions using pattern matching
#source.include_patterns = assets/*,images/*.png #source.include_patterns = assets/*,images/*.png
@ -28,7 +28,7 @@ source.include_exts = py,png,jpg,kv,atlas,tflite,sql
#source.exclude_patterns = license,images/*/*.jpg #source.exclude_patterns = license,images/*/*.jpg
# (str) Application versioning (method 1) # (str) Application versioning (method 1)
version = 0.1 version = 0.1.1
# (str) Application versioning (method 2) # (str) Application versioning (method 2)
# version.regex = __version__ = ['"](.*)['"] # version.regex = __version__ = ['"](.*)['"]
@ -36,7 +36,7 @@ version = 0.1
# (list) Application requirements # (list) Application requirements
# comma separated e.g. requirements = sqlite3,kivy # comma separated e.g. requirements = sqlite3,kivy
requirements = python3,kivy requirements = python3,kivy,sqlite3,kivymd==1.0.2,Pillow,opencv,kivy-garden.qrcode,qrcode,typing_extensions,pypng,pyzbar,xcamera,zbarcam
# (str) Custom source folders for requirements # (str) Custom source folders for requirements
# Sets custom source for any requirements with recipes # Sets custom source for any requirements with recipes
@ -92,7 +92,7 @@ fullscreen = 0
# (int) Android API to use (targetSdkVersion AND compileSdkVersion) # (int) Android API to use (targetSdkVersion AND compileSdkVersion)
# note: when changing, Dockerfile also needs to be changed to install corresponding build tools # note: when changing, Dockerfile also needs to be changed to install corresponding build tools
android.api = 28 android.api = 33
# (int) Minimum API required. You will need to set the android.ndk_api to be as low as this value. # (int) Minimum API required. You will need to set the android.ndk_api to be as low as this value.
android.minapi = 21 android.minapi = 21
@ -243,6 +243,8 @@ android.allow_backup = True
# Usage example : android.manifest_placeholders = [myCustomUrl:\"org.kivy.customurl\"] # Usage example : android.manifest_placeholders = [myCustomUrl:\"org.kivy.customurl\"]
# android.manifest_placeholders = [:] # android.manifest_placeholders = [:]
android.release_artifact = apk
# #
# Python for android (p4a) specific # Python for android (p4a) specific
# #

View File

@ -28,7 +28,7 @@ parts:
source: https://github.com/Bitmessage/PyBitmessage.git source: https://github.com/Bitmessage/PyBitmessage.git
override-pull: | override-pull: |
snapcraftctl pull snapcraftctl pull
snapcraftctl set-version $(git describe --tags --abbrev=0 | tr -d v) snapcraftctl set-version $(git describe --tags | cut -d- -f1,3 | tr -d v)
plugin: python plugin: python
python-version: python2 python-version: python2
build-packages: build-packages:

View File

@ -1,7 +1,8 @@
coverage coverage
psutil psutil
pycryptodome pycryptodome
PyQt5;python_version>="3.7" PyQt5;python_version>="3.7" and platform_machine=="x86_64"
mock;python_version<="2.7"
python_prctl;platform_system=="Linux" python_prctl;platform_system=="Linux"
six six
xvfbwrapper;platform_system=="Linux" xvfbwrapper;platform_system=="Linux"

View File

@ -1,13 +0,0 @@
#!/bin/sh
DOCKERFILE=packages/docker/Dockerfile.bionic
# explicitly mark appimage stage because it builds in any case
docker build --target appimage -t pybm/appimage -f $DOCKERFILE .
if [ $? -gt 0 ]; then
docker build --no-cache --target appimage -t pybm/appimage -f $DOCKERFILE .
fi
docker build --target tox -t pybm/tox -f $DOCKERFILE .
docker run --rm -t pybm/tox

View File

@ -13,7 +13,7 @@ from src.version import softwareVersion
EXTRAS_REQUIRE = { EXTRAS_REQUIRE = {
'docs': ['sphinx', 'sphinx_rtd_theme'], 'docs': ['sphinx'],
'gir': ['pygobject'], 'gir': ['pygobject'],
'json': ['jsonrpclib'], 'json': ['jsonrpclib'],
'notify2': ['notify2'], 'notify2': ['notify2'],
@ -92,7 +92,7 @@ if __name__ == "__main__":
) )
if os.environ.get('INSTALL_TESTS', False): if os.environ.get('INSTALL_TESTS', False):
packages.extend(['pybitmessage.mock', 'pybitmessage.backend', 'pybitmessage.bitmessagekivy.tests']) packages.extend(['pybitmessage.mockbm', 'pybitmessage.backend', 'pybitmessage.bitmessagekivy.tests'])
package_data[''].extend(['bitmessagekivy/tests/sampleData/*.dat']) package_data[''].extend(['bitmessagekivy/tests/sampleData/*.dat'])
# this will silently accept alternative providers of msgpack # this will silently accept alternative providers of msgpack

View File

@ -2,11 +2,16 @@
Operations with addresses Operations with addresses
""" """
# pylint: disable=inconsistent-return-statements # pylint: disable=inconsistent-return-statements
import hashlib
import logging import logging
from binascii import hexlify, unhexlify from binascii import hexlify, unhexlify
from struct import pack, unpack from struct import pack, unpack
try:
from highlevelcrypto import double_sha512
except ImportError:
from .highlevelcrypto import double_sha512
logger = logging.getLogger('default') logger = logging.getLogger('default')
@ -134,15 +139,6 @@ def decodeVarint(data):
return (encodedValue, 9) return (encodedValue, 9)
def calculateInventoryHash(data):
"""Calculate inventory hash from object data"""
sha = hashlib.new('sha512')
sha2 = hashlib.new('sha512')
sha.update(data)
sha2.update(sha.digest())
return sha2.digest()[0:32]
def encodeAddress(version, stream, ripe): def encodeAddress(version, stream, ripe):
"""Convert ripe to address""" """Convert ripe to address"""
if version >= 2 and version < 4: if version >= 2 and version < 4:
@ -166,12 +162,7 @@ def encodeAddress(version, stream, ripe):
storedBinaryData = encodeVarint(version) + encodeVarint(stream) + ripe storedBinaryData = encodeVarint(version) + encodeVarint(stream) + ripe
# Generate the checksum # Generate the checksum
sha = hashlib.new('sha512') checksum = double_sha512(storedBinaryData)[0:4]
sha.update(storedBinaryData)
currentHash = sha.digest()
sha = hashlib.new('sha512')
sha.update(currentHash)
checksum = sha.digest()[0:4]
# FIXME: encodeBase58 should take binary data, to reduce conversions # FIXME: encodeBase58 should take binary data, to reduce conversions
# encodeBase58(storedBinaryData + checksum) # encodeBase58(storedBinaryData + checksum)
@ -207,13 +198,7 @@ def decodeAddress(address):
data = unhexlify(hexdata) data = unhexlify(hexdata)
checksum = data[-4:] checksum = data[-4:]
sha = hashlib.new('sha512') if checksum != double_sha512(data[:-4])[0:4]:
sha.update(data[:-4])
currentHash = sha.digest()
sha = hashlib.new('sha512')
sha.update(currentHash)
if checksum != sha.digest()[0:4]:
status = 'checksumfailed' status = 'checksumfailed'
return status, 0, 0, '' return status, 0, 0, ''

View File

@ -71,9 +71,9 @@ from struct import pack, unpack
import six import six
from six.moves import configparser, http_client, xmlrpc_server from six.moves import configparser, http_client, xmlrpc_server
import defaults
import helper_inbox import helper_inbox
import helper_sent import helper_sent
import protocol
import proofofwork import proofofwork
import queues import queues
import shared import shared
@ -82,23 +82,25 @@ import shutdown
import state import state
from addresses import ( from addresses import (
addBMIfNotPresent, addBMIfNotPresent,
calculateInventoryHash,
decodeAddress, decodeAddress,
decodeVarint, decodeVarint,
varintDecodeError varintDecodeError
) )
from bmconfigparser import config from bmconfigparser import config
from debug import logger from debug import logger
from defaults import (
networkDefaultProofOfWorkNonceTrialsPerByte,
networkDefaultPayloadLengthExtraBytes)
from helper_sql import ( from helper_sql import (
SqlBulkExecute, sqlExecute, sqlQuery, sqlStoredProcedure, sql_ready) SqlBulkExecute, sqlExecute, sqlQuery, sqlStoredProcedure, sql_ready)
from inventory import Inventory from highlevelcrypto import calculateInventoryHash
try: try:
from network import BMConnectionPool from network import connectionpool
except ImportError: except ImportError:
BMConnectionPool = None connectionpool = None
from network import stats, StoppableThread from network import stats, StoppableThread, invQueue
from version import softwareVersion from version import softwareVersion
try: # TODO: write tests for XML vulnerabilities try: # TODO: write tests for XML vulnerabilities
@ -656,13 +658,11 @@ class BMRPCDispatcher(object):
nonceTrialsPerByte = self.config.get( nonceTrialsPerByte = self.config.get(
'bitmessagesettings', 'defaultnoncetrialsperbyte' 'bitmessagesettings', 'defaultnoncetrialsperbyte'
) if not totalDifficulty else int( ) if not totalDifficulty else int(
defaults.networkDefaultProofOfWorkNonceTrialsPerByte networkDefaultProofOfWorkNonceTrialsPerByte * totalDifficulty)
* totalDifficulty)
payloadLengthExtraBytes = self.config.get( payloadLengthExtraBytes = self.config.get(
'bitmessagesettings', 'defaultpayloadlengthextrabytes' 'bitmessagesettings', 'defaultpayloadlengthextrabytes'
) if not smallMessageDifficulty else int( ) if not smallMessageDifficulty else int(
defaults.networkDefaultPayloadLengthExtraBytes networkDefaultPayloadLengthExtraBytes * smallMessageDifficulty)
* smallMessageDifficulty)
if not isinstance(eighteenByteRipe, bool): if not isinstance(eighteenByteRipe, bool):
raise APIError( raise APIError(
@ -704,13 +704,11 @@ class BMRPCDispatcher(object):
nonceTrialsPerByte = self.config.get( nonceTrialsPerByte = self.config.get(
'bitmessagesettings', 'defaultnoncetrialsperbyte' 'bitmessagesettings', 'defaultnoncetrialsperbyte'
) if not totalDifficulty else int( ) if not totalDifficulty else int(
defaults.networkDefaultProofOfWorkNonceTrialsPerByte networkDefaultProofOfWorkNonceTrialsPerByte * totalDifficulty)
* totalDifficulty)
payloadLengthExtraBytes = self.config.get( payloadLengthExtraBytes = self.config.get(
'bitmessagesettings', 'defaultpayloadlengthextrabytes' 'bitmessagesettings', 'defaultpayloadlengthextrabytes'
) if not smallMessageDifficulty else int( ) if not smallMessageDifficulty else int(
defaults.networkDefaultPayloadLengthExtraBytes networkDefaultPayloadLengthExtraBytes * smallMessageDifficulty)
* smallMessageDifficulty)
if not passphrase: if not passphrase:
raise APIError(1, 'The specified passphrase is blank.') raise APIError(1, 'The specified passphrase is blank.')
@ -1283,68 +1281,73 @@ class BMRPCDispatcher(object):
}) })
return {'subscriptions': data} return {'subscriptions': data}
@command('disseminatePreEncryptedMsg') @command('disseminatePreEncryptedMsg', 'disseminatePreparedObject')
def HandleDisseminatePreEncryptedMsg( # pylint: disable=too-many-locals def HandleDisseminatePreparedObject(
self, encryptedPayload, requiredAverageProofOfWorkNonceTrialsPerByte, self, encryptedPayload,
requiredPayloadLengthExtraBytes): nonceTrialsPerByte=networkDefaultProofOfWorkNonceTrialsPerByte,
"""Handle a request to disseminate an encrypted message""" payloadLengthExtraBytes=networkDefaultPayloadLengthExtraBytes
):
"""
Handle a request to disseminate an encrypted message.
# The device issuing this command to PyBitmessage supplies a msg The device issuing this command to PyBitmessage supplies an object
# object that has already been encrypted but which still needs the POW that has already been encrypted but which may still need the PoW
# to be done. PyBitmessage accepts this msg object and sends it out to be done. PyBitmessage accepts this object and sends it out
# to the rest of the Bitmessage network as if it had generated to the rest of the Bitmessage network as if it had generated
# the message itself. Please do not yet add this to the api doc. the message itself.
*encryptedPayload* is a hex encoded string starting with the nonce,
8 zero bytes in case of no PoW done.
"""
encryptedPayload = self._decode(encryptedPayload, "hex") encryptedPayload = self._decode(encryptedPayload, "hex")
expiresTime = unpack('>Q', encryptedPayload[0:8])[0]
objectType = unpack('>I', encryptedPayload[8:12])[0] nonce, = unpack('>Q', encryptedPayload[:8])
TTL = expiresTime - time.time() + 300 # a bit of extra padding objectType, toStreamNumber, expiresTime = \
# Let us do the POW and attach it to the front protocol.decodeObjectParameters(encryptedPayload)
target = 2**64 / (
requiredAverageProofOfWorkNonceTrialsPerByte * ( if nonce == 0: # Let us do the POW and attach it to the front
len(encryptedPayload) + 8 encryptedPayload = encryptedPayload[8:]
+ requiredPayloadLengthExtraBytes + (( TTL = expiresTime - time.time() + 300 # a bit of extra padding
TTL * ( # Let us do the POW and attach it to the front
len(encryptedPayload) + 8 logger.debug("expiresTime: %s", expiresTime)
+ requiredPayloadLengthExtraBytes logger.debug("TTL: %s", TTL)
)) / (2 ** 16)) logger.debug("objectType: %s", objectType)
)) logger.info(
logger.debug("expiresTime: %s", expiresTime) '(For msg message via API) Doing proof of work. Total required'
logger.debug("TTL: %s", TTL) ' difficulty: %s\nRequired small message difficulty: %s',
logger.debug("objectType: %s", objectType) float(nonceTrialsPerByte)
logger.info( / networkDefaultProofOfWorkNonceTrialsPerByte,
'(For msg message via API) Doing proof of work. Total required' float(payloadLengthExtraBytes)
' difficulty: %s\nRequired small message difficulty: %s', / networkDefaultPayloadLengthExtraBytes,
float(requiredAverageProofOfWorkNonceTrialsPerByte) )
/ defaults.networkDefaultProofOfWorkNonceTrialsPerByte, powStartTime = time.time()
float(requiredPayloadLengthExtraBytes) target = 2**64 / (
/ defaults.networkDefaultPayloadLengthExtraBytes, nonceTrialsPerByte * (
) len(encryptedPayload) + 8 + payloadLengthExtraBytes + ((
powStartTime = time.time() TTL * (
initialHash = hashlib.sha512(encryptedPayload).digest() len(encryptedPayload) + 8 + payloadLengthExtraBytes
trialValue, nonce = proofofwork.run(target, initialHash) )) / (2 ** 16))
logger.info( ))
'(For msg message via API) Found proof of work %s\nNonce: %s\n' initialHash = hashlib.sha512(encryptedPayload).digest()
'POW took %s seconds. %s nonce trials per second.', trialValue, nonce = proofofwork.run(target, initialHash)
trialValue, nonce, int(time.time() - powStartTime), logger.info(
nonce / (time.time() - powStartTime) '(For msg message via API) Found proof of work %s\nNonce: %s\n'
) 'POW took %s seconds. %s nonce trials per second.',
encryptedPayload = pack('>Q', nonce) + encryptedPayload trialValue, nonce, int(time.time() - powStartTime),
parserPos = 20 nonce / (time.time() - powStartTime)
_, objectVersionLength = decodeVarint( )
encryptedPayload[parserPos:parserPos + 10]) encryptedPayload = pack('>Q', nonce) + encryptedPayload
parserPos += objectVersionLength
toStreamNumber, _ = decodeVarint(
encryptedPayload[parserPos:parserPos + 10])
inventoryHash = calculateInventoryHash(encryptedPayload) inventoryHash = calculateInventoryHash(encryptedPayload)
Inventory()[inventoryHash] = ( state.Inventory[inventoryHash] = (
objectType, toStreamNumber, encryptedPayload, objectType, toStreamNumber, encryptedPayload,
expiresTime, '' expiresTime, b''
) )
logger.info( logger.info(
'Broadcasting inv for msg(API disseminatePreEncryptedMsg' 'Broadcasting inv for msg(API disseminatePreEncryptedMsg'
' command): %s', hexlify(inventoryHash)) ' command): %s', hexlify(inventoryHash))
queues.invQueue.put((toStreamNumber, inventoryHash)) invQueue.put((toStreamNumber, inventoryHash))
return hexlify(inventoryHash) return hexlify(inventoryHash).decode()
@command('trashSentMessageByAckData') @command('trashSentMessageByAckData')
def HandleTrashSentMessageByAckDAta(self, ackdata): def HandleTrashSentMessageByAckDAta(self, ackdata):
@ -1367,8 +1370,8 @@ class BMRPCDispatcher(object):
# Let us do the POW # Let us do the POW
target = 2 ** 64 / (( target = 2 ** 64 / ((
len(payload) + defaults.networkDefaultPayloadLengthExtraBytes + 8 len(payload) + networkDefaultPayloadLengthExtraBytes + 8
) * defaults.networkDefaultProofOfWorkNonceTrialsPerByte) ) * networkDefaultProofOfWorkNonceTrialsPerByte)
logger.info('(For pubkey message via API) Doing proof of work...') logger.info('(For pubkey message via API) Doing proof of work...')
initialHash = hashlib.sha512(payload).digest() initialHash = hashlib.sha512(payload).digest()
trialValue, nonce = proofofwork.run(target, initialHash) trialValue, nonce = proofofwork.run(target, initialHash)
@ -1392,13 +1395,13 @@ class BMRPCDispatcher(object):
inventoryHash = calculateInventoryHash(payload) inventoryHash = calculateInventoryHash(payload)
objectType = 1 # .. todo::: support v4 pubkeys objectType = 1 # .. todo::: support v4 pubkeys
TTL = 28 * 24 * 60 * 60 TTL = 28 * 24 * 60 * 60
Inventory()[inventoryHash] = ( state.Inventory[inventoryHash] = (
objectType, pubkeyStreamNumber, payload, int(time.time()) + TTL, '' objectType, pubkeyStreamNumber, payload, int(time.time()) + TTL, ''
) )
logger.info( logger.info(
'broadcasting inv within API command disseminatePubkey with' 'broadcasting inv within API command disseminatePubkey with'
' hash: %s', hexlify(inventoryHash)) ' hash: %s', hexlify(inventoryHash))
queues.invQueue.put((pubkeyStreamNumber, inventoryHash)) invQueue.put((pubkeyStreamNumber, inventoryHash))
@command( @command(
'getMessageDataByDestinationHash', 'getMessageDataByDestinationTag') 'getMessageDataByDestinationHash', 'getMessageDataByDestinationTag')
@ -1472,18 +1475,18 @@ class BMRPCDispatcher(object):
Returns bitmessage connection information as dict with keys *inbound*, Returns bitmessage connection information as dict with keys *inbound*,
*outbound*. *outbound*.
""" """
if BMConnectionPool is None: if connectionpool is None:
raise APIError(21, 'Could not import BMConnectionPool.') raise APIError(21, 'Could not import BMConnectionPool.')
inboundConnections = [] inboundConnections = []
outboundConnections = [] outboundConnections = []
for i in BMConnectionPool().inboundConnections.values(): for i in connectionpool.pool.inboundConnections.values():
inboundConnections.append({ inboundConnections.append({
'host': i.destination.host, 'host': i.destination.host,
'port': i.destination.port, 'port': i.destination.port,
'fullyEstablished': i.fullyEstablished, 'fullyEstablished': i.fullyEstablished,
'userAgent': str(i.userAgent) 'userAgent': str(i.userAgent)
}) })
for i in BMConnectionPool().outboundConnections.values(): for i in connectionpool.pool.outboundConnections.values():
outboundConnections.append({ outboundConnections.append({
'host': i.destination.host, 'host': i.destination.host,
'port': i.destination.port, 'port': i.destination.port,

View File

@ -30,7 +30,6 @@ import state
from addresses import addBMIfNotPresent, decodeAddress from addresses import addBMIfNotPresent, decodeAddress
from bmconfigparser import config from bmconfigparser import config
from helper_sql import sqlExecute, sqlQuery from helper_sql import sqlExecute, sqlQuery
from inventory import Inventory
# pylint: disable=global-statement # pylint: disable=global-statement
@ -145,8 +144,8 @@ def scrollbox(d, text, height=None, width=None):
def resetlookups(): def resetlookups():
"""Reset the Inventory Lookups""" """Reset the Inventory Lookups"""
global inventorydata global inventorydata
inventorydata = Inventory().numberOfInventoryLookupsPerformed inventorydata = state.Inventory.numberOfInventoryLookupsPerformed
Inventory().numberOfInventoryLookupsPerformed = 0 state.Inventory.numberOfInventoryLookupsPerformed = 0
Timer(1, resetlookups, ()).start() Timer(1, resetlookups, ()).start()

View File

@ -6,14 +6,12 @@ allmail.py
============== ==============
All mails are managed in allmail screen All mails are managed in allmail screen
""" """
import logging
from kivy.clock import Clock from kivy.clock import Clock
from kivy.properties import ( from kivy.properties import ListProperty, StringProperty
ListProperty,
StringProperty
)
from kivy.uix.screenmanager import Screen from kivy.uix.screenmanager import Screen
from kivy.app import App from kivy.app import App
@ -21,47 +19,52 @@ from pybitmessage.bitmessagekivy.baseclass.common import (
show_limited_cnt, empty_screen_label, kivy_state_variables, show_limited_cnt, empty_screen_label, kivy_state_variables,
) )
import logging
logger = logging.getLogger('default') logger = logging.getLogger('default')
class Allmails(Screen): class AllMails(Screen):
"""Allmails Screen for kivy Ui""" """AllMails Screen for Kivy UI"""
data = ListProperty() data = ListProperty()
has_refreshed = True has_refreshed = True
all_mails = ListProperty() all_mails = ListProperty()
account = StringProperty() account = StringProperty()
label_str = 'yet no message for this account!!!!!!!!!!!!!' label_str = 'No messages for this account.'
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
"""Method Parsing the address""" """Initialize the AllMails screen."""
super(Allmails, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs) # pylint: disable=missing-super-argument
self.kivy_state = kivy_state_variables() self.kivy_state = kivy_state_variables()
if self.kivy_state.selected_address == '': self._initialize_selected_address()
if App.get_running_app().identity_list:
self.kivy_state.selected_address = App.get_running_app().identity_list[0]
Clock.schedule_once(self.init_ui, 0) Clock.schedule_once(self.init_ui, 0)
def init_ui(self, dt=0): def _initialize_selected_address(self):
"""Clock Schdule for method all mails""" """Initialize the selected address from the identity list."""
self.loadMessagelist() if not self.kivy_state.selected_address and App.get_running_app().identity_list:
logger.debug(dt) self.kivy_state.selected_address = App.get_running_app().identity_list[0]
def loadMessagelist(self): def init_ui(self, dt=0):
"""Load Inbox, Sent anf Draft list of messages""" """Initialize the UI by loading the message list."""
self.load_message_list()
logger.debug("UI initialized after %s seconds.", dt)
def load_message_list(self):
"""Load the Inbox, Sent, and Draft message lists."""
self.account = self.kivy_state.selected_address self.account = self.kivy_state.selected_address
self.ids.tag_label.text = '' self.ids.tag_label.text = 'All Mails' if self.all_mails else ''
self._update_mail_count()
def _update_mail_count(self):
"""Update the mail count and handle empty states."""
if self.all_mails: if self.all_mails:
self.ids.tag_label.text = 'All Mails' total_count = int(self.kivy_state.sent_count) + int(self.kivy_state.inbox_count)
self.kivy_state.all_count = str( self.kivy_state.all_count = str(total_count)
int(self.kivy_state.sent_count) + int(self.kivy_state.inbox_count)) self.set_all_mail_count(self.kivy_state.all_count)
self.set_AllmailCnt(self.kivy_state.all_count)
else: else:
self.set_AllmailCnt('0') self.set_all_mail_count('0')
self.ids.ml.add_widget(empty_screen_label(self.label_str)) self.ids.ml.add_widget(empty_screen_label(self.label_str))
@staticmethod @staticmethod
def set_AllmailCnt(Count): def set_all_mail_count(count):
"""This method is used to set allmails message count""" """Set the message count for all mails."""
allmailCnt_obj = App.get_running_app().root.ids.content_drawer.ids.allmail_cnt allmail_count_widget = App.get_running_app().root.ids.content_drawer.ids.allmail_cnt
allmailCnt_obj.ids.badge_txt.text = show_limited_cnt(int(Count)) allmail_count_widget.ids.badge_txt.text = show_limited_cnt(int(count))

View File

@ -5,54 +5,50 @@
draft.py draft.py
============== ==============
Draft screen Draft screen for managing draft messages in Kivy UI.
""" """
from kivy.clock import Clock from kivy.clock import Clock
from kivy.properties import ( from kivy.properties import ListProperty, StringProperty
ListProperty,
StringProperty
)
from kivy.uix.screenmanager import Screen from kivy.uix.screenmanager import Screen
from kivy.app import App from kivy.app import App
from pybitmessage.bitmessagekivy.baseclass.common import ( from pybitmessage.bitmessagekivy.baseclass.common import (
show_limited_cnt, empty_screen_label, show_limited_cnt, empty_screen_label, kivy_state_variables
kivy_state_variables
) )
import logging import logging
logger = logging.getLogger('default') logger = logging.getLogger('default')
class Draft(Screen): class Draft(Screen):
"""Draft screen class for kivy Ui""" """Draft screen class for Kivy UI"""
data = ListProperty() data = ListProperty()
account = StringProperty() account = StringProperty()
queryreturn = ListProperty() queryreturn = ListProperty()
has_refreshed = True has_refreshed = True
label_str = "yet no message for this account!!!!!!!!!!!!!" label_str = "Yet no message for this account!"
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
"""Method used for storing draft messages""" """Initialize the Draft screen and set the default account"""
super(Draft, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.kivy_state = kivy_state_variables() self.kivy_state = kivy_state_variables()
if self.kivy_state.selected_address == '': if not self.kivy_state.selected_address:
if App.get_running_app().identity_list: if App.get_running_app().identity_list:
self.kivy_state.selected_address = App.get_running_app().identity_list[0] self.kivy_state.selected_address = App.get_running_app().identity_list[0]
Clock.schedule_once(self.init_ui, 0) Clock.schedule_once(self.init_ui, 0)
def init_ui(self, dt=0): def init_ui(self, dt=0):
"""Clock Schedule for method draft accounts""" """Initialize the UI and load draft messages"""
self.load_draft() self.load_draft()
logger.debug(dt) logger.debug(f"UI initialized with dt: {dt}") # noqa: E999
def load_draft(self, where="", what=""): def load_draft(self, where="", what=""):
"""Load draft list for Draft messages""" """Load the list of draft messages"""
self.set_draft_count('0') self.set_draft_count('0')
self.ids.ml.add_widget(empty_screen_label(self.label_str)) self.ids.ml.add_widget(empty_screen_label(self.label_str))
@staticmethod @staticmethod
def set_draft_count(Count): def set_draft_count(count):
"""Set the count of draft mails""" """Set the count of draft messages in the UI"""
draftCnt_obj = App.get_running_app().root.ids.content_drawer.ids.draft_cnt draft_count_obj = App.get_running_app().root.ids.content_drawer.ids.draft_cnt
draftCnt_obj.ids.badge_txt.text = show_limited_cnt(int(Count)) draft_count_obj.ids.badge_txt.text = show_limited_cnt(int(count))

View File

@ -6,18 +6,14 @@
Kivy UI for inbox screen Kivy UI for inbox screen
""" """
from kivy.clock import Clock from kivy.clock import Clock
from kivy.properties import ( from kivy.properties import ListProperty, StringProperty
ListProperty,
StringProperty
)
from kivy.app import App from kivy.app import App
from kivy.uix.screenmanager import Screen from kivy.uix.screenmanager import Screen
from pybitmessage.bitmessagekivy.baseclass.common import kivy_state_variables, load_image_path from pybitmessage.bitmessagekivy.baseclass.common import kivy_state_variables, load_image_path
class Inbox(Screen): class Inbox(Screen):
"""Inbox Screen class for kivy Ui""" """Inbox Screen class for Kivy UI"""
queryreturn = ListProperty() queryreturn = ListProperty()
has_refreshed = True has_refreshed = True
@ -26,33 +22,32 @@ class Inbox(Screen):
label_str = "Yet no message for this account!" label_str = "Yet no message for this account!"
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
"""Initialize kivy variables""" """Initialize Kivy variables and set up the UI"""
super(Inbox, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs) # pylint: disable=missing-super-argument
self.kivy_running_app = App.get_running_app() self.kivy_running_app = App.get_running_app()
self.kivy_state = kivy_state_variables() self.kivy_state = kivy_state_variables()
self.image_dir = load_image_path() self.image_dir = load_image_path()
Clock.schedule_once(self.init_ui, 0) Clock.schedule_once(self.init_ui, 0)
def set_defaultAddress(self): def set_default_address(self):
"""Set default address""" """Set the default address if none is selected"""
if self.kivy_state.selected_address == "": if not self.kivy_state.selected_address and self.kivy_running_app.identity_list:
if self.kivy_running_app.identity_list: self.kivy_state.selected_address = self.kivy_running_app.identity_list[0]
self.kivy_state.selected_address = self.kivy_running_app.identity_list[0]
def init_ui(self, dt=0): def init_ui(self, dt=0):
"""loadMessagelist() call at specific interval""" """Initialize UI and load message list"""
self.loadMessagelist() self.loadMessagelist()
def loadMessagelist(self, where="", what=""): def loadMessagelist(self, where="", what=""):
"""Load inbox list for inbox messages""" """Load inbox messages"""
self.set_defaultAddress() self.set_default_address()
self.account = self.kivy_state.selected_address self.account = self.kivy_state.selected_address
def refresh_callback(self, *args): def refresh_callback(self, *args):
"""Load inbox messages while wating-loader spins & called in inbox.kv""" """Refresh the inbox messages while showing a loading spinner"""
def refresh_on_scroll_down(interval): def refresh_on_scroll_down(interval):
"""Reset fields and load data on scrolling upside down""" """Reset search fields and reload data on scroll"""
self.kivy_state.searching_text = "" self.kivy_state.searching_text = ""
self.children[2].children[1].ids.search_field.text = "" self.children[2].children[1].ids.search_field.text = ""
self.ids.ml.clear_widgets() self.ids.ml.clear_widgets()

View File

@ -174,7 +174,7 @@ class MailDetail(Screen): # pylint: disable=too-many-instance-attributes
self.parent.screens[3].clear_widgets() self.parent.screens[3].clear_widgets()
self.parent.screens[3].add_widget(Factory.Trash()) self.parent.screens[3].add_widget(Factory.Trash())
self.parent.screens[14].clear_widgets() self.parent.screens[14].clear_widgets()
self.parent.screens[14].add_widget(Factory.Allmails()) self.parent.screens[14].add_widget(Factory.AllMails())
Clock.schedule_once(self.callback_for_delete, 4) Clock.schedule_once(self.callback_for_delete, 4)
def callback_for_delete(self, dt=0): def callback_for_delete(self, dt=0):

View File

@ -14,7 +14,7 @@ from kivy.uix.screenmanager import Screen
from pybitmessage import state from pybitmessage import state
if os.environ.get('INSTALL_TESTS', False) and not state.backend_py3_compatible: if os.environ.get('INSTALL_TESTS', False) and not state.backend_py3_compatible:
from pybitmessage.mock import kivy_main from pybitmessage.mockbm import kivy_main
stats = kivy_main.network.stats stats = kivy_main.network.stats
objectracker = kivy_main.network.objectracker objectracker = kivy_main.network.objectracker
else: else:

View File

@ -40,7 +40,8 @@ def generate_hash(string):
try: try:
# make input case insensitive # make input case insensitive
string = str.lower(string) string = str.lower(string)
hash_object = hashlib.md5(str.encode(string)) # nosec B303 hash_object = hashlib.md5( # nosec B324, B303
str.encode(string))
print(hash_object.hexdigest()) print(hash_object.hexdigest())
# returned object is a hex string # returned object is a hex string
return hash_object.hexdigest() return hash_object.hexdigest()

View File

@ -1,4 +1,4 @@
<Allmails>: <AllMails>:
name: 'allmails' name: 'allmails'
BoxLayout: BoxLayout:
orientation: 'vertical' orientation: 'vertical'

View File

@ -250,7 +250,7 @@ MDNavigationLayout:
id:id_sent id:id_sent
Trash: Trash:
id:id_trash id:id_trash
Allmails: AllMails:
id:id_allmail id:id_allmail
Draft: Draft:
id:id_draft id:id_draft

View File

@ -45,7 +45,7 @@ from pybitmessage.bitmessagekivy.baseclass.popup import (
from pybitmessage.bitmessagekivy.baseclass.login import * # noqa: F401, F403 from pybitmessage.bitmessagekivy.baseclass.login import * # noqa: F401, F403
from pybitmessage.bitmessagekivy.uikivysignaler import UIkivySignaler from pybitmessage.bitmessagekivy.uikivysignaler import UIkivySignaler
from pybitmessage.mock.helper_startup import loadConfig, total_encrypted_messages_per_month from pybitmessage.mockbm.helper_startup import loadConfig, total_encrypted_messages_per_month
logger = logging.getLogger('default') logger = logging.getLogger('default')

View File

@ -59,7 +59,7 @@
"All Mails": { "All Mails": {
"kv_string": "allmails", "kv_string": "allmails",
"name_screen": "allmails", "name_screen": "allmails",
"Import": "from pybitmessage.bitmessagekivy.baseclass.allmail import Allmails" "Import": "from pybitmessage.bitmessagekivy.baseclass.allmail import AllMails"
}, },
"MailDetail": { "MailDetail": {
"kv_string": "maildetail", "kv_string": "maildetail",

View File

@ -7,6 +7,8 @@ import shutil
import tempfile import tempfile
from time import time, sleep from time import time, sleep
from requests.exceptions import ChunkedEncodingError
from telenium.tests import TeleniumTestCase from telenium.tests import TeleniumTestCase
from telenium.client import TeleniumHttpException from telenium.client import TeleniumHttpException
@ -32,7 +34,7 @@ def cleanup(files=_files):
class TeleniumTestProcess(TeleniumTestCase): class TeleniumTestProcess(TeleniumTestCase):
"""Setting Screen Functionality Testing""" """Setting Screen Functionality Testing"""
cmd_entrypoint = [os.path.join(os.path.abspath(os.getcwd()), 'src', 'mock', 'kivy_main.py')] cmd_entrypoint = [os.path.join(os.path.abspath(os.getcwd()), 'src', 'mockbm', 'kivy_main.py')]
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
@ -54,7 +56,10 @@ class TeleniumTestProcess(TeleniumTestCase):
def tearDownClass(cls): def tearDownClass(cls):
"""Ensures that pybitmessage stopped and removes files""" """Ensures that pybitmessage stopped and removes files"""
# pylint: disable=no-member # pylint: disable=no-member
super(TeleniumTestProcess, cls).tearDownClass() try:
super(TeleniumTestProcess, cls).tearDownClass()
except ChunkedEncodingError:
pass
cleanup() cleanup()
def assert_wait_no_except(self, selector, timeout=-1, value='inbox'): def assert_wait_no_except(self, selector, timeout=-1, value='inbox'):

View File

@ -12,6 +12,7 @@ The PyBitmessage startup script
import os import os
import sys import sys
try: try:
import pathmagic import pathmagic
except ImportError: except ImportError:
@ -156,13 +157,6 @@ class Main(object):
set_thread_name("PyBitmessage") set_thread_name("PyBitmessage")
state.dandelion = config.safeGetInt('network', 'dandelion')
# dandelion requires outbound connections, without them,
# stem objects will get stuck forever
if state.dandelion and not config.safeGetBoolean(
'bitmessagesettings', 'sendoutgoingconnections'):
state.dandelion = 0
if state.testmode or config.safeGetBoolean( if state.testmode or config.safeGetBoolean(
'bitmessagesettings', 'extralowdifficulty'): 'bitmessagesettings', 'extralowdifficulty'):
defaults.networkDefaultProofOfWorkNonceTrialsPerByte = int( defaults.networkDefaultProofOfWorkNonceTrialsPerByte = int(
@ -176,8 +170,7 @@ class Main(object):
# The closeEvent should command this thread to exit gracefully. # The closeEvent should command this thread to exit gracefully.
sqlLookup.daemon = False sqlLookup.daemon = False
sqlLookup.start() sqlLookup.start()
state.Inventory = Inventory() # init
Inventory() # init
if state.enableObjProc: # Not needed if objproc is disabled if state.enableObjProc: # Not needed if objproc is disabled
# Start the address generation thread # Start the address generation thread
@ -238,8 +231,7 @@ class Main(object):
upnpThread = upnp.uPnPThread() upnpThread = upnp.uPnPThread()
upnpThread.start() upnpThread.start()
else: else:
# Populate with hardcoded value (same as connectToStream above) network.connectionpool.pool.connectToStream(1)
state.streamsInWhichIAmParticipating.append(1)
if not daemon and state.enableGUI: if not daemon and state.enableGUI:
if state.curses: if state.curses:

View File

@ -62,6 +62,9 @@ except ImportError:
get_plugins = False get_plugins = False
is_windows = sys.platform.startswith('win')
# TODO: rewrite # TODO: rewrite
def powQueueSize(): def powQueueSize():
"""Returns the size of queues.workerQueue including current unfinished work""" """Returns the size of queues.workerQueue including current unfinished work"""
@ -80,7 +83,7 @@ def openKeysFile():
keysfile = os.path.join(state.appdata, 'keys.dat') keysfile = os.path.join(state.appdata, 'keys.dat')
if 'linux' in sys.platform: if 'linux' in sys.platform:
subprocess.call(["xdg-open", keysfile]) subprocess.call(["xdg-open", keysfile])
elif sys.platform.startswith('win'): elif is_windows:
os.startfile(keysfile) # pylint: disable=no-member os.startfile(keysfile) # pylint: disable=no-member
@ -868,7 +871,7 @@ class MyForm(settingsmixin.SMainWindow):
""" """
startonlogon = config.safeGetBoolean( startonlogon = config.safeGetBoolean(
'bitmessagesettings', 'startonlogon') 'bitmessagesettings', 'startonlogon')
if sys.platform.startswith('win'): # Auto-startup for Windows if is_windows: # Auto-startup for Windows
RUN_PATH = "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run" RUN_PATH = "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run"
settings = QtCore.QSettings( settings = QtCore.QSettings(
RUN_PATH, QtCore.QSettings.NativeFormat) RUN_PATH, QtCore.QSettings.NativeFormat)
@ -4233,6 +4236,14 @@ class BitmessageQtApplication(QtGui.QApplication):
# Unique identifier for this application # Unique identifier for this application
uuid = '6ec0149b-96e1-4be1-93ab-1465fb3ebf7c' uuid = '6ec0149b-96e1-4be1-93ab-1465fb3ebf7c'
@staticmethod
def get_windowstyle():
"""Get window style set in config or default"""
return config.safeGet(
'bitmessagesettings', 'windowstyle',
'Windows' if is_windows else 'GTK+'
)
def __init__(self, *argv): def __init__(self, *argv):
super(BitmessageQtApplication, self).__init__(*argv) super(BitmessageQtApplication, self).__init__(*argv)
id = BitmessageQtApplication.uuid id = BitmessageQtApplication.uuid
@ -4241,6 +4252,14 @@ class BitmessageQtApplication(QtGui.QApplication):
QtCore.QCoreApplication.setOrganizationDomain("bitmessage.org") QtCore.QCoreApplication.setOrganizationDomain("bitmessage.org")
QtCore.QCoreApplication.setApplicationName("pybitmessageqt") QtCore.QCoreApplication.setApplicationName("pybitmessageqt")
self.setStyle(self.get_windowstyle())
font = config.safeGet('bitmessagesettings', 'font')
if font:
# family, size, weight = font.split(',')
family, size = font.split(',')
self.setFont(QtGui.QFont(family, int(size)))
self.server = None self.server = None
self.is_running = False self.is_running = False

View File

@ -9,10 +9,10 @@ from PyQt4 import QtCore, QtGui
import queues import queues
import widgets import widgets
import state
from account import AccountMixin, GatewayAccount, MailchuckAccount, accountClass from account import AccountMixin, GatewayAccount, MailchuckAccount, accountClass
from addresses import addBMIfNotPresent, decodeAddress, encodeVarint from addresses import addBMIfNotPresent, decodeAddress, encodeVarint
from bmconfigparser import config as global_config from bmconfigparser import config as global_config
from inventory import Inventory
from tr import _translate from tr import _translate
@ -190,13 +190,13 @@ class NewSubscriptionDialog(AddressDataDialog):
" broadcasts." " broadcasts."
)) ))
else: else:
Inventory().flush() state.Inventory.flush()
doubleHashOfAddressData = hashlib.sha512(hashlib.sha512( doubleHashOfAddressData = hashlib.sha512(hashlib.sha512(
encodeVarint(addressVersion) encodeVarint(addressVersion)
+ encodeVarint(streamNumber) + ripe + encodeVarint(streamNumber) + ripe
).digest()).digest() ).digest()).digest()
tag = doubleHashOfAddressData[32:] tag = doubleHashOfAddressData[32:]
self.recent = Inventory().by_type_and_tag(3, tag) self.recent = state.Inventory.by_type_and_tag(3, tag)
count = len(self.recent) count = len(self.recent)
if count == 0: if count == 0:
self.checkBoxDisplayMessagesAlreadyInInventory.setText( self.checkBoxDisplayMessagesAlreadyInInventory.setText(

View File

@ -61,7 +61,8 @@ class Ui_MainWindow(object):
self.tabWidget.setMinimumSize(QtCore.QSize(0, 0)) self.tabWidget.setMinimumSize(QtCore.QSize(0, 0))
self.tabWidget.setBaseSize(QtCore.QSize(0, 0)) self.tabWidget.setBaseSize(QtCore.QSize(0, 0))
font = QtGui.QFont() font = QtGui.QFont()
font.setPointSize(9) base_size = QtGui.QApplication.instance().font().pointSize()
font.setPointSize(int(base_size * 0.75))
self.tabWidget.setFont(font) self.tabWidget.setFont(font)
self.tabWidget.setTabPosition(QtGui.QTabWidget.North) self.tabWidget.setTabPosition(QtGui.QTabWidget.North)
self.tabWidget.setTabShape(QtGui.QTabWidget.Rounded) self.tabWidget.setTabShape(QtGui.QTabWidget.Rounded)

View File

@ -10,8 +10,7 @@ import l10n
import network.stats import network.stats
import state import state
import widgets import widgets
from inventory import Inventory from network import connectionpool, knownnodes
from network import BMConnectionPool, knownnodes
from retranslateui import RetranslateMixin from retranslateui import RetranslateMixin
from tr import _translate from tr import _translate
from uisignaler import UISignaler from uisignaler import UISignaler
@ -50,7 +49,7 @@ class NetworkStatus(QtGui.QWidget, RetranslateMixin):
def startUpdate(self): def startUpdate(self):
"""Start a timer to update counters every 2 seconds""" """Start a timer to update counters every 2 seconds"""
Inventory().numberOfInventoryLookupsPerformed = 0 state.Inventory.numberOfInventoryLookupsPerformed = 0
self.runEveryTwoSeconds() self.runEveryTwoSeconds()
self.timer.start(2000) # milliseconds self.timer.start(2000) # milliseconds
@ -149,16 +148,16 @@ class NetworkStatus(QtGui.QWidget, RetranslateMixin):
# pylint: disable=too-many-branches,undefined-variable # pylint: disable=too-many-branches,undefined-variable
if outbound: if outbound:
try: try:
c = BMConnectionPool().outboundConnections[destination] c = connectionpool.pool.outboundConnections[destination]
except KeyError: except KeyError:
if add: if add:
return return
else: else:
try: try:
c = BMConnectionPool().inboundConnections[destination] c = connectionpool.pool.inboundConnections[destination]
except KeyError: except KeyError:
try: try:
c = BMConnectionPool().inboundConnections[destination.host] c = connectionpool.pool.inboundConnections[destination.host]
except KeyError: except KeyError:
if add: if add:
return return
@ -202,7 +201,7 @@ class NetworkStatus(QtGui.QWidget, RetranslateMixin):
self.tableWidgetConnectionCount.item(0, 0).setData(QtCore.Qt.UserRole, destination) self.tableWidgetConnectionCount.item(0, 0).setData(QtCore.Qt.UserRole, destination)
self.tableWidgetConnectionCount.item(0, 1).setData(QtCore.Qt.UserRole, outbound) self.tableWidgetConnectionCount.item(0, 1).setData(QtCore.Qt.UserRole, outbound)
else: else:
if not BMConnectionPool().inboundConnections: if not connectionpool.pool.inboundConnections:
self.window().setStatusIcon('yellow') self.window().setStatusIcon('yellow')
for i in range(self.tableWidgetConnectionCount.rowCount()): for i in range(self.tableWidgetConnectionCount.rowCount()):
if self.tableWidgetConnectionCount.item(i, 0).data(QtCore.Qt.UserRole).toPyObject() != destination: if self.tableWidgetConnectionCount.item(i, 0).data(QtCore.Qt.UserRole).toPyObject() != destination:
@ -229,8 +228,8 @@ class NetworkStatus(QtGui.QWidget, RetranslateMixin):
def runEveryTwoSeconds(self): def runEveryTwoSeconds(self):
"""Updates counters, runs every 2 seconds if the timer is running""" """Updates counters, runs every 2 seconds if the timer is running"""
self.labelLookupsPerSecond.setText(_translate("networkstatus", "Inventory lookups per second: %1").arg( self.labelLookupsPerSecond.setText(_translate("networkstatus", "Inventory lookups per second: %1").arg(
str(Inventory().numberOfInventoryLookupsPerformed / 2))) str(state.Inventory.numberOfInventoryLookupsPerformed / 2)))
Inventory().numberOfInventoryLookupsPerformed = 0 state.Inventory.numberOfInventoryLookupsPerformed = 0
self.updateNumberOfBytes() self.updateNumberOfBytes()
self.updateNumberOfObjectsToBeSynced() self.updateNumberOfObjectsToBeSynced()

View File

@ -20,7 +20,8 @@ import widgets
from bmconfigparser import config as config_obj from bmconfigparser import config as config_obj
from helper_sql import sqlExecute, sqlStoredProcedure from helper_sql import sqlExecute, sqlStoredProcedure
from helper_startup import start_proxyconfig from helper_startup import start_proxyconfig
from network import knownnodes, AnnounceThread from network import connectionpool, knownnodes
from network.announcethread import AnnounceThread
from network.asyncore_pollchoose import set_rates from network.asyncore_pollchoose import set_rates
from tr import _translate from tr import _translate
@ -40,14 +41,17 @@ def getSOCKSProxyType(config):
class SettingsDialog(QtGui.QDialog): class SettingsDialog(QtGui.QDialog):
"""The "Settings" dialog""" """The "Settings" dialog"""
# pylint: disable=too-many-instance-attributes
def __init__(self, parent=None, firstrun=False): def __init__(self, parent=None, firstrun=False):
super(SettingsDialog, self).__init__(parent) super(SettingsDialog, self).__init__(parent)
widgets.load('settings.ui', self) widgets.load('settings.ui', self)
self.app = QtGui.QApplication.instance()
self.parent = parent self.parent = parent
self.firstrun = firstrun self.firstrun = firstrun
self.config = config_obj self.config = config_obj
self.net_restart_needed = False self.net_restart_needed = False
self.font_setting = None
self.timer = QtCore.QTimer() self.timer = QtCore.QTimer()
if self.config.safeGetBoolean('bitmessagesettings', 'dontconnect'): if self.config.safeGetBoolean('bitmessagesettings', 'dontconnect'):
@ -84,6 +88,15 @@ class SettingsDialog(QtGui.QDialog):
def adjust_from_config(self, config): def adjust_from_config(self, config):
"""Adjust all widgets state according to config settings""" """Adjust all widgets state according to config settings"""
# pylint: disable=too-many-branches,too-many-statements # pylint: disable=too-many-branches,too-many-statements
current_style = self.app.get_windowstyle()
for i, sk in enumerate(QtGui.QStyleFactory.keys()):
self.comboBoxStyle.addItem(sk)
if sk == current_style:
self.comboBoxStyle.setCurrentIndex(i)
self.save_font_setting(self.app.font())
if not self.parent.tray.isSystemTrayAvailable(): if not self.parent.tray.isSystemTrayAvailable():
self.groupBoxTray.setEnabled(False) self.groupBoxTray.setEnabled(False)
self.groupBoxTray.setTitle(_translate( self.groupBoxTray.setTitle(_translate(
@ -136,7 +149,7 @@ class SettingsDialog(QtGui.QDialog):
"MainWindow", "MainWindow",
"Tray notifications not yet supported on your OS.")) "Tray notifications not yet supported on your OS."))
if 'win' not in sys.platform and not self.parent.desktop: if not sys.platform.startswith('win') and not self.parent.desktop:
self.checkBoxStartOnLogon.setDisabled(True) self.checkBoxStartOnLogon.setDisabled(True)
self.checkBoxStartOnLogon.setText(_translate( self.checkBoxStartOnLogon.setText(_translate(
"MainWindow", "Start-on-login not yet supported on your OS.")) "MainWindow", "Start-on-login not yet supported on your OS."))
@ -164,7 +177,7 @@ class SettingsDialog(QtGui.QDialog):
if self._proxy_type: if self._proxy_type:
for node, info in six.iteritems( for node, info in six.iteritems(
knownnodes.knownNodes.get( knownnodes.knownNodes.get(
min(state.streamsInWhichIAmParticipating), []) min(connectionpool.pool.streams), [])
): ):
if ( if (
node.host.endswith('.onion') and len(node.host) > 22 node.host.endswith('.onion') and len(node.host) > 22
@ -321,6 +334,18 @@ class SettingsDialog(QtGui.QDialog):
if status == 'success': if status == 'success':
self.parent.namecoin = nc self.parent.namecoin = nc
def save_font_setting(self, font):
"""Save user font setting and set the buttonFont text"""
font_setting = (font.family(), font.pointSize())
self.buttonFont.setText('{} {}'.format(*font_setting))
self.font_setting = '{},{}'.format(*font_setting)
def choose_font(self):
"""Show the font selection dialog"""
font, valid = QtGui.QFontDialog.getFont()
if valid:
self.save_font_setting(font)
def accept(self): def accept(self):
"""A callback for accepted event of buttonBox (OK button pressed)""" """A callback for accepted event of buttonBox (OK button pressed)"""
# pylint: disable=too-many-branches,too-many-statements # pylint: disable=too-many-branches,too-many-statements
@ -347,6 +372,20 @@ class SettingsDialog(QtGui.QDialog):
self.config.set('bitmessagesettings', 'replybelow', str( self.config.set('bitmessagesettings', 'replybelow', str(
self.checkBoxReplyBelow.isChecked())) self.checkBoxReplyBelow.isChecked()))
window_style = str(self.comboBoxStyle.currentText())
if self.app.get_windowstyle() != window_style or self.config.safeGet(
'bitmessagesettings', 'font'
) != self.font_setting:
self.config.set('bitmessagesettings', 'windowstyle', window_style)
self.config.set('bitmessagesettings', 'font', self.font_setting)
queues.UISignalQueue.put((
'updateStatusBar', (
_translate(
"MainWindow",
"You need to restart the application to apply"
" the window style or default font."), 1)
))
lang = str(self.languageComboBox.itemData( lang = str(self.languageComboBox.itemData(
self.languageComboBox.currentIndex()).toString()) self.languageComboBox.currentIndex()).toString())
self.config.set('bitmessagesettings', 'userlocale', lang) self.config.set('bitmessagesettings', 'userlocale', lang)

View File

@ -147,6 +147,32 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="9" column="0">
<widget class="QGroupBox" name="groupBoxStyle">
<property name="title">
<string>Custom Style</string>
</property>
<layout class="QHBoxLayout">
<item>
<widget class="QComboBox" name="comboBoxStyle">
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonFont">
<property name="text">
<string>Font</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="9" column="1"> <item row="9" column="1">
<widget class="QGroupBox" name="groupBox"> <widget class="QGroupBox" name="groupBox">
<property name="title"> <property name="title">
@ -1202,5 +1228,11 @@
</hint> </hint>
</hints> </hints>
</connection> </connection>
<connection>
<sender>buttonFont</sender>
<signal>clicked()</signal>
<receiver>settingsDialog</receiver>
<slot>choose_font</slot>
</connection>
</connections> </connections>
</ui> </ui>

View File

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

View File

@ -1,19 +1,23 @@
"""Common definitions for bitmessageqt tests""" """Common definitions for bitmessageqt tests"""
import Queue
import sys import sys
import unittest import unittest
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
from six.moves import queue
import bitmessageqt import bitmessageqt
import queues from bitmessageqt import _translate, config, queues
from tr import _translate
class TestBase(unittest.TestCase): class TestBase(unittest.TestCase):
"""Base class for bitmessageqt test case""" """Base class for bitmessageqt test case"""
@classmethod
def setUpClass(cls):
"""Provide the UI test cases with common settings"""
cls.config = config
def setUp(self): def setUp(self):
self.app = ( self.app = (
QtGui.QApplication.instance() QtGui.QApplication.instance()
@ -24,14 +28,21 @@ class TestBase(unittest.TestCase):
self.window.appIndicatorInit(self.app) self.window.appIndicatorInit(self.app)
def tearDown(self): def tearDown(self):
"""Search for exceptions in closures called by timer and fail if any"""
# self.app.deleteLater() # self.app.deleteLater()
concerning = []
while True: while True:
try: try:
thread, exc = queues.excQueue.get(block=False) thread, exc = queues.excQueue.get(block=False)
except Queue.Empty: except queue.Empty:
return break
if thread == 'tests': if thread == 'tests':
self.fail('Exception in the main thread: %s' % exc) concerning.append(exc)
if concerning:
self.fail(
'Exceptions found in the main thread:\n%s' % '\n'.join((
str(e) for e in concerning
)))
class TestMain(unittest.TestCase): class TestMain(unittest.TestCase):

View File

@ -1,10 +1,14 @@
"""Tests for PyBitmessage settings"""
import threading import threading
import time import time
from main import TestBase from PyQt4 import QtCore, QtGui, QtTest
from bmconfigparser import config from bmconfigparser import config
from bitmessageqt import settings from bitmessageqt import settings
from .main import TestBase
class TestSettings(TestBase): class TestSettings(TestBase):
"""A test case for the "Settings" dialog""" """A test case for the "Settings" dialog"""
@ -14,8 +18,7 @@ class TestSettings(TestBase):
def test_udp(self): def test_udp(self):
"""Test the effect of checkBoxUDP""" """Test the effect of checkBoxUDP"""
udp_setting = config.safeGetBoolean( udp_setting = config.safeGetBoolean('bitmessagesettings', 'udp')
'bitmessagesettings', 'udp')
self.assertEqual(udp_setting, self.dialog.checkBoxUDP.isChecked()) self.assertEqual(udp_setting, self.dialog.checkBoxUDP.isChecked())
self.dialog.checkBoxUDP.setChecked(not udp_setting) self.dialog.checkBoxUDP.setChecked(not udp_setting)
self.dialog.accept() self.dialog.accept()
@ -32,3 +35,44 @@ class TestSettings(TestBase):
else: else:
if not udp_setting: if not udp_setting:
self.fail('No Announcer thread found while udp set to True') self.fail('No Announcer thread found while udp set to True')
def test_styling(self):
"""Test custom windows style and font"""
style_setting = config.safeGet('bitmessagesettings', 'windowstyle')
font_setting = config.safeGet('bitmessagesettings', 'font')
self.assertIs(style_setting, None)
self.assertIs(font_setting, None)
style_control = self.dialog.comboBoxStyle
self.assertEqual(
style_control.currentText(), self.app.get_windowstyle())
def call_font_dialog():
"""A function to get the open font dialog and accept it"""
font_dialog = QtGui.QApplication.activeModalWidget()
self.assertTrue(isinstance(font_dialog, QtGui.QFontDialog))
selected_font = font_dialog.currentFont()
self.assertEqual(
config.safeGet('bitmessagesettings', 'font'), '{},{}'.format(
selected_font.family(), selected_font.pointSize()))
font_dialog.accept()
self.dialog.accept()
self.assertEqual(
config.safeGet('bitmessagesettings', 'windowstyle'),
style_control.currentText())
def click_font_button():
"""Use QtTest to click the button"""
QtTest.QTest.mouseClick(
self.dialog.buttonFont, QtCore.Qt.LeftButton)
style_count = style_control.count()
self.assertGreater(style_count, 1)
for i in range(style_count):
if i != style_control.currentIndex():
style_control.setCurrentIndex(i)
break
QtCore.QTimer.singleShot(30, click_font_button)
QtCore.QTimer.singleShot(60, call_font_dialog)
time.sleep(2)

View File

@ -1,7 +1,7 @@
""" """
A thread for creating addresses A thread for creating addresses
""" """
import hashlib
import time import time
from binascii import hexlify from binascii import hexlify
@ -14,10 +14,7 @@ import shared
import state import state
from addresses import decodeAddress, encodeAddress, encodeVarint from addresses import decodeAddress, encodeAddress, encodeVarint
from bmconfigparser import config from bmconfigparser import config
from fallback import RIPEMD160Hash
from network import StoppableThread from network import StoppableThread
from pyelliptic import arithmetic
from pyelliptic.openssl import OpenSSL
from tr import _translate from tr import _translate
@ -130,18 +127,13 @@ class addressGenerator(StoppableThread):
# the \x00 or \x00\x00 bytes thus making the address shorter. # the \x00 or \x00\x00 bytes thus making the address shorter.
startTime = time.time() startTime = time.time()
numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix = 0 numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix = 0
potentialPrivSigningKey = OpenSSL.rand(32) privSigningKey, pubSigningKey = highlevelcrypto.random_keys()
potentialPubSigningKey = highlevelcrypto.pointMult(
potentialPrivSigningKey)
while True: while True:
numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix += 1 numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix += 1
potentialPrivEncryptionKey = OpenSSL.rand(32) potentialPrivEncryptionKey, potentialPubEncryptionKey = \
potentialPubEncryptionKey = highlevelcrypto.pointMult( highlevelcrypto.random_keys()
potentialPrivEncryptionKey) ripe = highlevelcrypto.to_ripe(
sha = hashlib.new('sha512') pubSigningKey, potentialPubEncryptionKey)
sha.update(
potentialPubSigningKey + potentialPubEncryptionKey)
ripe = RIPEMD160Hash(sha.digest()).digest()
if ( if (
ripe[:numberOfNullBytesDemandedOnFrontOfRipeHash] ripe[:numberOfNullBytesDemandedOnFrontOfRipeHash]
== b'\x00' * numberOfNullBytesDemandedOnFrontOfRipeHash == b'\x00' * numberOfNullBytesDemandedOnFrontOfRipeHash
@ -164,20 +156,10 @@ class addressGenerator(StoppableThread):
address = encodeAddress( address = encodeAddress(
addressVersionNumber, streamNumber, ripe) addressVersionNumber, streamNumber, ripe)
# An excellent way for us to store our keys privSigningKeyWIF = highlevelcrypto.encodeWalletImportFormat(
# is in Wallet Import Format. Let us convert now. privSigningKey)
# https://en.bitcoin.it/wiki/Wallet_import_format privEncryptionKeyWIF = highlevelcrypto.encodeWalletImportFormat(
privSigningKey = b'\x80' + potentialPrivSigningKey potentialPrivEncryptionKey)
checksum = hashlib.sha256(hashlib.sha256(
privSigningKey).digest()).digest()[0:4]
privSigningKeyWIF = arithmetic.changebase(
privSigningKey + checksum, 256, 58)
privEncryptionKey = b'\x80' + potentialPrivEncryptionKey
checksum = hashlib.sha256(hashlib.sha256(
privEncryptionKey).digest()).digest()[0:4]
privEncryptionKeyWIF = arithmetic.changebase(
privEncryptionKey + checksum, 256, 58)
config.add_section(address) config.add_section(address)
config.set(address, 'label', label) config.set(address, 'label', label)
@ -249,24 +231,19 @@ class addressGenerator(StoppableThread):
numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix = 0 numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix = 0
while True: while True:
numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix += 1 numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix += 1
potentialPrivSigningKey = hashlib.sha512( potentialPrivSigningKey, potentialPubSigningKey = \
deterministicPassphrase highlevelcrypto.deterministic_keys(
+ encodeVarint(signingKeyNonce) deterministicPassphrase,
).digest()[:32] encodeVarint(signingKeyNonce))
potentialPrivEncryptionKey = hashlib.sha512( potentialPrivEncryptionKey, potentialPubEncryptionKey = \
deterministicPassphrase highlevelcrypto.deterministic_keys(
+ encodeVarint(encryptionKeyNonce) deterministicPassphrase,
).digest()[:32] encodeVarint(encryptionKeyNonce))
potentialPubSigningKey = highlevelcrypto.pointMult(
potentialPrivSigningKey)
potentialPubEncryptionKey = highlevelcrypto.pointMult(
potentialPrivEncryptionKey)
signingKeyNonce += 2 signingKeyNonce += 2
encryptionKeyNonce += 2 encryptionKeyNonce += 2
sha = hashlib.new('sha512') ripe = highlevelcrypto.to_ripe(
sha.update( potentialPubSigningKey, potentialPubEncryptionKey)
potentialPubSigningKey + potentialPubEncryptionKey)
ripe = RIPEMD160Hash(sha.digest()).digest()
if ( if (
ripe[:numberOfNullBytesDemandedOnFrontOfRipeHash] ripe[:numberOfNullBytesDemandedOnFrontOfRipeHash]
== b'\x00' * numberOfNullBytesDemandedOnFrontOfRipeHash == b'\x00' * numberOfNullBytesDemandedOnFrontOfRipeHash
@ -303,21 +280,12 @@ class addressGenerator(StoppableThread):
saveAddressToDisk = False saveAddressToDisk = False
if saveAddressToDisk and live: if saveAddressToDisk and live:
# An excellent way for us to store our keys is privSigningKeyWIF = \
# in Wallet Import Format. Let us convert now. highlevelcrypto.encodeWalletImportFormat(
# https://en.bitcoin.it/wiki/Wallet_import_format potentialPrivSigningKey)
privSigningKey = b'\x80' + potentialPrivSigningKey privEncryptionKeyWIF = \
checksum = hashlib.sha256(hashlib.sha256( highlevelcrypto.encodeWalletImportFormat(
privSigningKey).digest()).digest()[0:4] potentialPrivEncryptionKey)
privSigningKeyWIF = arithmetic.changebase(
privSigningKey + checksum, 256, 58)
privEncryptionKey = b'\x80' + \
potentialPrivEncryptionKey
checksum = hashlib.sha256(hashlib.sha256(
privEncryptionKey).digest()).digest()[0:4]
privEncryptionKeyWIF = arithmetic.changebase(
privEncryptionKey + checksum, 256, 58)
try: try:
config.add_section(address) config.add_section(address)
@ -369,10 +337,10 @@ class addressGenerator(StoppableThread):
highlevelcrypto.makeCryptor( highlevelcrypto.makeCryptor(
hexlify(potentialPrivEncryptionKey)) hexlify(potentialPrivEncryptionKey))
shared.myAddressesByHash[ripe] = address shared.myAddressesByHash[ripe] = address
tag = hashlib.sha512(hashlib.sha512( tag = highlevelcrypto.double_sha512(
encodeVarint(addressVersionNumber) encodeVarint(addressVersionNumber)
+ encodeVarint(streamNumber) + ripe + encodeVarint(streamNumber) + ripe
).digest()).digest()[32:] )[32:]
shared.myAddressesByTag[tag] = address shared.myAddressesByTag[tag] = address
if addressVersionNumber == 3: if addressVersionNumber == 3:
# If this is a chan address, # If this is a chan address,

View File

@ -24,14 +24,13 @@ import queues
import shared import shared
import state import state
from addresses import ( from addresses import (
calculateInventoryHash, decodeAddress, decodeVarint, decodeAddress, decodeVarint,
encodeAddress, encodeVarint, varintDecodeError encodeAddress, encodeVarint, varintDecodeError
) )
from bmconfigparser import config from bmconfigparser import config
from fallback import RIPEMD160Hash
from helper_sql import ( from helper_sql import (
sql_ready, sql_timeout, SqlBulkExecute, sqlExecute, sqlQuery) sql_ready, sql_timeout, SqlBulkExecute, sqlExecute, sqlQuery)
from network import bmproto, knownnodes from network import knownnodes, invQueue
from network.node import Peer from network.node import Peer
from tr import _translate from tr import _translate
@ -64,7 +63,6 @@ class objectProcessor(threading.Thread):
logger.debug( logger.debug(
'Loaded %s objects from disk into the objectProcessorQueue.', 'Loaded %s objects from disk into the objectProcessorQueue.',
len(queryreturn)) len(queryreturn))
self._ack_obj = bmproto.BMStringParser()
self.successfullyDecryptMessageTimings = [] self.successfullyDecryptMessageTimings = []
def run(self): def run(self):
@ -301,23 +299,20 @@ class objectProcessor(threading.Thread):
'(within processpubkey) payloadLength less than 146.' '(within processpubkey) payloadLength less than 146.'
' Sanity check failed.') ' Sanity check failed.')
readPosition += 4 readPosition += 4
publicSigningKey = data[readPosition:readPosition + 64] pubSigningKey = '\x04' + data[readPosition:readPosition + 64]
# Is it possible for a public key to be invalid such that trying to # Is it possible for a public key to be invalid such that trying to
# encrypt or sign with it will cause an error? If it is, it would # encrypt or sign with it will cause an error? If it is, it would
# be easiest to test them here. # be easiest to test them here.
readPosition += 64 readPosition += 64
publicEncryptionKey = data[readPosition:readPosition + 64] pubEncryptionKey = '\x04' + data[readPosition:readPosition + 64]
if len(publicEncryptionKey) < 64: if len(pubEncryptionKey) < 65:
return logger.debug( return logger.debug(
'publicEncryptionKey length less than 64. Sanity check' 'publicEncryptionKey length less than 64. Sanity check'
' failed.') ' failed.')
readPosition += 64 readPosition += 64
# The data we'll store in the pubkeys table. # The data we'll store in the pubkeys table.
dataToStore = data[20:readPosition] dataToStore = data[20:readPosition]
sha = hashlib.new('sha512') ripe = highlevelcrypto.to_ripe(pubSigningKey, pubEncryptionKey)
sha.update(
'\x04' + publicSigningKey + '\x04' + publicEncryptionKey)
ripe = RIPEMD160Hash(sha.digest()).digest()
if logger.isEnabledFor(logging.DEBUG): if logger.isEnabledFor(logging.DEBUG):
logger.debug( logger.debug(
@ -325,7 +320,7 @@ class objectProcessor(threading.Thread):
'\nripe %s\npublicSigningKey in hex: %s' '\nripe %s\npublicSigningKey in hex: %s'
'\npublicEncryptionKey in hex: %s', '\npublicEncryptionKey in hex: %s',
addressVersion, streamNumber, hexlify(ripe), addressVersion, streamNumber, hexlify(ripe),
hexlify(publicSigningKey), hexlify(publicEncryptionKey) hexlify(pubSigningKey), hexlify(pubEncryptionKey)
) )
address = encodeAddress(addressVersion, streamNumber, ripe) address = encodeAddress(addressVersion, streamNumber, ripe)
@ -355,9 +350,9 @@ class objectProcessor(threading.Thread):
' Sanity check failed.') ' Sanity check failed.')
return return
readPosition += 4 readPosition += 4
publicSigningKey = '\x04' + data[readPosition:readPosition + 64] pubSigningKey = '\x04' + data[readPosition:readPosition + 64]
readPosition += 64 readPosition += 64
publicEncryptionKey = '\x04' + data[readPosition:readPosition + 64] pubEncryptionKey = '\x04' + data[readPosition:readPosition + 64]
readPosition += 64 readPosition += 64
specifiedNonceTrialsPerByteLength = decodeVarint( specifiedNonceTrialsPerByteLength = decodeVarint(
data[readPosition:readPosition + 10])[1] data[readPosition:readPosition + 10])[1]
@ -374,15 +369,13 @@ class objectProcessor(threading.Thread):
signature = data[readPosition:readPosition + signatureLength] signature = data[readPosition:readPosition + signatureLength]
if highlevelcrypto.verify( if highlevelcrypto.verify(
data[8:endOfSignedDataPosition], data[8:endOfSignedDataPosition],
signature, hexlify(publicSigningKey)): signature, hexlify(pubSigningKey)):
logger.debug('ECDSA verify passed (within processpubkey)') logger.debug('ECDSA verify passed (within processpubkey)')
else: else:
logger.warning('ECDSA verify failed (within processpubkey)') logger.warning('ECDSA verify failed (within processpubkey)')
return return
sha = hashlib.new('sha512') ripe = highlevelcrypto.to_ripe(pubSigningKey, pubEncryptionKey)
sha.update(publicSigningKey + publicEncryptionKey)
ripe = RIPEMD160Hash(sha.digest()).digest()
if logger.isEnabledFor(logging.DEBUG): if logger.isEnabledFor(logging.DEBUG):
logger.debug( logger.debug(
@ -390,7 +383,7 @@ class objectProcessor(threading.Thread):
'\nripe %s\npublicSigningKey in hex: %s' '\nripe %s\npublicSigningKey in hex: %s'
'\npublicEncryptionKey in hex: %s', '\npublicEncryptionKey in hex: %s',
addressVersion, streamNumber, hexlify(ripe), addressVersion, streamNumber, hexlify(ripe),
hexlify(publicSigningKey), hexlify(publicEncryptionKey) hexlify(pubSigningKey), hexlify(pubEncryptionKey)
) )
address = encodeAddress(addressVersion, streamNumber, ripe) address = encodeAddress(addressVersion, streamNumber, ripe)
@ -456,7 +449,7 @@ class objectProcessor(threading.Thread):
streamNumberAsClaimedByMsg, streamNumberAsClaimedByMsgLength = \ streamNumberAsClaimedByMsg, streamNumberAsClaimedByMsgLength = \
decodeVarint(data[readPosition:readPosition + 9]) decodeVarint(data[readPosition:readPosition + 9])
readPosition += streamNumberAsClaimedByMsgLength readPosition += streamNumberAsClaimedByMsgLength
inventoryHash = calculateInventoryHash(data) inventoryHash = highlevelcrypto.calculateInventoryHash(data)
initialDecryptionSuccessful = False initialDecryptionSuccessful = False
# This is not an acknowledgement bound for me. See if it is a message # This is not an acknowledgement bound for me. See if it is a message
@ -586,13 +579,10 @@ class objectProcessor(threading.Thread):
helper_bitcoin.calculateTestnetAddressFromPubkey(pubSigningKey) helper_bitcoin.calculateTestnetAddressFromPubkey(pubSigningKey)
) )
# Used to detect and ignore duplicate messages in our inbox # Used to detect and ignore duplicate messages in our inbox
sigHash = hashlib.sha512( sigHash = highlevelcrypto.double_sha512(signature)[32:]
hashlib.sha512(signature).digest()).digest()[32:]
# calculate the fromRipe. # calculate the fromRipe.
sha = hashlib.new('sha512') ripe = highlevelcrypto.to_ripe(pubSigningKey, pubEncryptionKey)
sha.update(pubSigningKey + pubEncryptionKey)
ripe = RIPEMD160Hash(sha.digest()).digest()
fromAddress = encodeAddress( fromAddress = encodeAddress(
sendersAddressVersionNumber, sendersStreamNumber, ripe) sendersAddressVersionNumber, sendersStreamNumber, ripe)
@ -686,8 +676,7 @@ class objectProcessor(threading.Thread):
apiNotifyPath = config.safeGet( apiNotifyPath = config.safeGet(
'bitmessagesettings', 'apinotifypath') 'bitmessagesettings', 'apinotifypath')
if apiNotifyPath: if apiNotifyPath:
subprocess.call( # nosec B603 subprocess.call([apiNotifyPath, "newMessage"]) # nosec B603
[apiNotifyPath, "newMessage"])
# Let us now check and see whether our receiving address is # Let us now check and see whether our receiving address is
# behaving as a mailing list # behaving as a mailing list
@ -734,7 +723,13 @@ class objectProcessor(threading.Thread):
and not config.safeGetBoolean(toAddress, 'dontsendack') and not config.safeGetBoolean(toAddress, 'dontsendack')
and not config.safeGetBoolean(toAddress, 'chan') and not config.safeGetBoolean(toAddress, 'chan')
): ):
self._ack_obj.send_data(ackData[24:]) ackPayload = ackData[24:]
objectType, toStreamNumber, expiresTime = \
protocol.decodeObjectParameters(ackPayload)
inventoryHash = highlevelcrypto.calculateInventoryHash(ackPayload)
state.Inventory[inventoryHash] = (
objectType, toStreamNumber, ackPayload, expiresTime, b'')
invQueue.put((toStreamNumber, inventoryHash))
# Display timing data # Display timing data
timeRequiredToAttemptToDecryptMessage = time.time( timeRequiredToAttemptToDecryptMessage = time.time(
@ -758,7 +753,7 @@ class objectProcessor(threading.Thread):
state.numberOfBroadcastsProcessed += 1 state.numberOfBroadcastsProcessed += 1
queues.UISignalQueue.put(( queues.UISignalQueue.put((
'updateNumberOfBroadcastsProcessed', 'no data')) 'updateNumberOfBroadcastsProcessed', 'no data'))
inventoryHash = calculateInventoryHash(data) inventoryHash = highlevelcrypto.calculateInventoryHash(data)
readPosition = 20 # bypass the nonce, time, and object type readPosition = 20 # bypass the nonce, time, and object type
broadcastVersion, broadcastVersionLength = decodeVarint( broadcastVersion, broadcastVersionLength = decodeVarint(
data[readPosition:readPosition + 9]) data[readPosition:readPosition + 9])
@ -880,9 +875,8 @@ class objectProcessor(threading.Thread):
requiredPayloadLengthExtraBytes) requiredPayloadLengthExtraBytes)
endOfPubkeyPosition = readPosition endOfPubkeyPosition = readPosition
sha = hashlib.new('sha512') calculatedRipe = highlevelcrypto.to_ripe(
sha.update(sendersPubSigningKey + sendersPubEncryptionKey) sendersPubSigningKey, sendersPubEncryptionKey)
calculatedRipe = RIPEMD160Hash(sha.digest()).digest()
if broadcastVersion == 4: if broadcastVersion == 4:
if toRipe != calculatedRipe: if toRipe != calculatedRipe:
@ -892,10 +886,10 @@ class objectProcessor(threading.Thread):
' itself. Ignoring message.' ' itself. Ignoring message.'
) )
elif broadcastVersion == 5: elif broadcastVersion == 5:
calculatedTag = hashlib.sha512(hashlib.sha512( calculatedTag = highlevelcrypto.double_sha512(
encodeVarint(sendersAddressVersion) encodeVarint(sendersAddressVersion)
+ encodeVarint(sendersStream) + calculatedRipe + encodeVarint(sendersStream) + calculatedRipe
).digest()).digest()[32:] )[32:]
if calculatedTag != embeddedTag: if calculatedTag != embeddedTag:
return logger.debug( return logger.debug(
'The tag and encryption key used to encrypt this' 'The tag and encryption key used to encrypt this'
@ -925,8 +919,7 @@ class objectProcessor(threading.Thread):
return return
logger.debug('ECDSA verify passed') logger.debug('ECDSA verify passed')
# Used to detect and ignore duplicate messages in our inbox # Used to detect and ignore duplicate messages in our inbox
sigHash = hashlib.sha512( sigHash = highlevelcrypto.double_sha512(signature)[32:]
hashlib.sha512(signature).digest()).digest()[32:]
fromAddress = encodeAddress( fromAddress = encodeAddress(
sendersAddressVersion, sendersStream, calculatedRipe) sendersAddressVersion, sendersStream, calculatedRipe)
@ -1000,10 +993,10 @@ class objectProcessor(threading.Thread):
# Let us create the tag from the address and see if we were waiting # Let us create the tag from the address and see if we were waiting
# for it. # for it.
elif addressVersion >= 4: elif addressVersion >= 4:
tag = hashlib.sha512(hashlib.sha512( tag = highlevelcrypto.double_sha512(
encodeVarint(addressVersion) + encodeVarint(streamNumber) encodeVarint(addressVersion) + encodeVarint(streamNumber)
+ ripe + ripe
).digest()).digest()[32:] )[32:]
if tag in state.neededPubkeys: if tag in state.neededPubkeys:
del state.neededPubkeys[tag] del state.neededPubkeys[tag]
self.sendMessages(address) self.sendMessages(address)

View File

@ -27,8 +27,7 @@ import queues
import state import state
from bmconfigparser import config from bmconfigparser import config
from helper_sql import sqlExecute, sqlQuery from helper_sql import sqlExecute, sqlQuery
from inventory import Inventory from network import connectionpool, knownnodes, StoppableThread
from network import BMConnectionPool, knownnodes, StoppableThread
from tr import _translate from tr import _translate
@ -69,7 +68,7 @@ class singleCleaner(StoppableThread):
'updateStatusBar', 'updateStatusBar',
'Doing housekeeping (Flushing inventory in memory to disk...)' 'Doing housekeeping (Flushing inventory in memory to disk...)'
)) ))
Inventory().flush() state.Inventory.flush()
queues.UISignalQueue.put(('updateStatusBar', '')) queues.UISignalQueue.put(('updateStatusBar', ''))
# If we are running as a daemon then we are going to fill up the UI # If we are running as a daemon then we are going to fill up the UI
@ -82,7 +81,7 @@ class singleCleaner(StoppableThread):
tick = int(time.time()) tick = int(time.time())
if timeWeLastClearedInventoryAndPubkeysTables < tick - 7380: if timeWeLastClearedInventoryAndPubkeysTables < tick - 7380:
timeWeLastClearedInventoryAndPubkeysTables = tick timeWeLastClearedInventoryAndPubkeysTables = tick
Inventory().clean() state.Inventory.clean()
queues.workerQueue.put(('sendOnionPeerObj', '')) queues.workerQueue.put(('sendOnionPeerObj', ''))
# pubkeys # pubkeys
sqlExecute( sqlExecute(
@ -109,7 +108,7 @@ class singleCleaner(StoppableThread):
# Cleanup knownnodes and handle possible severe exception # Cleanup knownnodes and handle possible severe exception
# while writing it to disk # while writing it to disk
if state.enableNetwork: if state.enableNetwork:
knownnodes.cleanupKnownNodes() knownnodes.cleanupKnownNodes(connectionpool.pool)
except Exception as err: except Exception as err:
if "Errno 28" in str(err): if "Errno 28" in str(err):
self.logger.fatal( self.logger.fatal(
@ -130,7 +129,7 @@ class singleCleaner(StoppableThread):
os._exit(1) # pylint: disable=protected-access os._exit(1) # pylint: disable=protected-access
# inv/object tracking # inv/object tracking
for connection in BMConnectionPool().connections(): for connection in connectionpool.pool.connections():
connection.clean() connection.clean()
# discovery tracking # discovery tracking

View File

@ -25,13 +25,10 @@ import queues
import shared import shared
import state import state
import tr import tr
from addresses import ( from addresses import decodeAddress, decodeVarint, encodeVarint
calculateInventoryHash, decodeAddress, decodeVarint, encodeVarint
)
from bmconfigparser import config from bmconfigparser import config
from helper_sql import sqlExecute, sqlQuery from helper_sql import sqlExecute, sqlQuery
from inventory import Inventory from network import knownnodes, StoppableThread, invQueue
from network import knownnodes, StoppableThread
from six.moves import configparser, queue from six.moves import configparser, queue
@ -50,6 +47,8 @@ class singleWorker(StoppableThread):
def __init__(self): def __init__(self):
super(singleWorker, self).__init__(name="singleWorker") super(singleWorker, self).__init__(name="singleWorker")
self.digestAlg = config.safeGet(
'bitmessagesettings', 'digestalg', 'sha256')
proofofwork.init() proofofwork.init()
def stopThread(self): def stopThread(self):
@ -73,18 +72,16 @@ class singleWorker(StoppableThread):
queryreturn = sqlQuery( queryreturn = sqlQuery(
'''SELECT DISTINCT toaddress FROM sent''' '''SELECT DISTINCT toaddress FROM sent'''
''' WHERE (status='awaitingpubkey' AND folder='sent')''') ''' WHERE (status='awaitingpubkey' AND folder='sent')''')
for row in queryreturn: for toAddress, in queryreturn:
toAddress, = row toAddressVersionNumber, toStreamNumber, toRipe = \
# toStatus decodeAddress(toAddress)[1:]
_, toAddressVersionNumber, toStreamNumber, toRipe = \
decodeAddress(toAddress)
if toAddressVersionNumber <= 3: if toAddressVersionNumber <= 3:
state.neededPubkeys[toAddress] = 0 state.neededPubkeys[toAddress] = 0
elif toAddressVersionNumber >= 4: elif toAddressVersionNumber >= 4:
doubleHashOfAddressData = hashlib.sha512(hashlib.sha512( doubleHashOfAddressData = highlevelcrypto.double_sha512(
encodeVarint(toAddressVersionNumber) encodeVarint(toAddressVersionNumber)
+ encodeVarint(toStreamNumber) + toRipe + encodeVarint(toStreamNumber) + toRipe
).digest()).digest() )
# Note that this is the first half of the sha512 hash. # Note that this is the first half of the sha512 hash.
privEncryptionKey = doubleHashOfAddressData[:32] privEncryptionKey = doubleHashOfAddressData[:32]
tag = doubleHashOfAddressData[32:] tag = doubleHashOfAddressData[32:]
@ -119,7 +116,7 @@ class singleWorker(StoppableThread):
# For the case if user deleted knownnodes # For the case if user deleted knownnodes
# but is still having onionpeer objects in inventory # but is still having onionpeer objects in inventory
if not knownnodes.knownNodesActual: if not knownnodes.knownNodesActual:
for item in Inventory().by_type_and_tag(protocol.OBJECT_ONIONPEER): for item in state.Inventory.by_type_and_tag(protocol.OBJECT_ONIONPEER):
queues.objectProcessorQueue.put(( queues.objectProcessorQueue.put((
protocol.OBJECT_ONIONPEER, item.payload protocol.OBJECT_ONIONPEER, item.payload
)) ))
@ -195,15 +192,19 @@ class singleWorker(StoppableThread):
self.logger.info("Quitting...") self.logger.info("Quitting...")
def _getKeysForAddress(self, address): def _getKeysForAddress(self, address):
privSigningKeyBase58 = config.get( try:
address, 'privsigningkey') privSigningKeyBase58 = config.get(address, 'privsigningkey')
privEncryptionKeyBase58 = config.get( privEncryptionKeyBase58 = config.get(address, 'privencryptionkey')
address, 'privencryptionkey') except (configparser.NoSectionError, configparser.NoOptionError):
self.logger.error(
'Could not read or decode privkey for address %s', address)
raise ValueError
privSigningKeyHex = hexlify(shared.decodeWalletImportFormat( privSigningKeyHex = hexlify(highlevelcrypto.decodeWalletImportFormat(
privSigningKeyBase58)) privSigningKeyBase58.encode()))
privEncryptionKeyHex = hexlify(shared.decodeWalletImportFormat( privEncryptionKeyHex = hexlify(
privEncryptionKeyBase58)) highlevelcrypto.decodeWalletImportFormat(
privEncryptionKeyBase58.encode()))
# The \x04 on the beginning of the public keys are not sent. # The \x04 on the beginning of the public keys are not sent.
# This way there is only one acceptable way to encode # This way there is only one acceptable way to encode
@ -254,9 +255,7 @@ class singleWorker(StoppableThread):
message once it is done with the POW""" message once it is done with the POW"""
# Look up my stream number based on my address hash # Look up my stream number based on my address hash
myAddress = shared.myAddressesByHash[adressHash] myAddress = shared.myAddressesByHash[adressHash]
# status addressVersionNumber, streamNumber = decodeAddress(myAddress)[1:3]
_, addressVersionNumber, streamNumber, adressHash = (
decodeAddress(myAddress))
# 28 days from now plus or minus five minutes # 28 days from now plus or minus five minutes
TTL = int(28 * 24 * 60 * 60 + helper_random.randomrandrange(-300, 300)) TTL = int(28 * 24 * 60 * 60 + helper_random.randomrandrange(-300, 300))
@ -269,17 +268,15 @@ class singleWorker(StoppableThread):
payload += protocol.getBitfield(myAddress) payload += protocol.getBitfield(myAddress)
try: try:
# privSigningKeyHex, privEncryptionKeyHex pubSigningKey, pubEncryptionKey = self._getKeysForAddress(
_, _, pubSigningKey, pubEncryptionKey = \ myAddress)[2:]
self._getKeysForAddress(myAddress) except ValueError:
except (configparser.NoSectionError, configparser.NoOptionError) as err: return
self.logger.warning("Section or Option did not found: %s", err) except Exception: # pylint:disable=broad-exception-caught
except Exception as err:
self.logger.error( self.logger.error(
'Error within doPOWForMyV2Pubkey. Could not read' 'Error within doPOWForMyV2Pubkey. Could not read'
' the keys from the keys.dat file for a requested' ' the keys from the keys.dat file for a requested'
' address. %s\n', err ' address. %s\n', exc_info=True)
)
return return
payload += pubSigningKey + pubEncryptionKey payload += pubSigningKey + pubEncryptionKey
@ -288,15 +285,15 @@ class singleWorker(StoppableThread):
payload = self._doPOWDefaults( payload = self._doPOWDefaults(
payload, TTL, log_prefix='(For pubkey message)') payload, TTL, log_prefix='(For pubkey message)')
inventoryHash = calculateInventoryHash(payload) inventoryHash = highlevelcrypto.calculateInventoryHash(payload)
objectType = 1 objectType = 1
Inventory()[inventoryHash] = ( state.Inventory[inventoryHash] = (
objectType, streamNumber, payload, embeddedTime, '') objectType, streamNumber, payload, embeddedTime, '')
self.logger.info( self.logger.info(
'broadcasting inv with hash: %s', hexlify(inventoryHash)) 'broadcasting inv with hash: %s', hexlify(inventoryHash))
queues.invQueue.put((streamNumber, inventoryHash)) invQueue.put((streamNumber, inventoryHash))
queues.UISignalQueue.put(('updateStatusBar', '')) queues.UISignalQueue.put(('updateStatusBar', ''))
try: try:
config.set( config.set(
@ -318,8 +315,8 @@ class singleWorker(StoppableThread):
try: try:
myAddress = shared.myAddressesByHash[adressHash] myAddress = shared.myAddressesByHash[adressHash]
except KeyError: except KeyError:
# The address has been deleted. self.logger.warning( # The address has been deleted.
self.logger.warning("Can't find %s in myAddressByHash", hexlify(adressHash)) "Can't find %s in myAddressByHash", hexlify(adressHash))
return return
if config.safeGetBoolean(myAddress, 'chan'): if config.safeGetBoolean(myAddress, 'chan'):
self.logger.info('This is a chan address. Not sending pubkey.') self.logger.info('This is a chan address. Not sending pubkey.')
@ -351,14 +348,13 @@ class singleWorker(StoppableThread):
# , privEncryptionKeyHex # , privEncryptionKeyHex
privSigningKeyHex, _, pubSigningKey, pubEncryptionKey = \ privSigningKeyHex, _, pubSigningKey, pubEncryptionKey = \
self._getKeysForAddress(myAddress) self._getKeysForAddress(myAddress)
except (configparser.NoSectionError, configparser.NoOptionError) as err: except ValueError:
self.logger.warning("Section or Option did not found: %s", err) return
except Exception as err: except Exception: # pylint:disable=broad-exception-caught
self.logger.error( self.logger.error(
'Error within sendOutOrStoreMyV3Pubkey. Could not read' 'Error within sendOutOrStoreMyV3Pubkey. Could not read'
' the keys from the keys.dat file for a requested' ' the keys from the keys.dat file for a requested'
' address. %s\n', err ' address. %s\n', exc_info=True)
)
return return
payload += pubSigningKey + pubEncryptionKey payload += pubSigningKey + pubEncryptionKey
@ -368,7 +364,8 @@ class singleWorker(StoppableThread):
payload += encodeVarint(config.getint( payload += encodeVarint(config.getint(
myAddress, 'payloadlengthextrabytes')) myAddress, 'payloadlengthextrabytes'))
signature = highlevelcrypto.sign(payload, privSigningKeyHex) signature = highlevelcrypto.sign(
payload, privSigningKeyHex, self.digestAlg)
payload += encodeVarint(len(signature)) payload += encodeVarint(len(signature))
payload += signature payload += signature
@ -376,15 +373,15 @@ class singleWorker(StoppableThread):
payload = self._doPOWDefaults( payload = self._doPOWDefaults(
payload, TTL, log_prefix='(For pubkey message)') payload, TTL, log_prefix='(For pubkey message)')
inventoryHash = calculateInventoryHash(payload) inventoryHash = highlevelcrypto.calculateInventoryHash(payload)
objectType = 1 objectType = 1
Inventory()[inventoryHash] = ( state.Inventory[inventoryHash] = (
objectType, streamNumber, payload, embeddedTime, '') objectType, streamNumber, payload, embeddedTime, '')
self.logger.info( self.logger.info(
'broadcasting inv with hash: %s', hexlify(inventoryHash)) 'broadcasting inv with hash: %s', hexlify(inventoryHash))
queues.invQueue.put((streamNumber, inventoryHash)) invQueue.put((streamNumber, inventoryHash))
queues.UISignalQueue.put(('updateStatusBar', '')) queues.UISignalQueue.put(('updateStatusBar', ''))
try: try:
config.set( config.set(
@ -425,14 +422,13 @@ class singleWorker(StoppableThread):
# , privEncryptionKeyHex # , privEncryptionKeyHex
privSigningKeyHex, _, pubSigningKey, pubEncryptionKey = \ privSigningKeyHex, _, pubSigningKey, pubEncryptionKey = \
self._getKeysForAddress(myAddress) self._getKeysForAddress(myAddress)
except (configparser.NoSectionError, configparser.NoOptionError) as err: except ValueError:
self.logger.warning("Section or Option did not found: %s", err) return
except Exception as err: except Exception: # pylint:disable=broad-exception-caught
self.logger.error( self.logger.error(
'Error within sendOutOrStoreMyV4Pubkey. Could not read' 'Error within sendOutOrStoreMyV4Pubkey. Could not read'
' the keys from the keys.dat file for a requested' ' the keys from the keys.dat file for a requested'
' address. %s\n', err ' address. %s\n', exc_info=True)
)
return return
dataToEncrypt += pubSigningKey + pubEncryptionKey dataToEncrypt += pubSigningKey + pubEncryptionKey
@ -449,14 +445,13 @@ class singleWorker(StoppableThread):
# unencrypted, the pubkey with part of the hash so that nodes # unencrypted, the pubkey with part of the hash so that nodes
# know which pubkey object to try to decrypt # know which pubkey object to try to decrypt
# when they want to send a message. # when they want to send a message.
doubleHashOfAddressData = hashlib.sha512(hashlib.sha512( doubleHashOfAddressData = highlevelcrypto.double_sha512(
encodeVarint(addressVersionNumber) encodeVarint(addressVersionNumber)
+ encodeVarint(streamNumber) + addressHash + encodeVarint(streamNumber) + addressHash
).digest()).digest() )
payload += doubleHashOfAddressData[32:] # the tag payload += doubleHashOfAddressData[32:] # the tag
signature = highlevelcrypto.sign( signature = highlevelcrypto.sign(
payload + dataToEncrypt, privSigningKeyHex payload + dataToEncrypt, privSigningKeyHex, self.digestAlg)
)
dataToEncrypt += encodeVarint(len(signature)) dataToEncrypt += encodeVarint(len(signature))
dataToEncrypt += signature dataToEncrypt += signature
@ -469,9 +464,9 @@ class singleWorker(StoppableThread):
payload = self._doPOWDefaults( payload = self._doPOWDefaults(
payload, TTL, log_prefix='(For pubkey message)') payload, TTL, log_prefix='(For pubkey message)')
inventoryHash = calculateInventoryHash(payload) inventoryHash = highlevelcrypto.calculateInventoryHash(payload)
objectType = 1 objectType = 1
Inventory()[inventoryHash] = ( state.Inventory[inventoryHash] = (
objectType, streamNumber, payload, embeddedTime, objectType, streamNumber, payload, embeddedTime,
doubleHashOfAddressData[32:] doubleHashOfAddressData[32:]
) )
@ -479,7 +474,7 @@ class singleWorker(StoppableThread):
self.logger.info( self.logger.info(
'broadcasting inv with hash: %s', hexlify(inventoryHash)) 'broadcasting inv with hash: %s', hexlify(inventoryHash))
queues.invQueue.put((streamNumber, inventoryHash)) invQueue.put((streamNumber, inventoryHash))
queues.UISignalQueue.put(('updateStatusBar', '')) queues.UISignalQueue.put(('updateStatusBar', ''))
try: try:
config.set( config.set(
@ -505,9 +500,9 @@ class singleWorker(StoppableThread):
objectType = protocol.OBJECT_ONIONPEER objectType = protocol.OBJECT_ONIONPEER
# FIXME: ideally the objectPayload should be signed # FIXME: ideally the objectPayload should be signed
objectPayload = encodeVarint(peer.port) + protocol.encodeHost(peer.host) objectPayload = encodeVarint(peer.port) + protocol.encodeHost(peer.host)
tag = calculateInventoryHash(objectPayload) tag = highlevelcrypto.calculateInventoryHash(objectPayload)
if Inventory().by_type_and_tag(objectType, tag): if state.Inventory.by_type_and_tag(objectType, tag):
return # not expired return # not expired
payload = pack('>Q', embeddedTime) payload = pack('>Q', embeddedTime)
@ -519,15 +514,15 @@ class singleWorker(StoppableThread):
payload = self._doPOWDefaults( payload = self._doPOWDefaults(
payload, TTL, log_prefix='(For onionpeer object)') payload, TTL, log_prefix='(For onionpeer object)')
inventoryHash = calculateInventoryHash(payload) inventoryHash = highlevelcrypto.calculateInventoryHash(payload)
Inventory()[inventoryHash] = ( state.Inventory[inventoryHash] = (
objectType, streamNumber, buffer(payload), objectType, streamNumber, buffer(payload), # noqa: F821
embeddedTime, buffer(tag) embeddedTime, buffer(tag) # noqa: F821
) )
self.logger.info( self.logger.info(
'sending inv (within sendOnionPeerObj function) for object: %s', 'sending inv (within sendOnionPeerObj function) for object: %s',
hexlify(inventoryHash)) hexlify(inventoryHash))
queues.invQueue.put((streamNumber, inventoryHash)) invQueue.put((streamNumber, inventoryHash))
def sendBroadcast(self): def sendBroadcast(self):
"""Send a broadcast-type object (assemble the object, perform PoW and put it to the inv announcement queue)""" """Send a broadcast-type object (assemble the object, perform PoW and put it to the inv announcement queue)"""
@ -558,8 +553,7 @@ class singleWorker(StoppableThread):
# , privEncryptionKeyHex # , privEncryptionKeyHex
privSigningKeyHex, _, pubSigningKey, pubEncryptionKey = \ privSigningKeyHex, _, pubSigningKey, pubEncryptionKey = \
self._getKeysForAddress(fromaddress) self._getKeysForAddress(fromaddress)
except (configparser.NoSectionError, configparser.NoOptionError) as err: except ValueError:
self.logger.warning("Section or Option did not found: %s", err)
queues.UISignalQueue.put(( queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', ( 'updateSentItemStatusByAckdata', (
ackdata, ackdata,
@ -568,6 +562,7 @@ class singleWorker(StoppableThread):
"Error! Could not find sender address" "Error! Could not find sender address"
" (your address) in the keys.dat file.")) " (your address) in the keys.dat file."))
)) ))
continue
except Exception as err: except Exception as err:
self.logger.error( self.logger.error(
'Error within sendBroadcast. Could not read' 'Error within sendBroadcast. Could not read'
@ -613,10 +608,10 @@ class singleWorker(StoppableThread):
payload += encodeVarint(streamNumber) payload += encodeVarint(streamNumber)
if addressVersionNumber >= 4: if addressVersionNumber >= 4:
doubleHashOfAddressData = hashlib.sha512(hashlib.sha512( doubleHashOfAddressData = highlevelcrypto.double_sha512(
encodeVarint(addressVersionNumber) encodeVarint(addressVersionNumber)
+ encodeVarint(streamNumber) + ripe + encodeVarint(streamNumber) + ripe
).digest()).digest() )
tag = doubleHashOfAddressData[32:] tag = doubleHashOfAddressData[32:]
payload += tag payload += tag
else: else:
@ -641,7 +636,7 @@ class singleWorker(StoppableThread):
dataToSign = payload + dataToEncrypt dataToSign = payload + dataToEncrypt
signature = highlevelcrypto.sign( signature = highlevelcrypto.sign(
dataToSign, privSigningKeyHex) dataToSign, privSigningKeyHex, self.digestAlg)
dataToEncrypt += encodeVarint(len(signature)) dataToEncrypt += encodeVarint(len(signature))
dataToEncrypt += signature dataToEncrypt += signature
@ -686,16 +681,16 @@ class singleWorker(StoppableThread):
) )
continue continue
inventoryHash = calculateInventoryHash(payload) inventoryHash = highlevelcrypto.calculateInventoryHash(payload)
objectType = 3 objectType = 3
Inventory()[inventoryHash] = ( state.Inventory[inventoryHash] = (
objectType, streamNumber, payload, embeddedTime, tag) objectType, streamNumber, payload, embeddedTime, tag)
self.logger.info( self.logger.info(
'sending inv (within sendBroadcast function)' 'sending inv (within sendBroadcast function)'
' for object: %s', ' for object: %s',
hexlify(inventoryHash) hexlify(inventoryHash)
) )
queues.invQueue.put((streamNumber, inventoryHash)) invQueue.put((streamNumber, inventoryHash))
queues.UISignalQueue.put(( queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', ( 'updateSentItemStatusByAckdata', (
@ -795,10 +790,10 @@ class singleWorker(StoppableThread):
if toAddressVersionNumber <= 3: if toAddressVersionNumber <= 3:
toTag = '' toTag = ''
else: else:
toTag = hashlib.sha512(hashlib.sha512( toTag = highlevelcrypto.double_sha512(
encodeVarint(toAddressVersionNumber) encodeVarint(toAddressVersionNumber)
+ encodeVarint(toStreamNumber) + toRipe + encodeVarint(toStreamNumber) + toRipe
).digest()).digest()[32:] )[32:]
if toaddress in state.neededPubkeys or \ if toaddress in state.neededPubkeys or \
toTag in state.neededPubkeys: toTag in state.neededPubkeys:
# We already sent a request for the pubkey # We already sent a request for the pubkey
@ -832,11 +827,11 @@ class singleWorker(StoppableThread):
# already contains the toAddress and cryptor # already contains the toAddress and cryptor
# object associated with the tag for this toAddress. # object associated with the tag for this toAddress.
if toAddressVersionNumber >= 4: if toAddressVersionNumber >= 4:
doubleHashOfToAddressData = hashlib.sha512( doubleHashOfToAddressData = \
hashlib.sha512( highlevelcrypto.double_sha512(
encodeVarint(toAddressVersionNumber) + encodeVarint(toStreamNumber) + toRipe encodeVarint(toAddressVersionNumber)
).digest() + encodeVarint(toStreamNumber) + toRipe
).digest() )
# The first half of the sha512 hash. # The first half of the sha512 hash.
privEncryptionKey = doubleHashOfToAddressData[:32] privEncryptionKey = doubleHashOfToAddressData[:32]
# The second half of the sha512 hash. # The second half of the sha512 hash.
@ -847,7 +842,7 @@ class singleWorker(StoppableThread):
hexlify(privEncryptionKey)) hexlify(privEncryptionKey))
) )
for value in Inventory().by_type_and_tag(1, toTag): for value in state.Inventory.by_type_and_tag(1, toTag):
# if valid, this function also puts it # if valid, this function also puts it
# in the pubkeys table. # in the pubkeys table.
if protocol.decryptAndCheckPubkeyPayload( if protocol.decryptAndCheckPubkeyPayload(
@ -1116,8 +1111,9 @@ class singleWorker(StoppableThread):
' from the keys.dat file for our own address. %s\n', ' from the keys.dat file for our own address. %s\n',
err) err)
continue continue
privEncryptionKeyHex = hexlify(shared.decodeWalletImportFormat( privEncryptionKeyHex = hexlify(
privEncryptionKeyBase58)) highlevelcrypto.decodeWalletImportFormat(
privEncryptionKeyBase58.encode()))
pubEncryptionKeyBase256 = unhexlify(highlevelcrypto.privToPub( pubEncryptionKeyBase256 = unhexlify(highlevelcrypto.privToPub(
privEncryptionKeyHex))[1:] privEncryptionKeyHex))[1:]
requiredAverageProofOfWorkNonceTrialsPerByte = \ requiredAverageProofOfWorkNonceTrialsPerByte = \
@ -1146,8 +1142,7 @@ class singleWorker(StoppableThread):
privSigningKeyHex, privEncryptionKeyHex, \ privSigningKeyHex, privEncryptionKeyHex, \
pubSigningKey, pubEncryptionKey = self._getKeysForAddress( pubSigningKey, pubEncryptionKey = self._getKeysForAddress(
fromaddress) fromaddress)
except (configparser.NoSectionError, configparser.NoOptionError) as err: except ValueError:
self.logger.warning("Section or Option did not found: %s", err)
queues.UISignalQueue.put(( queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', ( 'updateSentItemStatusByAckdata', (
ackdata, ackdata,
@ -1156,6 +1151,7 @@ class singleWorker(StoppableThread):
"Error! Could not find sender address" "Error! Could not find sender address"
" (your address) in the keys.dat file.")) " (your address) in the keys.dat file."))
)) ))
continue
except Exception as err: except Exception as err:
self.logger.error( self.logger.error(
'Error within sendMsg. Could not read' 'Error within sendMsg. Could not read'
@ -1223,7 +1219,8 @@ class singleWorker(StoppableThread):
payload += fullAckPayload payload += fullAckPayload
dataToSign = pack('>Q', embeddedTime) + '\x00\x00\x00\x02' + \ dataToSign = pack('>Q', embeddedTime) + '\x00\x00\x00\x02' + \
encodeVarint(1) + encodeVarint(toStreamNumber) + payload encodeVarint(1) + encodeVarint(toStreamNumber) + payload
signature = highlevelcrypto.sign(dataToSign, privSigningKeyHex) signature = highlevelcrypto.sign(
dataToSign, privSigningKeyHex, self.digestAlg)
payload += encodeVarint(len(signature)) payload += encodeVarint(len(signature))
payload += signature payload += signature
@ -1301,9 +1298,9 @@ class singleWorker(StoppableThread):
) )
continue continue
inventoryHash = calculateInventoryHash(encryptedPayload) inventoryHash = highlevelcrypto.calculateInventoryHash(encryptedPayload)
objectType = 2 objectType = 2
Inventory()[inventoryHash] = ( state.Inventory[inventoryHash] = (
objectType, toStreamNumber, encryptedPayload, embeddedTime, '') objectType, toStreamNumber, encryptedPayload, embeddedTime, '')
if config.has_section(toaddress) or \ if config.has_section(toaddress) or \
not protocol.checkBitfield(behaviorBitfield, protocol.BITFIELD_DOESACK): not protocol.checkBitfield(behaviorBitfield, protocol.BITFIELD_DOESACK):
@ -1329,7 +1326,7 @@ class singleWorker(StoppableThread):
'Broadcasting inv for my msg(within sendmsg function): %s', 'Broadcasting inv for my msg(within sendmsg function): %s',
hexlify(inventoryHash) hexlify(inventoryHash)
) )
queues.invQueue.put((toStreamNumber, inventoryHash)) invQueue.put((toStreamNumber, inventoryHash))
# Update the sent message in the sent table with the # Update the sent message in the sent table with the
# necessary information. # necessary information.
@ -1351,8 +1348,7 @@ class singleWorker(StoppableThread):
# the message in our own inbox. # the message in our own inbox.
if config.has_section(toaddress): if config.has_section(toaddress):
# Used to detect and ignore duplicate messages in our inbox # Used to detect and ignore duplicate messages in our inbox
sigHash = hashlib.sha512(hashlib.sha512( sigHash = highlevelcrypto.double_sha512(signature)[32:]
signature).digest()).digest()[32:]
t = (inventoryHash, toaddress, fromaddress, subject, int( t = (inventoryHash, toaddress, fromaddress, subject, int(
time.time()), message, 'inbox', encoding, 0, sigHash) time.time()), message, 'inbox', encoding, 0, sigHash)
helper_inbox.insert(t) helper_inbox.insert(t)
@ -1372,7 +1368,7 @@ class singleWorker(StoppableThread):
if apiNotifyPath: if apiNotifyPath:
# There is no additional risk of remote exploitation or # There is no additional risk of remote exploitation or
# privilege escalation # privilege escalation
call([apiNotifyPath, "newMessage"]) # nosec:B603 call([apiNotifyPath, "newMessage"]) # nosec B603
def requestPubKey(self, toAddress): def requestPubKey(self, toAddress):
"""Send a getpubkey object""" """Send a getpubkey object"""
@ -1409,16 +1405,13 @@ class singleWorker(StoppableThread):
# neededPubkeys dictionary. But if we are recovering # neededPubkeys dictionary. But if we are recovering
# from a restart of the client then we have to put it in now. # from a restart of the client then we have to put it in now.
# Note that this is the first half of the sha512 hash. doubleHashOfAddressData = highlevelcrypto.double_sha512(
privEncryptionKey = hashlib.sha512(hashlib.sha512(
encodeVarint(addressVersionNumber) encodeVarint(addressVersionNumber)
+ encodeVarint(streamNumber) + ripe + encodeVarint(streamNumber) + ripe
).digest()).digest()[:32] )
privEncryptionKey = doubleHashOfAddressData[:32]
# Note that this is the second half of the sha512 hash. # Note that this is the second half of the sha512 hash.
tag = hashlib.sha512(hashlib.sha512( tag = doubleHashOfAddressData[32:]
encodeVarint(addressVersionNumber)
+ encodeVarint(streamNumber) + ripe
).digest()).digest()[32:]
if tag not in state.neededPubkeys: if tag not in state.neededPubkeys:
# We'll need this for when we receive a pubkey reply: # We'll need this for when we receive a pubkey reply:
# it will be encrypted and we'll need to decrypt it. # it will be encrypted and we'll need to decrypt it.
@ -1461,12 +1454,12 @@ class singleWorker(StoppableThread):
payload = self._doPOWDefaults(payload, TTL) payload = self._doPOWDefaults(payload, TTL)
inventoryHash = calculateInventoryHash(payload) inventoryHash = highlevelcrypto.calculateInventoryHash(payload)
objectType = 1 objectType = 1
Inventory()[inventoryHash] = ( state.Inventory[inventoryHash] = (
objectType, streamNumber, payload, embeddedTime, '') objectType, streamNumber, payload, embeddedTime, '')
self.logger.info('sending inv (for the getpubkey message)') self.logger.info('sending inv (for the getpubkey message)')
queues.invQueue.put((streamNumber, inventoryHash)) invQueue.put((streamNumber, inventoryHash))
# wait 10% past expiration # wait 10% past expiration
sleeptill = int(time.time() + TTL * 1.1) sleeptill = int(time.time() + TTL * 1.1)

View File

@ -287,7 +287,7 @@ def check_openssl():
path = ctypes.util.find_library('ssl') path = ctypes.util.find_library('ssl')
if path not in paths: if path not in paths:
paths.append(path) paths.append(path)
except: # nosec:B110 pylint:disable=bare-except except: # nosec B110 # pylint:disable=bare-except
pass pass
openssl_version = None openssl_version = None
@ -361,7 +361,7 @@ def check_curses():
return False return False
try: try:
subprocess.check_call(['which', 'dialog']) subprocess.check_call(['which', 'dialog']) # nosec B603, B607
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
logger.error( logger.error(
'Curses requires the `dialog` command to be installed as well as' 'Curses requires the `dialog` command to be installed as well as'

View File

@ -22,26 +22,26 @@ def genAckPayload(streamNumber=1, stealthLevel=0):
- level 1: a getpubkey request for a (random) dummy key hash - level 1: a getpubkey request for a (random) dummy key hash
- level 2: a standard message, encrypted to a random pubkey - level 2: a standard message, encrypted to a random pubkey
""" """
if stealthLevel == 2: # Generate privacy-enhanced payload if stealthLevel == 2: # Generate privacy-enhanced payload
# Generate a dummy privkey and derive the pubkey # Generate a dummy privkey and derive the pubkey
dummyPubKeyHex = highlevelcrypto.privToPub( dummyPubKeyHex = highlevelcrypto.privToPub(
hexlify(helper_random.randomBytes(32))) hexlify(highlevelcrypto.randomBytes(32)))
# Generate a dummy message of random length # Generate a dummy message of random length
# (the smallest possible standard-formatted message is 234 bytes) # (the smallest possible standard-formatted message is 234 bytes)
dummyMessage = helper_random.randomBytes( dummyMessage = highlevelcrypto.randomBytes(
helper_random.randomrandrange(234, 801)) helper_random.randomrandrange(234, 801))
# Encrypt the message using standard BM encryption (ECIES) # Encrypt the message using standard BM encryption (ECIES)
ackdata = highlevelcrypto.encrypt(dummyMessage, dummyPubKeyHex) ackdata = highlevelcrypto.encrypt(dummyMessage, dummyPubKeyHex)
acktype = 2 # message acktype = 2 # message
version = 1 version = 1
elif stealthLevel == 1: # Basic privacy payload (random getpubkey) elif stealthLevel == 1: # Basic privacy payload (random getpubkey)
ackdata = helper_random.randomBytes(32) ackdata = highlevelcrypto.randomBytes(32)
acktype = 0 # getpubkey acktype = 0 # getpubkey
version = 4 version = 4
else: # Minimum viable payload (non stealth) else: # Minimum viable payload (non stealth)
ackdata = helper_random.randomBytes(32) ackdata = highlevelcrypto.randomBytes(32)
acktype = 2 # message acktype = 2 # message
version = 1 version = 1

View File

@ -1,12 +1,7 @@
"""Convenience functions for random operations. Not suitable for security / cryptography operations.""" """Convenience functions for random operations. Not suitable for security / cryptography operations."""
import os
import random import random
try:
from pyelliptic.openssl import OpenSSL
except ImportError:
from .pyelliptic.openssl import OpenSSL
NoneType = type(None) NoneType = type(None)
@ -16,14 +11,6 @@ def seed():
random.seed() random.seed()
def randomBytes(n):
"""Method randomBytes."""
try:
return os.urandom(n)
except NotImplementedError:
return OpenSSL.rand(n)
def randomshuffle(population): def randomshuffle(population):
"""Method randomShuffle. """Method randomShuffle.

View File

@ -12,6 +12,7 @@ import sys
import time import time
from distutils.version import StrictVersion from distutils.version import StrictVersion
from struct import pack from struct import pack
from six.moves import configparser
try: try:
import defaults import defaults
@ -218,7 +219,8 @@ def updateConfig():
config.set( config.set(
addressInKeysFile, 'payloadlengthextrabytes', addressInKeysFile, 'payloadlengthextrabytes',
str(int(previousSmallMessageDifficulty * 1000))) str(int(previousSmallMessageDifficulty * 1000)))
except Exception: except (ValueError, TypeError, configparser.NoSectionError,
configparser.NoOptionError):
continue continue
config.set('bitmessagesettings', 'maxdownloadrate', '0') config.set('bitmessagesettings', 'maxdownloadrate', '0')
config.set('bitmessagesettings', 'maxuploadrate', '0') config.set('bitmessagesettings', 'maxuploadrate', '0')

View File

@ -7,25 +7,104 @@ High level cryptographic functions based on `.pyelliptic` OpenSSL bindings.
`More discussion. <https://github.com/yann2192/pyelliptic/issues/32>`_ `More discussion. <https://github.com/yann2192/pyelliptic/issues/32>`_
""" """
import hashlib
import os
from binascii import hexlify from binascii import hexlify
import pyelliptic try:
from pyelliptic import OpenSSL import pyelliptic
from pyelliptic import arithmetic as a from fallback import RIPEMD160Hash
from pyelliptic import OpenSSL
from bmconfigparser import config from pyelliptic import arithmetic as a
except ImportError:
__all__ = ['encrypt', 'makeCryptor', 'pointMult', 'privToPub', 'sign', 'verify'] from pybitmessage import pyelliptic
from pybitmessage.fallback import RIPEMD160Hash
from pybitmessage.pyelliptic import OpenSSL
from pybitmessage.pyelliptic import arithmetic as a
def makeCryptor(privkey, curve='secp256k1'): __all__ = [
"""Return a private `.pyelliptic.ECC` instance""" 'decodeWalletImportFormat', 'deterministic_keys',
private_key = a.changebase(privkey, 16, 256, minlen=32) 'double_sha512', 'calculateInventoryHash', 'encodeWalletImportFormat',
public_key = pointMult(private_key) 'encrypt', 'makeCryptor', 'pointMult', 'privToPub', 'randomBytes',
cryptor = pyelliptic.ECC( 'random_keys', 'sign', 'to_ripe', 'verify']
pubkey_x=public_key[1:-32], pubkey_y=public_key[-32:],
raw_privkey=private_key, curve=curve)
return cryptor # WIF (uses arithmetic ):
def decodeWalletImportFormat(WIFstring):
"""
Convert private key from base58 that's used in the config file to
8-bit binary string.
"""
fullString = a.changebase(WIFstring, 58, 256)
privkey = fullString[:-4]
if fullString[-4:] != \
hashlib.sha256(hashlib.sha256(privkey).digest()).digest()[:4]:
raise ValueError('Checksum failed')
elif privkey[0:1] == b'\x80': # checksum passed
return privkey[1:]
raise ValueError('No hex 80 prefix')
# An excellent way for us to store our keys
# is in Wallet Import Format. Let us convert now.
# https://en.bitcoin.it/wiki/Wallet_import_format
def encodeWalletImportFormat(privKey):
"""
Convert private key from binary 8-bit string into base58check WIF string.
"""
privKey = b'\x80' + privKey
checksum = hashlib.sha256(hashlib.sha256(privKey).digest()).digest()[0:4]
return a.changebase(privKey + checksum, 256, 58)
# Random
def randomBytes(n):
"""Get n random bytes"""
try:
return os.urandom(n)
except NotImplementedError:
return OpenSSL.rand(n)
# Hashes
def _bm160(data):
"""RIPEME160(SHA512(data)) -> bytes"""
return RIPEMD160Hash(hashlib.sha512(data).digest()).digest()
def to_ripe(signing_key, encryption_key):
"""Convert two public keys to a ripe hash"""
return _bm160(signing_key + encryption_key)
def double_sha512(data):
"""Binary double SHA512 digest"""
return hashlib.sha512(hashlib.sha512(data).digest()).digest()
def calculateInventoryHash(data):
"""Calculate inventory hash from object data"""
return double_sha512(data)[:32]
# Keys
def random_keys():
"""Return a pair of keys, private and public"""
priv = randomBytes(32)
pub = pointMult(priv)
return priv, pub
def deterministic_keys(passphrase, nonce):
"""Generate keys from *passphrase* and *nonce* (encoded as varint)"""
priv = hashlib.sha512(passphrase + nonce).digest()[:32]
pub = pointMult(priv)
return priv, pub
def hexToPubkey(pubkey): def hexToPubkey(pubkey):
@ -35,12 +114,6 @@ def hexToPubkey(pubkey):
return pubkey_bin return pubkey_bin
def makePubCryptor(pubkey):
"""Return a public `.pyelliptic.ECC` instance"""
pubkey_bin = hexToPubkey(pubkey)
return pyelliptic.ECC(curve='secp256k1', pubkey=pubkey_bin)
def privToPub(privkey): def privToPub(privkey):
"""Converts hex private key into hex public key""" """Converts hex private key into hex public key"""
private_key = a.changebase(privkey, 16, 256, minlen=32) private_key = a.changebase(privkey, 16, 256, minlen=32)
@ -48,63 +121,6 @@ def privToPub(privkey):
return hexlify(public_key) return hexlify(public_key)
def encrypt(msg, hexPubkey):
"""Encrypts message with hex public key"""
return pyelliptic.ECC(curve='secp256k1').encrypt(
msg, hexToPubkey(hexPubkey))
def decrypt(msg, hexPrivkey):
"""Decrypts message with hex private key"""
return makeCryptor(hexPrivkey).decrypt(msg)
def decryptFast(msg, cryptor):
"""Decrypts message with an existing `.pyelliptic.ECC` object"""
return cryptor.decrypt(msg)
def sign(msg, hexPrivkey):
"""
Signs with hex private key using SHA1 or SHA256 depending on
"digestalg" setting
"""
digestAlg = config.safeGet(
'bitmessagesettings', 'digestalg', 'sha256')
if digestAlg == "sha1":
# SHA1, this will eventually be deprecated
return makeCryptor(hexPrivkey).sign(
msg, digest_alg=OpenSSL.digest_ecdsa_sha1)
elif digestAlg == "sha256":
# SHA256. Eventually this will become the default
return makeCryptor(hexPrivkey).sign(msg, digest_alg=OpenSSL.EVP_sha256)
else:
raise ValueError("Unknown digest algorithm %s" % digestAlg)
def verify(msg, sig, hexPubkey):
"""Verifies with hex public key using SHA1 or SHA256"""
# As mentioned above, we must upgrade gracefully to use SHA256. So
# let us check the signature using both SHA1 and SHA256 and if one
# of them passes then we will be satisfied. Eventually this can
# be simplified and we'll only check with SHA256.
try:
# old SHA1 algorithm.
sigVerifyPassed = makePubCryptor(hexPubkey).verify(
sig, msg, digest_alg=OpenSSL.digest_ecdsa_sha1)
except:
sigVerifyPassed = False
if sigVerifyPassed:
# The signature check passed using SHA1
return True
# The signature check using SHA1 failed. Let us try it with SHA256.
try:
return makePubCryptor(hexPubkey).verify(
sig, msg, digest_alg=OpenSSL.EVP_sha256)
except:
return False
def pointMult(secret): def pointMult(secret):
""" """
Does an EC point multiplication; turns a private key into a public key. Does an EC point multiplication; turns a private key into a public key.
@ -142,3 +158,81 @@ def pointMult(secret):
OpenSSL.EC_POINT_free(pub_key) OpenSSL.EC_POINT_free(pub_key)
OpenSSL.BN_free(priv_key) OpenSSL.BN_free(priv_key)
OpenSSL.EC_KEY_free(k) OpenSSL.EC_KEY_free(k)
# Encryption
def makeCryptor(privkey, curve='secp256k1'):
"""Return a private `.pyelliptic.ECC` instance"""
private_key = a.changebase(privkey, 16, 256, minlen=32)
public_key = pointMult(private_key)
cryptor = pyelliptic.ECC(
pubkey_x=public_key[1:-32], pubkey_y=public_key[-32:],
raw_privkey=private_key, curve=curve)
return cryptor
def makePubCryptor(pubkey):
"""Return a public `.pyelliptic.ECC` instance"""
pubkey_bin = hexToPubkey(pubkey)
return pyelliptic.ECC(curve='secp256k1', pubkey=pubkey_bin)
def encrypt(msg, hexPubkey):
"""Encrypts message with hex public key"""
return pyelliptic.ECC(curve='secp256k1').encrypt(
msg, hexToPubkey(hexPubkey))
def decrypt(msg, hexPrivkey):
"""Decrypts message with hex private key"""
return makeCryptor(hexPrivkey).decrypt(msg)
def decryptFast(msg, cryptor):
"""Decrypts message with an existing `.pyelliptic.ECC` object"""
return cryptor.decrypt(msg)
# Signatures
def _choose_digest_alg(name):
"""
Choose openssl digest constant by name raises ValueError if not appropriate
"""
if name not in ("sha1", "sha256"):
raise ValueError("Unknown digest algorithm %s" % name)
return (
# SHA1, this will eventually be deprecated
OpenSSL.digest_ecdsa_sha1 if name == "sha1" else OpenSSL.EVP_sha256)
def sign(msg, hexPrivkey, digestAlg="sha256"):
"""
Signs with hex private key using SHA1 or SHA256 depending on
*digestAlg* keyword.
"""
return makeCryptor(hexPrivkey).sign(
msg, digest_alg=_choose_digest_alg(digestAlg))
def verify(msg, sig, hexPubkey, digestAlg=None):
"""Verifies with hex public key using SHA1 or SHA256"""
# As mentioned above, we must upgrade gracefully to use SHA256. So
# let us check the signature using both SHA1 and SHA256 and if one
# of them passes then we will be satisfied. Eventually this can
# be simplified and we'll only check with SHA256.
if digestAlg is None:
# old SHA1 algorithm.
sigVerifyPassed = verify(msg, sig, hexPubkey, "sha1")
if sigVerifyPassed:
# The signature check passed using SHA1
return True
# The signature check using SHA1 failed. Let us try it with SHA256.
return verify(msg, sig, hexPubkey, "sha256")
try:
return makePubCryptor(hexPubkey).verify(
sig, msg, digest_alg=_choose_digest_alg(digestAlg))
except:
return False

View File

@ -1,10 +1,9 @@
"""The Inventory singleton""" """The Inventory"""
# TODO make this dynamic, and watch out for frozen, like with messagetypes # TODO make this dynamic, and watch out for frozen, like with messagetypes
import storage.filesystem import storage.filesystem
import storage.sqlite import storage.sqlite
from bmconfigparser import config from bmconfigparser import config
from singleton import Singleton
def create_inventory_instance(backend="sqlite"): def create_inventory_instance(backend="sqlite"):
@ -17,10 +16,9 @@ def create_inventory_instance(backend="sqlite"):
"{}Inventory".format(backend.title()))() "{}Inventory".format(backend.title()))()
@Singleton class Inventory:
class Inventory():
""" """
Inventory singleton class which uses storage backends Inventory class which uses storage backends
to manage the inventory. to manage the inventory.
""" """
def __init__(self): def __init__(self):
@ -45,3 +43,6 @@ class Inventory():
# hint for pylint: this is dictionary like object # hint for pylint: this is dictionary like object
def __getitem__(self, key): def __getitem__(self, key):
return self._realInventory[key] return self._realInventory[key]
def __setitem__(self, key, value):
self._realInventory[key] = value

13
src/main-android-live.py Normal file
View File

@ -0,0 +1,13 @@
"""This module is for thread start."""
import state
import sys
from bitmessagemain import main
from termcolor import colored
print(colored('kivy is not supported at the moment for this version..', 'red'))
sys.exit()
if __name__ == '__main__':
state.kivy = True
print("Kivy Loading......")
main()

View File

@ -1,13 +1,31 @@
"""This module is for thread start.""" # pylint: disable=unused-import, wrong-import-position, ungrouped-imports
# flake8: noqa:E401, E402
"""Mock kivy app with mock threads."""
import os
from kivy.config import Config
from mockbm import multiqueue
import state import state
import sys
from bitmessagemain import main from mockbm.class_addressGenerator import FakeAddressGenerator # noqa:E402
from termcolor import colored from bitmessagekivy.mpybit import NavigateApp # noqa:E402
print(colored('kivy is not supported at the moment for this version..', 'red')) from mockbm import network # noqa:E402
sys.exit()
stats = network.stats
objectracker = network.objectracker
if __name__ == '__main__': def main():
state.kivy = True """main method for starting threads"""
print("Kivy Loading......") addressGeneratorThread = FakeAddressGenerator()
addressGeneratorThread.daemon = True
addressGeneratorThread.start()
state.kivyapp = NavigateApp()
state.kivyapp.run()
addressGeneratorThread.stopThread()
if __name__ == "__main__":
os.environ['INSTALL_TESTS'] = "True"
main() main()

View File

@ -5,7 +5,7 @@
import os import os
from kivy.config import Config from kivy.config import Config
from pybitmessage.mock import multiqueue from pybitmessage.mockbm import multiqueue
from pybitmessage import state from pybitmessage import state
if os.environ.get("INSTALL_TESTS", False): if os.environ.get("INSTALL_TESTS", False):
@ -16,9 +16,9 @@ if os.environ.get("INSTALL_TESTS", False):
Config.set("graphics", "left", 0) Config.set("graphics", "left", 0)
from pybitmessage.mock.class_addressGenerator import FakeAddressGenerator # noqa:E402 from pybitmessage.mockbm.class_addressGenerator import FakeAddressGenerator # noqa:E402
from pybitmessage.bitmessagekivy.mpybit import NavigateApp # noqa:E402 from pybitmessage.bitmessagekivy.mpybit import NavigateApp # noqa:E402
from pybitmessage.mock import network # noqa:E402 from pybitmessage.mockbm import network # noqa:E402
stats = network.stats stats = network.stats
objectracker = network.objectracker objectracker = network.objectracker

View File

@ -1,23 +1,27 @@
""" """
Network subsystem package Network subsystem package
""" """
from six.moves import queue
try: from .dandelion import Dandelion
from .announcethread import AnnounceThread
from .connectionpool import BMConnectionPool
except ImportError:
AnnounceThread = None
BMConnectionPool = None
from .threads import StoppableThread from .threads import StoppableThread
from .multiqueue import MultiQueue
dandelion_ins = Dandelion()
__all__ = ["AnnounceThread", "BMConnectionPool", "StoppableThread"] # network queues
invQueue = MultiQueue()
addrQueue = MultiQueue()
portCheckerQueue = queue.Queue()
receiveDataQueue = queue.Queue()
__all__ = ["StoppableThread"]
def start(config, state): def start(config, state):
"""Start network threads""" """Start network threads"""
from .announcethread import AnnounceThread
import connectionpool # pylint: disable=relative-import
from .addrthread import AddrThread from .addrthread import AddrThread
from .dandelion import Dandelion
from .downloadthread import DownloadThread from .downloadthread import DownloadThread
from .invthread import InvThread from .invthread import InvThread
from .networkthread import BMNetworkThread from .networkthread import BMNetworkThread
@ -25,10 +29,13 @@ def start(config, state):
from .receivequeuethread import ReceiveQueueThread from .receivequeuethread import ReceiveQueueThread
from .uploadthread import UploadThread from .uploadthread import UploadThread
# check and set dandelion enabled value at network startup
dandelion_ins.init_dandelion_enabled(config)
# pass pool instance into dandelion class instance
dandelion_ins.init_pool(connectionpool.pool)
readKnownNodes() readKnownNodes()
# init, needs to be early because other thread may access it early connectionpool.pool.connectToStream(1)
Dandelion()
BMConnectionPool().connectToStream(1)
for thread in ( for thread in (
BMNetworkThread(), InvThread(), AddrThread(), BMNetworkThread(), InvThread(), AddrThread(),
DownloadThread(), UploadThread() DownloadThread(), UploadThread()

View File

@ -1,14 +1,13 @@
""" """
Announce addresses as they are received from other hosts Announce addresses as they are received from other hosts
""" """
import random
from six.moves import queue from six.moves import queue
# magic imports! # magic imports!
import state import connectionpool
from helper_random import randomshuffle
from protocol import assembleAddrMessage from protocol import assembleAddrMessage
from queues import addrQueue # FIXME: init with queue from network import addrQueue # FIXME: init with queue
from network.connectionpool import BMConnectionPool
from threads import StoppableThread from threads import StoppableThread
@ -18,7 +17,7 @@ class AddrThread(StoppableThread):
name = "AddrBroadcaster" name = "AddrBroadcaster"
def run(self): def run(self):
while not state.shutdown: while not self._stopped:
chunk = [] chunk = []
while True: while True:
try: try:
@ -29,10 +28,10 @@ class AddrThread(StoppableThread):
if chunk: if chunk:
# Choose peers randomly # Choose peers randomly
connections = BMConnectionPool().establishedConnections() connections = connectionpool.pool.establishedConnections()
randomshuffle(connections) random.shuffle(connections)
for i in connections: for i in connections:
randomshuffle(chunk) random.shuffle(chunk)
filtered = [] filtered = []
for stream, peer, seen, destination in chunk: for stream, peer, seen, destination in chunk:
# peer's own address or address received from peer # peer's own address or address received from peer

View File

@ -4,10 +4,9 @@ Announce myself (node address)
import time import time
# magic imports! # magic imports!
import state import connectionpool
from bmconfigparser import config from bmconfigparser import config
from protocol import assembleAddrMessage from protocol import assembleAddrMessage
from network.connectionpool import BMConnectionPool
from node import Peer from node import Peer
from threads import StoppableThread from threads import StoppableThread
@ -20,7 +19,7 @@ class AnnounceThread(StoppableThread):
def run(self): def run(self):
lastSelfAnnounced = 0 lastSelfAnnounced = 0
while not self._stopped and state.shutdown == 0: while not self._stopped:
processed = 0 processed = 0
if lastSelfAnnounced < time.time() - self.announceInterval: if lastSelfAnnounced < time.time() - self.announceInterval:
self.announceSelf() self.announceSelf()
@ -31,10 +30,10 @@ class AnnounceThread(StoppableThread):
@staticmethod @staticmethod
def announceSelf(): def announceSelf():
"""Announce our presence""" """Announce our presence"""
for connection in BMConnectionPool().udpSockets.values(): for connection in connectionpool.pool.udpSockets.values():
if not connection.announcing: if not connection.announcing:
continue continue
for stream in state.streamsInWhichIAmParticipating: for stream in connectionpool.pool.streams:
addr = ( addr = (
stream, stream,
Peer( Peer(

View File

@ -9,6 +9,7 @@ Basic infrastructure for asynchronous socket service clients and servers.
import os import os
import select import select
import socket import socket
import random
import sys import sys
import time import time
import warnings import warnings
@ -19,7 +20,6 @@ from errno import (
) )
from threading import current_thread from threading import current_thread
import helper_random
try: try:
from errno import WSAEWOULDBLOCK from errno import WSAEWOULDBLOCK
@ -233,13 +233,13 @@ def select_poller(timeout=0.0, map=None):
if err.args[0] in (WSAENOTSOCK, ): if err.args[0] in (WSAENOTSOCK, ):
return return
for fd in helper_random.randomsample(r, len(r)): for fd in random.sample(r, len(r)):
obj = map.get(fd) obj = map.get(fd)
if obj is None: if obj is None:
continue continue
read(obj) read(obj)
for fd in helper_random.randomsample(w, len(w)): for fd in random.sample(w, len(w)):
obj = map.get(fd) obj = map.get(fd)
if obj is None: if obj is None:
continue continue
@ -297,7 +297,7 @@ def poll_poller(timeout=0.0, map=None):
except socket.error as err: except socket.error as err:
if err.args[0] in (EBADF, WSAENOTSOCK, EINTR): if err.args[0] in (EBADF, WSAENOTSOCK, EINTR):
return return
for fd, flags in helper_random.randomsample(r, len(r)): for fd, flags in random.sample(r, len(r)):
obj = map.get(fd) obj = map.get(fd)
if obj is None: if obj is None:
continue continue
@ -357,7 +357,7 @@ def epoll_poller(timeout=0.0, map=None):
if err.args[0] != EINTR: if err.args[0] != EINTR:
raise raise
r = [] r = []
for fd, flags in helper_random.randomsample(r, len(r)): for fd, flags in random.sample(r, len(r)):
obj = map.get(fd) obj = map.get(fd)
if obj is None: if obj is None:
continue continue
@ -420,7 +420,7 @@ def kqueue_poller(timeout=0.0, map=None):
events = kqueue_poller.pollster.control(updates, selectables, timeout) events = kqueue_poller.pollster.control(updates, selectables, timeout)
if len(events) > 1: if len(events) > 1:
events = helper_random.randomsample(events, len(events)) events = random.sample(events, len(events))
for event in events: for event in events:
fd = event.ident fd = event.ident

View File

@ -6,9 +6,9 @@ import time
import protocol import protocol
import state import state
from addresses import calculateInventoryHash import connectionpool
from inventory import Inventory from network import dandelion_ins
from network.dandelion import Dandelion from highlevelcrypto import calculateInventoryHash
logger = logging.getLogger('default') logger = logging.getLogger('default')
@ -100,7 +100,7 @@ class BMObject(object): # pylint: disable=too-many-instance-attributes
logger.warning( logger.warning(
'The object has invalid stream: %s', self.streamNumber) 'The object has invalid stream: %s', self.streamNumber)
raise BMObjectInvalidError() raise BMObjectInvalidError()
if self.streamNumber not in state.streamsInWhichIAmParticipating: if self.streamNumber not in connectionpool.pool.streams:
logger.debug( logger.debug(
'The streamNumber %i isn\'t one we are interested in.', 'The streamNumber %i isn\'t one we are interested in.',
self.streamNumber) self.streamNumber)
@ -113,9 +113,9 @@ class BMObject(object): # pylint: disable=too-many-instance-attributes
or advertise it unnecessarily) or advertise it unnecessarily)
""" """
# if it's a stem duplicate, pretend we don't have it # if it's a stem duplicate, pretend we don't have it
if Dandelion().hasHash(self.inventoryHash): if dandelion_ins.hasHash(self.inventoryHash):
return return
if self.inventoryHash in Inventory(): if self.inventoryHash in state.Inventory:
raise BMObjectAlreadyHaveError() raise BMObjectAlreadyHaveError()
def checkObjectByType(self): def checkObjectByType(self):

View File

@ -9,17 +9,15 @@ import re
import socket import socket
import struct import struct
import time import time
from binascii import hexlify
# magic imports! # magic imports!
import addresses import addresses
import connectionpool
import knownnodes import knownnodes
import protocol import protocol
import state import state
import connectionpool
from bmconfigparser import config from bmconfigparser import config
from inventory import Inventory from queues import objectProcessorQueue
from queues import invQueue, objectProcessorQueue, portCheckerQueue
from randomtrackingdict import RandomTrackingDict from randomtrackingdict import RandomTrackingDict
from network.advanceddispatcher import AdvancedDispatcher from network.advanceddispatcher import AdvancedDispatcher
from network.bmobject import ( from network.bmobject import (
@ -27,9 +25,8 @@ from network.bmobject import (
BMObjectInsufficientPOWError, BMObjectInvalidError, BMObjectInsufficientPOWError, BMObjectInvalidError,
BMObjectUnwantedStreamError BMObjectUnwantedStreamError
) )
from network.dandelion import Dandelion
from network.proxy import ProxyError from network.proxy import ProxyError
from network import dandelion_ins, invQueue, portCheckerQueue
from node import Node, Peer from node import Node, Peer
from objectracker import ObjectTracker, missingObjects from objectracker import ObjectTracker, missingObjects
@ -340,27 +337,27 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
self.pendingUpload[str(i)] = now self.pendingUpload[str(i)] = now
return True return True
def _command_inv(self, dandelion=False): def _command_inv(self, extend_dandelion_stem=False):
""" """
Common inv announce implementation: Common inv announce implementation:
both inv and dinv depending on *dandelion* kwarg both inv and dinv depending on *extend_dandelion_stem* kwarg
""" """
items = self.decode_payload_content("l32s") items = self.decode_payload_content("l32s")
if len(items) > protocol.MAX_OBJECT_COUNT: if len(items) > protocol.MAX_OBJECT_COUNT:
logger.error( logger.error(
'Too many items in %sinv message!', 'd' if dandelion else '') 'Too many items in %sinv message!', 'd' if extend_dandelion_stem else '')
raise BMProtoExcessiveDataError() raise BMProtoExcessiveDataError()
# ignore dinv if dandelion turned off # ignore dinv if dandelion turned off
if dandelion and not state.dandelion: if extend_dandelion_stem and not dandelion_ins.enabled:
return True return True
for i in map(str, items): for i in map(str, items):
if i in Inventory() and not Dandelion().hasHash(i): if i in state.Inventory and not dandelion_ins.hasHash(i):
continue continue
if dandelion and not Dandelion().hasHash(i): if extend_dandelion_stem and not dandelion_ins.hasHash(i):
Dandelion().addHash(i, self) dandelion_ins.addHash(i, self)
self.handleReceivedInventory(i) self.handleReceivedInventory(i)
return True return True
@ -413,7 +410,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
try: try:
self.object.checkObjectByType() self.object.checkObjectByType()
objectProcessorQueue.put(( objectProcessorQueue.put((
self.object.objectType, buffer(self.object.data))) self.object.objectType, buffer(self.object.data))) # noqa: F821
except BMObjectInvalidError: except BMObjectInvalidError:
BMProto.stopDownloadingObject(self.object.inventoryHash, True) BMProto.stopDownloadingObject(self.object.inventoryHash, True)
else: else:
@ -422,15 +419,15 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
except KeyError: except KeyError:
pass pass
if self.object.inventoryHash in Inventory() and Dandelion().hasHash( if self.object.inventoryHash in state.Inventory and dandelion_ins.hasHash(
self.object.inventoryHash): self.object.inventoryHash):
Dandelion().removeHash( dandelion_ins.removeHash(
self.object.inventoryHash, "cycle detection") self.object.inventoryHash, "cycle detection")
Inventory()[self.object.inventoryHash] = ( state.Inventory[self.object.inventoryHash] = (
self.object.objectType, self.object.streamNumber, self.object.objectType, self.object.streamNumber,
buffer(self.payload[objectOffset:]), self.object.expiresTime, buffer(self.payload[objectOffset:]), self.object.expiresTime, # noqa: F821
buffer(self.object.tag) buffer(self.object.tag) # noqa: F821
) )
self.handleReceivedObject( self.handleReceivedObject(
self.object.streamNumber, self.object.inventoryHash) self.object.streamNumber, self.object.inventoryHash)
@ -448,7 +445,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
for seenTime, stream, _, ip, port in self._decode_addr(): for seenTime, stream, _, ip, port in self._decode_addr():
ip = str(ip) ip = str(ip)
if ( if (
stream not in state.streamsInWhichIAmParticipating stream not in connectionpool.pool.streams
# FIXME: should check against complete list # FIXME: should check against complete list
or ip.startswith('bootstrap') or ip.startswith('bootstrap')
): ):
@ -543,7 +540,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
if not self.isOutbound: if not self.isOutbound:
self.append_write_buf(protocol.assembleVersionMessage( self.append_write_buf(protocol.assembleVersionMessage(
self.destination.host, self.destination.port, self.destination.host, self.destination.port,
connectionpool.BMConnectionPool().streams, True, connectionpool.pool.streams, dandelion_ins.enabled, True,
nodeid=self.nodeid)) nodeid=self.nodeid))
logger.debug( logger.debug(
'%(host)s:%(port)i sending version', '%(host)s:%(port)i sending version',
@ -599,7 +596,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
'Closed connection to %s because there is no overlapping' 'Closed connection to %s because there is no overlapping'
' interest in streams.', self.destination) ' interest in streams.', self.destination)
return False return False
if connectionpool.BMConnectionPool().inboundConnections.get( if connectionpool.pool.inboundConnections.get(
self.destination): self.destination):
try: try:
if not protocol.checkSocksIP(self.destination.host): if not protocol.checkSocksIP(self.destination.host):
@ -610,15 +607,15 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
'Closed connection to %s because we are already' 'Closed connection to %s because we are already'
' connected to that IP.', self.destination) ' connected to that IP.', self.destination)
return False return False
except Exception: # TODO: exception types except Exception: # nosec B110 # pylint:disable=broad-exception-caught
pass pass
if not self.isOutbound: if not self.isOutbound:
# incoming from a peer we're connected to as outbound, # incoming from a peer we're connected to as outbound,
# or server full report the same error to counter deanonymisation # or server full report the same error to counter deanonymisation
if ( if (
Peer(self.destination.host, self.peerNode.port) Peer(self.destination.host, self.peerNode.port)
in connectionpool.BMConnectionPool().inboundConnections in connectionpool.pool.inboundConnections
or len(connectionpool.BMConnectionPool()) or len(connectionpool.pool)
> config.safeGetInt( > config.safeGetInt(
'bitmessagesettings', 'maxtotalconnections') 'bitmessagesettings', 'maxtotalconnections')
+ config.safeGetInt( + config.safeGetInt(
@ -630,7 +627,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
'Closed connection to %s due to server full' 'Closed connection to %s due to server full'
' or duplicate inbound/outbound.', self.destination) ' or duplicate inbound/outbound.', self.destination)
return False return False
if connectionpool.BMConnectionPool().isAlreadyConnected(self.nonce): if connectionpool.pool.isAlreadyConnected(self.nonce):
self.append_write_buf(protocol.assembleErrorMessage( self.append_write_buf(protocol.assembleErrorMessage(
errorText="I'm connected to myself. Closing connection.", errorText="I'm connected to myself. Closing connection.",
fatal=2)) fatal=2))
@ -644,7 +641,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
@staticmethod @staticmethod
def stopDownloadingObject(hashId, forwardAnyway=False): def stopDownloadingObject(hashId, forwardAnyway=False):
"""Stop downloading object *hashId*""" """Stop downloading object *hashId*"""
for connection in connectionpool.BMConnectionPool().connections(): for connection in connectionpool.pool.connections():
try: try:
del connection.objectsNewToMe[hashId] del connection.objectsNewToMe[hashId]
except KeyError: except KeyError:
@ -678,32 +675,3 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
except AttributeError: except AttributeError:
logger.debug('Disconnected socket closing') logger.debug('Disconnected socket closing')
AdvancedDispatcher.handle_close(self) AdvancedDispatcher.handle_close(self)
class BMStringParser(BMProto):
"""
A special case of BMProto used by objectProcessor to send ACK
"""
def __init__(self):
super(BMStringParser, self).__init__()
self.destination = Peer('127.0.0.1', 8444)
self.payload = None
ObjectTracker.__init__(self)
def send_data(self, data):
"""Send object given by the data string"""
# This class is introduced specially for ACK sending, please
# change log strings if you are going to use it for something else
self.bm_proto_reset()
self.payload = data
try:
self.bm_command_object()
except BMObjectAlreadyHaveError:
pass # maybe the same msg received on different nodes
except BMObjectExpiredError:
logger.debug(
'Sending ACK failure (expired): %s', hexlify(data))
except Exception as e:
logger.debug(
'Exception of type %s while sending ACK',
type(e), exc_info=True)

View File

@ -5,11 +5,14 @@ Select which node to connect to
import logging import logging
import random import random
from six.moves import queue
import knownnodes import knownnodes
import protocol import protocol
import state import state
from bmconfigparser import config from bmconfigparser import config
from queues import queue, portCheckerQueue from network import portCheckerQueue
logger = logging.getLogger('default') logger = logging.getLogger('default')

View File

@ -7,9 +7,9 @@ import re
import socket import socket
import sys import sys
import time import time
import random
import asyncore_pollchoose as asyncore import asyncore_pollchoose as asyncore
import helper_random
import knownnodes import knownnodes
import protocol import protocol
import state import state
@ -17,7 +17,6 @@ from bmconfigparser import config
from connectionchooser import chooseConnection from connectionchooser import chooseConnection
from node import Peer from node import Peer
from proxy import Proxy from proxy import Proxy
from singleton import Singleton
from tcp import ( from tcp import (
bootstrap, Socks4aBMConnection, Socks5BMConnection, bootstrap, Socks4aBMConnection, Socks5BMConnection,
TCPConnection, TCPServer) TCPConnection, TCPServer)
@ -26,7 +25,6 @@ from udp import UDPSocket
logger = logging.getLogger('default') logger = logging.getLogger('default')
@Singleton
class BMConnectionPool(object): class BMConnectionPool(object):
"""Pool of all existing connections""" """Pool of all existing connections"""
# pylint: disable=too-many-instance-attributes # pylint: disable=too-many-instance-attributes
@ -90,7 +88,6 @@ class BMConnectionPool(object):
def connectToStream(self, streamNumber): def connectToStream(self, streamNumber):
"""Connect to a bitmessage stream""" """Connect to a bitmessage stream"""
self.streams.append(streamNumber) self.streams.append(streamNumber)
state.streamsInWhichIAmParticipating.append(streamNumber)
def getConnectionByAddr(self, addr): def getConnectionByAddr(self, addr):
""" """
@ -213,7 +210,7 @@ class BMConnectionPool(object):
connection_base = TCPConnection connection_base = TCPConnection
elif proxy_type == 'SOCKS5': elif proxy_type == 'SOCKS5':
connection_base = Socks5BMConnection connection_base = Socks5BMConnection
hostname = helper_random.randomchoice([ hostname = random.choice([ # nosec B311
'quzwelsuziwqgpt2.onion', None 'quzwelsuziwqgpt2.onion', None
]) ])
elif proxy_type == 'SOCKS4a': elif proxy_type == 'SOCKS4a':
@ -225,7 +222,7 @@ class BMConnectionPool(object):
bootstrapper = bootstrap(connection_base) bootstrapper = bootstrap(connection_base)
if not hostname: if not hostname:
port = helper_random.randomchoice([8080, 8444]) port = random.choice([8080, 8444]) # nosec B311
hostname = 'bootstrap%s.bitmessage.org' % port hostname = 'bootstrap%s.bitmessage.org' % port
else: else:
port = 8444 port = 8444
@ -292,7 +289,7 @@ class BMConnectionPool(object):
state.maximumNumberOfHalfOpenConnections - pending): state.maximumNumberOfHalfOpenConnections - pending):
try: try:
chosen = self.trustedPeer or chooseConnection( chosen = self.trustedPeer or chooseConnection(
helper_random.randomchoice(self.streams)) random.choice(self.streams)) # nosec B311
except ValueError: except ValueError:
continue continue
if chosen in self.outboundConnections: if chosen in self.outboundConnections:
@ -403,3 +400,6 @@ class BMConnectionPool(object):
pass pass
for i in reaper: for i in reaper:
self.removeConnection(i) self.removeConnection(i)
pool = BMConnectionPool()

View File

@ -7,10 +7,6 @@ from random import choice, expovariate, sample
from threading import RLock from threading import RLock
from time import time from time import time
import connectionpool
import state
from queues import invQueue
from singleton import Singleton
# randomise routes after 600 seconds # randomise routes after 600 seconds
REASSIGN_INTERVAL = 600 REASSIGN_INTERVAL = 600
@ -26,7 +22,6 @@ Stem = namedtuple('Stem', ['child', 'stream', 'timeout'])
logger = logging.getLogger('default') logger = logging.getLogger('default')
@Singleton
class Dandelion: # pylint: disable=old-style-class class Dandelion: # pylint: disable=old-style-class
"""Dandelion class for tracking stem/fluff stages.""" """Dandelion class for tracking stem/fluff stages."""
def __init__(self): def __init__(self):
@ -39,6 +34,8 @@ class Dandelion: # pylint: disable=old-style-class
# when to rerandomise routes # when to rerandomise routes
self.refresh = time() + REASSIGN_INTERVAL self.refresh = time() + REASSIGN_INTERVAL
self.lock = RLock() self.lock = RLock()
self.enabled = None
self.pool = None
@staticmethod @staticmethod
def poissonTimeout(start=None, average=0): def poissonTimeout(start=None, average=0):
@ -49,10 +46,23 @@ class Dandelion: # pylint: disable=old-style-class
average = FLUFF_TRIGGER_MEAN_DELAY average = FLUFF_TRIGGER_MEAN_DELAY
return start + expovariate(1.0 / average) + FLUFF_TRIGGER_FIXED_DELAY return start + expovariate(1.0 / average) + FLUFF_TRIGGER_FIXED_DELAY
def init_pool(self, pool):
"""pass pool instance"""
self.pool = pool
def init_dandelion_enabled(self, config):
"""Check if Dandelion is enabled and set value in enabled attribute"""
dandelion_enabled = config.safeGetInt('network', 'dandelion')
# dandelion requires outbound connections, without them,
# stem objects will get stuck forever
if not config.safeGetBoolean(
'bitmessagesettings', 'sendoutgoingconnections'):
dandelion_enabled = 0
self.enabled = dandelion_enabled
def addHash(self, hashId, source=None, stream=1): def addHash(self, hashId, source=None, stream=1):
"""Add inventory vector to dandelion stem""" """Add inventory vector to dandelion stem return status of dandelion enabled"""
if not state.dandelion: assert self.enabled is not None
return
with self.lock: with self.lock:
self.hashMap[hashId] = Stem( self.hashMap[hashId] = Stem(
self.getNodeStem(source), self.getNodeStem(source),
@ -91,7 +101,7 @@ class Dandelion: # pylint: disable=old-style-class
"""Child (i.e. next) node for an inventory vector during stem mode""" """Child (i.e. next) node for an inventory vector during stem mode"""
return self.hashMap[hashId].child return self.hashMap[hashId].child
def maybeAddStem(self, connection): def maybeAddStem(self, connection, invQueue):
""" """
If we had too few outbound connections, add the current one to the If we had too few outbound connections, add the current one to the
current stem list. Dandelion as designed by the authors should current stem list. Dandelion as designed by the authors should
@ -165,7 +175,7 @@ class Dandelion: # pylint: disable=old-style-class
self.nodeMap[node] = self.pickStem(node) self.nodeMap[node] = self.pickStem(node)
return self.nodeMap[node] return self.nodeMap[node]
def expire(self): def expire(self, invQueue):
"""Switch expired objects from stem to fluff mode""" """Switch expired objects from stem to fluff mode"""
with self.lock: with self.lock:
deadline = time() deadline = time()
@ -181,16 +191,18 @@ class Dandelion: # pylint: disable=old-style-class
def reRandomiseStems(self): def reRandomiseStems(self):
"""Re-shuffle stem mapping (parent <-> child pairs)""" """Re-shuffle stem mapping (parent <-> child pairs)"""
assert self.pool is not None
if self.refresh > time():
return
with self.lock: with self.lock:
try: try:
# random two connections # random two connections
self.stem = sample( self.stem = sample(
connectionpool.BMConnectionPool( self.pool.outboundConnections.values(), MAX_STEMS)
).outboundConnections.values(), MAX_STEMS)
# not enough stems available # not enough stems available
except ValueError: except ValueError:
self.stem = connectionpool.BMConnectionPool( self.stem = self.pool.outboundConnections.values()
).outboundConnections.values()
self.nodeMap = {} self.nodeMap = {}
# hashMap stays to cater for pending stems # hashMap stays to cater for pending stems
self.refresh = time() + REASSIGN_INTERVAL self.refresh = time() + REASSIGN_INTERVAL

View File

@ -2,13 +2,12 @@
`DownloadThread` class definition `DownloadThread` class definition
""" """
import time import time
import random
import state
import addresses import addresses
import helper_random
import protocol import protocol
from dandelion import Dandelion import connectionpool
from inventory import Inventory from network import dandelion_ins
from network.connectionpool import BMConnectionPool
from objectracker import missingObjects from objectracker import missingObjects
from threads import StoppableThread from threads import StoppableThread
@ -43,8 +42,8 @@ class DownloadThread(StoppableThread):
while not self._stopped: while not self._stopped:
requested = 0 requested = 0
# Choose downloading peers randomly # Choose downloading peers randomly
connections = BMConnectionPool().establishedConnections() connections = connectionpool.pool.establishedConnections()
helper_random.randomshuffle(connections) random.shuffle(connections)
requestChunk = max(int( requestChunk = max(int(
min(self.maxRequestChunk, len(missingObjects)) min(self.maxRequestChunk, len(missingObjects))
/ len(connections)), 1) if connections else 1 / len(connections)), 1) if connections else 1
@ -61,7 +60,7 @@ class DownloadThread(StoppableThread):
payload = bytearray() payload = bytearray()
chunkCount = 0 chunkCount = 0
for chunk in request: for chunk in request:
if chunk in Inventory() and not Dandelion().hasHash(chunk): if chunk in state.Inventory and not dandelion_ins.hasHash(chunk):
try: try:
del i.objectsNewToMe[chunk] del i.objectsNewToMe[chunk]
except KeyError: except KeyError:

View File

@ -8,9 +8,8 @@ from time import time
import addresses import addresses
import protocol import protocol
import state import state
from network.connectionpool import BMConnectionPool import connectionpool
from network.dandelion import Dandelion from network import dandelion_ins, invQueue
from queues import invQueue
from threads import StoppableThread from threads import StoppableThread
@ -19,7 +18,7 @@ def handleExpiredDandelion(expired):
the object""" the object"""
if not expired: if not expired:
return return
for i in BMConnectionPool().connections(): for i in connectionpool.pool.connections():
if not i.fullyEstablished: if not i.fullyEstablished:
continue continue
for x in expired: for x in expired:
@ -40,10 +39,10 @@ class InvThread(StoppableThread):
@staticmethod @staticmethod
def handleLocallyGenerated(stream, hashId): def handleLocallyGenerated(stream, hashId):
"""Locally generated inventory items require special handling""" """Locally generated inventory items require special handling"""
Dandelion().addHash(hashId, stream=stream) dandelion_ins.addHash(hashId, stream=stream)
for connection in BMConnectionPool().connections(): for connection in connectionpool.pool.connections():
if state.dandelion and connection != \ if dandelion_ins.enabled and connection != \
Dandelion().objectChildStem(hashId): dandelion_ins.objectChildStem(hashId):
continue continue
connection.objectsNewToThem[hashId] = time() connection.objectsNewToThem[hashId] = time()
@ -52,7 +51,7 @@ class InvThread(StoppableThread):
chunk = [] chunk = []
while True: while True:
# Dandelion fluff trigger by expiration # Dandelion fluff trigger by expiration
handleExpiredDandelion(Dandelion().expire()) handleExpiredDandelion(dandelion_ins.expire(invQueue))
try: try:
data = invQueue.get(False) data = invQueue.get(False)
chunk.append((data[0], data[1])) chunk.append((data[0], data[1]))
@ -63,7 +62,7 @@ class InvThread(StoppableThread):
break break
if chunk: if chunk:
for connection in BMConnectionPool().connections(): for connection in connectionpool.pool.connections():
fluffs = [] fluffs = []
stems = [] stems = []
for inv in chunk: for inv in chunk:
@ -75,10 +74,10 @@ class InvThread(StoppableThread):
except KeyError: except KeyError:
continue continue
try: try:
if connection == Dandelion().objectChildStem(inv[1]): if connection == dandelion_ins.objectChildStem(inv[1]):
# Fluff trigger by RNG # Fluff trigger by RNG
# auto-ignore if config set to 0, i.e. dandelion is off # auto-ignore if config set to 0, i.e. dandelion is off
if random.randint(1, 100) >= state.dandelion: # nosec:B311 if random.randint(1, 100) >= dandelion_ins.enabled: # nosec B311
fluffs.append(inv[1]) fluffs.append(inv[1])
# send a dinv only if the stem node supports dandelion # send a dinv only if the stem node supports dandelion
elif connection.services & protocol.NODE_DANDELION > 0: elif connection.services & protocol.NODE_DANDELION > 0:
@ -105,7 +104,6 @@ class InvThread(StoppableThread):
for _ in range(len(chunk)): for _ in range(len(chunk)):
invQueue.task_done() invQueue.task_done()
if Dandelion().refresh < time(): dandelion_ins.reRandomiseStems()
Dandelion().reRandomiseStems()
self.stop.wait(1) self.stop.wait(1)

View File

@ -85,7 +85,7 @@ def pickle_deserialize_old_knownnodes(source):
the new format is {Peer:{"lastseen":i, "rating":f}} the new format is {Peer:{"lastseen":i, "rating":f}}
""" """
global knownNodes global knownNodes
knownNodes = pickle.load(source) knownNodes = pickle.load(source) # nosec B301
for stream in knownNodes.keys(): for stream in knownNodes.keys():
for node, params in knownNodes[stream].iteritems(): for node, params in knownNodes[stream].iteritems():
if isinstance(params, (float, int)): if isinstance(params, (float, int)):
@ -226,7 +226,7 @@ def dns():
1, Peer('bootstrap%s.bitmessage.org' % port, port)) 1, Peer('bootstrap%s.bitmessage.org' % port, port))
def cleanupKnownNodes(): def cleanupKnownNodes(pool):
""" """
Cleanup knownnodes: remove old nodes and nodes with low rating Cleanup knownnodes: remove old nodes and nodes with low rating
""" """
@ -236,7 +236,7 @@ def cleanupKnownNodes():
with knownNodesLock: with knownNodesLock:
for stream in knownNodes: for stream in knownNodes:
if stream not in state.streamsInWhichIAmParticipating: if stream not in pool.streams:
continue continue
keys = knownNodes[stream].keys() keys = knownNodes[stream].keys()
for node in keys: for node in keys:

View File

@ -2,16 +2,11 @@
A queue with multiple internal subqueues. A queue with multiple internal subqueues.
Elements are added into a random subqueue, and retrieval rotates Elements are added into a random subqueue, and retrieval rotates
""" """
import random
from collections import deque from collections import deque
from six.moves import queue from six.moves import queue
try:
import helper_random
except ImportError:
from . import helper_random
class MultiQueue(queue.Queue): class MultiQueue(queue.Queue):
"""A base queue class""" """A base queue class"""
@ -38,7 +33,7 @@ class MultiQueue(queue.Queue):
# Put a new item in the queue # Put a new item in the queue
def _put(self, item): def _put(self, item):
# self.queue.append(item) # self.queue.append(item)
self.queues[helper_random.randomrandrange(self.queueCount)].append( self.queues[random.randrange(self.queueCount)].append( # nosec B311
(item)) (item))
# Get an item from the queue # Get an item from the queue

View File

@ -2,8 +2,7 @@
A thread to handle network concerns A thread to handle network concerns
""" """
import network.asyncore_pollchoose as asyncore import network.asyncore_pollchoose as asyncore
import state import connectionpool
from network.connectionpool import BMConnectionPool
from queues import excQueue from queues import excQueue
from threads import StoppableThread from threads import StoppableThread
@ -14,28 +13,28 @@ class BMNetworkThread(StoppableThread):
def run(self): def run(self):
try: try:
while not self._stopped and state.shutdown == 0: while not self._stopped:
BMConnectionPool().loop() connectionpool.pool.loop()
except Exception as e: except Exception as e:
excQueue.put((self.name, e)) excQueue.put((self.name, e))
raise raise
def stopThread(self): def stopThread(self):
super(BMNetworkThread, self).stopThread() super(BMNetworkThread, self).stopThread()
for i in BMConnectionPool().listeningSockets.values(): for i in connectionpool.pool.listeningSockets.values():
try: try:
i.close() i.close()
except: # nosec:B110 pylint:disable=bare-except except: # nosec B110 # pylint:disable=bare-except
pass pass
for i in BMConnectionPool().outboundConnections.values(): for i in connectionpool.pool.outboundConnections.values():
try: try:
i.close() i.close()
except: # nosec:B110 pylint:disable=bare-except except: # nosec B110 # pylint:disable=bare-except
pass pass
for i in BMConnectionPool().inboundConnections.values(): for i in connectionpool.pool.inboundConnections.values():
try: try:
i.close() i.close()
except: # nosec:B110 pylint:disable=bare-except except: # nosec B110 # pylint:disable=bare-except
pass pass
# just in case # just in case

View File

@ -4,8 +4,8 @@ Module for tracking objects
import time import time
from threading import RLock from threading import RLock
import network.connectionpool import connectionpool
from network.dandelion import Dandelion from network import dandelion_ins
from randomtrackingdict import RandomTrackingDict from randomtrackingdict import RandomTrackingDict
haveBloom = False haveBloom = False
@ -100,21 +100,21 @@ class ObjectTracker(object):
def handleReceivedObject(self, streamNumber, hashid): def handleReceivedObject(self, streamNumber, hashid):
"""Handling received object""" """Handling received object"""
for i in network.connectionpool.BMConnectionPool().connections(): for i in connectionpool.pool.connections():
if not i.fullyEstablished: if not i.fullyEstablished:
continue continue
try: try:
del i.objectsNewToMe[hashid] del i.objectsNewToMe[hashid]
except KeyError: except KeyError:
if streamNumber in i.streams and ( if streamNumber in i.streams and (
not Dandelion().hasHash(hashid) not dandelion_ins.hasHash(hashid)
or Dandelion().objectChildStem(hashid) == i): or dandelion_ins.objectChildStem(hashid) == i):
with i.objectsNewToThemLock: with i.objectsNewToThemLock:
i.objectsNewToThem[hashid] = time.time() i.objectsNewToThem[hashid] = time.time()
# update stream number, # update stream number,
# which we didn't have when we just received the dinv # which we didn't have when we just received the dinv
# also resets expiration of the stem mode # also resets expiration of the stem mode
Dandelion().setHashStream(hashid, streamNumber) dandelion_ins.setHashStream(hashid, streamNumber)
if i == self: if i == self:
try: try:

View File

@ -5,10 +5,9 @@ import errno
import Queue import Queue
import socket import socket
import state import connectionpool
from network.advanceddispatcher import UnknownStateError from network.advanceddispatcher import UnknownStateError
from network.connectionpool import BMConnectionPool from network import receiveDataQueue
from queues import receiveDataQueue
from threads import StoppableThread from threads import StoppableThread
@ -19,13 +18,13 @@ class ReceiveQueueThread(StoppableThread):
super(ReceiveQueueThread, self).__init__(name="ReceiveQueue_%i" % num) super(ReceiveQueueThread, self).__init__(name="ReceiveQueue_%i" % num)
def run(self): def run(self):
while not self._stopped and state.shutdown == 0: while not self._stopped:
try: try:
dest = receiveDataQueue.get(block=True, timeout=1) dest = receiveDataQueue.get(block=True, timeout=1)
except Queue.Empty: except Queue.Empty:
continue continue
if self._stopped or state.shutdown: if self._stopped:
break break
# cycle as long as there is data # cycle as long as there is data
@ -36,7 +35,7 @@ class ReceiveQueueThread(StoppableThread):
# enough data, or the connection is to be aborted # enough data, or the connection is to be aborted
try: try:
connection = BMConnectionPool().getConnectionByAddr(dest) connection = connectionpool.pool.getConnectionByAddr(dest)
# connection object not found # connection object not found
except KeyError: except KeyError:
receiveDataQueue.task_done() receiveDataQueue.task_done()

View File

@ -4,7 +4,7 @@ Network statistics
import time import time
import asyncore_pollchoose as asyncore import asyncore_pollchoose as asyncore
from network.connectionpool import BMConnectionPool import connectionpool
from objectracker import missingObjects from objectracker import missingObjects
@ -18,7 +18,7 @@ currentSentSpeed = 0
def connectedHostsList(): def connectedHostsList():
"""List of all the connected hosts""" """List of all the connected hosts"""
return BMConnectionPool().establishedConnections() return connectionpool.pool.establishedConnections()
def sentBytes(): def sentBytes():
@ -69,8 +69,8 @@ def pendingDownload():
def pendingUpload(): def pendingUpload():
"""Getting pending uploads""" """Getting pending uploads"""
# tmp = {} # tmp = {}
# for connection in BMConnectionPool().inboundConnections.values() + \ # for connection in connectionpool.pool.inboundConnections.values() + \
# BMConnectionPool().outboundConnections.values(): # connectionpool.pool.outboundConnections.values():
# for k in connection.objectsNewToThem.keys(): # for k in connection.objectsNewToThem.keys():
# tmp[k] = True # tmp[k] = True
# This probably isn't the correct logic so it's disabled # This probably isn't the correct logic so it's disabled

View File

@ -11,22 +11,20 @@ import time
# magic imports! # magic imports!
import addresses import addresses
import helper_random
import l10n import l10n
import protocol import protocol
import state import state
import connectionpool
from bmconfigparser import config from bmconfigparser import config
from helper_random import randomBytes from highlevelcrypto import randomBytes
from inventory import Inventory from network import dandelion_ins, invQueue, receiveDataQueue
from queues import invQueue, receiveDataQueue, UISignalQueue from queues import UISignalQueue
from tr import _translate from tr import _translate
import asyncore_pollchoose as asyncore import asyncore_pollchoose as asyncore
import connectionpool
import knownnodes import knownnodes
from network.advanceddispatcher import AdvancedDispatcher from network.advanceddispatcher import AdvancedDispatcher
from network.bmproto import BMProto from network.bmproto import BMProto
from network.dandelion import Dandelion
from network.objectracker import ObjectTracker from network.objectracker import ObjectTracker
from network.socks4a import Socks4aConnection from network.socks4a import Socks4aConnection
from network.socks5 import Socks5Connection from network.socks5 import Socks5Connection
@ -170,7 +168,7 @@ class TCPConnection(BMProto, TLSDispatcher):
knownnodes.increaseRating(self.destination) knownnodes.increaseRating(self.destination)
knownnodes.addKnownNode( knownnodes.addKnownNode(
self.streams, self.destination, time.time()) self.streams, self.destination, time.time())
Dandelion().maybeAddStem(self) dandelion_ins.maybeAddStem(self, invQueue)
self.sendAddr() self.sendAddr()
self.sendBigInv() self.sendBigInv()
@ -202,7 +200,7 @@ class TCPConnection(BMProto, TLSDispatcher):
elemCount = min( elemCount = min(
len(filtered), len(filtered),
maxAddrCount / 2 if n else maxAddrCount) maxAddrCount / 2 if n else maxAddrCount)
addrs[s] = helper_random.randomsample(filtered, elemCount) addrs[s] = random.sample(filtered, elemCount)
for substream in addrs: for substream in addrs:
for peer, params in addrs[substream]: for peer, params in addrs[substream]:
templist.append((substream, peer, params["lastseen"])) templist.append((substream, peer, params["lastseen"]))
@ -230,9 +228,9 @@ class TCPConnection(BMProto, TLSDispatcher):
# may lock for a long time, but I think it's better than # may lock for a long time, but I think it's better than
# thousands of small locks # thousands of small locks
with self.objectsNewToThemLock: with self.objectsNewToThemLock:
for objHash in Inventory().unexpired_hashes_by_stream(stream): for objHash in state.Inventory.unexpired_hashes_by_stream(stream):
# don't advertise stem objects on bigInv # don't advertise stem objects on bigInv
if Dandelion().hasHash(objHash): if dandelion_ins.hasHash(objHash):
continue continue
bigInvList[objHash] = 0 bigInvList[objHash] = 0
objectCount = 0 objectCount = 0
@ -269,7 +267,7 @@ class TCPConnection(BMProto, TLSDispatcher):
self.append_write_buf( self.append_write_buf(
protocol.assembleVersionMessage( protocol.assembleVersionMessage(
self.destination.host, self.destination.port, self.destination.host, self.destination.port,
connectionpool.BMConnectionPool().streams, connectionpool.pool.streams, dandelion_ins.enabled,
False, nodeid=self.nodeid)) False, nodeid=self.nodeid))
self.connectedAt = time.time() self.connectedAt = time.time()
receiveDataQueue.put(self.destination) receiveDataQueue.put(self.destination)
@ -294,7 +292,7 @@ class TCPConnection(BMProto, TLSDispatcher):
if host_is_global: if host_is_global:
knownnodes.addKnownNode( knownnodes.addKnownNode(
self.streams, self.destination, time.time()) self.streams, self.destination, time.time())
Dandelion().maybeRemoveStem(self) dandelion_ins.maybeRemoveStem(self)
else: else:
self.checkTimeOffsetNotification() self.checkTimeOffsetNotification()
if host_is_global: if host_is_global:
@ -320,7 +318,7 @@ class Socks5BMConnection(Socks5Connection, TCPConnection):
self.append_write_buf( self.append_write_buf(
protocol.assembleVersionMessage( protocol.assembleVersionMessage(
self.destination.host, self.destination.port, self.destination.host, self.destination.port,
connectionpool.BMConnectionPool().streams, connectionpool.pool.streams, dandelion_ins.enabled,
False, nodeid=self.nodeid)) False, nodeid=self.nodeid))
self.set_state("bm_header", expectBytes=protocol.Header.size) self.set_state("bm_header", expectBytes=protocol.Header.size)
return True return True
@ -344,7 +342,7 @@ class Socks4aBMConnection(Socks4aConnection, TCPConnection):
self.append_write_buf( self.append_write_buf(
protocol.assembleVersionMessage( protocol.assembleVersionMessage(
self.destination.host, self.destination.port, self.destination.host, self.destination.port,
connectionpool.BMConnectionPool().streams, connectionpool.pool.streams, dandelion_ins.enabled,
False, nodeid=self.nodeid)) False, nodeid=self.nodeid))
self.set_state("bm_header", expectBytes=protocol.Header.size) self.set_state("bm_header", expectBytes=protocol.Header.size)
return True return True
@ -432,7 +430,7 @@ class TCPServer(AdvancedDispatcher):
state.ownAddresses[Peer(*sock.getsockname())] = True state.ownAddresses[Peer(*sock.getsockname())] = True
if ( if (
len(connectionpool.BMConnectionPool()) len(connectionpool.pool)
> config.safeGetInt( > config.safeGetInt(
'bitmessagesettings', 'maxtotalconnections') 'bitmessagesettings', 'maxtotalconnections')
+ config.safeGetInt( + config.safeGetInt(
@ -444,7 +442,7 @@ class TCPServer(AdvancedDispatcher):
sock.close() sock.close()
return return
try: try:
connectionpool.BMConnectionPool().addConnection( connectionpool.pool.addConnection(
TCPConnection(sock=sock)) TCPConnection(sock=sock))
except socket.error: except socket.error:
pass pass

View File

@ -10,7 +10,7 @@ import sys
import network.asyncore_pollchoose as asyncore import network.asyncore_pollchoose as asyncore
import paths import paths
from network.advanceddispatcher import AdvancedDispatcher from network.advanceddispatcher import AdvancedDispatcher
from queues import receiveDataQueue from network import receiveDataQueue
logger = logging.getLogger('default') logger = logging.getLogger('default')

View File

@ -8,8 +8,9 @@ import time
# magic imports! # magic imports!
import protocol import protocol
import state import state
from queues import receiveDataQueue import connectionpool
from network import receiveDataQueue
from bmproto import BMProto from bmproto import BMProto
from node import Peer from node import Peer
from objectracker import ObjectTracker from objectracker import ObjectTracker
@ -81,7 +82,7 @@ class UDPSocket(BMProto): # pylint: disable=too-many-instance-attributes
remoteport = False remoteport = False
for seenTime, stream, _, ip, port in addresses: for seenTime, stream, _, ip, port in addresses:
decodedIP = protocol.checkIPAddress(str(ip)) decodedIP = protocol.checkIPAddress(str(ip))
if stream not in state.streamsInWhichIAmParticipating: if stream not in connectionpool.pool.streams:
continue continue
if (seenTime < time.time() - protocol.MAX_TIME_OFFSET if (seenTime < time.time() - protocol.MAX_TIME_OFFSET
or seenTime > time.time() + protocol.MAX_TIME_OFFSET): or seenTime > time.time() + protocol.MAX_TIME_OFFSET):

View File

@ -3,12 +3,12 @@
""" """
import time import time
import helper_random import random
import protocol import protocol
from inventory import Inventory import state
from network.connectionpool import BMConnectionPool import connectionpool
from network.dandelion import Dandelion
from randomtrackingdict import RandomTrackingDict from randomtrackingdict import RandomTrackingDict
from network import dandelion_ins
from threads import StoppableThread from threads import StoppableThread
@ -23,8 +23,8 @@ class UploadThread(StoppableThread):
while not self._stopped: while not self._stopped:
uploaded = 0 uploaded = 0
# Choose uploading peers randomly # Choose uploading peers randomly
connections = BMConnectionPool().establishedConnections() connections = connectionpool.pool.establishedConnections()
helper_random.randomshuffle(connections) random.shuffle(connections)
for i in connections: for i in connections:
now = time.time() now = time.time()
# avoid unnecessary delay # avoid unnecessary delay
@ -41,8 +41,8 @@ class UploadThread(StoppableThread):
chunk_count = 0 chunk_count = 0
for chunk in request: for chunk in request:
del i.pendingUpload[chunk] del i.pendingUpload[chunk]
if Dandelion().hasHash(chunk) and \ if dandelion_ins.hasHash(chunk) and \
i != Dandelion().objectChildStem(chunk): i != dandelion_ins.objectChildStem(chunk):
i.antiIntersectionDelay() i.antiIntersectionDelay()
self.logger.info( self.logger.info(
'%s asked for a stem object we didn\'t offer to it.', '%s asked for a stem object we didn\'t offer to it.',
@ -50,7 +50,7 @@ class UploadThread(StoppableThread):
break break
try: try:
payload.extend(protocol.CreatePacket( payload.extend(protocol.CreatePacket(
'object', Inventory()[chunk].payload)) 'object', state.Inventory[chunk].payload))
chunk_count += 1 chunk_count += 1
except KeyError: except KeyError:
i.antiIntersectionDelay() i.antiIntersectionDelay()

View File

@ -47,7 +47,7 @@ def initCL():
device_type=cl.device_type.GPU)) device_type=cl.device_type.GPU))
if platform.vendor not in vendors: if platform.vendor not in vendors:
vendors.append(platform.vendor) vendors.append(platform.vendor)
except: # nosec:B110 noqa:E722 pylint:disable=bare-except except: # nosec B110 # noqa:E722 # pylint:disable=bare-except
pass pass
if enabledGpus: if enabledGpus:
ctx = cl.Context(devices=enabledGpus) ctx = cl.Context(devices=enabledGpus)

View File

@ -11,14 +11,14 @@ try:
winsound.PlaySound(sound_file, winsound.SND_FILENAME) winsound.PlaySound(sound_file, winsound.SND_FILENAME)
except ImportError: except ImportError:
import os import os
import subprocess import subprocess # nosec B404
play_cmd = {} play_cmd = {}
def _subprocess(*args): def _subprocess(*args):
FNULL = open(os.devnull, 'wb') FNULL = open(os.devnull, 'wb')
subprocess.call( subprocess.call(
args, stdout=FNULL, stderr=subprocess.STDOUT, close_fds=True) args, stdout=FNULL, stderr=subprocess.STDOUT, close_fds=True) # nosec B603
def connect_plugin(sound_file): def connect_plugin(sound_file):
"""This function implements the entry point.""" """This function implements the entry point."""

View File

@ -4,14 +4,14 @@ Proof of work calculation
""" """
import ctypes import ctypes
import hashlib
import os import os
import subprocess # nosec B404
import sys import sys
import tempfile import tempfile
import time import time
from struct import pack, unpack from struct import pack, unpack
from subprocess import call
import highlevelcrypto
import openclpow import openclpow
import paths import paths
import queues import queues
@ -82,18 +82,25 @@ def _set_idle():
pid = win32api.GetCurrentProcessId() pid = win32api.GetCurrentProcessId()
handle = win32api.OpenProcess(win32con.PROCESS_ALL_ACCESS, True, pid) handle = win32api.OpenProcess(win32con.PROCESS_ALL_ACCESS, True, pid)
win32process.SetPriorityClass(handle, win32process.IDLE_PRIORITY_CLASS) win32process.SetPriorityClass(handle, win32process.IDLE_PRIORITY_CLASS)
except: # nosec:B110 noqa:E722 pylint:disable=bare-except except: # nosec B110 # noqa:E722 # pylint:disable=bare-except
# Windows 64-bit # Windows 64-bit
pass pass
def trial_value(nonce, initialHash):
"""Calculate PoW trial value"""
trialValue, = unpack(
'>Q', highlevelcrypto.double_sha512(
pack('>Q', nonce) + initialHash)[0:8])
return trialValue
def _pool_worker(nonce, initialHash, target, pool_size): def _pool_worker(nonce, initialHash, target, pool_size):
_set_idle() _set_idle()
trialValue = float('inf') trialValue = float('inf')
while trialValue > target: while trialValue > target:
nonce += pool_size nonce += pool_size
trialValue, = unpack('>Q', hashlib.sha512(hashlib.sha512( trialValue = trial_value(nonce, initialHash)
pack('>Q', nonce) + initialHash).digest()).digest()[0:8])
return [trialValue, nonce] return [trialValue, nonce]
@ -103,10 +110,9 @@ def _doSafePoW(target, initialHash):
trialValue = float('inf') trialValue = float('inf')
while trialValue > target and state.shutdown == 0: while trialValue > target and state.shutdown == 0:
nonce += 1 nonce += 1
trialValue, = unpack('>Q', hashlib.sha512(hashlib.sha512( trialValue = trial_value(nonce, initialHash)
pack('>Q', nonce) + initialHash).digest()).digest()[0:8])
if state.shutdown != 0: if state.shutdown != 0:
raise StopIteration("Interrupted") # pylint: misplaced-bare-raise raise StopIteration("Interrupted")
logger.debug("Safe PoW done") logger.debug("Safe PoW done")
return [trialValue, nonce] return [trialValue, nonce]
@ -135,7 +141,7 @@ def _doFastPoW(target, initialHash):
try: try:
pool.terminate() pool.terminate()
pool.join() pool.join()
except: # noqa:E722 except: # nosec B110 # noqa:E722 # pylint:disable=bare-except
pass pass
raise StopIteration("Interrupted") raise StopIteration("Interrupted")
for i in range(pool_size): for i in range(pool_size):
@ -163,7 +169,7 @@ def _doCPoW(target, initialHash):
logger.debug("C PoW start") logger.debug("C PoW start")
nonce = bmpow(out_h, out_m) nonce = bmpow(out_h, out_m)
trialValue, = unpack('>Q', hashlib.sha512(hashlib.sha512(pack('>Q', nonce) + initialHash).digest()).digest()[0:8]) trialValue = trial_value(nonce, initialHash)
if state.shutdown != 0: if state.shutdown != 0:
raise StopIteration("Interrupted") raise StopIteration("Interrupted")
logger.debug("C PoW done") logger.debug("C PoW done")
@ -173,7 +179,7 @@ def _doCPoW(target, initialHash):
def _doGPUPoW(target, initialHash): def _doGPUPoW(target, initialHash):
logger.debug("GPU PoW start") logger.debug("GPU PoW start")
nonce = openclpow.do_opencl_pow(initialHash.encode("hex"), target) nonce = openclpow.do_opencl_pow(initialHash.encode("hex"), target)
trialValue, = unpack('>Q', hashlib.sha512(hashlib.sha512(pack('>Q', nonce) + initialHash).digest()).digest()[0:8]) trialValue = trial_value(nonce, initialHash)
if trialValue > target: if trialValue > target:
deviceNames = ", ".join(gpu.name for gpu in openclpow.enabledGpus) deviceNames = ", ".join(gpu.name for gpu in openclpow.enabledGpus)
queues.UISignalQueue.put(( queues.UISignalQueue.put((
@ -272,16 +278,26 @@ def buildCPoW():
try: try:
if "bsd" in sys.platform: if "bsd" in sys.platform:
# BSD make # BSD make
call(["make", "-C", os.path.join(paths.codePath(), "bitmsghash"), '-f', 'Makefile.bsd']) subprocess.check_call([ # nosec B607, B603
"make", "-C", os.path.join(paths.codePath(), "bitmsghash"),
'-f', 'Makefile.bsd'])
else: else:
# GNU make # GNU make
call(["make", "-C", os.path.join(paths.codePath(), "bitmsghash")]) subprocess.check_call([ # nosec B607, B603
if os.path.exists(os.path.join(paths.codePath(), "bitmsghash", "bitmsghash.so")): "make", "-C", os.path.join(paths.codePath(), "bitmsghash")])
if os.path.exists(
os.path.join(paths.codePath(), "bitmsghash", "bitmsghash.so")
):
init() init()
notifyBuild(True) notifyBuild(True)
else: else:
notifyBuild(True) notifyBuild(True)
except (OSError, subprocess.CalledProcessError):
notifyBuild(True)
except: # noqa:E722 except: # noqa:E722
logger.warning(
'Unexpected exception rised when tried to build bitmsghash lib',
exc_info=True)
notifyBuild(True) notifyBuild(True)
@ -296,14 +312,14 @@ def run(target, initialHash):
return _doGPUPoW(target, initialHash) return _doGPUPoW(target, initialHash)
except StopIteration: except StopIteration:
raise raise
except: # nosec:B110 noqa:E722 pylint:disable=bare-except except: # nosec B110 # noqa:E722 # pylint:disable=bare-except
pass # fallback pass # fallback
if bmpow: if bmpow:
try: try:
return _doCPoW(target, initialHash) return _doCPoW(target, initialHash)
except StopIteration: except StopIteration:
raise raise
except: # nosec:B110 noqa:E722 pylint:disable=bare-except except: # nosec B110 # noqa:E722 # pylint:disable=bare-except
pass # fallback pass # fallback
if paths.frozen == "macosx_app" or not paths.frozen: if paths.frozen == "macosx_app" or not paths.frozen:
# on my (Peter Surda) Windows 10, Windows Defender # on my (Peter Surda) Windows 10, Windows Defender
@ -315,13 +331,13 @@ def run(target, initialHash):
except StopIteration: except StopIteration:
logger.error("Fast PoW got StopIteration") logger.error("Fast PoW got StopIteration")
raise raise
except: # noqa:E722 pylint:disable=bare-except except: # noqa:E722 # pylint:disable=bare-except
logger.error("Fast PoW got exception:", exc_info=True) logger.error("Fast PoW got exception:", exc_info=True)
try: try:
return _doSafePoW(target, initialHash) return _doSafePoW(target, initialHash)
except StopIteration: except StopIteration:
raise raise
except: # nosec:B110 noqa:E722 pylint:disable=bare-except except: # nosec B110 # noqa:E722 # pylint:disable=bare-except
pass # fallback pass # fallback

View File

@ -20,7 +20,6 @@ from addresses import (
encodeVarint, decodeVarint, decodeAddress, varintDecodeError) encodeVarint, decodeVarint, decodeAddress, varintDecodeError)
from bmconfigparser import config from bmconfigparser import config
from debug import logger from debug import logger
from fallback import RIPEMD160Hash
from helper_sql import sqlExecute from helper_sql import sqlExecute
from network.node import Peer from network.node import Peer
from version import softwareVersion from version import softwareVersion
@ -290,12 +289,11 @@ def isProofOfWorkSufficient(
if payloadLengthExtraBytes < defaults.networkDefaultPayloadLengthExtraBytes: if payloadLengthExtraBytes < defaults.networkDefaultPayloadLengthExtraBytes:
payloadLengthExtraBytes = defaults.networkDefaultPayloadLengthExtraBytes payloadLengthExtraBytes = defaults.networkDefaultPayloadLengthExtraBytes
endOfLifeTime, = unpack('>Q', data[8:16]) endOfLifeTime, = unpack('>Q', data[8:16])
TTL = endOfLifeTime - (int(recvTime) if recvTime else int(time.time())) TTL = endOfLifeTime - int(recvTime if recvTime else time.time())
if TTL < 300: if TTL < 300:
TTL = 300 TTL = 300
POW, = unpack('>Q', hashlib.sha512(hashlib.sha512( POW, = unpack('>Q', highlevelcrypto.double_sha512(
data[:8] + hashlib.sha512(data[8:]).digest() data[:8] + hashlib.sha512(data[8:]).digest())[0:8])
).digest()).digest()[0:8])
return POW <= 2 ** 64 / ( return POW <= 2 ** 64 / (
nonceTrialsPerByte * ( nonceTrialsPerByte * (
len(data) + payloadLengthExtraBytes len(data) + payloadLengthExtraBytes
@ -338,8 +336,8 @@ def assembleAddrMessage(peerList):
return retval return retval
def assembleVersionMessage( def assembleVersionMessage( # pylint: disable=too-many-arguments
remoteHost, remotePort, participatingStreams, server=False, nodeid=None remoteHost, remotePort, participatingStreams, dandelion_enabled=True, server=False, nodeid=None,
): ):
""" """
Construct the payload of a version message, Construct the payload of a version message,
@ -352,7 +350,7 @@ def assembleVersionMessage(
'>q', '>q',
NODE_NETWORK NODE_NETWORK
| (NODE_SSL if haveSSL(server) else 0) | (NODE_SSL if haveSSL(server) else 0)
| (NODE_DANDELION if state.dandelion else 0) | (NODE_DANDELION if dandelion_enabled else 0)
) )
payload += pack('>q', int(time.time())) payload += pack('>q', int(time.time()))
@ -376,7 +374,7 @@ def assembleVersionMessage(
'>q', '>q',
NODE_NETWORK NODE_NETWORK
| (NODE_SSL if haveSSL(server) else 0) | (NODE_SSL if haveSSL(server) else 0)
| (NODE_DANDELION if state.dandelion else 0) | (NODE_DANDELION if dandelion_enabled else 0)
) )
# = 127.0.0.1. This will be ignored by the remote host. # = 127.0.0.1. This will be ignored by the remote host.
# The actual remote connected IP will be used. # The actual remote connected IP will be used.
@ -436,6 +434,17 @@ def assembleErrorMessage(fatal=0, banTime=0, inventoryVector='', errorText=''):
# Packet decoding # Packet decoding
def decodeObjectParameters(data):
"""Decode the parameters of a raw object needed to put it in inventory"""
# BMProto.decode_payload_content("QQIvv")
expiresTime = unpack('>Q', data[8:16])[0]
objectType = unpack('>I', data[16:20])[0]
parserPos = 20 + decodeVarint(data[20:30])[1]
toStreamNumber = decodeVarint(data[parserPos:parserPos + 10])[0]
return objectType, toStreamNumber, expiresTime
def decryptAndCheckPubkeyPayload(data, address): def decryptAndCheckPubkeyPayload(data, address):
""" """
Version 4 pubkeys are encrypted. This function is run when we Version 4 pubkeys are encrypted. This function is run when we
@ -502,9 +511,9 @@ def decryptAndCheckPubkeyPayload(data, address):
readPosition = 0 readPosition = 0
# bitfieldBehaviors = decryptedData[readPosition:readPosition + 4] # bitfieldBehaviors = decryptedData[readPosition:readPosition + 4]
readPosition += 4 readPosition += 4
publicSigningKey = '\x04' + decryptedData[readPosition:readPosition + 64] pubSigningKey = '\x04' + decryptedData[readPosition:readPosition + 64]
readPosition += 64 readPosition += 64
publicEncryptionKey = '\x04' + decryptedData[readPosition:readPosition + 64] pubEncryptionKey = '\x04' + decryptedData[readPosition:readPosition + 64]
readPosition += 64 readPosition += 64
specifiedNonceTrialsPerByteLength = decodeVarint( specifiedNonceTrialsPerByteLength = decodeVarint(
decryptedData[readPosition:readPosition + 10])[1] decryptedData[readPosition:readPosition + 10])[1]
@ -520,7 +529,7 @@ def decryptAndCheckPubkeyPayload(data, address):
signature = decryptedData[readPosition:readPosition + signatureLength] signature = decryptedData[readPosition:readPosition + signatureLength]
if not highlevelcrypto.verify( if not highlevelcrypto.verify(
signedData, signature, hexlify(publicSigningKey)): signedData, signature, hexlify(pubSigningKey)):
logger.info( logger.info(
'ECDSA verify failed (within decryptAndCheckPubkeyPayload)') 'ECDSA verify failed (within decryptAndCheckPubkeyPayload)')
return 'failed' return 'failed'
@ -528,9 +537,7 @@ def decryptAndCheckPubkeyPayload(data, address):
logger.info( logger.info(
'ECDSA verify passed (within decryptAndCheckPubkeyPayload)') 'ECDSA verify passed (within decryptAndCheckPubkeyPayload)')
sha = hashlib.new('sha512') embeddedRipe = highlevelcrypto.to_ripe(pubSigningKey, pubEncryptionKey)
sha.update(publicSigningKey + publicEncryptionKey)
embeddedRipe = RIPEMD160Hash(sha.digest()).digest()
if embeddedRipe != ripe: if embeddedRipe != ripe:
# Although this pubkey object had the tag were were looking for # Although this pubkey object had the tag were were looking for
@ -548,7 +555,7 @@ def decryptAndCheckPubkeyPayload(data, address):
'addressVersion: %s, streamNumber: %s\nripe %s\n' 'addressVersion: %s, streamNumber: %s\nripe %s\n'
'publicSigningKey in hex: %s\npublicEncryptionKey in hex: %s', 'publicSigningKey in hex: %s\npublicEncryptionKey in hex: %s',
addressVersion, streamNumber, hexlify(ripe), addressVersion, streamNumber, hexlify(ripe),
hexlify(publicSigningKey), hexlify(publicEncryptionKey) hexlify(pubSigningKey), hexlify(pubEncryptionKey)
) )
t = (address, addressVersion, storedData, int(time.time()), 'yes') t = (address, addressVersion, storedData, int(time.time()), 'yes')

View File

@ -805,6 +805,10 @@ def loadOpenSSL():
'libcrypto.dylib', '/usr/local/opt/openssl/lib/libcrypto.dylib']) 'libcrypto.dylib', '/usr/local/opt/openssl/lib/libcrypto.dylib'])
elif 'win32' in sys.platform or 'win64' in sys.platform: elif 'win32' in sys.platform or 'win64' in sys.platform:
libdir.append('libeay32.dll') libdir.append('libeay32.dll')
# kivy
elif 'ANDROID_ARGUMENT' in environ:
libdir.append('libcrypto1.1.so')
libdir.append('libssl1.1.so')
else: else:
libdir.append('libcrypto.so') libdir.append('libcrypto.so')
libdir.append('libssl.so') libdir.append('libssl.so')
@ -819,7 +823,7 @@ def loadOpenSSL():
try: try:
OpenSSL = _OpenSSL(library) OpenSSL = _OpenSSL(library)
return return
except Exception: # nosec:B110 except Exception: # nosec B110
pass pass
raise Exception( raise Exception(
"Couldn't find and load the OpenSSL library. You must install it.") "Couldn't find and load the OpenSSL library. You must install it.")

View File

@ -2,6 +2,43 @@
from binascii import unhexlify from binascii import unhexlify
# These keys are from addresses test script
sample_pubsigningkey = (
b'044a367f049ec16cb6b6118eb734a9962d10b8db59c890cd08f210c43ff08bdf09d'
b'16f502ca26cd0713f38988a1237f1fc8fa07b15653c996dc4013af6d15505ce')
sample_pubencryptionkey = (
b'044597d59177fc1d89555d38915f581b5ff2286b39d022ca0283d2bdd5c36be5d3c'
b'e7b9b97792327851a562752e4b79475d1f51f5a71352482b241227f45ed36a9')
sample_privsigningkey = \
b'93d0b61371a54b53df143b954035d612f8efa8a3ed1cf842c2186bfd8f876665'
sample_privencryptionkey = \
b'4b0b73a54e19b059dc274ab69df095fe699f43b17397bca26fdf40f4d7400a3a'
# [chan] bitmessage
sample_privsigningkey_wif = \
b'5K42shDERM5g7Kbi3JT5vsAWpXMqRhWZpX835M2pdSoqQQpJMYm'
sample_privencryptionkey_wif = \
b'5HwugVWm31gnxtoYcvcK7oywH2ezYTh6Y4tzRxsndAeMi6NHqpA'
sample_wif_privsigningkey = \
b'a2e8b841a531c1c558ee0680c396789c7a2ea3ac4795ae3f000caf9fe367d144'
sample_wif_privencryptionkey = \
b'114ec0e2dca24a826a0eed064b0405b0ac148abc3b1d52729697f4d7b873fdc6'
sample_factor = \
66858749573256452658262553961707680376751171096153613379801854825275240965733
# G * sample_factor
sample_point = (
33567437183004486938355437500683826356288335339807546987348409590129959362313,
94730058721143827257669456336351159718085716196507891067256111928318063085006
)
sample_deterministic_addr3 = b'2DBPTgeSawWYZceFD69AbDT5q4iUWtj1ZN'
sample_deterministic_addr4 = b'2cWzSnwjJ7yRP3nLEWUV5LisTZyREWSzUK'
sample_daddr3_512 = 18875720106589866286514488037355423395410802084648916523381
sample_daddr4_512 = 25152821841976547050350277460563089811513157529113201589004
# pubkey K # pubkey K
sample_pubkey = unhexlify( sample_pubkey = unhexlify(
'0409d4e5c0ab3d25fe' '0409d4e5c0ab3d25fe'

View File

@ -10,25 +10,13 @@ try:
except ImportError: except ImportError:
from pybitmessage.pyelliptic import arithmetic from pybitmessage.pyelliptic import arithmetic
from .samples import (
# These keys are from addresses test script sample_deterministic_addr3, sample_deterministic_addr4,
sample_pubsigningkey = ( sample_daddr3_512, sample_daddr4_512,
b'044a367f049ec16cb6b6118eb734a9962d10b8db59c890cd08f210c43ff08bdf09d' sample_factor, sample_point, sample_pubsigningkey, sample_pubencryptionkey,
b'16f502ca26cd0713f38988a1237f1fc8fa07b15653c996dc4013af6d15505ce') sample_privsigningkey, sample_privencryptionkey,
sample_pubencryptionkey = ( sample_privsigningkey_wif, sample_privencryptionkey_wif,
b'044597d59177fc1d89555d38915f581b5ff2286b39d022ca0283d2bdd5c36be5d3c' sample_wif_privsigningkey, sample_wif_privencryptionkey
b'e7b9b97792327851a562752e4b79475d1f51f5a71352482b241227f45ed36a9')
sample_privsigningkey = \
b'93d0b61371a54b53df143b954035d612f8efa8a3ed1cf842c2186bfd8f876665'
sample_privencryptionkey = \
b'4b0b73a54e19b059dc274ab69df095fe699f43b17397bca26fdf40f4d7400a3a'
sample_factor = \
66858749573256452658262553961707680376751171096153613379801854825275240965733
# G * sample_factor
sample_point = (
33567437183004486938355437500683826356288335339807546987348409590129959362313,
94730058721143827257669456336351159718085716196507891067256111928318063085006
) )
@ -40,6 +28,34 @@ class TestArithmetic(unittest.TestCase):
sample_point, sample_point,
arithmetic.base10_multiply(arithmetic.G, sample_factor)) arithmetic.base10_multiply(arithmetic.G, sample_factor))
def test_base58(self):
"""Test encoding/decoding base58 using arithmetic functions"""
self.assertEqual(
arithmetic.decode(arithmetic.changebase(
sample_deterministic_addr4, 58, 256), 256), sample_daddr4_512)
self.assertEqual(
arithmetic.decode(arithmetic.changebase(
sample_deterministic_addr3, 58, 256), 256), sample_daddr3_512)
self.assertEqual(
arithmetic.changebase(
arithmetic.encode(sample_daddr4_512, 256), 256, 58),
sample_deterministic_addr4)
self.assertEqual(
arithmetic.changebase(
arithmetic.encode(sample_daddr3_512, 256), 256, 58),
sample_deterministic_addr3)
def test_wif(self):
"""Decode WIFs of [chan] bitmessage and check the keys"""
self.assertEqual(
sample_wif_privsigningkey,
arithmetic.changebase(arithmetic.changebase(
sample_privsigningkey_wif, 58, 256)[1:-4], 256, 16))
self.assertEqual(
sample_wif_privencryptionkey,
arithmetic.changebase(arithmetic.changebase(
sample_privencryptionkey_wif, 58, 256)[1:-4], 256, 16))
def test_decode(self): def test_decode(self):
"""Decode sample privsigningkey from hex to int and compare to factor""" """Decode sample privsigningkey from hex to int and compare to factor"""
self.assertEqual( self.assertEqual(

View File

@ -5,11 +5,6 @@ import time
from six.moves import queue from six.moves import queue
try:
from multiqueue import MultiQueue
except ImportError:
from .multiqueue import MultiQueue
class ObjectProcessorQueue(queue.Queue): class ObjectProcessorQueue(queue.Queue):
"""Special queue class using lock for `.threads.objectProcessor`""" """Special queue class using lock for `.threads.objectProcessor`"""
@ -44,10 +39,6 @@ addressGeneratorQueue = queue.Queue()
#: `.network.ReceiveQueueThread` instances dump objects they hear #: `.network.ReceiveQueueThread` instances dump objects they hear
#: on the network into this queue to be processed. #: on the network into this queue to be processed.
objectProcessorQueue = ObjectProcessorQueue() objectProcessorQueue = ObjectProcessorQueue()
invQueue = MultiQueue()
addrQueue = MultiQueue()
portCheckerQueue = queue.Queue()
receiveDataQueue = queue.Queue()
#: The address generator thread uses this queue to get information back #: The address generator thread uses this queue to get information back
#: to the API thread. #: to the API thread.
apiAddressGeneratorReturnQueue = queue.Queue() apiAddressGeneratorReturnQueue = queue.Queue()

View File

@ -11,7 +11,7 @@ from __future__ import division
import hashlib import hashlib
import os import os
import stat import stat
import subprocess import subprocess # nosec B404
import sys import sys
from binascii import hexlify from binascii import hexlify
@ -23,8 +23,6 @@ from bmconfigparser import config
from debug import logger from debug import logger
from helper_sql import sqlQuery from helper_sql import sqlQuery
from pyelliptic import arithmetic
myECCryptorObjects = {} myECCryptorObjects = {}
MyECSubscriptionCryptorObjects = {} MyECSubscriptionCryptorObjects = {}
@ -76,35 +74,6 @@ def isAddressInMyAddressBookSubscriptionsListOrWhitelist(address):
return False return False
def decodeWalletImportFormat(WIFstring):
# pylint: disable=inconsistent-return-statements
"""
Convert private key from base58 that's used in the config file to
8-bit binary string
"""
fullString = arithmetic.changebase(WIFstring, 58, 256)
privkey = fullString[:-4]
if fullString[-4:] != \
hashlib.sha256(hashlib.sha256(privkey).digest()).digest()[:4]:
logger.critical(
'Major problem! When trying to decode one of your'
' private keys, the checksum failed. Here are the first'
' 6 characters of the PRIVATE key: %s',
str(WIFstring)[:6]
)
os._exit(0) # pylint: disable=protected-access
# return ""
elif privkey[0] == '\x80': # checksum passed
return privkey[1:]
logger.critical(
'Major problem! When trying to decode one of your private keys,'
' the checksum passed but the key doesn\'t begin with hex 80.'
' Here is the PRIVATE key: %s', WIFstring
)
os._exit(0) # pylint: disable=protected-access
def reloadMyAddressHashes(): def reloadMyAddressHashes():
"""Reload keys for user's addresses from the config file""" """Reload keys for user's addresses from the config file"""
logger.debug('reloading keys from keys.dat file') logger.debug('reloading keys from keys.dat file')
@ -117,30 +86,39 @@ def reloadMyAddressHashes():
state.appdata, 'keys.dat')) state.appdata, 'keys.dat'))
hasEnabledKeys = False hasEnabledKeys = False
for addressInKeysFile in config.addresses(): for addressInKeysFile in config.addresses():
isEnabled = config.getboolean(addressInKeysFile, 'enabled') if not config.getboolean(addressInKeysFile, 'enabled'):
if isEnabled: continue
hasEnabledKeys = True
# status hasEnabledKeys = True
addressVersionNumber, streamNumber, hashobj = decodeAddress(addressInKeysFile)[1:]
if addressVersionNumber in (2, 3, 4): addressVersionNumber, streamNumber, hashobj = decodeAddress(
# Returns a simple 32 bytes of information encoded addressInKeysFile)[1:]
# in 64 Hex characters, or null if there was an error. if addressVersionNumber not in (2, 3, 4):
privEncryptionKey = hexlify(decodeWalletImportFormat( logger.error(
config.get(addressInKeysFile, 'privencryptionkey'))) 'Error in reloadMyAddressHashes: Can\'t handle'
# It is 32 bytes encoded as 64 hex characters ' address versions other than 2, 3, or 4.')
if len(privEncryptionKey) == 64: continue
myECCryptorObjects[hashobj] = \
highlevelcrypto.makeCryptor(privEncryptionKey) # Returns a simple 32 bytes of information encoded in 64 Hex characters
myAddressesByHash[hashobj] = addressInKeysFile try:
tag = hashlib.sha512(hashlib.sha512( privEncryptionKey = hexlify(
encodeVarint(addressVersionNumber) highlevelcrypto.decodeWalletImportFormat(config.get(
+ encodeVarint(streamNumber) + hashobj).digest()).digest()[32:] addressInKeysFile, 'privencryptionkey').encode()
myAddressesByTag[tag] = addressInKeysFile ))
else: except ValueError:
logger.error( logger.error(
'Error in reloadMyAddressHashes: Can\'t handle' 'Error in reloadMyAddressHashes: failed to decode'
' address versions other than 2, 3, or 4.' ' one of the private keys for address %s', addressInKeysFile)
) continue
# It is 32 bytes encoded as 64 hex characters
if len(privEncryptionKey) == 64:
myECCryptorObjects[hashobj] = \
highlevelcrypto.makeCryptor(privEncryptionKey)
myAddressesByHash[hashobj] = addressInKeysFile
tag = highlevelcrypto.double_sha512(
encodeVarint(addressVersionNumber)
+ encodeVarint(streamNumber) + hashobj)[32:]
myAddressesByTag[tag] = addressInKeysFile
if not keyfileSecure: if not keyfileSecure:
fixSensitiveFilePermissions(os.path.join( fixSensitiveFilePermissions(os.path.join(
@ -174,10 +152,10 @@ def reloadBroadcastSendersForWhichImWatching():
MyECSubscriptionCryptorObjects[hashobj] = \ MyECSubscriptionCryptorObjects[hashobj] = \
highlevelcrypto.makeCryptor(hexlify(privEncryptionKey)) highlevelcrypto.makeCryptor(hexlify(privEncryptionKey))
else: else:
doubleHashOfAddressData = hashlib.sha512(hashlib.sha512( doubleHashOfAddressData = highlevelcrypto.double_sha512(
encodeVarint(addressVersionNumber) encodeVarint(addressVersionNumber)
+ encodeVarint(streamNumber) + hashobj + encodeVarint(streamNumber) + hashobj
).digest()).digest() )
tag = doubleHashOfAddressData[32:] tag = doubleHashOfAddressData[32:]
privEncryptionKey = doubleHashOfAddressData[:32] privEncryptionKey = doubleHashOfAddressData[:32]
MyECSubscriptionCryptorObjects[tag] = \ MyECSubscriptionCryptorObjects[tag] = \
@ -212,10 +190,9 @@ def checkSensitiveFilePermissions(filename):
# Skip known problems for non-Win32 filesystems # Skip known problems for non-Win32 filesystems
# without POSIX permissions. # without POSIX permissions.
fstype = subprocess.check_output( fstype = subprocess.check_output(
'stat -f -c "%%T" %s' % (filename), ['/usr/bin/stat', '-f', '-c', '%T', filename],
shell=True,
stderr=subprocess.STDOUT stderr=subprocess.STDOUT
) ) # nosec B603
if 'fuseblk' in fstype: if 'fuseblk' in fstype:
logger.info( logger.info(
'Skipping file permissions check for %s.' 'Skipping file permissions check for %s.'

Some files were not shown because too many files have changed in this diff Show More