aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.appveyor.yml6
-rw-r--r--REVIEWERS3
-rw-r--r--build-aux/m4/bitcoin_qt.m42
-rw-r--r--build_msvc/bitcoin-qt/bitcoin-qt.vcxproj1
-rw-r--r--build_msvc/common.qt.init.vcxproj2
-rw-r--r--build_msvc/libsecp256k1/libsecp256k1.vcxproj9
-rwxr-xr-xci/test/00_setup_env_arm.sh2
-rwxr-xr-xci/test/00_setup_env_i686_centos.sh2
-rwxr-xr-xci/test/00_setup_env_mac.sh2
-rwxr-xr-xci/test/00_setup_env_mac_host.sh2
-rwxr-xr-xci/test/00_setup_env_native_asan.sh2
-rwxr-xr-xci/test/00_setup_env_native_fuzz.sh2
-rwxr-xr-xci/test/00_setup_env_native_multiprocess.sh2
-rwxr-xr-xci/test/00_setup_env_native_nowallet.sh2
-rwxr-xr-xci/test/00_setup_env_native_qt5.sh2
-rwxr-xr-xci/test/00_setup_env_native_tsan.sh2
-rwxr-xr-xci/test/00_setup_env_s390x.sh2
-rw-r--r--configure.ac26
-rw-r--r--contrib/devtools/pixie.py2
-rwxr-xr-xcontrib/devtools/symbol-check.py52
-rwxr-xr-xcontrib/devtools/test-symbol-check.py35
-rw-r--r--contrib/gitian-descriptors/gitian-linux.yml2
-rw-r--r--contrib/gitian-descriptors/gitian-osx-signer.yml2
-rw-r--r--contrib/gitian-descriptors/gitian-osx.yml2
-rw-r--r--contrib/gitian-descriptors/gitian-win.yml2
-rwxr-xr-xcontrib/guix/guix-attest169
-rwxr-xr-xcontrib/guix/guix-build22
-rwxr-xr-xcontrib/guix/guix-verify109
-rwxr-xr-xcontrib/guix/libexec/build.sh27
-rwxr-xr-xcontrib/guix/libexec/codesign.sh19
-rw-r--r--contrib/guix/manifest.scm12
-rw-r--r--contrib/signet/README.md33
-rwxr-xr-xcontrib/signet/miner24
-rw-r--r--depends/packages/qt.mk3
-rw-r--r--depends/patches/qt/fix_limits_header.patch44
-rw-r--r--doc/developer-notes.md7
-rw-r--r--doc/release-notes.md11
-rw-r--r--doc/tor.md10
-rw-r--r--src/Makefile.am1
-rw-r--r--src/Makefile.test.include6
-rw-r--r--src/addrdb.cpp22
-rw-r--r--src/addrman.cpp44
-rw-r--r--src/addrman.h96
-rw-r--r--src/bench/coin_selection.cpp2
-rw-r--r--src/bench/duplicate_inputs.cpp1
-rw-r--r--src/bitcoin-tx.cpp3
-rw-r--r--src/bitcoin-util.cpp99
-rw-r--r--src/bitcoind.cpp2
-rw-r--r--src/chain.h2
-rw-r--r--src/core_read.cpp1
-rw-r--r--src/external_signer.cpp8
-rw-r--r--src/external_signer.h16
-rw-r--r--src/index/base.cpp1
-rw-r--r--src/init.cpp24
-rw-r--r--src/init.h2
-rw-r--r--src/init/bitcoin-node.cpp2
-rw-r--r--src/init/bitcoind.cpp2
-rw-r--r--src/interfaces/chain.h3
-rw-r--r--src/interfaces/node.h4
-rw-r--r--src/interfaces/wallet.h10
-rw-r--r--src/key.cpp21
-rw-r--r--src/key.h12
-rw-r--r--src/miner.cpp12
-rw-r--r--src/net.cpp128
-rw-r--r--src/net.h26
-rw-r--r--src/net_processing.cpp127
-rw-r--r--src/netaddress.cpp2
-rw-r--r--src/netaddress.h19
-rw-r--r--src/node/blockstorage.cpp1
-rw-r--r--src/node/coin.cpp1
-rw-r--r--src/node/coinstats.cpp1
-rw-r--r--src/node/context.cpp1
-rw-r--r--src/node/context.h2
-rw-r--r--src/node/interfaces.cpp71
-rw-r--r--src/node/psbt.cpp6
-rw-r--r--src/node/transaction.cpp1
-rw-r--r--src/policy/packages.cpp62
-rw-r--r--src/policy/packages.h10
-rw-r--r--src/protocol.h83
-rw-r--r--src/psbt.cpp31
-rw-r--r--src/psbt.h11
-rw-r--r--src/pubkey.cpp4
-rw-r--r--src/pubkey.h10
-rw-r--r--src/qt/addressbookpage.cpp8
-rw-r--r--src/qt/bitcoin.cpp3
-rw-r--r--src/qt/bitcoingui.cpp27
-rw-r--r--src/qt/coincontroldialog.cpp12
-rw-r--r--src/qt/createwalletdialog.cpp67
-rw-r--r--src/qt/createwalletdialog.h4
-rw-r--r--src/qt/forms/createwalletdialog.ui11
-rw-r--r--src/qt/forms/debugwindow.ui56
-rw-r--r--src/qt/forms/optionsdialog.ui30
-rw-r--r--src/qt/forms/receiverequestdialog.ui13
-rw-r--r--src/qt/optionsdialog.cpp7
-rw-r--r--src/qt/optionsmodel.cpp15
-rw-r--r--src/qt/optionsmodel.h1
-rw-r--r--src/qt/overviewpage.cpp9
-rw-r--r--src/qt/overviewpage.h1
-rw-r--r--src/qt/peertablemodel.cpp97
-rw-r--r--src/qt/peertablemodel.h21
-rw-r--r--src/qt/psbtoperationsdialog.cpp6
-rw-r--r--src/qt/qrimagewidget.cpp4
-rw-r--r--src/qt/receivecoinsdialog.cpp10
-rw-r--r--src/qt/receiverequestdialog.cpp6
-rw-r--r--src/qt/rpcconsole.cpp68
-rw-r--r--src/qt/rpcconsole.h5
-rw-r--r--src/qt/sendcoinsdialog.cpp84
-rw-r--r--src/qt/test/apptests.cpp5
-rw-r--r--src/qt/test/wallettests.cpp4
-rw-r--r--src/qt/transactionview.cpp20
-rw-r--r--src/qt/walletcontroller.cpp13
-rw-r--r--src/qt/walletframe.cpp36
-rw-r--r--src/qt/walletframe.h11
-rw-r--r--src/qt/walletmodel.cpp14
-rw-r--r--src/qt/walletmodel.h1
-rw-r--r--src/qt/walletmodeltransaction.cpp5
-rw-r--r--src/qt/walletmodeltransaction.h2
-rw-r--r--src/qt/walletview.cpp7
-rw-r--r--src/qt/walletview.h3
-rw-r--r--src/rest.cpp2
-rw-r--r--src/rpc/blockchain.cpp1
-rw-r--r--src/rpc/mining.cpp2
-rw-r--r--src/rpc/net.cpp2
-rw-r--r--src/rpc/rawtransaction.cpp16
-rw-r--r--src/script/descriptor.cpp4
-rw-r--r--src/script/interpreter.cpp14
-rw-r--r--src/script/interpreter.h5
-rw-r--r--src/script/sign.cpp195
-rw-r--r--src/script/sign.h9
-rw-r--r--src/script/signingprovider.cpp13
-rw-r--r--src/script/signingprovider.h4
-rw-r--r--src/script/standard.cpp57
-rw-r--r--src/script/standard.h46
-rw-r--r--src/secp256k1/.cirrus.yml198
-rw-r--r--src/secp256k1/.travis.yml108
-rw-r--r--src/secp256k1/Makefile.am8
-rw-r--r--src/secp256k1/README.md4
-rw-r--r--src/secp256k1/build-aux/m4/ax_prog_cc_for_build.m42
-rw-r--r--src/secp256k1/build-aux/m4/bitcoin_secp.m413
-rwxr-xr-xsrc/secp256k1/ci/cirrus.sh (renamed from src/secp256k1/contrib/travis.sh)52
-rw-r--r--src/secp256k1/ci/linux-debian.Dockerfile13
-rw-r--r--src/secp256k1/configure.ac279
-rw-r--r--src/secp256k1/contrib/lax_der_parsing.c10
-rw-r--r--src/secp256k1/contrib/lax_der_parsing.h10
-rw-r--r--src/secp256k1/contrib/lax_der_privatekey_parsing.c10
-rw-r--r--src/secp256k1/contrib/lax_der_privatekey_parsing.h10
-rw-r--r--src/secp256k1/doc/safegcd_implementation.md765
-rw-r--r--src/secp256k1/include/secp256k1.h47
-rw-r--r--src/secp256k1/include/secp256k1_extrakeys.h13
-rw-r--r--src/secp256k1/include/secp256k1_recovery.h24
-rw-r--r--src/secp256k1/sage/gen_exhaustive_groups.sage7
-rw-r--r--src/secp256k1/sage/gen_split_lambda_constants.sage114
-rw-r--r--src/secp256k1/sage/group_prover.sage23
-rw-r--r--src/secp256k1/sage/prove_group_implementations.sage (renamed from src/secp256k1/sage/secp256k1.sage)0
-rw-r--r--src/secp256k1/sage/secp256k1_params.sage36
-rw-r--r--src/secp256k1/sage/weierstrass_prover.sage32
-rw-r--r--src/secp256k1/src/asm/field_10x26_arm.s10
-rw-r--r--src/secp256k1/src/assumptions.h10
-rw-r--r--src/secp256k1/src/basic-config.h29
-rw-r--r--src/secp256k1/src/bench.h10
-rw-r--r--src/secp256k1/src/bench_ecdh.c10
-rw-r--r--src/secp256k1/src/bench_ecmult.c11
-rw-r--r--src/secp256k1/src/bench_internal.c73
-rw-r--r--src/secp256k1/src/bench_recover.c10
-rw-r--r--src/secp256k1/src/bench_schnorrsig.c10
-rw-r--r--src/secp256k1/src/bench_sign.c18
-rw-r--r--src/secp256k1/src/bench_verify.c26
-rw-r--r--src/secp256k1/src/ecdsa.h10
-rw-r--r--src/secp256k1/src/ecdsa_impl.h10
-rw-r--r--src/secp256k1/src/eckey.h10
-rw-r--r--src/secp256k1/src/eckey_impl.h10
-rw-r--r--src/secp256k1/src/ecmult.h11
-rw-r--r--src/secp256k1/src/ecmult_const.h10
-rw-r--r--src/secp256k1/src/ecmult_const_impl.h10
-rw-r--r--src/secp256k1/src/ecmult_gen.h10
-rw-r--r--src/secp256k1/src/ecmult_gen_impl.h12
-rw-r--r--src/secp256k1/src/ecmult_impl.h16
-rw-r--r--src/secp256k1/src/field.h29
-rw-r--r--src/secp256k1/src/field_10x26.h10
-rw-r--r--src/secp256k1/src/field_10x26_impl.h103
-rw-r--r--src/secp256k1/src/field_5x52.h10
-rw-r--r--src/secp256k1/src/field_5x52_asm_impl.h10
-rw-r--r--src/secp256k1/src/field_5x52_impl.h91
-rw-r--r--src/secp256k1/src/field_5x52_int128_impl.h10
-rw-r--r--src/secp256k1/src/field_impl.h190
-rw-r--r--src/secp256k1/src/gen_context.c23
-rw-r--r--src/secp256k1/src/group.h25
-rw-r--r--src/secp256k1/src/group_impl.h37
-rw-r--r--src/secp256k1/src/hash.h10
-rw-r--r--src/secp256k1/src/hash_impl.h10
-rw-r--r--src/secp256k1/src/modinv32.h42
-rw-r--r--src/secp256k1/src/modinv32_impl.h587
-rw-r--r--src/secp256k1/src/modinv64.h46
-rw-r--r--src/secp256k1/src/modinv64_impl.h593
-rw-r--r--src/secp256k1/src/modules/ecdh/main_impl.h10
-rw-r--r--src/secp256k1/src/modules/ecdh/tests_impl.h10
-rw-r--r--src/secp256k1/src/modules/extrakeys/main_impl.h26
-rw-r--r--src/secp256k1/src/modules/extrakeys/tests_exhaustive_impl.h14
-rw-r--r--src/secp256k1/src/modules/extrakeys/tests_impl.h41
-rw-r--r--src/secp256k1/src/modules/recovery/main_impl.h22
-rw-r--r--src/secp256k1/src/modules/recovery/tests_exhaustive_impl.h10
-rw-r--r--src/secp256k1/src/modules/recovery/tests_impl.h10
-rw-r--r--src/secp256k1/src/modules/schnorrsig/main_impl.h16
-rw-r--r--src/secp256k1/src/modules/schnorrsig/tests_exhaustive_impl.h14
-rw-r--r--src/secp256k1/src/modules/schnorrsig/tests_impl.h16
-rw-r--r--src/secp256k1/src/num.h74
-rw-r--r--src/secp256k1/src/num_gmp.h20
-rw-r--r--src/secp256k1/src/num_gmp_impl.h288
-rw-r--r--src/secp256k1/src/num_impl.h24
-rw-r--r--src/secp256k1/src/scalar.h22
-rw-r--r--src/secp256k1/src/scalar_4x64.h10
-rw-r--r--src/secp256k1/src/scalar_4x64_impl.h252
-rw-r--r--src/secp256k1/src/scalar_8x32.h10
-rw-r--r--src/secp256k1/src/scalar_8x32_impl.h189
-rw-r--r--src/secp256k1/src/scalar_impl.h229
-rw-r--r--src/secp256k1/src/scalar_low.h10
-rw-r--r--src/secp256k1/src/scalar_low_impl.h29
-rw-r--r--src/secp256k1/src/scratch.h16
-rw-r--r--src/secp256k1/src/scratch_impl.h14
-rw-r--r--src/secp256k1/src/secp256k1.c81
-rw-r--r--src/secp256k1/src/selftest.h10
-rw-r--r--src/secp256k1/src/testrand.h10
-rw-r--r--src/secp256k1/src/testrand_impl.h10
-rw-r--r--src/secp256k1/src/tests.c1729
-rw-r--r--src/secp256k1/src/tests_exhaustive.c8
-rw-r--r--src/secp256k1/src/util.h89
-rw-r--r--src/secp256k1/src/valgrind_ctime_test.c78
-rw-r--r--src/test/addrman_tests.cpp4
-rw-r--r--src/test/blockfilter_index_tests.cpp26
-rw-r--r--src/test/coinstatsindex_tests.cpp8
-rw-r--r--src/test/denialofservice_tests.cpp49
-rw-r--r--src/test/fuzz/addrman.cpp22
-rw-r--r--src/test/fuzz/base_encode_decode.cpp7
-rw-r--r--src/test/fuzz/coins_view.cpp4
-rw-r--r--src/test/fuzz/crypto.cpp6
-rw-r--r--src/test/fuzz/deserialize.cpp53
-rw-r--r--src/test/fuzz/float.cpp29
-rw-r--r--src/test/fuzz/fuzz.cpp9
-rw-r--r--src/test/fuzz/load_external_block_file.cpp7
-rw-r--r--src/test/fuzz/node_eviction.cpp2
-rw-r--r--src/test/fuzz/process_message.cpp12
-rw-r--r--src/test/fuzz/util.h2
-rw-r--r--src/test/fuzz/validation_load_mempool.cpp7
-rw-r--r--src/test/interfaces_tests.cpp2
-rw-r--r--src/test/key_tests.cpp42
-rw-r--r--src/test/miner_tests.cpp93
-rw-r--r--src/test/net_peer_eviction_tests.cpp504
-rw-r--r--src/test/txindex_tests.cpp2
-rw-r--r--src/test/txvalidation_tests.cpp2
-rw-r--r--src/test/txvalidationcache_tests.cpp48
-rw-r--r--src/test/util/mining.cpp4
-rw-r--r--src/test/util/net.h12
-rw-r--r--src/test/util/setup_common.cpp23
-rw-r--r--src/test/util_tests.cpp19
-rw-r--r--src/test/validation_block_tests.cpp16
-rw-r--r--src/txmempool.cpp7
-rw-r--r--src/txmempool.h3
-rw-r--r--src/util/system.cpp17
-rw-r--r--src/util/system.h2
-rw-r--r--src/validation.cpp195
-rw-r--r--src/validation.h41
-rw-r--r--src/wallet/db.cpp2
-rw-r--r--src/wallet/external_signer_scriptpubkeyman.cpp8
-rw-r--r--src/wallet/external_signer_scriptpubkeyman.h5
-rw-r--r--src/wallet/interfaces.cpp10
-rw-r--r--src/wallet/rpcdump.cpp12
-rw-r--r--src/wallet/rpcwallet.cpp12
-rw-r--r--src/wallet/scriptpubkeyman.cpp9
-rw-r--r--src/wallet/scriptpubkeyman.h6
-rw-r--r--src/wallet/spend.cpp499
-rw-r--r--src/wallet/test/coinselector_tests.cpp68
-rw-r--r--src/wallet/test/psbt_wallet_tests.cpp2
-rw-r--r--src/wallet/test/wallet_tests.cpp38
-rw-r--r--src/wallet/wallet.cpp18
-rw-r--r--src/wallet/wallet.h5
-rw-r--r--src/wallet/walletdb.cpp7
-rw-r--r--src/zmq/zmqutil.cpp8
-rw-r--r--src/zmq/zmqutil.h4
-rwxr-xr-xtest/functional/feature_anchors.py32
-rwxr-xr-xtest/functional/feature_block.py21
-rwxr-xr-xtest/functional/feature_nulldummy.py8
-rwxr-xr-xtest/functional/feature_rbf.py147
-rwxr-xr-xtest/functional/feature_versionbits_warning.py4
-rwxr-xr-xtest/functional/mempool_package_onemore.py43
-rwxr-xr-xtest/functional/mempool_packages.py30
-rwxr-xr-xtest/functional/p2p_invalid_block.py19
-rwxr-xr-xtest/functional/p2p_leak.py6
-rwxr-xr-xtest/functional/rpc_packages.py3
-rw-r--r--test/functional/test_framework/blocktools.py5
-rwxr-xr-xtest/functional/test_framework/messages.py2
-rwxr-xr-xtest/functional/test_framework/test_node.py4
-rw-r--r--test/functional/test_framework/util.py43
-rw-r--r--test/functional/test_framework/wallet.py15
-rwxr-xr-xtest/functional/test_runner.py20
-rwxr-xr-xtest/functional/wallet_orphanedreward.py62
-rwxr-xr-xtest/functional/wallet_taproot.py136
-rwxr-xr-xtest/lint/lint-circular-dependencies.sh1
-rw-r--r--test/sanitizer_suppressions/ubsan3
298 files changed, 8302 insertions, 4322 deletions
diff --git a/.appveyor.yml b/.appveyor.yml
index fb95876c36..3ca7818eca 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -7,9 +7,9 @@ clone_depth: 5
environment:
PATH: 'C:\Python37-x64;C:\Python37-x64\Scripts;%PATH%'
PYTHONUTF8: 1
- QT_DOWNLOAD_URL: 'https://github.com/sipsorcery/qt_win_binary/releases/download/qt51210x64_vs2019_1694/Qt5.12.10_x64_static_vs2019_1694.zip'
- QT_DOWNLOAD_HASH: '3035a1307e8302bb3a76eba9bb3102979f945ab4022cc3bc2e1583edd44bdc99'
- QT_LOCAL_PATH: 'C:\Qt5.12.10_x64_static_vs2019_1694'
+ QT_DOWNLOAD_URL: 'https://github.com/sipsorcery/qt_win_binary/releases/download/qt51211x64_static_vs2019_16101/Qt5.12.11_x64_static_vs2019_16101.zip'
+ QT_DOWNLOAD_HASH: 'cf1b58107fadbf0d9a957d14dab16cde6b6eb6936a1908472da1f967dda34a3a'
+ QT_LOCAL_PATH: 'C:\Qt5.12.11_x64_static_vs2019_16101'
VCPKG_TAG: '75522bb1f2e7d863078bcd06322348f053a9e33f'
install:
# Disable zmq test for now since python zmq library on Windows would cause Access violation sometimes.
diff --git a/REVIEWERS b/REVIEWERS
index d4d8939e1c..e048036e42 100644
--- a/REVIEWERS
+++ b/REVIEWERS
@@ -57,8 +57,6 @@
/src/util/settings.* @ryanofsky
# Fuzzing
-/src/test/fuzz/ @practicalswift
-/doc/fuzzing.md @practicalswift
# Tests
/src/test/net_peer_eviction_tests.cpp @jonatack
@@ -117,7 +115,6 @@
/src/dbwrapper.* @jamesob
# Linter
-/test/lint/ @practicalswift
/test/lint/lint-shell.sh @hebasto
# Bech32
diff --git a/build-aux/m4/bitcoin_qt.m4 b/build-aux/m4/bitcoin_qt.m4
index 232a79bb36..5b5a8ed16e 100644
--- a/build-aux/m4/bitcoin_qt.m4
+++ b/build-aux/m4/bitcoin_qt.m4
@@ -150,7 +150,7 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[
AC_DEFINE(QT_QPA_PLATFORM_WINDOWS, 1, [Define this symbol if the qt platform is windows])
elif test "x$TARGET_OS" = xlinux; then
dnl workaround for https://bugreports.qt.io/browse/QTBUG-74874
- AX_CHECK_LINK_FLAG([-lxcb-shm], [QT_LIBS="-lxcb-shm $QT_LIBS"], [AC_MSG_ERROR([could not link against -lxcb-shm])])
+ AX_CHECK_LINK_FLAG([-lxcb-shm], [QT_LIBS="$QT_LIBS -lxcb-shm"], [AC_MSG_ERROR([could not link against -lxcb-shm])])
_BITCOIN_QT_CHECK_STATIC_PLUGIN([QXcbIntegrationPlugin], [-lqxcb])
AC_DEFINE(QT_QPA_PLATFORM_XCB, 1, [Define this symbol if the qt platform is xcb])
elif test "x$TARGET_OS" = xdarwin; then
diff --git a/build_msvc/bitcoin-qt/bitcoin-qt.vcxproj b/build_msvc/bitcoin-qt/bitcoin-qt.vcxproj
index 65ce1ee9da..a697c1dfb6 100644
--- a/build_msvc/bitcoin-qt/bitcoin-qt.vcxproj
+++ b/build_msvc/bitcoin-qt/bitcoin-qt.vcxproj
@@ -55,6 +55,7 @@
<AdditionalIncludeDirectories>$(QtIncludes);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
+ <SubSystem>Windows</SubSystem>
<AdditionalDependencies>$(QtReleaseLibraries);%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalOptions>/ignore:4206 /LTCG:OFF</AdditionalOptions>
</Link>
diff --git a/build_msvc/common.qt.init.vcxproj b/build_msvc/common.qt.init.vcxproj
index 68ad06c4ac..ce66a7ab34 100644
--- a/build_msvc/common.qt.init.vcxproj
+++ b/build_msvc/common.qt.init.vcxproj
@@ -2,7 +2,7 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="QtGlobals">
- <QtBaseDir>C:\Qt5.12.10_x64_static_vs2019_1694</QtBaseDir>
+ <QtBaseDir>C:\Qt5.12.11_x64_static_vs2019_16101</QtBaseDir>
<QtPluginsLibraryDir>$(QtBaseDir)\plugins</QtPluginsLibraryDir>
<QtLibraryDir>$(QtBaseDir)\lib</QtLibraryDir>
<QtIncludeDir>$(QtBaseDir)\include</QtIncludeDir>
diff --git a/build_msvc/libsecp256k1/libsecp256k1.vcxproj b/build_msvc/libsecp256k1/libsecp256k1.vcxproj
index c42918d6e1..f9b0a7975c 100644
--- a/build_msvc/libsecp256k1/libsecp256k1.vcxproj
+++ b/build_msvc/libsecp256k1/libsecp256k1.vcxproj
@@ -10,13 +10,14 @@
<ItemGroup>
<ClCompile Include="..\..\src\secp256k1\src\secp256k1.c" />
</ItemGroup>
- <ItemDefinitionGroup>
+ <ItemDefinitionGroup>
<ClCompile>
<PreprocessorDefinitions>ENABLE_MODULE_ECDH;ENABLE_MODULE_RECOVERY;ENABLE_MODULE_EXTRAKEYS;ENABLE_MODULE_SCHNORRSIG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <AdditionalIncludeDirectories>..\..\src\secp256k1;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
- </ClCompile>
+ <AdditionalIncludeDirectories>..\..\src\secp256k1;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4146;4244;4267;4334</DisableSpecificWarnings>
+ </ClCompile>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Import Project="..\common.vcxproj" />
-</Project> \ No newline at end of file
+</Project>
diff --git a/ci/test/00_setup_env_arm.sh b/ci/test/00_setup_env_arm.sh
index 07f099b85c..8d2b70e549 100755
--- a/ci/test/00_setup_env_arm.sh
+++ b/ci/test/00_setup_env_arm.sh
@@ -25,4 +25,4 @@ export RUN_FUNCTIONAL_TESTS=false
export GOAL="install"
# -Wno-psabi is to disable ABI warnings: "note: parameter passing for argument of type ... changed in GCC 7.1"
# This could be removed once the ABI change warning does not show up by default
-export BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports CXXFLAGS=-Wno-psabi --enable-external-signer"
+export BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports CXXFLAGS=-Wno-psabi"
diff --git a/ci/test/00_setup_env_i686_centos.sh b/ci/test/00_setup_env_i686_centos.sh
index 05c724fc0b..2ddb932907 100755
--- a/ci/test/00_setup_env_i686_centos.sh
+++ b/ci/test/00_setup_env_i686_centos.sh
@@ -11,6 +11,6 @@ export CONTAINER_NAME=ci_i686_centos_8
export DOCKER_NAME_TAG=centos:8
export DOCKER_PACKAGES="gcc-c++ glibc-devel.x86_64 libstdc++-devel.x86_64 glibc-devel.i686 libstdc++-devel.i686 ccache libtool make git python3 python3-zmq which patch lbzip2 dash rsync coreutils bison"
export GOAL="install"
-export BITCOIN_CONFIG="--enable-zmq --with-gui=qt5 --enable-reduce-exports --enable-external-signer"
+export BITCOIN_CONFIG="--enable-zmq --with-gui=qt5 --enable-reduce-exports"
export CONFIG_SHELL="/bin/dash"
export TEST_RUNNER_ENV="LC_ALL=en_US.UTF-8"
diff --git a/ci/test/00_setup_env_mac.sh b/ci/test/00_setup_env_mac.sh
index 196394e908..73ac09c1de 100755
--- a/ci/test/00_setup_env_mac.sh
+++ b/ci/test/00_setup_env_mac.sh
@@ -15,4 +15,4 @@ export XCODE_BUILD_ID=12A7403
export RUN_UNIT_TESTS=false
export RUN_FUNCTIONAL_TESTS=false
export GOAL="deploy"
-export BITCOIN_CONFIG="--with-gui --enable-reduce-exports --enable-external-signer"
+export BITCOIN_CONFIG="--with-gui --enable-reduce-exports"
diff --git a/ci/test/00_setup_env_mac_host.sh b/ci/test/00_setup_env_mac_host.sh
index 898c1530a1..c0d951a041 100755
--- a/ci/test/00_setup_env_mac_host.sh
+++ b/ci/test/00_setup_env_mac_host.sh
@@ -9,7 +9,7 @@ export LC_ALL=C.UTF-8
export HOST=x86_64-apple-darwin18
export PIP_PACKAGES="zmq lief"
export GOAL="install"
-export BITCOIN_CONFIG="--with-gui --enable-reduce-exports --enable-external-signer"
+export BITCOIN_CONFIG="--with-gui --enable-reduce-exports"
export CI_OS_NAME="macos"
export NO_DEPENDS=1
export OSX_SDK=""
diff --git a/ci/test/00_setup_env_native_asan.sh b/ci/test/00_setup_env_native_asan.sh
index 92af98aa9b..ab185b6e71 100755
--- a/ci/test/00_setup_env_native_asan.sh
+++ b/ci/test/00_setup_env_native_asan.sh
@@ -11,4 +11,4 @@ export PACKAGES="clang llvm python3-zmq qtbase5-dev qttools5-dev-tools libevent-
export DOCKER_NAME_TAG=ubuntu:hirsute
export NO_DEPENDS=1
export GOAL="install"
-export BITCOIN_CONFIG="--enable-zmq --with-incompatible-bdb --with-gui=qt5 CPPFLAGS='-DARENA_DEBUG -DDEBUG_LOCKORDER' --with-sanitizers=address,integer,undefined CC=clang CXX=clang++ --enable-external-signer"
+export BITCOIN_CONFIG="--enable-zmq --with-incompatible-bdb --with-gui=qt5 CPPFLAGS='-DARENA_DEBUG -DDEBUG_LOCKORDER' --with-sanitizers=address,integer,undefined CC=clang CXX=clang++"
diff --git a/ci/test/00_setup_env_native_fuzz.sh b/ci/test/00_setup_env_native_fuzz.sh
index bedd0cf9aa..58388fa928 100755
--- a/ci/test/00_setup_env_native_fuzz.sh
+++ b/ci/test/00_setup_env_native_fuzz.sh
@@ -14,5 +14,5 @@ export RUN_UNIT_TESTS=false
export RUN_FUNCTIONAL_TESTS=false
export RUN_FUZZ_TESTS=true
export GOAL="install"
-export BITCOIN_CONFIG="--enable-fuzz --with-sanitizers=fuzzer,address,undefined,integer CC=clang CXX=clang++ --enable-external-signer"
+export BITCOIN_CONFIG="--enable-fuzz --with-sanitizers=fuzzer,address,undefined,integer CC=clang CXX=clang++"
export CCACHE_SIZE=200M
diff --git a/ci/test/00_setup_env_native_multiprocess.sh b/ci/test/00_setup_env_native_multiprocess.sh
index 37d714400b..1418dfbc51 100755
--- a/ci/test/00_setup_env_native_multiprocess.sh
+++ b/ci/test/00_setup_env_native_multiprocess.sh
@@ -11,7 +11,7 @@ export DOCKER_NAME_TAG=ubuntu:20.04
export PACKAGES="cmake python3 python3-pip llvm clang"
export DEP_OPTS="DEBUG=1 MULTIPROCESS=1"
export GOAL="install"
-export BITCOIN_CONFIG="--enable-external-signer --enable-debug CC=clang CXX=clang++" # Use clang to avoid OOM
+export BITCOIN_CONFIG="--enable-debug CC=clang CXX=clang++" # Use clang to avoid OOM
export TEST_RUNNER_ENV="BITCOIND=bitcoin-node"
export RUN_SECURITY_TESTS="true"
export PIP_PACKAGES="lief"
diff --git a/ci/test/00_setup_env_native_nowallet.sh b/ci/test/00_setup_env_native_nowallet.sh
index a496b5af6e..d167c9198a 100755
--- a/ci/test/00_setup_env_native_nowallet.sh
+++ b/ci/test/00_setup_env_native_nowallet.sh
@@ -11,4 +11,4 @@ export DOCKER_NAME_TAG=ubuntu:18.04 # Use bionic to have one config run the tes
export PACKAGES="python3-zmq clang-5.0 llvm-5.0" # Use clang-5 to test C++17 compatibility, see doc/dependencies.md
export DEP_OPTS="NO_WALLET=1"
export GOAL="install"
-export BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports CC=clang-5.0 CXX=clang++-5.0 --enable-external-signer"
+export BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports CC=clang-5.0 CXX=clang++-5.0"
diff --git a/ci/test/00_setup_env_native_qt5.sh b/ci/test/00_setup_env_native_qt5.sh
index 61948ab221..9c57eba62a 100755
--- a/ci/test/00_setup_env_native_qt5.sh
+++ b/ci/test/00_setup_env_native_qt5.sh
@@ -16,4 +16,4 @@ export RUN_UNIT_TESTS="false"
export GOAL="install"
export PREVIOUS_RELEASES_TO_DOWNLOAD="v0.15.2 v0.16.3 v0.17.2 v0.18.1 v0.19.1"
export BITCOIN_CONFIG="--enable-zmq --with-libs=no --with-gui=qt5 --enable-glibc-back-compat --enable-reduce-exports
---enable-debug --disable-fuzz-binary CFLAGS=\"-g0 -O2 -funsigned-char\" CXXFLAGS=\"-g0 -O2 -funsigned-char\" --enable-external-signer"
+--enable-debug --disable-fuzz-binary CFLAGS=\"-g0 -O2 -funsigned-char\" CXXFLAGS=\"-g0 -O2 -funsigned-char\""
diff --git a/ci/test/00_setup_env_native_tsan.sh b/ci/test/00_setup_env_native_tsan.sh
index 33f63fa9ba..a5082bdaab 100755
--- a/ci/test/00_setup_env_native_tsan.sh
+++ b/ci/test/00_setup_env_native_tsan.sh
@@ -11,4 +11,4 @@ export DOCKER_NAME_TAG=ubuntu:hirsute
export PACKAGES="clang llvm libc++abi-dev libc++-dev python3-zmq"
export DEP_OPTS="CC=clang CXX='clang++ -stdlib=libc++'"
export GOAL="install"
-export BITCOIN_CONFIG="--enable-zmq --with-gui=no CPPFLAGS='-DARENA_DEBUG -DDEBUG_LOCKORDER' CXXFLAGS='-g' --with-sanitizers=thread CC=clang CXX='clang++ -stdlib=libc++' --enable-external-signer"
+export BITCOIN_CONFIG="--enable-zmq --with-gui=no CPPFLAGS='-DARENA_DEBUG -DDEBUG_LOCKORDER' CXXFLAGS='-g' --with-sanitizers=thread CC=clang CXX='clang++ -stdlib=libc++'"
diff --git a/ci/test/00_setup_env_s390x.sh b/ci/test/00_setup_env_s390x.sh
index 88b431f3c7..51a0fd9117 100755
--- a/ci/test/00_setup_env_s390x.sh
+++ b/ci/test/00_setup_env_s390x.sh
@@ -23,4 +23,4 @@ export RUN_UNIT_TESTS=true
export TEST_RUNNER_ENV="LC_ALL=C"
export RUN_FUNCTIONAL_TESTS=true
export GOAL="install"
-export BITCOIN_CONFIG="--enable-reduce-exports --with-incompatible-bdb --enable-external-signer"
+export BITCOIN_CONFIG="--enable-reduce-exports --with-incompatible-bdb"
diff --git a/configure.ac b/configure.ac
index 505468a8bb..8cfa2d39e1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -333,9 +333,9 @@ AC_ARG_ENABLE([werror],
[enable_werror=no])
AC_ARG_ENABLE([external-signer],
- [AS_HELP_STRING([--enable-external-signer],[compile external signer support (default is no, requires Boost::Process)])],
+ [AS_HELP_STRING([--enable-external-signer],[compile external signer support (default is yes, requires Boost::Process)])],
[use_external_signer=$enableval],
- [use_external_signer=no])
+ [use_external_signer=yes])
AC_LANG_PUSH([C++])
@@ -475,7 +475,9 @@ if test "x$CXXFLAGS_overridden" = "xno"; then
AX_CHECK_COMPILE_FLAG([-Wself-assign],[NOWARN_CXXFLAGS="$NOWARN_CXXFLAGS -Wno-self-assign"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Wunused-local-typedef],[NOWARN_CXXFLAGS="$NOWARN_CXXFLAGS -Wno-unused-local-typedef"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Wimplicit-fallthrough],[NOWARN_CXXFLAGS="$NOWARN_CXXFLAGS -Wno-implicit-fallthrough"],,[[$CXXFLAG_WERROR]])
- AX_CHECK_COMPILE_FLAG([-Wdeprecated-copy],[NOWARN_CXXFLAGS="$NOWARN_CXXFLAGS -Wno-deprecated-copy"],,[[$CXXFLAG_WERROR]])
+ if test x$suppress_external_warnings != xyes ; then
+ AX_CHECK_COMPILE_FLAG([-Wdeprecated-copy],[NOWARN_CXXFLAGS="$NOWARN_CXXFLAGS -Wno-deprecated-copy"],,[[$CXXFLAG_WERROR]])
+ fi
fi
dnl Don't allow extended (non-ASCII) symbols in identifiers. This is easier for code review.
@@ -1357,13 +1359,15 @@ if test x$enable_wallet != xno; then
fi
if test x$use_ebpf != xno; then
- AC_CHECK_HEADER([sys/sdt.h], [have_sdt=yes], [have_sdt=no])
-else
- have_sdt=no
-fi
-
-if test x$have_sdt = xyes; then
- AC_DEFINE([ENABLE_TRACING], [1], [Define to 1 to enable eBPF user static defined tracepoints])
+ AC_MSG_CHECKING([whether eBPF tracepoints are supported])
+ AC_COMPILE_IFELSE([
+ AC_LANG_PROGRAM(
+ [#include <sys/sdt.h>],
+ [DTRACE_PROBE("context", "event");]
+ )],
+ [AC_MSG_RESULT(yes); have_sdt=yes; AC_DEFINE([ENABLE_TRACING], [1], [Define to 1 to enable eBPF user static defined tracepoints])],
+ [AC_MSG_RESULT(no); have_sdt=no;]
+ )
fi
dnl Check for libminiupnpc (optional)
@@ -1915,7 +1919,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 --enable-module-schnorrsig --enable-experimental"
+ac_configure_args="${ac_configure_args} --disable-shared --with-pic --enable-benchmark=no --enable-module-recovery --enable-module-schnorrsig --enable-experimental"
AC_CONFIG_SUBDIRS([src/secp256k1])
AC_OUTPUT
diff --git a/contrib/devtools/pixie.py b/contrib/devtools/pixie.py
index 8cf06a799a..64660968ad 100644
--- a/contrib/devtools/pixie.py
+++ b/contrib/devtools/pixie.py
@@ -217,7 +217,7 @@ def _parse_verneed(section: Section, strings: bytes, eh: ELFHeader) -> Dict[int,
result = {}
while True:
verneed = Verneed(data, ofs, eh)
- aofs = verneed.vn_aux
+ aofs = ofs + verneed.vn_aux
while True:
vernaux = Vernaux(data, aofs, eh, strings)
result[vernaux.vna_other] = vernaux.name
diff --git a/contrib/devtools/symbol-check.py b/contrib/devtools/symbol-check.py
index d740a94560..56e4313d78 100755
--- a/contrib/devtools/symbol-check.py
+++ b/contrib/devtools/symbol-check.py
@@ -41,8 +41,16 @@ import pixie
#
MAX_VERSIONS = {
'GCC': (4,8,0),
-'GLIBC': (2,17),
-'LIBATOMIC': (1,0)
+'GLIBC': {
+ pixie.EM_386: (2,17),
+ pixie.EM_X86_64: (2,17),
+ pixie.EM_ARM: (2,17),
+ pixie.EM_AARCH64:(2,17),
+ pixie.EM_PPC64: (2,17),
+ pixie.EM_RISCV: (2,27),
+},
+'LIBATOMIC': (1,0),
+'V': (0,5,0), # xkb (bitcoin-qt only)
}
# See here for a description of _IO_stdin_used:
# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=634261#109
@@ -78,14 +86,6 @@ ELF_ALLOWED_LIBRARIES = {
'libfreetype.so.6', # font parsing
'libdl.so.2' # programming interface to dynamic linker
}
-ARCH_MIN_GLIBC_VER = {
-pixie.EM_386: (2,1),
-pixie.EM_X86_64: (2,2,5),
-pixie.EM_ARM: (2,4),
-pixie.EM_AARCH64:(2,17),
-pixie.EM_PPC64: (2,17),
-pixie.EM_RISCV: (2,27)
-}
MACHO_ALLOWED_LIBRARIES = {
# bitcoind and bitcoin-qt
@@ -161,7 +161,10 @@ def check_version(max_versions, version, arch) -> bool:
ver = tuple([int(x) for x in ver.split('.')])
if not lib in max_versions:
return False
- return ver <= max_versions[lib] or lib == 'GLIBC' and ver <= ARCH_MIN_GLIBC_VER[arch]
+ if isinstance(max_versions[lib], tuple):
+ return ver <= max_versions[lib]
+ else:
+ return ver <= max_versions[lib][arch]
def check_imported_symbols(filename) -> bool:
elf = pixie.load(filename)
@@ -212,6 +215,18 @@ def check_MACHO_libraries(filename) -> bool:
ok = False
return ok
+def check_MACHO_min_os(filename) -> bool:
+ binary = lief.parse(filename)
+ if binary.build_version.minos == [10,14,0]:
+ return True
+ return False
+
+def check_MACHO_sdk(filename) -> bool:
+ binary = lief.parse(filename)
+ if binary.build_version.sdk == [10, 15, 6]:
+ return True
+ return False
+
def check_PE_libraries(filename) -> bool:
ok: bool = True
binary = lief.parse(filename)
@@ -221,6 +236,14 @@ def check_PE_libraries(filename) -> bool:
ok = False
return ok
+def check_PE_subsystem_version(filename) -> bool:
+ binary = lief.parse(filename)
+ major: int = binary.optional_header.major_subsystem_version
+ minor: int = binary.optional_header.minor_subsystem_version
+ if major == 6 and minor == 1:
+ return True
+ return False
+
CHECKS = {
'ELF': [
('IMPORTED_SYMBOLS', check_imported_symbols),
@@ -228,10 +251,13 @@ CHECKS = {
('LIBRARY_DEPENDENCIES', check_ELF_libraries)
],
'MACHO': [
- ('DYNAMIC_LIBRARIES', check_MACHO_libraries)
+ ('DYNAMIC_LIBRARIES', check_MACHO_libraries),
+ ('MIN_OS', check_MACHO_min_os),
+ ('SDK', check_MACHO_sdk),
],
'PE' : [
- ('DYNAMIC_LIBRARIES', check_PE_libraries)
+ ('DYNAMIC_LIBRARIES', check_PE_libraries),
+ ('SUBSYSTEM_VERSION', check_PE_subsystem_version),
]
}
diff --git a/contrib/devtools/test-symbol-check.py b/contrib/devtools/test-symbol-check.py
index 106dfd2c5a..6ce2fa3560 100755
--- a/contrib/devtools/test-symbol-check.py
+++ b/contrib/devtools/test-symbol-check.py
@@ -98,7 +98,7 @@ class TestSymbolChecks(unittest.TestCase):
self.assertEqual(call_symbol_check(cc, source, executable, ['-lexpat']),
(1, 'libexpat.1.dylib is not in ALLOWED_LIBRARIES!\n' +
- executable + ': failed DYNAMIC_LIBRARIES'))
+ f'{executable}: failed DYNAMIC_LIBRARIES MIN_OS SDK'))
source = 'test2.c'
executable = 'test2'
@@ -114,7 +114,20 @@ class TestSymbolChecks(unittest.TestCase):
''')
self.assertEqual(call_symbol_check(cc, source, executable, ['-framework', 'CoreGraphics']),
- (0, ''))
+ (1, f'{executable}: failed MIN_OS SDK'))
+
+ source = 'test3.c'
+ executable = 'test3'
+ with open(source, 'w', encoding="utf8") as f:
+ f.write('''
+ int main()
+ {
+ return 0;
+ }
+ ''')
+
+ self.assertEqual(call_symbol_check(cc, source, executable, ['-mmacosx-version-min=10.14']),
+ (1, f'{executable}: failed SDK'))
def test_PE(self):
source = 'test1.c'
@@ -132,12 +145,26 @@ class TestSymbolChecks(unittest.TestCase):
}
''')
- self.assertEqual(call_symbol_check(cc, source, executable, ['-lpdh']),
+ self.assertEqual(call_symbol_check(cc, source, executable, ['-lpdh', '-Wl,--major-subsystem-version', '-Wl,6', '-Wl,--minor-subsystem-version', '-Wl,1']),
(1, 'pdh.dll is not in ALLOWED_LIBRARIES!\n' +
executable + ': failed DYNAMIC_LIBRARIES'))
source = 'test2.c'
executable = 'test2.exe'
+
+ with open(source, 'w', encoding="utf8") as f:
+ f.write('''
+ int main()
+ {
+ return 0;
+ }
+ ''')
+
+ self.assertEqual(call_symbol_check(cc, source, executable, ['-Wl,--major-subsystem-version', '-Wl,9', '-Wl,--minor-subsystem-version', '-Wl,9']),
+ (1, executable + ': failed SUBSYSTEM_VERSION'))
+
+ source = 'test3.c'
+ executable = 'test3.exe'
with open(source, 'w', encoding="utf8") as f:
f.write('''
#include <windows.h>
@@ -149,7 +176,7 @@ class TestSymbolChecks(unittest.TestCase):
}
''')
- self.assertEqual(call_symbol_check(cc, source, executable, ['-lole32']),
+ self.assertEqual(call_symbol_check(cc, source, executable, ['-lole32', '-Wl,--major-subsystem-version', '-Wl,6', '-Wl,--minor-subsystem-version', '-Wl,1']),
(0, ''))
diff --git a/contrib/gitian-descriptors/gitian-linux.yml b/contrib/gitian-descriptors/gitian-linux.yml
index bed3531720..e6dce7a8c6 100644
--- a/contrib/gitian-descriptors/gitian-linux.yml
+++ b/contrib/gitian-descriptors/gitian-linux.yml
@@ -99,7 +99,7 @@ script: |
done
}
- pip3 install lief==0.11.4
+ pip3 install lief==0.11.5
# Faketime for depends so intermediate results are comparable
export PATH_orig=${PATH}
diff --git a/contrib/gitian-descriptors/gitian-osx-signer.yml b/contrib/gitian-descriptors/gitian-osx-signer.yml
index 3f0c0c3332..addad0a5d2 100644
--- a/contrib/gitian-descriptors/gitian-osx-signer.yml
+++ b/contrib/gitian-descriptors/gitian-osx-signer.yml
@@ -14,7 +14,7 @@ remotes:
"dir": "signature"
- "url": "https://github.com/achow101/signapple.git"
"dir": "signapple"
- "commit": "c7e73aa27a7615ac9506559173f787e2906b25eb"
+ "commit": "b084cbbf44d5330448ffce0c7d118f75781b64bd"
files:
- "bitcoin-osx-unsigned.tar.gz"
script: |
diff --git a/contrib/gitian-descriptors/gitian-osx.yml b/contrib/gitian-descriptors/gitian-osx.yml
index 1d4506e3c2..a39618adb7 100644
--- a/contrib/gitian-descriptors/gitian-osx.yml
+++ b/contrib/gitian-descriptors/gitian-osx.yml
@@ -78,7 +78,7 @@ script: |
done
}
- pip3 install lief==0.11.4
+ pip3 install lief==0.11.5
# Faketime for depends so intermediate results are comparable
export PATH_orig=${PATH}
diff --git a/contrib/gitian-descriptors/gitian-win.yml b/contrib/gitian-descriptors/gitian-win.yml
index 03eba71366..ffe228a032 100644
--- a/contrib/gitian-descriptors/gitian-win.yml
+++ b/contrib/gitian-descriptors/gitian-win.yml
@@ -86,7 +86,7 @@ script: |
done
}
- pip3 install lief==0.11.4
+ pip3 install lief==0.11.5
# Faketime for depends so intermediate results are comparable
export PATH_orig=${PATH}
diff --git a/contrib/guix/guix-attest b/contrib/guix/guix-attest
index 081d1c0465..c8cf73d400 100755
--- a/contrib/guix/guix-attest
+++ b/contrib/guix/guix-attest
@@ -99,24 +99,34 @@ fi
# We should be able to find at least one output
################
-echo "Looking for build output directories in ${OUTDIR_BASE}"
+echo "Looking for build output SHA256SUMS fragments in ${OUTDIR_BASE}"
shopt -s nullglob
-OUTDIRS=( "${OUTDIR_BASE}"/* ) # This expands to an array of directories...
+sha256sum_fragments=( "$OUTDIR_BASE"/*/SHA256SUMS.part ) # This expands to an array of directories...
shopt -u nullglob
-if (( ${#OUTDIRS[@]} )); then
- echo "Found build output directories:"
- for outdir in "${OUTDIRS[@]}"; do
+noncodesigned_fragments=()
+codesigned_fragments=()
+
+if (( ${#sha256sum_fragments[@]} )); then
+ echo "Found build output SHA256SUMS fragments:"
+ for outdir in "${sha256sum_fragments[@]}"; do
echo " '$outdir'"
+ case "$outdir" in
+ "$OUTDIR_BASE"/*-codesigned/SHA256SUMS.part)
+ codesigned_fragments+=("$outdir")
+ ;;
+ *)
+ noncodesigned_fragments+=("$outdir")
+ ;;
+ esac
done
echo
else
- echo "ERR: Could not find any build output directories in ${OUTDIR_BASE}"
+ echo "ERR: Could not find any build output SHA256SUMS fragments in ${OUTDIR_BASE}"
exit 1
fi
-
##############
## Attest ##
##############
@@ -126,82 +136,105 @@ fi
# HOST: The output directory being attested
#
out_name() {
- basename "$1"
+ basename "$(dirname "$1")"
}
-# Usage: out_sig_dir $outdir
-#
-# outdir: The output directory being attested
-#
-out_sig_dir() {
- echo "$GUIX_SIGS_REPO/$VERSION/$(out_name "$1")/$signer_name"
-}
+shasum_already_exists() {
+cat <<EOF
+--
-# Accumulate a list of signature directories that already exist...
-outdirs_already_attested_to=()
+ERR: An ${1} file already exists for '${VERSION}' and attests
+ differently. You likely previously attested to a partial build (e.g. one
+ where you specified the HOST environment variable).
-echo "Attesting to build outputs for version: '${VERSION}'"
-echo ""
+ See the diff above for more context.
-# MAIN LOGIC: Loop through each output for VERSION and attest to output in
-# GUIX_SIGS_REPO as SIGNER, if attestation does not exist
-for outdir in "${OUTDIRS[@]}"; do
- if [ -e "${outdir}/SKIPATTEST.TAG" ]; then
- echo "${outname}: SKIPPING: Output directory marked with SKIPATTEST.TAG file"
- continue
- fi
- outname="$(out_name "$outdir")"
- outsigdir="$(out_sig_dir "$outdir")"
- if [ -e "$outsigdir" ]; then
- echo "${outname}: SKIPPING: Signature directory already exists in the specified guix.sigs repository"
- outdirs_already_attested_to+=("$outdir")
- else
- # Clean up incomplete sigdir if something fails (likely gpg)
- trap 'rm -rf "$outsigdir"' ERR
+Hint: You may wish to remove the existing attestations and their signatures by
+ invoking:
- mkdir -p "$outsigdir"
+ rm '${PWD}/${1}'{,.asc}
- (
- cd "$outdir"
+ Then try running this script again.
- if [ -e inputs.SHA256SUMS ]; then
- echo "${outname}: Including existent input SHA256SUMS"
- cat inputs.SHA256SUMS >> "$outsigdir"/SHA256SUMS
- fi
+EOF
+}
- echo "${outname}: Hashing build outputs to produce SHA256SUMS"
- files="$(find -L . -type f ! -iname '*.SHA256SUMS')"
- if [ -n "$files" ]; then
- cut -c3- <<< "$files" | env LC_ALL=C sort | xargs sha256sum >> "$outsigdir"/SHA256SUMS
+echo "Attesting to build outputs for version: '${VERSION}'"
+echo ""
+
+outsigdir="$GUIX_SIGS_REPO/$VERSION/$signer_name"
+mkdir -p "$outsigdir"
+(
+ cd "$outsigdir"
+
+ temp_noncodesigned="$(mktemp)"
+ trap 'rm -rf -- "$temp_noncodesigned"' EXIT
+
+ if (( ${#noncodesigned_fragments[@]} )); then
+ cat "${noncodesigned_fragments[@]}" \
+ | sort -u \
+ | sort -k2 \
+ > "$temp_noncodesigned"
+ if [ -e noncodesigned.SHA256SUMS ]; then
+ # The SHA256SUMS already exists, make sure it's exactly what we
+ # expect, error out if not
+ if diff -u noncodesigned.SHA256SUMS "$temp_noncodesigned"; then
+ echo "A noncodesigned.SHA256SUMS file already exists for '${VERSION}' and is up-to-date."
else
- echo "ERR: ${outname}: No outputs found in '${outdir}'"
+ shasum_already_exists noncodesigned.SHA256SUMS
exit 1
fi
- )
- if [ -z "$NO_SIGN" ]; then
- echo "${outname}: Signing SHA256SUMS to produce SHA256SUMS.asc"
- gpg --detach-sign --local-user "$gpg_key_name" --armor --output "$outsigdir"/SHA256SUMS.asc "$outsigdir"/SHA256SUMS
else
- echo "${outname}: Not signing SHA256SUMS as \$NO_SIGN is not empty"
+ mv "$temp_noncodesigned" noncodesigned.SHA256SUMS
fi
- echo ""
-
- trap - ERR # Reset ERR trap
+ else
+ echo "ERR: No noncodesigned outputs found for '${VERSION}', exiting..."
+ exit 1
fi
-done
-
-if (( ${#outdirs_already_attested_to[@]} )); then
-# ...so that we can print them out nicely in a warning message
-cat << EOF
-WARN: Signature directories from '$signer_name' already exist in the specified
- guix.sigs repository for the following output directories and were
- skipped:
+ temp_codesigned="$(mktemp)"
+ trap 'rm -rf -- "$temp_codesigned"' EXIT
+
+ if (( ${#codesigned_fragments[@]} )); then
+ # Note: all.SHA256SUMS attests to all of $sha256sum_fragments, but is
+ # not needed if there are no $codesigned_fragments
+ cat "${sha256sum_fragments[@]}" \
+ | sort -u \
+ | sort -k2 \
+ > "$temp_codesigned"
+ if [ -e codesigned.SHA256SUMS ]; then
+ # The SHA256SUMS already exists, make sure it's exactly what we
+ # expect, error out if not
+ if diff -u all.SHA256SUMS "$temp_codesigned"; then
+ echo "An all.SHA256SUMS file already exists for '${VERSION}' and is up-to-date."
+ else
+ shasum_already_exists all.SHA256SUMS
+ exit 1
+ fi
+ else
+ mv "$temp_codesigned" codesigned.SHA256SUMS
+ fi
+ else
+ # It is fine to have the codesigned outputs be missing (perhaps the
+ # detached codesigs have not been published yet), just print a log
+ # message instead of erroring out
+ echo "INFO: No codesigned outputs found for '${VERSION}', skipping..."
+ fi
-EOF
-for outdir in "${outdirs_already_attested_to[@]}"; do
- echo " '${outdir}'"
- echo " Corresponds to: '$(out_sig_dir "$outdir")'"
+ if [ -z "$NO_SIGN" ]; then
+ echo "Signing SHA256SUMS to produce SHA256SUMS.asc"
+ for i in *.SHA256SUMS; do
+ if [ ! -e "$i".asc ]; then
+ gpg --detach-sign \
+ --local-user "$gpg_key_name" \
+ --armor \
+ --output "$i".asc "$i"
+ else
+ echo "Signature already there"
+ fi
+ done
+ else
+ echo "Not signing SHA256SUMS as \$NO_SIGN is not empty"
+ fi
echo ""
-done
-fi
+)
diff --git a/contrib/guix/guix-build b/contrib/guix/guix-build
index 69c244a6fa..29d6701b25 100755
--- a/contrib/guix/guix-build
+++ b/contrib/guix/guix-build
@@ -139,6 +139,28 @@ for host in $HOSTS; do
done
################
+# VERSION_BASE should have enough space
+################
+
+avail_KiB="$(df -Pk "$VERSION_BASE" | sed 1d | tr -s ' ' | cut -d' ' -f4)"
+total_required_KiB=0
+for host in $HOSTS; do
+ case "$host" in
+ *darwin*) required_KiB=440000 ;;
+ *mingw*) required_KiB=7600000 ;;
+ *) required_KiB=6400000 ;;
+ esac
+ total_required_KiB=$((total_required_KiB+required_KiB))
+done
+
+if (( total_required_KiB > avail_KiB )); then
+ total_required_GiB=$((total_required_KiB / 1048576))
+ avail_GiB=$((avail_KiB / 1048576))
+ echo "Performing a Bitcoin Core Guix build for the selected HOSTS requires ${total_required_GiB} GiB, however, only ${avail_GiB} GiB is available. Please free up some disk space before performing the build."
+ exit 1
+fi
+
+################
# Check that we can connect to the guix-daemon
################
diff --git a/contrib/guix/guix-verify b/contrib/guix/guix-verify
index 629050956c..a6e2c4065e 100755
--- a/contrib/guix/guix-verify
+++ b/contrib/guix/guix-verify
@@ -56,58 +56,87 @@ cmd_usage
exit 1
fi
-################
-# We should be able to find at least one output
-################
+##############
+## Verify ##
+##############
OUTSIGDIR_BASE="${GUIX_SIGS_REPO}/${VERSION}"
-echo "Looking for output signature directories in '${OUTSIGDIR_BASE}'"
+echo "Looking for signature directories in '${OUTSIGDIR_BASE}'"
+echo ""
+
+# Usage: verify compare_manifest current_manifest
+verify() {
+ local compare_manifest="$1"
+ local current_manifest="$2"
+ if ! gpg --quiet --batch --verify "$current_manifest".asc "$current_manifest" 1>&2; then
+ echo "ERR: Failed to verify GPG signature in '${current_manifest}'"
+ echo ""
+ echo "Hint: Either the signature is invalid or the public key is missing"
+ echo ""
+ elif ! diff --report-identical "$compare_manifest" "$current_manifest" 1>&2; then
+ echo "ERR: The SHA256SUMS attestation in these two directories differ:"
+ echo " '${compare_manifest}'"
+ echo " '${current_manifest}'"
+ echo ""
+ else
+ echo "Verified: '${current_manifest}'"
+ echo ""
+ fi
+}
shopt -s nullglob
-OUTSIGDIRS=( "$OUTSIGDIR_BASE"/* ) # This expands to an array of directories...
+all_noncodesigned=( "$OUTSIGDIR_BASE"/*/noncodesigned.SHA256SUMS )
shopt -u nullglob
-if (( ${#OUTSIGDIRS[@]} )); then
- echo "Found output signature directories:"
- for outsigdir in "${OUTSIGDIRS[@]}"; do
- echo " '$outsigdir'"
+echo "--------------------"
+echo ""
+if (( ${#all_noncodesigned[@]} )); then
+ compare_noncodesigned="${all_noncodesigned[0]}"
+
+ for current_manifest in "${all_noncodesigned[@]}"; do
+ verify "$compare_noncodesigned" "$current_manifest"
done
- echo
+
+ echo "DONE: Checking output signatures for noncodesigned.SHA256SUMS"
+ echo ""
else
- echo "ERR: Could not find any output signature directories in ${OUTSIGDIR_BASE}"
- exit 1
+ echo "WARN: No signature directories with noncodesigned.SHA256SUMS found"
+ echo ""
fi
+shopt -s nullglob
+all_all=( "$OUTSIGDIR_BASE"/*/all.SHA256SUMS )
+shopt -u nullglob
-##############
-## Verify ##
-##############
+echo "--------------------"
+echo ""
+if (( ${#all_all[@]} )); then
+ compare_all="${all_all[0]}"
-# MAIN LOGIC: Loop through each output for VERSION and check that the SHA256SUMS
-# and SHA256SUMS.asc file match between signers, using the first
-# available signer as the arbitrary comparison base.
-for outsigdir in "${OUTSIGDIRS[@]}"; do
- echo "BEGIN: Checking output signatures for $(basename "$outsigdir")"
- echo ""
- signer_dirs=( "$outsigdir"/* ) # This expands to an array of directories...
- compare_signer_dir="${signer_dirs[0]}" # ...we just want the first one
- for current_signer_dir in "${signer_dirs[@]}"; do
- if ! gpg --quiet --batch --verify "$current_signer_dir"/SHA256SUMS.asc "$current_signer_dir"/SHA256SUMS; then
- echo "ERR: Failed to verify GPG signature in '${current_signer_dir}/SHA256SUMS.asc'"
- echo ""
- echo "Hint: Either the signature is invalid or the public key is missing"
- echo ""
- elif ! diff --report-identical "$compare_signer_dir"/SHA256SUMS "$current_signer_dir"/SHA256SUMS; then
- echo "ERR: The SHA256SUMS attestation in these two directories differ:"
- echo " '${compare_signer_dir}'"
- echo " '${current_signer_dir}'"
- echo ""
- else
- echo "Verified: '${current_signer_dir}'"
- echo ""
- fi
+ for current_manifest in "${all_all[@]}"; do
+ verify "$compare_all" "$current_manifest"
done
- echo "DONE: Checking output signatures for $(basename "$outsigdir")"
+
+ # Sanity check: there should be no entries that exist in
+ # noncodesigned.SHA256SUMS that doesn't exist in all.SHA256SUMS
+ if [[ "$(comm -23 <(sort "$compare_noncodesigned") <(sort "$compare_all") | wc -c)" -ne 0 ]]; then
+ echo "ERR: There are unique lines in noncodesigned.SHA256SUMS which"
+ echo " do not exist in all.SHA256SUMS, something went very wrong."
+ exit 1
+ fi
+
+ echo "DONE: Checking output signatures for all.SHA256SUMS"
echo ""
+else
+ echo "WARN: No signature directories with all.SHA256SUMS found"
+ echo ""
+fi
+
+echo "===================="
+echo ""
+if (( ${#all_noncodesigned[@]} + ${#all_all[@]} == 0 )); then
+ echo "ERR: Unable to perform any verifications as no signature directories"
+ echo " were found"
echo ""
-done
+ exit 1
+fi
diff --git a/contrib/guix/libexec/build.sh b/contrib/guix/libexec/build.sh
index 46bfa29b74..6741328473 100755
--- a/contrib/guix/libexec/build.sh
+++ b/contrib/guix/libexec/build.sh
@@ -230,20 +230,7 @@ if [ ! -e "$GIT_ARCHIVE" ]; then
git archive --prefix="${DISTNAME}/" --output="$GIT_ARCHIVE" HEAD
fi
-# tmpdir="$(mktemp -d)"
-# (
-# cd "$tmpdir"
-# mkdir -p inputs
-# ln -sf --target-directory=inputs "$GIT_ARCHIVE"
-
-# mkdir -p "$OUTDIR"
-# find -L inputs -type f -print0 | xargs -0 sha256sum > "${OUTDIR}/inputs.SHA256SUMS"
-# )
-
mkdir -p "$OUTDIR"
-cat << EOF > "$OUTDIR"/inputs.SHA256SUMS
-$(sha256sum "$GIT_ARCHIVE" | cut -d' ' -f1) inputs/$(basename "$GIT_ARCHIVE")
-EOF
###########################
# Binary Tarball Building #
@@ -447,4 +434,16 @@ mkdir -p "$DISTSRC"
esac
) # $DISTSRC
-mv --no-target-directory "$OUTDIR" "$ACTUAL_OUTDIR"
+rm -rf "$ACTUAL_OUTDIR"
+mv --no-target-directory "$OUTDIR" "$ACTUAL_OUTDIR" \
+ || ( rm -rf "$ACTUAL_OUTDIR" && exit 1 )
+
+(
+ cd /outdir-base
+ {
+ echo "$GIT_ARCHIVE"
+ find "$ACTUAL_OUTDIR" -type f
+ } | xargs realpath --relative-base="$PWD" \
+ | xargs sha256sum \
+ | sponge "$ACTUAL_OUTDIR"/SHA256SUMS.part
+)
diff --git a/contrib/guix/libexec/codesign.sh b/contrib/guix/libexec/codesign.sh
index 46b42a5712..b1eec686ec 100755
--- a/contrib/guix/libexec/codesign.sh
+++ b/contrib/guix/libexec/codesign.sh
@@ -55,10 +55,6 @@ if [ ! -e "$CODESIGNATURE_GIT_ARCHIVE" ]; then
fi
mkdir -p "$OUTDIR"
-cat << EOF > "$OUTDIR"/inputs.SHA256SUMS
-$(sha256sum "$UNSIGNED_TARBALL" | cut -d' ' -f1) inputs/$(basename "$UNSIGNED_TARBALL")
-$(sha256sum "$CODESIGNATURE_GIT_ARCHIVE" | cut -d' ' -f1) inputs/$(basename "$CODESIGNATURE_GIT_ARCHIVE")
-EOF
mkdir -p "$DISTSRC"
(
@@ -100,4 +96,17 @@ mkdir -p "$DISTSRC"
esac
) # $DISTSRC
-mv --no-target-directory "$OUTDIR" "$ACTUAL_OUTDIR"
+rm -rf "$ACTUAL_OUTDIR"
+mv --no-target-directory "$OUTDIR" "$ACTUAL_OUTDIR" \
+ || ( rm -rf "$ACTUAL_OUTDIR" && exit 1 )
+
+(
+ cd /outdir-base
+ {
+ echo "$UNSIGNED_TARBALL"
+ echo "$CODESIGNATURE_GIT_ARCHIVE"
+ find "$ACTUAL_OUTDIR" -type f
+ } | xargs realpath --relative-base="$PWD" \
+ | xargs sha256sum \
+ | sponge "$ACTUAL_OUTDIR"/SHA256SUMS.part
+)
diff --git a/contrib/guix/manifest.scm b/contrib/guix/manifest.scm
index d2bc789b60..ba168a2a4a 100644
--- a/contrib/guix/manifest.scm
+++ b/contrib/guix/manifest.scm
@@ -22,6 +22,7 @@
(gnu packages linux)
(gnu packages llvm)
(gnu packages mingw)
+ (gnu packages moreutils)
(gnu packages perl)
(gnu packages pkg-config)
(gnu packages python)
@@ -205,7 +206,7 @@ chain for " target " development."))
(define-public lief
(package
(name "python-lief")
- (version "0.11.4")
+ (version "0.11.5")
(source
(origin
(method git-fetch)
@@ -215,7 +216,7 @@ chain for " target " development."))
(file-name (git-file-name name version))
(sha256
(base32
- "0h4kcwr9z478almjqhmils8imfpflzk0r7d05g4xbkdyknn162qf"))))
+ "0qahjfg1n0x76ps2mbyljvws1l3qhkqvmxqbahps4qgywl2hbdkj"))))
(build-system python-build-system)
(native-inputs
`(("cmake" ,cmake)))
@@ -524,7 +525,7 @@ and endian independent.")
(license license:expat)))
(define-public python-signapple
- (let ((commit "4ff1c1754e37042c002a3f6375c47fd931f2030b"))
+ (let ((commit "b084cbbf44d5330448ffce0c7d118f75781b64bd"))
(package
(name "python-signapple")
(version (git-version "0.1" "1" commit))
@@ -532,12 +533,12 @@ and endian independent.")
(origin
(method git-fetch)
(uri (git-reference
- (url "https://github.com/dongcarl/signapple")
+ (url "https://github.com/achow101/signapple")
(commit commit)))
(file-name (git-file-name name commit))
(sha256
(base32
- "043czyzfm04rcx5xsp59vsppla3vm5g45dbp1npy2hww4066rlnh"))))
+ "0k7inccl2mzac3wq4asbr0kl8s4cghm8982z54kfascqg45shv01"))))
(build-system python-build-system)
(propagated-inputs
`(("python-asn1crypto" ,python-asn1crypto)
@@ -572,6 +573,7 @@ inspecting signatures in Mach-O binaries.")
patch
gawk
sed
+ moreutils
;; Compression and archiving
tar
bzip2
diff --git a/contrib/signet/README.md b/contrib/signet/README.md
index 71dc2f9638..706b296c54 100644
--- a/contrib/signet/README.md
+++ b/contrib/signet/README.md
@@ -21,27 +21,28 @@ accept one claim per day. See `--password` above.
miner
=====
-To mine the first block in your custom chain, you can run:
+You will first need to pick a difficulty target. Since signet chains are primarily protected by a signature rather than proof of work, there is no need to spend as much energy as possible mining, however you may wish to choose to spend more time than the absolute minimum. The calibrate subcommand can be used to pick a target appropriate for your hardware, eg:
cd src/
- CLI="./bitcoin-cli -conf=mysignet.conf"
- MINER="..contrib/signet/miner"
+ MINER="../contrib/signet/miner"
GRIND="./bitcoin-util grind"
- ADDR=$($CLI -signet getnewaddress)
- $MINER --cli="$CLI" generate --grind-cmd="$GRIND" --address="$ADDR" --set-block-time=-1
-
-This will mine a block with the current timestamp. If you want to backdate the chain, you can give a different timestamp to --set-block-time.
-
-You will then need to pick a difficulty target. Since signet chains are primarily protected by a signature rather than proof of work, there is no need to spend as much energy as possible mining, however you may wish to choose to spend more time than the absolute minimum. The calibrate subcommand can be used to pick a target, eg:
-
$MINER calibrate --grind-cmd="$GRIND"
nbits=1e00f403 for 25s average mining time
It defaults to estimating an nbits value resulting in 25s average time to find a block, but the --seconds parameter can be used to pick a different target, or the --nbits parameter can be used to estimate how long it will take for a given difficulty.
-Using the --ongoing parameter will then cause the signet miner to create blocks indefinitely. It will pick the time between blocks so that difficulty is adjusted to match the provided --nbits value.
+To mine the first block in your custom chain, you can run:
- $MINER --cli="$CLI" generate --grind-cmd="$GRIND" --address="$ADDR" --nbits=1e00f403 --ongoing
+ CLI="./bitcoin-cli -conf=mysignet.conf"
+ ADDR=$($CLI -signet getnewaddress)
+ NBITS=1e00f403
+ $MINER --cli="$CLI" generate --grind-cmd="$GRIND" --address="$ADDR" --nbits=$NBITS
+
+This will mine a single block with a backdated timestamp designed to allow 100 blocks to be mined as quickly as possible, so that it is possible to do transactions.
+
+Adding the --ongoing parameter will then cause the signet miner to create blocks indefinitely. It will pick the time between blocks so that difficulty is adjusted to match the provided --nbits value.
+
+ $MINER --cli="$CLI" generate --grind-cmd="$GRIND" --address="$ADDR" --nbits=$NBITS --ongoing
Other options
-------------
@@ -50,9 +51,11 @@ The --debug and --quiet options are available to control how noisy the signet mi
Instead of specifying --ongoing, you can specify --max-blocks=N to mine N blocks and stop.
-Instead of using a single address, a ranged descriptor may be provided instead (via the --descriptor parameter), with the reward for the block at height H being sent to the H'th address generated from the descriptor.
+The --set-block-time option is available to manually move timestamps forward or backward (subject to the rules that blocktime must be greater than mediantime, and dates can't be more than two hours in the future). It can only be used when mining a single block (ie, not when using --ongoing or --max-blocks greater than 1).
+
+Instead of using a single address, a ranged descriptor may be provided via the --descriptor parameter, with the reward for the block at height H being sent to the H'th address generated from the descriptor.
-Instead of calculating a specific nbits value, --min-nbits can be specified instead, in which case the mininmum signet difficulty will be targeted.
+Instead of calculating a specific nbits value, --min-nbits can be specified instead, in which case the minimum signet difficulty will be targeted. Signet's minimum difficulty corresponds to --nbits=1e0377ae.
By default, the signet miner mines blocks at fixed intervals with minimal variation. If you want blocks to appear more randomly, as they do in mainnet, specify the --poisson option.
@@ -76,5 +79,5 @@ These steps can instead be done explicitly:
$MINER --cli="$CLI" solvepsbt --grind-cmd="$GRIND" |
$CLI -signet -stdin submitblock
-This is intended to allow you to replace part of the pipeline for further experimentation, if desired.
+This is intended to allow you to replace part of the pipeline for further experimentation (eg, to sign the block with a hardware wallet).
diff --git a/contrib/signet/miner b/contrib/signet/miner
index a3fba49d0e..f1360818ae 100755
--- a/contrib/signet/miner
+++ b/contrib/signet/miner
@@ -428,10 +428,13 @@ def do_generate(args):
action_time = now
is_mine = True
elif bestheader["height"] == 0:
- logging.error("When mining first block in a new signet, must specify --set-block-time")
- return 1
+ time_delta = next_block_delta(int(bestheader["bits"], 16), bci["bestblockhash"], ultimate_target, args.poisson)
+ time_delta *= 100 # 100 blocks
+ logging.info("Backdating time for first block to %d minutes ago" % (time_delta/60))
+ mine_time = now - time_delta
+ action_time = now
+ is_mine = True
else:
-
time_delta = next_block_delta(int(bestheader["bits"], 16), bci["bestblockhash"], ultimate_target, args.poisson)
mine_time = bestheader["time"] + time_delta
@@ -520,12 +523,11 @@ def do_calibrate(args):
sys.stderr.write("Can only specify one of --nbits or --seconds\n")
return 1
if args.nbits is not None and len(args.nbits) != 8:
- sys.stderr.write("Must specify 8 hex digits for --nbits")
+ sys.stderr.write("Must specify 8 hex digits for --nbits\n")
return 1
TRIALS = 600 # gets variance down pretty low
TRIAL_BITS = 0x1e3ea75f # takes about 5m to do 600 trials
- #TRIAL_BITS = 0x1e7ea75f # XXX
header = CBlockHeader()
header.nBits = TRIAL_BITS
@@ -533,23 +535,14 @@ def do_calibrate(args):
start = time.time()
count = 0
- #CHECKS=[]
for i in range(TRIALS):
header.nTime = i
header.nNonce = 0
headhex = header.serialize().hex()
cmd = args.grind_cmd.split(" ") + [headhex]
newheadhex = subprocess.run(cmd, stdout=subprocess.PIPE, input=b"", check=True).stdout.strip()
- #newhead = FromHex(CBlockHeader(), newheadhex.decode('utf8'))
- #count += newhead.nNonce
- #if (i+1) % 100 == 0:
- # CHECKS.append((i+1, count, time.time()-start))
-
- #print("checks =", [c*1.0 / (b*targ*2**-256) for _,b,c in CHECKS])
avg = (time.time() - start) * 1.0 / TRIALS
- #exp_count = 2**256 / targ * TRIALS
- #print("avg =", avg, "count =", count, "exp_count =", exp_count)
if args.nbits is not None:
want_targ = nbits_to_target(int(args.nbits,16))
@@ -590,7 +583,6 @@ def main():
generate.add_argument("--nbits", default=None, type=str, help="Target nBits (specify difficulty)")
generate.add_argument("--min-nbits", action="store_true", help="Target minimum nBits (use min difficulty)")
generate.add_argument("--poisson", action="store_true", help="Simulate randomised block times")
- #generate.add_argument("--signcmd", default=None, type=str, help="Alternative signing command")
generate.add_argument("--multiminer", default=None, type=str, help="Specify which set of blocks to mine (eg: 1-40/100 for the first 40%%, 2/3 for the second 3rd)")
generate.add_argument("--backup-delay", default=300, type=int, help="Seconds to delay before mining blocks reserved for other miners (default=300)")
generate.add_argument("--standby-delay", default=0, type=int, help="Seconds to delay before mining blocks (default=0)")
@@ -605,7 +597,7 @@ def main():
sp.add_argument("--descriptor", default=None, type=str, help="Descriptor for block reward payment")
for sp in [solvepsbt, generate, calibrate]:
- sp.add_argument("--grind-cmd", default=None, type=str, help="Command to grind a block header for proof-of-work")
+ sp.add_argument("--grind-cmd", default=None, type=str, required=(sp==calibrate), help="Command to grind a block header for proof-of-work")
args = parser.parse_args(sys.argv[1:])
diff --git a/depends/packages/qt.mk b/depends/packages/qt.mk
index 52df26eb50..165c27f5e3 100644
--- a/depends/packages/qt.mk
+++ b/depends/packages/qt.mk
@@ -10,7 +10,7 @@ $(package)_patches=fix_qt_pkgconfig.patch mac-qmake.conf fix_no_printer.patch no
$(package)_patches+= fix_android_qmake_conf.patch fix_android_jni_static.patch dont_hardcode_pwd.patch
$(package)_patches+= drop_lrelease_dependency.patch no_sdk_version_check.patch
$(package)_patches+= fix_lib_paths.patch fix_android_pch.patch
-$(package)_patches+= qtbase-moc-ignore-gcc-macro.patch
+$(package)_patches+= qtbase-moc-ignore-gcc-macro.patch fix_limits_header.patch
$(package)_qttranslations_file_name=qttranslations-$($(package)_suffix)
$(package)_qttranslations_sha256_hash=577b0668a777eb2b451c61e8d026d79285371597ce9df06b6dee6c814164b7c3
@@ -231,6 +231,7 @@ define $(package)_preprocess_cmds
patch -p1 -i $($(package)_patch_dir)/no_sdk_version_check.patch && \
patch -p1 -i $($(package)_patch_dir)/fix_lib_paths.patch && \
patch -p1 -i $($(package)_patch_dir)/qtbase-moc-ignore-gcc-macro.patch && \
+ patch -p1 -i $($(package)_patch_dir)/fix_limits_header.patch && \
sed -i.old "s|updateqm.commands = \$$$$\$$$$LRELEASE|updateqm.commands = $($(package)_extract_dir)/qttools/bin/lrelease|" qttranslations/translations/translations.pro && \
mkdir -p qtbase/mkspecs/macx-clang-linux &&\
cp -f qtbase/mkspecs/macx-clang/qplatformdefs.h qtbase/mkspecs/macx-clang-linux/ &&\
diff --git a/depends/patches/qt/fix_limits_header.patch b/depends/patches/qt/fix_limits_header.patch
new file mode 100644
index 0000000000..e4313770e5
--- /dev/null
+++ b/depends/patches/qt/fix_limits_header.patch
@@ -0,0 +1,44 @@
+Fix compiling with GCC 11
+
+See: https://bugreports.qt.io/browse/QTBUG-90395.
+
+Upstream commits:
+ - Qt 5.15 -- unavailable as open source
+ - Qt 6.0: b2af6332ea37e45ab230a7a5d2d278f86d961b83
+ - Qt 6.1: 9c56d4da2ff631a8c1c30475bd792f6c86bda53c
+
+--- old/qtbase/src/corelib/global/qendian.h
++++ new/qtbase/src/corelib/global/qendian.h
+@@ -44,6 +44,8 @@
+ #include <QtCore/qfloat16.h>
+ #include <QtCore/qglobal.h>
+
++#include <limits>
++
+ // include stdlib.h and hope that it defines __GLIBC__ for glibc-based systems
+ #include <stdlib.h>
+ #include <string.h>
+
+--- old/qtbase/src/corelib/tools/qbytearraymatcher.h
++++ new/qtbase/src/corelib/tools/qbytearraymatcher.h
+@@ -42,6 +42,8 @@
+
+ #include <QtCore/qbytearray.h>
+
++#include <limits>
++
+ QT_BEGIN_NAMESPACE
+
+
+
+--- old/qtbase/src/tools/moc/generator.cpp
++++ new/qtbase/src/tools/moc/generator.cpp
+@@ -40,6 +40,8 @@
+ #include <QtCore/qplugin.h>
+ #include <QtCore/qstringview.h>
+
++#include <limits>
++
+ #include <math.h>
+ #include <stdio.h>
+
diff --git a/doc/developer-notes.md b/doc/developer-notes.md
index 2130332eec..c3a63b3523 100644
--- a/doc/developer-notes.md
+++ b/doc/developer-notes.md
@@ -1160,13 +1160,6 @@ A few guidelines for introducing and reviewing new RPC interfaces:
- *Rationale*: If not, the call can not be used with name-based arguments.
-- Set okSafeMode in the RPC command table to a sensible value: safe mode is when the
- blockchain is regarded to be in a confused state, and the client deems it unsafe to
- do anything irreversible such as send. Anything that just queries should be permitted.
-
- - *Rationale*: Troubleshooting a node in safe mode is difficult if half the
- RPCs don't work.
-
- Add every non-string RPC argument `(method, idx, name)` to the table `vRPCConvertParams` in `rpc/client.cpp`.
- *Rationale*: `bitcoin-cli` and the GUI debug console use this table to determine how to
diff --git a/doc/release-notes.md b/doc/release-notes.md
index 53106c9f82..cb7adde470 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -59,6 +59,13 @@ Notable changes
P2P and network changes
-----------------------
+- This release removes support for Tor version 2 hidden services in favor of Tor
+ v3 only, as the Tor network [dropped support for Tor
+ v2](https://blog.torproject.org/v2-deprecation-timeline) with the release of
+ Tor version 0.4.6. Henceforth, Bitcoin Core ignores Tor v2 addresses; it
+ neither rumors them over the network to other peers, nor stores them in memory
+ or to `peers.dat`. (#22050)
+
- Added NAT-PMP port mapping support via
[`libnatpmp`](https://miniupnp.tuxfamily.org/libnatpmp.html). (#18077)
@@ -142,8 +149,8 @@ Tools and Utilities
- A new CLI `-addrinfo` command returns the number of addresses known to the
node per network type (including Tor v2 versus v3) and total. This can be
useful to see if the node knows enough addresses in a network to use options
- like `-onlynet=<network>` or to upgrade to current and future Tor releases
- that support Tor v3 addresses only. (#21595)
+ like `-onlynet=<network>` or to upgrade to this release of Bitcoin Core 22.0
+ that supports Tor v3 only. (#21595)
Wallet
------
diff --git a/doc/tor.md b/doc/tor.md
index 2640a6109b..7d134b64e0 100644
--- a/doc/tor.md
+++ b/doc/tor.md
@@ -5,6 +5,14 @@ It is possible to run Bitcoin Core as a Tor onion service, and connect to such s
The following directions assume you have a Tor proxy running on port 9050. Many distributions default to having a SOCKS proxy listening on port 9050, but others may not. In particular, the Tor Browser Bundle defaults to listening on port 9150. See [Tor Project FAQ:TBBSocksPort](https://www.torproject.org/docs/faq.html.en#TBBSocksPort) for how to properly
configure Tor.
+## Compatibility
+
+- Starting with version 22.0, Bitcoin Core only supports Tor version 3 hidden
+ services (Tor v3). Tor v2 addresses are ignored by Bitcoin Core and neither
+ relayed nor stored.
+
+- Tor removed v2 support beginning with version 0.4.6.
+
## How to see information about your Tor configuration via Bitcoin Core
There are several ways to see your local onion address in Bitcoin Core:
@@ -18,7 +26,7 @@ information in the debug log about your Tor configuration.
CLI `-addrinfo` returns the number of addresses known to your node per network
type, including Tor v2 and v3. This is useful to see how many onion addresses
are known to your node for `-onlynet=onion` and how many Tor v3 addresses it
-knows when upgrading to current and future Tor releases that support Tor v3 only.
+knows when upgrading to Bitcoin Core v22.0 and up that supports Tor v3 only.
## 1. Run Bitcoin Core behind a Tor proxy
diff --git a/src/Makefile.am b/src/Makefile.am
index 66cb7cec2a..e2ed70556d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -351,6 +351,7 @@ libbitcoin_server_a_SOURCES = \
node/ui_interface.cpp \
noui.cpp \
policy/fees.cpp \
+ policy/packages.cpp \
policy/rbf.cpp \
policy/settings.cpp \
pow.cpp \
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index 105d09f730..fc2fd80166 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -35,11 +35,12 @@ BITCOIN_TEST_SUITE = \
$(TEST_UTIL_H)
FUZZ_SUITE_LD_COMMON = \
+ $(LIBTEST_UTIL) \
+ $(LIBTEST_FUZZ) \
$(LIBBITCOIN_SERVER) \
+ $(LIBBITCOIN_WALLET) \
$(LIBBITCOIN_COMMON) \
$(LIBBITCOIN_UTIL) \
- $(LIBTEST_UTIL) \
- $(LIBTEST_FUZZ) \
$(LIBBITCOIN_CONSENSUS) \
$(LIBBITCOIN_CRYPTO) \
$(LIBBITCOIN_CLI) \
@@ -160,7 +161,6 @@ BITCOIN_TESTS += \
wallet/test/scriptpubkeyman_tests.cpp
FUZZ_SUITE_LD_COMMON +=\
- $(LIBBITCOIN_WALLET) \
$(SQLITE_LIBS) \
$(BDB_LIBS)
diff --git a/src/addrdb.cpp b/src/addrdb.cpp
index c376aced10..bf2f6c7614 100644
--- a/src/addrdb.cpp
+++ b/src/addrdb.cpp
@@ -23,7 +23,7 @@ bool SerializeDB(Stream& stream, const Data& data)
{
// Write and commit header, data
try {
- CHashWriter hasher(SER_DISK, CLIENT_VERSION);
+ CHashWriter hasher(stream.GetType(), stream.GetVersion());
stream << Params().MessageStart() << data;
hasher << Params().MessageStart() << data;
stream << hasher.GetHash();
@@ -35,7 +35,7 @@ bool SerializeDB(Stream& stream, const Data& data)
}
template <typename Data>
-bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data& data)
+bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data& data, int version)
{
// Generate random temporary filename
uint16_t randv = 0;
@@ -45,7 +45,7 @@ bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data
// open temp output file, and associate with CAutoFile
fs::path pathTmp = gArgs.GetDataDirNet() / tmpfn;
FILE *file = fsbridge::fopen(pathTmp, "wb");
- CAutoFile fileout(file, SER_DISK, CLIENT_VERSION);
+ CAutoFile fileout(file, SER_DISK, version);
if (fileout.IsNull()) {
fileout.fclose();
remove(pathTmp);
@@ -106,11 +106,11 @@ bool DeserializeDB(Stream& stream, Data& data, bool fCheckSum = true)
}
template <typename Data>
-bool DeserializeFileDB(const fs::path& path, Data& data)
+bool DeserializeFileDB(const fs::path& path, Data& data, int version)
{
// open input file, and associate with CAutoFile
FILE* file = fsbridge::fopen(path, "rb");
- CAutoFile filein(file, SER_DISK, CLIENT_VERSION);
+ CAutoFile filein(file, SER_DISK, version);
if (filein.IsNull()) {
LogPrintf("Missing or invalid file %s\n", path.string());
return false;
@@ -125,12 +125,12 @@ CBanDB::CBanDB(fs::path ban_list_path) : m_ban_list_path(std::move(ban_list_path
bool CBanDB::Write(const banmap_t& banSet)
{
- return SerializeFileDB("banlist", m_ban_list_path, banSet);
+ return SerializeFileDB("banlist", m_ban_list_path, banSet, CLIENT_VERSION);
}
bool CBanDB::Read(banmap_t& banSet)
{
- return DeserializeFileDB(m_ban_list_path, banSet);
+ return DeserializeFileDB(m_ban_list_path, banSet, CLIENT_VERSION);
}
CAddrDB::CAddrDB()
@@ -140,12 +140,12 @@ CAddrDB::CAddrDB()
bool CAddrDB::Write(const CAddrMan& addr)
{
- return SerializeFileDB("peers", pathAddr, addr);
+ return SerializeFileDB("peers", pathAddr, addr, CLIENT_VERSION);
}
bool CAddrDB::Read(CAddrMan& addr)
{
- return DeserializeFileDB(pathAddr, addr);
+ return DeserializeFileDB(pathAddr, addr, CLIENT_VERSION);
}
bool CAddrDB::Read(CAddrMan& addr, CDataStream& ssPeers)
@@ -161,13 +161,13 @@ bool CAddrDB::Read(CAddrMan& addr, CDataStream& ssPeers)
void DumpAnchors(const fs::path& anchors_db_path, const std::vector<CAddress>& anchors)
{
LOG_TIME_SECONDS(strprintf("Flush %d outbound block-relay-only peer addresses to anchors.dat", anchors.size()));
- SerializeFileDB("anchors", anchors_db_path, anchors);
+ SerializeFileDB("anchors", anchors_db_path, anchors, CLIENT_VERSION | ADDRV2_FORMAT);
}
std::vector<CAddress> ReadAnchors(const fs::path& anchors_db_path)
{
std::vector<CAddress> anchors;
- if (DeserializeFileDB(anchors_db_path, anchors)) {
+ if (DeserializeFileDB(anchors_db_path, anchors, CLIENT_VERSION | ADDRV2_FORMAT)) {
LogPrintf("Loaded %i addresses from %s\n", anchors.size(), anchors_db_path.filename());
} else {
anchors.clear();
diff --git a/src/addrman.cpp b/src/addrman.cpp
index 14b412a038..8f702b5a8c 100644
--- a/src/addrman.cpp
+++ b/src/addrman.cpp
@@ -12,6 +12,8 @@
#include <cmath>
#include <optional>
+#include <unordered_map>
+#include <unordered_set>
int CAddrInfo::GetTriedBucket(const uint256& nKey, const std::vector<bool> &asmap) const
{
@@ -77,12 +79,14 @@ double CAddrInfo::GetChance(int64_t nNow) const
CAddrInfo* CAddrMan::Find(const CNetAddr& addr, int* pnId)
{
- std::map<CNetAddr, int>::iterator it = mapAddr.find(addr);
+ AssertLockHeld(cs);
+
+ const auto it = mapAddr.find(addr);
if (it == mapAddr.end())
return nullptr;
if (pnId)
*pnId = (*it).second;
- std::map<int, CAddrInfo>::iterator it2 = mapInfo.find((*it).second);
+ const auto it2 = mapInfo.find((*it).second);
if (it2 != mapInfo.end())
return &(*it2).second;
return nullptr;
@@ -90,6 +94,8 @@ CAddrInfo* CAddrMan::Find(const CNetAddr& addr, int* pnId)
CAddrInfo* CAddrMan::Create(const CAddress& addr, const CNetAddr& addrSource, int* pnId)
{
+ AssertLockHeld(cs);
+
int nId = nIdCount++;
mapInfo[nId] = CAddrInfo(addr, addrSource);
mapAddr[addr] = nId;
@@ -102,6 +108,8 @@ CAddrInfo* CAddrMan::Create(const CAddress& addr, const CNetAddr& addrSource, in
void CAddrMan::SwapRandom(unsigned int nRndPos1, unsigned int nRndPos2)
{
+ AssertLockHeld(cs);
+
if (nRndPos1 == nRndPos2)
return;
@@ -122,6 +130,8 @@ void CAddrMan::SwapRandom(unsigned int nRndPos1, unsigned int nRndPos2)
void CAddrMan::Delete(int nId)
{
+ AssertLockHeld(cs);
+
assert(mapInfo.count(nId) != 0);
CAddrInfo& info = mapInfo[nId];
assert(!info.fInTried);
@@ -136,6 +146,8 @@ void CAddrMan::Delete(int nId)
void CAddrMan::ClearNew(int nUBucket, int nUBucketPos)
{
+ AssertLockHeld(cs);
+
// if there is an entry in the specified bucket, delete it.
if (vvNew[nUBucket][nUBucketPos] != -1) {
int nIdDelete = vvNew[nUBucket][nUBucketPos];
@@ -151,6 +163,8 @@ void CAddrMan::ClearNew(int nUBucket, int nUBucketPos)
void CAddrMan::MakeTried(CAddrInfo& info, int nId)
{
+ AssertLockHeld(cs);
+
// remove the entry from all new buckets
for (int bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; bucket++) {
int pos = info.GetBucketPosition(nKey, true, bucket);
@@ -199,6 +213,8 @@ void CAddrMan::MakeTried(CAddrInfo& info, int nId)
void CAddrMan::Good_(const CService& addr, bool test_before_evict, int64_t nTime)
{
+ AssertLockHeld(cs);
+
int nId;
nLastGood = nTime;
@@ -265,6 +281,8 @@ void CAddrMan::Good_(const CService& addr, bool test_before_evict, int64_t nTime
bool CAddrMan::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimePenalty)
{
+ AssertLockHeld(cs);
+
if (!addr.IsRoutable())
return false;
@@ -338,6 +356,8 @@ bool CAddrMan::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimeP
void CAddrMan::Attempt_(const CService& addr, bool fCountFailure, int64_t nTime)
{
+ AssertLockHeld(cs);
+
CAddrInfo* pinfo = Find(addr);
// if not found, bail out
@@ -360,7 +380,9 @@ void CAddrMan::Attempt_(const CService& addr, bool fCountFailure, int64_t nTime)
CAddrInfo CAddrMan::Select_(bool newOnly)
{
- if (size() == 0)
+ AssertLockHeld(cs);
+
+ if (vRandom.empty())
return CAddrInfo();
if (newOnly && nNew == 0)
@@ -408,8 +430,10 @@ CAddrInfo CAddrMan::Select_(bool newOnly)
#ifdef DEBUG_ADDRMAN
int CAddrMan::Check_()
{
- std::set<int> setTried;
- std::map<int, int> mapNew;
+ AssertLockHeld(cs);
+
+ std::unordered_set<int> setTried;
+ std::unordered_map<int, int> mapNew;
if (vRandom.size() != (size_t)(nTried + nNew))
return -7;
@@ -485,6 +509,8 @@ int CAddrMan::Check_()
void CAddrMan::GetAddr_(std::vector<CAddress>& vAddr, size_t max_addresses, size_t max_pct, std::optional<Network> network)
{
+ AssertLockHeld(cs);
+
size_t nNodes = vRandom.size();
if (max_pct != 0) {
nNodes = max_pct * nNodes / 100;
@@ -517,6 +543,8 @@ void CAddrMan::GetAddr_(std::vector<CAddress>& vAddr, size_t max_addresses, size
void CAddrMan::Connected_(const CService& addr, int64_t nTime)
{
+ AssertLockHeld(cs);
+
CAddrInfo* pinfo = Find(addr);
// if not found, bail out
@@ -537,6 +565,8 @@ void CAddrMan::Connected_(const CService& addr, int64_t nTime)
void CAddrMan::SetServices_(const CService& addr, ServiceFlags nServices)
{
+ AssertLockHeld(cs);
+
CAddrInfo* pinfo = Find(addr);
// if not found, bail out
@@ -555,6 +585,8 @@ void CAddrMan::SetServices_(const CService& addr, ServiceFlags nServices)
void CAddrMan::ResolveCollisions_()
{
+ AssertLockHeld(cs);
+
for (std::set<int>::iterator it = m_tried_collisions.begin(); it != m_tried_collisions.end();) {
int id_new = *it;
@@ -614,6 +646,8 @@ void CAddrMan::ResolveCollisions_()
CAddrInfo CAddrMan::SelectTriedCollision_()
{
+ AssertLockHeld(cs);
+
if (m_tried_collisions.size() == 0) return CAddrInfo();
std::set<int>::iterator it = m_tried_collisions.begin();
diff --git a/src/addrman.h b/src/addrman.h
index 41994288db..665e253192 100644
--- a/src/addrman.h
+++ b/src/addrman.h
@@ -8,22 +8,22 @@
#include <clientversion.h>
#include <config/bitcoin-config.h>
+#include <fs.h>
+#include <hash.h>
#include <netaddress.h>
#include <protocol.h>
#include <random.h>
+#include <streams.h>
#include <sync.h>
#include <timedata.h>
#include <tinyformat.h>
#include <util/system.h>
-#include <fs.h>
-#include <hash.h>
#include <iostream>
-#include <map>
#include <optional>
#include <set>
#include <stdint.h>
-#include <streams.h>
+#include <unordered_map>
#include <vector>
/**
@@ -231,6 +231,7 @@ public:
*/
template <typename Stream>
void Serialize(Stream& s_) const
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
@@ -251,7 +252,7 @@ public:
int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT ^ (1 << 30);
s << nUBuckets;
- std::map<int, int> mapUnkIds;
+ std::unordered_map<int, int> mapUnkIds;
int nIds = 0;
for (const auto& entry : mapInfo) {
mapUnkIds[entry.first] = nIds;
@@ -296,10 +297,11 @@ public:
template <typename Stream>
void Unserialize(Stream& s_)
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
- Clear();
+ assert(vRandom.empty());
Format format;
s_ >> Using<CustomUintFormatter<1>>(format);
@@ -435,13 +437,13 @@ public:
// Prune new entries with refcount 0 (as a result of collisions).
int nLostUnk = 0;
- for (std::map<int, CAddrInfo>::const_iterator it = mapInfo.begin(); it != mapInfo.end(); ) {
+ for (auto it = mapInfo.cbegin(); it != mapInfo.cend(); ) {
if (it->second.fInTried == false && it->second.nRefCount == 0) {
- std::map<int, CAddrInfo>::const_iterator itCopy = it++;
+ const auto itCopy = it++;
Delete(itCopy->first);
- nLostUnk++;
+ ++nLostUnk;
} else {
- it++;
+ ++it;
}
}
if (nLost + nLostUnk > 0) {
@@ -452,6 +454,7 @@ public:
}
void Clear()
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
std::vector<int>().swap(vRandom);
@@ -487,26 +490,15 @@ public:
//! Return the number of (unique) addresses in all tables.
size_t size() const
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs); // TODO: Cache this in an atomic to avoid this overhead
return vRandom.size();
}
- //! Consistency check
- void Check()
- {
-#ifdef DEBUG_ADDRMAN
- {
- LOCK(cs);
- int err;
- if ((err=Check_()))
- LogPrintf("ADDRMAN CONSISTENCY CHECK FAILED!!! err=%i\n", err);
- }
-#endif
- }
-
//! Add a single address.
bool Add(const CAddress &addr, const CNetAddr& source, int64_t nTimePenalty = 0)
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
bool fRet = false;
@@ -521,6 +513,7 @@ public:
//! Add multiple addresses.
bool Add(const std::vector<CAddress> &vAddr, const CNetAddr& source, int64_t nTimePenalty = 0)
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
int nAdd = 0;
@@ -536,6 +529,7 @@ public:
//! Mark an entry as accessible.
void Good(const CService &addr, bool test_before_evict = true, int64_t nTime = GetAdjustedTime())
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
Check();
@@ -545,6 +539,7 @@ public:
//! Mark an entry as connection attempted to.
void Attempt(const CService &addr, bool fCountFailure, int64_t nTime = GetAdjustedTime())
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
Check();
@@ -554,6 +549,7 @@ public:
//! See if any to-be-evicted tried table entries have been tested and if so resolve the collisions.
void ResolveCollisions()
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
Check();
@@ -563,14 +559,12 @@ public:
//! Randomly select an address in tried that another address is attempting to evict.
CAddrInfo SelectTriedCollision()
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
- CAddrInfo ret;
- {
- LOCK(cs);
- Check();
- ret = SelectTriedCollision_();
- Check();
- }
+ LOCK(cs);
+ Check();
+ const CAddrInfo ret = SelectTriedCollision_();
+ Check();
return ret;
}
@@ -578,14 +572,12 @@ public:
* Choose an address to connect to.
*/
CAddrInfo Select(bool newOnly = false)
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
- CAddrInfo addrRet;
- {
- LOCK(cs);
- Check();
- addrRet = Select_(newOnly);
- Check();
- }
+ LOCK(cs);
+ Check();
+ const CAddrInfo addrRet = Select_(newOnly);
+ Check();
return addrRet;
}
@@ -597,19 +589,19 @@ public:
* @param[in] network Select only addresses of this network (nullopt = all).
*/
std::vector<CAddress> GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network)
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
+ LOCK(cs);
Check();
std::vector<CAddress> vAddr;
- {
- LOCK(cs);
- GetAddr_(vAddr, max_addresses, max_pct, network);
- }
+ GetAddr_(vAddr, max_addresses, max_pct, network);
Check();
return vAddr;
}
//! Outer function for Connected_()
void Connected(const CService &addr, int64_t nTime = GetAdjustedTime())
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
Check();
@@ -618,6 +610,7 @@ public:
}
void SetServices(const CService &addr, ServiceFlags nServices)
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
Check();
@@ -633,8 +626,8 @@ protected:
FastRandomContext insecure_rand;
private:
- //! critical section to protect the inner data structures
- mutable RecursiveMutex cs;
+ //! A mutex to protect the inner data structures.
+ mutable Mutex cs;
//! Serialization versions.
enum Format : uint8_t {
@@ -662,10 +655,10 @@ private:
int nIdCount GUARDED_BY(cs);
//! table with information about all nIds
- std::map<int, CAddrInfo> mapInfo GUARDED_BY(cs);
+ std::unordered_map<int, CAddrInfo> mapInfo GUARDED_BY(cs);
//! find an nId based on its network address
- std::map<CNetAddr, int> mapAddr GUARDED_BY(cs);
+ std::unordered_map<CNetAddr, int, CNetAddrHash> mapAddr GUARDED_BY(cs);
//! randomly-ordered vector of all nIds
std::vector<int> vRandom GUARDED_BY(cs);
@@ -725,6 +718,19 @@ private:
//! Return a random to-be-evicted tried table address.
CAddrInfo SelectTriedCollision_() EXCLUSIVE_LOCKS_REQUIRED(cs);
+ //! Consistency check
+ void Check()
+ EXCLUSIVE_LOCKS_REQUIRED(cs)
+ {
+#ifdef DEBUG_ADDRMAN
+ AssertLockHeld(cs);
+ const int err = Check_();
+ if (err) {
+ LogPrintf("ADDRMAN CONSISTENCY CHECK FAILED!!! err=%i\n", err);
+ }
+#endif
+ }
+
#ifdef DEBUG_ADDRMAN
//! Perform consistency check. Returns an error code or zero.
int Check_() EXCLUSIVE_LOCKS_REQUIRED(cs);
diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp
index c279a9af2f..5beb833b48 100644
--- a/src/bench/coin_selection.cpp
+++ b/src/bench/coin_selection.cpp
@@ -56,7 +56,7 @@ static void CoinSelection(benchmark::Bench& bench)
bench.run([&] {
std::set<CInputCoin> setCoinsRet;
CAmount nValueRet;
- bool success = wallet.SelectCoinsMinConf(1003 * COIN, filter_standard, coins, setCoinsRet, nValueRet, coin_selection_params);
+ bool success = wallet.AttemptSelection(1003 * COIN, filter_standard, coins, setCoinsRet, nValueRet, coin_selection_params);
assert(success);
assert(nValueRet == 1003 * COIN);
assert(setCoinsRet.size() == 2);
diff --git a/src/bench/duplicate_inputs.cpp b/src/bench/duplicate_inputs.cpp
index 4f6e1122b8..8703a1cf94 100644
--- a/src/bench/duplicate_inputs.cpp
+++ b/src/bench/duplicate_inputs.cpp
@@ -25,7 +25,6 @@ static void DuplicateInputs(benchmark::Bench& bench)
CMutableTransaction naughtyTx{};
LOCK(cs_main);
- assert(std::addressof(::ChainActive()) == std::addressof(testing_setup->m_node.chainman->ActiveChain()));
CBlockIndex* pindexPrev = testing_setup->m_node.chainman->ActiveChain().Tip();
assert(pindexPrev != nullptr);
block.nBits = GetNextWorkRequired(pindexPrev, &block, chainparams.GetConsensus());
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index 93ac4b8f7e..3fc87ae1ff 100644
--- a/src/bitcoin-tx.cpp
+++ b/src/bitcoin-tx.cpp
@@ -506,11 +506,12 @@ static void MutateTxDelOutput(CMutableTransaction& tx, const std::string& strOut
tx.vout.erase(tx.vout.begin() + outIdx);
}
-static const unsigned int N_SIGHASH_OPTS = 6;
+static const unsigned int N_SIGHASH_OPTS = 7;
static const struct {
const char *flagStr;
int flags;
} sighashOptions[N_SIGHASH_OPTS] = {
+ {"DEFAULT", SIGHASH_DEFAULT},
{"ALL", SIGHASH_ALL},
{"NONE", SIGHASH_NONE},
{"SINGLE", SIGHASH_SINGLE},
diff --git a/src/bitcoin-util.cpp b/src/bitcoin-util.cpp
index af07b28d3d..3f273e75f6 100644
--- a/src/bitcoin-util.cpp
+++ b/src/bitcoin-util.cpp
@@ -43,6 +43,8 @@ static void SetupBitcoinUtilArgs(ArgsManager &argsman)
argsman.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ argsman.AddCommand("grind", "Perform proof of work on hex header string", OptionsCategory::COMMANDS);
+
SetupChainParamsBaseOptions(argsman);
}
@@ -57,15 +59,7 @@ static int AppInitUtil(int argc, char* argv[])
return EXIT_FAILURE;
}
- // Check for chain settings (Params() calls are only valid after this clause)
- try {
- SelectParams(gArgs.GetChainName());
- } catch (const std::exception& e) {
- tfm::format(std::cerr, "Error: %s\n", e.what());
- return EXIT_FAILURE;
- }
-
- if (argc < 2 || HelpRequested(gArgs) || gArgs.IsArgSet("-version")) {
+ if (HelpRequested(gArgs) || gArgs.IsArgSet("-version")) {
// First part of help message is specific to this utility
std::string strUsage = PACKAGE_NAME " bitcoin-util utility version " + FormatFullVersion() + "\n";
if (!gArgs.IsArgSet("-version")) {
@@ -82,6 +76,15 @@ static int AppInitUtil(int argc, char* argv[])
}
return EXIT_SUCCESS;
}
+
+ // Check for chain settings (Params() calls are only valid after this clause)
+ try {
+ SelectParams(gArgs.GetChainName());
+ } catch (const std::exception& e) {
+ tfm::format(std::cerr, "Error: %s\n", e.what());
+ return EXIT_FAILURE;
+ }
+
return CONTINUE_EXECUTION;
}
@@ -111,17 +114,17 @@ static void grind_task(uint32_t nBits, CBlockHeader& header_orig, uint32_t offse
}
}
-static int Grind(int argc, char* argv[], std::string& strPrint)
+static int Grind(std::vector<std::string> args, std::string& strPrint)
{
- if (argc != 1) {
+ if (args.size() != 1) {
strPrint = "Must specify block header to grind";
- return 1;
+ return EXIT_FAILURE;
}
CBlockHeader header;
- if (!DecodeHexBlockHeader(header, argv[0])) {
+ if (!DecodeHexBlockHeader(header, args[0])) {
strPrint = "Could not decode block header";
- return 1;
+ return EXIT_FAILURE;
}
uint32_t nBits = header.nBits;
@@ -137,49 +140,13 @@ static int Grind(int argc, char* argv[], std::string& strPrint)
}
if (!found) {
strPrint = "Could not satisfy difficulty target";
- return 1;
+ return EXIT_FAILURE;
}
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << header;
strPrint = HexStr(ss);
- return 0;
-}
-
-static int CommandLineUtil(int argc, char* argv[])
-{
- if (argc <= 1) return 1;
-
- std::string strPrint;
- int nRet = 0;
-
- try {
- while (argc > 1 && IsSwitchChar(argv[1][0]) && (argv[1][1] != 0)) {
- --argc;
- ++argv;
- }
-
- char* command = argv[1];
- if (strcmp(command, "grind") == 0) {
- nRet = Grind(argc-2, argv+2, strPrint);
- } else {
- strPrint = strprintf("Unknown command %s", command);
- nRet = 1;
- }
- }
- catch (const std::exception& e) {
- strPrint = std::string("error: ") + e.what();
- nRet = EXIT_FAILURE;
- }
- catch (...) {
- PrintExceptionContinue(nullptr, "CommandLineUtil()");
- throw;
- }
-
- if (strPrint != "") {
- tfm::format(nRet == 0 ? std::cout : std::cerr, "%s\n", strPrint);
- }
- return nRet;
+ return EXIT_SUCCESS;
}
#ifdef WIN32
@@ -199,8 +166,7 @@ int main(int argc, char* argv[])
int ret = AppInitUtil(argc, argv);
if (ret != CONTINUE_EXECUTION)
return ret;
- }
- catch (const std::exception& e) {
+ } catch (const std::exception& e) {
PrintExceptionContinue(&e, "AppInitUtil()");
return EXIT_FAILURE;
} catch (...) {
@@ -208,14 +174,29 @@ int main(int argc, char* argv[])
return EXIT_FAILURE;
}
+ const auto cmd = gArgs.GetCommand();
+ if (!cmd) {
+ tfm::format(std::cerr, "Error: must specify a command\n");
+ return EXIT_FAILURE;
+ }
+
int ret = EXIT_FAILURE;
+ std::string strPrint;
try {
- ret = CommandLineUtil(argc, argv);
- }
- catch (const std::exception& e) {
- PrintExceptionContinue(&e, "CommandLineUtil()");
+ if (cmd->command == "grind") {
+ ret = Grind(cmd->args, strPrint);
+ } else {
+ assert(false); // unknown command should be caught earlier
+ }
+ } catch (const std::exception& e) {
+ strPrint = std::string("error: ") + e.what();
} catch (...) {
- PrintExceptionContinue(nullptr, "CommandLineUtil()");
+ strPrint = "unknown error";
+ }
+
+ if (strPrint != "") {
+ tfm::format(ret == 0 ? std::cout : std::cerr, "%s\n", strPrint);
}
+
return ret;
}
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index cf9e4fad44..654679af27 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -112,8 +112,8 @@ static bool AppInit(NodeContext& node, int argc, char* argv[])
util::ThreadSetInternalName("init");
// If Qt is used, parameters/bitcoin.conf are parsed in qt/bitcoin.cpp's main()
- SetupServerArgs(node);
ArgsManager& args = *Assert(node.args);
+ SetupServerArgs(args);
std::string error;
if (!args.ParseParameters(argc, argv, error)) {
return InitError(Untranslated(strprintf("Error parsing command line arguments: %s\n", error)));
diff --git a/src/chain.h b/src/chain.h
index 04a5db5a17..84a3a4e1e7 100644
--- a/src/chain.h
+++ b/src/chain.h
@@ -182,7 +182,7 @@ public:
//!
//! Note: this value is modified to show BLOCK_OPT_WITNESS during UTXO snapshot
//! load to avoid the block index being spuriously rewound.
- //! @sa RewindBlockIndex
+ //! @sa NeedsRedownload
//! @sa ActivateSnapshot
uint32_t nStatus{0};
diff --git a/src/core_read.cpp b/src/core_read.cpp
index b5fc93886d..6108961010 100644
--- a/src/core_read.cpp
+++ b/src/core_read.cpp
@@ -260,6 +260,7 @@ int ParseSighashString(const UniValue& sighash)
int hash_type = SIGHASH_ALL;
if (!sighash.isNull()) {
static std::map<std::string, int> map_sighash_values = {
+ {std::string("DEFAULT"), int(SIGHASH_DEFAULT)},
{std::string("ALL"), int(SIGHASH_ALL)},
{std::string("ALL|ANYONECANPAY"), int(SIGHASH_ALL|SIGHASH_ANYONECANPAY)},
{std::string("NONE"), int(SIGHASH_NONE)},
diff --git a/src/external_signer.cpp b/src/external_signer.cpp
index f16d21fa60..d6388b759a 100644
--- a/src/external_signer.cpp
+++ b/src/external_signer.cpp
@@ -13,9 +13,7 @@
#include <string>
#include <vector>
-#ifdef ENABLE_EXTERNAL_SIGNER
-
-ExternalSigner::ExternalSigner(const std::string& command, const std::string& fingerprint, const std::string chain, const std::string name): m_command(command), m_fingerprint(fingerprint), m_chain(chain), m_name(name) {}
+ExternalSigner::ExternalSigner(const std::string& command, const std::string chain, const std::string& fingerprint, const std::string name): m_command(command), m_chain(chain), m_fingerprint(fingerprint), m_name(name) {}
const std::string ExternalSigner::NetworkArg() const
{
@@ -55,7 +53,7 @@ bool ExternalSigner::Enumerate(const std::string& command, std::vector<ExternalS
if (model_field.isStr() && model_field.getValStr() != "") {
name += model_field.getValStr();
}
- signers.push_back(ExternalSigner(command, fingerprintStr, chain, name));
+ signers.push_back(ExternalSigner(command, chain, fingerprintStr, name));
}
return true;
}
@@ -116,5 +114,3 @@ bool ExternalSigner::SignTransaction(PartiallySignedTransaction& psbtx, std::str
return true;
}
-
-#endif // ENABLE_EXTERNAL_SIGNER
diff --git a/src/external_signer.h b/src/external_signer.h
index b3b202091a..e40fd7f010 100644
--- a/src/external_signer.h
+++ b/src/external_signer.h
@@ -11,8 +11,6 @@
#include <string>
#include <vector>
-#ifdef ENABLE_EXTERNAL_SIGNER
-
struct PartiallySignedTransaction;
//! Enables interaction with an external signing device or service, such as
@@ -23,24 +21,24 @@ private:
//! The command which handles interaction with the external signer.
std::string m_command;
+ //! Bitcoin mainnet, testnet, etc
+ std::string m_chain;
+
+ const std::string NetworkArg() const;
+
public:
//! @param[in] command the command which handles interaction with the external signer
//! @param[in] fingerprint master key fingerprint of the signer
//! @param[in] chain "main", "test", "regtest" or "signet"
//! @param[in] name device name
- ExternalSigner(const std::string& command, const std::string& fingerprint, const std::string chain, const std::string name);
+ ExternalSigner(const std::string& command, const std::string chain, const std::string& fingerprint, const std::string name);
//! Master key fingerprint of the signer
std::string m_fingerprint;
- //! Bitcoin mainnet, testnet, etc
- std::string m_chain;
-
//! Name of signer
std::string m_name;
- const std::string NetworkArg() const;
-
//! Obtain a list of signers. Calls `<command> enumerate`.
//! @param[in] command the command which handles interaction with the external signer
//! @param[in,out] signers vector to which new signers (with a unique master key fingerprint) are added
@@ -65,6 +63,4 @@ public:
bool SignTransaction(PartiallySignedTransaction& psbt, std::string& error);
};
-#endif // ENABLE_EXTERNAL_SIGNER
-
#endif // BITCOIN_EXTERNAL_SIGNER_H
diff --git a/src/index/base.cpp b/src/index/base.cpp
index 3a61af28b7..6fd2701e2e 100644
--- a/src/index/base.cpp
+++ b/src/index/base.cpp
@@ -340,7 +340,6 @@ void BaseIndex::Interrupt()
bool BaseIndex::Start(CChainState& active_chainstate)
{
- assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
m_chainstate = &active_chainstate;
// Need to register this ValidationInterface before running Init(), so that
// callbacks are not missed if Init sets m_synced to true.
diff --git a/src/init.cpp b/src/init.cpp
index 7f64b1acfa..4dc82811f9 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -283,7 +283,7 @@ void Shutdown(NodeContext& node)
init::UnsetGlobals();
node.mempool.reset();
node.fee_estimator.reset();
- node.chainman = nullptr;
+ node.chainman.reset();
node.scheduler.reset();
try {
@@ -347,12 +347,8 @@ static void OnRPCStopped()
LogPrint(BCLog::RPC, "RPC stopped.\n");
}
-void SetupServerArgs(NodeContext& node)
+void SetupServerArgs(ArgsManager& argsman)
{
- assert(!node.args);
- node.args = &gArgs;
- ArgsManager& argsman = *node.args;
-
SetupHelpOptions(argsman);
argsman.AddArg("-help-debug", "Print help message with debugging options and exit", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); // server-only for now
@@ -1179,8 +1175,8 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
node.mempool = std::make_unique<CTxMemPool>(node.fee_estimator.get(), check_ratio);
assert(!node.chainman);
- node.chainman = &g_chainman;
- ChainstateManager& chainman = *Assert(node.chainman);
+ node.chainman = std::make_unique<ChainstateManager>();
+ ChainstateManager& chainman = *node.chainman;
assert(!node.peerman);
node.peerman = PeerManager::make(chainparams, *node.connman, *node.addrman, node.banman.get(),
@@ -1385,7 +1381,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
// If the loaded chain has a wrong genesis, bail out immediately
// (we're likely using a testnet datadir, or the other way around).
if (!chainman.BlockIndex().empty() &&
- !g_chainman.m_blockman.LookupBlockIndex(chainparams.GetConsensus().hashGenesisBlock)) {
+ !chainman.m_blockman.LookupBlockIndex(chainparams.GetConsensus().hashGenesisBlock)) {
return InitError(_("Incorrect or no genesis block found. Wrong datadir for network?"));
}
@@ -1400,7 +1396,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
// If we're not mid-reindex (based on disk + args), add a genesis block on disk
// (otherwise we use the one already on disk).
// This is called again in ThreadImport after the reindex completes.
- if (!fReindex && !::ChainstateActive().LoadGenesisBlock(chainparams)) {
+ if (!fReindex && !chainman.ActiveChainstate().LoadGenesisBlock(chainparams)) {
strLoadError = _("Error initializing block database");
break;
}
@@ -1549,21 +1545,21 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
// ********************************************************* Step 8: start indexers
if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
g_txindex = std::make_unique<TxIndex>(nTxIndexCache, false, fReindex);
- if (!g_txindex->Start(::ChainstateActive())) {
+ if (!g_txindex->Start(chainman.ActiveChainstate())) {
return false;
}
}
for (const auto& filter_type : g_enabled_filter_types) {
InitBlockFilterIndex(filter_type, filter_index_cache, false, fReindex);
- if (!GetBlockFilterIndex(filter_type)->Start(::ChainstateActive())) {
+ if (!GetBlockFilterIndex(filter_type)->Start(chainman.ActiveChainstate())) {
return false;
}
}
if (args.GetBoolArg("-coinstatsindex", DEFAULT_COINSTATSINDEX)) {
g_coin_stats_index = std::make_unique<CoinStatsIndex>(/* cache size */ 0, false, fReindex);
- if (!g_coin_stats_index->Start(::ChainstateActive())) {
+ if (!g_coin_stats_index->Start(chainman.ActiveChainstate())) {
return false;
}
}
@@ -1611,7 +1607,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
// Either install a handler to notify us when genesis activates, or set fHaveGenesis directly.
// No locking, as this happens before any background thread is started.
boost::signals2::connection block_notify_genesis_wait_connection;
- if (::ChainActive().Tip() == nullptr) {
+ if (chainman.ActiveChain().Tip() == nullptr) {
block_notify_genesis_wait_connection = uiInterface.NotifyBlockTip_connect(std::bind(BlockNotifyGenesisWait, std::placeholders::_2));
} else {
fHaveGenesis = true;
diff --git a/src/init.h b/src/init.h
index 328eda9c7e..b856468e5d 100644
--- a/src/init.h
+++ b/src/init.h
@@ -69,7 +69,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info
/**
* Register all arguments with the ArgsManager
*/
-void SetupServerArgs(NodeContext& node);
+void SetupServerArgs(ArgsManager& argsman);
/** Returns licensing information (for -version) */
std::string LicenseInfo();
diff --git a/src/init/bitcoin-node.cpp b/src/init/bitcoin-node.cpp
index 49684ede83..6b6157c139 100644
--- a/src/init/bitcoin-node.cpp
+++ b/src/init/bitcoin-node.cpp
@@ -6,6 +6,7 @@
#include <interfaces/init.h>
#include <interfaces/ipc.h>
#include <node/context.h>
+#include <util/system.h>
#include <memory>
@@ -20,6 +21,7 @@ public:
: m_node(node),
m_ipc(interfaces::MakeIpc(EXE_NAME, arg0, *this))
{
+ m_node.args = &gArgs;
m_node.init = this;
}
std::unique_ptr<interfaces::Echo> makeEcho() override { return interfaces::MakeEcho(); }
diff --git a/src/init/bitcoind.cpp b/src/init/bitcoind.cpp
index 1e17ce4d3c..1d4504c24f 100644
--- a/src/init/bitcoind.cpp
+++ b/src/init/bitcoind.cpp
@@ -4,6 +4,7 @@
#include <interfaces/init.h>
#include <node/context.h>
+#include <util/system.h>
#include <memory>
@@ -14,6 +15,7 @@ class BitcoindInit : public interfaces::Init
public:
BitcoindInit(NodeContext& node) : m_node(node)
{
+ m_node.args = &gArgs;
m_node.init = this;
}
NodeContext& m_node;
diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h
index 3395741b1b..7cac435e96 100644
--- a/src/interfaces/chain.h
+++ b/src/interfaces/chain.h
@@ -277,6 +277,9 @@ public:
//! to be prepared to handle this by ignoring notifications about unknown
//! removed transactions and already added new transactions.
virtual void requestMempoolTransactions(Notifications& notifications) = 0;
+
+ //! Check if Taproot has activated
+ virtual bool isTaprootActive() const = 0;
};
//! Interface to let node manage chain clients (wallets, or maybe tools for
diff --git a/src/interfaces/node.h b/src/interfaces/node.h
index 1dd1e92e2f..77129423db 100644
--- a/src/interfaces/node.h
+++ b/src/interfaces/node.h
@@ -6,6 +6,7 @@
#define BITCOIN_INTERFACES_NODE_H
#include <amount.h> // For CAmount
+#include <external_signer.h>
#include <net.h> // For NodeId
#include <net_types.h> // For banmap_t
#include <netaddress.h> // For Network
@@ -110,6 +111,9 @@ public:
//! Disconnect node by id.
virtual bool disconnectById(NodeId id) = 0;
+ //! List external signers
+ virtual std::vector<ExternalSigner> externalSigners() = 0;
+
//! Get total bytes recv.
virtual int64_t getTotalBytesRecv() = 0;
diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h
index 88f93321f9..fb1febc11b 100644
--- a/src/interfaces/wallet.h
+++ b/src/interfaces/wallet.h
@@ -118,6 +118,9 @@ public:
//! Save or remove receive request.
virtual bool setAddressReceiveRequest(const CTxDestination& dest, const std::string& id, const std::string& value) = 0;
+ //! Display address on external signer
+ virtual bool displayAddress(const CTxDestination& dest) = 0;
+
//! Lock coin.
virtual void lockCoin(const COutPoint& output) = 0;
@@ -195,9 +198,9 @@ public:
virtual TransactionError fillPSBT(int sighash_type,
bool sign,
bool bip32derivs,
+ size_t* n_signed,
PartiallySignedTransaction& psbtx,
- bool& complete,
- size_t* n_signed) = 0;
+ bool& complete) = 0;
//! Get balances.
virtual WalletBalances getBalances() = 0;
@@ -252,6 +255,9 @@ public:
// Return whether private keys enabled.
virtual bool privateKeysDisabled() = 0;
+ // Return whether wallet uses an external signer.
+ virtual bool hasExternalSigner() = 0;
+
// Get default address type.
virtual OutputType getDefaultAddressType() = 0;
diff --git a/src/key.cpp b/src/key.cpp
index 5666adebb8..dcad386e77 100644
--- a/src/key.cpp
+++ b/src/key.cpp
@@ -7,10 +7,13 @@
#include <crypto/common.h>
#include <crypto/hmac_sha512.h>
+#include <hash.h>
#include <random.h>
#include <secp256k1.h>
+#include <secp256k1_extrakeys.h>
#include <secp256k1_recovery.h>
+#include <secp256k1_schnorrsig.h>
static secp256k1_context* secp256k1_context_sign = nullptr;
@@ -258,6 +261,24 @@ bool CKey::SignCompact(const uint256 &hash, std::vector<unsigned char>& vchSig)
return true;
}
+bool CKey::SignSchnorr(const uint256& hash, Span<unsigned char> sig, const uint256* merkle_root, const uint256* aux) const
+{
+ assert(sig.size() == 64);
+ secp256k1_keypair keypair;
+ if (!secp256k1_keypair_create(secp256k1_context_sign, &keypair, begin())) return false;
+ if (merkle_root) {
+ secp256k1_xonly_pubkey pubkey;
+ if (!secp256k1_keypair_xonly_pub(secp256k1_context_sign, &pubkey, nullptr, &keypair)) return false;
+ unsigned char pubkey_bytes[32];
+ if (!secp256k1_xonly_pubkey_serialize(secp256k1_context_sign, pubkey_bytes, &pubkey)) return false;
+ uint256 tweak = XOnlyPubKey(pubkey_bytes).ComputeTapTweakHash(merkle_root->IsNull() ? nullptr : merkle_root);
+ if (!secp256k1_keypair_xonly_tweak_add(GetVerifyContext(), &keypair, tweak.data())) return false;
+ }
+ bool ret = secp256k1_schnorrsig_sign(secp256k1_context_sign, sig.data(), hash.data(), &keypair, secp256k1_nonce_function_bip340, aux ? (void*)aux->data() : nullptr);
+ memory_cleanse(&keypair, sizeof(keypair));
+ return ret;
+}
+
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;
diff --git a/src/key.h b/src/key.h
index 3ee49a778b..d47e54800c 100644
--- a/src/key.h
+++ b/src/key.h
@@ -128,6 +128,18 @@ public:
*/
bool SignCompact(const uint256& hash, std::vector<unsigned char>& vchSig) const;
+ /**
+ * Create a BIP-340 Schnorr signature, for the xonly-pubkey corresponding to *this,
+ * optionally tweaked by *merkle_root. Additional nonce entropy can be provided through
+ * aux.
+ *
+ * When merkle_root is not nullptr, this results in a signature with a modified key as
+ * specified in BIP341:
+ * - If merkle_root->IsNull(): key + H_TapTweak(pubkey)*G
+ * - Otherwise: key + H_TapTweak(pubkey || *merkle_root)
+ */
+ bool SignSchnorr(const uint256& hash, Span<unsigned char> sig, const uint256* merkle_root = nullptr, const uint256* aux = nullptr) const;
+
//! Derive BIP32 child key.
bool Derive(CKey& keyChild, ChainCode &ccChild, unsigned int nChild, const ChainCode& cc) const;
diff --git a/src/miner.cpp b/src/miner.cpp
index eccddbb04f..0cf303eb3c 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -45,15 +45,7 @@ void RegenerateCommitments(CBlock& block, ChainstateManager& chainman)
tx.vout.erase(tx.vout.begin() + GetWitnessCommitmentIndex(block));
block.vtx.at(0) = MakeTransactionRef(tx);
- CBlockIndex* prev_block;
- {
- // TODO: Temporary scope to check correctness of refactored code.
- // Should be removed manually after merge of
- // https://github.com/bitcoin/bitcoin/pull/20158
- LOCK(::cs_main);
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(chainman.m_blockman));
- prev_block = chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock);
- }
+ CBlockIndex* prev_block = WITH_LOCK(::cs_main, return chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock));
GenerateCoinbaseCommitment(block, prev_block, Params().GetConsensus());
block.hashMerkleRoot = BlockMerkleRoot(block);
@@ -124,7 +116,6 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
pblocktemplate->vTxSigOpsCost.push_back(-1); // updated at end
LOCK2(cs_main, m_mempool.cs);
- assert(std::addressof(*::ChainActive().Tip()) == std::addressof(*m_chainstate.m_chain.Tip()));
CBlockIndex* pindexPrev = m_chainstate.m_chain.Tip();
assert(pindexPrev != nullptr);
nHeight = pindexPrev->nHeight + 1;
@@ -184,7 +175,6 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
pblocktemplate->vTxSigOpsCost[0] = WITNESS_SCALE_FACTOR * GetLegacySigOpCount(*pblock->vtx[0]);
BlockValidationState state;
- assert(std::addressof(::ChainstateActive()) == std::addressof(m_chainstate));
if (!TestBlockValidity(state, chainparams, m_chainstate, *pblock, pindexPrev, false, false)) {
throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, state.ToString()));
}
diff --git a/src/net.cpp b/src/net.cpp
index 6f9f17ed4e..60059249ed 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -42,6 +42,7 @@
#endif
#include <algorithm>
+#include <array>
#include <cstdint>
#include <functional>
#include <optional>
@@ -841,18 +842,6 @@ static bool ReverseCompareNodeTimeConnected(const NodeEvictionCandidate &a, cons
return a.nTimeConnected > b.nTimeConnected;
}
-static bool CompareLocalHostTimeConnected(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b)
-{
- if (a.m_is_local != b.m_is_local) return b.m_is_local;
- return a.nTimeConnected > b.nTimeConnected;
-}
-
-static bool CompareOnionTimeConnected(const NodeEvictionCandidate& a, const NodeEvictionCandidate& b)
-{
- if (a.m_is_onion != b.m_is_onion) return b.m_is_onion;
- return a.nTimeConnected > b.nTimeConnected;
-}
-
static bool CompareNetGroupKeyed(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) {
return a.nKeyedNetGroup < b.nKeyedNetGroup;
}
@@ -883,6 +872,26 @@ static bool CompareNodeBlockRelayOnlyTime(const NodeEvictionCandidate &a, const
return a.nTimeConnected > b.nTimeConnected;
}
+/**
+ * Sort eviction candidates by network/localhost and connection uptime.
+ * Candidates near the beginning are more likely to be evicted, and those
+ * near the end are more likely to be protected, e.g. less likely to be evicted.
+ * - First, nodes that are not `is_local` and that do not belong to `network`,
+ * sorted by increasing uptime (from most recently connected to connected longer).
+ * - Then, nodes that are `is_local` or belong to `network`, sorted by increasing uptime.
+ */
+struct CompareNodeNetworkTime {
+ const bool m_is_local;
+ const Network m_network;
+ CompareNodeNetworkTime(bool is_local, Network network) : m_is_local(is_local), m_network(network) {}
+ bool operator()(const NodeEvictionCandidate& a, const NodeEvictionCandidate& b) const
+ {
+ if (m_is_local && a.m_is_local != b.m_is_local) return b.m_is_local;
+ if ((a.m_network == m_network) != (b.m_network == m_network)) return b.m_network == m_network;
+ return a.nTimeConnected > b.nTimeConnected;
+ };
+};
+
//! Sort an array by the specified comparator, then erase the last K elements where predicate is true.
template <typename T, typename Comparator>
static void EraseLastKElements(
@@ -894,40 +903,72 @@ static void EraseLastKElements(
elements.erase(std::remove_if(elements.end() - eraseSize, elements.end(), predicate), elements.end());
}
-void ProtectEvictionCandidatesByRatio(std::vector<NodeEvictionCandidate>& vEvictionCandidates)
+void ProtectEvictionCandidatesByRatio(std::vector<NodeEvictionCandidate>& eviction_candidates)
{
// Protect the half of the remaining nodes which have been connected the longest.
// This replicates the non-eviction implicit behavior, and precludes attacks that start later.
- // To favorise the diversity of our peer connections, reserve up to (half + 2) of
- // these protected spots for onion and localhost peers, if any, even if they're not
- // longest uptime overall. This helps protect tor peers, which tend to be otherwise
+ // To favorise the diversity of our peer connections, reserve up to half of these protected
+ // spots for Tor/onion, localhost and I2P peers, even if they're not longest uptime overall.
+ // This helps protect these higher-latency peers that tend to be otherwise
// disadvantaged under our eviction criteria.
- const size_t initial_size = vEvictionCandidates.size();
- size_t total_protect_size = initial_size / 2;
- const size_t onion_protect_size = total_protect_size / 2;
-
- if (onion_protect_size) {
- // Pick out up to 1/4 peers connected via our onion service, sorted by longest uptime.
- EraseLastKElements(vEvictionCandidates, CompareOnionTimeConnected, onion_protect_size,
- [](const NodeEvictionCandidate& n) { return n.m_is_onion; });
- }
-
- const size_t localhost_min_protect_size{2};
- if (onion_protect_size >= localhost_min_protect_size) {
- // Allocate any remaining slots of the 1/4, or minimum 2 additional slots,
- // to localhost peers, sorted by longest uptime, as manually configured
- // hidden services not using `-bind=addr[:port]=onion` will not be detected
- // as inbound onion connections.
- const size_t remaining_tor_slots{onion_protect_size - (initial_size - vEvictionCandidates.size())};
- const size_t localhost_protect_size{std::max(remaining_tor_slots, localhost_min_protect_size)};
- EraseLastKElements(vEvictionCandidates, CompareLocalHostTimeConnected, localhost_protect_size,
- [](const NodeEvictionCandidate& n) { return n.m_is_local; });
+ const size_t initial_size = eviction_candidates.size();
+ const size_t total_protect_size{initial_size / 2};
+
+ // Disadvantaged networks to protect: I2P, localhost, Tor/onion. In case of equal counts, earlier
+ // array members have first opportunity to recover unused slots from the previous iteration.
+ struct Net { bool is_local; Network id; size_t count; };
+ std::array<Net, 3> networks{
+ {{false, NET_I2P, 0}, {/* localhost */ true, NET_MAX, 0}, {false, NET_ONION, 0}}};
+
+ // Count and store the number of eviction candidates per network.
+ for (Net& n : networks) {
+ n.count = std::count_if(eviction_candidates.cbegin(), eviction_candidates.cend(),
+ [&n](const NodeEvictionCandidate& c) {
+ return n.is_local ? c.m_is_local : c.m_network == n.id;
+ });
+ }
+ // Sort `networks` by ascending candidate count, to give networks having fewer candidates
+ // the first opportunity to recover unused protected slots from the previous iteration.
+ std::stable_sort(networks.begin(), networks.end(), [](Net a, Net b) { return a.count < b.count; });
+
+ // Protect up to 25% of the eviction candidates by disadvantaged network.
+ const size_t max_protect_by_network{total_protect_size / 2};
+ size_t num_protected{0};
+
+ while (num_protected < max_protect_by_network) {
+ const size_t disadvantaged_to_protect{max_protect_by_network - num_protected};
+ const size_t protect_per_network{
+ std::max(disadvantaged_to_protect / networks.size(), static_cast<size_t>(1))};
+
+ // Early exit flag if there are no remaining candidates by disadvantaged network.
+ bool protected_at_least_one{false};
+
+ for (const Net& n : networks) {
+ if (n.count == 0) continue;
+ const size_t before = eviction_candidates.size();
+ EraseLastKElements(eviction_candidates, CompareNodeNetworkTime(n.is_local, n.id),
+ protect_per_network, [&n](const NodeEvictionCandidate& c) {
+ return n.is_local ? c.m_is_local : c.m_network == n.id;
+ });
+ const size_t after = eviction_candidates.size();
+ if (before > after) {
+ protected_at_least_one = true;
+ num_protected += before - after;
+ if (num_protected >= max_protect_by_network) {
+ break;
+ }
+ }
+ }
+ if (!protected_at_least_one) {
+ break;
+ }
}
// Calculate how many we removed, and update our total number of peers that
// we want to protect based on uptime accordingly.
- total_protect_size -= initial_size - vEvictionCandidates.size();
- EraseLastKElements(vEvictionCandidates, ReverseCompareNodeTimeConnected, total_protect_size);
+ assert(num_protected == initial_size - eviction_candidates.size());
+ const size_t remaining_to_protect{total_protect_size - num_protected};
+ EraseLastKElements(eviction_candidates, ReverseCompareNodeTimeConnected, remaining_to_protect);
}
[[nodiscard]] std::optional<NodeId> SelectNodeToEvict(std::vector<NodeEvictionCandidate>&& vEvictionCandidates)
@@ -944,8 +985,7 @@ void ProtectEvictionCandidatesByRatio(std::vector<NodeEvictionCandidate>& vEvict
// An attacker cannot manipulate this metric without performing useful work.
EraseLastKElements(vEvictionCandidates, CompareNodeTXTime, 4);
// Protect up to 8 non-tx-relay peers that have sent us novel blocks.
- const size_t erase_size = std::min(size_t(8), vEvictionCandidates.size());
- EraseLastKElements(vEvictionCandidates, CompareNodeBlockRelayOnlyTime, erase_size,
+ EraseLastKElements(vEvictionCandidates, CompareNodeBlockRelayOnlyTime, 8,
[](const NodeEvictionCandidate& n) { return !n.fRelayTxes && n.fRelevantServices; });
// Protect 4 nodes that most recently sent us novel blocks.
@@ -1024,7 +1064,7 @@ bool CConnman::AttemptToEvictConnection()
HasAllDesirableServiceFlags(node->nServices),
peer_relay_txes, peer_filter_not_null, node->nKeyedNetGroup,
node->m_prefer_evict, node->addr.IsLocal(),
- node->m_inbound_onion};
+ node->ConnectedThroughNetwork()};
vEvictionCandidates.push_back(candidate);
}
}
@@ -2173,6 +2213,7 @@ void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai
void CConnman::ThreadMessageHandler()
{
+ FastRandomContext rng;
while (!flagInterruptMsgProc)
{
std::vector<CNode*> vNodesCopy;
@@ -2186,6 +2227,11 @@ void CConnman::ThreadMessageHandler()
bool fMoreWork = false;
+ // Randomize the order in which we process messages from/to our peers.
+ // This prevents attacks in which an attacker exploits having multiple
+ // consecutive connections in the vNodes list.
+ Shuffle(vNodesCopy.begin(), vNodesCopy.end(), rng);
+
for (CNode* pnode : vNodesCopy)
{
if (pnode->fDisconnect)
diff --git a/src/net.h b/src/net.h
index b43916c55e..01658e8973 100644
--- a/src/net.h
+++ b/src/net.h
@@ -1209,7 +1209,7 @@ struct NodeEvictionCandidate
uint64_t nKeyedNetGroup;
bool prefer_evict;
bool m_is_local;
- bool m_is_onion;
+ Network m_network;
};
/**
@@ -1227,20 +1227,20 @@ struct NodeEvictionCandidate
* longest, to replicate the non-eviction implicit behavior and preclude attacks
* that start later.
*
- * Half of these protected spots (1/4 of the total) are reserved for onion peers
- * connected via our tor control service, if any, sorted by longest uptime, even
- * if they're not longest uptime overall. Any remaining slots of the 1/4 are
- * then allocated to protect localhost peers, if any (or up to 2 localhost peers
- * if no slots remain and 2 or more onion peers were protected), sorted by
- * longest uptime, as manually configured hidden services not using
- * `-bind=addr[:port]=onion` will not be detected as inbound onion connections.
+ * Half of these protected spots (1/4 of the total) are reserved for the
+ * following categories of peers, sorted by longest uptime, even if they're not
+ * longest uptime overall:
+ *
+ * - onion peers connected via our tor control service
+ *
+ * - localhost peers, as manually configured hidden services not using
+ * `-bind=addr[:port]=onion` will not be detected as inbound onion connections
*
- * This helps protect onion peers, which tend to be otherwise disadvantaged
- * under our eviction criteria for their higher min ping times relative to IPv4
- * and IPv6 peers, and favorise the diversity of peer connections.
+ * - I2P peers
*
- * This function was extracted from SelectNodeToEvict() to be able to test the
- * ratio-based protection logic deterministically.
+ * This helps protect these privacy network peers, which tend to be otherwise
+ * disadvantaged under our eviction criteria for their higher min ping times
+ * relative to IPv4/IPv6 peers, and favorise the diversity of peer connections.
*/
void ProtectEvictionCandidatesByRatio(std::vector<NodeEvictionCandidate>& vEvictionCandidates);
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 2f2614a11a..9bb3dfdcf3 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -159,10 +159,10 @@ static constexpr size_t MAX_ADDR_TO_SEND{1000};
namespace {
/** Blocks that are in flight, and that are in the queue to be downloaded. */
struct QueuedBlock {
- uint256 hash;
- const CBlockIndex* pindex; //!< Optional.
- bool fValidatedHeaders; //!< Whether this block has validated headers at the time of request.
- std::unique_ptr<PartiallyDownloadedBlock> partialBlock; //!< Optional, used for CMPCTBLOCK downloads
+ /** BlockIndex. We must have this since we only request blocks when we've already validated the header. */
+ const CBlockIndex* pindex;
+ /** Optional, used for CMPCTBLOCK downloads */
+ std::unique_ptr<PartiallyDownloadedBlock> partialBlock;
};
/**
@@ -463,16 +463,20 @@ private:
Mutex m_recent_confirmed_transactions_mutex;
std::unique_ptr<CRollingBloomFilter> m_recent_confirmed_transactions GUARDED_BY(m_recent_confirmed_transactions_mutex);
- /* Returns a bool indicating whether we requested this block.
- * Also used if a block was /not/ received and timed out or started with another peer
+ /** Have we requested this block from a peer */
+ bool IsBlockRequested(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+ /** Remove this block from our tracked requested blocks. Called if:
+ * - the block has been recieved from a peer
+ * - the request for the block has timed out
*/
- bool MarkBlockAsReceived(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ void RemoveBlockRequest(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/* Mark a block as in flight
* Returns false, still setting pit, if the block was already in flight from the same peer
* pit will only be valid as long as the same cs_main lock is being held
*/
- bool MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const CBlockIndex* pindex = nullptr, std::list<QueuedBlock>::iterator** pit = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ bool BlockRequested(NodeId nodeid, const CBlockIndex& block, std::list<QueuedBlock>::iterator** pit = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
bool TipMayBeStale() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@@ -512,7 +516,7 @@ private:
std::list<NodeId> lNodesAnnouncingHeaderAndIDs GUARDED_BY(cs_main);
/** Number of peers from which we're downloading blocks. */
- int nPeersWithValidatedDownloads GUARDED_BY(cs_main) = 0;
+ int m_peers_downloading_from GUARDED_BY(cs_main) = 0;
/** Storage for orphan information */
TxOrphanage m_orphanage;
@@ -627,7 +631,6 @@ struct CNodeState {
//! When the first entry in vBlocksInFlight started downloading. Don't care when vBlocksInFlight is empty.
std::chrono::microseconds m_downloading_since{0us};
int nBlocksInFlight{0};
- int nBlocksInFlightValidHeaders{0};
//! Whether we consider this a preferred download peer.
bool fPreferredDownload{false};
//! Whether this peer wants invs or headers (when possible) for block announcements.
@@ -758,32 +761,42 @@ static void UpdatePreferredDownload(const CNode& node, CNodeState* state) EXCLUS
nPreferredDownload += state->fPreferredDownload;
}
-bool PeerManagerImpl::MarkBlockAsReceived(const uint256& hash)
+bool PeerManagerImpl::IsBlockRequested(const uint256& hash)
{
- std::map<uint256, std::pair<NodeId, std::list<QueuedBlock>::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash);
- if (itInFlight != mapBlocksInFlight.end()) {
- CNodeState *state = State(itInFlight->second.first);
- assert(state != nullptr);
- state->nBlocksInFlightValidHeaders -= itInFlight->second.second->fValidatedHeaders;
- if (state->nBlocksInFlightValidHeaders == 0 && itInFlight->second.second->fValidatedHeaders) {
- // Last validated block on the queue was received.
- nPeersWithValidatedDownloads--;
- }
- if (state->vBlocksInFlight.begin() == itInFlight->second.second) {
- // First block on the queue was received, update the start download time for the next one
- state->m_downloading_since = std::max(state->m_downloading_since, GetTime<std::chrono::microseconds>());
- }
- state->vBlocksInFlight.erase(itInFlight->second.second);
- state->nBlocksInFlight--;
- state->m_stalling_since = 0us;
- mapBlocksInFlight.erase(itInFlight);
- return true;
+ return mapBlocksInFlight.find(hash) != mapBlocksInFlight.end();
+}
+
+void PeerManagerImpl::RemoveBlockRequest(const uint256& hash)
+{
+ auto it = mapBlocksInFlight.find(hash);
+ if (it == mapBlocksInFlight.end()) {
+ // Block was not requested
+ return;
}
- return false;
+
+ auto [node_id, list_it] = it->second;
+ CNodeState *state = State(node_id);
+ assert(state != nullptr);
+
+ if (state->vBlocksInFlight.begin() == list_it) {
+ // First block on the queue was received, update the start download time for the next one
+ state->m_downloading_since = std::max(state->m_downloading_since, GetTime<std::chrono::microseconds>());
+ }
+ state->vBlocksInFlight.erase(list_it);
+
+ state->nBlocksInFlight--;
+ if (state->nBlocksInFlight == 0) {
+ // Last validated block on the queue was received.
+ m_peers_downloading_from--;
+ }
+ state->m_stalling_since = 0us;
+ mapBlocksInFlight.erase(it);
}
-bool PeerManagerImpl::MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const CBlockIndex* pindex, std::list<QueuedBlock>::iterator** pit)
+bool PeerManagerImpl::BlockRequested(NodeId nodeid, const CBlockIndex& block, std::list<QueuedBlock>::iterator** pit)
{
+ const uint256& hash{block.GetBlockHash()};
+
CNodeState *state = State(nodeid);
assert(state != nullptr);
@@ -797,22 +810,20 @@ bool PeerManagerImpl::MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, co
}
// Make sure it's not listed somewhere already.
- MarkBlockAsReceived(hash);
+ RemoveBlockRequest(hash);
std::list<QueuedBlock>::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(),
- {hash, pindex, pindex != nullptr, std::unique_ptr<PartiallyDownloadedBlock>(pit ? new PartiallyDownloadedBlock(&m_mempool) : nullptr)});
+ {&block, std::unique_ptr<PartiallyDownloadedBlock>(pit ? new PartiallyDownloadedBlock(&m_mempool) : nullptr)});
state->nBlocksInFlight++;
- state->nBlocksInFlightValidHeaders += it->fValidatedHeaders;
if (state->nBlocksInFlight == 1) {
// We're starting a block download (batch) from this peer.
state->m_downloading_since = GetTime<std::chrono::microseconds>();
- }
- if (state->nBlocksInFlightValidHeaders == 1 && pindex != nullptr) {
- nPeersWithValidatedDownloads++;
+ m_peers_downloading_from++;
}
itInFlight = mapBlocksInFlight.insert(std::make_pair(hash, std::make_pair(nodeid, it))).first;
- if (pit)
+ if (pit) {
*pit = &itInFlight->second.second;
+ }
return true;
}
@@ -993,7 +1004,7 @@ void PeerManagerImpl::FindNextBlocksToDownload(NodeId nodeid, unsigned int count
if (pindex->nStatus & BLOCK_HAVE_DATA || m_chainman.ActiveChain().Contains(pindex)) {
if (pindex->HaveTxsDownloaded())
state->pindexLastCommonBlock = pindex;
- } else if (mapBlocksInFlight.count(pindex->GetBlockHash()) == 0) {
+ } else if (!IsBlockRequested(pindex->GetBlockHash())) {
// The block is not already downloaded, and not yet in flight.
if (pindex->nHeight > nWindowEnd) {
// We reached the end of the window.
@@ -1144,13 +1155,13 @@ void PeerManagerImpl::FinalizeNode(const CNode& node)
nSyncStarted--;
for (const QueuedBlock& entry : state->vBlocksInFlight) {
- mapBlocksInFlight.erase(entry.hash);
+ mapBlocksInFlight.erase(entry.pindex->GetBlockHash());
}
WITH_LOCK(g_cs_orphans, m_orphanage.EraseForPeer(nodeid));
m_txrequest.DisconnectedPeer(nodeid);
nPreferredDownload -= state->fPreferredDownload;
- nPeersWithValidatedDownloads -= (state->nBlocksInFlightValidHeaders != 0);
- assert(nPeersWithValidatedDownloads >= 0);
+ m_peers_downloading_from -= (state->nBlocksInFlight != 0);
+ assert(m_peers_downloading_from >= 0);
m_outbound_peers_with_protect_from_disconnect -= state->m_chain_sync.m_protect;
assert(m_outbound_peers_with_protect_from_disconnect >= 0);
m_wtxid_relay_peers -= state->m_wtxid_relay;
@@ -1162,7 +1173,7 @@ void PeerManagerImpl::FinalizeNode(const CNode& node)
// Do a consistency check after the last peer is removed.
assert(mapBlocksInFlight.empty());
assert(nPreferredDownload == 0);
- assert(nPeersWithValidatedDownloads == 0);
+ assert(m_peers_downloading_from == 0);
assert(m_outbound_peers_with_protect_from_disconnect == 0);
assert(m_wtxid_relay_peers == 0);
assert(m_txrequest.Size() == 0);
@@ -1365,7 +1376,6 @@ PeerManagerImpl::PeerManagerImpl(const CChainParams& chainparams, CConnman& conn
m_stale_tip_check_time(0),
m_ignore_incoming_txs(ignore_incoming_txs)
{
- assert(std::addressof(g_chainman) == std::addressof(m_chainman));
// Initialize global variables that cannot be constructed at startup.
recentRejects.reset(new CRollingBloomFilter(120000, 0.000001));
@@ -2071,7 +2081,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
// Calculate all the blocks we'd need to switch to pindexLast, up to a limit.
while (pindexWalk && !m_chainman.ActiveChain().Contains(pindexWalk) && vToFetch.size() <= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
if (!(pindexWalk->nStatus & BLOCK_HAVE_DATA) &&
- !mapBlocksInFlight.count(pindexWalk->GetBlockHash()) &&
+ !IsBlockRequested(pindexWalk->GetBlockHash()) &&
(!IsWitnessEnabled(pindexWalk->pprev, m_chainparams.GetConsensus()) || State(pfrom.GetId())->fHaveWitness)) {
// We don't have this block, and it's not yet in flight.
vToFetch.push_back(pindexWalk);
@@ -2096,7 +2106,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
}
uint32_t nFetchFlags = GetFetchFlags(pfrom);
vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash()));
- MarkBlockAsInFlight(pfrom.GetId(), pindex->GetBlockHash(), pindex);
+ BlockRequested(pfrom.GetId(), *pindex);
LogPrint(BCLog::NET, "Requesting block %s from peer=%d\n",
pindex->GetBlockHash().ToString(), pfrom.GetId());
}
@@ -2842,7 +2852,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
LogPrint(BCLog::NET, "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom.GetId());
UpdateBlockAvailability(pfrom.GetId(), inv.hash);
- if (!fAlreadyHave && !fImporting && !fReindex && !mapBlocksInFlight.count(inv.hash)) {
+ if (!fAlreadyHave && !fImporting && !fReindex && !IsBlockRequested(inv.hash)) {
// Headers-first is the primary method of announcement on
// the network. If a node fell back to sending blocks by inv,
// it's probably for a re-org. The final block hash
@@ -3399,7 +3409,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
if ((!fAlreadyInFlight && nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) ||
(fAlreadyInFlight && blockInFlightIt->second.first == pfrom.GetId())) {
std::list<QueuedBlock>::iterator* queuedBlockIt = nullptr;
- if (!MarkBlockAsInFlight(pfrom.GetId(), pindex->GetBlockHash(), pindex, &queuedBlockIt)) {
+ if (!BlockRequested(pfrom.GetId(), *pindex, &queuedBlockIt)) {
if (!(*queuedBlockIt)->partialBlock)
(*queuedBlockIt)->partialBlock.reset(new PartiallyDownloadedBlock(&m_mempool));
else {
@@ -3412,7 +3422,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
PartiallyDownloadedBlock& partialBlock = *(*queuedBlockIt)->partialBlock;
ReadStatus status = partialBlock.InitData(cmpctblock, vExtraTxnForCompact);
if (status == READ_STATUS_INVALID) {
- MarkBlockAsReceived(pindex->GetBlockHash()); // Reset in-flight state in case Misbehaving does not result in a disconnect
+ RemoveBlockRequest(pindex->GetBlockHash()); // Reset in-flight state in case Misbehaving does not result in a disconnect
Misbehaving(pfrom.GetId(), 100, "invalid compact block");
return;
} else if (status == READ_STATUS_FAILED) {
@@ -3507,7 +3517,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// process from some other peer. We do this after calling
// ProcessNewBlock so that a malleated cmpctblock announcement
// can't be used to interfere with block relay.
- MarkBlockAsReceived(pblock->GetHash());
+ RemoveBlockRequest(pblock->GetHash());
}
}
return;
@@ -3539,7 +3549,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
PartiallyDownloadedBlock& partialBlock = *it->second.second->partialBlock;
ReadStatus status = partialBlock.FillBlock(*pblock, resp.txn);
if (status == READ_STATUS_INVALID) {
- MarkBlockAsReceived(resp.blockhash); // Reset in-flight state in case Misbehaving does not result in a disconnect
+ RemoveBlockRequest(resp.blockhash); // Reset in-flight state in case Misbehaving does not result in a disconnect
Misbehaving(pfrom.GetId(), 100, "invalid compact block/non-matching block transactions");
return;
} else if (status == READ_STATUS_FAILED) {
@@ -3565,7 +3575,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// though the block was successfully read, and rely on the
// handling in ProcessNewBlock to ensure the block index is
// updated, etc.
- MarkBlockAsReceived(resp.blockhash); // it is now an empty pointer
+ RemoveBlockRequest(resp.blockhash); // it is now an empty pointer
fBlockRead = true;
// mapBlockSource is used for potentially punishing peers and
// updating which peers send us compact blocks, so the race
@@ -3630,9 +3640,10 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
const uint256 hash(pblock->GetHash());
{
LOCK(cs_main);
- // Also always process if we requested the block explicitly, as we may
- // need it even though it is not a candidate for a new best tip.
- forceProcessing |= MarkBlockAsReceived(hash);
+ // Always process the block if we requested it, since we may
+ // need it even when it's not a candidate for a new best tip.
+ forceProcessing = IsBlockRequested(hash);
+ RemoveBlockRequest(hash);
// mapBlockSource is only used for punishing peers and setting
// which peers send us compact blocks, so the race between here and
// cs_main in ProcessNewBlock is fine.
@@ -4727,9 +4738,9 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
// to unreasonably increase our timeout.
if (state.vBlocksInFlight.size() > 0) {
QueuedBlock &queuedBlock = state.vBlocksInFlight.front();
- int nOtherPeersWithValidatedDownloads = nPeersWithValidatedDownloads - (state.nBlocksInFlightValidHeaders > 0);
+ int nOtherPeersWithValidatedDownloads = m_peers_downloading_from - 1;
if (current_time > state.m_downloading_since + std::chrono::seconds{consensusParams.nPowTargetSpacing} * (BLOCK_DOWNLOAD_TIMEOUT_BASE + BLOCK_DOWNLOAD_TIMEOUT_PER_PEER * nOtherPeersWithValidatedDownloads)) {
- LogPrintf("Timeout downloading block %s from peer=%d, disconnecting\n", queuedBlock.hash.ToString(), pto->GetId());
+ LogPrintf("Timeout downloading block %s from peer=%d, disconnecting\n", queuedBlock.pindex->GetBlockHash().ToString(), pto->GetId());
pto->fDisconnect = true;
return true;
}
@@ -4782,7 +4793,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
for (const CBlockIndex *pindex : vToDownload) {
uint32_t nFetchFlags = GetFetchFlags(*pto);
vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash()));
- MarkBlockAsInFlight(pto->GetId(), pindex->GetBlockHash(), pindex);
+ BlockRequested(pto->GetId(), *pindex);
LogPrint(BCLog::NET, "Requesting block %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(),
pindex->nHeight, pto->GetId());
}
diff --git a/src/netaddress.cpp b/src/netaddress.cpp
index 0ae8dd0698..1ea3969978 100644
--- a/src/netaddress.cpp
+++ b/src/netaddress.cpp
@@ -594,7 +594,7 @@ static std::string IPv6ToString(Span<const uint8_t> a, uint32_t scope_id)
return r;
}
-static std::string OnionToString(const Span<const uint8_t>& addr)
+static std::string OnionToString(Span<const uint8_t> addr)
{
uint8_t checksum[torv3::CHECKSUM_LEN];
torv3::Checksum(addr, checksum);
diff --git a/src/netaddress.h b/src/netaddress.h
index 0d04ab88fb..dd47ab5749 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -11,7 +11,9 @@
#include <attributes.h>
#include <compat.h>
+#include <crypto/siphash.h>
#include <prevector.h>
+#include <random.h>
#include <serialize.h>
#include <tinyformat.h>
#include <util/strencodings.h>
@@ -251,6 +253,7 @@ class CNetAddr
}
}
+ friend class CNetAddrHash;
friend class CSubNet;
private:
@@ -464,6 +467,22 @@ class CNetAddr
}
};
+class CNetAddrHash
+{
+public:
+ size_t operator()(const CNetAddr& a) const noexcept
+ {
+ CSipHasher hasher(m_salt_k0, m_salt_k1);
+ hasher.Write(a.m_net);
+ hasher.Write(a.m_addr.data(), a.m_addr.size());
+ return static_cast<size_t>(hasher.Finalize());
+ }
+
+private:
+ const uint64_t m_salt_k0 = GetRand(std::numeric_limits<uint64_t>::max());
+ const uint64_t m_salt_k1 = GetRand(std::numeric_limits<uint64_t>::max());
+};
+
class CSubNet
{
protected:
diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp
index 6c66c565ad..013d61282b 100644
--- a/src/node/blockstorage.cpp
+++ b/src/node/blockstorage.cpp
@@ -248,7 +248,6 @@ bool FindBlockPos(FlatFilePos& pos, unsigned int nAddSize, unsigned int nHeight,
// when the undo file is keeping up with the block file, we want to flush it explicitly
// when it is lagging behind (more blocks arrive than are being connected), we let the
// undo block write case handle it
- assert(std::addressof(::ChainActive()) == std::addressof(active_chain));
finalize_undo = (vinfoBlockFile[nFile].nHeightLast == (unsigned int)active_chain.Tip()->nHeight);
nFile++;
if (vinfoBlockFile.size() <= nFile) {
diff --git a/src/node/coin.cpp b/src/node/coin.cpp
index 23d4fa2aae..50fddf3ab0 100644
--- a/src/node/coin.cpp
+++ b/src/node/coin.cpp
@@ -13,7 +13,6 @@ void FindCoins(const NodeContext& node, std::map<COutPoint, Coin>& coins)
assert(node.mempool);
assert(node.chainman);
LOCK2(cs_main, node.mempool->cs);
- assert(std::addressof(::ChainstateActive()) == std::addressof(node.chainman->ActiveChainstate()));
CCoinsViewCache& chain_view = node.chainman->ActiveChainstate().CoinsTip();
CCoinsViewMemPool mempool_view(&chain_view, *node.mempool);
for (auto& coin : coins) {
diff --git a/src/node/coinstats.cpp b/src/node/coinstats.cpp
index 38c1d29250..67e497c218 100644
--- a/src/node/coinstats.cpp
+++ b/src/node/coinstats.cpp
@@ -97,7 +97,6 @@ static bool GetUTXOStats(CCoinsView* view, BlockManager& blockman, CCoinsStats&
if (!pindex) {
{
LOCK(cs_main);
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(blockman));
pindex = blockman.LookupBlockIndex(view->GetBestBlock());
}
}
diff --git a/src/node/context.cpp b/src/node/context.cpp
index 6d22a6b110..9afadd09a9 100644
--- a/src/node/context.cpp
+++ b/src/node/context.cpp
@@ -12,6 +12,7 @@
#include <policy/fees.h>
#include <scheduler.h>
#include <txmempool.h>
+#include <validation.h>
NodeContext::NodeContext() {}
NodeContext::~NodeContext() {}
diff --git a/src/node/context.h b/src/node/context.h
index 06adb33a80..135f9ea1c6 100644
--- a/src/node/context.h
+++ b/src/node/context.h
@@ -44,7 +44,7 @@ struct NodeContext {
std::unique_ptr<CTxMemPool> mempool;
std::unique_ptr<CBlockPolicyEstimator> fee_estimator;
std::unique_ptr<PeerManager> peerman;
- ChainstateManager* chainman{nullptr}; // Currently a raw pointer because the memory is not managed by this struct
+ std::unique_ptr<ChainstateManager> chainman;
std::unique_ptr<BanMan> banman;
ArgsManager* args{nullptr}; // Currently a raw pointer because the memory is not managed by this struct
std::unique_ptr<interfaces::Chain> chain;
diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp
index 8befbf5e30..fce3c1809c 100644
--- a/src/node/interfaces.cpp
+++ b/src/node/interfaces.cpp
@@ -6,6 +6,7 @@
#include <banman.h>
#include <chain.h>
#include <chainparams.h>
+#include <external_signer.h>
#include <init.h>
#include <interfaces/chain.h>
#include <interfaces/handler.h>
@@ -170,6 +171,24 @@ public:
}
return false;
}
+ std::vector<ExternalSigner> externalSigners() override
+ {
+#ifdef ENABLE_EXTERNAL_SIGNER
+ std::vector<ExternalSigner> signers = {};
+ const std::string command = gArgs.GetArg("-signer", "");
+ if (command == "") return signers;
+ ExternalSigner::Enumerate(command, signers, Params().NetworkIDString());
+ return signers;
+#else
+ // This result is undistinguisable from a succesful call that returns
+ // no signers. For the current GUI this doesn't matter, because the wallet
+ // creation dialog disables the external signer checkbox in both
+ // cases. The return type could be changed to std::optional<std::vector>
+ // (or something that also includes error messages) if this distinction
+ // becomes important.
+ return {};
+#endif // ENABLE_EXTERNAL_SIGNER
+ }
int64_t getTotalBytesRecv() override { return m_context->connman ? m_context->connman->GetTotalBytesRecv() : 0; }
int64_t getTotalBytesSent() override { return m_context->connman ? m_context->connman->GetTotalBytesSent() : 0; }
size_t getMempoolSize() override { return m_context->mempool ? m_context->mempool->size() : 0; }
@@ -187,26 +206,16 @@ public:
int getNumBlocks() override
{
LOCK(::cs_main);
- assert(std::addressof(::ChainActive()) == std::addressof(chainman().ActiveChain()));
return chainman().ActiveChain().Height();
}
uint256 getBestBlockHash() override
{
- const CBlockIndex* tip;
- {
- // TODO: Temporary scope to check correctness of refactored code.
- // Should be removed manually after merge of
- // https://github.com/bitcoin/bitcoin/pull/20158
- LOCK(cs_main);
- assert(std::addressof(::ChainActive()) == std::addressof(chainman().ActiveChain()));
- tip = chainman().ActiveChain().Tip();
- }
+ const CBlockIndex* tip = WITH_LOCK(::cs_main, return chainman().ActiveChain().Tip());
return tip ? tip->GetBlockHash() : Params().GenesisBlock().GetHash();
}
int64_t getLastBlockTime() override
{
LOCK(::cs_main);
- assert(std::addressof(::ChainActive()) == std::addressof(chainman().ActiveChain()));
if (chainman().ActiveChain().Tip()) {
return chainman().ActiveChain().Tip()->GetBlockTime();
}
@@ -217,22 +226,12 @@ public:
const CBlockIndex* tip;
{
LOCK(::cs_main);
- assert(std::addressof(::ChainActive()) == std::addressof(chainman().ActiveChain()));
tip = chainman().ActiveChain().Tip();
}
return GuessVerificationProgress(Params().TxData(), tip);
}
bool isInitialBlockDownload() override {
- const CChainState* active_chainstate;
- {
- // TODO: Temporary scope to check correctness of refactored code.
- // Should be removed manually after merge of
- // https://github.com/bitcoin/bitcoin/pull/20158
- LOCK(::cs_main);
- active_chainstate = &m_context->chainman->ActiveChainstate();
- assert(std::addressof(::ChainstateActive()) == std::addressof(*active_chainstate));
- }
- return active_chainstate->IsInitialBlockDownload();
+ return chainman().ActiveChainstate().IsInitialBlockDownload();
}
bool getReindex() override { return ::fReindex; }
bool getImporting() override { return ::fImporting; }
@@ -259,7 +258,6 @@ public:
bool getUnspentOutput(const COutPoint& output, Coin& coin) override
{
LOCK(::cs_main);
- assert(std::addressof(::ChainstateActive()) == std::addressof(chainman().ActiveChainstate()));
return chainman().ActiveChainstate().CoinsTip().GetCoin(output, coin);
}
WalletClient& walletClient() override
@@ -466,14 +464,12 @@ public:
bool checkFinalTx(const CTransaction& tx) override
{
LOCK(cs_main);
- assert(std::addressof(::ChainActive()) == std::addressof(chainman().ActiveChain()));
return CheckFinalTx(chainman().ActiveChain().Tip(), tx);
}
std::optional<int> findLocatorFork(const CBlockLocator& locator) override
{
LOCK(cs_main);
const CChain& active = Assert(m_node.chainman)->ActiveChain();
- assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
if (CBlockIndex* fork = m_node.chainman->m_blockman.FindForkInGlobalIndex(active, locator)) {
return fork->nHeight;
}
@@ -483,7 +479,6 @@ public:
{
WAIT_LOCK(cs_main, lock);
const CChain& active = Assert(m_node.chainman)->ActiveChain();
- assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
return FillBlock(m_node.chainman->m_blockman.LookupBlockIndex(hash), block, lock, active);
}
bool findFirstBlockWithTimeAndHeight(int64_t min_time, int min_height, const FoundBlock& block) override
@@ -496,7 +491,6 @@ public:
{
WAIT_LOCK(cs_main, lock);
const CChain& active = Assert(m_node.chainman)->ActiveChain();
- assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
if (const CBlockIndex* block = m_node.chainman->m_blockman.LookupBlockIndex(block_hash)) {
if (const CBlockIndex* ancestor = block->GetAncestor(ancestor_height)) {
return FillBlock(ancestor, ancestor_out, lock, active);
@@ -508,9 +502,7 @@ public:
{
WAIT_LOCK(cs_main, lock);
const CChain& active = Assert(m_node.chainman)->ActiveChain();
- assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
const CBlockIndex* block = m_node.chainman->m_blockman.LookupBlockIndex(block_hash);
- assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
const CBlockIndex* ancestor = m_node.chainman->m_blockman.LookupBlockIndex(ancestor_hash);
if (block && ancestor && block->GetAncestor(ancestor->nHeight) != ancestor) ancestor = nullptr;
return FillBlock(ancestor, ancestor_out, lock, active);
@@ -519,9 +511,7 @@ public:
{
WAIT_LOCK(cs_main, lock);
const CChain& active = Assert(m_node.chainman)->ActiveChain();
- assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
const CBlockIndex* block1 = m_node.chainman->m_blockman.LookupBlockIndex(block_hash1);
- assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
const CBlockIndex* block2 = m_node.chainman->m_blockman.LookupBlockIndex(block_hash2);
const CBlockIndex* ancestor = block1 && block2 ? LastCommonAncestor(block1, block2) : nullptr;
// Using & instead of && below to avoid short circuiting and leaving
@@ -532,7 +522,6 @@ public:
double guessVerificationProgress(const uint256& block_hash) override
{
LOCK(cs_main);
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(chainman().m_blockman));
return GuessVerificationProgress(Params().TxData(), chainman().m_blockman.LookupBlockIndex(block_hash));
}
bool hasBlocks(const uint256& block_hash, int min_height, std::optional<int> max_height) override
@@ -545,7 +534,6 @@ public:
// used to limit the range, and passing min_height that's too low or
// max_height that's too high will not crash or change the result.
LOCK(::cs_main);
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(chainman().m_blockman));
if (CBlockIndex* block = chainman().m_blockman.LookupBlockIndex(block_hash)) {
if (max_height && block->nHeight >= *max_height) block = block->GetAncestor(*max_height);
for (; block->nStatus & BLOCK_HAVE_DATA; block = block->pprev) {
@@ -637,16 +625,7 @@ public:
}
bool isReadyToBroadcast() override { return !::fImporting && !::fReindex && !isInitialBlockDownload(); }
bool isInitialBlockDownload() override {
- const CChainState* active_chainstate;
- {
- // TODO: Temporary scope to check correctness of refactored code.
- // Should be removed manually after merge of
- // https://github.com/bitcoin/bitcoin/pull/20158
- LOCK(::cs_main);
- active_chainstate = &chainman().ActiveChainstate();
- assert(std::addressof(::ChainstateActive()) == std::addressof(*active_chainstate));
- }
- return active_chainstate->IsInitialBlockDownload();
+ return chainman().ActiveChainstate().IsInitialBlockDownload();
}
bool shutdownRequested() override { return ShutdownRequested(); }
int64_t getAdjustedTime() override { return GetAdjustedTime(); }
@@ -709,6 +688,12 @@ public:
notifications.transactionAddedToMempool(entry.GetSharedTx(), 0 /* mempool_sequence */);
}
}
+ bool isTaprootActive() const override
+ {
+ LOCK(::cs_main);
+ const CBlockIndex* tip = Assert(m_node.chainman)->ActiveChain().Tip();
+ return VersionBitsState(tip, Params().GetConsensus(), Consensus::DEPLOYMENT_TAPROOT, versionbitscache) == ThresholdState::ACTIVE;
+ }
NodeContext& m_node;
};
} // namespace
diff --git a/src/node/psbt.cpp b/src/node/psbt.cpp
index c189018268..b013b6d579 100644
--- a/src/node/psbt.cpp
+++ b/src/node/psbt.cpp
@@ -23,6 +23,8 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)
result.inputs.resize(psbtx.tx->vin.size());
+ const PrecomputedTransactionData txdata = PrecomputePSBTData(psbtx);
+
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
PSBTInput& input = psbtx.inputs[i];
PSBTInputAnalysis& input_analysis = result.inputs[i];
@@ -61,7 +63,7 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)
// Figure out what is missing
SignatureData outdata;
- bool complete = SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, 1, &outdata);
+ bool complete = SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, &txdata, 1, &outdata);
// Things are missing
if (!complete) {
@@ -121,7 +123,7 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)
PSBTInput& input = psbtx.inputs[i];
Coin newcoin;
- if (!SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, 1, nullptr, true) || !psbtx.GetInputUTXO(newcoin.out, i)) {
+ if (!SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, nullptr, 1) || !psbtx.GetInputUTXO(newcoin.out, i)) {
success = false;
break;
} else {
diff --git a/src/node/transaction.cpp b/src/node/transaction.cpp
index a1e7a71e2c..f21b390915 100644
--- a/src/node/transaction.cpp
+++ b/src/node/transaction.cpp
@@ -40,7 +40,6 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
{ // cs_main scope
assert(node.chainman);
LOCK(cs_main);
- assert(std::addressof(::ChainstateActive()) == std::addressof(node.chainman->ActiveChainstate()));
// If the transaction is already confirmed in the chain, don't do anything
// and return early.
CCoinsViewCache &view = node.chainman->ActiveChainstate().CoinsTip();
diff --git a/src/policy/packages.cpp b/src/policy/packages.cpp
new file mode 100644
index 0000000000..cfd0539965
--- /dev/null
+++ b/src/policy/packages.cpp
@@ -0,0 +1,62 @@
+// Copyright (c) 2021 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 <consensus/validation.h>
+#include <policy/packages.h>
+#include <primitives/transaction.h>
+#include <uint256.h>
+#include <util/hasher.h>
+
+#include <numeric>
+#include <unordered_set>
+
+bool CheckPackage(const Package& txns, PackageValidationState& state)
+{
+ const unsigned int package_count = txns.size();
+
+ if (package_count > MAX_PACKAGE_COUNT) {
+ return state.Invalid(PackageValidationResult::PCKG_POLICY, "package-too-many-transactions");
+ }
+
+ const int64_t total_size = std::accumulate(txns.cbegin(), txns.cend(), 0,
+ [](int64_t sum, const auto& tx) { return sum + GetVirtualTransactionSize(*tx); });
+ // If the package only contains 1 tx, it's better to report the policy violation on individual tx size.
+ if (package_count > 1 && total_size > MAX_PACKAGE_SIZE * 1000) {
+ return state.Invalid(PackageValidationResult::PCKG_POLICY, "package-too-large");
+ }
+
+ // Require the package to be sorted in order of dependency, i.e. parents appear before children.
+ // An unsorted package will fail anyway on missing-inputs, but it's better to quit earlier and
+ // fail on something less ambiguous (missing-inputs could also be an orphan or trying to
+ // spend nonexistent coins).
+ std::unordered_set<uint256, SaltedTxidHasher> later_txids;
+ std::transform(txns.cbegin(), txns.cend(), std::inserter(later_txids, later_txids.end()),
+ [](const auto& tx) { return tx->GetHash(); });
+ for (const auto& tx : txns) {
+ for (const auto& input : tx->vin) {
+ if (later_txids.find(input.prevout.hash) != later_txids.end()) {
+ // The parent is a subsequent transaction in the package.
+ return state.Invalid(PackageValidationResult::PCKG_POLICY, "package-not-sorted");
+ }
+ }
+ later_txids.erase(tx->GetHash());
+ }
+
+ // Don't allow any conflicting transactions, i.e. spending the same inputs, in a package.
+ std::unordered_set<COutPoint, SaltedOutpointHasher> inputs_seen;
+ for (const auto& tx : txns) {
+ for (const auto& input : tx->vin) {
+ if (inputs_seen.find(input.prevout) != inputs_seen.end()) {
+ // This input is also present in another tx in the package.
+ return state.Invalid(PackageValidationResult::PCKG_POLICY, "conflict-in-package");
+ }
+ }
+ // Batch-add all the inputs for a tx at a time. If we added them 1 at a time, we could
+ // catch duplicate inputs within a single tx. This is a more severe, consensus error,
+ // and we want to report that from CheckTransaction instead.
+ std::transform(tx->vin.cbegin(), tx->vin.cend(), std::inserter(inputs_seen, inputs_seen.end()),
+ [](const auto& input) { return input.prevout; });
+ }
+ return true;
+}
diff --git a/src/policy/packages.h b/src/policy/packages.h
index 4b1463dcb3..6b7ac3e450 100644
--- a/src/policy/packages.h
+++ b/src/policy/packages.h
@@ -6,6 +6,7 @@
#define BITCOIN_POLICY_PACKAGES_H
#include <consensus/validation.h>
+#include <policy/policy.h>
#include <primitives/transaction.h>
#include <vector>
@@ -14,6 +15,7 @@
static constexpr uint32_t MAX_PACKAGE_COUNT{25};
/** Default maximum total virtual size of transactions in a package in KvB. */
static constexpr uint32_t MAX_PACKAGE_SIZE{101};
+static_assert(MAX_PACKAGE_SIZE * WITNESS_SCALE_FACTOR * 1000 >= MAX_STANDARD_TX_WEIGHT);
/** A "reason" why a package was invalid. It may be that one or more of the included
* transactions is invalid or the package itself violates our rules.
@@ -31,4 +33,12 @@ using Package = std::vector<CTransactionRef>;
class PackageValidationState : public ValidationState<PackageValidationResult> {};
+/** Context-free package policy checks:
+ * 1. The number of transactions cannot exceed MAX_PACKAGE_COUNT.
+ * 2. The total virtual size cannot exceed MAX_PACKAGE_SIZE.
+ * 3. If any dependencies exist between transactions, parents must appear before children.
+ * 4. Transactions cannot conflict, i.e., spend the same inputs.
+ */
+bool CheckPackage(const Package& txns, PackageValidationState& state);
+
#endif // BITCOIN_POLICY_PACKAGES_H
diff --git a/src/protocol.h b/src/protocol.h
index aaa9f1df40..f9248899dc 100644
--- a/src/protocol.h
+++ b/src/protocol.h
@@ -13,6 +13,7 @@
#include <netaddress.h>
#include <primitives/transaction.h>
#include <serialize.h>
+#include <streams.h>
#include <uint256.h>
#include <version.h>
@@ -358,6 +359,31 @@ class CAddress : public CService
{
static constexpr uint32_t TIME_INIT{100000000};
+ /** Historically, CAddress disk serialization stored the CLIENT_VERSION, optionally OR'ed with
+ * the ADDRV2_FORMAT flag to indicate V2 serialization. The first field has since been
+ * disentangled from client versioning, and now instead:
+ * - The low bits (masked by DISK_VERSION_IGNORE_MASK) store the fixed value DISK_VERSION_INIT,
+ * (in case any code exists that treats it as a client version) but are ignored on
+ * deserialization.
+ * - The high bits (masked by ~DISK_VERSION_IGNORE_MASK) store actual serialization information.
+ * Only 0 or DISK_VERSION_ADDRV2 (equal to the historical ADDRV2_FORMAT) are valid now, and
+ * any other value triggers a deserialization failure. Other values can be added later if
+ * needed.
+ *
+ * For disk deserialization, ADDRV2_FORMAT in the stream version signals that ADDRV2
+ * deserialization is permitted, but the actual format is determined by the high bits in the
+ * stored version field. For network serialization, the stream version having ADDRV2_FORMAT or
+ * not determines the actual format used (as it has no embedded version number).
+ */
+ static constexpr uint32_t DISK_VERSION_INIT{220000};
+ static constexpr uint32_t DISK_VERSION_IGNORE_MASK{0b00000000'00000111'11111111'11111111};
+ /** The version number written in disk serialized addresses to indicate V2 serializations.
+ * It must be exactly 1<<29, as that is the value that historical versions used for this
+ * (they used their internal ADDRV2_FORMAT flag here). */
+ static constexpr uint32_t DISK_VERSION_ADDRV2{1 << 29};
+ static_assert((DISK_VERSION_INIT & ~DISK_VERSION_IGNORE_MASK) == 0, "DISK_VERSION_INIT must be covered by DISK_VERSION_IGNORE_MASK");
+ static_assert((DISK_VERSION_ADDRV2 & DISK_VERSION_IGNORE_MASK) == 0, "DISK_VERSION_ADDRV2 must not be covered by DISK_VERSION_IGNORE_MASK");
+
public:
CAddress() : CService{} {};
CAddress(CService ipIn, ServiceFlags nServicesIn) : CService{ipIn}, nServices{nServicesIn} {};
@@ -365,22 +391,48 @@ public:
SERIALIZE_METHODS(CAddress, obj)
{
- SER_READ(obj, obj.nTime = TIME_INIT);
- int nVersion = s.GetVersion();
+ // CAddress has a distinct network serialization and a disk serialization, but it should never
+ // be hashed (except through CHashWriter in addrdb.cpp, which sets SER_DISK), and it's
+ // ambiguous what that would mean. Make sure no code relying on that is introduced:
+ assert(!(s.GetType() & SER_GETHASH));
+ bool use_v2;
+ bool store_time;
if (s.GetType() & SER_DISK) {
- READWRITE(nVersion);
- }
- if ((s.GetType() & SER_DISK) ||
- (nVersion != INIT_PROTO_VERSION && !(s.GetType() & SER_GETHASH))) {
+ // In the disk serialization format, the encoding (v1 or v2) is determined by a flag version
+ // that's part of the serialization itself. ADDRV2_FORMAT in the stream version only determines
+ // whether V2 is chosen/permitted at all.
+ uint32_t stored_format_version = DISK_VERSION_INIT;
+ if (s.GetVersion() & ADDRV2_FORMAT) stored_format_version |= DISK_VERSION_ADDRV2;
+ READWRITE(stored_format_version);
+ stored_format_version &= ~DISK_VERSION_IGNORE_MASK; // ignore low bits
+ if (stored_format_version == 0) {
+ use_v2 = false;
+ } else if (stored_format_version == DISK_VERSION_ADDRV2 && (s.GetVersion() & ADDRV2_FORMAT)) {
+ // Only support v2 deserialization if ADDRV2_FORMAT is set.
+ use_v2 = true;
+ } else {
+ throw std::ios_base::failure("Unsupported CAddress disk format version");
+ }
+ store_time = true;
+ } else {
+ // In the network serialization format, the encoding (v1 or v2) is determined directly by
+ // the value of ADDRV2_FORMAT in the stream version, as no explicitly encoded version
+ // exists in the stream.
+ assert(s.GetType() & SER_NETWORK);
+ use_v2 = s.GetVersion() & ADDRV2_FORMAT;
// The only time we serialize a CAddress object without nTime is in
// the initial VERSION messages which contain two CAddress records.
// At that point, the serialization version is INIT_PROTO_VERSION.
// After the version handshake, serialization version is >=
// MIN_PEER_PROTO_VERSION and all ADDR messages are serialized with
// nTime.
- READWRITE(obj.nTime);
+ store_time = s.GetVersion() != INIT_PROTO_VERSION;
}
- if (nVersion & ADDRV2_FORMAT) {
+
+ SER_READ(obj, obj.nTime = TIME_INIT);
+ if (store_time) READWRITE(obj.nTime);
+ // nServices is serialized as CompactSize in V2; as uint64_t in V1.
+ if (use_v2) {
uint64_t services_tmp;
SER_WRITE(obj, services_tmp = obj.nServices);
READWRITE(Using<CompactSizeFormatter<false>>(services_tmp));
@@ -388,13 +440,22 @@ public:
} else {
READWRITE(Using<CustomUintFormatter<8>>(obj.nServices));
}
- READWRITEAS(CService, obj);
+ // Invoke V1/V2 serializer for CService parent object.
+ OverrideStream<Stream> os(&s, s.GetType(), use_v2 ? ADDRV2_FORMAT : 0);
+ SerReadWriteMany(os, ser_action, ReadWriteAsHelper<CService>(obj));
}
- // disk and network only
+ //! Always included in serialization, except in the network format on INIT_PROTO_VERSION.
uint32_t nTime{TIME_INIT};
-
+ //! Serialized as uint64_t in V1, and as CompactSize in V2.
ServiceFlags nServices{NODE_NONE};
+
+ friend bool operator==(const CAddress& a, const CAddress& b)
+ {
+ return a.nTime == b.nTime &&
+ a.nServices == b.nServices &&
+ static_cast<const CService&>(a) == static_cast<const CService&>(b);
+ }
};
/** getdata message type flags */
diff --git a/src/psbt.cpp b/src/psbt.cpp
index a849b2ea53..5445bc8aa1 100644
--- a/src/psbt.cpp
+++ b/src/psbt.cpp
@@ -59,12 +59,15 @@ bool PartiallySignedTransaction::AddOutput(const CTxOut& txout, const PSBTOutput
bool PartiallySignedTransaction::GetInputUTXO(CTxOut& utxo, int input_index) const
{
- PSBTInput input = inputs[input_index];
+ const PSBTInput& input = inputs[input_index];
uint32_t prevout_index = tx->vin[input_index].prevout.n;
if (input.non_witness_utxo) {
if (prevout_index >= input.non_witness_utxo->vout.size()) {
return false;
}
+ if (input.non_witness_utxo->GetHash() != tx->vin[input_index].prevout.hash) {
+ return false;
+ }
utxo = input.non_witness_utxo->vout[prevout_index];
} else if (!input.witness_utxo.IsNull()) {
utxo = input.witness_utxo;
@@ -227,7 +230,24 @@ void UpdatePSBTOutput(const SigningProvider& provider, PartiallySignedTransactio
psbt_out.FromSignatureData(sigdata);
}
-bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, int sighash, SignatureData* out_sigdata, bool use_dummy)
+PrecomputedTransactionData PrecomputePSBTData(const PartiallySignedTransaction& psbt)
+{
+ const CMutableTransaction& tx = *psbt.tx;
+ bool have_all_spent_outputs = true;
+ std::vector<CTxOut> utxos(tx.vin.size());
+ for (size_t idx = 0; idx < tx.vin.size(); ++idx) {
+ if (!psbt.GetInputUTXO(utxos[idx], idx)) have_all_spent_outputs = false;
+ }
+ PrecomputedTransactionData txdata;
+ if (have_all_spent_outputs) {
+ txdata.Init(tx, std::move(utxos), true);
+ } else {
+ txdata.Init(tx, {}, true);
+ }
+ return txdata;
+}
+
+bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, const PrecomputedTransactionData* txdata, int sighash, SignatureData* out_sigdata)
{
PSBTInput& input = psbt.inputs.at(index);
const CMutableTransaction& tx = *psbt.tx;
@@ -267,10 +287,10 @@ bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction&
sigdata.witness = false;
bool sig_complete;
- if (use_dummy) {
+ if (txdata == nullptr) {
sig_complete = ProduceSignature(provider, DUMMY_SIGNATURE_CREATOR, utxo.scriptPubKey, sigdata);
} else {
- MutableTransactionSignatureCreator creator(&tx, index, utxo.nValue, sighash);
+ MutableTransactionSignatureCreator creator(&tx, index, utxo.nValue, txdata, sighash);
sig_complete = ProduceSignature(provider, creator, utxo.scriptPubKey, sigdata);
}
// Verify that a witness signature was produced in case one was required.
@@ -302,8 +322,9 @@ bool FinalizePSBT(PartiallySignedTransaction& psbtx)
// PartiallySignedTransaction did not understand them), this will combine them into a final
// script.
bool complete = true;
+ const PrecomputedTransactionData txdata = PrecomputePSBTData(psbtx);
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
- complete &= SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, SIGHASH_ALL);
+ complete &= SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, &txdata, SIGHASH_ALL);
}
return complete;
diff --git a/src/psbt.h b/src/psbt.h
index 96ae39fdb8..f6b82b43de 100644
--- a/src/psbt.h
+++ b/src/psbt.h
@@ -567,11 +567,18 @@ enum class PSBTRole {
std::string PSBTRoleName(PSBTRole role);
+/** Compute a PrecomputedTransactionData object from a psbt. */
+PrecomputedTransactionData PrecomputePSBTData(const PartiallySignedTransaction& psbt);
+
/** Checks whether a PSBTInput is already signed. */
bool PSBTInputSigned(const PSBTInput& input);
-/** Signs a PSBTInput, verifying that all provided data matches what is being signed. */
-bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, int sighash = SIGHASH_ALL, SignatureData* out_sigdata = nullptr, bool use_dummy = false);
+/** Signs a PSBTInput, verifying that all provided data matches what is being signed.
+ *
+ * txdata should be the output of PrecomputePSBTData (which can be shared across
+ * multiple SignPSBTInput calls). If it is nullptr, a dummy signature will be created.
+ **/
+bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, const PrecomputedTransactionData* txdata, int sighash = SIGHASH_ALL, SignatureData* out_sigdata = nullptr);
/** Counts the unsigned inputs of a PSBT. */
size_t CountPSBTUnsignedInputs(const PartiallySignedTransaction& psbt);
diff --git a/src/pubkey.cpp b/src/pubkey.cpp
index 51cc826b00..175a39b805 100644
--- a/src/pubkey.cpp
+++ b/src/pubkey.cpp
@@ -373,3 +373,7 @@ ECCVerifyHandle::~ECCVerifyHandle()
secp256k1_context_verify = nullptr;
}
}
+
+const secp256k1_context* GetVerifyContext() {
+ return secp256k1_context_verify;
+}
diff --git a/src/pubkey.h b/src/pubkey.h
index 152a48dd18..eec34a89c2 100644
--- a/src/pubkey.h
+++ b/src/pubkey.h
@@ -234,6 +234,10 @@ public:
* fail. */
bool IsFullyValid() const;
+ /** Test whether this is the 0 key (the result of default construction). This implies
+ * !IsFullyValid(). */
+ bool IsNull() const { return m_keydata.IsNull(); }
+
/** Construct an x-only pubkey from exactly 32 bytes. */
explicit XOnlyPubKey(Span<const unsigned char> bytes);
@@ -312,4 +316,10 @@ public:
~ECCVerifyHandle();
};
+typedef struct secp256k1_context_struct secp256k1_context;
+
+/** Access to the internal secp256k1 context used for verification. Only intended to be used
+ * by key.cpp. */
+const secp256k1_context* GetVerifyContext();
+
#endif // BITCOIN_PUBKEY_H
diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp
index 7024fc7654..c31f0aceea 100644
--- a/src/qt/addressbookpage.cpp
+++ b/src/qt/addressbookpage.cpp
@@ -114,12 +114,12 @@ AddressBookPage::AddressBookPage(const PlatformStyle *platformStyle, Mode _mode,
// Build context menu
contextMenu = new QMenu(this);
- contextMenu->addAction(tr("Copy Address"), this, &AddressBookPage::on_copyAddress_clicked);
- contextMenu->addAction(tr("Copy Label"), this, &AddressBookPage::onCopyLabelAction);
- contextMenu->addAction(tr("Edit"), this, &AddressBookPage::onEditAction);
+ contextMenu->addAction(tr("&Copy Address"), this, &AddressBookPage::on_copyAddress_clicked);
+ contextMenu->addAction(tr("Copy &Label"), this, &AddressBookPage::onCopyLabelAction);
+ contextMenu->addAction(tr("&Edit"), this, &AddressBookPage::onEditAction);
if (tab == SendingTab) {
- contextMenu->addAction(tr("Delete"), this, &AddressBookPage::on_deleteAddress_clicked);
+ contextMenu->addAction(tr("&Delete"), this, &AddressBookPage::on_deleteAddress_clicked);
}
connect(ui->tableView, &QWidget::customContextMenuRequested, this, &AddressBookPage::contextualMenu);
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 9e6cf56d31..442c813a5a 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -490,7 +490,8 @@ int GuiMain(int argc, char* argv[])
/// 2. Parse command-line options. We do this after qt in order to show an error if there are problems parsing these
// Command-line options take precedence:
- SetupServerArgs(node_context);
+ node_context.args = &gArgs;
+ SetupServerArgs(gArgs);
SetupUIArgs(gArgs);
std::string error;
if (!gArgs.ParseParameters(argc, argv, error)) {
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index e5dde88bd1..3d632ec702 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -105,6 +105,11 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty
{
/** Create wallet frame and make it the central widget */
walletFrame = new WalletFrame(_platformStyle, this);
+ connect(walletFrame, &WalletFrame::createWalletButtonClicked, [this] {
+ auto activity = new CreateWalletActivity(getWalletController(), this);
+ connect(activity, &CreateWalletActivity::finished, activity, &QObject::deleteLater);
+ activity->create();
+ });
setCentralWidget(walletFrame);
} else
#endif // ENABLE_WALLET
@@ -206,11 +211,6 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty
connect(labelBlocksIcon, &GUIUtil::ClickableLabel::clicked, this, &BitcoinGUI::showModalOverlay);
connect(progressBar, &GUIUtil::ClickableProgressBar::clicked, this, &BitcoinGUI::showModalOverlay);
-#ifdef ENABLE_WALLET
- if(enableWallet) {
- connect(walletFrame, &WalletFrame::requestedSyncWarningInfo, this, &BitcoinGUI::showModalOverlay);
- }
-#endif
#ifdef Q_OS_MAC
m_app_nap_inhibitor = new CAppNapInhibitor;
@@ -671,7 +671,10 @@ WalletController* BitcoinGUI::getWalletController()
void BitcoinGUI::addWallet(WalletModel* walletModel)
{
if (!walletFrame) return;
- if (!walletFrame->addWallet(walletModel)) return;
+
+ WalletView* wallet_view = new WalletView(platformStyle, walletFrame);
+ if (!walletFrame->addWallet(walletModel, wallet_view)) return;
+
rpcConsole->addWallet(walletModel);
if (m_wallet_selector->count() == 0) {
setWalletActionsEnabled(true);
@@ -681,6 +684,18 @@ void BitcoinGUI::addWallet(WalletModel* walletModel)
}
const QString display_name = walletModel->getDisplayName();
m_wallet_selector->addItem(display_name, QVariant::fromValue(walletModel));
+
+ connect(wallet_view, &WalletView::outOfSyncWarningClicked, this, &BitcoinGUI::showModalOverlay);
+ connect(wallet_view, &WalletView::transactionClicked, this, &BitcoinGUI::gotoHistoryPage);
+ connect(wallet_view, &WalletView::coinsSent, this, &BitcoinGUI::gotoHistoryPage);
+ connect(wallet_view, &WalletView::message, [this](const QString& title, const QString& message, unsigned int style) {
+ this->message(title, message, style);
+ });
+ connect(wallet_view, &WalletView::encryptionStatusChanged, this, &BitcoinGUI::updateWalletStatus);
+ connect(wallet_view, &WalletView::incomingTransaction, this, &BitcoinGUI::incomingTransaction);
+ connect(wallet_view, &WalletView::hdEnabledStatusChanged, this, &BitcoinGUI::updateWalletStatus);
+ connect(this, &BitcoinGUI::setPrivacy, wallet_view, &WalletView::setPrivacy);
+ wallet_view->setPrivacy(isPrivacyModeActivated());
}
void BitcoinGUI::removeWallet(WalletModel* walletModel)
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index 8ae0648141..2360fa9b37 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -52,13 +52,13 @@ CoinControlDialog::CoinControlDialog(CCoinControl& coin_control, WalletModel* _m
// context menu
contextMenu = new QMenu(this);
- contextMenu->addAction(tr("Copy address"), this, &CoinControlDialog::copyAddress);
- contextMenu->addAction(tr("Copy label"), this, &CoinControlDialog::copyLabel);
- contextMenu->addAction(tr("Copy amount"), this, &CoinControlDialog::copyAmount);
- copyTransactionHashAction = contextMenu->addAction(tr("Copy transaction ID"), this, &CoinControlDialog::copyTransactionHash);
+ contextMenu->addAction(tr("&Copy address"), this, &CoinControlDialog::copyAddress);
+ contextMenu->addAction(tr("Copy &label"), this, &CoinControlDialog::copyLabel);
+ contextMenu->addAction(tr("Copy &amount"), this, &CoinControlDialog::copyAmount);
+ copyTransactionHashAction = contextMenu->addAction(tr("Copy transaction &ID"), this, &CoinControlDialog::copyTransactionHash);
contextMenu->addSeparator();
- lockAction = contextMenu->addAction(tr("Lock unspent"), this, &CoinControlDialog::lockCoin);
- unlockAction = contextMenu->addAction(tr("Unlock unspent"), this, &CoinControlDialog::unlockCoin);
+ lockAction = contextMenu->addAction(tr("L&ock unspent"), this, &CoinControlDialog::lockCoin);
+ unlockAction = contextMenu->addAction(tr("&Unlock unspent"), this, &CoinControlDialog::unlockCoin);
connect(ui->treeWidget, &QWidget::customContextMenuRequested, this, &CoinControlDialog::showMenu);
// clipboard actions
diff --git a/src/qt/createwalletdialog.cpp b/src/qt/createwalletdialog.cpp
index 113bd30a0c..dc24bbc6a6 100644
--- a/src/qt/createwalletdialog.cpp
+++ b/src/qt/createwalletdialog.cpp
@@ -6,6 +6,7 @@
#include <config/bitcoin-config.h>
#endif
+#include <external_signer.h>
#include <qt/createwalletdialog.h>
#include <qt/forms/ui_createwalletdialog.h>
@@ -27,14 +28,40 @@ CreateWalletDialog::CreateWalletDialog(QWidget* parent) :
});
connect(ui->encrypt_wallet_checkbox, &QCheckBox::toggled, [this](bool checked) {
- // Disable the disable_privkeys_checkbox when isEncryptWalletChecked is
+ // Disable the disable_privkeys_checkbox and external_signer_checkbox when isEncryptWalletChecked is
// set to true, enable it when isEncryptWalletChecked is false.
ui->disable_privkeys_checkbox->setEnabled(!checked);
-
+#ifdef ENABLE_EXTERNAL_SIGNER
+ ui->external_signer_checkbox->setEnabled(!checked);
+#endif
// When the disable_privkeys_checkbox is disabled, uncheck it.
if (!ui->disable_privkeys_checkbox->isEnabled()) {
ui->disable_privkeys_checkbox->setChecked(false);
}
+
+ // When the external_signer_checkbox box is disabled, uncheck it.
+ if (!ui->external_signer_checkbox->isEnabled()) {
+ ui->external_signer_checkbox->setChecked(false);
+ }
+
+ });
+
+ connect(ui->external_signer_checkbox, &QCheckBox::toggled, [this](bool checked) {
+ ui->encrypt_wallet_checkbox->setEnabled(!checked);
+ ui->blank_wallet_checkbox->setEnabled(!checked);
+ ui->disable_privkeys_checkbox->setEnabled(!checked);
+ ui->descriptor_checkbox->setEnabled(!checked);
+
+ // The external signer checkbox is only enabled when a device is detected.
+ // In that case it is checked by default. Toggling it restores the other
+ // options to their default.
+ ui->descriptor_checkbox->setChecked(checked);
+ ui->encrypt_wallet_checkbox->setChecked(false);
+ ui->disable_privkeys_checkbox->setChecked(checked);
+ // The blank check box is ambiguous. This flag is always true for a
+ // watch-only wallet, even though we immedidately fetch keys from the
+ // external signer.
+ ui->blank_wallet_checkbox->setChecked(checked);
});
connect(ui->disable_privkeys_checkbox, &QCheckBox::toggled, [this](bool checked) {
@@ -63,11 +90,22 @@ CreateWalletDialog::CreateWalletDialog(QWidget* parent) :
ui->descriptor_checkbox->setToolTip(tr("Compiled without sqlite support (required for descriptor wallets)"));
ui->descriptor_checkbox->setEnabled(false);
ui->descriptor_checkbox->setChecked(false);
+ ui->external_signer_checkbox->setEnabled(false);
+ ui->external_signer_checkbox->setChecked(false);
#endif
+
#ifndef USE_BDB
ui->descriptor_checkbox->setEnabled(false);
ui->descriptor_checkbox->setChecked(true);
#endif
+
+#ifndef ENABLE_EXTERNAL_SIGNER
+ //: "External signing" means using devices such as hardware wallets.
+ ui->external_signer_checkbox->setToolTip(tr("Compiled without external signing support (required for external signing)"));
+ ui->external_signer_checkbox->setEnabled(false);
+ ui->external_signer_checkbox->setChecked(false);
+#endif
+
}
CreateWalletDialog::~CreateWalletDialog()
@@ -75,6 +113,26 @@ CreateWalletDialog::~CreateWalletDialog()
delete ui;
}
+void CreateWalletDialog::setSigners(const std::vector<ExternalSigner>& signers)
+{
+ if (!signers.empty()) {
+ ui->external_signer_checkbox->setEnabled(true);
+ ui->external_signer_checkbox->setChecked(true);
+ ui->encrypt_wallet_checkbox->setEnabled(false);
+ ui->encrypt_wallet_checkbox->setChecked(false);
+ // The order matters, because connect() is called when toggling a checkbox:
+ ui->blank_wallet_checkbox->setEnabled(false);
+ ui->blank_wallet_checkbox->setChecked(false);
+ ui->disable_privkeys_checkbox->setEnabled(false);
+ ui->disable_privkeys_checkbox->setChecked(true);
+ const std::string label = signers[0].m_name;
+ ui->wallet_name_line_edit->setText(QString::fromStdString(label));
+ ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
+ } else {
+ ui->external_signer_checkbox->setEnabled(false);
+ }
+}
+
QString CreateWalletDialog::walletName() const
{
return ui->wallet_name_line_edit->text();
@@ -99,3 +157,8 @@ bool CreateWalletDialog::isDescriptorWalletChecked() const
{
return ui->descriptor_checkbox->isChecked();
}
+
+bool CreateWalletDialog::isExternalSignerChecked() const
+{
+ return ui->external_signer_checkbox->isChecked();
+}
diff --git a/src/qt/createwalletdialog.h b/src/qt/createwalletdialog.h
index 20cce937c8..25ddf97585 100644
--- a/src/qt/createwalletdialog.h
+++ b/src/qt/createwalletdialog.h
@@ -7,6 +7,7 @@
#include <QDialog>
+class ExternalSigner;
class WalletModel;
namespace Ui {
@@ -23,11 +24,14 @@ public:
explicit CreateWalletDialog(QWidget* parent);
virtual ~CreateWalletDialog();
+ void setSigners(const std::vector<ExternalSigner>& signers);
+
QString walletName() const;
bool isEncryptWalletChecked() const;
bool isDisablePrivateKeysChecked() const;
bool isMakeBlankWalletChecked() const;
bool isDescriptorWalletChecked() const;
+ bool isExternalSignerChecked() const;
private:
Ui::CreateWalletDialog *ui;
diff --git a/src/qt/forms/createwalletdialog.ui b/src/qt/forms/createwalletdialog.ui
index 881869a46c..b11fb026b0 100644
--- a/src/qt/forms/createwalletdialog.ui
+++ b/src/qt/forms/createwalletdialog.ui
@@ -109,6 +109,16 @@
</property>
</widget>
</item>
+ <item>
+ <widget class="QCheckBox" name="external_signer_checkbox">
+ <property name="toolTip">
+ <string>Use an external signing device such as a hardware wallet. Configure the external signer script in wallet preferences first.</string>
+ </property>
+ <property name="text">
+ <string>External signer</string>
+ </property>
+ </widget>
+ </item>
</layout>
</widget>
</item>
@@ -143,6 +153,7 @@
<tabstop>disable_privkeys_checkbox</tabstop>
<tabstop>blank_wallet_checkbox</tabstop>
<tabstop>descriptor_checkbox</tabstop>
+ <tabstop>external_signer_checkbox</tabstop>
</tabstops>
<resources/>
<connections>
diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui
index 7a25ba907e..15e0d3fad9 100644
--- a/src/qt/forms/debugwindow.ui
+++ b/src/qt/forms/debugwindow.ui
@@ -470,13 +470,7 @@
</spacer>
</item>
<item>
- <widget class="QPushButton" name="fontSmallerButton">
- <property name="maximumSize">
- <size>
- <width>24</width>
- <height>24</height>
- </size>
- </property>
+ <widget class="QToolButton" name="fontSmallerButton">
<property name="toolTip">
<string>Decrease font size</string>
</property>
@@ -489,26 +483,14 @@
</property>
<property name="iconSize">
<size>
- <width>24</width>
- <height>16</height>
+ <width>22</width>
+ <height>22</height>
</size>
</property>
- <property name="autoDefault">
- <bool>false</bool>
- </property>
- <property name="flat">
- <bool>true</bool>
- </property>
</widget>
</item>
<item>
- <widget class="QPushButton" name="fontBiggerButton">
- <property name="maximumSize">
- <size>
- <width>24</width>
- <height>24</height>
- </size>
- </property>
+ <widget class="QToolButton" name="fontBiggerButton">
<property name="toolTip">
<string>Increase font size</string>
</property>
@@ -521,26 +503,14 @@
</property>
<property name="iconSize">
<size>
- <width>24</width>
- <height>16</height>
+ <width>22</width>
+ <height>22</height>
</size>
</property>
- <property name="autoDefault">
- <bool>false</bool>
- </property>
- <property name="flat">
- <bool>true</bool>
- </property>
</widget>
</item>
<item>
- <widget class="QPushButton" name="clearButton">
- <property name="maximumSize">
- <size>
- <width>24</width>
- <height>24</height>
- </size>
- </property>
+ <widget class="QToolButton" name="clearButton">
<property name="toolTip">
<string>Clear console</string>
</property>
@@ -554,15 +524,15 @@
<iconset resource="../bitcoin.qrc">
<normaloff>:/icons/remove</normaloff>:/icons/remove</iconset>
</property>
+ <property name="iconSize">
+ <size>
+ <width>22</width>
+ <height>22</height>
+ </size>
+ </property>
<property name="shortcut">
<string notr="true">Ctrl+L</string>
</property>
- <property name="autoDefault">
- <bool>false</bool>
- </property>
- <property name="flat">
- <bool>true</bool>
- </property>
</widget>
</item>
</layout>
diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui
index f199e8c1a1..bd72328c02 100644
--- a/src/qt/forms/optionsdialog.ui
+++ b/src/qt/forms/optionsdialog.ui
@@ -230,6 +230,36 @@
</widget>
</item>
<item>
+ <widget class="QGroupBox" name="groupBoxHww">
+ <property name="title">
+ <string>External Signer (e.g. hardware wallet)</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayoutHww">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayoutHww">
+ <item>
+ <widget class="QLabel" name="externalSignerPathLabel">
+ <property name="text">
+ <string>&amp;External signer script path</string>
+ </property>
+ <property name="buddy">
+ <cstring>externalSignerPath</cstring>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="externalSignerPath">
+ <property name="toolTip">
+ <string>Full path to a Bitcoin Core compatible script (e.g. C:\Downloads\hwi.exe or /Users/you/Downloads/hwi.py). Beware: malware can steal your coins!</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
<spacer name="verticalSpacer_Wallet">
<property name="orientation">
<enum>Qt::Vertical</enum>
diff --git a/src/qt/forms/receiverequestdialog.ui b/src/qt/forms/receiverequestdialog.ui
index 7d95a8bc90..70a7cf71de 100644
--- a/src/qt/forms/receiverequestdialog.ui
+++ b/src/qt/forms/receiverequestdialog.ui
@@ -255,6 +255,19 @@
</widget>
</item>
<item>
+ <widget class="QPushButton" name="btnVerify">
+ <property name="text">
+ <string>&amp;Verify</string>
+ </property>
+ <property name="toolTip">
+ <string>Verify this address on e.g. a hardware wallet screen</string>
+ </property>
+ <property name="autoDefault">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
<widget class="QPushButton" name="btnSaveAs">
<property name="text">
<string>&amp;Save Image…</string>
diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp
index 8a32994e3f..b12fe96567 100644
--- a/src/qt/optionsdialog.cpp
+++ b/src/qt/optionsdialog.cpp
@@ -92,6 +92,11 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) :
ui->thirdPartyTxUrls->setVisible(false);
}
+#ifndef ENABLE_EXTERNAL_SIGNER
+ //: "External signing" means using devices such as hardware wallets.
+ ui->externalSignerPath->setToolTip(tr("Compiled without external signing support (required for external signing)"));
+ ui->externalSignerPath->setEnabled(false);
+#endif
/* Display elements init */
QDir translations(":translations");
@@ -199,6 +204,7 @@ void OptionsDialog::setModel(OptionsModel *_model)
connect(ui->prune, &QCheckBox::clicked, this, &OptionsDialog::togglePruneWarning);
connect(ui->pruneSize, qOverload<int>(&QSpinBox::valueChanged), this, &OptionsDialog::showRestartWarning);
connect(ui->databaseCache, qOverload<int>(&QSpinBox::valueChanged), this, &OptionsDialog::showRestartWarning);
+ connect(ui->externalSignerPath, &QLineEdit::textChanged, [this]{ showRestartWarning(); });
connect(ui->threadsScriptVerif, qOverload<int>(&QSpinBox::valueChanged), this, &OptionsDialog::showRestartWarning);
/* Wallet */
connect(ui->spendZeroConfChange, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning);
@@ -233,6 +239,7 @@ void OptionsDialog::setMapper()
/* Wallet */
mapper->addMapping(ui->spendZeroConfChange, OptionsModel::SpendZeroConfChange);
mapper->addMapping(ui->coinControlFeatures, OptionsModel::CoinControlFeatures);
+ mapper->addMapping(ui->externalSignerPath, OptionsModel::ExternalSignerPath);
/* Network */
mapper->addMapping(ui->mapPortUpnp, OptionsModel::MapPortUPnP);
diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp
index abdf9e9ae6..24a4e9ee96 100644
--- a/src/qt/optionsmodel.cpp
+++ b/src/qt/optionsmodel.cpp
@@ -117,6 +117,13 @@ void OptionsModel::Init(bool resetSettings)
settings.setValue("bSpendZeroConfChange", true);
if (!gArgs.SoftSetBoolArg("-spendzeroconfchange", settings.value("bSpendZeroConfChange").toBool()))
addOverriddenOption("-spendzeroconfchange");
+
+ if (!settings.contains("external_signer_path"))
+ settings.setValue("external_signer_path", "");
+
+ if (!gArgs.SoftSetArg("-signer", settings.value("external_signer_path").toString().toStdString())) {
+ addOverriddenOption("-signer");
+ }
#endif
// Network
@@ -326,6 +333,8 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const
#ifdef ENABLE_WALLET
case SpendZeroConfChange:
return settings.value("bSpendZeroConfChange");
+ case ExternalSignerPath:
+ return settings.value("external_signer_path");
#endif
case DisplayUnit:
return nDisplayUnit;
@@ -445,6 +454,12 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in
setRestartRequired(true);
}
break;
+ case ExternalSignerPath:
+ if (settings.value("external_signer_path") != value.toString()) {
+ settings.setValue("external_signer_path", value.toString());
+ setRestartRequired(true);
+ }
+ break;
#endif
case DisplayUnit:
setDisplayUnit(value);
diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h
index 4d012a9b8f..535843e8ba 100644
--- a/src/qt/optionsmodel.h
+++ b/src/qt/optionsmodel.h
@@ -65,6 +65,7 @@ public:
Prune, // bool
PruneSize, // int
DatabaseCache, // int
+ ExternalSignerPath, // QString
SpendZeroConfChange, // bool
Listen, // bool
OptionIDRowCount,
diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp
index 27783bdf87..26e3dd0d60 100644
--- a/src/qt/overviewpage.cpp
+++ b/src/qt/overviewpage.cpp
@@ -166,8 +166,8 @@ OverviewPage::OverviewPage(const PlatformStyle *platformStyle, QWidget *parent)
// start with displaying the "out of sync" warnings
showOutOfSyncWarning(true);
- connect(ui->labelWalletStatus, &QPushButton::clicked, this, &OverviewPage::handleOutOfSyncWarningClicks);
- connect(ui->labelTransactionsStatus, &QPushButton::clicked, this, &OverviewPage::handleOutOfSyncWarningClicks);
+ connect(ui->labelWalletStatus, &QPushButton::clicked, this, &OverviewPage::outOfSyncWarningClicked);
+ connect(ui->labelTransactionsStatus, &QPushButton::clicked, this, &OverviewPage::outOfSyncWarningClicked);
}
void OverviewPage::handleTransactionClicked(const QModelIndex &index)
@@ -176,11 +176,6 @@ void OverviewPage::handleTransactionClicked(const QModelIndex &index)
Q_EMIT transactionClicked(filter->mapToSource(index));
}
-void OverviewPage::handleOutOfSyncWarningClicks()
-{
- Q_EMIT outOfSyncWarningClicked();
-}
-
void OverviewPage::setPrivacy(bool privacy)
{
m_privacy = privacy;
diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h
index 755a107a00..5270741c0d 100644
--- a/src/qt/overviewpage.h
+++ b/src/qt/overviewpage.h
@@ -65,7 +65,6 @@ private Q_SLOTS:
void handleTransactionClicked(const QModelIndex &index);
void updateAlerts(const QString &warnings);
void updateWatchOnlyLabels(bool showWatchOnly);
- void handleOutOfSyncWarningClicks();
void setMonospacedFont(bool use_embedded_font);
};
diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp
index 11441481bb..b324693692 100644
--- a/src/qt/peertablemodel.cpp
+++ b/src/qt/peertablemodel.cpp
@@ -14,52 +14,11 @@
#include <QList>
#include <QTimer>
-// private implementation
-class PeerTablePriv
-{
-public:
- /** Local cache of peer information */
- QList<CNodeCombinedStats> cachedNodeStats;
-
- /** Pull a full list of peers from vNodes into our cache */
- void refreshPeers(interfaces::Node& node)
- {
- cachedNodeStats.clear();
-
- interfaces::Node::NodesStats nodes_stats;
- node.getNodesStats(nodes_stats);
- cachedNodeStats.reserve(nodes_stats.size());
- for (const auto& node_stats : nodes_stats)
- {
- CNodeCombinedStats stats;
- stats.nodeStats = std::get<0>(node_stats);
- stats.fNodeStateStatsAvailable = std::get<1>(node_stats);
- stats.nodeStateStats = std::get<2>(node_stats);
- cachedNodeStats.append(stats);
- }
- }
-
- int size() const
- {
- return cachedNodeStats.size();
- }
-
- CNodeCombinedStats *index(int idx)
- {
- if (idx >= 0 && idx < cachedNodeStats.size())
- return &cachedNodeStats[idx];
-
- return nullptr;
- }
-};
-
PeerTableModel::PeerTableModel(interfaces::Node& node, QObject* parent) :
QAbstractTableModel(parent),
m_node(node),
timer(nullptr)
{
- priv.reset(new PeerTablePriv());
-
// set up timer for auto refresh
timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, &PeerTableModel::refresh);
@@ -84,15 +43,15 @@ void PeerTableModel::stopAutoRefresh()
timer->stop();
}
-int PeerTableModel::rowCount(const QModelIndex &parent) const
+int PeerTableModel::rowCount(const QModelIndex& parent) const
{
if (parent.isValid()) {
return 0;
}
- return priv->size();
+ return m_peers_data.size();
}
-int PeerTableModel::columnCount(const QModelIndex &parent) const
+int PeerTableModel::columnCount(const QModelIndex& parent) const
{
if (parent.isValid()) {
return 0;
@@ -100,7 +59,7 @@ int PeerTableModel::columnCount(const QModelIndex &parent) const
return columns.length();
}
-QVariant PeerTableModel::data(const QModelIndex &index, int role) const
+QVariant PeerTableModel::data(const QModelIndex& index, int role) const
{
if(!index.isValid())
return QVariant();
@@ -132,6 +91,7 @@ QVariant PeerTableModel::data(const QModelIndex &index, int role) const
} else if (role == Qt::TextAlignmentRole) {
switch (column) {
case NetNodeId:
+ return QVariant(Qt::AlignRight | Qt::AlignVCenter);
case Address:
return {};
case ConnectionType:
@@ -172,19 +132,52 @@ Qt::ItemFlags PeerTableModel::flags(const QModelIndex &index) const
return retval;
}
-QModelIndex PeerTableModel::index(int row, int column, const QModelIndex &parent) const
+QModelIndex PeerTableModel::index(int row, int column, const QModelIndex& parent) const
{
Q_UNUSED(parent);
- CNodeCombinedStats *data = priv->index(row);
- if (data)
- return createIndex(row, column, data);
+ if (0 <= row && row < rowCount() && 0 <= column && column < columnCount()) {
+ return createIndex(row, column, const_cast<CNodeCombinedStats*>(&m_peers_data[row]));
+ }
+
return QModelIndex();
}
void PeerTableModel::refresh()
{
- Q_EMIT layoutAboutToBeChanged();
- priv->refreshPeers(m_node);
- Q_EMIT layoutChanged();
+ interfaces::Node::NodesStats nodes_stats;
+ m_node.getNodesStats(nodes_stats);
+ decltype(m_peers_data) new_peers_data;
+ new_peers_data.reserve(nodes_stats.size());
+ for (const auto& node_stats : nodes_stats) {
+ const CNodeCombinedStats stats{std::get<0>(node_stats), std::get<2>(node_stats), std::get<1>(node_stats)};
+ new_peers_data.append(stats);
+ }
+
+ // Handle peer addition or removal as suggested in Qt Docs. See:
+ // - https://doc.qt.io/qt-5/model-view-programming.html#inserting-and-removing-rows
+ // - https://doc.qt.io/qt-5/model-view-programming.html#resizable-models
+ // We take advantage of the fact that the std::vector returned
+ // by interfaces::Node::getNodesStats is sorted by nodeid.
+ for (int i = 0; i < m_peers_data.size();) {
+ if (i < new_peers_data.size() && m_peers_data.at(i).nodeStats.nodeid == new_peers_data.at(i).nodeStats.nodeid) {
+ ++i;
+ continue;
+ }
+ // A peer has been removed from the table.
+ beginRemoveRows(QModelIndex(), i, i);
+ m_peers_data.erase(m_peers_data.begin() + i);
+ endRemoveRows();
+ }
+
+ if (m_peers_data.size() < new_peers_data.size()) {
+ // Some peers have been added to the end of the table.
+ beginInsertRows(QModelIndex(), m_peers_data.size(), new_peers_data.size() - 1);
+ m_peers_data.swap(new_peers_data);
+ endInsertRows();
+ } else {
+ m_peers_data.swap(new_peers_data);
+ }
+
+ Q_EMIT changed();
}
diff --git a/src/qt/peertablemodel.h b/src/qt/peertablemodel.h
index 3d195342f1..0ff1b5dba7 100644
--- a/src/qt/peertablemodel.h
+++ b/src/qt/peertablemodel.h
@@ -8,10 +8,11 @@
#include <net_processing.h> // For CNodeStateStats
#include <net.h>
-#include <memory>
-
#include <QAbstractTableModel>
+#include <QList>
+#include <QModelIndex>
#include <QStringList>
+#include <QVariant>
class PeerTablePriv;
@@ -61,18 +62,23 @@ public:
/** @name Methods overridden from QAbstractTableModel
@{*/
- int rowCount(const QModelIndex &parent) const override;
- int columnCount(const QModelIndex &parent) const override;
- QVariant data(const QModelIndex &index, int role) const override;
- QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
- QModelIndex index(int row, int column, const QModelIndex &parent) const override;
+ int rowCount(const QModelIndex& parent = QModelIndex()) const override;
+ int columnCount(const QModelIndex& parent = QModelIndex()) const override;
+ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
+ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
+ QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
/*@}*/
public Q_SLOTS:
void refresh();
+Q_SIGNALS:
+ void changed();
+
private:
+ //! Internal peer data structure.
+ QList<CNodeCombinedStats> m_peers_data{};
interfaces::Node& m_node;
const QStringList columns{
/*: Title of Peers Table column which contains a
@@ -99,7 +105,6 @@ private:
/*: Title of Peers Table column which contains the peer's
User Agent string. */
tr("User Agent")};
- std::unique_ptr<PeerTablePriv> priv;
QTimer *timer;
};
diff --git a/src/qt/psbtoperationsdialog.cpp b/src/qt/psbtoperationsdialog.cpp
index 99318c3bc0..2adfeeaaf0 100644
--- a/src/qt/psbtoperationsdialog.cpp
+++ b/src/qt/psbtoperationsdialog.cpp
@@ -50,7 +50,7 @@ void PSBTOperationsDialog::openWithPSBT(PartiallySignedTransaction psbtx)
bool complete;
size_t n_could_sign;
FinalizePSBT(psbtx); // Make sure all existing signatures are fully combined before checking for completeness.
- TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, m_transaction_data, complete, &n_could_sign);
+ TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, &n_could_sign, m_transaction_data, complete);
if (err != TransactionError::OK) {
showStatus(tr("Failed to load transaction: %1")
.arg(QString::fromStdString(TransactionErrorString(err).translated)), StatusLevel::ERR);
@@ -67,7 +67,7 @@ void PSBTOperationsDialog::signTransaction()
{
bool complete;
size_t n_signed;
- TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, true /* sign */, true /* bip32derivs */, m_transaction_data, complete, &n_signed);
+ TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, true /* sign */, true /* bip32derivs */, &n_signed, m_transaction_data, complete);
if (err != TransactionError::OK) {
showStatus(tr("Failed to sign transaction: %1")
@@ -226,7 +226,7 @@ void PSBTOperationsDialog::showStatus(const QString &msg, StatusLevel level) {
size_t PSBTOperationsDialog::couldSignInputs(const PartiallySignedTransaction &psbtx) {
size_t n_signed;
bool complete;
- TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, false /* bip32derivs */, m_transaction_data, complete, &n_signed);
+ TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, false /* bip32derivs */, &n_signed, m_transaction_data, complete);
if (err != TransactionError::OK) {
return 0;
diff --git a/src/qt/qrimagewidget.cpp b/src/qt/qrimagewidget.cpp
index f5200bb5c0..7cdd568644 100644
--- a/src/qt/qrimagewidget.cpp
+++ b/src/qt/qrimagewidget.cpp
@@ -27,8 +27,8 @@ QRImageWidget::QRImageWidget(QWidget *parent):
QLabel(parent), contextMenu(nullptr)
{
contextMenu = new QMenu(this);
- contextMenu->addAction(tr("Save Image…"), this, &QRImageWidget::saveImage);
- contextMenu->addAction(tr("Copy Image"), this, &QRImageWidget::copyImage);
+ contextMenu->addAction(tr("&Save Image…"), this, &QRImageWidget::saveImage);
+ contextMenu->addAction(tr("&Copy Image"), this, &QRImageWidget::copyImage);
}
bool QRImageWidget::setQR(const QString& data, const QString& text)
diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp
index 3f4d7f85e6..d47ee95826 100644
--- a/src/qt/receivecoinsdialog.cpp
+++ b/src/qt/receivecoinsdialog.cpp
@@ -44,11 +44,11 @@ ReceiveCoinsDialog::ReceiveCoinsDialog(const PlatformStyle *_platformStyle, QWid
// context menu
contextMenu = new QMenu(this);
- contextMenu->addAction(tr("Copy URI"), this, &ReceiveCoinsDialog::copyURI);
- contextMenu->addAction(tr("Copy address"), this, &ReceiveCoinsDialog::copyAddress);
- copyLabelAction = contextMenu->addAction(tr("Copy label"), this, &ReceiveCoinsDialog::copyLabel);
- copyMessageAction = contextMenu->addAction(tr("Copy message"), this, &ReceiveCoinsDialog::copyMessage);
- copyAmountAction = contextMenu->addAction(tr("Copy amount"), this, &ReceiveCoinsDialog::copyAmount);
+ contextMenu->addAction(tr("Copy &URI"), this, &ReceiveCoinsDialog::copyURI);
+ contextMenu->addAction(tr("&Copy address"), this, &ReceiveCoinsDialog::copyAddress);
+ copyLabelAction = contextMenu->addAction(tr("Copy &label"), this, &ReceiveCoinsDialog::copyLabel);
+ copyMessageAction = contextMenu->addAction(tr("Copy &message"), this, &ReceiveCoinsDialog::copyMessage);
+ copyAmountAction = contextMenu->addAction(tr("Copy &amount"), this, &ReceiveCoinsDialog::copyAmount);
connect(ui->recentRequestsView, &QWidget::customContextMenuRequested, this, &ReceiveCoinsDialog::showMenu);
connect(ui->clearButton, &QPushButton::clicked, this, &ReceiveCoinsDialog::clear);
diff --git a/src/qt/receiverequestdialog.cpp b/src/qt/receiverequestdialog.cpp
index 78ae5c07da..41f22e9c34 100644
--- a/src/qt/receiverequestdialog.cpp
+++ b/src/qt/receiverequestdialog.cpp
@@ -89,6 +89,12 @@ void ReceiveRequestDialog::setInfo(const SendCoinsRecipient &_info)
ui->wallet_tag->hide();
ui->wallet_content->hide();
}
+
+ ui->btnVerify->setVisible(model->wallet().hasExternalSigner());
+
+ connect(ui->btnVerify, &QPushButton::clicked, [this] {
+ model->displayAddress(info.address.toStdString());
+ });
}
void ReceiveRequestDialog::updateDisplayUnit()
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index 548b8cb34d..ff4bfb16f6 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -34,9 +34,12 @@
#include <wallet/wallet.h>
#endif
+#include <QAbstractButton>
#include <QDateTime>
#include <QFont>
#include <QKeyEvent>
+#include <QLatin1String>
+#include <QLocale>
#include <QMenu>
#include <QMessageBox>
#include <QScreen>
@@ -44,9 +47,10 @@
#include <QSettings>
#include <QString>
#include <QStringList>
+#include <QStyledItemDelegate>
#include <QTime>
#include <QTimer>
-
+#include <QVariant>
const int CONSOLE_HISTORY = 50;
const int INITIAL_TRAFFIC_GRAPH_MINS = 30;
@@ -128,6 +132,20 @@ public:
}
};
+class PeerIdViewDelegate : public QStyledItemDelegate
+{
+ Q_OBJECT
+public:
+ explicit PeerIdViewDelegate(QObject* parent = nullptr)
+ : QStyledItemDelegate(parent) {}
+
+ QString displayText(const QVariant& value, const QLocale& locale) const override
+ {
+ // Additional spaces should visually separate right-aligned content
+ // from the next column to the right.
+ return value.toString() + QLatin1String(" ");
+ }
+};
#include <qt/rpcconsole.moc>
@@ -469,6 +487,9 @@ RPCConsole::RPCConsole(interfaces::Node& node, const PlatformStyle *_platformSty
ui->splitter->restoreState(settings.value("RPCConsoleWidgetPeersTabSplitterSizes").toByteArray());
}
+ m_peer_widget_header_state = settings.value("PeersTabPeerHeaderState").toByteArray();
+ m_banlist_widget_header_state = settings.value("PeersTabBanlistHeaderState").toByteArray();
+
constexpr QChar nonbreaking_hyphen(8209);
const std::vector<QString> CONNECTION_TYPE_DOC{
tr("Inbound: initiated by peer"),
@@ -513,9 +534,9 @@ RPCConsole::RPCConsole(interfaces::Node& node, const PlatformStyle *_platformSty
ui->lineEdit->setMaxLength(16 * 1024 * 1024);
ui->messagesWidget->installEventFilter(this);
- connect(ui->clearButton, &QPushButton::clicked, [this] { clear(); });
- connect(ui->fontBiggerButton, &QPushButton::clicked, this, &RPCConsole::fontBigger);
- connect(ui->fontSmallerButton, &QPushButton::clicked, this, &RPCConsole::fontSmaller);
+ connect(ui->clearButton, &QAbstractButton::clicked, [this] { clear(); });
+ connect(ui->fontBiggerButton, &QAbstractButton::clicked, this, &RPCConsole::fontBigger);
+ connect(ui->fontSmallerButton, &QAbstractButton::clicked, this, &RPCConsole::fontSmaller);
connect(ui->btnClearTrafficGraph, &QPushButton::clicked, ui->trafficGraph, &TrafficGraphWidget::clear);
// disable the wallet selector by default
@@ -552,6 +573,9 @@ RPCConsole::~RPCConsole()
settings.setValue("RPCConsoleWidgetPeersTabSplitterSizes", ui->splitter->saveState());
}
+ settings.setValue("PeersTabPeerHeaderState", m_peer_widget_header_state);
+ settings.setValue("PeersTabBanlistHeaderState", m_banlist_widget_header_state);
+
m_node.rpcUnsetTimerInterface(rpcTimerInterface);
delete rpcTimerInterface;
delete ui;
@@ -640,23 +664,27 @@ void RPCConsole::setClientModel(ClientModel *model, int bestblock_height, int64_
ui->peerWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
ui->peerWidget->setSelectionMode(QAbstractItemView::ExtendedSelection);
ui->peerWidget->setContextMenuPolicy(Qt::CustomContextMenu);
- ui->peerWidget->setColumnWidth(PeerTableModel::Address, ADDRESS_COLUMN_WIDTH);
- ui->peerWidget->setColumnWidth(PeerTableModel::Subversion, SUBVERSION_COLUMN_WIDTH);
- ui->peerWidget->setColumnWidth(PeerTableModel::Ping, PING_COLUMN_WIDTH);
+
+ if (!ui->peerWidget->horizontalHeader()->restoreState(m_peer_widget_header_state)) {
+ ui->peerWidget->setColumnWidth(PeerTableModel::Address, ADDRESS_COLUMN_WIDTH);
+ ui->peerWidget->setColumnWidth(PeerTableModel::Subversion, SUBVERSION_COLUMN_WIDTH);
+ ui->peerWidget->setColumnWidth(PeerTableModel::Ping, PING_COLUMN_WIDTH);
+ }
ui->peerWidget->horizontalHeader()->setStretchLastSection(true);
+ ui->peerWidget->setItemDelegateForColumn(PeerTableModel::NetNodeId, new PeerIdViewDelegate(this));
// create peer table context menu
peersTableContextMenu = new QMenu(this);
- peersTableContextMenu->addAction(tr("Disconnect"), this, &RPCConsole::disconnectSelectedNode);
- peersTableContextMenu->addAction(ts.ban_for + " " + tr("1 hour"), [this] { banSelectedNode(60 * 60); });
- peersTableContextMenu->addAction(ts.ban_for + " " + tr("1 day"), [this] { banSelectedNode(60 * 60 * 24); });
- peersTableContextMenu->addAction(ts.ban_for + " " + tr("1 week"), [this] { banSelectedNode(60 * 60 * 24 * 7); });
- peersTableContextMenu->addAction(ts.ban_for + " " + tr("1 year"), [this] { banSelectedNode(60 * 60 * 24 * 365); });
+ peersTableContextMenu->addAction(tr("&Disconnect"), this, &RPCConsole::disconnectSelectedNode);
+ peersTableContextMenu->addAction(ts.ban_for + " " + tr("1 &hour"), [this] { banSelectedNode(60 * 60); });
+ peersTableContextMenu->addAction(ts.ban_for + " " + tr("1 d&ay"), [this] { banSelectedNode(60 * 60 * 24); });
+ peersTableContextMenu->addAction(ts.ban_for + " " + tr("1 &week"), [this] { banSelectedNode(60 * 60 * 24 * 7); });
+ peersTableContextMenu->addAction(ts.ban_for + " " + tr("1 &year"), [this] { banSelectedNode(60 * 60 * 24 * 365); });
connect(ui->peerWidget, &QTableView::customContextMenuRequested, this, &RPCConsole::showPeersTableContextMenu);
// peer table signal handling - update peer details when selecting new node
connect(ui->peerWidget->selectionModel(), &QItemSelectionModel::selectionChanged, this, &RPCConsole::updateDetailWidget);
- connect(model->getPeerTableModel(), &PeerTableModel::layoutChanged, this, &RPCConsole::updateDetailWidget);
+ connect(model->getPeerTableModel(), &PeerTableModel::changed, this, &RPCConsole::updateDetailWidget);
// set up ban table
ui->banlistWidget->setModel(model->getBanTableModel());
@@ -664,13 +692,16 @@ void RPCConsole::setClientModel(ClientModel *model, int bestblock_height, int64_
ui->banlistWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
ui->banlistWidget->setSelectionMode(QAbstractItemView::SingleSelection);
ui->banlistWidget->setContextMenuPolicy(Qt::CustomContextMenu);
- ui->banlistWidget->setColumnWidth(BanTableModel::Address, BANSUBNET_COLUMN_WIDTH);
- ui->banlistWidget->setColumnWidth(BanTableModel::Bantime, BANTIME_COLUMN_WIDTH);
+
+ if (!ui->banlistWidget->horizontalHeader()->restoreState(m_banlist_widget_header_state)) {
+ ui->banlistWidget->setColumnWidth(BanTableModel::Address, BANSUBNET_COLUMN_WIDTH);
+ ui->banlistWidget->setColumnWidth(BanTableModel::Bantime, BANTIME_COLUMN_WIDTH);
+ }
ui->banlistWidget->horizontalHeader()->setStretchLastSection(true);
// create ban table context menu
banTableContextMenu = new QMenu(this);
- banTableContextMenu->addAction(tr("Unban"), this, &RPCConsole::unbanSelectedNode);
+ banTableContextMenu->addAction(tr("&Unban"), this, &RPCConsole::unbanSelectedNode);
connect(ui->banlistWidget, &QTableView::customContextMenuRequested, this, &RPCConsole::showBanTableContextMenu);
// ban table signal handling - clear peer details when clicking a peer in the ban table
@@ -1171,6 +1202,11 @@ void RPCConsole::showEvent(QShowEvent *event)
void RPCConsole::hideEvent(QHideEvent *event)
{
+ // It is too late to call QHeaderView::saveState() in ~RPCConsole(), as all of
+ // the columns of QTableView child widgets will have zero width at that moment.
+ m_peer_widget_header_state = ui->peerWidget->horizontalHeader()->saveState();
+ m_banlist_widget_header_state = ui->banlistWidget->horizontalHeader()->saveState();
+
QWidget::hideEvent(event);
if (!clientModel || !clientModel->getPeerTableModel())
diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h
index 75f466642b..2412ae543c 100644
--- a/src/qt/rpcconsole.h
+++ b/src/qt/rpcconsole.h
@@ -10,9 +10,10 @@
#include <net.h>
-#include <QWidget>
+#include <QByteArray>
#include <QCompleter>
#include <QThread>
+#include <QWidget>
class ClientModel;
class PlatformStyle;
@@ -167,6 +168,8 @@ private:
QThread thread;
WalletModel* m_last_wallet_model{nullptr};
bool m_is_executing{false};
+ QByteArray m_peer_widget_header_state;
+ QByteArray m_banlist_widget_header_state;
/** Update UI with latest network info from model. */
void updateNetworkState();
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index 160b43324f..c9bf757dfc 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -199,7 +199,18 @@ void SendCoinsDialog::setModel(WalletModel *_model)
// set default rbf checkbox state
ui->optInRBF->setCheckState(Qt::Checked);
- if (model->wallet().privateKeysDisabled()) {
+ if (model->wallet().hasExternalSigner()) {
+ //: "device" usually means a hardware wallet
+ ui->sendButton->setText(tr("Sign on device"));
+ if (gArgs.GetArg("-signer", "") != "") {
+ ui->sendButton->setEnabled(true);
+ ui->sendButton->setToolTip(tr("Connect your hardware wallet first."));
+ } else {
+ ui->sendButton->setEnabled(false);
+ //: "External signer" means using devices such as hardware wallets.
+ ui->sendButton->setToolTip(tr("Set external signer script path in Options -> Wallet"));
+ }
+ } else if (model->wallet().privateKeysDisabled()) {
ui->sendButton->setText(tr("Cr&eate Unsigned"));
ui->sendButton->setToolTip(tr("Creates a Partially Signed Bitcoin Transaction (PSBT) for use with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.").arg(PACKAGE_NAME));
}
@@ -313,14 +324,14 @@ bool SendCoinsDialog::PrepareSendText(QString& question_string, QString& informa
formatted.append(recipientElement);
}
- if (model->wallet().privateKeysDisabled()) {
+ if (model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner()) {
question_string.append(tr("Do you want to draft this transaction?"));
} else {
question_string.append(tr("Are you sure you want to send?"));
}
question_string.append("<br /><span style='font-size:10pt;'>");
- if (model->wallet().privateKeysDisabled()) {
+ if (model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner()) {
question_string.append(tr("Please, review your transaction proposal. This will produce a Partially Signed Bitcoin Transaction (PSBT) which you can save or copy and then sign with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.").arg(PACKAGE_NAME));
} else {
question_string.append(tr("Please, review your transaction."));
@@ -386,8 +397,8 @@ void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
if (!PrepareSendText(question_string, informative_text, detailed_text)) return;
assert(m_current_transaction);
- const QString confirmation = model->wallet().privateKeysDisabled() ? tr("Confirm transaction proposal") : tr("Confirm send coins");
- const QString confirmButtonText = model->wallet().privateKeysDisabled() ? tr("Create Unsigned") : tr("Send");
+ const QString confirmation = model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner() ? tr("Confirm transaction proposal") : tr("Confirm send coins");
+ const QString confirmButtonText = model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner() ? tr("Create Unsigned") : tr("Sign and send");
SendConfirmationDialog confirmationDialog(confirmation, question_string, informative_text, detailed_text, SEND_CONFIRM_DELAY, confirmButtonText, this);
confirmationDialog.exec();
QMessageBox::StandardButton retval = static_cast<QMessageBox::StandardButton>(confirmationDialog.result());
@@ -403,9 +414,60 @@ void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
CMutableTransaction mtx = CMutableTransaction{*(m_current_transaction->getWtx())};
PartiallySignedTransaction psbtx(mtx);
bool complete = false;
- const TransactionError err = model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, psbtx, complete, nullptr);
+ // Always fill without signing first. This prevents an external signer
+ // from being called prematurely and is not expensive.
+ TransactionError err = model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, nullptr, psbtx, complete);
assert(!complete);
assert(err == TransactionError::OK);
+ if (model->wallet().hasExternalSigner()) {
+ try {
+ err = model->wallet().fillPSBT(SIGHASH_ALL, true /* sign */, true /* bip32derivs */, nullptr, psbtx, complete);
+ } catch (const std::runtime_error& e) {
+ QMessageBox::critical(nullptr, tr("Sign failed"), e.what());
+ send_failure = true;
+ return;
+ }
+ if (err == TransactionError::EXTERNAL_SIGNER_NOT_FOUND) {
+ //: "External signer" means using devices such as hardware wallets.
+ QMessageBox::critical(nullptr, tr("External signer not found"), "External signer not found");
+ send_failure = true;
+ return;
+ }
+ if (err == TransactionError::EXTERNAL_SIGNER_FAILED) {
+ //: "External signer" means using devices such as hardware wallets.
+ QMessageBox::critical(nullptr, tr("External signer failure"), "External signer failure");
+ send_failure = true;
+ return;
+ }
+ if (err != TransactionError::OK) {
+ tfm::format(std::cerr, "Failed to sign PSBT");
+ processSendCoinsReturn(WalletModel::TransactionCreationFailed);
+ send_failure = true;
+ return;
+ }
+ // fillPSBT does not always properly finalize
+ complete = FinalizeAndExtractPSBT(psbtx, mtx);
+ }
+
+ // Broadcast transaction if complete (even with an external signer this
+ // is not always the case, e.g. in a multisig wallet).
+ if (complete) {
+ const CTransactionRef tx = MakeTransactionRef(mtx);
+ m_current_transaction->setWtx(tx);
+ WalletModel::SendCoinsReturn sendStatus = model->sendCoins(*m_current_transaction);
+ // process sendStatus and on error generate message shown to user
+ processSendCoinsReturn(sendStatus);
+
+ if (sendStatus.status == WalletModel::OK) {
+ Q_EMIT coinsSent(m_current_transaction->getWtx()->GetHash());
+ } else {
+ send_failure = true;
+ }
+ return;
+ }
+
+ // Copy PSBT to clipboard and offer to save
+ assert(!complete);
// Serialize the PSBT
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << psbtx;
@@ -447,7 +509,7 @@ void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
break;
default:
assert(false);
- }
+ } // msgBox.exec()
} else {
// now send the prepared transaction
WalletModel::SendCoinsReturn sendStatus = model->sendCoins(*m_current_transaction);
@@ -614,7 +676,9 @@ void SendCoinsDialog::setBalance(const interfaces::WalletBalances& balances)
if(model && model->getOptionsModel())
{
CAmount balance = balances.balance;
- if (model->wallet().privateKeysDisabled()) {
+ if (model->wallet().hasExternalSigner()) {
+ ui->labelBalanceName->setText(tr("External balance:"));
+ } else if (model->wallet().privateKeysDisabled()) {
balance = balances.watch_only_balance;
ui->labelBalanceName->setText(tr("Watch-only balance:"));
}
@@ -698,7 +762,7 @@ void SendCoinsDialog::on_buttonMinimizeFee_clicked()
void SendCoinsDialog::useAvailableBalance(SendCoinsEntry* entry)
{
// Include watch-only for wallets without private key
- m_coin_control->fAllowWatchOnly = model->wallet().privateKeysDisabled();
+ m_coin_control->fAllowWatchOnly = model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner();
// Calculate available amount to send.
CAmount amount = model->wallet().getAvailableBalance(*m_coin_control);
@@ -753,7 +817,7 @@ void SendCoinsDialog::updateCoinControlState()
m_coin_control->m_confirm_target = getConfTargetForIndex(ui->confTargetSelector->currentIndex());
m_coin_control->m_signal_bip125_rbf = ui->optInRBF->isChecked();
// Include watch-only for wallets without private key
- m_coin_control->fAllowWatchOnly = model->wallet().privateKeysDisabled();
+ m_coin_control->fAllowWatchOnly = model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner();
}
void SendCoinsDialog::updateNumberOfBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool headers, SynchronizationState sync_state) {
diff --git a/src/qt/test/apptests.cpp b/src/qt/test/apptests.cpp
index cb3dbd2267..9c31cd50df 100644
--- a/src/qt/test/apptests.cpp
+++ b/src/qt/test/apptests.cpp
@@ -85,11 +85,6 @@ void AppTests::appTests()
// Reset global state to avoid interfering with later tests.
LogInstance().DisconnectTestLogger();
AbortShutdown();
- {
- LOCK(cs_main);
- UnloadBlockIndex(/* mempool */ nullptr, g_chainman);
- g_chainman.Reset();
- }
}
//! Entry point for BitcoinGUI tests.
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
index ea35f80cf5..3e1a0e0fa9 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -146,14 +146,14 @@ void TestGUI(interfaces::Node& node)
LOCK2(wallet->cs_wallet, spk_man->cs_KeyStore);
wallet->SetAddressBook(GetDestinationForKey(test.coinbaseKey.GetPubKey(), wallet->m_default_address_type), "", "receive");
spk_man->AddKeyPubKey(test.coinbaseKey, test.coinbaseKey.GetPubKey());
- wallet->SetLastBlockProcessed(105, ::ChainActive().Tip()->GetBlockHash());
+ wallet->SetLastBlockProcessed(105, node.context()->chainman->ActiveChain().Tip()->GetBlockHash());
}
{
WalletRescanReserver reserver(*wallet);
reserver.reserve();
CWallet::ScanResult result = wallet->ScanForWalletTransactions(Params().GetConsensus().hashGenesisBlock, 0 /* block height */, {} /* max height */, reserver, true /* fUpdate */);
QCOMPARE(result.status, CWallet::ScanResult::SUCCESS);
- QCOMPARE(result.last_scanned_block, ::ChainActive().Tip()->GetBlockHash());
+ QCOMPARE(result.last_scanned_block, node.context()->chainman->ActiveChain().Tip()->GetBlockHash());
QVERIFY(result.last_failed_block.IsNull());
}
wallet->SetBroadcastTransactions(true);
diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp
index 1e8e012dcf..4b1a546c7c 100644
--- a/src/qt/transactionview.cpp
+++ b/src/qt/transactionview.cpp
@@ -163,19 +163,19 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
contextMenu = new QMenu(this);
contextMenu->setObjectName("contextMenu");
- copyAddressAction = contextMenu->addAction(tr("Copy address"), this, &TransactionView::copyAddress);
- copyLabelAction = contextMenu->addAction(tr("Copy label"), this, &TransactionView::copyLabel);
- contextMenu->addAction(tr("Copy amount"), this, &TransactionView::copyAmount);
- contextMenu->addAction(tr("Copy transaction ID"), this, &TransactionView::copyTxID);
- contextMenu->addAction(tr("Copy raw transaction"), this, &TransactionView::copyTxHex);
- contextMenu->addAction(tr("Copy full transaction details"), this, &TransactionView::copyTxPlainText);
- contextMenu->addAction(tr("Show transaction details"), this, &TransactionView::showDetails);
+ copyAddressAction = contextMenu->addAction(tr("&Copy address"), this, &TransactionView::copyAddress);
+ copyLabelAction = contextMenu->addAction(tr("Copy &label"), this, &TransactionView::copyLabel);
+ contextMenu->addAction(tr("Copy &amount"), this, &TransactionView::copyAmount);
+ contextMenu->addAction(tr("Copy transaction &ID"), this, &TransactionView::copyTxID);
+ contextMenu->addAction(tr("Copy &raw transaction"), this, &TransactionView::copyTxHex);
+ contextMenu->addAction(tr("Copy full transaction &details"), this, &TransactionView::copyTxPlainText);
+ contextMenu->addAction(tr("&Show transaction details"), this, &TransactionView::showDetails);
contextMenu->addSeparator();
- bumpFeeAction = contextMenu->addAction(tr("Increase transaction fee"));
+ bumpFeeAction = contextMenu->addAction(tr("Increase transaction &fee"));
GUIUtil::ExceptionSafeConnect(bumpFeeAction, &QAction::triggered, this, &TransactionView::bumpFee);
bumpFeeAction->setObjectName("bumpFeeAction");
- abandonAction = contextMenu->addAction(tr("Abandon transaction"), this, &TransactionView::abandonTx);
- contextMenu->addAction(tr("Edit address label"), this, &TransactionView::editLabel);
+ abandonAction = contextMenu->addAction(tr("A&bandon transaction"), this, &TransactionView::abandonTx);
+ contextMenu->addAction(tr("&Edit address label"), this, &TransactionView::editLabel);
connect(dateWidget, qOverload<int>(&QComboBox::activated), this, &TransactionView::chooseDate);
connect(typeWidget, qOverload<int>(&QComboBox::activated), this, &TransactionView::chooseType);
diff --git a/src/qt/walletcontroller.cpp b/src/qt/walletcontroller.cpp
index aa26a01541..3cceb5ca5a 100644
--- a/src/qt/walletcontroller.cpp
+++ b/src/qt/walletcontroller.cpp
@@ -11,6 +11,7 @@
#include <qt/guiutil.h>
#include <qt/walletmodel.h>
+#include <external_signer.h>
#include <interfaces/handler.h>
#include <interfaces/node.h>
#include <util/string.h>
@@ -263,6 +264,9 @@ void CreateWalletActivity::createWallet()
if (m_create_wallet_dialog->isDescriptorWalletChecked()) {
flags |= WALLET_FLAG_DESCRIPTORS;
}
+ if (m_create_wallet_dialog->isExternalSignerChecked()) {
+ flags |= WALLET_FLAG_EXTERNAL_SIGNER;
+ }
QTimer::singleShot(500, worker(), [this, name, flags] {
std::unique_ptr<interfaces::Wallet> wallet = node().walletClient().createWallet(name, m_passphrase, flags, m_error_message, m_warning_message);
@@ -291,6 +295,15 @@ void CreateWalletActivity::finish()
void CreateWalletActivity::create()
{
m_create_wallet_dialog = new CreateWalletDialog(m_parent_widget);
+
+ std::vector<ExternalSigner> signers;
+ try {
+ signers = node().externalSigners();
+ } catch (const std::runtime_error& e) {
+ QMessageBox::critical(nullptr, tr("Can't list signers"), e.what());
+ }
+ m_create_wallet_dialog->setSigners(signers);
+
m_create_wallet_dialog->setWindowModality(Qt::ApplicationModal);
m_create_wallet_dialog->show();
diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp
index 02b3c62867..a1f357e0db 100644
--- a/src/qt/walletframe.cpp
+++ b/src/qt/walletframe.cpp
@@ -4,10 +4,7 @@
#include <qt/walletframe.h>
-#include <qt/bitcoingui.h>
-#include <qt/createwalletdialog.h>
#include <qt/overviewpage.h>
-#include <qt/walletcontroller.h>
#include <qt/walletmodel.h>
#include <qt/walletview.h>
@@ -19,9 +16,8 @@
#include <QPushButton>
#include <QVBoxLayout>
-WalletFrame::WalletFrame(const PlatformStyle* _platformStyle, BitcoinGUI* _gui)
- : QFrame(_gui),
- gui(_gui),
+WalletFrame::WalletFrame(const PlatformStyle* _platformStyle, QWidget* parent)
+ : QFrame(parent),
platformStyle(_platformStyle),
m_size_hint(OverviewPage{platformStyle, nullptr}.sizeHint())
{
@@ -42,11 +38,7 @@ WalletFrame::WalletFrame(const PlatformStyle* _platformStyle, BitcoinGUI* _gui)
// A button for create wallet dialog
QPushButton* create_wallet_button = new QPushButton(tr("Create a new wallet"), walletStack);
- connect(create_wallet_button, &QPushButton::clicked, [this] {
- auto activity = new CreateWalletActivity(gui->getWalletController(), this);
- connect(activity, &CreateWalletActivity::finished, activity, &QObject::deleteLater);
- activity->create();
- });
+ connect(create_wallet_button, &QPushButton::clicked, this, &WalletFrame::createWalletButtonClicked);
no_wallet_layout->addWidget(create_wallet_button, 0, Qt::AlignHCenter | Qt::AlignTop);
no_wallet_group->setLayout(no_wallet_layout);
@@ -66,17 +58,15 @@ void WalletFrame::setClientModel(ClientModel *_clientModel)
}
}
-bool WalletFrame::addWallet(WalletModel *walletModel)
+bool WalletFrame::addWallet(WalletModel* walletModel, WalletView* walletView)
{
- if (!gui || !clientModel || !walletModel) return false;
+ if (!clientModel || !walletModel) return false;
if (mapWalletViews.count(walletModel) > 0) return false;
- WalletView *walletView = new WalletView(platformStyle, this);
walletView->setClientModel(clientModel);
walletView->setWalletModel(walletModel);
walletView->showOutOfSyncWarning(bOutOfSync);
- walletView->setPrivacy(gui->isPrivacyModeActivated());
WalletView* current_wallet_view = currentWalletView();
if (current_wallet_view) {
@@ -88,17 +78,6 @@ bool WalletFrame::addWallet(WalletModel *walletModel)
walletStack->addWidget(walletView);
mapWalletViews[walletModel] = walletView;
- connect(walletView, &WalletView::outOfSyncWarningClicked, this, &WalletFrame::outOfSyncWarningClicked);
- connect(walletView, &WalletView::transactionClicked, gui, &BitcoinGUI::gotoHistoryPage);
- connect(walletView, &WalletView::coinsSent, gui, &BitcoinGUI::gotoHistoryPage);
- connect(walletView, &WalletView::message, [this](const QString& title, const QString& message, unsigned int style) {
- gui->message(title, message, style);
- });
- connect(walletView, &WalletView::encryptionStatusChanged, gui, &BitcoinGUI::updateWalletStatus);
- connect(walletView, &WalletView::incomingTransaction, gui, &BitcoinGUI::incomingTransaction);
- connect(walletView, &WalletView::hdEnabledStatusChanged, gui, &BitcoinGUI::updateWalletStatus);
- connect(gui, &BitcoinGUI::setPrivacy, walletView, &WalletView::setPrivacy);
-
return true;
}
@@ -263,8 +242,3 @@ WalletModel* WalletFrame::currentWalletModel() const
WalletView* wallet_view = currentWalletView();
return wallet_view ? wallet_view->getWalletModel() : nullptr;
}
-
-void WalletFrame::outOfSyncWarningClicked()
-{
- Q_EMIT requestedSyncWarningInfo();
-}
diff --git a/src/qt/walletframe.h b/src/qt/walletframe.h
index f57f8678d6..4f77bd716f 100644
--- a/src/qt/walletframe.h
+++ b/src/qt/walletframe.h
@@ -8,7 +8,6 @@
#include <QFrame>
#include <QMap>
-class BitcoinGUI;
class ClientModel;
class PlatformStyle;
class SendCoinsRecipient;
@@ -31,12 +30,12 @@ class WalletFrame : public QFrame
Q_OBJECT
public:
- explicit WalletFrame(const PlatformStyle *platformStyle, BitcoinGUI *_gui = nullptr);
+ explicit WalletFrame(const PlatformStyle* platformStyle, QWidget* parent);
~WalletFrame();
void setClientModel(ClientModel *clientModel);
- bool addWallet(WalletModel *walletModel);
+ bool addWallet(WalletModel* walletModel, WalletView* walletView);
void setCurrentWallet(WalletModel* wallet_model);
void removeWallet(WalletModel* wallet_model);
void removeAllWallets();
@@ -48,12 +47,10 @@ public:
QSize sizeHint() const override { return m_size_hint; }
Q_SIGNALS:
- /** Notify that the user has requested more information about the out-of-sync warning */
- void requestedSyncWarningInfo();
+ void createWalletButtonClicked();
private:
QStackedWidget *walletStack;
- BitcoinGUI *gui;
ClientModel *clientModel;
QMap<WalletModel*, WalletView*> mapWalletViews;
@@ -98,8 +95,6 @@ public Q_SLOTS:
void usedSendingAddresses();
/** Show used receiving addresses */
void usedReceivingAddresses();
- /** Pass on signal over requested out-of-sync-warning information */
- void outOfSyncWarningClicked();
};
#endif // BITCOIN_QT_WALLETFRAME_H
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index 7c58b8afd2..967dd588b4 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -525,7 +525,7 @@ bool WalletModel::bumpFee(uint256 hash, uint256& new_hash)
if (create_psbt) {
PartiallySignedTransaction psbtx(mtx);
bool complete = false;
- const TransactionError err = wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, psbtx, complete, nullptr);
+ const TransactionError err = wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, nullptr, psbtx, complete);
if (err != TransactionError::OK || complete) {
QMessageBox::critical(nullptr, tr("Fee bump error"), tr("Can't draft transaction."));
return false;
@@ -552,6 +552,18 @@ bool WalletModel::bumpFee(uint256 hash, uint256& new_hash)
return true;
}
+bool WalletModel::displayAddress(std::string sAddress)
+{
+ CTxDestination dest = DecodeDestination(sAddress);
+ bool res = false;
+ try {
+ res = m_wallet->displayAddress(dest);
+ } catch (const std::runtime_error& e) {
+ QMessageBox::critical(nullptr, tr("Can't display address"), e.what());
+ }
+ return res;
+}
+
bool WalletModel::isWalletEnabled()
{
return !gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET);
diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h
index b2ce5d69fb..47a21bcfcf 100644
--- a/src/qt/walletmodel.h
+++ b/src/qt/walletmodel.h
@@ -136,6 +136,7 @@ public:
UnlockContext requestUnlock();
bool bumpFee(uint256 hash, uint256& new_hash);
+ bool displayAddress(std::string sAddress);
static bool isWalletEnabled();
diff --git a/src/qt/walletmodeltransaction.cpp b/src/qt/walletmodeltransaction.cpp
index 25172e774c..d185ddb7e8 100644
--- a/src/qt/walletmodeltransaction.cpp
+++ b/src/qt/walletmodeltransaction.cpp
@@ -26,6 +26,11 @@ CTransactionRef& WalletModelTransaction::getWtx()
return wtx;
}
+void WalletModelTransaction::setWtx(const CTransactionRef& newTx)
+{
+ wtx = newTx;
+}
+
unsigned int WalletModelTransaction::getTransactionSize()
{
return wtx ? GetVirtualTransactionSize(*wtx) : 0;
diff --git a/src/qt/walletmodeltransaction.h b/src/qt/walletmodeltransaction.h
index f9a95362c8..120d240d91 100644
--- a/src/qt/walletmodeltransaction.h
+++ b/src/qt/walletmodeltransaction.h
@@ -27,6 +27,8 @@ public:
QList<SendCoinsRecipient> getRecipients() const;
CTransactionRef& getWtx();
+ void setWtx(const CTransactionRef&);
+
unsigned int getTransactionSize();
void setTransactionFee(const CAmount& newFee);
diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp
index cc9e1502f0..3b8cf4c7ed 100644
--- a/src/qt/walletview.cpp
+++ b/src/qt/walletview.cpp
@@ -73,7 +73,7 @@ WalletView::WalletView(const PlatformStyle *_platformStyle, QWidget *parent):
// Clicking on a transaction on the overview pre-selects the transaction on the transaction history page
connect(overviewPage, &OverviewPage::transactionClicked, transactionView, qOverload<const QModelIndex&>(&TransactionView::focusTransaction));
- connect(overviewPage, &OverviewPage::outOfSyncWarningClicked, this, &WalletView::requestedSyncWarningInfo);
+ connect(overviewPage, &OverviewPage::outOfSyncWarningClicked, this, &WalletView::outOfSyncWarningClicked);
connect(sendCoinsPage, &SendCoinsDialog::coinsSent, this, &WalletView::coinsSent);
// Highlight transaction after send
@@ -347,8 +347,3 @@ void WalletView::showProgress(const QString &title, int nProgress)
}
}
}
-
-void WalletView::requestedSyncWarningInfo()
-{
- Q_EMIT outOfSyncWarningClicked();
-}
diff --git a/src/qt/walletview.h b/src/qt/walletview.h
index 68f8a5e95b..fedf06b710 100644
--- a/src/qt/walletview.h
+++ b/src/qt/walletview.h
@@ -111,9 +111,6 @@ public Q_SLOTS:
/** Show progress dialog e.g. for rescan */
void showProgress(const QString &title, int nProgress);
- /** User has requested more information about the out of sync state */
- void requestedSyncWarningInfo();
-
Q_SIGNALS:
void setPrivacy(bool privacy);
void transactionClicked();
diff --git a/src/rest.cpp b/src/rest.cpp
index 747c7aea19..d599f381e3 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -125,7 +125,7 @@ static ChainstateManager* GetChainman(const std::any& context, HTTPRequest* req)
__FILE__, __LINE__, __func__, PACKAGE_BUGREPORT));
return nullptr;
}
- return node_context->chainman;
+ return node_context->chainman.get();
}
static RetFormat ParseDataFormat(std::string& param, const std::string& strReq)
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 83c1975d38..63897e0e05 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -84,7 +84,6 @@ ChainstateManager& EnsureChainman(const NodeContext& node)
if (!node.chainman) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Node chainman not found");
}
- WITH_LOCK(::cs_main, CHECK_NONFATAL(std::addressof(g_chainman) == std::addressof(*node.chainman)));
return *node.chainman;
}
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 6826e6fd07..327f961196 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -114,7 +114,6 @@ static bool GenerateBlock(ChainstateManager& chainman, CBlock& block, uint64_t&
{
LOCK(cs_main);
- CHECK_NONFATAL(std::addressof(::ChainActive()) == std::addressof(chainman.ActiveChain()));
IncrementExtraNonce(&block, chainman.ActiveChain().Tip(), extra_nonce);
}
@@ -147,7 +146,6 @@ static UniValue generateBlocks(ChainstateManager& chainman, const CTxMemPool& me
{ // Don't keep cs_main locked
LOCK(cs_main);
- CHECK_NONFATAL(std::addressof(::ChainActive()) == std::addressof(chainman.ActiveChain()));
nHeight = chainman.ActiveChain().Height();
nHeightEnd = nHeight+nGenerate;
}
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index c4d6e3d546..3013c76825 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -939,7 +939,7 @@ static RPCHelpMan addpeeraddress()
bool success{false};
if (LookupHost(addr_string, net_addr, false)) {
- CAddress address{CAddress({net_addr, port}, ServiceFlags(NODE_NETWORK | NODE_WITNESS))};
+ CAddress address{{net_addr, port}, ServiceFlags{NODE_NETWORK | NODE_WITNESS}};
address.nTime = GetAdjustedTime();
// The source address is set equal to the address. This is equivalent to the peer
// announcing itself.
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 339d711ac9..ccb3123714 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -753,7 +753,8 @@ static RPCHelpMan signrawtransactionwithkey()
},
},
},
- {"sighashtype", RPCArg::Type::STR, RPCArg::Default{"ALL"}, "The signature hash type. Must be one of:\n"
+ {"sighashtype", RPCArg::Type::STR, RPCArg::Default{"DEFAULT"}, "The signature hash type. Must be one of:\n"
+ " \"DEFAULT\"\n"
" \"ALL\"\n"
" \"NONE\"\n"
" \"SINGLE\"\n"
@@ -889,7 +890,7 @@ static RPCHelpMan testmempoolaccept()
"\nReturns result of mempool acceptance tests indicating if raw transaction(s) (serialized, hex-encoded) would be accepted by mempool.\n"
"\nIf multiple transactions are passed in, parents must come before children and package policies apply: the transactions cannot conflict with any mempool transactions or each other.\n"
"\nIf one transaction fails, other transactions may not be fully validated (the 'allowed' key will be blank).\n"
- "\nThe maximum number of transactions allowed is 25 (MAX_PACKAGE_COUNT)\n"
+ "\nThe maximum number of transactions allowed is " + ToString(MAX_PACKAGE_COUNT) + ".\n"
"\nThis checks if transactions violate the consensus or policy rules.\n"
"\nSee sendrawtransaction call.\n",
{
@@ -905,7 +906,7 @@ static RPCHelpMan testmempoolaccept()
RPCResult{
RPCResult::Type::ARR, "", "The result of the mempool acceptance test for each raw transaction in the input array.\n"
"Returns results for each transaction in the same order they were passed in.\n"
- "It is possible for transactions to not be fully validated ('allowed' unset) if an earlier transaction failed.\n",
+ "It is possible for transactions to not be fully validated ('allowed' unset) if another transaction failed.\n",
{
{RPCResult::Type::OBJ, "", "",
{
@@ -939,7 +940,6 @@ static RPCHelpMan testmempoolaccept()
UniValue::VARR,
UniValueType(), // VNUM or VSTR, checked inside AmountFromValue()
});
-
const UniValue raw_transactions = request.params[0].get_array();
if (raw_transactions.size() < 1 || raw_transactions.size() > MAX_PACKAGE_COUNT) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
@@ -951,6 +951,7 @@ static RPCHelpMan testmempoolaccept()
CFeeRate(AmountFromValue(request.params[1]));
std::vector<CTransactionRef> txns;
+ txns.reserve(raw_transactions.size());
for (const auto& rawtx : raw_transactions.getValues()) {
CMutableTransaction mtx;
if (!DecodeHexTx(mtx, rawtx.get_str())) {
@@ -971,8 +972,8 @@ static RPCHelpMan testmempoolaccept()
}();
UniValue rpc_result(UniValue::VARR);
- // We will check transaction fees we iterate through txns in order. If any transaction fee
- // exceeds maxfeerate, we will keave the rest of the validation results blank, because it
+ // We will check transaction fees while we iterate through txns in order. If any transaction fee
+ // exceeds maxfeerate, we will leave the rest of the validation results blank, because it
// doesn't make sense to return a validation result for a transaction if its ancestor(s) would
// not be submitted.
bool exit_early{false};
@@ -1655,6 +1656,7 @@ static RPCHelpMan utxoupdatepsbt()
}
// Fill the inputs
+ const PrecomputedTransactionData txdata = PrecomputePSBTData(psbtx);
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
PSBTInput& input = psbtx.inputs.at(i);
@@ -1671,7 +1673,7 @@ static RPCHelpMan utxoupdatepsbt()
// Update script/keypath information using descriptor data.
// Note that SignPSBTInput does a lot more than just constructing ECDSA signatures
// we don't actually care about those here, in fact.
- SignPSBTInput(public_provider, psbtx, i, /* sighash_type */ 1);
+ SignPSBTInput(public_provider, psbtx, i, &txdata, /* sighash_type */ 1);
}
// Update script/keypath information using descriptor data.
diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp
index 51cf8a7d62..84a8b06c5c 100644
--- a/src/script/descriptor.cpp
+++ b/src/script/descriptor.cpp
@@ -843,7 +843,9 @@ protected:
XOnlyPubKey xpk(keys[0]);
if (!xpk.IsFullyValid()) return {};
builder.Finalize(xpk);
- return Vector(GetScriptForDestination(builder.GetOutput()));
+ WitnessV1Taproot output = builder.GetOutput();
+ out.tr_spenddata[output].Merge(builder.GetSpendData());
+ return Vector(GetScriptForDestination(output));
}
bool ToStringSubScriptHelper(const SigningProvider* arg, std::string& ret, bool priv, bool normalized) const override
{
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index 3c3c3ac1a8..2dd173ee20 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -1420,7 +1420,7 @@ uint256 GetSpentScriptsSHA256(const std::vector<CTxOut>& outputs_spent)
} // namespace
template <class T>
-void PrecomputedTransactionData::Init(const T& txTo, std::vector<CTxOut>&& spent_outputs)
+void PrecomputedTransactionData::Init(const T& txTo, std::vector<CTxOut>&& spent_outputs, bool force)
{
assert(!m_spent_outputs_ready);
@@ -1431,9 +1431,9 @@ void PrecomputedTransactionData::Init(const T& txTo, std::vector<CTxOut>&& spent
}
// Determine which precomputation-impacting features this transaction uses.
- bool uses_bip143_segwit = false;
- bool uses_bip341_taproot = false;
- for (size_t inpos = 0; inpos < txTo.vin.size(); ++inpos) {
+ bool uses_bip143_segwit = force;
+ bool uses_bip341_taproot = force;
+ for (size_t inpos = 0; inpos < txTo.vin.size() && !(uses_bip143_segwit && uses_bip341_taproot); ++inpos) {
if (!txTo.vin[inpos].scriptWitness.IsNull()) {
if (m_spent_outputs_ready && m_spent_outputs[inpos].scriptPubKey.size() == 2 + WITNESS_V1_TAPROOT_SIZE &&
m_spent_outputs[inpos].scriptPubKey[0] == OP_1) {
@@ -1478,8 +1478,8 @@ PrecomputedTransactionData::PrecomputedTransactionData(const T& txTo)
}
// explicit instantiation
-template void PrecomputedTransactionData::Init(const CTransaction& txTo, std::vector<CTxOut>&& spent_outputs);
-template void PrecomputedTransactionData::Init(const CMutableTransaction& txTo, std::vector<CTxOut>&& spent_outputs);
+template void PrecomputedTransactionData::Init(const CTransaction& txTo, std::vector<CTxOut>&& spent_outputs, bool force);
+template void PrecomputedTransactionData::Init(const CMutableTransaction& txTo, std::vector<CTxOut>&& spent_outputs, bool force);
template PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo);
template PrecomputedTransactionData::PrecomputedTransactionData(const CMutableTransaction& txTo);
@@ -1711,7 +1711,7 @@ bool GenericTransactionSignatureChecker<T>::CheckSchnorrSignature(Span<const uns
if (hashtype == SIGHASH_DEFAULT) return set_error(serror, SCRIPT_ERR_SCHNORR_SIG_HASHTYPE);
}
uint256 sighash;
- assert(this->txdata);
+ if (!this->txdata) return HandleMissingData(m_mdb);
if (!SignatureHashSchnorr(sighash, execdata, *txTo, nIn, hashtype, sigversion, *this->txdata, m_mdb)) {
return set_error(serror, SCRIPT_ERR_SCHNORR_SIG_HASHTYPE);
}
diff --git a/src/script/interpreter.h b/src/script/interpreter.h
index fa4ee83e04..ced5c28bc1 100644
--- a/src/script/interpreter.h
+++ b/src/script/interpreter.h
@@ -168,7 +168,7 @@ struct PrecomputedTransactionData
PrecomputedTransactionData() = default;
template <class T>
- void Init(const T& tx, std::vector<CTxOut>&& spent_outputs);
+ void Init(const T& tx, std::vector<CTxOut>&& spent_outputs, bool force = false);
template <class T>
explicit PrecomputedTransactionData(const T& tx);
@@ -260,6 +260,9 @@ enum class MissingDataBehavior
FAIL, //!< Just act as if the signature was invalid
};
+template<typename T>
+bool SignatureHashSchnorr(uint256& hash_out, const ScriptExecutionData& execdata, const T& tx_to, uint32_t in_pos, uint8_t hash_type, SigVersion sigversion, const PrecomputedTransactionData& cache, MissingDataBehavior mdb);
+
template <class T>
class GenericTransactionSignatureChecker : public BaseSignatureChecker
{
diff --git a/src/script/sign.cpp b/src/script/sign.cpp
index da0092f9e3..65276f641f 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -11,13 +11,28 @@
#include <script/signingprovider.h>
#include <script/standard.h>
#include <uint256.h>
+#include <util/vector.h>
typedef std::vector<unsigned char> valtype;
-MutableTransactionSignatureCreator::MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn, MissingDataBehavior::FAIL) {}
+MutableTransactionSignatureCreator::MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn)
+ : txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn, MissingDataBehavior::FAIL),
+ m_txdata(nullptr)
+{
+}
+
+MutableTransactionSignatureCreator::MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, const PrecomputedTransactionData* txdata, int nHashTypeIn)
+ : txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn),
+ checker(txdata ? MutableTransactionSignatureChecker(txTo, nIn, amount, *txdata, MissingDataBehavior::FAIL) :
+ MutableTransactionSignatureChecker(txTo, nIn, amount, MissingDataBehavior::FAIL)),
+ m_txdata(txdata)
+{
+}
bool MutableTransactionSignatureCreator::CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& address, const CScript& scriptCode, SigVersion sigversion) const
{
+ assert(sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0);
+
CKey key;
if (!provider.GetKey(address, key))
return false;
@@ -26,13 +41,61 @@ bool MutableTransactionSignatureCreator::CreateSig(const SigningProvider& provid
if (sigversion == SigVersion::WITNESS_V0 && !key.IsCompressed())
return false;
- // Signing for witness scripts needs the amount.
- if (sigversion == SigVersion::WITNESS_V0 && amount < 0) return false;
+ // Signing without known amount does not work in witness scripts.
+ if (sigversion == SigVersion::WITNESS_V0 && !MoneyRange(amount)) return false;
+
+ // BASE/WITNESS_V0 signatures don't support explicit SIGHASH_DEFAULT, use SIGHASH_ALL instead.
+ const int hashtype = nHashType == SIGHASH_DEFAULT ? SIGHASH_ALL : nHashType;
- uint256 hash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion);
+ uint256 hash = SignatureHash(scriptCode, *txTo, nIn, hashtype, amount, sigversion, m_txdata);
if (!key.Sign(hash, vchSig))
return false;
- vchSig.push_back((unsigned char)nHashType);
+ vchSig.push_back((unsigned char)hashtype);
+ return true;
+}
+
+bool MutableTransactionSignatureCreator::CreateSchnorrSig(const SigningProvider& provider, std::vector<unsigned char>& sig, const XOnlyPubKey& pubkey, const uint256* leaf_hash, const uint256* merkle_root, SigVersion sigversion) const
+{
+ assert(sigversion == SigVersion::TAPROOT || sigversion == SigVersion::TAPSCRIPT);
+
+ CKey key;
+ {
+ // For now, use the old full pubkey-based key derivation logic. As it indexed by
+ // Hash160(full pubkey), we need to try both a version prefixed with 0x02, and one
+ // with 0x03.
+ unsigned char b[33] = {0x02};
+ std::copy(pubkey.begin(), pubkey.end(), b + 1);
+ CPubKey fullpubkey;
+ fullpubkey.Set(b, b + 33);
+ CKeyID keyid = fullpubkey.GetID();
+ if (!provider.GetKey(keyid, key)) {
+ b[0] = 0x03;
+ fullpubkey.Set(b, b + 33);
+ CKeyID keyid = fullpubkey.GetID();
+ if (!provider.GetKey(keyid, key)) return false;
+ }
+ }
+
+ // BIP341/BIP342 signing needs lots of precomputed transaction data. While some
+ // (non-SIGHASH_DEFAULT) sighash modes exist that can work with just some subset
+ // of data present, for now, only support signing when everything is provided.
+ if (!m_txdata || !m_txdata->m_bip341_taproot_ready || !m_txdata->m_spent_outputs_ready) return false;
+
+ ScriptExecutionData execdata;
+ execdata.m_annex_init = true;
+ execdata.m_annex_present = false; // Only support annex-less signing for now.
+ if (sigversion == SigVersion::TAPSCRIPT) {
+ execdata.m_codeseparator_pos_init = true;
+ execdata.m_codeseparator_pos = 0xFFFFFFFF; // Only support non-OP_CODESEPARATOR BIP342 signing for now.
+ if (!leaf_hash) return false; // BIP342 signing needs leaf hash.
+ execdata.m_tapleaf_hash_init = true;
+ execdata.m_tapleaf_hash = *leaf_hash;
+ }
+ uint256 hash;
+ if (!SignatureHashSchnorr(hash, execdata, *txTo, nIn, nHashType, sigversion, *m_txdata, MissingDataBehavior::FAIL)) return false;
+ sig.resize(64);
+ if (!key.SignSchnorr(hash, sig, merkle_root, nullptr)) return false;
+ if (nHashType) sig.push_back(nHashType);
return true;
}
@@ -92,6 +155,86 @@ static bool CreateSig(const BaseSignatureCreator& creator, SignatureData& sigdat
return false;
}
+static bool CreateTaprootScriptSig(const BaseSignatureCreator& creator, SignatureData& sigdata, const SigningProvider& provider, std::vector<unsigned char>& sig_out, const XOnlyPubKey& pubkey, const uint256& leaf_hash, SigVersion sigversion)
+{
+ auto lookup_key = std::make_pair(pubkey, leaf_hash);
+ auto it = sigdata.taproot_script_sigs.find(lookup_key);
+ if (it != sigdata.taproot_script_sigs.end()) {
+ sig_out = it->second;
+ }
+ if (creator.CreateSchnorrSig(provider, sig_out, pubkey, &leaf_hash, nullptr, sigversion)) {
+ sigdata.taproot_script_sigs[lookup_key] = sig_out;
+ return true;
+ }
+ return false;
+}
+
+static bool SignTaprootScript(const SigningProvider& provider, const BaseSignatureCreator& creator, SignatureData& sigdata, int leaf_version, const CScript& script, std::vector<valtype>& result)
+{
+ // Only BIP342 tapscript signing is supported for now.
+ if (leaf_version != TAPROOT_LEAF_TAPSCRIPT) return false;
+ SigVersion sigversion = SigVersion::TAPSCRIPT;
+
+ uint256 leaf_hash = (CHashWriter(HASHER_TAPLEAF) << uint8_t(leaf_version) << script).GetSHA256();
+
+ // <xonly pubkey> OP_CHECKSIG
+ if (script.size() == 34 && script[33] == OP_CHECKSIG && script[0] == 0x20) {
+ XOnlyPubKey pubkey(MakeSpan(script).subspan(1, 32));
+ std::vector<unsigned char> sig;
+ if (CreateTaprootScriptSig(creator, sigdata, provider, sig, pubkey, leaf_hash, sigversion)) {
+ result = Vector(std::move(sig));
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool SignTaproot(const SigningProvider& provider, const BaseSignatureCreator& creator, const WitnessV1Taproot& output, SignatureData& sigdata, std::vector<valtype>& result)
+{
+ TaprootSpendData spenddata;
+
+ // Gather information about this output.
+ if (provider.GetTaprootSpendData(output, spenddata)) {
+ sigdata.tr_spenddata.Merge(spenddata);
+ }
+
+ // Try key path spending.
+ {
+ std::vector<unsigned char> sig;
+ if (sigdata.taproot_key_path_sig.size() == 0) {
+ if (creator.CreateSchnorrSig(provider, sig, spenddata.internal_key, nullptr, &spenddata.merkle_root, SigVersion::TAPROOT)) {
+ sigdata.taproot_key_path_sig = sig;
+ }
+ }
+ if (sigdata.taproot_key_path_sig.size()) {
+ result = Vector(sigdata.taproot_key_path_sig);
+ return true;
+ }
+ }
+
+ // Try script path spending.
+ std::vector<std::vector<unsigned char>> smallest_result_stack;
+ for (const auto& [key, control_blocks] : sigdata.tr_spenddata.scripts) {
+ const auto& [script, leaf_ver] = key;
+ std::vector<std::vector<unsigned char>> result_stack;
+ if (SignTaprootScript(provider, creator, sigdata, leaf_ver, script, result_stack)) {
+ result_stack.emplace_back(std::begin(script), std::end(script)); // Push the script
+ result_stack.push_back(*control_blocks.begin()); // Push the smallest control block
+ if (smallest_result_stack.size() == 0 ||
+ GetSerializeSize(result_stack, PROTOCOL_VERSION) < GetSerializeSize(smallest_result_stack, PROTOCOL_VERSION)) {
+ smallest_result_stack = std::move(result_stack);
+ }
+ }
+ }
+ if (smallest_result_stack.size() != 0) {
+ result = std::move(smallest_result_stack);
+ return true;
+ }
+
+ return false;
+}
+
/**
* Sign scriptPubKey using signature made with creator.
* Signatures are returned in scriptSigRet (or returns false if scriptPubKey can't be signed),
@@ -113,7 +256,6 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
case TxoutType::NONSTANDARD:
case TxoutType::NULL_DATA:
case TxoutType::WITNESS_UNKNOWN:
- case TxoutType::WITNESS_V1_TAPROOT:
return false;
case TxoutType::PUBKEY:
if (!CreateSig(creator, sigdata, provider, sig, CPubKey(vSolutions[0]), scriptPubKey, sigversion)) return false;
@@ -175,6 +317,9 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
// Could not find witnessScript, add to missing
sigdata.missing_witness_script = uint256(vSolutions[0]);
return false;
+
+ case TxoutType::WITNESS_V1_TAPROOT:
+ return SignTaproot(provider, creator, WitnessV1Taproot(XOnlyPubKey{vSolutions[0]}), sigdata, ret);
} // no default case, so the compiler can warn about missing cases
assert(false);
}
@@ -205,7 +350,6 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato
bool solved = SignStep(provider, creator, fromPubKey, result, whichType, SigVersion::BASE, sigdata);
bool P2SH = false;
CScript subscript;
- sigdata.scriptWitness.stack.clear();
if (solved && whichType == TxoutType::SCRIPTHASH)
{
@@ -238,10 +382,17 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato
sigdata.scriptWitness.stack = result;
sigdata.witness = true;
result.clear();
+ } else if (whichType == TxoutType::WITNESS_V1_TAPROOT && !P2SH) {
+ sigdata.witness = true;
+ if (solved) {
+ sigdata.scriptWitness.stack = std::move(result);
+ }
+ result.clear();
} else if (solved && whichType == TxoutType::WITNESS_UNKNOWN) {
sigdata.witness = true;
}
+ if (!sigdata.witness) sigdata.scriptWitness.stack.clear();
if (P2SH) {
result.push_back(std::vector<unsigned char>(subscript.begin(), subscript.end()));
}
@@ -402,6 +553,7 @@ class DummySignatureChecker final : public BaseSignatureChecker
public:
DummySignatureChecker() {}
bool CheckECDSASignature(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override { return true; }
+ bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, const ScriptExecutionData& execdata, ScriptError* serror) const override { return true; }
};
const DummySignatureChecker DUMMY_CHECKER;
@@ -427,6 +579,11 @@ public:
vchSig[6 + m_r_len + m_s_len] = SIGHASH_ALL;
return true;
}
+ bool CreateSchnorrSig(const SigningProvider& provider, std::vector<unsigned char>& sig, const XOnlyPubKey& pubkey, const uint256* leaf_hash, const uint256* tweak, SigVersion sigversion) const override
+ {
+ sig.assign(64, '\000');
+ return true;
+ }
};
}
@@ -476,6 +633,26 @@ bool SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore,
// Use CTransaction for the constant parts of the
// transaction to avoid rehashing.
const CTransaction txConst(mtx);
+
+ PrecomputedTransactionData txdata;
+ std::vector<CTxOut> spent_outputs;
+ spent_outputs.resize(mtx.vin.size());
+ bool have_all_spent_outputs = true;
+ for (unsigned int i = 0; i < mtx.vin.size(); i++) {
+ CTxIn& txin = mtx.vin[i];
+ auto coin = coins.find(txin.prevout);
+ if (coin == coins.end() || coin->second.IsSpent()) {
+ have_all_spent_outputs = false;
+ } else {
+ spent_outputs[i] = CTxOut(coin->second.out.nValue, coin->second.out.scriptPubKey);
+ }
+ }
+ if (have_all_spent_outputs) {
+ txdata.Init(txConst, std::move(spent_outputs), true);
+ } else {
+ txdata.Init(txConst, {}, true);
+ }
+
// Sign what we can:
for (unsigned int i = 0; i < mtx.vin.size(); i++) {
CTxIn& txin = mtx.vin[i];
@@ -490,7 +667,7 @@ bool SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore,
SignatureData sigdata = DataFromTransaction(mtx, i, coin->second.out);
// Only sign SIGHASH_SINGLE if there's a corresponding output:
if (!fHashSingle || (i < mtx.vout.size())) {
- ProduceSignature(*keystore, MutableTransactionSignatureCreator(&mtx, i, amount, nHashType), prevPubKey, sigdata);
+ ProduceSignature(*keystore, MutableTransactionSignatureCreator(&mtx, i, amount, &txdata, nHashType), prevPubKey, sigdata);
}
UpdateInput(txin, sigdata);
@@ -502,7 +679,7 @@ bool SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore,
}
ScriptError serror = SCRIPT_ERR_OK;
- if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount, MissingDataBehavior::FAIL), &serror)) {
+ if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount, txdata, MissingDataBehavior::FAIL), &serror)) {
if (serror == SCRIPT_ERR_INVALID_STACK_OPERATION) {
// Unable to sign input and verification failed (possible attempt to partially sign).
input_errors[i] = "Unable to sign input, invalid stack size (possibly missing key)";
diff --git a/src/script/sign.h b/src/script/sign.h
index a1cfe1574d..b4e7318892 100644
--- a/src/script/sign.h
+++ b/src/script/sign.h
@@ -11,13 +11,13 @@
#include <pubkey.h>
#include <script/interpreter.h>
#include <script/keyorigin.h>
+#include <script/standard.h>
#include <span.h>
#include <streams.h>
class CKey;
class CKeyID;
class CScript;
-class CScriptID;
class CTransaction;
class SigningProvider;
@@ -31,6 +31,7 @@ public:
/** Create a singular (non-script) signature. */
virtual bool CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const =0;
+ virtual bool CreateSchnorrSig(const SigningProvider& provider, std::vector<unsigned char>& sig, const XOnlyPubKey& pubkey, const uint256* leaf_hash, const uint256* merkle_root, SigVersion sigversion) const =0;
};
/** A signature creator for transactions. */
@@ -40,11 +41,14 @@ class MutableTransactionSignatureCreator : public BaseSignatureCreator {
int nHashType;
CAmount amount;
const MutableTransactionSignatureChecker checker;
+ const PrecomputedTransactionData* m_txdata;
public:
MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn = SIGHASH_ALL);
+ MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, const PrecomputedTransactionData* txdata, int nHashTypeIn = SIGHASH_ALL);
const BaseSignatureChecker& Checker() const override { return checker; }
bool CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const override;
+ bool CreateSchnorrSig(const SigningProvider& provider, std::vector<unsigned char>& sig, const XOnlyPubKey& pubkey, const uint256* leaf_hash, const uint256* merkle_root, SigVersion sigversion) const override;
};
/** A signature creator that just produces 71-byte empty signatures. */
@@ -64,8 +68,11 @@ struct SignatureData {
CScript redeem_script; ///< The redeemScript (if any) for the input
CScript witness_script; ///< The witnessScript (if any) for the input. witnessScripts are used in P2WSH outputs.
CScriptWitness scriptWitness; ///< The scriptWitness of an input. Contains complete signatures or the traditional partial signatures format. scriptWitness is part of a transaction input per BIP 144.
+ TaprootSpendData tr_spenddata; ///< Taproot spending data.
std::map<CKeyID, SigPair> signatures; ///< BIP 174 style partial signatures for the input. May contain all signatures necessary for producing a final scriptSig or scriptWitness.
std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>> misc_pubkeys;
+ std::vector<unsigned char> taproot_key_path_sig; /// Schnorr signature for key path spending
+ std::map<std::pair<XOnlyPubKey, uint256>, std::vector<unsigned char>> taproot_script_sigs; ///< (Partial) schnorr signatures, indexed by XOnlyPubKey and leaf_hash.
std::vector<CKeyID> missing_pubkeys; ///< KeyIDs of pubkeys which could not be found
std::vector<CKeyID> missing_sigs; ///< KeyIDs of pubkeys for signatures which could not be found
uint160 missing_redeem_script; ///< ScriptID of the missing redeemScript (if any)
diff --git a/src/script/signingprovider.cpp b/src/script/signingprovider.cpp
index 9781ec32af..b80fbe22ce 100644
--- a/src/script/signingprovider.cpp
+++ b/src/script/signingprovider.cpp
@@ -44,6 +44,11 @@ bool HidingSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& inf
return m_provider->GetKeyOrigin(keyid, info);
}
+bool HidingSigningProvider::GetTaprootSpendData(const XOnlyPubKey& output_key, TaprootSpendData& spenddata) const
+{
+ return m_provider->GetTaprootSpendData(output_key, spenddata);
+}
+
bool FlatSigningProvider::GetCScript(const CScriptID& scriptid, CScript& script) const { return LookupHelper(scripts, scriptid, script); }
bool FlatSigningProvider::GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const { return LookupHelper(pubkeys, keyid, pubkey); }
bool FlatSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const
@@ -54,6 +59,10 @@ bool FlatSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info)
return ret;
}
bool FlatSigningProvider::GetKey(const CKeyID& keyid, CKey& key) const { return LookupHelper(keys, keyid, key); }
+bool FlatSigningProvider::GetTaprootSpendData(const XOnlyPubKey& output_key, TaprootSpendData& spenddata) const
+{
+ return LookupHelper(tr_spenddata, output_key, spenddata);
+}
FlatSigningProvider Merge(const FlatSigningProvider& a, const FlatSigningProvider& b)
{
@@ -66,6 +75,10 @@ FlatSigningProvider Merge(const FlatSigningProvider& a, const FlatSigningProvide
ret.keys.insert(b.keys.begin(), b.keys.end());
ret.origins = a.origins;
ret.origins.insert(b.origins.begin(), b.origins.end());
+ ret.tr_spenddata = a.tr_spenddata;
+ for (const auto& [output_key, spenddata] : b.tr_spenddata) {
+ ret.tr_spenddata[output_key].Merge(spenddata);
+ }
return ret;
}
diff --git a/src/script/signingprovider.h b/src/script/signingprovider.h
index 76f31d2f6f..939ae10622 100644
--- a/src/script/signingprovider.h
+++ b/src/script/signingprovider.h
@@ -25,6 +25,7 @@ public:
virtual bool GetKey(const CKeyID &address, CKey& key) const { return false; }
virtual bool HaveKey(const CKeyID &address) const { return false; }
virtual bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const { return false; }
+ virtual bool GetTaprootSpendData(const XOnlyPubKey& output_key, TaprootSpendData& spenddata) const { return false; }
};
extern const SigningProvider& DUMMY_SIGNING_PROVIDER;
@@ -42,6 +43,7 @@ public:
bool GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const override;
bool GetKey(const CKeyID& keyid, CKey& key) const override;
bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override;
+ bool GetTaprootSpendData(const XOnlyPubKey& output_key, TaprootSpendData& spenddata) const override;
};
struct FlatSigningProvider final : public SigningProvider
@@ -50,11 +52,13 @@ struct FlatSigningProvider final : public SigningProvider
std::map<CKeyID, CPubKey> pubkeys;
std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>> origins;
std::map<CKeyID, CKey> keys;
+ std::map<XOnlyPubKey, TaprootSpendData> tr_spenddata; /** Map from output key to spend data. */
bool GetCScript(const CScriptID& scriptid, CScript& script) const override;
bool GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const override;
bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override;
bool GetKey(const CKeyID& keyid, CKey& key) const override;
+ bool GetTaprootSpendData(const XOnlyPubKey& output_key, TaprootSpendData& spenddata) const override;
};
FlatSigningProvider Merge(const FlatSigningProvider& a, const FlatSigningProvider& b);
diff --git a/src/script/standard.cpp b/src/script/standard.cpp
index a4b11cc0a9..748f00dda5 100644
--- a/src/script/standard.cpp
+++ b/src/script/standard.cpp
@@ -377,6 +377,16 @@ bool IsValidDestination(const CTxDestination& dest) {
/*static*/ TaprootBuilder::NodeInfo TaprootBuilder::Combine(NodeInfo&& a, NodeInfo&& b)
{
NodeInfo ret;
+ /* Iterate over all tracked leaves in a, add b's hash to their Merkle branch, and move them to ret. */
+ for (auto& leaf : a.leaves) {
+ leaf.merkle_branch.push_back(b.hash);
+ ret.leaves.emplace_back(std::move(leaf));
+ }
+ /* Iterate over all tracked leaves in b, add a's hash to their Merkle branch, and move them to ret. */
+ for (auto& leaf : b.leaves) {
+ leaf.merkle_branch.push_back(a.hash);
+ ret.leaves.emplace_back(std::move(leaf));
+ }
/* Lexicographically sort a and b's hash, and compute parent hash. */
if (a.hash < b.hash) {
ret.hash = (CHashWriter(HASHER_TAPBRANCH) << a.hash << b.hash).GetSHA256();
@@ -386,6 +396,21 @@ bool IsValidDestination(const CTxDestination& dest) {
return ret;
}
+void TaprootSpendData::Merge(TaprootSpendData other)
+{
+ // TODO: figure out how to better deal with conflicting information
+ // being merged.
+ if (internal_key.IsNull() && !other.internal_key.IsNull()) {
+ internal_key = other.internal_key;
+ }
+ if (merkle_root.IsNull() && !other.merkle_root.IsNull()) {
+ merkle_root = other.merkle_root;
+ }
+ for (auto& [key, control_blocks] : other.scripts) {
+ scripts[key].merge(std::move(control_blocks));
+ }
+}
+
void TaprootBuilder::Insert(TaprootBuilder::NodeInfo&& node, int depth)
{
assert(depth >= 0 && (size_t)depth <= TAPROOT_CONTROL_MAX_NODE_COUNT);
@@ -435,13 +460,14 @@ void TaprootBuilder::Insert(TaprootBuilder::NodeInfo&& node, int depth)
return branch.size() == 0 || (branch.size() == 1 && branch[0]);
}
-TaprootBuilder& TaprootBuilder::Add(int depth, const CScript& script, int leaf_version)
+TaprootBuilder& TaprootBuilder::Add(int depth, const CScript& script, int leaf_version, bool track)
{
assert((leaf_version & ~TAPROOT_LEAF_MASK) == 0);
if (!IsValid()) return *this;
- /* Construct NodeInfo object with leaf hash. */
+ /* Construct NodeInfo object with leaf hash and (if track is true) also leaf information. */
NodeInfo node;
node.hash = (CHashWriter{HASHER_TAPLEAF} << uint8_t(leaf_version) << script).GetSHA256();
+ if (track) node.leaves.emplace_back(LeafInfo{script, leaf_version, {}});
/* Insert into the branch. */
Insert(std::move(node), depth);
return *this;
@@ -464,8 +490,33 @@ TaprootBuilder& TaprootBuilder::Finalize(const XOnlyPubKey& internal_key)
m_internal_key = internal_key;
auto ret = m_internal_key.CreateTapTweak(m_branch.size() == 0 ? nullptr : &m_branch[0]->hash);
assert(ret.has_value());
- std::tie(m_output_key, std::ignore) = *ret;
+ std::tie(m_output_key, m_parity) = *ret;
return *this;
}
WitnessV1Taproot TaprootBuilder::GetOutput() { return WitnessV1Taproot{m_output_key}; }
+
+TaprootSpendData TaprootBuilder::GetSpendData() const
+{
+ TaprootSpendData spd;
+ spd.merkle_root = m_branch.size() == 0 ? uint256() : m_branch[0]->hash;
+ spd.internal_key = m_internal_key;
+ if (m_branch.size()) {
+ // If any script paths exist, they have been combined into the root m_branch[0]
+ // by now. Compute the control block for each of its tracked leaves, and put them in
+ // spd.scripts.
+ for (const auto& leaf : m_branch[0]->leaves) {
+ std::vector<unsigned char> control_block;
+ control_block.resize(TAPROOT_CONTROL_BASE_SIZE + TAPROOT_CONTROL_NODE_SIZE * leaf.merkle_branch.size());
+ control_block[0] = leaf.leaf_version | (m_parity ? 1 : 0);
+ std::copy(m_internal_key.begin(), m_internal_key.end(), control_block.begin() + 1);
+ if (leaf.merkle_branch.size()) {
+ std::copy(leaf.merkle_branch[0].begin(),
+ leaf.merkle_branch[0].begin() + TAPROOT_CONTROL_NODE_SIZE * leaf.merkle_branch.size(),
+ control_block.begin() + TAPROOT_CONTROL_BASE_SIZE);
+ }
+ spd.scripts[{leaf.script, leaf.leaf_version}].insert(std::move(control_block));
+ }
+ }
+ return spd;
+}
diff --git a/src/script/standard.h b/src/script/standard.h
index d7ea5cef27..285dd4c116 100644
--- a/src/script/standard.h
+++ b/src/script/standard.h
@@ -11,6 +11,7 @@
#include <uint256.h>
#include <util/hash_type.h>
+#include <map>
#include <string>
#include <variant>
@@ -209,15 +210,50 @@ CScript GetScriptForRawPubKey(const CPubKey& pubkey);
/** Generate a multisig script. */
CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys);
+struct ShortestVectorFirstComparator
+{
+ bool operator()(const std::vector<unsigned char>& a, const std::vector<unsigned char>& b) const
+ {
+ if (a.size() < b.size()) return true;
+ if (a.size() > b.size()) return false;
+ return a < b;
+ }
+};
+
+struct TaprootSpendData
+{
+ /** The BIP341 internal key. */
+ XOnlyPubKey internal_key;
+ /** The Merkle root of the script tree (0 if no scripts). */
+ uint256 merkle_root;
+ /** Map from (script, leaf_version) to (sets of) control blocks.
+ * The control blocks are sorted by size, so that the signing logic can
+ * easily prefer the cheapest one. */
+ std::map<std::pair<CScript, int>, std::set<std::vector<unsigned char>, ShortestVectorFirstComparator>> scripts;
+ /** Merge other TaprootSpendData (for the same scriptPubKey) into this. */
+ void Merge(TaprootSpendData other);
+};
+
/** Utility class to construct Taproot outputs from internal key and script tree. */
class TaprootBuilder
{
private:
+ /** Information about a tracked leaf in the Merkle tree. */
+ struct LeafInfo
+ {
+ CScript script; //!< The script.
+ int leaf_version; //!< The leaf version for that script.
+ std::vector<uint256> merkle_branch; //!< The hashing partners above this leaf.
+ };
+
/** Information associated with a node in the Merkle tree. */
struct NodeInfo
{
/** Merkle hash of this node. */
uint256 hash;
+ /** Tracked leaves underneath this node (either from the node itself, or its children).
+ * The merkle_branch field for each is the partners to get to *this* node. */
+ std::vector<LeafInfo> leaves;
};
/** Whether the builder is in a valid state so far. */
bool m_valid = true;
@@ -260,7 +296,8 @@ private:
std::vector<std::optional<NodeInfo>> m_branch;
XOnlyPubKey m_internal_key; //!< The internal key, set when finalizing.
- XOnlyPubKey m_output_key; //!< The output key, computed when finalizing. */
+ XOnlyPubKey m_output_key; //!< The output key, computed when finalizing.
+ bool m_parity; //!< The tweak parity, computed when finalizing.
/** Combine information about a parent Merkle tree node from its child nodes. */
static NodeInfo Combine(NodeInfo&& a, NodeInfo&& b);
@@ -269,8 +306,9 @@ private:
public:
/** Add a new script at a certain depth in the tree. Add() operations must be called
- * in depth-first traversal order of binary tree. */
- TaprootBuilder& Add(int depth, const CScript& script, int leaf_version);
+ * in depth-first traversal order of binary tree. If track is true, it will be included in
+ * the GetSpendData() output. */
+ TaprootBuilder& Add(int depth, const CScript& script, int leaf_version, bool track = true);
/** Like Add(), but for a Merkle node with a given hash to the tree. */
TaprootBuilder& AddOmitted(int depth, const uint256& hash);
/** Finalize the construction. Can only be called when IsComplete() is true.
@@ -285,6 +323,8 @@ public:
WitnessV1Taproot GetOutput();
/** Check if a list of depths is legal (will lead to IsComplete()). */
static bool ValidDepths(const std::vector<int>& depths);
+ /** Compute spending data (after Finalize()). */
+ TaprootSpendData GetSpendData() const;
};
#endif // BITCOIN_SCRIPT_STANDARD_H
diff --git a/src/secp256k1/.cirrus.yml b/src/secp256k1/.cirrus.yml
new file mode 100644
index 0000000000..506a860336
--- /dev/null
+++ b/src/secp256k1/.cirrus.yml
@@ -0,0 +1,198 @@
+env:
+ WIDEMUL: auto
+ STATICPRECOMPUTATION: yes
+ ECMULTGENPRECISION: auto
+ ASM: no
+ BUILD: check
+ WITH_VALGRIND: yes
+ RUN_VALGRIND: no
+ EXTRAFLAGS:
+ HOST:
+ ECDH: no
+ RECOVERY: no
+ SCHNORRSIG: no
+ EXPERIMENTAL: no
+ CTIMETEST: yes
+ BENCH: yes
+ ITERS: 2
+ MAKEFLAGS: -j2
+
+cat_logs_snippet: &CAT_LOGS
+ always:
+ cat_tests_log_script:
+ - cat tests.log || true
+ cat_exhaustive_tests_log_script:
+ - cat exhaustive_tests.log || true
+ cat_valgrind_ctime_test_log_script:
+ - cat valgrind_ctime_test.log || true
+ cat_bench_log_script:
+ - cat bench.log || true
+ on_failure:
+ cat_config_log_script:
+ - cat config.log || true
+ cat_test_env_script:
+ - cat test_env.log || true
+ cat_ci_env_script:
+ - env
+
+merge_base_script_snippet: &MERGE_BASE
+ merge_base_script:
+ - if [ "$CIRRUS_PR" = "" ]; then exit 0; fi
+ - git fetch $CIRRUS_REPO_CLONE_URL $CIRRUS_BASE_BRANCH
+ - git config --global user.email "ci@ci.ci"
+ - git config --global user.name "ci"
+ - git merge FETCH_HEAD # Merge base to detect silent merge conflicts
+
+task:
+ name: "x86_64: Linux (Debian stable)"
+ container:
+ dockerfile: ci/linux-debian.Dockerfile
+ # Reduce number of CPUs to be able to do more builds in parallel.
+ cpu: 1
+ # More than enough for our scripts.
+ memory: 1G
+ matrix: &ENV_MATRIX
+ - env: {WIDEMUL: int64, RECOVERY: yes}
+ - env: {WIDEMUL: int64, ECDH: yes, EXPERIMENTAL: yes, SCHNORRSIG: yes}
+ - env: {WIDEMUL: int128}
+ - env: {WIDEMUL: int128, RECOVERY: yes, EXPERIMENTAL: yes, SCHNORRSIG: yes}
+ - env: {WIDEMUL: int128, ECDH: yes, EXPERIMENTAL: yes, SCHNORRSIG: yes}
+ - env: {WIDEMUL: int128, ASM: x86_64}
+ - env: { RECOVERY: yes, EXPERIMENTAL: yes, SCHNORRSIG: yes}
+ - env: { STATICPRECOMPUTATION: no}
+ - env: {BUILD: distcheck, WITH_VALGRIND: no, CTIMETEST: no, BENCH: no}
+ - env: {CPPFLAGS: -DDETERMINISTIC}
+ - env: {CFLAGS: -O0, CTIMETEST: no}
+ - env:
+ CFLAGS: "-fsanitize=undefined -fno-omit-frame-pointer"
+ LDFLAGS: "-fsanitize=undefined -fno-omit-frame-pointer"
+ UBSAN_OPTIONS: "print_stacktrace=1:halt_on_error=1"
+ ASM: x86_64
+ ECDH: yes
+ RECOVERY: yes
+ EXPERIMENTAL: yes
+ SCHNORRSIG: yes
+ CTIMETEST: no
+ - env: { ECMULTGENPRECISION: 2 }
+ - env: { ECMULTGENPRECISION: 8 }
+ - env:
+ RUN_VALGRIND: yes
+ ASM: x86_64
+ ECDH: yes
+ RECOVERY: yes
+ EXPERIMENTAL: yes
+ SCHNORRSIG: yes
+ EXTRAFLAGS: "--disable-openssl-tests"
+ BUILD:
+ matrix:
+ - env:
+ CC: gcc
+ - env:
+ CC: clang
+ << : *MERGE_BASE
+ test_script:
+ - ./ci/cirrus.sh
+ << : *CAT_LOGS
+
+task:
+ name: "i686: Linux (Debian stable)"
+ container:
+ dockerfile: ci/linux-debian.Dockerfile
+ cpu: 1
+ memory: 1G
+ env:
+ HOST: i686-linux-gnu
+ ECDH: yes
+ RECOVERY: yes
+ EXPERIMENTAL: yes
+ SCHNORRSIG: yes
+ matrix:
+ - env:
+ CC: i686-linux-gnu-gcc
+ - env:
+ CC: clang --target=i686-pc-linux-gnu -isystem /usr/i686-linux-gnu/include
+ test_script:
+ - ./ci/cirrus.sh
+ << : *CAT_LOGS
+
+task:
+ name: "x86_64: macOS Catalina"
+ macos_instance:
+ image: catalina-base
+ env:
+ HOMEBREW_NO_AUTO_UPDATE: 1
+ HOMEBREW_NO_INSTALL_CLEANUP: 1
+ # Cirrus gives us a fixed number of 12 virtual CPUs. Not that we even have that many jobs at the moment...
+ MAKEFLAGS: -j13
+ matrix:
+ << : *ENV_MATRIX
+ matrix:
+ - env:
+ CC: gcc-9
+ - env:
+ CC: clang
+ # Update Command Line Tools
+ # Uncomment this if the Command Line Tools on the CirrusCI macOS image are too old to brew valgrind.
+ # See https://apple.stackexchange.com/a/195963 for the implementation.
+ ## update_clt_script:
+ ## - system_profiler SPSoftwareDataType
+ ## - touch /tmp/.com.apple.dt.CommandLineTools.installondemand.in-progress
+ ## - |-
+ ## PROD=$(softwareupdate -l | grep "*.*Command Line" | tail -n 1 | awk -F"*" '{print $2}' | sed -e 's/^ *//' | sed 's/Label: //g' | tr -d '\n')
+ ## # For debugging
+ ## - softwareupdate -l && echo "PROD: $PROD"
+ ## - softwareupdate -i "$PROD" --verbose
+ ## - rm /tmp/.com.apple.dt.CommandLineTools.installondemand.in-progress
+ ##
+ brew_valgrind_pre_script:
+ - brew config
+ - brew tap --shallow LouisBrunner/valgrind
+ # Fetch valgrind source but don't build it yet.
+ - brew fetch --HEAD LouisBrunner/valgrind/valgrind
+ brew_valgrind_cache:
+ # This is $(brew --cellar valgrind) but command substition does not work here.
+ folder: /usr/local/Cellar/valgrind
+ # Rebuild cache if ...
+ fingerprint_script:
+ # ... macOS version changes:
+ - sw_vers
+ # ... brew changes:
+ - brew config
+ # ... valgrind changes:
+ - git -C "$(brew --cache)/valgrind--git" rev-parse HEAD
+ populate_script:
+ # If there's no hit in the cache, build and install valgrind.
+ - brew install --HEAD LouisBrunner/valgrind/valgrind
+ brew_valgrind_post_script:
+ # If we have restored valgrind from the cache, tell brew to create symlink to the PATH.
+ # If we haven't restored from cached (and just run brew install), this is a no-op.
+ - brew link valgrind
+ brew_script:
+ - brew install automake libtool gcc@9
+ << : *MERGE_BASE
+ test_script:
+ - ./ci/cirrus.sh
+ << : *CAT_LOGS
+
+task:
+ name: "s390x (big-endian): Linux (Debian stable, QEMU)"
+ container:
+ dockerfile: ci/linux-debian.Dockerfile
+ cpu: 1
+ memory: 1G
+ env:
+ QEMU_CMD: qemu-s390x
+ HOST: s390x-linux-gnu
+ BUILD:
+ WITH_VALGRIND: no
+ ECDH: yes
+ RECOVERY: yes
+ EXPERIMENTAL: yes
+ SCHNORRSIG: yes
+ CTIMETEST: no
+ << : *MERGE_BASE
+ test_script:
+ # https://sourceware.org/bugzilla/show_bug.cgi?id=27008
+ - rm /etc/ld.so.cache
+ - ./ci/cirrus.sh
+ << : *CAT_LOGS
diff --git a/src/secp256k1/.travis.yml b/src/secp256k1/.travis.yml
deleted file mode 100644
index ce8d6391b2..0000000000
--- a/src/secp256k1/.travis.yml
+++ /dev/null
@@ -1,108 +0,0 @@
-language: c
-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
- - valgrind
- - libtool-bin
-compiler:
- - clang
- - gcc
-env:
- global:
- - WIDEMUL=auto BIGNUM=auto STATICPRECOMPUTATION=yes ECMULTGENPRECISION=auto ASM=no BUILD=check WITH_VALGRIND=yes RUN_VALGRIND=no EXTRAFLAGS= HOST= ECDH=no RECOVERY=no SCHNORRSIG=no EXPERIMENTAL=no CTIMETEST=yes BENCH=yes ITERS=2
- matrix:
- - WIDEMUL=int64 RECOVERY=yes
- - WIDEMUL=int64 ECDH=yes EXPERIMENTAL=yes SCHNORRSIG=yes
- - WIDEMUL=int128
- - WIDEMUL=int128 RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes
- - WIDEMUL=int128 ECDH=yes EXPERIMENTAL=yes SCHNORRSIG=yes
- - WIDEMUL=int128 ASM=x86_64
- - BIGNUM=no
- - BIGNUM=no RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes
- - BIGNUM=no STATICPRECOMPUTATION=no
- - BUILD=distcheck WITH_VALGRIND=no CTIMETEST=no BENCH=no
- - CPPFLAGS=-DDETERMINISTIC
- - CFLAGS=-O0 CTIMETEST=no
- - ECMULTGENPRECISION=2
- - ECMULTGENPRECISION=8
- - RUN_VALGRIND=yes BIGNUM=no ASM=x86_64 ECDH=yes RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes EXTRAFLAGS="--disable-openssl-tests" BUILD=
-matrix:
- fast_finish: true
- include:
- - compiler: clang
- os: linux
- env: HOST=i686-linux-gnu
- 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
- 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
- - valgrind
- - libtool-bin
- - libc6-dbg:i386
- # S390x build (big endian system)
- - compiler: gcc
- env: HOST=s390x-unknown-linux-gnu ECDH=yes RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes CTIMETEST=
- arch: s390x
-
-# 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:
- - 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 023fa6067f..58c9635e53 100644
--- a/src/secp256k1/Makefile.am
+++ b/src/secp256k1/Makefile.am
@@ -14,8 +14,6 @@ noinst_HEADERS += src/scalar_8x32_impl.h
noinst_HEADERS += src/scalar_low_impl.h
noinst_HEADERS += src/group.h
noinst_HEADERS += src/group_impl.h
-noinst_HEADERS += src/num_gmp.h
-noinst_HEADERS += src/num_gmp_impl.h
noinst_HEADERS += src/ecdsa.h
noinst_HEADERS += src/ecdsa_impl.h
noinst_HEADERS += src/eckey.h
@@ -26,14 +24,16 @@ noinst_HEADERS += src/ecmult_const.h
noinst_HEADERS += src/ecmult_const_impl.h
noinst_HEADERS += src/ecmult_gen.h
noinst_HEADERS += src/ecmult_gen_impl.h
-noinst_HEADERS += src/num.h
-noinst_HEADERS += src/num_impl.h
noinst_HEADERS += src/field_10x26.h
noinst_HEADERS += src/field_10x26_impl.h
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/modinv32.h
+noinst_HEADERS += src/modinv32_impl.h
+noinst_HEADERS += src/modinv64.h
+noinst_HEADERS += src/modinv64_impl.h
noinst_HEADERS += src/assumptions.h
noinst_HEADERS += src/util.h
noinst_HEADERS += src/scratch.h
diff --git a/src/secp256k1/README.md b/src/secp256k1/README.md
index e070937235..197a56fff8 100644
--- a/src/secp256k1/README.md
+++ b/src/secp256k1/README.md
@@ -1,7 +1,7 @@
libsecp256k1
============
-[![Build Status](https://travis-ci.org/bitcoin-core/secp256k1.svg?branch=master)](https://travis-ci.org/bitcoin-core/secp256k1)
+[![Build Status](https://api.cirrus-ci.com/github/bitcoin-core/secp256k1.svg?branch=master)](https://cirrus-ci.com/github/bitcoin-core/secp256k1)
Optimized C library for ECDSA signatures and secret/public key operations on curve secp256k1.
@@ -34,11 +34,11 @@ Implementation details
* 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 (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.
* Using 4 64-bit limbs (relying on __int128 support in the compiler).
* Using 8 32-bit limbs.
+* Modular inverses (both field elements and scalars) based on [safegcd](https://gcd.cr.yp.to/index.html) with some modifications, and a variable-time variant (by Peter Dettman).
* Group operations
* Point addition formula specifically simplified for the curve equation (y^2 = x^3 + 7).
* Use addition between points in Jacobian and affine coordinates where possible.
diff --git a/src/secp256k1/build-aux/m4/ax_prog_cc_for_build.m4 b/src/secp256k1/build-aux/m4/ax_prog_cc_for_build.m4
index 77fd346a79..7bcbf3200c 100644
--- a/src/secp256k1/build-aux/m4/ax_prog_cc_for_build.m4
+++ b/src/secp256k1/build-aux/m4/ax_prog_cc_for_build.m4
@@ -1,5 +1,5 @@
# ===========================================================================
-# http://www.gnu.org/software/autoconf-archive/ax_prog_cc_for_build.html
+# https://www.gnu.org/software/autoconf-archive/ax_prog_cc_for_build.html
# ===========================================================================
#
# SYNOPSIS
diff --git a/src/secp256k1/build-aux/m4/bitcoin_secp.m4 b/src/secp256k1/build-aux/m4/bitcoin_secp.m4
index ece3d655ed..e57888ca18 100644
--- a/src/secp256k1/build-aux/m4/bitcoin_secp.m4
+++ b/src/secp256k1/build-aux/m4/bitcoin_secp.m4
@@ -75,15 +75,10 @@ if test x"$has_libcrypto" = x"yes" && test x"$has_openssl_ec" = x; then
fi
])
-dnl
-AC_DEFUN([SECP_GMP_CHECK],[
-if test x"$has_gmp" != x"yes"; then
+AC_DEFUN([SECP_VALGRIND_CHECK],[
+if test x"$has_valgrind" != x"yes"; then
CPPFLAGS_TEMP="$CPPFLAGS"
- CPPFLAGS="$GMP_CPPFLAGS $CPPFLAGS"
- LIBS_TEMP="$LIBS"
- LIBS="$GMP_LIBS $LIBS"
- AC_CHECK_HEADER(gmp.h,[AC_CHECK_LIB(gmp, __gmpz_init,[has_gmp=yes; GMP_LIBS="$GMP_LIBS -lgmp"; AC_DEFINE(HAVE_LIBGMP,1,[Define this symbol if libgmp is installed])])])
- CPPFLAGS="$CPPFLAGS_TEMP"
- LIBS="$LIBS_TEMP"
+ CPPFLAGS="$VALGRIND_CPPFLAGS $CPPFLAGS"
+ AC_CHECK_HEADER([valgrind/memcheck.h], [has_valgrind=yes; AC_DEFINE(HAVE_VALGRIND,1,[Define this symbol if valgrind is installed])])
fi
])
diff --git a/src/secp256k1/contrib/travis.sh b/src/secp256k1/ci/cirrus.sh
index 24cc9315cb..f26ca98d1d 100755
--- a/src/secp256k1/contrib/travis.sh
+++ b/src/secp256k1/ci/cirrus.sh
@@ -3,45 +3,63 @@
set -e
set -x
-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
+export LC_ALL=C
+
+env >> test_env.log
+
+$CC -v || true
+valgrind --version || true
+
+./autogen.sh
./configure \
--enable-experimental="$EXPERIMENTAL" \
- --with-test-override-wide-multiply="$WIDEMUL" --with-bignum="$BIGNUM" --with-asm="$ASM" \
+ --with-test-override-wide-multiply="$WIDEMUL" --with-asm="$ASM" \
--enable-ecmult-static-precomputation="$STATICPRECOMPUTATION" --with-ecmult-gen-precision="$ECMULTGENPRECISION" \
--enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" \
--enable-module-schnorrsig="$SCHNORRSIG" \
--with-valgrind="$WITH_VALGRIND" \
--host="$HOST" $EXTRAFLAGS
+# We have set "-j<n>" in MAKEFLAGS.
+make
+
+# Print information about binaries so that we can see that the architecture is correct
+file *tests || true
+file bench_* || true
+file .libs/* || true
+
if [ -n "$BUILD" ]
then
- make -j2 "$BUILD"
+ make "$BUILD"
fi
+
if [ "$RUN_VALGRIND" = "yes" ]
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)
+ # the `--error-exitcode` is required to make the test fail if valgrind found errors, otherwise it'll return 0 (https://www.valgrind.org/docs/manual/manual-core.html)
valgrind --error-exitcode=42 ./tests 16
valgrind --error-exitcode=42 ./exhaustive_tests
fi
+
+if [ -n "$QEMU_CMD" ]
+then
+ $QEMU_CMD ./tests 16
+ $QEMU_CMD ./exhaustive_tests
+fi
+
if [ "$BENCH" = "yes" ]
then
+ # Using the local `libtool` because on macOS the system's libtool has nothing to do with GNU libtool
+ EXEC='./libtool --mode=execute'
+ if [ -n "$QEMU_CMD" ]
+ then
+ EXEC="$EXEC $QEMU_CMD"
+ fi
if [ "$RUN_VALGRIND" = "yes" ]
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=
+ EXEC="$EXEC valgrind --error-exitcode=42"
fi
- # This limits the iterations in the benchmarks below to ITER(set in .travis.yml) iterations.
+ # This limits the iterations in the benchmarks below to ITER iterations.
export SECP256K1_BENCH_ITERS="$ITERS"
{
$EXEC ./bench_ecmult
diff --git a/src/secp256k1/ci/linux-debian.Dockerfile b/src/secp256k1/ci/linux-debian.Dockerfile
new file mode 100644
index 0000000000..5967cf8b31
--- /dev/null
+++ b/src/secp256k1/ci/linux-debian.Dockerfile
@@ -0,0 +1,13 @@
+FROM debian:stable
+
+RUN dpkg --add-architecture i386
+RUN dpkg --add-architecture s390x
+RUN apt-get update
+
+# dkpg-dev: to make pkg-config work in cross-builds
+RUN apt-get install --no-install-recommends --no-upgrade -y \
+ git ca-certificates \
+ make automake libtool pkg-config dpkg-dev valgrind qemu-user \
+ gcc clang libc6-dbg \
+ gcc-i686-linux-gnu libc6-dev-i386-cross libc6-dbg:i386 \
+ gcc-s390x-linux-gnu libc6-dev-s390x-cross libc6-dbg:s390x
diff --git a/src/secp256k1/configure.ac b/src/secp256k1/configure.ac
index eb3b449bec..1ed991afa7 100644
--- a/src/secp256k1/configure.ac
+++ b/src/secp256k1/configure.ac
@@ -14,7 +14,7 @@ AM_INIT_AUTOMAKE([foreign subdir-objects])
: ${CFLAGS="-g"}
LT_INIT
-dnl make the compilation flags quiet unless V=1 is used
+# Make the compilation flags quiet unless V=1 is used.
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
PKG_PROG_PKG_CONFIG
@@ -22,9 +22,16 @@ PKG_PROG_PKG_CONFIG
AC_PATH_TOOL(AR, ar)
AC_PATH_TOOL(RANLIB, ranlib)
AC_PATH_TOOL(STRIP, strip)
-AX_PROG_CC_FOR_BUILD
+# Save definition of AC_PROG_CC because AM_PROG_CC_C_O in automake<=1.13 will
+# redefine AC_PROG_CC to exit with an error, which avoids the user calling it
+# accidently and screwing up the effect of AM_PROG_CC_C_O. However, we'll need
+# AC_PROG_CC later on in AX_PROG_CC_FOR_BUILD, where its usage is fine, and
+# we'll carefully make sure not to call AC_PROG_CC anywhere else.
+m4_copy([AC_PROG_CC], [saved_AC_PROG_CC])
AM_PROG_CC_C_O
+# Restore AC_PROG_CC
+m4_rename_force([saved_AC_PROG_CC], [AC_PROG_CC])
AC_PROG_CC_C89
if test x"$ac_cv_prog_cc_c89" = x"no"; then
@@ -37,25 +44,23 @@ case $host_os in
if test x$cross_compiling != xyes; then
AC_PATH_PROG([BREW],brew,)
if test x$BREW != x; then
- dnl These Homebrew packages may be keg-only, meaning that they won't be found
- dnl in expected paths because they may conflict with system files. Ask
- dnl Homebrew where each one is located, then adjust paths accordingly.
-
+ # These Homebrew packages may be keg-only, meaning that they won't be found
+ # in expected paths because they may conflict with system files. Ask
+ # Homebrew where each one is located, then adjust paths accordingly.
openssl_prefix=`$BREW --prefix openssl 2>/dev/null`
- gmp_prefix=`$BREW --prefix gmp 2>/dev/null`
+ valgrind_prefix=`$BREW --prefix valgrind 2>/dev/null`
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"
- GMP_LIBS="-L$gmp_prefix/lib"
+ if test x$valgrind_prefix != x; then
+ VALGRIND_CPPFLAGS="-I$valgrind_prefix/include"
fi
else
AC_PATH_PROG([PORT],port,)
- dnl if homebrew isn't installed and macports is, add the macports default paths
- dnl as a last resort.
+ # If homebrew isn't installed and macports is, add the macports default paths
+ # as a last resort.
if test x$PORT != x; then
CPPFLAGS="$CPPFLAGS -isystem /opt/local/include"
LDFLAGS="$LDFLAGS -L/opt/local/lib"
@@ -78,6 +83,15 @@ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])],
])
saved_CFLAGS="$CFLAGS"
+CFLAGS="-Wconditional-uninitialized $CFLAGS"
+AC_MSG_CHECKING([if ${CC} supports -Wconditional-uninitialized])
+AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])],
+ [ AC_MSG_RESULT([yes]) ],
+ [ AC_MSG_RESULT([no])
+ CFLAGS="$saved_CFLAGS"
+ ])
+
+saved_CFLAGS="$CFLAGS"
CFLAGS="-fvisibility=hidden $CFLAGS"
AC_MSG_CHECKING([if ${CC} supports -fvisibility=hidden])
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])],
@@ -86,6 +100,10 @@ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])],
CFLAGS="$saved_CFLAGS"
])
+###
+### Define config arguments
+###
+
AC_ARG_ENABLE(benchmark,
AS_HELP_STRING([--enable-benchmark],[compile benchmark [default=yes]]),
[use_benchmark=$enableval],
@@ -146,13 +164,10 @@ AC_ARG_ENABLE(external_default_callbacks,
[use_external_default_callbacks=$enableval],
[use_external_default_callbacks=no])
-dnl Test-only override of the (autodetected by the C code) "widemul" setting.
-dnl Legal values are int64 (for [u]int64_t), int128 (for [unsigned] __int128), and auto (the default).
+# Test-only override of the (autodetected by the C code) "widemul" setting.
+# Legal values are int64 (for [u]int64_t), int128 (for [unsigned] __int128), and auto (the default).
AC_ARG_WITH([test-override-wide-multiply], [] ,[set_widemul=$withval], [set_widemul=auto])
-AC_ARG_WITH([bignum], [AS_HELP_STRING([--with-bignum=gmp|no|auto],
-[bignum implementation to use [default=auto]])],[req_bignum=$withval], [req_bignum=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])
@@ -177,15 +192,22 @@ AC_ARG_WITH([valgrind], [AS_HELP_STRING([--with-valgrind=yes|no|auto],
)],
[req_valgrind=$withval], [req_valgrind=auto])
+###
+### Handle config options (except for modules)
+###
+
if test x"$req_valgrind" = x"no"; then
enable_valgrind=no
else
- AC_CHECK_HEADER([valgrind/memcheck.h], [enable_valgrind=yes], [
+ SECP_VALGRIND_CHECK
+ if test x"$has_valgrind" != x"yes"; then
if test x"$req_valgrind" = x"yes"; then
AC_MSG_ERROR([Valgrind support explicitly requested but valgrind/memcheck.h header not available])
fi
enable_valgrind=no
- ], [])
+ else
+ enable_valgrind=yes
+ fi
fi
AM_CONDITIONAL([VALGRIND_ENABLED],[test "$enable_valgrind" = "yes"])
@@ -197,61 +219,6 @@ else
CFLAGS="-O2 $CFLAGS"
fi
-if test x"$use_ecmult_static_precomputation" != x"no"; then
- # Temporarily switch to an environment for the native compiler
- save_cross_compiling=$cross_compiling
- cross_compiling=no
- SAVE_CC="$CC"
- CC="$CC_FOR_BUILD"
- SAVE_CFLAGS="$CFLAGS"
- CFLAGS="$CFLAGS_FOR_BUILD"
- SAVE_CPPFLAGS="$CPPFLAGS"
- CPPFLAGS="$CPPFLAGS_FOR_BUILD"
- SAVE_LDFLAGS="$LDFLAGS"
- LDFLAGS="$LDFLAGS_FOR_BUILD"
-
- warn_CFLAGS_FOR_BUILD="-Wall -Wextra -Wno-unused-function"
- saved_CFLAGS="$CFLAGS"
- 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]) ],
- [ AC_MSG_RESULT([no])
- CFLAGS="$saved_CFLAGS"
- ])
-
- AC_MSG_CHECKING([for working native compiler: ${CC_FOR_BUILD}])
- AC_RUN_IFELSE(
- [AC_LANG_PROGRAM([], [])],
- [working_native_cc=yes],
- [working_native_cc=no],[:])
-
- CFLAGS_FOR_BUILD="$CFLAGS"
-
- # Restore the environment
- cross_compiling=$save_cross_compiling
- CC="$SAVE_CC"
- CFLAGS="$SAVE_CFLAGS"
- CPPFLAGS="$SAVE_CPPFLAGS"
- LDFLAGS="$SAVE_LDFLAGS"
-
- if test x"$working_native_cc" = x"no"; then
- AC_MSG_RESULT([no])
- set_precomp=no
- m4_define([please_set_for_build], [Please set CC_FOR_BUILD, CFLAGS_FOR_BUILD, CPPFLAGS_FOR_BUILD, and/or LDFLAGS_FOR_BUILD.])
- if test x"$use_ecmult_static_precomputation" = x"yes"; then
- AC_MSG_ERROR([native compiler ${CC_FOR_BUILD} does not produce working binaries. please_set_for_build])
- else
- AC_MSG_WARN([Disabling statically generated ecmult table because the native compiler ${CC_FOR_BUILD} does not produce working binaries. please_set_for_build])
- fi
- else
- AC_MSG_RESULT([yes])
- set_precomp=yes
- fi
-else
- set_precomp=no
-fi
-
if test x"$req_asm" = x"auto"; then
SECP_64BIT_ASM_CHECK
if test x"$has_64bit_asm" = x"yes"; then
@@ -279,33 +246,7 @@ else
esac
fi
-if test x"$req_bignum" = x"auto"; then
- SECP_GMP_CHECK
- if test x"$has_gmp" = x"yes"; then
- set_bignum=gmp
- fi
-
- if test x"$set_bignum" = x; then
- set_bignum=no
- fi
-else
- set_bignum=$req_bignum
- case $set_bignum in
- gmp)
- SECP_GMP_CHECK
- if test x"$has_gmp" != x"yes"; then
- AC_MSG_ERROR([gmp bignum explicitly requested but libgmp not available])
- fi
- ;;
- no)
- ;;
- *)
- AC_MSG_ERROR([invalid bignum implementation selection])
- ;;
- esac
-fi
-
-# select assembly optimization
+# Select assembly optimization
use_external_asm=no
case $set_asm in
@@ -322,7 +263,12 @@ no)
;;
esac
-# select wide multiplication implementation
+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
+
+
+# Select wide multiplication implementation
case $set_widemul in
int128)
AC_DEFINE(USE_FORCE_WIDEMUL_INT128, 1, [Define this symbol to force the use of the (unsigned) __int128 based wide multiplication implementation])
@@ -337,25 +283,7 @@ auto)
;;
esac
-# select bignum implementation
-case $set_bignum in
-gmp)
- AC_DEFINE(HAVE_LIBGMP, 1, [Define this symbol if libgmp is installed])
- AC_DEFINE(USE_NUM_GMP, 1, [Define this symbol to use the gmp implementation for num])
- AC_DEFINE(USE_FIELD_INV_NUM, 1, [Define this symbol to use the num-based field inverse implementation])
- AC_DEFINE(USE_SCALAR_INV_NUM, 1, [Define this symbol to use the num-based scalar inverse implementation])
- ;;
-no)
- AC_DEFINE(USE_NUM_NONE, 1, [Define this symbol to use no num implementation])
- AC_DEFINE(USE_FIELD_INV_BUILTIN, 1, [Define this symbol to use the native field inverse implementation])
- AC_DEFINE(USE_SCALAR_INV_BUILTIN, 1, [Define this symbol to use the native scalar inverse implementation])
- ;;
-*)
- AC_MSG_ERROR([invalid bignum implementation])
- ;;
-esac
-
-#set ecmult window size
+# Set ecmult window size
if test x"$req_ecmult_window" = x"auto"; then
set_ecmult_window=15
else
@@ -377,7 +305,7 @@ case $set_ecmult_window in
;;
esac
-#set ecmult gen precision
+# Set ecmult gen precision
if test x"$req_ecmult_gen_precision" = x"auto"; then
set_ecmult_gen_precision=4
else
@@ -419,15 +347,93 @@ else
enable_openssl_tests=no
fi
-if test x"$set_bignum" = x"gmp"; then
- SECP_LIBS="$SECP_LIBS $GMP_LIBS"
- SECP_INCLUDES="$SECP_INCLUDES $GMP_CPPFLAGS"
+if test x"$enable_valgrind" = x"yes"; then
+ SECP_INCLUDES="$SECP_INCLUDES $VALGRIND_CPPFLAGS"
+fi
+
+# Handle static precomputation (after everything which modifies CFLAGS and friends)
+if test x"$use_ecmult_static_precomputation" != x"no"; then
+ if test x"$cross_compiling" = x"no"; then
+ set_precomp=yes
+ if test x"${CC_FOR_BUILD+x}${CFLAGS_FOR_BUILD+x}${CPPFLAGS_FOR_BUILD+x}${LDFLAGS_FOR_BUILD+x}" != x; then
+ AC_MSG_WARN([CC_FOR_BUILD, CFLAGS_FOR_BUILD, CPPFLAGS_FOR_BUILD, and/or LDFLAGS_FOR_BUILD is set but ignored because we are not cross-compiling.])
+ fi
+ # If we're not cross-compiling, simply use the same compiler for building the static precompation code.
+ CC_FOR_BUILD="$CC"
+ CFLAGS_FOR_BUILD="$CFLAGS"
+ CPPFLAGS_FOR_BUILD="$CPPFLAGS"
+ LDFLAGS_FOR_BUILD="$LDFLAGS"
+ else
+ AX_PROG_CC_FOR_BUILD
+
+ # Temporarily switch to an environment for the native compiler
+ save_cross_compiling=$cross_compiling
+ cross_compiling=no
+ SAVE_CC="$CC"
+ CC="$CC_FOR_BUILD"
+ SAVE_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS_FOR_BUILD"
+ SAVE_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS_FOR_BUILD"
+ SAVE_LDFLAGS="$LDFLAGS"
+ LDFLAGS="$LDFLAGS_FOR_BUILD"
+
+ warn_CFLAGS_FOR_BUILD="-Wall -Wextra -Wno-unused-function"
+ saved_CFLAGS="$CFLAGS"
+ 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]) ],
+ [ AC_MSG_RESULT([no])
+ CFLAGS="$saved_CFLAGS"
+ ])
+
+ AC_MSG_CHECKING([for working native compiler: ${CC_FOR_BUILD}])
+ AC_RUN_IFELSE(
+ [AC_LANG_PROGRAM([], [])],
+ [working_native_cc=yes],
+ [working_native_cc=no],[:])
+
+ CFLAGS_FOR_BUILD="$CFLAGS"
+
+ # Restore the environment
+ cross_compiling=$save_cross_compiling
+ CC="$SAVE_CC"
+ CFLAGS="$SAVE_CFLAGS"
+ CPPFLAGS="$SAVE_CPPFLAGS"
+ LDFLAGS="$SAVE_LDFLAGS"
+
+ if test x"$working_native_cc" = x"no"; then
+ AC_MSG_RESULT([no])
+ set_precomp=no
+ m4_define([please_set_for_build], [Please set CC_FOR_BUILD, CFLAGS_FOR_BUILD, CPPFLAGS_FOR_BUILD, and/or LDFLAGS_FOR_BUILD.])
+ if test x"$use_ecmult_static_precomputation" = x"yes"; then
+ AC_MSG_ERROR([native compiler ${CC_FOR_BUILD} does not produce working binaries. please_set_for_build])
+ else
+ AC_MSG_WARN([Disabling statically generated ecmult table because the native compiler ${CC_FOR_BUILD} does not produce working binaries. please_set_for_build])
+ fi
+ else
+ AC_MSG_RESULT([yes])
+ set_precomp=yes
+ fi
+ fi
+
+ AC_SUBST(CC_FOR_BUILD)
+ AC_SUBST(CFLAGS_FOR_BUILD)
+ AC_SUBST(CPPFLAGS_FOR_BUILD)
+ AC_SUBST(LDFLAGS_FOR_BUILD)
+else
+ set_precomp=no
fi
if test x"$set_precomp" = x"yes"; then
AC_DEFINE(USE_ECMULT_STATIC_PRECOMPUTATION, 1, [Define this symbol to use a statically generated ecmult table])
fi
+###
+### Handle module options
+###
+
if test x"$enable_module_ecdh" = x"yes"; then
AC_DEFINE(ENABLE_MODULE_ECDH, 1, [Define this symbol to enable the ECDH module])
fi
@@ -447,14 +453,14 @@ if test x"$enable_module_extrakeys" = x"yes"; then
AC_DEFINE(ENABLE_MODULE_EXTRAKEYS, 1, [Define this symbol to enable the extrakeys module])
fi
-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
+###
+### Check for --enable-experimental if necessary
+###
+
if test x"$enable_experimental" = x"yes"; then
AC_MSG_NOTICE([******])
AC_MSG_NOTICE([WARNING: experimental build])
@@ -474,6 +480,10 @@ else
fi
fi
+###
+### Generate output
+###
+
AC_CONFIG_HEADERS([src/libsecp256k1-config.h])
AC_CONFIG_FILES([Makefile libsecp256k1.pc])
AC_SUBST(SECP_INCLUDES)
@@ -492,7 +502,7 @@ AM_CONDITIONAL([ENABLE_MODULE_SCHNORRSIG], [test x"$enable_module_schnorrsig" =
AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"])
AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"])
-dnl make sure nothing new is exported so that we don't break the cache
+# Make sure nothing new is exported so that we don't break the cache.
PKGCONFIG_PATH_TEMP="$PKG_CONFIG_PATH"
unset PKG_CONFIG_PATH
PKG_CONFIG_PATH="$PKGCONFIG_PATH_TEMP"
@@ -513,10 +523,9 @@ echo " module extrakeys = $enable_module_extrakeys"
echo " module schnorrsig = $enable_module_schnorrsig"
echo
echo " asm = $set_asm"
-echo " bignum = $set_bignum"
echo " ecmult window size = $set_ecmult_window"
echo " ecmult gen prec. bits = $set_ecmult_gen_precision"
-dnl Hide test-only options unless they're used.
+# Hide test-only options unless they're used.
if test x"$set_widemul" != xauto; then
echo " wide multiplication = $set_widemul"
fi
@@ -527,3 +536,9 @@ echo " CFLAGS = $CFLAGS"
echo " CPPFLAGS = $CPPFLAGS"
echo " LDFLAGS = $LDFLAGS"
echo
+if test x"$set_precomp" = x"yes"; then
+echo " CC_FOR_BUILD = $CC_FOR_BUILD"
+echo " CFLAGS_FOR_BUILD = $CFLAGS_FOR_BUILD"
+echo " CPPFLAGS_FOR_BUILD = $CPPFLAGS_FOR_BUILD"
+echo " LDFLAGS_FOR_BUILD = $LDFLAGS_FOR_BUILD"
+fi
diff --git a/src/secp256k1/contrib/lax_der_parsing.c b/src/secp256k1/contrib/lax_der_parsing.c
index f71db4b535..c1627e37e9 100644
--- a/src/secp256k1/contrib/lax_der_parsing.c
+++ b/src/secp256k1/contrib/lax_der_parsing.c
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2015 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2015 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#include <string.h>
#include <secp256k1.h>
diff --git a/src/secp256k1/contrib/lax_der_parsing.h b/src/secp256k1/contrib/lax_der_parsing.h
index 7eaf63bf6a..6b7255e28f 100644
--- a/src/secp256k1/contrib/lax_der_parsing.h
+++ b/src/secp256k1/contrib/lax_der_parsing.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2015 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2015 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
/****
* Please do not link this file directly. It is not part of the libsecp256k1
diff --git a/src/secp256k1/contrib/lax_der_privatekey_parsing.c b/src/secp256k1/contrib/lax_der_privatekey_parsing.c
index c2e63b4b8d..429760fbb6 100644
--- a/src/secp256k1/contrib/lax_der_privatekey_parsing.c
+++ b/src/secp256k1/contrib/lax_der_privatekey_parsing.c
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2014, 2015 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2014, 2015 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#include <string.h>
#include <secp256k1.h>
diff --git a/src/secp256k1/contrib/lax_der_privatekey_parsing.h b/src/secp256k1/contrib/lax_der_privatekey_parsing.h
index fece261fb9..602c7c556a 100644
--- a/src/secp256k1/contrib/lax_der_privatekey_parsing.h
+++ b/src/secp256k1/contrib/lax_der_privatekey_parsing.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2014, 2015 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2014, 2015 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
/****
* Please do not link this file directly. It is not part of the libsecp256k1
diff --git a/src/secp256k1/doc/safegcd_implementation.md b/src/secp256k1/doc/safegcd_implementation.md
new file mode 100644
index 0000000000..3ae556f9a7
--- /dev/null
+++ b/src/secp256k1/doc/safegcd_implementation.md
@@ -0,0 +1,765 @@
+# The safegcd implementation in libsecp256k1 explained
+
+This document explains the modular inverse implementation in the `src/modinv*.h` files. It is based
+on the paper
+["Fast constant-time gcd computation and modular inversion"](https://gcd.cr.yp.to/papers.html#safegcd)
+by Daniel J. Bernstein and Bo-Yin Yang. The references below are for the Date: 2019.04.13 version.
+
+The actual implementation is in C of course, but for demonstration purposes Python3 is used here.
+Most implementation aspects and optimizations are explained, except those that depend on the specific
+number representation used in the C code.
+
+## 1. Computing the Greatest Common Divisor (GCD) using divsteps
+
+The algorithm from the paper (section 11), at a very high level, is this:
+
+```python
+def gcd(f, g):
+ """Compute the GCD of an odd integer f and another integer g."""
+ assert f & 1 # require f to be odd
+ delta = 1 # additional state variable
+ while g != 0:
+ assert f & 1 # f will be odd in every iteration
+ if delta > 0 and g & 1:
+ delta, f, g = 1 - delta, g, (g - f) // 2
+ elif g & 1:
+ delta, f, g = 1 + delta, f, (g + f) // 2
+ else:
+ delta, f, g = 1 + delta, f, (g ) // 2
+ return abs(f)
+```
+
+It computes the greatest common divisor of an odd integer *f* and any integer *g*. Its inner loop
+keeps rewriting the variables *f* and *g* alongside a state variable *&delta;* that starts at *1*, until
+*g=0* is reached. At that point, *|f|* gives the GCD. Each of the transitions in the loop is called a
+"division step" (referred to as divstep in what follows).
+
+For example, *gcd(21, 14)* would be computed as:
+- Start with *&delta;=1 f=21 g=14*
+- Take the third branch: *&delta;=2 f=21 g=7*
+- Take the first branch: *&delta;=-1 f=7 g=-7*
+- Take the second branch: *&delta;=0 f=7 g=0*
+- The answer *|f| = 7*.
+
+Why it works:
+- Divsteps can be decomposed into two steps (see paragraph 8.2 in the paper):
+ - (a) If *g* is odd, replace *(f,g)* with *(g,g-f)* or (f,g+f), resulting in an even *g*.
+ - (b) Replace *(f,g)* with *(f,g/2)* (where *g* is guaranteed to be even).
+- Neither of those two operations change the GCD:
+ - For (a), assume *gcd(f,g)=c*, then it must be the case that *f=a&thinsp;c* and *g=b&thinsp;c* for some integers *a*
+ and *b*. As *(g,g-f)=(b&thinsp;c,(b-a)c)* and *(f,f+g)=(a&thinsp;c,(a+b)c)*, the result clearly still has
+ common factor *c*. Reasoning in the other direction shows that no common factor can be added by
+ doing so either.
+ - For (b), we know that *f* is odd, so *gcd(f,g)* clearly has no factor *2*, and we can remove
+ it from *g*.
+- The algorithm will eventually converge to *g=0*. This is proven in the paper (see theorem G.3).
+- It follows that eventually we find a final value *f'* for which *gcd(f,g) = gcd(f',0)*. As the
+ gcd of *f'* and *0* is *|f'|* by definition, that is our answer.
+
+Compared to more [traditional GCD algorithms](https://en.wikipedia.org/wiki/Euclidean_algorithm), this one has the property of only ever looking at
+the low-order bits of the variables to decide the next steps, and being easy to make
+constant-time (in more low-level languages than Python). The *&delta;* parameter is necessary to
+guide the algorithm towards shrinking the numbers' magnitudes without explicitly needing to look
+at high order bits.
+
+Properties that will become important later:
+- Performing more divsteps than needed is not a problem, as *f* does not change anymore after *g=0*.
+- Only even numbers are divided by *2*. This means that when reasoning about it algebraically we
+ do not need to worry about rounding.
+- At every point during the algorithm's execution the next *N* steps only depend on the bottom *N*
+ bits of *f* and *g*, and on *&delta;*.
+
+
+## 2. From GCDs to modular inverses
+
+We want an algorithm to compute the inverse *a* of *x* modulo *M*, i.e. the number a such that *a&thinsp;x=1
+mod M*. This inverse only exists if the GCD of *x* and *M* is *1*, but that is always the case if *M* is
+prime and *0 < x < M*. In what follows, assume that the modular inverse exists.
+It turns out this inverse can be computed as a side effect of computing the GCD by keeping track
+of how the internal variables can be written as linear combinations of the inputs at every step
+(see the [extended Euclidean algorithm](https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm)).
+Since the GCD is *1*, such an algorithm will compute numbers *a* and *b* such that a&thinsp;x + b&thinsp;M = 1*.
+Taking that expression *mod M* gives *a&thinsp;x mod M = 1*, and we see that *a* is the modular inverse of *x
+mod M*.
+
+A similar approach can be used to calculate modular inverses using the divsteps-based GCD
+algorithm shown above, if the modulus *M* is odd. To do so, compute *gcd(f=M,g=x)*, while keeping
+track of extra variables *d* and *e*, for which at every step *d = f/x (mod M)* and *e = g/x (mod M)*.
+*f/x* here means the number which multiplied with *x* gives *f mod M*. As *f* and *g* are initialized to *M*
+and *x* respectively, *d* and *e* just start off being *0* (*M/x mod M = 0/x mod M = 0*) and *1* (*x/x mod M
+= 1*).
+
+```python
+def div2(M, x):
+ """Helper routine to compute x/2 mod M (where M is odd)."""
+ assert M & 1
+ if x & 1: # If x is odd, make it even by adding M.
+ x += M
+ # x must be even now, so a clean division by 2 is possible.
+ return x // 2
+
+def modinv(M, x):
+ """Compute the inverse of x mod M (given that it exists, and M is odd)."""
+ assert M & 1
+ delta, f, g, d, e = 1, M, x, 0, 1
+ while g != 0:
+ # Note that while division by two for f and g is only ever done on even inputs, this is
+ # not true for d and e, so we need the div2 helper function.
+ if delta > 0 and g & 1:
+ delta, f, g, d, e = 1 - delta, g, (g - f) // 2, e, div2(M, e - d)
+ elif g & 1:
+ delta, f, g, d, e = 1 + delta, f, (g + f) // 2, d, div2(M, e + d)
+ else:
+ delta, f, g, d, e = 1 + delta, f, (g ) // 2, d, div2(M, e )
+ # Verify that the invariants d=f/x mod M, e=g/x mod M are maintained.
+ assert f % M == (d * x) % M
+ assert g % M == (e * x) % M
+ assert f == 1 or f == -1 # |f| is the GCD, it must be 1
+ # Because of invariant d = f/x (mod M), 1/x = d/f (mod M). As |f|=1, d/f = d*f.
+ return (d * f) % M
+```
+
+Also note that this approach to track *d* and *e* throughout the computation to determine the inverse
+is different from the paper. There (see paragraph 12.1 in the paper) a transition matrix for the
+entire computation is determined (see section 3 below) and the inverse is computed from that.
+The approach here avoids the need for 2x2 matrix multiplications of various sizes, and appears to
+be faster at the level of optimization we're able to do in C.
+
+
+## 3. Batching multiple divsteps
+
+Every divstep can be expressed as a matrix multiplication, applying a transition matrix *(1/2 t)*
+to both vectors *[f, g]* and *[d, e]* (see paragraph 8.1 in the paper):
+
+```
+ t = [ u, v ]
+ [ q, r ]
+
+ [ out_f ] = (1/2 * t) * [ in_f ]
+ [ out_g ] = [ in_g ]
+
+ [ out_d ] = (1/2 * t) * [ in_d ] (mod M)
+ [ out_e ] [ in_e ]
+```
+
+where *(u, v, q, r)* is *(0, 2, -1, 1)*, *(2, 0, 1, 1)*, or *(2, 0, 0, 1)*, depending on which branch is
+taken. As above, the resulting *f* and *g* are always integers.
+
+Performing multiple divsteps corresponds to a multiplication with the product of all the
+individual divsteps' transition matrices. As each transition matrix consists of integers
+divided by *2*, the product of these matrices will consist of integers divided by *2<sup>N</sup>* (see also
+theorem 9.2 in the paper). These divisions are expensive when updating *d* and *e*, so we delay
+them: we compute the integer coefficients of the combined transition matrix scaled by *2<sup>N</sup>*, and
+do one division by *2<sup>N</sup>* as a final step:
+
+```python
+def divsteps_n_matrix(delta, f, g):
+ """Compute delta and transition matrix t after N divsteps (multiplied by 2^N)."""
+ u, v, q, r = 1, 0, 0, 1 # start with identity matrix
+ for _ in range(N):
+ if delta > 0 and g & 1:
+ delta, f, g, u, v, q, r = 1 - delta, g, (g - f) // 2, 2*q, 2*r, q-u, r-v
+ elif g & 1:
+ delta, f, g, u, v, q, r = 1 + delta, f, (g + f) // 2, 2*u, 2*v, q+u, r+v
+ else:
+ delta, f, g, u, v, q, r = 1 + delta, f, (g ) // 2, 2*u, 2*v, q , r
+ return delta, (u, v, q, r)
+```
+
+As the branches in the divsteps are completely determined by the bottom *N* bits of *f* and *g*, this
+function to compute the transition matrix only needs to see those bottom bits. Furthermore all
+intermediate results and outputs fit in *(N+1)*-bit numbers (unsigned for *f* and *g*; signed for *u*, *v*,
+*q*, and *r*) (see also paragraph 8.3 in the paper). This means that an implementation using 64-bit
+integers could set *N=62* and compute the full transition matrix for 62 steps at once without any
+big integer arithmetic at all. This is the reason why this algorithm is efficient: it only needs
+to update the full-size *f*, *g*, *d*, and *e* numbers once every *N* steps.
+
+We still need functions to compute:
+
+```
+ [ out_f ] = (1/2^N * [ u, v ]) * [ in_f ]
+ [ out_g ] ( [ q, r ]) [ in_g ]
+
+ [ out_d ] = (1/2^N * [ u, v ]) * [ in_d ] (mod M)
+ [ out_e ] ( [ q, r ]) [ in_e ]
+```
+
+Because the divsteps transformation only ever divides even numbers by two, the result of *t&thinsp;[f,g]* is always even. When *t* is a composition of *N* divsteps, it follows that the resulting *f*
+and *g* will be multiple of *2<sup>N</sup>*, and division by *2<sup>N</sup>* is simply shifting them down:
+
+```python
+def update_fg(f, g, t):
+ """Multiply matrix t/2^N with [f, g]."""
+ u, v, q, r = t
+ cf, cg = u*f + v*g, q*f + r*g
+ # (t / 2^N) should cleanly apply to [f,g] so the result of t*[f,g] should have N zero
+ # bottom bits.
+ assert cf % 2**N == 0
+ assert cg % 2**N == 0
+ return cf >> N, cg >> N
+```
+
+The same is not true for *d* and *e*, and we need an equivalent of the `div2` function for division by *2<sup>N</sup> mod M*.
+This is easy if we have precomputed *1/M mod 2<sup>N</sup>* (which always exists for odd *M*):
+
+```python
+def div2n(M, Mi, x):
+ """Compute x/2^N mod M, given Mi = 1/M mod 2^N."""
+ assert (M * Mi) % 2**N == 1
+ # Find a factor m such that m*M has the same bottom N bits as x. We want:
+ # (m * M) mod 2^N = x mod 2^N
+ # <=> m mod 2^N = (x / M) mod 2^N
+ # <=> m mod 2^N = (x * Mi) mod 2^N
+ m = (Mi * x) % 2**N
+ # Subtract that multiple from x, cancelling its bottom N bits.
+ x -= m * M
+ # Now a clean division by 2^N is possible.
+ assert x % 2**N == 0
+ return (x >> N) % M
+
+def update_de(d, e, t, M, Mi):
+ """Multiply matrix t/2^N with [d, e], modulo M."""
+ u, v, q, r = t
+ cd, ce = u*d + v*e, q*d + r*e
+ return div2n(M, Mi, cd), div2n(M, Mi, ce)
+```
+
+With all of those, we can write a version of `modinv` that performs *N* divsteps at once:
+
+```python3
+def modinv(M, Mi, x):
+ """Compute the modular inverse of x mod M, given Mi=1/M mod 2^N."""
+ assert M & 1
+ delta, f, g, d, e = 1, M, x, 0, 1
+ while g != 0:
+ # Compute the delta and transition matrix t for the next N divsteps (this only needs
+ # (N+1)-bit signed integer arithmetic).
+ delta, t = divsteps_n_matrix(delta, f % 2**N, g % 2**N)
+ # Apply the transition matrix t to [f, g]:
+ f, g = update_fg(f, g, t)
+ # Apply the transition matrix t to [d, e]:
+ d, e = update_de(d, e, t, M, Mi)
+ return (d * f) % M
+```
+
+This means that in practice we'll always perform a multiple of *N* divsteps. This is not a problem
+because once *g=0*, further divsteps do not affect *f*, *g*, *d*, or *e* anymore (only *&delta;* keeps
+increasing). For variable time code such excess iterations will be mostly optimized away in later
+sections.
+
+
+## 4. Avoiding modulus operations
+
+So far, there are two places where we compute a remainder of big numbers modulo *M*: at the end of
+`div2n` in every `update_de`, and at the very end of `modinv` after potentially negating *d* due to the
+sign of *f*. These are relatively expensive operations when done generically.
+
+To deal with the modulus operation in `div2n`, we simply stop requiring *d* and *e* to be in range
+*[0,M)* all the time. Let's start by inlining `div2n` into `update_de`, and dropping the modulus
+operation at the end:
+
+```python
+def update_de(d, e, t, M, Mi):
+ """Multiply matrix t/2^N with [d, e] mod M, given Mi=1/M mod 2^N."""
+ u, v, q, r = t
+ cd, ce = u*d + v*e, q*d + r*e
+ # Cancel out bottom N bits of cd and ce.
+ md = -((Mi * cd) % 2**N)
+ me = -((Mi * ce) % 2**N)
+ cd += md * M
+ ce += me * M
+ # And cleanly divide by 2**N.
+ return cd >> N, ce >> N
+```
+
+Let's look at bounds on the ranges of these numbers. It can be shown that *|u|+|v|* and *|q|+|r|*
+never exceed *2<sup>N</sup>* (see paragraph 8.3 in the paper), and thus a multiplication with *t* will have
+outputs whose absolute values are at most *2<sup>N</sup>* times the maximum absolute input value. In case the
+inputs *d* and *e* are in *(-M,M)*, which is certainly true for the initial values *d=0* and *e=1* assuming
+*M > 1*, the multiplication results in numbers in range *(-2<sup>N</sup>M,2<sup>N</sup>M)*. Subtracting less than *2<sup>N</sup>*
+times *M* to cancel out *N* bits brings that up to *(-2<sup>N+1</sup>M,2<sup>N</sup>M)*, and
+dividing by *2<sup>N</sup>* at the end takes it to *(-2M,M)*. Another application of `update_de` would take that
+to *(-3M,2M)*, and so forth. This progressive expansion of the variables' ranges can be
+counteracted by incrementing *d* and *e* by *M* whenever they're negative:
+
+```python
+ ...
+ if d < 0:
+ d += M
+ if e < 0:
+ e += M
+ cd, ce = u*d + v*e, q*d + r*e
+ # Cancel out bottom N bits of cd and ce.
+ ...
+```
+
+With inputs in *(-2M,M)*, they will first be shifted into range *(-M,M)*, which means that the
+output will again be in *(-2M,M)*, and this remains the case regardless of how many `update_de`
+invocations there are. In what follows, we will try to make this more efficient.
+
+Note that increasing *d* by *M* is equal to incrementing *cd* by *u&thinsp;M* and *ce* by *q&thinsp;M*. Similarly,
+increasing *e* by *M* is equal to incrementing *cd* by *v&thinsp;M* and *ce* by *r&thinsp;M*. So we could instead write:
+
+```python
+ ...
+ cd, ce = u*d + v*e, q*d + r*e
+ # Perform the equivalent of incrementing d, e by M when they're negative.
+ if d < 0:
+ cd += u*M
+ ce += q*M
+ if e < 0:
+ cd += v*M
+ ce += r*M
+ # Cancel out bottom N bits of cd and ce.
+ md = -((Mi * cd) % 2**N)
+ me = -((Mi * ce) % 2**N)
+ cd += md * M
+ ce += me * M
+ ...
+```
+
+Now note that we have two steps of corrections to *cd* and *ce* that add multiples of *M*: this
+increment, and the decrement that cancels out bottom bits. The second one depends on the first
+one, but they can still be efficiently combined by only computing the bottom bits of *cd* and *ce*
+at first, and using that to compute the final *md*, *me* values:
+
+```python
+def update_de(d, e, t, M, Mi):
+ """Multiply matrix t/2^N with [d, e], modulo M."""
+ u, v, q, r = t
+ md, me = 0, 0
+ # Compute what multiples of M to add to cd and ce.
+ if d < 0:
+ md += u
+ me += q
+ if e < 0:
+ md += v
+ me += r
+ # Compute bottom N bits of t*[d,e] + M*[md,me].
+ cd, ce = (u*d + v*e + md*M) % 2**N, (q*d + r*e + me*M) % 2**N
+ # Correct md and me such that the bottom N bits of t*[d,e] + M*[md,me] are zero.
+ md -= (Mi * cd) % 2**N
+ me -= (Mi * ce) % 2**N
+ # Do the full computation.
+ cd, ce = u*d + v*e + md*M, q*d + r*e + me*M
+ # And cleanly divide by 2**N.
+ return cd >> N, ce >> N
+```
+
+One last optimization: we can avoid the *md&thinsp;M* and *me&thinsp;M* multiplications in the bottom bits of *cd*
+and *ce* by moving them to the *md* and *me* correction:
+
+```python
+ ...
+ # Compute bottom N bits of t*[d,e].
+ cd, ce = (u*d + v*e) % 2**N, (q*d + r*e) % 2**N
+ # Correct md and me such that the bottom N bits of t*[d,e]+M*[md,me] are zero.
+ # Note that this is not the same as {md = (-Mi * cd) % 2**N} etc. That would also result in N
+ # zero bottom bits, but isn't guaranteed to be a reduction of [0,2^N) compared to the
+ # previous md and me values, and thus would violate our bounds analysis.
+ md -= (Mi*cd + md) % 2**N
+ me -= (Mi*ce + me) % 2**N
+ ...
+```
+
+The resulting function takes *d* and *e* in range *(-2M,M)* as inputs, and outputs values in the same
+range. That also means that the *d* value at the end of `modinv` will be in that range, while we want
+a result in *[0,M)*. To do that, we need a normalization function. It's easy to integrate the
+conditional negation of *d* (based on the sign of *f*) into it as well:
+
+```python
+def normalize(sign, v, M):
+ """Compute sign*v mod M, where v is in range (-2*M,M); output in [0,M)."""
+ assert sign == 1 or sign == -1
+ # v in (-2*M,M)
+ if v < 0:
+ v += M
+ # v in (-M,M). Now multiply v with sign (which can only be 1 or -1).
+ if sign == -1:
+ v = -v
+ # v in (-M,M)
+ if v < 0:
+ v += M
+ # v in [0,M)
+ return v
+```
+
+And calling it in `modinv` is simply:
+
+```python
+ ...
+ return normalize(f, d, M)
+```
+
+
+## 5. Constant-time operation
+
+The primary selling point of the algorithm is fast constant-time operation. What code flow still
+depends on the input data so far?
+
+- the number of iterations of the while *g &ne; 0* loop in `modinv`
+- the branches inside `divsteps_n_matrix`
+- the sign checks in `update_de`
+- the sign checks in `normalize`
+
+To make the while loop in `modinv` constant time it can be replaced with a constant number of
+iterations. The paper proves (Theorem 11.2) that *741* divsteps are sufficient for any *256*-bit
+inputs, and [safegcd-bounds](https://github.com/sipa/safegcd-bounds) shows that the slightly better bound *724* is
+sufficient even. Given that every loop iteration performs *N* divsteps, it will run a total of
+*&lceil;724/N&rceil;* times.
+
+To deal with the branches in `divsteps_n_matrix` we will replace them with constant-time bitwise
+operations (and hope the C compiler isn't smart enough to turn them back into branches; see
+`valgrind_ctime_test.c` for automated tests that this isn't the case). To do so, observe that a
+divstep can be written instead as (compare to the inner loop of `gcd` in section 1).
+
+```python
+ x = -f if delta > 0 else f # set x equal to (input) -f or f
+ if g & 1:
+ g += x # set g to (input) g-f or g+f
+ if delta > 0:
+ delta = -delta
+ f += g # set f to (input) g (note that g was set to g-f before)
+ delta += 1
+ g >>= 1
+```
+
+To convert the above to bitwise operations, we rely on a trick to negate conditionally: per the
+definition of negative numbers in two's complement, (*-v == ~v + 1*) holds for every number *v*. As
+*-1* in two's complement is all *1* bits, bitflipping can be expressed as xor with *-1*. It follows
+that *-v == (v ^ -1) - (-1)*. Thus, if we have a variable *c* that takes on values *0* or *-1*, then
+*(v ^ c) - c* is *v* if *c=0* and *-v* if *c=-1*.
+
+Using this we can write:
+
+```python
+ x = -f if delta > 0 else f
+```
+
+in constant-time form as:
+
+```python
+ c1 = (-delta) >> 63
+ # Conditionally negate f based on c1:
+ x = (f ^ c1) - c1
+```
+
+To use that trick, we need a helper mask variable *c1* that resolves the condition *&delta;>0* to *-1*
+(if true) or *0* (if false). We compute *c1* using right shifting, which is equivalent to dividing by
+the specified power of *2* and rounding down (in Python, and also in C under the assumption of a typical two's complement system; see
+`assumptions.h` for tests that this is the case). Right shifting by *63* thus maps all
+numbers in range *[-2<sup>63</sup>,0)* to *-1*, and numbers in range *[0,2<sup>63</sup>)* to *0*.
+
+Using the facts that *x&0=0* and *x&(-1)=x* (on two's complement systems again), we can write:
+
+```python
+ if g & 1:
+ g += x
+```
+
+as:
+
+```python
+ # Compute c2=0 if g is even and c2=-1 if g is odd.
+ c2 = -(g & 1)
+ # This masks out x if g is even, and leaves x be if g is odd.
+ g += x & c2
+```
+
+Using the conditional negation trick again we can write:
+
+```python
+ if g & 1:
+ if delta > 0:
+ delta = -delta
+```
+
+as:
+
+```python
+ # Compute c3=-1 if g is odd and delta>0, and 0 otherwise.
+ c3 = c1 & c2
+ # Conditionally negate delta based on c3:
+ delta = (delta ^ c3) - c3
+```
+
+Finally:
+
+```python
+ if g & 1:
+ if delta > 0:
+ f += g
+```
+
+becomes:
+
+```python
+ f += g & c3
+```
+
+It turns out that this can be implemented more efficiently by applying the substitution
+*&eta;=-&delta;*. In this representation, negating *&delta;* corresponds to negating *&eta;*, and incrementing
+*&delta;* corresponds to decrementing *&eta;*. This allows us to remove the negation in the *c1*
+computation:
+
+```python
+ # Compute a mask c1 for eta < 0, and compute the conditional negation x of f:
+ c1 = eta >> 63
+ x = (f ^ c1) - c1
+ # Compute a mask c2 for odd g, and conditionally add x to g:
+ c2 = -(g & 1)
+ g += x & c2
+ # Compute a mask c for (eta < 0) and odd (input) g, and use it to conditionally negate eta,
+ # and add g to f:
+ c3 = c1 & c2
+ eta = (eta ^ c3) - c3
+ f += g & c3
+ # Incrementing delta corresponds to decrementing eta.
+ eta -= 1
+ g >>= 1
+```
+
+A variant of divsteps with better worst-case performance can be used instead: starting *&delta;* at
+*1/2* instead of *1*. This reduces the worst case number of iterations to *590* for *256*-bit inputs
+(which can be shown using convex hull analysis). In this case, the substitution *&zeta;=-(&delta;+1/2)*
+is used instead to keep the variable integral. Incrementing *&delta;* by *1* still translates to
+decrementing *&zeta;* by *1*, but negating *&delta;* now corresponds to going from *&zeta;* to *-(&zeta;+1)*, or
+*~&zeta;*. Doing that conditionally based on *c3* is simply:
+
+```python
+ ...
+ c3 = c1 & c2
+ zeta ^= c3
+ ...
+```
+
+By replacing the loop in `divsteps_n_matrix` with a variant of the divstep code above (extended to
+also apply all *f* operations to *u*, *v* and all *g* operations to *q*, *r*), a constant-time version of
+`divsteps_n_matrix` is obtained. The full code will be in section 7.
+
+These bit fiddling tricks can also be used to make the conditional negations and additions in
+`update_de` and `normalize` constant-time.
+
+
+## 6. Variable-time optimizations
+
+In section 5, we modified the `divsteps_n_matrix` function (and a few others) to be constant time.
+Constant time operations are only necessary when computing modular inverses of secret data. In
+other cases, it slows down calculations unnecessarily. In this section, we will construct a
+faster non-constant time `divsteps_n_matrix` function.
+
+To do so, first consider yet another way of writing the inner loop of divstep operations in
+`gcd` from section 1. This decomposition is also explained in the paper in section 8.2. We use
+the original version with initial *&delta;=1* and *&eta;=-&delta;* here.
+
+```python
+for _ in range(N):
+ if g & 1 and eta < 0:
+ eta, f, g = -eta, g, -f
+ if g & 1:
+ g += f
+ eta -= 1
+ g >>= 1
+```
+
+Whenever *g* is even, the loop only shifts *g* down and decreases *&eta;*. When *g* ends in multiple zero
+bits, these iterations can be consolidated into one step. This requires counting the bottom zero
+bits efficiently, which is possible on most platforms; it is abstracted here as the function
+`count_trailing_zeros`.
+
+```python
+def count_trailing_zeros(v):
+ """For a non-zero value v, find z such that v=(d<<z) for some odd d."""
+ return (v & -v).bit_length() - 1
+
+i = N # divsteps left to do
+while True:
+ # Get rid of all bottom zeros at once. In the first iteration, g may be odd and the following
+ # lines have no effect (until "if eta < 0").
+ zeros = min(i, count_trailing_zeros(g))
+ eta -= zeros
+ g >>= zeros
+ i -= zeros
+ if i == 0:
+ break
+ # We know g is odd now
+ if eta < 0:
+ eta, f, g = -eta, g, -f
+ g += f
+ # g is even now, and the eta decrement and g shift will happen in the next loop.
+```
+
+We can now remove multiple bottom *0* bits from *g* at once, but still need a full iteration whenever
+there is a bottom *1* bit. In what follows, we will get rid of multiple *1* bits simultaneously as
+well.
+
+Observe that as long as *&eta; &geq; 0*, the loop does not modify *f*. Instead, it cancels out bottom
+bits of *g* and shifts them out, and decreases *&eta;* and *i* accordingly - interrupting only when *&eta;*
+becomes negative, or when *i* reaches *0*. Combined, this is equivalent to adding a multiple of *f* to
+*g* to cancel out multiple bottom bits, and then shifting them out.
+
+It is easy to find what that multiple is: we want a number *w* such that *g+w&thinsp;f* has a few bottom
+zero bits. If that number of bits is *L*, we want *g+w&thinsp;f mod 2<sup>L</sup> = 0*, or *w = -g/f mod 2<sup>L</sup>*. Since *f*
+is odd, such a *w* exists for any *L*. *L* cannot be more than *i* steps (as we'd finish the loop before
+doing more) or more than *&eta;+1* steps (as we'd run `eta, f, g = -eta, g, f` at that point), but
+apart from that, we're only limited by the complexity of computing *w*.
+
+This code demonstrates how to cancel up to 4 bits per step:
+
+```python
+NEGINV16 = [15, 5, 3, 9, 7, 13, 11, 1] # NEGINV16[n//2] = (-n)^-1 mod 16, for odd n
+i = N
+while True:
+ zeros = min(i, count_trailing_zeros(g))
+ eta -= zeros
+ g >>= zeros
+ i -= zeros
+ if i == 0:
+ break
+ # We know g is odd now
+ if eta < 0:
+ eta, f, g = -eta, g, f
+ # Compute limit on number of bits to cancel
+ limit = min(min(eta + 1, i), 4)
+ # Compute w = -g/f mod 2**limit, using the table value for -1/f mod 2**4. Note that f is
+ # always odd, so its inverse modulo a power of two always exists.
+ w = (g * NEGINV16[(f & 15) // 2]) % (2**limit)
+ # As w = -g/f mod (2**limit), g+w*f mod 2**limit = 0 mod 2**limit.
+ g += w * f
+ assert g % (2**limit) == 0
+ # The next iteration will now shift out at least limit bottom zero bits from g.
+```
+
+By using a bigger table more bits can be cancelled at once. The table can also be implemented
+as a formula. Several formulas are known for computing modular inverses modulo powers of two;
+some can be found in Hacker's Delight second edition by Henry S. Warren, Jr. pages 245-247.
+Here we need the negated modular inverse, which is a simple transformation of those:
+
+- Instead of a 3-bit table:
+ - *-f* or *f ^ 6*
+- Instead of a 4-bit table:
+ - *1 - f(f + 1)*
+ - *-(f + (((f + 1) & 4) << 1))*
+- For larger tables the following technique can be used: if *w=-1/f mod 2<sup>L</sup>*, then *w(w&thinsp;f+2)* is
+ *-1/f mod 2<sup>2L</sup>*. This allows extending the previous formulas (or tables). In particular we
+ have this 6-bit function (based on the 3-bit function above):
+ - *f(f<sup>2</sup> - 2)*
+
+This loop, again extended to also handle *u*, *v*, *q*, and *r* alongside *f* and *g*, placed in
+`divsteps_n_matrix`, gives a significantly faster, but non-constant time version.
+
+
+## 7. Final Python version
+
+All together we need the following functions:
+
+- A way to compute the transition matrix in constant time, using the `divsteps_n_matrix` function
+ from section 2, but with its loop replaced by a variant of the constant-time divstep from
+ section 5, extended to handle *u*, *v*, *q*, *r*:
+
+```python
+def divsteps_n_matrix(zeta, f, g):
+ """Compute zeta and transition matrix t after N divsteps (multiplied by 2^N)."""
+ u, v, q, r = 1, 0, 0, 1 # start with identity matrix
+ for _ in range(N):
+ c1 = zeta >> 63
+ # Compute x, y, z as conditionally-negated versions of f, u, v.
+ x, y, z = (f ^ c1) - c1, (u ^ c1) - c1, (v ^ c1) - c1
+ c2 = -(g & 1)
+ # Conditionally add x, y, z to g, q, r.
+ g, q, r = g + (x & c2), q + (y & c2), r + (z & c2)
+ c1 &= c2 # reusing c1 here for the earlier c3 variable
+ zeta = (zeta ^ c1) - 1 # inlining the unconditional zeta decrement here
+ # Conditionally add g, q, r to f, u, v.
+ f, u, v = f + (g & c1), u + (q & c1), v + (r & c1)
+ # When shifting g down, don't shift q, r, as we construct a transition matrix multiplied
+ # by 2^N. Instead, shift f's coefficients u and v up.
+ g, u, v = g >> 1, u << 1, v << 1
+ return zeta, (u, v, q, r)
+```
+
+- The functions to update *f* and *g*, and *d* and *e*, from section 2 and section 4, with the constant-time
+ changes to `update_de` from section 5:
+
+```python
+def update_fg(f, g, t):
+ """Multiply matrix t/2^N with [f, g]."""
+ u, v, q, r = t
+ cf, cg = u*f + v*g, q*f + r*g
+ return cf >> N, cg >> N
+
+def update_de(d, e, t, M, Mi):
+ """Multiply matrix t/2^N with [d, e], modulo M."""
+ u, v, q, r = t
+ d_sign, e_sign = d >> 257, e >> 257
+ md, me = (u & d_sign) + (v & e_sign), (q & d_sign) + (r & e_sign)
+ cd, ce = (u*d + v*e) % 2**N, (q*d + r*e) % 2**N
+ md -= (Mi*cd + md) % 2**N
+ me -= (Mi*ce + me) % 2**N
+ cd, ce = u*d + v*e + M*md, q*d + r*e + M*me
+ return cd >> N, ce >> N
+```
+
+- The `normalize` function from section 4, made constant time as well:
+
+```python
+def normalize(sign, v, M):
+ """Compute sign*v mod M, where v in (-2*M,M); output in [0,M)."""
+ v_sign = v >> 257
+ # Conditionally add M to v.
+ v += M & v_sign
+ c = (sign - 1) >> 1
+ # Conditionally negate v.
+ v = (v ^ c) - c
+ v_sign = v >> 257
+ # Conditionally add M to v again.
+ v += M & v_sign
+ return v
+```
+
+- And finally the `modinv` function too, adapted to use *&zeta;* instead of *&delta;*, and using the fixed
+ iteration count from section 5:
+
+```python
+def modinv(M, Mi, x):
+ """Compute the modular inverse of x mod M, given Mi=1/M mod 2^N."""
+ zeta, f, g, d, e = -1, M, x, 0, 1
+ for _ in range((590 + N - 1) // N):
+ zeta, t = divsteps_n_matrix(zeta, f % 2**N, g % 2**N)
+ f, g = update_fg(f, g, t)
+ d, e = update_de(d, e, t, M, Mi)
+ return normalize(f, d, M)
+```
+
+- To get a variable time version, replace the `divsteps_n_matrix` function with one that uses the
+ divsteps loop from section 5, and a `modinv` version that calls it without the fixed iteration
+ count:
+
+```python
+NEGINV16 = [15, 5, 3, 9, 7, 13, 11, 1] # NEGINV16[n//2] = (-n)^-1 mod 16, for odd n
+def divsteps_n_matrix_var(eta, f, g):
+ """Compute eta and transition matrix t after N divsteps (multiplied by 2^N)."""
+ u, v, q, r = 1, 0, 0, 1
+ i = N
+ while True:
+ zeros = min(i, count_trailing_zeros(g))
+ eta, i = eta - zeros, i - zeros
+ g, u, v = g >> zeros, u << zeros, v << zeros
+ if i == 0:
+ break
+ if eta < 0:
+ eta, f, u, v, g, q, r = -eta, g, q, r, -f, -u, -v
+ limit = min(min(eta + 1, i), 4)
+ w = (g * NEGINV16[(f & 15) // 2]) % (2**limit)
+ g, q, r = g + w*f, q + w*u, r + w*v
+ return eta, (u, v, q, r)
+
+def modinv_var(M, Mi, x):
+ """Compute the modular inverse of x mod M, given Mi = 1/M mod 2^N."""
+ eta, f, g, d, e = -1, M, x, 0, 1
+ while g != 0:
+ eta, t = divsteps_n_matrix_var(eta, f % 2**N, g % 2**N)
+ f, g = update_fg(f, g, t)
+ d, e = update_de(d, e, t, M, Mi)
+ return normalize(f, d, Mi)
+```
diff --git a/src/secp256k1/include/secp256k1.h b/src/secp256k1/include/secp256k1.h
index 2178c8e2d6..d368488af2 100644
--- a/src/secp256k1/include/secp256k1.h
+++ b/src/secp256k1/include/secp256k1.h
@@ -11,7 +11,7 @@ extern "C" {
*
* 1. Context pointers go first, followed by output arguments, combined
* output/input arguments, and finally input-only arguments.
- * 2. Array lengths always immediately the follow the argument whose length
+ * 2. Array lengths always immediately 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, secret nonces,
@@ -452,7 +452,14 @@ SECP256K1_API int secp256k1_ecdsa_signature_serialize_compact(
* 0: incorrect or unparseable signature
* Args: ctx: a secp256k1 context object, initialized for verification.
* In: sig: the signature being verified (cannot be NULL)
- * msg32: the 32-byte message hash being verified (cannot be NULL)
+ * msghash32: the 32-byte message hash being verified (cannot be NULL).
+ * The verifier must make sure to apply a cryptographic
+ * hash function to the message by itself and not accept an
+ * msghash32 value directly. Otherwise, it would be easy to
+ * create a "valid" signature without knowledge of the
+ * secret key. See also
+ * https://bitcoin.stackexchange.com/a/81116/35586 for more
+ * background on this topic.
* pubkey: pointer to an initialized public key to verify with (cannot be NULL)
*
* To avoid accepting malleable signatures, only ECDSA signatures in lower-S
@@ -467,7 +474,7 @@ SECP256K1_API int secp256k1_ecdsa_signature_serialize_compact(
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_verify(
const secp256k1_context* ctx,
const secp256k1_ecdsa_signature *sig,
- const unsigned char *msg32,
+ const unsigned char *msghash32,
const secp256k1_pubkey *pubkey
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
@@ -532,12 +539,12 @@ SECP256K1_API extern const secp256k1_nonce_function secp256k1_nonce_function_def
*
* Returns: 1: signature created
* 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)
- * seckey: pointer to a 32-byte secret key (cannot be NULL)
- * noncefp:pointer to a nonce generation function. If NULL, secp256k1_nonce_function_default is used
- * ndata: pointer to arbitrary data used by the nonce generation function (can be NULL)
+ * 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: msghash32: the 32-byte message hash being signed (cannot be NULL)
+ * seckey: pointer to a 32-byte secret key (cannot be NULL)
+ * noncefp: pointer to a nonce generation function. If NULL, secp256k1_nonce_function_default is used
+ * ndata: pointer to arbitrary data used by the nonce generation function (can be NULL)
*
* The created signature is always in lower-S form. See
* secp256k1_ecdsa_signature_normalize for more details.
@@ -545,7 +552,7 @@ SECP256K1_API extern const secp256k1_nonce_function secp256k1_nonce_function_def
SECP256K1_API int secp256k1_ecdsa_sign(
const secp256k1_context* ctx,
secp256k1_ecdsa_signature *sig,
- const unsigned char *msg32,
+ const unsigned char *msghash32,
const unsigned char *seckey,
secp256k1_nonce_function noncefp,
const void *ndata
@@ -626,7 +633,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_negate(
* 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
+ * In: tweak32: 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).
@@ -634,7 +641,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_negate(
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_tweak_add(
const secp256k1_context* ctx,
unsigned char *seckey,
- const unsigned char *tweak
+ const unsigned char *tweak32
) 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
@@ -642,7 +649,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_tweak_add(
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_add(
const secp256k1_context* ctx,
unsigned char *seckey,
- const unsigned char *tweak
+ const unsigned char *tweak32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Tweak a public key by adding tweak times the generator to it.
@@ -654,7 +661,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_add(
* (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
+ * In: tweak32: 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).
@@ -662,7 +669,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_add(
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_add(
const secp256k1_context* ctx,
secp256k1_pubkey *pubkey,
- const unsigned char *tweak
+ const unsigned char *tweak32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Tweak a secret key by multiplying it by a tweak.
@@ -673,7 +680,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_add(
* 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
+ * In: tweak32: 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).
@@ -681,7 +688,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_add(
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_tweak_mul(
const secp256k1_context* ctx,
unsigned char *seckey,
- const unsigned char *tweak
+ const unsigned char *tweak32
) 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
@@ -689,7 +696,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_tweak_mul(
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_mul(
const secp256k1_context* ctx,
unsigned char *seckey,
- const unsigned char *tweak
+ const unsigned char *tweak32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Tweak a public key by multiplying it by a tweak value.
@@ -699,7 +706,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_mul(
* (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
+ * In: tweak32: 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).
@@ -707,7 +714,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_mul(
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_mul(
const secp256k1_context* ctx,
secp256k1_pubkey *pubkey,
- const unsigned char *tweak
+ const unsigned char *tweak32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Updates the context randomization to protect against side-channel leakage.
diff --git a/src/secp256k1/include/secp256k1_extrakeys.h b/src/secp256k1/include/secp256k1_extrakeys.h
index 0c5dff2c94..6fc7b290f8 100644
--- a/src/secp256k1/include/secp256k1_extrakeys.h
+++ b/src/secp256k1/include/secp256k1_extrakeys.h
@@ -165,6 +165,19 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_create(
const unsigned char *seckey
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
+/** Get the secret key from a keypair.
+ *
+ * Returns: 0 if the arguments are invalid. 1 otherwise.
+ * Args: ctx: pointer to a context object (cannot be NULL)
+ * Out: seckey: pointer to a 32-byte buffer for the secret key (cannot be NULL)
+ * In: keypair: pointer to a keypair (cannot be NULL)
+ */
+SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_sec(
+ const secp256k1_context* ctx,
+ unsigned char *seckey,
+ const secp256k1_keypair *keypair
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
+
/** Get the public key from a keypair.
*
* Returns: 0 if the arguments are invalid. 1 otherwise.
diff --git a/src/secp256k1/include/secp256k1_recovery.h b/src/secp256k1/include/secp256k1_recovery.h
index f8ccaecd3d..aa16532ce8 100644
--- a/src/secp256k1/include/secp256k1_recovery.h
+++ b/src/secp256k1/include/secp256k1_recovery.h
@@ -71,17 +71,17 @@ SECP256K1_API int secp256k1_ecdsa_recoverable_signature_serialize_compact(
*
* Returns: 1: signature created
* 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)
- * seckey: pointer to a 32-byte secret key (cannot be NULL)
- * noncefp:pointer to a nonce generation function. If NULL, secp256k1_nonce_function_default is used
- * ndata: pointer to arbitrary data used by the nonce generation function (can be NULL)
+ * 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: msghash32: the 32-byte message hash being signed (cannot be NULL)
+ * seckey: pointer to a 32-byte secret key (cannot be NULL)
+ * noncefp: pointer to a nonce generation function. If NULL, secp256k1_nonce_function_default is used
+ * ndata: pointer to arbitrary data used by the nonce generation function (can be NULL)
*/
SECP256K1_API int secp256k1_ecdsa_sign_recoverable(
const secp256k1_context* ctx,
secp256k1_ecdsa_recoverable_signature *sig,
- const unsigned char *msg32,
+ const unsigned char *msghash32,
const unsigned char *seckey,
secp256k1_nonce_function noncefp,
const void *ndata
@@ -91,16 +91,16 @@ SECP256K1_API int secp256k1_ecdsa_sign_recoverable(
*
* Returns: 1: public key successfully recovered (which guarantees a correct signature).
* 0: otherwise.
- * Args: ctx: pointer to a context object, initialized for verification (cannot be NULL)
- * Out: pubkey: pointer to the recovered public key (cannot be NULL)
- * In: sig: pointer to initialized signature that supports pubkey recovery (cannot be NULL)
- * msg32: the 32-byte message hash assumed to be signed (cannot be NULL)
+ * Args: ctx: pointer to a context object, initialized for verification (cannot be NULL)
+ * Out: pubkey: pointer to the recovered public key (cannot be NULL)
+ * In: sig: pointer to initialized signature that supports pubkey recovery (cannot be NULL)
+ * msghash32: the 32-byte message hash assumed to be signed (cannot be NULL)
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_recover(
const secp256k1_context* ctx,
secp256k1_pubkey *pubkey,
const secp256k1_ecdsa_recoverable_signature *sig,
- const unsigned char *msg32
+ const unsigned char *msghash32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
#ifdef __cplusplus
diff --git a/src/secp256k1/sage/gen_exhaustive_groups.sage b/src/secp256k1/sage/gen_exhaustive_groups.sage
index 3c3c984811..01d15dcdea 100644
--- a/src/secp256k1/sage/gen_exhaustive_groups.sage
+++ b/src/secp256k1/sage/gen_exhaustive_groups.sage
@@ -1,9 +1,4 @@
-# Define field size and field
-P = 2^256 - 2^32 - 977
-F = GF(P)
-BETA = F(0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee)
-
-assert(BETA != F(1) and BETA^3 == F(1))
+load("secp256k1_params.sage")
orders_done = set()
results = {}
diff --git a/src/secp256k1/sage/gen_split_lambda_constants.sage b/src/secp256k1/sage/gen_split_lambda_constants.sage
new file mode 100644
index 0000000000..7d4359e0f6
--- /dev/null
+++ b/src/secp256k1/sage/gen_split_lambda_constants.sage
@@ -0,0 +1,114 @@
+""" Generates the constants used in secp256k1_scalar_split_lambda.
+
+See the comments for secp256k1_scalar_split_lambda in src/scalar_impl.h for detailed explanations.
+"""
+
+load("secp256k1_params.sage")
+
+def inf_norm(v):
+ """Returns the infinity norm of a vector."""
+ return max(map(abs, v))
+
+def gauss_reduction(i1, i2):
+ v1, v2 = i1.copy(), i2.copy()
+ while True:
+ if inf_norm(v2) < inf_norm(v1):
+ v1, v2 = v2, v1
+ # This is essentially
+ # m = round((v1[0]*v2[0] + v1[1]*v2[1]) / (inf_norm(v1)**2))
+ # (rounding to the nearest integer) without relying on floating point arithmetic.
+ m = ((v1[0]*v2[0] + v1[1]*v2[1]) + (inf_norm(v1)**2) // 2) // (inf_norm(v1)**2)
+ if m == 0:
+ return v1, v2
+ v2[0] -= m*v1[0]
+ v2[1] -= m*v1[1]
+
+def find_split_constants_gauss():
+ """Find constants for secp256k1_scalar_split_lamdba using gauss reduction."""
+ (v11, v12), (v21, v22) = gauss_reduction([0, N], [1, int(LAMBDA)])
+
+ # We use related vectors in secp256k1_scalar_split_lambda.
+ A1, B1 = -v21, -v11
+ A2, B2 = v22, -v21
+
+ return A1, B1, A2, B2
+
+def find_split_constants_explicit_tof():
+ """Find constants for secp256k1_scalar_split_lamdba using the trace of Frobenius.
+
+ See Benjamin Smith: "Easy scalar decompositions for efficient scalar multiplication on
+ elliptic curves and genus 2 Jacobians" (https://eprint.iacr.org/2013/672), Example 2
+ """
+ assert P % 3 == 1 # The paper says P % 3 == 2 but that appears to be a mistake, see [10].
+ assert C.j_invariant() == 0
+
+ t = C.trace_of_frobenius()
+
+ c = Integer(sqrt((4*P - t**2)/3))
+ A1 = Integer((t - c)/2 - 1)
+ B1 = c
+
+ A2 = Integer((t + c)/2 - 1)
+ B2 = Integer(1 - (t - c)/2)
+
+ # We use a negated b values in secp256k1_scalar_split_lambda.
+ B1, B2 = -B1, -B2
+
+ return A1, B1, A2, B2
+
+A1, B1, A2, B2 = find_split_constants_explicit_tof()
+
+# For extra fun, use an independent method to recompute the constants.
+assert (A1, B1, A2, B2) == find_split_constants_gauss()
+
+# PHI : Z[l] -> Z_n where phi(a + b*l) == a + b*lambda mod n.
+def PHI(a,b):
+ return Z(a + LAMBDA*b)
+
+# Check that (A1, B1) and (A2, B2) are in the kernel of PHI.
+assert PHI(A1, B1) == Z(0)
+assert PHI(A2, B2) == Z(0)
+
+# Check that the parallelogram generated by (A1, A2) and (B1, B2)
+# is a fundamental domain by containing exactly N points.
+# Since the LHS is the determinant and N != 0, this also checks that
+# (A1, A2) and (B1, B2) are linearly independent. By the previous
+# assertions, (A1, A2) and (B1, B2) are a basis of the kernel.
+assert A1*B2 - B1*A2 == N
+
+# Check that their components are short enough.
+assert (A1 + A2)/2 < sqrt(N)
+assert B1 < sqrt(N)
+assert B2 < sqrt(N)
+
+G1 = round((2**384)*B2/N)
+G2 = round((2**384)*(-B1)/N)
+
+def rnddiv2(v):
+ if v & 1:
+ v += 1
+ return v >> 1
+
+def scalar_lambda_split(k):
+ """Equivalent to secp256k1_scalar_lambda_split()."""
+ c1 = rnddiv2((k * G1) >> 383)
+ c2 = rnddiv2((k * G2) >> 383)
+ c1 = (c1 * -B1) % N
+ c2 = (c2 * -B2) % N
+ r2 = (c1 + c2) % N
+ r1 = (k + r2 * -LAMBDA) % N
+ return (r1, r2)
+
+# The result of scalar_lambda_split can depend on the representation of k (mod n).
+SPECIAL = (2**383) // G2 + 1
+assert scalar_lambda_split(SPECIAL) != scalar_lambda_split(SPECIAL + N)
+
+print(' A1 =', hex(A1))
+print(' -B1 =', hex(-B1))
+print(' A2 =', hex(A2))
+print(' -B2 =', hex(-B2))
+print(' =', hex(Z(-B2)))
+print(' -LAMBDA =', hex(-LAMBDA))
+
+print(' G1 =', hex(G1))
+print(' G2 =', hex(G2))
diff --git a/src/secp256k1/sage/group_prover.sage b/src/secp256k1/sage/group_prover.sage
index 8521f07999..b200bfeae3 100644
--- a/src/secp256k1/sage/group_prover.sage
+++ b/src/secp256k1/sage/group_prover.sage
@@ -42,7 +42,7 @@
# as we assume that all constraints in it are complementary with each other.
#
# Based on the sage verification scripts used in the Explicit-Formulas Database
-# by Tanja Lange and others, see http://hyperelliptic.org/EFD
+# by Tanja Lange and others, see https://hyperelliptic.org/EFD
class fastfrac:
"""Fractions over rings."""
@@ -65,7 +65,7 @@ class fastfrac:
return self.top in I and self.bot not in I
def reduce(self,assumeZero):
- zero = self.R.ideal(map(numerator, assumeZero))
+ zero = self.R.ideal(list(map(numerator, assumeZero)))
return fastfrac(self.R, zero.reduce(self.top)) / fastfrac(self.R, zero.reduce(self.bot))
def __add__(self,other):
@@ -100,7 +100,7 @@ class fastfrac:
"""Multiply something else with a fraction."""
return self.__mul__(other)
- def __div__(self,other):
+ def __truediv__(self,other):
"""Divide two fractions."""
if parent(other) == ZZ:
return fastfrac(self.R,self.top,self.bot * other)
@@ -108,6 +108,11 @@ class fastfrac:
return fastfrac(self.R,self.top * other.bot,self.bot * other.top)
return NotImplemented
+ # Compatibility wrapper for Sage versions based on Python 2
+ def __div__(self,other):
+ """Divide two fractions."""
+ return self.__truediv__(other)
+
def __pow__(self,other):
"""Compute a power of a fraction."""
if parent(other) == ZZ:
@@ -175,7 +180,7 @@ class constraints:
def conflicts(R, con):
"""Check whether any of the passed non-zero assumptions is implied by the zero assumptions"""
- zero = R.ideal(map(numerator, con.zero))
+ zero = R.ideal(list(map(numerator, con.zero)))
if 1 in zero:
return True
# First a cheap check whether any of the individual nonzero terms conflict on
@@ -195,7 +200,7 @@ def conflicts(R, con):
def get_nonzero_set(R, assume):
"""Calculate a simple set of nonzero expressions"""
- zero = R.ideal(map(numerator, assume.zero))
+ zero = R.ideal(list(map(numerator, assume.zero)))
nonzero = set()
for nz in map(numerator, assume.nonzero):
for (f,n) in nz.factor():
@@ -208,7 +213,7 @@ def get_nonzero_set(R, assume):
def prove_nonzero(R, exprs, assume):
"""Check whether an expression is provably nonzero, given assumptions"""
- zero = R.ideal(map(numerator, assume.zero))
+ zero = R.ideal(list(map(numerator, assume.zero)))
nonzero = get_nonzero_set(R, assume)
expl = set()
ok = True
@@ -250,7 +255,7 @@ def prove_zero(R, exprs, assume):
r, e = prove_nonzero(R, dict(map(lambda x: (fastfrac(R, x.bot, 1), exprs[x]), exprs)), assume)
if not r:
return (False, map(lambda x: "Possibly zero denominator: %s" % x, e))
- zero = R.ideal(map(numerator, assume.zero))
+ zero = R.ideal(list(map(numerator, assume.zero)))
nonzero = prod(x for x in assume.nonzero)
expl = []
for expr in exprs:
@@ -265,8 +270,8 @@ def describe_extra(R, assume, assumeExtra):
"""Describe what assumptions are added, given existing assumptions"""
zerox = assume.zero.copy()
zerox.update(assumeExtra.zero)
- zero = R.ideal(map(numerator, assume.zero))
- zeroextra = R.ideal(map(numerator, zerox))
+ zero = R.ideal(list(map(numerator, assume.zero)))
+ zeroextra = R.ideal(list(map(numerator, zerox)))
nonzero = get_nonzero_set(R, assume)
ret = set()
# Iterate over the extra zero expressions
diff --git a/src/secp256k1/sage/secp256k1.sage b/src/secp256k1/sage/prove_group_implementations.sage
index a97e732f7f..a97e732f7f 100644
--- a/src/secp256k1/sage/secp256k1.sage
+++ b/src/secp256k1/sage/prove_group_implementations.sage
diff --git a/src/secp256k1/sage/secp256k1_params.sage b/src/secp256k1/sage/secp256k1_params.sage
new file mode 100644
index 0000000000..4e000726ed
--- /dev/null
+++ b/src/secp256k1/sage/secp256k1_params.sage
@@ -0,0 +1,36 @@
+"""Prime order of finite field underlying secp256k1 (2^256 - 2^32 - 977)"""
+P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
+
+"""Finite field underlying secp256k1"""
+F = FiniteField(P)
+
+"""Elliptic curve secp256k1: y^2 = x^3 + 7"""
+C = EllipticCurve([F(0), F(7)])
+
+"""Base point of secp256k1"""
+G = C.lift_x(0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798)
+
+"""Prime order of secp256k1"""
+N = C.order()
+
+"""Finite field of scalars of secp256k1"""
+Z = FiniteField(N)
+
+""" Beta value of secp256k1 non-trivial endomorphism: lambda * (x, y) = (beta * x, y)"""
+BETA = F(2)^((P-1)/3)
+
+""" Lambda value of secp256k1 non-trivial endomorphism: lambda * (x, y) = (beta * x, y)"""
+LAMBDA = Z(3)^((N-1)/3)
+
+assert is_prime(P)
+assert is_prime(N)
+
+assert BETA != F(1)
+assert BETA^3 == F(1)
+assert BETA^2 + BETA + 1 == 0
+
+assert LAMBDA != Z(1)
+assert LAMBDA^3 == Z(1)
+assert LAMBDA^2 + LAMBDA + 1 == 0
+
+assert Integer(LAMBDA)*G == C(BETA*G[0], G[1])
diff --git a/src/secp256k1/sage/weierstrass_prover.sage b/src/secp256k1/sage/weierstrass_prover.sage
index 03ef2ec901..b770c6dafe 100644
--- a/src/secp256k1/sage/weierstrass_prover.sage
+++ b/src/secp256k1/sage/weierstrass_prover.sage
@@ -175,24 +175,24 @@ laws_jacobian_weierstrass = {
def check_exhaustive_jacobian_weierstrass(name, A, B, branches, formula, p):
"""Verify an implementation of addition of Jacobian points on a Weierstrass curve, by executing and validating the result for every possible addition in a prime field"""
F = Integers(p)
- print "Formula %s on Z%i:" % (name, p)
+ print("Formula %s on Z%i:" % (name, p))
points = []
- for x in xrange(0, p):
- for y in xrange(0, p):
+ for x in range(0, p):
+ for y in range(0, p):
point = affinepoint(F(x), F(y))
r, e = concrete_verify(on_weierstrass_curve(A, B, point))
if r:
points.append(point)
- for za in xrange(1, p):
- for zb in xrange(1, p):
+ for za in range(1, p):
+ for zb in range(1, p):
for pa in points:
for pb in points:
- for ia in xrange(2):
- for ib in xrange(2):
+ for ia in range(2):
+ for ib in range(2):
pA = jacobianpoint(pa.x * F(za)^2, pa.y * F(za)^3, F(za), ia)
pB = jacobianpoint(pb.x * F(zb)^2, pb.y * F(zb)^3, F(zb), ib)
- for branch in xrange(0, branches):
+ for branch in range(0, branches):
assumeAssert, assumeBranch, pC = formula(branch, pA, pB)
pC.X = F(pC.X)
pC.Y = F(pC.Y)
@@ -206,13 +206,13 @@ def check_exhaustive_jacobian_weierstrass(name, A, B, branches, formula, p):
r, e = concrete_verify(assumeLaw)
if r:
if match:
- print " multiple branches for (%s,%s,%s,%s) + (%s,%s,%s,%s)" % (pA.X, pA.Y, pA.Z, pA.Infinity, pB.X, pB.Y, pB.Z, pB.Infinity)
+ print(" multiple branches for (%s,%s,%s,%s) + (%s,%s,%s,%s)" % (pA.X, pA.Y, pA.Z, pA.Infinity, pB.X, pB.Y, pB.Z, pB.Infinity))
else:
match = True
r, e = concrete_verify(require)
if not r:
- print " failure in branch %i for (%s,%s,%s,%s) + (%s,%s,%s,%s) = (%s,%s,%s,%s): %s" % (branch, pA.X, pA.Y, pA.Z, pA.Infinity, pB.X, pB.Y, pB.Z, pB.Infinity, pC.X, pC.Y, pC.Z, pC.Infinity, e)
- print
+ print(" failure in branch %i for (%s,%s,%s,%s) + (%s,%s,%s,%s) = (%s,%s,%s,%s): %s" % (branch, pA.X, pA.Y, pA.Z, pA.Infinity, pB.X, pB.Y, pB.Z, pB.Infinity, pC.X, pC.Y, pC.Z, pC.Infinity, e))
+ print()
def check_symbolic_function(R, assumeAssert, assumeBranch, f, A, B, pa, pb, pA, pB, pC):
@@ -242,9 +242,9 @@ def check_symbolic_jacobian_weierstrass(name, A, B, branches, formula):
for key in laws_jacobian_weierstrass:
res[key] = []
- print ("Formula " + name + ":")
+ print("Formula " + name + ":")
count = 0
- for branch in xrange(branches):
+ for branch in range(branches):
assumeFormula, assumeBranch, pC = formula(branch, pA, pB)
pC.X = lift(pC.X)
pC.Y = lift(pC.Y)
@@ -255,10 +255,10 @@ def check_symbolic_jacobian_weierstrass(name, A, B, branches, formula):
res[key].append((check_symbolic_function(R, assumeFormula, assumeBranch, laws_jacobian_weierstrass[key], A, B, pa, pb, pA, pB, pC), branch))
for key in res:
- print " %s:" % key
+ print(" %s:" % key)
val = res[key]
for x in val:
if x[0] is not None:
- print " branch %i: %s" % (x[1], x[0])
+ print(" branch %i: %s" % (x[1], x[0]))
- print
+ print()
diff --git a/src/secp256k1/src/asm/field_10x26_arm.s b/src/secp256k1/src/asm/field_10x26_arm.s
index 9a5bd06721..5f68cefc46 100644
--- a/src/secp256k1/src/asm/field_10x26_arm.s
+++ b/src/secp256k1/src/asm/field_10x26_arm.s
@@ -1,9 +1,9 @@
@ vim: set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab syntax=armasm:
-/**********************************************************************
- * Copyright (c) 2014 Wladimir J. van der Laan *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2014 Wladimir J. van der Laan *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
/*
ARM implementation of field_10x26 inner loops.
diff --git a/src/secp256k1/src/assumptions.h b/src/secp256k1/src/assumptions.h
index 77204de2b8..6dc527b288 100644
--- a/src/secp256k1/src/assumptions.h
+++ b/src/secp256k1/src/assumptions.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2020 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2020 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_ASSUMPTIONS_H
#define SECP256K1_ASSUMPTIONS_H
diff --git a/src/secp256k1/src/basic-config.h b/src/secp256k1/src/basic-config.h
index b0d82e89b4..6f7693cb8f 100644
--- a/src/secp256k1/src/basic-config.h
+++ b/src/secp256k1/src/basic-config.h
@@ -1,33 +1,16 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013, 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_BASIC_CONFIG_H
#define SECP256K1_BASIC_CONFIG_H
#ifdef USE_BASIC_CONFIG
-#undef USE_ASM_X86_64
-#undef USE_ECMULT_STATIC_PRECOMPUTATION
-#undef USE_EXTERNAL_ASM
-#undef USE_EXTERNAL_DEFAULT_CALLBACKS
-#undef USE_FIELD_INV_BUILTIN
-#undef USE_FIELD_INV_NUM
-#undef USE_NUM_GMP
-#undef USE_NUM_NONE
-#undef USE_SCALAR_INV_BUILTIN
-#undef USE_SCALAR_INV_NUM
-#undef USE_FORCE_WIDEMUL_INT64
-#undef USE_FORCE_WIDEMUL_INT128
-#undef ECMULT_WINDOW_SIZE
-
-#define USE_NUM_NONE 1
-#define USE_FIELD_INV_BUILTIN 1
-#define USE_SCALAR_INV_BUILTIN 1
-#define USE_WIDEMUL_64 1
#define ECMULT_WINDOW_SIZE 15
+#define ECMULT_GEN_PREC_BITS 4
#endif /* USE_BASIC_CONFIG */
diff --git a/src/secp256k1/src/bench.h b/src/secp256k1/src/bench.h
index 9bfed903e0..63c55df44d 100644
--- a/src/secp256k1/src/bench.h
+++ b/src/secp256k1/src/bench.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_BENCH_H
#define SECP256K1_BENCH_H
diff --git a/src/secp256k1/src/bench_ecdh.c b/src/secp256k1/src/bench_ecdh.c
index f099d33884..ab4b8f4244 100644
--- a/src/secp256k1/src/bench_ecdh.c
+++ b/src/secp256k1/src/bench_ecdh.c
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2015 Pieter Wuille, Andrew Poelstra *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2015 Pieter Wuille, Andrew Poelstra *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#include <string.h>
diff --git a/src/secp256k1/src/bench_ecmult.c b/src/secp256k1/src/bench_ecmult.c
index facd07ef31..204e85a5dd 100644
--- a/src/secp256k1/src/bench_ecmult.c
+++ b/src/secp256k1/src/bench_ecmult.c
@@ -1,15 +1,14 @@
-/**********************************************************************
- * Copyright (c) 2017 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2017 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#include <stdio.h>
#include "include/secp256k1.h"
#include "util.h"
#include "hash_impl.h"
-#include "num_impl.h"
#include "field_impl.h"
#include "group_impl.h"
#include "scalar_impl.h"
diff --git a/src/secp256k1/src/bench_internal.c b/src/secp256k1/src/bench_internal.c
index 5f2b7a9759..73b8a24ccb 100644
--- a/src/secp256k1/src/bench_internal.c
+++ b/src/secp256k1/src/bench_internal.c
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2014-2015 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2014-2015 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#include <stdio.h>
#include "include/secp256k1.h"
@@ -10,7 +10,6 @@
#include "assumptions.h"
#include "util.h"
#include "hash_impl.h"
-#include "num_impl.h"
#include "field_impl.h"
#include "group_impl.h"
#include "scalar_impl.h"
@@ -99,15 +98,6 @@ void bench_scalar_negate(void* arg, int iters) {
}
}
-void bench_scalar_sqr(void* arg, int iters) {
- int i;
- bench_inv *data = (bench_inv*)arg;
-
- for (i = 0; i < iters; i++) {
- secp256k1_scalar_sqr(&data->scalar[0], &data->scalar[0]);
- }
-}
-
void bench_scalar_mul(void* arg, int iters) {
int i;
bench_inv *data = (bench_inv*)arg;
@@ -255,26 +245,6 @@ void bench_group_add_affine_var(void* arg, int iters) {
}
}
-void bench_group_jacobi_var(void* arg, int iters) {
- int i, j = 0;
- bench_inv *data = (bench_inv*)arg;
-
- for (i = 0; i < iters; i++) {
- j += secp256k1_gej_has_quad_y_var(&data->gej[0]);
- /* Vary the Y and Z coordinates of the input (the X coordinate doesn't matter to
- secp256k1_gej_has_quad_y_var). Note that the resulting coordinates will
- generally not correspond to a point on the curve, but this is not a problem
- for the code being benchmarked here. Adding and normalizing have less
- overhead than EC operations (which could guarantee the point remains on the
- curve). */
- secp256k1_fe_add(&data->gej[0].y, &data->fe[1]);
- secp256k1_fe_add(&data->gej[0].z, &data->fe[2]);
- secp256k1_fe_normalize_var(&data->gej[0].y);
- secp256k1_fe_normalize_var(&data->gej[0].z);
- }
- CHECK(j <= iters);
-}
-
void bench_group_to_affine_var(void* arg, int iters) {
int i;
bench_inv *data = (bench_inv*)arg;
@@ -282,8 +252,10 @@ void bench_group_to_affine_var(void* arg, int iters) {
for (i = 0; i < iters; ++i) {
secp256k1_ge_set_gej_var(&data->ge[1], &data->gej[0]);
/* Use the output affine X/Y coordinates to vary the input X/Y/Z coordinates.
- Similar to bench_group_jacobi_var, this approach does not result in
- coordinates of points on the curve. */
+ Note that the resulting coordinates will generally not correspond to a point
+ on the curve, but this is not a problem for the code being benchmarked here.
+ Adding and normalizing have less overhead than EC operations (which could
+ guarantee the point remains on the curve). */
secp256k1_fe_add(&data->gej[0].x, &data->ge[1].y);
secp256k1_fe_add(&data->gej[0].y, &data->fe[2]);
secp256k1_fe_add(&data->gej[0].z, &data->ge[1].x);
@@ -369,35 +341,16 @@ void bench_context_sign(void* arg, int iters) {
}
}
-#ifndef USE_NUM_NONE
-void bench_num_jacobi(void* arg, int iters) {
- int i, j = 0;
- bench_inv *data = (bench_inv*)arg;
- secp256k1_num nx, na, norder;
-
- secp256k1_scalar_get_num(&nx, &data->scalar[0]);
- secp256k1_scalar_order_get_num(&norder);
- secp256k1_scalar_get_num(&na, &data->scalar[1]);
-
- for (i = 0; i < iters; i++) {
- j += secp256k1_num_jacobi(&nx, &norder);
- secp256k1_num_add(&nx, &nx, &na);
- }
- CHECK(j <= iters);
-}
-#endif
-
int main(int argc, char **argv) {
bench_inv data;
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);
if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "split")) run_benchmark("scalar_split", bench_scalar_split, bench_setup, NULL, &data, 10, iters);
- 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, "scalar") || have_flag(argc, argv, "inverse")) run_benchmark("scalar_inverse", bench_scalar_inverse, bench_setup, NULL, &data, 10, iters);
+ 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, iters);
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);
@@ -411,7 +364,6 @@ int main(int argc, char **argv) {
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, "group") || have_flag(argc, argv, "to_affine")) run_benchmark("group_to_affine_var", bench_group_to_affine_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, iters);
@@ -424,8 +376,5 @@ int main(int argc, char **argv) {
if (have_flag(argc, argv, "context") || have_flag(argc, argv, "verify")) run_benchmark("context_verify", bench_context_verify, bench_setup, NULL, &data, 10, 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, iters*10);
-#endif
return 0;
}
diff --git a/src/secp256k1/src/bench_recover.c b/src/secp256k1/src/bench_recover.c
index e952ed1215..3f6270ce84 100644
--- a/src/secp256k1/src/bench_recover.c
+++ b/src/secp256k1/src/bench_recover.c
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2014-2015 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2014-2015 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#include "include/secp256k1.h"
#include "include/secp256k1_recovery.h"
diff --git a/src/secp256k1/src/bench_schnorrsig.c b/src/secp256k1/src/bench_schnorrsig.c
index 315f5af28e..f7f591c41d 100644
--- a/src/secp256k1/src/bench_schnorrsig.c
+++ b/src/secp256k1/src/bench_schnorrsig.c
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2018-2020 Andrew Poelstra, Jonas Nick *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2018-2020 Andrew Poelstra, Jonas Nick *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#include <string.h>
#include <stdlib.h>
diff --git a/src/secp256k1/src/bench_sign.c b/src/secp256k1/src/bench_sign.c
index c6b2942cc0..933f367c4b 100644
--- a/src/secp256k1/src/bench_sign.c
+++ b/src/secp256k1/src/bench_sign.c
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#include "include/secp256k1.h"
#include "util.h"
@@ -12,11 +12,11 @@ typedef struct {
secp256k1_context* ctx;
unsigned char msg[32];
unsigned char key[32];
-} bench_sign;
+} bench_sign_data;
static void bench_sign_setup(void* arg) {
int i;
- bench_sign *data = (bench_sign*)arg;
+ bench_sign_data *data = (bench_sign_data*)arg;
for (i = 0; i < 32; i++) {
data->msg[i] = i + 1;
@@ -28,7 +28,7 @@ static void bench_sign_setup(void* arg) {
static void bench_sign_run(void* arg, int iters) {
int i;
- bench_sign *data = (bench_sign*)arg;
+ bench_sign_data *data = (bench_sign_data*)arg;
unsigned char sig[74];
for (i = 0; i < iters; i++) {
@@ -45,7 +45,7 @@ static void bench_sign_run(void* arg, int iters) {
}
int main(void) {
- bench_sign data;
+ bench_sign_data data;
int iters = get_iters(20000);
diff --git a/src/secp256k1/src/bench_verify.c b/src/secp256k1/src/bench_verify.c
index 272d3e5cc4..c56aefd369 100644
--- a/src/secp256k1/src/bench_verify.c
+++ b/src/secp256k1/src/bench_verify.c
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#include <stdio.h>
#include <string.h>
@@ -29,11 +29,11 @@ typedef struct {
#ifdef ENABLE_OPENSSL_TESTS
EC_GROUP* ec_group;
#endif
-} benchmark_verify_t;
+} bench_verify_data;
-static void benchmark_verify(void* arg, int iters) {
+static void bench_verify(void* arg, int iters) {
int i;
- benchmark_verify_t* data = (benchmark_verify_t*)arg;
+ bench_verify_data* data = (bench_verify_data*)arg;
for (i = 0; i < iters; i++) {
secp256k1_pubkey pubkey;
@@ -51,9 +51,9 @@ static void benchmark_verify(void* arg, int iters) {
}
#ifdef ENABLE_OPENSSL_TESTS
-static void benchmark_verify_openssl(void* arg, int iters) {
+static void bench_verify_openssl(void* arg, int iters) {
int i;
- benchmark_verify_t* data = (benchmark_verify_t*)arg;
+ bench_verify_data* data = (bench_verify_data*)arg;
for (i = 0; i < iters; i++) {
data->sig[data->siglen - 1] ^= (i & 0xFF);
@@ -84,7 +84,7 @@ int main(void) {
int i;
secp256k1_pubkey pubkey;
secp256k1_ecdsa_signature sig;
- benchmark_verify_t data;
+ bench_verify_data data;
int iters = get_iters(20000);
@@ -103,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, iters);
+ run_benchmark("ecdsa_verify", bench_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, iters);
+ run_benchmark("ecdsa_verify_openssl", bench_verify_openssl, NULL, NULL, &data, 10, iters);
EC_GROUP_free(data.ec_group);
#endif
diff --git a/src/secp256k1/src/ecdsa.h b/src/secp256k1/src/ecdsa.h
index 80590c7cc8..d5e54d8ce6 100644
--- a/src/secp256k1/src/ecdsa.h
+++ b/src/secp256k1/src/ecdsa.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013, 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_ECDSA_H
#define SECP256K1_ECDSA_H
diff --git a/src/secp256k1/src/ecdsa_impl.h b/src/secp256k1/src/ecdsa_impl.h
index 5f54b59faa..156a33d112 100644
--- a/src/secp256k1/src/ecdsa_impl.h
+++ b/src/secp256k1/src/ecdsa_impl.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2013-2015 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013-2015 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_ECDSA_IMPL_H
diff --git a/src/secp256k1/src/eckey.h b/src/secp256k1/src/eckey.h
index b621f1e6c3..5be3a64b84 100644
--- a/src/secp256k1/src/eckey.h
+++ b/src/secp256k1/src/eckey.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013, 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_ECKEY_H
#define SECP256K1_ECKEY_H
diff --git a/src/secp256k1/src/eckey_impl.h b/src/secp256k1/src/eckey_impl.h
index e2e72d9303..a39cb79653 100644
--- a/src/secp256k1/src/eckey_impl.h
+++ b/src/secp256k1/src/eckey_impl.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013, 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_ECKEY_IMPL_H
#define SECP256K1_ECKEY_IMPL_H
diff --git a/src/secp256k1/src/ecmult.h b/src/secp256k1/src/ecmult.h
index 09e8146414..7ab617e20e 100644
--- a/src/secp256k1/src/ecmult.h
+++ b/src/secp256k1/src/ecmult.h
@@ -1,13 +1,12 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014, 2017 Pieter Wuille, Andrew Poelstra *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013, 2014, 2017 Pieter Wuille, Andrew Poelstra *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_ECMULT_H
#define SECP256K1_ECMULT_H
-#include "num.h"
#include "group.h"
#include "scalar.h"
#include "scratch.h"
diff --git a/src/secp256k1/src/ecmult_const.h b/src/secp256k1/src/ecmult_const.h
index 03bb33257d..d6f0ea2227 100644
--- a/src/secp256k1/src/ecmult_const.h
+++ b/src/secp256k1/src/ecmult_const.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2015 Andrew Poelstra *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2015 Andrew Poelstra *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_ECMULT_CONST_H
#define SECP256K1_ECMULT_CONST_H
diff --git a/src/secp256k1/src/ecmult_const_impl.h b/src/secp256k1/src/ecmult_const_impl.h
index bb9511108b..0e1fb965cb 100644
--- a/src/secp256k1/src/ecmult_const_impl.h
+++ b/src/secp256k1/src/ecmult_const_impl.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2015 Pieter Wuille, Andrew Poelstra *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2015 Pieter Wuille, Andrew Poelstra *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_ECMULT_CONST_IMPL_H
#define SECP256K1_ECMULT_CONST_IMPL_H
diff --git a/src/secp256k1/src/ecmult_gen.h b/src/secp256k1/src/ecmult_gen.h
index 30815e5aa1..539618dcbb 100644
--- a/src/secp256k1/src/ecmult_gen.h
+++ b/src/secp256k1/src/ecmult_gen.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013, 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_ECMULT_GEN_H
#define SECP256K1_ECMULT_GEN_H
diff --git a/src/secp256k1/src/ecmult_gen_impl.h b/src/secp256k1/src/ecmult_gen_impl.h
index 30ac16518b..384a67faed 100644
--- a/src/secp256k1/src/ecmult_gen_impl.h
+++ b/src/secp256k1/src/ecmult_gen_impl.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014, 2015 Pieter Wuille, Gregory Maxwell *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013, 2014, 2015 Pieter Wuille, Gregory Maxwell *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_ECMULT_GEN_IMPL_H
#define SECP256K1_ECMULT_GEN_IMPL_H
@@ -144,7 +144,7 @@ static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context *ctx, secp25
* (https://cryptojedi.org/peter/data/chesrump-20130822.pdf) and
* "Cache Attacks and Countermeasures: the Case of AES", RSA 2006,
* by Dag Arne Osvik, Adi Shamir, and Eran Tromer
- * (http://www.tau.ac.il/~tromer/papers/cache.pdf)
+ * (https://www.tau.ac.il/~tromer/papers/cache.pdf)
*/
secp256k1_ge_storage_cmov(&adds, &(*ctx->prec)[j][i], i == bits);
}
diff --git a/src/secp256k1/src/ecmult_impl.h b/src/secp256k1/src/ecmult_impl.h
index a9e8b3c76c..5c2edac68f 100644
--- a/src/secp256k1/src/ecmult_impl.h
+++ b/src/secp256k1/src/ecmult_impl.h
@@ -1,8 +1,8 @@
-/*****************************************************************************
- * Copyright (c) 2013, 2014, 2017 Pieter Wuille, Andrew Poelstra, Jonas Nick *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php. *
- *****************************************************************************/
+/******************************************************************************
+ * Copyright (c) 2013, 2014, 2017 Pieter Wuille, Andrew Poelstra, Jonas Nick *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php. *
+ ******************************************************************************/
#ifndef SECP256K1_ECMULT_IMPL_H
#define SECP256K1_ECMULT_IMPL_H
@@ -595,11 +595,11 @@ static int secp256k1_ecmult_strauss_batch(const secp256k1_callback* error_callba
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));
- 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);
+ state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge));
+ state.pre_a_lam = (secp256k1_ge*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge));
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) {
+ if (points == NULL || scalars == NULL || state.prej == NULL || state.zr == NULL || state.pre_a == NULL || state.pre_a_lam == NULL || state.ps == NULL) {
secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
return 0;
}
diff --git a/src/secp256k1/src/field.h b/src/secp256k1/src/field.h
index aca1fb72c5..854aaebabc 100644
--- a/src/secp256k1/src/field.h
+++ b/src/secp256k1/src/field.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013, 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_FIELD_H
#define SECP256K1_FIELD_H
@@ -43,13 +43,12 @@ static void secp256k1_fe_normalize_weak(secp256k1_fe *r);
/** Normalize a field element, without constant-time guarantee. */
static void secp256k1_fe_normalize_var(secp256k1_fe *r);
-/** Verify whether a field element represents zero i.e. would normalize to a zero value. The field
- * implementation may optionally normalize the input, but this should not be relied upon. */
-static int secp256k1_fe_normalizes_to_zero(secp256k1_fe *r);
+/** Verify whether a field element represents zero i.e. would normalize to a zero value. */
+static int secp256k1_fe_normalizes_to_zero(const secp256k1_fe *r);
-/** Verify whether a field element represents zero i.e. would normalize to a zero value. The field
- * implementation may optionally normalize the input, but this should not be relied upon. */
-static int secp256k1_fe_normalizes_to_zero_var(secp256k1_fe *r);
+/** Verify whether a field element represents zero i.e. would normalize to a zero value,
+ * without constant-time guarantee. */
+static int secp256k1_fe_normalizes_to_zero_var(const secp256k1_fe *r);
/** Set a field element equal to a small integer. Resulting field element is normalized. */
static void secp256k1_fe_set_int(secp256k1_fe *r, int a);
@@ -104,9 +103,6 @@ static void secp256k1_fe_sqr(secp256k1_fe *r, const secp256k1_fe *a);
* itself. */
static int secp256k1_fe_sqrt(secp256k1_fe *r, const secp256k1_fe *a);
-/** Checks whether a field element is a quadratic residue. */
-static int secp256k1_fe_is_quad_var(const secp256k1_fe *a);
-
/** Sets a field element to be the (modular) inverse of another. Requires the input's magnitude to be
* at most 8. The output magnitude is 1 (but not guaranteed to be normalized). */
static void secp256k1_fe_inv(secp256k1_fe *r, const secp256k1_fe *a);
@@ -114,11 +110,6 @@ static void secp256k1_fe_inv(secp256k1_fe *r, const secp256k1_fe *a);
/** Potentially faster version of secp256k1_fe_inv, without constant-time guarantee. */
static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *a);
-/** Calculate the (modular) inverses of a batch of field elements. Requires the inputs' magnitudes to be
- * at most 8. The output magnitudes are 1 (but not guaranteed to be normalized). The inputs and
- * outputs must not overlap in memory. */
-static void secp256k1_fe_inv_all_var(secp256k1_fe *r, const secp256k1_fe *a, size_t len);
-
/** Convert a field element to the storage type. */
static void secp256k1_fe_to_storage(secp256k1_fe_storage *r, const secp256k1_fe *a);
diff --git a/src/secp256k1/src/field_10x26.h b/src/secp256k1/src/field_10x26.h
index 5ff03c8abc..9eb65607f1 100644
--- a/src/secp256k1/src/field_10x26.h
+++ b/src/secp256k1/src/field_10x26.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013, 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_FIELD_REPR_H
#define SECP256K1_FIELD_REPR_H
diff --git a/src/secp256k1/src/field_10x26_impl.h b/src/secp256k1/src/field_10x26_impl.h
index 651500ee8e..7a38c117f1 100644
--- a/src/secp256k1/src/field_10x26_impl.h
+++ b/src/secp256k1/src/field_10x26_impl.h
@@ -1,14 +1,15 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013, 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_FIELD_REPR_IMPL_H
#define SECP256K1_FIELD_REPR_IMPL_H
#include "util.h"
#include "field.h"
+#include "modinv32_impl.h"
#ifdef VERIFY
static void secp256k1_fe_verify(const secp256k1_fe *a) {
@@ -181,7 +182,7 @@ static void secp256k1_fe_normalize_var(secp256k1_fe *r) {
#endif
}
-static int secp256k1_fe_normalizes_to_zero(secp256k1_fe *r) {
+static int secp256k1_fe_normalizes_to_zero(const secp256k1_fe *r) {
uint32_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4],
t5 = r->n[5], t6 = r->n[6], t7 = r->n[7], t8 = r->n[8], t9 = r->n[9];
@@ -210,7 +211,7 @@ static int secp256k1_fe_normalizes_to_zero(secp256k1_fe *r) {
return (z0 == 0) | (z1 == 0x3FFFFFFUL);
}
-static int secp256k1_fe_normalizes_to_zero_var(secp256k1_fe *r) {
+static int secp256k1_fe_normalizes_to_zero_var(const secp256k1_fe *r) {
uint32_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9;
uint32_t z0, z1;
uint32_t x;
@@ -1164,4 +1165,92 @@ static SECP256K1_INLINE void secp256k1_fe_from_storage(secp256k1_fe *r, const se
#endif
}
+static void secp256k1_fe_from_signed30(secp256k1_fe *r, const secp256k1_modinv32_signed30 *a) {
+ const uint32_t M26 = UINT32_MAX >> 6;
+ const uint32_t a0 = a->v[0], a1 = a->v[1], a2 = a->v[2], a3 = a->v[3], a4 = a->v[4],
+ a5 = a->v[5], a6 = a->v[6], a7 = a->v[7], a8 = a->v[8];
+
+ /* The output from secp256k1_modinv32{_var} should be normalized to range [0,modulus), and
+ * have limbs in [0,2^30). The modulus is < 2^256, so the top limb must be below 2^(256-30*8).
+ */
+ VERIFY_CHECK(a0 >> 30 == 0);
+ VERIFY_CHECK(a1 >> 30 == 0);
+ VERIFY_CHECK(a2 >> 30 == 0);
+ VERIFY_CHECK(a3 >> 30 == 0);
+ VERIFY_CHECK(a4 >> 30 == 0);
+ VERIFY_CHECK(a5 >> 30 == 0);
+ VERIFY_CHECK(a6 >> 30 == 0);
+ VERIFY_CHECK(a7 >> 30 == 0);
+ VERIFY_CHECK(a8 >> 16 == 0);
+
+ r->n[0] = a0 & M26;
+ r->n[1] = (a0 >> 26 | a1 << 4) & M26;
+ r->n[2] = (a1 >> 22 | a2 << 8) & M26;
+ r->n[3] = (a2 >> 18 | a3 << 12) & M26;
+ r->n[4] = (a3 >> 14 | a4 << 16) & M26;
+ r->n[5] = (a4 >> 10 | a5 << 20) & M26;
+ r->n[6] = (a5 >> 6 | a6 << 24) & M26;
+ r->n[7] = (a6 >> 2 ) & M26;
+ r->n[8] = (a6 >> 28 | a7 << 2) & M26;
+ r->n[9] = (a7 >> 24 | a8 << 6);
+
+#ifdef VERIFY
+ r->magnitude = 1;
+ r->normalized = 1;
+ secp256k1_fe_verify(r);
+#endif
+}
+
+static void secp256k1_fe_to_signed30(secp256k1_modinv32_signed30 *r, const secp256k1_fe *a) {
+ const uint32_t M30 = UINT32_MAX >> 2;
+ const uint64_t a0 = a->n[0], a1 = a->n[1], a2 = a->n[2], a3 = a->n[3], a4 = a->n[4],
+ a5 = a->n[5], a6 = a->n[6], a7 = a->n[7], a8 = a->n[8], a9 = a->n[9];
+
+#ifdef VERIFY
+ VERIFY_CHECK(a->normalized);
+#endif
+
+ r->v[0] = (a0 | a1 << 26) & M30;
+ r->v[1] = (a1 >> 4 | a2 << 22) & M30;
+ r->v[2] = (a2 >> 8 | a3 << 18) & M30;
+ r->v[3] = (a3 >> 12 | a4 << 14) & M30;
+ r->v[4] = (a4 >> 16 | a5 << 10) & M30;
+ r->v[5] = (a5 >> 20 | a6 << 6) & M30;
+ r->v[6] = (a6 >> 24 | a7 << 2
+ | a8 << 28) & M30;
+ r->v[7] = (a8 >> 2 | a9 << 24) & M30;
+ r->v[8] = a9 >> 6;
+}
+
+static const secp256k1_modinv32_modinfo secp256k1_const_modinfo_fe = {
+ {{-0x3D1, -4, 0, 0, 0, 0, 0, 0, 65536}},
+ 0x2DDACACFL
+};
+
+static void secp256k1_fe_inv(secp256k1_fe *r, const secp256k1_fe *x) {
+ secp256k1_fe tmp;
+ secp256k1_modinv32_signed30 s;
+
+ tmp = *x;
+ secp256k1_fe_normalize(&tmp);
+ secp256k1_fe_to_signed30(&s, &tmp);
+ secp256k1_modinv32(&s, &secp256k1_const_modinfo_fe);
+ secp256k1_fe_from_signed30(r, &s);
+
+ VERIFY_CHECK(secp256k1_fe_normalizes_to_zero(r) == secp256k1_fe_normalizes_to_zero(&tmp));
+}
+
+static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *x) {
+ secp256k1_fe tmp;
+ secp256k1_modinv32_signed30 s;
+
+ tmp = *x;
+ secp256k1_fe_normalize_var(&tmp);
+ secp256k1_fe_to_signed30(&s, &tmp);
+ secp256k1_modinv32_var(&s, &secp256k1_const_modinfo_fe);
+ secp256k1_fe_from_signed30(r, &s);
+
+ VERIFY_CHECK(secp256k1_fe_normalizes_to_zero(r) == secp256k1_fe_normalizes_to_zero(&tmp));
+}
+
#endif /* SECP256K1_FIELD_REPR_IMPL_H */
diff --git a/src/secp256k1/src/field_5x52.h b/src/secp256k1/src/field_5x52.h
index 6a068484c2..50ee3f9ec9 100644
--- a/src/secp256k1/src/field_5x52.h
+++ b/src/secp256k1/src/field_5x52.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013, 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_FIELD_REPR_H
#define SECP256K1_FIELD_REPR_H
diff --git a/src/secp256k1/src/field_5x52_asm_impl.h b/src/secp256k1/src/field_5x52_asm_impl.h
index 1fc3171f6b..a2118044ab 100644
--- a/src/secp256k1/src/field_5x52_asm_impl.h
+++ b/src/secp256k1/src/field_5x52_asm_impl.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2013-2014 Diederik Huys, Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013-2014 Diederik Huys, Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
/**
* Changelog:
diff --git a/src/secp256k1/src/field_5x52_impl.h b/src/secp256k1/src/field_5x52_impl.h
index 71a38f915b..60ded927f6 100644
--- a/src/secp256k1/src/field_5x52_impl.h
+++ b/src/secp256k1/src/field_5x52_impl.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013, 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_FIELD_REPR_IMPL_H
#define SECP256K1_FIELD_REPR_IMPL_H
@@ -13,6 +13,7 @@
#include "util.h"
#include "field.h"
+#include "modinv64_impl.h"
#if defined(USE_ASM_X86_64)
#include "field_5x52_asm_impl.h"
@@ -161,7 +162,7 @@ static void secp256k1_fe_normalize_var(secp256k1_fe *r) {
#endif
}
-static int secp256k1_fe_normalizes_to_zero(secp256k1_fe *r) {
+static int secp256k1_fe_normalizes_to_zero(const secp256k1_fe *r) {
uint64_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4];
/* z0 tracks a possible raw value of 0, z1 tracks a possible raw value of P */
@@ -184,7 +185,7 @@ static int secp256k1_fe_normalizes_to_zero(secp256k1_fe *r) {
return (z0 == 0) | (z1 == 0xFFFFFFFFFFFFFULL);
}
-static int secp256k1_fe_normalizes_to_zero_var(secp256k1_fe *r) {
+static int secp256k1_fe_normalizes_to_zero_var(const secp256k1_fe *r) {
uint64_t t0, t1, t2, t3, t4;
uint64_t z0, z1;
uint64_t x;
@@ -498,4 +499,80 @@ static SECP256K1_INLINE void secp256k1_fe_from_storage(secp256k1_fe *r, const se
#endif
}
+static void secp256k1_fe_from_signed62(secp256k1_fe *r, const secp256k1_modinv64_signed62 *a) {
+ const uint64_t M52 = UINT64_MAX >> 12;
+ const uint64_t a0 = a->v[0], a1 = a->v[1], a2 = a->v[2], a3 = a->v[3], a4 = a->v[4];
+
+ /* The output from secp256k1_modinv64{_var} should be normalized to range [0,modulus), and
+ * have limbs in [0,2^62). The modulus is < 2^256, so the top limb must be below 2^(256-62*4).
+ */
+ VERIFY_CHECK(a0 >> 62 == 0);
+ VERIFY_CHECK(a1 >> 62 == 0);
+ VERIFY_CHECK(a2 >> 62 == 0);
+ VERIFY_CHECK(a3 >> 62 == 0);
+ VERIFY_CHECK(a4 >> 8 == 0);
+
+ r->n[0] = a0 & M52;
+ r->n[1] = (a0 >> 52 | a1 << 10) & M52;
+ r->n[2] = (a1 >> 42 | a2 << 20) & M52;
+ r->n[3] = (a2 >> 32 | a3 << 30) & M52;
+ r->n[4] = (a3 >> 22 | a4 << 40);
+
+#ifdef VERIFY
+ r->magnitude = 1;
+ r->normalized = 1;
+ secp256k1_fe_verify(r);
+#endif
+}
+
+static void secp256k1_fe_to_signed62(secp256k1_modinv64_signed62 *r, const secp256k1_fe *a) {
+ const uint64_t M62 = UINT64_MAX >> 2;
+ const uint64_t a0 = a->n[0], a1 = a->n[1], a2 = a->n[2], a3 = a->n[3], a4 = a->n[4];
+
+#ifdef VERIFY
+ VERIFY_CHECK(a->normalized);
+#endif
+
+ r->v[0] = (a0 | a1 << 52) & M62;
+ r->v[1] = (a1 >> 10 | a2 << 42) & M62;
+ r->v[2] = (a2 >> 20 | a3 << 32) & M62;
+ r->v[3] = (a3 >> 30 | a4 << 22) & M62;
+ r->v[4] = a4 >> 40;
+}
+
+static const secp256k1_modinv64_modinfo secp256k1_const_modinfo_fe = {
+ {{-0x1000003D1LL, 0, 0, 0, 256}},
+ 0x27C7F6E22DDACACFLL
+};
+
+static void secp256k1_fe_inv(secp256k1_fe *r, const secp256k1_fe *x) {
+ secp256k1_fe tmp;
+ secp256k1_modinv64_signed62 s;
+
+ tmp = *x;
+ secp256k1_fe_normalize(&tmp);
+ secp256k1_fe_to_signed62(&s, &tmp);
+ secp256k1_modinv64(&s, &secp256k1_const_modinfo_fe);
+ secp256k1_fe_from_signed62(r, &s);
+
+#ifdef VERIFY
+ VERIFY_CHECK(secp256k1_fe_normalizes_to_zero(r) == secp256k1_fe_normalizes_to_zero(&tmp));
+#endif
+}
+
+static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *x) {
+ secp256k1_fe tmp;
+ secp256k1_modinv64_signed62 s;
+
+ tmp = *x;
+ secp256k1_fe_normalize_var(&tmp);
+ secp256k1_fe_to_signed62(&s, &tmp);
+ secp256k1_modinv64_var(&s, &secp256k1_const_modinfo_fe);
+ secp256k1_fe_from_signed62(r, &s);
+
+#ifdef VERIFY
+ VERIFY_CHECK(secp256k1_fe_normalizes_to_zero(r) == secp256k1_fe_normalizes_to_zero(&tmp));
+#endif
+}
+
#endif /* SECP256K1_FIELD_REPR_IMPL_H */
diff --git a/src/secp256k1/src/field_5x52_int128_impl.h b/src/secp256k1/src/field_5x52_int128_impl.h
index bcbfb92ac2..314002ee39 100644
--- a/src/secp256k1/src/field_5x52_int128_impl.h
+++ b/src/secp256k1/src/field_5x52_int128_impl.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013, 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_FIELD_INNER5X52_IMPL_H
#define SECP256K1_FIELD_INNER5X52_IMPL_H
diff --git a/src/secp256k1/src/field_impl.h b/src/secp256k1/src/field_impl.h
index 18e4d2f30e..374284a1f4 100644
--- a/src/secp256k1/src/field_impl.h
+++ b/src/secp256k1/src/field_impl.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013, 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_FIELD_IMPL_H
#define SECP256K1_FIELD_IMPL_H
@@ -12,7 +12,6 @@
#endif
#include "util.h"
-#include "num.h"
#if defined(SECP256K1_WIDEMUL_INT128)
#include "field_5x52_impl.h"
@@ -136,185 +135,6 @@ static int secp256k1_fe_sqrt(secp256k1_fe *r, const secp256k1_fe *a) {
return secp256k1_fe_equal(&t1, a);
}
-static void secp256k1_fe_inv(secp256k1_fe *r, const secp256k1_fe *a) {
- secp256k1_fe x2, x3, x6, x9, x11, x22, x44, x88, x176, x220, x223, t1;
- int j;
-
- /** The binary representation of (p - 2) has 5 blocks of 1s, with lengths in
- * { 1, 2, 22, 223 }. Use an addition chain to calculate 2^n - 1 for each block:
- * [1], [2], 3, 6, 9, 11, [22], 44, 88, 176, 220, [223]
- */
-
- secp256k1_fe_sqr(&x2, a);
- secp256k1_fe_mul(&x2, &x2, a);
-
- secp256k1_fe_sqr(&x3, &x2);
- secp256k1_fe_mul(&x3, &x3, a);
-
- x6 = x3;
- for (j=0; j<3; j++) {
- secp256k1_fe_sqr(&x6, &x6);
- }
- secp256k1_fe_mul(&x6, &x6, &x3);
-
- x9 = x6;
- for (j=0; j<3; j++) {
- secp256k1_fe_sqr(&x9, &x9);
- }
- secp256k1_fe_mul(&x9, &x9, &x3);
-
- x11 = x9;
- for (j=0; j<2; j++) {
- secp256k1_fe_sqr(&x11, &x11);
- }
- secp256k1_fe_mul(&x11, &x11, &x2);
-
- x22 = x11;
- for (j=0; j<11; j++) {
- secp256k1_fe_sqr(&x22, &x22);
- }
- secp256k1_fe_mul(&x22, &x22, &x11);
-
- x44 = x22;
- for (j=0; j<22; j++) {
- secp256k1_fe_sqr(&x44, &x44);
- }
- secp256k1_fe_mul(&x44, &x44, &x22);
-
- x88 = x44;
- for (j=0; j<44; j++) {
- secp256k1_fe_sqr(&x88, &x88);
- }
- secp256k1_fe_mul(&x88, &x88, &x44);
-
- x176 = x88;
- for (j=0; j<88; j++) {
- secp256k1_fe_sqr(&x176, &x176);
- }
- secp256k1_fe_mul(&x176, &x176, &x88);
-
- x220 = x176;
- for (j=0; j<44; j++) {
- secp256k1_fe_sqr(&x220, &x220);
- }
- secp256k1_fe_mul(&x220, &x220, &x44);
-
- x223 = x220;
- for (j=0; j<3; j++) {
- secp256k1_fe_sqr(&x223, &x223);
- }
- secp256k1_fe_mul(&x223, &x223, &x3);
-
- /* The final result is then assembled using a sliding window over the blocks. */
-
- t1 = x223;
- for (j=0; j<23; j++) {
- secp256k1_fe_sqr(&t1, &t1);
- }
- secp256k1_fe_mul(&t1, &t1, &x22);
- for (j=0; j<5; j++) {
- secp256k1_fe_sqr(&t1, &t1);
- }
- secp256k1_fe_mul(&t1, &t1, a);
- for (j=0; j<3; j++) {
- secp256k1_fe_sqr(&t1, &t1);
- }
- secp256k1_fe_mul(&t1, &t1, &x2);
- for (j=0; j<2; j++) {
- secp256k1_fe_sqr(&t1, &t1);
- }
- secp256k1_fe_mul(r, a, &t1);
-}
-
-static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *a) {
-#if defined(USE_FIELD_INV_BUILTIN)
- secp256k1_fe_inv(r, a);
-#elif defined(USE_FIELD_INV_NUM)
- secp256k1_num n, m;
- static const secp256k1_fe negone = SECP256K1_FE_CONST(
- 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL,
- 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFEUL, 0xFFFFFC2EUL
- );
- /* secp256k1 field prime, value p defined in "Standards for Efficient Cryptography" (SEC2) 2.7.1. */
- static const unsigned char prime[32] = {
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFE,0xFF,0xFF,0xFC,0x2F
- };
- unsigned char b[32];
- int res;
- secp256k1_fe c = *a;
- secp256k1_fe_normalize_var(&c);
- secp256k1_fe_get_b32(b, &c);
- secp256k1_num_set_bin(&n, b, 32);
- secp256k1_num_set_bin(&m, prime, 32);
- secp256k1_num_mod_inverse(&n, &n, &m);
- secp256k1_num_get_bin(b, 32, &n);
- res = secp256k1_fe_set_b32(r, b);
- (void)res;
- VERIFY_CHECK(res);
- /* Verify the result is the (unique) valid inverse using non-GMP code. */
- secp256k1_fe_mul(&c, &c, r);
- secp256k1_fe_add(&c, &negone);
- CHECK(secp256k1_fe_normalizes_to_zero_var(&c));
-#else
-#error "Please select field inverse implementation"
-#endif
-}
-
-static void secp256k1_fe_inv_all_var(secp256k1_fe *r, const secp256k1_fe *a, size_t len) {
- secp256k1_fe u;
- size_t i;
- if (len < 1) {
- return;
- }
-
- VERIFY_CHECK((r + len <= a) || (a + len <= r));
-
- r[0] = a[0];
-
- i = 0;
- while (++i < len) {
- secp256k1_fe_mul(&r[i], &r[i - 1], &a[i]);
- }
-
- secp256k1_fe_inv_var(&u, &r[--i]);
-
- while (i > 0) {
- size_t j = i--;
- secp256k1_fe_mul(&r[j], &r[i], &u);
- secp256k1_fe_mul(&u, &u, &a[j]);
- }
-
- r[0] = u;
-}
-
-static int secp256k1_fe_is_quad_var(const secp256k1_fe *a) {
-#ifndef USE_NUM_NONE
- unsigned char b[32];
- secp256k1_num n;
- secp256k1_num m;
- /* secp256k1 field prime, value p defined in "Standards for Efficient Cryptography" (SEC2) 2.7.1. */
- static const unsigned char prime[32] = {
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFE,0xFF,0xFF,0xFC,0x2F
- };
-
- secp256k1_fe c = *a;
- secp256k1_fe_normalize_var(&c);
- secp256k1_fe_get_b32(b, &c);
- secp256k1_num_set_bin(&n, b, 32);
- secp256k1_num_set_bin(&m, prime, 32);
- return secp256k1_num_jacobi(&n, &m) >= 0;
-#else
- secp256k1_fe r;
- return secp256k1_fe_sqrt(&r, a);
-#endif
-}
-
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 8b7729aee4..024c557261 100644
--- a/src/secp256k1/src/gen_context.c
+++ b/src/secp256k1/src/gen_context.c
@@ -1,16 +1,17 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014, 2015 Thomas Daede, Cory Fields *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013, 2014, 2015 Thomas Daede, Cory Fields *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://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.
+/* 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"
+
+/* We can't require the precomputed tables when creating them. */
+#undef USE_ECMULT_STATIC_PRECOMPUTATION
#include "include/secp256k1.h"
#include "assumptions.h"
@@ -47,8 +48,8 @@ int main(int argc, char **argv) {
return -1;
}
- fprintf(fp, "#ifndef _SECP256K1_ECMULT_STATIC_CONTEXT_\n");
- fprintf(fp, "#define _SECP256K1_ECMULT_STATIC_CONTEXT_\n");
+ fprintf(fp, "#ifndef SECP256K1_ECMULT_STATIC_CONTEXT_H\n");
+ fprintf(fp, "#define SECP256K1_ECMULT_STATIC_CONTEXT_H\n");
fprintf(fp, "#include \"src/group.h\"\n");
fprintf(fp, "#define SC SECP256K1_GE_STORAGE_CONST\n");
fprintf(fp, "#if ECMULT_GEN_PREC_N != %d || ECMULT_GEN_PREC_G != %d\n", ECMULT_GEN_PREC_N, ECMULT_GEN_PREC_G);
diff --git a/src/secp256k1/src/group.h b/src/secp256k1/src/group.h
index 36e39ecf0f..b9cd334dae 100644
--- a/src/secp256k1/src/group.h
+++ b/src/secp256k1/src/group.h
@@ -1,13 +1,12 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013, 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_GROUP_H
#define SECP256K1_GROUP_H
-#include "num.h"
#include "field.h"
/** A group element of the secp256k1 curve, in affine coordinates. */
@@ -43,12 +42,6 @@ typedef struct {
/** Set a group element equal to the point with given X and Y coordinates */
static void secp256k1_ge_set_xy(secp256k1_ge *r, const secp256k1_fe *x, const secp256k1_fe *y);
-/** Set a group element (affine) equal to the point with the given X coordinate
- * and a Y coordinate that is a quadratic residue modulo p. The return value
- * is true iff a coordinate with the given X coordinate exists.
- */
-static int secp256k1_ge_set_xquad(secp256k1_ge *r, const secp256k1_fe *x);
-
/** Set a group element (affine) equal to the point with the given X coordinate, and given oddness
* for Y. Return value indicates whether the result is valid. */
static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int odd);
@@ -62,9 +55,12 @@ static int secp256k1_ge_is_valid_var(const secp256k1_ge *a);
/** Set r equal to the inverse of a (i.e., mirrored around the X axis) */
static void secp256k1_ge_neg(secp256k1_ge *r, const secp256k1_ge *a);
-/** Set a group element equal to another which is given in jacobian coordinates */
+/** Set a group element equal to another which is given in jacobian coordinates. Constant time. */
static void secp256k1_ge_set_gej(secp256k1_ge *r, secp256k1_gej *a);
+/** Set a group element equal to another which is given in jacobian coordinates. */
+static void secp256k1_ge_set_gej_var(secp256k1_ge *r, secp256k1_gej *a);
+
/** Set a batch of group elements equal to the inputs given in jacobian coordinates */
static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len);
@@ -93,9 +89,6 @@ static void secp256k1_gej_neg(secp256k1_gej *r, const secp256k1_gej *a);
/** Check whether a group element is the point at infinity. */
static int secp256k1_gej_is_infinity(const secp256k1_gej *a);
-/** Check whether a group element's y coordinate is a quadratic residue. */
-static int secp256k1_gej_has_quad_y_var(const secp256k1_gej *a);
-
/** Set r equal to the double of a. Constant time. */
static void secp256k1_gej_double(secp256k1_gej *r, const secp256k1_gej *a);
diff --git a/src/secp256k1/src/group_impl.h b/src/secp256k1/src/group_impl.h
index a5fbc91a0f..19ebd8f44e 100644
--- a/src/secp256k1/src/group_impl.h
+++ b/src/secp256k1/src/group_impl.h
@@ -1,13 +1,12 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013, 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_GROUP_IMPL_H
#define SECP256K1_GROUP_IMPL_H
-#include "num.h"
#include "field.h"
#include "group.h"
@@ -207,18 +206,14 @@ static void secp256k1_ge_clear(secp256k1_ge *r) {
secp256k1_fe_clear(&r->y);
}
-static int secp256k1_ge_set_xquad(secp256k1_ge *r, const secp256k1_fe *x) {
+static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int odd) {
secp256k1_fe x2, x3;
r->x = *x;
secp256k1_fe_sqr(&x2, x);
secp256k1_fe_mul(&x3, x, &x2);
r->infinity = 0;
secp256k1_fe_add(&x3, &secp256k1_fe_const_b);
- return secp256k1_fe_sqrt(&r->y, &x3);
-}
-
-static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int odd) {
- if (!secp256k1_ge_set_xquad(r, x)) {
+ if (!secp256k1_fe_sqrt(&r->y, &x3)) {
return 0;
}
secp256k1_fe_normalize_var(&r->y);
@@ -591,7 +586,7 @@ static void secp256k1_gej_add_ge(secp256k1_gej *r, const secp256k1_gej *a, const
secp256k1_fe_cmov(&n, &m, degenerate); /* n = M^3 * Malt (2) */
secp256k1_fe_sqr(&t, &rr_alt); /* t = Ralt^2 (1) */
secp256k1_fe_mul(&r->z, &a->z, &m_alt); /* r->z = Malt*Z (1) */
- infinity = secp256k1_fe_normalizes_to_zero(&r->z) * (1 - a->infinity);
+ infinity = secp256k1_fe_normalizes_to_zero(&r->z) & ~a->infinity;
secp256k1_fe_mul_int(&r->z, 2); /* r->z = Z3 = 2*Malt*Z (2) */
secp256k1_fe_negate(&q, &q, 1); /* q = -Q (2) */
secp256k1_fe_add(&t, &q); /* t = Ralt^2-Q (3) */
@@ -655,26 +650,12 @@ static void secp256k1_ge_mul_lambda(secp256k1_ge *r, const secp256k1_ge *a) {
secp256k1_fe_mul(&r->x, &r->x, &beta);
}
-static int secp256k1_gej_has_quad_y_var(const secp256k1_gej *a) {
- secp256k1_fe yz;
-
- if (a->infinity) {
- return 0;
- }
-
- /* We rely on the fact that the Jacobi symbol of 1 / a->z^3 is the same as
- * that of a->z. Thus a->y / a->z^3 is a quadratic residue iff a->y * a->z
- is */
- secp256k1_fe_mul(&yz, &a->y, &a->z);
- return secp256k1_fe_is_quad_var(&yz);
-}
-
static int secp256k1_ge_is_in_correct_subgroup(const secp256k1_ge* ge) {
#ifdef EXHAUSTIVE_TEST_ORDER
secp256k1_gej out;
int i;
- /* A very simple EC multiplication ladder that avoids a dependecy on ecmult. */
+ /* A very simple EC multiplication ladder that avoids a dependency on ecmult. */
secp256k1_gej_set_infinity(&out);
for (i = 0; i < 32; ++i) {
secp256k1_gej_double_var(&out, &out, NULL);
diff --git a/src/secp256k1/src/hash.h b/src/secp256k1/src/hash.h
index de26e4b89f..0947a09694 100644
--- a/src/secp256k1/src/hash.h
+++ b/src/secp256k1/src/hash.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_HASH_H
#define SECP256K1_HASH_H
diff --git a/src/secp256k1/src/hash_impl.h b/src/secp256k1/src/hash_impl.h
index 409772587b..f8cd3a1634 100644
--- a/src/secp256k1/src/hash_impl.h
+++ b/src/secp256k1/src/hash_impl.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_HASH_IMPL_H
#define SECP256K1_HASH_IMPL_H
diff --git a/src/secp256k1/src/modinv32.h b/src/secp256k1/src/modinv32.h
new file mode 100644
index 0000000000..0efdda9ab5
--- /dev/null
+++ b/src/secp256k1/src/modinv32.h
@@ -0,0 +1,42 @@
+/***********************************************************************
+ * Copyright (c) 2020 Peter Dettman *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+#ifndef SECP256K1_MODINV32_H
+#define SECP256K1_MODINV32_H
+
+#if defined HAVE_CONFIG_H
+#include "libsecp256k1-config.h"
+#endif
+
+#include "util.h"
+
+/* A signed 30-bit limb representation of integers.
+ *
+ * Its value is sum(v[i] * 2^(30*i), i=0..8). */
+typedef struct {
+ int32_t v[9];
+} secp256k1_modinv32_signed30;
+
+typedef struct {
+ /* The modulus in signed30 notation, must be odd and in [3, 2^256]. */
+ secp256k1_modinv32_signed30 modulus;
+
+ /* modulus^{-1} mod 2^30 */
+ uint32_t modulus_inv30;
+} secp256k1_modinv32_modinfo;
+
+/* Replace x with its modular inverse mod modinfo->modulus. x must be in range [0, modulus).
+ * If x is zero, the result will be zero as well. If not, the inverse must exist (i.e., the gcd of
+ * x and modulus must be 1). These rules are automatically satisfied if the modulus is prime.
+ *
+ * On output, all of x's limbs will be in [0, 2^30).
+ */
+static void secp256k1_modinv32_var(secp256k1_modinv32_signed30 *x, const secp256k1_modinv32_modinfo *modinfo);
+
+/* Same as secp256k1_modinv32_var, but constant time in x (not in the modulus). */
+static void secp256k1_modinv32(secp256k1_modinv32_signed30 *x, const secp256k1_modinv32_modinfo *modinfo);
+
+#endif /* SECP256K1_MODINV32_H */
diff --git a/src/secp256k1/src/modinv32_impl.h b/src/secp256k1/src/modinv32_impl.h
new file mode 100644
index 0000000000..661c5fc04c
--- /dev/null
+++ b/src/secp256k1/src/modinv32_impl.h
@@ -0,0 +1,587 @@
+/***********************************************************************
+ * Copyright (c) 2020 Peter Dettman *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+#ifndef SECP256K1_MODINV32_IMPL_H
+#define SECP256K1_MODINV32_IMPL_H
+
+#include "modinv32.h"
+
+#include "util.h"
+
+#include <stdlib.h>
+
+/* This file implements modular inversion based on the paper "Fast constant-time gcd computation and
+ * modular inversion" by Daniel J. Bernstein and Bo-Yin Yang.
+ *
+ * For an explanation of the algorithm, see doc/safegcd_implementation.md. This file contains an
+ * implementation for N=30, using 30-bit signed limbs represented as int32_t.
+ */
+
+#ifdef VERIFY
+static const secp256k1_modinv32_signed30 SECP256K1_SIGNED30_ONE = {{1}};
+
+/* Compute a*factor and put it in r. All but the top limb in r will be in range [0,2^30). */
+static void secp256k1_modinv32_mul_30(secp256k1_modinv32_signed30 *r, const secp256k1_modinv32_signed30 *a, int alen, int32_t factor) {
+ const int32_t M30 = (int32_t)(UINT32_MAX >> 2);
+ int64_t c = 0;
+ int i;
+ for (i = 0; i < 8; ++i) {
+ if (i < alen) c += (int64_t)a->v[i] * factor;
+ r->v[i] = (int32_t)c & M30; c >>= 30;
+ }
+ if (8 < alen) c += (int64_t)a->v[8] * factor;
+ VERIFY_CHECK(c == (int32_t)c);
+ r->v[8] = (int32_t)c;
+}
+
+/* Return -1 for a<b*factor, 0 for a==b*factor, 1 for a>b*factor. A consists of alen limbs; b has 9. */
+static int secp256k1_modinv32_mul_cmp_30(const secp256k1_modinv32_signed30 *a, int alen, const secp256k1_modinv32_signed30 *b, int32_t factor) {
+ int i;
+ secp256k1_modinv32_signed30 am, bm;
+ secp256k1_modinv32_mul_30(&am, a, alen, 1); /* Normalize all but the top limb of a. */
+ secp256k1_modinv32_mul_30(&bm, b, 9, factor);
+ for (i = 0; i < 8; ++i) {
+ /* Verify that all but the top limb of a and b are normalized. */
+ VERIFY_CHECK(am.v[i] >> 30 == 0);
+ VERIFY_CHECK(bm.v[i] >> 30 == 0);
+ }
+ for (i = 8; i >= 0; --i) {
+ if (am.v[i] < bm.v[i]) return -1;
+ if (am.v[i] > bm.v[i]) return 1;
+ }
+ return 0;
+}
+#endif
+
+/* Take as input a signed30 number in range (-2*modulus,modulus), and add a multiple of the modulus
+ * to it to bring it to range [0,modulus). If sign < 0, the input will also be negated in the
+ * process. The input must have limbs in range (-2^30,2^30). The output will have limbs in range
+ * [0,2^30). */
+static void secp256k1_modinv32_normalize_30(secp256k1_modinv32_signed30 *r, int32_t sign, const secp256k1_modinv32_modinfo *modinfo) {
+ const int32_t M30 = (int32_t)(UINT32_MAX >> 2);
+ int32_t r0 = r->v[0], r1 = r->v[1], r2 = r->v[2], r3 = r->v[3], r4 = r->v[4],
+ r5 = r->v[5], r6 = r->v[6], r7 = r->v[7], r8 = r->v[8];
+ int32_t cond_add, cond_negate;
+
+#ifdef VERIFY
+ /* Verify that all limbs are in range (-2^30,2^30). */
+ int i;
+ for (i = 0; i < 9; ++i) {
+ VERIFY_CHECK(r->v[i] >= -M30);
+ VERIFY_CHECK(r->v[i] <= M30);
+ }
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(r, 9, &modinfo->modulus, -2) > 0); /* r > -2*modulus */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(r, 9, &modinfo->modulus, 1) < 0); /* r < modulus */
+#endif
+
+ /* In a first step, add the modulus if the input is negative, and then negate if requested.
+ * This brings r from range (-2*modulus,modulus) to range (-modulus,modulus). As all input
+ * limbs are in range (-2^30,2^30), this cannot overflow an int32_t. Note that the right
+ * shifts below are signed sign-extending shifts (see assumptions.h for tests that that is
+ * indeed the behavior of the right shift operator). */
+ cond_add = r8 >> 31;
+ r0 += modinfo->modulus.v[0] & cond_add;
+ r1 += modinfo->modulus.v[1] & cond_add;
+ r2 += modinfo->modulus.v[2] & cond_add;
+ r3 += modinfo->modulus.v[3] & cond_add;
+ r4 += modinfo->modulus.v[4] & cond_add;
+ r5 += modinfo->modulus.v[5] & cond_add;
+ r6 += modinfo->modulus.v[6] & cond_add;
+ r7 += modinfo->modulus.v[7] & cond_add;
+ r8 += modinfo->modulus.v[8] & cond_add;
+ cond_negate = sign >> 31;
+ r0 = (r0 ^ cond_negate) - cond_negate;
+ r1 = (r1 ^ cond_negate) - cond_negate;
+ r2 = (r2 ^ cond_negate) - cond_negate;
+ r3 = (r3 ^ cond_negate) - cond_negate;
+ r4 = (r4 ^ cond_negate) - cond_negate;
+ r5 = (r5 ^ cond_negate) - cond_negate;
+ r6 = (r6 ^ cond_negate) - cond_negate;
+ r7 = (r7 ^ cond_negate) - cond_negate;
+ r8 = (r8 ^ cond_negate) - cond_negate;
+ /* Propagate the top bits, to bring limbs back to range (-2^30,2^30). */
+ r1 += r0 >> 30; r0 &= M30;
+ r2 += r1 >> 30; r1 &= M30;
+ r3 += r2 >> 30; r2 &= M30;
+ r4 += r3 >> 30; r3 &= M30;
+ r5 += r4 >> 30; r4 &= M30;
+ r6 += r5 >> 30; r5 &= M30;
+ r7 += r6 >> 30; r6 &= M30;
+ r8 += r7 >> 30; r7 &= M30;
+
+ /* In a second step add the modulus again if the result is still negative, bringing r to range
+ * [0,modulus). */
+ cond_add = r8 >> 31;
+ r0 += modinfo->modulus.v[0] & cond_add;
+ r1 += modinfo->modulus.v[1] & cond_add;
+ r2 += modinfo->modulus.v[2] & cond_add;
+ r3 += modinfo->modulus.v[3] & cond_add;
+ r4 += modinfo->modulus.v[4] & cond_add;
+ r5 += modinfo->modulus.v[5] & cond_add;
+ r6 += modinfo->modulus.v[6] & cond_add;
+ r7 += modinfo->modulus.v[7] & cond_add;
+ r8 += modinfo->modulus.v[8] & cond_add;
+ /* And propagate again. */
+ r1 += r0 >> 30; r0 &= M30;
+ r2 += r1 >> 30; r1 &= M30;
+ r3 += r2 >> 30; r2 &= M30;
+ r4 += r3 >> 30; r3 &= M30;
+ r5 += r4 >> 30; r4 &= M30;
+ r6 += r5 >> 30; r5 &= M30;
+ r7 += r6 >> 30; r6 &= M30;
+ r8 += r7 >> 30; r7 &= M30;
+
+ r->v[0] = r0;
+ r->v[1] = r1;
+ r->v[2] = r2;
+ r->v[3] = r3;
+ r->v[4] = r4;
+ r->v[5] = r5;
+ r->v[6] = r6;
+ r->v[7] = r7;
+ r->v[8] = r8;
+
+#ifdef VERIFY
+ VERIFY_CHECK(r0 >> 30 == 0);
+ VERIFY_CHECK(r1 >> 30 == 0);
+ VERIFY_CHECK(r2 >> 30 == 0);
+ VERIFY_CHECK(r3 >> 30 == 0);
+ VERIFY_CHECK(r4 >> 30 == 0);
+ VERIFY_CHECK(r5 >> 30 == 0);
+ VERIFY_CHECK(r6 >> 30 == 0);
+ VERIFY_CHECK(r7 >> 30 == 0);
+ VERIFY_CHECK(r8 >> 30 == 0);
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(r, 9, &modinfo->modulus, 0) >= 0); /* r >= 0 */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(r, 9, &modinfo->modulus, 1) < 0); /* r < modulus */
+#endif
+}
+
+/* Data type for transition matrices (see section 3 of explanation).
+ *
+ * t = [ u v ]
+ * [ q r ]
+ */
+typedef struct {
+ int32_t u, v, q, r;
+} secp256k1_modinv32_trans2x2;
+
+/* Compute the transition matrix and zeta for 30 divsteps.
+ *
+ * Input: zeta: initial zeta
+ * f0: bottom limb of initial f
+ * g0: bottom limb of initial g
+ * Output: t: transition matrix
+ * Return: final zeta
+ *
+ * Implements the divsteps_n_matrix function from the explanation.
+ */
+static int32_t secp256k1_modinv32_divsteps_30(int32_t zeta, uint32_t f0, uint32_t g0, secp256k1_modinv32_trans2x2 *t) {
+ /* u,v,q,r are the elements of the transformation matrix being built up,
+ * starting with the identity matrix. Semantically they are signed integers
+ * in range [-2^30,2^30], but here represented as unsigned mod 2^32. This
+ * permits left shifting (which is UB for negative numbers). The range
+ * being inside [-2^31,2^31) means that casting to signed works correctly.
+ */
+ uint32_t u = 1, v = 0, q = 0, r = 1;
+ uint32_t c1, c2, f = f0, g = g0, x, y, z;
+ int i;
+
+ for (i = 0; i < 30; ++i) {
+ VERIFY_CHECK((f & 1) == 1); /* f must always be odd */
+ VERIFY_CHECK((u * f0 + v * g0) == f << i);
+ VERIFY_CHECK((q * f0 + r * g0) == g << i);
+ /* Compute conditional masks for (zeta < 0) and for (g & 1). */
+ c1 = zeta >> 31;
+ c2 = -(g & 1);
+ /* Compute x,y,z, conditionally negated versions of f,u,v. */
+ x = (f ^ c1) - c1;
+ y = (u ^ c1) - c1;
+ z = (v ^ c1) - c1;
+ /* Conditionally add x,y,z to g,q,r. */
+ g += x & c2;
+ q += y & c2;
+ r += z & c2;
+ /* In what follows, c1 is a condition mask for (zeta < 0) and (g & 1). */
+ c1 &= c2;
+ /* Conditionally change zeta into -zeta-2 or zeta-1. */
+ zeta = (zeta ^ c1) - 1;
+ /* Conditionally add g,q,r to f,u,v. */
+ f += g & c1;
+ u += q & c1;
+ v += r & c1;
+ /* Shifts */
+ g >>= 1;
+ u <<= 1;
+ v <<= 1;
+ /* Bounds on zeta that follow from the bounds on iteration count (max 20*30 divsteps). */
+ VERIFY_CHECK(zeta >= -601 && zeta <= 601);
+ }
+ /* Return data in t and return value. */
+ t->u = (int32_t)u;
+ t->v = (int32_t)v;
+ t->q = (int32_t)q;
+ t->r = (int32_t)r;
+ /* The determinant of t must be a power of two. This guarantees that multiplication with t
+ * does not change the gcd of f and g, apart from adding a power-of-2 factor to it (which
+ * will be divided out again). As each divstep's individual matrix has determinant 2, the
+ * aggregate of 30 of them will have determinant 2^30. */
+ VERIFY_CHECK((int64_t)t->u * t->r - (int64_t)t->v * t->q == ((int64_t)1) << 30);
+ return zeta;
+}
+
+/* Compute the transition matrix and eta for 30 divsteps (variable time).
+ *
+ * Input: eta: initial eta
+ * f0: bottom limb of initial f
+ * g0: bottom limb of initial g
+ * Output: t: transition matrix
+ * Return: final eta
+ *
+ * Implements the divsteps_n_matrix_var function from the explanation.
+ */
+static int32_t secp256k1_modinv32_divsteps_30_var(int32_t eta, uint32_t f0, uint32_t g0, secp256k1_modinv32_trans2x2 *t) {
+ /* inv256[i] = -(2*i+1)^-1 (mod 256) */
+ static const uint8_t inv256[128] = {
+ 0xFF, 0x55, 0x33, 0x49, 0xC7, 0x5D, 0x3B, 0x11, 0x0F, 0xE5, 0xC3, 0x59,
+ 0xD7, 0xED, 0xCB, 0x21, 0x1F, 0x75, 0x53, 0x69, 0xE7, 0x7D, 0x5B, 0x31,
+ 0x2F, 0x05, 0xE3, 0x79, 0xF7, 0x0D, 0xEB, 0x41, 0x3F, 0x95, 0x73, 0x89,
+ 0x07, 0x9D, 0x7B, 0x51, 0x4F, 0x25, 0x03, 0x99, 0x17, 0x2D, 0x0B, 0x61,
+ 0x5F, 0xB5, 0x93, 0xA9, 0x27, 0xBD, 0x9B, 0x71, 0x6F, 0x45, 0x23, 0xB9,
+ 0x37, 0x4D, 0x2B, 0x81, 0x7F, 0xD5, 0xB3, 0xC9, 0x47, 0xDD, 0xBB, 0x91,
+ 0x8F, 0x65, 0x43, 0xD9, 0x57, 0x6D, 0x4B, 0xA1, 0x9F, 0xF5, 0xD3, 0xE9,
+ 0x67, 0xFD, 0xDB, 0xB1, 0xAF, 0x85, 0x63, 0xF9, 0x77, 0x8D, 0x6B, 0xC1,
+ 0xBF, 0x15, 0xF3, 0x09, 0x87, 0x1D, 0xFB, 0xD1, 0xCF, 0xA5, 0x83, 0x19,
+ 0x97, 0xAD, 0x8B, 0xE1, 0xDF, 0x35, 0x13, 0x29, 0xA7, 0x3D, 0x1B, 0xF1,
+ 0xEF, 0xC5, 0xA3, 0x39, 0xB7, 0xCD, 0xAB, 0x01
+ };
+
+ /* Transformation matrix; see comments in secp256k1_modinv32_divsteps_30. */
+ uint32_t u = 1, v = 0, q = 0, r = 1;
+ uint32_t f = f0, g = g0, m;
+ uint16_t w;
+ int i = 30, limit, zeros;
+
+ for (;;) {
+ /* Use a sentinel bit to count zeros only up to i. */
+ zeros = secp256k1_ctz32_var(g | (UINT32_MAX << i));
+ /* Perform zeros divsteps at once; they all just divide g by two. */
+ g >>= zeros;
+ u <<= zeros;
+ v <<= zeros;
+ eta -= zeros;
+ i -= zeros;
+ /* We're done once we've done 30 divsteps. */
+ if (i == 0) break;
+ VERIFY_CHECK((f & 1) == 1);
+ VERIFY_CHECK((g & 1) == 1);
+ VERIFY_CHECK((u * f0 + v * g0) == f << (30 - i));
+ VERIFY_CHECK((q * f0 + r * g0) == g << (30 - i));
+ /* Bounds on eta that follow from the bounds on iteration count (max 25*30 divsteps). */
+ VERIFY_CHECK(eta >= -751 && eta <= 751);
+ /* If eta is negative, negate it and replace f,g with g,-f. */
+ if (eta < 0) {
+ uint32_t tmp;
+ eta = -eta;
+ tmp = f; f = g; g = -tmp;
+ tmp = u; u = q; q = -tmp;
+ tmp = v; v = r; r = -tmp;
+ }
+ /* eta is now >= 0. In what follows we're going to cancel out the bottom bits of g. No more
+ * than i can be cancelled out (as we'd be done before that point), and no more than eta+1
+ * can be done as its sign will flip once that happens. */
+ limit = ((int)eta + 1) > i ? i : ((int)eta + 1);
+ /* m is a mask for the bottom min(limit, 8) bits (our table only supports 8 bits). */
+ VERIFY_CHECK(limit > 0 && limit <= 30);
+ m = (UINT32_MAX >> (32 - limit)) & 255U;
+ /* Find what multiple of f must be added to g to cancel its bottom min(limit, 8) bits. */
+ w = (g * inv256[(f >> 1) & 127]) & m;
+ /* Do so. */
+ g += f * w;
+ q += u * w;
+ r += v * w;
+ VERIFY_CHECK((g & m) == 0);
+ }
+ /* Return data in t and return value. */
+ t->u = (int32_t)u;
+ t->v = (int32_t)v;
+ t->q = (int32_t)q;
+ t->r = (int32_t)r;
+ /* The determinant of t must be a power of two. This guarantees that multiplication with t
+ * does not change the gcd of f and g, apart from adding a power-of-2 factor to it (which
+ * will be divided out again). As each divstep's individual matrix has determinant 2, the
+ * aggregate of 30 of them will have determinant 2^30. */
+ VERIFY_CHECK((int64_t)t->u * t->r - (int64_t)t->v * t->q == ((int64_t)1) << 30);
+ return eta;
+}
+
+/* Compute (t/2^30) * [d, e] mod modulus, where t is a transition matrix for 30 divsteps.
+ *
+ * On input and output, d and e are in range (-2*modulus,modulus). All output limbs will be in range
+ * (-2^30,2^30).
+ *
+ * This implements the update_de function from the explanation.
+ */
+static void secp256k1_modinv32_update_de_30(secp256k1_modinv32_signed30 *d, secp256k1_modinv32_signed30 *e, const secp256k1_modinv32_trans2x2 *t, const secp256k1_modinv32_modinfo* modinfo) {
+ const int32_t M30 = (int32_t)(UINT32_MAX >> 2);
+ const int32_t u = t->u, v = t->v, q = t->q, r = t->r;
+ int32_t di, ei, md, me, sd, se;
+ int64_t cd, ce;
+ int i;
+#ifdef VERIFY
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(d, 9, &modinfo->modulus, -2) > 0); /* d > -2*modulus */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(d, 9, &modinfo->modulus, 1) < 0); /* d < modulus */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(e, 9, &modinfo->modulus, -2) > 0); /* e > -2*modulus */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(e, 9, &modinfo->modulus, 1) < 0); /* e < modulus */
+ VERIFY_CHECK((labs(u) + labs(v)) >= 0); /* |u|+|v| doesn't overflow */
+ VERIFY_CHECK((labs(q) + labs(r)) >= 0); /* |q|+|r| doesn't overflow */
+ VERIFY_CHECK((labs(u) + labs(v)) <= M30 + 1); /* |u|+|v| <= 2^30 */
+ VERIFY_CHECK((labs(q) + labs(r)) <= M30 + 1); /* |q|+|r| <= 2^30 */
+#endif
+ /* [md,me] start as zero; plus [u,q] if d is negative; plus [v,r] if e is negative. */
+ sd = d->v[8] >> 31;
+ se = e->v[8] >> 31;
+ md = (u & sd) + (v & se);
+ me = (q & sd) + (r & se);
+ /* Begin computing t*[d,e]. */
+ di = d->v[0];
+ ei = e->v[0];
+ cd = (int64_t)u * di + (int64_t)v * ei;
+ ce = (int64_t)q * di + (int64_t)r * ei;
+ /* Correct md,me so that t*[d,e]+modulus*[md,me] has 30 zero bottom bits. */
+ md -= (modinfo->modulus_inv30 * (uint32_t)cd + md) & M30;
+ me -= (modinfo->modulus_inv30 * (uint32_t)ce + me) & M30;
+ /* Update the beginning of computation for t*[d,e]+modulus*[md,me] now md,me are known. */
+ cd += (int64_t)modinfo->modulus.v[0] * md;
+ ce += (int64_t)modinfo->modulus.v[0] * me;
+ /* Verify that the low 30 bits of the computation are indeed zero, and then throw them away. */
+ VERIFY_CHECK(((int32_t)cd & M30) == 0); cd >>= 30;
+ VERIFY_CHECK(((int32_t)ce & M30) == 0); ce >>= 30;
+ /* Now iteratively compute limb i=1..8 of t*[d,e]+modulus*[md,me], and store them in output
+ * limb i-1 (shifting down by 30 bits). */
+ for (i = 1; i < 9; ++i) {
+ di = d->v[i];
+ ei = e->v[i];
+ cd += (int64_t)u * di + (int64_t)v * ei;
+ ce += (int64_t)q * di + (int64_t)r * ei;
+ cd += (int64_t)modinfo->modulus.v[i] * md;
+ ce += (int64_t)modinfo->modulus.v[i] * me;
+ d->v[i - 1] = (int32_t)cd & M30; cd >>= 30;
+ e->v[i - 1] = (int32_t)ce & M30; ce >>= 30;
+ }
+ /* What remains is limb 9 of t*[d,e]+modulus*[md,me]; store it as output limb 8. */
+ d->v[8] = (int32_t)cd;
+ e->v[8] = (int32_t)ce;
+#ifdef VERIFY
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(d, 9, &modinfo->modulus, -2) > 0); /* d > -2*modulus */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(d, 9, &modinfo->modulus, 1) < 0); /* d < modulus */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(e, 9, &modinfo->modulus, -2) > 0); /* e > -2*modulus */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(e, 9, &modinfo->modulus, 1) < 0); /* e < modulus */
+#endif
+}
+
+/* Compute (t/2^30) * [f, g], where t is a transition matrix for 30 divsteps.
+ *
+ * This implements the update_fg function from the explanation.
+ */
+static void secp256k1_modinv32_update_fg_30(secp256k1_modinv32_signed30 *f, secp256k1_modinv32_signed30 *g, const secp256k1_modinv32_trans2x2 *t) {
+ const int32_t M30 = (int32_t)(UINT32_MAX >> 2);
+ const int32_t u = t->u, v = t->v, q = t->q, r = t->r;
+ int32_t fi, gi;
+ int64_t cf, cg;
+ int i;
+ /* Start computing t*[f,g]. */
+ fi = f->v[0];
+ gi = g->v[0];
+ cf = (int64_t)u * fi + (int64_t)v * gi;
+ cg = (int64_t)q * fi + (int64_t)r * gi;
+ /* Verify that the bottom 30 bits of the result are zero, and then throw them away. */
+ VERIFY_CHECK(((int32_t)cf & M30) == 0); cf >>= 30;
+ VERIFY_CHECK(((int32_t)cg & M30) == 0); cg >>= 30;
+ /* Now iteratively compute limb i=1..8 of t*[f,g], and store them in output limb i-1 (shifting
+ * down by 30 bits). */
+ for (i = 1; i < 9; ++i) {
+ fi = f->v[i];
+ gi = g->v[i];
+ cf += (int64_t)u * fi + (int64_t)v * gi;
+ cg += (int64_t)q * fi + (int64_t)r * gi;
+ f->v[i - 1] = (int32_t)cf & M30; cf >>= 30;
+ g->v[i - 1] = (int32_t)cg & M30; cg >>= 30;
+ }
+ /* What remains is limb 9 of t*[f,g]; store it as output limb 8. */
+ f->v[8] = (int32_t)cf;
+ g->v[8] = (int32_t)cg;
+}
+
+/* Compute (t/2^30) * [f, g], where t is a transition matrix for 30 divsteps.
+ *
+ * Version that operates on a variable number of limbs in f and g.
+ *
+ * This implements the update_fg function from the explanation in modinv64_impl.h.
+ */
+static void secp256k1_modinv32_update_fg_30_var(int len, secp256k1_modinv32_signed30 *f, secp256k1_modinv32_signed30 *g, const secp256k1_modinv32_trans2x2 *t) {
+ const int32_t M30 = (int32_t)(UINT32_MAX >> 2);
+ const int32_t u = t->u, v = t->v, q = t->q, r = t->r;
+ int32_t fi, gi;
+ int64_t cf, cg;
+ int i;
+ VERIFY_CHECK(len > 0);
+ /* Start computing t*[f,g]. */
+ fi = f->v[0];
+ gi = g->v[0];
+ cf = (int64_t)u * fi + (int64_t)v * gi;
+ cg = (int64_t)q * fi + (int64_t)r * gi;
+ /* Verify that the bottom 62 bits of the result are zero, and then throw them away. */
+ VERIFY_CHECK(((int32_t)cf & M30) == 0); cf >>= 30;
+ VERIFY_CHECK(((int32_t)cg & M30) == 0); cg >>= 30;
+ /* Now iteratively compute limb i=1..len of t*[f,g], and store them in output limb i-1 (shifting
+ * down by 30 bits). */
+ for (i = 1; i < len; ++i) {
+ fi = f->v[i];
+ gi = g->v[i];
+ cf += (int64_t)u * fi + (int64_t)v * gi;
+ cg += (int64_t)q * fi + (int64_t)r * gi;
+ f->v[i - 1] = (int32_t)cf & M30; cf >>= 30;
+ g->v[i - 1] = (int32_t)cg & M30; cg >>= 30;
+ }
+ /* What remains is limb (len) of t*[f,g]; store it as output limb (len-1). */
+ f->v[len - 1] = (int32_t)cf;
+ g->v[len - 1] = (int32_t)cg;
+}
+
+/* Compute the inverse of x modulo modinfo->modulus, and replace x with it (constant time in x). */
+static void secp256k1_modinv32(secp256k1_modinv32_signed30 *x, const secp256k1_modinv32_modinfo *modinfo) {
+ /* Start with d=0, e=1, f=modulus, g=x, zeta=-1. */
+ secp256k1_modinv32_signed30 d = {{0}};
+ secp256k1_modinv32_signed30 e = {{1}};
+ secp256k1_modinv32_signed30 f = modinfo->modulus;
+ secp256k1_modinv32_signed30 g = *x;
+ int i;
+ int32_t zeta = -1; /* zeta = -(delta+1/2); delta is initially 1/2. */
+
+ /* Do 20 iterations of 30 divsteps each = 600 divsteps. 590 suffices for 256-bit inputs. */
+ for (i = 0; i < 20; ++i) {
+ /* Compute transition matrix and new zeta after 30 divsteps. */
+ secp256k1_modinv32_trans2x2 t;
+ zeta = secp256k1_modinv32_divsteps_30(zeta, f.v[0], g.v[0], &t);
+ /* Update d,e using that transition matrix. */
+ secp256k1_modinv32_update_de_30(&d, &e, &t, modinfo);
+ /* Update f,g using that transition matrix. */
+#ifdef VERIFY
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, 9, &modinfo->modulus, -1) > 0); /* f > -modulus */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, 9, &modinfo->modulus, 1) <= 0); /* f <= modulus */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, 9, &modinfo->modulus, -1) > 0); /* g > -modulus */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, 9, &modinfo->modulus, 1) < 0); /* g < modulus */
+#endif
+ secp256k1_modinv32_update_fg_30(&f, &g, &t);
+#ifdef VERIFY
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, 9, &modinfo->modulus, -1) > 0); /* f > -modulus */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, 9, &modinfo->modulus, 1) <= 0); /* f <= modulus */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, 9, &modinfo->modulus, -1) > 0); /* g > -modulus */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, 9, &modinfo->modulus, 1) < 0); /* g < modulus */
+#endif
+ }
+
+ /* At this point sufficient iterations have been performed that g must have reached 0
+ * and (if g was not originally 0) f must now equal +/- GCD of the initial f, g
+ * values i.e. +/- 1, and d now contains +/- the modular inverse. */
+#ifdef VERIFY
+ /* g == 0 */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, 9, &SECP256K1_SIGNED30_ONE, 0) == 0);
+ /* |f| == 1, or (x == 0 and d == 0 and |f|=modulus) */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, 9, &SECP256K1_SIGNED30_ONE, -1) == 0 ||
+ secp256k1_modinv32_mul_cmp_30(&f, 9, &SECP256K1_SIGNED30_ONE, 1) == 0 ||
+ (secp256k1_modinv32_mul_cmp_30(x, 9, &SECP256K1_SIGNED30_ONE, 0) == 0 &&
+ secp256k1_modinv32_mul_cmp_30(&d, 9, &SECP256K1_SIGNED30_ONE, 0) == 0 &&
+ (secp256k1_modinv32_mul_cmp_30(&f, 9, &modinfo->modulus, 1) == 0 ||
+ secp256k1_modinv32_mul_cmp_30(&f, 9, &modinfo->modulus, -1) == 0)));
+#endif
+
+ /* Optionally negate d, normalize to [0,modulus), and return it. */
+ secp256k1_modinv32_normalize_30(&d, f.v[8], modinfo);
+ *x = d;
+}
+
+/* Compute the inverse of x modulo modinfo->modulus, and replace x with it (variable time). */
+static void secp256k1_modinv32_var(secp256k1_modinv32_signed30 *x, const secp256k1_modinv32_modinfo *modinfo) {
+ /* Start with d=0, e=1, f=modulus, g=x, eta=-1. */
+ secp256k1_modinv32_signed30 d = {{0, 0, 0, 0, 0, 0, 0, 0, 0}};
+ secp256k1_modinv32_signed30 e = {{1, 0, 0, 0, 0, 0, 0, 0, 0}};
+ secp256k1_modinv32_signed30 f = modinfo->modulus;
+ secp256k1_modinv32_signed30 g = *x;
+#ifdef VERIFY
+ int i = 0;
+#endif
+ int j, len = 9;
+ int32_t eta = -1; /* eta = -delta; delta is initially 1 (faster for the variable-time code) */
+ int32_t cond, fn, gn;
+
+ /* Do iterations of 30 divsteps each until g=0. */
+ while (1) {
+ /* Compute transition matrix and new eta after 30 divsteps. */
+ secp256k1_modinv32_trans2x2 t;
+ eta = secp256k1_modinv32_divsteps_30_var(eta, f.v[0], g.v[0], &t);
+ /* Update d,e using that transition matrix. */
+ secp256k1_modinv32_update_de_30(&d, &e, &t, modinfo);
+ /* Update f,g using that transition matrix. */
+#ifdef VERIFY
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, -1) > 0); /* f > -modulus */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, 1) <= 0); /* f <= modulus */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &modinfo->modulus, -1) > 0); /* g > -modulus */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &modinfo->modulus, 1) < 0); /* g < modulus */
+#endif
+ secp256k1_modinv32_update_fg_30_var(len, &f, &g, &t);
+ /* If the bottom limb of g is 0, there is a chance g=0. */
+ if (g.v[0] == 0) {
+ cond = 0;
+ /* Check if all other limbs are also 0. */
+ for (j = 1; j < len; ++j) {
+ cond |= g.v[j];
+ }
+ /* If so, we're done. */
+ if (cond == 0) break;
+ }
+
+ /* Determine if len>1 and limb (len-1) of both f and g is 0 or -1. */
+ fn = f.v[len - 1];
+ gn = g.v[len - 1];
+ cond = ((int32_t)len - 2) >> 31;
+ cond |= fn ^ (fn >> 31);
+ cond |= gn ^ (gn >> 31);
+ /* If so, reduce length, propagating the sign of f and g's top limb into the one below. */
+ if (cond == 0) {
+ f.v[len - 2] |= (uint32_t)fn << 30;
+ g.v[len - 2] |= (uint32_t)gn << 30;
+ --len;
+ }
+#ifdef VERIFY
+ VERIFY_CHECK(++i < 25); /* We should never need more than 25*30 = 750 divsteps */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, -1) > 0); /* f > -modulus */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, 1) <= 0); /* f <= modulus */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &modinfo->modulus, -1) > 0); /* g > -modulus */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &modinfo->modulus, 1) < 0); /* g < modulus */
+#endif
+ }
+
+ /* At this point g is 0 and (if g was not originally 0) f must now equal +/- GCD of
+ * the initial f, g values i.e. +/- 1, and d now contains +/- the modular inverse. */
+#ifdef VERIFY
+ /* g == 0 */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &SECP256K1_SIGNED30_ONE, 0) == 0);
+ /* |f| == 1, or (x == 0 and d == 0 and |f|=modulus) */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &SECP256K1_SIGNED30_ONE, -1) == 0 ||
+ secp256k1_modinv32_mul_cmp_30(&f, len, &SECP256K1_SIGNED30_ONE, 1) == 0 ||
+ (secp256k1_modinv32_mul_cmp_30(x, 9, &SECP256K1_SIGNED30_ONE, 0) == 0 &&
+ secp256k1_modinv32_mul_cmp_30(&d, 9, &SECP256K1_SIGNED30_ONE, 0) == 0 &&
+ (secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, 1) == 0 ||
+ secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, -1) == 0)));
+#endif
+
+ /* Optionally negate d, normalize to [0,modulus), and return it. */
+ secp256k1_modinv32_normalize_30(&d, f.v[len - 1], modinfo);
+ *x = d;
+}
+
+#endif /* SECP256K1_MODINV32_IMPL_H */
diff --git a/src/secp256k1/src/modinv64.h b/src/secp256k1/src/modinv64.h
new file mode 100644
index 0000000000..da506dfa9f
--- /dev/null
+++ b/src/secp256k1/src/modinv64.h
@@ -0,0 +1,46 @@
+/***********************************************************************
+ * Copyright (c) 2020 Peter Dettman *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+#ifndef SECP256K1_MODINV64_H
+#define SECP256K1_MODINV64_H
+
+#if defined HAVE_CONFIG_H
+#include "libsecp256k1-config.h"
+#endif
+
+#include "util.h"
+
+#ifndef SECP256K1_WIDEMUL_INT128
+#error "modinv64 requires 128-bit wide multiplication support"
+#endif
+
+/* A signed 62-bit limb representation of integers.
+ *
+ * Its value is sum(v[i] * 2^(62*i), i=0..4). */
+typedef struct {
+ int64_t v[5];
+} secp256k1_modinv64_signed62;
+
+typedef struct {
+ /* The modulus in signed62 notation, must be odd and in [3, 2^256]. */
+ secp256k1_modinv64_signed62 modulus;
+
+ /* modulus^{-1} mod 2^62 */
+ uint64_t modulus_inv62;
+} secp256k1_modinv64_modinfo;
+
+/* Replace x with its modular inverse mod modinfo->modulus. x must be in range [0, modulus).
+ * If x is zero, the result will be zero as well. If not, the inverse must exist (i.e., the gcd of
+ * x and modulus must be 1). These rules are automatically satisfied if the modulus is prime.
+ *
+ * On output, all of x's limbs will be in [0, 2^62).
+ */
+static void secp256k1_modinv64_var(secp256k1_modinv64_signed62 *x, const secp256k1_modinv64_modinfo *modinfo);
+
+/* Same as secp256k1_modinv64_var, but constant time in x (not in the modulus). */
+static void secp256k1_modinv64(secp256k1_modinv64_signed62 *x, const secp256k1_modinv64_modinfo *modinfo);
+
+#endif /* SECP256K1_MODINV64_H */
diff --git a/src/secp256k1/src/modinv64_impl.h b/src/secp256k1/src/modinv64_impl.h
new file mode 100644
index 0000000000..0743a9c821
--- /dev/null
+++ b/src/secp256k1/src/modinv64_impl.h
@@ -0,0 +1,593 @@
+/***********************************************************************
+ * Copyright (c) 2020 Peter Dettman *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+#ifndef SECP256K1_MODINV64_IMPL_H
+#define SECP256K1_MODINV64_IMPL_H
+
+#include "modinv64.h"
+
+#include "util.h"
+
+/* This file implements modular inversion based on the paper "Fast constant-time gcd computation and
+ * modular inversion" by Daniel J. Bernstein and Bo-Yin Yang.
+ *
+ * For an explanation of the algorithm, see doc/safegcd_implementation.md. This file contains an
+ * implementation for N=62, using 62-bit signed limbs represented as int64_t.
+ */
+
+#ifdef VERIFY
+/* Helper function to compute the absolute value of an int64_t.
+ * (we don't use abs/labs/llabs as it depends on the int sizes). */
+static int64_t secp256k1_modinv64_abs(int64_t v) {
+ VERIFY_CHECK(v > INT64_MIN);
+ if (v < 0) return -v;
+ return v;
+}
+
+static const secp256k1_modinv64_signed62 SECP256K1_SIGNED62_ONE = {{1}};
+
+/* Compute a*factor and put it in r. All but the top limb in r will be in range [0,2^62). */
+static void secp256k1_modinv64_mul_62(secp256k1_modinv64_signed62 *r, const secp256k1_modinv64_signed62 *a, int alen, int64_t factor) {
+ const int64_t M62 = (int64_t)(UINT64_MAX >> 2);
+ int128_t c = 0;
+ int i;
+ for (i = 0; i < 4; ++i) {
+ if (i < alen) c += (int128_t)a->v[i] * factor;
+ r->v[i] = (int64_t)c & M62; c >>= 62;
+ }
+ if (4 < alen) c += (int128_t)a->v[4] * factor;
+ VERIFY_CHECK(c == (int64_t)c);
+ r->v[4] = (int64_t)c;
+}
+
+/* Return -1 for a<b*factor, 0 for a==b*factor, 1 for a>b*factor. A has alen limbs; b has 5. */
+static int secp256k1_modinv64_mul_cmp_62(const secp256k1_modinv64_signed62 *a, int alen, const secp256k1_modinv64_signed62 *b, int64_t factor) {
+ int i;
+ secp256k1_modinv64_signed62 am, bm;
+ secp256k1_modinv64_mul_62(&am, a, alen, 1); /* Normalize all but the top limb of a. */
+ secp256k1_modinv64_mul_62(&bm, b, 5, factor);
+ for (i = 0; i < 4; ++i) {
+ /* Verify that all but the top limb of a and b are normalized. */
+ VERIFY_CHECK(am.v[i] >> 62 == 0);
+ VERIFY_CHECK(bm.v[i] >> 62 == 0);
+ }
+ for (i = 4; i >= 0; --i) {
+ if (am.v[i] < bm.v[i]) return -1;
+ if (am.v[i] > bm.v[i]) return 1;
+ }
+ return 0;
+}
+#endif
+
+/* Take as input a signed62 number in range (-2*modulus,modulus), and add a multiple of the modulus
+ * to it to bring it to range [0,modulus). If sign < 0, the input will also be negated in the
+ * process. The input must have limbs in range (-2^62,2^62). The output will have limbs in range
+ * [0,2^62). */
+static void secp256k1_modinv64_normalize_62(secp256k1_modinv64_signed62 *r, int64_t sign, const secp256k1_modinv64_modinfo *modinfo) {
+ const int64_t M62 = (int64_t)(UINT64_MAX >> 2);
+ int64_t r0 = r->v[0], r1 = r->v[1], r2 = r->v[2], r3 = r->v[3], r4 = r->v[4];
+ int64_t cond_add, cond_negate;
+
+#ifdef VERIFY
+ /* Verify that all limbs are in range (-2^62,2^62). */
+ int i;
+ for (i = 0; i < 5; ++i) {
+ VERIFY_CHECK(r->v[i] >= -M62);
+ VERIFY_CHECK(r->v[i] <= M62);
+ }
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(r, 5, &modinfo->modulus, -2) > 0); /* r > -2*modulus */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(r, 5, &modinfo->modulus, 1) < 0); /* r < modulus */
+#endif
+
+ /* In a first step, add the modulus if the input is negative, and then negate if requested.
+ * This brings r from range (-2*modulus,modulus) to range (-modulus,modulus). As all input
+ * limbs are in range (-2^62,2^62), this cannot overflow an int64_t. Note that the right
+ * shifts below are signed sign-extending shifts (see assumptions.h for tests that that is
+ * indeed the behavior of the right shift operator). */
+ cond_add = r4 >> 63;
+ r0 += modinfo->modulus.v[0] & cond_add;
+ r1 += modinfo->modulus.v[1] & cond_add;
+ r2 += modinfo->modulus.v[2] & cond_add;
+ r3 += modinfo->modulus.v[3] & cond_add;
+ r4 += modinfo->modulus.v[4] & cond_add;
+ cond_negate = sign >> 63;
+ r0 = (r0 ^ cond_negate) - cond_negate;
+ r1 = (r1 ^ cond_negate) - cond_negate;
+ r2 = (r2 ^ cond_negate) - cond_negate;
+ r3 = (r3 ^ cond_negate) - cond_negate;
+ r4 = (r4 ^ cond_negate) - cond_negate;
+ /* Propagate the top bits, to bring limbs back to range (-2^62,2^62). */
+ r1 += r0 >> 62; r0 &= M62;
+ r2 += r1 >> 62; r1 &= M62;
+ r3 += r2 >> 62; r2 &= M62;
+ r4 += r3 >> 62; r3 &= M62;
+
+ /* In a second step add the modulus again if the result is still negative, bringing
+ * r to range [0,modulus). */
+ cond_add = r4 >> 63;
+ r0 += modinfo->modulus.v[0] & cond_add;
+ r1 += modinfo->modulus.v[1] & cond_add;
+ r2 += modinfo->modulus.v[2] & cond_add;
+ r3 += modinfo->modulus.v[3] & cond_add;
+ r4 += modinfo->modulus.v[4] & cond_add;
+ /* And propagate again. */
+ r1 += r0 >> 62; r0 &= M62;
+ r2 += r1 >> 62; r1 &= M62;
+ r3 += r2 >> 62; r2 &= M62;
+ r4 += r3 >> 62; r3 &= M62;
+
+ r->v[0] = r0;
+ r->v[1] = r1;
+ r->v[2] = r2;
+ r->v[3] = r3;
+ r->v[4] = r4;
+
+#ifdef VERIFY
+ VERIFY_CHECK(r0 >> 62 == 0);
+ VERIFY_CHECK(r1 >> 62 == 0);
+ VERIFY_CHECK(r2 >> 62 == 0);
+ VERIFY_CHECK(r3 >> 62 == 0);
+ VERIFY_CHECK(r4 >> 62 == 0);
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(r, 5, &modinfo->modulus, 0) >= 0); /* r >= 0 */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(r, 5, &modinfo->modulus, 1) < 0); /* r < modulus */
+#endif
+}
+
+/* Data type for transition matrices (see section 3 of explanation).
+ *
+ * t = [ u v ]
+ * [ q r ]
+ */
+typedef struct {
+ int64_t u, v, q, r;
+} secp256k1_modinv64_trans2x2;
+
+/* Compute the transition matrix and eta for 59 divsteps (where zeta=-(delta+1/2)).
+ * Note that the transformation matrix is scaled by 2^62 and not 2^59.
+ *
+ * Input: zeta: initial zeta
+ * f0: bottom limb of initial f
+ * g0: bottom limb of initial g
+ * Output: t: transition matrix
+ * Return: final zeta
+ *
+ * Implements the divsteps_n_matrix function from the explanation.
+ */
+static int64_t secp256k1_modinv64_divsteps_59(int64_t zeta, uint64_t f0, uint64_t g0, secp256k1_modinv64_trans2x2 *t) {
+ /* u,v,q,r are the elements of the transformation matrix being built up,
+ * starting with the identity matrix times 8 (because the caller expects
+ * a result scaled by 2^62). Semantically they are signed integers
+ * in range [-2^62,2^62], but here represented as unsigned mod 2^64. This
+ * permits left shifting (which is UB for negative numbers). The range
+ * being inside [-2^63,2^63) means that casting to signed works correctly.
+ */
+ uint64_t u = 8, v = 0, q = 0, r = 8;
+ uint64_t c1, c2, f = f0, g = g0, x, y, z;
+ int i;
+
+ for (i = 3; i < 62; ++i) {
+ VERIFY_CHECK((f & 1) == 1); /* f must always be odd */
+ VERIFY_CHECK((u * f0 + v * g0) == f << i);
+ VERIFY_CHECK((q * f0 + r * g0) == g << i);
+ /* Compute conditional masks for (zeta < 0) and for (g & 1). */
+ c1 = zeta >> 63;
+ c2 = -(g & 1);
+ /* Compute x,y,z, conditionally negated versions of f,u,v. */
+ x = (f ^ c1) - c1;
+ y = (u ^ c1) - c1;
+ z = (v ^ c1) - c1;
+ /* Conditionally add x,y,z to g,q,r. */
+ g += x & c2;
+ q += y & c2;
+ r += z & c2;
+ /* In what follows, c1 is a condition mask for (zeta < 0) and (g & 1). */
+ c1 &= c2;
+ /* Conditionally change zeta into -zeta-2 or zeta-1. */
+ zeta = (zeta ^ c1) - 1;
+ /* Conditionally add g,q,r to f,u,v. */
+ f += g & c1;
+ u += q & c1;
+ v += r & c1;
+ /* Shifts */
+ g >>= 1;
+ u <<= 1;
+ v <<= 1;
+ /* Bounds on zeta that follow from the bounds on iteration count (max 10*59 divsteps). */
+ VERIFY_CHECK(zeta >= -591 && zeta <= 591);
+ }
+ /* Return data in t and return value. */
+ t->u = (int64_t)u;
+ t->v = (int64_t)v;
+ t->q = (int64_t)q;
+ t->r = (int64_t)r;
+ /* The determinant of t must be a power of two. This guarantees that multiplication with t
+ * does not change the gcd of f and g, apart from adding a power-of-2 factor to it (which
+ * will be divided out again). As each divstep's individual matrix has determinant 2, the
+ * aggregate of 59 of them will have determinant 2^59. Multiplying with the initial
+ * 8*identity (which has determinant 2^6) means the overall outputs has determinant
+ * 2^65. */
+ VERIFY_CHECK((int128_t)t->u * t->r - (int128_t)t->v * t->q == ((int128_t)1) << 65);
+ return zeta;
+}
+
+/* Compute the transition matrix and eta for 62 divsteps (variable time, eta=-delta).
+ *
+ * Input: eta: initial eta
+ * f0: bottom limb of initial f
+ * g0: bottom limb of initial g
+ * Output: t: transition matrix
+ * Return: final eta
+ *
+ * Implements the divsteps_n_matrix_var function from the explanation.
+ */
+static int64_t secp256k1_modinv64_divsteps_62_var(int64_t eta, uint64_t f0, uint64_t g0, secp256k1_modinv64_trans2x2 *t) {
+ /* Transformation matrix; see comments in secp256k1_modinv64_divsteps_62. */
+ uint64_t u = 1, v = 0, q = 0, r = 1;
+ uint64_t f = f0, g = g0, m;
+ uint32_t w;
+ int i = 62, limit, zeros;
+
+ for (;;) {
+ /* Use a sentinel bit to count zeros only up to i. */
+ zeros = secp256k1_ctz64_var(g | (UINT64_MAX << i));
+ /* Perform zeros divsteps at once; they all just divide g by two. */
+ g >>= zeros;
+ u <<= zeros;
+ v <<= zeros;
+ eta -= zeros;
+ i -= zeros;
+ /* We're done once we've done 62 divsteps. */
+ if (i == 0) break;
+ VERIFY_CHECK((f & 1) == 1);
+ VERIFY_CHECK((g & 1) == 1);
+ VERIFY_CHECK((u * f0 + v * g0) == f << (62 - i));
+ VERIFY_CHECK((q * f0 + r * g0) == g << (62 - i));
+ /* Bounds on eta that follow from the bounds on iteration count (max 12*62 divsteps). */
+ VERIFY_CHECK(eta >= -745 && eta <= 745);
+ /* If eta is negative, negate it and replace f,g with g,-f. */
+ if (eta < 0) {
+ uint64_t tmp;
+ eta = -eta;
+ tmp = f; f = g; g = -tmp;
+ tmp = u; u = q; q = -tmp;
+ tmp = v; v = r; r = -tmp;
+ /* Use a formula to cancel out up to 6 bits of g. Also, no more than i can be cancelled
+ * out (as we'd be done before that point), and no more than eta+1 can be done as its
+ * will flip again once that happens. */
+ limit = ((int)eta + 1) > i ? i : ((int)eta + 1);
+ VERIFY_CHECK(limit > 0 && limit <= 62);
+ /* m is a mask for the bottom min(limit, 6) bits. */
+ m = (UINT64_MAX >> (64 - limit)) & 63U;
+ /* Find what multiple of f must be added to g to cancel its bottom min(limit, 6)
+ * bits. */
+ w = (f * g * (f * f - 2)) & m;
+ } else {
+ /* In this branch, use a simpler formula that only lets us cancel up to 4 bits of g, as
+ * eta tends to be smaller here. */
+ limit = ((int)eta + 1) > i ? i : ((int)eta + 1);
+ VERIFY_CHECK(limit > 0 && limit <= 62);
+ /* m is a mask for the bottom min(limit, 4) bits. */
+ m = (UINT64_MAX >> (64 - limit)) & 15U;
+ /* Find what multiple of f must be added to g to cancel its bottom min(limit, 4)
+ * bits. */
+ w = f + (((f + 1) & 4) << 1);
+ w = (-w * g) & m;
+ }
+ g += f * w;
+ q += u * w;
+ r += v * w;
+ VERIFY_CHECK((g & m) == 0);
+ }
+ /* Return data in t and return value. */
+ t->u = (int64_t)u;
+ t->v = (int64_t)v;
+ t->q = (int64_t)q;
+ t->r = (int64_t)r;
+ /* The determinant of t must be a power of two. This guarantees that multiplication with t
+ * does not change the gcd of f and g, apart from adding a power-of-2 factor to it (which
+ * will be divided out again). As each divstep's individual matrix has determinant 2, the
+ * aggregate of 62 of them will have determinant 2^62. */
+ VERIFY_CHECK((int128_t)t->u * t->r - (int128_t)t->v * t->q == ((int128_t)1) << 62);
+ return eta;
+}
+
+/* Compute (t/2^62) * [d, e] mod modulus, where t is a transition matrix scaled by 2^62.
+ *
+ * On input and output, d and e are in range (-2*modulus,modulus). All output limbs will be in range
+ * (-2^62,2^62).
+ *
+ * This implements the update_de function from the explanation.
+ */
+static void secp256k1_modinv64_update_de_62(secp256k1_modinv64_signed62 *d, secp256k1_modinv64_signed62 *e, const secp256k1_modinv64_trans2x2 *t, const secp256k1_modinv64_modinfo* modinfo) {
+ const int64_t M62 = (int64_t)(UINT64_MAX >> 2);
+ const int64_t d0 = d->v[0], d1 = d->v[1], d2 = d->v[2], d3 = d->v[3], d4 = d->v[4];
+ const int64_t e0 = e->v[0], e1 = e->v[1], e2 = e->v[2], e3 = e->v[3], e4 = e->v[4];
+ const int64_t u = t->u, v = t->v, q = t->q, r = t->r;
+ int64_t md, me, sd, se;
+ int128_t cd, ce;
+#ifdef VERIFY
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(d, 5, &modinfo->modulus, -2) > 0); /* d > -2*modulus */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(d, 5, &modinfo->modulus, 1) < 0); /* d < modulus */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(e, 5, &modinfo->modulus, -2) > 0); /* e > -2*modulus */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(e, 5, &modinfo->modulus, 1) < 0); /* e < modulus */
+ VERIFY_CHECK((secp256k1_modinv64_abs(u) + secp256k1_modinv64_abs(v)) >= 0); /* |u|+|v| doesn't overflow */
+ VERIFY_CHECK((secp256k1_modinv64_abs(q) + secp256k1_modinv64_abs(r)) >= 0); /* |q|+|r| doesn't overflow */
+ VERIFY_CHECK((secp256k1_modinv64_abs(u) + secp256k1_modinv64_abs(v)) <= M62 + 1); /* |u|+|v| <= 2^62 */
+ VERIFY_CHECK((secp256k1_modinv64_abs(q) + secp256k1_modinv64_abs(r)) <= M62 + 1); /* |q|+|r| <= 2^62 */
+#endif
+ /* [md,me] start as zero; plus [u,q] if d is negative; plus [v,r] if e is negative. */
+ sd = d4 >> 63;
+ se = e4 >> 63;
+ md = (u & sd) + (v & se);
+ me = (q & sd) + (r & se);
+ /* Begin computing t*[d,e]. */
+ cd = (int128_t)u * d0 + (int128_t)v * e0;
+ ce = (int128_t)q * d0 + (int128_t)r * e0;
+ /* Correct md,me so that t*[d,e]+modulus*[md,me] has 62 zero bottom bits. */
+ md -= (modinfo->modulus_inv62 * (uint64_t)cd + md) & M62;
+ me -= (modinfo->modulus_inv62 * (uint64_t)ce + me) & M62;
+ /* Update the beginning of computation for t*[d,e]+modulus*[md,me] now md,me are known. */
+ cd += (int128_t)modinfo->modulus.v[0] * md;
+ ce += (int128_t)modinfo->modulus.v[0] * me;
+ /* Verify that the low 62 bits of the computation are indeed zero, and then throw them away. */
+ VERIFY_CHECK(((int64_t)cd & M62) == 0); cd >>= 62;
+ VERIFY_CHECK(((int64_t)ce & M62) == 0); ce >>= 62;
+ /* Compute limb 1 of t*[d,e]+modulus*[md,me], and store it as output limb 0 (= down shift). */
+ cd += (int128_t)u * d1 + (int128_t)v * e1;
+ ce += (int128_t)q * d1 + (int128_t)r * e1;
+ if (modinfo->modulus.v[1]) { /* Optimize for the case where limb of modulus is zero. */
+ cd += (int128_t)modinfo->modulus.v[1] * md;
+ ce += (int128_t)modinfo->modulus.v[1] * me;
+ }
+ d->v[0] = (int64_t)cd & M62; cd >>= 62;
+ e->v[0] = (int64_t)ce & M62; ce >>= 62;
+ /* Compute limb 2 of t*[d,e]+modulus*[md,me], and store it as output limb 1. */
+ cd += (int128_t)u * d2 + (int128_t)v * e2;
+ ce += (int128_t)q * d2 + (int128_t)r * e2;
+ if (modinfo->modulus.v[2]) { /* Optimize for the case where limb of modulus is zero. */
+ cd += (int128_t)modinfo->modulus.v[2] * md;
+ ce += (int128_t)modinfo->modulus.v[2] * me;
+ }
+ d->v[1] = (int64_t)cd & M62; cd >>= 62;
+ e->v[1] = (int64_t)ce & M62; ce >>= 62;
+ /* Compute limb 3 of t*[d,e]+modulus*[md,me], and store it as output limb 2. */
+ cd += (int128_t)u * d3 + (int128_t)v * e3;
+ ce += (int128_t)q * d3 + (int128_t)r * e3;
+ if (modinfo->modulus.v[3]) { /* Optimize for the case where limb of modulus is zero. */
+ cd += (int128_t)modinfo->modulus.v[3] * md;
+ ce += (int128_t)modinfo->modulus.v[3] * me;
+ }
+ d->v[2] = (int64_t)cd & M62; cd >>= 62;
+ e->v[2] = (int64_t)ce & M62; ce >>= 62;
+ /* Compute limb 4 of t*[d,e]+modulus*[md,me], and store it as output limb 3. */
+ cd += (int128_t)u * d4 + (int128_t)v * e4;
+ ce += (int128_t)q * d4 + (int128_t)r * e4;
+ cd += (int128_t)modinfo->modulus.v[4] * md;
+ ce += (int128_t)modinfo->modulus.v[4] * me;
+ d->v[3] = (int64_t)cd & M62; cd >>= 62;
+ e->v[3] = (int64_t)ce & M62; ce >>= 62;
+ /* What remains is limb 5 of t*[d,e]+modulus*[md,me]; store it as output limb 4. */
+ d->v[4] = (int64_t)cd;
+ e->v[4] = (int64_t)ce;
+#ifdef VERIFY
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(d, 5, &modinfo->modulus, -2) > 0); /* d > -2*modulus */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(d, 5, &modinfo->modulus, 1) < 0); /* d < modulus */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(e, 5, &modinfo->modulus, -2) > 0); /* e > -2*modulus */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(e, 5, &modinfo->modulus, 1) < 0); /* e < modulus */
+#endif
+}
+
+/* Compute (t/2^62) * [f, g], where t is a transition matrix scaled by 2^62.
+ *
+ * This implements the update_fg function from the explanation.
+ */
+static void secp256k1_modinv64_update_fg_62(secp256k1_modinv64_signed62 *f, secp256k1_modinv64_signed62 *g, const secp256k1_modinv64_trans2x2 *t) {
+ const int64_t M62 = (int64_t)(UINT64_MAX >> 2);
+ const int64_t f0 = f->v[0], f1 = f->v[1], f2 = f->v[2], f3 = f->v[3], f4 = f->v[4];
+ const int64_t g0 = g->v[0], g1 = g->v[1], g2 = g->v[2], g3 = g->v[3], g4 = g->v[4];
+ const int64_t u = t->u, v = t->v, q = t->q, r = t->r;
+ int128_t cf, cg;
+ /* Start computing t*[f,g]. */
+ cf = (int128_t)u * f0 + (int128_t)v * g0;
+ cg = (int128_t)q * f0 + (int128_t)r * g0;
+ /* Verify that the bottom 62 bits of the result are zero, and then throw them away. */
+ VERIFY_CHECK(((int64_t)cf & M62) == 0); cf >>= 62;
+ VERIFY_CHECK(((int64_t)cg & M62) == 0); cg >>= 62;
+ /* Compute limb 1 of t*[f,g], and store it as output limb 0 (= down shift). */
+ cf += (int128_t)u * f1 + (int128_t)v * g1;
+ cg += (int128_t)q * f1 + (int128_t)r * g1;
+ f->v[0] = (int64_t)cf & M62; cf >>= 62;
+ g->v[0] = (int64_t)cg & M62; cg >>= 62;
+ /* Compute limb 2 of t*[f,g], and store it as output limb 1. */
+ cf += (int128_t)u * f2 + (int128_t)v * g2;
+ cg += (int128_t)q * f2 + (int128_t)r * g2;
+ f->v[1] = (int64_t)cf & M62; cf >>= 62;
+ g->v[1] = (int64_t)cg & M62; cg >>= 62;
+ /* Compute limb 3 of t*[f,g], and store it as output limb 2. */
+ cf += (int128_t)u * f3 + (int128_t)v * g3;
+ cg += (int128_t)q * f3 + (int128_t)r * g3;
+ f->v[2] = (int64_t)cf & M62; cf >>= 62;
+ g->v[2] = (int64_t)cg & M62; cg >>= 62;
+ /* Compute limb 4 of t*[f,g], and store it as output limb 3. */
+ cf += (int128_t)u * f4 + (int128_t)v * g4;
+ cg += (int128_t)q * f4 + (int128_t)r * g4;
+ f->v[3] = (int64_t)cf & M62; cf >>= 62;
+ g->v[3] = (int64_t)cg & M62; cg >>= 62;
+ /* What remains is limb 5 of t*[f,g]; store it as output limb 4. */
+ f->v[4] = (int64_t)cf;
+ g->v[4] = (int64_t)cg;
+}
+
+/* Compute (t/2^62) * [f, g], where t is a transition matrix for 62 divsteps.
+ *
+ * Version that operates on a variable number of limbs in f and g.
+ *
+ * This implements the update_fg function from the explanation.
+ */
+static void secp256k1_modinv64_update_fg_62_var(int len, secp256k1_modinv64_signed62 *f, secp256k1_modinv64_signed62 *g, const secp256k1_modinv64_trans2x2 *t) {
+ const int64_t M62 = (int64_t)(UINT64_MAX >> 2);
+ const int64_t u = t->u, v = t->v, q = t->q, r = t->r;
+ int64_t fi, gi;
+ int128_t cf, cg;
+ int i;
+ VERIFY_CHECK(len > 0);
+ /* Start computing t*[f,g]. */
+ fi = f->v[0];
+ gi = g->v[0];
+ cf = (int128_t)u * fi + (int128_t)v * gi;
+ cg = (int128_t)q * fi + (int128_t)r * gi;
+ /* Verify that the bottom 62 bits of the result are zero, and then throw them away. */
+ VERIFY_CHECK(((int64_t)cf & M62) == 0); cf >>= 62;
+ VERIFY_CHECK(((int64_t)cg & M62) == 0); cg >>= 62;
+ /* Now iteratively compute limb i=1..len of t*[f,g], and store them in output limb i-1 (shifting
+ * down by 62 bits). */
+ for (i = 1; i < len; ++i) {
+ fi = f->v[i];
+ gi = g->v[i];
+ cf += (int128_t)u * fi + (int128_t)v * gi;
+ cg += (int128_t)q * fi + (int128_t)r * gi;
+ f->v[i - 1] = (int64_t)cf & M62; cf >>= 62;
+ g->v[i - 1] = (int64_t)cg & M62; cg >>= 62;
+ }
+ /* What remains is limb (len) of t*[f,g]; store it as output limb (len-1). */
+ f->v[len - 1] = (int64_t)cf;
+ g->v[len - 1] = (int64_t)cg;
+}
+
+/* Compute the inverse of x modulo modinfo->modulus, and replace x with it (constant time in x). */
+static void secp256k1_modinv64(secp256k1_modinv64_signed62 *x, const secp256k1_modinv64_modinfo *modinfo) {
+ /* Start with d=0, e=1, f=modulus, g=x, zeta=-1. */
+ secp256k1_modinv64_signed62 d = {{0, 0, 0, 0, 0}};
+ secp256k1_modinv64_signed62 e = {{1, 0, 0, 0, 0}};
+ secp256k1_modinv64_signed62 f = modinfo->modulus;
+ secp256k1_modinv64_signed62 g = *x;
+ int i;
+ int64_t zeta = -1; /* zeta = -(delta+1/2); delta starts at 1/2. */
+
+ /* Do 10 iterations of 59 divsteps each = 590 divsteps. This suffices for 256-bit inputs. */
+ for (i = 0; i < 10; ++i) {
+ /* Compute transition matrix and new zeta after 59 divsteps. */
+ secp256k1_modinv64_trans2x2 t;
+ zeta = secp256k1_modinv64_divsteps_59(zeta, f.v[0], g.v[0], &t);
+ /* Update d,e using that transition matrix. */
+ secp256k1_modinv64_update_de_62(&d, &e, &t, modinfo);
+ /* Update f,g using that transition matrix. */
+#ifdef VERIFY
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, 5, &modinfo->modulus, -1) > 0); /* f > -modulus */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, 5, &modinfo->modulus, 1) <= 0); /* f <= modulus */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, 5, &modinfo->modulus, -1) > 0); /* g > -modulus */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, 5, &modinfo->modulus, 1) < 0); /* g < modulus */
+#endif
+ secp256k1_modinv64_update_fg_62(&f, &g, &t);
+#ifdef VERIFY
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, 5, &modinfo->modulus, -1) > 0); /* f > -modulus */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, 5, &modinfo->modulus, 1) <= 0); /* f <= modulus */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, 5, &modinfo->modulus, -1) > 0); /* g > -modulus */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, 5, &modinfo->modulus, 1) < 0); /* g < modulus */
+#endif
+ }
+
+ /* At this point sufficient iterations have been performed that g must have reached 0
+ * and (if g was not originally 0) f must now equal +/- GCD of the initial f, g
+ * values i.e. +/- 1, and d now contains +/- the modular inverse. */
+#ifdef VERIFY
+ /* g == 0 */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, 5, &SECP256K1_SIGNED62_ONE, 0) == 0);
+ /* |f| == 1, or (x == 0 and d == 0 and |f|=modulus) */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, 5, &SECP256K1_SIGNED62_ONE, -1) == 0 ||
+ secp256k1_modinv64_mul_cmp_62(&f, 5, &SECP256K1_SIGNED62_ONE, 1) == 0 ||
+ (secp256k1_modinv64_mul_cmp_62(x, 5, &SECP256K1_SIGNED62_ONE, 0) == 0 &&
+ secp256k1_modinv64_mul_cmp_62(&d, 5, &SECP256K1_SIGNED62_ONE, 0) == 0 &&
+ (secp256k1_modinv64_mul_cmp_62(&f, 5, &modinfo->modulus, 1) == 0 ||
+ secp256k1_modinv64_mul_cmp_62(&f, 5, &modinfo->modulus, -1) == 0)));
+#endif
+
+ /* Optionally negate d, normalize to [0,modulus), and return it. */
+ secp256k1_modinv64_normalize_62(&d, f.v[4], modinfo);
+ *x = d;
+}
+
+/* Compute the inverse of x modulo modinfo->modulus, and replace x with it (variable time). */
+static void secp256k1_modinv64_var(secp256k1_modinv64_signed62 *x, const secp256k1_modinv64_modinfo *modinfo) {
+ /* Start with d=0, e=1, f=modulus, g=x, eta=-1. */
+ secp256k1_modinv64_signed62 d = {{0, 0, 0, 0, 0}};
+ secp256k1_modinv64_signed62 e = {{1, 0, 0, 0, 0}};
+ secp256k1_modinv64_signed62 f = modinfo->modulus;
+ secp256k1_modinv64_signed62 g = *x;
+#ifdef VERIFY
+ int i = 0;
+#endif
+ int j, len = 5;
+ int64_t eta = -1; /* eta = -delta; delta is initially 1 */
+ int64_t cond, fn, gn;
+
+ /* Do iterations of 62 divsteps each until g=0. */
+ while (1) {
+ /* Compute transition matrix and new eta after 62 divsteps. */
+ secp256k1_modinv64_trans2x2 t;
+ eta = secp256k1_modinv64_divsteps_62_var(eta, f.v[0], g.v[0], &t);
+ /* Update d,e using that transition matrix. */
+ secp256k1_modinv64_update_de_62(&d, &e, &t, modinfo);
+ /* Update f,g using that transition matrix. */
+#ifdef VERIFY
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, -1) > 0); /* f > -modulus */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, 1) <= 0); /* f <= modulus */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &modinfo->modulus, -1) > 0); /* g > -modulus */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &modinfo->modulus, 1) < 0); /* g < modulus */
+#endif
+ secp256k1_modinv64_update_fg_62_var(len, &f, &g, &t);
+ /* If the bottom limb of g is zero, there is a chance that g=0. */
+ if (g.v[0] == 0) {
+ cond = 0;
+ /* Check if the other limbs are also 0. */
+ for (j = 1; j < len; ++j) {
+ cond |= g.v[j];
+ }
+ /* If so, we're done. */
+ if (cond == 0) break;
+ }
+
+ /* Determine if len>1 and limb (len-1) of both f and g is 0 or -1. */
+ fn = f.v[len - 1];
+ gn = g.v[len - 1];
+ cond = ((int64_t)len - 2) >> 63;
+ cond |= fn ^ (fn >> 63);
+ cond |= gn ^ (gn >> 63);
+ /* If so, reduce length, propagating the sign of f and g's top limb into the one below. */
+ if (cond == 0) {
+ f.v[len - 2] |= (uint64_t)fn << 62;
+ g.v[len - 2] |= (uint64_t)gn << 62;
+ --len;
+ }
+#ifdef VERIFY
+ VERIFY_CHECK(++i < 12); /* We should never need more than 12*62 = 744 divsteps */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, -1) > 0); /* f > -modulus */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, 1) <= 0); /* f <= modulus */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &modinfo->modulus, -1) > 0); /* g > -modulus */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &modinfo->modulus, 1) < 0); /* g < modulus */
+#endif
+ }
+
+ /* At this point g is 0 and (if g was not originally 0) f must now equal +/- GCD of
+ * the initial f, g values i.e. +/- 1, and d now contains +/- the modular inverse. */
+#ifdef VERIFY
+ /* g == 0 */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &SECP256K1_SIGNED62_ONE, 0) == 0);
+ /* |f| == 1, or (x == 0 and d == 0 and |f|=modulus) */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &SECP256K1_SIGNED62_ONE, -1) == 0 ||
+ secp256k1_modinv64_mul_cmp_62(&f, len, &SECP256K1_SIGNED62_ONE, 1) == 0 ||
+ (secp256k1_modinv64_mul_cmp_62(x, 5, &SECP256K1_SIGNED62_ONE, 0) == 0 &&
+ secp256k1_modinv64_mul_cmp_62(&d, 5, &SECP256K1_SIGNED62_ONE, 0) == 0 &&
+ (secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, 1) == 0 ||
+ secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, -1) == 0)));
+#endif
+
+ /* Optionally negate d, normalize to [0,modulus), and return it. */
+ secp256k1_modinv64_normalize_62(&d, f.v[len - 1], modinfo);
+ *x = d;
+}
+
+#endif /* SECP256K1_MODINV64_IMPL_H */
diff --git a/src/secp256k1/src/modules/ecdh/main_impl.h b/src/secp256k1/src/modules/ecdh/main_impl.h
index 07a25b80d4..1ac67086be 100644
--- a/src/secp256k1/src/modules/ecdh/main_impl.h
+++ b/src/secp256k1/src/modules/ecdh/main_impl.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2015 Andrew Poelstra *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2015 Andrew Poelstra *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_MODULE_ECDH_MAIN_H
#define SECP256K1_MODULE_ECDH_MAIN_H
diff --git a/src/secp256k1/src/modules/ecdh/tests_impl.h b/src/secp256k1/src/modules/ecdh/tests_impl.h
index e8d2aeab9a..be07447a4b 100644
--- a/src/secp256k1/src/modules/ecdh/tests_impl.h
+++ b/src/secp256k1/src/modules/ecdh/tests_impl.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2015 Andrew Poelstra *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2015 Andrew Poelstra *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_MODULE_ECDH_TESTS_H
#define SECP256K1_MODULE_ECDH_TESTS_H
diff --git a/src/secp256k1/src/modules/extrakeys/main_impl.h b/src/secp256k1/src/modules/extrakeys/main_impl.h
index 5378d2f301..7390b22718 100644
--- a/src/secp256k1/src/modules/extrakeys/main_impl.h
+++ b/src/secp256k1/src/modules/extrakeys/main_impl.h
@@ -1,11 +1,11 @@
-/**********************************************************************
- * Copyright (c) 2020 Jonas Nick *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2020 Jonas Nick *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
-#ifndef _SECP256K1_MODULE_EXTRAKEYS_MAIN_
-#define _SECP256K1_MODULE_EXTRAKEYS_MAIN_
+#ifndef SECP256K1_MODULE_EXTRAKEYS_MAIN_H
+#define SECP256K1_MODULE_EXTRAKEYS_MAIN_H
#include "include/secp256k1.h"
#include "include/secp256k1_extrakeys.h"
@@ -180,12 +180,22 @@ int secp256k1_keypair_create(const secp256k1_context* ctx, secp256k1_keypair *ke
ret = secp256k1_ec_pubkey_create_helper(&ctx->ecmult_gen_ctx, &sk, &pk, seckey32);
secp256k1_keypair_save(keypair, &sk, &pk);
- memczero(keypair, sizeof(*keypair), !ret);
+ secp256k1_memczero(keypair, sizeof(*keypair), !ret);
secp256k1_scalar_clear(&sk);
return ret;
}
+int secp256k1_keypair_sec(const secp256k1_context* ctx, unsigned char *seckey, const secp256k1_keypair *keypair) {
+ VERIFY_CHECK(ctx != NULL);
+ ARG_CHECK(seckey != NULL);
+ memset(seckey, 0, 32);
+ ARG_CHECK(keypair != NULL);
+
+ memcpy(seckey, &keypair->data[0], 32);
+ return 1;
+}
+
int secp256k1_keypair_pub(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const secp256k1_keypair *keypair) {
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(pubkey != NULL);
diff --git a/src/secp256k1/src/modules/extrakeys/tests_exhaustive_impl.h b/src/secp256k1/src/modules/extrakeys/tests_exhaustive_impl.h
index 0e29bc6b09..0aca4fb72d 100644
--- a/src/secp256k1/src/modules/extrakeys/tests_exhaustive_impl.h
+++ b/src/secp256k1/src/modules/extrakeys/tests_exhaustive_impl.h
@@ -1,11 +1,11 @@
-/**********************************************************************
- * Copyright (c) 2020 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2020 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
-#ifndef _SECP256K1_MODULE_EXTRAKEYS_TESTS_EXHAUSTIVE_
-#define _SECP256K1_MODULE_EXTRAKEYS_TESTS_EXHAUSTIVE_
+#ifndef SECP256K1_MODULE_EXTRAKEYS_TESTS_EXHAUSTIVE_H
+#define SECP256K1_MODULE_EXTRAKEYS_TESTS_EXHAUSTIVE_H
#include "src/modules/extrakeys/main_impl.h"
#include "include/secp256k1_extrakeys.h"
diff --git a/src/secp256k1/src/modules/extrakeys/tests_impl.h b/src/secp256k1/src/modules/extrakeys/tests_impl.h
index 5ee135849e..9473a7dd48 100644
--- a/src/secp256k1/src/modules/extrakeys/tests_impl.h
+++ b/src/secp256k1/src/modules/extrakeys/tests_impl.h
@@ -1,11 +1,11 @@
-/**********************************************************************
- * Copyright (c) 2020 Jonas Nick *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2020 Jonas Nick *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
-#ifndef _SECP256K1_MODULE_EXTRAKEYS_TESTS_
-#define _SECP256K1_MODULE_EXTRAKEYS_TESTS_
+#ifndef SECP256K1_MODULE_EXTRAKEYS_TESTS_H
+#define SECP256K1_MODULE_EXTRAKEYS_TESTS_H
#include "secp256k1_extrakeys.h"
@@ -311,6 +311,7 @@ void test_xonly_pubkey_tweak_recursive(void) {
void test_keypair(void) {
unsigned char sk[32];
+ unsigned char sk_tmp[32];
unsigned char zeros96[96] = { 0 };
unsigned char overflows[32];
secp256k1_keypair keypair;
@@ -396,6 +397,28 @@ void test_keypair(void) {
CHECK(secp256k1_memcmp_var(&xonly_pk, &xonly_pk_tmp, sizeof(pk)) == 0);
CHECK(pk_parity == pk_parity_tmp);
+ /* Test keypair_seckey */
+ ecount = 0;
+ secp256k1_testrand256(sk);
+ CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
+ CHECK(secp256k1_keypair_sec(none, sk_tmp, &keypair) == 1);
+ CHECK(secp256k1_keypair_sec(none, NULL, &keypair) == 0);
+ CHECK(ecount == 1);
+ CHECK(secp256k1_keypair_sec(none, sk_tmp, NULL) == 0);
+ CHECK(ecount == 2);
+ CHECK(secp256k1_memcmp_var(zeros96, sk_tmp, sizeof(sk_tmp)) == 0);
+
+ /* keypair returns the same seckey it got */
+ CHECK(secp256k1_keypair_create(sign, &keypair, sk) == 1);
+ CHECK(secp256k1_keypair_sec(none, sk_tmp, &keypair) == 1);
+ CHECK(secp256k1_memcmp_var(sk, sk_tmp, sizeof(sk_tmp)) == 0);
+
+
+ /* Using an invalid keypair is fine for keypair_seckey */
+ memset(&keypair, 0, sizeof(keypair));
+ CHECK(secp256k1_keypair_sec(none, sk_tmp, &keypair) == 1);
+ CHECK(secp256k1_memcmp_var(zeros96, sk_tmp, sizeof(sk_tmp)) == 0);
+
secp256k1_context_destroy(none);
secp256k1_context_destroy(sign);
secp256k1_context_destroy(verify);
@@ -484,6 +507,7 @@ void test_keypair_add(void) {
secp256k1_pubkey output_pk_xy;
secp256k1_pubkey output_pk_expected;
unsigned char pk32[32];
+ unsigned char sk32[32];
int pk_parity;
secp256k1_testrand256(tweak);
@@ -501,7 +525,8 @@ void test_keypair_add(void) {
CHECK(secp256k1_memcmp_var(&output_pk_xy, &output_pk_expected, sizeof(output_pk_xy)) == 0);
/* Check that the secret key in the keypair is tweaked correctly */
- CHECK(secp256k1_ec_pubkey_create(ctx, &output_pk_expected, &keypair.data[0]) == 1);
+ CHECK(secp256k1_keypair_sec(none, sk32, &keypair) == 1);
+ CHECK(secp256k1_ec_pubkey_create(ctx, &output_pk_expected, sk32) == 1);
CHECK(secp256k1_memcmp_var(&output_pk_xy, &output_pk_expected, sizeof(output_pk_xy)) == 0);
}
secp256k1_context_destroy(none);
diff --git a/src/secp256k1/src/modules/recovery/main_impl.h b/src/secp256k1/src/modules/recovery/main_impl.h
index e2576aa953..7a440a729b 100644
--- a/src/secp256k1/src/modules/recovery/main_impl.h
+++ b/src/secp256k1/src/modules/recovery/main_impl.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2013-2015 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013-2015 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_MODULE_RECOVERY_MAIN_H
#define SECP256K1_MODULE_RECOVERY_MAIN_H
@@ -120,34 +120,34 @@ static int secp256k1_ecdsa_sig_recover(const secp256k1_ecmult_context *ctx, cons
return !secp256k1_gej_is_infinity(&qj);
}
-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) {
+int secp256k1_ecdsa_sign_recoverable(const secp256k1_context* ctx, secp256k1_ecdsa_recoverable_signature *signature, const unsigned char *msghash32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void* noncedata) {
secp256k1_scalar r, s;
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(msghash32 != NULL);
ARG_CHECK(signature != NULL);
ARG_CHECK(seckey != NULL);
- ret = secp256k1_ecdsa_sign_inner(ctx, &r, &s, &recid, msg32, seckey, noncefp, noncedata);
+ ret = secp256k1_ecdsa_sign_inner(ctx, &r, &s, &recid, msghash32, seckey, noncefp, noncedata);
secp256k1_ecdsa_recoverable_signature_save(signature, &r, &s, recid);
return ret;
}
-int secp256k1_ecdsa_recover(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const secp256k1_ecdsa_recoverable_signature *signature, const unsigned char *msg32) {
+int secp256k1_ecdsa_recover(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const secp256k1_ecdsa_recoverable_signature *signature, const unsigned char *msghash32) {
secp256k1_ge q;
secp256k1_scalar r, s;
secp256k1_scalar m;
int recid;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
- ARG_CHECK(msg32 != NULL);
+ ARG_CHECK(msghash32 != NULL);
ARG_CHECK(signature != NULL);
ARG_CHECK(pubkey != NULL);
secp256k1_ecdsa_recoverable_signature_load(ctx, &r, &s, &recid, signature);
VERIFY_CHECK(recid >= 0 && recid < 4); /* should have been caught in parse_compact */
- secp256k1_scalar_set_b32(&m, msg32, NULL);
+ secp256k1_scalar_set_b32(&m, msghash32, NULL);
if (secp256k1_ecdsa_sig_recover(&ctx->ecmult_ctx, &r, &s, &q, &m, recid)) {
secp256k1_pubkey_save(pubkey, &q);
return 1;
diff --git a/src/secp256k1/src/modules/recovery/tests_exhaustive_impl.h b/src/secp256k1/src/modules/recovery/tests_exhaustive_impl.h
index a2f381d77a..0ba9409c69 100644
--- a/src/secp256k1/src/modules/recovery/tests_exhaustive_impl.h
+++ b/src/secp256k1/src/modules/recovery/tests_exhaustive_impl.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2016 Andrew Poelstra *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2016 Andrew Poelstra *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_MODULE_RECOVERY_EXHAUSTIVE_TESTS_H
#define SECP256K1_MODULE_RECOVERY_EXHAUSTIVE_TESTS_H
diff --git a/src/secp256k1/src/modules/recovery/tests_impl.h b/src/secp256k1/src/modules/recovery/tests_impl.h
index 09cae38403..40dba87ce3 100644
--- a/src/secp256k1/src/modules/recovery/tests_impl.h
+++ b/src/secp256k1/src/modules/recovery/tests_impl.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2013-2015 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013-2015 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_MODULE_RECOVERY_TESTS_H
#define SECP256K1_MODULE_RECOVERY_TESTS_H
diff --git a/src/secp256k1/src/modules/schnorrsig/main_impl.h b/src/secp256k1/src/modules/schnorrsig/main_impl.h
index b0d8481f9b..22e1b33a5a 100644
--- a/src/secp256k1/src/modules/schnorrsig/main_impl.h
+++ b/src/secp256k1/src/modules/schnorrsig/main_impl.h
@@ -1,11 +1,11 @@
-/**********************************************************************
- * Copyright (c) 2018-2020 Andrew Poelstra, Jonas Nick *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2018-2020 Andrew Poelstra, Jonas Nick *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
-#ifndef _SECP256K1_MODULE_SCHNORRSIG_MAIN_
-#define _SECP256K1_MODULE_SCHNORRSIG_MAIN_
+#ifndef SECP256K1_MODULE_SCHNORRSIG_MAIN_H
+#define SECP256K1_MODULE_SCHNORRSIG_MAIN_H
#include "include/secp256k1.h"
#include "include/secp256k1_schnorrsig.h"
@@ -179,7 +179,7 @@ int secp256k1_schnorrsig_sign(const secp256k1_context* ctx, unsigned char *sig64
secp256k1_scalar_add(&e, &e, &k);
secp256k1_scalar_get_b32(&sig64[32], &e);
- memczero(sig64, 64, !ret);
+ secp256k1_memczero(sig64, 64, !ret);
secp256k1_scalar_clear(&k);
secp256k1_scalar_clear(&sk);
memset(seckey, 0, sizeof(seckey));
diff --git a/src/secp256k1/src/modules/schnorrsig/tests_exhaustive_impl.h b/src/secp256k1/src/modules/schnorrsig/tests_exhaustive_impl.h
index 4bf0bc1680..b4a428729f 100644
--- a/src/secp256k1/src/modules/schnorrsig/tests_exhaustive_impl.h
+++ b/src/secp256k1/src/modules/schnorrsig/tests_exhaustive_impl.h
@@ -1,11 +1,11 @@
-/**********************************************************************
- * Copyright (c) 2020 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2020 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
-#ifndef _SECP256K1_MODULE_SCHNORRSIG_TESTS_EXHAUSTIVE_
-#define _SECP256K1_MODULE_SCHNORRSIG_TESTS_EXHAUSTIVE_
+#ifndef SECP256K1_MODULE_SCHNORRSIG_TESTS_EXHAUSTIVE_H
+#define SECP256K1_MODULE_SCHNORRSIG_TESTS_EXHAUSTIVE_H
#include "include/secp256k1_schnorrsig.h"
#include "src/modules/schnorrsig/main_impl.h"
diff --git a/src/secp256k1/src/modules/schnorrsig/tests_impl.h b/src/secp256k1/src/modules/schnorrsig/tests_impl.h
index f522fcb320..338462fc9d 100644
--- a/src/secp256k1/src/modules/schnorrsig/tests_impl.h
+++ b/src/secp256k1/src/modules/schnorrsig/tests_impl.h
@@ -1,11 +1,11 @@
-/**********************************************************************
- * Copyright (c) 2018-2020 Andrew Poelstra, Jonas Nick *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
-
-#ifndef _SECP256K1_MODULE_SCHNORRSIG_TESTS_
-#define _SECP256K1_MODULE_SCHNORRSIG_TESTS_
+/***********************************************************************
+ * Copyright (c) 2018-2020 Andrew Poelstra, Jonas Nick *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
+
+#ifndef SECP256K1_MODULE_SCHNORRSIG_TESTS_H
+#define SECP256K1_MODULE_SCHNORRSIG_TESTS_H
#include "secp256k1_schnorrsig.h"
diff --git a/src/secp256k1/src/num.h b/src/secp256k1/src/num.h
deleted file mode 100644
index 49f2dd791d..0000000000
--- a/src/secp256k1/src/num.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
-
-#ifndef SECP256K1_NUM_H
-#define SECP256K1_NUM_H
-
-#ifndef USE_NUM_NONE
-
-#if defined HAVE_CONFIG_H
-#include "libsecp256k1-config.h"
-#endif
-
-#if defined(USE_NUM_GMP)
-#include "num_gmp.h"
-#else
-#error "Please select num implementation"
-#endif
-
-/** Copy a number. */
-static void secp256k1_num_copy(secp256k1_num *r, const secp256k1_num *a);
-
-/** Convert a number's absolute value to a binary big-endian string.
- * There must be enough place. */
-static void secp256k1_num_get_bin(unsigned char *r, unsigned int rlen, const secp256k1_num *a);
-
-/** Set a number to the value of a binary big-endian string. */
-static void secp256k1_num_set_bin(secp256k1_num *r, const unsigned char *a, unsigned int alen);
-
-/** Compute a modular inverse. The input must be less than the modulus. */
-static void secp256k1_num_mod_inverse(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *m);
-
-/** Compute the jacobi symbol (a|b). b must be positive and odd. */
-static int secp256k1_num_jacobi(const secp256k1_num *a, const secp256k1_num *b);
-
-/** Compare the absolute value of two numbers. */
-static int secp256k1_num_cmp(const secp256k1_num *a, const secp256k1_num *b);
-
-/** Test whether two number are equal (including sign). */
-static int secp256k1_num_eq(const secp256k1_num *a, const secp256k1_num *b);
-
-/** Add two (signed) numbers. */
-static void secp256k1_num_add(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b);
-
-/** Subtract two (signed) numbers. */
-static void secp256k1_num_sub(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b);
-
-/** Multiply two (signed) numbers. */
-static void secp256k1_num_mul(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b);
-
-/** Replace a number by its remainder modulo m. M's sign is ignored. The result is a number between 0 and m-1,
- even if r was negative. */
-static void secp256k1_num_mod(secp256k1_num *r, const secp256k1_num *m);
-
-/** Right-shift the passed number by bits bits. */
-static void secp256k1_num_shift(secp256k1_num *r, int bits);
-
-/** Check whether a number is zero. */
-static int secp256k1_num_is_zero(const secp256k1_num *a);
-
-/** Check whether a number is one. */
-static int secp256k1_num_is_one(const secp256k1_num *a);
-
-/** Check whether a number is strictly negative. */
-static int secp256k1_num_is_neg(const secp256k1_num *a);
-
-/** Change a number's sign. */
-static void secp256k1_num_negate(secp256k1_num *r);
-
-#endif
-
-#endif /* SECP256K1_NUM_H */
diff --git a/src/secp256k1/src/num_gmp.h b/src/secp256k1/src/num_gmp.h
deleted file mode 100644
index 3619844bd5..0000000000
--- a/src/secp256k1/src/num_gmp.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
-
-#ifndef SECP256K1_NUM_REPR_H
-#define SECP256K1_NUM_REPR_H
-
-#include <gmp.h>
-
-#define NUM_LIMBS ((256+GMP_NUMB_BITS-1)/GMP_NUMB_BITS)
-
-typedef struct {
- mp_limb_t data[2*NUM_LIMBS];
- int neg;
- int limbs;
-} secp256k1_num;
-
-#endif /* SECP256K1_NUM_REPR_H */
diff --git a/src/secp256k1/src/num_gmp_impl.h b/src/secp256k1/src/num_gmp_impl.h
deleted file mode 100644
index 0ae2a8ba0e..0000000000
--- a/src/secp256k1/src/num_gmp_impl.h
+++ /dev/null
@@ -1,288 +0,0 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
-
-#ifndef SECP256K1_NUM_REPR_IMPL_H
-#define SECP256K1_NUM_REPR_IMPL_H
-
-#include <string.h>
-#include <stdlib.h>
-#include <gmp.h>
-
-#include "util.h"
-#include "num.h"
-
-#ifdef VERIFY
-static void secp256k1_num_sanity(const secp256k1_num *a) {
- VERIFY_CHECK(a->limbs == 1 || (a->limbs > 1 && a->data[a->limbs-1] != 0));
-}
-#else
-#define secp256k1_num_sanity(a) do { } while(0)
-#endif
-
-static void secp256k1_num_copy(secp256k1_num *r, const secp256k1_num *a) {
- *r = *a;
-}
-
-static void secp256k1_num_get_bin(unsigned char *r, unsigned int rlen, const secp256k1_num *a) {
- unsigned char tmp[65];
- int len = 0;
- int shift = 0;
- if (a->limbs>1 || a->data[0] != 0) {
- len = mpn_get_str(tmp, 256, (mp_limb_t*)a->data, a->limbs);
- }
- while (shift < len && tmp[shift] == 0) shift++;
- VERIFY_CHECK(len-shift <= (int)rlen);
- memset(r, 0, rlen - len + shift);
- if (len > shift) {
- memcpy(r + rlen - len + shift, tmp + shift, len - shift);
- }
- memset(tmp, 0, sizeof(tmp));
-}
-
-static void secp256k1_num_set_bin(secp256k1_num *r, const unsigned char *a, unsigned int alen) {
- int len;
- VERIFY_CHECK(alen > 0);
- VERIFY_CHECK(alen <= 64);
- len = mpn_set_str(r->data, a, alen, 256);
- if (len == 0) {
- r->data[0] = 0;
- len = 1;
- }
- VERIFY_CHECK(len <= NUM_LIMBS*2);
- r->limbs = len;
- r->neg = 0;
- while (r->limbs > 1 && r->data[r->limbs-1]==0) {
- r->limbs--;
- }
-}
-
-static void secp256k1_num_add_abs(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b) {
- mp_limb_t c = mpn_add(r->data, a->data, a->limbs, b->data, b->limbs);
- r->limbs = a->limbs;
- if (c != 0) {
- VERIFY_CHECK(r->limbs < 2*NUM_LIMBS);
- r->data[r->limbs++] = c;
- }
-}
-
-static void secp256k1_num_sub_abs(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b) {
- mp_limb_t c = mpn_sub(r->data, a->data, a->limbs, b->data, b->limbs);
- (void)c;
- VERIFY_CHECK(c == 0);
- r->limbs = a->limbs;
- while (r->limbs > 1 && r->data[r->limbs-1]==0) {
- r->limbs--;
- }
-}
-
-static void secp256k1_num_mod(secp256k1_num *r, const secp256k1_num *m) {
- secp256k1_num_sanity(r);
- secp256k1_num_sanity(m);
-
- if (r->limbs >= m->limbs) {
- mp_limb_t t[2*NUM_LIMBS];
- mpn_tdiv_qr(t, r->data, 0, r->data, r->limbs, m->data, m->limbs);
- memset(t, 0, sizeof(t));
- r->limbs = m->limbs;
- while (r->limbs > 1 && r->data[r->limbs-1]==0) {
- r->limbs--;
- }
- }
-
- if (r->neg && (r->limbs > 1 || r->data[0] != 0)) {
- secp256k1_num_sub_abs(r, m, r);
- r->neg = 0;
- }
-}
-
-static void secp256k1_num_mod_inverse(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *m) {
- int i;
- mp_limb_t g[NUM_LIMBS+1];
- mp_limb_t u[NUM_LIMBS+1];
- mp_limb_t v[NUM_LIMBS+1];
- mp_size_t sn;
- mp_size_t gn;
- secp256k1_num_sanity(a);
- secp256k1_num_sanity(m);
-
- /** mpn_gcdext computes: (G,S) = gcdext(U,V), where
- * * G = gcd(U,V)
- * * G = U*S + V*T
- * * U has equal or more limbs than V, and V has no padding
- * If we set U to be (a padded version of) a, and V = m:
- * G = a*S + m*T
- * G = a*S mod m
- * Assuming G=1:
- * S = 1/a mod m
- */
- VERIFY_CHECK(m->limbs <= NUM_LIMBS);
- VERIFY_CHECK(m->data[m->limbs-1] != 0);
- for (i = 0; i < m->limbs; i++) {
- u[i] = (i < a->limbs) ? a->data[i] : 0;
- v[i] = m->data[i];
- }
- sn = NUM_LIMBS+1;
- gn = mpn_gcdext(g, r->data, &sn, u, m->limbs, v, m->limbs);
- (void)gn;
- VERIFY_CHECK(gn == 1);
- VERIFY_CHECK(g[0] == 1);
- r->neg = a->neg ^ m->neg;
- if (sn < 0) {
- mpn_sub(r->data, m->data, m->limbs, r->data, -sn);
- r->limbs = m->limbs;
- while (r->limbs > 1 && r->data[r->limbs-1]==0) {
- r->limbs--;
- }
- } else {
- r->limbs = sn;
- }
- memset(g, 0, sizeof(g));
- memset(u, 0, sizeof(u));
- memset(v, 0, sizeof(v));
-}
-
-static int secp256k1_num_jacobi(const secp256k1_num *a, const secp256k1_num *b) {
- int ret;
- mpz_t ga, gb;
- secp256k1_num_sanity(a);
- secp256k1_num_sanity(b);
- VERIFY_CHECK(!b->neg && (b->limbs > 0) && (b->data[0] & 1));
-
- mpz_inits(ga, gb, NULL);
-
- mpz_import(gb, b->limbs, -1, sizeof(mp_limb_t), 0, 0, b->data);
- mpz_import(ga, a->limbs, -1, sizeof(mp_limb_t), 0, 0, a->data);
- if (a->neg) {
- mpz_neg(ga, ga);
- }
-
- ret = mpz_jacobi(ga, gb);
-
- mpz_clears(ga, gb, NULL);
-
- return ret;
-}
-
-static int secp256k1_num_is_one(const secp256k1_num *a) {
- return (a->limbs == 1 && a->data[0] == 1);
-}
-
-static int secp256k1_num_is_zero(const secp256k1_num *a) {
- return (a->limbs == 1 && a->data[0] == 0);
-}
-
-static int secp256k1_num_is_neg(const secp256k1_num *a) {
- return (a->limbs > 1 || a->data[0] != 0) && a->neg;
-}
-
-static int secp256k1_num_cmp(const secp256k1_num *a, const secp256k1_num *b) {
- if (a->limbs > b->limbs) {
- return 1;
- }
- if (a->limbs < b->limbs) {
- return -1;
- }
- return mpn_cmp(a->data, b->data, a->limbs);
-}
-
-static int secp256k1_num_eq(const secp256k1_num *a, const secp256k1_num *b) {
- if (a->limbs > b->limbs) {
- return 0;
- }
- if (a->limbs < b->limbs) {
- return 0;
- }
- if ((a->neg && !secp256k1_num_is_zero(a)) != (b->neg && !secp256k1_num_is_zero(b))) {
- return 0;
- }
- return mpn_cmp(a->data, b->data, a->limbs) == 0;
-}
-
-static void secp256k1_num_subadd(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b, int bneg) {
- if (!(b->neg ^ bneg ^ a->neg)) { /* a and b have the same sign */
- r->neg = a->neg;
- if (a->limbs >= b->limbs) {
- secp256k1_num_add_abs(r, a, b);
- } else {
- secp256k1_num_add_abs(r, b, a);
- }
- } else {
- if (secp256k1_num_cmp(a, b) > 0) {
- r->neg = a->neg;
- secp256k1_num_sub_abs(r, a, b);
- } else {
- r->neg = b->neg ^ bneg;
- secp256k1_num_sub_abs(r, b, a);
- }
- }
-}
-
-static void secp256k1_num_add(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b) {
- secp256k1_num_sanity(a);
- secp256k1_num_sanity(b);
- secp256k1_num_subadd(r, a, b, 0);
-}
-
-static void secp256k1_num_sub(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b) {
- secp256k1_num_sanity(a);
- secp256k1_num_sanity(b);
- secp256k1_num_subadd(r, a, b, 1);
-}
-
-static void secp256k1_num_mul(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b) {
- mp_limb_t tmp[2*NUM_LIMBS+1];
- secp256k1_num_sanity(a);
- secp256k1_num_sanity(b);
-
- VERIFY_CHECK(a->limbs + b->limbs <= 2*NUM_LIMBS+1);
- if ((a->limbs==1 && a->data[0]==0) || (b->limbs==1 && b->data[0]==0)) {
- r->limbs = 1;
- r->neg = 0;
- r->data[0] = 0;
- return;
- }
- if (a->limbs >= b->limbs) {
- mpn_mul(tmp, a->data, a->limbs, b->data, b->limbs);
- } else {
- mpn_mul(tmp, b->data, b->limbs, a->data, a->limbs);
- }
- r->limbs = a->limbs + b->limbs;
- if (r->limbs > 1 && tmp[r->limbs - 1]==0) {
- r->limbs--;
- }
- VERIFY_CHECK(r->limbs <= 2*NUM_LIMBS);
- mpn_copyi(r->data, tmp, r->limbs);
- r->neg = a->neg ^ b->neg;
- memset(tmp, 0, sizeof(tmp));
-}
-
-static void secp256k1_num_shift(secp256k1_num *r, int bits) {
- if (bits % GMP_NUMB_BITS) {
- /* Shift within limbs. */
- mpn_rshift(r->data, r->data, r->limbs, bits % GMP_NUMB_BITS);
- }
- if (bits >= GMP_NUMB_BITS) {
- int i;
- /* Shift full limbs. */
- for (i = 0; i < r->limbs; i++) {
- int index = i + (bits / GMP_NUMB_BITS);
- if (index < r->limbs && index < 2*NUM_LIMBS) {
- r->data[i] = r->data[index];
- } else {
- r->data[i] = 0;
- }
- }
- }
- while (r->limbs>1 && r->data[r->limbs-1]==0) {
- r->limbs--;
- }
-}
-
-static void secp256k1_num_negate(secp256k1_num *r) {
- r->neg ^= 1;
-}
-
-#endif /* SECP256K1_NUM_REPR_IMPL_H */
diff --git a/src/secp256k1/src/num_impl.h b/src/secp256k1/src/num_impl.h
deleted file mode 100644
index c45193b033..0000000000
--- a/src/secp256k1/src/num_impl.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
-
-#ifndef SECP256K1_NUM_IMPL_H
-#define SECP256K1_NUM_IMPL_H
-
-#if defined HAVE_CONFIG_H
-#include "libsecp256k1-config.h"
-#endif
-
-#include "num.h"
-
-#if defined(USE_NUM_GMP)
-#include "num_gmp_impl.h"
-#elif defined(USE_NUM_NONE)
-/* Nothing. */
-#else
-#error "Please select num implementation"
-#endif
-
-#endif /* SECP256K1_NUM_IMPL_H */
diff --git a/src/secp256k1/src/scalar.h b/src/secp256k1/src/scalar.h
index fb3fb187ce..aaaa3d8827 100644
--- a/src/secp256k1/src/scalar.h
+++ b/src/secp256k1/src/scalar.h
@@ -1,13 +1,12 @@
-/**********************************************************************
- * Copyright (c) 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_SCALAR_H
#define SECP256K1_SCALAR_H
-#include "num.h"
#include "util.h"
#if defined HAVE_CONFIG_H
@@ -63,9 +62,6 @@ static void secp256k1_scalar_mul(secp256k1_scalar *r, const secp256k1_scalar *a,
* the low bits that were shifted off */
static int secp256k1_scalar_shr_int(secp256k1_scalar *r, int n);
-/** Compute the square of a scalar (modulo the group order). */
-static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a);
-
/** Compute the inverse of a scalar (modulo the group order). */
static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar *a);
@@ -91,14 +87,6 @@ static int secp256k1_scalar_is_high(const secp256k1_scalar *a);
* Returns -1 if the number was negated, 1 otherwise */
static int secp256k1_scalar_cond_negate(secp256k1_scalar *a, int flag);
-#ifndef USE_NUM_NONE
-/** Convert a scalar to a number. */
-static void secp256k1_scalar_get_num(secp256k1_num *r, const secp256k1_scalar *a);
-
-/** Get the order of the group as a number. */
-static void secp256k1_scalar_order_get_num(secp256k1_num *r);
-#endif
-
/** Compare two scalars. */
static int secp256k1_scalar_eq(const secp256k1_scalar *a, const secp256k1_scalar *b);
diff --git a/src/secp256k1/src/scalar_4x64.h b/src/secp256k1/src/scalar_4x64.h
index 19c7495d1c..700964291e 100644
--- a/src/secp256k1/src/scalar_4x64.h
+++ b/src/secp256k1/src/scalar_4x64.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_SCALAR_REPR_H
#define SECP256K1_SCALAR_REPR_H
diff --git a/src/secp256k1/src/scalar_4x64_impl.h b/src/secp256k1/src/scalar_4x64_impl.h
index 73cbd5e18a..a1def26fca 100644
--- a/src/secp256k1/src/scalar_4x64_impl.h
+++ b/src/secp256k1/src/scalar_4x64_impl.h
@@ -1,12 +1,14 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013, 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_SCALAR_REPR_IMPL_H
#define SECP256K1_SCALAR_REPR_IMPL_H
+#include "modinv64_impl.h"
+
/* Limbs of the secp256k1 order. */
#define SECP256K1_N_0 ((uint64_t)0xBFD25E8CD0364141ULL)
#define SECP256K1_N_1 ((uint64_t)0xBAAEDCE6AF48A03BULL)
@@ -212,28 +214,6 @@ static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) {
VERIFY_CHECK(c1 >= th); \
}
-/** Add 2*a*b to the number defined by (c0,c1,c2). c2 must never overflow. */
-#define muladd2(a,b) { \
- uint64_t tl, th, th2, tl2; \
- { \
- uint128_t t = (uint128_t)a * b; \
- th = t >> 64; /* at most 0xFFFFFFFFFFFFFFFE */ \
- tl = t; \
- } \
- th2 = th + th; /* at most 0xFFFFFFFFFFFFFFFE (in case th was 0x7FFFFFFFFFFFFFFF) */ \
- c2 += (th2 < th); /* never overflows by contract (verified the next line) */ \
- VERIFY_CHECK((th2 >= th) || (c2 != 0)); \
- tl2 = tl + tl; /* at most 0xFFFFFFFFFFFFFFFE (in case the lowest 63 bits of tl were 0x7FFFFFFFFFFFFFFF) */ \
- th2 += (tl2 < tl); /* at most 0xFFFFFFFFFFFFFFFF */ \
- c0 += tl2; /* overflow is handled on the next line */ \
- th2 += (c0 < tl2); /* second overflow is handled on the next line */ \
- c2 += (c0 < tl2) & (th2 == 0); /* never overflows by contract (verified the next line) */ \
- VERIFY_CHECK((c0 >= tl2) || (th2 != 0) || (c2 != 0)); \
- c1 += th2; /* overflow is handled on the next line */ \
- c2 += (c1 < th2); /* never overflows by contract (verified the next line) */ \
- VERIFY_CHECK((c1 >= th2) || (c2 != 0)); \
-}
-
/** Add a to the number defined by (c0,c1,c2). c2 must never overflow. */
#define sumadd(a) { \
unsigned int over; \
@@ -743,148 +723,10 @@ static void secp256k1_scalar_mul_512(uint64_t l[8], const secp256k1_scalar *a, c
#endif
}
-static void secp256k1_scalar_sqr_512(uint64_t l[8], const secp256k1_scalar *a) {
-#ifdef USE_ASM_X86_64
- __asm__ __volatile__(
- /* Preload */
- "movq 0(%%rdi), %%r11\n"
- "movq 8(%%rdi), %%r12\n"
- "movq 16(%%rdi), %%r13\n"
- "movq 24(%%rdi), %%r14\n"
- /* (rax,rdx) = a0 * a0 */
- "movq %%r11, %%rax\n"
- "mulq %%r11\n"
- /* Extract l0 */
- "movq %%rax, 0(%%rsi)\n"
- /* (r8,r9,r10) = (rdx,0) */
- "movq %%rdx, %%r8\n"
- "xorq %%r9, %%r9\n"
- "xorq %%r10, %%r10\n"
- /* (r8,r9,r10) += 2 * a0 * a1 */
- "movq %%r11, %%rax\n"
- "mulq %%r12\n"
- "addq %%rax, %%r8\n"
- "adcq %%rdx, %%r9\n"
- "adcq $0, %%r10\n"
- "addq %%rax, %%r8\n"
- "adcq %%rdx, %%r9\n"
- "adcq $0, %%r10\n"
- /* Extract l1 */
- "movq %%r8, 8(%%rsi)\n"
- "xorq %%r8, %%r8\n"
- /* (r9,r10,r8) += 2 * a0 * a2 */
- "movq %%r11, %%rax\n"
- "mulq %%r13\n"
- "addq %%rax, %%r9\n"
- "adcq %%rdx, %%r10\n"
- "adcq $0, %%r8\n"
- "addq %%rax, %%r9\n"
- "adcq %%rdx, %%r10\n"
- "adcq $0, %%r8\n"
- /* (r9,r10,r8) += a1 * a1 */
- "movq %%r12, %%rax\n"
- "mulq %%r12\n"
- "addq %%rax, %%r9\n"
- "adcq %%rdx, %%r10\n"
- "adcq $0, %%r8\n"
- /* Extract l2 */
- "movq %%r9, 16(%%rsi)\n"
- "xorq %%r9, %%r9\n"
- /* (r10,r8,r9) += 2 * a0 * a3 */
- "movq %%r11, %%rax\n"
- "mulq %%r14\n"
- "addq %%rax, %%r10\n"
- "adcq %%rdx, %%r8\n"
- "adcq $0, %%r9\n"
- "addq %%rax, %%r10\n"
- "adcq %%rdx, %%r8\n"
- "adcq $0, %%r9\n"
- /* (r10,r8,r9) += 2 * a1 * a2 */
- "movq %%r12, %%rax\n"
- "mulq %%r13\n"
- "addq %%rax, %%r10\n"
- "adcq %%rdx, %%r8\n"
- "adcq $0, %%r9\n"
- "addq %%rax, %%r10\n"
- "adcq %%rdx, %%r8\n"
- "adcq $0, %%r9\n"
- /* Extract l3 */
- "movq %%r10, 24(%%rsi)\n"
- "xorq %%r10, %%r10\n"
- /* (r8,r9,r10) += 2 * a1 * a3 */
- "movq %%r12, %%rax\n"
- "mulq %%r14\n"
- "addq %%rax, %%r8\n"
- "adcq %%rdx, %%r9\n"
- "adcq $0, %%r10\n"
- "addq %%rax, %%r8\n"
- "adcq %%rdx, %%r9\n"
- "adcq $0, %%r10\n"
- /* (r8,r9,r10) += a2 * a2 */
- "movq %%r13, %%rax\n"
- "mulq %%r13\n"
- "addq %%rax, %%r8\n"
- "adcq %%rdx, %%r9\n"
- "adcq $0, %%r10\n"
- /* Extract l4 */
- "movq %%r8, 32(%%rsi)\n"
- "xorq %%r8, %%r8\n"
- /* (r9,r10,r8) += 2 * a2 * a3 */
- "movq %%r13, %%rax\n"
- "mulq %%r14\n"
- "addq %%rax, %%r9\n"
- "adcq %%rdx, %%r10\n"
- "adcq $0, %%r8\n"
- "addq %%rax, %%r9\n"
- "adcq %%rdx, %%r10\n"
- "adcq $0, %%r8\n"
- /* Extract l5 */
- "movq %%r9, 40(%%rsi)\n"
- /* (r10,r8) += a3 * a3 */
- "movq %%r14, %%rax\n"
- "mulq %%r14\n"
- "addq %%rax, %%r10\n"
- "adcq %%rdx, %%r8\n"
- /* Extract l6 */
- "movq %%r10, 48(%%rsi)\n"
- /* Extract l7 */
- "movq %%r8, 56(%%rsi)\n"
- :
- : "S"(l), "D"(a->d)
- : "rax", "rdx", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "cc", "memory");
-#else
- /* 160 bit accumulator. */
- uint64_t c0 = 0, c1 = 0;
- uint32_t c2 = 0;
-
- /* l[0..7] = a[0..3] * b[0..3]. */
- muladd_fast(a->d[0], a->d[0]);
- extract_fast(l[0]);
- muladd2(a->d[0], a->d[1]);
- extract(l[1]);
- muladd2(a->d[0], a->d[2]);
- muladd(a->d[1], a->d[1]);
- extract(l[2]);
- muladd2(a->d[0], a->d[3]);
- muladd2(a->d[1], a->d[2]);
- extract(l[3]);
- muladd2(a->d[1], a->d[3]);
- muladd(a->d[2], a->d[2]);
- extract(l[4]);
- muladd2(a->d[2], a->d[3]);
- extract(l[5]);
- muladd_fast(a->d[3], a->d[3]);
- extract_fast(l[6]);
- VERIFY_CHECK(c1 == 0);
- l[7] = c0;
-#endif
-}
-
#undef sumadd
#undef sumadd_fast
#undef muladd
#undef muladd_fast
-#undef muladd2
#undef extract
#undef extract_fast
@@ -906,12 +748,6 @@ static int secp256k1_scalar_shr_int(secp256k1_scalar *r, int n) {
return ret;
}
-static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a) {
- uint64_t l[8];
- secp256k1_scalar_sqr_512(l, a);
- secp256k1_scalar_reduce_512(r, l);
-}
-
static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *k) {
r1->d[0] = k->d[0];
r1->d[1] = k->d[1];
@@ -955,4 +791,78 @@ static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const se
r->d[3] = (r->d[3] & mask0) | (a->d[3] & mask1);
}
+static void secp256k1_scalar_from_signed62(secp256k1_scalar *r, const secp256k1_modinv64_signed62 *a) {
+ const uint64_t a0 = a->v[0], a1 = a->v[1], a2 = a->v[2], a3 = a->v[3], a4 = a->v[4];
+
+ /* The output from secp256k1_modinv64{_var} should be normalized to range [0,modulus), and
+ * have limbs in [0,2^62). The modulus is < 2^256, so the top limb must be below 2^(256-62*4).
+ */
+ VERIFY_CHECK(a0 >> 62 == 0);
+ VERIFY_CHECK(a1 >> 62 == 0);
+ VERIFY_CHECK(a2 >> 62 == 0);
+ VERIFY_CHECK(a3 >> 62 == 0);
+ VERIFY_CHECK(a4 >> 8 == 0);
+
+ r->d[0] = a0 | a1 << 62;
+ r->d[1] = a1 >> 2 | a2 << 60;
+ r->d[2] = a2 >> 4 | a3 << 58;
+ r->d[3] = a3 >> 6 | a4 << 56;
+
+#ifdef VERIFY
+ VERIFY_CHECK(secp256k1_scalar_check_overflow(r) == 0);
+#endif
+}
+
+static void secp256k1_scalar_to_signed62(secp256k1_modinv64_signed62 *r, const secp256k1_scalar *a) {
+ const uint64_t M62 = UINT64_MAX >> 2;
+ const uint64_t a0 = a->d[0], a1 = a->d[1], a2 = a->d[2], a3 = a->d[3];
+
+#ifdef VERIFY
+ VERIFY_CHECK(secp256k1_scalar_check_overflow(a) == 0);
+#endif
+
+ r->v[0] = a0 & M62;
+ r->v[1] = (a0 >> 62 | a1 << 2) & M62;
+ r->v[2] = (a1 >> 60 | a2 << 4) & M62;
+ r->v[3] = (a2 >> 58 | a3 << 6) & M62;
+ r->v[4] = a3 >> 56;
+}
+
+static const secp256k1_modinv64_modinfo secp256k1_const_modinfo_scalar = {
+ {{0x3FD25E8CD0364141LL, 0x2ABB739ABD2280EELL, -0x15LL, 0, 256}},
+ 0x34F20099AA774EC1LL
+};
+
+static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar *x) {
+ secp256k1_modinv64_signed62 s;
+#ifdef VERIFY
+ int zero_in = secp256k1_scalar_is_zero(x);
+#endif
+ secp256k1_scalar_to_signed62(&s, x);
+ secp256k1_modinv64(&s, &secp256k1_const_modinfo_scalar);
+ secp256k1_scalar_from_signed62(r, &s);
+
+#ifdef VERIFY
+ VERIFY_CHECK(secp256k1_scalar_is_zero(r) == zero_in);
+#endif
+}
+
+static void secp256k1_scalar_inverse_var(secp256k1_scalar *r, const secp256k1_scalar *x) {
+ secp256k1_modinv64_signed62 s;
+#ifdef VERIFY
+ int zero_in = secp256k1_scalar_is_zero(x);
+#endif
+ secp256k1_scalar_to_signed62(&s, x);
+ secp256k1_modinv64_var(&s, &secp256k1_const_modinfo_scalar);
+ secp256k1_scalar_from_signed62(r, &s);
+
+#ifdef VERIFY
+ VERIFY_CHECK(secp256k1_scalar_is_zero(r) == zero_in);
+#endif
+}
+
+SECP256K1_INLINE static int secp256k1_scalar_is_even(const secp256k1_scalar *a) {
+ return !(a->d[0] & 1);
+}
+
#endif /* SECP256K1_SCALAR_REPR_IMPL_H */
diff --git a/src/secp256k1/src/scalar_8x32.h b/src/secp256k1/src/scalar_8x32.h
index 2c9a348e24..17863ef937 100644
--- a/src/secp256k1/src/scalar_8x32.h
+++ b/src/secp256k1/src/scalar_8x32.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_SCALAR_REPR_H
#define SECP256K1_SCALAR_REPR_H
diff --git a/src/secp256k1/src/scalar_8x32_impl.h b/src/secp256k1/src/scalar_8x32_impl.h
index 6853f79ecc..62c7ae7156 100644
--- a/src/secp256k1/src/scalar_8x32_impl.h
+++ b/src/secp256k1/src/scalar_8x32_impl.h
@@ -1,12 +1,14 @@
-/**********************************************************************
- * Copyright (c) 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_SCALAR_REPR_IMPL_H
#define SECP256K1_SCALAR_REPR_IMPL_H
+#include "modinv32_impl.h"
+
/* Limbs of the secp256k1 order. */
#define SECP256K1_N_0 ((uint32_t)0xD0364141UL)
#define SECP256K1_N_1 ((uint32_t)0xBFD25E8CUL)
@@ -291,28 +293,6 @@ static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) {
VERIFY_CHECK(c1 >= th); \
}
-/** Add 2*a*b to the number defined by (c0,c1,c2). c2 must never overflow. */
-#define muladd2(a,b) { \
- uint32_t tl, th, th2, tl2; \
- { \
- uint64_t t = (uint64_t)a * b; \
- th = t >> 32; /* at most 0xFFFFFFFE */ \
- tl = t; \
- } \
- th2 = th + th; /* at most 0xFFFFFFFE (in case th was 0x7FFFFFFF) */ \
- c2 += (th2 < th); /* never overflows by contract (verified the next line) */ \
- VERIFY_CHECK((th2 >= th) || (c2 != 0)); \
- tl2 = tl + tl; /* at most 0xFFFFFFFE (in case the lowest 63 bits of tl were 0x7FFFFFFF) */ \
- th2 += (tl2 < tl); /* at most 0xFFFFFFFF */ \
- c0 += tl2; /* overflow is handled on the next line */ \
- th2 += (c0 < tl2); /* second overflow is handled on the next line */ \
- c2 += (c0 < tl2) & (th2 == 0); /* never overflows by contract (verified the next line) */ \
- VERIFY_CHECK((c0 >= tl2) || (th2 != 0) || (c2 != 0)); \
- c1 += th2; /* overflow is handled on the next line */ \
- c2 += (c1 < th2); /* never overflows by contract (verified the next line) */ \
- VERIFY_CHECK((c1 >= th2) || (c2 != 0)); \
-}
-
/** Add a to the number defined by (c0,c1,c2). c2 must never overflow. */
#define sumadd(a) { \
unsigned int over; \
@@ -576,71 +556,10 @@ static void secp256k1_scalar_mul_512(uint32_t *l, const secp256k1_scalar *a, con
l[15] = c0;
}
-static void secp256k1_scalar_sqr_512(uint32_t *l, const secp256k1_scalar *a) {
- /* 96 bit accumulator. */
- uint32_t c0 = 0, c1 = 0, c2 = 0;
-
- /* l[0..15] = a[0..7]^2. */
- muladd_fast(a->d[0], a->d[0]);
- extract_fast(l[0]);
- muladd2(a->d[0], a->d[1]);
- extract(l[1]);
- muladd2(a->d[0], a->d[2]);
- muladd(a->d[1], a->d[1]);
- extract(l[2]);
- muladd2(a->d[0], a->d[3]);
- muladd2(a->d[1], a->d[2]);
- extract(l[3]);
- muladd2(a->d[0], a->d[4]);
- muladd2(a->d[1], a->d[3]);
- muladd(a->d[2], a->d[2]);
- extract(l[4]);
- muladd2(a->d[0], a->d[5]);
- muladd2(a->d[1], a->d[4]);
- muladd2(a->d[2], a->d[3]);
- extract(l[5]);
- muladd2(a->d[0], a->d[6]);
- muladd2(a->d[1], a->d[5]);
- muladd2(a->d[2], a->d[4]);
- muladd(a->d[3], a->d[3]);
- extract(l[6]);
- muladd2(a->d[0], a->d[7]);
- muladd2(a->d[1], a->d[6]);
- muladd2(a->d[2], a->d[5]);
- muladd2(a->d[3], a->d[4]);
- extract(l[7]);
- muladd2(a->d[1], a->d[7]);
- muladd2(a->d[2], a->d[6]);
- muladd2(a->d[3], a->d[5]);
- muladd(a->d[4], a->d[4]);
- extract(l[8]);
- muladd2(a->d[2], a->d[7]);
- muladd2(a->d[3], a->d[6]);
- muladd2(a->d[4], a->d[5]);
- extract(l[9]);
- muladd2(a->d[3], a->d[7]);
- muladd2(a->d[4], a->d[6]);
- muladd(a->d[5], a->d[5]);
- extract(l[10]);
- muladd2(a->d[4], a->d[7]);
- muladd2(a->d[5], a->d[6]);
- extract(l[11]);
- muladd2(a->d[5], a->d[7]);
- muladd(a->d[6], a->d[6]);
- extract(l[12]);
- muladd2(a->d[6], a->d[7]);
- extract(l[13]);
- muladd_fast(a->d[7], a->d[7]);
- extract_fast(l[14]);
- VERIFY_CHECK(c1 == 0);
- l[15] = c0;
-}
-
#undef sumadd
#undef sumadd_fast
#undef muladd
#undef muladd_fast
-#undef muladd2
#undef extract
#undef extract_fast
@@ -666,12 +585,6 @@ static int secp256k1_scalar_shr_int(secp256k1_scalar *r, int n) {
return ret;
}
-static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a) {
- uint32_t l[16];
- secp256k1_scalar_sqr_512(l, a);
- secp256k1_scalar_reduce_512(r, l);
-}
-
static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *k) {
r1->d[0] = k->d[0];
r1->d[1] = k->d[1];
@@ -731,4 +644,92 @@ static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const se
r->d[7] = (r->d[7] & mask0) | (a->d[7] & mask1);
}
+static void secp256k1_scalar_from_signed30(secp256k1_scalar *r, const secp256k1_modinv32_signed30 *a) {
+ const uint32_t a0 = a->v[0], a1 = a->v[1], a2 = a->v[2], a3 = a->v[3], a4 = a->v[4],
+ a5 = a->v[5], a6 = a->v[6], a7 = a->v[7], a8 = a->v[8];
+
+ /* The output from secp256k1_modinv32{_var} should be normalized to range [0,modulus), and
+ * have limbs in [0,2^30). The modulus is < 2^256, so the top limb must be below 2^(256-30*8).
+ */
+ VERIFY_CHECK(a0 >> 30 == 0);
+ VERIFY_CHECK(a1 >> 30 == 0);
+ VERIFY_CHECK(a2 >> 30 == 0);
+ VERIFY_CHECK(a3 >> 30 == 0);
+ VERIFY_CHECK(a4 >> 30 == 0);
+ VERIFY_CHECK(a5 >> 30 == 0);
+ VERIFY_CHECK(a6 >> 30 == 0);
+ VERIFY_CHECK(a7 >> 30 == 0);
+ VERIFY_CHECK(a8 >> 16 == 0);
+
+ r->d[0] = a0 | a1 << 30;
+ r->d[1] = a1 >> 2 | a2 << 28;
+ r->d[2] = a2 >> 4 | a3 << 26;
+ r->d[3] = a3 >> 6 | a4 << 24;
+ r->d[4] = a4 >> 8 | a5 << 22;
+ r->d[5] = a5 >> 10 | a6 << 20;
+ r->d[6] = a6 >> 12 | a7 << 18;
+ r->d[7] = a7 >> 14 | a8 << 16;
+
+#ifdef VERIFY
+ VERIFY_CHECK(secp256k1_scalar_check_overflow(r) == 0);
+#endif
+}
+
+static void secp256k1_scalar_to_signed30(secp256k1_modinv32_signed30 *r, const secp256k1_scalar *a) {
+ const uint32_t M30 = UINT32_MAX >> 2;
+ const uint32_t a0 = a->d[0], a1 = a->d[1], a2 = a->d[2], a3 = a->d[3],
+ a4 = a->d[4], a5 = a->d[5], a6 = a->d[6], a7 = a->d[7];
+
+#ifdef VERIFY
+ VERIFY_CHECK(secp256k1_scalar_check_overflow(a) == 0);
+#endif
+
+ r->v[0] = a0 & M30;
+ r->v[1] = (a0 >> 30 | a1 << 2) & M30;
+ r->v[2] = (a1 >> 28 | a2 << 4) & M30;
+ r->v[3] = (a2 >> 26 | a3 << 6) & M30;
+ r->v[4] = (a3 >> 24 | a4 << 8) & M30;
+ r->v[5] = (a4 >> 22 | a5 << 10) & M30;
+ r->v[6] = (a5 >> 20 | a6 << 12) & M30;
+ r->v[7] = (a6 >> 18 | a7 << 14) & M30;
+ r->v[8] = a7 >> 16;
+}
+
+static const secp256k1_modinv32_modinfo secp256k1_const_modinfo_scalar = {
+ {{0x10364141L, 0x3F497A33L, 0x348A03BBL, 0x2BB739ABL, -0x146L, 0, 0, 0, 65536}},
+ 0x2A774EC1L
+};
+
+static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar *x) {
+ secp256k1_modinv32_signed30 s;
+#ifdef VERIFY
+ int zero_in = secp256k1_scalar_is_zero(x);
+#endif
+ secp256k1_scalar_to_signed30(&s, x);
+ secp256k1_modinv32(&s, &secp256k1_const_modinfo_scalar);
+ secp256k1_scalar_from_signed30(r, &s);
+
+#ifdef VERIFY
+ VERIFY_CHECK(secp256k1_scalar_is_zero(r) == zero_in);
+#endif
+}
+
+static void secp256k1_scalar_inverse_var(secp256k1_scalar *r, const secp256k1_scalar *x) {
+ secp256k1_modinv32_signed30 s;
+#ifdef VERIFY
+ int zero_in = secp256k1_scalar_is_zero(x);
+#endif
+ secp256k1_scalar_to_signed30(&s, x);
+ secp256k1_modinv32_var(&s, &secp256k1_const_modinfo_scalar);
+ secp256k1_scalar_from_signed30(r, &s);
+
+#ifdef VERIFY
+ VERIFY_CHECK(secp256k1_scalar_is_zero(r) == zero_in);
+#endif
+}
+
+SECP256K1_INLINE static int secp256k1_scalar_is_even(const secp256k1_scalar *a) {
+ return !(a->d[0] & 1);
+}
+
#endif /* SECP256K1_SCALAR_REPR_IMPL_H */
diff --git a/src/secp256k1/src/scalar_impl.h b/src/secp256k1/src/scalar_impl.h
index fc75891818..e124474773 100644
--- a/src/secp256k1/src/scalar_impl.h
+++ b/src/secp256k1/src/scalar_impl.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_SCALAR_IMPL_H
#define SECP256K1_SCALAR_IMPL_H
@@ -31,231 +31,12 @@
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];
- secp256k1_scalar_get_b32(c, a);
- secp256k1_num_set_bin(r, c, 32);
-}
-
-/** secp256k1 curve order, see secp256k1_ecdsa_const_order_as_fe in ecdsa_impl.h */
-static void secp256k1_scalar_order_get_num(secp256k1_num *r) {
-#if defined(EXHAUSTIVE_TEST_ORDER)
- static const unsigned char order[32] = {
- 0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,EXHAUSTIVE_TEST_ORDER
- };
-#else
- static const unsigned char order[32] = {
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,
- 0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B,
- 0xBF,0xD2,0x5E,0x8C,0xD0,0x36,0x41,0x41
- };
-#endif
- secp256k1_num_set_bin(r, order, 32);
-}
-#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;
- *r = 0;
- for (i = 0; i < EXHAUSTIVE_TEST_ORDER; i++)
- if ((i * *x) % EXHAUSTIVE_TEST_ORDER == 1)
- *r = i;
- /* If this VERIFY_CHECK triggers we were given a noninvertible scalar (and thus
- * have a composite group order; fix it in exhaustive_tests.c). */
- VERIFY_CHECK(*r != 0);
-}
-#else
- secp256k1_scalar *t;
- int i;
- /* First compute xN as x ^ (2^N - 1) for some values of N,
- * and uM as x ^ M for some values of M. */
- secp256k1_scalar x2, x3, x6, x8, x14, x28, x56, x112, x126;
- secp256k1_scalar u2, u5, u9, u11, u13;
-
- secp256k1_scalar_sqr(&u2, x);
- secp256k1_scalar_mul(&x2, &u2, x);
- secp256k1_scalar_mul(&u5, &u2, &x2);
- secp256k1_scalar_mul(&x3, &u5, &u2);
- secp256k1_scalar_mul(&u9, &x3, &u2);
- secp256k1_scalar_mul(&u11, &u9, &u2);
- secp256k1_scalar_mul(&u13, &u11, &u2);
-
- secp256k1_scalar_sqr(&x6, &u13);
- secp256k1_scalar_sqr(&x6, &x6);
- secp256k1_scalar_mul(&x6, &x6, &u11);
-
- secp256k1_scalar_sqr(&x8, &x6);
- secp256k1_scalar_sqr(&x8, &x8);
- secp256k1_scalar_mul(&x8, &x8, &x2);
-
- secp256k1_scalar_sqr(&x14, &x8);
- for (i = 0; i < 5; i++) {
- secp256k1_scalar_sqr(&x14, &x14);
- }
- secp256k1_scalar_mul(&x14, &x14, &x6);
-
- secp256k1_scalar_sqr(&x28, &x14);
- for (i = 0; i < 13; i++) {
- secp256k1_scalar_sqr(&x28, &x28);
- }
- secp256k1_scalar_mul(&x28, &x28, &x14);
-
- secp256k1_scalar_sqr(&x56, &x28);
- for (i = 0; i < 27; i++) {
- secp256k1_scalar_sqr(&x56, &x56);
- }
- secp256k1_scalar_mul(&x56, &x56, &x28);
-
- secp256k1_scalar_sqr(&x112, &x56);
- for (i = 0; i < 55; i++) {
- secp256k1_scalar_sqr(&x112, &x112);
- }
- secp256k1_scalar_mul(&x112, &x112, &x56);
-
- secp256k1_scalar_sqr(&x126, &x112);
- for (i = 0; i < 13; i++) {
- secp256k1_scalar_sqr(&x126, &x126);
- }
- secp256k1_scalar_mul(&x126, &x126, &x14);
-
- /* Then accumulate the final result (t starts at x126). */
- t = &x126;
- for (i = 0; i < 3; i++) {
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, &u5); /* 101 */
- for (i = 0; i < 4; i++) { /* 0 */
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, &x3); /* 111 */
- for (i = 0; i < 4; i++) { /* 0 */
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, &u5); /* 101 */
- for (i = 0; i < 5; i++) { /* 0 */
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, &u11); /* 1011 */
- for (i = 0; i < 4; i++) {
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, &u11); /* 1011 */
- for (i = 0; i < 4; i++) { /* 0 */
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, &x3); /* 111 */
- for (i = 0; i < 5; i++) { /* 00 */
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, &x3); /* 111 */
- for (i = 0; i < 6; i++) { /* 00 */
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, &u13); /* 1101 */
- for (i = 0; i < 4; i++) { /* 0 */
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, &u5); /* 101 */
- for (i = 0; i < 3; i++) {
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, &x3); /* 111 */
- for (i = 0; i < 5; i++) { /* 0 */
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, &u9); /* 1001 */
- for (i = 0; i < 6; i++) { /* 000 */
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, &u5); /* 101 */
- for (i = 0; i < 10; i++) { /* 0000000 */
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, &x3); /* 111 */
- for (i = 0; i < 4; i++) { /* 0 */
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, &x3); /* 111 */
- for (i = 0; i < 9; i++) { /* 0 */
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, &x8); /* 11111111 */
- for (i = 0; i < 5; i++) { /* 0 */
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, &u9); /* 1001 */
- for (i = 0; i < 6; i++) { /* 00 */
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, &u11); /* 1011 */
- for (i = 0; i < 4; i++) {
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, &u13); /* 1101 */
- for (i = 0; i < 5; i++) {
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, &x2); /* 11 */
- for (i = 0; i < 6; i++) { /* 00 */
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, &u13); /* 1101 */
- for (i = 0; i < 10; i++) { /* 000000 */
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, &u13); /* 1101 */
- for (i = 0; i < 4; i++) {
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, &u9); /* 1001 */
- for (i = 0; i < 6; i++) { /* 00000 */
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, x); /* 1 */
- for (i = 0; i < 8; i++) { /* 00 */
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(r, t, &x6); /* 111111 */
-}
-
-SECP256K1_INLINE static int secp256k1_scalar_is_even(const secp256k1_scalar *a) {
- return !(a->d[0] & 1);
-}
-#endif
-
-static void secp256k1_scalar_inverse_var(secp256k1_scalar *r, const secp256k1_scalar *x) {
-#if defined(USE_SCALAR_INV_BUILTIN)
- secp256k1_scalar_inverse(r, x);
-#elif defined(USE_SCALAR_INV_NUM)
- unsigned char b[32];
- secp256k1_num n, m;
- secp256k1_scalar t = *x;
- secp256k1_scalar_get_b32(b, &t);
- secp256k1_num_set_bin(&n, b, 32);
- secp256k1_scalar_order_get_num(&m);
- secp256k1_num_mod_inverse(&n, &n, &m);
- secp256k1_num_get_bin(b, 32, &n);
- secp256k1_scalar_set_b32(r, b, NULL);
- /* Verify that the inverse was computed correctly, without GMP code. */
- secp256k1_scalar_mul(&t, &t, r);
- CHECK(secp256k1_scalar_is_one(&t));
-#else
-#error "Please select scalar inverse implementation"
-#endif
-}
-
/* These parameters are generated using sage/gen_exhaustive_groups.sage. */
#if defined(EXHAUSTIVE_TEST_ORDER)
# if EXHAUSTIVE_TEST_ORDER == 13
diff --git a/src/secp256k1/src/scalar_low.h b/src/secp256k1/src/scalar_low.h
index 2794a7f171..67051bd30b 100644
--- a/src/secp256k1/src/scalar_low.h
+++ b/src/secp256k1/src/scalar_low.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2015 Andrew Poelstra *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2015 Andrew Poelstra *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_SCALAR_REPR_H
#define SECP256K1_SCALAR_REPR_H
diff --git a/src/secp256k1/src/scalar_low_impl.h b/src/secp256k1/src/scalar_low_impl.h
index a615ec074b..7176f0b2ca 100644
--- a/src/secp256k1/src/scalar_low_impl.h
+++ b/src/secp256k1/src/scalar_low_impl.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2015 Andrew Poelstra *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2015 Andrew Poelstra *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_SCALAR_REPR_IMPL_H
#define SECP256K1_SCALAR_REPR_IMPL_H
@@ -104,10 +104,6 @@ static int secp256k1_scalar_shr_int(secp256k1_scalar *r, int n) {
return ret;
}
-static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a) {
- *r = (*a * *a) % EXHAUSTIVE_TEST_ORDER;
-}
-
static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) {
*r1 = *a;
*r2 = 0;
@@ -125,4 +121,19 @@ static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const se
*r = (*r & mask0) | (*a & mask1);
}
+static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar *x) {
+ int i;
+ *r = 0;
+ for (i = 0; i < EXHAUSTIVE_TEST_ORDER; i++)
+ if ((i * *x) % EXHAUSTIVE_TEST_ORDER == 1)
+ *r = i;
+ /* If this VERIFY_CHECK triggers we were given a noninvertible scalar (and thus
+ * have a composite group order; fix it in exhaustive_tests.c). */
+ VERIFY_CHECK(*r != 0);
+}
+
+static void secp256k1_scalar_inverse_var(secp256k1_scalar *r, const secp256k1_scalar *x) {
+ secp256k1_scalar_inverse(r, x);
+}
+
#endif /* SECP256K1_SCALAR_REPR_IMPL_H */
diff --git a/src/secp256k1/src/scratch.h b/src/secp256k1/src/scratch.h
index 77b35d126b..9dcb7581f6 100644
--- a/src/secp256k1/src/scratch.h
+++ b/src/secp256k1/src/scratch.h
@@ -1,11 +1,11 @@
-/**********************************************************************
- * Copyright (c) 2017 Andrew Poelstra *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
-
-#ifndef _SECP256K1_SCRATCH_
-#define _SECP256K1_SCRATCH_
+/***********************************************************************
+ * Copyright (c) 2017 Andrew Poelstra *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
+
+#ifndef SECP256K1_SCRATCH_H
+#define SECP256K1_SCRATCH_H
/* The typedef is used internally; the struct name is used in the public API
* (where it is exposed as a different typedef) */
diff --git a/src/secp256k1/src/scratch_impl.h b/src/secp256k1/src/scratch_impl.h
index f381e2e322..688e18eb66 100644
--- a/src/secp256k1/src/scratch_impl.h
+++ b/src/secp256k1/src/scratch_impl.h
@@ -1,11 +1,11 @@
-/**********************************************************************
- * Copyright (c) 2017 Andrew Poelstra *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2017 Andrew Poelstra *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
-#ifndef _SECP256K1_SCRATCH_IMPL_H_
-#define _SECP256K1_SCRATCH_IMPL_H_
+#ifndef SECP256K1_SCRATCH_IMPL_H
+#define SECP256K1_SCRATCH_IMPL_H
#include "util.h"
#include "scratch.h"
diff --git a/src/secp256k1/src/secp256k1.c b/src/secp256k1/src/secp256k1.c
index dae506d08c..aef3f99ac3 100644
--- a/src/secp256k1/src/secp256k1.c
+++ b/src/secp256k1/src/secp256k1.c
@@ -1,15 +1,14 @@
-/**********************************************************************
- * Copyright (c) 2013-2015 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013-2015 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#include "include/secp256k1.h"
#include "include/secp256k1_preallocated.h"
#include "assumptions.h"
#include "util.h"
-#include "num_impl.h"
#include "field_impl.h"
#include "scalar_impl.h"
#include "group_impl.h"
@@ -86,6 +85,8 @@ const secp256k1_context *secp256k1_context_no_precomp = &secp256k1_context_no_pr
size_t secp256k1_context_preallocated_size(unsigned int flags) {
size_t ret = ROUND_TO_ALIGN(sizeof(secp256k1_context));
+ /* A return value of 0 is reserved as an indicator for errors when we call this function internally. */
+ VERIFY_CHECK(ret != 0);
if (EXPECT((flags & SECP256K1_FLAGS_TYPE_MASK) != SECP256K1_FLAGS_TYPE_CONTEXT, 0)) {
secp256k1_callback_call(&default_illegal_callback,
@@ -122,21 +123,21 @@ secp256k1_context* secp256k1_context_preallocated_create(void* prealloc, unsigne
if (!secp256k1_selftest()) {
secp256k1_callback_call(&default_error_callback, "self test failed");
}
- VERIFY_CHECK(prealloc != NULL);
+
prealloc_size = secp256k1_context_preallocated_size(flags);
+ if (prealloc_size == 0) {
+ return NULL;
+ }
+ VERIFY_CHECK(prealloc != NULL);
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");
- return NULL;
- }
-
secp256k1_ecmult_context_init(&ret->ecmult_ctx);
secp256k1_ecmult_gen_context_init(&ret->ecmult_gen_ctx);
+ /* Flags have been checked by secp256k1_context_preallocated_size. */
+ VERIFY_CHECK((flags & SECP256K1_FLAGS_TYPE_MASK) == SECP256K1_FLAGS_TYPE_CONTEXT);
if (flags & SECP256K1_FLAGS_BIT_CONTEXT_SIGN) {
secp256k1_ecmult_gen_context_build(&ret->ecmult_gen_ctx, &prealloc);
}
@@ -420,17 +421,17 @@ int secp256k1_ecdsa_signature_normalize(const secp256k1_context* ctx, secp256k1_
return ret;
}
-int secp256k1_ecdsa_verify(const secp256k1_context* ctx, const secp256k1_ecdsa_signature *sig, const unsigned char *msg32, const secp256k1_pubkey *pubkey) {
+int secp256k1_ecdsa_verify(const secp256k1_context* ctx, const secp256k1_ecdsa_signature *sig, const unsigned char *msghash32, const secp256k1_pubkey *pubkey) {
secp256k1_ge q;
secp256k1_scalar r, s;
secp256k1_scalar m;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
- ARG_CHECK(msg32 != NULL);
+ ARG_CHECK(msghash32 != NULL);
ARG_CHECK(sig != NULL);
ARG_CHECK(pubkey != NULL);
- secp256k1_scalar_set_b32(&m, msg32, NULL);
+ secp256k1_scalar_set_b32(&m, msghash32, NULL);
secp256k1_ecdsa_signature_load(ctx, &r, &s, sig);
return (!secp256k1_scalar_is_high(&s) &&
secp256k1_pubkey_load(ctx, &q, pubkey) &&
@@ -531,16 +532,16 @@ static int secp256k1_ecdsa_sign_inner(const secp256k1_context* ctx, secp256k1_sc
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) {
+int secp256k1_ecdsa_sign(const secp256k1_context* ctx, secp256k1_ecdsa_signature *signature, const unsigned char *msghash32, 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(msghash32 != NULL);
ARG_CHECK(signature != NULL);
ARG_CHECK(seckey != NULL);
- ret = secp256k1_ecdsa_sign_inner(ctx, &r, &s, NULL, msg32, seckey, noncefp, noncedata);
+ ret = secp256k1_ecdsa_sign_inner(ctx, &r, &s, NULL, msghash32, seckey, noncefp, noncedata);
secp256k1_ecdsa_signature_save(signature, &r, &s);
return ret;
}
@@ -580,7 +581,7 @@ int secp256k1_ec_pubkey_create(const secp256k1_context* ctx, secp256k1_pubkey *p
ret = secp256k1_ec_pubkey_create_helper(&ctx->ecmult_gen_ctx, &seckey_scalar, &p, seckey);
secp256k1_pubkey_save(pubkey, &p);
- memczero(pubkey, sizeof(*pubkey), !ret);
+ secp256k1_memczero(pubkey, sizeof(*pubkey), !ret);
secp256k1_scalar_clear(&seckey_scalar);
return ret;
@@ -621,26 +622,26 @@ int secp256k1_ec_pubkey_negate(const secp256k1_context* ctx, secp256k1_pubkey *p
}
-static int secp256k1_ec_seckey_tweak_add_helper(secp256k1_scalar *sec, const unsigned char *tweak) {
+static int secp256k1_ec_seckey_tweak_add_helper(secp256k1_scalar *sec, const unsigned char *tweak32) {
secp256k1_scalar term;
int overflow = 0;
int ret = 0;
- secp256k1_scalar_set_b32(&term, tweak, &overflow);
+ secp256k1_scalar_set_b32(&term, tweak32, &overflow);
ret = (!overflow) & secp256k1_eckey_privkey_tweak_add(sec, &term);
secp256k1_scalar_clear(&term);
return ret;
}
-int secp256k1_ec_seckey_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 *tweak32) {
secp256k1_scalar sec;
int ret = 0;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(seckey != NULL);
- ARG_CHECK(tweak != NULL);
+ ARG_CHECK(tweak32 != NULL);
ret = secp256k1_scalar_set_b32_seckey(&sec, seckey);
- ret &= secp256k1_ec_seckey_tweak_add_helper(&sec, tweak);
+ ret &= secp256k1_ec_seckey_tweak_add_helper(&sec, tweak32);
secp256k1_scalar_cmov(&sec, &secp256k1_scalar_zero, !ret);
secp256k1_scalar_get_b32(seckey, &sec);
@@ -648,28 +649,28 @@ int secp256k1_ec_seckey_tweak_add(const secp256k1_context* ctx, unsigned char *s
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_privkey_tweak_add(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak32) {
+ return secp256k1_ec_seckey_tweak_add(ctx, seckey, tweak32);
}
-static int secp256k1_ec_pubkey_tweak_add_helper(const secp256k1_ecmult_context* ecmult_ctx, secp256k1_ge *p, const unsigned char *tweak) {
+static int secp256k1_ec_pubkey_tweak_add_helper(const secp256k1_ecmult_context* ecmult_ctx, secp256k1_ge *p, const unsigned char *tweak32) {
secp256k1_scalar term;
int overflow = 0;
- secp256k1_scalar_set_b32(&term, tweak, &overflow);
+ secp256k1_scalar_set_b32(&term, tweak32, &overflow);
return !overflow && secp256k1_eckey_pubkey_tweak_add(ecmult_ctx, p, &term);
}
-int secp256k1_ec_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *tweak) {
+int secp256k1_ec_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *tweak32) {
secp256k1_ge p;
int ret = 0;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
ARG_CHECK(pubkey != NULL);
- ARG_CHECK(tweak != NULL);
+ ARG_CHECK(tweak32 != NULL);
ret = secp256k1_pubkey_load(ctx, &p, pubkey);
memset(pubkey, 0, sizeof(*pubkey));
- ret = ret && secp256k1_ec_pubkey_tweak_add_helper(&ctx->ecmult_ctx, &p, tweak);
+ ret = ret && secp256k1_ec_pubkey_tweak_add_helper(&ctx->ecmult_ctx, &p, tweak32);
if (ret) {
secp256k1_pubkey_save(pubkey, &p);
}
@@ -677,16 +678,16 @@ int secp256k1_ec_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_pubkey
return ret;
}
-int secp256k1_ec_seckey_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 *tweak32) {
secp256k1_scalar factor;
secp256k1_scalar sec;
int ret = 0;
int overflow = 0;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(seckey != NULL);
- ARG_CHECK(tweak != NULL);
+ ARG_CHECK(tweak32 != NULL);
- secp256k1_scalar_set_b32(&factor, tweak, &overflow);
+ secp256k1_scalar_set_b32(&factor, tweak32, &overflow);
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);
@@ -697,11 +698,11 @@ int secp256k1_ec_seckey_tweak_mul(const secp256k1_context* ctx, unsigned char *s
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_privkey_tweak_mul(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak32) {
+ return secp256k1_ec_seckey_tweak_mul(ctx, seckey, tweak32);
}
-int secp256k1_ec_pubkey_tweak_mul(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *tweak) {
+int secp256k1_ec_pubkey_tweak_mul(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *tweak32) {
secp256k1_ge p;
secp256k1_scalar factor;
int ret = 0;
@@ -709,9 +710,9 @@ int secp256k1_ec_pubkey_tweak_mul(const secp256k1_context* ctx, secp256k1_pubkey
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
ARG_CHECK(pubkey != NULL);
- ARG_CHECK(tweak != NULL);
+ ARG_CHECK(tweak32 != NULL);
- secp256k1_scalar_set_b32(&factor, tweak, &overflow);
+ secp256k1_scalar_set_b32(&factor, tweak32, &overflow);
ret = !overflow && secp256k1_pubkey_load(ctx, &p, pubkey);
memset(pubkey, 0, sizeof(*pubkey));
if (ret) {
diff --git a/src/secp256k1/src/selftest.h b/src/secp256k1/src/selftest.h
index 0e37510c1e..52f1b8442e 100644
--- a/src/secp256k1/src/selftest.h
+++ b/src/secp256k1/src/selftest.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2020 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2020 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_SELFTEST_H
#define SECP256K1_SELFTEST_H
diff --git a/src/secp256k1/src/testrand.h b/src/secp256k1/src/testrand.h
index a76003d5b8..667d1867bd 100644
--- a/src/secp256k1/src/testrand.h
+++ b/src/secp256k1/src/testrand.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013, 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_TESTRAND_H
#define SECP256K1_TESTRAND_H
diff --git a/src/secp256k1/src/testrand_impl.h b/src/secp256k1/src/testrand_impl.h
index 3392566329..e643778f36 100644
--- a/src/secp256k1/src/testrand_impl.h
+++ b/src/secp256k1/src/testrand_impl.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2013-2015 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013-2015 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_TESTRAND_IMPL_H
#define SECP256K1_TESTRAND_IMPL_H
diff --git a/src/secp256k1/src/tests.c b/src/secp256k1/src/tests.c
index bb4b5b4c07..a146394305 100644
--- a/src/secp256k1/src/tests.c
+++ b/src/secp256k1/src/tests.c
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014, 2015 Pieter Wuille, Gregory Maxwell *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013, 2014, 2015 Pieter Wuille, Gregory Maxwell *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#if defined HAVE_CONFIG_H
#include "libsecp256k1-config.h"
@@ -18,12 +18,13 @@
#include "include/secp256k1.h"
#include "include/secp256k1_preallocated.h"
#include "testrand_impl.h"
+#include "util.h"
#ifdef ENABLE_OPENSSL_TESTS
-#include "openssl/bn.h"
-#include "openssl/ec.h"
-#include "openssl/ecdsa.h"
-#include "openssl/obj_mac.h"
+#include <openssl/bn.h>
+#include <openssl/ec.h>
+#include <openssl/ecdsa.h>
+#include <openssl/obj_mac.h>
# if OPENSSL_VERSION_NUMBER < 0x10100000L
void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps) {*pr = sig->r; *ps = sig->s;}
# endif
@@ -32,6 +33,11 @@ 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"
+#include "modinv32_impl.h"
+#ifdef SECP256K1_WIDEMUL_INT128
+#include "modinv64_impl.h"
+#endif
+
static int count = 64;
static secp256k1_context *ctx = NULL;
@@ -416,6 +422,25 @@ void run_scratch_tests(void) {
secp256k1_context_destroy(none);
}
+void run_ctz_tests(void) {
+ static const uint32_t b32[] = {1, 0xffffffff, 0x5e56968f, 0xe0d63129};
+ static const uint64_t b64[] = {1, 0xffffffffffffffff, 0xbcd02462139b3fc3, 0x98b5f80c769693ef};
+ int shift;
+ unsigned i;
+ for (i = 0; i < sizeof(b32) / sizeof(b32[0]); ++i) {
+ for (shift = 0; shift < 32; ++shift) {
+ CHECK(secp256k1_ctz32_var_debruijn(b32[i] << shift) == shift);
+ CHECK(secp256k1_ctz32_var(b32[i] << shift) == shift);
+ }
+ }
+ for (i = 0; i < sizeof(b64) / sizeof(b64[0]); ++i) {
+ for (shift = 0; shift < 64; ++shift) {
+ CHECK(secp256k1_ctz64_var_debruijn(b64[i] << shift) == shift);
+ CHECK(secp256k1_ctz64_var(b64[i] << shift) == shift);
+ }
+ }
+}
+
/***** HASH TESTS *****/
void run_sha256_tests(void) {
@@ -611,202 +636,924 @@ void run_rand_int(void) {
}
}
-/***** NUM TESTS *****/
+/***** MODINV TESTS *****/
+
+/* Compute the modular inverse of (odd) x mod 2^64. */
+uint64_t modinv2p64(uint64_t x) {
+ /* If w = 1/x mod 2^(2^L), then w*(2 - w*x) = 1/x mod 2^(2^(L+1)). See
+ * Hacker's Delight second edition, Henry S. Warren, Jr., pages 245-247 for
+ * why. Start with L=0, for which it is true for every odd x that
+ * 1/x=1 mod 2. Iterating 6 times gives us 1/x mod 2^64. */
+ int l;
+ uint64_t w = 1;
+ CHECK(x & 1);
+ for (l = 0; l < 6; ++l) w *= (2 - w*x);
+ return w;
+}
-#ifndef USE_NUM_NONE
-void random_num_negate(secp256k1_num *num) {
- if (secp256k1_testrand_bits(1)) {
- secp256k1_num_negate(num);
+/* compute out = (a*b) mod m; if b=NULL, treat b=1.
+ *
+ * Out is a 512-bit number (represented as 32 uint16_t's in LE order). The other
+ * arguments are 256-bit numbers (represented as 16 uint16_t's in LE order). */
+void mulmod256(uint16_t* out, const uint16_t* a, const uint16_t* b, const uint16_t* m) {
+ uint16_t mul[32];
+ uint64_t c = 0;
+ int i, j;
+ int m_bitlen = 0;
+ int mul_bitlen = 0;
+
+ if (b != NULL) {
+ /* Compute the product of a and b, and put it in mul. */
+ for (i = 0; i < 32; ++i) {
+ for (j = i <= 15 ? 0 : i - 15; j <= i && j <= 15; j++) {
+ c += (uint64_t)a[j] * b[i - j];
+ }
+ mul[i] = c & 0xFFFF;
+ c >>= 16;
+ }
+ CHECK(c == 0);
+
+ /* compute the highest set bit in mul */
+ for (i = 511; i >= 0; --i) {
+ if ((mul[i >> 4] >> (i & 15)) & 1) {
+ mul_bitlen = i;
+ break;
+ }
+ }
+ } else {
+ /* if b==NULL, set mul=a. */
+ memcpy(mul, a, 32);
+ memset(mul + 16, 0, 32);
+ /* compute the highest set bit in mul */
+ for (i = 255; i >= 0; --i) {
+ if ((mul[i >> 4] >> (i & 15)) & 1) {
+ mul_bitlen = i;
+ break;
+ }
+ }
}
-}
-void random_num_order_test(secp256k1_num *num) {
- secp256k1_scalar sc;
- random_scalar_order_test(&sc);
- secp256k1_scalar_get_num(num, &sc);
+ /* Compute the highest set bit in m. */
+ for (i = 255; i >= 0; --i) {
+ if ((m[i >> 4] >> (i & 15)) & 1) {
+ m_bitlen = i;
+ break;
+ }
+ }
+
+ /* Try do mul -= m<<i, for i going down to 0, whenever the result is not negative */
+ for (i = mul_bitlen - m_bitlen; i >= 0; --i) {
+ uint16_t mul2[32];
+ int64_t cs;
+
+ /* Compute mul2 = mul - m<<i. */
+ cs = 0; /* accumulator */
+ for (j = 0; j < 32; ++j) { /* j loops over the output limbs in mul2. */
+ /* Compute sub: the 16 bits in m that will be subtracted from mul2[j]. */
+ uint16_t sub = 0;
+ int p;
+ for (p = 0; p < 16; ++p) { /* p loops over the bit positions in mul2[j]. */
+ int bitpos = j * 16 - i + p; /* bitpos is the correspond bit position in m. */
+ if (bitpos >= 0 && bitpos < 256) {
+ sub |= ((m[bitpos >> 4] >> (bitpos & 15)) & 1) << p;
+ }
+ }
+ /* Add mul[j]-sub to accumulator, and shift bottom 16 bits out to mul2[j]. */
+ cs += mul[j];
+ cs -= sub;
+ mul2[j] = (cs & 0xFFFF);
+ cs >>= 16;
+ }
+ /* If remainder of subtraction is 0, set mul = mul2. */
+ if (cs == 0) {
+ memcpy(mul, mul2, sizeof(mul));
+ }
+ }
+ /* Sanity check: test that all limbs higher than m's highest are zero */
+ for (i = (m_bitlen >> 4) + 1; i < 32; ++i) {
+ CHECK(mul[i] == 0);
+ }
+ memcpy(out, mul, 32);
}
-void random_num_order(secp256k1_num *num) {
- secp256k1_scalar sc;
- random_scalar_order(&sc);
- secp256k1_scalar_get_num(num, &sc);
+/* Convert a 256-bit number represented as 16 uint16_t's to signed30 notation. */
+void uint16_to_signed30(secp256k1_modinv32_signed30* out, const uint16_t* in) {
+ int i;
+ memset(out->v, 0, sizeof(out->v));
+ for (i = 0; i < 256; ++i) {
+ out->v[i / 30] |= (int32_t)(((in[i >> 4]) >> (i & 15)) & 1) << (i % 30);
+ }
}
-void test_num_negate(void) {
- secp256k1_num n1;
- secp256k1_num n2;
- random_num_order_test(&n1); /* n1 = R */
- random_num_negate(&n1);
- secp256k1_num_copy(&n2, &n1); /* n2 = R */
- secp256k1_num_sub(&n1, &n2, &n1); /* n1 = n2-n1 = 0 */
- CHECK(secp256k1_num_is_zero(&n1));
- secp256k1_num_copy(&n1, &n2); /* n1 = R */
- secp256k1_num_negate(&n1); /* n1 = -R */
- CHECK(!secp256k1_num_is_zero(&n1));
- secp256k1_num_add(&n1, &n2, &n1); /* n1 = n2+n1 = 0 */
- CHECK(secp256k1_num_is_zero(&n1));
- secp256k1_num_copy(&n1, &n2); /* n1 = R */
- secp256k1_num_negate(&n1); /* n1 = -R */
- CHECK(secp256k1_num_is_neg(&n1) != secp256k1_num_is_neg(&n2));
- secp256k1_num_negate(&n1); /* n1 = R */
- CHECK(secp256k1_num_eq(&n1, &n2));
+/* Convert a 256-bit number in signed30 notation to a representation as 16 uint16_t's. */
+void signed30_to_uint16(uint16_t* out, const secp256k1_modinv32_signed30* in) {
+ int i;
+ memset(out, 0, 32);
+ for (i = 0; i < 256; ++i) {
+ out[i >> 4] |= (((in->v[i / 30]) >> (i % 30)) & 1) << (i & 15);
+ }
}
-void test_num_add_sub(void) {
+/* Randomly mutate the sign of limbs in signed30 representation, without changing the value. */
+void mutate_sign_signed30(secp256k1_modinv32_signed30* x) {
int i;
- secp256k1_scalar s;
- secp256k1_num n1;
- secp256k1_num n2;
- secp256k1_num n1p2, n2p1, n1m2, n2m1;
- random_num_order_test(&n1); /* n1 = R1 */
- if (secp256k1_testrand_bits(1)) {
- random_num_negate(&n1);
+ for (i = 0; i < 16; ++i) {
+ int pos = secp256k1_testrand_int(8);
+ if (x->v[pos] > 0 && x->v[pos + 1] <= 0x3fffffff) {
+ x->v[pos] -= 0x40000000;
+ x->v[pos + 1] += 1;
+ } else if (x->v[pos] < 0 && x->v[pos + 1] >= 0x3fffffff) {
+ x->v[pos] += 0x40000000;
+ x->v[pos + 1] -= 1;
+ }
}
- random_num_order_test(&n2); /* n2 = R2 */
- if (secp256k1_testrand_bits(1)) {
- random_num_negate(&n2);
- }
- secp256k1_num_add(&n1p2, &n1, &n2); /* n1p2 = R1 + R2 */
- secp256k1_num_add(&n2p1, &n2, &n1); /* n2p1 = R2 + R1 */
- secp256k1_num_sub(&n1m2, &n1, &n2); /* n1m2 = R1 - R2 */
- secp256k1_num_sub(&n2m1, &n2, &n1); /* n2m1 = R2 - R1 */
- CHECK(secp256k1_num_eq(&n1p2, &n2p1));
- CHECK(!secp256k1_num_eq(&n1p2, &n1m2));
- secp256k1_num_negate(&n2m1); /* n2m1 = -R2 + R1 */
- CHECK(secp256k1_num_eq(&n2m1, &n1m2));
- CHECK(!secp256k1_num_eq(&n2m1, &n1));
- secp256k1_num_add(&n2m1, &n2m1, &n2); /* n2m1 = -R2 + R1 + R2 = R1 */
- CHECK(secp256k1_num_eq(&n2m1, &n1));
- CHECK(!secp256k1_num_eq(&n2p1, &n1));
- secp256k1_num_sub(&n2p1, &n2p1, &n2); /* n2p1 = R2 + R1 - R2 = R1 */
- CHECK(secp256k1_num_eq(&n2p1, &n1));
-
- /* check is_one */
- secp256k1_scalar_set_int(&s, 1);
- secp256k1_scalar_get_num(&n1, &s);
- CHECK(secp256k1_num_is_one(&n1));
- /* check that 2^n + 1 is never 1 */
- secp256k1_scalar_get_num(&n2, &s);
- for (i = 0; i < 250; ++i) {
- secp256k1_num_add(&n1, &n1, &n1); /* n1 *= 2 */
- secp256k1_num_add(&n1p2, &n1, &n2); /* n1p2 = n1 + 1 */
- CHECK(!secp256k1_num_is_one(&n1p2));
+}
+
+/* Test secp256k1_modinv32{_var}, using inputs in 16-bit limb format, and returning inverse. */
+void test_modinv32_uint16(uint16_t* out, const uint16_t* in, const uint16_t* mod) {
+ uint16_t tmp[16];
+ secp256k1_modinv32_signed30 x;
+ secp256k1_modinv32_modinfo m;
+ int i, vartime, nonzero;
+
+ uint16_to_signed30(&x, in);
+ nonzero = (x.v[0] | x.v[1] | x.v[2] | x.v[3] | x.v[4] | x.v[5] | x.v[6] | x.v[7] | x.v[8]) != 0;
+ uint16_to_signed30(&m.modulus, mod);
+ mutate_sign_signed30(&m.modulus);
+
+ /* compute 1/modulus mod 2^30 */
+ m.modulus_inv30 = modinv2p64(m.modulus.v[0]) & 0x3fffffff;
+ CHECK(((m.modulus_inv30 * m.modulus.v[0]) & 0x3fffffff) == 1);
+
+ for (vartime = 0; vartime < 2; ++vartime) {
+ /* compute inverse */
+ (vartime ? secp256k1_modinv32_var : secp256k1_modinv32)(&x, &m);
+
+ /* produce output */
+ signed30_to_uint16(out, &x);
+
+ /* check if the inverse times the input is 1 (mod m), unless x is 0. */
+ mulmod256(tmp, out, in, mod);
+ CHECK(tmp[0] == nonzero);
+ for (i = 1; i < 16; ++i) CHECK(tmp[i] == 0);
+
+ /* invert again */
+ (vartime ? secp256k1_modinv32_var : secp256k1_modinv32)(&x, &m);
+
+ /* check if the result is equal to the input */
+ signed30_to_uint16(tmp, &x);
+ for (i = 0; i < 16; ++i) CHECK(tmp[i] == in[i]);
}
}
-void test_num_mod(void) {
+#ifdef SECP256K1_WIDEMUL_INT128
+/* Convert a 256-bit number represented as 16 uint16_t's to signed62 notation. */
+void uint16_to_signed62(secp256k1_modinv64_signed62* out, const uint16_t* in) {
int i;
- secp256k1_scalar s;
- secp256k1_num order, n;
-
- /* check that 0 mod anything is 0 */
- random_scalar_order_test(&s);
- secp256k1_scalar_get_num(&order, &s);
- secp256k1_scalar_set_int(&s, 0);
- secp256k1_scalar_get_num(&n, &s);
- secp256k1_num_mod(&n, &order);
- CHECK(secp256k1_num_is_zero(&n));
-
- /* check that anything mod 1 is 0 */
- secp256k1_scalar_set_int(&s, 1);
- secp256k1_scalar_get_num(&order, &s);
- secp256k1_scalar_get_num(&n, &s);
- secp256k1_num_mod(&n, &order);
- CHECK(secp256k1_num_is_zero(&n));
-
- /* check that increasing the number past 2^256 does not break this */
- random_scalar_order_test(&s);
- secp256k1_scalar_get_num(&n, &s);
- /* multiply by 2^8, which'll test this case with high probability */
- for (i = 0; i < 8; ++i) {
- secp256k1_num_add(&n, &n, &n);
+ memset(out->v, 0, sizeof(out->v));
+ for (i = 0; i < 256; ++i) {
+ out->v[i / 62] |= (int64_t)(((in[i >> 4]) >> (i & 15)) & 1) << (i % 62);
}
- secp256k1_num_mod(&n, &order);
- CHECK(secp256k1_num_is_zero(&n));
}
-void test_num_jacobi(void) {
- secp256k1_scalar sqr;
- secp256k1_scalar small;
- secp256k1_scalar five; /* five is not a quadratic residue */
- secp256k1_num order, n;
+/* Convert a 256-bit number in signed62 notation to a representation as 16 uint16_t's. */
+void signed62_to_uint16(uint16_t* out, const secp256k1_modinv64_signed62* in) {
int i;
- /* squares mod 5 are 1, 4 */
- const int jacobi5[10] = { 0, 1, -1, -1, 1, 0, 1, -1, -1, 1 };
+ memset(out, 0, 32);
+ for (i = 0; i < 256; ++i) {
+ out[i >> 4] |= (((in->v[i / 62]) >> (i % 62)) & 1) << (i & 15);
+ }
+}
- /* check some small values with 5 as the order */
- secp256k1_scalar_set_int(&five, 5);
- secp256k1_scalar_get_num(&order, &five);
- for (i = 0; i < 10; ++i) {
- secp256k1_scalar_set_int(&small, i);
- secp256k1_scalar_get_num(&n, &small);
- CHECK(secp256k1_num_jacobi(&n, &order) == jacobi5[i]);
+/* Randomly mutate the sign of limbs in signed62 representation, without changing the value. */
+void mutate_sign_signed62(secp256k1_modinv64_signed62* x) {
+ static const int64_t M62 = (int64_t)(UINT64_MAX >> 2);
+ int i;
+ for (i = 0; i < 8; ++i) {
+ int pos = secp256k1_testrand_int(4);
+ if (x->v[pos] > 0 && x->v[pos + 1] <= M62) {
+ x->v[pos] -= (M62 + 1);
+ x->v[pos + 1] += 1;
+ } else if (x->v[pos] < 0 && x->v[pos + 1] >= -M62) {
+ x->v[pos] += (M62 + 1);
+ x->v[pos + 1] -= 1;
+ }
}
+}
- /** test large values with 5 as group order */
- secp256k1_scalar_get_num(&order, &five);
- /* we first need a scalar which is not a multiple of 5 */
- do {
- secp256k1_num fiven;
- random_scalar_order_test(&sqr);
- secp256k1_scalar_get_num(&fiven, &five);
- secp256k1_scalar_get_num(&n, &sqr);
- secp256k1_num_mod(&n, &fiven);
- } while (secp256k1_num_is_zero(&n));
- /* next force it to be a residue. 2 is a nonresidue mod 5 so we can
- * just multiply by two, i.e. add the number to itself */
- if (secp256k1_num_jacobi(&n, &order) == -1) {
- secp256k1_num_add(&n, &n, &n);
- }
-
- /* test residue */
- CHECK(secp256k1_num_jacobi(&n, &order) == 1);
- /* test nonresidue */
- secp256k1_num_add(&n, &n, &n);
- CHECK(secp256k1_num_jacobi(&n, &order) == -1);
-
- /** test with secp group order as order */
- secp256k1_scalar_order_get_num(&order);
- random_scalar_order_test(&sqr);
- secp256k1_scalar_sqr(&sqr, &sqr);
- /* test residue */
- secp256k1_scalar_get_num(&n, &sqr);
- CHECK(secp256k1_num_jacobi(&n, &order) == 1);
- /* test nonresidue */
- secp256k1_scalar_mul(&sqr, &sqr, &five);
- secp256k1_scalar_get_num(&n, &sqr);
- CHECK(secp256k1_num_jacobi(&n, &order) == -1);
- /* test multiple of the order*/
- CHECK(secp256k1_num_jacobi(&order, &order) == 0);
-
- /* check one less than the order */
- secp256k1_scalar_set_int(&small, 1);
- secp256k1_scalar_get_num(&n, &small);
- secp256k1_num_sub(&n, &order, &n);
- CHECK(secp256k1_num_jacobi(&n, &order) == 1); /* sage confirms this is 1 */
+/* Test secp256k1_modinv64{_var}, using inputs in 16-bit limb format, and returning inverse. */
+void test_modinv64_uint16(uint16_t* out, const uint16_t* in, const uint16_t* mod) {
+ static const int64_t M62 = (int64_t)(UINT64_MAX >> 2);
+ uint16_t tmp[16];
+ secp256k1_modinv64_signed62 x;
+ secp256k1_modinv64_modinfo m;
+ int i, vartime, nonzero;
+
+ uint16_to_signed62(&x, in);
+ nonzero = (x.v[0] | x.v[1] | x.v[2] | x.v[3] | x.v[4]) != 0;
+ uint16_to_signed62(&m.modulus, mod);
+ mutate_sign_signed62(&m.modulus);
+
+ /* compute 1/modulus mod 2^62 */
+ m.modulus_inv62 = modinv2p64(m.modulus.v[0]) & M62;
+ CHECK(((m.modulus_inv62 * m.modulus.v[0]) & M62) == 1);
+
+ for (vartime = 0; vartime < 2; ++vartime) {
+ /* compute inverse */
+ (vartime ? secp256k1_modinv64_var : secp256k1_modinv64)(&x, &m);
+
+ /* produce output */
+ signed62_to_uint16(out, &x);
+
+ /* check if the inverse times the input is 1 (mod m), unless x is 0. */
+ mulmod256(tmp, out, in, mod);
+ CHECK(tmp[0] == nonzero);
+ for (i = 1; i < 16; ++i) CHECK(tmp[i] == 0);
+
+ /* invert again */
+ (vartime ? secp256k1_modinv64_var : secp256k1_modinv64)(&x, &m);
+
+ /* check if the result is equal to the input */
+ signed62_to_uint16(tmp, &x);
+ for (i = 0; i < 16; ++i) CHECK(tmp[i] == in[i]);
+ }
}
+#endif
-void run_num_smalltests(void) {
+/* test if a and b are coprime */
+int coprime(const uint16_t* a, const uint16_t* b) {
+ uint16_t x[16], y[16], t[16];
int i;
- for (i = 0; i < 100*count; i++) {
- test_num_negate();
- test_num_add_sub();
- test_num_mod();
- test_num_jacobi();
+ int iszero;
+ memcpy(x, a, 32);
+ memcpy(y, b, 32);
+
+ /* simple gcd loop: while x!=0, (x,y)=(y%x,x) */
+ while (1) {
+ iszero = 1;
+ for (i = 0; i < 16; ++i) {
+ if (x[i] != 0) {
+ iszero = 0;
+ break;
+ }
+ }
+ if (iszero) break;
+ mulmod256(t, y, NULL, x);
+ memcpy(y, x, 32);
+ memcpy(x, t, 32);
}
+
+ /* return whether y=1 */
+ if (y[0] != 1) return 0;
+ for (i = 1; i < 16; ++i) {
+ if (y[i] != 0) return 0;
+ }
+ return 1;
}
+
+void run_modinv_tests(void) {
+ /* Fixed test cases. Each tuple is (input, modulus, output), each as 16x16 bits in LE order. */
+ static const uint16_t CASES[][3][16] = {
+ /* Test cases triggering edge cases in divsteps */
+
+ /* Test case known to need 713 divsteps */
+ {{0x1513, 0x5389, 0x54e9, 0x2798, 0x1957, 0x66a0, 0x8057, 0x3477,
+ 0x7784, 0x1052, 0x326a, 0x9331, 0x6506, 0xa95c, 0x91f3, 0xfb5e},
+ {0x2bdd, 0x8df4, 0xcc61, 0x481f, 0xdae5, 0x5ca7, 0xf43b, 0x7d54,
+ 0x13d6, 0x469b, 0x2294, 0x20f4, 0xb2a4, 0xa2d1, 0x3ff1, 0xfd4b},
+ {0xffd8, 0xd9a0, 0x456e, 0x81bb, 0xbabd, 0x6cea, 0x6dbd, 0x73ab,
+ 0xbb94, 0x3d3c, 0xdf08, 0x31c4, 0x3e32, 0xc179, 0x2486, 0xb86b}},
+ /* Test case known to need 589 divsteps, reaching delta=-140 and
+ delta=141. */
+ {{0x3fb1, 0x903b, 0x4eb7, 0x4813, 0xd863, 0x26bf, 0xd89f, 0xa8a9,
+ 0x02fe, 0x57c6, 0x554a, 0x4eab, 0x165e, 0x3d61, 0xee1e, 0x456c},
+ {0x9295, 0x823b, 0x5c1f, 0x5386, 0x48e0, 0x02ff, 0x4c2a, 0xa2da,
+ 0xe58f, 0x967c, 0xc97e, 0x3f5a, 0x69fb, 0x52d9, 0x0a86, 0xb4a3},
+ {0x3d30, 0xb893, 0xa809, 0xa7a8, 0x26f5, 0x5b42, 0x55be, 0xf4d0,
+ 0x12c2, 0x7e6a, 0xe41a, 0x90c7, 0xebfa, 0xf920, 0x304e, 0x1419}},
+ /* Test case known to need 650 divsteps, and doing 65 consecutive (f,g/2) steps. */
+ {{0x8583, 0x5058, 0xbeae, 0xeb69, 0x48bc, 0x52bb, 0x6a9d, 0xcc94,
+ 0x2a21, 0x87d5, 0x5b0d, 0x42f6, 0x5b8a, 0x2214, 0xe9d6, 0xa040},
+ {0x7531, 0x27cb, 0x7e53, 0xb739, 0x6a5f, 0x83f5, 0xa45c, 0xcb1d,
+ 0x8a87, 0x1c9c, 0x51d7, 0x851c, 0xb9d8, 0x1fbe, 0xc241, 0xd4a3},
+ {0xcdb4, 0x275c, 0x7d22, 0xa906, 0x0173, 0xc054, 0x7fdf, 0x5005,
+ 0x7fb8, 0x9059, 0xdf51, 0x99df, 0x2654, 0x8f6e, 0x070f, 0xb347}},
+ /* example needing 713 divsteps; delta=-2..3 */
+ {{0xe2e9, 0xee91, 0x4345, 0xe5ad, 0xf3ec, 0x8f42, 0x0364, 0xd5c9,
+ 0xff49, 0xbef5, 0x4544, 0x4c7c, 0xae4b, 0xfd9d, 0xb35b, 0xda9d},
+ {0x36e7, 0x8cca, 0x2ed0, 0x47b3, 0xaca4, 0xb374, 0x7d2a, 0x0772,
+ 0x6bdb, 0xe0a7, 0x900b, 0xfe10, 0x788c, 0x6f22, 0xd909, 0xf298},
+ {0xd8c6, 0xba39, 0x13ed, 0x198c, 0x16c8, 0xb837, 0xa5f2, 0x9797,
+ 0x0113, 0x882a, 0x15b5, 0x324c, 0xabee, 0xe465, 0x8170, 0x85ac}},
+ /* example needing 713 divsteps; delta=-2..3 */
+ {{0xd5b7, 0x2966, 0x040e, 0xf59a, 0x0387, 0xd96d, 0xbfbc, 0xd850,
+ 0x2d96, 0x872a, 0xad81, 0xc03c, 0xbb39, 0xb7fa, 0xd904, 0xef78},
+ {0x6279, 0x4314, 0xfdd3, 0x1568, 0x0982, 0x4d13, 0x625f, 0x010c,
+ 0x22b1, 0x0cc3, 0xf22d, 0x5710, 0x1109, 0x5751, 0x7714, 0xfcf2},
+ {0xdb13, 0x5817, 0x232e, 0xe456, 0xbbbc, 0x6fbe, 0x4572, 0xa358,
+ 0xc76d, 0x928e, 0x0162, 0x5314, 0x8325, 0x5683, 0xe21b, 0xda88}},
+ /* example needing 713 divsteps; delta=-2..3 */
+ {{0xa06f, 0x71ee, 0x3bac, 0x9ebb, 0xdeaa, 0x09ed, 0x1cf7, 0x9ec9,
+ 0x7158, 0x8b72, 0x5d53, 0x5479, 0x5c75, 0xbb66, 0x9125, 0xeccc},
+ {0x2941, 0xd46c, 0x3cd4, 0x4a9d, 0x5c4a, 0x256b, 0xbd6c, 0x9b8e,
+ 0x8fe0, 0x8a14, 0xffe8, 0x2496, 0x618d, 0xa9d7, 0x5018, 0xfb29},
+ {0x437c, 0xbd60, 0x7590, 0x94bb, 0x0095, 0xd35e, 0xd4fe, 0xd6da,
+ 0x0d4e, 0x5342, 0x4cd2, 0x169b, 0x661c, 0x1380, 0xed2d, 0x85c1}},
+ /* example reaching delta=-64..65; 661 divsteps */
+ {{0xfde4, 0x68d6, 0x6c48, 0x7f77, 0x1c78, 0x96de, 0x2fd9, 0xa6c2,
+ 0xbbb5, 0xd319, 0x69cf, 0xd4b3, 0xa321, 0xcda0, 0x172e, 0xe530},
+ {0xd9e3, 0x0f60, 0x3d86, 0xeeab, 0x25ee, 0x9582, 0x2d50, 0xfe16,
+ 0xd4e2, 0xe3ba, 0x94e2, 0x9833, 0x6c5e, 0x8982, 0x13b6, 0xe598},
+ {0xe675, 0xf55a, 0x10f6, 0xabde, 0x5113, 0xecaa, 0x61ae, 0xad9f,
+ 0x0c27, 0xef33, 0x62e5, 0x211d, 0x08fa, 0xa78d, 0xc675, 0x8bae}},
+ /* example reaching delta=-64..65; 661 divsteps */
+ {{0x21bf, 0x52d5, 0x8fd4, 0xaa18, 0x156a, 0x7247, 0xebb8, 0x5717,
+ 0x4eb5, 0x1421, 0xb58f, 0x3b0b, 0x5dff, 0xe533, 0xb369, 0xd28a},
+ {0x9f6b, 0xe463, 0x2563, 0xc74d, 0x6d81, 0x636a, 0x8fc8, 0x7a94,
+ 0x9429, 0x1585, 0xf35e, 0x7ff5, 0xb64f, 0x9720, 0xba74, 0xe108},
+ {0xa5ab, 0xea7b, 0xfe5e, 0x8a85, 0x13be, 0x7934, 0xe8a0, 0xa187,
+ 0x86b5, 0xe477, 0xb9a4, 0x75d7, 0x538f, 0xdd70, 0xc781, 0xb67d}},
+ /* example reaching delta=-64..65; 661 divsteps */
+ {{0xa41a, 0x3e8d, 0xf1f5, 0x9493, 0x868c, 0x5103, 0x2725, 0x3ceb,
+ 0x6032, 0x3624, 0xdc6b, 0x9120, 0xbf4c, 0x8821, 0x91ad, 0xb31a},
+ {0x5c0b, 0xdda5, 0x20f8, 0x32a1, 0xaf73, 0x6ec5, 0x4779, 0x43d6,
+ 0xd454, 0x9573, 0xbf84, 0x5a58, 0xe04e, 0x307e, 0xd1d5, 0xe230},
+ {0xda15, 0xbcd6, 0x7180, 0xabd3, 0x04e6, 0x6986, 0xc0d7, 0x90bb,
+ 0x3a4d, 0x7c95, 0xaaab, 0x9ab3, 0xda34, 0xa7f6, 0x9636, 0x6273}},
+ /* example doing 123 consecutive (f,g/2) steps; 615 divsteps */
+ {{0xb4d6, 0xb38f, 0x00aa, 0xebda, 0xd4c2, 0x70b8, 0x9dad, 0x58ee,
+ 0x68f8, 0x48d3, 0xb5ff, 0xf422, 0x9e46, 0x2437, 0x18d0, 0xd9cc},
+ {0x5c83, 0xfed7, 0x97f5, 0x3f07, 0xcaad, 0x95b1, 0xb4a4, 0xb005,
+ 0x23af, 0xdd27, 0x6c0d, 0x932c, 0xe2b2, 0xe3ae, 0xfb96, 0xdf67},
+ {0x3105, 0x0127, 0xfd48, 0x039b, 0x35f1, 0xbc6f, 0x6c0a, 0xb572,
+ 0xe4df, 0xebad, 0x8edc, 0xb89d, 0x9555, 0x4c26, 0x1fef, 0x997c}},
+ /* example doing 123 consecutive (f,g/2) steps; 614 divsteps */
+ {{0x5138, 0xd474, 0x385f, 0xc964, 0x00f2, 0x6df7, 0x862d, 0xb185,
+ 0xb264, 0xe9e1, 0x466c, 0xf39e, 0xafaf, 0x5f41, 0x47e2, 0xc89d},
+ {0x8607, 0x9c81, 0x46a2, 0x7dcc, 0xcb0c, 0x9325, 0xe149, 0x2bde,
+ 0x6632, 0x2869, 0xa261, 0xb163, 0xccee, 0x22ae, 0x91e0, 0xcfd5},
+ {0x831c, 0xda22, 0xb080, 0xba7a, 0x26e2, 0x54b0, 0x073b, 0x5ea0,
+ 0xed4b, 0xcb3d, 0xbba1, 0xbec8, 0xf2ad, 0xae0d, 0x349b, 0x17d1}},
+ /* example doing 123 consecutive (f,g/2) steps; 614 divsteps */
+ {{0xe9a5, 0xb4ad, 0xd995, 0x9953, 0xcdff, 0x50d7, 0xf715, 0x9dc7,
+ 0x3e28, 0x15a9, 0x95a3, 0x8554, 0x5b5e, 0xad1d, 0x6d57, 0x3d50},
+ {0x3ad9, 0xbd60, 0x5cc7, 0x6b91, 0xadeb, 0x71f6, 0x7cc4, 0xa58a,
+ 0x2cce, 0xf17c, 0x38c9, 0x97ed, 0x65fb, 0x3fa6, 0xa6bc, 0xeb24},
+ {0xf96c, 0x1963, 0x8151, 0xa0cc, 0x299b, 0xf277, 0x001a, 0x16bb,
+ 0xfd2e, 0x532d, 0x0410, 0xe117, 0x6b00, 0x44ec, 0xca6a, 0x1745}},
+ /* example doing 446 (f,g/2) steps; 523 divsteps */
+ {{0x3758, 0xa56c, 0xe41e, 0x4e47, 0x0975, 0xa82b, 0x107c, 0x89cf,
+ 0x2093, 0x5a0c, 0xda37, 0xe007, 0x6074, 0x4f68, 0x2f5a, 0xbb8a},
+ {0x4beb, 0xa40f, 0x2c42, 0xd9d6, 0x97e8, 0xca7c, 0xd395, 0x894f,
+ 0x1f50, 0x8067, 0xa233, 0xb850, 0x1746, 0x1706, 0xbcda, 0xdf32},
+ {0x762a, 0xceda, 0x4c45, 0x1ca0, 0x8c37, 0xd8c5, 0xef57, 0x7a2c,
+ 0x6e98, 0xe38a, 0xc50e, 0x2ca9, 0xcb85, 0x24d5, 0xc29c, 0x61f6}},
+ /* example doing 446 (f,g/2) steps; 523 divsteps */
+ {{0x6f38, 0x74ad, 0x7332, 0x4073, 0x6521, 0xb876, 0xa370, 0xa6bd,
+ 0xcea5, 0xbd06, 0x969f, 0x77c6, 0x1e69, 0x7c49, 0x7d51, 0xb6e7},
+ {0x3f27, 0x4be4, 0xd81e, 0x1396, 0xb21f, 0x92aa, 0x6dc3, 0x6283,
+ 0x6ada, 0x3ca2, 0xc1e5, 0x8b9b, 0xd705, 0x5598, 0x8ba1, 0xe087},
+ {0x6a22, 0xe834, 0xbc8d, 0xcee9, 0x42fc, 0xfc77, 0x9c45, 0x1ca8,
+ 0xeb66, 0xed74, 0xaaf9, 0xe75f, 0xfe77, 0x46d2, 0x179b, 0xbf3e}},
+ /* example doing 336 (f,(f+g)/2) steps; 693 divsteps */
+ {{0x7ea7, 0x444e, 0x84ea, 0xc447, 0x7c1f, 0xab97, 0x3de6, 0x5878,
+ 0x4e8b, 0xc017, 0x03e0, 0xdc40, 0xbbd0, 0x74ce, 0x0169, 0x7ab5},
+ {0x4023, 0x154f, 0xfbe4, 0x8195, 0xfda0, 0xef54, 0x9e9a, 0xc703,
+ 0x2803, 0xf760, 0x6302, 0xed5b, 0x7157, 0x6456, 0xdd7d, 0xf14b},
+ {0xb6fb, 0xe3b3, 0x0733, 0xa77e, 0x44c5, 0x3003, 0xc937, 0xdd4d,
+ 0x5355, 0x14e9, 0x184e, 0xcefe, 0xe6b5, 0xf2e0, 0x0a28, 0x5b74}},
+ /* example doing 336 (f,(f+g)/2) steps; 687 divsteps */
+ {{0xa893, 0xb5f4, 0x1ede, 0xa316, 0x242c, 0xbdcc, 0xb017, 0x0836,
+ 0x3a37, 0x27fb, 0xfb85, 0x251e, 0xa189, 0xb15d, 0xa4b8, 0xc24c},
+ {0xb0b7, 0x57ba, 0xbb6d, 0x9177, 0xc896, 0xc7f2, 0x43b4, 0x85a6,
+ 0xe6c4, 0xe50e, 0x3109, 0x7ca5, 0xd73d, 0x13ff, 0x0c3d, 0xcd62},
+ {0x48ca, 0xdb34, 0xe347, 0x2cef, 0x4466, 0x10fb, 0x7ee1, 0x6344,
+ 0x4308, 0x966d, 0xd4d1, 0xb099, 0x994f, 0xd025, 0x2187, 0x5866}},
+ /* example doing 267 (g,(g-f)/2) steps; 678 divsteps */
+ {{0x0775, 0x1754, 0x01f6, 0xdf37, 0xc0be, 0x8197, 0x072f, 0x6cf5,
+ 0x8b36, 0x8069, 0x5590, 0xb92d, 0x6084, 0x47a4, 0x23fe, 0xddd5},
+ {0x8e1b, 0xda37, 0x27d9, 0x312e, 0x3a2f, 0xef6d, 0xd9eb, 0x8153,
+ 0xdcba, 0x9fa3, 0x9f80, 0xead5, 0x134d, 0x2ebb, 0x5ec0, 0xe032},
+ {0x1cb6, 0x5a61, 0x1bed, 0x77d6, 0xd5d1, 0x7498, 0xef33, 0x2dd2,
+ 0x1089, 0xedbd, 0x6958, 0x16ae, 0x336c, 0x45e6, 0x4361, 0xbadc}},
+ /* example doing 267 (g,(g-f)/2) steps; 676 divsteps */
+ {{0x0207, 0xf948, 0xc430, 0xf36b, 0xf0a7, 0x5d36, 0x751f, 0x132c,
+ 0x6f25, 0xa630, 0xca1f, 0xc967, 0xaf9c, 0x34e7, 0xa38f, 0xbe9f},
+ {0x5fb9, 0x7321, 0x6561, 0x5fed, 0x54ec, 0x9c3a, 0xee0e, 0x6717,
+ 0x49af, 0xb896, 0xf4f5, 0x451c, 0x722a, 0xf116, 0x64a9, 0xcf0b},
+ {0xf4d7, 0xdb47, 0xfef2, 0x4806, 0x4cb8, 0x18c7, 0xd9a7, 0x4951,
+ 0x14d8, 0x5c3a, 0xd22d, 0xd7b2, 0x750c, 0x3de7, 0x8b4a, 0x19aa}},
+
+ /* Test cases triggering edge cases in divsteps variant starting with delta=1/2 */
+
+ /* example needing 590 divsteps; delta=-5/2..7/2 */
+ {{0x9118, 0xb640, 0x53d7, 0x30ab, 0x2a23, 0xd907, 0x9323, 0x5b3a,
+ 0xb6d4, 0x538a, 0x7637, 0xfe97, 0xfd05, 0x3cc0, 0x453a, 0xfb7e},
+ {0x6983, 0x4f75, 0x4ad1, 0x48ad, 0xb2d9, 0x521d, 0x3dbc, 0x9cc0,
+ 0x4b60, 0x0ac6, 0xd3be, 0x0fb6, 0xd305, 0x3895, 0x2da5, 0xfdf8},
+ {0xcec1, 0x33ac, 0xa801, 0x8194, 0xe36c, 0x65ef, 0x103b, 0xca54,
+ 0xfa9b, 0xb41d, 0x9b52, 0xb6f7, 0xa611, 0x84aa, 0x3493, 0xbf54}},
+ /* example needing 590 divsteps; delta=-3/2..5/2 */
+ {{0xb5f2, 0x42d0, 0x35e8, 0x8ca0, 0x4b62, 0x6e1d, 0xbdf3, 0x890e,
+ 0x8c82, 0x23d8, 0xc79a, 0xc8e8, 0x789e, 0x353d, 0x9766, 0xea9d},
+ {0x6fa1, 0xacba, 0x4b7a, 0x5de1, 0x95d0, 0xc845, 0xebbf, 0x6f5a,
+ 0x30cf, 0x52db, 0x69b7, 0xe278, 0x4b15, 0x8411, 0x2ab2, 0xf3e7},
+ {0xf12c, 0x9d6d, 0x95fa, 0x1878, 0x9f13, 0x4fb5, 0x3c8b, 0xa451,
+ 0x7182, 0xc4b6, 0x7e2a, 0x7bb7, 0x6e0e, 0x5b68, 0xde55, 0x9927}},
+ /* example needing 590 divsteps; delta=-3/2..5/2 */
+ {{0x229c, 0x4ef8, 0x1e93, 0xe5dc, 0xcde5, 0x6d62, 0x263b, 0xad11,
+ 0xced0, 0x88ff, 0xae8e, 0x3183, 0x11d2, 0xa50b, 0x350d, 0xeb40},
+ {0x3157, 0xe2ea, 0x8a02, 0x0aa3, 0x5ae1, 0xb26c, 0xea27, 0x6805,
+ 0x87e2, 0x9461, 0x37c1, 0x2f8d, 0x85d2, 0x77a8, 0xf805, 0xeec9},
+ {0x6f4e, 0x2748, 0xf7e5, 0xd8d3, 0xabe2, 0x7270, 0xc4e0, 0xedc7,
+ 0xf196, 0x78ca, 0x9139, 0xd8af, 0x72c6, 0xaf2f, 0x85d2, 0x6cd3}},
+ /* example needing 590 divsteps; delta=-5/2..7/2 */
+ {{0xdce8, 0xf1fe, 0x6708, 0x021e, 0xf1ca, 0xd609, 0x5443, 0x85ce,
+ 0x7a05, 0x8f9c, 0x90c3, 0x52e7, 0x8e1d, 0x97b8, 0xc0bf, 0xf2a1},
+ {0xbd3d, 0xed11, 0x1625, 0xb4c5, 0x844c, 0xa413, 0x2569, 0xb9ba,
+ 0xcd35, 0xff84, 0xcd6e, 0x7f0b, 0x7d5d, 0x10df, 0x3efe, 0xfbe5},
+ {0xa9dd, 0xafef, 0xb1b7, 0x4c8d, 0x50e4, 0xafbf, 0x2d5a, 0xb27c,
+ 0x0653, 0x66b6, 0x5d36, 0x4694, 0x7e35, 0xc47c, 0x857f, 0x32c5}},
+ /* example needing 590 divsteps; delta=-3/2..5/2 */
+ {{0x7902, 0xc9f8, 0x926b, 0xaaeb, 0x90f8, 0x1c89, 0xcce3, 0x96b7,
+ 0x28b2, 0x87a2, 0x136d, 0x695a, 0xa8df, 0x9061, 0x9e31, 0xee82},
+ {0xd3a9, 0x3c02, 0x818c, 0x6b81, 0x34b3, 0xebbb, 0xe2c8, 0x7712,
+ 0xbfd6, 0x8248, 0xa6f4, 0xba6f, 0x03bb, 0xfb54, 0x7575, 0xfe89},
+ {0x8246, 0x0d63, 0x478e, 0xf946, 0xf393, 0x0451, 0x08c2, 0x5919,
+ 0x5fd6, 0x4c61, 0xbeb7, 0x9a15, 0x30e1, 0x55fc, 0x6a01, 0x3724}},
+ /* example reaching delta=-127/2..129/2; 571 divsteps */
+ {{0x3eff, 0x926a, 0x77f5, 0x1fff, 0x1a5b, 0xf3ef, 0xf64b, 0x8681,
+ 0xf800, 0xf9bc, 0x761d, 0xe268, 0x62b0, 0xa032, 0xba9c, 0xbe56},
+ {0xb8f9, 0x00e7, 0x47b7, 0xdffc, 0xfd9d, 0x5abb, 0xa19b, 0x1868,
+ 0x31fd, 0x3b29, 0x3674, 0x5449, 0xf54d, 0x1d19, 0x6ac7, 0xff6f},
+ {0xf1d7, 0x3551, 0x5682, 0x9adf, 0xe8aa, 0x19a5, 0x8340, 0x71db,
+ 0xb7ab, 0x4cfd, 0xf661, 0x632c, 0xc27e, 0xd3c6, 0xdf42, 0xd306}},
+ /* example reaching delta=-127/2..129/2; 571 divsteps */
+ {{0x0000, 0x0000, 0x0000, 0x0000, 0x3aff, 0x2ed7, 0xf2e0, 0xabc7,
+ 0x8aee, 0x166e, 0x7ed0, 0x9ac7, 0x714a, 0xb9c5, 0x4d58, 0xad6c},
+ {0x9cf9, 0x47e2, 0xa421, 0xb277, 0xffc2, 0x2747, 0x6486, 0x94c1,
+ 0x1d99, 0xd49b, 0x1096, 0x991a, 0xe986, 0xae02, 0xe89b, 0xea36},
+ {0x1fb4, 0x98d8, 0x19b7, 0x80e9, 0xcdac, 0xaa5a, 0xf1e6, 0x0074,
+ 0xe393, 0xed8b, 0x8d5c, 0xe17d, 0x81b3, 0xc16d, 0x54d3, 0x9be3}},
+ /* example reaching delta=-127/2..129/2; 571 divsteps */
+ {{0xd047, 0x7e36, 0x3157, 0x7ab6, 0xb4d9, 0x8dae, 0x7534, 0x4f5d,
+ 0x489e, 0xa8ab, 0x8a3d, 0xd52c, 0x62af, 0xa032, 0xba9c, 0xbe56},
+ {0xb1f1, 0x737f, 0x5964, 0x5afb, 0x3712, 0x8ef9, 0x19f7, 0x9669,
+ 0x664d, 0x03ad, 0xc352, 0xf7a5, 0xf545, 0x1d19, 0x6ac7, 0xff6f},
+ {0xa834, 0x5256, 0x27bc, 0x33bd, 0xba11, 0x5a7b, 0x791e, 0xe6c0,
+ 0x9ac4, 0x9370, 0x1130, 0x28b4, 0x2b2e, 0x231b, 0x082a, 0x796e}},
+ /* example doing 123 consecutive (f,g/2) steps; 554 divsteps */
+ {{0x6ab1, 0x6ea0, 0x1a99, 0xe0c2, 0xdd45, 0x645d, 0x8dbc, 0x466a,
+ 0xfa64, 0x4289, 0xd3f7, 0xfc8f, 0x2894, 0xe3c5, 0xa008, 0xcc14},
+ {0xc75f, 0xc083, 0x4cc2, 0x64f2, 0x2aff, 0x4c12, 0x8461, 0xc4ae,
+ 0xbbfa, 0xb336, 0xe4b2, 0x3ac5, 0x2c22, 0xf56c, 0x5381, 0xe943},
+ {0xcd80, 0x760d, 0x4395, 0xb3a6, 0xd497, 0xf583, 0x82bd, 0x1daa,
+ 0xbe92, 0x2613, 0xfdfb, 0x869b, 0x0425, 0xa333, 0x7056, 0xc9c5}},
+ /* example doing 123 consecutive (f,g/2) steps; 554 divsteps */
+ {{0x71d4, 0x64df, 0xec4f, 0x74d8, 0x7e0c, 0x40d3, 0x7073, 0x4cc8,
+ 0x2a2a, 0xb1ff, 0x8518, 0x6513, 0xb0ea, 0x640a, 0x62d9, 0xd5f4},
+ {0xdc75, 0xd937, 0x3b13, 0x1d36, 0xdf83, 0xd034, 0x1c1c, 0x4332,
+ 0x4cc3, 0xeeec, 0x7d94, 0x6771, 0x3384, 0x74b0, 0x947d, 0xf2c4},
+ {0x0a82, 0x37a4, 0x12d5, 0xec97, 0x972c, 0xe6bf, 0xc348, 0xa0a9,
+ 0xc50c, 0xdc7c, 0xae30, 0x19d1, 0x0fca, 0x35e1, 0xd6f6, 0x81ee}},
+ /* example doing 123 consecutive (f,g/2) steps; 554 divsteps */
+ {{0xa6b1, 0xabc5, 0x5bbc, 0x7f65, 0xdd32, 0xaa73, 0xf5a3, 0x1982,
+ 0xced4, 0xe949, 0x0fd6, 0x2bc4, 0x2bd7, 0xe3c5, 0xa008, 0xcc14},
+ {0x4b5f, 0x8f96, 0xa375, 0xfbcf, 0x1c7d, 0xf1ec, 0x03f5, 0xb35d,
+ 0xb999, 0xdb1f, 0xc9a1, 0xb4c7, 0x1dd5, 0xf56c, 0x5381, 0xe943},
+ {0xaa3d, 0x38b9, 0xf17d, 0xeed9, 0x9988, 0x69ee, 0xeb88, 0x1495,
+ 0x203f, 0x18c8, 0x82b7, 0xdcb2, 0x34a7, 0x6b00, 0x6998, 0x589a}},
+ /* example doing 453 (f,g/2) steps; 514 divsteps */
+ {{0xa478, 0xe60d, 0x3244, 0x60e6, 0xada3, 0xfe50, 0xb6b1, 0x2eae,
+ 0xd0ef, 0xa7b1, 0xef63, 0x05c0, 0xe213, 0x443e, 0x4427, 0x2448},
+ {0x258f, 0xf9ef, 0xe02b, 0x92dd, 0xd7f3, 0x252b, 0xa503, 0x9089,
+ 0xedff, 0x96c1, 0xfe3a, 0x3a39, 0x198a, 0x981d, 0x0627, 0xedb7},
+ {0x595a, 0x45be, 0x8fb0, 0x2265, 0xc210, 0x02b8, 0xdce9, 0xe241,
+ 0xcab6, 0xbf0d, 0x0049, 0x8d9a, 0x2f51, 0xae54, 0x5785, 0xb411}},
+ /* example doing 453 (f,g/2) steps; 514 divsteps */
+ {{0x48f0, 0x7db3, 0xdafe, 0x1c92, 0x5912, 0xe11a, 0xab52, 0xede1,
+ 0x3182, 0x8980, 0x5d2b, 0x9b5b, 0x8718, 0xda27, 0x1683, 0x1de2},
+ {0x168f, 0x6f36, 0xce7a, 0xf435, 0x19d4, 0xda5e, 0x2351, 0x9af5,
+ 0xb003, 0x0ef5, 0x3b4c, 0xecec, 0xa9f0, 0x78e1, 0xdfef, 0xe823},
+ {0x5f55, 0xfdcc, 0xb233, 0x2914, 0x84f0, 0x97d1, 0x9cf4, 0x2159,
+ 0xbf56, 0xb79c, 0x17a3, 0x7cef, 0xd5de, 0x34f0, 0x5311, 0x4c54}},
+ /* example doing 510 (f,(f+g)/2) steps; 512 divsteps */
+ {{0x2789, 0x2e04, 0x6e0e, 0xb6cd, 0xe4de, 0x4dbf, 0x228d, 0x7877,
+ 0xc335, 0x806b, 0x38cd, 0x8049, 0xa73b, 0xcfa2, 0x82f7, 0x9e19},
+ {0xc08d, 0xb99d, 0xb8f3, 0x663d, 0xbbb3, 0x1284, 0x1485, 0x1d49,
+ 0xc98f, 0x9e78, 0x1588, 0x11e3, 0xd91a, 0xa2c7, 0xfff1, 0xc7b9},
+ {0x1e1f, 0x411d, 0x7c49, 0x0d03, 0xe789, 0x2f8e, 0x5d55, 0xa95e,
+ 0x826e, 0x8de5, 0x52a0, 0x1abc, 0x4cd7, 0xd13a, 0x4395, 0x63e1}},
+ /* example doing 510 (f,(f+g)/2) steps; 512 divsteps */
+ {{0xd5a1, 0xf786, 0x555c, 0xb14b, 0x44ae, 0x535f, 0x4a49, 0xffc3,
+ 0xf497, 0x70d1, 0x57c8, 0xa933, 0xc85a, 0x1910, 0x75bf, 0x960b},
+ {0xfe53, 0x5058, 0x496d, 0xfdff, 0x6fb8, 0x4100, 0x92bd, 0xe0c4,
+ 0xda89, 0xe0a4, 0x841b, 0x43d4, 0xa388, 0x957f, 0x99ca, 0x9abf},
+ {0xe530, 0x05bc, 0xfeec, 0xfc7e, 0xbcd3, 0x1239, 0x54cb, 0x7042,
+ 0xbccb, 0x139e, 0x9076, 0x0203, 0x6068, 0x90c7, 0x1ddf, 0x488d}},
+ /* example doing 228 (g,(g-f)/2) steps; 538 divsteps */
+ {{0x9488, 0xe54b, 0x0e43, 0x81d2, 0x06e7, 0x4b66, 0x36d0, 0x53d6,
+ 0x2b68, 0x22ec, 0x3fa9, 0xc1a7, 0x9ad2, 0xa596, 0xb3ac, 0xdf42},
+ {0xe31f, 0x0b28, 0x5f3b, 0xc1ff, 0x344c, 0xbf5f, 0xd2ec, 0x2936,
+ 0x9995, 0xdeb2, 0xae6c, 0x2852, 0xa2c6, 0xb306, 0x8120, 0xe305},
+ {0xa56e, 0xfb98, 0x1537, 0x4d85, 0x619e, 0x866c, 0x3cd4, 0x779a,
+ 0xdd66, 0xa80d, 0xdc2f, 0xcae4, 0xc74c, 0x5175, 0xa65d, 0x605e}},
+ /* example doing 228 (g,(g-f)/2) steps; 537 divsteps */
+ {{0x8cd5, 0x376d, 0xd01b, 0x7176, 0x19ef, 0xcf09, 0x8403, 0x5e52,
+ 0x83c1, 0x44de, 0xb91e, 0xb33d, 0xe15c, 0x51e7, 0xbad8, 0x6359},
+ {0x3b75, 0xf812, 0x5f9e, 0xa04e, 0x92d3, 0x226e, 0x540e, 0x7c9a,
+ 0x31c6, 0x46d2, 0x0b7b, 0xdb4a, 0xe662, 0x4950, 0x0265, 0xf76f},
+ {0x09ed, 0x692f, 0xe8f1, 0x3482, 0xab54, 0x36b4, 0x8442, 0x6ae9,
+ 0x4329, 0x6505, 0x183b, 0x1c1d, 0x482d, 0x7d63, 0xb44f, 0xcc09}},
+
+ /* Test cases with the group order as modulus. */
+
+ /* Test case with the group order as modulus, needing 635 divsteps. */
+ {{0x95ed, 0x6c01, 0xd113, 0x5ff1, 0xd7d0, 0x29cc, 0x5817, 0x6120,
+ 0xca8e, 0xaad1, 0x25ae, 0x8e84, 0x9af6, 0x30bf, 0xf0ed, 0x1686},
+ {0x4141, 0xd036, 0x5e8c, 0xbfd2, 0xa03b, 0xaf48, 0xdce6, 0xbaae,
+ 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+ {0x1631, 0xbf4a, 0x286a, 0x2716, 0x469f, 0x2ac8, 0x1312, 0xe9bc,
+ 0x04f4, 0x304b, 0x9931, 0x113b, 0xd932, 0xc8f4, 0x0d0d, 0x01a1}},
+ /* example with group size as modulus needing 631 divsteps */
+ {{0x85ed, 0xc284, 0x9608, 0x3c56, 0x19b6, 0xbb5b, 0x2850, 0xdab7,
+ 0xa7f5, 0xe9ab, 0x06a4, 0x5bbb, 0x1135, 0xa186, 0xc424, 0xc68b},
+ {0x4141, 0xd036, 0x5e8c, 0xbfd2, 0xa03b, 0xaf48, 0xdce6, 0xbaae,
+ 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+ {0x8479, 0x450a, 0x8fa3, 0xde05, 0xb2f5, 0x7793, 0x7269, 0xbabb,
+ 0xc3b3, 0xd49b, 0x3377, 0x03c6, 0xe694, 0xc760, 0xd3cb, 0x2811}},
+ /* example with group size as modulus needing 565 divsteps starting at delta=1/2 */
+ {{0x8432, 0x5ceb, 0xa847, 0x6f1e, 0x51dd, 0x535a, 0x6ddc, 0x70ce,
+ 0x6e70, 0xc1f6, 0x18f2, 0x2a7e, 0xc8e7, 0x39f8, 0x7e96, 0xebbf},
+ {0x4141, 0xd036, 0x5e8c, 0xbfd2, 0xa03b, 0xaf48, 0xdce6, 0xbaae,
+ 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+ {0x257e, 0x449f, 0x689f, 0x89aa, 0x3989, 0xb661, 0x376c, 0x1e32,
+ 0x654c, 0xee2e, 0xf4e2, 0x33c8, 0x3f2f, 0x9716, 0x6046, 0xcaa3}},
+ /* Test case with the group size as modulus, needing 981 divsteps with
+ broken eta handling. */
+ {{0xfeb9, 0xb877, 0xee41, 0x7fa3, 0x87da, 0x94c4, 0x9d04, 0xc5ae,
+ 0x5708, 0x0994, 0xfc79, 0x0916, 0xbf32, 0x3ad8, 0xe11c, 0x5ca2},
+ {0x4141, 0xd036, 0x5e8c, 0xbfd2, 0xa03b, 0xaf48, 0xdce6, 0xbaae,
+ 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+ {0x0f12, 0x075e, 0xce1c, 0x6f92, 0xc80f, 0xca92, 0x9a04, 0x6126,
+ 0x4b6c, 0x57d6, 0xca31, 0x97f3, 0x1f99, 0xf4fd, 0xda4d, 0x42ce}},
+ /* Test case with the group size as modulus, input = 0. */
+ {{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
+ {0x4141, 0xd036, 0x5e8c, 0xbfd2, 0xa03b, 0xaf48, 0xdce6, 0xbaae,
+ 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+ {0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}},
+ /* Test case with the group size as modulus, input = 1. */
+ {{0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
+ {0x4141, 0xd036, 0x5e8c, 0xbfd2, 0xa03b, 0xaf48, 0xdce6, 0xbaae,
+ 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+ {0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}},
+ /* Test case with the group size as modulus, input = 2. */
+ {{0x0002, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
+ {0x4141, 0xd036, 0x5e8c, 0xbfd2, 0xa03b, 0xaf48, 0xdce6, 0xbaae,
+ 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+ {0x20a1, 0x681b, 0x2f46, 0xdfe9, 0x501d, 0x57a4, 0x6e73, 0x5d57,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x7fff}},
+ /* Test case with the group size as modulus, input = group - 1. */
+ {{0x4140, 0xd036, 0x5e8c, 0xbfd2, 0xa03b, 0xaf48, 0xdce6, 0xbaae,
+ 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+ {0x4141, 0xd036, 0x5e8c, 0xbfd2, 0xa03b, 0xaf48, 0xdce6, 0xbaae,
+ 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+ {0x4140, 0xd036, 0x5e8c, 0xbfd2, 0xa03b, 0xaf48, 0xdce6, 0xbaae,
+ 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff}},
+
+ /* Test cases with the field size as modulus. */
+
+ /* Test case with the field size as modulus, needing 637 divsteps. */
+ {{0x9ec3, 0x1919, 0xca84, 0x7c11, 0xf996, 0x06f3, 0x5408, 0x6688,
+ 0x1320, 0xdb8a, 0x632a, 0x0dcb, 0x8a84, 0x6bee, 0x9c95, 0xe34e},
+ {0xfc2f, 0xffff, 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+ {0x18e5, 0x19b6, 0xdf92, 0x1aaa, 0x09fb, 0x8a3f, 0x52b0, 0x8701,
+ 0xac0c, 0x2582, 0xda44, 0x9bcc, 0x6828, 0x1c53, 0xbd8f, 0xbd2c}},
+ /* example with field size as modulus needing 637 divsteps */
+ {{0xaec3, 0xa7cf, 0x2f2d, 0x0693, 0x5ad5, 0xa8ff, 0x7ec7, 0x30ff,
+ 0x0c8b, 0xc242, 0xcab2, 0x063a, 0xf86e, 0x6057, 0x9cbd, 0xf6d8},
+ {0xfc2f, 0xffff, 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+ {0x0310, 0x579d, 0xcb38, 0x9030, 0x3ded, 0x9bb9, 0x1234, 0x63ce,
+ 0x0c63, 0x8e3d, 0xacfe, 0x3c20, 0xdc85, 0xf859, 0x919e, 0x1d45}},
+ /* example with field size as modulus needing 564 divsteps starting at delta=1/2 */
+ {{0x63ae, 0x8d10, 0x0071, 0xdb5c, 0xb454, 0x78d1, 0x744a, 0x5f8e,
+ 0xe4d8, 0x87b1, 0x8e62, 0x9590, 0xcede, 0xa070, 0x36b4, 0x7f6f},
+ {0xfc2f, 0xffff, 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+ {0xfdc8, 0xe8d5, 0xbe15, 0x9f86, 0xa5fe, 0xf18e, 0xa7ff, 0xd291,
+ 0xf4c2, 0x9c87, 0xf150, 0x073e, 0x69b8, 0xf7c4, 0xee4b, 0xc7e6}},
+ /* Test case with the field size as modulus, needing 935 divsteps with
+ broken eta handling. */
+ {{0x1b37, 0xbdc3, 0x8bcd, 0x25e3, 0x1eae, 0x567d, 0x30b6, 0xf0d8,
+ 0x9277, 0x0cf8, 0x9c2e, 0xecd7, 0x631d, 0xe38f, 0xd4f8, 0x5c93},
+ {0xfc2f, 0xffff, 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+ {0x1622, 0xe05b, 0xe880, 0x7de9, 0x3e45, 0xb682, 0xee6c, 0x67ed,
+ 0xa179, 0x15db, 0x6b0d, 0xa656, 0x7ccb, 0x8ef7, 0xa2ff, 0xe279}},
+ /* Test case with the field size as modulus, input = 0. */
+ {{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
+ {0xfc2f, 0xffff, 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+ {0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}},
+ /* Test case with the field size as modulus, input = 1. */
+ {{0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
+ {0xfc2f, 0xffff, 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+ {0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}},
+ /* Test case with the field size as modulus, input = 2. */
+ {{0x0002, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
+ {0xfc2f, 0xffff, 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+ {0xfe18, 0x7fff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x7fff}},
+ /* Test case with the field size as modulus, input = field - 1. */
+ {{0xfc2e, 0xffff, 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+ {0xfc2f, 0xffff, 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+ {0xfc2e, 0xffff, 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff}},
+
+ /* Selected from a large number of random inputs to reach small/large
+ * d/e values in various configurations. */
+ {{0x3a08, 0x23e1, 0x4d8c, 0xe606, 0x3263, 0x67af, 0x9bf1, 0x9d70,
+ 0xf5fd, 0x12e4, 0x03c8, 0xb9ca, 0xe847, 0x8c5d, 0x6322, 0xbd30},
+ {0x8359, 0x59dd, 0x1831, 0x7c1a, 0x1e83, 0xaee1, 0x770d, 0xcea8,
+ 0xfbb1, 0xeed6, 0x10b5, 0xe2c6, 0x36ea, 0xee17, 0xe32c, 0xffff},
+ {0x1727, 0x0f36, 0x6f85, 0x5d0c, 0xca6c, 0x3072, 0x9628, 0x5842,
+ 0xcb44, 0x7c2b, 0xca4f, 0x62e5, 0x29b1, 0x6ffd, 0x9055, 0xc196}},
+ {{0x905d, 0x41c8, 0xa2ff, 0x295b, 0x72bb, 0x4679, 0x6d01, 0x2c98,
+ 0xb3e0, 0xc537, 0xa310, 0xe07e, 0xe72f, 0x4999, 0x1148, 0xf65e},
+ {0x5b41, 0x4239, 0x3c37, 0x5130, 0x30e3, 0xff35, 0xc51f, 0x1a43,
+ 0xdb23, 0x13cf, 0x9f49, 0xf70c, 0x5e70, 0xd411, 0x3005, 0xf8c6},
+ {0xc30e, 0x68f0, 0x201a, 0xe10c, 0x864a, 0x6243, 0xe946, 0x43ae,
+ 0xf3f1, 0x52dc, 0x1f7f, 0x50d4, 0x2797, 0x064c, 0x5ca4, 0x90e3}},
+ {{0xf1b5, 0xc6e5, 0xd2c4, 0xff95, 0x27c5, 0x0c92, 0x5d19, 0x7ae5,
+ 0x4fbe, 0x5438, 0x99e1, 0x880d, 0xd892, 0xa05c, 0x6ffd, 0x7eac},
+ {0x2153, 0xcc9d, 0xfc6c, 0x8358, 0x49a1, 0x01e2, 0xcef0, 0x4969,
+ 0xd69a, 0x8cef, 0xf5b2, 0xfd95, 0xdcc2, 0x71f4, 0x6ae2, 0xceeb},
+ {0x9b2e, 0xcdc6, 0x0a5c, 0x7317, 0x9084, 0xe228, 0x56cf, 0xd512,
+ 0x628a, 0xce21, 0x3473, 0x4e13, 0x8823, 0x1ed0, 0x34d0, 0xbfa3}},
+ {{0x5bae, 0x53e5, 0x5f4d, 0x21ca, 0xb875, 0x8ecf, 0x9aa6, 0xbe3c,
+ 0x9f96, 0x7b82, 0x375d, 0x4d3e, 0x491c, 0xb1eb, 0x04c9, 0xb6c8},
+ {0xfcfd, 0x10b7, 0x73b2, 0xd23b, 0xa357, 0x67da, 0x0d9f, 0x8702,
+ 0xa037, 0xff8e, 0x0e8b, 0x1801, 0x2c5c, 0x4e6e, 0x4558, 0xfff2},
+ {0xc50f, 0x5654, 0x6713, 0x5ef5, 0xa7ce, 0xa647, 0xc832, 0x69ce,
+ 0x1d5c, 0x4310, 0x0746, 0x5a01, 0x96ea, 0xde4b, 0xa88b, 0x5543}},
+ {{0xdc7f, 0x5e8c, 0x89d1, 0xb077, 0xd521, 0xcf90, 0x32fa, 0x5737,
+ 0x839e, 0x1464, 0x007c, 0x09c6, 0x9371, 0xe8ea, 0xc1cb, 0x75c4},
+ {0xe3a3, 0x107f, 0xa82a, 0xa375, 0x4578, 0x60f4, 0x75c9, 0x5ee4,
+ 0x3fd7, 0x2736, 0x2871, 0xd3d2, 0x5f1d, 0x1abb, 0xa764, 0xffff},
+ {0x45c6, 0x1f2e, 0xb14c, 0x84d7, 0x7bb7, 0x5a04, 0x0504, 0x3f33,
+ 0x5cc1, 0xb07a, 0x6a6c, 0x786f, 0x647f, 0xe1d7, 0x78a2, 0x4cf4}},
+ {{0xc006, 0x356f, 0x8cd2, 0x967b, 0xb49e, 0x2d4e, 0x14bf, 0x4bcb,
+ 0xddab, 0xd3f9, 0xa068, 0x2c1c, 0xd242, 0xa56d, 0xf2c7, 0x5f97},
+ {0x465b, 0xb745, 0x0e0d, 0x69a9, 0x987d, 0xcb37, 0xf637, 0xb311,
+ 0xc4d6, 0x2ddb, 0xf68f, 0x2af9, 0x959d, 0x3f53, 0x98f2, 0xf640},
+ {0xc0f2, 0x6bfb, 0xf5c3, 0x91c1, 0x6b05, 0x0825, 0x5ca0, 0x7df7,
+ 0x9d55, 0x6d9e, 0xfe94, 0x2ad9, 0xd9f0, 0xe68b, 0xa72b, 0xd1b2}},
+ {{0x2279, 0x61ba, 0x5bc6, 0x136b, 0xf544, 0x717c, 0xafda, 0x02bd,
+ 0x79af, 0x1fad, 0xea09, 0x81bb, 0x932b, 0x32c9, 0xdf1d, 0xe576},
+ {0x8215, 0x7817, 0xca82, 0x43b0, 0x9b06, 0xea65, 0x1291, 0x0621,
+ 0x0089, 0x46fe, 0xc5a6, 0xddd7, 0x8065, 0xc6a0, 0x214b, 0xfc64},
+ {0x04bf, 0x6f2a, 0x86b2, 0x841a, 0x4a95, 0xc632, 0x97b7, 0x5821,
+ 0x2b18, 0x1bb0, 0x3e97, 0x935e, 0xcc7d, 0x066b, 0xd513, 0xc251}},
+ {{0x76e8, 0x5bc2, 0x3eaa, 0x04fc, 0x9974, 0x92c1, 0x7c15, 0xfa89,
+ 0x1151, 0x36ee, 0x48b2, 0x049c, 0x5f16, 0xcee4, 0x925b, 0xe98e},
+ {0x913f, 0x0a2d, 0xa185, 0x9fea, 0xda5a, 0x4025, 0x40d7, 0x7cfa,
+ 0x88ca, 0xbbe8, 0xb265, 0xb7e4, 0x6cb1, 0xed64, 0xc6f9, 0xffb5},
+ {0x6ab1, 0x1a86, 0x5009, 0x152b, 0x1cc4, 0xe2c8, 0x960b, 0x19d0,
+ 0x3554, 0xc562, 0xd013, 0xcf91, 0x10e1, 0x7933, 0xe195, 0xcf49}},
+ {{0x9cb5, 0xd2d7, 0xc6ed, 0xa818, 0xb495, 0x06ee, 0x0f4a, 0x06e3,
+ 0x4c5a, 0x80ce, 0xd49a, 0x4cd7, 0x7487, 0x92af, 0xe516, 0x676c},
+ {0xd6e9, 0x6b85, 0x619a, 0xb52c, 0x20a0, 0x2f79, 0x3545, 0x1edd,
+ 0x5a6f, 0x8082, 0x9b80, 0xf8f8, 0xc78a, 0xd0a3, 0xadf4, 0xffff},
+ {0x01c2, 0x2118, 0xef5e, 0xa877, 0x046a, 0xd2c2, 0x2ad5, 0x951c,
+ 0x8900, 0xa5c9, 0x8d0f, 0x6b61, 0x55d3, 0xd572, 0x48de, 0x9219}},
+ {{0x5114, 0x0644, 0x23dd, 0x01d3, 0xc101, 0xa659, 0xea17, 0x640f,
+ 0xf767, 0x2644, 0x9cec, 0xd8ba, 0xd6da, 0x9156, 0x8aeb, 0x875a},
+ {0xc1bf, 0xdae9, 0xe96b, 0xce77, 0xf7a1, 0x3e99, 0x5c2e, 0x973b,
+ 0xd048, 0x5bd0, 0x4e8a, 0xcb85, 0xce39, 0x37f5, 0x815d, 0xffff},
+ {0x48cc, 0x35b6, 0x26d4, 0x2ea6, 0x50d6, 0xa2f9, 0x64b6, 0x03bf,
+ 0xd00c, 0xe057, 0x3343, 0xfb79, 0x3ce5, 0xf717, 0xc5af, 0xe185}},
+ {{0x13ff, 0x6c76, 0x2077, 0x16e0, 0xd5ca, 0xf2ad, 0x8dba, 0x8f49,
+ 0x7887, 0x16f9, 0xb646, 0xfc87, 0xfa31, 0x5096, 0xf08c, 0x3fbe},
+ {0x8139, 0x6fd7, 0xf6df, 0xa7bf, 0x6699, 0x5361, 0x6f65, 0x13c8,
+ 0xf4d1, 0xe28f, 0xc545, 0x0a8c, 0x5274, 0xb0a6, 0xffff, 0xffff},
+ {0x22ca, 0x0cd6, 0xc1b5, 0xb064, 0x44a7, 0x297b, 0x495f, 0x34ac,
+ 0xfa95, 0xec62, 0xf08d, 0x621c, 0x66a6, 0xba94, 0x84c6, 0x8ee0}},
+ {{0xaa30, 0x312e, 0x439c, 0x4e88, 0x2e2f, 0x32dc, 0xb880, 0xa28e,
+ 0xf795, 0xc910, 0xb406, 0x8dd7, 0xb187, 0xa5a5, 0x38f1, 0xe49e},
+ {0xfb19, 0xf64a, 0xba6a, 0x8ec2, 0x7255, 0xce89, 0x2cf9, 0x9cba,
+ 0xe1fe, 0x50da, 0x1705, 0xac52, 0xe3d4, 0x4269, 0x0648, 0xfd77},
+ {0xb4c8, 0x6e8a, 0x2b5f, 0x4c2d, 0x5a67, 0xa7bb, 0x7d6d, 0x5569,
+ 0xa0ea, 0x244a, 0xc0f2, 0xf73d, 0x58cf, 0xac7f, 0xd32b, 0x3018}},
+ {{0xc953, 0x1ae1, 0xae46, 0x8709, 0x19c2, 0xa986, 0x9abe, 0x1611,
+ 0x0395, 0xd5ab, 0xf0f6, 0xb5b0, 0x5b2b, 0x0317, 0x80ba, 0x376d},
+ {0xfe77, 0xbc03, 0xac2f, 0x9d00, 0xa175, 0x293d, 0x3b56, 0x0e3a,
+ 0x0a9c, 0xf40c, 0x690e, 0x1508, 0x95d4, 0xddc4, 0xe805, 0xffff},
+ {0xb1ce, 0x0929, 0xa5fe, 0x4b50, 0x9d5d, 0x8187, 0x2557, 0x4376,
+ 0x11ba, 0xdcef, 0xc1f3, 0xd531, 0x1824, 0x93f6, 0xd81f, 0x8f83}},
+ {{0xb8d2, 0xb900, 0x4a0c, 0x7188, 0xa5bf, 0x1b0b, 0x2ae5, 0xa35b,
+ 0x98e0, 0x610c, 0x86db, 0x2487, 0xa267, 0x002c, 0xebb6, 0xc5f4},
+ {0x9cdd, 0x1c1b, 0x2f06, 0x43d1, 0xce47, 0xc334, 0x6e60, 0xc016,
+ 0x989e, 0x0ab2, 0x0cac, 0x1196, 0xe2d9, 0x2e04, 0xc62b, 0xffff},
+ {0xdc36, 0x1f05, 0x6aa9, 0x7a20, 0x944f, 0x2fd3, 0xa553, 0xdb4f,
+ 0xbd5c, 0x3a75, 0x25d4, 0xe20e, 0xa387, 0x1410, 0xdbb1, 0x1b60}},
+ {{0x76b3, 0x2207, 0x4930, 0x5dd7, 0x65a0, 0xd55c, 0xb443, 0x53b7,
+ 0x5c22, 0x818a, 0xb2e7, 0x9de8, 0x9985, 0xed45, 0x33b1, 0x53e8},
+ {0x7913, 0x44e1, 0xf15b, 0x5edd, 0x34f3, 0x4eba, 0x0758, 0x7104,
+ 0x32d9, 0x28f3, 0x4401, 0x85c5, 0xb695, 0xb899, 0xc0f2, 0xffff},
+ {0x7f43, 0xd202, 0x24c9, 0x69f3, 0x74dc, 0x1a69, 0xeaee, 0x5405,
+ 0x1755, 0x4bb8, 0x04e3, 0x2fd2, 0xada8, 0x39eb, 0x5b4d, 0x96ca}},
+ {{0x807b, 0x7112, 0xc088, 0xdafd, 0x02fa, 0x9d95, 0x5e42, 0xc033,
+ 0xde0a, 0xeecf, 0x8e90, 0x8da1, 0xb17e, 0x9a5b, 0x4c6d, 0x1914},
+ {0x4871, 0xd1cb, 0x47d7, 0x327f, 0x09ec, 0x97bb, 0x2fae, 0xd346,
+ 0x6b78, 0x3707, 0xfeb2, 0xa6ab, 0x13df, 0x76b0, 0x8fb9, 0xffb3},
+ {0x179e, 0xb63b, 0x4784, 0x231e, 0x9f42, 0x7f1a, 0xa3fb, 0xdd8c,
+ 0xd1eb, 0xb4c9, 0x8ca7, 0x018c, 0xf691, 0x576c, 0xa7d6, 0xce27}},
+ {{0x5f45, 0x7c64, 0x083d, 0xedd5, 0x08a0, 0x0c64, 0x6c6f, 0xec3c,
+ 0xe2fb, 0x352c, 0x9303, 0x75e4, 0xb4e0, 0x8b09, 0xaca4, 0x7025},
+ {0x1025, 0xb482, 0xfed5, 0xa678, 0x8966, 0x9359, 0x5329, 0x98bb,
+ 0x85b2, 0x73ba, 0x9982, 0x6fdc, 0xf190, 0xbe8c, 0xdc5c, 0xfd93},
+ {0x83a2, 0x87a4, 0xa680, 0x52a1, 0x1ba1, 0x8848, 0x5db7, 0x9744,
+ 0x409c, 0x0745, 0x0e1e, 0x1cfc, 0x00cd, 0xf573, 0x2071, 0xccaa}},
+ {{0xf61f, 0x63d4, 0x536c, 0x9eb9, 0x5ddd, 0xbb11, 0x9014, 0xe904,
+ 0xfe01, 0x6b45, 0x1858, 0xcb5b, 0x4c38, 0x43e1, 0x381d, 0x7f94},
+ {0xf61f, 0x63d4, 0xd810, 0x7ca3, 0x8a04, 0x4b83, 0x11fc, 0xdf94,
+ 0x4169, 0xbd05, 0x608e, 0x7151, 0x4fbf, 0xb31a, 0x38a7, 0xa29b},
+ {0xe621, 0xdfa5, 0x3d06, 0x1d03, 0x81e6, 0x00da, 0x53a6, 0x965e,
+ 0x93e5, 0x2164, 0x5b61, 0x59b8, 0xa629, 0x8d73, 0x699a, 0x6111}},
+ {{0x4cc3, 0xd29e, 0xf4a3, 0x3428, 0x2048, 0xeec9, 0x5f50, 0x99a4,
+ 0x6de9, 0x05f2, 0x5aa9, 0x5fd2, 0x98b4, 0x1adc, 0x225f, 0x777f},
+ {0xe649, 0x37da, 0x5ba6, 0x5765, 0x3f4a, 0x8a1c, 0x2e79, 0xf550,
+ 0x1a54, 0xcd1e, 0x7218, 0x3c3c, 0x6311, 0xfe28, 0x95fb, 0xed97},
+ {0xe9b6, 0x0c47, 0x3f0e, 0x849b, 0x11f8, 0xe599, 0x5e4d, 0xd618,
+ 0xa06d, 0x33a0, 0x9a3e, 0x44db, 0xded8, 0x10f0, 0x94d2, 0x81fb}},
+ {{0x2e59, 0x7025, 0xd413, 0x455a, 0x1ce3, 0xbd45, 0x7263, 0x27f7,
+ 0x23e3, 0x518e, 0xbe06, 0xc8c4, 0xe332, 0x4276, 0x68b4, 0xb166},
+ {0x596f, 0x0cf6, 0xc8ec, 0x787b, 0x04c1, 0x473c, 0xd2b8, 0x8d54,
+ 0x9cdf, 0x77f2, 0xd3f3, 0x6735, 0x0638, 0xf80e, 0x9467, 0xc6aa},
+ {0xc7e7, 0x1822, 0xb62a, 0xec0d, 0x89cd, 0x7846, 0xbfa2, 0x35d5,
+ 0xfa38, 0x870f, 0x494b, 0x1697, 0x8b17, 0xf904, 0x10b6, 0x9822}},
+ {{0x6d5b, 0x1d4f, 0x0aaf, 0x807b, 0x35fb, 0x7ee8, 0x00c6, 0x059a,
+ 0xddf0, 0x1fb1, 0xc38a, 0xd78e, 0x2aa4, 0x79e7, 0xad28, 0xc3f1},
+ {0xe3bb, 0x174e, 0xe0a8, 0x74b6, 0xbd5b, 0x35f6, 0x6d23, 0x6328,
+ 0xc11f, 0x83e1, 0xf928, 0xa918, 0x838e, 0xbf43, 0xe243, 0xfffb},
+ {0x9cf2, 0x6b8b, 0x3476, 0x9d06, 0xdcf2, 0xdb8a, 0x89cd, 0x4857,
+ 0x75c2, 0xabb8, 0x490b, 0xc9bd, 0x890e, 0xe36e, 0xd552, 0xfffa}},
+ {{0x2f09, 0x9d62, 0xa9fc, 0xf090, 0xd6d1, 0x9d1d, 0x1828, 0xe413,
+ 0xc92b, 0x3d5a, 0x1373, 0x368c, 0xbaf2, 0x2158, 0x71eb, 0x08a3},
+ {0x2f09, 0x1d62, 0x4630, 0x0de1, 0x06dc, 0xf7f1, 0xc161, 0x1e92,
+ 0x7495, 0x97e4, 0x94b6, 0xa39e, 0x4f1b, 0x18f8, 0x7bd4, 0x0c4c},
+ {0xeb3d, 0x723d, 0x0907, 0x525b, 0x463a, 0x49a8, 0xc6b8, 0xce7f,
+ 0x740c, 0x0d7d, 0xa83b, 0x457f, 0xae8e, 0xc6af, 0xd331, 0x0475}},
+ {{0x6abd, 0xc7af, 0x3e4e, 0x95fd, 0x8fc4, 0xee25, 0x1f9c, 0x0afe,
+ 0x291d, 0xcde0, 0x48f4, 0xb2e8, 0xf7af, 0x8f8d, 0x0bd6, 0x078d},
+ {0x4037, 0xbf0e, 0x2081, 0xf363, 0x13b2, 0x381e, 0xfb6e, 0x818e,
+ 0x27e4, 0x5662, 0x18b0, 0x0cd2, 0x81f5, 0x9415, 0x0d6c, 0xf9fb},
+ {0xd205, 0x0981, 0x0498, 0x1f08, 0xdb93, 0x1732, 0x0579, 0x1424,
+ 0xad95, 0x642f, 0x050c, 0x1d6d, 0xfc95, 0xfc4a, 0xd41b, 0x3521}},
+ {{0xf23a, 0x4633, 0xaef4, 0x1a92, 0x3c8b, 0x1f09, 0x30f3, 0x4c56,
+ 0x2a2f, 0x4f62, 0xf5e4, 0x8329, 0x63cc, 0xb593, 0xec6a, 0xc428},
+ {0x93a7, 0xfcf6, 0x606d, 0xd4b2, 0x2aad, 0x28b4, 0xc65b, 0x8998,
+ 0x4e08, 0xd178, 0x0900, 0xc82b, 0x7470, 0xa342, 0x7c0f, 0xffff},
+ {0x315f, 0xf304, 0xeb7b, 0xe5c3, 0x1451, 0x6311, 0x8f37, 0x93a8,
+ 0x4a38, 0xa6c6, 0xe393, 0x1087, 0x6301, 0xd673, 0x4ec4, 0xffff}},
+ {{0x892e, 0xeed0, 0x1165, 0xcbc1, 0x5545, 0xa280, 0x7243, 0x10c9,
+ 0x9536, 0x36af, 0xb3fc, 0x2d7c, 0xe8a5, 0x09d6, 0xe1d4, 0xe85d},
+ {0xae09, 0xc28a, 0xd777, 0xbd80, 0x23d6, 0xf980, 0xeb7c, 0x4e0e,
+ 0xf7dc, 0x6475, 0xf10a, 0x2d33, 0x5dfd, 0x797a, 0x7f1c, 0xf71a},
+ {0x4064, 0x8717, 0xd091, 0x80b0, 0x4527, 0x8442, 0xac8b, 0x9614,
+ 0xc633, 0x35f5, 0x7714, 0x2e83, 0x4aaa, 0xd2e4, 0x1acd, 0x0562}},
+ {{0xdb64, 0x0937, 0x308b, 0x53b0, 0x00e8, 0xc77f, 0x2f30, 0x37f7,
+ 0x79ce, 0xeb7f, 0xde81, 0x9286, 0xafda, 0x0e62, 0xae00, 0x0067},
+ {0x2cc7, 0xd362, 0xb161, 0x0557, 0x4ff2, 0xb9c8, 0x06fe, 0x5f2b,
+ 0xde33, 0x0190, 0x28c6, 0xb886, 0xee2b, 0x5a4e, 0x3289, 0x0185},
+ {0x4215, 0x923e, 0xf34f, 0xb362, 0x88f8, 0xceec, 0xafdd, 0x7f42,
+ 0x0c57, 0x56b2, 0xa366, 0x6a08, 0x0826, 0xfb8f, 0x1b03, 0x0163}},
+ {{0xa4ba, 0x8408, 0x810a, 0xdeba, 0x47a3, 0x853a, 0xeb64, 0x2f74,
+ 0x3039, 0x038c, 0x7fbb, 0x498e, 0xd1e9, 0x46fb, 0x5691, 0x32a4},
+ {0xd749, 0xb49d, 0x20b7, 0x2af6, 0xd34a, 0xd2da, 0x0a10, 0xf781,
+ 0x58c9, 0x171f, 0x3cb6, 0x6337, 0x88cd, 0xcf1e, 0xb246, 0x7351},
+ {0xf729, 0xcf0a, 0x96ea, 0x032c, 0x4a8f, 0x42fe, 0xbac8, 0xec65,
+ 0x1510, 0x0d75, 0x4c17, 0x8d29, 0xa03f, 0x8b7e, 0x2c49, 0x0000}},
+ {{0x0fa4, 0x8e1c, 0x3788, 0xba3c, 0x8d52, 0xd89d, 0x12c8, 0xeced,
+ 0x9fe6, 0x9b88, 0xecf3, 0xe3c8, 0xac48, 0x76ed, 0xf23e, 0xda79},
+ {0x1103, 0x227c, 0x5b00, 0x3fcf, 0xc5d0, 0x2d28, 0x8020, 0x4d1c,
+ 0xc6b9, 0x67f9, 0x6f39, 0x989a, 0xda53, 0x3847, 0xd416, 0xe0d0},
+ {0xdd8e, 0xcf31, 0x3710, 0x7e44, 0xa511, 0x933c, 0x0cc3, 0x5145,
+ 0xf632, 0x5e1d, 0x038f, 0x5ce7, 0x7265, 0xda9d, 0xded6, 0x08f8}},
+ {{0xe2c8, 0x91d5, 0xa5f5, 0x735f, 0x6b58, 0x56dc, 0xb39d, 0x5c4a,
+ 0x57d0, 0xa1c2, 0xd92f, 0x9ad4, 0xf7c4, 0x51dd, 0xaf5c, 0x0096},
+ {0x1739, 0x7207, 0x7505, 0xbf35, 0x42de, 0x0a29, 0xa962, 0xdedf,
+ 0x53e8, 0x12bf, 0xcde7, 0xd8e2, 0x8d4d, 0x2c4b, 0xb1b1, 0x0628},
+ {0x992d, 0xe3a7, 0xb422, 0xc198, 0x23ab, 0xa6ef, 0xb45d, 0x50da,
+ 0xa738, 0x014a, 0x2310, 0x85fb, 0x5fe8, 0x1b18, 0x1774, 0x03a7}},
+ {{0x1f16, 0x2b09, 0x0236, 0xee90, 0xccf9, 0x9775, 0x8130, 0x4c91,
+ 0x9091, 0x310b, 0x6dc4, 0x86f6, 0xc2e8, 0xef60, 0xfc0e, 0xf3a4},
+ {0x9f49, 0xac15, 0x02af, 0x110f, 0xc59d, 0x5677, 0xa1a9, 0x38d5,
+ 0x914f, 0xa909, 0x3a3a, 0x4a39, 0x3703, 0xea30, 0x73da, 0xffad},
+ {0x15ed, 0xdd16, 0x83c7, 0x270a, 0x862f, 0xd8ad, 0xcaa1, 0x5f41,
+ 0x99a9, 0x3fc8, 0x7bb2, 0x360a, 0xb06d, 0xfadc, 0x1b36, 0xffa8}},
+ {{0xc4e0, 0xb8fd, 0x5106, 0xe169, 0x754c, 0xa58c, 0xc413, 0x8224,
+ 0x5483, 0x63ec, 0xd477, 0x8473, 0x4778, 0x9281, 0x0000, 0x0000},
+ {0x85e1, 0xff54, 0xb200, 0xe413, 0xf4f4, 0x4c0f, 0xfcec, 0xc183,
+ 0x60d3, 0x1b0c, 0x3834, 0x601c, 0x943c, 0xbe6e, 0x0002, 0x0000},
+ {0xf4f8, 0xfd5e, 0x61ef, 0xece8, 0x9199, 0xe5c4, 0x05a6, 0xe6c3,
+ 0xc4ae, 0x8b28, 0x66b1, 0x8a95, 0x9ece, 0x8f4a, 0x0001, 0x0000}},
+ {{0xeae9, 0xa1b4, 0xc6d8, 0x2411, 0x2b5a, 0x1dd0, 0x2dc9, 0xb57b,
+ 0x5ccd, 0x4957, 0xaf59, 0xa04b, 0x5f42, 0xab7c, 0x2826, 0x526f},
+ {0xf407, 0x165a, 0xb724, 0x2f12, 0x2ea1, 0x470b, 0x4464, 0xbd35,
+ 0x606f, 0xd73e, 0x50d3, 0x8a7f, 0x8029, 0x7ffc, 0xbe31, 0x6cfb},
+ {0x8171, 0x1f4c, 0xced2, 0x9c99, 0x6d7e, 0x5a0f, 0xfefb, 0x59e3,
+ 0xa0c8, 0xabd9, 0xc4c5, 0x57d3, 0xbfa3, 0x4f11, 0x96a2, 0x5a7d}},
+ {{0xe068, 0x4cc0, 0x8bcd, 0xc903, 0x9e52, 0xb3e1, 0xd745, 0x0995,
+ 0xdd8f, 0xf14b, 0xd2ac, 0xd65a, 0xda1d, 0xa742, 0xbac5, 0x474c},
+ {0x7481, 0xf2ad, 0x9757, 0x2d82, 0xb683, 0xb16b, 0x0002, 0x7b60,
+ 0x8f0c, 0x2594, 0x8f64, 0x3b7a, 0x3552, 0x8d9d, 0xb9d7, 0x67eb},
+ {0xcaab, 0xb9a1, 0xf966, 0xe311, 0x5b34, 0x0fa0, 0x6abc, 0x8134,
+ 0xab3d, 0x90f6, 0x1984, 0x9232, 0xec17, 0x74e5, 0x2ceb, 0x434e}},
+ {{0x0fb1, 0x7a55, 0x1a5c, 0x53eb, 0xd7b3, 0x7a01, 0xca32, 0x31f6,
+ 0x3b74, 0x679e, 0x1501, 0x6c57, 0xdb20, 0x8b7c, 0xd7d0, 0x8097},
+ {0xb127, 0xb20c, 0xe3a2, 0x96f3, 0xe0d8, 0xd50c, 0x14b4, 0x0b40,
+ 0x6eeb, 0xa258, 0x99db, 0x3c8c, 0x0f51, 0x4198, 0x3887, 0xffd0},
+ {0x0273, 0x9f8c, 0x9669, 0xbbba, 0x1c49, 0x767c, 0xc2af, 0x59f0,
+ 0x1366, 0xd397, 0x63ac, 0x6fe8, 0x1a9a, 0x1259, 0x01d0, 0x0016}},
+ {{0x7876, 0x2a35, 0xa24a, 0x433e, 0x5501, 0x573c, 0xd76d, 0xcb82,
+ 0x1334, 0xb4a6, 0xf290, 0xc797, 0xeae9, 0x2b83, 0x1e2b, 0x8b14},
+ {0x3885, 0x8aef, 0x9dea, 0x2b8c, 0xdd7c, 0xd7cd, 0xb0cc, 0x05ee,
+ 0x361b, 0x3800, 0xb0d4, 0x4c23, 0xbd3f, 0x5180, 0x9783, 0xff80},
+ {0xab36, 0x3104, 0xdae8, 0x0704, 0x4a28, 0x6714, 0x824b, 0x0051,
+ 0x8134, 0x1f6a, 0x712d, 0x1f03, 0x03b2, 0xecac, 0x377d, 0xfef9}}
+ };
+
+ int i, j, ok;
+
+ /* Test known inputs/outputs */
+ for (i = 0; (size_t)i < sizeof(CASES) / sizeof(CASES[0]); ++i) {
+ uint16_t out[16];
+ test_modinv32_uint16(out, CASES[i][0], CASES[i][1]);
+ for (j = 0; j < 16; ++j) CHECK(out[j] == CASES[i][2][j]);
+#ifdef SECP256K1_WIDEMUL_INT128
+ test_modinv64_uint16(out, CASES[i][0], CASES[i][1]);
+ for (j = 0; j < 16; ++j) CHECK(out[j] == CASES[i][2][j]);
#endif
+ }
+
+ for (i = 0; i < 100 * count; ++i) {
+ /* 256-bit numbers in 16-uint16_t's notation */
+ static const uint16_t ZERO[16] = {0};
+ uint16_t xd[16]; /* the number (in range [0,2^256)) to be inverted */
+ uint16_t md[16]; /* the modulus (odd, in range [3,2^256)) */
+ uint16_t id[16]; /* the inverse of xd mod md */
+
+ /* generate random xd and md, so that md is odd, md>1, xd<md, and gcd(xd,md)=1 */
+ do {
+ /* generate random xd and md (with many subsequent 0s and 1s) */
+ secp256k1_testrand256_test((unsigned char*)xd);
+ secp256k1_testrand256_test((unsigned char*)md);
+ md[0] |= 1; /* modulus must be odd */
+ /* If modulus is 1, find another one. */
+ ok = md[0] != 1;
+ for (j = 1; j < 16; ++j) ok |= md[j] != 0;
+ mulmod256(xd, xd, NULL, md); /* Make xd = xd mod md */
+ } while (!(ok && coprime(xd, md)));
+
+ test_modinv32_uint16(id, xd, md);
+#ifdef SECP256K1_WIDEMUL_INT128
+ test_modinv64_uint16(id, xd, md);
+#endif
+
+ /* In a few cases, also test with input=0 */
+ if (i < count) {
+ test_modinv32_uint16(id, ZERO, md);
+#ifdef SECP256K1_WIDEMUL_INT128
+ test_modinv64_uint16(id, ZERO, md);
+#endif
+ }
+ }
+}
/***** SCALAR TESTS *****/
+
void scalar_test(void) {
secp256k1_scalar s;
secp256k1_scalar s1;
secp256k1_scalar s2;
-#ifndef USE_NUM_NONE
- secp256k1_num snum, s1num, s2num;
- secp256k1_num order, half_order;
-#endif
unsigned char c[32];
/* Set 's' to a random scalar, with value 'snum'. */
@@ -819,16 +1566,6 @@ void scalar_test(void) {
random_scalar_order_test(&s2);
secp256k1_scalar_get_b32(c, &s2);
-#ifndef USE_NUM_NONE
- secp256k1_scalar_get_num(&snum, &s);
- secp256k1_scalar_get_num(&s1num, &s1);
- secp256k1_scalar_get_num(&s2num, &s2);
-
- secp256k1_scalar_order_get_num(&order);
- half_order = order;
- secp256k1_num_shift(&half_order, 1);
-#endif
-
{
int i;
/* Test that fetching groups of 4 bits from a scalar and recursing n(i)=16*n(i-1)+p(i) reconstructs it. */
@@ -868,80 +1605,6 @@ void scalar_test(void) {
CHECK(secp256k1_scalar_eq(&n, &s));
}
-#ifndef USE_NUM_NONE
- {
- /* Test that adding the scalars together is equal to adding their numbers together modulo the order. */
- secp256k1_num rnum;
- secp256k1_num r2num;
- secp256k1_scalar r;
- secp256k1_num_add(&rnum, &snum, &s2num);
- secp256k1_num_mod(&rnum, &order);
- secp256k1_scalar_add(&r, &s, &s2);
- secp256k1_scalar_get_num(&r2num, &r);
- CHECK(secp256k1_num_eq(&rnum, &r2num));
- }
-
- {
- /* Test that multiplying the scalars is equal to multiplying their numbers modulo the order. */
- secp256k1_scalar r;
- secp256k1_num r2num;
- secp256k1_num rnum;
- secp256k1_num_mul(&rnum, &snum, &s2num);
- secp256k1_num_mod(&rnum, &order);
- secp256k1_scalar_mul(&r, &s, &s2);
- secp256k1_scalar_get_num(&r2num, &r);
- CHECK(secp256k1_num_eq(&rnum, &r2num));
- /* The result can only be zero if at least one of the factors was zero. */
- CHECK(secp256k1_scalar_is_zero(&r) == (secp256k1_scalar_is_zero(&s) || secp256k1_scalar_is_zero(&s2)));
- /* The results can only be equal to one of the factors if that factor was zero, or the other factor was one. */
- CHECK(secp256k1_num_eq(&rnum, &snum) == (secp256k1_scalar_is_zero(&s) || secp256k1_scalar_is_one(&s2)));
- CHECK(secp256k1_num_eq(&rnum, &s2num) == (secp256k1_scalar_is_zero(&s2) || secp256k1_scalar_is_one(&s)));
- }
-
- {
- secp256k1_scalar neg;
- secp256k1_num negnum;
- secp256k1_num negnum2;
- /* Check that comparison with zero matches comparison with zero on the number. */
- CHECK(secp256k1_num_is_zero(&snum) == secp256k1_scalar_is_zero(&s));
- /* Check that comparison with the half order is equal to testing for high scalar. */
- CHECK(secp256k1_scalar_is_high(&s) == (secp256k1_num_cmp(&snum, &half_order) > 0));
- secp256k1_scalar_negate(&neg, &s);
- secp256k1_num_sub(&negnum, &order, &snum);
- secp256k1_num_mod(&negnum, &order);
- /* Check that comparison with the half order is equal to testing for high scalar after negation. */
- CHECK(secp256k1_scalar_is_high(&neg) == (secp256k1_num_cmp(&negnum, &half_order) > 0));
- /* Negating should change the high property, unless the value was already zero. */
- CHECK((secp256k1_scalar_is_high(&s) == secp256k1_scalar_is_high(&neg)) == secp256k1_scalar_is_zero(&s));
- secp256k1_scalar_get_num(&negnum2, &neg);
- /* Negating a scalar should be equal to (order - n) mod order on the number. */
- CHECK(secp256k1_num_eq(&negnum, &negnum2));
- secp256k1_scalar_add(&neg, &neg, &s);
- /* Adding a number to its negation should result in zero. */
- CHECK(secp256k1_scalar_is_zero(&neg));
- secp256k1_scalar_negate(&neg, &neg);
- /* Negating zero should still result in zero. */
- CHECK(secp256k1_scalar_is_zero(&neg));
- }
-
- {
- /* Test secp256k1_scalar_mul_shift_var. */
- secp256k1_scalar r;
- secp256k1_num one;
- secp256k1_num rnum;
- secp256k1_num rnum2;
- unsigned char cone[1] = {0x01};
- unsigned int shift = 256 + secp256k1_testrand_int(257);
- secp256k1_scalar_mul_shift_var(&r, &s1, &s2, shift);
- secp256k1_num_mul(&rnum, &s1num, &s2num);
- secp256k1_num_shift(&rnum, shift - 1);
- secp256k1_num_set_bin(&one, cone, 1);
- secp256k1_num_add(&rnum, &rnum, &one);
- secp256k1_num_shift(&rnum, 1);
- secp256k1_scalar_get_num(&rnum2, &r);
- CHECK(secp256k1_num_eq(&rnum, &rnum2));
- }
-
{
/* test secp256k1_scalar_shr_int */
secp256k1_scalar r;
@@ -955,34 +1618,6 @@ void scalar_test(void) {
CHECK(expected == low);
}
}
-#endif
-
- {
- /* Test that scalar inverses are equal to the inverse of their number modulo the order. */
- if (!secp256k1_scalar_is_zero(&s)) {
- secp256k1_scalar inv;
-#ifndef USE_NUM_NONE
- secp256k1_num invnum;
- secp256k1_num invnum2;
-#endif
- secp256k1_scalar_inverse(&inv, &s);
-#ifndef USE_NUM_NONE
- secp256k1_num_mod_inverse(&invnum, &snum, &order);
- secp256k1_scalar_get_num(&invnum2, &inv);
- CHECK(secp256k1_num_eq(&invnum, &invnum2));
-#endif
- secp256k1_scalar_mul(&inv, &inv, &s);
- /* Multiplying a scalar with its inverse must result in one. */
- CHECK(secp256k1_scalar_is_one(&inv));
- secp256k1_scalar_inverse(&inv, &inv);
- /* Inverting one must result in one. */
- CHECK(secp256k1_scalar_is_one(&inv));
-#ifndef USE_NUM_NONE
- secp256k1_scalar_get_num(&invnum, &inv);
- CHECK(secp256k1_num_is_one(&invnum));
-#endif
- }
- }
{
/* Test commutativity of add. */
@@ -1055,14 +1690,6 @@ void scalar_test(void) {
}
{
- /* Test square. */
- secp256k1_scalar r1, r2;
- secp256k1_scalar_sqr(&r1, &s1);
- secp256k1_scalar_mul(&r2, &s1, &s1);
- CHECK(secp256k1_scalar_eq(&r1, &r2));
- }
-
- {
/* Test multiplicative identity. */
secp256k1_scalar r1, v1;
secp256k1_scalar_set_int(&v1,1);
@@ -1126,48 +1753,6 @@ void run_scalar_tests(void) {
CHECK(secp256k1_scalar_is_zero(&o));
}
-#ifndef USE_NUM_NONE
- {
- /* Test secp256k1_scalar_set_b32 boundary conditions */
- secp256k1_num order;
- 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(&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(secp256k1_memcmp_var(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_eq(&scalar, &all_ones_minus_order));
- }
-#endif
-
{
/* Does check_overflow check catch all ones? */
static const secp256k1_scalar overflowed = SECP256K1_SCALAR_CONST(
@@ -1190,9 +1775,7 @@ void run_scalar_tests(void) {
secp256k1_scalar one;
secp256k1_scalar r1;
secp256k1_scalar r2;
-#if defined(USE_SCALAR_INV_NUM)
secp256k1_scalar zzv;
-#endif
int overflow;
unsigned char chal[33][2][32] = {
{{0xff, 0xff, 0x03, 0x07, 0x00, 0x00, 0x00, 0x00,
@@ -1742,10 +2325,8 @@ void run_scalar_tests(void) {
if (!secp256k1_scalar_is_zero(&y)) {
secp256k1_scalar_inverse(&zz, &y);
CHECK(!secp256k1_scalar_check_overflow(&zz));
-#if defined(USE_SCALAR_INV_NUM)
secp256k1_scalar_inverse_var(&zzv, &y);
CHECK(secp256k1_scalar_eq(&zzv, &zz));
-#endif
secp256k1_scalar_mul(&z, &z, &zz);
CHECK(!secp256k1_scalar_check_overflow(&z));
CHECK(secp256k1_scalar_eq(&x, &z));
@@ -1753,12 +2334,6 @@ void run_scalar_tests(void) {
CHECK(!secp256k1_scalar_check_overflow(&zz));
CHECK(secp256k1_scalar_eq(&one, &zz));
}
- secp256k1_scalar_mul(&z, &x, &x);
- CHECK(!secp256k1_scalar_check_overflow(&z));
- secp256k1_scalar_sqr(&zz, &x);
- CHECK(!secp256k1_scalar_check_overflow(&zz));
- CHECK(secp256k1_scalar_eq(&zz, &z));
- CHECK(secp256k1_scalar_eq(&r2, &zz));
}
}
}
@@ -1814,13 +2389,6 @@ int check_fe_equal(const secp256k1_fe *a, const secp256k1_fe *b) {
return secp256k1_fe_equal_var(&an, &bn);
}
-int check_fe_inverse(const secp256k1_fe *a, const secp256k1_fe *ai) {
- secp256k1_fe x;
- secp256k1_fe one = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1);
- secp256k1_fe_mul(&x, a, ai);
- return check_fe_equal(&x, &one);
-}
-
void run_field_convert(void) {
static const unsigned char b32[32] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
@@ -1940,52 +2508,6 @@ void run_field_misc(void) {
}
}
-void run_field_inv(void) {
- secp256k1_fe x, xi, xii;
- int i;
- for (i = 0; i < 10*count; i++) {
- random_fe_non_zero(&x);
- secp256k1_fe_inv(&xi, &x);
- CHECK(check_fe_inverse(&x, &xi));
- secp256k1_fe_inv(&xii, &xi);
- CHECK(check_fe_equal(&x, &xii));
- }
-}
-
-void run_field_inv_var(void) {
- secp256k1_fe x, xi, xii;
- int i;
- for (i = 0; i < 10*count; i++) {
- random_fe_non_zero(&x);
- secp256k1_fe_inv_var(&xi, &x);
- CHECK(check_fe_inverse(&x, &xi));
- secp256k1_fe_inv_var(&xii, &xi);
- CHECK(check_fe_equal(&x, &xii));
- }
-}
-
-void run_field_inv_all_var(void) {
- secp256k1_fe x[16], xi[16], xii[16];
- int i;
- /* Check it's safe to call for 0 elements */
- secp256k1_fe_inv_all_var(xi, x, 0);
- for (i = 0; i < count; i++) {
- size_t j;
- size_t len = secp256k1_testrand_int(15) + 1;
- for (j = 0; j < len; j++) {
- random_fe_non_zero(&x[j]);
- }
- secp256k1_fe_inv_all_var(xi, x, len);
- for (j = 0; j < len; j++) {
- CHECK(check_fe_inverse(&x[j], &xi[j]));
- }
- secp256k1_fe_inv_all_var(xii, xi, len);
- for (j = 0; j < len; j++) {
- CHECK(check_fe_equal(&x[j], &xii[j]));
- }
- }
-}
-
void run_sqr(void) {
secp256k1_fe x, s;
@@ -2050,6 +2572,318 @@ void run_sqrt(void) {
}
}
+/***** FIELD/SCALAR INVERSE TESTS *****/
+
+static const secp256k1_scalar scalar_minus_one = SECP256K1_SCALAR_CONST(
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFE,
+ 0xBAAEDCE6, 0xAF48A03B, 0xBFD25E8C, 0xD0364140
+);
+
+static const secp256k1_fe fe_minus_one = SECP256K1_FE_CONST(
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFE, 0xFFFFFC2E
+);
+
+/* These tests test the following identities:
+ *
+ * for x==0: 1/x == 0
+ * for x!=0: x*(1/x) == 1
+ * for x!=0 and x!=1: 1/(1/x - 1) + 1 == -1/(x-1)
+ */
+
+void test_inverse_scalar(secp256k1_scalar* out, const secp256k1_scalar* x, int var)
+{
+ secp256k1_scalar l, r, t;
+
+ (var ? secp256k1_scalar_inverse_var : secp256k1_scalar_inverse_var)(&l, x); /* l = 1/x */
+ if (out) *out = l;
+ if (secp256k1_scalar_is_zero(x)) {
+ CHECK(secp256k1_scalar_is_zero(&l));
+ return;
+ }
+ secp256k1_scalar_mul(&t, x, &l); /* t = x*(1/x) */
+ CHECK(secp256k1_scalar_is_one(&t)); /* x*(1/x) == 1 */
+ secp256k1_scalar_add(&r, x, &scalar_minus_one); /* r = x-1 */
+ if (secp256k1_scalar_is_zero(&r)) return;
+ (var ? secp256k1_scalar_inverse_var : secp256k1_scalar_inverse_var)(&r, &r); /* r = 1/(x-1) */
+ secp256k1_scalar_add(&l, &scalar_minus_one, &l); /* l = 1/x-1 */
+ (var ? secp256k1_scalar_inverse_var : secp256k1_scalar_inverse_var)(&l, &l); /* l = 1/(1/x-1) */
+ secp256k1_scalar_add(&l, &l, &secp256k1_scalar_one); /* l = 1/(1/x-1)+1 */
+ secp256k1_scalar_add(&l, &r, &l); /* l = 1/(1/x-1)+1 + 1/(x-1) */
+ CHECK(secp256k1_scalar_is_zero(&l)); /* l == 0 */
+}
+
+void test_inverse_field(secp256k1_fe* out, const secp256k1_fe* x, int var)
+{
+ secp256k1_fe l, r, t;
+
+ (var ? secp256k1_fe_inv_var : secp256k1_fe_inv)(&l, x) ; /* l = 1/x */
+ if (out) *out = l;
+ t = *x; /* t = x */
+ if (secp256k1_fe_normalizes_to_zero_var(&t)) {
+ CHECK(secp256k1_fe_normalizes_to_zero(&l));
+ return;
+ }
+ secp256k1_fe_mul(&t, x, &l); /* t = x*(1/x) */
+ secp256k1_fe_add(&t, &fe_minus_one); /* t = x*(1/x)-1 */
+ CHECK(secp256k1_fe_normalizes_to_zero(&t)); /* x*(1/x)-1 == 0 */
+ r = *x; /* r = x */
+ secp256k1_fe_add(&r, &fe_minus_one); /* r = x-1 */
+ if (secp256k1_fe_normalizes_to_zero_var(&r)) return;
+ (var ? secp256k1_fe_inv_var : secp256k1_fe_inv)(&r, &r); /* r = 1/(x-1) */
+ secp256k1_fe_add(&l, &fe_minus_one); /* l = 1/x-1 */
+ (var ? secp256k1_fe_inv_var : secp256k1_fe_inv)(&l, &l); /* l = 1/(1/x-1) */
+ secp256k1_fe_add(&l, &secp256k1_fe_one); /* l = 1/(1/x-1)+1 */
+ secp256k1_fe_add(&l, &r); /* l = 1/(1/x-1)+1 + 1/(x-1) */
+ CHECK(secp256k1_fe_normalizes_to_zero_var(&l)); /* l == 0 */
+}
+
+void run_inverse_tests(void)
+{
+ /* Fixed test cases for field inverses: pairs of (x, 1/x) mod p. */
+ static const secp256k1_fe fe_cases[][2] = {
+ /* 0 */
+ {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0),
+ SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)},
+ /* 1 */
+ {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1),
+ SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1)},
+ /* -1 */
+ {SECP256K1_FE_CONST(0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffffffe, 0xfffffc2e),
+ SECP256K1_FE_CONST(0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffffffe, 0xfffffc2e)},
+ /* 2 */
+ {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 2),
+ SECP256K1_FE_CONST(0x7fffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x7ffffe18)},
+ /* 2**128 */
+ {SECP256K1_FE_CONST(0, 0, 0, 1, 0, 0, 0, 0),
+ SECP256K1_FE_CONST(0xbcb223fe, 0xdc24a059, 0xd838091d, 0xd2253530, 0xffffffff, 0xffffffff, 0xffffffff, 0x434dd931)},
+ /* Input known to need 637 divsteps */
+ {SECP256K1_FE_CONST(0xe34e9c95, 0x6bee8a84, 0x0dcb632a, 0xdb8a1320, 0x66885408, 0x06f3f996, 0x7c11ca84, 0x19199ec3),
+ SECP256K1_FE_CONST(0xbd2cbd8f, 0x1c536828, 0x9bccda44, 0x2582ac0c, 0x870152b0, 0x8a3f09fb, 0x1aaadf92, 0x19b618e5)},
+ /* Input known to need 567 divsteps starting with delta=1/2. */
+ {SECP256K1_FE_CONST(0xf6bc3ba3, 0x636451c4, 0x3e46357d, 0x2c21d619, 0x0988e234, 0x15985661, 0x6672982b, 0xa7549bfc),
+ SECP256K1_FE_CONST(0xb024fdc7, 0x5547451e, 0x426c585f, 0xbd481425, 0x73df6b75, 0xeef6d9d0, 0x389d87d4, 0xfbb440ba)},
+ /* Input known to need 566 divsteps starting with delta=1/2. */
+ {SECP256K1_FE_CONST(0xb595d81b, 0x2e3c1e2f, 0x482dbc65, 0xe4865af7, 0x9a0a50aa, 0x29f9e618, 0x6f87d7a5, 0x8d1063ae),
+ SECP256K1_FE_CONST(0xc983337c, 0x5d5c74e1, 0x49918330, 0x0b53afb5, 0xa0428a0b, 0xce6eef86, 0x059bd8ef, 0xe5b908de)},
+ /* Set of 10 inputs accessing all 128 entries in the modinv32 divsteps_var table */
+ {SECP256K1_FE_CONST(0x00000000, 0x00000000, 0xe0ff1f80, 0x1f000000, 0x00000000, 0x00000000, 0xfeff0100, 0x00000000),
+ SECP256K1_FE_CONST(0x9faf9316, 0x77e5049d, 0x0b5e7a1b, 0xef70b893, 0x18c9e30c, 0x045e7fd7, 0x29eddf8c, 0xd62e9e3d)},
+ {SECP256K1_FE_CONST(0x621a538d, 0x511b2780, 0x35688252, 0x53f889a4, 0x6317c3ac, 0x32ba0a46, 0x6277c0d1, 0xccd31192),
+ SECP256K1_FE_CONST(0x38513b0c, 0x5eba856f, 0xe29e882e, 0x9b394d8c, 0x34bda011, 0xeaa66943, 0x6a841a4c, 0x6ae8bcff)},
+ {SECP256K1_FE_CONST(0x00000200, 0xf0ffff1f, 0x00000000, 0x0000e0ff, 0xffffffff, 0xfffcffff, 0xffffffff, 0xffff0100),
+ SECP256K1_FE_CONST(0x5da42a52, 0x3640de9e, 0x13e64343, 0x0c7591b7, 0x6c1e3519, 0xf048c5b6, 0x0484217c, 0xedbf8b2f)},
+ {SECP256K1_FE_CONST(0xd1343ef9, 0x4b952621, 0x7c52a2ee, 0x4ea1281b, 0x4ab46410, 0x9f26998d, 0xa686a8ff, 0x9f2103e8),
+ SECP256K1_FE_CONST(0x84044385, 0x9a4619bf, 0x74e35b6d, 0xa47e0c46, 0x6b7fb47d, 0x9ffab128, 0xb0775aa3, 0xcb318bd1)},
+ {SECP256K1_FE_CONST(0xb27235d2, 0xc56a52be, 0x210db37a, 0xd50d23a4, 0xbe621bdd, 0x5df22c6a, 0xe926ba62, 0xd2e4e440),
+ SECP256K1_FE_CONST(0x67a26e54, 0x483a9d3c, 0xa568469e, 0xd258ab3d, 0xb9ec9981, 0xdca9b1bd, 0x8d2775fe, 0x53ae429b)},
+ {SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00e0ffff, 0xffffff83, 0xffffffff, 0x3f00f00f, 0x000000e0, 0xffffffff),
+ SECP256K1_FE_CONST(0x310e10f8, 0x23bbfab0, 0xac94907d, 0x076c9a45, 0x8d357d7f, 0xc763bcee, 0x00d0e615, 0x5a6acef6)},
+ {SECP256K1_FE_CONST(0xfeff0300, 0x001c0000, 0xf80700c0, 0x0ff0ffff, 0xffffffff, 0x0fffffff, 0xffff0100, 0x7f0000fe),
+ SECP256K1_FE_CONST(0x28e2fdb4, 0x0709168b, 0x86f598b0, 0x3453a370, 0x530cf21f, 0x32f978d5, 0x1d527a71, 0x59269b0c)},
+ {SECP256K1_FE_CONST(0xc2591afa, 0x7bb98ef7, 0x090bb273, 0x85c14f87, 0xbb0b28e0, 0x54d3c453, 0x85c66753, 0xd5574d2f),
+ SECP256K1_FE_CONST(0xfdca70a2, 0x70ce627c, 0x95e66fae, 0x848a6dbb, 0x07ffb15c, 0x5f63a058, 0xba4140ed, 0x6113b503)},
+ {SECP256K1_FE_CONST(0xf5475db3, 0xedc7b5a3, 0x411c047e, 0xeaeb452f, 0xc625828e, 0x1cf5ad27, 0x8eec1060, 0xc7d3e690),
+ SECP256K1_FE_CONST(0x5eb756c0, 0xf963f4b9, 0xdc6a215e, 0xec8cc2d8, 0x2e9dec01, 0xde5eb88d, 0x6aba7164, 0xaecb2c5a)},
+ {SECP256K1_FE_CONST(0x00000000, 0x00f8ffff, 0xffffffff, 0x01000000, 0xe0ff1f00, 0x00000000, 0xffffff7f, 0x00000000),
+ SECP256K1_FE_CONST(0xe0d2e3d8, 0x49b6157d, 0xe54e88c2, 0x1a7f02ca, 0x7dd28167, 0xf1125d81, 0x7bfa444e, 0xbe110037)},
+ /* Selection of randomly generated inputs that reach high/low d/e values in various configurations. */
+ {SECP256K1_FE_CONST(0x13cc08a4, 0xd8c41f0f, 0x179c3e67, 0x54c46c67, 0xc4109221, 0x09ab3b13, 0xe24d9be1, 0xffffe950),
+ SECP256K1_FE_CONST(0xb80c8006, 0xd16abaa7, 0xcabd71e5, 0xcf6714f4, 0x966dd3d0, 0x64767a2d, 0xe92c4441, 0x51008cd1)},
+ {SECP256K1_FE_CONST(0xaa6db990, 0x95efbca1, 0x3cc6ff71, 0x0602e24a, 0xf49ff938, 0x99fffc16, 0x46f40993, 0xc6e72057),
+ SECP256K1_FE_CONST(0xd5d3dd69, 0xb0c195e5, 0x285f1d49, 0xe639e48c, 0x9223f8a9, 0xca1d731d, 0x9ca482f9, 0xa5b93e06)},
+ {SECP256K1_FE_CONST(0x1c680eac, 0xaeabffd8, 0x9bdc4aee, 0x1781e3de, 0xa3b08108, 0x0015f2e0, 0x94449e1b, 0x2f67a058),
+ SECP256K1_FE_CONST(0x7f083f8d, 0x31254f29, 0x6510f475, 0x245c373d, 0xc5622590, 0x4b323393, 0x32ed1719, 0xc127444b)},
+ {SECP256K1_FE_CONST(0x147d44b3, 0x012d83f8, 0xc160d386, 0x1a44a870, 0x9ba6be96, 0x8b962707, 0x267cbc1a, 0xb65b2f0a),
+ SECP256K1_FE_CONST(0x555554ff, 0x170aef1e, 0x50a43002, 0xe51fbd36, 0xafadb458, 0x7a8aded1, 0x0ca6cd33, 0x6ed9087c)},
+ {SECP256K1_FE_CONST(0x12423796, 0x22f0fe61, 0xf9ca017c, 0x5384d107, 0xa1fbf3b2, 0x3b018013, 0x916a3c37, 0x4000b98c),
+ SECP256K1_FE_CONST(0x20257700, 0x08668f94, 0x1177e306, 0x136c01f5, 0x8ed1fbd2, 0x95ec4589, 0xae38edb9, 0xfd19b6d7)},
+ {SECP256K1_FE_CONST(0xdcf2d030, 0x9ab42cb4, 0x93ffa181, 0xdcd23619, 0x39699b52, 0x08909a20, 0xb5a17695, 0x3a9dcf21),
+ SECP256K1_FE_CONST(0x1f701dea, 0xe211fb1f, 0x4f37180d, 0x63a0f51c, 0x29fe1e40, 0xa40b6142, 0x2e7b12eb, 0x982b06b6)},
+ {SECP256K1_FE_CONST(0x79a851f6, 0xa6314ed3, 0xb35a55e6, 0xca1c7d7f, 0xe32369ea, 0xf902432e, 0x375308c5, 0xdfd5b600),
+ SECP256K1_FE_CONST(0xcaae00c5, 0xe6b43851, 0x9dabb737, 0x38cba42c, 0xa02c8549, 0x7895dcbf, 0xbd183d71, 0xafe4476a)},
+ {SECP256K1_FE_CONST(0xede78fdd, 0xcfc92bf1, 0x4fec6c6c, 0xdb8d37e2, 0xfb66bc7b, 0x28701870, 0x7fa27c9a, 0x307196ec),
+ SECP256K1_FE_CONST(0x68193a6c, 0x9a8b87a7, 0x2a760c64, 0x13e473f6, 0x23ae7bed, 0x1de05422, 0x88865427, 0xa3418265)},
+ {SECP256K1_FE_CONST(0xa40b2079, 0xb8f88e89, 0xa7617997, 0x89baf5ae, 0x174df343, 0x75138eae, 0x2711595d, 0x3fc3e66c),
+ SECP256K1_FE_CONST(0x9f99c6a5, 0x6d685267, 0xd4b87c37, 0x9d9c4576, 0x358c692b, 0x6bbae0ed, 0x3389c93d, 0x7fdd2655)},
+ {SECP256K1_FE_CONST(0x7c74c6b6, 0xe98d9151, 0x72645cf1, 0x7f06e321, 0xcefee074, 0x15b2113a, 0x10a9be07, 0x08a45696),
+ SECP256K1_FE_CONST(0x8c919a88, 0x898bc1e0, 0x77f26f97, 0x12e655b7, 0x9ba0ac40, 0xe15bb19e, 0x8364cc3b, 0xe227a8ee)},
+ {SECP256K1_FE_CONST(0x109ba1ce, 0xdafa6d4a, 0xa1cec2b2, 0xeb1069f4, 0xb7a79e5b, 0xec6eb99b, 0xaec5f643, 0xee0e723e),
+ SECP256K1_FE_CONST(0x93d13eb8, 0x4bb0bcf9, 0xe64f5a71, 0xdbe9f359, 0x7191401c, 0x6f057a4a, 0xa407fe1b, 0x7ecb65cc)},
+ {SECP256K1_FE_CONST(0x3db076cd, 0xec74a5c9, 0xf61dd138, 0x90e23e06, 0xeeedd2d0, 0x74cbc4e0, 0x3dbe1e91, 0xded36a78),
+ SECP256K1_FE_CONST(0x3f07f966, 0x8e2a1e09, 0x706c71df, 0x02b5e9d5, 0xcb92ddbf, 0xcdd53010, 0x16545564, 0xe660b107)},
+ {SECP256K1_FE_CONST(0xe31c73ed, 0xb4c4b82c, 0x02ae35f7, 0x4cdec153, 0x98b522fd, 0xf7d2460c, 0x6bf7c0f8, 0x4cf67b0d),
+ SECP256K1_FE_CONST(0x4b8f1faf, 0x94e8b070, 0x19af0ff6, 0xa319cd31, 0xdf0a7ffb, 0xefaba629, 0x59c50666, 0x1fe5b843)},
+ {SECP256K1_FE_CONST(0x4c8b0e6e, 0x83392ab6, 0xc0e3e9f1, 0xbbd85497, 0x16698897, 0xf552d50d, 0x79652ddb, 0x12f99870),
+ SECP256K1_FE_CONST(0x56d5101f, 0xd23b7949, 0x17dc38d6, 0xf24022ef, 0xcf18e70a, 0x5cc34424, 0x438544c3, 0x62da4bca)},
+ {SECP256K1_FE_CONST(0xb0e040e2, 0x40cc35da, 0x7dd5c611, 0x7fccb178, 0x28888137, 0xbc930358, 0xea2cbc90, 0x775417dc),
+ SECP256K1_FE_CONST(0xca37f0d4, 0x016dd7c8, 0xab3ae576, 0x96e08d69, 0x68ed9155, 0xa9b44270, 0x900ae35d, 0x7c7800cd)},
+ {SECP256K1_FE_CONST(0x8a32ea49, 0x7fbb0bae, 0x69724a9d, 0x8e2105b2, 0xbdf69178, 0x862577ef, 0x35055590, 0x667ddaef),
+ SECP256K1_FE_CONST(0xd02d7ead, 0xc5e190f0, 0x559c9d72, 0xdaef1ffc, 0x64f9f425, 0xf43645ea, 0x7341e08d, 0x11768e96)},
+ {SECP256K1_FE_CONST(0xa3592d98, 0x9abe289d, 0x579ebea6, 0xbb0857a8, 0xe242ab73, 0x85f9a2ce, 0xb6998f0f, 0xbfffbfc6),
+ SECP256K1_FE_CONST(0x093c1533, 0x32032efa, 0x6aa46070, 0x0039599e, 0x589c35f4, 0xff525430, 0x7fe3777a, 0x44b43ddc)},
+ {SECP256K1_FE_CONST(0x647178a3, 0x229e607b, 0xcc98521a, 0xcce3fdd9, 0x1e1bc9c9, 0x97fb7c6a, 0x61b961e0, 0x99b10709),
+ SECP256K1_FE_CONST(0x98217c13, 0xd51ddf78, 0x96310e77, 0xdaebd908, 0x602ca683, 0xcb46d07a, 0xa1fcf17e, 0xc8e2feb3)},
+ {SECP256K1_FE_CONST(0x7334627c, 0x73f98968, 0x99464b4b, 0xf5964958, 0x1b95870d, 0xc658227e, 0x5e3235d8, 0xdcab5787),
+ SECP256K1_FE_CONST(0x000006fd, 0xc7e9dd94, 0x40ae367a, 0xe51d495c, 0x07603b9b, 0x2d088418, 0x6cc5c74c, 0x98514307)},
+ {SECP256K1_FE_CONST(0x82e83876, 0x96c28938, 0xa50dd1c5, 0x605c3ad1, 0xc048637d, 0x7a50825f, 0x335ed01a, 0x00005760),
+ SECP256K1_FE_CONST(0xb0393f9f, 0x9f2aa55e, 0xf5607e2e, 0x5287d961, 0x60b3e704, 0xf3e16e80, 0xb4f9a3ea, 0xfec7f02d)},
+ {SECP256K1_FE_CONST(0xc97b6cec, 0x3ee6b8dc, 0x98d24b58, 0x3c1970a1, 0xfe06297a, 0xae813529, 0xe76bb6bd, 0x771ae51d),
+ SECP256K1_FE_CONST(0x0507c702, 0xd407d097, 0x47ddeb06, 0xf6625419, 0x79f48f79, 0x7bf80d0b, 0xfc34b364, 0x253a5db1)},
+ {SECP256K1_FE_CONST(0xd559af63, 0x77ea9bc4, 0x3cf1ad14, 0x5c7a4bbb, 0x10e7d18b, 0x7ce0dfac, 0x380bb19d, 0x0bb99bd3),
+ SECP256K1_FE_CONST(0x00196119, 0xb9b00d92, 0x34edfdb5, 0xbbdc42fc, 0xd2daa33a, 0x163356ca, 0xaa8754c8, 0xb0ec8b0b)},
+ {SECP256K1_FE_CONST(0x8ddfa3dc, 0x52918da0, 0x640519dc, 0x0af8512a, 0xca2d33b2, 0xbde52514, 0xda9c0afc, 0xcb29fce4),
+ SECP256K1_FE_CONST(0xb3e4878d, 0x5cb69148, 0xcd54388b, 0xc23acce0, 0x62518ba8, 0xf09def92, 0x7b31e6aa, 0x6ba35b02)},
+ {SECP256K1_FE_CONST(0xf8207492, 0xe3049f0a, 0x65285f2b, 0x0bfff996, 0x00ca112e, 0xc05da837, 0x546d41f9, 0x5194fb91),
+ SECP256K1_FE_CONST(0x7b7ee50b, 0xa8ed4bbd, 0xf6469930, 0x81419a5c, 0x071441c7, 0x290d046e, 0x3b82ea41, 0x611c5f95)},
+ {SECP256K1_FE_CONST(0x050f7c80, 0x5bcd3c6b, 0x823cb724, 0x5ce74db7, 0xa4e39f5c, 0xbd8828d7, 0xfd4d3e07, 0x3ec2926a),
+ SECP256K1_FE_CONST(0x000d6730, 0xb0171314, 0x4764053d, 0xee157117, 0x48fd61da, 0xdea0b9db, 0x1d5e91c6, 0xbdc3f59e)},
+ {SECP256K1_FE_CONST(0x3e3ea8eb, 0x05d760cf, 0x23009263, 0xb3cb3ac9, 0x088f6f0d, 0x3fc182a3, 0xbd57087c, 0xe67c62f9),
+ SECP256K1_FE_CONST(0xbe988716, 0xa29c1bf6, 0x4456aed6, 0xab1e4720, 0x49929305, 0x51043bf4, 0xebd833dd, 0xdd511e8b)},
+ {SECP256K1_FE_CONST(0x6964d2a9, 0xa7fa6501, 0xa5959249, 0x142f4029, 0xea0c1b5f, 0x2f487ef6, 0x301ac80a, 0x768be5cd),
+ SECP256K1_FE_CONST(0x3918ffe4, 0x07492543, 0xed24d0b7, 0x3df95f8f, 0xaffd7cb4, 0x0de2191c, 0x9ec2f2ad, 0x2c0cb3c6)},
+ {SECP256K1_FE_CONST(0x37c93520, 0xf6ddca57, 0x2b42fd5e, 0xb5c7e4de, 0x11b5b81c, 0xb95e91f3, 0x95c4d156, 0x39877ccb),
+ SECP256K1_FE_CONST(0x9a94b9b5, 0x57eb71ee, 0x4c975b8b, 0xac5262a8, 0x077b0595, 0xe12a6b1f, 0xd728edef, 0x1a6bf956)}
+ };
+ /* Fixed test cases for scalar inverses: pairs of (x, 1/x) mod n. */
+ static const secp256k1_scalar scalar_cases[][2] = {
+ /* 0 */
+ {SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0),
+ SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0)},
+ /* 1 */
+ {SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 1),
+ SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 1)},
+ /* -1 */
+ {SECP256K1_SCALAR_CONST(0xffffffff, 0xffffffff, 0xffffffff, 0xfffffffe, 0xbaaedce6, 0xaf48a03b, 0xbfd25e8c, 0xd0364140),
+ SECP256K1_SCALAR_CONST(0xffffffff, 0xffffffff, 0xffffffff, 0xfffffffe, 0xbaaedce6, 0xaf48a03b, 0xbfd25e8c, 0xd0364140)},
+ /* 2 */
+ {SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 2),
+ SECP256K1_SCALAR_CONST(0x7fffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x5d576e73, 0x57a4501d, 0xdfe92f46, 0x681b20a1)},
+ /* 2**128 */
+ {SECP256K1_SCALAR_CONST(0, 0, 0, 1, 0, 0, 0, 0),
+ SECP256K1_SCALAR_CONST(0x50a51ac8, 0x34b9ec24, 0x4b0dff66, 0x5588b13e, 0x9984d5b3, 0xcf80ef0f, 0xd6a23766, 0xa3ee9f22)},
+ /* Input known to need 635 divsteps */
+ {SECP256K1_SCALAR_CONST(0xcb9f1d35, 0xdd4416c2, 0xcd71bf3f, 0x6365da66, 0x3c9b3376, 0x8feb7ae9, 0x32a5ef60, 0x19199ec3),
+ SECP256K1_SCALAR_CONST(0x1d7c7bba, 0xf1893d53, 0xb834bd09, 0x36b411dc, 0x42c2e42f, 0xec72c428, 0x5e189791, 0x8e9bc708)},
+ /* Input known to need 566 divsteps starting with delta=1/2. */
+ {SECP256K1_SCALAR_CONST(0x7e3c993d, 0xa4272488, 0xbc015b49, 0x2db54174, 0xd382083a, 0xebe6db35, 0x80f82eff, 0xcd132c72),
+ SECP256K1_SCALAR_CONST(0x086f34a0, 0x3e631f76, 0x77418f28, 0xcc84ac95, 0x6304439d, 0x365db268, 0x312c6ded, 0xd0b934f8)},
+ /* Input known to need 565 divsteps starting with delta=1/2. */
+ {SECP256K1_SCALAR_CONST(0xbad7e587, 0x3f307859, 0x60d93147, 0x8a18491e, 0xb38a9fd5, 0x254350d3, 0x4b1f0e4b, 0x7dd6edc4),
+ SECP256K1_SCALAR_CONST(0x89f2df26, 0x39e2b041, 0xf19bd876, 0xd039c8ac, 0xc2223add, 0x29c4943e, 0x6632d908, 0x515f467b)},
+ /* Selection of randomly generated inputs that reach low/high d/e values in various configurations. */
+ {SECP256K1_SCALAR_CONST(0x1950d757, 0xb37a5809, 0x435059bb, 0x0bb8997e, 0x07e1e3c8, 0x5e5d7d2c, 0x6a0ed8e3, 0xdbde180e),
+ SECP256K1_SCALAR_CONST(0xbf72af9b, 0x750309e2, 0x8dda230b, 0xfe432b93, 0x7e25e475, 0x4388251e, 0x633d894b, 0x3bcb6f8c)},
+ {SECP256K1_SCALAR_CONST(0x9bccf4e7, 0xc5a515e3, 0x50637aa9, 0xbb65a13f, 0x391749a1, 0x62de7d4e, 0xf6d7eabb, 0x3cd10ce0),
+ SECP256K1_SCALAR_CONST(0xaf2d5623, 0xb6385a33, 0xcd0365be, 0x5e92a70d, 0x7f09179c, 0x3baaf30f, 0x8f9cc83b, 0x20092f67)},
+ {SECP256K1_SCALAR_CONST(0x73a57111, 0xb242952a, 0x5c5dee59, 0xf3be2ace, 0xa30a7659, 0xa46e5f47, 0xd21267b1, 0x39e642c9),
+ SECP256K1_SCALAR_CONST(0xa711df07, 0xcbcf13ef, 0xd61cc6be, 0xbcd058ce, 0xb02cf157, 0x272d4a18, 0x86d0feb3, 0xcd5fa004)},
+ {SECP256K1_SCALAR_CONST(0x04884963, 0xce0580b1, 0xba547030, 0x3c691db3, 0x9cd2c84f, 0x24c7cebd, 0x97ebfdba, 0x3e785ec2),
+ SECP256K1_SCALAR_CONST(0xaaaaaf14, 0xd7c99ba7, 0x517ce2c1, 0x78a28b4c, 0x3769a851, 0xe5c5a03d, 0x4cc28f33, 0x0ec4dc5d)},
+ {SECP256K1_SCALAR_CONST(0x1679ed49, 0x21f537b1, 0x815cb8ae, 0x9efc511c, 0x5b9fa037, 0x0b0f275e, 0x6c985281, 0x6c4a9905),
+ SECP256K1_SCALAR_CONST(0xb14ac3d5, 0x62b52999, 0xef34ead1, 0xffca4998, 0x0294341a, 0x1f8172aa, 0xea1624f9, 0x302eea62)},
+ {SECP256K1_SCALAR_CONST(0x626b37c0, 0xf0057c35, 0xee982f83, 0x452a1fd3, 0xea826506, 0x48b08a9d, 0x1d2c4799, 0x4ad5f6ec),
+ SECP256K1_SCALAR_CONST(0xe38643b7, 0x567bfc2f, 0x5d2f1c15, 0xe327239c, 0x07112443, 0x69509283, 0xfd98e77a, 0xdb71c1e8)},
+ {SECP256K1_SCALAR_CONST(0x1850a3a7, 0x759efc56, 0x54f287b2, 0x14d1234b, 0xe263bbc9, 0xcf4d8927, 0xd5f85f27, 0x965bd816),
+ SECP256K1_SCALAR_CONST(0x3b071831, 0xcac9619a, 0xcceb0596, 0xf614d63b, 0x95d0db2f, 0xc6a00901, 0x8eaa2621, 0xabfa0009)},
+ {SECP256K1_SCALAR_CONST(0x94ae5d06, 0xa27dc400, 0x487d72be, 0xaa51ebed, 0xe475b5c0, 0xea675ffc, 0xf4df627a, 0xdca4222f),
+ SECP256K1_SCALAR_CONST(0x01b412ed, 0xd7830956, 0x1532537e, 0xe5e3dc99, 0x8fd3930a, 0x54f8d067, 0x32ef5760, 0x594438a5)},
+ {SECP256K1_SCALAR_CONST(0x1f24278a, 0xb5bfe374, 0xa328dbbc, 0xebe35f48, 0x6620e009, 0xd58bb1b4, 0xb5a6bf84, 0x8815f63a),
+ SECP256K1_SCALAR_CONST(0xfe928416, 0xca5ba2d3, 0xfde513da, 0x903a60c7, 0x9e58ad8a, 0x8783bee4, 0x083a3843, 0xa608c914)},
+ {SECP256K1_SCALAR_CONST(0xdc107d58, 0x274f6330, 0x67dba8bc, 0x26093111, 0x5201dfb8, 0x968ce3f5, 0xf34d1bd4, 0xf2146504),
+ SECP256K1_SCALAR_CONST(0x660cfa90, 0x13c3d93e, 0x7023b1e5, 0xedd09e71, 0x6d9c9d10, 0x7a3d2cdb, 0xdd08edc3, 0xaa78fcfb)},
+ {SECP256K1_SCALAR_CONST(0x7cd1e905, 0xc6f02776, 0x2f551cc7, 0x5da61cff, 0x7da05389, 0x1119d5a4, 0x631c7442, 0x894fd4f7),
+ SECP256K1_SCALAR_CONST(0xff20862a, 0x9d3b1a37, 0x1628803b, 0x3004ccae, 0xaa23282a, 0xa89a1109, 0xd94ece5e, 0x181bdc46)},
+ {SECP256K1_SCALAR_CONST(0x5b9dade8, 0x23d26c58, 0xcd12d818, 0x25b8ae97, 0x3dea04af, 0xf482c96b, 0xa062f254, 0x9e453640),
+ SECP256K1_SCALAR_CONST(0x50c38800, 0x15fa53f4, 0xbe1e5392, 0x5c9b120a, 0x262c22c7, 0x18fa0816, 0x5f2baab4, 0x8cb5db46)},
+ {SECP256K1_SCALAR_CONST(0x11cdaeda, 0x969c464b, 0xef1f4ab0, 0x5b01d22e, 0x656fd098, 0x882bea84, 0x65cdbe7a, 0x0c19ff03),
+ SECP256K1_SCALAR_CONST(0x1968d0fa, 0xac46f103, 0xb55f1f72, 0xb3820bed, 0xec6b359a, 0x4b1ae0ad, 0x7e38e1fb, 0x295ccdfb)},
+ {SECP256K1_SCALAR_CONST(0x2c351aa1, 0x26e91589, 0x194f8a1e, 0x06561f66, 0x0cb97b7f, 0x10914454, 0x134d1c03, 0x157266b4),
+ SECP256K1_SCALAR_CONST(0xbe49ada6, 0x92bd8711, 0x41b176c4, 0xa478ba95, 0x14883434, 0x9d1cd6f3, 0xcc4b847d, 0x22af80f5)},
+ {SECP256K1_SCALAR_CONST(0x6ba07c6e, 0x13a60edb, 0x6247f5c3, 0x84b5fa56, 0x76fe3ec5, 0x80426395, 0xf65ec2ae, 0x623ba730),
+ SECP256K1_SCALAR_CONST(0x25ac23f7, 0x418cd747, 0x98376f9d, 0x4a11c7bf, 0x24c8ebfe, 0x4c8a8655, 0x345f4f52, 0x1c515595)},
+ {SECP256K1_SCALAR_CONST(0x9397a712, 0x8abb6951, 0x2d4a3d54, 0x703b1c2a, 0x0661dca8, 0xd75c9b31, 0xaed4d24b, 0xd2ab2948),
+ SECP256K1_SCALAR_CONST(0xc52e8bef, 0xd55ce3eb, 0x1c897739, 0xeb9fb606, 0x36b9cd57, 0x18c51cc2, 0x6a87489e, 0xffd0dcf3)},
+ {SECP256K1_SCALAR_CONST(0xe6a808cc, 0xeb437888, 0xe97798df, 0x4e224e44, 0x7e3b380a, 0x207c1653, 0x889f3212, 0xc6738b6f),
+ SECP256K1_SCALAR_CONST(0x31f9ae13, 0xd1e08b20, 0x757a2e5e, 0x5243a0eb, 0x8ae35f73, 0x19bb6122, 0xb910f26b, 0xda70aa55)},
+ {SECP256K1_SCALAR_CONST(0xd0320548, 0xab0effe7, 0xa70779e0, 0x61a347a6, 0xb8c1e010, 0x9d5281f8, 0x2ee588a6, 0x80000000),
+ SECP256K1_SCALAR_CONST(0x1541897e, 0x78195c90, 0x7583dd9e, 0x728b6100, 0xbce8bc6d, 0x7a53b471, 0x5dcd9e45, 0x4425fcaf)},
+ {SECP256K1_SCALAR_CONST(0x93d623f1, 0xd45b50b0, 0x796e9186, 0x9eac9407, 0xd30edc20, 0xef6304cf, 0x250494e7, 0xba503de9),
+ SECP256K1_SCALAR_CONST(0x7026d638, 0x1178b548, 0x92043952, 0x3c7fb47c, 0xcd3ea236, 0x31d82b01, 0x612fc387, 0x80b9b957)},
+ {SECP256K1_SCALAR_CONST(0xf860ab39, 0x55f5d412, 0xa4d73bcc, 0x3b48bd90, 0xc248ffd3, 0x13ca10be, 0x8fba84cc, 0xdd28d6a3),
+ SECP256K1_SCALAR_CONST(0x5c32fc70, 0xe0b15d67, 0x76694700, 0xfe62be4d, 0xeacdb229, 0x7a4433d9, 0x52155cd0, 0x7649ab59)},
+ {SECP256K1_SCALAR_CONST(0x4e41311c, 0x0800af58, 0x7a690a8e, 0xe175c9ba, 0x6981ab73, 0xac532ea8, 0x5c1f5e63, 0x6ac1f189),
+ SECP256K1_SCALAR_CONST(0xfffffff9, 0xd075982c, 0x7fbd3825, 0xc05038a2, 0x4533b91f, 0x94ec5f45, 0xb280b28f, 0x842324dc)},
+ {SECP256K1_SCALAR_CONST(0x48e473bf, 0x3555eade, 0xad5d7089, 0x2424c4e4, 0x0a99397c, 0x2dc796d8, 0xb7a43a69, 0xd0364141),
+ SECP256K1_SCALAR_CONST(0x634976b2, 0xa0e47895, 0x1ec38593, 0x266d6fd0, 0x6f602644, 0x9bb762f1, 0x7180c704, 0xe23a4daa)},
+ {SECP256K1_SCALAR_CONST(0xbe83878d, 0x3292fc54, 0x26e71c62, 0x556ccedc, 0x7cbb8810, 0x4032a720, 0x34ead589, 0xe4d6bd13),
+ SECP256K1_SCALAR_CONST(0x6cd150ad, 0x25e59d0f, 0x74cbae3d, 0x6377534a, 0x1e6562e8, 0xb71b9d18, 0xe1e5d712, 0x8480abb3)},
+ {SECP256K1_SCALAR_CONST(0xcdddf2e5, 0xefc15f88, 0xc9ee06de, 0x8a846ca9, 0x28561581, 0x68daa5fb, 0xd1cf3451, 0xeb1782d0),
+ SECP256K1_SCALAR_CONST(0xffffffd9, 0xed8d2af4, 0x993c865a, 0x23e9681a, 0x3ca3a3dc, 0xe6d5a46e, 0xbd86bd87, 0x61b55c70)},
+ {SECP256K1_SCALAR_CONST(0xb6a18f1f, 0x04872df9, 0x08165ec4, 0x319ca19c, 0x6c0359ab, 0x1f7118fb, 0xc2ef8082, 0xca8b7785),
+ SECP256K1_SCALAR_CONST(0xff55b19b, 0x0f1ac78c, 0x0f0c88c2, 0x2358d5ad, 0x5f455e4e, 0x3330b72f, 0x274dc153, 0xffbf272b)},
+ {SECP256K1_SCALAR_CONST(0xea4898e5, 0x30eba3e8, 0xcf0e5c3d, 0x06ec6844, 0x01e26fb6, 0x75636225, 0xc5d08f4c, 0x1decafa0),
+ SECP256K1_SCALAR_CONST(0xe5a014a8, 0xe3c4ec1e, 0xea4f9b32, 0xcfc7b386, 0x00630806, 0x12c08d02, 0x6407ccc2, 0xb067d90e)},
+ {SECP256K1_SCALAR_CONST(0x70e9aea9, 0x7e933af0, 0x8a23bfab, 0x23e4b772, 0xff951863, 0x5ffcf47d, 0x6bebc918, 0x2ca58265),
+ SECP256K1_SCALAR_CONST(0xf4e00006, 0x81bc6441, 0x4eb6ec02, 0xc194a859, 0x80ad7c48, 0xba4e9afb, 0x8b6bdbe0, 0x989d8f77)},
+ {SECP256K1_SCALAR_CONST(0x3c56c774, 0x46efe6f0, 0xe93618b8, 0xf9b5a846, 0xd247df61, 0x83b1e215, 0x06dc8bcc, 0xeefc1bf5),
+ SECP256K1_SCALAR_CONST(0xfff8937a, 0x2cd9586b, 0x43c25e57, 0xd1cefa7a, 0x9fb91ed3, 0x95b6533d, 0x8ad0de5b, 0xafb93f00)},
+ {SECP256K1_SCALAR_CONST(0xfb5c2772, 0x5cb30e83, 0xe38264df, 0xe4e3ebf3, 0x392aa92e, 0xa68756a1, 0x51279ac5, 0xb50711a8),
+ SECP256K1_SCALAR_CONST(0x000013af, 0x1105bfe7, 0xa6bbd7fb, 0x3d638f99, 0x3b266b02, 0x072fb8bc, 0x39251130, 0x2e0fd0ea)}
+ };
+ int i, var, testrand;
+ unsigned char b32[32];
+ secp256k1_fe x_fe;
+ secp256k1_scalar x_scalar;
+ memset(b32, 0, sizeof(b32));
+ /* Test fixed test cases through test_inverse_{scalar,field}, both ways. */
+ for (i = 0; (size_t)i < sizeof(fe_cases)/sizeof(fe_cases[0]); ++i) {
+ for (var = 0; var <= 1; ++var) {
+ test_inverse_field(&x_fe, &fe_cases[i][0], var);
+ check_fe_equal(&x_fe, &fe_cases[i][1]);
+ test_inverse_field(&x_fe, &fe_cases[i][1], var);
+ check_fe_equal(&x_fe, &fe_cases[i][0]);
+ }
+ }
+ for (i = 0; (size_t)i < sizeof(scalar_cases)/sizeof(scalar_cases[0]); ++i) {
+ for (var = 0; var <= 1; ++var) {
+ test_inverse_scalar(&x_scalar, &scalar_cases[i][0], var);
+ CHECK(secp256k1_scalar_eq(&x_scalar, &scalar_cases[i][1]));
+ test_inverse_scalar(&x_scalar, &scalar_cases[i][1], var);
+ CHECK(secp256k1_scalar_eq(&x_scalar, &scalar_cases[i][0]));
+ }
+ }
+ /* Test inputs 0..999 and their respective negations. */
+ for (i = 0; i < 1000; ++i) {
+ b32[31] = i & 0xff;
+ b32[30] = (i >> 8) & 0xff;
+ secp256k1_scalar_set_b32(&x_scalar, b32, NULL);
+ secp256k1_fe_set_b32(&x_fe, b32);
+ for (var = 0; var <= 1; ++var) {
+ test_inverse_scalar(NULL, &x_scalar, var);
+ test_inverse_field(NULL, &x_fe, var);
+ }
+ secp256k1_scalar_negate(&x_scalar, &x_scalar);
+ secp256k1_fe_negate(&x_fe, &x_fe, 1);
+ for (var = 0; var <= 1; ++var) {
+ test_inverse_scalar(NULL, &x_scalar, var);
+ test_inverse_field(NULL, &x_fe, var);
+ }
+ }
+ /* test 128*count random inputs; half with testrand256_test, half with testrand256 */
+ for (testrand = 0; testrand <= 1; ++testrand) {
+ for (i = 0; i < 64 * count; ++i) {
+ (testrand ? secp256k1_testrand256_test : secp256k1_testrand256)(b32);
+ secp256k1_scalar_set_b32(&x_scalar, b32, NULL);
+ secp256k1_fe_set_b32(&x_fe, b32);
+ for (var = 0; var <= 1; ++var) {
+ test_inverse_scalar(NULL, &x_scalar, var);
+ test_inverse_field(NULL, &x_fe, var);
+ }
+ }
+ }
+}
+
/***** GROUP TESTS *****/
void ge_equals_ge(const secp256k1_ge *a, const secp256k1_ge *b) {
@@ -2111,7 +2945,6 @@ void test_ge(void) {
*/
secp256k1_ge *ge = (secp256k1_ge *)checked_malloc(&ctx->error_callback, sizeof(secp256k1_ge) * (1 + 4 * runs));
secp256k1_gej *gej = (secp256k1_gej *)checked_malloc(&ctx->error_callback, sizeof(secp256k1_gej) * (1 + 4 * runs));
- secp256k1_fe *zinv = (secp256k1_fe *)checked_malloc(&ctx->error_callback, sizeof(secp256k1_fe) * (1 + 4 * runs));
secp256k1_fe zf;
secp256k1_fe zfi2, zfi3;
@@ -2145,23 +2978,6 @@ void test_ge(void) {
}
}
- /* Compute z inverses. */
- {
- secp256k1_fe *zs = checked_malloc(&ctx->error_callback, sizeof(secp256k1_fe) * (1 + 4 * runs));
- for (i = 0; i < 4 * runs + 1; i++) {
- if (i == 0) {
- /* The point at infinity does not have a meaningful z inverse. Any should do. */
- do {
- random_field_element_test(&zs[i]);
- } while(secp256k1_fe_is_zero(&zs[i]));
- } else {
- zs[i] = gej[i].z;
- }
- }
- secp256k1_fe_inv_all_var(zinv, zs, 4 * runs + 1);
- free(zs);
- }
-
/* Generate random zf, and zfi2 = 1/zf^2, zfi3 = 1/zf^3 */
do {
random_field_element_test(&zf);
@@ -2270,16 +3086,9 @@ void test_ge(void) {
free(gej_shuffled);
}
- /* Test batch gej -> ge conversion with and without known z ratios. */
+ /* Test batch gej -> ge conversion without known z ratios. */
{
- secp256k1_fe *zr = (secp256k1_fe *)checked_malloc(&ctx->error_callback, (4 * runs + 1) * sizeof(secp256k1_fe));
secp256k1_ge *ge_set_all = (secp256k1_ge *)checked_malloc(&ctx->error_callback, (4 * runs + 1) * sizeof(secp256k1_ge));
- for (i = 0; i < 4 * runs + 1; i++) {
- /* Compute gej[i + 1].z / gez[i].z (with gej[n].z taken to be 1). */
- if (i < 4 * runs) {
- secp256k1_fe_mul(&zr[i + 1], &zinv[i], &gej[i + 1].z);
- }
- }
secp256k1_ge_set_all_gej_var(ge_set_all, gej, 4 * runs + 1);
for (i = 0; i < 4 * runs + 1; i++) {
secp256k1_fe s;
@@ -2288,7 +3097,6 @@ void test_ge(void) {
ge_equals_gej(&ge_set_all[i], &gej[i]);
}
free(ge_set_all);
- free(zr);
}
/* Test batch gej -> ge conversion with many infinities. */
@@ -2309,7 +3117,6 @@ void test_ge(void) {
free(ge);
free(gej);
- free(zinv);
}
@@ -2456,64 +3263,35 @@ void run_ec_combine(void) {
void test_group_decompress(const secp256k1_fe* x) {
/* The input itself, normalized. */
secp256k1_fe fex = *x;
- secp256k1_fe fez;
- /* Results of set_xquad_var, set_xo_var(..., 0), set_xo_var(..., 1). */
- secp256k1_ge ge_quad, ge_even, ge_odd;
- secp256k1_gej gej_quad;
+ /* Results of set_xo_var(..., 0), set_xo_var(..., 1). */
+ secp256k1_ge ge_even, ge_odd;
/* Return values of the above calls. */
- int res_quad, res_even, res_odd;
+ int res_even, res_odd;
secp256k1_fe_normalize_var(&fex);
- res_quad = secp256k1_ge_set_xquad(&ge_quad, &fex);
res_even = secp256k1_ge_set_xo_var(&ge_even, &fex, 0);
res_odd = secp256k1_ge_set_xo_var(&ge_odd, &fex, 1);
- CHECK(res_quad == res_even);
- CHECK(res_quad == res_odd);
+ CHECK(res_even == res_odd);
- if (res_quad) {
- secp256k1_fe_normalize_var(&ge_quad.x);
+ if (res_even) {
secp256k1_fe_normalize_var(&ge_odd.x);
secp256k1_fe_normalize_var(&ge_even.x);
- secp256k1_fe_normalize_var(&ge_quad.y);
secp256k1_fe_normalize_var(&ge_odd.y);
secp256k1_fe_normalize_var(&ge_even.y);
/* No infinity allowed. */
- CHECK(!ge_quad.infinity);
CHECK(!ge_even.infinity);
CHECK(!ge_odd.infinity);
/* Check that the x coordinates check out. */
- CHECK(secp256k1_fe_equal_var(&ge_quad.x, x));
CHECK(secp256k1_fe_equal_var(&ge_even.x, x));
CHECK(secp256k1_fe_equal_var(&ge_odd.x, x));
- /* Check that the Y coordinate result in ge_quad is a square. */
- CHECK(secp256k1_fe_is_quad_var(&ge_quad.y));
-
/* Check odd/even Y in ge_odd, ge_even. */
CHECK(secp256k1_fe_is_odd(&ge_odd.y));
CHECK(!secp256k1_fe_is_odd(&ge_even.y));
-
- /* Check secp256k1_gej_has_quad_y_var. */
- secp256k1_gej_set_ge(&gej_quad, &ge_quad);
- CHECK(secp256k1_gej_has_quad_y_var(&gej_quad));
- do {
- random_fe_test(&fez);
- } while (secp256k1_fe_is_zero(&fez));
- secp256k1_gej_rescale(&gej_quad, &fez);
- CHECK(secp256k1_gej_has_quad_y_var(&gej_quad));
- secp256k1_gej_neg(&gej_quad, &gej_quad);
- CHECK(!secp256k1_gej_has_quad_y_var(&gej_quad));
- do {
- random_fe_test(&fez);
- } while (secp256k1_fe_is_zero(&fez));
- secp256k1_gej_rescale(&gej_quad, &fez);
- CHECK(!secp256k1_gej_has_quad_y_var(&gej_quad));
- secp256k1_gej_neg(&gej_quad, &gej_quad);
- CHECK(secp256k1_gej_has_quad_y_var(&gej_quad));
}
}
@@ -4373,8 +5151,10 @@ void test_ecdsa_sign_verify(void) {
secp256k1_scalar one;
secp256k1_scalar msg, key;
secp256k1_scalar sigr, sigs;
- int recid;
int getrec;
+ /* Initialize recid to suppress a false positive -Wconditional-uninitialized in clang.
+ VG_UNDEF ensures that valgrind will still treat the variable as uninitialized. */
+ int recid = -1; VG_UNDEF(&recid, sizeof(recid));
random_scalar_order_test(&msg);
random_scalar_order_test(&key);
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pubj, &key);
@@ -5444,18 +6224,18 @@ void run_ecdsa_openssl(void) {
# include "modules/schnorrsig/tests_impl.h"
#endif
-void run_memczero_test(void) {
+void run_secp256k1_memczero_test(void) {
unsigned char buf1[6] = {1, 2, 3, 4, 5, 6};
unsigned char buf2[sizeof(buf1)];
- /* memczero(..., ..., 0) is a noop. */
+ /* secp256k1_memczero(..., ..., 0) is a noop. */
memcpy(buf2, buf1, sizeof(buf1));
- memczero(buf1, sizeof(buf1), 0);
+ secp256k1_memczero(buf1, sizeof(buf1), 0);
CHECK(secp256k1_memcmp_var(buf1, buf2, sizeof(buf1)) == 0);
- /* memczero(..., ..., 1) zeros the buffer. */
+ /* secp256k1_memczero(..., ..., 1) zeros the buffer. */
memset(buf2, 0, sizeof(buf2));
- memczero(buf1, sizeof(buf1) , 1);
+ secp256k1_memczero(buf1, sizeof(buf1) , 1);
CHECK(secp256k1_memcmp_var(buf1, buf2, sizeof(buf1)) == 0);
}
@@ -5626,6 +6406,15 @@ int main(int argc, char **argv) {
/* find iteration count */
if (argc > 1) {
count = strtol(argv[1], NULL, 0);
+ } else {
+ const char* env = getenv("SECP256K1_TEST_ITERS");
+ if (env) {
+ count = strtol(env, NULL, 0);
+ }
+ }
+ if (count <= 0) {
+ fputs("An iteration count of 0 or less is not allowed.\n", stderr);
+ return EXIT_FAILURE;
}
printf("test count = %i\n", count);
@@ -5646,22 +6435,18 @@ int main(int argc, char **argv) {
run_rand_bits();
run_rand_int();
+ run_ctz_tests();
+ run_modinv_tests();
+ run_inverse_tests();
+
run_sha256_tests();
run_hmac_sha256_tests();
run_rfc6979_hmac_sha256_tests();
-#ifndef USE_NUM_NONE
- /* num tests */
- run_num_smalltests();
-#endif
-
/* scalar tests */
run_scalar_tests();
/* field tests */
- run_field_inv();
- run_field_inv_var();
- run_field_inv_all_var();
run_field_misc();
run_field_convert();
run_sqr();
@@ -5723,7 +6508,7 @@ int main(int argc, char **argv) {
#endif
/* util tests */
- run_memczero_test();
+ run_secp256k1_memczero_test();
run_cmov_tests();
diff --git a/src/secp256k1/src/tests_exhaustive.c b/src/secp256k1/src/tests_exhaustive.c
index f4d5b8e176..2bb5381446 100644
--- a/src/secp256k1/src/tests_exhaustive.c
+++ b/src/secp256k1/src/tests_exhaustive.c
@@ -1,8 +1,8 @@
/***********************************************************************
- * Copyright (c) 2016 Andrew Poelstra *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+ * Copyright (c) 2016 Andrew Poelstra *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#if defined HAVE_CONFIG_H
#include "libsecp256k1-config.h"
diff --git a/src/secp256k1/src/util.h b/src/secp256k1/src/util.h
index 3a88a41bc6..f78846836c 100644
--- a/src/secp256k1/src/util.h
+++ b/src/secp256k1/src/util.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013, 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_UTIL_H
#define SECP256K1_UTIL_H
@@ -113,7 +113,7 @@ static SECP256K1_INLINE void *checked_realloc(const secp256k1_callback* cb, void
#define ALIGNMENT 16
#endif
-#define ROUND_TO_ALIGN(size) (((size + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT)
+#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,
@@ -141,7 +141,7 @@ static SECP256K1_INLINE void *manual_alloc(void** prealloc_ptr, size_t alloc_siz
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;
+ *prealloc_ptr = (unsigned char*)*prealloc_ptr + aligned_alloc_size;
return ret;
}
@@ -202,7 +202,7 @@ static SECP256K1_INLINE void *manual_alloc(void** prealloc_ptr, size_t alloc_siz
#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) {
+static SECP256K1_INLINE void secp256k1_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
@@ -260,14 +260,85 @@ static SECP256K1_INLINE void secp256k1_int_cmov(int *r, const int *a, int flag)
# define SECP256K1_WIDEMUL_INT128 1
#elif defined(USE_FORCE_WIDEMUL_INT64)
# define SECP256K1_WIDEMUL_INT64 1
-#elif defined(__SIZEOF_INT128__)
+#elif defined(UINT128_MAX) || defined(__SIZEOF_INT128__)
# define SECP256K1_WIDEMUL_INT128 1
#else
# define SECP256K1_WIDEMUL_INT64 1
#endif
#if defined(SECP256K1_WIDEMUL_INT128)
+# if !defined(UINT128_MAX) && defined(__SIZEOF_INT128__)
SECP256K1_GNUC_EXT typedef unsigned __int128 uint128_t;
SECP256K1_GNUC_EXT typedef __int128 int128_t;
+#define UINT128_MAX ((uint128_t)(-1))
+#define INT128_MAX ((int128_t)(UINT128_MAX >> 1))
+#define INT128_MIN (-INT128_MAX - 1)
+/* No (U)INT128_C macros because compilers providing __int128 do not support 128-bit literals. */
+# endif
+#endif
+
+#ifndef __has_builtin
+#define __has_builtin(x) 0
+#endif
+
+/* Determine the number of trailing zero bits in a (non-zero) 32-bit x.
+ * This function is only intended to be used as fallback for
+ * secp256k1_ctz32_var, but permits it to be tested separately. */
+static SECP256K1_INLINE int secp256k1_ctz32_var_debruijn(uint32_t x) {
+ static const uint8_t debruijn[32] = {
+ 0x00, 0x01, 0x02, 0x18, 0x03, 0x13, 0x06, 0x19, 0x16, 0x04, 0x14, 0x0A,
+ 0x10, 0x07, 0x0C, 0x1A, 0x1F, 0x17, 0x12, 0x05, 0x15, 0x09, 0x0F, 0x0B,
+ 0x1E, 0x11, 0x08, 0x0E, 0x1D, 0x0D, 0x1C, 0x1B
+ };
+ return debruijn[((x & -x) * 0x04D7651F) >> 27];
+}
+
+/* Determine the number of trailing zero bits in a (non-zero) 64-bit x.
+ * This function is only intended to be used as fallback for
+ * secp256k1_ctz64_var, but permits it to be tested separately. */
+static SECP256K1_INLINE int secp256k1_ctz64_var_debruijn(uint64_t x) {
+ static const uint8_t debruijn[64] = {
+ 0, 1, 2, 53, 3, 7, 54, 27, 4, 38, 41, 8, 34, 55, 48, 28,
+ 62, 5, 39, 46, 44, 42, 22, 9, 24, 35, 59, 56, 49, 18, 29, 11,
+ 63, 52, 6, 26, 37, 40, 33, 47, 61, 45, 43, 21, 23, 58, 17, 10,
+ 51, 25, 36, 32, 60, 20, 57, 16, 50, 31, 19, 15, 30, 14, 13, 12
+ };
+ return debruijn[((x & -x) * 0x022FDD63CC95386D) >> 58];
+}
+
+/* Determine the number of trailing zero bits in a (non-zero) 32-bit x. */
+static SECP256K1_INLINE int secp256k1_ctz32_var(uint32_t x) {
+ VERIFY_CHECK(x != 0);
+#if (__has_builtin(__builtin_ctz) || SECP256K1_GNUC_PREREQ(3,4))
+ /* If the unsigned type is sufficient to represent the largest uint32_t, consider __builtin_ctz. */
+ if (((unsigned)UINT32_MAX) == UINT32_MAX) {
+ return __builtin_ctz(x);
+ }
#endif
+#if (__has_builtin(__builtin_ctzl) || SECP256K1_GNUC_PREREQ(3,4))
+ /* Otherwise consider __builtin_ctzl (the unsigned long type is always at least 32 bits). */
+ return __builtin_ctzl(x);
+#else
+ /* If no suitable CTZ builtin is available, use a (variable time) software emulation. */
+ return secp256k1_ctz32_var_debruijn(x);
+#endif
+}
+
+/* Determine the number of trailing zero bits in a (non-zero) 64-bit x. */
+static SECP256K1_INLINE int secp256k1_ctz64_var(uint64_t x) {
+ VERIFY_CHECK(x != 0);
+#if (__has_builtin(__builtin_ctzl) || SECP256K1_GNUC_PREREQ(3,4))
+ /* If the unsigned long type is sufficient to represent the largest uint64_t, consider __builtin_ctzl. */
+ if (((unsigned long)UINT64_MAX) == UINT64_MAX) {
+ return __builtin_ctzl(x);
+ }
+#endif
+#if (__has_builtin(__builtin_ctzll) || SECP256K1_GNUC_PREREQ(3,4))
+ /* Otherwise consider __builtin_ctzll (the unsigned long long type is always at least 64 bits). */
+ return __builtin_ctzll(x);
+#else
+ /* If no suitable CTZ builtin is available, use a (variable time) software emulation. */
+ return secp256k1_ctz64_var_debruijn(x);
+#endif
+}
#endif /* SECP256K1_UTIL_H */
diff --git a/src/secp256k1/src/valgrind_ctime_test.c b/src/secp256k1/src/valgrind_ctime_test.c
index 3169e3651c..cfca5a196e 100644
--- a/src/secp256k1/src/valgrind_ctime_test.c
+++ b/src/secp256k1/src/valgrind_ctime_test.c
@@ -1,10 +1,12 @@
-/**********************************************************************
- * 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.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2020 Gregory Maxwell *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#include <valgrind/memcheck.h>
+#include <stdio.h>
+
#include "include/secp256k1.h"
#include "assumptions.h"
#include "util.h"
@@ -25,8 +27,42 @@
#include "include/secp256k1_schnorrsig.h"
#endif
+void run_tests(secp256k1_context *ctx, unsigned char *key);
+
int main(void) {
secp256k1_context* ctx;
+ unsigned char key[32];
+ int ret, i;
+
+ 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");
+ return 1;
+ }
+ ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN
+ | SECP256K1_CONTEXT_VERIFY
+ | SECP256K1_CONTEXT_DECLASSIFY);
+ /** 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;
+ }
+
+ run_tests(ctx, key);
+
+ /* 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;
+}
+
+void run_tests(secp256k1_context *ctx, unsigned char *key) {
secp256k1_ecdsa_signature signature;
secp256k1_pubkey pubkey;
size_t siglen = 74;
@@ -34,7 +70,6 @@ int main(void) {
int i;
int ret;
unsigned char msg[32];
- unsigned char key[32];
unsigned char sig[74];
unsigned char spubkey[33];
#ifdef ENABLE_MODULE_RECOVERY
@@ -45,26 +80,10 @@ int main(void) {
secp256k1_keypair keypair;
#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_VERIFY
- | SECP256K1_CONTEXT_DECLASSIFY);
-
/* Test keygen. */
VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
ret = secp256k1_ec_pubkey_create(ctx, &pubkey, key);
@@ -122,12 +141,6 @@ int main(void) {
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);
-
/* Test keypair_create and keypair_xonly_tweak_add. */
#ifdef ENABLE_MODULE_EXTRAKEYS
VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
@@ -140,6 +153,12 @@ int main(void) {
ret = secp256k1_keypair_xonly_tweak_add(ctx, &keypair, msg);
VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
CHECK(ret == 1);
+
+ VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
+ VALGRIND_MAKE_MEM_UNDEFINED(&keypair, sizeof(keypair));
+ ret = secp256k1_keypair_sec(ctx, key, &keypair);
+ VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+ CHECK(ret == 1);
#endif
#ifdef ENABLE_MODULE_SCHNORRSIG
@@ -151,7 +170,4 @@ int main(void) {
VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
CHECK(ret == 1);
#endif
-
- secp256k1_context_destroy(ctx);
- return 0;
}
diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp
index 49b40924e0..eb5c37b34d 100644
--- a/src/test/addrman_tests.cpp
+++ b/src/test/addrman_tests.cpp
@@ -74,9 +74,9 @@ public:
// Simulates connection failure so that we can test eviction of offline nodes
void SimConnFail(const CService& addr)
{
- LOCK(cs);
int64_t nLastSuccess = 1;
- Good_(addr, true, nLastSuccess); // Set last good connection in the deep past.
+ // Set last good connection in the deep past.
+ Good(addr, true, nLastSuccess);
bool count_failure = false;
int64_t nLastTry = GetAdjustedTime()-61;
diff --git a/src/test/blockfilter_index_tests.cpp b/src/test/blockfilter_index_tests.cpp
index 2f532ef598..2eb653e9ec 100644
--- a/src/test/blockfilter_index_tests.cpp
+++ b/src/test/blockfilter_index_tests.cpp
@@ -62,7 +62,7 @@ CBlock BuildChainTestingSetup::CreateBlock(const CBlockIndex* prev,
const CScript& scriptPubKey)
{
const CChainParams& chainparams = Params();
- std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler(::ChainstateActive(), *m_node.mempool, chainparams).CreateNewBlock(scriptPubKey);
+ std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler(m_node.chainman->ActiveChainstate(), *m_node.mempool, chainparams).CreateNewBlock(scriptPubKey);
CBlock& block = pblocktemplate->block;
block.hashPrevBlock = prev->GetBlockHash();
block.nTime = prev->nTime + 1;
@@ -117,9 +117,9 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
std::vector<BlockFilter> filters;
std::vector<uint256> filter_hashes;
- for (const CBlockIndex* block_index = ::ChainActive().Genesis();
+ for (const CBlockIndex* block_index = m_node.chainman->ActiveChain().Genesis();
block_index != nullptr;
- block_index = ::ChainActive().Next(block_index)) {
+ block_index = m_node.chainman->ActiveChain().Next(block_index)) {
BOOST_CHECK(!filter_index.LookupFilter(block_index, filter));
BOOST_CHECK(!filter_index.LookupFilterHeader(block_index, filter_header));
BOOST_CHECK(!filter_index.LookupFilterRange(block_index->nHeight, block_index, filters));
@@ -131,7 +131,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
// BlockUntilSyncedToCurrentChain should return false before index is started.
BOOST_CHECK(!filter_index.BlockUntilSyncedToCurrentChain());
- BOOST_REQUIRE(filter_index.Start(::ChainstateActive()));
+ BOOST_REQUIRE(filter_index.Start(m_node.chainman->ActiveChainstate()));
// Allow filter index to catch up with the block index.
constexpr int64_t timeout_ms = 10 * 1000;
@@ -145,9 +145,9 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
{
LOCK(cs_main);
const CBlockIndex* block_index;
- for (block_index = ::ChainActive().Genesis();
+ for (block_index = m_node.chainman->ActiveChain().Genesis();
block_index != nullptr;
- block_index = ::ChainActive().Next(block_index)) {
+ block_index = m_node.chainman->ActiveChain().Next(block_index)) {
CheckFilterLookups(filter_index, block_index, last_header);
}
}
@@ -156,7 +156,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
const CBlockIndex* tip;
{
LOCK(cs_main);
- tip = ::ChainActive().Tip();
+ tip = m_node.chainman->ActiveChain().Tip();
}
CKey coinbase_key_A, coinbase_key_B;
coinbase_key_A.MakeNewKey(true);
@@ -178,7 +178,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
const CBlockIndex* block_index;
{
LOCK(cs_main);
- block_index = g_chainman.m_blockman.LookupBlockIndex(block->GetHash());
+ block_index = m_node.chainman->m_blockman.LookupBlockIndex(block->GetHash());
}
BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
@@ -196,7 +196,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
const CBlockIndex* block_index;
{
LOCK(cs_main);
- block_index = g_chainman.m_blockman.LookupBlockIndex(block->GetHash());
+ block_index = m_node.chainman->m_blockman.LookupBlockIndex(block->GetHash());
}
BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
@@ -210,7 +210,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
const CBlockIndex* block_index;
{
LOCK(cs_main);
- block_index = g_chainman.m_blockman.LookupBlockIndex(block->GetHash());
+ block_index = m_node.chainman->m_blockman.LookupBlockIndex(block->GetHash());
}
BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
@@ -231,14 +231,14 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
{
LOCK(cs_main);
- block_index = g_chainman.m_blockman.LookupBlockIndex(chainA[i]->GetHash());
+ block_index = m_node.chainman->m_blockman.LookupBlockIndex(chainA[i]->GetHash());
}
BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
CheckFilterLookups(filter_index, block_index, chainA_last_header);
{
LOCK(cs_main);
- block_index = g_chainman.m_blockman.LookupBlockIndex(chainB[i]->GetHash());
+ block_index = m_node.chainman->m_blockman.LookupBlockIndex(chainB[i]->GetHash());
}
BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
CheckFilterLookups(filter_index, block_index, chainB_last_header);
@@ -250,7 +250,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
{
LOCK(cs_main);
- tip = ::ChainActive().Tip();
+ tip = m_node.chainman->ActiveChain().Tip();
}
BOOST_CHECK(filter_index.LookupFilterRange(0, tip, filters));
BOOST_CHECK(filter_index.LookupFilterHashRange(0, tip, filter_hashes));
diff --git a/src/test/coinstatsindex_tests.cpp b/src/test/coinstatsindex_tests.cpp
index 106fcd2a33..597d7a7340 100644
--- a/src/test/coinstatsindex_tests.cpp
+++ b/src/test/coinstatsindex_tests.cpp
@@ -22,7 +22,7 @@ BOOST_FIXTURE_TEST_CASE(coinstatsindex_initial_sync, TestChain100Setup)
const CBlockIndex* block_index;
{
LOCK(cs_main);
- block_index = ChainActive().Tip();
+ block_index = m_node.chainman->ActiveChain().Tip();
}
// CoinStatsIndex should not be found before it is started.
@@ -32,7 +32,7 @@ BOOST_FIXTURE_TEST_CASE(coinstatsindex_initial_sync, TestChain100Setup)
// is started.
BOOST_CHECK(!coin_stats_index.BlockUntilSyncedToCurrentChain());
- BOOST_REQUIRE(coin_stats_index.Start(::ChainstateActive()));
+ BOOST_REQUIRE(coin_stats_index.Start(m_node.chainman->ActiveChainstate()));
// Allow the CoinStatsIndex to catch up with the block index that is syncing
// in a background thread.
@@ -46,7 +46,7 @@ BOOST_FIXTURE_TEST_CASE(coinstatsindex_initial_sync, TestChain100Setup)
const CBlockIndex* genesis_block_index;
{
LOCK(cs_main);
- genesis_block_index = ChainActive().Genesis();
+ genesis_block_index = m_node.chainman->ActiveChain().Genesis();
}
BOOST_CHECK(coin_stats_index.LookUpStats(genesis_block_index, coin_stats));
@@ -64,7 +64,7 @@ BOOST_FIXTURE_TEST_CASE(coinstatsindex_initial_sync, TestChain100Setup)
const CBlockIndex* new_block_index;
{
LOCK(cs_main);
- new_block_index = ChainActive().Tip();
+ new_block_index = m_node.chainman->ActiveChain().Tip();
}
coin_stats_index.LookUpStats(new_block_index, new_coin_stats);
diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp
index a56ce51acb..57178d015d 100644
--- a/src/test/denialofservice_tests.cpp
+++ b/src/test/denialofservice_tests.cpp
@@ -14,36 +14,19 @@
#include <script/signingprovider.h>
#include <script/standard.h>
#include <serialize.h>
+#include <test/util/net.h>
+#include <test/util/setup_common.h>
#include <txorphanage.h>
#include <util/string.h>
#include <util/system.h>
#include <util/time.h>
#include <validation.h>
-#include <test/util/setup_common.h>
-
#include <array>
#include <stdint.h>
#include <boost/test/unit_test.hpp>
-struct CConnmanTest : public CConnman {
- using CConnman::CConnman;
- void AddNode(CNode& node)
- {
- LOCK(cs_vNodes);
- vNodes.push_back(&node);
- }
- void ClearNodes()
- {
- LOCK(cs_vNodes);
- for (CNode* node : vNodes) {
- delete node;
- }
- vNodes.clear();
- }
-};
-
static CService ip(uint32_t i)
{
struct in_addr s;
@@ -83,8 +66,8 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
// This test requires that we have a chain with non-zero work.
{
LOCK(cs_main);
- BOOST_CHECK(::ChainActive().Tip() != nullptr);
- BOOST_CHECK(::ChainActive().Tip()->nChainWork > 0);
+ BOOST_CHECK(m_node.chainman->ActiveChain().Tip() != nullptr);
+ BOOST_CHECK(m_node.chainman->ActiveChain().Tip()->nChainWork > 0);
}
// Test starts here
@@ -120,7 +103,7 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
peerLogic->FinalizeNode(dummyNode1);
}
-static void AddRandomOutboundPeer(std::vector<CNode *> &vNodes, PeerManager &peerLogic, CConnmanTest* connman)
+static void AddRandomOutboundPeer(std::vector<CNode*>& vNodes, PeerManager& peerLogic, ConnmanTestMsg& connman)
{
CAddress addr(ip(g_insecure_rand_ctx.randbits(32)), NODE_NONE);
vNodes.emplace_back(new CNode(id++, ServiceFlags(NODE_NETWORK | NODE_WITNESS), INVALID_SOCKET, addr, /* nKeyedNetGroupIn */ 0, /* nLocalHostNonceIn */ 0, CAddress(), /* pszDest */ "", ConnectionType::OUTBOUND_FULL_RELAY, /* inbound_onion */ false));
@@ -130,13 +113,13 @@ static void AddRandomOutboundPeer(std::vector<CNode *> &vNodes, PeerManager &pee
peerLogic.InitializeNode(&node);
node.fSuccessfullyConnected = true;
- connman->AddNode(node);
+ connman.AddTestNode(node);
}
BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
{
const CChainParams& chainparams = Params();
- auto connman = std::make_unique<CConnmanTest>(0x1337, 0x1337, *m_node.addrman);
+ auto connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman);
auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, nullptr,
*m_node.scheduler, *m_node.chainman, *m_node.mempool, false);
@@ -150,8 +133,8 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
std::vector<CNode *> vNodes;
// Mock some outbound peers
- for (int i=0; i<max_outbound_full_relay; ++i) {
- AddRandomOutboundPeer(vNodes, *peerLogic, connman.get());
+ for (int i = 0; i < max_outbound_full_relay; ++i) {
+ AddRandomOutboundPeer(vNodes, *peerLogic, *connman);
}
peerLogic->CheckForStaleTipAndEvictPeers();
@@ -176,7 +159,7 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
// If we add one more peer, something should get marked for eviction
// on the next check (since we're mocking the time to be in the future, the
// required time connected check should be satisfied).
- AddRandomOutboundPeer(vNodes, *peerLogic, connman.get());
+ AddRandomOutboundPeer(vNodes, *peerLogic, *connman);
peerLogic->CheckForStaleTipAndEvictPeers();
for (int i = 0; i < max_outbound_full_relay; ++i) {
@@ -202,14 +185,14 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
peerLogic->FinalizeNode(*node);
}
- connman->ClearNodes();
+ connman->ClearTestNodes();
}
BOOST_AUTO_TEST_CASE(peer_discouragement)
{
const CChainParams& chainparams = Params();
auto banman = std::make_unique<BanMan>(m_args.GetDataDirBase() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
- auto connman = std::make_unique<CConnmanTest>(0x1337, 0x1337, *m_node.addrman);
+ auto connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman);
auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, banman.get(),
*m_node.scheduler, *m_node.chainman, *m_node.mempool, false);
@@ -233,7 +216,7 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
nodes[0]->SetCommonVersion(PROTOCOL_VERSION);
peerLogic->InitializeNode(nodes[0]);
nodes[0]->fSuccessfullyConnected = true;
- connman->AddNode(*nodes[0]);
+ connman->AddTestNode(*nodes[0]);
peerLogic->Misbehaving(nodes[0]->GetId(), DISCOURAGEMENT_THRESHOLD, /* message */ ""); // Should be discouraged
{
LOCK(nodes[0]->cs_sendProcessing);
@@ -249,7 +232,7 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
nodes[1]->SetCommonVersion(PROTOCOL_VERSION);
peerLogic->InitializeNode(nodes[1]);
nodes[1]->fSuccessfullyConnected = true;
- connman->AddNode(*nodes[1]);
+ connman->AddTestNode(*nodes[1]);
peerLogic->Misbehaving(nodes[1]->GetId(), DISCOURAGEMENT_THRESHOLD - 1, /* message */ "");
{
LOCK(nodes[1]->cs_sendProcessing);
@@ -280,7 +263,7 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
nodes[2]->SetCommonVersion(PROTOCOL_VERSION);
peerLogic->InitializeNode(nodes[2]);
nodes[2]->fSuccessfullyConnected = true;
- connman->AddNode(*nodes[2]);
+ connman->AddTestNode(*nodes[2]);
peerLogic->Misbehaving(nodes[2]->GetId(), DISCOURAGEMENT_THRESHOLD, /* message */ "");
{
LOCK(nodes[2]->cs_sendProcessing);
@@ -296,7 +279,7 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
for (CNode* node : nodes) {
peerLogic->FinalizeNode(*node);
}
- connman->ClearNodes();
+ connman->ClearTestNodes();
}
BOOST_AUTO_TEST_CASE(DoS_bantime)
diff --git a/src/test/fuzz/addrman.cpp b/src/test/fuzz/addrman.cpp
index 98ae32a8d0..db0b461873 100644
--- a/src/test/fuzz/addrman.cpp
+++ b/src/test/fuzz/addrman.cpp
@@ -57,15 +57,6 @@ FUZZ_TARGET_INIT(addrman, initialize_addrman)
(void)addr_man.SelectTriedCollision();
},
[&] {
- (void)addr_man.Select(fuzzed_data_provider.ConsumeBool());
- },
- [&] {
- (void)addr_man.GetAddr(
- /* max_addresses */ fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096),
- /* max_pct */ fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096),
- /* network */ std::nullopt);
- },
- [&] {
const std::optional<CAddress> opt_address = ConsumeDeserializable<CAddress>(fuzzed_data_provider);
const std::optional<CNetAddr> opt_net_addr = ConsumeDeserializable<CNetAddr>(fuzzed_data_provider);
if (opt_address && opt_net_addr) {
@@ -109,12 +100,15 @@ FUZZ_TARGET_INIT(addrman, initialize_addrman)
if (opt_service) {
addr_man.SetServices(*opt_service, ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS));
}
- },
- [&] {
- (void)addr_man.Check();
});
}
- (void)addr_man.size();
+ const CAddrMan& const_addr_man{addr_man};
+ (void)/*const_*/addr_man.GetAddr(
+ /* max_addresses */ fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096),
+ /* max_pct */ fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096),
+ /* network */ std::nullopt);
+ (void)/*const_*/addr_man.Select(fuzzed_data_provider.ConsumeBool());
+ (void)const_addr_man.size();
CDataStream data_stream(SER_NETWORK, PROTOCOL_VERSION);
- data_stream << addr_man;
+ data_stream << const_addr_man;
}
diff --git a/src/test/fuzz/base_encode_decode.cpp b/src/test/fuzz/base_encode_decode.cpp
index 4470e13a61..2b4f15115b 100644
--- a/src/test/fuzz/base_encode_decode.cpp
+++ b/src/test/fuzz/base_encode_decode.cpp
@@ -14,7 +14,12 @@
#include <string>
#include <vector>
-FUZZ_TARGET(base_encode_decode)
+void initialize_base_encode_decode()
+{
+ static const ECCVerifyHandle verify_handle;
+}
+
+FUZZ_TARGET_INIT(base_encode_decode, initialize_base_encode_decode)
{
const std::string random_encoded_string(buffer.begin(), buffer.end());
diff --git a/src/test/fuzz/coins_view.cpp b/src/test/fuzz/coins_view.cpp
index b509ee0b26..42f19d16c6 100644
--- a/src/test/fuzz/coins_view.cpp
+++ b/src/test/fuzz/coins_view.cpp
@@ -27,6 +27,7 @@
#include <vector>
namespace {
+const TestingSetup* g_setup;
const Coin EMPTY_COIN{};
bool operator==(const Coin& a, const Coin& b)
@@ -39,6 +40,7 @@ bool operator==(const Coin& a, const Coin& b)
void initialize_coins_view()
{
static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
+ g_setup = testing_setup.get();
}
FUZZ_TARGET_INIT(coins_view, initialize_coins_view)
@@ -268,7 +270,7 @@ FUZZ_TARGET_INIT(coins_view, initialize_coins_view)
CCoinsStats stats{CoinStatsHashType::HASH_SERIALIZED};
bool expected_code_path = false;
try {
- (void)GetUTXOStats(&coins_view_cache, WITH_LOCK(::cs_main, return std::ref(g_chainman.m_blockman)), stats);
+ (void)GetUTXOStats(&coins_view_cache, WITH_LOCK(::cs_main, return std::ref(g_setup->m_node.chainman->m_blockman)), stats);
} catch (const std::logic_error&) {
expected_code_path = true;
}
diff --git a/src/test/fuzz/crypto.cpp b/src/test/fuzz/crypto.cpp
index eeeac18968..f83747e424 100644
--- a/src/test/fuzz/crypto.cpp
+++ b/src/test/fuzz/crypto.cpp
@@ -19,6 +19,10 @@
FUZZ_TARGET(crypto)
{
+ // Hashing is expensive with sanitizers enabled, so limit the number of
+ // calls
+ int limit_max_ops{30};
+
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
std::vector<uint8_t> data = ConsumeRandomLengthByteVector(fuzzed_data_provider);
if (data.empty()) {
@@ -36,7 +40,7 @@ FUZZ_TARGET(crypto)
SHA3_256 sha3;
CSipHasher sip_hasher{fuzzed_data_provider.ConsumeIntegral<uint64_t>(), fuzzed_data_provider.ConsumeIntegral<uint64_t>()};
- while (fuzzed_data_provider.ConsumeBool()) {
+ while (--limit_max_ops >= 0 && fuzzed_data_provider.ConsumeBool()) {
CallOneOf(
fuzzed_data_provider,
[&] {
diff --git a/src/test/fuzz/deserialize.cpp b/src/test/fuzz/deserialize.cpp
index 700e368319..721e4360d0 100644
--- a/src/test/fuzz/deserialize.cpp
+++ b/src/test/fuzz/deserialize.cpp
@@ -53,9 +53,9 @@ struct invalid_fuzzing_input_exception : public std::exception {
};
template <typename T>
-CDataStream Serialize(const T& obj, const int version = INIT_PROTO_VERSION)
+CDataStream Serialize(const T& obj, const int version = INIT_PROTO_VERSION, const int ser_type = SER_NETWORK)
{
- CDataStream ds(SER_NETWORK, version);
+ CDataStream ds(ser_type, version);
ds << obj;
return ds;
}
@@ -69,9 +69,9 @@ T Deserialize(CDataStream ds)
}
template <typename T>
-void DeserializeFromFuzzingInput(FuzzBufferType buffer, T& obj, const std::optional<int> protocol_version = std::nullopt)
+void DeserializeFromFuzzingInput(FuzzBufferType buffer, T& obj, const std::optional<int> protocol_version = std::nullopt, const int ser_type = SER_NETWORK)
{
- CDataStream ds(buffer, SER_NETWORK, INIT_PROTO_VERSION);
+ CDataStream ds(buffer, ser_type, INIT_PROTO_VERSION);
if (protocol_version) {
ds.SetVersion(*protocol_version);
} else {
@@ -92,9 +92,9 @@ void DeserializeFromFuzzingInput(FuzzBufferType buffer, T& obj, const std::optio
}
template <typename T>
-void AssertEqualAfterSerializeDeserialize(const T& obj, const int version = INIT_PROTO_VERSION)
+void AssertEqualAfterSerializeDeserialize(const T& obj, const int version = INIT_PROTO_VERSION, const int ser_type = SER_NETWORK)
{
- assert(Deserialize<T>(Serialize(obj, version)) == obj);
+ assert(Deserialize<T>(Serialize(obj, version, ser_type)) == obj);
}
} // namespace
@@ -136,14 +136,13 @@ FUZZ_TARGET_DESERIALIZE(partial_merkle_tree_deserialize, {
FUZZ_TARGET_DESERIALIZE(pub_key_deserialize, {
CPubKey pub_key;
DeserializeFromFuzzingInput(buffer, pub_key);
- // TODO: The following equivalence should hold for CPubKey? Fix.
- // AssertEqualAfterSerializeDeserialize(pub_key);
+ AssertEqualAfterSerializeDeserialize(pub_key);
})
FUZZ_TARGET_DESERIALIZE(script_deserialize, {
CScript script;
DeserializeFromFuzzingInput(buffer, script);
})
-FUZZ_TARGET_DESERIALIZE(subnet_deserialize, {
+FUZZ_TARGET_DESERIALIZE(sub_net_deserialize, {
CSubNet sub_net_1;
DeserializeFromFuzzingInput(buffer, sub_net_1, INIT_PROTO_VERSION);
AssertEqualAfterSerializeDeserialize(sub_net_1, INIT_PROTO_VERSION);
@@ -223,7 +222,7 @@ FUZZ_TARGET_DESERIALIZE(coins_deserialize, {
Coin coin;
DeserializeFromFuzzingInput(buffer, coin);
})
-FUZZ_TARGET_DESERIALIZE(net_address_deserialize, {
+FUZZ_TARGET_DESERIALIZE(netaddr_deserialize, {
CNetAddr na;
DeserializeFromFuzzingInput(buffer, na);
if (na.IsAddrV1Compatible()) {
@@ -231,7 +230,7 @@ FUZZ_TARGET_DESERIALIZE(net_address_deserialize, {
}
AssertEqualAfterSerializeDeserialize(na, INIT_PROTO_VERSION | ADDRV2_FORMAT);
})
-FUZZ_TARGET_DESERIALIZE(net_service_deserialize, {
+FUZZ_TARGET_DESERIALIZE(service_deserialize, {
CService s;
DeserializeFromFuzzingInput(buffer, s);
if (s.IsAddrV1Compatible()) {
@@ -251,9 +250,37 @@ FUZZ_TARGET_DESERIALIZE(messageheader_deserialize, {
DeserializeFromFuzzingInput(buffer, mh);
(void)mh.IsCommandValid();
})
-FUZZ_TARGET_DESERIALIZE(address_deserialize, {
+FUZZ_TARGET_DESERIALIZE(address_deserialize_v1_notime, {
CAddress a;
- DeserializeFromFuzzingInput(buffer, a);
+ DeserializeFromFuzzingInput(buffer, a, INIT_PROTO_VERSION);
+ // A CAddress without nTime (as is expected under INIT_PROTO_VERSION) will roundtrip
+ // in all 5 formats (with/without nTime, v1/v2, network/disk)
+ AssertEqualAfterSerializeDeserialize(a, INIT_PROTO_VERSION);
+ AssertEqualAfterSerializeDeserialize(a, PROTOCOL_VERSION);
+ AssertEqualAfterSerializeDeserialize(a, 0, SER_DISK);
+ AssertEqualAfterSerializeDeserialize(a, PROTOCOL_VERSION | ADDRV2_FORMAT);
+ AssertEqualAfterSerializeDeserialize(a, ADDRV2_FORMAT, SER_DISK);
+})
+FUZZ_TARGET_DESERIALIZE(address_deserialize_v1_withtime, {
+ CAddress a;
+ DeserializeFromFuzzingInput(buffer, a, PROTOCOL_VERSION);
+ // A CAddress in V1 mode will roundtrip in all 4 formats that have nTime.
+ AssertEqualAfterSerializeDeserialize(a, PROTOCOL_VERSION);
+ AssertEqualAfterSerializeDeserialize(a, 0, SER_DISK);
+ AssertEqualAfterSerializeDeserialize(a, PROTOCOL_VERSION | ADDRV2_FORMAT);
+ AssertEqualAfterSerializeDeserialize(a, ADDRV2_FORMAT, SER_DISK);
+})
+FUZZ_TARGET_DESERIALIZE(address_deserialize_v2, {
+ CAddress a;
+ DeserializeFromFuzzingInput(buffer, a, PROTOCOL_VERSION | ADDRV2_FORMAT);
+ // A CAddress in V2 mode will roundtrip in both V2 formats, and also in the V1 formats
+ // with time if it's V1 compatible.
+ if (a.IsAddrV1Compatible()) {
+ AssertEqualAfterSerializeDeserialize(a, PROTOCOL_VERSION);
+ AssertEqualAfterSerializeDeserialize(a, 0, SER_DISK);
+ }
+ AssertEqualAfterSerializeDeserialize(a, PROTOCOL_VERSION | ADDRV2_FORMAT);
+ AssertEqualAfterSerializeDeserialize(a, ADDRV2_FORMAT, SER_DISK);
})
FUZZ_TARGET_DESERIALIZE(inv_deserialize, {
CInv i;
diff --git a/src/test/fuzz/float.cpp b/src/test/fuzz/float.cpp
index adef66a3ee..2f77c8949e 100644
--- a/src/test/fuzz/float.cpp
+++ b/src/test/fuzz/float.cpp
@@ -5,6 +5,7 @@
#include <memusage.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
#include <util/serfloat.h>
#include <version.h>
@@ -17,7 +18,33 @@ FUZZ_TARGET(float)
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
{
- const double d = fuzzed_data_provider.ConsumeFloatingPoint<double>();
+ const double d{[&] {
+ double tmp;
+ CallOneOf(
+ fuzzed_data_provider,
+ // an actual number
+ [&] { tmp = fuzzed_data_provider.ConsumeFloatingPoint<double>(); },
+ // special numbers and NANs
+ [&] { tmp = fuzzed_data_provider.PickValueInArray({
+ std::numeric_limits<double>::infinity(),
+ -std::numeric_limits<double>::infinity(),
+ std::numeric_limits<double>::min(),
+ -std::numeric_limits<double>::min(),
+ std::numeric_limits<double>::max(),
+ -std::numeric_limits<double>::max(),
+ std::numeric_limits<double>::lowest(),
+ -std::numeric_limits<double>::lowest(),
+ std::numeric_limits<double>::quiet_NaN(),
+ -std::numeric_limits<double>::quiet_NaN(),
+ std::numeric_limits<double>::signaling_NaN(),
+ -std::numeric_limits<double>::signaling_NaN(),
+ std::numeric_limits<double>::denorm_min(),
+ -std::numeric_limits<double>::denorm_min(),
+ }); },
+ // Anything from raw memory (also checks that DecodeDouble doesn't crash on any input)
+ [&] { tmp = DecodeDouble(fuzzed_data_provider.ConsumeIntegral<uint64_t>()); });
+ return tmp;
+ }()};
(void)memusage::DynamicUsage(d);
uint64_t encoded = EncodeDouble(d);
diff --git a/src/test/fuzz/fuzz.cpp b/src/test/fuzz/fuzz.cpp
index 631c861bb6..a33297e0ed 100644
--- a/src/test/fuzz/fuzz.cpp
+++ b/src/test/fuzz/fuzz.cpp
@@ -13,6 +13,7 @@
#include <cstdint>
#include <exception>
#include <memory>
+#include <string>
#include <unistd.h>
#include <vector>
@@ -37,6 +38,14 @@ void initialize()
// Terminate immediately if a fuzzing harness ever tries to create a TCP socket.
CreateSock = [](const CService&) -> std::unique_ptr<Sock> { std::terminate(); };
+ // Terminate immediately if a fuzzing harness ever tries to perform a DNS lookup.
+ g_dns_lookup = [](const std::string& name, bool allow_lookup) {
+ if (allow_lookup) {
+ std::terminate();
+ }
+ return WrappedGetAddrInfo(name, false);
+ };
+
bool should_abort{false};
if (std::getenv("PRINT_ALL_FUZZ_TARGETS_AND_ABORT")) {
for (const auto& t : FuzzTargets()) {
diff --git a/src/test/fuzz/load_external_block_file.cpp b/src/test/fuzz/load_external_block_file.cpp
index dbd0c76d42..a7770c90e8 100644
--- a/src/test/fuzz/load_external_block_file.cpp
+++ b/src/test/fuzz/load_external_block_file.cpp
@@ -13,9 +13,14 @@
#include <cstdint>
#include <vector>
+namespace {
+const TestingSetup* g_setup;
+} // namespace
+
void initialize_load_external_block_file()
{
static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
+ g_setup = testing_setup.get();
}
FUZZ_TARGET_INIT(load_external_block_file, initialize_load_external_block_file)
@@ -27,5 +32,5 @@ FUZZ_TARGET_INIT(load_external_block_file, initialize_load_external_block_file)
return;
}
FlatFilePos flat_file_pos;
- ::ChainstateActive().LoadExternalBlockFile(Params(), fuzzed_block_file, fuzzed_data_provider.ConsumeBool() ? &flat_file_pos : nullptr);
+ g_setup->m_node.chainman->ActiveChainstate().LoadExternalBlockFile(Params(), fuzzed_block_file, fuzzed_data_provider.ConsumeBool() ? &flat_file_pos : nullptr);
}
diff --git a/src/test/fuzz/node_eviction.cpp b/src/test/fuzz/node_eviction.cpp
index 70ffc6bf37..a3f71426fa 100644
--- a/src/test/fuzz/node_eviction.cpp
+++ b/src/test/fuzz/node_eviction.cpp
@@ -31,7 +31,7 @@ FUZZ_TARGET(node_eviction)
/* nKeyedNetGroup */ fuzzed_data_provider.ConsumeIntegral<uint64_t>(),
/* prefer_evict */ fuzzed_data_provider.ConsumeBool(),
/* m_is_local */ fuzzed_data_provider.ConsumeBool(),
- /* m_is_onion */ fuzzed_data_provider.ConsumeBool(),
+ /* m_network */ fuzzed_data_provider.PickValueInArray(ALL_NETWORKS),
});
}
// Make a copy since eviction_candidates may be in some valid but otherwise
diff --git a/src/test/fuzz/process_message.cpp b/src/test/fuzz/process_message.cpp
index 7b99193ad0..c4e4d4c785 100644
--- a/src/test/fuzz/process_message.cpp
+++ b/src/test/fuzz/process_message.cpp
@@ -58,7 +58,19 @@ void initialize_process_message()
static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
g_setup = testing_setup.get();
+
+ // Temporary debug for https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=35027
+ {
+ LOCK(::cs_main);
+ assert(CheckDiskSpace(gArgs.GetDataDirNet()));
+ assert(CheckDiskSpace(gArgs.GetDataDirNet(), 48 * 2 * 2 * g_setup->m_node.chainman->ActiveChainstate().CoinsTip().GetCacheSize()));
+ }
for (int i = 0; i < 2 * COINBASE_MATURITY; i++) {
+ {
+ LOCK(::cs_main);
+ assert(CheckDiskSpace(gArgs.GetDataDirNet()));
+ assert(CheckDiskSpace(gArgs.GetDataDirNet(), 48 * 2 * 2 * g_setup->m_node.chainman->ActiveChainstate().CoinsTip().GetCacheSize()));
+ }
MineBlock(g_setup->m_node, CScript() << OP_TRUE);
}
SyncWithValidationInterfaceQueue();
diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h
index 36b1d5035c..023dcdb3e5 100644
--- a/src/test/fuzz/util.h
+++ b/src/test/fuzz/util.h
@@ -44,7 +44,7 @@ void CallOneOf(FuzzedDataProvider& fuzzed_data_provider, Callables... callables)
const size_t call_index{fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, call_size - 1)};
size_t i{0};
- return ((i++ == call_index ? callables() : void()), ...);
+ ((i++ == call_index ? callables() : void()), ...);
}
template <typename Collection>
diff --git a/src/test/fuzz/validation_load_mempool.cpp b/src/test/fuzz/validation_load_mempool.cpp
index e1a21b6c53..c2aaf486c5 100644
--- a/src/test/fuzz/validation_load_mempool.cpp
+++ b/src/test/fuzz/validation_load_mempool.cpp
@@ -14,9 +14,14 @@
#include <cstdint>
#include <vector>
+namespace {
+const TestingSetup* g_setup;
+} // namespace
+
void initialize_validation_load_mempool()
{
static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
+ g_setup = testing_setup.get();
}
FUZZ_TARGET_INIT(validation_load_mempool, initialize_validation_load_mempool)
@@ -29,6 +34,6 @@ FUZZ_TARGET_INIT(validation_load_mempool, initialize_validation_load_mempool)
auto fuzzed_fopen = [&](const fs::path&, const char*) {
return fuzzed_file_provider.open();
};
- (void)LoadMempool(pool, ::ChainstateActive(), fuzzed_fopen);
+ (void)LoadMempool(pool, g_setup->m_node.chainman->ActiveChainstate(), fuzzed_fopen);
(void)DumpMempool(pool, fuzzed_fopen, true);
}
diff --git a/src/test/interfaces_tests.cpp b/src/test/interfaces_tests.cpp
index 73463b071e..42a7c7798c 100644
--- a/src/test/interfaces_tests.cpp
+++ b/src/test/interfaces_tests.cpp
@@ -98,7 +98,7 @@ BOOST_AUTO_TEST_CASE(findCommonAncestor)
auto* orig_tip = active.Tip();
for (int i = 0; i < 10; ++i) {
BlockValidationState state;
- ChainstateActive().InvalidateBlock(state, Params(), active.Tip());
+ m_node.chainman->ActiveChainstate().InvalidateBlock(state, Params(), active.Tip());
}
BOOST_CHECK_EQUAL(active.Height(), orig_tip->nHeight - 10);
coinbaseKey.MakeNewKey(true);
diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp
index cb66d5164e..b915982d98 100644
--- a/src/test/key_tests.cpp
+++ b/src/test/key_tests.cpp
@@ -300,6 +300,48 @@ BOOST_AUTO_TEST_CASE(bip340_test_vectors)
auto sig = ParseHex(test.first[2]);
BOOST_CHECK_EQUAL(XOnlyPubKey(pubkey).VerifySchnorr(uint256(msg), sig), test.second);
}
+
+ static const std::vector<std::array<std::string, 5>> SIGN_VECTORS = {
+ {{"0000000000000000000000000000000000000000000000000000000000000003", "F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9", "0000000000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000000", "E907831F80848D1069A5371B402410364BDF1C5F8307B0084C55F1CE2DCA821525F66A4A85EA8B71E482A74F382D2CE5EBEEE8FDB2172F477DF4900D310536C0"}},
+ {{"B7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF", "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "0000000000000000000000000000000000000000000000000000000000000001", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "6896BD60EEAE296DB48A229FF71DFE071BDE413E6D43F917DC8DCF8C78DE33418906D11AC976ABCCB20B091292BFF4EA897EFCB639EA871CFA95F6DE339E4B0A"}},
+ {{"C90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B14E5C9", "DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EB8", "C87AA53824B4D7AE2EB035A2B5BBBCCC080E76CDC6D1692C4B0B62D798E6D906", "7E2D58D8B3BCDF1ABADEC7829054F90DDA9805AAB56C77333024B9D0A508B75C", "5831AAEED7B44BB74E5EAB94BA9D4294C49BCF2A60728D8B4C200F50DD313C1BAB745879A5AD954A72C45A91C3A51D3C7ADEA98D82F8481E0E1E03674A6F3FB7"}},
+ {{"0B432B2677937381AEF05BB02A66ECD012773062CF3FA2549E44F58ED2401710", "25D1DFF95105F5253C4022F628A996AD3A0D95FBF21D468A1B33F8C160D8F517", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", "7EB0509757E246F19449885651611CB965ECC1A187DD51B64FDA1EDC9637D5EC97582B9CB13DB3933705B32BA982AF5AF25FD78881EBB32771FC5922EFC66EA3"}},
+ };
+
+ for (const auto& [sec_hex, pub_hex, aux_hex, msg_hex, sig_hex] : SIGN_VECTORS) {
+ auto sec = ParseHex(sec_hex);
+ auto pub = ParseHex(pub_hex);
+ uint256 aux256(ParseHex(aux_hex));
+ uint256 msg256(ParseHex(msg_hex));
+ auto sig = ParseHex(sig_hex);
+ unsigned char sig64[64];
+
+ // Run the untweaked test vectors above, comparing with exact expected signature.
+ CKey key;
+ key.Set(sec.begin(), sec.end(), true);
+ XOnlyPubKey pubkey(key.GetPubKey());
+ BOOST_CHECK(std::equal(pubkey.begin(), pubkey.end(), pub.begin(), pub.end()));
+ bool ok = key.SignSchnorr(msg256, sig64, nullptr, &aux256);
+ BOOST_CHECK(ok);
+ BOOST_CHECK(std::vector<unsigned char>(sig64, sig64 + 64) == sig);
+ // Verify those signatures for good measure.
+ BOOST_CHECK(pubkey.VerifySchnorr(msg256, sig64));
+
+ // Do 10 iterations where we sign with a random Merkle root to tweak,
+ // and compare against the resulting tweaked keys, with random aux.
+ // In iteration i=0 we tweak with empty Merkle tree.
+ for (int i = 0; i < 10; ++i) {
+ uint256 merkle_root;
+ if (i) merkle_root = InsecureRand256();
+ auto tweaked = pubkey.CreateTapTweak(i ? &merkle_root : nullptr);
+ BOOST_CHECK(tweaked);
+ XOnlyPubKey tweaked_key = tweaked->first;
+ aux256 = InsecureRand256();
+ bool ok = key.SignSchnorr(msg256, sig64, &merkle_root, &aux256);
+ BOOST_CHECK(ok);
+ BOOST_CHECK(tweaked_key.VerifySchnorr(msg256, sig64));
+ }
+ }
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index c47d0eae1e..e20c5e4e8f 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -28,8 +28,8 @@ struct MinerTestingSetup : public TestingSetup {
void TestPackageSelection(const CChainParams& chainparams, const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_node.mempool->cs);
bool TestSequenceLocks(const CTransaction& tx, int flags) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_node.mempool->cs)
{
- CCoinsViewMemPool viewMempool(&m_node.chainman->ActiveChainstate().CoinsTip(), *m_node.mempool);
- return CheckSequenceLocks(m_node.chainman->ActiveChain().Tip(), viewMempool, tx, flags);
+ CCoinsViewMemPool view_mempool(&m_node.chainman->ActiveChainstate().CoinsTip(), *m_node.mempool);
+ return CheckSequenceLocks(m_node.chainman->ActiveChain().Tip(), view_mempool, tx, flags);
}
BlockAssembler AssemblerForTest(const CChainParams& params);
};
@@ -45,7 +45,7 @@ BlockAssembler MinerTestingSetup::AssemblerForTest(const CChainParams& params)
options.nBlockMaxWeight = MAX_BLOCK_WEIGHT;
options.blockMinFeeRate = blockMinFeeRate;
- return BlockAssembler(::ChainstateActive(), *m_node.mempool, params, options);
+ return BlockAssembler(m_node.chainman->ActiveChainstate(), *m_node.mempool, params, options);
}
constexpr static struct {
@@ -82,11 +82,11 @@ constexpr static struct {
{2, 0xbbbeb305}, {2, 0xfe1c810a},
};
-static CBlockIndex CreateBlockIndex(int nHeight) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+static CBlockIndex CreateBlockIndex(int nHeight, CBlockIndex* active_chain_tip) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
CBlockIndex index;
index.nHeight = nHeight;
- index.pprev = ::ChainActive().Tip();
+ index.pprev = active_chain_tip;
return index;
}
@@ -228,17 +228,17 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
{
LOCK(cs_main);
pblock->nVersion = 1;
- pblock->nTime = ::ChainActive().Tip()->GetMedianTimePast()+1;
+ pblock->nTime = m_node.chainman->ActiveChain().Tip()->GetMedianTimePast()+1;
CMutableTransaction txCoinbase(*pblock->vtx[0]);
txCoinbase.nVersion = 1;
txCoinbase.vin[0].scriptSig = CScript();
txCoinbase.vin[0].scriptSig.push_back(bi.extranonce);
- txCoinbase.vin[0].scriptSig.push_back(::ChainActive().Height());
+ txCoinbase.vin[0].scriptSig.push_back(m_node.chainman->ActiveChain().Height());
txCoinbase.vout.resize(1); // Ignore the (optional) segwit commitment added by CreateNewBlock (as the hardcoded nonces don't account for this)
txCoinbase.vout[0].scriptPubKey = CScript();
pblock->vtx[0] = MakeTransactionRef(std::move(txCoinbase));
if (txFirst.size() == 0)
- baseheight = ::ChainActive().Height();
+ baseheight = m_node.chainman->ActiveChain().Height();
if (txFirst.size() < 4)
txFirst.push_back(pblock->vtx[0]);
pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
@@ -364,29 +364,29 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
m_node.mempool->clear();
// subsidy changing
- int nHeight = ::ChainActive().Height();
+ int nHeight = m_node.chainman->ActiveChain().Height();
// Create an actual 209999-long block chain (without valid blocks).
- while (::ChainActive().Tip()->nHeight < 209999) {
- CBlockIndex* prev = ::ChainActive().Tip();
+ while (m_node.chainman->ActiveChain().Tip()->nHeight < 209999) {
+ CBlockIndex* prev = m_node.chainman->ActiveChain().Tip();
CBlockIndex* next = new CBlockIndex();
next->phashBlock = new uint256(InsecureRand256());
- ::ChainstateActive().CoinsTip().SetBestBlock(next->GetBlockHash());
+ m_node.chainman->ActiveChainstate().CoinsTip().SetBestBlock(next->GetBlockHash());
next->pprev = prev;
next->nHeight = prev->nHeight + 1;
next->BuildSkip();
- ::ChainActive().SetTip(next);
+ m_node.chainman->ActiveChain().SetTip(next);
}
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
// Extend to a 210000-long block chain.
- while (::ChainActive().Tip()->nHeight < 210000) {
- CBlockIndex* prev = ::ChainActive().Tip();
+ while (m_node.chainman->ActiveChain().Tip()->nHeight < 210000) {
+ CBlockIndex* prev = m_node.chainman->ActiveChain().Tip();
CBlockIndex* next = new CBlockIndex();
next->phashBlock = new uint256(InsecureRand256());
- ::ChainstateActive().CoinsTip().SetBestBlock(next->GetBlockHash());
+ m_node.chainman->ActiveChainstate().CoinsTip().SetBestBlock(next->GetBlockHash());
next->pprev = prev;
next->nHeight = prev->nHeight + 1;
next->BuildSkip();
- ::ChainActive().SetTip(next);
+ m_node.chainman->ActiveChain().SetTip(next);
}
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
@@ -409,16 +409,16 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
m_node.mempool->clear();
// Delete the dummy blocks again.
- while (::ChainActive().Tip()->nHeight > nHeight) {
- CBlockIndex* del = ::ChainActive().Tip();
- ::ChainActive().SetTip(del->pprev);
- ::ChainstateActive().CoinsTip().SetBestBlock(del->pprev->GetBlockHash());
+ while (m_node.chainman->ActiveChain().Tip()->nHeight > nHeight) {
+ CBlockIndex* del = m_node.chainman->ActiveChain().Tip();
+ m_node.chainman->ActiveChain().SetTip(del->pprev);
+ m_node.chainman->ActiveChainstate().CoinsTip().SetBestBlock(del->pprev->GetBlockHash());
delete del->phashBlock;
delete del;
}
// non-final txs in mempool
- SetMockTime(::ChainActive().Tip()->GetMedianTimePast()+1);
+ SetMockTime(m_node.chainman->ActiveChain().Tip()->GetMedianTimePast()+1);
int flags = LOCKTIME_VERIFY_SEQUENCE|LOCKTIME_MEDIAN_TIME_PAST;
// height map
std::vector<int> prevheights;
@@ -430,7 +430,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.vin[0].prevout.hash = txFirst[0]->GetHash(); // only 1 transaction
tx.vin[0].prevout.n = 0;
tx.vin[0].scriptSig = CScript() << OP_1;
- tx.vin[0].nSequence = ::ChainActive().Tip()->nHeight + 1; // txFirst[0] is the 2nd block
+ tx.vin[0].nSequence = m_node.chainman->ActiveChain().Tip()->nHeight + 1; // txFirst[0] is the 2nd block
prevheights[0] = baseheight + 1;
tx.vout.resize(1);
tx.vout[0].nValue = BLOCKSUBSIDY-HIGHFEE;
@@ -438,53 +438,62 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.nLockTime = 0;
hash = tx.GetHash();
m_node.mempool->addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
- BOOST_CHECK(CheckFinalTx(::ChainActive().Tip(), CTransaction(tx), flags)); // Locktime passes
+ BOOST_CHECK(CheckFinalTx(m_node.chainman->ActiveChain().Tip(), CTransaction(tx), flags)); // Locktime passes
BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks fail
- BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, prevheights, CreateBlockIndex(::ChainActive().Tip()->nHeight + 2))); // Sequence locks pass on 2nd block
+
+ {
+ CBlockIndex* active_chain_tip = m_node.chainman->ActiveChain().Tip();
+ BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, prevheights, CreateBlockIndex(active_chain_tip->nHeight + 2, active_chain_tip))); // Sequence locks pass on 2nd block
+ }
// relative time locked
tx.vin[0].prevout.hash = txFirst[1]->GetHash();
- tx.vin[0].nSequence = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG | (((::ChainActive().Tip()->GetMedianTimePast()+1-::ChainActive()[1]->GetMedianTimePast()) >> CTxIn::SEQUENCE_LOCKTIME_GRANULARITY) + 1); // txFirst[1] is the 3rd block
+ tx.vin[0].nSequence = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG | (((m_node.chainman->ActiveChain().Tip()->GetMedianTimePast()+1-m_node.chainman->ActiveChain()[1]->GetMedianTimePast()) >> CTxIn::SEQUENCE_LOCKTIME_GRANULARITY) + 1); // txFirst[1] is the 3rd block
prevheights[0] = baseheight + 2;
hash = tx.GetHash();
m_node.mempool->addUnchecked(entry.Time(GetTime()).FromTx(tx));
- BOOST_CHECK(CheckFinalTx(::ChainActive().Tip(), CTransaction(tx), flags)); // Locktime passes
+ BOOST_CHECK(CheckFinalTx(m_node.chainman->ActiveChain().Tip(), CTransaction(tx), flags)); // Locktime passes
BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks fail
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++)
- ::ChainActive().Tip()->GetAncestor(::ChainActive().Tip()->nHeight - i)->nTime += 512; //Trick the MedianTimePast
- BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, prevheights, CreateBlockIndex(::ChainActive().Tip()->nHeight + 1))); // Sequence locks pass 512 seconds later
+ m_node.chainman->ActiveChain().Tip()->GetAncestor(m_node.chainman->ActiveChain().Tip()->nHeight - i)->nTime += 512; //Trick the MedianTimePast
+
+ {
+ CBlockIndex* active_chain_tip = m_node.chainman->ActiveChain().Tip();
+ BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, prevheights, CreateBlockIndex(active_chain_tip->nHeight + 1, active_chain_tip))); // Sequence locks pass 512 seconds later
+ }
+
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++)
- ::ChainActive().Tip()->GetAncestor(::ChainActive().Tip()->nHeight - i)->nTime -= 512; //undo tricked MTP
+ m_node.chainman->ActiveChain().Tip()->GetAncestor(m_node.chainman->ActiveChain().Tip()->nHeight - i)->nTime -= 512; //undo tricked MTP
// absolute height locked
tx.vin[0].prevout.hash = txFirst[2]->GetHash();
tx.vin[0].nSequence = CTxIn::SEQUENCE_FINAL - 1;
prevheights[0] = baseheight + 3;
- tx.nLockTime = ::ChainActive().Tip()->nHeight + 1;
+ tx.nLockTime = m_node.chainman->ActiveChain().Tip()->nHeight + 1;
hash = tx.GetHash();
m_node.mempool->addUnchecked(entry.Time(GetTime()).FromTx(tx));
- BOOST_CHECK(!CheckFinalTx(::ChainActive().Tip(), CTransaction(tx), flags)); // Locktime fails
+ BOOST_CHECK(!CheckFinalTx(m_node.chainman->ActiveChain().Tip(), CTransaction(tx), flags)); // Locktime fails
BOOST_CHECK(TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks pass
- BOOST_CHECK(IsFinalTx(CTransaction(tx), ::ChainActive().Tip()->nHeight + 2, ::ChainActive().Tip()->GetMedianTimePast())); // Locktime passes on 2nd block
+ BOOST_CHECK(IsFinalTx(CTransaction(tx), m_node.chainman->ActiveChain().Tip()->nHeight + 2, m_node.chainman->ActiveChain().Tip()->GetMedianTimePast())); // Locktime passes on 2nd block
// absolute time locked
tx.vin[0].prevout.hash = txFirst[3]->GetHash();
- tx.nLockTime = ::ChainActive().Tip()->GetMedianTimePast();
+ tx.nLockTime = m_node.chainman->ActiveChain().Tip()->GetMedianTimePast();
prevheights.resize(1);
prevheights[0] = baseheight + 4;
hash = tx.GetHash();
m_node.mempool->addUnchecked(entry.Time(GetTime()).FromTx(tx));
- BOOST_CHECK(!CheckFinalTx(::ChainActive().Tip(), CTransaction(tx), flags)); // Locktime fails
+ BOOST_CHECK(!CheckFinalTx(m_node.chainman->ActiveChain().Tip(), CTransaction(tx), flags)); // Locktime fails
BOOST_CHECK(TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks pass
- BOOST_CHECK(IsFinalTx(CTransaction(tx), ::ChainActive().Tip()->nHeight + 2, ::ChainActive().Tip()->GetMedianTimePast() + 1)); // Locktime passes 1 second later
+ BOOST_CHECK(IsFinalTx(CTransaction(tx), m_node.chainman->ActiveChain().Tip()->nHeight + 2, m_node.chainman->ActiveChain().Tip()->GetMedianTimePast() + 1)); // Locktime passes 1 second later
// mempool-dependent transactions (not added)
tx.vin[0].prevout.hash = hash;
- prevheights[0] = ::ChainActive().Tip()->nHeight + 1;
+ prevheights[0] = m_node.chainman->ActiveChain().Tip()->nHeight + 1;
tx.nLockTime = 0;
tx.vin[0].nSequence = 0;
- BOOST_CHECK(CheckFinalTx(::ChainActive().Tip(), CTransaction(tx), flags)); // Locktime passes
+ BOOST_CHECK(CheckFinalTx(m_node.chainman->ActiveChain().Tip(), CTransaction(tx), flags)); // Locktime passes
BOOST_CHECK(TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks pass
tx.vin[0].nSequence = 1;
BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks fail
@@ -502,14 +511,14 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 3U);
// However if we advance height by 1 and time by 512, all of them should be mined
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++)
- ::ChainActive().Tip()->GetAncestor(::ChainActive().Tip()->nHeight - i)->nTime += 512; //Trick the MedianTimePast
- ::ChainActive().Tip()->nHeight++;
- SetMockTime(::ChainActive().Tip()->GetMedianTimePast() + 1);
+ m_node.chainman->ActiveChain().Tip()->GetAncestor(m_node.chainman->ActiveChain().Tip()->nHeight - i)->nTime += 512; //Trick the MedianTimePast
+ m_node.chainman->ActiveChain().Tip()->nHeight++;
+ SetMockTime(m_node.chainman->ActiveChain().Tip()->GetMedianTimePast() + 1);
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 5U);
- ::ChainActive().Tip()->nHeight--;
+ m_node.chainman->ActiveChain().Tip()->nHeight--;
SetMockTime(0);
m_node.mempool->clear();
diff --git a/src/test/net_peer_eviction_tests.cpp b/src/test/net_peer_eviction_tests.cpp
index 31d391bf7d..4bfd487b86 100644
--- a/src/test/net_peer_eviction_tests.cpp
+++ b/src/test/net_peer_eviction_tests.cpp
@@ -2,7 +2,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <netaddress.h>
#include <net.h>
+#include <test/util/net.h>
#include <test/util/setup_common.h>
#include <boost/test/unit_test.hpp>
@@ -15,11 +17,6 @@
BOOST_FIXTURE_TEST_SUITE(net_peer_eviction_tests, BasicTestingSetup)
-namespace {
-constexpr int NODE_EVICTION_TEST_ROUNDS{10};
-constexpr int NODE_EVICTION_TEST_UP_TO_N_NODES{200};
-} // namespace
-
std::vector<NodeEvictionCandidate> GetRandomNodeEvictionCandidates(const int n_candidates, FastRandomContext& random_context)
{
std::vector<NodeEvictionCandidate> candidates;
@@ -36,7 +33,7 @@ std::vector<NodeEvictionCandidate> GetRandomNodeEvictionCandidates(const int n_c
/* nKeyedNetGroup */ random_context.randrange(100),
/* prefer_evict */ random_context.randbool(),
/* m_is_local */ random_context.randbool(),
- /* m_is_onion */ random_context.randbool(),
+ /* m_network */ ALL_NETWORKS[random_context.randrange(ALL_NETWORKS.size())],
});
}
return candidates;
@@ -94,7 +91,8 @@ BOOST_AUTO_TEST_CASE(peer_protection_test)
BOOST_CHECK(IsProtected(
num_peers, [](NodeEvictionCandidate& c) {
c.nTimeConnected = c.id;
- c.m_is_onion = c.m_is_local = false;
+ c.m_is_local = false;
+ c.m_network = NET_IPV4;
},
/* protected_peer_ids */ {0, 1, 2, 3, 4, 5},
/* unprotected_peer_ids */ {6, 7, 8, 9, 10, 11},
@@ -104,129 +102,359 @@ BOOST_AUTO_TEST_CASE(peer_protection_test)
BOOST_CHECK(IsProtected(
num_peers, [num_peers](NodeEvictionCandidate& c) {
c.nTimeConnected = num_peers - c.id;
- c.m_is_onion = c.m_is_local = false;
+ c.m_is_local = false;
+ c.m_network = NET_IPV6;
},
/* protected_peer_ids */ {6, 7, 8, 9, 10, 11},
/* unprotected_peer_ids */ {0, 1, 2, 3, 4, 5},
random_context));
- // Test protection of onion and localhost peers...
+ // Test protection of onion, localhost, and I2P peers...
// Expect 1/4 onion peers to be protected from eviction,
- // independently of other characteristics.
+ // if no localhost or I2P peers.
BOOST_CHECK(IsProtected(
num_peers, [](NodeEvictionCandidate& c) {
- c.m_is_onion = (c.id == 3 || c.id == 8 || c.id == 9);
+ c.m_is_local = false;
+ c.m_network = (c.id == 3 || c.id == 8 || c.id == 9) ? NET_ONION : NET_IPV4;
},
/* protected_peer_ids */ {3, 8, 9},
/* unprotected_peer_ids */ {},
random_context));
- // Expect 1/4 onion peers and 1/4 of the others to be protected
- // from eviction, sorted by longest uptime (lowest nTimeConnected).
+ // Expect 1/4 onion peers and 1/4 of the other peers to be protected,
+ // sorted by longest uptime (lowest nTimeConnected), if no localhost or I2P peers.
BOOST_CHECK(IsProtected(
num_peers, [](NodeEvictionCandidate& c) {
c.nTimeConnected = c.id;
c.m_is_local = false;
- c.m_is_onion = (c.id == 3 || c.id > 7);
+ c.m_network = (c.id == 3 || c.id > 7) ? NET_ONION : NET_IPV6;
},
/* protected_peer_ids */ {0, 1, 2, 3, 8, 9},
/* unprotected_peer_ids */ {4, 5, 6, 7, 10, 11},
random_context));
// Expect 1/4 localhost peers to be protected from eviction,
- // if no onion peers.
+ // if no onion or I2P peers.
BOOST_CHECK(IsProtected(
num_peers, [](NodeEvictionCandidate& c) {
- c.m_is_onion = false;
c.m_is_local = (c.id == 1 || c.id == 9 || c.id == 11);
+ c.m_network = NET_IPV4;
},
/* protected_peer_ids */ {1, 9, 11},
/* unprotected_peer_ids */ {},
random_context));
// Expect 1/4 localhost peers and 1/4 of the other peers to be protected,
- // sorted by longest uptime (lowest nTimeConnected), if no onion peers.
+ // sorted by longest uptime (lowest nTimeConnected), if no onion or I2P peers.
BOOST_CHECK(IsProtected(
num_peers, [](NodeEvictionCandidate& c) {
c.nTimeConnected = c.id;
- c.m_is_onion = false;
c.m_is_local = (c.id > 6);
+ c.m_network = NET_IPV6;
},
/* protected_peer_ids */ {0, 1, 2, 7, 8, 9},
/* unprotected_peer_ids */ {3, 4, 5, 6, 10, 11},
random_context));
- // Combined test: expect 1/4 onion and 2 localhost peers to be protected
- // from eviction, sorted by longest uptime.
+ // Expect 1/4 I2P peers to be protected from eviction,
+ // if no onion or localhost peers.
+ BOOST_CHECK(IsProtected(
+ num_peers, [](NodeEvictionCandidate& c) {
+ c.m_is_local = false;
+ c.m_network = (c.id == 2 || c.id == 7 || c.id == 10) ? NET_I2P : NET_IPV4;
+ },
+ /* protected_peer_ids */ {2, 7, 10},
+ /* unprotected_peer_ids */ {},
+ random_context));
+
+ // Expect 1/4 I2P peers and 1/4 of the other peers to be protected,
+ // sorted by longest uptime (lowest nTimeConnected), if no onion or localhost peers.
BOOST_CHECK(IsProtected(
num_peers, [](NodeEvictionCandidate& c) {
c.nTimeConnected = c.id;
- c.m_is_onion = (c.id == 0 || c.id == 5 || c.id == 10);
- c.m_is_local = (c.id == 1 || c.id == 9 || c.id == 11);
+ c.m_is_local = false;
+ c.m_network = (c.id == 4 || c.id > 8) ? NET_I2P : NET_IPV6;
},
- /* protected_peer_ids */ {0, 1, 2, 5, 9, 10},
- /* unprotected_peer_ids */ {3, 4, 6, 7, 8, 11},
+ /* protected_peer_ids */ {0, 1, 2, 4, 9, 10},
+ /* unprotected_peer_ids */ {3, 5, 6, 7, 8, 11},
random_context));
- // Combined test: expect having only 1 onion to allow allocating the
- // remaining 2 of the 1/4 to localhost peers, sorted by longest uptime.
+ // Tests with 2 networks...
+
+ // Combined test: expect having 1 localhost and 1 onion peer out of 4 to
+ // protect 1 localhost, 0 onion and 1 other peer, sorted by longest uptime;
+ // stable sort breaks tie with array order of localhost first.
BOOST_CHECK(IsProtected(
- num_peers + 4, [](NodeEvictionCandidate& c) {
+ 4, [](NodeEvictionCandidate& c) {
c.nTimeConnected = c.id;
- c.m_is_onion = (c.id == 15);
- c.m_is_local = (c.id > 6 && c.id < 11);
+ c.m_is_local = (c.id == 4);
+ c.m_network = (c.id == 3) ? NET_ONION : NET_IPV4;
},
- /* protected_peer_ids */ {0, 1, 2, 3, 7, 8, 9, 15},
- /* unprotected_peer_ids */ {4, 5, 6, 10, 11, 12, 13, 14},
+ /* protected_peer_ids */ {0, 4},
+ /* unprotected_peer_ids */ {1, 2},
+ random_context));
+
+ // Combined test: expect having 1 localhost and 1 onion peer out of 7 to
+ // protect 1 localhost, 0 onion, and 2 other peers (3 total), sorted by
+ // uptime; stable sort breaks tie with array order of localhost first.
+ BOOST_CHECK(IsProtected(
+ 7, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id == 6);
+ c.m_network = (c.id == 5) ? NET_ONION : NET_IPV4;
+ },
+ /* protected_peer_ids */ {0, 1, 6},
+ /* unprotected_peer_ids */ {2, 3, 4, 5},
+ random_context));
+
+ // Combined test: expect having 1 localhost and 1 onion peer out of 8 to
+ // protect protect 1 localhost, 1 onion and 2 other peers (4 total), sorted
+ // by uptime; stable sort breaks tie with array order of localhost first.
+ BOOST_CHECK(IsProtected(
+ 8, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id == 6);
+ c.m_network = (c.id == 5) ? NET_ONION : NET_IPV4;
+ },
+ /* protected_peer_ids */ {0, 1, 5, 6},
+ /* unprotected_peer_ids */ {2, 3, 4, 7},
random_context));
- // Combined test: expect 2 onions (< 1/4) to allow allocating the minimum 2
- // localhost peers, sorted by longest uptime.
+ // Combined test: expect having 3 localhost and 3 onion peers out of 12 to
+ // protect 2 localhost and 1 onion, plus 3 other peers, sorted by longest
+ // uptime; stable sort breaks ties with the array order of localhost first.
BOOST_CHECK(IsProtected(
num_peers, [](NodeEvictionCandidate& c) {
c.nTimeConnected = c.id;
- c.m_is_onion = (c.id == 7 || c.id == 9);
- c.m_is_local = (c.id == 6 || c.id == 11);
+ c.m_is_local = (c.id == 6 || c.id == 9 || c.id == 11);
+ c.m_network = (c.id == 7 || c.id == 8 || c.id == 10) ? NET_ONION : NET_IPV6;
},
- /* protected_peer_ids */ {0, 1, 6, 7, 9, 11},
- /* unprotected_peer_ids */ {2, 3, 4, 5, 8, 10},
+ /* protected_peer_ids */ {0, 1, 2, 6, 7, 9},
+ /* unprotected_peer_ids */ {3, 4, 5, 8, 10, 11},
random_context));
- // Combined test: when > 1/4, expect max 1/4 onion and 2 localhost peers
- // to be protected from eviction, sorted by longest uptime.
+ // Combined test: expect having 4 localhost and 1 onion peer out of 12 to
+ // protect 2 localhost and 1 onion, plus 3 other peers, sorted by longest uptime.
BOOST_CHECK(IsProtected(
num_peers, [](NodeEvictionCandidate& c) {
c.nTimeConnected = c.id;
- c.m_is_onion = (c.id > 3 && c.id < 8);
- c.m_is_local = (c.id > 7);
+ c.m_is_local = (c.id > 4 && c.id < 9);
+ c.m_network = (c.id == 10) ? NET_ONION : NET_IPV4;
},
- /* protected_peer_ids */ {0, 4, 5, 6, 8, 9},
- /* unprotected_peer_ids */ {1, 2, 3, 7, 10, 11},
+ /* protected_peer_ids */ {0, 1, 2, 5, 6, 10},
+ /* unprotected_peer_ids */ {3, 4, 7, 8, 9, 11},
random_context));
- // Combined test: idem > 1/4 with only 8 peers: expect 2 onion and 2
- // localhost peers (1/4 + 2) to be protected, sorted by longest uptime.
+ // Combined test: expect having 4 localhost and 2 onion peers out of 16 to
+ // protect 2 localhost and 2 onions, plus 4 other peers, sorted by longest uptime.
BOOST_CHECK(IsProtected(
- 8, [](NodeEvictionCandidate& c) {
+ 16, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id == 6 || c.id == 9 || c.id == 11 || c.id == 12);
+ c.m_network = (c.id == 8 || c.id == 10) ? NET_ONION : NET_IPV6;
+ },
+ /* protected_peer_ids */ {0, 1, 2, 3, 6, 8, 9, 10},
+ /* unprotected_peer_ids */ {4, 5, 7, 11, 12, 13, 14, 15},
+ random_context));
+
+ // Combined test: expect having 5 localhost and 1 onion peer out of 16 to
+ // protect 3 localhost (recovering the unused onion slot), 1 onion, and 4
+ // others, sorted by longest uptime.
+ BOOST_CHECK(IsProtected(
+ 16, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id > 10);
+ c.m_network = (c.id == 10) ? NET_ONION : NET_IPV4;
+ },
+ /* protected_peer_ids */ {0, 1, 2, 3, 10, 11, 12, 13},
+ /* unprotected_peer_ids */ {4, 5, 6, 7, 8, 9, 14, 15},
+ random_context));
+
+ // Combined test: expect having 1 localhost and 4 onion peers out of 16 to
+ // protect 1 localhost and 3 onions (recovering the unused localhost slot),
+ // plus 4 others, sorted by longest uptime.
+ BOOST_CHECK(IsProtected(
+ 16, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id == 15);
+ c.m_network = (c.id > 6 && c.id < 11) ? NET_ONION : NET_IPV6;
+ },
+ /* protected_peer_ids */ {0, 1, 2, 3, 7, 8, 9, 15},
+ /* unprotected_peer_ids */ {5, 6, 10, 11, 12, 13, 14},
+ random_context));
+
+ // Combined test: expect having 2 onion and 4 I2P out of 12 peers to protect
+ // 2 onion (prioritized for having fewer candidates) and 1 I2P, plus 3
+ // others, sorted by longest uptime.
+ BOOST_CHECK(IsProtected(
+ num_peers, [](NodeEvictionCandidate& c) {
c.nTimeConnected = c.id;
- c.m_is_onion = (c.id > 1 && c.id < 5);
- c.m_is_local = (c.id > 4);
+ c.m_is_local = false;
+ if (c.id == 8 || c.id == 10) {
+ c.m_network = NET_ONION;
+ } else if (c.id == 6 || c.id == 9 || c.id == 11 || c.id == 12) {
+ c.m_network = NET_I2P;
+ } else {
+ c.m_network = NET_IPV4;
+ }
},
- /* protected_peer_ids */ {2, 3, 5, 6},
- /* unprotected_peer_ids */ {0, 1, 4, 7},
+ /* protected_peer_ids */ {0, 1, 2, 6, 8, 10},
+ /* unprotected_peer_ids */ {3, 4, 5, 7, 9, 11},
random_context));
- // Combined test: idem > 1/4 with only 6 peers: expect 1 onion peer and no
- // localhost peers (1/4 + 0) to be protected, sorted by longest uptime.
+ // Tests with 3 networks...
+
+ // Combined test: expect having 1 localhost, 1 I2P and 1 onion peer out of 4
+ // to protect 1 I2P, 0 localhost, 0 onion and 1 other peer (2 total), sorted
+ // by longest uptime; stable sort breaks tie with array order of I2P first.
BOOST_CHECK(IsProtected(
- 6, [](NodeEvictionCandidate& c) {
+ 4, [](NodeEvictionCandidate& c) {
c.nTimeConnected = c.id;
- c.m_is_onion = (c.id == 4 || c.id == 5);
c.m_is_local = (c.id == 3);
+ if (c.id == 4) {
+ c.m_network = NET_I2P;
+ } else if (c.id == 2) {
+ c.m_network = NET_ONION;
+ } else {
+ c.m_network = NET_IPV6;
+ }
},
- /* protected_peer_ids */ {0, 1, 4},
- /* unprotected_peer_ids */ {2, 3, 5},
+ /* protected_peer_ids */ {0, 4},
+ /* unprotected_peer_ids */ {1, 2},
+ random_context));
+
+ // Combined test: expect having 1 localhost, 1 I2P and 1 onion peer out of 7
+ // to protect 1 I2P, 0 localhost, 0 onion and 2 other peers (3 total) sorted
+ // by longest uptime; stable sort breaks tie with array order of I2P first.
+ BOOST_CHECK(IsProtected(
+ 7, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id == 4);
+ if (c.id == 6) {
+ c.m_network = NET_I2P;
+ } else if (c.id == 5) {
+ c.m_network = NET_ONION;
+ } else {
+ c.m_network = NET_IPV6;
+ }
+ },
+ /* protected_peer_ids */ {0, 1, 6},
+ /* unprotected_peer_ids */ {2, 3, 4, 5},
+ random_context));
+
+ // Combined test: expect having 1 localhost, 1 I2P and 1 onion peer out of 8
+ // to protect 1 I2P, 1 localhost, 0 onion and 2 other peers (4 total) sorted
+ // by uptime; stable sort breaks tie with array order of I2P then localhost.
+ BOOST_CHECK(IsProtected(
+ 8, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id == 6);
+ if (c.id == 5) {
+ c.m_network = NET_I2P;
+ } else if (c.id == 4) {
+ c.m_network = NET_ONION;
+ } else {
+ c.m_network = NET_IPV6;
+ }
+ },
+ /* protected_peer_ids */ {0, 1, 5, 6},
+ /* unprotected_peer_ids */ {2, 3, 4, 7},
+ random_context));
+
+ // Combined test: expect having 4 localhost, 2 I2P, and 2 onion peers out of
+ // 16 to protect 1 localhost, 2 I2P, and 1 onion (4/16 total), plus 4 others
+ // for 8 total, sorted by longest uptime.
+ BOOST_CHECK(IsProtected(
+ 16, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id == 6 || c.id > 11);
+ if (c.id == 7 || c.id == 11) {
+ c.m_network = NET_I2P;
+ } else if (c.id == 9 || c.id == 10) {
+ c.m_network = NET_ONION;
+ } else {
+ c.m_network = NET_IPV4;
+ }
+ },
+ /* protected_peer_ids */ {0, 1, 2, 3, 6, 7, 9, 11},
+ /* unprotected_peer_ids */ {4, 5, 8, 10, 12, 13, 14, 15},
+ random_context));
+
+ // Combined test: expect having 1 localhost, 8 I2P and 1 onion peer out of
+ // 24 to protect 1, 4, and 1 (6 total), plus 6 others for 12/24 total,
+ // sorted by longest uptime.
+ BOOST_CHECK(IsProtected(
+ 24, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id == 12);
+ if (c.id > 14 && c.id < 23) { // 4 protected instead of usual 2
+ c.m_network = NET_I2P;
+ } else if (c.id == 23) {
+ c.m_network = NET_ONION;
+ } else {
+ c.m_network = NET_IPV6;
+ }
+ },
+ /* protected_peer_ids */ {0, 1, 2, 3, 4, 5, 12, 15, 16, 17, 18, 23},
+ /* unprotected_peer_ids */ {6, 7, 8, 9, 10, 11, 13, 14, 19, 20, 21, 22},
+ random_context));
+
+ // Combined test: expect having 1 localhost, 3 I2P and 6 onion peers out of
+ // 24 to protect 1, 3, and 2 (6 total, I2P has fewer candidates and so gets the
+ // unused localhost slot), plus 6 others for 12/24 total, sorted by longest uptime.
+ BOOST_CHECK(IsProtected(
+ 24, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id == 15);
+ if (c.id == 12 || c.id == 14 || c.id == 17) {
+ c.m_network = NET_I2P;
+ } else if (c.id > 17) { // 4 protected instead of usual 2
+ c.m_network = NET_ONION;
+ } else {
+ c.m_network = NET_IPV4;
+ }
+ },
+ /* protected_peer_ids */ {0, 1, 2, 3, 4, 5, 12, 14, 15, 17, 18, 19},
+ /* unprotected_peer_ids */ {6, 7, 8, 9, 10, 11, 13, 16, 20, 21, 22, 23},
+ random_context));
+
+ // Combined test: expect having 1 localhost, 7 I2P and 4 onion peers out of
+ // 24 to protect 1 localhost, 2 I2P, and 3 onions (6 total), plus 6 others
+ // for 12/24 total, sorted by longest uptime.
+ BOOST_CHECK(IsProtected(
+ 24, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id == 13);
+ if (c.id > 16) {
+ c.m_network = NET_I2P;
+ } else if (c.id == 12 || c.id == 14 || c.id == 15 || c.id == 16) {
+ c.m_network = NET_ONION;
+ } else {
+ c.m_network = NET_IPV6;
+ }
+ },
+ /* protected_peer_ids */ {0, 1, 2, 3, 4, 5, 12, 13, 14, 15, 17, 18},
+ /* unprotected_peer_ids */ {6, 7, 8, 9, 10, 11, 16, 19, 20, 21, 22, 23},
+ random_context));
+
+ // Combined test: expect having 8 localhost, 4 I2P, and 3 onion peers out of
+ // 24 to protect 2 of each (6 total), plus 6 others for 12/24 total, sorted
+ // by longest uptime.
+ BOOST_CHECK(IsProtected(
+ 24, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id > 15);
+ if (c.id > 10 && c.id < 15) {
+ c.m_network = NET_I2P;
+ } else if (c.id > 6 && c.id < 10) {
+ c.m_network = NET_ONION;
+ } else {
+ c.m_network = NET_IPV4;
+ }
+ },
+ /* protected_peer_ids */ {0, 1, 2, 3, 4, 5, 7, 8, 11, 12, 16, 17},
+ /* unprotected_peer_ids */ {6, 9, 10, 13, 14, 15, 18, 19, 20, 21, 22, 23},
random_context));
}
@@ -257,91 +485,89 @@ BOOST_AUTO_TEST_CASE(peer_eviction_test)
{
FastRandomContext random_context{true};
- for (int i = 0; i < NODE_EVICTION_TEST_ROUNDS; ++i) {
- for (int number_of_nodes = 0; number_of_nodes < NODE_EVICTION_TEST_UP_TO_N_NODES; ++number_of_nodes) {
- // Four nodes with the highest keyed netgroup values should be
- // protected from eviction.
- BOOST_CHECK(!IsEvicted(
- number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
- candidate.nKeyedNetGroup = number_of_nodes - candidate.id;
- },
- {0, 1, 2, 3}, random_context));
-
- // Eight nodes with the lowest minimum ping time should be protected
- // from eviction.
- BOOST_CHECK(!IsEvicted(
- number_of_nodes, [](NodeEvictionCandidate& candidate) {
- candidate.m_min_ping_time = std::chrono::microseconds{candidate.id};
- },
- {0, 1, 2, 3, 4, 5, 6, 7}, random_context));
-
- // Four nodes that most recently sent us novel transactions accepted
- // into our mempool should be protected from eviction.
- BOOST_CHECK(!IsEvicted(
- number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
- candidate.nLastTXTime = number_of_nodes - candidate.id;
- },
- {0, 1, 2, 3}, random_context));
-
- // Up to eight non-tx-relay peers that most recently sent us novel
- // blocks should be protected from eviction.
- BOOST_CHECK(!IsEvicted(
- number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
- candidate.nLastBlockTime = number_of_nodes - candidate.id;
- if (candidate.id <= 7) {
- candidate.fRelayTxes = false;
- candidate.fRelevantServices = true;
- }
- },
- {0, 1, 2, 3, 4, 5, 6, 7}, random_context));
-
- // Four peers that most recently sent us novel blocks should be
- // protected from eviction.
- BOOST_CHECK(!IsEvicted(
- number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
- candidate.nLastBlockTime = number_of_nodes - candidate.id;
- },
- {0, 1, 2, 3}, random_context));
-
- // Combination of the previous two tests.
- BOOST_CHECK(!IsEvicted(
- number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
- candidate.nLastBlockTime = number_of_nodes - candidate.id;
- if (candidate.id <= 7) {
- candidate.fRelayTxes = false;
- candidate.fRelevantServices = true;
- }
- },
- {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, random_context));
-
- // Combination of all tests above.
- BOOST_CHECK(!IsEvicted(
- number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
- candidate.nKeyedNetGroup = number_of_nodes - candidate.id; // 4 protected
- candidate.m_min_ping_time = std::chrono::microseconds{candidate.id}; // 8 protected
- candidate.nLastTXTime = number_of_nodes - candidate.id; // 4 protected
- candidate.nLastBlockTime = number_of_nodes - candidate.id; // 4 protected
- },
- {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}, random_context));
-
- // An eviction is expected given >= 29 random eviction candidates. The eviction logic protects at most
- // four peers by net group, eight by lowest ping time, four by last time of novel tx, up to eight non-tx-relay
- // peers by last novel block time, and four more peers by last novel block time.
- if (number_of_nodes >= 29) {
- BOOST_CHECK(SelectNodeToEvict(GetRandomNodeEvictionCandidates(number_of_nodes, random_context)));
- }
-
- // No eviction is expected given <= 20 random eviction candidates. The eviction logic protects at least
- // four peers by net group, eight by lowest ping time, four by last time of novel tx and four peers by last
- // novel block time.
- if (number_of_nodes <= 20) {
- BOOST_CHECK(!SelectNodeToEvict(GetRandomNodeEvictionCandidates(number_of_nodes, random_context)));
- }
+ for (int number_of_nodes = 0; number_of_nodes < 200; ++number_of_nodes) {
+ // Four nodes with the highest keyed netgroup values should be
+ // protected from eviction.
+ BOOST_CHECK(!IsEvicted(
+ number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
+ candidate.nKeyedNetGroup = number_of_nodes - candidate.id;
+ },
+ {0, 1, 2, 3}, random_context));
+
+ // Eight nodes with the lowest minimum ping time should be protected
+ // from eviction.
+ BOOST_CHECK(!IsEvicted(
+ number_of_nodes, [](NodeEvictionCandidate& candidate) {
+ candidate.m_min_ping_time = std::chrono::microseconds{candidate.id};
+ },
+ {0, 1, 2, 3, 4, 5, 6, 7}, random_context));
+
+ // Four nodes that most recently sent us novel transactions accepted
+ // into our mempool should be protected from eviction.
+ BOOST_CHECK(!IsEvicted(
+ number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
+ candidate.nLastTXTime = number_of_nodes - candidate.id;
+ },
+ {0, 1, 2, 3}, random_context));
+
+ // Up to eight non-tx-relay peers that most recently sent us novel
+ // blocks should be protected from eviction.
+ BOOST_CHECK(!IsEvicted(
+ number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
+ candidate.nLastBlockTime = number_of_nodes - candidate.id;
+ if (candidate.id <= 7) {
+ candidate.fRelayTxes = false;
+ candidate.fRelevantServices = true;
+ }
+ },
+ {0, 1, 2, 3, 4, 5, 6, 7}, random_context));
+
+ // Four peers that most recently sent us novel blocks should be
+ // protected from eviction.
+ BOOST_CHECK(!IsEvicted(
+ number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
+ candidate.nLastBlockTime = number_of_nodes - candidate.id;
+ },
+ {0, 1, 2, 3}, random_context));
+
+ // Combination of the previous two tests.
+ BOOST_CHECK(!IsEvicted(
+ number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
+ candidate.nLastBlockTime = number_of_nodes - candidate.id;
+ if (candidate.id <= 7) {
+ candidate.fRelayTxes = false;
+ candidate.fRelevantServices = true;
+ }
+ },
+ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, random_context));
+
+ // Combination of all tests above.
+ BOOST_CHECK(!IsEvicted(
+ number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
+ candidate.nKeyedNetGroup = number_of_nodes - candidate.id; // 4 protected
+ candidate.m_min_ping_time = std::chrono::microseconds{candidate.id}; // 8 protected
+ candidate.nLastTXTime = number_of_nodes - candidate.id; // 4 protected
+ candidate.nLastBlockTime = number_of_nodes - candidate.id; // 4 protected
+ },
+ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}, random_context));
+
+ // An eviction is expected given >= 29 random eviction candidates. The eviction logic protects at most
+ // four peers by net group, eight by lowest ping time, four by last time of novel tx, up to eight non-tx-relay
+ // peers by last novel block time, and four more peers by last novel block time.
+ if (number_of_nodes >= 29) {
+ BOOST_CHECK(SelectNodeToEvict(GetRandomNodeEvictionCandidates(number_of_nodes, random_context)));
+ }
- // Cases left to test:
- // * "If any remaining peers are preferred for eviction consider only them. [...]"
- // * "Identify the network group with the most connections and youngest member. [...]"
+ // No eviction is expected given <= 20 random eviction candidates. The eviction logic protects at least
+ // four peers by net group, eight by lowest ping time, four by last time of novel tx and four peers by last
+ // novel block time.
+ if (number_of_nodes <= 20) {
+ BOOST_CHECK(!SelectNodeToEvict(GetRandomNodeEvictionCandidates(number_of_nodes, random_context)));
}
+
+ // Cases left to test:
+ // * "If any remaining peers are preferred for eviction consider only them. [...]"
+ // * "Identify the network group with the most connections and youngest member. [...]"
}
}
diff --git a/src/test/txindex_tests.cpp b/src/test/txindex_tests.cpp
index d47c54fd6e..3ce7ecb5f2 100644
--- a/src/test/txindex_tests.cpp
+++ b/src/test/txindex_tests.cpp
@@ -28,7 +28,7 @@ BOOST_FIXTURE_TEST_CASE(txindex_initial_sync, TestChain100Setup)
// BlockUntilSyncedToCurrentChain should return false before txindex is started.
BOOST_CHECK(!txindex.BlockUntilSyncedToCurrentChain());
- BOOST_REQUIRE(txindex.Start(::ChainstateActive()));
+ BOOST_REQUIRE(txindex.Start(m_node.chainman->ActiveChainstate()));
// Allow tx index to catch up with the block index.
constexpr int64_t timeout_ms = 10 * 1000;
diff --git a/src/test/txvalidation_tests.cpp b/src/test/txvalidation_tests.cpp
index 95ad85d0f8..ade9e210f2 100644
--- a/src/test/txvalidation_tests.cpp
+++ b/src/test/txvalidation_tests.cpp
@@ -37,7 +37,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_reject_coinbase, TestChain100Setup)
LOCK(cs_main);
unsigned int initialPoolSize = m_node.mempool->size();
- const MempoolAcceptResult result = AcceptToMemoryPool(::ChainstateActive(), *m_node.mempool, MakeTransactionRef(coinbaseTx),
+ const MempoolAcceptResult result = AcceptToMemoryPool(m_node.chainman->ActiveChainstate(), *m_node.mempool, MakeTransactionRef(coinbaseTx),
true /* bypass_limits */);
BOOST_CHECK(result.m_result_type == MempoolAcceptResult::ResultType::INVALID);
diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp
index 3244b58082..23195c0a26 100644
--- a/src/test/txvalidationcache_tests.cpp
+++ b/src/test/txvalidationcache_tests.cpp
@@ -31,7 +31,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
const auto ToMemPool = [this](const CMutableTransaction& tx) {
LOCK(cs_main);
- const MempoolAcceptResult result = AcceptToMemoryPool(::ChainstateActive(), *m_node.mempool, MakeTransactionRef(tx),
+ const MempoolAcceptResult result = AcceptToMemoryPool(m_node.chainman->ActiveChainstate(), *m_node.mempool, MakeTransactionRef(tx),
true /* bypass_limits */);
return result.m_result_type == MempoolAcceptResult::ResultType::VALID;
};
@@ -63,7 +63,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
block = CreateAndProcessBlock(spends, scriptPubKey);
{
LOCK(cs_main);
- BOOST_CHECK(::ChainActive().Tip()->GetBlockHash() != block.GetHash());
+ BOOST_CHECK(m_node.chainman->ActiveChain().Tip()->GetBlockHash() != block.GetHash());
}
// Test 2: ... and should be rejected if spend1 is in the memory pool
@@ -71,7 +71,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
block = CreateAndProcessBlock(spends, scriptPubKey);
{
LOCK(cs_main);
- BOOST_CHECK(::ChainActive().Tip()->GetBlockHash() != block.GetHash());
+ BOOST_CHECK(m_node.chainman->ActiveChain().Tip()->GetBlockHash() != block.GetHash());
}
m_node.mempool->clear();
@@ -80,7 +80,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
block = CreateAndProcessBlock(spends, scriptPubKey);
{
LOCK(cs_main);
- BOOST_CHECK(::ChainActive().Tip()->GetBlockHash() != block.GetHash());
+ BOOST_CHECK(m_node.chainman->ActiveChain().Tip()->GetBlockHash() != block.GetHash());
}
m_node.mempool->clear();
@@ -91,7 +91,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
block = CreateAndProcessBlock(oneSpend, scriptPubKey);
{
LOCK(cs_main);
- BOOST_CHECK(::ChainActive().Tip()->GetBlockHash() == block.GetHash());
+ BOOST_CHECK(m_node.chainman->ActiveChain().Tip()->GetBlockHash() == block.GetHash());
}
// spends[1] should have been removed from the mempool when the
// block with spends[0] is accepted:
@@ -109,7 +109,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
// should fail.
// Capture this interaction with the upgraded_nop argument: set it when evaluating
// any script flag that is implemented as an upgraded NOP code.
-static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t failing_flags, bool add_to_cache) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t failing_flags, bool add_to_cache, CCoinsViewCache& active_coins_tip) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
PrecomputedTransactionData txdata;
// If we add many more flags, this loop can get too expensive, but we can
@@ -126,7 +126,7 @@ static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t fail
// WITNESS requires P2SH
test_flags |= SCRIPT_VERIFY_P2SH;
}
- bool ret = CheckInputScripts(tx, state, &::ChainstateActive().CoinsTip(), test_flags, true, add_to_cache, txdata, nullptr);
+ bool ret = CheckInputScripts(tx, state, &active_coins_tip, test_flags, true, add_to_cache, txdata, nullptr);
// CheckInputScripts should succeed iff test_flags doesn't intersect with
// failing_flags
bool expected_return_value = !(test_flags & failing_flags);
@@ -136,13 +136,13 @@ static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t fail
if (ret && add_to_cache) {
// Check that we get a cache hit if the tx was valid
std::vector<CScriptCheck> scriptchecks;
- BOOST_CHECK(CheckInputScripts(tx, state, &::ChainstateActive().CoinsTip(), test_flags, true, add_to_cache, txdata, &scriptchecks));
+ BOOST_CHECK(CheckInputScripts(tx, state, &active_coins_tip, test_flags, true, add_to_cache, txdata, &scriptchecks));
BOOST_CHECK(scriptchecks.empty());
} else {
// Check that we get script executions to check, if the transaction
// was invalid, or we didn't add to cache.
std::vector<CScriptCheck> scriptchecks;
- BOOST_CHECK(CheckInputScripts(tx, state, &::ChainstateActive().CoinsTip(), test_flags, true, add_to_cache, txdata, &scriptchecks));
+ BOOST_CHECK(CheckInputScripts(tx, state, &active_coins_tip, test_flags, true, add_to_cache, txdata, &scriptchecks));
BOOST_CHECK_EQUAL(scriptchecks.size(), tx.vin.size());
}
}
@@ -205,20 +205,20 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
TxValidationState state;
PrecomputedTransactionData ptd_spend_tx;
- BOOST_CHECK(!CheckInputScripts(CTransaction(spend_tx), state, &::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, nullptr));
+ BOOST_CHECK(!CheckInputScripts(CTransaction(spend_tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, nullptr));
// If we call again asking for scriptchecks (as happens in
// ConnectBlock), we should add a script check object for this -- we're
// not caching invalidity (if that changes, delete this test case).
std::vector<CScriptCheck> scriptchecks;
- BOOST_CHECK(CheckInputScripts(CTransaction(spend_tx), state, &::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, &scriptchecks));
+ BOOST_CHECK(CheckInputScripts(CTransaction(spend_tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, &scriptchecks));
BOOST_CHECK_EQUAL(scriptchecks.size(), 1U);
// Test that CheckInputScripts returns true iff DERSIG-enforcing flags are
// not present. Don't add these checks to the cache, so that we can
// test later that block validation works fine in the absence of cached
// successes.
- ValidateCheckInputsForAllFlags(CTransaction(spend_tx), SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_STRICTENC, false);
+ ValidateCheckInputsForAllFlags(CTransaction(spend_tx), SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_STRICTENC, false, m_node.chainman->ActiveChainstate().CoinsTip());
}
// And if we produce a block with this tx, it should be valid (DERSIG not
@@ -227,8 +227,8 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
block = CreateAndProcessBlock({spend_tx}, p2pk_scriptPubKey);
LOCK(cs_main);
- BOOST_CHECK(::ChainActive().Tip()->GetBlockHash() == block.GetHash());
- BOOST_CHECK(::ChainstateActive().CoinsTip().GetBestBlock() == block.GetHash());
+ BOOST_CHECK(m_node.chainman->ActiveChain().Tip()->GetBlockHash() == block.GetHash());
+ BOOST_CHECK(m_node.chainman->ActiveChainstate().CoinsTip().GetBestBlock() == block.GetHash());
// Test P2SH: construct a transaction that is valid without P2SH, and
// then test validity with P2SH.
@@ -244,7 +244,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
std::vector<unsigned char> vchSig2(p2pk_scriptPubKey.begin(), p2pk_scriptPubKey.end());
invalid_under_p2sh_tx.vin[0].scriptSig << vchSig2;
- ValidateCheckInputsForAllFlags(CTransaction(invalid_under_p2sh_tx), SCRIPT_VERIFY_P2SH, true);
+ ValidateCheckInputsForAllFlags(CTransaction(invalid_under_p2sh_tx), SCRIPT_VERIFY_P2SH, true, m_node.chainman->ActiveChainstate().CoinsTip());
}
// Test CHECKLOCKTIMEVERIFY
@@ -267,13 +267,13 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
vchSig.push_back((unsigned char)SIGHASH_ALL);
invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 101;
- ValidateCheckInputsForAllFlags(CTransaction(invalid_with_cltv_tx), SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true);
+ ValidateCheckInputsForAllFlags(CTransaction(invalid_with_cltv_tx), SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, m_node.chainman->ActiveChainstate().CoinsTip());
// Make it valid, and check again
invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 100;
TxValidationState state;
PrecomputedTransactionData txdata;
- BOOST_CHECK(CheckInputScripts(CTransaction(invalid_with_cltv_tx), state, ::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, true, txdata, nullptr));
+ BOOST_CHECK(CheckInputScripts(CTransaction(invalid_with_cltv_tx), state, m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, true, txdata, nullptr));
}
// TEST CHECKSEQUENCEVERIFY
@@ -295,13 +295,13 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
vchSig.push_back((unsigned char)SIGHASH_ALL);
invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 101;
- ValidateCheckInputsForAllFlags(CTransaction(invalid_with_csv_tx), SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true);
+ ValidateCheckInputsForAllFlags(CTransaction(invalid_with_csv_tx), SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, m_node.chainman->ActiveChainstate().CoinsTip());
// Make it valid, and check again
invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 100;
TxValidationState state;
PrecomputedTransactionData txdata;
- BOOST_CHECK(CheckInputScripts(CTransaction(invalid_with_csv_tx), state, &::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true, txdata, nullptr));
+ BOOST_CHECK(CheckInputScripts(CTransaction(invalid_with_csv_tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true, txdata, nullptr));
}
// TODO: add tests for remaining script flags
@@ -324,11 +324,11 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
UpdateInput(valid_with_witness_tx.vin[0], sigdata);
// This should be valid under all script flags.
- ValidateCheckInputsForAllFlags(CTransaction(valid_with_witness_tx), 0, true);
+ ValidateCheckInputsForAllFlags(CTransaction(valid_with_witness_tx), 0, true, m_node.chainman->ActiveChainstate().CoinsTip());
// Remove the witness, and check that it is now invalid.
valid_with_witness_tx.vin[0].scriptWitness.SetNull();
- ValidateCheckInputsForAllFlags(CTransaction(valid_with_witness_tx), SCRIPT_VERIFY_WITNESS, true);
+ ValidateCheckInputsForAllFlags(CTransaction(valid_with_witness_tx), SCRIPT_VERIFY_WITNESS, true, m_node.chainman->ActiveChainstate().CoinsTip());
}
{
@@ -353,7 +353,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
}
// This should be valid under all script flags
- ValidateCheckInputsForAllFlags(CTransaction(tx), 0, true);
+ ValidateCheckInputsForAllFlags(CTransaction(tx), 0, true, m_node.chainman->ActiveChainstate().CoinsTip());
// Check that if the second input is invalid, but the first input is
// valid, the transaction is not cached.
@@ -363,12 +363,12 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
TxValidationState state;
PrecomputedTransactionData txdata;
// This transaction is now invalid under segwit, because of the second input.
- BOOST_CHECK(!CheckInputScripts(CTransaction(tx), state, &::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, nullptr));
+ BOOST_CHECK(!CheckInputScripts(CTransaction(tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, nullptr));
std::vector<CScriptCheck> scriptchecks;
// Make sure this transaction was not cached (ie because the first
// input was valid)
- BOOST_CHECK(CheckInputScripts(CTransaction(tx), state, &::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, &scriptchecks));
+ BOOST_CHECK(CheckInputScripts(CTransaction(tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, &scriptchecks));
// Should get 2 script checks back -- caching is on a whole-transaction basis.
BOOST_CHECK_EQUAL(scriptchecks.size(), 2U);
}
diff --git a/src/test/util/mining.cpp b/src/test/util/mining.cpp
index 1204873828..f6a11bc02e 100644
--- a/src/test/util/mining.cpp
+++ b/src/test/util/mining.cpp
@@ -74,12 +74,12 @@ CTxIn MineBlock(const NodeContext& node, const CScript& coinbase_scriptPubKey)
std::shared_ptr<CBlock> PrepareBlock(const NodeContext& node, const CScript& coinbase_scriptPubKey)
{
auto block = std::make_shared<CBlock>(
- BlockAssembler{::ChainstateActive(), *Assert(node.mempool), Params()}
+ BlockAssembler{Assert(node.chainman)->ActiveChainstate(), *Assert(node.mempool), Params()}
.CreateNewBlock(coinbase_scriptPubKey)
->block);
LOCK(cs_main);
- block->nTime = ::ChainActive().Tip()->GetMedianTimePast() + 1;
+ block->nTime = Assert(node.chainman)->ActiveChain().Tip()->GetMedianTimePast() + 1;
block->hashMerkleRoot = BlockMerkleRoot(*block);
return block;
diff --git a/src/test/util/net.h b/src/test/util/net.h
index 71685d437a..1b49a671bd 100644
--- a/src/test/util/net.h
+++ b/src/test/util/net.h
@@ -6,9 +6,11 @@
#define BITCOIN_TEST_UTIL_NET_H
#include <compat.h>
+#include <netaddress.h>
#include <net.h>
#include <util/sock.h>
+#include <array>
#include <cassert>
#include <cstring>
#include <string>
@@ -67,6 +69,16 @@ constexpr ConnectionType ALL_CONNECTION_TYPES[]{
ConnectionType::ADDR_FETCH,
};
+constexpr auto ALL_NETWORKS = std::array{
+ Network::NET_UNROUTABLE,
+ Network::NET_IPV4,
+ Network::NET_IPV6,
+ Network::NET_ONION,
+ Network::NET_I2P,
+ Network::NET_CJDNS,
+ Network::NET_INTERNAL,
+};
+
/**
* A mocked Sock alternative that returns a statically contained data upon read and succeeds
* and ignores all writes. The data to be returned is given to the constructor and when it is
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index 863c3ab565..e105e85e47 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -76,6 +76,7 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName, const std::ve
: m_path_root{fs::temp_directory_path() / "test_common_" PACKAGE_NAME / g_insecure_rand_ctx_temp_path.rand256().ToString()},
m_args{}
{
+ m_node.args = &gArgs;
const std::vector<const char*> arguments = Cat(
{
"dummy",
@@ -94,7 +95,7 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName, const std::ve
gArgs.ForceSetArg("-datadir", m_path_root.string());
gArgs.ClearPathCache();
{
- SetupServerArgs(m_node);
+ SetupServerArgs(*m_node.args);
std::string error;
const bool success{m_node.args->ParseParameters(arguments.size(), arguments.data(), error)};
assert(success);
@@ -145,7 +146,7 @@ ChainTestingSetup::ChainTestingSetup(const std::string& chainName, const std::ve
m_node.fee_estimator = std::make_unique<CBlockPolicyEstimator>();
m_node.mempool = std::make_unique<CTxMemPool>(m_node.fee_estimator.get(), 1);
- m_node.chainman = &::g_chainman;
+ m_node.chainman = std::make_unique<ChainstateManager>();
// Start script-checking threads. Set g_parallel_script_checks to true so they are used.
constexpr int script_check_threads = 2;
@@ -167,7 +168,7 @@ ChainTestingSetup::~ChainTestingSetup()
m_node.mempool.reset();
m_node.scheduler.reset();
m_node.chainman->Reset();
- m_node.chainman = nullptr;
+ m_node.chainman.reset();
pblocktree.reset();
}
@@ -180,17 +181,17 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const
RegisterAllCoreRPCCommands(tableRPC);
m_node.chainman->InitializeChainstate(*m_node.mempool);
- ::ChainstateActive().InitCoinsDB(
+ m_node.chainman->ActiveChainstate().InitCoinsDB(
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
- assert(!::ChainstateActive().CanFlushToDisk());
- ::ChainstateActive().InitCoinsCache(1 << 23);
- assert(::ChainstateActive().CanFlushToDisk());
- if (!::ChainstateActive().LoadGenesisBlock(chainparams)) {
+ assert(!m_node.chainman->ActiveChainstate().CanFlushToDisk());
+ m_node.chainman->ActiveChainstate().InitCoinsCache(1 << 23);
+ assert(m_node.chainman->ActiveChainstate().CanFlushToDisk());
+ if (!m_node.chainman->ActiveChainstate().LoadGenesisBlock(chainparams)) {
throw std::runtime_error("LoadGenesisBlock failed.");
}
BlockValidationState state;
- if (!::ChainstateActive().ActivateBestChain(state, chainparams)) {
+ if (!m_node.chainman->ActiveChainstate().ActivateBestChain(state, chainparams)) {
throw std::runtime_error(strprintf("ActivateBestChain failed. (%s)", state.ToString()));
}
@@ -240,7 +241,7 @@ CBlock TestChain100Setup::CreateAndProcessBlock(const std::vector<CMutableTransa
{
const CChainParams& chainparams = Params();
CTxMemPool empty_pool;
- CBlock block = BlockAssembler(::ChainstateActive(), empty_pool, chainparams).CreateNewBlock(scriptPubKey)->block;
+ CBlock block = BlockAssembler(m_node.chainman->ActiveChainstate(), empty_pool, chainparams).CreateNewBlock(scriptPubKey)->block;
Assert(block.vtx.size() == 1);
for (const CMutableTransaction& tx : txns) {
@@ -299,7 +300,7 @@ CMutableTransaction TestChain100Setup::CreateValidMempoolTransaction(CTransactio
// If submit=true, add transaction to the mempool.
if (submit) {
LOCK(cs_main);
- const MempoolAcceptResult result = AcceptToMemoryPool(::ChainstateActive(), *m_node.mempool.get(), MakeTransactionRef(mempool_txn), /* bypass_limits */ false);
+ const MempoolAcceptResult result = AcceptToMemoryPool(m_node.chainman->ActiveChainstate(), *m_node.mempool.get(), MakeTransactionRef(mempool_txn), /* bypass_limits */ false);
assert(result.m_result_type == MempoolAcceptResult::ResultType::VALID);
}
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index d463bcdd8e..7ce38519cf 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -329,6 +329,25 @@ BOOST_FIXTURE_TEST_CASE(util_CheckValue, CheckValueTest)
CheckValue(M::ALLOW_ANY, "-value=abc", Expect{"abc"}.String("abc").Int(0).Bool(false).List({"abc"}));
}
+struct NoIncludeConfTest {
+ std::string Parse(const char* arg)
+ {
+ TestArgsManager test;
+ test.SetupArgs({{"-includeconf", ArgsManager::ALLOW_ANY}});
+ std::array argv{"ignored", arg};
+ std::string error;
+ (void)test.ParseParameters(argv.size(), argv.data(), error);
+ return error;
+ }
+};
+
+BOOST_FIXTURE_TEST_CASE(util_NoIncludeConf, NoIncludeConfTest)
+{
+ BOOST_CHECK_EQUAL(Parse("-noincludeconf"), "");
+ BOOST_CHECK_EQUAL(Parse("-includeconf"), "-includeconf cannot be used from commandline; -includeconf=\"\"");
+ BOOST_CHECK_EQUAL(Parse("-includeconf=file"), "-includeconf cannot be used from commandline; -includeconf=\"file\"");
+}
+
BOOST_AUTO_TEST_CASE(util_ParseParameters)
{
TestArgsManager testArgs;
diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp
index 552be0a2da..e0bc10d660 100644
--- a/src/test/validation_block_tests.cpp
+++ b/src/test/validation_block_tests.cpp
@@ -84,8 +84,8 @@ std::shared_ptr<CBlock> MinerTestingSetup::Block(const uint256& prev_hash)
std::shared_ptr<CBlock> MinerTestingSetup::FinalizeBlock(std::shared_ptr<CBlock> pblock)
{
- LOCK(cs_main); // For g_chainman.m_blockman.LookupBlockIndex
- GenerateCoinbaseCommitment(*pblock, g_chainman.m_blockman.LookupBlockIndex(pblock->hashPrevBlock), Params().GetConsensus());
+ LOCK(cs_main); // For m_node.chainman->m_blockman.LookupBlockIndex
+ GenerateCoinbaseCommitment(*pblock, m_node.chainman->m_blockman.LookupBlockIndex(pblock->hashPrevBlock), Params().GetConsensus());
pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
@@ -162,7 +162,7 @@ BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering)
const CBlockIndex* initial_tip = nullptr;
{
LOCK(cs_main);
- initial_tip = ::ChainActive().Tip();
+ initial_tip = m_node.chainman->ActiveChain().Tip();
}
auto sub = std::make_shared<TestSubscriber>(initial_tip->GetBlockHash());
RegisterSharedValidationInterface(sub);
@@ -198,7 +198,7 @@ BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering)
UnregisterSharedValidationInterface(sub);
LOCK(cs_main);
- BOOST_CHECK_EQUAL(sub->m_expected_tip, ::ChainActive().Tip()->GetBlockHash());
+ BOOST_CHECK_EQUAL(sub->m_expected_tip, m_node.chainman->ActiveChain().Tip()->GetBlockHash());
}
/**
@@ -232,7 +232,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
// Run the test multiple times
for (int test_runs = 3; test_runs > 0; --test_runs) {
- BOOST_CHECK_EQUAL(last_mined->GetHash(), ::ChainActive().Tip()->GetBlockHash());
+ BOOST_CHECK_EQUAL(last_mined->GetHash(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
// Later on split from here
const uint256 split_hash{last_mined->hashPrevBlock};
@@ -273,7 +273,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
{
LOCK(cs_main);
for (const auto& tx : txs) {
- const MempoolAcceptResult result = AcceptToMemoryPool(::ChainstateActive(), *m_node.mempool, tx, false /* bypass_limits */);
+ const MempoolAcceptResult result = AcceptToMemoryPool(m_node.chainman->ActiveChainstate(), *m_node.mempool, tx, false /* bypass_limits */);
BOOST_REQUIRE(result.m_result_type == MempoolAcceptResult::ResultType::VALID);
}
}
@@ -306,7 +306,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
}
LOCK(cs_main);
// We are done with the reorg, so the tip must have changed
- assert(tip_init != ::ChainActive().Tip()->GetBlockHash());
+ assert(tip_init != m_node.chainman->ActiveChain().Tip()->GetBlockHash());
}};
// Submit the reorg in this thread to invalidate and remove the txs from the tx pool
@@ -314,7 +314,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
ProcessBlock(b);
}
// Check that the reorg was eventually successful
- BOOST_CHECK_EQUAL(last_mined->GetHash(), ::ChainActive().Tip()->GetBlockHash());
+ BOOST_CHECK_EQUAL(last_mined->GetHash(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
// We can join the other thread, which returns when the reorg was successful
rpc_thread.join();
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 4413da7ea7..c5a4bbf1b0 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -513,11 +513,10 @@ void CTxMemPool::removeForReorg(CChainState& active_chainstate, int flags)
for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
const CTransaction& tx = it->GetTx();
LockPoints lp = it->GetLockPoints();
- assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
bool validLP = TestLockPointValidity(active_chainstate.m_chain, &lp);
- CCoinsViewMemPool viewMempool(&active_chainstate.CoinsTip(), *this);
+ CCoinsViewMemPool view_mempool(&active_chainstate.CoinsTip(), *this);
if (!CheckFinalTx(active_chainstate.m_chain.Tip(), tx, flags)
- || !CheckSequenceLocks(active_chainstate.m_chain.Tip(), viewMempool, tx, flags, &lp, validLP)) {
+ || !CheckSequenceLocks(active_chainstate.m_chain.Tip(), view_mempool, tx, flags, &lp, validLP)) {
// Note if CheckSequenceLocks fails the LockPoints may still be invalid
// So it's critical that we remove the tx and not depend on the LockPoints.
txToRemove.insert(it);
@@ -638,10 +637,8 @@ void CTxMemPool::check(CChainState& active_chainstate) const
uint64_t innerUsage = 0;
CCoinsViewCache& active_coins_tip = active_chainstate.CoinsTip();
- assert(std::addressof(::ChainstateActive().CoinsTip()) == std::addressof(active_coins_tip)); // TODO: REVIEW-ONLY, REMOVE IN FUTURE COMMIT
CCoinsViewCache mempoolDuplicate(const_cast<CCoinsViewCache*>(&active_coins_tip));
const int64_t spendheight = active_chainstate.m_chain.Height() + 1;
- assert(g_chainman.m_blockman.GetSpendHeight(mempoolDuplicate) == spendheight); // TODO: REVIEW-ONLY, REMOVE IN FUTURE COMMIT
std::list<const CTxMemPoolEntry*> waitingOnDependants;
for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
diff --git a/src/txmempool.h b/src/txmempool.h
index 46b89049bb..ae4b16d377 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -874,7 +874,8 @@ protected:
public:
CCoinsViewMemPool(CCoinsView* baseIn, const CTxMemPool& mempoolIn);
bool GetCoin(const COutPoint &outpoint, Coin &coin) const override;
- /** Add the coins created by this transaction. */
+ /** Add the coins created by this transaction. These coins are only temporarily stored in
+ * m_temp_added and cannot be flushed to the back end. Only used for package validation. */
void PackageAddTransaction(const CTransactionRef& tx);
};
diff --git a/src/util/system.cpp b/src/util/system.cpp
index 5b87806a45..5ea0139275 100644
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -365,11 +365,14 @@ bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::strin
m_settings.command_line_options[key].push_back(value);
}
- // we do not allow -includeconf from command line
+ // we do not allow -includeconf from command line, only -noincludeconf
if (auto* includes = util::FindKey(m_settings.command_line_options, "includeconf")) {
- const auto& include{*util::SettingsSpan(*includes).begin()}; // pick first value as example
- error = "-includeconf cannot be used from commandline; -includeconf=" + include.write();
- return false;
+ const util::SettingsSpan values{*includes};
+ // Range may be empty if -noincludeconf was passed
+ if (!values.empty()) {
+ error = "-includeconf cannot be used from commandline; -includeconf=" + values.begin()->write();
+ return false; // pick first value as example
+ }
}
return true;
}
@@ -1240,9 +1243,9 @@ void runCommand(const std::string& strCommand)
}
#endif
-#ifdef ENABLE_EXTERNAL_SIGNER
UniValue RunCommandParseJSON(const std::string& str_command, const std::string& str_std_in)
{
+#ifdef ENABLE_EXTERNAL_SIGNER
namespace bp = boost::process;
UniValue result_json;
@@ -1274,8 +1277,10 @@ UniValue RunCommandParseJSON(const std::string& str_command, const std::string&
if (!result_json.read(result)) throw std::runtime_error("Unable to parse JSON: " + result);
return result_json;
-}
+#else
+ throw std::runtime_error("Compiled without external signing support (required for external signing).");
#endif // ENABLE_EXTERNAL_SIGNER
+}
void SetupEnvironment()
{
diff --git a/src/util/system.h b/src/util/system.h
index c4317c62d0..ea9870a343 100644
--- a/src/util/system.h
+++ b/src/util/system.h
@@ -102,7 +102,6 @@ std::string ShellEscape(const std::string& arg);
#if HAVE_SYSTEM
void runCommand(const std::string& strCommand);
#endif
-#ifdef ENABLE_EXTERNAL_SIGNER
/**
* Execute a command which returns JSON, and parse the result.
*
@@ -111,7 +110,6 @@ void runCommand(const std::string& strCommand);
* @return parsed JSON
*/
UniValue RunCommandParseJSON(const std::string& str_command, const std::string& str_std_in="");
-#endif // ENABLE_EXTERNAL_SIGNER
/**
* Most paths passed as configuration arguments are treated as relative to
diff --git a/src/validation.cpp b/src/validation.cpp
index 4c861599fd..b48e49a10b 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -101,21 +101,6 @@ bool CBlockIndexWorkComparator::operator()(const CBlockIndex *pa, const CBlockIn
return false;
}
-ChainstateManager g_chainman;
-
-CChainState& ChainstateActive()
-{
- LOCK(::cs_main);
- assert(g_chainman.m_active_chainstate);
- return *g_chainman.m_active_chainstate;
-}
-
-CChain& ChainActive()
-{
- LOCK(::cs_main);
- return ::ChainstateActive().m_chain;
-}
-
/**
* Mutex to guard access to validation specific variables, such as reading
* or changing the chainstate.
@@ -161,7 +146,6 @@ void FlushBlockFile(bool fFinalize = false, bool finalize_undo = false);
CBlockIndex* BlockManager::LookupBlockIndex(const uint256& hash) const
{
AssertLockHeld(cs_main);
- assert(std::addressof(g_chainman.BlockIndex()) == std::addressof(m_block_index));
BlockMap::const_iterator it = m_block_index.find(hash);
return it == m_block_index.end() ? nullptr : it->second;
}
@@ -170,7 +154,6 @@ CBlockIndex* BlockManager::FindForkInGlobalIndex(const CChain& chain, const CBlo
{
AssertLockHeld(cs_main);
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(*this));
// Find the latest block common to locator and chain - we expect that
// locator.vHave is sorted descending by height.
for (const uint256& hash : locator.vHave) {
@@ -198,7 +181,6 @@ bool CheckFinalTx(const CBlockIndex* active_chain_tip, const CTransaction &tx, i
{
AssertLockHeld(cs_main);
assert(active_chain_tip); // TODO: Make active_chain_tip a reference
- assert(std::addressof(*::ChainActive().Tip()) == std::addressof(*active_chain_tip));
// By convention a negative value for flags indicates that the
// current network-enforced consensus rules should be used. In
@@ -237,7 +219,6 @@ bool TestLockPointValidity(CChain& active_chain, const LockPoints* lp)
if (lp->maxInputBlock) {
// Check whether ::ChainActive() is an extension of the block at which the LockPoints
// calculation was valid. If not LockPoints are no longer valid
- assert(std::addressof(::ChainActive()) == std::addressof(active_chain));
if (!active_chain.Contains(lp->maxInputBlock)) {
return false;
}
@@ -331,7 +312,6 @@ static void LimitMempoolSize(CTxMemPool& pool, CCoinsViewCache& coins_cache, siz
std::vector<COutPoint> vNoSpendsRemaining;
pool.TrimToSize(limit, &vNoSpendsRemaining);
- assert(std::addressof(::ChainstateActive().CoinsTip()) == std::addressof(coins_cache));
for (const COutPoint& removed : vNoSpendsRemaining)
coins_cache.Uncache(removed);
}
@@ -339,7 +319,6 @@ static void LimitMempoolSize(CTxMemPool& pool, CCoinsViewCache& coins_cache, siz
static bool IsCurrentForFeeEstimation(CChainState& active_chainstate) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
AssertLockHeld(cs_main);
- assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
if (active_chainstate.IsInitialBlockDownload())
return false;
if (active_chainstate.m_chain.Tip()->GetBlockTime() < count_seconds(GetTime<std::chrono::seconds>() - MAX_FEE_ESTIMATION_TIP_AGE))
@@ -366,7 +345,6 @@ static void UpdateMempoolForReorg(CChainState& active_chainstate, CTxMemPool& me
{
AssertLockHeld(cs_main);
AssertLockHeld(mempool.cs);
- assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
std::vector<uint256> vHashUpdate;
// disconnectpool's insertion_order index sorts the entries from
// oldest to newest, but the oldest entry will be the last tx from the
@@ -433,7 +411,6 @@ static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, TxValidationS
assert(txFrom->vout.size() > txin.prevout.n);
assert(txFrom->vout[txin.prevout.n] == coin.out);
} else {
- assert(std::addressof(::ChainstateActive().CoinsTip()) == std::addressof(coins_tip));
const Coin& coinFromUTXOSet = coins_tip.AccessCoin(txin.prevout);
assert(!coinFromUTXOSet.IsSpent());
assert(coinFromUTXOSet.out == coin.out);
@@ -454,7 +431,6 @@ public:
m_limit_ancestor_size(gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT)*1000),
m_limit_descendants(gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT)),
m_limit_descendant_size(gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT)*1000) {
- assert(std::addressof(::ChainstateActive()) == std::addressof(m_active_chainstate));
}
// We put the arguments we're handed into a struct, so we can pass them
@@ -472,8 +448,10 @@ public:
*/
std::vector<COutPoint>& m_coins_to_uncache;
const bool m_test_accept;
- /** Disable BIP125 RBFing; disallow all conflicts with mempool transactions. */
- const bool disallow_mempool_conflicts;
+ /** Whether we allow transactions to replace mempool transactions by BIP125 rules. If false,
+ * any transaction spending the same inputs as a transaction in the mempool is considered
+ * a conflict. */
+ const bool m_allow_bip125_replacement{true};
};
// Single transaction acceptance
@@ -482,7 +460,7 @@ public:
/**
* Multiple transaction acceptance. Transactions may or may not be interdependent,
* but must not conflict with each other. Parents must come before children if any
- * dependencies exist, otherwise a TX_MISSING_INPUTS error will be returned.
+ * dependencies exist.
*/
PackageMempoolAcceptResult AcceptMultipleTransactions(const std::vector<CTransactionRef>& txns, ATMPArgs& args) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@@ -605,7 +583,6 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
// Only accept nLockTime-using transactions that can be mined in the next
// block; we don't want our mempool filled up with transactions that can't
// be mined yet.
- assert(std::addressof(::ChainActive()) == std::addressof(m_active_chainstate.m_chain));
if (!CheckFinalTx(m_active_chainstate.m_chain.Tip(), tx, STANDARD_LOCKTIME_VERIFY_FLAGS))
return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "non-final");
@@ -619,6 +596,10 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
{
const CTransaction* ptxConflicting = m_pool.GetConflictTx(txin.prevout);
if (ptxConflicting) {
+ if (!args.m_allow_bip125_replacement) {
+ // Transaction conflicts with a mempool tx, but we're not allowing replacements.
+ return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "bip125-replacement-disallowed");
+ }
if (!setConflicts.count(ptxConflicting->GetHash()))
{
// Allow opt-out of transaction replacement by setting
@@ -629,10 +610,13 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
// is for the sake of multi-party protocols, where we don't
// want a single party to be able to disable replacement.
//
- // The opt-out ignores descendants as anyone relying on
- // first-seen mempool behavior should be checking all
- // unconfirmed ancestors anyway; doing otherwise is hopelessly
- // insecure.
+ // Transactions that don't explicitly signal replaceability are
+ // *not* replaceable with the current logic, even if one of their
+ // unconfirmed ancestors signals replaceability. This diverges
+ // from BIP125's inherited signaling description (see CVE-2021-31876).
+ // Applications relying on first-seen mempool behavior should
+ // check all unconfirmed ancestors; otherwise an opt-in ancestor
+ // might be replaced, causing removal of this descendant.
bool fReplacementOptOut = true;
for (const CTxIn &_txin : ptxConflicting->vin)
{
@@ -642,7 +626,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
break;
}
}
- if (fReplacementOptOut || args.disallow_mempool_conflicts) {
+ if (fReplacementOptOut) {
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "txn-mempool-conflict");
}
@@ -654,7 +638,6 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
LockPoints lp;
m_view.SetBackend(m_viewmempool);
- assert(std::addressof(::ChainstateActive().CoinsTip()) == std::addressof(m_active_chainstate.CoinsTip()));
const CCoinsViewCache& coins_cache = m_active_chainstate.CoinsTip();
// do all inputs exist?
for (const CTxIn& txin : tx.vin) {
@@ -692,18 +675,15 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
// be mined yet.
// Pass in m_view which has all of the relevant inputs cached. Note that, since m_view's
// backend was removed, it no longer pulls coins from the mempool.
- assert(std::addressof(::ChainstateActive()) == std::addressof(m_active_chainstate));
if (!CheckSequenceLocks(m_active_chainstate.m_chain.Tip(), m_view, tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp))
return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "non-BIP68-final");
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(m_active_chainstate.m_blockman));
if (!Consensus::CheckTxInputs(tx, state, m_view, m_active_chainstate.m_blockman.GetSpendHeight(m_view), ws.m_base_fees)) {
return false; // state filled in by CheckTxInputs
}
// Check for non-standard pay-to-script-hash in inputs
const auto& params = args.m_chainparams.GetConsensus();
- assert(std::addressof(::ChainActive()) == std::addressof(m_active_chainstate.m_chain));
auto taproot_state = VersionBitsState(m_active_chainstate.m_chain.Tip(), params, Consensus::DEPLOYMENT_TAPROOT, versionbitscache);
if (fRequireStandard && !AreInputsStandard(tx, m_view, taproot_state == ThresholdState::ACTIVE)) {
return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD, "bad-txns-nonstandard-inputs");
@@ -730,7 +710,6 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
}
}
- assert(std::addressof(::ChainActive()) == std::addressof(m_active_chainstate.m_chain));
entry.reset(new CTxMemPoolEntry(ptx, ws.m_base_fees, nAcceptTime, m_active_chainstate.m_chain.Height(),
fSpendsCoinbase, nSigOpsCost, lp));
unsigned int nSize = entry->GetTxSize();
@@ -983,9 +962,7 @@ bool MemPoolAccept::ConsensusScriptChecks(const ATMPArgs& args, Workspace& ws, P
// There is a similar check in CreateNewBlock() to prevent creating
// invalid blocks (using TestBlockValidity), however allowing such
// transactions into the mempool can be exploited as a DoS attack.
- assert(std::addressof(::ChainActive()) == std::addressof(m_active_chainstate.m_chain));
unsigned int currentBlockScriptVerifyFlags = GetBlockScriptFlags(m_active_chainstate.m_chain.Tip(), chainparams.GetConsensus());
- assert(std::addressof(::ChainstateActive().CoinsTip()) == std::addressof(m_active_chainstate.CoinsTip()));
if (!CheckInputsFromMempoolAndCache(tx, state, m_view, m_pool, currentBlockScriptVerifyFlags, txdata, m_active_chainstate.CoinsTip())) {
return error("%s: BUG! PLEASE REPORT THIS! CheckInputScripts failed against latest-block but not STANDARD flags %s, %s",
__func__, hash.ToString(), state.ToString());
@@ -1026,7 +1003,6 @@ bool MemPoolAccept::Finalize(const ATMPArgs& args, Workspace& ws)
// - it's not being re-added during a reorg which bypasses typical mempool fee limits
// - the node is not behind
// - the transaction is not dependent on any other transactions in the mempool
- assert(std::addressof(::ChainstateActive()) == std::addressof(m_active_chainstate));
bool validForFeeEstimation = !fReplacementTransaction && !bypass_limits && IsCurrentForFeeEstimation(m_active_chainstate) && m_pool.HasNoInputsOf(tx);
// Store transaction in memory
@@ -1034,7 +1010,6 @@ bool MemPoolAccept::Finalize(const ATMPArgs& args, Workspace& ws)
// trim mempool and check if tx was trimmed
if (!bypass_limits) {
- assert(std::addressof(::ChainstateActive().CoinsTip()) == std::addressof(m_active_chainstate.CoinsTip()));
LimitMempoolSize(m_pool, m_active_chainstate.CoinsTip(), gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, std::chrono::hours{gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)});
if (!m_pool.exists(hash))
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "mempool full");
@@ -1077,65 +1052,15 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std::
{
AssertLockHeld(cs_main);
+ // These context-free package limits can be done before taking the mempool lock.
PackageValidationState package_state;
- const unsigned int package_count = txns.size();
-
- // These context-free package limits can be checked before taking the mempool lock.
- if (package_count > MAX_PACKAGE_COUNT) {
- package_state.Invalid(PackageValidationResult::PCKG_POLICY, "package-too-many-transactions");
- return PackageMempoolAcceptResult(package_state, {});
- }
-
- const int64_t total_size = std::accumulate(txns.cbegin(), txns.cend(), 0,
- [](int64_t sum, const auto& tx) { return sum + GetVirtualTransactionSize(*tx); });
- // If the package only contains 1 tx, it's better to report the policy violation on individual tx size.
- if (package_count > 1 && total_size > MAX_PACKAGE_SIZE * 1000) {
- package_state.Invalid(PackageValidationResult::PCKG_POLICY, "package-too-large");
- return PackageMempoolAcceptResult(package_state, {});
- }
+ if (!CheckPackage(txns, package_state)) return PackageMempoolAcceptResult(package_state, {});
- // Construct workspaces and check package policies.
std::vector<Workspace> workspaces{};
- workspaces.reserve(package_count);
- {
- std::unordered_set<uint256, SaltedTxidHasher> later_txids;
- std::transform(txns.cbegin(), txns.cend(), std::inserter(later_txids, later_txids.end()),
- [](const auto& tx) { return tx->GetHash(); });
- // Require the package to be sorted in order of dependency, i.e. parents appear before children.
- // An unsorted package will fail anyway on missing-inputs, but it's better to quit earlier and
- // fail on something less ambiguous (missing-inputs could also be an orphan or trying to
- // spend nonexistent coins).
- for (const auto& tx : txns) {
- for (const auto& input : tx->vin) {
- if (later_txids.find(input.prevout.hash) != later_txids.end()) {
- // The parent is a subsequent transaction in the package.
- package_state.Invalid(PackageValidationResult::PCKG_POLICY, "package-not-sorted");
- return PackageMempoolAcceptResult(package_state, {});
- }
- }
- later_txids.erase(tx->GetHash());
- workspaces.emplace_back(Workspace(tx));
- }
- }
+ workspaces.reserve(txns.size());
+ std::transform(txns.cbegin(), txns.cend(), std::back_inserter(workspaces),
+ [](const auto& tx) { return Workspace(tx); });
std::map<const uint256, const MempoolAcceptResult> results;
- {
- // Don't allow any conflicting transactions, i.e. spending the same inputs, in a package.
- std::unordered_set<COutPoint, SaltedOutpointHasher> inputs_seen;
- for (const auto& tx : txns) {
- for (const auto& input : tx->vin) {
- if (inputs_seen.find(input.prevout) != inputs_seen.end()) {
- // This input is also present in another tx in the package.
- package_state.Invalid(PackageValidationResult::PCKG_POLICY, "conflict-in-package");
- return PackageMempoolAcceptResult(package_state, {});
- }
- }
- // Batch-add all the inputs for a tx at a time. If we added them 1 at a time, we could
- // catch duplicate inputs within a single tx. This is a more severe, consensus error,
- // and we want to report that from CheckTransaction instead.
- std::transform(tx->vin.cbegin(), tx->vin.cend(), std::inserter(inputs_seen, inputs_seen.end()),
- [](const auto& input) { return input.prevout; });
- }
- }
LOCK(m_pool.cs);
@@ -1148,10 +1073,10 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std::
return PackageMempoolAcceptResult(package_state, std::move(results));
}
// Make the coins created by this transaction available for subsequent transactions in the
- // package to spend. Since we already checked conflicts in the package and RBFs are
- // impossible, we don't need to track the coins spent. Note that this logic will need to be
- // updated if RBFs in packages are allowed in the future.
- assert(args.disallow_mempool_conflicts);
+ // package to spend. Since we already checked conflicts in the package and we don't allow
+ // replacements, we don't need to track the coins spent. Note that this logic will need to be
+ // updated if package replace-by-fee is allowed in the future.
+ assert(!args.m_allow_bip125_replacement);
m_viewmempool.PackageAddTransaction(ws.m_ptx);
}
@@ -1185,9 +1110,8 @@ static MempoolAcceptResult AcceptToMemoryPoolWithTime(const CChainParams& chainp
{
std::vector<COutPoint> coins_to_uncache;
MemPoolAccept::ATMPArgs args { chainparams, nAcceptTime, bypass_limits, coins_to_uncache,
- test_accept, /* disallow_mempool_conflicts */ false };
+ test_accept, /* m_allow_bip125_replacement */ true };
- assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
const MempoolAcceptResult result = MemPoolAccept(pool, active_chainstate).AcceptSingleTransaction(tx, args);
if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
// Remove coins that were not present in the coins cache before calling
@@ -1207,7 +1131,6 @@ static MempoolAcceptResult AcceptToMemoryPoolWithTime(const CChainParams& chainp
MempoolAcceptResult AcceptToMemoryPool(CChainState& active_chainstate, CTxMemPool& pool, const CTransactionRef& tx,
bool bypass_limits, bool test_accept)
{
- assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
return AcceptToMemoryPoolWithTime(Params(), pool, active_chainstate, tx, GetTime(), bypass_limits, test_accept);
}
@@ -1222,12 +1145,10 @@ PackageMempoolAcceptResult ProcessNewPackage(CChainState& active_chainstate, CTx
std::vector<COutPoint> coins_to_uncache;
const CChainParams& chainparams = Params();
MemPoolAccept::ATMPArgs args { chainparams, GetTime(), /* bypass_limits */ false, coins_to_uncache,
- test_accept, /* disallow_mempool_conflicts */ true };
- assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
+ test_accept, /* m_allow_bip125_replacement */ false };
const PackageMempoolAcceptResult result = MemPoolAccept(pool, active_chainstate).AcceptMultipleTransactions(package, args);
// Uncache coins pertaining to transactions that were not submitted to the mempool.
- // Ensure the cache is still within its size limits.
for (const COutPoint& hashTx : coins_to_uncache) {
active_chainstate.CoinsTip().Uncache(hashTx);
}
@@ -1363,7 +1284,6 @@ static void AlertNotify(const std::string& strMessage)
void CChainState::CheckForkWarningConditions()
{
AssertLockHeld(cs_main);
- assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
// Before we get past initial download, we cannot reliably alert about forks
// (we assume we don't get stuck on a fork before finishing our initial sync)
@@ -1382,7 +1302,6 @@ void CChainState::CheckForkWarningConditions()
// Called both upon regular invalid block discovery *and* InvalidateBlock
void CChainState::InvalidChainFound(CBlockIndex* pindexNew)
{
- assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
if (!pindexBestInvalid || pindexNew->nChainWork > pindexBestInvalid->nChainWork)
pindexBestInvalid = pindexNew;
if (pindexBestHeader != nullptr && pindexBestHeader->GetAncestor(pindexNew->nHeight) == pindexNew) {
@@ -1401,8 +1320,9 @@ void CChainState::InvalidChainFound(CBlockIndex* pindexNew)
}
// Same as InvalidChainFound, above, except not called directly from InvalidateBlock,
-// which does its own setBlockIndexCandidates manageent.
-void CChainState::InvalidBlockFound(CBlockIndex *pindex, const BlockValidationState &state) {
+// which does its own setBlockIndexCandidates management.
+void CChainState::InvalidBlockFound(CBlockIndex* pindex, const BlockValidationState& state)
+{
if (state.GetResult() != BlockValidationResult::BLOCK_MUTATED) {
pindex->nStatus |= BLOCK_FAILED_VALID;
m_blockman.m_failed_blocks.insert(pindex);
@@ -1442,7 +1362,6 @@ bool CScriptCheck::operator()() {
int BlockManager::GetSpendHeight(const CCoinsViewCache& inputs)
{
AssertLockHeld(cs_main);
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(*this));
CBlockIndex* pindexPrev = LookupBlockIndex(inputs.GetBestBlock());
return pindexPrev->nHeight + 1;
}
@@ -1819,8 +1738,8 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
// may have let in a block that violates the rule prior to updating the
// software, and we would NOT be enforcing the rule here. Fully solving
// upgrade from one software version to the next after a consensus rule
- // change is potentially tricky and issue-specific (see RewindBlockIndex()
- // for one general approach that was used for BIP 141 deployment).
+ // change is potentially tricky and issue-specific (see NeedsRedownload()
+ // for one approach that was used for BIP 141 deployment).
// Also, currently the rule against blocks more than 2 hours in the future
// is enforced in ContextualCheckBlockHeader(); we wouldn't want to
// re-enforce that rule here (at least until we make it impossible for
@@ -2326,14 +2245,13 @@ static void UpdateTip(CTxMemPool& mempool, const CBlockIndex* pindexNew, const C
}
bilingual_str warning_messages;
- assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
if (!active_chainstate.IsInitialBlockDownload()) {
const CBlockIndex* pindex = pindexNew;
for (int bit = 0; bit < VERSIONBITS_NUM_BITS; bit++) {
WarningBitsConditionChecker checker(bit);
ThresholdState state = checker.GetStateFor(pindex, chainParams.GetConsensus(), warningcache[bit]);
if (state == ThresholdState::ACTIVE || state == ThresholdState::LOCKED_IN) {
- const bilingual_str warning = strprintf(_("Warning: unknown new rules activated (versionbit %i)"), bit);
+ const bilingual_str warning = strprintf(_("Unknown new rules activated (versionbit %i)"), bit);
if (state == ThresholdState::ACTIVE) {
DoWarning(warning);
} else {
@@ -2342,7 +2260,6 @@ static void UpdateTip(CTxMemPool& mempool, const CBlockIndex* pindexNew, const C
}
}
}
- assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
LogPrintf("%s: new best=%s height=%d version=0x%08x log2_work=%f tx=%lu date='%s' progress=%f cache=%.1fMiB(%utxo)%s\n", __func__,
pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, pindexNew->nVersion,
log(pindexNew->nChainWork.getdouble())/log(2.0), (unsigned long)pindexNew->nChainTx,
@@ -2602,7 +2519,6 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChai
{
AssertLockHeld(cs_main);
AssertLockHeld(m_mempool.cs);
- assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
const CBlockIndex* pindexOldTip = m_chain.Tip();
const CBlockIndex* pindexFork = m_chain.FindFork(pindexMostWork);
@@ -2702,7 +2618,6 @@ static bool NotifyHeaderTip(CChainState& chainstate) LOCKS_EXCLUDED(cs_main) {
if (pindexHeader != pindexHeaderOld) {
fNotify = true;
- assert(std::addressof(::ChainstateActive()) == std::addressof(chainstate));
fInitialBlockDownload = chainstate.IsInitialBlockDownload();
pindexHeaderOld = pindexHeader;
}
@@ -2913,7 +2828,6 @@ bool CChainState::InvalidateBlock(BlockValidationState& state, const CChainParam
// transactions back to the mempool if disconnecting was successful,
// and we're not doing a very deep invalidation (in which case
// keeping the mempool up to date is probably futile anyway).
- assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
UpdateMempoolForReorg(*this, m_mempool, disconnectpool, /* fAddToMempool = */ (++disconnected <= 10) && ret);
if (!ret) return false;
assert(invalid_walk_tip->pprev == m_chain.Tip());
@@ -3244,7 +3158,6 @@ CBlockIndex* BlockManager::GetLastCheckpoint(const CCheckpointData& data)
for (const MapCheckpoints::value_type& i : reverse_iterate(checkpoints))
{
const uint256& hash = i.second;
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(*this));
CBlockIndex* pindex = LookupBlockIndex(hash);
if (pindex) {
return pindex;
@@ -3277,7 +3190,6 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidatio
// Don't accept any forks from the main chain prior to last checkpoint.
// GetLastCheckpoint finds the last checkpoint in MapCheckpoints that's in our
// BlockIndex().
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(blockman));
CBlockIndex* pcheckpoint = blockman.GetLastCheckpoint(params.Checkpoints());
if (pcheckpoint && nHeight < pcheckpoint->nHeight) {
LogPrintf("ERROR: %s: forked chain older than last checkpoint (height %d)\n", __func__, nHeight);
@@ -3481,7 +3393,6 @@ bool BlockManager::AcceptBlockHeader(const CBlockHeader& block, BlockValidationS
// Exposed wrapper for AcceptBlockHeader
bool ChainstateManager::ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, BlockValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex)
{
- assert(std::addressof(::ChainstateActive()) == std::addressof(ActiveChainstate()));
AssertLockNotHeld(cs_main);
{
LOCK(cs_main);
@@ -3572,7 +3483,6 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, Block
// Write block to history file
if (fNewBlock) *fNewBlock = true;
- assert(std::addressof(::ChainActive()) == std::addressof(m_chain));
try {
FlatFilePos blockPos = SaveBlockToDisk(block, pindex->nHeight, m_chain, chainparams, dbp);
if (blockPos.IsNull()) {
@@ -3594,7 +3504,6 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, Block
bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<const CBlock>& block, bool force_processing, bool* new_block)
{
AssertLockNotHeld(cs_main);
- assert(std::addressof(::ChainstateActive()) == std::addressof(ActiveChainstate()));
{
CBlockIndex *pindex = nullptr;
@@ -3605,8 +3514,11 @@ bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const s
// Therefore, the following critical section must include the CheckBlock() call as well.
LOCK(cs_main);
- // Ensure that CheckBlock() passes before calling AcceptBlock, as
- // belt-and-suspenders.
+ // Skipping AcceptBlock() for CheckBlock() failures means that we will never mark a block as invalid if
+ // CheckBlock() fails. This is protective against consensus failure if there are any unknown forms of block
+ // malleability that cause CheckBlock() to fail; see e.g. CVE-2012-2459 and
+ // https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2019-February/016697.html. Because CheckBlock() is
+ // not very expensive, the anti-DoS benefits of caching failure (of a definitely-invalid block) are not substantial.
bool ret = CheckBlock(*block, state, chainparams.GetConsensus());
if (ret) {
// Store to disk
@@ -3636,7 +3548,6 @@ bool TestBlockValidity(BlockValidationState& state,
bool fCheckMerkleRoot)
{
AssertLockHeld(cs_main);
- assert(std::addressof(::ChainstateActive()) == std::addressof(chainstate));
assert(pindexPrev && pindexPrev == chainstate.m_chain.Tip());
CCoinsViewCache viewNew(&chainstate.CoinsTip());
uint256 block_hash(block.GetHash());
@@ -3646,7 +3557,6 @@ bool TestBlockValidity(BlockValidationState& state,
indexDummy.phashBlock = &block_hash;
// NOTE: CheckBlockHeader is called by CheckBlock
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(chainstate.m_blockman));
if (!ContextualCheckBlockHeader(block, state, chainstate.m_blockman, chainparams, pindexPrev, GetAdjustedTime()))
return error("%s: Consensus::ContextualCheckBlockHeader: %s", __func__, state.ToString());
if (!CheckBlock(block, state, chainparams.GetConsensus(), fCheckPOW, fCheckMerkleRoot))
@@ -3726,7 +3636,6 @@ void PruneBlockFilesManual(CChainState& active_chainstate, int nManualPruneHeigh
{
BlockValidationState state;
const CChainParams& chainparams = Params();
- assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
if (!active_chainstate.FlushStateToDisk(
chainparams, state, FlushStateMode::NONE, nManualPruneHeight)) {
LogPrintf("%s: failed to flush state (%s)\n", __func__, state.ToString());
@@ -3880,7 +3789,6 @@ void BlockManager::Unload() {
bool CChainState::LoadBlockIndexDB(const CChainParams& chainparams)
{
- assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
if (!m_blockman.LoadBlockIndex(
chainparams.GetConsensus(), *pblocktree,
setBlockIndexCandidates)) {
@@ -3937,7 +3845,6 @@ bool CChainState::LoadBlockIndexDB(const CChainParams& chainparams)
void CChainState::LoadMempool(const ArgsManager& args)
{
if (args.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
- assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
::LoadMempool(m_mempool, *this);
}
m_mempool.SetIsLoaded(!ShutdownRequested());
@@ -3989,7 +3896,6 @@ bool CVerifyDB::VerifyDB(
{
AssertLockHeld(cs_main);
- assert(std::addressof(::ChainstateActive()) == std::addressof(chainstate));
if (chainstate.m_chain.Tip() == nullptr || chainstate.m_chain.Tip()->pprev == nullptr)
return true;
@@ -4258,7 +4164,6 @@ bool CChainState::LoadGenesisBlock(const CChainParams& chainparams)
if (m_blockman.m_block_index.count(chainparams.GenesisBlock().GetHash()))
return true;
- assert(std::addressof(::ChainActive()) == std::addressof(m_chain));
try {
const CBlock& block = chainparams.GenesisBlock();
FlatFilePos blockPos = SaveBlockToDisk(block, 0, m_chain, chainparams, nullptr);
@@ -4322,7 +4227,6 @@ void CChainState::LoadExternalBlockFile(const CChainParams& chainparams, FILE* f
{
LOCK(cs_main);
// detect out of order blocks, and store them for later
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(m_blockman));
if (hash != chainparams.GetConsensus().hashGenesisBlock && !m_blockman.LookupBlockIndex(block.hashPrevBlock)) {
LogPrint(BCLog::REINDEX, "%s: Out of order block %s, parent %s not known\n", __func__, hash.ToString(),
block.hashPrevBlock.ToString());
@@ -4332,11 +4236,9 @@ void CChainState::LoadExternalBlockFile(const CChainParams& chainparams, FILE* f
}
// process in case the block isn't known yet
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(m_blockman));
CBlockIndex* pindex = m_blockman.LookupBlockIndex(hash);
if (!pindex || (pindex->nStatus & BLOCK_HAVE_DATA) == 0) {
BlockValidationState state;
- assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
if (AcceptBlock(pblock, state, chainparams, nullptr, true, dbp, nullptr)) {
nLoaded++;
}
@@ -4351,13 +4253,11 @@ void CChainState::LoadExternalBlockFile(const CChainParams& chainparams, FILE* f
// Activate the genesis block so normal node progress can continue
if (hash == chainparams.GetConsensus().hashGenesisBlock) {
BlockValidationState state;
- assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
if (!ActivateBestChain(state, chainparams, nullptr)) {
break;
}
}
- assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
NotifyHeaderTip(*this);
// Recursively process earlier encountered successors of this block
@@ -4376,7 +4276,6 @@ void CChainState::LoadExternalBlockFile(const CChainParams& chainparams, FILE* f
head.ToString());
LOCK(cs_main);
BlockValidationState dummy;
- assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
if (AcceptBlock(pblockrecursive, dummy, chainparams, nullptr, true, &it->second, nullptr))
{
nLoaded++;
@@ -4385,7 +4284,6 @@ void CChainState::LoadExternalBlockFile(const CChainParams& chainparams, FILE* f
}
range.first++;
mapBlocksUnknownParent.erase(it);
- assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
NotifyHeaderTip(*this);
}
}
@@ -4666,7 +4564,6 @@ bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate, FopenFn mocka
}
if (nTime > nNow - nExpiryTimeout) {
LOCK(cs_main);
- assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
if (AcceptToMemoryPoolWithTime(chainparams, pool, active_chainstate, tx, nTime, false /* bypass_limits */,
false /* test_accept */).m_result_type == MempoolAcceptResult::ResultType::VALID) {
++count;
@@ -5079,24 +4976,20 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
LOCK(::cs_main);
// Fake various pieces of CBlockIndex state:
- //
- // - nChainTx: so that we accurately report IBD-to-tip progress
- // - nTx: so that LoadBlockIndex() loads assumed-valid CBlockIndex entries
- // (among other things)
- // - nStatus & BLOCK_OPT_WITNESS: so that RewindBlockIndex() doesn't zealously
- // unwind the assumed-valid chain.
- //
CBlockIndex* index = nullptr;
for (int i = 0; i <= snapshot_chainstate.m_chain.Height(); ++i) {
index = snapshot_chainstate.m_chain[i];
+ // Fake nTx so that LoadBlockIndex() loads assumed-valid CBlockIndex
+ // entries (among other things)
if (!index->nTx) {
index->nTx = 1;
}
+ // Fake nChainTx so that GuessVerificationProgress reports accurately
index->nChainTx = index->pprev ? index->pprev->nChainTx + index->nTx : 1;
- // We need to fake this flag so that CChainState::RewindBlockIndex()
- // won't try to rewind the entire assumed-valid chain on startup.
+ // Fake BLOCK_OPT_WITNESS so that CChainState::NeedsRedownload()
+ // won't ask to rewind the entire assumed-valid chain on startup.
if (index->pprev && ::IsWitnessEnabled(index->pprev, ::Params().GetConsensus())) {
index->nStatus |= BLOCK_OPT_WITNESS;
}
diff --git a/src/validation.h b/src/validation.h
index 9c718b3d63..5720ba8071 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -234,11 +234,13 @@ MempoolAcceptResult AcceptToMemoryPool(CChainState& active_chainstate, CTxMemPoo
bool bypass_limits, bool test_accept=false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/**
-* Atomically test acceptance of a package. If the package only contains one tx, package rules still apply.
+* Atomically test acceptance of a package. If the package only contains one tx, package rules still
+* apply. Package validation does not allow BIP125 replacements, so the transaction(s) cannot spend
+* the same inputs as any transaction in the mempool.
* @param[in] txns Group of transactions which may be independent or contain
-* parent-child dependencies. The transactions must not conflict, i.e.
-* must not spend the same inputs, even if it would be a valid BIP125
-* replace-by-fee. Parents must appear before children.
+* parent-child dependencies. The transactions must not conflict
+* with each other, i.e., must not spend the same inputs. If any
+* dependencies exist, parents must appear before children.
* @returns a PackageMempoolAcceptResult which includes a MempoolAcceptResult for each transaction.
* If a transaction fails, validation will exit early and some results may be missing.
*/
@@ -269,9 +271,13 @@ bool TestLockPointValidity(CChain& active_chain, const LockPoints* lp) EXCLUSIVE
* Check if transaction will be BIP68 final in the next block to be created on top of tip.
* @param[in] tip Chain tip to check tx sequence locks against. For example,
* the tip of the current active chain.
- * @param[in] coins_view Any CCoinsView that provides access to the relevant coins
- * for checking sequence locks. Any CCoinsView can be passed in;
- * it is assumed to be consistent with the tip.
+ * @param[in] coins_view Any CCoinsView that provides access to the relevant coins for
+ * checking sequence locks. For example, it can be a CCoinsViewCache
+ * that isn't connected to anything but contains all the relevant
+ * coins, or a CCoinsViewMemPool that is connected to the
+ * mempool and chainstate UTXO set. In the latter case, the caller is
+ * responsible for holding the appropriate locks to ensure that
+ * calls to GetCoin() return correct coins.
* Simulates calling SequenceLocks() with data from the tip passed in.
* Optionally stores in LockPoints the resulting height and time calculated and the hash
* of the block needed for calculation or skips the calculation and uses the LockPoints
@@ -555,7 +561,7 @@ enum class CoinsCacheSizeState
*
* Anything that is contingent on the current tip of the chain is stored here,
* whereas block information and metadata independent of the current tip is
- * kept in `BlockMetadataManager`.
+ * kept in `BlockManager`.
*/
class CChainState
{
@@ -885,10 +891,6 @@ private:
CAutoFile& coins_file,
const SnapshotMetadata& metadata);
- // For access to m_active_chainstate.
- friend CChainState& ChainstateActive();
- friend CChain& ChainActive();
-
public:
std::thread m_load_block;
//! A single BlockManager instance is shared across each constructed
@@ -1011,16 +1013,13 @@ public:
//! Check to see if caches are out of balance and if so, call
//! ResizeCoinsCaches() as needed.
void MaybeRebalanceCaches() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
-};
-
-/** DEPRECATED! Please use node.chainman instead. May only be used in validation.cpp internally */
-extern ChainstateManager g_chainman GUARDED_BY(::cs_main);
-/** Please prefer the identical ChainstateManager::ActiveChainstate */
-CChainState& ChainstateActive();
-
-/** Please prefer the identical ChainstateManager::ActiveChain */
-CChain& ChainActive();
+ ~ChainstateManager() {
+ LOCK(::cs_main);
+ UnloadBlockIndex(/* mempool */ nullptr, *this);
+ Reset();
+ }
+};
/** Global variable that points to the active block tree (protected by cs_main) */
extern std::unique_ptr<CBlockTreeDB> pblocktree;
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp
index 5bf037b222..8d5316e0af 100644
--- a/src/wallet/db.cpp
+++ b/src/wallet/db.cpp
@@ -12,7 +12,7 @@
std::vector<fs::path> ListDatabases(const fs::path& wallet_dir)
{
- const size_t offset = wallet_dir.string().size() + 1;
+ const size_t offset = wallet_dir.string().size() + (wallet_dir == wallet_dir.root_name() ? 0 : 1);
std::vector<fs::path> paths;
boost::system::error_code ec;
diff --git a/src/wallet/external_signer_scriptpubkeyman.cpp b/src/wallet/external_signer_scriptpubkeyman.cpp
index fe2c810afa..efef1ec754 100644
--- a/src/wallet/external_signer_scriptpubkeyman.cpp
+++ b/src/wallet/external_signer_scriptpubkeyman.cpp
@@ -13,8 +13,6 @@
#include <utility>
#include <vector>
-#ifdef ENABLE_EXTERNAL_SIGNER
-
bool ExternalSignerScriptPubKeyMan::SetupDescriptor(std::unique_ptr<Descriptor> desc)
{
LOCK(cs_desc_man);
@@ -62,10 +60,10 @@ bool ExternalSignerScriptPubKeyMan::DisplayAddress(const CScript scriptPubKey, c
}
// If sign is true, transaction must previously have been filled
-TransactionError ExternalSignerScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbt, int sighash_type, bool sign, bool bip32derivs, int* n_signed) const
+TransactionError ExternalSignerScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type, bool sign, bool bip32derivs, int* n_signed) const
{
if (!sign) {
- return DescriptorScriptPubKeyMan::FillPSBT(psbt, sighash_type, false, bip32derivs, n_signed);
+ return DescriptorScriptPubKeyMan::FillPSBT(psbt, txdata, sighash_type, false, bip32derivs, n_signed);
}
// Already complete if every input is now signed
@@ -84,5 +82,3 @@ TransactionError ExternalSignerScriptPubKeyMan::FillPSBT(PartiallySignedTransact
FinalizePSBT(psbt); // This won't work in a multisig setup
return TransactionError::OK;
}
-
-#endif
diff --git a/src/wallet/external_signer_scriptpubkeyman.h b/src/wallet/external_signer_scriptpubkeyman.h
index 1786958912..8eed947b7b 100644
--- a/src/wallet/external_signer_scriptpubkeyman.h
+++ b/src/wallet/external_signer_scriptpubkeyman.h
@@ -5,7 +5,6 @@
#ifndef BITCOIN_WALLET_EXTERNAL_SIGNER_SCRIPTPUBKEYMAN_H
#define BITCOIN_WALLET_EXTERNAL_SIGNER_SCRIPTPUBKEYMAN_H
-#ifdef ENABLE_EXTERNAL_SIGNER
#include <wallet/scriptpubkeyman.h>
#include <memory>
@@ -29,8 +28,6 @@ class ExternalSignerScriptPubKeyMan : public DescriptorScriptPubKeyMan
bool DisplayAddress(const CScript scriptPubKey, const ExternalSigner &signer) const;
- TransactionError FillPSBT(PartiallySignedTransaction& psbt, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const override;
+ TransactionError FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const override;
};
-#endif
-
#endif // BITCOIN_WALLET_EXTERNAL_SIGNER_SCRIPTPUBKEYMAN_H
diff --git a/src/wallet/interfaces.cpp b/src/wallet/interfaces.cpp
index aca52964ee..5a832d020b 100644
--- a/src/wallet/interfaces.cpp
+++ b/src/wallet/interfaces.cpp
@@ -206,6 +206,11 @@ public:
WalletBatch batch{m_wallet->GetDatabase()};
return m_wallet->SetAddressReceiveRequest(batch, dest, id, value);
}
+ bool displayAddress(const CTxDestination& dest) override
+ {
+ LOCK(m_wallet->cs_wallet);
+ return m_wallet->DisplayAddress(dest);
+ }
void lockCoin(const COutPoint& output) override
{
LOCK(m_wallet->cs_wallet);
@@ -344,9 +349,9 @@ public:
TransactionError fillPSBT(int sighash_type,
bool sign,
bool bip32derivs,
+ size_t* n_signed,
PartiallySignedTransaction& psbtx,
- bool& complete,
- size_t* n_signed) override
+ bool& complete) override
{
return m_wallet->FillPSBT(psbtx, complete, sighash_type, sign, bip32derivs, n_signed);
}
@@ -446,6 +451,7 @@ public:
unsigned int getConfirmTarget() override { return m_wallet->m_confirm_target; }
bool hdEnabled() override { return m_wallet->IsHDEnabled(); }
bool canGetAddresses() override { return m_wallet->CanGetAddresses(); }
+ bool hasExternalSigner() override { return m_wallet->IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER); }
bool privateKeysDisabled() override { return m_wallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS); }
OutputType getDefaultAddressType() override { return m_wallet->m_default_address_type; }
CAmount getDefaultMaxTxFee() override { return m_wallet->m_default_max_tx_fee; }
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index 726b13beac..4e9ba83ead 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -1530,6 +1530,18 @@ static UniValue ProcessDescriptorImport(CWallet& wallet, const UniValue& data, c
}
}
+ // Taproot descriptors cannot be imported if Taproot is not yet active.
+ // Check if this is a Taproot descriptor
+ CTxDestination dest;
+ ExtractDestination(scripts[0], dest);
+ if (std::holds_alternative<WitnessV1Taproot>(dest)) {
+ // Check if Taproot is active
+ if (!wallet.chain().isTaprootActive()) {
+ // Taproot is not active, raise an error
+ throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import tr() descriptor when Taproot is not active");
+ }
+ }
+
// If private keys are enabled, check some things.
if (!wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
if (keys.keys.empty()) {
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 534c974178..ab34af2329 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -3320,7 +3320,8 @@ RPCHelpMan signrawtransactionwithwallet()
},
},
},
- {"sighashtype", RPCArg::Type::STR, RPCArg::Default{"ALL"}, "The signature hash type. Must be one of\n"
+ {"sighashtype", RPCArg::Type::STR, RPCArg::Default{"DEFAULT"}, "The signature hash type. Must be one of\n"
+ " \"DEFAULT\"\n"
" \"ALL\"\n"
" \"NONE\"\n"
" \"SINGLE\"\n"
@@ -3542,7 +3543,7 @@ static RPCHelpMan bumpfee_helper(std::string method_name)
} else {
PartiallySignedTransaction psbtx(mtx);
bool complete = false;
- const TransactionError err = pwallet->FillPSBT(psbtx, complete, SIGHASH_ALL, false /* sign */, true /* bip32derivs */);
+ const TransactionError err = pwallet->FillPSBT(psbtx, complete, SIGHASH_DEFAULT, false /* sign */, true /* bip32derivs */);
CHECK_NONFATAL(err == TransactionError::OK);
CHECK_NONFATAL(!complete);
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
@@ -4175,8 +4176,8 @@ static RPCHelpMan send()
// First fill transaction with our data without signing,
// so external signers are not asked sign more than once.
bool complete;
- pwallet->FillPSBT(psbtx, complete, SIGHASH_ALL, false, true);
- const TransactionError err = pwallet->FillPSBT(psbtx, complete, SIGHASH_ALL, true, false);
+ pwallet->FillPSBT(psbtx, complete, SIGHASH_DEFAULT, false, true);
+ const TransactionError err = pwallet->FillPSBT(psbtx, complete, SIGHASH_DEFAULT, true, false);
if (err != TransactionError::OK) {
throw JSONRPCTransactionError(err);
}
@@ -4291,7 +4292,8 @@ static RPCHelpMan walletprocesspsbt()
{
{"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction base64 string"},
{"sign", RPCArg::Type::BOOL, RPCArg::Default{true}, "Also sign the transaction when updating"},
- {"sighashtype", RPCArg::Type::STR, RPCArg::Default{"ALL"}, "The signature hash type to sign with if not specified by the PSBT. Must be one of\n"
+ {"sighashtype", RPCArg::Type::STR, RPCArg::Default{"DEFAULT"}, "The signature hash type to sign with if not specified by the PSBT. Must be one of\n"
+ " \"DEFAULT\"\n"
" \"ALL\"\n"
" \"NONE\"\n"
" \"SINGLE\"\n"
diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp
index 2eb9ca5c6d..c8baa0665e 100644
--- a/src/wallet/scriptpubkeyman.cpp
+++ b/src/wallet/scriptpubkeyman.cpp
@@ -13,7 +13,6 @@
#include <util/system.h>
#include <util/time.h>
#include <util/translation.h>
-#include <external_signer.h>
#include <wallet/scriptpubkeyman.h>
#include <optional>
@@ -597,7 +596,7 @@ SigningResult LegacyScriptPubKeyMan::SignMessage(const std::string& message, con
return SigningResult::SIGNING_FAILED;
}
-TransactionError LegacyScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, int sighash_type, bool sign, bool bip32derivs, int* n_signed) const
+TransactionError LegacyScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, const PrecomputedTransactionData& txdata, int sighash_type, bool sign, bool bip32derivs, int* n_signed) const
{
if (n_signed) {
*n_signed = 0;
@@ -626,7 +625,7 @@ TransactionError LegacyScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psb
}
SignatureData sigdata;
input.FillSignatureData(sigdata);
- SignPSBTInput(HidingSigningProvider(this, !sign, !bip32derivs), psbtx, i, sighash_type);
+ SignPSBTInput(HidingSigningProvider(this, !sign, !bip32derivs), psbtx, i, &txdata, sighash_type);
bool signed_one = PSBTInputSigned(input);
if (n_signed && (signed_one || !sign)) {
@@ -2083,7 +2082,7 @@ SigningResult DescriptorScriptPubKeyMan::SignMessage(const std::string& message,
return SigningResult::OK;
}
-TransactionError DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, int sighash_type, bool sign, bool bip32derivs, int* n_signed) const
+TransactionError DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, const PrecomputedTransactionData& txdata, int sighash_type, bool sign, bool bip32derivs, int* n_signed) const
{
if (n_signed) {
*n_signed = 0;
@@ -2133,7 +2132,7 @@ TransactionError DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction&
}
}
- SignPSBTInput(HidingSigningProvider(keys.get(), !sign, !bip32derivs), psbtx, i, sighash_type);
+ SignPSBTInput(HidingSigningProvider(keys.get(), !sign, !bip32derivs), psbtx, i, &txdata, sighash_type);
bool signed_one = PSBTInputSigned(input);
if (n_signed && (signed_one || !sign)) {
diff --git a/src/wallet/scriptpubkeyman.h b/src/wallet/scriptpubkeyman.h
index b8e34fbac3..3c4603608c 100644
--- a/src/wallet/scriptpubkeyman.h
+++ b/src/wallet/scriptpubkeyman.h
@@ -235,7 +235,7 @@ public:
/** Sign a message with the given script */
virtual SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const { return SigningResult::SIGNING_FAILED; };
/** Adds script and derivation path information to a PSBT, and optionally signs it. */
- virtual TransactionError FillPSBT(PartiallySignedTransaction& psbt, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const { return TransactionError::INVALID_PSBT; }
+ virtual TransactionError FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const { return TransactionError::INVALID_PSBT; }
virtual uint256 GetID() const { return uint256(); }
@@ -394,7 +394,7 @@ public:
bool SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, std::string>& input_errors) const override;
SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const override;
- TransactionError FillPSBT(PartiallySignedTransaction& psbt, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const override;
+ TransactionError FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const override;
uint256 GetID() const override;
@@ -605,7 +605,7 @@ public:
bool SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, std::string>& input_errors) const override;
SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const override;
- TransactionError FillPSBT(PartiallySignedTransaction& psbt, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const override;
+ TransactionError FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const override;
uint256 GetID() const override;
diff --git a/src/wallet/spend.cpp b/src/wallet/spend.cpp
index 97fc7acca5..c8ded4c51e 100644
--- a/src/wallet/spend.cpp
+++ b/src/wallet/spend.cpp
@@ -352,7 +352,7 @@ std::vector<OutputGroup> CWallet::GroupOutputs(const std::vector<COutput>& outpu
return groups_out;
}
-bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<COutput> coins,
+bool CWallet::AttemptSelection(const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<COutput> coins,
std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params) const
{
setCoinsRet.clear();
@@ -456,32 +456,32 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
// If possible, fund the transaction with confirmed UTXOs only. Prefer at least six
// confirmations on outputs received from other wallets and only spend confirmed change.
- if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(1, 6, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params)) return true;
- if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(1, 1, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params)) return true;
+ if (AttemptSelection(value_to_select, CoinEligibilityFilter(1, 6, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params)) return true;
+ if (AttemptSelection(value_to_select, CoinEligibilityFilter(1, 1, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params)) return true;
// Fall back to using zero confirmation change (but with as few ancestors in the mempool as
// possible) if we cannot fund the transaction otherwise.
if (m_spend_zero_conf_change) {
- if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, 2), vCoins, setCoinsRet, nValueRet, coin_selection_params)) return true;
- if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, std::min((size_t)4, max_ancestors/3), std::min((size_t)4, max_descendants/3)),
+ if (AttemptSelection(value_to_select, CoinEligibilityFilter(0, 1, 2), vCoins, setCoinsRet, nValueRet, coin_selection_params)) return true;
+ if (AttemptSelection(value_to_select, CoinEligibilityFilter(0, 1, std::min((size_t)4, max_ancestors/3), std::min((size_t)4, max_descendants/3)),
vCoins, setCoinsRet, nValueRet, coin_selection_params)) {
return true;
}
- if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, max_ancestors/2, max_descendants/2),
+ if (AttemptSelection(value_to_select, CoinEligibilityFilter(0, 1, max_ancestors/2, max_descendants/2),
vCoins, setCoinsRet, nValueRet, coin_selection_params)) {
return true;
}
// If partial groups are allowed, relax the requirement of spending OutputGroups (groups
// of UTXOs sent to the same address, which are obviously controlled by a single wallet)
// in their entirety.
- if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, max_ancestors-1, max_descendants-1, true /* include_partial_groups */),
+ if (AttemptSelection(value_to_select, CoinEligibilityFilter(0, 1, max_ancestors-1, max_descendants-1, true /* include_partial_groups */),
vCoins, setCoinsRet, nValueRet, coin_selection_params)) {
return true;
}
// Try with unsafe inputs if they are allowed. This may spend unconfirmed outputs
// received from other wallets.
if (coin_control.m_include_unsafe_inputs
- && SelectCoinsMinConf(value_to_select,
+ && AttemptSelection(value_to_select,
CoinEligibilityFilter(0 /* conf_mine */, 0 /* conf_theirs */, max_ancestors-1, max_descendants-1, true /* include_partial_groups */),
vCoins, setCoinsRet, nValueRet, coin_selection_params)) {
return true;
@@ -489,7 +489,7 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
// Try with unlimited ancestors/descendants. The transaction will still need to meet
// mempool ancestor/descendant policy to be accepted to mempool and broadcasted, but
// OutputGroups use heuristics that may overestimate ancestor/descendant counts.
- if (!fRejectLongChains && SelectCoinsMinConf(value_to_select,
+ if (!fRejectLongChains && AttemptSelection(value_to_select,
CoinEligibilityFilter(0, 1, std::numeric_limits<uint64_t>::max(), std::numeric_limits<uint64_t>::max(), true /* include_partial_groups */),
vCoins, setCoinsRet, nValueRet, coin_selection_params)) {
return true;
@@ -499,7 +499,7 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
return false;
}();
- // SelectCoinsMinConf clears setCoinsRet, so add the preset inputs from coin_control to the coinset
+ // AttemptSelection clears setCoinsRet, so add the preset inputs from coin_control to the coinset
util::insert(setCoinsRet, setPresetCoins);
// add preset inputs to the total value selected
@@ -578,287 +578,266 @@ bool CWallet::CreateTransactionInternal(
FeeCalculation& fee_calc_out,
bool sign)
{
- CAmount nValue = 0;
+ AssertLockHeld(cs_wallet);
+
+ CMutableTransaction txNew; // The resulting transaction that we make
+ txNew.nLockTime = GetLocktimeForNewTransaction(chain(), GetLastBlockHash(), GetLastBlockHeight());
+
+ CoinSelectionParams coin_selection_params; // Parameters for coin selection, init with dummy
+ coin_selection_params.m_avoid_partial_spends = coin_control.m_avoid_partial_spends;
+
+ CAmount recipients_sum = 0;
const OutputType change_type = TransactionChangeType(coin_control.m_change_type ? *coin_control.m_change_type : m_default_change_type, vecSend);
ReserveDestination reservedest(this, change_type);
- unsigned int nSubtractFeeFromAmount = 0;
- for (const auto& recipient : vecSend)
- {
- if (nValue < 0 || recipient.nAmount < 0)
- {
- error = _("Transaction amounts must not be negative");
- return false;
+ unsigned int outputs_to_subtract_fee_from = 0; // The number of outputs which we are subtracting the fee from
+ for (const auto& recipient : vecSend) {
+ recipients_sum += recipient.nAmount;
+
+ if (recipient.fSubtractFeeFromAmount) {
+ outputs_to_subtract_fee_from++;
+ coin_selection_params.m_subtract_fee_outputs = true;
}
- nValue += recipient.nAmount;
+ }
- if (recipient.fSubtractFeeFromAmount)
- nSubtractFeeFromAmount++;
+ // Create change script that will be used if we need change
+ // TODO: pass in scriptChange instead of reservedest so
+ // change transaction isn't always pay-to-bitcoin-address
+ CScript scriptChange;
+
+ // coin control: send change to custom address
+ if (!std::get_if<CNoDestination>(&coin_control.destChange)) {
+ scriptChange = GetScriptForDestination(coin_control.destChange);
+ } else { // no coin control: send change to newly generated address
+ // Note: We use a new key here to keep it from being obvious which side is the change.
+ // The drawback is that by not reusing a previous key, the change may be lost if a
+ // backup is restored, if the backup doesn't have the new private key for the change.
+ // If we reused the old key, it would be possible to add code to look for and
+ // rediscover unknown transactions that were written with keys of ours to recover
+ // post-backup change.
+
+ // Reserve a new key pair from key pool. If it fails, provide a dummy
+ // destination in case we don't need change.
+ CTxDestination dest;
+ if (!reservedest.GetReservedDestination(dest, true)) {
+ error = _("Transaction needs a change address, but we can't generate it. Please call keypoolrefill first.");
+ }
+ scriptChange = GetScriptForDestination(dest);
+ // A valid destination implies a change script (and
+ // vice-versa). An empty change script will abort later, if the
+ // change keypool ran out, but change is required.
+ CHECK_NONFATAL(IsValidDestination(dest) != scriptChange.empty());
}
- if (vecSend.empty())
- {
- error = _("Transaction must have at least one recipient");
- return false;
+ CTxOut change_prototype_txout(0, scriptChange);
+ coin_selection_params.change_output_size = GetSerializeSize(change_prototype_txout);
+
+ // Get size of spending the change output
+ int change_spend_size = CalculateMaximumSignedInputSize(change_prototype_txout, this);
+ // If the wallet doesn't know how to sign change output, assume p2sh-p2wpkh
+ // as lower-bound to allow BnB to do it's thing
+ if (change_spend_size == -1) {
+ coin_selection_params.change_spend_size = DUMMY_NESTED_P2WPKH_INPUT_SIZE;
+ } else {
+ coin_selection_params.change_spend_size = (size_t)change_spend_size;
}
- CMutableTransaction txNew;
+ // Set discard feerate
+ coin_selection_params.m_discard_feerate = GetDiscardRate(*this);
+
+ // Get the fee rate to use effective values in coin selection
FeeCalculation feeCalc;
- TxSize tx_sizes;
- int nBytes;
+ coin_selection_params.m_effective_feerate = 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 && coin_selection_params.m_effective_feerate > *coin_control.m_feerate) {
+ error = strprintf(_("Fee rate (%s) is lower than the minimum fee rate setting (%s)"), coin_control.m_feerate->ToString(FeeEstimateMode::SAT_VB), coin_selection_params.m_effective_feerate.ToString(FeeEstimateMode::SAT_VB));
+ return false;
+ }
+ if (feeCalc.reason == FeeReason::FALLBACK && !m_allow_fallback_fee) {
+ // eventually allow a fallback fee
+ error = _("Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.");
+ return false;
+ }
+
+ // Get long term estimate
+ CCoinControl cc_temp;
+ cc_temp.m_confirm_target = chain().estimateMaxBlocks();
+ coin_selection_params.m_long_term_feerate = GetMinimumFeeRate(*this, cc_temp, nullptr);
+
+ // Calculate the cost of change
+ // Cost of change is the cost of creating the change output + cost of spending the change output in the future.
+ // For creating the change output now, we use the effective feerate.
+ // For spending the change output in the future, we use the discard feerate for now.
+ // So cost of change = (change output size * effective feerate) + (size of spending change output * discard feerate)
+ coin_selection_params.m_change_fee = coin_selection_params.m_effective_feerate.GetFee(coin_selection_params.change_output_size);
+ coin_selection_params.m_cost_of_change = coin_selection_params.m_discard_feerate.GetFee(coin_selection_params.change_spend_size) + coin_selection_params.m_change_fee;
+
+ // vouts to the payees
+ if (!coin_selection_params.m_subtract_fee_outputs) {
+ coin_selection_params.tx_noinputs_size = 11; // Static vsize overhead + outputs vsize. 4 nVersion, 4 nLocktime, 1 input count, 1 output count, 1 witness overhead (dummy, flag, stack size)
+ }
+ for (const auto& recipient : vecSend)
{
- std::set<CInputCoin> setCoins;
- LOCK(cs_wallet);
- txNew.nLockTime = GetLocktimeForNewTransaction(chain(), GetLastBlockHash(), GetLastBlockHeight());
- {
- std::vector<COutput> vAvailableCoins;
- AvailableCoins(vAvailableCoins, &coin_control, 1, MAX_MONEY, MAX_MONEY, 0);
- CoinSelectionParams coin_selection_params; // Parameters for coin selection, init with dummy
- coin_selection_params.m_avoid_partial_spends = coin_control.m_avoid_partial_spends;
-
- // Create change script that will be used if we need change
- // TODO: pass in scriptChange instead of reservedest so
- // change transaction isn't always pay-to-bitcoin-address
- CScript scriptChange;
-
- // coin control: send change to custom address
- if (!std::get_if<CNoDestination>(&coin_control.destChange)) {
- scriptChange = GetScriptForDestination(coin_control.destChange);
- } else { // no coin control: send change to newly generated address
- // Note: We use a new key here to keep it from being obvious which side is the change.
- // The drawback is that by not reusing a previous key, the change may be lost if a
- // backup is restored, if the backup doesn't have the new private key for the change.
- // If we reused the old key, it would be possible to add code to look for and
- // rediscover unknown transactions that were written with keys of ours to recover
- // post-backup change.
-
- // Reserve a new key pair from key pool. If it fails, provide a dummy
- // destination in case we don't need change.
- CTxDestination dest;
- if (!reservedest.GetReservedDestination(dest, true)) {
- error = _("Transaction needs a change address, but we can't generate it. Please call keypoolrefill first.");
- }
- scriptChange = GetScriptForDestination(dest);
- // A valid destination implies a change script (and
- // vice-versa). An empty change script will abort later, if the
- // change keypool ran out, but change is required.
- CHECK_NONFATAL(IsValidDestination(dest) != scriptChange.empty());
- }
- CTxOut change_prototype_txout(0, scriptChange);
- coin_selection_params.change_output_size = GetSerializeSize(change_prototype_txout);
-
- // Get size of spending the change output
- int change_spend_size = CalculateMaximumSignedInputSize(change_prototype_txout, this);
- // If the wallet doesn't know how to sign change output, assume p2sh-p2wpkh
- // as lower-bound to allow BnB to do it's thing
- if (change_spend_size == -1) {
- coin_selection_params.change_spend_size = DUMMY_NESTED_P2WPKH_INPUT_SIZE;
- } else {
- coin_selection_params.change_spend_size = (size_t)change_spend_size;
- }
+ CTxOut txout(recipient.nAmount, recipient.scriptPubKey);
- // Set discard feerate
- coin_selection_params.m_discard_feerate = GetDiscardRate(*this);
+ // Include the fee cost for outputs.
+ if (!coin_selection_params.m_subtract_fee_outputs) {
+ coin_selection_params.tx_noinputs_size += ::GetSerializeSize(txout, PROTOCOL_VERSION);
+ }
- // Get the fee rate to use effective values in coin selection
- coin_selection_params.m_effective_feerate = 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 && coin_selection_params.m_effective_feerate > *coin_control.m_feerate) {
- error = strprintf(_("Fee rate (%s) is lower than the minimum fee rate setting (%s)"), coin_control.m_feerate->ToString(FeeEstimateMode::SAT_VB), coin_selection_params.m_effective_feerate.ToString(FeeEstimateMode::SAT_VB));
- return false;
- }
- if (feeCalc.reason == FeeReason::FALLBACK && !m_allow_fallback_fee) {
- // eventually allow a fallback fee
- error = _("Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.");
- return false;
- }
+ if (IsDust(txout, chain().relayDustFee()))
+ {
+ error = _("Transaction amount too small");
+ return false;
+ }
+ txNew.vout.push_back(txout);
+ }
- // Get long term estimate
- CCoinControl cc_temp;
- cc_temp.m_confirm_target = chain().estimateMaxBlocks();
- coin_selection_params.m_long_term_feerate = GetMinimumFeeRate(*this, cc_temp, nullptr);
+ // Include the fees for things that aren't inputs, excluding the change output
+ const CAmount not_input_fees = coin_selection_params.m_effective_feerate.GetFee(coin_selection_params.tx_noinputs_size);
+ CAmount selection_target = recipients_sum + not_input_fees;
- // Calculate the cost of change
- // Cost of change is the cost of creating the change output + cost of spending the change output in the future.
- // For creating the change output now, we use the effective feerate.
- // For spending the change output in the future, we use the discard feerate for now.
- // So cost of change = (change output size * effective feerate) + (size of spending change output * discard feerate)
- coin_selection_params.m_change_fee = coin_selection_params.m_effective_feerate.GetFee(coin_selection_params.change_output_size);
- coin_selection_params.m_cost_of_change = coin_selection_params.m_discard_feerate.GetFee(coin_selection_params.change_spend_size) + coin_selection_params.m_change_fee;
+ // Get available coins
+ std::vector<COutput> vAvailableCoins;
+ AvailableCoins(vAvailableCoins, &coin_control, 1, MAX_MONEY, MAX_MONEY, 0);
- coin_selection_params.m_subtract_fee_outputs = nSubtractFeeFromAmount != 0; // If we are doing subtract fee from recipient, don't use effective values
+ // Choose coins to use
+ CAmount inputs_sum = 0;
+ std::set<CInputCoin> setCoins;
+ if (!SelectCoins(vAvailableCoins, /* nTargetValue */ selection_target, setCoins, inputs_sum, coin_control, coin_selection_params))
+ {
+ error = _("Insufficient funds");
+ return false;
+ }
- // vouts to the payees
- if (!coin_selection_params.m_subtract_fee_outputs) {
- coin_selection_params.tx_noinputs_size = 11; // Static vsize overhead + outputs vsize. 4 nVersion, 4 nLocktime, 1 input count, 1 output count, 1 witness overhead (dummy, flag, stack size)
- }
- for (const auto& recipient : vecSend)
- {
- CTxOut txout(recipient.nAmount, recipient.scriptPubKey);
+ // Always make a change output
+ // We will reduce the fee from this change output later, and remove the output if it is too small.
+ const CAmount change_and_fee = inputs_sum - recipients_sum;
+ assert(change_and_fee >= 0);
+ CTxOut newTxOut(change_and_fee, scriptChange);
- // Include the fee cost for outputs.
- if (!coin_selection_params.m_subtract_fee_outputs) {
- coin_selection_params.tx_noinputs_size += ::GetSerializeSize(txout, PROTOCOL_VERSION);
- }
+ if (nChangePosInOut == -1)
+ {
+ // Insert change txn at random position:
+ nChangePosInOut = GetRandInt(txNew.vout.size()+1);
+ }
+ else if ((unsigned int)nChangePosInOut > txNew.vout.size())
+ {
+ error = _("Change index out of range");
+ return false;
+ }
- if (IsDust(txout, chain().relayDustFee()))
- {
- error = _("Transaction amount too small");
- return false;
- }
- txNew.vout.push_back(txout);
- }
+ assert(nChangePosInOut != -1);
+ auto change_position = txNew.vout.insert(txNew.vout.begin() + nChangePosInOut, newTxOut);
- // Include the fees for things that aren't inputs, excluding the change output
- const CAmount not_input_fees = coin_selection_params.m_effective_feerate.GetFee(coin_selection_params.tx_noinputs_size);
- CAmount nValueToSelect = nValue + not_input_fees;
+ // Shuffle selected coins and fill in final vin
+ std::vector<CInputCoin> selected_coins(setCoins.begin(), setCoins.end());
+ Shuffle(selected_coins.begin(), selected_coins.end(), FastRandomContext());
- // Choose coins to use
- CAmount inputs_sum = 0;
- setCoins.clear();
- if (!SelectCoins(vAvailableCoins, /* nTargetValue */ nValueToSelect, setCoins, inputs_sum, coin_control, coin_selection_params))
- {
- error = _("Insufficient funds");
- return false;
- }
-
- // Always make a change output
- // We will reduce the fee from this change output later, and remove the output if it is too small.
- const CAmount change_and_fee = inputs_sum - nValue;
- assert(change_and_fee >= 0);
- CTxOut newTxOut(change_and_fee, scriptChange);
+ // Note how the sequence number is set to non-maxint so that
+ // the nLockTime set above actually works.
+ //
+ // BIP125 defines opt-in RBF as any nSequence < maxint-1, so
+ // we use the highest possible value in that range (maxint-2)
+ // to avoid conflicting with other possible uses of nSequence,
+ // and in the spirit of "smallest possible change from prior
+ // behavior."
+ const uint32_t nSequence = coin_control.m_signal_bip125_rbf.value_or(m_signal_rbf) ? MAX_BIP125_RBF_SEQUENCE : (CTxIn::SEQUENCE_FINAL - 1);
+ for (const auto& coin : selected_coins) {
+ txNew.vin.push_back(CTxIn(coin.outpoint, CScript(), nSequence));
+ }
- if (nChangePosInOut == -1)
- {
- // Insert change txn at random position:
- nChangePosInOut = GetRandInt(txNew.vout.size()+1);
- }
- else if ((unsigned int)nChangePosInOut > txNew.vout.size())
- {
- error = _("Change index out of range");
- return false;
- }
+ // Calculate the transaction fee
+ TxSize tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), this, coin_control.fAllowWatchOnly);
+ int nBytes = tx_sizes.vsize;
+ if (nBytes < 0) {
+ error = _("Signing transaction failed");
+ return false;
+ }
+ nFeeRet = coin_selection_params.m_effective_feerate.GetFee(nBytes);
- assert(nChangePosInOut != -1);
- auto change_position = txNew.vout.insert(txNew.vout.begin() + nChangePosInOut, newTxOut);
+ // Subtract fee from the change output if not subtracting it from recipient outputs
+ CAmount fee_needed = nFeeRet;
+ if (!coin_selection_params.m_subtract_fee_outputs) {
+ change_position->nValue -= fee_needed;
+ }
- // Dummy fill vin for maximum size estimation
- //
- for (const auto& coin : setCoins) {
- txNew.vin.push_back(CTxIn(coin.outpoint,CScript()));
- }
+ // We want to drop the change to fees if:
+ // 1. The change output would be dust
+ // 2. The change is within the (almost) exact match window, i.e. it is less than or equal to the cost of the change output (cost_of_change)
+ CAmount change_amount = change_position->nValue;
+ if (IsDust(*change_position, coin_selection_params.m_discard_feerate) || change_amount <= coin_selection_params.m_cost_of_change)
+ {
+ nChangePosInOut = -1;
+ change_amount = 0;
+ txNew.vout.erase(change_position);
+
+ // Because we have dropped this change, the tx size and required fee will be different, so let's recalculate those
+ tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), this, coin_control.fAllowWatchOnly);
+ nBytes = tx_sizes.vsize;
+ fee_needed = coin_selection_params.m_effective_feerate.GetFee(nBytes);
+ }
- // Calculate the transaction fee
- tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), this, coin_control.fAllowWatchOnly);
- nBytes = tx_sizes.vsize;
- if (nBytes < 0) {
- error = _("Signing transaction failed");
- return false;
- }
- nFeeRet = coin_selection_params.m_effective_feerate.GetFee(nBytes);
+ // Update nFeeRet in case fee_needed changed due to dropping the change output
+ if (fee_needed <= change_and_fee - change_amount) {
+ nFeeRet = change_and_fee - change_amount;
+ }
- // Subtract fee from the change output if not subtrating it from recipient outputs
- CAmount fee_needed = nFeeRet;
- if (nSubtractFeeFromAmount == 0) {
- change_position->nValue -= fee_needed;
+ // Reduce output values for subtractFeeFromAmount
+ if (coin_selection_params.m_subtract_fee_outputs) {
+ CAmount to_reduce = fee_needed + change_amount - change_and_fee;
+ int i = 0;
+ bool fFirst = true;
+ for (const auto& recipient : vecSend)
+ {
+ if (i == nChangePosInOut) {
+ ++i;
}
+ CTxOut& txout = txNew.vout[i];
- // We want to drop the change to fees if:
- // 1. The change output would be dust
- // 2. The change is within the (almost) exact match window, i.e. it is less than or equal to the cost of the change output (cost_of_change)
- CAmount change_amount = change_position->nValue;
- if (IsDust(*change_position, coin_selection_params.m_discard_feerate) || change_amount <= coin_selection_params.m_cost_of_change)
+ if (recipient.fSubtractFeeFromAmount)
{
- nChangePosInOut = -1;
- change_amount = 0;
- txNew.vout.erase(change_position);
-
- // Because we have dropped this change, the tx size and required fee will be different, so let's recalculate those
- tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), this, coin_control.fAllowWatchOnly);
- nBytes = tx_sizes.vsize;
- fee_needed = coin_selection_params.m_effective_feerate.GetFee(nBytes);
- }
+ txout.nValue -= to_reduce / outputs_to_subtract_fee_from; // Subtract fee equally from each selected recipient
- // Update nFeeRet in case fee_needed changed due to dropping the change output
- if (fee_needed <= change_and_fee - change_amount) {
- nFeeRet = change_and_fee - change_amount;
- }
-
- // Reduce output values for subtractFeeFromAmount
- if (nSubtractFeeFromAmount != 0) {
- CAmount to_reduce = fee_needed + change_amount - change_and_fee;
- int i = 0;
- bool fFirst = true;
- for (const auto& recipient : vecSend)
+ if (fFirst) // first receiver pays the remainder not divisible by output count
{
- if (i == nChangePosInOut) {
- ++i;
- }
- CTxOut& txout = txNew.vout[i];
-
- if (recipient.fSubtractFeeFromAmount)
- {
- txout.nValue -= to_reduce / nSubtractFeeFromAmount; // Subtract fee equally from each selected recipient
-
- if (fFirst) // first receiver pays the remainder not divisible by output count
- {
- fFirst = false;
- txout.nValue -= to_reduce % nSubtractFeeFromAmount;
- }
-
- // Error if this output is reduced to be below dust
- if (IsDust(txout, chain().relayDustFee())) {
- if (txout.nValue < 0) {
- error = _("The transaction amount is too small to pay the fee");
- } else {
- error = _("The transaction amount is too small to send after the fee has been deducted");
- }
- return false;
- }
- }
- ++i;
+ fFirst = false;
+ txout.nValue -= to_reduce % outputs_to_subtract_fee_from;
}
- nFeeRet = fee_needed;
- }
- // Give up if change keypool ran out and change is required
- if (scriptChange.empty() && nChangePosInOut != -1) {
- return false;
+ // Error if this output is reduced to be below dust
+ if (IsDust(txout, chain().relayDustFee())) {
+ if (txout.nValue < 0) {
+ error = _("The transaction amount is too small to pay the fee");
+ } else {
+ error = _("The transaction amount is too small to send after the fee has been deducted");
+ }
+ return false;
+ }
}
+ ++i;
}
+ nFeeRet = fee_needed;
+ }
- // Shuffle selected coins and fill in final vin
- txNew.vin.clear();
- std::vector<CInputCoin> selected_coins(setCoins.begin(), setCoins.end());
- Shuffle(selected_coins.begin(), selected_coins.end(), FastRandomContext());
-
- // Note how the sequence number is set to non-maxint so that
- // the nLockTime set above actually works.
- //
- // BIP125 defines opt-in RBF as any nSequence < maxint-1, so
- // we use the highest possible value in that range (maxint-2)
- // to avoid conflicting with other possible uses of nSequence,
- // and in the spirit of "smallest possible change from prior
- // behavior."
- const uint32_t nSequence = coin_control.m_signal_bip125_rbf.value_or(m_signal_rbf) ? MAX_BIP125_RBF_SEQUENCE : (CTxIn::SEQUENCE_FINAL - 1);
- for (const auto& coin : selected_coins) {
- txNew.vin.push_back(CTxIn(coin.outpoint, CScript(), nSequence));
- }
+ // Give up if change keypool ran out and change is required
+ if (scriptChange.empty() && nChangePosInOut != -1) {
+ return false;
+ }
- if (sign && !SignTransaction(txNew)) {
- error = _("Signing transaction failed");
- return false;
- }
+ if (sign && !SignTransaction(txNew)) {
+ error = _("Signing transaction failed");
+ return false;
+ }
- // Return the constructed transaction data.
- tx = MakeTransactionRef(std::move(txNew));
+ // Return the constructed transaction data.
+ tx = MakeTransactionRef(std::move(txNew));
- // Limit size
- if ((sign && GetTransactionWeight(*tx) > MAX_STANDARD_TX_WEIGHT) ||
- (!sign && tx_sizes.weight > MAX_STANDARD_TX_WEIGHT))
- {
- error = _("Transaction too large");
- return false;
- }
+ // Limit size
+ if ((sign && GetTransactionWeight(*tx) > MAX_STANDARD_TX_WEIGHT) ||
+ (!sign && tx_sizes.weight > MAX_STANDARD_TX_WEIGHT))
+ {
+ error = _("Transaction too large");
+ return false;
}
if (nFeeRet > m_default_max_tx_fee) {
@@ -900,6 +879,18 @@ bool CWallet::CreateTransaction(
FeeCalculation& fee_calc_out,
bool sign)
{
+ if (vecSend.empty()) {
+ error = _("Transaction must have at least one recipient");
+ return false;
+ }
+
+ if (std::any_of(vecSend.cbegin(), vecSend.cend(), [](const auto& recipient){ return recipient.nAmount < 0; })) {
+ error = _("Transaction amounts must not be negative");
+ return false;
+ }
+
+ LOCK(cs_wallet);
+
int nChangePosIn = nChangePosInOut;
Assert(!tx); // tx is an out-param. TODO change the return type from bool to tx (or nullptr)
bool res = CreateTransactionInternal(vecSend, tx, nFeeRet, nChangePosInOut, error, coin_control, fee_calc_out, sign);
diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp
index 14c3578473..c65ebad52f 100644
--- a/src/wallet/test/coinselector_tests.cpp
+++ b/src/wallet/test/coinselector_tests.cpp
@@ -270,7 +270,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 1 * CENT, 2 * CENT, selection, value_ret));
}
- // Make sure that effective value is working in SelectCoinsMinConf when BnB is used
+ // Make sure that effective value is working in AttemptSelection when BnB is used
CoinSelectionParams coin_selection_params_bnb(/* change_output_size= */ 0,
/* change_spend_size= */ 0, /* effective_feerate= */ CFeeRate(3000),
/* long_term_feerate= */ CFeeRate(1000), /* discard_feerate= */ CFeeRate(1000),
@@ -280,14 +280,14 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
empty_wallet();
add_coin(1);
vCoins.at(0).nInputBytes = 40; // Make sure that it has a negative effective value. The next check should assert if this somehow got through. Otherwise it will fail
- BOOST_CHECK(!testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params_bnb));
+ BOOST_CHECK(!testWallet.AttemptSelection( 1 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params_bnb));
// Test fees subtracted from output:
empty_wallet();
add_coin(1 * CENT);
vCoins.at(0).nInputBytes = 40;
coin_selection_params_bnb.m_subtract_fee_outputs = true;
- BOOST_CHECK(testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params_bnb));
+ BOOST_CHECK(testWallet.AttemptSelection( 1 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params_bnb));
BOOST_CHECK_EQUAL(nValueRet, 1 * CENT);
// Make sure that can use BnB when there are preset inputs
@@ -322,24 +322,24 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
empty_wallet();
// with an empty wallet we can't even pay one cent
- BOOST_CHECK(!testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK(!testWallet.AttemptSelection( 1 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params));
add_coin(1*CENT, 4); // add a new 1 cent coin
// with a new 1 cent coin, we still can't find a mature 1 cent
- BOOST_CHECK(!testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK(!testWallet.AttemptSelection( 1 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params));
// but we can find a new 1 cent
- BOOST_CHECK( testWallet.SelectCoinsMinConf( 1 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK( testWallet.AttemptSelection( 1 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 1 * CENT);
add_coin(2*CENT); // add a mature 2 cent coin
// we can't make 3 cents of mature coins
- BOOST_CHECK(!testWallet.SelectCoinsMinConf( 3 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK(!testWallet.AttemptSelection( 3 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params));
// we can make 3 cents of new coins
- BOOST_CHECK( testWallet.SelectCoinsMinConf( 3 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK( testWallet.AttemptSelection( 3 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 3 * CENT);
add_coin(5*CENT); // add a mature 5 cent coin,
@@ -349,33 +349,33 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
// now we have new: 1+10=11 (of which 10 was self-sent), and mature: 2+5+20=27. total = 38
// we can't make 38 cents only if we disallow new coins:
- BOOST_CHECK(!testWallet.SelectCoinsMinConf(38 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK(!testWallet.AttemptSelection(38 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params));
// we can't even make 37 cents if we don't allow new coins even if they're from us
- BOOST_CHECK(!testWallet.SelectCoinsMinConf(38 * CENT, filter_standard_extra, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK(!testWallet.AttemptSelection(38 * CENT, filter_standard_extra, vCoins, setCoinsRet, nValueRet, coin_selection_params));
// but we can make 37 cents if we accept new coins from ourself
- BOOST_CHECK( testWallet.SelectCoinsMinConf(37 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK( testWallet.AttemptSelection(37 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 37 * CENT);
// and we can make 38 cents if we accept all new coins
- BOOST_CHECK( testWallet.SelectCoinsMinConf(38 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK( testWallet.AttemptSelection(38 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 38 * CENT);
// try making 34 cents from 1,2,5,10,20 - we can't do it exactly
- BOOST_CHECK( testWallet.SelectCoinsMinConf(34 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK( testWallet.AttemptSelection(34 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 35 * CENT); // but 35 cents is closest
BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U); // the best should be 20+10+5. it's incredibly unlikely the 1 or 2 got included (but possible)
// when we try making 7 cents, the smaller coins (1,2,5) are enough. We should see just 2+5
- BOOST_CHECK( testWallet.SelectCoinsMinConf( 7 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK( testWallet.AttemptSelection( 7 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 7 * CENT);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U);
// when we try making 8 cents, the smaller coins (1,2,5) are exactly enough.
- BOOST_CHECK( testWallet.SelectCoinsMinConf( 8 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK( testWallet.AttemptSelection( 8 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK(nValueRet == 8 * CENT);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U);
// when we try making 9 cents, no subset of smaller coins is enough, and we get the next bigger coin (10)
- BOOST_CHECK( testWallet.SelectCoinsMinConf( 9 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK( testWallet.AttemptSelection( 9 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 10 * CENT);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
@@ -389,30 +389,30 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
add_coin(30*CENT); // now we have 6+7+8+20+30 = 71 cents total
// check that we have 71 and not 72
- BOOST_CHECK( testWallet.SelectCoinsMinConf(71 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
- BOOST_CHECK(!testWallet.SelectCoinsMinConf(72 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK( testWallet.AttemptSelection(71 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK(!testWallet.AttemptSelection(72 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
// now try making 16 cents. the best smaller coins can do is 6+7+8 = 21; not as good at the next biggest coin, 20
- BOOST_CHECK( testWallet.SelectCoinsMinConf(16 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK( testWallet.AttemptSelection(16 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 20 * CENT); // we should get 20 in one coin
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
add_coin( 5*CENT); // now we have 5+6+7+8+20+30 = 75 cents total
// now if we try making 16 cents again, the smaller coins can make 5+6+7 = 18 cents, better than the next biggest coin, 20
- BOOST_CHECK( testWallet.SelectCoinsMinConf(16 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK( testWallet.AttemptSelection(16 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 18 * CENT); // we should get 18 in 3 coins
BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U);
add_coin( 18*CENT); // now we have 5+6+7+8+18+20+30
// and now if we try making 16 cents again, the smaller coins can make 5+6+7 = 18 cents, the same as the next biggest coin, 18
- BOOST_CHECK( testWallet.SelectCoinsMinConf(16 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK( testWallet.AttemptSelection(16 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 18 * CENT); // we should get 18 in 1 coin
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); // because in the event of a tie, the biggest coin wins
// now try making 11 cents. we should get 5+6
- BOOST_CHECK( testWallet.SelectCoinsMinConf(11 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK( testWallet.AttemptSelection(11 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 11 * CENT);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U);
@@ -421,11 +421,11 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
add_coin( 2*COIN);
add_coin( 3*COIN);
add_coin( 4*COIN); // now we have 5+6+7+8+18+20+30+100+200+300+400 = 1094 cents
- BOOST_CHECK( testWallet.SelectCoinsMinConf(95 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK( testWallet.AttemptSelection(95 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 1 * COIN); // we should get 1 BTC in 1 coin
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
- BOOST_CHECK( testWallet.SelectCoinsMinConf(195 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK( testWallet.AttemptSelection(195 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 2 * COIN); // we should get 2 BTC in 1 coin
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
@@ -440,14 +440,14 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
// try making 1 * MIN_CHANGE from the 1.5 * MIN_CHANGE
// we'll get change smaller than MIN_CHANGE whatever happens, so can expect MIN_CHANGE exactly
- BOOST_CHECK( testWallet.SelectCoinsMinConf(MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK( testWallet.AttemptSelection(MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, MIN_CHANGE);
// but if we add a bigger coin, small change is avoided
add_coin(1111*MIN_CHANGE);
// try making 1 from 0.1 + 0.2 + 0.3 + 0.4 + 0.5 + 1111 = 1112.5
- BOOST_CHECK( testWallet.SelectCoinsMinConf(1 * MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK( testWallet.AttemptSelection(1 * MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 1 * MIN_CHANGE); // we should get the exact amount
// if we add more small coins:
@@ -455,7 +455,7 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
add_coin(MIN_CHANGE * 7 / 10);
// and try again to make 1.0 * MIN_CHANGE
- BOOST_CHECK( testWallet.SelectCoinsMinConf(1 * MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK( testWallet.AttemptSelection(1 * MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 1 * MIN_CHANGE); // we should get the exact amount
// run the 'mtgox' test (see https://blockexplorer.com/tx/29a3efd3ef04f9153d47a990bd7b048a4b2d213daaa5fb8ed670fb85f13bdbcf)
@@ -464,7 +464,7 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
for (int j = 0; j < 20; j++)
add_coin(50000 * COIN);
- BOOST_CHECK( testWallet.SelectCoinsMinConf(500000 * COIN, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK( testWallet.AttemptSelection(500000 * COIN, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 500000 * COIN); // we should get the exact amount
BOOST_CHECK_EQUAL(setCoinsRet.size(), 10U); // in ten coins
@@ -477,7 +477,7 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
add_coin(MIN_CHANGE * 6 / 10);
add_coin(MIN_CHANGE * 7 / 10);
add_coin(1111 * MIN_CHANGE);
- BOOST_CHECK( testWallet.SelectCoinsMinConf(1 * MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK( testWallet.AttemptSelection(1 * MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 1111 * MIN_CHANGE); // we get the bigger coin
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
@@ -487,7 +487,7 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
add_coin(MIN_CHANGE * 6 / 10);
add_coin(MIN_CHANGE * 8 / 10);
add_coin(1111 * MIN_CHANGE);
- BOOST_CHECK( testWallet.SelectCoinsMinConf(MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK( testWallet.AttemptSelection(MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, MIN_CHANGE); // we should get the exact amount
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U); // in two coins 0.4+0.6
@@ -498,12 +498,12 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
add_coin(MIN_CHANGE * 100);
// trying to make 100.01 from these three coins
- BOOST_CHECK(testWallet.SelectCoinsMinConf(MIN_CHANGE * 10001 / 100, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK(testWallet.AttemptSelection(MIN_CHANGE * 10001 / 100, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, MIN_CHANGE * 10105 / 100); // we should get all coins
BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U);
// but if we try to make 99.9, we should take the bigger of the two small coins to avoid small change
- BOOST_CHECK(testWallet.SelectCoinsMinConf(MIN_CHANGE * 9990 / 100, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK(testWallet.AttemptSelection(MIN_CHANGE * 9990 / 100, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 101 * MIN_CHANGE);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U);
}
@@ -517,7 +517,7 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
// We only create the wallet once to save time, but we still run the coin selection RUN_TESTS times.
for (int i = 0; i < RUN_TESTS; i++) {
- BOOST_CHECK(testWallet.SelectCoinsMinConf(2000, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK(testWallet.AttemptSelection(2000, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
if (amt - 2000 < MIN_CHANGE) {
// needs more than one input:
@@ -602,7 +602,7 @@ BOOST_AUTO_TEST_CASE(ApproximateBestSubset)
add_coin(1000 * COIN);
add_coin(3 * COIN);
- BOOST_CHECK(testWallet.SelectCoinsMinConf(1003 * COIN, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK(testWallet.AttemptSelection(1003 * COIN, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 1003 * COIN);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U);
diff --git a/src/wallet/test/psbt_wallet_tests.cpp b/src/wallet/test/psbt_wallet_tests.cpp
index ce7e661b67..1cefa386b7 100644
--- a/src/wallet/test/psbt_wallet_tests.cpp
+++ b/src/wallet/test/psbt_wallet_tests.cpp
@@ -71,7 +71,7 @@ BOOST_AUTO_TEST_CASE(psbt_updater_test)
// Try to sign the mutated input
SignatureData sigdata;
- BOOST_CHECK(spk_man->FillPSBT(psbtx, SIGHASH_ALL, true, true) != TransactionError::OK);
+ BOOST_CHECK(spk_man->FillPSBT(psbtx, PrecomputePSBTData(psbtx), SIGHASH_ALL, true, true) != TransactionError::OK);
}
BOOST_AUTO_TEST_CASE(parse_hd_keypath)
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 6a791748b4..a0070b8dd3 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -83,17 +83,17 @@ static void AddKey(CWallet& wallet, const CKey& key)
BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
{
// Cap last block file size, and mine new block in a new block file.
- CBlockIndex* oldTip = ::ChainActive().Tip();
+ CBlockIndex* oldTip = m_node.chainman->ActiveChain().Tip();
GetBlockFileInfo(oldTip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE;
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
- CBlockIndex* newTip = ::ChainActive().Tip();
+ CBlockIndex* newTip = m_node.chainman->ActiveChain().Tip();
// Verify ScanForWalletTransactions fails to read an unknown start block.
{
CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
- wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
+ wallet.SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
}
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(wallet);
@@ -112,7 +112,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
- wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
+ wallet.SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
}
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(wallet);
@@ -138,7 +138,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
- wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
+ wallet.SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
}
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(wallet);
@@ -163,7 +163,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
- wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
+ wallet.SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
}
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(wallet);
@@ -180,10 +180,10 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
{
// Cap last block file size, and mine new block in a new block file.
- CBlockIndex* oldTip = ::ChainActive().Tip();
+ CBlockIndex* oldTip = m_node.chainman->ActiveChain().Tip();
GetBlockFileInfo(oldTip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE;
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
- CBlockIndex* newTip = ::ChainActive().Tip();
+ CBlockIndex* newTip = m_node.chainman->ActiveChain().Tip();
// Prune the older block file.
{
@@ -242,7 +242,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
{
// Create two blocks with same timestamp to verify that importwallet rescan
// will pick up both blocks, not just the first.
- const int64_t BLOCK_TIME = ::ChainActive().Tip()->GetBlockTimeMax() + 5;
+ const int64_t BLOCK_TIME = m_node.chainman->ActiveChain().Tip()->GetBlockTimeMax() + 5;
SetMockTime(BLOCK_TIME);
m_coinbase_txns.emplace_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
m_coinbase_txns.emplace_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
@@ -265,7 +265,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
spk_man->AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey());
AddWallet(wallet);
- wallet->SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
+ wallet->SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
}
JSONRPCRequest request;
request.params.setArray();
@@ -286,7 +286,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
request.params.setArray();
request.params.push_back(backup_file);
AddWallet(wallet);
- wallet->SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
+ wallet->SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
::importwallet().HandleRequest(request);
RemoveWallet(wallet, std::nullopt);
@@ -313,9 +313,9 @@ BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup)
CWalletTx wtx(&wallet, m_coinbase_txns.back());
LOCK2(wallet.cs_wallet, spk_man->cs_KeyStore);
- wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
+ wallet.SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
- CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, ::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash(), 0);
+ CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash(), 0);
wtx.m_confirm = confirm;
// Call GetImmatureCredit() once before adding the key to the wallet to
@@ -483,16 +483,16 @@ public:
wallet = std::make_unique<CWallet>(m_node.chain.get(), "", CreateMockWalletDatabase());
{
LOCK2(wallet->cs_wallet, ::cs_main);
- wallet->SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
+ wallet->SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
}
wallet->LoadWallet();
AddKey(*wallet, coinbaseKey);
WalletRescanReserver reserver(*wallet);
reserver.reserve();
- CWallet::ScanResult result = wallet->ScanForWalletTransactions(::ChainActive().Genesis()->GetBlockHash(), 0 /* start_height */, {} /* max_height */, reserver, false /* update */);
+ CWallet::ScanResult result = wallet->ScanForWalletTransactions(m_node.chainman->ActiveChain().Genesis()->GetBlockHash(), 0 /* start_height */, {} /* max_height */, reserver, false /* update */);
BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::SUCCESS);
- BOOST_CHECK_EQUAL(result.last_scanned_block, ::ChainActive().Tip()->GetBlockHash());
- BOOST_CHECK_EQUAL(*result.last_scanned_height, ::ChainActive().Height());
+ BOOST_CHECK_EQUAL(result.last_scanned_block, m_node.chainman->ActiveChain().Tip()->GetBlockHash());
+ BOOST_CHECK_EQUAL(*result.last_scanned_height, m_node.chainman->ActiveChain().Height());
BOOST_CHECK(result.last_failed_block.IsNull());
}
@@ -521,10 +521,10 @@ public:
CreateAndProcessBlock({CMutableTransaction(blocktx)}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
LOCK(wallet->cs_wallet);
- wallet->SetLastBlockProcessed(wallet->GetLastBlockHeight() + 1, ::ChainActive().Tip()->GetBlockHash());
+ wallet->SetLastBlockProcessed(wallet->GetLastBlockHeight() + 1, m_node.chainman->ActiveChain().Tip()->GetBlockHash());
auto it = wallet->mapWallet.find(tx->GetHash());
BOOST_CHECK(it != wallet->mapWallet.end());
- CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, ::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash(), 1);
+ CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash(), 1);
it->second.m_confirm = confirm;
return it->second;
}
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 4b6630de3c..256faf2b23 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -8,6 +8,7 @@
#include <chain.h>
#include <consensus/consensus.h>
#include <consensus/validation.h>
+#include <external_signer.h>
#include <fs.h>
#include <interfaces/chain.h>
#include <interfaces/wallet.h>
@@ -1807,7 +1808,7 @@ bool CWallet::SignTransaction(CMutableTransaction& tx) const
coins[input.prevout] = Coin(wtx.tx->vout[input.prevout.n], wtx.m_confirm.block_height, wtx.IsCoinBase());
}
std::map<int, std::string> input_errors;
- return SignTransaction(tx, coins, SIGHASH_ALL, input_errors);
+ return SignTransaction(tx, coins, SIGHASH_DEFAULT, input_errors);
}
bool CWallet::SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, std::string>& input_errors) const
@@ -1830,6 +1831,7 @@ TransactionError CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bool& comp
if (n_signed) {
*n_signed = 0;
}
+ const PrecomputedTransactionData txdata = PrecomputePSBTData(psbtx);
LOCK(cs_wallet);
// Get all of the previous transactions
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
@@ -1856,7 +1858,7 @@ TransactionError CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bool& comp
// Fill in information from ScriptPubKeyMans
for (ScriptPubKeyMan* spk_man : GetAllScriptPubKeyMans()) {
int n_signed_this_spkm = 0;
- TransactionError res = spk_man->FillPSBT(psbtx, sighash_type, sign, bip32derivs, &n_signed_this_spkm);
+ TransactionError res = spk_man->FillPSBT(psbtx, txdata, sighash_type, sign, bip32derivs, &n_signed_this_spkm);
if (res != TransactionError::OK) {
return res;
}
@@ -2215,7 +2217,6 @@ void ReserveDestination::ReturnDestination()
bool CWallet::DisplayAddress(const CTxDestination& dest)
{
-#ifdef ENABLE_EXTERNAL_SIGNER
CScript scriptPubKey = GetScriptForDestination(dest);
const auto spk_man = GetScriptPubKeyMan(scriptPubKey);
if (spk_man == nullptr) {
@@ -2227,9 +2228,6 @@ bool CWallet::DisplayAddress(const CTxDestination& dest)
}
ExternalSigner signer = ExternalSignerScriptPubKeyMan::GetExternalSigner();
return signer_spk_man->DisplayAddress(scriptPubKey, signer);
-#else
- return false;
-#endif
}
void CWallet::LockCoin(const COutPoint& output)
@@ -3063,12 +3061,8 @@ void CWallet::ConnectScriptPubKeyManNotifiers()
void CWallet::LoadDescriptorScriptPubKeyMan(uint256 id, WalletDescriptor& desc)
{
if (IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER)) {
-#ifdef ENABLE_EXTERNAL_SIGNER
auto spk_manager = std::unique_ptr<ScriptPubKeyMan>(new ExternalSignerScriptPubKeyMan(*this, desc));
m_spk_managers[id] = std::move(spk_manager);
-#else
- throw std::runtime_error(std::string(__func__) + ": Compiled without external signing support (required for external signing)");
-#endif
} else {
auto spk_manager = std::unique_ptr<ScriptPubKeyMan>(new DescriptorScriptPubKeyMan(*this, desc));
m_spk_managers[id] = std::move(spk_manager);
@@ -3108,7 +3102,6 @@ void CWallet::SetupDescriptorScriptPubKeyMans()
}
}
} else {
-#ifdef ENABLE_EXTERNAL_SIGNER
ExternalSigner signer = ExternalSignerScriptPubKeyMan::GetExternalSigner();
// TODO: add account parameter
@@ -3135,9 +3128,6 @@ void CWallet::SetupDescriptorScriptPubKeyMans()
AddActiveScriptPubKeyMan(id, t, internal);
}
}
-#else
- throw std::runtime_error(std::string(__func__) + ": Compiled without external signing support (required for external signing)");
-#endif // ENABLE_EXTERNAL_SIGNER
}
}
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 788a901f95..66f39edb4d 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -21,7 +21,6 @@
#include <validationinterface.h>
#include <wallet/coinselection.h>
#include <wallet/crypter.h>
-#include <external_signer.h>
#include <wallet/receive.h>
#include <wallet/scriptpubkeyman.h>
#include <wallet/spend.h>
@@ -326,7 +325,7 @@ private:
// ScriptPubKeyMan::GetID. In many cases it will be the hash of an internal structure
std::map<uint256, std::unique_ptr<ScriptPubKeyMan>> m_spk_managers;
- bool CreateTransactionInternal(const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, const CCoinControl& coin_control, FeeCalculation& fee_calc_out, bool sign);
+ bool CreateTransactionInternal(const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, const CCoinControl& coin_control, FeeCalculation& fee_calc_out, bool sign) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/**
* Catch wallet up to current chain, scanning new blocks, updating the best
@@ -445,7 +444,7 @@ public:
* param@[out] setCoinsRet Populated with the coins selected if successful.
* param@[out] nValueRet Used to return the total value of selected coins.
*/
- bool SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<COutput> coins,
+ bool AttemptSelection(const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<COutput> coins,
std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params) const;
bool IsSpent(const uint256& hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index c06b319b0b..24d5351945 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -712,6 +712,13 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
}
}
+#ifndef ENABLE_EXTERNAL_SIGNER
+ if (pwallet->IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER)) {
+ pwallet->WalletLogPrintf("Error: External signer wallet being loaded without external signer support compiled\n");
+ return DBErrors::TOO_NEW;
+ }
+#endif
+
// Get cursor
if (!m_batch->StartCursor())
{
diff --git a/src/zmq/zmqutil.cpp b/src/zmq/zmqutil.cpp
index f07a4ae9fd..b0f12388e5 100644
--- a/src/zmq/zmqutil.cpp
+++ b/src/zmq/zmqutil.cpp
@@ -5,10 +5,12 @@
#include <zmq/zmqutil.h>
#include <logging.h>
-
#include <zmq.h>
-void zmqError(const char* str)
+#include <cerrno>
+#include <string>
+
+void zmqError(const std::string& str)
{
- LogPrint(BCLog::ZMQ, "zmq: Error: %s, errno=%s\n", str, zmq_strerror(errno));
+ LogPrint(BCLog::ZMQ, "zmq: Error: %s, msg: %s\n", str, zmq_strerror(errno));
}
diff --git a/src/zmq/zmqutil.h b/src/zmq/zmqutil.h
index 4c1df5d6db..90c0b00edb 100644
--- a/src/zmq/zmqutil.h
+++ b/src/zmq/zmqutil.h
@@ -5,6 +5,8 @@
#ifndef BITCOIN_ZMQ_ZMQUTIL_H
#define BITCOIN_ZMQ_ZMQUTIL_H
-void zmqError(const char* str);
+#include <string>
+
+void zmqError(const std::string& str);
#endif // BITCOIN_ZMQ_ZMQUTIL_H
diff --git a/test/functional/feature_anchors.py b/test/functional/feature_anchors.py
index a60a723b3e..24bb02bc90 100755
--- a/test/functional/feature_anchors.py
+++ b/test/functional/feature_anchors.py
@@ -2,7 +2,7 @@
# 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.
-"""Test Anchors functionality"""
+"""Test block-relay-only anchors functionality"""
import os
@@ -10,6 +10,9 @@ from test_framework.p2p import P2PInterface
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal
+INBOUND_CONNECTIONS = 5
+BLOCK_RELAY_CONNECTIONS = 2
+
def check_node_connections(*, node, num_in, num_out):
info = node.getnetworkinfo()
@@ -25,19 +28,26 @@ class AnchorsTest(BitcoinTestFramework):
self.setup_nodes()
def run_test(self):
- self.log.info("Add 2 block-relay-only connections to node 0")
- for i in range(2):
+ node_anchors_path = os.path.join(
+ self.nodes[0].datadir, "regtest", "anchors.dat"
+ )
+
+ self.log.info("When node starts, check if anchors.dat doesn't exist")
+ assert not os.path.exists(node_anchors_path)
+
+ self.log.info(f"Add {BLOCK_RELAY_CONNECTIONS} block-relay-only connections to node")
+ for i in range(BLOCK_RELAY_CONNECTIONS):
self.log.debug(f"block-relay-only: {i}")
self.nodes[0].add_outbound_p2p_connection(
P2PInterface(), p2p_idx=i, connection_type="block-relay-only"
)
- self.log.info("Add 5 inbound connections to node 0")
- for i in range(5):
+ self.log.info(f"Add {INBOUND_CONNECTIONS} inbound connections to node")
+ for i in range(INBOUND_CONNECTIONS):
self.log.debug(f"inbound: {i}")
self.nodes[0].add_p2p_connection(P2PInterface())
- self.log.info("Check node 0 connections")
+ self.log.info("Check node connections")
check_node_connections(node=self.nodes[0], num_in=5, num_out=2)
# 127.0.0.1
@@ -57,14 +67,10 @@ class AnchorsTest(BitcoinTestFramework):
self.log.info("Stop node 0")
self.stop_node(0)
- node0_anchors_path = os.path.join(
- self.nodes[0].datadir, "regtest", "anchors.dat"
- )
-
# It should contain only the block-relay-only addresses
self.log.info("Check the addresses in anchors.dat")
- with open(node0_anchors_path, "rb") as file_handler:
+ with open(node_anchors_path, "rb") as file_handler:
anchors = file_handler.read().hex()
for port in block_relay_nodes_port:
@@ -74,11 +80,11 @@ class AnchorsTest(BitcoinTestFramework):
ip_port = ip + port
assert ip_port not in anchors
- self.log.info("Start node 0")
+ self.log.info("Start node")
self.start_node(0)
self.log.info("When node starts, check if anchors.dat doesn't exist anymore")
- assert not os.path.exists(node0_anchors_path)
+ assert not os.path.exists(node_anchors_path)
if __name__ == "__main__":
diff --git a/test/functional/feature_block.py b/test/functional/feature_block.py
index 158efb52c9..389db73bc9 100755
--- a/test/functional/feature_block.py
+++ b/test/functional/feature_block.py
@@ -591,6 +591,8 @@ class FullBlockTest(BitcoinTestFramework):
b44.hashPrevBlock = self.tip.sha256
b44.nBits = 0x207fffff
b44.vtx.append(coinbase)
+ tx = self.create_and_sign_transaction(out[14], 1)
+ b44.vtx.append(tx)
b44.hashMerkleRoot = b44.calc_merkle_root()
b44.solve()
self.tip = b44
@@ -678,7 +680,7 @@ class FullBlockTest(BitcoinTestFramework):
# Test block timestamps
# -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15)
# \-> b54 (15)
- #
+ # -> b44 (14)\-> b48 ()
self.move_tip(43)
b53 = self.next_block(53, spend=out[14])
self.send_blocks([b53], False)
@@ -698,6 +700,21 @@ class FullBlockTest(BitcoinTestFramework):
self.send_blocks([b55], True)
self.save_spendable_output()
+ # The block which was previously rejected because of being "too far(3 hours)" must be accepted 2 hours later.
+ # The new block is only 1 hour into future now and we must reorg onto to the new longer chain.
+ # The new bestblock b48p is invalidated manually.
+ # -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15)
+ # \-> b54 (15)
+ # -> b44 (14)\-> b48 () -> b48p ()
+ self.log.info("Accept a previously rejected future block at a later time")
+ node.setmocktime(int(time.time()) + 2*60*60)
+ self.move_tip(48)
+ self.block_heights[b48.sha256] = self.block_heights[b44.sha256] + 1 # b48 is a parent of b44
+ b48p = self.next_block("48p")
+ self.send_blocks([b48, b48p], success=True) # Reorg to the longer chain
+ node.invalidateblock(b48p.hash) # mark b48p as invalid
+ node.setmocktime(0)
+
# Test Merkle tree malleability
#
# -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57p2 (16)
@@ -1308,7 +1325,7 @@ class FullBlockTest(BitcoinTestFramework):
return create_tx_with_script(spend_tx, n, amount=value, script_pub_key=script)
# sign a transaction, using the key we know about
- # this signs input 0 in tx, which is assumed to be spending output n in spend_tx
+ # this signs input 0 in tx, which is assumed to be spending output 0 in spend_tx
def sign_tx(self, tx, spend_tx):
scriptPubKey = bytearray(spend_tx.vout[0].scriptPubKey)
if (scriptPubKey[0] == OP_TRUE): # an anyone-can-spend
diff --git a/test/functional/feature_nulldummy.py b/test/functional/feature_nulldummy.py
index 02e387c7ee..f467626801 100755
--- a/test/functional/feature_nulldummy.py
+++ b/test/functional/feature_nulldummy.py
@@ -28,6 +28,7 @@ from test_framework.util import assert_equal, assert_raises_rpc_error
NULLDUMMY_ERROR = "non-mandatory-script-verify-flag (Dummy CHECKMULTISIG argument must be zero)"
+
def trueDummy(tx):
scriptSig = CScript(tx.vin[0].scriptSig)
newscript = []
@@ -40,18 +41,17 @@ def trueDummy(tx):
tx.vin[0].scriptSig = CScript(newscript)
tx.rehash()
-class NULLDUMMYTest(BitcoinTestFramework):
+class NULLDUMMYTest(BitcoinTestFramework):
def set_test_params(self):
- # Need two nodes so GBT (getblocktemplate) doesn't complain that it's not connected.
- self.num_nodes = 2
+ self.num_nodes = 1
self.setup_clean_chain = True
# This script tests NULLDUMMY activation, which is part of the 'segwit' deployment, so we go through
# normal segwit activation here (and don't use the default always-on behaviour).
self.extra_args = [[
f'-segwitheight={COINBASE_MATURITY + 5}',
'-addresstype=legacy',
- ]] * 2
+ ]]
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
diff --git a/test/functional/feature_rbf.py b/test/functional/feature_rbf.py
index 344db5f652..b99b64e16d 100755
--- a/test/functional/feature_rbf.py
+++ b/test/functional/feature_rbf.py
@@ -7,17 +7,20 @@
from decimal import Decimal
from test_framework.blocktools import COINBASE_MATURITY
-from test_framework.messages import COIN, COutPoint, CTransaction, CTxIn, CTxOut
+from test_framework.messages import COIN, COutPoint, CTransaction, CTxIn, CTxOut, BIP125_SEQUENCE_NUMBER
from test_framework.script import CScript, OP_DROP
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, assert_raises_rpc_error, satoshi_round
from test_framework.script_util import DUMMY_P2WPKH_SCRIPT, DUMMY_2_P2WPKH_SCRIPT
+from test_framework.wallet import MiniWallet
MAX_REPLACEMENT_LIMIT = 100
+
def txToHex(tx):
return tx.serialize().hex()
+
def make_utxo(node, amount, confirmed=True, scriptPubKey=DUMMY_P2WPKH_SCRIPT):
"""Create a txout with a given amount and scriptPubKey
@@ -26,12 +29,12 @@ def make_utxo(node, amount, confirmed=True, scriptPubKey=DUMMY_P2WPKH_SCRIPT):
confirmed - txouts created will be confirmed in the blockchain;
unconfirmed otherwise.
"""
- fee = 1*COIN
- while node.getbalance() < satoshi_round((amount + fee)/COIN):
+ fee = 1 * COIN
+ while node.getbalance() < satoshi_round((amount + fee) / COIN):
node.generate(COINBASE_MATURITY)
new_addr = node.getnewaddress()
- txid = node.sendtoaddress(new_addr, satoshi_round((amount+fee)/COIN))
+ txid = node.sendtoaddress(new_addr, satoshi_round((amount + fee) / COIN))
tx1 = node.getrawtransaction(txid, 1)
txid = int(txid, 16)
i, _ = next(filter(lambda vout: new_addr == vout[1]['scriptPubKey']['address'], enumerate(tx1['vout'])))
@@ -78,10 +81,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
self.skip_if_no_wallet()
def run_test(self):
- # Leave IBD
- self.nodes[0].generate(1)
-
- make_utxo(self.nodes[0], 1*COIN)
+ make_utxo(self.nodes[0], 1 * COIN)
# Ensure nodes are synced
self.sync_all()
@@ -116,11 +116,14 @@ class ReplaceByFeeTest(BitcoinTestFramework):
self.log.info("Running test prioritised transactions...")
self.test_prioritised_transactions()
+ self.log.info("Running test no inherited signaling...")
+ self.test_no_inherited_signaling()
+
self.log.info("Passed")
def test_simple_doublespend(self):
"""Simple doublespend"""
- tx0_outpoint = make_utxo(self.nodes[0], int(1.1*COIN))
+ tx0_outpoint = make_utxo(self.nodes[0], int(1.1 * COIN))
# make_utxo may have generated a bunch of blocks, so we need to sync
# before we can spend the coins generated, or else the resulting
@@ -162,14 +165,14 @@ class ReplaceByFeeTest(BitcoinTestFramework):
def test_doublespend_chain(self):
"""Doublespend of a long chain"""
- initial_nValue = 50*COIN
+ initial_nValue = 50 * COIN
tx0_outpoint = make_utxo(self.nodes[0], initial_nValue)
prevout = tx0_outpoint
remaining_value = initial_nValue
chain_txids = []
- while remaining_value > 10*COIN:
- remaining_value -= 1*COIN
+ while remaining_value > 10 * COIN:
+ remaining_value -= 1 * COIN
tx = CTransaction()
tx.vin = [CTxIn(prevout, nSequence=0)]
tx.vout = [CTxOut(remaining_value, CScript([1, OP_DROP] * 15 + [1]))]
@@ -202,10 +205,10 @@ class ReplaceByFeeTest(BitcoinTestFramework):
def test_doublespend_tree(self):
"""Doublespend of a big tree of transactions"""
- initial_nValue = 50*COIN
+ initial_nValue = 50 * COIN
tx0_outpoint = make_utxo(self.nodes[0], initial_nValue)
- def branch(prevout, initial_value, max_txs, tree_width=5, fee=0.0001*COIN, _total_txs=None):
+ def branch(prevout, initial_value, max_txs, tree_width=5, fee=0.0001 * COIN, _total_txs=None):
if _total_txs is None:
_total_txs = [0]
if _total_txs[0] >= max_txs:
@@ -236,7 +239,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
_total_txs=_total_txs):
yield x
- fee = int(0.0001*COIN)
+ fee = int(0.0001 * COIN)
n = MAX_REPLACEMENT_LIMIT
tree_txs = list(branch(tx0_outpoint, initial_nValue, n, fee=fee))
assert_equal(len(tree_txs), n)
@@ -264,8 +267,8 @@ class ReplaceByFeeTest(BitcoinTestFramework):
# Try again, but with more total transactions than the "max txs
# double-spent at once" anti-DoS limit.
- for n in (MAX_REPLACEMENT_LIMIT+1, MAX_REPLACEMENT_LIMIT*2):
- fee = int(0.0001*COIN)
+ for n in (MAX_REPLACEMENT_LIMIT + 1, MAX_REPLACEMENT_LIMIT * 2):
+ fee = int(0.0001 * COIN)
tx0_outpoint = make_utxo(self.nodes[0], initial_nValue)
tree_txs = list(branch(tx0_outpoint, initial_nValue, n, fee=fee))
assert_equal(len(tree_txs), n)
@@ -283,7 +286,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
def test_replacement_feeperkb(self):
"""Replacement requires fee-per-KB to be higher"""
- tx0_outpoint = make_utxo(self.nodes[0], int(1.1*COIN))
+ tx0_outpoint = make_utxo(self.nodes[0], int(1.1 * COIN))
tx1a = CTransaction()
tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0)]
@@ -295,7 +298,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
# rejected.
tx1b = CTransaction()
tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)]
- tx1b.vout = [CTxOut(int(0.001*COIN), CScript([b'a'*999000]))]
+ tx1b.vout = [CTxOut(int(0.001 * COIN), CScript([b'a' * 999000]))]
tx1b_hex = txToHex(tx1b)
# This will raise an exception due to insufficient fee
@@ -303,8 +306,8 @@ class ReplaceByFeeTest(BitcoinTestFramework):
def test_spends_of_conflicting_outputs(self):
"""Replacements that spend conflicting tx outputs are rejected"""
- utxo1 = make_utxo(self.nodes[0], int(1.2*COIN))
- utxo2 = make_utxo(self.nodes[0], 3*COIN)
+ utxo1 = make_utxo(self.nodes[0], int(1.2 * COIN))
+ utxo2 = make_utxo(self.nodes[0], 3 * COIN)
tx1a = CTransaction()
tx1a.vin = [CTxIn(utxo1, nSequence=0)]
@@ -343,8 +346,8 @@ class ReplaceByFeeTest(BitcoinTestFramework):
def test_new_unconfirmed_inputs(self):
"""Replacements that add new unconfirmed inputs are rejected"""
- confirmed_utxo = make_utxo(self.nodes[0], int(1.1*COIN))
- unconfirmed_utxo = make_utxo(self.nodes[0], int(0.1*COIN), False)
+ confirmed_utxo = make_utxo(self.nodes[0], int(1.1 * COIN))
+ unconfirmed_utxo = make_utxo(self.nodes[0], int(0.1 * COIN), False)
tx1 = CTransaction()
tx1.vin = [CTxIn(confirmed_utxo)]
@@ -366,13 +369,13 @@ class ReplaceByFeeTest(BitcoinTestFramework):
# transactions
# Start by creating a single transaction with many outputs
- initial_nValue = 10*COIN
+ initial_nValue = 10 * COIN
utxo = make_utxo(self.nodes[0], initial_nValue)
- fee = int(0.0001*COIN)
- split_value = int((initial_nValue-fee)/(MAX_REPLACEMENT_LIMIT+1))
+ fee = int(0.0001 * COIN)
+ split_value = int((initial_nValue - fee) / (MAX_REPLACEMENT_LIMIT + 1))
outputs = []
- for _ in range(MAX_REPLACEMENT_LIMIT+1):
+ for _ in range(MAX_REPLACEMENT_LIMIT + 1):
outputs.append(CTxOut(split_value, CScript([1])))
splitting_tx = CTransaction()
@@ -384,7 +387,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
txid = int(txid, 16)
# Now spend each of those outputs individually
- for i in range(MAX_REPLACEMENT_LIMIT+1):
+ for i in range(MAX_REPLACEMENT_LIMIT + 1):
tx_i = CTransaction()
tx_i.vin = [CTxIn(COutPoint(txid, i), nSequence=0)]
tx_i.vout = [CTxOut(split_value - fee, DUMMY_P2WPKH_SCRIPT)]
@@ -394,9 +397,9 @@ class ReplaceByFeeTest(BitcoinTestFramework):
# Now create doublespend of the whole lot; should fail.
# Need a big enough fee to cover all spending transactions and have
# a higher fee rate
- double_spend_value = (split_value-100*fee)*(MAX_REPLACEMENT_LIMIT+1)
+ double_spend_value = (split_value - 100 * fee) * (MAX_REPLACEMENT_LIMIT + 1)
inputs = []
- for i in range(MAX_REPLACEMENT_LIMIT+1):
+ for i in range(MAX_REPLACEMENT_LIMIT + 1):
inputs.append(CTxIn(COutPoint(txid, i), nSequence=0))
double_tx = CTransaction()
double_tx.vin = inputs
@@ -415,7 +418,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
def test_opt_in(self):
"""Replacing should only work if orig tx opted in"""
- tx0_outpoint = make_utxo(self.nodes[0], int(1.1*COIN))
+ tx0_outpoint = make_utxo(self.nodes[0], int(1.1 * COIN))
# Create a non-opting in transaction
tx1a = CTransaction()
@@ -436,7 +439,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
# This will raise an exception
assert_raises_rpc_error(-26, "txn-mempool-conflict", self.nodes[0].sendrawtransaction, tx1b_hex, 0)
- tx1_outpoint = make_utxo(self.nodes[0], int(1.1*COIN))
+ tx1_outpoint = make_utxo(self.nodes[0], int(1.1 * COIN))
# Create a different non-opting in transaction
tx2a = CTransaction()
@@ -464,7 +467,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx3a = CTransaction()
tx3a.vin = [CTxIn(COutPoint(tx1a_txid, 0), nSequence=0xffffffff),
CTxIn(COutPoint(tx2a_txid, 0), nSequence=0xfffffffd)]
- tx3a.vout = [CTxOut(int(0.9*COIN), CScript([b'c'])), CTxOut(int(0.9*COIN), CScript([b'd']))]
+ tx3a.vout = [CTxOut(int(0.9 * COIN), CScript([b'c'])), CTxOut(int(0.9 * COIN), CScript([b'd']))]
tx3a_hex = txToHex(tx3a)
tx3a_txid = self.nodes[0].sendrawtransaction(tx3a_hex, 0)
@@ -492,7 +495,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
# correctly used by replacement logic
# 1. Check that feeperkb uses modified fees
- tx0_outpoint = make_utxo(self.nodes[0], int(1.1*COIN))
+ tx0_outpoint = make_utxo(self.nodes[0], int(1.1 * COIN))
tx1a = CTransaction()
tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0)]
@@ -503,14 +506,14 @@ class ReplaceByFeeTest(BitcoinTestFramework):
# Higher fee, but the actual fee per KB is much lower.
tx1b = CTransaction()
tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)]
- tx1b.vout = [CTxOut(int(0.001*COIN), CScript([b'a'*740000]))]
+ tx1b.vout = [CTxOut(int(0.001 * COIN), CScript([b'a' * 740000]))]
tx1b_hex = txToHex(tx1b)
# Verify tx1b cannot replace tx1a.
assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx1b_hex, 0)
# Use prioritisetransaction to set tx1a's fee to 0.
- self.nodes[0].prioritisetransaction(txid=tx1a_txid, fee_delta=int(-0.1*COIN))
+ self.nodes[0].prioritisetransaction(txid=tx1a_txid, fee_delta=int(-0.1 * COIN))
# Now tx1b should be able to replace tx1a
tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, 0)
@@ -518,7 +521,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
assert tx1b_txid in self.nodes[0].getrawmempool()
# 2. Check that absolute fee checks use modified fee.
- tx1_outpoint = make_utxo(self.nodes[0], int(1.1*COIN))
+ tx1_outpoint = make_utxo(self.nodes[0], int(1.1 * COIN))
tx2a = CTransaction()
tx2a.vin = [CTxIn(tx1_outpoint, nSequence=0)]
@@ -537,7 +540,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx2b_hex, 0)
# Now prioritise tx2b to have a higher modified fee
- self.nodes[0].prioritisetransaction(txid=tx2b.hash, fee_delta=int(0.1*COIN))
+ self.nodes[0].prioritisetransaction(txid=tx2b.hash, fee_delta=int(0.1 * COIN))
# tx2b should now be accepted
tx2b_txid = self.nodes[0].sendrawtransaction(tx2b_hex, 0)
@@ -547,11 +550,11 @@ class ReplaceByFeeTest(BitcoinTestFramework):
def test_rpc(self):
us0 = self.nodes[0].listunspent()[0]
ins = [us0]
- outs = {self.nodes[0].getnewaddress() : Decimal(1.0000000)}
+ outs = {self.nodes[0].getnewaddress(): Decimal(1.0000000)}
rawtx0 = self.nodes[0].createrawtransaction(ins, outs, 0, True)
rawtx1 = self.nodes[0].createrawtransaction(ins, outs, 0, False)
- json0 = self.nodes[0].decoderawtransaction(rawtx0)
- json1 = self.nodes[0].decoderawtransaction(rawtx1)
+ json0 = self.nodes[0].decoderawtransaction(rawtx0)
+ json1 = self.nodes[0].decoderawtransaction(rawtx1)
assert_equal(json0["vin"][0]["sequence"], 4294967293)
assert_equal(json1["vin"][0]["sequence"], 4294967295)
@@ -559,10 +562,68 @@ class ReplaceByFeeTest(BitcoinTestFramework):
frawtx2a = self.nodes[0].fundrawtransaction(rawtx2, {"replaceable": True})
frawtx2b = self.nodes[0].fundrawtransaction(rawtx2, {"replaceable": False})
- json0 = self.nodes[0].decoderawtransaction(frawtx2a['hex'])
- json1 = self.nodes[0].decoderawtransaction(frawtx2b['hex'])
+ json0 = self.nodes[0].decoderawtransaction(frawtx2a['hex'])
+ json1 = self.nodes[0].decoderawtransaction(frawtx2b['hex'])
assert_equal(json0["vin"][0]["sequence"], 4294967293)
assert_equal(json1["vin"][0]["sequence"], 4294967294)
+ def test_no_inherited_signaling(self):
+ wallet = MiniWallet(self.nodes[0])
+ wallet.scan_blocks(start=76, num=1)
+ confirmed_utxo = wallet.get_utxo()
+
+ # Create an explicitly opt-in parent transaction
+ optin_parent_tx = wallet.send_self_transfer(
+ from_node=self.nodes[0],
+ utxo_to_spend=confirmed_utxo,
+ sequence=BIP125_SEQUENCE_NUMBER,
+ fee_rate=Decimal('0.01'),
+ )
+ assert_equal(True, self.nodes[0].getmempoolentry(optin_parent_tx['txid'])['bip125-replaceable'])
+
+ replacement_parent_tx = wallet.create_self_transfer(
+ from_node=self.nodes[0],
+ utxo_to_spend=confirmed_utxo,
+ sequence=BIP125_SEQUENCE_NUMBER,
+ fee_rate=Decimal('0.02'),
+ )
+
+ # Test if parent tx can be replaced.
+ res = self.nodes[0].testmempoolaccept(rawtxs=[replacement_parent_tx['hex']])[0]
+
+ # Parent can be replaced.
+ assert_equal(res['allowed'], True)
+
+ # Create an opt-out child tx spending the opt-in parent
+ parent_utxo = wallet.get_utxo(txid=optin_parent_tx['txid'])
+ optout_child_tx = wallet.send_self_transfer(
+ from_node=self.nodes[0],
+ utxo_to_spend=parent_utxo,
+ sequence=0xffffffff,
+ fee_rate=Decimal('0.01'),
+ )
+
+ # Reports true due to inheritance
+ assert_equal(True, self.nodes[0].getmempoolentry(optout_child_tx['txid'])['bip125-replaceable'])
+
+ replacement_child_tx = wallet.create_self_transfer(
+ from_node=self.nodes[0],
+ utxo_to_spend=parent_utxo,
+ sequence=0xffffffff,
+ fee_rate=Decimal('0.02'),
+ mempool_valid=False,
+ )
+
+ # Broadcast replacement child tx
+ # BIP 125 :
+ # 1. The original transactions signal replaceability explicitly or through inheritance as described in the above
+ # Summary section.
+ # The original transaction (`optout_child_tx`) doesn't signal RBF but its parent (`optin_parent_tx`) does.
+ # The replacement transaction (`replacement_child_tx`) should be able to replace the original transaction.
+ # See CVE-2021-31876 for further explanations.
+ assert_equal(True, self.nodes[0].getmempoolentry(optin_parent_tx['txid'])['bip125-replaceable'])
+ assert_raises_rpc_error(-26, 'txn-mempool-conflict', self.nodes[0].sendrawtransaction, replacement_child_tx["hex"], 0)
+
+
if __name__ == '__main__':
ReplaceByFeeTest().main()
diff --git a/test/functional/feature_versionbits_warning.py b/test/functional/feature_versionbits_warning.py
index 062a7affb5..1c9e237d78 100755
--- a/test/functional/feature_versionbits_warning.py
+++ b/test/functional/feature_versionbits_warning.py
@@ -21,8 +21,8 @@ VB_TOP_BITS = 0x20000000
VB_UNKNOWN_BIT = 27 # Choose a bit unassigned to any deployment
VB_UNKNOWN_VERSION = VB_TOP_BITS | (1 << VB_UNKNOWN_BIT)
-WARN_UNKNOWN_RULES_ACTIVE = "unknown new rules activated (versionbit {})".format(VB_UNKNOWN_BIT)
-VB_PATTERN = re.compile("Warning: unknown new rules activated.*versionbit")
+WARN_UNKNOWN_RULES_ACTIVE = "Unknown new rules activated (versionbit {})".format(VB_UNKNOWN_BIT)
+VB_PATTERN = re.compile("Unknown new rules activated.*versionbit")
class VersionBitsWarningTest(BitcoinTestFramework):
def set_test_params(self):
diff --git a/test/functional/mempool_package_onemore.py b/test/functional/mempool_package_onemore.py
index 1e9895e621..fcd8b061fa 100755
--- a/test/functional/mempool_package_onemore.py
+++ b/test/functional/mempool_package_onemore.py
@@ -11,7 +11,11 @@ from decimal import Decimal
from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, assert_raises_rpc_error, satoshi_round
+from test_framework.util import (
+ assert_equal,
+ assert_raises_rpc_error,
+ chain_transaction,
+)
MAX_ANCESTORS = 25
MAX_DESCENDANTS = 25
@@ -24,23 +28,6 @@ class MempoolPackagesTest(BitcoinTestFramework):
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
- # Build a transaction that spends parent_txid:vout
- # Return amount sent
- def chain_transaction(self, node, parent_txids, vouts, value, fee, num_outputs):
- send_value = satoshi_round((value - fee)/num_outputs)
- inputs = []
- for (txid, vout) in zip(parent_txids, vouts):
- inputs.append({'txid' : txid, 'vout' : vout})
- outputs = {}
- for _ in range(num_outputs):
- outputs[node.getnewaddress()] = send_value
- rawtx = node.createrawtransaction(inputs, outputs, 0, True)
- signedtx = node.signrawtransactionwithwallet(rawtx)
- txid = node.sendrawtransaction(signedtx['hex'])
- fulltx = node.getrawtransaction(txid, 1)
- assert len(fulltx['vout']) == num_outputs # make sure we didn't generate a change output
- return (txid, send_value)
-
def run_test(self):
# Mine some blocks and have them mature.
self.nodes[0].generate(COINBASE_MATURITY + 1)
@@ -53,32 +40,32 @@ class MempoolPackagesTest(BitcoinTestFramework):
# MAX_ANCESTORS transactions off a confirmed tx should be fine
chain = []
for _ in range(4):
- (txid, sent_value) = self.chain_transaction(self.nodes[0], [txid], [vout], value, fee, 2)
+ (txid, sent_value) = chain_transaction(self.nodes[0], [txid], [vout], value, fee, 2)
vout = 0
value = sent_value
chain.append([txid, value])
for _ in range(MAX_ANCESTORS - 4):
- (txid, sent_value) = self.chain_transaction(self.nodes[0], [txid], [0], value, fee, 1)
+ (txid, sent_value) = chain_transaction(self.nodes[0], [txid], [0], value, fee, 1)
value = sent_value
chain.append([txid, value])
- (second_chain, second_chain_value) = self.chain_transaction(self.nodes[0], [utxo[1]['txid']], [utxo[1]['vout']], utxo[1]['amount'], fee, 1)
+ (second_chain, second_chain_value) = chain_transaction(self.nodes[0], [utxo[1]['txid']], [utxo[1]['vout']], utxo[1]['amount'], fee, 1)
# Check mempool has MAX_ANCESTORS + 1 transactions in it
assert_equal(len(self.nodes[0].getrawmempool(True)), MAX_ANCESTORS + 1)
# Adding one more transaction on to the chain should fail.
- assert_raises_rpc_error(-26, "too-long-mempool-chain, too many unconfirmed ancestors [limit: 25]", self.chain_transaction, self.nodes[0], [txid], [0], value, fee, 1)
+ assert_raises_rpc_error(-26, "too-long-mempool-chain, too many unconfirmed ancestors [limit: 25]", chain_transaction, self.nodes[0], [txid], [0], value, fee, 1)
# ...even if it chains on from some point in the middle of the chain.
- assert_raises_rpc_error(-26, "too-long-mempool-chain, too many descendants", self.chain_transaction, self.nodes[0], [chain[2][0]], [1], chain[2][1], fee, 1)
- assert_raises_rpc_error(-26, "too-long-mempool-chain, too many descendants", self.chain_transaction, self.nodes[0], [chain[1][0]], [1], chain[1][1], fee, 1)
+ assert_raises_rpc_error(-26, "too-long-mempool-chain, too many descendants", chain_transaction, self.nodes[0], [chain[2][0]], [1], chain[2][1], fee, 1)
+ assert_raises_rpc_error(-26, "too-long-mempool-chain, too many descendants", chain_transaction, self.nodes[0], [chain[1][0]], [1], chain[1][1], fee, 1)
# ...even if it chains on to two parent transactions with one in the chain.
- assert_raises_rpc_error(-26, "too-long-mempool-chain, too many descendants", self.chain_transaction, self.nodes[0], [chain[0][0], second_chain], [1, 0], chain[0][1] + second_chain_value, fee, 1)
+ assert_raises_rpc_error(-26, "too-long-mempool-chain, too many descendants", chain_transaction, self.nodes[0], [chain[0][0], second_chain], [1, 0], chain[0][1] + second_chain_value, fee, 1)
# ...especially if its > 40k weight
- assert_raises_rpc_error(-26, "too-long-mempool-chain, too many descendants", self.chain_transaction, self.nodes[0], [chain[0][0]], [1], chain[0][1], fee, 350)
+ assert_raises_rpc_error(-26, "too-long-mempool-chain, too many descendants", chain_transaction, self.nodes[0], [chain[0][0]], [1], chain[0][1], fee, 350)
# But not if it chains directly off the first transaction
- (replacable_txid, replacable_orig_value) = self.chain_transaction(self.nodes[0], [chain[0][0]], [1], chain[0][1], fee, 1)
+ (replacable_txid, replacable_orig_value) = chain_transaction(self.nodes[0], [chain[0][0]], [1], chain[0][1], fee, 1)
# and the second chain should work just fine
- self.chain_transaction(self.nodes[0], [second_chain], [0], second_chain_value, fee, 1)
+ chain_transaction(self.nodes[0], [second_chain], [0], second_chain_value, fee, 1)
# Make sure we can RBF the chain which used our carve-out rule
second_tx_outputs = {self.nodes[0].getrawtransaction(replacable_txid, True)["vout"][0]['scriptPubKey']['address']: replacable_orig_value - (Decimal(1) / Decimal(100))}
diff --git a/test/functional/mempool_packages.py b/test/functional/mempool_packages.py
index 606717d890..5fc3ec23ae 100755
--- a/test/functional/mempool_packages.py
+++ b/test/functional/mempool_packages.py
@@ -13,6 +13,7 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
+ chain_transaction,
satoshi_round,
)
@@ -42,21 +43,6 @@ class MempoolPackagesTest(BitcoinTestFramework):
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
- # Build a transaction that spends parent_txid:vout
- # Return amount sent
- def chain_transaction(self, node, parent_txid, vout, value, fee, num_outputs):
- send_value = satoshi_round((value - fee)/num_outputs)
- inputs = [ {'txid' : parent_txid, 'vout' : vout} ]
- outputs = {}
- for _ in range(num_outputs):
- outputs[node.getnewaddress()] = send_value
- rawtx = node.createrawtransaction(inputs, outputs)
- signedtx = node.signrawtransactionwithwallet(rawtx)
- txid = node.sendrawtransaction(signedtx['hex'])
- fulltx = node.getrawtransaction(txid, 1)
- assert len(fulltx['vout']) == num_outputs # make sure we didn't generate a change output
- return (txid, send_value)
-
def run_test(self):
# Mine some blocks and have them mature.
peer_inv_store = self.nodes[0].add_p2p_connection(P2PTxInvStore()) # keep track of invs
@@ -71,7 +57,7 @@ class MempoolPackagesTest(BitcoinTestFramework):
chain = []
witness_chain = []
for _ in range(MAX_ANCESTORS):
- (txid, sent_value) = self.chain_transaction(self.nodes[0], txid, 0, value, fee, 1)
+ (txid, sent_value) = chain_transaction(self.nodes[0], [txid], [0], value, fee, 1)
value = sent_value
chain.append(txid)
# We need the wtxids to check P2P announcements
@@ -189,7 +175,7 @@ class MempoolPackagesTest(BitcoinTestFramework):
assert_equal(mempool[x]['descendantfees'], descendant_fees * COIN + 1000)
# Adding one more transaction on to the chain should fail.
- assert_raises_rpc_error(-26, "too-long-mempool-chain", self.chain_transaction, self.nodes[0], txid, vout, value, fee, 1)
+ assert_raises_rpc_error(-26, "too-long-mempool-chain", chain_transaction, self.nodes[0], [txid], [vout], value, fee, 1)
# Check that prioritising a tx before it's added to the mempool works
# First clear the mempool by mining a block.
@@ -238,7 +224,7 @@ class MempoolPackagesTest(BitcoinTestFramework):
transaction_package = []
tx_children = []
# First create one parent tx with 10 children
- (txid, sent_value) = self.chain_transaction(self.nodes[0], txid, vout, value, fee, 10)
+ (txid, sent_value) = chain_transaction(self.nodes[0], [txid], [vout], value, fee, 10)
parent_transaction = txid
for i in range(10):
transaction_package.append({'txid': txid, 'vout': i, 'amount': sent_value})
@@ -247,7 +233,7 @@ class MempoolPackagesTest(BitcoinTestFramework):
chain = [] # save sent txs for the purpose of checking node1's mempool later (see below)
for _ in range(MAX_DESCENDANTS - 1):
utxo = transaction_package.pop(0)
- (txid, sent_value) = self.chain_transaction(self.nodes[0], utxo['txid'], utxo['vout'], utxo['amount'], fee, 10)
+ (txid, sent_value) = chain_transaction(self.nodes[0], [utxo['txid']], [utxo['vout']], utxo['amount'], fee, 10)
chain.append(txid)
if utxo['txid'] is parent_transaction:
tx_children.append(txid)
@@ -263,7 +249,7 @@ class MempoolPackagesTest(BitcoinTestFramework):
# Sending one more chained transaction will fail
utxo = transaction_package.pop(0)
- assert_raises_rpc_error(-26, "too-long-mempool-chain", self.chain_transaction, self.nodes[0], utxo['txid'], utxo['vout'], utxo['amount'], fee, 10)
+ assert_raises_rpc_error(-26, "too-long-mempool-chain", chain_transaction, self.nodes[0], [utxo['txid']], [utxo['vout']], utxo['amount'], fee, 10)
# Check that node1's mempool is as expected, containing:
# - txs from previous ancestor test (-> custom ancestor limit)
@@ -321,13 +307,13 @@ class MempoolPackagesTest(BitcoinTestFramework):
value = send_value
# Create tx1
- tx1_id, _ = self.chain_transaction(self.nodes[0], tx0_id, 0, value, fee, 1)
+ tx1_id, _ = chain_transaction(self.nodes[0], [tx0_id], [0], value, fee, 1)
# Create tx2-7
vout = 1
txid = tx0_id
for _ in range(6):
- (txid, sent_value) = self.chain_transaction(self.nodes[0], txid, vout, value, fee, 1)
+ (txid, sent_value) = chain_transaction(self.nodes[0], [txid], [vout], value, fee, 1)
vout = 0
value = sent_value
diff --git a/test/functional/p2p_invalid_block.py b/test/functional/p2p_invalid_block.py
index 483f25f48c..91666d0f08 100755
--- a/test/functional/p2p_invalid_block.py
+++ b/test/functional/p2p_invalid_block.py
@@ -9,8 +9,11 @@ In this test we connect to one node over p2p, and test block requests:
2) Invalid block with duplicated transaction should be re-requested.
3) Invalid block with bad coinbase value should be rejected and not
re-requested.
+4) Invalid block due to future timestamp is later accepted when that timestamp
+becomes valid.
"""
import copy
+import time
from test_framework.blocktools import create_block, create_coinbase, create_tx_with_script
from test_framework.messages import COIN
@@ -18,6 +21,9 @@ from test_framework.p2p import P2PDataStore
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal
+MAX_FUTURE_BLOCK_TIME = 2 * 60 * 60
+
+
class InvalidBlockRequestTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
@@ -133,5 +139,18 @@ class InvalidBlockRequestTest(BitcoinTestFramework):
self.log.info("Test inflation by duplicating input")
peer.send_blocks_and_test([block4], node, success=False, reject_reason='bad-txns-inputs-duplicate')
+ self.log.info("Test accepting identical block after rejecting it due to a future timestamp.")
+ t = int(time.time())
+ node.setmocktime(t)
+ # Set block time +1 second past max future validity
+ block = create_block(tip, create_coinbase(height), t + MAX_FUTURE_BLOCK_TIME + 1)
+ block.hashMerkleRoot = block.calc_merkle_root()
+ block.solve()
+ # Need force_send because the block will get rejected without a getdata otherwise
+ peer.send_blocks_and_test([block], node, force_send=True, success=False, reject_reason='time-too-new')
+ node.setmocktime(t + 1)
+ peer.send_blocks_and_test([block], node, success=True)
+
+
if __name__ == '__main__':
InvalidBlockRequestTest().main()
diff --git a/test/functional/p2p_leak.py b/test/functional/p2p_leak.py
index 71d5ca92b3..f1538e8ac7 100755
--- a/test/functional/p2p_leak.py
+++ b/test/functional/p2p_leak.py
@@ -29,6 +29,8 @@ from test_framework.util import (
assert_greater_than_or_equal,
)
+PEER_TIMEOUT = 3
+
class LazyPeer(P2PInterface):
def __init__(self):
@@ -98,7 +100,7 @@ class P2PVersionStore(P2PInterface):
class P2PLeakTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
- self.extra_args = [['-peertimeout=4']]
+ self.extra_args = [[f"-peertimeout={PEER_TIMEOUT}"]]
def create_old_version(self, nversion):
old_version_msg = msg_version()
@@ -134,7 +136,7 @@ class P2PLeakTest(BitcoinTestFramework):
self.nodes[0].generate(nblocks=1)
# Give the node enough time to possibly leak out a message
- time.sleep(5)
+ time.sleep(PEER_TIMEOUT + 2)
# Make sure only expected messages came in
assert not no_version_idle_peer.unexpected_msg
diff --git a/test/functional/rpc_packages.py b/test/functional/rpc_packages.py
index 3d8d81d6b8..d0848d9bd6 100755
--- a/test/functional/rpc_packages.py
+++ b/test/functional/rpc_packages.py
@@ -354,7 +354,8 @@ class RPCPackagesTest(BitcoinTestFramework):
# This transaction is a valid BIP125 replace-by-fee
assert testres_rbf_single[0]["allowed"]
testres_rbf_package = self.independent_txns_testres_blank + [{
- "txid": replacement_tx.rehash(), "wtxid": replacement_tx.getwtxid(), "allowed": False, "reject-reason": "txn-mempool-conflict"
+ "txid": replacement_tx.rehash(), "wtxid": replacement_tx.getwtxid(), "allowed": False,
+ "reject-reason": "bip125-replacement-disallowed"
}]
self.assert_testres_equal(self.independent_txns_hex + [signed_replacement_tx["hex"]], testres_rbf_package)
diff --git a/test/functional/test_framework/blocktools.py b/test/functional/test_framework/blocktools.py
index e91b44e776..f35ea6c122 100644
--- a/test/functional/test_framework/blocktools.py
+++ b/test/functional/test_framework/blocktools.py
@@ -181,6 +181,11 @@ def create_raw_transaction(node, txid, to_address, *, amount):
signed_psbt = wrpc.walletprocesspsbt(psbt)
psbt = signed_psbt['psbt']
final_psbt = node.finalizepsbt(psbt)
+ if not final_psbt["complete"]:
+ node.log.info(f'final_psbt={final_psbt}')
+ for w in node.listwallets():
+ wrpc = node.get_wallet_rpc(w)
+ node.log.info(f'listunspent={wrpc.listunspent()}')
assert_equal(final_psbt["complete"], True)
return final_psbt['hex']
diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py
index 5a9736a7a3..3d79174356 100755
--- a/test/functional/test_framework/messages.py
+++ b/test/functional/test_framework/messages.py
@@ -39,7 +39,7 @@ MAX_BLOOM_HASH_FUNCS = 50
COIN = 100000000 # 1 btc in satoshis
MAX_MONEY = 21000000 * COIN
-BIP125_SEQUENCE_NUMBER = 0xfffffffd # Sequence number that is BIP 125 opt-in and BIP 68-opt-out
+BIP125_SEQUENCE_NUMBER = 0xfffffffd # Sequence number that is rbf-opt-in (BIP 125) and csv-opt-out (BIP 68)
MAX_PROTOCOL_MESSAGE_LENGTH = 4000000 # Maximum length of incoming protocol messages
MAX_HEADERS_RESULTS = 2000 # Number of headers sent in one getheaders result
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index c17c16f797..ba52abc7dd 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -400,14 +400,14 @@ class TestNode():
self._raise_assertion_error('Expected messages "{}" does not partially match log:\n\n{}\n\n'.format(str(expected_msgs), print_log))
@contextlib.contextmanager
- def profile_with_perf(self, profile_name):
+ def profile_with_perf(self, profile_name: str):
"""
Context manager that allows easy profiling of node activity using `perf`.
See `test/functional/README.md` for details on perf usage.
Args:
- profile_name (str): This string will be appended to the
+ profile_name: This string will be appended to the
profile data filename generated by perf.
"""
subp = self._start_perf(profile_name)
diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py
index 55166ba0ad..f354ae0075 100644
--- a/test/functional/test_framework/util.py
+++ b/test/functional/test_framework/util.py
@@ -20,6 +20,7 @@ import unittest
from . import coverage
from .authproxy import AuthServiceProxy, JSONRPCException
from io import BytesIO
+from typing import Callable, Optional
logger = logging.getLogger("TestFramework.utils")
@@ -80,7 +81,7 @@ def assert_raises_message(exc, message, fun, *args, **kwds):
raise AssertionError("No exception raised")
-def assert_raises_process_error(returncode, output, fun, *args, **kwds):
+def assert_raises_process_error(returncode: int, output: str, fun: Callable, *args, **kwds):
"""Execute a process and asserts the process return code and output.
Calls function `fun` with arguments `args` and `kwds`. Catches a CalledProcessError
@@ -88,9 +89,9 @@ def assert_raises_process_error(returncode, output, fun, *args, **kwds):
no CalledProcessError was raised or if the return code and output are not as expected.
Args:
- returncode (int): the process return code.
- output (string): [a substring of] the process output.
- fun (function): the function to call. This should execute a process.
+ returncode: the process return code.
+ output: [a substring of] the process output.
+ fun: the function to call. This should execute a process.
args*: positional arguments for the function.
kwds**: named arguments for the function.
"""
@@ -105,7 +106,7 @@ def assert_raises_process_error(returncode, output, fun, *args, **kwds):
raise AssertionError("No exception raised")
-def assert_raises_rpc_error(code, message, fun, *args, **kwds):
+def assert_raises_rpc_error(code: Optional[int], message: Optional[str], fun: Callable, *args, **kwds):
"""Run an RPC and verify that a specific JSONRPC exception code and message is raised.
Calls function `fun` with arguments `args` and `kwds`. Catches a JSONRPCException
@@ -113,11 +114,11 @@ def assert_raises_rpc_error(code, message, fun, *args, **kwds):
no JSONRPCException was raised or if the error code/message are not as expected.
Args:
- code (int), optional: the error code returned by the RPC call (defined
- in src/rpc/protocol.h). Set to None if checking the error code is not required.
- message (string), optional: [a substring of] the error string returned by the
- RPC call. Set to None if checking the error string is not required.
- fun (function): the function to call. This should be the name of an RPC.
+ code: the error code returned by the RPC call (defined in src/rpc/protocol.h).
+ Set to None if checking the error code is not required.
+ message: [a substring of] the error string returned by the RPC call.
+ Set to None if checking the error string is not required.
+ fun: the function to call. This should be the name of an RPC.
args*: positional arguments for the function.
kwds**: named arguments for the function.
"""
@@ -480,6 +481,28 @@ def create_confirmed_utxos(fee, node, count):
return utxos
+def chain_transaction(node, parent_txids, vouts, value, fee, num_outputs):
+ """Build and send a transaction that spends the given inputs (specified
+ by lists of parent_txid:vout each), with the desired total value and fee,
+ equally divided up to the desired number of outputs.
+
+ Returns a tuple with the txid and the amount sent per output.
+ """
+ send_value = satoshi_round((value - fee)/num_outputs)
+ inputs = []
+ for (txid, vout) in zip(parent_txids, vouts):
+ inputs.append({'txid' : txid, 'vout' : vout})
+ outputs = {}
+ for _ in range(num_outputs):
+ outputs[node.getnewaddress()] = send_value
+ rawtx = node.createrawtransaction(inputs, outputs, 0, True)
+ signedtx = node.signrawtransactionwithwallet(rawtx)
+ txid = node.sendrawtransaction(signedtx['hex'])
+ fulltx = node.getrawtransaction(txid, 1)
+ assert len(fulltx['vout']) == num_outputs # make sure we didn't generate a change output
+ return (txid, send_value)
+
+
# 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():
diff --git a/test/functional/test_framework/wallet.py b/test/functional/test_framework/wallet.py
index bfb5916c37..ad58a62e28 100644
--- a/test/functional/test_framework/wallet.py
+++ b/test/functional/test_framework/wallet.py
@@ -6,6 +6,7 @@
from decimal import Decimal
from enum import Enum
+from typing import Optional
from test_framework.address import ADDRESS_BCRT1_P2WSH_OP_TRUE
from test_framework.key import ECKey
from test_framework.messages import (
@@ -105,12 +106,12 @@ class MiniWallet:
def get_address(self):
return self._address
- def get_utxo(self, *, txid='', mark_as_spent=True):
+ def get_utxo(self, *, txid: Optional[str]='', mark_as_spent=True):
"""
Returns a utxo and marks it as spent (pops it from the internal list)
Args:
- txid (string), optional: get the first utxo we find from a specific transaction
+ txid: get the first utxo we find from a specific transaction
Note: Can be used to get the change output immediately after a send_self_transfer
"""
@@ -123,13 +124,13 @@ class MiniWallet:
else:
return self._utxos[index]
- def send_self_transfer(self, *, fee_rate=Decimal("0.003"), from_node, utxo_to_spend=None, locktime=0):
+ def send_self_transfer(self, **kwargs):
"""Create and send a tx with the specified fee_rate. Fee may be exact or at most one satoshi higher than needed."""
- tx = self.create_self_transfer(fee_rate=fee_rate, from_node=from_node, utxo_to_spend=utxo_to_spend)
- self.sendrawtransaction(from_node=from_node, tx_hex=tx['hex'])
+ tx = self.create_self_transfer(**kwargs)
+ self.sendrawtransaction(from_node=kwargs['from_node'], tx_hex=tx['hex'])
return tx
- def create_self_transfer(self, *, fee_rate=Decimal("0.003"), from_node, utxo_to_spend=None, mempool_valid=True, locktime=0):
+ def create_self_transfer(self, *, fee_rate=Decimal("0.003"), from_node, utxo_to_spend=None, mempool_valid=True, locktime=0, sequence=0):
"""Create and return a tx with the specified fee_rate. Fee may be exact or at most one satoshi higher than needed."""
self._utxos = sorted(self._utxos, key=lambda k: k['value'])
utxo_to_spend = utxo_to_spend or self._utxos.pop() # Pick the largest utxo (if none provided) and hope it covers the fee
@@ -139,7 +140,7 @@ class MiniWallet:
assert send_value > 0
tx = CTransaction()
- tx.vin = [CTxIn(COutPoint(int(utxo_to_spend['txid'], 16), utxo_to_spend['vout']))]
+ tx.vin = [CTxIn(COutPoint(int(utxo_to_spend['txid'], 16), utxo_to_spend['vout']), nSequence=sequence)]
tx.vout = [CTxOut(int(send_value * COIN), self._scriptPubKey)]
tx.nLockTime = locktime
if not self._address:
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index d009223f12..ad1acd2e2f 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -19,6 +19,7 @@ import datetime
import os
import time
import shutil
+import signal
import subprocess
import sys
import tempfile
@@ -285,6 +286,7 @@ BASE_SCRIPTS = [
'feature_logging.py',
'feature_anchors.py',
'feature_coinstatsindex.py',
+ 'wallet_orphanedreward.py',
'p2p_node_network_limited.py',
'p2p_permissions.py',
'feature_blocksdir.py',
@@ -548,9 +550,11 @@ def run_tests(*, test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage=
all_passed = all(map(lambda test_result: test_result.was_successful, test_results)) and coverage_passed
- # This will be a no-op unless failfast is True in which case there may be dangling
- # processes which need to be killed.
- job_queue.kill_and_join()
+ # Clean up dangling processes if any. This may only happen with --failfast option.
+ # Killing the process group will also terminate the current process but that is
+ # not an issue
+ if len(job_queue.jobs):
+ os.killpg(os.getpgid(0), signal.SIGKILL)
sys.exit(not all_passed)
@@ -647,16 +651,6 @@ class TestHandler:
print('.', end='', flush=True)
dot_count += 1
- def kill_and_join(self):
- """Send SIGKILL to all jobs and block until all have ended."""
- procs = [i[2] for i in self.jobs]
-
- for proc in procs:
- proc.kill()
-
- for proc in procs:
- proc.wait()
-
class TestResult():
def __init__(self, name, status, time):
diff --git a/test/functional/wallet_orphanedreward.py b/test/functional/wallet_orphanedreward.py
new file mode 100755
index 0000000000..097df2cf41
--- /dev/null
+++ b/test/functional/wallet_orphanedreward.py
@@ -0,0 +1,62 @@
+#!/usr/bin/env python3
+# Copyright (c) 2020-2021 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 orphaned block rewards in the wallet."""
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_equal
+
+class OrphanedBlockRewardTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.num_nodes = 2
+
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+
+ def run_test(self):
+ # Generate some blocks and obtain some coins on node 0. We send
+ # some balance to node 1, which will hold it as a single coin.
+ self.nodes[0].generate(150)
+ self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 10)
+ self.nodes[0].generate(1)
+
+ # Get a block reward with node 1 and remember the block so we can orphan
+ # it later.
+ self.sync_blocks()
+ blk = self.nodes[1].generate(1)[0]
+ self.sync_blocks()
+
+ # Let the block reward mature and send coins including both
+ # the existing balance and the block reward.
+ self.nodes[0].generate(150)
+ self.sync_blocks()
+ assert_equal(self.nodes[1].getbalance(), 10 + 25)
+ txid = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 30)
+
+ # Orphan the block reward and make sure that the original coins
+ # from the wallet can still be spent.
+ self.nodes[0].invalidateblock(blk)
+ self.nodes[0].generate(152)
+ self.sync_blocks()
+ # Without the following abandontransaction call, the coins are
+ # not considered available yet.
+ assert_equal(self.nodes[1].getbalances()["mine"], {
+ "trusted": 0,
+ "untrusted_pending": 0,
+ "immature": 0,
+ })
+ # The following abandontransaction is necessary to make the later
+ # lines succeed, and probably should not be needed; see
+ # https://github.com/bitcoin/bitcoin/issues/14148.
+ self.nodes[1].abandontransaction(txid)
+ assert_equal(self.nodes[1].getbalances()["mine"], {
+ "trusted": 10,
+ "untrusted_pending": 0,
+ "immature": 0,
+ })
+ self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 9)
+
+if __name__ == '__main__':
+ OrphanedBlockRewardTest().main()
diff --git a/test/functional/wallet_taproot.py b/test/functional/wallet_taproot.py
index 65ca7bdef7..1547a90125 100755
--- a/test/functional/wallet_taproot.py
+++ b/test/functional/wallet_taproot.py
@@ -6,6 +6,7 @@
import random
+from decimal import Decimal
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal
from test_framework.descriptors import descsum_create
@@ -172,9 +173,9 @@ class WalletTaprootTest(BitcoinTestFramework):
"""Test generation and spending of P2TR address outputs."""
def set_test_params(self):
- self.num_nodes = 2
+ self.num_nodes = 3
self.setup_clean_chain = True
- self.extra_args = [['-keypool=100'], ['-keypool=100']]
+ self.extra_args = [['-keypool=100'], ['-keypool=100'], ["-vbparams=taproot:1:1"]]
self.supports_cli = False
def skip_test_if_missing_module(self):
@@ -230,14 +231,113 @@ class WalletTaprootTest(BitcoinTestFramework):
addr_r = self.make_addr(treefn, keys, i)
assert_equal(addr_g, addr_r)
+ # tr descriptors cannot be imported when Taproot is not active
+ result = self.privs_tr_enabled.importdescriptors([{"desc": desc, "timestamp": "now"}])
+ assert(result[0]["success"])
+ result = self.pubs_tr_enabled.importdescriptors([{"desc": desc_pub, "timestamp": "now"}])
+ assert(result[0]["success"])
+ if desc.startswith("tr"):
+ result = self.privs_tr_disabled.importdescriptors([{"desc": desc, "timestamp": "now"}])
+ assert(not result[0]["success"])
+ assert_equal(result[0]["error"]["code"], -4)
+ assert_equal(result[0]["error"]["message"], "Cannot import tr() descriptor when Taproot is not active")
+ result = self.pubs_tr_disabled.importdescriptors([{"desc": desc_pub, "timestamp": "now"}])
+ assert(not result[0]["success"])
+ assert_equal(result[0]["error"]["code"], -4)
+ assert_equal(result[0]["error"]["message"], "Cannot import tr() descriptor when Taproot is not active")
+
+ def do_test_sendtoaddress(self, comment, pattern, privmap, treefn, keys_pay, keys_change):
+ self.log.info("Testing %s through sendtoaddress" % comment)
+ desc_pay = self.make_desc(pattern, privmap, keys_pay)
+ desc_change = self.make_desc(pattern, privmap, keys_change)
+ desc_pay_pub = self.make_desc(pattern, privmap, keys_pay, True)
+ desc_change_pub = self.make_desc(pattern, privmap, keys_change, True)
+ assert_equal(self.nodes[0].getdescriptorinfo(desc_pay)['descriptor'], desc_pay_pub)
+ assert_equal(self.nodes[0].getdescriptorinfo(desc_change)['descriptor'], desc_change_pub)
+ result = self.rpc_online.importdescriptors([{"desc": desc_pay, "active": True, "timestamp": "now"}])
+ assert(result[0]['success'])
+ result = self.rpc_online.importdescriptors([{"desc": desc_change, "active": True, "timestamp": "now", "internal": True}])
+ assert(result[0]['success'])
+ for i in range(4):
+ addr_g = self.rpc_online.getnewaddress(address_type='bech32')
+ if treefn is not None:
+ addr_r = self.make_addr(treefn, keys_pay, i)
+ assert_equal(addr_g, addr_r)
+ boring_balance = int(self.boring.getbalance() * 100000000)
+ to_amnt = random.randrange(1000000, boring_balance)
+ self.boring.sendtoaddress(address=addr_g, amount=Decimal(to_amnt) / 100000000, subtractfeefromamount=True)
+ self.nodes[0].generatetoaddress(1, self.boring.getnewaddress())
+ test_balance = int(self.rpc_online.getbalance() * 100000000)
+ ret_amnt = random.randrange(100000, test_balance)
+ res = self.rpc_online.sendtoaddress(address=self.boring.getnewaddress(), amount=Decimal(ret_amnt) / 100000000, subtractfeefromamount=True)
+ self.nodes[0].generatetoaddress(1, self.boring.getnewaddress())
+ assert(self.rpc_online.gettransaction(res)["confirmations"] > 0)
+
+ def do_test_psbt(self, comment, pattern, privmap, treefn, keys_pay, keys_change):
+ self.log.info("Testing %s through PSBT" % comment)
+ desc_pay = self.make_desc(pattern, privmap, keys_pay, False)
+ desc_change = self.make_desc(pattern, privmap, keys_change, False)
+ desc_pay_pub = self.make_desc(pattern, privmap, keys_pay, True)
+ desc_change_pub = self.make_desc(pattern, privmap, keys_change, True)
+ assert_equal(self.nodes[0].getdescriptorinfo(desc_pay)['descriptor'], desc_pay_pub)
+ assert_equal(self.nodes[0].getdescriptorinfo(desc_change)['descriptor'], desc_change_pub)
+ result = self.psbt_online.importdescriptors([{"desc": desc_pay_pub, "active": True, "timestamp": "now"}])
+ assert(result[0]['success'])
+ result = self.psbt_online.importdescriptors([{"desc": desc_change_pub, "active": True, "timestamp": "now", "internal": True}])
+ assert(result[0]['success'])
+ result = self.psbt_offline.importdescriptors([{"desc": desc_pay, "active": True, "timestamp": "now"}])
+ assert(result[0]['success'])
+ result = self.psbt_offline.importdescriptors([{"desc": desc_change, "active": True, "timestamp": "now", "internal": True}])
+ assert(result[0]['success'])
+ for i in range(4):
+ addr_g = self.psbt_online.getnewaddress(address_type='bech32')
+ if treefn is not None:
+ addr_r = self.make_addr(treefn, keys_pay, i)
+ assert_equal(addr_g, addr_r)
+ boring_balance = int(self.boring.getbalance() * 100000000)
+ to_amnt = random.randrange(1000000, boring_balance)
+ self.boring.sendtoaddress(address=addr_g, amount=Decimal(to_amnt) / 100000000, subtractfeefromamount=True)
+ self.nodes[0].generatetoaddress(1, self.boring.getnewaddress())
+ test_balance = int(self.psbt_online.getbalance() * 100000000)
+ ret_amnt = random.randrange(100000, test_balance)
+ psbt = self.psbt_online.walletcreatefundedpsbt([], [{self.boring.getnewaddress(): Decimal(ret_amnt) / 100000000}], None, {"subtractFeeFromOutputs":[0]})['psbt']
+ res = self.psbt_offline.walletprocesspsbt(psbt)
+ assert(res['complete'])
+ rawtx = self.nodes[0].finalizepsbt(res['psbt'])['hex']
+ txid = self.nodes[0].sendrawtransaction(rawtx)
+ self.nodes[0].generatetoaddress(1, self.boring.getnewaddress())
+ assert(self.psbt_online.gettransaction(txid)['confirmations'] > 0)
+
def do_test(self, comment, pattern, privmap, treefn, nkeys):
- keys = self.rand_keys(nkeys)
- self.do_test_addr(comment, pattern, privmap, treefn, keys)
+ keys = self.rand_keys(nkeys * 4)
+ self.do_test_addr(comment, pattern, privmap, treefn, keys[0:nkeys])
+ self.do_test_sendtoaddress(comment, pattern, privmap, treefn, keys[0:nkeys], keys[nkeys:2*nkeys])
+ self.do_test_psbt(comment, pattern, privmap, treefn, keys[2*nkeys:3*nkeys], keys[3*nkeys:4*nkeys])
def run_test(self):
self.log.info("Creating wallets...")
+ self.nodes[0].createwallet(wallet_name="privs_tr_enabled", descriptors=True, blank=True)
+ self.privs_tr_enabled = self.nodes[0].get_wallet_rpc("privs_tr_enabled")
+ self.nodes[2].createwallet(wallet_name="privs_tr_disabled", descriptors=True, blank=True)
+ self.privs_tr_disabled=self.nodes[2].get_wallet_rpc("privs_tr_disabled")
+ self.nodes[0].createwallet(wallet_name="pubs_tr_enabled", descriptors=True, blank=True, disable_private_keys=True)
+ self.pubs_tr_enabled = self.nodes[0].get_wallet_rpc("pubs_tr_enabled")
+ self.nodes[2].createwallet(wallet_name="pubs_tr_disabled", descriptors=True, blank=True, disable_private_keys=True)
+ self.pubs_tr_disabled=self.nodes[2].get_wallet_rpc("pubs_tr_disabled")
+ self.nodes[0].createwallet(wallet_name="boring")
self.nodes[0].createwallet(wallet_name="addr_gen", descriptors=True, disable_private_keys=True, blank=True)
+ self.nodes[0].createwallet(wallet_name="rpc_online", descriptors=True, blank=True)
+ self.nodes[0].createwallet(wallet_name="psbt_online", descriptors=True, disable_private_keys=True, blank=True)
+ self.nodes[1].createwallet(wallet_name="psbt_offline", descriptors=True, blank=True)
+ self.boring = self.nodes[0].get_wallet_rpc("boring")
self.addr_gen = self.nodes[0].get_wallet_rpc("addr_gen")
+ self.rpc_online = self.nodes[0].get_wallet_rpc("rpc_online")
+ self.psbt_online = self.nodes[0].get_wallet_rpc("psbt_online")
+ self.psbt_offline = self.nodes[1].get_wallet_rpc("psbt_offline")
+
+ self.log.info("Mining blocks...")
+ gen_addr = self.boring.getnewaddress()
+ self.nodes[0].generatetoaddress(101, gen_addr)
self.do_test(
"tr(XPRV)",
@@ -254,6 +354,13 @@ class WalletTaprootTest(BitcoinTestFramework):
1
)
self.do_test(
+ "wpkh(XPRV)",
+ "wpkh($1/*)",
+ [True],
+ None,
+ 1
+ )
+ self.do_test(
"tr(XPRV,{H,{H,XPUB}})",
"tr($1/*,{pk($H),{pk($H),pk($2/*)}})",
[True, False],
@@ -261,6 +368,13 @@ class WalletTaprootTest(BitcoinTestFramework):
2
)
self.do_test(
+ "wsh(multi(1,XPRV,XPUB))",
+ "wsh(multi(1,$1/*,$2/*))",
+ [True, False],
+ None,
+ 2
+ )
+ self.do_test(
"tr(XPUB,{{H,{H,XPUB}},{H,{H,{H,XPRV}}}})",
"tr($1/*,{{pk($H),{pk($H),pk($2/*)}},{pk($H),{pk($H),{pk($H),pk($3/*)}}}})",
[False, False, True],
@@ -268,5 +382,19 @@ class WalletTaprootTest(BitcoinTestFramework):
3
)
+ self.log.info("Sending everything back...")
+
+ txid = self.rpc_online.sendtoaddress(address=self.boring.getnewaddress(), amount=self.rpc_online.getbalance(), subtractfeefromamount=True)
+ self.nodes[0].generatetoaddress(1, self.boring.getnewaddress())
+ assert(self.rpc_online.gettransaction(txid)["confirmations"] > 0)
+
+ psbt = self.psbt_online.walletcreatefundedpsbt([], [{self.boring.getnewaddress(): self.psbt_online.getbalance()}], None, {"subtractFeeFromOutputs": [0]})['psbt']
+ res = self.psbt_offline.walletprocesspsbt(psbt)
+ assert(res['complete'])
+ rawtx = self.nodes[0].finalizepsbt(res['psbt'])['hex']
+ txid = self.nodes[0].sendrawtransaction(rawtx)
+ self.nodes[0].generatetoaddress(1, self.boring.getnewaddress())
+ assert(self.psbt_online.gettransaction(txid)['confirmations'] > 0)
+
if __name__ == '__main__':
WalletTaprootTest().main()
diff --git a/test/lint/lint-circular-dependencies.sh b/test/lint/lint-circular-dependencies.sh
index 07d613f62d..f8f24bb1ff 100755
--- a/test/lint/lint-circular-dependencies.sh
+++ b/test/lint/lint-circular-dependencies.sh
@@ -17,7 +17,6 @@ EXPECTED_CIRCULAR_DEPENDENCIES=(
"index/coinstatsindex -> node/coinstats -> index/coinstatsindex"
"policy/fees -> txmempool -> policy/fees"
"qt/addresstablemodel -> qt/walletmodel -> qt/addresstablemodel"
- "qt/bitcoingui -> qt/walletframe -> qt/bitcoingui"
"qt/recentrequeststablemodel -> qt/walletmodel -> qt/recentrequeststablemodel"
"qt/sendcoinsdialog -> qt/walletmodel -> qt/sendcoinsdialog"
"qt/transactiontablemodel -> qt/walletmodel -> qt/transactiontablemodel"
diff --git a/test/sanitizer_suppressions/ubsan b/test/sanitizer_suppressions/ubsan
index 2850cfcea5..877adaccec 100644
--- a/test/sanitizer_suppressions/ubsan
+++ b/test/sanitizer_suppressions/ubsan
@@ -34,6 +34,9 @@ unsigned-integer-overflow:crypto/
unsigned-integer-overflow:FuzzedDataProvider.h
unsigned-integer-overflow:hash.cpp
unsigned-integer-overflow:leveldb/
+# temporary coinstats suppressions (will be removed and fixed in https://github.com/bitcoin/bitcoin/pull/22146)
+unsigned-integer-overflow:node/coinstats.cpp
+signed-integer-overflow:node/coinstats.cpp
unsigned-integer-overflow:policy/fees.cpp
unsigned-integer-overflow:prevector.h
unsigned-integer-overflow:pubkey.h