diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 000000000000..a16cedd7fda0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,23 @@ + +### Describe the issue + +### Is the issue reproducible? +#### List steps to reproduce below: +1. +2. +3. + +### Expected behavior +Tell us what should happen + +### Actual behavior +Tell us what happens instead + +### Any information in the debug.log file related to this issue? + +### Screenshots (if available) + +### What binary version was used (official or self compiled) + +### Machine specs: CPU, RAM, Disk space & OS (Windows, OS X, Linux) + diff --git a/.gitignore b/.gitignore index fd2d88159663..e9f38f982550 100644 --- a/.gitignore +++ b/.gitignore @@ -110,10 +110,9 @@ linux-build win32-build qa/pull-tester/run-bitcoind-for-test.sh qa/pull-tester/tests_config.py -qa/pull-tester/cache/* qa/pull-tester/test.*/* qa/tmp -cache/ +qa/cache/* share/BitcoindComparisonTool.jar !src/leveldb*/Makefile diff --git a/Makefile.am b/Makefile.am index 6405f10da5f0..b02b4db72ae2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -47,9 +47,9 @@ OSX_PACKAGING = $(OSX_DEPLOY_SCRIPT) $(OSX_FANCY_PLIST) $(OSX_INSTALLER_ICONS) \ $(top_srcdir)/contrib/macdeploy/detached-sig-apply.sh \ $(top_srcdir)/contrib/macdeploy/detached-sig-create.sh -COVERAGE_INFO = baseline_filtered_combined.info baseline.info block_test.info \ +COVERAGE_INFO = baseline_filtered_combined.info baseline.info \ leveldb_baseline.info test_dash_filtered.info total_coverage.info \ - baseline_filtered.info block_test_filtered.info rpc_test.info rpc_test_filtered.info \ + baseline_filtered.info rpc_test.info rpc_test_filtered.info \ leveldb_baseline_filtered.info test_dash_coverage.info test_dash.info dist-hook: @@ -175,16 +175,6 @@ test_dash.info: baseline_filtered_combined.info test_dash_filtered.info: test_dash.info $(LCOV) -r $< "/usr/include/*" -o $@ -block_test.info: test_dash_filtered.info - $(MKDIR_P) qa/tmp - -@TIMEOUT=15 qa/pull-tester/run-bitcoind-for-test.sh $(JAVA) -jar $(JAVA_COMPARISON_TOOL) qa/tmp/compTool $(COMPARISON_TOOL_REORG_TESTS) - $(LCOV) -c -d $(abs_builddir)/src --t BitcoinJBlockTest -o $@ - $(LCOV) -z -d $(abs_builddir)/src - $(LCOV) -z -d $(abs_builddir)/src/leveldb - -block_test_filtered.info: block_test.info - $(LCOV) -r $< "/usr/include/*" -o $@ - rpc_test.info: test_dash_filtered.info -@TIMEOUT=15 python qa/pull-tester/rpc-tests.py $(EXTENDED_RPC_TESTS) $(LCOV) -c -d $(abs_builddir)/src --t rpc-tests -o $@ @@ -197,8 +187,8 @@ rpc_test_filtered.info: rpc_test.info test_dash_coverage.info: baseline_filtered_combined.info test_dash_filtered.info $(LCOV) -a baseline_filtered.info -a leveldb_baseline_filtered.info -a test_dash_filtered.info -o $@ -total_coverage.info: baseline_filtered_combined.info test_dash_filtered.info block_test_filtered.info rpc_test_filtered.info - $(LCOV) -a baseline_filtered.info -a leveldb_baseline_filtered.info -a test_dash_filtered.info -a block_test_filtered.info -a rpc_test_filtered.info -o $@ | $(GREP) "\%" | $(AWK) '{ print substr($$3,2,50) "/" $$5 }' > coverage_percent.txt +total_coverage.info: baseline_filtered_combined.info test_dash_filtered.info rpc_test_filtered.info + $(LCOV) -a baseline_filtered.info -a leveldb_baseline_filtered.info -a test_dash_filtered.info -a rpc_test_filtered.info -o $@ | $(GREP) "\%" | $(AWK) '{ print substr($$3,2,50) "/" $$5 }' > coverage_percent.txt test_dash.coverage/.dirstamp: test_dash_coverage.info $(GENHTML) -s $< -o $(@D) @@ -212,14 +202,6 @@ cov: test_dash.coverage/.dirstamp total.coverage/.dirstamp endif -# Dash: disable the test until upstream switches from convoluted comparison tool -# Ref https://github.com/bitcoin/bitcoin/issues/4545 -if USE_COMPARISON_TOOL -check-local: - ## $(MKDIR_P) qa/tmp - ## @qa/pull-tester/run-bitcoind-for-test.sh $(JAVA) -jar $(JAVA_COMPARISON_TOOL) qa/tmp/compTool $(COMPARISON_TOOL_REORG_TESTS) 2>&1 -endif - dist_noinst_SCRIPTS = autogen.sh EXTRA_DIST = $(top_srcdir)/share/genbuild.sh qa/pull-tester/rpc-tests.py qa/rpc-tests $(DIST_DOCS) $(WINDOWS_PACKAGING) $(OSX_PACKAGING) $(BIN_CHECKS) diff --git a/configure.ac b/configure.ac index d57d3fc27825..6ef5b4c4d5a8 100644 --- a/configure.ac +++ b/configure.ac @@ -75,7 +75,6 @@ AC_PATH_TOOL(RANLIB, ranlib) AC_PATH_TOOL(STRIP, strip) AC_PATH_TOOL(GCOV, gcov) AC_PATH_PROG(LCOV, lcov) -AC_PATH_PROG(JAVA, java) AC_PATH_PROGS([PYTHON], [python3 python2.7 python2 python]) AC_PATH_PROG(GENHTML, genhtml) AC_PATH_PROG([GIT], [git]) @@ -122,16 +121,6 @@ AC_ARG_ENABLE(bench, [use_bench=$enableval], [use_bench=yes]) -AC_ARG_WITH([comparison-tool], - AS_HELP_STRING([--with-comparison-tool],[path to java comparison tool (requires --enable-tests)]), - [use_comparison_tool=$withval], - [use_comparison_tool=no]) - -AC_ARG_ENABLE([comparison-tool-reorg-tests], - AS_HELP_STRING([--enable-comparison-tool-reorg-tests],[enable expensive reorg tests in the comparison tool (default no)]), - [use_comparison_tool_reorg_tests=$enableval], - [use_comparison_tool_reorg_tests=no]) - AC_ARG_ENABLE([extended-rpc-tests], AS_HELP_STRING([--enable-extended-rpc-tests],[enable expensive RPC tests when using lcov (default no)]), [use_extended_rpc_tests=$enableval], @@ -392,19 +381,6 @@ if test x$use_pkgconfig = xyes; then ]) fi -if test x$use_comparison_tool != xno; then - AC_SUBST(JAVA_COMPARISON_TOOL, $use_comparison_tool) -fi - -if test x$use_comparison_tool_reorg_tests != xno; then - if test x$use_comparison_tool = x; then - AC_MSG_ERROR("comparison tool reorg tests but comparison tool was not specified") - fi - AC_SUBST(COMPARISON_TOOL_REORG_TESTS, 1) -else - AC_SUBST(COMPARISON_TOOL_REORG_TESTS, 0) -fi - if test x$use_extended_rpc_tests != xno; then AC_SUBST(EXTENDED_RPC_TESTS, -extended) fi @@ -416,18 +392,12 @@ if test x$use_lcov = xyes; then if test x$GCOV = x; then AC_MSG_ERROR("lcov testing requested but gcov not found") fi - if test x$JAVA = x; then - AC_MSG_ERROR("lcov testing requested but java not found") - fi if test x$PYTHON = x; then AC_MSG_ERROR("lcov testing requested but python not found") fi if test x$GENHTML = x; then AC_MSG_ERROR("lcov testing requested but genhtml not found") fi - if test x$use_comparison_tool = x; then - AC_MSG_ERROR("lcov testing requested but comparison tool was not specified") - fi LCOV="$LCOV --gcov-tool=$GCOV" AX_CHECK_COMPILE_FLAG([--coverage],[CXXFLAGS="$CXXFLAGS --coverage"], [AC_MSG_ERROR("lcov testing requested but --coverage flag does not work")]) @@ -826,6 +796,15 @@ else AC_DEFINE_UNQUOTED([ENABLE_ZMQ],[0],[Define to 1 to enable ZMQ functions]) fi + if test "x$use_zmq" = "xyes"; then + dnl Assume libzmq was built for static linking + case $host in + *mingw*) + ZMQ_CFLAGS="$ZMQ_CFLAGS -DZMQ_STATIC" + ;; + esac + fi + BITCOIN_QT_CHECK(AC_CHECK_LIB([protobuf] ,[main],[PROTOBUF_LIBS=-lprotobuf], BITCOIN_QT_FAIL(libprotobuf not found))) if test x$use_qr != xno; then BITCOIN_QT_CHECK([AC_CHECK_LIB([qrencode], [main],[QR_LIBS=-lqrencode], [have_qrencode=no])]) @@ -835,6 +814,12 @@ fi dnl univalue check +need_bundled_univalue=yes + +if test x$build_bitcoin_utils$build_bitcoind$bitcoin_enable_qt$use_tests$use_bench = xnonononono; then + need_bundled_univalue=no +else + if test x$system_univalue != xno ; then found_univalue=no if test x$use_pkgconfig = xyes; then @@ -856,6 +841,7 @@ if test x$system_univalue != xno ; then if test x$found_univalue = xyes ; then system_univalue=yes + need_bundled_univalue=no elif test x$system_univalue = xyes ; then AC_MSG_ERROR([univalue not found]) else @@ -863,22 +849,17 @@ if test x$system_univalue != xno ; then fi fi -if test x$system_univalue = xno ; then +if test x$need_bundled_univalue = xyes ; then UNIVALUE_CFLAGS='-I$(srcdir)/univalue/include' UNIVALUE_LIBS='univalue/libunivalue.la' fi -AM_CONDITIONAL([EMBEDDED_UNIVALUE],[test x$system_univalue = xno]) + +fi + +AM_CONDITIONAL([EMBEDDED_UNIVALUE],[test x$need_bundled_univalue = xyes]) AC_SUBST(UNIVALUE_CFLAGS) AC_SUBST(UNIVALUE_LIBS) -CXXFLAGS_TEMP="$CXXFLAGS" -LIBS_TEMP="$LIBS" -CXXFLAGS="$CXXFLAGS $SSL_CFLAGS $CRYPTO_CFLAGS" -LIBS="$LIBS $SSL_LIBS $CRYPTO_LIBS" -AC_CHECK_HEADER([openssl/ec.h],, AC_MSG_ERROR(OpenSSL ec header missing),) -CXXFLAGS="$CXXFLAGS_TEMP" -LIBS="$LIBS_TEMP" - BITCOIN_QT_PATH_PROGS([PROTOC], [protoc],$protoc_bin_path) AC_MSG_CHECKING([whether to build dashd]) @@ -1013,8 +994,8 @@ else AC_MSG_RESULT([no]) fi -if test x$build_bitcoin_utils$build_bitcoin_libs$build_bitcoind$bitcoin_enable_qt$use_tests = xnonononono; then - AC_MSG_ERROR([No targets! Please specify at least one of: --with-utils --with-libs --with-daemon --with-gui or --enable-tests]) +if test x$build_bitcoin_utils$build_bitcoin_libs$build_bitcoind$bitcoin_enable_qt$use_bench$use_tests = xnononononono; then + AC_MSG_ERROR([No targets! Please specify at least one of: --with-utils --with-libs --with-daemon --with-gui --enable-bench or --enable-tests]) fi AM_CONDITIONAL([TARGET_DARWIN], [test x$TARGET_OS = xdarwin]) @@ -1027,8 +1008,6 @@ AM_CONDITIONAL([ENABLE_QT_TESTS],[test x$BUILD_TEST_QT = xyes]) AM_CONDITIONAL([ENABLE_BENCH],[test x$use_bench = xyes]) AM_CONDITIONAL([USE_QRCODE], [test x$use_qr = xyes]) AM_CONDITIONAL([USE_LCOV],[test x$use_lcov = xyes]) -AM_CONDITIONAL([USE_COMPARISON_TOOL],[test x$use_comparison_tool != xno]) -AM_CONDITIONAL([USE_COMPARISON_TOOL_REORG_TESTS],[test x$use_comparison_tool_reorg_test != xno]) AM_CONDITIONAL([GLIBC_BACK_COMPAT],[test x$use_glibc_compat = xyes]) AM_CONDITIONAL([HARDEN],[test x$use_hardening = xyes]) @@ -1106,7 +1085,7 @@ PKGCONFIG_LIBDIR_TEMP="$PKG_CONFIG_LIBDIR" unset PKG_CONFIG_LIBDIR PKG_CONFIG_LIBDIR="$PKGCONFIG_LIBDIR_TEMP" -if test x$system_univalue = xno; then +if test x$need_bundled_univalue = xyes; then AC_CONFIG_SUBDIRS([src/univalue]) fi diff --git a/contrib/debian/copyright b/contrib/debian/copyright index 517a884acb2f..2cd065cb45ed 100644 --- a/contrib/debian/copyright +++ b/contrib/debian/copyright @@ -62,6 +62,10 @@ Files: src/qt/res/icons/tx_mined.png src/qt/res/src/mine.svg src/qt/res/icons/fontbigger.png src/qt/res/icons/fontsmaller.png + src/qt/res/icons/*/hd_disabled.png + src/qt/res/src/hd_disabled.svg + src/qt/res/icons/*/hd_enabled.png + src/qt/res/src/hd_enabled.svg Copyright: Jonas Schnelli License: Expat Comment: diff --git a/contrib/devtools/security-check.py b/contrib/devtools/security-check.py index 301fea85c1f6..c61d65264176 100755 --- a/contrib/devtools/security-check.py +++ b/contrib/devtools/security-check.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/env python ''' Perform basic ELF security checks on a series of executables. Exit status will be 0 if successful, and the program will be silent. diff --git a/contrib/devtools/symbol-check.py b/contrib/devtools/symbol-check.py index e26c0fbb9445..8f8685006e11 100755 --- a/contrib/devtools/symbol-check.py +++ b/contrib/devtools/symbol-check.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/env python # Copyright (c) 2014 Wladimir J. van der Laan # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/contrib/devtools/test-security-check.py b/contrib/devtools/test-security-check.py index fed7626aabaa..324b7bcd85ae 100755 --- a/contrib/devtools/test-security-check.py +++ b/contrib/devtools/test-security-check.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/env python2 ''' Test script for security-check.py ''' diff --git a/contrib/devtools/update-translations.py b/contrib/devtools/update-translations.py index 96fc41c99d03..ccdcc9f84ef4 100755 --- a/contrib/devtools/update-translations.py +++ b/contrib/devtools/update-translations.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # Copyright (c) 2014 Wladimir J. van der Laan # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/contrib/linearize/linearize-data.py b/contrib/linearize/linearize-data.py index 69eb9e1d9f43..d57c59904d7d 100755 --- a/contrib/linearize/linearize-data.py +++ b/contrib/linearize/linearize-data.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # # linearize-data.py: Construct a linear, no-fork version of the chain. # diff --git a/contrib/linearize/linearize-hashes.py b/contrib/linearize/linearize-hashes.py index ee87ceafb1b4..4f81241b44af 100755 --- a/contrib/linearize/linearize-hashes.py +++ b/contrib/linearize/linearize-hashes.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # # linearize-hashes.py: List blocks in a linear, no-fork version of the chain. # diff --git a/contrib/seeds/generate-seeds.py b/contrib/seeds/generate-seeds.py index 5df20ea1378a..3d0dcbee9754 100755 --- a/contrib/seeds/generate-seeds.py +++ b/contrib/seeds/generate-seeds.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # Copyright (c) 2014 Wladimir J. van der Laan # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/depends/config.site.in b/depends/config.site.in index e731537bf76b..27e3aedd8ed4 100644 --- a/depends/config.site.in +++ b/depends/config.site.in @@ -19,9 +19,6 @@ fi if test -z $with_protoc_bindir; then with_protoc_bindir=$depends_prefix/native/bin fi -if test -z $with_comparison_tool; then - with_comparison_tool=$depends_prefix/native/share/BitcoindComparisonTool_jar/BitcoindComparisonTool.jar -fi if test -z $enable_wallet && test -n "@no_wallet@"; then diff --git a/depends/packages/native_comparisontool.mk b/depends/packages/native_comparisontool.mk deleted file mode 100644 index e0ae0cec700e..000000000000 --- a/depends/packages/native_comparisontool.mk +++ /dev/null @@ -1,21 +0,0 @@ -package=native_comparisontool -$(package)_version=8c6666f -$(package)_download_path=https://github.com/theuni/bitcoind-comparisontool/raw/master -$(package)_file_name=pull-tests-$($(package)_version).jar -$(package)_sha256_hash=a865332b3827abcde684ab79f5f43c083b0b6a4c97ff5508c79f29fee24f11cd -$(package)_install_dirname=BitcoindComparisonTool_jar -$(package)_install_filename=BitcoindComparisonTool.jar - -define $(package)_extract_cmds -endef - -define $(package)_configure_cmds -endef - -define $(package)_build_cmds -endef - -define $(package)_stage_cmds - mkdir -p $($(package)_staging_prefix_dir)/share/$($(package)_install_dirname) && \ - cp $($(package)_source) $($(package)_staging_prefix_dir)/share/$($(package)_install_dirname)/$($(package)_install_filename) -endef diff --git a/depends/packages/packages.mk b/depends/packages/packages.mk index ac43ef4a2e30..4cf44385b88d 100644 --- a/depends/packages/packages.mk +++ b/depends/packages/packages.mk @@ -1,7 +1,5 @@ -packages:=boost openssl libevent -darwin_packages:=zeromq -linux_packages:=zeromq -native_packages := native_ccache native_comparisontool +packages:=boost openssl libevent zeromq +native_packages := native_ccache qt_native_packages = native_protobuf qt_packages = qrencode protobuf @@ -12,7 +10,6 @@ qt_i686_linux_packages:=$(qt_x86_64_linux_packages) qt_darwin_packages=qt qt_mingw32_packages=qt - wallet_packages=bdb upnp_packages=miniupnpc diff --git a/depends/packages/zeromq.mk b/depends/packages/zeromq.mk index f8901f72c26c..01146c26f69b 100644 --- a/depends/packages/zeromq.mk +++ b/depends/packages/zeromq.mk @@ -1,15 +1,22 @@ package=zeromq -$(package)_version=4.1.4 -$(package)_download_path=http://download.zeromq.org +$(package)_version=4.1.5 +$(package)_download_path=https://github.com/zeromq/zeromq4-1/releases/download/v$($(package)_version)/ $(package)_file_name=$(package)-$($(package)_version).tar.gz -$(package)_sha256_hash=e99f44fde25c2e4cb84ce440f87ca7d3fe3271c2b8cfbc67d55e4de25e6fe378 +$(package)_sha256_hash=04aac57f081ffa3a2ee5ed04887be9e205df3a7ddade0027460b8042432bdbcf +$(package)_patches=9114d3957725acd34aa8b8d011585812f3369411.patch 9e6745c12e0b100cd38acecc16ce7db02905e27c.patch define $(package)_set_vars - $(package)_config_opts=--without-documentation --disable-shared --without-libsodium + $(package)_config_opts=--without-documentation --disable-shared --without-libsodium --disable-curve $(package)_config_opts_linux=--with-pic $(package)_cxxflags=-std=c++11 endef +define $(package)_preprocess_cmds + patch -p1 < $($(package)_patch_dir)/9114d3957725acd34aa8b8d011585812f3369411.patch && \ + patch -p1 < $($(package)_patch_dir)/9e6745c12e0b100cd38acecc16ce7db02905e27c.patch && \ + ./autogen.sh +endef + define $(package)_config_cmds $($(package)_autoconf) endef diff --git a/depends/patches/zeromq/9114d3957725acd34aa8b8d011585812f3369411.patch b/depends/patches/zeromq/9114d3957725acd34aa8b8d011585812f3369411.patch new file mode 100644 index 000000000000..f704b3d94fda --- /dev/null +++ b/depends/patches/zeromq/9114d3957725acd34aa8b8d011585812f3369411.patch @@ -0,0 +1,22 @@ +From 9114d3957725acd34aa8b8d011585812f3369411 Mon Sep 17 00:00:00 2001 +From: Jeroen Ooms +Date: Tue, 20 Oct 2015 13:10:38 +0200 +Subject: [PATCH] enable static libraries on mingw + +--- + configure.ac | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/configure.ac b/configure.ac +index 393505b..e92131a 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -265,7 +265,7 @@ case "${host_os}" in + libzmq_dso_visibility="no" + + if test "x$enable_static" = "xyes"; then +- AC_MSG_ERROR([Building static libraries is not supported under MinGW32]) ++ CPPFLAGS="-DZMQ_STATIC" + fi + + # Set FD_SETSIZE to 1024 \ No newline at end of file diff --git a/depends/patches/zeromq/9e6745c12e0b100cd38acecc16ce7db02905e27c.patch b/depends/patches/zeromq/9e6745c12e0b100cd38acecc16ce7db02905e27c.patch new file mode 100644 index 000000000000..9aff2c179a19 --- /dev/null +++ b/depends/patches/zeromq/9e6745c12e0b100cd38acecc16ce7db02905e27c.patch @@ -0,0 +1,22 @@ +From 9e6745c12e0b100cd38acecc16ce7db02905e27c Mon Sep 17 00:00:00 2001 +From: David Millard +Date: Tue, 10 May 2016 13:53:53 -0700 +Subject: [PATCH] Fix autotools for static MinGW builds + +--- + configure.ac | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/configure.ac b/configure.ac +index 5a0fa14..def6ea7 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -259,7 +259,7 @@ case "${host_os}" in + libzmq_dso_visibility="no" + + if test "x$enable_static" = "xyes"; then +- CPPFLAGS="-DZMQ_STATIC" ++ CPPFLAGS="-DZMQ_STATIC $CPPFLAGS" + fi + + # Set FD_SETSIZE to 1024 \ No newline at end of file diff --git a/doc/README_osx.md b/doc/README_osx.md index aed3cd97e1ec..6a5c672277cf 100644 --- a/doc/README_osx.md +++ b/doc/README_osx.md @@ -22,7 +22,7 @@ These tools inject timestamps by default, which produce non-deterministic binaries. The ZERO_AR_DATE environment variable is used to disable that. This version of cctools has been patched to use the current version of clang's -headers and and its libLTO.so rather than those from llvmgcc, as it was +headers and its libLTO.so rather than those from llvmgcc, as it was originally done in toolchain4. To complicate things further, all builds must target an Apple SDK. These SDKs diff --git a/doc/gitian-building.md b/doc/gitian-building.md index ef63605e326c..67252e5bb7eb 100644 --- a/doc/gitian-building.md +++ b/doc/gitian-building.md @@ -55,7 +55,7 @@ In the VirtualBox GUI click "New" and choose the following parameters in the wiz ![](gitian-building/create_vm_memsize.png) -- Memory Size: at least 1024MB, anything less will really slow down the build. +- Memory Size: at least 3000MB, anything less and the build might not complete. ![](gitian-building/create_vm_hard_disk.png) diff --git a/doc/gitian-building/create_vm_memsize.png b/doc/gitian-building/create_vm_memsize.png index 5abfee533705..6f42cda73f3c 100644 Binary files a/doc/gitian-building/create_vm_memsize.png and b/doc/gitian-building/create_vm_memsize.png differ diff --git a/doc/release-process.md b/doc/release-process.md index bc8dacc64011..bf4f6fb65444 100644 --- a/doc/release-process.md +++ b/doc/release-process.md @@ -82,16 +82,16 @@ The gbuild invocations below DO NOT DO THIS by default. ### Build and sign Dash Core for Linux, Windows, and OS X: - ./bin/gbuild --commit dash=v${VERSION} ../dash/contrib/gitian-descriptors/gitian-linux.yml + ./bin/gbuild --memory 3000 --commit dash=v${VERSION} ../dash/contrib/gitian-descriptors/gitian-linux.yml ./bin/gsign --signer $SIGNER --release ${VERSION}-linux --destination ../gitian.sigs/ ../dash/contrib/gitian-descriptors/gitian-linux.yml mv build/out/dash-*.tar.gz build/out/src/dash-*.tar.gz ../ - ./bin/gbuild --commit dash=v${VERSION} ../dash/contrib/gitian-descriptors/gitian-win.yml + ./bin/gbuild --memory 3000 --commit dash=v${VERSION} ../dash/contrib/gitian-descriptors/gitian-win.yml ./bin/gsign --signer $SIGNER --release ${VERSION}-win-unsigned --destination ../gitian.sigs/ ../dash/contrib/gitian-descriptors/gitian-win.yml mv build/out/dash-*-win-unsigned.tar.gz inputs/dash-win-unsigned.tar.gz mv build/out/dash-*.zip build/out/dash-*.exe ../ - ./bin/gbuild --commit dash=v${VERSION} ../dash/contrib/gitian-descriptors/gitian-osx.yml + ./bin/gbuild --memory 3000 --commit dash=v${VERSION} ../dash/contrib/gitian-descriptors/gitian-osx.yml ./bin/gsign --signer $SIGNER --release ${VERSION}-osx-unsigned --destination ../gitian.sigs/ ../dash/contrib/gitian-descriptors/gitian-osx.yml mv build/out/dash-*-osx-unsigned.tar.gz inputs/dash-osx-unsigned.tar.gz mv build/out/dash-*.tar.gz build/out/dash-*.dmg ../ diff --git a/doc/translation_process.md b/doc/translation_process.md index 4c2cc910472c..beac60e3f57b 100644 --- a/doc/translation_process.md +++ b/doc/translation_process.md @@ -95,7 +95,7 @@ When new plurals are added to the source file, it's important to do the followin 7. Save the source file ### Translating a new language -To create a new language template, you will need to edit the languages manifest file `src/qt/bitcoin.qrc` and add a new entry. Below is an example of the english language entry. +To create a new language template, you will need to edit the languages manifest file `src/qt/dash.qrc` and add a new entry. Below is an example of the English language entry. ```xml diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index 92ded914725b..c741f20c0884 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -86,8 +86,6 @@ #Set env vars if "DASHD" not in os.environ: os.environ["DASHD"] = BUILDDIR + '/src/dashd' + EXEEXT -if "DASHCLI" not in os.environ: - os.environ["DASHCLI"] = BUILDDIR + '/src/dash-cli' + EXEEXT if EXEEXT == ".exe" and "-win" not in opts: # https://github.com/bitcoin/bitcoin/commit/d52802551752140cf41f0d9a225a43e84404d3e9 @@ -146,6 +144,7 @@ 'sendheaders.py', # NOTE: needs dash_hash to pass 'keypool.py', 'keypool-hd.py', + 'p2p-mempool.py', 'prioritise_transaction.py', 'invalidblockrequest.py', # NOTE: needs dash_hash to pass 'invalidtxrequest.py', # NOTE: needs dash_hash to pass @@ -170,7 +169,7 @@ 'txn_clone.py --mineblock', 'forknotify.py', 'invalidateblock.py', -# 'rpcbind_test.py', #temporary, bug in libevent, see #6655 + 'rpcbind_test.py', 'smartfees.py', 'maxblocksinflight.py', 'p2p-acceptblock.py', # NOTE: needs dash_hash to pass @@ -204,6 +203,7 @@ def runtests(): coverage = RPCCoverage() print("Initializing coverage directory at %s\n" % coverage.dir) flags = ["--srcdir=%s/src" % BUILDDIR] + passon_args + flags.append("--cachedir=%s/qa/cache" % BUILDDIR) if coverage: flags.append(coverage.flag) diff --git a/qa/rpc-tests/abandonconflict.py b/qa/rpc-tests/abandonconflict.py index c50c3cc562e5..874df487777c 100755 --- a/qa/rpc-tests/abandonconflict.py +++ b/qa/rpc-tests/abandonconflict.py @@ -68,7 +68,7 @@ def run_test(self): # In mempool txs from self should increase balance from change newbalance = self.nodes[0].getbalance() - assert(newbalance == balance - Decimal("30") + Decimal("24.9996")) + assert_equal(newbalance, balance - Decimal("30") + Decimal("24.9996")) balance = newbalance # Restart the node with a higher min relay fee so the parent tx is no longer in mempool @@ -78,16 +78,16 @@ def run_test(self): self.nodes[0]=start_node(0, self.options.tmpdir, ["-debug","-logtimemicros","-minrelaytxfee=0.0001"]) # Verify txs no longer in mempool - assert(len(self.nodes[0].getrawmempool()) == 0) + assert_equal(len(self.nodes[0].getrawmempool()), 0) # Not in mempool txs from self should only reduce balance # inputs are still spent, but change not received newbalance = self.nodes[0].getbalance() - assert(newbalance == balance - Decimal("24.9996")) + assert_equal(newbalance, balance - Decimal("24.9996")) # Unconfirmed received funds that are not in mempool, also shouldn't show # up in unconfirmed balance unconfbalance = self.nodes[0].getunconfirmedbalance() + self.nodes[0].getbalance() - assert(unconfbalance == newbalance) + assert_equal(unconfbalance, newbalance) # Also shouldn't show up in listunspent assert(not txABC2 in [utxo["txid"] for utxo in self.nodes[0].listunspent(0)]) balance = newbalance @@ -96,35 +96,35 @@ def run_test(self): # including that the child tx was also abandoned self.nodes[0].abandontransaction(txAB1) newbalance = self.nodes[0].getbalance() - assert(newbalance == balance + Decimal("30")) + assert_equal(newbalance, balance + Decimal("30")) balance = newbalance # Verify that even with a low min relay fee, the tx is not reaccepted from wallet on startup once abandoned stop_node(self.nodes[0],0) self.nodes[0]=start_node(0, self.options.tmpdir, ["-debug","-logtimemicros","-minrelaytxfee=0.00001"]) - assert(len(self.nodes[0].getrawmempool()) == 0) - assert(self.nodes[0].getbalance() == balance) + assert_equal(len(self.nodes[0].getrawmempool()), 0) + assert_equal(self.nodes[0].getbalance(), balance) # But if its received again then it is unabandoned # And since now in mempool, the change is available # But its child tx remains abandoned self.nodes[0].sendrawtransaction(signed["hex"]) newbalance = self.nodes[0].getbalance() - assert(newbalance == balance - Decimal("20") + Decimal("14.99998")) + assert_equal(newbalance, balance - Decimal("20") + Decimal("14.99998")) balance = newbalance # Send child tx again so its unabandoned self.nodes[0].sendrawtransaction(signed2["hex"]) newbalance = self.nodes[0].getbalance() - assert(newbalance == balance - Decimal("10") - Decimal("14.99998") + Decimal("24.9996")) + assert_equal(newbalance, balance - Decimal("10") - Decimal("14.99998") + Decimal("24.9996")) balance = newbalance # Remove using high relay fee again stop_node(self.nodes[0],0) self.nodes[0]=start_node(0, self.options.tmpdir, ["-debug","-logtimemicros","-minrelaytxfee=0.0001"]) - assert(len(self.nodes[0].getrawmempool()) == 0) + assert_equal(len(self.nodes[0].getrawmempool()), 0) newbalance = self.nodes[0].getbalance() - assert(newbalance == balance - Decimal("24.9996")) + assert_equal(newbalance, balance - Decimal("24.9996")) balance = newbalance # Create a double spend of AB1 by spending again from only A's 10 output @@ -143,7 +143,7 @@ def run_test(self): # Verify that B and C's 10 BTC outputs are available for spending again because AB1 is now conflicted newbalance = self.nodes[0].getbalance() - assert(newbalance == balance + Decimal("20")) + assert_equal(newbalance, balance + Decimal("20")) balance = newbalance # There is currently a minor bug around this and so this test doesn't work. See Issue #7315 @@ -151,7 +151,7 @@ def run_test(self): # Don't think C's should either self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash()) newbalance = self.nodes[0].getbalance() - #assert(newbalance == balance - Decimal("10")) + #assert_equal(newbalance, balance - Decimal("10")) print("If balance has not declined after invalidateblock then out of mempool wallet tx which is no longer") print("conflicted has not resumed causing its inputs to be seen as spent. See Issue #7315") print(str(balance) + " -> " + str(newbalance) + " ?") diff --git a/qa/rpc-tests/importprunedfunds.py b/qa/rpc-tests/importprunedfunds.py index 562b5fe6eaf7..24dc4ef1d3a2 100755 --- a/qa/rpc-tests/importprunedfunds.py +++ b/qa/rpc-tests/importprunedfunds.py @@ -5,7 +5,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * -import decimal + class ImportPrunedFundsTest(BitcoinTestFramework): diff --git a/qa/rpc-tests/p2p-feefilter.py b/qa/rpc-tests/p2p-feefilter.py index cd0501a3140c..96d99d38a73c 100755 --- a/qa/rpc-tests/p2p-feefilter.py +++ b/qa/rpc-tests/p2p-feefilter.py @@ -62,6 +62,7 @@ def setup_network(self): def run_test(self): node1 = self.nodes[1] + node0 = self.nodes[0] # Get out of IBD node1.generate(1) sync_blocks(self.nodes) @@ -91,8 +92,17 @@ def run_test(self): node1.settxfee(Decimal("0.00010000")) [node1.sendtoaddress(node1.getnewaddress(), 1) for x in range(3)] sync_mempools(self.nodes) # must be sure node 0 has received all txs - time.sleep(10) # wait 10 secs to be sure its doesn't relay any - assert(allInvsMatch([], test_node)) + + # Send one transaction from node0 that should be received, so that we + # we can sync the test on receipt (if node1's txs were relayed, they'd + # be received by the time this node0 tx is received). This is + # unfortunately reliant on the current relay behavior where we batch up + # to 35 entries in an inv, which means that when this next transaction + # is eligible for relay, the prior transactions from node1 are eligible + # as well. + node0.settxfee(Decimal("0.00020000")) + txids = [node0.sendtoaddress(node0.getnewaddress(), 1)] + assert(allInvsMatch(txids, test_node)) test_node.clear_invs() # Remove fee filter and check that txs are received again diff --git a/qa/rpc-tests/p2p-fullblocktest.py b/qa/rpc-tests/p2p-fullblocktest.py index 501a3ff97291..dd35aeb53c8e 100755 --- a/qa/rpc-tests/p2p-fullblocktest.py +++ b/qa/rpc-tests/p2p-fullblocktest.py @@ -25,9 +25,6 @@ def __init__(self, tx = CTransaction(), n = -1): each test. ''' -def hash160(s): - return hashlib.new('ripemd160', sha256(s)).digest() - # Use this class for tests that require behavior other than normal "mininode" behavior. # For now, it is used to serialize a bloated varint (b64). class CBrokenBlock(CBlock): diff --git a/qa/rpc-tests/p2p-mempool.py b/qa/rpc-tests/p2p-mempool.py index 5d2daf39f828..5c5d778f429b 100755 --- a/qa/rpc-tests/p2p-mempool.py +++ b/qa/rpc-tests/p2p-mempool.py @@ -72,8 +72,11 @@ def send_mempool(self): self.send_message(msg_mempool()) class P2PMempoolTests(BitcoinTestFramework): - def setup_chain(self): - initialize_chain_clean(self.options.tmpdir, 2) + + def __init__(self): + super().__init__() + self.setup_clean_chain = True + self.num_nodes = 2 def setup_network(self): # Start a node with maxuploadtarget of 200 MB (/24h) diff --git a/qa/rpc-tests/rpcbind_test.py b/qa/rpc-tests/rpcbind_test.py index 572273566b63..3ac32140ba89 100755 --- a/qa/rpc-tests/rpcbind_test.py +++ b/qa/rpc-tests/rpcbind_test.py @@ -5,143 +5,106 @@ # Test for -rpcbind, as well as -rpcallowip and -rpcconnect -# TODO extend this test from the test framework (like all other tests) - -import tempfile -import traceback - +from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * from test_framework.netutil import * -def run_bind_test(tmpdir, allow_ips, connect_to, addresses, expected): - ''' - Start a node with requested rpcallowip and rpcbind parameters, - then try to connect, and check if the set of bound addresses - matches the expected set. - ''' - expected = [(addr_to_hex(addr), port) for (addr, port) in expected] - base_args = ['-disablewallet', '-nolisten'] - if allow_ips: - base_args += ['-rpcallowip=' + x for x in allow_ips] - binds = ['-rpcbind='+addr for addr in addresses] - nodes = start_nodes(self.num_nodes, tmpdir, [base_args + binds], connect_to) - try: - pid = bitcoind_processes[0].pid - assert_equal(set(get_bind_addrs(pid)), set(expected)) - finally: - stop_nodes(nodes) - wait_bitcoinds() - -def run_allowip_test(tmpdir, allow_ips, rpchost, rpcport): - ''' - Start a node with rpcwallow IP, and request getinfo - at a non-localhost IP. - ''' - base_args = ['-disablewallet', '-nolisten'] + ['-rpcallowip='+x for x in allow_ips] - nodes = start_nodes(self.num_nodes, tmpdir, [base_args]) - try: - # connect to node through non-loopback interface - url = "http://rt:rt@%s:%d" % (rpchost, rpcport,) - node = get_rpc_proxy(url, 1) - node.getinfo() - finally: - node = None # make sure connection will be garbage collected and closed - stop_nodes(nodes) - wait_bitcoinds() - -def run_test(tmpdir): - assert(sys.platform.startswith('linux')) # due to OS-specific network stats queries, this test works only on Linux - # find the first non-loopback interface for testing - non_loopback_ip = None - for name,ip in all_interfaces(): - if ip != '127.0.0.1': - non_loopback_ip = ip - break - if non_loopback_ip is None: - assert(not 'This test requires at least one non-loopback IPv4 interface') - print("Using interface %s for testing" % non_loopback_ip) +class RPCBindTest(BitcoinTestFramework): - defaultport = rpc_port(0) + def __init__(self): + super().__init__() + self.setup_clean_chain = True + self.num_nodes = 1 - # check default without rpcallowip (IPv4 and IPv6 localhost) - run_bind_test(tmpdir, None, '127.0.0.1', [], - [('127.0.0.1', defaultport), ('::1', defaultport)]) - # check default with rpcallowip (IPv6 any) - run_bind_test(tmpdir, ['127.0.0.1'], '127.0.0.1', [], - [('::0', defaultport)]) - # check only IPv4 localhost (explicit) - run_bind_test(tmpdir, ['127.0.0.1'], '127.0.0.1', ['127.0.0.1'], - [('127.0.0.1', defaultport)]) - # check only IPv4 localhost (explicit) with alternative port - run_bind_test(tmpdir, ['127.0.0.1'], '127.0.0.1:32171', ['127.0.0.1:32171'], - [('127.0.0.1', 32171)]) - # check only IPv4 localhost (explicit) with multiple alternative ports on same host - run_bind_test(tmpdir, ['127.0.0.1'], '127.0.0.1:32171', ['127.0.0.1:32171', '127.0.0.1:32172'], - [('127.0.0.1', 32171), ('127.0.0.1', 32172)]) - # check only IPv6 localhost (explicit) - run_bind_test(tmpdir, ['[::1]'], '[::1]', ['[::1]'], - [('::1', defaultport)]) - # check both IPv4 and IPv6 localhost (explicit) - run_bind_test(tmpdir, ['127.0.0.1'], '127.0.0.1', ['127.0.0.1', '[::1]'], - [('127.0.0.1', defaultport), ('::1', defaultport)]) - # check only non-loopback interface - run_bind_test(tmpdir, [non_loopback_ip], non_loopback_ip, [non_loopback_ip], - [(non_loopback_ip, defaultport)]) - - # Check that with invalid rpcallowip, we are denied - run_allowip_test(tmpdir, [non_loopback_ip], non_loopback_ip, defaultport) - try: - run_allowip_test(tmpdir, ['1.1.1.1'], non_loopback_ip, defaultport) - assert(not 'Connection not denied by rpcallowip as expected') - except ValueError: + def setup_network(self): pass -def main(): - import optparse - - parser = optparse.OptionParser(usage="%prog [options]") - parser.add_option("--nocleanup", dest="nocleanup", default=False, action="store_true", - help="Leave bitcoinds and test.* datadir on exit or error") - parser.add_option("--srcdir", dest="srcdir", default="../../src", - help="Source directory containing bitcoind/bitcoin-cli (default: %default%)") - parser.add_option("--tmpdir", dest="tmpdir", default=tempfile.mkdtemp(prefix="test"), - help="Root directory for datadirs") - (options, args) = parser.parse_args() - - os.environ['PATH'] = options.srcdir+":"+os.environ['PATH'] - - check_json_precision() - - success = False - nodes = [] - try: - print("Initializing test directory "+options.tmpdir) - if not os.path.isdir(options.tmpdir): - os.makedirs(options.tmpdir) - initialize_chain(options.tmpdir) - - run_test(options.tmpdir) - - success = True - - except AssertionError as e: - print("Assertion failed: "+e.message) - except Exception as e: - print("Unexpected exception caught during testing: "+str(e)) - traceback.print_tb(sys.exc_info()[2]) - - if not options.nocleanup: - print("Cleaning up") - wait_bitcoinds() - shutil.rmtree(options.tmpdir) + def setup_nodes(self): + pass - if success: - print("Tests successful") - sys.exit(0) - else: - print("Failed") - sys.exit(1) + def run_bind_test(self, allow_ips, connect_to, addresses, expected): + ''' + Start a node with requested rpcallowip and rpcbind parameters, + then try to connect, and check if the set of bound addresses + matches the expected set. + ''' + expected = [(addr_to_hex(addr), port) for (addr, port) in expected] + base_args = ['-disablewallet', '-nolisten'] + if allow_ips: + base_args += ['-rpcallowip=' + x for x in allow_ips] + binds = ['-rpcbind='+addr for addr in addresses] + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, [base_args + binds], connect_to) + try: + pid = bitcoind_processes[0].pid + assert_equal(set(get_bind_addrs(pid)), set(expected)) + finally: + stop_nodes(self.nodes) + wait_bitcoinds() + + def run_allowip_test(self, allow_ips, rpchost, rpcport): + ''' + Start a node with rpcwallow IP, and request getinfo + at a non-localhost IP. + ''' + base_args = ['-disablewallet', '-nolisten'] + ['-rpcallowip='+x for x in allow_ips] + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, [base_args]) + try: + # connect to node through non-loopback interface + node = get_rpc_proxy(rpc_url(0, "%s:%d" % (rpchost, rpcport)), 0) + node.getinfo() + finally: + node = None # make sure connection will be garbage collected and closed + stop_nodes(self.nodes) + wait_bitcoinds() + + def run_test(self): + # due to OS-specific network stats queries, this test works only on Linux + assert(sys.platform.startswith('linux')) + # find the first non-loopback interface for testing + non_loopback_ip = None + for name,ip in all_interfaces(): + if ip != '127.0.0.1': + non_loopback_ip = ip + break + if non_loopback_ip is None: + assert(not 'This test requires at least one non-loopback IPv4 interface') + print("Using interface %s for testing" % non_loopback_ip) + + defaultport = rpc_port(0) + + # check default without rpcallowip (IPv4 and IPv6 localhost) + self.run_bind_test(None, '127.0.0.1', [], + [('127.0.0.1', defaultport), ('::1', defaultport)]) + # check default with rpcallowip (IPv6 any) + self.run_bind_test(['127.0.0.1'], '127.0.0.1', [], + [('::0', defaultport)]) + # check only IPv4 localhost (explicit) + self.run_bind_test(['127.0.0.1'], '127.0.0.1', ['127.0.0.1'], + [('127.0.0.1', defaultport)]) + # check only IPv4 localhost (explicit) with alternative port + self.run_bind_test(['127.0.0.1'], '127.0.0.1:32171', ['127.0.0.1:32171'], + [('127.0.0.1', 32171)]) + # check only IPv4 localhost (explicit) with multiple alternative ports on same host + self.run_bind_test(['127.0.0.1'], '127.0.0.1:32171', ['127.0.0.1:32171', '127.0.0.1:32172'], + [('127.0.0.1', 32171), ('127.0.0.1', 32172)]) + # check only IPv6 localhost (explicit) + self.run_bind_test(['[::1]'], '[::1]', ['[::1]'], + [('::1', defaultport)]) + # check both IPv4 and IPv6 localhost (explicit) + self.run_bind_test(['127.0.0.1'], '127.0.0.1', ['127.0.0.1', '[::1]'], + [('127.0.0.1', defaultport), ('::1', defaultport)]) + # check only non-loopback interface + self.run_bind_test([non_loopback_ip], non_loopback_ip, [non_loopback_ip], + [(non_loopback_ip, defaultport)]) + + # Check that with invalid rpcallowip, we are denied + self.run_allowip_test([non_loopback_ip], non_loopback_ip, defaultport) + try: + self.run_allowip_test(['1.1.1.1'], non_loopback_ip, defaultport) + assert(not 'Connection not denied by rpcallowip as expected') + except JSONRPCException: + pass if __name__ == '__main__': - main() + RPCBindTest().main() diff --git a/qa/rpc-tests/test_framework/mininode.py b/qa/rpc-tests/test_framework/mininode.py index c1735dc76696..cc79ca608afb 100755 --- a/qa/rpc-tests/test_framework/mininode.py +++ b/qa/rpc-tests/test_framework/mininode.py @@ -28,7 +28,7 @@ import time import sys import random -from binascii import hexlify, unhexlify +from .util import hex_str_to_bytes, bytes_to_hex_str from io import BytesIO from codecs import encode import hashlib @@ -48,6 +48,10 @@ COIN = 100000000 # 1 btc in satoshis +NODE_NETWORK = (1 << 0) +NODE_GETUTXO = (1 << 1) +NODE_BLOOM = (1 << 2) + # Keep our own socket map for asyncore, so that we can track disconnects # ourselves (to workaround an issue with closing an asyncore socket when # using select) @@ -243,12 +247,12 @@ def ser_int_vector(l): # Deserialize from a hex string representation (eg from RPC) def FromHex(obj, hex_string): - obj.deserialize(BytesIO(unhexlify(hex_string.encode('ascii')))) + obj.deserialize(BytesIO(hex_str_to_bytes(hex_string))) return obj # Convert a binary-serializable object to hex (eg for submission via RPC) def ToHex(obj): - return hexlify(obj.serialize()).decode('ascii') + return bytes_to_hex_str(obj.serialize()) # Objects that map to dashd objects, which can be serialized/deserialized @@ -366,7 +370,7 @@ def serialize(self): def __repr__(self): return "CTxIn(prevout=%s scriptSig=%s nSequence=%i)" \ - % (repr(self.prevout), hexlify(self.scriptSig), + % (repr(self.prevout), bytes_to_hex_str(self.scriptSig), self.nSequence) @@ -388,7 +392,7 @@ def serialize(self): def __repr__(self): return "CTxOut(nValue=%i.%08i scriptPubKey=%s)" \ % (self.nValue // COIN, self.nValue % COIN, - hexlify(self.scriptPubKey)) + bytes_to_hex_str(self.scriptPubKey)) class CTransaction(object): @@ -405,8 +409,8 @@ def __init__(self, tx=None): self.vin = copy.deepcopy(tx.vin) self.vout = copy.deepcopy(tx.vout) self.nLockTime = tx.nLockTime - self.sha256 = None - self.hash = None + self.sha256 = tx.sha256 + self.hash = tx.hash def deserialize(self, f): self.nVersion = struct.unpack(" 1: newhashes = [] for i in range(0, len(hashes), 2): @@ -541,6 +542,13 @@ def calc_merkle_root(self): hashes = newhashes return uint256_from_str(hashes[0]) + def calc_merkle_root(self): + hashes = [] + for tx in self.vtx: + tx.calc_sha256() + hashes.append(ser_uint256(tx.sha256)) + return self.get_merkle_root(hashes) + def is_valid(self): self.calc_sha256() target = uint256_from_compact(self.nBits) @@ -951,6 +959,7 @@ def serialize(self): def __repr__(self): return "msg_sendheaders()" + # getheaders message has # number of entries # vector of hashes @@ -1072,6 +1081,8 @@ def __init__(self): # tests; it causes message delivery to sleep for the specified time # before acquiring the global lock and delivering the next message. self.deliver_sleep_time = None + # Remember the services our peer has advertised + self.peer_services = None def set_deliver_sleep_time(self, value): with mininode_lock: @@ -1109,6 +1120,7 @@ def on_version(self, conn, message): conn.ver_send = min(MY_VERSION, message.nVersion) if message.nVersion < 209: conn.ver_recv = conn.ver_send + conn.nServices = message.nServices def on_verack(self, conn, message): conn.ver_recv = conn.ver_send @@ -1139,6 +1151,7 @@ def on_close(self, conn): pass def on_mempool(self, conn): pass def on_pong(self, conn, message): pass def on_feefilter(self, conn, message): pass + def on_sendheaders(self, conn, message): pass # More useful callbacks and functions for NodeConnCB's which have a single NodeConn class SingleNodeConnCB(NodeConnCB): @@ -1187,15 +1200,16 @@ class NodeConn(asyncore.dispatcher): b"getheaders": msg_getheaders, b"reject": msg_reject, b"mempool": msg_mempool, - b"feefilter": msg_feefilter + b"feefilter": msg_feefilter, + b"sendheaders": msg_sendheaders } MAGIC_BYTES = { "mainnet": b"\xbf\x0c\x6b\xbd", # mainnet "testnet3": b"\xce\xe2\xca\xff", # testnet3 - "regtest": b"\xfc\xc1\xb7\xdc" # regtest + "regtest": b"\xfc\xc1\xb7\xdc", # regtest } - def __init__(self, dstaddr, dstport, rpc, callback, net="regtest", services=1): + def __init__(self, dstaddr, dstport, rpc, callback, net="regtest", services=NODE_NETWORK): asyncore.dispatcher.__init__(self, map=mininode_socket_map) self.log = logging.getLogger("NodeConn(%s:%d)" % (dstaddr, dstport)) self.dstaddr = dstaddr @@ -1210,6 +1224,7 @@ def __init__(self, dstaddr, dstport, rpc, callback, net="regtest", services=1): self.network = net self.cb = callback self.disconnect = False + self.nServices = 0 # stuff version msg into sendbuf vt = msg_version() diff --git a/qa/rpc-tests/test_framework/script.py b/qa/rpc-tests/test_framework/script.py index e23a561e4581..d9497f9493bc 100644 --- a/qa/rpc-tests/test_framework/script.py +++ b/qa/rpc-tests/test_framework/script.py @@ -15,8 +15,9 @@ """ -from .mininode import CTransaction, CTxOut, hash256 +from .mininode import CTransaction, CTxOut, sha256, hash256 from binascii import hexlify +import hashlib import sys bchr = chr @@ -36,6 +37,10 @@ OPCODE_NAMES = {} +def hash160(s): + return hashlib.new('ripemd160', sha256(s)).digest() + + _opcode_instances = [] class CScriptOp(int): """A single script opcode""" diff --git a/qa/rpc-tests/test_framework/test_framework.py b/qa/rpc-tests/test_framework/test_framework.py index 7b7ddb4887f0..d7a7c9cc50f3 100755 --- a/qa/rpc-tests/test_framework/test_framework.py +++ b/qa/rpc-tests/test_framework/test_framework.py @@ -47,7 +47,7 @@ def setup_chain(self): if self.setup_clean_chain: initialize_chain_clean(self.options.tmpdir, self.num_nodes) else: - initialize_chain(self.options.tmpdir, self.num_nodes) + initialize_chain(self.options.tmpdir, self.num_nodes, self.options.cachedir) def setup_nodes(self): return start_nodes(self.num_nodes, self.options.tmpdir) @@ -108,6 +108,8 @@ def main(self): help="Don't stop dashds after the test execution") parser.add_option("--srcdir", dest="srcdir", default=os.path.normpath(os.path.dirname(os.path.realpath(__file__))+"/../../../src"), help="Source directory containing dashd/dash-cli (default: %default)") + parser.add_option("--cachedir", dest="cachedir", default=os.path.normpath(os.path.dirname(os.path.realpath(__file__))+"/../../cache"), + help="Directory for caching pregenerated datadirs") parser.add_option("--tmpdir", dest="tmpdir", default=tempfile.mkdtemp(prefix="test"), help="Root directory for datadirs") parser.add_option("--tracerpc", dest="trace_rpc", default=False, action="store_true", diff --git a/qa/rpc-tests/test_framework/util.py b/qa/rpc-tests/test_framework/util.py index d268063ad81e..b4cb54a11e4c 100644 --- a/qa/rpc-tests/test_framework/util.py +++ b/qa/rpc-tests/test_framework/util.py @@ -185,7 +185,15 @@ def rpc_auth_pair(n): def rpc_url(i, rpchost=None): rpc_u, rpc_p = rpc_auth_pair(i) - return "http://%s:%s@%s:%d" % (rpc_u, rpc_p, rpchost or '127.0.0.1', rpc_port(i)) + host = '127.0.0.1' + port = rpc_port(i) + if rpchost: + parts = rpchost.split(':') + if len(parts) == 2: + host, port = parts + else: + host = rpchost + return "http://%s:%s@%s:%d" % (rpc_u, rpc_p, host, int(port)) def wait_for_bitcoind_start(process, url, i): ''' @@ -207,7 +215,7 @@ def wait_for_bitcoind_start(process, url, i): raise # unkown JSON RPC exception time.sleep(0.25) -def initialize_chain(test_dir, num_nodes): +def initialize_chain(test_dir, num_nodes, cachedir): """ Create a cache of a 200-block-long chain (with wallet) for MAX_NODES Afterward, create num_nodes copies from the cache @@ -216,7 +224,7 @@ def initialize_chain(test_dir, num_nodes): assert num_nodes <= MAX_NODES create_cache = False for i in range(MAX_NODES): - if not os.path.isdir(os.path.join('cache', 'node'+str(i))): + if not os.path.isdir(os.path.join(cachedir, 'node'+str(i))): create_cache = True break @@ -224,12 +232,12 @@ def initialize_chain(test_dir, num_nodes): #find and delete old cache directories if any exist for i in range(MAX_NODES): - if os.path.isdir(os.path.join("cache","node"+str(i))): - shutil.rmtree(os.path.join("cache","node"+str(i))) + if os.path.isdir(os.path.join(cachedir,"node"+str(i))): + shutil.rmtree(os.path.join(cachedir,"node"+str(i))) # Create cache directories, run dashds: for i in range(MAX_NODES): - datadir=initialize_datadir("cache", i) + datadir=initialize_datadir(cachedir, i) args = [ os.getenv("DASHD", "dashd"), "-server", "-keypool=1", "-datadir="+datadir, "-discover=0" ] if i > 0: args.append("-connect=127.0.0.1:"+str(p2p_port(0))) @@ -271,13 +279,13 @@ def initialize_chain(test_dir, num_nodes): wait_bitcoinds() disable_mocktime() for i in range(MAX_NODES): - os.remove(log_filename("cache", i, "debug.log")) - os.remove(log_filename("cache", i, "db.log")) - os.remove(log_filename("cache", i, "peers.dat")) - os.remove(log_filename("cache", i, "fee_estimates.dat")) + os.remove(log_filename(cachedir, i, "debug.log")) + os.remove(log_filename(cachedir, i, "db.log")) + os.remove(log_filename(cachedir, i, "peers.dat")) + os.remove(log_filename(cachedir, i, "fee_estimates.dat")) for i in range(num_nodes): - from_dir = os.path.join("cache", "node"+str(i)) + from_dir = os.path.join(cachedir, "node"+str(i)) to_dir = os.path.join(test_dir, "node"+str(i)) shutil.copytree(from_dir, to_dir) initialize_datadir(test_dir, i) # Overwrite port/rpcport in dash.conf diff --git a/share/qt/extract_strings_qt.py b/share/qt/extract_strings_qt.py index aaf386349aff..ed98254aba34 100755 --- a/share/qt/extract_strings_qt.py +++ b/share/qt/extract_strings_qt.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python ''' Extract _("...") strings for translation and convert to Qt stringdefs so that they can be picked up by Qt linguist. diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index be3bef111429..e47514b937fc 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -215,6 +215,7 @@ RES_ICONS = \ qt/res/icons/drkblue/synced.png \ qt/res/icons/drkblue/transaction0.png \ qt/res/icons/drkblue/transaction2.png \ + qt/res/icons/drkblue/transaction_abandoned.png \ qt/res/icons/drkblue/transaction_conflicted.png \ qt/res/icons/drkblue/tx_inout.png \ qt/res/icons/drkblue/tx_input.png \ @@ -225,7 +226,6 @@ RES_ICONS = \ qt/res/icons/drkblue/verify.png \ qt/res/icons/drkblue/fontbigger.png \ qt/res/icons/drkblue/fontsmaller.png \ - qt/res/icons/drkblue/transaction_abandoned.png \ qt/res/icons/drkblue/network_disabled.png \ qt/res/icons/crownium/add.png \ qt/res/icons/crownium/address-book.png \ @@ -269,6 +269,7 @@ RES_ICONS = \ qt/res/icons/crownium/synced.png \ qt/res/icons/crownium/transaction0.png \ qt/res/icons/crownium/transaction2.png \ + qt/res/icons/crownium/transaction_abandoned.png \ qt/res/icons/crownium/transaction_conflicted.png \ qt/res/icons/crownium/tx_inout.png \ qt/res/icons/crownium/tx_input.png \ @@ -279,7 +280,6 @@ RES_ICONS = \ qt/res/icons/crownium/verify.png \ qt/res/icons/crownium/fontbigger.png \ qt/res/icons/crownium/fontsmaller.png \ - qt/res/icons/crownium/transaction_abandoned.png \ qt/res/icons/crownium/network_disabled.png \ qt/res/icons/light/add.png \ qt/res/icons/light/address-book.png \ @@ -377,6 +377,7 @@ RES_ICONS = \ qt/res/icons/trad/synced.png \ qt/res/icons/trad/transaction0.png \ qt/res/icons/trad/transaction2.png \ + qt/res/icons/trad/transaction_abandoned.png \ qt/res/icons/trad/transaction_conflicted.png \ qt/res/icons/trad/tx_inout.png \ qt/res/icons/trad/tx_input.png \ @@ -387,7 +388,6 @@ RES_ICONS = \ qt/res/icons/trad/verify.png \ qt/res/icons/trad/fontbigger.png \ qt/res/icons/trad/fontsmaller.png \ - qt/res/icons/trad/transaction_abandoned.png \ qt/res/icons/trad/network_disabled.png BITCOIN_QT_CPP = \ diff --git a/src/dsnotificationinterface.cpp b/src/dsnotificationinterface.cpp index 93f30fc27dda..75123ecef7a1 100644 --- a/src/dsnotificationinterface.cpp +++ b/src/dsnotificationinterface.cpp @@ -53,8 +53,8 @@ void CDSNotificationInterface::UpdatedBlockTip(const CBlockIndex *pindexNew, con governance.UpdatedBlockTip(pindexNew, connman); } -void CDSNotificationInterface::SyncTransaction(const CTransaction &tx, const CBlockIndex *pindex, const CBlock *pblock) +void CDSNotificationInterface::SyncTransaction(const CTransaction &tx, const CBlockIndex *pindex, int posInBlock) { - instantsend.SyncTransaction(tx, pblock); - CPrivateSend::SyncTransaction(tx, pblock); + instantsend.SyncTransaction(tx, pindex, posInBlock); + CPrivateSend::SyncTransaction(tx, pindex, posInBlock); } diff --git a/src/dsnotificationinterface.h b/src/dsnotificationinterface.h index 8a2d62e41259..9e96c9afe96b 100644 --- a/src/dsnotificationinterface.h +++ b/src/dsnotificationinterface.h @@ -21,7 +21,7 @@ class CDSNotificationInterface : public CValidationInterface void AcceptedBlockHeader(const CBlockIndex *pindexNew) override; void NotifyHeaderTip(const CBlockIndex *pindexNew, bool fInitialDownload) override; void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) override; - void SyncTransaction(const CTransaction &tx, const CBlockIndex *pindex, const CBlock *pblock); + void SyncTransaction(const CTransaction &tx, const CBlockIndex *pindex, int posInBlock); private: CConnman& connman; diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 7f64fe734e9e..032d460a0a1e 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -34,9 +35,6 @@ #endif #endif -#include // for to_lower() -#include - /** Maximum size of http request (request line + headers) */ static const size_t MAX_HEADERS_SIZE = 8192; @@ -68,8 +66,8 @@ class WorkQueue { private: /** Mutex protects entire object */ - CWaitableCriticalSection cs; - CConditionVariable cond; + std::mutex cs; + std::condition_variable cond; std::deque> queue; bool running; size_t maxDepth; @@ -82,12 +80,12 @@ class WorkQueue WorkQueue &wq; ThreadCounter(WorkQueue &w): wq(w) { - boost::lock_guard lock(wq.cs); + std::lock_guard lock(wq.cs); wq.numThreads += 1; } ~ThreadCounter() { - boost::lock_guard lock(wq.cs); + std::lock_guard lock(wq.cs); wq.numThreads -= 1; wq.cond.notify_all(); } @@ -108,7 +106,7 @@ class WorkQueue /** Enqueue a work item */ bool Enqueue(WorkItem* item) { - boost::unique_lock lock(cs); + std::unique_lock lock(cs); if (queue.size() >= maxDepth) { return false; } @@ -123,7 +121,7 @@ class WorkQueue while (true) { std::unique_ptr i; { - boost::unique_lock lock(cs); + std::unique_lock lock(cs); while (running && queue.empty()) cond.wait(lock); if (!running) @@ -137,14 +135,14 @@ class WorkQueue /** Interrupt and exit loops */ void Interrupt() { - boost::unique_lock lock(cs); + std::unique_lock lock(cs); running = false; cond.notify_all(); } /** Wait for worker threads to exit */ void WaitExit() { - boost::unique_lock lock(cs); + std::unique_lock lock(cs); while (numThreads > 0){ cond.wait(lock); } @@ -153,7 +151,7 @@ class WorkQueue /** Return current depth of queue */ size_t Depth() { - boost::unique_lock lock(cs); + std::unique_lock lock(cs); return queue.size(); } }; @@ -190,7 +188,7 @@ static bool ClientAllowed(const CNetAddr& netaddr) { if (!netaddr.IsValid()) return false; - BOOST_FOREACH (const CSubNet& subnet, rpc_allow_subnets) + for(const CSubNet& subnet : rpc_allow_subnets) if (subnet.Match(netaddr)) return true; return false; @@ -208,7 +206,7 @@ static bool InitHTTPAllowList() rpc_allow_subnets.push_back(CSubNet(localv6)); // always allow IPv6 localhost if (mapMultiArgs.count("-rpcallowip")) { const std::vector& vAllow = mapMultiArgs["-rpcallowip"]; - BOOST_FOREACH (std::string strAllow, vAllow) { + for (std::string strAllow : vAllow) { CSubNet subnet; LookupSubNet(strAllow.c_str(), subnet); if (!subnet.IsValid()) { @@ -221,7 +219,7 @@ static bool InitHTTPAllowList() } } std::string strAllowed; - BOOST_FOREACH (const CSubNet& subnet, rpc_allow_subnets) + for (const CSubNet& subnet : rpc_allow_subnets) strAllowed += subnet.ToString() + " "; LogPrint("http", "Allowing HTTP connections from: %s\n", strAllowed); return true; @@ -308,13 +306,14 @@ static void http_reject_request_cb(struct evhttp_request* req, void*) } /** Event dispatcher thread */ -static void ThreadHTTP(struct event_base* base, struct evhttp* http) +static bool ThreadHTTP(struct event_base* base, struct evhttp* http) { RenameThread("dash-http"); LogPrint("http", "Entering http event loop\n"); event_base_dispatch(base); // Event loop will be interrupted by InterruptHTTPServer() LogPrint("http", "Exited http event loop\n"); + return event_base_got_break(base) == 0; } /** Bind HTTP server to specified addresses */ @@ -443,17 +442,22 @@ bool InitHTTPServer() return true; } -boost::thread threadHTTP; +std::thread threadHTTP; +std::future threadResult; bool StartHTTPServer() { LogPrint("http", "Starting HTTP server\n"); int rpcThreads = std::max((long)GetArg("-rpcthreads", DEFAULT_HTTP_THREADS), 1L); LogPrintf("HTTP: starting %d worker threads\n", rpcThreads); - threadHTTP = boost::thread(boost::bind(&ThreadHTTP, eventBase, eventHTTP)); + std::packaged_task task(ThreadHTTP); + threadResult = task.get_future(); + threadHTTP = std::thread(std::move(task), eventBase, eventHTTP); - for (int i = 0; i < rpcThreads; i++) - boost::thread(boost::bind(&HTTPWorkQueueRun, workQueue)); + for (int i = 0; i < rpcThreads; i++) { + std::thread rpc_worker(HTTPWorkQueueRun, workQueue); + rpc_worker.detach(); + } return true; } @@ -462,7 +466,7 @@ void InterruptHTTPServer() LogPrint("http", "Interrupting HTTP server\n"); if (eventHTTP) { // Unlisten sockets - BOOST_FOREACH (evhttp_bound_socket *socket, boundSockets) { + for (evhttp_bound_socket *socket : boundSockets) { evhttp_del_accept_socket(eventHTTP, socket); } // Reject requests on current connections @@ -493,16 +497,11 @@ void StopHTTPServer() // master that appears to be solved, so in the future that solution // could be used again (if desirable). // (see discussion in https://github.com/bitcoin/bitcoin/pull/6990) -#if BOOST_VERSION >= 105000 - if (!threadHTTP.try_join_for(boost::chrono::milliseconds(2000))) { -#else - if (!threadHTTP.timed_join(boost::posix_time::milliseconds(2000))) { -#endif - + if (threadResult.valid() && threadResult.wait_for(std::chrono::milliseconds(2000)) == std::future_status::timeout) { LogPrintf("HTTP event loop did not exit within allotted time, sending loopbreak\n"); event_base_loopbreak(eventBase); - threadHTTP.join(); } + threadHTTP.join(); } if (eventHTTP) { evhttp_free(eventHTTP); @@ -529,7 +528,7 @@ static void httpevent_callback_fn(evutil_socket_t, short, void* data) delete self; } -HTTPEvent::HTTPEvent(struct event_base* base, bool deleteWhenTriggered, const boost::function& handler): +HTTPEvent::HTTPEvent(struct event_base* base, bool deleteWhenTriggered, const std::function& handler): deleteWhenTriggered(deleteWhenTriggered), handler(handler) { ev = event_new(base, -1, 0, httpevent_callback_fn, this); @@ -611,7 +610,7 @@ void HTTPRequest::WriteReply(int nStatus, const std::string& strReply) assert(evb); evbuffer_add(evb, strReply.data(), strReply.size()); HTTPEvent* ev = new HTTPEvent(eventBase, true, - boost::bind(evhttp_send_reply, req, nStatus, (const char*)NULL, (struct evbuffer *)NULL)); + std::bind(evhttp_send_reply, req, nStatus, (const char*)NULL, (struct evbuffer *)NULL)); ev->trigger(0); replySent = true; req = 0; // transferred back to main thread diff --git a/src/httpserver.h b/src/httpserver.h index 20a119cc5c9c..49d67f4b8842 100644 --- a/src/httpserver.h +++ b/src/httpserver.h @@ -7,9 +7,7 @@ #include #include -#include -#include -#include +#include static const int DEFAULT_HTTP_THREADS=4; static const int DEFAULT_HTTP_WORKQUEUE=16; @@ -35,7 +33,7 @@ void InterruptHTTPServer(); void StopHTTPServer(); /** Handler for requests to a certain HTTP path */ -typedef boost::function HTTPRequestHandler; +typedef std::function HTTPRequestHandler; /** Register handler for prefix. * If multiple handlers match a prefix, the first-registered one will * be invoked. @@ -132,7 +130,7 @@ class HTTPEvent * deleteWhenTriggered deletes this event object after the event is triggered (and the handler called) * handler is the handler to call when the event is triggered. */ - HTTPEvent(struct event_base* base, bool deleteWhenTriggered, const boost::function& handler); + HTTPEvent(struct event_base* base, bool deleteWhenTriggered, const std::function& handler); ~HTTPEvent(); /** Trigger the event. If tv is 0, trigger it immediately. Otherwise trigger it after @@ -141,7 +139,7 @@ class HTTPEvent void trigger(struct timeval* tv); bool deleteWhenTriggered; - boost::function handler; + std::function handler; private: struct event* ev; }; diff --git a/src/indirectmap.h b/src/indirectmap.h index 28e1e8deddbd..76da4a6bd557 100644 --- a/src/indirectmap.h +++ b/src/indirectmap.h @@ -1,3 +1,7 @@ +// Copyright (c) 2016 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef BITCOIN_INDIRECTMAP_H #define BITCOIN_INDIRECTMAP_H diff --git a/src/init.cpp b/src/init.cpp index c9d87fa358a2..0ad3e7198851 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -591,7 +591,7 @@ std::string LicenseInfo() { const std::string URL_SOURCE_CODE = ""; const std::string URL_WEBSITE = ""; - // todo: remove urls from translations on next change + return CopyrightHolders(_("Copyright (C)"), 2014, COPYRIGHT_YEAR) + "\n" + "\n" + strprintf(_("Please contribute if you find %s useful. " @@ -603,9 +603,9 @@ std::string LicenseInfo() "\n" + "\n" + _("This is experimental software.") + "\n" + - _("Distributed under the MIT software license, see the accompanying file COPYING or .") + "\n" + + strprintf(_("Distributed under the MIT software license, see the accompanying file %s or %s"), "COPYING", "") + "\n" + "\n" + - _("This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit and cryptographic software written by Eric Young and UPnP software written by Thomas Bernard.") + + strprintf(_("This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit %s and cryptographic software written by Eric Young and UPnP software written by Thomas Bernard."), "") + "\n"; } @@ -620,6 +620,21 @@ static void BlockNotifyCallback(bool initialSync, const CBlockIndex *pBlockIndex boost::thread t(runCommand, strCmd); // thread runs free } +static bool fHaveGenesis = false; +static boost::mutex cs_GenesisWait; +static CConditionVariable condvar_GenesisWait; + +static void BlockNotifyGenesisWait(bool, const CBlockIndex *pBlockIndex) +{ + if (pBlockIndex != NULL) { + { + boost::unique_lock lock_GenesisWait(cs_GenesisWait); + fHaveGenesis = true; + } + condvar_GenesisWait.notify_all(); + } +} + struct CImportingNow { CImportingNow() { @@ -1201,7 +1216,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) LogPrintf("Using data directory %s\n", strDataDir); LogPrintf("Using config file %s\n", GetConfigFile().string()); LogPrintf("Using at most %i connections (%i file descriptors available)\n", nMaxConnections, nFD); - std::ostringstream strErrors; LogPrintf("Using %u threads for script verification\n", nScriptCheckThreads); if (nScriptCheckThreads) { @@ -1450,7 +1464,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) // cache size calculations int64_t nTotalCache = (GetArg("-dbcache", nDefaultDbCache) << 20); nTotalCache = std::max(nTotalCache, nMinDbCache << 20); // total cache cannot be less than nMinDbCache - nTotalCache = std::min(nTotalCache, nMaxDbCache << 20); // total cache cannot be greated than nMaxDbcache + nTotalCache = std::min(nTotalCache, nMaxDbCache << 20); // total cache cannot be greater than nMaxDbcache int64_t nBlockTreeDBCache = nTotalCache / 8; nBlockTreeDBCache = std::min(nBlockTreeDBCache, (GetBoolArg("-txindex", DEFAULT_TXINDEX) ? nMaxBlockDBAndTxIndexCache : nMaxBlockDBCache) << 20); nTotalCache -= nBlockTreeDBCache; @@ -1628,6 +1642,17 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) // ********************************************************* Step 10: import blocks + if (!CheckDiskSpace()) + return false; + + // Either install a handler to notify us when genesis activates, or set fHaveGenesis directly. + // No locking, as this happens before any background thread is started. + if (chainActive.Tip() == NULL) { + uiInterface.NotifyBlockTip.connect(BlockNotifyGenesisWait); + } else { + fHaveGenesis = true; + } + if (mapArgs.count("-blocknotify")) uiInterface.NotifyBlockTip.connect(BlockNotifyCallback); @@ -1637,19 +1662,16 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) BOOST_FOREACH(const std::string& strFile, mapMultiArgs["-loadblock"]) vImportFiles.push_back(strFile); } + threadGroup.create_thread(boost::bind(&ThreadImport, vImportFiles)); // Wait for genesis block to be processed - bool fHaveGenesis = false; - while (!fHaveGenesis && !fRequestShutdown) { - { - LOCK(cs_main); - fHaveGenesis = (chainActive.Tip() != NULL); - } - - if (!fHaveGenesis) { - MilliSleep(10); + { + boost::unique_lock lock(cs_GenesisWait); + while (!fHaveGenesis) { + condvar_GenesisWait.wait(lock); } + uiInterface.NotifyBlockTip.disconnect(BlockNotifyGenesisWait); } // ********************************************************* Step 11a: setup PrivateSend @@ -1787,12 +1809,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) // ********************************************************* Step 12: start node - if (!CheckDiskSpace()) - return false; - - if (!strErrors.str().empty()) - return InitError(strErrors.str()); - //// debug print LogPrintf("mapBlockIndex.size() = %u\n", mapBlockIndex.size()); LogPrintf("chainActive.Height() = %d\n", chainActive.Height()); diff --git a/src/instantx.cpp b/src/instantx.cpp index ff0c63713031..26ff0ba58230 100644 --- a/src/instantx.cpp +++ b/src/instantx.cpp @@ -860,7 +860,7 @@ void CInstantSend::UpdatedBlockTip(const CBlockIndex *pindex) nCachedBlockHeight = pindex->nHeight; } -void CInstantSend::SyncTransaction(const CTransaction& tx, const CBlock* pblock) +void CInstantSend::SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, int posInBlock) { // Update lock candidates and votes if corresponding tx confirmed // or went from confirmed to 0-confirmed or conflicted. @@ -871,19 +871,8 @@ void CInstantSend::SyncTransaction(const CTransaction& tx, const CBlock* pblock) uint256 txHash = tx.GetHash(); - // When tx is 0-confirmed or conflicted, pblock is NULL and nHeightNew should be set to -1 - CBlockIndex* pblockindex = NULL; - if(pblock) { - uint256 blockHash = pblock->GetHash(); - BlockMap::iterator mi = mapBlockIndex.find(blockHash); - if(mi == mapBlockIndex.end() || !mi->second) { - // shouldn't happen - LogPrint("instantsend", "CTxLockRequest::SyncTransaction -- Failed to find block %s\n", blockHash.ToString()); - return; - } - pblockindex = mi->second; - } - int nHeightNew = pblockindex ? pblockindex->nHeight : -1; + // When tx is 0-confirmed or conflicted, posInBlock is SYNC_TRANSACTION_NOT_IN_BLOCK and nHeightNew should be set to -1 + int nHeightNew = posInBlock == CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK ? -1 : pindex->nHeight; LogPrint("instantsend", "CInstantSend::SyncTransaction -- txid=%s nHeightNew=%d\n", txHash.ToString(), nHeightNew); diff --git a/src/instantx.h b/src/instantx.h index 19976e1f450e..4d6016b51f92 100644 --- a/src/instantx.h +++ b/src/instantx.h @@ -114,7 +114,7 @@ class CInstantSend void Relay(const uint256& txHash, CConnman& connman); void UpdatedBlockTip(const CBlockIndex *pindex); - void SyncTransaction(const CTransaction& tx, const CBlock* pblock); + void SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, int posInBlock); std::string ToString(); }; diff --git a/src/key.h b/src/key.h index b4f48d59f5b0..b589710badff 100644 --- a/src/key.h +++ b/src/key.h @@ -15,7 +15,7 @@ #include -/** +/** * secp256k1: * const unsigned int PRIVATE_KEY_SIZE = 279; * const unsigned int PUBLIC_KEY_SIZE = 65; @@ -45,6 +45,8 @@ class CKey //! The actual byte data unsigned char vch[32]; + static_assert(sizeof(vch) == 32, "vch must be 32 bytes in length to not break serialization"); + //! Check whether the 32-byte array pointed to be vch is valid keydata. bool static Check(const unsigned char* vch); @@ -70,20 +72,19 @@ class CKey friend bool operator==(const CKey& a, const CKey& b) { - return a.fCompressed == b.fCompressed && a.size() == b.size() && - memcmp(&a.vch[0], &b.vch[0], a.size()) == 0; + return a.fCompressed == b.fCompressed && + a.size() == b.size() && + memcmp(&a.vch[0], &b.vch[0], a.size()) == 0; } //! Initialize using begin and end iterators to byte data. template void Set(const T pbegin, const T pend, bool fCompressedIn) { - if (pend - pbegin != 32) { + if (pend - pbegin != sizeof(vch)) { fValid = false; - return; - } - if (Check(&pbegin[0])) { - memcpy(vch, (unsigned char*)&pbegin[0], 32); + } else if (Check(&pbegin[0])) { + memcpy(vch, (unsigned char*)&pbegin[0], sizeof(vch)); fValid = true; fCompressed = fCompressedIn; } else { @@ -92,7 +93,7 @@ class CKey } //! Simple read-only vector-like interface. - unsigned int size() const { return (fValid ? 32 : 0); } + unsigned int size() const { return (fValid ? sizeof(vch) : 0); } const unsigned char* begin() const { return vch; } const unsigned char* end() const { return vch + size(); } @@ -110,7 +111,7 @@ class CKey /** * Convert the private key to a CPrivKey (serialized OpenSSL private key data). - * This is expensive. + * This is expensive. */ CPrivKey GetPrivKey() const; @@ -146,9 +147,6 @@ class CKey //! Load private key and check that public key matches. bool Load(CPrivKey& privkey, CPubKey& vchPubKey, bool fSkipCheck); - - //! Check whether an element of a signature (r or s) is valid. - static bool CheckSignatureElement(const unsigned char* vch, int len, bool half); }; struct CExtKey { @@ -160,8 +158,11 @@ struct CExtKey { friend bool operator==(const CExtKey& a, const CExtKey& b) { - return a.nDepth == b.nDepth && memcmp(&a.vchFingerprint[0], &b.vchFingerprint[0], 4) == 0 && a.nChild == b.nChild && - a.chaincode == b.chaincode && a.key == b.key; + return a.nDepth == b.nDepth && + memcmp(&a.vchFingerprint[0], &b.vchFingerprint[0], sizeof(vchFingerprint)) == 0 && + a.nChild == b.nChild && + a.chaincode == b.chaincode && + a.key == b.key; } void Encode(unsigned char code[BIP32_EXTKEY_SIZE]) const; diff --git a/src/limitedmap.h b/src/limitedmap.h index 4d9bb4fa21d5..7841d7f4a4ab 100644 --- a/src/limitedmap.h +++ b/src/limitedmap.h @@ -66,8 +66,11 @@ class limitedmap } void update(const_iterator itIn, const mapped_type& v) { - // TODO: When we switch to C++11, use map.erase(itIn, itIn) to get the non-const iterator. - iterator itTarget = map.find(itIn->first); + // Using map::erase() with empty range instead of map::find() to get a non-const iterator, + // since it is a constant time operation in C++11. For more details, see + // https://stackoverflow.com/questions/765148/how-to-remove-constness-of-const-iterator + iterator itTarget = map.erase(itIn, itIn); + if (itTarget == map.end()) return; std::pair itPair = rmap.equal_range(itTarget->second); diff --git a/src/net.cpp b/src/net.cpp index 51310ce7ae19..6cbe32377a3f 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -975,11 +975,11 @@ bool CConnman::AttemptToEvictConnection() uint64_t naMostConnections; unsigned int nMostConnections = 0; int64_t nMostConnectionsTime = 0; - std::map > mapAddrCounts; + std::map > mapNetGroupNodes; BOOST_FOREACH(const NodeEvictionCandidate &node, vEvictionCandidates) { - mapAddrCounts[node.nKeyedNetGroup].push_back(node); - int64_t grouptime = mapAddrCounts[node.nKeyedNetGroup][0].nTimeConnected; - size_t groupsize = mapAddrCounts[node.nKeyedNetGroup].size(); + mapNetGroupNodes[node.nKeyedNetGroup].push_back(node); + int64_t grouptime = mapNetGroupNodes[node.nKeyedNetGroup][0].nTimeConnected; + size_t groupsize = mapNetGroupNodes[node.nKeyedNetGroup].size(); if (groupsize > nMostConnections || (groupsize == nMostConnections && grouptime > nMostConnectionsTime)) { nMostConnections = groupsize; @@ -989,7 +989,7 @@ bool CConnman::AttemptToEvictConnection() } // Reduce to the network group with the most connections - vEvictionCandidates = std::move(mapAddrCounts[naMostConnections]); + vEvictionCandidates = std::move(mapNetGroupNodes[naMostConnections]); // Disconnect from the network group with the most connections NodeId evicted = vEvictionCandidates.front().id; @@ -2203,6 +2203,8 @@ bool CConnman::Start(CScheduler& scheduler, std::string& strNodeError, Options c DumpBanlist(); } + uiInterface.InitMessage(_("Starting network threads...")); + fAddressesInitialized = true; if (semOutbound == NULL) { diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 09c8f885b4fe..59c097f75d6d 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -2231,6 +2231,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } } + else if (strCommand == NetMsgType::NOTFOUND) { + // We do not care about the NOTFOUND message, but logging an Unknown Command + // message would be undesirable as we transmit it ourselves. + } + else { bool found = false; const std::vector &allMessages = getAllNetMessageTypes(); diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index 16fc37208776..19026adea131 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -116,7 +116,7 @@ CAmount CTransaction::GetValueOut() const { nValueOut += it->nValue; if (!MoneyRange(it->nValue) || !MoneyRange(nValueOut)) - throw std::runtime_error("CTransaction::GetValueOut(): value out of range"); + throw std::runtime_error(std::string(__func__) + ": value out of range"); } return nValueOut; } diff --git a/src/privatesend.cpp b/src/privatesend.cpp index 01ff945f1654..b8375afe0ea9 100644 --- a/src/privatesend.cpp +++ b/src/privatesend.cpp @@ -410,7 +410,7 @@ void CPrivateSend::UpdatedBlockTip(const CBlockIndex *pindex) } } -void CPrivateSend::SyncTransaction(const CTransaction& tx, const CBlock* pblock) +void CPrivateSend::SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, int posInBlock) { if (tx.IsCoinBase()) return; @@ -419,19 +419,8 @@ void CPrivateSend::SyncTransaction(const CTransaction& tx, const CBlock* pblock) uint256 txHash = tx.GetHash(); if (!mapDSTX.count(txHash)) return; - // When tx is 0-confirmed or conflicted, pblock is NULL and nConfirmedHeight should be set to -1 - CBlockIndex* pblockindex = NULL; - if(pblock) { - uint256 blockHash = pblock->GetHash(); - BlockMap::iterator mi = mapBlockIndex.find(blockHash); - if(mi == mapBlockIndex.end() || !mi->second) { - // shouldn't happen - LogPrint("privatesend", "CPrivateSendClient::SyncTransaction -- Failed to find block %s\n", blockHash.ToString()); - return; - } - pblockindex = mi->second; - } - mapDSTX[txHash].SetConfirmedHeight(pblockindex ? pblockindex->nHeight : -1); + // When tx is 0-confirmed or conflicted, posInBlock is SYNC_TRANSACTION_NOT_IN_BLOCK and nConfirmedHeight should be set to -1 + mapDSTX[txHash].SetConfirmedHeight(posInBlock == CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK ? -1 : pindex->nHeight); LogPrint("privatesend", "CPrivateSendClient::SyncTransaction -- txid=%s\n", txHash.ToString()); } diff --git a/src/privatesend.h b/src/privatesend.h index 8f78c7af0b91..654e7721db4b 100644 --- a/src/privatesend.h +++ b/src/privatesend.h @@ -353,7 +353,7 @@ class CPrivateSend static CDarksendBroadcastTx GetDSTX(const uint256& hash); static void UpdatedBlockTip(const CBlockIndex *pindex); - static void SyncTransaction(const CTransaction& tx, const CBlock* pblock); + static void SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, int posInBlock); }; void ThreadCheckPrivateSend(CConnman& connman); diff --git a/src/pubkey.h b/src/pubkey.h index db5444ea9d74..aebfdbc82662 100644 --- a/src/pubkey.h +++ b/src/pubkey.h @@ -13,7 +13,7 @@ #include #include -/** +/** * secp256k1: * const unsigned int PRIVATE_KEY_SIZE = 279; * const unsigned int PUBLIC_KEY_SIZE = 65; @@ -156,7 +156,7 @@ class CPubKey /* * Check syntactic correctness. - * + * * Note that this is consensus critical as CheckSig() calls it! */ bool IsValid() const @@ -203,8 +203,11 @@ struct CExtPubKey { friend bool operator==(const CExtPubKey &a, const CExtPubKey &b) { - return a.nDepth == b.nDepth && memcmp(&a.vchFingerprint[0], &b.vchFingerprint[0], 4) == 0 && a.nChild == b.nChild && - a.chaincode == b.chaincode && a.pubkey == b.pubkey; + return a.nDepth == b.nDepth && + memcmp(&a.vchFingerprint[0], &b.vchFingerprint[0], sizeof(vchFingerprint)) == 0 && + a.nChild == b.nChild && + a.chaincode == b.chaincode && + a.pubkey == b.pubkey; } void Encode(unsigned char code[BIP32_EXTKEY_SIZE]) const; diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 53141719f9df..cc5d2ed381e5 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -84,7 +84,7 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *platformStyle, const NetworkStyle *n clientModel(0), walletFrame(0), unitDisplayControl(0), - labelEncryptionIcon(0), + labelWalletEncryptionIcon(0), labelWalletHDStatusIcon(0), labelConnectionsIcon(0), labelBlocksIcon(0), @@ -207,7 +207,7 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *platformStyle, const NetworkStyle *n frameBlocksLayout->setContentsMargins(3,0,3,0); frameBlocksLayout->setSpacing(3); unitDisplayControl = new UnitDisplayStatusBarControl(platformStyle); - labelEncryptionIcon = new QLabel(); + labelWalletEncryptionIcon = new QLabel(); labelWalletHDStatusIcon = new QLabel(); labelConnectionsIcon = new GUIUtil::ClickableLabel(); @@ -217,7 +217,7 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *platformStyle, const NetworkStyle *n frameBlocksLayout->addStretch(); frameBlocksLayout->addWidget(unitDisplayControl); frameBlocksLayout->addStretch(); - frameBlocksLayout->addWidget(labelEncryptionIcon); + frameBlocksLayout->addWidget(labelWalletEncryptionIcon); frameBlocksLayout->addWidget(labelWalletHDStatusIcon); } frameBlocksLayout->addStretch(); @@ -1208,9 +1208,15 @@ void BitcoinGUI::closeEvent(QCloseEvent *event) QApplication::quit(); } + else + { + QMainWindow::showMinimized(); + event->ignore(); + } } -#endif +#else QMainWindow::closeEvent(event); +#endif } void BitcoinGUI::showEvent(QShowEvent *event) @@ -1302,7 +1308,7 @@ void BitcoinGUI::setEncryptionStatus(int status) switch(status) { case WalletModel::Unencrypted: - labelEncryptionIcon->hide(); + labelWalletEncryptionIcon->hide(); encryptWalletAction->setChecked(false); changePassphraseAction->setEnabled(false); unlockWalletAction->setVisible(false); @@ -1310,9 +1316,9 @@ void BitcoinGUI::setEncryptionStatus(int status) encryptWalletAction->setEnabled(true); break; case WalletModel::Unlocked: - labelEncryptionIcon->show(); - labelEncryptionIcon->setPixmap(QIcon(":/icons/" + theme + "/lock_open").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); - labelEncryptionIcon->setToolTip(tr("Wallet is encrypted and currently unlocked")); + labelWalletEncryptionIcon->show(); + labelWalletEncryptionIcon->setPixmap(QIcon(":/icons/" + theme + "/lock_open").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); + labelWalletEncryptionIcon->setToolTip(tr("Wallet is encrypted and currently unlocked")); encryptWalletAction->setChecked(true); changePassphraseAction->setEnabled(true); unlockWalletAction->setVisible(false); @@ -1320,9 +1326,9 @@ void BitcoinGUI::setEncryptionStatus(int status) encryptWalletAction->setEnabled(false); // TODO: decrypt currently not supported break; case WalletModel::UnlockedForMixingOnly: - labelEncryptionIcon->show(); - labelEncryptionIcon->setPixmap(QIcon(":/icons/" + theme + "/lock_open").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); - labelEncryptionIcon->setToolTip(tr("Wallet is encrypted and currently unlocked for mixing only")); + labelWalletEncryptionIcon->show(); + labelWalletEncryptionIcon->setPixmap(QIcon(":/icons/" + theme + "/lock_open").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); + labelWalletEncryptionIcon->setToolTip(tr("Wallet is encrypted and currently unlocked for mixing only")); encryptWalletAction->setChecked(true); changePassphraseAction->setEnabled(true); unlockWalletAction->setVisible(true); @@ -1330,9 +1336,9 @@ void BitcoinGUI::setEncryptionStatus(int status) encryptWalletAction->setEnabled(false); // TODO: decrypt currently not supported break; case WalletModel::Locked: - labelEncryptionIcon->show(); - labelEncryptionIcon->setPixmap(QIcon(":/icons/" + theme + "/lock_closed").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); - labelEncryptionIcon->setToolTip(tr("Wallet is encrypted and currently locked")); + labelWalletEncryptionIcon->show(); + labelWalletEncryptionIcon->setPixmap(QIcon(":/icons/" + theme + "/lock_closed").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); + labelWalletEncryptionIcon->setToolTip(tr("Wallet is encrypted and currently locked")); encryptWalletAction->setChecked(true); changePassphraseAction->setEnabled(true); unlockWalletAction->setVisible(true); diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index e77fd8ae653c..e03648b95103 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -85,7 +85,7 @@ class BitcoinGUI : public QMainWindow WalletFrame *walletFrame; UnitDisplayStatusBarControl *unitDisplayControl; - QLabel *labelEncryptionIcon; + QLabel *labelWalletEncryptionIcon; QLabel *labelWalletHDStatusIcon; QLabel *labelConnectionsIcon; QLabel *labelBlocksIcon; diff --git a/src/qt/dash.cpp b/src/qt/dash.cpp index bd37cf4468d8..e9d518dad5a1 100644 --- a/src/qt/dash.cpp +++ b/src/qt/dash.cpp @@ -623,7 +623,8 @@ int main(int argc, char *argv[]) /// 5. Now that settings and translations are available, ask user for data directory // User language is set up: pick a data directory - Intro::pickDataDirectory(); + if (!Intro::pickDataDirectory()) + return EXIT_SUCCESS; /// 6. Determine availability of data directory and parse dash.conf /// - Do not call GetDataDir(true) before this step finishes diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp index f18720f608e3..b9bc470c9790 100644 --- a/src/qt/intro.cpp +++ b/src/qt/intro.cpp @@ -166,14 +166,14 @@ QString Intro::getDefaultDataDirectory() return GUIUtil::boostPathToQString(GetDefaultDataDir()); } -void Intro::pickDataDirectory() +bool Intro::pickDataDirectory() { namespace fs = boost::filesystem; QSettings settings; /* If data directory provided on command line, no need to look at settings or show a picking dialog */ if(!GetArg("-datadir", "").empty()) - return; + return true; /* 1) Default data directory for operating system */ QString dataDirDefaultCurrent = getDefaultDataDirectory(); /* 2) Allow QSettings to override default dir */ @@ -193,7 +193,7 @@ void Intro::pickDataDirectory() if(!intro.exec()) { /* Cancel clicked */ - exit(EXIT_SUCCESS); + return false; } dataDir = intro.getDataDirectory(); try { @@ -215,6 +215,7 @@ void Intro::pickDataDirectory() */ if(dataDir != dataDirDefaultCurrent) SoftSetArg("-datadir", GUIUtil::qstringToBoostPath(dataDir).string()); // use OS locale for path setting + return true; } void Intro::setStatus(int status, const QString &message, quint64 bytesAvailable) diff --git a/src/qt/intro.h b/src/qt/intro.h index 9e2e96dc9eca..ee768a7ad8d1 100644 --- a/src/qt/intro.h +++ b/src/qt/intro.h @@ -35,10 +35,13 @@ class Intro : public QDialog /** * Determine data directory. Let the user choose if the current one doesn't exist. * + * @returns true if a data directory was selected, false if the user cancelled the selection + * dialog. + * * @note do NOT call global GetDataDir() before calling this function, this * will cause the wrong path to be cached. */ - static void pickDataDirectory(); + static bool pickDataDirectory(); /** * Determine default data directory for operating system. diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 6d03b4f9f9f9..df887eba4b73 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -48,6 +48,8 @@ void OptionsModel::Init(bool resetSettings) if (resetSettings) Reset(); + checkAndMigrate(); + this->resetSettings = resetSettings; QSettings settings; @@ -552,3 +554,22 @@ bool OptionsModel::isRestartRequired() QSettings settings; return settings.value("fRestartRequired", false).toBool(); } + +void OptionsModel::checkAndMigrate() +{ + // Migration of default values + // Check if the QSettings container was already loaded with this client version + QSettings settings; + static const char strSettingsVersionKey[] = "nSettingsVersion"; + int settingsVersion = settings.contains(strSettingsVersionKey) ? settings.value(strSettingsVersionKey).toInt() : 0; + if (settingsVersion < CLIENT_VERSION) + { + // -dbcache was bumped from 100 to 300 in 0.13 + // see https://github.com/bitcoin/bitcoin/pull/8273 + // force people to upgrade to the new value if they are using 100MB + if (settingsVersion < 130000 && settings.contains("nDatabaseCache") && settings.value("nDatabaseCache").toLongLong() == 100) + settings.setValue("nDatabaseCache", (qint64)nDefaultDbCache); + + settings.setValue(strSettingsVersionKey, CLIENT_VERSION); + } +} \ No newline at end of file diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index 88839639dd9d..cbe90a799fc4 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -95,9 +95,11 @@ class OptionsModel : public QAbstractListModel /* settings that were overriden by command-line */ QString strOverriddenByCommandLine; - /// Add option to list of GUI options overridden through command line/config file + // Add option to list of GUI options overridden through command line/config file void addOverriddenOption(const std::string &option); + // Check settings version and upgrade default values if required + void checkAndMigrate(); Q_SIGNALS: void displayUnitChanged(int unit); void privateSendRoundsChanged(); diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 08028eb65fc7..4a934ac027f0 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -231,11 +231,11 @@ UniValue getmininginfo(const UniValue& params, bool fHelp) " \"currentblocksize\": nnn, (numeric) The last block size\n" " \"currentblocktx\": nnn, (numeric) The last block transaction\n" " \"difficulty\": xxx.xxxxx (numeric) The current difficulty\n" - " \"errors\": \"...\" (string) Current errors\n" - " \"networkhashps\": n (numeric) An estimate of the number of hashes per second the network is generating to maintain the current difficulty\n" + " \"errors\": \"...\" (string) Current errors\n" + " \"networkhashps\": nnn, (numeric) The network hashes per second\n" " \"pooledtx\": n (numeric) The size of the mem pool\n" " \"testnet\": true|false (boolean) If using testnet or not\n" - " \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n" + " \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n" "}\n" "\nExamples:\n" + HelpExampleCli("getmininginfo", "") @@ -675,7 +675,7 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) if (nMaxVersionPreVB >= 2) { // If VB is supported by the client, nMaxVersionPreVB is -1, so we won't get here - // Because BIP 34 changed how the generation transaction is serialised, we can only use version/force back to v2 blocks + // Because BIP 34 changed how the generation transaction is serialized, we can only use version/force back to v2 blocks // This is safe to do [otherwise-]unconditionally only because we are throwing an exception above if a non-force deployment gets activated // Note that this can probably also be removed entirely after the first BIP9 non-force deployment (ie, probably segwit) gets activated aMutable.push_back("version/force"); diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index c75746fec964..801e770f955b 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -480,7 +480,7 @@ UniValue setban(const UniValue& params, bool fHelp) "\nExamples:\n" + HelpExampleCli("setban", "\"192.168.0.6\" \"add\" 86400") + HelpExampleCli("setban", "\"192.168.0.0/24\" \"add\"") - + HelpExampleRpc("setban", "\"192.168.0.6\", \"add\" 86400") + + HelpExampleRpc("setban", "\"192.168.0.6\", \"add\", 86400") ); if(!g_connman) throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 5ea00742b3a7..b7e97ec68508 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -26,6 +26,8 @@ #include #include // for to_upper() +#include // for unique_ptr + using namespace RPCServer; using namespace std; @@ -35,9 +37,8 @@ static std::string rpcWarmupStatus("RPC server started"); static CCriticalSection cs_rpcWarmup; /* Timer-creating functions */ static RPCTimerInterface* timerInterface = NULL; -/* Map of name to timer. - * @note Can be changed to std::unique_ptr when C++11 */ -static std::map > deadlineTimers; +/* Map of name to timer. */ +static std::map > deadlineTimers; static struct CRPCSignals { @@ -491,7 +492,7 @@ void RPCRunLater(const std::string& name, boost::function func, int6 throw JSONRPCError(RPC_INTERNAL_ERROR, "No timer handler registered for RPC"); deadlineTimers.erase(name); LogPrint("rpc", "queue run of timer %s in %i seconds (using %s)\n", name, nSeconds, timerInterface->Name()); - deadlineTimers.insert(std::make_pair(name, boost::shared_ptr(timerInterface->NewTimer(func, nSeconds*1000)))); + deadlineTimers.emplace(name, std::unique_ptr(timerInterface->NewTimer(func, nSeconds*1000))); } CRPCTable tableRPC; diff --git a/src/script/dashconsensus.h b/src/script/dashconsensus.h index 54233b77287e..7ac497d154ee 100644 --- a/src/script/dashconsensus.h +++ b/src/script/dashconsensus.h @@ -48,6 +48,7 @@ enum dashconsensus_SCRIPT_FLAGS_VERIFY_P2SH = (1U << 0), // evaluate P2SH (BIP16) subscripts dashconsensus_SCRIPT_FLAGS_VERIFY_DERSIG = (1U << 2), // enforce strict DER (BIP66) compliance dashconsensus_SCRIPT_FLAGS_VERIFY_CHECKLOCKTIMEVERIFY = (1U << 9), // enable CHECKLOCKTIMEVERIFY (BIP65) + dashconsensus_SCRIPT_FLAGS_VERIFY_CHECKSEQUENCEVERIFY = (1U << 10), // enable CHECKSEQUENCEVERIFY (BIP112) }; /// Returns 1 if the input nIn of the serialized transaction pointed to by diff --git a/src/secp256k1/.gitignore b/src/secp256k1/.gitignore index e0b7b7a48a31..efb277d34706 100644 --- a/src/secp256k1/.gitignore +++ b/src/secp256k1/.gitignore @@ -25,17 +25,24 @@ config.status libtool .deps/ .dirstamp -build-aux/ *.lo *.o *~ src/libsecp256k1-config.h src/libsecp256k1-config.h.in src/ecmult_static_context.h -m4/libtool.m4 -m4/ltoptions.m4 -m4/ltsugar.m4 -m4/ltversion.m4 -m4/lt~obsolete.m4 +build-aux/config.guess +build-aux/config.sub +build-aux/depcomp +build-aux/install-sh +build-aux/ltmain.sh +build-aux/m4/libtool.m4 +build-aux/m4/lt~obsolete.m4 +build-aux/m4/ltoptions.m4 +build-aux/m4/ltsugar.m4 +build-aux/m4/ltversion.m4 +build-aux/missing +build-aux/compile +build-aux/test-driver src/stamp-h1 libsecp256k1.pc diff --git a/src/secp256k1/.travis.yml b/src/secp256k1/.travis.yml index 4e1e73c39f57..2c5c63adadcb 100644 --- a/src/secp256k1/.travis.yml +++ b/src/secp256k1/.travis.yml @@ -6,26 +6,31 @@ addons: compiler: - clang - gcc +cache: + directories: + - src/java/guava/ env: global: - - FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no schnorr=no RECOVERY=no + - FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no schnorr=no RECOVERY=no EXPERIMENTAL=no + - GUAVA_URL=https://search.maven.org/remotecontent?filepath=com/google/guava/guava/18.0/guava-18.0.jar GUAVA_JAR=src/java/guava/guava-18.0.jar matrix: - SCALAR=32bit RECOVERY=yes - - SCALAR=32bit FIELD=32bit ECDH=yes + - SCALAR=32bit FIELD=32bit ECDH=yes EXPERIMENTAL=yes - SCALAR=64bit - FIELD=64bit RECOVERY=yes - FIELD=64bit ENDOMORPHISM=yes - - FIELD=64bit ENDOMORPHISM=yes ECDH=yes + - FIELD=64bit ENDOMORPHISM=yes ECDH=yes EXPERIMENTAL=yes - FIELD=64bit ASM=x86_64 - FIELD=64bit ENDOMORPHISM=yes ASM=x86_64 - - FIELD=32bit SCHNORR=yes + - FIELD=32bit SCHNORR=yes EXPERIMENTAL=yes - FIELD=32bit ENDOMORPHISM=yes - BIGNUM=no - - BIGNUM=no ENDOMORPHISM=yes SCHNORR=yes RECOVERY=yes + - BIGNUM=no ENDOMORPHISM=yes SCHNORR=yes RECOVERY=yes EXPERIMENTAL=yes - BIGNUM=no STATICPRECOMPUTATION=no - BUILD=distcheck - EXTRAFLAGS=CPPFLAGS=-DDETERMINISTIC - EXTRAFLAGS=CFLAGS=-O0 + - BUILD=check-java ECDH=yes SCHNORR=yes EXPERIMENTAL=yes matrix: fast_finish: true include: @@ -55,9 +60,11 @@ matrix: packages: - gcc-multilib - libgmp-dev:i386 +before_install: mkdir -p `dirname $GUAVA_JAR` +install: if [ ! -f $GUAVA_JAR ]; then wget $GUAVA_URL -O $GUAVA_JAR; fi before_script: ./autogen.sh script: - if [ -n "$HOST" ]; then export USE_HOST="--host=$HOST"; fi - if [ "x$HOST" = "xi686-linux-gnu" ]; then export CC="$CC -m32"; fi - - ./configure --enable-endomorphism=$ENDOMORPHISM --with-field=$FIELD --with-bignum=$BIGNUM --with-scalar=$SCALAR --enable-ecmult-static-precomputation=$STATICPRECOMPUTATION --enable-module-ecdh=$ECDH --enable-module-schnorr=$SCHNORR --enable-module-recovery=$RECOVERY $EXTRAFLAGS $USE_HOST && make -j2 $BUILD + - ./configure --enable-experimental=$EXPERIMENTAL --enable-endomorphism=$ENDOMORPHISM --with-field=$FIELD --with-bignum=$BIGNUM --with-scalar=$SCALAR --enable-ecmult-static-precomputation=$STATICPRECOMPUTATION --enable-module-ecdh=$ECDH --enable-module-schnorr=$SCHNORR --enable-module-recovery=$RECOVERY $EXTRAFLAGS $USE_HOST && make -j2 $BUILD os: linux diff --git a/src/secp256k1/Makefile.am b/src/secp256k1/Makefile.am index 7772a4e9d282..3d130bdcbd71 100644 --- a/src/secp256k1/Makefile.am +++ b/src/secp256k1/Makefile.am @@ -1,6 +1,12 @@ ACLOCAL_AMFLAGS = -I build-aux/m4 lib_LTLIBRARIES = libsecp256k1.la +if USE_JNI +JNI_LIB = libsecp256k1_jni.la +noinst_LTLIBRARIES = $(JNI_LIB) +else +JNI_LIB = +endif include_HEADERS = include/secp256k1.h noinst_HEADERS = noinst_HEADERS += src/scalar.h @@ -32,6 +38,7 @@ noinst_HEADERS += src/field_5x52_impl.h noinst_HEADERS += src/field_5x52_int128_impl.h noinst_HEADERS += src/field_5x52_asm_impl.h noinst_HEADERS += src/java/org_bitcoin_NativeSecp256k1.h +noinst_HEADERS += src/java/org_bitcoin_Secp256k1Context.h noinst_HEADERS += src/util.h noinst_HEADERS += src/testrand.h noinst_HEADERS += src/testrand_impl.h @@ -45,35 +52,80 @@ noinst_HEADERS += contrib/lax_der_parsing.c noinst_HEADERS += contrib/lax_der_privatekey_parsing.h noinst_HEADERS += contrib/lax_der_privatekey_parsing.c +if USE_EXTERNAL_ASM +COMMON_LIB = libsecp256k1_common.la +noinst_LTLIBRARIES = $(COMMON_LIB) +else +COMMON_LIB = +endif + pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libsecp256k1.pc +if USE_EXTERNAL_ASM +if USE_ASM_ARM +libsecp256k1_common_la_SOURCES = src/asm/field_10x26_arm.s +endif +endif + libsecp256k1_la_SOURCES = src/secp256k1.c -libsecp256k1_la_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/src $(SECP_INCLUDES) -libsecp256k1_la_LIBADD = $(SECP_LIBS) +libsecp256k1_la_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/include -I$(top_srcdir)/src $(SECP_INCLUDES) +libsecp256k1_la_LIBADD = $(JNI_LIB) $(SECP_LIBS) $(COMMON_LIB) +libsecp256k1_jni_la_SOURCES = src/java/org_bitcoin_NativeSecp256k1.c src/java/org_bitcoin_Secp256k1Context.c +libsecp256k1_jni_la_CPPFLAGS = -DSECP256K1_BUILD $(JNI_INCLUDES) noinst_PROGRAMS = if USE_BENCHMARK noinst_PROGRAMS += bench_verify bench_sign bench_internal bench_verify_SOURCES = src/bench_verify.c -bench_verify_LDADD = libsecp256k1.la $(SECP_LIBS) +bench_verify_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB) bench_sign_SOURCES = src/bench_sign.c -bench_sign_LDADD = libsecp256k1.la $(SECP_LIBS) +bench_sign_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB) bench_internal_SOURCES = src/bench_internal.c -bench_internal_LDADD = $(SECP_LIBS) -bench_internal_CPPFLAGS = $(SECP_INCLUDES) +bench_internal_LDADD = $(SECP_LIBS) $(COMMON_LIB) +bench_internal_CPPFLAGS = -DSECP256K1_BUILD $(SECP_INCLUDES) endif if USE_TESTS noinst_PROGRAMS += tests tests_SOURCES = src/tests.c -tests_CPPFLAGS = -DVERIFY -I$(top_srcdir)/src -I$(top_srcdir)/include $(SECP_INCLUDES) $(SECP_TEST_INCLUDES) -tests_LDADD = $(SECP_LIBS) $(SECP_TEST_LIBS) +tests_CPPFLAGS = -DSECP256K1_BUILD -DVERIFY -I$(top_srcdir)/src -I$(top_srcdir)/include $(SECP_INCLUDES) $(SECP_TEST_INCLUDES) +tests_LDADD = $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB) tests_LDFLAGS = -static TESTS = tests endif +JAVAROOT=src/java +JAVAORG=org/bitcoin +JAVA_GUAVA=$(srcdir)/$(JAVAROOT)/guava/guava-18.0.jar +CLASSPATH_ENV=CLASSPATH=$(JAVA_GUAVA) +JAVA_FILES= \ + $(JAVAROOT)/$(JAVAORG)/NativeSecp256k1.java \ + $(JAVAROOT)/$(JAVAORG)/NativeSecp256k1Test.java \ + $(JAVAROOT)/$(JAVAORG)/NativeSecp256k1Util.java \ + $(JAVAROOT)/$(JAVAORG)/Secp256k1Context.java + +if USE_JNI + +$(JAVA_GUAVA): + @echo Guava is missing. Fetch it via: \ + wget https://search.maven.org/remotecontent?filepath=com/google/guava/guava/18.0/guava-18.0.jar -O $(@) + @false + +.stamp-java: $(JAVA_FILES) + @echo Compiling $^ + $(AM_V_at)$(CLASSPATH_ENV) javac $^ + @touch $@ + +if USE_TESTS + +check-java: libsecp256k1.la $(JAVA_GUAVA) .stamp-java + $(AM_V_at)java -Djava.library.path="./:./src:./src/.libs:.libs/" -cp "$(JAVA_GUAVA):$(JAVAROOT)" $(JAVAORG)/NativeSecp256k1Test + +endif +endif + if USE_ECMULT_STATIC_PRECOMPUTATION CPPFLAGS_FOR_BUILD +=-I$(top_srcdir) CFLAGS_FOR_BUILD += -Wall -Wextra -Wno-unused-function @@ -93,10 +145,10 @@ $(bench_internal_OBJECTS): src/ecmult_static_context.h src/ecmult_static_context.h: $(gen_context_BIN) ./$(gen_context_BIN) -CLEANFILES = $(gen_context_BIN) src/ecmult_static_context.h +CLEANFILES = $(gen_context_BIN) src/ecmult_static_context.h $(JAVAROOT)/$(JAVAORG)/*.class .stamp-java endif -EXTRA_DIST = autogen.sh src/gen_context.c src/basic-config.h +EXTRA_DIST = autogen.sh src/gen_context.c src/basic-config.h $(JAVA_FILES) if ENABLE_MODULE_ECDH include src/modules/ecdh/Makefile.am.include diff --git a/src/secp256k1/README.md b/src/secp256k1/README.md index 6095db42205b..8cd344ea8123 100644 --- a/src/secp256k1/README.md +++ b/src/secp256k1/README.md @@ -1,7 +1,7 @@ libsecp256k1 ============ -[![Build Status](https://travis-ci.org/bitcoin/secp256k1.svg?branch=master)](https://travis-ci.org/bitcoin/secp256k1) +[![Build Status](https://travis-ci.org/bitcoin-core/secp256k1.svg?branch=master)](https://travis-ci.org/bitcoin-core/secp256k1) Optimized C library for EC operations on curve secp256k1. diff --git a/src/secp256k1/build-aux/m4/ax_jni_include_dir.m4 b/src/secp256k1/build-aux/m4/ax_jni_include_dir.m4 new file mode 100644 index 000000000000..1fc36276144a --- /dev/null +++ b/src/secp256k1/build-aux/m4/ax_jni_include_dir.m4 @@ -0,0 +1,140 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_jni_include_dir.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_JNI_INCLUDE_DIR +# +# DESCRIPTION +# +# AX_JNI_INCLUDE_DIR finds include directories needed for compiling +# programs using the JNI interface. +# +# JNI include directories are usually in the Java distribution. This is +# deduced from the value of $JAVA_HOME, $JAVAC, or the path to "javac", in +# that order. When this macro completes, a list of directories is left in +# the variable JNI_INCLUDE_DIRS. +# +# Example usage follows: +# +# AX_JNI_INCLUDE_DIR +# +# for JNI_INCLUDE_DIR in $JNI_INCLUDE_DIRS +# do +# CPPFLAGS="$CPPFLAGS -I$JNI_INCLUDE_DIR" +# done +# +# If you want to force a specific compiler: +# +# - at the configure.in level, set JAVAC=yourcompiler before calling +# AX_JNI_INCLUDE_DIR +# +# - at the configure level, setenv JAVAC +# +# Note: This macro can work with the autoconf M4 macros for Java programs. +# This particular macro is not part of the original set of macros. +# +# LICENSE +# +# Copyright (c) 2008 Don Anderson +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 10 + +AU_ALIAS([AC_JNI_INCLUDE_DIR], [AX_JNI_INCLUDE_DIR]) +AC_DEFUN([AX_JNI_INCLUDE_DIR],[ + +JNI_INCLUDE_DIRS="" + +if test "x$JAVA_HOME" != x; then + _JTOPDIR="$JAVA_HOME" +else + if test "x$JAVAC" = x; then + JAVAC=javac + fi + AC_PATH_PROG([_ACJNI_JAVAC], [$JAVAC], [no]) + if test "x$_ACJNI_JAVAC" = xno; then + AC_MSG_WARN([cannot find JDK; try setting \$JAVAC or \$JAVA_HOME]) + fi + _ACJNI_FOLLOW_SYMLINKS("$_ACJNI_JAVAC") + _JTOPDIR=`echo "$_ACJNI_FOLLOWED" | sed -e 's://*:/:g' -e 's:/[[^/]]*$::'` +fi + +case "$host_os" in + darwin*) _JTOPDIR=`echo "$_JTOPDIR" | sed -e 's:/[[^/]]*$::'` + _JINC="$_JTOPDIR/Headers";; + *) _JINC="$_JTOPDIR/include";; +esac +_AS_ECHO_LOG([_JTOPDIR=$_JTOPDIR]) +_AS_ECHO_LOG([_JINC=$_JINC]) + +# On Mac OS X 10.6.4, jni.h is a symlink: +# /System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers/jni.h +# -> ../../CurrentJDK/Headers/jni.h. + +AC_CACHE_CHECK(jni headers, ac_cv_jni_header_path, +[ +if test -f "$_JINC/jni.h"; then + ac_cv_jni_header_path="$_JINC" + JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $ac_cv_jni_header_path" +else + _JTOPDIR=`echo "$_JTOPDIR" | sed -e 's:/[[^/]]*$::'` + if test -f "$_JTOPDIR/include/jni.h"; then + ac_cv_jni_header_path="$_JTOPDIR/include" + JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $ac_cv_jni_header_path" + else + ac_cv_jni_header_path=none + fi +fi +]) + + + +# get the likely subdirectories for system specific java includes +case "$host_os" in +bsdi*) _JNI_INC_SUBDIRS="bsdos";; +darwin*) _JNI_INC_SUBDIRS="darwin";; +freebsd*) _JNI_INC_SUBDIRS="freebsd";; +linux*) _JNI_INC_SUBDIRS="linux genunix";; +osf*) _JNI_INC_SUBDIRS="alpha";; +solaris*) _JNI_INC_SUBDIRS="solaris";; +mingw*) _JNI_INC_SUBDIRS="win32";; +cygwin*) _JNI_INC_SUBDIRS="win32";; +*) _JNI_INC_SUBDIRS="genunix";; +esac + +if test "x$ac_cv_jni_header_path" != "xnone"; then + # add any subdirectories that are present + for JINCSUBDIR in $_JNI_INC_SUBDIRS + do + if test -d "$_JTOPDIR/include/$JINCSUBDIR"; then + JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $_JTOPDIR/include/$JINCSUBDIR" + fi + done +fi +]) + +# _ACJNI_FOLLOW_SYMLINKS +# Follows symbolic links on , +# finally setting variable _ACJNI_FOLLOWED +# ---------------------------------------- +AC_DEFUN([_ACJNI_FOLLOW_SYMLINKS],[ +# find the include directory relative to the javac executable +_cur="$1" +while ls -ld "$_cur" 2>/dev/null | grep " -> " >/dev/null; do + AC_MSG_CHECKING([symlink for $_cur]) + _slink=`ls -ld "$_cur" | sed 's/.* -> //'` + case "$_slink" in + /*) _cur="$_slink";; + # 'X' avoids triggering unwanted echo options. + *) _cur=`echo "X$_cur" | sed -e 's/^X//' -e 's:[[^/]]*$::'`"$_slink";; + esac + AC_MSG_RESULT([$_cur]) +done +_ACJNI_FOLLOWED="$_cur" +])# _ACJNI diff --git a/src/secp256k1/build-aux/m4/bitcoin_secp.m4 b/src/secp256k1/build-aux/m4/bitcoin_secp.m4 index d41bbb648708..b25d8adb92c7 100644 --- a/src/secp256k1/build-aux/m4/bitcoin_secp.m4 +++ b/src/secp256k1/build-aux/m4/bitcoin_secp.m4 @@ -3,13 +3,13 @@ AC_DEFUN([SECP_INT128_CHECK],[ has_int128=$ac_cv_type___int128 ]) -dnl +dnl escape "$0x" below using the m4 quadrigaph @S|@, and escape it again with a \ for the shell. AC_DEFUN([SECP_64BIT_ASM_CHECK],[ AC_MSG_CHECKING(for x86_64 assembly availability) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]],[[ uint64_t a = 11, tmp; - __asm__ __volatile__("movq $0x100000000,%1; mulq %%rsi" : "+a"(a) : "S"(tmp) : "cc", "%rdx"); + __asm__ __volatile__("movq \@S|@0x100000000,%1; mulq %%rsi" : "+a"(a) : "S"(tmp) : "cc", "%rdx"); ]])],[has_64bit_asm=yes],[has_64bit_asm=no]) AC_MSG_RESULT([$has_64bit_asm]) ]) diff --git a/src/secp256k1/configure.ac b/src/secp256k1/configure.ac index 786d8dcfb99e..0743c36690c7 100644 --- a/src/secp256k1/configure.ac +++ b/src/secp256k1/configure.ac @@ -29,6 +29,7 @@ AC_PROG_CC_C89 if test x"$ac_cv_prog_cc_c89" = x"no"; then AC_MSG_ERROR([c89 compiler support required]) fi +AM_PROG_AS case $host_os in *darwin*) @@ -93,23 +94,33 @@ AC_ARG_ENABLE(tests, [use_tests=$enableval], [use_tests=yes]) +AC_ARG_ENABLE(openssl_tests, + AS_HELP_STRING([--enable-openssl-tests],[enable OpenSSL tests, if OpenSSL is available (default is auto)]), + [enable_openssl_tests=$enableval], + [enable_openssl_tests=auto]) + +AC_ARG_ENABLE(experimental, + AS_HELP_STRING([--enable-experimental],[allow experimental configure options (default is no)]), + [use_experimental=$enableval], + [use_experimental=no]) + AC_ARG_ENABLE(endomorphism, AS_HELP_STRING([--enable-endomorphism],[enable endomorphism (default is no)]), [use_endomorphism=$enableval], [use_endomorphism=no]) - + AC_ARG_ENABLE(ecmult_static_precomputation, AS_HELP_STRING([--enable-ecmult-static-precomputation],[enable precomputed ecmult table for signing (default is yes)]), [use_ecmult_static_precomputation=$enableval], - [use_ecmult_static_precomputation=yes]) + [use_ecmult_static_precomputation=auto]) AC_ARG_ENABLE(module_ecdh, - AS_HELP_STRING([--enable-module-ecdh],[enable ECDH shared secret computation (default is no)]), + AS_HELP_STRING([--enable-module-ecdh],[enable ECDH shared secret computation (experimental)]), [enable_module_ecdh=$enableval], [enable_module_ecdh=no]) AC_ARG_ENABLE(module_schnorr, - AS_HELP_STRING([--enable-module-schnorr],[enable Schnorr signature module (default is no)]), + AS_HELP_STRING([--enable-module-schnorr],[enable Schnorr signature module (experimental)]), [enable_module_schnorr=$enableval], [enable_module_schnorr=no]) @@ -118,6 +129,11 @@ AC_ARG_ENABLE(module_recovery, [enable_module_recovery=$enableval], [enable_module_recovery=no]) +AC_ARG_ENABLE(jni, + AS_HELP_STRING([--enable-jni],[enable libsecp256k1_jni (default is auto)]), + [use_jni=$enableval], + [use_jni=auto]) + AC_ARG_WITH([field], [AS_HELP_STRING([--with-field=64bit|32bit|auto], [Specify Field Implementation. Default is auto])],[req_field=$withval], [req_field=auto]) @@ -127,8 +143,8 @@ AC_ARG_WITH([bignum], [AS_HELP_STRING([--with-bignum=gmp|no|auto], AC_ARG_WITH([scalar], [AS_HELP_STRING([--with-scalar=64bit|32bit|auto], [Specify scalar implementation. Default is auto])],[req_scalar=$withval], [req_scalar=auto]) -AC_ARG_WITH([asm], [AS_HELP_STRING([--with-asm=x86_64|no|auto] -[Specify assembly optimizations to use. Default is auto])],[req_asm=$withval], [req_asm=auto]) +AC_ARG_WITH([asm], [AS_HELP_STRING([--with-asm=x86_64|arm|no|auto] +[Specify assembly optimizations to use. Default is auto (experimental: arm)])],[req_asm=$withval], [req_asm=auto]) AC_CHECK_TYPES([__int128]) @@ -138,6 +154,34 @@ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[void myfunc() {__builtin_expect(0,0);}]])], [ AC_MSG_RESULT([no]) ]) +if test x"$use_ecmult_static_precomputation" != x"no"; then + save_cross_compiling=$cross_compiling + cross_compiling=no + TEMP_CC="$CC" + CC="$CC_FOR_BUILD" + AC_MSG_CHECKING([native compiler: ${CC_FOR_BUILD}]) + AC_RUN_IFELSE( + [AC_LANG_PROGRAM([], [return 0])], + [working_native_cc=yes], + [working_native_cc=no],[dnl]) + CC="$TEMP_CC" + cross_compiling=$save_cross_compiling + + if test x"$working_native_cc" = x"no"; then + set_precomp=no + if test x"$use_ecmult_static_precomputation" = x"yes"; then + AC_MSG_ERROR([${CC_FOR_BUILD} does not produce working binaries. Please set CC_FOR_BUILD]) + else + AC_MSG_RESULT([${CC_FOR_BUILD} does not produce working binaries. Please set CC_FOR_BUILD]) + fi + else + AC_MSG_RESULT([ok]) + set_precomp=yes + fi +else + set_precomp=no +fi + if test x"$req_asm" = x"auto"; then SECP_64BIT_ASM_CHECK if test x"$has_64bit_asm" = x"yes"; then @@ -155,6 +199,8 @@ else AC_MSG_ERROR([x86_64 assembly optimization requested but not available]) fi ;; + arm) + ;; no) ;; *) @@ -247,10 +293,15 @@ else fi # select assembly optimization +use_external_asm=no + case $set_asm in x86_64) AC_DEFINE(USE_ASM_X86_64, 1, [Define this symbol to enable x86_64 assembly optimizations]) ;; +arm) + use_external_asm=yes + ;; no) ;; *) @@ -305,16 +356,51 @@ esac if test x"$use_tests" = x"yes"; then SECP_OPENSSL_CHECK if test x"$has_openssl_ec" = x"yes"; then - AC_DEFINE(ENABLE_OPENSSL_TESTS, 1, [Define this symbol if OpenSSL EC functions are available]) - SECP_TEST_INCLUDES="$SSL_CFLAGS $CRYPTO_CFLAGS" - SECP_TEST_LIBS="$CRYPTO_LIBS" - - case $host in - *mingw*) - SECP_TEST_LIBS="$SECP_TEST_LIBS -lgdi32" - ;; - esac + if test x"$enable_openssl_tests" != x"no"; then + AC_DEFINE(ENABLE_OPENSSL_TESTS, 1, [Define this symbol if OpenSSL EC functions are available]) + SECP_TEST_INCLUDES="$SSL_CFLAGS $CRYPTO_CFLAGS" + SECP_TEST_LIBS="$CRYPTO_LIBS" + + case $host in + *mingw*) + SECP_TEST_LIBS="$SECP_TEST_LIBS -lgdi32" + ;; + esac + fi + else + if test x"$enable_openssl_tests" = x"yes"; then + AC_MSG_ERROR([OpenSSL tests requested but OpenSSL with EC support is not available]) + fi + fi +else + if test x"$enable_openssl_tests" = x"yes"; then + AC_MSG_ERROR([OpenSSL tests requested but tests are not enabled]) + fi +fi +if test x"$use_jni" != x"no"; then + AX_JNI_INCLUDE_DIR + have_jni_dependencies=yes + if test x"$enable_module_schnorr" = x"no"; then + have_jni_dependencies=no + fi + if test x"$enable_module_ecdh" = x"no"; then + have_jni_dependencies=no + fi + if test "x$JNI_INCLUDE_DIRS" = "x"; then + have_jni_dependencies=no + fi + if test "x$have_jni_dependencies" = "xno"; then + if test x"$use_jni" = x"yes"; then + AC_MSG_ERROR([jni support explicitly requested but headers/dependencies were not found. Enable ECDH and Schnorr and try again.]) + fi + AC_MSG_WARN([jni headers/dependencies not found. jni support disabled]) + use_jni=no + else + use_jni=yes + for JNI_INCLUDE_DIR in $JNI_INCLUDE_DIRS; do + JNI_INCLUDES="$JNI_INCLUDES -I$JNI_INCLUDE_DIR" + done fi fi @@ -345,18 +431,43 @@ fi AC_C_BIGENDIAN() +if test x"$use_external_asm" = x"yes"; then + AC_DEFINE(USE_EXTERNAL_ASM, 1, [Define this symbol if an external (non-inline) assembly implementation is used]) +fi + +AC_MSG_NOTICE([Using static precomputation: $set_precomp]) AC_MSG_NOTICE([Using assembly optimizations: $set_asm]) AC_MSG_NOTICE([Using field implementation: $set_field]) AC_MSG_NOTICE([Using bignum implementation: $set_bignum]) AC_MSG_NOTICE([Using scalar implementation: $set_scalar]) AC_MSG_NOTICE([Using endomorphism optimizations: $use_endomorphism]) AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh]) - AC_MSG_NOTICE([Building Schnorr signatures module: $enable_module_schnorr]) AC_MSG_NOTICE([Building ECDSA pubkey recovery module: $enable_module_recovery]) +AC_MSG_NOTICE([Using jni: $use_jni]) + +if test x"$enable_experimental" = x"yes"; then + AC_MSG_NOTICE([******]) + AC_MSG_NOTICE([WARNING: experimental build]) + AC_MSG_NOTICE([Experimental features do not have stable APIs or properties, and may not be safe for production use.]) + AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh]) + AC_MSG_NOTICE([Building Schnorr signatures module: $enable_module_schnorr]) + AC_MSG_NOTICE([******]) +else + if test x"$enable_module_schnorr" = x"yes"; then + AC_MSG_ERROR([Schnorr signature module is experimental. Use --enable-experimental to allow.]) + fi + if test x"$enable_module_ecdh" = x"yes"; then + AC_MSG_ERROR([ECDH module is experimental. Use --enable-experimental to allow.]) + fi + if test x"$set_asm" = x"arm"; then + AC_MSG_ERROR([ARM assembly optimization is experimental. Use --enable-experimental to allow.]) + fi +fi AC_CONFIG_HEADERS([src/libsecp256k1-config.h]) AC_CONFIG_FILES([Makefile libsecp256k1.pc]) +AC_SUBST(JNI_INCLUDES) AC_SUBST(SECP_INCLUDES) AC_SUBST(SECP_LIBS) AC_SUBST(SECP_TEST_LIBS) @@ -367,6 +478,9 @@ AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$use_ecmult_static_pr AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_SCHNORR], [test x"$enable_module_schnorr" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"]) +AM_CONDITIONAL([USE_JNI], [test x"$use_jni" == x"yes"]) +AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"]) +AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"]) dnl make sure nothing new is exported so that we don't break the cache PKGCONFIG_PATH_TEMP="$PKG_CONFIG_PATH" diff --git a/src/secp256k1/libsecp256k1.pc.in b/src/secp256k1/libsecp256k1.pc.in index 1c72dd00037b..a0d006f1131f 100644 --- a/src/secp256k1/libsecp256k1.pc.in +++ b/src/secp256k1/libsecp256k1.pc.in @@ -5,7 +5,7 @@ includedir=@includedir@ Name: libsecp256k1 Description: Optimized C library for EC operations on curve secp256k1 -URL: https://github.com/bitcoin/secp256k1 +URL: https://github.com/bitcoin-core/secp256k1 Version: @PACKAGE_VERSION@ Cflags: -I${includedir} Libs.private: @SECP_LIBS@ diff --git a/src/secp256k1/sage/group_prover.sage b/src/secp256k1/sage/group_prover.sage new file mode 100644 index 000000000000..ab580c5b23bb --- /dev/null +++ b/src/secp256k1/sage/group_prover.sage @@ -0,0 +1,322 @@ +# This code supports verifying group implementations which have branches +# or conditional statements (like cmovs), by allowing each execution path +# to independently set assumptions on input or intermediary variables. +# +# The general approach is: +# * A constraint is a tuple of two sets of of symbolic expressions: +# the first of which are required to evaluate to zero, the second of which +# are required to evaluate to nonzero. +# - A constraint is said to be conflicting if any of its nonzero expressions +# is in the ideal with basis the zero expressions (in other words: when the +# zero expressions imply that one of the nonzero expressions are zero). +# * There is a list of laws that describe the intended behaviour, including +# laws for addition and doubling. Each law is called with the symbolic point +# coordinates as arguments, and returns: +# - A constraint describing the assumptions under which it is applicable, +# called "assumeLaw" +# - A constraint describing the requirements of the law, called "require" +# * Implementations are transliterated into functions that operate as well on +# algebraic input points, and are called once per combination of branches +# exectured. Each execution returns: +# - A constraint describing the assumptions this implementation requires +# (such as Z1=1), called "assumeFormula" +# - A constraint describing the assumptions this specific branch requires, +# but which is by construction guaranteed to cover the entire space by +# merging the results from all branches, called "assumeBranch" +# - The result of the computation +# * All combinations of laws with implementation branches are tried, and: +# - If the combination of assumeLaw, assumeFormula, and assumeBranch results +# in a conflict, it means this law does not apply to this branch, and it is +# skipped. +# - For others, we try to prove the require constraints hold, assuming the +# information in assumeLaw + assumeFormula + assumeBranch, and if this does +# not succeed, we fail. +# + To prove an expression is zero, we check whether it belongs to the +# ideal with the assumed zero expressions as basis. This test is exact. +# + To prove an expression is nonzero, we check whether each of its +# factors is contained in the set of nonzero assumptions' factors. +# This test is not exact, so various combinations of original and +# reduced expressions' factors are tried. +# - If we succeed, we print out the assumptions from assumeFormula that +# weren't implied by assumeLaw already. Those from assumeBranch are skipped, +# as we assume that all constraints in it are complementary with each other. +# +# Based on the sage verification scripts used in the Explicit-Formulas Database +# by Tanja Lange and others, see http://hyperelliptic.org/EFD + +class fastfrac: + """Fractions over rings.""" + + def __init__(self,R,top,bot=1): + """Construct a fractional, given a ring, a numerator, and denominator.""" + self.R = R + if parent(top) == ZZ or parent(top) == R: + self.top = R(top) + self.bot = R(bot) + elif top.__class__ == fastfrac: + self.top = top.top + self.bot = top.bot * bot + else: + self.top = R(numerator(top)) + self.bot = R(denominator(top)) * bot + + def iszero(self,I): + """Return whether this fraction is zero given an ideal.""" + return self.top in I and self.bot not in I + + def reduce(self,assumeZero): + zero = self.R.ideal(map(numerator, assumeZero)) + return fastfrac(self.R, zero.reduce(self.top)) / fastfrac(self.R, zero.reduce(self.bot)) + + def __add__(self,other): + """Add two fractions.""" + if parent(other) == ZZ: + return fastfrac(self.R,self.top + self.bot * other,self.bot) + if other.__class__ == fastfrac: + return fastfrac(self.R,self.top * other.bot + self.bot * other.top,self.bot * other.bot) + return NotImplemented + + def __sub__(self,other): + """Subtract two fractions.""" + if parent(other) == ZZ: + return fastfrac(self.R,self.top - self.bot * other,self.bot) + if other.__class__ == fastfrac: + return fastfrac(self.R,self.top * other.bot - self.bot * other.top,self.bot * other.bot) + return NotImplemented + + def __neg__(self): + """Return the negation of a fraction.""" + return fastfrac(self.R,-self.top,self.bot) + + def __mul__(self,other): + """Multiply two fractions.""" + if parent(other) == ZZ: + return fastfrac(self.R,self.top * other,self.bot) + if other.__class__ == fastfrac: + return fastfrac(self.R,self.top * other.top,self.bot * other.bot) + return NotImplemented + + def __rmul__(self,other): + """Multiply something else with a fraction.""" + return self.__mul__(other) + + def __div__(self,other): + """Divide two fractions.""" + if parent(other) == ZZ: + return fastfrac(self.R,self.top,self.bot * other) + if other.__class__ == fastfrac: + return fastfrac(self.R,self.top * other.bot,self.bot * other.top) + return NotImplemented + + def __pow__(self,other): + """Compute a power of a fraction.""" + if parent(other) == ZZ: + if other < 0: + # Negative powers require flipping top and bottom + return fastfrac(self.R,self.bot ^ (-other),self.top ^ (-other)) + else: + return fastfrac(self.R,self.top ^ other,self.bot ^ other) + return NotImplemented + + def __str__(self): + return "fastfrac((" + str(self.top) + ") / (" + str(self.bot) + "))" + def __repr__(self): + return "%s" % self + + def numerator(self): + return self.top + +class constraints: + """A set of constraints, consisting of zero and nonzero expressions. + + Constraints can either be used to express knowledge or a requirement. + + Both the fields zero and nonzero are maps from expressions to description + strings. The expressions that are the keys in zero are required to be zero, + and the expressions that are the keys in nonzero are required to be nonzero. + + Note that (a != 0) and (b != 0) is the same as (a*b != 0), so all keys in + nonzero could be multiplied into a single key. This is often much less + efficient to work with though, so we keep them separate inside the + constraints. This allows higher-level code to do fast checks on the individual + nonzero elements, or combine them if needed for stronger checks. + + We can't multiply the different zero elements, as it would suffice for one of + the factors to be zero, instead of all of them. Instead, the zero elements are + typically combined into an ideal first. + """ + + def __init__(self, **kwargs): + if 'zero' in kwargs: + self.zero = dict(kwargs['zero']) + else: + self.zero = dict() + if 'nonzero' in kwargs: + self.nonzero = dict(kwargs['nonzero']) + else: + self.nonzero = dict() + + def negate(self): + return constraints(zero=self.nonzero, nonzero=self.zero) + + def __add__(self, other): + zero = self.zero.copy() + zero.update(other.zero) + nonzero = self.nonzero.copy() + nonzero.update(other.nonzero) + return constraints(zero=zero, nonzero=nonzero) + + def __str__(self): + return "constraints(zero=%s,nonzero=%s)" % (self.zero, self.nonzero) + + def __repr__(self): + return "%s" % self + + +def conflicts(R, con): + """Check whether any of the passed non-zero assumptions is implied by the zero assumptions""" + zero = R.ideal(map(numerator, con.zero)) + if 1 in zero: + return True + # First a cheap check whether any of the individual nonzero terms conflict on + # their own. + for nonzero in con.nonzero: + if nonzero.iszero(zero): + return True + # It can be the case that entries in the nonzero set do not individually + # conflict with the zero set, but their combination does. For example, knowing + # that either x or y is zero is equivalent to having x*y in the zero set. + # Having x or y individually in the nonzero set is not a conflict, but both + # simultaneously is, so that is the right thing to check for. + if reduce(lambda a,b: a * b, con.nonzero, fastfrac(R, 1)).iszero(zero): + return True + return False + + +def get_nonzero_set(R, assume): + """Calculate a simple set of nonzero expressions""" + zero = R.ideal(map(numerator, assume.zero)) + nonzero = set() + for nz in map(numerator, assume.nonzero): + for (f,n) in nz.factor(): + nonzero.add(f) + rnz = zero.reduce(nz) + for (f,n) in rnz.factor(): + nonzero.add(f) + return nonzero + + +def prove_nonzero(R, exprs, assume): + """Check whether an expression is provably nonzero, given assumptions""" + zero = R.ideal(map(numerator, assume.zero)) + nonzero = get_nonzero_set(R, assume) + expl = set() + ok = True + for expr in exprs: + if numerator(expr) in zero: + return (False, [exprs[expr]]) + allexprs = reduce(lambda a,b: numerator(a)*numerator(b), exprs, 1) + for (f, n) in allexprs.factor(): + if f not in nonzero: + ok = False + if ok: + return (True, None) + ok = True + for (f, n) in zero.reduce(numerator(allexprs)).factor(): + if f not in nonzero: + ok = False + if ok: + return (True, None) + ok = True + for expr in exprs: + for (f,n) in numerator(expr).factor(): + if f not in nonzero: + ok = False + if ok: + return (True, None) + ok = True + for expr in exprs: + for (f,n) in zero.reduce(numerator(expr)).factor(): + if f not in nonzero: + expl.add(exprs[expr]) + if expl: + return (False, list(expl)) + else: + return (True, None) + + +def prove_zero(R, exprs, assume): + """Check whether all of the passed expressions are provably zero, given assumptions""" + r, e = prove_nonzero(R, dict(map(lambda x: (fastfrac(R, x.bot, 1), exprs[x]), exprs)), assume) + if not r: + return (False, map(lambda x: "Possibly zero denominator: %s" % x, e)) + zero = R.ideal(map(numerator, assume.zero)) + nonzero = prod(x for x in assume.nonzero) + expl = [] + for expr in exprs: + if not expr.iszero(zero): + expl.append(exprs[expr]) + if not expl: + return (True, None) + return (False, expl) + + +def describe_extra(R, assume, assumeExtra): + """Describe what assumptions are added, given existing assumptions""" + zerox = assume.zero.copy() + zerox.update(assumeExtra.zero) + zero = R.ideal(map(numerator, assume.zero)) + zeroextra = R.ideal(map(numerator, zerox)) + nonzero = get_nonzero_set(R, assume) + ret = set() + # Iterate over the extra zero expressions + for base in assumeExtra.zero: + if base not in zero: + add = [] + for (f, n) in numerator(base).factor(): + if f not in nonzero: + add += ["%s" % f] + if add: + ret.add((" * ".join(add)) + " = 0 [%s]" % assumeExtra.zero[base]) + # Iterate over the extra nonzero expressions + for nz in assumeExtra.nonzero: + nzr = zeroextra.reduce(numerator(nz)) + if nzr not in zeroextra: + for (f,n) in nzr.factor(): + if zeroextra.reduce(f) not in nonzero: + ret.add("%s != 0" % zeroextra.reduce(f)) + return ", ".join(x for x in ret) + + +def check_symbolic(R, assumeLaw, assumeAssert, assumeBranch, require): + """Check a set of zero and nonzero requirements, given a set of zero and nonzero assumptions""" + assume = assumeLaw + assumeAssert + assumeBranch + + if conflicts(R, assume): + # This formula does not apply + return None + + describe = describe_extra(R, assumeLaw + assumeBranch, assumeAssert) + + ok, msg = prove_zero(R, require.zero, assume) + if not ok: + return "FAIL, %s fails (assuming %s)" % (str(msg), describe) + + res, expl = prove_nonzero(R, require.nonzero, assume) + if not res: + return "FAIL, %s fails (assuming %s)" % (str(expl), describe) + + if describe != "": + return "OK (assuming %s)" % describe + else: + return "OK" + + +def concrete_verify(c): + for k in c.zero: + if k != 0: + return (False, c.zero[k]) + for k in c.nonzero: + if k == 0: + return (False, c.nonzero[k]) + return (True, None) diff --git a/src/secp256k1/sage/secp256k1.sage b/src/secp256k1/sage/secp256k1.sage new file mode 100644 index 000000000000..a97e732f7fa3 --- /dev/null +++ b/src/secp256k1/sage/secp256k1.sage @@ -0,0 +1,306 @@ +# Test libsecp256k1' group operation implementations using prover.sage + +import sys + +load("group_prover.sage") +load("weierstrass_prover.sage") + +def formula_secp256k1_gej_double_var(a): + """libsecp256k1's secp256k1_gej_double_var, used by various addition functions""" + rz = a.Z * a.Y + rz = rz * 2 + t1 = a.X^2 + t1 = t1 * 3 + t2 = t1^2 + t3 = a.Y^2 + t3 = t3 * 2 + t4 = t3^2 + t4 = t4 * 2 + t3 = t3 * a.X + rx = t3 + rx = rx * 4 + rx = -rx + rx = rx + t2 + t2 = -t2 + t3 = t3 * 6 + t3 = t3 + t2 + ry = t1 * t3 + t2 = -t4 + ry = ry + t2 + return jacobianpoint(rx, ry, rz) + +def formula_secp256k1_gej_add_var(branch, a, b): + """libsecp256k1's secp256k1_gej_add_var""" + if branch == 0: + return (constraints(), constraints(nonzero={a.Infinity : 'a_infinite'}), b) + if branch == 1: + return (constraints(), constraints(zero={a.Infinity : 'a_finite'}, nonzero={b.Infinity : 'b_infinite'}), a) + z22 = b.Z^2 + z12 = a.Z^2 + u1 = a.X * z22 + u2 = b.X * z12 + s1 = a.Y * z22 + s1 = s1 * b.Z + s2 = b.Y * z12 + s2 = s2 * a.Z + h = -u1 + h = h + u2 + i = -s1 + i = i + s2 + if branch == 2: + r = formula_secp256k1_gej_double_var(a) + return (constraints(), constraints(zero={h : 'h=0', i : 'i=0', a.Infinity : 'a_finite', b.Infinity : 'b_finite'}), r) + if branch == 3: + return (constraints(), constraints(zero={h : 'h=0', a.Infinity : 'a_finite', b.Infinity : 'b_finite'}, nonzero={i : 'i!=0'}), point_at_infinity()) + i2 = i^2 + h2 = h^2 + h3 = h2 * h + h = h * b.Z + rz = a.Z * h + t = u1 * h2 + rx = t + rx = rx * 2 + rx = rx + h3 + rx = -rx + rx = rx + i2 + ry = -rx + ry = ry + t + ry = ry * i + h3 = h3 * s1 + h3 = -h3 + ry = ry + h3 + return (constraints(), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite'}, nonzero={h : 'h!=0'}), jacobianpoint(rx, ry, rz)) + +def formula_secp256k1_gej_add_ge_var(branch, a, b): + """libsecp256k1's secp256k1_gej_add_ge_var, which assume bz==1""" + if branch == 0: + return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(nonzero={a.Infinity : 'a_infinite'}), b) + if branch == 1: + return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(zero={a.Infinity : 'a_finite'}, nonzero={b.Infinity : 'b_infinite'}), a) + z12 = a.Z^2 + u1 = a.X + u2 = b.X * z12 + s1 = a.Y + s2 = b.Y * z12 + s2 = s2 * a.Z + h = -u1 + h = h + u2 + i = -s1 + i = i + s2 + if (branch == 2): + r = formula_secp256k1_gej_double_var(a) + return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite', h : 'h=0', i : 'i=0'}), r) + if (branch == 3): + return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite', h : 'h=0'}, nonzero={i : 'i!=0'}), point_at_infinity()) + i2 = i^2 + h2 = h^2 + h3 = h * h2 + rz = a.Z * h + t = u1 * h2 + rx = t + rx = rx * 2 + rx = rx + h3 + rx = -rx + rx = rx + i2 + ry = -rx + ry = ry + t + ry = ry * i + h3 = h3 * s1 + h3 = -h3 + ry = ry + h3 + return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite'}, nonzero={h : 'h!=0'}), jacobianpoint(rx, ry, rz)) + +def formula_secp256k1_gej_add_zinv_var(branch, a, b): + """libsecp256k1's secp256k1_gej_add_zinv_var""" + bzinv = b.Z^(-1) + if branch == 0: + return (constraints(), constraints(nonzero={b.Infinity : 'b_infinite'}), a) + if branch == 1: + bzinv2 = bzinv^2 + bzinv3 = bzinv2 * bzinv + rx = b.X * bzinv2 + ry = b.Y * bzinv3 + rz = 1 + return (constraints(), constraints(zero={b.Infinity : 'b_finite'}, nonzero={a.Infinity : 'a_infinite'}), jacobianpoint(rx, ry, rz)) + azz = a.Z * bzinv + z12 = azz^2 + u1 = a.X + u2 = b.X * z12 + s1 = a.Y + s2 = b.Y * z12 + s2 = s2 * azz + h = -u1 + h = h + u2 + i = -s1 + i = i + s2 + if branch == 2: + r = formula_secp256k1_gej_double_var(a) + return (constraints(), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite', h : 'h=0', i : 'i=0'}), r) + if branch == 3: + return (constraints(), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite', h : 'h=0'}, nonzero={i : 'i!=0'}), point_at_infinity()) + i2 = i^2 + h2 = h^2 + h3 = h * h2 + rz = a.Z + rz = rz * h + t = u1 * h2 + rx = t + rx = rx * 2 + rx = rx + h3 + rx = -rx + rx = rx + i2 + ry = -rx + ry = ry + t + ry = ry * i + h3 = h3 * s1 + h3 = -h3 + ry = ry + h3 + return (constraints(), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite'}, nonzero={h : 'h!=0'}), jacobianpoint(rx, ry, rz)) + +def formula_secp256k1_gej_add_ge(branch, a, b): + """libsecp256k1's secp256k1_gej_add_ge""" + zeroes = {} + nonzeroes = {} + a_infinity = False + if (branch & 4) != 0: + nonzeroes.update({a.Infinity : 'a_infinite'}) + a_infinity = True + else: + zeroes.update({a.Infinity : 'a_finite'}) + zz = a.Z^2 + u1 = a.X + u2 = b.X * zz + s1 = a.Y + s2 = b.Y * zz + s2 = s2 * a.Z + t = u1 + t = t + u2 + m = s1 + m = m + s2 + rr = t^2 + m_alt = -u2 + tt = u1 * m_alt + rr = rr + tt + degenerate = (branch & 3) == 3 + if (branch & 1) != 0: + zeroes.update({m : 'm_zero'}) + else: + nonzeroes.update({m : 'm_nonzero'}) + if (branch & 2) != 0: + zeroes.update({rr : 'rr_zero'}) + else: + nonzeroes.update({rr : 'rr_nonzero'}) + rr_alt = s1 + rr_alt = rr_alt * 2 + m_alt = m_alt + u1 + if not degenerate: + rr_alt = rr + m_alt = m + n = m_alt^2 + q = n * t + n = n^2 + if degenerate: + n = m + t = rr_alt^2 + rz = a.Z * m_alt + infinity = False + if (branch & 8) != 0: + if not a_infinity: + infinity = True + zeroes.update({rz : 'r.z=0'}) + else: + nonzeroes.update({rz : 'r.z!=0'}) + rz = rz * 2 + q = -q + t = t + q + rx = t + t = t * 2 + t = t + q + t = t * rr_alt + t = t + n + ry = -t + rx = rx * 4 + ry = ry * 4 + if a_infinity: + rx = b.X + ry = b.Y + rz = 1 + if infinity: + return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(zero=zeroes, nonzero=nonzeroes), point_at_infinity()) + return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(zero=zeroes, nonzero=nonzeroes), jacobianpoint(rx, ry, rz)) + +def formula_secp256k1_gej_add_ge_old(branch, a, b): + """libsecp256k1's old secp256k1_gej_add_ge, which fails when ay+by=0 but ax!=bx""" + a_infinity = (branch & 1) != 0 + zero = {} + nonzero = {} + if a_infinity: + nonzero.update({a.Infinity : 'a_infinite'}) + else: + zero.update({a.Infinity : 'a_finite'}) + zz = a.Z^2 + u1 = a.X + u2 = b.X * zz + s1 = a.Y + s2 = b.Y * zz + s2 = s2 * a.Z + z = a.Z + t = u1 + t = t + u2 + m = s1 + m = m + s2 + n = m^2 + q = n * t + n = n^2 + rr = t^2 + t = u1 * u2 + t = -t + rr = rr + t + t = rr^2 + rz = m * z + infinity = False + if (branch & 2) != 0: + if not a_infinity: + infinity = True + else: + return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(nonzero={z : 'conflict_a'}, zero={z : 'conflict_b'}), point_at_infinity()) + zero.update({rz : 'r.z=0'}) + else: + nonzero.update({rz : 'r.z!=0'}) + rz = rz * (0 if a_infinity else 2) + rx = t + q = -q + rx = rx + q + q = q * 3 + t = t * 2 + t = t + q + t = t * rr + t = t + n + ry = -t + rx = rx * (0 if a_infinity else 4) + ry = ry * (0 if a_infinity else 4) + t = b.X + t = t * (1 if a_infinity else 0) + rx = rx + t + t = b.Y + t = t * (1 if a_infinity else 0) + ry = ry + t + t = (1 if a_infinity else 0) + rz = rz + t + if infinity: + return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(zero=zero, nonzero=nonzero), point_at_infinity()) + return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(zero=zero, nonzero=nonzero), jacobianpoint(rx, ry, rz)) + +if __name__ == "__main__": + check_symbolic_jacobian_weierstrass("secp256k1_gej_add_var", 0, 7, 5, formula_secp256k1_gej_add_var) + check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge_var", 0, 7, 5, formula_secp256k1_gej_add_ge_var) + check_symbolic_jacobian_weierstrass("secp256k1_gej_add_zinv_var", 0, 7, 5, formula_secp256k1_gej_add_zinv_var) + check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge", 0, 7, 16, formula_secp256k1_gej_add_ge) + check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge_old [should fail]", 0, 7, 4, formula_secp256k1_gej_add_ge_old) + + if len(sys.argv) >= 2 and sys.argv[1] == "--exhaustive": + check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_var", 0, 7, 5, formula_secp256k1_gej_add_var, 43) + check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge_var", 0, 7, 5, formula_secp256k1_gej_add_ge_var, 43) + check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_zinv_var", 0, 7, 5, formula_secp256k1_gej_add_zinv_var, 43) + check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge", 0, 7, 16, formula_secp256k1_gej_add_ge, 43) + check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge_old [should fail]", 0, 7, 4, formula_secp256k1_gej_add_ge_old, 43) diff --git a/src/secp256k1/sage/weierstrass_prover.sage b/src/secp256k1/sage/weierstrass_prover.sage new file mode 100644 index 000000000000..03ef2ec901ea --- /dev/null +++ b/src/secp256k1/sage/weierstrass_prover.sage @@ -0,0 +1,264 @@ +# Prover implementation for Weierstrass curves of the form +# y^2 = x^3 + A * x + B, specifically with a = 0 and b = 7, with group laws +# operating on affine and Jacobian coordinates, including the point at infinity +# represented by a 4th variable in coordinates. + +load("group_prover.sage") + + +class affinepoint: + def __init__(self, x, y, infinity=0): + self.x = x + self.y = y + self.infinity = infinity + def __str__(self): + return "affinepoint(x=%s,y=%s,inf=%s)" % (self.x, self.y, self.infinity) + + +class jacobianpoint: + def __init__(self, x, y, z, infinity=0): + self.X = x + self.Y = y + self.Z = z + self.Infinity = infinity + def __str__(self): + return "jacobianpoint(X=%s,Y=%s,Z=%s,inf=%s)" % (self.X, self.Y, self.Z, self.Infinity) + + +def point_at_infinity(): + return jacobianpoint(1, 1, 1, 1) + + +def negate(p): + if p.__class__ == affinepoint: + return affinepoint(p.x, -p.y) + if p.__class__ == jacobianpoint: + return jacobianpoint(p.X, -p.Y, p.Z) + assert(False) + + +def on_weierstrass_curve(A, B, p): + """Return a set of zero-expressions for an affine point to be on the curve""" + return constraints(zero={p.x^3 + A*p.x + B - p.y^2: 'on_curve'}) + + +def tangential_to_weierstrass_curve(A, B, p12, p3): + """Return a set of zero-expressions for ((x12,y12),(x3,y3)) to be a line that is tangential to the curve at (x12,y12)""" + return constraints(zero={ + (p12.y - p3.y) * (p12.y * 2) - (p12.x^2 * 3 + A) * (p12.x - p3.x): 'tangential_to_curve' + }) + + +def colinear(p1, p2, p3): + """Return a set of zero-expressions for ((x1,y1),(x2,y2),(x3,y3)) to be collinear""" + return constraints(zero={ + (p1.y - p2.y) * (p1.x - p3.x) - (p1.y - p3.y) * (p1.x - p2.x): 'colinear_1', + (p2.y - p3.y) * (p2.x - p1.x) - (p2.y - p1.y) * (p2.x - p3.x): 'colinear_2', + (p3.y - p1.y) * (p3.x - p2.x) - (p3.y - p2.y) * (p3.x - p1.x): 'colinear_3' + }) + + +def good_affine_point(p): + return constraints(nonzero={p.x : 'nonzero_x', p.y : 'nonzero_y'}) + + +def good_jacobian_point(p): + return constraints(nonzero={p.X : 'nonzero_X', p.Y : 'nonzero_Y', p.Z^6 : 'nonzero_Z'}) + + +def good_point(p): + return constraints(nonzero={p.Z^6 : 'nonzero_X'}) + + +def finite(p, *affine_fns): + con = good_point(p) + constraints(zero={p.Infinity : 'finite_point'}) + if p.Z != 0: + return con + reduce(lambda a, b: a + b, (f(affinepoint(p.X / p.Z^2, p.Y / p.Z^3)) for f in affine_fns), con) + else: + return con + +def infinite(p): + return constraints(nonzero={p.Infinity : 'infinite_point'}) + + +def law_jacobian_weierstrass_add(A, B, pa, pb, pA, pB, pC): + """Check whether the passed set of coordinates is a valid Jacobian add, given assumptions""" + assumeLaw = (good_affine_point(pa) + + good_affine_point(pb) + + good_jacobian_point(pA) + + good_jacobian_point(pB) + + on_weierstrass_curve(A, B, pa) + + on_weierstrass_curve(A, B, pb) + + finite(pA) + + finite(pB) + + constraints(nonzero={pa.x - pb.x : 'different_x'})) + require = (finite(pC, lambda pc: on_weierstrass_curve(A, B, pc) + + colinear(pa, pb, negate(pc)))) + return (assumeLaw, require) + + +def law_jacobian_weierstrass_double(A, B, pa, pb, pA, pB, pC): + """Check whether the passed set of coordinates is a valid Jacobian doubling, given assumptions""" + assumeLaw = (good_affine_point(pa) + + good_affine_point(pb) + + good_jacobian_point(pA) + + good_jacobian_point(pB) + + on_weierstrass_curve(A, B, pa) + + on_weierstrass_curve(A, B, pb) + + finite(pA) + + finite(pB) + + constraints(zero={pa.x - pb.x : 'equal_x', pa.y - pb.y : 'equal_y'})) + require = (finite(pC, lambda pc: on_weierstrass_curve(A, B, pc) + + tangential_to_weierstrass_curve(A, B, pa, negate(pc)))) + return (assumeLaw, require) + + +def law_jacobian_weierstrass_add_opposites(A, B, pa, pb, pA, pB, pC): + assumeLaw = (good_affine_point(pa) + + good_affine_point(pb) + + good_jacobian_point(pA) + + good_jacobian_point(pB) + + on_weierstrass_curve(A, B, pa) + + on_weierstrass_curve(A, B, pb) + + finite(pA) + + finite(pB) + + constraints(zero={pa.x - pb.x : 'equal_x', pa.y + pb.y : 'opposite_y'})) + require = infinite(pC) + return (assumeLaw, require) + + +def law_jacobian_weierstrass_add_infinite_a(A, B, pa, pb, pA, pB, pC): + assumeLaw = (good_affine_point(pa) + + good_affine_point(pb) + + good_jacobian_point(pA) + + good_jacobian_point(pB) + + on_weierstrass_curve(A, B, pb) + + infinite(pA) + + finite(pB)) + require = finite(pC, lambda pc: constraints(zero={pc.x - pb.x : 'c.x=b.x', pc.y - pb.y : 'c.y=b.y'})) + return (assumeLaw, require) + + +def law_jacobian_weierstrass_add_infinite_b(A, B, pa, pb, pA, pB, pC): + assumeLaw = (good_affine_point(pa) + + good_affine_point(pb) + + good_jacobian_point(pA) + + good_jacobian_point(pB) + + on_weierstrass_curve(A, B, pa) + + infinite(pB) + + finite(pA)) + require = finite(pC, lambda pc: constraints(zero={pc.x - pa.x : 'c.x=a.x', pc.y - pa.y : 'c.y=a.y'})) + return (assumeLaw, require) + + +def law_jacobian_weierstrass_add_infinite_ab(A, B, pa, pb, pA, pB, pC): + assumeLaw = (good_affine_point(pa) + + good_affine_point(pb) + + good_jacobian_point(pA) + + good_jacobian_point(pB) + + infinite(pA) + + infinite(pB)) + require = infinite(pC) + return (assumeLaw, require) + + +laws_jacobian_weierstrass = { + 'add': law_jacobian_weierstrass_add, + 'double': law_jacobian_weierstrass_double, + 'add_opposite': law_jacobian_weierstrass_add_opposites, + 'add_infinite_a': law_jacobian_weierstrass_add_infinite_a, + 'add_infinite_b': law_jacobian_weierstrass_add_infinite_b, + 'add_infinite_ab': law_jacobian_weierstrass_add_infinite_ab +} + + +def check_exhaustive_jacobian_weierstrass(name, A, B, branches, formula, p): + """Verify an implementation of addition of Jacobian points on a Weierstrass curve, by executing and validating the result for every possible addition in a prime field""" + F = Integers(p) + print "Formula %s on Z%i:" % (name, p) + points = [] + for x in xrange(0, p): + for y in xrange(0, p): + point = affinepoint(F(x), F(y)) + r, e = concrete_verify(on_weierstrass_curve(A, B, point)) + if r: + points.append(point) + + for za in xrange(1, p): + for zb in xrange(1, p): + for pa in points: + for pb in points: + for ia in xrange(2): + for ib in xrange(2): + pA = jacobianpoint(pa.x * F(za)^2, pa.y * F(za)^3, F(za), ia) + pB = jacobianpoint(pb.x * F(zb)^2, pb.y * F(zb)^3, F(zb), ib) + for branch in xrange(0, branches): + assumeAssert, assumeBranch, pC = formula(branch, pA, pB) + pC.X = F(pC.X) + pC.Y = F(pC.Y) + pC.Z = F(pC.Z) + pC.Infinity = F(pC.Infinity) + r, e = concrete_verify(assumeAssert + assumeBranch) + if r: + match = False + for key in laws_jacobian_weierstrass: + assumeLaw, require = laws_jacobian_weierstrass[key](A, B, pa, pb, pA, pB, pC) + r, e = concrete_verify(assumeLaw) + if r: + if match: + print " multiple branches for (%s,%s,%s,%s) + (%s,%s,%s,%s)" % (pA.X, pA.Y, pA.Z, pA.Infinity, pB.X, pB.Y, pB.Z, pB.Infinity) + else: + match = True + r, e = concrete_verify(require) + if not r: + print " failure in branch %i for (%s,%s,%s,%s) + (%s,%s,%s,%s) = (%s,%s,%s,%s): %s" % (branch, pA.X, pA.Y, pA.Z, pA.Infinity, pB.X, pB.Y, pB.Z, pB.Infinity, pC.X, pC.Y, pC.Z, pC.Infinity, e) + print + + +def check_symbolic_function(R, assumeAssert, assumeBranch, f, A, B, pa, pb, pA, pB, pC): + assumeLaw, require = f(A, B, pa, pb, pA, pB, pC) + return check_symbolic(R, assumeLaw, assumeAssert, assumeBranch, require) + +def check_symbolic_jacobian_weierstrass(name, A, B, branches, formula): + """Verify an implementation of addition of Jacobian points on a Weierstrass curve symbolically""" + R. = PolynomialRing(QQ,8,order='invlex') + lift = lambda x: fastfrac(R,x) + ax = lift(ax) + ay = lift(ay) + Az = lift(Az) + bx = lift(bx) + by = lift(by) + Bz = lift(Bz) + Ai = lift(Ai) + Bi = lift(Bi) + + pa = affinepoint(ax, ay, Ai) + pb = affinepoint(bx, by, Bi) + pA = jacobianpoint(ax * Az^2, ay * Az^3, Az, Ai) + pB = jacobianpoint(bx * Bz^2, by * Bz^3, Bz, Bi) + + res = {} + + for key in laws_jacobian_weierstrass: + res[key] = [] + + print ("Formula " + name + ":") + count = 0 + for branch in xrange(branches): + assumeFormula, assumeBranch, pC = formula(branch, pA, pB) + pC.X = lift(pC.X) + pC.Y = lift(pC.Y) + pC.Z = lift(pC.Z) + pC.Infinity = lift(pC.Infinity) + + for key in laws_jacobian_weierstrass: + res[key].append((check_symbolic_function(R, assumeFormula, assumeBranch, laws_jacobian_weierstrass[key], A, B, pa, pb, pA, pB, pC), branch)) + + for key in res: + print " %s:" % key + val = res[key] + for x in val: + if x[0] is not None: + print " branch %i: %s" % (x[1], x[0]) + + print diff --git a/src/secp256k1/src/asm/field_10x26_arm.s b/src/secp256k1/src/asm/field_10x26_arm.s new file mode 100644 index 000000000000..5df561f2fc93 --- /dev/null +++ b/src/secp256k1/src/asm/field_10x26_arm.s @@ -0,0 +1,919 @@ +@ vim: set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab syntax=armasm: +/********************************************************************** + * Copyright (c) 2014 Wladimir J. van der Laan * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ +/* +ARM implementation of field_10x26 inner loops. + +Note: + +- To avoid unnecessary loads and make use of available registers, two + 'passes' have every time been interleaved, with the odd passes accumulating c' and d' + which will be added to c and d respectively in the the even passes + +*/ + + .syntax unified + .arch armv7-a + @ eabi attributes - see readelf -A + .eabi_attribute 8, 1 @ Tag_ARM_ISA_use = yes + .eabi_attribute 9, 0 @ Tag_Thumb_ISA_use = no + .eabi_attribute 10, 0 @ Tag_FP_arch = none + .eabi_attribute 24, 1 @ Tag_ABI_align_needed = 8-byte + .eabi_attribute 25, 1 @ Tag_ABI_align_preserved = 8-byte, except leaf SP + .eabi_attribute 30, 2 @ Tag_ABI_optimization_goals = Agressive Speed + .eabi_attribute 34, 1 @ Tag_CPU_unaligned_access = v6 + .text + + @ Field constants + .set field_R0, 0x3d10 + .set field_R1, 0x400 + .set field_not_M, 0xfc000000 @ ~M = ~0x3ffffff + + .align 2 + .global secp256k1_fe_mul_inner + .type secp256k1_fe_mul_inner, %function + @ Arguments: + @ r0 r Restrict: can overlap with a, not with b + @ r1 a + @ r2 b + @ Stack (total 4+10*4 = 44) + @ sp + #0 saved 'r' pointer + @ sp + #4 + 4*X t0,t1,t2,t3,t4,t5,t6,t7,u8,t9 +secp256k1_fe_mul_inner: + stmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, r14} + sub sp, sp, #48 @ frame=44 + alignment + str r0, [sp, #0] @ save result address, we need it only at the end + + /****************************************** + * Main computation code. + ****************************************** + + Allocation: + r0,r14,r7,r8 scratch + r1 a (pointer) + r2 b (pointer) + r3:r4 c + r5:r6 d + r11:r12 c' + r9:r10 d' + + Note: do not write to r[] here, it may overlap with a[] + */ + + /* A - interleaved with B */ + ldr r7, [r1, #0*4] @ a[0] + ldr r8, [r2, #9*4] @ b[9] + ldr r0, [r1, #1*4] @ a[1] + umull r5, r6, r7, r8 @ d = a[0] * b[9] + ldr r14, [r2, #8*4] @ b[8] + umull r9, r10, r0, r8 @ d' = a[1] * b[9] + ldr r7, [r1, #2*4] @ a[2] + umlal r5, r6, r0, r14 @ d += a[1] * b[8] + ldr r8, [r2, #7*4] @ b[7] + umlal r9, r10, r7, r14 @ d' += a[2] * b[8] + ldr r0, [r1, #3*4] @ a[3] + umlal r5, r6, r7, r8 @ d += a[2] * b[7] + ldr r14, [r2, #6*4] @ b[6] + umlal r9, r10, r0, r8 @ d' += a[3] * b[7] + ldr r7, [r1, #4*4] @ a[4] + umlal r5, r6, r0, r14 @ d += a[3] * b[6] + ldr r8, [r2, #5*4] @ b[5] + umlal r9, r10, r7, r14 @ d' += a[4] * b[6] + ldr r0, [r1, #5*4] @ a[5] + umlal r5, r6, r7, r8 @ d += a[4] * b[5] + ldr r14, [r2, #4*4] @ b[4] + umlal r9, r10, r0, r8 @ d' += a[5] * b[5] + ldr r7, [r1, #6*4] @ a[6] + umlal r5, r6, r0, r14 @ d += a[5] * b[4] + ldr r8, [r2, #3*4] @ b[3] + umlal r9, r10, r7, r14 @ d' += a[6] * b[4] + ldr r0, [r1, #7*4] @ a[7] + umlal r5, r6, r7, r8 @ d += a[6] * b[3] + ldr r14, [r2, #2*4] @ b[2] + umlal r9, r10, r0, r8 @ d' += a[7] * b[3] + ldr r7, [r1, #8*4] @ a[8] + umlal r5, r6, r0, r14 @ d += a[7] * b[2] + ldr r8, [r2, #1*4] @ b[1] + umlal r9, r10, r7, r14 @ d' += a[8] * b[2] + ldr r0, [r1, #9*4] @ a[9] + umlal r5, r6, r7, r8 @ d += a[8] * b[1] + ldr r14, [r2, #0*4] @ b[0] + umlal r9, r10, r0, r8 @ d' += a[9] * b[1] + ldr r7, [r1, #0*4] @ a[0] + umlal r5, r6, r0, r14 @ d += a[9] * b[0] + @ r7,r14 used in B + + bic r0, r5, field_not_M @ t9 = d & M + str r0, [sp, #4 + 4*9] + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + + /* B */ + umull r3, r4, r7, r14 @ c = a[0] * b[0] + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u0 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u0 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t0 = c & M + str r14, [sp, #4 + 0*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u0 * R1 + umlal r3, r4, r0, r14 + + /* C - interleaved with D */ + ldr r7, [r1, #0*4] @ a[0] + ldr r8, [r2, #2*4] @ b[2] + ldr r14, [r2, #1*4] @ b[1] + umull r11, r12, r7, r8 @ c' = a[0] * b[2] + ldr r0, [r1, #1*4] @ a[1] + umlal r3, r4, r7, r14 @ c += a[0] * b[1] + ldr r8, [r2, #0*4] @ b[0] + umlal r11, r12, r0, r14 @ c' += a[1] * b[1] + ldr r7, [r1, #2*4] @ a[2] + umlal r3, r4, r0, r8 @ c += a[1] * b[0] + ldr r14, [r2, #9*4] @ b[9] + umlal r11, r12, r7, r8 @ c' += a[2] * b[0] + ldr r0, [r1, #3*4] @ a[3] + umlal r5, r6, r7, r14 @ d += a[2] * b[9] + ldr r8, [r2, #8*4] @ b[8] + umull r9, r10, r0, r14 @ d' = a[3] * b[9] + ldr r7, [r1, #4*4] @ a[4] + umlal r5, r6, r0, r8 @ d += a[3] * b[8] + ldr r14, [r2, #7*4] @ b[7] + umlal r9, r10, r7, r8 @ d' += a[4] * b[8] + ldr r0, [r1, #5*4] @ a[5] + umlal r5, r6, r7, r14 @ d += a[4] * b[7] + ldr r8, [r2, #6*4] @ b[6] + umlal r9, r10, r0, r14 @ d' += a[5] * b[7] + ldr r7, [r1, #6*4] @ a[6] + umlal r5, r6, r0, r8 @ d += a[5] * b[6] + ldr r14, [r2, #5*4] @ b[5] + umlal r9, r10, r7, r8 @ d' += a[6] * b[6] + ldr r0, [r1, #7*4] @ a[7] + umlal r5, r6, r7, r14 @ d += a[6] * b[5] + ldr r8, [r2, #4*4] @ b[4] + umlal r9, r10, r0, r14 @ d' += a[7] * b[5] + ldr r7, [r1, #8*4] @ a[8] + umlal r5, r6, r0, r8 @ d += a[7] * b[4] + ldr r14, [r2, #3*4] @ b[3] + umlal r9, r10, r7, r8 @ d' += a[8] * b[4] + ldr r0, [r1, #9*4] @ a[9] + umlal r5, r6, r7, r14 @ d += a[8] * b[3] + ldr r8, [r2, #2*4] @ b[2] + umlal r9, r10, r0, r14 @ d' += a[9] * b[3] + umlal r5, r6, r0, r8 @ d += a[9] * b[2] + + bic r0, r5, field_not_M @ u1 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u1 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t1 = c & M + str r14, [sp, #4 + 1*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u1 * R1 + umlal r3, r4, r0, r14 + + /* D */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u2 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u2 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t2 = c & M + str r14, [sp, #4 + 2*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u2 * R1 + umlal r3, r4, r0, r14 + + /* E - interleaved with F */ + ldr r7, [r1, #0*4] @ a[0] + ldr r8, [r2, #4*4] @ b[4] + umull r11, r12, r7, r8 @ c' = a[0] * b[4] + ldr r8, [r2, #3*4] @ b[3] + umlal r3, r4, r7, r8 @ c += a[0] * b[3] + ldr r7, [r1, #1*4] @ a[1] + umlal r11, r12, r7, r8 @ c' += a[1] * b[3] + ldr r8, [r2, #2*4] @ b[2] + umlal r3, r4, r7, r8 @ c += a[1] * b[2] + ldr r7, [r1, #2*4] @ a[2] + umlal r11, r12, r7, r8 @ c' += a[2] * b[2] + ldr r8, [r2, #1*4] @ b[1] + umlal r3, r4, r7, r8 @ c += a[2] * b[1] + ldr r7, [r1, #3*4] @ a[3] + umlal r11, r12, r7, r8 @ c' += a[3] * b[1] + ldr r8, [r2, #0*4] @ b[0] + umlal r3, r4, r7, r8 @ c += a[3] * b[0] + ldr r7, [r1, #4*4] @ a[4] + umlal r11, r12, r7, r8 @ c' += a[4] * b[0] + ldr r8, [r2, #9*4] @ b[9] + umlal r5, r6, r7, r8 @ d += a[4] * b[9] + ldr r7, [r1, #5*4] @ a[5] + umull r9, r10, r7, r8 @ d' = a[5] * b[9] + ldr r8, [r2, #8*4] @ b[8] + umlal r5, r6, r7, r8 @ d += a[5] * b[8] + ldr r7, [r1, #6*4] @ a[6] + umlal r9, r10, r7, r8 @ d' += a[6] * b[8] + ldr r8, [r2, #7*4] @ b[7] + umlal r5, r6, r7, r8 @ d += a[6] * b[7] + ldr r7, [r1, #7*4] @ a[7] + umlal r9, r10, r7, r8 @ d' += a[7] * b[7] + ldr r8, [r2, #6*4] @ b[6] + umlal r5, r6, r7, r8 @ d += a[7] * b[6] + ldr r7, [r1, #8*4] @ a[8] + umlal r9, r10, r7, r8 @ d' += a[8] * b[6] + ldr r8, [r2, #5*4] @ b[5] + umlal r5, r6, r7, r8 @ d += a[8] * b[5] + ldr r7, [r1, #9*4] @ a[9] + umlal r9, r10, r7, r8 @ d' += a[9] * b[5] + ldr r8, [r2, #4*4] @ b[4] + umlal r5, r6, r7, r8 @ d += a[9] * b[4] + + bic r0, r5, field_not_M @ u3 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u3 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t3 = c & M + str r14, [sp, #4 + 3*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u3 * R1 + umlal r3, r4, r0, r14 + + /* F */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u4 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u4 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t4 = c & M + str r14, [sp, #4 + 4*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u4 * R1 + umlal r3, r4, r0, r14 + + /* G - interleaved with H */ + ldr r7, [r1, #0*4] @ a[0] + ldr r8, [r2, #6*4] @ b[6] + ldr r14, [r2, #5*4] @ b[5] + umull r11, r12, r7, r8 @ c' = a[0] * b[6] + ldr r0, [r1, #1*4] @ a[1] + umlal r3, r4, r7, r14 @ c += a[0] * b[5] + ldr r8, [r2, #4*4] @ b[4] + umlal r11, r12, r0, r14 @ c' += a[1] * b[5] + ldr r7, [r1, #2*4] @ a[2] + umlal r3, r4, r0, r8 @ c += a[1] * b[4] + ldr r14, [r2, #3*4] @ b[3] + umlal r11, r12, r7, r8 @ c' += a[2] * b[4] + ldr r0, [r1, #3*4] @ a[3] + umlal r3, r4, r7, r14 @ c += a[2] * b[3] + ldr r8, [r2, #2*4] @ b[2] + umlal r11, r12, r0, r14 @ c' += a[3] * b[3] + ldr r7, [r1, #4*4] @ a[4] + umlal r3, r4, r0, r8 @ c += a[3] * b[2] + ldr r14, [r2, #1*4] @ b[1] + umlal r11, r12, r7, r8 @ c' += a[4] * b[2] + ldr r0, [r1, #5*4] @ a[5] + umlal r3, r4, r7, r14 @ c += a[4] * b[1] + ldr r8, [r2, #0*4] @ b[0] + umlal r11, r12, r0, r14 @ c' += a[5] * b[1] + ldr r7, [r1, #6*4] @ a[6] + umlal r3, r4, r0, r8 @ c += a[5] * b[0] + ldr r14, [r2, #9*4] @ b[9] + umlal r11, r12, r7, r8 @ c' += a[6] * b[0] + ldr r0, [r1, #7*4] @ a[7] + umlal r5, r6, r7, r14 @ d += a[6] * b[9] + ldr r8, [r2, #8*4] @ b[8] + umull r9, r10, r0, r14 @ d' = a[7] * b[9] + ldr r7, [r1, #8*4] @ a[8] + umlal r5, r6, r0, r8 @ d += a[7] * b[8] + ldr r14, [r2, #7*4] @ b[7] + umlal r9, r10, r7, r8 @ d' += a[8] * b[8] + ldr r0, [r1, #9*4] @ a[9] + umlal r5, r6, r7, r14 @ d += a[8] * b[7] + ldr r8, [r2, #6*4] @ b[6] + umlal r9, r10, r0, r14 @ d' += a[9] * b[7] + umlal r5, r6, r0, r8 @ d += a[9] * b[6] + + bic r0, r5, field_not_M @ u5 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u5 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t5 = c & M + str r14, [sp, #4 + 5*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u5 * R1 + umlal r3, r4, r0, r14 + + /* H */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u6 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u6 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t6 = c & M + str r14, [sp, #4 + 6*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u6 * R1 + umlal r3, r4, r0, r14 + + /* I - interleaved with J */ + ldr r8, [r2, #8*4] @ b[8] + ldr r7, [r1, #0*4] @ a[0] + ldr r14, [r2, #7*4] @ b[7] + umull r11, r12, r7, r8 @ c' = a[0] * b[8] + ldr r0, [r1, #1*4] @ a[1] + umlal r3, r4, r7, r14 @ c += a[0] * b[7] + ldr r8, [r2, #6*4] @ b[6] + umlal r11, r12, r0, r14 @ c' += a[1] * b[7] + ldr r7, [r1, #2*4] @ a[2] + umlal r3, r4, r0, r8 @ c += a[1] * b[6] + ldr r14, [r2, #5*4] @ b[5] + umlal r11, r12, r7, r8 @ c' += a[2] * b[6] + ldr r0, [r1, #3*4] @ a[3] + umlal r3, r4, r7, r14 @ c += a[2] * b[5] + ldr r8, [r2, #4*4] @ b[4] + umlal r11, r12, r0, r14 @ c' += a[3] * b[5] + ldr r7, [r1, #4*4] @ a[4] + umlal r3, r4, r0, r8 @ c += a[3] * b[4] + ldr r14, [r2, #3*4] @ b[3] + umlal r11, r12, r7, r8 @ c' += a[4] * b[4] + ldr r0, [r1, #5*4] @ a[5] + umlal r3, r4, r7, r14 @ c += a[4] * b[3] + ldr r8, [r2, #2*4] @ b[2] + umlal r11, r12, r0, r14 @ c' += a[5] * b[3] + ldr r7, [r1, #6*4] @ a[6] + umlal r3, r4, r0, r8 @ c += a[5] * b[2] + ldr r14, [r2, #1*4] @ b[1] + umlal r11, r12, r7, r8 @ c' += a[6] * b[2] + ldr r0, [r1, #7*4] @ a[7] + umlal r3, r4, r7, r14 @ c += a[6] * b[1] + ldr r8, [r2, #0*4] @ b[0] + umlal r11, r12, r0, r14 @ c' += a[7] * b[1] + ldr r7, [r1, #8*4] @ a[8] + umlal r3, r4, r0, r8 @ c += a[7] * b[0] + ldr r14, [r2, #9*4] @ b[9] + umlal r11, r12, r7, r8 @ c' += a[8] * b[0] + ldr r0, [r1, #9*4] @ a[9] + umlal r5, r6, r7, r14 @ d += a[8] * b[9] + ldr r8, [r2, #8*4] @ b[8] + umull r9, r10, r0, r14 @ d' = a[9] * b[9] + umlal r5, r6, r0, r8 @ d += a[9] * b[8] + + bic r0, r5, field_not_M @ u7 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u7 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t7 = c & M + str r14, [sp, #4 + 7*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u7 * R1 + umlal r3, r4, r0, r14 + + /* J */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u8 = d & M + str r0, [sp, #4 + 8*4] + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u8 * R0 + umlal r3, r4, r0, r14 + + /****************************************** + * compute and write back result + ****************************************** + Allocation: + r0 r + r3:r4 c + r5:r6 d + r7 t0 + r8 t1 + r9 t2 + r11 u8 + r12 t9 + r1,r2,r10,r14 scratch + + Note: do not read from a[] after here, it may overlap with r[] + */ + ldr r0, [sp, #0] + add r1, sp, #4 + 3*4 @ r[3..7] = t3..7, r11=u8, r12=t9 + ldmia r1, {r2,r7,r8,r9,r10,r11,r12} + add r1, r0, #3*4 + stmia r1, {r2,r7,r8,r9,r10} + + bic r2, r3, field_not_M @ r[8] = c & M + str r2, [r0, #8*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u8 * R1 + umlal r3, r4, r11, r14 + movw r14, field_R0 @ c += d * R0 + umlal r3, r4, r5, r14 + adds r3, r3, r12 @ c += t9 + adc r4, r4, #0 + + add r1, sp, #4 + 0*4 @ r7,r8,r9 = t0,t1,t2 + ldmia r1, {r7,r8,r9} + + ubfx r2, r3, #0, #22 @ r[9] = c & (M >> 4) + str r2, [r0, #9*4] + mov r3, r3, lsr #22 @ c >>= 22 + orr r3, r3, r4, asl #10 + mov r4, r4, lsr #22 + movw r14, field_R1 << 4 @ c += d * (R1 << 4) + umlal r3, r4, r5, r14 + + movw r14, field_R0 >> 4 @ d = c * (R0 >> 4) + t0 (64x64 multiply+add) + umull r5, r6, r3, r14 @ d = c.lo * (R0 >> 4) + adds r5, r5, r7 @ d.lo += t0 + mla r6, r14, r4, r6 @ d.hi += c.hi * (R0 >> 4) + adc r6, r6, 0 @ d.hi += carry + + bic r2, r5, field_not_M @ r[0] = d & M + str r2, [r0, #0*4] + + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + + movw r14, field_R1 >> 4 @ d += c * (R1 >> 4) + t1 (64x64 multiply+add) + umull r1, r2, r3, r14 @ tmp = c.lo * (R1 >> 4) + adds r5, r5, r8 @ d.lo += t1 + adc r6, r6, #0 @ d.hi += carry + adds r5, r5, r1 @ d.lo += tmp.lo + mla r2, r14, r4, r2 @ tmp.hi += c.hi * (R1 >> 4) + adc r6, r6, r2 @ d.hi += carry + tmp.hi + + bic r2, r5, field_not_M @ r[1] = d & M + str r2, [r0, #1*4] + mov r5, r5, lsr #26 @ d >>= 26 (ignore hi) + orr r5, r5, r6, asl #6 + + add r5, r5, r9 @ d += t2 + str r5, [r0, #2*4] @ r[2] = d + + add sp, sp, #48 + ldmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, pc} + .size secp256k1_fe_mul_inner, .-secp256k1_fe_mul_inner + + .align 2 + .global secp256k1_fe_sqr_inner + .type secp256k1_fe_sqr_inner, %function + @ Arguments: + @ r0 r Can overlap with a + @ r1 a + @ Stack (total 4+10*4 = 44) + @ sp + #0 saved 'r' pointer + @ sp + #4 + 4*X t0,t1,t2,t3,t4,t5,t6,t7,u8,t9 +secp256k1_fe_sqr_inner: + stmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, r14} + sub sp, sp, #48 @ frame=44 + alignment + str r0, [sp, #0] @ save result address, we need it only at the end + /****************************************** + * Main computation code. + ****************************************** + + Allocation: + r0,r14,r2,r7,r8 scratch + r1 a (pointer) + r3:r4 c + r5:r6 d + r11:r12 c' + r9:r10 d' + + Note: do not write to r[] here, it may overlap with a[] + */ + /* A interleaved with B */ + ldr r0, [r1, #1*4] @ a[1]*2 + ldr r7, [r1, #0*4] @ a[0] + mov r0, r0, asl #1 + ldr r14, [r1, #9*4] @ a[9] + umull r3, r4, r7, r7 @ c = a[0] * a[0] + ldr r8, [r1, #8*4] @ a[8] + mov r7, r7, asl #1 + umull r5, r6, r7, r14 @ d = a[0]*2 * a[9] + ldr r7, [r1, #2*4] @ a[2]*2 + umull r9, r10, r0, r14 @ d' = a[1]*2 * a[9] + ldr r14, [r1, #7*4] @ a[7] + umlal r5, r6, r0, r8 @ d += a[1]*2 * a[8] + mov r7, r7, asl #1 + ldr r0, [r1, #3*4] @ a[3]*2 + umlal r9, r10, r7, r8 @ d' += a[2]*2 * a[8] + ldr r8, [r1, #6*4] @ a[6] + umlal r5, r6, r7, r14 @ d += a[2]*2 * a[7] + mov r0, r0, asl #1 + ldr r7, [r1, #4*4] @ a[4]*2 + umlal r9, r10, r0, r14 @ d' += a[3]*2 * a[7] + ldr r14, [r1, #5*4] @ a[5] + mov r7, r7, asl #1 + umlal r5, r6, r0, r8 @ d += a[3]*2 * a[6] + umlal r9, r10, r7, r8 @ d' += a[4]*2 * a[6] + umlal r5, r6, r7, r14 @ d += a[4]*2 * a[5] + umlal r9, r10, r14, r14 @ d' += a[5] * a[5] + + bic r0, r5, field_not_M @ t9 = d & M + str r0, [sp, #4 + 9*4] + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + + /* B */ + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u0 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u0 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t0 = c & M + str r14, [sp, #4 + 0*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u0 * R1 + umlal r3, r4, r0, r14 + + /* C interleaved with D */ + ldr r0, [r1, #0*4] @ a[0]*2 + ldr r14, [r1, #1*4] @ a[1] + mov r0, r0, asl #1 + ldr r8, [r1, #2*4] @ a[2] + umlal r3, r4, r0, r14 @ c += a[0]*2 * a[1] + mov r7, r8, asl #1 @ a[2]*2 + umull r11, r12, r14, r14 @ c' = a[1] * a[1] + ldr r14, [r1, #9*4] @ a[9] + umlal r11, r12, r0, r8 @ c' += a[0]*2 * a[2] + ldr r0, [r1, #3*4] @ a[3]*2 + ldr r8, [r1, #8*4] @ a[8] + umlal r5, r6, r7, r14 @ d += a[2]*2 * a[9] + mov r0, r0, asl #1 + ldr r7, [r1, #4*4] @ a[4]*2 + umull r9, r10, r0, r14 @ d' = a[3]*2 * a[9] + ldr r14, [r1, #7*4] @ a[7] + umlal r5, r6, r0, r8 @ d += a[3]*2 * a[8] + mov r7, r7, asl #1 + ldr r0, [r1, #5*4] @ a[5]*2 + umlal r9, r10, r7, r8 @ d' += a[4]*2 * a[8] + ldr r8, [r1, #6*4] @ a[6] + mov r0, r0, asl #1 + umlal r5, r6, r7, r14 @ d += a[4]*2 * a[7] + umlal r9, r10, r0, r14 @ d' += a[5]*2 * a[7] + umlal r5, r6, r0, r8 @ d += a[5]*2 * a[6] + umlal r9, r10, r8, r8 @ d' += a[6] * a[6] + + bic r0, r5, field_not_M @ u1 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u1 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t1 = c & M + str r14, [sp, #4 + 1*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u1 * R1 + umlal r3, r4, r0, r14 + + /* D */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u2 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u2 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t2 = c & M + str r14, [sp, #4 + 2*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u2 * R1 + umlal r3, r4, r0, r14 + + /* E interleaved with F */ + ldr r7, [r1, #0*4] @ a[0]*2 + ldr r0, [r1, #1*4] @ a[1]*2 + ldr r14, [r1, #2*4] @ a[2] + mov r7, r7, asl #1 + ldr r8, [r1, #3*4] @ a[3] + ldr r2, [r1, #4*4] + umlal r3, r4, r7, r8 @ c += a[0]*2 * a[3] + mov r0, r0, asl #1 + umull r11, r12, r7, r2 @ c' = a[0]*2 * a[4] + mov r2, r2, asl #1 @ a[4]*2 + umlal r11, r12, r0, r8 @ c' += a[1]*2 * a[3] + ldr r8, [r1, #9*4] @ a[9] + umlal r3, r4, r0, r14 @ c += a[1]*2 * a[2] + ldr r0, [r1, #5*4] @ a[5]*2 + umlal r11, r12, r14, r14 @ c' += a[2] * a[2] + ldr r14, [r1, #8*4] @ a[8] + mov r0, r0, asl #1 + umlal r5, r6, r2, r8 @ d += a[4]*2 * a[9] + ldr r7, [r1, #6*4] @ a[6]*2 + umull r9, r10, r0, r8 @ d' = a[5]*2 * a[9] + mov r7, r7, asl #1 + ldr r8, [r1, #7*4] @ a[7] + umlal r5, r6, r0, r14 @ d += a[5]*2 * a[8] + umlal r9, r10, r7, r14 @ d' += a[6]*2 * a[8] + umlal r5, r6, r7, r8 @ d += a[6]*2 * a[7] + umlal r9, r10, r8, r8 @ d' += a[7] * a[7] + + bic r0, r5, field_not_M @ u3 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u3 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t3 = c & M + str r14, [sp, #4 + 3*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u3 * R1 + umlal r3, r4, r0, r14 + + /* F */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u4 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u4 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t4 = c & M + str r14, [sp, #4 + 4*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u4 * R1 + umlal r3, r4, r0, r14 + + /* G interleaved with H */ + ldr r7, [r1, #0*4] @ a[0]*2 + ldr r0, [r1, #1*4] @ a[1]*2 + mov r7, r7, asl #1 + ldr r8, [r1, #5*4] @ a[5] + ldr r2, [r1, #6*4] @ a[6] + umlal r3, r4, r7, r8 @ c += a[0]*2 * a[5] + ldr r14, [r1, #4*4] @ a[4] + mov r0, r0, asl #1 + umull r11, r12, r7, r2 @ c' = a[0]*2 * a[6] + ldr r7, [r1, #2*4] @ a[2]*2 + umlal r11, r12, r0, r8 @ c' += a[1]*2 * a[5] + mov r7, r7, asl #1 + ldr r8, [r1, #3*4] @ a[3] + umlal r3, r4, r0, r14 @ c += a[1]*2 * a[4] + mov r0, r2, asl #1 @ a[6]*2 + umlal r11, r12, r7, r14 @ c' += a[2]*2 * a[4] + ldr r14, [r1, #9*4] @ a[9] + umlal r3, r4, r7, r8 @ c += a[2]*2 * a[3] + ldr r7, [r1, #7*4] @ a[7]*2 + umlal r11, r12, r8, r8 @ c' += a[3] * a[3] + mov r7, r7, asl #1 + ldr r8, [r1, #8*4] @ a[8] + umlal r5, r6, r0, r14 @ d += a[6]*2 * a[9] + umull r9, r10, r7, r14 @ d' = a[7]*2 * a[9] + umlal r5, r6, r7, r8 @ d += a[7]*2 * a[8] + umlal r9, r10, r8, r8 @ d' += a[8] * a[8] + + bic r0, r5, field_not_M @ u5 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u5 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t5 = c & M + str r14, [sp, #4 + 5*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u5 * R1 + umlal r3, r4, r0, r14 + + /* H */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u6 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u6 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t6 = c & M + str r14, [sp, #4 + 6*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u6 * R1 + umlal r3, r4, r0, r14 + + /* I interleaved with J */ + ldr r7, [r1, #0*4] @ a[0]*2 + ldr r0, [r1, #1*4] @ a[1]*2 + mov r7, r7, asl #1 + ldr r8, [r1, #7*4] @ a[7] + ldr r2, [r1, #8*4] @ a[8] + umlal r3, r4, r7, r8 @ c += a[0]*2 * a[7] + ldr r14, [r1, #6*4] @ a[6] + mov r0, r0, asl #1 + umull r11, r12, r7, r2 @ c' = a[0]*2 * a[8] + ldr r7, [r1, #2*4] @ a[2]*2 + umlal r11, r12, r0, r8 @ c' += a[1]*2 * a[7] + ldr r8, [r1, #5*4] @ a[5] + umlal r3, r4, r0, r14 @ c += a[1]*2 * a[6] + ldr r0, [r1, #3*4] @ a[3]*2 + mov r7, r7, asl #1 + umlal r11, r12, r7, r14 @ c' += a[2]*2 * a[6] + ldr r14, [r1, #4*4] @ a[4] + mov r0, r0, asl #1 + umlal r3, r4, r7, r8 @ c += a[2]*2 * a[5] + mov r2, r2, asl #1 @ a[8]*2 + umlal r11, r12, r0, r8 @ c' += a[3]*2 * a[5] + umlal r3, r4, r0, r14 @ c += a[3]*2 * a[4] + umlal r11, r12, r14, r14 @ c' += a[4] * a[4] + ldr r8, [r1, #9*4] @ a[9] + umlal r5, r6, r2, r8 @ d += a[8]*2 * a[9] + @ r8 will be used in J + + bic r0, r5, field_not_M @ u7 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u7 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t7 = c & M + str r14, [sp, #4 + 7*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u7 * R1 + umlal r3, r4, r0, r14 + + /* J */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + umlal r5, r6, r8, r8 @ d += a[9] * a[9] + + bic r0, r5, field_not_M @ u8 = d & M + str r0, [sp, #4 + 8*4] + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u8 * R0 + umlal r3, r4, r0, r14 + + /****************************************** + * compute and write back result + ****************************************** + Allocation: + r0 r + r3:r4 c + r5:r6 d + r7 t0 + r8 t1 + r9 t2 + r11 u8 + r12 t9 + r1,r2,r10,r14 scratch + + Note: do not read from a[] after here, it may overlap with r[] + */ + ldr r0, [sp, #0] + add r1, sp, #4 + 3*4 @ r[3..7] = t3..7, r11=u8, r12=t9 + ldmia r1, {r2,r7,r8,r9,r10,r11,r12} + add r1, r0, #3*4 + stmia r1, {r2,r7,r8,r9,r10} + + bic r2, r3, field_not_M @ r[8] = c & M + str r2, [r0, #8*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u8 * R1 + umlal r3, r4, r11, r14 + movw r14, field_R0 @ c += d * R0 + umlal r3, r4, r5, r14 + adds r3, r3, r12 @ c += t9 + adc r4, r4, #0 + + add r1, sp, #4 + 0*4 @ r7,r8,r9 = t0,t1,t2 + ldmia r1, {r7,r8,r9} + + ubfx r2, r3, #0, #22 @ r[9] = c & (M >> 4) + str r2, [r0, #9*4] + mov r3, r3, lsr #22 @ c >>= 22 + orr r3, r3, r4, asl #10 + mov r4, r4, lsr #22 + movw r14, field_R1 << 4 @ c += d * (R1 << 4) + umlal r3, r4, r5, r14 + + movw r14, field_R0 >> 4 @ d = c * (R0 >> 4) + t0 (64x64 multiply+add) + umull r5, r6, r3, r14 @ d = c.lo * (R0 >> 4) + adds r5, r5, r7 @ d.lo += t0 + mla r6, r14, r4, r6 @ d.hi += c.hi * (R0 >> 4) + adc r6, r6, 0 @ d.hi += carry + + bic r2, r5, field_not_M @ r[0] = d & M + str r2, [r0, #0*4] + + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + + movw r14, field_R1 >> 4 @ d += c * (R1 >> 4) + t1 (64x64 multiply+add) + umull r1, r2, r3, r14 @ tmp = c.lo * (R1 >> 4) + adds r5, r5, r8 @ d.lo += t1 + adc r6, r6, #0 @ d.hi += carry + adds r5, r5, r1 @ d.lo += tmp.lo + mla r2, r14, r4, r2 @ tmp.hi += c.hi * (R1 >> 4) + adc r6, r6, r2 @ d.hi += carry + tmp.hi + + bic r2, r5, field_not_M @ r[1] = d & M + str r2, [r0, #1*4] + mov r5, r5, lsr #26 @ d >>= 26 (ignore hi) + orr r5, r5, r6, asl #6 + + add r5, r5, r9 @ d += t2 + str r5, [r0, #2*4] @ r[2] = d + + add sp, sp, #48 + ldmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, pc} + .size secp256k1_fe_sqr_inner, .-secp256k1_fe_sqr_inner + diff --git a/src/secp256k1/src/bench_ecdh.c b/src/secp256k1/src/bench_ecdh.c index 5a7c6376e08a..cde5e2dbb4e4 100644 --- a/src/secp256k1/src/bench_ecdh.c +++ b/src/secp256k1/src/bench_ecdh.c @@ -28,7 +28,8 @@ static void bench_ecdh_setup(void* arg) { 0xa2, 0xba, 0xd1, 0x84, 0xf8, 0x83, 0xc6, 0x9f }; - data->ctx = secp256k1_context_create(0); + /* create a context with no capabilities */ + data->ctx = secp256k1_context_create(SECP256K1_FLAGS_TYPE_CONTEXT); for (i = 0; i < 32; i++) { data->scalar[i] = i + 1; } diff --git a/src/secp256k1/src/bench_internal.c b/src/secp256k1/src/bench_internal.c index 7809f5f8cfeb..0809f77bda10 100644 --- a/src/secp256k1/src/bench_internal.c +++ b/src/secp256k1/src/bench_internal.c @@ -181,12 +181,12 @@ void bench_field_inverse_var(void* arg) { } } -void bench_field_sqrt_var(void* arg) { +void bench_field_sqrt(void* arg) { int i; bench_inv_t *data = (bench_inv_t*)arg; for (i = 0; i < 20000; i++) { - secp256k1_fe_sqrt_var(&data->fe_x, &data->fe_x); + secp256k1_fe_sqrt(&data->fe_x, &data->fe_x); secp256k1_fe_add(&data->fe_x, &data->fe_y); } } @@ -227,6 +227,15 @@ void bench_group_add_affine_var(void* arg) { } } +void bench_group_jacobi_var(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 20000; i++) { + secp256k1_gej_has_quad_y_var(&data->gej_x); + } +} + void bench_ecmult_wnaf(void* arg) { int i; bench_inv_t *data = (bench_inv_t*)arg; @@ -299,6 +308,21 @@ void bench_context_sign(void* arg) { } } +#ifndef USE_NUM_NONE +void bench_num_jacobi(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + secp256k1_num nx, norder; + + secp256k1_scalar_get_num(&nx, &data->scalar_x); + secp256k1_scalar_order_get_num(&norder); + secp256k1_scalar_get_num(&norder, &data->scalar_y); + + for (i = 0; i < 200000; i++) { + secp256k1_num_jacobi(&nx, &norder); + } +} +#endif int have_flag(int argc, char** argv, char *flag) { char** argm = argv + argc; @@ -333,12 +357,13 @@ int main(int argc, char **argv) { if (have_flag(argc, argv, "field") || have_flag(argc, argv, "mul")) run_benchmark("field_mul", bench_field_mul, bench_setup, NULL, &data, 10, 200000); if (have_flag(argc, argv, "field") || have_flag(argc, argv, "inverse")) run_benchmark("field_inverse", bench_field_inverse, bench_setup, NULL, &data, 10, 20000); if (have_flag(argc, argv, "field") || have_flag(argc, argv, "inverse")) run_benchmark("field_inverse_var", bench_field_inverse_var, bench_setup, NULL, &data, 10, 20000); - if (have_flag(argc, argv, "field") || have_flag(argc, argv, "sqrt")) run_benchmark("field_sqrt_var", bench_field_sqrt_var, bench_setup, NULL, &data, 10, 20000); + if (have_flag(argc, argv, "field") || have_flag(argc, argv, "sqrt")) run_benchmark("field_sqrt", bench_field_sqrt, bench_setup, NULL, &data, 10, 20000); if (have_flag(argc, argv, "group") || have_flag(argc, argv, "double")) run_benchmark("group_double_var", bench_group_double_var, bench_setup, NULL, &data, 10, 200000); if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_var", bench_group_add_var, bench_setup, NULL, &data, 10, 200000); if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_affine", bench_group_add_affine, bench_setup, NULL, &data, 10, 200000); if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_affine_var", bench_group_add_affine_var, bench_setup, NULL, &data, 10, 200000); + if (have_flag(argc, argv, "group") || have_flag(argc, argv, "jacobi")) run_benchmark("group_jacobi_var", bench_group_jacobi_var, bench_setup, NULL, &data, 10, 20000); if (have_flag(argc, argv, "ecmult") || have_flag(argc, argv, "wnaf")) run_benchmark("wnaf_const", bench_wnaf_const, bench_setup, NULL, &data, 10, 20000); if (have_flag(argc, argv, "ecmult") || have_flag(argc, argv, "wnaf")) run_benchmark("ecmult_wnaf", bench_ecmult_wnaf, bench_setup, NULL, &data, 10, 20000); @@ -350,5 +375,8 @@ int main(int argc, char **argv) { if (have_flag(argc, argv, "context") || have_flag(argc, argv, "verify")) run_benchmark("context_verify", bench_context_verify, bench_setup, NULL, &data, 10, 20); if (have_flag(argc, argv, "context") || have_flag(argc, argv, "sign")) run_benchmark("context_sign", bench_context_sign, bench_setup, NULL, &data, 10, 200); +#ifndef USE_NUM_NONE + if (have_flag(argc, argv, "num") || have_flag(argc, argv, "jacobi")) run_benchmark("num_jacobi", bench_num_jacobi, bench_setup, NULL, &data, 10, 200000); +#endif return 0; } diff --git a/src/secp256k1/src/bench_verify.c b/src/secp256k1/src/bench_verify.c index 5718320cda29..418defa0aa22 100644 --- a/src/secp256k1/src/bench_verify.c +++ b/src/secp256k1/src/bench_verify.c @@ -11,6 +11,12 @@ #include "util.h" #include "bench.h" +#ifdef ENABLE_OPENSSL_TESTS +#include +#include +#include +#endif + typedef struct { secp256k1_context *ctx; unsigned char msg[32]; @@ -19,6 +25,9 @@ typedef struct { size_t siglen; unsigned char pubkey[33]; size_t pubkeylen; +#ifdef ENABLE_OPENSSL_TESTS + EC_GROUP* ec_group; +#endif } benchmark_verify_t; static void benchmark_verify(void* arg) { @@ -40,6 +49,36 @@ static void benchmark_verify(void* arg) { } } +#ifdef ENABLE_OPENSSL_TESTS +static void benchmark_verify_openssl(void* arg) { + int i; + benchmark_verify_t* data = (benchmark_verify_t*)arg; + + for (i = 0; i < 20000; i++) { + data->sig[data->siglen - 1] ^= (i & 0xFF); + data->sig[data->siglen - 2] ^= ((i >> 8) & 0xFF); + data->sig[data->siglen - 3] ^= ((i >> 16) & 0xFF); + { + EC_KEY *pkey = EC_KEY_new(); + const unsigned char *pubkey = &data->pubkey[0]; + int result; + + CHECK(pkey != NULL); + result = EC_KEY_set_group(pkey, data->ec_group); + CHECK(result); + result = (o2i_ECPublicKey(&pkey, &pubkey, data->pubkeylen)) != NULL; + CHECK(result); + result = ECDSA_verify(0, &data->msg[0], sizeof(data->msg), &data->sig[0], data->siglen, pkey) == (i == 0); + CHECK(result); + EC_KEY_free(pkey); + } + data->sig[data->siglen - 1] ^= (i & 0xFF); + data->sig[data->siglen - 2] ^= ((i >> 8) & 0xFF); + data->sig[data->siglen - 3] ^= ((i >> 16) & 0xFF); + } +} +#endif + int main(void) { int i; secp256k1_pubkey pubkey; @@ -62,6 +101,11 @@ int main(void) { CHECK(secp256k1_ec_pubkey_serialize(data.ctx, data.pubkey, &data.pubkeylen, &pubkey, SECP256K1_EC_COMPRESSED) == 1); run_benchmark("ecdsa_verify", benchmark_verify, NULL, NULL, &data, 10, 20000); +#ifdef ENABLE_OPENSSL_TESTS + data.ec_group = EC_GROUP_new_by_curve_name(NID_secp256k1); + run_benchmark("ecdsa_verify_openssl", benchmark_verify_openssl, NULL, NULL, &data, 10, 20000); + EC_GROUP_free(data.ec_group); +#endif secp256k1_context_destroy(data.ctx); return 0; diff --git a/src/secp256k1/src/ecmult_const_impl.h b/src/secp256k1/src/ecmult_const_impl.h index 90ac94770ea1..7a6a25318c4c 100644 --- a/src/secp256k1/src/ecmult_const_impl.h +++ b/src/secp256k1/src/ecmult_const_impl.h @@ -58,22 +58,24 @@ static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w) { int global_sign; int skew = 0; int word = 0; + /* 1 2 3 */ int u_last; int u; -#ifdef USE_ENDOMORPHISM int flip; int bit; secp256k1_scalar neg_s; int not_neg_one; - /* If we are using the endomorphism, we cannot handle even numbers by negating - * them, since we are working with 128-bit numbers whose negations would be 256 - * bits, eliminating the performance advantage. Instead we use a technique from + /* Note that we cannot handle even numbers by negating them to be odd, as is + * done in other implementations, since if our scalars were specified to have + * width < 256 for performance reasons, their negations would have width 256 + * and we'd lose any performance benefit. Instead, we use a technique from * Section 4.2 of the Okeya/Tagaki paper, which is to add either 1 (for even) - * or 2 (for odd) to the number we are encoding, then compensating after the - * multiplication. */ - /* Negative 128-bit numbers will be negated, since otherwise they are 256-bit */ + * or 2 (for odd) to the number we are encoding, returning a skew value indicating + * this, and having the caller compensate after doing the multiplication. */ + + /* Negative numbers will be negated to keep their bit representation below the maximum width */ flip = secp256k1_scalar_is_high(&s); /* We add 1 to even numbers, 2 to odd ones, noting that negation flips parity */ bit = flip ^ (s.d[0] & 1); @@ -89,11 +91,6 @@ static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w) { global_sign = secp256k1_scalar_cond_negate(&s, flip); global_sign *= not_neg_one * 2 - 1; skew = 1 << bit; -#else - /* Otherwise, we just negate to force oddness */ - int is_even = secp256k1_scalar_is_even(&s); - global_sign = secp256k1_scalar_cond_negate(&s, is_even); -#endif /* 4 */ u_last = secp256k1_scalar_shr_int(&s, w); @@ -127,15 +124,13 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons secp256k1_ge tmpa; secp256k1_fe Z; + int skew_1; + int wnaf_1[1 + WNAF_SIZE(WINDOW_A - 1)]; #ifdef USE_ENDOMORPHISM secp256k1_ge pre_a_lam[ECMULT_TABLE_SIZE(WINDOW_A)]; - int wnaf_1[1 + WNAF_SIZE(WINDOW_A - 1)]; int wnaf_lam[1 + WNAF_SIZE(WINDOW_A - 1)]; - int skew_1; int skew_lam; secp256k1_scalar q_1, q_lam; -#else - int wnaf[1 + WNAF_SIZE(WINDOW_A - 1)]; #endif int i; @@ -145,18 +140,10 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons #ifdef USE_ENDOMORPHISM /* split q into q_1 and q_lam (where q = q_1 + q_lam*lambda, and q_1 and q_lam are ~128 bit) */ secp256k1_scalar_split_lambda(&q_1, &q_lam, &sc); - /* no need for zero correction when using endomorphism since even - * numbers have one added to them anyway */ skew_1 = secp256k1_wnaf_const(wnaf_1, q_1, WINDOW_A - 1); skew_lam = secp256k1_wnaf_const(wnaf_lam, q_lam, WINDOW_A - 1); #else - int is_zero = secp256k1_scalar_is_zero(scalar); - /* the wNAF ladder cannot handle zero, so bump this to one .. we will - * correct the result after the fact */ - sc.d[0] += is_zero; - VERIFY_CHECK(!secp256k1_scalar_is_zero(&sc)); - - secp256k1_wnaf_const(wnaf, sc, WINDOW_A - 1); + skew_1 = secp256k1_wnaf_const(wnaf_1, sc, WINDOW_A - 1); #endif /* Calculate odd multiples of a. @@ -179,21 +166,15 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons /* first loop iteration (separated out so we can directly set r, rather * than having it start at infinity, get doubled several times, then have * its new value added to it) */ -#ifdef USE_ENDOMORPHISM i = wnaf_1[WNAF_SIZE(WINDOW_A - 1)]; VERIFY_CHECK(i != 0); ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a, i, WINDOW_A); secp256k1_gej_set_ge(r, &tmpa); - +#ifdef USE_ENDOMORPHISM i = wnaf_lam[WNAF_SIZE(WINDOW_A - 1)]; VERIFY_CHECK(i != 0); ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a_lam, i, WINDOW_A); secp256k1_gej_add_ge(r, r, &tmpa); -#else - i = wnaf[WNAF_SIZE(WINDOW_A - 1)]; - VERIFY_CHECK(i != 0); - ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a, i, WINDOW_A); - secp256k1_gej_set_ge(r, &tmpa); #endif /* remaining loop iterations */ for (i = WNAF_SIZE(WINDOW_A - 1) - 1; i >= 0; i--) { @@ -202,59 +183,57 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons for (j = 0; j < WINDOW_A - 1; ++j) { secp256k1_gej_double_nonzero(r, r, NULL); } -#ifdef USE_ENDOMORPHISM + n = wnaf_1[i]; ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a, n, WINDOW_A); VERIFY_CHECK(n != 0); secp256k1_gej_add_ge(r, r, &tmpa); - +#ifdef USE_ENDOMORPHISM n = wnaf_lam[i]; ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a_lam, n, WINDOW_A); VERIFY_CHECK(n != 0); secp256k1_gej_add_ge(r, r, &tmpa); -#else - n = wnaf[i]; - VERIFY_CHECK(n != 0); - ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a, n, WINDOW_A); - secp256k1_gej_add_ge(r, r, &tmpa); #endif } secp256k1_fe_mul(&r->z, &r->z, &Z); -#ifdef USE_ENDOMORPHISM { /* Correct for wNAF skew */ secp256k1_ge correction = *a; secp256k1_ge_storage correction_1_stor; +#ifdef USE_ENDOMORPHISM secp256k1_ge_storage correction_lam_stor; +#endif secp256k1_ge_storage a2_stor; secp256k1_gej tmpj; secp256k1_gej_set_ge(&tmpj, &correction); secp256k1_gej_double_var(&tmpj, &tmpj, NULL); secp256k1_ge_set_gej(&correction, &tmpj); secp256k1_ge_to_storage(&correction_1_stor, a); +#ifdef USE_ENDOMORPHISM secp256k1_ge_to_storage(&correction_lam_stor, a); +#endif secp256k1_ge_to_storage(&a2_stor, &correction); /* For odd numbers this is 2a (so replace it), for even ones a (so no-op) */ secp256k1_ge_storage_cmov(&correction_1_stor, &a2_stor, skew_1 == 2); +#ifdef USE_ENDOMORPHISM secp256k1_ge_storage_cmov(&correction_lam_stor, &a2_stor, skew_lam == 2); +#endif /* Apply the correction */ secp256k1_ge_from_storage(&correction, &correction_1_stor); secp256k1_ge_neg(&correction, &correction); secp256k1_gej_add_ge(r, r, &correction); +#ifdef USE_ENDOMORPHISM secp256k1_ge_from_storage(&correction, &correction_lam_stor); secp256k1_ge_neg(&correction, &correction); secp256k1_ge_mul_lambda(&correction, &correction); secp256k1_gej_add_ge(r, r, &correction); - } -#else - /* correct for zero */ - r->infinity |= is_zero; #endif + } } #endif diff --git a/src/secp256k1/src/ecmult_impl.h b/src/secp256k1/src/ecmult_impl.h index e6e5f4718807..81ae08e100b6 100644 --- a/src/secp256k1/src/ecmult_impl.h +++ b/src/secp256k1/src/ecmult_impl.h @@ -11,6 +11,8 @@ #include "scalar.h" #include "ecmult.h" +#include + /* optimal for 128-bit and 256-bit exponents. */ #define WINDOW_A 5 diff --git a/src/secp256k1/src/field.h b/src/secp256k1/src/field.h index 2d52af5e366c..c5ba074244e6 100644 --- a/src/secp256k1/src/field.h +++ b/src/secp256k1/src/field.h @@ -57,6 +57,9 @@ static int secp256k1_fe_is_zero(const secp256k1_fe *a); static int secp256k1_fe_is_odd(const secp256k1_fe *a); /** Compare two field elements. Requires magnitude-1 inputs. */ +static int secp256k1_fe_equal(const secp256k1_fe *a, const secp256k1_fe *b); + +/** Same as secp256k1_fe_equal, but may be variable time. */ static int secp256k1_fe_equal_var(const secp256k1_fe *a, const secp256k1_fe *b); /** Compare two field elements. Requires both inputs to be normalized */ @@ -92,7 +95,10 @@ static void secp256k1_fe_sqr(secp256k1_fe *r, const secp256k1_fe *a); * The input's magnitude can be at most 8. The output magnitude is 1 (but not * guaranteed to be normalized). The result in r will always be a square * itself. */ -static int secp256k1_fe_sqrt_var(secp256k1_fe *r, const secp256k1_fe *a); +static int secp256k1_fe_sqrt(secp256k1_fe *r, const secp256k1_fe *a); + +/** Checks whether a field element is a quadratic residue. */ +static int secp256k1_fe_is_quad_var(const secp256k1_fe *a); /** Sets a field element to be the (modular) inverse of another. Requires the input's magnitude to be * at most 8. The output magnitude is 1 (but not guaranteed to be normalized). */ diff --git a/src/secp256k1/src/field_10x26_impl.h b/src/secp256k1/src/field_10x26_impl.h index 212cc5396af8..7b8c07960847 100644 --- a/src/secp256k1/src/field_10x26_impl.h +++ b/src/secp256k1/src/field_10x26_impl.h @@ -7,8 +7,6 @@ #ifndef _SECP256K1_FIELD_REPR_IMPL_H_ #define _SECP256K1_FIELD_REPR_IMPL_H_ -#include -#include #include "util.h" #include "num.h" #include "field.h" @@ -429,6 +427,14 @@ SECP256K1_INLINE static void secp256k1_fe_add(secp256k1_fe *r, const secp256k1_f #endif } +#if defined(USE_EXTERNAL_ASM) + +/* External assembler implementation */ +void secp256k1_fe_mul_inner(uint32_t *r, const uint32_t *a, const uint32_t * SECP256K1_RESTRICT b); +void secp256k1_fe_sqr_inner(uint32_t *r, const uint32_t *a); + +#else + #ifdef VERIFY #define VERIFY_BITS(x, n) VERIFY_CHECK(((x) >> (n)) == 0) #else @@ -1037,7 +1043,7 @@ SECP256K1_INLINE static void secp256k1_fe_sqr_inner(uint32_t *r, const uint32_t VERIFY_BITS(r[2], 27); /* [r9 r8 r7 r6 r5 r4 r3 r2 r1 r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ } - +#endif static void secp256k1_fe_mul(secp256k1_fe *r, const secp256k1_fe *a, const secp256k1_fe * SECP256K1_RESTRICT b) { #ifdef VERIFY diff --git a/src/secp256k1/src/field_5x52_impl.h b/src/secp256k1/src/field_5x52_impl.h index b31e24ab811b..7a99eb21eccc 100644 --- a/src/secp256k1/src/field_5x52_impl.h +++ b/src/secp256k1/src/field_5x52_impl.h @@ -11,7 +11,6 @@ #include "libsecp256k1-config.h" #endif -#include #include "util.h" #include "num.h" #include "field.h" diff --git a/src/secp256k1/src/field_5x52_int128_impl.h b/src/secp256k1/src/field_5x52_int128_impl.h index 9280bb5ea221..0bf22bdd3ec8 100644 --- a/src/secp256k1/src/field_5x52_int128_impl.h +++ b/src/secp256k1/src/field_5x52_int128_impl.h @@ -137,7 +137,7 @@ SECP256K1_INLINE static void secp256k1_fe_mul_inner(uint64_t *r, const uint64_t VERIFY_BITS(r[2], 52); VERIFY_BITS(c, 63); /* [d 0 0 0 t4 t3+c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - c += d * R + t3;; + c += d * R + t3; VERIFY_BITS(c, 100); /* [t4 c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ r[3] = c & M; c >>= 52; @@ -259,7 +259,7 @@ SECP256K1_INLINE static void secp256k1_fe_sqr_inner(uint64_t *r, const uint64_t VERIFY_BITS(c, 63); /* [d 0 0 0 t4 t3+c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - c += d * R + t3;; + c += d * R + t3; VERIFY_BITS(c, 100); /* [t4 c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ r[3] = c & M; c >>= 52; diff --git a/src/secp256k1/src/field_impl.h b/src/secp256k1/src/field_impl.h index 77f4aae2f972..52cd902eb387 100644 --- a/src/secp256k1/src/field_impl.h +++ b/src/secp256k1/src/field_impl.h @@ -21,6 +21,13 @@ #error "Please select field implementation" #endif +SECP256K1_INLINE static int secp256k1_fe_equal(const secp256k1_fe *a, const secp256k1_fe *b) { + secp256k1_fe na; + secp256k1_fe_negate(&na, a, 1); + secp256k1_fe_add(&na, b); + return secp256k1_fe_normalizes_to_zero(&na); +} + SECP256K1_INLINE static int secp256k1_fe_equal_var(const secp256k1_fe *a, const secp256k1_fe *b) { secp256k1_fe na; secp256k1_fe_negate(&na, a, 1); @@ -28,7 +35,7 @@ SECP256K1_INLINE static int secp256k1_fe_equal_var(const secp256k1_fe *a, const return secp256k1_fe_normalizes_to_zero_var(&na); } -static int secp256k1_fe_sqrt_var(secp256k1_fe *r, const secp256k1_fe *a) { +static int secp256k1_fe_sqrt(secp256k1_fe *r, const secp256k1_fe *a) { /** Given that p is congruent to 3 mod 4, we can compute the square root of * a mod p as the (p+1)/4'th power of a. * @@ -123,7 +130,7 @@ static int secp256k1_fe_sqrt_var(secp256k1_fe *r, const secp256k1_fe *a) { /* Check that a square root was actually calculated */ secp256k1_fe_sqr(&t1, r); - return secp256k1_fe_equal_var(&t1, a); + return secp256k1_fe_equal(&t1, a); } static void secp256k1_fe_inv(secp256k1_fe *r, const secp256k1_fe *a) { @@ -280,4 +287,29 @@ static void secp256k1_fe_inv_all_var(size_t len, secp256k1_fe *r, const secp256k r[0] = u; } +static int secp256k1_fe_is_quad_var(const secp256k1_fe *a) { +#ifndef USE_NUM_NONE + unsigned char b[32]; + secp256k1_num n; + secp256k1_num m; + /* secp256k1 field prime, value p defined in "Standards for Efficient Cryptography" (SEC2) 2.7.1. */ + static const unsigned char prime[32] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFE,0xFF,0xFF,0xFC,0x2F + }; + + secp256k1_fe c = *a; + secp256k1_fe_normalize_var(&c); + secp256k1_fe_get_b32(b, &c); + secp256k1_num_set_bin(&n, b, 32); + secp256k1_num_set_bin(&m, prime, 32); + return secp256k1_num_jacobi(&n, &m) >= 0; +#else + secp256k1_fe r; + return secp256k1_fe_sqrt(&r, a); +#endif +} + #endif diff --git a/src/secp256k1/src/group.h b/src/secp256k1/src/group.h index ebfe1ca70cc1..d5157167447a 100644 --- a/src/secp256k1/src/group.h +++ b/src/secp256k1/src/group.h @@ -47,7 +47,7 @@ static void secp256k1_ge_set_xy(secp256k1_ge *r, const secp256k1_fe *x, const se * and a Y coordinate that is a quadratic residue modulo p. The return value * is true iff a coordinate with the given X coordinate exists. */ -static int secp256k1_ge_set_xquad_var(secp256k1_ge *r, const secp256k1_fe *x); +static int secp256k1_ge_set_xquad(secp256k1_ge *r, const secp256k1_fe *x); /** Set a group element (affine) equal to the point with the given X coordinate, and given oddness * for Y. Return value indicates whether the result is valid. */ @@ -94,6 +94,9 @@ static void secp256k1_gej_neg(secp256k1_gej *r, const secp256k1_gej *a); /** Check whether a group element is the point at infinity. */ static int secp256k1_gej_is_infinity(const secp256k1_gej *a); +/** Check whether a group element's y coordinate is a quadratic residue. */ +static int secp256k1_gej_has_quad_y_var(const secp256k1_gej *a); + /** Set r equal to the double of a. If rzr is not-NULL, r->z = a->z * *rzr (where infinity means an implicit z = 0). * a may not be zero. Constant time. */ static void secp256k1_gej_double_nonzero(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr); diff --git a/src/secp256k1/src/group_impl.h b/src/secp256k1/src/group_impl.h index 42e2f6e6ebf6..3e9c4c410d41 100644 --- a/src/secp256k1/src/group_impl.h +++ b/src/secp256k1/src/group_impl.h @@ -7,8 +7,6 @@ #ifndef _SECP256K1_GROUP_IMPL_H_ #define _SECP256K1_GROUP_IMPL_H_ -#include - #include "num.h" #include "field.h" #include "group.h" @@ -165,7 +163,7 @@ static void secp256k1_ge_clear(secp256k1_ge *r) { secp256k1_fe_clear(&r->y); } -static int secp256k1_ge_set_xquad_var(secp256k1_ge *r, const secp256k1_fe *x) { +static int secp256k1_ge_set_xquad(secp256k1_ge *r, const secp256k1_fe *x) { secp256k1_fe x2, x3, c; r->x = *x; secp256k1_fe_sqr(&x2, x); @@ -173,11 +171,11 @@ static int secp256k1_ge_set_xquad_var(secp256k1_ge *r, const secp256k1_fe *x) { r->infinity = 0; secp256k1_fe_set_int(&c, 7); secp256k1_fe_add(&c, &x3); - return secp256k1_fe_sqrt_var(&r->y, &c); + return secp256k1_fe_sqrt(&r->y, &c); } static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int odd) { - if (!secp256k1_ge_set_xquad_var(r, x)) { + if (!secp256k1_ge_set_xquad(r, x)) { return 0; } secp256k1_fe_normalize_var(&r->y); @@ -251,11 +249,23 @@ static int secp256k1_ge_is_valid_var(const secp256k1_ge *a) { } static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr) { - /* Operations: 3 mul, 4 sqr, 0 normalize, 12 mul_int/add/negate */ + /* Operations: 3 mul, 4 sqr, 0 normalize, 12 mul_int/add/negate. + * + * Note that there is an implementation described at + * https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l + * which trades a multiply for a square, but in practice this is actually slower, + * mainly because it requires more normalizations. + */ secp256k1_fe t1,t2,t3,t4; /** For secp256k1, 2Q is infinity if and only if Q is infinity. This is because if 2Q = infinity, * Q must equal -Q, or that Q.y == -(Q.y), or Q.y is 0. For a point on y^2 = x^3 + 7 to have * y=0, x^3 must be -7 mod p. However, -7 has no cube root mod p. + * + * Having said this, if this function receives a point on a sextic twist, e.g. by + * a fault attack, it is possible for y to be 0. This happens for y^2 = x^3 + 6, + * since -6 does have a cube root mod p. For this point, this function will not set + * the infinity flag even though the point doubles to infinity, and the result + * point will be gibberish (z = 0 but infinity = 0). */ r->infinity = a->infinity; if (r->infinity) { @@ -623,4 +633,18 @@ static void secp256k1_ge_mul_lambda(secp256k1_ge *r, const secp256k1_ge *a) { } #endif +static int secp256k1_gej_has_quad_y_var(const secp256k1_gej *a) { + secp256k1_fe yz; + + if (a->infinity) { + return 0; + } + + /* We rely on the fact that the Jacobi symbol of 1 / a->z^3 is the same as + * that of a->z. Thus a->y / a->z^3 is a quadratic residue iff a->y * a->z + is */ + secp256k1_fe_mul(&yz, &a->y, &a->z); + return secp256k1_fe_is_quad_var(&yz); +} + #endif diff --git a/src/secp256k1/src/hash.h b/src/secp256k1/src/hash.h index 0ff01e63fa73..fca98cab9f83 100644 --- a/src/secp256k1/src/hash.h +++ b/src/secp256k1/src/hash.h @@ -11,7 +11,7 @@ #include typedef struct { - uint32_t s[32]; + uint32_t s[8]; uint32_t buf[16]; /* In big endian */ size_t bytes; } secp256k1_sha256_t; diff --git a/src/secp256k1/src/hash_impl.h b/src/secp256k1/src/hash_impl.h index ae55df6d8a29..b47e65f830a9 100644 --- a/src/secp256k1/src/hash_impl.h +++ b/src/secp256k1/src/hash_impl.h @@ -269,15 +269,13 @@ static void secp256k1_rfc6979_hmac_sha256_finalize(secp256k1_rfc6979_hmac_sha256 rng->retry = 0; } - +#undef BE32 #undef Round -#undef sigma0 #undef sigma1 -#undef Sigma0 +#undef sigma0 #undef Sigma1 -#undef Ch +#undef Sigma0 #undef Maj -#undef ReadBE32 -#undef WriteBE32 +#undef Ch #endif diff --git a/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1.java b/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1.java index 90a498eaa2c4..be67048fbed6 100644 --- a/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1.java +++ b/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1.java @@ -1,60 +1,478 @@ +/* + * Copyright 2013 Google Inc. + * Copyright 2014-2016 the libsecp256k1 contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.bitcoin; import java.nio.ByteBuffer; import java.nio.ByteOrder; +import java.math.BigInteger; import com.google.common.base.Preconditions; - +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import static org.bitcoin.NativeSecp256k1Util.*; /** - * This class holds native methods to handle ECDSA verification. - * You can find an example library that can be used for this at - * https://github.com/sipa/secp256k1 + *

This class holds native methods to handle ECDSA verification.

+ * + *

You can find an example library that can be used for this at https://github.com/bitcoin/secp256k1

+ * + *

To build secp256k1 for use with bitcoinj, run + * `./configure --enable-jni --enable-experimental --enable-module-schnorr --enable-module-ecdh` + * and `make` then copy `.libs/libsecp256k1.so` to your system library path + * or point the JVM to the folder containing it with -Djava.library.path + *

*/ public class NativeSecp256k1 { - public static final boolean enabled; - static { - boolean isEnabled = true; - try { - System.loadLibrary("javasecp256k1"); - } catch (UnsatisfiedLinkError e) { - isEnabled = false; - } - enabled = isEnabled; - } - + + private static final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); + private static final Lock r = rwl.readLock(); + private static final Lock w = rwl.writeLock(); private static ThreadLocal nativeECDSABuffer = new ThreadLocal(); /** * Verifies the given secp256k1 signature in native code. * Calling when enabled == false is undefined (probably library not loaded) - * + * * @param data The data which was signed, must be exactly 32 bytes * @param signature The signature * @param pub The public key which did the signing */ - public static boolean verify(byte[] data, byte[] signature, byte[] pub) { + public static boolean verify(byte[] data, byte[] signature, byte[] pub) throws AssertFailException{ Preconditions.checkArgument(data.length == 32 && signature.length <= 520 && pub.length <= 520); ByteBuffer byteBuff = nativeECDSABuffer.get(); - if (byteBuff == null) { - byteBuff = ByteBuffer.allocateDirect(32 + 8 + 520 + 520); + if (byteBuff == null || byteBuff.capacity() < 520) { + byteBuff = ByteBuffer.allocateDirect(520); byteBuff.order(ByteOrder.nativeOrder()); nativeECDSABuffer.set(byteBuff); } byteBuff.rewind(); byteBuff.put(data); - byteBuff.putInt(signature.length); - byteBuff.putInt(pub.length); byteBuff.put(signature); byteBuff.put(pub); - return secp256k1_ecdsa_verify(byteBuff) == 1; + + byte[][] retByteArray; + + r.lock(); + try { + return secp256k1_ecdsa_verify(byteBuff, Secp256k1Context.getContext(), signature.length, pub.length) == 1; + } finally { + r.unlock(); + } + } + + /** + * libsecp256k1 Create an ECDSA signature. + * + * @param data Message hash, 32 bytes + * @param key Secret key, 32 bytes + * + * Return values + * @param sig byte array of signature + */ + public static byte[] sign(byte[] data, byte[] sec) throws AssertFailException{ + Preconditions.checkArgument(data.length == 32 && sec.length <= 32); + + ByteBuffer byteBuff = nativeECDSABuffer.get(); + if (byteBuff == null || byteBuff.capacity() < 32 + 32) { + byteBuff = ByteBuffer.allocateDirect(32 + 32); + byteBuff.order(ByteOrder.nativeOrder()); + nativeECDSABuffer.set(byteBuff); + } + byteBuff.rewind(); + byteBuff.put(data); + byteBuff.put(sec); + + byte[][] retByteArray; + + r.lock(); + try { + retByteArray = secp256k1_ecdsa_sign(byteBuff, Secp256k1Context.getContext()); + } finally { + r.unlock(); + } + + byte[] sigArr = retByteArray[0]; + int sigLen = new BigInteger(new byte[] { retByteArray[1][0] }).intValue(); + int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue(); + + assertEquals(sigArr.length, sigLen, "Got bad signature length."); + + return retVal == 0 ? new byte[0] : sigArr; + } + + /** + * libsecp256k1 Seckey Verify - returns 1 if valid, 0 if invalid + * + * @param seckey ECDSA Secret key, 32 bytes + */ + public static boolean secKeyVerify(byte[] seckey) { + Preconditions.checkArgument(seckey.length == 32); + + ByteBuffer byteBuff = nativeECDSABuffer.get(); + if (byteBuff == null || byteBuff.capacity() < seckey.length) { + byteBuff = ByteBuffer.allocateDirect(seckey.length); + byteBuff.order(ByteOrder.nativeOrder()); + nativeECDSABuffer.set(byteBuff); + } + byteBuff.rewind(); + byteBuff.put(seckey); + + r.lock(); + try { + return secp256k1_ec_seckey_verify(byteBuff,Secp256k1Context.getContext()) == 1; + } finally { + r.unlock(); + } + } + + + /** + * libsecp256k1 Compute Pubkey - computes public key from secret key + * + * @param seckey ECDSA Secret key, 32 bytes + * + * Return values + * @param pubkey ECDSA Public key, 33 or 65 bytes + */ + //TODO add a 'compressed' arg + public static byte[] computePubkey(byte[] seckey) throws AssertFailException{ + Preconditions.checkArgument(seckey.length == 32); + + ByteBuffer byteBuff = nativeECDSABuffer.get(); + if (byteBuff == null || byteBuff.capacity() < seckey.length) { + byteBuff = ByteBuffer.allocateDirect(seckey.length); + byteBuff.order(ByteOrder.nativeOrder()); + nativeECDSABuffer.set(byteBuff); + } + byteBuff.rewind(); + byteBuff.put(seckey); + + byte[][] retByteArray; + + r.lock(); + try { + retByteArray = secp256k1_ec_pubkey_create(byteBuff, Secp256k1Context.getContext()); + } finally { + r.unlock(); + } + + byte[] pubArr = retByteArray[0]; + int pubLen = new BigInteger(new byte[] { retByteArray[1][0] }).intValue(); + int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue(); + + assertEquals(pubArr.length, pubLen, "Got bad pubkey length."); + + return retVal == 0 ? new byte[0]: pubArr; + } + + /** + * libsecp256k1 Cleanup - This destroys the secp256k1 context object + * This should be called at the end of the program for proper cleanup of the context. + */ + public static synchronized void cleanup() { + w.lock(); + try { + secp256k1_destroy_context(Secp256k1Context.getContext()); + } finally { + w.unlock(); + } + } + + public static long cloneContext() { + r.lock(); + try { + return secp256k1_ctx_clone(Secp256k1Context.getContext()); + } finally { r.unlock(); } + } + + /** + * libsecp256k1 PrivKey Tweak-Mul - Tweak privkey by multiplying to it + * + * @param tweak some bytes to tweak with + * @param seckey 32-byte seckey + */ + public static byte[] privKeyTweakMul(byte[] privkey, byte[] tweak) throws AssertFailException{ + Preconditions.checkArgument(privkey.length == 32); + + ByteBuffer byteBuff = nativeECDSABuffer.get(); + if (byteBuff == null || byteBuff.capacity() < privkey.length + tweak.length) { + byteBuff = ByteBuffer.allocateDirect(privkey.length + tweak.length); + byteBuff.order(ByteOrder.nativeOrder()); + nativeECDSABuffer.set(byteBuff); + } + byteBuff.rewind(); + byteBuff.put(privkey); + byteBuff.put(tweak); + + byte[][] retByteArray; + r.lock(); + try { + retByteArray = secp256k1_privkey_tweak_mul(byteBuff,Secp256k1Context.getContext()); + } finally { + r.unlock(); + } + + byte[] privArr = retByteArray[0]; + + int privLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF; + int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue(); + + assertEquals(privArr.length, privLen, "Got bad pubkey length."); + + assertEquals(retVal, 1, "Failed return value check."); + + return privArr; } /** - * @param byteBuff signature format is byte[32] data, - * native-endian int signatureLength, native-endian int pubkeyLength, - * byte[signatureLength] signature, byte[pubkeyLength] pub - * @returns 1 for valid signature, anything else for invalid + * libsecp256k1 PrivKey Tweak-Add - Tweak privkey by adding to it + * + * @param tweak some bytes to tweak with + * @param seckey 32-byte seckey */ - private static native int secp256k1_ecdsa_verify(ByteBuffer byteBuff); + public static byte[] privKeyTweakAdd(byte[] privkey, byte[] tweak) throws AssertFailException{ + Preconditions.checkArgument(privkey.length == 32); + + ByteBuffer byteBuff = nativeECDSABuffer.get(); + if (byteBuff == null || byteBuff.capacity() < privkey.length + tweak.length) { + byteBuff = ByteBuffer.allocateDirect(privkey.length + tweak.length); + byteBuff.order(ByteOrder.nativeOrder()); + nativeECDSABuffer.set(byteBuff); + } + byteBuff.rewind(); + byteBuff.put(privkey); + byteBuff.put(tweak); + + byte[][] retByteArray; + r.lock(); + try { + retByteArray = secp256k1_privkey_tweak_add(byteBuff,Secp256k1Context.getContext()); + } finally { + r.unlock(); + } + + byte[] privArr = retByteArray[0]; + + int privLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF; + int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue(); + + assertEquals(privArr.length, privLen, "Got bad pubkey length."); + + assertEquals(retVal, 1, "Failed return value check."); + + return privArr; + } + + /** + * libsecp256k1 PubKey Tweak-Add - Tweak pubkey by adding to it + * + * @param tweak some bytes to tweak with + * @param pubkey 32-byte seckey + */ + public static byte[] pubKeyTweakAdd(byte[] pubkey, byte[] tweak) throws AssertFailException{ + Preconditions.checkArgument(pubkey.length == 33 || pubkey.length == 65); + + ByteBuffer byteBuff = nativeECDSABuffer.get(); + if (byteBuff == null || byteBuff.capacity() < pubkey.length + tweak.length) { + byteBuff = ByteBuffer.allocateDirect(pubkey.length + tweak.length); + byteBuff.order(ByteOrder.nativeOrder()); + nativeECDSABuffer.set(byteBuff); + } + byteBuff.rewind(); + byteBuff.put(pubkey); + byteBuff.put(tweak); + + byte[][] retByteArray; + r.lock(); + try { + retByteArray = secp256k1_pubkey_tweak_add(byteBuff,Secp256k1Context.getContext(), pubkey.length); + } finally { + r.unlock(); + } + + byte[] pubArr = retByteArray[0]; + + int pubLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF; + int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue(); + + assertEquals(pubArr.length, pubLen, "Got bad pubkey length."); + + assertEquals(retVal, 1, "Failed return value check."); + + return pubArr; + } + + /** + * libsecp256k1 PubKey Tweak-Mul - Tweak pubkey by multiplying to it + * + * @param tweak some bytes to tweak with + * @param pubkey 32-byte seckey + */ + public static byte[] pubKeyTweakMul(byte[] pubkey, byte[] tweak) throws AssertFailException{ + Preconditions.checkArgument(pubkey.length == 33 || pubkey.length == 65); + + ByteBuffer byteBuff = nativeECDSABuffer.get(); + if (byteBuff == null || byteBuff.capacity() < pubkey.length + tweak.length) { + byteBuff = ByteBuffer.allocateDirect(pubkey.length + tweak.length); + byteBuff.order(ByteOrder.nativeOrder()); + nativeECDSABuffer.set(byteBuff); + } + byteBuff.rewind(); + byteBuff.put(pubkey); + byteBuff.put(tweak); + + byte[][] retByteArray; + r.lock(); + try { + retByteArray = secp256k1_pubkey_tweak_mul(byteBuff,Secp256k1Context.getContext(), pubkey.length); + } finally { + r.unlock(); + } + + byte[] pubArr = retByteArray[0]; + + int pubLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF; + int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue(); + + assertEquals(pubArr.length, pubLen, "Got bad pubkey length."); + + assertEquals(retVal, 1, "Failed return value check."); + + return pubArr; + } + + /** + * libsecp256k1 create ECDH secret - constant time ECDH calculation + * + * @param seckey byte array of secret key used in exponentiaion + * @param pubkey byte array of public key used in exponentiaion + */ + public static byte[] createECDHSecret(byte[] seckey, byte[] pubkey) throws AssertFailException{ + Preconditions.checkArgument(seckey.length <= 32 && pubkey.length <= 65); + + ByteBuffer byteBuff = nativeECDSABuffer.get(); + if (byteBuff == null || byteBuff.capacity() < 32 + pubkey.length) { + byteBuff = ByteBuffer.allocateDirect(32 + pubkey.length); + byteBuff.order(ByteOrder.nativeOrder()); + nativeECDSABuffer.set(byteBuff); + } + byteBuff.rewind(); + byteBuff.put(seckey); + byteBuff.put(pubkey); + + byte[][] retByteArray; + r.lock(); + try { + retByteArray = secp256k1_ecdh(byteBuff, Secp256k1Context.getContext(), pubkey.length); + } finally { + r.unlock(); + } + + byte[] resArr = retByteArray[0]; + int retVal = new BigInteger(new byte[] { retByteArray[1][0] }).intValue(); + + assertEquals(resArr.length, 32, "Got bad result length."); + assertEquals(retVal, 1, "Failed return value check."); + + return resArr; + } + + /** + * libsecp256k1 randomize - updates the context randomization + * + * @param seed 32-byte random seed + */ + public static synchronized boolean randomize(byte[] seed) throws AssertFailException{ + Preconditions.checkArgument(seed.length == 32 || seed == null); + + ByteBuffer byteBuff = nativeECDSABuffer.get(); + if (byteBuff == null || byteBuff.capacity() < seed.length) { + byteBuff = ByteBuffer.allocateDirect(seed.length); + byteBuff.order(ByteOrder.nativeOrder()); + nativeECDSABuffer.set(byteBuff); + } + byteBuff.rewind(); + byteBuff.put(seed); + + w.lock(); + try { + return secp256k1_context_randomize(byteBuff, Secp256k1Context.getContext()) == 1; + } finally { + w.unlock(); + } + } + + public static byte[] schnorrSign(byte[] data, byte[] sec) throws AssertFailException { + Preconditions.checkArgument(data.length == 32 && sec.length <= 32); + + ByteBuffer byteBuff = nativeECDSABuffer.get(); + if (byteBuff == null) { + byteBuff = ByteBuffer.allocateDirect(32 + 32); + byteBuff.order(ByteOrder.nativeOrder()); + nativeECDSABuffer.set(byteBuff); + } + byteBuff.rewind(); + byteBuff.put(data); + byteBuff.put(sec); + + byte[][] retByteArray; + + r.lock(); + try { + retByteArray = secp256k1_schnorr_sign(byteBuff, Secp256k1Context.getContext()); + } finally { + r.unlock(); + } + + byte[] sigArr = retByteArray[0]; + int retVal = new BigInteger(new byte[] { retByteArray[1][0] }).intValue(); + + assertEquals(sigArr.length, 64, "Got bad signature length."); + + return retVal == 0 ? new byte[0] : sigArr; + } + + private static native long secp256k1_ctx_clone(long context); + + private static native int secp256k1_context_randomize(ByteBuffer byteBuff, long context); + + private static native byte[][] secp256k1_privkey_tweak_add(ByteBuffer byteBuff, long context); + + private static native byte[][] secp256k1_privkey_tweak_mul(ByteBuffer byteBuff, long context); + + private static native byte[][] secp256k1_pubkey_tweak_add(ByteBuffer byteBuff, long context, int pubLen); + + private static native byte[][] secp256k1_pubkey_tweak_mul(ByteBuffer byteBuff, long context, int pubLen); + + private static native void secp256k1_destroy_context(long context); + + private static native int secp256k1_ecdsa_verify(ByteBuffer byteBuff, long context, int sigLen, int pubLen); + + private static native byte[][] secp256k1_ecdsa_sign(ByteBuffer byteBuff, long context); + + private static native int secp256k1_ec_seckey_verify(ByteBuffer byteBuff, long context); + + private static native byte[][] secp256k1_ec_pubkey_create(ByteBuffer byteBuff, long context); + + private static native byte[][] secp256k1_ec_pubkey_parse(ByteBuffer byteBuff, long context, int inputLen); + + private static native byte[][] secp256k1_schnorr_sign(ByteBuffer byteBuff, long context); + + private static native byte[][] secp256k1_ecdh(ByteBuffer byteBuff, long context, int inputLen); + } diff --git a/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java b/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java new file mode 100644 index 000000000000..f18ce95810d1 --- /dev/null +++ b/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java @@ -0,0 +1,247 @@ +package org.bitcoin; + +import com.google.common.io.BaseEncoding; +import java.util.Arrays; +import java.math.BigInteger; +import javax.xml.bind.DatatypeConverter; +import static org.bitcoin.NativeSecp256k1Util.*; + +/** + * This class holds test cases defined for testing this library. + */ +public class NativeSecp256k1Test { + + //TODO improve comments/add more tests + /** + * This tests verify() for a valid signature + */ + public static void testVerifyPos() throws AssertFailException{ + boolean result = false; + byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()); //sha256hash of "testing" + byte[] sig = BaseEncoding.base16().lowerCase().decode("3044022079BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817980220294F14E883B3F525B5367756C2A11EF6CF84B730B36C17CB0C56F0AAB2C98589".toLowerCase()); + byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase()); + + result = NativeSecp256k1.verify( data, sig, pub); + assertEquals( result, true , "testVerifyPos"); + } + + /** + * This tests verify() for a non-valid signature + */ + public static void testVerifyNeg() throws AssertFailException{ + boolean result = false; + byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A91".toLowerCase()); //sha256hash of "testing" + byte[] sig = BaseEncoding.base16().lowerCase().decode("3044022079BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817980220294F14E883B3F525B5367756C2A11EF6CF84B730B36C17CB0C56F0AAB2C98589".toLowerCase()); + byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase()); + + result = NativeSecp256k1.verify( data, sig, pub); + //System.out.println(" TEST " + new BigInteger(1, resultbytes).toString(16)); + assertEquals( result, false , "testVerifyNeg"); + } + + /** + * This tests secret key verify() for a valid secretkey + */ + public static void testSecKeyVerifyPos() throws AssertFailException{ + boolean result = false; + byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase()); + + result = NativeSecp256k1.secKeyVerify( sec ); + //System.out.println(" TEST " + new BigInteger(1, resultbytes).toString(16)); + assertEquals( result, true , "testSecKeyVerifyPos"); + } + + /** + * This tests secret key verify() for a invalid secretkey + */ + public static void testSecKeyVerifyNeg() throws AssertFailException{ + boolean result = false; + byte[] sec = BaseEncoding.base16().lowerCase().decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase()); + + result = NativeSecp256k1.secKeyVerify( sec ); + //System.out.println(" TEST " + new BigInteger(1, resultbytes).toString(16)); + assertEquals( result, false , "testSecKeyVerifyNeg"); + } + + /** + * This tests public key create() for a valid secretkey + */ + public static void testPubKeyCreatePos() throws AssertFailException{ + byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase()); + + byte[] resultArr = NativeSecp256k1.computePubkey( sec); + String pubkeyString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + assertEquals( pubkeyString , "04C591A8FF19AC9C4E4E5793673B83123437E975285E7B442F4EE2654DFFCA5E2D2103ED494718C697AC9AEBCFD19612E224DB46661011863ED2FC54E71861E2A6" , "testPubKeyCreatePos"); + } + + /** + * This tests public key create() for a invalid secretkey + */ + public static void testPubKeyCreateNeg() throws AssertFailException{ + byte[] sec = BaseEncoding.base16().lowerCase().decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase()); + + byte[] resultArr = NativeSecp256k1.computePubkey( sec); + String pubkeyString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + assertEquals( pubkeyString, "" , "testPubKeyCreateNeg"); + } + + /** + * This tests sign() for a valid secretkey + */ + public static void testSignPos() throws AssertFailException{ + + byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()); //sha256hash of "testing" + byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase()); + + byte[] resultArr = NativeSecp256k1.sign(data, sec); + String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + assertEquals( sigString, "30440220182A108E1448DC8F1FB467D06A0F3BB8EA0533584CB954EF8DA112F1D60E39A202201C66F36DA211C087F3AF88B50EDF4F9BDAA6CF5FD6817E74DCA34DB12390C6E9" , "testSignPos"); + } + + /** + * This tests sign() for a invalid secretkey + */ + public static void testSignNeg() throws AssertFailException{ + byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()); //sha256hash of "testing" + byte[] sec = BaseEncoding.base16().lowerCase().decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase()); + + byte[] resultArr = NativeSecp256k1.sign(data, sec); + String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + assertEquals( sigString, "" , "testSignNeg"); + } + + /** + * This tests private key tweak-add + */ + public static void testPrivKeyTweakAdd_1() throws AssertFailException { + byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase()); + byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak" + + byte[] resultArr = NativeSecp256k1.privKeyTweakAdd( sec , data ); + String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + assertEquals( sigString , "A168571E189E6F9A7E2D657A4B53AE99B909F7E712D1C23CED28093CD57C88F3" , "testPrivKeyAdd_1"); + } + + /** + * This tests private key tweak-mul + */ + public static void testPrivKeyTweakMul_1() throws AssertFailException { + byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase()); + byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak" + + byte[] resultArr = NativeSecp256k1.privKeyTweakMul( sec , data ); + String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + assertEquals( sigString , "97F8184235F101550F3C71C927507651BD3F1CDB4A5A33B8986ACF0DEE20FFFC" , "testPrivKeyMul_1"); + } + + /** + * This tests private key tweak-add uncompressed + */ + public static void testPrivKeyTweakAdd_2() throws AssertFailException { + byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase()); + byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak" + + byte[] resultArr = NativeSecp256k1.pubKeyTweakAdd( pub , data ); + String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + assertEquals( sigString , "0411C6790F4B663CCE607BAAE08C43557EDC1A4D11D88DFCB3D841D0C6A941AF525A268E2A863C148555C48FB5FBA368E88718A46E205FABC3DBA2CCFFAB0796EF" , "testPrivKeyAdd_2"); + } + + /** + * This tests private key tweak-mul uncompressed + */ + public static void testPrivKeyTweakMul_2() throws AssertFailException { + byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase()); + byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak" + + byte[] resultArr = NativeSecp256k1.pubKeyTweakMul( pub , data ); + String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + assertEquals( sigString , "04E0FE6FE55EBCA626B98A807F6CAF654139E14E5E3698F01A9A658E21DC1D2791EC060D4F412A794D5370F672BC94B722640B5F76914151CFCA6E712CA48CC589" , "testPrivKeyMul_2"); + } + + /** + * This tests seed randomization + */ + public static void testRandomize() throws AssertFailException { + byte[] seed = BaseEncoding.base16().lowerCase().decode("A441B15FE9A3CF56661190A0B93B9DEC7D04127288CC87250967CF3B52894D11".toLowerCase()); //sha256hash of "random" + boolean result = NativeSecp256k1.randomize(seed); + assertEquals( result, true, "testRandomize"); + } + + /** + * This tests signSchnorr() for a valid secretkey + */ + public static void testSchnorrSign() throws AssertFailException{ + + byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()); //sha256hash of "testing" + byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase()); + + byte[] resultArr = NativeSecp256k1.schnorrSign(data, sec); + String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + assertEquals( sigString, "C5E929AA058B982048760422D3B563749B7D0E50C5EBD8CD2FFC23214BD6A2F1B072C13880997EBA847CF20F2F90FCE07C1CA33A890A4127095A351127F8D95F" , "testSchnorrSign"); + } + + /** + * This tests signSchnorr() for a valid secretkey + */ + public static void testCreateECDHSecret() throws AssertFailException{ + + byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase()); + byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase()); + + byte[] resultArr = NativeSecp256k1.createECDHSecret(sec, pub); + String ecdhString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + assertEquals( ecdhString, "2A2A67007A926E6594AF3EB564FC74005B37A9C8AEF2033C4552051B5C87F043" , "testCreateECDHSecret"); + } + + public static void main(String[] args) throws AssertFailException{ + + + System.out.println("\n libsecp256k1 enabled: " + Secp256k1Context.isEnabled() + "\n"); + + assertEquals( Secp256k1Context.isEnabled(), true, "isEnabled" ); + + //Test verify() success/fail + testVerifyPos(); + testVerifyNeg(); + + //Test secKeyVerify() success/fail + testSecKeyVerifyPos(); + testSecKeyVerifyNeg(); + + //Test computePubkey() success/fail + testPubKeyCreatePos(); + testPubKeyCreateNeg(); + + //Test sign() success/fail + testSignPos(); + testSignNeg(); + + //Test Schnorr (partial support) //TODO + testSchnorrSign(); + //testSchnorrVerify + //testSchnorrRecovery + + //Test privKeyTweakAdd() 1 + testPrivKeyTweakAdd_1(); + + //Test privKeyTweakMul() 2 + testPrivKeyTweakMul_1(); + + //Test privKeyTweakAdd() 3 + testPrivKeyTweakAdd_2(); + + //Test privKeyTweakMul() 4 + testPrivKeyTweakMul_2(); + + //Test randomize() + testRandomize(); + + //Test ECDH + testCreateECDHSecret(); + + NativeSecp256k1.cleanup(); + + System.out.println(" All tests passed." ); + + } +} diff --git a/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Util.java b/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Util.java new file mode 100644 index 000000000000..04732ba04436 --- /dev/null +++ b/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Util.java @@ -0,0 +1,45 @@ +/* + * Copyright 2014-2016 the libsecp256k1 contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.bitcoin; + +public class NativeSecp256k1Util{ + + public static void assertEquals( int val, int val2, String message ) throws AssertFailException{ + if( val != val2 ) + throw new AssertFailException("FAIL: " + message); + } + + public static void assertEquals( boolean val, boolean val2, String message ) throws AssertFailException{ + if( val != val2 ) + throw new AssertFailException("FAIL: " + message); + else + System.out.println("PASS: " + message); + } + + public static void assertEquals( String val, String val2, String message ) throws AssertFailException{ + if( !val.equals(val2) ) + throw new AssertFailException("FAIL: " + message); + else + System.out.println("PASS: " + message); + } + + public static class AssertFailException extends Exception { + public AssertFailException(String message) { + super( message ); + } + } +} diff --git a/src/secp256k1/src/java/org/bitcoin/Secp256k1Context.java b/src/secp256k1/src/java/org/bitcoin/Secp256k1Context.java new file mode 100644 index 000000000000..216c986a8b56 --- /dev/null +++ b/src/secp256k1/src/java/org/bitcoin/Secp256k1Context.java @@ -0,0 +1,51 @@ +/* + * Copyright 2014-2016 the libsecp256k1 contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.bitcoin; + +/** + * This class holds the context reference used in native methods + * to handle ECDSA operations. + */ +public class Secp256k1Context { + private static final boolean enabled; //true if the library is loaded + private static final long context; //ref to pointer to context obj + + static { //static initializer + boolean isEnabled = true; + long contextRef = -1; + try { + System.loadLibrary("secp256k1"); + contextRef = secp256k1_init_context(); + } catch (UnsatisfiedLinkError e) { + System.out.println("UnsatisfiedLinkError: " + e.toString()); + isEnabled = false; + } + enabled = isEnabled; + context = contextRef; + } + + public static boolean isEnabled() { + return enabled; + } + + public static long getContext() { + if(!enabled) return -1; //sanity check + return context; + } + + private static native long secp256k1_init_context(); +} diff --git a/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.c b/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.c index bb4cd707280a..dba9524dd4cc 100644 --- a/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.c +++ b/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.c @@ -1,23 +1,411 @@ +#include +#include +#include #include "org_bitcoin_NativeSecp256k1.h" #include "include/secp256k1.h" +#include "include/secp256k1_ecdh.h" +#include "include/secp256k1_recovery.h" +#include "include/secp256k1_schnorr.h" -JNIEXPORT jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1verify - (JNIEnv* env, jclass classObject, jobject byteBufferObject) + +SECP256K1_API jlong JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ctx_1clone + (JNIEnv* env, jclass classObject, jlong ctx_l) +{ + const secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + + jlong ctx_clone_l = (uintptr_t) secp256k1_context_clone(ctx); + + (void)classObject;(void)env; + + return ctx_clone_l; + +} + +SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1context_1randomize + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l) +{ + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + + const unsigned char* seed = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); + + (void)classObject; + + return secp256k1_context_randomize(ctx, seed); + +} + +SECP256K1_API void JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1destroy_1context + (JNIEnv* env, jclass classObject, jlong ctx_l) +{ + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + + secp256k1_context_destroy(ctx); + + (void)classObject;(void)env; +} + +SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1verify + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint siglen, jint publen) +{ + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + + unsigned char* data = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); + const unsigned char* sigdata = { (unsigned char*) (data + 32) }; + const unsigned char* pubdata = { (unsigned char*) (data + siglen + 32) }; + + secp256k1_ecdsa_signature sig; + secp256k1_pubkey pubkey; + + int ret = secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigdata, siglen); + + if( ret ) { + ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pubdata, publen); + + if( ret ) { + ret = secp256k1_ecdsa_verify(ctx, &sig, data, &pubkey); + } + } + + (void)classObject; + + return ret; +} + +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1sign + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l) +{ + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + unsigned char* data = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); + unsigned char* secKey = (unsigned char*) (data + 32); + + jobjectArray retArray; + jbyteArray sigArray, intsByteArray; + unsigned char intsarray[2]; + + secp256k1_ecdsa_signature sig[72]; + + int ret = secp256k1_ecdsa_sign(ctx, sig, data, secKey, NULL, NULL ); + + unsigned char outputSer[72]; + size_t outputLen = 72; + + if( ret ) { + int ret2 = secp256k1_ecdsa_signature_serialize_der(ctx,outputSer, &outputLen, sig ); (void)ret2; + } + + intsarray[0] = outputLen; + intsarray[1] = ret; + + retArray = (*env)->NewObjectArray(env, 2, + (*env)->FindClass(env, "[B"), + (*env)->NewByteArray(env, 1)); + + sigArray = (*env)->NewByteArray(env, outputLen); + (*env)->SetByteArrayRegion(env, sigArray, 0, outputLen, (jbyte*)outputSer); + (*env)->SetObjectArrayElement(env, retArray, 0, sigArray); + + intsByteArray = (*env)->NewByteArray(env, 2); + (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray); + (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray); + + (void)classObject; + + return retArray; +} + +SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1seckey_1verify + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l) +{ + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + unsigned char* secKey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); + + (void)classObject; + + return secp256k1_ec_seckey_verify(ctx, secKey); +} + +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1pubkey_1create + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l) +{ + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + const unsigned char* secKey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); + + secp256k1_pubkey pubkey; + + jobjectArray retArray; + jbyteArray pubkeyArray, intsByteArray; + unsigned char intsarray[2]; + + int ret = secp256k1_ec_pubkey_create(ctx, &pubkey, secKey); + + unsigned char outputSer[65]; + size_t outputLen = 65; + + if( ret ) { + int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey,SECP256K1_EC_UNCOMPRESSED );(void)ret2; + } + + intsarray[0] = outputLen; + intsarray[1] = ret; + + retArray = (*env)->NewObjectArray(env, 2, + (*env)->FindClass(env, "[B"), + (*env)->NewByteArray(env, 1)); + + pubkeyArray = (*env)->NewByteArray(env, outputLen); + (*env)->SetByteArrayRegion(env, pubkeyArray, 0, outputLen, (jbyte*)outputSer); + (*env)->SetObjectArrayElement(env, retArray, 0, pubkeyArray); + + intsByteArray = (*env)->NewByteArray(env, 2); + (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray); + (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray); + + (void)classObject; + + return retArray; + +} + +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1privkey_1tweak_1add + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l) +{ + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + unsigned char* privkey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); + const unsigned char* tweak = (unsigned char*) (privkey + 32); + + jobjectArray retArray; + jbyteArray privArray, intsByteArray; + unsigned char intsarray[2]; + + int privkeylen = 32; + + int ret = secp256k1_ec_privkey_tweak_add(ctx, privkey, tweak); + + intsarray[0] = privkeylen; + intsarray[1] = ret; + + retArray = (*env)->NewObjectArray(env, 2, + (*env)->FindClass(env, "[B"), + (*env)->NewByteArray(env, 1)); + + privArray = (*env)->NewByteArray(env, privkeylen); + (*env)->SetByteArrayRegion(env, privArray, 0, privkeylen, (jbyte*)privkey); + (*env)->SetObjectArrayElement(env, retArray, 0, privArray); + + intsByteArray = (*env)->NewByteArray(env, 2); + (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray); + (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray); + + (void)classObject; + + return retArray; +} + +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1privkey_1tweak_1mul + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l) +{ + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + unsigned char* privkey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); + const unsigned char* tweak = (unsigned char*) (privkey + 32); + + jobjectArray retArray; + jbyteArray privArray, intsByteArray; + unsigned char intsarray[2]; + + int privkeylen = 32; + + int ret = secp256k1_ec_privkey_tweak_mul(ctx, privkey, tweak); + + intsarray[0] = privkeylen; + intsarray[1] = ret; + + retArray = (*env)->NewObjectArray(env, 2, + (*env)->FindClass(env, "[B"), + (*env)->NewByteArray(env, 1)); + + privArray = (*env)->NewByteArray(env, privkeylen); + (*env)->SetByteArrayRegion(env, privArray, 0, privkeylen, (jbyte*)privkey); + (*env)->SetObjectArrayElement(env, retArray, 0, privArray); + + intsByteArray = (*env)->NewByteArray(env, 2); + (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray); + (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray); + + (void)classObject; + + return retArray; +} + +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1pubkey_1tweak_1add + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen) +{ + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; +/* secp256k1_pubkey* pubkey = (secp256k1_pubkey*) (*env)->GetDirectBufferAddress(env, byteBufferObject);*/ + unsigned char* pkey = (*env)->GetDirectBufferAddress(env, byteBufferObject); + const unsigned char* tweak = (unsigned char*) (pkey + publen); + + jobjectArray retArray; + jbyteArray pubArray, intsByteArray; + unsigned char intsarray[2]; + unsigned char outputSer[65]; + size_t outputLen = 65; + + secp256k1_pubkey pubkey; + int ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pkey, publen); + + if( ret ) { + ret = secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, tweak); + } + + if( ret ) { + int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey,SECP256K1_EC_UNCOMPRESSED );(void)ret2; + } + + intsarray[0] = outputLen; + intsarray[1] = ret; + + retArray = (*env)->NewObjectArray(env, 2, + (*env)->FindClass(env, "[B"), + (*env)->NewByteArray(env, 1)); + + pubArray = (*env)->NewByteArray(env, outputLen); + (*env)->SetByteArrayRegion(env, pubArray, 0, outputLen, (jbyte*)outputSer); + (*env)->SetObjectArrayElement(env, retArray, 0, pubArray); + + intsByteArray = (*env)->NewByteArray(env, 2); + (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray); + (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray); + + (void)classObject; + + return retArray; +} + +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1pubkey_1tweak_1mul + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen) { - unsigned char* data = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); - int sigLen = *((int*)(data + 32)); - int pubLen = *((int*)(data + 32 + 4)); + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + unsigned char* pkey = (*env)->GetDirectBufferAddress(env, byteBufferObject); + const unsigned char* tweak = (unsigned char*) (pkey + publen); + + jobjectArray retArray; + jbyteArray pubArray, intsByteArray; + unsigned char intsarray[2]; + unsigned char outputSer[65]; + size_t outputLen = 65; + + secp256k1_pubkey pubkey; + int ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pkey, publen); + + if ( ret ) { + ret = secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, tweak); + } + + if( ret ) { + int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey,SECP256K1_EC_UNCOMPRESSED );(void)ret2; + } + + intsarray[0] = outputLen; + intsarray[1] = ret; + + retArray = (*env)->NewObjectArray(env, 2, + (*env)->FindClass(env, "[B"), + (*env)->NewByteArray(env, 1)); + + pubArray = (*env)->NewByteArray(env, outputLen); + (*env)->SetByteArrayRegion(env, pubArray, 0, outputLen, (jbyte*)outputSer); + (*env)->SetObjectArrayElement(env, retArray, 0, pubArray); - return secp256k1_ecdsa_verify(data, 32, data+32+8, sigLen, data+32+8+sigLen, pubLen); + intsByteArray = (*env)->NewByteArray(env, 2); + (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray); + (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray); + + (void)classObject; + + return retArray; } -static void __javasecp256k1_attach(void) __attribute__((constructor)); -static void __javasecp256k1_detach(void) __attribute__((destructor)); +SECP256K1_API jlong JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1pubkey_1combine + (JNIEnv * env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint numkeys) +{ + (void)classObject;(void)env;(void)byteBufferObject;(void)ctx_l;(void)numkeys; -static void __javasecp256k1_attach(void) { - secp256k1_start(SECP256K1_START_VERIFY); + return 0; } -static void __javasecp256k1_detach(void) { - secp256k1_stop(); +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1schnorr_1sign + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l) +{ + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + unsigned char* data = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); + unsigned char* secKey = (unsigned char*) (data + 32); + + jobjectArray retArray; + jbyteArray sigArray, intsByteArray; + unsigned char intsarray[1]; + unsigned char sig[64]; + + int ret = secp256k1_schnorr_sign(ctx, sig, data, secKey, NULL, NULL); + + intsarray[0] = ret; + + retArray = (*env)->NewObjectArray(env, 2, + (*env)->FindClass(env, "[B"), + (*env)->NewByteArray(env, 1)); + + sigArray = (*env)->NewByteArray(env, 64); + (*env)->SetByteArrayRegion(env, sigArray, 0, 64, (jbyte*)sig); + (*env)->SetObjectArrayElement(env, retArray, 0, sigArray); + + intsByteArray = (*env)->NewByteArray(env, 1); + (*env)->SetByteArrayRegion(env, intsByteArray, 0, 1, (jbyte*)intsarray); + (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray); + + (void)classObject; + + return retArray; +} + +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdh + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen) +{ + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + const unsigned char* secdata = (*env)->GetDirectBufferAddress(env, byteBufferObject); + const unsigned char* pubdata = (const unsigned char*) (secdata + 32); + + jobjectArray retArray; + jbyteArray outArray, intsByteArray; + unsigned char intsarray[1]; + secp256k1_pubkey pubkey; + unsigned char nonce_res[32]; + size_t outputLen = 32; + + int ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pubdata, publen); + + if (ret) { + ret = secp256k1_ecdh( + ctx, + nonce_res, + &pubkey, + secdata + ); + } + + intsarray[0] = ret; + + retArray = (*env)->NewObjectArray(env, 2, + (*env)->FindClass(env, "[B"), + (*env)->NewByteArray(env, 1)); + + outArray = (*env)->NewByteArray(env, outputLen); + (*env)->SetByteArrayRegion(env, outArray, 0, 32, (jbyte*)nonce_res); + (*env)->SetObjectArrayElement(env, retArray, 0, outArray); + + intsByteArray = (*env)->NewByteArray(env, 1); + (*env)->SetByteArrayRegion(env, intsByteArray, 0, 1, (jbyte*)intsarray); + (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray); + + (void)classObject; + + return retArray; } diff --git a/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.h b/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.h index d7fb004fa841..4125a1f5233b 100644 --- a/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.h +++ b/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.h @@ -1,5 +1,6 @@ /* DO NOT EDIT THIS FILE - it is machine generated */ #include +#include "include/secp256k1.h" /* Header for class org_bitcoin_NativeSecp256k1 */ #ifndef _Included_org_bitcoin_NativeSecp256k1 @@ -7,13 +8,118 @@ #ifdef __cplusplus extern "C" { #endif +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_ctx_clone + * Signature: (J)J + */ +SECP256K1_API jlong JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ctx_1clone + (JNIEnv *, jclass, jlong); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_context_randomize + * Signature: (Ljava/nio/ByteBuffer;J)I + */ +SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1context_1randomize + (JNIEnv *, jclass, jobject, jlong); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_privkey_tweak_add + * Signature: (Ljava/nio/ByteBuffer;J)[[B + */ +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1privkey_1tweak_1add + (JNIEnv *, jclass, jobject, jlong); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_privkey_tweak_mul + * Signature: (Ljava/nio/ByteBuffer;J)[[B + */ +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1privkey_1tweak_1mul + (JNIEnv *, jclass, jobject, jlong); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_pubkey_tweak_add + * Signature: (Ljava/nio/ByteBuffer;JI)[[B + */ +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1pubkey_1tweak_1add + (JNIEnv *, jclass, jobject, jlong, jint); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_pubkey_tweak_mul + * Signature: (Ljava/nio/ByteBuffer;JI)[[B + */ +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1pubkey_1tweak_1mul + (JNIEnv *, jclass, jobject, jlong, jint); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_destroy_context + * Signature: (J)V + */ +SECP256K1_API void JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1destroy_1context + (JNIEnv *, jclass, jlong); + /* * Class: org_bitcoin_NativeSecp256k1 * Method: secp256k1_ecdsa_verify - * Signature: (Ljava/nio/ByteBuffer;)I + * Signature: (Ljava/nio/ByteBuffer;JII)I + */ +SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1verify + (JNIEnv *, jclass, jobject, jlong, jint, jint); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_ecdsa_sign + * Signature: (Ljava/nio/ByteBuffer;J)[[B + */ +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1sign + (JNIEnv *, jclass, jobject, jlong); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_ec_seckey_verify + * Signature: (Ljava/nio/ByteBuffer;J)I + */ +SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1seckey_1verify + (JNIEnv *, jclass, jobject, jlong); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_ec_pubkey_create + * Signature: (Ljava/nio/ByteBuffer;J)[[B */ -JNIEXPORT jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1verify - (JNIEnv *, jclass, jobject); +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1pubkey_1create + (JNIEnv *, jclass, jobject, jlong); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_ec_pubkey_parse + * Signature: (Ljava/nio/ByteBuffer;JI)[[B + */ +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1pubkey_1parse + (JNIEnv *, jclass, jobject, jlong, jint); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_schnorr_sign + * Signature: (Ljava/nio/ByteBuffer;JI)[[B + */ +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1schnorr_1sign + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_ecdh + * Signature: (Ljava/nio/ByteBuffer;JI)[[B + */ +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdh + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen); + #ifdef __cplusplus } diff --git a/src/secp256k1/src/java/org_bitcoin_Secp256k1Context.c b/src/secp256k1/src/java/org_bitcoin_Secp256k1Context.c new file mode 100644 index 000000000000..a52939e7e7da --- /dev/null +++ b/src/secp256k1/src/java/org_bitcoin_Secp256k1Context.c @@ -0,0 +1,15 @@ +#include +#include +#include "org_bitcoin_Secp256k1Context.h" +#include "include/secp256k1.h" + +SECP256K1_API jlong JNICALL Java_org_bitcoin_Secp256k1Context_secp256k1_1init_1context + (JNIEnv* env, jclass classObject) +{ + secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + + (void)classObject;(void)env; + + return (uintptr_t)ctx; +} + diff --git a/src/secp256k1/src/java/org_bitcoin_Secp256k1Context.h b/src/secp256k1/src/java/org_bitcoin_Secp256k1Context.h new file mode 100644 index 000000000000..0d2bc84b7f3f --- /dev/null +++ b/src/secp256k1/src/java/org_bitcoin_Secp256k1Context.h @@ -0,0 +1,22 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +#include "include/secp256k1.h" +/* Header for class org_bitcoin_Secp256k1Context */ + +#ifndef _Included_org_bitcoin_Secp256k1Context +#define _Included_org_bitcoin_Secp256k1Context +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: org_bitcoin_Secp256k1Context + * Method: secp256k1_init_context + * Signature: ()J + */ +SECP256K1_API jlong JNICALL Java_org_bitcoin_Secp256k1Context_secp256k1_1init_1context + (JNIEnv *, jclass); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/secp256k1/src/modules/ecdh/Makefile.am.include b/src/secp256k1/src/modules/ecdh/Makefile.am.include index 670b9c1152e7..e3088b469790 100644 --- a/src/secp256k1/src/modules/ecdh/Makefile.am.include +++ b/src/secp256k1/src/modules/ecdh/Makefile.am.include @@ -4,5 +4,5 @@ noinst_HEADERS += src/modules/ecdh/tests_impl.h if USE_BENCHMARK noinst_PROGRAMS += bench_ecdh bench_ecdh_SOURCES = src/bench_ecdh.c -bench_ecdh_LDADD = libsecp256k1.la $(SECP_LIBS) +bench_ecdh_LDADD = libsecp256k1.la $(SECP_LIBS) $(COMMON_LIB) endif diff --git a/src/secp256k1/src/modules/recovery/Makefile.am.include b/src/secp256k1/src/modules/recovery/Makefile.am.include index 5de3ea33ea5e..bf23c26e71c5 100644 --- a/src/secp256k1/src/modules/recovery/Makefile.am.include +++ b/src/secp256k1/src/modules/recovery/Makefile.am.include @@ -4,5 +4,5 @@ noinst_HEADERS += src/modules/recovery/tests_impl.h if USE_BENCHMARK noinst_PROGRAMS += bench_recover bench_recover_SOURCES = src/bench_recover.c -bench_recover_LDADD = libsecp256k1.la $(SECP_LIBS) +bench_recover_LDADD = libsecp256k1.la $(SECP_LIBS) $(COMMON_LIB) endif diff --git a/src/secp256k1/src/modules/schnorr/Makefile.am.include b/src/secp256k1/src/modules/schnorr/Makefile.am.include index b3bfa7d5cc49..f1af8e83255f 100644 --- a/src/secp256k1/src/modules/schnorr/Makefile.am.include +++ b/src/secp256k1/src/modules/schnorr/Makefile.am.include @@ -6,5 +6,5 @@ noinst_HEADERS += src/modules/schnorr/tests_impl.h if USE_BENCHMARK noinst_PROGRAMS += bench_schnorr_verify bench_schnorr_verify_SOURCES = src/bench_schnorr_verify.c -bench_schnorr_verify_LDADD = libsecp256k1.la $(SECP_LIBS) +bench_schnorr_verify_LDADD = libsecp256k1.la $(SECP_LIBS) $(COMMON_LIB) endif diff --git a/src/secp256k1/src/num.h b/src/secp256k1/src/num.h index ebfa71eb44bd..7bb9c5be8cf5 100644 --- a/src/secp256k1/src/num.h +++ b/src/secp256k1/src/num.h @@ -32,6 +32,9 @@ static void secp256k1_num_set_bin(secp256k1_num *r, const unsigned char *a, unsi /** Compute a modular inverse. The input must be less than the modulus. */ static void secp256k1_num_mod_inverse(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *m); +/** Compute the jacobi symbol (a|b). b must be positive and odd. */ +static int secp256k1_num_jacobi(const secp256k1_num *a, const secp256k1_num *b); + /** Compare the absolute value of two numbers. */ static int secp256k1_num_cmp(const secp256k1_num *a, const secp256k1_num *b); @@ -57,6 +60,9 @@ static void secp256k1_num_shift(secp256k1_num *r, int bits); /** Check whether a number is zero. */ static int secp256k1_num_is_zero(const secp256k1_num *a); +/** Check whether a number is one. */ +static int secp256k1_num_is_one(const secp256k1_num *a); + /** Check whether a number is strictly negative. */ static int secp256k1_num_is_neg(const secp256k1_num *a); diff --git a/src/secp256k1/src/num_gmp_impl.h b/src/secp256k1/src/num_gmp_impl.h index 7b6a89719ab1..3a46495eeac7 100644 --- a/src/secp256k1/src/num_gmp_impl.h +++ b/src/secp256k1/src/num_gmp_impl.h @@ -144,6 +144,32 @@ static void secp256k1_num_mod_inverse(secp256k1_num *r, const secp256k1_num *a, memset(v, 0, sizeof(v)); } +static int secp256k1_num_jacobi(const secp256k1_num *a, const secp256k1_num *b) { + int ret; + mpz_t ga, gb; + secp256k1_num_sanity(a); + secp256k1_num_sanity(b); + VERIFY_CHECK(!b->neg && (b->limbs > 0) && (b->data[0] & 1)); + + mpz_inits(ga, gb, NULL); + + mpz_import(gb, b->limbs, -1, sizeof(mp_limb_t), 0, 0, b->data); + mpz_import(ga, a->limbs, -1, sizeof(mp_limb_t), 0, 0, a->data); + if (a->neg) { + mpz_neg(ga, ga); + } + + ret = mpz_jacobi(ga, gb); + + mpz_clears(ga, gb, NULL); + + return ret; +} + +static int secp256k1_num_is_one(const secp256k1_num *a) { + return (a->limbs == 1 && a->data[0] == 1); +} + static int secp256k1_num_is_zero(const secp256k1_num *a) { return (a->limbs == 1 && a->data[0] == 0); } diff --git a/src/secp256k1/src/scalar_impl.h b/src/secp256k1/src/scalar_impl.h index 88ea97de863e..c5baf4df413e 100644 --- a/src/secp256k1/src/scalar_impl.h +++ b/src/secp256k1/src/scalar_impl.h @@ -7,8 +7,6 @@ #ifndef _SECP256K1_SCALAR_IMPL_H_ #define _SECP256K1_SCALAR_IMPL_H_ -#include - #include "group.h" #include "scalar.h" diff --git a/src/secp256k1/src/secp256k1.c b/src/secp256k1/src/secp256k1.c index 62d192baeb38..7973d60c36a8 100644 --- a/src/secp256k1/src/secp256k1.c +++ b/src/secp256k1/src/secp256k1.c @@ -4,8 +4,6 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#define SECP256K1_BUILD (1) - #include "include/secp256k1.h" #include "util.h" @@ -152,7 +150,6 @@ static void secp256k1_pubkey_save(secp256k1_pubkey* pubkey, secp256k1_ge* ge) { int secp256k1_ec_pubkey_parse(const secp256k1_context* ctx, secp256k1_pubkey* pubkey, const unsigned char *input, size_t inputlen) { secp256k1_ge Q; - (void)ctx; VERIFY_CHECK(ctx != NULL); ARG_CHECK(pubkey != NULL); memset(pubkey, 0, sizeof(*pubkey)); @@ -170,7 +167,6 @@ int secp256k1_ec_pubkey_serialize(const secp256k1_context* ctx, unsigned char *o size_t len; int ret = 0; - (void)ctx; VERIFY_CHECK(ctx != NULL); ARG_CHECK(outputlen != NULL); ARG_CHECK(*outputlen >= ((flags & SECP256K1_FLAGS_BIT_COMPRESSION) ? 33 : 65)); @@ -216,7 +212,7 @@ static void secp256k1_ecdsa_signature_save(secp256k1_ecdsa_signature* sig, const int secp256k1_ecdsa_signature_parse_der(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char *input, size_t inputlen) { secp256k1_scalar r, s; - (void)ctx; + VERIFY_CHECK(ctx != NULL); ARG_CHECK(sig != NULL); ARG_CHECK(input != NULL); @@ -234,7 +230,7 @@ int secp256k1_ecdsa_signature_parse_compact(const secp256k1_context* ctx, secp25 int ret = 1; int overflow = 0; - (void)ctx; + VERIFY_CHECK(ctx != NULL); ARG_CHECK(sig != NULL); ARG_CHECK(input64 != NULL); @@ -253,7 +249,7 @@ int secp256k1_ecdsa_signature_parse_compact(const secp256k1_context* ctx, secp25 int secp256k1_ecdsa_signature_serialize_der(const secp256k1_context* ctx, unsigned char *output, size_t *outputlen, const secp256k1_ecdsa_signature* sig) { secp256k1_scalar r, s; - (void)ctx; + VERIFY_CHECK(ctx != NULL); ARG_CHECK(output != NULL); ARG_CHECK(outputlen != NULL); ARG_CHECK(sig != NULL); @@ -265,7 +261,7 @@ int secp256k1_ecdsa_signature_serialize_der(const secp256k1_context* ctx, unsign int secp256k1_ecdsa_signature_serialize_compact(const secp256k1_context* ctx, unsigned char *output64, const secp256k1_ecdsa_signature* sig) { secp256k1_scalar r, s; - (void)ctx; + VERIFY_CHECK(ctx != NULL); ARG_CHECK(output64 != NULL); ARG_CHECK(sig != NULL); @@ -398,7 +394,6 @@ int secp256k1_ec_seckey_verify(const secp256k1_context* ctx, const unsigned char int overflow; VERIFY_CHECK(ctx != NULL); ARG_CHECK(seckey != NULL); - (void)ctx; secp256k1_scalar_set_b32(&sec, seckey, &overflow); ret = !overflow && !secp256k1_scalar_is_zero(&sec); @@ -437,7 +432,6 @@ int secp256k1_ec_privkey_tweak_add(const secp256k1_context* ctx, unsigned char * VERIFY_CHECK(ctx != NULL); ARG_CHECK(seckey != NULL); ARG_CHECK(tweak != NULL); - (void)ctx; secp256k1_scalar_set_b32(&term, tweak, &overflow); secp256k1_scalar_set_b32(&sec, seckey, NULL); @@ -485,7 +479,6 @@ int secp256k1_ec_privkey_tweak_mul(const secp256k1_context* ctx, unsigned char * VERIFY_CHECK(ctx != NULL); ARG_CHECK(seckey != NULL); ARG_CHECK(tweak != NULL); - (void)ctx; secp256k1_scalar_set_b32(&factor, tweak, &overflow); secp256k1_scalar_set_b32(&sec, seckey, NULL); diff --git a/src/secp256k1/src/tests.c b/src/secp256k1/src/tests.c index 687a5f2fdd80..b32cb9081371 100644 --- a/src/secp256k1/src/tests.c +++ b/src/secp256k1/src/tests.c @@ -473,6 +473,8 @@ void test_num_negate(void) { } void test_num_add_sub(void) { + int i; + secp256k1_scalar s; secp256k1_num n1; secp256k1_num n2; secp256k1_num n1p2, n2p1, n1m2, n2m1; @@ -498,6 +500,110 @@ void test_num_add_sub(void) { CHECK(!secp256k1_num_eq(&n2p1, &n1)); secp256k1_num_sub(&n2p1, &n2p1, &n2); /* n2p1 = R2 + R1 - R2 = R1 */ CHECK(secp256k1_num_eq(&n2p1, &n1)); + + /* check is_one */ + secp256k1_scalar_set_int(&s, 1); + secp256k1_scalar_get_num(&n1, &s); + CHECK(secp256k1_num_is_one(&n1)); + /* check that 2^n + 1 is never 1 */ + secp256k1_scalar_get_num(&n2, &s); + for (i = 0; i < 250; ++i) { + secp256k1_num_add(&n1, &n1, &n1); /* n1 *= 2 */ + secp256k1_num_add(&n1p2, &n1, &n2); /* n1p2 = n1 + 1 */ + CHECK(!secp256k1_num_is_one(&n1p2)); + } +} + +void test_num_mod(void) { + int i; + secp256k1_scalar s; + secp256k1_num order, n; + + /* check that 0 mod anything is 0 */ + random_scalar_order_test(&s); + secp256k1_scalar_get_num(&order, &s); + secp256k1_scalar_set_int(&s, 0); + secp256k1_scalar_get_num(&n, &s); + secp256k1_num_mod(&n, &order); + CHECK(secp256k1_num_is_zero(&n)); + + /* check that anything mod 1 is 0 */ + secp256k1_scalar_set_int(&s, 1); + secp256k1_scalar_get_num(&order, &s); + secp256k1_scalar_get_num(&n, &s); + secp256k1_num_mod(&n, &order); + CHECK(secp256k1_num_is_zero(&n)); + + /* check that increasing the number past 2^256 does not break this */ + random_scalar_order_test(&s); + secp256k1_scalar_get_num(&n, &s); + /* multiply by 2^8, which'll test this case with high probability */ + for (i = 0; i < 8; ++i) { + secp256k1_num_add(&n, &n, &n); + } + secp256k1_num_mod(&n, &order); + CHECK(secp256k1_num_is_zero(&n)); +} + +void test_num_jacobi(void) { + secp256k1_scalar sqr; + secp256k1_scalar small; + secp256k1_scalar five; /* five is not a quadratic residue */ + secp256k1_num order, n; + int i; + /* squares mod 5 are 1, 4 */ + const int jacobi5[10] = { 0, 1, -1, -1, 1, 0, 1, -1, -1, 1 }; + + /* check some small values with 5 as the order */ + secp256k1_scalar_set_int(&five, 5); + secp256k1_scalar_get_num(&order, &five); + for (i = 0; i < 10; ++i) { + secp256k1_scalar_set_int(&small, i); + secp256k1_scalar_get_num(&n, &small); + CHECK(secp256k1_num_jacobi(&n, &order) == jacobi5[i]); + } + + /** test large values with 5 as group order */ + secp256k1_scalar_get_num(&order, &five); + /* we first need a scalar which is not a multiple of 5 */ + do { + secp256k1_num fiven; + random_scalar_order_test(&sqr); + secp256k1_scalar_get_num(&fiven, &five); + secp256k1_scalar_get_num(&n, &sqr); + secp256k1_num_mod(&n, &fiven); + } while (secp256k1_num_is_zero(&n)); + /* next force it to be a residue. 2 is a nonresidue mod 5 so we can + * just multiply by two, i.e. add the number to itself */ + if (secp256k1_num_jacobi(&n, &order) == -1) { + secp256k1_num_add(&n, &n, &n); + } + + /* test residue */ + CHECK(secp256k1_num_jacobi(&n, &order) == 1); + /* test nonresidue */ + secp256k1_num_add(&n, &n, &n); + CHECK(secp256k1_num_jacobi(&n, &order) == -1); + + /** test with secp group order as order */ + secp256k1_scalar_order_get_num(&order); + random_scalar_order_test(&sqr); + secp256k1_scalar_sqr(&sqr, &sqr); + /* test residue */ + secp256k1_scalar_get_num(&n, &sqr); + CHECK(secp256k1_num_jacobi(&n, &order) == 1); + /* test nonresidue */ + secp256k1_scalar_mul(&sqr, &sqr, &five); + secp256k1_scalar_get_num(&n, &sqr); + CHECK(secp256k1_num_jacobi(&n, &order) == -1); + /* test multiple of the order*/ + CHECK(secp256k1_num_jacobi(&order, &order) == 0); + + /* check one less than the order */ + secp256k1_scalar_set_int(&small, 1); + secp256k1_scalar_get_num(&n, &small); + secp256k1_num_sub(&n, &order, &n); + CHECK(secp256k1_num_jacobi(&n, &order) == 1); /* sage confirms this is 1 */ } void run_num_smalltests(void) { @@ -505,6 +611,8 @@ void run_num_smalltests(void) { for (i = 0; i < 100*count; i++) { test_num_negate(); test_num_add_sub(); + test_num_mod(); + test_num_jacobi(); } } #endif @@ -689,6 +797,10 @@ void scalar_test(void) { secp256k1_scalar_inverse(&inv, &inv); /* Inverting one must result in one. */ CHECK(secp256k1_scalar_is_one(&inv)); +#ifndef USE_NUM_NONE + secp256k1_scalar_get_num(&invnum, &inv); + CHECK(secp256k1_num_is_one(&invnum)); +#endif } } @@ -855,7 +967,7 @@ void run_scalar_tests(void) { secp256k1_scalar zzv; #endif int overflow; - unsigned char chal[32][2][32] = { + unsigned char chal[33][2][32] = { {{0xff, 0xff, 0x03, 0x07, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, @@ -1111,9 +1223,17 @@ void run_scalar_tests(void) { {0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x00, 0xf8, 0x07, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xc7, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xff}} + 0xff, 0xc7, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xff}}, + {{0x82, 0xc9, 0xfa, 0xb0, 0x68, 0x04, 0xa0, 0x00, + 0x82, 0xc9, 0xfa, 0xb0, 0x68, 0x04, 0xa0, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0x03, 0xfb, + 0xfa, 0x8a, 0x7d, 0xdf, 0x13, 0x86, 0xe2, 0x03}, + {0x82, 0xc9, 0xfa, 0xb0, 0x68, 0x04, 0xa0, 0x00, + 0x82, 0xc9, 0xfa, 0xb0, 0x68, 0x04, 0xa0, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0x03, 0xfb, + 0xfa, 0x8a, 0x7d, 0xdf, 0x13, 0x86, 0xe2, 0x03}} }; - unsigned char res[32][2][32] = { + unsigned char res[33][2][32] = { {{0x0c, 0x3b, 0x0a, 0xca, 0x8d, 0x1a, 0x2f, 0xb9, 0x8a, 0x7b, 0x53, 0x5a, 0x1f, 0xc5, 0x22, 0xa1, 0x07, 0x2a, 0x48, 0xea, 0x02, 0xeb, 0xb3, 0xd6, @@ -1369,10 +1489,18 @@ void run_scalar_tests(void) { {0xe4, 0xf1, 0x23, 0x84, 0xe1, 0xb5, 0x9d, 0xf2, 0xb8, 0x73, 0x8b, 0x45, 0x2b, 0x35, 0x46, 0x38, 0x10, 0x2b, 0x50, 0xf8, 0x8b, 0x35, 0xcd, 0x34, - 0xc8, 0x0e, 0xf6, 0xdb, 0x09, 0x35, 0xf0, 0xda}} + 0xc8, 0x0e, 0xf6, 0xdb, 0x09, 0x35, 0xf0, 0xda}}, + {{0xdb, 0x21, 0x5c, 0x8d, 0x83, 0x1d, 0xb3, 0x34, + 0xc7, 0x0e, 0x43, 0xa1, 0x58, 0x79, 0x67, 0x13, + 0x1e, 0x86, 0x5d, 0x89, 0x63, 0xe6, 0x0a, 0x46, + 0x5c, 0x02, 0x97, 0x1b, 0x62, 0x43, 0x86, 0xf5}, + {0xdb, 0x21, 0x5c, 0x8d, 0x83, 0x1d, 0xb3, 0x34, + 0xc7, 0x0e, 0x43, 0xa1, 0x58, 0x79, 0x67, 0x13, + 0x1e, 0x86, 0x5d, 0x89, 0x63, 0xe6, 0x0a, 0x46, + 0x5c, 0x02, 0x97, 0x1b, 0x62, 0x43, 0x86, 0xf5}} }; secp256k1_scalar_set_int(&one, 1); - for (i = 0; i < 32; i++) { + for (i = 0; i < 33; i++) { secp256k1_scalar_set_b32(&x, chal[i][0], &overflow); CHECK(!overflow); secp256k1_scalar_set_b32(&y, chal[i][1], &overflow); @@ -1446,7 +1574,7 @@ void random_fe_non_zero(secp256k1_fe *nz) { void random_fe_non_square(secp256k1_fe *ns) { secp256k1_fe r; random_fe_non_zero(ns); - if (secp256k1_fe_sqrt_var(&r, ns)) { + if (secp256k1_fe_sqrt(&r, ns)) { secp256k1_fe_negate(ns, ns, 1); } } @@ -1641,7 +1769,7 @@ void run_sqr(void) { void test_sqrt(const secp256k1_fe *a, const secp256k1_fe *k) { secp256k1_fe r1, r2; - int v = secp256k1_fe_sqrt_var(&r1, a); + int v = secp256k1_fe_sqrt(&r1, a); CHECK((v == 0) == (k == NULL)); if (k != NULL) { @@ -1951,8 +2079,8 @@ void test_add_neg_y_diff_x(void) { * of the sum to be wrong (since infinity has no xy coordinates). * HOWEVER, if the x-coordinates are different, infinity is the * wrong answer, and such degeneracies are exposed. This is the - * root of https://github.com/bitcoin/secp256k1/issues/257 which - * this test is a regression test for. + * root of https://github.com/bitcoin-core/secp256k1/issues/257 + * which this test is a regression test for. * * These points were generated in sage as * # secp256k1 params @@ -2051,15 +2179,16 @@ void run_ec_combine(void) { void test_group_decompress(const secp256k1_fe* x) { /* The input itself, normalized. */ secp256k1_fe fex = *x; - secp256k1_fe tmp; + secp256k1_fe fez; /* Results of set_xquad_var, set_xo_var(..., 0), set_xo_var(..., 1). */ secp256k1_ge ge_quad, ge_even, ge_odd; + secp256k1_gej gej_quad; /* Return values of the above calls. */ int res_quad, res_even, res_odd; secp256k1_fe_normalize_var(&fex); - res_quad = secp256k1_ge_set_xquad_var(&ge_quad, &fex); + res_quad = secp256k1_ge_set_xquad(&ge_quad, &fex); res_even = secp256k1_ge_set_xo_var(&ge_even, &fex, 0); res_odd = secp256k1_ge_set_xo_var(&ge_odd, &fex, 1); @@ -2085,13 +2214,29 @@ void test_group_decompress(const secp256k1_fe* x) { CHECK(secp256k1_fe_equal_var(&ge_odd.x, x)); /* Check that the Y coordinate result in ge_quad is a square. */ - CHECK(secp256k1_fe_sqrt_var(&tmp, &ge_quad.y)); - secp256k1_fe_sqr(&tmp, &tmp); - CHECK(secp256k1_fe_equal_var(&tmp, &ge_quad.y)); + CHECK(secp256k1_fe_is_quad_var(&ge_quad.y)); /* Check odd/even Y in ge_odd, ge_even. */ CHECK(secp256k1_fe_is_odd(&ge_odd.y)); CHECK(!secp256k1_fe_is_odd(&ge_even.y)); + + /* Check secp256k1_gej_has_quad_y_var. */ + secp256k1_gej_set_ge(&gej_quad, &ge_quad); + CHECK(secp256k1_gej_has_quad_y_var(&gej_quad)); + do { + random_fe_test(&fez); + } while (secp256k1_fe_is_zero(&fez)); + secp256k1_gej_rescale(&gej_quad, &fez); + CHECK(secp256k1_gej_has_quad_y_var(&gej_quad)); + secp256k1_gej_neg(&gej_quad, &gej_quad); + CHECK(!secp256k1_gej_has_quad_y_var(&gej_quad)); + do { + random_fe_test(&fez); + } while (secp256k1_fe_is_zero(&fez)); + secp256k1_gej_rescale(&gej_quad, &fez); + CHECK(!secp256k1_gej_has_quad_y_var(&gej_quad)); + secp256k1_gej_neg(&gej_quad, &gej_quad); + CHECK(secp256k1_gej_has_quad_y_var(&gej_quad)); } } @@ -2383,9 +2528,7 @@ void test_constant_wnaf(const secp256k1_scalar *number, int w) { secp256k1_scalar x, shift; int wnaf[256] = {0}; int i; -#ifdef USE_ENDOMORPHISM int skew; -#endif secp256k1_scalar num = *number; secp256k1_scalar_set_int(&x, 0); @@ -2395,10 +2538,8 @@ void test_constant_wnaf(const secp256k1_scalar *number, int w) { for (i = 0; i < 16; ++i) { secp256k1_scalar_shr_int(&num, 8); } - skew = secp256k1_wnaf_const(wnaf, num, w); -#else - secp256k1_wnaf_const(wnaf, num, w); #endif + skew = secp256k1_wnaf_const(wnaf, num, w); for (i = WNAF_SIZE(w); i >= 0; --i) { secp256k1_scalar t; @@ -2417,10 +2558,8 @@ void test_constant_wnaf(const secp256k1_scalar *number, int w) { } secp256k1_scalar_add(&x, &x, &t); } -#ifdef USE_ENDOMORPHISM - /* Skew num because when encoding 128-bit numbers as odd we use an offset */ + /* Skew num because when encoding numbers as odd we use an offset */ secp256k1_scalar_cadd_bit(&num, skew == 2, 1); -#endif CHECK(secp256k1_scalar_eq(&x, &num)); } @@ -3484,12 +3623,14 @@ void run_ecdsa_end_to_end(void) { int test_ecdsa_der_parse(const unsigned char *sig, size_t siglen, int certainly_der, int certainly_not_der) { static const unsigned char zeroes[32] = {0}; +#ifdef ENABLE_OPENSSL_TESTS static const unsigned char max_scalar[32] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x40 }; +#endif int ret = 0; @@ -3607,13 +3748,13 @@ static void assign_big_endian(unsigned char *ptr, size_t ptrlen, uint32_t val) { static void damage_array(unsigned char *sig, size_t *len) { int pos; int action = secp256k1_rand_bits(3); - if (action < 1) { + if (action < 1 && *len > 3) { /* Delete a byte. */ pos = secp256k1_rand_int(*len); memmove(sig + pos, sig + pos + 1, *len - pos - 1); (*len)--; return; - } else if (action < 2) { + } else if (action < 2 && *len < 2048) { /* Insert a byte. */ pos = secp256k1_rand_int(1 + *len); memmove(sig + pos + 1, sig + pos, *len - pos); @@ -3785,6 +3926,7 @@ void run_ecdsa_der_parse(void) { int certainly_der = 0; int certainly_not_der = 0; random_ber_signature(buffer, &buflen, &certainly_der, &certainly_not_der); + CHECK(buflen <= 2048); for (j = 0; j < 16; j++) { int ret = 0; if (j > 0) { diff --git a/src/test/README.md b/src/test/README.md index c1fdc17c8e6b..9cb765107141 100644 --- a/src/test/README.md +++ b/src/test/README.md @@ -5,18 +5,15 @@ sense to simply use this framework rather than require developers to configure some other framework (we want as few impediments to creating unit tests as possible). -The build system is setup to compile an executable called "test_dash" +The build system is setup to compile an executable called `test_dash` that runs all of the unit tests. The main source file is called -test_dash.cpp, which simply includes other files that contain the -actual unit tests (outside of a couple required preprocessor -directives). The pattern is to create one test file for each class or -source file for which you want to create unit tests. The file naming -convention is "_tests.cpp" and such files should wrap -their tests in a test suite called "_tests". For an -examples of this pattern, examine uint160_tests.cpp and -uint256_tests.cpp. - -Add the source files to /src/Makefile.test.include to add them to the build. +test_dash.cpp. To add a new unit test file to our test suite you need +to add the file to `src/Makefile.test.include`. The pattern is to create +one test file for each class or source file for which you want to create +unit tests. The file naming convention is `_tests.cpp` +and such files should wrap their tests in a test suite +called `_tests`. For an example of this pattern, +examine `uint256_tests.cpp`. For further reading, I found the following website to be helpful in explaining how the boost unit test framework works: @@ -31,5 +28,5 @@ example, to run just the getarg_tests verbosely: test_dash --run_test=getarg_tests/doubledash -Run test_dash --help for the full list. +Run `test_dash --help` for the full list. diff --git a/src/test/bitcoin-util-test.py b/src/test/bitcoin-util-test.py index 95dd3e81b436..882b5c67b862 100755 --- a/src/test/bitcoin-util-test.py +++ b/src/test/bitcoin-util-test.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # Copyright 2014 BitPay, Inc. # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/test/buildenv.py.in b/src/test/buildenv.py.in index 1618bdeb764b..153f34a3dbc7 100644 --- a/src/test/buildenv.py.in +++ b/src/test/buildenv.py.in @@ -1,2 +1,2 @@ -#!/usr/bin/python +#!/usr/bin/env python exeext="@EXEEXT@" diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 5c81db25718b..eed13a001c13 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -263,12 +263,12 @@ class TestBuilder } public: - TestBuilder(const CScript& redeemScript, const std::string& comment_, int flags_, bool P2SH = false) : scriptPubKey(redeemScript), havePush(false), comment(comment_), flags(flags_), scriptError(SCRIPT_ERR_OK) + TestBuilder(const CScript& script_, const std::string& comment_, int flags_, bool P2SH = false) : scriptPubKey(script_), havePush(false), comment(comment_), flags(flags_), scriptError(SCRIPT_ERR_OK) { if (P2SH) { - creditTx = BuildCreditingTransaction(CScript() << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL); + creditTx = BuildCreditingTransaction(CScript() << OP_HASH160 << ToByteVector(CScriptID(script_)) << OP_EQUAL); } else { - creditTx = BuildCreditingTransaction(redeemScript); + creditTx = BuildCreditingTransaction(script_); } spendTx = BuildSpendingTransaction(CScript(), creditTx); } @@ -299,6 +299,11 @@ class TestBuilder return *this; } + TestBuilder& Push(const CScript& script) { + DoPush(std::vector(script.begin(), script.end())); + return *this; + } + TestBuilder& PushSig(const CKey& key, int nHashType = SIGHASH_ALL, unsigned int lenR = 32, unsigned int lenS = 32) { uint256 hash = SignatureHash(scriptPubKey, spendTx, 0, nHashType); diff --git a/src/test/test_dash.cpp b/src/test/test_dash.cpp index 6cca19305ef3..e2f844939660 100644 --- a/src/test/test_dash.cpp +++ b/src/test/test_dash.cpp @@ -64,6 +64,11 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha pcoinsdbview = new CCoinsViewDB(1 << 23, true); pcoinsTip = new CCoinsViewCache(pcoinsdbview); InitBlockIndex(chainparams); + { + CValidationState state; + bool ok = ActivateBestChain(state, chainparams); + BOOST_CHECK(ok); + } nScriptCheckThreads = 3; for (int i=0; i < nScriptCheckThreads-1; i++) threadGroup.create_thread(&ThreadScriptCheck); diff --git a/src/tinyformat.h b/src/tinyformat.h index c6ec0419b394..17f0360c42f2 100644 --- a/src/tinyformat.h +++ b/src/tinyformat.h @@ -67,7 +67,9 @@ // weekday, month, day, hour, min); // std::cout << date; // -// These are the three primary interface functions. +// These are the three primary interface functions. There is also a +// convenience function printfln() which appends a newline to the usual result +// of printf() for super simple logging. // // // User defined format functions @@ -86,6 +88,18 @@ // defined function bodies, use the macro TINYFORMAT_FOREACH_ARGNUM. For an // example, see the implementation of printf() at the end of the source file. // +// Sometimes it's useful to be able to pass a list of format arguments through +// to a non-template function. The FormatList class is provided as a way to do +// this by storing the argument list in a type-opaque way. Continuing the +// example from above, we construct a FormatList using makeFormatList(): +// +// FormatListRef formatList = tfm::makeFormatList(weekday, month, day, hour, min); +// +// The format list can now be passed into any non-template function and used +// via a call to the vformat() function: +// +// tfm::vformat(std::cout, "%s, %s %d, %.2d:%.2d\n", formatList); +// // // Additional API information // -------------------------- @@ -118,6 +132,7 @@ namespace tfm = tinyformat; //------------------------------------------------------------------------------ // Implementation details. +#include #include #include #include @@ -133,20 +148,20 @@ namespace tfm = tinyformat; # endif #endif -#ifdef __GNUC__ -# define TINYFORMAT_NOINLINE __attribute__((noinline)) -#elif defined(_MSC_VER) -# define TINYFORMAT_NOINLINE __declspec(noinline) -#else -# define TINYFORMAT_NOINLINE -#endif - #if defined(__GLIBCXX__) && __GLIBCXX__ < 20080201 // std::showpos is broken on old libstdc++ as provided with OSX. See // http://gcc.gnu.org/ml/libstdc++/2007-11/msg00075.html # define TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND #endif +#ifdef __APPLE__ +// Workaround OSX linker warning: xcode uses different default symbol +// visibilities for static libs vs executables (see issue #25) +# define TINYFORMAT_HIDDEN __attribute__((visibility("hidden"))) +#else +# define TINYFORMAT_HIDDEN +#endif + namespace tinyformat { //------------------------------------------------------------------------------ @@ -247,6 +262,29 @@ struct convertToInt static int invoke(const T& value) { return static_cast(value); } }; +// Format at most ntrunc characters to the given stream. +template +inline void formatTruncated(std::ostream& out, const T& value, int ntrunc) +{ + std::ostringstream tmp; + tmp << value; + std::string result = tmp.str(); + out.write(result.c_str(), (std::min)(ntrunc, static_cast(result.size()))); +} +#define TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR(type) \ +inline void formatTruncated(std::ostream& out, type* value, int ntrunc) \ +{ \ + std::streamsize len = 0; \ + while(len < ntrunc && value[len] != 0) \ + ++len; \ + out.write(value, len); \ +} +// Overload for const char* and char*. Could overload for signed & unsigned +// char too, but these are technically unneeded for printf compatibility. +TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR(const char) +TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR(char) +#undef TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR + } // namespace detail @@ -255,18 +293,20 @@ struct convertToInt // desired. -// Format a value into a stream. Called from format() for all types by default. -// -// Users may override this for their own types. When this function is called, -// the stream flags will have been modified according to the format string. -// The format specification is provided in the range [fmtBegin, fmtEnd). -// -// By default, formatValue() uses the usual stream insertion operator -// operator<< to format the type T, with special cases for the %c and %p -// conversions. +/// Format a value into a stream, delegating to operator<< by default. +/// +/// Users may override this for their own types. When this function is called, +/// the stream flags will have been modified according to the format string. +/// The format specification is provided in the range [fmtBegin, fmtEnd). For +/// truncating conversions, ntrunc is set to the desired maximum number of +/// characters, for example "%.7s" calls formatValue with ntrunc = 7. +/// +/// By default, formatValue() uses the usual stream insertion operator +/// operator<< to format the type T, with special cases for the %c and %p +/// conversions. template inline void formatValue(std::ostream& out, const char* /*fmtBegin*/, - const char* fmtEnd, const T& value) + const char* fmtEnd, int ntrunc, const T& value) { #ifndef TINYFORMAT_ALLOW_WCHAR_STRINGS // Since we don't support printing of wchar_t using "%ls", make it fail at @@ -288,6 +328,12 @@ inline void formatValue(std::ostream& out, const char* /*fmtBegin*/, #ifdef TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND else if(detail::formatZeroIntegerWorkaround::invoke(out, value)) /**/; #endif + else if(ntrunc >= 0) + { + // Take care not to overread C strings in truncating conversions like + // "%.4s" where at most 4 characters may be read. + detail::formatTruncated(out, value, ntrunc); + } else out << value; } @@ -296,7 +342,7 @@ inline void formatValue(std::ostream& out, const char* /*fmtBegin*/, // Overloaded version for char types to support printing as an integer #define TINYFORMAT_DEFINE_FORMATVALUE_CHAR(charType) \ inline void formatValue(std::ostream& out, const char* /*fmtBegin*/, \ - const char* fmtEnd, charType value) \ + const char* fmtEnd, int /**/, charType value) \ { \ switch(*(fmtEnd-1)) \ { \ @@ -435,225 +481,91 @@ cog.outl('#define TINYFORMAT_FOREACH_ARGNUM(m) \\\n ' + namespace detail { -// Class holding current position in format string and an output stream into -// which arguments are formatted. -class FormatIterator +// Type-opaque holder for an argument to format(), with associated actions on +// the type held as explicit function pointers. This allows FormatArg's for +// each argument to be allocated as a homogenous array inside FormatList +// whereas a naive implementation based on inheritance does not. +class FormatArg { public: - // Flags for features not representable with standard stream state - enum ExtraFormatFlags - { - Flag_None = 0, - Flag_TruncateToPrecision = 1<<0, // truncate length to stream precision() - Flag_SpacePadPositive = 1<<1, // pad positive values with spaces - Flag_VariableWidth = 1<<2, // variable field width in arg list - Flag_VariablePrecision = 1<<3 // variable field precision in arg list - }; - - // out is the output stream, fmt is the full format string - FormatIterator(std::ostream& out, const char* fmt) - : m_out(out), - m_fmt(fmt), - m_extraFlags(Flag_None), - m_wantWidth(false), - m_wantPrecision(false), - m_variableWidth(0), - m_variablePrecision(0), - m_origWidth(out.width()), - m_origPrecision(out.precision()), - m_origFlags(out.flags()), - m_origFill(out.fill()) + FormatArg() {} + + template + FormatArg(const T& value) + : m_value(static_cast(&value)), + m_formatImpl(&formatImpl), + m_toIntImpl(&toIntImpl) { } - // Print remaining part of format string. - void finish() + void format(std::ostream& out, const char* fmtBegin, + const char* fmtEnd, int ntrunc) const { - // It would be nice if we could do this from the destructor, but we - // can't if TINFORMAT_ERROR is used to throw an exception! - m_fmt = printFormatStringLiteral(m_out, m_fmt); - if(*m_fmt != '\0') - TINYFORMAT_ERROR("tinyformat: Too many conversion specifiers in format string"); + m_formatImpl(out, fmtBegin, fmtEnd, ntrunc, m_value); } - ~FormatIterator() + int toInt() const { - // Restore stream state - m_out.width(m_origWidth); - m_out.precision(m_origPrecision); - m_out.flags(m_origFlags); - m_out.fill(m_origFill); + return m_toIntImpl(m_value); } - template - void accept(const T& value); - private: - // Parse and return an integer from the string c, as atoi() - // On return, c is set to one past the end of the integer. - static int parseIntAndAdvance(const char*& c) + template + TINYFORMAT_HIDDEN static void formatImpl(std::ostream& out, const char* fmtBegin, + const char* fmtEnd, int ntrunc, const void* value) { - int i = 0; - for(;*c >= '0' && *c <= '9'; ++c) - i = 10*i + (*c - '0'); - return i; + formatValue(out, fmtBegin, fmtEnd, ntrunc, *static_cast(value)); } - // Format at most truncLen characters of a C string to the given - // stream. Return true if formatting proceeded (generic version always - // returns false) template - static bool formatCStringTruncate(std::ostream& /*out*/, const T& /*value*/, - std::streamsize /*truncLen*/) + TINYFORMAT_HIDDEN static int toIntImpl(const void* value) { - return false; - } -# define TINYFORMAT_DEFINE_FORMAT_C_STRING_TRUNCATE(type) \ - static bool formatCStringTruncate(std::ostream& out, type* value, \ - std::streamsize truncLen) \ - { \ - std::streamsize len = 0; \ - while(len < truncLen && value[len] != 0) \ - ++len; \ - out.write(value, len); \ - return true; \ - } - // Overload for const char* and char*. Could overload for signed & - // unsigned char too, but these are technically unneeded for printf - // compatibility. - TINYFORMAT_DEFINE_FORMAT_C_STRING_TRUNCATE(const char) - TINYFORMAT_DEFINE_FORMAT_C_STRING_TRUNCATE(char) -# undef TINYFORMAT_DEFINE_FORMAT_C_STRING_TRUNCATE - - // Print literal part of format string and return next format spec - // position. - // - // Skips over any occurrences of '%%', printing a literal '%' to the - // output. The position of the first % character of the next - // nontrivial format spec is returned, or the end of string. - static const char* printFormatStringLiteral(std::ostream& out, - const char* fmt) - { - const char* c = fmt; - for(; true; ++c) - { - switch(*c) - { - case '\0': - out.write(fmt, static_cast(c - fmt)); - return c; - case '%': - out.write(fmt, static_cast(c - fmt)); - if(*(c+1) != '%') - return c; - // for "%%", tack trailing % onto next literal section. - fmt = ++c; - break; - } - } + return convertToInt::invoke(*static_cast(value)); } - static const char* streamStateFromFormat(std::ostream& out, - unsigned int& extraFlags, - const char* fmtStart, - int variableWidth, - int variablePrecision); - - // Private copy & assign: Kill gcc warnings with -Weffc++ - FormatIterator(const FormatIterator&); - FormatIterator& operator=(const FormatIterator&); - - // Stream, current format string & state - std::ostream& m_out; - const char* m_fmt; - unsigned int m_extraFlags; - // State machine info for handling of variable width & precision - bool m_wantWidth; - bool m_wantPrecision; - int m_variableWidth; - int m_variablePrecision; - // Saved stream state - std::streamsize m_origWidth; - std::streamsize m_origPrecision; - std::ios::fmtflags m_origFlags; - char m_origFill; + const void* m_value; + void (*m_formatImpl)(std::ostream& out, const char* fmtBegin, + const char* fmtEnd, int ntrunc, const void* value); + int (*m_toIntImpl)(const void* value); }; -// Accept a value for formatting into the internal stream. -template -TINYFORMAT_NOINLINE // < greatly reduces bloat in optimized builds -void FormatIterator::accept(const T& value) +// Parse and return an integer from the string c, as atoi() +// On return, c is set to one past the end of the integer. +inline int parseIntAndAdvance(const char*& c) { - // Parse the format string - const char* fmtEnd = 0; - if(m_extraFlags == Flag_None && !m_wantWidth && !m_wantPrecision) - { - m_fmt = printFormatStringLiteral(m_out, m_fmt); - fmtEnd = streamStateFromFormat(m_out, m_extraFlags, m_fmt, 0, 0); - m_wantWidth = (m_extraFlags & Flag_VariableWidth) != 0; - m_wantPrecision = (m_extraFlags & Flag_VariablePrecision) != 0; - } - // Consume value as variable width and precision specifier if necessary - if(m_extraFlags & (Flag_VariableWidth | Flag_VariablePrecision)) - { - if(m_wantWidth || m_wantPrecision) - { - int v = convertToInt::invoke(value); - if(m_wantWidth) - { - m_variableWidth = v; - m_wantWidth = false; - } - else if(m_wantPrecision) - { - m_variablePrecision = v; - m_wantPrecision = false; - } - return; - } - // If we get here, we've set both the variable precision and width as - // required and we need to rerun the stream state setup to insert these. - fmtEnd = streamStateFromFormat(m_out, m_extraFlags, m_fmt, - m_variableWidth, m_variablePrecision); - } + int i = 0; + for(;*c >= '0' && *c <= '9'; ++c) + i = 10*i + (*c - '0'); + return i; +} - // Format the value into the stream. - if(!(m_extraFlags & (Flag_SpacePadPositive | Flag_TruncateToPrecision))) - formatValue(m_out, m_fmt, fmtEnd, value); - else +// Print literal part of format string and return next format spec +// position. +// +// Skips over any occurrences of '%%', printing a literal '%' to the +// output. The position of the first % character of the next +// nontrivial format spec is returned, or the end of string. +inline const char* printFormatStringLiteral(std::ostream& out, const char* fmt) +{ + const char* c = fmt; + for(;; ++c) { - // The following are special cases where there's no direct - // correspondence between stream formatting and the printf() behaviour. - // Instead, we simulate the behaviour crudely by formatting into a - // temporary string stream and munging the resulting string. - std::ostringstream tmpStream; - tmpStream.copyfmt(m_out); - if(m_extraFlags & Flag_SpacePadPositive) - tmpStream.setf(std::ios::showpos); - // formatCStringTruncate is required for truncating conversions like - // "%.4s" where at most 4 characters of the c-string should be read. - // If we didn't include this special case, we might read off the end. - if(!( (m_extraFlags & Flag_TruncateToPrecision) && - formatCStringTruncate(tmpStream, value, m_out.precision()) )) - { - // Not a truncated c-string; just format normally. - formatValue(tmpStream, m_fmt, fmtEnd, value); - } - std::string result = tmpStream.str(); // allocates... yuck. - if(m_extraFlags & Flag_SpacePadPositive) + switch(*c) { - for(size_t i = 0, iend = result.size(); i < iend; ++i) - if(result[i] == '+') - result[i] = ' '; + case '\0': + out.write(fmt, c - fmt); + return c; + case '%': + out.write(fmt, c - fmt); + if(*(c+1) != '%') + return c; + // for "%%", tack trailing % onto next literal section. + fmt = ++c; + break; + default: + break; } - if((m_extraFlags & Flag_TruncateToPrecision) && - (int)result.size() > (int)m_out.precision()) - m_out.write(result.c_str(), m_out.precision()); - else - m_out << result; } - m_extraFlags = Flag_None; - m_fmt = fmtEnd; } @@ -663,13 +575,14 @@ void FormatIterator::accept(const T& value) // with the form "%[flags][width][.precision][length]type". // // Formatting options which can't be natively represented using the ostream -// state are returned in the extraFlags parameter which is a bitwise -// combination of values from the ExtraFormatFlags enum. -inline const char* FormatIterator::streamStateFromFormat(std::ostream& out, - unsigned int& extraFlags, - const char* fmtStart, - int variableWidth, - int variablePrecision) +// state are returned in spacePadPositive (for space padded positive numbers) +// and ntrunc (for truncating conversions). argIndex is incremented if +// necessary to pull out variable width and precision . The function returns a +// pointer to the character after the end of the current format spec. +inline const char* streamStateFromFormat(std::ostream& out, bool& spacePadPositive, + int& ntrunc, const char* fmtStart, + const detail::FormatArg* formatters, + int& argIndex, int numFormatters) { if(*fmtStart != '%') { @@ -684,9 +597,9 @@ inline const char* FormatIterator::streamStateFromFormat(std::ostream& out, out.unsetf(std::ios::adjustfield | std::ios::basefield | std::ios::floatfield | std::ios::showbase | std::ios::boolalpha | std::ios::showpoint | std::ios::showpos | std::ios::uppercase); - extraFlags = Flag_None; bool precisionSet = false; bool widthSet = false; + int widthExtra = 0; const char* c = fmtStart + 1; // 1) Parse flags for(;; ++c) @@ -713,12 +626,15 @@ inline const char* FormatIterator::streamStateFromFormat(std::ostream& out, case ' ': // overridden by show positive sign, '+' flag. if(!(out.flags() & std::ios::showpos)) - extraFlags |= Flag_SpacePadPositive; + spacePadPositive = true; continue; case '+': out.setf(std::ios::showpos); - extraFlags &= ~Flag_SpacePadPositive; + spacePadPositive = false; + widthExtra = 1; continue; + default: + break; } break; } @@ -731,15 +647,19 @@ inline const char* FormatIterator::streamStateFromFormat(std::ostream& out, if(*c == '*') { widthSet = true; - if(variableWidth < 0) + int width = 0; + if(argIndex < numFormatters) + width = formatters[argIndex++].toInt(); + else + TINYFORMAT_ERROR("tinyformat: Not enough arguments to read variable width"); + if(width < 0) { // negative widths correspond to '-' flag set out.fill(' '); out.setf(std::ios::left, std::ios::adjustfield); - variableWidth = -variableWidth; + width = -width; } - out.width(variableWidth); - extraFlags |= Flag_VariableWidth; + out.width(width); ++c; } // 3) Parse precision @@ -750,8 +670,10 @@ inline const char* FormatIterator::streamStateFromFormat(std::ostream& out, if(*c == '*') { ++c; - extraFlags |= Flag_VariablePrecision; - precision = variablePrecision; + if(argIndex < numFormatters) + precision = formatters[argIndex++].toInt(); + else + TINYFORMAT_ERROR("tinyformat: Not enough arguments to read variable precision"); } else { @@ -814,7 +736,7 @@ inline const char* FormatIterator::streamStateFromFormat(std::ostream& out, break; case 's': if(precisionSet) - extraFlags |= Flag_TruncateToPrecision; + ntrunc = static_cast(out.precision()); // Make %s print booleans as "true" and "false" out.setf(std::ios::boolalpha); break; @@ -826,6 +748,8 @@ inline const char* FormatIterator::streamStateFromFormat(std::ostream& out, TINYFORMAT_ERROR("tinyformat: Conversion spec incorrectly " "terminated by end of string"); return c; + default: + break; } if(intConversion && precisionSet && !widthSet) { @@ -833,7 +757,7 @@ inline const char* FormatIterator::streamStateFromFormat(std::ostream& out, // padded with zeros on the left). This isn't really supported by the // iostreams, but we can approximately simulate it with the width if // the width isn't otherwise used. - out.width(out.precision()); + out.width(out.precision() + widthExtra); out.setf(std::ios::internal, std::ios::adjustfield); out.fill('0'); } @@ -841,170 +765,282 @@ inline const char* FormatIterator::streamStateFromFormat(std::ostream& out, } - //------------------------------------------------------------------------------ -// Private format function on top of which the public interface is implemented. -// We enforce a mimimum of one value to be formatted to prevent bugs looking like -// -// const char* myStr = "100% broken"; -// printf(myStr); // Parses % as a format specifier -#ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES - -template -void format(FormatIterator& fmtIter, const T1& value1) +inline void formatImpl(std::ostream& out, const char* fmt, + const detail::FormatArg* formatters, + int numFormatters) { - fmtIter.accept(value1); - fmtIter.finish(); + // Saved stream state + std::streamsize origWidth = out.width(); + std::streamsize origPrecision = out.precision(); + std::ios::fmtflags origFlags = out.flags(); + char origFill = out.fill(); + + for (int argIndex = 0; argIndex < numFormatters; ++argIndex) + { + // Parse the format string + fmt = printFormatStringLiteral(out, fmt); + bool spacePadPositive = false; + int ntrunc = -1; + const char* fmtEnd = streamStateFromFormat(out, spacePadPositive, ntrunc, fmt, + formatters, argIndex, numFormatters); + if (argIndex >= numFormatters) + { + // Check args remain after reading any variable width/precision + TINYFORMAT_ERROR("tinyformat: Not enough format arguments"); + return; + } + const FormatArg& arg = formatters[argIndex]; + // Format the arg into the stream. + if(!spacePadPositive) + arg.format(out, fmt, fmtEnd, ntrunc); + else + { + // The following is a special case with no direct correspondence + // between stream formatting and the printf() behaviour. Simulate + // it crudely by formatting into a temporary string stream and + // munging the resulting string. + std::ostringstream tmpStream; + tmpStream.copyfmt(out); + tmpStream.setf(std::ios::showpos); + arg.format(tmpStream, fmt, fmtEnd, ntrunc); + std::string result = tmpStream.str(); // allocates... yuck. + for(size_t i = 0, iend = result.size(); i < iend; ++i) + if(result[i] == '+') result[i] = ' '; + out << result; + } + fmt = fmtEnd; + } + + // Print remaining part of format string. + fmt = printFormatStringLiteral(out, fmt); + if(*fmt != '\0') + TINYFORMAT_ERROR("tinyformat: Too many conversion specifiers in format string"); + + // Restore stream state + out.width(origWidth); + out.precision(origPrecision); + out.flags(origFlags); + out.fill(origFill); } -// General version for C++11 -template -void format(FormatIterator& fmtIter, const T1& value1, const Args&... args) +} // namespace detail + + +/// List of template arguments format(), held in a type-opaque way. +/// +/// A const reference to FormatList (typedef'd as FormatListRef) may be +/// conveniently used to pass arguments to non-template functions: All type +/// information has been stripped from the arguments, leaving just enough of a +/// common interface to perform formatting as required. +class FormatList { - fmtIter.accept(value1); - format(fmtIter, args...); -} + public: + FormatList(detail::FormatArg* formatters, int N) + : m_formatters(formatters), m_N(N) { } -#else + friend void vformat(std::ostream& out, const char* fmt, + const FormatList& list); -inline void format(FormatIterator& fmtIter) + private: + const detail::FormatArg* m_formatters; + int m_N; +}; + +/// Reference to type-opaque format list for passing to vformat() +typedef const FormatList& FormatListRef; + + +namespace detail { + +// Format list subclass with fixed storage to avoid dynamic allocation +template +class FormatListN : public FormatList { - fmtIter.finish(); -} + public: +#ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES + template + FormatListN(const Args&... args) + : FormatList(&m_formatterStore[0], N), + m_formatterStore { FormatArg(args)... } + { static_assert(sizeof...(args) == N, "Number of args must be N"); } +#else // C++98 version + void init(int) {} +# define TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR(n) \ + \ + template \ + FormatListN(TINYFORMAT_VARARGS(n)) \ + : FormatList(&m_formatterStore[0], n) \ + { assert(n == N); init(0, TINYFORMAT_PASSARGS(n)); } \ + \ + template \ + void init(int i, TINYFORMAT_VARARGS(n)) \ + { \ + m_formatterStore[i] = FormatArg(v1); \ + init(i+1 TINYFORMAT_PASSARGS_TAIL(n)); \ + } -// General version for C++98 -#define TINYFORMAT_MAKE_FORMAT_DETAIL(n) \ -template \ -void format(detail::FormatIterator& fmtIter, TINYFORMAT_VARARGS(n)) \ -{ \ - fmtIter.accept(v1); \ - format(fmtIter TINYFORMAT_PASSARGS_TAIL(n)); \ -} + TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR) +# undef TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR +#endif -TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_FORMAT_DETAIL) -#undef TINYFORMAT_MAKE_FORMAT_DETAIL + private: + FormatArg m_formatterStore[N]; +}; -#endif // End C++98 variadic template emulation for format() +// Special 0-arg version - MSVC says zero-sized C array in struct is nonstandard +template<> class FormatListN<0> : public FormatList +{ + public: FormatListN() : FormatList(0, 0) {} +}; } // namespace detail //------------------------------------------------------------------------------ -// Implement all the main interface functions here in terms of detail::format() +// Primary API functions #ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES -// C++11 - the simple case -template -void format(std::ostream& out, const char* fmt, const T1& v1, const Args&... args) +/// Make type-agnostic format list from list of template arguments. +/// +/// The exact return type of this function is an implementation detail and +/// shouldn't be relied upon. Instead it should be stored as a FormatListRef: +/// +/// FormatListRef formatList = makeFormatList( /*...*/ ); +template +detail::FormatListN makeFormatList(const Args&... args) { - detail::FormatIterator fmtIter(out, fmt); - format(fmtIter, v1, args...); + return detail::FormatListN(args...); } -template -std::string format(const char* fmt, const T1& v1, const Args&... args) +#else // C++98 version + +inline detail::FormatListN<0> makeFormatList() +{ + return detail::FormatListN<0>(); +} +#define TINYFORMAT_MAKE_MAKEFORMATLIST(n) \ +template \ +detail::FormatListN makeFormatList(TINYFORMAT_VARARGS(n)) \ +{ \ + return detail::FormatListN(TINYFORMAT_PASSARGS(n)); \ +} +TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_MAKEFORMATLIST) +#undef TINYFORMAT_MAKE_MAKEFORMATLIST + +#endif + +/// Format list of arguments to the stream according to the given format string. +/// +/// The name vformat() is chosen for the semantic similarity to vprintf(): the +/// list of format arguments is held in a single function argument. +inline void vformat(std::ostream& out, const char* fmt, FormatListRef list) +{ + detail::formatImpl(out, fmt, list.m_formatters, list.m_N); +} + + +#ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES + +/// Format list of arguments to the stream according to given format string. +template +void format(std::ostream& out, const char* fmt, const Args&... args) +{ + vformat(out, fmt, makeFormatList(args...)); +} + +/// Format list of arguments according to the given format string and return +/// the result as a string. +template +std::string format(const char* fmt, const Args&... args) { std::ostringstream oss; - format(oss, fmt, v1, args...); + format(oss, fmt, args...); return oss.str(); } -template -std::string format(const std::string &fmt, const T1& v1, const Args&... args) +/// Format list of arguments to std::cout, according to the given format string +template +void printf(const char* fmt, const Args&... args) +{ + format(std::cout, fmt, args...); +} + +template +void printfln(const char* fmt, const Args&... args) +{ + format(std::cout, fmt, args...); + std::cout << '\n'; +} + +#else // C++98 version + +inline void format(std::ostream& out, const char* fmt) +{ + vformat(out, fmt, makeFormatList()); +} + +inline std::string format(const char* fmt) { std::ostringstream oss; - format(oss, fmt.c_str(), v1, args...); + format(oss, fmt); return oss.str(); } -template -void printf(const char* fmt, const T1& v1, const Args&... args) +inline void printf(const char* fmt) { - format(std::cout, fmt, v1, args...); + format(std::cout, fmt); } -#else +inline void printfln(const char* fmt) +{ + format(std::cout, fmt); + std::cout << '\n'; +} -// C++98 - define the interface functions using the wrapping macros #define TINYFORMAT_MAKE_FORMAT_FUNCS(n) \ \ template \ void format(std::ostream& out, const char* fmt, TINYFORMAT_VARARGS(n)) \ { \ - tinyformat::detail::FormatIterator fmtIter(out, fmt); \ - tinyformat::detail::format(fmtIter, TINYFORMAT_PASSARGS(n)); \ + vformat(out, fmt, makeFormatList(TINYFORMAT_PASSARGS(n))); \ } \ \ template \ std::string format(const char* fmt, TINYFORMAT_VARARGS(n)) \ { \ std::ostringstream oss; \ - tinyformat::format(oss, fmt, TINYFORMAT_PASSARGS(n)); \ + format(oss, fmt, TINYFORMAT_PASSARGS(n)); \ return oss.str(); \ } \ \ template \ -std::string format(const std::string &fmt, TINYFORMAT_VARARGS(n)) \ +void printf(const char* fmt, TINYFORMAT_VARARGS(n)) \ { \ - std::ostringstream oss; \ - tinyformat::format(oss, fmt.c_str(), TINYFORMAT_PASSARGS(n)); \ - return oss.str(); \ + format(std::cout, fmt, TINYFORMAT_PASSARGS(n)); \ } \ \ template \ -void printf(const char* fmt, TINYFORMAT_VARARGS(n)) \ +void printfln(const char* fmt, TINYFORMAT_VARARGS(n)) \ { \ - tinyformat::format(std::cout, fmt, TINYFORMAT_PASSARGS(n)); \ + format(std::cout, fmt, TINYFORMAT_PASSARGS(n)); \ + std::cout << '\n'; \ } TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_FORMAT_FUNCS) #undef TINYFORMAT_MAKE_FORMAT_FUNCS -#endif - -//------------------------------------------------------------------------------ -// Define deprecated wrapping macro for backward compatibility in tinyformat -// 1.x. Will be removed in version 2! -#define TINYFORMAT_WRAP_FORMAT_EXTRA_ARGS -#define TINYFORMAT_WRAP_FORMAT_N(n, returnType, funcName, funcDeclSuffix, \ - bodyPrefix, streamName, bodySuffix) \ -template \ -returnType funcName(TINYFORMAT_WRAP_FORMAT_EXTRA_ARGS const char* fmt, \ - TINYFORMAT_VARARGS(n)) funcDeclSuffix \ -{ \ - bodyPrefix \ - tinyformat::format(streamName, fmt, TINYFORMAT_PASSARGS(n)); \ - bodySuffix \ -} \ - -#define TINYFORMAT_WRAP_FORMAT(returnType, funcName, funcDeclSuffix, \ - bodyPrefix, streamName, bodySuffix) \ -inline \ -returnType funcName(TINYFORMAT_WRAP_FORMAT_EXTRA_ARGS const char* fmt \ - ) funcDeclSuffix \ -{ \ - bodyPrefix \ - tinyformat::detail::FormatIterator(streamName, fmt).finish(); \ - bodySuffix \ -} \ -TINYFORMAT_WRAP_FORMAT_N(1 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ -TINYFORMAT_WRAP_FORMAT_N(2 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ -TINYFORMAT_WRAP_FORMAT_N(3 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ -TINYFORMAT_WRAP_FORMAT_N(4 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ -TINYFORMAT_WRAP_FORMAT_N(5 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ -TINYFORMAT_WRAP_FORMAT_N(6 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ -TINYFORMAT_WRAP_FORMAT_N(7 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ -TINYFORMAT_WRAP_FORMAT_N(8 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ -TINYFORMAT_WRAP_FORMAT_N(9 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ -TINYFORMAT_WRAP_FORMAT_N(10, returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ -TINYFORMAT_WRAP_FORMAT_N(11, returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ -TINYFORMAT_WRAP_FORMAT_N(12, returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ -TINYFORMAT_WRAP_FORMAT_N(13, returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ -TINYFORMAT_WRAP_FORMAT_N(14, returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ -TINYFORMAT_WRAP_FORMAT_N(15, returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ -TINYFORMAT_WRAP_FORMAT_N(16, returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ +#endif +// Added for Bitcoin Core +template +std::string format(const std::string &fmt, const Args&... args) +{ + std::ostringstream oss; + format(oss, fmt.c_str(), args...); + return oss.str(); +} } // namespace tinyformat diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 06623908bbcd..b3bf5a567e04 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -776,6 +776,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const uint64_t innerUsage = 0; CCoinsViewCache mempoolDuplicate(const_cast(pcoins)); + const int64_t nSpendHeight = GetSpendHeight(mempoolDuplicate); LOCK(cs); list waitingOnDependants; @@ -855,7 +856,9 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const waitingOnDependants.push_back(&(*it)); else { CValidationState state; - assert(CheckInputs(tx, state, mempoolDuplicate, false, 0, false, NULL)); + bool fCheckResult = tx.IsCoinBase() || + Consensus::CheckTxInputs(tx, state, mempoolDuplicate, nSpendHeight); + assert(fCheckResult); UpdateCoins(tx, mempoolDuplicate, 1000000); } } @@ -869,7 +872,9 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const stepsSinceLastRemove++; assert(stepsSinceLastRemove < waitingOnDependants.size()); } else { - assert(CheckInputs(entry->GetTx(), state, mempoolDuplicate, false, 0, false, NULL)); + bool fCheckResult = entry->GetTx().IsCoinBase() || + Consensus::CheckTxInputs(entry->GetTx(), state, mempoolDuplicate, nSpendHeight); + assert(fCheckResult); UpdateCoins(entry->GetTx(), mempoolDuplicate, 1000000); stepsSinceLastRemove = 0; } diff --git a/src/util.h b/src/util.h index 70f4748e96aa..d02846ff63ef 100644 --- a/src/util.h +++ b/src/util.h @@ -98,33 +98,17 @@ int LogPrintStr(const std::string &str); #define LogPrintf(...) LogPrint(NULL, __VA_ARGS__) -template -static inline int LogPrint(const char* category, const char* fmt, const T1& v1, const Args&... args) +template +static inline int LogPrint(const char* category, const char* fmt, const Args&... args) { if(!LogAcceptCategory(category)) return 0; \ - return LogPrintStr(tfm::format(fmt, v1, args...)); + return LogPrintStr(tfm::format(fmt, args...)); } -template -bool error(const char* fmt, const T1& v1, const Args&... args) +template +bool error(const char* fmt, const Args&... args) { - LogPrintStr("ERROR: " + tfm::format(fmt, v1, args...) + "\n"); - return false; -} - -/** - * Zero-arg versions of logging and error, these are not covered by - * the variadic templates above (and don't take format arguments but - * bare strings). - */ -static inline int LogPrint(const char* category, const char* s) -{ - if(!LogAcceptCategory(category)) return 0; - return LogPrintStr(s); -} -static inline bool error(const char* s) -{ - LogPrintStr(std::string("ERROR: ") + s + "\n"); + LogPrintStr("ERROR: " + tfm::format(fmt, args...) + "\n"); return false; } diff --git a/src/validation.cpp b/src/validation.cpp index 37295058ca2e..05a0807d34c0 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -214,7 +214,7 @@ bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime) return true; if ((int64_t)tx.nLockTime < ((int64_t)tx.nLockTime < LOCKTIME_THRESHOLD ? (int64_t)nBlockHeight : nBlockTime)) return true; - BOOST_FOREACH(const CTxIn& txin, tx.vin) { + for (const auto& txin : tx.vin) { if (!(txin.nSequence == CTxIn::SEQUENCE_FINAL)) return false; } @@ -428,11 +428,11 @@ bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp, bool unsigned int GetLegacySigOpCount(const CTransaction& tx) { unsigned int nSigOps = 0; - BOOST_FOREACH(const CTxIn& txin, tx.vin) + for (const auto& txin : tx.vin) { nSigOps += txin.scriptSig.GetSigOpCount(false); } - BOOST_FOREACH(const CTxOut& txout, tx.vout) + for (const auto& txout : tx.vout) { nSigOps += txout.scriptPubKey.GetSigOpCount(false); } @@ -495,7 +495,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state) // Check for negative or overflow output values CAmount nValueOut = 0; - BOOST_FOREACH(const CTxOut& txout, tx.vout) + for (const auto& txout : tx.vout) { if (txout.nValue < 0) return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-negative"); @@ -508,7 +508,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state) // Check for duplicate inputs set vInOutPoints; - BOOST_FOREACH(const CTxIn& txin, tx.vin) + for (const auto& txin : tx.vin) { if (vInOutPoints.count(txin.prevout)) return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputs-duplicate"); @@ -522,7 +522,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state) } else { - BOOST_FOREACH(const CTxIn& txin, tx.vin) + for (const auto& txin : tx.vin) if (txin.prevout.IsNull()) return state.DoS(10, false, REJECT_INVALID, "bad-txns-prevout-null"); } @@ -646,7 +646,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C // nSequence >= maxint-1 on all inputs. // // maxint-1 is picked to still allow use of nLockTime by - // non-replacable transactions. All inputs rather than just one + // non-replaceable transactions. All inputs rather than just one // is for the sake of multi-party protocols, where we don't // want a single party to be able to disable replacement. // @@ -1014,7 +1014,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C } if(!fDryRun) - GetMainSignals().SyncTransaction(tx, NULL, NULL); + GetMainSignals().SyncTransaction(tx, NULL, CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK); return true; } @@ -2559,7 +2559,7 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara // Let wallets know transactions went from 1-confirmed to // 0-confirmed or conflicted: BOOST_FOREACH(const CTransaction &tx, block.vtx) { - GetMainSignals().SyncTransaction(tx, pindexDelete->pprev, NULL); + GetMainSignals().SyncTransaction(tx, pindexDelete->pprev, CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK); } return true; } @@ -2574,7 +2574,7 @@ static int64_t nTimePostConnect = 0; * Connect a new block to chainActive. pblock is either NULL or a pointer to a CBlock * corresponding to pindexNew, to bypass loading it again from disk. */ -bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const CBlock* pblock) +bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const CBlock* pblock, std::list &txConflicted, std::vector > &txChanged) { assert(pindexNew->pprev == chainActive.Tip()); // Read block from disk. @@ -2609,20 +2609,13 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, return false; int64_t nTime5 = GetTimeMicros(); nTimeChainState += nTime5 - nTime4; LogPrint("bench", " - Writing chainstate: %.2fms [%.2fs]\n", (nTime5 - nTime4) * 0.001, nTimeChainState * 0.000001); - // Remove conflicting transactions from the mempool. - list txConflicted; + // Remove conflicting transactions from the mempool.; mempool.removeForBlock(pblock->vtx, pindexNew->nHeight, txConflicted, !IsInitialBlockDownload()); // Update chainActive & related variables. UpdateTip(pindexNew, chainparams); - // Tell wallet about transactions that went from mempool - // to conflicted: - BOOST_FOREACH(const CTransaction &tx, txConflicted) { - GetMainSignals().SyncTransaction(tx, pindexNew, NULL); - } - // ... and about transactions that got confirmed: - BOOST_FOREACH(const CTransaction &tx, pblock->vtx) { - GetMainSignals().SyncTransaction(tx, pindexNew, pblock); - } + + for(unsigned int i=0; i < pblock->vtx.size(); i++) + txChanged.push_back(std::make_tuple(pblock->vtx[i], pindexNew, i)); int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1; LogPrint("bench", " - Connect postprocess: %.2fms [%.2fs]\n", (nTime6 - nTime5) * 0.001, nTimePostConnect * 0.000001); @@ -2747,7 +2740,7 @@ static void PruneBlockIndexCandidates() { * Try to make some progress towards making pindexMostWork the active block. * pblock is either NULL or a pointer to a CBlock corresponding to pindexMostWork. */ -static bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const CBlock* pblock, bool& fInvalidFound) +static bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const CBlock* pblock, bool& fInvalidFound, std::list& txConflicted, std::vector >& txChanged) { AssertLockHeld(cs_main); const CBlockIndex *pindexOldTip = chainActive.Tip(); @@ -2780,7 +2773,7 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c // Connect new blocks. BOOST_REVERSE_FOREACH(CBlockIndex *pindexConnect, vpindexToConnect) { - if (!ConnectTip(state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : NULL)) { + if (!ConnectTip(state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : NULL, txConflicted, txChanged)) { if (state.IsInvalid()) { // The block violates a consensus rule. if (!state.CorruptionPossible()) @@ -2855,6 +2848,8 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, break; const CBlockIndex *pindexFork; + std::list txConflicted; + std::vector > txChanged; bool fInitialDownload; { LOCK(cs_main); @@ -2868,7 +2863,7 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, return true; bool fInvalidFound = false; - if (!ActivateBestChainStep(state, chainparams, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : NULL, fInvalidFound)) + if (!ActivateBestChainStep(state, chainparams, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : NULL, fInvalidFound, txConflicted, txChanged)) return false; if (fInvalidFound) { @@ -2883,6 +2878,16 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, // Notifications/callbacks that can run without cs_main + // throw all transactions though the signal-interface + // while _not_ holding the cs_main lock + BOOST_FOREACH(const CTransaction &tx, txConflicted) + { + GetMainSignals().SyncTransaction(tx, pindexNewTip, CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK); + } + // ... and about transactions that got confirmed: + for(unsigned int i = 0; i < txChanged.size(); i++) + GetMainSignals().SyncTransaction(std::get<0>(txChanged[i]), std::get<1>(txChanged[i]), std::get<2>(txChanged[i])); + // Notify external listeners about the new tip. GetMainSignals().UpdatedBlockTip(pindexNewTip, pindexFork, fInitialDownload); @@ -3234,13 +3239,13 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P // END DASH // Check transactions - BOOST_FOREACH(const CTransaction& tx, block.vtx) + for (const auto& tx : block.vtx) if (!CheckTransaction(tx, state)) return state.Invalid(false, state.GetRejectCode(), state.GetRejectReason(), strprintf("Transaction check failed (tx hash %s) %s", tx.GetHash().ToString(), state.GetDebugMessage())); unsigned int nSigOps = 0; - BOOST_FOREACH(const CTransaction& tx, block.vtx) + for (const auto& tx : block.vtx) { nSigOps += GetLegacySigOpCount(tx); } @@ -3268,7 +3273,7 @@ static bool CheckIndexAgainstCheckpoint(const CBlockIndex* pindexPrev, CValidati return true; } -bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, CBlockIndex * const pindexPrev) +bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev) { int nHeight = pindexPrev->nHeight + 1; // Check proof of work @@ -3299,10 +3304,9 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta return true; } -bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIndex * const pindexPrev) +bool ContextualCheckBlock(const CBlock& block, CValidationState& state, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev) { const int nHeight = pindexPrev == NULL ? 0 : pindexPrev->nHeight + 1; - const Consensus::Params& consensusParams = Params().GetConsensus(); // Start enforcing BIP113 (Median Time Past) using versionbits logic. int nLockTimeFlags = 0; @@ -3324,7 +3328,7 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn // Check that all transactions are finalized and not over-sized // Also count sigops unsigned int nSigOps = 0; - BOOST_FOREACH(const CTransaction& tx, block.vtx) { + for (const auto& tx : block.vtx) { if (!IsFinalTx(tx, nHeight, nLockTimeCutoff)) { return state.DoS(10, false, REJECT_INVALID, "bad-txns-nonfinal", false, "non-final transaction"); } @@ -3460,7 +3464,8 @@ static bool AcceptBlock(const CBlock& block, CValidationState& state, const CCha } if (fNewBlock) *fNewBlock = true; - if ((!CheckBlock(block, state, chainparams.GetConsensus(), GetAdjustedTime())) || !ContextualCheckBlock(block, state, pindex->pprev)) { + if (!CheckBlock(block, state, chainparams.GetConsensus(), GetAdjustedTime()) || + !ContextualCheckBlock(block, state, chainparams.GetConsensus(), pindex->pprev)) { if (state.IsInvalid() && !state.CorruptionPossible()) { pindex->nStatus |= BLOCK_FAILED_VALID; setDirtyBlockIndex.insert(pindex); @@ -3550,7 +3555,7 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, return error("%s: Consensus::ContextualCheckBlockHeader: %s", __func__, FormatStateMessage(state)); if (!CheckBlock(block, state, chainparams.GetConsensus(), GetAdjustedTime(), fCheckPOW, fCheckMerkleRoot)) return error("%s: Consensus::CheckBlock: %s", __func__, FormatStateMessage(state)); - if (!ContextualCheckBlock(block, state, pindexPrev)) + if (!ContextualCheckBlock(block, state, chainparams.GetConsensus(), pindexPrev)) return error("%s: Consensus::ContextualCheckBlock: %s", __func__, FormatStateMessage(state)); if (!ConnectBlock(block, state, &indexDummy, viewNew, chainparams, true)) return false; @@ -3724,7 +3729,7 @@ CBlockIndex * InsertBlockIndex(uint256 hash) // Create new CBlockIndex* pindexNew = new CBlockIndex(); if (!pindexNew) - throw runtime_error("InsertBlockIndex(): new CBlockIndex failed"); + throw runtime_error(std::string(__func__) + ": new CBlockIndex failed"); mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; pindexNew->phashBlock = &((*mi).first); @@ -3882,7 +3887,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, int nGoodTransactions = 0; CValidationState state; int reportDone = 0; - LogPrintf("[0%]..."); + LogPrintf("[0%%]..."); for (CBlockIndex* pindex = chainActive.Tip(); pindex && pindex->pprev; pindex = pindex->pprev) { boost::this_thread::interruption_point(); @@ -4007,8 +4012,6 @@ static bool AddGenesisBlock(const CChainParams& chainparams, const CBlock& block CBlockIndex *pindex = AddToBlockIndex(block); if (!ReceivedBlockTransactions(block, state, pindex, blockPos)) return error("%s: genesis block not accepted", __func__); - if (!ActivateBestChain(state, chainparams, &block)) - return error("%s: genesis block cannot be activated", __func__); return true; } diff --git a/src/validation.h b/src/validation.h index 54e9d2975dd9..5bb6a30b224f 100644 --- a/src/validation.h +++ b/src/validation.h @@ -360,9 +360,22 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi /** Apply the effects of this transaction on the UTXO set represented by view */ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight); +/** Transaction validation functions */ + /** Context-independent validity checks */ bool CheckTransaction(const CTransaction& tx, CValidationState& state); +namespace Consensus { + +/** + * Check whether all inputs of this transaction are valid (no double spends and amounts) + * This does not modify the UTXO set. This does not check scripts and sigs. + * Preconditions: tx.IsCoinBase() is false. + */ +bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight); + +} // namespace Consensus + /** * Check if transaction is final and can be included in a block with the * specified height and time. Consensus critical. @@ -462,8 +475,8 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P /** Context-dependent validity checks. * By "context", we mean only the previous block headers, but not the UTXO * set; UTXO-related validity checks are done in ConnectBlock(). */ -bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, CBlockIndex* pindexPrev); -bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIndex *pindexPrev); +bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev); +bool ContextualCheckBlock(const CBlock& block, CValidationState& state, const Consensus::Params& consensusParams, const CBlockIndex *pindexPrev); /** Check a block is completely valid from start to finish (only works on top of our current best block, with cs_main held) */ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, const CBlock& block, CBlockIndex* pindexPrev, bool fCheckPOW = true, bool fCheckMerkleRoot = true); diff --git a/src/validationinterface.h b/src/validationinterface.h index 5c15f80bbb6a..391a594a04b2 100644 --- a/src/validationinterface.h +++ b/src/validationinterface.h @@ -34,7 +34,7 @@ class CValidationInterface { virtual void AcceptedBlockHeader(const CBlockIndex *pindexNew) {} virtual void NotifyHeaderTip(const CBlockIndex *pindexNew, bool fInitialDownload) {} virtual void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {} - virtual void SyncTransaction(const CTransaction &tx, const CBlockIndex *pindex, const CBlock *pblock) {} + virtual void SyncTransaction(const CTransaction &tx, const CBlockIndex *pindex, int posInBlock) {} virtual void NotifyTransactionLock(const CTransaction &tx) {} virtual void SetBestChain(const CBlockLocator &locator) {} virtual bool UpdatedTransaction(const uint256 &hash) { return false;} @@ -55,8 +55,10 @@ struct CMainSignals { boost::signals2::signal NotifyHeaderTip; /** Notifies listeners of updated block chain tip */ boost::signals2::signal UpdatedBlockTip; + /** A posInBlock value for SyncTransaction which indicates the transaction was conflicted, disconnected, or not in a block */ + static const int SYNC_TRANSACTION_NOT_IN_BLOCK = -1; /** Notifies listeners of updated transaction data (transaction, and optionally the block it is found in. */ - boost::signals2::signal SyncTransaction; + boost::signals2::signal SyncTransaction; /** Notifies listeners of an updated transaction lock without new data. */ boost::signals2::signal NotifyTransactionLock; /** Notifies listeners of an updated transaction without new data (for now: a coinbase potentially becoming visible). */ diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 7401d8745491..2f4b40ca8cdf 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -310,8 +310,7 @@ UniValue importprunedfunds(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); if (pwalletMain->IsMine(tx)) { - CWalletDB walletdb(pwalletMain->strWalletFile, "r+", false); - pwalletMain->AddToWallet(wtx, false, &walletdb); + pwalletMain->AddToWallet(wtx, false); return NullUniValue; } diff --git a/src/wallet/test/accounting_tests.cpp b/src/wallet/test/accounting_tests.cpp index d075b2b641a1..a6cada46a226 100644 --- a/src/wallet/test/accounting_tests.cpp +++ b/src/wallet/test/accounting_tests.cpp @@ -48,7 +48,7 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade) pwalletMain->AddAccountingEntry(ae, walletdb); wtx.mapValue["comment"] = "z"; - pwalletMain->AddToWallet(wtx, false, &walletdb); + pwalletMain->AddToWallet(wtx); vpwtx.push_back(&pwalletMain->mapWallet[wtx.GetHash()]); vpwtx[0]->nTimeReceived = (unsigned int)1333333335; vpwtx[0]->nOrderPos = -1; @@ -90,7 +90,7 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade) --tx.nLockTime; // Just to change the hash :) *static_cast(&wtx) = CTransaction(tx); } - pwalletMain->AddToWallet(wtx, false, &walletdb); + pwalletMain->AddToWallet(wtx); vpwtx.push_back(&pwalletMain->mapWallet[wtx.GetHash()]); vpwtx[1]->nTimeReceived = (unsigned int)1333333336; @@ -100,7 +100,7 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade) --tx.nLockTime; // Just to change the hash :) *static_cast(&wtx) = CTransaction(tx); } - pwalletMain->AddToWallet(wtx, false, &walletdb); + pwalletMain->AddToWallet(wtx); vpwtx.push_back(&pwalletMain->mapWallet[wtx.GetHash()]); vpwtx[2]->nTimeReceived = (unsigned int)1333333329; vpwtx[2]->nOrderPos = -1; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index c25587244516..e40ce6b4bc7f 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -959,146 +959,151 @@ void CWallet::MarkDirty() fAnonymizableTallyCachedNonDenom = false; } -bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletDB* pwalletdb) +bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose) { + LOCK(cs_wallet); + + CWalletDB walletdb(strWalletFile, "r+", fFlushOnClose); + uint256 hash = wtxIn.GetHash(); - if (fFromLoadWallet) + // Inserts only if not already there, returns tx inserted or tx found + pair::iterator, bool> ret = mapWallet.insert(make_pair(hash, wtxIn)); + CWalletTx& wtx = (*ret.first).second; + wtx.BindWallet(this); + bool fInsertedNew = ret.second; + if (fInsertedNew) { - mapWallet[hash] = wtxIn; - CWalletTx& wtx = mapWallet[hash]; - wtx.BindWallet(this); + wtx.nTimeReceived = GetAdjustedTime(); + wtx.nOrderPos = IncOrderPosNext(&walletdb); wtxOrdered.insert(make_pair(wtx.nOrderPos, TxPair(&wtx, (CAccountingEntry*)0))); - AddToSpends(hash); - BOOST_FOREACH(const CTxIn& txin, wtx.vin) { - if (mapWallet.count(txin.prevout.hash)) { - CWalletTx& prevtx = mapWallet[txin.prevout.hash]; - if (prevtx.nIndex == -1 && !prevtx.hashUnset()) { - MarkConflicted(prevtx.hashBlock, wtx.GetHash()); - } - } - } - } - else - { - LOCK(cs_wallet); - // Inserts only if not already there, returns tx inserted or tx found - pair::iterator, bool> ret = mapWallet.insert(make_pair(hash, wtxIn)); - CWalletTx& wtx = (*ret.first).second; - wtx.BindWallet(this); - bool fInsertedNew = ret.second; - if (fInsertedNew) - { - wtx.nTimeReceived = GetAdjustedTime(); - wtx.nOrderPos = IncOrderPosNext(pwalletdb); - wtxOrdered.insert(make_pair(wtx.nOrderPos, TxPair(&wtx, (CAccountingEntry*)0))); - wtx.nTimeSmart = wtx.nTimeReceived; - if (!wtxIn.hashUnset()) + wtx.nTimeSmart = wtx.nTimeReceived; + if (!wtxIn.hashUnset()) + { + if (mapBlockIndex.count(wtxIn.hashBlock)) { - if (mapBlockIndex.count(wtxIn.hashBlock)) + int64_t latestNow = wtx.nTimeReceived; + int64_t latestEntry = 0; { - int64_t latestNow = wtx.nTimeReceived; - int64_t latestEntry = 0; + // Tolerate times up to the last timestamp in the wallet not more than 5 minutes into the future + int64_t latestTolerated = latestNow + 300; + const TxItems & txOrdered = wtxOrdered; + for (TxItems::const_reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) { - // Tolerate times up to the last timestamp in the wallet not more than 5 minutes into the future - int64_t latestTolerated = latestNow + 300; - const TxItems & txOrdered = wtxOrdered; - for (TxItems::const_reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) + CWalletTx *const pwtx = (*it).second.first; + if (pwtx == &wtx) + continue; + CAccountingEntry *const pacentry = (*it).second.second; + int64_t nSmartTime; + if (pwtx) { - CWalletTx *const pwtx = (*it).second.first; - if (pwtx == &wtx) - continue; - CAccountingEntry *const pacentry = (*it).second.second; - int64_t nSmartTime; - if (pwtx) - { - nSmartTime = pwtx->nTimeSmart; - if (!nSmartTime) - nSmartTime = pwtx->nTimeReceived; - } - else - nSmartTime = pacentry->nTime; - if (nSmartTime <= latestTolerated) - { - latestEntry = nSmartTime; - if (nSmartTime > latestNow) - latestNow = nSmartTime; - break; - } + nSmartTime = pwtx->nTimeSmart; + if (!nSmartTime) + nSmartTime = pwtx->nTimeReceived; + } + else + nSmartTime = pacentry->nTime; + if (nSmartTime <= latestTolerated) + { + latestEntry = nSmartTime; + if (nSmartTime > latestNow) + latestNow = nSmartTime; + break; } } - - int64_t blocktime = mapBlockIndex[wtxIn.hashBlock]->GetBlockTime(); - wtx.nTimeSmart = std::max(latestEntry, std::min(blocktime, latestNow)); } - else - LogPrintf("AddToWallet(): found %s in block %s not in index\n", - wtxIn.GetHash().ToString(), - wtxIn.hashBlock.ToString()); + + int64_t blocktime = mapBlockIndex[wtxIn.hashBlock]->GetBlockTime(); + wtx.nTimeSmart = std::max(latestEntry, std::min(blocktime, latestNow)); } - AddToSpends(hash); - for(int i = 0; i < wtx.vout.size(); ++i) { - if (IsMine(wtx.vout[i]) && !IsSpent(hash, i)) { - setWalletUTXO.insert(COutPoint(hash, i)); - } + else + LogPrintf("AddToWallet(): found %s in block %s not in index\n", + wtxIn.GetHash().ToString(), + wtxIn.hashBlock.ToString()); + } + AddToSpends(hash); + for(int i = 0; i < wtx.vout.size(); ++i) { + if (IsMine(wtx.vout[i]) && !IsSpent(hash, i)) { + setWalletUTXO.insert(COutPoint(hash, i)); } } + } - bool fUpdated = false; - if (!fInsertedNew) + bool fUpdated = false; + if (!fInsertedNew) + { + // Merge + if (!wtxIn.hashUnset() && wtxIn.hashBlock != wtx.hashBlock) { - // Merge - if (!wtxIn.hashUnset() && wtxIn.hashBlock != wtx.hashBlock) - { - wtx.hashBlock = wtxIn.hashBlock; - fUpdated = true; - } - // If no longer abandoned, update - if (wtxIn.hashBlock.IsNull() && wtx.isAbandoned()) - { - wtx.hashBlock = wtxIn.hashBlock; - fUpdated = true; - } - if (wtxIn.nIndex != -1 && (wtxIn.nIndex != wtx.nIndex)) - { - wtx.nIndex = wtxIn.nIndex; - fUpdated = true; - } - if (wtxIn.fFromMe && wtxIn.fFromMe != wtx.fFromMe) - { - wtx.fFromMe = wtxIn.fFromMe; - fUpdated = true; - } + wtx.hashBlock = wtxIn.hashBlock; + fUpdated = true; + } + // If no longer abandoned, update + if (wtxIn.hashBlock.IsNull() && wtx.isAbandoned()) + { + wtx.hashBlock = wtxIn.hashBlock; + fUpdated = true; + } + if (wtxIn.nIndex != -1 && (wtxIn.nIndex != wtx.nIndex)) + { + wtx.nIndex = wtxIn.nIndex; + fUpdated = true; + } + if (wtxIn.fFromMe && wtxIn.fFromMe != wtx.fFromMe) + { + wtx.fFromMe = wtxIn.fFromMe; + fUpdated = true; } + } - //// debug print - LogPrintf("AddToWallet %s %s%s\n", wtxIn.GetHash().ToString(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : "")); + //// debug print + LogPrintf("AddToWallet %s %s%s\n", wtxIn.GetHash().ToString(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : "")); - // Write to disk - if (fInsertedNew || fUpdated) - if (!pwalletdb->WriteTx(wtx)) - return false; + // Write to disk + if (fInsertedNew || fUpdated) + if (!walletdb.WriteTx(wtx)) + return false; - // Break debit/credit balance caches: - wtx.MarkDirty(); + // Break debit/credit balance caches: + wtx.MarkDirty(); - // Notify UI of new or updated transaction - NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED); + // Notify UI of new or updated transaction + NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED); - // notify an external script when a wallet transaction comes in or is updated - std::string strCmd = GetArg("-walletnotify", ""); + // notify an external script when a wallet transaction comes in or is updated + std::string strCmd = GetArg("-walletnotify", ""); - if ( !strCmd.empty()) - { - boost::replace_all(strCmd, "%s", wtxIn.GetHash().GetHex()); - boost::thread t(runCommand, strCmd); // thread runs free - } + if ( !strCmd.empty()) + { + boost::replace_all(strCmd, "%s", wtxIn.GetHash().GetHex()); + boost::thread t(runCommand, strCmd); // thread runs free + } - fAnonymizableTallyCached = false; - fAnonymizableTallyCachedNonDenom = false; + fAnonymizableTallyCached = false; + fAnonymizableTallyCachedNonDenom = false; + return true; +} + +bool CWallet::LoadToWallet(const CWalletTx& wtxIn) +{ + uint256 hash = wtxIn.GetHash(); + + mapWallet[hash] = wtxIn; + CWalletTx& wtx = mapWallet[hash]; + wtx.BindWallet(this); + wtxOrdered.insert(make_pair(wtx.nOrderPos, TxPair(&wtx, (CAccountingEntry*)0))); + AddToSpends(hash); + BOOST_FOREACH(const CTxIn& txin, wtx.vin) { + if (mapWallet.count(txin.prevout.hash)) { + CWalletTx& prevtx = mapWallet[txin.prevout.hash]; + if (prevtx.nIndex == -1 && !prevtx.hashUnset()) { + MarkConflicted(prevtx.hashBlock, wtx.GetHash()); + } + } } + return true; } @@ -1107,18 +1112,18 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletD * pblock is optional, but should be provided if the transaction is known to be in a block. * If fUpdate is true, existing transactions will be updated. */ -bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate) +bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate) { { AssertLockHeld(cs_wallet); - if (pblock) { + if (posInBlock != -1) { BOOST_FOREACH(const CTxIn& txin, tx.vin) { std::pair range = mapTxSpends.equal_range(txin.prevout); while (range.first != range.second) { if (range.first->second != tx.GetHash()) { - LogPrintf("Transaction %s (in block %s) conflicts with wallet transaction %s (both spend %s:%i)\n", tx.GetHash().ToString(), pblock->GetHash().ToString(), range.first->second.ToString(), range.first->first.hash.ToString(), range.first->first.n); - MarkConflicted(pblock->GetHash(), range.first->second); + LogPrintf("Transaction %s (in block %s) conflicts with wallet transaction %s (both spend %s:%i)\n", tx.GetHash().ToString(), pIndex->GetBlockHash().ToString(), range.first->second.ToString(), range.first->first.hash.ToString(), range.first->first.n); + MarkConflicted(pIndex->GetBlockHash(), range.first->second); } range.first++; } @@ -1132,14 +1137,10 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl CWalletTx wtx(this,tx); // Get merkle branch if transaction was found in a block - if (pblock) - wtx.SetMerkleBranch(*pblock); + if (posInBlock != -1) + wtx.SetMerkleBranch(pIndex, posInBlock); - // Do not flush the wallet here for performance reasons - // this is safe, as in case of a crash, we rescan the necessary blocks on startup through our SetBestChain-mechanism - CWalletDB walletdb(strWalletFile, "r+", false); - - return AddToWallet(wtx, false, &walletdb); + return AddToWallet(wtx, false); } } return false; @@ -1268,11 +1269,11 @@ void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx) fAnonymizableTallyCachedNonDenom = false; } -void CWallet::SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, const CBlock* pblock) +void CWallet::SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, int posInBlock) { LOCK2(cs_main, cs_wallet); - if (!AddToWalletIfInvolvingMe(tx, pblock, true)) + if (!AddToWalletIfInvolvingMe(tx, pindex, posInBlock, true)) return; // Not one of ours // If a transaction changes 'conflicted' state, that changes the balance @@ -1430,7 +1431,7 @@ isminetype CWallet::IsMine(const CTxOut& txout) const CAmount CWallet::GetCredit(const CTxOut& txout, const isminefilter& filter) const { if (!MoneyRange(txout.nValue)) - throw std::runtime_error("CWallet::GetCredit(): value out of range"); + throw std::runtime_error(std::string(__func__) + ": value out of range"); return ((IsMine(txout) & filter) ? txout.nValue : 0); } @@ -1459,7 +1460,7 @@ bool CWallet::IsChange(const CTxOut& txout) const CAmount CWallet::GetChange(const CTxOut& txout) const { if (!MoneyRange(txout.nValue)) - throw std::runtime_error("CWallet::GetChange(): value out of range"); + throw std::runtime_error(std::string(__func__) + ": value out of range"); return (IsChange(txout) ? txout.nValue : 0); } @@ -1582,7 +1583,7 @@ CAmount CWallet::GetDebit(const CTransaction& tx, const isminefilter& filter) co { nDebit += GetDebit(txin, filter); if (!MoneyRange(nDebit)) - throw std::runtime_error("CWallet::GetDebit(): value out of range"); + throw std::runtime_error(std::string(__func__) + ": value out of range"); } return nDebit; } @@ -1594,7 +1595,7 @@ CAmount CWallet::GetCredit(const CTransaction& tx, const isminefilter& filter) c { nCredit += GetCredit(txout, filter); if (!MoneyRange(nCredit)) - throw std::runtime_error("CWallet::GetCredit(): value out of range"); + throw std::runtime_error(std::string(__func__) + ": value out of range"); } return nCredit; } @@ -1606,7 +1607,7 @@ CAmount CWallet::GetChange(const CTransaction& tx) const { nChange += GetChange(txout); if (!MoneyRange(nChange)) - throw std::runtime_error("CWallet::GetChange(): value out of range"); + throw std::runtime_error(std::string(__func__) + ": value out of range"); } return nChange; } @@ -1777,9 +1778,10 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) CBlock block; ReadBlockFromDisk(block, pindex, Params().GetConsensus()); - BOOST_FOREACH(CTransaction& tx, block.vtx) + int posInBlock; + for (posInBlock = 0; posInBlock < (int)block.vtx.size(); posInBlock++) { - if (AddToWalletIfInvolvingMe(tx, &block, fUpdate)) + if (AddToWalletIfInvolvingMe(block.vtx[posInBlock], pindex, posInBlock, fUpdate)) ret++; } pindex = chainActive.Next(pindex); @@ -1957,7 +1959,7 @@ CAmount CWalletTx::GetAvailableCredit(bool fUseCache) const const CTxOut &txout = vout[i]; nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE); if (!MoneyRange(nCredit)) - throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range"); + throw std::runtime_error(std::string(__func__) + ": value out of range"); } } @@ -2000,7 +2002,7 @@ CAmount CWalletTx::GetAvailableWatchOnlyCredit(const bool& fUseCache) const const CTxOut &txout = vout[i]; nCredit += pwallet->GetCredit(txout, ISMINE_WATCH_ONLY); if (!MoneyRange(nCredit)) - throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range"); + throw std::runtime_error(std::string(__func__) + ": value out of range"); } } @@ -2034,7 +2036,7 @@ CAmount CWalletTx::GetAnonymizedCredit(bool fUseCache) const if(nRounds >= privateSendClient.nPrivateSendRounds){ nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE); if (!MoneyRange(nCredit)) - throw std::runtime_error("CWalletTx::GetAnonymizedCredit() : value out of range"); + throw std::runtime_error(std::string(__func__) + ": value out of range"); } } @@ -2075,7 +2077,7 @@ CAmount CWalletTx::GetDenominatedCredit(bool unconfirmed, bool fUseCache) const nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE); if (!MoneyRange(nCredit)) - throw std::runtime_error("CWalletTx::GetDenominatedCredit() : value out of range"); + throw std::runtime_error(std::string(__func__) + ": value out of range"); } if(unconfirmed) { @@ -3608,17 +3610,12 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CCon LOCK2(cs_main, cs_wallet); LogPrintf("CommitTransaction:\n%s", wtxNew.ToString()); { - // This is only to keep the database open to defeat the auto-flush for the - // duration of this scope. This is the only place where this optimization - // maybe makes sense; please don't do it anywhere else. - CWalletDB* pwalletdb = fFileBacked ? new CWalletDB(strWalletFile,"r+") : NULL; - // Take key pair from key pool so it won't be used again reservekey.KeepKey(); // Add tx to wallet, because if it has change it's also ours, // otherwise just for transaction history. - AddToWallet(wtxNew, false, pwalletdb); + AddToWallet(wtxNew); // Notify that old coins are spent set updated_hahes; @@ -3632,8 +3629,6 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CCon NotifyTransactionChanged(this, txin.prevout.hash, CT_UPDATED); updated_hahes.insert(txin.prevout.hash); } - if (fFileBacked) - delete pwalletdb; } // Track how many getdata requests our transaction gets @@ -3925,7 +3920,7 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize) } // TODO: implement keypools for all accounts? if (!walletdb.WritePool(nEnd, CKeyPool(GenerateNewKey(0, fInternal), fInternal))) - throw runtime_error("TopUpKeyPool(): writing generated key failed"); + throw runtime_error(std::string(__func__) + ": writing generated key failed"); if (fInternal) { setInternalKeyPool.insert(nEnd); @@ -4276,7 +4271,7 @@ static void LoadReserveKeysToSet(std::set& setAddress, const std::set &mapKeyBirth) const { mapKeyBirth[it->first] = it->second.nCreateTime; // map in which we'll infer heights of other keys - CBlockIndex *pindexMax = chainActive[std::max(0, chainActive.Height() - 144)]; // the tip can be reorganised; use a 144-block safety margin + CBlockIndex *pindexMax = chainActive[std::max(0, chainActive.Height() - 144)]; // the tip can be reorganized; use a 144-block safety margin std::map mapKeyFirstBlock; std::set setKeys; GetKeys(setKeys); @@ -4966,31 +4961,19 @@ CWalletKey::CWalletKey(int64_t nExpires) nTimeExpires = nExpires; } -int CMerkleTx::SetMerkleBranch(const CBlock& block) +int CMerkleTx::SetMerkleBranch(const CBlockIndex* pindex, int posInBlock) { AssertLockHeld(cs_main); CBlock blockTmp; // Update the tx's hashBlock - hashBlock = block.GetHash(); + hashBlock = pindex->GetBlockHash(); - // Locate the transaction - for (nIndex = 0; nIndex < (int)block.vtx.size(); nIndex++) - if (block.vtx[nIndex] == *(CTransaction*)this) - break; - if (nIndex == (int)block.vtx.size()) - { - nIndex = -1; - LogPrintf("ERROR: SetMerkleBranch(): couldn't find tx in block\n"); - return 0; - } + // set the position of the transaction in the block + nIndex = posInBlock; // Is the tx in a block that's in the main chain - BlockMap::iterator mi = mapBlockIndex.find(hashBlock); - if (mi == mapBlockIndex.end()) - return 0; - const CBlockIndex* pindex = (*mi).second; - if (!pindex || !chainActive.Contains(pindex)) + if (!chainActive.Contains(pindex)) return 0; return chainActive.Height() - pindex->nHeight + 1; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 34aa643da9ee..65f91ecf2e59 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -241,7 +241,7 @@ class CMerkleTx : public CTransaction READWRITE(nIndex); } - int SetMerkleBranch(const CBlock& block); + int SetMerkleBranch(const CBlockIndex* pIndex, int posInBlock); /** * Return depth of transaction in blockchain: @@ -651,6 +651,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface /* HD derive new child key (on internal or external chain) */ void DeriveNewChildKey(const CKeyMetadata& metadata, CKey& secretRet, uint32_t nAccountIndex, bool fInternal /*= false*/); + bool fFileBacked; public: /* * Main wallet lock. @@ -661,7 +662,6 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface */ mutable CCriticalSection cs_wallet; - bool fFileBacked; const std::string strWalletFile; void LoadKeyPool(int nIndex, const CKeyPool &keypool) @@ -855,9 +855,10 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface bool GetAccountPubkey(CPubKey &pubKey, std::string strAccount, bool bForceNew = false); void MarkDirty(); - bool AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletDB* pwalletdb); - void SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, const CBlock* pblock); - bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate); + bool AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose=true); + bool LoadToWallet(const CWalletTx& wtxIn); + void SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, int posInBlock); + bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate); int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false); void ReacceptWalletTransactions(); void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman); diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index b2b2a77f732a..9b452038c00e 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -215,7 +215,7 @@ void CWalletDB::ListAccountCreditDebit(const string& strAccount, listclose(); - throw runtime_error("CWalletDB::ListAccountCreditDebit(): error scanning DB"); + throw runtime_error(std::string(__func__) + ": error scanning DB"); } // Unserialize @@ -400,7 +400,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, if (wtx.nOrderPos == -1) wss.fAnyUnordered = true; - pwallet->AddToWallet(wtx, true, NULL); + pwallet->LoadToWallet(wtx); } else if (strType == "acentry") { diff --git a/src/zmq/zmqnotificationinterface.cpp b/src/zmq/zmqnotificationinterface.cpp index 30f180ddaca1..09bfea87dd63 100644 --- a/src/zmq/zmqnotificationinterface.cpp +++ b/src/zmq/zmqnotificationinterface.cpp @@ -146,7 +146,7 @@ void CZMQNotificationInterface::UpdatedBlockTip(const CBlockIndex *pindexNew, co } } -void CZMQNotificationInterface::SyncTransaction(const CTransaction& tx, const CBlockIndex* pindex, const CBlock* pblock) +void CZMQNotificationInterface::SyncTransaction(const CTransaction& tx, const CBlockIndex* pindex, int posInBlock) { for (std::list::iterator i = notifiers.begin(); i!=notifiers.end(); ) { diff --git a/src/zmq/zmqnotificationinterface.h b/src/zmq/zmqnotificationinterface.h index 264f34c591a3..f9087558271b 100644 --- a/src/zmq/zmqnotificationinterface.h +++ b/src/zmq/zmqnotificationinterface.h @@ -24,7 +24,7 @@ class CZMQNotificationInterface : public CValidationInterface void Shutdown(); // CValidationInterface - void SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, const CBlock* pblock); + void SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, int posInBlock); void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload); void NotifyTransactionLock(const CTransaction &tx);