aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml8
-rw-r--r--Makefile.am11
-rw-r--r--build-aux/m4/bitcoin_qt.m4234
-rw-r--r--build_msvc/libsecp256k1_config.h3
-rwxr-xr-xci/test/00_setup_env.sh1
-rw-r--r--ci/test/00_setup_env_mac_host.sh1
-rw-r--r--ci/test/00_setup_env_native_qt5.sh1
-rw-r--r--ci/test/00_setup_env_win64.sh1
-rwxr-xr-xci/test/05_before_script.sh4
-rwxr-xr-xci/test/06_script_b.sh8
-rw-r--r--configure.ac7
-rwxr-xr-xcontrib/devtools/test-security-check.py7
-rw-r--r--contrib/testgen/base58.py2
-rw-r--r--depends/config.site.in9
-rw-r--r--depends/patches/qt/fix_qt_pkgconfig.patch16
-rw-r--r--src/Makefile.am2
-rw-r--r--src/Makefile.test.include7
-rw-r--r--src/chainparams.cpp1
-rw-r--r--src/fs.cpp5
-rw-r--r--src/interfaces/chain.h1
-rw-r--r--src/key.cpp78
-rw-r--r--src/net_processing.cpp30
-rw-r--r--src/noui.cpp28
-rw-r--r--src/qt/bitcoingui.cpp7
-rw-r--r--src/qt/coincontroldialog.cpp2
-rw-r--r--src/qt/rpcconsole.cpp1
-rw-r--r--src/qt/transactionrecord.cpp1
-rw-r--r--src/qt/walletcontroller.cpp4
-rw-r--r--src/rpc/mining.cpp2
-rw-r--r--src/secp256k1/.gitignore1
-rw-r--r--src/secp256k1/.travis.yml72
-rw-r--r--src/secp256k1/Makefile.am62
-rw-r--r--src/secp256k1/README.md65
-rw-r--r--src/secp256k1/SECURITY.md15
-rw-r--r--src/secp256k1/build-aux/m4/ax_jni_include_dir.m4145
-rw-r--r--src/secp256k1/build-aux/m4/bitcoin_secp.m43
-rw-r--r--src/secp256k1/configure.ac186
-rw-r--r--src/secp256k1/contrib/lax_der_parsing.c6
-rwxr-xr-xsrc/secp256k1/contrib/travis.sh65
-rw-r--r--src/secp256k1/include/secp256k1.h202
-rw-r--r--src/secp256k1/include/secp256k1_ecdh.h37
-rw-r--r--src/secp256k1/include/secp256k1_preallocated.h128
-rw-r--r--src/secp256k1/include/secp256k1_recovery.h2
-rw-r--r--src/secp256k1/src/asm/field_10x26_arm.s6
-rw-r--r--src/secp256k1/src/basic-config.h5
-rw-r--r--src/secp256k1/src/bench.h97
-rw-r--r--src/secp256k1/src/bench_ecdh.c15
-rw-r--r--src/secp256k1/src/bench_ecmult.c49
-rw-r--r--src/secp256k1/src/bench_internal.c214
-rw-r--r--src/secp256k1/src/bench_recover.c8
-rw-r--r--src/secp256k1/src/bench_sign.c8
-rw-r--r--src/secp256k1/src/bench_verify.c15
-rw-r--r--src/secp256k1/src/ecdsa_impl.h94
-rw-r--r--src/secp256k1/src/eckey_impl.h12
-rw-r--r--src/secp256k1/src/ecmult.h8
-rw-r--r--src/secp256k1/src/ecmult_const.h7
-rw-r--r--src/secp256k1/src/ecmult_const_impl.h49
-rw-r--r--src/secp256k1/src/ecmult_gen.h29
-rw-r--r--src/secp256k1/src/ecmult_gen_impl.h98
-rw-r--r--src/secp256k1/src/ecmult_impl.h183
-rw-r--r--src/secp256k1/src/field.h10
-rw-r--r--src/secp256k1/src/field_10x26_impl.h21
-rw-r--r--src/secp256k1/src/field_5x52_impl.h21
-rw-r--r--src/secp256k1/src/field_impl.h2
-rw-r--r--src/secp256k1/src/gen_context.c33
-rw-r--r--src/secp256k1/src/group.h13
-rw-r--r--src/secp256k1/src/group_impl.h55
-rw-r--r--src/secp256k1/src/hash_impl.h3
-rw-r--r--src/secp256k1/src/java/org/bitcoin/NativeSecp256k1.java446
-rw-r--r--src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java226
-rw-r--r--src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Util.java45
-rw-r--r--src/secp256k1/src/java/org/bitcoin/Secp256k1Context.java51
-rw-r--r--src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.c379
-rw-r--r--src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.h119
-rw-r--r--src/secp256k1/src/java/org_bitcoin_Secp256k1Context.c15
-rw-r--r--src/secp256k1/src/java/org_bitcoin_Secp256k1Context.h22
-rw-r--r--src/secp256k1/src/modules/ecdh/main_impl.h46
-rw-r--r--[-rwxr-xr-x]src/secp256k1/src/modules/recovery/main_impl.h39
-rw-r--r--src/secp256k1/src/modules/recovery/tests_impl.h2
-rw-r--r--src/secp256k1/src/scalar.h13
-rw-r--r--src/secp256k1/src/scalar_4x64_impl.h11
-rw-r--r--src/secp256k1/src/scalar_8x32_impl.h15
-rw-r--r--src/secp256k1/src/scalar_impl.h11
-rw-r--r--src/secp256k1/src/scalar_low.h2
-rw-r--r--src/secp256k1/src/scalar_low_impl.h13
-rw-r--r--src/secp256k1/src/scratch.h31
-rw-r--r--src/secp256k1/src/scratch_impl.h92
-rw-r--r--src/secp256k1/src/secp256k1.c312
-rw-r--r--src/secp256k1/src/tests.c679
-rw-r--r--src/secp256k1/src/tests_exhaustive.c6
-rw-r--r--src/secp256k1/src/util.h90
-rw-r--r--src/secp256k1/src/valgrind_ctime_test.c119
-rw-r--r--src/test/fuzz/crypto_common.cpp70
-rw-r--r--src/test/fuzz/util.h14
-rw-r--r--src/ui_interface.h3
-rw-r--r--src/validation.cpp24
-rw-r--r--src/wallet/bdb.cpp805
-rw-r--r--src/wallet/bdb.h337
-rw-r--r--src/wallet/db.cpp740
-rw-r--r--src/wallet/db.h379
-rw-r--r--src/wallet/rpcdump.cpp111
-rw-r--r--src/wallet/rpcwallet.cpp476
-rw-r--r--src/wallet/rpcwallet.h1
-rw-r--r--src/wallet/test/db_tests.cpp2
-rw-r--r--src/wallet/wallet.cpp13
-rw-r--r--src/wallet/wallet.h1
-rw-r--r--src/wallet/walletdb.cpp5
-rw-r--r--src/wallet/walletdb.h4
-rwxr-xr-xtest/functional/feature_backwards_compatibility.py3
-rwxr-xr-xtest/functional/mempool_compatibility.py75
-rwxr-xr-xtest/functional/p2p_compactblocks.py7
-rwxr-xr-xtest/functional/p2p_eviction.py129
-rwxr-xr-xtest/functional/p2p_filter.py166
-rwxr-xr-xtest/functional/p2p_invalid_messages.py186
-rwxr-xr-xtest/functional/p2p_mempool.py34
-rwxr-xr-xtest/functional/p2p_nobloomfilter_messages.py47
-rwxr-xr-xtest/functional/rpc_createmultisig.py14
-rwxr-xr-xtest/functional/rpc_help.py7
-rw-r--r--test/functional/test_framework/address.py71
-rw-r--r--test/functional/test_framework/base58.py70
-rw-r--r--test/functional/test_framework/key.py13
-rwxr-xr-xtest/functional/test_framework/mininode.py6
-rwxr-xr-xtest/functional/test_framework/test_framework.py12
-rw-r--r--test/functional/test_framework/util.py64
-rwxr-xr-xtest/functional/test_framework/wallet_util.py17
-rwxr-xr-xtest/functional/test_runner.py6
-rwxr-xr-xtest/functional/wallet_upgradewallet.py4
127 files changed, 4603 insertions, 4623 deletions
diff --git a/.travis.yml b/.travis.yml
index 03ab9d2613..283df78633 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -53,14 +53,16 @@ before_install:
install:
- set -o errexit; source ./ci/test/04_install.sh
before_script:
- - set -o errexit; source ./ci/test/05_before_script.sh
+ # Temporary workaround for https://github.com/bitcoin/bitcoin/issues/16368
+ - for i in {1..4}; do echo "$(sleep 500)" ; done &
+ - set -o errexit; source ./ci/test/05_before_script.sh &> "/dev/null"
script:
- export CONTINUE=1
- if [ $SECONDS -gt 1200 ]; then export CONTINUE=0; fi # Likely the depends build took very long
- - if [ $TRAVIS_REPO_SLUG = "bitcoin/bitcoin" ]; then export CONTINUE=1; fi # Whitelisted repo (90 minutes build time)
+ - if [ $TRAVIS_REPO_SLUG = "bitcoin/bitcoin" ]; then export CONTINUE=1; fi # continue on repos with extended build time (90 minutes)
- if [ $CONTINUE = "1" ]; then set -o errexit; source ./ci/test/06_script_a.sh; else set +o errexit; echo "$CACHE_ERR_MSG"; false; fi
- if [ $SECONDS -gt 2000 ]; then export CONTINUE=0; fi # Likely the build took very long; The tests take about 1000s, so we should abort if we have less than 50*60-1000=2000s left
- - if [ $TRAVIS_REPO_SLUG = "bitcoin/bitcoin" ]; then export CONTINUE=1; fi # Whitelisted repo (90 minutes build time)
+ - if [ $TRAVIS_REPO_SLUG = "bitcoin/bitcoin" ]; then export CONTINUE=1; fi # continue on repos with extended build time (90 minutes)
- if [ $CONTINUE = "1" ]; then set -o errexit; source ./ci/test/06_script_b.sh; else set +o errexit; echo "$CACHE_ERR_MSG"; false; fi
after_script:
- echo $TRAVIS_COMMIT_RANGE
diff --git a/Makefile.am b/Makefile.am
index 45dab3930d..75a164f49e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -342,3 +342,14 @@ clean-local: clean-docs
rm -rf coverage_percent.txt test_bitcoin.coverage/ total.coverage/ fuzz.coverage/ test/tmp/ cache/ $(OSX_APP)
rm -rf test/functional/__pycache__ test/functional/test_framework/__pycache__ test/cache share/rpcauth/__pycache__
rm -rf osx_volname dist/ dpi36.background.tiff dpi72.background.tiff
+
+test-security-check:
+if TARGET_DARWIN
+ $(AM_V_at) $(PYTHON) $(top_srcdir)/contrib/devtools/test-security-check.py TestSecurityChecks.test_MACHO
+endif
+if TARGET_WINDOWS
+ $(AM_V_at) $(PYTHON) $(top_srcdir)/contrib/devtools/test-security-check.py TestSecurityChecks.test_PE
+endif
+if TARGET_LINUX
+ $(AM_V_at) $(PYTHON) $(top_srcdir)/contrib/devtools/test-security-check.py TestSecurityChecks.test_ELF
+endif
diff --git a/build-aux/m4/bitcoin_qt.m4 b/build-aux/m4/bitcoin_qt.m4
index 83d054af5f..e171238cbc 100644
--- a/build-aux/m4/bitcoin_qt.m4
+++ b/build-aux/m4/bitcoin_qt.m4
@@ -79,25 +79,12 @@ AC_DEFUN([BITCOIN_QT_INIT],[
AC_SUBST(QT_TRANSLATION_DIR,$qt_translation_path)
])
-dnl Find the appropriate version of Qt libraries and includes.
-dnl Inputs: $1: Whether or not pkg-config should be used. yes|no. Default: yes.
-dnl Inputs: $2: If $1 is "yes" and --with-gui=auto, which qt version should be
-dnl tried first.
-dnl Outputs: See _BITCOIN_QT_FIND_LIBS_*
+dnl Find Qt libraries and includes.
+dnl Outputs: See _BITCOIN_QT_FIND_LIBS
dnl Outputs: Sets variables for all qt-related tools.
dnl Outputs: bitcoin_enable_qt, bitcoin_enable_qt_dbus, bitcoin_enable_qt_test
AC_DEFUN([BITCOIN_QT_CONFIGURE],[
- use_pkgconfig=$1
-
- if test "x$use_pkgconfig" = x; then
- use_pkgconfig=yes
- fi
-
- if test "x$use_pkgconfig" = xyes; then
- BITCOIN_QT_CHECK([_BITCOIN_QT_FIND_LIBS_WITH_PKGCONFIG])
- else
- BITCOIN_QT_CHECK([_BITCOIN_QT_FIND_LIBS_WITHOUT_PKGCONFIG])
- fi
+ BITCOIN_QT_CHECK([_BITCOIN_QT_FIND_LIBS])
dnl This is ugly and complicated. Yuck. Works as follows:
dnl For Qt5, we can check a header to find out whether Qt is build
@@ -117,8 +104,8 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[
_BITCOIN_QT_FIND_STATIC_PLUGINS
AC_DEFINE(QT_STATICPLUGIN, 1, [Define this symbol if qt plugins are static])
if test "x$TARGET_OS" != xandroid; then
- _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(QMinimalIntegrationPlugin)],[-lqminimal])
- AC_DEFINE(QT_QPA_PLATFORM_MINIMAL, 1, [Define this symbol if the minimal qt platform exists])
+ _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(QMinimalIntegrationPlugin)],[-lqminimal])
+ AC_DEFINE(QT_QPA_PLATFORM_MINIMAL, 1, [Define this symbol if the minimal qt platform exists])
fi
if test "x$TARGET_OS" = xwindows; then
_BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin)],[-lqwindows])
@@ -139,7 +126,7 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[
CXXFLAGS=$TEMP_CXXFLAGS
])
- if test "x$use_pkgconfig$qt_bin_path" = xyes; then
+ if test "x$qt_bin_path" = x; then
qt_bin_path="`$PKG_CONFIG --variable=host_bins Qt5Core 2>/dev/null`"
fi
@@ -213,7 +200,7 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[
dnl enable qt support
- AC_MSG_CHECKING(whether to build ]AC_PACKAGE_NAME[ GUI)
+ AC_MSG_CHECKING([whether to build ]AC_PACKAGE_NAME[ GUI])
BITCOIN_QT_CHECK([
bitcoin_enable_qt=yes
bitcoin_enable_qt_test=yes
@@ -255,57 +242,15 @@ dnl All macros below are internal and should _not_ be used from the main
dnl configure.ac.
dnl ----
-dnl Internal. Check included version of Qt against minimum specified in doc/dependencies.md
-dnl Requires: INCLUDES must be populated as necessary.
-dnl Output: bitcoin_cv_qt5=yes|no
-AC_DEFUN([_BITCOIN_QT_CHECK_QT5],[
- AC_CACHE_CHECK(for Qt 5, bitcoin_cv_qt5,[
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
- #include <QtCore/qconfig.h>
- #ifndef QT_VERSION
- # include <QtCore/qglobal.h>
- #endif
- ]],
- [[
- #if QT_VERSION < 0x050501
- choke
- #endif
- ]])],
- [bitcoin_cv_qt5=yes],
- [bitcoin_cv_qt5=no])
-])])
-
-dnl Internal. Check if the included version of Qt is greater than Qt58.
-dnl Requires: INCLUDES must be populated as necessary.
-dnl Output: bitcoin_cv_qt58=yes|no
-AC_DEFUN([_BITCOIN_QT_CHECK_QT58],[
- AC_CACHE_CHECK(for > Qt 5.7, bitcoin_cv_qt58,[
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
- #include <QtCore/qconfig.h>
- #ifndef QT_VERSION
- # include <QtCore/qglobal.h>
- #endif
- ]],
- [[
- #if QT_VERSION_MINOR < 8
- choke
- #endif
- ]])],
- [bitcoin_cv_qt58=yes],
- [bitcoin_cv_qt58=no])
-])])
-
-
dnl Internal. Check if the linked version of Qt was built as static libs.
dnl Requires: Qt5.
dnl Requires: INCLUDES and LIBS must be populated as necessary.
dnl Output: bitcoin_cv_static_qt=yes|no
-dnl Output: Defines QT_STATICPLUGIN if plugins are static.
AC_DEFUN([_BITCOIN_QT_IS_STATIC],[
AC_CACHE_CHECK(for static Qt, bitcoin_cv_static_qt,[
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#include <QtCore/qconfig.h>
- #ifndef QT_VERSION OR QT_VERSION_STR
+ #ifndef QT_VERSION
# include <QtCore/qglobal.h>
#endif
]],
@@ -317,9 +262,6 @@ AC_DEFUN([_BITCOIN_QT_IS_STATIC],[
[bitcoin_cv_static_qt=yes],
[bitcoin_cv_static_qt=no])
])
- if test "x$bitcoin_cv_static_qt" = xyes; then
- AC_DEFINE(QT_STATICPLUGIN, 1, [Define this symbol for static Qt plugins])
- fi
])
dnl Internal. Check if the link-requirements for static plugins are met.
@@ -353,70 +295,32 @@ AC_DEFUN([_BITCOIN_QT_FIND_STATIC_PLUGINS],[
if test -d "$qt_plugin_path/platforms/android"; then
QT_LIBS="$QT_LIBS -L$qt_plugin_path/platforms/android -lqtfreetype -lEGL"
fi
- if test "x$use_pkgconfig" = xyes; then
- : dnl
- m4_ifdef([PKG_CHECK_MODULES],[
- if test x$bitcoin_cv_qt58 = xno; then
- PKG_CHECK_MODULES([QTPLATFORM], [Qt5PlatformSupport], [QT_LIBS="$QTPLATFORM_LIBS $QT_LIBS"])
- else
- PKG_CHECK_MODULES([QTFONTDATABASE], [Qt5FontDatabaseSupport], [QT_LIBS="-lQt5FontDatabaseSupport $QT_LIBS"])
- PKG_CHECK_MODULES([QTEVENTDISPATCHER], [Qt5EventDispatcherSupport], [QT_LIBS="-lQt5EventDispatcherSupport $QT_LIBS"])
- PKG_CHECK_MODULES([QTTHEME], [Qt5ThemeSupport], [QT_LIBS="-lQt5ThemeSupport $QT_LIBS"])
- PKG_CHECK_MODULES([QTDEVICEDISCOVERY], [Qt5DeviceDiscoverySupport], [QT_LIBS="-lQt5DeviceDiscoverySupport $QT_LIBS"])
- PKG_CHECK_MODULES([QTACCESSIBILITY], [Qt5AccessibilitySupport], [QT_LIBS="-lQt5AccessibilitySupport $QT_LIBS"])
- PKG_CHECK_MODULES([QTFB], [Qt5FbSupport], [QT_LIBS="-lQt5FbSupport $QT_LIBS"])
- fi
- if test "x$TARGET_OS" = xlinux; then
- PKG_CHECK_MODULES([QTXCBQPA], [Qt5XcbQpa], [QT_LIBS="$QTXCBQPA_LIBS $QT_LIBS"])
- elif test "x$TARGET_OS" = xdarwin; then
- PKG_CHECK_MODULES([QTCLIPBOARD], [Qt5ClipboardSupport], [QT_LIBS="-lQt5ClipboardSupport $QT_LIBS"])
- PKG_CHECK_MODULES([QTGRAPHICS], [Qt5GraphicsSupport], [QT_LIBS="-lQt5GraphicsSupport $QT_LIBS"])
- PKG_CHECK_MODULES([QTCGL], [Qt5CglSupport], [QT_LIBS="-lQt5CglSupport $QT_LIBS"])
- fi
- ])
- else
- if test "x$TARGET_OS" = xwindows; then
- AC_CACHE_CHECK(for Qt >= 5.6, bitcoin_cv_need_platformsupport,[
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
- #include <QtCore/qconfig.h>
- #ifndef QT_VERSION
- # include <QtCore/qglobal.h>
- #endif
- ]],
- [[
- #if QT_VERSION < 0x050600 || QT_VERSION_MINOR < 6
- choke
- #endif
- ]])],
- [bitcoin_cv_need_platformsupport=yes],
- [bitcoin_cv_need_platformsupport=no])
- ])
- if test "x$bitcoin_cv_need_platformsupport" = xyes; then
- if test x$bitcoin_cv_qt58 = xno; then
- BITCOIN_QT_CHECK(AC_CHECK_LIB([${QT_LIB_PREFIX}PlatformSupport],[main],,BITCOIN_QT_FAIL(lib$QT_LIB_PREFIXPlatformSupport not found)))
- else
- BITCOIN_QT_CHECK(AC_CHECK_LIB([${QT_LIB_PREFIX}FontDatabaseSupport],[main],,BITCOIN_QT_FAIL(lib$QT_LIB_PREFIXFontDatabaseSupport not found)))
- BITCOIN_QT_CHECK(AC_CHECK_LIB([${QT_LIB_PREFIX}EventDispatcherSupport],[main],,BITCOIN_QT_FAIL(lib$QT_LIB_PREFIXEventDispatcherSupport not found)))
- BITCOIN_QT_CHECK(AC_CHECK_LIB([${QT_LIB_PREFIX}ThemeSupport],[main],,BITCOIN_QT_FAIL(lib$QT_LIB_PREFIXThemeSupport not found)))
- BITCOIN_QT_CHECK(AC_CHECK_LIB([${QT_LIB_PREFIX}FbSupport],[main],,BITCOIN_QT_FAIL(lib$QT_LIB_PREFIXFbSupport not found)))
- BITCOIN_QT_CHECK(AC_CHECK_LIB([${QT_LIB_PREFIX}DeviceDiscoverySupport],[main],,BITCOIN_QT_FAIL(lib$QT_LIB_PREFIXDeviceDiscoverySupport not found)))
- BITCOIN_QT_CHECK(AC_CHECK_LIB([${QT_LIB_PREFIX}AccessibilitySupport],[main],,BITCOIN_QT_FAIL(lib$QT_LIB_PREFIXAccessibilitySupport not found)))
- QT_LIBS="$QT_LIBS -lversion -ldwmapi -luxtheme"
- fi
- fi
- fi
- fi
- fi
+ m4_ifdef([PKG_CHECK_MODULES],[
+ if test x$bitcoin_cv_qt58 = xno; then
+ PKG_CHECK_MODULES([QTPLATFORM], [Qt5PlatformSupport], [QT_LIBS="$QTPLATFORM_LIBS $QT_LIBS"])
+ else
+ PKG_CHECK_MODULES([QTFONTDATABASE], [Qt5FontDatabaseSupport], [QT_LIBS="-lQt5FontDatabaseSupport $QT_LIBS"])
+ PKG_CHECK_MODULES([QTEVENTDISPATCHER], [Qt5EventDispatcherSupport], [QT_LIBS="-lQt5EventDispatcherSupport $QT_LIBS"])
+ PKG_CHECK_MODULES([QTTHEME], [Qt5ThemeSupport], [QT_LIBS="-lQt5ThemeSupport $QT_LIBS"])
+ PKG_CHECK_MODULES([QTDEVICEDISCOVERY], [Qt5DeviceDiscoverySupport], [QT_LIBS="-lQt5DeviceDiscoverySupport $QT_LIBS"])
+ PKG_CHECK_MODULES([QTACCESSIBILITY], [Qt5AccessibilitySupport], [QT_LIBS="-lQt5AccessibilitySupport $QT_LIBS"])
+ PKG_CHECK_MODULES([QTFB], [Qt5FbSupport], [QT_LIBS="-lQt5FbSupport $QT_LIBS"])
+ fi
+ if test "x$TARGET_OS" = xlinux; then
+ PKG_CHECK_MODULES([QTXCBQPA], [Qt5XcbQpa], [QT_LIBS="$QTXCBQPA_LIBS $QT_LIBS"])
+ elif test "x$TARGET_OS" = xdarwin; then
+ PKG_CHECK_MODULES([QTCLIPBOARD], [Qt5ClipboardSupport], [QT_LIBS="-lQt5ClipboardSupport $QT_LIBS"])
+ PKG_CHECK_MODULES([QTGRAPHICS], [Qt5GraphicsSupport], [QT_LIBS="-lQt5GraphicsSupport $QT_LIBS"])
+ PKG_CHECK_MODULES([QTCGL], [Qt5CglSupport], [QT_LIBS="-lQt5CglSupport $QT_LIBS"])
+ fi
+ ])
+ fi
])
dnl Internal. Find Qt libraries using pkg-config.
-dnl Inputs: bitcoin_qt_want_version (from --with-gui=). The version to check
-dnl first.
-dnl Inputs: $1: If bitcoin_qt_want_version is "auto", check for this version
-dnl first.
dnl Outputs: All necessary QT_* variables are set.
dnl Outputs: have_qt_test and have_qt_dbus are set (if applicable) to yes|no.
-AC_DEFUN([_BITCOIN_QT_FIND_LIBS_WITH_PKGCONFIG],[
+AC_DEFUN([_BITCOIN_QT_FIND_LIBS],[
m4_ifdef([PKG_CHECK_MODULES],[
QT_LIB_PREFIX=Qt5
qt5_modules="Qt5Core Qt5Gui Qt5Network Qt5Widgets"
@@ -437,83 +341,3 @@ AC_DEFUN([_BITCOIN_QT_FIND_LIBS_WITH_PKGCONFIG],[
])
true; dnl
])
-
-dnl Internal. Find Qt libraries without using pkg-config. Version is deduced
-dnl from the discovered headers.
-dnl Inputs: bitcoin_qt_want_version (from --with-gui=). The version to use.
-dnl If "auto", the version will be discovered by _BITCOIN_QT_CHECK_QT5.
-dnl Outputs: All necessary QT_* variables are set.
-dnl Outputs: have_qt_test and have_qt_dbus are set (if applicable) to yes|no.
-AC_DEFUN([_BITCOIN_QT_FIND_LIBS_WITHOUT_PKGCONFIG],[
- TEMP_CPPFLAGS="$CPPFLAGS"
- TEMP_CXXFLAGS="$CXXFLAGS"
- CXXFLAGS="$PIC_FLAGS $CXXFLAGS"
- TEMP_LIBS="$LIBS"
- BITCOIN_QT_CHECK([
- if test "x$qt_include_path" != x; then
- QT_INCLUDES="-I$qt_include_path -I$qt_include_path/QtCore -I$qt_include_path/QtGui -I$qt_include_path/QtWidgets -I$qt_include_path/QtNetwork -I$qt_include_path/QtTest -I$qt_include_path/QtDBus"
- CPPFLAGS="$QT_INCLUDES $CPPFLAGS"
- fi
- ])
-
- BITCOIN_QT_CHECK([AC_CHECK_HEADER([QtPlugin],,BITCOIN_QT_FAIL(QtCore headers missing))])
- BITCOIN_QT_CHECK([AC_CHECK_HEADER([QApplication],, BITCOIN_QT_FAIL(QtGui headers missing))])
- BITCOIN_QT_CHECK([AC_CHECK_HEADER([QLocalSocket],, BITCOIN_QT_FAIL(QtNetwork headers missing))])
-
- BITCOIN_QT_CHECK([
- if test "x$bitcoin_qt_want_version" = xauto; then
- _BITCOIN_QT_CHECK_QT5
- _BITCOIN_QT_CHECK_QT58
- fi
- QT_LIB_PREFIX=Qt5
- ])
-
- BITCOIN_QT_CHECK([
- LIBS=
- if test "x$qt_lib_path" != x; then
- LIBS="$LIBS -L$qt_lib_path"
- fi
-
- if test "x$TARGET_OS" = xwindows; then
- AC_CHECK_LIB([imm32], [main],, BITCOIN_QT_FAIL(libimm32 not found))
- fi
- ])
-
- BITCOIN_QT_CHECK(AC_CHECK_LIB([z] ,[main],,AC_MSG_WARN([zlib not found. Assuming qt has it built-in])))
- if test x$bitcoin_cv_qt58 = xno; then
- BITCOIN_QT_CHECK(AC_SEARCH_LIBS([png_error] ,[qtpng png],,AC_MSG_WARN([libpng not found. Assuming qt has it built-in])))
- BITCOIN_QT_CHECK(AC_SEARCH_LIBS([pcre16_exec], [qtpcre pcre16],,AC_MSG_WARN([libpcre16 not found. Assuming qt has it built-in])))
- else
- BITCOIN_QT_CHECK(AC_SEARCH_LIBS([png_error] ,[qtlibpng png],,AC_MSG_WARN([libpng not found. Assuming qt has it built-in])))
- BITCOIN_QT_CHECK(AC_SEARCH_LIBS([pcre2_match_16], [qtpcre2 libqtpcre2],,AC_MSG_WARN([libqtpcre2 not found. Assuming qt has it built-in])))
- fi
- BITCOIN_QT_CHECK(AC_SEARCH_LIBS([hb_ot_tags_from_script] ,[qtharfbuzzng qtharfbuzz harfbuzz],,AC_MSG_WARN([libharfbuzz not found. Assuming qt has it built-in or support is disabled])))
- BITCOIN_QT_CHECK(AC_CHECK_LIB([${QT_LIB_PREFIX}Core] ,[main],,BITCOIN_QT_FAIL(lib${QT_LIB_PREFIX}Core not found)))
- BITCOIN_QT_CHECK(AC_CHECK_LIB([${QT_LIB_PREFIX}Gui] ,[main],,BITCOIN_QT_FAIL(lib${QT_LIB_PREFIX}Gui not found)))
- BITCOIN_QT_CHECK(AC_CHECK_LIB([${QT_LIB_PREFIX}Network],[main],,BITCOIN_QT_FAIL(lib${QT_LIB_PREFIX}Network not found)))
- BITCOIN_QT_CHECK(AC_CHECK_LIB([${QT_LIB_PREFIX}Widgets],[main],,BITCOIN_QT_FAIL(lib${QT_LIB_PREFIX}Widgets not found)))
- QT_LIBS="$LIBS"
- LIBS="$TEMP_LIBS"
-
- BITCOIN_QT_CHECK([
- LIBS=
- if test "x$qt_lib_path" != x; then
- LIBS="-L$qt_lib_path"
- fi
- AC_CHECK_LIB([${QT_LIB_PREFIX}Test], [main],, have_qt_test=no)
- AC_CHECK_HEADER([QTest],, have_qt_test=no)
- QT_TEST_LIBS="$LIBS"
- if test "x$use_dbus" != xno; then
- LIBS=
- if test "x$qt_lib_path" != x; then
- LIBS="-L$qt_lib_path"
- fi
- AC_CHECK_LIB([${QT_LIB_PREFIX}DBus], [main],, have_qt_dbus=no)
- AC_CHECK_HEADER([QtDBus],, have_qt_dbus=no)
- QT_DBUS_LIBS="$LIBS"
- fi
- ])
- CPPFLAGS="$TEMP_CPPFLAGS"
- CXXFLAGS="$TEMP_CXXFLAGS"
- LIBS="$TEMP_LIBS"
-])
diff --git a/build_msvc/libsecp256k1_config.h b/build_msvc/libsecp256k1_config.h
index 5187c946a0..5978b9a0d9 100644
--- a/build_msvc/libsecp256k1_config.h
+++ b/build_msvc/libsecp256k1_config.h
@@ -26,4 +26,7 @@
#define USE_FIELD_10X26 1
#define USE_SCALAR_8X32 1
+#define ECMULT_GEN_PREC_BITS 4
+#define ECMULT_WINDOW_SIZE 15
+
#endif /* BITCOIN_LIBSECP256K1_CONFIG_H */
diff --git a/ci/test/00_setup_env.sh b/ci/test/00_setup_env.sh
index 56b8fc976e..711f436630 100755
--- a/ci/test/00_setup_env.sh
+++ b/ci/test/00_setup_env.sh
@@ -33,6 +33,7 @@ export HOST=${HOST:-$("$BASE_ROOT_DIR/depends/config.guess")}
export USE_BUSY_BOX=${USE_BUSY_BOX:-false}
export RUN_UNIT_TESTS=${RUN_UNIT_TESTS:-true}
export RUN_FUNCTIONAL_TESTS=${RUN_FUNCTIONAL_TESTS:-true}
+export RUN_SECURITY_TESTS=${RUN_SECURITY_TESTS:-false}
export TEST_RUNNER_ENV=${TEST_RUNNER_ENV:-}
export RUN_FUZZ_TESTS=${RUN_FUZZ_TESTS:-false}
export CONTAINER_NAME=${CONTAINER_NAME:-ci_unnamed}
diff --git a/ci/test/00_setup_env_mac_host.sh b/ci/test/00_setup_env_mac_host.sh
index 982e38daee..b24dc7278d 100644
--- a/ci/test/00_setup_env_mac_host.sh
+++ b/ci/test/00_setup_env_mac_host.sh
@@ -11,6 +11,7 @@ export PIP_PACKAGES="zmq"
export GOAL="install"
export BITCOIN_CONFIG="--enable-gui --enable-reduce-exports --enable-werror"
export TEST_RUNNER_EXTRA="wallet_disable" # Only run wallet_disable as a smoke test, see https://github.com/bitcoin/bitcoin/pull/17240#issuecomment-546022121 why the other tests are disabled
+export RUN_SECURITY_TESTS="true"
# Run without depends
export NO_DEPENDS=1
export OSX_SDK=""
diff --git a/ci/test/00_setup_env_native_qt5.sh b/ci/test/00_setup_env_native_qt5.sh
index fa07990756..496251a125 100644
--- a/ci/test/00_setup_env_native_qt5.sh
+++ b/ci/test/00_setup_env_native_qt5.sh
@@ -10,6 +10,7 @@ export CONTAINER_NAME=ci_native_qt5
export PACKAGES="python3-zmq qtbase5-dev qttools5-dev-tools libdbus-1-dev libharfbuzz-dev"
export DEP_OPTS="NO_QT=1 NO_UPNP=1 DEBUG=1 ALLOW_HOST_PACKAGES=1"
export TEST_RUNNER_EXTRA="--previous-releases --coverage --extended --exclude feature_dbcrash" # Run extended tests so that coverage does not fail, but exclude the very slow dbcrash
+export RUN_SECURITY_TESTS="true"
export RUN_UNIT_TESTS_SEQUENTIAL="true"
export RUN_UNIT_TESTS="false"
export GOAL="install"
diff --git a/ci/test/00_setup_env_win64.sh b/ci/test/00_setup_env_win64.sh
index 8f0c62a1a6..90c3ebd28e 100644
--- a/ci/test/00_setup_env_win64.sh
+++ b/ci/test/00_setup_env_win64.sh
@@ -10,5 +10,6 @@ export CONTAINER_NAME=ci_win64
export HOST=x86_64-w64-mingw32
export PACKAGES="python3 nsis g++-mingw-w64-x86-64 wine-binfmt wine64"
export RUN_FUNCTIONAL_TESTS=false
+export RUN_SECURITY_TESTS="true"
export GOAL="deploy"
export BITCOIN_CONFIG="--enable-reduce-exports --disable-gui-tests"
diff --git a/ci/test/05_before_script.sh b/ci/test/05_before_script.sh
index efaaf154b1..3685504524 100755
--- a/ci/test/05_before_script.sh
+++ b/ci/test/05_before_script.sh
@@ -33,9 +33,7 @@ if [ -z "$NO_DEPENDS" ]; then
else
SHELL_OPTS="CONFIG_SHELL="
fi
- # Temporary workaround for https://github.com/bitcoin/bitcoin/issues/16368
- python3 -c 'import time; [print(".") or time.sleep(500) for _ in range(4)]' &
- ( DOCKER_EXEC $SHELL_OPTS make $MAKEJOBS -C depends HOST=$HOST $DEP_OPTS ) &> /dev/null
+ DOCKER_EXEC $SHELL_OPTS make $MAKEJOBS -C depends HOST=$HOST $DEP_OPTS
fi
if [ -n "$PREVIOUS_RELEASES_TO_DOWNLOAD" ]; then
BEGIN_FOLD previous-versions
diff --git a/ci/test/06_script_b.sh b/ci/test/06_script_b.sh
index 51d84ee39d..96d44328b8 100755
--- a/ci/test/06_script_b.sh
+++ b/ci/test/06_script_b.sh
@@ -21,8 +21,6 @@ if [ -n "$USE_VALGRIND" ]; then
END_FOLD
fi
-bash -c "${CI_WAIT}" & # Print dots in case the tests take a long time to run
-
if [ "$RUN_UNIT_TESTS" = "true" ]; then
BEGIN_FOLD unit-tests
DOCKER_EXEC LD_LIBRARY_PATH=$DEPENDS_DIR/$HOST/lib make $MAKEJOBS check VERBOSE=1
@@ -41,6 +39,12 @@ if [ "$RUN_FUNCTIONAL_TESTS" = "true" ]; then
END_FOLD
fi
+if [ "$RUN_SECURITY_TESTS" = "true" ]; then
+ BEGIN_FOLD security-tests
+ DOCKER_EXEC make test-security-check
+ END_FOLD
+fi
+
if [ "$RUN_FUZZ_TESTS" = "true" ]; then
BEGIN_FOLD fuzz-tests
DOCKER_EXEC LD_LIBRARY_PATH=$DEPENDS_DIR/$HOST/lib test/fuzz/test_runner.py ${FUZZ_TESTS_CONFIG} $MAKEJOBS -l DEBUG ${DIR_FUZZ_IN}
diff --git a/configure.ac b/configure.ac
index 3bfbe2e7b0..474d8a7919 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1139,7 +1139,7 @@ else
BITCOIN_QT_INIT
dnl sets $bitcoin_enable_qt, $bitcoin_enable_qt_test, $bitcoin_enable_qt_dbus
- BITCOIN_QT_CONFIGURE([$use_pkgconfig])
+ BITCOIN_QT_CONFIGURE
fi
if test x$enable_wallet != xno; then
@@ -1600,6 +1600,7 @@ fi
AM_CONDITIONAL([TARGET_DARWIN], [test x$TARGET_OS = xdarwin])
AM_CONDITIONAL([BUILD_DARWIN], [test x$BUILD_OS = xdarwin])
+AM_CONDITIONAL([TARGET_LINUX], [test x$TARGET_OS = xlinux])
AM_CONDITIONAL([TARGET_WINDOWS], [test x$TARGET_OS = xwindows])
AM_CONDITIONAL([ENABLE_WALLET],[test x$enable_wallet = xyes])
AM_CONDITIONAL([ENABLE_TESTS],[test x$BUILD_TEST = xyes])
@@ -1688,6 +1689,8 @@ AC_SUBST(HAVE_WEAK_GETAUXVAL)
AC_CONFIG_FILES([Makefile src/Makefile doc/man/Makefile share/setup.nsi share/qt/Info.plist test/config.ini])
AC_CONFIG_FILES([contrib/devtools/split-debug.sh],[chmod +x contrib/devtools/split-debug.sh])
AM_COND_IF([HAVE_DOXYGEN], [AC_CONFIG_FILES([doc/Doxyfile])])
+AC_CONFIG_LINKS([contrib/devtools/security-check.py:contrib/devtools/security-check.py])
+AC_CONFIG_LINKS([contrib/devtools/test-security-check.py:contrib/devtools/test-security-check.py])
AC_CONFIG_LINKS([contrib/filter-lcov.py:contrib/filter-lcov.py])
AC_CONFIG_LINKS([test/functional/test_runner.py:test/functional/test_runner.py])
AC_CONFIG_LINKS([test/fuzz/test_runner.py:test/fuzz/test_runner.py])
@@ -1721,7 +1724,7 @@ if test x$need_bundled_univalue = xyes; then
AC_CONFIG_SUBDIRS([src/univalue])
fi
-ac_configure_args="${ac_configure_args} --disable-shared --with-pic --enable-benchmark=no --with-bignum=no --enable-module-recovery --disable-jni"
+ac_configure_args="${ac_configure_args} --disable-shared --with-pic --enable-benchmark=no --with-bignum=no --enable-module-recovery"
AC_CONFIG_SUBDIRS([src/secp256k1])
AC_OUTPUT
diff --git a/contrib/devtools/test-security-check.py b/contrib/devtools/test-security-check.py
index ea70b27941..629eba4f28 100755
--- a/contrib/devtools/test-security-check.py
+++ b/contrib/devtools/test-security-check.py
@@ -20,10 +20,9 @@ def write_testcode(filename):
''')
def call_security_check(cc, source, executable, options):
- subprocess.check_call([cc,source,'-o',executable] + options)
- p = subprocess.Popen(['./security-check.py',executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True)
- (stdout, stderr) = p.communicate()
- return (p.returncode, stdout.rstrip())
+ subprocess.run([cc,source,'-o',executable] + options, check=True)
+ p = subprocess.run(['./contrib/devtools/security-check.py',executable], stdout=subprocess.PIPE, universal_newlines=True)
+ return (p.returncode, p.stdout.rstrip())
class TestSecurityChecks(unittest.TestCase):
def test_ELF(self):
diff --git a/contrib/testgen/base58.py b/contrib/testgen/base58.py
index da67cb2d90..c7ebac50d4 100644
--- a/contrib/testgen/base58.py
+++ b/contrib/testgen/base58.py
@@ -107,7 +107,7 @@ def get_bcaddress_version(strAddress):
if __name__ == '__main__':
# Test case (from http://gitorious.org/bitcoin/python-base58.git)
- assert get_bcaddress_version('15VjRaDX9zpbA8LVnbrCAFzrVzN7ixHNsC') is 0
+ assert get_bcaddress_version('15VjRaDX9zpbA8LVnbrCAFzrVzN7ixHNsC') == 0
_ohai = 'o hai'.encode('ascii')
_tmp = b58encode(_ohai)
assert _tmp == 'DYB3oMS'
diff --git a/depends/config.site.in b/depends/config.site.in
index 103aa0d2ff..87a3379303 100644
--- a/depends/config.site.in
+++ b/depends/config.site.in
@@ -49,15 +49,6 @@ if test x@host_os@ = xdarwin; then
PORT=no
fi
-if test x@host_os@ = xmingw32; then
- if test -z $with_qt_incdir; then
- with_qt_incdir=$depends_prefix/include
- fi
- if test -z $with_qt_libdir; then
- with_qt_libdir=$depends_prefix/lib
- fi
-fi
-
PATH=$depends_prefix/native/bin:$PATH
PKG_CONFIG="`which pkg-config` --static"
diff --git a/depends/patches/qt/fix_qt_pkgconfig.patch b/depends/patches/qt/fix_qt_pkgconfig.patch
index 34302a9f2d..8c722ffb46 100644
--- a/depends/patches/qt/fix_qt_pkgconfig.patch
+++ b/depends/patches/qt/fix_qt_pkgconfig.patch
@@ -1,11 +1,23 @@
--- old/qtbase/mkspecs/features/qt_module.prf
+++ new/qtbase/mkspecs/features/qt_module.prf
-@@ -245,7 +245,7 @@
+@@ -264,7 +264,7 @@
load(qt_targets)
# this builds on top of qt_common
-!internal_module:!lib_bundle:if(unix|mingw) {
-+unix|mingw {
++if(unix|mingw):!if(darwin:debug_and_release:CONFIG(debug, debug|release)) {
CONFIG += create_pc
QMAKE_PKGCONFIG_DESTDIR = pkgconfig
host_build: \
+@@ -274,9 +274,9 @@
+ QMAKE_PKGCONFIG_INCDIR = $$[QT_INSTALL_HEADERS/raw]
+ QMAKE_PKGCONFIG_CFLAGS = -I${includedir}/$$MODULE_INCNAME
+ QMAKE_PKGCONFIG_NAME = $$replace(TARGET, ^Qt, "Qt$$QT_MAJOR_VERSION ")
+- QMAKE_PKGCONFIG_FILE = $$replace(TARGET, ^Qt, Qt$$QT_MAJOR_VERSION)
++ QMAKE_PKGCONFIG_FILE = $$replace(TARGET, ^Qt, Qt$$QT_MAJOR_VERSION)$$qtPlatformTargetSuffix()
+ for(i, MODULE_DEPENDS): \
+- QMAKE_PKGCONFIG_REQUIRES += $$replace(QT.$${i}.name, ^Qt, Qt$$section(QT.$${i}.VERSION, ., 0, 0))
++ QMAKE_PKGCONFIG_REQUIRES += $$replace(QT.$${i}.name, ^Qt, Qt$$section(QT.$${i}.VERSION, ., 0, 0))$$qtPlatformTargetSuffix()
+ isEmpty(QMAKE_PKGCONFIG_DESCRIPTION): \
+ QMAKE_PKGCONFIG_DESCRIPTION = $$replace(TARGET, ^Qt, "Qt ") module
+ pclib_replace.match = $$lib_replace.match
diff --git a/src/Makefile.am b/src/Makefile.am
index 7a280a67a7..a33ff8a461 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -240,6 +240,7 @@ BITCOIN_CORE_H = \
versionbits.h \
versionbitsinfo.h \
walletinitinterface.h \
+ wallet/bdb.h \
wallet/coincontrol.h \
wallet/context.h \
wallet/crypter.h \
@@ -350,6 +351,7 @@ libbitcoin_wallet_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
libbitcoin_wallet_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_wallet_a_SOURCES = \
interfaces/wallet.cpp \
+ wallet/bdb.cpp \
wallet/coincontrol.cpp \
wallet/context.cpp \
wallet/crypter.cpp \
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index 7909cb4a0f..03cd9133c8 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -32,6 +32,7 @@ FUZZ_TARGETS = \
test/fuzz/checkqueue \
test/fuzz/coins_deserialize \
test/fuzz/coins_view \
+ test/fuzz/crypto_common \
test/fuzz/cuckoocache \
test/fuzz/decode_tx \
test/fuzz/descriptor_parse \
@@ -478,6 +479,12 @@ test_fuzz_coins_view_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_coins_view_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
test_fuzz_coins_view_SOURCES = test/fuzz/coins_view.cpp
+test_fuzz_crypto_common_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_crypto_common_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_crypto_common_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_crypto_common_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_crypto_common_SOURCES = test/fuzz/crypto_common.cpp
+
test_fuzz_cuckoocache_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_cuckoocache_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_cuckoocache_LDADD = $(FUZZ_SUITE_LD_COMMON)
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index 799474fae2..092c45e4ce 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -121,6 +121,7 @@ public:
vSeeds.emplace_back("seed.btc.petertodd.org"); // Peter Todd, only supports x1, x5, x9, and xd
vSeeds.emplace_back("seed.bitcoin.sprovoost.nl"); // Sjors Provoost
vSeeds.emplace_back("dnsseed.emzy.de"); // Stephan Oeste
+ vSeeds.emplace_back("seed.bitcoin.wiz.biz"); // Jason Maurice
base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,0);
base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,5);
diff --git a/src/fs.cpp b/src/fs.cpp
index e68c97b3ca..eef9c81de9 100644
--- a/src/fs.cpp
+++ b/src/fs.cpp
@@ -5,10 +5,12 @@
#include <fs.h>
#ifndef WIN32
+#include <cstring>
#include <fcntl.h>
#include <string>
#include <sys/file.h>
#include <sys/utsname.h>
+#include <unistd.h>
#else
#ifndef NOMINMAX
#define NOMINMAX
@@ -31,7 +33,8 @@ FILE *fopen(const fs::path& p, const char *mode)
#ifndef WIN32
-static std::string GetErrorReason() {
+static std::string GetErrorReason()
+{
return std::strerror(errno);
}
diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h
index 61d7ddb934..65695707f7 100644
--- a/src/interfaces/chain.h
+++ b/src/interfaces/chain.h
@@ -8,6 +8,7 @@
#include <optional.h> // For Optional and nullopt
#include <primitives/transaction.h> // For CTransactionRef
+#include <functional>
#include <memory>
#include <stddef.h>
#include <stdint.h>
diff --git a/src/key.cpp b/src/key.cpp
index b6ed29e8e3..7eecc6e083 100644
--- a/src/key.cpp
+++ b/src/key.cpp
@@ -31,46 +31,46 @@ static secp256k1_context* secp256k1_context_sign = nullptr;
*
* out32 must point to an output buffer of length at least 32 bytes.
*/
-static int ec_privkey_import_der(const secp256k1_context* ctx, unsigned char *out32, const unsigned char *privkey, size_t privkeylen) {
- const unsigned char *end = privkey + privkeylen;
+static int ec_seckey_import_der(const secp256k1_context* ctx, unsigned char *out32, const unsigned char *seckey, size_t seckeylen) {
+ const unsigned char *end = seckey + seckeylen;
memset(out32, 0, 32);
/* sequence header */
- if (end - privkey < 1 || *privkey != 0x30u) {
+ if (end - seckey < 1 || *seckey != 0x30u) {
return 0;
}
- privkey++;
+ seckey++;
/* sequence length constructor */
- if (end - privkey < 1 || !(*privkey & 0x80u)) {
+ if (end - seckey < 1 || !(*seckey & 0x80u)) {
return 0;
}
- ptrdiff_t lenb = *privkey & ~0x80u; privkey++;
+ ptrdiff_t lenb = *seckey & ~0x80u; seckey++;
if (lenb < 1 || lenb > 2) {
return 0;
}
- if (end - privkey < lenb) {
+ if (end - seckey < lenb) {
return 0;
}
/* sequence length */
- ptrdiff_t len = privkey[lenb-1] | (lenb > 1 ? privkey[lenb-2] << 8 : 0u);
- privkey += lenb;
- if (end - privkey < len) {
+ ptrdiff_t len = seckey[lenb-1] | (lenb > 1 ? seckey[lenb-2] << 8 : 0u);
+ seckey += lenb;
+ if (end - seckey < len) {
return 0;
}
/* sequence element 0: version number (=1) */
- if (end - privkey < 3 || privkey[0] != 0x02u || privkey[1] != 0x01u || privkey[2] != 0x01u) {
+ if (end - seckey < 3 || seckey[0] != 0x02u || seckey[1] != 0x01u || seckey[2] != 0x01u) {
return 0;
}
- privkey += 3;
+ seckey += 3;
/* sequence element 1: octet string, up to 32 bytes */
- if (end - privkey < 2 || privkey[0] != 0x04u) {
+ if (end - seckey < 2 || seckey[0] != 0x04u) {
return 0;
}
- ptrdiff_t oslen = privkey[1];
- privkey += 2;
- if (oslen > 32 || end - privkey < oslen) {
+ ptrdiff_t oslen = seckey[1];
+ seckey += 2;
+ if (oslen > 32 || end - seckey < oslen) {
return 0;
}
- memcpy(out32 + (32 - oslen), privkey, oslen);
+ memcpy(out32 + (32 - oslen), seckey, oslen);
if (!secp256k1_ec_seckey_verify(ctx, out32)) {
memset(out32, 0, 32);
return 0;
@@ -83,17 +83,17 @@ static int ec_privkey_import_der(const secp256k1_context* ctx, unsigned char *ou
* <http://www.secg.org/sec1-v2.pdf>. The optional parameters and publicKey fields are
* included.
*
- * privkey must point to an output buffer of length at least CKey::SIZE bytes.
- * privkeylen must initially be set to the size of the privkey buffer. Upon return it
+ * seckey must point to an output buffer of length at least CKey::SIZE bytes.
+ * seckeylen must initially be set to the size of the seckey buffer. Upon return it
* will be set to the number of bytes used in the buffer.
* key32 must point to a 32-byte raw private key.
*/
-static int ec_privkey_export_der(const secp256k1_context *ctx, unsigned char *privkey, size_t *privkeylen, const unsigned char *key32, bool compressed) {
- assert(*privkeylen >= CKey::SIZE);
+static int ec_seckey_export_der(const secp256k1_context *ctx, unsigned char *seckey, size_t *seckeylen, const unsigned char *key32, bool compressed) {
+ assert(*seckeylen >= CKey::SIZE);
secp256k1_pubkey pubkey;
size_t pubkeylen = 0;
if (!secp256k1_ec_pubkey_create(ctx, &pubkey, key32)) {
- *privkeylen = 0;
+ *seckeylen = 0;
return 0;
}
if (compressed) {
@@ -111,15 +111,15 @@ static int ec_privkey_export_der(const secp256k1_context *ctx, unsigned char *pr
0xFF,0xFF,0xFF,0xFF,0xFE,0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B,0xBF,0xD2,0x5E,
0x8C,0xD0,0x36,0x41,0x41,0x02,0x01,0x01,0xA1,0x24,0x03,0x22,0x00
};
- unsigned char *ptr = privkey;
+ unsigned char *ptr = seckey;
memcpy(ptr, begin, sizeof(begin)); ptr += sizeof(begin);
memcpy(ptr, key32, 32); ptr += 32;
memcpy(ptr, middle, sizeof(middle)); ptr += sizeof(middle);
pubkeylen = CPubKey::COMPRESSED_SIZE;
secp256k1_ec_pubkey_serialize(ctx, ptr, &pubkeylen, &pubkey, SECP256K1_EC_COMPRESSED);
ptr += pubkeylen;
- *privkeylen = ptr - privkey;
- assert(*privkeylen == CKey::COMPRESSED_SIZE);
+ *seckeylen = ptr - seckey;
+ assert(*seckeylen == CKey::COMPRESSED_SIZE);
} else {
static const unsigned char begin[] = {
0x30,0x82,0x01,0x13,0x02,0x01,0x01,0x04,0x20
@@ -137,15 +137,15 @@ static int ec_privkey_export_der(const secp256k1_context *ctx, unsigned char *pr
0xFF,0xFF,0xFF,0xFF,0xFE,0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B,0xBF,0xD2,0x5E,
0x8C,0xD0,0x36,0x41,0x41,0x02,0x01,0x01,0xA1,0x44,0x03,0x42,0x00
};
- unsigned char *ptr = privkey;
+ unsigned char *ptr = seckey;
memcpy(ptr, begin, sizeof(begin)); ptr += sizeof(begin);
memcpy(ptr, key32, 32); ptr += 32;
memcpy(ptr, middle, sizeof(middle)); ptr += sizeof(middle);
pubkeylen = CPubKey::SIZE;
secp256k1_ec_pubkey_serialize(ctx, ptr, &pubkeylen, &pubkey, SECP256K1_EC_UNCOMPRESSED);
ptr += pubkeylen;
- *privkeylen = ptr - privkey;
- assert(*privkeylen == CKey::SIZE);
+ *seckeylen = ptr - seckey;
+ assert(*seckeylen == CKey::SIZE);
}
return 1;
}
@@ -165,20 +165,20 @@ void CKey::MakeNewKey(bool fCompressedIn) {
bool CKey::Negate()
{
assert(fValid);
- return secp256k1_ec_privkey_negate(secp256k1_context_sign, keydata.data());
+ return secp256k1_ec_seckey_negate(secp256k1_context_sign, keydata.data());
}
CPrivKey CKey::GetPrivKey() const {
assert(fValid);
- CPrivKey privkey;
+ CPrivKey seckey;
int ret;
- size_t privkeylen;
- privkey.resize(SIZE);
- privkeylen = SIZE;
- ret = ec_privkey_export_der(secp256k1_context_sign, privkey.data(), &privkeylen, begin(), fCompressed);
+ size_t seckeylen;
+ seckey.resize(SIZE);
+ seckeylen = SIZE;
+ ret = ec_seckey_export_der(secp256k1_context_sign, seckey.data(), &seckeylen, begin(), fCompressed);
assert(ret);
- privkey.resize(privkeylen);
- return privkey;
+ seckey.resize(seckeylen);
+ return seckey;
}
CPubKey CKey::GetPubKey() const {
@@ -258,8 +258,8 @@ bool CKey::SignCompact(const uint256 &hash, std::vector<unsigned char>& vchSig)
return true;
}
-bool CKey::Load(const CPrivKey &privkey, const CPubKey &vchPubKey, bool fSkipCheck=false) {
- if (!ec_privkey_import_der(secp256k1_context_sign, (unsigned char*)begin(), privkey.data(), privkey.size()))
+bool CKey::Load(const CPrivKey &seckey, const CPubKey &vchPubKey, bool fSkipCheck=false) {
+ if (!ec_seckey_import_der(secp256k1_context_sign, (unsigned char*)begin(), seckey.data(), seckey.size()))
return false;
fCompressed = vchPubKey.IsCompressed();
fValid = true;
@@ -284,7 +284,7 @@ bool CKey::Derive(CKey& keyChild, ChainCode &ccChild, unsigned int nChild, const
}
memcpy(ccChild.begin(), vout.data()+32, 32);
memcpy((unsigned char*)keyChild.begin(), begin(), 32);
- bool ret = secp256k1_ec_privkey_tweak_add(secp256k1_context_sign, (unsigned char*)keyChild.begin(), vout.data());
+ bool ret = secp256k1_ec_seckey_tweak_add(secp256k1_context_sign, (unsigned char*)keyChild.begin(), vout.data());
keyChild.fCompressed = true;
keyChild.fValid = ret;
return ret;
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index d48745aef2..aa5c9668e3 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -2215,20 +2215,6 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
}
- if (!(pfrom.GetLocalServices() & NODE_BLOOM) &&
- (msg_type == NetMsgType::FILTERLOAD ||
- msg_type == NetMsgType::FILTERADD))
- {
- if (pfrom.nVersion >= NO_BLOOM_VERSION) {
- LOCK(cs_main);
- Misbehaving(pfrom.GetId(), 100);
- return false;
- } else {
- pfrom.fDisconnect = true;
- return false;
- }
- }
-
if (msg_type == NetMsgType::VERSION) {
// Each connection can only send one version message
if (pfrom.nVersion != 0)
@@ -3447,6 +3433,10 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
}
if (msg_type == NetMsgType::FILTERLOAD) {
+ if (!(pfrom.GetLocalServices() & NODE_BLOOM)) {
+ pfrom.fDisconnect = true;
+ return true;
+ }
CBloomFilter filter;
vRecv >> filter;
@@ -3466,6 +3456,10 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
}
if (msg_type == NetMsgType::FILTERADD) {
+ if (!(pfrom.GetLocalServices() & NODE_BLOOM)) {
+ pfrom.fDisconnect = true;
+ return true;
+ }
std::vector<unsigned char> vData;
vRecv >> vData;
@@ -3490,13 +3484,15 @@ bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRec
}
if (msg_type == NetMsgType::FILTERCLEAR) {
+ if (!(pfrom.GetLocalServices() & NODE_BLOOM)) {
+ pfrom.fDisconnect = true;
+ return true;
+ }
if (pfrom.m_tx_relay == nullptr) {
return true;
}
LOCK(pfrom.m_tx_relay->cs_filter);
- if (pfrom.GetLocalServices() & NODE_BLOOM) {
- pfrom.m_tx_relay->pfilter = nullptr;
- }
+ pfrom.m_tx_relay->pfilter = nullptr;
pfrom.m_tx_relay->fRelayTxes = true;
return true;
}
diff --git a/src/noui.cpp b/src/noui.cpp
index ddb3a50ff7..821d10e3bc 100644
--- a/src/noui.cpp
+++ b/src/noui.cpp
@@ -23,24 +23,20 @@ bool noui_ThreadSafeMessageBox(const bilingual_str& message, const std::string&
{
bool fSecure = style & CClientUIInterface::SECURE;
style &= ~CClientUIInterface::SECURE;
- bool prefix = !(style & CClientUIInterface::MSG_NOPREFIX);
- style &= ~CClientUIInterface::MSG_NOPREFIX;
std::string strCaption;
- if (prefix) {
- switch (style) {
- case CClientUIInterface::MSG_ERROR:
- strCaption = "Error: ";
- break;
- case CClientUIInterface::MSG_WARNING:
- strCaption = "Warning: ";
- break;
- case CClientUIInterface::MSG_INFORMATION:
- strCaption = "Information: ";
- break;
- default:
- strCaption = caption + ": "; // Use supplied caption (can be empty)
- }
+ switch (style) {
+ case CClientUIInterface::MSG_ERROR:
+ strCaption = "Error: ";
+ break;
+ case CClientUIInterface::MSG_WARNING:
+ strCaption = "Warning: ";
+ break;
+ case CClientUIInterface::MSG_INFORMATION:
+ strCaption = "Information: ";
+ break;
+ default:
+ strCaption = caption + ": "; // Use supplied caption (can be empty)
}
if (!fSecure) {
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 195d80d47c..1092cdd754 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -1069,9 +1069,6 @@ void BitcoinGUI::message(const QString& title, QString message, unsigned int sty
int nMBoxIcon = QMessageBox::Information;
int nNotifyIcon = Notificator::Information;
- bool prefix = !(style & CClientUIInterface::MSG_NOPREFIX);
- style &= ~CClientUIInterface::MSG_NOPREFIX;
-
QString msgType;
if (!title.isEmpty()) {
msgType = title;
@@ -1079,11 +1076,11 @@ void BitcoinGUI::message(const QString& title, QString message, unsigned int sty
switch (style) {
case CClientUIInterface::MSG_ERROR:
msgType = tr("Error");
- if (prefix) message = tr("Error: %1").arg(message);
+ message = tr("Error: %1").arg(message);
break;
case CClientUIInterface::MSG_WARNING:
msgType = tr("Warning");
- if (prefix) message = tr("Warning: %1").arg(message);
+ message = tr("Warning: %1").arg(message);
break;
case CClientUIInterface::MSG_INFORMATION:
msgType = tr("Information");
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index db77c17df0..f44a9f285a 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -400,7 +400,6 @@ void CoinControlDialog::updateLabels(CCoinControl& m_coin_control, WalletModel *
// nPayAmount
CAmount nPayAmount = 0;
bool fDust = false;
- CMutableTransaction txDummy;
for (const CAmount &amount : CoinControlDialog::payAmounts)
{
nPayAmount += amount;
@@ -409,7 +408,6 @@ void CoinControlDialog::updateLabels(CCoinControl& m_coin_control, WalletModel *
{
// Assumes a p2pkh script size
CTxOut txout(amount, CScript() << std::vector<unsigned char>(24, 0));
- txDummy.vout.push_back(txout);
fDust |= IsDust(txout, model->node().getDustRelayFee());
}
}
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index 0f89d4e6fe..dafd517ca8 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -24,6 +24,7 @@
#include <univalue.h>
#ifdef ENABLE_WALLET
+#include <wallet/bdb.h>
#include <wallet/db.h>
#include <wallet/wallet.h>
#endif
diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp
index 01dff8069c..52007ef350 100644
--- a/src/qt/transactionrecord.cpp
+++ b/src/qt/transactionrecord.cpp
@@ -47,7 +47,6 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const interface
if(mine)
{
TransactionRecord sub(hash, nTime);
- CTxDestination address;
sub.idx = i; // vout index
sub.credit = txout.nValue;
sub.involvesWatchAddress = mine & ISMINE_WATCH_ONLY;
diff --git a/src/qt/walletcontroller.cpp b/src/qt/walletcontroller.cpp
index f1438e3439..3aed98e0e8 100644
--- a/src/qt/walletcontroller.cpp
+++ b/src/qt/walletcontroller.cpp
@@ -262,7 +262,7 @@ void CreateWalletActivity::finish()
{
destroyProgressDialog();
- if (!m_error_message.original.empty()) {
+ if (!m_error_message.empty()) {
QMessageBox::critical(m_parent_widget, tr("Create wallet failed"), QString::fromStdString(m_error_message.translated));
} else if (!m_warning_message.empty()) {
QMessageBox::warning(m_parent_widget, tr("Create wallet warning"), QString::fromStdString(Join(m_warning_message, Untranslated("\n")).translated));
@@ -303,7 +303,7 @@ void OpenWalletActivity::finish()
{
destroyProgressDialog();
- if (!m_error_message.original.empty()) {
+ if (!m_error_message.empty()) {
QMessageBox::critical(m_parent_widget, tr("Open wallet failed"), QString::fromStdString(m_error_message.translated));
} else if (!m_warning_message.empty()) {
QMessageBox::warning(m_parent_widget, tr("Open wallet warning"), QString::fromStdString(Join(m_warning_message, Untranslated("\n")).translated));
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index a2de175c85..d5fc3f57bb 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -252,7 +252,7 @@ static UniValue generatetoaddress(const JSONRPCRequest& request)
RPCExamples{
"\nGenerate 11 blocks to myaddress\n"
+ HelpExampleCli("generatetoaddress", "11 \"myaddress\"")
- + "If you are running the bitcoin core wallet, you can get a new address to send the newly generated bitcoin to with:\n"
+ + "If you are using the " PACKAGE_NAME " wallet, you can get a new address to send the newly generated bitcoin to with:\n"
+ HelpExampleCli("getnewaddress", "")
},
}.Check(request);
diff --git a/src/secp256k1/.gitignore b/src/secp256k1/.gitignore
index 55d325aeef..cb4331aa90 100644
--- a/src/secp256k1/.gitignore
+++ b/src/secp256k1/.gitignore
@@ -9,6 +9,7 @@ bench_internal
tests
exhaustive_tests
gen_context
+valgrind_ctime_test
*.exe
*.so
*.a
diff --git a/src/secp256k1/.travis.yml b/src/secp256k1/.travis.yml
index 74f658f4d1..a6ad6fb27e 100644
--- a/src/secp256k1/.travis.yml
+++ b/src/secp256k1/.travis.yml
@@ -1,18 +1,23 @@
language: c
-os: linux
+os:
+ - linux
+ - osx
+
+dist: bionic
+# Valgrind currently supports upto macOS 10.13, the latest xcode of that version is 10.1
+osx_image: xcode10.1
addons:
apt:
- packages: libgmp-dev
+ packages:
+ - libgmp-dev
+ - valgrind
+ - libtool-bin
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 RECOVERY=no EXPERIMENTAL=no JNI=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
+ - FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ECMULTGENPRECISION=auto ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no RECOVERY=no EXPERIMENTAL=no CTIMETEST=yes BENCH=yes ITERS=2
matrix:
- SCALAR=32bit RECOVERY=yes
- SCALAR=32bit FIELD=32bit ECDH=yes EXPERIMENTAL=yes
@@ -26,43 +31,78 @@ env:
- BIGNUM=no
- BIGNUM=no ENDOMORPHISM=yes RECOVERY=yes EXPERIMENTAL=yes
- BIGNUM=no STATICPRECOMPUTATION=no
- - BUILD=distcheck
- - EXTRAFLAGS=CPPFLAGS=-DDETERMINISTIC
- - EXTRAFLAGS=CFLAGS=-O0
- - BUILD=check-java JNI=yes ECDH=yes EXPERIMENTAL=yes
+ - BUILD=distcheck CTIMETEST= BENCH=
+ - CPPFLAGS=-DDETERMINISTIC
+ - CFLAGS=-O0 CTIMETEST=
+ - ECMULTGENPRECISION=2
+ - ECMULTGENPRECISION=8
+ - VALGRIND=yes ENDOMORPHISM=yes BIGNUM=no ASM=x86_64 EXPERIMENTAL=yes ECDH=yes RECOVERY=yes EXTRAFLAGS="--disable-openssl-tests" CPPFLAGS=-DVALGRIND BUILD=
+ - VALGRIND=yes BIGNUM=no ASM=x86_64 EXPERIMENTAL=yes ECDH=yes RECOVERY=yes EXTRAFLAGS="--disable-openssl-tests" CPPFLAGS=-DVALGRIND BUILD=
matrix:
fast_finish: true
include:
- compiler: clang
+ os: linux
env: HOST=i686-linux-gnu ENDOMORPHISM=yes
addons:
apt:
packages:
- gcc-multilib
- libgmp-dev:i386
+ - valgrind
+ - libtool-bin
+ - libc6-dbg:i386
- compiler: clang
env: HOST=i686-linux-gnu
+ os: linux
addons:
apt:
packages:
- gcc-multilib
+ - valgrind
+ - libtool-bin
+ - libc6-dbg:i386
- compiler: gcc
env: HOST=i686-linux-gnu ENDOMORPHISM=yes
+ os: linux
addons:
apt:
packages:
- gcc-multilib
+ - valgrind
+ - libtool-bin
+ - libc6-dbg:i386
- compiler: gcc
+ os: linux
env: HOST=i686-linux-gnu
addons:
apt:
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
+ - valgrind
+ - libtool-bin
+ - libc6-dbg:i386
+
+# We use this to install macOS dependencies instead of the built in `homebrew` plugin,
+# because in xcode earlier than 11 they have a bug requiring updating the system which overall takes ~8 minutes.
+# https://travis-ci.community/t/macos-build-fails-because-of-homebrew-bundle-unknown-command/7296
+before_install:
+ - if [ "${TRAVIS_OS_NAME}" = "osx" ]; then HOMEBREW_NO_AUTO_UPDATE=1 brew install gmp valgrind gcc@9; fi
+
before_script: ./autogen.sh
+
+# travis auto terminates jobs that go for 10 minutes without printing to stdout, but travis_wait doesn't work well with forking programs like valgrind (https://docs.travis-ci.com/user/common-build-problems/#build-times-out-because-no-output-was-received https://github.com/bitcoin-core/secp256k1/pull/750#issuecomment-623476860)
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-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-recovery=$RECOVERY --enable-jni=$JNI $EXTRAFLAGS $USE_HOST && make -j2 $BUILD
+ - function keep_alive() { while true; do echo -en "\a"; sleep 60; done }
+ - keep_alive &
+ - ./contrib/travis.sh
+ - kill %keep_alive
+
+after_script:
+ - cat ./tests.log
+ - cat ./exhaustive_tests.log
+ - cat ./valgrind_ctime_test.log
+ - cat ./bench.log
+ - $CC --version
+ - valgrind --version
diff --git a/src/secp256k1/Makefile.am b/src/secp256k1/Makefile.am
index 9e5b7dcce0..d8c1c79e8c 100644
--- a/src/secp256k1/Makefile.am
+++ b/src/secp256k1/Makefile.am
@@ -1,13 +1,8 @@
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
+include_HEADERS += include/secp256k1_preallocated.h
noinst_HEADERS =
noinst_HEADERS += src/scalar.h
noinst_HEADERS += src/scalar_4x64.h
@@ -39,8 +34,6 @@ noinst_HEADERS += src/field_5x52.h
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/scratch.h
noinst_HEADERS += src/scratch_impl.h
@@ -74,16 +67,19 @@ endif
libsecp256k1_la_SOURCES = src/secp256k1.c
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_la_LIBADD = $(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)
+if VALGRIND_ENABLED
+libsecp256k1_la_CPPFLAGS += -DVALGRIND
+endif
noinst_PROGRAMS =
if USE_BENCHMARK
noinst_PROGRAMS += bench_verify bench_sign bench_internal bench_ecmult
bench_verify_SOURCES = src/bench_verify.c
bench_verify_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB)
+# SECP_TEST_INCLUDES are only used here for CRYPTO_CPPFLAGS
+bench_verify_CPPFLAGS = -DSECP256K1_BUILD $(SECP_TEST_INCLUDES)
bench_sign_SOURCES = src/bench_sign.c
bench_sign_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB)
bench_internal_SOURCES = src/bench_internal.c
@@ -99,6 +95,12 @@ if USE_TESTS
noinst_PROGRAMS += tests
tests_SOURCES = src/tests.c
tests_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/src -I$(top_srcdir)/include $(SECP_INCLUDES) $(SECP_TEST_INCLUDES)
+if VALGRIND_ENABLED
+tests_CPPFLAGS += -DVALGRIND
+noinst_PROGRAMS += valgrind_ctime_test
+valgrind_ctime_test_SOURCES = src/valgrind_ctime_test.c
+valgrind_ctime_test_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB)
+endif
if !ENABLE_COVERAGE
tests_CPPFLAGS += -DVERIFY
endif
@@ -119,42 +121,12 @@ exhaustive_tests_LDFLAGS = -static
TESTS += exhaustive_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)
+CPPFLAGS_FOR_BUILD +=-I$(top_srcdir) -I$(builddir)/src
gen_context_OBJECTS = gen_context.o
gen_context_BIN = gen_context$(BUILD_EXEEXT)
-gen_%.o: src/gen_%.c
+gen_%.o: src/gen_%.c src/libsecp256k1-config.h
$(CC_FOR_BUILD) $(CPPFLAGS_FOR_BUILD) $(CFLAGS_FOR_BUILD) -c $< -o $@
$(gen_context_BIN): $(gen_context_OBJECTS)
@@ -168,10 +140,10 @@ $(bench_ecmult_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 $(JAVAROOT)/$(JAVAORG)/*.class .stamp-java
+CLEANFILES = $(gen_context_BIN) src/ecmult_static_context.h
endif
-EXTRA_DIST = autogen.sh src/gen_context.c src/basic-config.h $(JAVA_FILES)
+EXTRA_DIST = autogen.sh src/gen_context.c src/basic-config.h
if ENABLE_MODULE_ECDH
include src/modules/ecdh/Makefile.am.include
diff --git a/src/secp256k1/README.md b/src/secp256k1/README.md
index 8cd344ea81..434178b372 100644
--- a/src/secp256k1/README.md
+++ b/src/secp256k1/README.md
@@ -3,17 +3,22 @@ libsecp256k1
[![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.
+Optimized C library for ECDSA signatures and secret/public key operations on curve secp256k1.
-This library is a work in progress and is being used to research best practices. Use at your own risk.
+This library is intended to be the highest quality publicly available library for cryptography on the secp256k1 curve. However, the primary focus of its development has been for usage in the Bitcoin system and usage unlike Bitcoin's may be less well tested, verified, or suffer from a less well thought out interface. Correct usage requires some care and consideration that the library is fit for your application's purpose.
Features:
* secp256k1 ECDSA signing/verification and key generation.
-* Adding/multiplying private/public keys.
-* Serialization/parsing of private keys, public keys, signatures.
-* Constant time, constant memory access signing and pubkey generation.
-* Derandomized DSA (via RFC6979 or with a caller provided function.)
+* Additive and multiplicative tweaking of secret/public keys.
+* Serialization/parsing of secret keys, public keys, signatures.
+* Constant time, constant memory access signing and public key generation.
+* Derandomized ECDSA (via RFC6979 or with a caller provided function.)
* Very efficient implementation.
+* Suitable for embedded systems.
+* Optional module for public key recovery.
+* Optional module for ECDH key exchange (experimental).
+
+Experimental features have not received enough scrutiny to satisfy the standard of quality of this library but are made available for testing and review by the community. The APIs of these features should not be considered stable.
Implementation details
----------------------
@@ -23,11 +28,12 @@ Implementation details
* Extensive testing infrastructure.
* Structured to facilitate review and analysis.
* Intended to be portable to any system with a C89 compiler and uint64_t support.
+ * No use of floating types.
* Expose only higher level interfaces to minimize the API surface and improve application security. ("Be difficult to use insecurely.")
* Field operations
* Optimized implementation of arithmetic modulo the curve's field size (2^256 - 0x1000003D1).
* Using 5 52-bit limbs (including hand-optimized assembly for x86_64, by Diederik Huys).
- * Using 10 26-bit limbs.
+ * Using 10 26-bit limbs (including hand-optimized assembly for 32-bit ARM, by Wladimir J. van der Laan).
* Field inverses and square roots using a sliding window over blocks of 1s (by Peter Dettman).
* Scalar operations
* Optimized implementation without data-dependent branches of arithmetic modulo the curve's order.
@@ -45,9 +51,11 @@ Implementation details
* Optionally (off by default) use secp256k1's efficiently-computable endomorphism to split the P multiplicand into 2 half-sized ones.
* Point multiplication for signing
* Use a precomputed table of multiples of powers of 16 multiplied with the generator, so general multiplication becomes a series of additions.
- * Access the table with branch-free conditional moves so memory access is uniform.
- * No data-dependent branches
- * The precomputed tables add and eventually subtract points for which no known scalar (private key) is known, preventing even an attacker with control over the private key used to control the data internally.
+ * Intended to be completely free of timing sidechannels for secret-key operations (on reasonable hardware/toolchains)
+ * Access the table with branch-free conditional moves so memory access is uniform.
+ * No data-dependent branches
+ * Optional runtime blinding which attempts to frustrate differential power analysis.
+ * The precomputed tables add and eventually subtract points for which no known scalar (secret key) is known, preventing even an attacker with control over the secret key used to control the data internally.
Build steps
-----------
@@ -57,5 +65,40 @@ libsecp256k1 is built using autotools:
$ ./autogen.sh
$ ./configure
$ make
- $ ./tests
+ $ make check
$ sudo make install # optional
+
+Exhaustive tests
+-----------
+
+ $ ./exhaustive_tests
+
+With valgrind, you might need to increase the max stack size:
+
+ $ valgrind --max-stackframe=2500000 ./exhaustive_tests
+
+Test coverage
+-----------
+
+This library aims to have full coverage of the reachable lines and branches.
+
+To create a test coverage report, configure with `--enable-coverage` (use of GCC is necessary):
+
+ $ ./configure --enable-coverage
+
+Run the tests:
+
+ $ make check
+
+To create a report, `gcovr` is recommended, as it includes branch coverage reporting:
+
+ $ gcovr --exclude 'src/bench*' --print-summary
+
+To create a HTML report with coloured and annotated source code:
+
+ $ gcovr --exclude 'src/bench*' --html --html-details -o coverage.html
+
+Reporting a vulnerability
+------------
+
+See [SECURITY.md](SECURITY.md)
diff --git a/src/secp256k1/SECURITY.md b/src/secp256k1/SECURITY.md
new file mode 100644
index 0000000000..0e4d588030
--- /dev/null
+++ b/src/secp256k1/SECURITY.md
@@ -0,0 +1,15 @@
+# Security Policy
+
+## Reporting a Vulnerability
+
+To report security issues send an email to secp256k1-security@bitcoincore.org (not for support).
+
+The following keys may be used to communicate sensitive information to developers:
+
+| Name | Fingerprint |
+|------|-------------|
+| Pieter Wuille | 133E AC17 9436 F14A 5CF1 B794 860F EB80 4E66 9320 |
+| Andrew Poelstra | 699A 63EF C17A D3A9 A34C FFC0 7AD0 A91C 40BD 0091 |
+| Tim Ruffing | 09E0 3F87 1092 E40E 106E 902B 33BC 86AB 80FF 5516 |
+
+You can import a key by running the following command with that individual’s fingerprint: `gpg --recv-keys "<fingerprint>"` Ensure that you put quotes around fingerprints containing spaces.
diff --git a/src/secp256k1/build-aux/m4/ax_jni_include_dir.m4 b/src/secp256k1/build-aux/m4/ax_jni_include_dir.m4
deleted file mode 100644
index cdc78d87d4..0000000000
--- a/src/secp256k1/build-aux/m4/ax_jni_include_dir.m4
+++ /dev/null
@@ -1,145 +0,0 @@
-# ===========================================================================
-# https://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 <dda@sleepycat.com>
-#
-# 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 14
-
-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*) # Apple Java headers are inside the Xcode bundle.
- macos_version=$(sw_vers -productVersion | sed -n -e 's/^@<:@0-9@:>@*.\(@<:@0-9@:>@*\).@<:@0-9@:>@*/\1/p')
- if @<:@ "$macos_version" -gt "7" @:>@; then
- _JTOPDIR="$(xcrun --show-sdk-path)/System/Library/Frameworks/JavaVM.framework"
- _JINC="$_JTOPDIR/Headers"
- else
- _JTOPDIR="/System/Library/Frameworks/JavaVM.framework"
- _JINC="$_JTOPDIR/Headers"
- fi
- ;;
- *) _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";;
-freebsd*) _JNI_INC_SUBDIRS="freebsd";;
-darwin*) _JNI_INC_SUBDIRS="darwin";;
-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 <path>
-# Follows symbolic links on <path>,
-# 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 3b3975cbdd..1b2b71e6ab 100644
--- a/src/secp256k1/build-aux/m4/bitcoin_secp.m4
+++ b/src/secp256k1/build-aux/m4/bitcoin_secp.m4
@@ -38,6 +38,8 @@ AC_DEFUN([SECP_OPENSSL_CHECK],[
fi
if test x"$has_libcrypto" = x"yes" && test x"$has_openssl_ec" = x; then
AC_MSG_CHECKING(for EC functions in libcrypto)
+ CPPFLAGS_TEMP="$CPPFLAGS"
+ CPPFLAGS="$CRYPTO_CPPFLAGS $CPPFLAGS"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#include <openssl/ec.h>
#include <openssl/ecdsa.h>
@@ -51,6 +53,7 @@ if test x"$has_libcrypto" = x"yes" && test x"$has_openssl_ec" = x; then
ECDSA_SIG_free(sig_openssl);
]])],[has_openssl_ec=yes],[has_openssl_ec=no])
AC_MSG_RESULT([$has_openssl_ec])
+ CPPFLAGS="$CPPFLAGS_TEMP"
fi
])
diff --git a/src/secp256k1/configure.ac b/src/secp256k1/configure.ac
index 3b7a328c8a..6021b760b5 100644
--- a/src/secp256k1/configure.ac
+++ b/src/secp256k1/configure.ac
@@ -7,6 +7,11 @@ AH_TOP([#ifndef LIBSECP256K1_CONFIG_H])
AH_TOP([#define LIBSECP256K1_CONFIG_H])
AH_BOTTOM([#endif /*LIBSECP256K1_CONFIG_H*/])
AM_INIT_AUTOMAKE([foreign subdir-objects])
+
+# Set -g if CFLAGS are not already set, which matches the default autoconf
+# behavior (see PROG_CC in the Autoconf manual) with the exception that we don't
+# set -O2 here because we set it in any case (see further down).
+: ${CFLAGS="-g"}
LT_INIT
dnl make the compilation flags quiet unless V=1 is used
@@ -19,10 +24,6 @@ AC_PATH_TOOL(RANLIB, ranlib)
AC_PATH_TOOL(STRIP, strip)
AX_PROG_CC_FOR_BUILD
-if test "x$CFLAGS" = "x"; then
- CFLAGS="-g"
-fi
-
AM_PROG_CC_C_O
AC_PROG_CC_C89
@@ -45,6 +46,7 @@ case $host_os in
if test x$openssl_prefix != x; then
PKG_CONFIG_PATH="$openssl_prefix/lib/pkgconfig:$PKG_CONFIG_PATH"
export PKG_CONFIG_PATH
+ CRYPTO_CPPFLAGS="-I$openssl_prefix/include"
fi
if test x$gmp_prefix != x; then
GMP_CPPFLAGS="-I$gmp_prefix/include"
@@ -63,11 +65,11 @@ case $host_os in
;;
esac
-CFLAGS="$CFLAGS -W"
+CFLAGS="-W $CFLAGS"
warn_CFLAGS="-std=c89 -pedantic -Wall -Wextra -Wcast-align -Wnested-externs -Wshadow -Wstrict-prototypes -Wno-unused-function -Wno-long-long -Wno-overlength-strings"
saved_CFLAGS="$CFLAGS"
-CFLAGS="$CFLAGS $warn_CFLAGS"
+CFLAGS="$warn_CFLAGS $CFLAGS"
AC_MSG_CHECKING([if ${CC} supports ${warn_CFLAGS}])
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])],
[ AC_MSG_RESULT([yes]) ],
@@ -76,7 +78,7 @@ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])],
])
saved_CFLAGS="$CFLAGS"
-CFLAGS="$CFLAGS -fvisibility=hidden"
+CFLAGS="-fvisibility=hidden $CFLAGS"
AC_MSG_CHECKING([if ${CC} supports -fvisibility=hidden])
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])],
[ AC_MSG_RESULT([yes]) ],
@@ -85,42 +87,42 @@ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])],
])
AC_ARG_ENABLE(benchmark,
- AS_HELP_STRING([--enable-benchmark],[compile benchmark (default is yes)]),
+ AS_HELP_STRING([--enable-benchmark],[compile benchmark [default=yes]]),
[use_benchmark=$enableval],
[use_benchmark=yes])
AC_ARG_ENABLE(coverage,
- AS_HELP_STRING([--enable-coverage],[enable compiler flags to support kcov coverage analysis]),
+ AS_HELP_STRING([--enable-coverage],[enable compiler flags to support kcov coverage analysis [default=no]]),
[enable_coverage=$enableval],
[enable_coverage=no])
AC_ARG_ENABLE(tests,
- AS_HELP_STRING([--enable-tests],[compile tests (default is yes)]),
+ AS_HELP_STRING([--enable-tests],[compile tests [default=yes]]),
[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)]),
+ AS_HELP_STRING([--enable-openssl-tests],[enable OpenSSL tests [default=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)]),
+ AS_HELP_STRING([--enable-experimental],[allow experimental configure options [default=no]]),
[use_experimental=$enableval],
[use_experimental=no])
AC_ARG_ENABLE(exhaustive_tests,
- AS_HELP_STRING([--enable-exhaustive-tests],[compile exhaustive tests (default is yes)]),
+ AS_HELP_STRING([--enable-exhaustive-tests],[compile exhaustive tests [default=yes]]),
[use_exhaustive_tests=$enableval],
[use_exhaustive_tests=yes])
AC_ARG_ENABLE(endomorphism,
- AS_HELP_STRING([--enable-endomorphism],[enable endomorphism (default is no)]),
+ AS_HELP_STRING([--enable-endomorphism],[enable endomorphism [default=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)]),
+ AS_HELP_STRING([--enable-ecmult-static-precomputation],[enable precomputed ecmult table for signing [default=auto]]),
[use_ecmult_static_precomputation=$enableval],
[use_ecmult_static_precomputation=auto])
@@ -130,35 +132,55 @@ AC_ARG_ENABLE(module_ecdh,
[enable_module_ecdh=no])
AC_ARG_ENABLE(module_recovery,
- AS_HELP_STRING([--enable-module-recovery],[enable ECDSA pubkey recovery module (default is no)]),
+ AS_HELP_STRING([--enable-module-recovery],[enable ECDSA pubkey recovery module [default=no]]),
[enable_module_recovery=$enableval],
[enable_module_recovery=no])
-AC_ARG_ENABLE(jni,
- AS_HELP_STRING([--enable-jni],[enable libsecp256k1_jni (default is no)]),
- [use_jni=$enableval],
- [use_jni=no])
+AC_ARG_ENABLE(external_default_callbacks,
+ AS_HELP_STRING([--enable-external-default-callbacks],[enable external default callback functions [default=no]]),
+ [use_external_default_callbacks=$enableval],
+ [use_external_default_callbacks=no])
AC_ARG_WITH([field], [AS_HELP_STRING([--with-field=64bit|32bit|auto],
-[Specify Field Implementation. Default is auto])],[req_field=$withval], [req_field=auto])
+[finite field implementation to use [default=auto]])],[req_field=$withval], [req_field=auto])
AC_ARG_WITH([bignum], [AS_HELP_STRING([--with-bignum=gmp|no|auto],
-[Specify Bignum Implementation. Default is auto])],[req_bignum=$withval], [req_bignum=auto])
+[bignum implementation to use [default=auto]])],[req_bignum=$withval], [req_bignum=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|arm|no|auto]
-[Specify assembly optimizations to use. Default is auto (experimental: arm)])],[req_asm=$withval], [req_asm=auto])
+[scalar implementation to use [default=auto]])],[req_scalar=$withval], [req_scalar=auto])
+
+AC_ARG_WITH([asm], [AS_HELP_STRING([--with-asm=x86_64|arm|no|auto],
+[assembly optimizations to use (experimental: arm) [default=auto]])],[req_asm=$withval], [req_asm=auto])
+
+AC_ARG_WITH([ecmult-window], [AS_HELP_STRING([--with-ecmult-window=SIZE|auto],
+[window size for ecmult precomputation for verification, specified as integer in range [2..24].]
+[Larger values result in possibly better performance at the cost of an exponentially larger precomputed table.]
+[The table will store 2^(SIZE-2) * 64 bytes of data but can be larger in memory due to platform-specific padding and alignment.]
+[If the endomorphism optimization is enabled, two tables of this size are used instead of only one.]
+["auto" is a reasonable setting for desktop machines (currently 15). [default=auto]]
+)],
+[req_ecmult_window=$withval], [req_ecmult_window=auto])
+
+AC_ARG_WITH([ecmult-gen-precision], [AS_HELP_STRING([--with-ecmult-gen-precision=2|4|8|auto],
+[Precision bits to tune the precomputed table size for signing.]
+[The size of the table is 32kB for 2 bits, 64kB for 4 bits, 512kB for 8 bits of precision.]
+[A larger table size usually results in possible faster signing.]
+["auto" is a reasonable setting for desktop machines (currently 4). [default=auto]]
+)],
+[req_ecmult_gen_precision=$withval], [req_ecmult_gen_precision=auto])
AC_CHECK_TYPES([__int128])
+AC_CHECK_HEADER([valgrind/memcheck.h], [enable_valgrind=yes], [enable_valgrind=no], [])
+AM_CONDITIONAL([VALGRIND_ENABLED],[test "$enable_valgrind" = "yes"])
+
if test x"$enable_coverage" = x"yes"; then
AC_DEFINE(COVERAGE, 1, [Define this symbol to compile out all VERIFY code])
- CFLAGS="$CFLAGS -O0 --coverage"
- LDFLAGS="--coverage"
+ CFLAGS="-O0 --coverage $CFLAGS"
+ LDFLAGS="--coverage $LDFLAGS"
else
- CFLAGS="$CFLAGS -O3"
+ CFLAGS="-O2 $CFLAGS"
fi
if test x"$use_ecmult_static_precomputation" != x"no"; then
@@ -176,7 +198,7 @@ if test x"$use_ecmult_static_precomputation" != x"no"; then
warn_CFLAGS_FOR_BUILD="-Wall -Wextra -Wno-unused-function"
saved_CFLAGS="$CFLAGS"
- CFLAGS="$CFLAGS $warn_CFLAGS_FOR_BUILD"
+ CFLAGS="$warn_CFLAGS_FOR_BUILD $CFLAGS"
AC_MSG_CHECKING([if native ${CC_FOR_BUILD} supports ${warn_CFLAGS_FOR_BUILD}])
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])],
[ AC_MSG_RESULT([yes]) ],
@@ -188,7 +210,7 @@ if test x"$use_ecmult_static_precomputation" != x"no"; then
AC_RUN_IFELSE(
[AC_LANG_PROGRAM([], [])],
[working_native_cc=yes],
- [working_native_cc=no],[dnl])
+ [working_native_cc=no],[:])
CFLAGS_FOR_BUILD="$CFLAGS"
@@ -387,12 +409,50 @@ case $set_scalar in
;;
esac
+#set ecmult window size
+if test x"$req_ecmult_window" = x"auto"; then
+ set_ecmult_window=15
+else
+ set_ecmult_window=$req_ecmult_window
+fi
+
+error_window_size=['window size for ecmult precomputation not an integer in range [2..24] or "auto"']
+case $set_ecmult_window in
+''|*[[!0-9]]*)
+ # no valid integer
+ AC_MSG_ERROR($error_window_size)
+ ;;
+*)
+ if test "$set_ecmult_window" -lt 2 -o "$set_ecmult_window" -gt 24 ; then
+ # not in range
+ AC_MSG_ERROR($error_window_size)
+ fi
+ AC_DEFINE_UNQUOTED(ECMULT_WINDOW_SIZE, $set_ecmult_window, [Set window size for ecmult precomputation])
+ ;;
+esac
+
+#set ecmult gen precision
+if test x"$req_ecmult_gen_precision" = x"auto"; then
+ set_ecmult_gen_precision=4
+else
+ set_ecmult_gen_precision=$req_ecmult_gen_precision
+fi
+
+case $set_ecmult_gen_precision in
+2|4|8)
+ AC_DEFINE_UNQUOTED(ECMULT_GEN_PREC_BITS, $set_ecmult_gen_precision, [Set ecmult gen precision bits])
+ ;;
+*)
+ AC_MSG_ERROR(['ecmult gen precision not 2, 4, 8 or "auto"'])
+ ;;
+esac
+
if test x"$use_tests" = x"yes"; then
SECP_OPENSSL_CHECK
if test x"$has_openssl_ec" = x"yes"; then
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_INCLUDES="$SSL_CFLAGS $CRYPTO_CFLAGS $CRYPTO_CPPFLAGS"
SECP_TEST_LIBS="$CRYPTO_LIBS"
case $host in
@@ -412,29 +472,6 @@ else
fi
fi
-if test x"$use_jni" != x"no"; then
- AX_JNI_INCLUDE_DIR
- have_jni_dependencies=yes
- 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 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
-
if test x"$set_bignum" = x"gmp"; then
SECP_LIBS="$SECP_LIBS $GMP_LIBS"
SECP_INCLUDES="$SECP_INCLUDES $GMP_CPPFLAGS"
@@ -462,6 +499,10 @@ 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
+if test x"$use_external_default_callbacks" = x"yes"; then
+ AC_DEFINE(USE_EXTERNAL_DEFAULT_CALLBACKS, 1, [Define this symbol if an external implementation of the default callbacks is used])
+fi
+
if test x"$enable_experimental" = x"yes"; then
AC_MSG_NOTICE([******])
AC_MSG_NOTICE([WARNING: experimental build])
@@ -479,7 +520,6 @@ 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)
@@ -491,7 +531,6 @@ AM_CONDITIONAL([USE_BENCHMARK], [test x"$use_benchmark" = x"yes"])
AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$set_precomp" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = 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"])
@@ -504,21 +543,24 @@ AC_OUTPUT
echo
echo "Build Options:"
-echo " with endomorphism = $use_endomorphism"
-echo " with ecmult precomp = $set_precomp"
-echo " with jni = $use_jni"
-echo " with benchmarks = $use_benchmark"
-echo " with coverage = $enable_coverage"
-echo " module ecdh = $enable_module_ecdh"
-echo " module recovery = $enable_module_recovery"
+echo " with endomorphism = $use_endomorphism"
+echo " with ecmult precomp = $set_precomp"
+echo " with external callbacks = $use_external_default_callbacks"
+echo " with benchmarks = $use_benchmark"
+echo " with coverage = $enable_coverage"
+echo " module ecdh = $enable_module_ecdh"
+echo " module recovery = $enable_module_recovery"
echo
-echo " asm = $set_asm"
-echo " bignum = $set_bignum"
-echo " field = $set_field"
-echo " scalar = $set_scalar"
+echo " asm = $set_asm"
+echo " bignum = $set_bignum"
+echo " field = $set_field"
+echo " scalar = $set_scalar"
+echo " ecmult window size = $set_ecmult_window"
+echo " ecmult gen prec. bits = $set_ecmult_gen_precision"
echo
-echo " CC = $CC"
-echo " CFLAGS = $CFLAGS"
-echo " CPPFLAGS = $CPPFLAGS"
-echo " LDFLAGS = $LDFLAGS"
+echo " valgrind = $enable_valgrind"
+echo " CC = $CC"
+echo " CFLAGS = $CFLAGS"
+echo " CPPFLAGS = $CPPFLAGS"
+echo " LDFLAGS = $LDFLAGS"
echo
diff --git a/src/secp256k1/contrib/lax_der_parsing.c b/src/secp256k1/contrib/lax_der_parsing.c
index 5b141a9948..e177a0562d 100644
--- a/src/secp256k1/contrib/lax_der_parsing.c
+++ b/src/secp256k1/contrib/lax_der_parsing.c
@@ -32,7 +32,7 @@ int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_
lenbyte = input[pos++];
if (lenbyte & 0x80) {
lenbyte -= 0x80;
- if (pos + lenbyte > inputlen) {
+ if (lenbyte > inputlen - pos) {
return 0;
}
pos += lenbyte;
@@ -51,7 +51,7 @@ int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_
lenbyte = input[pos++];
if (lenbyte & 0x80) {
lenbyte -= 0x80;
- if (pos + lenbyte > inputlen) {
+ if (lenbyte > inputlen - pos) {
return 0;
}
while (lenbyte > 0 && input[pos] == 0) {
@@ -89,7 +89,7 @@ int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_
lenbyte = input[pos++];
if (lenbyte & 0x80) {
lenbyte -= 0x80;
- if (pos + lenbyte > inputlen) {
+ if (lenbyte > inputlen - pos) {
return 0;
}
while (lenbyte > 0 && input[pos] == 0) {
diff --git a/src/secp256k1/contrib/travis.sh b/src/secp256k1/contrib/travis.sh
new file mode 100755
index 0000000000..3909d16a27
--- /dev/null
+++ b/src/secp256k1/contrib/travis.sh
@@ -0,0 +1,65 @@
+#!/bin/sh
+
+set -e
+set -x
+
+if [ -n "$HOST" ]
+then
+ export USE_HOST="--host=$HOST"
+fi
+if [ "$HOST" = "i686-linux-gnu" ]
+then
+ export CC="$CC -m32"
+fi
+if [ "$TRAVIS_OS_NAME" = "osx" ] && [ "$TRAVIS_COMPILER" = "gcc" ]
+then
+ export CC="gcc-9"
+fi
+
+./configure \
+ --enable-experimental="$EXPERIMENTAL" --enable-endomorphism="$ENDOMORPHISM" \
+ --with-field="$FIELD" --with-bignum="$BIGNUM" --with-asm="$ASM" --with-scalar="$SCALAR" \
+ --enable-ecmult-static-precomputation="$STATICPRECOMPUTATION" --with-ecmult-gen-precision="$ECMULTGENPRECISION" \
+ --enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" "$EXTRAFLAGS" "$USE_HOST"
+
+if [ -n "$BUILD" ]
+then
+ make -j2 "$BUILD"
+fi
+if [ -n "$VALGRIND" ]
+then
+ make -j2
+ # the `--error-exitcode` is required to make the test fail if valgrind found errors, otherwise it'll return 0 (http://valgrind.org/docs/manual/manual-core.html)
+ valgrind --error-exitcode=42 ./tests 16
+ valgrind --error-exitcode=42 ./exhaustive_tests
+fi
+if [ -n "$BENCH" ]
+then
+ if [ -n "$VALGRIND" ]
+ then
+ # Using the local `libtool` because on macOS the system's libtool has nothing to do with GNU libtool
+ EXEC='./libtool --mode=execute valgrind --error-exitcode=42'
+ else
+ EXEC=
+ fi
+ # This limits the iterations in the benchmarks below to ITER(set in .travis.yml) iterations.
+ export SECP256K1_BENCH_ITERS="$ITERS"
+ {
+ $EXEC ./bench_ecmult
+ $EXEC ./bench_internal
+ $EXEC ./bench_sign
+ $EXEC ./bench_verify
+ } >> bench.log 2>&1
+ if [ "$RECOVERY" = "yes" ]
+ then
+ $EXEC ./bench_recover >> bench.log 2>&1
+ fi
+ if [ "$ECDH" = "yes" ]
+ then
+ $EXEC ./bench_ecdh >> bench.log 2>&1
+ fi
+fi
+if [ -n "$CTIMETEST" ]
+then
+ ./libtool --mode=execute valgrind --error-exitcode=42 ./valgrind_ctime_test > valgrind_ctime_test.log 2>&1
+fi
diff --git a/src/secp256k1/include/secp256k1.h b/src/secp256k1/include/secp256k1.h
index 43af09c330..2ba2dca388 100644
--- a/src/secp256k1/include/secp256k1.h
+++ b/src/secp256k1/include/secp256k1.h
@@ -14,7 +14,7 @@ extern "C" {
* 2. Array lengths always immediately the follow the argument whose length
* they describe, even if this violates rule 1.
* 3. Within the OUT/OUTIN/IN groups, pointers to data that is typically generated
- * later go first. This means: signatures, public nonces, private nonces,
+ * later go first. This means: signatures, public nonces, secret nonces,
* messages, public keys, secret keys, tweaks.
* 4. Arguments that are not data pointers go last, from more complex to less
* complex: function pointers, algorithm names, messages, void pointers,
@@ -33,9 +33,10 @@ extern "C" {
* verification).
*
* A constructed context can safely be used from multiple threads
- * simultaneously, but API call that take a non-const pointer to a context
+ * simultaneously, but API calls that take a non-const pointer to a context
* need exclusive access to it. In particular this is the case for
- * secp256k1_context_destroy and secp256k1_context_randomize.
+ * secp256k1_context_destroy, secp256k1_context_preallocated_destroy,
+ * and secp256k1_context_randomize.
*
* Regarding randomization, either do it once at creation time (in which case
* you do not need any locking for the other calls), or use a read-write lock.
@@ -161,14 +162,17 @@ typedef int (*secp256k1_nonce_function)(
/** The higher bits contain the actual data. Do not use directly. */
#define SECP256K1_FLAGS_BIT_CONTEXT_VERIFY (1 << 8)
#define SECP256K1_FLAGS_BIT_CONTEXT_SIGN (1 << 9)
+#define SECP256K1_FLAGS_BIT_CONTEXT_DECLASSIFY (1 << 10)
#define SECP256K1_FLAGS_BIT_COMPRESSION (1 << 8)
-/** Flags to pass to secp256k1_context_create. */
+/** Flags to pass to secp256k1_context_create, secp256k1_context_preallocated_size, and
+ * secp256k1_context_preallocated_create. */
#define SECP256K1_CONTEXT_VERIFY (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_VERIFY)
#define SECP256K1_CONTEXT_SIGN (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_SIGN)
+#define SECP256K1_CONTEXT_DECLASSIFY (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_DECLASSIFY)
#define SECP256K1_CONTEXT_NONE (SECP256K1_FLAGS_TYPE_CONTEXT)
-/** Flag to pass to secp256k1_ec_pubkey_serialize and secp256k1_ec_privkey_export. */
+/** Flag to pass to secp256k1_ec_pubkey_serialize. */
#define SECP256K1_EC_COMPRESSED (SECP256K1_FLAGS_TYPE_COMPRESSION | SECP256K1_FLAGS_BIT_COMPRESSION)
#define SECP256K1_EC_UNCOMPRESSED (SECP256K1_FLAGS_TYPE_COMPRESSION)
@@ -186,7 +190,11 @@ typedef int (*secp256k1_nonce_function)(
*/
SECP256K1_API extern const secp256k1_context *secp256k1_context_no_precomp;
-/** Create a secp256k1 context object.
+/** Create a secp256k1 context object (in dynamically allocated memory).
+ *
+ * This function uses malloc to allocate memory. It is guaranteed that malloc is
+ * called at most once for every call of this function. If you need to avoid dynamic
+ * memory allocation entirely, see the functions in secp256k1_preallocated.h.
*
* Returns: a newly created context object.
* In: flags: which parts of the context to initialize.
@@ -197,7 +205,11 @@ SECP256K1_API secp256k1_context* secp256k1_context_create(
unsigned int flags
) SECP256K1_WARN_UNUSED_RESULT;
-/** Copies a secp256k1 context object.
+/** Copy a secp256k1 context object (into dynamically allocated memory).
+ *
+ * This function uses malloc to allocate memory. It is guaranteed that malloc is
+ * called at most once for every call of this function. If you need to avoid dynamic
+ * memory allocation entirely, see the functions in secp256k1_preallocated.h.
*
* Returns: a newly created context object.
* Args: ctx: an existing context to copy (cannot be NULL)
@@ -206,10 +218,18 @@ SECP256K1_API secp256k1_context* secp256k1_context_clone(
const secp256k1_context* ctx
) SECP256K1_ARG_NONNULL(1) SECP256K1_WARN_UNUSED_RESULT;
-/** Destroy a secp256k1 context object.
+/** Destroy a secp256k1 context object (created in dynamically allocated memory).
*
* The context pointer may not be used afterwards.
- * Args: ctx: an existing context to destroy (cannot be NULL)
+ *
+ * The context to destroy must have been created using secp256k1_context_create
+ * or secp256k1_context_clone. If the context has instead been created using
+ * secp256k1_context_preallocated_create or secp256k1_context_preallocated_clone, the
+ * behaviour is undefined. In that case, secp256k1_context_preallocated_destroy must
+ * be used instead.
+ *
+ * Args: ctx: an existing context to destroy, constructed using
+ * secp256k1_context_create or secp256k1_context_clone
*/
SECP256K1_API void secp256k1_context_destroy(
secp256k1_context* ctx
@@ -229,11 +249,28 @@ SECP256K1_API void secp256k1_context_destroy(
* to cause a crash, though its return value and output arguments are
* undefined.
*
+ * When this function has not been called (or called with fn==NULL), then the
+ * default handler will be used. The library provides a default handler which
+ * writes the message to stderr and calls abort. This default handler can be
+ * replaced at link time if the preprocessor macro
+ * USE_EXTERNAL_DEFAULT_CALLBACKS is defined, which is the case if the build
+ * has been configured with --enable-external-default-callbacks. Then the
+ * following two symbols must be provided to link against:
+ * - void secp256k1_default_illegal_callback_fn(const char* message, void* data);
+ * - void secp256k1_default_error_callback_fn(const char* message, void* data);
+ * The library can call these default handlers even before a proper callback data
+ * pointer could have been set using secp256k1_context_set_illegal_callback or
+ * secp256k1_context_set_error_callback, e.g., when the creation of a context
+ * fails. In this case, the corresponding default handler will be called with
+ * the data pointer argument set to NULL.
+ *
* Args: ctx: an existing context object (cannot be NULL)
* In: fun: a pointer to a function to call when an illegal argument is
- * passed to the API, taking a message and an opaque pointer
- * (NULL restores a default handler that calls abort).
+ * passed to the API, taking a message and an opaque pointer.
+ * (NULL restores the default handler.)
* data: the opaque pointer to pass to fun above.
+ *
+ * See also secp256k1_context_set_error_callback.
*/
SECP256K1_API void secp256k1_context_set_illegal_callback(
secp256k1_context* ctx,
@@ -253,9 +290,12 @@ SECP256K1_API void secp256k1_context_set_illegal_callback(
*
* Args: ctx: an existing context object (cannot be NULL)
* In: fun: a pointer to a function to call when an internal error occurs,
- * taking a message and an opaque pointer (NULL restores a default
- * handler that calls abort).
+ * taking a message and an opaque pointer (NULL restores the
+ * default handler, see secp256k1_context_set_illegal_callback
+ * for details).
* data: the opaque pointer to pass to fun above.
+ *
+ * See also secp256k1_context_set_illegal_callback.
*/
SECP256K1_API void secp256k1_context_set_error_callback(
secp256k1_context* ctx,
@@ -267,21 +307,24 @@ SECP256K1_API void secp256k1_context_set_error_callback(
*
* Returns: a newly created scratch space.
* Args: ctx: an existing context object (cannot be NULL)
- * In: max_size: maximum amount of memory to allocate
+ * In: size: amount of memory to be available as scratch space. Some extra
+ * (<100 bytes) will be allocated for extra accounting.
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT secp256k1_scratch_space* secp256k1_scratch_space_create(
const secp256k1_context* ctx,
- size_t max_size
+ size_t size
) SECP256K1_ARG_NONNULL(1);
/** Destroy a secp256k1 scratch space.
*
* The pointer may not be used afterwards.
- * Args: scratch: space to destroy
+ * Args: ctx: a secp256k1 context object.
+ * scratch: space to destroy
*/
SECP256K1_API void secp256k1_scratch_space_destroy(
+ const secp256k1_context* ctx,
secp256k1_scratch_space* scratch
-);
+) SECP256K1_ARG_NONNULL(1);
/** Parse a variable-length public key into the pubkey object.
*
@@ -488,7 +531,7 @@ SECP256K1_API extern const secp256k1_nonce_function secp256k1_nonce_function_def
/** Create an ECDSA signature.
*
* Returns: 1: signature created
- * 0: the nonce generation function failed, or the private key was invalid.
+ * 0: the nonce generation function failed, or the secret key was invalid.
* Args: ctx: pointer to a context object, initialized for signing (cannot be NULL)
* Out: sig: pointer to an array where the signature will be placed (cannot be NULL)
* In: msg32: the 32-byte message hash being signed (cannot be NULL)
@@ -510,6 +553,11 @@ SECP256K1_API int secp256k1_ecdsa_sign(
/** Verify an ECDSA secret key.
*
+ * A secret key is valid if it is not 0 and less than the secp256k1 curve order
+ * when interpreted as an integer (most significant byte first). The
+ * probability of choosing a 32-byte string uniformly at random which is an
+ * invalid secret key is negligible.
+ *
* Returns: 1: secret key is valid
* 0: secret key is invalid
* Args: ctx: pointer to a context object (cannot be NULL)
@@ -526,7 +574,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_verify(
* 0: secret was invalid, try again
* Args: ctx: pointer to a context object, initialized for signing (cannot be NULL)
* Out: pubkey: pointer to the created public key (cannot be NULL)
- * In: seckey: pointer to a 32-byte private key (cannot be NULL)
+ * In: seckey: pointer to a 32-byte secret key (cannot be NULL)
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_create(
const secp256k1_context* ctx,
@@ -534,12 +582,24 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_create(
const unsigned char *seckey
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
-/** Negates a private key in place.
+/** Negates a secret key in place.
*
- * Returns: 1 always
- * Args: ctx: pointer to a context object
- * In/Out: seckey: pointer to the 32-byte private key to be negated (cannot be NULL)
+ * Returns: 0 if the given secret key is invalid according to
+ * secp256k1_ec_seckey_verify. 1 otherwise
+ * Args: ctx: pointer to a context object
+ * In/Out: seckey: pointer to the 32-byte secret key to be negated. If the
+ * secret key is invalid according to
+ * secp256k1_ec_seckey_verify, this function returns 0 and
+ * seckey will be set to some unspecified value. (cannot be
+ * NULL)
*/
+SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_negate(
+ const secp256k1_context* ctx,
+ unsigned char *seckey
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2);
+
+/** Same as secp256k1_ec_seckey_negate, but DEPRECATED. Will be removed in
+ * future versions. */
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_negate(
const secp256k1_context* ctx,
unsigned char *seckey
@@ -556,15 +616,29 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_negate(
secp256k1_pubkey *pubkey
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2);
-/** Tweak a private key by adding tweak to it.
- * Returns: 0 if the tweak was out of range (chance of around 1 in 2^128 for
- * uniformly random 32-byte arrays, or if the resulting private key
- * would be invalid (only when the tweak is the complement of the
- * private key). 1 otherwise.
- * Args: ctx: pointer to a context object (cannot be NULL).
- * In/Out: seckey: pointer to a 32-byte private key.
- * In: tweak: pointer to a 32-byte tweak.
- */
+/** Tweak a secret key by adding tweak to it.
+ *
+ * Returns: 0 if the arguments are invalid or the resulting secret key would be
+ * invalid (only when the tweak is the negation of the secret key). 1
+ * otherwise.
+ * Args: ctx: pointer to a context object (cannot be NULL).
+ * In/Out: seckey: pointer to a 32-byte secret key. If the secret key is
+ * invalid according to secp256k1_ec_seckey_verify, this
+ * function returns 0. seckey will be set to some unspecified
+ * value if this function returns 0. (cannot be NULL)
+ * In: tweak: pointer to a 32-byte tweak. If the tweak is invalid according to
+ * secp256k1_ec_seckey_verify, this function returns 0. For
+ * uniformly random 32-byte arrays the chance of being invalid
+ * is negligible (around 1 in 2^128) (cannot be NULL).
+ */
+SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_tweak_add(
+ const secp256k1_context* ctx,
+ unsigned char *seckey,
+ const unsigned char *tweak
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
+
+/** Same as secp256k1_ec_seckey_tweak_add, but DEPRECATED. Will be removed in
+ * future versions. */
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_add(
const secp256k1_context* ctx,
unsigned char *seckey,
@@ -572,14 +646,18 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_add(
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Tweak a public key by adding tweak times the generator to it.
- * Returns: 0 if the tweak was out of range (chance of around 1 in 2^128 for
- * uniformly random 32-byte arrays, or if the resulting public key
- * would be invalid (only when the tweak is the complement of the
- * corresponding private key). 1 otherwise.
- * Args: ctx: pointer to a context object initialized for validation
+ *
+ * Returns: 0 if the arguments are invalid or the resulting public key would be
+ * invalid (only when the tweak is the negation of the corresponding
+ * secret key). 1 otherwise.
+ * Args: ctx: pointer to a context object initialized for validation
* (cannot be NULL).
- * In/Out: pubkey: pointer to a public key object.
- * In: tweak: pointer to a 32-byte tweak.
+ * In/Out: pubkey: pointer to a public key object. pubkey will be set to an
+ * invalid value if this function returns 0 (cannot be NULL).
+ * In: tweak: pointer to a 32-byte tweak. If the tweak is invalid according to
+ * secp256k1_ec_seckey_verify, this function returns 0. For
+ * uniformly random 32-byte arrays the chance of being invalid
+ * is negligible (around 1 in 2^128) (cannot be NULL).
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_add(
const secp256k1_context* ctx,
@@ -587,13 +665,27 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_add(
const unsigned char *tweak
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
-/** Tweak a private key by multiplying it by a tweak.
- * Returns: 0 if the tweak was out of range (chance of around 1 in 2^128 for
- * uniformly random 32-byte arrays, or equal to zero. 1 otherwise.
- * Args: ctx: pointer to a context object (cannot be NULL).
- * In/Out: seckey: pointer to a 32-byte private key.
- * In: tweak: pointer to a 32-byte tweak.
+/** Tweak a secret key by multiplying it by a tweak.
+ *
+ * Returns: 0 if the arguments are invalid. 1 otherwise.
+ * Args: ctx: pointer to a context object (cannot be NULL).
+ * In/Out: seckey: pointer to a 32-byte secret key. If the secret key is
+ * invalid according to secp256k1_ec_seckey_verify, this
+ * function returns 0. seckey will be set to some unspecified
+ * value if this function returns 0. (cannot be NULL)
+ * In: tweak: pointer to a 32-byte tweak. If the tweak is invalid according to
+ * secp256k1_ec_seckey_verify, this function returns 0. For
+ * uniformly random 32-byte arrays the chance of being invalid
+ * is negligible (around 1 in 2^128) (cannot be NULL).
*/
+SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_tweak_mul(
+ const secp256k1_context* ctx,
+ unsigned char *seckey,
+ const unsigned char *tweak
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
+
+/** Same as secp256k1_ec_seckey_tweak_mul, but DEPRECATED. Will be removed in
+ * future versions. */
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_mul(
const secp256k1_context* ctx,
unsigned char *seckey,
@@ -601,12 +693,16 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_mul(
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Tweak a public key by multiplying it by a tweak value.
- * Returns: 0 if the tweak was out of range (chance of around 1 in 2^128 for
- * uniformly random 32-byte arrays, or equal to zero. 1 otherwise.
- * Args: ctx: pointer to a context object initialized for validation
- * (cannot be NULL).
- * In/Out: pubkey: pointer to a public key obkect.
- * In: tweak: pointer to a 32-byte tweak.
+ *
+ * Returns: 0 if the arguments are invalid. 1 otherwise.
+ * Args: ctx: pointer to a context object initialized for validation
+ * (cannot be NULL).
+ * In/Out: pubkey: pointer to a public key object. pubkey will be set to an
+ * invalid value if this function returns 0 (cannot be NULL).
+ * In: tweak: pointer to a 32-byte tweak. If the tweak is invalid according to
+ * secp256k1_ec_seckey_verify, this function returns 0. For
+ * uniformly random 32-byte arrays the chance of being invalid
+ * is negligible (around 1 in 2^128) (cannot be NULL).
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_mul(
const secp256k1_context* ctx,
@@ -636,7 +732,8 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_mul(
* contexts not initialized for signing; then it will have no effect and return 1.
*
* You should call this after secp256k1_context_create or
- * secp256k1_context_clone, and may call this repeatedly afterwards.
+ * secp256k1_context_clone (and secp256k1_context_preallocated_create or
+ * secp256k1_context_clone, resp.), and you may call this repeatedly afterwards.
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_context_randomize(
secp256k1_context* ctx,
@@ -644,6 +741,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_context_randomize(
) SECP256K1_ARG_NONNULL(1);
/** Add a number of public keys together.
+ *
* Returns: 1: the sum of the public keys is valid.
* 0: the sum of the public keys is not valid.
* Args: ctx: pointer to a context object
diff --git a/src/secp256k1/include/secp256k1_ecdh.h b/src/secp256k1/include/secp256k1_ecdh.h
index df5fde235c..4058e9c043 100644
--- a/src/secp256k1/include/secp256k1_ecdh.h
+++ b/src/secp256k1/include/secp256k1_ecdh.h
@@ -7,43 +7,50 @@
extern "C" {
#endif
-/** A pointer to a function that applies hash function to a point
+/** A pointer to a function that hashes an EC point to obtain an ECDH secret
*
- * Returns: 1 if a point was successfully hashed. 0 will cause ecdh to fail
- * Out: output: pointer to an array to be filled by the function
- * In: x: pointer to a 32-byte x coordinate
- * y: pointer to a 32-byte y coordinate
- * data: Arbitrary data pointer that is passed through
+ * Returns: 1 if the point was successfully hashed.
+ * 0 will cause secp256k1_ecdh to fail and return 0.
+ * Other return values are not allowed, and the behaviour of
+ * secp256k1_ecdh is undefined for other return values.
+ * Out: output: pointer to an array to be filled by the function
+ * In: x32: pointer to a 32-byte x coordinate
+ * y32: pointer to a 32-byte y coordinate
+ * data: arbitrary data pointer that is passed through
*/
typedef int (*secp256k1_ecdh_hash_function)(
unsigned char *output,
- const unsigned char *x,
- const unsigned char *y,
+ const unsigned char *x32,
+ const unsigned char *y32,
void *data
);
-/** An implementation of SHA256 hash function that applies to compressed public key. */
+/** An implementation of SHA256 hash function that applies to compressed public key.
+ * Populates the output parameter with 32 bytes. */
SECP256K1_API extern const secp256k1_ecdh_hash_function secp256k1_ecdh_hash_function_sha256;
-/** A default ecdh hash function (currently equal to secp256k1_ecdh_hash_function_sha256). */
+/** A default ECDH hash function (currently equal to secp256k1_ecdh_hash_function_sha256).
+ * Populates the output parameter with 32 bytes. */
SECP256K1_API extern const secp256k1_ecdh_hash_function secp256k1_ecdh_hash_function_default;
/** Compute an EC Diffie-Hellman secret in constant time
+ *
* Returns: 1: exponentiation was successful
- * 0: scalar was invalid (zero or overflow)
+ * 0: scalar was invalid (zero or overflow) or hashfp returned 0
* Args: ctx: pointer to a context object (cannot be NULL)
- * Out: output: pointer to an array to be filled by the function
+ * Out: output: pointer to an array to be filled by hashfp
* In: pubkey: a pointer to a secp256k1_pubkey containing an
* initialized public key
- * privkey: a 32-byte scalar with which to multiply the point
+ * seckey: a 32-byte scalar with which to multiply the point
* hashfp: pointer to a hash function. If NULL, secp256k1_ecdh_hash_function_sha256 is used
- * data: Arbitrary data pointer that is passed through
+ * (in which case, 32 bytes will be written to output)
+ * data: arbitrary data pointer that is passed through to hashfp
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdh(
const secp256k1_context* ctx,
unsigned char *output,
const secp256k1_pubkey *pubkey,
- const unsigned char *privkey,
+ const unsigned char *seckey,
secp256k1_ecdh_hash_function hashfp,
void *data
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
diff --git a/src/secp256k1/include/secp256k1_preallocated.h b/src/secp256k1/include/secp256k1_preallocated.h
new file mode 100644
index 0000000000..a9ae15d5ae
--- /dev/null
+++ b/src/secp256k1/include/secp256k1_preallocated.h
@@ -0,0 +1,128 @@
+#ifndef SECP256K1_PREALLOCATED_H
+#define SECP256K1_PREALLOCATED_H
+
+#include "secp256k1.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* The module provided by this header file is intended for settings in which it
+ * is not possible or desirable to rely on dynamic memory allocation. It provides
+ * functions for creating, cloning, and destroying secp256k1 context objects in a
+ * contiguous fixed-size block of memory provided by the caller.
+ *
+ * Context objects created by functions in this module can be used like contexts
+ * objects created by functions in secp256k1.h, i.e., they can be passed to any
+ * API function that expects a context object (see secp256k1.h for details). The
+ * only exception is that context objects created by functions in this module
+ * must be destroyed using secp256k1_context_preallocated_destroy (in this
+ * module) instead of secp256k1_context_destroy (in secp256k1.h).
+ *
+ * It is guaranteed that functions in this module will not call malloc or its
+ * friends realloc, calloc, and free.
+ */
+
+/** Determine the memory size of a secp256k1 context object to be created in
+ * caller-provided memory.
+ *
+ * The purpose of this function is to determine how much memory must be provided
+ * to secp256k1_context_preallocated_create.
+ *
+ * Returns: the required size of the caller-provided memory block
+ * In: flags: which parts of the context to initialize.
+ */
+SECP256K1_API size_t secp256k1_context_preallocated_size(
+ unsigned int flags
+) SECP256K1_WARN_UNUSED_RESULT;
+
+/** Create a secp256k1 context object in caller-provided memory.
+ *
+ * The caller must provide a pointer to a rewritable contiguous block of memory
+ * of size at least secp256k1_context_preallocated_size(flags) bytes, suitably
+ * aligned to hold an object of any type.
+ *
+ * The block of memory is exclusively owned by the created context object during
+ * the lifetime of this context object, which begins with the call to this
+ * function and ends when a call to secp256k1_context_preallocated_destroy
+ * (which destroys the context object again) returns. During the lifetime of the
+ * context object, the caller is obligated not to access this block of memory,
+ * i.e., the caller may not read or write the memory, e.g., by copying the memory
+ * contents to a different location or trying to create a second context object
+ * in the memory. In simpler words, the prealloc pointer (or any pointer derived
+ * from it) should not be used during the lifetime of the context object.
+ *
+ * Returns: a newly created context object.
+ * In: prealloc: a pointer to a rewritable contiguous block of memory of
+ * size at least secp256k1_context_preallocated_size(flags)
+ * bytes, as detailed above (cannot be NULL)
+ * flags: which parts of the context to initialize.
+ *
+ * See also secp256k1_context_randomize (in secp256k1.h)
+ * and secp256k1_context_preallocated_destroy.
+ */
+SECP256K1_API secp256k1_context* secp256k1_context_preallocated_create(
+ void* prealloc,
+ unsigned int flags
+) SECP256K1_ARG_NONNULL(1) SECP256K1_WARN_UNUSED_RESULT;
+
+/** Determine the memory size of a secp256k1 context object to be copied into
+ * caller-provided memory.
+ *
+ * Returns: the required size of the caller-provided memory block.
+ * In: ctx: an existing context to copy (cannot be NULL)
+ */
+SECP256K1_API size_t secp256k1_context_preallocated_clone_size(
+ const secp256k1_context* ctx
+) SECP256K1_ARG_NONNULL(1) SECP256K1_WARN_UNUSED_RESULT;
+
+/** Copy a secp256k1 context object into caller-provided memory.
+ *
+ * The caller must provide a pointer to a rewritable contiguous block of memory
+ * of size at least secp256k1_context_preallocated_size(flags) bytes, suitably
+ * aligned to hold an object of any type.
+ *
+ * The block of memory is exclusively owned by the created context object during
+ * the lifetime of this context object, see the description of
+ * secp256k1_context_preallocated_create for details.
+ *
+ * Returns: a newly created context object.
+ * Args: ctx: an existing context to copy (cannot be NULL)
+ * In: prealloc: a pointer to a rewritable contiguous block of memory of
+ * size at least secp256k1_context_preallocated_size(flags)
+ * bytes, as detailed above (cannot be NULL)
+ */
+SECP256K1_API secp256k1_context* secp256k1_context_preallocated_clone(
+ const secp256k1_context* ctx,
+ void* prealloc
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_WARN_UNUSED_RESULT;
+
+/** Destroy a secp256k1 context object that has been created in
+ * caller-provided memory.
+ *
+ * The context pointer may not be used afterwards.
+ *
+ * The context to destroy must have been created using
+ * secp256k1_context_preallocated_create or secp256k1_context_preallocated_clone.
+ * If the context has instead been created using secp256k1_context_create or
+ * secp256k1_context_clone, the behaviour is undefined. In that case,
+ * secp256k1_context_destroy must be used instead.
+ *
+ * If required, it is the responsibility of the caller to deallocate the block
+ * of memory properly after this function returns, e.g., by calling free on the
+ * preallocated pointer given to secp256k1_context_preallocated_create or
+ * secp256k1_context_preallocated_clone.
+ *
+ * Args: ctx: an existing context to destroy, constructed using
+ * secp256k1_context_preallocated_create or
+ * secp256k1_context_preallocated_clone (cannot be NULL)
+ */
+SECP256K1_API void secp256k1_context_preallocated_destroy(
+ secp256k1_context* ctx
+);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SECP256K1_PREALLOCATED_H */
diff --git a/src/secp256k1/include/secp256k1_recovery.h b/src/secp256k1/include/secp256k1_recovery.h
index cf6c5ed7f5..f8ccaecd3d 100644
--- a/src/secp256k1/include/secp256k1_recovery.h
+++ b/src/secp256k1/include/secp256k1_recovery.h
@@ -70,7 +70,7 @@ SECP256K1_API int secp256k1_ecdsa_recoverable_signature_serialize_compact(
/** Create a recoverable ECDSA signature.
*
* Returns: 1: signature created
- * 0: the nonce generation function failed, or the private key was invalid.
+ * 0: the nonce generation function failed, or the secret key was invalid.
* Args: ctx: pointer to a context object, initialized for signing (cannot be NULL)
* Out: sig: pointer to an array where the signature will be placed (cannot be NULL)
* In: msg32: the 32-byte message hash being signed (cannot be NULL)
diff --git a/src/secp256k1/src/asm/field_10x26_arm.s b/src/secp256k1/src/asm/field_10x26_arm.s
index 5a9cc3ffcf..9a5bd06721 100644
--- a/src/secp256k1/src/asm/field_10x26_arm.s
+++ b/src/secp256k1/src/asm/field_10x26_arm.s
@@ -16,15 +16,9 @@ Note:
*/
.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 = Aggressive Speed
- .eabi_attribute 34, 1 @ Tag_CPU_unaligned_access = v6
.text
@ Field constants
diff --git a/src/secp256k1/src/basic-config.h b/src/secp256k1/src/basic-config.h
index fc588061ca..e9be39d4ca 100644
--- a/src/secp256k1/src/basic-config.h
+++ b/src/secp256k1/src/basic-config.h
@@ -10,7 +10,10 @@
#ifdef USE_BASIC_CONFIG
#undef USE_ASM_X86_64
+#undef USE_ECMULT_STATIC_PRECOMPUTATION
#undef USE_ENDOMORPHISM
+#undef USE_EXTERNAL_ASM
+#undef USE_EXTERNAL_DEFAULT_CALLBACKS
#undef USE_FIELD_10X26
#undef USE_FIELD_5X52
#undef USE_FIELD_INV_BUILTIN
@@ -21,12 +24,14 @@
#undef USE_SCALAR_8X32
#undef USE_SCALAR_INV_BUILTIN
#undef USE_SCALAR_INV_NUM
+#undef ECMULT_WINDOW_SIZE
#define USE_NUM_NONE 1
#define USE_FIELD_INV_BUILTIN 1
#define USE_SCALAR_INV_BUILTIN 1
#define USE_FIELD_10X26 1
#define USE_SCALAR_8X32 1
+#define ECMULT_WINDOW_SIZE 15
#endif /* USE_BASIC_CONFIG */
diff --git a/src/secp256k1/src/bench.h b/src/secp256k1/src/bench.h
index 5b59783f68..9bfed903e0 100644
--- a/src/secp256k1/src/bench.h
+++ b/src/secp256k1/src/bench.h
@@ -7,45 +7,87 @@
#ifndef SECP256K1_BENCH_H
#define SECP256K1_BENCH_H
+#include <stdint.h>
#include <stdio.h>
#include <string.h>
-#include <math.h>
#include "sys/time.h"
-static double gettimedouble(void) {
+static int64_t gettime_i64(void) {
struct timeval tv;
gettimeofday(&tv, NULL);
- return tv.tv_usec * 0.000001 + tv.tv_sec;
+ return (int64_t)tv.tv_usec + (int64_t)tv.tv_sec * 1000000LL;
}
-void print_number(double x) {
- double y = x;
- int c = 0;
- if (y < 0.0) {
- y = -y;
+#define FP_EXP (6)
+#define FP_MULT (1000000LL)
+
+/* Format fixed point number. */
+void print_number(const int64_t x) {
+ int64_t x_abs, y;
+ int c, i, rounding;
+ size_t ptr;
+ char buffer[30];
+
+ if (x == INT64_MIN) {
+ /* Prevent UB. */
+ printf("ERR");
+ return;
}
- while (y > 0 && y < 100.0) {
- y *= 10.0;
+ x_abs = x < 0 ? -x : x;
+
+ /* Determine how many decimals we want to show (more than FP_EXP makes no
+ * sense). */
+ y = x_abs;
+ c = 0;
+ while (y > 0LL && y < 100LL * FP_MULT && c < FP_EXP) {
+ y *= 10LL;
c++;
}
- printf("%.*f", c, x);
+
+ /* Round to 'c' decimals. */
+ y = x_abs;
+ rounding = 0;
+ for (i = c; i < FP_EXP; ++i) {
+ rounding = (y % 10) >= 5;
+ y /= 10;
+ }
+ y += rounding;
+
+ /* Format and print the number. */
+ ptr = sizeof(buffer) - 1;
+ buffer[ptr] = 0;
+ if (c != 0) {
+ for (i = 0; i < c; ++i) {
+ buffer[--ptr] = '0' + (y % 10);
+ y /= 10;
+ }
+ buffer[--ptr] = '.';
+ }
+ do {
+ buffer[--ptr] = '0' + (y % 10);
+ y /= 10;
+ } while (y != 0);
+ if (x < 0) {
+ buffer[--ptr] = '-';
+ }
+ printf("%s", &buffer[ptr]);
}
-void run_benchmark(char *name, void (*benchmark)(void*), void (*setup)(void*), void (*teardown)(void*), void* data, int count, int iter) {
+void run_benchmark(char *name, void (*benchmark)(void*, int), void (*setup)(void*), void (*teardown)(void*, int), void* data, int count, int iter) {
int i;
- double min = HUGE_VAL;
- double sum = 0.0;
- double max = 0.0;
+ int64_t min = INT64_MAX;
+ int64_t sum = 0;
+ int64_t max = 0;
for (i = 0; i < count; i++) {
- double begin, total;
+ int64_t begin, total;
if (setup != NULL) {
setup(data);
}
- begin = gettimedouble();
- benchmark(data);
- total = gettimedouble() - begin;
+ begin = gettime_i64();
+ benchmark(data, iter);
+ total = gettime_i64() - begin;
if (teardown != NULL) {
- teardown(data);
+ teardown(data, iter);
}
if (total < min) {
min = total;
@@ -56,11 +98,11 @@ void run_benchmark(char *name, void (*benchmark)(void*), void (*setup)(void*), v
sum += total;
}
printf("%s: min ", name);
- print_number(min * 1000000.0 / iter);
+ print_number(min * FP_MULT / iter);
printf("us / avg ");
- print_number((sum / count) * 1000000.0 / iter);
+ print_number(((sum * FP_MULT) / count) / iter);
printf("us / max ");
- print_number(max * 1000000.0 / iter);
+ print_number(max * FP_MULT / iter);
printf("us\n");
}
@@ -79,4 +121,13 @@ int have_flag(int argc, char** argv, char *flag) {
return 0;
}
+int get_iters(int default_iters) {
+ char* env = getenv("SECP256K1_BENCH_ITERS");
+ if (env) {
+ return strtol(env, NULL, 0);
+ } else {
+ return default_iters;
+ }
+}
+
#endif /* SECP256K1_BENCH_H */
diff --git a/src/secp256k1/src/bench_ecdh.c b/src/secp256k1/src/bench_ecdh.c
index c1dd5a6ac9..f099d33884 100644
--- a/src/secp256k1/src/bench_ecdh.c
+++ b/src/secp256k1/src/bench_ecdh.c
@@ -28,20 +28,18 @@ static void bench_ecdh_setup(void* arg) {
0xa2, 0xba, 0xd1, 0x84, 0xf8, 0x83, 0xc6, 0x9f
};
- /* 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;
}
CHECK(secp256k1_ec_pubkey_parse(data->ctx, &data->point, point, sizeof(point)) == 1);
}
-static void bench_ecdh(void* arg) {
+static void bench_ecdh(void* arg, int iters) {
int i;
unsigned char res[32];
bench_ecdh_data *data = (bench_ecdh_data*)arg;
- for (i = 0; i < 20000; i++) {
+ for (i = 0; i < iters; i++) {
CHECK(secp256k1_ecdh(data->ctx, res, &data->point, data->scalar, NULL, NULL) == 1);
}
}
@@ -49,6 +47,13 @@ static void bench_ecdh(void* arg) {
int main(void) {
bench_ecdh_data data;
- run_benchmark("ecdh", bench_ecdh, bench_ecdh_setup, NULL, &data, 10, 20000);
+ int iters = get_iters(20000);
+
+ /* create a context with no capabilities */
+ data.ctx = secp256k1_context_create(SECP256K1_FLAGS_TYPE_CONTEXT);
+
+ run_benchmark("ecdh", bench_ecdh, bench_ecdh_setup, NULL, &data, 10, iters);
+
+ secp256k1_context_destroy(data.ctx);
return 0;
}
diff --git a/src/secp256k1/src/bench_ecmult.c b/src/secp256k1/src/bench_ecmult.c
index 6d0ed1f436..facd07ef31 100644
--- a/src/secp256k1/src/bench_ecmult.c
+++ b/src/secp256k1/src/bench_ecmult.c
@@ -18,7 +18,6 @@
#include "secp256k1.c"
#define POINTS 32768
-#define ITERS 10000
typedef struct {
/* Setup once in advance */
@@ -55,16 +54,16 @@ static int bench_callback(secp256k1_scalar* sc, secp256k1_ge* ge, size_t idx, vo
return 1;
}
-static void bench_ecmult(void* arg) {
+static void bench_ecmult(void* arg, int iters) {
bench_data* data = (bench_data*)arg;
- size_t count = data->count;
int includes_g = data->includes_g;
- size_t iters = 1 + ITERS / count;
- size_t iter;
+ int iter;
+ int count = data->count;
+ iters = iters / data->count;
for (iter = 0; iter < iters; ++iter) {
- data->ecmult_multi(&data->ctx->ecmult_ctx, data->scratch, &data->output[iter], data->includes_g ? &data->scalars[data->offset1] : NULL, bench_callback, arg, count - includes_g);
+ data->ecmult_multi(&data->ctx->error_callback, &data->ctx->ecmult_ctx, data->scratch, &data->output[iter], data->includes_g ? &data->scalars[data->offset1] : NULL, bench_callback, arg, count - includes_g);
data->offset1 = (data->offset1 + count) % POINTS;
data->offset2 = (data->offset2 + count - 1) % POINTS;
}
@@ -76,10 +75,10 @@ static void bench_ecmult_setup(void* arg) {
data->offset2 = (data->count * 0x7f6f537b + 0x6a1a8f49) % POINTS;
}
-static void bench_ecmult_teardown(void* arg) {
+static void bench_ecmult_teardown(void* arg, int iters) {
bench_data* data = (bench_data*)arg;
- size_t iters = 1 + ITERS / data->count;
- size_t iter;
+ int iter;
+ iters = iters / data->count;
/* Verify the results in teardown, to avoid doing comparisons while benchmarking. */
for (iter = 0; iter < iters; ++iter) {
secp256k1_gej tmp;
@@ -104,10 +103,10 @@ static void generate_scalar(uint32_t num, secp256k1_scalar* scalar) {
CHECK(!overflow);
}
-static void run_test(bench_data* data, size_t count, int includes_g) {
+static void run_test(bench_data* data, size_t count, int includes_g, int num_iters) {
char str[32];
static const secp256k1_scalar zero = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0);
- size_t iters = 1 + ITERS / count;
+ size_t iters = 1 + num_iters / count;
size_t iter;
data->count = count;
@@ -130,7 +129,7 @@ static void run_test(bench_data* data, size_t count, int includes_g) {
/* Run the benchmark. */
sprintf(str, includes_g ? "ecmult_%ig" : "ecmult_%i", (int)count);
- run_benchmark(str, bench_ecmult, bench_ecmult_setup, bench_ecmult_teardown, data, 10, count * (1 + ITERS / count));
+ run_benchmark(str, bench_ecmult, bench_ecmult_setup, bench_ecmult_teardown, data, 10, count * iters);
}
int main(int argc, char **argv) {
@@ -139,6 +138,8 @@ int main(int argc, char **argv) {
secp256k1_gej* pubkeys_gej;
size_t scratch_size;
+ int iters = get_iters(10000);
+
data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
scratch_size = secp256k1_strauss_scratch_size(POINTS) + STRAUSS_SCRATCH_OBJECTS*16;
data.scratch = secp256k1_scratch_space_create(data.ctx, scratch_size);
@@ -154,7 +155,7 @@ int main(int argc, char **argv) {
} else if(have_flag(argc, argv, "simple")) {
printf("Using simple algorithm:\n");
data.ecmult_multi = secp256k1_ecmult_multi_var;
- secp256k1_scratch_space_destroy(data.scratch);
+ secp256k1_scratch_space_destroy(data.ctx, data.scratch);
data.scratch = NULL;
} else {
fprintf(stderr, "%s: unrecognized argument '%s'.\n", argv[0], argv[1]);
@@ -167,8 +168,8 @@ int main(int argc, char **argv) {
data.scalars = malloc(sizeof(secp256k1_scalar) * POINTS);
data.seckeys = malloc(sizeof(secp256k1_scalar) * POINTS);
data.pubkeys = malloc(sizeof(secp256k1_ge) * POINTS);
- data.expected_output = malloc(sizeof(secp256k1_gej) * (ITERS + 1));
- data.output = malloc(sizeof(secp256k1_gej) * (ITERS + 1));
+ data.expected_output = malloc(sizeof(secp256k1_gej) * (iters + 1));
+ data.output = malloc(sizeof(secp256k1_gej) * (iters + 1));
/* Generate a set of scalars, and private/public keypairs. */
pubkeys_gej = malloc(sizeof(secp256k1_gej) * POINTS);
@@ -185,18 +186,24 @@ int main(int argc, char **argv) {
free(pubkeys_gej);
for (i = 1; i <= 8; ++i) {
- run_test(&data, i, 1);
+ run_test(&data, i, 1, iters);
}
- for (p = 0; p <= 11; ++p) {
- for (i = 9; i <= 16; ++i) {
- run_test(&data, i << p, 1);
+ /* This is disabled with low count of iterations because the loop runs 77 times even with iters=1
+ * and the higher it goes the longer the computation takes(more points)
+ * So we don't run this benchmark with low iterations to prevent slow down */
+ if (iters > 2) {
+ for (p = 0; p <= 11; ++p) {
+ for (i = 9; i <= 16; ++i) {
+ run_test(&data, i << p, 1, iters);
+ }
}
}
- secp256k1_context_destroy(data.ctx);
+
if (data.scratch != NULL) {
- secp256k1_scratch_space_destroy(data.scratch);
+ secp256k1_scratch_space_destroy(data.ctx, data.scratch);
}
+ secp256k1_context_destroy(data.ctx);
free(data.scalars);
free(data.pubkeys);
free(data.seckeys);
diff --git a/src/secp256k1/src/bench_internal.c b/src/secp256k1/src/bench_internal.c
index 9071724331..20759127d3 100644
--- a/src/secp256k1/src/bench_internal.c
+++ b/src/secp256k1/src/bench_internal.c
@@ -56,263 +56,272 @@ void bench_setup(void* arg) {
memcpy(data->data + 32, init_y, 32);
}
-void bench_scalar_add(void* arg) {
- int i;
+void bench_scalar_add(void* arg, int iters) {
+ int i, j = 0;
bench_inv *data = (bench_inv*)arg;
- for (i = 0; i < 2000000; i++) {
- secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
+ for (i = 0; i < iters; i++) {
+ j += secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
}
+ CHECK(j <= iters);
}
-void bench_scalar_negate(void* arg) {
+void bench_scalar_negate(void* arg, int iters) {
int i;
bench_inv *data = (bench_inv*)arg;
- for (i = 0; i < 2000000; i++) {
+ for (i = 0; i < iters; i++) {
secp256k1_scalar_negate(&data->scalar_x, &data->scalar_x);
}
}
-void bench_scalar_sqr(void* arg) {
+void bench_scalar_sqr(void* arg, int iters) {
int i;
bench_inv *data = (bench_inv*)arg;
- for (i = 0; i < 200000; i++) {
+ for (i = 0; i < iters; i++) {
secp256k1_scalar_sqr(&data->scalar_x, &data->scalar_x);
}
}
-void bench_scalar_mul(void* arg) {
+void bench_scalar_mul(void* arg, int iters) {
int i;
bench_inv *data = (bench_inv*)arg;
- for (i = 0; i < 200000; i++) {
+ for (i = 0; i < iters; i++) {
secp256k1_scalar_mul(&data->scalar_x, &data->scalar_x, &data->scalar_y);
}
}
#ifdef USE_ENDOMORPHISM
-void bench_scalar_split(void* arg) {
- int i;
+void bench_scalar_split(void* arg, int iters) {
+ int i, j = 0;
bench_inv *data = (bench_inv*)arg;
- for (i = 0; i < 20000; i++) {
- secp256k1_scalar l, r;
- secp256k1_scalar_split_lambda(&l, &r, &data->scalar_x);
- secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
+ for (i = 0; i < iters; i++) {
+ secp256k1_scalar_split_lambda(&data->scalar_x, &data->scalar_y, &data->scalar_x);
+ j += secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
}
+ CHECK(j <= iters);
}
#endif
-void bench_scalar_inverse(void* arg) {
- int i;
+void bench_scalar_inverse(void* arg, int iters) {
+ int i, j = 0;
bench_inv *data = (bench_inv*)arg;
- for (i = 0; i < 2000; i++) {
+ for (i = 0; i < iters; i++) {
secp256k1_scalar_inverse(&data->scalar_x, &data->scalar_x);
- secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
+ j += secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
}
+ CHECK(j <= iters);
}
-void bench_scalar_inverse_var(void* arg) {
- int i;
+void bench_scalar_inverse_var(void* arg, int iters) {
+ int i, j = 0;
bench_inv *data = (bench_inv*)arg;
- for (i = 0; i < 2000; i++) {
+ for (i = 0; i < iters; i++) {
secp256k1_scalar_inverse_var(&data->scalar_x, &data->scalar_x);
- secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
+ j += secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
}
+ CHECK(j <= iters);
}
-void bench_field_normalize(void* arg) {
+void bench_field_normalize(void* arg, int iters) {
int i;
bench_inv *data = (bench_inv*)arg;
- for (i = 0; i < 2000000; i++) {
+ for (i = 0; i < iters; i++) {
secp256k1_fe_normalize(&data->fe_x);
}
}
-void bench_field_normalize_weak(void* arg) {
+void bench_field_normalize_weak(void* arg, int iters) {
int i;
bench_inv *data = (bench_inv*)arg;
- for (i = 0; i < 2000000; i++) {
+ for (i = 0; i < iters; i++) {
secp256k1_fe_normalize_weak(&data->fe_x);
}
}
-void bench_field_mul(void* arg) {
+void bench_field_mul(void* arg, int iters) {
int i;
bench_inv *data = (bench_inv*)arg;
- for (i = 0; i < 200000; i++) {
+ for (i = 0; i < iters; i++) {
secp256k1_fe_mul(&data->fe_x, &data->fe_x, &data->fe_y);
}
}
-void bench_field_sqr(void* arg) {
+void bench_field_sqr(void* arg, int iters) {
int i;
bench_inv *data = (bench_inv*)arg;
- for (i = 0; i < 200000; i++) {
+ for (i = 0; i < iters; i++) {
secp256k1_fe_sqr(&data->fe_x, &data->fe_x);
}
}
-void bench_field_inverse(void* arg) {
+void bench_field_inverse(void* arg, int iters) {
int i;
bench_inv *data = (bench_inv*)arg;
- for (i = 0; i < 20000; i++) {
+ for (i = 0; i < iters; i++) {
secp256k1_fe_inv(&data->fe_x, &data->fe_x);
secp256k1_fe_add(&data->fe_x, &data->fe_y);
}
}
-void bench_field_inverse_var(void* arg) {
+void bench_field_inverse_var(void* arg, int iters) {
int i;
bench_inv *data = (bench_inv*)arg;
- for (i = 0; i < 20000; i++) {
+ for (i = 0; i < iters; i++) {
secp256k1_fe_inv_var(&data->fe_x, &data->fe_x);
secp256k1_fe_add(&data->fe_x, &data->fe_y);
}
}
-void bench_field_sqrt(void* arg) {
- int i;
+void bench_field_sqrt(void* arg, int iters) {
+ int i, j = 0;
bench_inv *data = (bench_inv*)arg;
secp256k1_fe t;
- for (i = 0; i < 20000; i++) {
+ for (i = 0; i < iters; i++) {
t = data->fe_x;
- secp256k1_fe_sqrt(&data->fe_x, &t);
+ j += secp256k1_fe_sqrt(&data->fe_x, &t);
secp256k1_fe_add(&data->fe_x, &data->fe_y);
}
+ CHECK(j <= iters);
}
-void bench_group_double_var(void* arg) {
+void bench_group_double_var(void* arg, int iters) {
int i;
bench_inv *data = (bench_inv*)arg;
- for (i = 0; i < 200000; i++) {
+ for (i = 0; i < iters; i++) {
secp256k1_gej_double_var(&data->gej_x, &data->gej_x, NULL);
}
}
-void bench_group_add_var(void* arg) {
+void bench_group_add_var(void* arg, int iters) {
int i;
bench_inv *data = (bench_inv*)arg;
- for (i = 0; i < 200000; i++) {
+ for (i = 0; i < iters; i++) {
secp256k1_gej_add_var(&data->gej_x, &data->gej_x, &data->gej_y, NULL);
}
}
-void bench_group_add_affine(void* arg) {
+void bench_group_add_affine(void* arg, int iters) {
int i;
bench_inv *data = (bench_inv*)arg;
- for (i = 0; i < 200000; i++) {
+ for (i = 0; i < iters; i++) {
secp256k1_gej_add_ge(&data->gej_x, &data->gej_x, &data->ge_y);
}
}
-void bench_group_add_affine_var(void* arg) {
+void bench_group_add_affine_var(void* arg, int iters) {
int i;
bench_inv *data = (bench_inv*)arg;
- for (i = 0; i < 200000; i++) {
+ for (i = 0; i < iters; i++) {
secp256k1_gej_add_ge_var(&data->gej_x, &data->gej_x, &data->ge_y, NULL);
}
}
-void bench_group_jacobi_var(void* arg) {
- int i;
+void bench_group_jacobi_var(void* arg, int iters) {
+ int i, j = 0;
bench_inv *data = (bench_inv*)arg;
- for (i = 0; i < 20000; i++) {
- secp256k1_gej_has_quad_y_var(&data->gej_x);
+ for (i = 0; i < iters; i++) {
+ j += secp256k1_gej_has_quad_y_var(&data->gej_x);
}
+ CHECK(j == iters);
}
-void bench_ecmult_wnaf(void* arg) {
- int i;
+void bench_ecmult_wnaf(void* arg, int iters) {
+ int i, bits = 0, overflow = 0;
bench_inv *data = (bench_inv*)arg;
- for (i = 0; i < 20000; i++) {
- secp256k1_ecmult_wnaf(data->wnaf, 256, &data->scalar_x, WINDOW_A);
- secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
+ for (i = 0; i < iters; i++) {
+ bits += secp256k1_ecmult_wnaf(data->wnaf, 256, &data->scalar_x, WINDOW_A);
+ overflow += secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
}
+ CHECK(overflow >= 0);
+ CHECK(bits <= 256*iters);
}
-void bench_wnaf_const(void* arg) {
- int i;
+void bench_wnaf_const(void* arg, int iters) {
+ int i, bits = 0, overflow = 0;
bench_inv *data = (bench_inv*)arg;
- for (i = 0; i < 20000; i++) {
- secp256k1_wnaf_const(data->wnaf, data->scalar_x, WINDOW_A, 256);
- secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
+ for (i = 0; i < iters; i++) {
+ bits += secp256k1_wnaf_const(data->wnaf, &data->scalar_x, WINDOW_A, 256);
+ overflow += secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
}
+ CHECK(overflow >= 0);
+ CHECK(bits <= 256*iters);
}
-void bench_sha256(void* arg) {
+void bench_sha256(void* arg, int iters) {
int i;
bench_inv *data = (bench_inv*)arg;
secp256k1_sha256 sha;
- for (i = 0; i < 20000; i++) {
+ for (i = 0; i < iters; i++) {
secp256k1_sha256_initialize(&sha);
secp256k1_sha256_write(&sha, data->data, 32);
secp256k1_sha256_finalize(&sha, data->data);
}
}
-void bench_hmac_sha256(void* arg) {
+void bench_hmac_sha256(void* arg, int iters) {
int i;
bench_inv *data = (bench_inv*)arg;
secp256k1_hmac_sha256 hmac;
- for (i = 0; i < 20000; i++) {
+ for (i = 0; i < iters; i++) {
secp256k1_hmac_sha256_initialize(&hmac, data->data, 32);
secp256k1_hmac_sha256_write(&hmac, data->data, 32);
secp256k1_hmac_sha256_finalize(&hmac, data->data);
}
}
-void bench_rfc6979_hmac_sha256(void* arg) {
+void bench_rfc6979_hmac_sha256(void* arg, int iters) {
int i;
bench_inv *data = (bench_inv*)arg;
secp256k1_rfc6979_hmac_sha256 rng;
- for (i = 0; i < 20000; i++) {
+ for (i = 0; i < iters; i++) {
secp256k1_rfc6979_hmac_sha256_initialize(&rng, data->data, 64);
secp256k1_rfc6979_hmac_sha256_generate(&rng, data->data, 32);
}
}
-void bench_context_verify(void* arg) {
+void bench_context_verify(void* arg, int iters) {
int i;
(void)arg;
- for (i = 0; i < 20; i++) {
+ for (i = 0; i < iters; i++) {
secp256k1_context_destroy(secp256k1_context_create(SECP256K1_CONTEXT_VERIFY));
}
}
-void bench_context_sign(void* arg) {
+void bench_context_sign(void* arg, int iters) {
int i;
(void)arg;
- for (i = 0; i < 200; i++) {
+ for (i = 0; i < iters; i++) {
secp256k1_context_destroy(secp256k1_context_create(SECP256K1_CONTEXT_SIGN));
}
}
#ifndef USE_NUM_NONE
-void bench_num_jacobi(void* arg) {
- int i;
+void bench_num_jacobi(void* arg, int iters) {
+ int i, j = 0;
bench_inv *data = (bench_inv*)arg;
secp256k1_num nx, norder;
@@ -320,50 +329,53 @@ void bench_num_jacobi(void* arg) {
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);
+ for (i = 0; i < iters; i++) {
+ j += secp256k1_num_jacobi(&nx, &norder);
}
+ CHECK(j <= iters);
}
#endif
int main(int argc, char **argv) {
bench_inv data;
- if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "add")) run_benchmark("scalar_add", bench_scalar_add, bench_setup, NULL, &data, 10, 2000000);
- if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "negate")) run_benchmark("scalar_negate", bench_scalar_negate, bench_setup, NULL, &data, 10, 2000000);
- if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "sqr")) run_benchmark("scalar_sqr", bench_scalar_sqr, bench_setup, NULL, &data, 10, 200000);
- if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "mul")) run_benchmark("scalar_mul", bench_scalar_mul, bench_setup, NULL, &data, 10, 200000);
+ int iters = get_iters(20000);
+
+ if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "add")) run_benchmark("scalar_add", bench_scalar_add, bench_setup, NULL, &data, 10, iters*100);
+ if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "negate")) run_benchmark("scalar_negate", bench_scalar_negate, bench_setup, NULL, &data, 10, iters*100);
+ if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "sqr")) run_benchmark("scalar_sqr", bench_scalar_sqr, bench_setup, NULL, &data, 10, iters*10);
+ if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "mul")) run_benchmark("scalar_mul", bench_scalar_mul, bench_setup, NULL, &data, 10, iters*10);
#ifdef USE_ENDOMORPHISM
- if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "split")) run_benchmark("scalar_split", bench_scalar_split, bench_setup, NULL, &data, 10, 20000);
+ if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "split")) run_benchmark("scalar_split", bench_scalar_split, bench_setup, NULL, &data, 10, iters);
#endif
if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "inverse")) run_benchmark("scalar_inverse", bench_scalar_inverse, bench_setup, NULL, &data, 10, 2000);
if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "inverse")) run_benchmark("scalar_inverse_var", bench_scalar_inverse_var, bench_setup, NULL, &data, 10, 2000);
- if (have_flag(argc, argv, "field") || have_flag(argc, argv, "normalize")) run_benchmark("field_normalize", bench_field_normalize, bench_setup, NULL, &data, 10, 2000000);
- if (have_flag(argc, argv, "field") || have_flag(argc, argv, "normalize")) run_benchmark("field_normalize_weak", bench_field_normalize_weak, bench_setup, NULL, &data, 10, 2000000);
- if (have_flag(argc, argv, "field") || have_flag(argc, argv, "sqr")) run_benchmark("field_sqr", bench_field_sqr, bench_setup, NULL, &data, 10, 200000);
- 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", bench_field_sqrt, bench_setup, NULL, &data, 10, 20000);
+ if (have_flag(argc, argv, "field") || have_flag(argc, argv, "normalize")) run_benchmark("field_normalize", bench_field_normalize, bench_setup, NULL, &data, 10, iters*100);
+ if (have_flag(argc, argv, "field") || have_flag(argc, argv, "normalize")) run_benchmark("field_normalize_weak", bench_field_normalize_weak, bench_setup, NULL, &data, 10, iters*100);
+ if (have_flag(argc, argv, "field") || have_flag(argc, argv, "sqr")) run_benchmark("field_sqr", bench_field_sqr, bench_setup, NULL, &data, 10, iters*10);
+ if (have_flag(argc, argv, "field") || have_flag(argc, argv, "mul")) run_benchmark("field_mul", bench_field_mul, bench_setup, NULL, &data, 10, iters*10);
+ if (have_flag(argc, argv, "field") || have_flag(argc, argv, "inverse")) run_benchmark("field_inverse", bench_field_inverse, bench_setup, NULL, &data, 10, iters);
+ 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, iters);
+ if (have_flag(argc, argv, "field") || have_flag(argc, argv, "sqrt")) run_benchmark("field_sqrt", bench_field_sqrt, bench_setup, NULL, &data, 10, iters);
- 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, "group") || have_flag(argc, argv, "double")) run_benchmark("group_double_var", bench_group_double_var, bench_setup, NULL, &data, 10, iters*10);
+ 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, iters*10);
+ 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, iters*10);
+ 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, iters*10);
+ 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, iters);
- 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);
+ if (have_flag(argc, argv, "ecmult") || have_flag(argc, argv, "wnaf")) run_benchmark("wnaf_const", bench_wnaf_const, bench_setup, NULL, &data, 10, iters);
+ if (have_flag(argc, argv, "ecmult") || have_flag(argc, argv, "wnaf")) run_benchmark("ecmult_wnaf", bench_ecmult_wnaf, bench_setup, NULL, &data, 10, iters);
- if (have_flag(argc, argv, "hash") || have_flag(argc, argv, "sha256")) run_benchmark("hash_sha256", bench_sha256, bench_setup, NULL, &data, 10, 20000);
- if (have_flag(argc, argv, "hash") || have_flag(argc, argv, "hmac")) run_benchmark("hash_hmac_sha256", bench_hmac_sha256, bench_setup, NULL, &data, 10, 20000);
- if (have_flag(argc, argv, "hash") || have_flag(argc, argv, "rng6979")) run_benchmark("hash_rfc6979_hmac_sha256", bench_rfc6979_hmac_sha256, bench_setup, NULL, &data, 10, 20000);
+ if (have_flag(argc, argv, "hash") || have_flag(argc, argv, "sha256")) run_benchmark("hash_sha256", bench_sha256, bench_setup, NULL, &data, 10, iters);
+ if (have_flag(argc, argv, "hash") || have_flag(argc, argv, "hmac")) run_benchmark("hash_hmac_sha256", bench_hmac_sha256, bench_setup, NULL, &data, 10, iters);
+ if (have_flag(argc, argv, "hash") || have_flag(argc, argv, "rng6979")) run_benchmark("hash_rfc6979_hmac_sha256", bench_rfc6979_hmac_sha256, bench_setup, NULL, &data, 10, iters);
- 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);
+ if (have_flag(argc, argv, "context") || have_flag(argc, argv, "verify")) run_benchmark("context_verify", bench_context_verify, bench_setup, NULL, &data, 10, 1 + iters/1000);
+ if (have_flag(argc, argv, "context") || have_flag(argc, argv, "sign")) run_benchmark("context_sign", bench_context_sign, bench_setup, NULL, &data, 10, 1 + iters/100);
#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);
+ if (have_flag(argc, argv, "num") || have_flag(argc, argv, "jacobi")) run_benchmark("num_jacobi", bench_num_jacobi, bench_setup, NULL, &data, 10, iters*10);
#endif
return 0;
}
diff --git a/src/secp256k1/src/bench_recover.c b/src/secp256k1/src/bench_recover.c
index b806eed94e..e952ed1215 100644
--- a/src/secp256k1/src/bench_recover.c
+++ b/src/secp256k1/src/bench_recover.c
@@ -15,13 +15,13 @@ typedef struct {
unsigned char sig[64];
} bench_recover_data;
-void bench_recover(void* arg) {
+void bench_recover(void* arg, int iters) {
int i;
bench_recover_data *data = (bench_recover_data*)arg;
secp256k1_pubkey pubkey;
unsigned char pubkeyc[33];
- for (i = 0; i < 20000; i++) {
+ for (i = 0; i < iters; i++) {
int j;
size_t pubkeylen = 33;
secp256k1_ecdsa_recoverable_signature sig;
@@ -51,9 +51,11 @@ void bench_recover_setup(void* arg) {
int main(void) {
bench_recover_data data;
+ int iters = get_iters(20000);
+
data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY);
- run_benchmark("ecdsa_recover", bench_recover, bench_recover_setup, NULL, &data, 10, 20000);
+ run_benchmark("ecdsa_recover", bench_recover, bench_recover_setup, NULL, &data, 10, iters);
secp256k1_context_destroy(data.ctx);
return 0;
diff --git a/src/secp256k1/src/bench_sign.c b/src/secp256k1/src/bench_sign.c
index 544b43963c..c6b2942cc0 100644
--- a/src/secp256k1/src/bench_sign.c
+++ b/src/secp256k1/src/bench_sign.c
@@ -26,12 +26,12 @@ static void bench_sign_setup(void* arg) {
}
}
-static void bench_sign_run(void* arg) {
+static void bench_sign_run(void* arg, int iters) {
int i;
bench_sign *data = (bench_sign*)arg;
unsigned char sig[74];
- for (i = 0; i < 20000; i++) {
+ for (i = 0; i < iters; i++) {
size_t siglen = 74;
int j;
secp256k1_ecdsa_signature signature;
@@ -47,9 +47,11 @@ static void bench_sign_run(void* arg) {
int main(void) {
bench_sign data;
+ int iters = get_iters(20000);
+
data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
- run_benchmark("ecdsa_sign", bench_sign_run, bench_sign_setup, NULL, &data, 10, 20000);
+ run_benchmark("ecdsa_sign", bench_sign_run, bench_sign_setup, NULL, &data, 10, iters);
secp256k1_context_destroy(data.ctx);
return 0;
diff --git a/src/secp256k1/src/bench_verify.c b/src/secp256k1/src/bench_verify.c
index 418defa0aa..272d3e5cc4 100644
--- a/src/secp256k1/src/bench_verify.c
+++ b/src/secp256k1/src/bench_verify.c
@@ -17,6 +17,7 @@
#include <openssl/obj_mac.h>
#endif
+
typedef struct {
secp256k1_context *ctx;
unsigned char msg[32];
@@ -30,11 +31,11 @@ typedef struct {
#endif
} benchmark_verify_t;
-static void benchmark_verify(void* arg) {
+static void benchmark_verify(void* arg, int iters) {
int i;
benchmark_verify_t* data = (benchmark_verify_t*)arg;
- for (i = 0; i < 20000; i++) {
+ for (i = 0; i < iters; i++) {
secp256k1_pubkey pubkey;
secp256k1_ecdsa_signature sig;
data->sig[data->siglen - 1] ^= (i & 0xFF);
@@ -50,11 +51,11 @@ static void benchmark_verify(void* arg) {
}
#ifdef ENABLE_OPENSSL_TESTS
-static void benchmark_verify_openssl(void* arg) {
+static void benchmark_verify_openssl(void* arg, int iters) {
int i;
benchmark_verify_t* data = (benchmark_verify_t*)arg;
- for (i = 0; i < 20000; i++) {
+ for (i = 0; i < iters; i++) {
data->sig[data->siglen - 1] ^= (i & 0xFF);
data->sig[data->siglen - 2] ^= ((i >> 8) & 0xFF);
data->sig[data->siglen - 3] ^= ((i >> 16) & 0xFF);
@@ -85,6 +86,8 @@ int main(void) {
secp256k1_ecdsa_signature sig;
benchmark_verify_t data;
+ int iters = get_iters(20000);
+
data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
for (i = 0; i < 32; i++) {
@@ -100,10 +103,10 @@ int main(void) {
data.pubkeylen = 33;
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);
+ run_benchmark("ecdsa_verify", benchmark_verify, NULL, NULL, &data, 10, iters);
#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);
+ run_benchmark("ecdsa_verify_openssl", benchmark_verify_openssl, NULL, NULL, &data, 10, iters);
EC_GROUP_free(data.ec_group);
#endif
diff --git a/src/secp256k1/src/ecdsa_impl.h b/src/secp256k1/src/ecdsa_impl.h
index c3400042d8..5f54b59faa 100644
--- a/src/secp256k1/src/ecdsa_impl.h
+++ b/src/secp256k1/src/ecdsa_impl.h
@@ -46,68 +46,73 @@ static const secp256k1_fe secp256k1_ecdsa_const_p_minus_order = SECP256K1_FE_CON
0, 0, 0, 1, 0x45512319UL, 0x50B75FC4UL, 0x402DA172UL, 0x2FC9BAEEUL
);
-static int secp256k1_der_read_len(const unsigned char **sigp, const unsigned char *sigend) {
- int lenleft, b1;
- size_t ret = 0;
+static int secp256k1_der_read_len(size_t *len, const unsigned char **sigp, const unsigned char *sigend) {
+ size_t lenleft;
+ unsigned char b1;
+ VERIFY_CHECK(len != NULL);
+ *len = 0;
if (*sigp >= sigend) {
- return -1;
+ return 0;
}
b1 = *((*sigp)++);
if (b1 == 0xFF) {
/* X.690-0207 8.1.3.5.c the value 0xFF shall not be used. */
- return -1;
+ return 0;
}
if ((b1 & 0x80) == 0) {
/* X.690-0207 8.1.3.4 short form length octets */
- return b1;
+ *len = b1;
+ return 1;
}
if (b1 == 0x80) {
/* Indefinite length is not allowed in DER. */
- return -1;
+ return 0;
}
/* X.690-207 8.1.3.5 long form length octets */
- lenleft = b1 & 0x7F;
- if (lenleft > sigend - *sigp) {
- return -1;
+ lenleft = b1 & 0x7F; /* lenleft is at least 1 */
+ if (lenleft > (size_t)(sigend - *sigp)) {
+ return 0;
}
if (**sigp == 0) {
/* Not the shortest possible length encoding. */
- return -1;
+ return 0;
}
- if ((size_t)lenleft > sizeof(size_t)) {
+ if (lenleft > sizeof(size_t)) {
/* The resulting length would exceed the range of a size_t, so
* certainly longer than the passed array size.
*/
- return -1;
+ return 0;
}
while (lenleft > 0) {
- ret = (ret << 8) | **sigp;
- if (ret + lenleft > (size_t)(sigend - *sigp)) {
- /* Result exceeds the length of the passed array. */
- return -1;
- }
+ *len = (*len << 8) | **sigp;
(*sigp)++;
lenleft--;
}
- if (ret < 128) {
+ if (*len > (size_t)(sigend - *sigp)) {
+ /* Result exceeds the length of the passed array. */
+ return 0;
+ }
+ if (*len < 128) {
/* Not the shortest possible length encoding. */
- return -1;
+ return 0;
}
- return ret;
+ return 1;
}
static int secp256k1_der_parse_integer(secp256k1_scalar *r, const unsigned char **sig, const unsigned char *sigend) {
int overflow = 0;
unsigned char ra[32] = {0};
- int rlen;
+ size_t rlen;
if (*sig == sigend || **sig != 0x02) {
/* Not a primitive integer (X.690-0207 8.3.1). */
return 0;
}
(*sig)++;
- rlen = secp256k1_der_read_len(sig, sigend);
- if (rlen <= 0 || (*sig) + rlen > sigend) {
+ if (secp256k1_der_read_len(&rlen, sig, sigend) == 0) {
+ return 0;
+ }
+ if (rlen == 0 || *sig + rlen > sigend) {
/* Exceeds bounds or not at least length 1 (X.690-0207 8.3.1). */
return 0;
}
@@ -123,8 +128,11 @@ static int secp256k1_der_parse_integer(secp256k1_scalar *r, const unsigned char
/* Negative. */
overflow = 1;
}
- while (rlen > 0 && **sig == 0) {
- /* Skip leading zero bytes */
+ /* There is at most one leading zero byte:
+ * if there were two leading zero bytes, we would have failed and returned 0
+ * because of excessive 0x00 padding already. */
+ if (rlen > 0 && **sig == 0) {
+ /* Skip leading zero byte */
rlen--;
(*sig)++;
}
@@ -144,18 +152,16 @@ static int secp256k1_der_parse_integer(secp256k1_scalar *r, const unsigned char
static int secp256k1_ecdsa_sig_parse(secp256k1_scalar *rr, secp256k1_scalar *rs, const unsigned char *sig, size_t size) {
const unsigned char *sigend = sig + size;
- int rlen;
+ size_t rlen;
if (sig == sigend || *(sig++) != 0x30) {
/* The encoding doesn't start with a constructed sequence (X.690-0207 8.9.1). */
return 0;
}
- rlen = secp256k1_der_read_len(&sig, sigend);
- if (rlen < 0 || sig + rlen > sigend) {
- /* Tuple exceeds bounds */
+ if (secp256k1_der_read_len(&rlen, &sig, sigend) == 0) {
return 0;
}
- if (sig + rlen != sigend) {
- /* Garbage after tuple. */
+ if (rlen != (size_t)(sigend - sig)) {
+ /* Tuple exceeds bounds or garage after tuple. */
return 0;
}
@@ -274,6 +280,7 @@ static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, sec
secp256k1_ge r;
secp256k1_scalar n;
int overflow = 0;
+ int high;
secp256k1_ecmult_gen(ctx, &rp, nonce);
secp256k1_ge_set_gej(&r, &rp);
@@ -281,15 +288,11 @@ static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, sec
secp256k1_fe_normalize(&r.y);
secp256k1_fe_get_b32(b, &r.x);
secp256k1_scalar_set_b32(sigr, b, &overflow);
- /* These two conditions should be checked before calling */
- VERIFY_CHECK(!secp256k1_scalar_is_zero(sigr));
- VERIFY_CHECK(overflow == 0);
-
if (recid) {
/* The overflow condition is cryptographically unreachable as hitting it requires finding the discrete log
* of some P where P.x >= order, and only 1 in about 2^127 points meet this criteria.
*/
- *recid = (overflow ? 2 : 0) | (secp256k1_fe_is_odd(&r.y) ? 1 : 0);
+ *recid = (overflow << 1) | secp256k1_fe_is_odd(&r.y);
}
secp256k1_scalar_mul(&n, sigr, seckey);
secp256k1_scalar_add(&n, &n, message);
@@ -298,16 +301,15 @@ static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, sec
secp256k1_scalar_clear(&n);
secp256k1_gej_clear(&rp);
secp256k1_ge_clear(&r);
- if (secp256k1_scalar_is_zero(sigs)) {
- return 0;
- }
- if (secp256k1_scalar_is_high(sigs)) {
- secp256k1_scalar_negate(sigs, sigs);
- if (recid) {
- *recid ^= 1;
- }
+ high = secp256k1_scalar_is_high(sigs);
+ secp256k1_scalar_cond_negate(sigs, high);
+ if (recid) {
+ *recid ^= high;
}
- return 1;
+ /* P.x = order is on the curve, so technically sig->r could end up being zero, which would be an invalid signature.
+ * This is cryptographically unreachable as hitting it requires finding the discrete log of P.x = N.
+ */
+ return !secp256k1_scalar_is_zero(sigr) & !secp256k1_scalar_is_zero(sigs);
}
#endif /* SECP256K1_ECDSA_IMPL_H */
diff --git a/src/secp256k1/src/eckey_impl.h b/src/secp256k1/src/eckey_impl.h
index 7c5b789325..e2e72d9303 100644
--- a/src/secp256k1/src/eckey_impl.h
+++ b/src/secp256k1/src/eckey_impl.h
@@ -54,10 +54,7 @@ static int secp256k1_eckey_pubkey_serialize(secp256k1_ge *elem, unsigned char *p
static int secp256k1_eckey_privkey_tweak_add(secp256k1_scalar *key, const secp256k1_scalar *tweak) {
secp256k1_scalar_add(key, key, tweak);
- if (secp256k1_scalar_is_zero(key)) {
- return 0;
- }
- return 1;
+ return !secp256k1_scalar_is_zero(key);
}
static int secp256k1_eckey_pubkey_tweak_add(const secp256k1_ecmult_context *ctx, secp256k1_ge *key, const secp256k1_scalar *tweak) {
@@ -75,12 +72,11 @@ static int secp256k1_eckey_pubkey_tweak_add(const secp256k1_ecmult_context *ctx,
}
static int secp256k1_eckey_privkey_tweak_mul(secp256k1_scalar *key, const secp256k1_scalar *tweak) {
- if (secp256k1_scalar_is_zero(tweak)) {
- return 0;
- }
+ int ret;
+ ret = !secp256k1_scalar_is_zero(tweak);
secp256k1_scalar_mul(key, key, tweak);
- return 1;
+ return ret;
}
static int secp256k1_eckey_pubkey_tweak_mul(const secp256k1_ecmult_context *ctx, secp256k1_ge *key, const secp256k1_scalar *tweak) {
diff --git a/src/secp256k1/src/ecmult.h b/src/secp256k1/src/ecmult.h
index 3d75a960f4..c9b198239d 100644
--- a/src/secp256k1/src/ecmult.h
+++ b/src/secp256k1/src/ecmult.h
@@ -20,10 +20,10 @@ typedef struct {
#endif
} secp256k1_ecmult_context;
+static const size_t SECP256K1_ECMULT_CONTEXT_PREALLOCATED_SIZE;
static void secp256k1_ecmult_context_init(secp256k1_ecmult_context *ctx);
-static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, const secp256k1_callback *cb);
-static void secp256k1_ecmult_context_clone(secp256k1_ecmult_context *dst,
- const secp256k1_ecmult_context *src, const secp256k1_callback *cb);
+static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, void **prealloc);
+static void secp256k1_ecmult_context_finalize_memcpy(secp256k1_ecmult_context *dst, const secp256k1_ecmult_context *src);
static void secp256k1_ecmult_context_clear(secp256k1_ecmult_context *ctx);
static int secp256k1_ecmult_context_is_built(const secp256k1_ecmult_context *ctx);
@@ -43,6 +43,6 @@ typedef int (secp256k1_ecmult_multi_callback)(secp256k1_scalar *sc, secp256k1_ge
* 0 if there is not enough scratch space for a single point or
* callback returns 0
*/
-static int secp256k1_ecmult_multi_var(const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n);
+static int secp256k1_ecmult_multi_var(const secp256k1_callback* error_callback, const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n);
#endif /* SECP256K1_ECMULT_H */
diff --git a/src/secp256k1/src/ecmult_const.h b/src/secp256k1/src/ecmult_const.h
index d4804b8b68..03bb33257d 100644
--- a/src/secp256k1/src/ecmult_const.h
+++ b/src/secp256k1/src/ecmult_const.h
@@ -10,8 +10,11 @@
#include "scalar.h"
#include "group.h"
-/* Here `bits` should be set to the maximum bitlength of the _absolute value_ of `q`, plus
- * one because we internally sometimes add 2 to the number during the WNAF conversion. */
+/**
+ * Multiply: R = q*A (in constant-time)
+ * Here `bits` should be set to the maximum bitlength of the _absolute value_ of `q`, plus
+ * one because we internally sometimes add 2 to the number during the WNAF conversion.
+ */
static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *q, int bits);
#endif /* SECP256K1_ECMULT_CONST_H */
diff --git a/src/secp256k1/src/ecmult_const_impl.h b/src/secp256k1/src/ecmult_const_impl.h
index 8411752eb0..6d6d354aa4 100644
--- a/src/secp256k1/src/ecmult_const_impl.h
+++ b/src/secp256k1/src/ecmult_const_impl.h
@@ -14,16 +14,22 @@
/* This is like `ECMULT_TABLE_GET_GE` but is constant time */
#define ECMULT_CONST_TABLE_GET_GE(r,pre,n,w) do { \
- int m; \
- int abs_n = (n) * (((n) > 0) * 2 - 1); \
- int idx_n = abs_n / 2; \
+ int m = 0; \
+ /* Extract the sign-bit for a constant time absolute-value. */ \
+ int mask = (n) >> (sizeof(n) * CHAR_BIT - 1); \
+ int abs_n = ((n) + mask) ^ mask; \
+ int idx_n = abs_n >> 1; \
secp256k1_fe neg_y; \
VERIFY_CHECK(((n) & 1) == 1); \
VERIFY_CHECK((n) >= -((1 << ((w)-1)) - 1)); \
VERIFY_CHECK((n) <= ((1 << ((w)-1)) - 1)); \
VERIFY_SETUP(secp256k1_fe_clear(&(r)->x)); \
VERIFY_SETUP(secp256k1_fe_clear(&(r)->y)); \
- for (m = 0; m < ECMULT_TABLE_SIZE(w); m++) { \
+ /* Unconditionally set r->x = (pre)[m].x. r->y = (pre)[m].y. because it's either the correct one \
+ * or will get replaced in the later iterations, this is needed to make sure `r` is initialized. */ \
+ (r)->x = (pre)[m].x; \
+ (r)->y = (pre)[m].y; \
+ for (m = 1; m < ECMULT_TABLE_SIZE(w); m++) { \
/* This loop is used to avoid secret data in array indices. See
* the comment in ecmult_gen_impl.h for rationale. */ \
secp256k1_fe_cmov(&(r)->x, &(pre)[m].x, m == idx_n); \
@@ -44,11 +50,11 @@
*
* Adapted from `The Width-w NAF Method Provides Small Memory and Fast Elliptic Scalar
* Multiplications Secure against Side Channel Attacks`, Okeya and Tagaki. M. Joye (Ed.)
- * CT-RSA 2003, LNCS 2612, pp. 328-443, 2003. Springer-Verlagy Berlin Heidelberg 2003
+ * CT-RSA 2003, LNCS 2612, pp. 328-443, 2003. Springer-Verlag Berlin Heidelberg 2003
*
* Numbers reference steps of `Algorithm SPA-resistant Width-w NAF with Odd Scalar` on pp. 335
*/
-static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w, int size) {
+static int secp256k1_wnaf_const(int *wnaf, const secp256k1_scalar *scalar, int w, int size) {
int global_sign;
int skew = 0;
int word = 0;
@@ -59,8 +65,12 @@ static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w, int size)
int flip;
int bit;
- secp256k1_scalar neg_s;
+ secp256k1_scalar s;
int not_neg_one;
+
+ VERIFY_CHECK(w > 0);
+ VERIFY_CHECK(size > 0);
+
/* 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
@@ -75,12 +85,13 @@ static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w, int size)
* {1, 2} we want to add to the scalar when ensuring that it's odd. Further
* complicating things, -1 interacts badly with `secp256k1_scalar_cadd_bit` and
* we need to special-case it in this logic. */
- flip = secp256k1_scalar_is_high(&s);
+ flip = secp256k1_scalar_is_high(scalar);
/* We add 1 to even numbers, 2 to odd ones, noting that negation flips parity */
- bit = flip ^ !secp256k1_scalar_is_even(&s);
+ bit = flip ^ !secp256k1_scalar_is_even(scalar);
/* We check for negative one, since adding 2 to it will cause an overflow */
- secp256k1_scalar_negate(&neg_s, &s);
- not_neg_one = !secp256k1_scalar_is_one(&neg_s);
+ secp256k1_scalar_negate(&s, scalar);
+ not_neg_one = !secp256k1_scalar_is_one(&s);
+ s = *scalar;
secp256k1_scalar_cadd_bit(&s, bit, not_neg_one);
/* If we had negative one, flip == 1, s.d[0] == 0, bit == 1, so caller expects
* that we added two to it and flipped it. In fact for -1 these operations are
@@ -93,7 +104,7 @@ static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w, int size)
/* 4 */
u_last = secp256k1_scalar_shr_int(&s, w);
- while (word * w < size) {
+ do {
int sign;
int even;
@@ -109,7 +120,7 @@ static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w, int size)
wnaf[word++] = u_last * global_sign;
u_last = u;
- }
+ } while (word * w < size);
wnaf[word] = u * global_sign;
VERIFY_CHECK(secp256k1_scalar_is_zero(&s));
@@ -132,7 +143,6 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
int wnaf_1[1 + WNAF_SIZE(WINDOW_A - 1)];
int i;
- secp256k1_scalar sc = *scalar;
/* build wnaf representation for q. */
int rsize = size;
@@ -140,13 +150,13 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
if (size > 128) {
rsize = 128;
/* 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);
- skew_1 = secp256k1_wnaf_const(wnaf_1, q_1, WINDOW_A - 1, 128);
- skew_lam = secp256k1_wnaf_const(wnaf_lam, q_lam, WINDOW_A - 1, 128);
+ secp256k1_scalar_split_lambda(&q_1, &q_lam, scalar);
+ skew_1 = secp256k1_wnaf_const(wnaf_1, &q_1, WINDOW_A - 1, 128);
+ skew_lam = secp256k1_wnaf_const(wnaf_lam, &q_lam, WINDOW_A - 1, 128);
} else
#endif
{
- skew_1 = secp256k1_wnaf_const(wnaf_1, sc, WINDOW_A - 1, size);
+ skew_1 = secp256k1_wnaf_const(wnaf_1, scalar, WINDOW_A - 1, size);
#ifdef USE_ENDOMORPHISM
skew_lam = 0;
#endif
@@ -168,6 +178,7 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) {
secp256k1_ge_mul_lambda(&pre_a_lam[i], &pre_a[i]);
}
+
}
#endif
@@ -191,7 +202,7 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
int n;
int j;
for (j = 0; j < WINDOW_A - 1; ++j) {
- secp256k1_gej_double_nonzero(r, r, NULL);
+ secp256k1_gej_double_nonzero(r, r);
}
n = wnaf_1[i];
diff --git a/src/secp256k1/src/ecmult_gen.h b/src/secp256k1/src/ecmult_gen.h
index 7564b7015f..30815e5aa1 100644
--- a/src/secp256k1/src/ecmult_gen.h
+++ b/src/secp256k1/src/ecmult_gen.h
@@ -10,28 +10,35 @@
#include "scalar.h"
#include "group.h"
+#if ECMULT_GEN_PREC_BITS != 2 && ECMULT_GEN_PREC_BITS != 4 && ECMULT_GEN_PREC_BITS != 8
+# error "Set ECMULT_GEN_PREC_BITS to 2, 4 or 8."
+#endif
+#define ECMULT_GEN_PREC_B ECMULT_GEN_PREC_BITS
+#define ECMULT_GEN_PREC_G (1 << ECMULT_GEN_PREC_B)
+#define ECMULT_GEN_PREC_N (256 / ECMULT_GEN_PREC_B)
+
typedef struct {
/* For accelerating the computation of a*G:
* To harden against timing attacks, use the following mechanism:
- * * Break up the multiplicand into groups of 4 bits, called n_0, n_1, n_2, ..., n_63.
- * * Compute sum(n_i * 16^i * G + U_i, i=0..63), where:
- * * U_i = U * 2^i (for i=0..62)
- * * U_i = U * (1-2^63) (for i=63)
- * where U is a point with no known corresponding scalar. Note that sum(U_i, i=0..63) = 0.
- * For each i, and each of the 16 possible values of n_i, (n_i * 16^i * G + U_i) is
- * precomputed (call it prec(i, n_i)). The formula now becomes sum(prec(i, n_i), i=0..63).
+ * * Break up the multiplicand into groups of PREC_B bits, called n_0, n_1, n_2, ..., n_(PREC_N-1).
+ * * Compute sum(n_i * (PREC_G)^i * G + U_i, i=0 ... PREC_N-1), where:
+ * * U_i = U * 2^i, for i=0 ... PREC_N-2
+ * * U_i = U * (1-2^(PREC_N-1)), for i=PREC_N-1
+ * where U is a point with no known corresponding scalar. Note that sum(U_i, i=0 ... PREC_N-1) = 0.
+ * For each i, and each of the PREC_G possible values of n_i, (n_i * (PREC_G)^i * G + U_i) is
+ * precomputed (call it prec(i, n_i)). The formula now becomes sum(prec(i, n_i), i=0 ... PREC_N-1).
* None of the resulting prec group elements have a known scalar, and neither do any of
* the intermediate sums while computing a*G.
*/
- secp256k1_ge_storage (*prec)[64][16]; /* prec[j][i] = 16^j * i * G + U_i */
+ secp256k1_ge_storage (*prec)[ECMULT_GEN_PREC_N][ECMULT_GEN_PREC_G]; /* prec[j][i] = (PREC_G)^j * i * G + U_i */
secp256k1_scalar blind;
secp256k1_gej initial;
} secp256k1_ecmult_gen_context;
+static const size_t SECP256K1_ECMULT_GEN_CONTEXT_PREALLOCATED_SIZE;
static void secp256k1_ecmult_gen_context_init(secp256k1_ecmult_gen_context* ctx);
-static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context* ctx, const secp256k1_callback* cb);
-static void secp256k1_ecmult_gen_context_clone(secp256k1_ecmult_gen_context *dst,
- const secp256k1_ecmult_gen_context* src, const secp256k1_callback* cb);
+static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context* ctx, void **prealloc);
+static void secp256k1_ecmult_gen_context_finalize_memcpy(secp256k1_ecmult_gen_context *dst, const secp256k1_ecmult_gen_context* src);
static void secp256k1_ecmult_gen_context_clear(secp256k1_ecmult_gen_context* ctx);
static int secp256k1_ecmult_gen_context_is_built(const secp256k1_ecmult_gen_context* ctx);
diff --git a/src/secp256k1/src/ecmult_gen_impl.h b/src/secp256k1/src/ecmult_gen_impl.h
index d64505dc00..30ac16518b 100644
--- a/src/secp256k1/src/ecmult_gen_impl.h
+++ b/src/secp256k1/src/ecmult_gen_impl.h
@@ -7,6 +7,7 @@
#ifndef SECP256K1_ECMULT_GEN_IMPL_H
#define SECP256K1_ECMULT_GEN_IMPL_H
+#include "util.h"
#include "scalar.h"
#include "group.h"
#include "ecmult_gen.h"
@@ -14,23 +15,32 @@
#ifdef USE_ECMULT_STATIC_PRECOMPUTATION
#include "ecmult_static_context.h"
#endif
+
+#ifndef USE_ECMULT_STATIC_PRECOMPUTATION
+ static const size_t SECP256K1_ECMULT_GEN_CONTEXT_PREALLOCATED_SIZE = ROUND_TO_ALIGN(sizeof(*((secp256k1_ecmult_gen_context*) NULL)->prec));
+#else
+ static const size_t SECP256K1_ECMULT_GEN_CONTEXT_PREALLOCATED_SIZE = 0;
+#endif
+
static void secp256k1_ecmult_gen_context_init(secp256k1_ecmult_gen_context *ctx) {
ctx->prec = NULL;
}
-static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context *ctx, const secp256k1_callback* cb) {
+static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context *ctx, void **prealloc) {
#ifndef USE_ECMULT_STATIC_PRECOMPUTATION
- secp256k1_ge prec[1024];
+ secp256k1_ge prec[ECMULT_GEN_PREC_N * ECMULT_GEN_PREC_G];
secp256k1_gej gj;
secp256k1_gej nums_gej;
int i, j;
+ size_t const prealloc_size = SECP256K1_ECMULT_GEN_CONTEXT_PREALLOCATED_SIZE;
+ void* const base = *prealloc;
#endif
if (ctx->prec != NULL) {
return;
}
#ifndef USE_ECMULT_STATIC_PRECOMPUTATION
- ctx->prec = (secp256k1_ge_storage (*)[64][16])checked_malloc(cb, sizeof(*ctx->prec));
+ ctx->prec = (secp256k1_ge_storage (*)[ECMULT_GEN_PREC_N][ECMULT_GEN_PREC_G])manual_alloc(prealloc, prealloc_size, base, prealloc_size);
/* get the generator */
secp256k1_gej_set_ge(&gj, &secp256k1_ge_const_g);
@@ -54,39 +64,39 @@ static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context *ctx
/* compute prec. */
{
- secp256k1_gej precj[1024]; /* Jacobian versions of prec. */
+ secp256k1_gej precj[ECMULT_GEN_PREC_N * ECMULT_GEN_PREC_G]; /* Jacobian versions of prec. */
secp256k1_gej gbase;
secp256k1_gej numsbase;
- gbase = gj; /* 16^j * G */
+ gbase = gj; /* PREC_G^j * G */
numsbase = nums_gej; /* 2^j * nums. */
- for (j = 0; j < 64; j++) {
- /* Set precj[j*16 .. j*16+15] to (numsbase, numsbase + gbase, ..., numsbase + 15*gbase). */
- precj[j*16] = numsbase;
- for (i = 1; i < 16; i++) {
- secp256k1_gej_add_var(&precj[j*16 + i], &precj[j*16 + i - 1], &gbase, NULL);
+ for (j = 0; j < ECMULT_GEN_PREC_N; j++) {
+ /* Set precj[j*PREC_G .. j*PREC_G+(PREC_G-1)] to (numsbase, numsbase + gbase, ..., numsbase + (PREC_G-1)*gbase). */
+ precj[j*ECMULT_GEN_PREC_G] = numsbase;
+ for (i = 1; i < ECMULT_GEN_PREC_G; i++) {
+ secp256k1_gej_add_var(&precj[j*ECMULT_GEN_PREC_G + i], &precj[j*ECMULT_GEN_PREC_G + i - 1], &gbase, NULL);
}
- /* Multiply gbase by 16. */
- for (i = 0; i < 4; i++) {
+ /* Multiply gbase by PREC_G. */
+ for (i = 0; i < ECMULT_GEN_PREC_B; i++) {
secp256k1_gej_double_var(&gbase, &gbase, NULL);
}
/* Multiply numbase by 2. */
secp256k1_gej_double_var(&numsbase, &numsbase, NULL);
- if (j == 62) {
+ if (j == ECMULT_GEN_PREC_N - 2) {
/* In the last iteration, numsbase is (1 - 2^j) * nums instead. */
secp256k1_gej_neg(&numsbase, &numsbase);
secp256k1_gej_add_var(&numsbase, &numsbase, &nums_gej, NULL);
}
}
- secp256k1_ge_set_all_gej_var(prec, precj, 1024);
+ secp256k1_ge_set_all_gej_var(prec, precj, ECMULT_GEN_PREC_N * ECMULT_GEN_PREC_G);
}
- for (j = 0; j < 64; j++) {
- for (i = 0; i < 16; i++) {
- secp256k1_ge_to_storage(&(*ctx->prec)[j][i], &prec[j*16 + i]);
+ for (j = 0; j < ECMULT_GEN_PREC_N; j++) {
+ for (i = 0; i < ECMULT_GEN_PREC_G; i++) {
+ secp256k1_ge_to_storage(&(*ctx->prec)[j][i], &prec[j*ECMULT_GEN_PREC_G + i]);
}
}
#else
- (void)cb;
- ctx->prec = (secp256k1_ge_storage (*)[64][16])secp256k1_ecmult_static_context;
+ (void)prealloc;
+ ctx->prec = (secp256k1_ge_storage (*)[ECMULT_GEN_PREC_N][ECMULT_GEN_PREC_G])secp256k1_ecmult_static_context;
#endif
secp256k1_ecmult_gen_blind(ctx, NULL);
}
@@ -95,27 +105,18 @@ static int secp256k1_ecmult_gen_context_is_built(const secp256k1_ecmult_gen_cont
return ctx->prec != NULL;
}
-static void secp256k1_ecmult_gen_context_clone(secp256k1_ecmult_gen_context *dst,
- const secp256k1_ecmult_gen_context *src, const secp256k1_callback* cb) {
- if (src->prec == NULL) {
- dst->prec = NULL;
- } else {
+static void secp256k1_ecmult_gen_context_finalize_memcpy(secp256k1_ecmult_gen_context *dst, const secp256k1_ecmult_gen_context *src) {
#ifndef USE_ECMULT_STATIC_PRECOMPUTATION
- dst->prec = (secp256k1_ge_storage (*)[64][16])checked_malloc(cb, sizeof(*dst->prec));
- memcpy(dst->prec, src->prec, sizeof(*dst->prec));
+ if (src->prec != NULL) {
+ /* We cast to void* first to suppress a -Wcast-align warning. */
+ dst->prec = (secp256k1_ge_storage (*)[ECMULT_GEN_PREC_N][ECMULT_GEN_PREC_G])(void*)((unsigned char*)dst + ((unsigned char*)src->prec - (unsigned char*)src));
+ }
#else
- (void)cb;
- dst->prec = src->prec;
+ (void)dst, (void)src;
#endif
- dst->initial = src->initial;
- dst->blind = src->blind;
- }
}
static void secp256k1_ecmult_gen_context_clear(secp256k1_ecmult_gen_context *ctx) {
-#ifndef USE_ECMULT_STATIC_PRECOMPUTATION
- free(ctx->prec);
-#endif
secp256k1_scalar_clear(&ctx->blind);
secp256k1_gej_clear(&ctx->initial);
ctx->prec = NULL;
@@ -132,9 +133,9 @@ static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context *ctx, secp25
/* Blind scalar/point multiplication by computing (n-b)G + bG instead of nG. */
secp256k1_scalar_add(&gnb, gn, &ctx->blind);
add.infinity = 0;
- for (j = 0; j < 64; j++) {
- bits = secp256k1_scalar_get_bits(&gnb, j * 4, 4);
- for (i = 0; i < 16; i++) {
+ for (j = 0; j < ECMULT_GEN_PREC_N; j++) {
+ bits = secp256k1_scalar_get_bits(&gnb, j * ECMULT_GEN_PREC_B, ECMULT_GEN_PREC_B);
+ for (i = 0; i < ECMULT_GEN_PREC_G; i++) {
/** This uses a conditional move to avoid any secret data in array indexes.
* _Any_ use of secret indexes has been demonstrated to result in timing
* sidechannels, even when the cache-line access patterns are uniform.
@@ -162,7 +163,7 @@ static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const
secp256k1_fe s;
unsigned char nonce32[32];
secp256k1_rfc6979_hmac_sha256 rng;
- int retry;
+ int overflow;
unsigned char keydata[64] = {0};
if (seed32 == NULL) {
/* When seed is NULL, reset the initial point and blinding value. */
@@ -182,21 +183,18 @@ static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const
}
secp256k1_rfc6979_hmac_sha256_initialize(&rng, keydata, seed32 ? 64 : 32);
memset(keydata, 0, sizeof(keydata));
- /* Retry for out of range results to achieve uniformity. */
- do {
- secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32);
- retry = !secp256k1_fe_set_b32(&s, nonce32);
- retry |= secp256k1_fe_is_zero(&s);
- } while (retry); /* This branch true is cryptographically unreachable. Requires sha256_hmac output > Fp. */
+ /* Accept unobservably small non-uniformity. */
+ secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32);
+ overflow = !secp256k1_fe_set_b32(&s, nonce32);
+ overflow |= secp256k1_fe_is_zero(&s);
+ secp256k1_fe_cmov(&s, &secp256k1_fe_one, overflow);
/* Randomize the projection to defend against multiplier sidechannels. */
secp256k1_gej_rescale(&ctx->initial, &s);
secp256k1_fe_clear(&s);
- do {
- secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32);
- secp256k1_scalar_set_b32(&b, nonce32, &retry);
- /* A blinding value of 0 works, but would undermine the projection hardening. */
- retry |= secp256k1_scalar_is_zero(&b);
- } while (retry); /* This branch true is cryptographically unreachable. Requires sha256_hmac output > order. */
+ secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32);
+ secp256k1_scalar_set_b32(&b, nonce32, NULL);
+ /* A blinding value of 0 works, but would undermine the projection hardening. */
+ secp256k1_scalar_cmov(&b, &secp256k1_scalar_one, secp256k1_scalar_is_zero(&b));
secp256k1_rfc6979_hmac_sha256_finalize(&rng);
memset(nonce32, 0, 32);
secp256k1_ecmult_gen(ctx, &gb, &b);
diff --git a/src/secp256k1/src/ecmult_impl.h b/src/secp256k1/src/ecmult_impl.h
index 1986914a4f..f03fa9469d 100644
--- a/src/secp256k1/src/ecmult_impl.h
+++ b/src/secp256k1/src/ecmult_impl.h
@@ -10,6 +10,7 @@
#include <string.h>
#include <stdint.h>
+#include "util.h"
#include "group.h"
#include "scalar.h"
#include "ecmult.h"
@@ -30,16 +31,32 @@
# endif
#else
/* optimal for 128-bit and 256-bit exponents. */
-#define WINDOW_A 5
-/** larger numbers may result in slightly better performance, at the cost of
- exponentially larger precomputed tables. */
-#ifdef USE_ENDOMORPHISM
-/** Two tables for window size 15: 1.375 MiB. */
-#define WINDOW_G 15
-#else
-/** One table for window size 16: 1.375 MiB. */
-#define WINDOW_G 16
+# define WINDOW_A 5
+/** Larger values for ECMULT_WINDOW_SIZE result in possibly better
+ * performance at the cost of an exponentially larger precomputed
+ * table. The exact table size is
+ * (1 << (WINDOW_G - 2)) * sizeof(secp256k1_ge_storage) bytes,
+ * where sizeof(secp256k1_ge_storage) is typically 64 bytes but can
+ * be larger due to platform-specific padding and alignment.
+ * If the endomorphism optimization is enabled (USE_ENDOMORMPHSIM)
+ * two tables of this size are used instead of only one.
+ */
+# define WINDOW_G ECMULT_WINDOW_SIZE
#endif
+
+/* Noone will ever need more than a window size of 24. The code might
+ * be correct for larger values of ECMULT_WINDOW_SIZE but this is not
+ * not tested.
+ *
+ * The following limitations are known, and there are probably more:
+ * If WINDOW_G > 27 and size_t has 32 bits, then the code is incorrect
+ * because the size of the memory object that we allocate (in bytes)
+ * will not fit in a size_t.
+ * If WINDOW_G > 31 and int has 32 bits, then the code is incorrect
+ * because certain expressions will overflow.
+ */
+#if ECMULT_WINDOW_SIZE < 2 || ECMULT_WINDOW_SIZE > 24
+# error Set ECMULT_WINDOW_SIZE to an integer in range [2..24].
#endif
#ifdef USE_ENDOMORPHISM
@@ -121,7 +138,7 @@ static void secp256k1_ecmult_odd_multiples_table(int n, secp256k1_gej *prej, sec
* It only operates on tables sized for WINDOW_A wnaf multiples.
* - secp256k1_ecmult_odd_multiples_table_storage_var, which converts its
* resulting point set to actually affine points, and stores those in pre.
- * It operates on tables of any size, but uses heap-allocated temporaries.
+ * It operates on tables of any size.
*
* To compute a*P + b*G, we compute a table for P using the first function,
* and for G using the second (which requires an inverse, but it only needs to
@@ -294,6 +311,13 @@ static void secp256k1_ecmult_odd_multiples_table_storage_var(const int n, secp25
} \
} while(0)
+static const size_t SECP256K1_ECMULT_CONTEXT_PREALLOCATED_SIZE =
+ ROUND_TO_ALIGN(sizeof((*((secp256k1_ecmult_context*) NULL)->pre_g)[0]) * ECMULT_TABLE_SIZE(WINDOW_G))
+#ifdef USE_ENDOMORPHISM
+ + ROUND_TO_ALIGN(sizeof((*((secp256k1_ecmult_context*) NULL)->pre_g_128)[0]) * ECMULT_TABLE_SIZE(WINDOW_G))
+#endif
+ ;
+
static void secp256k1_ecmult_context_init(secp256k1_ecmult_context *ctx) {
ctx->pre_g = NULL;
#ifdef USE_ENDOMORPHISM
@@ -301,8 +325,10 @@ static void secp256k1_ecmult_context_init(secp256k1_ecmult_context *ctx) {
#endif
}
-static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, const secp256k1_callback *cb) {
+static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, void **prealloc) {
secp256k1_gej gj;
+ void* const base = *prealloc;
+ size_t const prealloc_size = SECP256K1_ECMULT_CONTEXT_PREALLOCATED_SIZE;
if (ctx->pre_g != NULL) {
return;
@@ -311,7 +337,12 @@ static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, const
/* get the generator */
secp256k1_gej_set_ge(&gj, &secp256k1_ge_const_g);
- ctx->pre_g = (secp256k1_ge_storage (*)[])checked_malloc(cb, sizeof((*ctx->pre_g)[0]) * ECMULT_TABLE_SIZE(WINDOW_G));
+ {
+ size_t size = sizeof((*ctx->pre_g)[0]) * ((size_t)ECMULT_TABLE_SIZE(WINDOW_G));
+ /* check for overflow */
+ VERIFY_CHECK(size / sizeof((*ctx->pre_g)[0]) == ((size_t)ECMULT_TABLE_SIZE(WINDOW_G)));
+ ctx->pre_g = (secp256k1_ge_storage (*)[])manual_alloc(prealloc, sizeof((*ctx->pre_g)[0]) * ECMULT_TABLE_SIZE(WINDOW_G), base, prealloc_size);
+ }
/* precompute the tables with odd multiples */
secp256k1_ecmult_odd_multiples_table_storage_var(ECMULT_TABLE_SIZE(WINDOW_G), *ctx->pre_g, &gj);
@@ -321,7 +352,10 @@ static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, const
secp256k1_gej g_128j;
int i;
- ctx->pre_g_128 = (secp256k1_ge_storage (*)[])checked_malloc(cb, sizeof((*ctx->pre_g_128)[0]) * ECMULT_TABLE_SIZE(WINDOW_G));
+ size_t size = sizeof((*ctx->pre_g_128)[0]) * ((size_t) ECMULT_TABLE_SIZE(WINDOW_G));
+ /* check for overflow */
+ VERIFY_CHECK(size / sizeof((*ctx->pre_g_128)[0]) == ((size_t)ECMULT_TABLE_SIZE(WINDOW_G)));
+ ctx->pre_g_128 = (secp256k1_ge_storage (*)[])manual_alloc(prealloc, sizeof((*ctx->pre_g_128)[0]) * ECMULT_TABLE_SIZE(WINDOW_G), base, prealloc_size);
/* calculate 2^128*generator */
g_128j = gj;
@@ -333,22 +367,14 @@ static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, const
#endif
}
-static void secp256k1_ecmult_context_clone(secp256k1_ecmult_context *dst,
- const secp256k1_ecmult_context *src, const secp256k1_callback *cb) {
- if (src->pre_g == NULL) {
- dst->pre_g = NULL;
- } else {
- size_t size = sizeof((*dst->pre_g)[0]) * ECMULT_TABLE_SIZE(WINDOW_G);
- dst->pre_g = (secp256k1_ge_storage (*)[])checked_malloc(cb, size);
- memcpy(dst->pre_g, src->pre_g, size);
+static void secp256k1_ecmult_context_finalize_memcpy(secp256k1_ecmult_context *dst, const secp256k1_ecmult_context *src) {
+ if (src->pre_g != NULL) {
+ /* We cast to void* first to suppress a -Wcast-align warning. */
+ dst->pre_g = (secp256k1_ge_storage (*)[])(void*)((unsigned char*)dst + ((unsigned char*)(src->pre_g) - (unsigned char*)src));
}
#ifdef USE_ENDOMORPHISM
- if (src->pre_g_128 == NULL) {
- dst->pre_g_128 = NULL;
- } else {
- size_t size = sizeof((*dst->pre_g_128)[0]) * ECMULT_TABLE_SIZE(WINDOW_G);
- dst->pre_g_128 = (secp256k1_ge_storage (*)[])checked_malloc(cb, size);
- memcpy(dst->pre_g_128, src->pre_g_128, size);
+ if (src->pre_g_128 != NULL) {
+ dst->pre_g_128 = (secp256k1_ge_storage (*)[])(void*)((unsigned char*)dst + ((unsigned char*)(src->pre_g_128) - (unsigned char*)src));
}
#endif
}
@@ -358,10 +384,6 @@ static int secp256k1_ecmult_context_is_built(const secp256k1_ecmult_context *ctx
}
static void secp256k1_ecmult_context_clear(secp256k1_ecmult_context *ctx) {
- free(ctx->pre_g);
-#ifdef USE_ENDOMORPHISM
- free(ctx->pre_g_128);
-#endif
secp256k1_ecmult_context_init(ctx);
}
@@ -373,7 +395,7 @@ static void secp256k1_ecmult_context_clear(secp256k1_ecmult_context *ctx) {
* than the number of bits in the (absolute value) of the input.
*/
static int secp256k1_ecmult_wnaf(int *wnaf, int len, const secp256k1_scalar *a, int w) {
- secp256k1_scalar s = *a;
+ secp256k1_scalar s;
int last_set_bit = -1;
int bit = 0;
int sign = 1;
@@ -386,6 +408,7 @@ static int secp256k1_ecmult_wnaf(int *wnaf, int len, const secp256k1_scalar *a,
memset(wnaf, 0, len * sizeof(wnaf[0]));
+ s = *a;
if (secp256k1_scalar_get_bits(&s, 255, 1)) {
secp256k1_scalar_negate(&s, &s);
sign = -1;
@@ -418,7 +441,7 @@ static int secp256k1_ecmult_wnaf(int *wnaf, int len, const secp256k1_scalar *a,
CHECK(carry == 0);
while (bit < 256) {
CHECK(secp256k1_scalar_get_bits(&s, bit++, 1) == 0);
- }
+ }
#endif
return last_set_bit + 1;
}
@@ -626,52 +649,55 @@ static size_t secp256k1_strauss_scratch_size(size_t n_points) {
return n_points*point_size;
}
-static int secp256k1_ecmult_strauss_batch(const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points, size_t cb_offset) {
+static int secp256k1_ecmult_strauss_batch(const secp256k1_callback* error_callback, const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points, size_t cb_offset) {
secp256k1_gej* points;
secp256k1_scalar* scalars;
struct secp256k1_strauss_state state;
size_t i;
+ const size_t scratch_checkpoint = secp256k1_scratch_checkpoint(error_callback, scratch);
secp256k1_gej_set_infinity(r);
if (inp_g_sc == NULL && n_points == 0) {
return 1;
}
- if (!secp256k1_scratch_allocate_frame(scratch, secp256k1_strauss_scratch_size(n_points), STRAUSS_SCRATCH_OBJECTS)) {
- return 0;
- }
- points = (secp256k1_gej*)secp256k1_scratch_alloc(scratch, n_points * sizeof(secp256k1_gej));
- scalars = (secp256k1_scalar*)secp256k1_scratch_alloc(scratch, n_points * sizeof(secp256k1_scalar));
- state.prej = (secp256k1_gej*)secp256k1_scratch_alloc(scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_gej));
- state.zr = (secp256k1_fe*)secp256k1_scratch_alloc(scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_fe));
+ points = (secp256k1_gej*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(secp256k1_gej));
+ scalars = (secp256k1_scalar*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(secp256k1_scalar));
+ state.prej = (secp256k1_gej*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_gej));
+ state.zr = (secp256k1_fe*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_fe));
#ifdef USE_ENDOMORPHISM
- state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(scratch, n_points * 2 * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge));
+ state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(error_callback, scratch, n_points * 2 * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge));
state.pre_a_lam = state.pre_a + n_points * ECMULT_TABLE_SIZE(WINDOW_A);
#else
- state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge));
+ state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge));
#endif
- state.ps = (struct secp256k1_strauss_point_state*)secp256k1_scratch_alloc(scratch, n_points * sizeof(struct secp256k1_strauss_point_state));
+ state.ps = (struct secp256k1_strauss_point_state*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(struct secp256k1_strauss_point_state));
+
+ if (points == NULL || scalars == NULL || state.prej == NULL || state.zr == NULL || state.pre_a == NULL) {
+ secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
+ return 0;
+ }
for (i = 0; i < n_points; i++) {
secp256k1_ge point;
if (!cb(&scalars[i], &point, i+cb_offset, cbdata)) {
- secp256k1_scratch_deallocate_frame(scratch);
+ secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
return 0;
}
secp256k1_gej_set_ge(&points[i], &point);
}
secp256k1_ecmult_strauss_wnaf(ctx, &state, r, n_points, points, scalars, inp_g_sc);
- secp256k1_scratch_deallocate_frame(scratch);
+ secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
return 1;
}
/* Wrapper for secp256k1_ecmult_multi_func interface */
-static int secp256k1_ecmult_strauss_batch_single(const secp256k1_ecmult_context *actx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) {
- return secp256k1_ecmult_strauss_batch(actx, scratch, r, inp_g_sc, cb, cbdata, n, 0);
+static int secp256k1_ecmult_strauss_batch_single(const secp256k1_callback* error_callback, const secp256k1_ecmult_context *actx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) {
+ return secp256k1_ecmult_strauss_batch(error_callback, actx, scratch, r, inp_g_sc, cb, cbdata, n, 0);
}
-static size_t secp256k1_strauss_max_points(secp256k1_scratch *scratch) {
- return secp256k1_scratch_max_allocation(scratch, STRAUSS_SCRATCH_OBJECTS) / secp256k1_strauss_scratch_size(1);
+static size_t secp256k1_strauss_max_points(const secp256k1_callback* error_callback, secp256k1_scratch *scratch) {
+ return secp256k1_scratch_max_allocation(error_callback, scratch, STRAUSS_SCRATCH_OBJECTS) / secp256k1_strauss_scratch_size(1);
}
/** Convert a number to WNAF notation.
@@ -963,7 +989,8 @@ static size_t secp256k1_pippenger_scratch_size(size_t n_points, int bucket_windo
return (sizeof(secp256k1_gej) << bucket_window) + sizeof(struct secp256k1_pippenger_state) + entries * entry_size;
}
-static int secp256k1_ecmult_pippenger_batch(const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points, size_t cb_offset) {
+static int secp256k1_ecmult_pippenger_batch(const secp256k1_callback* error_callback, const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points, size_t cb_offset) {
+ const size_t scratch_checkpoint = secp256k1_scratch_checkpoint(error_callback, scratch);
/* Use 2(n+1) with the endomorphism, n+1 without, when calculating batch
* sizes. The reason for +1 is that we add the G scalar to the list of
* other scalars. */
@@ -988,15 +1015,21 @@ static int secp256k1_ecmult_pippenger_batch(const secp256k1_ecmult_context *ctx,
}
bucket_window = secp256k1_pippenger_bucket_window(n_points);
- if (!secp256k1_scratch_allocate_frame(scratch, secp256k1_pippenger_scratch_size(n_points, bucket_window), PIPPENGER_SCRATCH_OBJECTS)) {
+ points = (secp256k1_ge *) secp256k1_scratch_alloc(error_callback, scratch, entries * sizeof(*points));
+ scalars = (secp256k1_scalar *) secp256k1_scratch_alloc(error_callback, scratch, entries * sizeof(*scalars));
+ state_space = (struct secp256k1_pippenger_state *) secp256k1_scratch_alloc(error_callback, scratch, sizeof(*state_space));
+ if (points == NULL || scalars == NULL || state_space == NULL) {
+ secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
+ return 0;
+ }
+
+ state_space->ps = (struct secp256k1_pippenger_point_state *) secp256k1_scratch_alloc(error_callback, scratch, entries * sizeof(*state_space->ps));
+ state_space->wnaf_na = (int *) secp256k1_scratch_alloc(error_callback, scratch, entries*(WNAF_SIZE(bucket_window+1)) * sizeof(int));
+ buckets = (secp256k1_gej *) secp256k1_scratch_alloc(error_callback, scratch, (1<<bucket_window) * sizeof(*buckets));
+ if (state_space->ps == NULL || state_space->wnaf_na == NULL || buckets == NULL) {
+ secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
return 0;
}
- points = (secp256k1_ge *) secp256k1_scratch_alloc(scratch, entries * sizeof(*points));
- scalars = (secp256k1_scalar *) secp256k1_scratch_alloc(scratch, entries * sizeof(*scalars));
- state_space = (struct secp256k1_pippenger_state *) secp256k1_scratch_alloc(scratch, sizeof(*state_space));
- state_space->ps = (struct secp256k1_pippenger_point_state *) secp256k1_scratch_alloc(scratch, entries * sizeof(*state_space->ps));
- state_space->wnaf_na = (int *) secp256k1_scratch_alloc(scratch, entries*(WNAF_SIZE(bucket_window+1)) * sizeof(int));
- buckets = (secp256k1_gej *) secp256k1_scratch_alloc(scratch, sizeof(*buckets) << bucket_window);
if (inp_g_sc != NULL) {
scalars[0] = *inp_g_sc;
@@ -1010,7 +1043,7 @@ static int secp256k1_ecmult_pippenger_batch(const secp256k1_ecmult_context *ctx,
while (point_idx < n_points) {
if (!cb(&scalars[idx], &points[idx], point_idx + cb_offset, cbdata)) {
- secp256k1_scratch_deallocate_frame(scratch);
+ secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
return 0;
}
idx++;
@@ -1034,13 +1067,13 @@ static int secp256k1_ecmult_pippenger_batch(const secp256k1_ecmult_context *ctx,
for(i = 0; i < 1<<bucket_window; i++) {
secp256k1_gej_clear(&buckets[i]);
}
- secp256k1_scratch_deallocate_frame(scratch);
+ secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
return 1;
}
/* Wrapper for secp256k1_ecmult_multi_func interface */
-static int secp256k1_ecmult_pippenger_batch_single(const secp256k1_ecmult_context *actx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) {
- return secp256k1_ecmult_pippenger_batch(actx, scratch, r, inp_g_sc, cb, cbdata, n, 0);
+static int secp256k1_ecmult_pippenger_batch_single(const secp256k1_callback* error_callback, const secp256k1_ecmult_context *actx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) {
+ return secp256k1_ecmult_pippenger_batch(error_callback, actx, scratch, r, inp_g_sc, cb, cbdata, n, 0);
}
/**
@@ -1048,8 +1081,8 @@ static int secp256k1_ecmult_pippenger_batch_single(const secp256k1_ecmult_contex
* a given scratch space. The function ensures that fewer points may also be
* used.
*/
-static size_t secp256k1_pippenger_max_points(secp256k1_scratch *scratch) {
- size_t max_alloc = secp256k1_scratch_max_allocation(scratch, PIPPENGER_SCRATCH_OBJECTS);
+static size_t secp256k1_pippenger_max_points(const secp256k1_callback* error_callback, secp256k1_scratch *scratch) {
+ size_t max_alloc = secp256k1_scratch_max_allocation(error_callback, scratch, PIPPENGER_SCRATCH_OBJECTS);
int bucket_window;
size_t res = 0;
@@ -1131,11 +1164,11 @@ static int secp256k1_ecmult_multi_batch_size_helper(size_t *n_batches, size_t *n
return 1;
}
-typedef int (*secp256k1_ecmult_multi_func)(const secp256k1_ecmult_context*, secp256k1_scratch*, secp256k1_gej*, const secp256k1_scalar*, secp256k1_ecmult_multi_callback cb, void*, size_t);
-static int secp256k1_ecmult_multi_var(const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) {
+typedef int (*secp256k1_ecmult_multi_func)(const secp256k1_callback* error_callback, const secp256k1_ecmult_context*, secp256k1_scratch*, secp256k1_gej*, const secp256k1_scalar*, secp256k1_ecmult_multi_callback cb, void*, size_t);
+static int secp256k1_ecmult_multi_var(const secp256k1_callback* error_callback, const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) {
size_t i;
- int (*f)(const secp256k1_ecmult_context*, secp256k1_scratch*, secp256k1_gej*, const secp256k1_scalar*, secp256k1_ecmult_multi_callback cb, void*, size_t, size_t);
+ int (*f)(const secp256k1_callback* error_callback, const secp256k1_ecmult_context*, secp256k1_scratch*, secp256k1_gej*, const secp256k1_scalar*, secp256k1_ecmult_multi_callback cb, void*, size_t, size_t);
size_t n_batches;
size_t n_batch_points;
@@ -1152,16 +1185,18 @@ static int secp256k1_ecmult_multi_var(const secp256k1_ecmult_context *ctx, secp2
return secp256k1_ecmult_multi_simple_var(ctx, r, inp_g_sc, cb, cbdata, n);
}
- /* Compute the batch sizes for pippenger given a scratch space. If it's greater than a threshold
- * use pippenger. Otherwise use strauss */
- if (!secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, secp256k1_pippenger_max_points(scratch), n)) {
- return 0;
+ /* Compute the batch sizes for Pippenger's algorithm given a scratch space. If it's greater than
+ * a threshold use Pippenger's algorithm. Otherwise use Strauss' algorithm.
+ * As a first step check if there's enough space for Pippenger's algo (which requires less space
+ * than Strauss' algo) and if not, use the simple algorithm. */
+ if (!secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, secp256k1_pippenger_max_points(error_callback, scratch), n)) {
+ return secp256k1_ecmult_multi_simple_var(ctx, r, inp_g_sc, cb, cbdata, n);
}
if (n_batch_points >= ECMULT_PIPPENGER_THRESHOLD) {
f = secp256k1_ecmult_pippenger_batch;
} else {
- if (!secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, secp256k1_strauss_max_points(scratch), n)) {
- return 0;
+ if (!secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, secp256k1_strauss_max_points(error_callback, scratch), n)) {
+ return secp256k1_ecmult_multi_simple_var(ctx, r, inp_g_sc, cb, cbdata, n);
}
f = secp256k1_ecmult_strauss_batch;
}
@@ -1169,7 +1204,7 @@ static int secp256k1_ecmult_multi_var(const secp256k1_ecmult_context *ctx, secp2
size_t nbp = n < n_batch_points ? n : n_batch_points;
size_t offset = n_batch_points*i;
secp256k1_gej tmp;
- if (!f(ctx, scratch, &tmp, i == 0 ? inp_g_sc : NULL, cb, cbdata, nbp, offset)) {
+ if (!f(error_callback, ctx, scratch, &tmp, i == 0 ? inp_g_sc : NULL, cb, cbdata, nbp, offset)) {
return 0;
}
secp256k1_gej_add_var(r, r, &tmp, NULL);
diff --git a/src/secp256k1/src/field.h b/src/secp256k1/src/field.h
index bb6692ad57..7993a1f11e 100644
--- a/src/secp256k1/src/field.h
+++ b/src/secp256k1/src/field.h
@@ -32,10 +32,12 @@
#include "util.h"
-/** Normalize a field element. */
+/** Normalize a field element. This brings the field element to a canonical representation, reduces
+ * its magnitude to 1, and reduces it modulo field size `p`.
+ */
static void secp256k1_fe_normalize(secp256k1_fe *r);
-/** Weakly normalize a field element: reduce it magnitude to 1, but don't fully normalize. */
+/** Weakly normalize a field element: reduce its magnitude to 1, but don't fully normalize. */
static void secp256k1_fe_normalize_weak(secp256k1_fe *r);
/** Normalize a field element, without constant-time guarantee. */
@@ -123,10 +125,10 @@ static void secp256k1_fe_to_storage(secp256k1_fe_storage *r, const secp256k1_fe
/** Convert a field element back from the storage type. */
static void secp256k1_fe_from_storage(secp256k1_fe *r, const secp256k1_fe_storage *a);
-/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. */
+/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. Both *r and *a must be initialized.*/
static void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, const secp256k1_fe_storage *a, int flag);
-/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. */
+/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. Both *r and *a must be initialized.*/
static void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag);
#endif /* SECP256K1_FIELD_H */
diff --git a/src/secp256k1/src/field_10x26_impl.h b/src/secp256k1/src/field_10x26_impl.h
index 4ae4fdcec8..651500ee8e 100644
--- a/src/secp256k1/src/field_10x26_impl.h
+++ b/src/secp256k1/src/field_10x26_impl.h
@@ -320,6 +320,7 @@ static int secp256k1_fe_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b) {
}
static int secp256k1_fe_set_b32(secp256k1_fe *r, const unsigned char *a) {
+ int ret;
r->n[0] = (uint32_t)a[31] | ((uint32_t)a[30] << 8) | ((uint32_t)a[29] << 16) | ((uint32_t)(a[28] & 0x3) << 24);
r->n[1] = (uint32_t)((a[28] >> 2) & 0x3f) | ((uint32_t)a[27] << 6) | ((uint32_t)a[26] << 14) | ((uint32_t)(a[25] & 0xf) << 22);
r->n[2] = (uint32_t)((a[25] >> 4) & 0xf) | ((uint32_t)a[24] << 4) | ((uint32_t)a[23] << 12) | ((uint32_t)(a[22] & 0x3f) << 20);
@@ -331,15 +332,17 @@ static int secp256k1_fe_set_b32(secp256k1_fe *r, const unsigned char *a) {
r->n[8] = (uint32_t)a[5] | ((uint32_t)a[4] << 8) | ((uint32_t)a[3] << 16) | ((uint32_t)(a[2] & 0x3) << 24);
r->n[9] = (uint32_t)((a[2] >> 2) & 0x3f) | ((uint32_t)a[1] << 6) | ((uint32_t)a[0] << 14);
- if (r->n[9] == 0x3FFFFFUL && (r->n[8] & r->n[7] & r->n[6] & r->n[5] & r->n[4] & r->n[3] & r->n[2]) == 0x3FFFFFFUL && (r->n[1] + 0x40UL + ((r->n[0] + 0x3D1UL) >> 26)) > 0x3FFFFFFUL) {
- return 0;
- }
+ ret = !((r->n[9] == 0x3FFFFFUL) & ((r->n[8] & r->n[7] & r->n[6] & r->n[5] & r->n[4] & r->n[3] & r->n[2]) == 0x3FFFFFFUL) & ((r->n[1] + 0x40UL + ((r->n[0] + 0x3D1UL) >> 26)) > 0x3FFFFFFUL));
#ifdef VERIFY
r->magnitude = 1;
- r->normalized = 1;
- secp256k1_fe_verify(r);
+ if (ret) {
+ r->normalized = 1;
+ secp256k1_fe_verify(r);
+ } else {
+ r->normalized = 0;
+ }
#endif
- return 1;
+ return ret;
}
/** Convert a field element to a 32-byte big endian value. Requires the input to be normalized */
@@ -1094,6 +1097,7 @@ static void secp256k1_fe_sqr(secp256k1_fe *r, const secp256k1_fe *a) {
static SECP256K1_INLINE void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag) {
uint32_t mask0, mask1;
+ VG_CHECK_VERIFY(r->n, sizeof(r->n));
mask0 = flag + ~((uint32_t)0);
mask1 = ~mask0;
r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1);
@@ -1107,15 +1111,16 @@ static SECP256K1_INLINE void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_
r->n[8] = (r->n[8] & mask0) | (a->n[8] & mask1);
r->n[9] = (r->n[9] & mask0) | (a->n[9] & mask1);
#ifdef VERIFY
- if (a->magnitude > r->magnitude) {
+ if (flag) {
r->magnitude = a->magnitude;
+ r->normalized = a->normalized;
}
- r->normalized &= a->normalized;
#endif
}
static SECP256K1_INLINE void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, const secp256k1_fe_storage *a, int flag) {
uint32_t mask0, mask1;
+ VG_CHECK_VERIFY(r->n, sizeof(r->n));
mask0 = flag + ~((uint32_t)0);
mask1 = ~mask0;
r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1);
diff --git a/src/secp256k1/src/field_5x52_impl.h b/src/secp256k1/src/field_5x52_impl.h
index f4263320d5..71a38f915b 100644
--- a/src/secp256k1/src/field_5x52_impl.h
+++ b/src/secp256k1/src/field_5x52_impl.h
@@ -283,6 +283,7 @@ static int secp256k1_fe_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b) {
}
static int secp256k1_fe_set_b32(secp256k1_fe *r, const unsigned char *a) {
+ int ret;
r->n[0] = (uint64_t)a[31]
| ((uint64_t)a[30] << 8)
| ((uint64_t)a[29] << 16)
@@ -317,15 +318,17 @@ static int secp256k1_fe_set_b32(secp256k1_fe *r, const unsigned char *a) {
| ((uint64_t)a[2] << 24)
| ((uint64_t)a[1] << 32)
| ((uint64_t)a[0] << 40);
- if (r->n[4] == 0x0FFFFFFFFFFFFULL && (r->n[3] & r->n[2] & r->n[1]) == 0xFFFFFFFFFFFFFULL && r->n[0] >= 0xFFFFEFFFFFC2FULL) {
- return 0;
- }
+ ret = !((r->n[4] == 0x0FFFFFFFFFFFFULL) & ((r->n[3] & r->n[2] & r->n[1]) == 0xFFFFFFFFFFFFFULL) & (r->n[0] >= 0xFFFFEFFFFFC2FULL));
#ifdef VERIFY
r->magnitude = 1;
- r->normalized = 1;
- secp256k1_fe_verify(r);
+ if (ret) {
+ r->normalized = 1;
+ secp256k1_fe_verify(r);
+ } else {
+ r->normalized = 0;
+ }
#endif
- return 1;
+ return ret;
}
/** Convert a field element to a 32-byte big endian value. Requires the input to be normalized */
@@ -446,6 +449,7 @@ static void secp256k1_fe_sqr(secp256k1_fe *r, const secp256k1_fe *a) {
static SECP256K1_INLINE void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag) {
uint64_t mask0, mask1;
+ VG_CHECK_VERIFY(r->n, sizeof(r->n));
mask0 = flag + ~((uint64_t)0);
mask1 = ~mask0;
r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1);
@@ -454,15 +458,16 @@ static SECP256K1_INLINE void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_
r->n[3] = (r->n[3] & mask0) | (a->n[3] & mask1);
r->n[4] = (r->n[4] & mask0) | (a->n[4] & mask1);
#ifdef VERIFY
- if (a->magnitude > r->magnitude) {
+ if (flag) {
r->magnitude = a->magnitude;
+ r->normalized = a->normalized;
}
- r->normalized &= a->normalized;
#endif
}
static SECP256K1_INLINE void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, const secp256k1_fe_storage *a, int flag) {
uint64_t mask0, mask1;
+ VG_CHECK_VERIFY(r->n, sizeof(r->n));
mask0 = flag + ~((uint64_t)0);
mask1 = ~mask0;
r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1);
diff --git a/src/secp256k1/src/field_impl.h b/src/secp256k1/src/field_impl.h
index 6070caccfe..485921a60e 100644
--- a/src/secp256k1/src/field_impl.h
+++ b/src/secp256k1/src/field_impl.h
@@ -315,4 +315,6 @@ static int secp256k1_fe_is_quad_var(const secp256k1_fe *a) {
#endif
}
+static const secp256k1_fe secp256k1_fe_one = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1);
+
#endif /* SECP256K1_FIELD_IMPL_H */
diff --git a/src/secp256k1/src/gen_context.c b/src/secp256k1/src/gen_context.c
index 87d296ebf0..539f574bfd 100644
--- a/src/secp256k1/src/gen_context.c
+++ b/src/secp256k1/src/gen_context.c
@@ -4,10 +4,16 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
+// Autotools creates libsecp256k1-config.h, of which ECMULT_GEN_PREC_BITS is needed.
+// ifndef guard so downstream users can define their own if they do not use autotools.
+#if !defined(ECMULT_GEN_PREC_BITS)
+#include "libsecp256k1-config.h"
+#endif
#define USE_BASIC_CONFIG 1
-
#include "basic-config.h"
+
#include "include/secp256k1.h"
+#include "util.h"
#include "field_impl.h"
#include "scalar_impl.h"
#include "group_impl.h"
@@ -26,6 +32,7 @@ static const secp256k1_callback default_error_callback = {
int main(int argc, char **argv) {
secp256k1_ecmult_gen_context ctx;
+ void *prealloc, *base;
int inner;
int outer;
FILE* fp;
@@ -38,26 +45,31 @@ int main(int argc, char **argv) {
fprintf(stderr, "Could not open src/ecmult_static_context.h for writing!\n");
return -1;
}
-
+
fprintf(fp, "#ifndef _SECP256K1_ECMULT_STATIC_CONTEXT_\n");
fprintf(fp, "#define _SECP256K1_ECMULT_STATIC_CONTEXT_\n");
fprintf(fp, "#include \"src/group.h\"\n");
fprintf(fp, "#define SC SECP256K1_GE_STORAGE_CONST\n");
- fprintf(fp, "static const secp256k1_ge_storage secp256k1_ecmult_static_context[64][16] = {\n");
+ fprintf(fp, "#if ECMULT_GEN_PREC_N != %d || ECMULT_GEN_PREC_G != %d\n", ECMULT_GEN_PREC_N, ECMULT_GEN_PREC_G);
+ fprintf(fp, " #error configuration mismatch, invalid ECMULT_GEN_PREC_N, ECMULT_GEN_PREC_G. Try deleting ecmult_static_context.h before the build.\n");
+ fprintf(fp, "#endif\n");
+ fprintf(fp, "static const secp256k1_ge_storage secp256k1_ecmult_static_context[ECMULT_GEN_PREC_N][ECMULT_GEN_PREC_G] = {\n");
+ base = checked_malloc(&default_error_callback, SECP256K1_ECMULT_GEN_CONTEXT_PREALLOCATED_SIZE);
+ prealloc = base;
secp256k1_ecmult_gen_context_init(&ctx);
- secp256k1_ecmult_gen_context_build(&ctx, &default_error_callback);
- for(outer = 0; outer != 64; outer++) {
+ secp256k1_ecmult_gen_context_build(&ctx, &prealloc);
+ for(outer = 0; outer != ECMULT_GEN_PREC_N; outer++) {
fprintf(fp,"{\n");
- for(inner = 0; inner != 16; inner++) {
+ for(inner = 0; inner != ECMULT_GEN_PREC_G; inner++) {
fprintf(fp," SC(%uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu)", SECP256K1_GE_STORAGE_CONST_GET((*ctx.prec)[outer][inner]));
- if (inner != 15) {
+ if (inner != ECMULT_GEN_PREC_G - 1) {
fprintf(fp,",\n");
} else {
fprintf(fp,"\n");
}
}
- if (outer != 63) {
+ if (outer != ECMULT_GEN_PREC_N - 1) {
fprintf(fp,"},\n");
} else {
fprintf(fp,"}\n");
@@ -65,10 +77,11 @@ int main(int argc, char **argv) {
}
fprintf(fp,"};\n");
secp256k1_ecmult_gen_context_clear(&ctx);
-
+ free(base);
+
fprintf(fp, "#undef SC\n");
fprintf(fp, "#endif\n");
fclose(fp);
-
+
return 0;
}
diff --git a/src/secp256k1/src/group.h b/src/secp256k1/src/group.h
index 8e122ab429..863644f0f0 100644
--- a/src/secp256k1/src/group.h
+++ b/src/secp256k1/src/group.h
@@ -95,14 +95,13 @@ 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);
+/** Set r equal to the double of a, a cannot be infinity. Constant time. */
+static void secp256k1_gej_double_nonzero(secp256k1_gej *r, 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). */
+/** Set r equal to the double of a. If rzr is not-NULL this sets *rzr such that r->z == a->z * *rzr (where infinity means an implicit z = 0). */
static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr);
-/** Set r equal to the sum of a and b. If rzr is non-NULL, r->z = a->z * *rzr (a cannot be infinity in that case). */
+/** Set r equal to the sum of a and b. If rzr is non-NULL this sets *rzr such that r->z == a->z * *rzr (a cannot be infinity in that case). */
static void secp256k1_gej_add_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_gej *b, secp256k1_fe *rzr);
/** Set r equal to the sum of a and b (with b given in affine coordinates, and not infinity). */
@@ -110,7 +109,7 @@ static void secp256k1_gej_add_ge(secp256k1_gej *r, const secp256k1_gej *a, const
/** Set r equal to the sum of a and b (with b given in affine coordinates). This is more efficient
than secp256k1_gej_add_var. It is identical to secp256k1_gej_add_ge but without constant-time
- guarantee, and b is allowed to be infinity. If rzr is non-NULL, r->z = a->z * *rzr (a cannot be infinity in that case). */
+ guarantee, and b is allowed to be infinity. If rzr is non-NULL this sets *rzr such that r->z == a->z * *rzr (a cannot be infinity in that case). */
static void secp256k1_gej_add_ge_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_ge *b, secp256k1_fe *rzr);
/** Set r equal to the sum of a and b (with the inverse of b's Z coordinate passed as bzinv). */
@@ -133,7 +132,7 @@ static void secp256k1_ge_to_storage(secp256k1_ge_storage *r, const secp256k1_ge
/** Convert a group element back from the storage type. */
static void secp256k1_ge_from_storage(secp256k1_ge *r, const secp256k1_ge_storage *a);
-/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. */
+/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. Both *r and *a must be initialized.*/
static void secp256k1_ge_storage_cmov(secp256k1_ge_storage *r, const secp256k1_ge_storage *a, int flag);
/** Rescale a jacobian point by b which must be non-zero. Constant-time. */
diff --git a/src/secp256k1/src/group_impl.h b/src/secp256k1/src/group_impl.h
index 9b93c39e92..43b039becf 100644
--- a/src/secp256k1/src/group_impl.h
+++ b/src/secp256k1/src/group_impl.h
@@ -303,7 +303,7 @@ static int secp256k1_ge_is_valid_var(const secp256k1_ge *a) {
return secp256k1_fe_equal_var(&y2, &x3);
}
-static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr) {
+static SECP256K1_INLINE void secp256k1_gej_double_nonzero(secp256k1_gej *r, const secp256k1_gej *a) {
/* Operations: 3 mul, 4 sqr, 0 normalize, 12 mul_int/add/negate.
*
* Note that there is an implementation described at
@@ -312,29 +312,9 @@ static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, s
* 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) {
- if (rzr != NULL) {
- secp256k1_fe_set_int(rzr, 1);
- }
- return;
- }
- if (rzr != NULL) {
- *rzr = a->y;
- secp256k1_fe_normalize_weak(rzr);
- secp256k1_fe_mul_int(rzr, 2);
- }
+ VERIFY_CHECK(!secp256k1_gej_is_infinity(a));
+ r->infinity = 0;
secp256k1_fe_mul(&r->z, &a->z, &a->y);
secp256k1_fe_mul_int(&r->z, 2); /* Z' = 2*Y*Z (2) */
@@ -358,9 +338,32 @@ static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, s
secp256k1_fe_add(&r->y, &t2); /* Y' = 36*X^3*Y^2 - 27*X^6 - 8*Y^4 (4) */
}
-static SECP256K1_INLINE void secp256k1_gej_double_nonzero(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr) {
- VERIFY_CHECK(!secp256k1_gej_is_infinity(a));
- secp256k1_gej_double_var(r, a, rzr);
+static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr) {
+ /** 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).
+ */
+ if (a->infinity) {
+ r->infinity = 1;
+ if (rzr != NULL) {
+ secp256k1_fe_set_int(rzr, 1);
+ }
+ return;
+ }
+
+ if (rzr != NULL) {
+ *rzr = a->y;
+ secp256k1_fe_normalize_weak(rzr);
+ secp256k1_fe_mul_int(rzr, 2);
+ }
+
+ secp256k1_gej_double_nonzero(r, a);
}
static void secp256k1_gej_add_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_gej *b, secp256k1_fe *rzr) {
diff --git a/src/secp256k1/src/hash_impl.h b/src/secp256k1/src/hash_impl.h
index 009f26beba..782f97216c 100644
--- a/src/secp256k1/src/hash_impl.h
+++ b/src/secp256k1/src/hash_impl.h
@@ -131,7 +131,8 @@ static void secp256k1_sha256_transform(uint32_t* s, const uint32_t* chunk) {
static void secp256k1_sha256_write(secp256k1_sha256 *hash, const unsigned char *data, size_t len) {
size_t bufsize = hash->bytes & 0x3F;
hash->bytes += len;
- while (bufsize + len >= 64) {
+ VERIFY_CHECK(hash->bytes >= len);
+ while (len >= 64 - bufsize) {
/* Fill the buffer, and process it. */
size_t chunk_len = 64 - bufsize;
memcpy(((unsigned char*)hash->buf) + bufsize, data, chunk_len);
diff --git a/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1.java b/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1.java
deleted file mode 100644
index 1c67802fba..0000000000
--- a/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1.java
+++ /dev/null
@@ -1,446 +0,0 @@
-/*
- * 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.*;
-
-/**
- * <p>This class holds native methods to handle ECDSA verification.</p>
- *
- * <p>You can find an example library that can be used for this at https://github.com/bitcoin/secp256k1</p>
- *
- * <p>To build secp256k1 for use with bitcoinj, run
- * `./configure --enable-jni --enable-experimental --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
- * </p>
- */
-public class NativeSecp256k1 {
-
- private static final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
- private static final Lock r = rwl.readLock();
- private static final Lock w = rwl.writeLock();
- private static ThreadLocal<ByteBuffer> nativeECDSABuffer = new ThreadLocal<ByteBuffer>();
- /**
- * 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) throws AssertFailException{
- Preconditions.checkArgument(data.length == 32 && signature.length <= 520 && pub.length <= 520);
-
- ByteBuffer byteBuff = nativeECDSABuffer.get();
- if (byteBuff == null || byteBuff.capacity() < 520) {
- byteBuff = ByteBuffer.allocateDirect(520);
- byteBuff.order(ByteOrder.nativeOrder());
- nativeECDSABuffer.set(byteBuff);
- }
- byteBuff.rewind();
- byteBuff.put(data);
- byteBuff.put(signature);
- byteBuff.put(pub);
-
- 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;
- }
-
- /**
- * libsecp256k1 PrivKey Tweak-Add - Tweak privkey by adding to it
- *
- * @param tweak some bytes to tweak with
- * @param seckey 32-byte seckey
- */
- 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();
- }
- }
-
- 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_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
deleted file mode 100644
index d766a1029c..0000000000
--- a/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java
+++ /dev/null
@@ -1,226 +0,0 @@
-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 an 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");
- }
-
- 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 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
deleted file mode 100644
index 04732ba044..0000000000
--- a/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Util.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 216c986a8b..0000000000
--- a/src/secp256k1/src/java/org/bitcoin/Secp256k1Context.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * 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
deleted file mode 100644
index b50970b4f2..0000000000
--- a/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.c
+++ /dev/null
@@ -1,379 +0,0 @@
-#include <stdlib.h>
-#include <stdint.h>
-#include <string.h>
-#include "org_bitcoin_NativeSecp256k1.h"
-#include "include/secp256k1.h"
-#include "include/secp256k1_ecdh.h"
-#include "include/secp256k1_recovery.h"
-
-
-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)
-{
- 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);
-
- 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 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;
-
- return 0;
-}
-
-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,
- NULL,
- NULL
- );
- }
-
- 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
deleted file mode 100644
index fe613c9e9e..0000000000
--- a/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.h
+++ /dev/null
@@ -1,119 +0,0 @@
-/* DO NOT EDIT THIS FILE - it is machine generated */
-#include <jni.h>
-#include "include/secp256k1.h"
-/* Header for class org_bitcoin_NativeSecp256k1 */
-
-#ifndef _Included_org_bitcoin_NativeSecp256k1
-#define _Included_org_bitcoin_NativeSecp256k1
-#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;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
- */
-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_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
-}
-#endif
-#endif
diff --git a/src/secp256k1/src/java/org_bitcoin_Secp256k1Context.c b/src/secp256k1/src/java/org_bitcoin_Secp256k1Context.c
deleted file mode 100644
index a52939e7e7..0000000000
--- a/src/secp256k1/src/java/org_bitcoin_Secp256k1Context.c
+++ /dev/null
@@ -1,15 +0,0 @@
-#include <stdlib.h>
-#include <stdint.h>
-#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
deleted file mode 100644
index 0d2bc84b7f..0000000000
--- a/src/secp256k1/src/java/org_bitcoin_Secp256k1Context.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/* DO NOT EDIT THIS FILE - it is machine generated */
-#include <jni.h>
-#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/main_impl.h b/src/secp256k1/src/modules/ecdh/main_impl.h
index 44cb68e750..07a25b80d4 100644
--- a/src/secp256k1/src/modules/ecdh/main_impl.h
+++ b/src/secp256k1/src/modules/ecdh/main_impl.h
@@ -10,14 +10,14 @@
#include "include/secp256k1_ecdh.h"
#include "ecmult_const_impl.h"
-static int ecdh_hash_function_sha256(unsigned char *output, const unsigned char *x, const unsigned char *y, void *data) {
- unsigned char version = (y[31] & 0x01) | 0x02;
+static int ecdh_hash_function_sha256(unsigned char *output, const unsigned char *x32, const unsigned char *y32, void *data) {
+ unsigned char version = (y32[31] & 0x01) | 0x02;
secp256k1_sha256 sha;
(void)data;
secp256k1_sha256_initialize(&sha);
secp256k1_sha256_write(&sha, &version, 1);
- secp256k1_sha256_write(&sha, x, 32);
+ secp256k1_sha256_write(&sha, x32, 32);
secp256k1_sha256_finalize(&sha, output);
return 1;
@@ -32,36 +32,40 @@ int secp256k1_ecdh(const secp256k1_context* ctx, unsigned char *output, const se
secp256k1_gej res;
secp256k1_ge pt;
secp256k1_scalar s;
+ unsigned char x[32];
+ unsigned char y[32];
+
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(output != NULL);
ARG_CHECK(point != NULL);
ARG_CHECK(scalar != NULL);
+
if (hashfp == NULL) {
hashfp = secp256k1_ecdh_hash_function_default;
}
secp256k1_pubkey_load(ctx, &pt, point);
secp256k1_scalar_set_b32(&s, scalar, &overflow);
- if (overflow || secp256k1_scalar_is_zero(&s)) {
- ret = 0;
- } else {
- unsigned char x[32];
- unsigned char y[32];
-
- secp256k1_ecmult_const(&res, &pt, &s, 256);
- secp256k1_ge_set_gej(&pt, &res);
-
- /* Compute a hash of the point */
- secp256k1_fe_normalize(&pt.x);
- secp256k1_fe_normalize(&pt.y);
- secp256k1_fe_get_b32(x, &pt.x);
- secp256k1_fe_get_b32(y, &pt.y);
-
- ret = hashfp(output, x, y, data);
- }
+ overflow |= secp256k1_scalar_is_zero(&s);
+ secp256k1_scalar_cmov(&s, &secp256k1_scalar_one, overflow);
+
+ secp256k1_ecmult_const(&res, &pt, &s, 256);
+ secp256k1_ge_set_gej(&pt, &res);
+
+ /* Compute a hash of the point */
+ secp256k1_fe_normalize(&pt.x);
+ secp256k1_fe_normalize(&pt.y);
+ secp256k1_fe_get_b32(x, &pt.x);
+ secp256k1_fe_get_b32(y, &pt.y);
+
+ ret = hashfp(output, x, y, data);
+
+ memset(x, 0, 32);
+ memset(y, 0, 32);
secp256k1_scalar_clear(&s);
- return ret;
+
+ return !!ret & !overflow;
}
#endif /* SECP256K1_MODULE_ECDH_MAIN_H */
diff --git a/src/secp256k1/src/modules/recovery/main_impl.h b/src/secp256k1/src/modules/recovery/main_impl.h
index 2f6691c5a1..e2576aa953 100755..100644
--- a/src/secp256k1/src/modules/recovery/main_impl.h
+++ b/src/secp256k1/src/modules/recovery/main_impl.h
@@ -122,48 +122,15 @@ static int secp256k1_ecdsa_sig_recover(const secp256k1_ecmult_context *ctx, cons
int secp256k1_ecdsa_sign_recoverable(const secp256k1_context* ctx, secp256k1_ecdsa_recoverable_signature *signature, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void* noncedata) {
secp256k1_scalar r, s;
- secp256k1_scalar sec, non, msg;
- int recid;
- int ret = 0;
- int overflow = 0;
+ int ret, recid;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
ARG_CHECK(msg32 != NULL);
ARG_CHECK(signature != NULL);
ARG_CHECK(seckey != NULL);
- if (noncefp == NULL) {
- noncefp = secp256k1_nonce_function_default;
- }
- secp256k1_scalar_set_b32(&sec, seckey, &overflow);
- /* Fail if the secret key is invalid. */
- if (!overflow && !secp256k1_scalar_is_zero(&sec)) {
- unsigned char nonce32[32];
- unsigned int count = 0;
- secp256k1_scalar_set_b32(&msg, msg32, NULL);
- while (1) {
- ret = noncefp(nonce32, msg32, seckey, NULL, (void*)noncedata, count);
- if (!ret) {
- break;
- }
- secp256k1_scalar_set_b32(&non, nonce32, &overflow);
- if (!secp256k1_scalar_is_zero(&non) && !overflow) {
- if (secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, &r, &s, &sec, &msg, &non, &recid)) {
- break;
- }
- }
- count++;
- }
- memset(nonce32, 0, 32);
- secp256k1_scalar_clear(&msg);
- secp256k1_scalar_clear(&non);
- secp256k1_scalar_clear(&sec);
- }
- if (ret) {
- secp256k1_ecdsa_recoverable_signature_save(signature, &r, &s, recid);
- } else {
- memset(signature, 0, sizeof(*signature));
- }
+ ret = secp256k1_ecdsa_sign_inner(ctx, &r, &s, &recid, msg32, seckey, noncefp, noncedata);
+ secp256k1_ecdsa_recoverable_signature_save(signature, &r, &s, recid);
return ret;
}
diff --git a/src/secp256k1/src/modules/recovery/tests_impl.h b/src/secp256k1/src/modules/recovery/tests_impl.h
index 5c9bbe8610..38a533a755 100644
--- a/src/secp256k1/src/modules/recovery/tests_impl.h
+++ b/src/secp256k1/src/modules/recovery/tests_impl.h
@@ -215,7 +215,7 @@ void test_ecdsa_recovery_edge_cases(void) {
};
const unsigned char sig64[64] = {
/* Generated by signing the above message with nonce 'This is the nonce we will use...'
- * and secret key 0 (which is not valid), resulting in recid 0. */
+ * and secret key 0 (which is not valid), resulting in recid 1. */
0x67, 0xCB, 0x28, 0x5F, 0x9C, 0xD1, 0x94, 0xE8,
0x40, 0xD6, 0x29, 0x39, 0x7A, 0xF5, 0x56, 0x96,
0x62, 0xFD, 0xE4, 0x46, 0x49, 0x99, 0x59, 0x63,
diff --git a/src/secp256k1/src/scalar.h b/src/secp256k1/src/scalar.h
index 59304cb66e..2a74703523 100644
--- a/src/secp256k1/src/scalar.h
+++ b/src/secp256k1/src/scalar.h
@@ -32,9 +32,17 @@ static unsigned int secp256k1_scalar_get_bits(const secp256k1_scalar *a, unsigne
/** Access bits from a scalar. Not constant time. */
static unsigned int secp256k1_scalar_get_bits_var(const secp256k1_scalar *a, unsigned int offset, unsigned int count);
-/** Set a scalar from a big endian byte array. */
+/** Set a scalar from a big endian byte array. The scalar will be reduced modulo group order `n`.
+ * In: bin: pointer to a 32-byte array.
+ * Out: r: scalar to be set.
+ * overflow: non-zero if the scalar was bigger or equal to `n` before reduction, zero otherwise (can be NULL).
+ */
static void secp256k1_scalar_set_b32(secp256k1_scalar *r, const unsigned char *bin, int *overflow);
+/** Set a scalar from a big endian byte array and returns 1 if it is a valid
+ * seckey and 0 otherwise. */
+static int secp256k1_scalar_set_b32_seckey(secp256k1_scalar *r, const unsigned char *bin);
+
/** Set a scalar to an unsigned integer. */
static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsigned int v);
@@ -103,4 +111,7 @@ static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar
/** Multiply a and b (without taking the modulus!), divide by 2**shift, and round to the nearest integer. Shift must be at least 256. */
static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b, unsigned int shift);
+/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. Both *r and *a must be initialized.*/
+static void secp256k1_scalar_cmov(secp256k1_scalar *r, const secp256k1_scalar *a, int flag);
+
#endif /* SECP256K1_SCALAR_H */
diff --git a/src/secp256k1/src/scalar_4x64_impl.h b/src/secp256k1/src/scalar_4x64_impl.h
index d378335d99..8f539c4bc6 100644
--- a/src/secp256k1/src/scalar_4x64_impl.h
+++ b/src/secp256k1/src/scalar_4x64_impl.h
@@ -946,4 +946,15 @@ SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r,
secp256k1_scalar_cadd_bit(r, 0, (l[(shift - 1) >> 6] >> ((shift - 1) & 0x3f)) & 1);
}
+static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const secp256k1_scalar *a, int flag) {
+ uint64_t mask0, mask1;
+ VG_CHECK_VERIFY(r->d, sizeof(r->d));
+ mask0 = flag + ~((uint64_t)0);
+ mask1 = ~mask0;
+ r->d[0] = (r->d[0] & mask0) | (a->d[0] & mask1);
+ r->d[1] = (r->d[1] & mask0) | (a->d[1] & mask1);
+ r->d[2] = (r->d[2] & mask0) | (a->d[2] & mask1);
+ r->d[3] = (r->d[3] & mask0) | (a->d[3] & mask1);
+}
+
#endif /* SECP256K1_SCALAR_REPR_IMPL_H */
diff --git a/src/secp256k1/src/scalar_8x32_impl.h b/src/secp256k1/src/scalar_8x32_impl.h
index 4f9ed61fea..3c372f34fe 100644
--- a/src/secp256k1/src/scalar_8x32_impl.h
+++ b/src/secp256k1/src/scalar_8x32_impl.h
@@ -718,4 +718,19 @@ SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r,
secp256k1_scalar_cadd_bit(r, 0, (l[(shift - 1) >> 5] >> ((shift - 1) & 0x1f)) & 1);
}
+static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const secp256k1_scalar *a, int flag) {
+ uint32_t mask0, mask1;
+ VG_CHECK_VERIFY(r->d, sizeof(r->d));
+ mask0 = flag + ~((uint32_t)0);
+ mask1 = ~mask0;
+ r->d[0] = (r->d[0] & mask0) | (a->d[0] & mask1);
+ r->d[1] = (r->d[1] & mask0) | (a->d[1] & mask1);
+ r->d[2] = (r->d[2] & mask0) | (a->d[2] & mask1);
+ r->d[3] = (r->d[3] & mask0) | (a->d[3] & mask1);
+ r->d[4] = (r->d[4] & mask0) | (a->d[4] & mask1);
+ r->d[5] = (r->d[5] & mask0) | (a->d[5] & mask1);
+ r->d[6] = (r->d[6] & mask0) | (a->d[6] & mask1);
+ r->d[7] = (r->d[7] & mask0) | (a->d[7] & mask1);
+}
+
#endif /* SECP256K1_SCALAR_REPR_IMPL_H */
diff --git a/src/secp256k1/src/scalar_impl.h b/src/secp256k1/src/scalar_impl.h
index fa790570ff..70cd73db06 100644
--- a/src/secp256k1/src/scalar_impl.h
+++ b/src/secp256k1/src/scalar_impl.h
@@ -7,8 +7,8 @@
#ifndef SECP256K1_SCALAR_IMPL_H
#define SECP256K1_SCALAR_IMPL_H
-#include "group.h"
#include "scalar.h"
+#include "util.h"
#if defined HAVE_CONFIG_H
#include "libsecp256k1-config.h"
@@ -24,6 +24,9 @@
#error "Please select scalar implementation"
#endif
+static const secp256k1_scalar secp256k1_scalar_one = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 1);
+static const secp256k1_scalar secp256k1_scalar_zero = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0);
+
#ifndef USE_NUM_NONE
static void secp256k1_scalar_get_num(secp256k1_num *r, const secp256k1_scalar *a) {
unsigned char c[32];
@@ -52,6 +55,12 @@ static void secp256k1_scalar_order_get_num(secp256k1_num *r) {
}
#endif
+static int secp256k1_scalar_set_b32_seckey(secp256k1_scalar *r, const unsigned char *bin) {
+ int overflow;
+ secp256k1_scalar_set_b32(r, bin, &overflow);
+ return (!overflow) & (!secp256k1_scalar_is_zero(r));
+}
+
static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar *x) {
#if defined(EXHAUSTIVE_TEST_ORDER)
int i;
diff --git a/src/secp256k1/src/scalar_low.h b/src/secp256k1/src/scalar_low.h
index 5836febc5b..2794a7f171 100644
--- a/src/secp256k1/src/scalar_low.h
+++ b/src/secp256k1/src/scalar_low.h
@@ -12,4 +12,6 @@
/** A scalar modulo the group order of the secp256k1 curve. */
typedef uint32_t secp256k1_scalar;
+#define SECP256K1_SCALAR_CONST(d7, d6, d5, d4, d3, d2, d1, d0) (d0)
+
#endif /* SECP256K1_SCALAR_REPR_H */
diff --git a/src/secp256k1/src/scalar_low_impl.h b/src/secp256k1/src/scalar_low_impl.h
index c80e70c5a2..b79cf1ff6c 100644
--- a/src/secp256k1/src/scalar_low_impl.h
+++ b/src/secp256k1/src/scalar_low_impl.h
@@ -38,8 +38,11 @@ static int secp256k1_scalar_add(secp256k1_scalar *r, const secp256k1_scalar *a,
static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int flag) {
if (flag && bit < 32)
- *r += (1 << bit);
+ *r += ((uint32_t)1 << bit);
#ifdef VERIFY
+ VERIFY_CHECK(bit < 32);
+ /* Verify that adding (1 << bit) will not overflow any in-range scalar *r by overflowing the underlying uint32_t. */
+ VERIFY_CHECK(((uint32_t)1 << bit) - 1 <= UINT32_MAX - EXHAUSTIVE_TEST_ORDER);
VERIFY_CHECK(secp256k1_scalar_check_overflow(r) == 0);
#endif
}
@@ -111,4 +114,12 @@ SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const
return *a == *b;
}
+static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const secp256k1_scalar *a, int flag) {
+ uint32_t mask0, mask1;
+ VG_CHECK_VERIFY(r, sizeof(*r));
+ mask0 = flag + ~((uint32_t)0);
+ mask1 = ~mask0;
+ *r = (*r & mask0) | (*a & mask1);
+}
+
#endif /* SECP256K1_SCALAR_REPR_IMPL_H */
diff --git a/src/secp256k1/src/scratch.h b/src/secp256k1/src/scratch.h
index fef377af0d..77b35d126b 100644
--- a/src/secp256k1/src/scratch.h
+++ b/src/secp256k1/src/scratch.h
@@ -7,33 +7,36 @@
#ifndef _SECP256K1_SCRATCH_
#define _SECP256K1_SCRATCH_
-#define SECP256K1_SCRATCH_MAX_FRAMES 5
-
/* The typedef is used internally; the struct name is used in the public API
* (where it is exposed as a different typedef) */
typedef struct secp256k1_scratch_space_struct {
- void *data[SECP256K1_SCRATCH_MAX_FRAMES];
- size_t offset[SECP256K1_SCRATCH_MAX_FRAMES];
- size_t frame_size[SECP256K1_SCRATCH_MAX_FRAMES];
- size_t frame;
+ /** guard against interpreting this object as other types */
+ unsigned char magic[8];
+ /** actual allocated data */
+ void *data;
+ /** amount that has been allocated (i.e. `data + offset` is the next
+ * available pointer) */
+ size_t alloc_size;
+ /** maximum size available to allocate */
size_t max_size;
- const secp256k1_callback* error_callback;
} secp256k1_scratch;
static secp256k1_scratch* secp256k1_scratch_create(const secp256k1_callback* error_callback, size_t max_size);
-static void secp256k1_scratch_destroy(secp256k1_scratch* scratch);
+static void secp256k1_scratch_destroy(const secp256k1_callback* error_callback, secp256k1_scratch* scratch);
-/** Attempts to allocate a new stack frame with `n` available bytes. Returns 1 on success, 0 on failure */
-static int secp256k1_scratch_allocate_frame(secp256k1_scratch* scratch, size_t n, size_t objects);
+/** Returns an opaque object used to "checkpoint" a scratch space. Used
+ * with `secp256k1_scratch_apply_checkpoint` to undo allocations. */
+static size_t secp256k1_scratch_checkpoint(const secp256k1_callback* error_callback, const secp256k1_scratch* scratch);
-/** Deallocates a stack frame */
-static void secp256k1_scratch_deallocate_frame(secp256k1_scratch* scratch);
+/** Applies a check point received from `secp256k1_scratch_checkpoint`,
+ * undoing all allocations since that point. */
+static void secp256k1_scratch_apply_checkpoint(const secp256k1_callback* error_callback, secp256k1_scratch* scratch, size_t checkpoint);
/** Returns the maximum allocation the scratch space will allow */
-static size_t secp256k1_scratch_max_allocation(const secp256k1_scratch* scratch, size_t n_objects);
+static size_t secp256k1_scratch_max_allocation(const secp256k1_callback* error_callback, const secp256k1_scratch* scratch, size_t n_objects);
/** Returns a pointer into the most recently allocated frame, or NULL if there is insufficient available space */
-static void *secp256k1_scratch_alloc(secp256k1_scratch* scratch, size_t n);
+static void *secp256k1_scratch_alloc(const secp256k1_callback* error_callback, secp256k1_scratch* scratch, size_t n);
#endif
diff --git a/src/secp256k1/src/scratch_impl.h b/src/secp256k1/src/scratch_impl.h
index abed713b21..4cee700001 100644
--- a/src/secp256k1/src/scratch_impl.h
+++ b/src/secp256k1/src/scratch_impl.h
@@ -7,78 +7,80 @@
#ifndef _SECP256K1_SCRATCH_IMPL_H_
#define _SECP256K1_SCRATCH_IMPL_H_
+#include "util.h"
#include "scratch.h"
-/* Using 16 bytes alignment because common architectures never have alignment
- * requirements above 8 for any of the types we care about. In addition we
- * leave some room because currently we don't care about a few bytes.
- * TODO: Determine this at configure time. */
-#define ALIGNMENT 16
-
-static secp256k1_scratch* secp256k1_scratch_create(const secp256k1_callback* error_callback, size_t max_size) {
- secp256k1_scratch* ret = (secp256k1_scratch*)checked_malloc(error_callback, sizeof(*ret));
+static secp256k1_scratch* secp256k1_scratch_create(const secp256k1_callback* error_callback, size_t size) {
+ const size_t base_alloc = ((sizeof(secp256k1_scratch) + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT;
+ void *alloc = checked_malloc(error_callback, base_alloc + size);
+ secp256k1_scratch* ret = (secp256k1_scratch *)alloc;
if (ret != NULL) {
memset(ret, 0, sizeof(*ret));
- ret->max_size = max_size;
- ret->error_callback = error_callback;
+ memcpy(ret->magic, "scratch", 8);
+ ret->data = (void *) ((char *) alloc + base_alloc);
+ ret->max_size = size;
}
return ret;
}
-static void secp256k1_scratch_destroy(secp256k1_scratch* scratch) {
+static void secp256k1_scratch_destroy(const secp256k1_callback* error_callback, secp256k1_scratch* scratch) {
if (scratch != NULL) {
- VERIFY_CHECK(scratch->frame == 0);
+ VERIFY_CHECK(scratch->alloc_size == 0); /* all checkpoints should be applied */
+ if (memcmp(scratch->magic, "scratch", 8) != 0) {
+ secp256k1_callback_call(error_callback, "invalid scratch space");
+ return;
+ }
+ memset(scratch->magic, 0, sizeof(scratch->magic));
free(scratch);
}
}
-static size_t secp256k1_scratch_max_allocation(const secp256k1_scratch* scratch, size_t objects) {
- size_t i = 0;
- size_t allocated = 0;
- for (i = 0; i < scratch->frame; i++) {
- allocated += scratch->frame_size[i];
- }
- if (scratch->max_size - allocated <= objects * ALIGNMENT) {
+static size_t secp256k1_scratch_checkpoint(const secp256k1_callback* error_callback, const secp256k1_scratch* scratch) {
+ if (memcmp(scratch->magic, "scratch", 8) != 0) {
+ secp256k1_callback_call(error_callback, "invalid scratch space");
return 0;
}
- return scratch->max_size - allocated - objects * ALIGNMENT;
+ return scratch->alloc_size;
}
-static int secp256k1_scratch_allocate_frame(secp256k1_scratch* scratch, size_t n, size_t objects) {
- VERIFY_CHECK(scratch->frame < SECP256K1_SCRATCH_MAX_FRAMES);
-
- if (n <= secp256k1_scratch_max_allocation(scratch, objects)) {
- n += objects * ALIGNMENT;
- scratch->data[scratch->frame] = checked_malloc(scratch->error_callback, n);
- if (scratch->data[scratch->frame] == NULL) {
- return 0;
- }
- scratch->frame_size[scratch->frame] = n;
- scratch->offset[scratch->frame] = 0;
- scratch->frame++;
- return 1;
- } else {
- return 0;
+static void secp256k1_scratch_apply_checkpoint(const secp256k1_callback* error_callback, secp256k1_scratch* scratch, size_t checkpoint) {
+ if (memcmp(scratch->magic, "scratch", 8) != 0) {
+ secp256k1_callback_call(error_callback, "invalid scratch space");
+ return;
+ }
+ if (checkpoint > scratch->alloc_size) {
+ secp256k1_callback_call(error_callback, "invalid checkpoint");
+ return;
}
+ scratch->alloc_size = checkpoint;
}
-static void secp256k1_scratch_deallocate_frame(secp256k1_scratch* scratch) {
- VERIFY_CHECK(scratch->frame > 0);
- scratch->frame -= 1;
- free(scratch->data[scratch->frame]);
+static size_t secp256k1_scratch_max_allocation(const secp256k1_callback* error_callback, const secp256k1_scratch* scratch, size_t objects) {
+ if (memcmp(scratch->magic, "scratch", 8) != 0) {
+ secp256k1_callback_call(error_callback, "invalid scratch space");
+ return 0;
+ }
+ if (scratch->max_size - scratch->alloc_size <= objects * (ALIGNMENT - 1)) {
+ return 0;
+ }
+ return scratch->max_size - scratch->alloc_size - objects * (ALIGNMENT - 1);
}
-static void *secp256k1_scratch_alloc(secp256k1_scratch* scratch, size_t size) {
+static void *secp256k1_scratch_alloc(const secp256k1_callback* error_callback, secp256k1_scratch* scratch, size_t size) {
void *ret;
- size_t frame = scratch->frame - 1;
- size = ((size + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT;
+ size = ROUND_TO_ALIGN(size);
+
+ if (memcmp(scratch->magic, "scratch", 8) != 0) {
+ secp256k1_callback_call(error_callback, "invalid scratch space");
+ return NULL;
+ }
- if (scratch->frame == 0 || size + scratch->offset[frame] > scratch->frame_size[frame]) {
+ if (size > scratch->max_size - scratch->alloc_size) {
return NULL;
}
- ret = (void *) ((unsigned char *) scratch->data[frame] + scratch->offset[frame]);
+ ret = (void *) ((char *) scratch->data + scratch->alloc_size);
memset(ret, 0, size);
- scratch->offset[frame] += size;
+ scratch->alloc_size += size;
return ret;
}
diff --git a/src/secp256k1/src/secp256k1.c b/src/secp256k1/src/secp256k1.c
index 15981f46e2..b03a6e6345 100644
--- a/src/secp256k1/src/secp256k1.c
+++ b/src/secp256k1/src/secp256k1.c
@@ -5,6 +5,7 @@
**********************************************************************/
#include "include/secp256k1.h"
+#include "include/secp256k1_preallocated.h"
#include "util.h"
#include "num_impl.h"
@@ -19,6 +20,10 @@
#include "hash_impl.h"
#include "scratch_impl.h"
+#if defined(VALGRIND)
+# include <valgrind/memcheck.h>
+#endif
+
#define ARG_CHECK(cond) do { \
if (EXPECT(!(cond), 0)) { \
secp256k1_callback_call(&ctx->illegal_callback, #cond); \
@@ -26,53 +31,101 @@
} \
} while(0)
-static void default_illegal_callback_fn(const char* str, void* data) {
+#define ARG_CHECK_NO_RETURN(cond) do { \
+ if (EXPECT(!(cond), 0)) { \
+ secp256k1_callback_call(&ctx->illegal_callback, #cond); \
+ } \
+} while(0)
+
+#ifndef USE_EXTERNAL_DEFAULT_CALLBACKS
+#include <stdlib.h>
+#include <stdio.h>
+static void secp256k1_default_illegal_callback_fn(const char* str, void* data) {
(void)data;
fprintf(stderr, "[libsecp256k1] illegal argument: %s\n", str);
abort();
}
-
-static const secp256k1_callback default_illegal_callback = {
- default_illegal_callback_fn,
- NULL
-};
-
-static void default_error_callback_fn(const char* str, void* data) {
+static void secp256k1_default_error_callback_fn(const char* str, void* data) {
(void)data;
fprintf(stderr, "[libsecp256k1] internal consistency check failed: %s\n", str);
abort();
}
+#else
+void secp256k1_default_illegal_callback_fn(const char* str, void* data);
+void secp256k1_default_error_callback_fn(const char* str, void* data);
+#endif
-static const secp256k1_callback default_error_callback = {
- default_error_callback_fn,
+static const secp256k1_callback default_illegal_callback = {
+ secp256k1_default_illegal_callback_fn,
NULL
};
+static const secp256k1_callback default_error_callback = {
+ secp256k1_default_error_callback_fn,
+ NULL
+};
struct secp256k1_context_struct {
secp256k1_ecmult_context ecmult_ctx;
secp256k1_ecmult_gen_context ecmult_gen_ctx;
secp256k1_callback illegal_callback;
secp256k1_callback error_callback;
+ int declassify;
};
static const secp256k1_context secp256k1_context_no_precomp_ = {
{ 0 },
{ 0 },
- { default_illegal_callback_fn, 0 },
- { default_error_callback_fn, 0 }
+ { secp256k1_default_illegal_callback_fn, 0 },
+ { secp256k1_default_error_callback_fn, 0 },
+ 0
};
const secp256k1_context *secp256k1_context_no_precomp = &secp256k1_context_no_precomp_;
-secp256k1_context* secp256k1_context_create(unsigned int flags) {
- secp256k1_context* ret = (secp256k1_context*)checked_malloc(&default_error_callback, sizeof(secp256k1_context));
+size_t secp256k1_context_preallocated_size(unsigned int flags) {
+ size_t ret = ROUND_TO_ALIGN(sizeof(secp256k1_context));
+
+ if (EXPECT((flags & SECP256K1_FLAGS_TYPE_MASK) != SECP256K1_FLAGS_TYPE_CONTEXT, 0)) {
+ secp256k1_callback_call(&default_illegal_callback,
+ "Invalid flags");
+ return 0;
+ }
+
+ if (flags & SECP256K1_FLAGS_BIT_CONTEXT_SIGN) {
+ ret += SECP256K1_ECMULT_GEN_CONTEXT_PREALLOCATED_SIZE;
+ }
+ if (flags & SECP256K1_FLAGS_BIT_CONTEXT_VERIFY) {
+ ret += SECP256K1_ECMULT_CONTEXT_PREALLOCATED_SIZE;
+ }
+ return ret;
+}
+
+size_t secp256k1_context_preallocated_clone_size(const secp256k1_context* ctx) {
+ size_t ret = ROUND_TO_ALIGN(sizeof(secp256k1_context));
+ VERIFY_CHECK(ctx != NULL);
+ if (secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)) {
+ ret += SECP256K1_ECMULT_GEN_CONTEXT_PREALLOCATED_SIZE;
+ }
+ if (secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)) {
+ ret += SECP256K1_ECMULT_CONTEXT_PREALLOCATED_SIZE;
+ }
+ return ret;
+}
+
+secp256k1_context* secp256k1_context_preallocated_create(void* prealloc, unsigned int flags) {
+ void* const base = prealloc;
+ size_t prealloc_size;
+ secp256k1_context* ret;
+
+ VERIFY_CHECK(prealloc != NULL);
+ prealloc_size = secp256k1_context_preallocated_size(flags);
+ ret = (secp256k1_context*)manual_alloc(&prealloc, sizeof(secp256k1_context), base, prealloc_size);
ret->illegal_callback = default_illegal_callback;
ret->error_callback = default_error_callback;
if (EXPECT((flags & SECP256K1_FLAGS_TYPE_MASK) != SECP256K1_FLAGS_TYPE_CONTEXT, 0)) {
secp256k1_callback_call(&ret->illegal_callback,
"Invalid flags");
- free(ret);
return NULL;
}
@@ -80,47 +133,80 @@ secp256k1_context* secp256k1_context_create(unsigned int flags) {
secp256k1_ecmult_gen_context_init(&ret->ecmult_gen_ctx);
if (flags & SECP256K1_FLAGS_BIT_CONTEXT_SIGN) {
- secp256k1_ecmult_gen_context_build(&ret->ecmult_gen_ctx, &ret->error_callback);
+ secp256k1_ecmult_gen_context_build(&ret->ecmult_gen_ctx, &prealloc);
}
if (flags & SECP256K1_FLAGS_BIT_CONTEXT_VERIFY) {
- secp256k1_ecmult_context_build(&ret->ecmult_ctx, &ret->error_callback);
+ secp256k1_ecmult_context_build(&ret->ecmult_ctx, &prealloc);
+ }
+ ret->declassify = !!(flags & SECP256K1_FLAGS_BIT_CONTEXT_DECLASSIFY);
+
+ return (secp256k1_context*) ret;
+}
+
+secp256k1_context* secp256k1_context_create(unsigned int flags) {
+ size_t const prealloc_size = secp256k1_context_preallocated_size(flags);
+ secp256k1_context* ctx = (secp256k1_context*)checked_malloc(&default_error_callback, prealloc_size);
+ if (EXPECT(secp256k1_context_preallocated_create(ctx, flags) == NULL, 0)) {
+ free(ctx);
+ return NULL;
}
+ return ctx;
+}
+
+secp256k1_context* secp256k1_context_preallocated_clone(const secp256k1_context* ctx, void* prealloc) {
+ size_t prealloc_size;
+ secp256k1_context* ret;
+ VERIFY_CHECK(ctx != NULL);
+ ARG_CHECK(prealloc != NULL);
+
+ prealloc_size = secp256k1_context_preallocated_clone_size(ctx);
+ ret = (secp256k1_context*)prealloc;
+ memcpy(ret, ctx, prealloc_size);
+ secp256k1_ecmult_gen_context_finalize_memcpy(&ret->ecmult_gen_ctx, &ctx->ecmult_gen_ctx);
+ secp256k1_ecmult_context_finalize_memcpy(&ret->ecmult_ctx, &ctx->ecmult_ctx);
return ret;
}
secp256k1_context* secp256k1_context_clone(const secp256k1_context* ctx) {
- secp256k1_context* ret = (secp256k1_context*)checked_malloc(&ctx->error_callback, sizeof(secp256k1_context));
- ret->illegal_callback = ctx->illegal_callback;
- ret->error_callback = ctx->error_callback;
- secp256k1_ecmult_context_clone(&ret->ecmult_ctx, &ctx->ecmult_ctx, &ctx->error_callback);
- secp256k1_ecmult_gen_context_clone(&ret->ecmult_gen_ctx, &ctx->ecmult_gen_ctx, &ctx->error_callback);
+ secp256k1_context* ret;
+ size_t prealloc_size;
+
+ VERIFY_CHECK(ctx != NULL);
+ prealloc_size = secp256k1_context_preallocated_clone_size(ctx);
+ ret = (secp256k1_context*)checked_malloc(&ctx->error_callback, prealloc_size);
+ ret = secp256k1_context_preallocated_clone(ctx, ret);
return ret;
}
-void secp256k1_context_destroy(secp256k1_context* ctx) {
- CHECK(ctx != secp256k1_context_no_precomp);
+void secp256k1_context_preallocated_destroy(secp256k1_context* ctx) {
+ ARG_CHECK_NO_RETURN(ctx != secp256k1_context_no_precomp);
if (ctx != NULL) {
secp256k1_ecmult_context_clear(&ctx->ecmult_ctx);
secp256k1_ecmult_gen_context_clear(&ctx->ecmult_gen_ctx);
+ }
+}
+void secp256k1_context_destroy(secp256k1_context* ctx) {
+ if (ctx != NULL) {
+ secp256k1_context_preallocated_destroy(ctx);
free(ctx);
}
}
void secp256k1_context_set_illegal_callback(secp256k1_context* ctx, void (*fun)(const char* message, void* data), const void* data) {
- CHECK(ctx != secp256k1_context_no_precomp);
+ ARG_CHECK_NO_RETURN(ctx != secp256k1_context_no_precomp);
if (fun == NULL) {
- fun = default_illegal_callback_fn;
+ fun = secp256k1_default_illegal_callback_fn;
}
ctx->illegal_callback.fn = fun;
ctx->illegal_callback.data = data;
}
void secp256k1_context_set_error_callback(secp256k1_context* ctx, void (*fun)(const char* message, void* data), const void* data) {
- CHECK(ctx != secp256k1_context_no_precomp);
+ ARG_CHECK_NO_RETURN(ctx != secp256k1_context_no_precomp);
if (fun == NULL) {
- fun = default_error_callback_fn;
+ fun = secp256k1_default_error_callback_fn;
}
ctx->error_callback.fn = fun;
ctx->error_callback.data = data;
@@ -131,8 +217,23 @@ secp256k1_scratch_space* secp256k1_scratch_space_create(const secp256k1_context*
return secp256k1_scratch_create(&ctx->error_callback, max_size);
}
-void secp256k1_scratch_space_destroy(secp256k1_scratch_space* scratch) {
- secp256k1_scratch_destroy(scratch);
+void secp256k1_scratch_space_destroy(const secp256k1_context *ctx, secp256k1_scratch_space* scratch) {
+ VERIFY_CHECK(ctx != NULL);
+ secp256k1_scratch_destroy(&ctx->error_callback, scratch);
+}
+
+/* Mark memory as no-longer-secret for the purpose of analysing constant-time behaviour
+ * of the software. This is setup for use with valgrind but could be substituted with
+ * the appropriate instrumentation for other analysis tools.
+ */
+static SECP256K1_INLINE void secp256k1_declassify(const secp256k1_context* ctx, void *p, size_t len) {
+#if defined(VALGRIND)
+ if (EXPECT(ctx->declassify,0)) VALGRIND_MAKE_MEM_DEFINED(p, len);
+#else
+ (void)ctx;
+ (void)p;
+ (void)len;
+#endif
}
static int secp256k1_pubkey_load(const secp256k1_context* ctx, secp256k1_ge* ge, const secp256k1_pubkey* pubkey) {
@@ -366,61 +467,83 @@ static int nonce_function_rfc6979(unsigned char *nonce32, const unsigned char *m
const secp256k1_nonce_function secp256k1_nonce_function_rfc6979 = nonce_function_rfc6979;
const secp256k1_nonce_function secp256k1_nonce_function_default = nonce_function_rfc6979;
-int secp256k1_ecdsa_sign(const secp256k1_context* ctx, secp256k1_ecdsa_signature *signature, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void* noncedata) {
- secp256k1_scalar r, s;
+static int secp256k1_ecdsa_sign_inner(const secp256k1_context* ctx, secp256k1_scalar* r, secp256k1_scalar* s, int* recid, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void* noncedata) {
secp256k1_scalar sec, non, msg;
int ret = 0;
- int overflow = 0;
- VERIFY_CHECK(ctx != NULL);
- ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
- ARG_CHECK(msg32 != NULL);
- ARG_CHECK(signature != NULL);
- ARG_CHECK(seckey != NULL);
+ int is_sec_valid;
+ unsigned char nonce32[32];
+ unsigned int count = 0;
+ /* Default initialization here is important so we won't pass uninit values to the cmov in the end */
+ *r = secp256k1_scalar_zero;
+ *s = secp256k1_scalar_zero;
+ if (recid) {
+ *recid = 0;
+ }
if (noncefp == NULL) {
noncefp = secp256k1_nonce_function_default;
}
- secp256k1_scalar_set_b32(&sec, seckey, &overflow);
/* Fail if the secret key is invalid. */
- if (!overflow && !secp256k1_scalar_is_zero(&sec)) {
- unsigned char nonce32[32];
- unsigned int count = 0;
- secp256k1_scalar_set_b32(&msg, msg32, NULL);
- while (1) {
- ret = noncefp(nonce32, msg32, seckey, NULL, (void*)noncedata, count);
- if (!ret) {
+ is_sec_valid = secp256k1_scalar_set_b32_seckey(&sec, seckey);
+ secp256k1_scalar_cmov(&sec, &secp256k1_scalar_one, !is_sec_valid);
+ secp256k1_scalar_set_b32(&msg, msg32, NULL);
+ while (1) {
+ int is_nonce_valid;
+ ret = !!noncefp(nonce32, msg32, seckey, NULL, (void*)noncedata, count);
+ if (!ret) {
+ break;
+ }
+ is_nonce_valid = secp256k1_scalar_set_b32_seckey(&non, nonce32);
+ /* The nonce is still secret here, but it being invalid is is less likely than 1:2^255. */
+ secp256k1_declassify(ctx, &is_nonce_valid, sizeof(is_nonce_valid));
+ if (is_nonce_valid) {
+ ret = secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, r, s, &sec, &msg, &non, recid);
+ /* The final signature is no longer a secret, nor is the fact that we were successful or not. */
+ secp256k1_declassify(ctx, &ret, sizeof(ret));
+ if (ret) {
break;
}
- secp256k1_scalar_set_b32(&non, nonce32, &overflow);
- if (!overflow && !secp256k1_scalar_is_zero(&non)) {
- if (secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, &r, &s, &sec, &msg, &non, NULL)) {
- break;
- }
- }
- count++;
}
- memset(nonce32, 0, 32);
- secp256k1_scalar_clear(&msg);
- secp256k1_scalar_clear(&non);
- secp256k1_scalar_clear(&sec);
+ count++;
}
- if (ret) {
- secp256k1_ecdsa_signature_save(signature, &r, &s);
- } else {
- memset(signature, 0, sizeof(*signature));
+ /* We don't want to declassify is_sec_valid and therefore the range of
+ * seckey. As a result is_sec_valid is included in ret only after ret was
+ * used as a branching variable. */
+ ret &= is_sec_valid;
+ memset(nonce32, 0, 32);
+ secp256k1_scalar_clear(&msg);
+ secp256k1_scalar_clear(&non);
+ secp256k1_scalar_clear(&sec);
+ secp256k1_scalar_cmov(r, &secp256k1_scalar_zero, !ret);
+ secp256k1_scalar_cmov(s, &secp256k1_scalar_zero, !ret);
+ if (recid) {
+ const int zero = 0;
+ secp256k1_int_cmov(recid, &zero, !ret);
}
return ret;
}
+int secp256k1_ecdsa_sign(const secp256k1_context* ctx, secp256k1_ecdsa_signature *signature, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void* noncedata) {
+ secp256k1_scalar r, s;
+ int ret;
+ VERIFY_CHECK(ctx != NULL);
+ ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
+ ARG_CHECK(msg32 != NULL);
+ ARG_CHECK(signature != NULL);
+ ARG_CHECK(seckey != NULL);
+
+ ret = secp256k1_ecdsa_sign_inner(ctx, &r, &s, NULL, msg32, seckey, noncefp, noncedata);
+ secp256k1_ecdsa_signature_save(signature, &r, &s);
+ return ret;
+}
+
int secp256k1_ec_seckey_verify(const secp256k1_context* ctx, const unsigned char *seckey) {
secp256k1_scalar sec;
int ret;
- int overflow;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(seckey != NULL);
- secp256k1_scalar_set_b32(&sec, seckey, &overflow);
- ret = !overflow && !secp256k1_scalar_is_zero(&sec);
+ ret = secp256k1_scalar_set_b32_seckey(&sec, seckey);
secp256k1_scalar_clear(&sec);
return ret;
}
@@ -429,7 +552,6 @@ int secp256k1_ec_pubkey_create(const secp256k1_context* ctx, secp256k1_pubkey *p
secp256k1_gej pj;
secp256k1_ge p;
secp256k1_scalar sec;
- int overflow;
int ret = 0;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(pubkey != NULL);
@@ -437,27 +559,35 @@ int secp256k1_ec_pubkey_create(const secp256k1_context* ctx, secp256k1_pubkey *p
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
ARG_CHECK(seckey != NULL);
- secp256k1_scalar_set_b32(&sec, seckey, &overflow);
- ret = (!overflow) & (!secp256k1_scalar_is_zero(&sec));
- if (ret) {
- secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pj, &sec);
- secp256k1_ge_set_gej(&p, &pj);
- secp256k1_pubkey_save(pubkey, &p);
- }
+ ret = secp256k1_scalar_set_b32_seckey(&sec, seckey);
+ secp256k1_scalar_cmov(&sec, &secp256k1_scalar_one, !ret);
+
+ secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pj, &sec);
+ secp256k1_ge_set_gej(&p, &pj);
+ secp256k1_pubkey_save(pubkey, &p);
+ memczero(pubkey, sizeof(*pubkey), !ret);
+
secp256k1_scalar_clear(&sec);
return ret;
}
-int secp256k1_ec_privkey_negate(const secp256k1_context* ctx, unsigned char *seckey) {
+int secp256k1_ec_seckey_negate(const secp256k1_context* ctx, unsigned char *seckey) {
secp256k1_scalar sec;
+ int ret = 0;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(seckey != NULL);
- secp256k1_scalar_set_b32(&sec, seckey, NULL);
+ ret = secp256k1_scalar_set_b32_seckey(&sec, seckey);
+ secp256k1_scalar_cmov(&sec, &secp256k1_scalar_zero, !ret);
secp256k1_scalar_negate(&sec, &sec);
secp256k1_scalar_get_b32(seckey, &sec);
- return 1;
+ secp256k1_scalar_clear(&sec);
+ return ret;
+}
+
+int secp256k1_ec_privkey_negate(const secp256k1_context* ctx, unsigned char *seckey) {
+ return secp256k1_ec_seckey_negate(ctx, seckey);
}
int secp256k1_ec_pubkey_negate(const secp256k1_context* ctx, secp256k1_pubkey *pubkey) {
@@ -475,7 +605,7 @@ int secp256k1_ec_pubkey_negate(const secp256k1_context* ctx, secp256k1_pubkey *p
return ret;
}
-int secp256k1_ec_privkey_tweak_add(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak) {
+int secp256k1_ec_seckey_tweak_add(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak) {
secp256k1_scalar term;
secp256k1_scalar sec;
int ret = 0;
@@ -485,19 +615,21 @@ int secp256k1_ec_privkey_tweak_add(const secp256k1_context* ctx, unsigned char *
ARG_CHECK(tweak != NULL);
secp256k1_scalar_set_b32(&term, tweak, &overflow);
- secp256k1_scalar_set_b32(&sec, seckey, NULL);
+ ret = secp256k1_scalar_set_b32_seckey(&sec, seckey);
- ret = !overflow && secp256k1_eckey_privkey_tweak_add(&sec, &term);
- memset(seckey, 0, 32);
- if (ret) {
- secp256k1_scalar_get_b32(seckey, &sec);
- }
+ ret &= (!overflow) & secp256k1_eckey_privkey_tweak_add(&sec, &term);
+ secp256k1_scalar_cmov(&sec, &secp256k1_scalar_zero, !ret);
+ secp256k1_scalar_get_b32(seckey, &sec);
secp256k1_scalar_clear(&sec);
secp256k1_scalar_clear(&term);
return ret;
}
+int secp256k1_ec_privkey_tweak_add(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak) {
+ return secp256k1_ec_seckey_tweak_add(ctx, seckey, tweak);
+}
+
int secp256k1_ec_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *tweak) {
secp256k1_ge p;
secp256k1_scalar term;
@@ -522,7 +654,7 @@ int secp256k1_ec_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_pubkey
return ret;
}
-int secp256k1_ec_privkey_tweak_mul(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak) {
+int secp256k1_ec_seckey_tweak_mul(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak) {
secp256k1_scalar factor;
secp256k1_scalar sec;
int ret = 0;
@@ -532,18 +664,20 @@ int secp256k1_ec_privkey_tweak_mul(const secp256k1_context* ctx, unsigned char *
ARG_CHECK(tweak != NULL);
secp256k1_scalar_set_b32(&factor, tweak, &overflow);
- secp256k1_scalar_set_b32(&sec, seckey, NULL);
- ret = !overflow && secp256k1_eckey_privkey_tweak_mul(&sec, &factor);
- memset(seckey, 0, 32);
- if (ret) {
- secp256k1_scalar_get_b32(seckey, &sec);
- }
+ ret = secp256k1_scalar_set_b32_seckey(&sec, seckey);
+ ret &= (!overflow) & secp256k1_eckey_privkey_tweak_mul(&sec, &factor);
+ secp256k1_scalar_cmov(&sec, &secp256k1_scalar_zero, !ret);
+ secp256k1_scalar_get_b32(seckey, &sec);
secp256k1_scalar_clear(&sec);
secp256k1_scalar_clear(&factor);
return ret;
}
+int secp256k1_ec_privkey_tweak_mul(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak) {
+ return secp256k1_ec_seckey_tweak_mul(ctx, seckey, tweak);
+}
+
int secp256k1_ec_pubkey_tweak_mul(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *tweak) {
secp256k1_ge p;
secp256k1_scalar factor;
diff --git a/src/secp256k1/src/tests.c b/src/secp256k1/src/tests.c
index f1c4db929a..374ed7dc12 100644
--- a/src/secp256k1/src/tests.c
+++ b/src/secp256k1/src/tests.c
@@ -16,6 +16,7 @@
#include "secp256k1.c"
#include "include/secp256k1.h"
+#include "include/secp256k1_preallocated.h"
#include "testrand_impl.h"
#ifdef ENABLE_OPENSSL_TESTS
@@ -31,17 +32,6 @@ void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps)
#include "contrib/lax_der_parsing.c"
#include "contrib/lax_der_privatekey_parsing.c"
-#if !defined(VG_CHECK)
-# if defined(VALGRIND)
-# include <valgrind/memcheck.h>
-# define VG_UNDEF(x,y) VALGRIND_MAKE_MEM_UNDEFINED((x),(y))
-# define VG_CHECK(x,y) VALGRIND_CHECK_MEM_IS_DEFINED((x),(y))
-# else
-# define VG_UNDEF(x,y)
-# define VG_CHECK(x,y)
-# endif
-#endif
-
static int count = 64;
static secp256k1_context *ctx = NULL;
@@ -82,7 +72,9 @@ void random_field_element_magnitude(secp256k1_fe *fe) {
secp256k1_fe_negate(&zero, &zero, 0);
secp256k1_fe_mul_int(&zero, n - 1);
secp256k1_fe_add(fe, &zero);
- VERIFY_CHECK(fe->magnitude == n);
+#ifdef VERIFY
+ CHECK(fe->magnitude == n);
+#endif
}
void random_group_element_test(secp256k1_ge *ge) {
@@ -137,23 +129,53 @@ void random_scalar_order(secp256k1_scalar *num) {
} while(1);
}
-void run_context_tests(void) {
+void random_scalar_order_b32(unsigned char *b32) {
+ secp256k1_scalar num;
+ random_scalar_order(&num);
+ secp256k1_scalar_get_b32(b32, &num);
+}
+
+void run_context_tests(int use_prealloc) {
secp256k1_pubkey pubkey;
secp256k1_pubkey zero_pubkey;
secp256k1_ecdsa_signature sig;
unsigned char ctmp[32];
int32_t ecount;
int32_t ecount2;
- secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
- secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
- secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY);
- secp256k1_context *both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
+ secp256k1_context *none;
+ secp256k1_context *sign;
+ secp256k1_context *vrfy;
+ secp256k1_context *both;
+ void *none_prealloc = NULL;
+ void *sign_prealloc = NULL;
+ void *vrfy_prealloc = NULL;
+ void *both_prealloc = NULL;
secp256k1_gej pubj;
secp256k1_ge pub;
secp256k1_scalar msg, key, nonce;
secp256k1_scalar sigr, sigs;
+ if (use_prealloc) {
+ none_prealloc = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_NONE));
+ sign_prealloc = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_SIGN));
+ vrfy_prealloc = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_VERIFY));
+ both_prealloc = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY));
+ CHECK(none_prealloc != NULL);
+ CHECK(sign_prealloc != NULL);
+ CHECK(vrfy_prealloc != NULL);
+ CHECK(both_prealloc != NULL);
+ none = secp256k1_context_preallocated_create(none_prealloc, SECP256K1_CONTEXT_NONE);
+ sign = secp256k1_context_preallocated_create(sign_prealloc, SECP256K1_CONTEXT_SIGN);
+ vrfy = secp256k1_context_preallocated_create(vrfy_prealloc, SECP256K1_CONTEXT_VERIFY);
+ both = secp256k1_context_preallocated_create(both_prealloc, SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
+ } else {
+ none = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
+ sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
+ vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY);
+ both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
+ }
+
memset(&zero_pubkey, 0, sizeof(zero_pubkey));
ecount = 0;
@@ -163,14 +185,57 @@ void run_context_tests(void) {
secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, NULL);
CHECK(vrfy->error_callback.fn != sign->error_callback.fn);
+ /* check if sizes for cloning are consistent */
+ CHECK(secp256k1_context_preallocated_clone_size(none) == secp256k1_context_preallocated_size(SECP256K1_CONTEXT_NONE));
+ CHECK(secp256k1_context_preallocated_clone_size(sign) == secp256k1_context_preallocated_size(SECP256K1_CONTEXT_SIGN));
+ CHECK(secp256k1_context_preallocated_clone_size(vrfy) == secp256k1_context_preallocated_size(SECP256K1_CONTEXT_VERIFY));
+ CHECK(secp256k1_context_preallocated_clone_size(both) == secp256k1_context_preallocated_size(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY));
+
/*** clone and destroy all of them to make sure cloning was complete ***/
{
secp256k1_context *ctx_tmp;
- ctx_tmp = none; none = secp256k1_context_clone(none); secp256k1_context_destroy(ctx_tmp);
- ctx_tmp = sign; sign = secp256k1_context_clone(sign); secp256k1_context_destroy(ctx_tmp);
- ctx_tmp = vrfy; vrfy = secp256k1_context_clone(vrfy); secp256k1_context_destroy(ctx_tmp);
- ctx_tmp = both; both = secp256k1_context_clone(both); secp256k1_context_destroy(ctx_tmp);
+ if (use_prealloc) {
+ /* clone into a non-preallocated context and then again into a new preallocated one. */
+ ctx_tmp = none; none = secp256k1_context_clone(none); secp256k1_context_preallocated_destroy(ctx_tmp);
+ free(none_prealloc); none_prealloc = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_NONE)); CHECK(none_prealloc != NULL);
+ ctx_tmp = none; none = secp256k1_context_preallocated_clone(none, none_prealloc); secp256k1_context_destroy(ctx_tmp);
+
+ ctx_tmp = sign; sign = secp256k1_context_clone(sign); secp256k1_context_preallocated_destroy(ctx_tmp);
+ free(sign_prealloc); sign_prealloc = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_SIGN)); CHECK(sign_prealloc != NULL);
+ ctx_tmp = sign; sign = secp256k1_context_preallocated_clone(sign, sign_prealloc); secp256k1_context_destroy(ctx_tmp);
+
+ ctx_tmp = vrfy; vrfy = secp256k1_context_clone(vrfy); secp256k1_context_preallocated_destroy(ctx_tmp);
+ free(vrfy_prealloc); vrfy_prealloc = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_VERIFY)); CHECK(vrfy_prealloc != NULL);
+ ctx_tmp = vrfy; vrfy = secp256k1_context_preallocated_clone(vrfy, vrfy_prealloc); secp256k1_context_destroy(ctx_tmp);
+
+ ctx_tmp = both; both = secp256k1_context_clone(both); secp256k1_context_preallocated_destroy(ctx_tmp);
+ free(both_prealloc); both_prealloc = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY)); CHECK(both_prealloc != NULL);
+ ctx_tmp = both; both = secp256k1_context_preallocated_clone(both, both_prealloc); secp256k1_context_destroy(ctx_tmp);
+ } else {
+ /* clone into a preallocated context and then again into a new non-preallocated one. */
+ void *prealloc_tmp;
+
+ prealloc_tmp = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_NONE)); CHECK(prealloc_tmp != NULL);
+ ctx_tmp = none; none = secp256k1_context_preallocated_clone(none, prealloc_tmp); secp256k1_context_destroy(ctx_tmp);
+ ctx_tmp = none; none = secp256k1_context_clone(none); secp256k1_context_preallocated_destroy(ctx_tmp);
+ free(prealloc_tmp);
+
+ prealloc_tmp = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_SIGN)); CHECK(prealloc_tmp != NULL);
+ ctx_tmp = sign; sign = secp256k1_context_preallocated_clone(sign, prealloc_tmp); secp256k1_context_destroy(ctx_tmp);
+ ctx_tmp = sign; sign = secp256k1_context_clone(sign); secp256k1_context_preallocated_destroy(ctx_tmp);
+ free(prealloc_tmp);
+
+ prealloc_tmp = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_VERIFY)); CHECK(prealloc_tmp != NULL);
+ ctx_tmp = vrfy; vrfy = secp256k1_context_preallocated_clone(vrfy, prealloc_tmp); secp256k1_context_destroy(ctx_tmp);
+ ctx_tmp = vrfy; vrfy = secp256k1_context_clone(vrfy); secp256k1_context_preallocated_destroy(ctx_tmp);
+ free(prealloc_tmp);
+
+ prealloc_tmp = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY)); CHECK(prealloc_tmp != NULL);
+ ctx_tmp = both; both = secp256k1_context_preallocated_clone(both, prealloc_tmp); secp256k1_context_destroy(ctx_tmp);
+ ctx_tmp = both; both = secp256k1_context_clone(both); secp256k1_context_preallocated_destroy(ctx_tmp);
+ free(prealloc_tmp);
+ }
}
/* Verify that the error callback makes it across the clone. */
@@ -229,10 +294,6 @@ void run_context_tests(void) {
secp256k1_context_set_illegal_callback(vrfy, NULL, NULL);
secp256k1_context_set_illegal_callback(sign, NULL, NULL);
- /* This shouldn't leak memory, due to already-set tests. */
- secp256k1_ecmult_gen_context_build(&sign->ecmult_gen_ctx, NULL);
- secp256k1_ecmult_context_build(&vrfy->ecmult_ctx, NULL);
-
/* obtain a working nonce */
do {
random_scalar_order_test(&nonce);
@@ -247,49 +308,95 @@ void run_context_tests(void) {
CHECK(secp256k1_ecdsa_sig_verify(&both->ecmult_ctx, &sigr, &sigs, &pub, &msg));
/* cleanup */
- secp256k1_context_destroy(none);
- secp256k1_context_destroy(sign);
- secp256k1_context_destroy(vrfy);
- secp256k1_context_destroy(both);
+ if (use_prealloc) {
+ secp256k1_context_preallocated_destroy(none);
+ secp256k1_context_preallocated_destroy(sign);
+ secp256k1_context_preallocated_destroy(vrfy);
+ secp256k1_context_preallocated_destroy(both);
+ free(none_prealloc);
+ free(sign_prealloc);
+ free(vrfy_prealloc);
+ free(both_prealloc);
+ } else {
+ secp256k1_context_destroy(none);
+ secp256k1_context_destroy(sign);
+ secp256k1_context_destroy(vrfy);
+ secp256k1_context_destroy(both);
+ }
/* Defined as no-op. */
secp256k1_context_destroy(NULL);
+ secp256k1_context_preallocated_destroy(NULL);
+
}
void run_scratch_tests(void) {
+ const size_t adj_alloc = ((500 + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT;
+
int32_t ecount = 0;
+ size_t checkpoint;
+ size_t checkpoint_2;
secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
secp256k1_scratch_space *scratch;
+ secp256k1_scratch_space local_scratch;
/* Test public API */
secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount);
+ secp256k1_context_set_error_callback(none, counting_illegal_callback_fn, &ecount);
scratch = secp256k1_scratch_space_create(none, 1000);
CHECK(scratch != NULL);
CHECK(ecount == 0);
/* Test internal API */
- CHECK(secp256k1_scratch_max_allocation(scratch, 0) == 1000);
- CHECK(secp256k1_scratch_max_allocation(scratch, 1) < 1000);
-
- /* Allocating 500 bytes with no frame fails */
- CHECK(secp256k1_scratch_alloc(scratch, 500) == NULL);
- CHECK(secp256k1_scratch_max_allocation(scratch, 0) == 1000);
-
- /* ...but pushing a new stack frame does affect the max allocation */
- CHECK(secp256k1_scratch_allocate_frame(scratch, 500, 1 == 1));
- CHECK(secp256k1_scratch_max_allocation(scratch, 1) < 500); /* 500 - ALIGNMENT */
- CHECK(secp256k1_scratch_alloc(scratch, 500) != NULL);
- CHECK(secp256k1_scratch_alloc(scratch, 500) == NULL);
-
- CHECK(secp256k1_scratch_allocate_frame(scratch, 500, 1) == 0);
+ CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 0) == 1000);
+ CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 1) == 1000 - (ALIGNMENT - 1));
+ CHECK(scratch->alloc_size == 0);
+ CHECK(scratch->alloc_size % ALIGNMENT == 0);
+
+ /* Allocating 500 bytes succeeds */
+ checkpoint = secp256k1_scratch_checkpoint(&none->error_callback, scratch);
+ CHECK(secp256k1_scratch_alloc(&none->error_callback, scratch, 500) != NULL);
+ CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 0) == 1000 - adj_alloc);
+ CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 1) == 1000 - adj_alloc - (ALIGNMENT - 1));
+ CHECK(scratch->alloc_size != 0);
+ CHECK(scratch->alloc_size % ALIGNMENT == 0);
+
+ /* Allocating another 500 bytes fails */
+ CHECK(secp256k1_scratch_alloc(&none->error_callback, scratch, 500) == NULL);
+ CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 0) == 1000 - adj_alloc);
+ CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 1) == 1000 - adj_alloc - (ALIGNMENT - 1));
+ CHECK(scratch->alloc_size != 0);
+ CHECK(scratch->alloc_size % ALIGNMENT == 0);
+
+ /* ...but it succeeds once we apply the checkpoint to undo it */
+ secp256k1_scratch_apply_checkpoint(&none->error_callback, scratch, checkpoint);
+ CHECK(scratch->alloc_size == 0);
+ CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 0) == 1000);
+ CHECK(secp256k1_scratch_alloc(&none->error_callback, scratch, 500) != NULL);
+ CHECK(scratch->alloc_size != 0);
+
+ /* try to apply a bad checkpoint */
+ checkpoint_2 = secp256k1_scratch_checkpoint(&none->error_callback, scratch);
+ secp256k1_scratch_apply_checkpoint(&none->error_callback, scratch, checkpoint);
+ CHECK(ecount == 0);
+ secp256k1_scratch_apply_checkpoint(&none->error_callback, scratch, checkpoint_2); /* checkpoint_2 is after checkpoint */
+ CHECK(ecount == 1);
+ secp256k1_scratch_apply_checkpoint(&none->error_callback, scratch, (size_t) -1); /* this is just wildly invalid */
+ CHECK(ecount == 2);
- /* ...and this effect is undone by popping the frame */
- secp256k1_scratch_deallocate_frame(scratch);
- CHECK(secp256k1_scratch_max_allocation(scratch, 0) == 1000);
- CHECK(secp256k1_scratch_alloc(scratch, 500) == NULL);
+ /* try to use badly initialized scratch space */
+ secp256k1_scratch_space_destroy(none, scratch);
+ memset(&local_scratch, 0, sizeof(local_scratch));
+ scratch = &local_scratch;
+ CHECK(!secp256k1_scratch_max_allocation(&none->error_callback, scratch, 0));
+ CHECK(ecount == 3);
+ CHECK(secp256k1_scratch_alloc(&none->error_callback, scratch, 500) == NULL);
+ CHECK(ecount == 4);
+ secp256k1_scratch_space_destroy(none, scratch);
+ CHECK(ecount == 5);
/* cleanup */
- secp256k1_scratch_space_destroy(scratch);
+ secp256k1_scratch_space_destroy(none, NULL); /* no-op */
secp256k1_context_destroy(none);
}
@@ -965,11 +1072,31 @@ void scalar_test(void) {
}
+void run_scalar_set_b32_seckey_tests(void) {
+ unsigned char b32[32];
+ secp256k1_scalar s1;
+ secp256k1_scalar s2;
+
+ /* Usually set_b32 and set_b32_seckey give the same result */
+ random_scalar_order_b32(b32);
+ secp256k1_scalar_set_b32(&s1, b32, NULL);
+ CHECK(secp256k1_scalar_set_b32_seckey(&s2, b32) == 1);
+ CHECK(secp256k1_scalar_eq(&s1, &s2) == 1);
+
+ memset(b32, 0, sizeof(b32));
+ CHECK(secp256k1_scalar_set_b32_seckey(&s2, b32) == 0);
+ memset(b32, 0xFF, sizeof(b32));
+ CHECK(secp256k1_scalar_set_b32_seckey(&s2, b32) == 0);
+}
+
void run_scalar_tests(void) {
int i;
for (i = 0; i < 128 * count; i++) {
scalar_test();
}
+ for (i = 0; i < count; i++) {
+ run_scalar_set_b32_seckey_tests();
+ }
{
/* (-1)+1 should be zero. */
@@ -985,16 +1112,43 @@ void run_scalar_tests(void) {
#ifndef USE_NUM_NONE
{
- /* A scalar with value of the curve order should be 0. */
+ /* Test secp256k1_scalar_set_b32 boundary conditions */
secp256k1_num order;
- secp256k1_scalar zero;
+ secp256k1_scalar scalar;
unsigned char bin[32];
+ unsigned char bin_tmp[32];
int overflow = 0;
+ /* 2^256-1 - order */
+ static const secp256k1_scalar all_ones_minus_order = SECP256K1_SCALAR_CONST(
+ 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x00000001UL,
+ 0x45512319UL, 0x50B75FC4UL, 0x402DA173UL, 0x2FC9BEBEUL
+ );
+
+ /* A scalar set to 0s should be 0. */
+ memset(bin, 0, 32);
+ secp256k1_scalar_set_b32(&scalar, bin, &overflow);
+ CHECK(overflow == 0);
+ CHECK(secp256k1_scalar_is_zero(&scalar));
+
+ /* A scalar with value of the curve order should be 0. */
secp256k1_scalar_order_get_num(&order);
secp256k1_num_get_bin(bin, 32, &order);
- secp256k1_scalar_set_b32(&zero, bin, &overflow);
+ secp256k1_scalar_set_b32(&scalar, bin, &overflow);
+ CHECK(overflow == 1);
+ CHECK(secp256k1_scalar_is_zero(&scalar));
+
+ /* A scalar with value of the curve order minus one should not overflow. */
+ bin[31] -= 1;
+ secp256k1_scalar_set_b32(&scalar, bin, &overflow);
+ CHECK(overflow == 0);
+ secp256k1_scalar_get_b32(bin_tmp, &scalar);
+ CHECK(memcmp(bin, bin_tmp, 32) == 0);
+
+ /* A scalar set to all 1s should overflow. */
+ memset(bin, 0xFF, 32);
+ secp256k1_scalar_set_b32(&scalar, bin, &overflow);
CHECK(overflow == 1);
- CHECK(secp256k1_scalar_is_zero(&zero));
+ CHECK(secp256k1_scalar_eq(&scalar, &all_ones_minus_order));
}
#endif
@@ -1709,24 +1863,32 @@ void run_field_misc(void) {
/* Test fe conditional move; z is not normalized here. */
q = x;
secp256k1_fe_cmov(&x, &z, 0);
- VERIFY_CHECK(!x.normalized && x.magnitude == z.magnitude);
+#ifdef VERIFY
+ CHECK(x.normalized && x.magnitude == 1);
+#endif
secp256k1_fe_cmov(&x, &x, 1);
CHECK(fe_memcmp(&x, &z) != 0);
CHECK(fe_memcmp(&x, &q) == 0);
secp256k1_fe_cmov(&q, &z, 1);
- VERIFY_CHECK(!q.normalized && q.magnitude == z.magnitude);
+#ifdef VERIFY
+ CHECK(!q.normalized && q.magnitude == z.magnitude);
+#endif
CHECK(fe_memcmp(&q, &z) == 0);
secp256k1_fe_normalize_var(&x);
secp256k1_fe_normalize_var(&z);
CHECK(!secp256k1_fe_equal_var(&x, &z));
secp256k1_fe_normalize_var(&q);
secp256k1_fe_cmov(&q, &z, (i&1));
- VERIFY_CHECK(q.normalized && q.magnitude == 1);
+#ifdef VERIFY
+ CHECK(q.normalized && q.magnitude == 1);
+#endif
for (j = 0; j < 6; j++) {
secp256k1_fe_negate(&z, &z, j+1);
secp256k1_fe_normalize_var(&q);
secp256k1_fe_cmov(&q, &z, (j&1));
- VERIFY_CHECK(!q.normalized && q.magnitude == (j+2));
+#ifdef VERIFY
+ CHECK((q.normalized != (j&1)) && q.magnitude == ((j&1) ? z.magnitude : 1));
+#endif
}
secp256k1_fe_normalize_var(&z);
/* Test storage conversion and conditional moves. */
@@ -2120,7 +2282,7 @@ void test_ge(void) {
/* Test batch gej -> ge conversion with many infinities. */
for (i = 0; i < 4 * runs + 1; i++) {
random_group_element_test(&ge[i]);
- /* randomly set half the points to infinitiy */
+ /* randomly set half the points to infinity */
if(secp256k1_fe_is_odd(&ge[i].x)) {
secp256k1_ge_set_infinity(&ge[i]);
}
@@ -2572,14 +2734,13 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e
secp256k1_gej r;
secp256k1_gej r2;
ecmult_multi_data data;
- secp256k1_scratch *scratch_empty;
data.sc = sc;
data.pt = pt;
secp256k1_scalar_set_int(&szero, 0);
/* No points to multiply */
- CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, NULL, ecmult_multi_callback, &data, 0));
+ CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, NULL, ecmult_multi_callback, &data, 0));
/* Check 1- and 2-point multiplies against ecmult */
for (ncount = 0; ncount < count; ncount++) {
@@ -2595,36 +2756,31 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e
/* only G scalar */
secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &ptgj, &szero, &sc[0]);
- CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &sc[0], ecmult_multi_callback, &data, 0));
+ CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &sc[0], ecmult_multi_callback, &data, 0));
secp256k1_gej_neg(&r2, &r2);
secp256k1_gej_add_var(&r, &r, &r2, NULL);
CHECK(secp256k1_gej_is_infinity(&r));
/* 1-point */
secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &ptgj, &sc[0], &szero);
- CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 1));
+ CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 1));
secp256k1_gej_neg(&r2, &r2);
secp256k1_gej_add_var(&r, &r, &r2, NULL);
CHECK(secp256k1_gej_is_infinity(&r));
- /* Try to multiply 1 point, but scratch space is empty */
- scratch_empty = secp256k1_scratch_create(&ctx->error_callback, 0);
- CHECK(!ecmult_multi(&ctx->ecmult_ctx, scratch_empty, &r, &szero, ecmult_multi_callback, &data, 1));
- secp256k1_scratch_destroy(scratch_empty);
-
/* Try to multiply 1 point, but callback returns false */
- CHECK(!ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_false_callback, &data, 1));
+ CHECK(!ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_false_callback, &data, 1));
/* 2-point */
secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &ptgj, &sc[0], &sc[1]);
- CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 2));
+ CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 2));
secp256k1_gej_neg(&r2, &r2);
secp256k1_gej_add_var(&r, &r, &r2, NULL);
CHECK(secp256k1_gej_is_infinity(&r));
/* 2-point with G scalar */
secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &ptgj, &sc[0], &sc[1]);
- CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &sc[1], ecmult_multi_callback, &data, 1));
+ CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &sc[1], ecmult_multi_callback, &data, 1));
secp256k1_gej_neg(&r2, &r2);
secp256k1_gej_add_var(&r, &r, &r2, NULL);
CHECK(secp256k1_gej_is_infinity(&r));
@@ -2641,7 +2797,7 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e
random_scalar_order(&sc[i]);
secp256k1_ge_set_infinity(&pt[i]);
}
- CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j]));
+ CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j]));
CHECK(secp256k1_gej_is_infinity(&r));
}
@@ -2651,7 +2807,7 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e
pt[i] = ptg;
secp256k1_scalar_set_int(&sc[i], 0);
}
- CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j]));
+ CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j]));
CHECK(secp256k1_gej_is_infinity(&r));
}
@@ -2664,7 +2820,7 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e
pt[2 * i + 1] = ptg;
}
- CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j]));
+ CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j]));
CHECK(secp256k1_gej_is_infinity(&r));
random_scalar_order(&sc[0]);
@@ -2677,7 +2833,7 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e
secp256k1_ge_neg(&pt[2*i+1], &pt[2*i]);
}
- CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j]));
+ CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j]));
CHECK(secp256k1_gej_is_infinity(&r));
}
@@ -2692,7 +2848,7 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e
secp256k1_scalar_negate(&sc[i], &sc[i]);
}
- CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 32));
+ CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 32));
CHECK(secp256k1_gej_is_infinity(&r));
}
@@ -2711,7 +2867,7 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e
}
secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &r, &sc[0], &szero);
- CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 20));
+ CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 20));
secp256k1_gej_neg(&r2, &r2);
secp256k1_gej_add_var(&r, &r, &r2, NULL);
CHECK(secp256k1_gej_is_infinity(&r));
@@ -2734,7 +2890,7 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e
secp256k1_gej_set_ge(&p0j, &pt[0]);
secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &p0j, &rs, &szero);
- CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 20));
+ CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 20));
secp256k1_gej_neg(&r2, &r2);
secp256k1_gej_add_var(&r, &r, &r2, NULL);
CHECK(secp256k1_gej_is_infinity(&r));
@@ -2747,13 +2903,13 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e
}
secp256k1_scalar_clear(&sc[0]);
- CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 20));
+ CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 20));
secp256k1_scalar_clear(&sc[1]);
secp256k1_scalar_clear(&sc[2]);
secp256k1_scalar_clear(&sc[3]);
secp256k1_scalar_clear(&sc[4]);
- CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 6));
- CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 5));
+ CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 6));
+ CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 5));
CHECK(secp256k1_gej_is_infinity(&r));
/* Run through s0*(t0*P) + s1*(t1*P) exhaustively for many small values of s0, s1, t0, t1 */
@@ -2798,7 +2954,7 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e
secp256k1_scalar_add(&tmp1, &tmp1, &tmp2);
secp256k1_ecmult(&ctx->ecmult_ctx, &expected, &ptgj, &tmp1, &szero);
- CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &actual, &szero, ecmult_multi_callback, &data, 2));
+ CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &actual, &szero, ecmult_multi_callback, &data, 2));
secp256k1_gej_neg(&expected, &expected);
secp256k1_gej_add_var(&actual, &actual, &expected, NULL);
CHECK(secp256k1_gej_is_infinity(&actual));
@@ -2809,6 +2965,24 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e
}
}
+void test_ecmult_multi_batch_single(secp256k1_ecmult_multi_func ecmult_multi) {
+ secp256k1_scalar szero;
+ secp256k1_scalar sc[32];
+ secp256k1_ge pt[32];
+ secp256k1_gej r;
+ ecmult_multi_data data;
+ secp256k1_scratch *scratch_empty;
+
+ data.sc = sc;
+ data.pt = pt;
+ secp256k1_scalar_set_int(&szero, 0);
+
+ /* Try to multiply 1 point, but scratch space is empty.*/
+ scratch_empty = secp256k1_scratch_create(&ctx->error_callback, 0);
+ CHECK(!ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch_empty, &r, &szero, ecmult_multi_callback, &data, 1));
+ secp256k1_scratch_destroy(&ctx->error_callback, scratch_empty);
+}
+
void test_secp256k1_pippenger_bucket_window_inv(void) {
int i;
@@ -2839,17 +3013,27 @@ void test_ecmult_multi_pippenger_max_points(void) {
int bucket_window = 0;
for(; scratch_size < max_size; scratch_size+=256) {
+ size_t i;
+ size_t total_alloc;
+ size_t checkpoint;
scratch = secp256k1_scratch_create(&ctx->error_callback, scratch_size);
CHECK(scratch != NULL);
- n_points_supported = secp256k1_pippenger_max_points(scratch);
+ checkpoint = secp256k1_scratch_checkpoint(&ctx->error_callback, scratch);
+ n_points_supported = secp256k1_pippenger_max_points(&ctx->error_callback, scratch);
if (n_points_supported == 0) {
- secp256k1_scratch_destroy(scratch);
+ secp256k1_scratch_destroy(&ctx->error_callback, scratch);
continue;
}
bucket_window = secp256k1_pippenger_bucket_window(n_points_supported);
- CHECK(secp256k1_scratch_allocate_frame(scratch, secp256k1_pippenger_scratch_size(n_points_supported, bucket_window), PIPPENGER_SCRATCH_OBJECTS));
- secp256k1_scratch_deallocate_frame(scratch);
- secp256k1_scratch_destroy(scratch);
+ /* allocate `total_alloc` bytes over `PIPPENGER_SCRATCH_OBJECTS` many allocations */
+ total_alloc = secp256k1_pippenger_scratch_size(n_points_supported, bucket_window);
+ for (i = 0; i < PIPPENGER_SCRATCH_OBJECTS - 1; i++) {
+ CHECK(secp256k1_scratch_alloc(&ctx->error_callback, scratch, 1));
+ total_alloc--;
+ }
+ CHECK(secp256k1_scratch_alloc(&ctx->error_callback, scratch, total_alloc));
+ secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, checkpoint);
+ secp256k1_scratch_destroy(&ctx->error_callback, scratch);
}
CHECK(bucket_window == PIPPENGER_MAX_BUCKET_WINDOW);
}
@@ -2932,19 +3116,25 @@ void test_ecmult_multi_batching(void) {
}
data.sc = sc;
data.pt = pt;
+ secp256k1_gej_neg(&r2, &r2);
- /* Test with empty scratch space */
+ /* Test with empty scratch space. It should compute the correct result using
+ * ecmult_mult_simple algorithm which doesn't require a scratch space. */
scratch = secp256k1_scratch_create(&ctx->error_callback, 0);
- CHECK(!secp256k1_ecmult_multi_var(&ctx->ecmult_ctx, scratch, &r, &scG, ecmult_multi_callback, &data, 1));
- secp256k1_scratch_destroy(scratch);
+ CHECK(secp256k1_ecmult_multi_var(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &scG, ecmult_multi_callback, &data, n_points));
+ secp256k1_gej_add_var(&r, &r, &r2, NULL);
+ CHECK(secp256k1_gej_is_infinity(&r));
+ secp256k1_scratch_destroy(&ctx->error_callback, scratch);
/* Test with space for 1 point in pippenger. That's not enough because
- * ecmult_multi selects strauss which requires more memory. */
+ * ecmult_multi selects strauss which requires more memory. It should
+ * therefore select the simple algorithm. */
scratch = secp256k1_scratch_create(&ctx->error_callback, secp256k1_pippenger_scratch_size(1, 1) + PIPPENGER_SCRATCH_OBJECTS*ALIGNMENT);
- CHECK(!secp256k1_ecmult_multi_var(&ctx->ecmult_ctx, scratch, &r, &scG, ecmult_multi_callback, &data, 1));
- secp256k1_scratch_destroy(scratch);
+ CHECK(secp256k1_ecmult_multi_var(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &scG, ecmult_multi_callback, &data, n_points));
+ secp256k1_gej_add_var(&r, &r, &r2, NULL);
+ CHECK(secp256k1_gej_is_infinity(&r));
+ secp256k1_scratch_destroy(&ctx->error_callback, scratch);
- secp256k1_gej_neg(&r2, &r2);
for(i = 1; i <= n_points; i++) {
if (i > ECMULT_PIPPENGER_THRESHOLD) {
int bucket_window = secp256k1_pippenger_bucket_window(i);
@@ -2954,10 +3144,10 @@ void test_ecmult_multi_batching(void) {
size_t scratch_size = secp256k1_strauss_scratch_size(i);
scratch = secp256k1_scratch_create(&ctx->error_callback, scratch_size + STRAUSS_SCRATCH_OBJECTS*ALIGNMENT);
}
- CHECK(secp256k1_ecmult_multi_var(&ctx->ecmult_ctx, scratch, &r, &scG, ecmult_multi_callback, &data, n_points));
+ CHECK(secp256k1_ecmult_multi_var(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &scG, ecmult_multi_callback, &data, n_points));
secp256k1_gej_add_var(&r, &r, &r2, NULL);
CHECK(secp256k1_gej_is_infinity(&r));
- secp256k1_scratch_destroy(scratch);
+ secp256k1_scratch_destroy(&ctx->error_callback, scratch);
}
free(sc);
free(pt);
@@ -2972,13 +3162,15 @@ void run_ecmult_multi_tests(void) {
test_ecmult_multi(scratch, secp256k1_ecmult_multi_var);
test_ecmult_multi(NULL, secp256k1_ecmult_multi_var);
test_ecmult_multi(scratch, secp256k1_ecmult_pippenger_batch_single);
+ test_ecmult_multi_batch_single(secp256k1_ecmult_pippenger_batch_single);
test_ecmult_multi(scratch, secp256k1_ecmult_strauss_batch_single);
- secp256k1_scratch_destroy(scratch);
+ test_ecmult_multi_batch_single(secp256k1_ecmult_strauss_batch_single);
+ secp256k1_scratch_destroy(&ctx->error_callback, scratch);
/* Run test_ecmult_multi with space for exactly one point */
scratch = secp256k1_scratch_create(&ctx->error_callback, secp256k1_strauss_scratch_size(1) + STRAUSS_SCRATCH_OBJECTS*ALIGNMENT);
test_ecmult_multi(scratch, secp256k1_ecmult_multi_var);
- secp256k1_scratch_destroy(scratch);
+ secp256k1_scratch_destroy(&ctx->error_callback, scratch);
test_ecmult_multi_batch_size_helper();
test_ecmult_multi_batching();
@@ -3050,7 +3242,7 @@ void test_constant_wnaf(const secp256k1_scalar *number, int w) {
}
bits = 128;
#endif
- skew = secp256k1_wnaf_const(wnaf, num, w, bits);
+ skew = secp256k1_wnaf_const(wnaf, &num, w, bits);
for (i = WNAF_SIZE_BITS(bits, w); i >= 0; --i) {
secp256k1_scalar t;
@@ -3786,37 +3978,57 @@ void run_eckey_edge_case_test(void) {
pubkey_negone = pubkey;
/* Tweak of zero leaves the value unchanged. */
memset(ctmp2, 0, 32);
- CHECK(secp256k1_ec_privkey_tweak_add(ctx, ctmp, ctmp2) == 1);
+ CHECK(secp256k1_ec_seckey_tweak_add(ctx, ctmp, ctmp2) == 1);
CHECK(memcmp(orderc, ctmp, 31) == 0 && ctmp[31] == 0x40);
memcpy(&pubkey2, &pubkey, sizeof(pubkey));
CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, ctmp2) == 1);
CHECK(memcmp(&pubkey, &pubkey2, sizeof(pubkey)) == 0);
/* Multiply tweak of zero zeroizes the output. */
- CHECK(secp256k1_ec_privkey_tweak_mul(ctx, ctmp, ctmp2) == 0);
+ CHECK(secp256k1_ec_seckey_tweak_mul(ctx, ctmp, ctmp2) == 0);
CHECK(memcmp(zeros, ctmp, 32) == 0);
CHECK(secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, ctmp2) == 0);
CHECK(memcmp(&pubkey, zeros, sizeof(pubkey)) == 0);
memcpy(&pubkey, &pubkey2, sizeof(pubkey));
- /* Overflowing key tweak zeroizes. */
+ /* If seckey_tweak_add or seckey_tweak_mul are called with an overflowing
+ seckey, the seckey is zeroized. */
+ memcpy(ctmp, orderc, 32);
+ memset(ctmp2, 0, 32);
+ ctmp2[31] = 0x01;
+ CHECK(secp256k1_ec_seckey_verify(ctx, ctmp2) == 1);
+ CHECK(secp256k1_ec_seckey_verify(ctx, ctmp) == 0);
+ CHECK(secp256k1_ec_seckey_tweak_add(ctx, ctmp, ctmp2) == 0);
+ CHECK(memcmp(zeros, ctmp, 32) == 0);
+ memcpy(ctmp, orderc, 32);
+ CHECK(secp256k1_ec_seckey_tweak_mul(ctx, ctmp, ctmp2) == 0);
+ CHECK(memcmp(zeros, ctmp, 32) == 0);
+ /* If seckey_tweak_add or seckey_tweak_mul are called with an overflowing
+ tweak, the seckey is zeroized. */
memcpy(ctmp, orderc, 32);
ctmp[31] = 0x40;
- CHECK(secp256k1_ec_privkey_tweak_add(ctx, ctmp, orderc) == 0);
+ CHECK(secp256k1_ec_seckey_tweak_add(ctx, ctmp, orderc) == 0);
CHECK(memcmp(zeros, ctmp, 32) == 0);
memcpy(ctmp, orderc, 32);
ctmp[31] = 0x40;
- CHECK(secp256k1_ec_privkey_tweak_mul(ctx, ctmp, orderc) == 0);
+ CHECK(secp256k1_ec_seckey_tweak_mul(ctx, ctmp, orderc) == 0);
CHECK(memcmp(zeros, ctmp, 32) == 0);
memcpy(ctmp, orderc, 32);
ctmp[31] = 0x40;
+ /* If pubkey_tweak_add or pubkey_tweak_mul are called with an overflowing
+ tweak, the pubkey is zeroized. */
CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, orderc) == 0);
CHECK(memcmp(&pubkey, zeros, sizeof(pubkey)) == 0);
memcpy(&pubkey, &pubkey2, sizeof(pubkey));
CHECK(secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, orderc) == 0);
CHECK(memcmp(&pubkey, zeros, sizeof(pubkey)) == 0);
memcpy(&pubkey, &pubkey2, sizeof(pubkey));
- /* Private key tweaks results in a key of zero. */
+ /* If the resulting key in secp256k1_ec_seckey_tweak_add and
+ * secp256k1_ec_pubkey_tweak_add is 0 the functions fail and in the latter
+ * case the pubkey is zeroized. */
+ memcpy(ctmp, orderc, 32);
+ ctmp[31] = 0x40;
+ memset(ctmp2, 0, 32);
ctmp2[31] = 1;
- CHECK(secp256k1_ec_privkey_tweak_add(ctx, ctmp2, ctmp) == 0);
+ CHECK(secp256k1_ec_seckey_tweak_add(ctx, ctmp2, ctmp) == 0);
CHECK(memcmp(zeros, ctmp2, 32) == 0);
ctmp2[31] = 1;
CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, ctmp2) == 0);
@@ -3824,7 +4036,7 @@ void run_eckey_edge_case_test(void) {
memcpy(&pubkey, &pubkey2, sizeof(pubkey));
/* Tweak computation wraps and results in a key of 1. */
ctmp2[31] = 2;
- CHECK(secp256k1_ec_privkey_tweak_add(ctx, ctmp2, ctmp) == 1);
+ CHECK(secp256k1_ec_seckey_tweak_add(ctx, ctmp2, ctmp) == 1);
CHECK(memcmp(ctmp2, zeros, 31) == 0 && ctmp2[31] == 1);
ctmp2[31] = 2;
CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, ctmp2) == 1);
@@ -3872,16 +4084,16 @@ void run_eckey_edge_case_test(void) {
CHECK(ecount == 2);
ecount = 0;
memset(ctmp2, 0, 32);
- CHECK(secp256k1_ec_privkey_tweak_add(ctx, NULL, ctmp2) == 0);
+ CHECK(secp256k1_ec_seckey_tweak_add(ctx, NULL, ctmp2) == 0);
CHECK(ecount == 1);
- CHECK(secp256k1_ec_privkey_tweak_add(ctx, ctmp, NULL) == 0);
+ CHECK(secp256k1_ec_seckey_tweak_add(ctx, ctmp, NULL) == 0);
CHECK(ecount == 2);
ecount = 0;
memset(ctmp2, 0, 32);
ctmp2[31] = 1;
- CHECK(secp256k1_ec_privkey_tweak_mul(ctx, NULL, ctmp2) == 0);
+ CHECK(secp256k1_ec_seckey_tweak_mul(ctx, NULL, ctmp2) == 0);
CHECK(ecount == 1);
- CHECK(secp256k1_ec_privkey_tweak_mul(ctx, ctmp, NULL) == 0);
+ CHECK(secp256k1_ec_seckey_tweak_mul(ctx, ctmp, NULL) == 0);
CHECK(ecount == 2);
ecount = 0;
CHECK(secp256k1_ec_pubkey_create(ctx, NULL, ctmp) == 0);
@@ -3954,6 +4166,41 @@ void run_eckey_edge_case_test(void) {
secp256k1_context_set_illegal_callback(ctx, NULL, NULL);
}
+void run_eckey_negate_test(void) {
+ unsigned char seckey[32];
+ unsigned char seckey_tmp[32];
+
+ random_scalar_order_b32(seckey);
+ memcpy(seckey_tmp, seckey, 32);
+
+ /* Verify negation changes the key and changes it back */
+ CHECK(secp256k1_ec_seckey_negate(ctx, seckey) == 1);
+ CHECK(memcmp(seckey, seckey_tmp, 32) != 0);
+ CHECK(secp256k1_ec_seckey_negate(ctx, seckey) == 1);
+ CHECK(memcmp(seckey, seckey_tmp, 32) == 0);
+
+ /* Check that privkey alias gives same result */
+ CHECK(secp256k1_ec_seckey_negate(ctx, seckey) == 1);
+ CHECK(secp256k1_ec_privkey_negate(ctx, seckey_tmp) == 1);
+ CHECK(memcmp(seckey, seckey_tmp, 32) == 0);
+
+ /* Negating all 0s fails */
+ memset(seckey, 0, 32);
+ memset(seckey_tmp, 0, 32);
+ CHECK(secp256k1_ec_seckey_negate(ctx, seckey) == 0);
+ /* Check that seckey is not modified */
+ CHECK(memcmp(seckey, seckey_tmp, 32) == 0);
+
+ /* Negating an overflowing seckey fails and the seckey is zeroed. In this
+ * test, the seckey has 16 random bytes to ensure that ec_seckey_negate
+ * doesn't just set seckey to a constant value in case of failure. */
+ random_scalar_order_b32(seckey);
+ memset(seckey, 0xFF, 16);
+ memset(seckey_tmp, 0, 32);
+ CHECK(secp256k1_ec_seckey_negate(ctx, seckey) == 0);
+ CHECK(memcmp(seckey, seckey_tmp, 32) == 0);
+}
+
void random_sign(secp256k1_scalar *sigr, secp256k1_scalar *sigs, const secp256k1_scalar *key, const secp256k1_scalar *msg, int *recid) {
secp256k1_scalar nonce;
do {
@@ -4093,15 +4340,22 @@ void test_ecdsa_end_to_end(void) {
if (secp256k1_rand_int(3) == 0) {
int ret1;
int ret2;
+ int ret3;
unsigned char rnd[32];
+ unsigned char privkey_tmp[32];
secp256k1_pubkey pubkey2;
secp256k1_rand256_test(rnd);
- ret1 = secp256k1_ec_privkey_tweak_add(ctx, privkey, rnd);
+ memcpy(privkey_tmp, privkey, 32);
+ ret1 = secp256k1_ec_seckey_tweak_add(ctx, privkey, rnd);
ret2 = secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, rnd);
+ /* Check that privkey alias gives same result */
+ ret3 = secp256k1_ec_privkey_tweak_add(ctx, privkey_tmp, rnd);
CHECK(ret1 == ret2);
+ CHECK(ret2 == ret3);
if (ret1 == 0) {
return;
}
+ CHECK(memcmp(privkey, privkey_tmp, 32) == 0);
CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey2, privkey) == 1);
CHECK(memcmp(&pubkey, &pubkey2, sizeof(pubkey)) == 0);
}
@@ -4110,15 +4364,22 @@ void test_ecdsa_end_to_end(void) {
if (secp256k1_rand_int(3) == 0) {
int ret1;
int ret2;
+ int ret3;
unsigned char rnd[32];
+ unsigned char privkey_tmp[32];
secp256k1_pubkey pubkey2;
secp256k1_rand256_test(rnd);
- ret1 = secp256k1_ec_privkey_tweak_mul(ctx, privkey, rnd);
+ memcpy(privkey_tmp, privkey, 32);
+ ret1 = secp256k1_ec_seckey_tweak_mul(ctx, privkey, rnd);
ret2 = secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, rnd);
+ /* Check that privkey alias gives same result */
+ ret3 = secp256k1_ec_privkey_tweak_mul(ctx, privkey_tmp, rnd);
CHECK(ret1 == ret2);
+ CHECK(ret2 == ret3);
if (ret1 == 0) {
return;
}
+ CHECK(memcmp(privkey, privkey_tmp, 32) == 0);
CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey2, privkey) == 1);
CHECK(memcmp(&pubkey, &pubkey2, sizeof(pubkey)) == 0);
}
@@ -4315,7 +4576,7 @@ int test_ecdsa_der_parse(const unsigned char *sig, size_t siglen, int certainly_
if (valid_der) {
ret |= (!roundtrips_der_lax) << 12;
ret |= (len_der != len_der_lax) << 13;
- ret |= (memcmp(roundtrip_der_lax, roundtrip_der, len_der) != 0) << 14;
+ ret |= ((len_der != len_der_lax) || (memcmp(roundtrip_der_lax, roundtrip_der, len_der) != 0)) << 14;
}
ret |= (roundtrips_der != roundtrips_der_lax) << 15;
if (parsed_der) {
@@ -4356,7 +4617,7 @@ int test_ecdsa_der_parse(const unsigned char *sig, size_t siglen, int certainly_
ret |= (roundtrips_der != roundtrips_openssl) << 7;
if (roundtrips_openssl) {
ret |= (len_der != (size_t)len_openssl) << 8;
- ret |= (memcmp(roundtrip_der, roundtrip_openssl, len_der) != 0) << 9;
+ ret |= ((len_der != (size_t)len_openssl) || (memcmp(roundtrip_der, roundtrip_openssl, len_der) != 0)) << 9;
}
#endif
return ret;
@@ -5016,9 +5277,188 @@ void run_ecdsa_openssl(void) {
# include "modules/recovery/tests_impl.h"
#endif
+void run_memczero_test(void) {
+ unsigned char buf1[6] = {1, 2, 3, 4, 5, 6};
+ unsigned char buf2[sizeof(buf1)];
+
+ /* memczero(..., ..., 0) is a noop. */
+ memcpy(buf2, buf1, sizeof(buf1));
+ memczero(buf1, sizeof(buf1), 0);
+ CHECK(memcmp(buf1, buf2, sizeof(buf1)) == 0);
+
+ /* memczero(..., ..., 1) zeros the buffer. */
+ memset(buf2, 0, sizeof(buf2));
+ memczero(buf1, sizeof(buf1) , 1);
+ CHECK(memcmp(buf1, buf2, sizeof(buf1)) == 0);
+}
+
+void int_cmov_test(void) {
+ int r = INT_MAX;
+ int a = 0;
+
+ secp256k1_int_cmov(&r, &a, 0);
+ CHECK(r == INT_MAX);
+
+ r = 0; a = INT_MAX;
+ secp256k1_int_cmov(&r, &a, 1);
+ CHECK(r == INT_MAX);
+
+ a = 0;
+ secp256k1_int_cmov(&r, &a, 1);
+ CHECK(r == 0);
+
+ a = 1;
+ secp256k1_int_cmov(&r, &a, 1);
+ CHECK(r == 1);
+
+ r = 1; a = 0;
+ secp256k1_int_cmov(&r, &a, 0);
+ CHECK(r == 1);
+
+}
+
+void fe_cmov_test(void) {
+ static const secp256k1_fe zero = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0);
+ static const secp256k1_fe one = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1);
+ static const secp256k1_fe max = SECP256K1_FE_CONST(
+ 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL,
+ 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL
+ );
+ secp256k1_fe r = max;
+ secp256k1_fe a = zero;
+
+ secp256k1_fe_cmov(&r, &a, 0);
+ CHECK(memcmp(&r, &max, sizeof(r)) == 0);
+
+ r = zero; a = max;
+ secp256k1_fe_cmov(&r, &a, 1);
+ CHECK(memcmp(&r, &max, sizeof(r)) == 0);
+
+ a = zero;
+ secp256k1_fe_cmov(&r, &a, 1);
+ CHECK(memcmp(&r, &zero, sizeof(r)) == 0);
+
+ a = one;
+ secp256k1_fe_cmov(&r, &a, 1);
+ CHECK(memcmp(&r, &one, sizeof(r)) == 0);
+
+ r = one; a = zero;
+ secp256k1_fe_cmov(&r, &a, 0);
+ CHECK(memcmp(&r, &one, sizeof(r)) == 0);
+}
+
+void fe_storage_cmov_test(void) {
+ static const secp256k1_fe_storage zero = SECP256K1_FE_STORAGE_CONST(0, 0, 0, 0, 0, 0, 0, 0);
+ static const secp256k1_fe_storage one = SECP256K1_FE_STORAGE_CONST(0, 0, 0, 0, 0, 0, 0, 1);
+ static const secp256k1_fe_storage max = SECP256K1_FE_STORAGE_CONST(
+ 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL,
+ 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL
+ );
+ secp256k1_fe_storage r = max;
+ secp256k1_fe_storage a = zero;
+
+ secp256k1_fe_storage_cmov(&r, &a, 0);
+ CHECK(memcmp(&r, &max, sizeof(r)) == 0);
+
+ r = zero; a = max;
+ secp256k1_fe_storage_cmov(&r, &a, 1);
+ CHECK(memcmp(&r, &max, sizeof(r)) == 0);
+
+ a = zero;
+ secp256k1_fe_storage_cmov(&r, &a, 1);
+ CHECK(memcmp(&r, &zero, sizeof(r)) == 0);
+
+ a = one;
+ secp256k1_fe_storage_cmov(&r, &a, 1);
+ CHECK(memcmp(&r, &one, sizeof(r)) == 0);
+
+ r = one; a = zero;
+ secp256k1_fe_storage_cmov(&r, &a, 0);
+ CHECK(memcmp(&r, &one, sizeof(r)) == 0);
+}
+
+void scalar_cmov_test(void) {
+ static const secp256k1_scalar zero = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0);
+ static const secp256k1_scalar one = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 1);
+ static const secp256k1_scalar max = SECP256K1_SCALAR_CONST(
+ 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL,
+ 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL
+ );
+ secp256k1_scalar r = max;
+ secp256k1_scalar a = zero;
+
+ secp256k1_scalar_cmov(&r, &a, 0);
+ CHECK(memcmp(&r, &max, sizeof(r)) == 0);
+
+ r = zero; a = max;
+ secp256k1_scalar_cmov(&r, &a, 1);
+ CHECK(memcmp(&r, &max, sizeof(r)) == 0);
+
+ a = zero;
+ secp256k1_scalar_cmov(&r, &a, 1);
+ CHECK(memcmp(&r, &zero, sizeof(r)) == 0);
+
+ a = one;
+ secp256k1_scalar_cmov(&r, &a, 1);
+ CHECK(memcmp(&r, &one, sizeof(r)) == 0);
+
+ r = one; a = zero;
+ secp256k1_scalar_cmov(&r, &a, 0);
+ CHECK(memcmp(&r, &one, sizeof(r)) == 0);
+}
+
+void ge_storage_cmov_test(void) {
+ static const secp256k1_ge_storage zero = SECP256K1_GE_STORAGE_CONST(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+ static const secp256k1_ge_storage one = SECP256K1_GE_STORAGE_CONST(0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1);
+ static const secp256k1_ge_storage max = SECP256K1_GE_STORAGE_CONST(
+ 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL,
+ 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL,
+ 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL,
+ 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL
+ );
+ secp256k1_ge_storage r = max;
+ secp256k1_ge_storage a = zero;
+
+ secp256k1_ge_storage_cmov(&r, &a, 0);
+ CHECK(memcmp(&r, &max, sizeof(r)) == 0);
+
+ r = zero; a = max;
+ secp256k1_ge_storage_cmov(&r, &a, 1);
+ CHECK(memcmp(&r, &max, sizeof(r)) == 0);
+
+ a = zero;
+ secp256k1_ge_storage_cmov(&r, &a, 1);
+ CHECK(memcmp(&r, &zero, sizeof(r)) == 0);
+
+ a = one;
+ secp256k1_ge_storage_cmov(&r, &a, 1);
+ CHECK(memcmp(&r, &one, sizeof(r)) == 0);
+
+ r = one; a = zero;
+ secp256k1_ge_storage_cmov(&r, &a, 0);
+ CHECK(memcmp(&r, &one, sizeof(r)) == 0);
+}
+
+void run_cmov_tests(void) {
+ int_cmov_test();
+ fe_cmov_test();
+ fe_storage_cmov_test();
+ scalar_cmov_test();
+ ge_storage_cmov_test();
+}
+
int main(int argc, char **argv) {
unsigned char seed16[16] = {0};
unsigned char run32[32] = {0};
+
+ /* Disable buffering for stdout to improve reliability of getting
+ * diagnostic information. Happens right at the start of main because
+ * setbuf must be used before any other operation on the stream. */
+ setbuf(stdout, NULL);
+ /* Also disable buffering for stderr because it's not guaranteed that it's
+ * unbuffered on all systems. */
+ setbuf(stderr, NULL);
+
/* find iteration count */
if (argc > 1) {
count = strtol(argv[1], NULL, 0);
@@ -5030,7 +5470,7 @@ int main(int argc, char **argv) {
const char* ch = argv[2];
while (pos < 16 && ch[0] != 0 && ch[1] != 0) {
unsigned short sh;
- if (sscanf(ch, "%2hx", &sh)) {
+ if ((sscanf(ch, "%2hx", &sh)) == 1) {
seed16[pos] = sh;
} else {
break;
@@ -5062,7 +5502,8 @@ int main(int argc, char **argv) {
printf("random seed = %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", seed16[0], seed16[1], seed16[2], seed16[3], seed16[4], seed16[5], seed16[6], seed16[7], seed16[8], seed16[9], seed16[10], seed16[11], seed16[12], seed16[13], seed16[14], seed16[15]);
/* initialize */
- run_context_tests();
+ run_context_tests(0);
+ run_context_tests(1);
run_scratch_tests();
ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
if (secp256k1_rand_bits(1)) {
@@ -5119,6 +5560,9 @@ int main(int argc, char **argv) {
/* EC key edge cases */
run_eckey_edge_case_test();
+ /* EC key arithmetic test */
+ run_eckey_negate_test();
+
#ifdef ENABLE_MODULE_ECDH
/* ecdh tests */
run_ecdh_tests();
@@ -5139,6 +5583,11 @@ int main(int argc, char **argv) {
run_recovery_tests();
#endif
+ /* util tests */
+ run_memczero_test();
+
+ run_cmov_tests();
+
secp256k1_rand256(run32);
printf("random run = %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", run32[0], run32[1], run32[2], run32[3], run32[4], run32[5], run32[6], run32[7], run32[8], run32[9], run32[10], run32[11], run32[12], run32[13], run32[14], run32[15]);
diff --git a/src/secp256k1/src/tests_exhaustive.c b/src/secp256k1/src/tests_exhaustive.c
index ab9779b02f..8cca1cef21 100644
--- a/src/secp256k1/src/tests_exhaustive.c
+++ b/src/secp256k1/src/tests_exhaustive.c
@@ -142,7 +142,7 @@ void test_exhaustive_addition(const secp256k1_ge *group, const secp256k1_gej *gr
for (i = 0; i < order; i++) {
secp256k1_gej tmp;
if (i > 0) {
- secp256k1_gej_double_nonzero(&tmp, &groupj[i], NULL);
+ secp256k1_gej_double_nonzero(&tmp, &groupj[i]);
ge_equals_gej(&group[(2 * i) % order], &tmp);
}
secp256k1_gej_double_var(&tmp, &groupj[i], NULL);
@@ -212,14 +212,14 @@ void test_exhaustive_ecmult_multi(const secp256k1_context *ctx, const secp256k1_
data.pt[0] = group[x];
data.pt[1] = group[y];
- secp256k1_ecmult_multi_var(&ctx->ecmult_ctx, scratch, &tmp, &g_sc, ecmult_multi_callback, &data, 2);
+ secp256k1_ecmult_multi_var(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &tmp, &g_sc, ecmult_multi_callback, &data, 2);
ge_equals_gej(&group[(i * x + j * y + k) % order], &tmp);
}
}
}
}
}
- secp256k1_scratch_destroy(scratch);
+ secp256k1_scratch_destroy(&ctx->error_callback, scratch);
}
void r_from_k(secp256k1_scalar *r, const secp256k1_ge *group, int k) {
diff --git a/src/secp256k1/src/util.h b/src/secp256k1/src/util.h
index e1f5b76452..8289e23e0c 100644
--- a/src/secp256k1/src/util.h
+++ b/src/secp256k1/src/util.h
@@ -14,6 +14,7 @@
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
+#include <limits.h>
typedef struct {
void (*fn)(const char *text, void* data);
@@ -68,6 +69,25 @@ static SECP256K1_INLINE void secp256k1_callback_call(const secp256k1_callback *
#define VERIFY_SETUP(stmt)
#endif
+/* Define `VG_UNDEF` and `VG_CHECK` when VALGRIND is defined */
+#if !defined(VG_CHECK)
+# if defined(VALGRIND)
+# include <valgrind/memcheck.h>
+# define VG_UNDEF(x,y) VALGRIND_MAKE_MEM_UNDEFINED((x),(y))
+# define VG_CHECK(x,y) VALGRIND_CHECK_MEM_IS_DEFINED((x),(y))
+# else
+# define VG_UNDEF(x,y)
+# define VG_CHECK(x,y)
+# endif
+#endif
+
+/* Like `VG_CHECK` but on VERIFY only */
+#if defined(VERIFY)
+#define VG_CHECK_VERIFY(x,y) VG_CHECK((x), (y))
+#else
+#define VG_CHECK_VERIFY(x,y)
+#endif
+
static SECP256K1_INLINE void *checked_malloc(const secp256k1_callback* cb, size_t size) {
void *ret = malloc(size);
if (ret == NULL) {
@@ -84,6 +104,47 @@ static SECP256K1_INLINE void *checked_realloc(const secp256k1_callback* cb, void
return ret;
}
+#if defined(__BIGGEST_ALIGNMENT__)
+#define ALIGNMENT __BIGGEST_ALIGNMENT__
+#else
+/* Using 16 bytes alignment because common architectures never have alignment
+ * requirements above 8 for any of the types we care about. In addition we
+ * leave some room because currently we don't care about a few bytes. */
+#define ALIGNMENT 16
+#endif
+
+#define ROUND_TO_ALIGN(size) (((size + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT)
+
+/* Assume there is a contiguous memory object with bounds [base, base + max_size)
+ * of which the memory range [base, *prealloc_ptr) is already allocated for usage,
+ * where *prealloc_ptr is an aligned pointer. In that setting, this functions
+ * reserves the subobject [*prealloc_ptr, *prealloc_ptr + alloc_size) of
+ * alloc_size bytes by increasing *prealloc_ptr accordingly, taking into account
+ * alignment requirements.
+ *
+ * The function returns an aligned pointer to the newly allocated subobject.
+ *
+ * This is useful for manual memory management: if we're simply given a block
+ * [base, base + max_size), the caller can use this function to allocate memory
+ * in this block and keep track of the current allocation state with *prealloc_ptr.
+ *
+ * It is VERIFY_CHECKed that there is enough space left in the memory object and
+ * *prealloc_ptr is aligned relative to base.
+ */
+static SECP256K1_INLINE void *manual_alloc(void** prealloc_ptr, size_t alloc_size, void* base, size_t max_size) {
+ size_t aligned_alloc_size = ROUND_TO_ALIGN(alloc_size);
+ void* ret;
+ VERIFY_CHECK(prealloc_ptr != NULL);
+ VERIFY_CHECK(*prealloc_ptr != NULL);
+ VERIFY_CHECK(base != NULL);
+ VERIFY_CHECK((unsigned char*)*prealloc_ptr >= (unsigned char*)base);
+ VERIFY_CHECK(((unsigned char*)*prealloc_ptr - (unsigned char*)base) % ALIGNMENT == 0);
+ VERIFY_CHECK((unsigned char*)*prealloc_ptr - (unsigned char*)base + aligned_alloc_size <= max_size);
+ ret = *prealloc_ptr;
+ *((unsigned char**)prealloc_ptr) += aligned_alloc_size;
+ return ret;
+}
+
/* Macro for restrict, when available and not in a VERIFY build. */
#if defined(SECP256K1_BUILD) && defined(VERIFY)
# define SECP256K1_RESTRICT
@@ -118,4 +179,33 @@ static SECP256K1_INLINE void *checked_realloc(const secp256k1_callback* cb, void
SECP256K1_GNUC_EXT typedef unsigned __int128 uint128_t;
#endif
+/* Zero memory if flag == 1. Flag must be 0 or 1. Constant time. */
+static SECP256K1_INLINE void memczero(void *s, size_t len, int flag) {
+ unsigned char *p = (unsigned char *)s;
+ /* Access flag with a volatile-qualified lvalue.
+ This prevents clang from figuring out (after inlining) that flag can
+ take only be 0 or 1, which leads to variable time code. */
+ volatile int vflag = flag;
+ unsigned char mask = -(unsigned char) vflag;
+ while (len) {
+ *p &= ~mask;
+ p++;
+ len--;
+ }
+}
+
+/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. Both *r and *a must be initialized and non-negative.*/
+static SECP256K1_INLINE void secp256k1_int_cmov(int *r, const int *a, int flag) {
+ unsigned int mask0, mask1, r_masked, a_masked;
+ /* Casting a negative int to unsigned and back to int is implementation defined behavior */
+ VERIFY_CHECK(*r >= 0 && *a >= 0);
+
+ mask0 = (unsigned int)flag + ~0u;
+ mask1 = ~mask0;
+ r_masked = ((unsigned int)*r & mask0);
+ a_masked = ((unsigned int)*a & mask1);
+
+ *r = (int)(r_masked | a_masked);
+}
+
#endif /* SECP256K1_UTIL_H */
diff --git a/src/secp256k1/src/valgrind_ctime_test.c b/src/secp256k1/src/valgrind_ctime_test.c
new file mode 100644
index 0000000000..60a82d599e
--- /dev/null
+++ b/src/secp256k1/src/valgrind_ctime_test.c
@@ -0,0 +1,119 @@
+/**********************************************************************
+ * Copyright (c) 2020 Gregory Maxwell *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+#include <valgrind/memcheck.h>
+#include "include/secp256k1.h"
+#include "util.h"
+
+#if ENABLE_MODULE_ECDH
+# include "include/secp256k1_ecdh.h"
+#endif
+
+#if ENABLE_MODULE_RECOVERY
+# include "include/secp256k1_recovery.h"
+#endif
+
+int main(void) {
+ secp256k1_context* ctx;
+ secp256k1_ecdsa_signature signature;
+ secp256k1_pubkey pubkey;
+ size_t siglen = 74;
+ size_t outputlen = 33;
+ int i;
+ int ret;
+ unsigned char msg[32];
+ unsigned char key[32];
+ unsigned char sig[74];
+ unsigned char spubkey[33];
+#if ENABLE_MODULE_RECOVERY
+ secp256k1_ecdsa_recoverable_signature recoverable_signature;
+ int recid;
+#endif
+
+ if (!RUNNING_ON_VALGRIND) {
+ fprintf(stderr, "This test can only usefully be run inside valgrind.\n");
+ fprintf(stderr, "Usage: libtool --mode=execute valgrind ./valgrind_ctime_test\n");
+ exit(1);
+ }
+
+ /** In theory, testing with a single secret input should be sufficient:
+ * If control flow depended on secrets the tool would generate an error.
+ */
+ for (i = 0; i < 32; i++) {
+ key[i] = i + 65;
+ }
+ for (i = 0; i < 32; i++) {
+ msg[i] = i + 1;
+ }
+
+ ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_DECLASSIFY);
+
+ /* Test keygen. */
+ VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
+ ret = secp256k1_ec_pubkey_create(ctx, &pubkey, key);
+ VALGRIND_MAKE_MEM_DEFINED(&pubkey, sizeof(secp256k1_pubkey));
+ VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+ CHECK(ret);
+ CHECK(secp256k1_ec_pubkey_serialize(ctx, spubkey, &outputlen, &pubkey, SECP256K1_EC_COMPRESSED) == 1);
+
+ /* Test signing. */
+ VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
+ ret = secp256k1_ecdsa_sign(ctx, &signature, msg, key, NULL, NULL);
+ VALGRIND_MAKE_MEM_DEFINED(&signature, sizeof(secp256k1_ecdsa_signature));
+ VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+ CHECK(ret);
+ CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, sig, &siglen, &signature));
+
+#if ENABLE_MODULE_ECDH
+ /* Test ECDH. */
+ VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
+ ret = secp256k1_ecdh(ctx, msg, &pubkey, key, NULL, NULL);
+ VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+ CHECK(ret == 1);
+#endif
+
+#if ENABLE_MODULE_RECOVERY
+ /* Test signing a recoverable signature. */
+ VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
+ ret = secp256k1_ecdsa_sign_recoverable(ctx, &recoverable_signature, msg, key, NULL, NULL);
+ VALGRIND_MAKE_MEM_DEFINED(&recoverable_signature, sizeof(recoverable_signature));
+ VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+ CHECK(ret);
+ CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(ctx, sig, &recid, &recoverable_signature));
+ CHECK(recid >= 0 && recid <= 3);
+#endif
+
+ VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
+ ret = secp256k1_ec_seckey_verify(ctx, key);
+ VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+ CHECK(ret == 1);
+
+ VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
+ ret = secp256k1_ec_seckey_negate(ctx, key);
+ VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+ CHECK(ret == 1);
+
+ VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
+ VALGRIND_MAKE_MEM_UNDEFINED(msg, 32);
+ ret = secp256k1_ec_seckey_tweak_add(ctx, key, msg);
+ VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+ CHECK(ret == 1);
+
+ VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
+ VALGRIND_MAKE_MEM_UNDEFINED(msg, 32);
+ ret = secp256k1_ec_seckey_tweak_mul(ctx, key, msg);
+ VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+ CHECK(ret == 1);
+
+ /* Test context randomisation. Do this last because it leaves the context tainted. */
+ VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
+ ret = secp256k1_context_randomize(ctx, key);
+ VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+ CHECK(ret);
+
+ secp256k1_context_destroy(ctx);
+ return 0;
+}
diff --git a/src/test/fuzz/crypto_common.cpp b/src/test/fuzz/crypto_common.cpp
new file mode 100644
index 0000000000..7ccb125216
--- /dev/null
+++ b/src/test/fuzz/crypto_common.cpp
@@ -0,0 +1,70 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <crypto/common.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <array>
+#include <cassert>
+#include <cstdint>
+#include <cstring>
+#include <vector>
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+ const uint16_t random_u16 = fuzzed_data_provider.ConsumeIntegral<uint16_t>();
+ const uint32_t random_u32 = fuzzed_data_provider.ConsumeIntegral<uint32_t>();
+ const uint64_t random_u64 = fuzzed_data_provider.ConsumeIntegral<uint64_t>();
+ const std::vector<uint8_t> random_bytes_2 = ConsumeFixedLengthByteVector(fuzzed_data_provider, 2);
+ const std::vector<uint8_t> random_bytes_4 = ConsumeFixedLengthByteVector(fuzzed_data_provider, 4);
+ const std::vector<uint8_t> random_bytes_8 = ConsumeFixedLengthByteVector(fuzzed_data_provider, 8);
+
+ std::array<uint8_t, 2> writele16_arr;
+ WriteLE16(writele16_arr.data(), random_u16);
+ assert(ReadLE16(writele16_arr.data()) == random_u16);
+
+ std::array<uint8_t, 4> writele32_arr;
+ WriteLE32(writele32_arr.data(), random_u32);
+ assert(ReadLE32(writele32_arr.data()) == random_u32);
+
+ std::array<uint8_t, 8> writele64_arr;
+ WriteLE64(writele64_arr.data(), random_u64);
+ assert(ReadLE64(writele64_arr.data()) == random_u64);
+
+ std::array<uint8_t, 4> writebe32_arr;
+ WriteBE32(writebe32_arr.data(), random_u32);
+ assert(ReadBE32(writebe32_arr.data()) == random_u32);
+
+ std::array<uint8_t, 8> writebe64_arr;
+ WriteBE64(writebe64_arr.data(), random_u64);
+ assert(ReadBE64(writebe64_arr.data()) == random_u64);
+
+ const uint16_t readle16_result = ReadLE16(random_bytes_2.data());
+ std::array<uint8_t, 2> readle16_arr;
+ WriteLE16(readle16_arr.data(), readle16_result);
+ assert(std::memcmp(random_bytes_2.data(), readle16_arr.data(), 2) == 0);
+
+ const uint32_t readle32_result = ReadLE32(random_bytes_4.data());
+ std::array<uint8_t, 4> readle32_arr;
+ WriteLE32(readle32_arr.data(), readle32_result);
+ assert(std::memcmp(random_bytes_4.data(), readle32_arr.data(), 4) == 0);
+
+ const uint64_t readle64_result = ReadLE64(random_bytes_8.data());
+ std::array<uint8_t, 8> readle64_arr;
+ WriteLE64(readle64_arr.data(), readle64_result);
+ assert(std::memcmp(random_bytes_8.data(), readle64_arr.data(), 8) == 0);
+
+ const uint32_t readbe32_result = ReadBE32(random_bytes_4.data());
+ std::array<uint8_t, 4> readbe32_arr;
+ WriteBE32(readbe32_arr.data(), readbe32_result);
+ assert(std::memcmp(random_bytes_4.data(), readbe32_arr.data(), 4) == 0);
+
+ const uint64_t readbe64_result = ReadBE64(random_bytes_8.data());
+ std::array<uint8_t, 8> readbe64_arr;
+ WriteBE64(readbe64_arr.data(), readbe64_result);
+ assert(std::memcmp(random_bytes_8.data(), readbe64_arr.data(), 8) == 0);
+}
diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h
index f26878a704..1c1b2cd254 100644
--- a/src/test/fuzz/util.h
+++ b/src/test/fuzz/util.h
@@ -214,4 +214,18 @@ NODISCARD inline bool ContainsSpentInput(const CTransaction& tx, const CCoinsVie
return false;
}
+/**
+ * Returns a byte vector of specified size regardless of the number of remaining bytes available
+ * from the fuzzer. Pads with zero value bytes if needed to achieve the specified size.
+ */
+NODISCARD inline std::vector<uint8_t> ConsumeFixedLengthByteVector(FuzzedDataProvider& fuzzed_data_provider, const size_t length) noexcept
+{
+ std::vector<uint8_t> result(length);
+ const std::vector<uint8_t> random_bytes = fuzzed_data_provider.ConsumeBytes<uint8_t>(length);
+ if (!random_bytes.empty()) {
+ std::memcpy(result.data(), random_bytes.data(), random_bytes.size());
+ }
+ return result;
+}
+
#endif // BITCOIN_TEST_FUZZ_UTIL_H
diff --git a/src/ui_interface.h b/src/ui_interface.h
index d45811178f..9f70d7b35c 100644
--- a/src/ui_interface.h
+++ b/src/ui_interface.h
@@ -67,9 +67,6 @@ public:
/** Force blocking, modal message box dialog (not just OS notification) */
MODAL = 0x10000000U,
- /** Do not prepend error/warning prefix */
- MSG_NOPREFIX = 0x20000000U,
-
/** Do not print contents of message to debug log */
SECURE = 0x40000000U,
diff --git a/src/validation.cpp b/src/validation.cpp
index cbe89443b8..49c0142be2 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -1662,24 +1662,24 @@ bool UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex* pindex)
return true;
}
+constexpr auto AbortError = InitError;
+
/** Abort with a message */
-// TODO: AbortNode() should take bilingual_str userMessage parameter.
-static bool AbortNode(const std::string& strMessage, const std::string& userMessage = "", unsigned int prefix = 0)
+static bool AbortNode(const std::string& strMessage, bilingual_str user_message = bilingual_str())
{
SetMiscWarning(Untranslated(strMessage));
LogPrintf("*** %s\n", strMessage);
- if (!userMessage.empty()) {
- uiInterface.ThreadSafeMessageBox(Untranslated(userMessage), "", CClientUIInterface::MSG_ERROR | prefix);
- } else {
- uiInterface.ThreadSafeMessageBox(_("Error: A fatal internal error occurred, see debug.log for details"), "", CClientUIInterface::MSG_ERROR | CClientUIInterface::MSG_NOPREFIX);
+ if (user_message.empty()) {
+ user_message = _("A fatal internal error occurred, see debug.log for details");
}
+ AbortError(user_message);
StartShutdown();
return false;
}
-static bool AbortNode(BlockValidationState& state, const std::string& strMessage, const std::string& userMessage = "", unsigned int prefix = 0)
+static bool AbortNode(BlockValidationState& state, const std::string& strMessage, const bilingual_str& userMessage = bilingual_str())
{
- AbortNode(strMessage, userMessage, prefix);
+ AbortNode(strMessage, userMessage);
return state.Error(strMessage);
}
@@ -2344,7 +2344,7 @@ bool CChainState::FlushStateToDisk(
if (fDoFullFlush || fPeriodicWrite) {
// Depend on nMinDiskSpace to ensure we can write block index
if (!CheckDiskSpace(GetBlocksDir())) {
- return AbortNode(state, "Disk space is too low!", _("Error: Disk space is too low!").translated, CClientUIInterface::MSG_NOPREFIX);
+ return AbortNode(state, "Disk space is too low!", _("Disk space is too low!"));
}
{
LOG_TIME_MILLIS_WITH_CATEGORY("write block and undo data to disk", BCLog::BENCH);
@@ -2392,7 +2392,7 @@ bool CChainState::FlushStateToDisk(
// an overestimation, as most will delete an existing entry or
// overwrite one. Still, use a conservative safety factor of 2.
if (!CheckDiskSpace(GetDataDir(), 48 * 2 * 2 * CoinsTip().GetCacheSize())) {
- return AbortNode(state, "Disk space is too low!", _("Error: Disk space is too low!").translated, CClientUIInterface::MSG_NOPREFIX);
+ return AbortNode(state, "Disk space is too low!", _("Disk space is too low!"));
}
// Flush the chainstate (which may refer to block index entries).
if (!CoinsTip().Flush())
@@ -3299,7 +3299,7 @@ static bool FindBlockPos(FlatFilePos &pos, unsigned int nAddSize, unsigned int n
bool out_of_space;
size_t bytes_allocated = BlockFileSeq().Allocate(pos, nAddSize, out_of_space);
if (out_of_space) {
- return AbortNode("Disk space is too low!", _("Error: Disk space is too low!").translated, CClientUIInterface::MSG_NOPREFIX);
+ return AbortNode("Disk space is too low!", _("Disk space is too low!"));
}
if (bytes_allocated != 0 && fPruneMode) {
fCheckForPruning = true;
@@ -3323,7 +3323,7 @@ static bool FindUndoPos(BlockValidationState &state, int nFile, FlatFilePos &pos
bool out_of_space;
size_t bytes_allocated = UndoFileSeq().Allocate(pos, nAddSize, out_of_space);
if (out_of_space) {
- return AbortNode(state, "Disk space is too low!", _("Error: Disk space is too low!").translated, CClientUIInterface::MSG_NOPREFIX);
+ return AbortNode(state, "Disk space is too low!", _("Disk space is too low!"));
}
if (bytes_allocated != 0 && fPruneMode) {
fCheckForPruning = true;
diff --git a/src/wallet/bdb.cpp b/src/wallet/bdb.cpp
new file mode 100644
index 0000000000..7ed9c88122
--- /dev/null
+++ b/src/wallet/bdb.cpp
@@ -0,0 +1,805 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <wallet/bdb.h>
+#include <wallet/db.h>
+
+#include <util/strencodings.h>
+#include <util/translation.h>
+
+#include <stdint.h>
+
+#ifndef WIN32
+#include <sys/stat.h>
+#endif
+
+namespace {
+
+//! Make sure database has a unique fileid within the environment. If it
+//! doesn't, throw an error. BDB caches do not work properly when more than one
+//! open database has the same fileid (values written to one database may show
+//! up in reads to other databases).
+//!
+//! BerkeleyDB generates unique fileids by default
+//! (https://docs.oracle.com/cd/E17275_01/html/programmer_reference/program_copy.html),
+//! so bitcoin should never create different databases with the same fileid, but
+//! this error can be triggered if users manually copy database files.
+void CheckUniqueFileid(const BerkeleyEnvironment& env, const std::string& filename, Db& db, WalletDatabaseFileId& fileid)
+{
+ if (env.IsMock()) return;
+
+ int ret = db.get_mpf()->get_fileid(fileid.value);
+ if (ret != 0) {
+ throw std::runtime_error(strprintf("BerkeleyBatch: Can't open database %s (get_fileid failed with %d)", filename, ret));
+ }
+
+ for (const auto& item : env.m_fileids) {
+ if (fileid == item.second && &fileid != &item.second) {
+ throw std::runtime_error(strprintf("BerkeleyBatch: Can't open database %s (duplicates fileid %s from %s)", filename,
+ HexStr(std::begin(item.second.value), std::end(item.second.value)), item.first));
+ }
+ }
+}
+
+RecursiveMutex cs_db;
+std::map<std::string, std::weak_ptr<BerkeleyEnvironment>> g_dbenvs GUARDED_BY(cs_db); //!< Map from directory name to db environment.
+} // namespace
+
+bool WalletDatabaseFileId::operator==(const WalletDatabaseFileId& rhs) const
+{
+ return memcmp(value, &rhs.value, sizeof(value)) == 0;
+}
+
+bool IsBDBWalletLoaded(const fs::path& wallet_path)
+{
+ fs::path env_directory;
+ std::string database_filename;
+ SplitWalletPath(wallet_path, env_directory, database_filename);
+ LOCK(cs_db);
+ auto env = g_dbenvs.find(env_directory.string());
+ if (env == g_dbenvs.end()) return false;
+ auto database = env->second.lock();
+ return database && database->IsDatabaseLoaded(database_filename);
+}
+
+/**
+ * @param[in] wallet_path Path to wallet directory. Or (for backwards compatibility only) a path to a berkeley btree data file inside a wallet directory.
+ * @param[out] database_filename Filename of berkeley btree data file inside the wallet directory.
+ * @return A shared pointer to the BerkeleyEnvironment object for the wallet directory, never empty because ~BerkeleyEnvironment
+ * erases the weak pointer from the g_dbenvs map.
+ * @post A new BerkeleyEnvironment weak pointer is inserted into g_dbenvs if the directory path key was not already in the map.
+ */
+std::shared_ptr<BerkeleyEnvironment> GetWalletEnv(const fs::path& wallet_path, std::string& database_filename)
+{
+ fs::path env_directory;
+ SplitWalletPath(wallet_path, env_directory, database_filename);
+ LOCK(cs_db);
+ auto inserted = g_dbenvs.emplace(env_directory.string(), std::weak_ptr<BerkeleyEnvironment>());
+ if (inserted.second) {
+ auto env = std::make_shared<BerkeleyEnvironment>(env_directory.string());
+ inserted.first->second = env;
+ return env;
+ }
+ return inserted.first->second.lock();
+}
+
+//
+// BerkeleyBatch
+//
+
+void BerkeleyEnvironment::Close()
+{
+ if (!fDbEnvInit)
+ return;
+
+ fDbEnvInit = false;
+
+ for (auto& db : m_databases) {
+ auto count = mapFileUseCount.find(db.first);
+ assert(count == mapFileUseCount.end() || count->second == 0);
+ BerkeleyDatabase& database = db.second.get();
+ if (database.m_db) {
+ database.m_db->close(0);
+ database.m_db.reset();
+ }
+ }
+
+ FILE* error_file = nullptr;
+ dbenv->get_errfile(&error_file);
+
+ int ret = dbenv->close(0);
+ if (ret != 0)
+ LogPrintf("BerkeleyEnvironment::Close: Error %d closing database environment: %s\n", ret, DbEnv::strerror(ret));
+ if (!fMockDb)
+ DbEnv((u_int32_t)0).remove(strPath.c_str(), 0);
+
+ if (error_file) fclose(error_file);
+
+ UnlockDirectory(strPath, ".walletlock");
+}
+
+void BerkeleyEnvironment::Reset()
+{
+ dbenv.reset(new DbEnv(DB_CXX_NO_EXCEPTIONS));
+ fDbEnvInit = false;
+ fMockDb = false;
+}
+
+BerkeleyEnvironment::BerkeleyEnvironment(const fs::path& dir_path) : strPath(dir_path.string())
+{
+ Reset();
+}
+
+BerkeleyEnvironment::~BerkeleyEnvironment()
+{
+ LOCK(cs_db);
+ g_dbenvs.erase(strPath);
+ Close();
+}
+
+bool BerkeleyEnvironment::Open(bool retry)
+{
+ if (fDbEnvInit) {
+ return true;
+ }
+
+ fs::path pathIn = strPath;
+ TryCreateDirectories(pathIn);
+ if (!LockDirectory(pathIn, ".walletlock")) {
+ LogPrintf("Cannot obtain a lock on wallet directory %s. Another instance of bitcoin may be using it.\n", strPath);
+ return false;
+ }
+
+ fs::path pathLogDir = pathIn / "database";
+ TryCreateDirectories(pathLogDir);
+ fs::path pathErrorFile = pathIn / "db.log";
+ LogPrintf("BerkeleyEnvironment::Open: LogDir=%s ErrorFile=%s\n", pathLogDir.string(), pathErrorFile.string());
+
+ unsigned int nEnvFlags = 0;
+ if (gArgs.GetBoolArg("-privdb", DEFAULT_WALLET_PRIVDB))
+ nEnvFlags |= DB_PRIVATE;
+
+ dbenv->set_lg_dir(pathLogDir.string().c_str());
+ dbenv->set_cachesize(0, 0x100000, 1); // 1 MiB should be enough for just the wallet
+ dbenv->set_lg_bsize(0x10000);
+ dbenv->set_lg_max(1048576);
+ dbenv->set_lk_max_locks(40000);
+ dbenv->set_lk_max_objects(40000);
+ dbenv->set_errfile(fsbridge::fopen(pathErrorFile, "a")); /// debug
+ dbenv->set_flags(DB_AUTO_COMMIT, 1);
+ dbenv->set_flags(DB_TXN_WRITE_NOSYNC, 1);
+ dbenv->log_set_config(DB_LOG_AUTO_REMOVE, 1);
+ int ret = dbenv->open(strPath.c_str(),
+ DB_CREATE |
+ DB_INIT_LOCK |
+ DB_INIT_LOG |
+ DB_INIT_MPOOL |
+ DB_INIT_TXN |
+ DB_THREAD |
+ DB_RECOVER |
+ nEnvFlags,
+ S_IRUSR | S_IWUSR);
+ if (ret != 0) {
+ LogPrintf("BerkeleyEnvironment::Open: Error %d opening database environment: %s\n", ret, DbEnv::strerror(ret));
+ int ret2 = dbenv->close(0);
+ if (ret2 != 0) {
+ LogPrintf("BerkeleyEnvironment::Open: Error %d closing failed database environment: %s\n", ret2, DbEnv::strerror(ret2));
+ }
+ Reset();
+ if (retry) {
+ // try moving the database env out of the way
+ fs::path pathDatabaseBak = pathIn / strprintf("database.%d.bak", GetTime());
+ try {
+ fs::rename(pathLogDir, pathDatabaseBak);
+ LogPrintf("Moved old %s to %s. Retrying.\n", pathLogDir.string(), pathDatabaseBak.string());
+ } catch (const fs::filesystem_error&) {
+ // failure is ok (well, not really, but it's not worse than what we started with)
+ }
+ // try opening it again one more time
+ if (!Open(false /* retry */)) {
+ // if it still fails, it probably means we can't even create the database env
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ fDbEnvInit = true;
+ fMockDb = false;
+ return true;
+}
+
+//! Construct an in-memory mock Berkeley environment for testing
+BerkeleyEnvironment::BerkeleyEnvironment()
+{
+ Reset();
+
+ LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::MakeMock\n");
+
+ dbenv->set_cachesize(1, 0, 1);
+ dbenv->set_lg_bsize(10485760 * 4);
+ dbenv->set_lg_max(10485760);
+ dbenv->set_lk_max_locks(10000);
+ dbenv->set_lk_max_objects(10000);
+ dbenv->set_flags(DB_AUTO_COMMIT, 1);
+ dbenv->log_set_config(DB_LOG_IN_MEMORY, 1);
+ int ret = dbenv->open(nullptr,
+ DB_CREATE |
+ DB_INIT_LOCK |
+ DB_INIT_LOG |
+ DB_INIT_MPOOL |
+ DB_INIT_TXN |
+ DB_THREAD |
+ DB_PRIVATE,
+ S_IRUSR | S_IWUSR);
+ if (ret > 0) {
+ throw std::runtime_error(strprintf("BerkeleyEnvironment::MakeMock: Error %d opening database environment.", ret));
+ }
+
+ fDbEnvInit = true;
+ fMockDb = true;
+}
+
+bool BerkeleyEnvironment::Verify(const std::string& strFile)
+{
+ LOCK(cs_db);
+ assert(mapFileUseCount.count(strFile) == 0);
+
+ Db db(dbenv.get(), 0);
+ int result = db.verify(strFile.c_str(), nullptr, nullptr, 0);
+ return result == 0;
+}
+
+BerkeleyBatch::SafeDbt::SafeDbt()
+{
+ m_dbt.set_flags(DB_DBT_MALLOC);
+}
+
+BerkeleyBatch::SafeDbt::SafeDbt(void* data, size_t size)
+ : m_dbt(data, size)
+{
+}
+
+BerkeleyBatch::SafeDbt::~SafeDbt()
+{
+ if (m_dbt.get_data() != nullptr) {
+ // Clear memory, e.g. in case it was a private key
+ memory_cleanse(m_dbt.get_data(), m_dbt.get_size());
+ // under DB_DBT_MALLOC, data is malloced by the Dbt, but must be
+ // freed by the caller.
+ // https://docs.oracle.com/cd/E17275_01/html/api_reference/C/dbt.html
+ if (m_dbt.get_flags() & DB_DBT_MALLOC) {
+ free(m_dbt.get_data());
+ }
+ }
+}
+
+const void* BerkeleyBatch::SafeDbt::get_data() const
+{
+ return m_dbt.get_data();
+}
+
+u_int32_t BerkeleyBatch::SafeDbt::get_size() const
+{
+ return m_dbt.get_size();
+}
+
+BerkeleyBatch::SafeDbt::operator Dbt*()
+{
+ return &m_dbt;
+}
+
+bool BerkeleyBatch::VerifyEnvironment(const fs::path& file_path, bilingual_str& errorStr)
+{
+ std::string walletFile;
+ std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(file_path, walletFile);
+ fs::path walletDir = env->Directory();
+
+ LogPrintf("Using BerkeleyDB version %s\n", BerkeleyDatabaseVersion());
+ LogPrintf("Using wallet %s\n", file_path.string());
+
+ if (!env->Open(true /* retry */)) {
+ errorStr = strprintf(_("Error initializing wallet database environment %s!"), walletDir);
+ return false;
+ }
+
+ return true;
+}
+
+bool BerkeleyBatch::VerifyDatabaseFile(const fs::path& file_path, bilingual_str& errorStr)
+{
+ std::string walletFile;
+ std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(file_path, walletFile);
+ fs::path walletDir = env->Directory();
+
+ if (fs::exists(walletDir / walletFile))
+ {
+ if (!env->Verify(walletFile)) {
+ errorStr = strprintf(_("%s corrupt. Try using the wallet tool bitcoin-wallet to salvage or restoring a backup."), walletFile);
+ return false;
+ }
+ }
+ // also return true if files does not exists
+ return true;
+}
+
+void BerkeleyEnvironment::CheckpointLSN(const std::string& strFile)
+{
+ dbenv->txn_checkpoint(0, 0, 0);
+ if (fMockDb)
+ return;
+ dbenv->lsn_reset(strFile.c_str(), 0);
+}
+
+
+BerkeleyBatch::BerkeleyBatch(BerkeleyDatabase& database, const char* pszMode, bool fFlushOnCloseIn) : pdb(nullptr), activeTxn(nullptr)
+{
+ fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w'));
+ fFlushOnClose = fFlushOnCloseIn;
+ env = database.env.get();
+ if (database.IsDummy()) {
+ return;
+ }
+ const std::string &strFilename = database.strFile;
+
+ bool fCreate = strchr(pszMode, 'c') != nullptr;
+ unsigned int nFlags = DB_THREAD;
+ if (fCreate)
+ nFlags |= DB_CREATE;
+
+ {
+ LOCK(cs_db);
+ if (!env->Open(false /* retry */))
+ throw std::runtime_error("BerkeleyBatch: Failed to open database environment.");
+
+ pdb = database.m_db.get();
+ if (pdb == nullptr) {
+ int ret;
+ std::unique_ptr<Db> pdb_temp = MakeUnique<Db>(env->dbenv.get(), 0);
+
+ bool fMockDb = env->IsMock();
+ if (fMockDb) {
+ DbMpoolFile* mpf = pdb_temp->get_mpf();
+ ret = mpf->set_flags(DB_MPOOL_NOFILE, 1);
+ if (ret != 0) {
+ throw std::runtime_error(strprintf("BerkeleyBatch: Failed to configure for no temp file backing for database %s", strFilename));
+ }
+ }
+
+ ret = pdb_temp->open(nullptr, // Txn pointer
+ fMockDb ? nullptr : strFilename.c_str(), // Filename
+ fMockDb ? strFilename.c_str() : "main", // Logical db name
+ DB_BTREE, // Database type
+ nFlags, // Flags
+ 0);
+
+ if (ret != 0) {
+ throw std::runtime_error(strprintf("BerkeleyBatch: Error %d, can't open database %s", ret, strFilename));
+ }
+
+ // Call CheckUniqueFileid on the containing BDB environment to
+ // avoid BDB data consistency bugs that happen when different data
+ // files in the same environment have the same fileid.
+ //
+ // Also call CheckUniqueFileid on all the other g_dbenvs to prevent
+ // bitcoin from opening the same data file through another
+ // environment when the file is referenced through equivalent but
+ // not obviously identical symlinked or hard linked or bind mounted
+ // paths. In the future a more relaxed check for equal inode and
+ // device ids could be done instead, which would allow opening
+ // different backup copies of a wallet at the same time. Maybe even
+ // more ideally, an exclusive lock for accessing the database could
+ // be implemented, so no equality checks are needed at all. (Newer
+ // versions of BDB have an set_lk_exclusive method for this
+ // purpose, but the older version we use does not.)
+ for (const auto& env : g_dbenvs) {
+ CheckUniqueFileid(*env.second.lock().get(), strFilename, *pdb_temp, this->env->m_fileids[strFilename]);
+ }
+
+ pdb = pdb_temp.release();
+ database.m_db.reset(pdb);
+
+ if (fCreate && !Exists(std::string("version"))) {
+ bool fTmp = fReadOnly;
+ fReadOnly = false;
+ Write(std::string("version"), CLIENT_VERSION);
+ fReadOnly = fTmp;
+ }
+ }
+ ++env->mapFileUseCount[strFilename];
+ strFile = strFilename;
+ }
+}
+
+void BerkeleyBatch::Flush()
+{
+ if (activeTxn)
+ return;
+
+ // Flush database activity from memory pool to disk log
+ unsigned int nMinutes = 0;
+ if (fReadOnly)
+ nMinutes = 1;
+
+ if (env) { // env is nullptr for dummy databases (i.e. in tests). Don't actually flush if env is nullptr so we don't segfault
+ env->dbenv->txn_checkpoint(nMinutes ? gArgs.GetArg("-dblogsize", DEFAULT_WALLET_DBLOGSIZE) * 1024 : 0, nMinutes, 0);
+ }
+}
+
+void BerkeleyDatabase::IncrementUpdateCounter()
+{
+ ++nUpdateCounter;
+}
+
+void BerkeleyBatch::Close()
+{
+ if (!pdb)
+ return;
+ if (activeTxn)
+ activeTxn->abort();
+ activeTxn = nullptr;
+ pdb = nullptr;
+
+ if (fFlushOnClose)
+ Flush();
+
+ {
+ LOCK(cs_db);
+ --env->mapFileUseCount[strFile];
+ }
+ env->m_db_in_use.notify_all();
+}
+
+void BerkeleyEnvironment::CloseDb(const std::string& strFile)
+{
+ {
+ LOCK(cs_db);
+ auto it = m_databases.find(strFile);
+ assert(it != m_databases.end());
+ BerkeleyDatabase& database = it->second.get();
+ if (database.m_db) {
+ // Close the database handle
+ database.m_db->close(0);
+ database.m_db.reset();
+ }
+ }
+}
+
+void BerkeleyEnvironment::ReloadDbEnv()
+{
+ // Make sure that no Db's are in use
+ AssertLockNotHeld(cs_db);
+ std::unique_lock<RecursiveMutex> lock(cs_db);
+ m_db_in_use.wait(lock, [this](){
+ for (auto& count : mapFileUseCount) {
+ if (count.second > 0) return false;
+ }
+ return true;
+ });
+
+ std::vector<std::string> filenames;
+ for (auto it : m_databases) {
+ filenames.push_back(it.first);
+ }
+ // Close the individual Db's
+ for (const std::string& filename : filenames) {
+ CloseDb(filename);
+ }
+ // Reset the environment
+ Flush(true); // This will flush and close the environment
+ Reset();
+ Open(true);
+}
+
+bool BerkeleyBatch::Rewrite(BerkeleyDatabase& database, const char* pszSkip)
+{
+ if (database.IsDummy()) {
+ return true;
+ }
+ BerkeleyEnvironment *env = database.env.get();
+ const std::string& strFile = database.strFile;
+ while (true) {
+ {
+ LOCK(cs_db);
+ if (!env->mapFileUseCount.count(strFile) || env->mapFileUseCount[strFile] == 0) {
+ // Flush log data to the dat file
+ env->CloseDb(strFile);
+ env->CheckpointLSN(strFile);
+ env->mapFileUseCount.erase(strFile);
+
+ bool fSuccess = true;
+ LogPrintf("BerkeleyBatch::Rewrite: Rewriting %s...\n", strFile);
+ std::string strFileRes = strFile + ".rewrite";
+ { // surround usage of db with extra {}
+ BerkeleyBatch db(database, "r");
+ std::unique_ptr<Db> pdbCopy = MakeUnique<Db>(env->dbenv.get(), 0);
+
+ int ret = pdbCopy->open(nullptr, // Txn pointer
+ strFileRes.c_str(), // Filename
+ "main", // Logical db name
+ DB_BTREE, // Database type
+ DB_CREATE, // Flags
+ 0);
+ if (ret > 0) {
+ LogPrintf("BerkeleyBatch::Rewrite: Can't create database file %s\n", strFileRes);
+ fSuccess = false;
+ }
+
+ Dbc* pcursor = db.GetCursor();
+ if (pcursor)
+ while (fSuccess) {
+ CDataStream ssKey(SER_DISK, CLIENT_VERSION);
+ CDataStream ssValue(SER_DISK, CLIENT_VERSION);
+ int ret1 = db.ReadAtCursor(pcursor, ssKey, ssValue);
+ if (ret1 == DB_NOTFOUND) {
+ pcursor->close();
+ break;
+ } else if (ret1 != 0) {
+ pcursor->close();
+ fSuccess = false;
+ break;
+ }
+ if (pszSkip &&
+ strncmp(ssKey.data(), pszSkip, std::min(ssKey.size(), strlen(pszSkip))) == 0)
+ continue;
+ if (strncmp(ssKey.data(), "\x07version", 8) == 0) {
+ // Update version:
+ ssValue.clear();
+ ssValue << CLIENT_VERSION;
+ }
+ Dbt datKey(ssKey.data(), ssKey.size());
+ Dbt datValue(ssValue.data(), ssValue.size());
+ int ret2 = pdbCopy->put(nullptr, &datKey, &datValue, DB_NOOVERWRITE);
+ if (ret2 > 0)
+ fSuccess = false;
+ }
+ if (fSuccess) {
+ db.Close();
+ env->CloseDb(strFile);
+ if (pdbCopy->close(0))
+ fSuccess = false;
+ } else {
+ pdbCopy->close(0);
+ }
+ }
+ if (fSuccess) {
+ Db dbA(env->dbenv.get(), 0);
+ if (dbA.remove(strFile.c_str(), nullptr, 0))
+ fSuccess = false;
+ Db dbB(env->dbenv.get(), 0);
+ if (dbB.rename(strFileRes.c_str(), nullptr, strFile.c_str(), 0))
+ fSuccess = false;
+ }
+ if (!fSuccess)
+ LogPrintf("BerkeleyBatch::Rewrite: Failed to rewrite database file %s\n", strFileRes);
+ return fSuccess;
+ }
+ }
+ UninterruptibleSleep(std::chrono::milliseconds{100});
+ }
+}
+
+
+void BerkeleyEnvironment::Flush(bool fShutdown)
+{
+ int64_t nStart = GetTimeMillis();
+ // Flush log data to the actual data file on all files that are not in use
+ LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: [%s] Flush(%s)%s\n", strPath, fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started");
+ if (!fDbEnvInit)
+ return;
+ {
+ LOCK(cs_db);
+ std::map<std::string, int>::iterator mi = mapFileUseCount.begin();
+ while (mi != mapFileUseCount.end()) {
+ std::string strFile = (*mi).first;
+ int nRefCount = (*mi).second;
+ LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: Flushing %s (refcount = %d)...\n", strFile, nRefCount);
+ if (nRefCount == 0) {
+ // Move log data to the dat file
+ CloseDb(strFile);
+ LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: %s checkpoint\n", strFile);
+ dbenv->txn_checkpoint(0, 0, 0);
+ LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: %s detach\n", strFile);
+ if (!fMockDb)
+ dbenv->lsn_reset(strFile.c_str(), 0);
+ LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: %s closed\n", strFile);
+ mapFileUseCount.erase(mi++);
+ } else
+ mi++;
+ }
+ LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: Flush(%s)%s took %15dms\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started", GetTimeMillis() - nStart);
+ if (fShutdown) {
+ char** listp;
+ if (mapFileUseCount.empty()) {
+ dbenv->log_archive(&listp, DB_ARCH_REMOVE);
+ Close();
+ if (!fMockDb) {
+ fs::remove_all(fs::path(strPath) / "database");
+ }
+ }
+ }
+ }
+}
+
+bool BerkeleyBatch::PeriodicFlush(BerkeleyDatabase& database)
+{
+ if (database.IsDummy()) {
+ return true;
+ }
+ bool ret = false;
+ BerkeleyEnvironment *env = database.env.get();
+ const std::string& strFile = database.strFile;
+ TRY_LOCK(cs_db, lockDb);
+ if (lockDb)
+ {
+ // Don't do this if any databases are in use
+ int nRefCount = 0;
+ std::map<std::string, int>::iterator mit = env->mapFileUseCount.begin();
+ while (mit != env->mapFileUseCount.end())
+ {
+ nRefCount += (*mit).second;
+ mit++;
+ }
+
+ if (nRefCount == 0)
+ {
+ std::map<std::string, int>::iterator mi = env->mapFileUseCount.find(strFile);
+ if (mi != env->mapFileUseCount.end())
+ {
+ LogPrint(BCLog::WALLETDB, "Flushing %s\n", strFile);
+ int64_t nStart = GetTimeMillis();
+
+ // Flush wallet file so it's self contained
+ env->CloseDb(strFile);
+ env->CheckpointLSN(strFile);
+
+ env->mapFileUseCount.erase(mi++);
+ LogPrint(BCLog::WALLETDB, "Flushed %s %dms\n", strFile, GetTimeMillis() - nStart);
+ ret = true;
+ }
+ }
+ }
+
+ return ret;
+}
+
+bool BerkeleyDatabase::Rewrite(const char* pszSkip)
+{
+ return BerkeleyBatch::Rewrite(*this, pszSkip);
+}
+
+bool BerkeleyDatabase::Backup(const std::string& strDest) const
+{
+ if (IsDummy()) {
+ return false;
+ }
+ while (true)
+ {
+ {
+ LOCK(cs_db);
+ if (!env->mapFileUseCount.count(strFile) || env->mapFileUseCount[strFile] == 0)
+ {
+ // Flush log data to the dat file
+ env->CloseDb(strFile);
+ env->CheckpointLSN(strFile);
+ env->mapFileUseCount.erase(strFile);
+
+ // Copy wallet file
+ fs::path pathSrc = env->Directory() / strFile;
+ fs::path pathDest(strDest);
+ if (fs::is_directory(pathDest))
+ pathDest /= strFile;
+
+ try {
+ if (fs::equivalent(pathSrc, pathDest)) {
+ LogPrintf("cannot backup to wallet source file %s\n", pathDest.string());
+ return false;
+ }
+
+ fs::copy_file(pathSrc, pathDest, fs::copy_option::overwrite_if_exists);
+ LogPrintf("copied %s to %s\n", strFile, pathDest.string());
+ return true;
+ } catch (const fs::filesystem_error& e) {
+ LogPrintf("error copying %s to %s - %s\n", strFile, pathDest.string(), fsbridge::get_filesystem_error_message(e));
+ return false;
+ }
+ }
+ }
+ UninterruptibleSleep(std::chrono::milliseconds{100});
+ }
+}
+
+void BerkeleyDatabase::Flush(bool shutdown)
+{
+ if (!IsDummy()) {
+ env->Flush(shutdown);
+ if (shutdown) {
+ LOCK(cs_db);
+ g_dbenvs.erase(env->Directory().string());
+ env = nullptr;
+ } else {
+ // TODO: To avoid g_dbenvs.erase erasing the environment prematurely after the
+ // first database shutdown when multiple databases are open in the same
+ // environment, should replace raw database `env` pointers with shared or weak
+ // pointers, or else separate the database and environment shutdowns so
+ // environments can be shut down after databases.
+ env->m_fileids.erase(strFile);
+ }
+ }
+}
+
+void BerkeleyDatabase::ReloadDbEnv()
+{
+ if (!IsDummy()) {
+ env->ReloadDbEnv();
+ }
+}
+
+Dbc* BerkeleyBatch::GetCursor()
+{
+ if (!pdb)
+ return nullptr;
+ Dbc* pcursor = nullptr;
+ int ret = pdb->cursor(nullptr, &pcursor, 0);
+ if (ret != 0)
+ return nullptr;
+ return pcursor;
+}
+
+int BerkeleyBatch::ReadAtCursor(Dbc* pcursor, CDataStream& ssKey, CDataStream& ssValue)
+{
+ // Read at cursor
+ SafeDbt datKey;
+ SafeDbt datValue;
+ int ret = pcursor->get(datKey, datValue, DB_NEXT);
+ if (ret != 0)
+ return ret;
+ else if (datKey.get_data() == nullptr || datValue.get_data() == nullptr)
+ return 99999;
+
+ // Convert to streams
+ ssKey.SetType(SER_DISK);
+ ssKey.clear();
+ ssKey.write((char*)datKey.get_data(), datKey.get_size());
+ ssValue.SetType(SER_DISK);
+ ssValue.clear();
+ ssValue.write((char*)datValue.get_data(), datValue.get_size());
+ return 0;
+}
+
+bool BerkeleyBatch::TxnBegin()
+{
+ if (!pdb || activeTxn)
+ return false;
+ DbTxn* ptxn = env->TxnBegin();
+ if (!ptxn)
+ return false;
+ activeTxn = ptxn;
+ return true;
+}
+
+bool BerkeleyBatch::TxnCommit()
+{
+ if (!pdb || !activeTxn)
+ return false;
+ int ret = activeTxn->commit(0);
+ activeTxn = nullptr;
+ return (ret == 0);
+}
+
+bool BerkeleyBatch::TxnAbort()
+{
+ if (!pdb || !activeTxn)
+ return false;
+ int ret = activeTxn->abort();
+ activeTxn = nullptr;
+ return (ret == 0);
+}
+
+std::string BerkeleyDatabaseVersion()
+{
+ return DbEnv::version(nullptr, nullptr, nullptr);
+}
diff --git a/src/wallet/bdb.h b/src/wallet/bdb.h
new file mode 100644
index 0000000000..5ed364344b
--- /dev/null
+++ b/src/wallet/bdb.h
@@ -0,0 +1,337 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2020 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_WALLET_BDB_H
+#define BITCOIN_WALLET_BDB_H
+
+#include <clientversion.h>
+#include <fs.h>
+#include <serialize.h>
+#include <streams.h>
+#include <util/system.h>
+#include <wallet/db.h>
+
+#include <atomic>
+#include <map>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wsuggest-override"
+#endif
+#include <db_cxx.h>
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+
+struct bilingual_str;
+
+static const unsigned int DEFAULT_WALLET_DBLOGSIZE = 100;
+static const bool DEFAULT_WALLET_PRIVDB = true;
+
+struct WalletDatabaseFileId {
+ u_int8_t value[DB_FILE_ID_LEN];
+ bool operator==(const WalletDatabaseFileId& rhs) const;
+};
+
+class BerkeleyDatabase;
+
+class BerkeleyEnvironment
+{
+private:
+ bool fDbEnvInit;
+ bool fMockDb;
+ // Don't change into fs::path, as that can result in
+ // shutdown problems/crashes caused by a static initialized internal pointer.
+ std::string strPath;
+
+public:
+ std::unique_ptr<DbEnv> dbenv;
+ std::map<std::string, int> mapFileUseCount;
+ std::map<std::string, std::reference_wrapper<BerkeleyDatabase>> m_databases;
+ std::unordered_map<std::string, WalletDatabaseFileId> m_fileids;
+ std::condition_variable_any m_db_in_use;
+
+ BerkeleyEnvironment(const fs::path& env_directory);
+ BerkeleyEnvironment();
+ ~BerkeleyEnvironment();
+ void Reset();
+
+ bool IsMock() const { return fMockDb; }
+ bool IsInitialized() const { return fDbEnvInit; }
+ bool IsDatabaseLoaded(const std::string& db_filename) const { return m_databases.find(db_filename) != m_databases.end(); }
+ fs::path Directory() const { return strPath; }
+
+ bool Verify(const std::string& strFile);
+
+ bool Open(bool retry);
+ void Close();
+ void Flush(bool fShutdown);
+ void CheckpointLSN(const std::string& strFile);
+
+ void CloseDb(const std::string& strFile);
+ void ReloadDbEnv();
+
+ DbTxn* TxnBegin(int flags = DB_TXN_WRITE_NOSYNC)
+ {
+ DbTxn* ptxn = nullptr;
+ int ret = dbenv->txn_begin(nullptr, &ptxn, flags);
+ if (!ptxn || ret != 0)
+ return nullptr;
+ return ptxn;
+ }
+};
+
+/** Get BerkeleyEnvironment and database filename given a wallet path. */
+std::shared_ptr<BerkeleyEnvironment> GetWalletEnv(const fs::path& wallet_path, std::string& database_filename);
+
+/** Return wheter a BDB wallet database is currently loaded. */
+bool IsBDBWalletLoaded(const fs::path& wallet_path);
+
+/** An instance of this class represents one database.
+ * For BerkeleyDB this is just a (env, strFile) tuple.
+ **/
+class BerkeleyDatabase
+{
+ friend class BerkeleyBatch;
+public:
+ /** Create dummy DB handle */
+ BerkeleyDatabase() : nUpdateCounter(0), nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0), env(nullptr)
+ {
+ }
+
+ /** Create DB handle to real database */
+ BerkeleyDatabase(std::shared_ptr<BerkeleyEnvironment> env, std::string filename) :
+ nUpdateCounter(0), nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0), env(std::move(env)), strFile(std::move(filename))
+ {
+ auto inserted = this->env->m_databases.emplace(strFile, std::ref(*this));
+ assert(inserted.second);
+ }
+
+ ~BerkeleyDatabase() {
+ if (env) {
+ size_t erased = env->m_databases.erase(strFile);
+ assert(erased == 1);
+ }
+ }
+
+ /** Return object for accessing database at specified path. */
+ static std::unique_ptr<BerkeleyDatabase> Create(const fs::path& path)
+ {
+ std::string filename;
+ return MakeUnique<BerkeleyDatabase>(GetWalletEnv(path, filename), std::move(filename));
+ }
+
+ /** Return object for accessing dummy database with no read/write capabilities. */
+ static std::unique_ptr<BerkeleyDatabase> CreateDummy()
+ {
+ return MakeUnique<BerkeleyDatabase>();
+ }
+
+ /** Return object for accessing temporary in-memory database. */
+ static std::unique_ptr<BerkeleyDatabase> CreateMock()
+ {
+ return MakeUnique<BerkeleyDatabase>(std::make_shared<BerkeleyEnvironment>(), "");
+ }
+
+ /** Rewrite the entire database on disk, with the exception of key pszSkip if non-zero
+ */
+ bool Rewrite(const char* pszSkip=nullptr);
+
+ /** Back up the entire database to a file.
+ */
+ bool Backup(const std::string& strDest) const;
+
+ /** Make sure all changes are flushed to disk.
+ */
+ void Flush(bool shutdown);
+
+ void IncrementUpdateCounter();
+
+ void ReloadDbEnv();
+
+ std::atomic<unsigned int> nUpdateCounter;
+ unsigned int nLastSeen;
+ unsigned int nLastFlushed;
+ int64_t nLastWalletUpdate;
+
+ /**
+ * Pointer to shared database environment.
+ *
+ * Normally there is only one BerkeleyDatabase object per
+ * BerkeleyEnvivonment, but in the special, backwards compatible case where
+ * multiple wallet BDB data files are loaded from the same directory, this
+ * will point to a shared instance that gets freed when the last data file
+ * is closed.
+ */
+ std::shared_ptr<BerkeleyEnvironment> env;
+
+ /** Database pointer. This is initialized lazily and reset during flushes, so it can be null. */
+ std::unique_ptr<Db> m_db;
+
+private:
+ std::string strFile;
+
+ /** Return whether this database handle is a dummy for testing.
+ * Only to be used at a low level, application should ideally not care
+ * about this.
+ */
+ bool IsDummy() const { return env == nullptr; }
+};
+
+/** RAII class that provides access to a Berkeley database */
+class BerkeleyBatch
+{
+ /** RAII class that automatically cleanses its data on destruction */
+ class SafeDbt final
+ {
+ Dbt m_dbt;
+
+ public:
+ // construct Dbt with internally-managed data
+ SafeDbt();
+ // construct Dbt with provided data
+ SafeDbt(void* data, size_t size);
+ ~SafeDbt();
+
+ // delegate to Dbt
+ const void* get_data() const;
+ u_int32_t get_size() const;
+
+ // conversion operator to access the underlying Dbt
+ operator Dbt*();
+ };
+
+protected:
+ Db* pdb;
+ std::string strFile;
+ DbTxn* activeTxn;
+ bool fReadOnly;
+ bool fFlushOnClose;
+ BerkeleyEnvironment *env;
+
+public:
+ explicit BerkeleyBatch(BerkeleyDatabase& database, const char* pszMode = "r+", bool fFlushOnCloseIn=true);
+ ~BerkeleyBatch() { Close(); }
+
+ BerkeleyBatch(const BerkeleyBatch&) = delete;
+ BerkeleyBatch& operator=(const BerkeleyBatch&) = delete;
+
+ void Flush();
+ void Close();
+
+ /* flush the wallet passively (TRY_LOCK)
+ ideal to be called periodically */
+ static bool PeriodicFlush(BerkeleyDatabase& database);
+ /* verifies the database environment */
+ static bool VerifyEnvironment(const fs::path& file_path, bilingual_str& errorStr);
+ /* verifies the database file */
+ static bool VerifyDatabaseFile(const fs::path& file_path, bilingual_str& errorStr);
+
+ template <typename K, typename T>
+ bool Read(const K& key, T& value)
+ {
+ if (!pdb)
+ return false;
+
+ // Key
+ CDataStream ssKey(SER_DISK, CLIENT_VERSION);
+ ssKey.reserve(1000);
+ ssKey << key;
+ SafeDbt datKey(ssKey.data(), ssKey.size());
+
+ // Read
+ SafeDbt datValue;
+ int ret = pdb->get(activeTxn, datKey, datValue, 0);
+ bool success = false;
+ if (datValue.get_data() != nullptr) {
+ // Unserialize value
+ try {
+ CDataStream ssValue((char*)datValue.get_data(), (char*)datValue.get_data() + datValue.get_size(), SER_DISK, CLIENT_VERSION);
+ ssValue >> value;
+ success = true;
+ } catch (const std::exception&) {
+ // In this case success remains 'false'
+ }
+ }
+ return ret == 0 && success;
+ }
+
+ template <typename K, typename T>
+ bool Write(const K& key, const T& value, bool fOverwrite = true)
+ {
+ if (!pdb)
+ return true;
+ if (fReadOnly)
+ assert(!"Write called on database in read-only mode");
+
+ // Key
+ CDataStream ssKey(SER_DISK, CLIENT_VERSION);
+ ssKey.reserve(1000);
+ ssKey << key;
+ SafeDbt datKey(ssKey.data(), ssKey.size());
+
+ // Value
+ CDataStream ssValue(SER_DISK, CLIENT_VERSION);
+ ssValue.reserve(10000);
+ ssValue << value;
+ SafeDbt datValue(ssValue.data(), ssValue.size());
+
+ // Write
+ int ret = pdb->put(activeTxn, datKey, datValue, (fOverwrite ? 0 : DB_NOOVERWRITE));
+ return (ret == 0);
+ }
+
+ template <typename K>
+ bool Erase(const K& key)
+ {
+ if (!pdb)
+ return false;
+ if (fReadOnly)
+ assert(!"Erase called on database in read-only mode");
+
+ // Key
+ CDataStream ssKey(SER_DISK, CLIENT_VERSION);
+ ssKey.reserve(1000);
+ ssKey << key;
+ SafeDbt datKey(ssKey.data(), ssKey.size());
+
+ // Erase
+ int ret = pdb->del(activeTxn, datKey, 0);
+ return (ret == 0 || ret == DB_NOTFOUND);
+ }
+
+ template <typename K>
+ bool Exists(const K& key)
+ {
+ if (!pdb)
+ return false;
+
+ // Key
+ CDataStream ssKey(SER_DISK, CLIENT_VERSION);
+ ssKey.reserve(1000);
+ ssKey << key;
+ SafeDbt datKey(ssKey.data(), ssKey.size());
+
+ // Exists
+ int ret = pdb->exists(activeTxn, datKey, 0);
+ return (ret == 0);
+ }
+
+ Dbc* GetCursor();
+ int ReadAtCursor(Dbc* pcursor, CDataStream& ssKey, CDataStream& ssValue);
+ bool TxnBegin();
+ bool TxnCommit();
+ bool TxnAbort();
+
+ bool static Rewrite(BerkeleyDatabase& database, const char* pszSkip = nullptr);
+};
+
+std::string BerkeleyDatabaseVersion();
+
+#endif // BITCOIN_WALLET_BDB_H
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp
index d90e8e6433..1eb82a03c7 100644
--- a/src/wallet/db.cpp
+++ b/src/wallet/db.cpp
@@ -3,55 +3,12 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <fs.h>
#include <wallet/db.h>
-#include <util/strencodings.h>
-#include <util/translation.h>
+#include <string>
-#include <stdint.h>
-
-#ifndef WIN32
-#include <sys/stat.h>
-#endif
-
-namespace {
-
-//! Make sure database has a unique fileid within the environment. If it
-//! doesn't, throw an error. BDB caches do not work properly when more than one
-//! open database has the same fileid (values written to one database may show
-//! up in reads to other databases).
-//!
-//! BerkeleyDB generates unique fileids by default
-//! (https://docs.oracle.com/cd/E17275_01/html/programmer_reference/program_copy.html),
-//! so bitcoin should never create different databases with the same fileid, but
-//! this error can be triggered if users manually copy database files.
-void CheckUniqueFileid(const BerkeleyEnvironment& env, const std::string& filename, Db& db, WalletDatabaseFileId& fileid)
-{
- if (env.IsMock()) return;
-
- int ret = db.get_mpf()->get_fileid(fileid.value);
- if (ret != 0) {
- throw std::runtime_error(strprintf("BerkeleyBatch: Can't open database %s (get_fileid failed with %d)", filename, ret));
- }
-
- for (const auto& item : env.m_fileids) {
- if (fileid == item.second && &fileid != &item.second) {
- throw std::runtime_error(strprintf("BerkeleyBatch: Can't open database %s (duplicates fileid %s from %s)", filename,
- HexStr(std::begin(item.second.value), std::end(item.second.value)), item.first));
- }
- }
-}
-
-RecursiveMutex cs_db;
-std::map<std::string, std::weak_ptr<BerkeleyEnvironment>> g_dbenvs GUARDED_BY(cs_db); //!< Map from directory name to db environment.
-} // namespace
-
-bool WalletDatabaseFileId::operator==(const WalletDatabaseFileId& rhs) const
-{
- return memcmp(value, &rhs.value, sizeof(value)) == 0;
-}
-
-static void SplitWalletPath(const fs::path& wallet_path, fs::path& env_directory, std::string& database_filename)
+void SplitWalletPath(const fs::path& wallet_path, fs::path& env_directory, std::string& database_filename)
{
if (fs::is_regular_file(wallet_path)) {
// Special case for backwards compatibility: if wallet path points to an
@@ -67,18 +24,6 @@ static void SplitWalletPath(const fs::path& wallet_path, fs::path& env_directory
}
}
-bool IsWalletLoaded(const fs::path& wallet_path)
-{
- fs::path env_directory;
- std::string database_filename;
- SplitWalletPath(wallet_path, env_directory, database_filename);
- LOCK(cs_db);
- auto env = g_dbenvs.find(env_directory.string());
- if (env == g_dbenvs.end()) return false;
- auto database = env->second.lock();
- return database && database->IsDatabaseLoaded(database_filename);
-}
-
fs::path WalletDataFilePath(const fs::path& wallet_path)
{
fs::path env_directory;
@@ -86,682 +31,3 @@ fs::path WalletDataFilePath(const fs::path& wallet_path)
SplitWalletPath(wallet_path, env_directory, database_filename);
return env_directory / database_filename;
}
-
-/**
- * @param[in] wallet_path Path to wallet directory. Or (for backwards compatibility only) a path to a berkeley btree data file inside a wallet directory.
- * @param[out] database_filename Filename of berkeley btree data file inside the wallet directory.
- * @return A shared pointer to the BerkeleyEnvironment object for the wallet directory, never empty because ~BerkeleyEnvironment
- * erases the weak pointer from the g_dbenvs map.
- * @post A new BerkeleyEnvironment weak pointer is inserted into g_dbenvs if the directory path key was not already in the map.
- */
-std::shared_ptr<BerkeleyEnvironment> GetWalletEnv(const fs::path& wallet_path, std::string& database_filename)
-{
- fs::path env_directory;
- SplitWalletPath(wallet_path, env_directory, database_filename);
- LOCK(cs_db);
- auto inserted = g_dbenvs.emplace(env_directory.string(), std::weak_ptr<BerkeleyEnvironment>());
- if (inserted.second) {
- auto env = std::make_shared<BerkeleyEnvironment>(env_directory.string());
- inserted.first->second = env;
- return env;
- }
- return inserted.first->second.lock();
-}
-
-//
-// BerkeleyBatch
-//
-
-void BerkeleyEnvironment::Close()
-{
- if (!fDbEnvInit)
- return;
-
- fDbEnvInit = false;
-
- for (auto& db : m_databases) {
- auto count = mapFileUseCount.find(db.first);
- assert(count == mapFileUseCount.end() || count->second == 0);
- BerkeleyDatabase& database = db.second.get();
- if (database.m_db) {
- database.m_db->close(0);
- database.m_db.reset();
- }
- }
-
- FILE* error_file = nullptr;
- dbenv->get_errfile(&error_file);
-
- int ret = dbenv->close(0);
- if (ret != 0)
- LogPrintf("BerkeleyEnvironment::Close: Error %d closing database environment: %s\n", ret, DbEnv::strerror(ret));
- if (!fMockDb)
- DbEnv((u_int32_t)0).remove(strPath.c_str(), 0);
-
- if (error_file) fclose(error_file);
-
- UnlockDirectory(strPath, ".walletlock");
-}
-
-void BerkeleyEnvironment::Reset()
-{
- dbenv.reset(new DbEnv(DB_CXX_NO_EXCEPTIONS));
- fDbEnvInit = false;
- fMockDb = false;
-}
-
-BerkeleyEnvironment::BerkeleyEnvironment(const fs::path& dir_path) : strPath(dir_path.string())
-{
- Reset();
-}
-
-BerkeleyEnvironment::~BerkeleyEnvironment()
-{
- LOCK(cs_db);
- g_dbenvs.erase(strPath);
- Close();
-}
-
-bool BerkeleyEnvironment::Open(bool retry)
-{
- if (fDbEnvInit) {
- return true;
- }
-
- fs::path pathIn = strPath;
- TryCreateDirectories(pathIn);
- if (!LockDirectory(pathIn, ".walletlock")) {
- LogPrintf("Cannot obtain a lock on wallet directory %s. Another instance of bitcoin may be using it.\n", strPath);
- return false;
- }
-
- fs::path pathLogDir = pathIn / "database";
- TryCreateDirectories(pathLogDir);
- fs::path pathErrorFile = pathIn / "db.log";
- LogPrintf("BerkeleyEnvironment::Open: LogDir=%s ErrorFile=%s\n", pathLogDir.string(), pathErrorFile.string());
-
- unsigned int nEnvFlags = 0;
- if (gArgs.GetBoolArg("-privdb", DEFAULT_WALLET_PRIVDB))
- nEnvFlags |= DB_PRIVATE;
-
- dbenv->set_lg_dir(pathLogDir.string().c_str());
- dbenv->set_cachesize(0, 0x100000, 1); // 1 MiB should be enough for just the wallet
- dbenv->set_lg_bsize(0x10000);
- dbenv->set_lg_max(1048576);
- dbenv->set_lk_max_locks(40000);
- dbenv->set_lk_max_objects(40000);
- dbenv->set_errfile(fsbridge::fopen(pathErrorFile, "a")); /// debug
- dbenv->set_flags(DB_AUTO_COMMIT, 1);
- dbenv->set_flags(DB_TXN_WRITE_NOSYNC, 1);
- dbenv->log_set_config(DB_LOG_AUTO_REMOVE, 1);
- int ret = dbenv->open(strPath.c_str(),
- DB_CREATE |
- DB_INIT_LOCK |
- DB_INIT_LOG |
- DB_INIT_MPOOL |
- DB_INIT_TXN |
- DB_THREAD |
- DB_RECOVER |
- nEnvFlags,
- S_IRUSR | S_IWUSR);
- if (ret != 0) {
- LogPrintf("BerkeleyEnvironment::Open: Error %d opening database environment: %s\n", ret, DbEnv::strerror(ret));
- int ret2 = dbenv->close(0);
- if (ret2 != 0) {
- LogPrintf("BerkeleyEnvironment::Open: Error %d closing failed database environment: %s\n", ret2, DbEnv::strerror(ret2));
- }
- Reset();
- if (retry) {
- // try moving the database env out of the way
- fs::path pathDatabaseBak = pathIn / strprintf("database.%d.bak", GetTime());
- try {
- fs::rename(pathLogDir, pathDatabaseBak);
- LogPrintf("Moved old %s to %s. Retrying.\n", pathLogDir.string(), pathDatabaseBak.string());
- } catch (const fs::filesystem_error&) {
- // failure is ok (well, not really, but it's not worse than what we started with)
- }
- // try opening it again one more time
- if (!Open(false /* retry */)) {
- // if it still fails, it probably means we can't even create the database env
- return false;
- }
- } else {
- return false;
- }
- }
-
- fDbEnvInit = true;
- fMockDb = false;
- return true;
-}
-
-//! Construct an in-memory mock Berkeley environment for testing
-BerkeleyEnvironment::BerkeleyEnvironment()
-{
- Reset();
-
- LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::MakeMock\n");
-
- dbenv->set_cachesize(1, 0, 1);
- dbenv->set_lg_bsize(10485760 * 4);
- dbenv->set_lg_max(10485760);
- dbenv->set_lk_max_locks(10000);
- dbenv->set_lk_max_objects(10000);
- dbenv->set_flags(DB_AUTO_COMMIT, 1);
- dbenv->log_set_config(DB_LOG_IN_MEMORY, 1);
- int ret = dbenv->open(nullptr,
- DB_CREATE |
- DB_INIT_LOCK |
- DB_INIT_LOG |
- DB_INIT_MPOOL |
- DB_INIT_TXN |
- DB_THREAD |
- DB_PRIVATE,
- S_IRUSR | S_IWUSR);
- if (ret > 0) {
- throw std::runtime_error(strprintf("BerkeleyEnvironment::MakeMock: Error %d opening database environment.", ret));
- }
-
- fDbEnvInit = true;
- fMockDb = true;
-}
-
-bool BerkeleyEnvironment::Verify(const std::string& strFile)
-{
- LOCK(cs_db);
- assert(mapFileUseCount.count(strFile) == 0);
-
- Db db(dbenv.get(), 0);
- int result = db.verify(strFile.c_str(), nullptr, nullptr, 0);
- return result == 0;
-}
-
-BerkeleyBatch::SafeDbt::SafeDbt()
-{
- m_dbt.set_flags(DB_DBT_MALLOC);
-}
-
-BerkeleyBatch::SafeDbt::SafeDbt(void* data, size_t size)
- : m_dbt(data, size)
-{
-}
-
-BerkeleyBatch::SafeDbt::~SafeDbt()
-{
- if (m_dbt.get_data() != nullptr) {
- // Clear memory, e.g. in case it was a private key
- memory_cleanse(m_dbt.get_data(), m_dbt.get_size());
- // under DB_DBT_MALLOC, data is malloced by the Dbt, but must be
- // freed by the caller.
- // https://docs.oracle.com/cd/E17275_01/html/api_reference/C/dbt.html
- if (m_dbt.get_flags() & DB_DBT_MALLOC) {
- free(m_dbt.get_data());
- }
- }
-}
-
-const void* BerkeleyBatch::SafeDbt::get_data() const
-{
- return m_dbt.get_data();
-}
-
-u_int32_t BerkeleyBatch::SafeDbt::get_size() const
-{
- return m_dbt.get_size();
-}
-
-BerkeleyBatch::SafeDbt::operator Dbt*()
-{
- return &m_dbt;
-}
-
-bool BerkeleyBatch::VerifyEnvironment(const fs::path& file_path, bilingual_str& errorStr)
-{
- std::string walletFile;
- std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(file_path, walletFile);
- fs::path walletDir = env->Directory();
-
- LogPrintf("Using BerkeleyDB version %s\n", BerkeleyDatabaseVersion());
- LogPrintf("Using wallet %s\n", file_path.string());
-
- if (!env->Open(true /* retry */)) {
- errorStr = strprintf(_("Error initializing wallet database environment %s!"), walletDir);
- return false;
- }
-
- return true;
-}
-
-bool BerkeleyBatch::VerifyDatabaseFile(const fs::path& file_path, bilingual_str& errorStr)
-{
- std::string walletFile;
- std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(file_path, walletFile);
- fs::path walletDir = env->Directory();
-
- if (fs::exists(walletDir / walletFile))
- {
- if (!env->Verify(walletFile)) {
- errorStr = strprintf(_("%s corrupt. Try using the wallet tool bitcoin-wallet to salvage or restoring a backup."), walletFile);
- return false;
- }
- }
- // also return true if files does not exists
- return true;
-}
-
-void BerkeleyEnvironment::CheckpointLSN(const std::string& strFile)
-{
- dbenv->txn_checkpoint(0, 0, 0);
- if (fMockDb)
- return;
- dbenv->lsn_reset(strFile.c_str(), 0);
-}
-
-
-BerkeleyBatch::BerkeleyBatch(BerkeleyDatabase& database, const char* pszMode, bool fFlushOnCloseIn) : pdb(nullptr), activeTxn(nullptr)
-{
- fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w'));
- fFlushOnClose = fFlushOnCloseIn;
- env = database.env.get();
- if (database.IsDummy()) {
- return;
- }
- const std::string &strFilename = database.strFile;
-
- bool fCreate = strchr(pszMode, 'c') != nullptr;
- unsigned int nFlags = DB_THREAD;
- if (fCreate)
- nFlags |= DB_CREATE;
-
- {
- LOCK(cs_db);
- if (!env->Open(false /* retry */))
- throw std::runtime_error("BerkeleyBatch: Failed to open database environment.");
-
- pdb = database.m_db.get();
- if (pdb == nullptr) {
- int ret;
- std::unique_ptr<Db> pdb_temp = MakeUnique<Db>(env->dbenv.get(), 0);
-
- bool fMockDb = env->IsMock();
- if (fMockDb) {
- DbMpoolFile* mpf = pdb_temp->get_mpf();
- ret = mpf->set_flags(DB_MPOOL_NOFILE, 1);
- if (ret != 0) {
- throw std::runtime_error(strprintf("BerkeleyBatch: Failed to configure for no temp file backing for database %s", strFilename));
- }
- }
-
- ret = pdb_temp->open(nullptr, // Txn pointer
- fMockDb ? nullptr : strFilename.c_str(), // Filename
- fMockDb ? strFilename.c_str() : "main", // Logical db name
- DB_BTREE, // Database type
- nFlags, // Flags
- 0);
-
- if (ret != 0) {
- throw std::runtime_error(strprintf("BerkeleyBatch: Error %d, can't open database %s", ret, strFilename));
- }
-
- // Call CheckUniqueFileid on the containing BDB environment to
- // avoid BDB data consistency bugs that happen when different data
- // files in the same environment have the same fileid.
- //
- // Also call CheckUniqueFileid on all the other g_dbenvs to prevent
- // bitcoin from opening the same data file through another
- // environment when the file is referenced through equivalent but
- // not obviously identical symlinked or hard linked or bind mounted
- // paths. In the future a more relaxed check for equal inode and
- // device ids could be done instead, which would allow opening
- // different backup copies of a wallet at the same time. Maybe even
- // more ideally, an exclusive lock for accessing the database could
- // be implemented, so no equality checks are needed at all. (Newer
- // versions of BDB have an set_lk_exclusive method for this
- // purpose, but the older version we use does not.)
- for (const auto& env : g_dbenvs) {
- CheckUniqueFileid(*env.second.lock().get(), strFilename, *pdb_temp, this->env->m_fileids[strFilename]);
- }
-
- pdb = pdb_temp.release();
- database.m_db.reset(pdb);
-
- if (fCreate && !Exists(std::string("version"))) {
- bool fTmp = fReadOnly;
- fReadOnly = false;
- Write(std::string("version"), CLIENT_VERSION);
- fReadOnly = fTmp;
- }
- }
- ++env->mapFileUseCount[strFilename];
- strFile = strFilename;
- }
-}
-
-void BerkeleyBatch::Flush()
-{
- if (activeTxn)
- return;
-
- // Flush database activity from memory pool to disk log
- unsigned int nMinutes = 0;
- if (fReadOnly)
- nMinutes = 1;
-
- if (env) { // env is nullptr for dummy databases (i.e. in tests). Don't actually flush if env is nullptr so we don't segfault
- env->dbenv->txn_checkpoint(nMinutes ? gArgs.GetArg("-dblogsize", DEFAULT_WALLET_DBLOGSIZE) * 1024 : 0, nMinutes, 0);
- }
-}
-
-void BerkeleyDatabase::IncrementUpdateCounter()
-{
- ++nUpdateCounter;
-}
-
-void BerkeleyBatch::Close()
-{
- if (!pdb)
- return;
- if (activeTxn)
- activeTxn->abort();
- activeTxn = nullptr;
- pdb = nullptr;
-
- if (fFlushOnClose)
- Flush();
-
- {
- LOCK(cs_db);
- --env->mapFileUseCount[strFile];
- }
- env->m_db_in_use.notify_all();
-}
-
-void BerkeleyEnvironment::CloseDb(const std::string& strFile)
-{
- {
- LOCK(cs_db);
- auto it = m_databases.find(strFile);
- assert(it != m_databases.end());
- BerkeleyDatabase& database = it->second.get();
- if (database.m_db) {
- // Close the database handle
- database.m_db->close(0);
- database.m_db.reset();
- }
- }
-}
-
-void BerkeleyEnvironment::ReloadDbEnv()
-{
- // Make sure that no Db's are in use
- AssertLockNotHeld(cs_db);
- std::unique_lock<RecursiveMutex> lock(cs_db);
- m_db_in_use.wait(lock, [this](){
- for (auto& count : mapFileUseCount) {
- if (count.second > 0) return false;
- }
- return true;
- });
-
- std::vector<std::string> filenames;
- for (auto it : m_databases) {
- filenames.push_back(it.first);
- }
- // Close the individual Db's
- for (const std::string& filename : filenames) {
- CloseDb(filename);
- }
- // Reset the environment
- Flush(true); // This will flush and close the environment
- Reset();
- Open(true);
-}
-
-bool BerkeleyBatch::Rewrite(BerkeleyDatabase& database, const char* pszSkip)
-{
- if (database.IsDummy()) {
- return true;
- }
- BerkeleyEnvironment *env = database.env.get();
- const std::string& strFile = database.strFile;
- while (true) {
- {
- LOCK(cs_db);
- if (!env->mapFileUseCount.count(strFile) || env->mapFileUseCount[strFile] == 0) {
- // Flush log data to the dat file
- env->CloseDb(strFile);
- env->CheckpointLSN(strFile);
- env->mapFileUseCount.erase(strFile);
-
- bool fSuccess = true;
- LogPrintf("BerkeleyBatch::Rewrite: Rewriting %s...\n", strFile);
- std::string strFileRes = strFile + ".rewrite";
- { // surround usage of db with extra {}
- BerkeleyBatch db(database, "r");
- std::unique_ptr<Db> pdbCopy = MakeUnique<Db>(env->dbenv.get(), 0);
-
- int ret = pdbCopy->open(nullptr, // Txn pointer
- strFileRes.c_str(), // Filename
- "main", // Logical db name
- DB_BTREE, // Database type
- DB_CREATE, // Flags
- 0);
- if (ret > 0) {
- LogPrintf("BerkeleyBatch::Rewrite: Can't create database file %s\n", strFileRes);
- fSuccess = false;
- }
-
- Dbc* pcursor = db.GetCursor();
- if (pcursor)
- while (fSuccess) {
- CDataStream ssKey(SER_DISK, CLIENT_VERSION);
- CDataStream ssValue(SER_DISK, CLIENT_VERSION);
- int ret1 = db.ReadAtCursor(pcursor, ssKey, ssValue);
- if (ret1 == DB_NOTFOUND) {
- pcursor->close();
- break;
- } else if (ret1 != 0) {
- pcursor->close();
- fSuccess = false;
- break;
- }
- if (pszSkip &&
- strncmp(ssKey.data(), pszSkip, std::min(ssKey.size(), strlen(pszSkip))) == 0)
- continue;
- if (strncmp(ssKey.data(), "\x07version", 8) == 0) {
- // Update version:
- ssValue.clear();
- ssValue << CLIENT_VERSION;
- }
- Dbt datKey(ssKey.data(), ssKey.size());
- Dbt datValue(ssValue.data(), ssValue.size());
- int ret2 = pdbCopy->put(nullptr, &datKey, &datValue, DB_NOOVERWRITE);
- if (ret2 > 0)
- fSuccess = false;
- }
- if (fSuccess) {
- db.Close();
- env->CloseDb(strFile);
- if (pdbCopy->close(0))
- fSuccess = false;
- } else {
- pdbCopy->close(0);
- }
- }
- if (fSuccess) {
- Db dbA(env->dbenv.get(), 0);
- if (dbA.remove(strFile.c_str(), nullptr, 0))
- fSuccess = false;
- Db dbB(env->dbenv.get(), 0);
- if (dbB.rename(strFileRes.c_str(), nullptr, strFile.c_str(), 0))
- fSuccess = false;
- }
- if (!fSuccess)
- LogPrintf("BerkeleyBatch::Rewrite: Failed to rewrite database file %s\n", strFileRes);
- return fSuccess;
- }
- }
- UninterruptibleSleep(std::chrono::milliseconds{100});
- }
-}
-
-
-void BerkeleyEnvironment::Flush(bool fShutdown)
-{
- int64_t nStart = GetTimeMillis();
- // Flush log data to the actual data file on all files that are not in use
- LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: [%s] Flush(%s)%s\n", strPath, fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started");
- if (!fDbEnvInit)
- return;
- {
- LOCK(cs_db);
- std::map<std::string, int>::iterator mi = mapFileUseCount.begin();
- while (mi != mapFileUseCount.end()) {
- std::string strFile = (*mi).first;
- int nRefCount = (*mi).second;
- LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: Flushing %s (refcount = %d)...\n", strFile, nRefCount);
- if (nRefCount == 0) {
- // Move log data to the dat file
- CloseDb(strFile);
- LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: %s checkpoint\n", strFile);
- dbenv->txn_checkpoint(0, 0, 0);
- LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: %s detach\n", strFile);
- if (!fMockDb)
- dbenv->lsn_reset(strFile.c_str(), 0);
- LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: %s closed\n", strFile);
- mapFileUseCount.erase(mi++);
- } else
- mi++;
- }
- LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: Flush(%s)%s took %15dms\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started", GetTimeMillis() - nStart);
- if (fShutdown) {
- char** listp;
- if (mapFileUseCount.empty()) {
- dbenv->log_archive(&listp, DB_ARCH_REMOVE);
- Close();
- if (!fMockDb) {
- fs::remove_all(fs::path(strPath) / "database");
- }
- }
- }
- }
-}
-
-bool BerkeleyBatch::PeriodicFlush(BerkeleyDatabase& database)
-{
- if (database.IsDummy()) {
- return true;
- }
- bool ret = false;
- BerkeleyEnvironment *env = database.env.get();
- const std::string& strFile = database.strFile;
- TRY_LOCK(cs_db, lockDb);
- if (lockDb)
- {
- // Don't do this if any databases are in use
- int nRefCount = 0;
- std::map<std::string, int>::iterator mit = env->mapFileUseCount.begin();
- while (mit != env->mapFileUseCount.end())
- {
- nRefCount += (*mit).second;
- mit++;
- }
-
- if (nRefCount == 0)
- {
- std::map<std::string, int>::iterator mi = env->mapFileUseCount.find(strFile);
- if (mi != env->mapFileUseCount.end())
- {
- LogPrint(BCLog::WALLETDB, "Flushing %s\n", strFile);
- int64_t nStart = GetTimeMillis();
-
- // Flush wallet file so it's self contained
- env->CloseDb(strFile);
- env->CheckpointLSN(strFile);
-
- env->mapFileUseCount.erase(mi++);
- LogPrint(BCLog::WALLETDB, "Flushed %s %dms\n", strFile, GetTimeMillis() - nStart);
- ret = true;
- }
- }
- }
-
- return ret;
-}
-
-bool BerkeleyDatabase::Rewrite(const char* pszSkip)
-{
- return BerkeleyBatch::Rewrite(*this, pszSkip);
-}
-
-bool BerkeleyDatabase::Backup(const std::string& strDest) const
-{
- if (IsDummy()) {
- return false;
- }
- while (true)
- {
- {
- LOCK(cs_db);
- if (!env->mapFileUseCount.count(strFile) || env->mapFileUseCount[strFile] == 0)
- {
- // Flush log data to the dat file
- env->CloseDb(strFile);
- env->CheckpointLSN(strFile);
- env->mapFileUseCount.erase(strFile);
-
- // Copy wallet file
- fs::path pathSrc = env->Directory() / strFile;
- fs::path pathDest(strDest);
- if (fs::is_directory(pathDest))
- pathDest /= strFile;
-
- try {
- if (fs::equivalent(pathSrc, pathDest)) {
- LogPrintf("cannot backup to wallet source file %s\n", pathDest.string());
- return false;
- }
-
- fs::copy_file(pathSrc, pathDest, fs::copy_option::overwrite_if_exists);
- LogPrintf("copied %s to %s\n", strFile, pathDest.string());
- return true;
- } catch (const fs::filesystem_error& e) {
- LogPrintf("error copying %s to %s - %s\n", strFile, pathDest.string(), fsbridge::get_filesystem_error_message(e));
- return false;
- }
- }
- }
- UninterruptibleSleep(std::chrono::milliseconds{100});
- }
-}
-
-void BerkeleyDatabase::Flush(bool shutdown)
-{
- if (!IsDummy()) {
- env->Flush(shutdown);
- if (shutdown) {
- LOCK(cs_db);
- g_dbenvs.erase(env->Directory().string());
- env = nullptr;
- } else {
- // TODO: To avoid g_dbenvs.erase erasing the environment prematurely after the
- // first database shutdown when multiple databases are open in the same
- // environment, should replace raw database `env` pointers with shared or weak
- // pointers, or else separate the database and environment shutdowns so
- // environments can be shut down after databases.
- env->m_fileids.erase(strFile);
- }
- }
-}
-
-void BerkeleyDatabase::ReloadDbEnv()
-{
- if (!IsDummy()) {
- env->ReloadDbEnv();
- }
-}
-
-std::string BerkeleyDatabaseVersion()
-{
- return DbEnv::version(nullptr, nullptr, nullptr);
-}
diff --git a/src/wallet/db.h b/src/wallet/db.h
index 54ce144ffc..1322bf54fa 100644
--- a/src/wallet/db.h
+++ b/src/wallet/db.h
@@ -6,389 +6,12 @@
#ifndef BITCOIN_WALLET_DB_H
#define BITCOIN_WALLET_DB_H
-#include <clientversion.h>
#include <fs.h>
-#include <serialize.h>
-#include <streams.h>
-#include <util/system.h>
-#include <atomic>
-#include <map>
-#include <memory>
#include <string>
-#include <unordered_map>
-#include <vector>
-
-#if defined(__GNUC__) && !defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wsuggest-override"
-#endif
-#include <db_cxx.h>
-#if defined(__GNUC__) && !defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-
-struct bilingual_str;
-
-static const unsigned int DEFAULT_WALLET_DBLOGSIZE = 100;
-static const bool DEFAULT_WALLET_PRIVDB = true;
-
-struct WalletDatabaseFileId {
- u_int8_t value[DB_FILE_ID_LEN];
- bool operator==(const WalletDatabaseFileId& rhs) const;
-};
-
-class BerkeleyDatabase;
-
-class BerkeleyEnvironment
-{
-private:
- bool fDbEnvInit;
- bool fMockDb;
- // Don't change into fs::path, as that can result in
- // shutdown problems/crashes caused by a static initialized internal pointer.
- std::string strPath;
-
-public:
- std::unique_ptr<DbEnv> dbenv;
- std::map<std::string, int> mapFileUseCount;
- std::map<std::string, std::reference_wrapper<BerkeleyDatabase>> m_databases;
- std::unordered_map<std::string, WalletDatabaseFileId> m_fileids;
- std::condition_variable_any m_db_in_use;
-
- BerkeleyEnvironment(const fs::path& env_directory);
- BerkeleyEnvironment();
- ~BerkeleyEnvironment();
- void Reset();
-
- bool IsMock() const { return fMockDb; }
- bool IsInitialized() const { return fDbEnvInit; }
- bool IsDatabaseLoaded(const std::string& db_filename) const { return m_databases.find(db_filename) != m_databases.end(); }
- fs::path Directory() const { return strPath; }
-
- bool Verify(const std::string& strFile);
-
- bool Open(bool retry);
- void Close();
- void Flush(bool fShutdown);
- void CheckpointLSN(const std::string& strFile);
-
- void CloseDb(const std::string& strFile);
- void ReloadDbEnv();
-
- DbTxn* TxnBegin(int flags = DB_TXN_WRITE_NOSYNC)
- {
- DbTxn* ptxn = nullptr;
- int ret = dbenv->txn_begin(nullptr, &ptxn, flags);
- if (!ptxn || ret != 0)
- return nullptr;
- return ptxn;
- }
-};
-
-/** Return whether a wallet database is currently loaded. */
-bool IsWalletLoaded(const fs::path& wallet_path);
/** Given a wallet directory path or legacy file path, return path to main data file in the wallet database. */
fs::path WalletDataFilePath(const fs::path& wallet_path);
-
-/** Get BerkeleyEnvironment and database filename given a wallet path. */
-std::shared_ptr<BerkeleyEnvironment> GetWalletEnv(const fs::path& wallet_path, std::string& database_filename);
-
-/** An instance of this class represents one database.
- * For BerkeleyDB this is just a (env, strFile) tuple.
- **/
-class BerkeleyDatabase
-{
- friend class BerkeleyBatch;
-public:
- /** Create dummy DB handle */
- BerkeleyDatabase() : nUpdateCounter(0), nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0), env(nullptr)
- {
- }
-
- /** Create DB handle to real database */
- BerkeleyDatabase(std::shared_ptr<BerkeleyEnvironment> env, std::string filename) :
- nUpdateCounter(0), nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0), env(std::move(env)), strFile(std::move(filename))
- {
- auto inserted = this->env->m_databases.emplace(strFile, std::ref(*this));
- assert(inserted.second);
- }
-
- ~BerkeleyDatabase() {
- if (env) {
- size_t erased = env->m_databases.erase(strFile);
- assert(erased == 1);
- }
- }
-
- /** Return object for accessing database at specified path. */
- static std::unique_ptr<BerkeleyDatabase> Create(const fs::path& path)
- {
- std::string filename;
- return MakeUnique<BerkeleyDatabase>(GetWalletEnv(path, filename), std::move(filename));
- }
-
- /** Return object for accessing dummy database with no read/write capabilities. */
- static std::unique_ptr<BerkeleyDatabase> CreateDummy()
- {
- return MakeUnique<BerkeleyDatabase>();
- }
-
- /** Return object for accessing temporary in-memory database. */
- static std::unique_ptr<BerkeleyDatabase> CreateMock()
- {
- return MakeUnique<BerkeleyDatabase>(std::make_shared<BerkeleyEnvironment>(), "");
- }
-
- /** Rewrite the entire database on disk, with the exception of key pszSkip if non-zero
- */
- bool Rewrite(const char* pszSkip=nullptr);
-
- /** Back up the entire database to a file.
- */
- bool Backup(const std::string& strDest) const;
-
- /** Make sure all changes are flushed to disk.
- */
- void Flush(bool shutdown);
-
- void IncrementUpdateCounter();
-
- void ReloadDbEnv();
-
- std::atomic<unsigned int> nUpdateCounter;
- unsigned int nLastSeen;
- unsigned int nLastFlushed;
- int64_t nLastWalletUpdate;
-
- /**
- * Pointer to shared database environment.
- *
- * Normally there is only one BerkeleyDatabase object per
- * BerkeleyEnvivonment, but in the special, backwards compatible case where
- * multiple wallet BDB data files are loaded from the same directory, this
- * will point to a shared instance that gets freed when the last data file
- * is closed.
- */
- std::shared_ptr<BerkeleyEnvironment> env;
-
- /** Database pointer. This is initialized lazily and reset during flushes, so it can be null. */
- std::unique_ptr<Db> m_db;
-
-private:
- std::string strFile;
-
- /** Return whether this database handle is a dummy for testing.
- * Only to be used at a low level, application should ideally not care
- * about this.
- */
- bool IsDummy() const { return env == nullptr; }
-};
-
-/** RAII class that provides access to a Berkeley database */
-class BerkeleyBatch
-{
- /** RAII class that automatically cleanses its data on destruction */
- class SafeDbt final
- {
- Dbt m_dbt;
-
- public:
- // construct Dbt with internally-managed data
- SafeDbt();
- // construct Dbt with provided data
- SafeDbt(void* data, size_t size);
- ~SafeDbt();
-
- // delegate to Dbt
- const void* get_data() const;
- u_int32_t get_size() const;
-
- // conversion operator to access the underlying Dbt
- operator Dbt*();
- };
-
-protected:
- Db* pdb;
- std::string strFile;
- DbTxn* activeTxn;
- bool fReadOnly;
- bool fFlushOnClose;
- BerkeleyEnvironment *env;
-
-public:
- explicit BerkeleyBatch(BerkeleyDatabase& database, const char* pszMode = "r+", bool fFlushOnCloseIn=true);
- ~BerkeleyBatch() { Close(); }
-
- BerkeleyBatch(const BerkeleyBatch&) = delete;
- BerkeleyBatch& operator=(const BerkeleyBatch&) = delete;
-
- void Flush();
- void Close();
-
- /* flush the wallet passively (TRY_LOCK)
- ideal to be called periodically */
- static bool PeriodicFlush(BerkeleyDatabase& database);
- /* verifies the database environment */
- static bool VerifyEnvironment(const fs::path& file_path, bilingual_str& errorStr);
- /* verifies the database file */
- static bool VerifyDatabaseFile(const fs::path& file_path, bilingual_str& errorStr);
-
- template <typename K, typename T>
- bool Read(const K& key, T& value)
- {
- if (!pdb)
- return false;
-
- // Key
- CDataStream ssKey(SER_DISK, CLIENT_VERSION);
- ssKey.reserve(1000);
- ssKey << key;
- SafeDbt datKey(ssKey.data(), ssKey.size());
-
- // Read
- SafeDbt datValue;
- int ret = pdb->get(activeTxn, datKey, datValue, 0);
- bool success = false;
- if (datValue.get_data() != nullptr) {
- // Unserialize value
- try {
- CDataStream ssValue((char*)datValue.get_data(), (char*)datValue.get_data() + datValue.get_size(), SER_DISK, CLIENT_VERSION);
- ssValue >> value;
- success = true;
- } catch (const std::exception&) {
- // In this case success remains 'false'
- }
- }
- return ret == 0 && success;
- }
-
- template <typename K, typename T>
- bool Write(const K& key, const T& value, bool fOverwrite = true)
- {
- if (!pdb)
- return true;
- if (fReadOnly)
- assert(!"Write called on database in read-only mode");
-
- // Key
- CDataStream ssKey(SER_DISK, CLIENT_VERSION);
- ssKey.reserve(1000);
- ssKey << key;
- SafeDbt datKey(ssKey.data(), ssKey.size());
-
- // Value
- CDataStream ssValue(SER_DISK, CLIENT_VERSION);
- ssValue.reserve(10000);
- ssValue << value;
- SafeDbt datValue(ssValue.data(), ssValue.size());
-
- // Write
- int ret = pdb->put(activeTxn, datKey, datValue, (fOverwrite ? 0 : DB_NOOVERWRITE));
- return (ret == 0);
- }
-
- template <typename K>
- bool Erase(const K& key)
- {
- if (!pdb)
- return false;
- if (fReadOnly)
- assert(!"Erase called on database in read-only mode");
-
- // Key
- CDataStream ssKey(SER_DISK, CLIENT_VERSION);
- ssKey.reserve(1000);
- ssKey << key;
- SafeDbt datKey(ssKey.data(), ssKey.size());
-
- // Erase
- int ret = pdb->del(activeTxn, datKey, 0);
- return (ret == 0 || ret == DB_NOTFOUND);
- }
-
- template <typename K>
- bool Exists(const K& key)
- {
- if (!pdb)
- return false;
-
- // Key
- CDataStream ssKey(SER_DISK, CLIENT_VERSION);
- ssKey.reserve(1000);
- ssKey << key;
- SafeDbt datKey(ssKey.data(), ssKey.size());
-
- // Exists
- int ret = pdb->exists(activeTxn, datKey, 0);
- return (ret == 0);
- }
-
- Dbc* GetCursor()
- {
- if (!pdb)
- return nullptr;
- Dbc* pcursor = nullptr;
- int ret = pdb->cursor(nullptr, &pcursor, 0);
- if (ret != 0)
- return nullptr;
- return pcursor;
- }
-
- int ReadAtCursor(Dbc* pcursor, CDataStream& ssKey, CDataStream& ssValue)
- {
- // Read at cursor
- SafeDbt datKey;
- SafeDbt datValue;
- int ret = pcursor->get(datKey, datValue, DB_NEXT);
- if (ret != 0)
- return ret;
- else if (datKey.get_data() == nullptr || datValue.get_data() == nullptr)
- return 99999;
-
- // Convert to streams
- ssKey.SetType(SER_DISK);
- ssKey.clear();
- ssKey.write((char*)datKey.get_data(), datKey.get_size());
- ssValue.SetType(SER_DISK);
- ssValue.clear();
- ssValue.write((char*)datValue.get_data(), datValue.get_size());
- return 0;
- }
-
- bool TxnBegin()
- {
- if (!pdb || activeTxn)
- return false;
- DbTxn* ptxn = env->TxnBegin();
- if (!ptxn)
- return false;
- activeTxn = ptxn;
- return true;
- }
-
- bool TxnCommit()
- {
- if (!pdb || !activeTxn)
- return false;
- int ret = activeTxn->commit(0);
- activeTxn = nullptr;
- return (ret == 0);
- }
-
- bool TxnAbort()
- {
- if (!pdb || !activeTxn)
- return false;
- int ret = activeTxn->abort();
- activeTxn = nullptr;
- return (ret == 0);
- }
-
- bool static Rewrite(BerkeleyDatabase& database, const char* pszSkip = nullptr);
-};
-
-std::string BerkeleyDatabaseVersion();
+void SplitWalletPath(const fs::path& wallet_path, fs::path& env_directory, std::string& database_filename);
#endif // BITCOIN_WALLET_DB_H
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index d5f6d63a46..d9fe741a6e 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -92,12 +92,6 @@ static void RescanWallet(CWallet& wallet, const WalletRescanReserver& reserver,
UniValue importprivkey(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"importprivkey",
"\nAdds a private key (as returned by dumpprivkey) to your wallet. Requires a new wallet backup.\n"
"Hint: use importmulti to import more than one private key.\n"
@@ -124,6 +118,10 @@ UniValue importprivkey(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import private keys to a wallet with private keys disabled");
}
@@ -195,12 +193,6 @@ UniValue importprivkey(const JSONRPCRequest& request)
UniValue abortrescan(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"abortrescan",
"\nStops current wallet rescan triggered by an RPC call, e.g. by an importprivkey call.\n"
"Note: Use \"getwalletinfo\" to query the scanning progress.\n",
@@ -216,6 +208,10 @@ UniValue abortrescan(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
if (!pwallet->IsScanning() || pwallet->IsAbortingRescan()) return false;
pwallet->AbortRescan();
return true;
@@ -223,12 +219,6 @@ UniValue abortrescan(const JSONRPCRequest& request)
UniValue importaddress(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"importaddress",
"\nAdds an address or script (in hex) that can be watched as if it were in your wallet but cannot be used to spend. Requires a new wallet backup.\n"
"\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
@@ -255,6 +245,10 @@ UniValue importaddress(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
EnsureLegacyScriptPubKeyMan(*pwallet, true);
std::string strLabel;
@@ -325,12 +319,6 @@ UniValue importaddress(const JSONRPCRequest& request)
UniValue importprunedfunds(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"importprunedfunds",
"\nImports funds without rescan. Corresponding address or script must previously be included in wallet. Aimed towards pruned wallets. The end-user is responsible to import additional transactions that subsequently spend the imported outputs or rescan after the point in the blockchain the transaction is included.\n",
{
@@ -341,6 +329,10 @@ UniValue importprunedfunds(const JSONRPCRequest& request)
RPCExamples{""},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
CMutableTransaction tx;
if (!DecodeHexTx(tx, request.params[0].get_str()))
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
@@ -383,12 +375,6 @@ UniValue importprunedfunds(const JSONRPCRequest& request)
UniValue removeprunedfunds(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"removeprunedfunds",
"\nDeletes the specified transaction from the wallet. Meant for use with pruned wallets and as a companion to importprunedfunds. This will affect wallet balances.\n",
{
@@ -402,6 +388,10 @@ UniValue removeprunedfunds(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
LOCK(pwallet->cs_wallet);
uint256 hash(ParseHashV(request.params[0], "txid"));
@@ -422,12 +412,6 @@ UniValue removeprunedfunds(const JSONRPCRequest& request)
UniValue importpubkey(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"importpubkey",
"\nAdds a public key (in hex) that can be watched as if it were in your wallet but cannot be used to spend. Requires a new wallet backup.\n"
"Hint: use importmulti to import more than one public key.\n"
@@ -450,6 +434,10 @@ UniValue importpubkey(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
EnsureLegacyScriptPubKeyMan(*wallet, true);
std::string strLabel;
@@ -509,12 +497,6 @@ UniValue importpubkey(const JSONRPCRequest& request)
UniValue importwallet(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"importwallet",
"\nImports keys from a wallet dump file (see dumpwallet). Requires a new wallet backup to include imported keys.\n"
"Note: Use \"getwalletinfo\" to query the scanning progress.\n",
@@ -532,6 +514,10 @@ UniValue importwallet(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
EnsureLegacyScriptPubKeyMan(*wallet, true);
if (pwallet->chain().havePruned()) {
@@ -667,12 +653,6 @@ UniValue importwallet(const JSONRPCRequest& request)
UniValue dumpprivkey(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"dumpprivkey",
"\nReveals the private key corresponding to 'address'.\n"
"Then the importprivkey can be used with this output\n",
@@ -689,6 +669,10 @@ UniValue dumpprivkey(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*wallet);
LOCK2(pwallet->cs_wallet, spk_man.cs_KeyStore);
@@ -714,11 +698,6 @@ UniValue dumpprivkey(const JSONRPCRequest& request)
UniValue dumpwallet(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
- if (!EnsureWalletIsAvailable(pwallet.get(), request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"dumpwallet",
"\nDumps all wallet keys in a human-readable format to a server-side file. This does not allow overwriting existing files.\n"
"Imported scripts are included in the dumpfile, but corresponding BIP173 addresses, etc. may not be added automatically by importwallet.\n"
@@ -739,6 +718,9 @@ UniValue dumpwallet(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
+
CWallet& wallet = *pwallet;
LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(wallet);
@@ -1259,12 +1241,6 @@ static int64_t GetImportTimestamp(const UniValue& data, int64_t now)
UniValue importmulti(const JSONRPCRequest& mainRequest)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(mainRequest);
- CWallet* const pwallet = wallet.get();
- if (!EnsureWalletIsAvailable(pwallet, mainRequest.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"importmulti",
"\nImport addresses/scripts (with private or public keys, redeem script (P2SH)), optionally rescanning the blockchain from the earliest creation time of the imported scripts. Requires a new wallet backup.\n"
"If an address/script is imported without all of the private keys required to spend from that address, it will be watchonly. The 'watchonly' option must be set to true in this case or a warning will be returned.\n"
@@ -1340,6 +1316,9 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
},
}.Check(mainRequest);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(mainRequest);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
RPCTypeCheck(mainRequest.params, {UniValue::VARR, UniValue::VOBJ});
@@ -1585,14 +1564,8 @@ static UniValue ProcessDescriptorImport(CWallet * const pwallet, const UniValue&
return result;
}
-UniValue importdescriptors(const JSONRPCRequest& main_request) {
- // Acquire the wallet
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(main_request);
- CWallet* const pwallet = wallet.get();
- if (!EnsureWalletIsAvailable(pwallet, main_request.fHelp)) {
- return NullUniValue;
- }
-
+UniValue importdescriptors(const JSONRPCRequest& main_request)
+{
RPCHelpMan{"importdescriptors",
"\nImport descriptors. This will trigger a rescan of the blockchain based on the earliest timestamp of all descriptors being imported. Requires a new wallet backup.\n"
"\nNote: This call can take over an hour to complete if using an early timestamp; during that time, other rpc calls\n"
@@ -1644,6 +1617,10 @@ UniValue importdescriptors(const JSONRPCRequest& main_request) {
},
}.Check(main_request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(main_request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
// Make sure wallet is a descriptor wallet
if (!pwallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
throw JSONRPCError(RPC_WALLET_ERROR, "importdescriptors is not available for non-descriptor wallets");
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index ae2b19e923..c3a64cf46a 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -93,6 +93,7 @@ bool GetWalletNameFromJSONRPCRequest(const JSONRPCRequest& request, std::string&
std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& request)
{
+ CHECK_NONFATAL(!request.fHelp);
std::string wallet_name;
if (GetWalletNameFromJSONRPCRequest(request, wallet_name)) {
std::shared_ptr<CWallet> pwallet = GetWallet(wallet_name);
@@ -101,14 +102,11 @@ std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& reques
}
std::vector<std::shared_ptr<CWallet>> wallets = GetWallets();
- return wallets.size() == 1 || (request.fHelp && wallets.size() > 0) ? wallets[0] : nullptr;
-}
+ if (wallets.size() == 1) {
+ return wallets[0];
+ }
-bool EnsureWalletIsAvailable(const CWallet* pwallet, bool avoidException)
-{
- if (pwallet) return true;
- if (avoidException) return false;
- if (!HasWallets()) {
+ if (wallets.empty()) {
throw JSONRPCError(
RPC_METHOD_NOT_FOUND, "Method not found (wallet method is disabled because no wallet is loaded)");
}
@@ -195,13 +193,6 @@ static std::string LabelFromValue(const UniValue& value)
static UniValue getnewaddress(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"getnewaddress",
"\nReturns a new Bitcoin address for receiving payments.\n"
"If 'label' is specified, it is added to the address book \n"
@@ -219,6 +210,10 @@ static UniValue getnewaddress(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
LOCK(pwallet->cs_wallet);
if (!pwallet->CanGetAddresses()) {
@@ -248,13 +243,6 @@ static UniValue getnewaddress(const JSONRPCRequest& request)
static UniValue getrawchangeaddress(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"getrawchangeaddress",
"\nReturns a new Bitcoin address, for receiving change.\n"
"This is for use with raw transactions, NOT normal use.\n",
@@ -270,6 +258,10 @@ static UniValue getrawchangeaddress(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
LOCK(pwallet->cs_wallet);
if (!pwallet->CanGetAddresses(true)) {
@@ -294,13 +286,6 @@ static UniValue getrawchangeaddress(const JSONRPCRequest& request)
static UniValue setlabel(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"setlabel",
"\nSets the label associated with the given address.\n",
{
@@ -314,6 +299,10 @@ static UniValue setlabel(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
LOCK(pwallet->cs_wallet);
CTxDestination dest = DecodeDestination(request.params[0].get_str());
@@ -366,13 +355,6 @@ static CTransactionRef SendMoney(CWallet* const pwallet, const CTxDestination& a
static UniValue sendtoaddress(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"sendtoaddress",
"\nSend an amount to a given address." +
HELP_REQUIRING_PASSPHRASE,
@@ -406,6 +388,10 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
@@ -461,13 +447,6 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
static UniValue listaddressgroupings(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"listaddressgroupings",
"\nLists groups of addresses which have had their common ownership\n"
"made public by common use as inputs or as the resulting change\n"
@@ -493,6 +472,10 @@ static UniValue listaddressgroupings(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
@@ -523,13 +506,6 @@ static UniValue listaddressgroupings(const JSONRPCRequest& request)
static UniValue signmessage(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"signmessage",
"\nSign a message with the private key of an address" +
HELP_REQUIRING_PASSPHRASE,
@@ -552,6 +528,10 @@ static UniValue signmessage(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
LOCK(pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet);
@@ -628,13 +608,6 @@ static CAmount GetReceived(const CWallet& wallet, const UniValue& params, bool b
static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"getreceivedbyaddress",
"\nReturns the total amount received by the given address in transactions with at least minconf confirmations.\n",
{
@@ -656,6 +629,10 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
@@ -668,13 +645,6 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
static UniValue getreceivedbylabel(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"getreceivedbylabel",
"\nReturns the total amount received by addresses with <label> in transactions with at least [minconf] confirmations.\n",
{
@@ -696,6 +666,10 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
@@ -708,13 +682,6 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request)
static UniValue getbalance(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"getbalance",
"\nReturns the total available balance.\n"
"The available balance is what the wallet considers currently spendable, and is\n"
@@ -738,6 +705,10 @@ static UniValue getbalance(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
@@ -765,13 +736,6 @@ static UniValue getbalance(const JSONRPCRequest& request)
static UniValue getunconfirmedbalance(const JSONRPCRequest &request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"getunconfirmedbalance",
"DEPRECATED\nIdentical to getbalances().mine.untrusted_pending\n",
{},
@@ -779,6 +743,10 @@ static UniValue getunconfirmedbalance(const JSONRPCRequest &request)
RPCExamples{""},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
@@ -791,13 +759,6 @@ static UniValue getunconfirmedbalance(const JSONRPCRequest &request)
static UniValue sendmany(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"sendmany",
"\nSend multiple times. Amounts are double-precision floating point numbers." +
HELP_REQUIRING_PASSPHRASE,
@@ -841,6 +802,10 @@ static UniValue sendmany(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
@@ -925,13 +890,6 @@ static UniValue sendmany(const JSONRPCRequest& request)
static UniValue addmultisigaddress(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"addmultisigaddress",
"\nAdd an nrequired-to-sign multisignature address to the wallet. Requires a new wallet backup.\n"
"Each key is a Bitcoin address or hex-encoded public key.\n"
@@ -964,6 +922,10 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet);
LOCK2(pwallet->cs_wallet, spk_man.cs_KeyStore);
@@ -1167,13 +1129,6 @@ static UniValue ListReceived(const CWallet* const pwallet, const UniValue& param
static UniValue listreceivedbyaddress(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"listreceivedbyaddress",
"\nList balances by receiving address.\n",
{
@@ -1207,6 +1162,10 @@ static UniValue listreceivedbyaddress(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
@@ -1218,13 +1177,6 @@ static UniValue listreceivedbyaddress(const JSONRPCRequest& request)
static UniValue listreceivedbylabel(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"listreceivedbylabel",
"\nList received transactions by label.\n",
{
@@ -1251,6 +1203,10 @@ static UniValue listreceivedbylabel(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
@@ -1379,13 +1335,6 @@ static const std::vector<RPCResult> TransactionDescriptionString()
UniValue listtransactions(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"listtransactions",
"\nIf a label name is provided, this will return only incoming transactions paying to addresses with the specified label.\n"
"\nReturns up to 'count' most recent transactions skipping the first 'from' transactions.\n",
@@ -1433,6 +1382,10 @@ UniValue listtransactions(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
@@ -1492,12 +1445,6 @@ UniValue listtransactions(const JSONRPCRequest& request)
static UniValue listsinceblock(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
-
- if (!EnsureWalletIsAvailable(pwallet.get(), request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"listsinceblock",
"\nGet all transactions in blocks since block [blockhash], or all transactions if omitted.\n"
"If \"blockhash\" is no longer a part of the main chain, transactions from the fork point onward are included.\n"
@@ -1551,6 +1498,9 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
+
const CWallet& wallet = *pwallet;
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
@@ -1633,13 +1583,6 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
static UniValue gettransaction(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"gettransaction",
"\nGet detailed information about in-wallet transaction <txid>\n",
{
@@ -1694,6 +1637,10 @@ static UniValue gettransaction(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
@@ -1746,13 +1693,6 @@ static UniValue gettransaction(const JSONRPCRequest& request)
static UniValue abandontransaction(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"abandontransaction",
"\nMark in-wallet transaction <txid> as abandoned\n"
"This will mark this transaction and all its in-wallet descendants as abandoned which will allow\n"
@@ -1769,6 +1709,10 @@ static UniValue abandontransaction(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
@@ -1790,13 +1734,6 @@ static UniValue abandontransaction(const JSONRPCRequest& request)
static UniValue backupwallet(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"backupwallet",
"\nSafely copies current wallet file to destination, which can be a directory or a path with filename.\n",
{
@@ -1809,6 +1746,10 @@ static UniValue backupwallet(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
@@ -1826,13 +1767,6 @@ static UniValue backupwallet(const JSONRPCRequest& request)
static UniValue keypoolrefill(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"keypoolrefill",
"\nFills the keypool."+
HELP_REQUIRING_PASSPHRASE,
@@ -1846,6 +1780,10 @@ static UniValue keypoolrefill(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
if (pwallet->IsLegacy() && pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet");
}
@@ -1873,13 +1811,6 @@ static UniValue keypoolrefill(const JSONRPCRequest& request)
static UniValue walletpassphrase(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"walletpassphrase",
"\nStores the wallet decryption key in memory for 'timeout' seconds.\n"
"This is needed prior to performing transactions related to private keys such as sending bitcoins\n"
@@ -1901,6 +1832,10 @@ static UniValue walletpassphrase(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
int64_t nSleepTime;
int64_t relock_time;
// Prevent concurrent calls to walletpassphrase with the same wallet.
@@ -1970,13 +1905,6 @@ static UniValue walletpassphrase(const JSONRPCRequest& request)
static UniValue walletpassphrasechange(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"walletpassphrasechange",
"\nChanges the wallet passphrase from 'oldpassphrase' to 'newpassphrase'.\n",
{
@@ -1990,6 +1918,10 @@ static UniValue walletpassphrasechange(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
LOCK(pwallet->cs_wallet);
if (!pwallet->IsCrypted()) {
@@ -2020,13 +1952,6 @@ static UniValue walletpassphrasechange(const JSONRPCRequest& request)
static UniValue walletlock(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"walletlock",
"\nRemoves the wallet encryption key from memory, locking the wallet.\n"
"After calling this method, you will need to call walletpassphrase again\n"
@@ -2045,6 +1970,10 @@ static UniValue walletlock(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
LOCK(pwallet->cs_wallet);
if (!pwallet->IsCrypted()) {
@@ -2060,13 +1989,6 @@ static UniValue walletlock(const JSONRPCRequest& request)
static UniValue encryptwallet(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"encryptwallet",
"\nEncrypts the wallet with 'passphrase'. This is for first time encryption.\n"
"After this, any calls that interact with private keys such as sending or signing \n"
@@ -2091,6 +2013,10 @@ static UniValue encryptwallet(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
LOCK(pwallet->cs_wallet);
if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
@@ -2120,13 +2046,6 @@ static UniValue encryptwallet(const JSONRPCRequest& request)
static UniValue lockunspent(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"lockunspent",
"\nUpdates list of temporarily unspendable outputs.\n"
"Temporarily lock (unlock=false) or unlock (unlock=true) specified transaction outputs.\n"
@@ -2165,6 +2084,10 @@ static UniValue lockunspent(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
@@ -2246,13 +2169,6 @@ static UniValue lockunspent(const JSONRPCRequest& request)
static UniValue listlockunspent(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"listlockunspent",
"\nReturns list of temporarily unspendable outputs.\n"
"See the lockunspent call to lock and unlock transactions for spending.\n",
@@ -2281,6 +2197,10 @@ static UniValue listlockunspent(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
LOCK(pwallet->cs_wallet);
std::vector<COutPoint> vOutpts;
@@ -2301,13 +2221,6 @@ static UniValue listlockunspent(const JSONRPCRequest& request)
static UniValue settxfee(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"settxfee",
"\nSet the transaction fee per kB for this wallet. Overrides the global -paytxfee command line parameter.\n"
"Can be deactivated by passing 0 as the fee. In that case automatic fee selection will be used by default.\n",
@@ -2323,6 +2236,10 @@ static UniValue settxfee(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
LOCK(pwallet->cs_wallet);
CAmount nAmount = AmountFromValue(request.params[0]);
@@ -2344,12 +2261,6 @@ static UniValue settxfee(const JSONRPCRequest& request)
static UniValue getbalances(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const rpc_wallet = GetWalletForJSONRPCRequest(request);
- if (!EnsureWalletIsAvailable(rpc_wallet.get(), request.fHelp)) {
- return NullUniValue;
- }
- CWallet& wallet = *rpc_wallet;
-
RPCHelpMan{
"getbalances",
"Returns an object with all balances in " + CURRENCY_UNIT + ".\n",
@@ -2377,6 +2288,10 @@ static UniValue getbalances(const JSONRPCRequest& request)
HelpExampleRpc("getbalances", "")},
}.Check(request);
+ std::shared_ptr<CWallet> const rpc_wallet = GetWalletForJSONRPCRequest(request);
+ if (!rpc_wallet) return NullUniValue;
+ CWallet& wallet = *rpc_wallet;
+
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
wallet.BlockUntilSyncedToCurrentChain();
@@ -2411,13 +2326,6 @@ static UniValue getbalances(const JSONRPCRequest& request)
static UniValue getwalletinfo(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"getwalletinfo",
"Returns an object containing various wallet state info.\n",
{},
@@ -2453,6 +2361,10 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
@@ -2560,12 +2472,7 @@ static UniValue listwallets(const JSONRPCRequest& request)
UniValue obj(UniValue::VARR);
for (const std::shared_ptr<CWallet>& wallet : GetWallets()) {
- if (!EnsureWalletIsAvailable(wallet.get(), request.fHelp)) {
- return NullUniValue;
- }
-
LOCK(wallet->cs_wallet);
-
obj.push_back(wallet->GetName());
}
@@ -2621,13 +2528,6 @@ static UniValue loadwallet(const JSONRPCRequest& request)
static UniValue setwalletflag(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
std::string flags = "";
for (auto& it : WALLET_FLAG_MAP)
if (it.second & MUTABLE_WALLET_FLAGS)
@@ -2652,6 +2552,10 @@ static UniValue setwalletflag(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
std::string flag_str = request.params[0].get_str();
bool value = request.params[1].isNull() || request.params[1].get_bool();
@@ -2804,13 +2708,6 @@ static UniValue unloadwallet(const JSONRPCRequest& request)
static UniValue listunspent(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{
"listunspent",
"\nReturns array of unspent transaction outputs\n"
@@ -2868,6 +2765,10 @@ static UniValue listunspent(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
int nMinDepth = 1;
if (!request.params[0].isNull()) {
RPCTypeCheckArgument(request.params[0], UniValue::VNUM);
@@ -3137,13 +3038,6 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
static UniValue fundrawtransaction(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"fundrawtransaction",
"\nAdd inputs to a transaction until it has enough in value to meet its out value.\n"
"This will not modify existing inputs, and will add at most one change output to the outputs.\n"
@@ -3213,6 +3107,10 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
RPCTypeCheck(request.params, {UniValue::VSTR, UniValueType(), UniValue::VBOOL});
// parse hex string from parameter
@@ -3237,13 +3135,6 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"signrawtransactionwithwallet",
"\nSign inputs for raw transaction (serialized, hex-encoded).\n"
"The second optional argument (may be null) is an array of previous transaction outputs that\n"
@@ -3297,6 +3188,10 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR, UniValue::VSTR}, true);
CMutableTransaction mtx;
@@ -3331,13 +3226,6 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
static UniValue bumpfee(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
- return NullUniValue;
-
RPCHelpMan{"bumpfee",
"\nBumps the fee of an opt-in-RBF transaction T, replacing it with a new transaction B.\n"
"An opt-in RBF transaction with the given txid must be in the wallet.\n"
@@ -3389,6 +3277,10 @@ static UniValue bumpfee(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VOBJ});
uint256 hash(ParseHashV(request.params[0], "txid"));
@@ -3503,13 +3395,6 @@ static UniValue bumpfee(const JSONRPCRequest& request)
UniValue rescanblockchain(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"rescanblockchain",
"\nRescan the local blockchain for wallet related transactions.\n"
"Note: Use \"getwalletinfo\" to query the scanning progress.\n",
@@ -3530,6 +3415,10 @@ UniValue rescanblockchain(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
WalletRescanReserver reserver(*pwallet);
if (!reserver.reserve()) {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
@@ -3702,13 +3591,6 @@ static UniValue AddressBookDataToJSON(const CAddressBookData& data, const bool v
UniValue getaddressinfo(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"getaddressinfo",
"\nReturn information about the given bitcoin address.\n"
"Some of the information will only be present if the address is in the active wallet.\n",
@@ -3769,6 +3651,10 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
LOCK(pwallet->cs_wallet);
UniValue ret(UniValue::VOBJ);
@@ -3845,13 +3731,6 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
static UniValue getaddressesbylabel(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"getaddressesbylabel",
"\nReturns the list of addresses assigned the specified label.\n",
{
@@ -3872,6 +3751,10 @@ static UniValue getaddressesbylabel(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
LOCK(pwallet->cs_wallet);
std::string label = LabelFromValue(request.params[0]);
@@ -3905,13 +3788,6 @@ static UniValue getaddressesbylabel(const JSONRPCRequest& request)
static UniValue listlabels(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"listlabels",
"\nReturns the list of all labels, or labels that are assigned to addresses with a specific purpose.\n",
{
@@ -3935,6 +3811,10 @@ static UniValue listlabels(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
LOCK(pwallet->cs_wallet);
std::string purpose;
@@ -3961,13 +3841,6 @@ static UniValue listlabels(const JSONRPCRequest& request)
UniValue sethdseed(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"sethdseed",
"\nSet or generate a new HD wallet seed. Non-HD wallets will not be upgraded to being a HD wallet. Wallets that are already\n"
"HD will have a new HD seed set so that new keys added to the keypool will be derived from this new seed.\n"
@@ -3990,6 +3863,10 @@ UniValue sethdseed(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet, true);
if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
@@ -4034,13 +3911,6 @@ UniValue sethdseed(const JSONRPCRequest& request)
UniValue walletprocesspsbt(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"walletprocesspsbt",
"\nUpdate a PSBT with input information from our wallet and then sign inputs\n"
"that we can sign for." +
@@ -4069,6 +3939,10 @@ UniValue walletprocesspsbt(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL, UniValue::VSTR});
// Unserialize the transaction
@@ -4101,13 +3975,6 @@ UniValue walletprocesspsbt(const JSONRPCRequest& request)
UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"walletcreatefundedpsbt",
"\nCreates and funds a transaction in the Partially Signed Transaction format. Inputs will be added if supplied inputs are not enough\n"
"Implements the Creator and Updater roles.\n",
@@ -4182,6 +4049,10 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
RPCTypeCheck(request.params, {
UniValue::VARR,
UniValueType(), // ARR or OBJ, checked later
@@ -4226,13 +4097,6 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
static UniValue upgradewallet(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"upgradewallet",
"\nUpgrade the wallet. Upgrades to the latest version if no version number is specified\n"
"New keys may be generated and a new wallet backup will need to be made.",
@@ -4246,6 +4110,10 @@ static UniValue upgradewallet(const JSONRPCRequest& request)
}
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
RPCTypeCheck(request.params, {UniValue::VNUM}, true);
EnsureWalletIsUnlocked(pwallet);
diff --git a/src/wallet/rpcwallet.h b/src/wallet/rpcwallet.h
index d00221a04c..fb1e91282b 100644
--- a/src/wallet/rpcwallet.h
+++ b/src/wallet/rpcwallet.h
@@ -31,7 +31,6 @@ Span<const CRPCCommand> GetWalletRPCCommands();
std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& request);
void EnsureWalletIsUnlocked(const CWallet*);
-bool EnsureWalletIsAvailable(const CWallet*, bool avoidException);
WalletContext& EnsureWalletContext(const util::Ref& context);
LegacyScriptPubKeyMan& EnsureLegacyScriptPubKeyMan(CWallet& wallet, bool also_create = false);
diff --git a/src/wallet/test/db_tests.cpp b/src/wallet/test/db_tests.cpp
index f4a4c9fa7c..8f0083cd2e 100644
--- a/src/wallet/test/db_tests.cpp
+++ b/src/wallet/test/db_tests.cpp
@@ -8,7 +8,7 @@
#include <fs.h>
#include <test/util/setup_common.h>
-#include <wallet/db.h>
+#include <wallet/bdb.h>
BOOST_FIXTURE_TEST_SUITE(db_tests, BasicTestingSetup)
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index a59aa8b980..cf42c1cb3c 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -77,12 +77,6 @@ bool RemoveWallet(const std::shared_ptr<CWallet>& wallet)
return true;
}
-bool HasWallets()
-{
- LOCK(cs_wallets);
- return !vpwallets.empty();
-}
-
std::vector<std::shared_ptr<CWallet>> GetWallets()
{
LOCK(cs_wallets);
@@ -752,7 +746,6 @@ void CWallet::SetSpentKeyState(WalletBatch& batch, const uint256& hash, unsigned
bool CWallet::IsSpentKey(const uint256& hash, unsigned int n) const
{
AssertLockHeld(cs_wallet);
- CTxDestination dst;
const CWalletTx* srctx = GetWalletTx(hash);
if (srctx) {
assert(srctx->tx->vout.size() > n);
@@ -2748,6 +2741,12 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransac
// Get the fee rate to use effective values in coin selection
CFeeRate nFeeRateNeeded = GetMinimumFeeRate(*this, coin_control, &feeCalc);
+ // Do not, ever, assume that it's fine to change the fee rate if the user has explicitly
+ // provided one
+ if (coin_control.m_feerate && nFeeRateNeeded > *coin_control.m_feerate) {
+ error = strprintf(_("Fee rate (%s) is lower than the minimum fee rate setting (%s)"), coin_control.m_feerate->ToString(), nFeeRateNeeded.ToString());
+ return false;
+ }
nFeeRet = 0;
bool pick_new_inputs = true;
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 67331dc3be..cf000b0b70 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -51,7 +51,6 @@ void UnloadWallet(std::shared_ptr<CWallet>&& wallet);
bool AddWallet(const std::shared_ptr<CWallet>& wallet);
bool RemoveWallet(const std::shared_ptr<CWallet>& wallet);
-bool HasWallets();
std::vector<std::shared_ptr<CWallet>> GetWallets();
std::shared_ptr<CWallet> GetWallet(const std::string& name);
std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const WalletLocation& location, bilingual_str& error, std::vector<bilingual_str>& warnings);
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index cb516f70f0..2f6c2b883a 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -1018,3 +1018,8 @@ bool WalletBatch::TxnAbort()
{
return m_batch.TxnAbort();
}
+
+bool IsWalletLoaded(const fs::path& wallet_path)
+{
+ return IsBDBWalletLoaded(wallet_path);
+}
diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h
index b95ed24d12..1037bd579f 100644
--- a/src/wallet/walletdb.h
+++ b/src/wallet/walletdb.h
@@ -8,6 +8,7 @@
#include <amount.h>
#include <script/sign.h>
+#include <wallet/bdb.h>
#include <wallet/db.h>
#include <wallet/walletutil.h>
#include <key.h>
@@ -289,4 +290,7 @@ void MaybeCompactWalletDB();
//! Unserialize a given Key-Value pair and load it into the wallet
bool ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, std::string& strType, std::string& strErr);
+/** Return whether a wallet database is currently loaded. */
+bool IsWalletLoaded(const fs::path& wallet_path);
+
#endif // BITCOIN_WALLET_WALLETDB_H
diff --git a/test/functional/feature_backwards_compatibility.py b/test/functional/feature_backwards_compatibility.py
index 596ff206f2..0ab309f9b9 100755
--- a/test/functional/feature_backwards_compatibility.py
+++ b/test/functional/feature_backwards_compatibility.py
@@ -26,7 +26,6 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.descriptors import descsum_create
from test_framework.util import (
- adjust_bitcoin_conf_for_pre_17,
assert_equal,
sync_blocks,
sync_mempools,
@@ -60,8 +59,6 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
170100,
160300,
])
- # adapt bitcoin.conf, because older bitcoind's don't recognize config sections
- adjust_bitcoin_conf_for_pre_17(self.nodes[5].bitcoinconf)
self.start_nodes()
diff --git a/test/functional/mempool_compatibility.py b/test/functional/mempool_compatibility.py
new file mode 100755
index 0000000000..999399dec0
--- /dev/null
+++ b/test/functional/mempool_compatibility.py
@@ -0,0 +1,75 @@
+#!/usr/bin/env python3
+# Copyright (c) 2017-2020 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Test that mempool.dat is both backward and forward compatible between versions
+
+NOTE: The test is designed to prevent cases when compatibility is broken accidentally.
+In case we need to break mempool compatibility we can continue to use the test by just bumping the version number.
+
+Download node binaries:
+contrib/devtools/previous_release.sh -b v0.19.1 v0.18.1 v0.17.1 v0.16.3 v0.15.2
+
+Only v0.15.2 is required by this test. The rest is used in other backwards compatibility tests.
+"""
+
+import os
+
+from test_framework.test_framework import BitcoinTestFramework
+
+
+class MempoolCompatibilityTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 2
+
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+ self.skip_if_no_previous_releases()
+
+ def setup_network(self):
+ self.add_nodes(self.num_nodes, versions=[
+ 150200, # oldest version supported by the test framework
+ None,
+ ])
+ self.start_nodes()
+ self.import_deterministic_coinbase_privkeys()
+
+ def run_test(self):
+ self.log.info("Test that mempool.dat is compatible between versions")
+
+ old_node = self.nodes[0]
+ new_node = self.nodes[1]
+ recipient = old_node.getnewaddress()
+ self.stop_node(1)
+
+ self.log.info("Add a transaction to mempool on old node and shutdown")
+ old_tx_hash = old_node.sendtoaddress(recipient, 0.0001)
+ assert old_tx_hash in old_node.getrawmempool()
+ self.stop_node(0)
+
+ self.log.info("Move mempool.dat from old to new node")
+ old_node_mempool = os.path.join(old_node.datadir, self.chain, 'mempool.dat')
+ new_node_mempool = os.path.join(new_node.datadir, self.chain, 'mempool.dat')
+ os.rename(old_node_mempool, new_node_mempool)
+
+ self.log.info("Start new node and verify mempool contains the tx")
+ self.start_node(1)
+ assert old_tx_hash in new_node.getrawmempool()
+
+ self.log.info("Add unbroadcasted tx to mempool on new node and shutdown")
+ unbroadcasted_tx_hash = new_node.sendtoaddress(recipient, 0.0001)
+ assert unbroadcasted_tx_hash in new_node.getrawmempool()
+ mempool = new_node.getrawmempool(True)
+ assert mempool[unbroadcasted_tx_hash]['unbroadcast']
+ self.stop_node(1)
+
+ self.log.info("Move mempool.dat from new to old node")
+ os.rename(new_node_mempool, old_node_mempool)
+
+ self.log.info("Start old node again and verify mempool contains both txs")
+ self.start_node(0, ['-nowallet'])
+ assert old_tx_hash in old_node.getrawmempool()
+ assert unbroadcasted_tx_hash in old_node.getrawmempool()
+
+if __name__ == "__main__":
+ MempoolCompatibilityTest().main()
diff --git a/test/functional/p2p_compactblocks.py b/test/functional/p2p_compactblocks.py
index d77a744758..0b3738b572 100755
--- a/test/functional/p2p_compactblocks.py
+++ b/test/functional/p2p_compactblocks.py
@@ -305,10 +305,9 @@ class CompactBlocksTest(BitcoinTestFramework):
self.check_compactblock_construction_from_block(version, header_and_shortids, block_hash, block)
# Now fetch the compact block using a normal non-announce getdata
- with mininode_lock:
- test_node.clear_block_announcement()
- inv = CInv(MSG_CMPCT_BLOCK, block_hash)
- test_node.send_message(msg_getdata([inv]))
+ test_node.clear_block_announcement()
+ inv = CInv(MSG_CMPCT_BLOCK, block_hash)
+ test_node.send_message(msg_getdata([inv]))
wait_until(test_node.received_block_announcement, timeout=30, lock=mininode_lock)
diff --git a/test/functional/p2p_eviction.py b/test/functional/p2p_eviction.py
new file mode 100755
index 0000000000..b2b3a89aab
--- /dev/null
+++ b/test/functional/p2p_eviction.py
@@ -0,0 +1,129 @@
+#!/usr/bin/env python3
+# Copyright (c) 2019 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+""" Test node eviction logic
+
+When the number of peers has reached the limit of maximum connections,
+the next connecting inbound peer will trigger the eviction mechanism.
+We cannot currently test the parts of the eviction logic that are based on
+address/netgroup since in the current framework, all peers are connecting from
+the same local address. See Issue #14210 for more info.
+Therefore, this test is limited to the remaining protection criteria.
+"""
+
+import time
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.mininode import P2PInterface, P2PDataStore
+from test_framework.util import assert_equal, wait_until
+from test_framework.blocktools import create_block, create_coinbase
+from test_framework.messages import CTransaction, FromHex, msg_pong, msg_tx
+
+
+class SlowP2PDataStore(P2PDataStore):
+ def on_ping(self, message):
+ time.sleep(0.1)
+ self.send_message(msg_pong(message.nonce))
+
+class SlowP2PInterface(P2PInterface):
+ def on_ping(self, message):
+ time.sleep(0.1)
+ self.send_message(msg_pong(message.nonce))
+
+class P2PEvict(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.num_nodes = 1
+ # The choice of maxconnections=32 results in a maximum of 21 inbound connections
+ # (32 - 10 outbound - 1 feeler). 20 inbound peers are protected from eviction:
+ # 4 by netgroup, 4 that sent us blocks, 4 that sent us transactions and 8 via lowest ping time
+ self.extra_args = [['-maxconnections=32']]
+
+ def run_test(self):
+ protected_peers = set() # peers that we expect to be protected from eviction
+ current_peer = -1
+ node = self.nodes[0]
+ node.generatetoaddress(101, node.get_deterministic_priv_key().address)
+
+ self.log.info("Create 4 peers and protect them from eviction by sending us a block")
+ for _ in range(4):
+ block_peer = node.add_p2p_connection(SlowP2PDataStore())
+ current_peer += 1
+ block_peer.sync_with_ping()
+ best_block = node.getbestblockhash()
+ tip = int(best_block, 16)
+ best_block_time = node.getblock(best_block)['time']
+ block = create_block(tip, create_coinbase(node.getblockcount() + 1), best_block_time + 1)
+ block.solve()
+ block_peer.send_blocks_and_test([block], node, success=True)
+ protected_peers.add(current_peer)
+
+ self.log.info("Create 5 slow-pinging peers, making them eviction candidates")
+ for _ in range(5):
+ node.add_p2p_connection(SlowP2PInterface())
+ current_peer += 1
+
+ self.log.info("Create 4 peers and protect them from eviction by sending us a tx")
+ for i in range(4):
+ txpeer = node.add_p2p_connection(SlowP2PInterface())
+ current_peer += 1
+ txpeer.sync_with_ping()
+
+ prevtx = node.getblock(node.getblockhash(i + 1), 2)['tx'][0]
+ rawtx = node.createrawtransaction(
+ inputs=[{'txid': prevtx['txid'], 'vout': 0}],
+ outputs=[{node.get_deterministic_priv_key().address: 50 - 0.00125}],
+ )
+ sigtx = node.signrawtransactionwithkey(
+ hexstring=rawtx,
+ privkeys=[node.get_deterministic_priv_key().key],
+ prevtxs=[{
+ 'txid': prevtx['txid'],
+ 'vout': 0,
+ 'scriptPubKey': prevtx['vout'][0]['scriptPubKey']['hex'],
+ }],
+ )['hex']
+ txpeer.send_message(msg_tx(FromHex(CTransaction(), sigtx)))
+ protected_peers.add(current_peer)
+
+ self.log.info("Create 8 peers and protect them from eviction by having faster pings")
+ for _ in range(8):
+ fastpeer = node.add_p2p_connection(P2PInterface())
+ current_peer += 1
+ wait_until(lambda: "ping" in fastpeer.last_message, timeout=10)
+
+ # Make sure by asking the node what the actual min pings are
+ peerinfo = node.getpeerinfo()
+ pings = {}
+ for i in range(len(peerinfo)):
+ pings[i] = peerinfo[i]['minping'] if 'minping' in peerinfo[i] else 1000000
+ sorted_pings = sorted(pings.items(), key=lambda x: x[1])
+
+ # Usually the 8 fast peers are protected. In rare case of unreliable pings,
+ # one of the slower peers might have a faster min ping though.
+ for i in range(8):
+ protected_peers.add(sorted_pings[i][0])
+
+ self.log.info("Create peer that triggers the eviction mechanism")
+ node.add_p2p_connection(SlowP2PInterface())
+
+ # One of the non-protected peers must be evicted. We can't be sure which one because
+ # 4 peers are protected via netgroup, which is identical for all peers,
+ # and the eviction mechanism doesn't preserve the order of identical elements.
+ evicted_peers = []
+ for i in range(len(node.p2ps)):
+ if not node.p2ps[i].is_connected:
+ evicted_peers.append(i)
+
+ self.log.info("Test that one peer was evicted")
+ self.log.debug("{} evicted peer: {}".format(len(evicted_peers), set(evicted_peers)))
+ assert_equal(len(evicted_peers), 1)
+
+ self.log.info("Test that no peer expected to be protected was evicted")
+ self.log.debug("{} protected peers: {}".format(len(protected_peers), protected_peers))
+ assert evicted_peers[0] not in protected_peers
+
+if __name__ == '__main__':
+ P2PEvict().main()
diff --git a/test/functional/p2p_filter.py b/test/functional/p2p_filter.py
index 15955a938c..5726a73e40 100755
--- a/test/functional/p2p_filter.py
+++ b/test/functional/p2p_filter.py
@@ -16,13 +16,15 @@ from test_framework.messages import (
msg_filterclear,
msg_filterload,
msg_getdata,
+ msg_mempool,
+ msg_version,
)
-from test_framework.mininode import P2PInterface
+from test_framework.mininode import P2PInterface, mininode_lock
from test_framework.script import MAX_SCRIPT_ELEMENT_SIZE
from test_framework.test_framework import BitcoinTestFramework
-class FilterNode(P2PInterface):
+class P2PBloomFilter(P2PInterface):
# This is a P2SH watch-only wallet
watch_script_pubkey = 'a914ffffffffffffffffffffffffffffffffffffffff87'
# The initial filter (n=10, fp=0.000001) with just the above scriptPubKey added
@@ -34,6 +36,11 @@ class FilterNode(P2PInterface):
nFlags=1,
)
+ def __init__(self):
+ super().__init__()
+ self._tx_received = False
+ self._merkleblock_received = False
+
def on_inv(self, message):
want = msg_getdata()
for i in message.inv:
@@ -46,10 +53,30 @@ class FilterNode(P2PInterface):
self.send_message(want)
def on_merkleblock(self, message):
- self.merkleblock_received = True
+ self._merkleblock_received = True
def on_tx(self, message):
- self.tx_received = True
+ self._tx_received = True
+
+ @property
+ def tx_received(self):
+ with mininode_lock:
+ return self._tx_received
+
+ @tx_received.setter
+ def tx_received(self, value):
+ with mininode_lock:
+ self._tx_received = value
+
+ @property
+ def merkleblock_received(self):
+ with mininode_lock:
+ return self._merkleblock_received
+
+ @merkleblock_received.setter
+ def merkleblock_received(self, value):
+ with mininode_lock:
+ self._merkleblock_received = value
class FilterTest(BitcoinTestFramework):
@@ -64,95 +91,144 @@ class FilterTest(BitcoinTestFramework):
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
- def test_size_limits(self, filter_node):
+ def test_size_limits(self, filter_peer):
self.log.info('Check that too large filter is rejected')
with self.nodes[0].assert_debug_log(['Misbehaving']):
- filter_node.send_and_ping(msg_filterload(data=b'\xbb'*(MAX_BLOOM_FILTER_SIZE+1)))
+ filter_peer.send_and_ping(msg_filterload(data=b'\xbb'*(MAX_BLOOM_FILTER_SIZE+1)))
self.log.info('Check that max size filter is accepted')
with self.nodes[0].assert_debug_log([], unexpected_msgs=['Misbehaving']):
- filter_node.send_and_ping(msg_filterload(data=b'\xbb'*(MAX_BLOOM_FILTER_SIZE)))
- filter_node.send_and_ping(msg_filterclear())
+ filter_peer.send_and_ping(msg_filterload(data=b'\xbb'*(MAX_BLOOM_FILTER_SIZE)))
+ filter_peer.send_and_ping(msg_filterclear())
self.log.info('Check that filter with too many hash functions is rejected')
with self.nodes[0].assert_debug_log(['Misbehaving']):
- filter_node.send_and_ping(msg_filterload(data=b'\xaa', nHashFuncs=MAX_BLOOM_HASH_FUNCS+1))
+ filter_peer.send_and_ping(msg_filterload(data=b'\xaa', nHashFuncs=MAX_BLOOM_HASH_FUNCS+1))
self.log.info('Check that filter with max hash functions is accepted')
with self.nodes[0].assert_debug_log([], unexpected_msgs=['Misbehaving']):
- filter_node.send_and_ping(msg_filterload(data=b'\xaa', nHashFuncs=MAX_BLOOM_HASH_FUNCS))
+ filter_peer.send_and_ping(msg_filterload(data=b'\xaa', nHashFuncs=MAX_BLOOM_HASH_FUNCS))
# Don't send filterclear until next two filteradd checks are done
self.log.info('Check that max size data element to add to the filter is accepted')
with self.nodes[0].assert_debug_log([], unexpected_msgs=['Misbehaving']):
- filter_node.send_and_ping(msg_filteradd(data=b'\xcc'*(MAX_SCRIPT_ELEMENT_SIZE)))
+ filter_peer.send_and_ping(msg_filteradd(data=b'\xcc'*(MAX_SCRIPT_ELEMENT_SIZE)))
self.log.info('Check that too large data element to add to the filter is rejected')
with self.nodes[0].assert_debug_log(['Misbehaving']):
- filter_node.send_and_ping(msg_filteradd(data=b'\xcc'*(MAX_SCRIPT_ELEMENT_SIZE+1)))
+ filter_peer.send_and_ping(msg_filteradd(data=b'\xcc'*(MAX_SCRIPT_ELEMENT_SIZE+1)))
- filter_node.send_and_ping(msg_filterclear())
+ filter_peer.send_and_ping(msg_filterclear())
- def run_test(self):
- filter_node = self.nodes[0].add_p2p_connection(FilterNode())
+ def test_msg_mempool(self):
+ self.log.info("Check that a node with bloom filters enabled services p2p mempool messages")
+ filter_peer = P2PBloomFilter()
- self.test_size_limits(filter_node)
+ self.log.info("Create a tx relevant to the peer before connecting")
+ filter_address = self.nodes[0].decodescript(filter_peer.watch_script_pubkey)['addresses'][0]
+ txid = self.nodes[0].sendtoaddress(filter_address, 90)
- self.log.info('Add filtered P2P connection to the node')
- filter_node.send_and_ping(filter_node.watch_filter_init)
- filter_address = self.nodes[0].decodescript(filter_node.watch_script_pubkey)['addresses'][0]
+ self.log.info("Send a mempool msg after connecting and check that the tx is received")
+ self.nodes[0].add_p2p_connection(filter_peer)
+ filter_peer.send_and_ping(filter_peer.watch_filter_init)
+ self.nodes[0].p2p.send_message(msg_mempool())
+ filter_peer.wait_for_tx(txid)
+
+ def test_frelay_false(self, filter_peer):
+ self.log.info("Check that a node with fRelay set to false does not receive invs until the filter is set")
+ filter_peer.tx_received = False
+ filter_address = self.nodes[0].decodescript(filter_peer.watch_script_pubkey)['addresses'][0]
+ self.nodes[0].sendtoaddress(filter_address, 90)
+ # Sync to make sure the reason filter_peer doesn't receive the tx is not p2p delays
+ filter_peer.sync_with_ping()
+ assert not filter_peer.tx_received
+
+ # Clear the mempool so that this transaction does not impact subsequent tests
+ self.nodes[0].generate(1)
+
+ def test_filter(self, filter_peer):
+ # Set the bloomfilter using filterload
+ filter_peer.send_and_ping(filter_peer.watch_filter_init)
+ # If fRelay is not already True, sending filterload sets it to True
+ assert self.nodes[0].getpeerinfo()[0]['relaytxes']
+ filter_address = self.nodes[0].decodescript(filter_peer.watch_script_pubkey)['addresses'][0]
self.log.info('Check that we receive merkleblock and tx if the filter matches a tx in a block')
block_hash = self.nodes[0].generatetoaddress(1, filter_address)[0]
txid = self.nodes[0].getblock(block_hash)['tx'][0]
- filter_node.wait_for_merkleblock(block_hash)
- filter_node.wait_for_tx(txid)
+ filter_peer.wait_for_merkleblock(block_hash)
+ filter_peer.wait_for_tx(txid)
self.log.info('Check that we only receive a merkleblock if the filter does not match a tx in a block')
- filter_node.tx_received = False
+ filter_peer.tx_received = False
block_hash = self.nodes[0].generatetoaddress(1, self.nodes[0].getnewaddress())[0]
- filter_node.wait_for_merkleblock(block_hash)
- assert not filter_node.tx_received
+ filter_peer.wait_for_merkleblock(block_hash)
+ assert not filter_peer.tx_received
self.log.info('Check that we not receive a tx if the filter does not match a mempool tx')
- filter_node.merkleblock_received = False
- filter_node.tx_received = False
+ filter_peer.merkleblock_received = False
+ filter_peer.tx_received = False
self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 90)
- filter_node.sync_with_ping()
- filter_node.sync_with_ping()
- assert not filter_node.merkleblock_received
- assert not filter_node.tx_received
+ filter_peer.sync_with_ping()
+ filter_peer.sync_with_ping()
+ assert not filter_peer.merkleblock_received
+ assert not filter_peer.tx_received
- self.log.info('Check that we receive a tx in reply to a mempool msg if the filter matches a mempool tx')
- filter_node.merkleblock_received = False
+ self.log.info('Check that we receive a tx if the filter matches a mempool tx')
+ filter_peer.merkleblock_received = False
txid = self.nodes[0].sendtoaddress(filter_address, 90)
- filter_node.wait_for_tx(txid)
- assert not filter_node.merkleblock_received
+ filter_peer.wait_for_tx(txid)
+ assert not filter_peer.merkleblock_received
self.log.info('Check that after deleting filter all txs get relayed again')
- filter_node.send_and_ping(msg_filterclear())
+ filter_peer.send_and_ping(msg_filterclear())
for _ in range(5):
txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 7)
- filter_node.wait_for_tx(txid)
+ filter_peer.wait_for_tx(txid)
self.log.info('Check that request for filtered blocks is ignored if no filter is set')
- filter_node.merkleblock_received = False
- filter_node.tx_received = False
+ filter_peer.merkleblock_received = False
+ filter_peer.tx_received = False
with self.nodes[0].assert_debug_log(expected_msgs=['received getdata']):
block_hash = self.nodes[0].generatetoaddress(1, self.nodes[0].getnewaddress())[0]
- filter_node.wait_for_inv([CInv(MSG_BLOCK, int(block_hash, 16))])
- filter_node.sync_with_ping()
- assert not filter_node.merkleblock_received
- assert not filter_node.tx_received
+ filter_peer.wait_for_inv([CInv(MSG_BLOCK, int(block_hash, 16))])
+ filter_peer.sync_with_ping()
+ assert not filter_peer.merkleblock_received
+ assert not filter_peer.tx_received
self.log.info('Check that sending "filteradd" if no filter is set is treated as misbehavior')
with self.nodes[0].assert_debug_log(['Misbehaving']):
- filter_node.send_and_ping(msg_filteradd(data=b'letsmisbehave'))
+ filter_peer.send_and_ping(msg_filteradd(data=b'letsmisbehave'))
self.log.info("Check that division-by-zero remote crash bug [CVE-2013-5700] is fixed")
- filter_node.send_and_ping(msg_filterload(data=b'', nHashFuncs=1))
- filter_node.send_and_ping(msg_filteradd(data=b'letstrytocrashthisnode'))
+ filter_peer.send_and_ping(msg_filterload(data=b'', nHashFuncs=1))
+ filter_peer.send_and_ping(msg_filteradd(data=b'letstrytocrashthisnode'))
+ self.nodes[0].disconnect_p2ps()
+ def run_test(self):
+ filter_peer = self.nodes[0].add_p2p_connection(P2PBloomFilter())
+ self.log.info('Test filter size limits')
+ self.test_size_limits(filter_peer)
+
+ self.log.info('Test BIP 37 for a node with fRelay = True (default)')
+ self.test_filter(filter_peer)
+ self.nodes[0].disconnect_p2ps()
+
+ self.log.info('Test BIP 37 for a node with fRelay = False')
+ # Add peer but do not send version yet
+ filter_peer_without_nrelay = self.nodes[0].add_p2p_connection(P2PBloomFilter(), send_version=False, wait_for_verack=False)
+ # Send version with fRelay=False
+ filter_peer_without_nrelay.wait_until(lambda: filter_peer_without_nrelay.is_connected, timeout=10)
+ version_without_fRelay = msg_version()
+ version_without_fRelay.nRelay = 0
+ filter_peer_without_nrelay.send_message(version_without_fRelay)
+ filter_peer_without_nrelay.wait_for_verack()
+ assert not self.nodes[0].getpeerinfo()[0]['relaytxes']
+ self.test_frelay_false(filter_peer_without_nrelay)
+ self.test_filter(filter_peer_without_nrelay)
+
+ self.log.info('Test msg_mempool')
+ self.test_msg_mempool()
if __name__ == '__main__':
FilterTest().main()
diff --git a/test/functional/p2p_invalid_messages.py b/test/functional/p2p_invalid_messages.py
index 81302374c9..402f72685c 100755
--- a/test/functional/p2p_invalid_messages.py
+++ b/test/functional/p2p_invalid_messages.py
@@ -3,27 +3,23 @@
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test node responses to invalid network messages."""
-import asyncio
-import struct
-import sys
-
from test_framework.messages import (
CBlockHeader,
CInv,
msg_getdata,
msg_headers,
msg_inv,
- msg_ping,
MSG_TX,
ser_string,
)
from test_framework.mininode import (
- NetworkThread,
P2PDataStore,
P2PInterface,
)
from test_framework.test_framework import BitcoinTestFramework
+MSG_LIMIT = 4 * 1000 * 1000 # 4MB, per MAX_PROTOCOL_MESSAGE_LENGTH
+VALID_DATA_LIMIT = MSG_LIMIT - 5 # Account for the 5-byte length prefix
class msg_unrecognized:
"""Nonsensical message. Modeled after similar types in test_framework.messages."""
@@ -46,128 +42,20 @@ class InvalidMessagesTest(BitcoinTestFramework):
self.setup_clean_chain = True
def run_test(self):
- """
- . Test msg header
- 0. Send a bunch of large (4MB) messages of an unrecognized type. Check to see
- that it isn't an effective DoS against the node.
-
- 1. Send an oversized (4MB+) message and check that we're disconnected.
-
- 2. Send a few messages with an incorrect data size in the header, ensure the
- messages are ignored.
- """
self.test_magic_bytes()
self.test_checksum()
self.test_size()
self.test_msgtype()
self.test_large_inv()
-
- node = self.nodes[0]
- self.node = node
- node.add_p2p_connection(P2PDataStore())
- conn2 = node.add_p2p_connection(P2PDataStore())
-
- msg_limit = 4 * 1000 * 1000 # 4MB, per MAX_PROTOCOL_MESSAGE_LENGTH
- valid_data_limit = msg_limit - 5 # Account for the 4-byte length prefix
-
- #
- # 0.
- #
- # Send as large a message as is valid, ensure we aren't disconnected but
- # also can't exhaust resources.
- #
- msg_at_size = msg_unrecognized(str_data="b" * valid_data_limit)
- assert len(msg_at_size.serialize()) == msg_limit
-
- self.log.info("Sending a bunch of large, junk messages to test memory exhaustion. May take a bit...")
-
- # Run a bunch of times to test for memory exhaustion.
- for _ in range(80):
- node.p2p.send_message(msg_at_size)
-
- # Check that, even though the node is being hammered by nonsense from one
- # connection, it can still service other peers in a timely way.
- for _ in range(20):
- conn2.sync_with_ping(timeout=2)
-
- # Peer 1, despite serving up a bunch of nonsense, should still be connected.
- self.log.info("Waiting for node to drop junk messages.")
- node.p2p.sync_with_ping(timeout=400)
- assert node.p2p.is_connected
-
- #
- # 1.
- #
- # Send an oversized message, ensure we're disconnected.
- #
- # Under macOS this test is skipped due to an unexpected error code
- # returned from the closing socket which python/asyncio does not
- # yet know how to handle.
- #
- if sys.platform != 'darwin':
- msg_over_size = msg_unrecognized(str_data="b" * (valid_data_limit + 1))
- assert len(msg_over_size.serialize()) == (msg_limit + 1)
-
- # An unknown message type (or *any* message type) over
- # MAX_PROTOCOL_MESSAGE_LENGTH should result in a disconnect.
- node.p2p.send_message(msg_over_size)
- node.p2p.wait_for_disconnect(timeout=4)
-
- node.disconnect_p2ps()
- conn = node.add_p2p_connection(P2PDataStore())
- conn.wait_for_verack()
- else:
- self.log.info("Skipping test p2p_invalid_messages/1 (oversized message) under macOS")
-
- #
- # 2.
- #
- # Send messages with an incorrect data size in the header.
- #
- actual_size = 100
- msg = msg_unrecognized(str_data="b" * actual_size)
-
- # TODO: handle larger-than cases. I haven't been able to pin down what behavior to expect.
- for wrong_size in (2, 77, 78, 79):
- self.log.info("Sending a message with incorrect size of {}".format(wrong_size))
-
- # Unmodified message should submit okay.
- node.p2p.send_and_ping(msg)
-
- # A message lying about its data size results in a disconnect when the incorrect
- # data size is less than the actual size.
- #
- # TODO: why does behavior change at 78 bytes?
- #
- node.p2p.send_raw_message(self._tweak_msg_data_size(msg, wrong_size))
-
- # For some reason unknown to me, we sometimes have to push additional data to the
- # peer in order for it to realize a disconnect.
- try:
- node.p2p.send_message(msg_ping(nonce=123123))
- except IOError:
- pass
-
- node.p2p.wait_for_disconnect(timeout=10)
- node.disconnect_p2ps()
- node.add_p2p_connection(P2PDataStore())
-
- # Node is still up.
- conn = node.add_p2p_connection(P2PDataStore())
+ self.test_resource_exhaustion()
def test_magic_bytes(self):
conn = self.nodes[0].add_p2p_connection(P2PDataStore())
-
- async def swap_magic_bytes():
- conn._on_data = lambda: None # Need to ignore all incoming messages from now, since they come with "invalid" magic bytes
- conn.magic_bytes = b'\x00\x11\x22\x32'
-
- # Call .result() to block until the atomic swap is complete, otherwise
- # we might run into races later on
- asyncio.run_coroutine_threadsafe(swap_magic_bytes(), NetworkThread.network_event_loop).result()
-
- with self.nodes[0].assert_debug_log(['PROCESSMESSAGE: INVALID MESSAGESTART ping']):
- conn.send_message(msg_ping(nonce=0xff))
+ with self.nodes[0].assert_debug_log(['PROCESSMESSAGE: INVALID MESSAGESTART badmsg']):
+ msg = conn.build_message(msg_unrecognized(str_data="d"))
+ # modify magic bytes
+ msg = b'\xff' * 4 + msg[4:]
+ conn.send_raw_message(msg)
conn.wait_for_disconnect(timeout=1)
self.nodes[0].disconnect_p2ps()
@@ -175,11 +63,8 @@ class InvalidMessagesTest(BitcoinTestFramework):
conn = self.nodes[0].add_p2p_connection(P2PDataStore())
with self.nodes[0].assert_debug_log(['CHECKSUM ERROR (badmsg, 2 bytes), expected 78df0a04 was ffffffff']):
msg = conn.build_message(msg_unrecognized(str_data="d"))
- cut_len = (
- 4 + # magic
- 12 + # msgtype
- 4 #len
- )
+ # Checksum is after start bytes (4B), message type (12B), len (4B)
+ cut_len = 4 + 12 + 4
# modify checksum
msg = msg[:cut_len] + b'\xff' * 4 + msg[cut_len + 4:]
self.nodes[0].p2p.send_raw_message(msg)
@@ -189,13 +74,9 @@ class InvalidMessagesTest(BitcoinTestFramework):
def test_size(self):
conn = self.nodes[0].add_p2p_connection(P2PDataStore())
with self.nodes[0].assert_debug_log(['']):
- msg = conn.build_message(msg_unrecognized(str_data="d"))
- cut_len = (
- 4 + # magic
- 12 # msgtype
- )
- # modify len to MAX_SIZE + 1
- msg = msg[:cut_len] + struct.pack("<I", 0x02000000 + 1) + msg[cut_len + 4:]
+ # Create a message with oversized payload
+ msg = msg_unrecognized(str_data="d" * (VALID_DATA_LIMIT + 1))
+ msg = conn.build_message(msg)
self.nodes[0].p2p.send_raw_message(msg)
conn.wait_for_disconnect(timeout=1)
self.nodes[0].disconnect_p2ps()
@@ -225,25 +106,28 @@ class InvalidMessagesTest(BitcoinTestFramework):
conn.send_and_ping(msg)
self.nodes[0].disconnect_p2ps()
- def _tweak_msg_data_size(self, message, wrong_size):
- """
- Return a raw message based on another message but with an incorrect data size in
- the message header.
- """
- raw_msg = self.node.p2p.build_message(message)
-
- bad_size_bytes = struct.pack("<I", wrong_size)
- num_header_bytes_before_size = 4 + 12
-
- # Replace the correct data size in the message with an incorrect one.
- raw_msg_with_wrong_size = (
- raw_msg[:num_header_bytes_before_size] +
- bad_size_bytes +
- raw_msg[(num_header_bytes_before_size + len(bad_size_bytes)):]
- )
- assert len(raw_msg) == len(raw_msg_with_wrong_size)
-
- return raw_msg_with_wrong_size
+ def test_resource_exhaustion(self):
+ conn = self.nodes[0].add_p2p_connection(P2PDataStore())
+ conn2 = self.nodes[0].add_p2p_connection(P2PDataStore())
+ msg_at_size = msg_unrecognized(str_data="b" * VALID_DATA_LIMIT)
+ assert len(msg_at_size.serialize()) == MSG_LIMIT
+
+ self.log.info("Sending a bunch of large, junk messages to test memory exhaustion. May take a bit...")
+
+ # Run a bunch of times to test for memory exhaustion.
+ for _ in range(80):
+ conn.send_message(msg_at_size)
+
+ # Check that, even though the node is being hammered by nonsense from one
+ # connection, it can still service other peers in a timely way.
+ for _ in range(20):
+ conn2.sync_with_ping(timeout=2)
+
+ # Peer 1, despite being served up a bunch of nonsense, should still be connected.
+ self.log.info("Waiting for node to drop junk messages.")
+ conn.sync_with_ping(timeout=400)
+ assert conn.is_connected
+ self.nodes[0].disconnect_p2ps()
if __name__ == '__main__':
diff --git a/test/functional/p2p_mempool.py b/test/functional/p2p_mempool.py
deleted file mode 100755
index a8fcb181e6..0000000000
--- a/test/functional/p2p_mempool.py
+++ /dev/null
@@ -1,34 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (c) 2015-2018 The Bitcoin Core developers
-# Distributed under the MIT software license, see the accompanying
-# file COPYING or http://www.opensource.org/licenses/mit-license.php.
-"""Test p2p mempool message.
-
-Test that nodes are disconnected if they send mempool messages when bloom
-filters are not enabled.
-"""
-
-from test_framework.messages import msg_mempool
-from test_framework.mininode import P2PInterface
-from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal
-
-class P2PMempoolTests(BitcoinTestFramework):
- def set_test_params(self):
- self.setup_clean_chain = True
- self.num_nodes = 1
- self.extra_args = [["-peerbloomfilters=0"]]
-
- def run_test(self):
- # Add a p2p connection
- self.nodes[0].add_p2p_connection(P2PInterface())
-
- #request mempool
- self.nodes[0].p2p.send_message(msg_mempool())
- self.nodes[0].p2p.wait_for_disconnect()
-
- #mininode must be disconnected at this point
- assert_equal(len(self.nodes[0].getpeerinfo()), 0)
-
-if __name__ == '__main__':
- P2PMempoolTests().main()
diff --git a/test/functional/p2p_nobloomfilter_messages.py b/test/functional/p2p_nobloomfilter_messages.py
new file mode 100755
index 0000000000..8478a752e7
--- /dev/null
+++ b/test/functional/p2p_nobloomfilter_messages.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python3
+# Copyright (c) 2015-2018 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Test invalid p2p messages for nodes with bloom filters disabled.
+
+Test that, when bloom filters are not enabled, nodes are disconnected if:
+1. They send a p2p mempool message
+2. They send a p2p filterload message
+3. They send a p2p filteradd message
+4. They send a p2p filterclear message
+"""
+
+from test_framework.messages import msg_mempool, msg_filteradd, msg_filterload, msg_filterclear
+from test_framework.mininode import P2PInterface
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_equal
+
+
+class P2PNobloomfilterMessages(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.num_nodes = 1
+ self.extra_args = [["-peerbloomfilters=0"]]
+
+ def test_message_causes_disconnect(self, message):
+ # Add a p2p connection that sends a message and check that it disconnects
+ peer = self.nodes[0].add_p2p_connection(P2PInterface())
+ peer.send_message(message)
+ peer.wait_for_disconnect()
+ assert_equal(len(self.nodes[0].getpeerinfo()), 0)
+
+ def run_test(self):
+ self.log.info("Test that node is disconnected if it sends mempool message")
+ self.test_message_causes_disconnect(msg_mempool())
+
+ self.log.info("Test that node is disconnected if it sends filterload message")
+ self.test_message_causes_disconnect(msg_filterload())
+
+ self.log.info("Test that node is disconnected if it sends filteradd message")
+ self.test_message_causes_disconnect(msg_filteradd(data=b'\xcc'))
+
+ self.log.info("Test that peer is disconnected if it sends a filterclear message")
+ self.test_message_causes_disconnect(msg_filterclear())
+
+if __name__ == '__main__':
+ P2PNobloomfilterMessages().main()
diff --git a/test/functional/rpc_createmultisig.py b/test/functional/rpc_createmultisig.py
index 56e9ecfcc2..3c81a4a4e2 100755
--- a/test/functional/rpc_createmultisig.py
+++ b/test/functional/rpc_createmultisig.py
@@ -3,21 +3,21 @@
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test multisig RPCs"""
+import binascii
+import decimal
+import itertools
+import json
+import os
from test_framework.authproxy import JSONRPCException
from test_framework.descriptors import descsum_create, drop_origins
+from test_framework.key import ECPubKey, ECKey
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_raises_rpc_error,
assert_equal,
)
-from test_framework.key import ECPubKey, ECKey, bytes_to_wif
-
-import binascii
-import decimal
-import itertools
-import json
-import os
+from test_framework.wallet_util import bytes_to_wif
class RpcCreateMultiSigTest(BitcoinTestFramework):
def set_test_params(self):
diff --git a/test/functional/rpc_help.py b/test/functional/rpc_help.py
index 027ae368e7..9b981b864e 100755
--- a/test/functional/rpc_help.py
+++ b/test/functional/rpc_help.py
@@ -18,6 +18,8 @@ class HelpRpcTest(BitcoinTestFramework):
def run_test(self):
self.test_categories()
self.dump_help()
+ if self.is_wallet_compiled():
+ self.wallet_help()
def test_categories(self):
node = self.nodes[0]
@@ -53,6 +55,11 @@ class HelpRpcTest(BitcoinTestFramework):
# Make sure the node can generate the help at runtime without crashing
f.write(self.nodes[0].help(call))
+ def wallet_help(self):
+ assert 'getnewaddress ( "label" "address_type" )' in self.nodes[0].help('getnewaddress')
+ self.restart_node(0, extra_args=['-nowallet=1'])
+ assert 'getnewaddress ( "label" "address_type" )' in self.nodes[0].help('getnewaddress')
+
if __name__ == '__main__':
HelpRpcTest().main()
diff --git a/test/functional/test_framework/address.py b/test/functional/test_framework/address.py
index 822a7f9763..9506b63f82 100644
--- a/test/functional/test_framework/address.py
+++ b/test/functional/test_framework/address.py
@@ -5,13 +5,15 @@
"""Encode and decode BASE58, P2PKH and P2SH addresses."""
import enum
+import unittest
-from .base58 import byte_to_base58
-from .script import hash160, sha256, CScript, OP_0
+from .script import hash256, hash160, sha256, CScript, OP_0
from .util import hex_str_to_bytes
from . import segwit_addr
+from test_framework.util import assert_equal
+
ADDRESS_BCRT1_UNSPENDABLE = 'bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3xueyj'
ADDRESS_BCRT1_UNSPENDABLE_DESCRIPTOR = 'addr(bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3xueyj)#juyq9d97'
# Coins sent to this address can be spent with a witness stack of just OP_TRUE
@@ -23,6 +25,52 @@ class AddressType(enum.Enum):
p2sh_segwit = 'p2sh-segwit'
legacy = 'legacy' # P2PKH
+
+chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
+
+
+def byte_to_base58(b, version):
+ result = ''
+ str = b.hex()
+ str = chr(version).encode('latin-1').hex() + str
+ checksum = hash256(hex_str_to_bytes(str)).hex()
+ str += checksum[:8]
+ value = int('0x'+str,0)
+ while value > 0:
+ result = chars[value % 58] + result
+ value //= 58
+ while (str[:2] == '00'):
+ result = chars[0] + result
+ str = str[2:]
+ return result
+
+
+def base58_to_byte(s, verify_checksum=True):
+ if not s:
+ return b''
+ n = 0
+ for c in s:
+ n *= 58
+ assert c in chars
+ digit = chars.index(c)
+ n += digit
+ h = '%x' % n
+ if len(h) % 2:
+ h = '0' + h
+ res = n.to_bytes((n.bit_length() + 7) // 8, 'big')
+ pad = 0
+ for c in s:
+ if c == chars[0]:
+ pad += 1
+ else:
+ break
+ res = b'\x00' * pad + res
+ if verify_checksum:
+ assert_equal(hash256(res[:-4])[:4], res[-4:])
+
+ return res[1:-4], int(res[0])
+
+
def keyhash_to_p2pkh(hash, main = False):
assert len(hash) == 20
version = 0 if main else 111
@@ -80,3 +128,22 @@ def check_script(script):
if (type(script) is bytes or type(script) is CScript):
return script
assert False
+
+
+class TestFrameworkScript(unittest.TestCase):
+ def test_base58encodedecode(self):
+ def check_base58(data, version):
+ self.assertEqual(base58_to_byte(byte_to_base58(data, version)), (data, version))
+
+ check_base58(b'\x1f\x8e\xa1p*{\xd4\x94\x1b\xca\tA\xb8R\xc4\xbb\xfe\xdb.\x05', 111)
+ check_base58(b':\x0b\x05\xf4\xd7\xf6l;\xa7\x00\x9fE50)l\x84\\\xc9\xcf', 111)
+ check_base58(b'A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 111)
+ check_base58(b'\0A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 111)
+ check_base58(b'\0\0A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 111)
+ check_base58(b'\0\0\0A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 111)
+ check_base58(b'\x1f\x8e\xa1p*{\xd4\x94\x1b\xca\tA\xb8R\xc4\xbb\xfe\xdb.\x05', 0)
+ check_base58(b':\x0b\x05\xf4\xd7\xf6l;\xa7\x00\x9fE50)l\x84\\\xc9\xcf', 0)
+ check_base58(b'A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 0)
+ check_base58(b'\0A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 0)
+ check_base58(b'\0\0A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 0)
+ check_base58(b'\0\0\0A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 0)
diff --git a/test/functional/test_framework/base58.py b/test/functional/test_framework/base58.py
deleted file mode 100644
index 3dab3569d0..0000000000
--- a/test/functional/test_framework/base58.py
+++ /dev/null
@@ -1,70 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (c) 2016-2020 The Bitcoin Core developers
-# Distributed under the MIT software license, see the accompanying
-# file COPYING or http://www.opensource.org/licenses/mit-license.php.
-"""Encode BASE58."""
-
-import unittest
-
-from .messages import hash256
-from .util import hex_str_to_bytes, assert_equal
-
-chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
-
-def byte_to_base58(b, version):
- result = ''
- str = b.hex()
- str = chr(version).encode('latin-1').hex() + str
- checksum = hash256(hex_str_to_bytes(str)).hex()
- str += checksum[:8]
- value = int('0x'+str,0)
- while value > 0:
- result = chars[value % 58] + result
- value //= 58
- while (str[:2] == '00'):
- result = chars[0] + result
- str = str[2:]
- return result
-
-def base58_to_byte(s, verify_checksum=True):
- if not s:
- return b''
- n = 0
- for c in s:
- n *= 58
- assert c in chars
- digit = chars.index(c)
- n += digit
- h = '%x' % n
- if len(h) % 2:
- h = '0' + h
- res = n.to_bytes((n.bit_length() + 7) // 8, 'big')
- pad = 0
- for c in s:
- if c == chars[0]:
- pad += 1
- else:
- break
- res = b'\x00' * pad + res
- if verify_checksum:
- assert_equal(hash256(res[:-4])[:4], res[-4:])
-
- return res[1:-4], int(res[0])
-
-class TestFrameworkScript(unittest.TestCase):
- def test_base58encodedecode(self):
- def check_base58(data, version):
- self.assertEqual(base58_to_byte(byte_to_base58(data, version)), (data, version))
-
- check_base58(b'\x1f\x8e\xa1p*{\xd4\x94\x1b\xca\tA\xb8R\xc4\xbb\xfe\xdb.\x05', 111)
- check_base58(b':\x0b\x05\xf4\xd7\xf6l;\xa7\x00\x9fE50)l\x84\\\xc9\xcf', 111)
- check_base58(b'A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 111)
- check_base58(b'\0A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 111)
- check_base58(b'\0\0A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 111)
- check_base58(b'\0\0\0A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 111)
- check_base58(b'\x1f\x8e\xa1p*{\xd4\x94\x1b\xca\tA\xb8R\xc4\xbb\xfe\xdb.\x05', 0)
- check_base58(b':\x0b\x05\xf4\xd7\xf6l;\xa7\x00\x9fE50)l\x84\\\xc9\xcf', 0)
- check_base58(b'A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 0)
- check_base58(b'\0A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 0)
- check_base58(b'\0\0A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 0)
- check_base58(b'\0\0\0A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 0)
diff --git a/test/functional/test_framework/key.py b/test/functional/test_framework/key.py
index 5a6d0b912b..912c0ca978 100644
--- a/test/functional/test_framework/key.py
+++ b/test/functional/test_framework/key.py
@@ -8,8 +8,6 @@ keys, and is trivially vulnerable to side channel attacks. Do not use for
anything but tests."""
import random
-from .base58 import byte_to_base58
-
def modinv(a, n):
"""Compute the modular inverse of a modulo n
@@ -386,14 +384,3 @@ class ECKey():
rb = r.to_bytes((r.bit_length() + 8) // 8, 'big')
sb = s.to_bytes((s.bit_length() + 8) // 8, 'big')
return b'\x30' + bytes([4 + len(rb) + len(sb), 2, len(rb)]) + rb + bytes([2, len(sb)]) + sb
-
-def bytes_to_wif(b, compressed=True):
- if compressed:
- b += b'\x01'
- return byte_to_base58(b, 239)
-
-def generate_wif_key():
- # Makes a WIF privkey for imports
- k = ECKey()
- k.generate()
- return bytes_to_wif(k.get_bytes(), k.is_compressed)
diff --git a/test/functional/test_framework/mininode.py b/test/functional/test_framework/mininode.py
index 45063aaff2..b6c37bc7e0 100755
--- a/test/functional/test_framework/mininode.py
+++ b/test/functional/test_framework/mininode.py
@@ -492,7 +492,7 @@ class P2PInterface(P2PConnection):
# P2PConnection acquires this lock whenever delivering a message to a P2PInterface.
# This lock should be acquired in the thread running the test logic to synchronize
# access to any data shared with the P2PInterface or P2PConnection.
-mininode_lock = threading.RLock()
+mininode_lock = threading.Lock()
class NetworkThread(threading.Thread):
@@ -658,8 +658,6 @@ class P2PTxInvStore(P2PInterface):
# save txid
self.tx_invs_received[i.hash] += 1
- super().on_inv(message)
-
def get_invs(self):
with mininode_lock:
return list(self.tx_invs_received.keys())
@@ -669,6 +667,6 @@ class P2PTxInvStore(P2PInterface):
The mempool should mark unbroadcast=False for these transactions.
"""
# Wait until invs have been received (and getdatas sent) for each txid.
- self.wait_until(lambda: set(self.get_invs()) == set([int(tx, 16) for tx in txns]), timeout)
+ self.wait_until(lambda: set(self.tx_invs_received.keys()) == set([int(tx, 16) for tx in txns]), timeout)
# Flush messages and wait for the getdatas to be processed
self.sync_with_ping()
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index 9f5e9e5f0d..c9fad91481 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -452,7 +452,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
assert_equal(len(binary), num_nodes)
assert_equal(len(binary_cli), num_nodes)
for i in range(num_nodes):
- self.nodes.append(TestNode(
+ test_node_i = TestNode(
i,
get_datadir_path(self.options.tmpdir, i),
chain=self.chain,
@@ -470,7 +470,15 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
start_perf=self.options.perf,
use_valgrind=self.options.valgrind,
descriptors=self.options.descriptors,
- ))
+ )
+ self.nodes.append(test_node_i)
+ if not test_node_i.version_is_at_least(170000):
+ # adjust conf for pre 17
+ conf_file = test_node_i.bitcoinconf
+ with open(conf_file, 'r', encoding='utf8') as conf:
+ conf_data = conf.read()
+ with open(conf_file, 'w', encoding='utf8') as conf:
+ conf.write(conf_data.replace('[regtest]', ''))
def start_node(self, i, *args, **kwargs):
"""Start a bitcoind"""
diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py
index 52306c8c3d..17b2cbb971 100644
--- a/test/functional/test_framework/util.py
+++ b/test/functional/test_framework/util.py
@@ -25,6 +25,7 @@ logger = logging.getLogger("TestFramework.utils")
# Assert functions
##################
+
def assert_approx(v, vexp, vspan=0.00001):
"""Assert that `v` is within `vspan` of `vexp`"""
if v < vexp - vspan:
@@ -32,6 +33,7 @@ def assert_approx(v, vexp, vspan=0.00001):
if v > vexp + vspan:
raise AssertionError("%s > [%s..%s]" % (str(v), str(vexp - vspan), str(vexp + vspan)))
+
def assert_fee_amount(fee, tx_size, fee_per_kB):
"""Assert the fee was in range"""
target_fee = round(tx_size * fee_per_kB / 1000, 8)
@@ -41,21 +43,26 @@ def assert_fee_amount(fee, tx_size, fee_per_kB):
if fee > (tx_size + 2) * fee_per_kB / 1000:
raise AssertionError("Fee of %s BTC too high! (Should be %s BTC)" % (str(fee), str(target_fee)))
+
def assert_equal(thing1, thing2, *args):
if thing1 != thing2 or any(thing1 != arg for arg in args):
raise AssertionError("not(%s)" % " == ".join(str(arg) for arg in (thing1, thing2) + args))
+
def assert_greater_than(thing1, thing2):
if thing1 <= thing2:
raise AssertionError("%s <= %s" % (str(thing1), str(thing2)))
+
def assert_greater_than_or_equal(thing1, thing2):
if thing1 < thing2:
raise AssertionError("%s < %s" % (str(thing1), str(thing2)))
+
def assert_raises(exc, fun, *args, **kwds):
assert_raises_message(exc, None, fun, *args, **kwds)
+
def assert_raises_message(exc, message, fun, *args, **kwds):
try:
fun(*args, **kwds)
@@ -71,6 +78,7 @@ def assert_raises_message(exc, message, fun, *args, **kwds):
else:
raise AssertionError("No exception raised")
+
def assert_raises_process_error(returncode, output, fun, *args, **kwds):
"""Execute a process and asserts the process return code and output.
@@ -95,6 +103,7 @@ def assert_raises_process_error(returncode, output, fun, *args, **kwds):
else:
raise AssertionError("No exception raised")
+
def assert_raises_rpc_error(code, message, fun, *args, **kwds):
"""Run an RPC and verify that a specific JSONRPC exception code and message is raised.
@@ -113,6 +122,7 @@ def assert_raises_rpc_error(code, message, fun, *args, **kwds):
"""
assert try_rpc(code, message, fun, *args, **kwds), "No exception raised"
+
def try_rpc(code, message, fun, *args, **kwds):
"""Tries to run an rpc command.
@@ -134,22 +144,22 @@ def try_rpc(code, message, fun, *args, **kwds):
else:
return False
+
def assert_is_hex_string(string):
try:
int(string, 16)
except Exception as e:
- raise AssertionError(
- "Couldn't interpret %r as hexadecimal; raised: %s" % (string, e))
+ raise AssertionError("Couldn't interpret %r as hexadecimal; raised: %s" % (string, e))
+
def assert_is_hash_string(string, length=64):
if not isinstance(string, str):
raise AssertionError("Expected a string, got type %r" % type(string))
elif length and len(string) != length:
- raise AssertionError(
- "String of length %d expected; got %d" % (length, len(string)))
+ raise AssertionError("String of length %d expected; got %d" % (length, len(string)))
elif not re.match('[abcdef0-9]+$', string):
- raise AssertionError(
- "String %r contains invalid characters for a hash." % string)
+ raise AssertionError("String %r contains invalid characters for a hash." % string)
+
def assert_array_result(object_array, to_match, expected, should_not_find=False):
"""
@@ -180,9 +190,11 @@ def assert_array_result(object_array, to_match, expected, should_not_find=False)
if num_matched > 0 and should_not_find:
raise AssertionError("Objects were found %s" % (str(to_match)))
+
# Utility functions
###################
+
def check_json_precision():
"""Make sure json library being used does not lose precision converting BTC values"""
n = Decimal("20000000.00000003")
@@ -190,11 +202,13 @@ def check_json_precision():
if satoshis != 2000000000000003:
raise RuntimeError("JSON encode/decode loses precision")
+
def EncodeDecimal(o):
if isinstance(o, Decimal):
return str(o)
raise TypeError(repr(o) + " is not JSON serializable")
+
def count_bytes(hex_string):
return len(bytearray.fromhex(hex_string))
@@ -202,12 +216,15 @@ def count_bytes(hex_string):
def hex_str_to_bytes(hex_str):
return unhexlify(hex_str.encode('ascii'))
+
def str_to_b64str(string):
return b64encode(string.encode('utf-8')).decode('ascii')
+
def satoshi_round(amount):
return Decimal(amount).quantize(Decimal('0.00000001'), rounding=ROUND_DOWN)
+
def wait_until(predicate, *, attempts=float('inf'), timeout=float('inf'), lock=None, timeout_factor=1.0):
if attempts == float('inf') and timeout == float('inf'):
timeout = 60
@@ -235,6 +252,7 @@ def wait_until(predicate, *, attempts=float('inf'), timeout=float('inf'), lock=N
raise AssertionError("Predicate {} not true after {} seconds".format(predicate_source, timeout))
raise RuntimeError('Unreachable')
+
# RPC/P2P connection constants and functions
############################################
@@ -250,6 +268,7 @@ class PortSeed:
# Must be initialized with a unique integer for each process
n = None
+
def get_rpc_proxy(url, node_number, *, timeout=None, coveragedir=None):
"""
Args:
@@ -271,18 +290,20 @@ def get_rpc_proxy(url, node_number, *, timeout=None, coveragedir=None):
proxy = AuthServiceProxy(url, **proxy_kwargs)
proxy.url = url # store URL on proxy for info
- coverage_logfile = coverage.get_filename(
- coveragedir, node_number) if coveragedir else None
+ coverage_logfile = coverage.get_filename(coveragedir, node_number) if coveragedir else None
return coverage.AuthServiceProxyWrapper(proxy, coverage_logfile)
+
def p2p_port(n):
assert n <= MAX_NODES
return PORT_MIN + n + (MAX_NODES * PortSeed.n) % (PORT_RANGE - 1 - MAX_NODES)
+
def rpc_port(n):
return PORT_MIN + PORT_RANGE + n + (MAX_NODES * PortSeed.n) % (PORT_RANGE - 1 - MAX_NODES)
+
def rpc_url(datadir, i, chain, rpchost):
rpc_u, rpc_p = get_auth_cookie(datadir, chain)
host = '127.0.0.1'
@@ -295,9 +316,11 @@ def rpc_url(datadir, i, chain, rpchost):
host = rpchost
return "http://%s:%s@%s:%d" % (rpc_u, rpc_p, host, int(port))
+
# Node functions
################
+
def initialize_datadir(dirname, n, chain):
datadir = get_datadir_path(dirname, n)
if not os.path.isdir(datadir):
@@ -327,21 +350,17 @@ def initialize_datadir(dirname, n, chain):
os.makedirs(os.path.join(datadir, 'stdout'), exist_ok=True)
return datadir
-def adjust_bitcoin_conf_for_pre_17(conf_file):
- with open(conf_file,'r', encoding='utf8') as conf:
- conf_data = conf.read()
- with open(conf_file, 'w', encoding='utf8') as conf:
- conf_data_changed = conf_data.replace('[regtest]', '')
- conf.write(conf_data_changed)
def get_datadir_path(dirname, n):
return os.path.join(dirname, "node" + str(n))
+
def append_config(datadir, options):
with open(os.path.join(datadir, "bitcoin.conf"), 'a', encoding='utf8') as f:
for option in options:
f.write(option + "\n")
+
def get_auth_cookie(datadir, chain):
user = None
password = None
@@ -366,20 +385,24 @@ def get_auth_cookie(datadir, chain):
raise ValueError("No RPC credentials")
return user, password
+
# If a cookie file exists in the given datadir, delete it.
def delete_cookie_file(datadir, chain):
if os.path.isfile(os.path.join(datadir, chain, ".cookie")):
logger.debug("Deleting leftover cookie file")
os.remove(os.path.join(datadir, chain, ".cookie"))
+
def softfork_active(node, key):
"""Return whether a softfork is active."""
return node.getblockchaininfo()['softforks'][key]['active']
+
def set_node_times(nodes, t):
for node in nodes:
node.setmocktime(t)
+
def disconnect_nodes(from_connection, node_num):
def get_peer_ids():
result = []
@@ -392,7 +415,7 @@ def disconnect_nodes(from_connection, node_num):
if not peer_ids:
logger.warning("disconnect_nodes: {} and {} were not connected".format(
from_connection.index,
- node_num
+ node_num,
))
return
for peer_id in peer_ids:
@@ -402,12 +425,13 @@ def disconnect_nodes(from_connection, node_num):
# If this node is disconnected between calculating the peer id
# and issuing the disconnect, don't worry about it.
# This avoids a race condition if we're mass-disconnecting peers.
- if e.error['code'] != -29: # RPC_CLIENT_NODE_NOT_CONNECTED
+ if e.error['code'] != -29: # RPC_CLIENT_NODE_NOT_CONNECTED
raise
# wait to disconnect
wait_until(lambda: not get_peer_ids(), timeout=5)
+
def connect_nodes(from_connection, node_num):
ip_port = "127.0.0.1:" + str(p2p_port(node_num))
from_connection.addnode(ip_port, "onetry")
@@ -479,6 +503,7 @@ def find_output(node, txid, amount, *, blockhash=None):
return i
raise RuntimeError("find_output txid %s : %s not found" % (txid, str(amount)))
+
def gather_inputs(from_node, amount_needed, confirmations_required=1):
"""
Return a random set of unspent txouts that are enough to pay amount_needed
@@ -496,6 +521,7 @@ def gather_inputs(from_node, amount_needed, confirmations_required=1):
raise RuntimeError("Insufficient funds: need %d, have %d" % (amount_needed, total_in))
return (total_in, inputs)
+
def make_change(from_node, amount_in, amount_out, fee):
"""
Create change output(s), return them
@@ -513,6 +539,7 @@ def make_change(from_node, amount_in, amount_out, fee):
outputs[from_node.getnewaddress()] = change
return outputs
+
def random_transaction(nodes, amount, min_fee, fee_increment, fee_variants):
"""
Create a random transaction.
@@ -532,6 +559,7 @@ def random_transaction(nodes, amount, min_fee, fee_increment, fee_variants):
return (txid, signresult["hex"], fee)
+
# Helper to create at least "count" utxos
# Pass in a fee that is sufficient for relay and mining new transactions.
def create_confirmed_utxos(fee, node, count):
@@ -564,6 +592,7 @@ def create_confirmed_utxos(fee, node, count):
assert len(utxos) >= count
return utxos
+
# Create large OP_RETURN txouts that can be appended to a transaction
# to make it large (helper for constructing large transactions).
def gen_return_txouts():
@@ -583,6 +612,7 @@ def gen_return_txouts():
txouts.append(txout)
return txouts
+
# Create a spend of each passed-in utxo, splicing in "txouts" to each raw
# transaction to make it large. See gen_return_txouts() above.
def create_lots_of_big_transactions(node, txouts, utxos, num, fee):
@@ -606,6 +636,7 @@ def create_lots_of_big_transactions(node, txouts, utxos, num, fee):
txids.append(txid)
return txids
+
def mine_large_block(node, utxos=None):
# generate a 66k transaction,
# and 14 of them is close to the 1MB block limit
@@ -619,6 +650,7 @@ def mine_large_block(node, utxos=None):
create_lots_of_big_transactions(node, txouts, utxos, num, fee=fee)
node.generate(1)
+
def find_vout_for_address(node, txid, addr):
"""
Locate the vout index of the given transaction sending to the
diff --git a/test/functional/test_framework/wallet_util.py b/test/functional/test_framework/wallet_util.py
index 1b6686ff45..b9c0fb6691 100755
--- a/test/functional/test_framework/wallet_util.py
+++ b/test/functional/test_framework/wallet_util.py
@@ -6,6 +6,7 @@
from collections import namedtuple
from test_framework.address import (
+ byte_to_base58,
key_to_p2pkh,
key_to_p2sh_p2wpkh,
key_to_p2wpkh,
@@ -13,10 +14,7 @@ from test_framework.address import (
script_to_p2sh_p2wsh,
script_to_p2wsh,
)
-from test_framework.key import (
- bytes_to_wif,
- ECKey,
-)
+from test_framework.key import ECKey
from test_framework.script import (
CScript,
OP_0,
@@ -120,3 +118,14 @@ def test_address(node, address, **kwargs):
raise AssertionError("key {} unexpectedly returned in getaddressinfo.".format(key))
elif addr_info[key] != value:
raise AssertionError("key {} value {} did not match expected value {}".format(key, addr_info[key], value))
+
+def bytes_to_wif(b, compressed=True):
+ if compressed:
+ b += b'\x01'
+ return byte_to_base58(b, 239)
+
+def generate_wif_key():
+ # Makes a WIF privkey for imports
+ k = ECKey()
+ k.generate()
+ return bytes_to_wif(k.get_bytes(), k.is_compressed)
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 0acd1e552a..79a172706c 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -67,7 +67,7 @@ TEST_EXIT_PASSED = 0
TEST_EXIT_SKIPPED = 77
TEST_FRAMEWORK_MODULES = [
- "base58",
+ "address",
"blocktools",
"script",
]
@@ -164,7 +164,7 @@ BASE_SCRIPTS = [
'wallet_keypool.py',
'wallet_keypool.py --descriptors',
'wallet_descriptor.py',
- 'p2p_mempool.py',
+ 'p2p_nobloomfilter_messages.py',
'p2p_filter.py',
'rpc_setban.py',
'p2p_blocksonly.py',
@@ -190,6 +190,7 @@ BASE_SCRIPTS = [
'rpc_preciousblock.py',
'wallet_importprunedfunds.py',
'p2p_leak_tx.py',
+ 'p2p_eviction.py',
'rpc_signmessage.py',
'rpc_generateblock.py',
'wallet_balance.py',
@@ -232,6 +233,7 @@ BASE_SCRIPTS = [
'feature_includeconf.py',
'feature_asmap.py',
'mempool_unbroadcast.py',
+ 'mempool_compatibility.py',
'rpc_deriveaddresses.py',
'rpc_deriveaddresses.py --usecli',
'rpc_scantxoutset.py',
diff --git a/test/functional/wallet_upgradewallet.py b/test/functional/wallet_upgradewallet.py
index bb81746715..cc2139a027 100755
--- a/test/functional/wallet_upgradewallet.py
+++ b/test/functional/wallet_upgradewallet.py
@@ -16,7 +16,6 @@ import shutil
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
- adjust_bitcoin_conf_for_pre_17,
assert_equal,
assert_greater_than,
assert_is_hex_string,
@@ -46,9 +45,6 @@ class UpgradeWalletTest(BitcoinTestFramework):
160300,
150200,
])
- # adapt bitcoin.conf, because older bitcoind's don't recognize config sections
- adjust_bitcoin_conf_for_pre_17(self.nodes[1].bitcoinconf)
- adjust_bitcoin_conf_for_pre_17(self.nodes[2].bitcoinconf)
self.start_nodes()
def dumb_sync_blocks(self):