mirror of
https://github.com/LBRYFoundation/LBRY-Vault.git
synced 2025-08-23 17:47:31 +00:00
Merge pull request #5947 from SomberNight/202002_ecdsa
make libsecp256k1 a mandatory dependency
This commit is contained in:
commit
28dc1928a0
30 changed files with 466 additions and 721 deletions
|
@ -12,6 +12,9 @@ graft electrum
|
||||||
prune electrum/tests
|
prune electrum/tests
|
||||||
graft contrib/udev
|
graft contrib/udev
|
||||||
|
|
||||||
|
exclude electrum/*.so
|
||||||
|
exclude electrum/*.so.0
|
||||||
|
|
||||||
global-exclude __pycache__
|
global-exclude __pycache__
|
||||||
global-exclude *.py[co~]
|
global-exclude *.py[co~]
|
||||||
global-exclude *.py.orig
|
global-exclude *.py.orig
|
||||||
|
|
34
README.rst
34
README.rst
|
@ -26,11 +26,28 @@ Electrum - Lightweight Bitcoin client
|
||||||
Getting started
|
Getting started
|
||||||
===============
|
===============
|
||||||
|
|
||||||
Electrum is a pure python application. If you want to use the
|
Electrum itself is pure Python, and so are most of the required dependencies.
|
||||||
Qt interface, install the Qt dependencies::
|
|
||||||
|
Non-python dependencies
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
If you want to use the Qt interface, install the Qt dependencies::
|
||||||
|
|
||||||
sudo apt-get install python3-pyqt5
|
sudo apt-get install python3-pyqt5
|
||||||
|
|
||||||
|
For elliptic curve operations, libsecp256k1 is a required dependency::
|
||||||
|
|
||||||
|
sudo apt-get install libsecp256k1-0
|
||||||
|
|
||||||
|
Alternatively, when running from a cloned repository, a script is provided to build
|
||||||
|
libsecp256k1 yourself::
|
||||||
|
|
||||||
|
./contrib/make_libsecp256k1.sh
|
||||||
|
|
||||||
|
|
||||||
|
Running from tar.gz
|
||||||
|
-------------------
|
||||||
|
|
||||||
If you downloaded the official package (tar.gz), you can run
|
If you downloaded the official package (tar.gz), you can run
|
||||||
Electrum from its root directory without installing it on your
|
Electrum from its root directory without installing it on your
|
||||||
system; all the python dependencies are included in the 'packages'
|
system; all the python dependencies are included in the 'packages'
|
||||||
|
@ -40,22 +57,19 @@ directory. To run Electrum from its root directory, just do::
|
||||||
|
|
||||||
You can also install Electrum on your system, by running this command::
|
You can also install Electrum on your system, by running this command::
|
||||||
|
|
||||||
sudo apt-get install python3-setuptools
|
sudo apt-get install python3-setuptools python3-pip
|
||||||
python3 -m pip install .[fast]
|
python3 -m pip install --user .
|
||||||
|
|
||||||
This will download and install the Python dependencies used by
|
This will download and install the Python dependencies used by
|
||||||
Electrum instead of using the 'packages' directory.
|
Electrum instead of using the 'packages' directory.
|
||||||
The 'fast' extra contains some optional dependencies that we think
|
|
||||||
are often useful but they are not strictly needed.
|
|
||||||
|
|
||||||
If you cloned the git repository, you need to compile extra files
|
If you cloned the git repository, you need to compile extra files
|
||||||
before you can run Electrum. Read the next section, "Development
|
before you can run Electrum. Read the next section, "Development
|
||||||
Version".
|
version".
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Development version
|
Development version
|
||||||
===================
|
-------------------
|
||||||
|
|
||||||
Check out the code from GitHub::
|
Check out the code from GitHub::
|
||||||
|
|
||||||
|
@ -65,7 +79,7 @@ Check out the code from GitHub::
|
||||||
|
|
||||||
Run install (this should install dependencies)::
|
Run install (this should install dependencies)::
|
||||||
|
|
||||||
python3 -m pip install .[fast]
|
python3 -m pip install --user .
|
||||||
|
|
||||||
|
|
||||||
Compile the protobuf description file::
|
Compile the protobuf description file::
|
||||||
|
|
|
@ -10,10 +10,11 @@ BUILDDIR="$CONTRIB_APPIMAGE/build/appimage"
|
||||||
APPDIR="$BUILDDIR/electrum.AppDir"
|
APPDIR="$BUILDDIR/electrum.AppDir"
|
||||||
CACHEDIR="$CONTRIB_APPIMAGE/.cache/appimage"
|
CACHEDIR="$CONTRIB_APPIMAGE/.cache/appimage"
|
||||||
|
|
||||||
|
export GCC_STRIP_BINARIES="1"
|
||||||
|
|
||||||
# pinned versions
|
# pinned versions
|
||||||
PYTHON_VERSION=3.7.6
|
PYTHON_VERSION=3.7.6
|
||||||
PKG2APPIMAGE_COMMIT="eb8f3acdd9f11ab19b78f5cb15daa772367daf15"
|
PKG2APPIMAGE_COMMIT="eb8f3acdd9f11ab19b78f5cb15daa772367daf15"
|
||||||
LIBSECP_VERSION="b408c6a8b287003d1ade5709e6f7bc3c7f1d5be7"
|
|
||||||
SQUASHFSKIT_COMMIT="ae0d656efa2d0df2fcac795b6823b44462f19386"
|
SQUASHFSKIT_COMMIT="ae0d656efa2d0df2fcac795b6823b44462f19386"
|
||||||
|
|
||||||
|
|
||||||
|
@ -45,7 +46,6 @@ info "building python."
|
||||||
tar xf "$CACHEDIR/Python-$PYTHON_VERSION.tar.xz" -C "$BUILDDIR"
|
tar xf "$CACHEDIR/Python-$PYTHON_VERSION.tar.xz" -C "$BUILDDIR"
|
||||||
(
|
(
|
||||||
cd "$BUILDDIR/Python-$PYTHON_VERSION"
|
cd "$BUILDDIR/Python-$PYTHON_VERSION"
|
||||||
export SOURCE_DATE_EPOCH=1530212462
|
|
||||||
LC_ALL=C export BUILD_DATE=$(date -u -d "@$SOURCE_DATE_EPOCH" "+%b %d %Y")
|
LC_ALL=C export BUILD_DATE=$(date -u -d "@$SOURCE_DATE_EPOCH" "+%b %d %Y")
|
||||||
LC_ALL=C export BUILD_TIME=$(date -u -d "@$SOURCE_DATE_EPOCH" "+%H:%M:%S")
|
LC_ALL=C export BUILD_TIME=$(date -u -d "@$SOURCE_DATE_EPOCH" "+%H:%M:%S")
|
||||||
# Patch taken from Ubuntu http://archive.ubuntu.com/ubuntu/pool/main/p/python3.7/python3.7_3.7.6-1.debian.tar.xz
|
# Patch taken from Ubuntu http://archive.ubuntu.com/ubuntu/pool/main/p/python3.7/python3.7_3.7.6-1.debian.tar.xz
|
||||||
|
@ -77,26 +77,8 @@ git clone "https://github.com/squashfskit/squashfskit.git" "$BUILDDIR/squashfski
|
||||||
MKSQUASHFS="$BUILDDIR/squashfskit/squashfs-tools/mksquashfs"
|
MKSQUASHFS="$BUILDDIR/squashfskit/squashfs-tools/mksquashfs"
|
||||||
|
|
||||||
|
|
||||||
info "building libsecp256k1."
|
"$CONTRIB"/make_libsecp256k1.sh || fail "Could not build libsecp"
|
||||||
(
|
cp -f "$PROJECT_ROOT/electrum/libsecp256k1.so.0" "$APPDIR/usr/lib/libsecp256k1.so.0" || fail "Could not copy libsecp to its destination"
|
||||||
git clone https://github.com/bitcoin-core/secp256k1 "$CACHEDIR"/secp256k1 \
|
|
||||||
|| (cd "$CACHEDIR"/secp256k1 && git reset --hard && git pull)
|
|
||||||
cd "$CACHEDIR"/secp256k1
|
|
||||||
git reset --hard "$LIBSECP_VERSION"
|
|
||||||
git clean -f -x -q
|
|
||||||
export SOURCE_DATE_EPOCH=1530212462
|
|
||||||
echo "LDFLAGS = -no-undefined" >> Makefile.am
|
|
||||||
./autogen.sh
|
|
||||||
./configure \
|
|
||||||
--prefix="$APPDIR/usr" \
|
|
||||||
--enable-module-recovery \
|
|
||||||
--enable-experimental \
|
|
||||||
--enable-module-ecdh \
|
|
||||||
--disable-jni \
|
|
||||||
-q
|
|
||||||
make -j4 -s || fail "Could not build libsecp"
|
|
||||||
make -s install > /dev/null || fail "Could not install libsecp"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
appdir_python() {
|
appdir_python() {
|
||||||
|
@ -224,7 +206,6 @@ rm -rf "$PYDIR"/site-packages/PyQt5/Qt.so
|
||||||
|
|
||||||
# these are deleted as they were not deterministic; and are not needed anyway
|
# these are deleted as they were not deterministic; and are not needed anyway
|
||||||
find "$APPDIR" -path '*/__pycache__*' -delete
|
find "$APPDIR" -path '*/__pycache__*' -delete
|
||||||
rm "$APPDIR"/usr/lib/libsecp256k1.a
|
|
||||||
# note that jsonschema-*.dist-info is needed by that package as it uses 'pkg_resources.get_distribution'
|
# note that jsonschema-*.dist-info is needed by that package as it uses 'pkg_resources.get_distribution'
|
||||||
# also, see https://gitlab.com/python-devs/importlib_metadata/issues/71
|
# also, see https://gitlab.com/python-devs/importlib_metadata/issues/71
|
||||||
for f in "$PYDIR"/site-packages/jsonschema-*.dist-info; do mv "$f" "$(echo "$f" | sed s/\.dist-info/\.dist-info2/)"; done
|
for f in "$PYDIR"/site-packages/jsonschema-*.dist-info; do mv "$f" "$(echo "$f" | sed s/\.dist-info/\.dist-info2/)"; done
|
||||||
|
|
|
@ -6,7 +6,6 @@ NAME_ROOT=electrum
|
||||||
export WINEPREFIX=/opt/wine64
|
export WINEPREFIX=/opt/wine64
|
||||||
export WINEDEBUG=-all
|
export WINEDEBUG=-all
|
||||||
export PYTHONDONTWRITEBYTECODE=1
|
export PYTHONDONTWRITEBYTECODE=1
|
||||||
export PYTHONHASHSEED=22
|
|
||||||
|
|
||||||
PYHOME=c:/python3
|
PYHOME=c:/python3
|
||||||
PYTHON="wine $PYHOME/python.exe -OO -B"
|
PYTHON="wine $PYHOME/python.exe -OO -B"
|
||||||
|
|
|
@ -1,56 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
# heavily based on https://github.com/ofek/coincurve/blob/417e726f553460f88d7edfa5dc67bfda397c4e4a/.travis/build_windows_wheels.sh
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
here="$(dirname "$(readlink -e "$0")")"
|
|
||||||
LIBSECP_VERSION="b408c6a8b287003d1ade5709e6f7bc3c7f1d5be7"
|
|
||||||
|
|
||||||
. "$CONTRIB"/build_tools_util.sh
|
|
||||||
|
|
||||||
info "building libsecp256k1..."
|
|
||||||
|
|
||||||
|
|
||||||
build_dll() {
|
|
||||||
#sudo apt-get install -y mingw-w64
|
|
||||||
export SOURCE_DATE_EPOCH=1530212462
|
|
||||||
echo "LDFLAGS = -no-undefined" >> Makefile.am
|
|
||||||
./autogen.sh
|
|
||||||
# Note: set both --build and --host when running configure
|
|
||||||
# Otherwise weird voodoo magic happens with Docker and Wine.
|
|
||||||
# https://www.gnu.org/software/autoconf/manual/autoconf-2.69/html_node/Hosts-and-Cross_002dCompilation.html
|
|
||||||
LDFLAGS="-Wl,--no-insert-timestamp" ./configure \
|
|
||||||
--host=$1 \
|
|
||||||
--build=x86_64-pc-linux-gnu \
|
|
||||||
--enable-module-recovery \
|
|
||||||
--enable-experimental \
|
|
||||||
--enable-module-ecdh \
|
|
||||||
--disable-jni
|
|
||||||
make -j4
|
|
||||||
${1}-strip .libs/libsecp256k1-0.dll
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
cd "$CACHEDIR"
|
|
||||||
|
|
||||||
if [ -f "secp256k1/libsecp256k1.dll" ]; then
|
|
||||||
info "libsecp256k1.dll already built, skipping"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
if [ ! -d secp256k1 ]; then
|
|
||||||
git clone https://github.com/bitcoin-core/secp256k1.git
|
|
||||||
fi
|
|
||||||
|
|
||||||
cd secp256k1
|
|
||||||
git reset --hard
|
|
||||||
git clean -f -x -q
|
|
||||||
git checkout $LIBSECP_VERSION
|
|
||||||
|
|
||||||
build_dll i686-w64-mingw32 # 64-bit would be: x86_64-w64-mingw32
|
|
||||||
mv .libs/libsecp256k1-0.dll libsecp256k1.dll
|
|
||||||
|
|
||||||
find -exec touch -d '2000-11-11T11:11:11+00:00' {} +
|
|
||||||
|
|
||||||
info "building libsecp256k1 finished"
|
|
|
@ -2,16 +2,19 @@
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# Lucky number
|
|
||||||
export PYTHONHASHSEED=22
|
|
||||||
|
|
||||||
here="$(dirname "$(readlink -e "$0")")"
|
here="$(dirname "$(readlink -e "$0")")"
|
||||||
test -n "$here" -a -d "$here" || exit
|
test -n "$here" -a -d "$here" || exit
|
||||||
|
|
||||||
export CONTRIB="$here/.."
|
export CONTRIB="$here/.."
|
||||||
|
export PROJECT_ROOT="$CONTRIB/.."
|
||||||
export CACHEDIR="$here/.cache"
|
export CACHEDIR="$here/.cache"
|
||||||
export PIP_CACHE_DIR="$CACHEDIR/pip_cache"
|
export PIP_CACHE_DIR="$CACHEDIR/pip_cache"
|
||||||
|
|
||||||
|
export BUILD_TYPE="wine"
|
||||||
|
export GCC_TRIPLET_HOST="i686-w64-mingw32"
|
||||||
|
export GCC_TRIPLET_BUILD="x86_64-pc-linux-gnu"
|
||||||
|
export GCC_STRIP_BINARIES="1"
|
||||||
|
|
||||||
. "$CONTRIB"/build_tools_util.sh
|
. "$CONTRIB"/build_tools_util.sh
|
||||||
|
|
||||||
info "Clearing $here/build and $here/dist..."
|
info "Clearing $here/build and $here/dist..."
|
||||||
|
@ -20,7 +23,11 @@ rm "$here"/dist/* -rf
|
||||||
|
|
||||||
mkdir -p "$CACHEDIR" "$PIP_CACHE_DIR"
|
mkdir -p "$CACHEDIR" "$PIP_CACHE_DIR"
|
||||||
|
|
||||||
$here/build-secp256k1.sh || fail "build-secp256k1 failed"
|
if [ -f "$PROJECT_ROOT/electrum/libsecp256k1-0.dll" ]; then
|
||||||
|
info "libsecp256k1 already built, skipping"
|
||||||
|
else
|
||||||
|
"$CONTRIB"/make_libsecp256k1.sh || fail "Could not build libsecp"
|
||||||
|
fi
|
||||||
|
|
||||||
$here/prepare-wine.sh || fail "prepare-wine failed"
|
$here/prepare-wine.sh || fail "prepare-wine failed"
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ binaries = []
|
||||||
# Workaround for "Retro Look":
|
# Workaround for "Retro Look":
|
||||||
binaries += [b for b in collect_dynamic_libs('PyQt5') if 'qwindowsvista' in b[0]]
|
binaries += [b for b in collect_dynamic_libs('PyQt5') if 'qwindowsvista' in b[0]]
|
||||||
|
|
||||||
binaries += [('C:/tmp/libsecp256k1.dll', '.')]
|
binaries += [('C:/tmp/libsecp256k1-0.dll', '.')]
|
||||||
binaries += [('C:/tmp/libusb-1.0.dll', '.')]
|
binaries += [('C:/tmp/libusb-1.0.dll', '.')]
|
||||||
|
|
||||||
datas = [
|
datas = [
|
||||||
|
|
|
@ -89,7 +89,6 @@ info "Compiling libusb..."
|
||||||
git remote add origin $LIBUSB_REPO
|
git remote add origin $LIBUSB_REPO
|
||||||
git fetch --depth 1 origin $LIBUSB_COMMIT
|
git fetch --depth 1 origin $LIBUSB_COMMIT
|
||||||
git checkout -b pinned FETCH_HEAD
|
git checkout -b pinned FETCH_HEAD
|
||||||
export SOURCE_DATE_EPOCH=1530212462
|
|
||||||
echo "libusb_1_0_la_LDFLAGS += -Wc,-static" >> libusb/Makefile.am
|
echo "libusb_1_0_la_LDFLAGS += -Wc,-static" >> libusb/Makefile.am
|
||||||
./bootstrap.sh || fail "Could not bootstrap libusb"
|
./bootstrap.sh || fail "Could not bootstrap libusb"
|
||||||
host="i686-w64-mingw32"
|
host="i686-w64-mingw32"
|
||||||
|
@ -102,8 +101,8 @@ info "Compiling libusb..."
|
||||||
cp "$CACHEDIR/libusb/libusb/.libs/libusb-1.0.dll" $WINEPREFIX/drive_c/tmp/ || fail "Could not copy libusb to its destination"
|
cp "$CACHEDIR/libusb/libusb/.libs/libusb-1.0.dll" $WINEPREFIX/drive_c/tmp/ || fail "Could not copy libusb to its destination"
|
||||||
|
|
||||||
|
|
||||||
# copy libsecp dll (already built by build-secp256k1.sh)
|
# copy libsecp dll (already built)
|
||||||
cp "$CACHEDIR/secp256k1/libsecp256k1.dll" $WINEPREFIX/drive_c/tmp/ || fail "Could not copy libsecp to its destination"
|
cp "$PROJECT_ROOT/electrum/libsecp256k1-0.dll" $WINEPREFIX/drive_c/tmp/ || fail "Could not copy libsecp to its destination"
|
||||||
|
|
||||||
|
|
||||||
info "Building PyInstaller."
|
info "Building PyInstaller."
|
||||||
|
|
|
@ -70,3 +70,64 @@ function retry() {
|
||||||
|
|
||||||
return $result
|
return $result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function gcc_with_triplet()
|
||||||
|
{
|
||||||
|
TRIPLET="$1"
|
||||||
|
CMD="$2"
|
||||||
|
shift 2
|
||||||
|
if [ -n "$TRIPLET" ] ; then
|
||||||
|
"$TRIPLET-$CMD" "$@"
|
||||||
|
else
|
||||||
|
"$CMD" "$@"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function gcc_host()
|
||||||
|
{
|
||||||
|
gcc_with_triplet "$GCC_TRIPLET_HOST" "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
function gcc_build()
|
||||||
|
{
|
||||||
|
gcc_with_triplet "$GCC_TRIPLET_BUILD" "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
function host_strip()
|
||||||
|
{
|
||||||
|
if [ "$GCC_STRIP_BINARIES" -ne "0" ] ; then
|
||||||
|
case "$BUILD_TYPE" in
|
||||||
|
linux|wine)
|
||||||
|
gcc_host strip "$@"
|
||||||
|
;;
|
||||||
|
darwin)
|
||||||
|
# TODO: Strip on macOS?
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# on MacOS, there is no realpath by default
|
||||||
|
if ! [ -x "$(command -v realpath)" ]; then
|
||||||
|
function realpath() {
|
||||||
|
[[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
export SOURCE_DATE_EPOCH=1530212462
|
||||||
|
export PYTHONHASHSEED=22
|
||||||
|
# Set the build type, overridden by wine build
|
||||||
|
export BUILD_TYPE="${BUILD_TYPE:-$(uname | tr '[:upper:]' '[:lower:]')}"
|
||||||
|
# No additional autoconf flags by default
|
||||||
|
export AUTOCONF_FLAGS=""
|
||||||
|
# Add host / build flags if the triplets are set
|
||||||
|
if [ -n "$GCC_TRIPLET_HOST" ] ; then
|
||||||
|
export AUTOCONF_FLAGS="$AUTOCONF_FLAGS --host=$GCC_TRIPLET_HOST"
|
||||||
|
fi
|
||||||
|
if [ -n "$GCC_TRIPLET_BUILD" ] ; then
|
||||||
|
export AUTOCONF_FLAGS="$AUTOCONF_FLAGS --build=$GCC_TRIPLET_BUILD"
|
||||||
|
fi
|
||||||
|
|
||||||
|
export GCC_STRIP_BINARIES="${GCC_STRIP_BINARIES:-0}"
|
||||||
|
|
||||||
|
|
49
contrib/make_libsecp256k1.sh
Executable file
49
contrib/make_libsecp256k1.sh
Executable file
|
@ -0,0 +1,49 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
LIBSECP_VERSION="b408c6a8b287003d1ade5709e6f7bc3c7f1d5be7"
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
. $(dirname "$0")/build_tools_util.sh || (echo "Could not source build_tools_util.sh" && exit 1)
|
||||||
|
|
||||||
|
here=$(dirname $(realpath "$0" 2> /dev/null || grealpath "$0"))
|
||||||
|
CONTRIB="$here"
|
||||||
|
PROJECT_ROOT="$CONTRIB/.."
|
||||||
|
|
||||||
|
pkgname="secp256k1"
|
||||||
|
info "Building $pkgname..."
|
||||||
|
|
||||||
|
(
|
||||||
|
cd $CONTRIB
|
||||||
|
if [ ! -d secp256k1 ]; then
|
||||||
|
git clone https://github.com/bitcoin-core/secp256k1.git
|
||||||
|
fi
|
||||||
|
cd secp256k1
|
||||||
|
git reset --hard
|
||||||
|
git clean -f -x -q
|
||||||
|
git checkout $LIBSECP_VERSION
|
||||||
|
|
||||||
|
if ! [ -x configure ] ; then
|
||||||
|
echo "libsecp256k1_la_LDFLAGS = -no-undefined" >> Makefile.am
|
||||||
|
echo "LDFLAGS = -no-undefined" >> Makefile.am
|
||||||
|
./autogen.sh || fail "Could not run autogen for $pkgname. Please make sure you have automake and libtool installed, and try again."
|
||||||
|
fi
|
||||||
|
if ! [ -r config.status ] ; then
|
||||||
|
./configure \
|
||||||
|
$AUTOCONF_FLAGS \
|
||||||
|
--prefix="$here/$pkgname/dist" \
|
||||||
|
--enable-module-recovery \
|
||||||
|
--enable-experimental \
|
||||||
|
--enable-module-ecdh \
|
||||||
|
--disable-jni \
|
||||||
|
--disable-tests \
|
||||||
|
--disable-static \
|
||||||
|
--enable-shared || fail "Could not configure $pkgname. Please make sure you have a C compiler installed and try again."
|
||||||
|
fi
|
||||||
|
make -j4 || fail "Could not build $pkgname"
|
||||||
|
make install || fail "Could not install $pkgname"
|
||||||
|
. "$here/$pkgname/dist/lib/libsecp256k1.la"
|
||||||
|
host_strip "$here/$pkgname/dist/lib/$dlname"
|
||||||
|
cp -fpv "$here/$pkgname/dist/lib/$dlname" "$PROJECT_ROOT/electrum" || fail "Could not copy the $pkgname binary to its destination"
|
||||||
|
info "$dlname has been placed in the inner 'electrum' folder."
|
||||||
|
)
|
|
@ -21,7 +21,3 @@ function DoCodeSignMaybe { # ARGS: infoName fileOrDirName codesignIdentity
|
||||||
info "Code signing ${infoName}..."
|
info "Code signing ${infoName}..."
|
||||||
codesign -f -v $deep -s "$identity" "$file" || fail "Could not code sign ${infoName}"
|
codesign -f -v $deep -s "$identity" "$file" || fail "Could not code sign ${infoName}"
|
||||||
}
|
}
|
||||||
|
|
||||||
function realpath() {
|
|
||||||
[[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"
|
|
||||||
}
|
|
||||||
|
|
|
@ -7,6 +7,8 @@ PACKAGE=Electrum
|
||||||
GIT_REPO=https://github.com/spesmilo/electrum
|
GIT_REPO=https://github.com/spesmilo/electrum
|
||||||
LIBSECP_VERSION="b408c6a8b287003d1ade5709e6f7bc3c7f1d5be7"
|
LIBSECP_VERSION="b408c6a8b287003d1ade5709e6f7bc3c7f1d5be7"
|
||||||
|
|
||||||
|
export GCC_STRIP_BINARIES="1"
|
||||||
|
|
||||||
. $(dirname "$0")/base.sh
|
. $(dirname "$0")/base.sh
|
||||||
|
|
||||||
CONTRIB_OSX="$(dirname "$(realpath "$0")")"
|
CONTRIB_OSX="$(dirname "$(realpath "$0")")"
|
||||||
|
@ -16,7 +18,6 @@ ROOT_FOLDER="$CONTRIB/.."
|
||||||
src_dir=$(dirname "$0")
|
src_dir=$(dirname "$0")
|
||||||
cd $src_dir/../..
|
cd $src_dir/../..
|
||||||
|
|
||||||
export PYTHONHASHSEED=22
|
|
||||||
VERSION=`git describe --tags --dirty --always`
|
VERSION=`git describe --tags --dirty --always`
|
||||||
|
|
||||||
which brew > /dev/null 2>&1 || fail "Please install brew from https://brew.sh/ to continue"
|
which brew > /dev/null 2>&1 || fail "Please install brew from https://brew.sh/ to continue"
|
||||||
|
@ -96,17 +97,10 @@ cp $BUILDDIR/libusb/1.0.22/lib/libusb-1.0.dylib contrib/osx
|
||||||
echo "82c368dfd4da017ceb32b12ca885576f325503428a4966cc09302cbd62702493 contrib/osx/libusb-1.0.dylib" | \
|
echo "82c368dfd4da017ceb32b12ca885576f325503428a4966cc09302cbd62702493 contrib/osx/libusb-1.0.dylib" | \
|
||||||
shasum -a 256 -c || fail "libusb checksum mismatched"
|
shasum -a 256 -c || fail "libusb checksum mismatched"
|
||||||
|
|
||||||
info "Building libsecp256k1"
|
info "Preparing for building libsecp256k1"
|
||||||
brew install autoconf automake libtool
|
brew install autoconf automake libtool
|
||||||
git clone https://github.com/bitcoin-core/secp256k1 $BUILDDIR/secp256k1
|
"$CONTRIB"/make_libsecp256k1.sh || fail "Could not build libsecp"
|
||||||
pushd $BUILDDIR/secp256k1
|
cp "$ROOT_FOLDER"/electrum/libsecp256k1.0.dylib contrib/osx
|
||||||
git reset --hard $LIBSECP_VERSION
|
|
||||||
git clean -f -x -q
|
|
||||||
./autogen.sh
|
|
||||||
./configure --enable-module-recovery --enable-experimental --enable-module-ecdh --disable-jni
|
|
||||||
make -j4
|
|
||||||
popd
|
|
||||||
cp $BUILDDIR/secp256k1/.libs/libsecp256k1.0.dylib contrib/osx
|
|
||||||
|
|
||||||
info "Building CalinsQRReader..."
|
info "Building CalinsQRReader..."
|
||||||
d=contrib/osx/CalinsQRReader
|
d=contrib/osx/CalinsQRReader
|
||||||
|
|
|
@ -65,7 +65,7 @@ def _CKD_priv(parent_privkey: bytes, parent_chaincode: bytes,
|
||||||
child_privkey = (I_left + ecc.string_to_number(parent_privkey)) % ecc.CURVE_ORDER
|
child_privkey = (I_left + ecc.string_to_number(parent_privkey)) % ecc.CURVE_ORDER
|
||||||
if I_left >= ecc.CURVE_ORDER or child_privkey == 0:
|
if I_left >= ecc.CURVE_ORDER or child_privkey == 0:
|
||||||
raise ecc.InvalidECPointException()
|
raise ecc.InvalidECPointException()
|
||||||
child_privkey = ecc.number_to_string(child_privkey, ecc.CURVE_ORDER)
|
child_privkey = int.to_bytes(child_privkey, length=32, byteorder='big', signed=False)
|
||||||
child_chaincode = I[32:]
|
child_chaincode = I[32:]
|
||||||
return child_privkey, child_chaincode
|
return child_privkey, child_chaincode
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ import traceback
|
||||||
import sys
|
import sys
|
||||||
import threading
|
import threading
|
||||||
from typing import Dict, Optional, Tuple, Iterable
|
from typing import Dict, Optional, Tuple, Iterable
|
||||||
from base64 import b64decode
|
from base64 import b64decode, b64encode
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
|
@ -44,7 +44,7 @@ from aiorpcx import TaskGroup
|
||||||
from .network import Network
|
from .network import Network
|
||||||
from .util import (json_decode, to_bytes, to_string, profiler, standardize_path, constant_time_compare)
|
from .util import (json_decode, to_bytes, to_string, profiler, standardize_path, constant_time_compare)
|
||||||
from .util import PR_PAID, PR_EXPIRED, get_request_status
|
from .util import PR_PAID, PR_EXPIRED, get_request_status
|
||||||
from .util import log_exceptions, ignore_exceptions
|
from .util import log_exceptions, ignore_exceptions, randrange
|
||||||
from .wallet import Wallet, Abstract_Wallet
|
from .wallet import Wallet, Abstract_Wallet
|
||||||
from .storage import WalletStorage
|
from .storage import WalletStorage
|
||||||
from .wallet_db import WalletDB
|
from .wallet_db import WalletDB
|
||||||
|
@ -124,11 +124,10 @@ def get_rpc_credentials(config: SimpleConfig) -> Tuple[str, str]:
|
||||||
rpc_password = config.get('rpcpassword', None)
|
rpc_password = config.get('rpcpassword', None)
|
||||||
if rpc_user is None or rpc_password is None:
|
if rpc_user is None or rpc_password is None:
|
||||||
rpc_user = 'user'
|
rpc_user = 'user'
|
||||||
import ecdsa, base64
|
|
||||||
bits = 128
|
bits = 128
|
||||||
nbytes = bits // 8 + (bits % 8 > 0)
|
nbytes = bits // 8 + (bits % 8 > 0)
|
||||||
pw_int = ecdsa.util.randrange(pow(2, bits))
|
pw_int = randrange(pow(2, bits))
|
||||||
pw_b64 = base64.b64encode(
|
pw_b64 = b64encode(
|
||||||
pw_int.to_bytes(nbytes, 'big'), b'-_')
|
pw_int.to_bytes(nbytes, 'big'), b'-_')
|
||||||
rpc_password = to_string(pw_b64, 'ascii')
|
rpc_password = to_string(pw_b64, 'ascii')
|
||||||
config.set_key('rpcuser', rpc_user)
|
config.set_key('rpcuser', rpc_user)
|
||||||
|
|
|
@ -101,8 +101,8 @@ def python_validate_rrsig(rrset, rrsig, keys, origin=None, now=None):
|
||||||
keyptr = keyptr[2:]
|
keyptr = keyptr[2:]
|
||||||
rsa_e = keyptr[0:bytes]
|
rsa_e = keyptr[0:bytes]
|
||||||
rsa_n = keyptr[bytes:]
|
rsa_n = keyptr[bytes:]
|
||||||
n = ecdsa.util.string_to_number(rsa_n)
|
n = int.from_bytes(rsa_n, byteorder='big', signed=False)
|
||||||
e = ecdsa.util.string_to_number(rsa_e)
|
e = int.from_bytes(rsa_e, byteorder='big', signed=False)
|
||||||
pubkey = rsakey.RSAKey(n, e)
|
pubkey = rsakey.RSAKey(n, e)
|
||||||
sig = rrsig.signature
|
sig = rrsig.signature
|
||||||
|
|
||||||
|
@ -117,15 +117,15 @@ def python_validate_rrsig(rrset, rrsig, keys, origin=None, now=None):
|
||||||
# shouldn't happen
|
# shouldn't happen
|
||||||
raise ValidationFailure('unknown ECDSA curve')
|
raise ValidationFailure('unknown ECDSA curve')
|
||||||
keyptr = candidate_key.key
|
keyptr = candidate_key.key
|
||||||
x = ecdsa.util.string_to_number(keyptr[0:key_len])
|
x = int.from_bytes(keyptr[0:key_len], byteorder='big', signed=False)
|
||||||
y = ecdsa.util.string_to_number(keyptr[key_len:key_len * 2])
|
y = int.from_bytes(keyptr[key_len:key_len * 2], byteorder='big', signed=False)
|
||||||
assert ecdsa.ecdsa.point_is_valid(curve.generator, x, y)
|
assert ecdsa.ecdsa.point_is_valid(curve.generator, x, y)
|
||||||
point = ecdsa.ellipticcurve.Point(curve.curve, x, y, curve.order)
|
point = ecdsa.ellipticcurve.Point(curve.curve, x, y, curve.order)
|
||||||
verifying_key = ecdsa.keys.VerifyingKey.from_public_point(point, curve)
|
verifying_key = ecdsa.keys.VerifyingKey.from_public_point(point, curve)
|
||||||
r = rrsig.signature[:key_len]
|
r = rrsig.signature[:key_len]
|
||||||
s = rrsig.signature[key_len:]
|
s = rrsig.signature[key_len:]
|
||||||
sig = ecdsa.ecdsa.Signature(ecdsa.util.string_to_number(r),
|
sig = ecdsa.ecdsa.Signature(int.from_bytes(r, byteorder='big', signed=False),
|
||||||
ecdsa.util.string_to_number(s))
|
int.from_bytes(s, byteorder='big', signed=False))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise ValidationFailure('unknown algorithm %u' % rrsig.algorithm)
|
raise ValidationFailure('unknown algorithm %u' % rrsig.algorithm)
|
||||||
|
@ -156,7 +156,7 @@ def python_validate_rrsig(rrset, rrsig, keys, origin=None, now=None):
|
||||||
return
|
return
|
||||||
|
|
||||||
elif _is_ecdsa(rrsig.algorithm):
|
elif _is_ecdsa(rrsig.algorithm):
|
||||||
diglong = ecdsa.util.string_to_number(digest)
|
diglong = int.from_bytes(digest, byteorder='big', signed=False)
|
||||||
if verifying_key.pubkey.verifies(diglong, sig):
|
if verifying_key.pubkey.verifies(diglong, sig):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
416
electrum/ecc.py
416
electrum/ecc.py
|
@ -26,189 +26,148 @@
|
||||||
import base64
|
import base64
|
||||||
import hashlib
|
import hashlib
|
||||||
import functools
|
import functools
|
||||||
import copy
|
|
||||||
from typing import Union, Tuple, Optional
|
from typing import Union, Tuple, Optional
|
||||||
|
from ctypes import (
|
||||||
|
byref, c_byte, c_int, c_uint, c_char_p, c_size_t, c_void_p, create_string_buffer,
|
||||||
|
CFUNCTYPE, POINTER, cast
|
||||||
|
)
|
||||||
|
|
||||||
import ecdsa
|
from .util import bfh, bh2u, assert_bytes, to_bytes, InvalidPassword, profiler, randrange
|
||||||
from ecdsa.ecdsa import curve_secp256k1, generator_secp256k1
|
|
||||||
from ecdsa.curves import SECP256k1
|
|
||||||
from ecdsa.ellipticcurve import Point
|
|
||||||
from ecdsa.util import string_to_number, number_to_string
|
|
||||||
|
|
||||||
from .util import bfh, bh2u, assert_bytes, to_bytes, InvalidPassword, profiler
|
|
||||||
from .crypto import (sha256d, aes_encrypt_with_iv, aes_decrypt_with_iv, hmac_oneshot)
|
from .crypto import (sha256d, aes_encrypt_with_iv, aes_decrypt_with_iv, hmac_oneshot)
|
||||||
from .ecc_fast import do_monkey_patching_of_python_ecdsa_internals_with_libsecp256k1
|
|
||||||
from . import msqr
|
|
||||||
from . import constants
|
from . import constants
|
||||||
from .logging import get_logger
|
from .logging import get_logger
|
||||||
|
from .ecc_fast import _libsecp256k1, SECP256K1_EC_UNCOMPRESSED
|
||||||
|
|
||||||
_logger = get_logger(__name__)
|
_logger = get_logger(__name__)
|
||||||
do_monkey_patching_of_python_ecdsa_internals_with_libsecp256k1()
|
|
||||||
|
|
||||||
CURVE_ORDER = SECP256k1.order
|
|
||||||
|
|
||||||
|
|
||||||
def generator():
|
def string_to_number(b: bytes) -> int:
|
||||||
return ECPubkey.from_point(generator_secp256k1)
|
return int.from_bytes(b, byteorder='big', signed=False)
|
||||||
|
|
||||||
|
|
||||||
def point_at_infinity():
|
def sig_string_from_der_sig(der_sig: bytes) -> bytes:
|
||||||
return ECPubkey(None)
|
r, s = get_r_and_s_from_der_sig(der_sig)
|
||||||
|
return sig_string_from_r_and_s(r, s)
|
||||||
|
|
||||||
|
|
||||||
def sig_string_from_der_sig(der_sig: bytes, order=CURVE_ORDER) -> bytes:
|
def der_sig_from_sig_string(sig_string: bytes) -> bytes:
|
||||||
r, s = ecdsa.util.sigdecode_der(der_sig, order)
|
r, s = get_r_and_s_from_sig_string(sig_string)
|
||||||
return ecdsa.util.sigencode_string(r, s, order)
|
return der_sig_from_r_and_s(r, s)
|
||||||
|
|
||||||
|
|
||||||
def der_sig_from_sig_string(sig_string: bytes, order=CURVE_ORDER) -> bytes:
|
def der_sig_from_r_and_s(r: int, s: int) -> bytes:
|
||||||
r, s = ecdsa.util.sigdecode_string(sig_string, order)
|
sig_string = (int.to_bytes(r, length=32, byteorder="big") +
|
||||||
return ecdsa.util.sigencode_der_canonize(r, s, order)
|
int.to_bytes(s, length=32, byteorder="big"))
|
||||||
|
sig = create_string_buffer(64)
|
||||||
|
ret = _libsecp256k1.secp256k1_ecdsa_signature_parse_compact(_libsecp256k1.ctx, sig, sig_string)
|
||||||
|
if not ret:
|
||||||
|
raise Exception("Bad signature")
|
||||||
|
ret = _libsecp256k1.secp256k1_ecdsa_signature_normalize(_libsecp256k1.ctx, sig, sig)
|
||||||
|
der_sig = create_string_buffer(80) # this much space should be enough
|
||||||
|
der_sig_size = c_size_t(len(der_sig))
|
||||||
|
ret = _libsecp256k1.secp256k1_ecdsa_signature_serialize_der(_libsecp256k1.ctx, der_sig, byref(der_sig_size), sig)
|
||||||
|
if not ret:
|
||||||
|
raise Exception("failed to serialize DER sig")
|
||||||
|
der_sig_size = der_sig_size.value
|
||||||
|
return bytes(der_sig)[:der_sig_size]
|
||||||
|
|
||||||
|
|
||||||
def der_sig_from_r_and_s(r: int, s: int, order=CURVE_ORDER) -> bytes:
|
def get_r_and_s_from_der_sig(der_sig: bytes) -> Tuple[int, int]:
|
||||||
return ecdsa.util.sigencode_der_canonize(r, s, order)
|
assert isinstance(der_sig, bytes)
|
||||||
|
sig = create_string_buffer(64)
|
||||||
|
ret = _libsecp256k1.secp256k1_ecdsa_signature_parse_der(_libsecp256k1.ctx, sig, der_sig, len(der_sig))
|
||||||
def get_r_and_s_from_der_sig(der_sig: bytes, order=CURVE_ORDER) -> Tuple[int, int]:
|
if not ret:
|
||||||
r, s = ecdsa.util.sigdecode_der(der_sig, order)
|
raise Exception("Bad signature")
|
||||||
|
ret = _libsecp256k1.secp256k1_ecdsa_signature_normalize(_libsecp256k1.ctx, sig, sig)
|
||||||
|
compact_signature = create_string_buffer(64)
|
||||||
|
_libsecp256k1.secp256k1_ecdsa_signature_serialize_compact(_libsecp256k1.ctx, compact_signature, sig)
|
||||||
|
r = int.from_bytes(compact_signature[:32], byteorder="big")
|
||||||
|
s = int.from_bytes(compact_signature[32:], byteorder="big")
|
||||||
return r, s
|
return r, s
|
||||||
|
|
||||||
|
|
||||||
def get_r_and_s_from_sig_string(sig_string: bytes, order=CURVE_ORDER) -> Tuple[int, int]:
|
def get_r_and_s_from_sig_string(sig_string: bytes) -> Tuple[int, int]:
|
||||||
r, s = ecdsa.util.sigdecode_string(sig_string, order)
|
if not (isinstance(sig_string, bytes) and len(sig_string) == 64):
|
||||||
|
raise Exception("sig_string must be bytes, and 64 bytes exactly")
|
||||||
|
sig = create_string_buffer(64)
|
||||||
|
ret = _libsecp256k1.secp256k1_ecdsa_signature_parse_compact(_libsecp256k1.ctx, sig, sig_string)
|
||||||
|
if not ret:
|
||||||
|
raise Exception("Bad signature")
|
||||||
|
ret = _libsecp256k1.secp256k1_ecdsa_signature_normalize(_libsecp256k1.ctx, sig, sig)
|
||||||
|
compact_signature = create_string_buffer(64)
|
||||||
|
_libsecp256k1.secp256k1_ecdsa_signature_serialize_compact(_libsecp256k1.ctx, compact_signature, sig)
|
||||||
|
r = int.from_bytes(compact_signature[:32], byteorder="big")
|
||||||
|
s = int.from_bytes(compact_signature[32:], byteorder="big")
|
||||||
return r, s
|
return r, s
|
||||||
|
|
||||||
|
|
||||||
def sig_string_from_r_and_s(r: int, s: int, order=CURVE_ORDER) -> bytes:
|
def sig_string_from_r_and_s(r: int, s: int) -> bytes:
|
||||||
return ecdsa.util.sigencode_string_canonize(r, s, order)
|
sig_string = (int.to_bytes(r, length=32, byteorder="big") +
|
||||||
|
int.to_bytes(s, length=32, byteorder="big"))
|
||||||
|
sig = create_string_buffer(64)
|
||||||
|
ret = _libsecp256k1.secp256k1_ecdsa_signature_parse_compact(_libsecp256k1.ctx, sig, sig_string)
|
||||||
|
if not ret:
|
||||||
|
raise Exception("Bad signature")
|
||||||
|
ret = _libsecp256k1.secp256k1_ecdsa_signature_normalize(_libsecp256k1.ctx, sig, sig)
|
||||||
|
compact_signature = create_string_buffer(64)
|
||||||
|
_libsecp256k1.secp256k1_ecdsa_signature_serialize_compact(_libsecp256k1.ctx, compact_signature, sig)
|
||||||
|
return bytes(compact_signature)
|
||||||
|
|
||||||
|
|
||||||
def point_to_ser(point, compressed=True) -> Optional[bytes]:
|
def _x_and_y_from_pubkey_bytes(pubkey: bytes) -> Tuple[int, int]:
|
||||||
if isinstance(point, tuple):
|
pubkey_ptr = create_string_buffer(64)
|
||||||
assert len(point) == 2, f'unexpected point: {point}'
|
ret = _libsecp256k1.secp256k1_ec_pubkey_parse(
|
||||||
x, y = point
|
_libsecp256k1.ctx, pubkey_ptr, pubkey, len(pubkey))
|
||||||
else:
|
if not ret:
|
||||||
x, y = point.x(), point.y()
|
raise InvalidECPointException('public key could not be parsed or is invalid')
|
||||||
if x is None or y is None: # infinity
|
|
||||||
return None
|
|
||||||
if compressed:
|
|
||||||
return bfh(('%02x' % (2+(y&1))) + ('%064x' % x))
|
|
||||||
return bfh('04'+('%064x' % x)+('%064x' % y))
|
|
||||||
|
|
||||||
|
pubkey_serialized = create_string_buffer(65)
|
||||||
def get_y_coord_from_x(x: int, *, odd: bool) -> int:
|
pubkey_size = c_size_t(65)
|
||||||
curve = curve_secp256k1
|
_libsecp256k1.secp256k1_ec_pubkey_serialize(
|
||||||
_p = curve.p()
|
_libsecp256k1.ctx, pubkey_serialized, byref(pubkey_size), pubkey_ptr, SECP256K1_EC_UNCOMPRESSED)
|
||||||
_a = curve.a()
|
pubkey_serialized = bytes(pubkey_serialized)
|
||||||
_b = curve.b()
|
assert pubkey_serialized[0] == 0x04, pubkey_serialized
|
||||||
x = x % _p
|
x = int.from_bytes(pubkey_serialized[1:33], byteorder='big', signed=False)
|
||||||
y2 = (pow(x, 3, _p) + _a * x + _b) % _p
|
y = int.from_bytes(pubkey_serialized[33:65], byteorder='big', signed=False)
|
||||||
y = msqr.modular_sqrt(y2, _p)
|
return x, y
|
||||||
if curve.contains_point(x, y):
|
|
||||||
if odd == bool(y & 1):
|
|
||||||
return y
|
|
||||||
return _p - y
|
|
||||||
raise InvalidECPointException()
|
|
||||||
|
|
||||||
|
|
||||||
def ser_to_point(ser: bytes) -> Tuple[int, int]:
|
|
||||||
if ser[0] not in (0x02, 0x03, 0x04):
|
|
||||||
raise ValueError('Unexpected first byte: {}'.format(ser[0]))
|
|
||||||
if ser[0] == 0x04:
|
|
||||||
return string_to_number(ser[1:33]), string_to_number(ser[33:])
|
|
||||||
x = string_to_number(ser[1:])
|
|
||||||
odd = ser[0] == 0x03
|
|
||||||
return x, get_y_coord_from_x(x, odd=odd)
|
|
||||||
|
|
||||||
|
|
||||||
def _ser_to_python_ecdsa_point(ser: bytes) -> ecdsa.ellipticcurve.Point:
|
|
||||||
x, y = ser_to_point(ser)
|
|
||||||
try:
|
|
||||||
return Point(curve_secp256k1, x, y, CURVE_ORDER)
|
|
||||||
except:
|
|
||||||
raise InvalidECPointException()
|
|
||||||
|
|
||||||
|
|
||||||
class InvalidECPointException(Exception):
|
class InvalidECPointException(Exception):
|
||||||
"""e.g. not on curve, or infinity"""
|
"""e.g. not on curve, or infinity"""
|
||||||
|
|
||||||
|
|
||||||
class _MyVerifyingKey(ecdsa.VerifyingKey):
|
|
||||||
@classmethod
|
|
||||||
def from_signature(klass, sig, recid, h, curve): # TODO use libsecp??
|
|
||||||
""" See http://www.secg.org/download/aid-780/sec1-v2.pdf, chapter 4.1.6 """
|
|
||||||
from ecdsa import util, numbertheory
|
|
||||||
from . import msqr
|
|
||||||
curveFp = curve.curve
|
|
||||||
G = curve.generator
|
|
||||||
order = G.order()
|
|
||||||
# extract r,s from signature
|
|
||||||
r, s = util.sigdecode_string(sig, order)
|
|
||||||
# 1.1
|
|
||||||
x = r + (recid//2) * order
|
|
||||||
# 1.3
|
|
||||||
alpha = ( x * x * x + curveFp.a() * x + curveFp.b() ) % curveFp.p()
|
|
||||||
beta = msqr.modular_sqrt(alpha, curveFp.p())
|
|
||||||
y = beta if (beta - recid) % 2 == 0 else curveFp.p() - beta
|
|
||||||
# 1.4 the constructor checks that nR is at infinity
|
|
||||||
try:
|
|
||||||
R = Point(curveFp, x, y, order)
|
|
||||||
except:
|
|
||||||
raise InvalidECPointException()
|
|
||||||
# 1.5 compute e from message:
|
|
||||||
e = string_to_number(h)
|
|
||||||
minus_e = -e % order
|
|
||||||
# 1.6 compute Q = r^-1 (sR - eG)
|
|
||||||
inv_r = numbertheory.inverse_mod(r,order)
|
|
||||||
try:
|
|
||||||
Q = inv_r * ( s * R + minus_e * G )
|
|
||||||
except:
|
|
||||||
raise InvalidECPointException()
|
|
||||||
return klass.from_public_point( Q, curve )
|
|
||||||
|
|
||||||
|
|
||||||
class _MySigningKey(ecdsa.SigningKey):
|
|
||||||
"""Enforce low S values in signatures"""
|
|
||||||
|
|
||||||
def sign_number(self, number, entropy=None, k=None):
|
|
||||||
r, s = ecdsa.SigningKey.sign_number(self, number, entropy, k)
|
|
||||||
if s > CURVE_ORDER//2:
|
|
||||||
s = CURVE_ORDER - s
|
|
||||||
return r, s
|
|
||||||
|
|
||||||
|
|
||||||
class _PubkeyForPointAtInfinity:
|
|
||||||
point = ecdsa.ellipticcurve.INFINITY
|
|
||||||
|
|
||||||
|
|
||||||
@functools.total_ordering
|
@functools.total_ordering
|
||||||
class ECPubkey(object):
|
class ECPubkey(object):
|
||||||
|
|
||||||
def __init__(self, b: Optional[bytes]):
|
def __init__(self, b: Optional[bytes]):
|
||||||
if b is not None:
|
if b is not None:
|
||||||
assert_bytes(b)
|
assert_bytes(b)
|
||||||
point = _ser_to_python_ecdsa_point(b)
|
self._x, self._y = _x_and_y_from_pubkey_bytes(b)
|
||||||
self._pubkey = ecdsa.ecdsa.Public_key(generator_secp256k1, point)
|
|
||||||
else:
|
else:
|
||||||
self._pubkey = _PubkeyForPointAtInfinity()
|
self._x, self._y = None, None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_sig_string(cls, sig_string: bytes, recid: int, msg_hash: bytes):
|
def from_sig_string(cls, sig_string: bytes, recid: int, msg_hash: bytes) -> 'ECPubkey':
|
||||||
assert_bytes(sig_string)
|
assert_bytes(sig_string)
|
||||||
if len(sig_string) != 64:
|
if len(sig_string) != 64:
|
||||||
raise Exception('Wrong encoding')
|
raise Exception(f'wrong encoding used for signature? len={len(sig_string)} (should be 64)')
|
||||||
if recid < 0 or recid > 3:
|
if recid < 0 or recid > 3:
|
||||||
raise ValueError('recid is {}, but should be 0 <= recid <= 3'.format(recid))
|
raise ValueError('recid is {}, but should be 0 <= recid <= 3'.format(recid))
|
||||||
ecdsa_verifying_key = _MyVerifyingKey.from_signature(sig_string, recid, msg_hash, curve=SECP256k1)
|
sig65 = create_string_buffer(65)
|
||||||
ecdsa_point = ecdsa_verifying_key.pubkey.point
|
ret = _libsecp256k1.secp256k1_ecdsa_recoverable_signature_parse_compact(
|
||||||
return ECPubkey.from_point(ecdsa_point)
|
_libsecp256k1.ctx, sig65, sig_string, recid)
|
||||||
|
if not ret:
|
||||||
|
raise Exception('failed to parse signature')
|
||||||
|
pubkey = create_string_buffer(64)
|
||||||
|
ret = _libsecp256k1.secp256k1_ecdsa_recover(_libsecp256k1.ctx, pubkey, sig65, msg_hash)
|
||||||
|
if not ret:
|
||||||
|
raise InvalidECPointException('failed to recover public key')
|
||||||
|
return ECPubkey._from_libsecp256k1_pubkey_ptr(pubkey)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_signature65(cls, sig: bytes, msg_hash: bytes):
|
def from_signature65(cls, sig: bytes, msg_hash: bytes) -> Tuple['ECPubkey', bool]:
|
||||||
if len(sig) != 65:
|
if len(sig) != 65:
|
||||||
raise Exception("Wrong encoding")
|
raise Exception(f'wrong encoding used for signature? len={len(sig)} (should be 65)')
|
||||||
nV = sig[0]
|
nV = sig[0]
|
||||||
if nV < 27 or nV >= 35:
|
if nV < 27 or nV >= 35:
|
||||||
raise Exception("Bad encoding")
|
raise Exception("Bad encoding")
|
||||||
|
@ -221,28 +180,70 @@ class ECPubkey(object):
|
||||||
return cls.from_sig_string(sig[1:], recid, msg_hash), compressed
|
return cls.from_sig_string(sig[1:], recid, msg_hash), compressed
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_point(cls, point):
|
def from_x_and_y(cls, x: int, y: int) -> 'ECPubkey':
|
||||||
_bytes = point_to_ser(point, compressed=False) # faster than compressed
|
_bytes = (b'\x04'
|
||||||
|
+ int.to_bytes(x, length=32, byteorder='big', signed=False)
|
||||||
|
+ int.to_bytes(y, length=32, byteorder='big', signed=False))
|
||||||
return ECPubkey(_bytes)
|
return ECPubkey(_bytes)
|
||||||
|
|
||||||
def get_public_key_bytes(self, compressed=True):
|
def get_public_key_bytes(self, compressed=True):
|
||||||
if self.is_at_infinity(): raise Exception('point is at infinity')
|
if self.is_at_infinity(): raise Exception('point is at infinity')
|
||||||
return point_to_ser(self.point(), compressed)
|
x = int.to_bytes(self.x(), length=32, byteorder='big', signed=False)
|
||||||
|
y = int.to_bytes(self.y(), length=32, byteorder='big', signed=False)
|
||||||
|
if compressed:
|
||||||
|
header = b'\x03' if self.y() & 1 else b'\x02'
|
||||||
|
return header + x
|
||||||
|
else:
|
||||||
|
header = b'\x04'
|
||||||
|
return header + x + y
|
||||||
|
|
||||||
def get_public_key_hex(self, compressed=True):
|
def get_public_key_hex(self, compressed=True):
|
||||||
return bh2u(self.get_public_key_bytes(compressed))
|
return bh2u(self.get_public_key_bytes(compressed))
|
||||||
|
|
||||||
def point(self) -> Tuple[int, int]:
|
def point(self) -> Tuple[int, int]:
|
||||||
return self._pubkey.point.x(), self._pubkey.point.y()
|
return self.x(), self.y()
|
||||||
|
|
||||||
|
def x(self) -> int:
|
||||||
|
return self._x
|
||||||
|
|
||||||
|
def y(self) -> int:
|
||||||
|
return self._y
|
||||||
|
|
||||||
|
def _to_libsecp256k1_pubkey_ptr(self):
|
||||||
|
pubkey = create_string_buffer(64)
|
||||||
|
public_pair_bytes = self.get_public_key_bytes(compressed=False)
|
||||||
|
ret = _libsecp256k1.secp256k1_ec_pubkey_parse(
|
||||||
|
_libsecp256k1.ctx, pubkey, public_pair_bytes, len(public_pair_bytes))
|
||||||
|
if not ret:
|
||||||
|
raise Exception('public key could not be parsed or is invalid')
|
||||||
|
return pubkey
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _from_libsecp256k1_pubkey_ptr(cls, pubkey) -> 'ECPubkey':
|
||||||
|
pubkey_serialized = create_string_buffer(65)
|
||||||
|
pubkey_size = c_size_t(65)
|
||||||
|
_libsecp256k1.secp256k1_ec_pubkey_serialize(
|
||||||
|
_libsecp256k1.ctx, pubkey_serialized, byref(pubkey_size), pubkey, SECP256K1_EC_UNCOMPRESSED)
|
||||||
|
return ECPubkey(bytes(pubkey_serialized))
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
if self.is_at_infinity():
|
||||||
|
return f"<ECPubkey infinity>"
|
||||||
return f"<ECPubkey {self.get_public_key_hex()}>"
|
return f"<ECPubkey {self.get_public_key_hex()}>"
|
||||||
|
|
||||||
def __mul__(self, other: int):
|
def __mul__(self, other: int):
|
||||||
if not isinstance(other, int):
|
if not isinstance(other, int):
|
||||||
raise TypeError('multiplication not defined for ECPubkey and {}'.format(type(other)))
|
raise TypeError('multiplication not defined for ECPubkey and {}'.format(type(other)))
|
||||||
ecdsa_point = self._pubkey.point * other
|
|
||||||
return self.from_point(ecdsa_point)
|
other %= CURVE_ORDER
|
||||||
|
if self.is_at_infinity() or other == 0:
|
||||||
|
return POINT_AT_INFINITY
|
||||||
|
pubkey = self._to_libsecp256k1_pubkey_ptr()
|
||||||
|
|
||||||
|
ret = _libsecp256k1.secp256k1_ec_pubkey_tweak_mul(_libsecp256k1.ctx, pubkey, other.to_bytes(32, byteorder="big"))
|
||||||
|
if not ret:
|
||||||
|
return POINT_AT_INFINITY
|
||||||
|
return ECPubkey._from_libsecp256k1_pubkey_ptr(pubkey)
|
||||||
|
|
||||||
def __rmul__(self, other: int):
|
def __rmul__(self, other: int):
|
||||||
return self * other
|
return self * other
|
||||||
|
@ -250,38 +251,36 @@ class ECPubkey(object):
|
||||||
def __add__(self, other):
|
def __add__(self, other):
|
||||||
if not isinstance(other, ECPubkey):
|
if not isinstance(other, ECPubkey):
|
||||||
raise TypeError('addition not defined for ECPubkey and {}'.format(type(other)))
|
raise TypeError('addition not defined for ECPubkey and {}'.format(type(other)))
|
||||||
ecdsa_point = self._pubkey.point + other._pubkey.point
|
if self.is_at_infinity(): return other
|
||||||
return self.from_point(ecdsa_point)
|
if other.is_at_infinity(): return self
|
||||||
|
|
||||||
def __eq__(self, other):
|
pubkey1 = self._to_libsecp256k1_pubkey_ptr()
|
||||||
return self._pubkey.point.x() == other._pubkey.point.x() \
|
pubkey2 = other._to_libsecp256k1_pubkey_ptr()
|
||||||
and self._pubkey.point.y() == other._pubkey.point.y()
|
pubkey_sum = create_string_buffer(64)
|
||||||
|
|
||||||
|
pubkey1 = cast(pubkey1, c_char_p)
|
||||||
|
pubkey2 = cast(pubkey2, c_char_p)
|
||||||
|
array_of_pubkey_ptrs = (c_char_p * 2)(pubkey1, pubkey2)
|
||||||
|
ret = _libsecp256k1.secp256k1_ec_pubkey_combine(_libsecp256k1.ctx, pubkey_sum, array_of_pubkey_ptrs, 2)
|
||||||
|
if not ret:
|
||||||
|
return POINT_AT_INFINITY
|
||||||
|
return ECPubkey._from_libsecp256k1_pubkey_ptr(pubkey_sum)
|
||||||
|
|
||||||
|
def __eq__(self, other) -> bool:
|
||||||
|
if not isinstance(other, ECPubkey):
|
||||||
|
return False
|
||||||
|
return self.point() == other.point()
|
||||||
|
|
||||||
def __ne__(self, other):
|
def __ne__(self, other):
|
||||||
return not (self == other)
|
return not (self == other)
|
||||||
|
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
return hash(self._pubkey.point.x())
|
return hash(self.point())
|
||||||
|
|
||||||
def __lt__(self, other):
|
def __lt__(self, other):
|
||||||
if not isinstance(other, ECPubkey):
|
if not isinstance(other, ECPubkey):
|
||||||
raise TypeError('comparison not defined for ECPubkey and {}'.format(type(other)))
|
raise TypeError('comparison not defined for ECPubkey and {}'.format(type(other)))
|
||||||
return self._pubkey.point.x() < other._pubkey.point.x()
|
return (self.x() or 0) < (other.x() or 0)
|
||||||
|
|
||||||
def __deepcopy__(self, memo: dict = None):
|
|
||||||
# note: This custom deepcopy implementation needed as copy.deepcopy(self._pubkey) raises.
|
|
||||||
if memo is None: memo = {}
|
|
||||||
cls = self.__class__
|
|
||||||
result = cls.__new__(cls)
|
|
||||||
memo[id(self)] = result
|
|
||||||
for k, v in self.__dict__.items():
|
|
||||||
if k == '_pubkey' and not self.is_at_infinity():
|
|
||||||
point = _ser_to_python_ecdsa_point(self.get_public_key_bytes(compressed=False))
|
|
||||||
_pubkey_copy = ecdsa.ecdsa.Public_key(generator_secp256k1, point)
|
|
||||||
setattr(result, k, _pubkey_copy)
|
|
||||||
else:
|
|
||||||
setattr(result, k, copy.deepcopy(v, memo))
|
|
||||||
return result
|
|
||||||
|
|
||||||
def verify_message_for_address(self, sig65: bytes, message: bytes, algo=lambda x: sha256d(msg_magic(x))) -> None:
|
def verify_message_for_address(self, sig65: bytes, message: bytes, algo=lambda x: sha256d(msg_magic(x))) -> None:
|
||||||
assert_bytes(message)
|
assert_bytes(message)
|
||||||
|
@ -293,13 +292,23 @@ class ECPubkey(object):
|
||||||
# check message
|
# check message
|
||||||
self.verify_message_hash(sig65[1:], h)
|
self.verify_message_hash(sig65[1:], h)
|
||||||
|
|
||||||
|
# TODO return bool instead of raising
|
||||||
def verify_message_hash(self, sig_string: bytes, msg_hash: bytes) -> None:
|
def verify_message_hash(self, sig_string: bytes, msg_hash: bytes) -> None:
|
||||||
assert_bytes(sig_string)
|
assert_bytes(sig_string)
|
||||||
if len(sig_string) != 64:
|
if len(sig_string) != 64:
|
||||||
raise Exception('Wrong encoding')
|
raise Exception(f'wrong encoding used for signature? len={len(sig_string)} (should be 64)')
|
||||||
ecdsa_point = self._pubkey.point
|
if not (isinstance(msg_hash, bytes) and len(msg_hash) == 32):
|
||||||
verifying_key = _MyVerifyingKey.from_public_point(ecdsa_point, curve=SECP256k1)
|
raise Exception("msg_hash must be bytes, and 32 bytes exactly")
|
||||||
verifying_key.verify_digest(sig_string, msg_hash, sigdecode=ecdsa.util.sigdecode_string)
|
|
||||||
|
sig = create_string_buffer(64)
|
||||||
|
ret = _libsecp256k1.secp256k1_ecdsa_signature_parse_compact(_libsecp256k1.ctx, sig, sig_string)
|
||||||
|
if not ret:
|
||||||
|
raise Exception("Bad signature")
|
||||||
|
ret = _libsecp256k1.secp256k1_ecdsa_signature_normalize(_libsecp256k1.ctx, sig, sig)
|
||||||
|
|
||||||
|
pubkey = self._to_libsecp256k1_pubkey_ptr()
|
||||||
|
if 1 != _libsecp256k1.secp256k1_ecdsa_verify(_libsecp256k1.ctx, sig, msg_hash, pubkey):
|
||||||
|
raise Exception("Bad signature")
|
||||||
|
|
||||||
def encrypt_message(self, message: bytes, magic: bytes = b'BIE1') -> bytes:
|
def encrypt_message(self, message: bytes, magic: bytes = b'BIE1') -> bytes:
|
||||||
"""
|
"""
|
||||||
|
@ -323,7 +332,7 @@ class ECPubkey(object):
|
||||||
return CURVE_ORDER
|
return CURVE_ORDER
|
||||||
|
|
||||||
def is_at_infinity(self):
|
def is_at_infinity(self):
|
||||||
return self == point_at_infinity()
|
return self == POINT_AT_INFINITY
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def is_pubkey_bytes(cls, b: bytes):
|
def is_pubkey_bytes(cls, b: bytes):
|
||||||
|
@ -334,6 +343,12 @@ class ECPubkey(object):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
GENERATOR = ECPubkey(bytes.fromhex('0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798'
|
||||||
|
'483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8'))
|
||||||
|
CURVE_ORDER = 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFE_BAAEDCE6_AF48A03B_BFD25E8C_D0364141
|
||||||
|
POINT_AT_INFINITY = ECPubkey(None)
|
||||||
|
|
||||||
|
|
||||||
def msg_magic(message: bytes) -> bytes:
|
def msg_magic(message: bytes) -> bytes:
|
||||||
from .bitcoin import var_int
|
from .bitcoin import var_int
|
||||||
length = bfh(var_int(len(message)))
|
length = bfh(var_int(len(message)))
|
||||||
|
@ -387,12 +402,12 @@ class ECPrivkey(ECPubkey):
|
||||||
raise InvalidECPointException('Invalid secret scalar (not within curve order)')
|
raise InvalidECPointException('Invalid secret scalar (not within curve order)')
|
||||||
self.secret_scalar = secret
|
self.secret_scalar = secret
|
||||||
|
|
||||||
point = generator_secp256k1 * secret
|
pubkey = GENERATOR * secret
|
||||||
super().__init__(point_to_ser(point))
|
super().__init__(pubkey.get_public_key_bytes(compressed=False))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_secret_scalar(cls, secret_scalar: int):
|
def from_secret_scalar(cls, secret_scalar: int):
|
||||||
secret_bytes = number_to_string(secret_scalar, CURVE_ORDER)
|
secret_bytes = int.to_bytes(secret_scalar, length=32, byteorder='big', signed=False)
|
||||||
return ECPrivkey(secret_bytes)
|
return ECPrivkey(secret_bytes)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -408,7 +423,7 @@ class ECPrivkey(ECPubkey):
|
||||||
scalar = string_to_number(privkey_bytes) % CURVE_ORDER
|
scalar = string_to_number(privkey_bytes) % CURVE_ORDER
|
||||||
if scalar == 0:
|
if scalar == 0:
|
||||||
raise Exception('invalid EC private key scalar: zero')
|
raise Exception('invalid EC private key scalar: zero')
|
||||||
privkey_32bytes = number_to_string(scalar, CURVE_ORDER)
|
privkey_32bytes = int.to_bytes(scalar, length=32, byteorder='big', signed=False)
|
||||||
return privkey_32bytes
|
return privkey_32bytes
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
@ -416,37 +431,49 @@ class ECPrivkey(ECPubkey):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generate_random_key(cls):
|
def generate_random_key(cls):
|
||||||
randint = ecdsa.util.randrange(CURVE_ORDER)
|
randint = randrange(CURVE_ORDER)
|
||||||
ephemeral_exponent = number_to_string(randint, CURVE_ORDER)
|
ephemeral_exponent = int.to_bytes(randint, length=32, byteorder='big', signed=False)
|
||||||
return ECPrivkey(ephemeral_exponent)
|
return ECPrivkey(ephemeral_exponent)
|
||||||
|
|
||||||
def get_secret_bytes(self) -> bytes:
|
def get_secret_bytes(self) -> bytes:
|
||||||
return number_to_string(self.secret_scalar, CURVE_ORDER)
|
return int.to_bytes(self.secret_scalar, length=32, byteorder='big', signed=False)
|
||||||
|
|
||||||
def sign(self, data: bytes, sigencode=None, sigdecode=None) -> bytes:
|
def sign(self, msg_hash: bytes, sigencode=None) -> bytes:
|
||||||
|
if not (isinstance(msg_hash, bytes) and len(msg_hash) == 32):
|
||||||
|
raise Exception("msg_hash to be signed must be bytes, and 32 bytes exactly")
|
||||||
if sigencode is None:
|
if sigencode is None:
|
||||||
sigencode = sig_string_from_r_and_s
|
sigencode = sig_string_from_r_and_s
|
||||||
if sigdecode is None:
|
|
||||||
sigdecode = get_r_and_s_from_sig_string
|
privkey_bytes = self.secret_scalar.to_bytes(32, byteorder="big")
|
||||||
private_key = _MySigningKey.from_secret_exponent(self.secret_scalar, curve=SECP256k1)
|
nonce_function = None
|
||||||
def sig_encode_r_s(r, s, order):
|
sig = create_string_buffer(64)
|
||||||
|
def sign_with_extra_entropy(extra_entropy):
|
||||||
|
ret = _libsecp256k1.secp256k1_ecdsa_sign(
|
||||||
|
_libsecp256k1.ctx, sig, msg_hash, privkey_bytes,
|
||||||
|
nonce_function, extra_entropy)
|
||||||
|
if not ret:
|
||||||
|
raise Exception('the nonce generation function failed, or the private key was invalid')
|
||||||
|
compact_signature = create_string_buffer(64)
|
||||||
|
_libsecp256k1.secp256k1_ecdsa_signature_serialize_compact(_libsecp256k1.ctx, compact_signature, sig)
|
||||||
|
r = int.from_bytes(compact_signature[:32], byteorder="big")
|
||||||
|
s = int.from_bytes(compact_signature[32:], byteorder="big")
|
||||||
return r, s
|
return r, s
|
||||||
r, s = private_key.sign_digest_deterministic(data, hashfunc=hashlib.sha256, sigencode=sig_encode_r_s)
|
|
||||||
|
r, s = sign_with_extra_entropy(extra_entropy=None)
|
||||||
counter = 0
|
counter = 0
|
||||||
while r >= 2**255: # grind for low R value https://github.com/bitcoin/bitcoin/pull/13666
|
while r >= 2**255: # grind for low R value https://github.com/bitcoin/bitcoin/pull/13666
|
||||||
counter += 1
|
counter += 1
|
||||||
extra_entropy = int.to_bytes(counter, 32, 'little')
|
extra_entropy = counter.to_bytes(32, byteorder="little")
|
||||||
r, s = private_key.sign_digest_deterministic(data, hashfunc=hashlib.sha256, sigencode=sig_encode_r_s, extra_entropy=extra_entropy)
|
r, s = sign_with_extra_entropy(extra_entropy=extra_entropy)
|
||||||
sig = sigencode(r, s, CURVE_ORDER)
|
|
||||||
public_key = private_key.get_verifying_key()
|
sig_string = sig_string_from_r_and_s(r, s)
|
||||||
if not public_key.verify_digest(sig, data, sigdecode=sigdecode):
|
self.verify_message_hash(sig_string, msg_hash)
|
||||||
raise Exception('Sanity check verifying our own signature failed.')
|
|
||||||
|
sig = sigencode(r, s)
|
||||||
return sig
|
return sig
|
||||||
|
|
||||||
def sign_transaction(self, hashed_preimage: bytes) -> bytes:
|
def sign_transaction(self, hashed_preimage: bytes) -> bytes:
|
||||||
return self.sign(hashed_preimage,
|
return self.sign(hashed_preimage, sigencode=der_sig_from_r_and_s)
|
||||||
sigencode=der_sig_from_r_and_s,
|
|
||||||
sigdecode=get_r_and_s_from_der_sig)
|
|
||||||
|
|
||||||
def sign_message(self, message: bytes, is_compressed: bool, algo=lambda x: sha256d(msg_magic(x))) -> bytes:
|
def sign_message(self, message: bytes, is_compressed: bool, algo=lambda x: sha256d(msg_magic(x))) -> bytes:
|
||||||
def bruteforce_recid(sig_string):
|
def bruteforce_recid(sig_string):
|
||||||
|
@ -462,9 +489,7 @@ class ECPrivkey(ECPubkey):
|
||||||
|
|
||||||
message = to_bytes(message, 'utf8')
|
message = to_bytes(message, 'utf8')
|
||||||
msg_hash = algo(message)
|
msg_hash = algo(message)
|
||||||
sig_string = self.sign(msg_hash,
|
sig_string = self.sign(msg_hash, sigencode=sig_string_from_r_and_s)
|
||||||
sigencode=sig_string_from_r_and_s,
|
|
||||||
sigdecode=get_r_and_s_from_sig_string)
|
|
||||||
sig65, recid = bruteforce_recid(sig_string)
|
sig65, recid = bruteforce_recid(sig_string)
|
||||||
return sig65
|
return sig65
|
||||||
|
|
||||||
|
@ -479,12 +504,9 @@ class ECPrivkey(ECPubkey):
|
||||||
if magic_found != magic:
|
if magic_found != magic:
|
||||||
raise Exception('invalid ciphertext: invalid magic bytes')
|
raise Exception('invalid ciphertext: invalid magic bytes')
|
||||||
try:
|
try:
|
||||||
ecdsa_point = _ser_to_python_ecdsa_point(ephemeral_pubkey_bytes)
|
ephemeral_pubkey = ECPubkey(ephemeral_pubkey_bytes)
|
||||||
except InvalidECPointException as e:
|
except InvalidECPointException as e:
|
||||||
raise Exception('invalid ciphertext: invalid ephemeral pubkey') from e
|
raise Exception('invalid ciphertext: invalid ephemeral pubkey') from e
|
||||||
if not ecdsa.ecdsa.point_is_valid(generator_secp256k1, ecdsa_point.x(), ecdsa_point.y()):
|
|
||||||
raise Exception('invalid ciphertext: invalid ephemeral pubkey')
|
|
||||||
ephemeral_pubkey = ECPubkey.from_point(ecdsa_point)
|
|
||||||
ecdh_key = (ephemeral_pubkey * self.secret_scalar).get_public_key_bytes(compressed=True)
|
ecdh_key = (ephemeral_pubkey * self.secret_scalar).get_public_key_bytes(compressed=True)
|
||||||
key = hashlib.sha512(ecdh_key).digest()
|
key = hashlib.sha512(ecdh_key).digest()
|
||||||
iv, key_e, key_m = key[0:16], key[16:32], key[32:]
|
iv, key_e, key_m = key[0:16], key[16:32], key[32:]
|
||||||
|
|
|
@ -5,14 +5,11 @@ import os
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
import ctypes
|
import ctypes
|
||||||
from ctypes.util import find_library
|
|
||||||
from ctypes import (
|
from ctypes import (
|
||||||
byref, c_byte, c_int, c_uint, c_char_p, c_size_t, c_void_p, create_string_buffer,
|
byref, c_byte, c_int, c_uint, c_char_p, c_size_t, c_void_p, create_string_buffer,
|
||||||
CFUNCTYPE, POINTER, cast
|
CFUNCTYPE, POINTER, cast
|
||||||
)
|
)
|
||||||
|
|
||||||
import ecdsa
|
|
||||||
|
|
||||||
from .logging import get_logger
|
from .logging import get_logger
|
||||||
|
|
||||||
|
|
||||||
|
@ -36,19 +33,32 @@ SECP256K1_EC_COMPRESSED = (SECP256K1_FLAGS_TYPE_COMPRESSION | SECP256K1_FLAGS_BI
|
||||||
SECP256K1_EC_UNCOMPRESSED = (SECP256K1_FLAGS_TYPE_COMPRESSION)
|
SECP256K1_EC_UNCOMPRESSED = (SECP256K1_FLAGS_TYPE_COMPRESSION)
|
||||||
|
|
||||||
|
|
||||||
|
class LibModuleMissing(Exception): pass
|
||||||
|
|
||||||
|
|
||||||
def load_library():
|
def load_library():
|
||||||
if sys.platform == 'darwin':
|
if sys.platform == 'darwin':
|
||||||
library_path = 'libsecp256k1.0.dylib'
|
library_paths = (os.path.join(os.path.dirname(__file__), 'libsecp256k1.0.dylib'),
|
||||||
|
'libsecp256k1.0.dylib')
|
||||||
elif sys.platform in ('windows', 'win32'):
|
elif sys.platform in ('windows', 'win32'):
|
||||||
library_path = 'libsecp256k1.dll'
|
library_paths = (os.path.join(os.path.dirname(__file__), 'libsecp256k1-0.dll'),
|
||||||
|
'libsecp256k1-0.dll')
|
||||||
elif 'ANDROID_DATA' in os.environ:
|
elif 'ANDROID_DATA' in os.environ:
|
||||||
library_path = 'libsecp256k1.so'
|
library_paths = ('libsecp256k1.so',)
|
||||||
else:
|
else: # desktop Linux and similar
|
||||||
library_path = 'libsecp256k1.so.0'
|
library_paths = (os.path.join(os.path.dirname(__file__), 'libsecp256k1.so.0'),
|
||||||
|
'libsecp256k1.so.0')
|
||||||
|
|
||||||
secp256k1 = ctypes.cdll.LoadLibrary(library_path)
|
secp256k1 = None
|
||||||
|
for libpath in library_paths:
|
||||||
|
try:
|
||||||
|
secp256k1 = ctypes.cdll.LoadLibrary(libpath)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
break
|
||||||
if not secp256k1:
|
if not secp256k1:
|
||||||
_logger.warning('libsecp256k1 library failed to load')
|
_logger.error('libsecp256k1 library failed to load')
|
||||||
return None
|
return None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -82,189 +92,48 @@ def load_library():
|
||||||
secp256k1.secp256k1_ecdsa_signature_serialize_compact.argtypes = [c_void_p, c_char_p, c_char_p]
|
secp256k1.secp256k1_ecdsa_signature_serialize_compact.argtypes = [c_void_p, c_char_p, c_char_p]
|
||||||
secp256k1.secp256k1_ecdsa_signature_serialize_compact.restype = c_int
|
secp256k1.secp256k1_ecdsa_signature_serialize_compact.restype = c_int
|
||||||
|
|
||||||
|
secp256k1.secp256k1_ecdsa_signature_parse_der.argtypes = [c_void_p, c_char_p, c_char_p, c_size_t]
|
||||||
|
secp256k1.secp256k1_ecdsa_signature_parse_der.restype = c_int
|
||||||
|
|
||||||
|
secp256k1.secp256k1_ecdsa_signature_serialize_der.argtypes = [c_void_p, c_char_p, c_void_p, c_char_p]
|
||||||
|
secp256k1.secp256k1_ecdsa_signature_serialize_der.restype = c_int
|
||||||
|
|
||||||
secp256k1.secp256k1_ec_pubkey_tweak_mul.argtypes = [c_void_p, c_char_p, c_char_p]
|
secp256k1.secp256k1_ec_pubkey_tweak_mul.argtypes = [c_void_p, c_char_p, c_char_p]
|
||||||
secp256k1.secp256k1_ec_pubkey_tweak_mul.restype = c_int
|
secp256k1.secp256k1_ec_pubkey_tweak_mul.restype = c_int
|
||||||
|
|
||||||
secp256k1.secp256k1_ec_pubkey_combine.argtypes = [c_void_p, c_char_p, c_void_p, c_size_t]
|
secp256k1.secp256k1_ec_pubkey_combine.argtypes = [c_void_p, c_char_p, c_void_p, c_size_t]
|
||||||
secp256k1.secp256k1_ec_pubkey_combine.restype = c_int
|
secp256k1.secp256k1_ec_pubkey_combine.restype = c_int
|
||||||
|
|
||||||
|
# --enable-module-recovery
|
||||||
|
try:
|
||||||
|
secp256k1.secp256k1_ecdsa_recover.argtypes = [c_void_p, c_char_p, c_char_p, c_char_p]
|
||||||
|
secp256k1.secp256k1_ecdsa_recover.restype = c_int
|
||||||
|
|
||||||
|
secp256k1.secp256k1_ecdsa_recoverable_signature_parse_compact.argtypes = [c_void_p, c_char_p, c_char_p, c_int]
|
||||||
|
secp256k1.secp256k1_ecdsa_recoverable_signature_parse_compact.restype = c_int
|
||||||
|
except (OSError, AttributeError):
|
||||||
|
raise LibModuleMissing('libsecp256k1 library found but it was built '
|
||||||
|
'without required module (--enable-module-recovery)')
|
||||||
|
|
||||||
secp256k1.ctx = secp256k1.secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY)
|
secp256k1.ctx = secp256k1.secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY)
|
||||||
r = secp256k1.secp256k1_context_randomize(secp256k1.ctx, os.urandom(32))
|
ret = secp256k1.secp256k1_context_randomize(secp256k1.ctx, os.urandom(32))
|
||||||
if r:
|
if not ret:
|
||||||
return secp256k1
|
_logger.error('secp256k1_context_randomize failed')
|
||||||
else:
|
|
||||||
_logger.warning('secp256k1_context_randomize failed')
|
|
||||||
return None
|
return None
|
||||||
except (OSError, AttributeError):
|
|
||||||
_logger.warning('libsecp256k1 library was found and loaded but there was an error when using it')
|
return secp256k1
|
||||||
|
except (OSError, AttributeError) as e:
|
||||||
|
_logger.error(f'libsecp256k1 library was found and loaded but there was an error when using it: {repr(e)}')
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
class _patched_functions:
|
_libsecp256k1 = None
|
||||||
prepared_to_patch = False
|
|
||||||
monkey_patching_active = False
|
|
||||||
|
|
||||||
|
|
||||||
def _prepare_monkey_patching_of_python_ecdsa_internals_with_libsecp256k1():
|
|
||||||
if not _libsecp256k1:
|
|
||||||
return
|
|
||||||
|
|
||||||
# save original functions so that we can undo patching (needed for tests)
|
|
||||||
_patched_functions.orig_sign = staticmethod(ecdsa.ecdsa.Private_key.sign)
|
|
||||||
_patched_functions.orig_verify = staticmethod(ecdsa.ecdsa.Public_key.verifies)
|
|
||||||
_patched_functions.orig_mul = staticmethod(ecdsa.ellipticcurve.Point.__mul__)
|
|
||||||
_patched_functions.orig_add = staticmethod(ecdsa.ellipticcurve.Point.__add__)
|
|
||||||
|
|
||||||
curve_secp256k1 = ecdsa.ecdsa.curve_secp256k1
|
|
||||||
curve_order = ecdsa.curves.SECP256k1.order
|
|
||||||
point_at_infinity = ecdsa.ellipticcurve.INFINITY
|
|
||||||
|
|
||||||
def _get_ptr_to_well_formed_pubkey_string_buffer_from_ecdsa_point(point: ecdsa.ellipticcurve.Point):
|
|
||||||
assert point.curve() == curve_secp256k1
|
|
||||||
pubkey = create_string_buffer(64)
|
|
||||||
public_pair_bytes = b'\4' + point.x().to_bytes(32, byteorder="big") + point.y().to_bytes(32, byteorder="big")
|
|
||||||
r = _libsecp256k1.secp256k1_ec_pubkey_parse(
|
|
||||||
_libsecp256k1.ctx, pubkey, public_pair_bytes, len(public_pair_bytes))
|
|
||||||
if not r:
|
|
||||||
raise Exception('public key could not be parsed or is invalid')
|
|
||||||
return pubkey
|
|
||||||
|
|
||||||
def _get_ecdsa_point_from_libsecp256k1_pubkey_object(pubkey) -> ecdsa.ellipticcurve.Point:
|
|
||||||
pubkey_serialized = create_string_buffer(65)
|
|
||||||
pubkey_size = c_size_t(65)
|
|
||||||
_libsecp256k1.secp256k1_ec_pubkey_serialize(
|
|
||||||
_libsecp256k1.ctx, pubkey_serialized, byref(pubkey_size), pubkey, SECP256K1_EC_UNCOMPRESSED)
|
|
||||||
x = int.from_bytes(pubkey_serialized[1:33], byteorder="big")
|
|
||||||
y = int.from_bytes(pubkey_serialized[33:], byteorder="big")
|
|
||||||
return ecdsa.ellipticcurve.Point(curve_secp256k1, x, y, curve_order)
|
|
||||||
|
|
||||||
def add(self: ecdsa.ellipticcurve.Point, other: ecdsa.ellipticcurve.Point) -> ecdsa.ellipticcurve.Point:
|
|
||||||
if self.curve() != curve_secp256k1:
|
|
||||||
# this operation is not on the secp256k1 curve; use original implementation
|
|
||||||
return _patched_functions.orig_add(self, other)
|
|
||||||
if self == point_at_infinity: return other
|
|
||||||
if other == point_at_infinity: return self
|
|
||||||
|
|
||||||
pubkey1 = _get_ptr_to_well_formed_pubkey_string_buffer_from_ecdsa_point(self)
|
|
||||||
pubkey2 = _get_ptr_to_well_formed_pubkey_string_buffer_from_ecdsa_point(other)
|
|
||||||
pubkey_sum = create_string_buffer(64)
|
|
||||||
|
|
||||||
pubkey1 = cast(pubkey1, c_char_p)
|
|
||||||
pubkey2 = cast(pubkey2, c_char_p)
|
|
||||||
array_of_pubkey_ptrs = (c_char_p * 2)(pubkey1, pubkey2)
|
|
||||||
r = _libsecp256k1.secp256k1_ec_pubkey_combine(_libsecp256k1.ctx, pubkey_sum, array_of_pubkey_ptrs, 2)
|
|
||||||
if not r:
|
|
||||||
return point_at_infinity
|
|
||||||
return _get_ecdsa_point_from_libsecp256k1_pubkey_object(pubkey_sum)
|
|
||||||
|
|
||||||
def mul(self: ecdsa.ellipticcurve.Point, other: int) -> ecdsa.ellipticcurve.Point:
|
|
||||||
if self.curve() != curve_secp256k1:
|
|
||||||
# this operation is not on the secp256k1 curve; use original implementation
|
|
||||||
return _patched_functions.orig_mul(self, other)
|
|
||||||
other %= curve_order
|
|
||||||
if self == point_at_infinity or other == 0:
|
|
||||||
return point_at_infinity
|
|
||||||
pubkey = _get_ptr_to_well_formed_pubkey_string_buffer_from_ecdsa_point(self)
|
|
||||||
r = _libsecp256k1.secp256k1_ec_pubkey_tweak_mul(_libsecp256k1.ctx, pubkey, other.to_bytes(32, byteorder="big"))
|
|
||||||
if not r:
|
|
||||||
return point_at_infinity
|
|
||||||
return _get_ecdsa_point_from_libsecp256k1_pubkey_object(pubkey)
|
|
||||||
|
|
||||||
def sign(self: ecdsa.ecdsa.Private_key, hash: int, random_k: int) -> ecdsa.ecdsa.Signature:
|
|
||||||
# note: random_k is ignored
|
|
||||||
if self.public_key.curve != curve_secp256k1:
|
|
||||||
# this operation is not on the secp256k1 curve; use original implementation
|
|
||||||
return _patched_functions.orig_sign(self, hash, random_k)
|
|
||||||
secret_exponent = self.secret_multiplier
|
|
||||||
nonce_function = None
|
|
||||||
sig = create_string_buffer(64)
|
|
||||||
sig_hash_bytes = hash.to_bytes(32, byteorder="big")
|
|
||||||
def sign_with_extra_entropy(extra_entropy):
|
|
||||||
ret = _libsecp256k1.secp256k1_ecdsa_sign(
|
|
||||||
_libsecp256k1.ctx, sig, sig_hash_bytes, secret_exponent.to_bytes(32, byteorder="big"),
|
|
||||||
nonce_function, extra_entropy)
|
|
||||||
if not ret:
|
|
||||||
raise Exception('the nonce generation function failed, or the private key was invalid')
|
|
||||||
compact_signature = create_string_buffer(64)
|
|
||||||
_libsecp256k1.secp256k1_ecdsa_signature_serialize_compact(_libsecp256k1.ctx, compact_signature, sig)
|
|
||||||
r = int.from_bytes(compact_signature[:32], byteorder="big")
|
|
||||||
s = int.from_bytes(compact_signature[32:], byteorder="big")
|
|
||||||
return r, s
|
|
||||||
|
|
||||||
r, s = sign_with_extra_entropy(extra_entropy=None)
|
|
||||||
counter = 0
|
|
||||||
while r >= 2**255: # grind for low R value https://github.com/bitcoin/bitcoin/pull/13666
|
|
||||||
counter += 1
|
|
||||||
extra_entropy = counter.to_bytes(32, byteorder="little")
|
|
||||||
r, s = sign_with_extra_entropy(extra_entropy=extra_entropy)
|
|
||||||
return ecdsa.ecdsa.Signature(r, s)
|
|
||||||
|
|
||||||
def verify(self: ecdsa.ecdsa.Public_key, hash: int, signature: ecdsa.ecdsa.Signature) -> bool:
|
|
||||||
if self.curve != curve_secp256k1:
|
|
||||||
# this operation is not on the secp256k1 curve; use original implementation
|
|
||||||
return _patched_functions.orig_verify(self, hash, signature)
|
|
||||||
sig = create_string_buffer(64)
|
|
||||||
input64 = signature.r.to_bytes(32, byteorder="big") + signature.s.to_bytes(32, byteorder="big")
|
|
||||||
r = _libsecp256k1.secp256k1_ecdsa_signature_parse_compact(_libsecp256k1.ctx, sig, input64)
|
|
||||||
if not r:
|
|
||||||
return False
|
|
||||||
r = _libsecp256k1.secp256k1_ecdsa_signature_normalize(_libsecp256k1.ctx, sig, sig)
|
|
||||||
|
|
||||||
public_pair_bytes = b'\4' + self.point.x().to_bytes(32, byteorder="big") + self.point.y().to_bytes(32, byteorder="big")
|
|
||||||
pubkey = create_string_buffer(64)
|
|
||||||
r = _libsecp256k1.secp256k1_ec_pubkey_parse(
|
|
||||||
_libsecp256k1.ctx, pubkey, public_pair_bytes, len(public_pair_bytes))
|
|
||||||
if not r:
|
|
||||||
return False
|
|
||||||
|
|
||||||
return 1 == _libsecp256k1.secp256k1_ecdsa_verify(_libsecp256k1.ctx, sig, hash.to_bytes(32, byteorder="big"), pubkey)
|
|
||||||
|
|
||||||
# save new functions so that we can (re-)do patching
|
|
||||||
_patched_functions.fast_sign = sign
|
|
||||||
_patched_functions.fast_verify = verify
|
|
||||||
_patched_functions.fast_mul = mul
|
|
||||||
_patched_functions.fast_add = add
|
|
||||||
|
|
||||||
_patched_functions.prepared_to_patch = True
|
|
||||||
|
|
||||||
|
|
||||||
def do_monkey_patching_of_python_ecdsa_internals_with_libsecp256k1():
|
|
||||||
if not _libsecp256k1:
|
|
||||||
# FIXME logging 'verbosity' is not yet initialised
|
|
||||||
_logger.info('libsecp256k1 library not available, falling back to python-ecdsa. '
|
|
||||||
'This means signing operations will be slower.')
|
|
||||||
return
|
|
||||||
if not _patched_functions.prepared_to_patch:
|
|
||||||
raise Exception("can't patch python-ecdsa without preparations")
|
|
||||||
ecdsa.ecdsa.Private_key.sign = _patched_functions.fast_sign
|
|
||||||
ecdsa.ecdsa.Public_key.verifies = _patched_functions.fast_verify
|
|
||||||
ecdsa.ellipticcurve.Point.__mul__ = _patched_functions.fast_mul
|
|
||||||
ecdsa.ellipticcurve.Point.__add__ = _patched_functions.fast_add
|
|
||||||
|
|
||||||
_patched_functions.monkey_patching_active = True
|
|
||||||
|
|
||||||
|
|
||||||
def undo_monkey_patching_of_python_ecdsa_internals_with_libsecp256k1():
|
|
||||||
if not _libsecp256k1:
|
|
||||||
return
|
|
||||||
if not _patched_functions.prepared_to_patch:
|
|
||||||
raise Exception("can't patch python-ecdsa without preparations")
|
|
||||||
ecdsa.ecdsa.Private_key.sign = _patched_functions.orig_sign
|
|
||||||
ecdsa.ecdsa.Public_key.verifies = _patched_functions.orig_verify
|
|
||||||
ecdsa.ellipticcurve.Point.__mul__ = _patched_functions.orig_mul
|
|
||||||
ecdsa.ellipticcurve.Point.__add__ = _patched_functions.orig_add
|
|
||||||
|
|
||||||
_patched_functions.monkey_patching_active = False
|
|
||||||
|
|
||||||
|
|
||||||
def is_using_fast_ecc():
|
|
||||||
return _patched_functions.monkey_patching_active
|
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
_libsecp256k1 = load_library()
|
_libsecp256k1 = load_library()
|
||||||
except BaseException as e:
|
except BaseException as e:
|
||||||
_logger.warning(f'failed to load libsecp256k1: {repr(e)}')
|
_logger.error(f'failed to load libsecp256k1: {repr(e)}')
|
||||||
_libsecp256k1 = None
|
|
||||||
|
|
||||||
_prepare_monkey_patching_of_python_ecdsa_internals_with_libsecp256k1()
|
|
||||||
|
if _libsecp256k1 is None:
|
||||||
|
# hard fail:
|
||||||
|
sys.exit(f"Error: Failed to load libsecp256k1.")
|
||||||
|
|
|
@ -36,7 +36,7 @@ from .bitcoin import deserialize_privkey, serialize_privkey
|
||||||
from .bip32 import (convert_bip32_path_to_list_of_uint32, BIP32_PRIME,
|
from .bip32 import (convert_bip32_path_to_list_of_uint32, BIP32_PRIME,
|
||||||
is_xpub, is_xprv, BIP32Node, normalize_bip32_derivation,
|
is_xpub, is_xprv, BIP32Node, normalize_bip32_derivation,
|
||||||
convert_bip32_intpath_to_strpath)
|
convert_bip32_intpath_to_strpath)
|
||||||
from .ecc import string_to_number, number_to_string
|
from .ecc import string_to_number
|
||||||
from .crypto import (pw_decode, pw_encode, sha256, sha256d, PW_HASH_VERSION_LATEST,
|
from .crypto import (pw_decode, pw_encode, sha256, sha256d, PW_HASH_VERSION_LATEST,
|
||||||
SUPPORTED_PW_HASH_VERSIONS, UnsupportedPasswordHashVersion, hash_160)
|
SUPPORTED_PW_HASH_VERSIONS, UnsupportedPasswordHashVersion, hash_160)
|
||||||
from .util import (InvalidPassword, WalletFileException,
|
from .util import (InvalidPassword, WalletFileException,
|
||||||
|
@ -615,7 +615,7 @@ class Old_KeyStore(MasterPublicKeyMixin, Deterministic_KeyStore):
|
||||||
def get_pubkey_from_mpk(cls, mpk, for_change, n) -> bytes:
|
def get_pubkey_from_mpk(cls, mpk, for_change, n) -> bytes:
|
||||||
z = cls.get_sequence(mpk, for_change, n)
|
z = cls.get_sequence(mpk, for_change, n)
|
||||||
master_public_key = ecc.ECPubkey(bfh('04'+mpk))
|
master_public_key = ecc.ECPubkey(bfh('04'+mpk))
|
||||||
public_key = master_public_key + z*ecc.generator()
|
public_key = master_public_key + z*ecc.GENERATOR
|
||||||
return public_key.get_public_key_bytes(compressed=False)
|
return public_key.get_public_key_bytes(compressed=False)
|
||||||
|
|
||||||
@lru_cache(maxsize=None)
|
@lru_cache(maxsize=None)
|
||||||
|
@ -626,7 +626,7 @@ class Old_KeyStore(MasterPublicKeyMixin, Deterministic_KeyStore):
|
||||||
|
|
||||||
def _get_private_key_from_stretched_exponent(self, for_change, n, secexp):
|
def _get_private_key_from_stretched_exponent(self, for_change, n, secexp):
|
||||||
secexp = (secexp + self.get_sequence(self.mpk, for_change, n)) % ecc.CURVE_ORDER
|
secexp = (secexp + self.get_sequence(self.mpk, for_change, n)) % ecc.CURVE_ORDER
|
||||||
pk = number_to_string(secexp, ecc.CURVE_ORDER)
|
pk = int.to_bytes(secexp, length=32, byteorder='big', signed=False)
|
||||||
return pk
|
return pk
|
||||||
|
|
||||||
def get_private_key(self, sequence: Sequence[int], password):
|
def get_private_key(self, sequence: Sequence[int], password):
|
||||||
|
|
|
@ -1039,7 +1039,7 @@ class Peer(Logger):
|
||||||
timestamp=now.to_bytes(4, byteorder="big"),
|
timestamp=now.to_bytes(4, byteorder="big"),
|
||||||
)
|
)
|
||||||
sighash = sha256d(chan_upd[2 + 64:])
|
sighash = sha256d(chan_upd[2 + 64:])
|
||||||
sig = ecc.ECPrivkey(self.privkey).sign(sighash, sig_string_from_r_and_s, get_r_and_s_from_sig_string)
|
sig = ecc.ECPrivkey(self.privkey).sign(sighash, sig_string_from_r_and_s)
|
||||||
message_type, payload = decode_msg(chan_upd)
|
message_type, payload = decode_msg(chan_upd)
|
||||||
payload['signature'] = sig
|
payload['signature'] = sig
|
||||||
chan_upd = encode_msg(message_type, **payload)
|
chan_upd = encode_msg(message_type, **payload)
|
||||||
|
@ -1071,8 +1071,8 @@ class Peer(Logger):
|
||||||
)
|
)
|
||||||
to_hash = chan_ann[256+2:]
|
to_hash = chan_ann[256+2:]
|
||||||
h = sha256d(to_hash)
|
h = sha256d(to_hash)
|
||||||
bitcoin_signature = ecc.ECPrivkey(chan.config[LOCAL].multisig_key.privkey).sign(h, sig_string_from_r_and_s, get_r_and_s_from_sig_string)
|
bitcoin_signature = ecc.ECPrivkey(chan.config[LOCAL].multisig_key.privkey).sign(h, sig_string_from_r_and_s)
|
||||||
node_signature = ecc.ECPrivkey(self.privkey).sign(h, sig_string_from_r_and_s, get_r_and_s_from_sig_string)
|
node_signature = ecc.ECPrivkey(self.privkey).sign(h, sig_string_from_r_and_s)
|
||||||
self.send_message("announcement_signatures",
|
self.send_message("announcement_signatures",
|
||||||
channel_id=chan.channel_id,
|
channel_id=chan.channel_id,
|
||||||
short_channel_id=chan.short_channel_id,
|
short_channel_id=chan.short_channel_id,
|
||||||
|
|
|
@ -254,7 +254,7 @@ def privkey_to_pubkey(priv: bytes) -> bytes:
|
||||||
return ecc.ECPrivkey(priv[:32]).get_public_key_bytes()
|
return ecc.ECPrivkey(priv[:32]).get_public_key_bytes()
|
||||||
|
|
||||||
def derive_pubkey(basepoint: bytes, per_commitment_point: bytes) -> bytes:
|
def derive_pubkey(basepoint: bytes, per_commitment_point: bytes) -> bytes:
|
||||||
p = ecc.ECPubkey(basepoint) + ecc.generator() * ecc.string_to_number(sha256(per_commitment_point + basepoint))
|
p = ecc.ECPubkey(basepoint) + ecc.GENERATOR * ecc.string_to_number(sha256(per_commitment_point + basepoint))
|
||||||
return p.get_public_key_bytes()
|
return p.get_public_key_bytes()
|
||||||
|
|
||||||
def derive_privkey(secret: int, per_commitment_point: bytes) -> int:
|
def derive_privkey(secret: int, per_commitment_point: bytes) -> int:
|
||||||
|
@ -275,7 +275,7 @@ def derive_blinded_privkey(basepoint_secret: bytes, per_commitment_secret: bytes
|
||||||
k1 = ecc.string_to_number(basepoint_secret) * ecc.string_to_number(sha256(basepoint + per_commitment_point))
|
k1 = ecc.string_to_number(basepoint_secret) * ecc.string_to_number(sha256(basepoint + per_commitment_point))
|
||||||
k2 = ecc.string_to_number(per_commitment_secret) * ecc.string_to_number(sha256(per_commitment_point + basepoint))
|
k2 = ecc.string_to_number(per_commitment_secret) * ecc.string_to_number(sha256(per_commitment_point + basepoint))
|
||||||
sum = (k1 + k2) % ecc.CURVE_ORDER
|
sum = (k1 + k2) % ecc.CURVE_ORDER
|
||||||
return ecc.number_to_string(sum, CURVE_ORDER)
|
return int.to_bytes(sum, length=32, byteorder='big', signed=False)
|
||||||
|
|
||||||
|
|
||||||
def make_htlc_tx_output(amount_msat, local_feerate, revocationpubkey, local_delayedpubkey, success, to_self_delay):
|
def make_htlc_tx_output(amount_msat, local_feerate, revocationpubkey, local_delayedpubkey, success, to_self_delay):
|
||||||
|
|
|
@ -40,7 +40,6 @@ from .lntransport import LNTransport, LNResponderTransport
|
||||||
from .lnpeer import Peer, LN_P2P_NETWORK_TIMEOUT
|
from .lnpeer import Peer, LN_P2P_NETWORK_TIMEOUT
|
||||||
from .lnaddr import lnencode, LnAddr, lndecode
|
from .lnaddr import lnencode, LnAddr, lndecode
|
||||||
from .ecc import der_sig_from_sig_string
|
from .ecc import der_sig_from_sig_string
|
||||||
from .ecc_fast import is_using_fast_ecc
|
|
||||||
from .lnchannel import Channel
|
from .lnchannel import Channel
|
||||||
from .lnchannel import channel_states, peer_states
|
from .lnchannel import channel_states, peer_states
|
||||||
from . import lnutil
|
from . import lnutil
|
||||||
|
@ -303,7 +302,6 @@ class LNGossip(LNWorker):
|
||||||
self.localfeatures |= LnLocalFeatures.GOSSIP_QUERIES_OPT
|
self.localfeatures |= LnLocalFeatures.GOSSIP_QUERIES_OPT
|
||||||
self.localfeatures |= LnLocalFeatures.GOSSIP_QUERIES_REQ
|
self.localfeatures |= LnLocalFeatures.GOSSIP_QUERIES_REQ
|
||||||
self.unknown_ids = set()
|
self.unknown_ids = set()
|
||||||
assert is_using_fast_ecc(), "verifying LN gossip msgs without libsecp256k1 is hopeless"
|
|
||||||
|
|
||||||
def start_network(self, network: 'Network'):
|
def start_network(self, network: 'Network'):
|
||||||
assert network
|
assert network
|
||||||
|
|
|
@ -28,9 +28,7 @@ import hashlib
|
||||||
import unicodedata
|
import unicodedata
|
||||||
import string
|
import string
|
||||||
|
|
||||||
import ecdsa
|
from .util import resource_path, bfh, bh2u, randrange
|
||||||
|
|
||||||
from .util import resource_path, bfh, bh2u
|
|
||||||
from .crypto import hmac_oneshot
|
from .crypto import hmac_oneshot
|
||||||
from . import version
|
from . import version
|
||||||
from .logging import Logger
|
from .logging import Logger
|
||||||
|
@ -180,7 +178,7 @@ class Mnemonic(Logger):
|
||||||
entropy = 1
|
entropy = 1
|
||||||
while entropy < pow(2, n - bpw):
|
while entropy < pow(2, n - bpw):
|
||||||
# try again if seed would not contain enough words
|
# try again if seed would not contain enough words
|
||||||
entropy = ecdsa.util.randrange(pow(2, n))
|
entropy = randrange(pow(2, n))
|
||||||
nonce = 0
|
nonce = 0
|
||||||
while True:
|
while True:
|
||||||
nonce += 1
|
nonce += 1
|
||||||
|
|
|
@ -1,94 +0,0 @@
|
||||||
# from http://eli.thegreenplace.net/2009/03/07/computing-modular-square-roots-in-python/
|
|
||||||
|
|
||||||
def modular_sqrt(a, p):
|
|
||||||
""" Find a quadratic residue (mod p) of 'a'. p
|
|
||||||
must be an odd prime.
|
|
||||||
|
|
||||||
Solve the congruence of the form:
|
|
||||||
x^2 = a (mod p)
|
|
||||||
And returns x. Note that p - x is also a root.
|
|
||||||
|
|
||||||
0 is returned is no square root exists for
|
|
||||||
these a and p.
|
|
||||||
|
|
||||||
The Tonelli-Shanks algorithm is used (except
|
|
||||||
for some simple cases in which the solution
|
|
||||||
is known from an identity). This algorithm
|
|
||||||
runs in polynomial time (unless the
|
|
||||||
generalized Riemann hypothesis is false).
|
|
||||||
"""
|
|
||||||
# Simple cases
|
|
||||||
#
|
|
||||||
if legendre_symbol(a, p) != 1:
|
|
||||||
return 0
|
|
||||||
elif a == 0:
|
|
||||||
return 0
|
|
||||||
elif p == 2:
|
|
||||||
return p
|
|
||||||
elif p % 4 == 3:
|
|
||||||
return pow(a, (p + 1) // 4, p)
|
|
||||||
|
|
||||||
# Partition p-1 to s * 2^e for an odd s (i.e.
|
|
||||||
# reduce all the powers of 2 from p-1)
|
|
||||||
#
|
|
||||||
s = p - 1
|
|
||||||
e = 0
|
|
||||||
while s % 2 == 0:
|
|
||||||
s //= 2
|
|
||||||
e += 1
|
|
||||||
|
|
||||||
# Find some 'n' with a legendre symbol n|p = -1.
|
|
||||||
# Shouldn't take long.
|
|
||||||
#
|
|
||||||
n = 2
|
|
||||||
while legendre_symbol(n, p) != -1:
|
|
||||||
n += 1
|
|
||||||
|
|
||||||
# Here be dragons!
|
|
||||||
# Read the paper "Square roots from 1; 24, 51,
|
|
||||||
# 10 to Dan Shanks" by Ezra Brown for more
|
|
||||||
# information
|
|
||||||
#
|
|
||||||
|
|
||||||
# x is a guess of the square root that gets better
|
|
||||||
# with each iteration.
|
|
||||||
# b is the "fudge factor" - by how much we're off
|
|
||||||
# with the guess. The invariant x^2 = ab (mod p)
|
|
||||||
# is maintained throughout the loop.
|
|
||||||
# g is used for successive powers of n to update
|
|
||||||
# both a and b
|
|
||||||
# r is the exponent - decreases with each update
|
|
||||||
#
|
|
||||||
x = pow(a, (s + 1) // 2, p)
|
|
||||||
b = pow(a, s, p)
|
|
||||||
g = pow(n, s, p)
|
|
||||||
r = e
|
|
||||||
|
|
||||||
while True:
|
|
||||||
t = b
|
|
||||||
m = 0
|
|
||||||
for m in range(r):
|
|
||||||
if t == 1:
|
|
||||||
break
|
|
||||||
t = pow(t, 2, p)
|
|
||||||
|
|
||||||
if m == 0:
|
|
||||||
return x
|
|
||||||
|
|
||||||
gs = pow(g, 2 ** (r - m - 1), p)
|
|
||||||
g = (gs * gs) % p
|
|
||||||
x = (x * gs) % p
|
|
||||||
b = (b * g) % p
|
|
||||||
r = m
|
|
||||||
|
|
||||||
def legendre_symbol(a, p):
|
|
||||||
""" Compute the Legendre symbol a|p using
|
|
||||||
Euler's criterion. p is a prime, a is
|
|
||||||
relatively prime to p (if p divides
|
|
||||||
a, then a|p = 0)
|
|
||||||
|
|
||||||
Returns 1 if a has a square root modulo
|
|
||||||
p, -1 otherwise.
|
|
||||||
"""
|
|
||||||
ls = pow(a, (p - 1) // 2, p)
|
|
||||||
return -1 if ls == p - 1 else ls
|
|
|
@ -15,8 +15,7 @@ from electrum.bip32 import (BIP32Node, convert_bip32_intpath_to_strpath,
|
||||||
normalize_bip32_derivation, is_all_public_derivation)
|
normalize_bip32_derivation, is_all_public_derivation)
|
||||||
from electrum.crypto import sha256d, SUPPORTED_PW_HASH_VERSIONS
|
from electrum.crypto import sha256d, SUPPORTED_PW_HASH_VERSIONS
|
||||||
from electrum import ecc, crypto, constants
|
from electrum import ecc, crypto, constants
|
||||||
from electrum.ecc import number_to_string, string_to_number
|
from electrum.util import bfh, bh2u, InvalidPassword, randrange
|
||||||
from electrum.util import bfh, bh2u, InvalidPassword
|
|
||||||
from electrum.storage import WalletStorage
|
from electrum.storage import WalletStorage
|
||||||
from electrum.keystore import xtype_from_derivation
|
from electrum.keystore import xtype_from_derivation
|
||||||
|
|
||||||
|
@ -33,31 +32,6 @@ except ImportError:
|
||||||
sys.exit("Error: python-ecdsa does not seem to be installed. Try 'sudo python3 -m pip install ecdsa'")
|
sys.exit("Error: python-ecdsa does not seem to be installed. Try 'sudo python3 -m pip install ecdsa'")
|
||||||
|
|
||||||
|
|
||||||
def needs_test_with_all_ecc_implementations(func):
|
|
||||||
"""Function decorator to run a unit test twice:
|
|
||||||
once when libsecp256k1 is not available, once when it is.
|
|
||||||
|
|
||||||
NOTE: this is inherently sequential;
|
|
||||||
tests running in parallel would break things
|
|
||||||
"""
|
|
||||||
def run_test(*args, **kwargs):
|
|
||||||
if FAST_TESTS: # if set, only run tests once, using fastest implementation
|
|
||||||
func(*args, **kwargs)
|
|
||||||
return
|
|
||||||
ecc_fast.undo_monkey_patching_of_python_ecdsa_internals_with_libsecp256k1()
|
|
||||||
try:
|
|
||||||
# first test without libsecp
|
|
||||||
func(*args, **kwargs)
|
|
||||||
finally:
|
|
||||||
ecc_fast.do_monkey_patching_of_python_ecdsa_internals_with_libsecp256k1()
|
|
||||||
# if libsecp is not available, we are done
|
|
||||||
if not ecc_fast._libsecp256k1:
|
|
||||||
return
|
|
||||||
# if libsecp is available, test again now
|
|
||||||
func(*args, **kwargs)
|
|
||||||
return run_test
|
|
||||||
|
|
||||||
|
|
||||||
def needs_test_with_all_aes_implementations(func):
|
def needs_test_with_all_aes_implementations(func):
|
||||||
"""Function decorator to run a unit test twice:
|
"""Function decorator to run a unit test twice:
|
||||||
once when pycryptodomex is not available, once when it is.
|
once when pycryptodomex is not available, once when it is.
|
||||||
|
@ -95,15 +69,14 @@ class Test_bitcoin(ElectrumTestCase):
|
||||||
self.assertTrue(bool(crypto.AES))
|
self.assertTrue(bool(crypto.AES))
|
||||||
|
|
||||||
@needs_test_with_all_aes_implementations
|
@needs_test_with_all_aes_implementations
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
def test_crypto(self):
|
def test_crypto(self):
|
||||||
for message in [b"Chancellor on brink of second bailout for banks", b'\xff'*512]:
|
for message in [b"Chancellor on brink of second bailout for banks", b'\xff'*512]:
|
||||||
self._do_test_crypto(message)
|
self._do_test_crypto(message)
|
||||||
|
|
||||||
def _do_test_crypto(self, message):
|
def _do_test_crypto(self, message):
|
||||||
G = ecc.generator()
|
G = ecc.GENERATOR
|
||||||
_r = G.order()
|
_r = G.order()
|
||||||
pvk = ecdsa.util.randrange(_r)
|
pvk = randrange(_r)
|
||||||
|
|
||||||
Pub = pvk*G
|
Pub = pvk*G
|
||||||
pubkey_c = Pub.get_public_key_bytes(True)
|
pubkey_c = Pub.get_public_key_bytes(True)
|
||||||
|
@ -111,7 +84,7 @@ class Test_bitcoin(ElectrumTestCase):
|
||||||
addr_c = public_key_to_p2pkh(pubkey_c)
|
addr_c = public_key_to_p2pkh(pubkey_c)
|
||||||
|
|
||||||
#print "Private key ", '%064x'%pvk
|
#print "Private key ", '%064x'%pvk
|
||||||
eck = ecc.ECPrivkey(number_to_string(pvk,_r))
|
eck = ecc.ECPrivkey.from_secret_scalar(pvk)
|
||||||
|
|
||||||
#print "Compressed public key ", pubkey_c.encode('hex')
|
#print "Compressed public key ", pubkey_c.encode('hex')
|
||||||
enc = ecc.ECPubkey(pubkey_c).encrypt_message(message)
|
enc = ecc.ECPubkey(pubkey_c).encrypt_message(message)
|
||||||
|
@ -127,13 +100,12 @@ class Test_bitcoin(ElectrumTestCase):
|
||||||
#print signature
|
#print signature
|
||||||
eck.verify_message_for_address(signature, message)
|
eck.verify_message_for_address(signature, message)
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
def test_ecc_sanity(self):
|
def test_ecc_sanity(self):
|
||||||
G = ecc.generator()
|
G = ecc.GENERATOR
|
||||||
n = G.order()
|
n = G.order()
|
||||||
self.assertEqual(ecc.CURVE_ORDER, n)
|
self.assertEqual(ecc.CURVE_ORDER, n)
|
||||||
inf = n * G
|
inf = n * G
|
||||||
self.assertEqual(ecc.point_at_infinity(), inf)
|
self.assertEqual(ecc.POINT_AT_INFINITY, inf)
|
||||||
self.assertTrue(inf.is_at_infinity())
|
self.assertTrue(inf.is_at_infinity())
|
||||||
self.assertFalse(G.is_at_infinity())
|
self.assertFalse(G.is_at_infinity())
|
||||||
self.assertEqual(11 * G, 7 * G + 4 * G)
|
self.assertEqual(11 * G, 7 * G + 4 * G)
|
||||||
|
@ -155,7 +127,6 @@ class Test_bitcoin(ElectrumTestCase):
|
||||||
self.assertEqual(2 * G, inf + 2 * G)
|
self.assertEqual(2 * G, inf + 2 * G)
|
||||||
self.assertEqual(inf, 3 * G + (-3 * G))
|
self.assertEqual(inf, 3 * G + (-3 * G))
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
def test_msg_signing(self):
|
def test_msg_signing(self):
|
||||||
msg1 = b'Chancellor on brink of second bailout for banks'
|
msg1 = b'Chancellor on brink of second bailout for banks'
|
||||||
msg2 = b'Electrum'
|
msg2 = b'Electrum'
|
||||||
|
@ -185,7 +156,6 @@ class Test_bitcoin(ElectrumTestCase):
|
||||||
self.assertFalse(ecc.verify_message_with_address(addr1, sig2, msg1))
|
self.assertFalse(ecc.verify_message_with_address(addr1, sig2, msg1))
|
||||||
|
|
||||||
@needs_test_with_all_aes_implementations
|
@needs_test_with_all_aes_implementations
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
def test_decrypt_message(self):
|
def test_decrypt_message(self):
|
||||||
key = WalletStorage.get_eckey_from_password('pw123')
|
key = WalletStorage.get_eckey_from_password('pw123')
|
||||||
self.assertEqual(b'me<(s_s)>age', key.decrypt_message(b'QklFMQMDFtgT3zWSQsa+Uie8H/WvfUjlu9UN9OJtTt3KlgKeSTi6SQfuhcg1uIz9hp3WIUOFGTLr4RNQBdjPNqzXwhkcPi2Xsbiw6UCNJncVPJ6QBg=='))
|
self.assertEqual(b'me<(s_s)>age', key.decrypt_message(b'QklFMQMDFtgT3zWSQsa+Uie8H/WvfUjlu9UN9OJtTt3KlgKeSTi6SQfuhcg1uIz9hp3WIUOFGTLr4RNQBdjPNqzXwhkcPi2Xsbiw6UCNJncVPJ6QBg=='))
|
||||||
|
@ -193,7 +163,6 @@ class Test_bitcoin(ElectrumTestCase):
|
||||||
self.assertEqual(b'hey_there' * 100, key.decrypt_message(b'QklFMQLOOsabsXtGQH8edAa6VOUa5wX8/DXmxX9NyHoAx1a5bWgllayGRVPeI2bf0ZdWK0tfal0ap0ZIVKbd2eOJybqQkILqT6E1/Syzq0Zicyb/AA1eZNkcX5y4gzloxinw00ubCA8M7gcUjJpOqbnksATcJ5y2YYXcHMGGfGurWu6uJ/UyrNobRidWppRMW5yR9/6utyNvT6OHIolCMEf7qLcmtneoXEiz51hkRdZS7weNf9mGqSbz9a2NL3sdh1A0feHIjAZgcCKcAvksNUSauf0/FnIjzTyPRpjRDMeDC8Ci3sGiuO3cvpWJwhZfbjcS26KmBv2CHWXfRRNFYOInHZNIXWNAoBB47Il5bGSMd+uXiGr+SQ9tNvcu+BiJNmFbxYqg+oQ8dGAl1DtvY2wJVY8k7vO9BIWSpyIxfGw7EDifhc5vnOmGe016p6a01C3eVGxgl23UYMrP7+fpjOcPmTSF4rk5U5ljEN3MSYqlf1QEv0OqlI9q1TwTK02VBCjMTYxDHsnt04OjNBkNO8v5uJ4NR+UUDBEp433z53I59uawZ+dbk4v4ZExcl8EGmKm3Gzbal/iJ/F7KQuX2b/ySEhLOFVYFWxK73X1nBvCSK2mC2/8fCw8oI5pmvzJwQhcCKTdEIrz3MMvAHqtPScDUOjzhXxInQOCb3+UBj1PPIdqkYLvZss1TEaBwYZjLkVnK2MBj7BaqT6Rp6+5A/fippUKHsnB6eYMEPR2YgDmCHL+4twxHJG6UWdP3ybaKiiAPy2OHNP6PTZ0HrqHOSJzBSDD+Z8YpaRg29QX3UEWlqnSKaan0VYAsV1VeaN0XFX46/TWO0L5tjhYVXJJYGqo6tIQJymxATLFRF6AZaD1Mwd27IAL04WkmoQoXfO6OFfwdp/shudY/1gBkDBvGPICBPtnqkvhGF+ZF3IRkuPwiFWeXmwBxKHsRx/3+aJu32Ml9+za41zVk2viaxcGqwTc5KMexQFLAUwqhv+aIik7U+5qk/gEVSuRoVkihoweFzKolNF+BknH2oB4rZdPixag5Zje3DvgjsSFlOl69W/67t/Gs8htfSAaHlsB8vWRQr9+v/lxTbrAw+O0E+sYGoObQ4qQMyQshNZEHbpPg63eWiHtJJnrVBvOeIbIHzoLDnMDsWVWZSMzAQ1vhX1H5QLgSEbRlKSliVY03kDkh/Nk/KOn+B2q37Ialq4JcRoIYFGJ8AoYEAD0tRuTqFddIclE75HzwaNG7NyKW1plsa72ciOPwsPJsdd5F0qdSQ3OSKtooTn7uf6dXOc4lDkfrVYRlZ0PX'))
|
self.assertEqual(b'hey_there' * 100, key.decrypt_message(b'QklFMQLOOsabsXtGQH8edAa6VOUa5wX8/DXmxX9NyHoAx1a5bWgllayGRVPeI2bf0ZdWK0tfal0ap0ZIVKbd2eOJybqQkILqT6E1/Syzq0Zicyb/AA1eZNkcX5y4gzloxinw00ubCA8M7gcUjJpOqbnksATcJ5y2YYXcHMGGfGurWu6uJ/UyrNobRidWppRMW5yR9/6utyNvT6OHIolCMEf7qLcmtneoXEiz51hkRdZS7weNf9mGqSbz9a2NL3sdh1A0feHIjAZgcCKcAvksNUSauf0/FnIjzTyPRpjRDMeDC8Ci3sGiuO3cvpWJwhZfbjcS26KmBv2CHWXfRRNFYOInHZNIXWNAoBB47Il5bGSMd+uXiGr+SQ9tNvcu+BiJNmFbxYqg+oQ8dGAl1DtvY2wJVY8k7vO9BIWSpyIxfGw7EDifhc5vnOmGe016p6a01C3eVGxgl23UYMrP7+fpjOcPmTSF4rk5U5ljEN3MSYqlf1QEv0OqlI9q1TwTK02VBCjMTYxDHsnt04OjNBkNO8v5uJ4NR+UUDBEp433z53I59uawZ+dbk4v4ZExcl8EGmKm3Gzbal/iJ/F7KQuX2b/ySEhLOFVYFWxK73X1nBvCSK2mC2/8fCw8oI5pmvzJwQhcCKTdEIrz3MMvAHqtPScDUOjzhXxInQOCb3+UBj1PPIdqkYLvZss1TEaBwYZjLkVnK2MBj7BaqT6Rp6+5A/fippUKHsnB6eYMEPR2YgDmCHL+4twxHJG6UWdP3ybaKiiAPy2OHNP6PTZ0HrqHOSJzBSDD+Z8YpaRg29QX3UEWlqnSKaan0VYAsV1VeaN0XFX46/TWO0L5tjhYVXJJYGqo6tIQJymxATLFRF6AZaD1Mwd27IAL04WkmoQoXfO6OFfwdp/shudY/1gBkDBvGPICBPtnqkvhGF+ZF3IRkuPwiFWeXmwBxKHsRx/3+aJu32Ml9+za41zVk2viaxcGqwTc5KMexQFLAUwqhv+aIik7U+5qk/gEVSuRoVkihoweFzKolNF+BknH2oB4rZdPixag5Zje3DvgjsSFlOl69W/67t/Gs8htfSAaHlsB8vWRQr9+v/lxTbrAw+O0E+sYGoObQ4qQMyQshNZEHbpPg63eWiHtJJnrVBvOeIbIHzoLDnMDsWVWZSMzAQ1vhX1H5QLgSEbRlKSliVY03kDkh/Nk/KOn+B2q37Ialq4JcRoIYFGJ8AoYEAD0tRuTqFddIclE75HzwaNG7NyKW1plsa72ciOPwsPJsdd5F0qdSQ3OSKtooTn7uf6dXOc4lDkfrVYRlZ0PX'))
|
||||||
|
|
||||||
@needs_test_with_all_aes_implementations
|
@needs_test_with_all_aes_implementations
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
def test_encrypt_message(self):
|
def test_encrypt_message(self):
|
||||||
key = WalletStorage.get_eckey_from_password('secret_password77')
|
key = WalletStorage.get_eckey_from_password('secret_password77')
|
||||||
msgs = [
|
msgs = [
|
||||||
|
@ -207,7 +176,6 @@ class Test_bitcoin(ElectrumTestCase):
|
||||||
self.assertEqual(plaintext, key.decrypt_message(ciphertext2))
|
self.assertEqual(plaintext, key.decrypt_message(ciphertext2))
|
||||||
self.assertNotEqual(ciphertext1, ciphertext2)
|
self.assertNotEqual(ciphertext1, ciphertext2)
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
def test_sign_transaction(self):
|
def test_sign_transaction(self):
|
||||||
eckey1 = ecc.ECPrivkey(bfh('7e1255fddb52db1729fc3ceb21a46f95b8d9fe94cc83425e936a6c5223bb679d'))
|
eckey1 = ecc.ECPrivkey(bfh('7e1255fddb52db1729fc3ceb21a46f95b8d9fe94cc83425e936a6c5223bb679d'))
|
||||||
sig1 = eckey1.sign_transaction(bfh('5a548b12369a53faaa7e51b5081829474ebdd9c924b3a8230b69aa0be254cd94'))
|
sig1 = eckey1.sign_transaction(bfh('5a548b12369a53faaa7e51b5081829474ebdd9c924b3a8230b69aa0be254cd94'))
|
||||||
|
@ -423,7 +391,6 @@ class Test_xprv_xpub(ElectrumTestCase):
|
||||||
|
|
||||||
return xpub, xprv
|
return xpub, xprv
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
def test_bip32(self):
|
def test_bip32(self):
|
||||||
# see https://en.bitcoin.it/wiki/BIP_0032_TestVectors
|
# see https://en.bitcoin.it/wiki/BIP_0032_TestVectors
|
||||||
xpub, xprv = self._do_test_bip32("000102030405060708090a0b0c0d0e0f", "m/0'/1/2'/2/1000000000")
|
xpub, xprv = self._do_test_bip32("000102030405060708090a0b0c0d0e0f", "m/0'/1/2'/2/1000000000")
|
||||||
|
@ -434,14 +401,12 @@ class Test_xprv_xpub(ElectrumTestCase):
|
||||||
self.assertEqual("xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt", xpub)
|
self.assertEqual("xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt", xpub)
|
||||||
self.assertEqual("xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j", xprv)
|
self.assertEqual("xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j", xprv)
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
def test_xpub_from_xprv(self):
|
def test_xpub_from_xprv(self):
|
||||||
"""We can derive the xpub key from a xprv."""
|
"""We can derive the xpub key from a xprv."""
|
||||||
for xprv_details in self.xprv_xpub:
|
for xprv_details in self.xprv_xpub:
|
||||||
result = xpub_from_xprv(xprv_details['xprv'])
|
result = xpub_from_xprv(xprv_details['xprv'])
|
||||||
self.assertEqual(result, xprv_details['xpub'])
|
self.assertEqual(result, xprv_details['xpub'])
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
def test_is_xpub(self):
|
def test_is_xpub(self):
|
||||||
for xprv_details in self.xprv_xpub:
|
for xprv_details in self.xprv_xpub:
|
||||||
xpub = xprv_details['xpub']
|
xpub = xprv_details['xpub']
|
||||||
|
@ -449,13 +414,11 @@ class Test_xprv_xpub(ElectrumTestCase):
|
||||||
self.assertFalse(is_xpub('xpub1nval1d'))
|
self.assertFalse(is_xpub('xpub1nval1d'))
|
||||||
self.assertFalse(is_xpub('xpub661MyMwAqRbcFWohJWt7PHsFEJfZAvw9ZxwQoDa4SoMgsDDM1T7WK3u9E4edkC4ugRnZ8E4xDZRpk8Rnts3Nbt97dPwT52WRONGBADWRONG'))
|
self.assertFalse(is_xpub('xpub661MyMwAqRbcFWohJWt7PHsFEJfZAvw9ZxwQoDa4SoMgsDDM1T7WK3u9E4edkC4ugRnZ8E4xDZRpk8Rnts3Nbt97dPwT52WRONGBADWRONG'))
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
def test_xpub_type(self):
|
def test_xpub_type(self):
|
||||||
for xprv_details in self.xprv_xpub:
|
for xprv_details in self.xprv_xpub:
|
||||||
xpub = xprv_details['xpub']
|
xpub = xprv_details['xpub']
|
||||||
self.assertEqual(xprv_details['xtype'], xpub_type(xpub))
|
self.assertEqual(xprv_details['xtype'], xpub_type(xpub))
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
def test_is_xprv(self):
|
def test_is_xprv(self):
|
||||||
for xprv_details in self.xprv_xpub:
|
for xprv_details in self.xprv_xpub:
|
||||||
xprv = xprv_details['xprv']
|
xprv = xprv_details['xprv']
|
||||||
|
@ -680,7 +643,6 @@ class Test_keyImport(ElectrumTestCase):
|
||||||
'scripthash': '5b07ddfde826f5125ee823900749103cea37808038ecead5505a766a07c34445'},
|
'scripthash': '5b07ddfde826f5125ee823900749103cea37808038ecead5505a766a07c34445'},
|
||||||
)
|
)
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
def test_public_key_from_private_key(self):
|
def test_public_key_from_private_key(self):
|
||||||
for priv_details in self.priv_pub_addr:
|
for priv_details in self.priv_pub_addr:
|
||||||
txin_type, privkey, compressed = deserialize_privkey(priv_details['priv'])
|
txin_type, privkey, compressed = deserialize_privkey(priv_details['priv'])
|
||||||
|
@ -689,13 +651,11 @@ class Test_keyImport(ElectrumTestCase):
|
||||||
self.assertEqual(priv_details['txin_type'], txin_type)
|
self.assertEqual(priv_details['txin_type'], txin_type)
|
||||||
self.assertEqual(priv_details['compressed'], compressed)
|
self.assertEqual(priv_details['compressed'], compressed)
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
def test_address_from_private_key(self):
|
def test_address_from_private_key(self):
|
||||||
for priv_details in self.priv_pub_addr:
|
for priv_details in self.priv_pub_addr:
|
||||||
addr2 = address_from_private_key(priv_details['priv'])
|
addr2 = address_from_private_key(priv_details['priv'])
|
||||||
self.assertEqual(priv_details['address'], addr2)
|
self.assertEqual(priv_details['address'], addr2)
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
def test_is_valid_address(self):
|
def test_is_valid_address(self):
|
||||||
for priv_details in self.priv_pub_addr:
|
for priv_details in self.priv_pub_addr:
|
||||||
addr = priv_details['address']
|
addr = priv_details['address']
|
||||||
|
@ -721,7 +681,6 @@ class Test_keyImport(ElectrumTestCase):
|
||||||
self.assertTrue(is_address('bc1qxq64lrwt02hm7tu25lr3hm9tgzh58snfe67yt6'))
|
self.assertTrue(is_address('bc1qxq64lrwt02hm7tu25lr3hm9tgzh58snfe67yt6'))
|
||||||
self.assertFalse(is_address('bc1qxq64lrwt02hm7tu25lr3hm9tgzh58snfe67yt5'))
|
self.assertFalse(is_address('bc1qxq64lrwt02hm7tu25lr3hm9tgzh58snfe67yt5'))
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
def test_is_private_key(self):
|
def test_is_private_key(self):
|
||||||
for priv_details in self.priv_pub_addr:
|
for priv_details in self.priv_pub_addr:
|
||||||
self.assertTrue(is_private_key(priv_details['priv']))
|
self.assertTrue(is_private_key(priv_details['priv']))
|
||||||
|
@ -730,39 +689,33 @@ class Test_keyImport(ElectrumTestCase):
|
||||||
self.assertFalse(is_private_key(priv_details['address']))
|
self.assertFalse(is_private_key(priv_details['address']))
|
||||||
self.assertFalse(is_private_key("not a privkey"))
|
self.assertFalse(is_private_key("not a privkey"))
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
def test_serialize_privkey(self):
|
def test_serialize_privkey(self):
|
||||||
for priv_details in self.priv_pub_addr:
|
for priv_details in self.priv_pub_addr:
|
||||||
txin_type, privkey, compressed = deserialize_privkey(priv_details['priv'])
|
txin_type, privkey, compressed = deserialize_privkey(priv_details['priv'])
|
||||||
priv2 = serialize_privkey(privkey, compressed, txin_type)
|
priv2 = serialize_privkey(privkey, compressed, txin_type)
|
||||||
self.assertEqual(priv_details['exported_privkey'], priv2)
|
self.assertEqual(priv_details['exported_privkey'], priv2)
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
def test_address_to_scripthash(self):
|
def test_address_to_scripthash(self):
|
||||||
for priv_details in self.priv_pub_addr:
|
for priv_details in self.priv_pub_addr:
|
||||||
sh = address_to_scripthash(priv_details['address'])
|
sh = address_to_scripthash(priv_details['address'])
|
||||||
self.assertEqual(priv_details['scripthash'], sh)
|
self.assertEqual(priv_details['scripthash'], sh)
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
def test_is_minikey(self):
|
def test_is_minikey(self):
|
||||||
for priv_details in self.priv_pub_addr:
|
for priv_details in self.priv_pub_addr:
|
||||||
minikey = priv_details['minikey']
|
minikey = priv_details['minikey']
|
||||||
priv = priv_details['priv']
|
priv = priv_details['priv']
|
||||||
self.assertEqual(minikey, is_minikey(priv))
|
self.assertEqual(minikey, is_minikey(priv))
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
def test_is_compressed_privkey(self):
|
def test_is_compressed_privkey(self):
|
||||||
for priv_details in self.priv_pub_addr:
|
for priv_details in self.priv_pub_addr:
|
||||||
self.assertEqual(priv_details['compressed'],
|
self.assertEqual(priv_details['compressed'],
|
||||||
is_compressed_privkey(priv_details['priv']))
|
is_compressed_privkey(priv_details['priv']))
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
def test_segwit_uncompressed_pubkey(self):
|
def test_segwit_uncompressed_pubkey(self):
|
||||||
with self.assertRaises(BitcoinException):
|
with self.assertRaises(BitcoinException):
|
||||||
is_private_key("p2wpkh-p2sh:5JKXxT3wAZHcybJ9YNkuHur9vou6uuAnorBV9A8vVxGNFH5wvTW",
|
is_private_key("p2wpkh-p2sh:5JKXxT3wAZHcybJ9YNkuHur9vou6uuAnorBV9A8vVxGNFH5wvTW",
|
||||||
raise_on_error=True)
|
raise_on_error=True)
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
def test_wif_with_invalid_magic_byte_for_compressed_pubkey(self):
|
def test_wif_with_invalid_magic_byte_for_compressed_pubkey(self):
|
||||||
with self.assertRaises(BitcoinException):
|
with self.assertRaises(BitcoinException):
|
||||||
is_private_key("KwFAa6AumokBD2dVqQLPou42jHiVsvThY1n25HJ8Ji8REf1wxAQb",
|
is_private_key("KwFAa6AumokBD2dVqQLPou42jHiVsvThY1n25HJ8Ji8REf1wxAQb",
|
||||||
|
|
|
@ -3,12 +3,10 @@ import dns
|
||||||
from electrum import dnssec
|
from electrum import dnssec
|
||||||
|
|
||||||
from . import ElectrumTestCase
|
from . import ElectrumTestCase
|
||||||
from .test_bitcoin import needs_test_with_all_ecc_implementations
|
|
||||||
|
|
||||||
|
|
||||||
class TestDnsSec(ElectrumTestCase):
|
class TestDnsSec(ElectrumTestCase):
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
def test_python_validate_rrsig_ecdsa(self):
|
def test_python_validate_rrsig_ecdsa(self):
|
||||||
rrset = dns.rrset.from_text("getmonero.org.", 3599, 1, 48,
|
rrset = dns.rrset.from_text("getmonero.org.", 3599, 1, 48,
|
||||||
"257 3 13 mdsswUyr3DPW132mOi8V9xESWE8jTo0d xCjjnopKl+GqJxpVXckHAeF+KkxLbxIL fDLUT0rAK9iUzy1L53eKGQ==",
|
"257 3 13 mdsswUyr3DPW132mOi8V9xESWE8jTo0d xCjjnopKl+GqJxpVXckHAeF+KkxLbxIL fDLUT0rAK9iUzy1L53eKGQ==",
|
||||||
|
|
|
@ -13,7 +13,6 @@ from electrum.plugins.trustedcoin import trustedcoin
|
||||||
from electrum.plugins.trustedcoin.legacy_tx_format import serialize_tx_in_legacy_format
|
from electrum.plugins.trustedcoin.legacy_tx_format import serialize_tx_in_legacy_format
|
||||||
|
|
||||||
from . import ElectrumTestCase, TestCaseForTestnet
|
from . import ElectrumTestCase, TestCaseForTestnet
|
||||||
from .test_bitcoin import needs_test_with_all_ecc_implementations
|
|
||||||
|
|
||||||
signed_blob = '01000000012a5c9a94fcde98f5581cd00162c60a13936ceb75389ea65bf38633b424eb4031000000006c493046022100a82bbc57a0136751e5433f41cf000b3f1a99c6744775e76ec764fb78c54ee100022100f9e80b7de89de861dc6fb0c1429d5da72c2b6b2ee2406bc9bfb1beedd729d985012102e61d176da16edd1d258a200ad9759ef63adf8e14cd97f53227bae35cdb84d2f6ffffffff0140420f00000000001976a914230ac37834073a42146f11ef8414ae929feaafc388ac00000000'
|
signed_blob = '01000000012a5c9a94fcde98f5581cd00162c60a13936ceb75389ea65bf38633b424eb4031000000006c493046022100a82bbc57a0136751e5433f41cf000b3f1a99c6744775e76ec764fb78c54ee100022100f9e80b7de89de861dc6fb0c1429d5da72c2b6b2ee2406bc9bfb1beedd729d985012102e61d176da16edd1d258a200ad9759ef63adf8e14cd97f53227bae35cdb84d2f6ffffffff0140420f00000000001976a914230ac37834073a42146f11ef8414ae929feaafc388ac00000000'
|
||||||
v2_blob = "0200000001191601a44a81e061502b7bfbc6eaa1cef6d1e6af5308ef96c9342f71dbf4b9b5000000006b483045022100a6d44d0a651790a477e75334adfb8aae94d6612d01187b2c02526e340a7fd6c8022028bdf7a64a54906b13b145cd5dab21a26bd4b85d6044e9b97bceab5be44c2a9201210253e8e0254b0c95776786e40984c1aa32a7d03efa6bdacdea5f421b774917d346feffffff026b20fa04000000001976a914024db2e87dd7cfd0e5f266c5f212e21a31d805a588aca0860100000000001976a91421919b94ae5cefcdf0271191459157cdb41c4cbf88aca6240700"
|
v2_blob = "0200000001191601a44a81e061502b7bfbc6eaa1cef6d1e6af5308ef96c9342f71dbf4b9b5000000006b483045022100a6d44d0a651790a477e75334adfb8aae94d6612d01187b2c02526e340a7fd6c8022028bdf7a64a54906b13b145cd5dab21a26bd4b85d6044e9b97bceab5be44c2a9201210253e8e0254b0c95776786e40984c1aa32a7d03efa6bdacdea5f421b774917d346feffffff026b20fa04000000001976a914024db2e87dd7cfd0e5f266c5f212e21a31d805a588aca0860100000000001976a91421919b94ae5cefcdf0271191459157cdb41c4cbf88aca6240700"
|
||||||
|
@ -64,7 +63,6 @@ class TestBCDataStream(ElectrumTestCase):
|
||||||
|
|
||||||
class TestTransaction(ElectrumTestCase):
|
class TestTransaction(ElectrumTestCase):
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
def test_tx_update_signatures(self):
|
def test_tx_update_signatures(self):
|
||||||
tx = tx_from_any("cHNidP8BAFUBAAAAASpcmpT83pj1WBzQAWLGChOTbOt1OJ6mW/OGM7Qk60AxAAAAAAD/////AUBCDwAAAAAAGXapFCMKw3g0BzpCFG8R74QUrpKf6q/DiKwAAAAAAAAA")
|
tx = tx_from_any("cHNidP8BAFUBAAAAASpcmpT83pj1WBzQAWLGChOTbOt1OJ6mW/OGM7Qk60AxAAAAAAD/////AUBCDwAAAAAAGXapFCMKw3g0BzpCFG8R74QUrpKf6q/DiKwAAAAAAAAA")
|
||||||
tx.inputs()[0].script_type = 'p2pkh'
|
tx.inputs()[0].script_type = 'p2pkh'
|
||||||
|
@ -73,7 +71,6 @@ class TestTransaction(ElectrumTestCase):
|
||||||
tx.update_signatures(signed_blob_signatures)
|
tx.update_signatures(signed_blob_signatures)
|
||||||
self.assertEqual(tx.serialize(), signed_blob)
|
self.assertEqual(tx.serialize(), signed_blob)
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
def test_tx_deserialize_for_signed_network_tx(self):
|
def test_tx_deserialize_for_signed_network_tx(self):
|
||||||
tx = transaction.Transaction(signed_blob)
|
tx = transaction.Transaction(signed_blob)
|
||||||
tx.deserialize()
|
tx.deserialize()
|
||||||
|
|
|
@ -18,7 +18,6 @@ from electrum.plugins.trustedcoin import trustedcoin
|
||||||
|
|
||||||
from . import TestCaseForTestnet
|
from . import TestCaseForTestnet
|
||||||
from . import ElectrumTestCase
|
from . import ElectrumTestCase
|
||||||
from .test_bitcoin import needs_test_with_all_ecc_implementations
|
|
||||||
|
|
||||||
|
|
||||||
UNICODE_HORROR_HEX = 'e282bf20f09f988020f09f98882020202020e3818620e38191e3819fe381be20e3828fe3828b2077cda2cda2cd9d68cda16fcda2cda120ccb8cda26bccb5cd9f6eccb4cd98c7ab77ccb8cc9b73cd9820cc80cc8177cd98cda2e1b8a9ccb561d289cca1cda27420cca7cc9568cc816fccb572cd8fccb5726f7273cca120ccb6cda1cda06cc4afccb665cd9fcd9f20ccb6cd9d696ecda220cd8f74cc9568ccb7cca1cd9f6520cd9fcd9f64cc9b61cd9c72cc95cda16bcca2cca820cda168ccb465cd8f61ccb7cca2cca17274cc81cd8f20ccb4ccb7cda0c3b2ccb5ccb666ccb82075cca7cd986ec3adcc9bcd9c63cda2cd8f6fccb7cd8f64ccb8cda265cca1cd9d3fcd9e'
|
UNICODE_HORROR_HEX = 'e282bf20f09f988020f09f98882020202020e3818620e38191e3819fe381be20e3828fe3828b2077cda2cda2cd9d68cda16fcda2cda120ccb8cda26bccb5cd9f6eccb4cd98c7ab77ccb8cc9b73cd9820cc80cc8177cd98cda2e1b8a9ccb561d289cca1cda27420cca7cc9568cc816fccb572cd8fccb5726f7273cca120ccb6cda1cda06cc4afccb665cd9fcd9f20ccb6cd9d696ecda220cd8f74cc9568ccb7cca1cd9f6520cd9fcd9f64cc9b61cd9c72cc95cda16bcca2cca820cda168ccb465cd8f61ccb7cca2cca17274cc81cd8f20ccb4ccb7cda0c3b2ccb5ccb666ccb82075cca7cd986ec3adcc9bcd9c63cda2cd8f6fccb7cd8f64ccb8cda265cca1cd9d3fcd9e'
|
||||||
|
@ -83,7 +82,6 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
self.config = SimpleConfig({'electrum_path': self.electrum_path})
|
self.config = SimpleConfig({'electrum_path': self.electrum_path})
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
||||||
def test_electrum_seed_standard(self, mock_save_db):
|
def test_electrum_seed_standard(self, mock_save_db):
|
||||||
seed_words = 'cycle rocket west magnet parrot shuffle foot correct salt library feed song'
|
seed_words = 'cycle rocket west magnet parrot shuffle foot correct salt library feed song'
|
||||||
|
@ -103,7 +101,6 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
|
||||||
self.assertEqual(w.get_receiving_addresses()[0], '1NNkttn1YvVGdqBW4PR6zvc3Zx3H5owKRf')
|
self.assertEqual(w.get_receiving_addresses()[0], '1NNkttn1YvVGdqBW4PR6zvc3Zx3H5owKRf')
|
||||||
self.assertEqual(w.get_change_addresses()[0], '1KSezYMhAJMWqFbVFB2JshYg69UpmEXR4D')
|
self.assertEqual(w.get_change_addresses()[0], '1KSezYMhAJMWqFbVFB2JshYg69UpmEXR4D')
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
||||||
def test_electrum_seed_segwit(self, mock_save_db):
|
def test_electrum_seed_segwit(self, mock_save_db):
|
||||||
seed_words = 'bitter grass shiver impose acquire brush forget axis eager alone wine silver'
|
seed_words = 'bitter grass shiver impose acquire brush forget axis eager alone wine silver'
|
||||||
|
@ -123,7 +120,6 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
|
||||||
self.assertEqual(w.get_receiving_addresses()[0], 'bc1q3g5tmkmlvxryhh843v4dz026avatc0zzr6h3af')
|
self.assertEqual(w.get_receiving_addresses()[0], 'bc1q3g5tmkmlvxryhh843v4dz026avatc0zzr6h3af')
|
||||||
self.assertEqual(w.get_change_addresses()[0], 'bc1qdy94n2q5qcp0kg7v9yzwe6wvfkhnvyzje7nx2p')
|
self.assertEqual(w.get_change_addresses()[0], 'bc1qdy94n2q5qcp0kg7v9yzwe6wvfkhnvyzje7nx2p')
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
||||||
def test_electrum_seed_segwit_passphrase(self, mock_save_db):
|
def test_electrum_seed_segwit_passphrase(self, mock_save_db):
|
||||||
seed_words = 'bitter grass shiver impose acquire brush forget axis eager alone wine silver'
|
seed_words = 'bitter grass shiver impose acquire brush forget axis eager alone wine silver'
|
||||||
|
@ -143,7 +139,6 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
|
||||||
self.assertEqual(w.get_receiving_addresses()[0], 'bc1qx94dutas7ysn2my645cyttujrms5d9p57f6aam')
|
self.assertEqual(w.get_receiving_addresses()[0], 'bc1qx94dutas7ysn2my645cyttujrms5d9p57f6aam')
|
||||||
self.assertEqual(w.get_change_addresses()[0], 'bc1qcywwsy87sdp8vz5rfjh3sxdv6rt95kujdqq38g')
|
self.assertEqual(w.get_change_addresses()[0], 'bc1qcywwsy87sdp8vz5rfjh3sxdv6rt95kujdqq38g')
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
||||||
def test_electrum_seed_old(self, mock_save_db):
|
def test_electrum_seed_old(self, mock_save_db):
|
||||||
seed_words = 'powerful random nobody notice nothing important anyway look away hidden message over'
|
seed_words = 'powerful random nobody notice nothing important anyway look away hidden message over'
|
||||||
|
@ -162,7 +157,6 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
|
||||||
self.assertEqual(w.get_receiving_addresses()[0], '1FJEEB8ihPMbzs2SkLmr37dHyRFzakqUmo')
|
self.assertEqual(w.get_receiving_addresses()[0], '1FJEEB8ihPMbzs2SkLmr37dHyRFzakqUmo')
|
||||||
self.assertEqual(w.get_change_addresses()[0], '1KRW8pH6HFHZh889VDq6fEKvmrsmApwNfe')
|
self.assertEqual(w.get_change_addresses()[0], '1KRW8pH6HFHZh889VDq6fEKvmrsmApwNfe')
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
||||||
def test_electrum_seed_2fa_legacy(self, mock_save_db):
|
def test_electrum_seed_2fa_legacy(self, mock_save_db):
|
||||||
seed_words = 'kiss live scene rude gate step hip quarter bunker oxygen motor glove'
|
seed_words = 'kiss live scene rude gate step hip quarter bunker oxygen motor glove'
|
||||||
|
@ -197,7 +191,6 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
|
||||||
self.assertEqual(w.get_receiving_addresses()[0], '35L8XmCDoEBKeaWRjvmZvoZvhp8BXMMMPV')
|
self.assertEqual(w.get_receiving_addresses()[0], '35L8XmCDoEBKeaWRjvmZvoZvhp8BXMMMPV')
|
||||||
self.assertEqual(w.get_change_addresses()[0], '3PeZEcumRqHSPNN43hd4yskGEBdzXgY8Cy')
|
self.assertEqual(w.get_change_addresses()[0], '3PeZEcumRqHSPNN43hd4yskGEBdzXgY8Cy')
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
||||||
def test_electrum_seed_2fa_segwit(self, mock_save_db):
|
def test_electrum_seed_2fa_segwit(self, mock_save_db):
|
||||||
seed_words = 'universe topic remind silver february ranch shine worth innocent cattle enhance wise'
|
seed_words = 'universe topic remind silver february ranch shine worth innocent cattle enhance wise'
|
||||||
|
@ -232,7 +225,6 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
|
||||||
self.assertEqual(w.get_receiving_addresses()[0], 'bc1qpmufh0zjp5prfsrk2yskcy82sa26srqkd97j0457andc6m0gh5asw7kqd2')
|
self.assertEqual(w.get_receiving_addresses()[0], 'bc1qpmufh0zjp5prfsrk2yskcy82sa26srqkd97j0457andc6m0gh5asw7kqd2')
|
||||||
self.assertEqual(w.get_change_addresses()[0], 'bc1qd4q50nft7kxm9yglfnpup9ed2ukj3tkxp793y0zya8dc9m39jcwq308dxz')
|
self.assertEqual(w.get_change_addresses()[0], 'bc1qd4q50nft7kxm9yglfnpup9ed2ukj3tkxp793y0zya8dc9m39jcwq308dxz')
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
||||||
def test_bip39_seed_bip44_standard(self, mock_save_db):
|
def test_bip39_seed_bip44_standard(self, mock_save_db):
|
||||||
seed_words = 'treat dwarf wealth gasp brass outside high rent blood crowd make initial'
|
seed_words = 'treat dwarf wealth gasp brass outside high rent blood crowd make initial'
|
||||||
|
@ -251,7 +243,6 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
|
||||||
self.assertEqual(w.get_receiving_addresses()[0], '16j7Dqk3Z9DdTdBtHcCVLaNQy9MTgywUUo')
|
self.assertEqual(w.get_receiving_addresses()[0], '16j7Dqk3Z9DdTdBtHcCVLaNQy9MTgywUUo')
|
||||||
self.assertEqual(w.get_change_addresses()[0], '1GG5bVeWgAp5XW7JLCphse14QaC4qiHyWn')
|
self.assertEqual(w.get_change_addresses()[0], '1GG5bVeWgAp5XW7JLCphse14QaC4qiHyWn')
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
||||||
def test_bip39_seed_bip44_standard_passphrase(self, mock_save_db):
|
def test_bip39_seed_bip44_standard_passphrase(self, mock_save_db):
|
||||||
seed_words = 'treat dwarf wealth gasp brass outside high rent blood crowd make initial'
|
seed_words = 'treat dwarf wealth gasp brass outside high rent blood crowd make initial'
|
||||||
|
@ -270,7 +261,6 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
|
||||||
self.assertEqual(w.get_receiving_addresses()[0], '1F88g2naBMhDB7pYFttPWGQgryba3hPevM')
|
self.assertEqual(w.get_receiving_addresses()[0], '1F88g2naBMhDB7pYFttPWGQgryba3hPevM')
|
||||||
self.assertEqual(w.get_change_addresses()[0], '1H4QD1rg2zQJ4UjuAVJr5eW1fEM8WMqyxh')
|
self.assertEqual(w.get_change_addresses()[0], '1H4QD1rg2zQJ4UjuAVJr5eW1fEM8WMqyxh')
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
||||||
def test_bip39_seed_bip49_p2sh_segwit(self, mock_save_db):
|
def test_bip39_seed_bip49_p2sh_segwit(self, mock_save_db):
|
||||||
seed_words = 'treat dwarf wealth gasp brass outside high rent blood crowd make initial'
|
seed_words = 'treat dwarf wealth gasp brass outside high rent blood crowd make initial'
|
||||||
|
@ -289,7 +279,6 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
|
||||||
self.assertEqual(w.get_receiving_addresses()[0], '35ohQTdNykjkF1Mn9nAVEFjupyAtsPAK1W')
|
self.assertEqual(w.get_receiving_addresses()[0], '35ohQTdNykjkF1Mn9nAVEFjupyAtsPAK1W')
|
||||||
self.assertEqual(w.get_change_addresses()[0], '3KaBTcviBLEJajTEMstsA2GWjYoPzPK7Y7')
|
self.assertEqual(w.get_change_addresses()[0], '3KaBTcviBLEJajTEMstsA2GWjYoPzPK7Y7')
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
||||||
def test_bip39_seed_bip84_native_segwit(self, mock_save_db):
|
def test_bip39_seed_bip84_native_segwit(self, mock_save_db):
|
||||||
# test case from bip84
|
# test case from bip84
|
||||||
|
@ -309,7 +298,6 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
|
||||||
self.assertEqual(w.get_receiving_addresses()[0], 'bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu')
|
self.assertEqual(w.get_receiving_addresses()[0], 'bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu')
|
||||||
self.assertEqual(w.get_change_addresses()[0], 'bc1q8c6fshw2dlwun7ekn9qwf37cu2rn755upcp6el')
|
self.assertEqual(w.get_change_addresses()[0], 'bc1q8c6fshw2dlwun7ekn9qwf37cu2rn755upcp6el')
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
||||||
def test_electrum_multisig_seed_standard(self, mock_save_db):
|
def test_electrum_multisig_seed_standard(self, mock_save_db):
|
||||||
seed_words = 'blast uniform dragon fiscal ensure vast young utility dinosaur abandon rookie sure'
|
seed_words = 'blast uniform dragon fiscal ensure vast young utility dinosaur abandon rookie sure'
|
||||||
|
@ -332,7 +320,6 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
|
||||||
self.assertEqual(w.get_receiving_addresses()[0], '32ji3QkAgXNz6oFoRfakyD3ys1XXiERQYN')
|
self.assertEqual(w.get_receiving_addresses()[0], '32ji3QkAgXNz6oFoRfakyD3ys1XXiERQYN')
|
||||||
self.assertEqual(w.get_change_addresses()[0], '36XWwEHrrVCLnhjK5MrVVGmUHghr9oWTN1')
|
self.assertEqual(w.get_change_addresses()[0], '36XWwEHrrVCLnhjK5MrVVGmUHghr9oWTN1')
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
||||||
def test_electrum_multisig_seed_segwit(self, mock_save_db):
|
def test_electrum_multisig_seed_segwit(self, mock_save_db):
|
||||||
seed_words = 'snow nest raise royal more walk demise rotate smooth spirit canyon gun'
|
seed_words = 'snow nest raise royal more walk demise rotate smooth spirit canyon gun'
|
||||||
|
@ -355,7 +342,6 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
|
||||||
self.assertEqual(w.get_receiving_addresses()[0], 'bc1qvzezdcv6vs5h45ugkavp896e0nde5c5lg5h0fwe2xyfhnpkxq6gq7pnwlc')
|
self.assertEqual(w.get_receiving_addresses()[0], 'bc1qvzezdcv6vs5h45ugkavp896e0nde5c5lg5h0fwe2xyfhnpkxq6gq7pnwlc')
|
||||||
self.assertEqual(w.get_change_addresses()[0], 'bc1qxqf840dqswcmu7a8v82fj6ej0msx08flvuy6kngr7axstjcaq6us9hrehd')
|
self.assertEqual(w.get_change_addresses()[0], 'bc1qxqf840dqswcmu7a8v82fj6ej0msx08flvuy6kngr7axstjcaq6us9hrehd')
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
||||||
def test_bip39_multisig_seed_bip45_standard(self, mock_save_db):
|
def test_bip39_multisig_seed_bip45_standard(self, mock_save_db):
|
||||||
seed_words = 'treat dwarf wealth gasp brass outside high rent blood crowd make initial'
|
seed_words = 'treat dwarf wealth gasp brass outside high rent blood crowd make initial'
|
||||||
|
@ -378,7 +364,6 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
|
||||||
self.assertEqual(w.get_receiving_addresses()[0], '3JPTQ2nitVxXBJ1yhMeDwH6q417UifE3bN')
|
self.assertEqual(w.get_receiving_addresses()[0], '3JPTQ2nitVxXBJ1yhMeDwH6q417UifE3bN')
|
||||||
self.assertEqual(w.get_change_addresses()[0], '3FGyDuxgUDn2pSZe5xAJH1yUwSdhzDMyEE')
|
self.assertEqual(w.get_change_addresses()[0], '3FGyDuxgUDn2pSZe5xAJH1yUwSdhzDMyEE')
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
||||||
def test_bip39_multisig_seed_p2sh_segwit(self, mock_save_db):
|
def test_bip39_multisig_seed_p2sh_segwit(self, mock_save_db):
|
||||||
# bip39 seed: pulse mixture jazz invite dune enrich minor weapon mosquito flight fly vapor
|
# bip39 seed: pulse mixture jazz invite dune enrich minor weapon mosquito flight fly vapor
|
||||||
|
@ -400,7 +385,6 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
|
||||||
self.assertEqual(w.get_receiving_addresses()[0], '35LeC45QgCVeRor1tJD6LiDgPbybBXisns')
|
self.assertEqual(w.get_receiving_addresses()[0], '35LeC45QgCVeRor1tJD6LiDgPbybBXisns')
|
||||||
self.assertEqual(w.get_change_addresses()[0], '39RhtDchc6igmx5tyoimhojFL1ZbQBrXa6')
|
self.assertEqual(w.get_change_addresses()[0], '39RhtDchc6igmx5tyoimhojFL1ZbQBrXa6')
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
||||||
def test_bip32_extended_version_bytes(self, mock_save_db):
|
def test_bip32_extended_version_bytes(self, mock_save_db):
|
||||||
seed_words = 'crouch dumb relax small truck age shine pink invite spatial object tenant'
|
seed_words = 'crouch dumb relax small truck age shine pink invite spatial object tenant'
|
||||||
|
@ -488,7 +472,6 @@ class TestWalletKeystoreAddressIntegrityForTestnet(TestCaseForTestnet):
|
||||||
self.assertEqual(w.get_receiving_addresses()[0], '2MzsfTfTGomPRne6TkctMmoDj6LwmVkDrMt')
|
self.assertEqual(w.get_receiving_addresses()[0], '2MzsfTfTGomPRne6TkctMmoDj6LwmVkDrMt')
|
||||||
self.assertEqual(w.get_change_addresses()[0], '2NFp9w8tbYYP9Ze2xQpeYBJQjx3gbXymHX7')
|
self.assertEqual(w.get_change_addresses()[0], '2NFp9w8tbYYP9Ze2xQpeYBJQjx3gbXymHX7')
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
||||||
def test_bip32_extended_version_bytes(self, mock_save_db):
|
def test_bip32_extended_version_bytes(self, mock_save_db):
|
||||||
seed_words = 'crouch dumb relax small truck age shine pink invite spatial object tenant'
|
seed_words = 'crouch dumb relax small truck age shine pink invite spatial object tenant'
|
||||||
|
@ -559,7 +542,6 @@ class TestWalletSending(TestCaseForTestnet):
|
||||||
ks = keystore.from_seed(seed_words, '', False)
|
ks = keystore.from_seed(seed_words, '', False)
|
||||||
return WalletIntegrityHelper.create_standard_wallet(ks, gap_limit=2, config=self.config)
|
return WalletIntegrityHelper.create_standard_wallet(ks, gap_limit=2, config=self.config)
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
||||||
def test_sending_between_p2wpkh_and_compressed_p2pkh(self, mock_save_db):
|
def test_sending_between_p2wpkh_and_compressed_p2pkh(self, mock_save_db):
|
||||||
wallet1 = self.create_standard_wallet_from_seed('bitter grass shiver impose acquire brush forget axis eager alone wine silver')
|
wallet1 = self.create_standard_wallet_from_seed('bitter grass shiver impose acquire brush forget axis eager alone wine silver')
|
||||||
|
@ -616,7 +598,6 @@ class TestWalletSending(TestCaseForTestnet):
|
||||||
self.assertEqual((0, funding_output_value - 250000 - 5000 + 100000, 0), wallet1.get_balance())
|
self.assertEqual((0, funding_output_value - 250000 - 5000 + 100000, 0), wallet1.get_balance())
|
||||||
self.assertEqual((0, 250000 - 5000 - 100000, 0), wallet2.get_balance())
|
self.assertEqual((0, 250000 - 5000 - 100000, 0), wallet2.get_balance())
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
||||||
def test_sending_between_p2sh_2of3_and_uncompressed_p2pkh(self, mock_save_db):
|
def test_sending_between_p2sh_2of3_and_uncompressed_p2pkh(self, mock_save_db):
|
||||||
wallet1a = WalletIntegrityHelper.create_multisig_wallet(
|
wallet1a = WalletIntegrityHelper.create_multisig_wallet(
|
||||||
|
@ -697,7 +678,6 @@ class TestWalletSending(TestCaseForTestnet):
|
||||||
self.assertEqual((0, funding_output_value - 370000 - 5000 + 100000, 0), wallet1a.get_balance())
|
self.assertEqual((0, funding_output_value - 370000 - 5000 + 100000, 0), wallet1a.get_balance())
|
||||||
self.assertEqual((0, 370000 - 5000 - 100000, 0), wallet2.get_balance())
|
self.assertEqual((0, 370000 - 5000 - 100000, 0), wallet2.get_balance())
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
||||||
def test_sending_between_p2wsh_2of3_and_p2wsh_p2sh_2of2(self, mock_save_db):
|
def test_sending_between_p2wsh_2of3_and_p2wsh_p2sh_2of2(self, mock_save_db):
|
||||||
wallet1a = WalletIntegrityHelper.create_multisig_wallet(
|
wallet1a = WalletIntegrityHelper.create_multisig_wallet(
|
||||||
|
@ -807,7 +787,6 @@ class TestWalletSending(TestCaseForTestnet):
|
||||||
self.assertEqual((0, funding_output_value - 165000 - 5000 + 100000, 0), wallet1a.get_balance())
|
self.assertEqual((0, funding_output_value - 165000 - 5000 + 100000, 0), wallet1a.get_balance())
|
||||||
self.assertEqual((0, 165000 - 5000 - 100000, 0), wallet2a.get_balance())
|
self.assertEqual((0, 165000 - 5000 - 100000, 0), wallet2a.get_balance())
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
||||||
def test_sending_between_p2sh_1of2_and_p2wpkh_p2sh(self, mock_save_db):
|
def test_sending_between_p2sh_1of2_and_p2wpkh_p2sh(self, mock_save_db):
|
||||||
wallet1a = WalletIntegrityHelper.create_multisig_wallet(
|
wallet1a = WalletIntegrityHelper.create_multisig_wallet(
|
||||||
|
@ -877,7 +856,6 @@ class TestWalletSending(TestCaseForTestnet):
|
||||||
self.assertEqual((0, funding_output_value - 1000000 - 5000 + 300000, 0), wallet1a.get_balance())
|
self.assertEqual((0, funding_output_value - 1000000 - 5000 + 300000, 0), wallet1a.get_balance())
|
||||||
self.assertEqual((0, 1000000 - 5000 - 300000, 0), wallet2.get_balance())
|
self.assertEqual((0, 1000000 - 5000 - 300000, 0), wallet2.get_balance())
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
||||||
def test_rbf(self, mock_save_db):
|
def test_rbf(self, mock_save_db):
|
||||||
self.maxDiff = None
|
self.maxDiff = None
|
||||||
|
@ -958,7 +936,6 @@ class TestWalletSending(TestCaseForTestnet):
|
||||||
wallet.receive_tx_callback(tx.txid(), tx, TX_HEIGHT_UNCONFIRMED)
|
wallet.receive_tx_callback(tx.txid(), tx, TX_HEIGHT_UNCONFIRMED)
|
||||||
self.assertEqual((0, 7484320, 0), wallet.get_balance())
|
self.assertEqual((0, 7484320, 0), wallet.get_balance())
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
||||||
def test_cpfp_p2pkh(self, mock_save_db):
|
def test_cpfp_p2pkh(self, mock_save_db):
|
||||||
wallet = self.create_standard_wallet_from_seed('fold object utility erase deputy output stadium feed stereo usage modify bean')
|
wallet = self.create_standard_wallet_from_seed('fold object utility erase deputy output stadium feed stereo usage modify bean')
|
||||||
|
@ -1360,7 +1337,6 @@ class TestWalletSending(TestCaseForTestnet):
|
||||||
wallet.receive_tx_callback(tx.txid(), tx, TX_HEIGHT_UNCONFIRMED)
|
wallet.receive_tx_callback(tx.txid(), tx, TX_HEIGHT_UNCONFIRMED)
|
||||||
self.assertEqual((0, 3_900_000, 0), wallet.get_balance())
|
self.assertEqual((0, 3_900_000, 0), wallet.get_balance())
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
||||||
def test_cpfp_p2wpkh(self, mock_save_db):
|
def test_cpfp_p2wpkh(self, mock_save_db):
|
||||||
wallet = self.create_standard_wallet_from_seed('frost repair depend effort salon ring foam oak cancel receive save usage')
|
wallet = self.create_standard_wallet_from_seed('frost repair depend effort salon ring foam oak cancel receive save usage')
|
||||||
|
@ -1394,7 +1370,6 @@ class TestWalletSending(TestCaseForTestnet):
|
||||||
wallet.receive_tx_callback(tx.txid(), tx, TX_HEIGHT_UNCONFIRMED)
|
wallet.receive_tx_callback(tx.txid(), tx, TX_HEIGHT_UNCONFIRMED)
|
||||||
self.assertEqual((0, funding_output_value - 50000, 0), wallet.get_balance())
|
self.assertEqual((0, funding_output_value - 50000, 0), wallet.get_balance())
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
def test_sweep_p2pk(self):
|
def test_sweep_p2pk(self):
|
||||||
|
|
||||||
class NetworkMock:
|
class NetworkMock:
|
||||||
|
@ -1419,7 +1394,6 @@ class TestWalletSending(TestCaseForTestnet):
|
||||||
self.assertEqual('7f827fc5256c274fd1094eb7e020c8ded0baf820356f61aa4f14a9093b0ea0ee', tx_copy.txid())
|
self.assertEqual('7f827fc5256c274fd1094eb7e020c8ded0baf820356f61aa4f14a9093b0ea0ee', tx_copy.txid())
|
||||||
self.assertEqual('7f827fc5256c274fd1094eb7e020c8ded0baf820356f61aa4f14a9093b0ea0ee', tx_copy.wtxid())
|
self.assertEqual('7f827fc5256c274fd1094eb7e020c8ded0baf820356f61aa4f14a9093b0ea0ee', tx_copy.wtxid())
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
||||||
def test_coinjoin_between_two_p2wpkh_electrum_seeds(self, mock_save_db):
|
def test_coinjoin_between_two_p2wpkh_electrum_seeds(self, mock_save_db):
|
||||||
wallet1 = WalletIntegrityHelper.create_standard_wallet(
|
wallet1 = WalletIntegrityHelper.create_standard_wallet(
|
||||||
|
@ -1511,7 +1485,6 @@ class TestWalletOfflineSigning(TestCaseForTestnet):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
self.config = SimpleConfig({'electrum_path': self.electrum_path})
|
self.config = SimpleConfig({'electrum_path': self.electrum_path})
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
||||||
def test_sending_offline_old_electrum_seed_online_mpk(self, mock_save_db):
|
def test_sending_offline_old_electrum_seed_online_mpk(self, mock_save_db):
|
||||||
wallet_offline = WalletIntegrityHelper.create_standard_wallet(
|
wallet_offline = WalletIntegrityHelper.create_standard_wallet(
|
||||||
|
@ -1558,7 +1531,6 @@ class TestWalletOfflineSigning(TestCaseForTestnet):
|
||||||
self.assertEqual('06032230d0bf6a277bc4f8c39e3311a712e0e614626d0dea7cc9f592abfae5d8', tx.txid())
|
self.assertEqual('06032230d0bf6a277bc4f8c39e3311a712e0e614626d0dea7cc9f592abfae5d8', tx.txid())
|
||||||
self.assertEqual('06032230d0bf6a277bc4f8c39e3311a712e0e614626d0dea7cc9f592abfae5d8', tx.wtxid())
|
self.assertEqual('06032230d0bf6a277bc4f8c39e3311a712e0e614626d0dea7cc9f592abfae5d8', tx.wtxid())
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
||||||
def test_sending_offline_xprv_online_xpub_p2pkh(self, mock_save_db):
|
def test_sending_offline_xprv_online_xpub_p2pkh(self, mock_save_db):
|
||||||
wallet_offline = WalletIntegrityHelper.create_standard_wallet(
|
wallet_offline = WalletIntegrityHelper.create_standard_wallet(
|
||||||
|
@ -1604,7 +1576,6 @@ class TestWalletOfflineSigning(TestCaseForTestnet):
|
||||||
self.assertEqual('d9c21696eca80321933e7444ca928aaf25eeda81aaa2f4e5c085d4d0a9cf7aa7', tx.txid())
|
self.assertEqual('d9c21696eca80321933e7444ca928aaf25eeda81aaa2f4e5c085d4d0a9cf7aa7', tx.txid())
|
||||||
self.assertEqual('d9c21696eca80321933e7444ca928aaf25eeda81aaa2f4e5c085d4d0a9cf7aa7', tx.wtxid())
|
self.assertEqual('d9c21696eca80321933e7444ca928aaf25eeda81aaa2f4e5c085d4d0a9cf7aa7', tx.wtxid())
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
||||||
def test_sending_offline_xprv_online_xpub_p2wpkh_p2sh(self, mock_save_db):
|
def test_sending_offline_xprv_online_xpub_p2wpkh_p2sh(self, mock_save_db):
|
||||||
wallet_offline = WalletIntegrityHelper.create_standard_wallet(
|
wallet_offline = WalletIntegrityHelper.create_standard_wallet(
|
||||||
|
@ -1651,7 +1622,6 @@ class TestWalletOfflineSigning(TestCaseForTestnet):
|
||||||
self.assertEqual('3f0d188519237478258ad2bf881643618635d11c2bb95512e830fcf2eda3c522', tx.txid())
|
self.assertEqual('3f0d188519237478258ad2bf881643618635d11c2bb95512e830fcf2eda3c522', tx.txid())
|
||||||
self.assertEqual('27b78ec072a403b0545258e7a1a8d494e4b6fd48bf77f4251a12160c92207cbc', tx.wtxid())
|
self.assertEqual('27b78ec072a403b0545258e7a1a8d494e4b6fd48bf77f4251a12160c92207cbc', tx.wtxid())
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
||||||
def test_sending_offline_xprv_online_xpub_p2wpkh(self, mock_save_db):
|
def test_sending_offline_xprv_online_xpub_p2wpkh(self, mock_save_db):
|
||||||
wallet_offline = WalletIntegrityHelper.create_standard_wallet(
|
wallet_offline = WalletIntegrityHelper.create_standard_wallet(
|
||||||
|
@ -1698,7 +1668,6 @@ class TestWalletOfflineSigning(TestCaseForTestnet):
|
||||||
self.assertEqual('ee76c0c6da87f0eb5ab4d1ae05d3942512dcd3c4c42518f9d3619e74400cfc1f', tx.txid())
|
self.assertEqual('ee76c0c6da87f0eb5ab4d1ae05d3942512dcd3c4c42518f9d3619e74400cfc1f', tx.txid())
|
||||||
self.assertEqual('484e350beaa722a744bb3e2aa38de005baa8526d86536d6143e5814355acf775', tx.wtxid())
|
self.assertEqual('484e350beaa722a744bb3e2aa38de005baa8526d86536d6143e5814355acf775', tx.wtxid())
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
||||||
def test_offline_signing_beyond_gap_limit(self, mock_save_db):
|
def test_offline_signing_beyond_gap_limit(self, mock_save_db):
|
||||||
wallet_offline = WalletIntegrityHelper.create_standard_wallet(
|
wallet_offline = WalletIntegrityHelper.create_standard_wallet(
|
||||||
|
@ -1745,7 +1714,6 @@ class TestWalletOfflineSigning(TestCaseForTestnet):
|
||||||
self.assertEqual('ee76c0c6da87f0eb5ab4d1ae05d3942512dcd3c4c42518f9d3619e74400cfc1f', tx.txid())
|
self.assertEqual('ee76c0c6da87f0eb5ab4d1ae05d3942512dcd3c4c42518f9d3619e74400cfc1f', tx.txid())
|
||||||
self.assertEqual('484e350beaa722a744bb3e2aa38de005baa8526d86536d6143e5814355acf775', tx.wtxid())
|
self.assertEqual('484e350beaa722a744bb3e2aa38de005baa8526d86536d6143e5814355acf775', tx.wtxid())
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
||||||
def test_sending_offline_wif_online_addr_p2pkh(self, mock_save_db): # compressed pubkey
|
def test_sending_offline_wif_online_addr_p2pkh(self, mock_save_db): # compressed pubkey
|
||||||
wallet_offline = WalletIntegrityHelper.create_imported_wallet(privkeys=True, config=self.config)
|
wallet_offline = WalletIntegrityHelper.create_imported_wallet(privkeys=True, config=self.config)
|
||||||
|
@ -1784,7 +1752,6 @@ class TestWalletOfflineSigning(TestCaseForTestnet):
|
||||||
self.assertEqual('e56da664631b8c666c6df38ec80c954c4ac3c4f56f040faf0070e4681e937fc4', tx.txid())
|
self.assertEqual('e56da664631b8c666c6df38ec80c954c4ac3c4f56f040faf0070e4681e937fc4', tx.txid())
|
||||||
self.assertEqual('e56da664631b8c666c6df38ec80c954c4ac3c4f56f040faf0070e4681e937fc4', tx.wtxid())
|
self.assertEqual('e56da664631b8c666c6df38ec80c954c4ac3c4f56f040faf0070e4681e937fc4', tx.wtxid())
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
||||||
def test_sending_offline_wif_online_addr_p2wpkh_p2sh(self, mock_save_db):
|
def test_sending_offline_wif_online_addr_p2wpkh_p2sh(self, mock_save_db):
|
||||||
wallet_offline = WalletIntegrityHelper.create_imported_wallet(privkeys=True, config=self.config)
|
wallet_offline = WalletIntegrityHelper.create_imported_wallet(privkeys=True, config=self.config)
|
||||||
|
@ -1823,7 +1790,6 @@ class TestWalletOfflineSigning(TestCaseForTestnet):
|
||||||
self.assertEqual('7642816d051aa3b333b6564bb6e44fe3a5885bfe7db9860dfbc9973a5c9a6562', tx.txid())
|
self.assertEqual('7642816d051aa3b333b6564bb6e44fe3a5885bfe7db9860dfbc9973a5c9a6562', tx.txid())
|
||||||
self.assertEqual('9bb9949974954613945756c48ca5525cd5cba1b667ccb10c7a53e1ed076a1117', tx.wtxid())
|
self.assertEqual('9bb9949974954613945756c48ca5525cd5cba1b667ccb10c7a53e1ed076a1117', tx.wtxid())
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
||||||
def test_sending_offline_wif_online_addr_p2wpkh(self, mock_save_db):
|
def test_sending_offline_wif_online_addr_p2wpkh(self, mock_save_db):
|
||||||
wallet_offline = WalletIntegrityHelper.create_imported_wallet(privkeys=True, config=self.config)
|
wallet_offline = WalletIntegrityHelper.create_imported_wallet(privkeys=True, config=self.config)
|
||||||
|
@ -1862,7 +1828,6 @@ class TestWalletOfflineSigning(TestCaseForTestnet):
|
||||||
self.assertEqual('f8039bd85279f2b5698f15d47f2e338d067d09af391bd8a19467aa94d03f280c', tx.txid())
|
self.assertEqual('f8039bd85279f2b5698f15d47f2e338d067d09af391bd8a19467aa94d03f280c', tx.txid())
|
||||||
self.assertEqual('3b7cc3c3352bbb43ddc086487ac696e09f2863c3d9e8636721851b8008a83ffa', tx.wtxid())
|
self.assertEqual('3b7cc3c3352bbb43ddc086487ac696e09f2863c3d9e8636721851b8008a83ffa', tx.wtxid())
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
||||||
def test_sending_offline_xprv_online_addr_p2pkh(self, mock_save_db): # compressed pubkey
|
def test_sending_offline_xprv_online_addr_p2pkh(self, mock_save_db): # compressed pubkey
|
||||||
wallet_offline = WalletIntegrityHelper.create_standard_wallet(
|
wallet_offline = WalletIntegrityHelper.create_standard_wallet(
|
||||||
|
@ -1905,7 +1870,6 @@ class TestWalletOfflineSigning(TestCaseForTestnet):
|
||||||
self.assertEqual('e56da664631b8c666c6df38ec80c954c4ac3c4f56f040faf0070e4681e937fc4', tx.txid())
|
self.assertEqual('e56da664631b8c666c6df38ec80c954c4ac3c4f56f040faf0070e4681e937fc4', tx.txid())
|
||||||
self.assertEqual('e56da664631b8c666c6df38ec80c954c4ac3c4f56f040faf0070e4681e937fc4', tx.wtxid())
|
self.assertEqual('e56da664631b8c666c6df38ec80c954c4ac3c4f56f040faf0070e4681e937fc4', tx.wtxid())
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
||||||
def test_sending_offline_xprv_online_addr_p2wpkh_p2sh(self, mock_save_db):
|
def test_sending_offline_xprv_online_addr_p2wpkh_p2sh(self, mock_save_db):
|
||||||
wallet_offline = WalletIntegrityHelper.create_standard_wallet(
|
wallet_offline = WalletIntegrityHelper.create_standard_wallet(
|
||||||
|
@ -1948,7 +1912,6 @@ class TestWalletOfflineSigning(TestCaseForTestnet):
|
||||||
self.assertEqual('7642816d051aa3b333b6564bb6e44fe3a5885bfe7db9860dfbc9973a5c9a6562', tx.txid())
|
self.assertEqual('7642816d051aa3b333b6564bb6e44fe3a5885bfe7db9860dfbc9973a5c9a6562', tx.txid())
|
||||||
self.assertEqual('9bb9949974954613945756c48ca5525cd5cba1b667ccb10c7a53e1ed076a1117', tx.wtxid())
|
self.assertEqual('9bb9949974954613945756c48ca5525cd5cba1b667ccb10c7a53e1ed076a1117', tx.wtxid())
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
||||||
def test_sending_offline_xprv_online_addr_p2wpkh(self, mock_save_db):
|
def test_sending_offline_xprv_online_addr_p2wpkh(self, mock_save_db):
|
||||||
wallet_offline = WalletIntegrityHelper.create_standard_wallet(
|
wallet_offline = WalletIntegrityHelper.create_standard_wallet(
|
||||||
|
@ -1991,7 +1954,6 @@ class TestWalletOfflineSigning(TestCaseForTestnet):
|
||||||
self.assertEqual('f8039bd85279f2b5698f15d47f2e338d067d09af391bd8a19467aa94d03f280c', tx.txid())
|
self.assertEqual('f8039bd85279f2b5698f15d47f2e338d067d09af391bd8a19467aa94d03f280c', tx.txid())
|
||||||
self.assertEqual('3b7cc3c3352bbb43ddc086487ac696e09f2863c3d9e8636721851b8008a83ffa', tx.wtxid())
|
self.assertEqual('3b7cc3c3352bbb43ddc086487ac696e09f2863c3d9e8636721851b8008a83ffa', tx.wtxid())
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
||||||
def test_sending_offline_hd_multisig_online_addr_p2sh(self, mock_save_db):
|
def test_sending_offline_hd_multisig_online_addr_p2sh(self, mock_save_db):
|
||||||
# 2-of-3 legacy p2sh multisig
|
# 2-of-3 legacy p2sh multisig
|
||||||
|
@ -2058,7 +2020,6 @@ class TestWalletOfflineSigning(TestCaseForTestnet):
|
||||||
self.assertEqual('0e8fdc8257a85ebe7eeab14a53c2c258c61a511f64176b7f8fc016bc2263d307', tx.txid())
|
self.assertEqual('0e8fdc8257a85ebe7eeab14a53c2c258c61a511f64176b7f8fc016bc2263d307', tx.txid())
|
||||||
self.assertEqual('0e8fdc8257a85ebe7eeab14a53c2c258c61a511f64176b7f8fc016bc2263d307', tx.wtxid())
|
self.assertEqual('0e8fdc8257a85ebe7eeab14a53c2c258c61a511f64176b7f8fc016bc2263d307', tx.wtxid())
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
||||||
def test_sending_offline_hd_multisig_online_addr_p2wsh_p2sh(self, mock_save_db):
|
def test_sending_offline_hd_multisig_online_addr_p2wsh_p2sh(self, mock_save_db):
|
||||||
# 2-of-2 p2sh-embedded segwit multisig
|
# 2-of-2 p2sh-embedded segwit multisig
|
||||||
|
@ -2129,7 +2090,6 @@ class TestWalletOfflineSigning(TestCaseForTestnet):
|
||||||
self.assertEqual('6a58a51591142429203b62b6ddf6b799a6926882efac229998c51bee6c3573eb', tx.txid())
|
self.assertEqual('6a58a51591142429203b62b6ddf6b799a6926882efac229998c51bee6c3573eb', tx.txid())
|
||||||
self.assertEqual('96d0bca1001778c54e4c3a07929fab5562c5b5a23fd1ca3aa3870cc5df2bf97d', tx.wtxid())
|
self.assertEqual('96d0bca1001778c54e4c3a07929fab5562c5b5a23fd1ca3aa3870cc5df2bf97d', tx.wtxid())
|
||||||
|
|
||||||
@needs_test_with_all_ecc_implementations
|
|
||||||
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
@mock.patch.object(wallet.Abstract_Wallet, 'save_db')
|
||||||
def test_sending_offline_hd_multisig_online_addr_p2wsh(self, mock_save_db):
|
def test_sending_offline_hd_multisig_online_addr_p2wsh(self, mock_save_db):
|
||||||
# 2-of-3 p2wsh multisig
|
# 2-of-3 p2wsh multisig
|
||||||
|
|
|
@ -47,6 +47,7 @@ from aiohttp_socks import SocksConnector, SocksVer
|
||||||
from aiorpcx import TaskGroup
|
from aiorpcx import TaskGroup
|
||||||
import certifi
|
import certifi
|
||||||
import dns.resolver
|
import dns.resolver
|
||||||
|
import ecdsa
|
||||||
|
|
||||||
from .i18n import _
|
from .i18n import _
|
||||||
from .logging import get_logger, Logger
|
from .logging import get_logger, Logger
|
||||||
|
@ -1266,3 +1267,9 @@ def resolve_dns_srv(host: str):
|
||||||
'port': srv.port,
|
'port': srv.port,
|
||||||
}
|
}
|
||||||
return [dict_from_srv_record(srv) for srv in srv_records]
|
return [dict_from_srv_record(srv) for srv in srv_records]
|
||||||
|
|
||||||
|
|
||||||
|
def randrange(bound: int) -> int:
|
||||||
|
"""Return a random integer k such that 1 <= k < bound, uniformly
|
||||||
|
distributed across that range."""
|
||||||
|
return ecdsa.util.randrange(bound)
|
||||||
|
|
|
@ -70,7 +70,6 @@ from .address_synchronizer import (AddressSynchronizer, TX_HEIGHT_LOCAL,
|
||||||
from .util import PR_PAID, PR_UNPAID, PR_UNKNOWN, PR_EXPIRED, PR_INFLIGHT
|
from .util import PR_PAID, PR_UNPAID, PR_UNKNOWN, PR_EXPIRED, PR_INFLIGHT
|
||||||
from .contacts import Contacts
|
from .contacts import Contacts
|
||||||
from .interface import NetworkException
|
from .interface import NetworkException
|
||||||
from .ecc_fast import is_using_fast_ecc
|
|
||||||
from .mnemonic import Mnemonic
|
from .mnemonic import Mnemonic
|
||||||
from .logging import get_logger
|
from .logging import get_logger
|
||||||
from .lnworker import LNWallet
|
from .lnworker import LNWallet
|
||||||
|
@ -270,9 +269,6 @@ class Abstract_Wallet(AddressSynchronizer, ABC):
|
||||||
def init_lightning(self):
|
def init_lightning(self):
|
||||||
if self.db.get('lightning_privkey2'):
|
if self.db.get('lightning_privkey2'):
|
||||||
return
|
return
|
||||||
if not is_using_fast_ecc():
|
|
||||||
raise Exception('libsecp256k1 library not available. '
|
|
||||||
'Verifying Lightning channels is too computationally expensive without libsecp256k1, aborting.')
|
|
||||||
# TODO derive this deterministically from wallet.keystore at keystore generation time
|
# TODO derive this deterministically from wallet.keystore at keystore generation time
|
||||||
# probably along a hardened path ( lnd-equivalent would be m/1017'/coinType'/ )
|
# probably along a hardened path ( lnd-equivalent would be m/1017'/coinType'/ )
|
||||||
seed = os.urandom(32)
|
seed = os.urandom(32)
|
||||||
|
@ -2085,9 +2081,6 @@ class Deterministic_Wallet(Abstract_Wallet):
|
||||||
|
|
||||||
@profiler
|
@profiler
|
||||||
def try_detecting_internal_addresses_corruption(self):
|
def try_detecting_internal_addresses_corruption(self):
|
||||||
if not is_using_fast_ecc():
|
|
||||||
self.logger.info("internal address corruption test skipped due to missing libsecp256k1")
|
|
||||||
return
|
|
||||||
addresses_all = self.get_addresses()
|
addresses_all = self.get_addresses()
|
||||||
# sample 1: first few
|
# sample 1: first few
|
||||||
addresses_sample1 = addresses_all[:10]
|
addresses_sample1 = addresses_all[:10]
|
||||||
|
|
|
@ -27,8 +27,6 @@ import hashlib
|
||||||
import time
|
import time
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
import ecdsa
|
|
||||||
|
|
||||||
from . import util
|
from . import util
|
||||||
from .util import profiler, bh2u
|
from .util import profiler, bh2u
|
||||||
from .logging import get_logger
|
from .logging import get_logger
|
||||||
|
@ -250,8 +248,8 @@ class X509(object):
|
||||||
exponent = spk.next_node(modulus)
|
exponent = spk.next_node(modulus)
|
||||||
rsa_n = spk.get_value_of_type(modulus, 'INTEGER')
|
rsa_n = spk.get_value_of_type(modulus, 'INTEGER')
|
||||||
rsa_e = spk.get_value_of_type(exponent, 'INTEGER')
|
rsa_e = spk.get_value_of_type(exponent, 'INTEGER')
|
||||||
self.modulus = ecdsa.util.string_to_number(rsa_n)
|
self.modulus = int.from_bytes(rsa_n, byteorder='big', signed=False)
|
||||||
self.exponent = ecdsa.util.string_to_number(rsa_e)
|
self.exponent = int.from_bytes(rsa_e, byteorder='big', signed=False)
|
||||||
else:
|
else:
|
||||||
subject_public_key = der.next_node(public_key_algo)
|
subject_public_key = der.next_node(public_key_algo)
|
||||||
spk = der.get_value_of_type(subject_public_key, 'BIT STRING')
|
spk = der.get_value_of_type(subject_public_key, 'BIT STRING')
|
||||||
|
|
Loading…
Add table
Reference in a new issue