Compare commits
98 Commits
Author | SHA1 | Date | |
---|---|---|---|
1ee3883898 | |||
79ba94bc7f | |||
3eec0da204 | |||
41ce3ef1ba | |||
db69b9885f | |||
4ad06d7c59 | |||
85c3720fcc | |||
dc57f71353 | |||
efaa63c14c | |||
e6ece59e0d | |||
cf3716950f | |||
759c6228c3 | |||
d6823a6fb1 | |||
|
5b537eabe1 | ||
|
49e89ecdf2 | ||
|
5cd4ecb437 | ||
|
6578b5b925 | ||
27e566140d | |||
65d2a37cb2 | |||
b68beaab20 | |||
2b3034a42f | |||
|
e578759a3f | ||
205e25337f | |||
a69e4eebf8 | |||
8280e058c7 | |||
638b5a9b1a | |||
de445d6bd9 | |||
de11bc484c | |||
c3c41902cf | |||
d46af0835a | |||
0ccbf6832f | |||
ec1b05ee90 | |||
21bef1058d | |||
|
a71b44e95c | ||
|
a209d65a26 | ||
3a04e351cc | |||
41fd17b637 | |||
aaaac0f034 | |||
|
e571ba8a51 | ||
|
657c1de16b | ||
3b36676793 | |||
de9ee28516 | |||
8ef9c4d839 | |||
|
28355d70c7 | ||
8fa9da4cb4 | |||
f28935f8fe | |||
06ed879fca | |||
|
95af3a859b | ||
|
1c8ae8fef3 | ||
|
5faef8d40e | ||
|
d555a79200 | ||
0ed566500f | |||
c51b2875df | |||
5c1dcc5645 | |||
d547a8be2f | |||
acab92c561 | |||
ae3ff8c07e | |||
629efb253e | |||
f6bd1546a7 | |||
7836538290 | |||
44a4a370a6 | |||
e1ae33389c | |||
7348568c78 | |||
3ed84a5863 | |||
1b9773f2cf | |||
c7a3bfacfa | |||
feaee60632 | |||
c1ca7044d2 | |||
17a09a665b | |||
5a48ee0ad5 | |||
799237c7ff | |||
fd3567b3fa | |||
13d090e344 | |||
2a93b04332 | |||
8fb6410097 | |||
2030e08db3 | |||
1794384f01 | |||
9747f65884 | |||
|
a39fc2cff5 | ||
73ffd26094 | |||
|
b8749c6d7c | ||
|
563e557b1b | ||
|
2e98a1eb2d | ||
|
6d8c7f3bbc | ||
7dc91af6d8 | |||
|
d8843791ab | ||
95659b6f6c | |||
698932f487 | |||
b211c795ad | |||
|
6a33fe58e1 | ||
498b21864e | |||
19ec364b72 | |||
|
d33959defc | ||
13e6116d8e | |||
|
c734ac7b5f | ||
b8e099539e | |||
|
5af8b8d3f7 | ||
|
dd64a7b507 |
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
tox -e lint-basic # || exit 1
|
tox -e lint-basic || exit 1
|
||||||
tox
|
tox
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
|
@ -1 +0,0 @@
|
||||||
../tox-bionic/test.sh
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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"
|
||||||
},
|
},
|
||||||
|
|
|
@ -5,3 +5,4 @@ __pycache__
|
||||||
.buildozer
|
.buildozer
|
||||||
.tox
|
.tox
|
||||||
mprofile_*
|
mprofile_*
|
||||||
|
**.so
|
||||||
|
|
23
.travis.yml
23
.travis.yml
|
@ -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
|
|
35
INSTALL.md
35
INSTALL.md
|
@ -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
11
docker-test.sh
Executable 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
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
#
|
#
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
|
4
setup.py
4
setup.py
|
@ -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
|
||||||
|
|
|
@ -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, ''
|
||||||
|
|
||||||
|
|
153
src/api.py
153
src/api.py
|
@ -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,
|
||||||
|
|
|
@ -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()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<Allmails>:
|
<AllMails>:
|
||||||
name: 'allmails'
|
name: 'allmails'
|
||||||
BoxLayout:
|
BoxLayout:
|
||||||
orientation: 'vertical'
|
orientation: 'vertical'
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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')
|
||||||
|
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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'):
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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'
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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')
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
13
src/main-android-live.py
Normal 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()
|
36
src/main.py
36
src/main.py
|
@ -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()
|
||||||
|
|
|
@ -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
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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)
|
|
||||||
|
|
|
@ -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')
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
|
@ -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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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')
|
||||||
|
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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."""
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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')
|
||||||
|
|
|
@ -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.")
|
||||||
|
|
|
@ -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'
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
Reference in New Issue
Block a user