mirror of
https://github.com/LBRYFoundation/LBRY-Vault.git
synced 2025-08-31 17:31:36 +00:00
Merge branch 'master' into TextCompleter
This commit is contained in:
commit
54d220c311
102 changed files with 3023 additions and 1555 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -16,6 +16,7 @@ env/
|
|||
.tox/
|
||||
.buildozer/
|
||||
bin/
|
||||
/app.fil
|
||||
|
||||
# tox files
|
||||
.cache/
|
||||
|
|
|
@ -4,12 +4,13 @@ include electrum.conf.sample
|
|||
include electrum.desktop
|
||||
include *.py
|
||||
include electrum
|
||||
include contrib/requirements/requirements.txt
|
||||
include contrib/requirements/requirements-hw.txt
|
||||
recursive-include lib *.py
|
||||
recursive-include gui *.py
|
||||
recursive-include plugins *.py
|
||||
recursive-include packages *.py
|
||||
recursive-include packages cacert.pem
|
||||
include app.fil
|
||||
include icons.qrc
|
||||
recursive-include icons *
|
||||
recursive-include scripts *
|
||||
|
|
10
README.rst
10
README.rst
|
@ -15,7 +15,9 @@ Electrum - Lightweight Bitcoin client
|
|||
.. image:: https://coveralls.io/repos/github/spesmilo/electrum/badge.svg?branch=master
|
||||
:target: https://coveralls.io/github/spesmilo/electrum?branch=master
|
||||
:alt: Test coverage statistics
|
||||
|
||||
.. image:: https://img.shields.io/badge/help-translating-blue.svg
|
||||
:target: https://crowdin.com/project/electrum
|
||||
:alt: Help translating Electrum online
|
||||
|
||||
|
||||
|
||||
|
@ -39,10 +41,12 @@ directory. To run Electrum from its root directory, just do::
|
|||
You can also install Electrum on your system, by running this command::
|
||||
|
||||
sudo apt-get install python3-setuptools
|
||||
python3 setup.py install
|
||||
pip3 install .[full]
|
||||
|
||||
This will download and install the Python dependencies used by
|
||||
Electrum, instead of using the 'packages' directory.
|
||||
The 'full' 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
|
||||
before you can run Electrum. Read the next section, "Development
|
||||
|
@ -60,7 +64,7 @@ Check out the code from Github::
|
|||
|
||||
Run install (this should install dependencies)::
|
||||
|
||||
python3 setup.py install
|
||||
pip3 install .[full]
|
||||
|
||||
Compile the icons file for Qt::
|
||||
|
||||
|
|
|
@ -1,5 +1,25 @@
|
|||
# Release 3.1.2 - (March 28, 2018)
|
||||
|
||||
# Release 3.1 - (March 5, 2018)
|
||||
* Kivy/android: request PIN on startup
|
||||
* Improve OSX build process
|
||||
* Fix various bugs with hardware wallets
|
||||
* Other minor bugfixes
|
||||
|
||||
# Release 3.1.1 - (March 12, 2018)
|
||||
|
||||
* fix #4031: Trezor T support
|
||||
* partial fix #4060: proxy and hardware wallet can't be used together
|
||||
* fix #4039: can't set address labels
|
||||
* fix crash related to coinbase transactions
|
||||
* MacOS: use internal graphics card
|
||||
* fix openalias related crashes
|
||||
* speed-up capital gains calculations
|
||||
* hw wallet encryption: re-prompt for passphrase if incorrect
|
||||
* other minor fixes.
|
||||
|
||||
|
||||
|
||||
# Release 3.1.0 - (March 5, 2018)
|
||||
|
||||
* Memory-pool based fee estimation. Dynamic fees can target a desired
|
||||
depth in the memory pool. This feature is optional, and ETA-based
|
||||
|
|
29
app.fil
29
app.fil
|
@ -1,29 +0,0 @@
|
|||
gui/qt/__init__.py
|
||||
gui/qt/main_window.py
|
||||
gui/qt/history_list.py
|
||||
gui/qt/contact_list.py
|
||||
gui/qt/invoice_list.py
|
||||
gui/qt/request_list.py
|
||||
gui/qt/installwizard.py
|
||||
gui/qt/network_dialog.py
|
||||
gui/qt/password_dialog.py
|
||||
gui/qt/util.py
|
||||
gui/qt/seed_dialog.py
|
||||
gui/qt/transaction_dialog.py
|
||||
gui/qt/address_dialog.py
|
||||
gui/qt/qrcodewidget.py
|
||||
gui/qt/qrtextedit.py
|
||||
gui/qt/qrwindow.py
|
||||
gui/kivy/main.kv
|
||||
gui/kivy/main_window.py
|
||||
gui/kivy/uix/dialogs/__init__.py
|
||||
gui/kivy/uix/dialogs/fee_dialog.py
|
||||
gui/kivy/uix/dialogs/installwizard.py
|
||||
gui/kivy/uix/dialogs/settings.py
|
||||
gui/kivy/uix/dialogs/wallets.py
|
||||
gui/kivy/uix/ui_screens/history.kv
|
||||
gui/kivy/uix/ui_screens/receive.kv
|
||||
gui/kivy/uix/ui_screens/send.kv
|
||||
plugins/labels/qt.py
|
||||
plugins/trezor/qt.py
|
||||
plugins/virtualkeyboard/qt.py
|
|
@ -2,16 +2,35 @@ Building Mac OS binaries
|
|||
========================
|
||||
|
||||
This guide explains how to build Electrum binaries for macOS systems.
|
||||
We build our binaries on El Capitan (10.11.6) as building it on High Sierra
|
||||
makes the binaries incompatible with older versions.
|
||||
|
||||
This assumes that the Xcode command line tools (and thus git) are already installed.
|
||||
The build process consists of two steps:
|
||||
|
||||
## 1. Building the binary
|
||||
|
||||
This needs to be done on a system running macOS or OS X. We use El Capitan (10.11.6) as building it on High Sierra
|
||||
makes the binaries incompatible with older versions.
|
||||
|
||||
Before starting, make sure that the Xcode command line tools are installed (e.g. you have `git`).
|
||||
|
||||
|
||||
## 1. Run the script
|
||||
cd electrum
|
||||
./contrib/build-osx/make_osx
|
||||
|
||||
This creates a folder named Electrum.app.
|
||||
|
||||
## 2. Building the image
|
||||
The usual way to distribute macOS applications is to use image files containing the
|
||||
application. Although these images can be created on a Mac with the built-in `hdiutil`,
|
||||
they are not deterministic.
|
||||
|
||||
Instead, we use the toolchain that Bitcoin uses: genisoimage and libdmg-hfsplus.
|
||||
These tools do not work on macOS, so you need a separate Linux machine (or VM).
|
||||
|
||||
./make_osx
|
||||
Copy the Electrum.app directory over and install the dependencies, e.g.:
|
||||
|
||||
## 2. Done
|
||||
apt install libcap-dev cmake make gcc faketime
|
||||
|
||||
Then you can just invoke `package.sh` with the path to the app:
|
||||
|
||||
cd electrum
|
||||
./contrib/build-osx/package.sh ~/Electrum.app/
|
12
contrib/build-osx/base.sh
Normal file
12
contrib/build-osx/base.sh
Normal file
|
@ -0,0 +1,12 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
RED='\033[0;31m'
|
||||
BLUE='\033[0,34m'
|
||||
NC='\033[0m' # No Color
|
||||
function info {
|
||||
printf "\r💬 ${BLUE}INFO:${NC} ${1}\n"
|
||||
}
|
||||
function fail {
|
||||
printf "\r🗯 ${RED}ERROR:${NC} ${1}\n"
|
||||
exit 1
|
||||
}
|
86
contrib/build-osx/cdrkit-deterministic.patch
Normal file
86
contrib/build-osx/cdrkit-deterministic.patch
Normal file
|
@ -0,0 +1,86 @@
|
|||
--- cdrkit-1.1.11.old/genisoimage/tree.c 2008-10-21 19:57:47.000000000 -0400
|
||||
+++ cdrkit-1.1.11/genisoimage/tree.c 2013-12-06 00:23:18.489622668 -0500
|
||||
@@ -1139,8 +1139,9 @@
|
||||
scan_directory_tree(struct directory *this_dir, char *path,
|
||||
struct directory_entry *de)
|
||||
{
|
||||
- DIR *current_dir;
|
||||
+ int current_file;
|
||||
char whole_path[PATH_MAX];
|
||||
+ struct dirent **d_list;
|
||||
struct dirent *d_entry;
|
||||
struct directory *parent;
|
||||
int dflag;
|
||||
@@ -1164,7 +1165,8 @@
|
||||
this_dir->dir_flags |= DIR_WAS_SCANNED;
|
||||
|
||||
errno = 0; /* Paranoia */
|
||||
- current_dir = opendir(path);
|
||||
+ //current_dir = opendir(path);
|
||||
+ current_file = scandir(path, &d_list, NULL, alphasort);
|
||||
d_entry = NULL;
|
||||
|
||||
/*
|
||||
@@ -1173,12 +1175,12 @@
|
||||
*/
|
||||
old_path = path;
|
||||
|
||||
- if (current_dir) {
|
||||
+ if (current_file >= 0) {
|
||||
errno = 0;
|
||||
- d_entry = readdir(current_dir);
|
||||
+ d_entry = d_list[0];
|
||||
}
|
||||
|
||||
- if (!current_dir || !d_entry) {
|
||||
+ if (current_file < 0 || !d_entry) {
|
||||
int ret = 1;
|
||||
|
||||
#ifdef USE_LIBSCHILY
|
||||
@@ -1191,8 +1193,8 @@
|
||||
de->isorec.flags[0] &= ~ISO_DIRECTORY;
|
||||
ret = 0;
|
||||
}
|
||||
- if (current_dir)
|
||||
- closedir(current_dir);
|
||||
+ if(d_list)
|
||||
+ free(d_list);
|
||||
return (ret);
|
||||
}
|
||||
#ifdef ABORT_DEEP_ISO_ONLY
|
||||
@@ -1208,7 +1210,7 @@
|
||||
errmsgno(EX_BAD, "use Rock Ridge extensions via -R or -r,\n");
|
||||
errmsgno(EX_BAD, "or allow deep ISO9660 directory nesting via -D.\n");
|
||||
}
|
||||
- closedir(current_dir);
|
||||
+ free(d_list);
|
||||
return (1);
|
||||
}
|
||||
#endif
|
||||
@@ -1250,13 +1252,13 @@
|
||||
* The first time through, skip this, since we already asked
|
||||
* for the first entry when we opened the directory.
|
||||
*/
|
||||
- if (dflag)
|
||||
- d_entry = readdir(current_dir);
|
||||
+ if (dflag && current_file >= 0)
|
||||
+ d_entry = d_list[current_file];
|
||||
dflag++;
|
||||
|
||||
- if (!d_entry)
|
||||
+ if (current_file < 0)
|
||||
break;
|
||||
-
|
||||
+ current_file--;
|
||||
/* OK, got a valid entry */
|
||||
|
||||
/* If we do not want all files, then pitch the backups. */
|
||||
@@ -1348,7 +1350,7 @@
|
||||
insert_file_entry(this_dir, whole_path, d_entry->d_name);
|
||||
#endif /* APPLE_HYB */
|
||||
}
|
||||
- closedir(current_dir);
|
||||
+ free(d_list);
|
||||
|
||||
#ifdef APPLE_HYB
|
||||
/*
|
|
@ -1,28 +1,18 @@
|
|||
#!/bin/bash
|
||||
RED='\033[0;31m'
|
||||
BLUE='\033[0,34m'
|
||||
NC='\033[0m' # No Color
|
||||
function info {
|
||||
printf "\r💬 ${BLUE}INFO:${NC} ${1}\n"
|
||||
}
|
||||
function fail {
|
||||
printf "\r🗯 ${RED}ERROR:${NC} ${1}\n"
|
||||
exit 1
|
||||
}
|
||||
#!/usr/bin/env bash
|
||||
|
||||
build_dir=$(dirname "$0")
|
||||
test -n "$build_dir" -a -d "$build_dir" || exit
|
||||
cd $build_dir/../..
|
||||
|
||||
export PYTHONHASHSEED=22
|
||||
VERSION=`git describe --tags`
|
||||
|
||||
# Paramterize
|
||||
# Parameterize
|
||||
PYTHON_VERSION=3.6.4
|
||||
BUILDDIR=/tmp/electrum-build
|
||||
PACKAGE=Electrum
|
||||
GIT_REPO=https://github.com/spesmilo/electrum
|
||||
|
||||
. $(dirname "$0")/base.sh
|
||||
|
||||
src_dir=$(dirname "$0")
|
||||
cd $src_dir/../..
|
||||
|
||||
export PYTHONHASHSEED=22
|
||||
VERSION=`git describe --tags`
|
||||
|
||||
info "Installing Python $PYTHON_VERSION"
|
||||
export PATH="~/.pyenv/bin:~/.pyenv/shims:~/Library/Python/3.6/bin:$PATH"
|
||||
|
@ -61,9 +51,9 @@ cp $BUILDDIR/electrum-icons/icons_rc.py ./gui/qt/
|
|||
|
||||
|
||||
info "Downloading libusb..."
|
||||
curl https://homebrew.bintray.com/bottles/libusb-1.0.21.el_capitan.bottle.tar.gz | \
|
||||
curl https://homebrew.bintray.com/bottles/libusb-1.0.22.el_capitan.bottle.tar.gz | \
|
||||
tar xz --directory $BUILDDIR
|
||||
cp $BUILDDIR/libusb/1.0.21/lib/libusb-1.0.dylib contrib/build-osx
|
||||
cp $BUILDDIR/libusb/1.0.22/lib/libusb-1.0.dylib contrib/build-osx
|
||||
|
||||
info "Installing requirements..."
|
||||
python3 -m pip install -Ir ./contrib/deterministic-build/requirements.txt --user && \
|
||||
|
@ -77,6 +67,13 @@ fail "Could not install hardware wallet requirements"
|
|||
info "Building $PACKAGE..."
|
||||
python3 setup.py install --user > /dev/null || fail "Could not build $PACKAGE"
|
||||
|
||||
info "Faking timestamps..."
|
||||
for d in ~/Library/Python/ ~/.pyenv .; do
|
||||
pushd $d
|
||||
find . -exec touch -t '200101220000' {} +
|
||||
popd
|
||||
done
|
||||
|
||||
info "Building binary"
|
||||
pyinstaller --noconfirm --ascii --name $VERSION contrib/build-osx/osx.spec || fail "Could not build binary"
|
||||
|
||||
|
|
|
@ -93,7 +93,8 @@ app = BUNDLE(exe,
|
|||
name=PACKAGE + '.app',
|
||||
icon=electrum+ICONS_FILE,
|
||||
bundle_identifier=None,
|
||||
info_plist = {
|
||||
'NSHighResolutionCapable':'True'
|
||||
info_plist={
|
||||
'NSHighResolutionCapable': 'True',
|
||||
'NSSupportsAutomaticGraphicsSwitching': 'True'
|
||||
}
|
||||
)
|
||||
|
|
88
contrib/build-osx/package.sh
Executable file
88
contrib/build-osx/package.sh
Executable file
|
@ -0,0 +1,88 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
cdrkit_version=1.1.11
|
||||
cdrkit_download_path=http://distro.ibiblio.org/fatdog/source/600/c
|
||||
cdrkit_file_name=cdrkit-${cdrkit_version}.tar.bz2
|
||||
cdrkit_sha256_hash=b50d64c214a65b1a79afe3a964c691931a4233e2ba605d793eb85d0ac3652564
|
||||
cdrkit_patches=cdrkit-deterministic.patch
|
||||
genisoimage=genisoimage-$cdrkit_version
|
||||
|
||||
libdmg_url=https://github.com/theuni/libdmg-hfsplus
|
||||
|
||||
|
||||
export LD_PRELOAD=$(locate libfaketime.so.1)
|
||||
export FAKETIME="2000-01-22 00:00:00"
|
||||
export PATH=$PATH:~/bin
|
||||
|
||||
. $(dirname "$0")/base.sh
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
echo "Usage: $0 Electrum.app"
|
||||
exit -127
|
||||
fi
|
||||
|
||||
mkdir -p ~/bin
|
||||
|
||||
if ! which ${genisoimage} > /dev/null 2>&1; then
|
||||
mkdir -p /tmp/electrum-macos
|
||||
cd /tmp/electrum-macos
|
||||
info "Downloading cdrkit $cdrkit_version"
|
||||
wget -nc ${cdrkit_download_path}/${cdrkit_file_name}
|
||||
tar xvf ${cdrkit_file_name}
|
||||
|
||||
info "Patching genisoimage"
|
||||
cd cdrkit-${cdrkit_version}
|
||||
patch -p1 < ../cdrkit-deterministic.patch
|
||||
|
||||
info "Building genisoimage"
|
||||
cmake . -Wno-dev
|
||||
make genisoimage
|
||||
cp genisoimage/genisoimage ~/bin/${genisoimage}
|
||||
fi
|
||||
|
||||
if ! which dmg > /dev/null 2>&1; then
|
||||
mkdir -p /tmp/electrum-macos
|
||||
cd /tmp/electrum-macos
|
||||
info "Downloading libdmg"
|
||||
LD_PRELOAD= git clone ${libdmg_url}
|
||||
cd libdmg-hfsplus
|
||||
info "Building libdmg"
|
||||
cmake .
|
||||
make
|
||||
cp dmg/dmg ~/bin
|
||||
fi
|
||||
|
||||
${genisoimage} -version || fail "Unable to install genisoimage"
|
||||
dmg -|| fail "Unable to install libdmg"
|
||||
|
||||
plist=$1/Contents/Info.plist
|
||||
test -f "$plist" || fail "Info.plist not found"
|
||||
VERSION=$(grep -1 ShortVersionString $plist |tail -1|gawk 'match($0, /<string>(.*)<\/string>/, a) {print a[1]}')
|
||||
echo $VERSION
|
||||
|
||||
rm -rf /tmp/electrum-macos/image > /dev/null 2>&1
|
||||
mkdir /tmp/electrum-macos/image/
|
||||
cp -r $1 /tmp/electrum-macos/image/
|
||||
|
||||
build_dir=$(dirname "$1")
|
||||
test -n "$build_dir" -a -d "$build_dir" || exit
|
||||
cd $build_dir
|
||||
|
||||
${genisoimage} \
|
||||
-no-cache-inodes \
|
||||
-D \
|
||||
-l \
|
||||
-probe \
|
||||
-V "Electrum" \
|
||||
-no-pad \
|
||||
-r \
|
||||
-dir-mode 0755 \
|
||||
-apple \
|
||||
-o Electrum_uncompressed.dmg \
|
||||
/tmp/electrum-macos/image || fail "Unable to create uncompressed dmg"
|
||||
|
||||
dmg dmg Electrum_uncompressed.dmg electrum-$VERSION.dmg || fail "Unable to create compressed dmg"
|
||||
rm Electrum_uncompressed.dmg
|
||||
|
||||
echo "Done."
|
||||
md5sum electrum-$VERSION.dmg
|
|
@ -13,6 +13,10 @@ echo "Clearing $here/build and $here/dist..."
|
|||
rm "$here"/build/* -rf
|
||||
rm "$here"/dist/* -rf
|
||||
|
||||
mkdir -p /tmp/electrum-build
|
||||
mkdir -p /tmp/electrum-build/pip-cache
|
||||
export PIP_CACHE_DIR="/tmp/electrum-build/pip-cache"
|
||||
|
||||
$here/prepare-wine.sh || exit 1
|
||||
|
||||
echo "Resetting modification time in C:\Python..."
|
||||
|
|
|
@ -10,6 +10,8 @@ for i, x in enumerate(sys.argv):
|
|||
else:
|
||||
raise BaseException('no name')
|
||||
|
||||
PYTHON_VERSION = '3.5.4'
|
||||
PYHOME = 'c:/python' + PYTHON_VERSION
|
||||
|
||||
home = 'C:\\electrum\\'
|
||||
|
||||
|
@ -21,7 +23,7 @@ hiddenimports += collect_submodules('keepkeylib')
|
|||
hiddenimports += collect_submodules('websocket')
|
||||
|
||||
# Add libusb binary
|
||||
binaries = [("c:/python3.5.4/libusb-1.0.dll", ".")]
|
||||
binaries = [(PYHOME+"/libusb-1.0.dll", ".")]
|
||||
|
||||
# Workaround for "Retro Look":
|
||||
binaries += [b for b in collect_dynamic_libs('PyQt5') if 'qwindowsvista' in b[0]]
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Please update these carefully, some versions won't work under Wine
|
||||
NSIS_FILENAME=nsis-3.02.1-setup.exe
|
||||
NSIS_FILENAME=nsis-3.03-setup.exe
|
||||
NSIS_URL=https://prdownloads.sourceforge.net/nsis/$NSIS_FILENAME?download
|
||||
NSIS_SHA256=736c9062a02e297e335f82252e648a883171c98e0d5120439f538c81d429552e
|
||||
NSIS_SHA256=bd3b15ab62ec6b0c7a00f46022d441af03277be893326f6fea8e212dc2d77743
|
||||
|
||||
ZBAR_FILENAME=zbarw-20121031-setup.exe
|
||||
ZBAR_URL=https://sourceforge.net/projects/zbarw/files/$ZBAR_FILENAME/download
|
||||
ZBAR_SHA256=177e32b272fa76528a3af486b74e9cb356707be1c5ace4ed3fcee9723e2c2c02
|
||||
|
||||
LIBUSB_FILENAME=libusb-1.0.21.7z
|
||||
LIBUSB_URL=https://prdownloads.sourceforge.net/project/libusb/libusb-1.0/libusb-1.0.21/$LIBUSB_FILENAME?download
|
||||
LIBUSB_SHA256=acdde63a40b1477898aee6153f9d91d1a2e8a5d93f832ca8ab876498f3a6d2b8
|
||||
LIBUSB_FILENAME=libusb-1.0.22.7z
|
||||
LIBUSB_URL=https://prdownloads.sourceforge.net/project/libusb/libusb-1.0/libusb-1.0.22/$LIBUSB_FILENAME?download
|
||||
LIBUSB_SHA256=671f1a420757b4480e7fadc8313d6fb3cbb75ca00934c417c1efa6e77fb8779b
|
||||
|
||||
PYTHON_VERSION=3.5.4
|
||||
|
||||
|
@ -54,6 +54,27 @@ download_if_not_exist() {
|
|||
fi
|
||||
}
|
||||
|
||||
# https://github.com/travis-ci/travis-build/blob/master/lib/travis/build/templates/header.sh
|
||||
retry() {
|
||||
local result=0
|
||||
local count=1
|
||||
while [ $count -le 3 ]; do
|
||||
[ $result -ne 0 ] && {
|
||||
echo -e "\nThe command \"$@\" failed. Retrying, $count of 3.\n" >&2
|
||||
}
|
||||
! { "$@"; result=$?; }
|
||||
[ $result -eq 0 ] && break
|
||||
count=$(($count + 1))
|
||||
sleep 1
|
||||
done
|
||||
|
||||
[ $count -gt 3 ] && {
|
||||
echo -e "\nThe command \"$@\" failed 3 times.\n" >&2
|
||||
}
|
||||
|
||||
return $result
|
||||
}
|
||||
|
||||
# Let's begin!
|
||||
here=$(dirname $(readlink -e $0))
|
||||
set -e
|
||||
|
@ -65,8 +86,6 @@ echo "done"
|
|||
|
||||
wine 'wineboot'
|
||||
|
||||
mkdir -p /tmp/electrum-build
|
||||
|
||||
cd /tmp/electrum-build
|
||||
|
||||
# Install Python
|
||||
|
@ -74,12 +93,12 @@ cd /tmp/electrum-build
|
|||
# keys from https://www.python.org/downloads/#pubkeys
|
||||
KEYLIST_PYTHON_DEV="531F072D39700991925FED0C0EDDC5F26A45C816 26DEA9D4613391EF3E25C9FF0A5B101836580288 CBC547978A3964D14B9AB36A6AF053F07D9DC8D2 C01E1CAD5EA2C4F0B8E3571504C367C218ADD4FF 12EF3DC38047DA382D18A5B999CDEA9DA4135B38 8417157EDBE73D9EAC1E539B126EB563A74B06BF DBBF2EEBF925FAADCF1F3FFFD9866941EA5BBD71 2BA0DB82515BBB9EFFAC71C5C9BE28DEE6DF025C 0D96DF4D4110E5C43FBFB17F2D347EA6AA65421D C9B104B3DD3AA72D7CCB1066FB9921286F5E1540 97FC712E4C024BBEA48A61ED3A5CA953F73C700D 7ED10B6531D7C8E1BC296021FC624643487034E5"
|
||||
KEYRING_PYTHON_DEV="keyring-electrum-build-python-dev.gpg"
|
||||
KEYSERVER_PYTHON_DEV="hkp://keys.gnupg.net"
|
||||
gpg --no-default-keyring --keyring $KEYRING_PYTHON_DEV --keyserver $KEYSERVER_PYTHON_DEV --recv-keys $KEYLIST_PYTHON_DEV
|
||||
KEYSERVER_PYTHON_DEV="hkp://pool.sks-keyservers.net"
|
||||
retry gpg --no-default-keyring --keyring $KEYRING_PYTHON_DEV --keyserver $KEYSERVER_PYTHON_DEV --recv-keys $KEYLIST_PYTHON_DEV
|
||||
for msifile in core dev exe lib pip tools; do
|
||||
echo "Installing $msifile..."
|
||||
wget -nc "https://www.python.org/ftp/python/$PYTHON_VERSION/win32/${msifile}.msi"
|
||||
wget -nc "https://www.python.org/ftp/python/$PYTHON_VERSION/win32/${msifile}.msi.asc"
|
||||
wget -N -c "https://www.python.org/ftp/python/$PYTHON_VERSION/win32/${msifile}.msi"
|
||||
wget -N -c "https://www.python.org/ftp/python/$PYTHON_VERSION/win32/${msifile}.msi.asc"
|
||||
verify_signature "${msifile}.msi.asc" $KEYRING_PYTHON_DEV
|
||||
wine msiexec /i "${msifile}.msi" /qb TARGETDIR=C:/python$PYTHON_VERSION
|
||||
done
|
||||
|
@ -117,11 +136,6 @@ verify_hash $LIBUSB_FILENAME "$LIBUSB_SHA256"
|
|||
|
||||
cp libusb/MS32/dll/libusb-1.0.dll $WINEPREFIX/drive_c/python$PYTHON_VERSION/
|
||||
|
||||
# Install UPX
|
||||
#wget -O upx.zip "https://downloads.sourceforge.net/project/upx/upx/3.08/upx308w.zip"
|
||||
#unzip -o upx.zip
|
||||
#cp upx*/upx.exe .
|
||||
|
||||
# add dlls needed for pyinstaller:
|
||||
cp $WINEPREFIX/drive_c/python$PYTHON_VERSION/Lib/site-packages/PyQt5/Qt/bin/* $WINEPREFIX/drive_c/python$PYTHON_VERSION/
|
||||
|
||||
|
|
38
contrib/deterministic-build/find_restricted_dependencies.py
Executable file
38
contrib/deterministic-build/find_restricted_dependencies.py
Executable file
|
@ -0,0 +1,38 @@
|
|||
#!/usr/bin/env python3
|
||||
import sys
|
||||
|
||||
import requests
|
||||
|
||||
|
||||
def check_restriction(p, r):
|
||||
# See: https://www.python.org/dev/peps/pep-0496/
|
||||
# Hopefully we don't need to parse the whole microlanguage
|
||||
if "extra" in r and "[" not in p:
|
||||
return False
|
||||
for marker in ["os_name", "platform_release", "sys_platform", "platform_system"]:
|
||||
if marker in r:
|
||||
return True
|
||||
|
||||
|
||||
for p in sys.stdin.read().split():
|
||||
p = p.strip()
|
||||
if not p:
|
||||
continue
|
||||
assert "==" in p, "This script expects a list of packages with pinned version, e.g. package==1.2.3, not {}".format(p)
|
||||
p, v = p.rsplit("==", 1)
|
||||
try:
|
||||
data = requests.get("https://pypi.org/pypi/{}/{}/json".format(p, v)).json()["info"]
|
||||
except ValueError:
|
||||
raise BaseException("Package could not be found: {}=={}".format(p, v))
|
||||
try:
|
||||
for r in data["requires_dist"]:
|
||||
if ";" not in r:
|
||||
continue
|
||||
d, restricted = r.split(";", 1)
|
||||
if check_restriction(d, restricted):
|
||||
print(d, sep=" ")
|
||||
print("Installing {} from {} although it is only needed for {}".format(d, p, restricted), file=sys.stderr)
|
||||
except TypeError:
|
||||
# Has no dependencies at all
|
||||
continue
|
||||
|
|
@ -1,5 +1,57 @@
|
|||
pycryptodomex==3.4.12
|
||||
PyQt5==5.10
|
||||
sip==4.19.7
|
||||
six==1.11.0
|
||||
websocket-client==0.46.0
|
||||
pip==9.0.3 \
|
||||
--hash=sha256:7bf48f9a693be1d58f49f7af7e0ae9fe29fd671cde8a55e6edca3581c4ef5796 \
|
||||
--hash=sha256:c3ede34530e0e0b2381e7363aded78e0c33291654937e7373032fda04e8803e5
|
||||
pycryptodomex==3.5.1 \
|
||||
--hash=sha256:16ab612ca9164e971dc00f8fe895ac835e8bfe64c3174b368f80172ff5a98300 \
|
||||
--hash=sha256:299a79efba6152ea438cc37f7349161e7bbd914f918342cad6316a4a5f29f2d7 \
|
||||
--hash=sha256:2a55e8fd69c84287b44e2c9c07eaad314e76680b86e873774314c27266728670 \
|
||||
--hash=sha256:2f71dc2b91288cf4a164287858eaccdc7053bf5765ebc47c5188f94eccc35e80 \
|
||||
--hash=sha256:2f81caf3ee08f00a957fd074c33430e8781958c616e864c5a1e709fb954750bc \
|
||||
--hash=sha256:3d4c77f1d4273ae753e49dac5c916f2278b0dd354a0c5f2a29fcf88bbae4efa9 \
|
||||
--hash=sha256:563512542dcab3e95d8cef70e45cc5a43ef35ff84bc040c388b305015343e51e \
|
||||
--hash=sha256:5d058decae88f86833a430afc0517df815d9efa4255b3a6d576c7fb305cb56d4 \
|
||||
--hash=sha256:5ec5903197d256b4559ff5c6a4756c34219ec81aff92be1174681623ba1e6383 \
|
||||
--hash=sha256:67f6573ff84ce7f7ea8ffc01ba5821c15dc85bf43291e4f8e11d7b6e2d5f504e \
|
||||
--hash=sha256:729da9aa2b8ea0bd8e35bc89ecd1ff4e482e6e9c2275e2e19de8b68dd8156fb5 \
|
||||
--hash=sha256:82df0a7cda5c94e9e4c62fb8d6507d5418f6593c8ed1b40b538a771ca003b597 \
|
||||
--hash=sha256:91b87c3abb24da1a980cb0f05e150eb0525235129bc5cb59277ea96860677f0a \
|
||||
--hash=sha256:a02b1b17d7c86b12bc1d4ede75846a7971e7df6d75508cd0696e383c18cad4ce \
|
||||
--hash=sha256:a36d5a5b73e51d66e3f1da53ce00e56de860a9c529f2811bb8d95374d9da06df \
|
||||
--hash=sha256:a7d836d6284c4734841c7c9d851be546650302ebca281de851129c22f1298ad5 \
|
||||
--hash=sha256:bb60d38111ebc383a5a1c909545562926c66c846d03fc65ba7b8a3487cb23078 \
|
||||
--hash=sha256:bf2e6cf6e78c8e6d63eeaa9641cad5008a382af98f2dc25cb7c6444f13133df9 \
|
||||
--hash=sha256:e303a4a1b242d3277e8dea07ab4e3737d0d1ed122990c713d6f88b0dda10c378 \
|
||||
--hash=sha256:e378bd7a09257a7a9a58f7f04b088991cf23a99847e9f42d6f996b4e52a11c33 \
|
||||
--hash=sha256:e75e7fe725dd5989e89da25a2fe7e3d35ed8123ac30eaae2f2340d0ba0431a88 \
|
||||
--hash=sha256:eac46844350302c93f3fd3eaa37353ee9e25cffcd1c574dfffb22de157ddce17 \
|
||||
--hash=sha256:f0ca00abf69827e78415050780cf838c7af9f378e591611210e25a03d6d0ea90
|
||||
PyQt5==5.10.1 \
|
||||
--hash=sha256:1e652910bd1ffd23a3a48c510ecad23a57a853ed26b782cd54b16658e6f271ac \
|
||||
--hash=sha256:4db7113f464c733a99fcb66c4c093a47cf7204ad3f8b3bda502efcc0839ac14b \
|
||||
--hash=sha256:9c17ab3974c1fc7bbb04cc1c9dae780522c0ebc158613f3025fccae82227b5f7 \
|
||||
--hash=sha256:f6035baa009acf45e5f460cf88f73580ad5dc0e72330029acd99e477f20a5d61
|
||||
setuptools==39.0.1 \
|
||||
--hash=sha256:8010754433e3211b9cdbbf784b50f30e80bf40fc6b05eb5f865fab83300599b8 \
|
||||
--hash=sha256:bec7badf0f60e7fc8153fac47836edc41b74e5d541d7692e614e635720d6a7c7
|
||||
SIP==4.19.8 \
|
||||
--hash=sha256:09f9a4e6c28afd0bafedb26ffba43375b97fe7207bd1a0d3513f79b7d168b331 \
|
||||
--hash=sha256:105edaaa1c8aa486662226360bd3999b4b89dd56de3e314d82b83ed0587d8783 \
|
||||
--hash=sha256:1bb10aac55bd5ab0e2ee74b3047aa2016cfa7932077c73f602a6f6541af8cd51 \
|
||||
--hash=sha256:265ddf69235dd70571b7d4da20849303b436192e875ce7226be7144ca702a45c \
|
||||
--hash=sha256:52074f7cb5488e8b75b52f34ec2230bc75d22986c7fe5cd3f2d266c23f3349a7 \
|
||||
--hash=sha256:5ff887a33839de8fc77d7f69aed0259b67a384dc91a1dc7588e328b0b980bde2 \
|
||||
--hash=sha256:74da4ddd20c5b35c19cda753ce1e8e1f71616931391caeac2de7a1715945c679 \
|
||||
--hash=sha256:7d69e9cf4f8253a3c0dfc5ba6bb9ac8087b8239851f22998e98cb35cfe497b68 \
|
||||
--hash=sha256:97bb93ee0ef01ba90f57be2b606e08002660affd5bc380776dd8b0fcaa9e093a \
|
||||
--hash=sha256:cf98150a99e43fda7ae22abe655b6f202e491d6291486548daa56cb15a2fcf85 \
|
||||
--hash=sha256:d9023422127b94d11c1a84bfa94933e959c484f2c79553c1ef23c69fe00d25f8 \
|
||||
--hash=sha256:e72955e12f4fccf27aa421be383453d697b8a44bde2cc26b08d876fd492d0174
|
||||
six==1.11.0 \
|
||||
--hash=sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9 \
|
||||
--hash=sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb
|
||||
websocket-client==0.47.0 \
|
||||
--hash=sha256:188b68b14fdb2d8eb1a111f21b9ffd2dbf1dbc4e4c1d28cf2c37cdbf1dd1cae6 \
|
||||
--hash=sha256:a453dc4dfa6e0db3d8fd7738a308a88effe6240c59f3226eb93e8f020c216149
|
||||
wheel==0.31.0 \
|
||||
--hash=sha256:1ae8153bed701cb062913b72429bcf854ba824f973735427681882a688cb55ce \
|
||||
--hash=sha256:9cdc8ab2cc9c3c2e2727a4b67c22881dbb0e1c503d592992594c5e131c867107
|
||||
|
|
|
@ -1,18 +1,119 @@
|
|||
btchip-python==0.1.24
|
||||
certifi==2018.1.18
|
||||
chardet==3.0.4
|
||||
click==6.7
|
||||
Cython==0.27.3
|
||||
ecdsa==0.13
|
||||
hidapi==0.7.99.post21
|
||||
idna==2.6
|
||||
keepkey==4.0.2
|
||||
libusb1==1.6.4
|
||||
mnemonic==0.18
|
||||
pbkdf2==1.3
|
||||
protobuf==3.5.1
|
||||
pyblake2==1.1.0
|
||||
requests==2.18.4
|
||||
six==1.11.0
|
||||
trezor==0.9.0
|
||||
urllib3==1.22
|
||||
btchip-python==0.1.26 \
|
||||
--hash=sha256:427d67c5b4f4709605c51dd91d5d44a2ad8f541693673817765271e4b3a1461e
|
||||
certifi==2018.1.18 \
|
||||
--hash=sha256:14131608ad2fd56836d33a71ee60fa1c82bc9d2c8d98b7bdbc631fe1b3cd1296 \
|
||||
--hash=sha256:edbc3f203427eef571f79a7692bb160a2b0f7ccaa31953e99bd17e307cf63f7d
|
||||
chardet==3.0.4 \
|
||||
--hash=sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae \
|
||||
--hash=sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691
|
||||
click==6.7 \
|
||||
--hash=sha256:29f99fc6125fbc931b758dc053b3114e55c77a6e4c6c3a2674a2dc986016381d \
|
||||
--hash=sha256:f15516df478d5a56180fbf80e68f206010e6d160fc39fa508b65e035fd75130b
|
||||
Cython==0.28.1 \
|
||||
--hash=sha256:0a44c3645a84724d4f7f7c1126f3807cad14271f210bb1a9a15bfd330c8d20b9 \
|
||||
--hash=sha256:152ee5f345012ca3bb7cc71da2d3736ee20f52cd8476e4d49e5e25c5a4102b12 \
|
||||
--hash=sha256:15cbde95cdf6a346c63c0b38b895aa3186d0a49adcaf42b9e19c4cb0473cb921 \
|
||||
--hash=sha256:175273eb6a90af364b9b2aea74e3c3eebcde9caec02d617cd8886c0933ec3304 \
|
||||
--hash=sha256:1a434e7621ca6671ce949893fef94b13a580853ba976e729f68dda5ab270ee8a \
|
||||
--hash=sha256:1da199a5be7c486ee89b4a8bda7f00f9fd98b800d9af3a1fe3fc63e68dadea85 \
|
||||
--hash=sha256:26196ff8fe00e7c4c5815a310c368edfc1a1c8a3c90ac9220435d1b01e795cf7 \
|
||||
--hash=sha256:2b73b062658511167dde37a51acb80ae6ddea1ffa284ebdbc47a900f21e63c70 \
|
||||
--hash=sha256:2b9aa64473fefbe988e36a30915732a0e2a75ffe0f3e81a70e3f8d367c2e4119 \
|
||||
--hash=sha256:32638aefc164404ac70e5f86558cd3aece34df17db16da905abf5e664073bae4 \
|
||||
--hash=sha256:330c95c03e39835976d1410f5fa877b75fcc5c50dc146d4c02377fc719b1f8c9 \
|
||||
--hash=sha256:38b499fa317cf6939e46317457240553b13e974f08ed387523f10af26e269f6c \
|
||||
--hash=sha256:3c60caa0075aa1f1c5e10b5352d2e8bb9a18361ce9fca50fa7d4baea67804ade \
|
||||
--hash=sha256:3fc9b945541cadb5a10316e48b5e73f4b6f635b7d30156f502b66f3766545b23 \
|
||||
--hash=sha256:427299c47cfe04d97f9f09ea55570c05898a87b64caf6ddebb529b6f64e5d228 \
|
||||
--hash=sha256:4e07e619af85e7c1ec2344ecab558929bb4acbca25f8f170d07dc677e8ee413f \
|
||||
--hash=sha256:5010e048fb9791522fe626bd40137b8a847ba77a6e656bb64d6d7acdc45ece32 \
|
||||
--hash=sha256:70bc2806fc9e5affcf74c0d4fa12cba57ffb94cdfc28b975692c8df56ea86402 \
|
||||
--hash=sha256:7cdd5303121024269610236876d9f4beb6a909a1ea5d7bc48e7bbf5d8774a3f2 \
|
||||
--hash=sha256:80856aa45004514a3ff5e102bd18fbd5230d234311de1f83d4e5b97ef42f6c93 \
|
||||
--hash=sha256:996ae959ab2600b8ad4c80981afc32e89b42d0abe3234c48e960e40180459cb2 \
|
||||
--hash=sha256:b5c2e31be55bc61d3c758889d06b16d84f4fda944832fbed63c113ec2dbc5f97 \
|
||||
--hash=sha256:c39b3a042bf5ded7c8336c82b1fa817e1f46a7ef16d41d66b3d3340e7a3b60ed \
|
||||
--hash=sha256:d08f5dd2fbf7d1506c9d986a8352b2423170002ddb635b184d2a916d2b5df0d6 \
|
||||
--hash=sha256:d2b636c16931663aeb8ffb91cf871a912e66e7200755ce68aa8c502c16eb366f \
|
||||
--hash=sha256:e27e12834ac315c7d67ca697a325d42ff8395e9b82fb62c8665bb583eb0df589 \
|
||||
--hash=sha256:e312dd688b97e9f97199a8e4ba18b65db2747157630761d27193a18761b23fed \
|
||||
--hash=sha256:e47bbe74d6c87fab2e22f1580aa3e4a8e7482b4265b1fc76685fc49f90e18f99 \
|
||||
--hash=sha256:ea113ff58e96152738e646b8ee77b41d3994735df77bee0a26cd413a67e03c0f \
|
||||
--hash=sha256:ea22d79133583b5b0f856dbfda097363228ae0a3d77431deaba90634e5d4853a
|
||||
ecdsa==0.13 \
|
||||
--hash=sha256:40d002cf360d0e035cf2cb985e1308d41aaa087cbfc135b2dc2d844296ea546c \
|
||||
--hash=sha256:64cf1ee26d1cde3c73c6d7d107f835fed7c6a2904aef9eac223d57ad800c43fa
|
||||
hidapi==0.7.99.post21 \
|
||||
--hash=sha256:1ac170f4d601c340f2cd52fd06e85c5e77bad7ceac811a7bb54b529f7dc28c24 \
|
||||
--hash=sha256:8d3be666f464347022e2b47caf9132287885d9eacc7895314fc8fefcb4e42946 \
|
||||
--hash=sha256:b4b1f6aff0192e9be153fe07c1b7576cb7a1ff52e78e3f76d867be95301a8e87 \
|
||||
--hash=sha256:bf03f06f586ce7d8aeb697a94b7dba12dc9271aae92d7a8d4486360ff711a660 \
|
||||
--hash=sha256:c76de162937326fcd57aa399f94939ce726242323e65c15c67e183da1f6c26f7 \
|
||||
--hash=sha256:d4ad1e46aef98783a9e6274d523b8b1e766acfc3d72828cd44a337564d984cfa \
|
||||
--hash=sha256:d4b5787a04613503357606bb10e59c3e2c1114fa00ee328b838dd257f41cbd7b \
|
||||
--hash=sha256:e0be1aa6566979266a8fc845ab0e18613f4918cf2c977fe67050f5dc7e2a9a97 \
|
||||
--hash=sha256:edfb16b16a298717cf05b8c8a9ad1828b6ff3de5e93048ceccd74e6ae4ff0922
|
||||
idna==2.6 \
|
||||
--hash=sha256:2c6a5de3089009e3da7c5dde64a141dbc8551d5b7f6cf4ed7c2568d0cc520a8f \
|
||||
--hash=sha256:8c7309c718f94b3a625cb648ace320157ad16ff131ae0af362c9f21b80ef6ec4
|
||||
keepkey==4.0.2 \
|
||||
--hash=sha256:cddee60ae405841cdff789cbc54168ceaeb2282633420f2be155554c25c69138
|
||||
libusb1==1.6.4 \
|
||||
--hash=sha256:8c930d9c1d037d9c83924c82608aa6a1adcaa01ca0e4a23ee0e8e18d7eee670d
|
||||
mnemonic==0.18 \
|
||||
--hash=sha256:02a7306a792370f4a0c106c2cf1ce5a0c84b9dbd7e71c6792fdb9ad88a727f1d
|
||||
pbkdf2==1.3 \
|
||||
--hash=sha256:ac6397369f128212c43064a2b4878038dab78dab41875364554aaf2a684e6979
|
||||
pip==9.0.3 \
|
||||
--hash=sha256:7bf48f9a693be1d58f49f7af7e0ae9fe29fd671cde8a55e6edca3581c4ef5796 \
|
||||
--hash=sha256:c3ede34530e0e0b2381e7363aded78e0c33291654937e7373032fda04e8803e5
|
||||
protobuf==3.5.2.post1 \
|
||||
--hash=sha256:01ccd6d03449ae75b779fb5bf4ed62177d61afe3c5e6465ccf3f8b2e1a84afbe \
|
||||
--hash=sha256:1d92cc30b0b46cced33adde5853d920179eb5ea8eecdee9552502a7f29cc3f21 \
|
||||
--hash=sha256:242e4c7ae565267a8bc8b92d707177f915607ea4bd73244bec6cbf4a49b96661 \
|
||||
--hash=sha256:3b60685732bd0cbdc802dfcb6071efbcf5d927ce3127c13c33ea1a8efae3aa76 \
|
||||
--hash=sha256:3f655e1f99c3e14d56ca900af1b9a4715b691319a295cc38939d7f77eabd5e7c \
|
||||
--hash=sha256:560a38e692a69957a70ba0e5839aa67430efd63072bf91b0539dac19055694cd \
|
||||
--hash=sha256:5c1c8f6a0a68a874e3beff89255959dd80fad45870e96c88944a1b81a22dd5f5 \
|
||||
--hash=sha256:628a3bf0794a8b3cabb18db11eb67cc10e0cc6e5525d557ae7b682bb73fa2018 \
|
||||
--hash=sha256:7222d6616108b33ad6cbeff8117062a73c43cdc8fa8f64f6a322ebeb663e710e \
|
||||
--hash=sha256:76ef6ca3c50e4cfd044861586d5f1b352e0fe7f17f883df6c165bad5b4d0e10a \
|
||||
--hash=sha256:7c193e6964e752bd056735594826c5b03274ceb8f07349d3ae47d9766250ba96 \
|
||||
--hash=sha256:869e12bcfb5759e683f53ec1dd6155b7be034065431da289f0cb4510040a0799 \
|
||||
--hash=sha256:905414e5ea6cdb78d8730f66335755152b46685fcb9fc2f2134024e3ea9e8dcc \
|
||||
--hash=sha256:ac0067e3c60737865ed72bb7416e02297d229d960902802d874c0e167128c809 \
|
||||
--hash=sha256:adf716a89c9cc1891ead79a861c427071ef59172f0e11967b00565a9547b3bd0 \
|
||||
--hash=sha256:bcfa99f5a82f5eaaf6e5cee5bfdca5a1670f5740aec1d93dae170645ed1a16b0 \
|
||||
--hash=sha256:cc94079ae6cbcea5ae194464a30f3223f075e06a0446f52bca9ddbeb6e9f412a \
|
||||
--hash=sha256:d5d9edfdc5a3a01d06062d677b121081629782edf0e05ca1be14f15bb947eeee \
|
||||
--hash=sha256:e269ab7a50bf0fa6fe6a88ea7dcc7a1079ae9450d9ab9b7730ac32916d55508b \
|
||||
--hash=sha256:e7fd33a3474cbe18fd5b5620784a0fa21fcae3e402b1806e29c6b450c7f61706
|
||||
pyblake2==1.1.1 \
|
||||
--hash=sha256:11c1d9d94cbaf5a4834aadf7f57bcb29eae1d174721269f242ca891f62cd6502 \
|
||||
--hash=sha256:427e7e91d644c3b9952e84145e211e4e3197fc4a3a0dbbd87b6da6b6cfa0a0df \
|
||||
--hash=sha256:4903d64e1a24f0cf2f8b8a1e0aaab12898951112b370ab9600651a4be4387c99 \
|
||||
--hash=sha256:6886b050521aed0293b2f67a3e1da74ea6080e4be19b57d9e1ae3d6ff10e223a \
|
||||
--hash=sha256:8cc4198ce61dddd33c9e66a216fc70be04fab66d02baa79e6bdebd83f16af57e \
|
||||
--hash=sha256:8ec8e9087d13c99b354ab6d8b4cadb1758633db5946ff95a6bc7ac538b6d7b3d \
|
||||
--hash=sha256:a785faf939810dca4aef525b6f59890fdcabdef09228cb30f4d77c3021707846 \
|
||||
--hash=sha256:e51b86e685045e2f8896d581b230effb1cc69f1134e11318f3607d98fa5ba95c \
|
||||
--hash=sha256:f51051de4eb27dc63c525a562daf9ead14e3e3583f096b9b90d3a360b5ca4995
|
||||
requests==2.18.4 \
|
||||
--hash=sha256:6a1b267aa90cac58ac3a765d067950e7dbbf75b1da07e895d1f594193a40a38b \
|
||||
--hash=sha256:9c443e7324ba5b85070c4a818ade28bfabedf16ea10206da1132edaa6dda237e
|
||||
rlp==0.6.0 \
|
||||
--hash=sha256:87879a0ba1479b760cee98af165de2eee95258b261faa293199f60742be96f34
|
||||
setuptools==39.0.1 \
|
||||
--hash=sha256:8010754433e3211b9cdbbf784b50f30e80bf40fc6b05eb5f865fab83300599b8 \
|
||||
--hash=sha256:bec7badf0f60e7fc8153fac47836edc41b74e5d541d7692e614e635720d6a7c7
|
||||
six==1.11.0 \
|
||||
--hash=sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9 \
|
||||
--hash=sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb
|
||||
trezor==0.9.1 \
|
||||
--hash=sha256:a481191011bade98f1e9f1201e7c72a83945050657bbc90dc4ac32dc8b8b46a4
|
||||
urllib3==1.22 \
|
||||
--hash=sha256:06330f386d6e4b195fbfc736b297f58c5a892e4440e54d294d7004e3a9bbea1b \
|
||||
--hash=sha256:cc44da8e1145637334317feebd728bd869a35285b93cbb4cca2577da7e62db4f
|
||||
wheel==0.31.0 \
|
||||
--hash=sha256:1ae8153bed701cb062913b72429bcf854ba824f973735427681882a688cb55ce \
|
||||
--hash=sha256:9cdc8ab2cc9c3c2e2727a4b67c22881dbb0e1c503d592992594c5e131c867107
|
||||
|
|
|
@ -1,14 +1,69 @@
|
|||
certifi==2018.1.18
|
||||
chardet==3.0.4
|
||||
dnspython==1.15.0
|
||||
ecdsa==0.13
|
||||
idna==2.6
|
||||
jsonrpclib-pelix==0.3.1
|
||||
pbkdf2==1.3
|
||||
protobuf==3.5.1
|
||||
pyaes==1.6.1
|
||||
PySocks==1.6.8
|
||||
qrcode==5.3
|
||||
requests==2.18.4
|
||||
six==1.11.0
|
||||
urllib3==1.22
|
||||
certifi==2018.1.18 \
|
||||
--hash=sha256:14131608ad2fd56836d33a71ee60fa1c82bc9d2c8d98b7bdbc631fe1b3cd1296 \
|
||||
--hash=sha256:edbc3f203427eef571f79a7692bb160a2b0f7ccaa31953e99bd17e307cf63f7d
|
||||
chardet==3.0.4 \
|
||||
--hash=sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae \
|
||||
--hash=sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691
|
||||
dnspython==1.15.0 \
|
||||
--hash=sha256:40f563e1f7a7b80dc5a4e76ad75c23da53d62f1e15e6e517293b04e1f84ead7c \
|
||||
--hash=sha256:861e6e58faa730f9845aaaa9c6c832851fbf89382ac52915a51f89c71accdd31
|
||||
ecdsa==0.13 \
|
||||
--hash=sha256:40d002cf360d0e035cf2cb985e1308d41aaa087cbfc135b2dc2d844296ea546c \
|
||||
--hash=sha256:64cf1ee26d1cde3c73c6d7d107f835fed7c6a2904aef9eac223d57ad800c43fa
|
||||
idna==2.6 \
|
||||
--hash=sha256:2c6a5de3089009e3da7c5dde64a141dbc8551d5b7f6cf4ed7c2568d0cc520a8f \
|
||||
--hash=sha256:8c7309c718f94b3a625cb648ace320157ad16ff131ae0af362c9f21b80ef6ec4
|
||||
jsonrpclib-pelix==0.3.1 \
|
||||
--hash=sha256:5417b1508d5a50ec64f6e5b88907f111155d52607b218ff3ba9a777afb2e49e3 \
|
||||
--hash=sha256:bd89a6093bc4d47dc8a096197aacb827359944a4533be5193f3845f57b9f91b4
|
||||
pbkdf2==1.3 \
|
||||
--hash=sha256:ac6397369f128212c43064a2b4878038dab78dab41875364554aaf2a684e6979
|
||||
pip==9.0.3 \
|
||||
--hash=sha256:7bf48f9a693be1d58f49f7af7e0ae9fe29fd671cde8a55e6edca3581c4ef5796 \
|
||||
--hash=sha256:c3ede34530e0e0b2381e7363aded78e0c33291654937e7373032fda04e8803e5
|
||||
protobuf==3.5.2.post1 \
|
||||
--hash=sha256:01ccd6d03449ae75b779fb5bf4ed62177d61afe3c5e6465ccf3f8b2e1a84afbe \
|
||||
--hash=sha256:1d92cc30b0b46cced33adde5853d920179eb5ea8eecdee9552502a7f29cc3f21 \
|
||||
--hash=sha256:242e4c7ae565267a8bc8b92d707177f915607ea4bd73244bec6cbf4a49b96661 \
|
||||
--hash=sha256:3b60685732bd0cbdc802dfcb6071efbcf5d927ce3127c13c33ea1a8efae3aa76 \
|
||||
--hash=sha256:3f655e1f99c3e14d56ca900af1b9a4715b691319a295cc38939d7f77eabd5e7c \
|
||||
--hash=sha256:560a38e692a69957a70ba0e5839aa67430efd63072bf91b0539dac19055694cd \
|
||||
--hash=sha256:5c1c8f6a0a68a874e3beff89255959dd80fad45870e96c88944a1b81a22dd5f5 \
|
||||
--hash=sha256:628a3bf0794a8b3cabb18db11eb67cc10e0cc6e5525d557ae7b682bb73fa2018 \
|
||||
--hash=sha256:7222d6616108b33ad6cbeff8117062a73c43cdc8fa8f64f6a322ebeb663e710e \
|
||||
--hash=sha256:76ef6ca3c50e4cfd044861586d5f1b352e0fe7f17f883df6c165bad5b4d0e10a \
|
||||
--hash=sha256:7c193e6964e752bd056735594826c5b03274ceb8f07349d3ae47d9766250ba96 \
|
||||
--hash=sha256:869e12bcfb5759e683f53ec1dd6155b7be034065431da289f0cb4510040a0799 \
|
||||
--hash=sha256:905414e5ea6cdb78d8730f66335755152b46685fcb9fc2f2134024e3ea9e8dcc \
|
||||
--hash=sha256:ac0067e3c60737865ed72bb7416e02297d229d960902802d874c0e167128c809 \
|
||||
--hash=sha256:adf716a89c9cc1891ead79a861c427071ef59172f0e11967b00565a9547b3bd0 \
|
||||
--hash=sha256:bcfa99f5a82f5eaaf6e5cee5bfdca5a1670f5740aec1d93dae170645ed1a16b0 \
|
||||
--hash=sha256:cc94079ae6cbcea5ae194464a30f3223f075e06a0446f52bca9ddbeb6e9f412a \
|
||||
--hash=sha256:d5d9edfdc5a3a01d06062d677b121081629782edf0e05ca1be14f15bb947eeee \
|
||||
--hash=sha256:e269ab7a50bf0fa6fe6a88ea7dcc7a1079ae9450d9ab9b7730ac32916d55508b \
|
||||
--hash=sha256:e7fd33a3474cbe18fd5b5620784a0fa21fcae3e402b1806e29c6b450c7f61706
|
||||
pyaes==1.6.1 \
|
||||
--hash=sha256:02c1b1405c38d3c370b085fb952dd8bea3fadcee6411ad99f312cc129c536d8f
|
||||
PySocks==1.6.8 \
|
||||
--hash=sha256:3fe52c55890a248676fd69dc9e3c4e811718b777834bcaab7a8125cf9deac672
|
||||
qrcode==6.0 \
|
||||
--hash=sha256:037b0db4c93f44586e37f84c3da3f763874fcac85b2974a69a98e399ac78e1bf \
|
||||
--hash=sha256:de4ffc15065e6ff20a551ad32b6b41264f3c75275675406ddfa8e3530d154be3
|
||||
requests==2.18.4 \
|
||||
--hash=sha256:6a1b267aa90cac58ac3a765d067950e7dbbf75b1da07e895d1f594193a40a38b \
|
||||
--hash=sha256:9c443e7324ba5b85070c4a818ade28bfabedf16ea10206da1132edaa6dda237e
|
||||
setuptools==39.0.1 \
|
||||
--hash=sha256:8010754433e3211b9cdbbf784b50f30e80bf40fc6b05eb5f865fab83300599b8 \
|
||||
--hash=sha256:bec7badf0f60e7fc8153fac47836edc41b74e5d541d7692e614e635720d6a7c7
|
||||
six==1.11.0 \
|
||||
--hash=sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9 \
|
||||
--hash=sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb
|
||||
urllib3==1.22 \
|
||||
--hash=sha256:06330f386d6e4b195fbfc736b297f58c5a892e4440e54d294d7004e3a9bbea1b \
|
||||
--hash=sha256:cc44da8e1145637334317feebd728bd869a35285b93cbb4cca2577da7e62db4f
|
||||
wheel==0.31.0 \
|
||||
--hash=sha256:1ae8153bed701cb062913b72429bcf854ba824f973735427681882a688cb55ce \
|
||||
--hash=sha256:9cdc8ab2cc9c3c2e2727a4b67c22881dbb0e1c503d592992594c5e131c867107
|
||||
colorama==0.3.9 \
|
||||
--hash=sha256:463f8483208e921368c9f306094eb6f725c6ca42b0f97e313cb5d5512459feda \
|
||||
--hash=sha256:48eb22f4f8461b1df5734a074b57042430fb06e1d61bd1e11b078c0fe6d7a1f1
|
||||
|
|
|
@ -5,18 +5,35 @@ venv_dir=~/.electrum-venv
|
|||
contrib=$(dirname "$0")
|
||||
|
||||
which virtualenv > /dev/null 2>&1 || { echo "Please install virtualenv" && exit 1; }
|
||||
python3 -m hashin -h > /dev/null 2>&1 || { python3 -m pip install hashin; }
|
||||
other_python=$(which python3)
|
||||
|
||||
for i in '' '-hw' '-binaries'; do
|
||||
rm "$venv_dir" -rf
|
||||
rm -rf "$venv_dir"
|
||||
virtualenv -p $(which python3) $venv_dir
|
||||
|
||||
source $venv_dir/bin/activate
|
||||
|
||||
echo "Installing $i dependencies"
|
||||
echo "Installing $m dependencies"
|
||||
|
||||
python -m pip install -r $contrib/requirements/requirements${i}.txt --upgrade
|
||||
|
||||
pip freeze | sed '/^Electrum/ d' > $contrib/deterministic-build/requirements${i}.txt
|
||||
echo "OK."
|
||||
|
||||
requirements=$(pip freeze --all)
|
||||
restricted=$(echo $requirements | $other_python $contrib/deterministic-build/find_restricted_dependencies.py)
|
||||
requirements="$requirements $restricted"
|
||||
|
||||
echo "Generating package hashes..."
|
||||
rm $contrib/deterministic-build/requirements${i}.txt
|
||||
touch $contrib/deterministic-build/requirements${i}.txt
|
||||
|
||||
for requirement in $requirements; do
|
||||
echo -e "\r Hashing $requirement..."
|
||||
$other_python -m hashin -r $contrib/deterministic-build/requirements${i}.txt ${requirement}
|
||||
done
|
||||
|
||||
echo "OK."
|
||||
done
|
||||
|
||||
echo "Done. Updated requirements"
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#!/usr/bin/env python3
|
||||
import os
|
||||
import subprocess
|
||||
import io
|
||||
import zipfile
|
||||
import requests
|
||||
|
@ -7,21 +8,31 @@ import requests
|
|||
os.chdir(os.path.dirname(os.path.realpath(__file__)))
|
||||
os.chdir('..')
|
||||
|
||||
code_directories = 'gui plugins lib'
|
||||
cmd = "find {} -type f -name '*.py' -o -name '*.kv'".format(code_directories)
|
||||
|
||||
files = subprocess.check_output(cmd, shell=True)
|
||||
|
||||
with open("app.fil", "wb") as f:
|
||||
f.write(files)
|
||||
|
||||
print("Found {} files to translate".format(len(files.splitlines())))
|
||||
|
||||
# Generate fresh translation template
|
||||
if not os.path.exists('lib/locale'):
|
||||
os.mkdir('lib/locale')
|
||||
cmd = 'xgettext -s --no-wrap -f app.fil --output=lib/locale/messages.pot'
|
||||
cmd = 'xgettext -s --from-code UTF-8 --language Python --no-wrap -f app.fil --output=lib/locale/messages.pot'
|
||||
print('Generate template')
|
||||
os.system(cmd)
|
||||
|
||||
os.chdir('lib')
|
||||
|
||||
crowdin_identifier = 'electrum'
|
||||
crowdin_file_name = 'electrum-client/messages.pot'
|
||||
crowdin_file_name = 'files[electrum-client/messages.pot]'
|
||||
locale_file_name = 'locale/messages.pot'
|
||||
crowdin_api_key = None
|
||||
|
||||
filename = '~/.crowdin_api_key'
|
||||
filename = os.path.expanduser('~/.crowdin_api_key')
|
||||
if os.path.exists(filename):
|
||||
with open(filename) as f:
|
||||
crowdin_api_key = f.read().strip()
|
||||
|
@ -33,13 +44,14 @@ if crowdin_api_key:
|
|||
# Push to Crowdin
|
||||
print('Push to Crowdin')
|
||||
url = ('https://api.crowdin.com/api/project/' + crowdin_identifier + '/update-file?key=' + crowdin_api_key)
|
||||
with open(locale_file_name,'rb') as f:
|
||||
with open(locale_file_name, 'rb') as f:
|
||||
files = {crowdin_file_name: f}
|
||||
requests.request('POST', url, files=files)
|
||||
response = requests.request('POST', url, files=files)
|
||||
print("", "update-file:", "-"*20, response.text, "-"*20, sep="\n")
|
||||
# Build translations
|
||||
print('Build translations')
|
||||
response = requests.request('GET', 'https://api.crowdin.com/api/project/' + crowdin_identifier + '/export?key=' + crowdin_api_key).content
|
||||
print(response)
|
||||
response = requests.request('GET', 'https://api.crowdin.com/api/project/' + crowdin_identifier + '/export?key=' + crowdin_api_key)
|
||||
print("", "export:", "-" * 20, response.text, "-" * 20, sep="\n")
|
||||
|
||||
# Download & unzip
|
||||
print('Download translations')
|
||||
|
|
22
electrum
22
electrum
|
@ -26,21 +26,6 @@
|
|||
import os
|
||||
import sys
|
||||
|
||||
# from https://gist.github.com/tito/09c42fb4767721dc323d
|
||||
import threading
|
||||
try:
|
||||
import jnius
|
||||
except:
|
||||
jnius = None
|
||||
if jnius:
|
||||
orig_thread_run = threading.Thread.run
|
||||
def thread_check_run(*args, **kwargs):
|
||||
try:
|
||||
return orig_thread_run(*args, **kwargs)
|
||||
finally:
|
||||
jnius.detach()
|
||||
threading.Thread.run = thread_check_run
|
||||
|
||||
script_dir = os.path.dirname(os.path.realpath(__file__))
|
||||
is_bundle = getattr(sys, 'frozen', False)
|
||||
is_local = not is_bundle and os.path.exists(os.path.join(script_dir, "electrum.desktop"))
|
||||
|
@ -93,7 +78,7 @@ from electrum import constants
|
|||
from electrum import SimpleConfig, Network
|
||||
from electrum.wallet import Wallet, Imported_Wallet
|
||||
from electrum.storage import WalletStorage, get_derivation_used_for_hw_device_encryption
|
||||
from electrum.util import print_msg, print_stderr, json_encode, json_decode
|
||||
from electrum.util import print_msg, print_stderr, json_encode, json_decode, UserCancelled
|
||||
from electrum.util import set_verbosity, InvalidPassword
|
||||
from electrum.commands import get_parser, known_commands, Commands, config_variables
|
||||
from electrum import daemon
|
||||
|
@ -295,7 +280,10 @@ def get_password_for_hw_device_encrypted_storage(plugins):
|
|||
name, device_info = devices[0]
|
||||
plugin = plugins.get_plugin(name)
|
||||
derivation = get_derivation_used_for_hw_device_encryption()
|
||||
xpub = plugin.get_xpub(device_info.device.id_, derivation, 'standard', plugin.handler)
|
||||
try:
|
||||
xpub = plugin.get_xpub(device_info.device.id_, derivation, 'standard', plugin.handler)
|
||||
except UserCancelled:
|
||||
sys.exit(0)
|
||||
password = keystore.Xpub.get_pubkey_from_xpub(xpub, ())
|
||||
return password
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
PYTHON_VER="$(python3 -c 'import sys; print(sys.version[:3])')"
|
||||
|
||||
cd $(dirname $0)
|
||||
if [ -e ./env/bin/activate ]; then
|
||||
source ./env/bin/activate
|
||||
else
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
[Desktop Entry]
|
||||
Comment=Lightweight Bitcoin Client
|
||||
Exec=electrum %u
|
||||
Exec=sh -c "PATH=\"\\$HOME/.local/bin:\\$PATH\" electrum %u"
|
||||
GenericName[en_US]=Bitcoin Wallet
|
||||
GenericName=Bitcoin Wallet
|
||||
Icon=electrum
|
||||
|
@ -14,4 +14,8 @@ StartupNotify=false
|
|||
Terminal=false
|
||||
Type=Application
|
||||
MimeType=x-scheme-handler/bitcoin;
|
||||
Actions=Testnet;
|
||||
|
||||
[Desktop Action Testnet]
|
||||
Exec=sh -c "PATH=\"\\$HOME/.local/bin:\\$PATH\" electrum --testnet %u"
|
||||
Name=Testnet mode
|
||||
|
|
|
@ -285,7 +285,8 @@
|
|||
|
||||
<KButton@Button>:
|
||||
size_hint: 1, None
|
||||
height: '48dp'
|
||||
height: '60dp'
|
||||
font_size: '30dp'
|
||||
on_release:
|
||||
self.parent.update_amount(self.text)
|
||||
|
||||
|
@ -372,9 +373,6 @@
|
|||
tab_height: '48dp'
|
||||
tab_width: panel.width/3
|
||||
strip_border: 0, 0, 0, 0
|
||||
InvoicesScreen:
|
||||
id: invoices_screen
|
||||
tab: invoices_tab
|
||||
SendScreen:
|
||||
id: send_screen
|
||||
tab: send_tab
|
||||
|
@ -384,29 +382,18 @@
|
|||
ReceiveScreen:
|
||||
id: receive_screen
|
||||
tab: receive_tab
|
||||
AddressScreen:
|
||||
id: address_screen
|
||||
tab: address_tab
|
||||
CleanHeader:
|
||||
id: invoices_tab
|
||||
text: _('Invoices')
|
||||
slide: 0
|
||||
CleanHeader:
|
||||
id: send_tab
|
||||
text: _('Send')
|
||||
slide: 1
|
||||
slide: 0
|
||||
CleanHeader:
|
||||
id: history_tab
|
||||
text: _('Balance')
|
||||
slide: 2
|
||||
slide: 1
|
||||
CleanHeader:
|
||||
id: receive_tab
|
||||
text: _('Receive')
|
||||
slide: 3
|
||||
CleanHeader:
|
||||
id: address_tab
|
||||
text: _('Addresses')
|
||||
slide: 4
|
||||
slide: 2
|
||||
|
||||
|
||||
<ActionOvrButton@ActionButton>
|
||||
|
|
|
@ -244,6 +244,7 @@ class ElectrumWindow(App):
|
|||
self.tabs = None
|
||||
self.is_exit = False
|
||||
self.wallet = None
|
||||
self.pause_time = 0
|
||||
|
||||
App.__init__(self)#, **kwargs)
|
||||
|
||||
|
@ -445,7 +446,6 @@ class ElectrumWindow(App):
|
|||
#win.softinput_mode = 'below_target'
|
||||
self.on_size(win, win.size)
|
||||
self.init_ui()
|
||||
self.load_wallet_by_name(self.electrum_config.get_wallet_path())
|
||||
# init plugins
|
||||
run_hook('init_kivy', self)
|
||||
# fiat currency
|
||||
|
@ -467,6 +467,8 @@ class ElectrumWindow(App):
|
|||
self.network.register_callback(self.on_fee, ['fee'])
|
||||
self.network.register_callback(self.on_quotes, ['on_quotes'])
|
||||
self.network.register_callback(self.on_history, ['on_history'])
|
||||
# load wallet
|
||||
self.load_wallet_by_name(self.electrum_config.get_wallet_path())
|
||||
# URI passed in config
|
||||
uri = self.electrum_config.get('url')
|
||||
if uri:
|
||||
|
@ -484,17 +486,18 @@ class ElectrumWindow(App):
|
|||
wallet.start_threads(self.daemon.network)
|
||||
self.daemon.add_wallet(wallet)
|
||||
self.load_wallet(wallet)
|
||||
self.on_resume()
|
||||
|
||||
def load_wallet_by_name(self, path):
|
||||
if not path:
|
||||
return
|
||||
if self.wallet and self.wallet.storage.path == path:
|
||||
return
|
||||
wallet = self.daemon.load_wallet(path, None)
|
||||
if wallet:
|
||||
if wallet != self.wallet:
|
||||
self.stop_wallet()
|
||||
if wallet.has_password():
|
||||
self.password_dialog(wallet, _('Enter PIN code'), lambda x: self.load_wallet(wallet), self.stop)
|
||||
else:
|
||||
self.load_wallet(wallet)
|
||||
self.on_resume()
|
||||
else:
|
||||
Logger.debug('Electrum: Wallet not found. Launching install wizard')
|
||||
storage = WalletStorage(path)
|
||||
|
@ -504,6 +507,7 @@ class ElectrumWindow(App):
|
|||
wizard.run(action)
|
||||
|
||||
def on_stop(self):
|
||||
Logger.info('on_stop')
|
||||
self.stop_wallet()
|
||||
|
||||
def stop_wallet(self):
|
||||
|
@ -617,6 +621,8 @@ class ElectrumWindow(App):
|
|||
|
||||
@profiler
|
||||
def load_wallet(self, wallet):
|
||||
if self.wallet:
|
||||
self.stop_wallet()
|
||||
self.wallet = wallet
|
||||
self.update_wallet()
|
||||
# Once GUI has been initialized check if we want to announce something
|
||||
|
@ -684,12 +690,16 @@ class ElectrumWindow(App):
|
|||
Logger.Error('Notification: needs plyer; `sudo pip install plyer`')
|
||||
|
||||
def on_pause(self):
|
||||
self.pause_time = time.time()
|
||||
# pause nfc
|
||||
if self.nfcscanner:
|
||||
self.nfcscanner.nfc_disable()
|
||||
return True
|
||||
|
||||
def on_resume(self):
|
||||
now = time.time()
|
||||
if self.wallet.has_password and now - self.pause_time > 60:
|
||||
self.password_dialog(self.wallet, _('Enter PIN'), None, self.stop)
|
||||
if self.nfcscanner:
|
||||
self.nfcscanner.nfc_enable()
|
||||
|
||||
|
@ -824,7 +834,6 @@ class ElectrumWindow(App):
|
|||
d = LabelDialog(_('Enter description'), text, callback)
|
||||
d.open()
|
||||
|
||||
@profiler
|
||||
def amount_dialog(self, screen, show_max):
|
||||
from .uix.dialogs.amount_dialog import AmountDialog
|
||||
amount = screen.amount
|
||||
|
@ -836,6 +845,34 @@ class ElectrumWindow(App):
|
|||
popup = AmountDialog(show_max, amount, cb)
|
||||
popup.open()
|
||||
|
||||
def invoices_dialog(self, screen):
|
||||
from .uix.dialogs.invoices import InvoicesDialog
|
||||
if len(self.wallet.invoices.sorted_list()) == 0:
|
||||
self.show_info(' '.join([
|
||||
_('No saved invoices.'),
|
||||
_('Signed invoices are saved automatically when you scan them.'),
|
||||
_('You may also save unsigned requests or contact addresses using the save button.')
|
||||
]))
|
||||
return
|
||||
popup = InvoicesDialog(self, screen, None)
|
||||
popup.update()
|
||||
popup.open()
|
||||
|
||||
def requests_dialog(self, screen):
|
||||
from .uix.dialogs.requests import RequestsDialog
|
||||
if len(self.wallet.get_sorted_requests(self.electrum_config)) == 0:
|
||||
self.show_info(_('No saved requests.'))
|
||||
return
|
||||
popup = RequestsDialog(self, screen, None)
|
||||
popup.update()
|
||||
popup.open()
|
||||
|
||||
def addresses_dialog(self, screen):
|
||||
from .uix.dialogs.addresses import AddressesDialog
|
||||
popup = AddressesDialog(self, screen, None)
|
||||
popup.update()
|
||||
popup.open()
|
||||
|
||||
def fee_dialog(self, label, dt):
|
||||
from .uix.dialogs.fee_dialog import FeeDialog
|
||||
def cb():
|
||||
|
@ -848,7 +885,8 @@ class ElectrumWindow(App):
|
|||
|
||||
def protected(self, msg, f, args):
|
||||
if self.wallet.has_password():
|
||||
self.password_dialog(msg, f, args)
|
||||
on_success = lambda pw: f(*(args + (pw,)))
|
||||
self.password_dialog(self.wallet, msg, on_success, lambda: None)
|
||||
else:
|
||||
f(*(args + (None,)))
|
||||
|
||||
|
@ -860,7 +898,7 @@ class ElectrumWindow(App):
|
|||
|
||||
def _delete_wallet(self, b):
|
||||
if b:
|
||||
basename = os.path.basename(self.wallet.storage.path)
|
||||
basename = self.wallet.basename()
|
||||
self.protected(_("Enter your PIN code to confirm deletion of {}").format(basename), self.__delete_wallet, ())
|
||||
|
||||
def __delete_wallet(self, pw):
|
||||
|
@ -898,40 +936,23 @@ class ElectrumWindow(App):
|
|||
if passphrase:
|
||||
label.text += '\n\n' + _('Passphrase') + ': ' + passphrase
|
||||
|
||||
def change_password(self, cb):
|
||||
if self.wallet.has_password():
|
||||
self.protected(_("Changing PIN code.") + '\n' + _("Enter your current PIN:"), self._change_password, (cb,))
|
||||
else:
|
||||
self._change_password(cb, None)
|
||||
|
||||
def _change_password(self, cb, old_password):
|
||||
if self.wallet.has_password():
|
||||
if old_password is None:
|
||||
return
|
||||
try:
|
||||
self.wallet.check_password(old_password)
|
||||
except InvalidPassword:
|
||||
self.show_error("Invalid PIN")
|
||||
return
|
||||
self.password_dialog(_('Enter new PIN'), self._change_password2, (cb, old_password,))
|
||||
|
||||
def _change_password2(self, cb, old_password, new_password):
|
||||
self.password_dialog(_('Confirm new PIN'), self._change_password3, (cb, old_password, new_password))
|
||||
|
||||
def _change_password3(self, cb, old_password, new_password, confirmed_password):
|
||||
if new_password == confirmed_password:
|
||||
self.wallet.update_password(old_password, new_password)
|
||||
cb()
|
||||
else:
|
||||
self.show_error("PIN numbers do not match")
|
||||
|
||||
def password_dialog(self, msg, f, args):
|
||||
def password_dialog(self, wallet, msg, on_success, on_failure):
|
||||
from .uix.dialogs.password_dialog import PasswordDialog
|
||||
def callback(pw):
|
||||
Clock.schedule_once(lambda x: f(*(args + (pw,))), 0.1)
|
||||
if self._password_dialog is None:
|
||||
self._password_dialog = PasswordDialog()
|
||||
self._password_dialog.init(msg, callback)
|
||||
self._password_dialog.init(self, wallet, msg, on_success, on_failure)
|
||||
self._password_dialog.open()
|
||||
|
||||
def change_password(self, cb):
|
||||
from .uix.dialogs.password_dialog import PasswordDialog
|
||||
if self._password_dialog is None:
|
||||
self._password_dialog = PasswordDialog()
|
||||
message = _("Changing PIN code.") + '\n' + _("Enter your current PIN:")
|
||||
def on_success(old_password, new_password):
|
||||
self.wallet.update_password(old_password, new_password)
|
||||
self.show_info(_("Your PIN code was updated"))
|
||||
on_failure = lambda: self.show_error(_("PIN codes do not match"))
|
||||
self._password_dialog.init(self, self.wallet, message, on_success, on_failure, is_change=1)
|
||||
self._password_dialog.open()
|
||||
|
||||
def export_private_keys(self, pk_label, addr):
|
||||
|
|
BIN
gui/kivy/theming/light/share.png
Normal file
BIN
gui/kivy/theming/light/share.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.2 KiB |
|
@ -47,7 +47,6 @@ class ContextMenu(Bubble):
|
|||
l = MenuItem()
|
||||
l.text = _(k)
|
||||
def func(f=v):
|
||||
Clock.schedule_once(lambda dt: self.hide(), 0.1)
|
||||
Clock.schedule_once(lambda dt: f(obj), 0.15)
|
||||
l.on_release = func
|
||||
self.ids.buttons.add_widget(l)
|
||||
|
|
216
gui/kivy/uix/dialogs/addresses.py
Normal file
216
gui/kivy/uix/dialogs/addresses.py
Normal file
|
@ -0,0 +1,216 @@
|
|||
from kivy.app import App
|
||||
from kivy.factory import Factory
|
||||
from kivy.properties import ObjectProperty
|
||||
from kivy.lang import Builder
|
||||
from decimal import Decimal
|
||||
|
||||
Builder.load_string('''
|
||||
<AddressLabel@Label>
|
||||
text_size: self.width, None
|
||||
halign: 'left'
|
||||
valign: 'top'
|
||||
|
||||
<AddressItem@CardItem>
|
||||
address: ''
|
||||
memo: ''
|
||||
amount: ''
|
||||
status: ''
|
||||
BoxLayout:
|
||||
spacing: '8dp'
|
||||
height: '32dp'
|
||||
orientation: 'vertical'
|
||||
Widget
|
||||
AddressLabel:
|
||||
text: root.address
|
||||
shorten: True
|
||||
Widget
|
||||
AddressLabel:
|
||||
text: (root.amount if root.status == 'Funded' else root.status) + ' ' + root.memo
|
||||
color: .699, .699, .699, 1
|
||||
font_size: '13sp'
|
||||
shorten: True
|
||||
Widget
|
||||
|
||||
<AddressesDialog@Popup>
|
||||
id: popup
|
||||
title: _('Addresses')
|
||||
message: ''
|
||||
pr_status: 'Pending'
|
||||
show_change: 0
|
||||
show_used: 0
|
||||
on_message:
|
||||
self.update()
|
||||
BoxLayout:
|
||||
id:box
|
||||
padding: '12dp', '70dp', '12dp', '12dp'
|
||||
spacing: '12dp'
|
||||
orientation: 'vertical'
|
||||
size_hint: 1, 1.1
|
||||
BoxLayout:
|
||||
spacing: '6dp'
|
||||
size_hint: 1, None
|
||||
orientation: 'horizontal'
|
||||
AddressFilter:
|
||||
opacity: 1
|
||||
size_hint: 1, None
|
||||
height: self.minimum_height
|
||||
spacing: '5dp'
|
||||
AddressButton:
|
||||
id: search
|
||||
text: {0:_('Receiving'), 1:_('Change'), 2:_('All')}[root.show_change]
|
||||
on_release:
|
||||
root.show_change = (root.show_change + 1) % 3
|
||||
Clock.schedule_once(lambda dt: root.update())
|
||||
AddressFilter:
|
||||
opacity: 1
|
||||
size_hint: 1, None
|
||||
height: self.minimum_height
|
||||
spacing: '5dp'
|
||||
AddressButton:
|
||||
id: search
|
||||
text: {0:_('All'), 1:_('Unused'), 2:_('Funded'), 3:_('Used')}[root.show_used]
|
||||
on_release:
|
||||
root.show_used = (root.show_used + 1) % 4
|
||||
Clock.schedule_once(lambda dt: root.update())
|
||||
AddressFilter:
|
||||
opacity: 1
|
||||
size_hint: 1, None
|
||||
height: self.minimum_height
|
||||
spacing: '5dp'
|
||||
canvas.before:
|
||||
Color:
|
||||
rgba: 0.9, 0.9, 0.9, 1
|
||||
AddressButton:
|
||||
id: change
|
||||
text: root.message if root.message else _('Search')
|
||||
on_release: Clock.schedule_once(lambda dt: app.description_dialog(popup))
|
||||
ScrollView:
|
||||
GridLayout:
|
||||
cols: 1
|
||||
id: search_container
|
||||
size_hint_y: None
|
||||
height: self.minimum_height
|
||||
''')
|
||||
|
||||
|
||||
from electrum_gui.kivy.i18n import _
|
||||
from electrum_gui.kivy.uix.context_menu import ContextMenu
|
||||
|
||||
|
||||
class EmptyLabel(Factory.Label):
|
||||
pass
|
||||
|
||||
|
||||
class AddressesDialog(Factory.Popup):
|
||||
|
||||
def __init__(self, app, screen, callback):
|
||||
Factory.Popup.__init__(self)
|
||||
self.app = app
|
||||
self.screen = screen
|
||||
self.callback = callback
|
||||
self.cards = {}
|
||||
self.context_menu = None
|
||||
|
||||
def get_card(self, addr, balance, is_used, label):
|
||||
ci = self.cards.get(addr)
|
||||
if ci is None:
|
||||
ci = Factory.AddressItem()
|
||||
ci.screen = self
|
||||
ci.address = addr
|
||||
self.cards[addr] = ci
|
||||
|
||||
ci.memo = label
|
||||
ci.amount = self.app.format_amount_and_units(balance)
|
||||
request = self.app.wallet.get_payment_request(addr, self.app.electrum_config)
|
||||
if is_used:
|
||||
ci.status = _('Used')
|
||||
else:
|
||||
ci.status = _('Funded') if balance > 0 else _('Unused')
|
||||
return ci
|
||||
|
||||
|
||||
def update(self):
|
||||
self.menu_actions = [(_('Use'), self.do_show), (_('Details'), self.do_view)]
|
||||
wallet = self.app.wallet
|
||||
if self.show_change == 0:
|
||||
_list = wallet.get_receiving_addresses()
|
||||
elif self.show_change == 1:
|
||||
_list = wallet.get_change_addresses()
|
||||
else:
|
||||
_list = wallet.get_addresses()
|
||||
search = self.message
|
||||
container = self.ids.search_container
|
||||
container.clear_widgets()
|
||||
n = 0
|
||||
for address in _list:
|
||||
label = wallet.labels.get(address, '')
|
||||
balance = sum(wallet.get_addr_balance(address))
|
||||
is_used = wallet.is_used(address)
|
||||
if self.show_used == 1 and (balance or is_used):
|
||||
continue
|
||||
if self.show_used == 2 and balance == 0:
|
||||
continue
|
||||
if self.show_used == 3 and not is_used:
|
||||
continue
|
||||
card = self.get_card(address, balance, is_used, label)
|
||||
if search and not self.ext_search(card, search):
|
||||
continue
|
||||
container.add_widget(card)
|
||||
n += 1
|
||||
if not n:
|
||||
msg = _('No address matching your search')
|
||||
container.add_widget(EmptyLabel(text=msg))
|
||||
|
||||
def do_show(self, obj):
|
||||
self.hide_menu()
|
||||
self.dismiss()
|
||||
self.app.show_request(obj.address)
|
||||
|
||||
def do_view(self, obj):
|
||||
req = self.app.wallet.get_payment_request(obj.address, self.app.electrum_config)
|
||||
if req:
|
||||
c, u, x = self.app.wallet.get_addr_balance(obj.address)
|
||||
balance = c + u + x
|
||||
if balance > 0:
|
||||
req['fund'] = balance
|
||||
status = req.get('status')
|
||||
amount = req.get('amount')
|
||||
address = req['address']
|
||||
if amount:
|
||||
status = req.get('status')
|
||||
status = request_text[status]
|
||||
else:
|
||||
received_amount = self.app.wallet.get_addr_received(address)
|
||||
status = self.app.format_amount_and_units(received_amount)
|
||||
self.app.show_pr_details(req, status, False)
|
||||
|
||||
else:
|
||||
req = { 'address': obj.address, 'status' : obj.status }
|
||||
status = obj.status
|
||||
c, u, x = self.app.wallet.get_addr_balance(obj.address)
|
||||
balance = c + u + x
|
||||
if balance > 0:
|
||||
req['fund'] = balance
|
||||
self.app.show_addr_details(req, status)
|
||||
|
||||
def do_delete(self, obj):
|
||||
from .dialogs.question import Question
|
||||
def cb(result):
|
||||
if result:
|
||||
self.app.wallet.remove_payment_request(obj.address, self.app.electrum_config)
|
||||
self.update()
|
||||
d = Question(_('Delete request?'), cb)
|
||||
d.open()
|
||||
|
||||
def ext_search(self, card, search):
|
||||
return card.memo.find(search) >= 0 or card.amount.find(search) >= 0
|
||||
|
||||
def show_menu(self, obj):
|
||||
self.hide_menu()
|
||||
self.context_menu = ContextMenu(obj, self.menu_actions)
|
||||
self.ids.box.add_widget(self.context_menu)
|
||||
|
||||
def hide_menu(self):
|
||||
if self.context_menu is not None:
|
||||
self.ids.box.remove_widget(self.context_menu)
|
||||
self.context_menu = None
|
|
@ -802,27 +802,17 @@ class InstallWizard(BaseWizard, Widget):
|
|||
app = App.get_running_app()
|
||||
Clock.schedule_once(lambda dt: app.show_error(msg))
|
||||
|
||||
def password_dialog(self, message, callback):
|
||||
popup = PasswordDialog()
|
||||
popup.init(message, callback)
|
||||
popup.open()
|
||||
|
||||
def request_password(self, run_next, force_disable_encrypt_cb=False):
|
||||
def callback(pin):
|
||||
if pin:
|
||||
self.run('confirm_password', pin, run_next)
|
||||
else:
|
||||
run_next(None, None)
|
||||
self.password_dialog('Choose a PIN code', callback)
|
||||
|
||||
def confirm_password(self, pin, run_next):
|
||||
def callback(conf):
|
||||
if conf == pin:
|
||||
run_next(pin, False)
|
||||
else:
|
||||
self.show_error(_('PIN mismatch'))
|
||||
self.run('request_password', run_next)
|
||||
self.password_dialog('Confirm your PIN code', callback)
|
||||
def on_success(old_pin, pin):
|
||||
assert old_pin is None
|
||||
run_next(pin, False)
|
||||
def on_failure():
|
||||
self.show_error(_('PIN mismatch'))
|
||||
self.run('request_password', run_next)
|
||||
popup = PasswordDialog()
|
||||
app = App.get_running_app()
|
||||
popup.init(app, None, _('Choose PIN code'), on_success, on_failure, is_change=2)
|
||||
popup.open()
|
||||
|
||||
def action_dialog(self, action, run_next):
|
||||
f = getattr(self, action)
|
||||
|
|
169
gui/kivy/uix/dialogs/invoices.py
Normal file
169
gui/kivy/uix/dialogs/invoices.py
Normal file
|
@ -0,0 +1,169 @@
|
|||
from kivy.app import App
|
||||
from kivy.factory import Factory
|
||||
from kivy.properties import ObjectProperty
|
||||
from kivy.lang import Builder
|
||||
from decimal import Decimal
|
||||
|
||||
Builder.load_string('''
|
||||
<InvoicesLabel@Label>
|
||||
#color: .305, .309, .309, 1
|
||||
text_size: self.width, None
|
||||
halign: 'left'
|
||||
valign: 'top'
|
||||
|
||||
<InvoiceItem@CardItem>
|
||||
requestor: ''
|
||||
memo: ''
|
||||
amount: ''
|
||||
status: ''
|
||||
date: ''
|
||||
icon: 'atlas://gui/kivy/theming/light/important'
|
||||
Image:
|
||||
id: icon
|
||||
source: root.icon
|
||||
size_hint: None, 1
|
||||
width: self.height *.54
|
||||
mipmap: True
|
||||
BoxLayout:
|
||||
spacing: '8dp'
|
||||
height: '32dp'
|
||||
orientation: 'vertical'
|
||||
Widget
|
||||
InvoicesLabel:
|
||||
text: root.requestor
|
||||
shorten: True
|
||||
Widget
|
||||
InvoicesLabel:
|
||||
text: root.memo
|
||||
color: .699, .699, .699, 1
|
||||
font_size: '13sp'
|
||||
shorten: True
|
||||
Widget
|
||||
BoxLayout:
|
||||
spacing: '8dp'
|
||||
height: '32dp'
|
||||
orientation: 'vertical'
|
||||
Widget
|
||||
InvoicesLabel:
|
||||
text: root.amount
|
||||
font_size: '15sp'
|
||||
halign: 'right'
|
||||
width: '110sp'
|
||||
Widget
|
||||
InvoicesLabel:
|
||||
text: root.status
|
||||
font_size: '13sp'
|
||||
halign: 'right'
|
||||
color: .699, .699, .699, 1
|
||||
Widget
|
||||
|
||||
|
||||
<InvoicesDialog@Popup>
|
||||
id: popup
|
||||
title: _('Invoices')
|
||||
BoxLayout:
|
||||
id: box
|
||||
orientation: 'vertical'
|
||||
spacing: '1dp'
|
||||
ScrollView:
|
||||
GridLayout:
|
||||
cols: 1
|
||||
id: invoices_container
|
||||
size_hint: 1, None
|
||||
height: self.minimum_height
|
||||
spacing: '2dp'
|
||||
padding: '12dp'
|
||||
''')
|
||||
|
||||
from kivy.properties import BooleanProperty
|
||||
from electrum_gui.kivy.i18n import _
|
||||
from electrum.util import format_time
|
||||
from electrum.paymentrequest import PR_UNPAID, PR_PAID, PR_UNKNOWN, PR_EXPIRED
|
||||
from electrum_gui.kivy.uix.context_menu import ContextMenu
|
||||
|
||||
invoice_text = {
|
||||
PR_UNPAID:_('Pending'),
|
||||
PR_UNKNOWN:_('Unknown'),
|
||||
PR_PAID:_('Paid'),
|
||||
PR_EXPIRED:_('Expired')
|
||||
}
|
||||
pr_icon = {
|
||||
PR_UNPAID: 'atlas://gui/kivy/theming/light/important',
|
||||
PR_UNKNOWN: 'atlas://gui/kivy/theming/light/important',
|
||||
PR_PAID: 'atlas://gui/kivy/theming/light/confirmed',
|
||||
PR_EXPIRED: 'atlas://gui/kivy/theming/light/close'
|
||||
}
|
||||
|
||||
|
||||
class InvoicesDialog(Factory.Popup):
|
||||
|
||||
def __init__(self, app, screen, callback):
|
||||
Factory.Popup.__init__(self)
|
||||
self.app = app
|
||||
self.screen = screen
|
||||
self.callback = callback
|
||||
self.cards = {}
|
||||
self.context_menu = None
|
||||
|
||||
def get_card(self, pr):
|
||||
key = pr.get_id()
|
||||
ci = self.cards.get(key)
|
||||
if ci is None:
|
||||
ci = Factory.InvoiceItem()
|
||||
ci.key = key
|
||||
ci.screen = self
|
||||
self.cards[key] = ci
|
||||
ci.requestor = pr.get_requestor()
|
||||
ci.memo = pr.get_memo()
|
||||
amount = pr.get_amount()
|
||||
if amount:
|
||||
ci.amount = self.app.format_amount_and_units(amount)
|
||||
status = self.app.wallet.invoices.get_status(ci.key)
|
||||
ci.status = invoice_text[status]
|
||||
ci.icon = pr_icon[status]
|
||||
else:
|
||||
ci.amount = _('No Amount')
|
||||
ci.status = ''
|
||||
exp = pr.get_expiration_date()
|
||||
ci.date = format_time(exp) if exp else _('Never')
|
||||
return ci
|
||||
|
||||
def update(self):
|
||||
self.menu_actions = [('Pay', self.do_pay), ('Details', self.do_view), ('Delete', self.do_delete)]
|
||||
invoices_list = self.ids.invoices_container
|
||||
invoices_list.clear_widgets()
|
||||
_list = self.app.wallet.invoices.sorted_list()
|
||||
for pr in _list:
|
||||
ci = self.get_card(pr)
|
||||
invoices_list.add_widget(ci)
|
||||
|
||||
def do_pay(self, obj):
|
||||
self.hide_menu()
|
||||
self.dismiss()
|
||||
pr = self.app.wallet.invoices.get(obj.key)
|
||||
self.app.on_pr(pr)
|
||||
|
||||
def do_view(self, obj):
|
||||
pr = self.app.wallet.invoices.get(obj.key)
|
||||
pr.verify(self.app.wallet.contacts)
|
||||
self.app.show_pr_details(pr.get_dict(), obj.status, True)
|
||||
|
||||
def do_delete(self, obj):
|
||||
from .question import Question
|
||||
def cb(result):
|
||||
if result:
|
||||
self.app.wallet.invoices.remove(obj.key)
|
||||
self.hide_menu()
|
||||
self.update()
|
||||
d = Question(_('Delete invoice?'), cb)
|
||||
d.open()
|
||||
|
||||
def show_menu(self, obj):
|
||||
self.hide_menu()
|
||||
self.context_menu = ContextMenu(obj, self.menu_actions)
|
||||
self.ids.box.add_widget(self.context_menu)
|
||||
|
||||
def hide_menu(self):
|
||||
if self.context_menu is not None:
|
||||
self.ids.box.remove_widget(self.context_menu)
|
||||
self.context_menu = None
|
|
@ -5,35 +5,42 @@ from kivy.lang import Builder
|
|||
from decimal import Decimal
|
||||
from kivy.clock import Clock
|
||||
|
||||
from electrum.util import InvalidPassword
|
||||
from electrum_gui.kivy.i18n import _
|
||||
|
||||
Builder.load_string('''
|
||||
|
||||
<PasswordDialog@Popup>
|
||||
id: popup
|
||||
title: _('PIN Code')
|
||||
title: 'Electrum'
|
||||
message: ''
|
||||
size_hint: 0.9, 0.9
|
||||
BoxLayout:
|
||||
size_hint: 1, 1
|
||||
orientation: 'vertical'
|
||||
Widget:
|
||||
size_hint: 1, 1
|
||||
size_hint: 1, 0.05
|
||||
Label:
|
||||
font_size: '20dp'
|
||||
text: root.message
|
||||
text_size: self.width, None
|
||||
size: self.texture_size
|
||||
Widget:
|
||||
size_hint: 1, 1
|
||||
size_hint: 1, 0.05
|
||||
Label:
|
||||
id: a
|
||||
text: ' * '*len(kb.password) + ' o '*(6-len(kb.password))
|
||||
font_size: '50dp'
|
||||
text: '*'*len(kb.password) + '-'*(6-len(kb.password))
|
||||
size: self.texture_size
|
||||
Widget:
|
||||
size_hint: 1, 1
|
||||
size_hint: 1, 0.05
|
||||
GridLayout:
|
||||
id: kb
|
||||
size_hint: 1, None
|
||||
height: self.minimum_height
|
||||
update_amount: popup.update_password
|
||||
password: ''
|
||||
on_password: popup.on_password(self.password)
|
||||
size_hint: 1, None
|
||||
height: '200dp'
|
||||
spacing: '2dp'
|
||||
cols: 3
|
||||
KButton:
|
||||
text: '1'
|
||||
|
@ -59,30 +66,44 @@ Builder.load_string('''
|
|||
text: '0'
|
||||
KButton:
|
||||
text: '<'
|
||||
BoxLayout:
|
||||
size_hint: 1, None
|
||||
height: '48dp'
|
||||
Widget:
|
||||
size_hint: 0.5, None
|
||||
Button:
|
||||
size_hint: 0.5, None
|
||||
height: '48dp'
|
||||
text: _('Cancel')
|
||||
on_release:
|
||||
popup.dismiss()
|
||||
popup.callback(None)
|
||||
''')
|
||||
|
||||
|
||||
class PasswordDialog(Factory.Popup):
|
||||
|
||||
#def __init__(self, message, callback):
|
||||
# Factory.Popup.__init__(self)
|
||||
|
||||
def init(self, message, callback):
|
||||
def init(self, app, wallet, message, on_success, on_failure, is_change=0):
|
||||
self.app = app
|
||||
self.wallet = wallet
|
||||
self.message = message
|
||||
self.callback = callback
|
||||
self.on_success = on_success
|
||||
self.on_failure = on_failure
|
||||
self.ids.kb.password = ''
|
||||
self.success = False
|
||||
self.is_change = is_change
|
||||
self.pw = None
|
||||
self.new_password = None
|
||||
self.title = 'Electrum' + (' - ' + self.wallet.basename() if self.wallet else '')
|
||||
|
||||
def check_password(self, password):
|
||||
if self.is_change > 1:
|
||||
return True
|
||||
try:
|
||||
self.wallet.check_password(password)
|
||||
return True
|
||||
except InvalidPassword as e:
|
||||
return False
|
||||
|
||||
def on_dismiss(self):
|
||||
if not self.success:
|
||||
if self.on_failure:
|
||||
self.on_failure()
|
||||
else:
|
||||
# keep dialog open
|
||||
return True
|
||||
else:
|
||||
if self.on_success:
|
||||
args = (self.pw, self.new_password) if self.is_change else (self.pw,)
|
||||
Clock.schedule_once(lambda dt: self.on_success(*args), 0.1)
|
||||
|
||||
def update_password(self, c):
|
||||
kb = self.ids.kb
|
||||
|
@ -97,5 +118,25 @@ class PasswordDialog(Factory.Popup):
|
|||
|
||||
def on_password(self, pw):
|
||||
if len(pw) == 6:
|
||||
self.dismiss()
|
||||
Clock.schedule_once(lambda dt: self.callback(pw), 0.1)
|
||||
if self.check_password(pw):
|
||||
if self.is_change == 0:
|
||||
self.success = True
|
||||
self.pw = pw
|
||||
self.message = _('Please wait...')
|
||||
self.dismiss()
|
||||
elif self.is_change == 1:
|
||||
self.pw = pw
|
||||
self.message = _('Enter new PIN')
|
||||
self.ids.kb.password = ''
|
||||
self.is_change = 2
|
||||
elif self.is_change == 2:
|
||||
self.new_password = pw
|
||||
self.message = _('Confirm new PIN')
|
||||
self.ids.kb.password = ''
|
||||
self.is_change = 3
|
||||
elif self.is_change == 3:
|
||||
self.success = pw == self.new_password
|
||||
self.dismiss()
|
||||
else:
|
||||
self.app.show_error(_('Wrong PIN'))
|
||||
self.ids.kb.password = ''
|
||||
|
|
157
gui/kivy/uix/dialogs/requests.py
Normal file
157
gui/kivy/uix/dialogs/requests.py
Normal file
|
@ -0,0 +1,157 @@
|
|||
from kivy.app import App
|
||||
from kivy.factory import Factory
|
||||
from kivy.properties import ObjectProperty
|
||||
from kivy.lang import Builder
|
||||
from decimal import Decimal
|
||||
|
||||
Builder.load_string('''
|
||||
<RequestLabel@Label>
|
||||
#color: .305, .309, .309, 1
|
||||
text_size: self.width, None
|
||||
halign: 'left'
|
||||
valign: 'top'
|
||||
|
||||
<RequestItem@CardItem>
|
||||
address: ''
|
||||
memo: ''
|
||||
amount: ''
|
||||
status: ''
|
||||
date: ''
|
||||
icon: 'atlas://gui/kivy/theming/light/important'
|
||||
Image:
|
||||
id: icon
|
||||
source: root.icon
|
||||
size_hint: None, 1
|
||||
width: self.height *.54
|
||||
mipmap: True
|
||||
BoxLayout:
|
||||
spacing: '8dp'
|
||||
height: '32dp'
|
||||
orientation: 'vertical'
|
||||
Widget
|
||||
RequestLabel:
|
||||
text: root.address
|
||||
shorten: True
|
||||
Widget
|
||||
RequestLabel:
|
||||
text: root.memo
|
||||
color: .699, .699, .699, 1
|
||||
font_size: '13sp'
|
||||
shorten: True
|
||||
Widget
|
||||
BoxLayout:
|
||||
spacing: '8dp'
|
||||
height: '32dp'
|
||||
orientation: 'vertical'
|
||||
Widget
|
||||
RequestLabel:
|
||||
text: root.amount
|
||||
halign: 'right'
|
||||
font_size: '15sp'
|
||||
Widget
|
||||
RequestLabel:
|
||||
text: root.status
|
||||
halign: 'right'
|
||||
font_size: '13sp'
|
||||
color: .699, .699, .699, 1
|
||||
Widget
|
||||
|
||||
<RequestsDialog@Popup>
|
||||
id: popup
|
||||
title: _('Requests')
|
||||
BoxLayout:
|
||||
id:box
|
||||
orientation: 'vertical'
|
||||
spacing: '1dp'
|
||||
ScrollView:
|
||||
GridLayout:
|
||||
cols: 1
|
||||
id: requests_container
|
||||
size_hint: 1, None
|
||||
height: self.minimum_height
|
||||
spacing: '2dp'
|
||||
padding: '12dp'
|
||||
''')
|
||||
|
||||
from kivy.properties import BooleanProperty
|
||||
from electrum_gui.kivy.i18n import _
|
||||
from electrum.util import format_time
|
||||
from electrum.paymentrequest import PR_UNPAID, PR_PAID, PR_UNKNOWN, PR_EXPIRED
|
||||
from electrum_gui.kivy.uix.context_menu import ContextMenu
|
||||
|
||||
pr_icon = {
|
||||
PR_UNPAID: 'atlas://gui/kivy/theming/light/important',
|
||||
PR_UNKNOWN: 'atlas://gui/kivy/theming/light/important',
|
||||
PR_PAID: 'atlas://gui/kivy/theming/light/confirmed',
|
||||
PR_EXPIRED: 'atlas://gui/kivy/theming/light/close'
|
||||
}
|
||||
request_text = {
|
||||
PR_UNPAID: _('Pending'),
|
||||
PR_UNKNOWN: _('Unknown'),
|
||||
PR_PAID: _('Received'),
|
||||
PR_EXPIRED: _('Expired')
|
||||
}
|
||||
|
||||
|
||||
class RequestsDialog(Factory.Popup):
|
||||
|
||||
def __init__(self, app, screen, callback):
|
||||
Factory.Popup.__init__(self)
|
||||
self.app = app
|
||||
self.screen = screen
|
||||
self.callback = callback
|
||||
self.cards = {}
|
||||
self.context_menu = None
|
||||
|
||||
def get_card(self, req):
|
||||
address = req['address']
|
||||
ci = self.cards.get(address)
|
||||
if ci is None:
|
||||
ci = Factory.RequestItem()
|
||||
ci.address = address
|
||||
ci.screen = self
|
||||
self.cards[address] = ci
|
||||
|
||||
amount = req.get('amount')
|
||||
ci.amount = self.app.format_amount_and_units(amount) if amount else ''
|
||||
ci.memo = req.get('memo', '')
|
||||
status, conf = self.app.wallet.get_request_status(address)
|
||||
ci.status = request_text[status]
|
||||
ci.icon = pr_icon[status]
|
||||
#exp = pr.get_expiration_date()
|
||||
#ci.date = format_time(exp) if exp else _('Never')
|
||||
return ci
|
||||
|
||||
def update(self):
|
||||
self.menu_actions = [(_('Show'), self.do_show), (_('Delete'), self.do_delete)]
|
||||
requests_list = self.ids.requests_container
|
||||
requests_list.clear_widgets()
|
||||
_list = self.app.wallet.get_sorted_requests(self.app.electrum_config)
|
||||
for pr in _list:
|
||||
ci = self.get_card(pr)
|
||||
requests_list.add_widget(ci)
|
||||
|
||||
def do_show(self, obj):
|
||||
self.hide_menu()
|
||||
self.dismiss()
|
||||
self.app.show_request(obj.address)
|
||||
|
||||
def do_delete(self, req):
|
||||
from .question import Question
|
||||
def cb(result):
|
||||
if result:
|
||||
self.app.wallet.remove_payment_request(req.address, self.app.electrum_config)
|
||||
self.hide_menu()
|
||||
self.update()
|
||||
d = Question(_('Delete request'), cb)
|
||||
d.open()
|
||||
|
||||
def show_menu(self, obj):
|
||||
self.hide_menu()
|
||||
self.context_menu = ContextMenu(obj, self.menu_actions)
|
||||
self.ids.box.add_widget(self.context_menu)
|
||||
|
||||
def hide_menu(self):
|
||||
if self.context_menu is not None:
|
||||
self.ids.box.remove_widget(self.context_menu)
|
||||
self.context_menu = None
|
|
@ -36,9 +36,8 @@ Builder.load_string('''
|
|||
action: partial(root.language_dialog, self)
|
||||
CardSeparator
|
||||
SettingsItem:
|
||||
status: '' if root.disable_pin else ('ON' if root.use_encryption else 'OFF')
|
||||
disabled: root.disable_pin
|
||||
title: _('PIN code') + ': ' + self.status
|
||||
title: _('PIN code')
|
||||
description: _("Change your PIN code.")
|
||||
action: partial(root.change_password, self)
|
||||
CardSeparator
|
||||
|
|
|
@ -7,7 +7,7 @@ from kivy.uix.bubble import Bubble, BubbleButton
|
|||
from kivy.properties import ListProperty
|
||||
from kivy.uix.widget import Widget
|
||||
|
||||
from electrum_gui.i18n import _
|
||||
from electrum_gui.kivy.i18n import _
|
||||
|
||||
class ContextMenuItem(Widget):
|
||||
'''abstract class
|
||||
|
|
|
@ -27,8 +27,6 @@ from .context_menu import ContextMenu
|
|||
|
||||
from electrum_gui.kivy.i18n import _
|
||||
|
||||
class EmptyLabel(Factory.Label):
|
||||
pass
|
||||
|
||||
class CScreen(Factory.Screen):
|
||||
__events__ = ('on_activate', 'on_deactivate', 'on_enter', 'on_leave')
|
||||
|
@ -219,7 +217,6 @@ class SendScreen(CScreen):
|
|||
pr = make_unsigned_request(req).SerializeToString()
|
||||
pr = PaymentRequest(pr)
|
||||
self.app.wallet.invoices.add(pr)
|
||||
self.app.update_tab('invoices')
|
||||
self.app.show_info(_("Invoice saved"))
|
||||
if pr.is_pr():
|
||||
self.screen.is_pr = True
|
||||
|
@ -374,221 +371,32 @@ class ReceiveScreen(CScreen):
|
|||
def save_request(self):
|
||||
addr = self.screen.address
|
||||
if not addr:
|
||||
return
|
||||
return False
|
||||
amount = self.screen.amount
|
||||
message = self.screen.message
|
||||
amount = self.app.get_amount(amount) if amount else 0
|
||||
req = self.app.wallet.make_payment_request(addr, amount, message, None)
|
||||
self.app.wallet.add_payment_request(req, self.app.electrum_config)
|
||||
self.app.update_tab('requests')
|
||||
try:
|
||||
self.app.wallet.add_payment_request(req, self.app.electrum_config)
|
||||
added_request = True
|
||||
except Exception as e:
|
||||
self.app.show_error(_('Error adding payment request') + ':\n' + str(e))
|
||||
added_request = False
|
||||
finally:
|
||||
self.app.update_tab('requests')
|
||||
return added_request
|
||||
|
||||
def on_amount_or_message(self):
|
||||
self.save_request()
|
||||
Clock.schedule_once(lambda dt: self.update_qr())
|
||||
|
||||
def do_new(self):
|
||||
addr = self.get_new_address()
|
||||
if not addr:
|
||||
self.app.show_info(_('Please use the existing requests first.'))
|
||||
else:
|
||||
self.save_request()
|
||||
self.app.show_info(_('New request added to your list.'))
|
||||
|
||||
|
||||
invoice_text = {
|
||||
PR_UNPAID:_('Pending'),
|
||||
PR_UNKNOWN:_('Unknown'),
|
||||
PR_PAID:_('Paid'),
|
||||
PR_EXPIRED:_('Expired')
|
||||
}
|
||||
request_text = {
|
||||
PR_UNPAID: _('Pending'),
|
||||
PR_UNKNOWN: _('Unknown'),
|
||||
PR_PAID: _('Received'),
|
||||
PR_EXPIRED: _('Expired')
|
||||
}
|
||||
pr_icon = {
|
||||
PR_UNPAID: 'atlas://gui/kivy/theming/light/important',
|
||||
PR_UNKNOWN: 'atlas://gui/kivy/theming/light/important',
|
||||
PR_PAID: 'atlas://gui/kivy/theming/light/confirmed',
|
||||
PR_EXPIRED: 'atlas://gui/kivy/theming/light/close'
|
||||
}
|
||||
|
||||
|
||||
class InvoicesScreen(CScreen):
|
||||
kvname = 'invoices'
|
||||
cards = {}
|
||||
|
||||
def get_card(self, pr):
|
||||
key = pr.get_id()
|
||||
ci = self.cards.get(key)
|
||||
if ci is None:
|
||||
ci = Factory.InvoiceItem()
|
||||
ci.key = key
|
||||
ci.screen = self
|
||||
self.cards[key] = ci
|
||||
|
||||
ci.requestor = pr.get_requestor()
|
||||
ci.memo = pr.get_memo()
|
||||
amount = pr.get_amount()
|
||||
if amount:
|
||||
ci.amount = self.app.format_amount_and_units(amount)
|
||||
status = self.app.wallet.invoices.get_status(ci.key)
|
||||
ci.status = invoice_text[status]
|
||||
ci.icon = pr_icon[status]
|
||||
else:
|
||||
ci.amount = _('No Amount')
|
||||
ci.status = ''
|
||||
exp = pr.get_expiration_date()
|
||||
ci.date = format_time(exp) if exp else _('Never')
|
||||
return ci
|
||||
|
||||
def update(self):
|
||||
self.menu_actions = [('Pay', self.do_pay), ('Details', self.do_view), ('Delete', self.do_delete)]
|
||||
invoices_list = self.screen.ids.invoices_container
|
||||
invoices_list.clear_widgets()
|
||||
_list = self.app.wallet.invoices.sorted_list()
|
||||
for pr in _list:
|
||||
ci = self.get_card(pr)
|
||||
invoices_list.add_widget(ci)
|
||||
if not _list:
|
||||
msg = _('This screen shows the list of payment requests that have been sent to you. You may also use it to store contact addresses.')
|
||||
invoices_list.add_widget(EmptyLabel(text=msg))
|
||||
|
||||
def do_pay(self, obj):
|
||||
pr = self.app.wallet.invoices.get(obj.key)
|
||||
self.app.on_pr(pr)
|
||||
|
||||
def do_view(self, obj):
|
||||
pr = self.app.wallet.invoices.get(obj.key)
|
||||
pr.verify(self.app.wallet.contacts)
|
||||
self.app.show_pr_details(pr.get_dict(), obj.status, True)
|
||||
|
||||
def do_delete(self, obj):
|
||||
from .dialogs.question import Question
|
||||
def cb(result):
|
||||
if result:
|
||||
self.app.wallet.invoices.remove(obj.key)
|
||||
self.app.update_tab('invoices')
|
||||
d = Question(_('Delete invoice?'), cb)
|
||||
d.open()
|
||||
|
||||
|
||||
address_icon = {
|
||||
'Pending' : 'atlas://gui/kivy/theming/light/important',
|
||||
'Paid' : 'atlas://gui/kivy/theming/light/confirmed'
|
||||
}
|
||||
|
||||
class AddressScreen(CScreen):
|
||||
kvname = 'address'
|
||||
cards = {}
|
||||
|
||||
def get_card(self, addr, balance, is_used, label):
|
||||
ci = self.cards.get(addr)
|
||||
if ci is None:
|
||||
ci = Factory.AddressItem()
|
||||
ci.screen = self
|
||||
ci.address = addr
|
||||
self.cards[addr] = ci
|
||||
|
||||
ci.memo = label
|
||||
ci.amount = self.app.format_amount_and_units(balance)
|
||||
request = self.app.wallet.get_payment_request(addr, self.app.electrum_config)
|
||||
if is_used:
|
||||
ci.status = _('Used')
|
||||
elif request:
|
||||
status, conf = self.app.wallet.get_request_status(addr)
|
||||
requested_amount = request.get('amount')
|
||||
# make sure that requested amount is > 0
|
||||
if status == PR_PAID:
|
||||
s = _('Request paid')
|
||||
elif status == PR_UNPAID:
|
||||
s = _('Request pending')
|
||||
elif status == PR_EXPIRED:
|
||||
s = _('Request expired')
|
||||
else:
|
||||
s = ''
|
||||
ci.status = s + ': ' + self.app.format_amount_and_units(requested_amount)
|
||||
else:
|
||||
ci.status = _('Funded') if balance>0 else _('Unused')
|
||||
return ci
|
||||
|
||||
|
||||
def update(self):
|
||||
self.menu_actions = [('Receive', self.do_show), ('Details', self.do_view)]
|
||||
wallet = self.app.wallet
|
||||
if self.screen.show_change == 0:
|
||||
_list = wallet.get_receiving_addresses()
|
||||
elif self.screen.show_change == 1:
|
||||
_list = wallet.get_change_addresses()
|
||||
else:
|
||||
_list = wallet.get_addresses()
|
||||
search = self.screen.message
|
||||
container = self.screen.ids.search_container
|
||||
container.clear_widgets()
|
||||
n = 0
|
||||
for address in _list:
|
||||
label = wallet.labels.get(address, '')
|
||||
balance = sum(wallet.get_addr_balance(address))
|
||||
is_used = wallet.is_used(address)
|
||||
if self.screen.show_used == 1 and (balance or is_used):
|
||||
continue
|
||||
if self.screen.show_used == 2 and balance == 0:
|
||||
continue
|
||||
if self.screen.show_used == 3 and not is_used:
|
||||
continue
|
||||
card = self.get_card(address, balance, is_used, label)
|
||||
if search and not self.ext_search(card, search):
|
||||
continue
|
||||
container.add_widget(card)
|
||||
n += 1
|
||||
if not n:
|
||||
msg = _('No address matching your search')
|
||||
container.add_widget(EmptyLabel(text=msg))
|
||||
|
||||
def do_show(self, obj):
|
||||
self.app.show_request(obj.address)
|
||||
|
||||
def do_view(self, obj):
|
||||
req = self.app.wallet.get_payment_request(obj.address, self.app.electrum_config)
|
||||
if req:
|
||||
c, u, x = self.app.wallet.get_addr_balance(obj.address)
|
||||
balance = c + u + x
|
||||
if balance > 0:
|
||||
req['fund'] = balance
|
||||
status = req.get('status')
|
||||
amount = req.get('amount')
|
||||
address = req['address']
|
||||
if amount:
|
||||
status = req.get('status')
|
||||
status = request_text[status]
|
||||
else:
|
||||
received_amount = self.app.wallet.get_addr_received(address)
|
||||
status = self.app.format_amount_and_units(received_amount)
|
||||
self.app.show_pr_details(req, status, False)
|
||||
|
||||
else:
|
||||
req = { 'address': obj.address, 'status' : obj.status }
|
||||
status = obj.status
|
||||
c, u, x = self.app.wallet.get_addr_balance(obj.address)
|
||||
balance = c + u + x
|
||||
if balance > 0:
|
||||
req['fund'] = balance
|
||||
self.app.show_addr_details(req, status)
|
||||
|
||||
def do_delete(self, obj):
|
||||
from .dialogs.question import Question
|
||||
def cb(result):
|
||||
if result:
|
||||
self.app.wallet.remove_payment_request(obj.address, self.app.electrum_config)
|
||||
self.update()
|
||||
d = Question(_('Delete request?'), cb)
|
||||
d.open()
|
||||
|
||||
def ext_search(self, card, search):
|
||||
return card.memo.find(search) >= 0 or card.amount.find(search) >= 0
|
||||
|
||||
|
||||
def do_save(self):
|
||||
if self.save_request():
|
||||
self.app.show_info(_('Request was saved.'))
|
||||
|
||||
|
||||
class TabbedCarousel(Factory.TabbedPanel):
|
||||
|
|
|
@ -1,90 +0,0 @@
|
|||
#:import _ electrum_gui.kivy.i18n._
|
||||
#:import Decimal decimal.Decimal
|
||||
#:set btc_symbol chr(171)
|
||||
#:set mbtc_symbol chr(187)
|
||||
#:set font_light 'gui/kivy/data/fonts/Roboto-Condensed.ttf'
|
||||
|
||||
<AddressLabel@Label>
|
||||
text_size: self.width, None
|
||||
halign: 'left'
|
||||
valign: 'top'
|
||||
|
||||
<AddressItem@CardItem>
|
||||
address: ''
|
||||
memo: ''
|
||||
amount: ''
|
||||
status: ''
|
||||
BoxLayout:
|
||||
spacing: '8dp'
|
||||
height: '32dp'
|
||||
orientation: 'vertical'
|
||||
Widget
|
||||
AddressLabel:
|
||||
text: root.address
|
||||
shorten: True
|
||||
Widget
|
||||
AddressLabel:
|
||||
text: (root.amount if root.status == 'Funded' else root.status) + ' ' + root.memo
|
||||
color: .699, .699, .699, 1
|
||||
font_size: '13sp'
|
||||
shorten: True
|
||||
Widget
|
||||
|
||||
AddressScreen:
|
||||
id: addr_screen
|
||||
name: 'address'
|
||||
message: ''
|
||||
pr_status: 'Pending'
|
||||
show_change: 0
|
||||
show_used: 0
|
||||
on_message:
|
||||
self.parent.update()
|
||||
BoxLayout
|
||||
padding: '12dp', '70dp', '12dp', '12dp'
|
||||
spacing: '12dp'
|
||||
orientation: 'vertical'
|
||||
size_hint: 1, 1.1
|
||||
BoxLayout:
|
||||
spacing: '6dp'
|
||||
size_hint: 1, None
|
||||
orientation: 'horizontal'
|
||||
AddressFilter:
|
||||
opacity: 1
|
||||
size_hint: 1, None
|
||||
height: self.minimum_height
|
||||
spacing: '5dp'
|
||||
AddressButton:
|
||||
id: search
|
||||
text: {0:_('Receiving'), 1:_('Change'), 2:_('All')}[root.show_change]
|
||||
on_release:
|
||||
root.show_change = (root.show_change + 1) % 3
|
||||
Clock.schedule_once(lambda dt: app.address_screen.update())
|
||||
AddressFilter:
|
||||
opacity: 1
|
||||
size_hint: 1, None
|
||||
height: self.minimum_height
|
||||
spacing: '5dp'
|
||||
AddressButton:
|
||||
id: search
|
||||
text: {0:_('All'), 1:_('Unused'), 2:_('Funded'), 3:_('Used')}[root.show_used]
|
||||
on_release:
|
||||
root.show_used = (root.show_used + 1) % 4
|
||||
Clock.schedule_once(lambda dt: app.address_screen.update())
|
||||
AddressFilter:
|
||||
opacity: 1
|
||||
size_hint: 1, None
|
||||
height: self.minimum_height
|
||||
spacing: '5dp'
|
||||
canvas.before:
|
||||
Color:
|
||||
rgba: 0.9, 0.9, 0.9, 1
|
||||
AddressButton:
|
||||
id: change
|
||||
text: root.message if root.message else _('Search')
|
||||
on_release: Clock.schedule_once(lambda dt: app.description_dialog(addr_screen))
|
||||
ScrollView:
|
||||
GridLayout:
|
||||
cols: 1
|
||||
id: search_container
|
||||
size_hint_y: None
|
||||
height: self.minimum_height
|
|
@ -1,66 +0,0 @@
|
|||
<InvoicesLabel@Label>
|
||||
#color: .305, .309, .309, 1
|
||||
text_size: self.width, None
|
||||
halign: 'left'
|
||||
valign: 'top'
|
||||
|
||||
<InvoiceItem@CardItem>
|
||||
requestor: ''
|
||||
memo: ''
|
||||
amount: ''
|
||||
status: ''
|
||||
date: ''
|
||||
icon: 'atlas://gui/kivy/theming/light/important'
|
||||
Image:
|
||||
id: icon
|
||||
source: root.icon
|
||||
size_hint: None, 1
|
||||
width: self.height *.54
|
||||
mipmap: True
|
||||
BoxLayout:
|
||||
spacing: '8dp'
|
||||
height: '32dp'
|
||||
orientation: 'vertical'
|
||||
Widget
|
||||
InvoicesLabel:
|
||||
text: root.requestor
|
||||
shorten: True
|
||||
Widget
|
||||
InvoicesLabel:
|
||||
text: root.memo
|
||||
color: .699, .699, .699, 1
|
||||
font_size: '13sp'
|
||||
shorten: True
|
||||
Widget
|
||||
BoxLayout:
|
||||
spacing: '8dp'
|
||||
height: '32dp'
|
||||
orientation: 'vertical'
|
||||
Widget
|
||||
InvoicesLabel:
|
||||
text: root.amount
|
||||
font_size: '15sp'
|
||||
halign: 'right'
|
||||
width: '110sp'
|
||||
Widget
|
||||
InvoicesLabel:
|
||||
text: root.status
|
||||
font_size: '13sp'
|
||||
halign: 'right'
|
||||
color: .699, .699, .699, 1
|
||||
Widget
|
||||
|
||||
|
||||
InvoicesScreen:
|
||||
name: 'invoices'
|
||||
BoxLayout:
|
||||
orientation: 'vertical'
|
||||
spacing: '1dp'
|
||||
ScrollView:
|
||||
GridLayout:
|
||||
cols: 1
|
||||
id: invoices_container
|
||||
size_hint: 1, None
|
||||
height: self.minimum_height
|
||||
spacing: '2dp'
|
||||
padding: '12dp'
|
|
@ -70,7 +70,7 @@ ReceiveScreen:
|
|||
id: address_label
|
||||
text: s.address if s.address else _('Bitcoin Address')
|
||||
shorten: True
|
||||
disabled: True
|
||||
on_release: Clock.schedule_once(lambda dt: app.addresses_dialog(s))
|
||||
CardSeparator:
|
||||
opacity: message_selection.opacity
|
||||
color: blue_bottom.foreground_color
|
||||
|
@ -110,16 +110,31 @@ ReceiveScreen:
|
|||
BoxLayout:
|
||||
size_hint: 1, None
|
||||
height: '48dp'
|
||||
IconButton:
|
||||
icon: 'atlas://gui/kivy/theming/light/save'
|
||||
size_hint: 0.6, None
|
||||
height: '48dp'
|
||||
on_release: s.parent.do_save()
|
||||
Button:
|
||||
text: _('Requests')
|
||||
size_hint: 1, None
|
||||
height: '48dp'
|
||||
on_release: Clock.schedule_once(lambda dt: app.requests_dialog(s))
|
||||
Button:
|
||||
text: _('Copy')
|
||||
size_hint: 1, None
|
||||
height: '48dp'
|
||||
on_release: s.parent.do_copy()
|
||||
Button:
|
||||
text: _('Share')
|
||||
size_hint: 1, None
|
||||
IconButton:
|
||||
icon: 'atlas://gui/kivy/theming/light/share'
|
||||
size_hint: 0.6, None
|
||||
height: '48dp'
|
||||
on_release: s.parent.do_share()
|
||||
BoxLayout:
|
||||
size_hint: 1, None
|
||||
height: '48dp'
|
||||
Widget
|
||||
size_hint: 2, 1
|
||||
Button:
|
||||
text: _('New')
|
||||
size_hint: 1, None
|
||||
|
|
|
@ -1,66 +0,0 @@
|
|||
<RequestLabel@Label>
|
||||
#color: .305, .309, .309, 1
|
||||
text_size: self.width, None
|
||||
halign: 'left'
|
||||
valign: 'top'
|
||||
|
||||
<RequestItem@CardItem>
|
||||
address: ''
|
||||
memo: ''
|
||||
amount: ''
|
||||
status: ''
|
||||
date: ''
|
||||
icon: 'atlas://gui/kivy/theming/light/important'
|
||||
Image:
|
||||
id: icon
|
||||
source: root.icon
|
||||
size_hint: None, 1
|
||||
width: self.height *.54
|
||||
mipmap: True
|
||||
BoxLayout:
|
||||
spacing: '8dp'
|
||||
height: '32dp'
|
||||
orientation: 'vertical'
|
||||
Widget
|
||||
RequestLabel:
|
||||
text: root.address
|
||||
shorten: True
|
||||
Widget
|
||||
RequestLabel:
|
||||
text: root.memo
|
||||
color: .699, .699, .699, 1
|
||||
font_size: '13sp'
|
||||
shorten: True
|
||||
Widget
|
||||
BoxLayout:
|
||||
spacing: '8dp'
|
||||
height: '32dp'
|
||||
orientation: 'vertical'
|
||||
Widget
|
||||
RequestLabel:
|
||||
text: root.amount
|
||||
halign: 'right'
|
||||
font_size: '15sp'
|
||||
Widget
|
||||
RequestLabel:
|
||||
text: root.status
|
||||
halign: 'right'
|
||||
font_size: '13sp'
|
||||
color: .699, .699, .699, 1
|
||||
Widget
|
||||
|
||||
|
||||
|
||||
RequestsScreen:
|
||||
name: 'requests'
|
||||
BoxLayout:
|
||||
orientation: 'vertical'
|
||||
spacing: '1dp'
|
||||
ScrollView:
|
||||
GridLayout:
|
||||
cols: 1
|
||||
id: requests_container
|
||||
size_hint_y: None
|
||||
height: self.minimum_height
|
||||
spacing: '2dp'
|
||||
padding: '12dp'
|
|
@ -34,6 +34,7 @@ SendScreen:
|
|||
text: s.address if s.address else _('Recipient')
|
||||
shorten: True
|
||||
on_release: Clock.schedule_once(lambda dt: app.show_info(_('Copy and paste the recipient address using the Paste button, or use the camera to scan a QR code.')))
|
||||
#on_release: Clock.schedule_once(lambda dt: app.popup_dialog('contacts'))
|
||||
CardSeparator:
|
||||
opacity: int(not root.is_pr)
|
||||
color: blue_bottom.foreground_color
|
||||
|
@ -92,26 +93,30 @@ SendScreen:
|
|||
BoxLayout:
|
||||
size_hint: 1, None
|
||||
height: '48dp'
|
||||
IconButton:
|
||||
size_hint: 0.6, 1
|
||||
on_release: s.parent.do_save()
|
||||
icon: 'atlas://gui/kivy/theming/light/save'
|
||||
Button:
|
||||
text: _('Invoices')
|
||||
size_hint: 1, 1
|
||||
on_release: Clock.schedule_once(lambda dt: app.invoices_dialog(s))
|
||||
Button:
|
||||
text: _('Paste')
|
||||
on_release: s.parent.do_paste()
|
||||
IconButton:
|
||||
id: qr
|
||||
size_hint: 0.6, 1
|
||||
on_release: Clock.schedule_once(lambda dt: app.scan_qr(on_complete=app.on_qr))
|
||||
icon: 'atlas://gui/kivy/theming/light/camera'
|
||||
Button:
|
||||
text: _('Paste')
|
||||
on_release: s.parent.do_paste()
|
||||
Button:
|
||||
text: _('Clear')
|
||||
on_release: s.parent.do_clear()
|
||||
IconButton:
|
||||
size_hint: 0.6, 1
|
||||
on_release: s.parent.do_save()
|
||||
icon: 'atlas://gui/kivy/theming/light/save'
|
||||
BoxLayout:
|
||||
size_hint: 1, None
|
||||
height: '48dp'
|
||||
Button:
|
||||
text: _('Clear')
|
||||
on_release: s.parent.do_clear()
|
||||
Widget:
|
||||
size_hint: 2, 1
|
||||
size_hint: 1, 1
|
||||
Button:
|
||||
text: _('Pay')
|
||||
size_hint: 1, 1
|
||||
|
|
|
@ -44,7 +44,8 @@ from electrum import WalletStorage
|
|||
# from electrum.synchronizer import Synchronizer
|
||||
# from electrum.verifier import SPV
|
||||
# from electrum.util import DebugMem
|
||||
from electrum.util import UserCancelled, print_error
|
||||
from electrum.util import (UserCancelled, print_error,
|
||||
WalletFileException, BitcoinException)
|
||||
# from electrum.wallet import Abstract_Wallet
|
||||
|
||||
from .installwizard import InstallWizard, GoBack
|
||||
|
@ -185,42 +186,51 @@ class ElectrumGui:
|
|||
|
||||
def start_new_window(self, path, uri):
|
||||
'''Raises the window for the wallet if it is open. Otherwise
|
||||
opens the wallet and creates a new window for it.'''
|
||||
for w in self.windows:
|
||||
if w.wallet.storage.path == path:
|
||||
w.bring_to_top()
|
||||
break
|
||||
else:
|
||||
opens the wallet and creates a new window for it'''
|
||||
try:
|
||||
wallet = self.daemon.load_wallet(path, None)
|
||||
except BaseException as e:
|
||||
traceback.print_exc(file=sys.stdout)
|
||||
d = QMessageBox(QMessageBox.Warning, _('Error'),
|
||||
_('Cannot load wallet') + ' (1):\n' + str(e))
|
||||
d.exec_()
|
||||
return
|
||||
if not wallet:
|
||||
storage = WalletStorage(path, manual_upgrades=True)
|
||||
wizard = InstallWizard(self.config, self.app, self.plugins, storage)
|
||||
try:
|
||||
wallet = self.daemon.load_wallet(path, None)
|
||||
except BaseException as e:
|
||||
traceback.print_exc(file=sys.stdout)
|
||||
wallet = wizard.run_and_get_wallet(self.daemon.get_wallet)
|
||||
except UserCancelled:
|
||||
pass
|
||||
except GoBack as e:
|
||||
print_error('[start_new_window] Exception caught (GoBack)', e)
|
||||
except (WalletFileException, BitcoinException) as e:
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
d = QMessageBox(QMessageBox.Warning, _('Error'),
|
||||
_('Cannot load wallet:') + '\n' + str(e))
|
||||
_('Cannot load wallet') + ' (2):\n' + str(e))
|
||||
d.exec_()
|
||||
return
|
||||
if not wallet:
|
||||
storage = WalletStorage(path, manual_upgrades=True)
|
||||
wizard = InstallWizard(self.config, self.app, self.plugins, storage)
|
||||
try:
|
||||
wallet = wizard.run_and_get_wallet()
|
||||
except UserCancelled:
|
||||
pass
|
||||
except GoBack as e:
|
||||
print_error('[start_new_window] Exception caught (GoBack)', e)
|
||||
finally:
|
||||
wizard.terminate()
|
||||
if not wallet:
|
||||
return
|
||||
if not wallet:
|
||||
return
|
||||
|
||||
if not self.daemon.get_wallet(wallet.storage.path):
|
||||
# wallet was not in memory
|
||||
wallet.start_threads(self.daemon.network)
|
||||
self.daemon.add_wallet(wallet)
|
||||
try:
|
||||
w = self.create_window_for_wallet(wallet)
|
||||
except BaseException as e:
|
||||
traceback.print_exc(file=sys.stdout)
|
||||
d = QMessageBox(QMessageBox.Warning, _('Error'),
|
||||
_('Cannot create window for wallet:') + '\n' + str(e))
|
||||
d.exec_()
|
||||
return
|
||||
try:
|
||||
for w in self.windows:
|
||||
if w.wallet.storage.path == wallet.storage.path:
|
||||
w.bring_to_top()
|
||||
return
|
||||
w = self.create_window_for_wallet(wallet)
|
||||
except BaseException as e:
|
||||
traceback.print_exc(file=sys.stdout)
|
||||
d = QMessageBox(QMessageBox.Warning, _('Error'),
|
||||
_('Cannot create window for wallet') + ':\n' + str(e))
|
||||
d.exec_()
|
||||
return
|
||||
if uri:
|
||||
w.pay_to_URI(uri)
|
||||
w.bring_to_top()
|
||||
|
|
|
@ -123,7 +123,7 @@ class AddressList(MyTreeWidget):
|
|||
address_item.setText(0, _('receiving'))
|
||||
address_item.setBackground(0, ColorScheme.GREEN.as_color(True))
|
||||
address_item.setFont(1, QFont(MONOSPACE_FONT))
|
||||
address_item.setData(1, Qt.UserRole, address)
|
||||
address_item.setData(0, Qt.UserRole, address) # column 0; independent from address column
|
||||
if self.wallet.is_frozen(address):
|
||||
address_item.setBackground(1, ColorScheme.BLUE.as_color(True))
|
||||
if self.wallet.is_beyond_limit(address):
|
||||
|
|
|
@ -223,7 +223,7 @@ class Console(QtWidgets.QPlainTextEdit):
|
|||
exec(command, self.namespace, self.namespace)
|
||||
except SystemExit:
|
||||
self.close()
|
||||
except Exception:
|
||||
except BaseException:
|
||||
traceback_lines = traceback.format_exc().split('\n')
|
||||
# Remove traceback mentioning this file, and a linebreak
|
||||
for i in (3,2,1,-1):
|
||||
|
|
|
@ -38,6 +38,8 @@ from PyQt5.QtWidgets import *
|
|||
from electrum.i18n import _
|
||||
from electrum import ELECTRUM_VERSION, bitcoin, constants
|
||||
|
||||
from .util import MessageBoxMixin
|
||||
|
||||
issue_template = """<h2>Traceback</h2>
|
||||
<pre>
|
||||
{traceback}
|
||||
|
@ -54,7 +56,7 @@ issue_template = """<h2>Traceback</h2>
|
|||
report_server = "https://crashhub.electrum.org/crash"
|
||||
|
||||
|
||||
class Exception_Window(QWidget):
|
||||
class Exception_Window(QWidget, MessageBoxMixin):
|
||||
_active_window = None
|
||||
|
||||
def __init__(self, main_window, exctype, value, tb):
|
||||
|
@ -75,7 +77,9 @@ class Exception_Window(QWidget):
|
|||
'information:')))
|
||||
|
||||
collapse_info = QPushButton(_("Show report contents"))
|
||||
collapse_info.clicked.connect(lambda: QMessageBox.about(self, "Report contents", self.get_report_string()))
|
||||
collapse_info.clicked.connect(
|
||||
lambda: self.msg_box(QMessageBox.NoIcon,
|
||||
self, "Report contents", self.get_report_string()))
|
||||
main_box.addWidget(collapse_info)
|
||||
|
||||
main_box.addWidget(QLabel(_("Please briefly describe what led to the error (optional):")))
|
||||
|
|
|
@ -161,6 +161,9 @@ class HistoryList(MyTreeWidget, AcceptFileDragDrop):
|
|||
|
||||
def show_summary(self):
|
||||
h = self.summary
|
||||
if not h:
|
||||
self.parent.show_message(_("Nothing to summarize."))
|
||||
return
|
||||
start_date = h.get('start_date')
|
||||
end_date = h.get('end_date')
|
||||
format_amount = lambda x: self.parent.format_amount(x.value) + ' ' + self.parent.base_unit()
|
||||
|
@ -232,7 +235,7 @@ class HistoryList(MyTreeWidget, AcceptFileDragDrop):
|
|||
label = tx_item['label']
|
||||
status, status_str = self.wallet.get_tx_status(tx_hash, height, conf, timestamp)
|
||||
has_invoice = self.wallet.invoices.paid.get(tx_hash)
|
||||
icon = QIcon(":icons/" + TX_ICONS[status])
|
||||
icon = self.icon_cache.get(":icons/" + TX_ICONS[status])
|
||||
v_str = self.parent.format_amount(value, True, whitespaces=True)
|
||||
balance_str = self.parent.format_amount(balance, whitespaces=True)
|
||||
entry = ['', tx_hash, status_str, label, v_str, balance_str]
|
||||
|
@ -250,10 +253,10 @@ class HistoryList(MyTreeWidget, AcceptFileDragDrop):
|
|||
item.setToolTip(0, str(conf) + " confirmation" + ("s" if conf != 1 else ""))
|
||||
item.setData(0, SortableTreeWidgetItem.DataRole, (status, conf))
|
||||
if has_invoice:
|
||||
item.setIcon(3, QIcon(":icons/seal"))
|
||||
item.setIcon(3, self.icon_cache.get(":icons/seal"))
|
||||
for i in range(len(entry)):
|
||||
if i>3:
|
||||
item.setTextAlignment(i, Qt.AlignRight)
|
||||
item.setTextAlignment(i, Qt.AlignRight | Qt.AlignVCenter)
|
||||
if i!=2:
|
||||
item.setFont(i, QFont(MONOSPACE_FONT))
|
||||
if value and value < 0:
|
||||
|
@ -299,7 +302,7 @@ class HistoryList(MyTreeWidget, AcceptFileDragDrop):
|
|||
|
||||
def update_item(self, tx_hash, height, conf, timestamp):
|
||||
status, status_str = self.wallet.get_tx_status(tx_hash, height, conf, timestamp)
|
||||
icon = QIcon(":icons/" + TX_ICONS[status])
|
||||
icon = self.icon_cache.get(":icons/" + TX_ICONS[status])
|
||||
items = self.findItems(tx_hash, Qt.UserRole|Qt.MatchContains|Qt.MatchRecursive, column=1)
|
||||
if items:
|
||||
item = items[0]
|
||||
|
@ -344,7 +347,7 @@ class HistoryList(MyTreeWidget, AcceptFileDragDrop):
|
|||
if child_tx:
|
||||
menu.addAction(_("Child pays for parent"), lambda: self.parent.cpfp(tx, child_tx))
|
||||
if pr_key:
|
||||
menu.addAction(QIcon(":icons/seal"), _("View invoice"), lambda: self.parent.show_invoice(pr_key))
|
||||
menu.addAction(self.icon_cache.get(":icons/seal"), _("View invoice"), lambda: self.parent.show_invoice(pr_key))
|
||||
if tx_URL:
|
||||
menu.addAction(_("View on block explorer"), lambda: webbrowser.open(tx_URL))
|
||||
menu.exec_(self.viewport().mapToGlobal(position))
|
||||
|
@ -408,7 +411,7 @@ class HistoryList(MyTreeWidget, AcceptFileDragDrop):
|
|||
lines.append([item['txid'], item.get('label', ''), item['confirmations'], item['value'], item['date']])
|
||||
else:
|
||||
lines.append(item)
|
||||
with open(fileName, "w+") as f:
|
||||
with open(fileName, "w+", encoding='utf-8') as f:
|
||||
if is_csv:
|
||||
import csv
|
||||
transaction = csv.writer(f, lineterminator='\n')
|
||||
|
|
|
@ -148,7 +148,7 @@ class InstallWizard(QDialog, MessageBoxMixin, BaseWizard):
|
|||
self.raise_()
|
||||
self.refresh_gui() # Need for QT on MacOSX. Lame.
|
||||
|
||||
def run_and_get_wallet(self):
|
||||
def run_and_get_wallet(self, get_wallet_from_daemon):
|
||||
|
||||
vbox = QVBoxLayout()
|
||||
hbox = QHBoxLayout()
|
||||
|
@ -181,10 +181,15 @@ class InstallWizard(QDialog, MessageBoxMixin, BaseWizard):
|
|||
|
||||
def on_filename(filename):
|
||||
path = os.path.join(wallet_folder, filename)
|
||||
wallet_from_memory = get_wallet_from_daemon(path)
|
||||
try:
|
||||
self.storage = WalletStorage(path, manual_upgrades=True)
|
||||
if wallet_from_memory:
|
||||
self.storage = wallet_from_memory.storage
|
||||
else:
|
||||
self.storage = WalletStorage(path, manual_upgrades=True)
|
||||
self.next_button.setEnabled(True)
|
||||
except IOError:
|
||||
except BaseException:
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
self.storage = None
|
||||
self.next_button.setEnabled(False)
|
||||
if self.storage:
|
||||
|
@ -192,7 +197,7 @@ class InstallWizard(QDialog, MessageBoxMixin, BaseWizard):
|
|||
msg =_("This file does not exist.") + '\n' \
|
||||
+ _("Press 'Next' to create this wallet, or choose another file.")
|
||||
pw = False
|
||||
else:
|
||||
elif not wallet_from_memory:
|
||||
if self.storage.is_encrypted_with_user_pw():
|
||||
msg = _("This file is encrypted with a password.") + '\n' \
|
||||
+ _('Enter your password or choose another file.')
|
||||
|
@ -204,6 +209,10 @@ class InstallWizard(QDialog, MessageBoxMixin, BaseWizard):
|
|||
else:
|
||||
msg = _("Press 'Next' to open this wallet.")
|
||||
pw = False
|
||||
else:
|
||||
msg = _("This file is already open in memory.") + "\n" \
|
||||
+ _("Press 'Next' to create/focus window.")
|
||||
pw = False
|
||||
else:
|
||||
msg = _('Cannot read file')
|
||||
pw = False
|
||||
|
@ -228,6 +237,9 @@ class InstallWizard(QDialog, MessageBoxMixin, BaseWizard):
|
|||
return
|
||||
if not self.storage.file_exists():
|
||||
break
|
||||
wallet_from_memory = get_wallet_from_daemon(self.storage.path)
|
||||
if wallet_from_memory:
|
||||
return wallet_from_memory
|
||||
if self.storage.file_exists() and self.storage.is_encrypted():
|
||||
if self.storage.is_encrypted_with_user_pw():
|
||||
password = self.pw_e.text()
|
||||
|
@ -245,14 +257,12 @@ class InstallWizard(QDialog, MessageBoxMixin, BaseWizard):
|
|||
try:
|
||||
self.run('choose_hw_device', HWD_SETUP_DECRYPT_WALLET)
|
||||
except InvalidPassword as e:
|
||||
# FIXME if we get here because of mistyped passphrase
|
||||
# then that passphrase gets "cached"
|
||||
QMessageBox.information(
|
||||
None, _('Error'),
|
||||
_('Failed to decrypt using this hardware device.') + '\n' +
|
||||
_('If you use a passphrase, make sure it is correct.'))
|
||||
self.stack = []
|
||||
return self.run_and_get_wallet()
|
||||
return self.run_and_get_wallet(get_wallet_from_daemon)
|
||||
except BaseException as e:
|
||||
traceback.print_exc(file=sys.stdout)
|
||||
QMessageBox.information(None, _('Error'), str(e))
|
||||
|
@ -302,8 +312,6 @@ class InstallWizard(QDialog, MessageBoxMixin, BaseWizard):
|
|||
self.wallet = Wallet(self.storage)
|
||||
return self.wallet
|
||||
|
||||
|
||||
|
||||
def finished(self):
|
||||
"""Called in hardware client wrapper, in order to close popups."""
|
||||
return
|
||||
|
@ -340,7 +348,7 @@ class InstallWizard(QDialog, MessageBoxMixin, BaseWizard):
|
|||
if not result and raise_on_cancel:
|
||||
raise UserCancelled
|
||||
if result == 1:
|
||||
raise GoBack
|
||||
raise GoBack from None
|
||||
self.title.setVisible(False)
|
||||
self.back_button.setEnabled(False)
|
||||
self.next_button.setEnabled(False)
|
||||
|
|
|
@ -48,7 +48,7 @@ class InvoiceList(MyTreeWidget):
|
|||
exp = pr.get_expiration_date()
|
||||
date_str = format_time(exp) if exp else _('Never')
|
||||
item = QTreeWidgetItem([date_str, requestor, pr.memo, self.parent.format_amount(pr.get_amount(), whitespaces=True), pr_tooltips.get(status,'')])
|
||||
item.setIcon(4, QIcon(pr_icons.get(status)))
|
||||
item.setIcon(4, self.icon_cache.get(pr_icons.get(status)))
|
||||
item.setData(0, Qt.UserRole, key)
|
||||
item.setFont(1, QFont(MONOSPACE_FONT))
|
||||
item.setFont(3, QFont(MONOSPACE_FONT))
|
||||
|
|
|
@ -47,7 +47,7 @@ from electrum.i18n import _
|
|||
from electrum.util import (format_time, format_satoshis, PrintError,
|
||||
format_satoshis_plain, NotEnoughFunds,
|
||||
UserCancelled, NoDynamicFeeEstimates, profiler,
|
||||
export_meta, import_meta, bh2u, bfh)
|
||||
export_meta, import_meta, bh2u, bfh, InvalidPassword)
|
||||
from electrum import Transaction
|
||||
from electrum import util, bitcoin, commands, coinchooser
|
||||
from electrum import paymentrequest
|
||||
|
@ -396,7 +396,11 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
|
|||
self.show_warning(msg, title=_('Information'))
|
||||
|
||||
def open_wallet(self):
|
||||
wallet_folder = self.get_wallet_folder()
|
||||
try:
|
||||
wallet_folder = self.get_wallet_folder()
|
||||
except FileNotFoundError as e:
|
||||
self.show_error(str(e))
|
||||
return
|
||||
filename, __ = QFileDialog.getOpenFileName(self, "Select your wallet file", wallet_folder)
|
||||
if not filename:
|
||||
return
|
||||
|
@ -409,13 +413,12 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
|
|||
filename, __ = QFileDialog.getSaveFileName(self, _('Enter a filename for the copy of your wallet'), wallet_folder)
|
||||
if not filename:
|
||||
return
|
||||
|
||||
new_path = os.path.join(wallet_folder, filename)
|
||||
if new_path != path:
|
||||
try:
|
||||
shutil.copy2(path, new_path)
|
||||
self.show_message(_("A copy of your wallet file was created in")+" '%s'" % str(new_path), title=_("Wallet backup created"))
|
||||
except (IOError, os.error) as reason:
|
||||
except BaseException as reason:
|
||||
self.show_critical(_("Electrum was unable to copy your wallet file to the specified location.") + "\n" + str(reason), title=_("Unable to create backup"))
|
||||
|
||||
def update_recently_visited(self, filename):
|
||||
|
@ -441,7 +444,11 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
|
|||
return os.path.dirname(os.path.abspath(self.config.get_wallet_path()))
|
||||
|
||||
def new_wallet(self):
|
||||
wallet_folder = self.get_wallet_folder()
|
||||
try:
|
||||
wallet_folder = self.get_wallet_folder()
|
||||
except FileNotFoundError as e:
|
||||
self.show_error(str(e))
|
||||
return
|
||||
i = 1
|
||||
while True:
|
||||
filename = "wallet_%d" % i
|
||||
|
@ -531,7 +538,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
|
|||
|
||||
help_menu = menubar.addMenu(_("&Help"))
|
||||
help_menu.addAction(_("&About"), self.show_about)
|
||||
help_menu.addAction(_("&Official website"), lambda: webbrowser.open("http://electrum.org"))
|
||||
help_menu.addAction(_("&Official website"), lambda: webbrowser.open("https://electrum.org"))
|
||||
help_menu.addSeparator()
|
||||
help_menu.addAction(_("&Documentation"), lambda: webbrowser.open("http://docs.electrum.org/")).setShortcut(QKeySequence.HelpContents)
|
||||
help_menu.addAction(_("&Report Bug"), self.show_report_bug)
|
||||
|
@ -663,8 +670,8 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
|
|||
edit.setStyleSheet(ColorScheme.DEFAULT.as_stylesheet())
|
||||
fiat_e.is_last_edited = (edit == fiat_e)
|
||||
amount = edit.get_amount()
|
||||
rate = self.fx.exchange_rate() if self.fx else None
|
||||
if rate is None or amount is None:
|
||||
rate = self.fx.exchange_rate() if self.fx else Decimal('NaN')
|
||||
if rate.is_nan() or amount is None:
|
||||
if edit is fiat_e:
|
||||
btc_e.setText("")
|
||||
if fee_e:
|
||||
|
@ -1182,7 +1189,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
|
|||
self.fee_adv_controls.setVisible(False)
|
||||
|
||||
self.preview_button = EnterButton(_("Preview"), self.do_preview)
|
||||
self.preview_button.setToolTip(_('Display the details of your transactions before signing it.'))
|
||||
self.preview_button.setToolTip(_('Display the details of your transaction before signing it.'))
|
||||
self.send_button = EnterButton(_("Send"), self.do_send)
|
||||
self.clear_button = EnterButton(_("Clear"), self.do_clear)
|
||||
buttons = QHBoxLayout()
|
||||
|
@ -1329,8 +1336,13 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
|
|||
# actual fees often differ somewhat.
|
||||
if freeze_feerate or self.fee_slider.is_active():
|
||||
displayed_feerate = self.feerate_e.get_amount()
|
||||
displayed_feerate = displayed_feerate // 1000 if displayed_feerate else 0
|
||||
displayed_fee = displayed_feerate * size
|
||||
if displayed_feerate:
|
||||
displayed_feerate = displayed_feerate // 1000
|
||||
else:
|
||||
# fallback to actual fee
|
||||
displayed_feerate = fee // size if fee is not None else None
|
||||
self.feerate_e.setAmount(displayed_feerate)
|
||||
displayed_fee = displayed_feerate * size if displayed_feerate is not None else None
|
||||
self.fee_e.setAmount(displayed_fee)
|
||||
else:
|
||||
if freeze_fee:
|
||||
|
@ -1767,8 +1779,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
|
|||
def remove_address(self, addr):
|
||||
if self.question(_("Do you want to remove")+" %s "%addr +_("from your wallet?")):
|
||||
self.wallet.delete_address(addr)
|
||||
self.address_list.update()
|
||||
self.history_list.update()
|
||||
self.need_update.set() # history, addresses, coins
|
||||
self.clear_receive_tab()
|
||||
|
||||
def get_coins(self):
|
||||
|
@ -1827,6 +1838,9 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
|
|||
|
||||
def show_invoice(self, key):
|
||||
pr = self.invoices.get(key)
|
||||
if pr is None:
|
||||
self.show_error('Cannot find payment request in wallet.')
|
||||
return
|
||||
pr.verify(self.contacts)
|
||||
self.show_pr_details(pr)
|
||||
|
||||
|
@ -1968,10 +1982,10 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
|
|||
return
|
||||
try:
|
||||
self.wallet.update_password(old_password, new_password, encrypt_file)
|
||||
except BaseException as e:
|
||||
except InvalidPassword as e:
|
||||
self.show_error(str(e))
|
||||
return
|
||||
except:
|
||||
except BaseException:
|
||||
traceback.print_exc(file=sys.stdout)
|
||||
self.show_error(_('Failed to update password'))
|
||||
return
|
||||
|
@ -2304,7 +2318,11 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
|
|||
self.pay_to_URI(data)
|
||||
return
|
||||
# else if the user scanned an offline signed tx
|
||||
data = bh2u(bitcoin.base_decode(data, length=None, base=43))
|
||||
try:
|
||||
data = bh2u(bitcoin.base_decode(data, length=None, base=43))
|
||||
except BaseException as e:
|
||||
self.show_error((_('Could not decode QR code')+':\n{}').format(e))
|
||||
return
|
||||
tx = self.tx_from_text(data)
|
||||
if not tx:
|
||||
return
|
||||
|
@ -2621,13 +2639,13 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
|
|||
|
||||
msg = '\n'.join([
|
||||
_('Time based: fee rate is based on average confirmation time estimates'),
|
||||
_('Mempool based: fee rate is targetting a depth in the memory pool')
|
||||
_('Mempool based: fee rate is targeting a depth in the memory pool')
|
||||
]
|
||||
)
|
||||
fee_type_label = HelpLabel(_('Fee estimation') + ':', msg)
|
||||
fee_type_combo = QComboBox()
|
||||
fee_type_combo.addItems([_('Static'), _('ETA'), _('Mempool')])
|
||||
fee_type_combo.setCurrentIndex(1 if self.config.use_mempool_fees() else 0)
|
||||
fee_type_combo.setCurrentIndex((2 if self.config.use_mempool_fees() else 1) if self.config.is_dynfee() else 0)
|
||||
def on_fee_type(x):
|
||||
self.config.set_key('mempool_fees', x==2)
|
||||
self.config.set_key('dynamic_fees', x>0)
|
||||
|
@ -2648,7 +2666,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
|
|||
use_rbf_cb.setChecked(self.config.get('use_rbf', True))
|
||||
use_rbf_cb.setToolTip(
|
||||
_('If you check this box, your transactions will be marked as non-final,') + '\n' + \
|
||||
_('and you will have the possiblity, while they are unconfirmed, to replace them with transactions that pay higher fees.') + '\n' + \
|
||||
_('and you will have the possibility, while they are unconfirmed, to replace them with transactions that pay higher fees.') + '\n' + \
|
||||
_('Note that some merchants do not accept non-final transactions until they are confirmed.'))
|
||||
def on_use_rbf(x):
|
||||
self.config.set_key('use_rbf', x == Qt.Checked)
|
||||
|
@ -2658,7 +2676,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
|
|||
msg = _('OpenAlias record, used to receive coins and to sign payment requests.') + '\n\n'\
|
||||
+ _('The following alias providers are available:') + '\n'\
|
||||
+ '\n'.join(['https://cryptoname.co/', 'http://xmr.link']) + '\n\n'\
|
||||
+ 'For more information, see http://openalias.org'
|
||||
+ 'For more information, see https://openalias.org'
|
||||
alias_label = HelpLabel(_('OpenAlias') + ':', msg)
|
||||
alias = self.config.get('alias','')
|
||||
alias_e = QLineEdit(alias)
|
||||
|
|
|
@ -28,6 +28,8 @@ import re
|
|||
from decimal import Decimal
|
||||
|
||||
from electrum import bitcoin
|
||||
from electrum.util import bfh
|
||||
|
||||
from .qrtextedit import ScanQRTextEdit
|
||||
from .completion_text_edit import CompletionTextEdit
|
||||
from . import util
|
||||
|
@ -92,9 +94,12 @@ class PayToEdit(CompletionTextEdit, ScanQRTextEdit):
|
|||
for word in x.split():
|
||||
if word[0:3] == 'OP_':
|
||||
assert word in opcodes.lookup
|
||||
script += chr(opcodes.lookup[word])
|
||||
opcode_int = opcodes.lookup[word]
|
||||
assert opcode_int < 256 # opcode is single-byte
|
||||
script += bitcoin.int_to_hex(opcode_int)
|
||||
else:
|
||||
script += push_script(word).decode('hex')
|
||||
bfh(word) # to test it is hex data
|
||||
script += push_script(word)
|
||||
return script
|
||||
|
||||
def parse_amount(self, x):
|
||||
|
|
|
@ -46,9 +46,13 @@ class ScanQRTextEdit(ButtonsTextEdit, MessageBoxMixin):
|
|||
fileName, __ = QFileDialog.getOpenFileName(self, 'select file')
|
||||
if not fileName:
|
||||
return
|
||||
with open(fileName, "r") as f:
|
||||
data = f.read()
|
||||
self.setText(data)
|
||||
try:
|
||||
with open(fileName, "r") as f:
|
||||
data = f.read()
|
||||
except BaseException as e:
|
||||
self.show_error(_('Error opening file') + ':\n' + str(e))
|
||||
else:
|
||||
self.setText(data)
|
||||
|
||||
def qr_input(self):
|
||||
from electrum import qrscanner, get_config
|
||||
|
|
|
@ -98,10 +98,10 @@ class RequestList(MyTreeWidget):
|
|||
amount_str = self.parent.format_amount(amount) if amount else ""
|
||||
item = QTreeWidgetItem([date, address, '', message, amount_str, pr_tooltips.get(status,'')])
|
||||
if signature is not None:
|
||||
item.setIcon(2, QIcon(":icons/seal.png"))
|
||||
item.setIcon(2, self.icon_cache.get(":icons/seal.png"))
|
||||
item.setToolTip(2, 'signed by '+ requestor)
|
||||
if status is not PR_UNKNOWN:
|
||||
item.setIcon(6, QIcon(pr_icons.get(status)))
|
||||
item.setIcon(6, self.icon_cache.get(pr_icons.get(status)))
|
||||
self.addTopLevelItem(item)
|
||||
|
||||
|
||||
|
|
|
@ -179,11 +179,12 @@ class TxDialog(QDialog, MessageBoxMixin):
|
|||
|
||||
def sign(self):
|
||||
def sign_done(success):
|
||||
if success:
|
||||
# note: with segwit we could save partially signed tx, because they have a txid
|
||||
if self.tx.is_complete():
|
||||
self.prompt_if_unsaved = True
|
||||
self.saved = False
|
||||
self.save_button.setDisabled(False)
|
||||
self.save_button.setToolTip("")
|
||||
self.save_button.setDisabled(False)
|
||||
self.save_button.setToolTip("")
|
||||
self.update()
|
||||
self.main_window.pop_top_level_window(self)
|
||||
|
||||
|
@ -289,7 +290,7 @@ class TxDialog(QDialog, MessageBoxMixin):
|
|||
cursor.insertText(prevout_hash[-8:] + ":%-4d " % prevout_n, ext)
|
||||
addr = x.get('address')
|
||||
if addr == "(pubkey)":
|
||||
_addr = self.wallet.find_pay_to_pubkey_address(prevout_hash, prevout_n)
|
||||
_addr = self.wallet.get_txin_address(x)
|
||||
if _addr:
|
||||
addr = _addr
|
||||
if addr is None:
|
||||
|
|
|
@ -393,6 +393,8 @@ class MyTreeWidget(QTreeWidget):
|
|||
self.addChild = self.addTopLevelItem
|
||||
self.insertChild = self.insertTopLevelItem
|
||||
|
||||
self.icon_cache = IconCache()
|
||||
|
||||
# Control which columns are editable
|
||||
self.editor = None
|
||||
self.pending_update = False
|
||||
|
@ -779,6 +781,17 @@ class SortableTreeWidgetItem(QTreeWidgetItem):
|
|||
return self.text(column) < other.text(column)
|
||||
|
||||
|
||||
class IconCache:
|
||||
|
||||
def __init__(self):
|
||||
self.__cache = {}
|
||||
|
||||
def get(self, file_name):
|
||||
if file_name not in self.__cache:
|
||||
self.__cache[file_name] = QIcon(file_name)
|
||||
return self.__cache[file_name]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = QApplication([])
|
||||
t = WaitingDialog(None, 'testing ...', lambda: [time.sleep(1)], lambda x: QMessageBox.information(None, 'done', "done"))
|
||||
|
|
|
@ -33,7 +33,7 @@ from .keystore import bip44_derivation
|
|||
from .wallet import Imported_Wallet, Standard_Wallet, Multisig_Wallet, wallet_types
|
||||
from .storage import STO_EV_USER_PW, STO_EV_XPUB_PW, get_derivation_used_for_hw_device_encryption
|
||||
from .i18n import _
|
||||
from .util import UserCancelled
|
||||
from .util import UserCancelled, InvalidPassword
|
||||
|
||||
# hardware device setup purpose
|
||||
HWD_SETUP_NEW_WALLET, HWD_SETUP_DECRYPT_WALLET = range(0, 2)
|
||||
|
@ -164,7 +164,7 @@ class BaseWizard(object):
|
|||
k = keystore.Imported_KeyStore({})
|
||||
self.storage.put('keystore', k.dump())
|
||||
w = Imported_Wallet(self.storage)
|
||||
for x in text.split():
|
||||
for x in keystore.get_private_keys(text):
|
||||
w.import_private_key(x, None)
|
||||
self.keystores.append(w.keystore)
|
||||
else:
|
||||
|
@ -202,20 +202,32 @@ class BaseWizard(object):
|
|||
# scan devices
|
||||
devices = []
|
||||
devmgr = self.plugins.device_manager
|
||||
for name, description, plugin in support:
|
||||
try:
|
||||
# FIXME: side-effect: unpaired_device_info sets client.handler
|
||||
u = devmgr.unpaired_device_infos(None, plugin)
|
||||
except:
|
||||
devmgr.print_error("error", name)
|
||||
continue
|
||||
devices += list(map(lambda x: (name, x), u))
|
||||
try:
|
||||
scanned_devices = devmgr.scan_devices()
|
||||
except BaseException as e:
|
||||
devmgr.print_error('error scanning devices: {}'.format(e))
|
||||
debug_msg = ' {}:\n {}'.format(_('Error scanning devices'), e)
|
||||
else:
|
||||
debug_msg = ''
|
||||
for name, description, plugin in support:
|
||||
try:
|
||||
# FIXME: side-effect: unpaired_device_info sets client.handler
|
||||
u = devmgr.unpaired_device_infos(None, plugin, devices=scanned_devices)
|
||||
except BaseException as e:
|
||||
devmgr.print_error('error getting device infos for {}: {}'.format(name, e))
|
||||
debug_msg += ' {}:\n {}\n'.format(plugin.name, e)
|
||||
continue
|
||||
devices += list(map(lambda x: (name, x), u))
|
||||
if not debug_msg:
|
||||
debug_msg = ' {}'.format(_('No exceptions encountered.'))
|
||||
if not devices:
|
||||
msg = ''.join([
|
||||
_('No hardware device detected.') + '\n',
|
||||
_('To trigger a rescan, press \'Next\'.') + '\n\n',
|
||||
_('If your device is not detected on Windows, go to "Settings", "Devices", "Connected devices", and do "Remove device". Then, plug your device again.') + ' ',
|
||||
_('On Linux, you might have to add a new permission to your udev rules.'),
|
||||
_('On Linux, you might have to add a new permission to your udev rules.') + '\n\n',
|
||||
_('Debug message') + '\n',
|
||||
debug_msg
|
||||
])
|
||||
self.confirm_dialog(title=title, message=msg, run_next= lambda x: self.choose_hw_device(purpose))
|
||||
return
|
||||
|
@ -243,6 +255,9 @@ class BaseWizard(object):
|
|||
devmgr.unpair_id(device_info.device.id_)
|
||||
self.choose_hw_device(purpose)
|
||||
return
|
||||
except UserCancelled:
|
||||
self.choose_hw_device(purpose)
|
||||
return
|
||||
except BaseException as e:
|
||||
self.show_error(str(e))
|
||||
self.choose_hw_device(purpose)
|
||||
|
@ -259,7 +274,15 @@ class BaseWizard(object):
|
|||
derivation = get_derivation_used_for_hw_device_encryption()
|
||||
xpub = self.plugin.get_xpub(device_info.device.id_, derivation, 'standard', self)
|
||||
password = keystore.Xpub.get_pubkey_from_xpub(xpub, ())
|
||||
self.storage.decrypt(password)
|
||||
try:
|
||||
self.storage.decrypt(password)
|
||||
except InvalidPassword:
|
||||
# try to clear session so that user can type another passphrase
|
||||
devmgr = self.plugins.device_manager
|
||||
client = devmgr.client_by_id(device_info.device.id_)
|
||||
if hasattr(client, 'clear_session'): # FIXME not all hw wallet plugins have this
|
||||
client.clear_session()
|
||||
raise
|
||||
else:
|
||||
raise Exception('unknown purpose: %s' % purpose)
|
||||
|
||||
|
@ -459,10 +482,6 @@ class BaseWizard(object):
|
|||
def show_xpub_and_add_cosigners(self, xpub):
|
||||
self.show_xpub_dialog(xpub=xpub, run_next=lambda x: self.run('choose_keystore'))
|
||||
|
||||
def on_cosigner(self, text, password, i):
|
||||
k = keystore.from_master_key(text, password)
|
||||
self.on_keystore(k)
|
||||
|
||||
def choose_seed_type(self):
|
||||
title = _('Choose Seed type')
|
||||
message = ' '.join([
|
||||
|
|
|
@ -32,7 +32,7 @@ import json
|
|||
import ecdsa
|
||||
import pyaes
|
||||
|
||||
from .util import bfh, bh2u, to_string
|
||||
from .util import bfh, bh2u, to_string, BitcoinException
|
||||
from . import version
|
||||
from .util import print_error, InvalidPassword, assert_bytes, to_bytes, inv_dict
|
||||
from . import segwit_addr
|
||||
|
@ -44,7 +44,7 @@ from . import constants
|
|||
COINBASE_MATURITY = 100
|
||||
COIN = 100000000
|
||||
|
||||
# supported types of transction outputs
|
||||
# supported types of transaction outputs
|
||||
TYPE_ADDRESS = 0
|
||||
TYPE_PUBKEY = 1
|
||||
TYPE_SCRIPT = 2
|
||||
|
@ -144,6 +144,9 @@ def rev_hex(s):
|
|||
|
||||
def int_to_hex(i, length=1):
|
||||
assert isinstance(i, int)
|
||||
if i < 0:
|
||||
# two's complement
|
||||
i = pow(256, length) + i
|
||||
s = hex(i)[2:].rstrip('L')
|
||||
s = "0"*(2*length - len(s)) + s
|
||||
return rev_hex(s)
|
||||
|
@ -261,7 +264,7 @@ def hash_160(public_key):
|
|||
return md.digest()
|
||||
|
||||
|
||||
def hash160_to_b58_address(h160, addrtype, witness_program_version=1):
|
||||
def hash160_to_b58_address(h160, addrtype):
|
||||
s = bytes([addrtype])
|
||||
s += h160
|
||||
return base_encode(s+Hash(s)[0:4], base=58)
|
||||
|
@ -273,23 +276,29 @@ def b58_address_to_hash160(addr):
|
|||
return _bytes[0], _bytes[1:21]
|
||||
|
||||
|
||||
def hash160_to_p2pkh(h160):
|
||||
return hash160_to_b58_address(h160, constants.net.ADDRTYPE_P2PKH)
|
||||
def hash160_to_p2pkh(h160, *, net=None):
|
||||
if net is None:
|
||||
net = constants.net
|
||||
return hash160_to_b58_address(h160, net.ADDRTYPE_P2PKH)
|
||||
|
||||
def hash160_to_p2sh(h160):
|
||||
return hash160_to_b58_address(h160, constants.net.ADDRTYPE_P2SH)
|
||||
def hash160_to_p2sh(h160, *, net=None):
|
||||
if net is None:
|
||||
net = constants.net
|
||||
return hash160_to_b58_address(h160, net.ADDRTYPE_P2SH)
|
||||
|
||||
def public_key_to_p2pkh(public_key):
|
||||
return hash160_to_p2pkh(hash_160(public_key))
|
||||
|
||||
def hash_to_segwit_addr(h):
|
||||
return segwit_addr.encode(constants.net.SEGWIT_HRP, 0, h)
|
||||
def hash_to_segwit_addr(h, witver, *, net=None):
|
||||
if net is None:
|
||||
net = constants.net
|
||||
return segwit_addr.encode(net.SEGWIT_HRP, witver, h)
|
||||
|
||||
def public_key_to_p2wpkh(public_key):
|
||||
return hash_to_segwit_addr(hash_160(public_key))
|
||||
return hash_to_segwit_addr(hash_160(public_key), witver=0)
|
||||
|
||||
def script_to_p2wsh(script):
|
||||
return hash_to_segwit_addr(sha256(bfh(script)))
|
||||
return hash_to_segwit_addr(sha256(bfh(script)), witver=0)
|
||||
|
||||
def p2wpkh_nested_script(pubkey):
|
||||
pkh = bh2u(hash_160(bfh(pubkey)))
|
||||
|
@ -303,7 +312,7 @@ def pubkey_to_address(txin_type, pubkey):
|
|||
if txin_type == 'p2pkh':
|
||||
return public_key_to_p2pkh(bfh(pubkey))
|
||||
elif txin_type == 'p2wpkh':
|
||||
return hash_to_segwit_addr(hash_160(bfh(pubkey)))
|
||||
return public_key_to_p2wpkh(bfh(pubkey))
|
||||
elif txin_type == 'p2wpkh-p2sh':
|
||||
scriptSig = p2wpkh_nested_script(pubkey)
|
||||
return hash160_to_p2sh(hash_160(bfh(scriptSig)))
|
||||
|
@ -322,14 +331,16 @@ def redeem_script_to_address(txin_type, redeem_script):
|
|||
raise NotImplementedError(txin_type)
|
||||
|
||||
|
||||
def script_to_address(script):
|
||||
def script_to_address(script, *, net=None):
|
||||
from .transaction import get_address_from_output_script
|
||||
t, addr = get_address_from_output_script(bfh(script))
|
||||
t, addr = get_address_from_output_script(bfh(script), net=net)
|
||||
assert t == TYPE_ADDRESS
|
||||
return addr
|
||||
|
||||
def address_to_script(addr):
|
||||
witver, witprog = segwit_addr.decode(constants.net.SEGWIT_HRP, addr)
|
||||
def address_to_script(addr, *, net=None):
|
||||
if net is None:
|
||||
net = constants.net
|
||||
witver, witprog = segwit_addr.decode(net.SEGWIT_HRP, addr)
|
||||
if witprog is not None:
|
||||
assert (0 <= witver <= 16)
|
||||
OP_n = witver + 0x50 if witver > 0 else 0
|
||||
|
@ -337,16 +348,16 @@ def address_to_script(addr):
|
|||
script += push_script(bh2u(bytes(witprog)))
|
||||
return script
|
||||
addrtype, hash_160 = b58_address_to_hash160(addr)
|
||||
if addrtype == constants.net.ADDRTYPE_P2PKH:
|
||||
if addrtype == net.ADDRTYPE_P2PKH:
|
||||
script = '76a9' # op_dup, op_hash_160
|
||||
script += push_script(bh2u(hash_160))
|
||||
script += '88ac' # op_equalverify, op_checksig
|
||||
elif addrtype == constants.net.ADDRTYPE_P2SH:
|
||||
elif addrtype == net.ADDRTYPE_P2SH:
|
||||
script = 'a9' # op_hash_160
|
||||
script += push_script(bh2u(hash_160))
|
||||
script += '87' # op_equal
|
||||
else:
|
||||
raise BaseException('unknown address type')
|
||||
raise BitcoinException('unknown address type: {}'.format(addrtype))
|
||||
return script
|
||||
|
||||
def address_to_scripthash(addr):
|
||||
|
@ -408,7 +419,10 @@ def base_decode(v, length, base):
|
|||
chars = __b43chars
|
||||
long_value = 0
|
||||
for (i, c) in enumerate(v[::-1]):
|
||||
long_value += chars.find(bytes([c])) * (base**i)
|
||||
digit = chars.find(bytes([c]))
|
||||
if digit == -1:
|
||||
raise ValueError('Forbidden character {} for base {}'.format(c, base))
|
||||
long_value += digit * (base**i)
|
||||
result = bytearray()
|
||||
while long_value >= 256:
|
||||
div, mod = divmod(long_value, 256)
|
||||
|
@ -428,6 +442,10 @@ def base_decode(v, length, base):
|
|||
return bytes(result)
|
||||
|
||||
|
||||
class InvalidChecksum(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def EncodeBase58Check(vchIn):
|
||||
hash = Hash(vchIn)
|
||||
return base_encode(vchIn + hash[0:4], base=58)
|
||||
|
@ -440,13 +458,14 @@ def DecodeBase58Check(psz):
|
|||
hash = Hash(key)
|
||||
cs32 = hash[0:4]
|
||||
if cs32 != csum:
|
||||
return None
|
||||
raise InvalidChecksum('expected {}, actual {}'.format(bh2u(cs32), bh2u(csum)))
|
||||
else:
|
||||
return key
|
||||
|
||||
|
||||
# backwards compat
|
||||
# extended WIF for segwit (used in 3.0.x; but still used internally)
|
||||
# the keys in this dict should be a superset of what Imported Wallets can import
|
||||
SCRIPT_TYPES = {
|
||||
'p2pkh':0,
|
||||
'p2wpkh':1,
|
||||
|
@ -479,9 +498,12 @@ def deserialize_privkey(key):
|
|||
if ':' in key:
|
||||
txin_type, key = key.split(sep=':', maxsplit=1)
|
||||
assert txin_type in SCRIPT_TYPES
|
||||
vch = DecodeBase58Check(key)
|
||||
if not vch:
|
||||
raise BaseException("cannot deserialize", key)
|
||||
try:
|
||||
vch = DecodeBase58Check(key)
|
||||
except BaseException:
|
||||
neutered_privkey = str(key)[:3] + '..' + str(key)[-2:]
|
||||
raise BitcoinException("cannot deserialize privkey {}"
|
||||
.format(neutered_privkey))
|
||||
|
||||
if txin_type is None:
|
||||
# keys exported in version 3.0.x encoded script type in first byte
|
||||
|
@ -876,7 +898,8 @@ def deserialize_xkey(xkey, prv, *, net=None):
|
|||
net = constants.net
|
||||
xkey = DecodeBase58Check(xkey)
|
||||
if len(xkey) != 78:
|
||||
raise BaseException('Invalid length')
|
||||
raise BitcoinException('Invalid length for extended key: {}'
|
||||
.format(len(xkey)))
|
||||
depth = xkey[4]
|
||||
fingerprint = xkey[5:9]
|
||||
child_number = xkey[9:13]
|
||||
|
@ -884,7 +907,8 @@ def deserialize_xkey(xkey, prv, *, net=None):
|
|||
header = int('0x' + bh2u(xkey[0:4]), 16)
|
||||
headers = net.XPRV_HEADERS if prv else net.XPUB_HEADERS
|
||||
if header not in headers.values():
|
||||
raise BaseException('Invalid xpub format', hex(header))
|
||||
raise BitcoinException('Invalid extended key format: {}'
|
||||
.format(hex(header)))
|
||||
xtype = list(headers.keys())[list(headers.values()).index(header)]
|
||||
n = 33 if prv else 32
|
||||
K_or_k = xkey[13+n:]
|
||||
|
|
|
@ -255,6 +255,10 @@ class Blockchain(util.PrintError):
|
|||
with open(name, 'rb') as f:
|
||||
f.seek(delta * 80)
|
||||
h = f.read(80)
|
||||
elif not os.path.exists(util.get_headers_dir(self.config)):
|
||||
raise Exception('Electrum datadir does not exist. Was it deleted while running?')
|
||||
else:
|
||||
raise Exception('Cannot find headers file but datadir is there. Should be at {}'.format(name))
|
||||
if h == bytes([0])*80:
|
||||
return None
|
||||
return deserialize_header(h, height)
|
||||
|
@ -313,6 +317,8 @@ class Blockchain(util.PrintError):
|
|||
return bitsN << 24 | bitsBase
|
||||
|
||||
def can_connect(self, header, check_height=True):
|
||||
if header is None:
|
||||
return False
|
||||
height = header['block_height']
|
||||
if check_height and self.height() != height - 1:
|
||||
#self.print_error("cannot connect at height", height)
|
||||
|
|
|
@ -34,7 +34,7 @@ from functools import wraps
|
|||
from decimal import Decimal
|
||||
|
||||
from .import util
|
||||
from .util import bfh, bh2u, format_satoshis, json_decode, print_error
|
||||
from .util import bfh, bh2u, format_satoshis, json_decode, print_error, json_encode
|
||||
from .import bitcoin
|
||||
from .bitcoin import is_address, hash_160, COIN, TYPE_ADDRESS
|
||||
from .i18n import _
|
||||
|
@ -159,19 +159,13 @@ class Commands:
|
|||
return True
|
||||
|
||||
@command('')
|
||||
def make_seed(self, nbits=132, entropy=1, language=None, segwit=False):
|
||||
def make_seed(self, nbits=132, language=None, segwit=False):
|
||||
"""Create a seed"""
|
||||
from .mnemonic import Mnemonic
|
||||
t = 'segwit' if segwit else 'standard'
|
||||
s = Mnemonic(language).make_seed(t, nbits, custom_entropy=entropy)
|
||||
s = Mnemonic(language).make_seed(t, nbits)
|
||||
return s
|
||||
|
||||
@command('')
|
||||
def check_seed(self, seed, entropy=1, language=None):
|
||||
"""Check that a seed was generated with given entropy"""
|
||||
from .mnemonic import Mnemonic
|
||||
return Mnemonic(language).check_seed(seed, entropy)
|
||||
|
||||
@command('n')
|
||||
def getaddresshistory(self, address):
|
||||
"""Return the transaction history of any address. Note: This is a
|
||||
|
@ -207,7 +201,7 @@ class Commands:
|
|||
keypairs = {}
|
||||
inputs = jsontx.get('inputs')
|
||||
outputs = jsontx.get('outputs')
|
||||
locktime = jsontx.get('locktime', 0)
|
||||
locktime = jsontx.get('lockTime', 0)
|
||||
for txin in inputs:
|
||||
if txin.get('output'):
|
||||
prevout_hash, prevout_n = txin['output'].split(':')
|
||||
|
@ -418,6 +412,8 @@ class Commands:
|
|||
tx = self.wallet.make_unsigned_transaction(coins, final_outputs, self.config, fee, change_addr)
|
||||
if locktime != None:
|
||||
tx.locktime = locktime
|
||||
if rbf is None:
|
||||
rbf = self.config.get('use_rbf', True)
|
||||
if rbf:
|
||||
tx.set_rbf(True)
|
||||
if not unsigned:
|
||||
|
@ -426,7 +422,7 @@ class Commands:
|
|||
return tx
|
||||
|
||||
@command('wp')
|
||||
def payto(self, destination, amount, fee=None, from_addr=None, change_addr=None, nocheck=False, unsigned=False, rbf=False, password=None, locktime=None):
|
||||
def payto(self, destination, amount, fee=None, from_addr=None, change_addr=None, nocheck=False, unsigned=False, rbf=None, password=None, locktime=None):
|
||||
"""Create a transaction. """
|
||||
tx_fee = satoshis(fee)
|
||||
domain = from_addr.split(',') if from_addr else None
|
||||
|
@ -434,7 +430,7 @@ class Commands:
|
|||
return tx.as_dict()
|
||||
|
||||
@command('wp')
|
||||
def paytomany(self, outputs, fee=None, from_addr=None, change_addr=None, nocheck=False, unsigned=False, rbf=False, password=None, locktime=None):
|
||||
def paytomany(self, outputs, fee=None, from_addr=None, change_addr=None, nocheck=False, unsigned=False, rbf=None, password=None, locktime=None):
|
||||
"""Create a multi-output transaction. """
|
||||
tx_fee = satoshis(fee)
|
||||
domain = from_addr.split(',') if from_addr else None
|
||||
|
@ -455,7 +451,7 @@ class Commands:
|
|||
from .exchange_rate import FxThread
|
||||
fx = FxThread(self.config, None)
|
||||
kwargs['fx'] = fx
|
||||
return self.wallet.get_full_history(**kwargs)
|
||||
return json_encode(self.wallet.get_full_history(**kwargs))
|
||||
|
||||
@command('w')
|
||||
def setlabel(self, key, label):
|
||||
|
@ -697,7 +693,6 @@ command_options = {
|
|||
'from_addr': ("-F", "Source address (must be a wallet address; use sweep to spend from non-wallet address)."),
|
||||
'change_addr': ("-c", "Change address. Default is a spare address, or the source address if it's not in the wallet"),
|
||||
'nbits': (None, "Number of bits of entropy"),
|
||||
'entropy': (None, "Custom entropy"),
|
||||
'segwit': (None, "Create segwit seed"),
|
||||
'language': ("-L", "Default language for wordlist"),
|
||||
'privkey': (None, "Private key. Set to '?' to get a prompt."),
|
||||
|
@ -726,7 +721,6 @@ arg_types = {
|
|||
'nbits': int,
|
||||
'imax': int,
|
||||
'year': int,
|
||||
'entropy': int,
|
||||
'tx': tx_from_str,
|
||||
'pubkeys': json_loads,
|
||||
'jsontx': json_loads,
|
||||
|
|
|
@ -87,7 +87,7 @@ class BitcoinTestnet:
|
|||
XPUB_HEADERS = {
|
||||
'standard': 0x043587cf, # tpub
|
||||
'p2wpkh-p2sh': 0x044a5262, # upub
|
||||
'p2wsh-p2sh': 0x024285ef, # Upub
|
||||
'p2wsh-p2sh': 0x024289ef, # Upub
|
||||
'p2wpkh': 0x045f1cf6, # vpub
|
||||
'p2wsh': 0x02575483, # Vpub
|
||||
}
|
||||
|
|
|
@ -22,13 +22,14 @@
|
|||
# SOFTWARE.
|
||||
import re
|
||||
import dns
|
||||
from dns.exception import DNSException
|
||||
import json
|
||||
import traceback
|
||||
import sys
|
||||
|
||||
from . import bitcoin
|
||||
from . import dnssec
|
||||
from .util import export_meta, import_meta
|
||||
from .util import export_meta, import_meta, print_error, to_string
|
||||
|
||||
|
||||
class Contacts(dict):
|
||||
|
@ -96,10 +97,14 @@ class Contacts(dict):
|
|||
def resolve_openalias(self, url):
|
||||
# support email-style addresses, per the OA standard
|
||||
url = url.replace('@', '.')
|
||||
records, validated = dnssec.query(url, dns.rdatatype.TXT)
|
||||
try:
|
||||
records, validated = dnssec.query(url, dns.rdatatype.TXT)
|
||||
except DNSException as e:
|
||||
print_error('Error resolving openalias: ', str(e))
|
||||
return None
|
||||
prefix = 'btc'
|
||||
for record in records:
|
||||
string = record.strings[0]
|
||||
string = to_string(record.strings[0], 'utf8')
|
||||
if string.startswith('oa1:' + prefix):
|
||||
address = self.find_regex(string, r'recipient_address=([A-Za-z0-9]+)')
|
||||
name = self.find_regex(string, r'recipient_name=([^;]+)')
|
||||
|
|
|
@ -173,7 +173,8 @@ class Daemon(DaemonThread):
|
|||
elif sub == 'load_wallet':
|
||||
path = config.get_wallet_path()
|
||||
wallet = self.load_wallet(path, config.get('password'))
|
||||
self.cmd_runner.wallet = wallet
|
||||
if wallet is not None:
|
||||
self.cmd_runner.wallet = wallet
|
||||
response = wallet is not None
|
||||
elif sub == 'close_wallet':
|
||||
path = config.get_wallet_path()
|
||||
|
@ -185,6 +186,9 @@ class Daemon(DaemonThread):
|
|||
elif sub == 'status':
|
||||
if self.network:
|
||||
p = self.network.get_parameters()
|
||||
current_wallet = self.cmd_runner.wallet
|
||||
current_wallet_path = current_wallet.storage.path \
|
||||
if current_wallet else None
|
||||
response = {
|
||||
'path': self.network.config.path,
|
||||
'server': p[0],
|
||||
|
@ -196,6 +200,7 @@ class Daemon(DaemonThread):
|
|||
'version': ELECTRUM_VERSION,
|
||||
'wallets': {k: w.is_up_to_date()
|
||||
for k, w in self.wallets.items()},
|
||||
'current_wallet': current_wallet_path,
|
||||
'fee_per_kb': self.config.fee_per_kb(),
|
||||
}
|
||||
else:
|
||||
|
|
|
@ -66,7 +66,7 @@ class ExchangeBase(PrintError):
|
|||
if os.path.exists(filename):
|
||||
timestamp = os.stat(filename).st_mtime
|
||||
try:
|
||||
with open(filename, 'r') as f:
|
||||
with open(filename, 'r', encoding='utf-8') as f:
|
||||
h = json.loads(f.read())
|
||||
h['timestamp'] = timestamp
|
||||
except:
|
||||
|
@ -87,7 +87,7 @@ class ExchangeBase(PrintError):
|
|||
self.print_error("failed fx history:", e)
|
||||
return
|
||||
filename = os.path.join(cache_dir, self.name() + '_' + ccy)
|
||||
with open(filename, 'w') as f:
|
||||
with open(filename, 'w', encoding='utf-8') as f:
|
||||
f.write(json.dumps(h))
|
||||
h['timestamp'] = time.time()
|
||||
self.history[ccy] = h
|
||||
|
@ -382,7 +382,7 @@ def get_exchanges_and_currencies():
|
|||
import os, json
|
||||
path = os.path.join(os.path.dirname(__file__), 'currencies.json')
|
||||
try:
|
||||
with open(path, 'r') as f:
|
||||
with open(path, 'r', encoding='utf-8') as f:
|
||||
return json.loads(f.read())
|
||||
except:
|
||||
pass
|
||||
|
@ -399,7 +399,7 @@ def get_exchanges_and_currencies():
|
|||
except:
|
||||
print(name, "error")
|
||||
continue
|
||||
with open(path, 'w') as f:
|
||||
with open(path, 'w', encoding='utf-8') as f:
|
||||
f.write(json.dumps(d, indent=4, sort_keys=True))
|
||||
return d
|
||||
|
||||
|
|
|
@ -144,7 +144,7 @@ class TcpConnection(threading.Thread, util.PrintError):
|
|||
context = self.get_ssl_context(cert_reqs=ssl.CERT_REQUIRED, ca_certs=ca_path)
|
||||
s = context.wrap_socket(s, do_handshake_on_connect=True)
|
||||
except ssl.SSLError as e:
|
||||
print_error(e)
|
||||
self.print_error(e)
|
||||
s = None
|
||||
except:
|
||||
return
|
||||
|
@ -172,8 +172,10 @@ class TcpConnection(threading.Thread, util.PrintError):
|
|||
# workaround android bug
|
||||
cert = re.sub("([^\n])-----END CERTIFICATE-----","\\1\n-----END CERTIFICATE-----",cert)
|
||||
temporary_path = cert_path + '.temp'
|
||||
with open(temporary_path,"w") as f:
|
||||
with open(temporary_path, "w", encoding='utf-8') as f:
|
||||
f.write(cert)
|
||||
f.flush()
|
||||
os.fsync(f.fileno())
|
||||
else:
|
||||
is_new = False
|
||||
|
||||
|
@ -199,7 +201,7 @@ class TcpConnection(threading.Thread, util.PrintError):
|
|||
os.unlink(rej)
|
||||
os.rename(temporary_path, rej)
|
||||
else:
|
||||
with open(cert_path) as f:
|
||||
with open(cert_path, encoding='utf-8') as f:
|
||||
cert = f.read()
|
||||
try:
|
||||
b = pem.dePem(cert, 'CERTIFICATE')
|
||||
|
@ -295,8 +297,8 @@ class Interface(util.PrintError):
|
|||
wire_requests = self.unsent_requests[0:n]
|
||||
try:
|
||||
self.pipe.send_all([make_dict(*r) for r in wire_requests])
|
||||
except socket.error as e:
|
||||
self.print_error("socket error:", e)
|
||||
except BaseException as e:
|
||||
self.print_error("pipe send error:", e)
|
||||
return False
|
||||
self.unsent_requests = self.unsent_requests[n:]
|
||||
for request in wire_requests:
|
||||
|
@ -396,7 +398,7 @@ def test_certificates():
|
|||
certs = os.listdir(mydir)
|
||||
for c in certs:
|
||||
p = os.path.join(mydir,c)
|
||||
with open(p) as f:
|
||||
with open(p, encoding='utf-8') as f:
|
||||
cert = f.read()
|
||||
check_cert(c, cert)
|
||||
|
||||
|
|
|
@ -29,7 +29,8 @@ from unicodedata import normalize
|
|||
from . import bitcoin
|
||||
from .bitcoin import *
|
||||
from . import constants
|
||||
from .util import PrintError, InvalidPassword, hfu
|
||||
from .util import (PrintError, InvalidPassword, hfu, WalletFileException,
|
||||
BitcoinException)
|
||||
from .mnemonic import Mnemonic, load_wordlist
|
||||
from .plugins import run_hook
|
||||
|
||||
|
@ -75,6 +76,8 @@ class KeyStore(PrintError):
|
|||
return False
|
||||
return bool(self.get_tx_derivations(tx))
|
||||
|
||||
def ready_to_sign(self):
|
||||
return not self.is_watching_only()
|
||||
|
||||
|
||||
class Software_KeyStore(KeyStore):
|
||||
|
@ -142,6 +145,10 @@ class Imported_KeyStore(Software_KeyStore):
|
|||
# re-serialize the key so the internal storage format is consistent
|
||||
serialized_privkey = serialize_privkey(
|
||||
privkey, compressed, txin_type, internal_use=True)
|
||||
# NOTE: if the same pubkey is reused for multiple addresses (script types),
|
||||
# there will only be one pubkey-privkey pair for it in self.keypairs,
|
||||
# and the privkey will encode a txin_type but that txin_type can not be trusted.
|
||||
# Removing keys complicates this further.
|
||||
self.keypairs[pubkey] = pw_encode(serialized_privkey, password)
|
||||
return txin_type, pubkey
|
||||
|
||||
|
@ -531,6 +538,17 @@ class Hardware_KeyStore(KeyStore, Xpub):
|
|||
password = self.get_pubkey_from_xpub(xpub, ())
|
||||
return password
|
||||
|
||||
def has_usable_connection_with_device(self):
|
||||
if not hasattr(self, 'plugin'):
|
||||
return False
|
||||
client = self.plugin.get_client(self, force_pair=False)
|
||||
if client is None:
|
||||
return False
|
||||
return client.has_usable_connection_with_device()
|
||||
|
||||
def ready_to_sign(self):
|
||||
return super().ready_to_sign() and self.has_usable_connection_with_device()
|
||||
|
||||
|
||||
def bip39_normalize_passphrase(passphrase):
|
||||
return normalize('NFKD', passphrase or '')
|
||||
|
@ -615,7 +633,8 @@ def xpubkey_to_address(x_pubkey):
|
|||
mpk, s = Old_KeyStore.parse_xpubkey(x_pubkey)
|
||||
pubkey = Old_KeyStore.get_pubkey_from_mpk(mpk, s[0], s[1])
|
||||
else:
|
||||
raise BaseException("Cannot parse pubkey")
|
||||
raise BitcoinException("Cannot parse pubkey. prefix: {}"
|
||||
.format(x_pubkey[0:2]))
|
||||
if pubkey:
|
||||
address = public_key_to_p2pkh(bfh(pubkey))
|
||||
return pubkey, address
|
||||
|
@ -634,14 +653,15 @@ def hardware_keystore(d):
|
|||
if hw_type in hw_keystores:
|
||||
constructor = hw_keystores[hw_type]
|
||||
return constructor(d)
|
||||
raise BaseException('unknown hardware type', hw_type)
|
||||
raise WalletFileException('unknown hardware type: {}'.format(hw_type))
|
||||
|
||||
def load_keystore(storage, name):
|
||||
w = storage.get('wallet_type', 'standard')
|
||||
d = storage.get(name, {})
|
||||
t = d.get('type')
|
||||
if not t:
|
||||
raise BaseException('wallet format requires update')
|
||||
raise WalletFileException(
|
||||
'Wallet format requires update.\n'
|
||||
'Cannot find keystore for name {}'.format(name))
|
||||
if t == 'old':
|
||||
k = Old_KeyStore(d)
|
||||
elif t == 'imported':
|
||||
|
@ -651,7 +671,8 @@ def load_keystore(storage, name):
|
|||
elif t == 'hardware':
|
||||
k = hardware_keystore(d)
|
||||
else:
|
||||
raise BaseException('unknown wallet type', t)
|
||||
raise WalletFileException(
|
||||
'Unknown type {} for keystore named {}'.format(t, name))
|
||||
return k
|
||||
|
||||
|
||||
|
@ -709,7 +730,7 @@ def from_seed(seed, passphrase, is_p2sh):
|
|||
xtype = 'p2wsh' if is_p2sh else 'p2wpkh'
|
||||
keystore.add_xprv_from_seed(bip32_seed, xtype, der)
|
||||
else:
|
||||
raise BaseException(t)
|
||||
raise BitcoinException('Unexpected seed type {}'.format(t))
|
||||
return keystore
|
||||
|
||||
def from_private_key_list(text):
|
||||
|
@ -743,5 +764,5 @@ def from_master_key(text):
|
|||
elif is_xpub(text):
|
||||
k = from_xpub(text)
|
||||
else:
|
||||
raise BaseException('Invalid key')
|
||||
raise BitcoinException('Invalid master key')
|
||||
return k
|
||||
|
|
|
@ -91,7 +91,7 @@ def normalize_text(seed):
|
|||
|
||||
def load_wordlist(filename):
|
||||
path = os.path.join(os.path.dirname(__file__), 'wordlist', filename)
|
||||
with open(path, 'r') as f:
|
||||
with open(path, 'r', encoding='utf-8') as f:
|
||||
s = f.read().strip()
|
||||
s = unicodedata.normalize('NFKD', s)
|
||||
lines = s.split('\n')
|
||||
|
@ -157,28 +157,21 @@ class Mnemonic(object):
|
|||
i = i*n + k
|
||||
return i
|
||||
|
||||
def check_seed(self, seed, custom_entropy):
|
||||
assert is_new_seed(seed)
|
||||
i = self.mnemonic_decode(seed)
|
||||
return i % custom_entropy == 0
|
||||
|
||||
def make_seed(self, seed_type='standard', num_bits=132, custom_entropy=1):
|
||||
def make_seed(self, seed_type='standard', num_bits=132):
|
||||
prefix = version.seed_prefix(seed_type)
|
||||
# increase num_bits in order to obtain a uniform distibution for the last word
|
||||
bpw = math.log(len(self.wordlist), 2)
|
||||
num_bits = int(math.ceil(num_bits/bpw) * bpw)
|
||||
# handle custom entropy; make sure we add at least 16 bits
|
||||
n_custom = int(math.ceil(math.log(custom_entropy, 2)))
|
||||
n = max(16, num_bits - n_custom)
|
||||
print_error("make_seed", prefix, "adding %d bits"%n)
|
||||
my_entropy = 1
|
||||
while my_entropy < pow(2, n - bpw):
|
||||
# rounding
|
||||
n = int(math.ceil(num_bits/bpw) * bpw)
|
||||
print_error("make_seed. prefix: '%s'"%prefix, "entropy: %d bits"%n)
|
||||
entropy = 1
|
||||
while entropy < pow(2, n - bpw):
|
||||
# try again if seed would not contain enough words
|
||||
my_entropy = ecdsa.util.randrange(pow(2, n))
|
||||
entropy = ecdsa.util.randrange(pow(2, n))
|
||||
nonce = 0
|
||||
while True:
|
||||
nonce += 1
|
||||
i = custom_entropy * (my_entropy + nonce)
|
||||
i = entropy + nonce
|
||||
seed = self.mnemonic_encode(i)
|
||||
assert i == self.mnemonic_decode(seed)
|
||||
if is_old_seed(seed):
|
||||
|
|
|
@ -246,7 +246,7 @@ class Network(util.DaemonThread):
|
|||
return []
|
||||
path = os.path.join(self.config.path, "recent_servers")
|
||||
try:
|
||||
with open(path, "r") as f:
|
||||
with open(path, "r", encoding='utf-8') as f:
|
||||
data = f.read()
|
||||
return json.loads(data)
|
||||
except:
|
||||
|
@ -258,7 +258,7 @@ class Network(util.DaemonThread):
|
|||
path = os.path.join(self.config.path, "recent_servers")
|
||||
s = json.dumps(self.recent_servers, indent=4, sort_keys=True)
|
||||
try:
|
||||
with open(path, "w") as f:
|
||||
with open(path, "w", encoding='utf-8') as f:
|
||||
f.write(s)
|
||||
except:
|
||||
pass
|
||||
|
@ -319,7 +319,7 @@ class Network(util.DaemonThread):
|
|||
self.queue_request('server.peers.subscribe', [])
|
||||
self.request_fee_estimates()
|
||||
self.queue_request('blockchain.relayfee', [])
|
||||
for h in self.subscribed_addresses:
|
||||
for h in list(self.subscribed_addresses):
|
||||
self.queue_request('blockchain.scripthash.subscribe', [h])
|
||||
|
||||
def request_fee_estimates(self):
|
||||
|
@ -563,7 +563,7 @@ class Network(util.DaemonThread):
|
|||
self.notify('fee')
|
||||
elif method == 'blockchain.relayfee':
|
||||
if error is None:
|
||||
self.relay_fee = int(result * COIN)
|
||||
self.relay_fee = int(result * COIN) if result is not None else None
|
||||
self.print_error("relayfee", self.relay_fee)
|
||||
elif method == 'blockchain.block.get_chunk':
|
||||
self.on_get_chunk(interface, response)
|
||||
|
@ -677,7 +677,7 @@ class Network(util.DaemonThread):
|
|||
# check cached response for subscriptions
|
||||
r = self.sub_cache.get(k)
|
||||
if r is not None:
|
||||
util.print_error("cache hit", k)
|
||||
self.print_error("cache hit", k)
|
||||
callback(r)
|
||||
else:
|
||||
message_id = self.queue_request(method, params)
|
||||
|
@ -1089,7 +1089,7 @@ class Network(util.DaemonThread):
|
|||
def export_checkpoints(self, path):
|
||||
# run manually from the console to generate checkpoints
|
||||
cp = self.blockchain().get_checkpoints()
|
||||
with open(path, 'w') as f:
|
||||
with open(path, 'w', encoding='utf-8') as f:
|
||||
f.write(json.dumps(cp, indent=4))
|
||||
|
||||
def max_checkpoint(self):
|
||||
|
|
|
@ -89,7 +89,7 @@ def get_payment_request(url):
|
|||
error = "payment URL not pointing to a valid server"
|
||||
elif u.scheme == 'file':
|
||||
try:
|
||||
with open(u.path, 'r') as f:
|
||||
with open(u.path, 'r', encoding='utf-8') as f:
|
||||
data = f.read()
|
||||
except IOError:
|
||||
data = None
|
||||
|
@ -385,9 +385,9 @@ def check_ssl_config(config):
|
|||
from . import pem
|
||||
key_path = config.get('ssl_privkey')
|
||||
cert_path = config.get('ssl_chain')
|
||||
with open(key_path, 'r') as f:
|
||||
with open(key_path, 'r', encoding='utf-8') as f:
|
||||
params = pem.parse_private_key(f.read())
|
||||
with open(cert_path, 'r') as f:
|
||||
with open(cert_path, 'r', encoding='utf-8') as f:
|
||||
s = f.read()
|
||||
bList = pem.dePemList(s, "CERTIFICATE")
|
||||
# verify chain
|
||||
|
@ -405,10 +405,10 @@ def check_ssl_config(config):
|
|||
|
||||
def sign_request_with_x509(pr, key_path, cert_path):
|
||||
from . import pem
|
||||
with open(key_path, 'r') as f:
|
||||
with open(key_path, 'r', encoding='utf-8') as f:
|
||||
params = pem.parse_private_key(f.read())
|
||||
privkey = rsakey.RSAKey(*params)
|
||||
with open(cert_path, 'r') as f:
|
||||
with open(cert_path, 'r', encoding='utf-8') as f:
|
||||
s = f.read()
|
||||
bList = pem.dePemList(s, "CERTIFICATE")
|
||||
certificates = pb2.X509Certificates()
|
||||
|
@ -453,7 +453,11 @@ class InvoiceStore(object):
|
|||
|
||||
def set_paid(self, pr, txid):
|
||||
pr.tx = txid
|
||||
self.paid[txid] = pr.get_id()
|
||||
pr_id = pr.get_id()
|
||||
self.paid[txid] = pr_id
|
||||
if pr_id not in self.invoices:
|
||||
# in case the user had deleted it previously
|
||||
self.add(pr)
|
||||
|
||||
def load(self, d):
|
||||
for k, v in d.items():
|
||||
|
|
10
lib/plot.py
10
lib/plot.py
|
@ -1,17 +1,13 @@
|
|||
from PyQt5.QtGui import *
|
||||
from electrum.i18n import _
|
||||
|
||||
|
||||
import datetime
|
||||
from collections import defaultdict
|
||||
from electrum.bitcoin import COIN
|
||||
|
||||
import matplotlib
|
||||
matplotlib.use('Qt5Agg')
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.dates as md
|
||||
from matplotlib.patches import Ellipse
|
||||
from matplotlib.offsetbox import AnchoredOffsetbox, TextArea, DrawingArea, HPacker
|
||||
|
||||
from .i18n import _
|
||||
from .bitcoin import COIN
|
||||
|
||||
|
||||
class NothingToPlotException(Exception):
|
||||
|
|
|
@ -461,12 +461,14 @@ class DeviceMgr(ThreadJob, PrintError):
|
|||
def unpaired_device_infos(self, handler, plugin, devices=None):
|
||||
'''Returns a list of DeviceInfo objects: one for each connected,
|
||||
unpaired device accepted by the plugin.'''
|
||||
if not plugin.libraries_available:
|
||||
raise Exception('Missing libraries for {}'.format(plugin.name))
|
||||
if devices is None:
|
||||
devices = self.scan_devices()
|
||||
devices = [dev for dev in devices if not self.xpub_by_id(dev.id_)]
|
||||
infos = []
|
||||
for device in devices:
|
||||
if not device.product_key in plugin.DEVICE_IDS:
|
||||
if device.product_key not in plugin.DEVICE_IDS:
|
||||
continue
|
||||
client = self.create_client(device, handler, plugin)
|
||||
if not client:
|
||||
|
@ -482,9 +484,14 @@ class DeviceMgr(ThreadJob, PrintError):
|
|||
infos = self.unpaired_device_infos(handler, plugin, devices)
|
||||
if infos:
|
||||
break
|
||||
msg = _('Please insert your {}. Verify the cable is '
|
||||
'connected and that no other application is using it.\n\n'
|
||||
'Try to connect again?').format(plugin.device)
|
||||
msg = _('Please insert your {}').format(plugin.device)
|
||||
if keystore.label:
|
||||
msg += ' ({})'.format(keystore.label)
|
||||
msg += '. {}\n\n{}'.format(
|
||||
_('Verify the cable is connected and that '
|
||||
'no other application is using it.'),
|
||||
_('Try to connect again?')
|
||||
)
|
||||
if not handler.yes_no_question(msg):
|
||||
raise UserCancelled()
|
||||
devices = None
|
||||
|
@ -495,7 +502,7 @@ class DeviceMgr(ThreadJob, PrintError):
|
|||
if info.label == keystore.label:
|
||||
return info
|
||||
msg = _("Please select which {} device to use:").format(plugin.device)
|
||||
descriptions = [info.label + ' (%s)'%(_("initialized") if info.initialized else _("wiped")) for info in infos]
|
||||
descriptions = [str(info.label) + ' (%s)'%(_("initialized") if info.initialized else _("wiped")) for info in infos]
|
||||
c = handler.query_choice(msg, descriptions)
|
||||
if c is None:
|
||||
raise UserCancelled()
|
||||
|
@ -506,17 +513,15 @@ class DeviceMgr(ThreadJob, PrintError):
|
|||
handler.win.wallet.save_keystore()
|
||||
return info
|
||||
|
||||
def scan_devices(self):
|
||||
# All currently supported hardware libraries use hid, so we
|
||||
# assume it here. This can be easily abstracted if necessary.
|
||||
# Note this import must be local so those without hardware
|
||||
# wallet libraries are not affected.
|
||||
import hid
|
||||
self.print_error("scanning devices...")
|
||||
def _scan_devices_with_hid(self):
|
||||
try:
|
||||
import hid
|
||||
except ImportError:
|
||||
return []
|
||||
|
||||
with self.hid_lock:
|
||||
hid_list = hid.enumerate(0, 0)
|
||||
|
||||
# First see what's connected that we know about
|
||||
devices = []
|
||||
for d in hid_list:
|
||||
product_key = (d['vendor_id'], d['product_id'])
|
||||
|
@ -530,18 +535,31 @@ class DeviceMgr(ThreadJob, PrintError):
|
|||
id_ += str(interface_number) + str(usage_page)
|
||||
devices.append(Device(d['path'], interface_number,
|
||||
id_, product_key, usage_page))
|
||||
return devices
|
||||
|
||||
def scan_devices(self):
|
||||
self.print_error("scanning devices...")
|
||||
|
||||
# First see what's connected that we know about
|
||||
devices = self._scan_devices_with_hid()
|
||||
|
||||
# Let plugin handlers enumerate devices we don't know about
|
||||
for f in self.enumerate_func:
|
||||
devices.extend(f())
|
||||
try:
|
||||
new_devices = f()
|
||||
except BaseException as e:
|
||||
self.print_error('custom device enum failed. func {}, error {}'
|
||||
.format(str(f), str(e)))
|
||||
else:
|
||||
devices.extend(new_devices)
|
||||
|
||||
# Now find out what was disconnected
|
||||
# find out what was disconnected
|
||||
pairs = [(dev.path, dev.id_) for dev in devices]
|
||||
disconnected_ids = []
|
||||
with self.lock:
|
||||
connected = {}
|
||||
for client, pair in self.clients.items():
|
||||
if pair in pairs:
|
||||
if pair in pairs and client.has_usable_connection_with_device():
|
||||
connected[client] = pair
|
||||
else:
|
||||
disconnected_ids.append(pair[1])
|
||||
|
|
|
@ -36,7 +36,7 @@ else:
|
|||
|
||||
try:
|
||||
libzbar = ctypes.cdll.LoadLibrary(name)
|
||||
except OSError:
|
||||
except BaseException:
|
||||
libzbar = None
|
||||
|
||||
|
||||
|
|
|
@ -211,9 +211,14 @@ class SimpleConfig(PrintError):
|
|||
return
|
||||
path = os.path.join(self.path, "config")
|
||||
s = json.dumps(self.user_config, indent=4, sort_keys=True)
|
||||
with open(path, "w") as f:
|
||||
f.write(s)
|
||||
os.chmod(path, stat.S_IREAD | stat.S_IWRITE)
|
||||
try:
|
||||
with open(path, "w", encoding='utf-8') as f:
|
||||
f.write(s)
|
||||
os.chmod(path, stat.S_IREAD | stat.S_IWRITE)
|
||||
except FileNotFoundError:
|
||||
# datadir probably deleted while running...
|
||||
if os.path.exists(self.path): # or maybe not?
|
||||
raise
|
||||
|
||||
def get_wallet_path(self):
|
||||
"""Set the path of the wallet."""
|
||||
|
@ -228,6 +233,10 @@ class SimpleConfig(PrintError):
|
|||
return path
|
||||
|
||||
# default path
|
||||
if not os.path.exists(self.path):
|
||||
raise FileNotFoundError(
|
||||
_('Electrum datadir does not exist. Was it deleted while running?') + '\n' +
|
||||
_('Should be at {}').format(self.path))
|
||||
dirpath = os.path.join(self.path, "wallets")
|
||||
if not os.path.exists(dirpath):
|
||||
if os.path.islink(dirpath):
|
||||
|
@ -489,7 +498,7 @@ def read_user_config(path):
|
|||
if not os.path.exists(config_path):
|
||||
return {}
|
||||
try:
|
||||
with open(config_path, "r") as f:
|
||||
with open(config_path, "r", encoding='utf-8') as f:
|
||||
data = f.read()
|
||||
result = json.loads(data)
|
||||
except:
|
||||
|
|
|
@ -33,7 +33,7 @@ import pbkdf2, hmac, hashlib
|
|||
import base64
|
||||
import zlib
|
||||
|
||||
from .util import PrintError, profiler, InvalidPassword
|
||||
from .util import PrintError, profiler, InvalidPassword, WalletFileException
|
||||
from .plugins import run_hook, plugin_loaders
|
||||
from .keystore import bip44_derivation
|
||||
from . import bitcoin
|
||||
|
@ -51,6 +51,8 @@ FINAL_SEED_VERSION = 16 # electrum >= 2.7 will set this to prevent
|
|||
def multisig_type(wallet_type):
|
||||
'''If wallet_type is mofn multi-sig, return [m, n],
|
||||
otherwise return None.'''
|
||||
if not wallet_type:
|
||||
return None
|
||||
match = re.match('(\d+)of(\d+)', wallet_type)
|
||||
if match:
|
||||
match = [int(x) for x in match.group(1, 2)]
|
||||
|
@ -75,7 +77,7 @@ class WalletStorage(PrintError):
|
|||
self.modified = False
|
||||
self.pubkey = None
|
||||
if self.file_exists():
|
||||
with open(self.path, "r") as f:
|
||||
with open(self.path, "r", encoding='utf-8') as f:
|
||||
self.raw = f.read()
|
||||
self._encryption_version = self._init_encryption_version()
|
||||
if not self.is_encrypted():
|
||||
|
@ -111,7 +113,7 @@ class WalletStorage(PrintError):
|
|||
|
||||
if not self.manual_upgrades:
|
||||
if self.requires_split():
|
||||
raise BaseException("This wallet has multiple accounts and must be split")
|
||||
raise WalletFileException("This wallet has multiple accounts and must be split")
|
||||
if self.requires_upgrade():
|
||||
self.upgrade()
|
||||
|
||||
|
@ -172,7 +174,7 @@ class WalletStorage(PrintError):
|
|||
elif v == STO_EV_XPUB_PW:
|
||||
return b'BIE2'
|
||||
else:
|
||||
raise Exception('no encryption magic for version: %s' % v)
|
||||
raise WalletFileException('no encryption magic for version: %s' % v)
|
||||
|
||||
def decrypt(self, password):
|
||||
ec_key = self.get_key(password)
|
||||
|
@ -255,7 +257,7 @@ class WalletStorage(PrintError):
|
|||
s = s.decode('utf8')
|
||||
|
||||
temp_path = "%s.tmp.%s" % (self.path, os.getpid())
|
||||
with open(temp_path, "w") as f:
|
||||
with open(temp_path, "w", encoding='utf-8') as f:
|
||||
f.write(s)
|
||||
f.flush()
|
||||
os.fsync(f.fileno())
|
||||
|
@ -318,7 +320,7 @@ class WalletStorage(PrintError):
|
|||
storage2.write()
|
||||
result.append(new_path)
|
||||
else:
|
||||
raise BaseException("This wallet has multiple accounts and must be split")
|
||||
raise WalletFileException("This wallet has multiple accounts and must be split")
|
||||
return result
|
||||
|
||||
def requires_upgrade(self):
|
||||
|
@ -417,7 +419,7 @@ class WalletStorage(PrintError):
|
|||
d['seed'] = seed
|
||||
self.put(key, d)
|
||||
else:
|
||||
raise
|
||||
raise WalletFileException('Unable to tell wallet type. Is this even a wallet file?')
|
||||
# remove junk
|
||||
self.put('master_public_key', None)
|
||||
self.put('master_public_keys', None)
|
||||
|
@ -541,7 +543,7 @@ class WalletStorage(PrintError):
|
|||
else:
|
||||
addresses.append(addr)
|
||||
if addresses and keypairs:
|
||||
raise BaseException('mixed addresses and privkeys')
|
||||
raise WalletFileException('mixed addresses and privkeys')
|
||||
elif addresses:
|
||||
self.put('addresses', addresses)
|
||||
self.put('accounts', None)
|
||||
|
@ -551,7 +553,7 @@ class WalletStorage(PrintError):
|
|||
self.put('keypairs', keypairs)
|
||||
self.put('accounts', None)
|
||||
else:
|
||||
raise BaseException('no addresses or privkeys')
|
||||
raise WalletFileException('no addresses or privkeys')
|
||||
|
||||
def convert_account(self):
|
||||
if not self._is_upgrade_method_needed(0, 13):
|
||||
|
@ -564,9 +566,9 @@ class WalletStorage(PrintError):
|
|||
if cur_version > max_version:
|
||||
return False
|
||||
elif cur_version < min_version:
|
||||
raise BaseException(
|
||||
('storage upgrade: unexpected version %d (should be %d-%d)'
|
||||
% (cur_version, min_version, max_version)))
|
||||
raise WalletFileException(
|
||||
'storage upgrade: unexpected version {} (should be {}-{})'
|
||||
.format(cur_version, min_version, max_version))
|
||||
else:
|
||||
return True
|
||||
|
||||
|
@ -582,7 +584,9 @@ class WalletStorage(PrintError):
|
|||
if not seed_version:
|
||||
seed_version = OLD_SEED_VERSION if len(self.get('master_public_key','')) == 128 else NEW_SEED_VERSION
|
||||
if seed_version > FINAL_SEED_VERSION:
|
||||
raise BaseException('This version of Electrum is too old to open this wallet')
|
||||
raise WalletFileException('This version of Electrum is too old to open this wallet.\n'
|
||||
'(highest supported storage version: {}, version of this file: {})'
|
||||
.format(FINAL_SEED_VERSION, seed_version))
|
||||
if seed_version==14 and self.get('seed_type') == 'segwit':
|
||||
self.raise_unsupported_version(seed_version)
|
||||
if seed_version >=12:
|
||||
|
@ -605,4 +609,4 @@ class WalletStorage(PrintError):
|
|||
else:
|
||||
# creation was complete if electrum was run from source
|
||||
msg += "\nPlease open this file with Electrum 1.9.8, and move your coins to a new wallet."
|
||||
raise BaseException(msg)
|
||||
raise WalletFileException(msg)
|
||||
|
|
|
@ -50,6 +50,8 @@ class Synchronizer(ThreadJob):
|
|||
self.requested_histories = {}
|
||||
self.requested_addrs = set()
|
||||
self.lock = Lock()
|
||||
|
||||
self.initialized = False
|
||||
self.initialize()
|
||||
|
||||
def parse_response(self, response):
|
||||
|
@ -84,7 +86,7 @@ class Synchronizer(ThreadJob):
|
|||
return bh2u(hashlib.sha256(status.encode('ascii')).digest())
|
||||
|
||||
def on_address_status(self, response):
|
||||
if self.wallet.synchronizer is None:
|
||||
if self.wallet.synchronizer is None and self.initialized:
|
||||
return # we have been killed, this was just an orphan callback
|
||||
params, result = self.parse_response(response)
|
||||
if not params:
|
||||
|
@ -100,14 +102,17 @@ class Synchronizer(ThreadJob):
|
|||
self.requested_addrs.remove(addr)
|
||||
|
||||
def on_address_history(self, response):
|
||||
if self.wallet.synchronizer is None:
|
||||
if self.wallet.synchronizer is None and self.initialized:
|
||||
return # we have been killed, this was just an orphan callback
|
||||
params, result = self.parse_response(response)
|
||||
if not params:
|
||||
return
|
||||
addr = params[0]
|
||||
server_status = self.requested_histories.get(addr)
|
||||
if server_status is None:
|
||||
self.print_error("receiving history (unsolicited)", addr, len(result))
|
||||
return
|
||||
self.print_error("receiving history", addr, len(result))
|
||||
server_status = self.requested_histories[addr]
|
||||
hashes = set(map(lambda item: item['tx_hash'], result))
|
||||
hist = list(map(lambda item: (item['tx_hash'], item['height']), result))
|
||||
# tx_fees
|
||||
|
@ -131,7 +136,7 @@ class Synchronizer(ThreadJob):
|
|||
self.requested_histories.pop(addr)
|
||||
|
||||
def tx_response(self, response):
|
||||
if self.wallet.synchronizer is None:
|
||||
if self.wallet.synchronizer is None and self.initialized:
|
||||
return # we have been killed, this was just an orphan callback
|
||||
params, result = self.parse_response(response)
|
||||
if not params:
|
||||
|
@ -183,6 +188,7 @@ class Synchronizer(ThreadJob):
|
|||
if self.requested_tx:
|
||||
self.print_error("missing tx", self.requested_tx)
|
||||
self.subscribe_to_addresses(set(self.wallet.get_addresses()))
|
||||
self.initialized = True
|
||||
|
||||
def run(self):
|
||||
'''Called from the network proxy thread main loop.'''
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
import unittest
|
||||
|
||||
from lib import constants
|
||||
|
||||
|
||||
class TestCaseForTestnet(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
constants.set_testnet()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
super().tearDownClass()
|
||||
constants.set_mainnet()
|
|
@ -11,10 +11,13 @@ from lib.bitcoin import (
|
|||
var_int, op_push, address_to_script, regenerate_key,
|
||||
verify_message, deserialize_privkey, serialize_privkey, is_segwit_address,
|
||||
is_b58_address, address_to_scripthash, is_minikey, is_compressed, is_xpub,
|
||||
xpub_type, is_xprv, is_bip32_derivation, seed_type)
|
||||
xpub_type, is_xprv, is_bip32_derivation, seed_type, EncodeBase58Check)
|
||||
from lib.util import bfh
|
||||
from lib import constants
|
||||
|
||||
from . import TestCaseForTestnet
|
||||
|
||||
|
||||
try:
|
||||
import ecdsa
|
||||
except ImportError:
|
||||
|
@ -164,17 +167,7 @@ class Test_bitcoin(unittest.TestCase):
|
|||
self.assertEqual(address_to_script('3PyjzJ3im7f7bcV724GR57edKDqoZvH7Ji'), 'a914f47c8954e421031ad04ecd8e7752c9479206b9d387')
|
||||
|
||||
|
||||
class Test_bitcoin_testnet(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
constants.set_testnet()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
super().tearDownClass()
|
||||
constants.set_mainnet()
|
||||
class Test_bitcoin_testnet(TestCaseForTestnet):
|
||||
|
||||
def test_address_to_script(self):
|
||||
# bech32 native segwit
|
||||
|
@ -267,6 +260,79 @@ class Test_xprv_xpub(unittest.TestCase):
|
|||
self.assertFalse(is_bip32_derivation(""))
|
||||
self.assertFalse(is_bip32_derivation("m/q8462"))
|
||||
|
||||
def test_version_bytes(self):
|
||||
xprv_headers_b58 = {
|
||||
'standard': 'xprv',
|
||||
'p2wpkh-p2sh': 'yprv',
|
||||
'p2wsh-p2sh': 'Yprv',
|
||||
'p2wpkh': 'zprv',
|
||||
'p2wsh': 'Zprv',
|
||||
}
|
||||
xpub_headers_b58 = {
|
||||
'standard': 'xpub',
|
||||
'p2wpkh-p2sh': 'ypub',
|
||||
'p2wsh-p2sh': 'Ypub',
|
||||
'p2wpkh': 'zpub',
|
||||
'p2wsh': 'Zpub',
|
||||
}
|
||||
for xtype, xkey_header_bytes in constants.net.XPRV_HEADERS.items():
|
||||
xkey_header_bytes = bfh("%08x" % xkey_header_bytes)
|
||||
xkey_bytes = xkey_header_bytes + bytes([0] * 74)
|
||||
xkey_b58 = EncodeBase58Check(xkey_bytes)
|
||||
self.assertTrue(xkey_b58.startswith(xprv_headers_b58[xtype]))
|
||||
|
||||
xkey_bytes = xkey_header_bytes + bytes([255] * 74)
|
||||
xkey_b58 = EncodeBase58Check(xkey_bytes)
|
||||
self.assertTrue(xkey_b58.startswith(xprv_headers_b58[xtype]))
|
||||
|
||||
for xtype, xkey_header_bytes in constants.net.XPUB_HEADERS.items():
|
||||
xkey_header_bytes = bfh("%08x" % xkey_header_bytes)
|
||||
xkey_bytes = xkey_header_bytes + bytes([0] * 74)
|
||||
xkey_b58 = EncodeBase58Check(xkey_bytes)
|
||||
self.assertTrue(xkey_b58.startswith(xpub_headers_b58[xtype]))
|
||||
|
||||
xkey_bytes = xkey_header_bytes + bytes([255] * 74)
|
||||
xkey_b58 = EncodeBase58Check(xkey_bytes)
|
||||
self.assertTrue(xkey_b58.startswith(xpub_headers_b58[xtype]))
|
||||
|
||||
|
||||
class Test_xprv_xpub_testnet(TestCaseForTestnet):
|
||||
|
||||
def test_version_bytes(self):
|
||||
xprv_headers_b58 = {
|
||||
'standard': 'tprv',
|
||||
'p2wpkh-p2sh': 'uprv',
|
||||
'p2wsh-p2sh': 'Uprv',
|
||||
'p2wpkh': 'vprv',
|
||||
'p2wsh': 'Vprv',
|
||||
}
|
||||
xpub_headers_b58 = {
|
||||
'standard': 'tpub',
|
||||
'p2wpkh-p2sh': 'upub',
|
||||
'p2wsh-p2sh': 'Upub',
|
||||
'p2wpkh': 'vpub',
|
||||
'p2wsh': 'Vpub',
|
||||
}
|
||||
for xtype, xkey_header_bytes in constants.net.XPRV_HEADERS.items():
|
||||
xkey_header_bytes = bfh("%08x" % xkey_header_bytes)
|
||||
xkey_bytes = xkey_header_bytes + bytes([0] * 74)
|
||||
xkey_b58 = EncodeBase58Check(xkey_bytes)
|
||||
self.assertTrue(xkey_b58.startswith(xprv_headers_b58[xtype]))
|
||||
|
||||
xkey_bytes = xkey_header_bytes + bytes([255] * 74)
|
||||
xkey_b58 = EncodeBase58Check(xkey_bytes)
|
||||
self.assertTrue(xkey_b58.startswith(xprv_headers_b58[xtype]))
|
||||
|
||||
for xtype, xkey_header_bytes in constants.net.XPUB_HEADERS.items():
|
||||
xkey_header_bytes = bfh("%08x" % xkey_header_bytes)
|
||||
xkey_bytes = xkey_header_bytes + bytes([0] * 74)
|
||||
xkey_b58 = EncodeBase58Check(xkey_bytes)
|
||||
self.assertTrue(xkey_b58.startswith(xpub_headers_b58[xtype]))
|
||||
|
||||
xkey_bytes = xkey_header_bytes + bytes([255] * 74)
|
||||
xkey_b58 = EncodeBase58Check(xkey_bytes)
|
||||
self.assertTrue(xkey_b58.startswith(xpub_headers_b58[xtype]))
|
||||
|
||||
|
||||
class Test_keyImport(unittest.TestCase):
|
||||
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import unittest
|
||||
|
||||
from lib import transaction
|
||||
from lib.bitcoin import TYPE_ADDRESS
|
||||
|
||||
from lib.keystore import xpubkey_to_address
|
||||
|
||||
from lib.util import bh2u
|
||||
from lib.util import bh2u, bfh
|
||||
|
||||
unsigned_blob = '01000000012a5c9a94fcde98f5581cd00162c60a13936ceb75389ea65bf38633b424eb4031000000005701ff4c53ff0488b21e03ef2afea18000000089689bff23e1e7fb2f161daa37270a97a3d8c2e537584b2d304ecb47b86d21fc021b010d3bd425f8cf2e04824bfdf1f1f5ff1d51fadd9a41f9e3fb8dd3403b1bfe00000000ffffffff0140420f00000000001976a914230ac37834073a42146f11ef8414ae929feaafc388ac00000000'
|
||||
signed_blob = '01000000012a5c9a94fcde98f5581cd00162c60a13936ceb75389ea65bf38633b424eb4031000000006c493046022100a82bbc57a0136751e5433f41cf000b3f1a99c6744775e76ec764fb78c54ee100022100f9e80b7de89de861dc6fb0c1429d5da72c2b6b2ee2406bc9bfb1beedd729d985012102e61d176da16edd1d258a200ad9759ef63adf8e14cd97f53227bae35cdb84d2f6ffffffff0140420f00000000001976a914230ac37834073a42146f11ef8414ae929feaafc388ac00000000'
|
||||
|
@ -167,450 +166,593 @@ class TestTransaction(unittest.TestCase):
|
|||
tx = transaction.Transaction(v2_blob)
|
||||
self.assertEqual(tx.txid(), "b97f9180173ab141b61b9f944d841e60feec691d6daab4d4d932b24dd36606fe")
|
||||
|
||||
def test_get_address_from_output_script(self):
|
||||
# the inverse of this test is in test_bitcoin: test_address_to_script
|
||||
addr_from_script = lambda script: transaction.get_address_from_output_script(bfh(script))
|
||||
ADDR = transaction.TYPE_ADDRESS
|
||||
|
||||
# bech32 native segwit
|
||||
# test vectors from BIP-0173
|
||||
self.assertEqual((ADDR, 'bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4'), addr_from_script('0014751e76e8199196d454941c45d1b3a323f1433bd6'))
|
||||
self.assertEqual((ADDR, 'bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx'), addr_from_script('5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6'))
|
||||
self.assertEqual((ADDR, 'bc1sw50qa3jx3s'), addr_from_script('6002751e'))
|
||||
self.assertEqual((ADDR, 'bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj'), addr_from_script('5210751e76e8199196d454941c45d1b3a323'))
|
||||
|
||||
# base58 p2pkh
|
||||
self.assertEqual((ADDR, '14gcRovpkCoGkCNBivQBvw7eso7eiNAbxG'), addr_from_script('76a91428662c67561b95c79d2257d2a93d9d151c977e9188ac'))
|
||||
self.assertEqual((ADDR, '1BEqfzh4Y3zzLosfGhw1AsqbEKVW6e1qHv'), addr_from_script('76a914704f4b81cadb7bf7e68c08cd3657220f680f863c88ac'))
|
||||
|
||||
# base58 p2sh
|
||||
self.assertEqual((ADDR, '35ZqQJcBQMZ1rsv8aSuJ2wkC7ohUCQMJbT'), addr_from_script('a9142a84cf00d47f699ee7bbc1dea5ec1bdecb4ac15487'))
|
||||
self.assertEqual((ADDR, '3PyjzJ3im7f7bcV724GR57edKDqoZvH7Ji'), addr_from_script('a914f47c8954e421031ad04ecd8e7752c9479206b9d387'))
|
||||
|
||||
#####
|
||||
|
||||
def _run_naive_tests_on_tx(self, raw_tx, txid):
|
||||
tx = transaction.Transaction(raw_tx)
|
||||
self.assertEqual(txid, tx.txid())
|
||||
self.assertEqual(raw_tx, tx.serialize())
|
||||
self.assertTrue(tx.estimated_size() >= 0)
|
||||
|
||||
def test_txid_coinbase_to_p2pk(self):
|
||||
tx = transaction.Transaction('01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4103400d0302ef02062f503253482f522cfabe6d6dd90d39663d10f8fd25ec88338295d4c6ce1c90d4aeb368d8bdbadcc1da3b635801000000000000000474073e03ffffffff013c25cf2d01000000434104b0bd634234abbb1ba1e986e884185c61cf43e001f9137f23c2c409273eb16e6537a576782eba668a7ef8bd3b3cfb1edb7117ab65129b8a2e681f3c1e0908ef7bac00000000')
|
||||
self.assertEqual('dbaf14e1c476e76ea05a8b71921a46d6b06f0a950f17c5f9f1a03b8fae467f10', tx.txid())
|
||||
raw_tx = '01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4103400d0302ef02062f503253482f522cfabe6d6dd90d39663d10f8fd25ec88338295d4c6ce1c90d4aeb368d8bdbadcc1da3b635801000000000000000474073e03ffffffff013c25cf2d01000000434104b0bd634234abbb1ba1e986e884185c61cf43e001f9137f23c2c409273eb16e6537a576782eba668a7ef8bd3b3cfb1edb7117ab65129b8a2e681f3c1e0908ef7bac00000000'
|
||||
txid = 'dbaf14e1c476e76ea05a8b71921a46d6b06f0a950f17c5f9f1a03b8fae467f10'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_coinbase_to_p2pkh(self):
|
||||
tx = transaction.Transaction('01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff25033ca0030400001256124d696e656420627920425443204775696c640800000d41000007daffffffff01c00d1298000000001976a91427a1f12771de5cc3b73941664b2537c15316be4388ac00000000')
|
||||
self.assertEqual('4328f9311c6defd9ae1bd7f4516b62acf64b361eb39dfcf09d9925c5fd5c61e8', tx.txid())
|
||||
raw_tx = '01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff25033ca0030400001256124d696e656420627920425443204775696c640800000d41000007daffffffff01c00d1298000000001976a91427a1f12771de5cc3b73941664b2537c15316be4388ac00000000'
|
||||
txid = '4328f9311c6defd9ae1bd7f4516b62acf64b361eb39dfcf09d9925c5fd5c61e8'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_segwit_coinbase_to_p2pk(self):
|
||||
tx = transaction.Transaction('020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0502cd010101ffffffff0240be402500000000232103f4e686cdfc96f375e7c338c40c9b85f4011bb843a3e62e46a1de424ef87e9385ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000')
|
||||
self.assertEqual('fb5a57c24e640a6d8d831eb6e41505f3d54363c507da3733b098d820e3803301', tx.txid())
|
||||
raw_tx = '020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0502cd010101ffffffff0240be402500000000232103f4e686cdfc96f375e7c338c40c9b85f4011bb843a3e62e46a1de424ef87e9385ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000'
|
||||
txid = 'fb5a57c24e640a6d8d831eb6e41505f3d54363c507da3733b098d820e3803301'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_segwit_coinbase_to_p2pkh(self):
|
||||
tx = transaction.Transaction('020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0502c3010101ffffffff0240be4025000000001976a9141ea896d897483e0eb33dd6423f4a07970d0a0a2788ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000')
|
||||
self.assertEqual('ed3d100577477d799107eba97e76770b3efa253c7200e9abfb43da5d2b33513e', tx.txid())
|
||||
raw_tx = '020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0502c3010101ffffffff0240be4025000000001976a9141ea896d897483e0eb33dd6423f4a07970d0a0a2788ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000'
|
||||
txid = 'ed3d100577477d799107eba97e76770b3efa253c7200e9abfb43da5d2b33513e'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_p2pk_to_p2pkh(self):
|
||||
tx = transaction.Transaction('010000000118231a31d2df84f884ced6af11dc24306319577d4d7c340124a7e2dd9c314077000000004847304402200b6c45891aed48937241907bc3e3868ee4c792819821fcde33311e5a3da4789a02205021b59692b652a01f5f009bd481acac2f647a7d9c076d71d85869763337882e01fdffffff016c95052a010000001976a9149c4891e7791da9e622532c97f43863768264faaf88ac00000000')
|
||||
self.assertEqual('90ba90a5b115106d26663fce6c6215b8699c5d4b2672dd30756115f3337dddf9', tx.txid())
|
||||
raw_tx = '010000000118231a31d2df84f884ced6af11dc24306319577d4d7c340124a7e2dd9c314077000000004847304402200b6c45891aed48937241907bc3e3868ee4c792819821fcde33311e5a3da4789a02205021b59692b652a01f5f009bd481acac2f647a7d9c076d71d85869763337882e01fdffffff016c95052a010000001976a9149c4891e7791da9e622532c97f43863768264faaf88ac00000000'
|
||||
txid = '90ba90a5b115106d26663fce6c6215b8699c5d4b2672dd30756115f3337dddf9'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_p2pk_to_p2sh(self):
|
||||
tx = transaction.Transaction('0100000001e4643183d6497823576d17ac2439fb97eba24be8137f312e10fcc16483bb2d070000000048473044022032bbf0394dfe3b004075e3cbb3ea7071b9184547e27f8f73f967c4b3f6a21fa4022073edd5ae8b7b638f25872a7a308bb53a848baa9b9cc70af45fcf3c683d36a55301fdffffff011821814a0000000017a9143c640bc28a346749c09615b50211cb051faff00f8700000000')
|
||||
self.assertEqual('172bdf5a690b874385b98d7ab6f6af807356f03a26033c6a65ab79b4ac2085b5', tx.txid())
|
||||
raw_tx = '0100000001e4643183d6497823576d17ac2439fb97eba24be8137f312e10fcc16483bb2d070000000048473044022032bbf0394dfe3b004075e3cbb3ea7071b9184547e27f8f73f967c4b3f6a21fa4022073edd5ae8b7b638f25872a7a308bb53a848baa9b9cc70af45fcf3c683d36a55301fdffffff011821814a0000000017a9143c640bc28a346749c09615b50211cb051faff00f8700000000'
|
||||
txid = '172bdf5a690b874385b98d7ab6f6af807356f03a26033c6a65ab79b4ac2085b5'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_p2pk_to_p2wpkh(self):
|
||||
tx = transaction.Transaction('01000000015e5e2bf15f5793fdfd01e0ccd380033797ed2d4dba9498426ca84904176c26610000000049483045022100c77aff69f7ab4bb148f9bccffc5a87ee893c4f7f7f96c97ba98d2887a0f632b9022046367bdb683d58fa5b2e43cfc8a9c6d57724a27e03583942d8e7b9afbfeea5ab01fdffffff017289824a00000000160014460fc70f208bffa9abf3ae4abbd2f629d9cdcf5900000000')
|
||||
self.assertEqual('ca554b1014952f900aa8cf6e7ab02137a6fdcf933ad6a218de3891a2ef0c350d', tx.txid())
|
||||
raw_tx = '01000000015e5e2bf15f5793fdfd01e0ccd380033797ed2d4dba9498426ca84904176c26610000000049483045022100c77aff69f7ab4bb148f9bccffc5a87ee893c4f7f7f96c97ba98d2887a0f632b9022046367bdb683d58fa5b2e43cfc8a9c6d57724a27e03583942d8e7b9afbfeea5ab01fdffffff017289824a00000000160014460fc70f208bffa9abf3ae4abbd2f629d9cdcf5900000000'
|
||||
txid = 'ca554b1014952f900aa8cf6e7ab02137a6fdcf933ad6a218de3891a2ef0c350d'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_p2pkh_to_p2pkh(self):
|
||||
tx = transaction.Transaction('0100000001f9dd7d33f315617530dd72264b5d9c69b815626cce3f66266d1015b1a590ba90000000006a4730440220699bfee3d280a499daf4af5593e8750b54fef0557f3c9f717bfa909493a84f60022057718eec7985b7796bb8630bf6ea2e9bf2892ac21bd6ab8f741a008537139ffe012103b4289890b40590447b57f773b5843bf0400e9cead08be225fac587b3c2a8e973fdffffff01ec24052a010000001976a914ce9ff3d15ed5f3a3d94b583b12796d063879b11588ac00000000')
|
||||
self.assertEqual('24737c68f53d4b519939119ed83b2a8d44d716d7f3ca98bcecc0fbb92c2085ce', tx.txid())
|
||||
raw_tx = '0100000001f9dd7d33f315617530dd72264b5d9c69b815626cce3f66266d1015b1a590ba90000000006a4730440220699bfee3d280a499daf4af5593e8750b54fef0557f3c9f717bfa909493a84f60022057718eec7985b7796bb8630bf6ea2e9bf2892ac21bd6ab8f741a008537139ffe012103b4289890b40590447b57f773b5843bf0400e9cead08be225fac587b3c2a8e973fdffffff01ec24052a010000001976a914ce9ff3d15ed5f3a3d94b583b12796d063879b11588ac00000000'
|
||||
txid = '24737c68f53d4b519939119ed83b2a8d44d716d7f3ca98bcecc0fbb92c2085ce'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_p2pkh_to_p2sh(self):
|
||||
tx = transaction.Transaction('010000000195232c30f6611b9f2f82ec63f5b443b132219c425e1824584411f3d16a7a54bc000000006b4830450221009f39ac457dc8ff316e5cc03161c9eff6212d8694ccb88d801dbb32e85d8ed100022074230bb05e99b85a6a50d2b71e7bf04d80be3f1d014ea038f93943abd79421d101210317be0f7e5478e087453b9b5111bdad586038720f16ac9658fd16217ffd7e5785fdffffff0200e40b540200000017a914d81df3751b9e7dca920678cc19cac8d7ec9010b08718dfd63c2c0000001976a914303c42b63569ff5b390a2016ff44651cd84c7c8988acc7010000')
|
||||
self.assertEqual('155e4740fa59f374abb4e133b87247dccc3afc233cb97c2bf2b46bba3094aedc', tx.txid())
|
||||
raw_tx = '010000000195232c30f6611b9f2f82ec63f5b443b132219c425e1824584411f3d16a7a54bc000000006b4830450221009f39ac457dc8ff316e5cc03161c9eff6212d8694ccb88d801dbb32e85d8ed100022074230bb05e99b85a6a50d2b71e7bf04d80be3f1d014ea038f93943abd79421d101210317be0f7e5478e087453b9b5111bdad586038720f16ac9658fd16217ffd7e5785fdffffff0200e40b540200000017a914d81df3751b9e7dca920678cc19cac8d7ec9010b08718dfd63c2c0000001976a914303c42b63569ff5b390a2016ff44651cd84c7c8988acc7010000'
|
||||
txid = '155e4740fa59f374abb4e133b87247dccc3afc233cb97c2bf2b46bba3094aedc'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_p2pkh_to_p2wpkh(self):
|
||||
tx = transaction.Transaction('0100000001ce85202cb9fbc0ecbc98caf3d716d7448d2a3bd89e113999514b3df5687c7324000000006b483045022100adab7b6cb1179079c9dfc0021f4db0346730b7c16555fcc4363059dcdd95f653022028bcb816f4fb98615fb8f4b18af3ad3708e2d72f94a6466cc2736055860422cf012102a16a25148dd692462a691796db0a4a5531bcca970a04107bf184a2c9f7fd8b12fdffffff012eb6042a010000001600147d0170de18eecbe84648979d52b666dddee0b47400000000')
|
||||
self.assertEqual('ed29e100499e2a3a64a2b0cb3a68655b9acd690d29690fa541be530462bf3d3c', tx.txid())
|
||||
raw_tx = '0100000001ce85202cb9fbc0ecbc98caf3d716d7448d2a3bd89e113999514b3df5687c7324000000006b483045022100adab7b6cb1179079c9dfc0021f4db0346730b7c16555fcc4363059dcdd95f653022028bcb816f4fb98615fb8f4b18af3ad3708e2d72f94a6466cc2736055860422cf012102a16a25148dd692462a691796db0a4a5531bcca970a04107bf184a2c9f7fd8b12fdffffff012eb6042a010000001600147d0170de18eecbe84648979d52b666dddee0b47400000000'
|
||||
txid = 'ed29e100499e2a3a64a2b0cb3a68655b9acd690d29690fa541be530462bf3d3c'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_p2sh_to_p2pkh(self):
|
||||
tx = transaction.Transaction('01000000000101f9823f87af35d158e7dc81a67011f4e511e3f6cab07ac108e524b0ff8b950b39000000002322002041f0237866eb72e4a75cd6faf5ccd738703193907d883aa7b3a8169c636706a9fdffffff020065cd1d000000001976a9148150cd6cf729e7e262699875fec1f760b0aab3cc88acc46f9a3b0000000017a91433ccd0f95a7b9d8eef68be40bb59c64d6e14d87287040047304402205ca97126a5956c2deaa956a2006d79a348775d727074a04b71d9c18eb5e5525402207b9353497af15881100a2786adab56c8930c02d46cc1a8b55496c06e22d3459b01483045022100b4fa898057927c2d920ae79bca752dda58202ea8617d3e6ed96cbd5d1c0eb2fc02200824c0e742d1b4d643cec439444f5d8779c18d4f42c2c87cce24044a3babf2df0147522102db78786b3c214826bd27010e3c663b02d67144499611ee3f2461c633eb8f1247210377082028c124098b59a5a1e0ea7fd3ebca72d59c793aecfeedd004304bac15cd52aec9010000')
|
||||
self.assertEqual('17e1d498ba82503e3bfa81ac4897a57e33f3d36b41bcf4765ba604466c478986', tx.txid())
|
||||
raw_tx = '01000000000101f9823f87af35d158e7dc81a67011f4e511e3f6cab07ac108e524b0ff8b950b39000000002322002041f0237866eb72e4a75cd6faf5ccd738703193907d883aa7b3a8169c636706a9fdffffff020065cd1d000000001976a9148150cd6cf729e7e262699875fec1f760b0aab3cc88acc46f9a3b0000000017a91433ccd0f95a7b9d8eef68be40bb59c64d6e14d87287040047304402205ca97126a5956c2deaa956a2006d79a348775d727074a04b71d9c18eb5e5525402207b9353497af15881100a2786adab56c8930c02d46cc1a8b55496c06e22d3459b01483045022100b4fa898057927c2d920ae79bca752dda58202ea8617d3e6ed96cbd5d1c0eb2fc02200824c0e742d1b4d643cec439444f5d8779c18d4f42c2c87cce24044a3babf2df0147522102db78786b3c214826bd27010e3c663b02d67144499611ee3f2461c633eb8f1247210377082028c124098b59a5a1e0ea7fd3ebca72d59c793aecfeedd004304bac15cd52aec9010000'
|
||||
txid = '17e1d498ba82503e3bfa81ac4897a57e33f3d36b41bcf4765ba604466c478986'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_p2sh_to_p2sh(self):
|
||||
tx = transaction.Transaction('01000000000101b58520acb479ab656a3c03263af0567380aff6b67a8db98543870b695adf2b170000000017160014cfd2b9f7ed9d4d4429ed6946dbb3315f75e85f14fdffffff020065cd1d0000000017a91485f5681bec38f9f07ae9790d7f27c2bb90b5b63c87106ab32c0000000017a914ff402e164dfce874435641ae9ac41fc6fb14c4e18702483045022100b3d1c89c7c92151ed1df78815924569446782776b6a2c170ca5d74c5dd1ad9b102201d7bab1974fd2aa66546dd15c1f1e276d787453cec31b55a2bd97b050abf20140121024a1742ece86df3dbce4717c228cf51e625030cef7f5e6dde33a4fffdd17569eac7010000')
|
||||
self.assertEqual('ead0e7abfb24ddbcd6b89d704d7a6091e43804a458baa930adf6f1cb5b6b42f7', tx.txid())
|
||||
raw_tx = '01000000000101b58520acb479ab656a3c03263af0567380aff6b67a8db98543870b695adf2b170000000017160014cfd2b9f7ed9d4d4429ed6946dbb3315f75e85f14fdffffff020065cd1d0000000017a91485f5681bec38f9f07ae9790d7f27c2bb90b5b63c87106ab32c0000000017a914ff402e164dfce874435641ae9ac41fc6fb14c4e18702483045022100b3d1c89c7c92151ed1df78815924569446782776b6a2c170ca5d74c5dd1ad9b102201d7bab1974fd2aa66546dd15c1f1e276d787453cec31b55a2bd97b050abf20140121024a1742ece86df3dbce4717c228cf51e625030cef7f5e6dde33a4fffdd17569eac7010000'
|
||||
txid = 'ead0e7abfb24ddbcd6b89d704d7a6091e43804a458baa930adf6f1cb5b6b42f7'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_p2sh_to_p2wpkh(self):
|
||||
tx = transaction.Transaction('010000000001018689476c4604a65b76f4bc416bd3f3337ea59748ac81fa3b3e5082ba98d4e1170100000023220020ae40340707f9726c0f453c3d47c96e7f3b7b4b85608eb3668b69bbef9c7ab374fdffffff0218b2cc1d0000000017a914f2fdd81e606ff2ab804d7bb46bf8838a711c277b870065cd1d0000000016001496ad8959c1f0382984ecc4da61c118b4c8751e5104004730440220387b9e7d402fbcada9ba55a27a8d0563eafa9904ebd2f8f7e3d86e4b45bc0ec202205f37fa0e2bf8cbd384f804562651d7c6f69adce5db4c1a5b9103250a47f73e6b01473044022074903f4dd4fd6b32289be909eb5109924740daa55e79be6dbd728687683f9afa02205d934d981ca12cbec450611ca81dc4127f8da5e07dd63d41049380502de3f15401475221025c3810b37147105106cef970f9b91d3735819dee4882d515c1187dbd0b8f0c792103e007c492323084f1c103beff255836408af89bb9ae7f2fcf60502c28ff4b0c9152aeca010000')
|
||||
self.assertEqual('6f294c84cbd0241650931b4c1be3dfb2f175d682c7a9538b30b173e1083deed3', tx.txid())
|
||||
raw_tx = '010000000001018689476c4604a65b76f4bc416bd3f3337ea59748ac81fa3b3e5082ba98d4e1170100000023220020ae40340707f9726c0f453c3d47c96e7f3b7b4b85608eb3668b69bbef9c7ab374fdffffff0218b2cc1d0000000017a914f2fdd81e606ff2ab804d7bb46bf8838a711c277b870065cd1d0000000016001496ad8959c1f0382984ecc4da61c118b4c8751e5104004730440220387b9e7d402fbcada9ba55a27a8d0563eafa9904ebd2f8f7e3d86e4b45bc0ec202205f37fa0e2bf8cbd384f804562651d7c6f69adce5db4c1a5b9103250a47f73e6b01473044022074903f4dd4fd6b32289be909eb5109924740daa55e79be6dbd728687683f9afa02205d934d981ca12cbec450611ca81dc4127f8da5e07dd63d41049380502de3f15401475221025c3810b37147105106cef970f9b91d3735819dee4882d515c1187dbd0b8f0c792103e007c492323084f1c103beff255836408af89bb9ae7f2fcf60502c28ff4b0c9152aeca010000'
|
||||
txid = '6f294c84cbd0241650931b4c1be3dfb2f175d682c7a9538b30b173e1083deed3'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_p2wpkh_to_p2pkh(self):
|
||||
tx = transaction.Transaction('0100000000010197e6bf4a70bc118e3a8d9842ed80422e335679dfc29b5ba0f9123f6a5863b8470000000000fdffffff02402bca7f130000001600146f579c953d9e7e7719f2baa20bde22eb5f24119200e87648170000001976a9140cd8fa5fd81c3acf33f93efd179b388de8dd693388ac0247304402204ff33b3ea8fb270f62409bfc257457ca5eb1fec5e4d3a7c11aa487207e131d4d022032726b998e338e5245746716e5cd0b40d32b69d1535c3d841f049d98a5d819b1012102dc3ce3220363aff579eb2c45c973e8b186a829c987c3caea77c61975666e7d1bc8010000')
|
||||
self.assertEqual('c721ed35767a3a209b688e68e3bb136a72d2b631fe81c56be8bdbb948c343dbc', tx.txid())
|
||||
raw_tx = '0100000000010197e6bf4a70bc118e3a8d9842ed80422e335679dfc29b5ba0f9123f6a5863b8470000000000fdffffff02402bca7f130000001600146f579c953d9e7e7719f2baa20bde22eb5f24119200e87648170000001976a9140cd8fa5fd81c3acf33f93efd179b388de8dd693388ac0247304402204ff33b3ea8fb270f62409bfc257457ca5eb1fec5e4d3a7c11aa487207e131d4d022032726b998e338e5245746716e5cd0b40d32b69d1535c3d841f049d98a5d819b1012102dc3ce3220363aff579eb2c45c973e8b186a829c987c3caea77c61975666e7d1bc8010000'
|
||||
txid = 'c721ed35767a3a209b688e68e3bb136a72d2b631fe81c56be8bdbb948c343dbc'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_p2wpkh_to_p2sh(self):
|
||||
tx = transaction.Transaction('010000000001013c3dbf620453be41a50f69290d69cd9a5b65683acbb0a2643a2a9e4900e129ed0000000000fdffffff02002f68590000000017a914c7c4dcd0ddf70f15c6df13b4a4d56e9f13c49b2787a0429cd000000000160014e514e3ecf89731e7853e4f3a20983484c569d3910247304402205368cc548209303db5a8f2ebc282bd0f7af0d080ce0f7637758587f94d3971fb0220098cec5752554758bc5fa4de332b980d5e0054a807541581dc5e4de3ed29647501210233717cd73d95acfdf6bd72c4fb5df27cd6bd69ce947daa3f4a442183a97877efc8010000')
|
||||
self.assertEqual('390b958bffb024e508c17ab0caf6e311e5f41170a681dce758d135af873f82f9', tx.txid())
|
||||
raw_tx = '010000000001013c3dbf620453be41a50f69290d69cd9a5b65683acbb0a2643a2a9e4900e129ed0000000000fdffffff02002f68590000000017a914c7c4dcd0ddf70f15c6df13b4a4d56e9f13c49b2787a0429cd000000000160014e514e3ecf89731e7853e4f3a20983484c569d3910247304402205368cc548209303db5a8f2ebc282bd0f7af0d080ce0f7637758587f94d3971fb0220098cec5752554758bc5fa4de332b980d5e0054a807541581dc5e4de3ed29647501210233717cd73d95acfdf6bd72c4fb5df27cd6bd69ce947daa3f4a442183a97877efc8010000'
|
||||
txid = '390b958bffb024e508c17ab0caf6e311e5f41170a681dce758d135af873f82f9'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_p2wpkh_to_p2wpkh(self):
|
||||
tx = transaction.Transaction('010000000001010d350cefa29138de18a2d63a93cffda63721b07a6ecfa80a902f9514104b55ca0000000000fdffffff012a4a824a00000000160014b869999d342a5d42d6dc7af1efc28456da40297a024730440220475bb55814a52ea1036919e4408218c693b8bf93637b9f54c821b5baa3b846e102207276ed7a79493142c11fb01808a4142bbdd525ae7bdccdf8ecb7b8e3c856b4d90121024cdeaca7a53a7e23a1edbe9260794eaa83063534b5f111ee3c67d8b0cb88f0eec8010000')
|
||||
self.assertEqual('51087ece75c697cc872d2e643d646b0f3e1f2666fa1820b7bff4343d50dd680e', tx.txid())
|
||||
raw_tx = '010000000001010d350cefa29138de18a2d63a93cffda63721b07a6ecfa80a902f9514104b55ca0000000000fdffffff012a4a824a00000000160014b869999d342a5d42d6dc7af1efc28456da40297a024730440220475bb55814a52ea1036919e4408218c693b8bf93637b9f54c821b5baa3b846e102207276ed7a79493142c11fb01808a4142bbdd525ae7bdccdf8ecb7b8e3c856b4d90121024cdeaca7a53a7e23a1edbe9260794eaa83063534b5f111ee3c67d8b0cb88f0eec8010000'
|
||||
txid = '51087ece75c697cc872d2e643d646b0f3e1f2666fa1820b7bff4343d50dd680e'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_input_p2wsh_p2sh_not_multisig(self):
|
||||
tx = transaction.Transaction('0100000000010160f84fdcda039c3ca1b20038adea2d49a53db92f7c467e8def13734232bb610804000000232200202814720f16329ab81cb8867c4d447bd13255931f23e6655944c9ada1797fcf88ffffffff0ba3dcfc04000000001976a91488124a57c548c9e7b1dd687455af803bd5765dea88acc9f44900000000001976a914da55045a0ccd40a56ce861946d13eb861eb5f2d788ac49825e000000000017a914ca34d4b190e36479aa6e0023cfe0a8537c6aa8dd87680c0d00000000001976a914651102524c424b2e7c44787c4f21e4c54dffafc088acf02fa9000000000017a914ee6c596e6f7066466d778d4f9ba633a564a6e95d874d250900000000001976a9146ca7976b48c04fd23867748382ee8401b1d27c2988acf5119600000000001976a914cf47d5dcdba02fd547c600697097252d38c3214a88ace08a12000000000017a914017bef79d92d5ec08c051786bad317e5dd3befcf87e3d76201000000001976a9148ec1b88b66d142bcbdb42797a0fd402c23e0eec288ac718f6900000000001976a914e66344472a224ce6f843f2989accf435ae6a808988ac65e51300000000001976a914cad6717c13a2079066f876933834210ebbe68c3f88ac0347304402201a4907c4706104320313e182ecbb1b265b2d023a79586671386de86bb47461590220472c3db9fc99a728ebb9b555a72e3481d20b181bd059a9c1acadfb853d90c96c01210338a46f2a54112fef8803c8478bc17e5f8fc6a5ec276903a946c1fafb2e3a8b181976a914eda8660085bf607b82bd18560ca8f3a9ec49178588ac00000000')
|
||||
self.assertEqual('e9933221a150f78f9f224899f8568ff6422ffcc28ca3d53d87936368ff7c4b1d', tx.txid())
|
||||
raw_tx = '0100000000010160f84fdcda039c3ca1b20038adea2d49a53db92f7c467e8def13734232bb610804000000232200202814720f16329ab81cb8867c4d447bd13255931f23e6655944c9ada1797fcf88ffffffff0ba3dcfc04000000001976a91488124a57c548c9e7b1dd687455af803bd5765dea88acc9f44900000000001976a914da55045a0ccd40a56ce861946d13eb861eb5f2d788ac49825e000000000017a914ca34d4b190e36479aa6e0023cfe0a8537c6aa8dd87680c0d00000000001976a914651102524c424b2e7c44787c4f21e4c54dffafc088acf02fa9000000000017a914ee6c596e6f7066466d778d4f9ba633a564a6e95d874d250900000000001976a9146ca7976b48c04fd23867748382ee8401b1d27c2988acf5119600000000001976a914cf47d5dcdba02fd547c600697097252d38c3214a88ace08a12000000000017a914017bef79d92d5ec08c051786bad317e5dd3befcf87e3d76201000000001976a9148ec1b88b66d142bcbdb42797a0fd402c23e0eec288ac718f6900000000001976a914e66344472a224ce6f843f2989accf435ae6a808988ac65e51300000000001976a914cad6717c13a2079066f876933834210ebbe68c3f88ac0347304402201a4907c4706104320313e182ecbb1b265b2d023a79586671386de86bb47461590220472c3db9fc99a728ebb9b555a72e3481d20b181bd059a9c1acadfb853d90c96c01210338a46f2a54112fef8803c8478bc17e5f8fc6a5ec276903a946c1fafb2e3a8b181976a914eda8660085bf607b82bd18560ca8f3a9ec49178588ac00000000'
|
||||
txid = 'e9933221a150f78f9f224899f8568ff6422ffcc28ca3d53d87936368ff7c4b1d'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
# input: p2sh, not multisig
|
||||
def test_txid_regression_issue_3899(self):
|
||||
tx = transaction.Transaction('0100000004328685b0352c981d3d451b471ae3bfc78b82565dc2a54049a81af273f0a9fd9c010000000b0009630330472d5fae685bffffffff328685b0352c981d3d451b471ae3bfc78b82565dc2a54049a81af273f0a9fd9c020000000b0009630359646d5fae6858ffffffff328685b0352c981d3d451b471ae3bfc78b82565dc2a54049a81af273f0a9fd9c030000000b000963034bd4715fae6854ffffffff328685b0352c981d3d451b471ae3bfc78b82565dc2a54049a81af273f0a9fd9c040000000b000963036de8705fae6860ffffffff0130750000000000001976a914b5abca61d20f9062fb1fdbb880d9d93bac36675188ac00000000')
|
||||
self.assertEqual('f570d5d1e965ee61bcc7005f8fefb1d3abbed9d7ddbe035e2a68fa07e5fc4a0d', tx.txid())
|
||||
raw_tx = '0100000004328685b0352c981d3d451b471ae3bfc78b82565dc2a54049a81af273f0a9fd9c010000000b0009630330472d5fae685bffffffff328685b0352c981d3d451b471ae3bfc78b82565dc2a54049a81af273f0a9fd9c020000000b0009630359646d5fae6858ffffffff328685b0352c981d3d451b471ae3bfc78b82565dc2a54049a81af273f0a9fd9c030000000b000963034bd4715fae6854ffffffff328685b0352c981d3d451b471ae3bfc78b82565dc2a54049a81af273f0a9fd9c040000000b000963036de8705fae6860ffffffff0130750000000000001976a914b5abca61d20f9062fb1fdbb880d9d93bac36675188ac00000000'
|
||||
txid = 'f570d5d1e965ee61bcc7005f8fefb1d3abbed9d7ddbe035e2a68fa07e5fc4a0d'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_negative_version_num(self):
|
||||
raw_tx = 'f0b47b9a01ecf5e5c3bbf2cf1f71ecdc7f708b0b222432e914b394e24aad1494a42990ddfc000000008b483045022100852744642305a99ad74354e9495bf43a1f96ded470c256cd32e129290f1fa191022030c11d294af6a61b3da6ed2c0c296251d21d113cfd71ec11126517034b0dcb70014104a0fe6e4a600f859a0932f701d3af8e0ecd4be886d91045f06a5a6b931b95873aea1df61da281ba29cadb560dad4fc047cf47b4f7f2570da4c0b810b3dfa7e500ffffffff0240420f00000000001976a9147eeacb8a9265cd68c92806611f704fc55a21e1f588ac05f00d00000000001976a914eb3bd8ccd3ba6f1570f844b59ba3e0a667024a6a88acff7f0000'
|
||||
txid = 'c659729a7fea5071361c2c1a68551ca2bf77679b27086cc415adeeb03852e369'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
|
||||
# these transactions are from Bitcoin Core unit tests --->
|
||||
# https://github.com/bitcoin/bitcoin/blob/11376b5583a283772c82f6d32d0007cdbf5b8ef0/src/test/data/tx_valid.json
|
||||
|
||||
def test_txid_bitcoin_core_0001(self):
|
||||
tx = transaction.Transaction('0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000490047304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000')
|
||||
self.assertEqual('23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63', tx.txid())
|
||||
raw_tx = '0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000490047304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000'
|
||||
txid = '23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0002(self):
|
||||
tx = transaction.Transaction('0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004a0048304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2bab01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000')
|
||||
self.assertEqual('fcabc409d8e685da28536e1e5ccc91264d755cd4c57ed4cae3dbaa4d3b93e8ed', tx.txid())
|
||||
raw_tx = '0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004a0048304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2bab01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000'
|
||||
txid = 'fcabc409d8e685da28536e1e5ccc91264d755cd4c57ed4cae3dbaa4d3b93e8ed'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0003(self):
|
||||
tx = transaction.Transaction('0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004a01ff47304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000')
|
||||
self.assertEqual('c9aa95f2c48175fdb70b34c23f1c3fc44f869b073a6f79b1343fbce30c3cb575', tx.txid())
|
||||
raw_tx = '0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004a01ff47304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000'
|
||||
txid = 'c9aa95f2c48175fdb70b34c23f1c3fc44f869b073a6f79b1343fbce30c3cb575'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0004(self):
|
||||
tx = transaction.Transaction('0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000495147304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000')
|
||||
self.assertEqual('da94fda32b55deb40c3ed92e135d69df7efc4ee6665e0beb07ef500f407c9fd2', tx.txid())
|
||||
raw_tx = '0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000495147304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000'
|
||||
txid = 'da94fda32b55deb40c3ed92e135d69df7efc4ee6665e0beb07ef500f407c9fd2'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0005(self):
|
||||
tx = transaction.Transaction('0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000494f47304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000')
|
||||
self.assertEqual('f76f897b206e4f78d60fe40f2ccb542184cfadc34354d3bb9bdc30cc2f432b86', tx.txid())
|
||||
raw_tx = '0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000494f47304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000'
|
||||
txid = 'f76f897b206e4f78d60fe40f2ccb542184cfadc34354d3bb9bdc30cc2f432b86'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0006(self):
|
||||
tx = transaction.Transaction('01000000010276b76b07f4935c70acf54fbf1f438a4c397a9fb7e633873c4dd3bc062b6b40000000008c493046022100d23459d03ed7e9511a47d13292d3430a04627de6235b6e51a40f9cd386f2abe3022100e7d25b080f0bb8d8d5f878bba7d54ad2fda650ea8d158a33ee3cbd11768191fd004104b0e2c879e4daf7b9ab68350228c159766676a14f5815084ba166432aab46198d4cca98fa3e9981d0a90b2effc514b76279476550ba3663fdcaff94c38420e9d5000000000100093d00000000001976a9149a7b0f3b80c6baaeedce0a0842553800f832ba1f88ac00000000')
|
||||
self.assertEqual('c99c49da4c38af669dea436d3e73780dfdb6c1ecf9958baa52960e8baee30e73', tx.txid())
|
||||
raw_tx = '01000000010276b76b07f4935c70acf54fbf1f438a4c397a9fb7e633873c4dd3bc062b6b40000000008c493046022100d23459d03ed7e9511a47d13292d3430a04627de6235b6e51a40f9cd386f2abe3022100e7d25b080f0bb8d8d5f878bba7d54ad2fda650ea8d158a33ee3cbd11768191fd004104b0e2c879e4daf7b9ab68350228c159766676a14f5815084ba166432aab46198d4cca98fa3e9981d0a90b2effc514b76279476550ba3663fdcaff94c38420e9d5000000000100093d00000000001976a9149a7b0f3b80c6baaeedce0a0842553800f832ba1f88ac00000000'
|
||||
txid = 'c99c49da4c38af669dea436d3e73780dfdb6c1ecf9958baa52960e8baee30e73'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0007(self):
|
||||
tx = transaction.Transaction('01000000010001000000000000000000000000000000000000000000000000000000000000000000006a473044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a012103ba8c8b86dea131c22ab967e6dd99bdae8eff7a1f75a2c35f1f944109e3fe5e22ffffffff010000000000000000015100000000')
|
||||
self.assertEqual('e41ffe19dff3cbedb413a2ca3fbbcd05cb7fd7397ffa65052f8928aa9c700092', tx.txid())
|
||||
raw_tx = '01000000010001000000000000000000000000000000000000000000000000000000000000000000006a473044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a012103ba8c8b86dea131c22ab967e6dd99bdae8eff7a1f75a2c35f1f944109e3fe5e22ffffffff010000000000000000015100000000'
|
||||
txid = 'e41ffe19dff3cbedb413a2ca3fbbcd05cb7fd7397ffa65052f8928aa9c700092'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0008(self):
|
||||
tx = transaction.Transaction('01000000023d6cf972d4dff9c519eff407ea800361dd0a121de1da8b6f4138a2f25de864b4000000008a4730440220ffda47bfc776bcd269da4832626ac332adfca6dd835e8ecd83cd1ebe7d709b0e022049cffa1cdc102a0b56e0e04913606c70af702a1149dc3b305ab9439288fee090014104266abb36d66eb4218a6dd31f09bb92cf3cfa803c7ea72c1fc80a50f919273e613f895b855fb7465ccbc8919ad1bd4a306c783f22cd3227327694c4fa4c1c439affffffff21ebc9ba20594737864352e95b727f1a565756f9d365083eb1a8596ec98c97b7010000008a4730440220503ff10e9f1e0de731407a4a245531c9ff17676eda461f8ceeb8c06049fa2c810220c008ac34694510298fa60b3f000df01caa244f165b727d4896eb84f81e46bcc4014104266abb36d66eb4218a6dd31f09bb92cf3cfa803c7ea72c1fc80a50f919273e613f895b855fb7465ccbc8919ad1bd4a306c783f22cd3227327694c4fa4c1c439affffffff01f0da5200000000001976a914857ccd42dded6df32949d4646dfa10a92458cfaa88ac00000000')
|
||||
self.assertEqual('f7fdd091fa6d8f5e7a8c2458f5c38faffff2d3f1406b6e4fe2c99dcc0d2d1cbb', tx.txid())
|
||||
raw_tx = '01000000023d6cf972d4dff9c519eff407ea800361dd0a121de1da8b6f4138a2f25de864b4000000008a4730440220ffda47bfc776bcd269da4832626ac332adfca6dd835e8ecd83cd1ebe7d709b0e022049cffa1cdc102a0b56e0e04913606c70af702a1149dc3b305ab9439288fee090014104266abb36d66eb4218a6dd31f09bb92cf3cfa803c7ea72c1fc80a50f919273e613f895b855fb7465ccbc8919ad1bd4a306c783f22cd3227327694c4fa4c1c439affffffff21ebc9ba20594737864352e95b727f1a565756f9d365083eb1a8596ec98c97b7010000008a4730440220503ff10e9f1e0de731407a4a245531c9ff17676eda461f8ceeb8c06049fa2c810220c008ac34694510298fa60b3f000df01caa244f165b727d4896eb84f81e46bcc4014104266abb36d66eb4218a6dd31f09bb92cf3cfa803c7ea72c1fc80a50f919273e613f895b855fb7465ccbc8919ad1bd4a306c783f22cd3227327694c4fa4c1c439affffffff01f0da5200000000001976a914857ccd42dded6df32949d4646dfa10a92458cfaa88ac00000000'
|
||||
txid = 'f7fdd091fa6d8f5e7a8c2458f5c38faffff2d3f1406b6e4fe2c99dcc0d2d1cbb'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0009(self):
|
||||
tx = transaction.Transaction('01000000020002000000000000000000000000000000000000000000000000000000000000000000000151ffffffff0001000000000000000000000000000000000000000000000000000000000000000000006b483045022100c9cdd08798a28af9d1baf44a6c77bcc7e279f47dc487c8c899911bc48feaffcc0220503c5c50ae3998a733263c5c0f7061b483e2b56c4c41b456e7d2f5a78a74c077032102d5c25adb51b61339d2b05315791e21bbe80ea470a49db0135720983c905aace0ffffffff010000000000000000015100000000')
|
||||
self.assertEqual('b56471690c3ff4f7946174e51df68b47455a0d29344c351377d712e6d00eabe5', tx.txid())
|
||||
raw_tx = '01000000020002000000000000000000000000000000000000000000000000000000000000000000000151ffffffff0001000000000000000000000000000000000000000000000000000000000000000000006b483045022100c9cdd08798a28af9d1baf44a6c77bcc7e279f47dc487c8c899911bc48feaffcc0220503c5c50ae3998a733263c5c0f7061b483e2b56c4c41b456e7d2f5a78a74c077032102d5c25adb51b61339d2b05315791e21bbe80ea470a49db0135720983c905aace0ffffffff010000000000000000015100000000'
|
||||
txid = 'b56471690c3ff4f7946174e51df68b47455a0d29344c351377d712e6d00eabe5'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0010(self):
|
||||
tx = transaction.Transaction('010000000100010000000000000000000000000000000000000000000000000000000000000000000009085768617420697320ffffffff010000000000000000015100000000')
|
||||
self.assertEqual('99517e5b47533453cc7daa332180f578be68b80370ecfe84dbfff7f19d791da4', tx.txid())
|
||||
raw_tx = '010000000100010000000000000000000000000000000000000000000000000000000000000000000009085768617420697320ffffffff010000000000000000015100000000'
|
||||
txid = '99517e5b47533453cc7daa332180f578be68b80370ecfe84dbfff7f19d791da4'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0011(self):
|
||||
tx = transaction.Transaction('01000000010001000000000000000000000000000000000000000000000000000000000000000000006e493046022100c66c9cdf4c43609586d15424c54707156e316d88b0a1534c9e6b0d4f311406310221009c0fe51dbc9c4ab7cc25d3fdbeccf6679fe6827f08edf2b4a9f16ee3eb0e438a0123210338e8034509af564c62644c07691942e0c056752008a173c89f60ab2a88ac2ebfacffffffff010000000000000000015100000000')
|
||||
self.assertEqual('ab097537b528871b9b64cb79a769ae13c3c3cd477cc9dddeebe657eabd7fdcea', tx.txid())
|
||||
raw_tx = '01000000010001000000000000000000000000000000000000000000000000000000000000000000006e493046022100c66c9cdf4c43609586d15424c54707156e316d88b0a1534c9e6b0d4f311406310221009c0fe51dbc9c4ab7cc25d3fdbeccf6679fe6827f08edf2b4a9f16ee3eb0e438a0123210338e8034509af564c62644c07691942e0c056752008a173c89f60ab2a88ac2ebfacffffffff010000000000000000015100000000'
|
||||
txid = 'ab097537b528871b9b64cb79a769ae13c3c3cd477cc9dddeebe657eabd7fdcea'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0012(self):
|
||||
tx = transaction.Transaction('01000000010001000000000000000000000000000000000000000000000000000000000000000000006e493046022100e1eadba00d9296c743cb6ecc703fd9ddc9b3cd12906176a226ae4c18d6b00796022100a71aef7d2874deff681ba6080f1b278bac7bb99c61b08a85f4311970ffe7f63f012321030c0588dc44d92bdcbf8e72093466766fdc265ead8db64517b0c542275b70fffbacffffffff010040075af0750700015100000000')
|
||||
self.assertEqual('4d163e00f1966e9a1eab8f9374c3e37f4deb4857c247270e25f7d79a999d2dc9', tx.txid())
|
||||
raw_tx = '01000000010001000000000000000000000000000000000000000000000000000000000000000000006e493046022100e1eadba00d9296c743cb6ecc703fd9ddc9b3cd12906176a226ae4c18d6b00796022100a71aef7d2874deff681ba6080f1b278bac7bb99c61b08a85f4311970ffe7f63f012321030c0588dc44d92bdcbf8e72093466766fdc265ead8db64517b0c542275b70fffbacffffffff010040075af0750700015100000000'
|
||||
txid = '4d163e00f1966e9a1eab8f9374c3e37f4deb4857c247270e25f7d79a999d2dc9'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0013(self):
|
||||
tx = transaction.Transaction('01000000010001000000000000000000000000000000000000000000000000000000000000000000006d483045022027deccc14aa6668e78a8c9da3484fbcd4f9dcc9bb7d1b85146314b21b9ae4d86022100d0b43dece8cfb07348de0ca8bc5b86276fa88f7f2138381128b7c36ab2e42264012321029bb13463ddd5d2cc05da6e84e37536cb9525703cfd8f43afdb414988987a92f6acffffffff020040075af075070001510000000000000000015100000000')
|
||||
self.assertEqual('9fe2ef9dde70e15d78894a4800b7df3bbfb1addb9a6f7d7c204492fdb6ee6cc4', tx.txid())
|
||||
raw_tx = '01000000010001000000000000000000000000000000000000000000000000000000000000000000006d483045022027deccc14aa6668e78a8c9da3484fbcd4f9dcc9bb7d1b85146314b21b9ae4d86022100d0b43dece8cfb07348de0ca8bc5b86276fa88f7f2138381128b7c36ab2e42264012321029bb13463ddd5d2cc05da6e84e37536cb9525703cfd8f43afdb414988987a92f6acffffffff020040075af075070001510000000000000000015100000000'
|
||||
txid = '9fe2ef9dde70e15d78894a4800b7df3bbfb1addb9a6f7d7c204492fdb6ee6cc4'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0014(self):
|
||||
tx = transaction.Transaction('01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff025151ffffffff010000000000000000015100000000')
|
||||
self.assertEqual('99d3825137602e577aeaf6a2e3c9620fd0e605323dc5265da4a570593be791d4', tx.txid())
|
||||
raw_tx = '01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff025151ffffffff010000000000000000015100000000'
|
||||
txid = '99d3825137602e577aeaf6a2e3c9620fd0e605323dc5265da4a570593be791d4'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0015(self):
|
||||
tx = transaction.Transaction('01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff6451515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151ffffffff010000000000000000015100000000')
|
||||
self.assertEqual('c0d67409923040cc766bbea12e4c9154393abef706db065ac2e07d91a9ba4f84', tx.txid())
|
||||
raw_tx = '01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff6451515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151ffffffff010000000000000000015100000000'
|
||||
txid = 'c0d67409923040cc766bbea12e4c9154393abef706db065ac2e07d91a9ba4f84'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0016(self):
|
||||
tx = transaction.Transaction('010000000200010000000000000000000000000000000000000000000000000000000000000000000049483045022100d180fd2eb9140aeb4210c9204d3f358766eb53842b2a9473db687fa24b12a3cc022079781799cd4f038b85135bbe49ec2b57f306b2bb17101b17f71f000fcab2b6fb01ffffffff0002000000000000000000000000000000000000000000000000000000000000000000004847304402205f7530653eea9b38699e476320ab135b74771e1c48b81a5d041e2ca84b9be7a802200ac8d1f40fb026674fe5a5edd3dea715c27baa9baca51ed45ea750ac9dc0a55e81ffffffff010100000000000000015100000000')
|
||||
self.assertEqual('c610d85d3d5fdf5046be7f123db8a0890cee846ee58de8a44667cfd1ab6b8666', tx.txid())
|
||||
raw_tx = '010000000200010000000000000000000000000000000000000000000000000000000000000000000049483045022100d180fd2eb9140aeb4210c9204d3f358766eb53842b2a9473db687fa24b12a3cc022079781799cd4f038b85135bbe49ec2b57f306b2bb17101b17f71f000fcab2b6fb01ffffffff0002000000000000000000000000000000000000000000000000000000000000000000004847304402205f7530653eea9b38699e476320ab135b74771e1c48b81a5d041e2ca84b9be7a802200ac8d1f40fb026674fe5a5edd3dea715c27baa9baca51ed45ea750ac9dc0a55e81ffffffff010100000000000000015100000000'
|
||||
txid = 'c610d85d3d5fdf5046be7f123db8a0890cee846ee58de8a44667cfd1ab6b8666'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0017(self):
|
||||
tx = transaction.Transaction('01000000020001000000000000000000000000000000000000000000000000000000000000000000004948304502203a0f5f0e1f2bdbcd04db3061d18f3af70e07f4f467cbc1b8116f267025f5360b022100c792b6e215afc5afc721a351ec413e714305cb749aae3d7fee76621313418df101010000000002000000000000000000000000000000000000000000000000000000000000000000004847304402205f7530653eea9b38699e476320ab135b74771e1c48b81a5d041e2ca84b9be7a802200ac8d1f40fb026674fe5a5edd3dea715c27baa9baca51ed45ea750ac9dc0a55e81ffffffff010100000000000000015100000000')
|
||||
self.assertEqual('a647a7b3328d2c698bfa1ee2dd4e5e05a6cea972e764ccb9bd29ea43817ca64f', tx.txid())
|
||||
raw_tx = '01000000020001000000000000000000000000000000000000000000000000000000000000000000004948304502203a0f5f0e1f2bdbcd04db3061d18f3af70e07f4f467cbc1b8116f267025f5360b022100c792b6e215afc5afc721a351ec413e714305cb749aae3d7fee76621313418df101010000000002000000000000000000000000000000000000000000000000000000000000000000004847304402205f7530653eea9b38699e476320ab135b74771e1c48b81a5d041e2ca84b9be7a802200ac8d1f40fb026674fe5a5edd3dea715c27baa9baca51ed45ea750ac9dc0a55e81ffffffff010100000000000000015100000000'
|
||||
txid = 'a647a7b3328d2c698bfa1ee2dd4e5e05a6cea972e764ccb9bd29ea43817ca64f'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0018(self):
|
||||
tx = transaction.Transaction('010000000370ac0a1ae588aaf284c308d67ca92c69a39e2db81337e563bf40c59da0a5cf63000000006a4730440220360d20baff382059040ba9be98947fd678fb08aab2bb0c172efa996fd8ece9b702201b4fb0de67f015c90e7ac8a193aeab486a1f587e0f54d0fb9552ef7f5ce6caec032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff7d815b6447e35fbea097e00e028fb7dfbad4f3f0987b4734676c84f3fcd0e804010000006b483045022100c714310be1e3a9ff1c5f7cacc65c2d8e781fc3a88ceb063c6153bf950650802102200b2d0979c76e12bb480da635f192cc8dc6f905380dd4ac1ff35a4f68f462fffd032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff3f1f097333e4d46d51f5e77b53264db8f7f5d2e18217e1099957d0f5af7713ee010000006c493046022100b663499ef73273a3788dea342717c2640ac43c5a1cf862c9e09b206fcb3f6bb8022100b09972e75972d9148f2bdd462e5cb69b57c1214b88fc55ca638676c07cfc10d8032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff0380841e00000000001976a914bfb282c70c4191f45b5a6665cad1682f2c9cfdfb88ac80841e00000000001976a9149857cc07bed33a5cf12b9c5e0500b675d500c81188ace0fd1c00000000001976a91443c52850606c872403c0601e69fa34b26f62db4a88ac00000000')
|
||||
self.assertEqual('afd9c17f8913577ec3509520bd6e5d63e9c0fd2a5f70c787993b097ba6ca9fae', tx.txid())
|
||||
raw_tx = '010000000370ac0a1ae588aaf284c308d67ca92c69a39e2db81337e563bf40c59da0a5cf63000000006a4730440220360d20baff382059040ba9be98947fd678fb08aab2bb0c172efa996fd8ece9b702201b4fb0de67f015c90e7ac8a193aeab486a1f587e0f54d0fb9552ef7f5ce6caec032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff7d815b6447e35fbea097e00e028fb7dfbad4f3f0987b4734676c84f3fcd0e804010000006b483045022100c714310be1e3a9ff1c5f7cacc65c2d8e781fc3a88ceb063c6153bf950650802102200b2d0979c76e12bb480da635f192cc8dc6f905380dd4ac1ff35a4f68f462fffd032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff3f1f097333e4d46d51f5e77b53264db8f7f5d2e18217e1099957d0f5af7713ee010000006c493046022100b663499ef73273a3788dea342717c2640ac43c5a1cf862c9e09b206fcb3f6bb8022100b09972e75972d9148f2bdd462e5cb69b57c1214b88fc55ca638676c07cfc10d8032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff0380841e00000000001976a914bfb282c70c4191f45b5a6665cad1682f2c9cfdfb88ac80841e00000000001976a9149857cc07bed33a5cf12b9c5e0500b675d500c81188ace0fd1c00000000001976a91443c52850606c872403c0601e69fa34b26f62db4a88ac00000000'
|
||||
txid = 'afd9c17f8913577ec3509520bd6e5d63e9c0fd2a5f70c787993b097ba6ca9fae'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0019(self):
|
||||
tx = transaction.Transaction('01000000012312503f2491a2a97fcd775f11e108a540a5528b5d4dee7a3c68ae4add01dab300000000fdfe0000483045022100f6649b0eddfdfd4ad55426663385090d51ee86c3481bdc6b0c18ea6c0ece2c0b0220561c315b07cffa6f7dd9df96dbae9200c2dee09bf93cc35ca05e6cdf613340aa0148304502207aacee820e08b0b174e248abd8d7a34ed63b5da3abedb99934df9fddd65c05c4022100dfe87896ab5ee3df476c2655f9fbe5bd089dccbef3e4ea05b5d121169fe7f5f4014c695221031d11db38972b712a9fe1fc023577c7ae3ddb4a3004187d41c45121eecfdbb5b7210207ec36911b6ad2382860d32989c7b8728e9489d7bbc94a6b5509ef0029be128821024ea9fac06f666a4adc3fc1357b7bec1fd0bdece2b9d08579226a8ebde53058e453aeffffffff0180380100000000001976a914c9b99cddf847d10685a4fabaa0baf505f7c3dfab88ac00000000')
|
||||
self.assertEqual('f4b05f978689c89000f729cae187dcfbe64c9819af67a4f05c0b4d59e717d64d', tx.txid())
|
||||
raw_tx = '01000000012312503f2491a2a97fcd775f11e108a540a5528b5d4dee7a3c68ae4add01dab300000000fdfe0000483045022100f6649b0eddfdfd4ad55426663385090d51ee86c3481bdc6b0c18ea6c0ece2c0b0220561c315b07cffa6f7dd9df96dbae9200c2dee09bf93cc35ca05e6cdf613340aa0148304502207aacee820e08b0b174e248abd8d7a34ed63b5da3abedb99934df9fddd65c05c4022100dfe87896ab5ee3df476c2655f9fbe5bd089dccbef3e4ea05b5d121169fe7f5f4014c695221031d11db38972b712a9fe1fc023577c7ae3ddb4a3004187d41c45121eecfdbb5b7210207ec36911b6ad2382860d32989c7b8728e9489d7bbc94a6b5509ef0029be128821024ea9fac06f666a4adc3fc1357b7bec1fd0bdece2b9d08579226a8ebde53058e453aeffffffff0180380100000000001976a914c9b99cddf847d10685a4fabaa0baf505f7c3dfab88ac00000000'
|
||||
txid = 'f4b05f978689c89000f729cae187dcfbe64c9819af67a4f05c0b4d59e717d64d'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0020(self):
|
||||
tx = transaction.Transaction('0100000001f709fa82596e4f908ee331cb5e0ed46ab331d7dcfaf697fe95891e73dac4ebcb000000008c20ca42095840735e89283fec298e62ac2ddea9b5f34a8cbb7097ad965b87568100201b1b01dc829177da4a14551d2fc96a9db00c6501edfa12f22cd9cefd335c227f483045022100a9df60536df5733dd0de6bc921fab0b3eee6426501b43a228afa2c90072eb5ca02201c78b74266fac7d1db5deff080d8a403743203f109fbcabf6d5a760bf87386d20100ffffffff01c075790000000000232103611f9a45c18f28f06f19076ad571c344c82ce8fcfe34464cf8085217a2d294a6ac00000000')
|
||||
self.assertEqual('cc60b1f899ec0a69b7c3f25ddf32c4524096a9c5b01cbd84c6d0312a0c478984', tx.txid())
|
||||
raw_tx = '0100000001f709fa82596e4f908ee331cb5e0ed46ab331d7dcfaf697fe95891e73dac4ebcb000000008c20ca42095840735e89283fec298e62ac2ddea9b5f34a8cbb7097ad965b87568100201b1b01dc829177da4a14551d2fc96a9db00c6501edfa12f22cd9cefd335c227f483045022100a9df60536df5733dd0de6bc921fab0b3eee6426501b43a228afa2c90072eb5ca02201c78b74266fac7d1db5deff080d8a403743203f109fbcabf6d5a760bf87386d20100ffffffff01c075790000000000232103611f9a45c18f28f06f19076ad571c344c82ce8fcfe34464cf8085217a2d294a6ac00000000'
|
||||
txid = 'cc60b1f899ec0a69b7c3f25ddf32c4524096a9c5b01cbd84c6d0312a0c478984'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0021(self):
|
||||
tx = transaction.Transaction('01000000012c651178faca83be0b81c8c1375c4b0ad38d53c8fe1b1c4255f5e795c25792220000000049483045022100d6044562284ac76c985018fc4a90127847708c9edb280996c507b28babdc4b2a02203d74eca3f1a4d1eea7ff77b528fde6d5dc324ec2dbfdb964ba885f643b9704cd01ffffffff010100000000000000232102c2410f8891ae918cab4ffc4bb4a3b0881be67c7a1e7faa8b5acf9ab8932ec30cac00000000')
|
||||
self.assertEqual('1edc7f214659d52c731e2016d258701911bd62a0422f72f6c87a1bc8dd3f8667', tx.txid())
|
||||
raw_tx = '01000000012c651178faca83be0b81c8c1375c4b0ad38d53c8fe1b1c4255f5e795c25792220000000049483045022100d6044562284ac76c985018fc4a90127847708c9edb280996c507b28babdc4b2a02203d74eca3f1a4d1eea7ff77b528fde6d5dc324ec2dbfdb964ba885f643b9704cd01ffffffff010100000000000000232102c2410f8891ae918cab4ffc4bb4a3b0881be67c7a1e7faa8b5acf9ab8932ec30cac00000000'
|
||||
txid = '1edc7f214659d52c731e2016d258701911bd62a0422f72f6c87a1bc8dd3f8667'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0022(self):
|
||||
tx = transaction.Transaction('0100000001f725ea148d92096a79b1709611e06e94c63c4ef61cbae2d9b906388efd3ca99c000000000100ffffffff0101000000000000002321028a1d66975dbdf97897e3a4aef450ebeb5b5293e4a0b4a6d3a2daaa0b2b110e02ac00000000')
|
||||
self.assertEqual('018adb7133fde63add9149a2161802a1bcf4bdf12c39334e880c073480eda2ff', tx.txid())
|
||||
raw_tx = '0100000001f725ea148d92096a79b1709611e06e94c63c4ef61cbae2d9b906388efd3ca99c000000000100ffffffff0101000000000000002321028a1d66975dbdf97897e3a4aef450ebeb5b5293e4a0b4a6d3a2daaa0b2b110e02ac00000000'
|
||||
txid = '018adb7133fde63add9149a2161802a1bcf4bdf12c39334e880c073480eda2ff'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0023(self):
|
||||
tx = transaction.Transaction('0100000001be599efaa4148474053c2fa031c7262398913f1dc1d9ec201fd44078ed004e44000000004900473044022022b29706cb2ed9ef0cb3c97b72677ca2dfd7b4160f7b4beb3ba806aa856c401502202d1e52582412eba2ed474f1f437a427640306fd3838725fab173ade7fe4eae4a01ffffffff010100000000000000232103ac4bba7e7ca3e873eea49e08132ad30c7f03640b6539e9b59903cf14fd016bbbac00000000')
|
||||
self.assertEqual('1464caf48c708a6cc19a296944ded9bb7f719c9858986d2501cf35068b9ce5a2', tx.txid())
|
||||
raw_tx = '0100000001be599efaa4148474053c2fa031c7262398913f1dc1d9ec201fd44078ed004e44000000004900473044022022b29706cb2ed9ef0cb3c97b72677ca2dfd7b4160f7b4beb3ba806aa856c401502202d1e52582412eba2ed474f1f437a427640306fd3838725fab173ade7fe4eae4a01ffffffff010100000000000000232103ac4bba7e7ca3e873eea49e08132ad30c7f03640b6539e9b59903cf14fd016bbbac00000000'
|
||||
txid = '1464caf48c708a6cc19a296944ded9bb7f719c9858986d2501cf35068b9ce5a2'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0024(self):
|
||||
tx = transaction.Transaction('010000000112b66d5e8c7d224059e946749508efea9d66bf8d0c83630f080cf30be8bb6ae100000000490047304402206ffe3f14caf38ad5c1544428e99da76ffa5455675ec8d9780fac215ca17953520220779502985e194d84baa36b9bd40a0dbd981163fa191eb884ae83fc5bd1c86b1101ffffffff010100000000000000232103905380c7013e36e6e19d305311c1b81fce6581f5ee1c86ef0627c68c9362fc9fac00000000')
|
||||
self.assertEqual('1fb73fbfc947d52f5d80ba23b67c06a232ad83fdd49d1c0a657602f03fbe8f7a', tx.txid())
|
||||
raw_tx = '010000000112b66d5e8c7d224059e946749508efea9d66bf8d0c83630f080cf30be8bb6ae100000000490047304402206ffe3f14caf38ad5c1544428e99da76ffa5455675ec8d9780fac215ca17953520220779502985e194d84baa36b9bd40a0dbd981163fa191eb884ae83fc5bd1c86b1101ffffffff010100000000000000232103905380c7013e36e6e19d305311c1b81fce6581f5ee1c86ef0627c68c9362fc9fac00000000'
|
||||
txid = '1fb73fbfc947d52f5d80ba23b67c06a232ad83fdd49d1c0a657602f03fbe8f7a'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0025(self):
|
||||
tx = transaction.Transaction('0100000001b0ef70cc644e0d37407e387e73bfad598d852a5aa6d691d72b2913cebff4bceb000000004a00473044022068cd4851fc7f9a892ab910df7a24e616f293bcb5c5fbdfbc304a194b26b60fba022078e6da13d8cb881a22939b952c24f88b97afd06b4c47a47d7f804c9a352a6d6d0100ffffffff0101000000000000002321033bcaa0a602f0d44cc9d5637c6e515b0471db514c020883830b7cefd73af04194ac00000000')
|
||||
self.assertEqual('24cecfce0fa880b09c9b4a66c5134499d1b09c01cc5728cd182638bea070e6ab', tx.txid())
|
||||
raw_tx = '0100000001b0ef70cc644e0d37407e387e73bfad598d852a5aa6d691d72b2913cebff4bceb000000004a00473044022068cd4851fc7f9a892ab910df7a24e616f293bcb5c5fbdfbc304a194b26b60fba022078e6da13d8cb881a22939b952c24f88b97afd06b4c47a47d7f804c9a352a6d6d0100ffffffff0101000000000000002321033bcaa0a602f0d44cc9d5637c6e515b0471db514c020883830b7cefd73af04194ac00000000'
|
||||
txid = '24cecfce0fa880b09c9b4a66c5134499d1b09c01cc5728cd182638bea070e6ab'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0026(self):
|
||||
tx = transaction.Transaction('0100000001c188aa82f268fcf08ba18950f263654a3ea6931dabc8bf3ed1d4d42aaed74cba000000004b0000483045022100940378576e069aca261a6b26fb38344e4497ca6751bb10905c76bb689f4222b002204833806b014c26fd801727b792b1260003c55710f87c5adbd7a9cb57446dbc9801ffffffff0101000000000000002321037c615d761e71d38903609bf4f46847266edc2fb37532047d747ba47eaae5ffe1ac00000000')
|
||||
self.assertEqual('9eaa819e386d6a54256c9283da50c230f3d8cd5376d75c4dcc945afdeb157dd7', tx.txid())
|
||||
raw_tx = '0100000001c188aa82f268fcf08ba18950f263654a3ea6931dabc8bf3ed1d4d42aaed74cba000000004b0000483045022100940378576e069aca261a6b26fb38344e4497ca6751bb10905c76bb689f4222b002204833806b014c26fd801727b792b1260003c55710f87c5adbd7a9cb57446dbc9801ffffffff0101000000000000002321037c615d761e71d38903609bf4f46847266edc2fb37532047d747ba47eaae5ffe1ac00000000'
|
||||
txid = '9eaa819e386d6a54256c9283da50c230f3d8cd5376d75c4dcc945afdeb157dd7'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0027(self):
|
||||
tx = transaction.Transaction('01000000012432b60dc72cebc1a27ce0969c0989c895bdd9e62e8234839117f8fc32d17fbc000000004a493046022100a576b52051962c25e642c0fd3d77ee6c92487048e5d90818bcf5b51abaccd7900221008204f8fb121be4ec3b24483b1f92d89b1b0548513a134e345c5442e86e8617a501ffffffff010000000000000000016a00000000')
|
||||
self.assertEqual('46224764c7870f95b58f155bce1e38d4da8e99d42dbb632d0dd7c07e092ee5aa', tx.txid())
|
||||
raw_tx = '01000000012432b60dc72cebc1a27ce0969c0989c895bdd9e62e8234839117f8fc32d17fbc000000004a493046022100a576b52051962c25e642c0fd3d77ee6c92487048e5d90818bcf5b51abaccd7900221008204f8fb121be4ec3b24483b1f92d89b1b0548513a134e345c5442e86e8617a501ffffffff010000000000000000016a00000000'
|
||||
txid = '46224764c7870f95b58f155bce1e38d4da8e99d42dbb632d0dd7c07e092ee5aa'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0028(self):
|
||||
tx = transaction.Transaction('01000000014710b0e7cf9f8930de259bdc4b84aa5dfb9437b665a3e3a21ff26e0bf994e183000000004a493046022100a166121a61b4eeb19d8f922b978ff6ab58ead8a5a5552bf9be73dc9c156873ea02210092ad9bc43ee647da4f6652c320800debcf08ec20a094a0aaf085f63ecb37a17201ffffffff010000000000000000016a00000000')
|
||||
self.assertEqual('8d66836045db9f2d7b3a75212c5e6325f70603ee27c8333a3bce5bf670d9582e', tx.txid())
|
||||
raw_tx = '01000000014710b0e7cf9f8930de259bdc4b84aa5dfb9437b665a3e3a21ff26e0bf994e183000000004a493046022100a166121a61b4eeb19d8f922b978ff6ab58ead8a5a5552bf9be73dc9c156873ea02210092ad9bc43ee647da4f6652c320800debcf08ec20a094a0aaf085f63ecb37a17201ffffffff010000000000000000016a00000000'
|
||||
txid = '8d66836045db9f2d7b3a75212c5e6325f70603ee27c8333a3bce5bf670d9582e'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0029(self):
|
||||
tx = transaction.Transaction('01000000015ebaa001d8e4ec7a88703a3bcf69d98c874bca6299cca0f191512bf2a7826832000000004948304502203bf754d1c6732fbf87c5dcd81258aefd30f2060d7bd8ac4a5696f7927091dad1022100f5bcb726c4cf5ed0ed34cc13dadeedf628ae1045b7cb34421bc60b89f4cecae701ffffffff010000000000000000016a00000000')
|
||||
self.assertEqual('aab7ef280abbb9cc6fbaf524d2645c3daf4fcca2b3f53370e618d9cedf65f1f8', tx.txid())
|
||||
raw_tx = '01000000015ebaa001d8e4ec7a88703a3bcf69d98c874bca6299cca0f191512bf2a7826832000000004948304502203bf754d1c6732fbf87c5dcd81258aefd30f2060d7bd8ac4a5696f7927091dad1022100f5bcb726c4cf5ed0ed34cc13dadeedf628ae1045b7cb34421bc60b89f4cecae701ffffffff010000000000000000016a00000000'
|
||||
txid = 'aab7ef280abbb9cc6fbaf524d2645c3daf4fcca2b3f53370e618d9cedf65f1f8'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0030(self):
|
||||
tx = transaction.Transaction('010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a900000000924830450221009c0a27f886a1d8cb87f6f595fbc3163d28f7a81ec3c4b252ee7f3ac77fd13ffa02203caa8dfa09713c8c4d7ef575c75ed97812072405d932bd11e6a1593a98b679370148304502201e3861ef39a526406bad1e20ecad06be7375ad40ddb582c9be42d26c3a0d7b240221009d0a3985e96522e59635d19cc4448547477396ce0ef17a58e7d74c3ef464292301ffffffff010000000000000000016a00000000')
|
||||
self.assertEqual('6327783a064d4e350c454ad5cd90201aedf65b1fc524e73709c52f0163739190', tx.txid())
|
||||
raw_tx = '010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a900000000924830450221009c0a27f886a1d8cb87f6f595fbc3163d28f7a81ec3c4b252ee7f3ac77fd13ffa02203caa8dfa09713c8c4d7ef575c75ed97812072405d932bd11e6a1593a98b679370148304502201e3861ef39a526406bad1e20ecad06be7375ad40ddb582c9be42d26c3a0d7b240221009d0a3985e96522e59635d19cc4448547477396ce0ef17a58e7d74c3ef464292301ffffffff010000000000000000016a00000000'
|
||||
txid = '6327783a064d4e350c454ad5cd90201aedf65b1fc524e73709c52f0163739190'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0031(self):
|
||||
tx = transaction.Transaction('010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a9000000004a48304502207a6974a77c591fa13dff60cabbb85a0de9e025c09c65a4b2285e47ce8e22f761022100f0efaac9ff8ac36b10721e0aae1fb975c90500b50c56e8a0cc52b0403f0425dd0100ffffffff010000000000000000016a00000000')
|
||||
self.assertEqual('892464645599cc3c2d165adcc612e5f982a200dfaa3e11e9ce1d228027f46880', tx.txid())
|
||||
raw_tx = '010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a9000000004a48304502207a6974a77c591fa13dff60cabbb85a0de9e025c09c65a4b2285e47ce8e22f761022100f0efaac9ff8ac36b10721e0aae1fb975c90500b50c56e8a0cc52b0403f0425dd0100ffffffff010000000000000000016a00000000'
|
||||
txid = '892464645599cc3c2d165adcc612e5f982a200dfaa3e11e9ce1d228027f46880'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0032(self):
|
||||
tx = transaction.Transaction('010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a9000000004a483045022100fa4a74ba9fd59c59f46c3960cf90cbe0d2b743c471d24a3d5d6db6002af5eebb02204d70ec490fd0f7055a7c45f86514336e3a7f03503dacecabb247fc23f15c83510151ffffffff010000000000000000016a00000000')
|
||||
self.assertEqual('578db8c6c404fec22c4a8afeaf32df0e7b767c4dda3478e0471575846419e8fc', tx.txid())
|
||||
raw_tx = '010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a9000000004a483045022100fa4a74ba9fd59c59f46c3960cf90cbe0d2b743c471d24a3d5d6db6002af5eebb02204d70ec490fd0f7055a7c45f86514336e3a7f03503dacecabb247fc23f15c83510151ffffffff010000000000000000016a00000000'
|
||||
txid = '578db8c6c404fec22c4a8afeaf32df0e7b767c4dda3478e0471575846419e8fc'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0033(self):
|
||||
tx = transaction.Transaction('0100000001e0be9e32f1f89c3d916c4f21e55cdcd096741b895cc76ac353e6023a05f4f7cc00000000d86149304602210086e5f736a2c3622ebb62bd9d93d8e5d76508b98be922b97160edc3dcca6d8c47022100b23c312ac232a4473f19d2aeb95ab7bdf2b65518911a0d72d50e38b5dd31dc820121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ac4730440220508fa761865c8abd81244a168392876ee1d94e8ed83897066b5e2df2400dad24022043f5ee7538e87e9c6aef7ef55133d3e51da7cc522830a9c4d736977a76ef755c0121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000')
|
||||
self.assertEqual('974f5148a0946f9985e75a240bb24c573adbbdc25d61e7b016cdbb0a5355049f', tx.txid())
|
||||
raw_tx = '0100000001e0be9e32f1f89c3d916c4f21e55cdcd096741b895cc76ac353e6023a05f4f7cc00000000d86149304602210086e5f736a2c3622ebb62bd9d93d8e5d76508b98be922b97160edc3dcca6d8c47022100b23c312ac232a4473f19d2aeb95ab7bdf2b65518911a0d72d50e38b5dd31dc820121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ac4730440220508fa761865c8abd81244a168392876ee1d94e8ed83897066b5e2df2400dad24022043f5ee7538e87e9c6aef7ef55133d3e51da7cc522830a9c4d736977a76ef755c0121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000'
|
||||
txid = '974f5148a0946f9985e75a240bb24c573adbbdc25d61e7b016cdbb0a5355049f'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0034(self):
|
||||
tx = transaction.Transaction('01000000013c6f30f99a5161e75a2ce4bca488300ca0c6112bde67f0807fe983feeff0c91001000000e608646561646265656675ab61493046022100ce18d384221a731c993939015e3d1bcebafb16e8c0b5b5d14097ec8177ae6f28022100bcab227af90bab33c3fe0a9abfee03ba976ee25dc6ce542526e9b2e56e14b7f10121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ac493046022100c3b93edcc0fd6250eb32f2dd8a0bba1754b0f6c3be8ed4100ed582f3db73eba2022100bf75b5bd2eff4d6bf2bda2e34a40fcc07d4aa3cf862ceaa77b47b81eff829f9a01ab21038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000')
|
||||
self.assertEqual('b0097ec81df231893a212657bf5fe5a13b2bff8b28c0042aca6fc4159f79661b', tx.txid())
|
||||
raw_tx = '01000000013c6f30f99a5161e75a2ce4bca488300ca0c6112bde67f0807fe983feeff0c91001000000e608646561646265656675ab61493046022100ce18d384221a731c993939015e3d1bcebafb16e8c0b5b5d14097ec8177ae6f28022100bcab227af90bab33c3fe0a9abfee03ba976ee25dc6ce542526e9b2e56e14b7f10121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ac493046022100c3b93edcc0fd6250eb32f2dd8a0bba1754b0f6c3be8ed4100ed582f3db73eba2022100bf75b5bd2eff4d6bf2bda2e34a40fcc07d4aa3cf862ceaa77b47b81eff829f9a01ab21038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000'
|
||||
txid = 'b0097ec81df231893a212657bf5fe5a13b2bff8b28c0042aca6fc4159f79661b'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0035(self):
|
||||
tx = transaction.Transaction('01000000016f3dbe2ca96fa217e94b1017860be49f20820dea5c91bdcb103b0049d5eb566000000000fd1d0147304402203989ac8f9ad36b5d0919d97fa0a7f70c5272abee3b14477dc646288a8b976df5022027d19da84a066af9053ad3d1d7459d171b7e3a80bc6c4ef7a330677a6be548140147304402203989ac8f9ad36b5d0919d97fa0a7f70c5272abee3b14477dc646288a8b976df5022027d19da84a066af9053ad3d1d7459d171b7e3a80bc6c4ef7a330677a6be548140121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ac47304402203757e937ba807e4a5da8534c17f9d121176056406a6465054bdd260457515c1a02200f02eccf1bec0f3a0d65df37889143c2e88ab7acec61a7b6f5aa264139141a2b0121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000')
|
||||
self.assertEqual('feeba255656c80c14db595736c1c7955c8c0a497622ec96e3f2238fbdd43a7c9', tx.txid())
|
||||
raw_tx = '01000000016f3dbe2ca96fa217e94b1017860be49f20820dea5c91bdcb103b0049d5eb566000000000fd1d0147304402203989ac8f9ad36b5d0919d97fa0a7f70c5272abee3b14477dc646288a8b976df5022027d19da84a066af9053ad3d1d7459d171b7e3a80bc6c4ef7a330677a6be548140147304402203989ac8f9ad36b5d0919d97fa0a7f70c5272abee3b14477dc646288a8b976df5022027d19da84a066af9053ad3d1d7459d171b7e3a80bc6c4ef7a330677a6be548140121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ac47304402203757e937ba807e4a5da8534c17f9d121176056406a6465054bdd260457515c1a02200f02eccf1bec0f3a0d65df37889143c2e88ab7acec61a7b6f5aa264139141a2b0121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000'
|
||||
txid = 'feeba255656c80c14db595736c1c7955c8c0a497622ec96e3f2238fbdd43a7c9'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0036(self):
|
||||
tx = transaction.Transaction('01000000012139c555ccb81ee5b1e87477840991ef7b386bc3ab946b6b682a04a621006b5a01000000fdb40148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f2204148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390175ac4830450220646b72c35beeec51f4d5bc1cbae01863825750d7f490864af354e6ea4f625e9c022100f04b98432df3a9641719dbced53393022e7249fb59db993af1118539830aab870148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a580039017521038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000')
|
||||
self.assertEqual('a0c984fc820e57ddba97f8098fa640c8a7eb3fe2f583923da886b7660f505e1e', tx.txid())
|
||||
raw_tx = '01000000012139c555ccb81ee5b1e87477840991ef7b386bc3ab946b6b682a04a621006b5a01000000fdb40148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f2204148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390175ac4830450220646b72c35beeec51f4d5bc1cbae01863825750d7f490864af354e6ea4f625e9c022100f04b98432df3a9641719dbced53393022e7249fb59db993af1118539830aab870148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a580039017521038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000'
|
||||
txid = 'a0c984fc820e57ddba97f8098fa640c8a7eb3fe2f583923da886b7660f505e1e'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0037(self):
|
||||
tx = transaction.Transaction('0100000002f9cbafc519425637ba4227f8d0a0b7160b4e65168193d5af39747891de98b5b5000000006b4830450221008dd619c563e527c47d9bd53534a770b102e40faa87f61433580e04e271ef2f960220029886434e18122b53d5decd25f1f4acb2480659fea20aabd856987ba3c3907e0121022b78b756e2258af13779c1a1f37ea6800259716ca4b7f0b87610e0bf3ab52a01ffffffff42e7988254800876b69f24676b3e0205b77be476512ca4d970707dd5c60598ab00000000fd260100483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a53034930460221008431bdfa72bc67f9d41fe72e94c88fb8f359ffa30b33c72c121c5a877d922e1002210089ef5fc22dd8bfc6bf9ffdb01a9862d27687d424d1fefbab9e9c7176844a187a014c9052483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303210378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71210378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c7153aeffffffff01a08601000000000017a914d8dacdadb7462ae15cd906f1878706d0da8660e68700000000')
|
||||
self.assertEqual('5df1375ffe61ac35ca178ebb0cab9ea26dedbd0e96005dfcee7e379fa513232f', tx.txid())
|
||||
raw_tx = '0100000002f9cbafc519425637ba4227f8d0a0b7160b4e65168193d5af39747891de98b5b5000000006b4830450221008dd619c563e527c47d9bd53534a770b102e40faa87f61433580e04e271ef2f960220029886434e18122b53d5decd25f1f4acb2480659fea20aabd856987ba3c3907e0121022b78b756e2258af13779c1a1f37ea6800259716ca4b7f0b87610e0bf3ab52a01ffffffff42e7988254800876b69f24676b3e0205b77be476512ca4d970707dd5c60598ab00000000fd260100483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a53034930460221008431bdfa72bc67f9d41fe72e94c88fb8f359ffa30b33c72c121c5a877d922e1002210089ef5fc22dd8bfc6bf9ffdb01a9862d27687d424d1fefbab9e9c7176844a187a014c9052483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303210378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71210378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c7153aeffffffff01a08601000000000017a914d8dacdadb7462ae15cd906f1878706d0da8660e68700000000'
|
||||
txid = '5df1375ffe61ac35ca178ebb0cab9ea26dedbd0e96005dfcee7e379fa513232f'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0038(self):
|
||||
tx = transaction.Transaction('0100000002dbb33bdf185b17f758af243c5d3c6e164cc873f6bb9f40c0677d6e0f8ee5afce000000006b4830450221009627444320dc5ef8d7f68f35010b4c050a6ed0d96b67a84db99fda9c9de58b1e02203e4b4aaa019e012e65d69b487fdf8719df72f488fa91506a80c49a33929f1fd50121022b78b756e2258af13779c1a1f37ea6800259716ca4b7f0b87610e0bf3ab52a01ffffffffdbb33bdf185b17f758af243c5d3c6e164cc873f6bb9f40c0677d6e0f8ee5afce010000009300483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303ffffffff01a0860100000000001976a9149bc0bbdd3024da4d0c38ed1aecf5c68dd1d3fa1288ac00000000')
|
||||
self.assertEqual('ded7ff51d89a4e1ec48162aee5a96447214d93dfb3837946af2301a28f65dbea', tx.txid())
|
||||
raw_tx = '0100000002dbb33bdf185b17f758af243c5d3c6e164cc873f6bb9f40c0677d6e0f8ee5afce000000006b4830450221009627444320dc5ef8d7f68f35010b4c050a6ed0d96b67a84db99fda9c9de58b1e02203e4b4aaa019e012e65d69b487fdf8719df72f488fa91506a80c49a33929f1fd50121022b78b756e2258af13779c1a1f37ea6800259716ca4b7f0b87610e0bf3ab52a01ffffffffdbb33bdf185b17f758af243c5d3c6e164cc873f6bb9f40c0677d6e0f8ee5afce010000009300483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303ffffffff01a0860100000000001976a9149bc0bbdd3024da4d0c38ed1aecf5c68dd1d3fa1288ac00000000'
|
||||
txid = 'ded7ff51d89a4e1ec48162aee5a96447214d93dfb3837946af2301a28f65dbea'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0039(self):
|
||||
tx = transaction.Transaction('010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000')
|
||||
self.assertEqual('3444be2e216abe77b46015e481d8cc21abd4c20446aabf49cd78141c9b9db87e', tx.txid())
|
||||
raw_tx = '010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000'
|
||||
txid = '3444be2e216abe77b46015e481d8cc21abd4c20446aabf49cd78141c9b9db87e'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0040(self):
|
||||
tx = transaction.Transaction('0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ff64cd1d')
|
||||
self.assertEqual('abd62b4627d8d9b2d95fcfd8c87e37d2790637ce47d28018e3aece63c1d62649', tx.txid())
|
||||
raw_tx = '0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ff64cd1d'
|
||||
txid = 'abd62b4627d8d9b2d95fcfd8c87e37d2790637ce47d28018e3aece63c1d62649'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0041(self):
|
||||
tx = transaction.Transaction('01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d')
|
||||
self.assertEqual('58b6de8413603b7f556270bf48caedcf17772e7105f5419f6a80be0df0b470da', tx.txid())
|
||||
raw_tx = '01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d'
|
||||
txid = '58b6de8413603b7f556270bf48caedcf17772e7105f5419f6a80be0df0b470da'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0042(self):
|
||||
tx = transaction.Transaction('0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ffffffff')
|
||||
self.assertEqual('5f99c0abf511294d76cbe144d86b77238a03e086974bc7a8ea0bdb2c681a0324', tx.txid())
|
||||
raw_tx = '0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ffffffff'
|
||||
txid = '5f99c0abf511294d76cbe144d86b77238a03e086974bc7a8ea0bdb2c681a0324'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0043(self):
|
||||
tx = transaction.Transaction('010000000100010000000000000000000000000000000000000000000000000000000000000000000000feffffff0100000000000000000000000000')
|
||||
self.assertEqual('25d35877eaba19497710666473c50d5527d38503e3521107a3fc532b74cd7453', tx.txid())
|
||||
raw_tx = '010000000100010000000000000000000000000000000000000000000000000000000000000000000000feffffff0100000000000000000000000000'
|
||||
txid = '25d35877eaba19497710666473c50d5527d38503e3521107a3fc532b74cd7453'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0044(self):
|
||||
tx = transaction.Transaction('0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000feffffff')
|
||||
self.assertEqual('1b9aef851895b93c62c29fbd6ca4d45803f4007eff266e2f96ff11e9b6ef197b', tx.txid())
|
||||
raw_tx = '0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000feffffff'
|
||||
txid = '1b9aef851895b93c62c29fbd6ca4d45803f4007eff266e2f96ff11e9b6ef197b'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0045(self):
|
||||
tx = transaction.Transaction('010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000')
|
||||
self.assertEqual('3444be2e216abe77b46015e481d8cc21abd4c20446aabf49cd78141c9b9db87e', tx.txid())
|
||||
raw_tx = '010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000'
|
||||
txid = '3444be2e216abe77b46015e481d8cc21abd4c20446aabf49cd78141c9b9db87e'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0046(self):
|
||||
tx = transaction.Transaction('01000000010001000000000000000000000000000000000000000000000000000000000000000000000251b1000000000100000000000000000001000000')
|
||||
self.assertEqual('f53761038a728b1f17272539380d96e93f999218f8dcb04a8469b523445cd0fd', tx.txid())
|
||||
raw_tx = '01000000010001000000000000000000000000000000000000000000000000000000000000000000000251b1000000000100000000000000000001000000'
|
||||
txid = 'f53761038a728b1f17272539380d96e93f999218f8dcb04a8469b523445cd0fd'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0047(self):
|
||||
tx = transaction.Transaction('0100000001000100000000000000000000000000000000000000000000000000000000000000000000030251b1000000000100000000000000000001000000')
|
||||
self.assertEqual('d193f0f32fceaf07bb25c897c8f99ca6f69a52f6274ca64efc2a2e180cb97fc1', tx.txid())
|
||||
raw_tx = '0100000001000100000000000000000000000000000000000000000000000000000000000000000000030251b1000000000100000000000000000001000000'
|
||||
txid = 'd193f0f32fceaf07bb25c897c8f99ca6f69a52f6274ca64efc2a2e180cb97fc1'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0048(self):
|
||||
tx = transaction.Transaction('010000000132211bdd0d568506804eef0d8cc3db68c3d766ab9306cdfcc0a9c89616c8dbb1000000006c493045022100c7bb0faea0522e74ff220c20c022d2cb6033f8d167fb89e75a50e237a35fd6d202203064713491b1f8ad5f79e623d0219ad32510bfaa1009ab30cbee77b59317d6e30001210237af13eb2d84e4545af287b919c2282019c9691cc509e78e196a9d8274ed1be0ffffffff0100000000000000001976a914f1b3ed2eda9a2ebe5a9374f692877cdf87c0f95b88ac00000000')
|
||||
self.assertEqual('50a1e0e6a134a564efa078e3bd088e7e8777c2c0aec10a752fd8706470103b89', tx.txid())
|
||||
raw_tx = '010000000132211bdd0d568506804eef0d8cc3db68c3d766ab9306cdfcc0a9c89616c8dbb1000000006c493045022100c7bb0faea0522e74ff220c20c022d2cb6033f8d167fb89e75a50e237a35fd6d202203064713491b1f8ad5f79e623d0219ad32510bfaa1009ab30cbee77b59317d6e30001210237af13eb2d84e4545af287b919c2282019c9691cc509e78e196a9d8274ed1be0ffffffff0100000000000000001976a914f1b3ed2eda9a2ebe5a9374f692877cdf87c0f95b88ac00000000'
|
||||
txid = '50a1e0e6a134a564efa078e3bd088e7e8777c2c0aec10a752fd8706470103b89'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0049(self):
|
||||
tx = transaction.Transaction('020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000')
|
||||
self.assertEqual('e2207d1aaf6b74e5d98c2fa326d2dc803b56b30a3f90ce779fa5edb762f38755', tx.txid())
|
||||
raw_tx = '020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000'
|
||||
txid = 'e2207d1aaf6b74e5d98c2fa326d2dc803b56b30a3f90ce779fa5edb762f38755'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0050(self):
|
||||
tx = transaction.Transaction('020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffff00000100000000000000000000000000')
|
||||
self.assertEqual('f335864f7c12ec7946d2c123deb91eb978574b647af125a414262380c7fbd55c', tx.txid())
|
||||
raw_tx = '020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffff00000100000000000000000000000000'
|
||||
txid = 'f335864f7c12ec7946d2c123deb91eb978574b647af125a414262380c7fbd55c'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0051(self):
|
||||
tx = transaction.Transaction('020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffbf7f0100000000000000000000000000')
|
||||
self.assertEqual('d1edbcde44691e98a7b7f556bd04966091302e29ad9af3c2baac38233667e0d2', tx.txid())
|
||||
raw_tx = '020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffbf7f0100000000000000000000000000'
|
||||
txid = 'd1edbcde44691e98a7b7f556bd04966091302e29ad9af3c2baac38233667e0d2'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0052(self):
|
||||
tx = transaction.Transaction('020000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000')
|
||||
self.assertEqual('3a13e1b6371c545147173cc4055f0ed73686a9f73f092352fb4b39ca27d360e6', tx.txid())
|
||||
raw_tx = '020000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000'
|
||||
txid = '3a13e1b6371c545147173cc4055f0ed73686a9f73f092352fb4b39ca27d360e6'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0053(self):
|
||||
tx = transaction.Transaction('020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffff40000100000000000000000000000000')
|
||||
self.assertEqual('bffda23e40766d292b0510a1b556453c558980c70c94ab158d8286b3413e220d', tx.txid())
|
||||
raw_tx = '020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffff40000100000000000000000000000000'
|
||||
txid = 'bffda23e40766d292b0510a1b556453c558980c70c94ab158d8286b3413e220d'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0054(self):
|
||||
tx = transaction.Transaction('020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffff7f0100000000000000000000000000')
|
||||
self.assertEqual('01a86c65460325dc6699714d26df512a62a854a669f6ed2e6f369a238e048cfd', tx.txid())
|
||||
raw_tx = '020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffff7f0100000000000000000000000000'
|
||||
txid = '01a86c65460325dc6699714d26df512a62a854a669f6ed2e6f369a238e048cfd'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0055(self):
|
||||
tx = transaction.Transaction('020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000800100000000000000000000000000')
|
||||
self.assertEqual('f6d2359c5de2d904e10517d23e7c8210cca71076071bbf46de9fbd5f6233dbf1', tx.txid())
|
||||
raw_tx = '020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000800100000000000000000000000000'
|
||||
txid = 'f6d2359c5de2d904e10517d23e7c8210cca71076071bbf46de9fbd5f6233dbf1'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0056(self):
|
||||
tx = transaction.Transaction('020000000100010000000000000000000000000000000000000000000000000000000000000000000000feffffff0100000000000000000000000000')
|
||||
self.assertEqual('19c2b7377229dae7aa3e50142a32fd37cef7171a01682f536e9ffa80c186f6c9', tx.txid())
|
||||
raw_tx = '020000000100010000000000000000000000000000000000000000000000000000000000000000000000feffffff0100000000000000000000000000'
|
||||
txid = '19c2b7377229dae7aa3e50142a32fd37cef7171a01682f536e9ffa80c186f6c9'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0057(self):
|
||||
tx = transaction.Transaction('020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff0100000000000000000000000000')
|
||||
self.assertEqual('c9dda3a24cc8a5acb153d1085ecd2fecf6f87083122f8cdecc515b1148d4c40d', tx.txid())
|
||||
raw_tx = '020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff0100000000000000000000000000'
|
||||
txid = 'c9dda3a24cc8a5acb153d1085ecd2fecf6f87083122f8cdecc515b1148d4c40d'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0058(self):
|
||||
tx = transaction.Transaction('020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffbf7f0100000000000000000000000000')
|
||||
self.assertEqual('d1edbcde44691e98a7b7f556bd04966091302e29ad9af3c2baac38233667e0d2', tx.txid())
|
||||
raw_tx = '020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffbf7f0100000000000000000000000000'
|
||||
txid = 'd1edbcde44691e98a7b7f556bd04966091302e29ad9af3c2baac38233667e0d2'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0059(self):
|
||||
tx = transaction.Transaction('020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffff7f0100000000000000000000000000')
|
||||
self.assertEqual('01a86c65460325dc6699714d26df512a62a854a669f6ed2e6f369a238e048cfd', tx.txid())
|
||||
raw_tx = '020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffff7f0100000000000000000000000000'
|
||||
txid = '01a86c65460325dc6699714d26df512a62a854a669f6ed2e6f369a238e048cfd'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0060(self):
|
||||
tx = transaction.Transaction('02000000010001000000000000000000000000000000000000000000000000000000000000000000000251b2010000000100000000000000000000000000')
|
||||
self.assertEqual('4b5e0aae1251a9dc66b4d5f483f1879bf518ea5e1765abc5a9f2084b43ed1ea7', tx.txid())
|
||||
raw_tx = '02000000010001000000000000000000000000000000000000000000000000000000000000000000000251b2010000000100000000000000000000000000'
|
||||
txid = '4b5e0aae1251a9dc66b4d5f483f1879bf518ea5e1765abc5a9f2084b43ed1ea7'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0061(self):
|
||||
tx = transaction.Transaction('0200000001000100000000000000000000000000000000000000000000000000000000000000000000030251b2010000000100000000000000000000000000')
|
||||
self.assertEqual('5f16eb3ca4581e2dfb46a28140a4ee15f85e4e1c032947da8b93549b53c105f5', tx.txid())
|
||||
raw_tx = '0200000001000100000000000000000000000000000000000000000000000000000000000000000000030251b2010000000100000000000000000000000000'
|
||||
txid = '5f16eb3ca4581e2dfb46a28140a4ee15f85e4e1c032947da8b93549b53c105f5'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0062(self):
|
||||
tx = transaction.Transaction('0100000000010100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e8030000000000001976a9144c9c3dfac4207d5d8cb89df5722cb3d712385e3f88ac02483045022100cfb07164b36ba64c1b1e8c7720a56ad64d96f6ef332d3d37f9cb3c96477dc44502200a464cd7a9cf94cd70f66ce4f4f0625ef650052c7afcfe29d7d7e01830ff91ed012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc7100000000')
|
||||
self.assertEqual('b2ce556154e5ab22bec0a2f990b2b843f4f4085486c0d2cd82873685c0012004', tx.txid())
|
||||
raw_tx = '0100000000010100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e8030000000000001976a9144c9c3dfac4207d5d8cb89df5722cb3d712385e3f88ac02483045022100cfb07164b36ba64c1b1e8c7720a56ad64d96f6ef332d3d37f9cb3c96477dc44502200a464cd7a9cf94cd70f66ce4f4f0625ef650052c7afcfe29d7d7e01830ff91ed012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc7100000000'
|
||||
txid = 'b2ce556154e5ab22bec0a2f990b2b843f4f4085486c0d2cd82873685c0012004'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0063(self):
|
||||
tx = transaction.Transaction('0100000000010100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e8030000000000001976a9144c9c3dfac4207d5d8cb89df5722cb3d712385e3f88ac02483045022100aa5d8aa40a90f23ce2c3d11bc845ca4a12acd99cbea37de6b9f6d86edebba8cb022022dedc2aa0a255f74d04c0b76ece2d7c691f9dd11a64a8ac49f62a99c3a05f9d01232103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ac00000000')
|
||||
self.assertEqual('b2ce556154e5ab22bec0a2f990b2b843f4f4085486c0d2cd82873685c0012004', tx.txid())
|
||||
raw_tx = '0100000000010100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e8030000000000001976a9144c9c3dfac4207d5d8cb89df5722cb3d712385e3f88ac02483045022100aa5d8aa40a90f23ce2c3d11bc845ca4a12acd99cbea37de6b9f6d86edebba8cb022022dedc2aa0a255f74d04c0b76ece2d7c691f9dd11a64a8ac49f62a99c3a05f9d01232103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ac00000000'
|
||||
txid = 'b2ce556154e5ab22bec0a2f990b2b843f4f4085486c0d2cd82873685c0012004'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0064(self):
|
||||
tx = transaction.Transaction('01000000000101000100000000000000000000000000000000000000000000000000000000000000000000171600144c9c3dfac4207d5d8cb89df5722cb3d712385e3fffffffff01e8030000000000001976a9144c9c3dfac4207d5d8cb89df5722cb3d712385e3f88ac02483045022100cfb07164b36ba64c1b1e8c7720a56ad64d96f6ef332d3d37f9cb3c96477dc44502200a464cd7a9cf94cd70f66ce4f4f0625ef650052c7afcfe29d7d7e01830ff91ed012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc7100000000')
|
||||
self.assertEqual('fee125c6cd142083fabd0187b1dd1f94c66c89ec6e6ef6da1374881c0c19aece', tx.txid())
|
||||
raw_tx = '01000000000101000100000000000000000000000000000000000000000000000000000000000000000000171600144c9c3dfac4207d5d8cb89df5722cb3d712385e3fffffffff01e8030000000000001976a9144c9c3dfac4207d5d8cb89df5722cb3d712385e3f88ac02483045022100cfb07164b36ba64c1b1e8c7720a56ad64d96f6ef332d3d37f9cb3c96477dc44502200a464cd7a9cf94cd70f66ce4f4f0625ef650052c7afcfe29d7d7e01830ff91ed012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc7100000000'
|
||||
txid = 'fee125c6cd142083fabd0187b1dd1f94c66c89ec6e6ef6da1374881c0c19aece'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0065(self):
|
||||
tx = transaction.Transaction('0100000000010100010000000000000000000000000000000000000000000000000000000000000000000023220020ff25429251b5a84f452230a3c75fd886b7fc5a7865ce4a7bb7a9d7c5be6da3dbffffffff01e8030000000000001976a9144c9c3dfac4207d5d8cb89df5722cb3d712385e3f88ac02483045022100aa5d8aa40a90f23ce2c3d11bc845ca4a12acd99cbea37de6b9f6d86edebba8cb022022dedc2aa0a255f74d04c0b76ece2d7c691f9dd11a64a8ac49f62a99c3a05f9d01232103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ac00000000')
|
||||
self.assertEqual('5f32557914351fee5f89ddee6c8983d476491d29e601d854e3927299e50450da', tx.txid())
|
||||
raw_tx = '0100000000010100010000000000000000000000000000000000000000000000000000000000000000000023220020ff25429251b5a84f452230a3c75fd886b7fc5a7865ce4a7bb7a9d7c5be6da3dbffffffff01e8030000000000001976a9144c9c3dfac4207d5d8cb89df5722cb3d712385e3f88ac02483045022100aa5d8aa40a90f23ce2c3d11bc845ca4a12acd99cbea37de6b9f6d86edebba8cb022022dedc2aa0a255f74d04c0b76ece2d7c691f9dd11a64a8ac49f62a99c3a05f9d01232103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ac00000000'
|
||||
txid = '5f32557914351fee5f89ddee6c8983d476491d29e601d854e3927299e50450da'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0066(self):
|
||||
tx = transaction.Transaction('0100000000010400010000000000000000000000000000000000000000000000000000000000000200000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000300000000ffffffff05540b0000000000000151d0070000000000000151840300000000000001513c0f00000000000001512c010000000000000151000248304502210092f4777a0f17bf5aeb8ae768dec5f2c14feabf9d1fe2c89c78dfed0f13fdb86902206da90a86042e252bcd1e80a168c719e4a1ddcc3cebea24b9812c5453c79107e9832103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71000000000000')
|
||||
self.assertEqual('07dfa2da3d67c8a2b9f7bd31862161f7b497829d5da90a88ba0f1a905e7a43f7', tx.txid())
|
||||
raw_tx = '0100000000010400010000000000000000000000000000000000000000000000000000000000000200000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000300000000ffffffff05540b0000000000000151d0070000000000000151840300000000000001513c0f00000000000001512c010000000000000151000248304502210092f4777a0f17bf5aeb8ae768dec5f2c14feabf9d1fe2c89c78dfed0f13fdb86902206da90a86042e252bcd1e80a168c719e4a1ddcc3cebea24b9812c5453c79107e9832103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71000000000000'
|
||||
txid = '07dfa2da3d67c8a2b9f7bd31862161f7b497829d5da90a88ba0f1a905e7a43f7'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0067(self):
|
||||
tx = transaction.Transaction('0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff03e8030000000000000151d0070000000000000151b80b0000000000000151000248304502210092f4777a0f17bf5aeb8ae768dec5f2c14feabf9d1fe2c89c78dfed0f13fdb86902206da90a86042e252bcd1e80a168c719e4a1ddcc3cebea24b9812c5453c79107e9832103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000')
|
||||
self.assertEqual('8a1bddf924d24570074b09d7967c145e54dc4cee7972a92fd975a2ad9e64b424', tx.txid())
|
||||
raw_tx = '0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff03e8030000000000000151d0070000000000000151b80b0000000000000151000248304502210092f4777a0f17bf5aeb8ae768dec5f2c14feabf9d1fe2c89c78dfed0f13fdb86902206da90a86042e252bcd1e80a168c719e4a1ddcc3cebea24b9812c5453c79107e9832103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000'
|
||||
txid = '8a1bddf924d24570074b09d7967c145e54dc4cee7972a92fd975a2ad9e64b424'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0068(self):
|
||||
tx = transaction.Transaction('0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff0484030000000000000151d0070000000000000151540b0000000000000151c800000000000000015100024730440220699e6b0cfe015b64ca3283e6551440a34f901ba62dd4c72fe1cb815afb2e6761022021cc5e84db498b1479de14efda49093219441adc6c543e5534979605e273d80b032103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000')
|
||||
self.assertEqual('f92bb6e4f3ff89172f23ef647f74c13951b665848009abb5862cdf7a0412415a', tx.txid())
|
||||
raw_tx = '0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff0484030000000000000151d0070000000000000151540b0000000000000151c800000000000000015100024730440220699e6b0cfe015b64ca3283e6551440a34f901ba62dd4c72fe1cb815afb2e6761022021cc5e84db498b1479de14efda49093219441adc6c543e5534979605e273d80b032103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000'
|
||||
txid = 'f92bb6e4f3ff89172f23ef647f74c13951b665848009abb5862cdf7a0412415a'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0069(self):
|
||||
tx = transaction.Transaction('0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff03e8030000000000000151d0070000000000000151b80b000000000000015100024730440220699e6b0cfe015b64ca3283e6551440a34f901ba62dd4c72fe1cb815afb2e6761022021cc5e84db498b1479de14efda49093219441adc6c543e5534979605e273d80b032103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000')
|
||||
self.assertEqual('8a1bddf924d24570074b09d7967c145e54dc4cee7972a92fd975a2ad9e64b424', tx.txid())
|
||||
raw_tx = '0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff03e8030000000000000151d0070000000000000151b80b000000000000015100024730440220699e6b0cfe015b64ca3283e6551440a34f901ba62dd4c72fe1cb815afb2e6761022021cc5e84db498b1479de14efda49093219441adc6c543e5534979605e273d80b032103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000'
|
||||
txid = '8a1bddf924d24570074b09d7967c145e54dc4cee7972a92fd975a2ad9e64b424'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0070(self):
|
||||
tx = transaction.Transaction('0100000000010400010000000000000000000000000000000000000000000000000000000000000200000000ffffffff00010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000300000000ffffffff04b60300000000000001519e070000000000000151860b00000000000001009600000000000000015100000248304502210091b32274295c2a3fa02f5bce92fb2789e3fc6ea947fbe1a76e52ea3f4ef2381a022079ad72aefa3837a2e0c033a8652a59731da05fa4a813f4fc48e87c075037256b822103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000')
|
||||
self.assertEqual('e657e25fc9f2b33842681613402759222a58cf7dd504d6cdc0b69a0b8c2e7dcb', tx.txid())
|
||||
raw_tx = '0100000000010400010000000000000000000000000000000000000000000000000000000000000200000000ffffffff00010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000300000000ffffffff04b60300000000000001519e070000000000000151860b00000000000001009600000000000000015100000248304502210091b32274295c2a3fa02f5bce92fb2789e3fc6ea947fbe1a76e52ea3f4ef2381a022079ad72aefa3837a2e0c033a8652a59731da05fa4a813f4fc48e87c075037256b822103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000'
|
||||
txid = 'e657e25fc9f2b33842681613402759222a58cf7dd504d6cdc0b69a0b8c2e7dcb'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0071(self):
|
||||
tx = transaction.Transaction('0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff03e8030000000000000151d0070000000000000151b80b0000000000000151000248304502210091b32274295c2a3fa02f5bce92fb2789e3fc6ea947fbe1a76e52ea3f4ef2381a022079ad72aefa3837a2e0c033a8652a59731da05fa4a813f4fc48e87c075037256b822103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000')
|
||||
self.assertEqual('8a1bddf924d24570074b09d7967c145e54dc4cee7972a92fd975a2ad9e64b424', tx.txid())
|
||||
raw_tx = '0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff03e8030000000000000151d0070000000000000151b80b0000000000000151000248304502210091b32274295c2a3fa02f5bce92fb2789e3fc6ea947fbe1a76e52ea3f4ef2381a022079ad72aefa3837a2e0c033a8652a59731da05fa4a813f4fc48e87c075037256b822103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000'
|
||||
txid = '8a1bddf924d24570074b09d7967c145e54dc4cee7972a92fd975a2ad9e64b424'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0072(self):
|
||||
tx = transaction.Transaction('0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff04b60300000000000001519e070000000000000151860b0000000000000100960000000000000001510002473044022022fceb54f62f8feea77faac7083c3b56c4676a78f93745adc8a35800bc36adfa022026927df9abcf0a8777829bcfcce3ff0a385fa54c3f9df577405e3ef24ee56479022103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000')
|
||||
self.assertEqual('4ede5e22992d43d42ccdf6553fb46e448aa1065ba36423f979605c1e5ab496b8', tx.txid())
|
||||
raw_tx = '0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff04b60300000000000001519e070000000000000151860b0000000000000100960000000000000001510002473044022022fceb54f62f8feea77faac7083c3b56c4676a78f93745adc8a35800bc36adfa022026927df9abcf0a8777829bcfcce3ff0a385fa54c3f9df577405e3ef24ee56479022103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000'
|
||||
txid = '4ede5e22992d43d42ccdf6553fb46e448aa1065ba36423f979605c1e5ab496b8'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0073(self):
|
||||
tx = transaction.Transaction('0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff03e8030000000000000151d0070000000000000151b80b00000000000001510002473044022022fceb54f62f8feea77faac7083c3b56c4676a78f93745adc8a35800bc36adfa022026927df9abcf0a8777829bcfcce3ff0a385fa54c3f9df577405e3ef24ee56479022103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000')
|
||||
self.assertEqual('8a1bddf924d24570074b09d7967c145e54dc4cee7972a92fd975a2ad9e64b424', tx.txid())
|
||||
raw_tx = '0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff03e8030000000000000151d0070000000000000151b80b00000000000001510002473044022022fceb54f62f8feea77faac7083c3b56c4676a78f93745adc8a35800bc36adfa022026927df9abcf0a8777829bcfcce3ff0a385fa54c3f9df577405e3ef24ee56479022103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000'
|
||||
txid = '8a1bddf924d24570074b09d7967c145e54dc4cee7972a92fd975a2ad9e64b424'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0074(self):
|
||||
tx = transaction.Transaction('01000000000103000100000000000000000000000000000000000000000000000000000000000000000000000200000000010000000000000000000000000000000000000000000000000000000000000100000000ffffffff000100000000000000000000000000000000000000000000000000000000000002000000000200000003e8030000000000000151d0070000000000000151b80b00000000000001510002473044022022fceb54f62f8feea77faac7083c3b56c4676a78f93745adc8a35800bc36adfa022026927df9abcf0a8777829bcfcce3ff0a385fa54c3f9df577405e3ef24ee56479022103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000')
|
||||
self.assertEqual('cfe9f4b19f52b8366860aec0d2b5815e329299b2e9890d477edd7f1182be7ac8', tx.txid())
|
||||
raw_tx = '01000000000103000100000000000000000000000000000000000000000000000000000000000000000000000200000000010000000000000000000000000000000000000000000000000000000000000100000000ffffffff000100000000000000000000000000000000000000000000000000000000000002000000000200000003e8030000000000000151d0070000000000000151b80b00000000000001510002473044022022fceb54f62f8feea77faac7083c3b56c4676a78f93745adc8a35800bc36adfa022026927df9abcf0a8777829bcfcce3ff0a385fa54c3f9df577405e3ef24ee56479022103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000'
|
||||
txid = 'cfe9f4b19f52b8366860aec0d2b5815e329299b2e9890d477edd7f1182be7ac8'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0075(self):
|
||||
tx = transaction.Transaction('0100000000010400010000000000000000000000000000000000000000000000000000000000000200000000ffffffff00010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000300000000ffffffff03e8030000000000000151d0070000000000000151b80b0000000000000151000002483045022100a3cec69b52cba2d2de623eeef89e0ba1606184ea55476c0f8189fda231bc9cbb022003181ad597f7c380a7d1c740286b1d022b8b04ded028b833282e055e03b8efef812103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000')
|
||||
self.assertEqual('aee8f4865ca40fa77ff2040c0d7de683bea048b103d42ca406dc07dd29d539cb', tx.txid())
|
||||
raw_tx = '0100000000010400010000000000000000000000000000000000000000000000000000000000000200000000ffffffff00010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000300000000ffffffff03e8030000000000000151d0070000000000000151b80b0000000000000151000002483045022100a3cec69b52cba2d2de623eeef89e0ba1606184ea55476c0f8189fda231bc9cbb022003181ad597f7c380a7d1c740286b1d022b8b04ded028b833282e055e03b8efef812103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000'
|
||||
txid = 'aee8f4865ca40fa77ff2040c0d7de683bea048b103d42ca406dc07dd29d539cb'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0076(self):
|
||||
tx = transaction.Transaction('0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff03e8030000000000000151d0070000000000000151b80b00000000000001510002483045022100a3cec69b52cba2d2de623eeef89e0ba1606184ea55476c0f8189fda231bc9cbb022003181ad597f7c380a7d1c740286b1d022b8b04ded028b833282e055e03b8efef812103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000')
|
||||
self.assertEqual('8a1bddf924d24570074b09d7967c145e54dc4cee7972a92fd975a2ad9e64b424', tx.txid())
|
||||
raw_tx = '0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff03e8030000000000000151d0070000000000000151b80b00000000000001510002483045022100a3cec69b52cba2d2de623eeef89e0ba1606184ea55476c0f8189fda231bc9cbb022003181ad597f7c380a7d1c740286b1d022b8b04ded028b833282e055e03b8efef812103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000'
|
||||
txid = '8a1bddf924d24570074b09d7967c145e54dc4cee7972a92fd975a2ad9e64b424'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0077(self):
|
||||
tx = transaction.Transaction('0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff03e8030000000000000151d0070000000000000151b80b00000000000001510002483045022100a3cec69b52cba2d2de623ffffffffff1606184ea55476c0f8189fda231bc9cbb022003181ad597f7c380a7d1c740286b1d022b8b04ded028b833282e055e03b8efef812103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000')
|
||||
self.assertEqual('8a1bddf924d24570074b09d7967c145e54dc4cee7972a92fd975a2ad9e64b424', tx.txid())
|
||||
raw_tx = '0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff03e8030000000000000151d0070000000000000151b80b00000000000001510002483045022100a3cec69b52cba2d2de623ffffffffff1606184ea55476c0f8189fda231bc9cbb022003181ad597f7c380a7d1c740286b1d022b8b04ded028b833282e055e03b8efef812103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000'
|
||||
txid = '8a1bddf924d24570074b09d7967c145e54dc4cee7972a92fd975a2ad9e64b424'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0078(self):
|
||||
tx = transaction.Transaction('0100000000010100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff010000000000000000015102fd08020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002755100000000')
|
||||
self.assertEqual('d93ab9e12d7c29d2adc13d5cdf619d53eec1f36eb6612f55af52be7ba0448e97', tx.txid())
|
||||
raw_tx = '0100000000010100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff010000000000000000015102fd08020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002755100000000'
|
||||
txid = 'd93ab9e12d7c29d2adc13d5cdf619d53eec1f36eb6612f55af52be7ba0448e97'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0079(self):
|
||||
tx = transaction.Transaction('0100000000010c00010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff0001000000000000000000000000000000000000000000000000000000000000020000006a473044022026c2e65b33fcd03b2a3b0f25030f0244bd23cc45ae4dec0f48ae62255b1998a00220463aa3982b718d593a6b9e0044513fd67a5009c2fdccc59992cffc2b167889f4012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ffffffff0001000000000000000000000000000000000000000000000000000000000000030000006a4730440220008bd8382911218dcb4c9f2e75bf5c5c3635f2f2df49b36994fde85b0be21a1a02205a539ef10fb4c778b522c1be852352ea06c67ab74200977c722b0bc68972575a012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ffffffff0001000000000000000000000000000000000000000000000000000000000000040000006b483045022100d9436c32ff065127d71e1a20e319e4fe0a103ba0272743dbd8580be4659ab5d302203fd62571ee1fe790b182d078ecfd092a509eac112bea558d122974ef9cc012c7012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ffffffff0001000000000000000000000000000000000000000000000000000000000000050000006a47304402200e2c149b114ec546015c13b2b464bbcb0cdc5872e6775787527af6cbc4830b6c02207e9396c6979fb15a9a2b96ca08a633866eaf20dc0ff3c03e512c1d5a1654f148012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ffffffff0001000000000000000000000000000000000000000000000000000000000000060000006b483045022100b20e70d897dc15420bccb5e0d3e208d27bdd676af109abbd3f88dbdb7721e6d6022005836e663173fbdfe069f54cde3c2decd3d0ea84378092a5d9d85ec8642e8a41012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ffffffff00010000000000000000000000000000000000000000000000000000000000000700000000ffffffff00010000000000000000000000000000000000000000000000000000000000000800000000ffffffff00010000000000000000000000000000000000000000000000000000000000000900000000ffffffff00010000000000000000000000000000000000000000000000000000000000000a00000000ffffffff00010000000000000000000000000000000000000000000000000000000000000b0000006a47304402206639c6e05e3b9d2675a7f3876286bdf7584fe2bbd15e0ce52dd4e02c0092cdc60220757d60b0a61fc95ada79d23746744c72bac1545a75ff6c2c7cdb6ae04e7e9592012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ffffffff0ce8030000000000000151e9030000000000000151ea030000000000000151eb030000000000000151ec030000000000000151ed030000000000000151ee030000000000000151ef030000000000000151f0030000000000000151f1030000000000000151f2030000000000000151f30300000000000001510248304502210082219a54f61bf126bfc3fa068c6e33831222d1d7138c6faa9d33ca87fd4202d6022063f9902519624254d7c2c8ea7ba2d66ae975e4e229ae38043973ec707d5d4a83012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc7102473044022017fb58502475848c1b09f162cb1688d0920ff7f142bed0ef904da2ccc88b168f02201798afa61850c65e77889cbcd648a5703b487895517c88f85cdd18b021ee246a012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc7100000000000247304402202830b7926e488da75782c81a54cd281720890d1af064629ebf2e31bf9f5435f30220089afaa8b455bbeb7d9b9c3fe1ed37d07685ade8455c76472cda424d93e4074a012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc7102473044022026326fcdae9207b596c2b05921dbac11d81040c4d40378513670f19d9f4af893022034ecd7a282c0163b89aaa62c22ec202cef4736c58cd251649bad0d8139bcbf55012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71024730440220214978daeb2f38cd426ee6e2f44131a33d6b191af1c216247f1dd7d74c16d84a02205fdc05529b0bc0c430b4d5987264d9d075351c4f4484c16e91662e90a72aab24012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710247304402204a6e9f199dc9672cf2ff8094aaa784363be1eb62b679f7ff2df361124f1dca3302205eeb11f70fab5355c9c8ad1a0700ea355d315e334822fa182227e9815308ee8f012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000')
|
||||
self.assertEqual('b83579db5246aa34255642768167132a0c3d2932b186cd8fb9f5490460a0bf91', tx.txid())
|
||||
raw_tx = '0100000000010c00010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff0001000000000000000000000000000000000000000000000000000000000000020000006a473044022026c2e65b33fcd03b2a3b0f25030f0244bd23cc45ae4dec0f48ae62255b1998a00220463aa3982b718d593a6b9e0044513fd67a5009c2fdccc59992cffc2b167889f4012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ffffffff0001000000000000000000000000000000000000000000000000000000000000030000006a4730440220008bd8382911218dcb4c9f2e75bf5c5c3635f2f2df49b36994fde85b0be21a1a02205a539ef10fb4c778b522c1be852352ea06c67ab74200977c722b0bc68972575a012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ffffffff0001000000000000000000000000000000000000000000000000000000000000040000006b483045022100d9436c32ff065127d71e1a20e319e4fe0a103ba0272743dbd8580be4659ab5d302203fd62571ee1fe790b182d078ecfd092a509eac112bea558d122974ef9cc012c7012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ffffffff0001000000000000000000000000000000000000000000000000000000000000050000006a47304402200e2c149b114ec546015c13b2b464bbcb0cdc5872e6775787527af6cbc4830b6c02207e9396c6979fb15a9a2b96ca08a633866eaf20dc0ff3c03e512c1d5a1654f148012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ffffffff0001000000000000000000000000000000000000000000000000000000000000060000006b483045022100b20e70d897dc15420bccb5e0d3e208d27bdd676af109abbd3f88dbdb7721e6d6022005836e663173fbdfe069f54cde3c2decd3d0ea84378092a5d9d85ec8642e8a41012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ffffffff00010000000000000000000000000000000000000000000000000000000000000700000000ffffffff00010000000000000000000000000000000000000000000000000000000000000800000000ffffffff00010000000000000000000000000000000000000000000000000000000000000900000000ffffffff00010000000000000000000000000000000000000000000000000000000000000a00000000ffffffff00010000000000000000000000000000000000000000000000000000000000000b0000006a47304402206639c6e05e3b9d2675a7f3876286bdf7584fe2bbd15e0ce52dd4e02c0092cdc60220757d60b0a61fc95ada79d23746744c72bac1545a75ff6c2c7cdb6ae04e7e9592012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ffffffff0ce8030000000000000151e9030000000000000151ea030000000000000151eb030000000000000151ec030000000000000151ed030000000000000151ee030000000000000151ef030000000000000151f0030000000000000151f1030000000000000151f2030000000000000151f30300000000000001510248304502210082219a54f61bf126bfc3fa068c6e33831222d1d7138c6faa9d33ca87fd4202d6022063f9902519624254d7c2c8ea7ba2d66ae975e4e229ae38043973ec707d5d4a83012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc7102473044022017fb58502475848c1b09f162cb1688d0920ff7f142bed0ef904da2ccc88b168f02201798afa61850c65e77889cbcd648a5703b487895517c88f85cdd18b021ee246a012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc7100000000000247304402202830b7926e488da75782c81a54cd281720890d1af064629ebf2e31bf9f5435f30220089afaa8b455bbeb7d9b9c3fe1ed37d07685ade8455c76472cda424d93e4074a012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc7102473044022026326fcdae9207b596c2b05921dbac11d81040c4d40378513670f19d9f4af893022034ecd7a282c0163b89aaa62c22ec202cef4736c58cd251649bad0d8139bcbf55012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71024730440220214978daeb2f38cd426ee6e2f44131a33d6b191af1c216247f1dd7d74c16d84a02205fdc05529b0bc0c430b4d5987264d9d075351c4f4484c16e91662e90a72aab24012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710247304402204a6e9f199dc9672cf2ff8094aaa784363be1eb62b679f7ff2df361124f1dca3302205eeb11f70fab5355c9c8ad1a0700ea355d315e334822fa182227e9815308ee8f012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000'
|
||||
txid = 'b83579db5246aa34255642768167132a0c3d2932b186cd8fb9f5490460a0bf91'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0080(self):
|
||||
tx = transaction.Transaction('010000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e803000000000000015100000000')
|
||||
self.assertEqual('2b1e44fff489d09091e5e20f9a01bbc0e8d80f0662e629fd10709cdb4922a874', tx.txid())
|
||||
raw_tx = '010000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e803000000000000015100000000'
|
||||
txid = '2b1e44fff489d09091e5e20f9a01bbc0e8d80f0662e629fd10709cdb4922a874'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0081(self):
|
||||
tx = transaction.Transaction('0100000000010200010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff01d00700000000000001510003483045022100e078de4e96a0e05dcdc0a414124dd8475782b5f3f0ed3f607919e9a5eeeb22bf02201de309b3a3109adb3de8074b3610d4cf454c49b61247a2779a0bcbf31c889333032103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc711976a9144c9c3dfac4207d5d8cb89df5722cb3d712385e3f88ac00000000')
|
||||
self.assertEqual('60ebb1dd0b598e20dd0dd462ef6723dd49f8f803b6a2492926012360119cfdd7', tx.txid())
|
||||
raw_tx = '0100000000010200010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff01d00700000000000001510003483045022100e078de4e96a0e05dcdc0a414124dd8475782b5f3f0ed3f607919e9a5eeeb22bf02201de309b3a3109adb3de8074b3610d4cf454c49b61247a2779a0bcbf31c889333032103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc711976a9144c9c3dfac4207d5d8cb89df5722cb3d712385e3f88ac00000000'
|
||||
txid = '60ebb1dd0b598e20dd0dd462ef6723dd49f8f803b6a2492926012360119cfdd7'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0082(self):
|
||||
tx = transaction.Transaction('0100000000010200010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff02e8030000000000000151e90300000000000001510247304402206d59682663faab5e4cb733c562e22cdae59294895929ec38d7c016621ff90da0022063ef0af5f970afe8a45ea836e3509b8847ed39463253106ac17d19c437d3d56b832103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710248304502210085001a820bfcbc9f9de0298af714493f8a37b3b354bfd21a7097c3e009f2018c022050a8b4dbc8155d4d04da2f5cdd575dcf8dd0108de8bec759bd897ea01ecb3af7832103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc7100000000')
|
||||
self.assertEqual('ed0c7f4163e275f3f77064f471eac861d01fdf55d03aa6858ebd3781f70bf003', tx.txid())
|
||||
raw_tx = '0100000000010200010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff02e8030000000000000151e90300000000000001510247304402206d59682663faab5e4cb733c562e22cdae59294895929ec38d7c016621ff90da0022063ef0af5f970afe8a45ea836e3509b8847ed39463253106ac17d19c437d3d56b832103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710248304502210085001a820bfcbc9f9de0298af714493f8a37b3b354bfd21a7097c3e009f2018c022050a8b4dbc8155d4d04da2f5cdd575dcf8dd0108de8bec759bd897ea01ecb3af7832103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc7100000000'
|
||||
txid = 'ed0c7f4163e275f3f77064f471eac861d01fdf55d03aa6858ebd3781f70bf003'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0083(self):
|
||||
tx = transaction.Transaction('0100000000010200010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000000000000ffffffff02e9030000000000000151e80300000000000001510248304502210085001a820bfcbc9f9de0298af714493f8a37b3b354bfd21a7097c3e009f2018c022050a8b4dbc8155d4d04da2f5cdd575dcf8dd0108de8bec759bd897ea01ecb3af7832103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710247304402206d59682663faab5e4cb733c562e22cdae59294895929ec38d7c016621ff90da0022063ef0af5f970afe8a45ea836e3509b8847ed39463253106ac17d19c437d3d56b832103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc7100000000')
|
||||
self.assertEqual('f531ddf5ce141e1c8a7fdfc85cc634e5ff686f446a5cf7483e9dbe076b844862', tx.txid())
|
||||
raw_tx = '0100000000010200010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000000000000ffffffff02e9030000000000000151e80300000000000001510248304502210085001a820bfcbc9f9de0298af714493f8a37b3b354bfd21a7097c3e009f2018c022050a8b4dbc8155d4d04da2f5cdd575dcf8dd0108de8bec759bd897ea01ecb3af7832103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710247304402206d59682663faab5e4cb733c562e22cdae59294895929ec38d7c016621ff90da0022063ef0af5f970afe8a45ea836e3509b8847ed39463253106ac17d19c437d3d56b832103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc7100000000'
|
||||
txid = 'f531ddf5ce141e1c8a7fdfc85cc634e5ff686f446a5cf7483e9dbe076b844862'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0084(self):
|
||||
tx = transaction.Transaction('01000000020001000000000000000000000000000000000000000000000000000000000000000000004847304402202a0b4b1294d70540235ae033d78e64b4897ec859c7b6f1b2b1d8a02e1d46006702201445e756d2254b0f1dfda9ab8e1e1bc26df9668077403204f32d16a49a36eb6983ffffffff00010000000000000000000000000000000000000000000000000000000000000100000049483045022100acb96cfdbda6dc94b489fd06f2d720983b5f350e31ba906cdbd800773e80b21c02200d74ea5bdf114212b4bbe9ed82c36d2e369e302dff57cb60d01c428f0bd3daab83ffffffff02e8030000000000000151e903000000000000015100000000')
|
||||
self.assertEqual('98229b70948f1c17851a541f1fe532bf02c408267fecf6d7e174c359ae870654', tx.txid())
|
||||
raw_tx = '01000000020001000000000000000000000000000000000000000000000000000000000000000000004847304402202a0b4b1294d70540235ae033d78e64b4897ec859c7b6f1b2b1d8a02e1d46006702201445e756d2254b0f1dfda9ab8e1e1bc26df9668077403204f32d16a49a36eb6983ffffffff00010000000000000000000000000000000000000000000000000000000000000100000049483045022100acb96cfdbda6dc94b489fd06f2d720983b5f350e31ba906cdbd800773e80b21c02200d74ea5bdf114212b4bbe9ed82c36d2e369e302dff57cb60d01c428f0bd3daab83ffffffff02e8030000000000000151e903000000000000015100000000'
|
||||
txid = '98229b70948f1c17851a541f1fe532bf02c408267fecf6d7e174c359ae870654'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0085(self):
|
||||
tx = transaction.Transaction('01000000000102fe3dc9208094f3ffd12645477b3dc56f60ec4fa8e6f5d67c565d1c6b9216b36e000000004847304402200af4e47c9b9629dbecc21f73af989bdaa911f7e6f6c2e9394588a3aa68f81e9902204f3fcf6ade7e5abb1295b6774c8e0abd94ae62217367096bc02ee5e435b67da201ffffffff0815cf020f013ed6cf91d29f4202e8a58726b1ac6c79da47c23d1bee0a6925f80000000000ffffffff0100f2052a010000001976a914a30741f8145e5acadf23f751864167f32e0963f788ac000347304402200de66acf4527789bfda55fc5459e214fa6083f936b430a762c629656216805ac0220396f550692cd347171cbc1ef1f51e15282e837bb2b30860dc77c8f78bc8501e503473044022027dc95ad6b740fe5129e7e62a75dd00f291a2aeb1200b84b09d9e3789406b6c002201a9ecd315dd6a0e632ab20bbb98948bc0c6fb204f2c286963bb48517a7058e27034721026dccc749adc2a9d0d89497ac511f760f45c47dc5ed9cf352a58ac706453880aeadab210255a9626aebf5e29c0e6538428ba0d1dcf6ca98ffdf086aa8ced5e0d0215ea465ac00000000')
|
||||
self.assertEqual('570e3730deeea7bd8bc92c836ccdeb4dd4556f2c33f2a1f7b889a4cb4e48d3ab', tx.txid())
|
||||
raw_tx = '01000000000102fe3dc9208094f3ffd12645477b3dc56f60ec4fa8e6f5d67c565d1c6b9216b36e000000004847304402200af4e47c9b9629dbecc21f73af989bdaa911f7e6f6c2e9394588a3aa68f81e9902204f3fcf6ade7e5abb1295b6774c8e0abd94ae62217367096bc02ee5e435b67da201ffffffff0815cf020f013ed6cf91d29f4202e8a58726b1ac6c79da47c23d1bee0a6925f80000000000ffffffff0100f2052a010000001976a914a30741f8145e5acadf23f751864167f32e0963f788ac000347304402200de66acf4527789bfda55fc5459e214fa6083f936b430a762c629656216805ac0220396f550692cd347171cbc1ef1f51e15282e837bb2b30860dc77c8f78bc8501e503473044022027dc95ad6b740fe5129e7e62a75dd00f291a2aeb1200b84b09d9e3789406b6c002201a9ecd315dd6a0e632ab20bbb98948bc0c6fb204f2c286963bb48517a7058e27034721026dccc749adc2a9d0d89497ac511f760f45c47dc5ed9cf352a58ac706453880aeadab210255a9626aebf5e29c0e6538428ba0d1dcf6ca98ffdf086aa8ced5e0d0215ea465ac00000000'
|
||||
txid = '570e3730deeea7bd8bc92c836ccdeb4dd4556f2c33f2a1f7b889a4cb4e48d3ab'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0086(self):
|
||||
tx = transaction.Transaction('01000000000102e9b542c5176808107ff1df906f46bb1f2583b16112b95ee5380665ba7fcfc0010000000000ffffffff80e68831516392fcd100d186b3c2c7b95c80b53c77e77c35ba03a66b429a2a1b0000000000ffffffff0280969800000000001976a914de4b231626ef508c9a74a8517e6783c0546d6b2888ac80969800000000001976a9146648a8cd4531e1ec47f35916de8e259237294d1e88ac02483045022100f6a10b8604e6dc910194b79ccfc93e1bc0ec7c03453caaa8987f7d6c3413566002206216229ede9b4d6ec2d325be245c5b508ff0339bf1794078e20bfe0babc7ffe683270063ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac024730440220032521802a76ad7bf74d0e2c218b72cf0cbc867066e2e53db905ba37f130397e02207709e2188ed7f08f4c952d9d13986da504502b8c3be59617e043552f506c46ff83275163ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac00000000')
|
||||
self.assertEqual('e0b8142f587aaa322ca32abce469e90eda187f3851043cc4f2a0fff8c13fc84e', tx.txid())
|
||||
raw_tx = '01000000000102e9b542c5176808107ff1df906f46bb1f2583b16112b95ee5380665ba7fcfc0010000000000ffffffff80e68831516392fcd100d186b3c2c7b95c80b53c77e77c35ba03a66b429a2a1b0000000000ffffffff0280969800000000001976a914de4b231626ef508c9a74a8517e6783c0546d6b2888ac80969800000000001976a9146648a8cd4531e1ec47f35916de8e259237294d1e88ac02483045022100f6a10b8604e6dc910194b79ccfc93e1bc0ec7c03453caaa8987f7d6c3413566002206216229ede9b4d6ec2d325be245c5b508ff0339bf1794078e20bfe0babc7ffe683270063ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac024730440220032521802a76ad7bf74d0e2c218b72cf0cbc867066e2e53db905ba37f130397e02207709e2188ed7f08f4c952d9d13986da504502b8c3be59617e043552f506c46ff83275163ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac00000000'
|
||||
txid = 'e0b8142f587aaa322ca32abce469e90eda187f3851043cc4f2a0fff8c13fc84e'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0087(self):
|
||||
tx = transaction.Transaction('0100000000010280e68831516392fcd100d186b3c2c7b95c80b53c77e77c35ba03a66b429a2a1b0000000000ffffffffe9b542c5176808107ff1df906f46bb1f2583b16112b95ee5380665ba7fcfc0010000000000ffffffff0280969800000000001976a9146648a8cd4531e1ec47f35916de8e259237294d1e88ac80969800000000001976a914de4b231626ef508c9a74a8517e6783c0546d6b2888ac024730440220032521802a76ad7bf74d0e2c218b72cf0cbc867066e2e53db905ba37f130397e02207709e2188ed7f08f4c952d9d13986da504502b8c3be59617e043552f506c46ff83275163ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac02483045022100f6a10b8604e6dc910194b79ccfc93e1bc0ec7c03453caaa8987f7d6c3413566002206216229ede9b4d6ec2d325be245c5b508ff0339bf1794078e20bfe0babc7ffe683270063ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac00000000')
|
||||
self.assertEqual('b9ecf72df06b8f98f8b63748d1aded5ffc1a1186f8a302e63cf94f6250e29f4d', tx.txid())
|
||||
raw_tx = '0100000000010280e68831516392fcd100d186b3c2c7b95c80b53c77e77c35ba03a66b429a2a1b0000000000ffffffffe9b542c5176808107ff1df906f46bb1f2583b16112b95ee5380665ba7fcfc0010000000000ffffffff0280969800000000001976a9146648a8cd4531e1ec47f35916de8e259237294d1e88ac80969800000000001976a914de4b231626ef508c9a74a8517e6783c0546d6b2888ac024730440220032521802a76ad7bf74d0e2c218b72cf0cbc867066e2e53db905ba37f130397e02207709e2188ed7f08f4c952d9d13986da504502b8c3be59617e043552f506c46ff83275163ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac02483045022100f6a10b8604e6dc910194b79ccfc93e1bc0ec7c03453caaa8987f7d6c3413566002206216229ede9b4d6ec2d325be245c5b508ff0339bf1794078e20bfe0babc7ffe683270063ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac00000000'
|
||||
txid = 'b9ecf72df06b8f98f8b63748d1aded5ffc1a1186f8a302e63cf94f6250e29f4d'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0088(self):
|
||||
tx = transaction.Transaction('0100000000010136641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e0100000023220020a16b5755f7f6f96dbd65f5f0d6ab9418b89af4b1f14a1bb8a09062c35f0dcb54ffffffff0200e9a435000000001976a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688acc0832f05000000001976a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac080047304402206ac44d672dac41f9b00e28f4df20c52eeb087207e8d758d76d92c6fab3b73e2b0220367750dbbe19290069cba53d096f44530e4f98acaa594810388cf7409a1870ce01473044022068c7946a43232757cbdf9176f009a928e1cd9a1a8c212f15c1e11ac9f2925d9002205b75f937ff2f9f3c1246e547e54f62e027f64eefa2695578cc6432cdabce271502473044022059ebf56d98010a932cf8ecfec54c48e6139ed6adb0728c09cbe1e4fa0915302e022007cd986c8fa870ff5d2b3a89139c9fe7e499259875357e20fcbb15571c76795403483045022100fbefd94bd0a488d50b79102b5dad4ab6ced30c4069f1eaa69a4b5a763414067e02203156c6a5c9cf88f91265f5a942e96213afae16d83321c8b31bb342142a14d16381483045022100a5263ea0553ba89221984bd7f0b13613db16e7a70c549a86de0cc0444141a407022005c360ef0ae5a5d4f9f2f87a56c1546cc8268cab08c73501d6b3be2e1e1a8a08824730440220525406a1482936d5a21888260dc165497a90a15669636d8edca6b9fe490d309c022032af0c646a34a44d1f4576bf6a4a74b67940f8faa84c7df9abe12a01a11e2b4783cf56210307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba32103b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b21034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a21033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f42103a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac162102d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b56ae00000000')
|
||||
self.assertEqual('27eae69aff1dd4388c0fa05cbbfe9a3983d1b0b5811ebcd4199b86f299370aac', tx.txid())
|
||||
raw_tx = '0100000000010136641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e0100000023220020a16b5755f7f6f96dbd65f5f0d6ab9418b89af4b1f14a1bb8a09062c35f0dcb54ffffffff0200e9a435000000001976a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688acc0832f05000000001976a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac080047304402206ac44d672dac41f9b00e28f4df20c52eeb087207e8d758d76d92c6fab3b73e2b0220367750dbbe19290069cba53d096f44530e4f98acaa594810388cf7409a1870ce01473044022068c7946a43232757cbdf9176f009a928e1cd9a1a8c212f15c1e11ac9f2925d9002205b75f937ff2f9f3c1246e547e54f62e027f64eefa2695578cc6432cdabce271502473044022059ebf56d98010a932cf8ecfec54c48e6139ed6adb0728c09cbe1e4fa0915302e022007cd986c8fa870ff5d2b3a89139c9fe7e499259875357e20fcbb15571c76795403483045022100fbefd94bd0a488d50b79102b5dad4ab6ced30c4069f1eaa69a4b5a763414067e02203156c6a5c9cf88f91265f5a942e96213afae16d83321c8b31bb342142a14d16381483045022100a5263ea0553ba89221984bd7f0b13613db16e7a70c549a86de0cc0444141a407022005c360ef0ae5a5d4f9f2f87a56c1546cc8268cab08c73501d6b3be2e1e1a8a08824730440220525406a1482936d5a21888260dc165497a90a15669636d8edca6b9fe490d309c022032af0c646a34a44d1f4576bf6a4a74b67940f8faa84c7df9abe12a01a11e2b4783cf56210307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba32103b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b21034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a21033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f42103a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac162102d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b56ae00000000'
|
||||
txid = '27eae69aff1dd4388c0fa05cbbfe9a3983d1b0b5811ebcd4199b86f299370aac'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0089(self):
|
||||
tx = transaction.Transaction('010000000169c12106097dc2e0526493ef67f21269fe888ef05c7a3a5dacab38e1ac8387f1581b0000b64830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0121037a3fb04bcdb09eba90f69961ba1692a3528e45e67c85b200df820212d7594d334aad4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e01ffffffff0101000000000000000000000000')
|
||||
self.assertEqual('22d020638e3b7e1f2f9a63124ac76f5e333c74387862e3675f64b25e960d3641', tx.txid())
|
||||
raw_tx = '010000000169c12106097dc2e0526493ef67f21269fe888ef05c7a3a5dacab38e1ac8387f1581b0000b64830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0121037a3fb04bcdb09eba90f69961ba1692a3528e45e67c85b200df820212d7594d334aad4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e01ffffffff0101000000000000000000000000'
|
||||
txid = '22d020638e3b7e1f2f9a63124ac76f5e333c74387862e3675f64b25e960d3641'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0090(self):
|
||||
tx = transaction.Transaction('0100000000010169c12106097dc2e0526493ef67f21269fe888ef05c7a3a5dacab38e1ac8387f14c1d000000ffffffff01010000000000000000034830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e012102a9781d66b61fb5a7ef00ac5ad5bc6ffc78be7b44a566e3c87870e1079368df4c4aad4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0100000000')
|
||||
self.assertEqual('2862bc0c69d2af55da7284d1b16a7cddc03971b77e5a97939cca7631add83bf5', tx.txid())
|
||||
raw_tx = '0100000000010169c12106097dc2e0526493ef67f21269fe888ef05c7a3a5dacab38e1ac8387f14c1d000000ffffffff01010000000000000000034830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e012102a9781d66b61fb5a7ef00ac5ad5bc6ffc78be7b44a566e3c87870e1079368df4c4aad4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0100000000'
|
||||
txid = '2862bc0c69d2af55da7284d1b16a7cddc03971b77e5a97939cca7631add83bf5'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0091(self):
|
||||
tx = transaction.Transaction('01000000019275cb8d4a485ce95741c013f7c0d28722160008021bb469a11982d47a662896581b0000fd6f01004830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c03959601522102cd74a2809ffeeed0092bc124fd79836706e41f048db3f6ae9df8708cefb83a1c2102e615999372426e46fd107b76eaf007156a507584aa2cc21de9eee3bdbd26d36c4c9552af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c0395960175ffffffff0101000000000000000000000000')
|
||||
self.assertEqual('1aebf0c98f01381765a8c33d688f8903e4d01120589ac92b78f1185dc1f4119c', tx.txid())
|
||||
raw_tx = '01000000019275cb8d4a485ce95741c013f7c0d28722160008021bb469a11982d47a662896581b0000fd6f01004830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c03959601522102cd74a2809ffeeed0092bc124fd79836706e41f048db3f6ae9df8708cefb83a1c2102e615999372426e46fd107b76eaf007156a507584aa2cc21de9eee3bdbd26d36c4c9552af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c0395960175ffffffff0101000000000000000000000000'
|
||||
txid = '1aebf0c98f01381765a8c33d688f8903e4d01120589ac92b78f1185dc1f4119c'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
def test_txid_bitcoin_core_0092(self):
|
||||
tx = transaction.Transaction('010000000001019275cb8d4a485ce95741c013f7c0d28722160008021bb469a11982d47a6628964c1d000000ffffffff0101000000000000000007004830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c0395960101022102966f109c54e85d3aee8321301136cedeb9fc710fdef58a9de8a73942f8e567c021034ffc99dd9a79dd3cb31e2ab3e0b09e0e67db41ac068c625cd1f491576016c84e9552af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c039596017500000000')
|
||||
self.assertEqual('45d17fb7db86162b2b6ca29fa4e163acf0ef0b54110e49b819bda1f948d423a3', tx.txid())
|
||||
raw_tx = '010000000001019275cb8d4a485ce95741c013f7c0d28722160008021bb469a11982d47a6628964c1d000000ffffffff0101000000000000000007004830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c0395960101022102966f109c54e85d3aee8321301136cedeb9fc710fdef58a9de8a73942f8e567c021034ffc99dd9a79dd3cb31e2ab3e0b09e0e67db41ac068c625cd1f491576016c84e9552af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c039596017500000000'
|
||||
txid = '45d17fb7db86162b2b6ca29fa4e163acf0ef0b54110e49b819bda1f948d423a3'
|
||||
self._run_naive_tests_on_tx(raw_tx, txid)
|
||||
|
||||
# txns from Bitcoin Core ends <---
|
||||
|
||||
|
|
|
@ -5,36 +5,42 @@ import lib.bitcoin as bitcoin
|
|||
import lib.keystore as keystore
|
||||
import lib.storage as storage
|
||||
import lib.wallet as wallet
|
||||
from lib import constants
|
||||
|
||||
from plugins.trustedcoin import trustedcoin
|
||||
|
||||
from . import TestCaseForTestnet
|
||||
|
||||
# TODO passphrase/seed_extension
|
||||
class TestWalletKeystoreAddressIntegrity(unittest.TestCase):
|
||||
|
||||
class WalletIntegrityHelper:
|
||||
|
||||
gap_limit = 1 # make tests run faster
|
||||
|
||||
def _check_seeded_keystore_sanity(self, ks):
|
||||
self.assertTrue (ks.is_deterministic())
|
||||
self.assertFalse(ks.is_watching_only())
|
||||
self.assertFalse(ks.can_import())
|
||||
self.assertTrue (ks.has_seed())
|
||||
@classmethod
|
||||
def check_seeded_keystore_sanity(cls, test_obj, ks):
|
||||
test_obj.assertTrue(ks.is_deterministic())
|
||||
test_obj.assertFalse(ks.is_watching_only())
|
||||
test_obj.assertFalse(ks.can_import())
|
||||
test_obj.assertTrue(ks.has_seed())
|
||||
|
||||
def _check_xpub_keystore_sanity(self, ks):
|
||||
self.assertTrue (ks.is_deterministic())
|
||||
self.assertTrue (ks.is_watching_only())
|
||||
self.assertFalse(ks.can_import())
|
||||
self.assertFalse(ks.has_seed())
|
||||
@classmethod
|
||||
def check_xpub_keystore_sanity(cls, test_obj, ks):
|
||||
test_obj.assertTrue(ks.is_deterministic())
|
||||
test_obj.assertTrue(ks.is_watching_only())
|
||||
test_obj.assertFalse(ks.can_import())
|
||||
test_obj.assertFalse(ks.has_seed())
|
||||
|
||||
def _create_standard_wallet(self, ks):
|
||||
@classmethod
|
||||
def create_standard_wallet(cls, ks):
|
||||
store = storage.WalletStorage('if_this_exists_mocking_failed_648151893')
|
||||
store.put('keystore', ks.dump())
|
||||
store.put('gap_limit', self.gap_limit)
|
||||
store.put('gap_limit', cls.gap_limit)
|
||||
w = wallet.Standard_Wallet(store)
|
||||
w.synchronize()
|
||||
return w
|
||||
|
||||
def _create_multisig_wallet(self, ks1, ks2, ks3=None):
|
||||
@classmethod
|
||||
def create_multisig_wallet(cls, ks1, ks2, ks3=None):
|
||||
"""Creates a 2-of-2 or 2-of-3 multisig wallet."""
|
||||
store = storage.WalletStorage('if_this_exists_mocking_failed_648151893')
|
||||
store.put('x%d/' % 1, ks1.dump())
|
||||
|
@ -45,11 +51,15 @@ class TestWalletKeystoreAddressIntegrity(unittest.TestCase):
|
|||
multisig_type = "%dof%d" % (2, 3)
|
||||
store.put('x%d/' % 3, ks3.dump())
|
||||
store.put('wallet_type', multisig_type)
|
||||
store.put('gap_limit', self.gap_limit)
|
||||
store.put('gap_limit', cls.gap_limit)
|
||||
w = wallet.Multisig_Wallet(store)
|
||||
w.synchronize()
|
||||
return w
|
||||
|
||||
|
||||
# TODO passphrase/seed_extension
|
||||
class TestWalletKeystoreAddressIntegrityForMainnet(unittest.TestCase):
|
||||
|
||||
@mock.patch.object(storage.WalletStorage, '_write')
|
||||
def test_electrum_seed_standard(self, mock_write):
|
||||
seed_words = 'cycle rocket west magnet parrot shuffle foot correct salt library feed song'
|
||||
|
@ -57,13 +67,13 @@ class TestWalletKeystoreAddressIntegrity(unittest.TestCase):
|
|||
|
||||
ks = keystore.from_seed(seed_words, '', False)
|
||||
|
||||
self._check_seeded_keystore_sanity(ks)
|
||||
WalletIntegrityHelper.check_seeded_keystore_sanity(self, ks)
|
||||
self.assertTrue(isinstance(ks, keystore.BIP32_KeyStore))
|
||||
|
||||
self.assertEqual(ks.xprv, 'xprv9s21ZrQH143K32jECVM729vWgGq4mUDJCk1ozqAStTphzQtCTuoFmFafNoG1g55iCnBTXUzz3zWnDb5CVLGiFvmaZjuazHDL8a81cPQ8KL6')
|
||||
self.assertEqual(ks.xpub, 'xpub661MyMwAqRbcFWohJWt7PHsFEJfZAvw9ZxwQoDa4SoMgsDDM1T7WK3u9E4edkC4ugRnZ8E4xDZRpk8Rnts3Nbt97dPwT52CwBdDWroaZf8U')
|
||||
|
||||
w = self._create_standard_wallet(ks)
|
||||
w = WalletIntegrityHelper.create_standard_wallet(ks)
|
||||
self.assertEqual(w.txin_type, 'p2pkh')
|
||||
|
||||
self.assertEqual(w.get_receiving_addresses()[0], '1NNkttn1YvVGdqBW4PR6zvc3Zx3H5owKRf')
|
||||
|
@ -76,13 +86,13 @@ class TestWalletKeystoreAddressIntegrity(unittest.TestCase):
|
|||
|
||||
ks = keystore.from_seed(seed_words, '', False)
|
||||
|
||||
self._check_seeded_keystore_sanity(ks)
|
||||
WalletIntegrityHelper.check_seeded_keystore_sanity(self, ks)
|
||||
self.assertTrue(isinstance(ks, keystore.BIP32_KeyStore))
|
||||
|
||||
self.assertEqual(ks.xprv, 'zprvAZswDvNeJeha8qZ8g7efN3FXYVJLaEUsE9TW6qXDEbVe74AZ75c2sZFZXPNFzxnhChDQ89oC8C5AjWwHmH1HeRKE1c4kKBQAmjUDdKDUZw2')
|
||||
self.assertEqual(ks.xpub, 'zpub6nsHdRuY92FsMKdbn9BfjBCG6X8pyhCibNP6uDvpnw2cyrVhecvHRMa3Ne8kdJZxjxgwnpbHLkcR4bfnhHy6auHPJyDTQ3kianeuVLdkCYQ')
|
||||
|
||||
w = self._create_standard_wallet(ks)
|
||||
w = WalletIntegrityHelper.create_standard_wallet(ks)
|
||||
self.assertEqual(w.txin_type, 'p2wpkh')
|
||||
|
||||
self.assertEqual(w.get_receiving_addresses()[0], 'bc1q3g5tmkmlvxryhh843v4dz026avatc0zzr6h3af')
|
||||
|
@ -95,12 +105,12 @@ class TestWalletKeystoreAddressIntegrity(unittest.TestCase):
|
|||
|
||||
ks = keystore.from_seed(seed_words, '', False)
|
||||
|
||||
self._check_seeded_keystore_sanity(ks)
|
||||
WalletIntegrityHelper.check_seeded_keystore_sanity(self, ks)
|
||||
self.assertTrue(isinstance(ks, keystore.Old_KeyStore))
|
||||
|
||||
self.assertEqual(ks.mpk, 'e9d4b7866dd1e91c862aebf62a49548c7dbf7bcc6e4b7b8c9da820c7737968df9c09d5a3e271dc814a29981f81b3faaf2737b551ef5dcc6189cf0f8252c442b3')
|
||||
|
||||
w = self._create_standard_wallet(ks)
|
||||
w = WalletIntegrityHelper.create_standard_wallet(ks)
|
||||
self.assertEqual(w.txin_type, 'p2pkh')
|
||||
|
||||
self.assertEqual(w.get_receiving_addresses()[0], '1FJEEB8ihPMbzs2SkLmr37dHyRFzakqUmo')
|
||||
|
@ -130,10 +140,10 @@ class TestWalletKeystoreAddressIntegrity(unittest.TestCase):
|
|||
'x2/': {'xpub': xpub2}})
|
||||
xpub3 = trustedcoin.make_xpub(trustedcoin.get_signing_xpub(), long_user_id)
|
||||
ks3 = keystore.from_xpub(xpub3)
|
||||
self._check_xpub_keystore_sanity(ks3)
|
||||
WalletIntegrityHelper.check_xpub_keystore_sanity(self, ks3)
|
||||
self.assertTrue(isinstance(ks3, keystore.BIP32_KeyStore))
|
||||
|
||||
w = self._create_multisig_wallet(ks1, ks2, ks3)
|
||||
w = WalletIntegrityHelper.create_multisig_wallet(ks1, ks2, ks3)
|
||||
self.assertEqual(w.txin_type, 'p2sh')
|
||||
|
||||
self.assertEqual(w.get_receiving_addresses()[0], '35L8XmCDoEBKeaWRjvmZvoZvhp8BXMMMPV')
|
||||
|
@ -151,7 +161,7 @@ class TestWalletKeystoreAddressIntegrity(unittest.TestCase):
|
|||
self.assertEqual(ks.xprv, 'xprv9zGLcNEb3cHUKizLVBz6RYeE9bEZAVPjH2pD1DEzCnPcsemWc3d3xTao8sfhfUmDLMq6e3RcEMEvJG1Et8dvfL8DV4h7mwm9J6AJsW9WXQD')
|
||||
self.assertEqual(ks.xpub, 'xpub6DFh1smUsyqmYD4obDX6ngaxhd53Zx7aeFjoobebm7vbkT6f9awJWFuGzBT9FQJEWFBL7UyhMXtYzRcwDuVbcxtv9Ce2W9eMm4KXLdvdbjv')
|
||||
|
||||
w = self._create_standard_wallet(ks)
|
||||
w = WalletIntegrityHelper.create_standard_wallet(ks)
|
||||
self.assertEqual(w.txin_type, 'p2pkh')
|
||||
|
||||
self.assertEqual(w.get_receiving_addresses()[0], '16j7Dqk3Z9DdTdBtHcCVLaNQy9MTgywUUo')
|
||||
|
@ -169,7 +179,7 @@ class TestWalletKeystoreAddressIntegrity(unittest.TestCase):
|
|||
self.assertEqual(ks.xprv, 'yprvAJEYHeNEPcyBoQYM7sGCxDiNCTX65u4ANgZuSGTrKN5YCC9MP84SBayrgaMyZV7zvkHrr3HVPTK853s2SPk4EttPazBZBmz6QfDkXeE8Zr7')
|
||||
self.assertEqual(ks.xpub, 'ypub6XDth9u8DzXV1tcpDtoDKMf6kVMaVMn1juVWEesTshcX4zUVvfNgjPJLXrD9N7AdTLnbHFL64KmBn3SNaTe69iZYbYCqLCCNPZKbLz9niQ4')
|
||||
|
||||
w = self._create_standard_wallet(ks)
|
||||
w = WalletIntegrityHelper.create_standard_wallet(ks)
|
||||
self.assertEqual(w.txin_type, 'p2wpkh-p2sh')
|
||||
|
||||
self.assertEqual(w.get_receiving_addresses()[0], '35ohQTdNykjkF1Mn9nAVEFjupyAtsPAK1W')
|
||||
|
@ -188,7 +198,7 @@ class TestWalletKeystoreAddressIntegrity(unittest.TestCase):
|
|||
self.assertEqual(ks.xprv, 'zprvAdG4iTXWBoARxkkzNpNh8r6Qag3irQB8PzEMkAFeTRXxHpbF9z4QgEvBRmfvqWvGp42t42nvgGpNgYSJA9iefm1yYNZKEm7z6qUWCroSQnE')
|
||||
self.assertEqual(ks.xpub, 'zpub6rFR7y4Q2AijBEqTUquhVz398htDFrtymD9xYYfG1m4wAcvPhXNfE3EfH1r1ADqtfSdVCToUG868RvUUkgDKf31mGDtKsAYz2oz2AGutZYs')
|
||||
|
||||
w = self._create_standard_wallet(ks)
|
||||
w = WalletIntegrityHelper.create_standard_wallet(ks)
|
||||
self.assertEqual(w.txin_type, 'p2wpkh')
|
||||
|
||||
self.assertEqual(w.get_receiving_addresses()[0], 'bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu')
|
||||
|
@ -200,17 +210,17 @@ class TestWalletKeystoreAddressIntegrity(unittest.TestCase):
|
|||
self.assertEqual(bitcoin.seed_type(seed_words), 'standard')
|
||||
|
||||
ks1 = keystore.from_seed(seed_words, '', True)
|
||||
self._check_seeded_keystore_sanity(ks1)
|
||||
WalletIntegrityHelper.check_seeded_keystore_sanity(self, ks1)
|
||||
self.assertTrue(isinstance(ks1, keystore.BIP32_KeyStore))
|
||||
self.assertEqual(ks1.xprv, 'xprv9s21ZrQH143K3t9vo23J3hajRbzvkRLJ6Y1zFrUFAfU3t8oooMPfb7f87cn5KntgqZs5nipZkCiBFo5ZtaSD2eDo7j7CMuFV8Zu6GYLTpY6')
|
||||
self.assertEqual(ks1.xpub, 'xpub661MyMwAqRbcGNEPu3aJQqXTydqR9t49Tkwb4Esrj112kw8xLthv8uybxvaki4Ygt9xiwZUQGeFTG7T2TUzR3eA4Zp3aq5RXsABHFBUrq4c')
|
||||
|
||||
# electrum seed: ghost into match ivory badge robot record tackle radar elbow traffic loud
|
||||
ks2 = keystore.from_xpub('xpub661MyMwAqRbcGfCPEkkyo5WmcrhTq8mi3xuBS7VEZ3LYvsgY1cCFDbenT33bdD12axvrmXhuX3xkAbKci3yZY9ZEk8vhLic7KNhLjqdh5ec')
|
||||
self._check_xpub_keystore_sanity(ks2)
|
||||
WalletIntegrityHelper.check_xpub_keystore_sanity(self, ks2)
|
||||
self.assertTrue(isinstance(ks2, keystore.BIP32_KeyStore))
|
||||
|
||||
w = self._create_multisig_wallet(ks1, ks2)
|
||||
w = WalletIntegrityHelper.create_multisig_wallet(ks1, ks2)
|
||||
self.assertEqual(w.txin_type, 'p2sh')
|
||||
|
||||
self.assertEqual(w.get_receiving_addresses()[0], '32ji3QkAgXNz6oFoRfakyD3ys1XXiERQYN')
|
||||
|
@ -222,17 +232,17 @@ class TestWalletKeystoreAddressIntegrity(unittest.TestCase):
|
|||
self.assertEqual(bitcoin.seed_type(seed_words), 'segwit')
|
||||
|
||||
ks1 = keystore.from_seed(seed_words, '', True)
|
||||
self._check_seeded_keystore_sanity(ks1)
|
||||
WalletIntegrityHelper.check_seeded_keystore_sanity(self, ks1)
|
||||
self.assertTrue(isinstance(ks1, keystore.BIP32_KeyStore))
|
||||
self.assertEqual(ks1.xprv, 'ZprvAjxLRqPiDfPDxXrm8JvcoCGRAW6xUtktucG6AMtdzaEbTEJN8qcECvujfhtDU3jLJ9g3Dr3Gz5m1ypfMs8iSUh62gWyHZ73bYLRWyeHf6y4')
|
||||
self.assertEqual(ks1.xpub, 'Zpub6xwgqLvc42wXB1wEELTdALD9iXwStMUkGqBgxkJFYumaL2dWgNvUkjEDWyDFZD3fZuDWDzd1KQJ4NwVHS7hs6H6QkpNYSShfNiUZsgMdtNg')
|
||||
|
||||
# electrum seed: hedgehog sunset update estate number jungle amount piano friend donate upper wool
|
||||
ks2 = keystore.from_xpub('Zpub6y4oYeETXAbzLNg45wcFDGwEG3vpgsyMJybiAfi2pJtNF3i3fJVxK2BeZJaw7VeKZm192QHvXP3uHDNpNmNDbQft9FiMzkKUhNXQafUMYUY')
|
||||
self._check_xpub_keystore_sanity(ks2)
|
||||
WalletIntegrityHelper.check_xpub_keystore_sanity(self, ks2)
|
||||
self.assertTrue(isinstance(ks2, keystore.BIP32_KeyStore))
|
||||
|
||||
w = self._create_multisig_wallet(ks1, ks2)
|
||||
w = WalletIntegrityHelper.create_multisig_wallet(ks1, ks2)
|
||||
self.assertEqual(w.txin_type, 'p2wsh')
|
||||
|
||||
self.assertEqual(w.get_receiving_addresses()[0], 'bc1qvzezdcv6vs5h45ugkavp896e0nde5c5lg5h0fwe2xyfhnpkxq6gq7pnwlc')
|
||||
|
@ -251,10 +261,10 @@ class TestWalletKeystoreAddressIntegrity(unittest.TestCase):
|
|||
# bip39 seed: tray machine cook badge night page project uncover ritual toward person enact
|
||||
# der: m/45'/0
|
||||
ks2 = keystore.from_xpub('xpub6B26nSWddbWv7J3qQn9FbwPPQktSBdPQfLfHhRK4375QoZq8fvM8rQey1koGSTxC5xVoMzNMaBETMUmCqmXzjc8HyAbN7LqrvE4ovGRwNGg')
|
||||
self._check_xpub_keystore_sanity(ks2)
|
||||
WalletIntegrityHelper.check_xpub_keystore_sanity(self, ks2)
|
||||
self.assertTrue(isinstance(ks2, keystore.BIP32_KeyStore))
|
||||
|
||||
w = self._create_multisig_wallet(ks1, ks2)
|
||||
w = WalletIntegrityHelper.create_multisig_wallet(ks1, ks2)
|
||||
self.assertEqual(w.txin_type, 'p2sh')
|
||||
|
||||
self.assertEqual(w.get_receiving_addresses()[0], '3JPTQ2nitVxXBJ1yhMeDwH6q417UifE3bN')
|
||||
|
@ -272,11 +282,35 @@ class TestWalletKeystoreAddressIntegrity(unittest.TestCase):
|
|||
# bip39 seed: slab mixture skin evoke harsh tattoo rare crew sphere extend balcony frost
|
||||
# der: m/49'/0'/0'
|
||||
ks2 = keystore.from_xpub('Ypub6iNDhL4WWq5kFZcdFqHHwX4YTH4rYGp8xbndpRrY7WNZFFRfogSrL7wRTajmVHgR46AT1cqUG1mrcRd7h1WXwBsgX2QvT3zFbBCDiSDLkau')
|
||||
self._check_xpub_keystore_sanity(ks2)
|
||||
WalletIntegrityHelper.check_xpub_keystore_sanity(self, ks2)
|
||||
self.assertTrue(isinstance(ks2, keystore.BIP32_KeyStore))
|
||||
|
||||
w = self._create_multisig_wallet(ks1, ks2)
|
||||
w = WalletIntegrityHelper.create_multisig_wallet(ks1, ks2)
|
||||
self.assertEqual(w.txin_type, 'p2wsh-p2sh')
|
||||
|
||||
self.assertEqual(w.get_receiving_addresses()[0], '35LeC45QgCVeRor1tJD6LiDgPbybBXisns')
|
||||
self.assertEqual(w.get_change_addresses()[0], '39RhtDchc6igmx5tyoimhojFL1ZbQBrXa6')
|
||||
|
||||
|
||||
class TestWalletKeystoreAddressIntegrityForTestnet(TestCaseForTestnet):
|
||||
|
||||
@mock.patch.object(storage.WalletStorage, '_write')
|
||||
def test_bip39_multisig_seed_p2sh_segwit_testnet(self, mock_write):
|
||||
# bip39 seed: finish seminar arrange erosion sunny coil insane together pretty lunch lunch rose
|
||||
# der: m/49'/1'/0'
|
||||
# NOTE: there is currently no bip43 standard derivation path for p2wsh-p2sh
|
||||
ks1 = keystore.from_xprv('Uprv9BEixD3As2LK5h6G2SNT3cTqbZpsWYPceKTSuVAm1yuSybxSvQz2MV1o8cHTtctQmj4HAenb3eh5YJv4YRZjv35i8fofVnNbs4Dd2B4i5je')
|
||||
self.assertTrue(isinstance(ks1, keystore.BIP32_KeyStore))
|
||||
self.assertEqual(ks1.xpub, 'Upub5QE5Mia4hPtcJBAj8TuTQkQa9bfMv17U1YP3hsaNaKSRrQHbTxJGuHLGyv3MbKZixuPyjfXGUdbTjE4KwyFcX8YD7PX5ybTDbP11UT8UpZR')
|
||||
|
||||
# bip39 seed: square page wood spy oil story rebel give milk screen slide shuffle
|
||||
# der: m/49'/1'/0'
|
||||
ks2 = keystore.from_xpub('Upub5QRzUGRJuWJe5MxGzwgQAeyJjzcdGTXkkq77w6EfBkCyf5iWppSaZ4caY2MgWcU9LP4a4uE5apUFN4wLoENoe9tpu26mrUxeGsH84dN3JFh')
|
||||
WalletIntegrityHelper.check_xpub_keystore_sanity(self, ks2)
|
||||
self.assertTrue(isinstance(ks2, keystore.BIP32_KeyStore))
|
||||
|
||||
w = WalletIntegrityHelper.create_multisig_wallet(ks1, ks2)
|
||||
self.assertEqual(w.txin_type, 'p2wsh-p2sh')
|
||||
|
||||
self.assertEqual(w.get_receiving_addresses()[0], '2MzsfTfTGomPRne6TkctMmoDj6LwmVkDrMt')
|
||||
self.assertEqual(w.get_change_addresses()[0], '2NFp9w8tbYYP9Ze2xQpeYBJQjx3gbXymHX7')
|
||||
|
|
|
@ -229,10 +229,10 @@ opcodes = Enumeration("Opcodes", [
|
|||
"OP_WITHIN", "OP_RIPEMD160", "OP_SHA1", "OP_SHA256", "OP_HASH160",
|
||||
"OP_HASH256", "OP_CODESEPARATOR", "OP_CHECKSIG", "OP_CHECKSIGVERIFY", "OP_CHECKMULTISIG",
|
||||
"OP_CHECKMULTISIGVERIFY",
|
||||
("OP_SINGLEBYTE_END", 0xF0),
|
||||
("OP_DOUBLEBYTE_BEGIN", 0xF000),
|
||||
"OP_PUBKEY", "OP_PUBKEYHASH",
|
||||
("OP_INVALIDOPCODE", 0xFFFF),
|
||||
("OP_NOP1", 0xB0),
|
||||
("OP_CHECKLOCKTIMEVERIFY", 0xB1), ("OP_CHECKSEQUENCEVERIFY", 0xB2),
|
||||
"OP_NOP4", "OP_NOP5", "OP_NOP6", "OP_NOP7", "OP_NOP8", "OP_NOP9", "OP_NOP10",
|
||||
("OP_INVALIDOPCODE", 0xFF),
|
||||
])
|
||||
|
||||
|
||||
|
@ -242,10 +242,6 @@ def script_GetOp(_bytes):
|
|||
vch = None
|
||||
opcode = _bytes[i]
|
||||
i += 1
|
||||
if opcode >= opcodes.OP_SINGLEBYTE_END:
|
||||
opcode <<= 8
|
||||
opcode |= _bytes[i]
|
||||
i += 1
|
||||
|
||||
if opcode <= opcodes.OP_PUSHDATA4:
|
||||
nSize = opcode
|
||||
|
@ -402,7 +398,8 @@ def parse_redeemScript(s):
|
|||
redeemScript = multisig_script(pubkeys, m)
|
||||
return m, n, x_pubkeys, pubkeys, redeemScript
|
||||
|
||||
def get_address_from_output_script(_bytes):
|
||||
|
||||
def get_address_from_output_script(_bytes, *, net=None):
|
||||
decoded = [x for x in script_GetOp(_bytes)]
|
||||
|
||||
# The Genesis Block, self-payments, and pay-by-IP-address payments look like:
|
||||
|
@ -415,17 +412,19 @@ def get_address_from_output_script(_bytes):
|
|||
# DUP HASH160 20 BYTES:... EQUALVERIFY CHECKSIG
|
||||
match = [ opcodes.OP_DUP, opcodes.OP_HASH160, opcodes.OP_PUSHDATA4, opcodes.OP_EQUALVERIFY, opcodes.OP_CHECKSIG ]
|
||||
if match_decoded(decoded, match):
|
||||
return TYPE_ADDRESS, hash160_to_p2pkh(decoded[2][1])
|
||||
return TYPE_ADDRESS, hash160_to_p2pkh(decoded[2][1], net=net)
|
||||
|
||||
# p2sh
|
||||
match = [ opcodes.OP_HASH160, opcodes.OP_PUSHDATA4, opcodes.OP_EQUAL ]
|
||||
if match_decoded(decoded, match):
|
||||
return TYPE_ADDRESS, hash160_to_p2sh(decoded[1][1])
|
||||
return TYPE_ADDRESS, hash160_to_p2sh(decoded[1][1], net=net)
|
||||
|
||||
# segwit address
|
||||
match = [ opcodes.OP_0, opcodes.OP_PUSHDATA4 ]
|
||||
if match_decoded(decoded, match):
|
||||
return TYPE_ADDRESS, hash_to_segwit_addr(decoded[1][1])
|
||||
possible_witness_versions = [opcodes.OP_0] + list(range(opcodes.OP_1, opcodes.OP_16 + 1))
|
||||
for witver, opcode in enumerate(possible_witness_versions):
|
||||
match = [ opcode, opcodes.OP_PUSHDATA4 ]
|
||||
if match_decoded(decoded, match):
|
||||
return TYPE_ADDRESS, hash_to_segwit_addr(decoded[1][1], witver=witver, net=net)
|
||||
|
||||
return TYPE_SCRIPT, bh2u(_bytes)
|
||||
|
||||
|
@ -439,16 +438,16 @@ def parse_input(vds):
|
|||
d['prevout_hash'] = prevout_hash
|
||||
d['prevout_n'] = prevout_n
|
||||
d['sequence'] = sequence
|
||||
d['x_pubkeys'] = []
|
||||
d['pubkeys'] = []
|
||||
d['signatures'] = {}
|
||||
d['address'] = None
|
||||
d['num_sig'] = 0
|
||||
if prevout_hash == '00'*32:
|
||||
d['type'] = 'coinbase'
|
||||
d['scriptSig'] = bh2u(scriptSig)
|
||||
else:
|
||||
d['x_pubkeys'] = []
|
||||
d['pubkeys'] = []
|
||||
d['signatures'] = {}
|
||||
d['address'] = None
|
||||
d['type'] = 'unknown'
|
||||
d['num_sig'] = 0
|
||||
if scriptSig:
|
||||
d['scriptSig'] = bh2u(scriptSig)
|
||||
try:
|
||||
|
@ -607,6 +606,8 @@ class Transaction:
|
|||
@classmethod
|
||||
def get_sorted_pubkeys(self, txin):
|
||||
# sort pubkeys and x_pubkeys, using the order of pubkeys
|
||||
if txin['type'] == 'coinbase':
|
||||
return [], []
|
||||
x_pubkeys = txin['x_pubkeys']
|
||||
pubkeys = txin.get('pubkeys')
|
||||
if pubkeys is None:
|
||||
|
@ -707,6 +708,8 @@ class Transaction:
|
|||
def get_siglist(self, txin, estimate_size=False):
|
||||
# if we have enough signatures, we use the actual pubkeys
|
||||
# otherwise, use extended pubkeys (with bip32 derivation)
|
||||
if txin['type'] == 'coinbase':
|
||||
return [], []
|
||||
num_sig = txin.get('num_sig', 1)
|
||||
if estimate_size:
|
||||
pubkey_size = self.estimate_pubkey_size_for_txin(txin)
|
||||
|
@ -728,10 +731,12 @@ class Transaction:
|
|||
|
||||
@classmethod
|
||||
def serialize_witness(self, txin, estimate_size=False):
|
||||
add_w = lambda x: var_int(len(x)//2) + x
|
||||
if not self.is_segwit_input(txin):
|
||||
return '00'
|
||||
if txin['type'] == 'coinbase':
|
||||
return txin['witness']
|
||||
pubkeys, sig_list = self.get_siglist(txin, estimate_size)
|
||||
add_w = lambda x: var_int(len(x) // 2) + x
|
||||
if txin['type'] in ['p2wpkh', 'p2wpkh-p2sh']:
|
||||
witness = var_int(2) + add_w(sig_list[0]) + add_w(pubkeys[0])
|
||||
elif txin['type'] in ['p2wsh', 'p2wsh-p2sh']:
|
||||
|
@ -790,7 +795,9 @@ class Transaction:
|
|||
return script
|
||||
|
||||
@classmethod
|
||||
def is_txin_complete(self, txin):
|
||||
def is_txin_complete(cls, txin):
|
||||
if txin['type'] == 'coinbase':
|
||||
return True
|
||||
num_sig = txin.get('num_sig', 1)
|
||||
x_signatures = txin['signatures']
|
||||
signatures = list(filter(None, x_signatures))
|
||||
|
|
16
lib/util.py
16
lib/util.py
|
@ -84,6 +84,12 @@ class TimeoutException(Exception):
|
|||
return self.message
|
||||
|
||||
|
||||
class WalletFileException(Exception): pass
|
||||
|
||||
|
||||
class BitcoinException(Exception): pass
|
||||
|
||||
|
||||
# Throw this exception to unwind the stack like when an error occurs.
|
||||
# However unlike other exceptions the user won't be informed.
|
||||
class UserCancelled(Exception):
|
||||
|
@ -412,7 +418,7 @@ def format_satoshis(x, is_diff=False, num_zeros = 0, decimal_point = 8, whitespa
|
|||
return 'unknown'
|
||||
x = int(x) # Some callers pass Decimal
|
||||
scale_factor = pow (10, decimal_point)
|
||||
integer_part = "{:n}".format(int(abs(x) / scale_factor))
|
||||
integer_part = "{:d}".format(int(abs(x) / scale_factor))
|
||||
if x < 0:
|
||||
integer_part = '-' + integer_part
|
||||
elif is_diff:
|
||||
|
@ -730,10 +736,6 @@ class SocketPipe:
|
|||
print_error("SSLError:", e)
|
||||
time.sleep(0.1)
|
||||
continue
|
||||
except OSError as e:
|
||||
print_error("OSError", e)
|
||||
time.sleep(0.1)
|
||||
continue
|
||||
|
||||
|
||||
class QueuePipe:
|
||||
|
@ -804,7 +806,7 @@ def versiontuple(v):
|
|||
|
||||
def import_meta(path, validater, load_meta):
|
||||
try:
|
||||
with open(path, 'r') as f:
|
||||
with open(path, 'r', encoding='utf-8') as f:
|
||||
d = validater(json.loads(f.read()))
|
||||
load_meta(d)
|
||||
#backwards compatibility for JSONDecodeError
|
||||
|
@ -818,7 +820,7 @@ def import_meta(path, validater, load_meta):
|
|||
|
||||
def export_meta(meta, fileName):
|
||||
try:
|
||||
with open(fileName, 'w+') as f:
|
||||
with open(fileName, 'w+', encoding='utf-8') as f:
|
||||
json.dump(meta, f, indent=4, sort_keys=True)
|
||||
except (IOError, os.error) as e:
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
ELECTRUM_VERSION = '3.1' # version of the client package
|
||||
ELECTRUM_VERSION = '3.1.2' # version of the client package
|
||||
PROTOCOL_VERSION = '1.2' # protocol version requested
|
||||
|
||||
# The hash of the mnemonic seed must begin with this
|
||||
|
|
152
lib/wallet.py
152
lib/wallet.py
|
@ -39,12 +39,14 @@ from functools import partial
|
|||
from collections import defaultdict
|
||||
from numbers import Number
|
||||
from decimal import Decimal
|
||||
import itertools
|
||||
|
||||
import sys
|
||||
|
||||
from .i18n import _
|
||||
from .util import (NotEnoughFunds, PrintError, UserCancelled, profiler,
|
||||
format_satoshis, NoDynamicFeeEstimates, TimeoutException)
|
||||
format_satoshis, NoDynamicFeeEstimates, TimeoutException,
|
||||
WalletFileException, BitcoinException)
|
||||
|
||||
from .bitcoin import *
|
||||
from .version import *
|
||||
|
@ -131,6 +133,7 @@ def sweep_preparations(privkeys, network, imax=100):
|
|||
find_utxos_for_privkey('p2pk', privkey, compressed)
|
||||
if not inputs:
|
||||
raise BaseException(_('No inputs found. (Note that inputs need to be confirmed)'))
|
||||
# FIXME actually inputs need not be confirmed now, see https://github.com/kyuupichan/electrumx/issues/365
|
||||
return inputs, keypairs
|
||||
|
||||
|
||||
|
@ -186,7 +189,12 @@ class Abstract_Wallet(PrintError):
|
|||
self.synchronizer = None
|
||||
self.verifier = None
|
||||
|
||||
self.gap_limit_for_change = 6 # constant
|
||||
self.gap_limit_for_change = 6 # constant
|
||||
|
||||
# locks: if you need to take multiple ones, acquire them in the order they are defined here!
|
||||
self.lock = threading.RLock()
|
||||
self.transaction_lock = threading.RLock()
|
||||
|
||||
# saved fields
|
||||
self.use_change = storage.get('use_change', True)
|
||||
self.multiple_change = storage.get('multiple_change', False)
|
||||
|
@ -200,6 +208,8 @@ class Abstract_Wallet(PrintError):
|
|||
self.load_transactions()
|
||||
self.build_spent_outpoints()
|
||||
|
||||
self.test_addresses_sanity()
|
||||
|
||||
# load requests
|
||||
self.receive_requests = self.storage.get('payment_requests', {})
|
||||
|
||||
|
@ -215,10 +225,6 @@ class Abstract_Wallet(PrintError):
|
|||
# wallet.up_to_date is true when the wallet is synchronized (stronger requirement)
|
||||
self.up_to_date = False
|
||||
|
||||
# locks: if you need to take multiple ones, acquire them in the order they are defined here!
|
||||
self.lock = threading.RLock()
|
||||
self.transaction_lock = threading.RLock()
|
||||
|
||||
self.check_history()
|
||||
|
||||
# save wallet type the first time
|
||||
|
@ -229,6 +235,8 @@ class Abstract_Wallet(PrintError):
|
|||
self.invoices = InvoiceStore(self.storage)
|
||||
self.contacts = Contacts(self.storage)
|
||||
|
||||
self.coin_price_cache = {}
|
||||
|
||||
|
||||
def diagnostic_name(self):
|
||||
return self.basename()
|
||||
|
@ -247,6 +255,7 @@ class Abstract_Wallet(PrintError):
|
|||
self.pruned_txo = self.storage.get('pruned_txo', {})
|
||||
tx_list = self.storage.get('transactions', {})
|
||||
self.transactions = {}
|
||||
self._history_local = {} # address -> set(txid)
|
||||
for tx_hash, raw in tx_list.items():
|
||||
tx = Transaction(raw)
|
||||
self.transactions[tx_hash] = tx
|
||||
|
@ -254,6 +263,8 @@ class Abstract_Wallet(PrintError):
|
|||
and (tx_hash not in self.pruned_txo.values()):
|
||||
self.print_error("removing unreferenced tx", tx_hash)
|
||||
self.transactions.pop(tx_hash)
|
||||
else:
|
||||
self._add_tx_to_local_history(tx_hash)
|
||||
|
||||
@profiler
|
||||
def save_transactions(self, write=False):
|
||||
|
@ -327,6 +338,12 @@ class Abstract_Wallet(PrintError):
|
|||
self.receiving_addresses = d.get('receiving', [])
|
||||
self.change_addresses = d.get('change', [])
|
||||
|
||||
def test_addresses_sanity(self):
|
||||
addrs = self.get_receiving_addresses()
|
||||
if len(addrs) > 0:
|
||||
if not bitcoin.is_address(addrs[0]):
|
||||
raise WalletFileException('The addresses in this wallet are not bitcoin addresses.')
|
||||
|
||||
def synchronize(self):
|
||||
pass
|
||||
|
||||
|
@ -657,8 +674,9 @@ class Abstract_Wallet(PrintError):
|
|||
def get_addr_balance(self, address):
|
||||
received, sent = self.get_addr_io(address)
|
||||
c = u = x = 0
|
||||
local_height = self.get_local_height()
|
||||
for txo, (tx_height, v, is_cb) in received.items():
|
||||
if is_cb and tx_height + COINBASE_MATURITY > self.get_local_height():
|
||||
if is_cb and tx_height + COINBASE_MATURITY > local_height:
|
||||
x += v
|
||||
elif tx_height > 0:
|
||||
c += v
|
||||
|
@ -720,12 +738,30 @@ class Abstract_Wallet(PrintError):
|
|||
# we need self.transaction_lock but get_tx_height will take self.lock
|
||||
# so we need to take that too here, to enforce order of locks
|
||||
with self.lock, self.transaction_lock:
|
||||
for tx_hash in self.transactions:
|
||||
if addr in self.txi.get(tx_hash, []) or addr in self.txo.get(tx_hash, []):
|
||||
tx_height = self.get_tx_height(tx_hash)[0]
|
||||
h.append((tx_hash, tx_height))
|
||||
related_txns = self._history_local.get(addr, set())
|
||||
for tx_hash in related_txns:
|
||||
tx_height = self.get_tx_height(tx_hash)[0]
|
||||
h.append((tx_hash, tx_height))
|
||||
return h
|
||||
|
||||
def _add_tx_to_local_history(self, txid):
|
||||
with self.transaction_lock:
|
||||
for addr in itertools.chain(self.txi.get(txid, []), self.txo.get(txid, [])):
|
||||
cur_hist = self._history_local.get(addr, set())
|
||||
cur_hist.add(txid)
|
||||
self._history_local[addr] = cur_hist
|
||||
|
||||
def _remove_tx_from_local_history(self, txid):
|
||||
with self.transaction_lock:
|
||||
for addr in itertools.chain(self.txi.get(txid, []), self.txo.get(txid, [])):
|
||||
cur_hist = self._history_local.get(addr, set())
|
||||
try:
|
||||
cur_hist.remove(txid)
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
self._history_local[addr] = cur_hist
|
||||
|
||||
def get_txin_address(self, txi):
|
||||
addr = txi.get('address')
|
||||
if addr != "(pubkey)":
|
||||
|
@ -776,6 +812,9 @@ class Abstract_Wallet(PrintError):
|
|||
return conflicting_txns
|
||||
|
||||
def add_transaction(self, tx_hash, tx):
|
||||
assert tx_hash, tx_hash
|
||||
assert tx, tx
|
||||
assert tx.is_complete()
|
||||
# we need self.transaction_lock but get_tx_height will take self.lock
|
||||
# so we need to take that too here, to enforce order of locks
|
||||
with self.lock, self.transaction_lock:
|
||||
|
@ -861,6 +900,9 @@ class Abstract_Wallet(PrintError):
|
|||
if dd.get(addr) is None:
|
||||
dd[addr] = []
|
||||
dd[addr].append((ser, v))
|
||||
self._add_tx_to_local_history(next_tx)
|
||||
# add to local history
|
||||
self._add_tx_to_local_history(tx_hash)
|
||||
# save
|
||||
self.transactions[tx_hash] = tx
|
||||
return True
|
||||
|
@ -877,9 +919,11 @@ class Abstract_Wallet(PrintError):
|
|||
# undo spent_outpoints that are in pruned_txo
|
||||
for ser, hh in list(self.pruned_txo.items()):
|
||||
if hh == tx_hash:
|
||||
self.spent_outpoints.pop(ser)
|
||||
self.spent_outpoints.pop(ser, None)
|
||||
self.pruned_txo.pop(ser)
|
||||
|
||||
self._remove_tx_from_local_history(tx_hash)
|
||||
|
||||
# add tx to pruned_txo, and undo the txi addition
|
||||
for next_tx, dd in self.txi.items():
|
||||
for addr, l in list(dd.items()):
|
||||
|
@ -987,11 +1031,11 @@ class Abstract_Wallet(PrintError):
|
|||
def get_full_history(self, domain=None, from_timestamp=None, to_timestamp=None, fx=None, show_addresses=False):
|
||||
from .util import timestamp_to_datetime, Satoshis, Fiat
|
||||
out = []
|
||||
capital_gains = 0
|
||||
income = 0
|
||||
expenditures = 0
|
||||
fiat_income = 0
|
||||
fiat_expenditures = 0
|
||||
capital_gains = Decimal(0)
|
||||
fiat_income = Decimal(0)
|
||||
fiat_expenditures = Decimal(0)
|
||||
h = self.get_history(domain)
|
||||
for tx_hash, height, conf, timestamp, value, balance in h:
|
||||
if from_timestamp and (timestamp or time.time()) < from_timestamp:
|
||||
|
@ -1032,7 +1076,7 @@ class Abstract_Wallet(PrintError):
|
|||
else:
|
||||
income += value
|
||||
# fiat computations
|
||||
if fx is not None:
|
||||
if fx and fx.is_enabled():
|
||||
date = timestamp_to_datetime(timestamp)
|
||||
fiat_value = self.get_fiat_value(tx_hash, fx.ccy)
|
||||
fiat_default = fiat_value is None
|
||||
|
@ -1069,7 +1113,7 @@ class Abstract_Wallet(PrintError):
|
|||
'income': Satoshis(income),
|
||||
'expenditures': Satoshis(expenditures)
|
||||
}
|
||||
if fx:
|
||||
if fx and fx.is_enabled():
|
||||
unrealized = self.unrealized_gains(domain, fx.timestamp_rate, fx.ccy)
|
||||
summary['capital_gains'] = Fiat(capital_gains, fx.ccy)
|
||||
summary['fiat_income'] = Fiat(fiat_income, fx.ccy)
|
||||
|
@ -1153,7 +1197,7 @@ class Abstract_Wallet(PrintError):
|
|||
_type, data, value = o
|
||||
if _type == TYPE_ADDRESS:
|
||||
if not is_address(data):
|
||||
raise BaseException("Invalid bitcoin address:" + data)
|
||||
raise BaseException("Invalid bitcoin address: {}".format(data))
|
||||
if value == '!':
|
||||
if i_max is not None:
|
||||
raise BaseException("More than one output set to spend max")
|
||||
|
@ -1326,7 +1370,7 @@ class Abstract_Wallet(PrintError):
|
|||
|
||||
def bump_fee(self, tx, delta):
|
||||
if tx.is_final():
|
||||
raise BaseException(_("Cannot bump fee: transaction is final"))
|
||||
raise BaseException(_('Cannot bump fee') + ': ' + _('transaction is final'))
|
||||
inputs = copy.deepcopy(tx.inputs())
|
||||
outputs = copy.deepcopy(tx.outputs())
|
||||
for txin in inputs:
|
||||
|
@ -1357,7 +1401,7 @@ class Abstract_Wallet(PrintError):
|
|||
if delta > 0:
|
||||
continue
|
||||
if delta > 0:
|
||||
raise BaseException(_('Cannot bump fee: could not find suitable outputs'))
|
||||
raise BaseException(_('Cannot bump fee') + ': ' + _('could not find suitable outputs'))
|
||||
locktime = self.get_local_height()
|
||||
tx_new = Transaction.from_io(inputs, outputs, locktime=locktime)
|
||||
tx_new.BIP_LI01_sort()
|
||||
|
@ -1429,7 +1473,7 @@ class Abstract_Wallet(PrintError):
|
|||
xpubs = self.get_master_public_keys()
|
||||
for txout in tx.outputs():
|
||||
_type, addr, amount = txout
|
||||
if self.is_change(addr):
|
||||
if self.is_mine(addr):
|
||||
index = self.get_address_index(addr)
|
||||
pubkeys = self.get_public_keys(addr)
|
||||
# sort xpubs using the order of pubkeys
|
||||
|
@ -1443,8 +1487,8 @@ class Abstract_Wallet(PrintError):
|
|||
# hardware wallets require extra info
|
||||
if any([(isinstance(k, Hardware_KeyStore) and k.can_sign(tx)) for k in self.get_keystores()]):
|
||||
self.add_hw_info(tx)
|
||||
# sign
|
||||
for k in self.get_keystores():
|
||||
# sign. start with ready keystores.
|
||||
for k in sorted(self.get_keystores(), key=lambda ks: ks.ready_to_sign(), reverse=True):
|
||||
try:
|
||||
if k.can_sign(tx):
|
||||
k.sign_transaction(tx, password)
|
||||
|
@ -1516,7 +1560,10 @@ class Abstract_Wallet(PrintError):
|
|||
baseurl = 'file://' + rdir
|
||||
rewrite = config.get('url_rewrite')
|
||||
if rewrite:
|
||||
baseurl = baseurl.replace(*rewrite)
|
||||
try:
|
||||
baseurl = baseurl.replace(*rewrite)
|
||||
except BaseException as e:
|
||||
self.print_stderr('Invalid config setting for "url_rewrite". err:', e)
|
||||
out['request_url'] = os.path.join(baseurl, 'req', key[0], key[1], key, key)
|
||||
out['URI'] += '&r=' + out['request_url']
|
||||
out['index_url'] = os.path.join(baseurl, 'index.html') + '?id=' + key
|
||||
|
@ -1575,6 +1622,11 @@ class Abstract_Wallet(PrintError):
|
|||
|
||||
def add_payment_request(self, req, config):
|
||||
addr = req['address']
|
||||
if not bitcoin.is_address(addr):
|
||||
raise Exception(_('Invalid Bitcoin address.'))
|
||||
if not self.is_mine(addr):
|
||||
raise Exception(_('Address not in wallet.'))
|
||||
|
||||
amount = req.get('amount')
|
||||
message = req.get('memo')
|
||||
self.receive_requests[addr] = req
|
||||
|
@ -1596,7 +1648,7 @@ class Abstract_Wallet(PrintError):
|
|||
f.write(pr.SerializeToString())
|
||||
# reload
|
||||
req = self.get_payment_request(addr, config)
|
||||
with open(os.path.join(path, key + '.json'), 'w') as f:
|
||||
with open(os.path.join(path, key + '.json'), 'w', encoding='utf-8') as f:
|
||||
f.write(json.dumps(req))
|
||||
return req
|
||||
|
||||
|
@ -1615,13 +1667,14 @@ class Abstract_Wallet(PrintError):
|
|||
return True
|
||||
|
||||
def get_sorted_requests(self, config):
|
||||
def f(x):
|
||||
def f(addr):
|
||||
try:
|
||||
addr = x.get('address')
|
||||
return self.get_address_index(addr) or addr
|
||||
return self.get_address_index(addr)
|
||||
except:
|
||||
return addr
|
||||
return sorted(map(lambda x: self.get_payment_request(x, config), self.receive_requests.keys()), key=f)
|
||||
return
|
||||
keys = map(lambda x: (f(x), x), self.receive_requests.keys())
|
||||
sorted_keys = sorted(filter(lambda x: x[0] is not None, keys))
|
||||
return [self.get_payment_request(x[1], config) for x in sorted_keys]
|
||||
|
||||
def get_fingerprint(self):
|
||||
raise NotImplementedError()
|
||||
|
@ -1724,11 +1777,12 @@ class Abstract_Wallet(PrintError):
|
|||
def txin_value(self, txin):
|
||||
txid = txin['prevout_hash']
|
||||
prev_n = txin['prevout_n']
|
||||
for address, d in self.txo[txid].items():
|
||||
for address, d in self.txo.get(txid, {}).items():
|
||||
for n, v, cb in d:
|
||||
if n == prev_n:
|
||||
return v
|
||||
raise BaseException('unknown txin value')
|
||||
# may occur if wallet is not synchronized
|
||||
return None
|
||||
|
||||
def price_at_timestamp(self, txid, price_func):
|
||||
height, conf, timestamp = self.get_tx_height(txid)
|
||||
|
@ -1757,8 +1811,16 @@ class Abstract_Wallet(PrintError):
|
|||
Acquisition price of a coin.
|
||||
This assumes that either all inputs are mine, or no input is mine.
|
||||
"""
|
||||
if txin_value is None:
|
||||
return Decimal('NaN')
|
||||
cache_key = "{}:{}:{}".format(str(txid), str(ccy), str(txin_value))
|
||||
result = self.coin_price_cache.get(cache_key, None)
|
||||
if result is not None:
|
||||
return result
|
||||
if self.txi.get(txid, {}) != {}:
|
||||
return self.average_price(txid, price_func, ccy) * txin_value/Decimal(COIN)
|
||||
result = self.average_price(txid, price_func, ccy) * txin_value/Decimal(COIN)
|
||||
self.coin_price_cache[cache_key] = result
|
||||
return result
|
||||
else:
|
||||
fiat_value = self.get_fiat_value(txid, ccy)
|
||||
if fiat_value is not None:
|
||||
|
@ -1767,6 +1829,7 @@ class Abstract_Wallet(PrintError):
|
|||
p = self.price_at_timestamp(txid, price_func)
|
||||
return p * txin_value/Decimal(COIN)
|
||||
|
||||
|
||||
class Simple_Wallet(Abstract_Wallet):
|
||||
# wallet with a single keystore
|
||||
|
||||
|
@ -1903,8 +1966,18 @@ class Imported_Wallet(Simple_Wallet):
|
|||
pubkey = self.get_public_key(address)
|
||||
self.addresses.pop(address)
|
||||
if pubkey:
|
||||
self.keystore.delete_imported_key(pubkey)
|
||||
self.save_keystore()
|
||||
# delete key iff no other address uses it (e.g. p2pkh and p2wpkh for same key)
|
||||
for txin_type in bitcoin.SCRIPT_TYPES.keys():
|
||||
try:
|
||||
addr2 = bitcoin.pubkey_to_address(txin_type, pubkey)
|
||||
except NotImplementedError:
|
||||
pass
|
||||
else:
|
||||
if addr2 in self.addresses:
|
||||
break
|
||||
else:
|
||||
self.keystore.delete_imported_key(pubkey)
|
||||
self.save_keystore()
|
||||
self.storage.put('addresses', self.addresses)
|
||||
|
||||
self.storage.write()
|
||||
|
@ -1919,14 +1992,15 @@ class Imported_Wallet(Simple_Wallet):
|
|||
try:
|
||||
txin_type, pubkey = self.keystore.import_privkey(sec, pw)
|
||||
except Exception:
|
||||
raise BaseException('Invalid private key', sec)
|
||||
neutered_privkey = str(sec)[:3] + '..' + str(sec)[-2:]
|
||||
raise BitcoinException('Invalid private key: {}'.format(neutered_privkey))
|
||||
if txin_type in ['p2pkh', 'p2wpkh', 'p2wpkh-p2sh']:
|
||||
if redeem_script is not None:
|
||||
raise BaseException('Cannot use redeem script with', txin_type, sec)
|
||||
raise BitcoinException('Cannot use redeem script with script type {}'.format(txin_type))
|
||||
addr = bitcoin.pubkey_to_address(txin_type, pubkey)
|
||||
elif txin_type in ['p2sh', 'p2wsh', 'p2wsh-p2sh']:
|
||||
if redeem_script is None:
|
||||
raise BaseException('Redeem script required for', txin_type, sec)
|
||||
raise BitcoinException('Redeem script required for script type {}'.format(txin_type))
|
||||
addr = bitcoin.redeem_script_to_address(txin_type, redeem_script)
|
||||
else:
|
||||
raise NotImplementedError(txin_type)
|
||||
|
@ -2280,5 +2354,5 @@ class Wallet(object):
|
|||
return Multisig_Wallet
|
||||
if wallet_type in wallet_constructors:
|
||||
return wallet_constructors[wallet_type]
|
||||
raise RuntimeError("Unknown wallet type: " + wallet_type)
|
||||
raise RuntimeError("Unknown wallet type: " + str(wallet_type))
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@ class WsClientThread(util.DaemonThread):
|
|||
# read json file
|
||||
rdir = self.config.get('requests_dir')
|
||||
n = os.path.join(rdir, 'req', request_id[0], request_id[1], request_id, request_id + '.json')
|
||||
with open(n) as f:
|
||||
with open(n, encoding='utf-8') as f:
|
||||
s = f.read()
|
||||
d = json.loads(s)
|
||||
addr = d.get('address')
|
||||
|
|
|
@ -284,7 +284,7 @@ class X509(object):
|
|||
return self.AKI if self.AKI else repr(self.issuer)
|
||||
|
||||
def get_common_name(self):
|
||||
return self.subject.get('2.5.4.3', 'unknown').decode()
|
||||
return self.subject.get('2.5.4.3', b'unknown').decode()
|
||||
|
||||
def get_signature(self):
|
||||
return self.cert_sig_algo, self.signature, self.data
|
||||
|
@ -313,7 +313,7 @@ def load_certificates(ca_path):
|
|||
ca_list = {}
|
||||
ca_keyID = {}
|
||||
# ca_path = '/tmp/tmp.txt'
|
||||
with open(ca_path, 'r') as f:
|
||||
with open(ca_path, 'r', encoding='utf-8') as f:
|
||||
s = f.read()
|
||||
bList = pem.dePemList(s, "CERTIFICATE")
|
||||
for b in bList:
|
||||
|
|
|
@ -43,9 +43,7 @@ import sys
|
|||
import traceback
|
||||
|
||||
|
||||
PORT = 12344
|
||||
HOST = 'cosigner.electrum.org'
|
||||
server = ServerProxy('http://%s:%d'%(HOST,PORT), allow_none=True)
|
||||
server = ServerProxy('https://cosigner.electrum.org/', allow_none=True)
|
||||
|
||||
|
||||
class Listener(util.DaemonThread):
|
||||
|
@ -175,7 +173,8 @@ class Plugin(BasePlugin):
|
|||
for window, xpub, K, _hash in self.cosigner_list:
|
||||
if not self.cosigner_can_sign(tx, xpub):
|
||||
continue
|
||||
message = bitcoin.encrypt_message(bfh(tx.raw), bh2u(K)).decode('ascii')
|
||||
raw_tx_bytes = bfh(str(tx))
|
||||
message = bitcoin.encrypt_message(raw_tx_bytes, bh2u(K)).decode('ascii')
|
||||
try:
|
||||
server.put(_hash, message)
|
||||
except Exception as e:
|
||||
|
|
|
@ -82,6 +82,13 @@ class DigitalBitbox_Client():
|
|||
def is_paired(self):
|
||||
return self.password is not None
|
||||
|
||||
def has_usable_connection_with_device(self):
|
||||
try:
|
||||
self.dbb_has_password()
|
||||
except BaseException:
|
||||
return False
|
||||
return True
|
||||
|
||||
def _get_xpub(self, bip32_path):
|
||||
if self.check_device_dialog():
|
||||
return self.hid_send_encrypt(b'{"xpub": "%s"}' % bip32_path.encode('utf8'))
|
||||
|
@ -106,7 +113,7 @@ class DigitalBitbox_Client():
|
|||
def dbb_has_password(self):
|
||||
reply = self.hid_send_plain(b'{"ping":""}')
|
||||
if 'ping' not in reply:
|
||||
raise Exception('Device communication error. Please unplug and replug your Digital Bitbox.')
|
||||
raise Exception(_('Device communication error. Please unplug and replug your Digital Bitbox.'))
|
||||
if reply['ping'] == 'password':
|
||||
return True
|
||||
return False
|
||||
|
@ -124,9 +131,11 @@ class DigitalBitbox_Client():
|
|||
if password is None:
|
||||
return None
|
||||
if len(password) < 4:
|
||||
msg = _("Password must have at least 4 characters.\r\n\r\nEnter password:")
|
||||
msg = _("Password must have at least 4 characters.") \
|
||||
+ "\n\n" + _("Enter password:")
|
||||
elif len(password) > 64:
|
||||
msg = _("Password must have less than 64 characters.\r\n\r\nEnter password:")
|
||||
msg = _("Password must have less than 64 characters.") \
|
||||
+ "\n\n" + _("Enter password:")
|
||||
else:
|
||||
return password.encode('utf8')
|
||||
|
||||
|
@ -137,9 +146,11 @@ class DigitalBitbox_Client():
|
|||
if password is None:
|
||||
return False
|
||||
if len(password) < 4:
|
||||
msg = _("Password must have at least 4 characters.\r\n\r\nEnter password:")
|
||||
msg = _("Password must have at least 4 characters.") + \
|
||||
"\n\n" + _("Enter password:")
|
||||
elif len(password) > 64:
|
||||
msg = _("Password must have less than 64 characters.\r\n\r\nEnter password:")
|
||||
msg = _("Password must have less than 64 characters.") + \
|
||||
"\n\n" + _("Enter password:")
|
||||
else:
|
||||
self.password = password.encode('utf8')
|
||||
return True
|
||||
|
@ -150,10 +161,11 @@ class DigitalBitbox_Client():
|
|||
if self.password is None and not self.dbb_has_password():
|
||||
if not self.setupRunning:
|
||||
return False # A fresh device cannot connect to an existing wallet
|
||||
msg = _("An uninitialized Digital Bitbox is detected. " \
|
||||
"Enter a new password below.\r\n\r\n REMEMBER THE PASSWORD!\r\n\r\n" \
|
||||
"You cannot access your coins or a backup without the password.\r\n" \
|
||||
"A backup is saved automatically when generating a new wallet.")
|
||||
msg = _("An uninitialized Digital Bitbox is detected.") + " " + \
|
||||
_("Enter a new password below.") + "\n\n" + \
|
||||
_("REMEMBER THE PASSWORD!") + "\n\n" + \
|
||||
_("You cannot access your coins or a backup without the password.") + "\n" + \
|
||||
_("A backup is saved automatically when generating a new wallet.")
|
||||
if self.password_dialog(msg):
|
||||
reply = self.hid_send_plain(b'{"password":"' + self.password + b'"}')
|
||||
else:
|
||||
|
@ -163,19 +175,19 @@ class DigitalBitbox_Client():
|
|||
msg = _("Enter your Digital Bitbox password:")
|
||||
while self.password is None:
|
||||
if not self.password_dialog(msg):
|
||||
return False
|
||||
raise UserCancelled()
|
||||
reply = self.hid_send_encrypt(b'{"led":"blink"}')
|
||||
if 'error' in reply:
|
||||
self.password = None
|
||||
if reply['error']['code'] == 109:
|
||||
msg = _("Incorrect password entered.\r\n\r\n" \
|
||||
+ reply['error']['message'] + "\r\n\r\n" \
|
||||
"Enter your Digital Bitbox password:")
|
||||
msg = _("Incorrect password entered.") + "\n\n" + \
|
||||
reply['error']['message'] + "\n\n" + \
|
||||
_("Enter your Digital Bitbox password:")
|
||||
else:
|
||||
# Should never occur
|
||||
msg = _("Unexpected error occurred.\r\n\r\n" \
|
||||
+ reply['error']['message'] + "\r\n\r\n" \
|
||||
"Enter your Digital Bitbox password:")
|
||||
msg = _("Unexpected error occurred.") + "\n\n" + \
|
||||
reply['error']['message'] + "\n\n" + \
|
||||
_("Enter your Digital Bitbox password:")
|
||||
|
||||
# Initialize device if not yet initialized
|
||||
if not self.setupRunning:
|
||||
|
@ -191,7 +203,7 @@ class DigitalBitbox_Client():
|
|||
|
||||
|
||||
def recover_or_erase_dialog(self):
|
||||
msg = _("The Digital Bitbox is already seeded. Choose an option:\n")
|
||||
msg = _("The Digital Bitbox is already seeded. Choose an option:") + "\n"
|
||||
choices = [
|
||||
(_("Create a wallet using the current seed")),
|
||||
(_("Load a wallet from the micro SD card (the current seed is overwritten)")),
|
||||
|
@ -208,13 +220,13 @@ class DigitalBitbox_Client():
|
|||
return
|
||||
else:
|
||||
if self.hid_send_encrypt(b'{"device":"info"}')['device']['lock']:
|
||||
raise Exception("Full 2FA enabled. This is not supported yet.")
|
||||
raise Exception(_("Full 2FA enabled. This is not supported yet."))
|
||||
# Use existing seed
|
||||
self.isInitialized = True
|
||||
|
||||
|
||||
def seed_device_dialog(self):
|
||||
msg = _("Choose how to initialize your Digital Bitbox:\n")
|
||||
msg = _("Choose how to initialize your Digital Bitbox:") + "\n"
|
||||
choices = [
|
||||
(_("Generate a new random wallet")),
|
||||
(_("Load a wallet from the micro SD card"))
|
||||
|
@ -280,9 +292,9 @@ class DigitalBitbox_Client():
|
|||
|
||||
|
||||
def dbb_erase(self):
|
||||
self.handler.show_message(_("Are you sure you want to erase the Digital Bitbox?\r\n\r\n" \
|
||||
"To continue, touch the Digital Bitbox's light for 3 seconds.\r\n\r\n" \
|
||||
"To cancel, briefly touch the light or wait for the timeout."))
|
||||
self.handler.show_message(_("Are you sure you want to erase the Digital Bitbox?") + "\n\n" +
|
||||
_("To continue, touch the Digital Bitbox's light for 3 seconds.") + "\n\n" +
|
||||
_("To cancel, briefly touch the light or wait for the timeout."))
|
||||
hid_reply = self.hid_send_encrypt(b'{"reset":"__ERASE__"}')
|
||||
self.handler.finished()
|
||||
if 'error' in hid_reply:
|
||||
|
@ -305,9 +317,9 @@ class DigitalBitbox_Client():
|
|||
raise Exception('Canceled by user')
|
||||
key = self.stretch_key(key)
|
||||
if show_msg:
|
||||
self.handler.show_message(_("Loading backup...\r\n\r\n" \
|
||||
"To continue, touch the Digital Bitbox's light for 3 seconds.\r\n\r\n" \
|
||||
"To cancel, briefly touch the light or wait for the timeout."))
|
||||
self.handler.show_message(_("Loading backup...") + "\n\n" +
|
||||
_("To continue, touch the Digital Bitbox's light for 3 seconds.") + "\n\n" +
|
||||
_("To cancel, briefly touch the light or wait for the timeout."))
|
||||
msg = b'{"seed":{"source": "backup", "key": "%s", "filename": "%s"}}' % (key, backups['backup'][f].encode('utf8'))
|
||||
hid_reply = self.hid_send_encrypt(msg)
|
||||
self.handler.finished()
|
||||
|
@ -441,12 +453,12 @@ class DigitalBitbox_KeyStore(Hardware_KeyStore):
|
|||
dbb_client = self.plugin.get_client(self)
|
||||
|
||||
if not dbb_client.is_paired():
|
||||
raise Exception("Could not sign message.")
|
||||
raise Exception(_("Could not sign message."))
|
||||
|
||||
reply = dbb_client.hid_send_encrypt(msg)
|
||||
self.handler.show_message(_("Signing message ...\r\n\r\n" \
|
||||
"To continue, touch the Digital Bitbox's blinking light for 3 seconds.\r\n\r\n" \
|
||||
"To cancel, briefly touch the blinking light or wait for the timeout."))
|
||||
self.handler.show_message(_("Signing message ...") + "\n\n" +
|
||||
_("To continue, touch the Digital Bitbox's blinking light for 3 seconds.") + "\n\n" +
|
||||
_("To cancel, briefly touch the blinking light or wait for the timeout."))
|
||||
reply = dbb_client.hid_send_encrypt(msg) # Send twice, first returns an echo for smart verification (not implemented)
|
||||
self.handler.finished()
|
||||
|
||||
|
@ -454,7 +466,7 @@ class DigitalBitbox_KeyStore(Hardware_KeyStore):
|
|||
raise Exception(reply['error']['message'])
|
||||
|
||||
if 'sign' not in reply:
|
||||
raise Exception("Could not sign message.")
|
||||
raise Exception(_("Could not sign message."))
|
||||
|
||||
if 'recid' in reply['sign'][0]:
|
||||
# firmware > v2.1.1
|
||||
|
@ -463,7 +475,7 @@ class DigitalBitbox_KeyStore(Hardware_KeyStore):
|
|||
pk = point_to_ser(pk.pubkey.point, compressed)
|
||||
addr = public_key_to_p2pkh(pk)
|
||||
if verify_message(addr, sig, message) is False:
|
||||
raise Exception("Could not sign message")
|
||||
raise Exception(_("Could not sign message"))
|
||||
elif 'pubkey' in reply['sign'][0]:
|
||||
# firmware <= v2.1.1
|
||||
for i in range(4):
|
||||
|
@ -475,7 +487,7 @@ class DigitalBitbox_KeyStore(Hardware_KeyStore):
|
|||
except Exception:
|
||||
continue
|
||||
else:
|
||||
raise Exception("Could not sign message")
|
||||
raise Exception(_("Could not sign message"))
|
||||
|
||||
|
||||
except BaseException as e:
|
||||
|
@ -576,14 +588,14 @@ class DigitalBitbox_KeyStore(Hardware_KeyStore):
|
|||
self.plugin.comserver_post_notification(reply)
|
||||
|
||||
if steps > 1:
|
||||
self.handler.show_message(_("Signing large transaction. Please be patient ...\r\n\r\n" \
|
||||
"To continue, touch the Digital Bitbox's blinking light for 3 seconds. " \
|
||||
"(Touch " + str(step + 1) + " of " + str(int(steps)) + ")\r\n\r\n" \
|
||||
"To cancel, briefly touch the blinking light or wait for the timeout.\r\n\r\n"))
|
||||
self.handler.show_message(_("Signing large transaction. Please be patient ...") + "\n\n" +
|
||||
_("To continue, touch the Digital Bitbox's blinking light for 3 seconds.") + " " +
|
||||
_("(Touch {} of {})").format((step + 1), steps) + "\n\n" +
|
||||
_("To cancel, briefly touch the blinking light or wait for the timeout.") + "\n\n")
|
||||
else:
|
||||
self.handler.show_message(_("Signing transaction ...\r\n\r\n" \
|
||||
"To continue, touch the Digital Bitbox's blinking light for 3 seconds.\r\n\r\n" \
|
||||
"To cancel, briefly touch the blinking light or wait for the timeout."))
|
||||
self.handler.show_message(_("Signing transaction...") + "\n\n" +
|
||||
_("To continue, touch the Digital Bitbox's blinking light for 3 seconds.") + "\n\n" +
|
||||
_("To cancel, briefly touch the blinking light or wait for the timeout."))
|
||||
|
||||
# Send twice, first returns an echo for smart verification
|
||||
reply = dbb_client.hid_send_encrypt(msg)
|
||||
|
@ -719,3 +731,13 @@ class DigitalBitboxPlugin(HW_PluginBase):
|
|||
if client is not None:
|
||||
client.check_device_dialog()
|
||||
return client
|
||||
|
||||
def show_address(self, wallet, keystore, address):
|
||||
change, index = wallet.get_address_index(address)
|
||||
keypath = '%s/%d/%d' % (keystore.derivation, change, index)
|
||||
xpub = self.get_client(keystore)._get_xpub(keypath)
|
||||
verify_request_payload = {
|
||||
"type": 'p2pkh',
|
||||
"echo": xpub['echo'],
|
||||
}
|
||||
self.comserver_post_notification(verify_request_payload)
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
from functools import partial
|
||||
|
||||
from ..hw_wallet.qt import QtHandlerBase, QtPluginBase
|
||||
from .digitalbitbox import DigitalBitboxPlugin
|
||||
|
||||
|
@ -30,14 +32,7 @@ class Plugin(DigitalBitboxPlugin, QtPluginBase):
|
|||
|
||||
if len(addrs) == 1:
|
||||
def show_address():
|
||||
change, index = wallet.get_address_index(addrs[0])
|
||||
keypath = '%s/%d/%d' % (keystore.derivation, change, index)
|
||||
xpub = self.get_client(keystore)._get_xpub(keypath)
|
||||
verify_request_payload = {
|
||||
"type": 'p2pkh',
|
||||
"echo": xpub['echo'],
|
||||
}
|
||||
self.comserver_post_notification(verify_request_payload)
|
||||
keystore.thread.add(partial(self.show_address, wallet, keystore, addrs[0]))
|
||||
|
||||
menu.addAction(_("Show on {}").format(self.device), show_address)
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
import random
|
||||
import time
|
||||
import threading
|
||||
import base64
|
||||
|
@ -45,11 +45,12 @@ from PyQt5.QtWidgets import (QVBoxLayout, QLabel, QGridLayout, QLineEdit,
|
|||
from electrum.plugins import BasePlugin, hook
|
||||
from electrum.paymentrequest import PaymentRequest
|
||||
from electrum.i18n import _
|
||||
from electrum.util import PrintError
|
||||
from electrum_gui.qt.util import (EnterButton, Buttons, CloseButton, OkButton,
|
||||
WindowModalDialog, get_parent_main_window)
|
||||
|
||||
|
||||
class Processor(threading.Thread):
|
||||
class Processor(threading.Thread, PrintError):
|
||||
polling_interval = 5*60
|
||||
|
||||
def __init__(self, imap_server, username, password, callback):
|
||||
|
@ -59,6 +60,8 @@ class Processor(threading.Thread):
|
|||
self.password = password
|
||||
self.imap_server = imap_server
|
||||
self.on_receive = callback
|
||||
self.M = None
|
||||
self.connect_wait = 100 # ms, between failed connection attempts
|
||||
|
||||
def poll(self):
|
||||
try:
|
||||
|
@ -80,13 +83,18 @@ class Processor(threading.Thread):
|
|||
self.on_receive(pr_str)
|
||||
|
||||
def run(self):
|
||||
self.M = imaplib.IMAP4_SSL(self.imap_server)
|
||||
self.M.login(self.username, self.password)
|
||||
while True:
|
||||
self.poll()
|
||||
time.sleep(self.polling_interval)
|
||||
self.M.close()
|
||||
self.M.logout()
|
||||
try:
|
||||
self.M = imaplib.IMAP4_SSL(self.imap_server)
|
||||
self.M.login(self.username, self.password)
|
||||
except BaseException as e:
|
||||
self.print_error(e)
|
||||
self.connect_wait *= 2
|
||||
# Reconnect when host changes
|
||||
while self.M and self.M.host == self.imap_server:
|
||||
self.poll()
|
||||
time.sleep(self.polling_interval)
|
||||
time.sleep(random.randint(0, self.connect_wait))
|
||||
|
||||
def send(self, recipient, message, payment_request):
|
||||
msg = MIMEMultipart()
|
||||
|
@ -98,10 +106,13 @@ class Processor(threading.Thread):
|
|||
encode_base64(part)
|
||||
part.add_header('Content-Disposition', 'attachment; filename="payreq.btc"')
|
||||
msg.attach(part)
|
||||
s = smtplib.SMTP_SSL(self.imap_server, timeout=2)
|
||||
s.login(self.username, self.password)
|
||||
s.sendmail(self.username, [recipient], msg.as_string())
|
||||
s.quit()
|
||||
try:
|
||||
s = smtplib.SMTP_SSL(self.imap_server, timeout=2)
|
||||
s.login(self.username, self.password)
|
||||
s.sendmail(self.username, [recipient], msg.as_string())
|
||||
s.quit()
|
||||
except BaseException as e:
|
||||
self.print_error(e)
|
||||
|
||||
|
||||
class QEmailSignalObject(QObject):
|
||||
|
@ -225,3 +236,27 @@ class Plugin(BasePlugin):
|
|||
password = str(password_e.text())
|
||||
self.config.set_key('email_password', password)
|
||||
self.password = password
|
||||
|
||||
check_connection = CheckConnectionThread(server, username, password)
|
||||
check_connection.connection_error_signal.connect(lambda e: window.show_message(
|
||||
_("Unable to connect to mail server:\n {}").format(e) + "\n" +
|
||||
_("Please check your connection and credentials.")
|
||||
))
|
||||
check_connection.start()
|
||||
|
||||
|
||||
class CheckConnectionThread(QThread):
|
||||
connection_error_signal = pyqtSignal(str)
|
||||
|
||||
def __init__(self, server, username, password):
|
||||
super().__init__()
|
||||
self.server = server
|
||||
self.username = username
|
||||
self.password = password
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
conn = imaplib.IMAP4_SSL(self.server)
|
||||
conn.login(self.username, self.password)
|
||||
except BaseException as e:
|
||||
self.connection_error_signal.emit(str(e))
|
||||
|
|
|
@ -32,6 +32,9 @@ class CmdLineHandler:
|
|||
def show_message(self, msg, on_cancel=None):
|
||||
print_msg(msg)
|
||||
|
||||
def show_error(self, msg):
|
||||
print_msg(msg)
|
||||
|
||||
def update_status(self, b):
|
||||
print_error('trezor status', b)
|
||||
|
||||
|
|
|
@ -50,6 +50,9 @@ class GuiMixin(object):
|
|||
else:
|
||||
msg = _("Enter your current {} PIN:")
|
||||
pin = self.handler.get_pin(msg.format(self.device))
|
||||
if len(pin) > 9:
|
||||
self.handler.show_error(_('The PIN cannot be longer than 9 characters.'))
|
||||
pin = '' # to cancel below
|
||||
if not pin:
|
||||
return self.proto.Cancel()
|
||||
return self.proto.PinMatrixAck(pin=pin)
|
||||
|
@ -66,7 +69,13 @@ class GuiMixin(object):
|
|||
if passphrase is None:
|
||||
return self.proto.Cancel()
|
||||
passphrase = bip39_normalize_passphrase(passphrase)
|
||||
return self.proto.PassphraseAck(passphrase=passphrase)
|
||||
|
||||
ack = self.proto.PassphraseAck(passphrase=passphrase)
|
||||
length = len(ack.passphrase)
|
||||
if length > 50:
|
||||
self.handler.show_error(_("Too long passphrase ({} > 50 chars).").format(length))
|
||||
return self.proto.Cancel()
|
||||
return ack
|
||||
|
||||
def callback_WordRequest(self, msg):
|
||||
self.step += 1
|
||||
|
@ -110,6 +119,14 @@ class KeepKeyClientBase(GuiMixin, PrintError):
|
|||
def is_pairable(self):
|
||||
return not self.features.bootloader_mode
|
||||
|
||||
def has_usable_connection_with_device(self):
|
||||
try:
|
||||
res = self.ping("electrum pinging device")
|
||||
assert res == "electrum pinging device"
|
||||
except BaseException:
|
||||
return False
|
||||
return True
|
||||
|
||||
def used(self):
|
||||
self.last_operation = time.time()
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import threading
|
||||
|
||||
from binascii import hexlify, unhexlify
|
||||
|
||||
from electrum.util import bfh, bh2u
|
||||
|
@ -72,8 +70,6 @@ class KeepKeyCompatiblePlugin(HW_PluginBase):
|
|||
|
||||
def __init__(self, parent, config, name):
|
||||
HW_PluginBase.__init__(self, parent, config, name)
|
||||
self.main_thread = threading.current_thread()
|
||||
# FIXME: move to base class when Ledger is fixed
|
||||
if self.libraries_available:
|
||||
self.device_manager().register_devices(self.DEVICE_IDS)
|
||||
|
||||
|
@ -303,56 +299,83 @@ class KeepKeyCompatiblePlugin(HW_PluginBase):
|
|||
return inputs
|
||||
|
||||
def tx_outputs(self, derivation, tx, segwit=False):
|
||||
|
||||
def create_output_by_derivation(info):
|
||||
index, xpubs, m = info
|
||||
if len(xpubs) == 1:
|
||||
script_type = self.types.PAYTOP2SHWITNESS if segwit else self.types.PAYTOADDRESS
|
||||
address_n = self.client_class.expand_path(derivation + "/%d/%d" % index)
|
||||
txoutputtype = self.types.TxOutputType(
|
||||
amount=amount,
|
||||
script_type=script_type,
|
||||
address_n=address_n,
|
||||
)
|
||||
else:
|
||||
script_type = self.types.PAYTOP2SHWITNESS if segwit else self.types.PAYTOMULTISIG
|
||||
address_n = self.client_class.expand_path("/%d/%d" % index)
|
||||
nodes = map(self.ckd_public.deserialize, xpubs)
|
||||
pubkeys = [self.types.HDNodePathType(node=node, address_n=address_n) for node in nodes]
|
||||
multisig = self.types.MultisigRedeemScriptType(
|
||||
pubkeys=pubkeys,
|
||||
signatures=[b''] * len(pubkeys),
|
||||
m=m)
|
||||
txoutputtype = self.types.TxOutputType(
|
||||
multisig=multisig,
|
||||
amount=amount,
|
||||
address_n=self.client_class.expand_path(derivation + "/%d/%d" % index),
|
||||
script_type=script_type)
|
||||
return txoutputtype
|
||||
|
||||
def create_output_by_address():
|
||||
txoutputtype = self.types.TxOutputType()
|
||||
txoutputtype.amount = amount
|
||||
if _type == TYPE_SCRIPT:
|
||||
txoutputtype.script_type = self.types.PAYTOOPRETURN
|
||||
txoutputtype.op_return_data = address[2:]
|
||||
elif _type == TYPE_ADDRESS:
|
||||
if is_segwit_address(address):
|
||||
txoutputtype.script_type = self.types.PAYTOWITNESS
|
||||
else:
|
||||
addrtype, hash_160 = b58_address_to_hash160(address)
|
||||
if addrtype == constants.net.ADDRTYPE_P2PKH:
|
||||
txoutputtype.script_type = self.types.PAYTOADDRESS
|
||||
elif addrtype == constants.net.ADDRTYPE_P2SH:
|
||||
txoutputtype.script_type = self.types.PAYTOSCRIPTHASH
|
||||
else:
|
||||
raise BaseException('addrtype: ' + str(addrtype))
|
||||
txoutputtype.address = address
|
||||
return txoutputtype
|
||||
|
||||
def is_any_output_on_change_branch():
|
||||
for _type, address, amount in tx.outputs():
|
||||
info = tx.output_info.get(address)
|
||||
if info is not None:
|
||||
index, xpubs, m = info
|
||||
if index[0] == 1:
|
||||
return True
|
||||
return False
|
||||
|
||||
outputs = []
|
||||
has_change = False
|
||||
any_output_on_change_branch = is_any_output_on_change_branch()
|
||||
|
||||
for _type, address, amount in tx.outputs():
|
||||
use_create_by_derivation = False
|
||||
|
||||
info = tx.output_info.get(address)
|
||||
if info is not None and not has_change:
|
||||
has_change = True # no more than one change address
|
||||
addrtype, hash_160 = b58_address_to_hash160(address)
|
||||
index, xpubs, m = info
|
||||
if len(xpubs) == 1:
|
||||
script_type = self.types.PAYTOP2SHWITNESS if segwit else self.types.PAYTOADDRESS
|
||||
address_n = self.client_class.expand_path(derivation + "/%d/%d"%index)
|
||||
txoutputtype = self.types.TxOutputType(
|
||||
amount = amount,
|
||||
script_type = script_type,
|
||||
address_n = address_n,
|
||||
)
|
||||
else:
|
||||
script_type = self.types.PAYTOP2SHWITNESS if segwit else self.types.PAYTOMULTISIG
|
||||
address_n = self.client_class.expand_path("/%d/%d"%index)
|
||||
nodes = map(self.ckd_public.deserialize, xpubs)
|
||||
pubkeys = [ self.types.HDNodePathType(node=node, address_n=address_n) for node in nodes]
|
||||
multisig = self.types.MultisigRedeemScriptType(
|
||||
pubkeys = pubkeys,
|
||||
signatures = [b''] * len(pubkeys),
|
||||
m = m)
|
||||
txoutputtype = self.types.TxOutputType(
|
||||
multisig = multisig,
|
||||
amount = amount,
|
||||
address_n = self.client_class.expand_path(derivation + "/%d/%d"%index),
|
||||
script_type = script_type)
|
||||
else:
|
||||
txoutputtype = self.types.TxOutputType()
|
||||
txoutputtype.amount = amount
|
||||
if _type == TYPE_SCRIPT:
|
||||
txoutputtype.script_type = self.types.PAYTOOPRETURN
|
||||
txoutputtype.op_return_data = address[2:]
|
||||
elif _type == TYPE_ADDRESS:
|
||||
if is_segwit_address(address):
|
||||
txoutputtype.script_type = self.types.PAYTOWITNESS
|
||||
else:
|
||||
addrtype, hash_160 = b58_address_to_hash160(address)
|
||||
if addrtype == constants.net.ADDRTYPE_P2PKH:
|
||||
txoutputtype.script_type = self.types.PAYTOADDRESS
|
||||
elif addrtype == constants.net.ADDRTYPE_P2SH:
|
||||
txoutputtype.script_type = self.types.PAYTOSCRIPTHASH
|
||||
else:
|
||||
raise BaseException('addrtype: ' + str(addrtype))
|
||||
txoutputtype.address = address
|
||||
on_change_branch = index[0] == 1
|
||||
# prioritise hiding outputs on the 'change' branch from user
|
||||
# because no more than one change address allowed
|
||||
if on_change_branch == any_output_on_change_branch:
|
||||
use_create_by_derivation = True
|
||||
has_change = True
|
||||
|
||||
if use_create_by_derivation:
|
||||
txoutputtype = create_output_by_derivation(info)
|
||||
else:
|
||||
txoutputtype = create_output_by_address()
|
||||
outputs.append(txoutputtype)
|
||||
|
||||
return outputs
|
||||
|
|
|
@ -250,7 +250,7 @@ class QtPlugin(QtPluginBase):
|
|||
vbox.addWidget(QLabel(msg))
|
||||
vbox.addWidget(text)
|
||||
pin = QLineEdit()
|
||||
pin.setValidator(QRegExpValidator(QRegExp('[1-9]{0,10}')))
|
||||
pin.setValidator(QRegExpValidator(QRegExp('[1-9]{0,9}')))
|
||||
pin.setMaximumWidth(100)
|
||||
hbox_pin = QHBoxLayout()
|
||||
hbox_pin.addWidget(QLabel(_("Enter your PIN (digits 1-9):")))
|
||||
|
|
|
@ -16,7 +16,7 @@ class LabelsPlugin(BasePlugin):
|
|||
|
||||
def __init__(self, parent, config, name):
|
||||
BasePlugin.__init__(self, parent, config, name)
|
||||
self.target_host = 'labels.bauerj.eu'
|
||||
self.target_host = 'labels.electrum.org'
|
||||
self.wallets = {}
|
||||
|
||||
def encode(self, wallet, msg):
|
||||
|
@ -45,7 +45,7 @@ class LabelsPlugin(BasePlugin):
|
|||
|
||||
@hook
|
||||
def set_label(self, wallet, item, label):
|
||||
if not wallet in self.wallets:
|
||||
if wallet not in self.wallets:
|
||||
return
|
||||
if not item:
|
||||
return
|
||||
|
@ -55,7 +55,7 @@ class LabelsPlugin(BasePlugin):
|
|||
"walletNonce": nonce,
|
||||
"externalId": self.encode(wallet, item),
|
||||
"encryptedLabel": self.encode(wallet, label)}
|
||||
t = threading.Thread(target=self.do_request,
|
||||
t = threading.Thread(target=self.do_request_safe,
|
||||
args=["POST", "/label", False, bundle])
|
||||
t.setDaemon(True)
|
||||
t.start()
|
||||
|
@ -78,8 +78,18 @@ class LabelsPlugin(BasePlugin):
|
|||
raise BaseException(response["error"])
|
||||
return response
|
||||
|
||||
def do_request_safe(self, *args, **kwargs):
|
||||
try:
|
||||
self.do_request(*args, **kwargs)
|
||||
except BaseException as e:
|
||||
#traceback.print_exc(file=sys.stderr)
|
||||
self.print_error('error doing request')
|
||||
|
||||
def push_thread(self, wallet):
|
||||
wallet_id = self.wallets[wallet][2]
|
||||
wallet_data = self.wallets.get(wallet, None)
|
||||
if not wallet_data:
|
||||
raise Exception('Wallet {} not loaded'.format(wallet))
|
||||
wallet_id = wallet_data[2]
|
||||
bundle = {"labels": [],
|
||||
"walletId": wallet_id,
|
||||
"walletNonce": self.get_nonce(wallet)}
|
||||
|
@ -95,42 +105,47 @@ class LabelsPlugin(BasePlugin):
|
|||
self.do_request("POST", "/labels", True, bundle)
|
||||
|
||||
def pull_thread(self, wallet, force):
|
||||
wallet_id = self.wallets[wallet][2]
|
||||
wallet_data = self.wallets.get(wallet, None)
|
||||
if not wallet_data:
|
||||
raise Exception('Wallet {} not loaded'.format(wallet))
|
||||
wallet_id = wallet_data[2]
|
||||
nonce = 1 if force else self.get_nonce(wallet) - 1
|
||||
self.print_error("asking for labels since nonce", nonce)
|
||||
response = self.do_request("GET", ("/labels/since/%d/for/%s" % (nonce, wallet_id) ))
|
||||
if response["labels"] is None:
|
||||
self.print_error('no new labels')
|
||||
return
|
||||
result = {}
|
||||
for label in response["labels"]:
|
||||
try:
|
||||
key = self.decode(wallet, label["externalId"])
|
||||
value = self.decode(wallet, label["encryptedLabel"])
|
||||
except:
|
||||
continue
|
||||
try:
|
||||
json.dumps(key)
|
||||
json.dumps(value)
|
||||
except:
|
||||
self.print_error('error: no json', key)
|
||||
continue
|
||||
result[key] = value
|
||||
|
||||
for key, value in result.items():
|
||||
if force or not wallet.labels.get(key):
|
||||
wallet.labels[key] = value
|
||||
|
||||
self.print_error("received %d labels" % len(response))
|
||||
# do not write to disk because we're in a daemon thread
|
||||
wallet.storage.put('labels', wallet.labels)
|
||||
self.set_nonce(wallet, response["nonce"] + 1)
|
||||
self.on_pulled(wallet)
|
||||
|
||||
def pull_thread_safe(self, wallet, force):
|
||||
try:
|
||||
response = self.do_request("GET", ("/labels/since/%d/for/%s" % (nonce, wallet_id) ))
|
||||
if response["labels"] is None:
|
||||
self.print_error('no new labels')
|
||||
return
|
||||
result = {}
|
||||
for label in response["labels"]:
|
||||
try:
|
||||
key = self.decode(wallet, label["externalId"])
|
||||
value = self.decode(wallet, label["encryptedLabel"])
|
||||
except:
|
||||
continue
|
||||
try:
|
||||
json.dumps(key)
|
||||
json.dumps(value)
|
||||
except:
|
||||
self.print_error('error: no json', key)
|
||||
continue
|
||||
result[key] = value
|
||||
|
||||
for key, value in result.items():
|
||||
if force or not wallet.labels.get(key):
|
||||
wallet.labels[key] = value
|
||||
|
||||
self.print_error("received %d labels" % len(response))
|
||||
# do not write to disk because we're in a daemon thread
|
||||
wallet.storage.put('labels', wallet.labels)
|
||||
self.set_nonce(wallet, response["nonce"] + 1)
|
||||
self.on_pulled(wallet)
|
||||
|
||||
except Exception as e:
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
self.print_error("could not retrieve labels")
|
||||
self.pull_thread(wallet, force)
|
||||
except BaseException as e:
|
||||
# traceback.print_exc(file=sys.stderr)
|
||||
self.print_error('could not retrieve labels')
|
||||
|
||||
def start_wallet(self, wallet):
|
||||
nonce = self.get_nonce(wallet)
|
||||
|
@ -144,7 +159,7 @@ class LabelsPlugin(BasePlugin):
|
|||
wallet_id = hashlib.sha256(mpk).hexdigest()
|
||||
self.wallets[wallet] = (password, iv, wallet_id)
|
||||
# If there is an auth token we can try to actually start syncing
|
||||
t = threading.Thread(target=self.pull_thread, args=(wallet, False))
|
||||
t = threading.Thread(target=self.pull_thread_safe, args=(wallet, False))
|
||||
t.setDaemon(True)
|
||||
t.start()
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
from functools import partial
|
||||
import traceback
|
||||
import sys
|
||||
|
||||
from PyQt5.QtGui import *
|
||||
from PyQt5.QtCore import *
|
||||
|
@ -37,10 +39,12 @@ class Plugin(LabelsPlugin):
|
|||
hbox.addWidget(QLabel("Label sync options:"))
|
||||
upload = ThreadedButton("Force upload",
|
||||
partial(self.push_thread, wallet),
|
||||
partial(self.done_processing, d))
|
||||
partial(self.done_processing_success, d),
|
||||
partial(self.done_processing_error, d))
|
||||
download = ThreadedButton("Force download",
|
||||
partial(self.pull_thread, wallet, True),
|
||||
partial(self.done_processing, d))
|
||||
partial(self.done_processing_success, d),
|
||||
partial(self.done_processing_error, d))
|
||||
vbox = QVBoxLayout()
|
||||
vbox.addWidget(upload)
|
||||
vbox.addWidget(download)
|
||||
|
@ -54,13 +58,20 @@ class Plugin(LabelsPlugin):
|
|||
def on_pulled(self, wallet):
|
||||
self.obj.labels_changed_signal.emit(wallet)
|
||||
|
||||
def done_processing(self, dialog, result):
|
||||
def done_processing_success(self, dialog, result):
|
||||
dialog.show_message(_("Your labels have been synchronised."))
|
||||
|
||||
def done_processing_error(self, dialog, result):
|
||||
traceback.print_exception(*result, file=sys.stderr)
|
||||
dialog.show_error(_("Error synchronising labels") + ':\n' + str(result[:2]))
|
||||
|
||||
@hook
|
||||
def on_new_window(self, window):
|
||||
def load_wallet(self, wallet, window):
|
||||
# FIXME if the user just enabled the plugin, this hook won't be called
|
||||
# as the wallet is already loaded, and hence the plugin will be in
|
||||
# a non-functional state for that window
|
||||
self.obj.labels_changed_signal.connect(window.update_tabs)
|
||||
self.start_wallet(window.wallet)
|
||||
self.start_wallet(wallet)
|
||||
|
||||
@hook
|
||||
def on_close_window(self, window):
|
||||
|
|
|
@ -1,16 +1,24 @@
|
|||
import os
|
||||
import hashlib
|
||||
import logging
|
||||
import json
|
||||
import copy
|
||||
from binascii import hexlify, unhexlify
|
||||
|
||||
import websocket
|
||||
|
||||
from PyQt5.Qt import QDialog, QLineEdit, QTextEdit, QVBoxLayout, QLabel
|
||||
import PyQt5.QtCore as QtCore
|
||||
from PyQt5.QtWidgets import *
|
||||
|
||||
from btchip.btchip import *
|
||||
|
||||
from electrum.i18n import _
|
||||
from electrum_gui.qt.util import *
|
||||
from electrum.util import print_msg
|
||||
|
||||
import os, hashlib, websocket, logging, json, copy
|
||||
from electrum import constants, bitcoin
|
||||
from electrum_gui.qt.qrcodewidget import QRCodeWidget
|
||||
from btchip.btchip import *
|
||||
|
||||
|
||||
DEBUG = False
|
||||
|
||||
|
@ -37,7 +45,7 @@ class LedgerAuthDialog(QDialog):
|
|||
self.handler = handler
|
||||
self.txdata = data
|
||||
self.idxs = self.txdata['keycardData'] if self.txdata['confirmationType'] > 1 else ''
|
||||
self.setMinimumWidth(600)
|
||||
self.setMinimumWidth(650)
|
||||
self.setWindowTitle(_("Ledger Wallet Authentication"))
|
||||
self.cfg = copy.deepcopy(self.handler.win.wallet.get_keystore().cfg)
|
||||
self.dongle = self.handler.win.wallet.get_keystore().get_client().dongle
|
||||
|
@ -110,17 +118,23 @@ class LedgerAuthDialog(QDialog):
|
|||
card = QVBoxLayout()
|
||||
self.cardbox.setLayout(card)
|
||||
self.addrtext = QTextEdit()
|
||||
self.addrtext.setStyleSheet("QTextEdit { color:blue; background-color:lightgray; padding:15px 10px; border:none; font-size:20pt; }")
|
||||
self.addrtext.setStyleSheet("QTextEdit { color:blue; background-color:lightgray; padding:15px 10px; border:none; font-size:20pt; font-family:monospace; }")
|
||||
self.addrtext.setReadOnly(True)
|
||||
self.addrtext.setMaximumHeight(120)
|
||||
self.addrtext.setMaximumHeight(130)
|
||||
card.addWidget(self.addrtext)
|
||||
|
||||
def pin_changed(s):
|
||||
if len(s) < len(self.idxs):
|
||||
i = self.idxs[len(s)]
|
||||
addr = self.txdata['address']
|
||||
addr = addr[:i] + '<u><b>' + addr[i:i+1] + '</u></b>' + addr[i+1:]
|
||||
self.addrtext.setHtml(str(addr))
|
||||
if not constants.net.TESTNET:
|
||||
text = addr[:i] + '<u><b>' + addr[i:i+1] + '</u></b>' + addr[i+1:]
|
||||
else:
|
||||
# pin needs to be created from mainnet address
|
||||
addr_mainnet = bitcoin.script_to_address(bitcoin.address_to_script(addr), net=constants.BitcoinMainnet)
|
||||
addr_mainnet = addr_mainnet[:i] + '<u><b>' + addr_mainnet[i:i+1] + '</u></b>' + addr_mainnet[i+1:]
|
||||
text = str(addr) + '\n' + str(addr_mainnet)
|
||||
self.addrtext.setHtml(str(text))
|
||||
else:
|
||||
self.addrtext.setHtml(_("Press Enter"))
|
||||
|
||||
|
@ -179,8 +193,8 @@ class LedgerAuthDialog(QDialog):
|
|||
self.pinbox.setVisible(self.cfg['mode'] == 0)
|
||||
self.cardbox.setVisible(self.cfg['mode'] == 1)
|
||||
self.pintxt.setFocus(True) if self.cfg['mode'] == 0 else self.cardtxt.setFocus(True)
|
||||
self.setMaximumHeight(200)
|
||||
|
||||
self.setMaximumHeight(400)
|
||||
|
||||
def do_pairing(self):
|
||||
rng = os.urandom(16)
|
||||
pairID = (hexlify(rng) + hexlify(hashlib.sha256(rng).digest()[0:1])).decode('utf-8')
|
||||
|
@ -338,11 +352,7 @@ class LedgerWebSocket(QThread):
|
|||
ws.send( self.txreq )
|
||||
debug_msg("Req Sent", self.txreq)
|
||||
|
||||
|
||||
def debug_msg(*args):
|
||||
if DEBUG:
|
||||
print_msg(*args)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -57,6 +57,13 @@ class Ledger_Client():
|
|||
def i4b(self, x):
|
||||
return pack('>I', x)
|
||||
|
||||
def has_usable_connection_with_device(self):
|
||||
try:
|
||||
self.dongleObject.getFirmwareVersion()
|
||||
except BaseException:
|
||||
return False
|
||||
return True
|
||||
|
||||
def test_pin_unlocked(func):
|
||||
"""Function decorator to test the Ledger for being unlocked, and if not,
|
||||
raise a human-readable exception.
|
||||
|
@ -180,8 +187,8 @@ class Ledger_Client():
|
|||
try:
|
||||
self.perform_hw1_preflight()
|
||||
except BTChipException as e:
|
||||
if (e.sw == 0x6d00):
|
||||
raise BaseException("Device not in Bitcoin mode")
|
||||
if (e.sw == 0x6d00 or e.sw == 0x6700):
|
||||
raise BaseException(_("Device not in Bitcoin mode")) from e
|
||||
raise e
|
||||
self.preflightDone = True
|
||||
|
||||
|
@ -229,6 +236,16 @@ class Ledger_KeyStore(Hardware_KeyStore):
|
|||
self.client = None
|
||||
raise Exception(message)
|
||||
|
||||
def set_and_unset_signing(func):
|
||||
"""Function decorator to set and unset self.signing."""
|
||||
def wrapper(self, *args, **kwargs):
|
||||
try:
|
||||
self.signing = True
|
||||
return func(self, *args, **kwargs)
|
||||
finally:
|
||||
self.signing = False
|
||||
return wrapper
|
||||
|
||||
def address_id_stripped(self, address):
|
||||
# Strip the leading "m/"
|
||||
change, index = self.get_address_index(address)
|
||||
|
@ -239,8 +256,8 @@ class Ledger_KeyStore(Hardware_KeyStore):
|
|||
def decrypt_message(self, pubkey, message, password):
|
||||
raise RuntimeError(_('Encryption and decryption are currently not supported for {}').format(self.device))
|
||||
|
||||
@set_and_unset_signing
|
||||
def sign_message(self, sequence, message, password):
|
||||
self.signing = True
|
||||
message = message.encode('utf8')
|
||||
message_hash = hashlib.sha256(message).hexdigest().upper()
|
||||
# prompt for the PIN before displaying the dialog if necessary
|
||||
|
@ -259,16 +276,17 @@ class Ledger_KeyStore(Hardware_KeyStore):
|
|||
except BTChipException as e:
|
||||
if e.sw == 0x6a80:
|
||||
self.give_error("Unfortunately, this message cannot be signed by the Ledger wallet. Only alphanumerical messages shorter than 140 characters are supported. Please remove any extra characters (tab, carriage return) and retry.")
|
||||
elif e.sw == 0x6985: # cancelled by user
|
||||
return b''
|
||||
else:
|
||||
self.give_error(e, True)
|
||||
except UserWarning:
|
||||
self.handler.show_error(_('Cancelled by user'))
|
||||
return ''
|
||||
return b''
|
||||
except Exception as e:
|
||||
self.give_error(e, True)
|
||||
finally:
|
||||
self.handler.finished()
|
||||
self.signing = False
|
||||
# Parse the ASN.1 signature
|
||||
rLength = signature[3]
|
||||
r = signature[4 : 4 + rLength]
|
||||
|
@ -281,12 +299,11 @@ class Ledger_KeyStore(Hardware_KeyStore):
|
|||
# And convert it
|
||||
return bytes([27 + 4 + (signature[0] & 0x01)]) + r + s
|
||||
|
||||
|
||||
@set_and_unset_signing
|
||||
def sign_transaction(self, tx, password):
|
||||
if tx.is_complete():
|
||||
return
|
||||
client = self.get_client()
|
||||
self.signing = True
|
||||
inputs = []
|
||||
inputsPaths = []
|
||||
pubKeys = []
|
||||
|
@ -360,7 +377,8 @@ class Ledger_KeyStore(Hardware_KeyStore):
|
|||
for _type, address, amount in tx.outputs():
|
||||
assert _type == TYPE_ADDRESS
|
||||
info = tx.output_info.get(address)
|
||||
if (info is not None) and (len(tx.outputs()) != 1):
|
||||
if (info is not None) and len(tx.outputs()) > 1 \
|
||||
and info[0][0] == 1: # "is on 'change' branch"
|
||||
index, xpubs, m = info
|
||||
changePath = self.get_derivation()[2:] + "/%d/%d"%index
|
||||
changeAmount = amount
|
||||
|
@ -400,7 +418,12 @@ class Ledger_KeyStore(Hardware_KeyStore):
|
|||
if segwitTransaction:
|
||||
self.get_client().startUntrustedTransaction(True, inputIndex,
|
||||
chipInputs, redeemScripts[inputIndex])
|
||||
outputData = self.get_client().finalizeInputFull(txOutput)
|
||||
if changePath:
|
||||
# we don't set meaningful outputAddress, amount and fees
|
||||
# as we only care about the alternateEncoding==True branch
|
||||
outputData = self.get_client().finalizeInput(b'', 0, 0, changePath, bfh(rawTx))
|
||||
else:
|
||||
outputData = self.get_client().finalizeInputFull(txOutput)
|
||||
outputData['outputData'] = txOutput
|
||||
transactionOutput = outputData['outputData']
|
||||
if outputData['confirmationNeeded']:
|
||||
|
@ -423,7 +446,12 @@ class Ledger_KeyStore(Hardware_KeyStore):
|
|||
while inputIndex < len(inputs):
|
||||
self.get_client().startUntrustedTransaction(firstTransaction, inputIndex,
|
||||
chipInputs, redeemScripts[inputIndex])
|
||||
outputData = self.get_client().finalizeInputFull(txOutput)
|
||||
if changePath:
|
||||
# we don't set meaningful outputAddress, amount and fees
|
||||
# as we only care about the alternateEncoding==True branch
|
||||
outputData = self.get_client().finalizeInput(b'', 0, 0, changePath, bfh(rawTx))
|
||||
else:
|
||||
outputData = self.get_client().finalizeInputFull(txOutput)
|
||||
outputData['outputData'] = txOutput
|
||||
if firstTransaction:
|
||||
transactionOutput = outputData['outputData']
|
||||
|
@ -446,6 +474,12 @@ class Ledger_KeyStore(Hardware_KeyStore):
|
|||
except UserWarning:
|
||||
self.handler.show_error(_('Cancelled by user'))
|
||||
return
|
||||
except BTChipException as e:
|
||||
if e.sw == 0x6985: # cancelled by user
|
||||
return
|
||||
else:
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
self.give_error(e, True)
|
||||
except BaseException as e:
|
||||
traceback.print_exc(file=sys.stdout)
|
||||
self.give_error(e, True)
|
||||
|
@ -456,10 +490,9 @@ class Ledger_KeyStore(Hardware_KeyStore):
|
|||
signingPos = inputs[i][4]
|
||||
txin['signatures'][signingPos] = bh2u(signatures[i])
|
||||
tx.raw = tx.serialize()
|
||||
self.signing = False
|
||||
|
||||
@set_and_unset_signing
|
||||
def show_address(self, sequence, txin_type):
|
||||
self.signing = True
|
||||
client = self.get_client()
|
||||
address_path = self.get_derivation()[2:] + "/%d/%d"%sequence
|
||||
self.handler.show_message(_("Showing address ..."))
|
||||
|
@ -478,7 +511,6 @@ class Ledger_KeyStore(Hardware_KeyStore):
|
|||
self.handler.show_error(e)
|
||||
finally:
|
||||
self.handler.finished()
|
||||
self.signing = False
|
||||
|
||||
class LedgerPlugin(HW_PluginBase):
|
||||
libraries_available = BTCHIP
|
||||
|
@ -499,17 +531,17 @@ class LedgerPlugin(HW_PluginBase):
|
|||
if self.libraries_available:
|
||||
self.device_manager().register_devices(self.DEVICE_IDS)
|
||||
|
||||
def btchip_is_connected(self, keystore):
|
||||
try:
|
||||
self.get_client(keystore).getFirmwareVersion()
|
||||
except Exception as e:
|
||||
return False
|
||||
return True
|
||||
|
||||
def get_btchip_device(self, device):
|
||||
ledger = False
|
||||
if (device.product_key[0] == 0x2581 and device.product_key[1] == 0x3b7c) or (device.product_key[0] == 0x2581 and device.product_key[1] == 0x4b7c) or (device.product_key[0] == 0x2c97):
|
||||
ledger = True
|
||||
if device.product_key[0] == 0x2581 and device.product_key[1] == 0x3b7c:
|
||||
ledger = True
|
||||
if device.product_key[0] == 0x2581 and device.product_key[1] == 0x4b7c:
|
||||
ledger = True
|
||||
if device.product_key[0] == 0x2c97:
|
||||
if device.interface_number == 0 or device.usage_page == 0xffa0:
|
||||
ledger = True
|
||||
else:
|
||||
return None # non-compatible interface of a nano s or blue
|
||||
dev = hid.device()
|
||||
dev.open_path(device.path)
|
||||
dev.set_nonblocking(True)
|
||||
|
@ -541,7 +573,6 @@ class LedgerPlugin(HW_PluginBase):
|
|||
|
||||
def get_client(self, keystore, force_pair=True):
|
||||
# All client interaction should not be in the main GUI thread
|
||||
#assert self.main_thread != threading.current_thread()
|
||||
devmgr = self.device_manager()
|
||||
handler = keystore.handler
|
||||
with devmgr.hid_lock:
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
import threading
|
||||
|
||||
from PyQt5.Qt import QInputDialog, QLineEdit, QVBoxLayout, QLabel
|
||||
#from btchip.btchipPersoWizard import StartBTChipPersoDialog
|
||||
|
||||
from electrum.i18n import _
|
||||
from electrum.plugins import hook
|
||||
from electrum.wallet import Standard_Wallet
|
||||
from .ledger import LedgerPlugin
|
||||
from ..hw_wallet.qt import QtHandlerBase, QtPluginBase
|
||||
from electrum_gui.qt.util import *
|
||||
|
||||
#from btchip.btchipPersoWizard import StartBTChipPersoDialog
|
||||
from .ledger import LedgerPlugin
|
||||
from ..hw_wallet.qt import QtHandlerBase, QtPluginBase
|
||||
|
||||
|
||||
class Plugin(LedgerPlugin, QtPluginBase):
|
||||
icon_unpaired = ":icons/ledger_unpaired.png"
|
||||
|
@ -77,11 +75,7 @@ class Ledger_Handler(QtHandlerBase):
|
|||
return
|
||||
|
||||
def setup_dialog(self):
|
||||
self.show_error(_('Initialization of Ledger HW devices is currently disabled.'))
|
||||
return
|
||||
dialog = StartBTChipPersoDialog()
|
||||
dialog.exec_()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -3,8 +3,8 @@ from .clientbase import TrezorClientBase
|
|||
|
||||
class TrezorClient(TrezorClientBase, ProtocolMixin, BaseClient):
|
||||
def __init__(self, transport, handler, plugin):
|
||||
BaseClient.__init__(self, transport)
|
||||
ProtocolMixin.__init__(self, transport)
|
||||
BaseClient.__init__(self, transport=transport)
|
||||
ProtocolMixin.__init__(self, transport=transport)
|
||||
TrezorClientBase.__init__(self, handler, plugin, proto)
|
||||
|
||||
|
||||
|
|
|
@ -50,6 +50,9 @@ class GuiMixin(object):
|
|||
else:
|
||||
msg = _("Enter your current {} PIN:")
|
||||
pin = self.handler.get_pin(msg.format(self.device))
|
||||
if len(pin) > 9:
|
||||
self.handler.show_error(_('The PIN cannot be longer than 9 characters.'))
|
||||
pin = '' # to cancel below
|
||||
if not pin:
|
||||
return self.proto.Cancel()
|
||||
return self.proto.PinMatrixAck(pin=pin)
|
||||
|
@ -69,7 +72,13 @@ class GuiMixin(object):
|
|||
if passphrase is None:
|
||||
return self.proto.Cancel()
|
||||
passphrase = bip39_normalize_passphrase(passphrase)
|
||||
return self.proto.PassphraseAck(passphrase=passphrase)
|
||||
|
||||
ack = self.proto.PassphraseAck(passphrase=passphrase)
|
||||
length = len(ack.passphrase)
|
||||
if length > 50:
|
||||
self.handler.show_error(_("Too long passphrase ({} > 50 chars).").format(length))
|
||||
return self.proto.Cancel()
|
||||
return ack
|
||||
|
||||
def callback_PassphraseStateRequest(self, msg):
|
||||
return self.proto.PassphraseStateAck()
|
||||
|
@ -116,6 +125,14 @@ class TrezorClientBase(GuiMixin, PrintError):
|
|||
def is_pairable(self):
|
||||
return not self.features.bootloader_mode
|
||||
|
||||
def has_usable_connection_with_device(self):
|
||||
try:
|
||||
res = self.ping("electrum pinging device")
|
||||
assert res == "electrum pinging device"
|
||||
except BaseException:
|
||||
return False
|
||||
return True
|
||||
|
||||
def used(self):
|
||||
self.last_operation = time.time()
|
||||
|
||||
|
|
|
@ -251,7 +251,7 @@ class QtPlugin(QtPluginBase):
|
|||
vbox.addWidget(QLabel(msg))
|
||||
vbox.addWidget(text)
|
||||
pin = QLineEdit()
|
||||
pin.setValidator(QRegExpValidator(QRegExp('[1-9]{0,10}')))
|
||||
pin.setValidator(QRegExpValidator(QRegExp('[1-9]{0,9}')))
|
||||
pin.setMaximumWidth(100)
|
||||
hbox_pin = QHBoxLayout()
|
||||
hbox_pin.addWidget(QLabel(_("Enter your PIN (digits 1-9):")))
|
||||
|
|
95
plugins/trezor/transport.py
Normal file
95
plugins/trezor/transport.py
Normal file
|
@ -0,0 +1,95 @@
|
|||
from electrum.util import PrintError
|
||||
|
||||
|
||||
class TrezorTransport(PrintError):
|
||||
|
||||
@staticmethod
|
||||
def all_transports():
|
||||
"""Reimplemented trezorlib.transport.all_transports so that we can
|
||||
enable/disable specific transports.
|
||||
"""
|
||||
try:
|
||||
# only to detect trezorlib version
|
||||
from trezorlib.transport import all_transports
|
||||
except ImportError:
|
||||
# old trezorlib. compat for trezorlib < 0.9.2
|
||||
transports = []
|
||||
#try:
|
||||
# from trezorlib.transport_bridge import BridgeTransport
|
||||
# transports.append(BridgeTransport)
|
||||
#except BaseException:
|
||||
# pass
|
||||
try:
|
||||
from trezorlib.transport_hid import HidTransport
|
||||
transports.append(HidTransport)
|
||||
except BaseException:
|
||||
pass
|
||||
try:
|
||||
from trezorlib.transport_udp import UdpTransport
|
||||
transports.append(UdpTransport)
|
||||
except BaseException:
|
||||
pass
|
||||
try:
|
||||
from trezorlib.transport_webusb import WebUsbTransport
|
||||
transports.append(WebUsbTransport)
|
||||
except BaseException:
|
||||
pass
|
||||
else:
|
||||
# new trezorlib.
|
||||
transports = []
|
||||
#try:
|
||||
# from trezorlib.transport.bridge import BridgeTransport
|
||||
# transports.append(BridgeTransport)
|
||||
#except BaseException:
|
||||
# pass
|
||||
try:
|
||||
from trezorlib.transport.hid import HidTransport
|
||||
transports.append(HidTransport)
|
||||
except BaseException:
|
||||
pass
|
||||
try:
|
||||
from trezorlib.transport.udp import UdpTransport
|
||||
transports.append(UdpTransport)
|
||||
except BaseException:
|
||||
pass
|
||||
try:
|
||||
from trezorlib.transport.webusb import WebUsbTransport
|
||||
transports.append(WebUsbTransport)
|
||||
except BaseException:
|
||||
pass
|
||||
return transports
|
||||
return transports
|
||||
|
||||
def enumerate_devices(self):
|
||||
"""Just like trezorlib.transport.enumerate_devices,
|
||||
but with exception catching, so that transports can fail separately.
|
||||
"""
|
||||
devices = []
|
||||
for transport in self.all_transports():
|
||||
try:
|
||||
new_devices = transport.enumerate()
|
||||
except BaseException as e:
|
||||
self.print_error('enumerate failed for {}. error {}'
|
||||
.format(transport.__name__, str(e)))
|
||||
else:
|
||||
devices.extend(new_devices)
|
||||
return devices
|
||||
|
||||
def get_transport(self, path=None):
|
||||
"""Reimplemented trezorlib.transport.get_transport,
|
||||
(1) for old trezorlib
|
||||
(2) to be able to disable specific transports
|
||||
(3) to call our own enumerate_devices that catches exceptions
|
||||
"""
|
||||
if path is None:
|
||||
try:
|
||||
return self.enumerate_devices()[0]
|
||||
except IndexError:
|
||||
raise Exception("No TREZOR device found") from None
|
||||
|
||||
def match_prefix(a, b):
|
||||
return a.startswith(b) or b.startswith(a)
|
||||
transports = [t for t in self.all_transports() if match_prefix(path, t.PATH_PREFIX)]
|
||||
if transports:
|
||||
return transports[0].find_by_path(path)
|
||||
raise Exception("Unknown path prefix '%s'" % path)
|
|
@ -1,5 +1,3 @@
|
|||
import threading
|
||||
|
||||
from binascii import hexlify, unhexlify
|
||||
|
||||
from electrum.util import bfh, bh2u, versiontuple
|
||||
|
@ -93,7 +91,6 @@ class TrezorPlugin(HW_PluginBase):
|
|||
|
||||
def __init__(self, parent, config, name):
|
||||
HW_PluginBase.__init__(self, parent, config, name)
|
||||
self.main_thread = threading.current_thread()
|
||||
|
||||
try:
|
||||
# Minimal test if python-trezor is installed
|
||||
|
@ -117,6 +114,7 @@ class TrezorPlugin(HW_PluginBase):
|
|||
return
|
||||
|
||||
from . import client
|
||||
from . import transport
|
||||
import trezorlib.ckd_public
|
||||
import trezorlib.messages
|
||||
self.client_class = client.TrezorClient
|
||||
|
@ -124,17 +122,17 @@ class TrezorPlugin(HW_PluginBase):
|
|||
self.types = trezorlib.messages
|
||||
self.DEVICE_IDS = ('TREZOR',)
|
||||
|
||||
self.transport_handler = transport.TrezorTransport()
|
||||
self.device_manager().register_enumerate_func(self.enumerate)
|
||||
|
||||
def enumerate(self):
|
||||
from trezorlib.device import TrezorDevice
|
||||
return [Device(d.get_path(), -1, d.get_path(), 'TREZOR', 0) for d in TrezorDevice.enumerate()]
|
||||
devices = self.transport_handler.enumerate_devices()
|
||||
return [Device(d.get_path(), -1, d.get_path(), 'TREZOR', 0) for d in devices]
|
||||
|
||||
def create_client(self, device, handler):
|
||||
from trezorlib.device import TrezorDevice
|
||||
try:
|
||||
self.print_error("connecting to device at", device.path)
|
||||
transport = TrezorDevice.find_by_path(device.path)
|
||||
transport = self.transport_handler.get_transport(device.path)
|
||||
except BaseException as e:
|
||||
self.print_error("cannot connect at", device.path, str(e))
|
||||
return None
|
||||
|
@ -379,56 +377,86 @@ class TrezorPlugin(HW_PluginBase):
|
|||
return inputs
|
||||
|
||||
def tx_outputs(self, derivation, tx, script_gen=SCRIPT_GEN_LEGACY):
|
||||
|
||||
def create_output_by_derivation(info):
|
||||
index, xpubs, m = info
|
||||
if len(xpubs) == 1:
|
||||
if script_gen == SCRIPT_GEN_NATIVE_SEGWIT:
|
||||
script_type = self.types.OutputScriptType.PAYTOWITNESS
|
||||
elif script_gen == SCRIPT_GEN_P2SH_SEGWIT:
|
||||
script_type = self.types.OutputScriptType.PAYTOP2SHWITNESS
|
||||
else:
|
||||
script_type = self.types.OutputScriptType.PAYTOADDRESS
|
||||
address_n = self.client_class.expand_path(derivation + "/%d/%d" % index)
|
||||
txoutputtype = self.types.TxOutputType(
|
||||
amount=amount,
|
||||
script_type=script_type,
|
||||
address_n=address_n,
|
||||
)
|
||||
else:
|
||||
if script_gen == SCRIPT_GEN_NATIVE_SEGWIT:
|
||||
script_type = self.types.OutputScriptType.PAYTOWITNESS
|
||||
elif script_gen == SCRIPT_GEN_P2SH_SEGWIT:
|
||||
script_type = self.types.OutputScriptType.PAYTOP2SHWITNESS
|
||||
else:
|
||||
script_type = self.types.OutputScriptType.PAYTOMULTISIG
|
||||
address_n = self.client_class.expand_path("/%d/%d" % index)
|
||||
nodes = map(self.ckd_public.deserialize, xpubs)
|
||||
pubkeys = [self.types.HDNodePathType(node=node, address_n=address_n) for node in nodes]
|
||||
multisig = self.types.MultisigRedeemScriptType(
|
||||
pubkeys=pubkeys,
|
||||
signatures=[b''] * len(pubkeys),
|
||||
m=m)
|
||||
txoutputtype = self.types.TxOutputType(
|
||||
multisig=multisig,
|
||||
amount=amount,
|
||||
address_n=self.client_class.expand_path(derivation + "/%d/%d" % index),
|
||||
script_type=script_type)
|
||||
return txoutputtype
|
||||
|
||||
def create_output_by_address():
|
||||
txoutputtype = self.types.TxOutputType()
|
||||
txoutputtype.amount = amount
|
||||
if _type == TYPE_SCRIPT:
|
||||
txoutputtype.script_type = self.types.OutputScriptType.PAYTOOPRETURN
|
||||
txoutputtype.op_return_data = address[2:]
|
||||
elif _type == TYPE_ADDRESS:
|
||||
txoutputtype.script_type = self.types.OutputScriptType.PAYTOADDRESS
|
||||
txoutputtype.address = address
|
||||
return txoutputtype
|
||||
|
||||
def is_any_output_on_change_branch():
|
||||
for _type, address, amount in tx.outputs():
|
||||
info = tx.output_info.get(address)
|
||||
if info is not None:
|
||||
index, xpubs, m = info
|
||||
if index[0] == 1:
|
||||
return True
|
||||
return False
|
||||
|
||||
outputs = []
|
||||
has_change = False
|
||||
any_output_on_change_branch = is_any_output_on_change_branch()
|
||||
|
||||
for _type, address, amount in tx.outputs():
|
||||
use_create_by_derivation = False
|
||||
|
||||
info = tx.output_info.get(address)
|
||||
if info is not None and not has_change:
|
||||
has_change = True # no more than one change address
|
||||
index, xpubs, m = info
|
||||
if len(xpubs) == 1:
|
||||
if script_gen == SCRIPT_GEN_NATIVE_SEGWIT:
|
||||
script_type = self.types.OutputScriptType.PAYTOWITNESS
|
||||
elif script_gen == SCRIPT_GEN_P2SH_SEGWIT:
|
||||
script_type = self.types.OutputScriptType.PAYTOP2SHWITNESS
|
||||
else:
|
||||
script_type = self.types.OutputScriptType.PAYTOADDRESS
|
||||
address_n = self.client_class.expand_path(derivation + "/%d/%d"%index)
|
||||
txoutputtype = self.types.TxOutputType(
|
||||
amount = amount,
|
||||
script_type = script_type,
|
||||
address_n = address_n,
|
||||
)
|
||||
else:
|
||||
if script_gen == SCRIPT_GEN_NATIVE_SEGWIT:
|
||||
script_type = self.types.OutputScriptType.PAYTOWITNESS
|
||||
elif script_gen == SCRIPT_GEN_P2SH_SEGWIT:
|
||||
script_type = self.types.OutputScriptType.PAYTOP2SHWITNESS
|
||||
else:
|
||||
script_type = self.types.OutputScriptType.PAYTOMULTISIG
|
||||
address_n = self.client_class.expand_path("/%d/%d"%index)
|
||||
nodes = map(self.ckd_public.deserialize, xpubs)
|
||||
pubkeys = [ self.types.HDNodePathType(node=node, address_n=address_n) for node in nodes]
|
||||
multisig = self.types.MultisigRedeemScriptType(
|
||||
pubkeys = pubkeys,
|
||||
signatures = [b''] * len(pubkeys),
|
||||
m = m)
|
||||
txoutputtype = self.types.TxOutputType(
|
||||
multisig = multisig,
|
||||
amount = amount,
|
||||
address_n = self.client_class.expand_path(derivation + "/%d/%d"%index),
|
||||
script_type = script_type)
|
||||
else:
|
||||
txoutputtype = self.types.TxOutputType()
|
||||
txoutputtype.amount = amount
|
||||
if _type == TYPE_SCRIPT:
|
||||
txoutputtype.script_type = self.types.OutputScriptType.PAYTOOPRETURN
|
||||
txoutputtype.op_return_data = address[2:]
|
||||
elif _type == TYPE_ADDRESS:
|
||||
txoutputtype.script_type = self.types.OutputScriptType.PAYTOADDRESS
|
||||
txoutputtype.address = address
|
||||
on_change_branch = index[0] == 1
|
||||
# prioritise hiding outputs on the 'change' branch from user
|
||||
# because no more than one change address allowed
|
||||
# note: ^ restriction can be removed once we require fw
|
||||
# that has https://github.com/trezor/trezor-mcu/pull/306
|
||||
if on_change_branch == any_output_on_change_branch:
|
||||
use_create_by_derivation = True
|
||||
has_change = True
|
||||
|
||||
if use_create_by_derivation:
|
||||
txoutputtype = create_output_by_derivation(info)
|
||||
else:
|
||||
txoutputtype = create_output_by_address()
|
||||
outputs.append(txoutputtype)
|
||||
|
||||
return outputs
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue