diff options
193 files changed, 6421 insertions, 3151 deletions
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index eedeeb4e54..0000000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,27 +0,0 @@ -<!-- This issue tracker is only for technical issues related to Bitcoin Core. - -General bitcoin questions and/or support requests are best directed to the Bitcoin StackExchange at https://bitcoin.stackexchange.com. - -For reporting security issues, please read instructions at https://bitcoincore.org/en/contact/. - -If the node is "stuck" during sync or giving "block checksum mismatch" errors, please ensure your hardware is stable by running memtest and observe CPU temperature with a load-test tool such as linpack before creating an issue! - -Any report, issue or feature request related to the GUI should be reported at -https://github.com/bitcoin-core/gui/issues/ ---> - -<!-- Describe the issue --> -<!--- What behavior did you expect? --> - -<!--- What was the actual behavior (provide screenshots if the issue is GUI-related)? --> - -<!--- How reliably can you reproduce the issue, what are the steps to do so? --> - -<!-- What version of Bitcoin Core are you using, where did you get it (website, self-compiled, etc)? --> - -<!-- What type of machine are you observing the error on (OS/CPU and disk type)? --> - -<!-- GUI-related issue? What is your operating system and its version? If Linux, what is your desktop environment and graphical shell? --> - -<!-- Any extra information that might be useful in the debugging process. --> -<!--- This is normally the contents of a `debug.log` or `config.log` file. Raw text or a link to a pastebin type site are preferred. --> diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml new file mode 100644 index 0000000000..83922b54cb --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -0,0 +1,93 @@ +name: Bug report +description: Submit a new bug report. +labels: [bug] +body: + - type: markdown + attributes: + value: | + ## This issue tracker is only for technical issues related to Bitcoin Core. + + * General bitcoin questions and/or support requests should use Bitcoin StackExchange at https://bitcoin.stackexchange.com. + * For reporting security issues, please read instructions at https://bitcoincore.org/en/contact/. + * If the node is "stuck" during sync or giving "block checksum mismatch" errors, please ensure your hardware is stable by running `memtest` and observe CPU temperature with a load-test tool such as `linpack` before creating an issue. + + ---- + - type: checkboxes + attributes: + label: Is there an existing issue for this? + description: Please search to see if an issue already exists for the bug you encountered. + options: + - label: I have searched the existing issues + required: true + - type: textarea + id: current-behaviour + attributes: + label: Current behaviour + description: Tell us what went wrong + validations: + required: true + - type: textarea + id: expected-behaviour + attributes: + label: Expected behaviour + description: Tell us what you expected to happen + validations: + required: true + - type: textarea + id: reproduction-steps + attributes: + label: Steps to reproduce + description: | + Tell us how to reproduce your bug. Please attach related screenshots if necessary. + * Run-time or compile-time configuration options + * Actions taken + validations: + required: true + - type: textarea + id: logs + attributes: + label: Relevant log output + description: | + Please copy and paste any relevant log output or attach a debug log file. + + You can find the debug.log in your [data dir.](https://github.com/bitcoin/bitcoin/blob/master/doc/files.md#data-directory-location) + + Please be aware that the debug log might contain personally identifying information. + validations: + required: false + - type: dropdown + attributes: + label: How did you obtain Bitcoin Core + multiple: false + options: + - Compiled from source + - Pre-built binaries + - Package manager + - Other + validations: + required: true + - type: input + id: core-version + attributes: + label: What version of Bitcoin Core are you using? + description: Run `bitcoind --version` or in Bitcoin-QT use `Help > About Bitcoin Core` + placeholder: e.g. v24.0.1 or master@e1bf547 + validations: + required: true + - type: input + id: os + attributes: + label: Operating system and version + placeholder: e.g. "MacOS Ventura 13.2" or "Ubuntu 22.04 LTS" + validations: + required: true + - type: textarea + id: machine-specs + attributes: + label: Machine specifications + description: | + What are the specifications of the host machine? + e.g. OS/CPU and disk type, network connectivity + validations: + required: false + diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index fb91208954..0000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,41 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve (use this for suspected bugs only, if not sure, open a regular issue below) -title: '' -labels: Bug -assignees: '' - ---- - -<!-- This issue tracker is only for technical issues related to Bitcoin Core. - -General bitcoin questions and/or support requests are best directed to the Bitcoin StackExchange at https://bitcoin.stackexchange.com. - -For reporting security issues, please read instructions at https://bitcoincore.org/en/contact/. - -If the node is "stuck" during sync or giving "block checksum mismatch" errors, please ensure your hardware is stable by running memtest and observe CPU temperature with a load-test tool such as linpack before creating an issue! --> - -<!-- Describe the issue --> - -**Expected behavior** - -<!--- What behavior did you expect? --> - -**Actual behavior** - -<!--- What was the actual behavior (provide screenshots if the issue is GUI-related)? --> - -**To reproduce** - -<!--- How reliably can you reproduce the issue, what are the steps to do so? --> - -**System information** - -<!-- What version of Bitcoin Core are you using, where did you get it (website, self-compiled, etc)? --> - -<!-- What type of machine are you observing the error on (OS/CPU and disk type)? --> - -<!-- GUI-related issue? What is your operating system and its version? If Linux, what is your desktop environment and graphical shell? --> - -<!-- Any extra information that might be useful in the debugging process. --> -<!--- This is normally the contents of a `debug.log` or `config.log` file. Raw text or a link to a pastebin type site are preferred. --> diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000000..40370284a6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: true +contact_links: + - name: Bitcoin Core Security Policy + url: https://github.com/bitcoin/bitcoin/blob/master/SECURITY.md + about: View security policy + - name: Bitcoin Core Developers + url: https://bitcoincore.org + about: Bitcoin Core homepage diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 2d5685185e..0000000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: '' -labels: Feature -assignees: '' - ---- - -**Is your feature request related to a problem? Please describe.** -<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] --> - -**Describe the solution you'd like** -<!-- A clear and concise description of what you want to happen. --> - -**Describe alternatives you've considered** -<!-- A clear and concise description of any alternative solutions or features you've considered. --> - -**Additional context** -<!-- Add any other context or screenshots about the feature request here. --> diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000000..4622fd9819 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,36 @@ +name: Feature Request +description: Suggest an idea for this project. +labels: [Feature] +body: + - type: textarea + id: feature + attributes: + label: Please describe the feature you'd like to see added. + description: Attach screenshots or logs if applicable. + validations: + required: true + - type: textarea + id: related-problem + attributes: + label: Is your feature related to a problem, if so please describe it. + description: Attach screenshots or logs if applicable. + validations: + required: false + - type: textarea + id: solution + attributes: + label: Describe the solution you'd like + validations: + required: false + - type: textarea + id: alternatives + attributes: + label: Describe any alternatives you've considered + validations: + required: false + - type: textarea + id: additional-context + attributes: + label: Please leave any additional context + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/good_first_issue.md b/.github/ISSUE_TEMPLATE/good_first_issue.md deleted file mode 100644 index ff943032ba..0000000000 --- a/.github/ISSUE_TEMPLATE/good_first_issue.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -name: Good first issue -about: '(Regular devs only): Suggest a new good first issue' -title: '' -labels: '' -assignees: '' - ---- - -<!-- Needs the label "good first issue" assigned manually before or after opening --> - -<!-- A good first issue is an uncontroversial issue, that has a relatively unique and obvious solution --> - -<!-- Motivate the issue and explain the solution briefly --> - -#### Useful skills: - -<!-- (For example, “std::thread”, “Qt5 GUI and async GUI design” or “basic understanding of Bitcoin mining and the Bitcoin Core RPC interface”.) --> - -#### Want to work on this issue? - -For guidance on contributing, please read [CONTRIBUTING.md](https://github.com/bitcoin/bitcoin/blob/master/CONTRIBUTING.md) before opening your pull request. diff --git a/.github/ISSUE_TEMPLATE/good_first_issue.yml b/.github/ISSUE_TEMPLATE/good_first_issue.yml new file mode 100644 index 0000000000..c40dad9687 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/good_first_issue.yml @@ -0,0 +1,42 @@ +name: Good First Issue +description: (Regular devs only) Suggest a new good first issue +labels: [good first issue] +body: + - type: markdown + attributes: + value: | + Please add the label "good first issue" manually before or after opening + + A good first issue is an uncontroversial issue, that has a relatively unique and obvious solution + + Motivate the issue and explain the solution briefly + - type: textarea + id: motivation + attributes: + label: Motivation + description: Motivate the issue + validations: + required: true + - type: textarea + id: solution + attributes: + label: Possible solution + description: Describe a possible solution + validations: + required: false + - type: textarea + id: useful-skills + attributes: + label: Useful Skills + description: For example, “`std::thread`”, “Qt5 GUI and async GUI design” or “basic understanding of Bitcoin mining and the Bitcoin Core RPC interface”. + validations: + required: false + - type: textarea + attributes: + label: Guidance for new contributors + description: Please leave this to automatically add the footer for new contributors + value: | + Want to work on this issue? + + For guidance on contributing, please read [CONTRIBUTING.md](https://github.com/bitcoin/bitcoin/blob/master/CONTRIBUTING.md) before opening your pull request. + diff --git a/.github/ISSUE_TEMPLATE/gui_issue.md b/.github/ISSUE_TEMPLATE/gui_issue.md deleted file mode 100644 index 37acc81e21..0000000000 --- a/.github/ISSUE_TEMPLATE/gui_issue.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -name: An issue or feature request related to the GUI -about: Any report, issue or feature request related to the GUI should be reported at https://github.com/bitcoin-core/gui/issues/ -title: Any report, issue or feature request related to the GUI should be reported at https://github.com/bitcoin-core/gui/issues/ -labels: GUI -assignees: '' - ---- - -Any report, issue or feature request related to the GUI should be reported at -https://github.com/bitcoin-core/gui/issues/ diff --git a/.github/ISSUE_TEMPLATE/gui_issue.yml b/.github/ISSUE_TEMPLATE/gui_issue.yml new file mode 100644 index 0000000000..4fe578e9b5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/gui_issue.yml @@ -0,0 +1,18 @@ +name: Issue or feature request related to the GUI +description: Any report, issue or feature request related to the GUI +labels: [GUI] +body: +- type: checkboxes + id: acknowledgement + attributes: + label: Issues, reports or feature requests related to the GUI should be opened directly on the GUI repo + description: https://github.com/bitcoin-core/gui/issues/ + options: + - label: I still think this issue should be opened here + required: true +- type: textarea + id: gui-request + attributes: + label: Report + validations: + required: true diff --git a/ci/test/00_setup_env.sh b/ci/test/00_setup_env.sh index ab830b8ec0..e6905b6dbe 100755 --- a/ci/test/00_setup_env.sh +++ b/ci/test/00_setup_env.sh @@ -44,7 +44,6 @@ export RUN_SECURITY_TESTS=${RUN_SECURITY_TESTS:-false} export TEST_RUNNER_TIMEOUT_FACTOR=${TEST_RUNNER_TIMEOUT_FACTOR:-40} export TEST_RUNNER_ENV=${TEST_RUNNER_ENV:-} export RUN_FUZZ_TESTS=${RUN_FUZZ_TESTS:-false} -export EXPECTED_TESTS_DURATION_IN_SECONDS=${EXPECTED_TESTS_DURATION_IN_SECONDS:-1000} export CONTAINER_NAME=${CONTAINER_NAME:-ci_unnamed} export CI_IMAGE_NAME_TAG=${CI_IMAGE_NAME_TAG:-ubuntu:20.04} diff --git a/ci/test/00_setup_env_win64.sh b/ci/test/00_setup_env_win64.sh index 817ee724c2..3adfbf6e47 100755 --- a/ci/test/00_setup_env_win64.sh +++ b/ci/test/00_setup_env_win64.sh @@ -13,4 +13,4 @@ export DPKG_ADD_ARCH="i386" export PACKAGES="python3 nsis g++-mingw-w64-x86-64-posix wine-binfmt wine64 wine32 file" export RUN_FUNCTIONAL_TESTS=false export GOAL="deploy" -export BITCOIN_CONFIG="--enable-reduce-exports --disable-external-signer --disable-gui-tests" +export BITCOIN_CONFIG="--enable-reduce-exports --enable-external-signer --disable-gui-tests" diff --git a/ci/test/06_script_b.sh b/ci/test/06_script_b.sh index 115d727ca3..7a01672d18 100755 --- a/ci/test/06_script_b.sh +++ b/ci/test/06_script_b.sh @@ -42,6 +42,7 @@ if [ "${RUN_TIDY}" = "true" ]; then ( CI_EXEC run-clang-tidy -quiet "${MAKEJOBS}" ) | grep -C5 "error" export P_CI_DIR="${BASE_BUILD_DIR}/bitcoin-$HOST/" CI_EXEC "python3 ${DIR_IWYU}/include-what-you-use/iwyu_tool.py"\ + " src/common/init.cpp"\ " src/common/url.cpp"\ " src/compat"\ " src/dbwrapper.cpp"\ @@ -50,12 +51,14 @@ if [ "${RUN_TIDY}" = "true" ]; then " src/node/chainstate.cpp"\ " src/node/chainstatemanager_args.cpp"\ " src/node/mempool_args.cpp"\ + " src/node/minisketchwrapper.cpp"\ " src/node/utxo_snapshot.cpp"\ " src/node/validation_cache_args.cpp"\ " src/policy/feerate.cpp"\ " src/policy/packages.cpp"\ " src/policy/settings.cpp"\ " src/primitives/transaction.cpp"\ + " src/random.cpp"\ " src/rpc/fees.cpp"\ " src/rpc/signmessage.cpp"\ " src/test/fuzz/txorphan.cpp"\ @@ -66,6 +69,7 @@ if [ "${RUN_TIDY}" = "true" ]; then " src/util/bytevectorhash.cpp"\ " src/util/check.cpp"\ " src/util/error.cpp"\ + " src/util/exception.cpp"\ " src/util/getuniquepath.cpp"\ " src/util/hasher.cpp"\ " src/util/message.cpp"\ diff --git a/configure.ac b/configure.ac index 5f09c8a5b6..cbe3dbcf19 100644 --- a/configure.ac +++ b/configure.ac @@ -1492,45 +1492,45 @@ if test "$use_boost" = "yes"; then fi if test "$use_external_signer" != "no"; then - case $host in - *mingw*) - dnl Boost Process uses Boost Filesystem when targeting Windows. Also, - dnl since Boost 1.71.0, Process does not work with mingw-w64 without - dnl workarounds. See 67669ab425b52a2b6be3d2f3b3b7e3939b676a2c. - if test "$use_external_signer" = "yes"; then - AC_MSG_ERROR([External signing is not supported on Windows]) - fi - use_external_signer="no"; - ;; - *) - AC_MSG_CHECKING([whether Boost.Process can be used]) - TEMP_CXXFLAGS="$CXXFLAGS" - dnl Boost 1.78 requires the following workaround. - dnl See: https://github.com/boostorg/process/issues/235 - CXXFLAGS="$CXXFLAGS -Wno-error=narrowing" - TEMP_CPPFLAGS="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" - TEMP_LDFLAGS="$LDFLAGS" - dnl Boost 1.73 and older require the following workaround. - LDFLAGS="$LDFLAGS $PTHREAD_CFLAGS" - AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <boost/process.hpp>]])], - [have_boost_process="yes"], - [have_boost_process="no"]) - LDFLAGS="$TEMP_LDFLAGS" - CPPFLAGS="$TEMP_CPPFLAGS" - CXXFLAGS="$TEMP_CXXFLAGS" - AC_MSG_RESULT([$have_boost_process]) - if test "$have_boost_process" = "yes"; then - use_external_signer="yes" - AC_DEFINE([ENABLE_EXTERNAL_SIGNER], [1], [Define if external signer support is enabled]) - else - if test "$use_external_signer" = "yes"; then - AC_MSG_ERROR([External signing is not supported for this Boost version]) - fi - use_external_signer="no"; - fi - ;; - esac + AC_MSG_CHECKING([whether Boost.Process can be used]) + TEMP_CXXFLAGS="$CXXFLAGS" + dnl Boost 1.78 requires the following workaround. + dnl See: https://github.com/boostorg/process/issues/235 + CXXFLAGS="$CXXFLAGS -Wno-error=narrowing" + TEMP_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + TEMP_LDFLAGS="$LDFLAGS" + dnl Boost 1.73 and older require the following workaround. + LDFLAGS="$LDFLAGS $PTHREAD_CFLAGS" + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + #define BOOST_PROCESS_USE_STD_FS + #include <boost/process.hpp> + ]],[[ + namespace bp = boost::process; + bp::opstream stdin_stream; + bp::ipstream stdout_stream; + bp::child c("dummy", bp::std_out > stdout_stream, bp::std_err > stdout_stream, bp::std_in < stdin_stream); + stdin_stream << std::string{"test"} << std::endl; + if (c.running()) c.terminate(); + c.wait(); + c.exit_code(); + ]])], + [have_boost_process="yes"], + [have_boost_process="no"]) + LDFLAGS="$TEMP_LDFLAGS" + CPPFLAGS="$TEMP_CPPFLAGS" + CXXFLAGS="$TEMP_CXXFLAGS" + AC_MSG_RESULT([$have_boost_process]) + if test "$have_boost_process" = "yes"; then + use_external_signer="yes" + AC_DEFINE([ENABLE_EXTERNAL_SIGNER], [1], [Define if external signer support is enabled]) + AC_DEFINE([BOOST_PROCESS_USE_STD_FS], [1], [Defined to avoid Boost::Process trying to use Boost Filesystem]) + else + if test "$use_external_signer" = "yes"; then + AC_MSG_ERROR([External signing is not supported for this Boost version]) + fi + use_external_signer="no"; + fi fi AM_CONDITIONAL([ENABLE_EXTERNAL_SIGNER], [test "$use_external_signer" = "yes"]) diff --git a/contrib/guix/manifest.scm b/contrib/guix/manifest.scm index e266e7d0ee..dc42563cdd 100644 --- a/contrib/guix/manifest.scm +++ b/contrib/guix/manifest.scm @@ -139,9 +139,11 @@ chain for " target " development.")) ;; https://gcc.gnu.org/install/configure.html (define (hardened-gcc gcc) (package-with-extra-configure-variable ( - package-with-extra-configure-variable gcc - "--enable-default-ssp" "yes") - "--enable-default-pie" "yes")) + package-with-extra-configure-variable ( + package-with-extra-configure-variable gcc + "--enable-initfini-array" "yes") + "--enable-default-ssp" "yes") + "--enable-default-pie" "yes")) (define* (make-bitcoin-cross-toolchain target #:key diff --git a/doc/REST-interface.md b/doc/REST-interface.md index a704b969df..2d7d0e3769 100644 --- a/doc/REST-interface.md +++ b/doc/REST-interface.md @@ -134,11 +134,15 @@ Returns various information about the transaction mempool. Only supports JSON as output format. Refer to the `getmempoolinfo` RPC help for details. -`GET /rest/mempool/contents.json` +`GET /rest/mempool/contents.json?verbose=<true|false>&mempool_sequence=<false|true>` Returns the transactions in the mempool. Only supports JSON as output format. -Refer to the `getrawmempool` RPC help for details. +Refer to the `getrawmempool` RPC help for details. Defaults to setting +`verbose=true` and `mempool_sequence=false`. + +*Query parameters for `verbose` and `mempool_sequence` available in 25.0 and up.* + Risks ------------- diff --git a/doc/design/assumeutxo.md b/doc/design/assumeutxo.md index ea51b1b87f..469c551536 100644 --- a/doc/design/assumeutxo.md +++ b/doc/design/assumeutxo.md @@ -3,9 +3,9 @@ Assumeutxo is a feature that allows fast bootstrapping of a validating bitcoind instance with a very similar security model to assumevalid. -The RPC commands `dumptxoutset` and `loadtxoutset` are used to respectively generate -and load UTXO snapshots. The utility script `./contrib/devtools/utxo_snapshot.sh` may -be of use. +The RPC commands `dumptxoutset` and `loadtxoutset` (yet to be merged) are used to +respectively generate and load UTXO snapshots. The utility script +`./contrib/devtools/utxo_snapshot.sh` may be of use. ## General background @@ -22,10 +22,6 @@ be of use. chainstate running asynchronously in the background. We also use this flag to control which index entries are added to setBlockIndexCandidates during LoadBlockIndex(). -- Indexing implementations via BaseIndex can no longer assume that indexation happens - sequentially, since background validation chainstates can submit BlockConnected - events out of order with the active chain. - - The concept of UTXO snapshots is treated as an implementation detail that lives behind the ChainstateManager interface. The external presentation of the changes required to facilitate the use of UTXO snapshots is the understanding that there are @@ -76,9 +72,15 @@ original chainstate remains in use as active. Once the snapshot chainstate is loaded and validated, it is promoted to active chainstate and a sync to tip begins. A new chainstate directory is created in the -datadir for the snapshot chainstate called `chainstate_snapshot`. When this directory -is present in the datadir, the snapshot chainstate will be detected and loaded as -active on node startup (via `DetectSnapshotChainstate()`). +datadir for the snapshot chainstate called `chainstate_snapshot`. + +When this directory is present in the datadir, the snapshot chainstate will be detected +and loaded as active on node startup (via `DetectSnapshotChainstate()`). + +A special file is created within that directory, `base_blockhash`, which contains the +serialized `uint256` of the base block of the snapshot. This is used to reinitialize +the snapshot chainstate on subsequent inits. Otherwise, the directory is a normal +leveldb database. | | | | ---------- | ----------- | @@ -88,7 +90,7 @@ active on node startup (via `DetectSnapshotChainstate()`). The snapshot begins to sync to tip from its base block, technically in parallel with the original chainstate, but it is given priority during block download and is allocated most of the cache (see `MaybeRebalanceCaches()` and usages) as our chief -consideration is getting to network tip. +goal is getting to network tip. **Failure consideration:** if shutdown happens at any point during this phase, both chainstates will be detected during the next init and the process will resume. @@ -107,33 +109,32 @@ sequentially. ### Background chainstate hits snapshot base block Once the tip of the background chainstate hits the base block of the snapshot -chainstate, we stop use of the background chainstate by setting `m_stop_use` (not yet -committed - see #15606), in `CompleteSnapshotValidation()`, which is checked in -`ActivateBestChain()`). We hash the background chainstate's UTXO set contents and -ensure it matches the compiled value in `CMainParams::m_assumeutxo_data`. - -The background chainstate data lingers on disk until shutdown, when in -`ChainstateManager::Reset()`, the background chainstate is cleaned up with -`ValidatedSnapshotShutdownCleanup()`, which renames the `chainstate_[hash]` datadir as -`chainstate`. +chainstate, we stop use of the background chainstate by setting `m_disabled`, in +`CompleteSnapshotValidation()`, which is checked in `ActivateBestChain()`). We hash the +background chainstate's UTXO set contents and ensure it matches the compiled value in +`CMainParams::m_assumeutxo_data`. | | | | ---------- | ----------- | -| number of chainstates | 2 (ibd has `m_stop_use=true`) | +| number of chainstates | 2 (ibd has `m_disabled=true`) | | active chainstate | snapshot | -**Failure consideration:** if bitcoind unexpectedly halts after `m_stop_use` is set on -the background chainstate but before `CompleteSnapshotValidation()` can finish, the -need to complete snapshot validation will be detected on subsequent init by -`ChainstateManager::CheckForUncleanShutdown()`. +The background chainstate data lingers on disk until the program is restarted. ### Bitcoind restarts sometime after snapshot validation has completed -When bitcoind initializes again, what began as the snapshot chainstate is now -indistinguishable from a chainstate that has been built from the traditional IBD -process, and will be initialized as such. +After a shutdown and subsequent restart, `LoadChainstate()` cleans up the background +chainstate with `ValidatedSnapshotCleanup()`, which renames the `chainstate_snapshot` +datadir as `chainstate` and removes the now unnecessary background chainstate data. | | | | ---------- | ----------- | | number of chainstates | 1 | -| active chainstate | ibd | +| active chainstate | ibd (was snapshot, but is now fully validated) | + +What began as the snapshot chainstate is now indistinguishable from a chainstate that +has been built from the traditional IBD process, and will be initialized as such. + +A file will be left in `chainstate/base_blockhash`, which indicates that the +chainstate, even though now fully validated, was originally started from a snapshot +with the corresponding base blockhash. diff --git a/doc/developer-notes.md b/doc/developer-notes.md index f15df1bf73..ceaba8cb99 100644 --- a/doc/developer-notes.md +++ b/doc/developer-notes.md @@ -217,11 +217,14 @@ apt install clang-tidy bear clang Then, pass clang as compiler to configure, and use bear to produce the `compile_commands.json`: ```sh -./autogen.sh && ./configure CC=clang CXX=clang++ -make clean && bear make -j $(nproc) # For bear 2.x -make clean && bear -- make -j $(nproc) # For bear 3.x +./autogen.sh && ./configure CC=clang CXX=clang++ --enable-suppress-external-warnings +make clean && bear --config src/.bear-tidy-config -- make -j $(nproc) ``` +The output is denoised of errors from external dependencies and includes with +`--enable-suppress-external-warnings` and `--config src/.bear-tidy-config`. Both +options may be omitted to view the full list of errors. + To run clang-tidy on all source files: ```sh @@ -857,12 +860,12 @@ Strings and formatting buffer overflows, and surprises with `\0` characters. Also, some C string manipulations tend to act differently depending on platform, or even the user locale. -- Use `ParseInt32`, `ParseInt64`, `ParseUInt32`, `ParseUInt64`, `ParseDouble` from `utilstrencodings.h` for number parsing. +- Use `ToIntegral` from [`strencodings.h`](/src/util/strencodings.h) for number parsing. In legacy code you might also find `ParseInt*` family of functions, `ParseDouble` or `LocaleIndependentAtoi`. - *Rationale*: These functions do overflow checking and avoid pesky locale issues. - Avoid using locale dependent functions if possible. You can use the provided - [`lint-locale-dependence.sh`](/test/lint/lint-locale-dependence.sh) + [`lint-locale-dependence.py`](/test/lint/lint-locale-dependence.py) to check for accidental use of locale dependent functions. - *Rationale*: Unnecessary locale dependence can cause bugs that are very tricky to isolate and fix. diff --git a/doc/release-note-26194.md b/doc/release-note-26194.md new file mode 100644 index 0000000000..b72dbf9a23 --- /dev/null +++ b/doc/release-note-26194.md @@ -0,0 +1,4 @@ +Add `next_index` in `listdescriptors` RPC +----------------- + +- Added a new `next_index` field in the response in `listdescriptors` to have the same format as `importdescriptors` diff --git a/src/Makefile.am b/src/Makefile.am index 72e7db5334..53c809c901 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -134,6 +134,7 @@ BITCOIN_CORE_H = \ clientversion.h \ coins.h \ common/bloom.h \ + common/init.h \ common/run_command.h \ common/url.h \ compat/assumptions.h \ @@ -173,7 +174,9 @@ BITCOIN_CORE_H = \ interfaces/ipc.h \ interfaces/node.h \ interfaces/wallet.h \ + kernel/blockmanager_opts.h \ kernel/chain.h \ + kernel/chainparams.h \ kernel/chainstatemanager_opts.h \ kernel/checks.h \ kernel/coinstats.h \ @@ -199,6 +202,7 @@ BITCOIN_CORE_H = \ netbase.h \ netgroup.h \ netmessagemaker.h \ + node/blockmanager_args.h \ node/blockstorage.h \ node/caches.h \ node/chainstate.h \ @@ -278,6 +282,7 @@ BITCOIN_CORE_H = \ util/check.h \ util/epochguard.h \ util/error.h \ + util/exception.h \ util/fastrange.h \ util/fees.h \ util/getuniquepath.h \ @@ -385,6 +390,7 @@ libbitcoin_node_a_SOURCES = \ net.cpp \ net_processing.cpp \ netgroup.cpp \ + node/blockmanager_args.cpp \ node/blockstorage.cpp \ node/caches.cpp \ node/chainstate.cpp \ @@ -640,6 +646,7 @@ libbitcoin_common_a_SOURCES = \ chainparams.cpp \ coins.cpp \ common/bloom.cpp \ + common/init.cpp \ common/interfaces.cpp \ common/run_command.cpp \ compressor.cpp \ @@ -648,6 +655,7 @@ libbitcoin_common_a_SOURCES = \ deploymentinfo.cpp \ external_signer.cpp \ init/common.cpp \ + kernel/chainparams.cpp \ key.cpp \ key_io.cpp \ merkleblock.cpp \ @@ -697,6 +705,7 @@ libbitcoin_util_a_SOURCES = \ util/bytevectorhash.cpp \ util/check.cpp \ util/error.cpp \ + util/exception.cpp \ util/fees.cpp \ util/getuniquepath.cpp \ util/hasher.cpp \ @@ -903,6 +912,7 @@ libbitcoinkernel_la_SOURCES = \ hash.cpp \ kernel/chain.cpp \ kernel/checks.cpp \ + kernel/chainparams.cpp \ kernel/coinstats.cpp \ kernel/context.cpp \ kernel/cs_main.cpp \ @@ -940,6 +950,7 @@ libbitcoinkernel_la_SOURCES = \ txmempool.cpp \ uint256.cpp \ util/check.cpp \ + util/exception.cpp \ util/getuniquepath.cpp \ util/hasher.cpp \ util/moneystr.cpp \ diff --git a/src/Makefile.test.include b/src/Makefile.test.include index fa77e28736..a39b0abd9d 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -147,6 +147,7 @@ BITCOIN_TESTS =\ test/timedata_tests.cpp \ test/torcontrol_tests.cpp \ test/transaction_tests.cpp \ + test/translation_tests.cpp \ test/txindex_tests.cpp \ test/txpackage_tests.cpp \ test/txreconciliation_tests.cpp \ @@ -179,7 +180,8 @@ BITCOIN_TESTS += \ wallet/test/ismine_tests.cpp \ wallet/test/rpc_util_tests.cpp \ wallet/test/scriptpubkeyman_tests.cpp \ - wallet/test/walletload_tests.cpp + wallet/test/walletload_tests.cpp \ + wallet/test/group_outputs_tests.cpp FUZZ_SUITE_LD_COMMON +=\ $(SQLITE_LIBS) \ diff --git a/src/addrdb.cpp b/src/addrdb.cpp index 7be13c8f1e..9ae8244d1c 100644 --- a/src/addrdb.cpp +++ b/src/addrdb.cpp @@ -11,6 +11,7 @@ #include <cstdint> #include <fs.h> #include <hash.h> +#include <logging.h> #include <logging/timer.h> #include <netbase.h> #include <netgroup.h> diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp index 11b0e0dee2..265d4bf655 100644 --- a/src/bench/coin_selection.cpp +++ b/src/bench/coin_selection.cpp @@ -75,8 +75,9 @@ static void CoinSelection(benchmark::Bench& bench) /*tx_noinputs_size=*/ 0, /*avoid_partial=*/ false, }; + auto group = wallet::GroupOutputs(wallet, available_coins, coin_selection_params, {{filter_standard}})[filter_standard]; bench.run([&] { - auto result = AttemptSelection(wallet, 1003 * COIN, filter_standard, available_coins, coin_selection_params, /*allow_mixed_output_types=*/true); + auto result = AttemptSelection(1003 * COIN, group, coin_selection_params, /*allow_mixed_output_types=*/true); assert(result); assert(result->GetSelectedValue() == 1003 * COIN); assert(result->GetInputSet().size() == 2); @@ -91,7 +92,7 @@ static void add_coin(const CAmount& nValue, int nInput, std::vector<OutputGroup> tx.vout[nInput].nValue = nValue; COutput output(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 0, /*input_bytes=*/ -1, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ true, /*fees=*/ 0); set.emplace_back(); - set.back().Insert(output, /*ancestors=*/ 0, /*descendants=*/ 0, /*positive_only=*/ false); + set.back().Insert(std::make_shared<COutput>(output), /*ancestors=*/ 0, /*descendants=*/ 0); } // Copied from src/wallet/test/coinselector_tests.cpp static CAmount make_hard_case(int utxos, std::vector<OutputGroup>& utxo_pool) diff --git a/src/bitcoin-chainstate.cpp b/src/bitcoin-chainstate.cpp index 423fa79c6f..abe3af70c3 100644 --- a/src/bitcoin-chainstate.cpp +++ b/src/bitcoin-chainstate.cpp @@ -11,6 +11,7 @@ // // It is part of the libbitcoinkernel project. +#include <kernel/chainparams.h> #include <kernel/checks.h> #include <kernel/context.h> #include <kernel/validation_cache_sizes.h> @@ -52,7 +53,7 @@ int main(int argc, char* argv[]) // SETUP: Misc Globals SelectParams(CBaseChainParams::MAIN); - const CChainParams& chainparams = Params(); + auto chainparams = CChainParams::Main(); kernel::Context kernel_context{}; // We can't use a goto here, but we can use an assert since none of the @@ -81,11 +82,11 @@ int main(int argc, char* argv[]) // SETUP: Chainstate const ChainstateManager::Options chainman_opts{ - .chainparams = chainparams, + .chainparams = *chainparams, .datadir = gArgs.GetDataDirNet(), .adjusted_time_callback = NodeClock::now, }; - ChainstateManager chainman{chainman_opts}; + ChainstateManager chainman{chainman_opts, {}}; node::CacheSizes cache_sizes; cache_sizes.block_tree_db = 2 << 20; diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index 77613f389c..eb52b30ae4 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -19,6 +19,7 @@ #include <rpc/request.h> #include <tinyformat.h> #include <univalue.h> +#include <util/exception.h> #include <util/strencodings.h> #include <util/system.h> #include <util/translation.h> diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index 57ca2bbe8a..f36b6054bb 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -12,14 +12,15 @@ #include <consensus/amount.h> #include <consensus/consensus.h> #include <core_io.h> -#include <key_io.h> #include <fs.h> +#include <key_io.h> #include <policy/policy.h> #include <primitives/transaction.h> #include <script/script.h> #include <script/sign.h> #include <script/signingprovider.h> #include <univalue.h> +#include <util/exception.h> #include <util/moneystr.h> #include <util/rbf.h> #include <util/strencodings.h> diff --git a/src/bitcoin-util.cpp b/src/bitcoin-util.cpp index 61d4b9c6f1..cf59ca9bff 100644 --- a/src/bitcoin-util.cpp +++ b/src/bitcoin-util.cpp @@ -14,6 +14,7 @@ #include <compat/compat.h> #include <core_io.h> #include <streams.h> +#include <util/exception.h> #include <util/system.h> #include <util/translation.h> #include <version.h> diff --git a/src/bitcoin-wallet.cpp b/src/bitcoin-wallet.cpp index fcb845fe64..1ebe98b920 100644 --- a/src/bitcoin-wallet.cpp +++ b/src/bitcoin-wallet.cpp @@ -16,6 +16,7 @@ #include <logging.h> #include <pubkey.h> #include <tinyformat.h> +#include <util/exception.h> #include <util/system.h> #include <util/translation.h> #include <wallet/wallettool.h> diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index 6851f86297..2ff34125e1 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -9,6 +9,7 @@ #include <chainparams.h> #include <clientversion.h> +#include <common/init.h> #include <common/url.h> #include <compat/compat.h> #include <init.h> @@ -19,6 +20,7 @@ #include <noui.h> #include <shutdown.h> #include <util/check.h> +#include <util/exception.h> #include <util/strencodings.h> #include <util/syscall_sandbox.h> #include <util/syserror.h> @@ -120,7 +122,7 @@ static bool AppInit(NodeContext& node, int argc, char* argv[]) SetupServerArgs(args); std::string error; if (!args.ParseParameters(argc, argv, error)) { - return InitError(Untranslated(strprintf("Error parsing command line arguments: %s\n", error))); + return InitError(Untranslated(strprintf("Error parsing command line arguments: %s", error))); } // Process help and version before taking care about datadir @@ -150,31 +152,17 @@ static bool AppInit(NodeContext& node, int argc, char* argv[]) std::any context{&node}; try { - if (!CheckDataDirOption(args)) { - return InitError(Untranslated(strprintf("Specified data directory \"%s\" does not exist.\n", args.GetArg("-datadir", "")))); - } - if (!args.ReadConfigFiles(error, true)) { - return InitError(Untranslated(strprintf("Error reading configuration file: %s\n", error))); - } - // Check for chain settings (Params() calls are only valid after this clause) - try { - SelectParams(args.GetChainName()); - } catch (const std::exception& e) { - return InitError(Untranslated(strprintf("%s\n", e.what()))); + if (auto error = common::InitConfig(args)) { + return InitError(error->message, error->details); } // Error out when loose non-argument tokens are encountered on command line for (int i = 1; i < argc; i++) { if (!IsSwitchChar(argv[i][0])) { - return InitError(Untranslated(strprintf("Command line contains unexpected token '%s', see bitcoind -h for a list of options.\n", argv[i]))); + return InitError(Untranslated(strprintf("Command line contains unexpected token '%s', see bitcoind -h for a list of options.", argv[i]))); } } - if (!args.InitSettings(error)) { - InitError(Untranslated(error)); - return false; - } - // -server defaults to true for bitcoind but not for the GUI so do this here args.SoftSetBoolArg("-server", true); // Set this early so that parameter interactions go to console @@ -210,7 +198,7 @@ static bool AppInit(NodeContext& node, int argc, char* argv[]) } break; case -1: // Error happened. - return InitError(Untranslated(strprintf("fork_daemon() failed: %s\n", SysErrorString(errno)))); + return InitError(Untranslated(strprintf("fork_daemon() failed: %s", SysErrorString(errno)))); default: { // Parent: wait and exit. int token = daemon_ep.TokenRead(); if (token) { // Success @@ -222,7 +210,7 @@ static bool AppInit(NodeContext& node, int argc, char* argv[]) } } #else - return InitError(Untranslated("-daemon is not supported on this operating system\n")); + return InitError(Untranslated("-daemon is not supported on this operating system")); #endif // HAVE_DECL_FORK } // Lock data directory after daemonization diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 19b5c332f4..6f48ee41b3 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -15,513 +15,43 @@ #include <assert.h> -static CBlock CreateGenesisBlock(const char* pszTimestamp, const CScript& genesisOutputScript, uint32_t nTime, uint32_t nNonce, uint32_t nBits, int32_t nVersion, const CAmount& genesisReward) +void ReadSigNetArgs(const ArgsManager& args, CChainParams::SigNetOptions& options) { - CMutableTransaction txNew; - txNew.nVersion = 1; - txNew.vin.resize(1); - txNew.vout.resize(1); - txNew.vin[0].scriptSig = CScript() << 486604799 << CScriptNum(4) << std::vector<unsigned char>((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp)); - txNew.vout[0].nValue = genesisReward; - txNew.vout[0].scriptPubKey = genesisOutputScript; - - CBlock genesis; - genesis.nTime = nTime; - genesis.nBits = nBits; - genesis.nNonce = nNonce; - genesis.nVersion = nVersion; - genesis.vtx.push_back(MakeTransactionRef(std::move(txNew))); - genesis.hashPrevBlock.SetNull(); - genesis.hashMerkleRoot = BlockMerkleRoot(genesis); - return genesis; -} - -/** - * Build the genesis block. Note that the output of its generation - * transaction cannot be spent since it did not originally exist in the - * database. - * - * CBlock(hash=000000000019d6, ver=1, hashPrevBlock=00000000000000, hashMerkleRoot=4a5e1e, nTime=1231006505, nBits=1d00ffff, nNonce=2083236893, vtx=1) - * CTransaction(hash=4a5e1e, ver=1, vin.size=1, vout.size=1, nLockTime=0) - * CTxIn(COutPoint(000000, -1), coinbase 04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73) - * CTxOut(nValue=50.00000000, scriptPubKey=0x5F1DF16B2B704C8A578D0B) - * vMerkleTree: 4a5e1e - */ -static CBlock CreateGenesisBlock(uint32_t nTime, uint32_t nNonce, uint32_t nBits, int32_t nVersion, const CAmount& genesisReward) -{ - const char* pszTimestamp = "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks"; - const CScript genesisOutputScript = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG; - return CreateGenesisBlock(pszTimestamp, genesisOutputScript, nTime, nNonce, nBits, nVersion, genesisReward); -} - -/** - * Main network on which people trade goods and services. - */ -class CMainParams : public CChainParams { -public: - CMainParams() { - strNetworkID = CBaseChainParams::MAIN; - consensus.signet_blocks = false; - consensus.signet_challenge.clear(); - consensus.nSubsidyHalvingInterval = 210000; - consensus.script_flag_exceptions.emplace( // BIP16 exception - uint256S("0x00000000000002dc756eebf4f49723ed8d30cc28a5f108eb94b1ba88ac4f9c22"), SCRIPT_VERIFY_NONE); - consensus.script_flag_exceptions.emplace( // Taproot exception - uint256S("0x0000000000000000000f14c35b2d841e986ab5441de8c585d5ffe55ea1e395ad"), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS); - consensus.BIP34Height = 227931; - consensus.BIP34Hash = uint256S("0x000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8"); - consensus.BIP65Height = 388381; // 000000000000000004c2b624ed5d7756c508d90fd0da2c7c679febfa6c4735f0 - consensus.BIP66Height = 363725; // 00000000000000000379eaa19dce8c9b722d46ae6a57c2f1a988119488b50931 - consensus.CSVHeight = 419328; // 000000000000000004a1b34462cb8aeebd5799177f7a29cf28f2d1961716b5b5 - consensus.SegwitHeight = 481824; // 0000000000000000001c8018d9cb3b742ef25114f27563e3fc4a1902167f9893 - consensus.MinBIP9WarningHeight = 483840; // segwit activation height + miner confirmation window - consensus.powLimit = uint256S("00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); - consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks - consensus.nPowTargetSpacing = 10 * 60; - consensus.fPowAllowMinDifficultyBlocks = false; - consensus.fPowNoRetargeting = false; - consensus.nRuleChangeActivationThreshold = 1815; // 90% of 2016 - consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing - consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28; - consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE; - consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; - consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].min_activation_height = 0; // No activation delay - - // Deployment of Taproot (BIPs 340-342) - consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2; - consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = 1619222400; // April 24th, 2021 - consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = 1628640000; // August 11th, 2021 - consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 709632; // Approximately November 12th, 2021 - - consensus.nMinimumChainWork = uint256S("0x00000000000000000000000000000000000000003404ba0801921119f903495e"); - consensus.defaultAssumeValid = uint256S("0x00000000000000000009c97098b5295f7e5f183ac811fb5d1534040adb93cabd"); // 751565 - - /** - * The message start string is designed to be unlikely to occur in normal data. - * The characters are rarely used upper ASCII, not valid as UTF-8, and produce - * a large 32-bit integer with any alignment. - */ - pchMessageStart[0] = 0xf9; - pchMessageStart[1] = 0xbe; - pchMessageStart[2] = 0xb4; - pchMessageStart[3] = 0xd9; - nDefaultPort = 8333; - nPruneAfterHeight = 100000; - m_assumed_blockchain_size = 496; - m_assumed_chain_state_size = 6; - - genesis = CreateGenesisBlock(1231006505, 2083236893, 0x1d00ffff, 1, 50 * COIN); - consensus.hashGenesisBlock = genesis.GetHash(); - assert(consensus.hashGenesisBlock == uint256S("0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f")); - assert(genesis.hashMerkleRoot == uint256S("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b")); - - // Note that of those which support the service bits prefix, most only support a subset of - // possible options. - // This is fine at runtime as we'll fall back to using them as an addrfetch if they don't support the - // service bits we want, but we should get them updated to support all service bits wanted by any - // release ASAP to avoid it where possible. - vSeeds.emplace_back("seed.bitcoin.sipa.be."); // Pieter Wuille, only supports x1, x5, x9, and xd - vSeeds.emplace_back("dnsseed.bluematt.me."); // Matt Corallo, only supports x9 - vSeeds.emplace_back("dnsseed.bitcoin.dashjr.org."); // Luke Dashjr - vSeeds.emplace_back("seed.bitcoinstats.com."); // Christian Decker, supports x1 - xf - vSeeds.emplace_back("seed.bitcoin.jonasschnelli.ch."); // Jonas Schnelli, only supports x1, x5, x9, and xd - vSeeds.emplace_back("seed.btc.petertodd.org."); // Peter Todd, only supports x1, x5, x9, and xd - vSeeds.emplace_back("seed.bitcoin.sprovoost.nl."); // Sjors Provoost - vSeeds.emplace_back("dnsseed.emzy.de."); // Stephan Oeste - vSeeds.emplace_back("seed.bitcoin.wiz.biz."); // Jason Maurice - - base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,0); - base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,5); - base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1,128); - base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x88, 0xB2, 0x1E}; - base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x88, 0xAD, 0xE4}; - - bech32_hrp = "bc"; - - vFixedSeeds = std::vector<uint8_t>(std::begin(chainparams_seed_main), std::end(chainparams_seed_main)); - - fDefaultConsistencyChecks = false; - fRequireStandard = true; - m_is_test_chain = false; - m_is_mockable_chain = false; - - checkpointData = { - { - { 11111, uint256S("0x0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d")}, - { 33333, uint256S("0x000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6")}, - { 74000, uint256S("0x0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20")}, - {105000, uint256S("0x00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97")}, - {134444, uint256S("0x00000000000005b12ffd4cd315cd34ffd4a594f430ac814c91184a0d42d2b0fe")}, - {168000, uint256S("0x000000000000099e61ea72015e79632f216fe6cb33d7899acb35b75c8303b763")}, - {193000, uint256S("0x000000000000059f452a5f7340de6682a977387c17010ff6e6c3bd83ca8b1317")}, - {210000, uint256S("0x000000000000048b95347e83192f69cf0366076336c639f9b7228e9ba171342e")}, - {216116, uint256S("0x00000000000001b4f4b433e81ee46494af945cf96014816a4e2370f11b23df4e")}, - {225430, uint256S("0x00000000000001c108384350f74090433e7fcf79a606b8e797f065b130575932")}, - {250000, uint256S("0x000000000000003887df1f29024b06fc2200b55f8af8f35453d7be294df2d214")}, - {279000, uint256S("0x0000000000000001ae8c72a0b0c301f67e3afca10e819efa9041e458e9bd7e40")}, - {295000, uint256S("0x00000000000000004d9b4ef50f0f9d686fd69db2e03af35a100370c64632a983")}, - } - }; - - m_assumeutxo_data = MapAssumeutxo{ - // TODO to be specified in a future patch. - }; - - chainTxData = ChainTxData{ - // Data from RPC: getchaintxstats 4096 00000000000000000009c97098b5295f7e5f183ac811fb5d1534040adb93cabd - .nTime = 1661697692, - .nTxCount = 760120522, - .dTxRate = 2.925802860942233, - }; - } -}; - -/** - * Testnet (v3): public test network which is reset from time to time. - */ -class CTestNetParams : public CChainParams { -public: - CTestNetParams() { - strNetworkID = CBaseChainParams::TESTNET; - consensus.signet_blocks = false; - consensus.signet_challenge.clear(); - consensus.nSubsidyHalvingInterval = 210000; - consensus.script_flag_exceptions.emplace( // BIP16 exception - uint256S("0x00000000dd30457c001f4095d208cc1296b0eed002427aa599874af7a432b105"), SCRIPT_VERIFY_NONE); - consensus.BIP34Height = 21111; - consensus.BIP34Hash = uint256S("0x0000000023b3a96d3484e5abb3755c413e7d41500f8e2a5c3f0dd01299cd8ef8"); - consensus.BIP65Height = 581885; // 00000000007f6655f22f98e72ed80d8b06dc761d5da09df0fa1dc4be4f861eb6 - consensus.BIP66Height = 330776; // 000000002104c8c45e99a8853285a3b592602a3ccde2b832481da85e9e4ba182 - consensus.CSVHeight = 770112; // 00000000025e930139bac5c6c31a403776da130831ab85be56578f3fa75369bb - consensus.SegwitHeight = 834624; // 00000000002b980fcd729daaa248fd9316a5200e9b367f4ff2c42453e84201ca - consensus.MinBIP9WarningHeight = 836640; // segwit activation height + miner confirmation window - consensus.powLimit = uint256S("00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); - consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks - consensus.nPowTargetSpacing = 10 * 60; - consensus.fPowAllowMinDifficultyBlocks = true; - consensus.fPowNoRetargeting = false; - consensus.nRuleChangeActivationThreshold = 1512; // 75% for testchains - consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing - consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28; - consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE; - consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; - consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].min_activation_height = 0; // No activation delay - - // Deployment of Taproot (BIPs 340-342) - consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2; - consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = 1619222400; // April 24th, 2021 - consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = 1628640000; // August 11th, 2021 - consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 0; // No activation delay - - consensus.nMinimumChainWork = uint256S("0x00000000000000000000000000000000000000000000076f6e7cbd0beade5d20"); - consensus.defaultAssumeValid = uint256S("0x0000000000000004877fa2d36316398528de4f347df2f8a96f76613a298ce060"); // 2344474 - - pchMessageStart[0] = 0x0b; - pchMessageStart[1] = 0x11; - pchMessageStart[2] = 0x09; - pchMessageStart[3] = 0x07; - nDefaultPort = 18333; - nPruneAfterHeight = 1000; - m_assumed_blockchain_size = 42; - m_assumed_chain_state_size = 2; - - genesis = CreateGenesisBlock(1296688602, 414098458, 0x1d00ffff, 1, 50 * COIN); - consensus.hashGenesisBlock = genesis.GetHash(); - assert(consensus.hashGenesisBlock == uint256S("0x000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943")); - assert(genesis.hashMerkleRoot == uint256S("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b")); - - vFixedSeeds.clear(); - vSeeds.clear(); - // nodes with support for servicebits filtering should be at the top - vSeeds.emplace_back("testnet-seed.bitcoin.jonasschnelli.ch."); - vSeeds.emplace_back("seed.tbtc.petertodd.org."); - vSeeds.emplace_back("seed.testnet.bitcoin.sprovoost.nl."); - vSeeds.emplace_back("testnet-seed.bluematt.me."); // Just a static list of stable node(s), only supports x9 - - base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,111); - base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,196); - base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1,239); - base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x35, 0x87, 0xCF}; - base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94}; - - bech32_hrp = "tb"; - - vFixedSeeds = std::vector<uint8_t>(std::begin(chainparams_seed_test), std::end(chainparams_seed_test)); - - fDefaultConsistencyChecks = false; - fRequireStandard = false; - m_is_test_chain = true; - m_is_mockable_chain = false; - - checkpointData = { - { - {546, uint256S("000000002a936ca763904c3c35fce2f3556c559c0214345d31b1bcebf76acb70")}, - } - }; - - m_assumeutxo_data = MapAssumeutxo{ - // TODO to be specified in a future patch. - }; - - chainTxData = ChainTxData{ - // Data from RPC: getchaintxstats 4096 0000000000000004877fa2d36316398528de4f347df2f8a96f76613a298ce060 - .nTime = 1661705221, - .nTxCount = 63531852, - .dTxRate = 0.1079119341520164, - }; + if (args.IsArgSet("-signetseednode")) { + options.seeds.emplace(args.GetArgs("-signetseednode")); } -}; - -/** - * Signet: test network with an additional consensus parameter (see BIP325). - */ -class SigNetParams : public CChainParams { -public: - explicit SigNetParams(const ArgsManager& args) { - std::vector<uint8_t> bin; - vSeeds.clear(); - - if (!args.IsArgSet("-signetchallenge")) { - bin = ParseHex("512103ad5e0edad18cb1f0fc0d28a3d4f1f3e445640337489abb10404f2d1e086be430210359ef5021964fe22d6f8e05b2463c9540ce96883fe3b278760f048f5189f2e6c452ae"); - vSeeds.emplace_back("seed.signet.bitcoin.sprovoost.nl."); - - // Hardcoded nodes can be removed once there are more DNS seeds - vSeeds.emplace_back("178.128.221.177"); - vSeeds.emplace_back("v7ajjeirttkbnt32wpy3c6w3emwnfr3fkla7hpxcfokr3ysd3kqtzmqd.onion:38333"); - - consensus.nMinimumChainWork = uint256S("0x000000000000000000000000000000000000000000000000000001291fc22898"); - consensus.defaultAssumeValid = uint256S("0x000000d1a0e224fa4679d2fb2187ba55431c284fa1b74cbc8cfda866fd4d2c09"); // 105495 - m_assumed_blockchain_size = 1; - m_assumed_chain_state_size = 0; - chainTxData = ChainTxData{ - // Data from RPC: getchaintxstats 4096 000000d1a0e224fa4679d2fb2187ba55431c284fa1b74cbc8cfda866fd4d2c09 - .nTime = 1661702566, - .nTxCount = 1903567, - .dTxRate = 0.02336701143027275, - }; - } else { - const auto signet_challenge = args.GetArgs("-signetchallenge"); - if (signet_challenge.size() != 1) { - throw std::runtime_error(strprintf("%s: -signetchallenge cannot be multiple values.", __func__)); - } - bin = ParseHex(signet_challenge[0]); - - consensus.nMinimumChainWork = uint256{}; - consensus.defaultAssumeValid = uint256{}; - m_assumed_blockchain_size = 0; - m_assumed_chain_state_size = 0; - chainTxData = ChainTxData{ - 0, - 0, - 0, - }; - LogPrintf("Signet with challenge %s\n", signet_challenge[0]); - } - - if (args.IsArgSet("-signetseednode")) { - vSeeds = args.GetArgs("-signetseednode"); + if (args.IsArgSet("-signetchallenge")) { + const auto signet_challenge = args.GetArgs("-signetchallenge"); + if (signet_challenge.size() != 1) { + throw std::runtime_error(strprintf("%s: -signetchallenge cannot be multiple values.", __func__)); } - - strNetworkID = CBaseChainParams::SIGNET; - consensus.signet_blocks = true; - consensus.signet_challenge.assign(bin.begin(), bin.end()); - consensus.nSubsidyHalvingInterval = 210000; - consensus.BIP34Height = 1; - consensus.BIP34Hash = uint256{}; - consensus.BIP65Height = 1; - consensus.BIP66Height = 1; - consensus.CSVHeight = 1; - consensus.SegwitHeight = 1; - consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks - consensus.nPowTargetSpacing = 10 * 60; - consensus.fPowAllowMinDifficultyBlocks = false; - consensus.fPowNoRetargeting = false; - consensus.nRuleChangeActivationThreshold = 1815; // 90% of 2016 - consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing - consensus.MinBIP9WarningHeight = 0; - consensus.powLimit = uint256S("00000377ae000000000000000000000000000000000000000000000000000000"); - consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28; - consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE; - consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; - consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].min_activation_height = 0; // No activation delay - - // Activation of Taproot (BIPs 340-342) - consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2; - consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE; - consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; - consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 0; // No activation delay - - // message start is defined as the first 4 bytes of the sha256d of the block script - HashWriter h{}; - h << consensus.signet_challenge; - uint256 hash = h.GetHash(); - memcpy(pchMessageStart, hash.begin(), 4); - - nDefaultPort = 38333; - nPruneAfterHeight = 1000; - - genesis = CreateGenesisBlock(1598918400, 52613770, 0x1e0377ae, 1, 50 * COIN); - consensus.hashGenesisBlock = genesis.GetHash(); - assert(consensus.hashGenesisBlock == uint256S("0x00000008819873e925422c1ff0f99f7cc9bbb232af63a077a480a3633bee1ef6")); - assert(genesis.hashMerkleRoot == uint256S("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b")); - - vFixedSeeds.clear(); - - base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,111); - base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,196); - base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1,239); - base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x35, 0x87, 0xCF}; - base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94}; - - bech32_hrp = "tb"; - - fDefaultConsistencyChecks = false; - fRequireStandard = true; - m_is_test_chain = true; - m_is_mockable_chain = false; + options.challenge.emplace(ParseHex(signet_challenge[0])); } -}; - -/** - * Regression test: intended for private networks only. Has minimal difficulty to ensure that - * blocks can be found instantly. - */ -class CRegTestParams : public CChainParams { -public: - explicit CRegTestParams(const ArgsManager& args) { - strNetworkID = CBaseChainParams::REGTEST; - consensus.signet_blocks = false; - consensus.signet_challenge.clear(); - consensus.nSubsidyHalvingInterval = 150; - consensus.BIP34Height = 1; // Always active unless overridden - consensus.BIP34Hash = uint256(); - consensus.BIP65Height = 1; // Always active unless overridden - consensus.BIP66Height = 1; // Always active unless overridden - consensus.CSVHeight = 1; // Always active unless overridden - consensus.SegwitHeight = 0; // Always active unless overridden - consensus.MinBIP9WarningHeight = 0; - consensus.powLimit = uint256S("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); - consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks - consensus.nPowTargetSpacing = 10 * 60; - consensus.fPowAllowMinDifficultyBlocks = true; - consensus.fPowNoRetargeting = true; - consensus.nRuleChangeActivationThreshold = 108; // 75% for testchains - consensus.nMinerConfirmationWindow = 144; // Faster than normal for regtest (144 instead of 2016) - - consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28; - consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 0; - consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; - consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].min_activation_height = 0; // No activation delay - - consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2; - consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE; - consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; - consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 0; // No activation delay - - consensus.nMinimumChainWork = uint256{}; - consensus.defaultAssumeValid = uint256{}; - - pchMessageStart[0] = 0xfa; - pchMessageStart[1] = 0xbf; - pchMessageStart[2] = 0xb5; - pchMessageStart[3] = 0xda; - nDefaultPort = 18444; - nPruneAfterHeight = args.GetBoolArg("-fastprune", false) ? 100 : 1000; - m_assumed_blockchain_size = 0; - m_assumed_chain_state_size = 0; - - UpdateActivationParametersFromArgs(args); - - genesis = CreateGenesisBlock(1296688602, 2, 0x207fffff, 1, 50 * COIN); - consensus.hashGenesisBlock = genesis.GetHash(); - assert(consensus.hashGenesisBlock == uint256S("0x0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206")); - assert(genesis.hashMerkleRoot == uint256S("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b")); - - vFixedSeeds.clear(); //!< Regtest mode doesn't have any fixed seeds. - vSeeds.clear(); - vSeeds.emplace_back("dummySeed.invalid."); - - fDefaultConsistencyChecks = true; - fRequireStandard = true; - m_is_test_chain = true; - m_is_mockable_chain = true; - - checkpointData = { - { - {0, uint256S("0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206")}, - } - }; - - m_assumeutxo_data = MapAssumeutxo{ - { - 110, - {AssumeutxoHash{uint256S("0x1ebbf5850204c0bdb15bf030f47c7fe91d45c44c712697e4509ba67adb01c618")}, 110}, - }, - { - 200, - {AssumeutxoHash{uint256S("0x51c8d11d8b5c1de51543c579736e786aa2736206d1e11e627568029ce092cf62")}, 200}, - }, - }; - - chainTxData = ChainTxData{ - 0, - 0, - 0 - }; - - base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,111); - base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,196); - base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1,239); - base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x35, 0x87, 0xCF}; - base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94}; - - bech32_hrp = "bcrt"; - } - - /** - * Allows modifying the Version Bits regtest parameters. - */ - void UpdateVersionBitsParameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout, int min_activation_height) - { - consensus.vDeployments[d].nStartTime = nStartTime; - consensus.vDeployments[d].nTimeout = nTimeout; - consensus.vDeployments[d].min_activation_height = min_activation_height; - } - void UpdateActivationParametersFromArgs(const ArgsManager& args); -}; +} -static void MaybeUpdateHeights(const ArgsManager& args, Consensus::Params& consensus) +void ReadRegTestArgs(const ArgsManager& args, CChainParams::RegTestOptions& options) { + if (auto value = args.GetBoolArg("-fastprune")) options.fastprune = *value; + for (const std::string& arg : args.GetArgs("-testactivationheight")) { const auto found{arg.find('@')}; if (found == std::string::npos) { throw std::runtime_error(strprintf("Invalid format (%s) for -testactivationheight=name@height.", arg)); } - const auto name{arg.substr(0, found)}; + const auto value{arg.substr(found + 1)}; int32_t height; if (!ParseInt32(value, &height) || height < 0 || height >= std::numeric_limits<int>::max()) { throw std::runtime_error(strprintf("Invalid height value (%s) for -testactivationheight=name@height.", arg)); } - if (name == "segwit") { - consensus.SegwitHeight = int{height}; - } else if (name == "bip34") { - consensus.BIP34Height = int{height}; - } else if (name == "dersig") { - consensus.BIP66Height = int{height}; - } else if (name == "cltv") { - consensus.BIP65Height = int{height}; - } else if (name == "csv") { - consensus.CSVHeight = int{height}; + + const auto deployment_name{arg.substr(0, found)}; + if (const auto buried_deployment = GetBuriedDeployment(deployment_name)) { + options.activation_heights[*buried_deployment] = height; } else { throw std::runtime_error(strprintf("Invalid name (%s) for -testactivationheight=name@height.", arg)); } } -} - -void CRegTestParams::UpdateActivationParametersFromArgs(const ArgsManager& args) -{ - MaybeUpdateHeights(args, consensus); if (!args.IsArgSet("-vbparams")) return; @@ -530,23 +60,26 @@ void CRegTestParams::UpdateActivationParametersFromArgs(const ArgsManager& args) if (vDeploymentParams.size() < 3 || 4 < vDeploymentParams.size()) { throw std::runtime_error("Version bits parameters malformed, expecting deployment:start:end[:min_activation_height]"); } - int64_t nStartTime, nTimeout; - int min_activation_height = 0; - if (!ParseInt64(vDeploymentParams[1], &nStartTime)) { + CChainParams::VersionBitsParameters vbparams{}; + if (!ParseInt64(vDeploymentParams[1], &vbparams.start_time)) { throw std::runtime_error(strprintf("Invalid nStartTime (%s)", vDeploymentParams[1])); } - if (!ParseInt64(vDeploymentParams[2], &nTimeout)) { + if (!ParseInt64(vDeploymentParams[2], &vbparams.timeout)) { throw std::runtime_error(strprintf("Invalid nTimeout (%s)", vDeploymentParams[2])); } - if (vDeploymentParams.size() >= 4 && !ParseInt32(vDeploymentParams[3], &min_activation_height)) { - throw std::runtime_error(strprintf("Invalid min_activation_height (%s)", vDeploymentParams[3])); + if (vDeploymentParams.size() >= 4) { + if (!ParseInt32(vDeploymentParams[3], &vbparams.min_activation_height)) { + throw std::runtime_error(strprintf("Invalid min_activation_height (%s)", vDeploymentParams[3])); + } + } else { + vbparams.min_activation_height = 0; } bool found = false; for (int j=0; j < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++j) { if (vDeploymentParams[0] == VersionBitsDeploymentInfo[j].name) { - UpdateVersionBitsParameters(Consensus::DeploymentPos(j), nStartTime, nTimeout, min_activation_height); + options.version_bits_parameters[Consensus::DeploymentPos(j)] = vbparams; found = true; - LogPrintf("Setting version bits activation parameters for %s to start=%ld, timeout=%ld, min_activation_height=%d\n", vDeploymentParams[0], nStartTime, nTimeout, min_activation_height); + LogPrintf("Setting version bits activation parameters for %s to start=%ld, timeout=%ld, min_activation_height=%d\n", vDeploymentParams[0], vbparams.start_time, vbparams.timeout, vbparams.min_activation_height); break; } } @@ -566,13 +99,17 @@ const CChainParams &Params() { std::unique_ptr<const CChainParams> CreateChainParams(const ArgsManager& args, const std::string& chain) { if (chain == CBaseChainParams::MAIN) { - return std::unique_ptr<CChainParams>(new CMainParams()); + return CChainParams::Main(); } else if (chain == CBaseChainParams::TESTNET) { - return std::unique_ptr<CChainParams>(new CTestNetParams()); + return CChainParams::TestNet(); } else if (chain == CBaseChainParams::SIGNET) { - return std::unique_ptr<CChainParams>(new SigNetParams(args)); + auto opts = CChainParams::SigNetOptions{}; + ReadSigNetArgs(args, opts); + return CChainParams::SigNet(opts); } else if (chain == CBaseChainParams::REGTEST) { - return std::unique_ptr<CChainParams>(new CRegTestParams(args)); + auto opts = CChainParams::RegTestOptions{}; + ReadRegTestArgs(args, opts); + return CChainParams::RegTest(opts); } throw std::runtime_error(strprintf("%s: Unknown chain %s.", __func__, chain)); } diff --git a/src/chainparams.h b/src/chainparams.h index 66592ffdda..cb34d068e1 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -6,6 +6,8 @@ #ifndef BITCOIN_CHAINPARAMS_H #define BITCOIN_CHAINPARAMS_H +#include <kernel/chainparams.h> + #include <chainparamsbase.h> #include <consensus/params.h> #include <netaddress.h> @@ -13,139 +15,12 @@ #include <protocol.h> #include <util/hash_type.h> +#include <cstdint> #include <memory> #include <string> +#include <unordered_map> #include <vector> -typedef std::map<int, uint256> MapCheckpoints; - -struct CCheckpointData { - MapCheckpoints mapCheckpoints; - - int GetHeight() const { - const auto& final_checkpoint = mapCheckpoints.rbegin(); - return final_checkpoint->first /* height */; - } -}; - -struct AssumeutxoHash : public BaseHash<uint256> { - explicit AssumeutxoHash(const uint256& hash) : BaseHash(hash) {} -}; - -/** - * Holds configuration for use during UTXO snapshot load and validation. The contents - * here are security critical, since they dictate which UTXO snapshots are recognized - * as valid. - */ -struct AssumeutxoData { - //! The expected hash of the deserialized UTXO set. - const AssumeutxoHash hash_serialized; - - //! Used to populate the nChainTx value, which is used during BlockManager::LoadBlockIndex(). - //! - //! We need to hardcode the value here because this is computed cumulatively using block data, - //! which we do not necessarily have at the time of snapshot load. - const unsigned int nChainTx; -}; - -using MapAssumeutxo = std::map<int, const AssumeutxoData>; - -/** - * Holds various statistics on transactions within a chain. Used to estimate - * verification progress during chain sync. - * - * See also: CChainParams::TxData, GuessVerificationProgress. - */ -struct ChainTxData { - int64_t nTime; //!< UNIX timestamp of last known number of transactions - int64_t nTxCount; //!< total number of transactions between genesis and that timestamp - double dTxRate; //!< estimated number of transactions per second after that timestamp -}; - -/** - * CChainParams defines various tweakable parameters of a given instance of the - * Bitcoin system. - */ -class CChainParams -{ -public: - enum Base58Type { - PUBKEY_ADDRESS, - SCRIPT_ADDRESS, - SECRET_KEY, - EXT_PUBLIC_KEY, - EXT_SECRET_KEY, - - MAX_BASE58_TYPES - }; - - const Consensus::Params& GetConsensus() const { return consensus; } - const CMessageHeader::MessageStartChars& MessageStart() const { return pchMessageStart; } - uint16_t GetDefaultPort() const { return nDefaultPort; } - uint16_t GetDefaultPort(Network net) const - { - return net == NET_I2P ? I2P_SAM31_PORT : GetDefaultPort(); - } - uint16_t GetDefaultPort(const std::string& addr) const - { - CNetAddr a; - return a.SetSpecial(addr) ? GetDefaultPort(a.GetNetwork()) : GetDefaultPort(); - } - - const CBlock& GenesisBlock() const { return genesis; } - /** Default value for -checkmempool and -checkblockindex argument */ - bool DefaultConsistencyChecks() const { return fDefaultConsistencyChecks; } - /** Policy: Filter transactions that do not match well-defined patterns */ - bool RequireStandard() const { return fRequireStandard; } - /** If this chain is exclusively used for testing */ - bool IsTestChain() const { return m_is_test_chain; } - /** If this chain allows time to be mocked */ - bool IsMockableChain() const { return m_is_mockable_chain; } - uint64_t PruneAfterHeight() const { return nPruneAfterHeight; } - /** Minimum free space (in GB) needed for data directory */ - uint64_t AssumedBlockchainSize() const { return m_assumed_blockchain_size; } - /** Minimum free space (in GB) needed for data directory when pruned; Does not include prune target*/ - uint64_t AssumedChainStateSize() const { return m_assumed_chain_state_size; } - /** Whether it is possible to mine blocks on demand (no retargeting) */ - bool MineBlocksOnDemand() const { return consensus.fPowNoRetargeting; } - /** Return the network string */ - std::string NetworkIDString() const { return strNetworkID; } - /** Return the list of hostnames to look up for DNS seeds */ - const std::vector<std::string>& DNSSeeds() const { return vSeeds; } - const std::vector<unsigned char>& Base58Prefix(Base58Type type) const { return base58Prefixes[type]; } - const std::string& Bech32HRP() const { return bech32_hrp; } - const std::vector<uint8_t>& FixedSeeds() const { return vFixedSeeds; } - const CCheckpointData& Checkpoints() const { return checkpointData; } - - //! Get allowed assumeutxo configuration. - //! @see ChainstateManager - const MapAssumeutxo& Assumeutxo() const { return m_assumeutxo_data; } - - const ChainTxData& TxData() const { return chainTxData; } -protected: - CChainParams() {} - - Consensus::Params consensus; - CMessageHeader::MessageStartChars pchMessageStart; - uint16_t nDefaultPort; - uint64_t nPruneAfterHeight; - uint64_t m_assumed_blockchain_size; - uint64_t m_assumed_chain_state_size; - std::vector<std::string> vSeeds; - std::vector<unsigned char> base58Prefixes[MAX_BASE58_TYPES]; - std::string bech32_hrp; - std::string strNetworkID; - CBlock genesis; - std::vector<uint8_t> vFixedSeeds; - bool fDefaultConsistencyChecks; - bool fRequireStandard; - bool m_is_test_chain; - bool m_is_mockable_chain; - CCheckpointData checkpointData; - MapAssumeutxo m_assumeutxo_data; - ChainTxData chainTxData; -}; - /** * Creates and returns a std::unique_ptr<CChainParams> of the chosen chain. * @returns a CChainParams* of the chosen chain. diff --git a/src/common/init.cpp b/src/common/init.cpp new file mode 100644 index 0000000000..159eb7e2ef --- /dev/null +++ b/src/common/init.cpp @@ -0,0 +1,74 @@ +// Copyright (c) 2023 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 <common/init.h> +#include <chainparams.h> +#include <fs.h> +#include <tinyformat.h> +#include <util/system.h> +#include <util/translation.h> + +#include <algorithm> +#include <exception> +#include <optional> + +namespace common { +std::optional<ConfigError> InitConfig(ArgsManager& args, SettingsAbortFn settings_abort_fn) +{ + try { + if (!CheckDataDirOption(args)) { + return ConfigError{ConfigStatus::FAILED, strprintf(_("Specified data directory \"%s\" does not exist."), args.GetArg("-datadir", ""))}; + } + std::string error; + if (!args.ReadConfigFiles(error, true)) { + return ConfigError{ConfigStatus::FAILED, strprintf(_("Error reading configuration file: %s"), error)}; + } + + // Check for chain settings (Params() calls are only valid after this clause) + SelectParams(args.GetChainName()); + + // Create datadir if it does not exist. + const auto base_path{args.GetDataDirBase()}; + if (!fs::exists(base_path)) { + // When creating a *new* datadir, also create a "wallets" subdirectory, + // whether or not the wallet is enabled now, so if the wallet is enabled + // in the future, it will use the "wallets" subdirectory for creating + // and listing wallets, rather than the top-level directory where + // wallets could be mixed up with other files. For backwards + // compatibility, wallet code will use the "wallets" subdirectory only + // if it already exists, but never create it itself. There is discussion + // in https://github.com/bitcoin/bitcoin/issues/16220 about ways to + // change wallet code so it would no longer be necessary to create + // "wallets" subdirectories here. + fs::create_directories(base_path / "wallets"); + } + const auto net_path{args.GetDataDirNet()}; + if (!fs::exists(net_path)) { + fs::create_directories(net_path / "wallets"); + } + + // Create settings.json if -nosettings was not specified. + if (args.GetSettingsPath()) { + std::vector<std::string> details; + if (!args.ReadSettingsFile(&details)) { + const bilingual_str& message = _("Settings file could not be read"); + if (!settings_abort_fn) { + return ConfigError{ConfigStatus::FAILED, message, details}; + } else if (settings_abort_fn(message, details)) { + return ConfigError{ConfigStatus::ABORTED, message, details}; + } else { + details.clear(); // User chose to ignore the error and proceed. + } + } + if (!args.WriteSettingsFile(&details)) { + const bilingual_str& message = _("Settings file could not be written"); + return ConfigError{ConfigStatus::FAILED_WRITE, message, details}; + } + } + } catch (const std::exception& e) { + return ConfigError{ConfigStatus::FAILED, Untranslated(e.what())}; + } + return {}; +} +} // namespace common diff --git a/src/common/init.h b/src/common/init.h new file mode 100644 index 0000000000..380ac3ac7e --- /dev/null +++ b/src/common/init.h @@ -0,0 +1,39 @@ +// Copyright (c) 2023 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_COMMON_INIT_H +#define BITCOIN_COMMON_INIT_H + +#include <util/translation.h> + +#include <functional> +#include <optional> +#include <string> +#include <vector> + +class ArgsManager; + +namespace common { +enum class ConfigStatus { + FAILED, //!< Failed generically. + FAILED_WRITE, //!< Failed to write settings.json + ABORTED, //!< Aborted by user +}; + +struct ConfigError { + ConfigStatus status; + bilingual_str message{}; + std::vector<std::string> details{}; +}; + +//! Callback function to let the user decide whether to abort loading if +//! settings.json file exists and can't be parsed, or to ignore the error and +//! overwrite the file. +using SettingsAbortFn = std::function<bool(const bilingual_str& message, const std::vector<std::string>& details)>; + +/* Read config files, and create datadir and settings.json if they don't exist. */ +std::optional<ConfigError> InitConfig(ArgsManager& args, SettingsAbortFn settings_abort_fn = nullptr); +} // namespace common + +#endif // BITCOIN_COMMON_INIT_H diff --git a/src/deploymentinfo.cpp b/src/deploymentinfo.cpp index 246932e56d..185a7dcb54 100644 --- a/src/deploymentinfo.cpp +++ b/src/deploymentinfo.cpp @@ -6,6 +6,8 @@ #include <consensus/params.h> +#include <string_view> + const struct VBDeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION_BITS_DEPLOYMENTS] = { { /*.name =*/ "testdummy", @@ -34,3 +36,19 @@ std::string DeploymentName(Consensus::BuriedDeployment dep) } // no default case, so the compiler can warn about missing cases return ""; } + +std::optional<Consensus::BuriedDeployment> GetBuriedDeployment(const std::string_view name) +{ + if (name == "segwit") { + return Consensus::BuriedDeployment::DEPLOYMENT_SEGWIT; + } else if (name == "bip34") { + return Consensus::BuriedDeployment::DEPLOYMENT_HEIGHTINCB; + } else if (name == "dersig") { + return Consensus::BuriedDeployment::DEPLOYMENT_DERSIG; + } else if (name == "cltv") { + return Consensus::BuriedDeployment::DEPLOYMENT_CLTV; + } else if (name == "csv") { + return Consensus::BuriedDeployment::DEPLOYMENT_CSV; + } + return std::nullopt; +} diff --git a/src/deploymentinfo.h b/src/deploymentinfo.h index 8b909dedb4..72ba297ea0 100644 --- a/src/deploymentinfo.h +++ b/src/deploymentinfo.h @@ -7,6 +7,7 @@ #include <consensus/params.h> +#include <optional> #include <string> struct VBDeploymentInfo { @@ -26,4 +27,6 @@ inline std::string DeploymentName(Consensus::DeploymentPos pos) return VersionBitsDeploymentInfo[pos].name; } +std::optional<Consensus::BuriedDeployment> GetBuriedDeployment(const std::string_view deployment_name); + #endif // BITCOIN_DEPLOYMENTINFO_H diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 4e4f27f1be..942caa042d 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -21,12 +21,14 @@ #include <util/threadnames.h> #include <util/translation.h> +#include <condition_variable> #include <cstdio> #include <cstdlib> #include <deque> #include <memory> #include <optional> #include <string> +#include <unordered_set> #include <sys/types.h> #include <sys/stat.h> @@ -34,6 +36,7 @@ #include <event2/buffer.h> #include <event2/bufferevent.h> #include <event2/http.h> +#include <event2/http_struct.h> #include <event2/keyvalq_struct.h> #include <event2/thread.h> #include <event2/util.h> @@ -146,6 +149,10 @@ static GlobalMutex g_httppathhandlers_mutex; static std::vector<HTTPPathHandler> pathHandlers GUARDED_BY(g_httppathhandlers_mutex); //! Bound listening sockets static std::vector<evhttp_bound_socket *> boundSockets; +//! Track active requests +static GlobalMutex g_requests_mutex; +static std::condition_variable g_requests_cv; +static std::unordered_set<evhttp_request*> g_requests GUARDED_BY(g_requests_mutex); /** Check if a network address is allowed to access the HTTP server */ static bool ClientAllowed(const CNetAddr& netaddr) @@ -207,6 +214,17 @@ std::string RequestMethodString(HTTPRequest::RequestMethod m) /** HTTP request callback */ static void http_request_cb(struct evhttp_request* req, void* arg) { + // Track requests and notify when a request is completed. + { + WITH_LOCK(g_requests_mutex, g_requests.insert(req)); + g_requests_cv.notify_all(); + evhttp_request_set_on_complete_cb(req, [](struct evhttp_request* req, void*) { + auto n{WITH_LOCK(g_requests_mutex, return g_requests.erase(req))}; + assert(n == 1); + g_requests_cv.notify_all(); + }, nullptr); + } + // Disable reading to work around a libevent bug, fixed in 2.2.0. if (event_get_version_number() >= 0x02010600 && event_get_version_number() < 0x02020001) { evhttp_connection* conn = evhttp_request_get_connection(req); @@ -458,15 +476,27 @@ void StopHTTPServer() evhttp_del_accept_socket(eventHTTP, socket); } boundSockets.clear(); - if (eventBase) { - LogPrint(BCLog::HTTP, "Waiting for HTTP event thread to exit\n"); - if (g_thread_http.joinable()) g_thread_http.join(); + { + WAIT_LOCK(g_requests_mutex, lock); + if (!g_requests.empty()) { + LogPrint(BCLog::HTTP, "Waiting for %d requests to stop HTTP server\n", g_requests.size()); + } + g_requests_cv.wait(lock, []() EXCLUSIVE_LOCKS_REQUIRED(g_requests_mutex) { + return g_requests.empty(); + }); } if (eventHTTP) { - evhttp_free(eventHTTP); - eventHTTP = nullptr; + // Schedule a callback to call evhttp_free in the event base thread, so + // that evhttp_free does not need to be called again after the handling + // of unfinished request connections that follows. + event_base_once(eventBase, -1, EV_TIMEOUT, [](evutil_socket_t, short, void*) { + evhttp_free(eventHTTP); + eventHTTP = nullptr; + }, nullptr, nullptr); } if (eventBase) { + LogPrint(BCLog::HTTP, "Waiting for HTTP event thread to exit\n"); + if (g_thread_http.joinable()) g_thread_http.join(); event_base_free(eventBase); eventBase = nullptr; } diff --git a/src/index/base.cpp b/src/index/base.cpp index 6f2ce2efe4..8a311296c2 100644 --- a/src/index/base.cpp +++ b/src/index/base.cpp @@ -6,6 +6,7 @@ #include <index/base.h> #include <interfaces/chain.h> #include <kernel/chain.h> +#include <logging.h> #include <node/blockstorage.h> #include <node/context.h> #include <node/database_args.h> @@ -35,7 +36,7 @@ static void FatalError(const char* fmt, const Args&... args) std::string strMessage = tfm::format(fmt, args...); SetMiscWarning(Untranslated(strMessage)); LogPrintf("*** %s\n", strMessage); - AbortError(_("A fatal internal error occurred, see debug.log for details")); + InitError(_("A fatal internal error occurred, see debug.log for details")); StartShutdown(); } diff --git a/src/index/coinstatsindex.cpp b/src/index/coinstatsindex.cpp index 8cece7d78d..4d637e217a 100644 --- a/src/index/coinstatsindex.cpp +++ b/src/index/coinstatsindex.cpp @@ -7,6 +7,7 @@ #include <crypto/muhash.h> #include <index/coinstatsindex.h> #include <kernel/coinstats.h> +#include <logging.h> #include <node/blockstorage.h> #include <serialize.h> #include <txdb.h> diff --git a/src/index/txindex.cpp b/src/index/txindex.cpp index 25ccc3e636..9095e7afeb 100644 --- a/src/index/txindex.cpp +++ b/src/index/txindex.cpp @@ -5,6 +5,7 @@ #include <index/txindex.h> #include <index/disktxpos.h> +#include <logging.h> #include <node/blockstorage.h> #include <util/system.h> #include <validation.h> diff --git a/src/init.cpp b/src/init.cpp index 281e0d4966..8a45c38ce3 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -37,6 +37,7 @@ #include <net_processing.h> #include <netbase.h> #include <netgroup.h> +#include <node/blockmanager_args.h> #include <node/blockstorage.h> #include <node/caches.h> #include <node/chainstate.h> @@ -122,9 +123,7 @@ using node::ShouldPersistMempool; using node::NodeContext; using node::ThreadImport; using node::VerifyLoadedChainstate; -using node::fPruneMode; using node::fReindex; -using node::nPruneTarget; static constexpr bool DEFAULT_PROXYRANDOMIZE{true}; static constexpr bool DEFAULT_REST_ENABLE{false}; @@ -477,7 +476,7 @@ void SetupServerArgs(ArgsManager& argsman) argsman.AddArg("-externalip=<ip>", "Specify your own public address", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsman.AddArg("-fixedseeds", strprintf("Allow fixed seeds if DNS seeds don't provide peers (default: %u)", DEFAULT_FIXEDSEEDS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsman.AddArg("-forcednsseed", strprintf("Always query for peer addresses via DNS lookup (default: %u)", DEFAULT_FORCEDNSSEED), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); - argsman.AddArg("-listen", "Accept connections from outside (default: 1 if no -proxy or -connect)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); + argsman.AddArg("-listen", strprintf("Accept connections from outside (default: %u if no -proxy or -connect)", DEFAULT_LISTEN), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsman.AddArg("-listenonion", strprintf("Automatically create Tor onion service (default: %d)", DEFAULT_LISTEN_ONION), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsman.AddArg("-maxconnections=<n>", strprintf("Maintain at most <n> connections to peers (default: %u). This limit does not apply to connections manually added via -addnode or the addnode RPC, which have a separate limit of %u.", DEFAULT_MAX_PEER_CONNECTIONS, MAX_ADDNODE_CONNECTIONS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsman.AddArg("-maxreceivebuffer=<n>", strprintf("Maximum per-connection receive buffer, <n>*1000 bytes (default: %u)", DEFAULT_MAXRECEIVEBUFFER), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); @@ -867,7 +866,7 @@ bool AppInitParameterInteraction(const ArgsManager& args, bool use_syscall_sandb InitWarning(warnings); } - if (!fs::is_directory(gArgs.GetBlocksDirPath())) { + if (!fs::is_directory(args.GetBlocksDirPath())) { return InitError(strprintf(_("Specified blocks directory \"%s\" does not exist."), args.GetArg("-blocksdir", ""))); } @@ -945,22 +944,6 @@ bool AppInitParameterInteraction(const ArgsManager& args, bool use_syscall_sandb init::SetLoggingCategories(args); init::SetLoggingLevel(args); - // block pruning; get the amount of disk space (in MiB) to allot for block & undo files - int64_t nPruneArg = args.GetIntArg("-prune", 0); - if (nPruneArg < 0) { - return InitError(_("Prune cannot be configured with a negative value.")); - } - nPruneTarget = (uint64_t) nPruneArg * 1024 * 1024; - if (nPruneArg == 1) { // manual pruning: -prune=1 - nPruneTarget = std::numeric_limits<uint64_t>::max(); - fPruneMode = true; - } else if (nPruneTarget) { - if (nPruneTarget < MIN_DISK_SPACE_FOR_BLOCK_FILES) { - return InitError(strprintf(_("Prune configured below the minimum of %d MiB. Please use a higher number."), MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024)); - } - fPruneMode = true; - } - nConnectTimeout = args.GetIntArg("-timeout", DEFAULT_CONNECT_TIMEOUT); if (nConnectTimeout <= 0) { nConnectTimeout = DEFAULT_CONNECT_TIMEOUT; @@ -1051,6 +1034,10 @@ bool AppInitParameterInteraction(const ArgsManager& args, bool use_syscall_sandb if (const auto error{ApplyArgsManOptions(args, chainman_opts_dummy)}) { return InitError(*error); } + node::BlockManager::Options blockman_opts_dummy{}; + if (const auto error{ApplyArgsManOptions(args, blockman_opts_dummy)}) { + return InitError(*error); + } } return true; @@ -1224,7 +1211,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) if (args.IsArgSet("-asmap")) { fs::path asmap_path = args.GetPathArg("-asmap", DEFAULT_ASMAP_FILENAME); if (!asmap_path.is_absolute()) { - asmap_path = gArgs.GetDataDirNet() / asmap_path; + asmap_path = args.GetDataDirNet() / asmap_path; } if (!fs::exists(asmap_path)) { InitError(strprintf(_("Could not find asmap file %s"), fs::quoted(fs::PathToString(asmap_path)))); @@ -1254,7 +1241,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) } assert(!node.banman); - node.banman = std::make_unique<BanMan>(gArgs.GetDataDirNet() / "banlist", &uiInterface, args.GetIntArg("-bantime", DEFAULT_MISBEHAVING_BANTIME)); + node.banman = std::make_unique<BanMan>(args.GetDataDirNet() / "banlist", &uiInterface, args.GetIntArg("-bantime", DEFAULT_MISBEHAVING_BANTIME)); assert(!node.connman); node.connman = std::make_unique<CConnman>(GetRand<uint64_t>(), GetRand<uint64_t>(), @@ -1450,6 +1437,9 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) }; Assert(!ApplyArgsManOptions(args, chainman_opts)); // no error can happen, already checked in AppInitParameterInteraction + node::BlockManager::Options blockman_opts{}; + Assert(!ApplyArgsManOptions(args, blockman_opts)); // no error can happen, already checked in AppInitParameterInteraction + // cache size calculations CacheSizes cache_sizes = CalculateCacheSizes(args, g_enabled_filter_types.size()); @@ -1485,7 +1475,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) for (bool fLoaded = false; !fLoaded && !ShutdownRequested();) { node.mempool = std::make_unique<CTxMemPool>(mempool_opts); - node.chainman = std::make_unique<ChainstateManager>(chainman_opts); + node.chainman = std::make_unique<ChainstateManager>(chainman_opts, blockman_opts); ChainstateManager& chainman = *node.chainman; node::ChainstateLoadOptions options; @@ -1618,12 +1608,12 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) // ********************************************************* Step 11: import blocks - if (!CheckDiskSpace(gArgs.GetDataDirNet())) { - InitError(strprintf(_("Error: Disk space is low for %s"), fs::quoted(fs::PathToString(gArgs.GetDataDirNet())))); + if (!CheckDiskSpace(args.GetDataDirNet())) { + InitError(strprintf(_("Error: Disk space is low for %s"), fs::quoted(fs::PathToString(args.GetDataDirNet())))); return false; } - if (!CheckDiskSpace(gArgs.GetBlocksDirPath())) { - InitError(strprintf(_("Error: Disk space is low for %s"), fs::quoted(fs::PathToString(gArgs.GetBlocksDirPath())))); + if (!CheckDiskSpace(args.GetBlocksDirPath())) { + InitError(strprintf(_("Error: Disk space is low for %s"), fs::quoted(fs::PathToString(args.GetBlocksDirPath())))); return false; } @@ -1631,10 +1621,11 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) // On first startup, warn on low block storage space if (!fReindex && !fReindexChainState && chain_active_height <= 1) { + uint64_t assumed_chain_bytes{chainparams.AssumedBlockchainSize() * 1024 * 1024 * 1024}; uint64_t additional_bytes_needed{ chainman.m_blockman.IsPruneMode() ? - chainman.m_blockman.GetPruneTarget() : - chainparams.AssumedBlockchainSize() * 1024 * 1024 * 1024}; + std::min(chainman.m_blockman.GetPruneTarget(), assumed_chain_bytes) : + assumed_chain_bytes}; if (!CheckDiskSpace(args.GetBlocksDirPath(), additional_bytes_needed)) { InitWarning(strprintf(_( @@ -1715,7 +1706,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) if (node.peerman) node.peerman->SetBestHeight(chain_active_height); // Map ports with UPnP or NAT-PMP. - StartMapPort(args.GetBoolArg("-upnp", DEFAULT_UPNP), gArgs.GetBoolArg("-natpmp", DEFAULT_NATPMP)); + StartMapPort(args.GetBoolArg("-upnp", DEFAULT_UPNP), args.GetBoolArg("-natpmp", DEFAULT_NATPMP)); CConnman::Options connOptions; connOptions.nLocalServices = nLocalServices; diff --git a/src/kernel/blockmanager_opts.h b/src/kernel/blockmanager_opts.h new file mode 100644 index 0000000000..9dc93b6dd2 --- /dev/null +++ b/src/kernel/blockmanager_opts.h @@ -0,0 +1,20 @@ +// Copyright (c) 2022 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_KERNEL_BLOCKMANAGER_OPTS_H +#define BITCOIN_KERNEL_BLOCKMANAGER_OPTS_H + +namespace kernel { + +/** + * An options struct for `BlockManager`, more ergonomically referred to as + * `BlockManager::Options` due to the using-declaration in `BlockManager`. + */ +struct BlockManagerOpts { + uint64_t prune_target{0}; +}; + +} // namespace kernel + +#endif // BITCOIN_KERNEL_BLOCKMANAGER_OPTS_H diff --git a/src/kernel/chainparams.cpp b/src/kernel/chainparams.cpp new file mode 100644 index 0000000000..e0c4aff6f4 --- /dev/null +++ b/src/kernel/chainparams.cpp @@ -0,0 +1,532 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-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 <kernel/chainparams.h> + +#include <chainparamsseeds.h> +#include <consensus/amount.h> +#include <consensus/merkle.h> +#include <consensus/params.h> +#include <hash.h> +#include <chainparamsbase.h> +#include <logging.h> +#include <primitives/block.h> +#include <primitives/transaction.h> +#include <script/interpreter.h> +#include <script/script.h> +#include <uint256.h> +#include <util/strencodings.h> + +#include <algorithm> +#include <cassert> +#include <cstdint> +#include <cstring> +#include <type_traits> + +static CBlock CreateGenesisBlock(const char* pszTimestamp, const CScript& genesisOutputScript, uint32_t nTime, uint32_t nNonce, uint32_t nBits, int32_t nVersion, const CAmount& genesisReward) +{ + CMutableTransaction txNew; + txNew.nVersion = 1; + txNew.vin.resize(1); + txNew.vout.resize(1); + txNew.vin[0].scriptSig = CScript() << 486604799 << CScriptNum(4) << std::vector<unsigned char>((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp)); + txNew.vout[0].nValue = genesisReward; + txNew.vout[0].scriptPubKey = genesisOutputScript; + + CBlock genesis; + genesis.nTime = nTime; + genesis.nBits = nBits; + genesis.nNonce = nNonce; + genesis.nVersion = nVersion; + genesis.vtx.push_back(MakeTransactionRef(std::move(txNew))); + genesis.hashPrevBlock.SetNull(); + genesis.hashMerkleRoot = BlockMerkleRoot(genesis); + return genesis; +} + +/** + * Build the genesis block. Note that the output of its generation + * transaction cannot be spent since it did not originally exist in the + * database. + * + * CBlock(hash=000000000019d6, ver=1, hashPrevBlock=00000000000000, hashMerkleRoot=4a5e1e, nTime=1231006505, nBits=1d00ffff, nNonce=2083236893, vtx=1) + * CTransaction(hash=4a5e1e, ver=1, vin.size=1, vout.size=1, nLockTime=0) + * CTxIn(COutPoint(000000, -1), coinbase 04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73) + * CTxOut(nValue=50.00000000, scriptPubKey=0x5F1DF16B2B704C8A578D0B) + * vMerkleTree: 4a5e1e + */ +static CBlock CreateGenesisBlock(uint32_t nTime, uint32_t nNonce, uint32_t nBits, int32_t nVersion, const CAmount& genesisReward) +{ + const char* pszTimestamp = "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks"; + const CScript genesisOutputScript = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG; + return CreateGenesisBlock(pszTimestamp, genesisOutputScript, nTime, nNonce, nBits, nVersion, genesisReward); +} + +/** + * Main network on which people trade goods and services. + */ +class CMainParams : public CChainParams { +public: + CMainParams() { + strNetworkID = CBaseChainParams::MAIN; + consensus.signet_blocks = false; + consensus.signet_challenge.clear(); + consensus.nSubsidyHalvingInterval = 210000; + consensus.script_flag_exceptions.emplace( // BIP16 exception + uint256S("0x00000000000002dc756eebf4f49723ed8d30cc28a5f108eb94b1ba88ac4f9c22"), SCRIPT_VERIFY_NONE); + consensus.script_flag_exceptions.emplace( // Taproot exception + uint256S("0x0000000000000000000f14c35b2d841e986ab5441de8c585d5ffe55ea1e395ad"), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS); + consensus.BIP34Height = 227931; + consensus.BIP34Hash = uint256S("0x000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8"); + consensus.BIP65Height = 388381; // 000000000000000004c2b624ed5d7756c508d90fd0da2c7c679febfa6c4735f0 + consensus.BIP66Height = 363725; // 00000000000000000379eaa19dce8c9b722d46ae6a57c2f1a988119488b50931 + consensus.CSVHeight = 419328; // 000000000000000004a1b34462cb8aeebd5799177f7a29cf28f2d1961716b5b5 + consensus.SegwitHeight = 481824; // 0000000000000000001c8018d9cb3b742ef25114f27563e3fc4a1902167f9893 + consensus.MinBIP9WarningHeight = 483840; // segwit activation height + miner confirmation window + consensus.powLimit = uint256S("00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks + consensus.nPowTargetSpacing = 10 * 60; + consensus.fPowAllowMinDifficultyBlocks = false; + consensus.fPowNoRetargeting = false; + consensus.nRuleChangeActivationThreshold = 1815; // 90% of 2016 + consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28; + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE; + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].min_activation_height = 0; // No activation delay + + // Deployment of Taproot (BIPs 340-342) + consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2; + consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = 1619222400; // April 24th, 2021 + consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = 1628640000; // August 11th, 2021 + consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 709632; // Approximately November 12th, 2021 + + consensus.nMinimumChainWork = uint256S("0x00000000000000000000000000000000000000003404ba0801921119f903495e"); + consensus.defaultAssumeValid = uint256S("0x00000000000000000009c97098b5295f7e5f183ac811fb5d1534040adb93cabd"); // 751565 + + /** + * The message start string is designed to be unlikely to occur in normal data. + * The characters are rarely used upper ASCII, not valid as UTF-8, and produce + * a large 32-bit integer with any alignment. + */ + pchMessageStart[0] = 0xf9; + pchMessageStart[1] = 0xbe; + pchMessageStart[2] = 0xb4; + pchMessageStart[3] = 0xd9; + nDefaultPort = 8333; + nPruneAfterHeight = 100000; + m_assumed_blockchain_size = 496; + m_assumed_chain_state_size = 6; + + genesis = CreateGenesisBlock(1231006505, 2083236893, 0x1d00ffff, 1, 50 * COIN); + consensus.hashGenesisBlock = genesis.GetHash(); + assert(consensus.hashGenesisBlock == uint256S("0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f")); + assert(genesis.hashMerkleRoot == uint256S("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b")); + + // Note that of those which support the service bits prefix, most only support a subset of + // possible options. + // This is fine at runtime as we'll fall back to using them as an addrfetch if they don't support the + // service bits we want, but we should get them updated to support all service bits wanted by any + // release ASAP to avoid it where possible. + vSeeds.emplace_back("seed.bitcoin.sipa.be."); // Pieter Wuille, only supports x1, x5, x9, and xd + vSeeds.emplace_back("dnsseed.bluematt.me."); // Matt Corallo, only supports x9 + vSeeds.emplace_back("dnsseed.bitcoin.dashjr.org."); // Luke Dashjr + vSeeds.emplace_back("seed.bitcoinstats.com."); // Christian Decker, supports x1 - xf + vSeeds.emplace_back("seed.bitcoin.jonasschnelli.ch."); // Jonas Schnelli, only supports x1, x5, x9, and xd + vSeeds.emplace_back("seed.btc.petertodd.org."); // Peter Todd, only supports x1, x5, x9, and xd + vSeeds.emplace_back("seed.bitcoin.sprovoost.nl."); // Sjors Provoost + vSeeds.emplace_back("dnsseed.emzy.de."); // Stephan Oeste + vSeeds.emplace_back("seed.bitcoin.wiz.biz."); // Jason Maurice + + base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,0); + base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,5); + base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1,128); + base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x88, 0xB2, 0x1E}; + base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x88, 0xAD, 0xE4}; + + bech32_hrp = "bc"; + + vFixedSeeds = std::vector<uint8_t>(std::begin(chainparams_seed_main), std::end(chainparams_seed_main)); + + fDefaultConsistencyChecks = false; + fRequireStandard = true; + m_is_test_chain = false; + m_is_mockable_chain = false; + + checkpointData = { + { + { 11111, uint256S("0x0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d")}, + { 33333, uint256S("0x000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6")}, + { 74000, uint256S("0x0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20")}, + {105000, uint256S("0x00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97")}, + {134444, uint256S("0x00000000000005b12ffd4cd315cd34ffd4a594f430ac814c91184a0d42d2b0fe")}, + {168000, uint256S("0x000000000000099e61ea72015e79632f216fe6cb33d7899acb35b75c8303b763")}, + {193000, uint256S("0x000000000000059f452a5f7340de6682a977387c17010ff6e6c3bd83ca8b1317")}, + {210000, uint256S("0x000000000000048b95347e83192f69cf0366076336c639f9b7228e9ba171342e")}, + {216116, uint256S("0x00000000000001b4f4b433e81ee46494af945cf96014816a4e2370f11b23df4e")}, + {225430, uint256S("0x00000000000001c108384350f74090433e7fcf79a606b8e797f065b130575932")}, + {250000, uint256S("0x000000000000003887df1f29024b06fc2200b55f8af8f35453d7be294df2d214")}, + {279000, uint256S("0x0000000000000001ae8c72a0b0c301f67e3afca10e819efa9041e458e9bd7e40")}, + {295000, uint256S("0x00000000000000004d9b4ef50f0f9d686fd69db2e03af35a100370c64632a983")}, + } + }; + + m_assumeutxo_data = MapAssumeutxo{ + // TODO to be specified in a future patch. + }; + + chainTxData = ChainTxData{ + // Data from RPC: getchaintxstats 4096 00000000000000000009c97098b5295f7e5f183ac811fb5d1534040adb93cabd + .nTime = 1661697692, + .nTxCount = 760120522, + .dTxRate = 2.925802860942233, + }; + } +}; + +/** + * Testnet (v3): public test network which is reset from time to time. + */ +class CTestNetParams : public CChainParams { +public: + CTestNetParams() { + strNetworkID = CBaseChainParams::TESTNET; + consensus.signet_blocks = false; + consensus.signet_challenge.clear(); + consensus.nSubsidyHalvingInterval = 210000; + consensus.script_flag_exceptions.emplace( // BIP16 exception + uint256S("0x00000000dd30457c001f4095d208cc1296b0eed002427aa599874af7a432b105"), SCRIPT_VERIFY_NONE); + consensus.BIP34Height = 21111; + consensus.BIP34Hash = uint256S("0x0000000023b3a96d3484e5abb3755c413e7d41500f8e2a5c3f0dd01299cd8ef8"); + consensus.BIP65Height = 581885; // 00000000007f6655f22f98e72ed80d8b06dc761d5da09df0fa1dc4be4f861eb6 + consensus.BIP66Height = 330776; // 000000002104c8c45e99a8853285a3b592602a3ccde2b832481da85e9e4ba182 + consensus.CSVHeight = 770112; // 00000000025e930139bac5c6c31a403776da130831ab85be56578f3fa75369bb + consensus.SegwitHeight = 834624; // 00000000002b980fcd729daaa248fd9316a5200e9b367f4ff2c42453e84201ca + consensus.MinBIP9WarningHeight = 836640; // segwit activation height + miner confirmation window + consensus.powLimit = uint256S("00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks + consensus.nPowTargetSpacing = 10 * 60; + consensus.fPowAllowMinDifficultyBlocks = true; + consensus.fPowNoRetargeting = false; + consensus.nRuleChangeActivationThreshold = 1512; // 75% for testchains + consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28; + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE; + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].min_activation_height = 0; // No activation delay + + // Deployment of Taproot (BIPs 340-342) + consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2; + consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = 1619222400; // April 24th, 2021 + consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = 1628640000; // August 11th, 2021 + consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 0; // No activation delay + + consensus.nMinimumChainWork = uint256S("0x00000000000000000000000000000000000000000000076f6e7cbd0beade5d20"); + consensus.defaultAssumeValid = uint256S("0x0000000000000004877fa2d36316398528de4f347df2f8a96f76613a298ce060"); // 2344474 + + pchMessageStart[0] = 0x0b; + pchMessageStart[1] = 0x11; + pchMessageStart[2] = 0x09; + pchMessageStart[3] = 0x07; + nDefaultPort = 18333; + nPruneAfterHeight = 1000; + m_assumed_blockchain_size = 42; + m_assumed_chain_state_size = 2; + + genesis = CreateGenesisBlock(1296688602, 414098458, 0x1d00ffff, 1, 50 * COIN); + consensus.hashGenesisBlock = genesis.GetHash(); + assert(consensus.hashGenesisBlock == uint256S("0x000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943")); + assert(genesis.hashMerkleRoot == uint256S("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b")); + + vFixedSeeds.clear(); + vSeeds.clear(); + // nodes with support for servicebits filtering should be at the top + vSeeds.emplace_back("testnet-seed.bitcoin.jonasschnelli.ch."); + vSeeds.emplace_back("seed.tbtc.petertodd.org."); + vSeeds.emplace_back("seed.testnet.bitcoin.sprovoost.nl."); + vSeeds.emplace_back("testnet-seed.bluematt.me."); // Just a static list of stable node(s), only supports x9 + + base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,111); + base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,196); + base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1,239); + base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x35, 0x87, 0xCF}; + base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94}; + + bech32_hrp = "tb"; + + vFixedSeeds = std::vector<uint8_t>(std::begin(chainparams_seed_test), std::end(chainparams_seed_test)); + + fDefaultConsistencyChecks = false; + fRequireStandard = false; + m_is_test_chain = true; + m_is_mockable_chain = false; + + checkpointData = { + { + {546, uint256S("000000002a936ca763904c3c35fce2f3556c559c0214345d31b1bcebf76acb70")}, + } + }; + + m_assumeutxo_data = MapAssumeutxo{ + // TODO to be specified in a future patch. + }; + + chainTxData = ChainTxData{ + // Data from RPC: getchaintxstats 4096 0000000000000004877fa2d36316398528de4f347df2f8a96f76613a298ce060 + .nTime = 1661705221, + .nTxCount = 63531852, + .dTxRate = 0.1079119341520164, + }; + } +}; + +/** + * Signet: test network with an additional consensus parameter (see BIP325). + */ +class SigNetParams : public CChainParams { +public: + explicit SigNetParams(const SigNetOptions& options) + { + std::vector<uint8_t> bin; + vSeeds.clear(); + + if (!options.challenge) { + bin = ParseHex("512103ad5e0edad18cb1f0fc0d28a3d4f1f3e445640337489abb10404f2d1e086be430210359ef5021964fe22d6f8e05b2463c9540ce96883fe3b278760f048f5189f2e6c452ae"); + vSeeds.emplace_back("seed.signet.bitcoin.sprovoost.nl."); + + // Hardcoded nodes can be removed once there are more DNS seeds + vSeeds.emplace_back("178.128.221.177"); + vSeeds.emplace_back("v7ajjeirttkbnt32wpy3c6w3emwnfr3fkla7hpxcfokr3ysd3kqtzmqd.onion:38333"); + + consensus.nMinimumChainWork = uint256S("0x000000000000000000000000000000000000000000000000000001291fc22898"); + consensus.defaultAssumeValid = uint256S("0x000000d1a0e224fa4679d2fb2187ba55431c284fa1b74cbc8cfda866fd4d2c09"); // 105495 + m_assumed_blockchain_size = 1; + m_assumed_chain_state_size = 0; + chainTxData = ChainTxData{ + // Data from RPC: getchaintxstats 4096 000000d1a0e224fa4679d2fb2187ba55431c284fa1b74cbc8cfda866fd4d2c09 + .nTime = 1661702566, + .nTxCount = 1903567, + .dTxRate = 0.02336701143027275, + }; + } else { + bin = *options.challenge; + consensus.nMinimumChainWork = uint256{}; + consensus.defaultAssumeValid = uint256{}; + m_assumed_blockchain_size = 0; + m_assumed_chain_state_size = 0; + chainTxData = ChainTxData{ + 0, + 0, + 0, + }; + LogPrintf("Signet with challenge %s\n", HexStr(bin)); + } + + if (options.seeds) { + vSeeds = *options.seeds; + } + + strNetworkID = CBaseChainParams::SIGNET; + consensus.signet_blocks = true; + consensus.signet_challenge.assign(bin.begin(), bin.end()); + consensus.nSubsidyHalvingInterval = 210000; + consensus.BIP34Height = 1; + consensus.BIP34Hash = uint256{}; + consensus.BIP65Height = 1; + consensus.BIP66Height = 1; + consensus.CSVHeight = 1; + consensus.SegwitHeight = 1; + consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks + consensus.nPowTargetSpacing = 10 * 60; + consensus.fPowAllowMinDifficultyBlocks = false; + consensus.fPowNoRetargeting = false; + consensus.nRuleChangeActivationThreshold = 1815; // 90% of 2016 + consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing + consensus.MinBIP9WarningHeight = 0; + consensus.powLimit = uint256S("00000377ae000000000000000000000000000000000000000000000000000000"); + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28; + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE; + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].min_activation_height = 0; // No activation delay + + // Activation of Taproot (BIPs 340-342) + consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2; + consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE; + consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; + consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 0; // No activation delay + + // message start is defined as the first 4 bytes of the sha256d of the block script + HashWriter h{}; + h << consensus.signet_challenge; + uint256 hash = h.GetHash(); + memcpy(pchMessageStart, hash.begin(), 4); + + nDefaultPort = 38333; + nPruneAfterHeight = 1000; + + genesis = CreateGenesisBlock(1598918400, 52613770, 0x1e0377ae, 1, 50 * COIN); + consensus.hashGenesisBlock = genesis.GetHash(); + assert(consensus.hashGenesisBlock == uint256S("0x00000008819873e925422c1ff0f99f7cc9bbb232af63a077a480a3633bee1ef6")); + assert(genesis.hashMerkleRoot == uint256S("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b")); + + vFixedSeeds.clear(); + + base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,111); + base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,196); + base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1,239); + base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x35, 0x87, 0xCF}; + base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94}; + + bech32_hrp = "tb"; + + fDefaultConsistencyChecks = false; + fRequireStandard = true; + m_is_test_chain = true; + m_is_mockable_chain = false; + } +}; + +/** + * Regression test: intended for private networks only. Has minimal difficulty to ensure that + * blocks can be found instantly. + */ +class CRegTestParams : public CChainParams +{ +public: + explicit CRegTestParams(const RegTestOptions& opts) + { + strNetworkID = CBaseChainParams::REGTEST; + consensus.signet_blocks = false; + consensus.signet_challenge.clear(); + consensus.nSubsidyHalvingInterval = 150; + consensus.BIP34Height = 1; // Always active unless overridden + consensus.BIP34Hash = uint256(); + consensus.BIP65Height = 1; // Always active unless overridden + consensus.BIP66Height = 1; // Always active unless overridden + consensus.CSVHeight = 1; // Always active unless overridden + consensus.SegwitHeight = 0; // Always active unless overridden + consensus.MinBIP9WarningHeight = 0; + consensus.powLimit = uint256S("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks + consensus.nPowTargetSpacing = 10 * 60; + consensus.fPowAllowMinDifficultyBlocks = true; + consensus.fPowNoRetargeting = true; + consensus.nRuleChangeActivationThreshold = 108; // 75% for testchains + consensus.nMinerConfirmationWindow = 144; // Faster than normal for regtest (144 instead of 2016) + + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28; + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 0; + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].min_activation_height = 0; // No activation delay + + consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2; + consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE; + consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; + consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 0; // No activation delay + + consensus.nMinimumChainWork = uint256{}; + consensus.defaultAssumeValid = uint256{}; + + pchMessageStart[0] = 0xfa; + pchMessageStart[1] = 0xbf; + pchMessageStart[2] = 0xb5; + pchMessageStart[3] = 0xda; + nDefaultPort = 18444; + nPruneAfterHeight = opts.fastprune ? 100 : 1000; + m_assumed_blockchain_size = 0; + m_assumed_chain_state_size = 0; + + for (const auto& [dep, height] : opts.activation_heights) { + switch (dep) { + case Consensus::BuriedDeployment::DEPLOYMENT_SEGWIT: + consensus.SegwitHeight = int{height}; + break; + case Consensus::BuriedDeployment::DEPLOYMENT_HEIGHTINCB: + consensus.BIP34Height = int{height}; + break; + case Consensus::BuriedDeployment::DEPLOYMENT_DERSIG: + consensus.BIP66Height = int{height}; + break; + case Consensus::BuriedDeployment::DEPLOYMENT_CLTV: + consensus.BIP65Height = int{height}; + break; + case Consensus::BuriedDeployment::DEPLOYMENT_CSV: + consensus.CSVHeight = int{height}; + break; + } + } + + for (const auto& [deployment_pos, version_bits_params] : opts.version_bits_parameters) { + consensus.vDeployments[deployment_pos].nStartTime = version_bits_params.start_time; + consensus.vDeployments[deployment_pos].nTimeout = version_bits_params.timeout; + consensus.vDeployments[deployment_pos].min_activation_height = version_bits_params.min_activation_height; + } + + genesis = CreateGenesisBlock(1296688602, 2, 0x207fffff, 1, 50 * COIN); + consensus.hashGenesisBlock = genesis.GetHash(); + assert(consensus.hashGenesisBlock == uint256S("0x0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206")); + assert(genesis.hashMerkleRoot == uint256S("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b")); + + vFixedSeeds.clear(); //!< Regtest mode doesn't have any fixed seeds. + vSeeds.clear(); + vSeeds.emplace_back("dummySeed.invalid."); + + fDefaultConsistencyChecks = true; + fRequireStandard = true; + m_is_test_chain = true; + m_is_mockable_chain = true; + + checkpointData = { + { + {0, uint256S("0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206")}, + } + }; + + m_assumeutxo_data = MapAssumeutxo{ + { + 110, + {AssumeutxoHash{uint256S("0x1ebbf5850204c0bdb15bf030f47c7fe91d45c44c712697e4509ba67adb01c618")}, 110}, + }, + { + 200, + {AssumeutxoHash{uint256S("0x51c8d11d8b5c1de51543c579736e786aa2736206d1e11e627568029ce092cf62")}, 200}, + }, + }; + + chainTxData = ChainTxData{ + 0, + 0, + 0 + }; + + base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,111); + base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,196); + base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1,239); + base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x35, 0x87, 0xCF}; + base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94}; + + bech32_hrp = "bcrt"; + } +}; + +std::unique_ptr<const CChainParams> CChainParams::SigNet(const SigNetOptions& options) +{ + return std::make_unique<const SigNetParams>(options); +} + +std::unique_ptr<const CChainParams> CChainParams::RegTest(const RegTestOptions& options) +{ + return std::make_unique<const CRegTestParams>(options); +} + +std::unique_ptr<const CChainParams> CChainParams::Main() +{ + return std::make_unique<const CMainParams>(); +} + +std::unique_ptr<const CChainParams> CChainParams::TestNet() +{ + return std::make_unique<const CTestNetParams>(); +} diff --git a/src/kernel/chainparams.h b/src/kernel/chainparams.h new file mode 100644 index 0000000000..32fe618dbd --- /dev/null +++ b/src/kernel/chainparams.h @@ -0,0 +1,187 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-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. + +#ifndef BITCOIN_KERNEL_CHAINPARAMS_H +#define BITCOIN_KERNEL_CHAINPARAMS_H + +#include <consensus/params.h> +#include <netaddress.h> +#include <primitives/block.h> +#include <protocol.h> +#include <uint256.h> +#include <util/hash_type.h> + +#include <cstdint> +#include <iterator> +#include <map> +#include <memory> +#include <optional> +#include <string> +#include <unordered_map> +#include <utility> +#include <vector> + +typedef std::map<int, uint256> MapCheckpoints; + +struct CCheckpointData { + MapCheckpoints mapCheckpoints; + + int GetHeight() const { + const auto& final_checkpoint = mapCheckpoints.rbegin(); + return final_checkpoint->first /* height */; + } +}; + +struct AssumeutxoHash : public BaseHash<uint256> { + explicit AssumeutxoHash(const uint256& hash) : BaseHash(hash) {} +}; + +/** + * Holds configuration for use during UTXO snapshot load and validation. The contents + * here are security critical, since they dictate which UTXO snapshots are recognized + * as valid. + */ +struct AssumeutxoData { + //! The expected hash of the deserialized UTXO set. + const AssumeutxoHash hash_serialized; + + //! Used to populate the nChainTx value, which is used during BlockManager::LoadBlockIndex(). + //! + //! We need to hardcode the value here because this is computed cumulatively using block data, + //! which we do not necessarily have at the time of snapshot load. + const unsigned int nChainTx; +}; + +using MapAssumeutxo = std::map<int, const AssumeutxoData>; + +/** + * Holds various statistics on transactions within a chain. Used to estimate + * verification progress during chain sync. + * + * See also: CChainParams::TxData, GuessVerificationProgress. + */ +struct ChainTxData { + int64_t nTime; //!< UNIX timestamp of last known number of transactions + int64_t nTxCount; //!< total number of transactions between genesis and that timestamp + double dTxRate; //!< estimated number of transactions per second after that timestamp +}; + +/** + * CChainParams defines various tweakable parameters of a given instance of the + * Bitcoin system. + */ +class CChainParams +{ +public: + enum Base58Type { + PUBKEY_ADDRESS, + SCRIPT_ADDRESS, + SECRET_KEY, + EXT_PUBLIC_KEY, + EXT_SECRET_KEY, + + MAX_BASE58_TYPES + }; + + const Consensus::Params& GetConsensus() const { return consensus; } + const CMessageHeader::MessageStartChars& MessageStart() const { return pchMessageStart; } + uint16_t GetDefaultPort() const { return nDefaultPort; } + uint16_t GetDefaultPort(Network net) const + { + return net == NET_I2P ? I2P_SAM31_PORT : GetDefaultPort(); + } + uint16_t GetDefaultPort(const std::string& addr) const + { + CNetAddr a; + return a.SetSpecial(addr) ? GetDefaultPort(a.GetNetwork()) : GetDefaultPort(); + } + + const CBlock& GenesisBlock() const { return genesis; } + /** Default value for -checkmempool and -checkblockindex argument */ + bool DefaultConsistencyChecks() const { return fDefaultConsistencyChecks; } + /** Policy: Filter transactions that do not match well-defined patterns */ + bool RequireStandard() const { return fRequireStandard; } + /** If this chain is exclusively used for testing */ + bool IsTestChain() const { return m_is_test_chain; } + /** If this chain allows time to be mocked */ + bool IsMockableChain() const { return m_is_mockable_chain; } + uint64_t PruneAfterHeight() const { return nPruneAfterHeight; } + /** Minimum free space (in GB) needed for data directory */ + uint64_t AssumedBlockchainSize() const { return m_assumed_blockchain_size; } + /** Minimum free space (in GB) needed for data directory when pruned; Does not include prune target*/ + uint64_t AssumedChainStateSize() const { return m_assumed_chain_state_size; } + /** Whether it is possible to mine blocks on demand (no retargeting) */ + bool MineBlocksOnDemand() const { return consensus.fPowNoRetargeting; } + /** Return the network string */ + std::string NetworkIDString() const { return strNetworkID; } + /** Return the list of hostnames to look up for DNS seeds */ + const std::vector<std::string>& DNSSeeds() const { return vSeeds; } + const std::vector<unsigned char>& Base58Prefix(Base58Type type) const { return base58Prefixes[type]; } + const std::string& Bech32HRP() const { return bech32_hrp; } + const std::vector<uint8_t>& FixedSeeds() const { return vFixedSeeds; } + const CCheckpointData& Checkpoints() const { return checkpointData; } + + //! Get allowed assumeutxo configuration. + //! @see ChainstateManager + const MapAssumeutxo& Assumeutxo() const { return m_assumeutxo_data; } + + const ChainTxData& TxData() const { return chainTxData; } + + /** + * SigNetOptions holds configurations for creating a signet CChainParams. + */ + struct SigNetOptions { + std::optional<std::vector<uint8_t>> challenge{}; + std::optional<std::vector<std::string>> seeds{}; + }; + + /** + * VersionBitsParameters holds activation parameters + */ + struct VersionBitsParameters { + int64_t start_time; + int64_t timeout; + int min_activation_height; + }; + + /** + * RegTestOptions holds configurations for creating a regtest CChainParams. + */ + struct RegTestOptions { + std::unordered_map<Consensus::DeploymentPos, VersionBitsParameters> version_bits_parameters{}; + std::unordered_map<Consensus::BuriedDeployment, int> activation_heights{}; + bool fastprune{false}; + }; + + static std::unique_ptr<const CChainParams> RegTest(const RegTestOptions& options); + static std::unique_ptr<const CChainParams> SigNet(const SigNetOptions& options); + static std::unique_ptr<const CChainParams> Main(); + static std::unique_ptr<const CChainParams> TestNet(); + +protected: + CChainParams() {} + + Consensus::Params consensus; + CMessageHeader::MessageStartChars pchMessageStart; + uint16_t nDefaultPort; + uint64_t nPruneAfterHeight; + uint64_t m_assumed_blockchain_size; + uint64_t m_assumed_chain_state_size; + std::vector<std::string> vSeeds; + std::vector<unsigned char> base58Prefixes[MAX_BASE58_TYPES]; + std::string bech32_hrp; + std::string strNetworkID; + CBlock genesis; + std::vector<uint8_t> vFixedSeeds; + bool fDefaultConsistencyChecks; + bool fRequireStandard; + bool m_is_test_chain; + bool m_is_mockable_chain; + CCheckpointData checkpointData; + MapAssumeutxo m_assumeutxo_data; + ChainTxData chainTxData; +}; + +#endif // BITCOIN_KERNEL_CHAINPARAMS_H diff --git a/src/kernel/coinstats.cpp b/src/kernel/coinstats.cpp index 82d7d8c46b..4b75c387a6 100644 --- a/src/kernel/coinstats.cpp +++ b/src/kernel/coinstats.cpp @@ -8,6 +8,7 @@ #include <coins.h> #include <crypto/muhash.h> #include <hash.h> +#include <logging.h> #include <node/blockstorage.h> #include <primitives/transaction.h> #include <script/script.h> @@ -19,7 +20,6 @@ #include <uint256.h> #include <util/check.h> #include <util/overflow.h> -#include <util/system.h> #include <validation.h> #include <version.h> diff --git a/src/key_io.cpp b/src/key_io.cpp index 6dd6f82112..4659a59544 100644 --- a/src/key_io.cpp +++ b/src/key_io.cpp @@ -107,17 +107,17 @@ CTxDestination DecodeDestination(const std::string& str, const CChainParams& par std::equal(script_prefix.begin(), script_prefix.end(), data.begin())) || (data.size() >= pubkey_prefix.size() && std::equal(pubkey_prefix.begin(), pubkey_prefix.end(), data.begin()))) { - error_str = "Invalid length for Base58 address"; + error_str = "Invalid length for Base58 address (P2PKH or P2SH)"; } else { - error_str = "Invalid prefix for Base58-encoded address"; + error_str = "Invalid or unsupported Base58-encoded address."; } return CNoDestination(); } else if (!is_bech32) { // Try Base58 decoding without the checksum, using a much larger max length if (!DecodeBase58(str, data, 100)) { - error_str = "Not a valid Bech32 or Base58 encoding"; + error_str = "Invalid or unsupported Segwit (Bech32) or Base58 encoding."; } else { - error_str = "Invalid checksum or length of Base58 address"; + error_str = "Invalid checksum or length of Base58 address (P2PKH or P2SH)"; } return CNoDestination(); } @@ -127,7 +127,7 @@ CTxDestination DecodeDestination(const std::string& str, const CChainParams& par if ((dec.encoding == bech32::Encoding::BECH32 || dec.encoding == bech32::Encoding::BECH32M) && dec.data.size() > 0) { // Bech32 decoding if (dec.hrp != params.Bech32HRP()) { - error_str = "Invalid prefix for Bech32 address"; + error_str = strprintf("Invalid or unsupported prefix for Segwit (Bech32) address (expected %s, got %s).", params.Bech32HRP(), dec.hrp); return CNoDestination(); } int version = dec.data[0]; // The first 5 bit symbol is the witness version (0-16) diff --git a/src/logging.h b/src/logging.h index 35fb598cef..954731d214 100644 --- a/src/logging.h +++ b/src/logging.h @@ -257,4 +257,11 @@ static inline void LogPrintf_(const std::string& logging_function, const std::st } \ } while (0) +template <typename... Args> +bool error(const char* fmt, const Args&... args) +{ + LogPrintf("ERROR: %s\n", tfm::format(fmt, args...)); + return false; +} + #endif // BITCOIN_LOGGING_H diff --git a/src/net.cpp b/src/net.cpp index 4e4f2f78be..d1895b7172 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -16,12 +16,13 @@ #include <compat/compat.h> #include <consensus/consensus.h> #include <crypto/sha256.h> -#include <node/eviction.h> #include <fs.h> #include <i2p.h> +#include <logging.h> #include <net_permissions.h> #include <netaddress.h> #include <netbase.h> +#include <node/eviction.h> #include <node/interface_ui.h> #include <protocol.h> #include <random.h> diff --git a/src/netbase.cpp b/src/netbase.cpp index 797f1e17f2..f39a3635f4 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -6,12 +6,12 @@ #include <netbase.h> #include <compat/compat.h> +#include <logging.h> #include <sync.h> #include <tinyformat.h> #include <util/sock.h> #include <util/strencodings.h> #include <util/string.h> -#include <util/system.h> #include <util/time.h> #include <atomic> diff --git a/src/node/blockmanager_args.cpp b/src/node/blockmanager_args.cpp new file mode 100644 index 0000000000..5fb5c8beed --- /dev/null +++ b/src/node/blockmanager_args.cpp @@ -0,0 +1,30 @@ +// Copyright (c) 2023 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 <node/blockmanager_args.h> + +#include <util/system.h> +#include <validation.h> + +namespace node { +std::optional<bilingual_str> ApplyArgsManOptions(const ArgsManager& args, BlockManager::Options& opts) +{ + // block pruning; get the amount of disk space (in MiB) to allot for block & undo files + int64_t nPruneArg{args.GetIntArg("-prune", opts.prune_target)}; + if (nPruneArg < 0) { + return _("Prune cannot be configured with a negative value."); + } + uint64_t nPruneTarget{uint64_t(nPruneArg) * 1024 * 1024}; + if (nPruneArg == 1) { // manual pruning: -prune=1 + nPruneTarget = BlockManager::PRUNE_TARGET_MANUAL; + } else if (nPruneTarget) { + if (nPruneTarget < MIN_DISK_SPACE_FOR_BLOCK_FILES) { + return strprintf(_("Prune configured below the minimum of %d MiB. Please use a higher number."), MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024); + } + } + opts.prune_target = nPruneTarget; + + return std::nullopt; +} +} // namespace node diff --git a/src/node/blockmanager_args.h b/src/node/blockmanager_args.h new file mode 100644 index 0000000000..e657c6bb45 --- /dev/null +++ b/src/node/blockmanager_args.h @@ -0,0 +1,20 @@ + +// Copyright (c) 2023 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_NODE_BLOCKMANAGER_ARGS_H +#define BITCOIN_NODE_BLOCKMANAGER_ARGS_H + +#include <node/blockstorage.h> + +#include <optional> + +class ArgsManager; +struct bilingual_str; + +namespace node { +std::optional<bilingual_str> ApplyArgsManOptions(const ArgsManager& args, BlockManager::Options& opts); +} // namespace node + +#endif // BITCOIN_NODE_BLOCKMANAGER_ARGS_H diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp index c0a25572fc..53721b807c 100644 --- a/src/node/blockstorage.cpp +++ b/src/node/blockstorage.cpp @@ -5,12 +5,13 @@ #include <node/blockstorage.h> #include <chain.h> -#include <chainparams.h> #include <clientversion.h> #include <consensus/validation.h> #include <flatfile.h> #include <fs.h> #include <hash.h> +#include <logging.h> +#include <kernel/chainparams.h> #include <pow.h> #include <reverse_iterator.h> #include <shutdown.h> @@ -25,10 +26,7 @@ #include <unordered_map> namespace node { -std::atomic_bool fImporting(false); std::atomic_bool fReindex(false); -bool fPruneMode = false; -uint64_t nPruneTarget = 0; bool CBlockIndexWorkComparator::operator()(const CBlockIndex* pa, const CBlockIndex* pb) const { @@ -153,7 +151,7 @@ void BlockManager::PruneOneBlockFile(const int fileNumber) void BlockManager::FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight, int chain_tip_height) { - assert(fPruneMode && nManualPruneHeight > 0); + assert(IsPruneMode() && nManualPruneHeight > 0); LOCK2(cs_main, cs_LastBlockFile); if (chain_tip_height < 0) { @@ -177,7 +175,7 @@ void BlockManager::FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nM void BlockManager::FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight, int chain_tip_height, int prune_height, bool is_ibd) { LOCK2(cs_main, cs_LastBlockFile); - if (chain_tip_height < 0 || nPruneTarget == 0) { + if (chain_tip_height < 0 || GetPruneTarget() == 0) { return; } if ((uint64_t)chain_tip_height <= nPruneAfterHeight) { @@ -193,14 +191,14 @@ void BlockManager::FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPr uint64_t nBytesToPrune; int count = 0; - if (nCurrentUsage + nBuffer >= nPruneTarget) { + if (nCurrentUsage + nBuffer >= GetPruneTarget()) { // On a prune event, the chainstate DB is flushed. // To avoid excessive prune events negating the benefit of high dbcache // values, we should not prune too rapidly. // So when pruning in IBD, increase the buffer a bit to avoid a re-prune too soon. if (is_ibd) { // Since this is only relevant during IBD, we use a fixed 10% - nBuffer += nPruneTarget / 10; + nBuffer += GetPruneTarget() / 10; } for (int fileNumber = 0; fileNumber < m_last_blockfile; fileNumber++) { @@ -210,7 +208,7 @@ void BlockManager::FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPr continue; } - if (nCurrentUsage + nBuffer < nPruneTarget) { // are we below our target? + if (nCurrentUsage + nBuffer < GetPruneTarget()) { // are we below our target? break; } @@ -228,9 +226,9 @@ void BlockManager::FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPr } LogPrint(BCLog::PRUNE, "target=%dMiB actual=%dMiB diff=%dMiB max_prune_height=%d removed %d blk/rev pairs\n", - nPruneTarget/1024/1024, nCurrentUsage/1024/1024, - ((int64_t)nPruneTarget - (int64_t)nCurrentUsage)/1024/1024, - nLastBlockWeCanPrune, count); + GetPruneTarget() / 1024 / 1024, nCurrentUsage / 1024 / 1024, + (int64_t(GetPruneTarget()) - int64_t(nCurrentUsage)) / 1024 / 1024, + nLastBlockWeCanPrune, count); } void BlockManager::UpdatePruneLock(const std::string& name, const PruneLockInfo& lock_info) { @@ -656,7 +654,7 @@ bool BlockManager::FindBlockPos(FlatFilePos& pos, unsigned int nAddSize, unsigne if (out_of_space) { return AbortNode("Disk space is too low!", _("Disk space is too low!")); } - if (bytes_allocated != 0 && fPruneMode) { + if (bytes_allocated != 0 && IsPruneMode()) { m_check_for_pruning = true; } } @@ -680,7 +678,7 @@ bool BlockManager::FindUndoPos(BlockValidationState& state, int nFile, FlatFileP if (out_of_space) { return AbortNode(state, "Disk space is too low!", _("Disk space is too low!")); } - if (bytes_allocated != 0 && fPruneMode) { + if (bytes_allocated != 0 && IsPruneMode()) { m_check_for_pruning = true; } @@ -845,17 +843,20 @@ FlatFilePos BlockManager::SaveBlockToDisk(const CBlock& block, int nHeight, CCha return blockPos; } -struct CImportingNow { - CImportingNow() +class ImportingNow +{ + std::atomic<bool>& m_importing; + +public: + ImportingNow(std::atomic<bool>& importing) : m_importing{importing} { - assert(fImporting == false); - fImporting = true; + assert(m_importing == false); + m_importing = true; } - - ~CImportingNow() + ~ImportingNow() { - assert(fImporting == true); - fImporting = false; + assert(m_importing == true); + m_importing = false; } }; @@ -865,7 +866,7 @@ void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFile ScheduleBatchPriority(); { - CImportingNow imp; + ImportingNow imp{chainman.m_blockman.m_importing}; // -reindex if (fReindex) { @@ -931,7 +932,7 @@ void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFile StartShutdown(); return; } - } // End scope of CImportingNow + } // End scope of ImportingNow chainman.ActiveChainstate().LoadMempool(mempool_path); } } // namespace node diff --git a/src/node/blockstorage.h b/src/node/blockstorage.h index 09aff7f08b..5ba0045b8b 100644 --- a/src/node/blockstorage.h +++ b/src/node/blockstorage.h @@ -8,6 +8,7 @@ #include <attributes.h> #include <chain.h> #include <fs.h> +#include <kernel/blockmanager_opts.h> #include <kernel/cs_main.h> #include <protocol.h> #include <sync.h> @@ -46,10 +47,7 @@ static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB /** Size of header written by WriteBlockToDisk before a serialized CBlock */ static constexpr size_t BLOCK_SERIALIZATION_HEADER_SIZE = CMessageHeader::MESSAGE_START_SIZE + sizeof(unsigned int); -extern std::atomic_bool fImporting; extern std::atomic_bool fReindex; -extern bool fPruneMode; -extern uint64_t nPruneTarget; // Because validation code takes pointers to the map's CBlockIndex objects, if // we ever switch to another associative container, we need to either use a @@ -124,6 +122,8 @@ private: */ bool m_check_for_pruning = false; + const bool m_prune_mode; + /** Dirty block index entries. */ std::set<CBlockIndex*> m_dirty_blockindex; @@ -138,7 +138,17 @@ private: */ std::unordered_map<std::string, PruneLockInfo> m_prune_locks GUARDED_BY(::cs_main); + const kernel::BlockManagerOpts m_opts; + public: + using Options = kernel::BlockManagerOpts; + + explicit BlockManager(Options opts) + : m_prune_mode{opts.prune_target > 0}, + m_opts{std::move(opts)} {}; + + std::atomic<bool> m_importing{false}; + BlockMap m_block_index GUARDED_BY(cs_main); std::vector<CBlockIndex*> GetAllBlockIndices() EXCLUSIVE_LOCKS_REQUIRED(::cs_main); @@ -181,15 +191,13 @@ public: FlatFilePos SaveBlockToDisk(const CBlock& block, int nHeight, CChain& active_chain, const CChainParams& chainparams, const FlatFilePos* dbp); /** Whether running in -prune mode. */ - [[nodiscard]] bool IsPruneMode() const { return fPruneMode; } + [[nodiscard]] bool IsPruneMode() const { return m_prune_mode; } /** Attempt to stay below this number of bytes of block files. */ - [[nodiscard]] uint64_t GetPruneTarget() const { return nPruneTarget; } + [[nodiscard]] uint64_t GetPruneTarget() const { return m_opts.prune_target; } + static constexpr auto PRUNE_TARGET_MANUAL{std::numeric_limits<uint64_t>::max()}; - [[nodiscard]] bool LoadingBlocks() const - { - return fImporting || fReindex; - } + [[nodiscard]] bool LoadingBlocks() const { return m_importing || fReindex; } /** Calculate the amount of disk space the block & undo files currently use */ uint64_t CalculateCurrentUsage(); diff --git a/src/node/chainstate.cpp b/src/node/chainstate.cpp index 626010d26f..cfd3472592 100644 --- a/src/node/chainstate.cpp +++ b/src/node/chainstate.cpp @@ -28,38 +28,13 @@ #include <vector> namespace node { -ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSizes& cache_sizes, - const ChainstateLoadOptions& options) +// Complete initialization of chainstates after the initial call has been made +// to ChainstateManager::InitializeChainstate(). +static ChainstateLoadResult CompleteChainstateInitialization( + ChainstateManager& chainman, + const CacheSizes& cache_sizes, + const ChainstateLoadOptions& options) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { - auto is_coinsview_empty = [&](Chainstate* chainstate) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { - return options.reindex || options.reindex_chainstate || chainstate->CoinsTip().GetBestBlock().IsNull(); - }; - - if (!chainman.AssumedValidBlock().IsNull()) { - LogPrintf("Assuming ancestors of block %s have valid signatures.\n", chainman.AssumedValidBlock().GetHex()); - } else { - LogPrintf("Validating signatures for all blocks.\n"); - } - LogPrintf("Setting nMinimumChainWork=%s\n", chainman.MinimumChainWork().GetHex()); - if (chainman.MinimumChainWork() < UintToArith256(chainman.GetConsensus().nMinimumChainWork)) { - LogPrintf("Warning: nMinimumChainWork set below default value of %s\n", chainman.GetConsensus().nMinimumChainWork.GetHex()); - } - if (chainman.m_blockman.GetPruneTarget() == std::numeric_limits<uint64_t>::max()) { - LogPrintf("Block pruning enabled. Use RPC call pruneblockchain(height) to manually prune block and undo files.\n"); - } else if (chainman.m_blockman.GetPruneTarget()) { - LogPrintf("Prune configured to target %u MiB on disk for block and undo files.\n", chainman.m_blockman.GetPruneTarget() / 1024 / 1024); - } - - LOCK(cs_main); - chainman.m_total_coinstip_cache = cache_sizes.coins; - chainman.m_total_coinsdb_cache = cache_sizes.coins_db; - - // Load the fully validated chainstate. - chainman.InitializeChainstate(options.mempool); - - // Load a chain created from a UTXO snapshot, if any exist. - chainman.DetectSnapshotChainstate(options.mempool); - auto& pblocktree{chainman.m_blockman.m_block_tree_db}; // new CBlockTreeDB tries to delete the existing file, which // fails if it's still open from the previous loop. Close it first: @@ -111,6 +86,13 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSize return {ChainstateLoadStatus::FAILURE, _("Error initializing block database")}; } + auto is_coinsview_empty = [&](Chainstate* chainstate) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { + return options.reindex || options.reindex_chainstate || chainstate->CoinsTip().GetBestBlock().IsNull(); + }; + + assert(chainman.m_total_coinstip_cache > 0); + assert(chainman.m_total_coinsdb_cache > 0); + // Conservative value which is arbitrarily chosen, as it will ultimately be changed // by a call to `chainman.MaybeRebalanceCaches()`. We just need to make sure // that the sum of the two caches (40%) does not exceed the allowable amount @@ -175,6 +157,84 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSize return {ChainstateLoadStatus::SUCCESS, {}}; } +ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSizes& cache_sizes, + const ChainstateLoadOptions& options) +{ + if (!chainman.AssumedValidBlock().IsNull()) { + LogPrintf("Assuming ancestors of block %s have valid signatures.\n", chainman.AssumedValidBlock().GetHex()); + } else { + LogPrintf("Validating signatures for all blocks.\n"); + } + LogPrintf("Setting nMinimumChainWork=%s\n", chainman.MinimumChainWork().GetHex()); + if (chainman.MinimumChainWork() < UintToArith256(chainman.GetConsensus().nMinimumChainWork)) { + LogPrintf("Warning: nMinimumChainWork set below default value of %s\n", chainman.GetConsensus().nMinimumChainWork.GetHex()); + } + if (chainman.m_blockman.GetPruneTarget() == BlockManager::PRUNE_TARGET_MANUAL) { + LogPrintf("Block pruning enabled. Use RPC call pruneblockchain(height) to manually prune block and undo files.\n"); + } else if (chainman.m_blockman.GetPruneTarget()) { + LogPrintf("Prune configured to target %u MiB on disk for block and undo files.\n", chainman.m_blockman.GetPruneTarget() / 1024 / 1024); + } + + LOCK(cs_main); + + chainman.m_total_coinstip_cache = cache_sizes.coins; + chainman.m_total_coinsdb_cache = cache_sizes.coins_db; + + // Load the fully validated chainstate. + chainman.InitializeChainstate(options.mempool); + + // Load a chain created from a UTXO snapshot, if any exist. + chainman.DetectSnapshotChainstate(options.mempool); + + auto [init_status, init_error] = CompleteChainstateInitialization(chainman, cache_sizes, options); + if (init_status != ChainstateLoadStatus::SUCCESS) { + return {init_status, init_error}; + } + + // If a snapshot chainstate was fully validated by a background chainstate during + // the last run, detect it here and clean up the now-unneeded background + // chainstate. + // + // Why is this cleanup done here (on subsequent restart) and not just when the + // snapshot is actually validated? Because this entails unusual + // filesystem operations to move leveldb data directories around, and that seems + // too risky to do in the middle of normal runtime. + auto snapshot_completion = chainman.MaybeCompleteSnapshotValidation(); + + if (snapshot_completion == SnapshotCompletionResult::SKIPPED) { + // do nothing; expected case + } else if (snapshot_completion == SnapshotCompletionResult::SUCCESS) { + LogPrintf("[snapshot] cleaning up unneeded background chainstate, then reinitializing\n"); + if (!chainman.ValidatedSnapshotCleanup()) { + AbortNode("Background chainstate cleanup failed unexpectedly."); + } + + // Because ValidatedSnapshotCleanup() has torn down chainstates with + // ChainstateManager::ResetChainstates(), reinitialize them here without + // duplicating the blockindex work above. + assert(chainman.GetAll().empty()); + assert(!chainman.IsSnapshotActive()); + assert(!chainman.IsSnapshotValidated()); + + chainman.InitializeChainstate(options.mempool); + + // A reload of the block index is required to recompute setBlockIndexCandidates + // for the fully validated chainstate. + chainman.ActiveChainstate().UnloadBlockIndex(); + + auto [init_status, init_error] = CompleteChainstateInitialization(chainman, cache_sizes, options); + if (init_status != ChainstateLoadStatus::SUCCESS) { + return {init_status, init_error}; + } + } else { + return {ChainstateLoadStatus::FAILURE, _( + "UTXO snapshot failed to validate. " + "Restart to resume normal initial block download, or try loading a different snapshot.")}; + } + + return {ChainstateLoadStatus::SUCCESS, {}}; +} + ChainstateLoadResult VerifyLoadedChainstate(ChainstateManager& chainman, const ChainstateLoadOptions& options) { auto is_coinsview_empty = [&](Chainstate* chainstate) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { diff --git a/src/node/interface_ui.cpp b/src/node/interface_ui.cpp index 08d1e03541..9dd1e7d9cf 100644 --- a/src/node/interface_ui.cpp +++ b/src/node/interface_ui.cpp @@ -4,6 +4,7 @@ #include <node/interface_ui.h> +#include <util/string.h> #include <util/translation.h> #include <boost/signals2/optional_last_value.hpp> @@ -62,6 +63,18 @@ bool InitError(const bilingual_str& str) return false; } +bool InitError(const bilingual_str& str, const std::vector<std::string>& details) +{ + // For now just flatten the list of error details into a string to pass to + // the base InitError overload. In the future, if more init code provides + // error details, the details could be passed separately from the main + // message for rich display in the GUI. But currently the only init + // functions which provide error details are ones that run during early init + // before the GUI uiInterface is registered, so there's no point passing + // main messages and details separately to uiInterface yet. + return InitError(details.empty() ? str : strprintf(Untranslated("%s:\n%s"), str, MakeUnorderedList(details))); +} + void InitWarning(const bilingual_str& str) { uiInterface.ThreadSafeMessageBox(str, "", CClientUIInterface::MSG_WARNING); diff --git a/src/node/interface_ui.h b/src/node/interface_ui.h index 9f6503b4a1..22c241cb78 100644 --- a/src/node/interface_ui.h +++ b/src/node/interface_ui.h @@ -116,7 +116,7 @@ void InitWarning(const bilingual_str& str); /** Show error message **/ bool InitError(const bilingual_str& str); -constexpr auto AbortError = InitError; +bool InitError(const bilingual_str& str, const std::vector<std::string>& details); extern CClientUIInterface uiInterface; diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index 9c2db5ff0c..b397661df4 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -86,14 +86,14 @@ class NodeImpl : public Node { public: explicit NodeImpl(NodeContext& context) { setContext(&context); } - void initLogging() override { InitLogging(*Assert(m_context->args)); } - void initParameterInteraction() override { InitParameterInteraction(*Assert(m_context->args)); } + void initLogging() override { InitLogging(args()); } + void initParameterInteraction() override { InitParameterInteraction(args()); } bilingual_str getWarnings() override { return GetWarnings(true); } uint32_t getLogCategories() override { return LogInstance().GetCategoryMask(); } bool baseInitialize() override { - if (!AppInitBasicSetup(gArgs)) return false; - if (!AppInitParameterInteraction(gArgs, /*use_syscall_sandbox=*/false)) return false; + if (!AppInitBasicSetup(args())) return false; + if (!AppInitParameterInteraction(args(), /*use_syscall_sandbox=*/false)) return false; m_context->kernel = std::make_unique<kernel::Context>(); if (!AppInitSanityChecks(*m_context->kernel)) return false; @@ -116,7 +116,7 @@ public: { StartShutdown(); // Stop RPC for clean shutdown if any of waitfor* commands is executed. - if (gArgs.GetBoolArg("-server", false)) { + if (args().GetBoolArg("-server", false)) { InterruptRPC(); StopRPC(); } @@ -125,28 +125,28 @@ public: bool isSettingIgnored(const std::string& name) override { bool ignored = false; - gArgs.LockSettings([&](util::Settings& settings) { + args().LockSettings([&](util::Settings& settings) { if (auto* options = util::FindKey(settings.command_line_options, name)) { ignored = !options->empty(); } }); return ignored; } - util::SettingsValue getPersistentSetting(const std::string& name) override { return gArgs.GetPersistentSetting(name); } + util::SettingsValue getPersistentSetting(const std::string& name) override { return args().GetPersistentSetting(name); } void updateRwSetting(const std::string& name, const util::SettingsValue& value) override { - gArgs.LockSettings([&](util::Settings& settings) { + args().LockSettings([&](util::Settings& settings) { if (value.isNull()) { settings.rw_settings.erase(name); } else { settings.rw_settings[name] = value; } }); - gArgs.WriteSettingsFile(); + args().WriteSettingsFile(); } void forceSetting(const std::string& name, const util::SettingsValue& value) override { - gArgs.LockSettings([&](util::Settings& settings) { + args().LockSettings([&](util::Settings& settings) { if (value.isNull()) { settings.forced_settings.erase(name); } else { @@ -156,11 +156,11 @@ public: } void resetSettings() override { - gArgs.WriteSettingsFile(/*errors=*/nullptr, /*backup=*/true); - gArgs.LockSettings([&](util::Settings& settings) { + args().WriteSettingsFile(/*errors=*/nullptr, /*backup=*/true); + args().LockSettings([&](util::Settings& settings) { settings.rw_settings.clear(); }); - gArgs.WriteSettingsFile(); + args().WriteSettingsFile(); } void mapPort(bool use_upnp, bool use_natpmp) override { StartMapPort(use_upnp, use_natpmp); } bool getProxy(Network net, Proxy& proxy_info) override { return GetProxy(net, proxy_info); } @@ -237,7 +237,7 @@ public: { #ifdef ENABLE_EXTERNAL_SIGNER std::vector<ExternalSigner> signers = {}; - const std::string command = gArgs.GetArg("-signer", ""); + const std::string command = args().GetArg("-signer", ""); if (command == "") return {}; ExternalSigner::Enumerate(command, signers, Params().NetworkIDString()); std::vector<std::unique_ptr<interfaces::ExternalSigner>> result; @@ -295,7 +295,7 @@ public: bool isInitialBlockDownload() override { return chainman().ActiveChainstate().IsInitialBlockDownload(); } - bool isLoadingBlocks() override { return node::fReindex || node::fImporting; } + bool isLoadingBlocks() override { return chainman().m_blockman.LoadingBlocks(); } void setNetworkActive(bool active) override { if (m_context->connman) { @@ -388,6 +388,7 @@ public: { m_context = context; } + ArgsManager& args() { return *Assert(Assert(m_context)->args); } ChainstateManager& chainman() { return *Assert(m_context->chainman); } NodeContext* m_context{nullptr}; }; @@ -744,16 +745,16 @@ public: int rpcSerializationFlags() override { return RPCSerializationFlags(); } util::SettingsValue getSetting(const std::string& name) override { - return gArgs.GetSetting(name); + return args().GetSetting(name); } std::vector<util::SettingsValue> getSettingsList(const std::string& name) override { - return gArgs.GetSettingsList(name); + return args().GetSettingsList(name); } util::SettingsValue getRwSetting(const std::string& name) override { util::SettingsValue result; - gArgs.LockSettings([&](const util::Settings& settings) { + args().LockSettings([&](const util::Settings& settings) { if (const util::SettingsValue* value = util::FindKey(settings.rw_settings, name)) { result = *value; } @@ -762,14 +763,14 @@ public: } bool updateRwSetting(const std::string& name, const util::SettingsValue& value, bool write) override { - gArgs.LockSettings([&](util::Settings& settings) { + args().LockSettings([&](util::Settings& settings) { if (value.isNull()) { settings.rw_settings.erase(name); } else { settings.rw_settings[name] = value; } }); - return !write || gArgs.WriteSettingsFile(); + return !write || args().WriteSettingsFile(); } void requestMempoolTransactions(Notifications& notifications) override { @@ -785,6 +786,7 @@ public: } NodeContext* context() override { return &m_node; } + ArgsManager& args() { return *Assert(m_node.args); } ChainstateManager& chainman() { return *Assert(m_node.chainman); } NodeContext& m_node; }; diff --git a/src/node/mempool_args.cpp b/src/node/mempool_args.cpp index 8c929e5e0d..a0a2e43107 100644 --- a/src/node/mempool_args.cpp +++ b/src/node/mempool_args.cpp @@ -7,8 +7,8 @@ #include <kernel/mempool_limits.h> #include <kernel/mempool_options.h> -#include <chainparams.h> #include <consensus/amount.h> +#include <kernel/chainparams.h> #include <logging.h> #include <policy/feerate.h> #include <policy/policy.h> diff --git a/src/node/minisketchwrapper.cpp b/src/node/minisketchwrapper.cpp index 67e823cb68..96707f7a0a 100644 --- a/src/node/minisketchwrapper.cpp +++ b/src/node/minisketchwrapper.cpp @@ -23,17 +23,17 @@ static constexpr uint32_t BITS = 32; uint32_t FindBestImplementation() { - std::optional<std::pair<int64_t, uint32_t>> best; + std::optional<std::pair<SteadyClock::duration, uint32_t>> best; uint32_t max_impl = Minisketch::MaxImplementation(); for (uint32_t impl = 0; impl <= max_impl; ++impl) { - std::vector<int64_t> benches; + std::vector<SteadyClock::duration> benches; uint64_t offset = 0; /* Run a little benchmark with capacity 32, adding 184 entries, and decoding 11 of them once. */ for (int b = 0; b < 11; ++b) { if (!Minisketch::ImplementationSupported(BITS, impl)) break; Minisketch sketch(BITS, impl, 32); - auto start = GetTimeMicros(); + auto start = SteadyClock::now(); for (uint64_t e = 0; e < 100; ++e) { sketch.Add(e*1337 + b*13337 + offset); } @@ -41,7 +41,7 @@ uint32_t FindBestImplementation() sketch.Add(e*1337 + b*13337 + offset); } offset += (*sketch.Decode(32))[0]; - auto stop = GetTimeMicros(); + auto stop = SteadyClock::now(); benches.push_back(stop - start); } /* Remember which implementation has the best median benchmark time. */ diff --git a/src/qt/bantablemodel.cpp b/src/qt/bantablemodel.cpp index 4f57cd4457..4592d29abc 100644 --- a/src/qt/bantablemodel.cpp +++ b/src/qt/bantablemodel.cpp @@ -43,7 +43,7 @@ public: /** Order (ascending or descending) to sort nodes by */ Qt::SortOrder sortOrder; - /** Pull a full list of banned nodes from CNode into our cache */ + /** Pull a full list of banned nodes from interfaces::Node into our cache */ void refreshBanlist(interfaces::Node& node) { banmap_t banMap; @@ -178,3 +178,9 @@ bool BanTableModel::shouldShow() { return priv->size() > 0; } + +bool BanTableModel::unban(const QModelIndex& index) +{ + CCombinedBan* ban{static_cast<CCombinedBan*>(index.internalPointer())}; + return ban != nullptr && m_node.unban(ban->subnet); +} diff --git a/src/qt/bantablemodel.h b/src/qt/bantablemodel.h index 0a30905172..adb97ad1c0 100644 --- a/src/qt/bantablemodel.h +++ b/src/qt/bantablemodel.h @@ -37,7 +37,7 @@ private: }; /** - Qt model providing information about connected peers, similar to the + Qt model providing information about banned peers, similar to the "getpeerinfo" RPC call. Used by the rpc console UI. */ class BanTableModel : public QAbstractTableModel @@ -68,6 +68,8 @@ public: bool shouldShow(); + bool unban(const QModelIndex& index); + public Q_SLOTS: void refresh(); diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 99faa51ea0..2c413e8b43 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -9,6 +9,7 @@ #include <qt/bitcoin.h> #include <chainparams.h> +#include <common/init.h> #include <init.h> #include <interfaces/handler.h> #include <interfaces/init.h> @@ -28,6 +29,7 @@ #include <qt/utilitydialog.h> #include <qt/winshutdownmonitor.h> #include <uint256.h> +#include <util/exception.h> #include <util/string.h> #include <util/system.h> #include <util/threadnames.h> @@ -165,54 +167,36 @@ static void initTranslations(QTranslator &qtTranslatorBase, QTranslator &qtTrans } } -static bool InitSettings() +static bool ErrorSettingsRead(const bilingual_str& error, const std::vector<std::string>& details) { - gArgs.EnsureDataDir(); - if (!gArgs.GetSettingsPath()) { - return true; // Do nothing if settings file disabled. - } - - std::vector<std::string> errors; - if (!gArgs.ReadSettingsFile(&errors)) { - std::string error = QT_TRANSLATE_NOOP("bitcoin-core", "Settings file could not be read"); - std::string error_translated = QCoreApplication::translate("bitcoin-core", error.c_str()).toStdString(); - InitError(Untranslated(strprintf("%s:\n%s\n", error, MakeUnorderedList(errors)))); - - QMessageBox messagebox(QMessageBox::Critical, PACKAGE_NAME, QString::fromStdString(strprintf("%s.", error_translated)), QMessageBox::Reset | QMessageBox::Abort); - /*: Explanatory text shown on startup when the settings file cannot be read. - Prompts user to make a choice between resetting or aborting. */ - messagebox.setInformativeText(QObject::tr("Do you want to reset settings to default values, or to abort without making changes?")); - messagebox.setDetailedText(QString::fromStdString(MakeUnorderedList(errors))); - messagebox.setTextFormat(Qt::PlainText); - messagebox.setDefaultButton(QMessageBox::Reset); - switch (messagebox.exec()) { - case QMessageBox::Reset: - break; - case QMessageBox::Abort: - return false; - default: - assert(false); - } - } - - errors.clear(); - if (!gArgs.WriteSettingsFile(&errors)) { - std::string error = QT_TRANSLATE_NOOP("bitcoin-core", "Settings file could not be written"); - std::string error_translated = QCoreApplication::translate("bitcoin-core", error.c_str()).toStdString(); - InitError(Untranslated(strprintf("%s:\n%s\n", error, MakeUnorderedList(errors)))); - - QMessageBox messagebox(QMessageBox::Critical, PACKAGE_NAME, QString::fromStdString(strprintf("%s.", error_translated)), QMessageBox::Ok); - /*: Explanatory text shown on startup when the settings file could not be written. - Prompts user to check that we have the ability to write to the file. - Explains that the user has the option of running without a settings file.*/ - messagebox.setInformativeText(QObject::tr("A fatal error occurred. Check that settings file is writable, or try running with -nosettings.")); - messagebox.setDetailedText(QString::fromStdString(MakeUnorderedList(errors))); - messagebox.setTextFormat(Qt::PlainText); - messagebox.setDefaultButton(QMessageBox::Ok); - messagebox.exec(); + QMessageBox messagebox(QMessageBox::Critical, PACKAGE_NAME, QString::fromStdString(strprintf("%s.", error.translated)), QMessageBox::Reset | QMessageBox::Abort); + /*: Explanatory text shown on startup when the settings file cannot be read. + Prompts user to make a choice between resetting or aborting. */ + messagebox.setInformativeText(QObject::tr("Do you want to reset settings to default values, or to abort without making changes?")); + messagebox.setDetailedText(QString::fromStdString(MakeUnorderedList(details))); + messagebox.setTextFormat(Qt::PlainText); + messagebox.setDefaultButton(QMessageBox::Reset); + switch (messagebox.exec()) { + case QMessageBox::Reset: return false; + case QMessageBox::Abort: + return true; + default: + assert(false); } - return true; +} + +static void ErrorSettingsWrite(const bilingual_str& error, const std::vector<std::string>& details) +{ + QMessageBox messagebox(QMessageBox::Critical, PACKAGE_NAME, QString::fromStdString(strprintf("%s.", error.translated)), QMessageBox::Ok); + /*: Explanatory text shown on startup when the settings file could not be written. + Prompts user to check that we have the ability to write to the file. + Explains that the user has the option of running without a settings file.*/ + messagebox.setInformativeText(QObject::tr("A fatal error occurred. Check that settings file is writable, or try running with -nosettings.")); + messagebox.setDetailedText(QString::fromStdString(MakeUnorderedList(details))); + messagebox.setTextFormat(Qt::PlainText); + messagebox.setDefaultButton(QMessageBox::Ok); + messagebox.exec(); } /* qDebug() message handler --> debug.log */ @@ -546,7 +530,7 @@ int GuiMain(int argc, char* argv[]) SetupUIArgs(gArgs); std::string error; if (!gArgs.ParseParameters(argc, argv, error)) { - InitError(strprintf(Untranslated("Error parsing command line arguments: %s\n"), error)); + InitError(strprintf(Untranslated("Error parsing command line arguments: %s"), error)); // Create a message box, because the gui has neither been created nor has subscribed to core signals QMessageBox::critical(nullptr, PACKAGE_NAME, // message cannot be translated because translations have not been initialized @@ -587,34 +571,23 @@ int GuiMain(int argc, char* argv[]) // Gracefully exit if the user cancels if (!Intro::showIfNeeded(did_show_intro, prune_MiB)) return EXIT_SUCCESS; - /// 6a. Determine availability of data directory - if (!CheckDataDirOption(gArgs)) { - InitError(strprintf(Untranslated("Specified data directory \"%s\" does not exist.\n"), gArgs.GetArg("-datadir", ""))); - QMessageBox::critical(nullptr, PACKAGE_NAME, - QObject::tr("Error: Specified data directory \"%1\" does not exist.").arg(QString::fromStdString(gArgs.GetArg("-datadir", "")))); - return EXIT_FAILURE; - } - try { - /// 6b. Parse bitcoin.conf - /// - Do not call gArgs.GetDataDirNet() before this step finishes - if (!gArgs.ReadConfigFiles(error, true)) { - InitError(strprintf(Untranslated("Error reading configuration file: %s\n"), error)); - QMessageBox::critical(nullptr, PACKAGE_NAME, - QObject::tr("Error: Cannot parse configuration file: %1.").arg(QString::fromStdString(error))); - return EXIT_FAILURE; + /// 6-7. Parse bitcoin.conf, determine network, switch to network specific + /// options, and create datadir and settings.json. + // - Do not call gArgs.GetDataDirNet() before this step finishes + // - Do not call Params() before this step + // - QSettings() will use the new application name after this, resulting in network-specific settings + // - Needs to be done before createOptionsModel + if (auto error = common::InitConfig(gArgs, ErrorSettingsRead)) { + InitError(error->message, error->details); + if (error->status == common::ConfigStatus::FAILED_WRITE) { + // Show a custom error message to provide more information in the + // case of a datadir write error. + ErrorSettingsWrite(error->message, error->details); + } else if (error->status != common::ConfigStatus::ABORTED) { + // Show a generic message in other cases, and no additional error + // message in the case of a read error if the user decided to abort. + QMessageBox::critical(nullptr, PACKAGE_NAME, QObject::tr("Error: %1").arg(QString::fromStdString(error->message.translated))); } - - /// 7. Determine network (and switch to network specific options) - // - Do not call Params() before this step - // - Do this after parsing the configuration file, as the network can be switched there - // - QSettings() will use the new application name after this, resulting in network-specific settings - // - Needs to be done before createOptionsModel - - // Check for chain settings (Params() calls are only valid after this clause) - SelectParams(gArgs.GetChainName()); - } catch(std::exception &e) { - InitError(Untranslated(strprintf("%s\n", e.what()))); - QMessageBox::critical(nullptr, PACKAGE_NAME, QObject::tr("Error: %1").arg(e.what())); return EXIT_FAILURE; } #ifdef ENABLE_WALLET @@ -622,10 +595,6 @@ int GuiMain(int argc, char* argv[]) PaymentServer::ipcParseCommandLine(argc, argv); #endif - if (!InitSettings()) { - return EXIT_FAILURE; - } - QScopedPointer<const NetworkStyle> networkStyle(NetworkStyle::instantiate(Params().NetworkIDString())); assert(!networkStyle.isNull()); // Allow for separate UI settings for testnets diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index c025d4e3aa..0eae7d3e79 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -456,6 +456,7 @@ void BitcoinGUI::createActions() m_wallet_controller->closeAllWallets(this); }); connect(m_mask_values_action, &QAction::toggled, this, &BitcoinGUI::setPrivacy); + connect(m_mask_values_action, &QAction::toggled, this, &BitcoinGUI::enableHistoryAction); } #endif // ENABLE_WALLET @@ -668,6 +669,12 @@ void BitcoinGUI::setClientModel(ClientModel *_clientModel, interfaces::BlockAndH } #ifdef ENABLE_WALLET +void BitcoinGUI::enableHistoryAction(bool privacy) +{ + historyAction->setEnabled(!privacy); + if (historyAction->isChecked()) gotoOverviewPage(); +} + void BitcoinGUI::setWalletController(WalletController* wallet_controller) { assert(!m_wallet_controller); @@ -716,7 +723,9 @@ void BitcoinGUI::addWallet(WalletModel* walletModel) connect(wallet_view, &WalletView::encryptionStatusChanged, this, &BitcoinGUI::updateWalletStatus); connect(wallet_view, &WalletView::incomingTransaction, this, &BitcoinGUI::incomingTransaction); connect(this, &BitcoinGUI::setPrivacy, wallet_view, &WalletView::setPrivacy); - wallet_view->setPrivacy(isPrivacyModeActivated()); + const bool privacy = isPrivacyModeActivated(); + wallet_view->setPrivacy(privacy); + enableHistoryAction(privacy); const QString display_name = walletModel->getDisplayName(); m_wallet_selector->addItem(display_name, QVariant::fromValue(walletModel)); } diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 1a83057146..92b889263b 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -288,6 +288,8 @@ public Q_SLOTS: void gotoVerifyMessageTab(QString addr = ""); /** Load Partially Signed Bitcoin Transaction from file or clipboard */ void gotoLoadPSBT(bool from_clipboard = false); + /** Enable history action when privacy is changed */ + void enableHistoryAction(bool privacy); /** Show open dialog */ void openClicked(); diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 67c5295ae3..83c78d5c18 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -20,6 +20,7 @@ #include <protocol.h> #include <script/script.h> #include <script/standard.h> +#include <util/exception.h> #include <util/system.h> #include <util/time.h> diff --git a/src/qt/initexecutor.cpp b/src/qt/initexecutor.cpp index b63e7ec01c..bb3f970b55 100644 --- a/src/qt/initexecutor.cpp +++ b/src/qt/initexecutor.cpp @@ -5,7 +5,7 @@ #include <qt/initexecutor.h> #include <interfaces/node.h> -#include <util/system.h> +#include <util/exception.h> #include <util/threadnames.h> #include <exception> diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index b46a3c039b..0e712062af 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -11,7 +11,6 @@ #include <chainparams.h> #include <interfaces/node.h> -#include <netbase.h> #include <qt/bantablemodel.h> #include <qt/clientmodel.h> #include <qt/guiutil.h> @@ -1308,17 +1307,13 @@ void RPCConsole::unbanSelectedNode() // Get selected ban addresses QList<QModelIndex> nodes = GUIUtil::getEntryData(ui->banlistWidget, BanTableModel::Address); - for(int i = 0; i < nodes.count(); i++) - { - // Get currently selected ban address - QString strNode = nodes.at(i).data().toString(); - CSubNet possibleSubnet; - - LookupSubNet(strNode.toStdString(), possibleSubnet); - if (possibleSubnet.IsValid() && m_node.unban(possibleSubnet)) - { - clientModel->getBanTableModel()->refresh(); - } + BanTableModel* ban_table_model{clientModel->getBanTableModel()}; + bool unbanned{false}; + for (const auto& node_index : nodes) { + unbanned |= ban_table_model->unban(node_index); + } + if (unbanned) { + ban_table_model->refresh(); } } diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index aa5c82ddb1..351305f3fa 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -531,6 +531,7 @@ void TransactionView::showDetails() { TransactionDescDialog *dlg = new TransactionDescDialog(selection.at(0)); dlg->setAttribute(Qt::WA_DeleteOnClose); + m_opened_dialogs.append(dlg); dlg->show(); } } @@ -637,6 +638,11 @@ bool TransactionView::eventFilter(QObject *obj, QEvent *event) return true; } } + if (event->type() == QEvent::EnabledChange) { + if (!isEnabled()) { + closeOpenedDialogs(); + } + } return QWidget::eventFilter(obj, event); } @@ -646,3 +652,12 @@ void TransactionView::updateWatchOnlyColumn(bool fHaveWatchOnly) watchOnlyWidget->setVisible(fHaveWatchOnly); transactionView->setColumnHidden(TransactionTableModel::Watchonly, !fHaveWatchOnly); } + +void TransactionView::closeOpenedDialogs() +{ + // close all dialogs opened from this view + for (QDialog* dlg : m_opened_dialogs) { + dlg->close(); + } + m_opened_dialogs.clear(); +} diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h index fe6e3ac7c0..29fb722afe 100644 --- a/src/qt/transactionview.h +++ b/src/qt/transactionview.h @@ -13,6 +13,7 @@ #include <QKeyEvent> class PlatformStyle; +class TransactionDescDialog; class TransactionFilterProxy; class WalletModel; @@ -90,6 +91,8 @@ private: const PlatformStyle* m_platform_style; + QList<TransactionDescDialog*> m_opened_dialogs; + private Q_SLOTS: void contextualMenu(const QPoint &); void dateRangeChanged(); @@ -121,6 +124,7 @@ public Q_SLOTS: void changedAmount(); void changedSearch(); void exportClicked(); + void closeOpenedDialogs(); void focusTransaction(const QModelIndex&); void focusTransaction(const uint256& txid); }; diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp index e62821d5bd..09e0771534 100644 --- a/src/qt/walletview.cpp +++ b/src/qt/walletview.cpp @@ -93,6 +93,7 @@ WalletView::WalletView(WalletModel* wallet_model, const PlatformStyle* _platform connect(transactionView, &TransactionView::message, this, &WalletView::message); connect(this, &WalletView::setPrivacy, overviewPage, &OverviewPage::setPrivacy); + connect(this, &WalletView::setPrivacy, this, &WalletView::disableTransactionView); // Receive and pass through messages from wallet model connect(walletModel, &WalletModel::message, this, &WalletView::message); @@ -278,3 +279,8 @@ void WalletView::showProgress(const QString &title, int nProgress) } } } + +void WalletView::disableTransactionView(bool disable) +{ + transactionView->setDisabled(disable); +} diff --git a/src/qt/walletview.h b/src/qt/walletview.h index ebceef9cf9..475085044d 100644 --- a/src/qt/walletview.h +++ b/src/qt/walletview.h @@ -107,6 +107,9 @@ public Q_SLOTS: /** Show progress dialog e.g. for rescan */ void showProgress(const QString &title, int nProgress); +private Q_SLOTS: + void disableTransactionView(bool disable); + Q_SIGNALS: void setPrivacy(bool privacy); void transactionClicked(); diff --git a/src/random.cpp b/src/random.cpp index 432592589a..f4c51574cc 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -221,14 +221,14 @@ static void SeedHardwareSlow(CSHA512& hasher) noexcept { } /** Use repeated SHA512 to strengthen the randomness in seed32, and feed into hasher. */ -static void Strengthen(const unsigned char (&seed)[32], int microseconds, CSHA512& hasher) noexcept +static void Strengthen(const unsigned char (&seed)[32], SteadyClock::duration dur, CSHA512& hasher) noexcept { CSHA512 inner_hasher; inner_hasher.Write(seed, sizeof(seed)); // Hash loop unsigned char buffer[64]; - int64_t stop = GetTimeMicros() + microseconds; + const auto stop{SteadyClock::now() + dur}; do { for (int i = 0; i < 1000; ++i) { inner_hasher.Finalize(buffer); @@ -238,7 +238,7 @@ static void Strengthen(const unsigned char (&seed)[32], int microseconds, CSHA51 // Benchmark operation and feed it into outer hasher. int64_t perf = GetPerformanceCounter(); hasher.Write((const unsigned char*)&perf, sizeof(perf)); - } while (GetTimeMicros() < stop); + } while (SteadyClock::now() < stop); // Produce output from inner state and feed it to outer hasher. inner_hasher.Finalize(buffer); @@ -492,13 +492,13 @@ static void SeedSlow(CSHA512& hasher, RNGState& rng) noexcept } /** Extract entropy from rng, strengthen it, and feed it into hasher. */ -static void SeedStrengthen(CSHA512& hasher, RNGState& rng, int microseconds) noexcept +static void SeedStrengthen(CSHA512& hasher, RNGState& rng, SteadyClock::duration dur) noexcept { // Generate 32 bytes of entropy from the RNG, and a copy of the entropy already in hasher. unsigned char strengthen_seed[32]; rng.MixExtract(strengthen_seed, sizeof(strengthen_seed), CSHA512(hasher), false); // Strengthen the seed, and feed it into hasher. - Strengthen(strengthen_seed, microseconds, hasher); + Strengthen(strengthen_seed, dur, hasher); } static void SeedPeriodic(CSHA512& hasher, RNGState& rng) noexcept @@ -518,7 +518,7 @@ static void SeedPeriodic(CSHA512& hasher, RNGState& rng) noexcept LogPrint(BCLog::RAND, "Feeding %i bytes of dynamic environment data into RNG\n", hasher.Size() - old_size); // Strengthen for 10 ms - SeedStrengthen(hasher, rng, 10000); + SeedStrengthen(hasher, rng, 10ms); } static void SeedStartup(CSHA512& hasher, RNGState& rng) noexcept @@ -538,7 +538,7 @@ static void SeedStartup(CSHA512& hasher, RNGState& rng) noexcept LogPrint(BCLog::RAND, "Feeding %i bytes of environment data into RNG\n", hasher.Size() - old_size); // Strengthen for 100 ms - SeedStrengthen(hasher, rng, 100000); + SeedStrengthen(hasher, rng, 100ms); } enum class RNGLevel { diff --git a/src/rest.cpp b/src/rest.cpp index a874f4eb6d..fa119ddfb7 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -652,7 +652,20 @@ static bool rest_mempool(const std::any& context, HTTPRequest* req, const std::s case RESTResponseFormat::JSON: { std::string str_json; if (param == "contents") { - str_json = MempoolToJSON(*mempool, true).write() + "\n"; + const std::string raw_verbose{req->GetQueryParameter("verbose").value_or("true")}; + if (raw_verbose != "true" && raw_verbose != "false") { + return RESTERR(req, HTTP_BAD_REQUEST, "The \"verbose\" query parameter must be either \"true\" or \"false\"."); + } + const std::string raw_mempool_sequence{req->GetQueryParameter("mempool_sequence").value_or("false")}; + if (raw_mempool_sequence != "true" && raw_mempool_sequence != "false") { + return RESTERR(req, HTTP_BAD_REQUEST, "The \"mempool_sequence\" query parameter must be either \"true\" or \"false\"."); + } + const bool verbose{raw_verbose == "true"}; + const bool mempool_sequence{raw_mempool_sequence == "true"}; + if (verbose && mempool_sequence) { + return RESTERR(req, HTTP_BAD_REQUEST, "Verbose results cannot contain mempool sequence values. (hint: set \"verbose=false\")"); + } + str_json = MempoolToJSON(*mempool, verbose, mempool_sequence).write() + "\n"; } else { str_json = MempoolInfoToJSON(*mempool).write() + "\n"; } diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 28a619fe54..1a9b265fbe 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -432,7 +432,8 @@ static RPCHelpMan getblockfrompeer() "We must have the header for this block, e.g. using submitheader.\n" "Subsequent calls for the same block and a new peer will cause the response from the previous peer to be ignored.\n" "Peers generally ignore requests for a stale block that they never fully verified, or one that is more than a month old.\n" - "When a peer does not respond with a block, we will disconnect.\n\n" + "When a peer does not respond with a block, we will disconnect.\n" + "Note: The block could be re-pruned as soon as it is received.\n\n" "Returns an empty JSON object if the request was successfully scheduled.", { {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash to try to fetch"}, @@ -1248,7 +1249,6 @@ RPCHelpMan getblockchaininfo() }, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { - const ArgsManager& args{EnsureAnyArgsman(request.context)}; ChainstateManager& chainman = EnsureAnyChainman(request.context); LOCK(cs_main); Chainstate& active_chainstate = chainman.ActiveChainstate(); @@ -1271,8 +1271,7 @@ RPCHelpMan getblockchaininfo() if (chainman.m_blockman.IsPruneMode()) { obj.pushKV("pruneheight", chainman.m_blockman.GetFirstStoredBlock(tip)->nHeight); - // if 0, execution bypasses the whole if block. - bool automatic_pruning{args.GetIntArg("-prune", 0) != 1}; + const bool automatic_pruning{chainman.m_blockman.GetPruneTarget() != BlockManager::PRUNE_TARGET_MANUAL}; obj.pushKV("automatic_pruning", automatic_pruning); if (automatic_pruning) { obj.pushKV("prune_target_size", chainman.m_blockman.GetPruneTarget()); @@ -2106,6 +2105,10 @@ static RPCHelpMan scantxoutset() " combo(<pubkey>) P2PK, P2PKH, P2WPKH, and P2SH-P2WPKH outputs for the given pubkey\n" " pkh(<pubkey>) P2PKH outputs for the given pubkey\n" " sh(multi(<n>,<pubkey>,<pubkey>,...)) P2SH-multisig outputs for the given threshold and pubkeys\n" + " tr(<pubkey>) P2TR\n" + " tr(<pubkey>,{pk(<pubkey>)}) P2TR with single fallback pubkey in tapscript\n" + " rawtr(<pubkey>) P2TR with the specified key as output key rather than inner\n" + " wsh(and_v(v:pk(<pubkey>),after(2))) P2WSH miniscript with mandatory pubkey and a timelock\n" "\nIn the above, <pubkey> either refers to a fixed public key in hexadecimal notation, or to an xpub/xprv optionally followed by one\n" "or more path elements separated by \"/\", and optionally ending in \"/*\" (unhardened), or \"/*'\" or \"/*h\" (hardened) to specify all\n" "unhardened or hardened child keys.\n" diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 5ed8aee9ea..21d49fda9d 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -216,10 +216,10 @@ static RPCHelpMan getrawtransaction() {RPCResult::Type::NUM, "fee", /*optional=*/true, "transaction fee in " + CURRENCY_UNIT + ", omitted if block undo data is not available"}, {RPCResult::Type::ARR, "vin", "", { - {RPCResult::Type::OBJ, "", "utxo being spent, omitted if block undo data is not available", + {RPCResult::Type::OBJ, "", "utxo being spent", { {RPCResult::Type::ELISION, "", "Same output as verbosity = 1"}, - {RPCResult::Type::OBJ, "prevout", /*optional=*/true, "Only if undo information is available)", + {RPCResult::Type::OBJ, "prevout", /*optional=*/true, "The previous output, omitted if block undo data is not available", { {RPCResult::Type::BOOL, "generated", "Coinbase or not"}, {RPCResult::Type::NUM, "height", "The height of the prevout"}, diff --git a/src/script/signingprovider.cpp b/src/script/signingprovider.cpp index 5123dd81ac..ef055573b9 100644 --- a/src/script/signingprovider.cpp +++ b/src/script/signingprovider.cpp @@ -7,7 +7,7 @@ #include <script/signingprovider.h> #include <script/standard.h> -#include <util/system.h> +#include <logging.h> const SigningProvider& DUMMY_SIGNING_PROVIDER = SigningProvider(); diff --git a/src/secp256k1/.cirrus.yml b/src/secp256k1/.cirrus.yml index 51e3bc9484..0b904a4e38 100644 --- a/src/secp256k1/.cirrus.yml +++ b/src/secp256k1/.cirrus.yml @@ -1,6 +1,9 @@ env: + ### cirrus config + CIRRUS_CLONE_DEPTH: 1 ### compiler options HOST: + WRAPPER_CMD: # Specific warnings can be disabled with -Wno-error=foo. # -pedantic-errors is not equivalent to -Werror=pedantic and thus not implied by -Werror according to the GCC manual. WERROR_CFLAGS: -Werror -pedantic-errors @@ -22,7 +25,7 @@ env: SECP256K1_TEST_ITERS: BENCH: yes SECP256K1_BENCH_ITERS: 2 - CTIMETEST: yes + CTIMETESTS: yes # Compile and run the tests EXAMPLES: yes @@ -35,10 +38,12 @@ cat_logs_snippet: &CAT_LOGS always: cat_tests_log_script: - cat tests.log || true + cat_noverify_tests_log_script: + - cat noverify_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_ctime_tests_log_script: + - cat ctime_tests.log || true cat_bench_log_script: - cat bench.log || true cat_config_log_script: @@ -51,10 +56,8 @@ cat_logs_snippet: &CAT_LOGS 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 + - git fetch --depth=1 $CIRRUS_REPO_CLONE_URL "pull/${CIRRUS_PR}/merge" + - git checkout FETCH_HEAD # Use merged changes to detect silent merge conflicts linux_container_snippet: &LINUX_CONTAINER container: @@ -78,9 +81,10 @@ task: - env: {WIDEMUL: int128, ECDH: yes, SCHNORRSIG: yes} - env: {WIDEMUL: int128, ASM: x86_64} - env: { RECOVERY: yes, SCHNORRSIG: yes} - - env: {BUILD: distcheck, WITH_VALGRIND: no, CTIMETEST: no, BENCH: no} + - env: {CTIMETESTS: no, RECOVERY: yes, ECDH: yes, SCHNORRSIG: yes, CPPFLAGS: -DVERIFY} + - env: {BUILD: distcheck, WITH_VALGRIND: no, CTIMETESTS: no, BENCH: no} - env: {CPPFLAGS: -DDETERMINISTIC} - - env: {CFLAGS: -O0, CTIMETEST: no} + - env: {CFLAGS: -O0, CTIMETESTS: no} - env: { ECMULTGENPRECISION: 2, ECMULTWINDOW: 2 } - env: { ECMULTGENPRECISION: 8, ECMULTWINDOW: 4 } matrix: @@ -125,7 +129,7 @@ task: env: ASM: no WITH_VALGRIND: no - CTIMETEST: no + CTIMETESTS: no matrix: - env: CC: gcc @@ -150,7 +154,7 @@ task: ECDH: yes RECOVERY: yes SCHNORRSIG: yes - CTIMETEST: no + CTIMETESTS: no << : *MERGE_BASE test_script: # https://sourceware.org/bugzilla/show_bug.cgi?id=27008 @@ -169,7 +173,7 @@ task: ECDH: yes RECOVERY: yes SCHNORRSIG: yes - CTIMETEST: no + CTIMETESTS: no matrix: - env: {} - env: {EXPERIMENTAL: yes, ASM: arm} @@ -189,7 +193,7 @@ task: ECDH: yes RECOVERY: yes SCHNORRSIG: yes - CTIMETEST: no + CTIMETESTS: no << : *MERGE_BASE test_script: - ./ci/cirrus.sh @@ -206,7 +210,7 @@ task: ECDH: yes RECOVERY: yes SCHNORRSIG: yes - CTIMETEST: no + CTIMETESTS: no << : *MERGE_BASE test_script: - ./ci/cirrus.sh @@ -220,7 +224,7 @@ task: ECDH: yes RECOVERY: yes SCHNORRSIG: yes - CTIMETEST: no + CTIMETESTS: no matrix: - name: "x86_64 (mingw32-w64): Windows (Debian stable, Wine)" env: @@ -243,7 +247,7 @@ task: RECOVERY: yes EXPERIMENTAL: yes SCHNORRSIG: yes - CTIMETEST: no + CTIMETESTS: no # Use a MinGW-w64 host to tell ./configure we're building for Windows. # This will detect some MinGW-w64 tools but then make will need only # the MSVC tools CC, AR and NM as specified below. @@ -254,7 +258,7 @@ task: # Set non-essential options that affect the CLI messages here. # (They depend on the user's taste, so we don't want to set them automatically in configure.ac.) CFLAGS: -nologo -diagnostics:caret - LDFLAGS: -XCClinker -nologo -XCClinker -diagnostics:caret + LDFLAGS: -Xlinker -Xlinker -Xlinker -nologo matrix: - name: "x86_64 (MSVC): Windows (Debian stable, Wine)" - name: "x86_64 (MSVC): Windows (Debian stable, Wine, int128_struct)" @@ -282,7 +286,7 @@ task: ECDH: yes RECOVERY: yes SCHNORRSIG: yes - CTIMETEST: no + CTIMETESTS: no matrix: - name: "Valgrind (memcheck)" container: @@ -327,10 +331,11 @@ task: ECDH: yes RECOVERY: yes SCHNORRSIG: yes - CTIMETEST: no + CTIMETESTS: yes CC: clang SECP256K1_TEST_ITERS: 32 ASM: no + WITH_VALGRIND: no container: memory: 2G matrix: @@ -375,3 +380,30 @@ task: test_script: - cd sage - sage prove_group_implementations.sage + +task: + name: "x86_64: Windows (VS 2022)" + windows_container: + image: cirrusci/windowsservercore:visualstudio2022 + cpu: 4 + memory: 3840MB + env: + PATH: '%CIRRUS_WORKING_DIR%\build\src\RelWithDebInfo;%PATH%' + x64_NATIVE_TOOLS: '"C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Auxiliary\Build\vcvars64.bat"' + # Ignore MSBuild warning MSB8029. + # See: https://learn.microsoft.com/en-us/visualstudio/msbuild/errors/msb8029?view=vs-2022 + IgnoreWarnIntDirInTempDetected: 'true' + merge_script: + - PowerShell -NoLogo -Command if ($env:CIRRUS_PR -ne $null) { git fetch $env:CIRRUS_REPO_CLONE_URL pull/$env:CIRRUS_PR/merge; git reset --hard FETCH_HEAD; } + configure_script: + - '%x64_NATIVE_TOOLS%' + - cmake -G "Visual Studio 17 2022" -A x64 -S . -B build -DSECP256K1_ENABLE_MODULE_RECOVERY=ON -DSECP256K1_BUILD_EXAMPLES=ON + build_script: + - '%x64_NATIVE_TOOLS%' + - cmake --build build --config RelWithDebInfo -- -property:UseMultiToolTask=true;CL_MPcount=5 + check_script: + - '%x64_NATIVE_TOOLS%' + - ctest --test-dir build -j 5 + - build\src\RelWithDebInfo\bench_ecmult.exe + - build\src\RelWithDebInfo\bench_internal.exe + - build\src\RelWithDebInfo\bench.exe diff --git a/src/secp256k1/.gitignore b/src/secp256k1/.gitignore index 80c646b771..bc7e499de7 100644 --- a/src/secp256k1/.gitignore +++ b/src/secp256k1/.gitignore @@ -1,11 +1,12 @@ bench bench_ecmult bench_internal +noverify_tests tests exhaustive_tests precompute_ecmult_gen precompute_ecmult -valgrind_ctime_test +ctime_tests ecdh_example ecdsa_example schnorr_example @@ -42,8 +43,6 @@ coverage.*.html *.gcno *.gcov -src/libsecp256k1-config.h -src/libsecp256k1-config.h.in build-aux/ar-lib build-aux/config.guess build-aux/config.sub @@ -58,5 +57,7 @@ build-aux/m4/ltversion.m4 build-aux/missing build-aux/compile build-aux/test-driver -src/stamp-h1 libsecp256k1.pc + +# Default CMake build directory. +/build diff --git a/src/secp256k1/CHANGELOG.md b/src/secp256k1/CHANGELOG.md index 7443483423..7f43843641 100644 --- a/src/secp256k1/CHANGELOG.md +++ b/src/secp256k1/CHANGELOG.md @@ -1,28 +1,61 @@ # Changelog -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +## [0.3.0] - 2023-03-08 + +#### Added + - Added experimental support for CMake builds. Traditional GNU Autotools builds (`./configure` and `make`) remain fully supported. + - Usage examples: Added a recommended method for securely clearing sensitive data, e.g., secret keys, from memory. + - Tests: Added a new test binary `noverify_tests`. This binary runs the tests without some additional checks present in the ordinary `tests` binary and is thereby closer to production binaries. The `noverify_tests` binary is automatically run as part of the `make check` target. + +#### Fixed + - Fixed declarations of API variables for MSVC (`__declspec(dllimport)`). This fixes MSVC builds of programs which link against a libsecp256k1 DLL dynamically and use API variables (and not only API functions). Unfortunately, the MSVC linker now will emit warning `LNK4217` when trying to link against libsecp256k1 statically. Pass `/ignore:4217` to the linker to suppress this warning. + +#### Changed + - Forbade cloning or destroying `secp256k1_context_static`. Create a new context instead of cloning the static context. (If this change breaks your code, your code is probably wrong.) + - Forbade randomizing (copies of) `secp256k1_context_static`. Randomizing a copy of `secp256k1_context_static` did not have any effect and did not provide defense-in-depth protection against side-channel attacks. Create a new context if you want to benefit from randomization. + +#### Removed + - Removed the configuration header `src/libsecp256k1-config.h`. We recommend passing flags to `./configure` or `cmake` to set configuration options (see `./configure --help` or `cmake -LH`). If you cannot or do not want to use one of the supported build systems, pass configuration flags such as `-DSECP256K1_ENABLE_MODULE_SCHNORRSIG` manually to the compiler (see the file `configure.ac` for supported flags). + +#### ABI Compatibility + +Due to changes in the API regarding `secp256k1_context_static` described above, the ABI is *not* compatible with previous versions. + ## [0.2.0] - 2022-12-12 -### Added +#### Added + - Added usage examples for common use cases in a new `examples/` directory. - Added `secp256k1_selftest`, to be used in conjunction with `secp256k1_context_static`. + - Added support for 128-bit wide multiplication on MSVC for x86_64 and arm64, giving roughly a 20% speedup on those platforms. -### Changed - - Enabled modules schnorrsig, extrakeys and ECDH by default in `./configure`. +#### Changed + - Enabled modules `schnorrsig`, `extrakeys` and `ecdh` by default in `./configure`. + - The `secp256k1_nonce_function_rfc6979` nonce function, used by default by `secp256k1_ecdsa_sign`, now reduces the message hash modulo the group order to match the specification. This only affects improper use of ECDSA signing API. -### Deprecated +#### Deprecated - Deprecated context flags `SECP256K1_CONTEXT_VERIFY` and `SECP256K1_CONTEXT_SIGN`. Use `SECP256K1_CONTEXT_NONE` instead. - Renamed `secp256k1_context_no_precomp` to `secp256k1_context_static`. + - Module `schnorrsig`: renamed `secp256k1_schnorrsig_sign` to `secp256k1_schnorrsig_sign32`. -### ABI Compatibility +#### ABI Compatibility Since this is the first release, we do not compare application binary interfaces. -However, there are unreleased versions of libsecp256k1 that are *not* ABI compatible with this version. +However, there are earlier unreleased versions of libsecp256k1 that are *not* ABI compatible with this version. ## [0.1.0] - 2013-03-05 to 2021-12-25 This version was in fact never released. The number was given by the build system since the introduction of autotools in Jan 2014 (ea0fe5a5bf0c04f9cc955b2966b614f5f378c6f6). Therefore, this version number does not uniquely identify a set of source files. + +[unreleased]: https://github.com/bitcoin-core/secp256k1/compare/v0.3.0...HEAD +[0.3.0]: https://github.com/bitcoin-core/secp256k1/compare/v0.2.0...v0.3.0 +[0.2.0]: https://github.com/bitcoin-core/secp256k1/compare/423b6d19d373f1224fd671a982584d7e7900bc93..v0.2.0 +[0.1.0]: https://github.com/bitcoin-core/secp256k1/commit/423b6d19d373f1224fd671a982584d7e7900bc93 diff --git a/src/secp256k1/CMakeLists.txt b/src/secp256k1/CMakeLists.txt new file mode 100644 index 0000000000..5c8aad6fcc --- /dev/null +++ b/src/secp256k1/CMakeLists.txt @@ -0,0 +1,302 @@ +cmake_minimum_required(VERSION 3.1) + +if(CMAKE_VERSION VERSION_GREATER 3.14) + # MSVC runtime library flags are selected by the CMAKE_MSVC_RUNTIME_LIBRARY abstraction. + cmake_policy(SET CMP0091 NEW) + # MSVC warning flags are not in CMAKE_<LANG>_FLAGS by default. + cmake_policy(SET CMP0092 NEW) +endif() + +# The package (a.k.a. release) version is based on semantic versioning 2.0.0 of +# the API. All changes in experimental modules are treated as +# backwards-compatible and therefore at most increase the minor version. +project(libsecp256k1 VERSION 0.3.0 LANGUAGES C) + +# The library version is based on libtool versioning of the ABI. The set of +# rules for updating the version can be found here: +# https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html +# All changes in experimental modules are treated as if they don't affect the +# interface and therefore only increase the revision. +set(${PROJECT_NAME}_LIB_VERSION_CURRENT 2) +set(${PROJECT_NAME}_LIB_VERSION_REVISION 0) +set(${PROJECT_NAME}_LIB_VERSION_AGE 0) + +set(CMAKE_C_STANDARD 90) +set(CMAKE_C_EXTENSIONS OFF) + +list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) + +# We do not use CMake's BUILD_SHARED_LIBS option. +option(SECP256K1_BUILD_SHARED "Build shared library." ON) +option(SECP256K1_BUILD_STATIC "Build static library." ON) +if(NOT SECP256K1_BUILD_SHARED AND NOT SECP256K1_BUILD_STATIC) + message(FATAL_ERROR "At least one of SECP256K1_BUILD_SHARED and SECP256K1_BUILD_STATIC must be enabled.") +endif() + +option(SECP256K1_ENABLE_MODULE_ECDH "Enable ECDH module." ON) +if(SECP256K1_ENABLE_MODULE_ECDH) + add_definitions(-DENABLE_MODULE_ECDH=1) +endif() + +option(SECP256K1_ENABLE_MODULE_RECOVERY "Enable ECDSA pubkey recovery module." OFF) +if(SECP256K1_ENABLE_MODULE_RECOVERY) + add_definitions(-DENABLE_MODULE_RECOVERY=1) +endif() + +option(SECP256K1_ENABLE_MODULE_EXTRAKEYS "Enable extrakeys module." ON) +option(SECP256K1_ENABLE_MODULE_SCHNORRSIG "Enable schnorrsig module." ON) +if(SECP256K1_ENABLE_MODULE_SCHNORRSIG) + set(SECP256K1_ENABLE_MODULE_EXTRAKEYS ON) + add_definitions(-DENABLE_MODULE_SCHNORRSIG=1) +endif() +if(SECP256K1_ENABLE_MODULE_EXTRAKEYS) + add_definitions(-DENABLE_MODULE_EXTRAKEYS=1) +endif() + +option(SECP256K1_USE_EXTERNAL_DEFAULT_CALLBACKS "Enable external default callback functions." OFF) +if(SECP256K1_USE_EXTERNAL_DEFAULT_CALLBACKS) + add_definitions(-DUSE_EXTERNAL_DEFAULT_CALLBACKS=1) +endif() + +set(SECP256K1_ECMULT_WINDOW_SIZE "AUTO" CACHE STRING "Window size for ecmult precomputation for verification, specified as integer in range [2..24]. \"AUTO\" is a reasonable setting for desktop machines (currently 15). [default=AUTO]") +set_property(CACHE SECP256K1_ECMULT_WINDOW_SIZE PROPERTY STRINGS "AUTO" 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24) +include(CheckStringOptionValue) +check_string_option_value(SECP256K1_ECMULT_WINDOW_SIZE) +if(SECP256K1_ECMULT_WINDOW_SIZE STREQUAL "AUTO") + set(SECP256K1_ECMULT_WINDOW_SIZE 15) +endif() +add_definitions(-DECMULT_WINDOW_SIZE=${SECP256K1_ECMULT_WINDOW_SIZE}) + +set(SECP256K1_ECMULT_GEN_PREC_BITS "AUTO" CACHE STRING "Precision bits to tune the precomputed table size for signing, specified as integer 2, 4 or 8. \"AUTO\" is a reasonable setting for desktop machines (currently 4). [default=AUTO]") +set_property(CACHE SECP256K1_ECMULT_GEN_PREC_BITS PROPERTY STRINGS "AUTO" 2 4 8) +check_string_option_value(SECP256K1_ECMULT_GEN_PREC_BITS) +if(SECP256K1_ECMULT_GEN_PREC_BITS STREQUAL "AUTO") + set(SECP256K1_ECMULT_GEN_PREC_BITS 4) +endif() +add_definitions(-DECMULT_GEN_PREC_BITS=${SECP256K1_ECMULT_GEN_PREC_BITS}) + +set(SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY "OFF" CACHE STRING "Test-only override of the (autodetected by the C code) \"widemul\" setting. Legal values are: \"OFF\", \"int128_struct\", \"int128\" or \"int64\". [default=OFF]") +set_property(CACHE SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY PROPERTY STRINGS "OFF" "int128_struct" "int128" "int64") +check_string_option_value(SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY) +if(SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY) + string(TOUPPER "${SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY}" widemul_upper_value) + add_definitions(-DUSE_FORCE_WIDEMUL_${widemul_upper_value}=1) +endif() +mark_as_advanced(FORCE SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY) + +set(SECP256K1_ASM "AUTO" CACHE STRING "Assembly optimizations to use: \"AUTO\", \"OFF\", \"x86_64\" or \"arm\" (experimental). [default=AUTO]") +set_property(CACHE SECP256K1_ASM PROPERTY STRINGS "AUTO" "OFF" "x86_64" "arm") +check_string_option_value(SECP256K1_ASM) +if(SECP256K1_ASM STREQUAL "arm") + enable_language(ASM) + add_definitions(-DUSE_EXTERNAL_ASM=1) +elseif(SECP256K1_ASM) + include(Check64bitAssembly) + check_64bit_assembly() + if(HAS_64BIT_ASM) + set(SECP256K1_ASM "x86_64") + add_definitions(-DUSE_ASM_X86_64=1) + elseif(SECP256K1_ASM STREQUAL "AUTO") + set(SECP256K1_ASM "OFF") + else() + message(FATAL_ERROR "x86_64 assembly optimization requested but not available.") + endif() +endif() + +option(SECP256K1_EXPERIMENTAL "Allow experimental configuration options." OFF) +if(NOT SECP256K1_EXPERIMENTAL) + if(SECP256K1_ASM STREQUAL "arm") + message(FATAL_ERROR "ARM assembly optimization is experimental. Use -DSECP256K1_EXPERIMENTAL=ON to allow.") + endif() +endif() + +set(SECP256K1_VALGRIND "AUTO" CACHE STRING "Build with extra checks for running inside Valgrind. [default=AUTO]") +set_property(CACHE SECP256K1_VALGRIND PROPERTY STRINGS "AUTO" "OFF" "ON") +check_string_option_value(SECP256K1_VALGRIND) +if(SECP256K1_VALGRIND) + find_package(Valgrind MODULE) + if(Valgrind_FOUND) + set(SECP256K1_VALGRIND ON) + include_directories(${Valgrind_INCLUDE_DIR}) + add_definitions(-DVALGRIND) + elseif(SECP256K1_VALGRIND STREQUAL "AUTO") + set(SECP256K1_VALGRIND OFF) + else() + message(FATAL_ERROR "Valgrind support requested but valgrind/memcheck.h header not available.") + endif() +endif() + +option(SECP256K1_BUILD_BENCHMARK "Build benchmarks." ON) +option(SECP256K1_BUILD_TESTS "Build tests." ON) +option(SECP256K1_BUILD_EXHAUSTIVE_TESTS "Build exhaustive tests." ON) +option(SECP256K1_BUILD_CTIME_TESTS "Build constant-time tests." ${SECP256K1_VALGRIND}) +option(SECP256K1_BUILD_EXAMPLES "Build examples." OFF) + +# Redefine configuration flags. +# We leave assertions on, because they are only used in the examples, and we want them always on there. +if(MSVC) + string(REGEX REPLACE "/DNDEBUG[ \t\r\n]*" "" CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}") + string(REGEX REPLACE "/DNDEBUG[ \t\r\n]*" "" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}") + string(REGEX REPLACE "/DNDEBUG[ \t\r\n]*" "" CMAKE_C_FLAGS_MINSIZEREL "${CMAKE_C_FLAGS_MINSIZEREL}") +else() + string(REGEX REPLACE "-DNDEBUG[ \t\r\n]*" "" CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}") + string(REGEX REPLACE "-DNDEBUG[ \t\r\n]*" "" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}") + string(REGEX REPLACE "-DNDEBUG[ \t\r\n]*" "" CMAKE_C_FLAGS_MINSIZEREL "${CMAKE_C_FLAGS_MINSIZEREL}") + # Prefer -O2 optimization level. (-O3 is CMake's default for Release for many compilers.) + string(REGEX REPLACE "-O3[ \t\r\n]*" "-O2" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}") +endif() + +# Define custom "Coverage" build type. +set(CMAKE_C_FLAGS_COVERAGE "${CMAKE_C_FLAGS_RELWITHDEBINFO} -O0 -DCOVERAGE=1 --coverage -Wno-unused-parameter" CACHE STRING + "Flags used by the C compiler during \"Coverage\" builds." + FORCE +) +set(CMAKE_EXE_LINKER_FLAGS_COVERAGE "${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO} --coverage" CACHE STRING + "Flags used for linking binaries during \"Coverage\" builds." + FORCE +) +set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE "${CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO} --coverage" CACHE STRING + "Flags used by the shared libraries linker during \"Coverage\" builds." + FORCE +) +mark_as_advanced( + CMAKE_C_FLAGS_COVERAGE + CMAKE_EXE_LINKER_FLAGS_COVERAGE + CMAKE_SHARED_LINKER_FLAGS_COVERAGE +) + +if(CMAKE_CONFIGURATION_TYPES) + set(CMAKE_CONFIGURATION_TYPES "RelWithDebInfo" "Release" "Debug" "MinSizeRel" "Coverage") +endif() + +get_property(cached_cmake_build_type CACHE CMAKE_BUILD_TYPE PROPERTY TYPE) +if(cached_cmake_build_type) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY + STRINGS "RelWithDebInfo" "Release" "Debug" "MinSizeRel" "Coverage" + ) +endif() + +set(default_build_type "RelWithDebInfo") +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + message(STATUS "Setting build type to \"${default_build_type}\" as none was specified") + set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE STRING "Choose the type of build." FORCE) +endif() + +include(TryAddCompileOption) +if(MSVC) + try_add_compile_option(/W2) + try_add_compile_option(/wd4146) +else() + try_add_compile_option(-pedantic) + try_add_compile_option(-Wall) + try_add_compile_option(-Wcast-align) + try_add_compile_option(-Wcast-align=strict) + try_add_compile_option(-Wconditional-uninitialized) + try_add_compile_option(-Wextra) + try_add_compile_option(-Wnested-externs) + try_add_compile_option(-Wno-long-long) + try_add_compile_option(-Wno-overlength-strings) + try_add_compile_option(-Wno-unused-function) + try_add_compile_option(-Wreserved-identifier) + try_add_compile_option(-Wshadow) + try_add_compile_option(-Wstrict-prototypes) + try_add_compile_option(-Wundef) +endif() + +if(CMAKE_VERSION VERSION_GREATER 3.2) + # Honor visibility properties for all target types. + # See: https://cmake.org/cmake/help/latest/policy/CMP0063.html + cmake_policy(SET CMP0063 NEW) +endif() +set(CMAKE_C_VISIBILITY_PRESET hidden) + +# Ask CTest to create a "check" target (e.g., make check) as alias for the "test" target. +# CTEST_TEST_TARGET_ALIAS is not documented but supposed to be user-facing. +# See: https://gitlab.kitware.com/cmake/cmake/-/commit/816c9d1aa1f2b42d40c81a991b68c96eb12b6d2 +set(CTEST_TEST_TARGET_ALIAS check) +include(CTest) +# We do not use CTest's BUILD_TESTING because a single toggle for all tests is too coarse for our needs. +mark_as_advanced(BUILD_TESTING) +if(SECP256K1_BUILD_BENCHMARK OR SECP256K1_BUILD_TESTS OR SECP256K1_BUILD_EXHAUSTIVE_TESTS OR SECP256K1_BUILD_CTIME_TESTS OR SECP256K1_BUILD_EXAMPLES) + enable_testing() +endif() + +add_subdirectory(src) +if(SECP256K1_BUILD_EXAMPLES) + add_subdirectory(examples) +endif() + +message("\n") +message("secp256k1 configure summary") +message("===========================") +message("Build artifacts:") +message(" shared library ...................... ${SECP256K1_BUILD_SHARED}") +message(" static library ...................... ${SECP256K1_BUILD_STATIC}") +message("Optional modules:") +message(" ECDH ................................ ${SECP256K1_ENABLE_MODULE_ECDH}") +message(" ECDSA pubkey recovery ............... ${SECP256K1_ENABLE_MODULE_RECOVERY}") +message(" extrakeys ........................... ${SECP256K1_ENABLE_MODULE_EXTRAKEYS}") +message(" schnorrsig .......................... ${SECP256K1_ENABLE_MODULE_SCHNORRSIG}") +message("Parameters:") +message(" ecmult window size .................. ${SECP256K1_ECMULT_WINDOW_SIZE}") +message(" ecmult gen precision bits ........... ${SECP256K1_ECMULT_GEN_PREC_BITS}") +message("Optional features:") +message(" assembly optimization ............... ${SECP256K1_ASM}") +message(" external callbacks .................. ${SECP256K1_USE_EXTERNAL_DEFAULT_CALLBACKS}") +if(SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY) + message(" wide multiplication (test-only) ..... ${SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY}") +endif() +message("Optional binaries:") +message(" benchmark ........................... ${SECP256K1_BUILD_BENCHMARK}") +message(" noverify_tests ...................... ${SECP256K1_BUILD_TESTS}") +set(tests_status "${SECP256K1_BUILD_TESTS}") +if(CMAKE_BUILD_TYPE STREQUAL "Coverage") + set(tests_status OFF) +endif() +message(" tests ............................... ${tests_status}") +message(" exhaustive tests .................... ${SECP256K1_BUILD_EXHAUSTIVE_TESTS}") +message(" ctime_tests ......................... ${SECP256K1_BUILD_CTIME_TESTS}") +message(" examples ............................ ${SECP256K1_BUILD_EXAMPLES}") +message("") +if(CMAKE_CROSSCOMPILING) + set(cross_status "TRUE, for ${CMAKE_SYSTEM_NAME}, ${CMAKE_SYSTEM_PROCESSOR}") +else() + set(cross_status "FALSE") +endif() +message("Cross compiling ....................... ${cross_status}") +message("Valgrind .............................. ${SECP256K1_VALGRIND}") +get_directory_property(definitions COMPILE_DEFINITIONS) +string(REPLACE ";" " " definitions "${definitions}") +message("Preprocessor defined macros ........... ${definitions}") +message("C compiler ............................ ${CMAKE_C_COMPILER}") +message("CFLAGS ................................ ${CMAKE_C_FLAGS}") +get_directory_property(compile_options COMPILE_OPTIONS) +string(REPLACE ";" " " compile_options "${compile_options}") +message("Compile options ....................... " ${compile_options}) +if(DEFINED CMAKE_BUILD_TYPE) + message("Build type:") + message(" - CMAKE_BUILD_TYPE ................... ${CMAKE_BUILD_TYPE}") + string(TOUPPER "${CMAKE_BUILD_TYPE}" build_type) + message(" - CFLAGS ............................. ${CMAKE_C_FLAGS_${build_type}}") + message(" - LDFLAGS for executables ............ ${CMAKE_EXE_LINKER_FLAGS_${build_type}}") + message(" - LDFLAGS for shared libraries ....... ${CMAKE_SHARED_LINKER_FLAGS_${build_type}}") +else() + message("Available configurations .............. ${CMAKE_CONFIGURATION_TYPES}") + message("RelWithDebInfo configuration:") + message(" - CFLAGS ............................. ${CMAKE_C_FLAGS_RELWITHDEBINFO}") + message(" - LDFLAGS for executables ............ ${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO}") + message(" - LDFLAGS for shared libraries ....... ${CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO}") + message("Debug configuration:") + message(" - CFLAGS ............................. ${CMAKE_C_FLAGS_DEBUG}") + message(" - LDFLAGS for executables ............ ${CMAKE_EXE_LINKER_FLAGS_DEBUG}") + message(" - LDFLAGS for shared libraries ....... ${CMAKE_SHARED_LINKER_FLAGS_DEBUG}") +endif() +message("\n") +if(SECP256K1_EXPERIMENTAL) + message( + " ******\n" + " WARNING: experimental build\n" + " Experimental features do not have stable APIs or properties, and may not be safe for production use.\n" + " ******\n" + ) +endif() diff --git a/src/secp256k1/Makefile.am b/src/secp256k1/Makefile.am index ad50504f7e..e3fdf4da27 100644 --- a/src/secp256k1/Makefile.am +++ b/src/secp256k1/Makefile.am @@ -47,6 +47,7 @@ noinst_HEADERS += src/modinv64_impl.h noinst_HEADERS += src/precomputed_ecmult.h noinst_HEADERS += src/precomputed_ecmult_gen.h noinst_HEADERS += src/assumptions.h +noinst_HEADERS += src/checkmem.h noinst_HEADERS += src/util.h noinst_HEADERS += src/int128.h noinst_HEADERS += src/int128_impl.h @@ -68,12 +69,14 @@ noinst_HEADERS += contrib/lax_der_parsing.h noinst_HEADERS += contrib/lax_der_parsing.c noinst_HEADERS += contrib/lax_der_privatekey_parsing.h noinst_HEADERS += contrib/lax_der_privatekey_parsing.c -noinst_HEADERS += examples/random.h +noinst_HEADERS += examples/examples_util.h PRECOMPUTED_LIB = libsecp256k1_precomputed.la noinst_LTLIBRARIES = $(PRECOMPUTED_LIB) libsecp256k1_precomputed_la_SOURCES = src/precomputed_ecmult.c src/precomputed_ecmult_gen.c -libsecp256k1_precomputed_la_CPPFLAGS = $(SECP_INCLUDES) +# We need `-I$(top_srcdir)/src` in VPATH builds if libsecp256k1_precomputed_la_SOURCES have been recreated in the build tree. +# This helps users and packagers who insist on recreating the precomputed files (e.g., Gentoo). +libsecp256k1_precomputed_la_CPPFLAGS = -I$(top_srcdir)/src $(SECP_CONFIG_DEFINES) if USE_EXTERNAL_ASM COMMON_LIB = libsecp256k1_common.la @@ -92,55 +95,58 @@ endif endif libsecp256k1_la_SOURCES = src/secp256k1.c -libsecp256k1_la_CPPFLAGS = $(SECP_INCLUDES) -libsecp256k1_la_LIBADD = $(SECP_LIBS) $(COMMON_LIB) $(PRECOMPUTED_LIB) +libsecp256k1_la_CPPFLAGS = $(SECP_CONFIG_DEFINES) +libsecp256k1_la_LIBADD = $(COMMON_LIB) $(PRECOMPUTED_LIB) libsecp256k1_la_LDFLAGS = -no-undefined -version-info $(LIB_VERSION_CURRENT):$(LIB_VERSION_REVISION):$(LIB_VERSION_AGE) -if VALGRIND_ENABLED -libsecp256k1_la_CPPFLAGS += -DVALGRIND -endif - noinst_PROGRAMS = if USE_BENCHMARK noinst_PROGRAMS += bench bench_internal bench_ecmult bench_SOURCES = src/bench.c -bench_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB) +bench_LDADD = libsecp256k1.la +bench_CPPFLAGS = $(SECP_CONFIG_DEFINES) bench_internal_SOURCES = src/bench_internal.c -bench_internal_LDADD = $(SECP_LIBS) $(COMMON_LIB) $(PRECOMPUTED_LIB) -bench_internal_CPPFLAGS = $(SECP_INCLUDES) +bench_internal_LDADD = $(COMMON_LIB) $(PRECOMPUTED_LIB) +bench_internal_CPPFLAGS = $(SECP_CONFIG_DEFINES) bench_ecmult_SOURCES = src/bench_ecmult.c -bench_ecmult_LDADD = $(SECP_LIBS) $(COMMON_LIB) $(PRECOMPUTED_LIB) -bench_ecmult_CPPFLAGS = $(SECP_INCLUDES) +bench_ecmult_LDADD = $(COMMON_LIB) $(PRECOMPUTED_LIB) +bench_ecmult_CPPFLAGS = $(SECP_CONFIG_DEFINES) endif TESTS = if USE_TESTS +TESTS += noverify_tests +noinst_PROGRAMS += noverify_tests +noverify_tests_SOURCES = src/tests.c +noverify_tests_CPPFLAGS = $(SECP_CONFIG_DEFINES) +noverify_tests_LDADD = $(COMMON_LIB) $(PRECOMPUTED_LIB) +noverify_tests_LDFLAGS = -static +if !ENABLE_COVERAGE +TESTS += tests noinst_PROGRAMS += tests -tests_SOURCES = src/tests.c -tests_CPPFLAGS = $(SECP_INCLUDES) $(SECP_TEST_INCLUDES) -if VALGRIND_ENABLED -tests_CPPFLAGS += -DVALGRIND -noinst_PROGRAMS += valgrind_ctime_test -valgrind_ctime_test_SOURCES = src/valgrind_ctime_test.c -valgrind_ctime_test_LDADD = libsecp256k1.la $(SECP_LIBS) $(COMMON_LIB) +tests_SOURCES = $(noverify_tests_SOURCES) +tests_CPPFLAGS = $(noverify_tests_CPPFLAGS) -DVERIFY +tests_LDADD = $(noverify_tests_LDADD) +tests_LDFLAGS = $(noverify_tests_LDFLAGS) endif -if !ENABLE_COVERAGE -tests_CPPFLAGS += -DVERIFY endif -tests_LDADD = $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB) $(PRECOMPUTED_LIB) -tests_LDFLAGS = -static -TESTS += tests + +if USE_CTIME_TESTS +noinst_PROGRAMS += ctime_tests +ctime_tests_SOURCES = src/ctime_tests.c +ctime_tests_LDADD = libsecp256k1.la +ctime_tests_CPPFLAGS = $(SECP_CONFIG_DEFINES) endif if USE_EXHAUSTIVE_TESTS noinst_PROGRAMS += exhaustive_tests exhaustive_tests_SOURCES = src/tests_exhaustive.c -exhaustive_tests_CPPFLAGS = $(SECP_INCLUDES) +exhaustive_tests_CPPFLAGS = $(SECP_CONFIG_DEFINES) if !ENABLE_COVERAGE exhaustive_tests_CPPFLAGS += -DVERIFY endif # Note: do not include $(PRECOMPUTED_LIB) in exhaustive_tests (it uses runtime-generated tables). -exhaustive_tests_LDADD = $(SECP_LIBS) $(COMMON_LIB) +exhaustive_tests_LDADD = $(COMMON_LIB) exhaustive_tests_LDFLAGS = -static TESTS += exhaustive_tests endif @@ -184,12 +190,12 @@ EXTRA_PROGRAMS = precompute_ecmult precompute_ecmult_gen CLEANFILES = $(EXTRA_PROGRAMS) precompute_ecmult_SOURCES = src/precompute_ecmult.c -precompute_ecmult_CPPFLAGS = $(SECP_INCLUDES) -precompute_ecmult_LDADD = $(SECP_LIBS) $(COMMON_LIB) +precompute_ecmult_CPPFLAGS = $(SECP_CONFIG_DEFINES) +precompute_ecmult_LDADD = $(COMMON_LIB) precompute_ecmult_gen_SOURCES = src/precompute_ecmult_gen.c -precompute_ecmult_gen_CPPFLAGS = $(SECP_INCLUDES) -precompute_ecmult_gen_LDADD = $(SECP_LIBS) $(COMMON_LIB) +precompute_ecmult_gen_CPPFLAGS = $(SECP_CONFIG_DEFINES) +precompute_ecmult_gen_LDADD = $(COMMON_LIB) # See Automake manual, Section "Errors with distclean". # We don't list any dependencies for the prebuilt files here because diff --git a/src/secp256k1/README.md b/src/secp256k1/README.md index ffdc9aeaee..19dabe8505 100644 --- a/src/secp256k1/README.md +++ b/src/secp256k1/README.md @@ -60,10 +60,8 @@ Implementation details * Optional runtime blinding which attempts to frustrate differential power analysis. * The precomputed tables add and eventually subtract points for which no known scalar (secret key) is known, preventing even an attacker with control over the secret key used to control the data internally. -Build steps ------------ - -libsecp256k1 is built using autotools: +Building with Autotools +----------------------- $ ./autogen.sh $ ./configure @@ -73,6 +71,43 @@ libsecp256k1 is built using autotools: To compile optional modules (such as Schnorr signatures), you need to run `./configure` with additional flags (such as `--enable-module-schnorrsig`). Run `./configure --help` to see the full list of available flags. +Building with CMake (experimental) +---------------------------------- + +To maintain a pristine source tree, CMake encourages to perform an out-of-source build by using a separate dedicated build tree. + +### Building on POSIX systems + + $ mkdir build && cd build + $ cmake .. + $ make + $ make check # run the test suite + $ sudo make install # optional + +To compile optional modules (such as Schnorr signatures), you need to run `cmake` with additional flags (such as `-DSECP256K1_ENABLE_MODULE_SCHNORRSIG=ON`). Run `cmake .. -LH` to see the full list of available flags. + +### Cross compiling + +To alleviate issues with cross compiling, preconfigured toolchain files are available in the `cmake` directory. +For example, to cross compile for Windows: + + $ cmake .. -DCMAKE_TOOLCHAIN_FILE=../cmake/x86_64-w64-mingw32.toolchain.cmake + +To cross compile for Android with [NDK](https://developer.android.com/ndk/guides/cmake) (using NDK's toolchain file, and assuming the `ANDROID_NDK_ROOT` environment variable has been set): + + $ cmake .. -DCMAKE_TOOLCHAIN_FILE="${ANDROID_NDK_ROOT}/build/cmake/android.toolchain.cmake" -DANDROID_ABI=arm64-v8a -DANDROID_PLATFORM=28 + +### Building on Windows + +To build on Windows with Visual Studio, a proper [generator](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html#visual-studio-generators) must be specified for a new build tree. + +The following example assumes using of Visual Studio 2022 and CMake v3.21+. + +In "Developer Command Prompt for VS 2022": + + >cmake -G "Visual Studio 17 2022" -A x64 -S . -B build + >cmake --build build --config RelWithDebInfo + Usage examples ----------- Usage examples can be found in the [examples](examples) directory. To compile them you need to configure with `--enable-examples`. diff --git a/src/secp256k1/build-aux/m4/bitcoin_secp.m4 b/src/secp256k1/build-aux/m4/bitcoin_secp.m4 index 98be915b67..624f5e956e 100644 --- a/src/secp256k1/build-aux/m4/bitcoin_secp.m4 +++ b/src/secp256k1/build-aux/m4/bitcoin_secp.m4 @@ -20,7 +20,7 @@ if test x"$has_valgrind" != x"yes"; then #if defined(NVALGRIND) # error "Valgrind does not support this platform." #endif - ]])], [has_valgrind=yes; AC_DEFINE(HAVE_VALGRIND,1,[Define this symbol if valgrind is installed, and it supports the host platform])]) + ]])], [has_valgrind=yes]) fi AC_MSG_RESULT($has_valgrind) ]) diff --git a/src/secp256k1/ci/cirrus.sh b/src/secp256k1/ci/cirrus.sh index fb5854a777..8495c39203 100755 --- a/src/secp256k1/ci/cirrus.sh +++ b/src/secp256k1/ci/cirrus.sh @@ -1,7 +1,6 @@ #!/bin/sh -set -e -set -x +set -eux export LC_ALL=C @@ -11,14 +10,20 @@ print_environment() { set +x # There are many ways to print variable names and their content. This one # does not rely on bash. - for i in WERROR_CFLAGS MAKEFLAGS BUILD \ + for var in WERROR_CFLAGS MAKEFLAGS BUILD \ ECMULTWINDOW ECMULTGENPRECISION ASM WIDEMUL WITH_VALGRIND EXTRAFLAGS \ EXPERIMENTAL ECDH RECOVERY SCHNORRSIG \ - SECP256K1_TEST_ITERS BENCH SECP256K1_BENCH_ITERS CTIMETEST\ + SECP256K1_TEST_ITERS BENCH SECP256K1_BENCH_ITERS CTIMETESTS\ EXAMPLES \ - WRAPPER_CMD CC AR NM HOST + HOST WRAPPER_CMD \ + CC CFLAGS CPPFLAGS AR NM do - eval 'printf "%s %s " "$i=\"${'"$i"'}\""' + eval "isset=\${$var+x}" + if [ -n "$isset" ]; then + eval "val=\${$var}" + # shellcheck disable=SC2154 + printf '%s="%s" ' "$var" "$val" + fi done echo "$0" set -x @@ -29,6 +34,8 @@ print_environment # This speeds up jobs with many invocations of wine (e.g., ./configure with MSVC) tremendously. case "$WRAPPER_CMD" in *wine*) + # Make sure to shutdown wineserver whenever we exit. + trap "wineserver -k || true" EXIT INT HUP # This is apparently only reliable when we run a dummy command such as "hh.exe" afterwards. wineserver -p && wine hh.exe ;; @@ -36,7 +43,7 @@ esac env >> test_env.log -if [ -n "$CC" ]; then +if [ -n "${CC+x}" ]; then # The MSVC compiler "cl" doesn't understand "-v" $CC -v || true fi @@ -57,6 +64,7 @@ fi --enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" \ --enable-module-schnorrsig="$SCHNORRSIG" \ --enable-examples="$EXAMPLES" \ + --enable-ctime-tests="$CTIMETESTS" \ --with-valgrind="$WITH_VALGRIND" \ --host="$HOST" $EXTRAFLAGS @@ -73,14 +81,15 @@ export LOG_COMPILER="$WRAPPER_CMD" make "$BUILD" +# Using the local `libtool` because on macOS the system's libtool has nothing to do with GNU libtool +EXEC='./libtool --mode=execute' +if [ -n "$WRAPPER_CMD" ] +then + EXEC="$EXEC $WRAPPER_CMD" +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 "$WRAPPER_CMD" ] - then - EXEC="$EXEC $WRAPPER_CMD" - fi { $EXEC ./bench_ecmult $EXEC ./bench_internal @@ -88,9 +97,13 @@ then } >> bench.log 2>&1 fi -if [ "$CTIMETEST" = "yes" ] +if [ "$CTIMETESTS" = "yes" ] then - ./libtool --mode=execute valgrind --error-exitcode=42 ./valgrind_ctime_test > valgrind_ctime_test.log 2>&1 + if [ "$WITH_VALGRIND" = "yes" ]; then + ./libtool --mode=execute valgrind --error-exitcode=42 ./ctime_tests > ctime_tests.log 2>&1 + else + $EXEC ./ctime_tests > ctime_tests.log 2>&1 + fi fi # Rebuild precomputed files (if not cross-compiling). @@ -100,9 +113,6 @@ then make precomp fi -# Shutdown wineserver again -wineserver -k || true - # Check that no repo files have been modified by the build. # (This fails for example if the precomp files need to be updated in the repo.) git diff --exit-code diff --git a/src/secp256k1/cmake/Check64bitAssembly.cmake b/src/secp256k1/cmake/Check64bitAssembly.cmake new file mode 100644 index 0000000000..3f65887765 --- /dev/null +++ b/src/secp256k1/cmake/Check64bitAssembly.cmake @@ -0,0 +1,14 @@ +include(CheckCSourceCompiles) + +function(check_64bit_assembly) + check_c_source_compiles(" + #include <stdint.h> + + int main() + { + uint64_t a = 11, tmp; + __asm__ __volatile__(\"movq $0x100000000,%1; mulq %%rsi\" : \"+a\"(a) : \"S\"(tmp) : \"cc\", \"%rdx\"); + } + " HAS_64BIT_ASM) + set(HAS_64BIT_ASM ${HAS_64BIT_ASM} PARENT_SCOPE) +endfunction() diff --git a/src/secp256k1/cmake/CheckStringOptionValue.cmake b/src/secp256k1/cmake/CheckStringOptionValue.cmake new file mode 100644 index 0000000000..bc4d7b5749 --- /dev/null +++ b/src/secp256k1/cmake/CheckStringOptionValue.cmake @@ -0,0 +1,12 @@ +function(check_string_option_value option) + get_property(expected_values CACHE ${option} PROPERTY STRINGS) + if(expected_values) + foreach(value IN LISTS expected_values) + if(value STREQUAL "${${option}}") + return() + endif() + endforeach() + message(FATAL_ERROR "${option} value is \"${${option}}\", but must be one of ${expected_values}.") + endif() + message(AUTHOR_WARNING "The STRINGS property must be set before invoking `check_string_option_value' function.") +endfunction() diff --git a/src/secp256k1/cmake/FindValgrind.cmake b/src/secp256k1/cmake/FindValgrind.cmake new file mode 100644 index 0000000000..f6c1f58649 --- /dev/null +++ b/src/secp256k1/cmake/FindValgrind.cmake @@ -0,0 +1,41 @@ +if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin") + find_program(BREW_COMMAND brew) + execute_process( + COMMAND ${BREW_COMMAND} --prefix valgrind + OUTPUT_VARIABLE valgrind_brew_prefix + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE + ) +endif() + +set(hints_paths) +if(valgrind_brew_prefix) + set(hints_paths ${valgrind_brew_prefix}/include) +endif() + +find_path(Valgrind_INCLUDE_DIR + NAMES valgrind/memcheck.h + HINTS ${hints_paths} +) + +if(Valgrind_INCLUDE_DIR) + include(CheckCSourceCompiles) + set(CMAKE_REQUIRED_INCLUDES ${Valgrind_INCLUDE_DIR}) + check_c_source_compiles(" + #include <valgrind/memcheck.h> + #if defined(NVALGRIND) + # error \"Valgrind does not support this platform.\" + #endif + + int main() {} + " Valgrind_WORKS) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Valgrind + REQUIRED_VARS Valgrind_INCLUDE_DIR Valgrind_WORKS +) + +mark_as_advanced( + Valgrind_INCLUDE_DIR +) diff --git a/src/secp256k1/cmake/TryAddCompileOption.cmake b/src/secp256k1/cmake/TryAddCompileOption.cmake new file mode 100644 index 0000000000..f53c252c2d --- /dev/null +++ b/src/secp256k1/cmake/TryAddCompileOption.cmake @@ -0,0 +1,23 @@ +include(CheckCCompilerFlag) + +function(try_add_compile_option option) + string(MAKE_C_IDENTIFIER ${option} result) + string(TOUPPER ${result} result) + set(result "C_SUPPORTS${result}") + set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) + if(NOT MSVC) + set(CMAKE_REQUIRED_FLAGS "-Werror") + endif() + check_c_compiler_flag(${option} ${result}) + if(${result}) + get_property(compile_options + DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + PROPERTY COMPILE_OPTIONS + ) + list(APPEND compile_options "${option}") + set_property( + DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + PROPERTY COMPILE_OPTIONS "${compile_options}" + ) + endif() +endfunction() diff --git a/src/secp256k1/cmake/arm-linux-gnueabihf.toolchain.cmake b/src/secp256k1/cmake/arm-linux-gnueabihf.toolchain.cmake new file mode 100644 index 0000000000..0d91912b6d --- /dev/null +++ b/src/secp256k1/cmake/arm-linux-gnueabihf.toolchain.cmake @@ -0,0 +1,3 @@ +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_PROCESSOR arm) +set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc) diff --git a/src/secp256k1/cmake/config.cmake.in b/src/secp256k1/cmake/config.cmake.in new file mode 100644 index 0000000000..46b180ab19 --- /dev/null +++ b/src/secp256k1/cmake/config.cmake.in @@ -0,0 +1,5 @@ +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@-targets.cmake") + +check_required_components(@PROJECT_NAME@) diff --git a/src/secp256k1/cmake/x86_64-w64-mingw32.toolchain.cmake b/src/secp256k1/cmake/x86_64-w64-mingw32.toolchain.cmake new file mode 100644 index 0000000000..96119b72d1 --- /dev/null +++ b/src/secp256k1/cmake/x86_64-w64-mingw32.toolchain.cmake @@ -0,0 +1,3 @@ +set(CMAKE_SYSTEM_NAME Windows) +set(CMAKE_SYSTEM_PROCESSOR x86_64) +set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc) diff --git a/src/secp256k1/configure.ac b/src/secp256k1/configure.ac index 68f279b17b..a46a0a7be3 100644 --- a/src/secp256k1/configure.ac +++ b/src/secp256k1/configure.ac @@ -4,7 +4,7 @@ AC_PREREQ([2.60]) # the API. All changes in experimental modules are treated as # backwards-compatible and therefore at most increase the minor version. define(_PKG_VERSION_MAJOR, 0) -define(_PKG_VERSION_MINOR, 2) +define(_PKG_VERSION_MINOR, 3) define(_PKG_VERSION_PATCH, 0) define(_PKG_VERSION_IS_RELEASE, true) @@ -13,7 +13,7 @@ define(_PKG_VERSION_IS_RELEASE, true) # https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html # All changes in experimental modules are treated as if they don't affect the # interface and therefore only increase the revision. -define(_LIB_VERSION_CURRENT, 1) +define(_LIB_VERSION_CURRENT, 2) define(_LIB_VERSION_REVISION, 0) define(_LIB_VERSION_AGE, 0) @@ -22,9 +22,6 @@ AC_INIT([libsecp256k1],m4_join([.], _PKG_VERSION_MAJOR, _PKG_VERSION_MINOR, _PKG AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_MACRO_DIR([build-aux/m4]) AC_CANONICAL_HOST -AH_TOP([#ifndef LIBSECP256K1_CONFIG_H]) -AH_TOP([#define LIBSECP256K1_CONFIG_H]) -AH_BOTTOM([#endif /*LIBSECP256K1_CONFIG_H*/]) # Require Automake 1.11.2 for AM_PROG_AR AM_INIT_AUTOMAKE([1.11.2 foreign subdir-objects]) @@ -105,6 +102,7 @@ AC_DEFUN([SECP_TRY_APPEND_DEFAULT_CFLAGS], [ SECP_TRY_APPEND_CFLAGS([-Wcast-align], $1) # GCC >= 2.95 SECP_TRY_APPEND_CFLAGS([-Wcast-align=strict], $1) # GCC >= 8.0 SECP_TRY_APPEND_CFLAGS([-Wconditional-uninitialized], $1) # Clang >= 3.0 only + SECP_TRY_APPEND_CFLAGS([-Wreserved-identifier], $1) # Clang >= 13.0 only SECP_TRY_APPEND_CFLAGS([-fvisibility=hidden], $1) # GCC >= 4.0 CFLAGS="$SECP_TRY_APPEND_DEFAULT_CFLAGS_saved_CFLAGS" @@ -117,6 +115,12 @@ AC_DEFUN([SECP_TRY_APPEND_DEFAULT_CFLAGS], [ if test x"$GCC" != x"yes" && test x"$build_windows" = x"yes"; then SECP_TRY_APPEND_CFLAGS([-W2 -wd4146], $1) # Moderate warning level, disable warning C4146 "unary minus operator applied to unsigned type, result still unsigned" SECP_TRY_APPEND_CFLAGS([-external:anglebrackets -external:W0], $1) # Suppress warnings from #include <...> files + # We pass -ignore:4217 to the MSVC linker to suppress warning 4217 when + # importing variables from a statically linked secp256k1. + # (See the libtool manual, section "Windows DLLs" for background.) + # Unfortunately, libtool tries to be too clever and strips "-Xlinker arg" + # into "arg", so this will be " -Xlinker -ignore:4217" after stripping. + LDFLAGS="-Xlinker -Xlinker -Xlinker -ignore:4217 $LDFLAGS" fi ]) SECP_TRY_APPEND_DEFAULT_CFLAGS(SECP_CFLAGS) @@ -142,6 +146,10 @@ AC_ARG_ENABLE(tests, AS_HELP_STRING([--enable-tests],[compile tests [default=yes]]), [], [SECP_SET_DEFAULT([enable_tests], [yes], [yes])]) +AC_ARG_ENABLE(ctime_tests, + AS_HELP_STRING([--enable-ctime-tests],[compile constant-time tests [default=yes if valgrind enabled]]), [], + [SECP_SET_DEFAULT([enable_ctime_tests], [auto], [auto])]) + AC_ARG_ENABLE(experimental, AS_HELP_STRING([--enable-experimental],[allow experimental configure options [default=no]]), [], [SECP_SET_DEFAULT([enable_experimental], [no], [yes])]) @@ -225,10 +233,13 @@ else enable_valgrind=yes fi fi -AM_CONDITIONAL([VALGRIND_ENABLED],[test "$enable_valgrind" = "yes"]) + +if test x"$enable_ctime_tests" = x"auto"; then + enable_ctime_tests=$enable_valgrind +fi if test x"$enable_coverage" = x"yes"; then - AC_DEFINE(COVERAGE, 1, [Define this symbol to compile out all VERIFY code]) + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DCOVERAGE=1" SECP_CFLAGS="-O0 --coverage $SECP_CFLAGS" LDFLAGS="--coverage $LDFLAGS" else @@ -270,7 +281,7 @@ enable_external_asm=no case $set_asm in x86_64) - AC_DEFINE(USE_ASM_X86_64, 1, [Define this symbol to enable x86_64 assembly optimizations]) + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DUSE_ASM_X86_64=1" ;; arm) enable_external_asm=yes @@ -283,20 +294,20 @@ no) esac if test x"$enable_external_asm" = x"yes"; then - AC_DEFINE(USE_EXTERNAL_ASM, 1, [Define this symbol if an external (non-inline) assembly implementation is used]) + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DUSE_EXTERNAL_ASM=1" fi # Select wide multiplication implementation case $set_widemul in int128_struct) - AC_DEFINE(USE_FORCE_WIDEMUL_INT128_STRUCT, 1, [Define this symbol to force the use of the structure for simulating (unsigned) int128 based wide multiplication]) + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DUSE_FORCE_WIDEMUL_INT128_STRUCT=1" ;; int128) - AC_DEFINE(USE_FORCE_WIDEMUL_INT128, 1, [Define this symbol to force the use of the (unsigned) __int128 based wide multiplication implementation]) + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DUSE_FORCE_WIDEMUL_INT128=1" ;; int64) - AC_DEFINE(USE_FORCE_WIDEMUL_INT64, 1, [Define this symbol to force the use of the (u)int64_t based wide multiplication implementation]) + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DUSE_FORCE_WIDEMUL_INT64=1" ;; auto) ;; @@ -323,7 +334,7 @@ case $set_ecmult_window in # not in range AC_MSG_ERROR($error_window_size) fi - AC_DEFINE_UNQUOTED(ECMULT_WINDOW_SIZE, $set_ecmult_window, [Set window size for ecmult precomputation]) + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DECMULT_WINDOW_SIZE=$set_ecmult_window" ;; esac @@ -336,7 +347,7 @@ fi case $set_ecmult_gen_precision in 2|4|8) - AC_DEFINE_UNQUOTED(ECMULT_GEN_PREC_BITS, $set_ecmult_gen_precision, [Set ecmult gen precision bits]) + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DECMULT_GEN_PREC_BITS=$set_ecmult_gen_precision" ;; *) AC_MSG_ERROR(['ecmult gen precision not 2, 4, 8 or "auto"']) @@ -344,7 +355,7 @@ case $set_ecmult_gen_precision in esac if test x"$enable_valgrind" = x"yes"; then - SECP_INCLUDES="$SECP_INCLUDES $VALGRIND_CPPFLAGS" + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES $VALGRIND_CPPFLAGS -DVALGRIND" fi # Add -Werror and similar flags passed from the outside (for testing, e.g., in CI). @@ -357,26 +368,26 @@ SECP_CFLAGS="$SECP_CFLAGS $WERROR_CFLAGS" ### if test x"$enable_module_ecdh" = x"yes"; then - AC_DEFINE(ENABLE_MODULE_ECDH, 1, [Define this symbol to enable the ECDH module]) + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_ECDH=1" fi if test x"$enable_module_recovery" = x"yes"; then - AC_DEFINE(ENABLE_MODULE_RECOVERY, 1, [Define this symbol to enable the ECDSA pubkey recovery module]) + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_RECOVERY=1" fi if test x"$enable_module_schnorrsig" = x"yes"; then - AC_DEFINE(ENABLE_MODULE_SCHNORRSIG, 1, [Define this symbol to enable the schnorrsig module]) + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_SCHNORRSIG=1" enable_module_extrakeys=yes fi # Test if extrakeys is set after the schnorrsig module to allow the schnorrsig # module to set enable_module_extrakeys=yes if test x"$enable_module_extrakeys" = x"yes"; then - AC_DEFINE(ENABLE_MODULE_EXTRAKEYS, 1, [Define this symbol to enable the extrakeys module]) + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_EXTRAKEYS=1" fi if test x"$enable_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]) + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DUSE_EXTERNAL_DEFAULT_CALLBACKS=1" fi ### @@ -398,15 +409,12 @@ fi ### Generate output ### -AC_CONFIG_HEADERS([src/libsecp256k1-config.h]) AC_CONFIG_FILES([Makefile libsecp256k1.pc]) -AC_SUBST(SECP_INCLUDES) -AC_SUBST(SECP_LIBS) -AC_SUBST(SECP_TEST_LIBS) -AC_SUBST(SECP_TEST_INCLUDES) AC_SUBST(SECP_CFLAGS) +AC_SUBST(SECP_CONFIG_DEFINES) AM_CONDITIONAL([ENABLE_COVERAGE], [test x"$enable_coverage" = x"yes"]) AM_CONDITIONAL([USE_TESTS], [test x"$enable_tests" != x"no"]) +AM_CONDITIONAL([USE_CTIME_TESTS], [test x"$enable_ctime_tests" = x"yes"]) AM_CONDITIONAL([USE_EXHAUSTIVE_TESTS], [test x"$enable_exhaustive_tests" != x"no"]) AM_CONDITIONAL([USE_EXAMPLES], [test x"$enable_examples" != x"no"]) AM_CONDITIONAL([USE_BENCHMARK], [test x"$enable_benchmark" = x"yes"]) @@ -428,6 +436,7 @@ echo "Build Options:" echo " with external callbacks = $enable_external_default_callbacks" echo " with benchmarks = $enable_benchmark" echo " with tests = $enable_tests" +echo " with ctime tests = $enable_ctime_tests" echo " with coverage = $enable_coverage" echo " with examples = $enable_examples" echo " module ecdh = $enable_module_ecdh" diff --git a/src/secp256k1/doc/release-process.md b/src/secp256k1/doc/release-process.md index 91e3616915..b522f89657 100644 --- a/src/secp256k1/doc/release-process.md +++ b/src/secp256k1/doc/release-process.md @@ -23,7 +23,7 @@ This process also assumes that there will be no minor releases for old major rel git tag -s v$MAJOR.$MINOR.$PATCH -m "libsecp256k1 $MAJOR.$MINOR.$PATCH" $RELEASE_COMMIT git push git@github.com:bitcoin-core/secp256k1.git v$MAJOR.$MINOR.$PATCH ``` -3. Open a PR to the master branch with a commit (using message `"release: bump version after $MAJOR.$MINOR.$PATCH"`, for example) that sets `_PKG_VERSION_IS_RELEASE` to `false` and `_PKG_VERSION_PATCH` to `$PATCH + 1` and increases `_LIB_VERSION_REVISION`. If other maintainers are not present to approve the PR, it can be merged without ACKs. +3. Open a PR to the master branch with a commit (using message `"release cleanup: bump version after $MAJOR.$MINOR.$PATCH"`, for example) that sets `_PKG_VERSION_IS_RELEASE` to `false` and `_PKG_VERSION_PATCH` to `$PATCH + 1` and increases `_LIB_VERSION_REVISION`. If other maintainers are not present to approve the PR, it can be merged without ACKs. 4. Create a new GitHub release with a link to the corresponding entry in [CHANGELOG.md](../CHANGELOG.md). ## Maintenance release diff --git a/src/secp256k1/doc/safegcd_implementation.md b/src/secp256k1/doc/safegcd_implementation.md index 063aa8efae..5dbbb7bbd2 100644 --- a/src/secp256k1/doc/safegcd_implementation.md +++ b/src/secp256k1/doc/safegcd_implementation.md @@ -1,7 +1,7 @@ # 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 +This document explains the modular inverse and Jacobi symbol implementations 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. @@ -410,7 +410,7 @@ sufficient even. Given that every loop iteration performs *N* divsteps, it will 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 +`ctime_tests.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 @@ -769,3 +769,51 @@ def modinv_var(M, Mi, x): d, e = update_de(d, e, t, M, Mi) return normalize(f, d, Mi) ``` + +## 8. From GCDs to Jacobi symbol + +We can also use a similar approach to calculate Jacobi symbol *(x | M)* by keeping track of an +extra variable *j*, for which at every step *(x | M) = j (g | f)*. As we update *f* and *g*, we +make corresponding updates to *j* using +[properties of the Jacobi symbol](https://en.wikipedia.org/wiki/Jacobi_symbol#Properties): +* *((g/2) | f)* is either *(g | f)* or *-(g | f)*, depending on the value of *f mod 8* (negating if it's *3* or *5*). +* *(f | g)* is either *(g | f)* or *-(g | f)*, depending on *f mod 4* and *g mod 4* (negating if both are *3*). + +These updates depend only on the values of *f* and *g* modulo *4* or *8*, and can thus be applied +very quickly, as long as we keep track of a few additional bits of *f* and *g*. Overall, this +calculation is slightly simpler than the one for the modular inverse because we no longer need to +keep track of *d* and *e*. + +However, one difficulty of this approach is that the Jacobi symbol *(a | n)* is only defined for +positive odd integers *n*, whereas in the original safegcd algorithm, *f, g* can take negative +values. We resolve this by using the following modified steps: + +```python + # Before + if delta > 0 and g & 1: + delta, f, g = 1 - delta, g, (g - f) // 2 + + # After + if delta > 0 and g & 1: + delta, f, g = 1 - delta, g, (g + f) // 2 +``` + +The algorithm is still correct, since the changed divstep, called a "posdivstep" (see section 8.4 +and E.5 in the paper) preserves *gcd(f, g)*. However, there's no proof that the modified algorithm +will converge. The justification for posdivsteps is completely empirical: in practice, it appears +that the vast majority of nonzero inputs converge to *f=g=gcd(f<sub>0</sub>, g<sub>0</sub>)* in a +number of steps proportional to their logarithm. + +Note that: +- We require inputs to satisfy *gcd(x, M) = 1*, as otherwise *f=1* is not reached. +- We require inputs *x &neq; 0*, because applying posdivstep with *g=0* has no effect. +- We need to update the termination condition from *g=0* to *f=1*. + +We account for the possibility of nonconvergence by only performing a bounded number of +posdivsteps, and then falling back to square-root based Jacobi calculation if a solution has not +yet been found. + +The optimizations in sections 3-7 above are described in the context of the original divsteps, but +in the C implementation we also adapt most of them (not including "avoiding modulus operations", +since it's not necessary to track *d, e*, and "constant-time operation", since we never calculate +Jacobi symbols for secret data) to the posdivsteps version. diff --git a/src/secp256k1/examples/CMakeLists.txt b/src/secp256k1/examples/CMakeLists.txt new file mode 100644 index 0000000000..0884b645e0 --- /dev/null +++ b/src/secp256k1/examples/CMakeLists.txt @@ -0,0 +1,34 @@ +add_library(example INTERFACE) +target_include_directories(example INTERFACE + ${PROJECT_SOURCE_DIR}/include +) +target_compile_options(example INTERFACE + $<$<C_COMPILER_ID:MSVC>:/wd4005> +) +target_link_libraries(example INTERFACE + $<$<PLATFORM_ID:Windows>:bcrypt> +) +if(SECP256K1_BUILD_SHARED) + target_link_libraries(example INTERFACE secp256k1) +elseif(SECP256K1_BUILD_STATIC) + target_link_libraries(example INTERFACE secp256k1_static) + if(MSVC) + target_link_options(example INTERFACE /IGNORE:4217) + endif() +endif() + +add_executable(ecdsa_example ecdsa.c) +target_link_libraries(ecdsa_example example) +add_test(ecdsa_example ecdsa_example) + +if(SECP256K1_ENABLE_MODULE_ECDH) + add_executable(ecdh_example ecdh.c) + target_link_libraries(ecdh_example example) + add_test(ecdh_example ecdh_example) +endif() + +if(SECP256K1_ENABLE_MODULE_SCHNORRSIG) + add_executable(schnorr_example schnorr.c) + target_link_libraries(schnorr_example example) + add_test(schnorr_example schnorr_example) +endif() diff --git a/src/secp256k1/examples/ecdh.c b/src/secp256k1/examples/ecdh.c index 027d52fd5f..4b7b7d6154 100644 --- a/src/secp256k1/examples/ecdh.c +++ b/src/secp256k1/examples/ecdh.c @@ -14,8 +14,7 @@ #include <secp256k1.h> #include <secp256k1_ecdh.h> -#include "random.h" - +#include "examples_util.h" int main(void) { unsigned char seckey1[32]; @@ -112,12 +111,12 @@ int main(void) { * example through "out of bounds" array access (see Heartbleed), Or the OS * swapping them to disk. Hence, we overwrite the secret key buffer with zeros. * - * TODO: Prevent these writes from being optimized out, as any good compiler + * Here we are preventing these writes from being optimized out, as any good compiler * will remove any writes that aren't used. */ - memset(seckey1, 0, sizeof(seckey1)); - memset(seckey2, 0, sizeof(seckey2)); - memset(shared_secret1, 0, sizeof(shared_secret1)); - memset(shared_secret2, 0, sizeof(shared_secret2)); + secure_erase(seckey1, sizeof(seckey1)); + secure_erase(seckey2, sizeof(seckey2)); + secure_erase(shared_secret1, sizeof(shared_secret1)); + secure_erase(shared_secret2, sizeof(shared_secret2)); return 0; } diff --git a/src/secp256k1/examples/ecdsa.c b/src/secp256k1/examples/ecdsa.c index 7e4f1b13ac..d1d2b0e365 100644 --- a/src/secp256k1/examples/ecdsa.c +++ b/src/secp256k1/examples/ecdsa.c @@ -13,9 +13,7 @@ #include <secp256k1.h> -#include "random.h" - - +#include "examples_util.h" int main(void) { /* Instead of signing the message directly, we must sign a 32-byte hash. @@ -34,7 +32,7 @@ int main(void) { unsigned char compressed_pubkey[33]; unsigned char serialized_signature[64]; size_t len; - int is_signature_valid; + int is_signature_valid, is_signature_valid2; int return_val; secp256k1_pubkey pubkey; secp256k1_ecdsa_signature sig; @@ -116,18 +114,26 @@ int main(void) { printf("Signature: "); print_hex(serialized_signature, sizeof(serialized_signature)); - /* This will clear everything from the context and free the memory */ secp256k1_context_destroy(ctx); + /* Bonus example: if all we need is signature verification (and no key + generation or signing), we don't need to use a context created via + secp256k1_context_create(). We can simply use the static (i.e., global) + context secp256k1_context_static. See its description in + include/secp256k1.h for details. */ + is_signature_valid2 = secp256k1_ecdsa_verify(secp256k1_context_static, + &sig, msg_hash, &pubkey); + assert(is_signature_valid2 == is_signature_valid); + /* It's best practice to try to clear secrets from memory after using them. * This is done because some bugs can allow an attacker to leak memory, for * example through "out of bounds" array access (see Heartbleed), Or the OS * swapping them to disk. Hence, we overwrite the secret key buffer with zeros. * - * TODO: Prevent these writes from being optimized out, as any good compiler + * Here we are preventing these writes from being optimized out, as any good compiler * will remove any writes that aren't used. */ - memset(seckey, 0, sizeof(seckey)); + secure_erase(seckey, sizeof(seckey)); return 0; } diff --git a/src/secp256k1/examples/random.h b/src/secp256k1/examples/examples_util.h index 439226f09f..a52b1fa115 100644 --- a/src/secp256k1/examples/random.h +++ b/src/secp256k1/examples/examples_util.h @@ -71,3 +71,32 @@ static void print_hex(unsigned char* data, size_t size) { } printf("\n"); } + +#if defined(_MSC_VER) +// For SecureZeroMemory +#include <Windows.h> +#endif +/* Cleanses memory to prevent leaking sensitive info. Won't be optimized out. */ +static SECP256K1_INLINE void secure_erase(void *ptr, size_t len) { +#if defined(_MSC_VER) + /* SecureZeroMemory is guaranteed not to be optimized out by MSVC. */ + SecureZeroMemory(ptr, len); +#elif defined(__GNUC__) + /* We use a memory barrier that scares the compiler away from optimizing out the memset. + * + * Quoting Adam Langley <agl@google.com> in commit ad1907fe73334d6c696c8539646c21b11178f20f + * in BoringSSL (ISC License): + * As best as we can tell, this is sufficient to break any optimisations that + * might try to eliminate "superfluous" memsets. + * This method used in memzero_explicit() the Linux kernel, too. Its advantage is that it is + * pretty efficient, because the compiler can still implement the memset() efficently, + * just not remove it entirely. See "Dead Store Elimination (Still) Considered Harmful" by + * Yang et al. (USENIX Security 2017) for more background. + */ + memset(ptr, 0, len); + __asm__ __volatile__("" : : "r"(ptr) : "memory"); +#else + void *(*volatile const volatile_memset)(void *, int, size_t) = memset; + volatile_memset(ptr, 0, len); +#endif +} diff --git a/src/secp256k1/examples/schnorr.c b/src/secp256k1/examples/schnorr.c index 207c45c422..4c0dd1c1a9 100644 --- a/src/secp256k1/examples/schnorr.c +++ b/src/secp256k1/examples/schnorr.c @@ -15,7 +15,7 @@ #include <secp256k1_extrakeys.h> #include <secp256k1_schnorrsig.h> -#include "random.h" +#include "examples_util.h" int main(void) { unsigned char msg[12] = "Hello World!"; @@ -26,7 +26,7 @@ int main(void) { unsigned char auxiliary_rand[32]; unsigned char serialized_pubkey[32]; unsigned char signature[64]; - int is_signature_valid; + int is_signature_valid, is_signature_valid2; int return_val; secp256k1_xonly_pubkey pubkey; secp256k1_keypair keypair; @@ -135,14 +135,22 @@ int main(void) { /* This will clear everything from the context and free the memory */ secp256k1_context_destroy(ctx); + /* Bonus example: if all we need is signature verification (and no key + generation or signing), we don't need to use a context created via + secp256k1_context_create(). We can simply use the static (i.e., global) + context secp256k1_context_static. See its description in + include/secp256k1.h for details. */ + is_signature_valid2 = secp256k1_schnorrsig_verify(secp256k1_context_static, + signature, msg_hash, 32, &pubkey); + assert(is_signature_valid2 == is_signature_valid); + /* It's best practice to try to clear secrets from memory after using them. * This is done because some bugs can allow an attacker to leak memory, for * example through "out of bounds" array access (see Heartbleed), Or the OS * swapping them to disk. Hence, we overwrite the secret key buffer with zeros. * - * TODO: Prevent these writes from being optimized out, as any good compiler + * Here we are preventing these writes from being optimized out, as any good compiler * will remove any writes that aren't used. */ - memset(seckey, 0, sizeof(seckey)); - + secure_erase(seckey, sizeof(seckey)); return 0; } diff --git a/src/secp256k1/include/secp256k1.h b/src/secp256k1/include/secp256k1.h index 826ab75850..325f35eb04 100644 --- a/src/secp256k1/include/secp256k1.h +++ b/src/secp256k1/include/secp256k1.h @@ -145,21 +145,28 @@ typedef int (*secp256k1_nonce_function)( # define SECP256K1_NO_BUILD #endif -/** At secp256k1 build-time DLL_EXPORT is defined when building objects destined - * for a shared library, but not for those intended for static libraries. - */ - -#ifndef SECP256K1_API -# if defined(_WIN32) -# if defined(SECP256K1_BUILD) && defined(DLL_EXPORT) -# define SECP256K1_API __declspec(dllexport) -# else -# define SECP256K1_API +/* Symbol visibility. See libtool manual, section "Windows DLLs". */ +#if defined(_WIN32) && !defined(__GNUC__) +# ifdef SECP256K1_BUILD +# ifdef DLL_EXPORT +# define SECP256K1_API __declspec (dllexport) +# define SECP256K1_API_VAR extern __declspec (dllexport) # endif -# elif defined(__GNUC__) && (__GNUC__ >= 4) && defined(SECP256K1_BUILD) -# define SECP256K1_API __attribute__ ((visibility ("default"))) +# elif defined _MSC_VER +# define SECP256K1_API +# define SECP256K1_API_VAR extern __declspec (dllimport) +# elif defined DLL_EXPORT +# define SECP256K1_API __declspec (dllimport) +# define SECP256K1_API_VAR extern __declspec (dllimport) +# endif +#endif +#ifndef SECP256K1_API +# if defined(__GNUC__) && (__GNUC__ >= 4) && defined(SECP256K1_BUILD) +# define SECP256K1_API __attribute__ ((visibility ("default"))) +# define SECP256K1_API_VAR extern __attribute__ ((visibility ("default"))) # else # define SECP256K1_API +# define SECP256K1_API_VAR extern # endif #endif @@ -231,10 +238,10 @@ typedef int (*secp256k1_nonce_function)( * * It is highly recommended to call secp256k1_selftest before using this context. */ -SECP256K1_API extern const secp256k1_context *secp256k1_context_static; +SECP256K1_API_VAR const secp256k1_context *secp256k1_context_static; /** Deprecated alias for secp256k1_context_static. */ -SECP256K1_API extern const secp256k1_context *secp256k1_context_no_precomp +SECP256K1_API_VAR const secp256k1_context *secp256k1_context_no_precomp SECP256K1_DEPRECATED("Use secp256k1_context_static instead"); /** Perform basic self tests (to be used in conjunction with secp256k1_context_static) @@ -291,8 +298,11 @@ SECP256K1_API secp256k1_context* secp256k1_context_create( * called at most once for every call of this function. If you need to avoid dynamic * memory allocation entirely, see the functions in secp256k1_preallocated.h. * + * Cloning secp256k1_context_static is not possible, and should not be emulated by + * the caller (e.g., using memcpy). Create a new context instead. + * * Returns: a newly created context object. - * Args: ctx: an existing context to copy + * Args: ctx: an existing context to copy (not secp256k1_context_static) */ SECP256K1_API secp256k1_context* secp256k1_context_clone( const secp256k1_context* ctx @@ -310,6 +320,7 @@ SECP256K1_API secp256k1_context* secp256k1_context_clone( * * Args: ctx: an existing context to destroy, constructed using * secp256k1_context_create or secp256k1_context_clone + * (i.e., not secp256k1_context_static). */ SECP256K1_API void secp256k1_context_destroy( secp256k1_context* ctx @@ -627,10 +638,10 @@ SECP256K1_API int secp256k1_ecdsa_signature_normalize( * If a data pointer is passed, it is assumed to be a pointer to 32 bytes of * extra entropy. */ -SECP256K1_API extern const secp256k1_nonce_function secp256k1_nonce_function_rfc6979; +SECP256K1_API_VAR const secp256k1_nonce_function secp256k1_nonce_function_rfc6979; /** A default safe nonce generation function (currently equal to secp256k1_nonce_function_rfc6979). */ -SECP256K1_API extern const secp256k1_nonce_function secp256k1_nonce_function_default; +SECP256K1_API_VAR const secp256k1_nonce_function secp256k1_nonce_function_default; /** Create an ECDSA signature. * @@ -820,10 +831,10 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_mul( /** Randomizes the context to provide enhanced protection against side-channel leakage. * - * Returns: 1: randomization successful (or called on copy of secp256k1_context_static) + * Returns: 1: randomization successful * 0: error - * Args: ctx: pointer to a context object. - * In: seed32: pointer to a 32-byte random seed (NULL resets to initial state) + * Args: ctx: pointer to a context object (not secp256k1_context_static). + * In: seed32: pointer to a 32-byte random seed (NULL resets to initial state). * * While secp256k1 code is written and tested to be constant-time no matter what * secret values are, it is possible that a compiler may output code which is not, @@ -838,21 +849,17 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_mul( * functions that perform computations involving secret keys, e.g., signing and * public key generation. It is possible to call this function more than once on * the same context, and doing so before every few computations involving secret - * keys is recommended as a defense-in-depth measure. + * keys is recommended as a defense-in-depth measure. Randomization of the static + * context secp256k1_context_static is not supported. * * Currently, the random seed is mainly used for blinding multiplications of a * secret scalar with the elliptic curve base point. Multiplications of this * kind are performed by exactly those API functions which are documented to - * require a context that is not the secp256k1_context_static. As a rule of thumb, + * require a context that is not secp256k1_context_static. As a rule of thumb, * these are all functions which take a secret key (or a keypair) as an input. * A notable exception to that rule is the ECDH module, which relies on a different * kind of elliptic curve point multiplication and thus does not benefit from * enhanced protection against side-channel leakage currently. - * - * It is safe call this function on a copy of secp256k1_context_static in writable - * memory (e.g., obtained via secp256k1_context_clone). In that case, this - * function is guaranteed to return 1, but the call will have no effect because - * the static context (or a copy thereof) is not meant to be randomized. */ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_context_randomize( secp256k1_context* ctx, diff --git a/src/secp256k1/include/secp256k1_ecdh.h b/src/secp256k1/include/secp256k1_ecdh.h index c8577984b1..625061b282 100644 --- a/src/secp256k1/include/secp256k1_ecdh.h +++ b/src/secp256k1/include/secp256k1_ecdh.h @@ -27,11 +27,11 @@ typedef int (*secp256k1_ecdh_hash_function)( /** An implementation of SHA256 hash function that applies to compressed public key. * Populates the output parameter with 32 bytes. */ -SECP256K1_API extern const secp256k1_ecdh_hash_function secp256k1_ecdh_hash_function_sha256; +SECP256K1_API_VAR const secp256k1_ecdh_hash_function secp256k1_ecdh_hash_function_sha256; /** A default ECDH hash function (currently equal to secp256k1_ecdh_hash_function_sha256). * Populates the output parameter with 32 bytes. */ -SECP256K1_API extern const secp256k1_ecdh_hash_function secp256k1_ecdh_hash_function_default; +SECP256K1_API_VAR const secp256k1_ecdh_hash_function secp256k1_ecdh_hash_function_default; /** Compute an EC Diffie-Hellman secret in constant time * diff --git a/src/secp256k1/include/secp256k1_preallocated.h b/src/secp256k1/include/secp256k1_preallocated.h index ed846f75f9..ffa96dd339 100644 --- a/src/secp256k1/include/secp256k1_preallocated.h +++ b/src/secp256k1/include/secp256k1_preallocated.h @@ -88,8 +88,11 @@ SECP256K1_API size_t secp256k1_context_preallocated_clone_size( * the lifetime of this context object, see the description of * secp256k1_context_preallocated_create for details. * + * Cloning secp256k1_context_static is not possible, and should not be emulated by + * the caller (e.g., using memcpy). Create a new context instead. + * * Returns: a newly created context object. - * Args: ctx: an existing context to copy. + * Args: ctx: an existing context to copy (not secp256k1_context_static). * In: prealloc: a pointer to a rewritable contiguous block of memory of * size at least secp256k1_context_preallocated_size(flags) * bytes, as detailed above. @@ -117,7 +120,8 @@ SECP256K1_API secp256k1_context* secp256k1_context_preallocated_clone( * * Args: ctx: an existing context to destroy, constructed using * secp256k1_context_preallocated_create or - * secp256k1_context_preallocated_clone. + * secp256k1_context_preallocated_clone + * (i.e., not secp256k1_context_static). */ SECP256K1_API void secp256k1_context_preallocated_destroy( secp256k1_context* ctx diff --git a/src/secp256k1/include/secp256k1_schnorrsig.h b/src/secp256k1/include/secp256k1_schnorrsig.h index e579e1b1d8..4cd2d98256 100644 --- a/src/secp256k1/include/secp256k1_schnorrsig.h +++ b/src/secp256k1/include/secp256k1_schnorrsig.h @@ -61,7 +61,7 @@ typedef int (*secp256k1_nonce_function_hardened)( * Therefore, to create BIP-340 compliant signatures, algo must be set to * "BIP0340/nonce" and algolen to 13. */ -SECP256K1_API extern const secp256k1_nonce_function_hardened secp256k1_nonce_function_bip340; +SECP256K1_API_VAR const secp256k1_nonce_function_hardened secp256k1_nonce_function_bip340; /** Data structure that contains additional arguments for schnorrsig_sign_custom. * diff --git a/src/secp256k1/libsecp256k1.pc.in b/src/secp256k1/libsecp256k1.pc.in index 694e98eef5..0fb6f48a6c 100644 --- a/src/secp256k1/libsecp256k1.pc.in +++ b/src/secp256k1/libsecp256k1.pc.in @@ -9,5 +9,4 @@ URL: https://github.com/bitcoin-core/secp256k1 Version: @PACKAGE_VERSION@ Cflags: -I${includedir} Libs: -L${libdir} -lsecp256k1 -Libs.private: @SECP_LIBS@ diff --git a/src/secp256k1/sage/gen_exhaustive_groups.sage b/src/secp256k1/sage/gen_exhaustive_groups.sage index 01d15dcdea..070bc1285f 100644 --- a/src/secp256k1/sage/gen_exhaustive_groups.sage +++ b/src/secp256k1/sage/gen_exhaustive_groups.sage @@ -1,124 +1,156 @@ load("secp256k1_params.sage") +MAX_ORDER = 1000 + +# Set of (curve) orders we have encountered so far. orders_done = set() -results = {} -first = True + +# Map from (subgroup) orders to [b, int(gen.x), int(gen.y), gen, lambda] for those subgroups. +solutions = {} + +# Iterate over curves of the form y^2 = x^3 + B. for b in range(1, P): - # There are only 6 curves (up to isomorphism) of the form y^2=x^3+B. Stop once we have tried all. + # There are only 6 curves (up to isomorphism) of the form y^2 = x^3 + B. Stop once we have tried all. if len(orders_done) == 6: break E = EllipticCurve(F, [0, b]) print("Analyzing curve y^2 = x^3 + %i" % b) n = E.order() + # Skip curves with an order we've already tried if n in orders_done: print("- Isomorphic to earlier curve") + print() continue orders_done.add(n) + # Skip curves isomorphic to the real secp256k1 if n.is_pseudoprime(): - print(" - Isomorphic to secp256k1") + assert E.is_isomorphic(C) + print("- Isomorphic to secp256k1") + print() continue - print("- Finding subgroups") - - # Find what prime subgroups exist - for f, _ in n.factor(): - print("- Analyzing subgroup of order %i" % f) - # Skip subgroups of order >1000 - if f < 4 or f > 1000: - print(" - Bad size") - continue - - # Iterate over X coordinates until we find one that is on the curve, has order f, - # and for which curve isomorphism exists that maps it to X coordinate 1. - for x in range(1, P): - # Skip X coordinates not on the curve, and construct the full point otherwise. - if not E.is_x_coord(x): - continue - G = E.lift_x(F(x)) + print("- Finding prime subgroups") - print(" - Analyzing (multiples of) point with X=%i" % x) + # Map from group_order to a set of independent generators for that order. + curve_gens = {} - # Skip points whose order is not a multiple of f. Project the point to have - # order f otherwise. - if (G.order() % f): - print(" - Bad order") + for g in E.gens(): + # Find what prime subgroups of group generated by g exist. + g_order = g.order() + for f, _ in g.order().factor(): + # Skip subgroups that have bad size. + if f < 4: + print(f" - Subgroup of size {f}: too small") + continue + if f > MAX_ORDER: + print(f" - Subgroup of size {f}: too large") continue - G = G * (G.order() // f) + + # Construct a generator for that subgroup. + gen = g * (g_order // f) + assert(gen.order() == f) + + # Add to set the minimal multiple of gen. + curve_gens.setdefault(f, set()).add(min([j*gen for j in range(1, f)])) + print(f" - Subgroup of size {f}: ok") + + for f in sorted(curve_gens.keys()): + print(f"- Constructing group of order {f}") + cbrts = sorted([int(c) for c in Integers(f)(1).nth_root(3, all=true) if c != 1]) + gens = list(curve_gens[f]) + sol_count = 0 + no_endo_count = 0 + + # Consider all non-zero linear combinations of the independent generators. + for j in range(1, f**len(gens)): + gen = sum(gens[k] * ((j // f**k) % f) for k in range(len(gens))) + assert not gen.is_zero() + assert (f*gen).is_zero() # Find lambda for endomorphism. Skip if none can be found. lam = None - for l in Integers(f)(1).nth_root(3, all=True): - if int(l)*G == E(BETA*G[0], G[1]): - lam = int(l) + for l in cbrts: + if l*gen == E(BETA*gen[0], gen[1]): + lam = l break + if lam is None: - print(" - No endomorphism for this subgroup") - break - - # Now look for an isomorphism of the curve that gives this point an X - # coordinate equal to 1. - # If (x,y) is on y^2 = x^3 + b, then (a^2*x, a^3*y) is on y^2 = x^3 + a^6*b. - # So look for m=a^2=1/x. - m = F(1)/G[0] - if not m.is_square(): - print(" - No curve isomorphism maps it to a point with X=1") - continue - a = m.sqrt() - rb = a^6*b - RE = EllipticCurve(F, [0, rb]) - - # Use as generator twice the image of G under the above isormorphism. - # This means that generator*(1/2 mod f) will have X coordinate 1. - RG = RE(1, a^3*G[1]) * 2 - # And even Y coordinate. - if int(RG[1]) % 2: - RG = -RG - assert(RG.order() == f) - assert(lam*RG == RE(BETA*RG[0], RG[1])) - - # We have found curve RE:y^2=x^3+rb with generator RG of order f. Remember it - results[f] = {"b": rb, "G": RG, "lambda": lam} - print(" - Found solution") - break - - print("") - -print("") -print("") -print("/* To be put in src/group_impl.h: */") + no_endo_count += 1 + else: + sol_count += 1 + solutions.setdefault(f, []).append((b, int(gen[0]), int(gen[1]), gen, lam)) + + print(f" - Found {sol_count} generators (plus {no_endo_count} without endomorphism)") + + print() + +def output_generator(g, name): + print(f"#define {name} SECP256K1_GE_CONST(\\") + print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x,\\" % tuple((int(g[0]) >> (32 * (7 - i))) & 0xffffffff for i in range(4))) + print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x,\\" % tuple((int(g[0]) >> (32 * (7 - i))) & 0xffffffff for i in range(4, 8))) + print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x,\\" % tuple((int(g[1]) >> (32 * (7 - i))) & 0xffffffff for i in range(4))) + print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x\\" % tuple((int(g[1]) >> (32 * (7 - i))) & 0xffffffff for i in range(4, 8))) + print(")") + +def output_b(b): + print(f"#define SECP256K1_B {int(b)}") + +print() +print("To be put in src/group_impl.h:") +print() +print("/* Begin of section generated by sage/gen_exhaustive_groups.sage. */") +for f in sorted(solutions.keys()): + # Use as generator/2 the one with lowest b, and lowest (x, y) generator (interpreted as non-negative integers). + b, _, _, HALF_G, lam = min(solutions[f]) + output_generator(2 * HALF_G, f"SECP256K1_G_ORDER_{f}") +print("/** Generator for secp256k1, value 'g' defined in") +print(" * \"Standards for Efficient Cryptography\" (SEC2) 2.7.1.") +print(" */") +output_generator(G, "SECP256K1_G") +print("/* These exhaustive group test orders and generators are chosen such that:") +print(" * - The field size is equal to that of secp256k1, so field code is the same.") +print(" * - The curve equation is of the form y^2=x^3+B for some small constant B.") +print(" * - The subgroup has a generator 2*P, where P.x is as small as possible.") +print(f" * - The subgroup has size less than {MAX_ORDER} to permit exhaustive testing.") +print(" * - The subgroup admits an endomorphism of the form lambda*(x,y) == (beta*x,y).") +print(" */") +print("#if defined(EXHAUSTIVE_TEST_ORDER)") first = True -for f in sorted(results.keys()): - b = results[f]["b"] - G = results[f]["G"] - print("# %s EXHAUSTIVE_TEST_ORDER == %i" % ("if" if first else "elif", f)) +for f in sorted(solutions.keys()): + b, _, _, _, lam = min(solutions[f]) + print(f"# {'if' if first else 'elif'} EXHAUSTIVE_TEST_ORDER == {f}") first = False - print("static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST(") - print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x," % tuple((int(G[0]) >> (32 * (7 - i))) & 0xffffffff for i in range(4))) - print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x," % tuple((int(G[0]) >> (32 * (7 - i))) & 0xffffffff for i in range(4, 8))) - print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x," % tuple((int(G[1]) >> (32 * (7 - i))) & 0xffffffff for i in range(4))) - print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x" % tuple((int(G[1]) >> (32 * (7 - i))) & 0xffffffff for i in range(4, 8))) - print(");") - print("static const secp256k1_fe secp256k1_fe_const_b = SECP256K1_FE_CONST(") - print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x," % tuple((int(b) >> (32 * (7 - i))) & 0xffffffff for i in range(4))) - print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x" % tuple((int(b) >> (32 * (7 - i))) & 0xffffffff for i in range(4, 8))) - print(");") + print() + print(f"static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_G_ORDER_{f};") + output_b(b) + print() print("# else") print("# error No known generator for the specified exhaustive test group order.") print("# endif") +print("#else") +print() +print("static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_G;") +output_b(7) +print() +print("#endif") +print("/* End of section generated by sage/gen_exhaustive_groups.sage. */") + -print("") -print("") -print("/* To be put in src/scalar_impl.h: */") +print() +print() +print("To be put in src/scalar_impl.h:") +print() +print("/* Begin of section generated by sage/gen_exhaustive_groups.sage. */") first = True -for f in sorted(results.keys()): - lam = results[f]["lambda"] +for f in sorted(solutions.keys()): + _, _, _, _, lam = min(solutions[f]) print("# %s EXHAUSTIVE_TEST_ORDER == %i" % ("if" if first else "elif", f)) first = False print("# define EXHAUSTIVE_TEST_LAMBDA %i" % lam) print("# else") print("# error No known lambda for the specified exhaustive test group order.") print("# endif") -print("") +print("/* End of section generated by sage/gen_exhaustive_groups.sage. */") diff --git a/src/secp256k1/sage/prove_group_implementations.sage b/src/secp256k1/sage/prove_group_implementations.sage index 652bd87f11..23799be52b 100644 --- a/src/secp256k1/sage/prove_group_implementations.sage +++ b/src/secp256k1/sage/prove_group_implementations.sage @@ -148,7 +148,7 @@ def formula_secp256k1_gej_add_ge(branch, a, b): zeroes = {} nonzeroes = {} a_infinity = False - if (branch & 4) != 0: + if (branch & 2) != 0: nonzeroes.update({a.Infinity : 'a_infinite'}) a_infinity = True else: @@ -167,15 +167,11 @@ def formula_secp256k1_gej_add_ge(branch, a, b): m_alt = -u2 tt = u1 * m_alt rr = rr + tt - degenerate = (branch & 3) == 3 - if (branch & 1) != 0: + degenerate = (branch & 1) != 0 + if degenerate: zeroes.update({m : 'm_zero'}) else: nonzeroes.update({m : 'm_nonzero'}) - if (branch & 2) != 0: - zeroes.update({rr : 'rr_zero'}) - else: - nonzeroes.update({rr : 'rr_nonzero'}) rr_alt = s1 rr_alt = rr_alt * 2 m_alt = m_alt + u1 @@ -190,13 +186,6 @@ def formula_secp256k1_gej_add_ge(branch, a, b): n = m t = rr_alt^2 rz = a.Z * m_alt - infinity = False - if (branch & 8) != 0: - if not a_infinity: - infinity = True - zeroes.update({rz : 'r.z=0'}) - else: - nonzeroes.update({rz : 'r.z!=0'}) t = t + q rx = t t = t * 2 @@ -209,8 +198,11 @@ def formula_secp256k1_gej_add_ge(branch, a, b): rx = b.X ry = b.Y rz = 1 - if infinity: + if (branch & 4) != 0: + zeroes.update({rz : 'r.z = 0'}) return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(zero=zeroes, nonzero=nonzeroes), point_at_infinity()) + else: + nonzeroes.update({rz : 'r.z != 0'}) return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(zero=zeroes, nonzero=nonzeroes), jacobianpoint(rx, ry, rz)) def formula_secp256k1_gej_add_ge_old(branch, a, b): @@ -280,14 +272,14 @@ if __name__ == "__main__": success = success & check_symbolic_jacobian_weierstrass("secp256k1_gej_add_var", 0, 7, 5, formula_secp256k1_gej_add_var) success = success & check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge_var", 0, 7, 5, formula_secp256k1_gej_add_ge_var) success = success & check_symbolic_jacobian_weierstrass("secp256k1_gej_add_zinv_var", 0, 7, 5, formula_secp256k1_gej_add_zinv_var) - success = success & check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge", 0, 7, 16, formula_secp256k1_gej_add_ge) + success = success & check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge", 0, 7, 8, formula_secp256k1_gej_add_ge) success = success & (not check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge_old [should fail]", 0, 7, 4, formula_secp256k1_gej_add_ge_old)) if len(sys.argv) >= 2 and sys.argv[1] == "--exhaustive": success = success & check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_var", 0, 7, 5, formula_secp256k1_gej_add_var, 43) success = success & check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge_var", 0, 7, 5, formula_secp256k1_gej_add_ge_var, 43) success = success & check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_zinv_var", 0, 7, 5, formula_secp256k1_gej_add_zinv_var, 43) - success = success & check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge", 0, 7, 16, formula_secp256k1_gej_add_ge, 43) + success = success & check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge", 0, 7, 8, formula_secp256k1_gej_add_ge, 43) success = success & (not check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge_old [should fail]", 0, 7, 4, formula_secp256k1_gej_add_ge_old, 43)) sys.exit(int(not success)) diff --git a/src/secp256k1/src/CMakeLists.txt b/src/secp256k1/src/CMakeLists.txt new file mode 100644 index 0000000000..26272d0950 --- /dev/null +++ b/src/secp256k1/src/CMakeLists.txt @@ -0,0 +1,151 @@ +# Must be included before CMAKE_INSTALL_INCLUDEDIR is used. +include(GNUInstallDirs) +set(${PROJECT_NAME}_installables "") + +if(SECP256K1_ASM STREQUAL "arm") + add_library(common OBJECT + asm/field_10x26_arm.s + ) + set(common_obj "$<TARGET_OBJECTS:common>") +else() + set(common_obj "") +endif() + +add_library(precomputed OBJECT + precomputed_ecmult.c + precomputed_ecmult_gen.c +) +set(internal_obj "$<TARGET_OBJECTS:precomputed>" "${common_obj}") + +add_library(secp256k1 SHARED EXCLUDE_FROM_ALL + secp256k1.c + ${internal_obj} +) +target_include_directories(secp256k1 INTERFACE + $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}> +) +target_compile_definitions(secp256k1 PRIVATE + $<$<PLATFORM_ID:Windows>:DLL_EXPORT> +) +set_target_properties(secp256k1 PROPERTIES + VERSION "${${PROJECT_NAME}_LIB_VERSION_CURRENT}.${${PROJECT_NAME}_LIB_VERSION_AGE}.${${PROJECT_NAME}_LIB_VERSION_REVISION}" + SOVERSION "${${PROJECT_NAME}_LIB_VERSION_CURRENT}" +) +if(SECP256K1_BUILD_SHARED) + get_target_property(use_pic secp256k1 POSITION_INDEPENDENT_CODE) + set_target_properties(precomputed PROPERTIES POSITION_INDEPENDENT_CODE ${use_pic}) + set_target_properties(secp256k1 PROPERTIES EXCLUDE_FROM_ALL FALSE) + list(APPEND ${PROJECT_NAME}_installables secp256k1) +endif() + +add_library(secp256k1_static STATIC EXCLUDE_FROM_ALL + secp256k1.c + ${internal_obj} +) +target_include_directories(secp256k1_static INTERFACE + $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}> +) +if(NOT MSVC) + set_target_properties(secp256k1_static PROPERTIES + OUTPUT_NAME secp256k1 + ) +endif() +if(SECP256K1_BUILD_STATIC) + set_target_properties(secp256k1_static PROPERTIES EXCLUDE_FROM_ALL FALSE) + list(APPEND ${PROJECT_NAME}_installables secp256k1_static) +endif() + +add_library(binary_interface INTERFACE) +target_compile_definitions(binary_interface INTERFACE + $<$<C_COMPILER_ID:MSVC>:_CRT_SECURE_NO_WARNINGS> +) + +add_library(link_library INTERFACE) +if(SECP256K1_BUILD_SHARED) + target_link_libraries(link_library INTERFACE secp256k1) +elseif(SECP256K1_BUILD_STATIC) + target_link_libraries(link_library INTERFACE secp256k1_static) +endif() + +if(SECP256K1_BUILD_BENCHMARK) + add_executable(bench bench.c) + target_link_libraries(bench binary_interface link_library) + add_executable(bench_internal bench_internal.c ${internal_obj}) + target_link_libraries(bench_internal binary_interface) + add_executable(bench_ecmult bench_ecmult.c ${internal_obj}) + target_link_libraries(bench_ecmult binary_interface) +endif() + +if(SECP256K1_BUILD_TESTS) + add_executable(noverify_tests tests.c ${internal_obj}) + target_link_libraries(noverify_tests binary_interface) + add_test(noverify_tests noverify_tests) + if(NOT CMAKE_BUILD_TYPE STREQUAL "Coverage") + add_executable(tests tests.c ${internal_obj}) + target_compile_definitions(tests PRIVATE VERIFY) + target_link_libraries(tests binary_interface) + add_test(tests tests) + endif() +endif() + +if(SECP256K1_BUILD_EXHAUSTIVE_TESTS) + # Note: do not include $<TARGET_OBJECTS:precomputed> in exhaustive_tests (it uses runtime-generated tables). + add_executable(exhaustive_tests tests_exhaustive.c ${common_obj}) + target_compile_definitions(exhaustive_tests PRIVATE $<$<NOT:$<CONFIG:Coverage>>:VERIFY>) + target_link_libraries(exhaustive_tests binary_interface) + add_test(exhaustive_tests exhaustive_tests) +endif() + +if(SECP256K1_BUILD_CTIME_TESTS) + add_executable(ctime_tests ctime_tests.c) + target_link_libraries(ctime_tests binary_interface link_library) +endif() + +install(TARGETS ${${PROJECT_NAME}_installables} + EXPORT ${PROJECT_NAME}-targets + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} +) +set(${PROJECT_NAME}_headers + "${PROJECT_SOURCE_DIR}/include/secp256k1.h" + "${PROJECT_SOURCE_DIR}/include/secp256k1_preallocated.h" +) +if(SECP256K1_ENABLE_MODULE_ECDH) + list(APPEND ${PROJECT_NAME}_headers "${PROJECT_SOURCE_DIR}/include/secp256k1_ecdh.h") +endif() +if(SECP256K1_ENABLE_MODULE_RECOVERY) + list(APPEND ${PROJECT_NAME}_headers "${PROJECT_SOURCE_DIR}/include/secp256k1_recovery.h") +endif() +if(SECP256K1_ENABLE_MODULE_EXTRAKEYS) + list(APPEND ${PROJECT_NAME}_headers "${PROJECT_SOURCE_DIR}/include/secp256k1_extrakeys.h") +endif() +if(SECP256K1_ENABLE_MODULE_SCHNORRSIG) + list(APPEND ${PROJECT_NAME}_headers "${PROJECT_SOURCE_DIR}/include/secp256k1_schnorrsig.h") +endif() +install(FILES ${${PROJECT_NAME}_headers} + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} +) + +install(EXPORT ${PROJECT_NAME}-targets + FILE ${PROJECT_NAME}-targets.cmake + NAMESPACE ${PROJECT_NAME}:: + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} +) + +include(CMakePackageConfigHelpers) +configure_package_config_file( + ${PROJECT_SOURCE_DIR}/cmake/config.cmake.in + ${PROJECT_NAME}-config.cmake + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} + NO_SET_AND_CHECK_MACRO +) +write_basic_package_version_file(${PROJECT_NAME}-config-version.cmake + COMPATIBILITY SameMajorVersion +) +install( + FILES + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} +) diff --git a/src/secp256k1/src/bench.c b/src/secp256k1/src/bench.c index e68021aa28..833f70718b 100644 --- a/src/secp256k1/src/bench.c +++ b/src/secp256k1/src/bench.c @@ -11,7 +11,7 @@ #include "util.h" #include "bench.h" -void help(int default_iters) { +static void help(int default_iters) { printf("Benchmarks the following algorithms:\n"); printf(" - ECDSA signing/verification\n"); diff --git a/src/secp256k1/src/bench.h b/src/secp256k1/src/bench.h index 611ba11f04..bf9a932ff4 100644 --- a/src/secp256k1/src/bench.h +++ b/src/secp256k1/src/bench.h @@ -38,7 +38,7 @@ static int64_t gettime_i64(void) { #define FP_MULT (1000000LL) /* Format fixed point number. */ -void print_number(const int64_t x) { +static void print_number(const int64_t x) { int64_t x_abs, y; int c, i, rounding, g; /* g = integer part size, c = fractional part size */ size_t ptr; @@ -95,7 +95,7 @@ void print_number(const int64_t x) { printf("%-*s", FP_EXP, &buffer[ptr + g]); /* Prints fractional part */ } -void run_benchmark(char *name, void (*benchmark)(void*, int), void (*setup)(void*), void (*teardown)(void*, int), void* data, int count, int iter) { +static void run_benchmark(char *name, void (*benchmark)(void*, int), void (*setup)(void*), void (*teardown)(void*, int), void* data, int count, int iter) { int i; int64_t min = INT64_MAX; int64_t sum = 0; @@ -129,7 +129,7 @@ void run_benchmark(char *name, void (*benchmark)(void*, int), void (*setup)(void printf("\n"); } -int have_flag(int argc, char** argv, char *flag) { +static int have_flag(int argc, char** argv, char *flag) { char** argm = argv + argc; argv++; while (argv != argm) { @@ -145,7 +145,7 @@ int have_flag(int argc, char** argv, char *flag) { returns: - 1 if the user entered an invalid argument - 0 if all the user entered arguments are valid */ -int have_invalid_args(int argc, char** argv, char** valid_args, size_t n) { +static int have_invalid_args(int argc, char** argv, char** valid_args, size_t n) { size_t i; int found_valid; char** argm = argv + argc; @@ -167,7 +167,7 @@ int have_invalid_args(int argc, char** argv, char** valid_args, size_t n) { return 0; } -int get_iters(int default_iters) { +static int get_iters(int default_iters) { char* env = getenv("SECP256K1_BENCH_ITERS"); if (env) { return strtol(env, NULL, 0); @@ -176,7 +176,7 @@ int get_iters(int default_iters) { } } -void print_output_table_header_row(void) { +static void print_output_table_header_row(void) { char* bench_str = "Benchmark"; /* left justified */ char* min_str = " Min(us) "; /* center alignment */ char* avg_str = " Avg(us) "; diff --git a/src/secp256k1/src/bench_ecmult.c b/src/secp256k1/src/bench_ecmult.c index 9d0db340e1..98fb798d82 100644 --- a/src/secp256k1/src/bench_ecmult.c +++ b/src/secp256k1/src/bench_ecmult.c @@ -18,7 +18,7 @@ #define POINTS 32768 -void help(char **argv) { +static void help(char **argv) { printf("Benchmark EC multiplication algorithms\n"); printf("\n"); printf("Usage: %s <help|pippenger_wnaf|strauss_wnaf|simple>\n", argv[0]); diff --git a/src/secp256k1/src/bench_internal.c b/src/secp256k1/src/bench_internal.c index 2224058f64..c248ab8ebc 100644 --- a/src/secp256k1/src/bench_internal.c +++ b/src/secp256k1/src/bench_internal.c @@ -27,7 +27,7 @@ typedef struct { int wnaf[256]; } bench_inv; -void bench_setup(void* arg) { +static void bench_setup(void* arg) { bench_inv *data = (bench_inv*)arg; static const unsigned char init[4][32] = { @@ -79,7 +79,7 @@ void bench_setup(void* arg) { memcpy(data->data + 32, init[1], 32); } -void bench_scalar_add(void* arg, int iters) { +static void bench_scalar_add(void* arg, int iters) { int i, j = 0; bench_inv *data = (bench_inv*)arg; @@ -89,7 +89,7 @@ void bench_scalar_add(void* arg, int iters) { CHECK(j <= iters); } -void bench_scalar_negate(void* arg, int iters) { +static void bench_scalar_negate(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; @@ -98,7 +98,7 @@ void bench_scalar_negate(void* arg, int iters) { } } -void bench_scalar_mul(void* arg, int iters) { +static void bench_scalar_mul(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; @@ -107,18 +107,19 @@ void bench_scalar_mul(void* arg, int iters) { } } -void bench_scalar_split(void* arg, int iters) { +static void bench_scalar_split(void* arg, int iters) { int i, j = 0; bench_inv *data = (bench_inv*)arg; + secp256k1_scalar tmp; for (i = 0; i < iters; i++) { - secp256k1_scalar_split_lambda(&data->scalar[0], &data->scalar[1], &data->scalar[0]); - j += secp256k1_scalar_add(&data->scalar[0], &data->scalar[0], &data->scalar[1]); + secp256k1_scalar_split_lambda(&tmp, &data->scalar[1], &data->scalar[0]); + j += secp256k1_scalar_add(&data->scalar[0], &tmp, &data->scalar[1]); } CHECK(j <= iters); } -void bench_scalar_inverse(void* arg, int iters) { +static void bench_scalar_inverse(void* arg, int iters) { int i, j = 0; bench_inv *data = (bench_inv*)arg; @@ -129,7 +130,7 @@ void bench_scalar_inverse(void* arg, int iters) { CHECK(j <= iters); } -void bench_scalar_inverse_var(void* arg, int iters) { +static void bench_scalar_inverse_var(void* arg, int iters) { int i, j = 0; bench_inv *data = (bench_inv*)arg; @@ -140,7 +141,7 @@ void bench_scalar_inverse_var(void* arg, int iters) { CHECK(j <= iters); } -void bench_field_half(void* arg, int iters) { +static void bench_field_half(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; @@ -149,7 +150,7 @@ void bench_field_half(void* arg, int iters) { } } -void bench_field_normalize(void* arg, int iters) { +static void bench_field_normalize(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; @@ -158,7 +159,7 @@ void bench_field_normalize(void* arg, int iters) { } } -void bench_field_normalize_weak(void* arg, int iters) { +static void bench_field_normalize_weak(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; @@ -167,7 +168,7 @@ void bench_field_normalize_weak(void* arg, int iters) { } } -void bench_field_mul(void* arg, int iters) { +static void bench_field_mul(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; @@ -176,7 +177,7 @@ void bench_field_mul(void* arg, int iters) { } } -void bench_field_sqr(void* arg, int iters) { +static void bench_field_sqr(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; @@ -185,7 +186,7 @@ void bench_field_sqr(void* arg, int iters) { } } -void bench_field_inverse(void* arg, int iters) { +static void bench_field_inverse(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; @@ -195,7 +196,7 @@ void bench_field_inverse(void* arg, int iters) { } } -void bench_field_inverse_var(void* arg, int iters) { +static void bench_field_inverse_var(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; @@ -205,7 +206,7 @@ void bench_field_inverse_var(void* arg, int iters) { } } -void bench_field_sqrt(void* arg, int iters) { +static void bench_field_sqrt(void* arg, int iters) { int i, j = 0; bench_inv *data = (bench_inv*)arg; secp256k1_fe t; @@ -218,7 +219,20 @@ void bench_field_sqrt(void* arg, int iters) { CHECK(j <= iters); } -void bench_group_double_var(void* arg, int iters) { +static void bench_field_is_square_var(void* arg, int iters) { + int i, j = 0; + bench_inv *data = (bench_inv*)arg; + secp256k1_fe t = data->fe[0]; + + for (i = 0; i < iters; i++) { + j += secp256k1_fe_is_square_var(&t); + secp256k1_fe_add(&t, &data->fe[1]); + secp256k1_fe_normalize_var(&t); + } + CHECK(j <= iters); +} + +static void bench_group_double_var(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; @@ -227,7 +241,7 @@ void bench_group_double_var(void* arg, int iters) { } } -void bench_group_add_var(void* arg, int iters) { +static void bench_group_add_var(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; @@ -236,7 +250,7 @@ void bench_group_add_var(void* arg, int iters) { } } -void bench_group_add_affine(void* arg, int iters) { +static void bench_group_add_affine(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; @@ -245,7 +259,7 @@ void bench_group_add_affine(void* arg, int iters) { } } -void bench_group_add_affine_var(void* arg, int iters) { +static void bench_group_add_affine_var(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; @@ -254,7 +268,7 @@ void bench_group_add_affine_var(void* arg, int iters) { } } -void bench_group_add_zinv_var(void* arg, int iters) { +static void bench_group_add_zinv_var(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; @@ -263,7 +277,7 @@ void bench_group_add_zinv_var(void* arg, int iters) { } } -void bench_group_to_affine_var(void* arg, int iters) { +static void bench_group_to_affine_var(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; @@ -283,7 +297,7 @@ void bench_group_to_affine_var(void* arg, int iters) { } } -void bench_ecmult_wnaf(void* arg, int iters) { +static void bench_ecmult_wnaf(void* arg, int iters) { int i, bits = 0, overflow = 0; bench_inv *data = (bench_inv*)arg; @@ -295,7 +309,7 @@ void bench_ecmult_wnaf(void* arg, int iters) { CHECK(bits <= 256*iters); } -void bench_wnaf_const(void* arg, int iters) { +static void bench_wnaf_const(void* arg, int iters) { int i, bits = 0, overflow = 0; bench_inv *data = (bench_inv*)arg; @@ -307,8 +321,7 @@ void bench_wnaf_const(void* arg, int iters) { CHECK(bits <= 256*iters); } - -void bench_sha256(void* arg, int iters) { +static void bench_sha256(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; secp256k1_sha256 sha; @@ -320,7 +333,7 @@ void bench_sha256(void* arg, int iters) { } } -void bench_hmac_sha256(void* arg, int iters) { +static void bench_hmac_sha256(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; secp256k1_hmac_sha256 hmac; @@ -332,7 +345,7 @@ void bench_hmac_sha256(void* arg, int iters) { } } -void bench_rfc6979_hmac_sha256(void* arg, int iters) { +static void bench_rfc6979_hmac_sha256(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; secp256k1_rfc6979_hmac_sha256 rng; @@ -343,7 +356,7 @@ void bench_rfc6979_hmac_sha256(void* arg, int iters) { } } -void bench_context(void* arg, int iters) { +static void bench_context(void* arg, int iters) { int i; (void)arg; for (i = 0; i < iters; i++) { @@ -371,6 +384,7 @@ int main(int argc, char **argv) { if (d || have_flag(argc, argv, "field") || have_flag(argc, argv, "mul")) run_benchmark("field_mul", bench_field_mul, bench_setup, NULL, &data, 10, iters*10); if (d || have_flag(argc, argv, "field") || have_flag(argc, argv, "inverse")) run_benchmark("field_inverse", bench_field_inverse, bench_setup, NULL, &data, 10, iters); if (d || have_flag(argc, argv, "field") || have_flag(argc, argv, "inverse")) run_benchmark("field_inverse_var", bench_field_inverse_var, bench_setup, NULL, &data, 10, iters); + if (d || have_flag(argc, argv, "field") || have_flag(argc, argv, "issquare")) run_benchmark("field_is_square_var", bench_field_is_square_var, bench_setup, NULL, &data, 10, iters); if (d || have_flag(argc, argv, "field") || have_flag(argc, argv, "sqrt")) run_benchmark("field_sqrt", bench_field_sqrt, bench_setup, NULL, &data, 10, iters); if (d || have_flag(argc, argv, "group") || have_flag(argc, argv, "double")) run_benchmark("group_double_var", bench_group_double_var, bench_setup, NULL, &data, 10, iters*10); diff --git a/src/secp256k1/src/checkmem.h b/src/secp256k1/src/checkmem.h new file mode 100644 index 0000000000..571e4cc389 --- /dev/null +++ b/src/secp256k1/src/checkmem.h @@ -0,0 +1,88 @@ +/*********************************************************************** + * Copyright (c) 2022 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +/* The code here is inspired by Kris Kwiatkowski's approach in + * https://github.com/kriskwiatkowski/pqc/blob/main/src/common/ct_check.h + * to provide a general interface for memory-checking mechanisms, primarily + * for constant-time checking. + */ + +/* These macros are defined by this header file: + * + * - SECP256K1_CHECKMEM_ENABLED: + * - 1 if memory-checking integration is available, 0 otherwise. + * This is just a compile-time macro. Use the next macro to check it is actually + * available at runtime. + * - SECP256K1_CHECKMEM_RUNNING(): + * - Acts like a function call, returning 1 if memory checking is available + * at runtime. + * - SECP256K1_CHECKMEM_CHECK(p, len): + * - Assert or otherwise fail in case the len-byte memory block pointed to by p is + * not considered entirely defined. + * - SECP256K1_CHECKMEM_CHECK_VERIFY(p, len): + * - Like SECP256K1_CHECKMEM_CHECK, but only works in VERIFY mode. + * - SECP256K1_CHECKMEM_UNDEFINE(p, len): + * - marks the len-byte memory block pointed to by p as undefined data (secret data, + * in the context of constant-time checking). + * - SECP256K1_CHECKMEM_DEFINE(p, len): + * - marks the len-byte memory pointed to by p as defined data (public data, in the + * context of constant-time checking). + * + */ + +#ifndef SECP256K1_CHECKMEM_H +#define SECP256K1_CHECKMEM_H + +/* Define a statement-like macro that ignores the arguments. */ +#define SECP256K1_CHECKMEM_NOOP(p, len) do { (void)(p); (void)(len); } while(0) + +/* If compiling under msan, map the SECP256K1_CHECKMEM_* functionality to msan. + * Choose this preferentially, even when VALGRIND is defined, as msan-compiled + * binaries can't be run under valgrind anyway. */ +#if defined(__has_feature) +# if __has_feature(memory_sanitizer) +# include <sanitizer/msan_interface.h> +# define SECP256K1_CHECKMEM_ENABLED 1 +# define SECP256K1_CHECKMEM_UNDEFINE(p, len) __msan_allocated_memory((p), (len)) +# define SECP256K1_CHECKMEM_DEFINE(p, len) __msan_unpoison((p), (len)) +# define SECP256K1_CHECKMEM_CHECK(p, len) __msan_check_mem_is_initialized((p), (len)) +# define SECP256K1_CHECKMEM_RUNNING() (1) +# endif +#endif + +/* If valgrind integration is desired (through the VALGRIND define), implement the + * SECP256K1_CHECKMEM_* macros using valgrind. */ +#if !defined SECP256K1_CHECKMEM_ENABLED +# if defined VALGRIND +# include <stddef.h> +# include <valgrind/memcheck.h> +# define SECP256K1_CHECKMEM_ENABLED 1 +# define SECP256K1_CHECKMEM_UNDEFINE(p, len) VALGRIND_MAKE_MEM_UNDEFINED((p), (len)) +# define SECP256K1_CHECKMEM_DEFINE(p, len) VALGRIND_MAKE_MEM_DEFINED((p), (len)) +# define SECP256K1_CHECKMEM_CHECK(p, len) VALGRIND_CHECK_MEM_IS_DEFINED((p), (len)) + /* VALGRIND_MAKE_MEM_DEFINED returns 0 iff not running on memcheck. + * This is more precise than the RUNNING_ON_VALGRIND macro, which + * checks for valgrind in general instead of memcheck specifically. */ +# define SECP256K1_CHECKMEM_RUNNING() (VALGRIND_MAKE_MEM_DEFINED(NULL, 0) != 0) +# endif +#endif + +/* As a fall-back, map these macros to dummy statements. */ +#if !defined SECP256K1_CHECKMEM_ENABLED +# define SECP256K1_CHECKMEM_ENABLED 0 +# define SECP256K1_CHECKMEM_UNDEFINE(p, len) SECP256K1_CHECKMEM_NOOP((p), (len)) +# define SECP256K1_CHECKMEM_DEFINE(p, len) SECP256K1_CHECKMEM_NOOP((p), (len)) +# define SECP256K1_CHECKMEM_CHECK(p, len) SECP256K1_CHECKMEM_NOOP((p), (len)) +# define SECP256K1_CHECKMEM_RUNNING() (0) +#endif + +#if defined VERIFY +#define SECP256K1_CHECKMEM_CHECK_VERIFY(p, len) SECP256K1_CHECKMEM_CHECK((p), (len)) +#else +#define SECP256K1_CHECKMEM_CHECK_VERIFY(p, len) SECP256K1_CHECKMEM_NOOP((p), (len)) +#endif + +#endif /* SECP256K1_CHECKMEM_H */ diff --git a/src/secp256k1/src/valgrind_ctime_test.c b/src/secp256k1/src/ctime_tests.c index a0f888b00f..713eb427d3 100644 --- a/src/secp256k1/src/valgrind_ctime_test.c +++ b/src/secp256k1/src/ctime_tests.c @@ -4,12 +4,15 @@ * 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" +#include "checkmem.h" + +#if !SECP256K1_CHECKMEM_ENABLED +# error "This tool cannot be compiled without memory-checking interface (valgrind or msan)" +#endif #ifdef ENABLE_MODULE_ECDH # include "../include/secp256k1_ecdh.h" @@ -27,16 +30,16 @@ #include "../include/secp256k1_schnorrsig.h" #endif -void run_tests(secp256k1_context *ctx, unsigned char *key); +static 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"); + if (!SECP256K1_CHECKMEM_RUNNING()) { + fprintf(stderr, "This test can only usefully be run inside valgrind because it was not compiled under msan.\n"); + fprintf(stderr, "Usage: libtool --mode=execute valgrind ./ctime_tests\n"); return 1; } ctx = secp256k1_context_create(SECP256K1_CONTEXT_DECLASSIFY); @@ -51,16 +54,16 @@ int main(void) { /* Test context randomisation. Do this last because it leaves the context * tainted. */ - VALGRIND_MAKE_MEM_UNDEFINED(key, 32); + SECP256K1_CHECKMEM_UNDEFINE(key, 32); ret = secp256k1_context_randomize(ctx, key); - VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); CHECK(ret); secp256k1_context_destroy(ctx); return 0; } -void run_tests(secp256k1_context *ctx, unsigned char *key) { +static void run_tests(secp256k1_context *ctx, unsigned char *key) { secp256k1_ecdsa_signature signature; secp256k1_pubkey pubkey; size_t siglen = 74; @@ -83,89 +86,89 @@ void run_tests(secp256k1_context *ctx, unsigned char *key) { } /* Test keygen. */ - VALGRIND_MAKE_MEM_UNDEFINED(key, 32); + SECP256K1_CHECKMEM_UNDEFINE(key, 32); ret = secp256k1_ec_pubkey_create(ctx, &pubkey, key); - VALGRIND_MAKE_MEM_DEFINED(&pubkey, sizeof(secp256k1_pubkey)); - VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); + SECP256K1_CHECKMEM_DEFINE(&pubkey, sizeof(secp256k1_pubkey)); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); CHECK(ret); CHECK(secp256k1_ec_pubkey_serialize(ctx, spubkey, &outputlen, &pubkey, SECP256K1_EC_COMPRESSED) == 1); /* Test signing. */ - VALGRIND_MAKE_MEM_UNDEFINED(key, 32); + SECP256K1_CHECKMEM_UNDEFINE(key, 32); ret = secp256k1_ecdsa_sign(ctx, &signature, msg, key, NULL, NULL); - VALGRIND_MAKE_MEM_DEFINED(&signature, sizeof(secp256k1_ecdsa_signature)); - VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); + SECP256K1_CHECKMEM_DEFINE(&signature, sizeof(secp256k1_ecdsa_signature)); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); CHECK(ret); CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, sig, &siglen, &signature)); #ifdef ENABLE_MODULE_ECDH /* Test ECDH. */ - VALGRIND_MAKE_MEM_UNDEFINED(key, 32); + SECP256K1_CHECKMEM_UNDEFINE(key, 32); ret = secp256k1_ecdh(ctx, msg, &pubkey, key, NULL, NULL); - VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); CHECK(ret == 1); #endif #ifdef ENABLE_MODULE_RECOVERY /* Test signing a recoverable signature. */ - VALGRIND_MAKE_MEM_UNDEFINED(key, 32); + SECP256K1_CHECKMEM_UNDEFINE(key, 32); ret = secp256k1_ecdsa_sign_recoverable(ctx, &recoverable_signature, msg, key, NULL, NULL); - VALGRIND_MAKE_MEM_DEFINED(&recoverable_signature, sizeof(recoverable_signature)); - VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); + SECP256K1_CHECKMEM_DEFINE(&recoverable_signature, sizeof(recoverable_signature)); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); CHECK(ret); CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(ctx, sig, &recid, &recoverable_signature)); CHECK(recid >= 0 && recid <= 3); #endif - VALGRIND_MAKE_MEM_UNDEFINED(key, 32); + SECP256K1_CHECKMEM_UNDEFINE(key, 32); ret = secp256k1_ec_seckey_verify(ctx, key); - VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); CHECK(ret == 1); - VALGRIND_MAKE_MEM_UNDEFINED(key, 32); + SECP256K1_CHECKMEM_UNDEFINE(key, 32); ret = secp256k1_ec_seckey_negate(ctx, key); - VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); CHECK(ret == 1); - VALGRIND_MAKE_MEM_UNDEFINED(key, 32); - VALGRIND_MAKE_MEM_UNDEFINED(msg, 32); + SECP256K1_CHECKMEM_UNDEFINE(key, 32); + SECP256K1_CHECKMEM_UNDEFINE(msg, 32); ret = secp256k1_ec_seckey_tweak_add(ctx, key, msg); - VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); CHECK(ret == 1); - VALGRIND_MAKE_MEM_UNDEFINED(key, 32); - VALGRIND_MAKE_MEM_UNDEFINED(msg, 32); + SECP256K1_CHECKMEM_UNDEFINE(key, 32); + SECP256K1_CHECKMEM_UNDEFINE(msg, 32); ret = secp256k1_ec_seckey_tweak_mul(ctx, key, msg); - VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); CHECK(ret == 1); /* Test keypair_create and keypair_xonly_tweak_add. */ #ifdef ENABLE_MODULE_EXTRAKEYS - VALGRIND_MAKE_MEM_UNDEFINED(key, 32); + SECP256K1_CHECKMEM_UNDEFINE(key, 32); ret = secp256k1_keypair_create(ctx, &keypair, key); - VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); CHECK(ret == 1); /* The tweak is not treated as a secret in keypair_tweak_add */ - VALGRIND_MAKE_MEM_DEFINED(msg, 32); + SECP256K1_CHECKMEM_DEFINE(msg, 32); ret = secp256k1_keypair_xonly_tweak_add(ctx, &keypair, msg); - VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); CHECK(ret == 1); - VALGRIND_MAKE_MEM_UNDEFINED(key, 32); - VALGRIND_MAKE_MEM_UNDEFINED(&keypair, sizeof(keypair)); + SECP256K1_CHECKMEM_UNDEFINE(key, 32); + SECP256K1_CHECKMEM_UNDEFINE(&keypair, sizeof(keypair)); ret = secp256k1_keypair_sec(ctx, key, &keypair); - VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); CHECK(ret == 1); #endif #ifdef ENABLE_MODULE_SCHNORRSIG - VALGRIND_MAKE_MEM_UNDEFINED(key, 32); + SECP256K1_CHECKMEM_UNDEFINE(key, 32); ret = secp256k1_keypair_create(ctx, &keypair, key); - VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); CHECK(ret == 1); ret = secp256k1_schnorrsig_sign32(ctx, sig, msg, &keypair, NULL); - VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); CHECK(ret == 1); #endif } diff --git a/src/secp256k1/src/field.h b/src/secp256k1/src/field.h index 2584a494ee..64ceead4d2 100644 --- a/src/secp256k1/src/field.h +++ b/src/secp256k1/src/field.h @@ -18,10 +18,6 @@ * imply normality. */ -#if defined HAVE_CONFIG_H -#include "libsecp256k1-config.h" -#endif - #include "util.h" #if defined(SECP256K1_WIDEMUL_INT128) @@ -89,6 +85,9 @@ static void secp256k1_fe_get_b32(unsigned char *r, const secp256k1_fe *a); * as an argument. The magnitude of the output is one higher. */ static void secp256k1_fe_negate(secp256k1_fe *r, const secp256k1_fe *a, int m); +/** Adds a small integer (up to 0x7FFF) to r. The resulting magnitude increases by one. */ +static void secp256k1_fe_add_int(secp256k1_fe *r, int a); + /** Multiplies the passed field element with a small integer constant. Multiplies the magnitude by that * small integer. */ static void secp256k1_fe_mul_int(secp256k1_fe *r, int a); @@ -139,4 +138,7 @@ static void secp256k1_fe_half(secp256k1_fe *r); * magnitude set to 'm' and is normalized if (and only if) 'm' is zero. */ static void secp256k1_fe_get_bounds(secp256k1_fe *r, int m); +/** Determine whether a is a square (modulo p). */ +static int secp256k1_fe_is_square_var(const secp256k1_fe *a); + #endif /* SECP256K1_FIELD_H */ diff --git a/src/secp256k1/src/field_10x26_impl.h b/src/secp256k1/src/field_10x26_impl.h index 21742bf6eb..46b72ce78d 100644 --- a/src/secp256k1/src/field_10x26_impl.h +++ b/src/secp256k1/src/field_10x26_impl.h @@ -7,6 +7,7 @@ #ifndef SECP256K1_FIELD_REPR_IMPL_H #define SECP256K1_FIELD_REPR_IMPL_H +#include "checkmem.h" #include "util.h" #include "field.h" #include "modinv32_impl.h" @@ -481,6 +482,20 @@ SECP256K1_INLINE static void secp256k1_fe_add(secp256k1_fe *r, const secp256k1_f #endif } +SECP256K1_INLINE static void secp256k1_fe_add_int(secp256k1_fe *r, int a) { +#ifdef VERIFY + secp256k1_fe_verify(r); + VERIFY_CHECK(a >= 0); + VERIFY_CHECK(a <= 0x7FFF); +#endif + r->n[0] += a; +#ifdef VERIFY + r->magnitude += 1; + r->normalized = 0; + secp256k1_fe_verify(r); +#endif +} + #if defined(USE_EXTERNAL_ASM) /* External assembler implementation */ @@ -1132,7 +1147,7 @@ static void secp256k1_fe_sqr(secp256k1_fe *r, const secp256k1_fe *a) { static SECP256K1_INLINE void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag) { uint32_t mask0, mask1; - VG_CHECK_VERIFY(r->n, sizeof(r->n)); + SECP256K1_CHECKMEM_CHECK_VERIFY(r->n, sizeof(r->n)); mask0 = flag + ~((uint32_t)0); mask1 = ~mask0; r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1); @@ -1231,7 +1246,7 @@ static SECP256K1_INLINE void secp256k1_fe_half(secp256k1_fe *r) { static SECP256K1_INLINE void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, const secp256k1_fe_storage *a, int flag) { uint32_t mask0, mask1; - VG_CHECK_VERIFY(r->n, sizeof(r->n)); + SECP256K1_CHECKMEM_CHECK_VERIFY(r->n, sizeof(r->n)); mask0 = flag + ~((uint32_t)0); mask1 = ~mask0; r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1); @@ -1364,4 +1379,31 @@ static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *x) { VERIFY_CHECK(secp256k1_fe_normalizes_to_zero(r) == secp256k1_fe_normalizes_to_zero(&tmp)); } +static int secp256k1_fe_is_square_var(const secp256k1_fe *x) { + secp256k1_fe tmp; + secp256k1_modinv32_signed30 s; + int jac, ret; + + tmp = *x; + secp256k1_fe_normalize_var(&tmp); + /* secp256k1_jacobi32_maybe_var cannot deal with input 0. */ + if (secp256k1_fe_is_zero(&tmp)) return 1; + secp256k1_fe_to_signed30(&s, &tmp); + jac = secp256k1_jacobi32_maybe_var(&s, &secp256k1_const_modinfo_fe); + if (jac == 0) { + /* secp256k1_jacobi32_maybe_var failed to compute the Jacobi symbol. Fall back + * to computing a square root. This should be extremely rare with random + * input (except in VERIFY mode, where a lower iteration count is used). */ + secp256k1_fe dummy; + ret = secp256k1_fe_sqrt(&dummy, &tmp); + } else { +#ifdef VERIFY + secp256k1_fe dummy; + VERIFY_CHECK(jac == 2*secp256k1_fe_sqrt(&dummy, &tmp) - 1); +#endif + ret = jac >= 0; + } + return ret; +} + #endif /* SECP256K1_FIELD_REPR_IMPL_H */ diff --git a/src/secp256k1/src/field_5x52_impl.h b/src/secp256k1/src/field_5x52_impl.h index 6bd202f587..4c4466eceb 100644 --- a/src/secp256k1/src/field_5x52_impl.h +++ b/src/secp256k1/src/field_5x52_impl.h @@ -7,10 +7,7 @@ #ifndef SECP256K1_FIELD_REPR_IMPL_H #define SECP256K1_FIELD_REPR_IMPL_H -#if defined HAVE_CONFIG_H -#include "libsecp256k1-config.h" -#endif - +#include "checkmem.h" #include "util.h" #include "field.h" #include "modinv64_impl.h" @@ -428,6 +425,20 @@ SECP256K1_INLINE static void secp256k1_fe_mul_int(secp256k1_fe *r, int a) { #endif } +SECP256K1_INLINE static void secp256k1_fe_add_int(secp256k1_fe *r, int a) { +#ifdef VERIFY + secp256k1_fe_verify(r); + VERIFY_CHECK(a >= 0); + VERIFY_CHECK(a <= 0x7FFF); +#endif + r->n[0] += a; +#ifdef VERIFY + r->magnitude += 1; + r->normalized = 0; + secp256k1_fe_verify(r); +#endif +} + SECP256K1_INLINE static void secp256k1_fe_add(secp256k1_fe *r, const secp256k1_fe *a) { #ifdef VERIFY secp256k1_fe_verify(a); @@ -476,7 +487,7 @@ static void secp256k1_fe_sqr(secp256k1_fe *r, const secp256k1_fe *a) { static SECP256K1_INLINE void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag) { uint64_t mask0, mask1; - VG_CHECK_VERIFY(r->n, sizeof(r->n)); + SECP256K1_CHECKMEM_CHECK_VERIFY(r->n, sizeof(r->n)); mask0 = flag + ~((uint64_t)0); mask1 = ~mask0; r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1); @@ -559,7 +570,7 @@ static SECP256K1_INLINE void secp256k1_fe_half(secp256k1_fe *r) { static SECP256K1_INLINE void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, const secp256k1_fe_storage *a, int flag) { uint64_t mask0, mask1; - VG_CHECK_VERIFY(r->n, sizeof(r->n)); + SECP256K1_CHECKMEM_CHECK_VERIFY(r->n, sizeof(r->n)); mask0 = flag + ~((uint64_t)0); mask1 = ~mask0; r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1); @@ -667,4 +678,31 @@ static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *x) { #endif } +static int secp256k1_fe_is_square_var(const secp256k1_fe *x) { + secp256k1_fe tmp; + secp256k1_modinv64_signed62 s; + int jac, ret; + + tmp = *x; + secp256k1_fe_normalize_var(&tmp); + /* secp256k1_jacobi64_maybe_var cannot deal with input 0. */ + if (secp256k1_fe_is_zero(&tmp)) return 1; + secp256k1_fe_to_signed62(&s, &tmp); + jac = secp256k1_jacobi64_maybe_var(&s, &secp256k1_const_modinfo_fe); + if (jac == 0) { + /* secp256k1_jacobi64_maybe_var failed to compute the Jacobi symbol. Fall back + * to computing a square root. This should be extremely rare with random + * input (except in VERIFY mode, where a lower iteration count is used). */ + secp256k1_fe dummy; + ret = secp256k1_fe_sqrt(&dummy, &tmp); + } else { +#ifdef VERIFY + secp256k1_fe dummy; + VERIFY_CHECK(jac == 2*secp256k1_fe_sqrt(&dummy, &tmp) - 1); +#endif + ret = jac >= 0; + } + return ret; +} + #endif /* SECP256K1_FIELD_REPR_IMPL_H */ diff --git a/src/secp256k1/src/field_impl.h b/src/secp256k1/src/field_impl.h index 0a4a04d9ac..0a03076bbc 100644 --- a/src/secp256k1/src/field_impl.h +++ b/src/secp256k1/src/field_impl.h @@ -7,10 +7,6 @@ #ifndef SECP256K1_FIELD_IMPL_H #define SECP256K1_FIELD_IMPL_H -#if defined HAVE_CONFIG_H -#include "libsecp256k1-config.h" -#endif - #include "util.h" #if defined(SECP256K1_WIDEMUL_INT128) diff --git a/src/secp256k1/src/group_impl.h b/src/secp256k1/src/group_impl.h index dfe6e32c7f..82ce3f8d8b 100644 --- a/src/secp256k1/src/group_impl.h +++ b/src/secp256k1/src/group_impl.h @@ -10,59 +10,69 @@ #include "field.h" #include "group.h" +/* Begin of section generated by sage/gen_exhaustive_groups.sage. */ +#define SECP256K1_G_ORDER_7 SECP256K1_GE_CONST(\ + 0x66625d13, 0x317ffe44, 0x63d32cff, 0x1ca02b9b,\ + 0xe5c6d070, 0x50b4b05e, 0x81cc30db, 0xf5166f0a,\ + 0x1e60e897, 0xa7c00c7c, 0x2df53eb6, 0x98274ff4,\ + 0x64252f42, 0x8ca44e17, 0x3b25418c, 0xff4ab0cf\ +) #define SECP256K1_G_ORDER_13 SECP256K1_GE_CONST(\ - 0xc3459c3d, 0x35326167, 0xcd86cce8, 0x07a2417f,\ - 0x5b8bd567, 0xde8538ee, 0x0d507b0c, 0xd128f5bb,\ - 0x8e467fec, 0xcd30000a, 0x6cc1184e, 0x25d382c2,\ - 0xa2f4494e, 0x2fbe9abc, 0x8b64abac, 0xd005fb24\ + 0xa2482ff8, 0x4bf34edf, 0xa51262fd, 0xe57921db,\ + 0xe0dd2cb7, 0xa5914790, 0xbc71631f, 0xc09704fb,\ + 0x942536cb, 0xa3e49492, 0x3a701cc3, 0xee3e443f,\ + 0xdf182aa9, 0x15b8aa6a, 0x166d3b19, 0xba84b045\ ) #define SECP256K1_G_ORDER_199 SECP256K1_GE_CONST(\ - 0x226e653f, 0xc8df7744, 0x9bacbf12, 0x7d1dcbf9,\ - 0x87f05b2a, 0xe7edbd28, 0x1f564575, 0xc48dcf18,\ - 0xa13872c2, 0xe933bb17, 0x5d9ffd5b, 0xb5b6e10c,\ - 0x57fe3c00, 0xbaaaa15a, 0xe003ec3e, 0x9c269bae\ + 0x7fb07b5c, 0xd07c3bda, 0x553902e2, 0x7a87ea2c,\ + 0x35108a7f, 0x051f41e5, 0xb76abad5, 0x1f2703ad,\ + 0x0a251539, 0x5b4c4438, 0x952a634f, 0xac10dd4d,\ + 0x6d6f4745, 0x98990c27, 0x3a4f3116, 0xd32ff969\ ) /** Generator for secp256k1, value 'g' defined in * "Standards for Efficient Cryptography" (SEC2) 2.7.1. */ #define SECP256K1_G SECP256K1_GE_CONST(\ - 0x79BE667EUL, 0xF9DCBBACUL, 0x55A06295UL, 0xCE870B07UL,\ - 0x029BFCDBUL, 0x2DCE28D9UL, 0x59F2815BUL, 0x16F81798UL,\ - 0x483ADA77UL, 0x26A3C465UL, 0x5DA4FBFCUL, 0x0E1108A8UL,\ - 0xFD17B448UL, 0xA6855419UL, 0x9C47D08FUL, 0xFB10D4B8UL\ + 0x79be667e, 0xf9dcbbac, 0x55a06295, 0xce870b07,\ + 0x029bfcdb, 0x2dce28d9, 0x59f2815b, 0x16f81798,\ + 0x483ada77, 0x26a3c465, 0x5da4fbfc, 0x0e1108a8,\ + 0xfd17b448, 0xa6855419, 0x9c47d08f, 0xfb10d4b8\ ) /* These exhaustive group test orders and generators are chosen such that: * - The field size is equal to that of secp256k1, so field code is the same. - * - The curve equation is of the form y^2=x^3+B for some constant B. - * - The subgroup has a generator 2*P, where P.x=1. + * - The curve equation is of the form y^2=x^3+B for some small constant B. + * - The subgroup has a generator 2*P, where P.x is as small as possible. * - The subgroup has size less than 1000 to permit exhaustive testing. * - The subgroup admits an endomorphism of the form lambda*(x,y) == (beta*x,y). - * - * These parameters are generated using sage/gen_exhaustive_groups.sage. */ #if defined(EXHAUSTIVE_TEST_ORDER) -# if EXHAUSTIVE_TEST_ORDER == 13 +# if EXHAUSTIVE_TEST_ORDER == 7 + +static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_G_ORDER_7; +#define SECP256K1_B 6 + +# elif EXHAUSTIVE_TEST_ORDER == 13 + static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_G_ORDER_13; +#define SECP256K1_B 2 -static const secp256k1_fe secp256k1_fe_const_b = SECP256K1_FE_CONST( - 0x3d3486b2, 0x159a9ca5, 0xc75638be, 0xb23a69bc, - 0x946a45ab, 0x24801247, 0xb4ed2b8e, 0x26b6a417 -); # elif EXHAUSTIVE_TEST_ORDER == 199 + static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_G_ORDER_199; +#define SECP256K1_B 4 -static const secp256k1_fe secp256k1_fe_const_b = SECP256K1_FE_CONST( - 0x2cca28fa, 0xfc614b80, 0x2a3db42b, 0x00ba00b1, - 0xbea8d943, 0xdace9ab2, 0x9536daea, 0x0074defb -); # else # error No known generator for the specified exhaustive test group order. # endif #else + static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_G; +#define SECP256K1_B 7 -static const secp256k1_fe secp256k1_fe_const_b = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 7); #endif +/* End of section generated by sage/gen_exhaustive_groups.sage. */ + +static const secp256k1_fe secp256k1_fe_const_b = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, SECP256K1_B); static void secp256k1_ge_set_gej_zinv(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zi) { secp256k1_fe zi2; @@ -217,7 +227,7 @@ static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int o secp256k1_fe_sqr(&x2, x); secp256k1_fe_mul(&x3, x, &x2); r->infinity = 0; - secp256k1_fe_add(&x3, &secp256k1_fe_const_b); + secp256k1_fe_add_int(&x3, SECP256K1_B); if (!secp256k1_fe_sqrt(&r->y, &x3)) { return 0; } @@ -272,7 +282,7 @@ static int secp256k1_ge_is_valid_var(const secp256k1_ge *a) { /* y^2 = x^3 + 7 */ secp256k1_fe_sqr(&y2, &a->y); secp256k1_fe_sqr(&x3, &a->x); secp256k1_fe_mul(&x3, &x3, &a->x); - secp256k1_fe_add(&x3, &secp256k1_fe_const_b); + secp256k1_fe_add_int(&x3, SECP256K1_B); secp256k1_fe_normalize_weak(&x3); return secp256k1_fe_equal_var(&y2, &x3); } @@ -522,11 +532,11 @@ static void secp256k1_gej_add_ge(secp256k1_gej *r, const secp256k1_gej *a, const /* Operations: 7 mul, 5 sqr, 24 add/cmov/half/mul_int/negate/normalize_weak/normalizes_to_zero */ secp256k1_fe zz, u1, u2, s1, s2, t, tt, m, n, q, rr; secp256k1_fe m_alt, rr_alt; - int infinity, degenerate; + int degenerate; VERIFY_CHECK(!b->infinity); VERIFY_CHECK(a->infinity == 0 || a->infinity == 1); - /** In: + /* In: * Eric Brier and Marc Joye, Weierstrass Elliptic Curves and Side-Channel Attacks. * In D. Naccache and P. Paillier, Eds., Public Key Cryptography, vol. 2274 of Lecture Notes in Computer Science, pages 335-345. Springer-Verlag, 2002. * we find as solution for a unified addition/doubling formula: @@ -588,10 +598,9 @@ static void secp256k1_gej_add_ge(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_fe_negate(&m_alt, &u2, 1); /* Malt = -X2*Z1^2 */ secp256k1_fe_mul(&tt, &u1, &m_alt); /* tt = -U1*U2 (2) */ secp256k1_fe_add(&rr, &tt); /* rr = R = T^2-U1*U2 (3) */ - /** If lambda = R/M = 0/0 we have a problem (except in the "trivial" - * case that Z = z1z2 = 0, and this is special-cased later on). */ - degenerate = secp256k1_fe_normalizes_to_zero(&m) & - secp256k1_fe_normalizes_to_zero(&rr); + /* If lambda = R/M = R/0 we have a problem (except in the "trivial" + * case that Z = z1z2 = 0, and this is special-cased later on). */ + degenerate = secp256k1_fe_normalizes_to_zero(&m); /* This only occurs when y1 == -y2 and x1^3 == x2^3, but x1 != x2. * This means either x1 == beta*x2 or beta*x1 == x2, where beta is * a nontrivial cube root of one. In either case, an alternate @@ -603,7 +612,7 @@ static void secp256k1_gej_add_ge(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_fe_cmov(&rr_alt, &rr, !degenerate); secp256k1_fe_cmov(&m_alt, &m, !degenerate); - /* Now Ralt / Malt = lambda and is guaranteed not to be 0/0. + /* Now Ralt / Malt = lambda and is guaranteed not to be Ralt / 0. * From here on out Ralt and Malt represent the numerator * and denominator of lambda; R and M represent the explicit * expressions x1^2 + x2^2 + x1x2 and y1 + y2. */ @@ -618,7 +627,6 @@ 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 = Z3 = Malt*Z (1) */ - infinity = secp256k1_fe_normalizes_to_zero(&r->z) & ~a->infinity; secp256k1_fe_add(&t, &q); /* t = Ralt^2 + Q (2) */ r->x = t; /* r->x = X3 = Ralt^2 + Q (2) */ secp256k1_fe_mul_int(&t, 2); /* t = 2*X3 (4) */ @@ -628,11 +636,28 @@ static void secp256k1_gej_add_ge(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_fe_negate(&r->y, &t, 3); /* r->y = -(Ralt*(2*X3 + Q) + M^3*Malt) (4) */ secp256k1_fe_half(&r->y); /* r->y = Y3 = -(Ralt*(2*X3 + Q) + M^3*Malt)/2 (3) */ - /** In case a->infinity == 1, replace r with (b->x, b->y, 1). */ + /* In case a->infinity == 1, replace r with (b->x, b->y, 1). */ secp256k1_fe_cmov(&r->x, &b->x, a->infinity); secp256k1_fe_cmov(&r->y, &b->y, a->infinity); secp256k1_fe_cmov(&r->z, &secp256k1_fe_one, a->infinity); - r->infinity = infinity; + + /* Set r->infinity if r->z is 0. + * + * If a->infinity is set, then r->infinity = (r->z == 0) = (1 == 0) = false, + * which is correct because the function assumes that b is not infinity. + * + * Now assume !a->infinity. This implies Z = Z1 != 0. + * + * Case y1 = -y2: + * In this case we could have a = -b, namely if x1 = x2. + * We have degenerate = true, r->z = (x1 - x2) * Z. + * Then r->infinity = ((x1 - x2)Z == 0) = (x1 == x2) = (a == -b). + * + * Case y1 != -y2: + * In this case, we can't have a = -b. + * We have degenerate = false, r->z = (y1 + y2) * Z. + * Then r->infinity = ((y1 + y2)Z == 0) = (y1 == -y2) = false. */ + r->infinity = secp256k1_fe_normalizes_to_zero(&r->z); } static void secp256k1_gej_rescale(secp256k1_gej *r, const secp256k1_fe *s) { diff --git a/src/secp256k1/src/int128.h b/src/secp256k1/src/int128.h index 84d969a236..5355fbfae0 100644 --- a/src/secp256k1/src/int128.h +++ b/src/secp256k1/src/int128.h @@ -66,7 +66,12 @@ static SECP256K1_INLINE void secp256k1_i128_det(secp256k1_int128 *r, int64_t a, */ static SECP256K1_INLINE void secp256k1_i128_rshift(secp256k1_int128 *r, unsigned int b); -/* Return the low 64-bits of a 128-bit value interpreted as an signed 64-bit value. */ +/* Return the input value modulo 2^64. */ +static SECP256K1_INLINE uint64_t secp256k1_i128_to_u64(const secp256k1_int128 *a); + +/* Return the value as a signed 64-bit value. + * Requires the input to be between INT64_MIN and INT64_MAX. + */ static SECP256K1_INLINE int64_t secp256k1_i128_to_i64(const secp256k1_int128 *a); /* Write a signed 64-bit value to r. */ @@ -75,10 +80,10 @@ static SECP256K1_INLINE void secp256k1_i128_from_i64(secp256k1_int128 *r, int64_ /* Compare two 128-bit values for equality. */ static SECP256K1_INLINE int secp256k1_i128_eq_var(const secp256k1_int128 *a, const secp256k1_int128 *b); -/* Tests if r is equal to 2^n. +/* Tests if r is equal to sign*2^n (sign must be 1 or -1). * n must be strictly less than 127. */ -static SECP256K1_INLINE int secp256k1_i128_check_pow2(const secp256k1_int128 *r, unsigned int n); +static SECP256K1_INLINE int secp256k1_i128_check_pow2(const secp256k1_int128 *r, unsigned int n, int sign); #endif diff --git a/src/secp256k1/src/int128_native_impl.h b/src/secp256k1/src/int128_native_impl.h index e4b7f4106c..996e542cf9 100644 --- a/src/secp256k1/src/int128_native_impl.h +++ b/src/secp256k1/src/int128_native_impl.h @@ -67,7 +67,12 @@ static SECP256K1_INLINE void secp256k1_i128_rshift(secp256k1_int128 *r, unsigned *r >>= n; } +static SECP256K1_INLINE uint64_t secp256k1_i128_to_u64(const secp256k1_int128 *a) { + return (uint64_t)*a; +} + static SECP256K1_INLINE int64_t secp256k1_i128_to_i64(const secp256k1_int128 *a) { + VERIFY_CHECK(INT64_MIN <= *a && *a <= INT64_MAX); return *a; } @@ -79,9 +84,10 @@ static SECP256K1_INLINE int secp256k1_i128_eq_var(const secp256k1_int128 *a, con return *a == *b; } -static SECP256K1_INLINE int secp256k1_i128_check_pow2(const secp256k1_int128 *r, unsigned int n) { +static SECP256K1_INLINE int secp256k1_i128_check_pow2(const secp256k1_int128 *r, unsigned int n, int sign) { VERIFY_CHECK(n < 127); - return (*r == (int128_t)1 << n); + VERIFY_CHECK(sign == 1 || sign == -1); + return (*r == (int128_t)((uint128_t)sign << n)); } #endif diff --git a/src/secp256k1/src/int128_struct_impl.h b/src/secp256k1/src/int128_struct_impl.h index b5f8fb7b65..2eb337cb54 100644 --- a/src/secp256k1/src/int128_struct_impl.h +++ b/src/secp256k1/src/int128_struct_impl.h @@ -170,8 +170,14 @@ static SECP256K1_INLINE void secp256k1_i128_rshift(secp256k1_int128 *r, unsigned } } +static SECP256K1_INLINE uint64_t secp256k1_i128_to_u64(const secp256k1_int128 *a) { + return a->lo; +} + static SECP256K1_INLINE int64_t secp256k1_i128_to_i64(const secp256k1_int128 *a) { - return (int64_t)a->lo; + /* Verify that a represents a 64 bit signed value by checking that the high bits are a sign extension of the low bits. */ + VERIFY_CHECK(a->hi == -(a->lo >> 63)); + return (int64_t)secp256k1_i128_to_u64(a); } static SECP256K1_INLINE void secp256k1_i128_from_i64(secp256k1_int128 *r, int64_t a) { @@ -183,10 +189,11 @@ static SECP256K1_INLINE int secp256k1_i128_eq_var(const secp256k1_int128 *a, con return a->hi == b->hi && a->lo == b->lo; } -static SECP256K1_INLINE int secp256k1_i128_check_pow2(const secp256k1_int128 *r, unsigned int n) { - VERIFY_CHECK(n < 127); - return n >= 64 ? r->hi == (uint64_t)1 << (n - 64) && r->lo == 0 - : r->hi == 0 && r->lo == (uint64_t)1 << n; +static SECP256K1_INLINE int secp256k1_i128_check_pow2(const secp256k1_int128 *r, unsigned int n, int sign) { + VERIFY_CHECK(n < 127); + VERIFY_CHECK(sign == 1 || sign == -1); + return n >= 64 ? r->hi == (uint64_t)sign << (n - 64) && r->lo == 0 + : r->hi == (uint64_t)((sign - 1) >> 1) && r->lo == (uint64_t)sign << n; } #endif diff --git a/src/secp256k1/src/modinv32.h b/src/secp256k1/src/modinv32.h index 0efdda9ab5..846c642f8c 100644 --- a/src/secp256k1/src/modinv32.h +++ b/src/secp256k1/src/modinv32.h @@ -7,10 +7,6 @@ #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. @@ -39,4 +35,9 @@ static void secp256k1_modinv32_var(secp256k1_modinv32_signed30 *x, const secp256 /* 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); +/* Compute the Jacobi symbol for (x | modinfo->modulus). x must be coprime with modulus (and thus + * cannot be 0, as modulus >= 3). All limbs of x must be non-negative. Returns 0 if the result + * cannot be computed. */ +static int secp256k1_jacobi32_maybe_var(const 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 index 661c5fc04c..643750560e 100644 --- a/src/secp256k1/src/modinv32_impl.h +++ b/src/secp256k1/src/modinv32_impl.h @@ -232,6 +232,21 @@ static int32_t secp256k1_modinv32_divsteps_30(int32_t zeta, uint32_t f0, uint32_ return zeta; } +/* inv256[i] = -(2*i+1)^-1 (mod 256) */ +static const uint8_t secp256k1_modinv32_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 +}; + /* Compute the transition matrix and eta for 30 divsteps (variable time). * * Input: eta: initial eta @@ -243,21 +258,6 @@ static int32_t secp256k1_modinv32_divsteps_30(int32_t zeta, uint32_t f0, uint32_ * 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; @@ -297,7 +297,7 @@ static int32_t secp256k1_modinv32_divsteps_30_var(int32_t eta, uint32_t f0, uint 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; + w = (g * secp256k1_modinv32_inv256[(f >> 1) & 127]) & m; /* Do so. */ g += f * w; q += u * w; @@ -317,6 +317,86 @@ static int32_t secp256k1_modinv32_divsteps_30_var(int32_t eta, uint32_t f0, uint return eta; } +/* Compute the transition matrix and eta for 30 posdivsteps (variable time, eta=-delta), and keeps track + * of the Jacobi symbol along the way. f0 and g0 must be f and g mod 2^32 rather than 2^30, because + * Jacobi tracking requires knowing (f mod 8) rather than just (f mod 2). + * + * Input: eta: initial eta + * f0: bottom limb of initial f + * g0: bottom limb of initial g + * Output: t: transition matrix + * Input/Output: (*jacp & 1) is bitflipped if and only if the Jacobi symbol of (f | g) changes sign + * by applying the returned transformation matrix to it. The other bits of *jacp may + * change, but are meaningless. + * Return: final eta + */ +static int32_t secp256k1_modinv32_posdivsteps_30_var(int32_t eta, uint32_t f0, uint32_t g0, secp256k1_modinv32_trans2x2 *t, int *jacp) { + /* Transformation matrix. */ + 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; + int jac = *jacp; + + 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; + /* Update the bottom bit of jac: when dividing g by an odd power of 2, + * if (f mod 8) is 3 or 5, the Jacobi symbol changes sign. */ + jac ^= (zeros & ((f >> 1) ^ (f >> 2))); + /* We're done once we've done 30 posdivsteps. */ + 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)); + /* If eta is negative, negate it and replace f,g with g,f. */ + if (eta < 0) { + uint32_t tmp; + eta = -eta; + /* Update bottom bit of jac: when swapping f and g, the Jacobi symbol changes sign + * if both f and g are 3 mod 4. */ + jac ^= ((f & g) >> 1); + 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 * secp256k1_modinv32_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 or -2, + * the aggregate of 30 of them will have determinant 2^30 or -2^30. */ + VERIFY_CHECK((int64_t)t->u * t->r - (int64_t)t->v * t->q == ((int64_t)1) << 30 || + (int64_t)t->u * t->r - (int64_t)t->v * t->q == -(((int64_t)1) << 30)); + *jacp = jac; + 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 @@ -335,10 +415,8 @@ static void secp256k1_modinv32_update_de_30(secp256k1_modinv32_signed30 *d, secp 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 */ + VERIFY_CHECK(labs(u) <= (M30 + 1 - labs(v))); /* |u|+|v| <= 2^30 */ + VERIFY_CHECK(labs(q) <= (M30 + 1 - labs(r))); /* |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; @@ -584,4 +662,74 @@ static void secp256k1_modinv32_var(secp256k1_modinv32_signed30 *x, const secp256 *x = d; } +/* Do up to 50 iterations of 30 posdivsteps (up to 1500 steps; more is extremely rare) each until f=1. + * In VERIFY mode use a lower number of iterations (750, close to the median 756), so failure actually occurs. */ +#ifdef VERIFY +#define JACOBI32_ITERATIONS 25 +#else +#define JACOBI32_ITERATIONS 50 +#endif + +/* Compute the Jacobi symbol of x modulo modinfo->modulus (variable time). gcd(x,modulus) must be 1. */ +static int secp256k1_jacobi32_maybe_var(const secp256k1_modinv32_signed30 *x, const secp256k1_modinv32_modinfo *modinfo) { + /* Start with f=modulus, g=x, eta=-1. */ + secp256k1_modinv32_signed30 f = modinfo->modulus; + secp256k1_modinv32_signed30 g = *x; + int j, len = 9; + int32_t eta = -1; /* eta = -delta; delta is initially 1 */ + int32_t cond, fn, gn; + int jac = 0; + int count; + + /* The input limbs must all be non-negative. */ + VERIFY_CHECK(g.v[0] >= 0 && g.v[1] >= 0 && g.v[2] >= 0 && g.v[3] >= 0 && g.v[4] >= 0 && g.v[5] >= 0 && g.v[6] >= 0 && g.v[7] >= 0 && g.v[8] >= 0); + + /* If x > 0, then if the loop below converges, it converges to f=g=gcd(x,modulus). Since we + * require that gcd(x,modulus)=1 and modulus>=3, x cannot be 0. Thus, we must reach f=1 (or + * time out). */ + VERIFY_CHECK((g.v[0] | g.v[1] | g.v[2] | g.v[3] | g.v[4] | g.v[5] | g.v[6] | g.v[7] | g.v[8]) != 0); + + for (count = 0; count < JACOBI32_ITERATIONS; ++count) { + /* Compute transition matrix and new eta after 30 posdivsteps. */ + secp256k1_modinv32_trans2x2 t; + eta = secp256k1_modinv32_posdivsteps_30_var(eta, f.v[0] | ((uint32_t)f.v[1] << 30), g.v[0] | ((uint32_t)g.v[1] << 30), &t, &jac); + /* Update f,g using that transition matrix. */ +#ifdef VERIFY + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, 0) > 0); /* f > 0 */ + 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, 0) > 0); /* g > 0 */ + 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 f is 1, there is a chance that f=1. */ + if (f.v[0] == 1) { + cond = 0; + /* Check if the other limbs are also 0. */ + for (j = 1; j < len; ++j) { + cond |= f.v[j]; + } + /* If so, we're done. If f=1, the Jacobi symbol (g | f)=1. */ + if (cond == 0) return 1 - 2*(jac & 1); + } + + /* Determine if len>1 and limb (len-1) of both f and g is 0. */ + fn = f.v[len - 1]; + gn = g.v[len - 1]; + cond = ((int32_t)len - 2) >> 31; + cond |= fn; + cond |= gn; + /* If so, reduce length. */ + if (cond == 0) --len; +#ifdef VERIFY + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, 0) > 0); /* f > 0 */ + 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, 0) > 0); /* g > 0 */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &modinfo->modulus, 1) < 0); /* g < modulus */ +#endif + } + + /* The loop failed to converge to f=g after 1500 iterations. Return 0, indicating unknown result. */ + return 0; +} + #endif /* SECP256K1_MODINV32_IMPL_H */ diff --git a/src/secp256k1/src/modinv64.h b/src/secp256k1/src/modinv64.h index da506dfa9f..f4208e6c23 100644 --- a/src/secp256k1/src/modinv64.h +++ b/src/secp256k1/src/modinv64.h @@ -7,10 +7,6 @@ #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 @@ -43,4 +39,9 @@ static void secp256k1_modinv64_var(secp256k1_modinv64_signed62 *x, const secp256 /* 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); +/* Compute the Jacobi symbol for (x | modinfo->modulus). x must be coprime with modulus (and thus + * cannot be 0, as modulus >= 3). All limbs of x must be non-negative. Returns 0 if the result + * cannot be computed. */ +static int secp256k1_jacobi64_maybe_var(const 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 index 50be2e5e78..e33727d385 100644 --- a/src/secp256k1/src/modinv64_impl.h +++ b/src/secp256k1/src/modinv64_impl.h @@ -39,13 +39,13 @@ 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); + const uint64_t M62 = UINT64_MAX >> 2; secp256k1_int128 c, d; int i; secp256k1_i128_from_i64(&c, 0); for (i = 0; i < 4; ++i) { if (i < alen) secp256k1_i128_accum_mul(&c, a->v[i], factor); - r->v[i] = secp256k1_i128_to_i64(&c) & M62; secp256k1_i128_rshift(&c, 62); + r->v[i] = secp256k1_i128_to_u64(&c) & M62; secp256k1_i128_rshift(&c, 62); } if (4 < alen) secp256k1_i128_accum_mul(&c, a->v[4], factor); secp256k1_i128_from_i64(&d, secp256k1_i128_to_i64(&c)); @@ -71,11 +71,13 @@ static int secp256k1_modinv64_mul_cmp_62(const secp256k1_modinv64_signed62 *a, i return 0; } -/* Check if the determinant of t is equal to 1 << n. */ -static int secp256k1_modinv64_det_check_pow2(const secp256k1_modinv64_trans2x2 *t, unsigned int n) { +/* Check if the determinant of t is equal to 1 << n. If abs, check if |det t| == 1 << n. */ +static int secp256k1_modinv64_det_check_pow2(const secp256k1_modinv64_trans2x2 *t, unsigned int n, int abs) { secp256k1_int128 a; secp256k1_i128_det(&a, t->u, t->v, t->q, t->r); - return secp256k1_i128_check_pow2(&a, n); + if (secp256k1_i128_check_pow2(&a, n, 1)) return 1; + if (abs && secp256k1_i128_check_pow2(&a, n, -1)) return 1; + return 0; } #endif @@ -218,7 +220,7 @@ static int64_t secp256k1_modinv64_divsteps_59(int64_t zeta, uint64_t f0, uint64_ * 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(secp256k1_modinv64_det_check_pow2(t, 65)); + VERIFY_CHECK(secp256k1_modinv64_det_check_pow2(t, 65, 0)); #endif return zeta; } @@ -266,7 +268,7 @@ static int64_t secp256k1_modinv64_divsteps_62_var(int64_t eta, uint64_t f0, uint 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. */ + * sign 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. */ @@ -301,8 +303,100 @@ static int64_t secp256k1_modinv64_divsteps_62_var(int64_t eta, uint64_t f0, uint * 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(secp256k1_modinv64_det_check_pow2(t, 62)); + VERIFY_CHECK(secp256k1_modinv64_det_check_pow2(t, 62, 0)); +#endif + return eta; +} + +/* Compute the transition matrix and eta for 62 posdivsteps (variable time, eta=-delta), and keeps track + * of the Jacobi symbol along the way. f0 and g0 must be f and g mod 2^64 rather than 2^62, because + * Jacobi tracking requires knowing (f mod 8) rather than just (f mod 2). + * + * Input: eta: initial eta + * f0: bottom limb of initial f + * g0: bottom limb of initial g + * Output: t: transition matrix + * Input/Output: (*jacp & 1) is bitflipped if and only if the Jacobi symbol of (f | g) changes sign + * by applying the returned transformation matrix to it. The other bits of *jacp may + * change, but are meaningless. + * Return: final eta + */ +static int64_t secp256k1_modinv64_posdivsteps_62_var(int64_t eta, uint64_t f0, uint64_t g0, secp256k1_modinv64_trans2x2 *t, int *jacp) { + /* 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; + int jac = *jacp; + + 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; + /* Update the bottom bit of jac: when dividing g by an odd power of 2, + * if (f mod 8) is 3 or 5, the Jacobi symbol changes sign. */ + jac ^= (zeros & ((f >> 1) ^ (f >> 2))); + /* We're done once we've done 62 posdivsteps. */ + 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)); + /* 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; + /* Update bottom bit of jac: when swapping f and g, the Jacobi symbol changes sign + * if both f and g are 3 mod 4. */ + jac ^= ((f & g) >> 1); + /* 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 + * sign 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; +#ifdef VERIFY + /* 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 or -2, + * the aggregate of 62 of them will have determinant 2^62 or -2^62. */ + VERIFY_CHECK(secp256k1_modinv64_det_check_pow2(t, 62, 1)); #endif + *jacp = jac; return eta; } @@ -314,7 +408,7 @@ static int64_t secp256k1_modinv64_divsteps_62_var(int64_t eta, uint64_t f0, uint * 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 uint64_t M62 = 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; @@ -325,10 +419,8 @@ static void secp256k1_modinv64_update_de_62(secp256k1_modinv64_signed62 *d, secp 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 */ + VERIFY_CHECK(secp256k1_modinv64_abs(u) <= (((int64_t)1 << 62) - secp256k1_modinv64_abs(v))); /* |u|+|v| <= 2^62 */ + VERIFY_CHECK(secp256k1_modinv64_abs(q) <= (((int64_t)1 << 62) - secp256k1_modinv64_abs(r))); /* |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; @@ -341,14 +433,14 @@ static void secp256k1_modinv64_update_de_62(secp256k1_modinv64_signed62 *d, secp secp256k1_i128_mul(&ce, q, d0); secp256k1_i128_accum_mul(&ce, r, e0); /* Correct md,me so that t*[d,e]+modulus*[md,me] has 62 zero bottom bits. */ - md -= (modinfo->modulus_inv62 * (uint64_t)secp256k1_i128_to_i64(&cd) + md) & M62; - me -= (modinfo->modulus_inv62 * (uint64_t)secp256k1_i128_to_i64(&ce) + me) & M62; + md -= (modinfo->modulus_inv62 * secp256k1_i128_to_u64(&cd) + md) & M62; + me -= (modinfo->modulus_inv62 * secp256k1_i128_to_u64(&ce) + me) & M62; /* Update the beginning of computation for t*[d,e]+modulus*[md,me] now md,me are known. */ secp256k1_i128_accum_mul(&cd, modinfo->modulus.v[0], md); secp256k1_i128_accum_mul(&ce, modinfo->modulus.v[0], me); /* Verify that the low 62 bits of the computation are indeed zero, and then throw them away. */ - VERIFY_CHECK((secp256k1_i128_to_i64(&cd) & M62) == 0); secp256k1_i128_rshift(&cd, 62); - VERIFY_CHECK((secp256k1_i128_to_i64(&ce) & M62) == 0); secp256k1_i128_rshift(&ce, 62); + VERIFY_CHECK((secp256k1_i128_to_u64(&cd) & M62) == 0); secp256k1_i128_rshift(&cd, 62); + VERIFY_CHECK((secp256k1_i128_to_u64(&ce) & M62) == 0); secp256k1_i128_rshift(&ce, 62); /* Compute limb 1 of t*[d,e]+modulus*[md,me], and store it as output limb 0 (= down shift). */ secp256k1_i128_accum_mul(&cd, u, d1); secp256k1_i128_accum_mul(&cd, v, e1); @@ -358,8 +450,8 @@ static void secp256k1_modinv64_update_de_62(secp256k1_modinv64_signed62 *d, secp secp256k1_i128_accum_mul(&cd, modinfo->modulus.v[1], md); secp256k1_i128_accum_mul(&ce, modinfo->modulus.v[1], me); } - d->v[0] = secp256k1_i128_to_i64(&cd) & M62; secp256k1_i128_rshift(&cd, 62); - e->v[0] = secp256k1_i128_to_i64(&ce) & M62; secp256k1_i128_rshift(&ce, 62); + d->v[0] = secp256k1_i128_to_u64(&cd) & M62; secp256k1_i128_rshift(&cd, 62); + e->v[0] = secp256k1_i128_to_u64(&ce) & M62; secp256k1_i128_rshift(&ce, 62); /* Compute limb 2 of t*[d,e]+modulus*[md,me], and store it as output limb 1. */ secp256k1_i128_accum_mul(&cd, u, d2); secp256k1_i128_accum_mul(&cd, v, e2); @@ -369,8 +461,8 @@ static void secp256k1_modinv64_update_de_62(secp256k1_modinv64_signed62 *d, secp secp256k1_i128_accum_mul(&cd, modinfo->modulus.v[2], md); secp256k1_i128_accum_mul(&ce, modinfo->modulus.v[2], me); } - d->v[1] = secp256k1_i128_to_i64(&cd) & M62; secp256k1_i128_rshift(&cd, 62); - e->v[1] = secp256k1_i128_to_i64(&ce) & M62; secp256k1_i128_rshift(&ce, 62); + d->v[1] = secp256k1_i128_to_u64(&cd) & M62; secp256k1_i128_rshift(&cd, 62); + e->v[1] = secp256k1_i128_to_u64(&ce) & M62; secp256k1_i128_rshift(&ce, 62); /* Compute limb 3 of t*[d,e]+modulus*[md,me], and store it as output limb 2. */ secp256k1_i128_accum_mul(&cd, u, d3); secp256k1_i128_accum_mul(&cd, v, e3); @@ -380,8 +472,8 @@ static void secp256k1_modinv64_update_de_62(secp256k1_modinv64_signed62 *d, secp secp256k1_i128_accum_mul(&cd, modinfo->modulus.v[3], md); secp256k1_i128_accum_mul(&ce, modinfo->modulus.v[3], me); } - d->v[2] = secp256k1_i128_to_i64(&cd) & M62; secp256k1_i128_rshift(&cd, 62); - e->v[2] = secp256k1_i128_to_i64(&ce) & M62; secp256k1_i128_rshift(&ce, 62); + d->v[2] = secp256k1_i128_to_u64(&cd) & M62; secp256k1_i128_rshift(&cd, 62); + e->v[2] = secp256k1_i128_to_u64(&ce) & M62; secp256k1_i128_rshift(&ce, 62); /* Compute limb 4 of t*[d,e]+modulus*[md,me], and store it as output limb 3. */ secp256k1_i128_accum_mul(&cd, u, d4); secp256k1_i128_accum_mul(&cd, v, e4); @@ -389,8 +481,8 @@ static void secp256k1_modinv64_update_de_62(secp256k1_modinv64_signed62 *d, secp secp256k1_i128_accum_mul(&ce, r, e4); secp256k1_i128_accum_mul(&cd, modinfo->modulus.v[4], md); secp256k1_i128_accum_mul(&ce, modinfo->modulus.v[4], me); - d->v[3] = secp256k1_i128_to_i64(&cd) & M62; secp256k1_i128_rshift(&cd, 62); - e->v[3] = secp256k1_i128_to_i64(&ce) & M62; secp256k1_i128_rshift(&ce, 62); + d->v[3] = secp256k1_i128_to_u64(&cd) & M62; secp256k1_i128_rshift(&cd, 62); + e->v[3] = secp256k1_i128_to_u64(&ce) & M62; secp256k1_i128_rshift(&ce, 62); /* What remains is limb 5 of t*[d,e]+modulus*[md,me]; store it as output limb 4. */ d->v[4] = secp256k1_i128_to_i64(&cd); e->v[4] = secp256k1_i128_to_i64(&ce); @@ -407,7 +499,7 @@ static void secp256k1_modinv64_update_de_62(secp256k1_modinv64_signed62 *d, secp * 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 uint64_t M62 = 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; @@ -418,36 +510,36 @@ static void secp256k1_modinv64_update_fg_62(secp256k1_modinv64_signed62 *f, secp secp256k1_i128_mul(&cg, q, f0); secp256k1_i128_accum_mul(&cg, r, g0); /* Verify that the bottom 62 bits of the result are zero, and then throw them away. */ - VERIFY_CHECK((secp256k1_i128_to_i64(&cf) & M62) == 0); secp256k1_i128_rshift(&cf, 62); - VERIFY_CHECK((secp256k1_i128_to_i64(&cg) & M62) == 0); secp256k1_i128_rshift(&cg, 62); + VERIFY_CHECK((secp256k1_i128_to_u64(&cf) & M62) == 0); secp256k1_i128_rshift(&cf, 62); + VERIFY_CHECK((secp256k1_i128_to_u64(&cg) & M62) == 0); secp256k1_i128_rshift(&cg, 62); /* Compute limb 1 of t*[f,g], and store it as output limb 0 (= down shift). */ secp256k1_i128_accum_mul(&cf, u, f1); secp256k1_i128_accum_mul(&cf, v, g1); secp256k1_i128_accum_mul(&cg, q, f1); secp256k1_i128_accum_mul(&cg, r, g1); - f->v[0] = secp256k1_i128_to_i64(&cf) & M62; secp256k1_i128_rshift(&cf, 62); - g->v[0] = secp256k1_i128_to_i64(&cg) & M62; secp256k1_i128_rshift(&cg, 62); + f->v[0] = secp256k1_i128_to_u64(&cf) & M62; secp256k1_i128_rshift(&cf, 62); + g->v[0] = secp256k1_i128_to_u64(&cg) & M62; secp256k1_i128_rshift(&cg, 62); /* Compute limb 2 of t*[f,g], and store it as output limb 1. */ secp256k1_i128_accum_mul(&cf, u, f2); secp256k1_i128_accum_mul(&cf, v, g2); secp256k1_i128_accum_mul(&cg, q, f2); secp256k1_i128_accum_mul(&cg, r, g2); - f->v[1] = secp256k1_i128_to_i64(&cf) & M62; secp256k1_i128_rshift(&cf, 62); - g->v[1] = secp256k1_i128_to_i64(&cg) & M62; secp256k1_i128_rshift(&cg, 62); + f->v[1] = secp256k1_i128_to_u64(&cf) & M62; secp256k1_i128_rshift(&cf, 62); + g->v[1] = secp256k1_i128_to_u64(&cg) & M62; secp256k1_i128_rshift(&cg, 62); /* Compute limb 3 of t*[f,g], and store it as output limb 2. */ secp256k1_i128_accum_mul(&cf, u, f3); secp256k1_i128_accum_mul(&cf, v, g3); secp256k1_i128_accum_mul(&cg, q, f3); secp256k1_i128_accum_mul(&cg, r, g3); - f->v[2] = secp256k1_i128_to_i64(&cf) & M62; secp256k1_i128_rshift(&cf, 62); - g->v[2] = secp256k1_i128_to_i64(&cg) & M62; secp256k1_i128_rshift(&cg, 62); + f->v[2] = secp256k1_i128_to_u64(&cf) & M62; secp256k1_i128_rshift(&cf, 62); + g->v[2] = secp256k1_i128_to_u64(&cg) & M62; secp256k1_i128_rshift(&cg, 62); /* Compute limb 4 of t*[f,g], and store it as output limb 3. */ secp256k1_i128_accum_mul(&cf, u, f4); secp256k1_i128_accum_mul(&cf, v, g4); secp256k1_i128_accum_mul(&cg, q, f4); secp256k1_i128_accum_mul(&cg, r, g4); - f->v[3] = secp256k1_i128_to_i64(&cf) & M62; secp256k1_i128_rshift(&cf, 62); - g->v[3] = secp256k1_i128_to_i64(&cg) & M62; secp256k1_i128_rshift(&cg, 62); + f->v[3] = secp256k1_i128_to_u64(&cf) & M62; secp256k1_i128_rshift(&cf, 62); + g->v[3] = secp256k1_i128_to_u64(&cg) & M62; secp256k1_i128_rshift(&cg, 62); /* What remains is limb 5 of t*[f,g]; store it as output limb 4. */ f->v[4] = secp256k1_i128_to_i64(&cf); g->v[4] = secp256k1_i128_to_i64(&cg); @@ -460,7 +552,7 @@ static void secp256k1_modinv64_update_fg_62(secp256k1_modinv64_signed62 *f, secp * 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 uint64_t M62 = UINT64_MAX >> 2; const int64_t u = t->u, v = t->v, q = t->q, r = t->r; int64_t fi, gi; secp256k1_int128 cf, cg; @@ -474,8 +566,8 @@ static void secp256k1_modinv64_update_fg_62_var(int len, secp256k1_modinv64_sign secp256k1_i128_mul(&cg, q, fi); secp256k1_i128_accum_mul(&cg, r, gi); /* Verify that the bottom 62 bits of the result are zero, and then throw them away. */ - VERIFY_CHECK((secp256k1_i128_to_i64(&cf) & M62) == 0); secp256k1_i128_rshift(&cf, 62); - VERIFY_CHECK((secp256k1_i128_to_i64(&cg) & M62) == 0); secp256k1_i128_rshift(&cg, 62); + VERIFY_CHECK((secp256k1_i128_to_u64(&cf) & M62) == 0); secp256k1_i128_rshift(&cf, 62); + VERIFY_CHECK((secp256k1_i128_to_u64(&cg) & M62) == 0); secp256k1_i128_rshift(&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) { @@ -485,8 +577,8 @@ static void secp256k1_modinv64_update_fg_62_var(int len, secp256k1_modinv64_sign secp256k1_i128_accum_mul(&cf, v, gi); secp256k1_i128_accum_mul(&cg, q, fi); secp256k1_i128_accum_mul(&cg, r, gi); - f->v[i - 1] = secp256k1_i128_to_i64(&cf) & M62; secp256k1_i128_rshift(&cf, 62); - g->v[i - 1] = secp256k1_i128_to_i64(&cg) & M62; secp256k1_i128_rshift(&cg, 62); + f->v[i - 1] = secp256k1_i128_to_u64(&cf) & M62; secp256k1_i128_rshift(&cf, 62); + g->v[i - 1] = secp256k1_i128_to_u64(&cg) & M62; secp256k1_i128_rshift(&cg, 62); } /* What remains is limb (len) of t*[f,g]; store it as output limb (len-1). */ f->v[len - 1] = secp256k1_i128_to_i64(&cf); @@ -626,4 +718,74 @@ static void secp256k1_modinv64_var(secp256k1_modinv64_signed62 *x, const secp256 *x = d; } +/* Do up to 25 iterations of 62 posdivsteps (up to 1550 steps; more is extremely rare) each until f=1. + * In VERIFY mode use a lower number of iterations (744, close to the median 756), so failure actually occurs. */ +#ifdef VERIFY +#define JACOBI64_ITERATIONS 12 +#else +#define JACOBI64_ITERATIONS 25 +#endif + +/* Compute the Jacobi symbol of x modulo modinfo->modulus (variable time). gcd(x,modulus) must be 1. */ +static int secp256k1_jacobi64_maybe_var(const secp256k1_modinv64_signed62 *x, const secp256k1_modinv64_modinfo *modinfo) { + /* Start with f=modulus, g=x, eta=-1. */ + secp256k1_modinv64_signed62 f = modinfo->modulus; + secp256k1_modinv64_signed62 g = *x; + int j, len = 5; + int64_t eta = -1; /* eta = -delta; delta is initially 1 */ + int64_t cond, fn, gn; + int jac = 0; + int count; + + /* The input limbs must all be non-negative. */ + VERIFY_CHECK(g.v[0] >= 0 && g.v[1] >= 0 && g.v[2] >= 0 && g.v[3] >= 0 && g.v[4] >= 0); + + /* If x > 0, then if the loop below converges, it converges to f=g=gcd(x,modulus). Since we + * require that gcd(x,modulus)=1 and modulus>=3, x cannot be 0. Thus, we must reach f=1 (or + * time out). */ + VERIFY_CHECK((g.v[0] | g.v[1] | g.v[2] | g.v[3] | g.v[4]) != 0); + + for (count = 0; count < JACOBI64_ITERATIONS; ++count) { + /* Compute transition matrix and new eta after 62 posdivsteps. */ + secp256k1_modinv64_trans2x2 t; + eta = secp256k1_modinv64_posdivsteps_62_var(eta, f.v[0] | ((uint64_t)f.v[1] << 62), g.v[0] | ((uint64_t)g.v[1] << 62), &t, &jac); + /* Update f,g using that transition matrix. */ +#ifdef VERIFY + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, 0) > 0); /* f > 0 */ + 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, 0) > 0); /* g > 0 */ + 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 f is 1, there is a chance that f=1. */ + if (f.v[0] == 1) { + cond = 0; + /* Check if the other limbs are also 0. */ + for (j = 1; j < len; ++j) { + cond |= f.v[j]; + } + /* If so, we're done. When f=1, the Jacobi symbol (g | f)=1. */ + if (cond == 0) return 1 - 2*(jac & 1); + } + + /* Determine if len>1 and limb (len-1) of both f and g is 0. */ + fn = f.v[len - 1]; + gn = g.v[len - 1]; + cond = ((int64_t)len - 2) >> 63; + cond |= fn; + cond |= gn; + /* If so, reduce length. */ + if (cond == 0) --len; +#ifdef VERIFY + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, 0) > 0); /* f > 0 */ + 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, 0) > 0); /* g > 0 */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &modinfo->modulus, 1) < 0); /* g < modulus */ +#endif + } + + /* The loop failed to converge to f=g after 1550 iterations. Return 0, indicating unknown result. */ + return 0; +} + #endif /* SECP256K1_MODINV64_IMPL_H */ diff --git a/src/secp256k1/src/modules/ecdh/bench_impl.h b/src/secp256k1/src/modules/ecdh/bench_impl.h index 8df15bcf43..c23aaa94d1 100644 --- a/src/secp256k1/src/modules/ecdh/bench_impl.h +++ b/src/secp256k1/src/modules/ecdh/bench_impl.h @@ -42,7 +42,7 @@ static void bench_ecdh(void* arg, int iters) { } } -void run_ecdh_bench(int iters, int argc, char** argv) { +static void run_ecdh_bench(int iters, int argc, char** argv) { bench_ecdh_data data; int d = argc == 1; diff --git a/src/secp256k1/src/modules/ecdh/tests_impl.h b/src/secp256k1/src/modules/ecdh/tests_impl.h index ce644d572a..fa6f232227 100644 --- a/src/secp256k1/src/modules/ecdh/tests_impl.h +++ b/src/secp256k1/src/modules/ecdh/tests_impl.h @@ -7,7 +7,7 @@ #ifndef SECP256K1_MODULE_ECDH_TESTS_H #define SECP256K1_MODULE_ECDH_TESTS_H -int ecdh_hash_function_test_fail(unsigned char *output, const unsigned char *x, const unsigned char *y, void *data) { +static int ecdh_hash_function_test_fail(unsigned char *output, const unsigned char *x, const unsigned char *y, void *data) { (void)output; (void)x; (void)y; @@ -15,7 +15,7 @@ int ecdh_hash_function_test_fail(unsigned char *output, const unsigned char *x, return 0; } -int ecdh_hash_function_custom(unsigned char *output, const unsigned char *x, const unsigned char *y, void *data) { +static int ecdh_hash_function_custom(unsigned char *output, const unsigned char *x, const unsigned char *y, void *data) { (void)data; /* Save x and y as uncompressed public key */ output[0] = 0x04; @@ -24,7 +24,7 @@ int ecdh_hash_function_custom(unsigned char *output, const unsigned char *x, con return 1; } -void test_ecdh_api(void) { +static void test_ecdh_api(void) { /* Setup context that just counts errors */ secp256k1_context *tctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); secp256k1_pubkey point; @@ -53,14 +53,14 @@ void test_ecdh_api(void) { secp256k1_context_destroy(tctx); } -void test_ecdh_generator_basepoint(void) { +static void test_ecdh_generator_basepoint(void) { unsigned char s_one[32] = { 0 }; secp256k1_pubkey point[2]; int i; s_one[31] = 1; /* Check against pubkey creation when the basepoint is the generator */ - for (i = 0; i < 2 * count; ++i) { + for (i = 0; i < 2 * COUNT; ++i) { secp256k1_sha256 sha; unsigned char s_b32[32]; unsigned char output_ecdh[65]; @@ -72,20 +72,20 @@ void test_ecdh_generator_basepoint(void) { random_scalar_order(&s); secp256k1_scalar_get_b32(s_b32, &s); - CHECK(secp256k1_ec_pubkey_create(ctx, &point[0], s_one) == 1); - CHECK(secp256k1_ec_pubkey_create(ctx, &point[1], s_b32) == 1); + CHECK(secp256k1_ec_pubkey_create(CTX, &point[0], s_one) == 1); + CHECK(secp256k1_ec_pubkey_create(CTX, &point[1], s_b32) == 1); /* compute using ECDH function with custom hash function */ - CHECK(secp256k1_ecdh(ctx, output_ecdh, &point[0], s_b32, ecdh_hash_function_custom, NULL) == 1); + CHECK(secp256k1_ecdh(CTX, output_ecdh, &point[0], s_b32, ecdh_hash_function_custom, NULL) == 1); /* compute "explicitly" */ - CHECK(secp256k1_ec_pubkey_serialize(ctx, point_ser, &point_ser_len, &point[1], SECP256K1_EC_UNCOMPRESSED) == 1); + CHECK(secp256k1_ec_pubkey_serialize(CTX, point_ser, &point_ser_len, &point[1], SECP256K1_EC_UNCOMPRESSED) == 1); /* compare */ CHECK(secp256k1_memcmp_var(output_ecdh, point_ser, 65) == 0); /* compute using ECDH function with default hash function */ - CHECK(secp256k1_ecdh(ctx, output_ecdh, &point[0], s_b32, NULL, NULL) == 1); + CHECK(secp256k1_ecdh(CTX, output_ecdh, &point[0], s_b32, NULL, NULL) == 1); /* compute "explicitly" */ - CHECK(secp256k1_ec_pubkey_serialize(ctx, point_ser, &point_ser_len, &point[1], SECP256K1_EC_COMPRESSED) == 1); + CHECK(secp256k1_ec_pubkey_serialize(CTX, point_ser, &point_ser_len, &point[1], SECP256K1_EC_COMPRESSED) == 1); secp256k1_sha256_initialize(&sha); secp256k1_sha256_write(&sha, point_ser, point_ser_len); secp256k1_sha256_finalize(&sha, output_ser); @@ -94,7 +94,7 @@ void test_ecdh_generator_basepoint(void) { } } -void test_bad_scalar(void) { +static void test_bad_scalar(void) { unsigned char s_zero[32] = { 0 }; unsigned char s_overflow[32] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, @@ -110,21 +110,21 @@ void test_bad_scalar(void) { /* Create random point */ random_scalar_order(&rand); secp256k1_scalar_get_b32(s_rand, &rand); - CHECK(secp256k1_ec_pubkey_create(ctx, &point, s_rand) == 1); + CHECK(secp256k1_ec_pubkey_create(CTX, &point, s_rand) == 1); /* Try to multiply it by bad values */ - CHECK(secp256k1_ecdh(ctx, output, &point, s_zero, NULL, NULL) == 0); - CHECK(secp256k1_ecdh(ctx, output, &point, s_overflow, NULL, NULL) == 0); + CHECK(secp256k1_ecdh(CTX, output, &point, s_zero, NULL, NULL) == 0); + CHECK(secp256k1_ecdh(CTX, output, &point, s_overflow, NULL, NULL) == 0); /* ...and a good one */ s_overflow[31] -= 1; - CHECK(secp256k1_ecdh(ctx, output, &point, s_overflow, NULL, NULL) == 1); + CHECK(secp256k1_ecdh(CTX, output, &point, s_overflow, NULL, NULL) == 1); /* Hash function failure results in ecdh failure */ - CHECK(secp256k1_ecdh(ctx, output, &point, s_overflow, ecdh_hash_function_test_fail, NULL) == 0); + CHECK(secp256k1_ecdh(CTX, output, &point, s_overflow, ecdh_hash_function_test_fail, NULL) == 0); } /** Test that ECDH(sG, 1/s) == ECDH((1/s)G, s) == ECDH(G, 1) for a few random s. */ -void test_result_basepoint(void) { +static void test_result_basepoint(void) { secp256k1_pubkey point; secp256k1_scalar rand; unsigned char s[32]; @@ -136,26 +136,26 @@ void test_result_basepoint(void) { unsigned char s_one[32] = { 0 }; s_one[31] = 1; - CHECK(secp256k1_ec_pubkey_create(ctx, &point, s_one) == 1); - CHECK(secp256k1_ecdh(ctx, out_base, &point, s_one, NULL, NULL) == 1); + CHECK(secp256k1_ec_pubkey_create(CTX, &point, s_one) == 1); + CHECK(secp256k1_ecdh(CTX, out_base, &point, s_one, NULL, NULL) == 1); - for (i = 0; i < 2 * count; i++) { + for (i = 0; i < 2 * COUNT; i++) { random_scalar_order(&rand); secp256k1_scalar_get_b32(s, &rand); secp256k1_scalar_inverse(&rand, &rand); secp256k1_scalar_get_b32(s_inv, &rand); - CHECK(secp256k1_ec_pubkey_create(ctx, &point, s) == 1); - CHECK(secp256k1_ecdh(ctx, out, &point, s_inv, NULL, NULL) == 1); + CHECK(secp256k1_ec_pubkey_create(CTX, &point, s) == 1); + CHECK(secp256k1_ecdh(CTX, out, &point, s_inv, NULL, NULL) == 1); CHECK(secp256k1_memcmp_var(out, out_base, 32) == 0); - CHECK(secp256k1_ec_pubkey_create(ctx, &point, s_inv) == 1); - CHECK(secp256k1_ecdh(ctx, out_inv, &point, s, NULL, NULL) == 1); + CHECK(secp256k1_ec_pubkey_create(CTX, &point, s_inv) == 1); + CHECK(secp256k1_ecdh(CTX, out_inv, &point, s, NULL, NULL) == 1); CHECK(secp256k1_memcmp_var(out_inv, out_base, 32) == 0); } } -void run_ecdh_tests(void) { +static void run_ecdh_tests(void) { test_ecdh_api(); test_ecdh_generator_basepoint(); test_bad_scalar(); diff --git a/src/secp256k1/src/modules/extrakeys/tests_impl.h b/src/secp256k1/src/modules/extrakeys/tests_impl.h index 8030aedad6..ae1655923b 100644 --- a/src/secp256k1/src/modules/extrakeys/tests_impl.h +++ b/src/secp256k1/src/modules/extrakeys/tests_impl.h @@ -14,7 +14,7 @@ static void set_counting_callbacks(secp256k1_context *ctx0, int *ecount) { secp256k1_context_set_illegal_callback(ctx0, counting_illegal_callback_fn, ecount); } -void test_xonly_pubkey(void) { +static void test_xonly_pubkey(void) { secp256k1_pubkey pk; secp256k1_xonly_pubkey xonly_pk, xonly_pk_tmp; secp256k1_ge pk1; @@ -30,52 +30,52 @@ void test_xonly_pubkey(void) { int ecount; - set_counting_callbacks(ctx, &ecount); + set_counting_callbacks(CTX, &ecount); secp256k1_testrand256(sk); memset(ones32, 0xFF, 32); secp256k1_testrand256(xy_sk); - CHECK(secp256k1_ec_pubkey_create(ctx, &pk, sk) == 1); - CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &xonly_pk, &pk_parity, &pk) == 1); + CHECK(secp256k1_ec_pubkey_create(CTX, &pk, sk) == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(CTX, &xonly_pk, &pk_parity, &pk) == 1); /* Test xonly_pubkey_from_pubkey */ ecount = 0; - CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &xonly_pk, &pk_parity, &pk) == 1); - CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, NULL, &pk_parity, &pk) == 0); + CHECK(secp256k1_xonly_pubkey_from_pubkey(CTX, &xonly_pk, &pk_parity, &pk) == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(CTX, NULL, &pk_parity, &pk) == 0); CHECK(ecount == 1); - CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &xonly_pk, NULL, &pk) == 1); - CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &xonly_pk, &pk_parity, NULL) == 0); + CHECK(secp256k1_xonly_pubkey_from_pubkey(CTX, &xonly_pk, NULL, &pk) == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(CTX, &xonly_pk, &pk_parity, NULL) == 0); CHECK(ecount == 2); memset(&pk, 0, sizeof(pk)); - CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &xonly_pk, &pk_parity, &pk) == 0); + CHECK(secp256k1_xonly_pubkey_from_pubkey(CTX, &xonly_pk, &pk_parity, &pk) == 0); CHECK(ecount == 3); /* Choose a secret key such that the resulting pubkey and xonly_pubkey match. */ memset(sk, 0, sizeof(sk)); sk[0] = 1; - CHECK(secp256k1_ec_pubkey_create(ctx, &pk, sk) == 1); - CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &xonly_pk, &pk_parity, &pk) == 1); + CHECK(secp256k1_ec_pubkey_create(CTX, &pk, sk) == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(CTX, &xonly_pk, &pk_parity, &pk) == 1); CHECK(secp256k1_memcmp_var(&pk, &xonly_pk, sizeof(pk)) == 0); CHECK(pk_parity == 0); /* Choose a secret key such that pubkey and xonly_pubkey are each others * negation. */ sk[0] = 2; - CHECK(secp256k1_ec_pubkey_create(ctx, &pk, sk) == 1); - CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &xonly_pk, &pk_parity, &pk) == 1); + CHECK(secp256k1_ec_pubkey_create(CTX, &pk, sk) == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(CTX, &xonly_pk, &pk_parity, &pk) == 1); CHECK(secp256k1_memcmp_var(&xonly_pk, &pk, sizeof(xonly_pk)) != 0); CHECK(pk_parity == 1); - secp256k1_pubkey_load(ctx, &pk1, &pk); - secp256k1_pubkey_load(ctx, &pk2, (secp256k1_pubkey *) &xonly_pk); + secp256k1_pubkey_load(CTX, &pk1, &pk); + secp256k1_pubkey_load(CTX, &pk2, (secp256k1_pubkey *) &xonly_pk); CHECK(secp256k1_fe_equal(&pk1.x, &pk2.x) == 1); secp256k1_fe_negate(&y, &pk2.y, 1); CHECK(secp256k1_fe_equal(&pk1.y, &y) == 1); /* Test xonly_pubkey_serialize and xonly_pubkey_parse */ ecount = 0; - CHECK(secp256k1_xonly_pubkey_serialize(ctx, NULL, &xonly_pk) == 0); + CHECK(secp256k1_xonly_pubkey_serialize(CTX, NULL, &xonly_pk) == 0); CHECK(ecount == 1); - CHECK(secp256k1_xonly_pubkey_serialize(ctx, buf32, NULL) == 0); + CHECK(secp256k1_xonly_pubkey_serialize(CTX, buf32, NULL) == 0); CHECK(secp256k1_memcmp_var(buf32, zeros64, 32) == 0); CHECK(ecount == 2); { @@ -83,52 +83,52 @@ void test_xonly_pubkey(void) { * special casing. */ secp256k1_xonly_pubkey pk_tmp; memset(&pk_tmp, 0, sizeof(pk_tmp)); - CHECK(secp256k1_xonly_pubkey_serialize(ctx, buf32, &pk_tmp) == 0); + CHECK(secp256k1_xonly_pubkey_serialize(CTX, buf32, &pk_tmp) == 0); } /* pubkey_load called illegal callback */ CHECK(ecount == 3); - CHECK(secp256k1_xonly_pubkey_serialize(ctx, buf32, &xonly_pk) == 1); + CHECK(secp256k1_xonly_pubkey_serialize(CTX, buf32, &xonly_pk) == 1); ecount = 0; - CHECK(secp256k1_xonly_pubkey_parse(ctx, NULL, buf32) == 0); + CHECK(secp256k1_xonly_pubkey_parse(CTX, NULL, buf32) == 0); CHECK(ecount == 1); - CHECK(secp256k1_xonly_pubkey_parse(ctx, &xonly_pk, NULL) == 0); + CHECK(secp256k1_xonly_pubkey_parse(CTX, &xonly_pk, NULL) == 0); CHECK(ecount == 2); /* Serialization and parse roundtrip */ - CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &xonly_pk, NULL, &pk) == 1); - CHECK(secp256k1_xonly_pubkey_serialize(ctx, buf32, &xonly_pk) == 1); - CHECK(secp256k1_xonly_pubkey_parse(ctx, &xonly_pk_tmp, buf32) == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(CTX, &xonly_pk, NULL, &pk) == 1); + CHECK(secp256k1_xonly_pubkey_serialize(CTX, buf32, &xonly_pk) == 1); + CHECK(secp256k1_xonly_pubkey_parse(CTX, &xonly_pk_tmp, buf32) == 1); CHECK(secp256k1_memcmp_var(&xonly_pk, &xonly_pk_tmp, sizeof(xonly_pk)) == 0); /* Test parsing invalid field elements */ memset(&xonly_pk, 1, sizeof(xonly_pk)); /* Overflowing field element */ - CHECK(secp256k1_xonly_pubkey_parse(ctx, &xonly_pk, ones32) == 0); + CHECK(secp256k1_xonly_pubkey_parse(CTX, &xonly_pk, ones32) == 0); CHECK(secp256k1_memcmp_var(&xonly_pk, zeros64, sizeof(xonly_pk)) == 0); memset(&xonly_pk, 1, sizeof(xonly_pk)); /* There's no point with x-coordinate 0 on secp256k1 */ - CHECK(secp256k1_xonly_pubkey_parse(ctx, &xonly_pk, zeros64) == 0); + CHECK(secp256k1_xonly_pubkey_parse(CTX, &xonly_pk, zeros64) == 0); CHECK(secp256k1_memcmp_var(&xonly_pk, zeros64, sizeof(xonly_pk)) == 0); /* If a random 32-byte string can not be parsed with ec_pubkey_parse * (because interpreted as X coordinate it does not correspond to a point on * the curve) then xonly_pubkey_parse should fail as well. */ - for (i = 0; i < count; i++) { + for (i = 0; i < COUNT; i++) { unsigned char rand33[33]; secp256k1_testrand256(&rand33[1]); rand33[0] = SECP256K1_TAG_PUBKEY_EVEN; - if (!secp256k1_ec_pubkey_parse(ctx, &pk, rand33, 33)) { + if (!secp256k1_ec_pubkey_parse(CTX, &pk, rand33, 33)) { memset(&xonly_pk, 1, sizeof(xonly_pk)); - CHECK(secp256k1_xonly_pubkey_parse(ctx, &xonly_pk, &rand33[1]) == 0); + CHECK(secp256k1_xonly_pubkey_parse(CTX, &xonly_pk, &rand33[1]) == 0); CHECK(secp256k1_memcmp_var(&xonly_pk, zeros64, sizeof(xonly_pk)) == 0); } else { - CHECK(secp256k1_xonly_pubkey_parse(ctx, &xonly_pk, &rand33[1]) == 1); + CHECK(secp256k1_xonly_pubkey_parse(CTX, &xonly_pk, &rand33[1]) == 1); } } CHECK(ecount == 2); } -void test_xonly_pubkey_comparison(void) { +static void test_xonly_pubkey_comparison(void) { unsigned char pk1_ser[32] = { 0x58, 0x84, 0xb3, 0xa2, 0x4b, 0x97, 0x37, 0x88, 0x92, 0x38, 0xa6, 0x26, 0x62, 0x52, 0x35, 0x11, 0xd0, 0x9a, 0xa1, 0x1b, 0x80, 0x0b, 0x5e, 0x93, 0x80, 0x26, 0x11, 0xef, 0x67, 0x4b, 0xd9, 0x23 @@ -141,30 +141,30 @@ void test_xonly_pubkey_comparison(void) { secp256k1_xonly_pubkey pk2; int ecount = 0; - set_counting_callbacks(ctx, &ecount); + set_counting_callbacks(CTX, &ecount); - CHECK(secp256k1_xonly_pubkey_parse(ctx, &pk1, pk1_ser) == 1); - CHECK(secp256k1_xonly_pubkey_parse(ctx, &pk2, pk2_ser) == 1); + CHECK(secp256k1_xonly_pubkey_parse(CTX, &pk1, pk1_ser) == 1); + CHECK(secp256k1_xonly_pubkey_parse(CTX, &pk2, pk2_ser) == 1); - CHECK(secp256k1_xonly_pubkey_cmp(ctx, NULL, &pk2) < 0); + CHECK(secp256k1_xonly_pubkey_cmp(CTX, NULL, &pk2) < 0); CHECK(ecount == 1); - CHECK(secp256k1_xonly_pubkey_cmp(ctx, &pk1, NULL) > 0); + CHECK(secp256k1_xonly_pubkey_cmp(CTX, &pk1, NULL) > 0); CHECK(ecount == 2); - CHECK(secp256k1_xonly_pubkey_cmp(ctx, &pk1, &pk2) < 0); - CHECK(secp256k1_xonly_pubkey_cmp(ctx, &pk2, &pk1) > 0); - CHECK(secp256k1_xonly_pubkey_cmp(ctx, &pk1, &pk1) == 0); - CHECK(secp256k1_xonly_pubkey_cmp(ctx, &pk2, &pk2) == 0); + CHECK(secp256k1_xonly_pubkey_cmp(CTX, &pk1, &pk2) < 0); + CHECK(secp256k1_xonly_pubkey_cmp(CTX, &pk2, &pk1) > 0); + CHECK(secp256k1_xonly_pubkey_cmp(CTX, &pk1, &pk1) == 0); + CHECK(secp256k1_xonly_pubkey_cmp(CTX, &pk2, &pk2) == 0); CHECK(ecount == 2); memset(&pk1, 0, sizeof(pk1)); /* illegal pubkey */ - CHECK(secp256k1_xonly_pubkey_cmp(ctx, &pk1, &pk2) < 0); + CHECK(secp256k1_xonly_pubkey_cmp(CTX, &pk1, &pk2) < 0); CHECK(ecount == 3); - CHECK(secp256k1_xonly_pubkey_cmp(ctx, &pk1, &pk1) == 0); + CHECK(secp256k1_xonly_pubkey_cmp(CTX, &pk1, &pk1) == 0); CHECK(ecount == 5); - CHECK(secp256k1_xonly_pubkey_cmp(ctx, &pk2, &pk1) > 0); + CHECK(secp256k1_xonly_pubkey_cmp(CTX, &pk2, &pk1) > 0); CHECK(ecount == 6); } -void test_xonly_pubkey_tweak(void) { +static void test_xonly_pubkey_tweak(void) { unsigned char zeros64[64] = { 0 }; unsigned char overflows[32]; unsigned char sk[32]; @@ -177,48 +177,48 @@ void test_xonly_pubkey_tweak(void) { int ecount; - set_counting_callbacks(ctx, &ecount); + set_counting_callbacks(CTX, &ecount); memset(overflows, 0xff, sizeof(overflows)); secp256k1_testrand256(tweak); secp256k1_testrand256(sk); - CHECK(secp256k1_ec_pubkey_create(ctx, &internal_pk, sk) == 1); - CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &internal_xonly_pk, &pk_parity, &internal_pk) == 1); + CHECK(secp256k1_ec_pubkey_create(CTX, &internal_pk, sk) == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(CTX, &internal_xonly_pk, &pk_parity, &internal_pk) == 1); ecount = 0; - CHECK(secp256k1_xonly_pubkey_tweak_add(ctx, &output_pk, &internal_xonly_pk, tweak) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_add(CTX, &output_pk, &internal_xonly_pk, tweak) == 1); CHECK(ecount == 0); - CHECK(secp256k1_xonly_pubkey_tweak_add(ctx, &output_pk, &internal_xonly_pk, tweak) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_add(CTX, &output_pk, &internal_xonly_pk, tweak) == 1); CHECK(ecount == 0); - CHECK(secp256k1_xonly_pubkey_tweak_add(ctx, &output_pk, &internal_xonly_pk, tweak) == 1); - CHECK(secp256k1_xonly_pubkey_tweak_add(ctx, NULL, &internal_xonly_pk, tweak) == 0); + CHECK(secp256k1_xonly_pubkey_tweak_add(CTX, &output_pk, &internal_xonly_pk, tweak) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_add(CTX, NULL, &internal_xonly_pk, tweak) == 0); CHECK(ecount == 1); - CHECK(secp256k1_xonly_pubkey_tweak_add(ctx, &output_pk, NULL, tweak) == 0); + CHECK(secp256k1_xonly_pubkey_tweak_add(CTX, &output_pk, NULL, tweak) == 0); CHECK(ecount == 2); /* NULL internal_xonly_pk zeroes the output_pk */ CHECK(secp256k1_memcmp_var(&output_pk, zeros64, sizeof(output_pk)) == 0); - CHECK(secp256k1_xonly_pubkey_tweak_add(ctx, &output_pk, &internal_xonly_pk, NULL) == 0); + CHECK(secp256k1_xonly_pubkey_tweak_add(CTX, &output_pk, &internal_xonly_pk, NULL) == 0); CHECK(ecount == 3); /* NULL tweak zeroes the output_pk */ CHECK(secp256k1_memcmp_var(&output_pk, zeros64, sizeof(output_pk)) == 0); /* Invalid tweak zeroes the output_pk */ - CHECK(secp256k1_xonly_pubkey_tweak_add(ctx, &output_pk, &internal_xonly_pk, overflows) == 0); + CHECK(secp256k1_xonly_pubkey_tweak_add(CTX, &output_pk, &internal_xonly_pk, overflows) == 0); CHECK(secp256k1_memcmp_var(&output_pk, zeros64, sizeof(output_pk)) == 0); /* A zero tweak is fine */ - CHECK(secp256k1_xonly_pubkey_tweak_add(ctx, &output_pk, &internal_xonly_pk, zeros64) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_add(CTX, &output_pk, &internal_xonly_pk, zeros64) == 1); /* Fails if the resulting key was infinity */ - for (i = 0; i < count; i++) { + for (i = 0; i < COUNT; i++) { secp256k1_scalar scalar_tweak; /* Because sk may be negated before adding, we need to try with tweak = * sk as well as tweak = -sk. */ secp256k1_scalar_set_b32(&scalar_tweak, sk, NULL); secp256k1_scalar_negate(&scalar_tweak, &scalar_tweak); secp256k1_scalar_get_b32(tweak, &scalar_tweak); - CHECK((secp256k1_xonly_pubkey_tweak_add(ctx, &output_pk, &internal_xonly_pk, sk) == 0) - || (secp256k1_xonly_pubkey_tweak_add(ctx, &output_pk, &internal_xonly_pk, tweak) == 0)); + CHECK((secp256k1_xonly_pubkey_tweak_add(CTX, &output_pk, &internal_xonly_pk, sk) == 0) + || (secp256k1_xonly_pubkey_tweak_add(CTX, &output_pk, &internal_xonly_pk, tweak) == 0)); CHECK(secp256k1_memcmp_var(&output_pk, zeros64, sizeof(output_pk)) == 0); } @@ -226,12 +226,12 @@ void test_xonly_pubkey_tweak(void) { memset(&internal_xonly_pk, 0, sizeof(internal_xonly_pk)); secp256k1_testrand256(tweak); ecount = 0; - CHECK(secp256k1_xonly_pubkey_tweak_add(ctx, &output_pk, &internal_xonly_pk, tweak) == 0); + CHECK(secp256k1_xonly_pubkey_tweak_add(CTX, &output_pk, &internal_xonly_pk, tweak) == 0); CHECK(ecount == 1); CHECK(secp256k1_memcmp_var(&output_pk, zeros64, sizeof(output_pk)) == 0); } -void test_xonly_pubkey_tweak_check(void) { +static void test_xonly_pubkey_tweak_check(void) { unsigned char zeros64[64] = { 0 }; unsigned char overflows[32]; unsigned char sk[32]; @@ -246,49 +246,49 @@ void test_xonly_pubkey_tweak_check(void) { int ecount; - set_counting_callbacks(ctx, &ecount); + set_counting_callbacks(CTX, &ecount); memset(overflows, 0xff, sizeof(overflows)); secp256k1_testrand256(tweak); secp256k1_testrand256(sk); - CHECK(secp256k1_ec_pubkey_create(ctx, &internal_pk, sk) == 1); - CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &internal_xonly_pk, &pk_parity, &internal_pk) == 1); + CHECK(secp256k1_ec_pubkey_create(CTX, &internal_pk, sk) == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(CTX, &internal_xonly_pk, &pk_parity, &internal_pk) == 1); ecount = 0; - CHECK(secp256k1_xonly_pubkey_tweak_add(ctx, &output_pk, &internal_xonly_pk, tweak) == 1); - CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &output_xonly_pk, &pk_parity, &output_pk) == 1); - CHECK(secp256k1_xonly_pubkey_serialize(ctx, buf32, &output_xonly_pk) == 1); - CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, buf32, pk_parity, &internal_xonly_pk, tweak) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_add(CTX, &output_pk, &internal_xonly_pk, tweak) == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(CTX, &output_xonly_pk, &pk_parity, &output_pk) == 1); + CHECK(secp256k1_xonly_pubkey_serialize(CTX, buf32, &output_xonly_pk) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_add_check(CTX, buf32, pk_parity, &internal_xonly_pk, tweak) == 1); CHECK(ecount == 0); - CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, buf32, pk_parity, &internal_xonly_pk, tweak) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_add_check(CTX, buf32, pk_parity, &internal_xonly_pk, tweak) == 1); CHECK(ecount == 0); - CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, buf32, pk_parity, &internal_xonly_pk, tweak) == 1); - CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, NULL, pk_parity, &internal_xonly_pk, tweak) == 0); + CHECK(secp256k1_xonly_pubkey_tweak_add_check(CTX, buf32, pk_parity, &internal_xonly_pk, tweak) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_add_check(CTX, NULL, pk_parity, &internal_xonly_pk, tweak) == 0); CHECK(ecount == 1); /* invalid pk_parity value */ - CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, buf32, 2, &internal_xonly_pk, tweak) == 0); + CHECK(secp256k1_xonly_pubkey_tweak_add_check(CTX, buf32, 2, &internal_xonly_pk, tweak) == 0); CHECK(ecount == 1); - CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, buf32, pk_parity, NULL, tweak) == 0); + CHECK(secp256k1_xonly_pubkey_tweak_add_check(CTX, buf32, pk_parity, NULL, tweak) == 0); CHECK(ecount == 2); - CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, buf32, pk_parity, &internal_xonly_pk, NULL) == 0); + CHECK(secp256k1_xonly_pubkey_tweak_add_check(CTX, buf32, pk_parity, &internal_xonly_pk, NULL) == 0); CHECK(ecount == 3); memset(tweak, 1, sizeof(tweak)); - CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &internal_xonly_pk, NULL, &internal_pk) == 1); - CHECK(secp256k1_xonly_pubkey_tweak_add(ctx, &output_pk, &internal_xonly_pk, tweak) == 1); - CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &output_xonly_pk, &pk_parity, &output_pk) == 1); - CHECK(secp256k1_xonly_pubkey_serialize(ctx, output_pk32, &output_xonly_pk) == 1); - CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, output_pk32, pk_parity, &internal_xonly_pk, tweak) == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(CTX, &internal_xonly_pk, NULL, &internal_pk) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_add(CTX, &output_pk, &internal_xonly_pk, tweak) == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(CTX, &output_xonly_pk, &pk_parity, &output_pk) == 1); + CHECK(secp256k1_xonly_pubkey_serialize(CTX, output_pk32, &output_xonly_pk) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_add_check(CTX, output_pk32, pk_parity, &internal_xonly_pk, tweak) == 1); /* Wrong pk_parity */ - CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, output_pk32, !pk_parity, &internal_xonly_pk, tweak) == 0); + CHECK(secp256k1_xonly_pubkey_tweak_add_check(CTX, output_pk32, !pk_parity, &internal_xonly_pk, tweak) == 0); /* Wrong public key */ - CHECK(secp256k1_xonly_pubkey_serialize(ctx, buf32, &internal_xonly_pk) == 1); - CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, buf32, pk_parity, &internal_xonly_pk, tweak) == 0); + CHECK(secp256k1_xonly_pubkey_serialize(CTX, buf32, &internal_xonly_pk) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_add_check(CTX, buf32, pk_parity, &internal_xonly_pk, tweak) == 0); /* Overflowing tweak not allowed */ - CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, output_pk32, pk_parity, &internal_xonly_pk, overflows) == 0); - CHECK(secp256k1_xonly_pubkey_tweak_add(ctx, &output_pk, &internal_xonly_pk, overflows) == 0); + CHECK(secp256k1_xonly_pubkey_tweak_add_check(CTX, output_pk32, pk_parity, &internal_xonly_pk, overflows) == 0); + CHECK(secp256k1_xonly_pubkey_tweak_add(CTX, &output_pk, &internal_xonly_pk, overflows) == 0); CHECK(secp256k1_memcmp_var(&output_pk, zeros64, sizeof(output_pk)) == 0); CHECK(ecount == 3); } @@ -297,7 +297,7 @@ void test_xonly_pubkey_tweak_check(void) { * additional pubkeys by calling tweak_add. Then verifies every tweak starting * from the last pubkey. */ #define N_PUBKEYS 32 -void test_xonly_pubkey_tweak_recursive(void) { +static void test_xonly_pubkey_tweak_recursive(void) { unsigned char sk[32]; secp256k1_pubkey pk[N_PUBKEYS]; unsigned char pk_serialized[32]; @@ -305,28 +305,28 @@ void test_xonly_pubkey_tweak_recursive(void) { int i; secp256k1_testrand256(sk); - CHECK(secp256k1_ec_pubkey_create(ctx, &pk[0], sk) == 1); + CHECK(secp256k1_ec_pubkey_create(CTX, &pk[0], sk) == 1); /* Add tweaks */ for (i = 0; i < N_PUBKEYS - 1; i++) { secp256k1_xonly_pubkey xonly_pk; memset(tweak[i], i + 1, sizeof(tweak[i])); - CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &xonly_pk, NULL, &pk[i]) == 1); - CHECK(secp256k1_xonly_pubkey_tweak_add(ctx, &pk[i + 1], &xonly_pk, tweak[i]) == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(CTX, &xonly_pk, NULL, &pk[i]) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_add(CTX, &pk[i + 1], &xonly_pk, tweak[i]) == 1); } /* Verify tweaks */ for (i = N_PUBKEYS - 1; i > 0; i--) { secp256k1_xonly_pubkey xonly_pk; int pk_parity; - CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &xonly_pk, &pk_parity, &pk[i]) == 1); - CHECK(secp256k1_xonly_pubkey_serialize(ctx, pk_serialized, &xonly_pk) == 1); - CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &xonly_pk, NULL, &pk[i - 1]) == 1); - CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, pk_serialized, pk_parity, &xonly_pk, tweak[i - 1]) == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(CTX, &xonly_pk, &pk_parity, &pk[i]) == 1); + CHECK(secp256k1_xonly_pubkey_serialize(CTX, pk_serialized, &xonly_pk) == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(CTX, &xonly_pk, NULL, &pk[i - 1]) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_add_check(CTX, pk_serialized, pk_parity, &xonly_pk, tweak[i - 1]) == 1); } } #undef N_PUBKEYS -void test_keypair(void) { +static void test_keypair(void) { unsigned char sk[32]; unsigned char sk_tmp[32]; unsigned char zeros96[96] = { 0 }; @@ -336,10 +336,9 @@ void test_keypair(void) { secp256k1_xonly_pubkey xonly_pk, xonly_pk_tmp; int pk_parity, pk_parity_tmp; int ecount; - secp256k1_context *sttc = secp256k1_context_clone(secp256k1_context_static); - set_counting_callbacks(ctx, &ecount); - set_counting_callbacks(sttc, &ecount); + set_counting_callbacks(CTX, &ecount); + set_counting_callbacks(STATIC_CTX, &ecount); CHECK(sizeof(zeros96) == sizeof(keypair)); memset(overflows, 0xFF, sizeof(overflows)); @@ -347,103 +346,105 @@ void test_keypair(void) { /* Test keypair_create */ ecount = 0; secp256k1_testrand256(sk); - CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1); + CHECK(secp256k1_keypair_create(CTX, &keypair, sk) == 1); CHECK(secp256k1_memcmp_var(zeros96, &keypair, sizeof(keypair)) != 0); CHECK(ecount == 0); - CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1); + CHECK(secp256k1_keypair_create(CTX, &keypair, sk) == 1); CHECK(secp256k1_memcmp_var(zeros96, &keypair, sizeof(keypair)) != 0); CHECK(ecount == 0); - CHECK(secp256k1_keypair_create(ctx, NULL, sk) == 0); + CHECK(secp256k1_keypair_create(CTX, NULL, sk) == 0); CHECK(ecount == 1); - CHECK(secp256k1_keypair_create(ctx, &keypair, NULL) == 0); + CHECK(secp256k1_keypair_create(CTX, &keypair, NULL) == 0); CHECK(secp256k1_memcmp_var(zeros96, &keypair, sizeof(keypair)) == 0); CHECK(ecount == 2); - CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1); + CHECK(secp256k1_keypair_create(CTX, &keypair, sk) == 1); CHECK(ecount == 2); - CHECK(secp256k1_keypair_create(sttc, &keypair, sk) == 0); + CHECK(secp256k1_keypair_create(STATIC_CTX, &keypair, sk) == 0); CHECK(secp256k1_memcmp_var(zeros96, &keypair, sizeof(keypair)) == 0); CHECK(ecount == 3); /* Invalid secret key */ - CHECK(secp256k1_keypair_create(ctx, &keypair, zeros96) == 0); + CHECK(secp256k1_keypair_create(CTX, &keypair, zeros96) == 0); CHECK(secp256k1_memcmp_var(zeros96, &keypair, sizeof(keypair)) == 0); - CHECK(secp256k1_keypair_create(ctx, &keypair, overflows) == 0); + CHECK(secp256k1_keypair_create(CTX, &keypair, overflows) == 0); CHECK(secp256k1_memcmp_var(zeros96, &keypair, sizeof(keypair)) == 0); /* Test keypair_pub */ ecount = 0; secp256k1_testrand256(sk); - CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1); - CHECK(secp256k1_keypair_pub(ctx, &pk, &keypair) == 1); - CHECK(secp256k1_keypair_pub(ctx, NULL, &keypair) == 0); + CHECK(secp256k1_keypair_create(CTX, &keypair, sk) == 1); + CHECK(secp256k1_keypair_pub(CTX, &pk, &keypair) == 1); + CHECK(secp256k1_keypair_pub(CTX, NULL, &keypair) == 0); CHECK(ecount == 1); - CHECK(secp256k1_keypair_pub(ctx, &pk, NULL) == 0); + CHECK(secp256k1_keypair_pub(CTX, &pk, NULL) == 0); CHECK(ecount == 2); CHECK(secp256k1_memcmp_var(zeros96, &pk, sizeof(pk)) == 0); /* Using an invalid keypair is fine for keypair_pub */ memset(&keypair, 0, sizeof(keypair)); - CHECK(secp256k1_keypair_pub(ctx, &pk, &keypair) == 1); + CHECK(secp256k1_keypair_pub(CTX, &pk, &keypair) == 1); CHECK(secp256k1_memcmp_var(zeros96, &pk, sizeof(pk)) == 0); /* keypair holds the same pubkey as pubkey_create */ - CHECK(secp256k1_ec_pubkey_create(ctx, &pk, sk) == 1); - CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1); - CHECK(secp256k1_keypair_pub(ctx, &pk_tmp, &keypair) == 1); + CHECK(secp256k1_ec_pubkey_create(CTX, &pk, sk) == 1); + CHECK(secp256k1_keypair_create(CTX, &keypair, sk) == 1); + CHECK(secp256k1_keypair_pub(CTX, &pk_tmp, &keypair) == 1); CHECK(secp256k1_memcmp_var(&pk, &pk_tmp, sizeof(pk)) == 0); /** Test keypair_xonly_pub **/ ecount = 0; secp256k1_testrand256(sk); - CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1); - CHECK(secp256k1_keypair_xonly_pub(ctx, &xonly_pk, &pk_parity, &keypair) == 1); - CHECK(secp256k1_keypair_xonly_pub(ctx, NULL, &pk_parity, &keypair) == 0); + CHECK(secp256k1_keypair_create(CTX, &keypair, sk) == 1); + CHECK(secp256k1_keypair_xonly_pub(CTX, &xonly_pk, &pk_parity, &keypair) == 1); + CHECK(secp256k1_keypair_xonly_pub(CTX, NULL, &pk_parity, &keypair) == 0); CHECK(ecount == 1); - CHECK(secp256k1_keypair_xonly_pub(ctx, &xonly_pk, NULL, &keypair) == 1); - CHECK(secp256k1_keypair_xonly_pub(ctx, &xonly_pk, &pk_parity, NULL) == 0); + CHECK(secp256k1_keypair_xonly_pub(CTX, &xonly_pk, NULL, &keypair) == 1); + CHECK(secp256k1_keypair_xonly_pub(CTX, &xonly_pk, &pk_parity, NULL) == 0); CHECK(ecount == 2); CHECK(secp256k1_memcmp_var(zeros96, &xonly_pk, sizeof(xonly_pk)) == 0); /* Using an invalid keypair will set the xonly_pk to 0 (first reset * xonly_pk). */ - CHECK(secp256k1_keypair_xonly_pub(ctx, &xonly_pk, &pk_parity, &keypair) == 1); + CHECK(secp256k1_keypair_xonly_pub(CTX, &xonly_pk, &pk_parity, &keypair) == 1); memset(&keypair, 0, sizeof(keypair)); - CHECK(secp256k1_keypair_xonly_pub(ctx, &xonly_pk, &pk_parity, &keypair) == 0); + CHECK(secp256k1_keypair_xonly_pub(CTX, &xonly_pk, &pk_parity, &keypair) == 0); CHECK(secp256k1_memcmp_var(zeros96, &xonly_pk, sizeof(xonly_pk)) == 0); CHECK(ecount == 3); /** keypair holds the same xonly pubkey as pubkey_create **/ - CHECK(secp256k1_ec_pubkey_create(ctx, &pk, sk) == 1); - CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &xonly_pk, &pk_parity, &pk) == 1); - CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1); - CHECK(secp256k1_keypair_xonly_pub(ctx, &xonly_pk_tmp, &pk_parity_tmp, &keypair) == 1); + CHECK(secp256k1_ec_pubkey_create(CTX, &pk, sk) == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(CTX, &xonly_pk, &pk_parity, &pk) == 1); + CHECK(secp256k1_keypair_create(CTX, &keypair, sk) == 1); + CHECK(secp256k1_keypair_xonly_pub(CTX, &xonly_pk_tmp, &pk_parity_tmp, &keypair) == 1); 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(ctx, sk_tmp, &keypair) == 1); - CHECK(secp256k1_keypair_sec(ctx, NULL, &keypair) == 0); + CHECK(secp256k1_keypair_create(CTX, &keypair, sk) == 1); + CHECK(secp256k1_keypair_sec(CTX, sk_tmp, &keypair) == 1); + CHECK(secp256k1_keypair_sec(CTX, NULL, &keypair) == 0); CHECK(ecount == 1); - CHECK(secp256k1_keypair_sec(ctx, sk_tmp, NULL) == 0); + CHECK(secp256k1_keypair_sec(CTX, 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(ctx, &keypair, sk) == 1); - CHECK(secp256k1_keypair_sec(ctx, sk_tmp, &keypair) == 1); + CHECK(secp256k1_keypair_create(CTX, &keypair, sk) == 1); + CHECK(secp256k1_keypair_sec(CTX, 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(ctx, sk_tmp, &keypair) == 1); + CHECK(secp256k1_keypair_sec(CTX, sk_tmp, &keypair) == 1); CHECK(secp256k1_memcmp_var(zeros96, sk_tmp, sizeof(sk_tmp)) == 0); - secp256k1_context_destroy(sttc); + + secp256k1_context_set_error_callback(STATIC_CTX, NULL, NULL); + secp256k1_context_set_illegal_callback(STATIC_CTX, NULL, NULL); } -void test_keypair_add(void) { +static void test_keypair_add(void) { unsigned char sk[32]; secp256k1_keypair keypair; unsigned char overflows[32]; @@ -452,49 +453,49 @@ void test_keypair_add(void) { int i; int ecount = 0; - set_counting_callbacks(ctx, &ecount); + set_counting_callbacks(CTX, &ecount); CHECK(sizeof(zeros96) == sizeof(keypair)); secp256k1_testrand256(sk); secp256k1_testrand256(tweak); memset(overflows, 0xFF, 32); - CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1); + CHECK(secp256k1_keypair_create(CTX, &keypair, sk) == 1); - CHECK(secp256k1_keypair_xonly_tweak_add(ctx, &keypair, tweak) == 1); + CHECK(secp256k1_keypair_xonly_tweak_add(CTX, &keypair, tweak) == 1); CHECK(ecount == 0); - CHECK(secp256k1_keypair_xonly_tweak_add(ctx, &keypair, tweak) == 1); + CHECK(secp256k1_keypair_xonly_tweak_add(CTX, &keypair, tweak) == 1); CHECK(ecount == 0); - CHECK(secp256k1_keypair_xonly_tweak_add(ctx, &keypair, tweak) == 1); - CHECK(secp256k1_keypair_xonly_tweak_add(ctx, NULL, tweak) == 0); + CHECK(secp256k1_keypair_xonly_tweak_add(CTX, &keypair, tweak) == 1); + CHECK(secp256k1_keypair_xonly_tweak_add(CTX, NULL, tweak) == 0); CHECK(ecount == 1); - CHECK(secp256k1_keypair_xonly_tweak_add(ctx, &keypair, NULL) == 0); + CHECK(secp256k1_keypair_xonly_tweak_add(CTX, &keypair, NULL) == 0); CHECK(ecount == 2); /* This does not set the keypair to zeroes */ CHECK(secp256k1_memcmp_var(&keypair, zeros96, sizeof(keypair)) != 0); /* Invalid tweak zeroes the keypair */ - CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1); - CHECK(secp256k1_keypair_xonly_tweak_add(ctx, &keypair, overflows) == 0); + CHECK(secp256k1_keypair_create(CTX, &keypair, sk) == 1); + CHECK(secp256k1_keypair_xonly_tweak_add(CTX, &keypair, overflows) == 0); CHECK(secp256k1_memcmp_var(&keypair, zeros96, sizeof(keypair)) == 0); /* A zero tweak is fine */ - CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1); - CHECK(secp256k1_keypair_xonly_tweak_add(ctx, &keypair, zeros96) == 1); + CHECK(secp256k1_keypair_create(CTX, &keypair, sk) == 1); + CHECK(secp256k1_keypair_xonly_tweak_add(CTX, &keypair, zeros96) == 1); /* Fails if the resulting keypair was (sk=0, pk=infinity) */ - for (i = 0; i < count; i++) { + for (i = 0; i < COUNT; i++) { secp256k1_scalar scalar_tweak; secp256k1_keypair keypair_tmp; secp256k1_testrand256(sk); - CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1); + CHECK(secp256k1_keypair_create(CTX, &keypair, sk) == 1); memcpy(&keypair_tmp, &keypair, sizeof(keypair)); /* Because sk may be negated before adding, we need to try with tweak = * sk as well as tweak = -sk. */ secp256k1_scalar_set_b32(&scalar_tweak, sk, NULL); secp256k1_scalar_negate(&scalar_tweak, &scalar_tweak); secp256k1_scalar_get_b32(tweak, &scalar_tweak); - CHECK((secp256k1_keypair_xonly_tweak_add(ctx, &keypair, sk) == 0) - || (secp256k1_keypair_xonly_tweak_add(ctx, &keypair_tmp, tweak) == 0)); + CHECK((secp256k1_keypair_xonly_tweak_add(CTX, &keypair, sk) == 0) + || (secp256k1_keypair_xonly_tweak_add(CTX, &keypair_tmp, tweak) == 0)); CHECK(secp256k1_memcmp_var(&keypair, zeros96, sizeof(keypair)) == 0 || secp256k1_memcmp_var(&keypair_tmp, zeros96, sizeof(keypair_tmp)) == 0); } @@ -503,23 +504,23 @@ void test_keypair_add(void) { memset(&keypair, 0, sizeof(keypair)); secp256k1_testrand256(tweak); ecount = 0; - CHECK(secp256k1_keypair_xonly_tweak_add(ctx, &keypair, tweak) == 0); + CHECK(secp256k1_keypair_xonly_tweak_add(CTX, &keypair, tweak) == 0); CHECK(ecount == 1); CHECK(secp256k1_memcmp_var(&keypair, zeros96, sizeof(keypair)) == 0); /* Only seckey part of keypair invalid */ - CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1); + CHECK(secp256k1_keypair_create(CTX, &keypair, sk) == 1); memset(&keypair, 0, 32); - CHECK(secp256k1_keypair_xonly_tweak_add(ctx, &keypair, tweak) == 0); + CHECK(secp256k1_keypair_xonly_tweak_add(CTX, &keypair, tweak) == 0); CHECK(ecount == 2); /* Only pubkey part of keypair invalid */ - CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1); + CHECK(secp256k1_keypair_create(CTX, &keypair, sk) == 1); memset(&keypair.data[32], 0, 64); - CHECK(secp256k1_keypair_xonly_tweak_add(ctx, &keypair, tweak) == 0); + CHECK(secp256k1_keypair_xonly_tweak_add(CTX, &keypair, tweak) == 0); CHECK(ecount == 3); /* Check that the keypair_tweak_add implementation is correct */ - CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1); - for (i = 0; i < count; i++) { + CHECK(secp256k1_keypair_create(CTX, &keypair, sk) == 1); + for (i = 0; i < COUNT; i++) { secp256k1_xonly_pubkey internal_pk; secp256k1_xonly_pubkey output_pk; secp256k1_pubkey output_pk_xy; @@ -529,27 +530,27 @@ void test_keypair_add(void) { int pk_parity; secp256k1_testrand256(tweak); - CHECK(secp256k1_keypair_xonly_pub(ctx, &internal_pk, NULL, &keypair) == 1); - CHECK(secp256k1_keypair_xonly_tweak_add(ctx, &keypair, tweak) == 1); - CHECK(secp256k1_keypair_xonly_pub(ctx, &output_pk, &pk_parity, &keypair) == 1); + CHECK(secp256k1_keypair_xonly_pub(CTX, &internal_pk, NULL, &keypair) == 1); + CHECK(secp256k1_keypair_xonly_tweak_add(CTX, &keypair, tweak) == 1); + CHECK(secp256k1_keypair_xonly_pub(CTX, &output_pk, &pk_parity, &keypair) == 1); /* Check that it passes xonly_pubkey_tweak_add_check */ - CHECK(secp256k1_xonly_pubkey_serialize(ctx, pk32, &output_pk) == 1); - CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, pk32, pk_parity, &internal_pk, tweak) == 1); + CHECK(secp256k1_xonly_pubkey_serialize(CTX, pk32, &output_pk) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_add_check(CTX, pk32, pk_parity, &internal_pk, tweak) == 1); /* Check that the resulting pubkey matches xonly_pubkey_tweak_add */ - CHECK(secp256k1_keypair_pub(ctx, &output_pk_xy, &keypair) == 1); - CHECK(secp256k1_xonly_pubkey_tweak_add(ctx, &output_pk_expected, &internal_pk, tweak) == 1); + CHECK(secp256k1_keypair_pub(CTX, &output_pk_xy, &keypair) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_add(CTX, &output_pk_expected, &internal_pk, tweak) == 1); 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_keypair_sec(ctx, sk32, &keypair) == 1); - CHECK(secp256k1_ec_pubkey_create(ctx, &output_pk_expected, sk32) == 1); + CHECK(secp256k1_keypair_sec(CTX, 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); } } -void run_extrakeys_tests(void) { +static void run_extrakeys_tests(void) { /* xonly key test cases */ test_xonly_pubkey(); test_xonly_pubkey_tweak(); diff --git a/src/secp256k1/src/modules/recovery/bench_impl.h b/src/secp256k1/src/modules/recovery/bench_impl.h index ffa00df479..57108d4524 100644 --- a/src/secp256k1/src/modules/recovery/bench_impl.h +++ b/src/secp256k1/src/modules/recovery/bench_impl.h @@ -15,7 +15,7 @@ typedef struct { unsigned char sig[64]; } bench_recover_data; -void bench_recover(void* arg, int iters) { +static void bench_recover(void* arg, int iters) { int i; bench_recover_data *data = (bench_recover_data*)arg; secp256k1_pubkey pubkey; @@ -36,7 +36,7 @@ void bench_recover(void* arg, int iters) { } } -void bench_recover_setup(void* arg) { +static void bench_recover_setup(void* arg) { int i; bench_recover_data *data = (bench_recover_data*)arg; @@ -48,7 +48,7 @@ void bench_recover_setup(void* arg) { } } -void run_recovery_bench(int iters, int argc, char** argv) { +static void run_recovery_bench(int iters, int argc, char** argv) { bench_recover_data data; int d = argc == 1; diff --git a/src/secp256k1/src/modules/recovery/tests_exhaustive_impl.h b/src/secp256k1/src/modules/recovery/tests_exhaustive_impl.h index ed9386b6f8..6bbc02b9a8 100644 --- a/src/secp256k1/src/modules/recovery/tests_exhaustive_impl.h +++ b/src/secp256k1/src/modules/recovery/tests_exhaustive_impl.h @@ -10,7 +10,7 @@ #include "main_impl.h" #include "../../../include/secp256k1_recovery.h" -void test_exhaustive_recovery_sign(const secp256k1_context *ctx, const secp256k1_ge *group) { +static void test_exhaustive_recovery_sign(const secp256k1_context *ctx, const secp256k1_ge *group) { int i, j, k; uint64_t iter = 0; @@ -43,8 +43,7 @@ void test_exhaustive_recovery_sign(const secp256k1_context *ctx, const secp256k1 (k * (EXHAUSTIVE_TEST_ORDER - s)) % EXHAUSTIVE_TEST_ORDER == (i + r * j) % EXHAUSTIVE_TEST_ORDER); /* The recid's second bit is for conveying overflow (R.x value >= group order). * In the actual secp256k1 this is an astronomically unlikely event, but in the - * small group used here, it will be the case for all points except the ones where - * R.x=1 (which the group is specifically selected to have). + * small group used here, it will almost certainly be the case for all points. * Note that this isn't actually useful; full recovery would need to convey * floor(R.x / group_order), but only one bit is used as that is sufficient * in the real group. */ @@ -79,7 +78,7 @@ void test_exhaustive_recovery_sign(const secp256k1_context *ctx, const secp256k1 } } -void test_exhaustive_recovery_verify(const secp256k1_context *ctx, const secp256k1_ge *group) { +static void test_exhaustive_recovery_verify(const secp256k1_context *ctx, const secp256k1_ge *group) { /* This is essentially a copy of test_exhaustive_verify, with recovery added */ int s, r, msg, key; uint64_t iter = 0; diff --git a/src/secp256k1/src/modules/recovery/tests_impl.h b/src/secp256k1/src/modules/recovery/tests_impl.h index 0ff9294e38..3502c71ffe 100644 --- a/src/secp256k1/src/modules/recovery/tests_impl.h +++ b/src/secp256k1/src/modules/recovery/tests_impl.h @@ -28,9 +28,8 @@ static int recovery_test_nonce_function(unsigned char *nonce32, const unsigned c return secp256k1_testrand_bits(1); } -void test_ecdsa_recovery_api(void) { +static void test_ecdsa_recovery_api(void) { /* Setup contexts that just count errors */ - secp256k1_context *sttc = secp256k1_context_clone(secp256k1_context_static); secp256k1_pubkey pubkey; secp256k1_pubkey recpubkey; secp256k1_ecdsa_signature normal_sig; @@ -46,88 +45,89 @@ void test_ecdsa_recovery_api(void) { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - secp256k1_context_set_error_callback(ctx, counting_illegal_callback_fn, &ecount); - secp256k1_context_set_illegal_callback(ctx, counting_illegal_callback_fn, &ecount); - secp256k1_context_set_error_callback(sttc, counting_illegal_callback_fn, &ecount); - secp256k1_context_set_illegal_callback(sttc, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(CTX, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(CTX, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(STATIC_CTX, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(STATIC_CTX, counting_illegal_callback_fn, &ecount); /* Construct and verify corresponding public key. */ - CHECK(secp256k1_ec_seckey_verify(ctx, privkey) == 1); - CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, privkey) == 1); + CHECK(secp256k1_ec_seckey_verify(CTX, privkey) == 1); + CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey, privkey) == 1); /* Check bad contexts and NULLs for signing */ ecount = 0; - CHECK(secp256k1_ecdsa_sign_recoverable(ctx, &recsig, message, privkey, NULL, NULL) == 1); + CHECK(secp256k1_ecdsa_sign_recoverable(CTX, &recsig, message, privkey, NULL, NULL) == 1); CHECK(ecount == 0); - CHECK(secp256k1_ecdsa_sign_recoverable(ctx, NULL, message, privkey, NULL, NULL) == 0); + CHECK(secp256k1_ecdsa_sign_recoverable(CTX, NULL, message, privkey, NULL, NULL) == 0); CHECK(ecount == 1); - CHECK(secp256k1_ecdsa_sign_recoverable(ctx, &recsig, NULL, privkey, NULL, NULL) == 0); + CHECK(secp256k1_ecdsa_sign_recoverable(CTX, &recsig, NULL, privkey, NULL, NULL) == 0); CHECK(ecount == 2); - CHECK(secp256k1_ecdsa_sign_recoverable(ctx, &recsig, message, NULL, NULL, NULL) == 0); + CHECK(secp256k1_ecdsa_sign_recoverable(CTX, &recsig, message, NULL, NULL, NULL) == 0); CHECK(ecount == 3); - CHECK(secp256k1_ecdsa_sign_recoverable(sttc, &recsig, message, privkey, NULL, NULL) == 0); + CHECK(secp256k1_ecdsa_sign_recoverable(STATIC_CTX, &recsig, message, privkey, NULL, NULL) == 0); CHECK(ecount == 4); /* This will fail or succeed randomly, and in either case will not ARG_CHECK failure */ - secp256k1_ecdsa_sign_recoverable(ctx, &recsig, message, privkey, recovery_test_nonce_function, NULL); + secp256k1_ecdsa_sign_recoverable(CTX, &recsig, message, privkey, recovery_test_nonce_function, NULL); CHECK(ecount == 4); /* These will all fail, but not in ARG_CHECK way */ - CHECK(secp256k1_ecdsa_sign_recoverable(ctx, &recsig, message, zero_privkey, NULL, NULL) == 0); - CHECK(secp256k1_ecdsa_sign_recoverable(ctx, &recsig, message, over_privkey, NULL, NULL) == 0); + CHECK(secp256k1_ecdsa_sign_recoverable(CTX, &recsig, message, zero_privkey, NULL, NULL) == 0); + CHECK(secp256k1_ecdsa_sign_recoverable(CTX, &recsig, message, over_privkey, NULL, NULL) == 0); /* This one will succeed. */ - CHECK(secp256k1_ecdsa_sign_recoverable(ctx, &recsig, message, privkey, NULL, NULL) == 1); + CHECK(secp256k1_ecdsa_sign_recoverable(CTX, &recsig, message, privkey, NULL, NULL) == 1); CHECK(ecount == 4); /* Check signing with a goofy nonce function */ /* Check bad contexts and NULLs for recovery */ ecount = 0; - CHECK(secp256k1_ecdsa_recover(ctx, &recpubkey, &recsig, message) == 1); + CHECK(secp256k1_ecdsa_recover(CTX, &recpubkey, &recsig, message) == 1); CHECK(ecount == 0); - CHECK(secp256k1_ecdsa_recover(ctx, NULL, &recsig, message) == 0); + CHECK(secp256k1_ecdsa_recover(CTX, NULL, &recsig, message) == 0); CHECK(ecount == 1); - CHECK(secp256k1_ecdsa_recover(ctx, &recpubkey, NULL, message) == 0); + CHECK(secp256k1_ecdsa_recover(CTX, &recpubkey, NULL, message) == 0); CHECK(ecount == 2); - CHECK(secp256k1_ecdsa_recover(ctx, &recpubkey, &recsig, NULL) == 0); + CHECK(secp256k1_ecdsa_recover(CTX, &recpubkey, &recsig, NULL) == 0); CHECK(ecount == 3); /* Check NULLs for conversion */ - CHECK(secp256k1_ecdsa_sign(ctx, &normal_sig, message, privkey, NULL, NULL) == 1); + CHECK(secp256k1_ecdsa_sign(CTX, &normal_sig, message, privkey, NULL, NULL) == 1); ecount = 0; - CHECK(secp256k1_ecdsa_recoverable_signature_convert(ctx, NULL, &recsig) == 0); + CHECK(secp256k1_ecdsa_recoverable_signature_convert(CTX, NULL, &recsig) == 0); CHECK(ecount == 1); - CHECK(secp256k1_ecdsa_recoverable_signature_convert(ctx, &normal_sig, NULL) == 0); + CHECK(secp256k1_ecdsa_recoverable_signature_convert(CTX, &normal_sig, NULL) == 0); CHECK(ecount == 2); - CHECK(secp256k1_ecdsa_recoverable_signature_convert(ctx, &normal_sig, &recsig) == 1); + CHECK(secp256k1_ecdsa_recoverable_signature_convert(CTX, &normal_sig, &recsig) == 1); /* Check NULLs for de/serialization */ - CHECK(secp256k1_ecdsa_sign_recoverable(ctx, &recsig, message, privkey, NULL, NULL) == 1); + CHECK(secp256k1_ecdsa_sign_recoverable(CTX, &recsig, message, privkey, NULL, NULL) == 1); ecount = 0; - CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(ctx, NULL, &recid, &recsig) == 0); + CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(CTX, NULL, &recid, &recsig) == 0); CHECK(ecount == 1); - CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(ctx, sig, NULL, &recsig) == 0); + CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(CTX, sig, NULL, &recsig) == 0); CHECK(ecount == 2); - CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(ctx, sig, &recid, NULL) == 0); + CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(CTX, sig, &recid, NULL) == 0); CHECK(ecount == 3); - CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(ctx, sig, &recid, &recsig) == 1); + CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(CTX, sig, &recid, &recsig) == 1); - CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, NULL, sig, recid) == 0); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(CTX, NULL, sig, recid) == 0); CHECK(ecount == 4); - CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &recsig, NULL, recid) == 0); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(CTX, &recsig, NULL, recid) == 0); CHECK(ecount == 5); - CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &recsig, sig, -1) == 0); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(CTX, &recsig, sig, -1) == 0); CHECK(ecount == 6); - CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &recsig, sig, 5) == 0); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(CTX, &recsig, sig, 5) == 0); CHECK(ecount == 7); /* overflow in signature will fail but not affect ecount */ memcpy(sig, over_privkey, 32); - CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &recsig, sig, recid) == 0); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(CTX, &recsig, sig, recid) == 0); CHECK(ecount == 7); /* cleanup */ - secp256k1_context_destroy(sttc); + secp256k1_context_set_error_callback(STATIC_CTX, NULL, NULL); + secp256k1_context_set_illegal_callback(STATIC_CTX, NULL, NULL); } -void test_ecdsa_recovery_end_to_end(void) { +static void test_ecdsa_recovery_end_to_end(void) { unsigned char extra[32] = {0x00}; unsigned char privkey[32]; unsigned char message[32]; @@ -148,45 +148,45 @@ void test_ecdsa_recovery_end_to_end(void) { } /* Construct and verify corresponding public key. */ - CHECK(secp256k1_ec_seckey_verify(ctx, privkey) == 1); - CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, privkey) == 1); + CHECK(secp256k1_ec_seckey_verify(CTX, privkey) == 1); + CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey, privkey) == 1); /* Serialize/parse compact and verify/recover. */ extra[0] = 0; - CHECK(secp256k1_ecdsa_sign_recoverable(ctx, &rsignature[0], message, privkey, NULL, NULL) == 1); - CHECK(secp256k1_ecdsa_sign(ctx, &signature[0], message, privkey, NULL, NULL) == 1); - CHECK(secp256k1_ecdsa_sign_recoverable(ctx, &rsignature[4], message, privkey, NULL, NULL) == 1); - CHECK(secp256k1_ecdsa_sign_recoverable(ctx, &rsignature[1], message, privkey, NULL, extra) == 1); + CHECK(secp256k1_ecdsa_sign_recoverable(CTX, &rsignature[0], message, privkey, NULL, NULL) == 1); + CHECK(secp256k1_ecdsa_sign(CTX, &signature[0], message, privkey, NULL, NULL) == 1); + CHECK(secp256k1_ecdsa_sign_recoverable(CTX, &rsignature[4], message, privkey, NULL, NULL) == 1); + CHECK(secp256k1_ecdsa_sign_recoverable(CTX, &rsignature[1], message, privkey, NULL, extra) == 1); extra[31] = 1; - CHECK(secp256k1_ecdsa_sign_recoverable(ctx, &rsignature[2], message, privkey, NULL, extra) == 1); + CHECK(secp256k1_ecdsa_sign_recoverable(CTX, &rsignature[2], message, privkey, NULL, extra) == 1); extra[31] = 0; extra[0] = 1; - CHECK(secp256k1_ecdsa_sign_recoverable(ctx, &rsignature[3], message, privkey, NULL, extra) == 1); - CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(ctx, sig, &recid, &rsignature[4]) == 1); - CHECK(secp256k1_ecdsa_recoverable_signature_convert(ctx, &signature[4], &rsignature[4]) == 1); + CHECK(secp256k1_ecdsa_sign_recoverable(CTX, &rsignature[3], message, privkey, NULL, extra) == 1); + CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(CTX, sig, &recid, &rsignature[4]) == 1); + CHECK(secp256k1_ecdsa_recoverable_signature_convert(CTX, &signature[4], &rsignature[4]) == 1); CHECK(secp256k1_memcmp_var(&signature[4], &signature[0], 64) == 0); - CHECK(secp256k1_ecdsa_verify(ctx, &signature[4], message, &pubkey) == 1); + CHECK(secp256k1_ecdsa_verify(CTX, &signature[4], message, &pubkey) == 1); memset(&rsignature[4], 0, sizeof(rsignature[4])); - CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsignature[4], sig, recid) == 1); - CHECK(secp256k1_ecdsa_recoverable_signature_convert(ctx, &signature[4], &rsignature[4]) == 1); - CHECK(secp256k1_ecdsa_verify(ctx, &signature[4], message, &pubkey) == 1); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(CTX, &rsignature[4], sig, recid) == 1); + CHECK(secp256k1_ecdsa_recoverable_signature_convert(CTX, &signature[4], &rsignature[4]) == 1); + CHECK(secp256k1_ecdsa_verify(CTX, &signature[4], message, &pubkey) == 1); /* Parse compact (with recovery id) and recover. */ - CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsignature[4], sig, recid) == 1); - CHECK(secp256k1_ecdsa_recover(ctx, &recpubkey, &rsignature[4], message) == 1); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(CTX, &rsignature[4], sig, recid) == 1); + CHECK(secp256k1_ecdsa_recover(CTX, &recpubkey, &rsignature[4], message) == 1); CHECK(secp256k1_memcmp_var(&pubkey, &recpubkey, sizeof(pubkey)) == 0); /* Serialize/destroy/parse signature and verify again. */ - CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(ctx, sig, &recid, &rsignature[4]) == 1); + CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(CTX, sig, &recid, &rsignature[4]) == 1); sig[secp256k1_testrand_bits(6)] += 1 + secp256k1_testrand_int(255); - CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsignature[4], sig, recid) == 1); - CHECK(secp256k1_ecdsa_recoverable_signature_convert(ctx, &signature[4], &rsignature[4]) == 1); - CHECK(secp256k1_ecdsa_verify(ctx, &signature[4], message, &pubkey) == 0); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(CTX, &rsignature[4], sig, recid) == 1); + CHECK(secp256k1_ecdsa_recoverable_signature_convert(CTX, &signature[4], &rsignature[4]) == 1); + CHECK(secp256k1_ecdsa_verify(CTX, &signature[4], message, &pubkey) == 0); /* Recover again */ - CHECK(secp256k1_ecdsa_recover(ctx, &recpubkey, &rsignature[4], message) == 0 || + CHECK(secp256k1_ecdsa_recover(CTX, &recpubkey, &rsignature[4], message) == 0 || secp256k1_memcmp_var(&pubkey, &recpubkey, sizeof(pubkey)) != 0); } /* Tests several edge cases. */ -void test_ecdsa_recovery_edge_cases(void) { +static void test_ecdsa_recovery_edge_cases(void) { const unsigned char msg32[32] = { 'T', 'h', 'i', 's', ' ', 'i', 's', ' ', 'a', ' ', 'v', 'e', 'r', 'y', ' ', 's', @@ -222,14 +222,14 @@ void test_ecdsa_recovery_edge_cases(void) { secp256k1_ecdsa_signature sig; int recid; - CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sig64, 0)); - CHECK(!secp256k1_ecdsa_recover(ctx, &pubkey, &rsig, msg32)); - CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sig64, 1)); - CHECK(secp256k1_ecdsa_recover(ctx, &pubkey, &rsig, msg32)); - CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sig64, 2)); - CHECK(!secp256k1_ecdsa_recover(ctx, &pubkey, &rsig, msg32)); - CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sig64, 3)); - CHECK(!secp256k1_ecdsa_recover(ctx, &pubkey, &rsig, msg32)); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(CTX, &rsig, sig64, 0)); + CHECK(!secp256k1_ecdsa_recover(CTX, &pubkey, &rsig, msg32)); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(CTX, &rsig, sig64, 1)); + CHECK(secp256k1_ecdsa_recover(CTX, &pubkey, &rsig, msg32)); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(CTX, &rsig, sig64, 2)); + CHECK(!secp256k1_ecdsa_recover(CTX, &pubkey, &rsig, msg32)); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(CTX, &rsig, sig64, 3)); + CHECK(!secp256k1_ecdsa_recover(CTX, &pubkey, &rsig, msg32)); for (recid = 0; recid < 4; recid++) { int i; @@ -274,40 +274,40 @@ void test_ecdsa_recovery_edge_cases(void) { 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x45, 0x02, 0x01, 0x04 }; - CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sigb64, recid) == 1); - CHECK(secp256k1_ecdsa_recover(ctx, &pubkeyb, &rsig, msg32) == 1); - CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbder, sizeof(sigbder)) == 1); - CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 1); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(CTX, &rsig, sigb64, recid) == 1); + CHECK(secp256k1_ecdsa_recover(CTX, &pubkeyb, &rsig, msg32) == 1); + CHECK(secp256k1_ecdsa_signature_parse_der(CTX, &sig, sigbder, sizeof(sigbder)) == 1); + CHECK(secp256k1_ecdsa_verify(CTX, &sig, msg32, &pubkeyb) == 1); for (recid2 = 0; recid2 < 4; recid2++) { secp256k1_pubkey pubkey2b; - CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sigb64, recid2) == 1); - CHECK(secp256k1_ecdsa_recover(ctx, &pubkey2b, &rsig, msg32) == 1); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(CTX, &rsig, sigb64, recid2) == 1); + CHECK(secp256k1_ecdsa_recover(CTX, &pubkey2b, &rsig, msg32) == 1); /* Verifying with (order + r,4) should always fail. */ - CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderlong, sizeof(sigbderlong)) == 1); - CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(CTX, &sig, sigbderlong, sizeof(sigbderlong)) == 1); + CHECK(secp256k1_ecdsa_verify(CTX, &sig, msg32, &pubkeyb) == 0); } /* DER parsing tests. */ /* Zero length r/s. */ - CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigcder_zr, sizeof(sigcder_zr)) == 0); - CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigcder_zs, sizeof(sigcder_zs)) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(CTX, &sig, sigcder_zr, sizeof(sigcder_zr)) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(CTX, &sig, sigcder_zs, sizeof(sigcder_zs)) == 0); /* Leading zeros. */ - CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt1, sizeof(sigbderalt1)) == 0); - CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt2, sizeof(sigbderalt2)) == 0); - CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt3, sizeof(sigbderalt3)) == 0); - CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt4, sizeof(sigbderalt4)) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(CTX, &sig, sigbderalt1, sizeof(sigbderalt1)) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(CTX, &sig, sigbderalt2, sizeof(sigbderalt2)) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(CTX, &sig, sigbderalt3, sizeof(sigbderalt3)) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(CTX, &sig, sigbderalt4, sizeof(sigbderalt4)) == 0); sigbderalt3[4] = 1; - CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt3, sizeof(sigbderalt3)) == 1); - CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(CTX, &sig, sigbderalt3, sizeof(sigbderalt3)) == 1); + CHECK(secp256k1_ecdsa_verify(CTX, &sig, msg32, &pubkeyb) == 0); sigbderalt4[7] = 1; - CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt4, sizeof(sigbderalt4)) == 1); - CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(CTX, &sig, sigbderalt4, sizeof(sigbderalt4)) == 1); + CHECK(secp256k1_ecdsa_verify(CTX, &sig, msg32, &pubkeyb) == 0); /* Damage signature. */ sigbder[7]++; - CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbder, sizeof(sigbder)) == 1); - CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(CTX, &sig, sigbder, sizeof(sigbder)) == 1); + CHECK(secp256k1_ecdsa_verify(CTX, &sig, msg32, &pubkeyb) == 0); sigbder[7]--; - CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbder, 6) == 0); - CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbder, sizeof(sigbder) - 1) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(CTX, &sig, sigbder, 6) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(CTX, &sig, sigbder, sizeof(sigbder) - 1) == 0); for(i = 0; i < 8; i++) { int c; unsigned char orig = sigbder[i]; @@ -317,7 +317,7 @@ void test_ecdsa_recovery_edge_cases(void) { continue; } sigbder[i] = c; - CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbder, sizeof(sigbder)) == 0 || secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(CTX, &sig, sigbder, sizeof(sigbder)) == 0 || secp256k1_ecdsa_verify(CTX, &sig, msg32, &pubkeyb) == 0); } sigbder[i] = orig; } @@ -338,33 +338,33 @@ void test_ecdsa_recovery_edge_cases(void) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, }; secp256k1_pubkey pubkeyc; - CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sigc64, 0) == 1); - CHECK(secp256k1_ecdsa_recover(ctx, &pubkeyc, &rsig, msg32) == 1); - CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigcder, sizeof(sigcder)) == 1); - CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyc) == 1); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(CTX, &rsig, sigc64, 0) == 1); + CHECK(secp256k1_ecdsa_recover(CTX, &pubkeyc, &rsig, msg32) == 1); + CHECK(secp256k1_ecdsa_signature_parse_der(CTX, &sig, sigcder, sizeof(sigcder)) == 1); + CHECK(secp256k1_ecdsa_verify(CTX, &sig, msg32, &pubkeyc) == 1); sigcder[4] = 0; sigc64[31] = 0; - CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sigc64, 0) == 1); - CHECK(secp256k1_ecdsa_recover(ctx, &pubkeyb, &rsig, msg32) == 0); - CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigcder, sizeof(sigcder)) == 1); - CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyc) == 0); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(CTX, &rsig, sigc64, 0) == 1); + CHECK(secp256k1_ecdsa_recover(CTX, &pubkeyb, &rsig, msg32) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(CTX, &sig, sigcder, sizeof(sigcder)) == 1); + CHECK(secp256k1_ecdsa_verify(CTX, &sig, msg32, &pubkeyc) == 0); sigcder[4] = 1; sigcder[7] = 0; sigc64[31] = 1; sigc64[63] = 0; - CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sigc64, 0) == 1); - CHECK(secp256k1_ecdsa_recover(ctx, &pubkeyb, &rsig, msg32) == 0); - CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigcder, sizeof(sigcder)) == 1); - CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyc) == 0); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(CTX, &rsig, sigc64, 0) == 1); + CHECK(secp256k1_ecdsa_recover(CTX, &pubkeyb, &rsig, msg32) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(CTX, &sig, sigcder, sizeof(sigcder)) == 1); + CHECK(secp256k1_ecdsa_verify(CTX, &sig, msg32, &pubkeyc) == 0); } } -void run_recovery_tests(void) { +static void run_recovery_tests(void) { int i; - for (i = 0; i < count; i++) { + for (i = 0; i < COUNT; i++) { test_ecdsa_recovery_api(); } - for (i = 0; i < 64*count; i++) { + for (i = 0; i < 64*COUNT; i++) { test_ecdsa_recovery_end_to_end(); } test_ecdsa_recovery_edge_cases(); diff --git a/src/secp256k1/src/modules/schnorrsig/bench_impl.h b/src/secp256k1/src/modules/schnorrsig/bench_impl.h index f0b0d3de75..93a878ede3 100644 --- a/src/secp256k1/src/modules/schnorrsig/bench_impl.h +++ b/src/secp256k1/src/modules/schnorrsig/bench_impl.h @@ -21,7 +21,7 @@ typedef struct { const unsigned char **msgs; } bench_schnorrsig_data; -void bench_schnorrsig_sign(void* arg, int iters) { +static void bench_schnorrsig_sign(void* arg, int iters) { bench_schnorrsig_data *data = (bench_schnorrsig_data *)arg; int i; unsigned char msg[MSGLEN] = {0}; @@ -34,7 +34,7 @@ void bench_schnorrsig_sign(void* arg, int iters) { } } -void bench_schnorrsig_verify(void* arg, int iters) { +static void bench_schnorrsig_verify(void* arg, int iters) { bench_schnorrsig_data *data = (bench_schnorrsig_data *)arg; int i; @@ -45,7 +45,7 @@ void bench_schnorrsig_verify(void* arg, int iters) { } } -void run_schnorrsig_bench(int iters, int argc, char** argv) { +static void run_schnorrsig_bench(int iters, int argc, char** argv) { int i; bench_schnorrsig_data data; int d = argc == 1; diff --git a/src/secp256k1/src/modules/schnorrsig/tests_impl.h b/src/secp256k1/src/modules/schnorrsig/tests_impl.h index 06cc097cc1..062005ee63 100644 --- a/src/secp256k1/src/modules/schnorrsig/tests_impl.h +++ b/src/secp256k1/src/modules/schnorrsig/tests_impl.h @@ -12,7 +12,7 @@ /* Checks that a bit flip in the n_flip-th argument (that has n_bytes many * bytes) changes the hash function */ -void nonce_function_bip340_bitflip(unsigned char **args, size_t n_flip, size_t n_bytes, size_t msglen, size_t algolen) { +static void nonce_function_bip340_bitflip(unsigned char **args, size_t n_flip, size_t n_bytes, size_t msglen, size_t algolen) { unsigned char nonces[2][32]; CHECK(nonce_function_bip340(nonces[0], args[0], msglen, args[1], args[2], args[3], algolen, args[4]) == 1); secp256k1_testrand_flip(args[n_flip], n_bytes); @@ -23,7 +23,7 @@ void nonce_function_bip340_bitflip(unsigned char **args, size_t n_flip, size_t n /* Tests for the equality of two sha256 structs. This function only produces a * correct result if an integer multiple of 64 many bytes have been written * into the hash functions. */ -void test_sha256_eq(const secp256k1_sha256 *sha1, const secp256k1_sha256 *sha2) { +static void test_sha256_eq(const secp256k1_sha256 *sha1, const secp256k1_sha256 *sha2) { /* Is buffer fully consumed? */ CHECK((sha1->bytes & 0x3F) == 0); @@ -31,7 +31,7 @@ void test_sha256_eq(const secp256k1_sha256 *sha1, const secp256k1_sha256 *sha2) CHECK(secp256k1_memcmp_var(sha1->s, sha2->s, sizeof(sha1->s)) == 0); } -void run_nonce_function_bip340_tests(void) { +static void run_nonce_function_bip340_tests(void) { unsigned char tag[13] = "BIP0340/nonce"; unsigned char aux_tag[11] = "BIP0340/aux"; unsigned char algo[13] = "BIP0340/nonce"; @@ -72,7 +72,7 @@ void run_nonce_function_bip340_tests(void) { args[2] = pk; args[3] = algo; args[4] = aux_rand; - for (i = 0; i < count; i++) { + for (i = 0; i < COUNT; i++) { nonce_function_bip340_bitflip(args, 0, 32, msglen, algolen); nonce_function_bip340_bitflip(args, 1, 32, msglen, algolen); nonce_function_bip340_bitflip(args, 2, 32, msglen, algolen); @@ -90,7 +90,7 @@ void run_nonce_function_bip340_tests(void) { secp256k1_testrand_bytes_test(algo, algolen); CHECK(nonce_function_bip340(nonce, msg, msglen, key, pk, algo, algolen, NULL) == 1); - for (i = 0; i < count; i++) { + for (i = 0; i < COUNT; i++) { unsigned char nonce2[32]; uint32_t offset = secp256k1_testrand_int(msglen - 1); size_t msglen_tmp = (msglen + offset) % msglen; @@ -114,7 +114,7 @@ void run_nonce_function_bip340_tests(void) { CHECK(secp256k1_memcmp_var(nonce_z, nonce, 32) == 0); } -void test_schnorrsig_api(void) { +static void test_schnorrsig_api(void) { unsigned char sk1[32]; unsigned char sk2[32]; unsigned char sk3[32]; @@ -128,82 +128,82 @@ void test_schnorrsig_api(void) { secp256k1_schnorrsig_extraparams invalid_extraparams = {{ 0 }, NULL, NULL}; /** setup **/ - secp256k1_context *sttc = secp256k1_context_clone(secp256k1_context_static); - int ecount; + int ecount = 0; - secp256k1_context_set_error_callback(ctx, counting_illegal_callback_fn, &ecount); - secp256k1_context_set_illegal_callback(ctx, counting_illegal_callback_fn, &ecount); - secp256k1_context_set_error_callback(sttc, counting_illegal_callback_fn, &ecount); - secp256k1_context_set_illegal_callback(sttc, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(CTX, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(CTX, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(STATIC_CTX, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(STATIC_CTX, counting_illegal_callback_fn, &ecount); secp256k1_testrand256(sk1); secp256k1_testrand256(sk2); secp256k1_testrand256(sk3); secp256k1_testrand256(msg); - CHECK(secp256k1_keypair_create(ctx, &keypairs[0], sk1) == 1); - CHECK(secp256k1_keypair_create(ctx, &keypairs[1], sk2) == 1); - CHECK(secp256k1_keypair_create(ctx, &keypairs[2], sk3) == 1); - CHECK(secp256k1_keypair_xonly_pub(ctx, &pk[0], NULL, &keypairs[0]) == 1); - CHECK(secp256k1_keypair_xonly_pub(ctx, &pk[1], NULL, &keypairs[1]) == 1); - CHECK(secp256k1_keypair_xonly_pub(ctx, &pk[2], NULL, &keypairs[2]) == 1); + CHECK(secp256k1_keypair_create(CTX, &keypairs[0], sk1) == 1); + CHECK(secp256k1_keypair_create(CTX, &keypairs[1], sk2) == 1); + CHECK(secp256k1_keypair_create(CTX, &keypairs[2], sk3) == 1); + CHECK(secp256k1_keypair_xonly_pub(CTX, &pk[0], NULL, &keypairs[0]) == 1); + CHECK(secp256k1_keypair_xonly_pub(CTX, &pk[1], NULL, &keypairs[1]) == 1); + CHECK(secp256k1_keypair_xonly_pub(CTX, &pk[2], NULL, &keypairs[2]) == 1); memset(&zero_pk, 0, sizeof(zero_pk)); /** main test body **/ ecount = 0; - CHECK(secp256k1_schnorrsig_sign32(ctx, sig, msg, &keypairs[0], NULL) == 1); + CHECK(secp256k1_schnorrsig_sign32(CTX, sig, msg, &keypairs[0], NULL) == 1); CHECK(ecount == 0); - CHECK(secp256k1_schnorrsig_sign32(ctx, NULL, msg, &keypairs[0], NULL) == 0); + CHECK(secp256k1_schnorrsig_sign32(CTX, NULL, msg, &keypairs[0], NULL) == 0); CHECK(ecount == 1); - CHECK(secp256k1_schnorrsig_sign32(ctx, sig, NULL, &keypairs[0], NULL) == 0); + CHECK(secp256k1_schnorrsig_sign32(CTX, sig, NULL, &keypairs[0], NULL) == 0); CHECK(ecount == 2); - CHECK(secp256k1_schnorrsig_sign32(ctx, sig, msg, NULL, NULL) == 0); + CHECK(secp256k1_schnorrsig_sign32(CTX, sig, msg, NULL, NULL) == 0); CHECK(ecount == 3); - CHECK(secp256k1_schnorrsig_sign32(ctx, sig, msg, &invalid_keypair, NULL) == 0); + CHECK(secp256k1_schnorrsig_sign32(CTX, sig, msg, &invalid_keypair, NULL) == 0); CHECK(ecount == 4); - CHECK(secp256k1_schnorrsig_sign32(sttc, sig, msg, &keypairs[0], NULL) == 0); + CHECK(secp256k1_schnorrsig_sign32(STATIC_CTX, sig, msg, &keypairs[0], NULL) == 0); CHECK(ecount == 5); ecount = 0; - CHECK(secp256k1_schnorrsig_sign_custom(ctx, sig, msg, sizeof(msg), &keypairs[0], &extraparams) == 1); + CHECK(secp256k1_schnorrsig_sign_custom(CTX, sig, msg, sizeof(msg), &keypairs[0], &extraparams) == 1); CHECK(ecount == 0); - CHECK(secp256k1_schnorrsig_sign_custom(ctx, NULL, msg, sizeof(msg), &keypairs[0], &extraparams) == 0); + CHECK(secp256k1_schnorrsig_sign_custom(CTX, NULL, msg, sizeof(msg), &keypairs[0], &extraparams) == 0); CHECK(ecount == 1); - CHECK(secp256k1_schnorrsig_sign_custom(ctx, sig, NULL, sizeof(msg), &keypairs[0], &extraparams) == 0); + CHECK(secp256k1_schnorrsig_sign_custom(CTX, sig, NULL, sizeof(msg), &keypairs[0], &extraparams) == 0); CHECK(ecount == 2); - CHECK(secp256k1_schnorrsig_sign_custom(ctx, sig, NULL, 0, &keypairs[0], &extraparams) == 1); + CHECK(secp256k1_schnorrsig_sign_custom(CTX, sig, NULL, 0, &keypairs[0], &extraparams) == 1); CHECK(ecount == 2); - CHECK(secp256k1_schnorrsig_sign_custom(ctx, sig, msg, sizeof(msg), NULL, &extraparams) == 0); + CHECK(secp256k1_schnorrsig_sign_custom(CTX, sig, msg, sizeof(msg), NULL, &extraparams) == 0); CHECK(ecount == 3); - CHECK(secp256k1_schnorrsig_sign_custom(ctx, sig, msg, sizeof(msg), &invalid_keypair, &extraparams) == 0); + CHECK(secp256k1_schnorrsig_sign_custom(CTX, sig, msg, sizeof(msg), &invalid_keypair, &extraparams) == 0); CHECK(ecount == 4); - CHECK(secp256k1_schnorrsig_sign_custom(ctx, sig, msg, sizeof(msg), &keypairs[0], NULL) == 1); + CHECK(secp256k1_schnorrsig_sign_custom(CTX, sig, msg, sizeof(msg), &keypairs[0], NULL) == 1); CHECK(ecount == 4); - CHECK(secp256k1_schnorrsig_sign_custom(ctx, sig, msg, sizeof(msg), &keypairs[0], &invalid_extraparams) == 0); + CHECK(secp256k1_schnorrsig_sign_custom(CTX, sig, msg, sizeof(msg), &keypairs[0], &invalid_extraparams) == 0); CHECK(ecount == 5); - CHECK(secp256k1_schnorrsig_sign_custom(sttc, sig, msg, sizeof(msg), &keypairs[0], &extraparams) == 0); + CHECK(secp256k1_schnorrsig_sign_custom(STATIC_CTX, sig, msg, sizeof(msg), &keypairs[0], &extraparams) == 0); CHECK(ecount == 6); ecount = 0; - CHECK(secp256k1_schnorrsig_sign32(ctx, sig, msg, &keypairs[0], NULL) == 1); - CHECK(secp256k1_schnorrsig_verify(ctx, sig, msg, sizeof(msg), &pk[0]) == 1); + CHECK(secp256k1_schnorrsig_sign32(CTX, sig, msg, &keypairs[0], NULL) == 1); + CHECK(secp256k1_schnorrsig_verify(CTX, sig, msg, sizeof(msg), &pk[0]) == 1); CHECK(ecount == 0); - CHECK(secp256k1_schnorrsig_verify(ctx, NULL, msg, sizeof(msg), &pk[0]) == 0); + CHECK(secp256k1_schnorrsig_verify(CTX, NULL, msg, sizeof(msg), &pk[0]) == 0); CHECK(ecount == 1); - CHECK(secp256k1_schnorrsig_verify(ctx, sig, NULL, sizeof(msg), &pk[0]) == 0); + CHECK(secp256k1_schnorrsig_verify(CTX, sig, NULL, sizeof(msg), &pk[0]) == 0); CHECK(ecount == 2); - CHECK(secp256k1_schnorrsig_verify(ctx, sig, NULL, 0, &pk[0]) == 0); + CHECK(secp256k1_schnorrsig_verify(CTX, sig, NULL, 0, &pk[0]) == 0); CHECK(ecount == 2); - CHECK(secp256k1_schnorrsig_verify(ctx, sig, msg, sizeof(msg), NULL) == 0); + CHECK(secp256k1_schnorrsig_verify(CTX, sig, msg, sizeof(msg), NULL) == 0); CHECK(ecount == 3); - CHECK(secp256k1_schnorrsig_verify(ctx, sig, msg, sizeof(msg), &zero_pk) == 0); + CHECK(secp256k1_schnorrsig_verify(CTX, sig, msg, sizeof(msg), &zero_pk) == 0); CHECK(ecount == 4); - secp256k1_context_destroy(sttc); + secp256k1_context_set_error_callback(STATIC_CTX, NULL, NULL); + secp256k1_context_set_illegal_callback(STATIC_CTX, NULL, NULL); } /* Checks that hash initialized by secp256k1_schnorrsig_sha256_tagged has the * expected state. */ -void test_schnorrsig_sha256_tagged(void) { +static void test_schnorrsig_sha256_tagged(void) { unsigned char tag[17] = "BIP0340/challenge"; secp256k1_sha256 sha; secp256k1_sha256 sha_optimized; @@ -215,33 +215,33 @@ void test_schnorrsig_sha256_tagged(void) { /* Helper function for schnorrsig_bip_vectors * Signs the message and checks that it's the same as expected_sig. */ -void test_schnorrsig_bip_vectors_check_signing(const unsigned char *sk, const unsigned char *pk_serialized, const unsigned char *aux_rand, const unsigned char *msg32, const unsigned char *expected_sig) { +static void test_schnorrsig_bip_vectors_check_signing(const unsigned char *sk, const unsigned char *pk_serialized, const unsigned char *aux_rand, const unsigned char *msg32, const unsigned char *expected_sig) { unsigned char sig[64]; secp256k1_keypair keypair; secp256k1_xonly_pubkey pk, pk_expected; - CHECK(secp256k1_keypair_create(ctx, &keypair, sk)); - CHECK(secp256k1_schnorrsig_sign32(ctx, sig, msg32, &keypair, aux_rand)); + CHECK(secp256k1_keypair_create(CTX, &keypair, sk)); + CHECK(secp256k1_schnorrsig_sign32(CTX, sig, msg32, &keypair, aux_rand)); CHECK(secp256k1_memcmp_var(sig, expected_sig, 64) == 0); - CHECK(secp256k1_xonly_pubkey_parse(ctx, &pk_expected, pk_serialized)); - CHECK(secp256k1_keypair_xonly_pub(ctx, &pk, NULL, &keypair)); + CHECK(secp256k1_xonly_pubkey_parse(CTX, &pk_expected, pk_serialized)); + CHECK(secp256k1_keypair_xonly_pub(CTX, &pk, NULL, &keypair)); CHECK(secp256k1_memcmp_var(&pk, &pk_expected, sizeof(pk)) == 0); - CHECK(secp256k1_schnorrsig_verify(ctx, sig, msg32, 32, &pk)); + CHECK(secp256k1_schnorrsig_verify(CTX, sig, msg32, 32, &pk)); } /* Helper function for schnorrsig_bip_vectors * Checks that both verify and verify_batch (TODO) return the same value as expected. */ -void test_schnorrsig_bip_vectors_check_verify(const unsigned char *pk_serialized, const unsigned char *msg32, const unsigned char *sig, int expected) { +static void test_schnorrsig_bip_vectors_check_verify(const unsigned char *pk_serialized, const unsigned char *msg32, const unsigned char *sig, int expected) { secp256k1_xonly_pubkey pk; - CHECK(secp256k1_xonly_pubkey_parse(ctx, &pk, pk_serialized)); - CHECK(expected == secp256k1_schnorrsig_verify(ctx, sig, msg32, 32, &pk)); + CHECK(secp256k1_xonly_pubkey_parse(CTX, &pk, pk_serialized)); + CHECK(expected == secp256k1_schnorrsig_verify(CTX, sig, msg32, 32, &pk)); } /* Test vectors according to BIP-340 ("Schnorr Signatures for secp256k1"). See * https://github.com/bitcoin/bips/blob/master/bip-0340/test-vectors.csv. */ -void test_schnorrsig_bip_vectors(void) { +static void test_schnorrsig_bip_vectors(void) { { /* Test vector 0 */ const unsigned char sk[32] = { @@ -434,7 +434,7 @@ void test_schnorrsig_bip_vectors(void) { }; secp256k1_xonly_pubkey pk_parsed; /* No need to check the signature of the test vector as parsing the pubkey already fails */ - CHECK(!secp256k1_xonly_pubkey_parse(ctx, &pk_parsed, pk)); + CHECK(!secp256k1_xonly_pubkey_parse(CTX, &pk_parsed, pk)); } { /* Test vector 6 */ @@ -654,7 +654,7 @@ void test_schnorrsig_bip_vectors(void) { }; secp256k1_xonly_pubkey pk_parsed; /* No need to check the signature of the test vector as parsing the pubkey already fails */ - CHECK(!secp256k1_xonly_pubkey_parse(ctx, &pk_parsed, pk)); + CHECK(!secp256k1_xonly_pubkey_parse(CTX, &pk_parsed, pk)); } } @@ -699,7 +699,7 @@ static int nonce_function_overflowing(unsigned char *nonce32, const unsigned cha return 1; } -void test_schnorrsig_sign(void) { +static void test_schnorrsig_sign(void) { unsigned char sk[32]; secp256k1_xonly_pubkey pk; secp256k1_keypair keypair; @@ -712,36 +712,36 @@ void test_schnorrsig_sign(void) { secp256k1_testrand256(sk); secp256k1_testrand256(aux_rand); - CHECK(secp256k1_keypair_create(ctx, &keypair, sk)); - CHECK(secp256k1_keypair_xonly_pub(ctx, &pk, NULL, &keypair)); - CHECK(secp256k1_schnorrsig_sign32(ctx, sig, msg, &keypair, NULL) == 1); - CHECK(secp256k1_schnorrsig_verify(ctx, sig, msg, sizeof(msg), &pk)); + CHECK(secp256k1_keypair_create(CTX, &keypair, sk)); + CHECK(secp256k1_keypair_xonly_pub(CTX, &pk, NULL, &keypair)); + CHECK(secp256k1_schnorrsig_sign32(CTX, sig, msg, &keypair, NULL) == 1); + CHECK(secp256k1_schnorrsig_verify(CTX, sig, msg, sizeof(msg), &pk)); /* Check that deprecated alias gives the same result */ - CHECK(secp256k1_schnorrsig_sign(ctx, sig2, msg, &keypair, NULL) == 1); + CHECK(secp256k1_schnorrsig_sign(CTX, sig2, msg, &keypair, NULL) == 1); CHECK(secp256k1_memcmp_var(sig, sig2, sizeof(sig)) == 0); /* Test different nonce functions */ - CHECK(secp256k1_schnorrsig_sign_custom(ctx, sig, msg, sizeof(msg), &keypair, &extraparams) == 1); - CHECK(secp256k1_schnorrsig_verify(ctx, sig, msg, sizeof(msg), &pk)); + CHECK(secp256k1_schnorrsig_sign_custom(CTX, sig, msg, sizeof(msg), &keypair, &extraparams) == 1); + CHECK(secp256k1_schnorrsig_verify(CTX, sig, msg, sizeof(msg), &pk)); memset(sig, 1, sizeof(sig)); extraparams.noncefp = nonce_function_failing; - CHECK(secp256k1_schnorrsig_sign_custom(ctx, sig, msg, sizeof(msg), &keypair, &extraparams) == 0); + CHECK(secp256k1_schnorrsig_sign_custom(CTX, sig, msg, sizeof(msg), &keypair, &extraparams) == 0); CHECK(secp256k1_memcmp_var(sig, zeros64, sizeof(sig)) == 0); memset(&sig, 1, sizeof(sig)); extraparams.noncefp = nonce_function_0; - CHECK(secp256k1_schnorrsig_sign_custom(ctx, sig, msg, sizeof(msg), &keypair, &extraparams) == 0); + CHECK(secp256k1_schnorrsig_sign_custom(CTX, sig, msg, sizeof(msg), &keypair, &extraparams) == 0); CHECK(secp256k1_memcmp_var(sig, zeros64, sizeof(sig)) == 0); memset(&sig, 1, sizeof(sig)); extraparams.noncefp = nonce_function_overflowing; - CHECK(secp256k1_schnorrsig_sign_custom(ctx, sig, msg, sizeof(msg), &keypair, &extraparams) == 1); - CHECK(secp256k1_schnorrsig_verify(ctx, sig, msg, sizeof(msg), &pk)); + CHECK(secp256k1_schnorrsig_sign_custom(CTX, sig, msg, sizeof(msg), &keypair, &extraparams) == 1); + CHECK(secp256k1_schnorrsig_verify(CTX, sig, msg, sizeof(msg), &pk)); /* When using the default nonce function, schnorrsig_sign_custom produces * the same result as schnorrsig_sign with aux_rand = extraparams.ndata */ extraparams.noncefp = NULL; extraparams.ndata = aux_rand; - CHECK(secp256k1_schnorrsig_sign_custom(ctx, sig, msg, sizeof(msg), &keypair, &extraparams) == 1); - CHECK(secp256k1_schnorrsig_sign32(ctx, sig2, msg, &keypair, extraparams.ndata) == 1); + CHECK(secp256k1_schnorrsig_sign_custom(CTX, sig, msg, sizeof(msg), &keypair, &extraparams) == 1); + CHECK(secp256k1_schnorrsig_sign32(CTX, sig2, msg, &keypair, extraparams.ndata) == 1); CHECK(secp256k1_memcmp_var(sig, sig2, sizeof(sig)) == 0); } @@ -749,7 +749,7 @@ void test_schnorrsig_sign(void) { /* Creates N_SIGS valid signatures and verifies them with verify and * verify_batch (TODO). Then flips some bits and checks that verification now * fails. */ -void test_schnorrsig_sign_verify(void) { +static void test_schnorrsig_sign_verify(void) { unsigned char sk[32]; unsigned char msg[N_SIGS][32]; unsigned char sig[N_SIGS][64]; @@ -759,13 +759,13 @@ void test_schnorrsig_sign_verify(void) { secp256k1_scalar s; secp256k1_testrand256(sk); - CHECK(secp256k1_keypair_create(ctx, &keypair, sk)); - CHECK(secp256k1_keypair_xonly_pub(ctx, &pk, NULL, &keypair)); + CHECK(secp256k1_keypair_create(CTX, &keypair, sk)); + CHECK(secp256k1_keypair_xonly_pub(CTX, &pk, NULL, &keypair)); for (i = 0; i < N_SIGS; i++) { secp256k1_testrand256(msg[i]); - CHECK(secp256k1_schnorrsig_sign32(ctx, sig[i], msg[i], &keypair, NULL)); - CHECK(secp256k1_schnorrsig_verify(ctx, sig[i], msg[i], sizeof(msg[i]), &pk)); + CHECK(secp256k1_schnorrsig_sign32(CTX, sig[i], msg[i], &keypair, NULL)); + CHECK(secp256k1_schnorrsig_verify(CTX, sig[i], msg[i], sizeof(msg[i]), &pk)); } { @@ -775,40 +775,40 @@ void test_schnorrsig_sign_verify(void) { size_t byte_idx = secp256k1_testrand_bits(5); unsigned char xorbyte = secp256k1_testrand_int(254)+1; sig[sig_idx][byte_idx] ^= xorbyte; - CHECK(!secp256k1_schnorrsig_verify(ctx, sig[sig_idx], msg[sig_idx], sizeof(msg[sig_idx]), &pk)); + CHECK(!secp256k1_schnorrsig_verify(CTX, sig[sig_idx], msg[sig_idx], sizeof(msg[sig_idx]), &pk)); sig[sig_idx][byte_idx] ^= xorbyte; byte_idx = secp256k1_testrand_bits(5); sig[sig_idx][32+byte_idx] ^= xorbyte; - CHECK(!secp256k1_schnorrsig_verify(ctx, sig[sig_idx], msg[sig_idx], sizeof(msg[sig_idx]), &pk)); + CHECK(!secp256k1_schnorrsig_verify(CTX, sig[sig_idx], msg[sig_idx], sizeof(msg[sig_idx]), &pk)); sig[sig_idx][32+byte_idx] ^= xorbyte; byte_idx = secp256k1_testrand_bits(5); msg[sig_idx][byte_idx] ^= xorbyte; - CHECK(!secp256k1_schnorrsig_verify(ctx, sig[sig_idx], msg[sig_idx], sizeof(msg[sig_idx]), &pk)); + CHECK(!secp256k1_schnorrsig_verify(CTX, sig[sig_idx], msg[sig_idx], sizeof(msg[sig_idx]), &pk)); msg[sig_idx][byte_idx] ^= xorbyte; /* Check that above bitflips have been reversed correctly */ - CHECK(secp256k1_schnorrsig_verify(ctx, sig[sig_idx], msg[sig_idx], sizeof(msg[sig_idx]), &pk)); + CHECK(secp256k1_schnorrsig_verify(CTX, sig[sig_idx], msg[sig_idx], sizeof(msg[sig_idx]), &pk)); } /* Test overflowing s */ - CHECK(secp256k1_schnorrsig_sign32(ctx, sig[0], msg[0], &keypair, NULL)); - CHECK(secp256k1_schnorrsig_verify(ctx, sig[0], msg[0], sizeof(msg[0]), &pk)); + CHECK(secp256k1_schnorrsig_sign32(CTX, sig[0], msg[0], &keypair, NULL)); + CHECK(secp256k1_schnorrsig_verify(CTX, sig[0], msg[0], sizeof(msg[0]), &pk)); memset(&sig[0][32], 0xFF, 32); - CHECK(!secp256k1_schnorrsig_verify(ctx, sig[0], msg[0], sizeof(msg[0]), &pk)); + CHECK(!secp256k1_schnorrsig_verify(CTX, sig[0], msg[0], sizeof(msg[0]), &pk)); /* Test negative s */ - CHECK(secp256k1_schnorrsig_sign32(ctx, sig[0], msg[0], &keypair, NULL)); - CHECK(secp256k1_schnorrsig_verify(ctx, sig[0], msg[0], sizeof(msg[0]), &pk)); + CHECK(secp256k1_schnorrsig_sign32(CTX, sig[0], msg[0], &keypair, NULL)); + CHECK(secp256k1_schnorrsig_verify(CTX, sig[0], msg[0], sizeof(msg[0]), &pk)); secp256k1_scalar_set_b32(&s, &sig[0][32], NULL); secp256k1_scalar_negate(&s, &s); secp256k1_scalar_get_b32(&sig[0][32], &s); - CHECK(!secp256k1_schnorrsig_verify(ctx, sig[0], msg[0], sizeof(msg[0]), &pk)); + CHECK(!secp256k1_schnorrsig_verify(CTX, sig[0], msg[0], sizeof(msg[0]), &pk)); /* The empty message can be signed & verified */ - CHECK(secp256k1_schnorrsig_sign_custom(ctx, sig[0], NULL, 0, &keypair, NULL) == 1); - CHECK(secp256k1_schnorrsig_verify(ctx, sig[0], NULL, 0, &pk) == 1); + CHECK(secp256k1_schnorrsig_sign_custom(CTX, sig[0], NULL, 0, &keypair, NULL) == 1); + CHECK(secp256k1_schnorrsig_verify(CTX, sig[0], NULL, 0, &pk) == 1); { /* Test varying message lengths */ @@ -817,16 +817,16 @@ void test_schnorrsig_sign_verify(void) { for (i = 0; i < sizeof(msg_large); i += 32) { secp256k1_testrand256(&msg_large[i]); } - CHECK(secp256k1_schnorrsig_sign_custom(ctx, sig[0], msg_large, msglen, &keypair, NULL) == 1); - CHECK(secp256k1_schnorrsig_verify(ctx, sig[0], msg_large, msglen, &pk) == 1); + CHECK(secp256k1_schnorrsig_sign_custom(CTX, sig[0], msg_large, msglen, &keypair, NULL) == 1); + CHECK(secp256k1_schnorrsig_verify(CTX, sig[0], msg_large, msglen, &pk) == 1); /* Verification for a random wrong message length fails */ msglen = (msglen + (sizeof(msg_large) - 1)) % sizeof(msg_large); - CHECK(secp256k1_schnorrsig_verify(ctx, sig[0], msg_large, msglen, &pk) == 0); + CHECK(secp256k1_schnorrsig_verify(CTX, sig[0], msg_large, msglen, &pk) == 0); } } #undef N_SIGS -void test_schnorrsig_taproot(void) { +static void test_schnorrsig_taproot(void) { unsigned char sk[32]; secp256k1_keypair keypair; secp256k1_xonly_pubkey internal_pk; @@ -840,36 +840,36 @@ void test_schnorrsig_taproot(void) { /* Create output key */ secp256k1_testrand256(sk); - CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1); - CHECK(secp256k1_keypair_xonly_pub(ctx, &internal_pk, NULL, &keypair) == 1); + CHECK(secp256k1_keypair_create(CTX, &keypair, sk) == 1); + CHECK(secp256k1_keypair_xonly_pub(CTX, &internal_pk, NULL, &keypair) == 1); /* In actual taproot the tweak would be hash of internal_pk */ - CHECK(secp256k1_xonly_pubkey_serialize(ctx, tweak, &internal_pk) == 1); - CHECK(secp256k1_keypair_xonly_tweak_add(ctx, &keypair, tweak) == 1); - CHECK(secp256k1_keypair_xonly_pub(ctx, &output_pk, &pk_parity, &keypair) == 1); - CHECK(secp256k1_xonly_pubkey_serialize(ctx, output_pk_bytes, &output_pk) == 1); + CHECK(secp256k1_xonly_pubkey_serialize(CTX, tweak, &internal_pk) == 1); + CHECK(secp256k1_keypair_xonly_tweak_add(CTX, &keypair, tweak) == 1); + CHECK(secp256k1_keypair_xonly_pub(CTX, &output_pk, &pk_parity, &keypair) == 1); + CHECK(secp256k1_xonly_pubkey_serialize(CTX, output_pk_bytes, &output_pk) == 1); /* Key spend */ secp256k1_testrand256(msg); - CHECK(secp256k1_schnorrsig_sign32(ctx, sig, msg, &keypair, NULL) == 1); + CHECK(secp256k1_schnorrsig_sign32(CTX, sig, msg, &keypair, NULL) == 1); /* Verify key spend */ - CHECK(secp256k1_xonly_pubkey_parse(ctx, &output_pk, output_pk_bytes) == 1); - CHECK(secp256k1_schnorrsig_verify(ctx, sig, msg, sizeof(msg), &output_pk) == 1); + CHECK(secp256k1_xonly_pubkey_parse(CTX, &output_pk, output_pk_bytes) == 1); + CHECK(secp256k1_schnorrsig_verify(CTX, sig, msg, sizeof(msg), &output_pk) == 1); /* Script spend */ - CHECK(secp256k1_xonly_pubkey_serialize(ctx, internal_pk_bytes, &internal_pk) == 1); + CHECK(secp256k1_xonly_pubkey_serialize(CTX, internal_pk_bytes, &internal_pk) == 1); /* Verify script spend */ - CHECK(secp256k1_xonly_pubkey_parse(ctx, &internal_pk, internal_pk_bytes) == 1); - CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, output_pk_bytes, pk_parity, &internal_pk, tweak) == 1); + CHECK(secp256k1_xonly_pubkey_parse(CTX, &internal_pk, internal_pk_bytes) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_add_check(CTX, output_pk_bytes, pk_parity, &internal_pk, tweak) == 1); } -void run_schnorrsig_tests(void) { +static void run_schnorrsig_tests(void) { int i; run_nonce_function_bip340_tests(); test_schnorrsig_api(); test_schnorrsig_sha256_tagged(); test_schnorrsig_bip_vectors(); - for (i = 0; i < count; i++) { + for (i = 0; i < COUNT; i++) { test_schnorrsig_sign(); test_schnorrsig_sign_verify(); } diff --git a/src/secp256k1/src/precompute_ecmult.c b/src/secp256k1/src/precompute_ecmult.c index 2aa37b8fe3..10aba5b97d 100644 --- a/src/secp256k1/src/precompute_ecmult.c +++ b/src/secp256k1/src/precompute_ecmult.c @@ -7,12 +7,6 @@ #include <inttypes.h> #include <stdio.h> -/* Autotools creates libsecp256k1-config.h, of which ECMULT_WINDOW_SIZE is needed. - ifndef guard so downstream users can define their own if they do not use autotools. */ -#if !defined(ECMULT_WINDOW_SIZE) -#include "libsecp256k1-config.h" -#endif - #include "../include/secp256k1.h" #include "assumptions.h" @@ -74,9 +68,6 @@ int main(void) { fprintf(fp, "/* This file contains an array secp256k1_pre_g with odd multiples of the base point G and\n"); fprintf(fp, " * an array secp256k1_pre_g_128 with odd multiples of 2^128*G for accelerating the computation of a*P + b*G.\n"); fprintf(fp, " */\n"); - fprintf(fp, "#if defined HAVE_CONFIG_H\n"); - fprintf(fp, "# include \"libsecp256k1-config.h\"\n"); - fprintf(fp, "#endif\n"); fprintf(fp, "#include \"../include/secp256k1.h\"\n"); fprintf(fp, "#include \"group.h\"\n"); fprintf(fp, "#include \"ecmult.h\"\n"); diff --git a/src/secp256k1/src/precompute_ecmult_gen.c b/src/secp256k1/src/precompute_ecmult_gen.c index a4ec8e0dc6..bfe212fdd2 100644 --- a/src/secp256k1/src/precompute_ecmult_gen.c +++ b/src/secp256k1/src/precompute_ecmult_gen.c @@ -33,9 +33,6 @@ int main(int argc, char **argv) { fprintf(fp, "/* This file was automatically generated by precompute_ecmult_gen. */\n"); fprintf(fp, "/* See ecmult_gen_impl.h for details about the contents of this file. */\n"); - fprintf(fp, "#if defined HAVE_CONFIG_H\n"); - fprintf(fp, "# include \"libsecp256k1-config.h\"\n"); - fprintf(fp, "#endif\n"); fprintf(fp, "#include \"../include/secp256k1.h\"\n"); fprintf(fp, "#include \"group.h\"\n"); fprintf(fp, "#include \"ecmult_gen.h\"\n"); diff --git a/src/secp256k1/src/precomputed_ecmult.c b/src/secp256k1/src/precomputed_ecmult.c index 3e67f37b74..fbc634ef1b 100644 --- a/src/secp256k1/src/precomputed_ecmult.c +++ b/src/secp256k1/src/precomputed_ecmult.c @@ -2,9 +2,6 @@ /* This file contains an array secp256k1_pre_g with odd multiples of the base point G and * an array secp256k1_pre_g_128 with odd multiples of 2^128*G for accelerating the computation of a*P + b*G. */ -#if defined HAVE_CONFIG_H -# include "libsecp256k1-config.h" -#endif #include "../include/secp256k1.h" #include "group.h" #include "ecmult.h" diff --git a/src/secp256k1/src/precomputed_ecmult.h b/src/secp256k1/src/precomputed_ecmult.h index 949b62c874..a4aa83e4ca 100644 --- a/src/secp256k1/src/precomputed_ecmult.h +++ b/src/secp256k1/src/precomputed_ecmult.h @@ -13,7 +13,9 @@ extern "C" { #include "group.h" #if defined(EXHAUSTIVE_TEST_ORDER) -#if EXHAUSTIVE_TEST_ORDER == 13 +# if EXHAUSTIVE_TEST_ORDER == 7 +# define WINDOW_G 3 +# elif EXHAUSTIVE_TEST_ORDER == 13 # define WINDOW_G 4 # elif EXHAUSTIVE_TEST_ORDER == 199 # define WINDOW_G 8 diff --git a/src/secp256k1/src/precomputed_ecmult_gen.c b/src/secp256k1/src/precomputed_ecmult_gen.c index d67291fcf5..e9d62a1c1b 100644 --- a/src/secp256k1/src/precomputed_ecmult_gen.c +++ b/src/secp256k1/src/precomputed_ecmult_gen.c @@ -1,8 +1,5 @@ /* This file was automatically generated by precompute_ecmult_gen. */ /* See ecmult_gen_impl.h for details about the contents of this file. */ -#if defined HAVE_CONFIG_H -# include "libsecp256k1-config.h" -#endif #include "../include/secp256k1.h" #include "group.h" #include "ecmult_gen.h" diff --git a/src/secp256k1/src/scalar.h b/src/secp256k1/src/scalar.h index aaaa3d8827..63c0d646a3 100644 --- a/src/secp256k1/src/scalar.h +++ b/src/secp256k1/src/scalar.h @@ -9,10 +9,6 @@ #include "util.h" -#if defined HAVE_CONFIG_H -#include "libsecp256k1-config.h" -#endif - #if defined(EXHAUSTIVE_TEST_ORDER) #include "scalar_low.h" #elif defined(SECP256K1_WIDEMUL_INT128) @@ -92,9 +88,10 @@ static int secp256k1_scalar_eq(const secp256k1_scalar *a, const secp256k1_scalar /** Find r1 and r2 such that r1+r2*2^128 = k. */ static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *k); -/** Find r1 and r2 such that r1+r2*lambda = k, - * where r1 and r2 or their negations are maximum 128 bits long (see secp256k1_ge_mul_lambda). */ -static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *k); +/** Find r1 and r2 such that r1+r2*lambda = k, where r1 and r2 or their + * negations are maximum 128 bits long (see secp256k1_ge_mul_lambda). It is + * required that r1, r2, and k all point to different objects. */ +static void secp256k1_scalar_split_lambda(secp256k1_scalar * SECP256K1_RESTRICT r1, secp256k1_scalar * SECP256K1_RESTRICT r2, const secp256k1_scalar * SECP256K1_RESTRICT k); /** Multiply a and b (without taking the modulus!), divide by 2**shift, and round to the nearest integer. Shift must be at least 256. */ static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b, unsigned int shift); diff --git a/src/secp256k1/src/scalar_4x64_impl.h b/src/secp256k1/src/scalar_4x64_impl.h index 4588219d3a..1b83575b3e 100644 --- a/src/secp256k1/src/scalar_4x64_impl.h +++ b/src/secp256k1/src/scalar_4x64_impl.h @@ -7,6 +7,7 @@ #ifndef SECP256K1_SCALAR_REPR_IMPL_H #define SECP256K1_SCALAR_REPR_IMPL_H +#include "checkmem.h" #include "int128.h" #include "modinv64_impl.h" @@ -810,7 +811,7 @@ SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const secp256k1_scalar *a, int flag) { uint64_t mask0, mask1; - VG_CHECK_VERIFY(r->d, sizeof(r->d)); + SECP256K1_CHECKMEM_CHECK_VERIFY(r->d, sizeof(r->d)); mask0 = flag + ~((uint64_t)0); mask1 = ~mask0; r->d[0] = (r->d[0] & mask0) | (a->d[0] & mask1); diff --git a/src/secp256k1/src/scalar_8x32_impl.h b/src/secp256k1/src/scalar_8x32_impl.h index 62c7ae7156..c433adce75 100644 --- a/src/secp256k1/src/scalar_8x32_impl.h +++ b/src/secp256k1/src/scalar_8x32_impl.h @@ -7,6 +7,7 @@ #ifndef SECP256K1_SCALAR_REPR_IMPL_H #define SECP256K1_SCALAR_REPR_IMPL_H +#include "checkmem.h" #include "modinv32_impl.h" /* Limbs of the secp256k1 order. */ @@ -631,7 +632,7 @@ SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const secp256k1_scalar *a, int flag) { uint32_t mask0, mask1; - VG_CHECK_VERIFY(r->d, sizeof(r->d)); + SECP256K1_CHECKMEM_CHECK_VERIFY(r->d, sizeof(r->d)); mask0 = flag + ~((uint32_t)0); mask1 = ~mask0; r->d[0] = (r->d[0] & mask0) | (a->d[0] & mask1); diff --git a/src/secp256k1/src/scalar_impl.h b/src/secp256k1/src/scalar_impl.h index 1b690e3944..bed7f95fcb 100644 --- a/src/secp256k1/src/scalar_impl.h +++ b/src/secp256k1/src/scalar_impl.h @@ -14,10 +14,6 @@ #include "scalar.h" #include "util.h" -#if defined HAVE_CONFIG_H -#include "libsecp256k1-config.h" -#endif - #if defined(EXHAUSTIVE_TEST_ORDER) #include "scalar_low_impl.h" #elif defined(SECP256K1_WIDEMUL_INT128) @@ -37,15 +33,18 @@ static int secp256k1_scalar_set_b32_seckey(secp256k1_scalar *r, const unsigned c return (!overflow) & (!secp256k1_scalar_is_zero(r)); } -/* These parameters are generated using sage/gen_exhaustive_groups.sage. */ #if defined(EXHAUSTIVE_TEST_ORDER) -# if EXHAUSTIVE_TEST_ORDER == 13 +/* Begin of section generated by sage/gen_exhaustive_groups.sage. */ +# if EXHAUSTIVE_TEST_ORDER == 7 +# define EXHAUSTIVE_TEST_LAMBDA 2 +# elif EXHAUSTIVE_TEST_ORDER == 13 # define EXHAUSTIVE_TEST_LAMBDA 9 # elif EXHAUSTIVE_TEST_ORDER == 199 # define EXHAUSTIVE_TEST_LAMBDA 92 # else # error No known lambda for the specified exhaustive test group order. # endif +/* End of section generated by sage/gen_exhaustive_groups.sage. */ /** * Find r1 and r2 given k, such that r1 + r2 * lambda == k mod n; unlike in the @@ -53,7 +52,10 @@ static int secp256k1_scalar_set_b32_seckey(secp256k1_scalar *r, const unsigned c * nontrivial to get full test coverage for the exhaustive tests. We therefore * (arbitrarily) set r2 = k + 5 (mod n) and r1 = k - r2 * lambda (mod n). */ -static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *k) { +static void secp256k1_scalar_split_lambda(secp256k1_scalar * SECP256K1_RESTRICT r1, secp256k1_scalar * SECP256K1_RESTRICT r2, const secp256k1_scalar * SECP256K1_RESTRICT k) { + VERIFY_CHECK(r1 != k); + VERIFY_CHECK(r2 != k); + VERIFY_CHECK(r1 != r2); *r2 = (*k + 5) % EXHAUSTIVE_TEST_ORDER; *r1 = (*k + (EXHAUSTIVE_TEST_ORDER - *r2) * EXHAUSTIVE_TEST_LAMBDA) % EXHAUSTIVE_TEST_ORDER; } @@ -120,7 +122,7 @@ static void secp256k1_scalar_split_lambda_verify(const secp256k1_scalar *r1, con * * See proof below. */ -static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *k) { +static void secp256k1_scalar_split_lambda(secp256k1_scalar * SECP256K1_RESTRICT r1, secp256k1_scalar * SECP256K1_RESTRICT r2, const secp256k1_scalar * SECP256K1_RESTRICT k) { secp256k1_scalar c1, c2; static const secp256k1_scalar minus_b1 = SECP256K1_SCALAR_CONST( 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x00000000UL, @@ -140,6 +142,7 @@ static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar ); VERIFY_CHECK(r1 != k); VERIFY_CHECK(r2 != k); + VERIFY_CHECK(r1 != r2); /* these _var calls are constant time since the shift amount is constant */ secp256k1_scalar_mul_shift_var(&c1, k, &g1, 384); secp256k1_scalar_mul_shift_var(&c2, k, &g2, 384); diff --git a/src/secp256k1/src/scalar_low_impl.h b/src/secp256k1/src/scalar_low_impl.h index 7176f0b2ca..e780083339 100644 --- a/src/secp256k1/src/scalar_low_impl.h +++ b/src/secp256k1/src/scalar_low_impl.h @@ -7,6 +7,7 @@ #ifndef SECP256K1_SCALAR_REPR_IMPL_H #define SECP256K1_SCALAR_REPR_IMPL_H +#include "checkmem.h" #include "scalar.h" #include <string.h> @@ -115,7 +116,7 @@ SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const secp256k1_scalar *a, int flag) { uint32_t mask0, mask1; - VG_CHECK_VERIFY(r, sizeof(*r)); + SECP256K1_CHECKMEM_CHECK_VERIFY(r, sizeof(*r)); mask0 = flag + ~((uint32_t)0); mask1 = ~mask0; *r = (*r & mask0) | (*a & mask1); diff --git a/src/secp256k1/src/secp256k1.c b/src/secp256k1/src/secp256k1.c index 5ed3824161..7af333ca90 100644 --- a/src/secp256k1/src/secp256k1.c +++ b/src/secp256k1/src/secp256k1.c @@ -21,6 +21,7 @@ #include "../include/secp256k1_preallocated.h" #include "assumptions.h" +#include "checkmem.h" #include "util.h" #include "field_impl.h" @@ -40,10 +41,6 @@ # error "secp256k1.h processed without SECP256K1_BUILD defined while building secp256k1.c" #endif -#if defined(VALGRIND) -# include <valgrind/memcheck.h> -#endif - #define ARG_CHECK(cond) do { \ if (EXPECT(!(cond), 0)) { \ secp256k1_callback_call(&ctx->illegal_callback, #cond); \ @@ -51,9 +48,10 @@ } \ } while(0) -#define ARG_CHECK_NO_RETURN(cond) do { \ +#define ARG_CHECK_VOID(cond) do { \ if (EXPECT(!(cond), 0)) { \ secp256k1_callback_call(&ctx->illegal_callback, #cond); \ + return; \ } \ } while(0) @@ -75,6 +73,15 @@ static const secp256k1_context secp256k1_context_static_ = { const secp256k1_context *secp256k1_context_static = &secp256k1_context_static_; const secp256k1_context *secp256k1_context_no_precomp = &secp256k1_context_static_; +/* Helper function that determines if a context is proper, i.e., is not the static context or a copy thereof. + * + * This is intended for "context" functions such as secp256k1_context_clone. Function which need specific + * features of a context should still check for these features directly. For example, a function that needs + * ecmult_gen should directly check for the existence of the ecmult_gen context. */ +static int secp256k1_context_is_proper(const secp256k1_context* ctx) { + return secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx); +} + void secp256k1_selftest(void) { if (!secp256k1_selftest_passes()) { secp256k1_callback_call(&default_error_callback, "self test failed"); @@ -92,13 +99,19 @@ size_t secp256k1_context_preallocated_size(unsigned int flags) { return 0; } + if (EXPECT(!SECP256K1_CHECKMEM_RUNNING() && (flags & SECP256K1_FLAGS_BIT_CONTEXT_DECLASSIFY), 0)) { + secp256k1_callback_call(&default_illegal_callback, + "Declassify flag requires running with memory checking"); + return 0; + } + return ret; } size_t secp256k1_context_preallocated_clone_size(const secp256k1_context* ctx) { - size_t ret = sizeof(secp256k1_context); VERIFY_CHECK(ctx != NULL); - return ret; + ARG_CHECK(secp256k1_context_is_proper(ctx)); + return sizeof(secp256k1_context); } secp256k1_context* secp256k1_context_preallocated_create(void* prealloc, unsigned int flags) { @@ -139,6 +152,7 @@ secp256k1_context* secp256k1_context_preallocated_clone(const secp256k1_context* secp256k1_context* ret; VERIFY_CHECK(ctx != NULL); ARG_CHECK(prealloc != NULL); + ARG_CHECK(secp256k1_context_is_proper(ctx)); ret = (secp256k1_context*)prealloc; *ret = *ctx; @@ -150,6 +164,8 @@ secp256k1_context* secp256k1_context_clone(const secp256k1_context* ctx) { size_t prealloc_size; VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_context_is_proper(ctx)); + prealloc_size = secp256k1_context_preallocated_clone_size(ctx); ret = (secp256k1_context*)checked_malloc(&ctx->error_callback, prealloc_size); ret = secp256k1_context_preallocated_clone(ctx, ret); @@ -157,21 +173,33 @@ secp256k1_context* secp256k1_context_clone(const secp256k1_context* ctx) { } void secp256k1_context_preallocated_destroy(secp256k1_context* ctx) { - ARG_CHECK_NO_RETURN(ctx != secp256k1_context_static); - if (ctx != NULL) { - secp256k1_ecmult_gen_context_clear(&ctx->ecmult_gen_ctx); + ARG_CHECK_VOID(ctx == NULL || secp256k1_context_is_proper(ctx)); + + /* Defined as noop */ + if (ctx == NULL) { + return; } + + secp256k1_ecmult_gen_context_clear(&ctx->ecmult_gen_ctx); } void secp256k1_context_destroy(secp256k1_context* ctx) { - if (ctx != NULL) { - secp256k1_context_preallocated_destroy(ctx); - free(ctx); + ARG_CHECK_VOID(ctx == NULL || secp256k1_context_is_proper(ctx)); + + /* Defined as noop */ + if (ctx == NULL) { + return; } + + secp256k1_context_preallocated_destroy(ctx); + free(ctx); } void secp256k1_context_set_illegal_callback(secp256k1_context* ctx, void (*fun)(const char* message, void* data), const void* data) { - ARG_CHECK_NO_RETURN(ctx != secp256k1_context_static); + /* We compare pointers instead of checking secp256k1_context_is_proper() here + because setting callbacks is allowed on *copies* of the static context: + it's harmless and makes testing easier. */ + ARG_CHECK_VOID(ctx != secp256k1_context_static); if (fun == NULL) { fun = secp256k1_default_illegal_callback_fn; } @@ -180,7 +208,10 @@ void secp256k1_context_set_illegal_callback(secp256k1_context* ctx, void (*fun)( } void secp256k1_context_set_error_callback(secp256k1_context* ctx, void (*fun)(const char* message, void* data), const void* data) { - ARG_CHECK_NO_RETURN(ctx != secp256k1_context_static); + /* We compare pointers instead of checking secp256k1_context_is_proper() here + because setting callbacks is allowed on *copies* of the static context: + it's harmless and makes testing easier. */ + ARG_CHECK_VOID(ctx != secp256k1_context_static); if (fun == NULL) { fun = secp256k1_default_error_callback_fn; } @@ -199,17 +230,10 @@ void secp256k1_scratch_space_destroy(const secp256k1_context *ctx, secp256k1_scr } /* Mark memory as no-longer-secret for the purpose of analysing constant-time behaviour - * of the software. This is setup for use with valgrind but could be substituted with - * the appropriate instrumentation for other analysis tools. + * of the software. */ static SECP256K1_INLINE void secp256k1_declassify(const secp256k1_context* ctx, const void *p, size_t len) { -#if defined(VALGRIND) - if (EXPECT(ctx->declassify,0)) VALGRIND_MAKE_MEM_DEFINED(p, len); -#else - (void)ctx; - (void)p; - (void)len; -#endif + if (EXPECT(ctx->declassify, 0)) SECP256K1_CHECKMEM_DEFINE(p, len); } static int secp256k1_pubkey_load(const secp256k1_context* ctx, secp256k1_ge* ge, const secp256k1_pubkey* pubkey) { @@ -725,6 +749,8 @@ int secp256k1_ec_pubkey_tweak_mul(const secp256k1_context* ctx, secp256k1_pubkey int secp256k1_context_randomize(secp256k1_context* ctx, const unsigned char *seed32) { VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_context_is_proper(ctx)); + if (secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)) { secp256k1_ecmult_gen_blind(&ctx->ecmult_gen_ctx, seed32); } diff --git a/src/secp256k1/src/testrand.h b/src/secp256k1/src/testrand.h index bd149bb1b4..d109bb9f8b 100644 --- a/src/secp256k1/src/testrand.h +++ b/src/secp256k1/src/testrand.h @@ -7,10 +7,6 @@ #ifndef SECP256K1_TESTRAND_H #define SECP256K1_TESTRAND_H -#if defined HAVE_CONFIG_H -#include "libsecp256k1-config.h" -#endif - /* A non-cryptographic RNG used only for test infrastructure. */ /** Seed the pseudorandom number generator for testing. */ diff --git a/src/secp256k1/src/tests.c b/src/secp256k1/src/tests.c index 53613f420a..1c0d797349 100644 --- a/src/secp256k1/src/tests.c +++ b/src/secp256k1/src/tests.c @@ -4,10 +4,6 @@ * file COPYING or https://www.opensource.org/licenses/mit-license.php.* ***********************************************************************/ -#if defined HAVE_CONFIG_H -#include "libsecp256k1-config.h" -#endif - #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -18,6 +14,7 @@ #include "../include/secp256k1.h" #include "../include/secp256k1_preallocated.h" #include "testrand_impl.h" +#include "checkmem.h" #include "util.h" #include "../contrib/lax_der_parsing.c" @@ -29,16 +26,49 @@ #include "int128_impl.h" #endif -#define CONDITIONAL_TEST(cnt, nam) if (count < (cnt)) { printf("Skipping %s (iteration count too low)\n", nam); } else +#define CONDITIONAL_TEST(cnt, nam) if (COUNT < (cnt)) { printf("Skipping %s (iteration count too low)\n", nam); } else + +static int COUNT = 64; +static secp256k1_context *CTX = NULL; +static secp256k1_context *STATIC_CTX = NULL; + +static int all_bytes_equal(const void* s, unsigned char value, size_t n) { + const unsigned char *p = s; + size_t i; + + for (i = 0; i < n; i++) { + if (p[i] != value) { + return 0; + } + } + return 1; +} -static int count = 64; -static secp256k1_context *ctx = NULL; +/* TODO Use CHECK_ILLEGAL(_VOID) everywhere and get rid of the uncounting callback */ +/* CHECK that expr_or_stmt calls the illegal callback of ctx exactly once + * + * For checking functions that use ARG_CHECK_VOID */ +#define CHECK_ILLEGAL_VOID(ctx, expr_or_stmt) do { \ + int32_t _calls_to_illegal_callback = 0; \ + secp256k1_callback _saved_illegal_cb = ctx->illegal_callback; \ + secp256k1_context_set_illegal_callback(ctx, \ + counting_illegal_callback_fn, &_calls_to_illegal_callback); \ + { expr_or_stmt; } \ + ctx->illegal_callback = _saved_illegal_cb; \ + CHECK(_calls_to_illegal_callback == 1); \ +} while(0); + +/* CHECK that expr calls the illegal callback of ctx exactly once and that expr == 0 + * + * For checking functions that use ARG_CHECK */ +#define CHECK_ILLEGAL(ctx, expr) CHECK_ILLEGAL_VOID(ctx, CHECK((expr) == 0)) static void counting_illegal_callback_fn(const char* str, void* data) { /* Dummy callback function that just counts. */ int32_t *p; (void)str; p = data; + CHECK(*p != INT32_MAX); (*p)++; } @@ -47,10 +77,11 @@ static void uncounting_illegal_callback_fn(const char* str, void* data) { int32_t *p; (void)str; p = data; + CHECK(*p != INT32_MIN); (*p)--; } -void random_field_element_test(secp256k1_fe *fe) { +static void random_field_element_test(secp256k1_fe *fe) { do { unsigned char b32[32]; secp256k1_testrand256_test(b32); @@ -60,7 +91,7 @@ void random_field_element_test(secp256k1_fe *fe) { } while(1); } -void random_field_element_magnitude(secp256k1_fe *fe) { +static void random_field_element_magnitude(secp256k1_fe *fe) { secp256k1_fe zero; int n = secp256k1_testrand_int(9); secp256k1_fe_normalize(fe); @@ -76,7 +107,7 @@ void random_field_element_magnitude(secp256k1_fe *fe) { #endif } -void random_group_element_test(secp256k1_ge *ge) { +static void random_group_element_test(secp256k1_ge *ge) { secp256k1_fe fe; do { random_field_element_test(&fe); @@ -88,7 +119,7 @@ void random_group_element_test(secp256k1_ge *ge) { ge->infinity = 0; } -void random_group_element_jacobian_test(secp256k1_gej *gej, const secp256k1_ge *ge) { +static void random_group_element_jacobian_test(secp256k1_gej *gej, const secp256k1_ge *ge) { secp256k1_fe z2, z3; do { random_field_element_test(&gej->z); @@ -103,13 +134,13 @@ void random_group_element_jacobian_test(secp256k1_gej *gej, const secp256k1_ge * gej->infinity = ge->infinity; } -void random_gej_test(secp256k1_gej *gej) { +static void random_gej_test(secp256k1_gej *gej) { secp256k1_ge ge; random_group_element_test(&ge); random_group_element_jacobian_test(gej, &ge); } -void random_scalar_order_test(secp256k1_scalar *num) { +static void random_scalar_order_test(secp256k1_scalar *num) { do { unsigned char b32[32]; int overflow = 0; @@ -122,7 +153,7 @@ void random_scalar_order_test(secp256k1_scalar *num) { } while(1); } -void random_scalar_order(secp256k1_scalar *num) { +static void random_scalar_order(secp256k1_scalar *num) { do { unsigned char b32[32]; int overflow = 0; @@ -135,96 +166,179 @@ void random_scalar_order(secp256k1_scalar *num) { } while(1); } -void random_scalar_order_b32(unsigned char *b32) { +static void random_scalar_order_b32(unsigned char *b32) { secp256k1_scalar num; random_scalar_order(&num); secp256k1_scalar_get_b32(b32, &num); } -void run_selftest_tests(void) { +static void run_selftest_tests(void) { /* Test public API */ secp256k1_selftest(); } -int ecmult_gen_context_eq(const secp256k1_ecmult_gen_context *a, const secp256k1_ecmult_gen_context *b) { +static int ecmult_gen_context_eq(const secp256k1_ecmult_gen_context *a, const secp256k1_ecmult_gen_context *b) { return a->built == b->built && secp256k1_scalar_eq(&a->blind, &b->blind) && secp256k1_gej_eq_var(&a->initial, &b->initial); } -int context_eq(const secp256k1_context *a, const secp256k1_context *b) { +static int context_eq(const secp256k1_context *a, const secp256k1_context *b) { return a->declassify == b->declassify && ecmult_gen_context_eq(&a->ecmult_gen_ctx, &b->ecmult_gen_ctx) && a->illegal_callback.fn == b->illegal_callback.fn - && a->illegal_callback.data == b->illegal_callback. -data + && a->illegal_callback.data == b->illegal_callback.data && a->error_callback.fn == b->error_callback.fn && a->error_callback.data == b->error_callback.data; } -void test_deprecated_flags(void) { +static void run_deprecated_context_flags_test(void) { + /* Check that a context created with any of the flags in the flags array is + * identical to the NONE context. */ unsigned int flags[] = { SECP256K1_CONTEXT_SIGN, SECP256K1_CONTEXT_VERIFY, SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY }; + secp256k1_context *none_ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); int i; - /* Check that a context created with any of the flags in the flags array is - * identical to the NONE context. */ for (i = 0; i < (int)(sizeof(flags)/sizeof(flags[0])); i++) { secp256k1_context *tmp_ctx; CHECK(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_NONE) == secp256k1_context_preallocated_size(flags[i])); tmp_ctx = secp256k1_context_create(flags[i]); - CHECK(context_eq(ctx, tmp_ctx)); + CHECK(context_eq(none_ctx, tmp_ctx)); secp256k1_context_destroy(tmp_ctx); } + secp256k1_context_destroy(none_ctx); } -void run_context_tests(int use_prealloc) { +static void run_ec_illegal_argument_tests(void) { + int ecount = 0; + int ecount2 = 10; secp256k1_pubkey pubkey; secp256k1_pubkey zero_pubkey; secp256k1_ecdsa_signature sig; unsigned char ctmp[32]; - int32_t ecount; - int32_t ecount2; - secp256k1_context *sttc; - void *ctx_prealloc = NULL; - void *sttc_prealloc = NULL; + + /* Setup */ + secp256k1_context_set_illegal_callback(STATIC_CTX, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(CTX, counting_illegal_callback_fn, &ecount2); + memset(ctmp, 1, 32); + memset(&zero_pubkey, 0, sizeof(zero_pubkey)); + + /* Verify context-type checking illegal-argument errors. */ + CHECK(secp256k1_ec_pubkey_create(STATIC_CTX, &pubkey, ctmp) == 0); + CHECK(ecount == 1); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey, ctmp) == 1); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ecdsa_sign(STATIC_CTX, &sig, ctmp, ctmp, NULL, NULL) == 0); + CHECK(ecount == 2); + SECP256K1_CHECKMEM_UNDEFINE(&sig, sizeof(sig)); + CHECK(secp256k1_ecdsa_sign(CTX, &sig, ctmp, ctmp, NULL, NULL) == 1); + SECP256K1_CHECKMEM_CHECK(&sig, sizeof(sig)); + CHECK(ecount2 == 10); + CHECK(secp256k1_ecdsa_verify(CTX, &sig, ctmp, &pubkey) == 1); + CHECK(ecount2 == 10); + CHECK(secp256k1_ecdsa_verify(STATIC_CTX, &sig, ctmp, &pubkey) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_ec_pubkey_tweak_add(CTX, &pubkey, ctmp) == 1); + CHECK(ecount2 == 10); + CHECK(secp256k1_ec_pubkey_tweak_add(STATIC_CTX, &pubkey, ctmp) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_ec_pubkey_tweak_mul(CTX, &pubkey, ctmp) == 1); + CHECK(ecount2 == 10); + CHECK(secp256k1_ec_pubkey_negate(STATIC_CTX, &pubkey) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_ec_pubkey_negate(CTX, &pubkey) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_ec_pubkey_negate(STATIC_CTX, &zero_pubkey) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_ec_pubkey_negate(CTX, NULL) == 0); + CHECK(ecount2 == 11); + CHECK(secp256k1_ec_pubkey_tweak_mul(STATIC_CTX, &pubkey, ctmp) == 1); + CHECK(ecount == 3); + + /* Clean up */ + secp256k1_context_set_illegal_callback(STATIC_CTX, NULL, NULL); + secp256k1_context_set_illegal_callback(CTX, NULL, NULL); +} + +static void run_static_context_tests(int use_prealloc) { + /* Check that deprecated secp256k1_context_no_precomp is an alias to secp256k1_context_static. */ + CHECK(secp256k1_context_no_precomp == secp256k1_context_static); + + { + unsigned char seed[32] = {0x17}; + + /* Randomizing secp256k1_context_static is not supported. */ + CHECK_ILLEGAL(STATIC_CTX, secp256k1_context_randomize(STATIC_CTX, seed)); + CHECK_ILLEGAL(STATIC_CTX, secp256k1_context_randomize(STATIC_CTX, NULL)); + + /* Destroying or cloning secp256k1_context_static is not supported. */ + if (use_prealloc) { + CHECK_ILLEGAL(STATIC_CTX, secp256k1_context_preallocated_clone_size(STATIC_CTX)); + { + secp256k1_context *my_static_ctx = malloc(sizeof(*STATIC_CTX)); + CHECK(my_static_ctx != NULL); + memset(my_static_ctx, 0x2a, sizeof(*my_static_ctx)); + CHECK_ILLEGAL(STATIC_CTX, secp256k1_context_preallocated_clone(STATIC_CTX, my_static_ctx)); + CHECK(all_bytes_equal(my_static_ctx, 0x2a, sizeof(*my_static_ctx))); + free(my_static_ctx); + } + CHECK_ILLEGAL_VOID(STATIC_CTX, secp256k1_context_preallocated_destroy(STATIC_CTX)); + } else { + CHECK_ILLEGAL(STATIC_CTX, secp256k1_context_clone(STATIC_CTX)); + CHECK_ILLEGAL_VOID(STATIC_CTX, secp256k1_context_destroy(STATIC_CTX)); + } + } + + { + /* Verify that setting and resetting illegal callback works */ + int32_t dummy = 0; + secp256k1_context_set_illegal_callback(STATIC_CTX, counting_illegal_callback_fn, &dummy); + CHECK(STATIC_CTX->illegal_callback.fn == counting_illegal_callback_fn); + CHECK(STATIC_CTX->illegal_callback.data == &dummy); + secp256k1_context_set_illegal_callback(STATIC_CTX, NULL, NULL); + CHECK(STATIC_CTX->illegal_callback.fn == secp256k1_default_illegal_callback_fn); + CHECK(STATIC_CTX->illegal_callback.data == NULL); + } +} + +static void run_proper_context_tests(int use_prealloc) { + int32_t dummy = 0; + secp256k1_context *my_ctx, *my_ctx_fresh; + void *my_ctx_prealloc = NULL; + unsigned char seed[32] = {0x17}; secp256k1_gej pubj; secp256k1_ge pub; secp256k1_scalar msg, key, nonce; secp256k1_scalar sigr, sigs; - /* Check that deprecated secp256k1_context_no_precomp is an alias to secp256k1_context_static. */ - CHECK(secp256k1_context_no_precomp == secp256k1_context_static); + /* Fresh reference context for comparison */ + my_ctx_fresh = secp256k1_context_create(SECP256K1_CONTEXT_NONE); if (use_prealloc) { - ctx_prealloc = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_NONE)); - CHECK(ctx_prealloc != NULL); - ctx = secp256k1_context_preallocated_create(ctx_prealloc, SECP256K1_CONTEXT_NONE); - sttc_prealloc = malloc(secp256k1_context_preallocated_clone_size(secp256k1_context_static)); - CHECK(sttc_prealloc != NULL); - sttc = secp256k1_context_preallocated_clone(secp256k1_context_static, sttc_prealloc); + my_ctx_prealloc = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_NONE)); + CHECK(my_ctx_prealloc != NULL); + my_ctx = secp256k1_context_preallocated_create(my_ctx_prealloc, SECP256K1_CONTEXT_NONE); } else { - sttc = secp256k1_context_clone(secp256k1_context_static); - ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + my_ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); } - test_deprecated_flags(); + /* Randomize and reset randomization */ + CHECK(context_eq(my_ctx, my_ctx_fresh)); + CHECK(secp256k1_context_randomize(my_ctx, seed) == 1); + CHECK(!context_eq(my_ctx, my_ctx_fresh)); + CHECK(secp256k1_context_randomize(my_ctx, NULL) == 1); + CHECK(context_eq(my_ctx, my_ctx_fresh)); - memset(&zero_pubkey, 0, sizeof(zero_pubkey)); - - ecount = 0; - ecount2 = 10; - secp256k1_context_set_illegal_callback(sttc, counting_illegal_callback_fn, &ecount); - secp256k1_context_set_illegal_callback(ctx, counting_illegal_callback_fn, &ecount2); /* set error callback (to a function that still aborts in case malloc() fails in secp256k1_context_clone() below) */ - secp256k1_context_set_error_callback(ctx, secp256k1_default_illegal_callback_fn, NULL); - CHECK(ctx->error_callback.fn != sttc->error_callback.fn); - CHECK(ctx->error_callback.fn == secp256k1_default_illegal_callback_fn); + secp256k1_context_set_error_callback(my_ctx, secp256k1_default_illegal_callback_fn, NULL); + CHECK(my_ctx->error_callback.fn != secp256k1_default_error_callback_fn); + CHECK(my_ctx->error_callback.fn == secp256k1_default_illegal_callback_fn); /* check if sizes for cloning are consistent */ - CHECK(secp256k1_context_preallocated_clone_size(ctx) == secp256k1_context_preallocated_size(SECP256K1_CONTEXT_NONE)); - CHECK(secp256k1_context_preallocated_clone_size(sttc) >= sizeof(secp256k1_context)); + CHECK(secp256k1_context_preallocated_clone_size(my_ctx) == secp256k1_context_preallocated_size(SECP256K1_CONTEXT_NONE)); /*** clone and destroy all of them to make sure cloning was complete ***/ { @@ -232,104 +346,86 @@ void run_context_tests(int use_prealloc) { if (use_prealloc) { /* clone into a non-preallocated context and then again into a new preallocated one. */ - ctx_tmp = ctx; ctx = secp256k1_context_clone(ctx); secp256k1_context_preallocated_destroy(ctx_tmp); - free(ctx_prealloc); ctx_prealloc = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_NONE)); CHECK(ctx_prealloc != NULL); - ctx_tmp = ctx; ctx = secp256k1_context_preallocated_clone(ctx, ctx_prealloc); secp256k1_context_destroy(ctx_tmp); + ctx_tmp = my_ctx; + my_ctx = secp256k1_context_clone(my_ctx); + CHECK(context_eq(ctx_tmp, my_ctx)); + secp256k1_context_preallocated_destroy(ctx_tmp); + + free(my_ctx_prealloc); + my_ctx_prealloc = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_NONE)); + CHECK(my_ctx_prealloc != NULL); + ctx_tmp = my_ctx; + my_ctx = secp256k1_context_preallocated_clone(my_ctx, my_ctx_prealloc); + CHECK(context_eq(ctx_tmp, my_ctx)); + secp256k1_context_destroy(ctx_tmp); } else { /* clone into a preallocated context and then again into a new non-preallocated one. */ void *prealloc_tmp; - prealloc_tmp = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_NONE)); CHECK(prealloc_tmp != NULL); - ctx_tmp = ctx; ctx = secp256k1_context_preallocated_clone(ctx, prealloc_tmp); secp256k1_context_destroy(ctx_tmp); - ctx_tmp = ctx; ctx = secp256k1_context_clone(ctx); secp256k1_context_preallocated_destroy(ctx_tmp); + prealloc_tmp = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_NONE)); + CHECK(prealloc_tmp != NULL); + ctx_tmp = my_ctx; + my_ctx = secp256k1_context_preallocated_clone(my_ctx, prealloc_tmp); + CHECK(context_eq(ctx_tmp, my_ctx)); + secp256k1_context_destroy(ctx_tmp); + + ctx_tmp = my_ctx; + my_ctx = secp256k1_context_clone(my_ctx); + CHECK(context_eq(ctx_tmp, my_ctx)); + secp256k1_context_preallocated_destroy(ctx_tmp); free(prealloc_tmp); } } /* Verify that the error callback makes it across the clone. */ - CHECK(ctx->error_callback.fn != sttc->error_callback.fn); - CHECK(ctx->error_callback.fn == secp256k1_default_illegal_callback_fn); + CHECK(my_ctx->error_callback.fn != secp256k1_default_error_callback_fn); + CHECK(my_ctx->error_callback.fn == secp256k1_default_illegal_callback_fn); /* And that it resets back to default. */ - secp256k1_context_set_error_callback(ctx, NULL, NULL); - CHECK(ctx->error_callback.fn == sttc->error_callback.fn); + secp256k1_context_set_error_callback(my_ctx, NULL, NULL); + CHECK(my_ctx->error_callback.fn == secp256k1_default_error_callback_fn); + CHECK(context_eq(my_ctx, my_ctx_fresh)); + + /* Verify that setting and resetting illegal callback works */ + secp256k1_context_set_illegal_callback(my_ctx, counting_illegal_callback_fn, &dummy); + CHECK(my_ctx->illegal_callback.fn == counting_illegal_callback_fn); + CHECK(my_ctx->illegal_callback.data == &dummy); + secp256k1_context_set_illegal_callback(my_ctx, NULL, NULL); + CHECK(my_ctx->illegal_callback.fn == secp256k1_default_illegal_callback_fn); + CHECK(my_ctx->illegal_callback.data == NULL); + CHECK(context_eq(my_ctx, my_ctx_fresh)); /*** attempt to use them ***/ random_scalar_order_test(&msg); random_scalar_order_test(&key); - secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pubj, &key); + secp256k1_ecmult_gen(&my_ctx->ecmult_gen_ctx, &pubj, &key); secp256k1_ge_set_gej(&pub, &pubj); - /* Verify context-type checking illegal-argument errors. */ - memset(ctmp, 1, 32); - CHECK(secp256k1_ec_pubkey_create(sttc, &pubkey, ctmp) == 0); - CHECK(ecount == 1); - VG_UNDEF(&pubkey, sizeof(pubkey)); - CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, ctmp) == 1); - VG_CHECK(&pubkey, sizeof(pubkey)); - CHECK(secp256k1_ecdsa_sign(sttc, &sig, ctmp, ctmp, NULL, NULL) == 0); - CHECK(ecount == 2); - VG_UNDEF(&sig, sizeof(sig)); - CHECK(secp256k1_ecdsa_sign(ctx, &sig, ctmp, ctmp, NULL, NULL) == 1); - VG_CHECK(&sig, sizeof(sig)); - CHECK(ecount2 == 10); - CHECK(secp256k1_ecdsa_verify(ctx, &sig, ctmp, &pubkey) == 1); - CHECK(ecount2 == 10); - CHECK(secp256k1_ecdsa_verify(sttc, &sig, ctmp, &pubkey) == 1); - CHECK(ecount == 2); - CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, ctmp) == 1); - CHECK(ecount2 == 10); - CHECK(secp256k1_ec_pubkey_tweak_add(sttc, &pubkey, ctmp) == 1); - CHECK(ecount == 2); - CHECK(secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, ctmp) == 1); - CHECK(ecount2 == 10); - CHECK(secp256k1_ec_pubkey_negate(sttc, &pubkey) == 1); - CHECK(ecount == 2); - CHECK(secp256k1_ec_pubkey_negate(ctx, &pubkey) == 1); - CHECK(ecount == 2); - CHECK(secp256k1_ec_pubkey_negate(ctx, NULL) == 0); - CHECK(ecount2 == 11); - CHECK(secp256k1_ec_pubkey_negate(sttc, &zero_pubkey) == 0); - CHECK(ecount == 3); - CHECK(secp256k1_ec_pubkey_tweak_mul(sttc, &pubkey, ctmp) == 1); - CHECK(ecount == 3); - CHECK(secp256k1_context_randomize(sttc, ctmp) == 1); - CHECK(ecount == 3); - CHECK(secp256k1_context_randomize(sttc, NULL) == 1); - CHECK(ecount == 3); - CHECK(secp256k1_context_randomize(ctx, ctmp) == 1); - CHECK(ecount2 == 11); - CHECK(secp256k1_context_randomize(ctx, NULL) == 1); - CHECK(ecount2 == 11); - secp256k1_context_set_illegal_callback(sttc, NULL, NULL); - secp256k1_context_set_illegal_callback(ctx, NULL, NULL); - /* obtain a working nonce */ do { random_scalar_order_test(&nonce); - } while(!secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, &sigr, &sigs, &key, &msg, &nonce, NULL)); + } while(!secp256k1_ecdsa_sig_sign(&my_ctx->ecmult_gen_ctx, &sigr, &sigs, &key, &msg, &nonce, NULL)); /* try signing */ - CHECK(secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, &sigr, &sigs, &key, &msg, &nonce, NULL)); + CHECK(secp256k1_ecdsa_sig_sign(&my_ctx->ecmult_gen_ctx, &sigr, &sigs, &key, &msg, &nonce, NULL)); /* try verifying */ CHECK(secp256k1_ecdsa_sig_verify(&sigr, &sigs, &pub, &msg)); /* cleanup */ if (use_prealloc) { - secp256k1_context_preallocated_destroy(ctx); - secp256k1_context_preallocated_destroy(sttc); - free(ctx_prealloc); - free(sttc_prealloc); + secp256k1_context_preallocated_destroy(my_ctx); + free(my_ctx_prealloc); } else { - secp256k1_context_destroy(ctx); - secp256k1_context_destroy(sttc); + secp256k1_context_destroy(my_ctx); } + secp256k1_context_destroy(my_ctx_fresh); + /* Defined as no-op. */ secp256k1_context_destroy(NULL); secp256k1_context_preallocated_destroy(NULL); } -void run_scratch_tests(void) { +static void run_scratch_tests(void) { const size_t adj_alloc = ((500 + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT; int32_t ecount = 0; @@ -338,83 +434,82 @@ void run_scratch_tests(void) { secp256k1_scratch_space *scratch; secp256k1_scratch_space local_scratch; - ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + secp256k1_context_set_illegal_callback(CTX, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(CTX, counting_illegal_callback_fn, &ecount); /* Test public API */ - secp256k1_context_set_illegal_callback(ctx, counting_illegal_callback_fn, &ecount); - secp256k1_context_set_error_callback(ctx, counting_illegal_callback_fn, &ecount); - - scratch = secp256k1_scratch_space_create(ctx, 1000); + scratch = secp256k1_scratch_space_create(CTX, 1000); CHECK(scratch != NULL); CHECK(ecount == 0); /* Test internal API */ - CHECK(secp256k1_scratch_max_allocation(&ctx->error_callback, scratch, 0) == 1000); - CHECK(secp256k1_scratch_max_allocation(&ctx->error_callback, scratch, 1) == 1000 - (ALIGNMENT - 1)); + CHECK(secp256k1_scratch_max_allocation(&CTX->error_callback, scratch, 0) == 1000); + CHECK(secp256k1_scratch_max_allocation(&CTX->error_callback, scratch, 1) == 1000 - (ALIGNMENT - 1)); CHECK(scratch->alloc_size == 0); CHECK(scratch->alloc_size % ALIGNMENT == 0); /* Allocating 500 bytes succeeds */ - checkpoint = secp256k1_scratch_checkpoint(&ctx->error_callback, scratch); - CHECK(secp256k1_scratch_alloc(&ctx->error_callback, scratch, 500) != NULL); - CHECK(secp256k1_scratch_max_allocation(&ctx->error_callback, scratch, 0) == 1000 - adj_alloc); - CHECK(secp256k1_scratch_max_allocation(&ctx->error_callback, scratch, 1) == 1000 - adj_alloc - (ALIGNMENT - 1)); + checkpoint = secp256k1_scratch_checkpoint(&CTX->error_callback, scratch); + CHECK(secp256k1_scratch_alloc(&CTX->error_callback, scratch, 500) != NULL); + CHECK(secp256k1_scratch_max_allocation(&CTX->error_callback, scratch, 0) == 1000 - adj_alloc); + CHECK(secp256k1_scratch_max_allocation(&CTX->error_callback, scratch, 1) == 1000 - adj_alloc - (ALIGNMENT - 1)); CHECK(scratch->alloc_size != 0); CHECK(scratch->alloc_size % ALIGNMENT == 0); /* Allocating another 501 bytes fails */ - CHECK(secp256k1_scratch_alloc(&ctx->error_callback, scratch, 501) == NULL); - CHECK(secp256k1_scratch_max_allocation(&ctx->error_callback, scratch, 0) == 1000 - adj_alloc); - CHECK(secp256k1_scratch_max_allocation(&ctx->error_callback, scratch, 1) == 1000 - adj_alloc - (ALIGNMENT - 1)); + CHECK(secp256k1_scratch_alloc(&CTX->error_callback, scratch, 501) == NULL); + CHECK(secp256k1_scratch_max_allocation(&CTX->error_callback, scratch, 0) == 1000 - adj_alloc); + CHECK(secp256k1_scratch_max_allocation(&CTX->error_callback, scratch, 1) == 1000 - adj_alloc - (ALIGNMENT - 1)); CHECK(scratch->alloc_size != 0); CHECK(scratch->alloc_size % ALIGNMENT == 0); /* ...but it succeeds once we apply the checkpoint to undo it */ - secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, checkpoint); + secp256k1_scratch_apply_checkpoint(&CTX->error_callback, scratch, checkpoint); CHECK(scratch->alloc_size == 0); - CHECK(secp256k1_scratch_max_allocation(&ctx->error_callback, scratch, 0) == 1000); - CHECK(secp256k1_scratch_alloc(&ctx->error_callback, scratch, 500) != NULL); + CHECK(secp256k1_scratch_max_allocation(&CTX->error_callback, scratch, 0) == 1000); + CHECK(secp256k1_scratch_alloc(&CTX->error_callback, scratch, 500) != NULL); CHECK(scratch->alloc_size != 0); /* try to apply a bad checkpoint */ - checkpoint_2 = secp256k1_scratch_checkpoint(&ctx->error_callback, scratch); - secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, checkpoint); + checkpoint_2 = secp256k1_scratch_checkpoint(&CTX->error_callback, scratch); + secp256k1_scratch_apply_checkpoint(&CTX->error_callback, scratch, checkpoint); CHECK(ecount == 0); - secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, checkpoint_2); /* checkpoint_2 is after checkpoint */ + secp256k1_scratch_apply_checkpoint(&CTX->error_callback, scratch, checkpoint_2); /* checkpoint_2 is after checkpoint */ CHECK(ecount == 1); - secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, (size_t) -1); /* this is just wildly invalid */ + secp256k1_scratch_apply_checkpoint(&CTX->error_callback, scratch, (size_t) -1); /* this is just wildly invalid */ CHECK(ecount == 2); /* try to use badly initialized scratch space */ - secp256k1_scratch_space_destroy(ctx, scratch); + secp256k1_scratch_space_destroy(CTX, scratch); memset(&local_scratch, 0, sizeof(local_scratch)); scratch = &local_scratch; - CHECK(!secp256k1_scratch_max_allocation(&ctx->error_callback, scratch, 0)); + CHECK(!secp256k1_scratch_max_allocation(&CTX->error_callback, scratch, 0)); CHECK(ecount == 3); - CHECK(secp256k1_scratch_alloc(&ctx->error_callback, scratch, 500) == NULL); + CHECK(secp256k1_scratch_alloc(&CTX->error_callback, scratch, 500) == NULL); CHECK(ecount == 4); - secp256k1_scratch_space_destroy(ctx, scratch); + secp256k1_scratch_space_destroy(CTX, scratch); CHECK(ecount == 5); /* Test that large integers do not wrap around in a bad way */ - scratch = secp256k1_scratch_space_create(ctx, 1000); + scratch = secp256k1_scratch_space_create(CTX, 1000); /* Try max allocation with a large number of objects. Only makes sense if * ALIGNMENT is greater than 1 because otherwise the objects take no extra * space. */ - CHECK(ALIGNMENT <= 1 || !secp256k1_scratch_max_allocation(&ctx->error_callback, scratch, (SIZE_MAX / (ALIGNMENT - 1)) + 1)); + CHECK(ALIGNMENT <= 1 || !secp256k1_scratch_max_allocation(&CTX->error_callback, scratch, (SIZE_MAX / (ALIGNMENT - 1)) + 1)); /* Try allocating SIZE_MAX to test wrap around which only happens if * ALIGNMENT > 1, otherwise it returns NULL anyway because the scratch * space is too small. */ - CHECK(secp256k1_scratch_alloc(&ctx->error_callback, scratch, SIZE_MAX) == NULL); - secp256k1_scratch_space_destroy(ctx, scratch); + CHECK(secp256k1_scratch_alloc(&CTX->error_callback, scratch, SIZE_MAX) == NULL); + secp256k1_scratch_space_destroy(CTX, scratch); /* cleanup */ - secp256k1_scratch_space_destroy(ctx, NULL); /* no-op */ - secp256k1_context_destroy(ctx); -} + secp256k1_scratch_space_destroy(CTX, NULL); /* no-op */ + secp256k1_context_set_illegal_callback(CTX, NULL, NULL); + secp256k1_context_set_error_callback(CTX, NULL, NULL); +} -void run_ctz_tests(void) { +static 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; @@ -435,7 +530,7 @@ void run_ctz_tests(void) { /***** HASH TESTS *****/ -void run_sha256_known_output_tests(void) { +static void run_sha256_known_output_tests(void) { static const char *inputs[] = { "", "abc", "message digest", "secure hash algorithm", "SHA256 is considered to be safe", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", @@ -536,7 +631,7 @@ for x in digests: print(x + ',') ``` */ -void run_sha256_counter_tests(void) { +static void run_sha256_counter_tests(void) { static const char *input = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmno"; static const secp256k1_sha256 midstates[] = { {{0xa2b5c8bb, 0x26c88bb3, 0x2abdc3d2, 0x9def99a3, 0xdfd21a6e, 0x41fe585b, 0x7ef2c440, 0x2b79adda}, @@ -594,7 +689,7 @@ void run_sha256_counter_tests(void) { } } -void run_hmac_sha256_tests(void) { +static void run_hmac_sha256_tests(void) { static const char *keys[6] = { "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b", "\x4a\x65\x66\x65", @@ -638,7 +733,7 @@ void run_hmac_sha256_tests(void) { } } -void run_rfc6979_hmac_sha256_tests(void) { +static void run_rfc6979_hmac_sha256_tests(void) { static const unsigned char key1[65] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x00, 0x4b, 0xf5, 0x12, 0x2f, 0x34, 0x45, 0x54, 0xc5, 0x3b, 0xde, 0x2e, 0xbb, 0x8c, 0xd2, 0xb7, 0xe3, 0xd1, 0x60, 0x0a, 0xd6, 0x31, 0xc3, 0x85, 0xa5, 0xd7, 0xcc, 0xe2, 0x3c, 0x77, 0x85, 0x45, 0x9a, 0}; static const unsigned char out1[3][32] = { {0x4f, 0xe2, 0x95, 0x25, 0xb2, 0x08, 0x68, 0x09, 0x15, 0x9a, 0xcd, 0xf0, 0x50, 0x6e, 0xfb, 0x86, 0xb0, 0xec, 0x93, 0x2c, 0x7b, 0xa4, 0x42, 0x56, 0xab, 0x32, 0x1e, 0x42, 0x1e, 0x67, 0xe9, 0xfb}, @@ -679,7 +774,7 @@ void run_rfc6979_hmac_sha256_tests(void) { secp256k1_rfc6979_hmac_sha256_finalize(&rng); } -void run_tagged_sha256_tests(void) { +static void run_tagged_sha256_tests(void) { int ecount = 0; unsigned char tag[32] = { 0 }; unsigned char msg[32] = { 0 }; @@ -691,27 +786,27 @@ void run_tagged_sha256_tests(void) { 0xE2, 0x76, 0x55, 0x9A, 0x3B, 0xDE, 0x55, 0xB3 }; - secp256k1_context_set_illegal_callback(ctx, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(CTX, counting_illegal_callback_fn, &ecount); /* API test */ - CHECK(secp256k1_tagged_sha256(ctx, hash32, tag, sizeof(tag), msg, sizeof(msg)) == 1); - CHECK(secp256k1_tagged_sha256(ctx, NULL, tag, sizeof(tag), msg, sizeof(msg)) == 0); + CHECK(secp256k1_tagged_sha256(CTX, hash32, tag, sizeof(tag), msg, sizeof(msg)) == 1); + CHECK(secp256k1_tagged_sha256(CTX, NULL, tag, sizeof(tag), msg, sizeof(msg)) == 0); CHECK(ecount == 1); - CHECK(secp256k1_tagged_sha256(ctx, hash32, NULL, 0, msg, sizeof(msg)) == 0); + CHECK(secp256k1_tagged_sha256(CTX, hash32, NULL, 0, msg, sizeof(msg)) == 0); CHECK(ecount == 2); - CHECK(secp256k1_tagged_sha256(ctx, hash32, tag, sizeof(tag), NULL, 0) == 0); + CHECK(secp256k1_tagged_sha256(CTX, hash32, tag, sizeof(tag), NULL, 0) == 0); CHECK(ecount == 3); /* Static test vector */ memcpy(tag, "tag", 3); memcpy(msg, "msg", 3); - CHECK(secp256k1_tagged_sha256(ctx, hash32, tag, 3, msg, 3) == 1); + CHECK(secp256k1_tagged_sha256(CTX, hash32, tag, 3, msg, 3) == 1); CHECK(secp256k1_memcmp_var(hash32, hash_expected, sizeof(hash32)) == 0); } /***** RANDOM TESTS *****/ -void test_rand_bits(int rand32, int bits) { +static void test_rand_bits(int rand32, int bits) { /* (1-1/2^B)^rounds[B] < 1/10^9, so rounds is the number of iterations to * get a false negative chance below once in a billion */ static const unsigned int rounds[7] = {1, 30, 73, 156, 322, 653, 1316}; @@ -746,7 +841,7 @@ void test_rand_bits(int rand32, int bits) { } /* Subrange must be a whole divisor of range, and at most 64 */ -void test_rand_int(uint32_t range, uint32_t subrange) { +static void test_rand_int(uint32_t range, uint32_t subrange) { /* (1-1/subrange)^rounds < 1/10^9 */ int rounds = (subrange * 2073) / 100; int i; @@ -762,7 +857,7 @@ void test_rand_int(uint32_t range, uint32_t subrange) { CHECK(((~x) << (64 - subrange)) == 0); } -void run_rand_bits(void) { +static void run_rand_bits(void) { size_t b; test_rand_bits(1, 32); for (b = 1; b <= 32; b++) { @@ -770,7 +865,7 @@ void run_rand_bits(void) { } } -void run_rand_int(void) { +static void run_rand_int(void) { static const uint32_t ms[] = {1, 3, 17, 1000, 13771, 999999, 33554432}; static const uint32_t ss[] = {1, 3, 6, 9, 13, 31, 64}; unsigned int m, s; @@ -784,7 +879,7 @@ void run_rand_int(void) { /***** MODINV TESTS *****/ /* Compute the modular inverse of (odd) x mod 2^64. */ -uint64_t modinv2p64(uint64_t x) { +static 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 @@ -801,7 +896,7 @@ uint64_t modinv2p64(uint64_t x) { * * 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) { +static 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; @@ -885,7 +980,7 @@ void mulmod256(uint16_t* out, const uint16_t* a, const uint16_t* b, const uint16 } /* 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) { +static 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) { @@ -894,7 +989,7 @@ void uint16_to_signed30(secp256k1_modinv32_signed30* out, const uint16_t* in) { } /* 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) { +static void signed30_to_uint16(uint16_t* out, const secp256k1_modinv32_signed30* in) { int i; memset(out, 0, 32); for (i = 0; i < 256; ++i) { @@ -903,7 +998,7 @@ void signed30_to_uint16(uint16_t* out, const secp256k1_modinv32_signed30* in) { } /* Randomly mutate the sign of limbs in signed30 representation, without changing the value. */ -void mutate_sign_signed30(secp256k1_modinv32_signed30* x) { +static void mutate_sign_signed30(secp256k1_modinv32_signed30* x) { int i; for (i = 0; i < 16; ++i) { int pos = secp256k1_testrand_bits(3); @@ -918,7 +1013,7 @@ void mutate_sign_signed30(secp256k1_modinv32_signed30* x) { } /* 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) { +static 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; @@ -927,12 +1022,32 @@ void test_modinv32_uint16(uint16_t* out, const uint16_t* in, const uint16_t* mod 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); + /* Test secp256k1_jacobi32_maybe_var. */ + if (nonzero) { + int jac; + uint16_t sqr[16], negone[16]; + mulmod256(sqr, in, in, mod); + uint16_to_signed30(&x, sqr); + /* Compute jacobi symbol of in^2, which must be 1 (or uncomputable). */ + jac = secp256k1_jacobi32_maybe_var(&x, &m); + CHECK(jac == 0 || jac == 1); + /* Then compute the jacobi symbol of -(in^2). x and -x have opposite + * jacobi symbols if and only if (mod % 4) == 3. */ + negone[0] = mod[0] - 1; + for (i = 1; i < 16; ++i) negone[i] = mod[i]; + mulmod256(sqr, sqr, negone, mod); + uint16_to_signed30(&x, sqr); + jac = secp256k1_jacobi32_maybe_var(&x, &m); + CHECK(jac == 0 || jac == 1 - (mod[0] & 2)); + } + + uint16_to_signed30(&x, in); + mutate_sign_signed30(&m.modulus); for (vartime = 0; vartime < 2; ++vartime) { /* compute inverse */ (vartime ? secp256k1_modinv32_var : secp256k1_modinv32)(&x, &m); @@ -956,7 +1071,7 @@ void test_modinv32_uint16(uint16_t* out, const uint16_t* in, const uint16_t* mod #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) { +static void uint16_to_signed62(secp256k1_modinv64_signed62* out, const uint16_t* in) { int i; memset(out->v, 0, sizeof(out->v)); for (i = 0; i < 256; ++i) { @@ -965,7 +1080,7 @@ void uint16_to_signed62(secp256k1_modinv64_signed62* out, const uint16_t* in) { } /* 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) { +static void signed62_to_uint16(uint16_t* out, const secp256k1_modinv64_signed62* in) { int i; memset(out, 0, 32); for (i = 0; i < 256; ++i) { @@ -974,7 +1089,7 @@ void signed62_to_uint16(uint16_t* out, const secp256k1_modinv64_signed62* in) { } /* Randomly mutate the sign of limbs in signed62 representation, without changing the value. */ -void mutate_sign_signed62(secp256k1_modinv64_signed62* x) { +static 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) { @@ -990,7 +1105,7 @@ void mutate_sign_signed62(secp256k1_modinv64_signed62* x) { } /* 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 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; @@ -1000,12 +1115,32 @@ void test_modinv64_uint16(uint16_t* out, const uint16_t* in, const uint16_t* mod 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); + /* Test secp256k1_jacobi64_maybe_var. */ + if (nonzero) { + int jac; + uint16_t sqr[16], negone[16]; + mulmod256(sqr, in, in, mod); + uint16_to_signed62(&x, sqr); + /* Compute jacobi symbol of in^2, which must be 1 (or uncomputable). */ + jac = secp256k1_jacobi64_maybe_var(&x, &m); + CHECK(jac == 0 || jac == 1); + /* Then compute the jacobi symbol of -(in^2). x and -x have opposite + * jacobi symbols if and only if (mod % 4) == 3. */ + negone[0] = mod[0] - 1; + for (i = 1; i < 16; ++i) negone[i] = mod[i]; + mulmod256(sqr, sqr, negone, mod); + uint16_to_signed62(&x, sqr); + jac = secp256k1_jacobi64_maybe_var(&x, &m); + CHECK(jac == 0 || jac == 1 - (mod[0] & 2)); + } + + uint16_to_signed62(&x, in); + mutate_sign_signed62(&m.modulus); for (vartime = 0; vartime < 2; ++vartime) { /* compute inverse */ (vartime ? secp256k1_modinv64_var : secp256k1_modinv64)(&x, &m); @@ -1029,7 +1164,7 @@ void test_modinv64_uint16(uint16_t* out, const uint16_t* in, const uint16_t* mod #endif /* test if a and b are coprime */ -int coprime(const uint16_t* a, const uint16_t* b) { +static int coprime(const uint16_t* a, const uint16_t* b) { uint16_t x[16], y[16], t[16]; int i; int iszero; @@ -1059,7 +1194,7 @@ int coprime(const uint16_t* a, const uint16_t* b) { return 1; } -void run_modinv_tests(void) { +static 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 */ @@ -1661,7 +1796,7 @@ void run_modinv_tests(void) { #endif } - for (i = 0; i < 100 * count; ++i) { + 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 */ @@ -1686,7 +1821,7 @@ void run_modinv_tests(void) { #endif /* In a few cases, also test with input=0 */ - if (i < count) { + if (i < COUNT) { test_modinv32_uint16(id, ZERO, md); #ifdef SECP256K1_WIDEMUL_INT128 test_modinv64_uint16(id, ZERO, md); @@ -1699,7 +1834,7 @@ void run_modinv_tests(void) { #ifdef SECP256K1_WIDEMUL_INT128 /* Add two 256-bit numbers (represented as 16 uint16_t's in LE order) together mod 2^256. */ -void add256(uint16_t* out, const uint16_t* a, const uint16_t* b) { +static void add256(uint16_t* out, const uint16_t* a, const uint16_t* b) { int i; uint32_t carry = 0; for (i = 0; i < 16; ++i) { @@ -1711,7 +1846,7 @@ void add256(uint16_t* out, const uint16_t* a, const uint16_t* b) { } /* Negate a 256-bit number (represented as 16 uint16_t's in LE order) mod 2^256. */ -void neg256(uint16_t* out, const uint16_t* a) { +static void neg256(uint16_t* out, const uint16_t* a) { int i; uint32_t carry = 1; for (i = 0; i < 16; ++i) { @@ -1722,7 +1857,7 @@ void neg256(uint16_t* out, const uint16_t* a) { } /* Right-shift a 256-bit number (represented as 16 uint16_t's in LE order). */ -void rshift256(uint16_t* out, const uint16_t* a, int n, int sign_extend) { +static void rshift256(uint16_t* out, const uint16_t* a, int n, int sign_extend) { uint16_t sign = sign_extend && (a[15] >> 15); int i, j; for (i = 15; i >= 0; --i) { @@ -1740,7 +1875,7 @@ void rshift256(uint16_t* out, const uint16_t* a, int n, int sign_extend) { } /* Load a 64-bit unsigned integer into an array of 16 uint16_t's in LE order representing a 256-bit value. */ -void load256u64(uint16_t* out, uint64_t v, int is_signed) { +static void load256u64(uint16_t* out, uint64_t v, int is_signed) { int i; uint64_t sign = is_signed && (v >> 63) ? UINT64_MAX : 0; for (i = 0; i < 4; ++i) { @@ -1752,7 +1887,7 @@ void load256u64(uint16_t* out, uint64_t v, int is_signed) { } /* Load a 128-bit unsigned integer into an array of 16 uint16_t's in LE order representing a 256-bit value. */ -void load256two64(uint16_t* out, uint64_t hi, uint64_t lo, int is_signed) { +static void load256two64(uint16_t* out, uint64_t hi, uint64_t lo, int is_signed) { int i; uint64_t sign = is_signed && (hi >> 63) ? UINT64_MAX : 0; for (i = 0; i < 4; ++i) { @@ -1767,7 +1902,7 @@ void load256two64(uint16_t* out, uint64_t hi, uint64_t lo, int is_signed) { } /* Check whether the 256-bit value represented by array of 16-bit values is in range -2^127 < v < 2^127. */ -int int256is127(const uint16_t* v) { +static int int256is127(const uint16_t* v) { int all_0 = ((v[7] & 0x8000) == 0), all_1 = ((v[7] & 0x8000) == 0x8000); int i; for (i = 8; i < 16; ++i) { @@ -1777,22 +1912,22 @@ int int256is127(const uint16_t* v) { return all_0 || all_1; } -void load256u128(uint16_t* out, const secp256k1_uint128* v) { +static void load256u128(uint16_t* out, const secp256k1_uint128* v) { uint64_t lo = secp256k1_u128_to_u64(v), hi = secp256k1_u128_hi_u64(v); load256two64(out, hi, lo, 0); } -void load256i128(uint16_t* out, const secp256k1_int128* v) { +static void load256i128(uint16_t* out, const secp256k1_int128* v) { uint64_t lo; int64_t hi; secp256k1_int128 c = *v; - lo = secp256k1_i128_to_i64(&c); + lo = secp256k1_i128_to_u64(&c); secp256k1_i128_rshift(&c, 64); hi = secp256k1_i128_to_i64(&c); load256two64(out, hi, lo, 1); } -void run_int128_test_case(void) { +static void run_int128_test_case(void) { unsigned char buf[32]; uint64_t v[4]; secp256k1_int128 swa, swz; @@ -1903,12 +2038,14 @@ void run_int128_test_case(void) { secp256k1_i128_rshift(&swz, uc % 127); load256i128(rswz, &swz); CHECK(secp256k1_memcmp_var(rswr, rswz, 16) == 0); - /* test secp256k1_i128_to_i64 */ - CHECK((uint64_t)secp256k1_i128_to_i64(&swa) == v[0]); + /* test secp256k1_i128_to_u64 */ + CHECK(secp256k1_i128_to_u64(&swa) == v[0]); /* test secp256k1_i128_from_i64 */ secp256k1_i128_from_i64(&swz, sb); load256i128(rswz, &swz); CHECK(secp256k1_memcmp_var(rsb, rswz, 16) == 0); + /* test secp256k1_i128_to_i64 */ + CHECK(secp256k1_i128_to_i64(&swz) == sb); /* test secp256k1_i128_eq_var */ { int expect = (uc & 1); @@ -1925,34 +2062,60 @@ void run_int128_test_case(void) { } CHECK(secp256k1_i128_eq_var(&swa, &swz) == expect); } - /* test secp256k1_i128_check_pow2 */ + /* test secp256k1_i128_check_pow2 (sign == 1) */ { int expect = (uc & 1); int pos = ub % 127; if (expect) { - /* If expect==1, set swz to exactly (2 << pos). */ + /* If expect==1, set swz to exactly 2^pos. */ uint64_t hi = 0; uint64_t lo = 0; - if (pos & 64) { + if (pos >= 64) { hi = (((uint64_t)1) << (pos & 63)); } else { lo = (((uint64_t)1) << (pos & 63)); } secp256k1_i128_load(&swz, hi, lo); } else { - /* If expect==0, set swz = swa, but update expect=1 if swa happens to equal (2 << pos). */ - if (pos & 64) { + /* If expect==0, set swz = swa, but update expect=1 if swa happens to equal 2^pos. */ + if (pos >= 64) { if ((v[1] == (((uint64_t)1) << (pos & 63))) && v[0] == 0) expect = 1; } else { if ((v[0] == (((uint64_t)1) << (pos & 63))) && v[1] == 0) expect = 1; } swz = swa; } - CHECK(secp256k1_i128_check_pow2(&swz, pos) == expect); + CHECK(secp256k1_i128_check_pow2(&swz, pos, 1) == expect); + } + /* test secp256k1_i128_check_pow2 (sign == -1) */ + { + int expect = (uc & 1); + int pos = ub % 127; + if (expect) { + /* If expect==1, set swz to exactly -2^pos. */ + uint64_t hi = ~(uint64_t)0; + uint64_t lo = ~(uint64_t)0; + if (pos >= 64) { + hi <<= (pos & 63); + lo = 0; + } else { + lo <<= (pos & 63); + } + secp256k1_i128_load(&swz, hi, lo); + } else { + /* If expect==0, set swz = swa, but update expect=1 if swa happens to equal -2^pos. */ + if (pos >= 64) { + if ((v[1] == ((~(uint64_t)0) << (pos & 63))) && v[0] == 0) expect = 1; + } else { + if ((v[0] == ((~(uint64_t)0) << (pos & 63))) && v[1] == ~(uint64_t)0) expect = 1; + } + swz = swa; + } + CHECK(secp256k1_i128_check_pow2(&swz, pos, -1) == expect); } } -void run_int128_tests(void) { +static void run_int128_tests(void) { { /* secp256k1_u128_accum_mul */ secp256k1_uint128 res; @@ -1968,34 +2131,34 @@ void run_int128_tests(void) { /* Compute INT128_MAX = 2^127 - 1 with secp256k1_i128_accum_mul */ secp256k1_i128_mul(&res, INT64_MAX, INT64_MAX); secp256k1_i128_accum_mul(&res, INT64_MAX, INT64_MAX); - CHECK(secp256k1_i128_to_i64(&res) == 2); + CHECK(secp256k1_i128_to_u64(&res) == 2); secp256k1_i128_accum_mul(&res, 4, 9223372036854775807); secp256k1_i128_accum_mul(&res, 1, 1); - CHECK((uint64_t)secp256k1_i128_to_i64(&res) == UINT64_MAX); + CHECK(secp256k1_i128_to_u64(&res) == UINT64_MAX); secp256k1_i128_rshift(&res, 64); CHECK(secp256k1_i128_to_i64(&res) == INT64_MAX); /* Compute INT128_MIN = - 2^127 with secp256k1_i128_accum_mul */ secp256k1_i128_mul(&res, INT64_MAX, INT64_MIN); - CHECK(secp256k1_i128_to_i64(&res) == INT64_MIN); + CHECK(secp256k1_i128_to_u64(&res) == (uint64_t)INT64_MIN); secp256k1_i128_accum_mul(&res, INT64_MAX, INT64_MIN); - CHECK(secp256k1_i128_to_i64(&res) == 0); + CHECK(secp256k1_i128_to_u64(&res) == 0); secp256k1_i128_accum_mul(&res, 2, INT64_MIN); - CHECK(secp256k1_i128_to_i64(&res) == 0); + CHECK(secp256k1_i128_to_u64(&res) == 0); secp256k1_i128_rshift(&res, 64); CHECK(secp256k1_i128_to_i64(&res) == INT64_MIN); } { /* Randomized tests. */ int i; - for (i = 0; i < 256 * count; ++i) run_int128_test_case(); + for (i = 0; i < 256 * COUNT; ++i) run_int128_test_case(); } } #endif /***** SCALAR TESTS *****/ -void scalar_test(void) { +static void scalar_test(void) { secp256k1_scalar s; secp256k1_scalar s1; secp256k1_scalar s2; @@ -2160,7 +2323,7 @@ void scalar_test(void) { } -void run_scalar_set_b32_seckey_tests(void) { +static void run_scalar_set_b32_seckey_tests(void) { unsigned char b32[32]; secp256k1_scalar s1; secp256k1_scalar s2; @@ -2177,12 +2340,12 @@ void run_scalar_set_b32_seckey_tests(void) { CHECK(secp256k1_scalar_set_b32_seckey(&s2, b32) == 0); } -void run_scalar_tests(void) { +static void run_scalar_tests(void) { int i; - for (i = 0; i < 128 * count; i++) { + for (i = 0; i < 128 * COUNT; i++) { scalar_test(); } - for (i = 0; i < count; i++) { + for (i = 0; i < COUNT; i++) { run_scalar_set_b32_seckey_tests(); } @@ -2785,7 +2948,7 @@ void run_scalar_tests(void) { /***** FIELD TESTS *****/ -void random_fe(secp256k1_fe *x) { +static void random_fe(secp256k1_fe *x) { unsigned char bin[32]; do { secp256k1_testrand256(bin); @@ -2795,7 +2958,7 @@ void random_fe(secp256k1_fe *x) { } while(1); } -void random_fe_test(secp256k1_fe *x) { +static void random_fe_test(secp256k1_fe *x) { unsigned char bin[32]; do { secp256k1_testrand256_test(bin); @@ -2805,7 +2968,7 @@ void random_fe_test(secp256k1_fe *x) { } while(1); } -void random_fe_non_zero(secp256k1_fe *nz) { +static void random_fe_non_zero(secp256k1_fe *nz) { int tries = 10; while (--tries >= 0) { random_fe(nz); @@ -2818,7 +2981,7 @@ void random_fe_non_zero(secp256k1_fe *nz) { CHECK(tries >= 0); } -void random_fe_non_square(secp256k1_fe *ns) { +static void random_fe_non_square(secp256k1_fe *ns) { secp256k1_fe r; random_fe_non_zero(ns); if (secp256k1_fe_sqrt(&r, ns)) { @@ -2826,7 +2989,7 @@ void random_fe_non_square(secp256k1_fe *ns) { } } -int check_fe_equal(const secp256k1_fe *a, const secp256k1_fe *b) { +static int check_fe_equal(const secp256k1_fe *a, const secp256k1_fe *b) { secp256k1_fe an = *a; secp256k1_fe bn = *b; secp256k1_fe_normalize_weak(&an); @@ -2834,7 +2997,7 @@ int check_fe_equal(const secp256k1_fe *a, const secp256k1_fe *b) { return secp256k1_fe_equal_var(&an, &bn); } -void run_field_convert(void) { +static void run_field_convert(void) { static const unsigned char b32[32] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, @@ -2865,7 +3028,7 @@ void run_field_convert(void) { } /* Returns true if two field elements have the same representation. */ -int fe_identical(const secp256k1_fe *a, const secp256k1_fe *b) { +static int fe_identical(const secp256k1_fe *a, const secp256k1_fe *b) { int ret = 1; #ifdef VERIFY ret &= (a->magnitude == b->magnitude); @@ -2876,7 +3039,7 @@ int fe_identical(const secp256k1_fe *a, const secp256k1_fe *b) { return ret; } -void run_field_half(void) { +static void run_field_half(void) { secp256k1_fe t, u; int m; @@ -2925,14 +3088,15 @@ void run_field_half(void) { } } -void run_field_misc(void) { +static void run_field_misc(void) { secp256k1_fe x; secp256k1_fe y; secp256k1_fe z; secp256k1_fe q; + int v; secp256k1_fe fe5 = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 5); int i, j; - for (i = 0; i < 1000 * count; i++) { + for (i = 0; i < 1000 * COUNT; i++) { secp256k1_fe_storage xs, ys, zs; if (i & 1) { random_fe(&x); @@ -2940,6 +3104,14 @@ void run_field_misc(void) { random_fe_test(&x); } random_fe_non_zero(&y); + v = secp256k1_testrand_bits(15); + /* Test that fe_add_int is equivalent to fe_set_int + fe_add. */ + secp256k1_fe_set_int(&q, v); /* q = v */ + z = x; /* z = x */ + secp256k1_fe_add(&z, &q); /* z = x+v */ + q = x; /* q = x */ + secp256k1_fe_add_int(&q, v); /* q = x+v */ + CHECK(check_fe_equal(&q, &z)); /* Test the fe equality and comparison operations. */ CHECK(secp256k1_fe_cmp_var(&x, &x) == 0); CHECK(secp256k1_fe_equal_var(&x, &x)); @@ -3017,7 +3189,7 @@ void run_field_misc(void) { } } -void test_fe_mul(const secp256k1_fe* a, const secp256k1_fe* b, int use_sqr) +static void test_fe_mul(const secp256k1_fe* a, const secp256k1_fe* b, int use_sqr) { secp256k1_fe c, an, bn; /* Variables in BE 32-byte format. */ @@ -3060,9 +3232,9 @@ void test_fe_mul(const secp256k1_fe* a, const secp256k1_fe* b, int use_sqr) CHECK(secp256k1_memcmp_var(t16, c16, 32) == 0); } -void run_fe_mul(void) { +static void run_fe_mul(void) { int i; - for (i = 0; i < 100 * count; ++i) { + for (i = 0; i < 100 * COUNT; ++i) { secp256k1_fe a, b, c, d; random_fe(&a); random_field_element_magnitude(&a); @@ -3081,7 +3253,7 @@ void run_fe_mul(void) { } } -void run_sqr(void) { +static void run_sqr(void) { secp256k1_fe x, s; { @@ -3097,7 +3269,7 @@ void run_sqr(void) { } } -void test_sqrt(const secp256k1_fe *a, const secp256k1_fe *k) { +static void test_sqrt(const secp256k1_fe *a, const secp256k1_fe *k) { secp256k1_fe r1, r2; int v = secp256k1_fe_sqrt(&r1, a); CHECK((v == 0) == (k == NULL)); @@ -3111,7 +3283,7 @@ void test_sqrt(const secp256k1_fe *a, const secp256k1_fe *k) { } } -void run_sqrt(void) { +static void run_sqrt(void) { secp256k1_fe ns, x, s, t; int i; @@ -3133,11 +3305,13 @@ void run_sqrt(void) { for (i = 0; i < 10; i++) { int j; random_fe_non_square(&ns); - for (j = 0; j < count; j++) { + for (j = 0; j < COUNT; j++) { random_fe(&x); secp256k1_fe_sqr(&s, &x); + CHECK(secp256k1_fe_is_square_var(&s)); test_sqrt(&s, &x); secp256k1_fe_negate(&t, &s, 1); + CHECK(!secp256k1_fe_is_square_var(&t)); test_sqrt(&t, NULL); secp256k1_fe_mul(&t, &s, &ns); test_sqrt(&t, NULL); @@ -3164,7 +3338,7 @@ static const secp256k1_fe fe_minus_one = SECP256K1_FE_CONST( * 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) +static void test_inverse_scalar(secp256k1_scalar* out, const secp256k1_scalar* x, int var) { secp256k1_scalar l, r, t; @@ -3186,7 +3360,7 @@ void test_inverse_scalar(secp256k1_scalar* out, const secp256k1_scalar* x, int v CHECK(secp256k1_scalar_is_zero(&l)); /* l == 0 */ } -void test_inverse_field(secp256k1_fe* out, const secp256k1_fe* x, int var) +static void test_inverse_field(secp256k1_fe* out, const secp256k1_fe* x, int var) { secp256k1_fe l, r, t; @@ -3206,12 +3380,12 @@ void test_inverse_field(secp256k1_fe* out, const secp256k1_fe* x, int var) (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_int(&l, 1); /* 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) +static 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] = { @@ -3445,7 +3619,7 @@ void run_inverse_tests(void) } /* 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) { + 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); @@ -3459,7 +3633,7 @@ void run_inverse_tests(void) /***** GROUP TESTS *****/ -void ge_equals_ge(const secp256k1_ge *a, const secp256k1_ge *b) { +static void ge_equals_ge(const secp256k1_ge *a, const secp256k1_ge *b) { CHECK(a->infinity == b->infinity); if (a->infinity) { return; @@ -3469,7 +3643,7 @@ void ge_equals_ge(const secp256k1_ge *a, const secp256k1_ge *b) { } /* This compares jacobian points including their Z, not just their geometric meaning. */ -int gej_xyz_equals_gej(const secp256k1_gej *a, const secp256k1_gej *b) { +static int gej_xyz_equals_gej(const secp256k1_gej *a, const secp256k1_gej *b) { secp256k1_gej a2; secp256k1_gej b2; int ret = 1; @@ -3490,7 +3664,7 @@ int gej_xyz_equals_gej(const secp256k1_gej *a, const secp256k1_gej *b) { return ret; } -void ge_equals_gej(const secp256k1_ge *a, const secp256k1_gej *b) { +static void ge_equals_gej(const secp256k1_ge *a, const secp256k1_gej *b) { secp256k1_fe z2s; secp256k1_fe u1, u2, s1, s2; CHECK(a->infinity == b->infinity); @@ -3507,7 +3681,7 @@ void ge_equals_gej(const secp256k1_ge *a, const secp256k1_gej *b) { CHECK(secp256k1_fe_equal_var(&s1, &s2)); } -void test_ge(void) { +static void test_ge(void) { int i, i1; int runs = 6; /* 25 points are used: @@ -3516,8 +3690,8 @@ void test_ge(void) { * negation, and then those two again but with randomized Z coordinate. * - The same is then done for lambda*p1 and lambda^2*p1. */ - 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_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 zf; secp256k1_fe zfi2, zfi3; @@ -3640,7 +3814,7 @@ void test_ge(void) { /* Test adding all points together in random order equals infinity. */ { secp256k1_gej sum = SECP256K1_GEJ_CONST_INFINITY; - secp256k1_gej *gej_shuffled = (secp256k1_gej *)checked_malloc(&ctx->error_callback, (4 * runs + 1) * sizeof(secp256k1_gej)); + secp256k1_gej *gej_shuffled = (secp256k1_gej *)checked_malloc(&CTX->error_callback, (4 * runs + 1) * sizeof(secp256k1_gej)); for (i = 0; i < 4 * runs + 1; i++) { gej_shuffled[i] = gej[i]; } @@ -3661,7 +3835,7 @@ void test_ge(void) { /* Test batch gej -> ge conversion without known z ratios. */ { - secp256k1_ge *ge_set_all = (secp256k1_ge *)checked_malloc(&ctx->error_callback, (4 * runs + 1) * sizeof(secp256k1_ge)); + secp256k1_ge *ge_set_all = (secp256k1_ge *)checked_malloc(&CTX->error_callback, (4 * runs + 1) * sizeof(secp256k1_ge)); secp256k1_ge_set_all_gej_var(ge_set_all, gej, 4 * runs + 1); for (i = 0; i < 4 * runs + 1; i++) { secp256k1_fe s; @@ -3706,8 +3880,7 @@ void test_ge(void) { free(gej); } - -void test_intialized_inf(void) { +static void test_intialized_inf(void) { secp256k1_ge p; secp256k1_gej pj, npj, infj1, infj2, infj3; secp256k1_fe zinv; @@ -3739,7 +3912,7 @@ void test_intialized_inf(void) { } -void test_add_neg_y_diff_x(void) { +static void test_add_neg_y_diff_x(void) { /* The point of this test is to check that we can add two points * whose y-coordinates are negatives of each other but whose x * coordinates differ. If the x-coordinates were the same, these @@ -3806,16 +3979,16 @@ void test_add_neg_y_diff_x(void) { ge_equals_gej(&res, &sumj); } -void run_ge(void) { +static void run_ge(void) { int i; - for (i = 0; i < count * 32; i++) { + for (i = 0; i < COUNT * 32; i++) { test_ge(); } test_add_neg_y_diff_x(); test_intialized_inf(); } -void test_gej_cmov(const secp256k1_gej *a, const secp256k1_gej *b) { +static void test_gej_cmov(const secp256k1_gej *a, const secp256k1_gej *b) { secp256k1_gej t = *a; secp256k1_gej_cmov(&t, b, 0); CHECK(gej_xyz_equals_gej(&t, a)); @@ -3823,12 +3996,12 @@ void test_gej_cmov(const secp256k1_gej *a, const secp256k1_gej *b) { CHECK(gej_xyz_equals_gej(&t, b)); } -void run_gej(void) { +static void run_gej(void) { int i; secp256k1_gej a, b; /* Tests for secp256k1_gej_cmov */ - for (i = 0; i < count; i++) { + for (i = 0; i < COUNT; i++) { secp256k1_gej_set_infinity(&a); secp256k1_gej_set_infinity(&b); test_gej_cmov(&a, &b); @@ -3846,7 +4019,7 @@ void run_gej(void) { } /* Tests for secp256k1_gej_eq_var */ - for (i = 0; i < count; i++) { + for (i = 0; i < COUNT; i++) { secp256k1_fe fe; random_gej_test(&a); random_gej_test(&b); @@ -3862,7 +4035,7 @@ void run_gej(void) { } } -void test_ec_combine(void) { +static void test_ec_combine(void) { secp256k1_scalar sum = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0); secp256k1_pubkey data[6]; const secp256k1_pubkey* d[6]; @@ -3875,26 +4048,26 @@ void test_ec_combine(void) { secp256k1_scalar s; random_scalar_order_test(&s); secp256k1_scalar_add(&sum, &sum, &s); - secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &Qj, &s); + secp256k1_ecmult_gen(&CTX->ecmult_gen_ctx, &Qj, &s); secp256k1_ge_set_gej(&Q, &Qj); secp256k1_pubkey_save(&data[i - 1], &Q); d[i - 1] = &data[i - 1]; - secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &Qj, &sum); + secp256k1_ecmult_gen(&CTX->ecmult_gen_ctx, &Qj, &sum); secp256k1_ge_set_gej(&Q, &Qj); secp256k1_pubkey_save(&sd, &Q); - CHECK(secp256k1_ec_pubkey_combine(ctx, &sd2, d, i) == 1); + CHECK(secp256k1_ec_pubkey_combine(CTX, &sd2, d, i) == 1); CHECK(secp256k1_memcmp_var(&sd, &sd2, sizeof(sd)) == 0); } } -void run_ec_combine(void) { +static void run_ec_combine(void) { int i; - for (i = 0; i < count * 8; i++) { + for (i = 0; i < COUNT * 8; i++) { test_ec_combine(); } } -void test_group_decompress(const secp256k1_fe* x) { +static void test_group_decompress(const secp256k1_fe* x) { /* The input itself, normalized. */ secp256k1_fe fex = *x; /* Results of set_xo_var(..., 0), set_xo_var(..., 1). */ @@ -3929,9 +4102,9 @@ void test_group_decompress(const secp256k1_fe* x) { } } -void run_group_decompress(void) { +static void run_group_decompress(void) { int i; - for (i = 0; i < count * 4; i++) { + for (i = 0; i < COUNT * 4; i++) { secp256k1_fe fe; random_fe_test(&fe); test_group_decompress(&fe); @@ -3940,7 +4113,7 @@ void run_group_decompress(void) { /***** ECMULT TESTS *****/ -void test_pre_g_table(const secp256k1_ge_storage * pre_g, size_t n) { +static void test_pre_g_table(const secp256k1_ge_storage * pre_g, size_t n) { /* Tests the pre_g / pre_g_128 tables for consistency. * For independent verification we take a "geometric" approach to verification. * We check that every entry is on-curve. @@ -3990,7 +4163,7 @@ void test_pre_g_table(const secp256k1_ge_storage * pre_g, size_t n) { } } -void run_ecmult_pre_g(void) { +static void run_ecmult_pre_g(void) { secp256k1_ge_storage gs; secp256k1_gej gj; secp256k1_ge g; @@ -4014,7 +4187,7 @@ void run_ecmult_pre_g(void) { CHECK(secp256k1_memcmp_var(&gs, &secp256k1_pre_g_128[0], sizeof(gs)) == 0); } -void run_ecmult_chain(void) { +static void run_ecmult_chain(void) { /* random starting point A (on the curve) */ secp256k1_gej a = SECP256K1_GEJ_CONST( 0x8b30bbe9, 0xae2a9906, 0x96b22f67, 0x0709dff3, @@ -4044,7 +4217,7 @@ void run_ecmult_chain(void) { /* the point being computed */ x = a; - for (i = 0; i < 200*count; i++) { + for (i = 0; i < 200*COUNT; i++) { /* in each iteration, compute X = xn*X + gn*G; */ secp256k1_ecmult(&x, &x, &xn, &gn); /* also compute ae and ge: the actual accumulated factors for A and G */ @@ -4073,7 +4246,7 @@ void run_ecmult_chain(void) { CHECK(secp256k1_gej_eq_var(&x, &x2)); } -void test_point_times_order(const secp256k1_gej *point) { +static void test_point_times_order(const secp256k1_gej *point) { /* X * (point + G) + (order-X) * (pointer + G) = 0 */ secp256k1_scalar x; secp256k1_scalar nx; @@ -4137,7 +4310,7 @@ static const secp256k1_scalar scalars_near_split_bounds[20] = { SECP256K1_SCALAR_CONST(0x26c75a99, 0x80b861c1, 0x4a4c3805, 0x1024c8b4, 0x704d760e, 0xe95e7cd3, 0xde1bfdb1, 0xce2c5a45) }; -void test_ecmult_target(const secp256k1_scalar* target, int mode) { +static void test_ecmult_target(const secp256k1_scalar* target, int mode) { /* Mode: 0=ecmult_gen, 1=ecmult, 2=ecmult_const */ secp256k1_scalar n1, n2; secp256k1_ge p; @@ -4157,9 +4330,9 @@ void test_ecmult_target(const secp256k1_scalar* target, int mode) { /* EC multiplications */ if (mode == 0) { - secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &p1j, &n1); - secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &p2j, &n2); - secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &ptj, target); + secp256k1_ecmult_gen(&CTX->ecmult_gen_ctx, &p1j, &n1); + secp256k1_ecmult_gen(&CTX->ecmult_gen_ctx, &p2j, &n2); + secp256k1_ecmult_gen(&CTX->ecmult_gen_ctx, &ptj, target); } else if (mode == 1) { secp256k1_ecmult(&p1j, &pj, &n1, &zero); secp256k1_ecmult(&p2j, &pj, &n2, &zero); @@ -4176,10 +4349,10 @@ void test_ecmult_target(const secp256k1_scalar* target, int mode) { CHECK(secp256k1_gej_is_infinity(&ptj)); } -void run_ecmult_near_split_bound(void) { +static void run_ecmult_near_split_bound(void) { int i; unsigned j; - for (i = 0; i < 4*count; ++i) { + for (i = 0; i < 4*COUNT; ++i) { for (j = 0; j < sizeof(scalars_near_split_bounds) / sizeof(scalars_near_split_bounds[0]); ++j) { test_ecmult_target(&scalars_near_split_bounds[j], 0); test_ecmult_target(&scalars_near_split_bounds[j], 1); @@ -4188,7 +4361,7 @@ void run_ecmult_near_split_bound(void) { } } -void run_point_times_order(void) { +static void run_point_times_order(void) { int i; secp256k1_fe x = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 2); static const secp256k1_fe xr = SECP256K1_FE_CONST( @@ -4209,7 +4382,7 @@ void run_point_times_order(void) { CHECK(secp256k1_fe_equal_var(&x, &xr)); } -void ecmult_const_random_mult(void) { +static void ecmult_const_random_mult(void) { /* random starting point A (on the curve) */ secp256k1_ge a = SECP256K1_GE_CONST( 0x6d986544, 0x57ff52b8, 0xcf1b8126, 0x5b802a5b, @@ -4236,7 +4409,7 @@ void ecmult_const_random_mult(void) { ge_equals_gej(&expected_b, &b); } -void ecmult_const_commutativity(void) { +static void ecmult_const_commutativity(void) { secp256k1_scalar a; secp256k1_scalar b; secp256k1_gej res1; @@ -4257,7 +4430,7 @@ void ecmult_const_commutativity(void) { ge_equals_ge(&mid1, &mid2); } -void ecmult_const_mult_zero_one(void) { +static void ecmult_const_mult_zero_one(void) { secp256k1_scalar zero = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0); secp256k1_scalar one = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 1); secp256k1_scalar negone; @@ -4279,7 +4452,7 @@ void ecmult_const_mult_zero_one(void) { ge_equals_ge(&res2, &point); } -void ecmult_const_chain_multiply(void) { +static void ecmult_const_chain_multiply(void) { /* Check known result (randomly generated test problem from sage) */ const secp256k1_scalar scalar = SECP256K1_SCALAR_CONST( 0x4968d524, 0x2abf9b7a, 0x466abbcf, 0x34b11b6d, @@ -4305,7 +4478,7 @@ void ecmult_const_chain_multiply(void) { ge_equals_gej(&res, &expected_point); } -void run_ecmult_const_tests(void) { +static void run_ecmult_const_tests(void) { ecmult_const_mult_zero_one(); ecmult_const_random_mult(); ecmult_const_commutativity(); @@ -4332,7 +4505,7 @@ static int ecmult_multi_false_callback(secp256k1_scalar *sc, secp256k1_ge *pt, s return 0; } -void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func ecmult_multi) { +static void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func ecmult_multi) { int ncount; secp256k1_scalar szero; secp256k1_scalar sc[32]; @@ -4346,10 +4519,10 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e secp256k1_scalar_set_int(&szero, 0); /* No points to multiply */ - CHECK(ecmult_multi(&ctx->error_callback, scratch, &r, NULL, ecmult_multi_callback, &data, 0)); + CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, NULL, ecmult_multi_callback, &data, 0)); /* Check 1- and 2-point multiplies against ecmult */ - for (ncount = 0; ncount < count; ncount++) { + for (ncount = 0; ncount < COUNT; ncount++) { secp256k1_ge ptg; secp256k1_gej ptgj; random_scalar_order(&sc[0]); @@ -4362,30 +4535,30 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e /* only G scalar */ secp256k1_ecmult(&r2, &ptgj, &szero, &sc[0]); - CHECK(ecmult_multi(&ctx->error_callback, scratch, &r, &sc[0], ecmult_multi_callback, &data, 0)); + CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &sc[0], ecmult_multi_callback, &data, 0)); CHECK(secp256k1_gej_eq_var(&r, &r2)); /* 1-point */ secp256k1_ecmult(&r2, &ptgj, &sc[0], &szero); - CHECK(ecmult_multi(&ctx->error_callback, scratch, &r, &szero, ecmult_multi_callback, &data, 1)); + CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &szero, ecmult_multi_callback, &data, 1)); CHECK(secp256k1_gej_eq_var(&r, &r2)); /* Try to multiply 1 point, but callback returns false */ - CHECK(!ecmult_multi(&ctx->error_callback, scratch, &r, &szero, ecmult_multi_false_callback, &data, 1)); + CHECK(!ecmult_multi(&CTX->error_callback, scratch, &r, &szero, ecmult_multi_false_callback, &data, 1)); /* 2-point */ secp256k1_ecmult(&r2, &ptgj, &sc[0], &sc[1]); - CHECK(ecmult_multi(&ctx->error_callback, scratch, &r, &szero, ecmult_multi_callback, &data, 2)); + CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &szero, ecmult_multi_callback, &data, 2)); CHECK(secp256k1_gej_eq_var(&r, &r2)); /* 2-point with G scalar */ secp256k1_ecmult(&r2, &ptgj, &sc[0], &sc[1]); - CHECK(ecmult_multi(&ctx->error_callback, scratch, &r, &sc[1], ecmult_multi_callback, &data, 1)); + CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &sc[1], ecmult_multi_callback, &data, 1)); CHECK(secp256k1_gej_eq_var(&r, &r2)); } /* Check infinite outputs of various forms */ - for (ncount = 0; ncount < count; ncount++) { + for (ncount = 0; ncount < COUNT; ncount++) { secp256k1_ge ptg; size_t i, j; size_t sizes[] = { 2, 10, 32 }; @@ -4395,7 +4568,7 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e random_scalar_order(&sc[i]); secp256k1_ge_set_infinity(&pt[i]); } - CHECK(ecmult_multi(&ctx->error_callback, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j])); + CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j])); CHECK(secp256k1_gej_is_infinity(&r)); } @@ -4405,7 +4578,7 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e pt[i] = ptg; secp256k1_scalar_set_int(&sc[i], 0); } - CHECK(ecmult_multi(&ctx->error_callback, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j])); + CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j])); CHECK(secp256k1_gej_is_infinity(&r)); } @@ -4418,7 +4591,7 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e pt[2 * i + 1] = ptg; } - CHECK(ecmult_multi(&ctx->error_callback, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j])); + CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j])); CHECK(secp256k1_gej_is_infinity(&r)); random_scalar_order(&sc[0]); @@ -4431,7 +4604,7 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e secp256k1_ge_neg(&pt[2*i+1], &pt[2*i]); } - CHECK(ecmult_multi(&ctx->error_callback, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j])); + CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j])); CHECK(secp256k1_gej_is_infinity(&r)); } @@ -4446,12 +4619,12 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e secp256k1_scalar_negate(&sc[i], &sc[i]); } - CHECK(ecmult_multi(&ctx->error_callback, scratch, &r, &szero, ecmult_multi_callback, &data, 32)); + CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &szero, ecmult_multi_callback, &data, 32)); CHECK(secp256k1_gej_is_infinity(&r)); } /* Check random points, constant scalar */ - for (ncount = 0; ncount < count; ncount++) { + for (ncount = 0; ncount < COUNT; ncount++) { size_t i; secp256k1_gej_set_infinity(&r); @@ -4465,12 +4638,12 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e } secp256k1_ecmult(&r2, &r, &sc[0], &szero); - CHECK(ecmult_multi(&ctx->error_callback, scratch, &r, &szero, ecmult_multi_callback, &data, 20)); + CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &szero, ecmult_multi_callback, &data, 20)); CHECK(secp256k1_gej_eq_var(&r, &r2)); } /* Check random scalars, constant point */ - for (ncount = 0; ncount < count; ncount++) { + for (ncount = 0; ncount < COUNT; ncount++) { size_t i; secp256k1_ge ptg; secp256k1_gej p0j; @@ -4486,7 +4659,7 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e secp256k1_gej_set_ge(&p0j, &pt[0]); secp256k1_ecmult(&r2, &p0j, &rs, &szero); - CHECK(ecmult_multi(&ctx->error_callback, scratch, &r, &szero, ecmult_multi_callback, &data, 20)); + CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &szero, ecmult_multi_callback, &data, 20)); CHECK(secp256k1_gej_eq_var(&r, &r2)); } @@ -4497,13 +4670,13 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e } secp256k1_scalar_clear(&sc[0]); - CHECK(ecmult_multi(&ctx->error_callback, scratch, &r, &szero, ecmult_multi_callback, &data, 20)); + CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &szero, ecmult_multi_callback, &data, 20)); secp256k1_scalar_clear(&sc[1]); secp256k1_scalar_clear(&sc[2]); secp256k1_scalar_clear(&sc[3]); secp256k1_scalar_clear(&sc[4]); - CHECK(ecmult_multi(&ctx->error_callback, scratch, &r, &szero, ecmult_multi_callback, &data, 6)); - CHECK(ecmult_multi(&ctx->error_callback, scratch, &r, &szero, ecmult_multi_callback, &data, 5)); + CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &szero, ecmult_multi_callback, &data, 6)); + CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &szero, ecmult_multi_callback, &data, 5)); CHECK(secp256k1_gej_is_infinity(&r)); /* Run through s0*(t0*P) + s1*(t1*P) exhaustively for many small values of s0, s1, t0, t1 */ @@ -4548,7 +4721,7 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e secp256k1_scalar_add(&tmp1, &tmp1, &tmp2); secp256k1_ecmult(&expected, &ptgj, &tmp1, &szero); - CHECK(ecmult_multi(&ctx->error_callback, scratch, &actual, &szero, ecmult_multi_callback, &data, 2)); + CHECK(ecmult_multi(&CTX->error_callback, scratch, &actual, &szero, ecmult_multi_callback, &data, 2)); CHECK(secp256k1_gej_eq_var(&actual, &expected)); } } @@ -4557,7 +4730,7 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e } } -int test_ecmult_multi_random(secp256k1_scratch *scratch) { +static int test_ecmult_multi_random(secp256k1_scratch *scratch) { /* Large random test for ecmult_multi_* functions which exercises: * - Few or many inputs (0 up to 128, roughly exponentially distributed). * - Few or many 0*P or a*INF inputs (roughly uniformly distributed). @@ -4624,7 +4797,7 @@ int test_ecmult_multi_random(secp256k1_scratch *scratch) { secp256k1_scalar_mul(&scalars[filled], &sc_tmp, &g_scalar); secp256k1_scalar_inverse_var(&sc_tmp, &sc_tmp); secp256k1_scalar_negate(&sc_tmp, &sc_tmp); - secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &gejs[filled], &sc_tmp); + secp256k1_ecmult_gen(&CTX->ecmult_gen_ctx, &gejs[filled], &sc_tmp); ++filled; ++mults; } @@ -4716,14 +4889,14 @@ int test_ecmult_multi_random(secp256k1_scratch *scratch) { /* Invoke ecmult_multi code. */ data.sc = scalars; data.pt = ges; - CHECK(ecmult_multi(&ctx->error_callback, scratch, &computed, g_scalar_ptr, ecmult_multi_callback, &data, filled)); + CHECK(ecmult_multi(&CTX->error_callback, scratch, &computed, g_scalar_ptr, ecmult_multi_callback, &data, filled)); mults += num_nonzero + g_nonzero; /* Compare with expected result. */ CHECK(secp256k1_gej_eq_var(&computed, &expected)); return mults; } -void test_ecmult_multi_batch_single(secp256k1_ecmult_multi_func ecmult_multi) { +static void test_ecmult_multi_batch_single(secp256k1_ecmult_multi_func ecmult_multi) { secp256k1_scalar szero; secp256k1_scalar sc; secp256k1_ge pt; @@ -4738,12 +4911,12 @@ void test_ecmult_multi_batch_single(secp256k1_ecmult_multi_func ecmult_multi) { secp256k1_scalar_set_int(&szero, 0); /* Try to multiply 1 point, but scratch space is empty.*/ - scratch_empty = secp256k1_scratch_create(&ctx->error_callback, 0); - CHECK(!ecmult_multi(&ctx->error_callback, scratch_empty, &r, &szero, ecmult_multi_callback, &data, 1)); - secp256k1_scratch_destroy(&ctx->error_callback, scratch_empty); + scratch_empty = secp256k1_scratch_create(&CTX->error_callback, 0); + CHECK(!ecmult_multi(&CTX->error_callback, scratch_empty, &r, &szero, ecmult_multi_callback, &data, 1)); + secp256k1_scratch_destroy(&CTX->error_callback, scratch_empty); } -void test_secp256k1_pippenger_bucket_window_inv(void) { +static void test_secp256k1_pippenger_bucket_window_inv(void) { int i; CHECK(secp256k1_pippenger_bucket_window_inv(0) == 0); @@ -4763,7 +4936,7 @@ void test_secp256k1_pippenger_bucket_window_inv(void) { * Probabilistically test the function returning the maximum number of possible points * for a given scratch space. */ -void test_ecmult_multi_pippenger_max_points(void) { +static void test_ecmult_multi_pippenger_max_points(void) { size_t scratch_size = secp256k1_testrand_bits(8); size_t max_size = secp256k1_pippenger_scratch_size(secp256k1_pippenger_bucket_window_inv(PIPPENGER_MAX_BUCKET_WINDOW-1)+512, 12); secp256k1_scratch *scratch; @@ -4774,29 +4947,29 @@ void test_ecmult_multi_pippenger_max_points(void) { size_t i; size_t total_alloc; size_t checkpoint; - scratch = secp256k1_scratch_create(&ctx->error_callback, scratch_size); + scratch = secp256k1_scratch_create(&CTX->error_callback, scratch_size); CHECK(scratch != NULL); - checkpoint = secp256k1_scratch_checkpoint(&ctx->error_callback, scratch); - n_points_supported = secp256k1_pippenger_max_points(&ctx->error_callback, scratch); + checkpoint = secp256k1_scratch_checkpoint(&CTX->error_callback, scratch); + n_points_supported = secp256k1_pippenger_max_points(&CTX->error_callback, scratch); if (n_points_supported == 0) { - secp256k1_scratch_destroy(&ctx->error_callback, scratch); + secp256k1_scratch_destroy(&CTX->error_callback, scratch); continue; } bucket_window = secp256k1_pippenger_bucket_window(n_points_supported); /* allocate `total_alloc` bytes over `PIPPENGER_SCRATCH_OBJECTS` many allocations */ total_alloc = secp256k1_pippenger_scratch_size(n_points_supported, bucket_window); for (i = 0; i < PIPPENGER_SCRATCH_OBJECTS - 1; i++) { - CHECK(secp256k1_scratch_alloc(&ctx->error_callback, scratch, 1)); + CHECK(secp256k1_scratch_alloc(&CTX->error_callback, scratch, 1)); total_alloc--; } - CHECK(secp256k1_scratch_alloc(&ctx->error_callback, scratch, total_alloc)); - secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, checkpoint); - secp256k1_scratch_destroy(&ctx->error_callback, scratch); + CHECK(secp256k1_scratch_alloc(&CTX->error_callback, scratch, total_alloc)); + secp256k1_scratch_apply_checkpoint(&CTX->error_callback, scratch, checkpoint); + secp256k1_scratch_destroy(&CTX->error_callback, scratch); } CHECK(bucket_window == PIPPENGER_MAX_BUCKET_WINDOW); } -void test_ecmult_multi_batch_size_helper(void) { +static void test_ecmult_multi_batch_size_helper(void) { size_t n_batches, n_batch_points, max_n_batch_points, n; max_n_batch_points = 0; @@ -4844,12 +5017,12 @@ void test_ecmult_multi_batch_size_helper(void) { * Run secp256k1_ecmult_multi_var with num points and a scratch space restricted to * 1 <= i <= num points. */ -void test_ecmult_multi_batching(void) { +static void test_ecmult_multi_batching(void) { static const int n_points = 2*ECMULT_PIPPENGER_THRESHOLD; secp256k1_scalar scG; secp256k1_scalar szero; - secp256k1_scalar *sc = (secp256k1_scalar *)checked_malloc(&ctx->error_callback, sizeof(secp256k1_scalar) * n_points); - secp256k1_ge *pt = (secp256k1_ge *)checked_malloc(&ctx->error_callback, sizeof(secp256k1_ge) * n_points); + secp256k1_scalar *sc = (secp256k1_scalar *)checked_malloc(&CTX->error_callback, sizeof(secp256k1_scalar) * n_points); + secp256k1_ge *pt = (secp256k1_ge *)checked_malloc(&CTX->error_callback, sizeof(secp256k1_ge) * n_points); secp256k1_gej r; secp256k1_gej r2; ecmult_multi_data data; @@ -4878,46 +5051,46 @@ void test_ecmult_multi_batching(void) { /* Test with empty scratch space. It should compute the correct result using * ecmult_mult_simple algorithm which doesn't require a scratch space. */ - scratch = secp256k1_scratch_create(&ctx->error_callback, 0); - CHECK(secp256k1_ecmult_multi_var(&ctx->error_callback, scratch, &r, &scG, ecmult_multi_callback, &data, n_points)); + scratch = secp256k1_scratch_create(&CTX->error_callback, 0); + CHECK(secp256k1_ecmult_multi_var(&CTX->error_callback, scratch, &r, &scG, ecmult_multi_callback, &data, n_points)); secp256k1_gej_add_var(&r, &r, &r2, NULL); CHECK(secp256k1_gej_is_infinity(&r)); - secp256k1_scratch_destroy(&ctx->error_callback, scratch); + secp256k1_scratch_destroy(&CTX->error_callback, scratch); /* Test with space for 1 point in pippenger. That's not enough because * ecmult_multi selects strauss which requires more memory. It should * therefore select the simple algorithm. */ - scratch = secp256k1_scratch_create(&ctx->error_callback, secp256k1_pippenger_scratch_size(1, 1) + PIPPENGER_SCRATCH_OBJECTS*ALIGNMENT); - CHECK(secp256k1_ecmult_multi_var(&ctx->error_callback, scratch, &r, &scG, ecmult_multi_callback, &data, n_points)); + scratch = secp256k1_scratch_create(&CTX->error_callback, secp256k1_pippenger_scratch_size(1, 1) + PIPPENGER_SCRATCH_OBJECTS*ALIGNMENT); + CHECK(secp256k1_ecmult_multi_var(&CTX->error_callback, scratch, &r, &scG, ecmult_multi_callback, &data, n_points)); secp256k1_gej_add_var(&r, &r, &r2, NULL); CHECK(secp256k1_gej_is_infinity(&r)); - secp256k1_scratch_destroy(&ctx->error_callback, scratch); + secp256k1_scratch_destroy(&CTX->error_callback, scratch); for(i = 1; i <= n_points; i++) { if (i > ECMULT_PIPPENGER_THRESHOLD) { int bucket_window = secp256k1_pippenger_bucket_window(i); size_t scratch_size = secp256k1_pippenger_scratch_size(i, bucket_window); - scratch = secp256k1_scratch_create(&ctx->error_callback, scratch_size + PIPPENGER_SCRATCH_OBJECTS*ALIGNMENT); + scratch = secp256k1_scratch_create(&CTX->error_callback, scratch_size + PIPPENGER_SCRATCH_OBJECTS*ALIGNMENT); } else { size_t scratch_size = secp256k1_strauss_scratch_size(i); - scratch = secp256k1_scratch_create(&ctx->error_callback, scratch_size + STRAUSS_SCRATCH_OBJECTS*ALIGNMENT); + scratch = secp256k1_scratch_create(&CTX->error_callback, scratch_size + STRAUSS_SCRATCH_OBJECTS*ALIGNMENT); } - CHECK(secp256k1_ecmult_multi_var(&ctx->error_callback, scratch, &r, &scG, ecmult_multi_callback, &data, n_points)); + CHECK(secp256k1_ecmult_multi_var(&CTX->error_callback, scratch, &r, &scG, ecmult_multi_callback, &data, n_points)); secp256k1_gej_add_var(&r, &r, &r2, NULL); CHECK(secp256k1_gej_is_infinity(&r)); - secp256k1_scratch_destroy(&ctx->error_callback, scratch); + secp256k1_scratch_destroy(&CTX->error_callback, scratch); } free(sc); free(pt); } -void run_ecmult_multi_tests(void) { +static void run_ecmult_multi_tests(void) { secp256k1_scratch *scratch; - int64_t todo = (int64_t)320 * count; + int64_t todo = (int64_t)320 * COUNT; test_secp256k1_pippenger_bucket_window_inv(); test_ecmult_multi_pippenger_max_points(); - scratch = secp256k1_scratch_create(&ctx->error_callback, 819200); + scratch = secp256k1_scratch_create(&CTX->error_callback, 819200); test_ecmult_multi(scratch, secp256k1_ecmult_multi_var); test_ecmult_multi(NULL, secp256k1_ecmult_multi_var); test_ecmult_multi(scratch, secp256k1_ecmult_pippenger_batch_single); @@ -4927,18 +5100,18 @@ void run_ecmult_multi_tests(void) { while (todo > 0) { todo -= test_ecmult_multi_random(scratch); } - secp256k1_scratch_destroy(&ctx->error_callback, scratch); + secp256k1_scratch_destroy(&CTX->error_callback, scratch); /* Run test_ecmult_multi with space for exactly one point */ - scratch = secp256k1_scratch_create(&ctx->error_callback, secp256k1_strauss_scratch_size(1) + STRAUSS_SCRATCH_OBJECTS*ALIGNMENT); + scratch = secp256k1_scratch_create(&CTX->error_callback, secp256k1_strauss_scratch_size(1) + STRAUSS_SCRATCH_OBJECTS*ALIGNMENT); test_ecmult_multi(scratch, secp256k1_ecmult_multi_var); - secp256k1_scratch_destroy(&ctx->error_callback, scratch); + secp256k1_scratch_destroy(&CTX->error_callback, scratch); test_ecmult_multi_batch_size_helper(); test_ecmult_multi_batching(); } -void test_wnaf(const secp256k1_scalar *number, int w) { +static void test_wnaf(const secp256k1_scalar *number, int w) { secp256k1_scalar x, two, t; int wnaf[256]; int zeroes = -1; @@ -4972,7 +5145,7 @@ void test_wnaf(const secp256k1_scalar *number, int w) { CHECK(secp256k1_scalar_eq(&x, number)); /* check that wnaf represents number */ } -void test_constant_wnaf_negate(const secp256k1_scalar *number) { +static void test_constant_wnaf_negate(const secp256k1_scalar *number) { secp256k1_scalar neg1 = *number; secp256k1_scalar neg2 = *number; int sign1 = 1; @@ -4987,7 +5160,7 @@ void test_constant_wnaf_negate(const secp256k1_scalar *number) { CHECK(secp256k1_scalar_eq(&neg1, &neg2)); } -void test_constant_wnaf(const secp256k1_scalar *number, int w) { +static void test_constant_wnaf(const secp256k1_scalar *number, int w) { secp256k1_scalar x, shift; int wnaf[256] = {0}; int i; @@ -5027,7 +5200,7 @@ void test_constant_wnaf(const secp256k1_scalar *number, int w) { CHECK(secp256k1_scalar_eq(&x, &num)); } -void test_fixed_wnaf(const secp256k1_scalar *number, int w) { +static void test_fixed_wnaf(const secp256k1_scalar *number, int w) { secp256k1_scalar x, shift; int wnaf[256] = {0}; int i; @@ -5064,7 +5237,7 @@ void test_fixed_wnaf(const secp256k1_scalar *number, int w) { /* Checks that the first 8 elements of wnaf are equal to wnaf_expected and the * rest is 0.*/ -void test_fixed_wnaf_small_helper(int *wnaf, int *wnaf_expected, int w) { +static void test_fixed_wnaf_small_helper(int *wnaf, int *wnaf_expected, int w) { int i; for (i = WNAF_SIZE(w)-1; i >= 8; --i) { CHECK(wnaf[i] == 0); @@ -5074,7 +5247,7 @@ void test_fixed_wnaf_small_helper(int *wnaf, int *wnaf_expected, int w) { } } -void test_fixed_wnaf_small(void) { +static void test_fixed_wnaf_small(void) { int w = 4; int wnaf[256] = {0}; int i; @@ -5128,7 +5301,7 @@ void test_fixed_wnaf_small(void) { } } -void run_wnaf(void) { +static void run_wnaf(void) { int i; secp256k1_scalar n = {{0}}; @@ -5160,7 +5333,7 @@ void run_wnaf(void) { /* Test 0 for fixed wnaf */ test_fixed_wnaf_small(); /* Random tests */ - for (i = 0; i < count; i++) { + for (i = 0; i < COUNT; i++) { random_scalar_order(&n); test_wnaf(&n, 4+(i%10)); test_constant_wnaf_negate(&n); @@ -5182,7 +5355,7 @@ static int test_ecmult_accumulate_cb(secp256k1_scalar* sc, secp256k1_ge* pt, siz return 1; } -void test_ecmult_accumulate(secp256k1_sha256* acc, const secp256k1_scalar* x, secp256k1_scratch* scratch) { +static void test_ecmult_accumulate(secp256k1_sha256* acc, const secp256k1_scalar* x, secp256k1_scratch* scratch) { /* Compute x*G in 6 different ways, serialize it uncompressed, and feed it into acc. */ secp256k1_gej rj1, rj2, rj3, rj4, rj5, rj6, gj, infj; secp256k1_ge r; @@ -5191,7 +5364,7 @@ void test_ecmult_accumulate(secp256k1_sha256* acc, const secp256k1_scalar* x, se size_t size = 65; secp256k1_gej_set_ge(&gj, &secp256k1_ge_const_g); secp256k1_gej_set_infinity(&infj); - secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj1, x); + secp256k1_ecmult_gen(&CTX->ecmult_gen_ctx, &rj1, x); secp256k1_ecmult(&rj2, &gj, x, &zero); secp256k1_ecmult(&rj3, &infj, &zero, x); secp256k1_ecmult_multi_var(NULL, scratch, &rj4, x, NULL, NULL, 0); @@ -5215,7 +5388,7 @@ void test_ecmult_accumulate(secp256k1_sha256* acc, const secp256k1_scalar* x, se } } -void test_ecmult_constants_2bit(void) { +static void test_ecmult_constants_2bit(void) { /* Using test_ecmult_accumulate, test ecmult for: * - For i in 0..36: * - Key i @@ -5228,7 +5401,7 @@ void test_ecmult_constants_2bit(void) { secp256k1_sha256 acc; unsigned char b32[32]; int i, j; - secp256k1_scratch_space *scratch = secp256k1_scratch_space_create(ctx, 65536); + secp256k1_scratch_space *scratch = secp256k1_scratch_space_create(CTX, 65536); /* Expected hash of all the computed points; created with an independent * implementation. */ @@ -5256,10 +5429,10 @@ void test_ecmult_constants_2bit(void) { secp256k1_sha256_finalize(&acc, b32); CHECK(secp256k1_memcmp_var(b32, expected32, 32) == 0); - secp256k1_scratch_space_destroy(ctx, scratch); + secp256k1_scratch_space_destroy(CTX, scratch); } -void test_ecmult_constants_sha(uint32_t prefix, size_t iter, const unsigned char* expected32) { +static void test_ecmult_constants_sha(uint32_t prefix, size_t iter, const unsigned char* expected32) { /* Using test_ecmult_accumulate, test ecmult for: * - Key 0 * - Key 1 @@ -5272,7 +5445,7 @@ void test_ecmult_constants_sha(uint32_t prefix, size_t iter, const unsigned char unsigned char b32[32]; unsigned char inp[6]; size_t i; - secp256k1_scratch_space *scratch = secp256k1_scratch_space_create(ctx, 65536); + secp256k1_scratch_space *scratch = secp256k1_scratch_space_create(CTX, 65536); inp[0] = prefix & 0xFF; inp[1] = (prefix >> 8) & 0xFF; @@ -5299,10 +5472,10 @@ void test_ecmult_constants_sha(uint32_t prefix, size_t iter, const unsigned char secp256k1_sha256_finalize(&acc, b32); CHECK(secp256k1_memcmp_var(b32, expected32, 32) == 0); - secp256k1_scratch_space_destroy(ctx, scratch); + secp256k1_scratch_space_destroy(CTX, scratch); } -void run_ecmult_constants(void) { +static void run_ecmult_constants(void) { /* Expected hashes of all points in the tests below. Computed using an * independent implementation. */ static const unsigned char expected32_6bit20[32] = { @@ -5336,7 +5509,7 @@ void run_ecmult_constants(void) { } } -void test_ecmult_gen_blind(void) { +static void test_ecmult_gen_blind(void) { /* Test ecmult_gen() blinding and confirm that the blinding changes, the affine points match, and the z's don't match. */ secp256k1_scalar key; secp256k1_scalar b; @@ -5346,32 +5519,32 @@ void test_ecmult_gen_blind(void) { secp256k1_gej i; secp256k1_ge pge; random_scalar_order_test(&key); - secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pgej, &key); + secp256k1_ecmult_gen(&CTX->ecmult_gen_ctx, &pgej, &key); secp256k1_testrand256(seed32); - b = ctx->ecmult_gen_ctx.blind; - i = ctx->ecmult_gen_ctx.initial; - secp256k1_ecmult_gen_blind(&ctx->ecmult_gen_ctx, seed32); - CHECK(!secp256k1_scalar_eq(&b, &ctx->ecmult_gen_ctx.blind)); - secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pgej2, &key); + b = CTX->ecmult_gen_ctx.blind; + i = CTX->ecmult_gen_ctx.initial; + secp256k1_ecmult_gen_blind(&CTX->ecmult_gen_ctx, seed32); + CHECK(!secp256k1_scalar_eq(&b, &CTX->ecmult_gen_ctx.blind)); + secp256k1_ecmult_gen(&CTX->ecmult_gen_ctx, &pgej2, &key); CHECK(!gej_xyz_equals_gej(&pgej, &pgej2)); - CHECK(!gej_xyz_equals_gej(&i, &ctx->ecmult_gen_ctx.initial)); + CHECK(!gej_xyz_equals_gej(&i, &CTX->ecmult_gen_ctx.initial)); secp256k1_ge_set_gej(&pge, &pgej); ge_equals_gej(&pge, &pgej2); } -void test_ecmult_gen_blind_reset(void) { +static void test_ecmult_gen_blind_reset(void) { /* Test ecmult_gen() blinding reset and confirm that the blinding is consistent. */ secp256k1_scalar b; secp256k1_gej initial; - secp256k1_ecmult_gen_blind(&ctx->ecmult_gen_ctx, 0); - b = ctx->ecmult_gen_ctx.blind; - initial = ctx->ecmult_gen_ctx.initial; - secp256k1_ecmult_gen_blind(&ctx->ecmult_gen_ctx, 0); - CHECK(secp256k1_scalar_eq(&b, &ctx->ecmult_gen_ctx.blind)); - CHECK(gej_xyz_equals_gej(&initial, &ctx->ecmult_gen_ctx.initial)); + secp256k1_ecmult_gen_blind(&CTX->ecmult_gen_ctx, 0); + b = CTX->ecmult_gen_ctx.blind; + initial = CTX->ecmult_gen_ctx.initial; + secp256k1_ecmult_gen_blind(&CTX->ecmult_gen_ctx, 0); + CHECK(secp256k1_scalar_eq(&b, &CTX->ecmult_gen_ctx.blind)); + CHECK(gej_xyz_equals_gej(&initial, &CTX->ecmult_gen_ctx.initial)); } -void run_ecmult_gen_blind(void) { +static void run_ecmult_gen_blind(void) { int i; test_ecmult_gen_blind_reset(); for (i = 0; i < 10; i++) { @@ -5380,7 +5553,7 @@ void run_ecmult_gen_blind(void) { } /***** ENDOMORPHISH TESTS *****/ -void test_scalar_split(const secp256k1_scalar* full) { +static void test_scalar_split(const secp256k1_scalar* full) { secp256k1_scalar s, s1, slam; const unsigned char zero[32] = {0}; unsigned char tmp[32]; @@ -5407,7 +5580,7 @@ void test_scalar_split(const secp256k1_scalar* full) { } -void run_endomorphism_tests(void) { +static void run_endomorphism_tests(void) { unsigned i; static secp256k1_scalar s; test_scalar_split(&secp256k1_scalar_zero); @@ -5418,7 +5591,7 @@ void run_endomorphism_tests(void) { secp256k1_scalar_add(&s, &secp256k1_const_lambda, &secp256k1_scalar_one); test_scalar_split(&s); - for (i = 0; i < 100U * count; ++i) { + for (i = 0; i < 100U * COUNT; ++i) { secp256k1_scalar full; random_scalar_order_test(&full); test_scalar_split(&full); @@ -5428,19 +5601,19 @@ void run_endomorphism_tests(void) { } } -void ec_pubkey_parse_pointtest(const unsigned char *input, int xvalid, int yvalid) { +static void ec_pubkey_parse_pointtest(const unsigned char *input, int xvalid, int yvalid) { unsigned char pubkeyc[65]; secp256k1_pubkey pubkey; secp256k1_ge ge; size_t pubkeyclen; int32_t ecount; ecount = 0; - secp256k1_context_set_illegal_callback(ctx, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(CTX, counting_illegal_callback_fn, &ecount); for (pubkeyclen = 3; pubkeyclen <= 65; pubkeyclen++) { /* Smaller sizes are tested exhaustively elsewhere. */ int32_t i; memcpy(&pubkeyc[1], input, 64); - VG_UNDEF(&pubkeyc[pubkeyclen], 65 - pubkeyclen); + SECP256K1_CHECKMEM_UNDEFINE(&pubkeyc[pubkeyclen], 65 - pubkeyclen); for (i = 0; i < 256; i++) { /* Try all type bytes. */ int xpass; @@ -5459,29 +5632,29 @@ void ec_pubkey_parse_pointtest(const unsigned char *input, int xvalid, int yvali unsigned char pubkeyo[65]; size_t outl; memset(&pubkey, 0, sizeof(pubkey)); - VG_UNDEF(&pubkey, sizeof(pubkey)); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey)); ecount = 0; - CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, pubkeyclen) == 1); - VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, pubkeyc, pubkeyclen) == 1); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey)); outl = 65; - VG_UNDEF(pubkeyo, 65); - CHECK(secp256k1_ec_pubkey_serialize(ctx, pubkeyo, &outl, &pubkey, SECP256K1_EC_COMPRESSED) == 1); - VG_CHECK(pubkeyo, outl); + SECP256K1_CHECKMEM_UNDEFINE(pubkeyo, 65); + CHECK(secp256k1_ec_pubkey_serialize(CTX, pubkeyo, &outl, &pubkey, SECP256K1_EC_COMPRESSED) == 1); + SECP256K1_CHECKMEM_CHECK(pubkeyo, outl); CHECK(outl == 33); CHECK(secp256k1_memcmp_var(&pubkeyo[1], &pubkeyc[1], 32) == 0); CHECK((pubkeyclen != 33) || (pubkeyo[0] == pubkeyc[0])); if (ypass) { /* This test isn't always done because we decode with alternative signs, so the y won't match. */ CHECK(pubkeyo[0] == ysign); - CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 1); + CHECK(secp256k1_pubkey_load(CTX, &ge, &pubkey) == 1); memset(&pubkey, 0, sizeof(pubkey)); - VG_UNDEF(&pubkey, sizeof(pubkey)); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey)); secp256k1_pubkey_save(&pubkey, &ge); - VG_CHECK(&pubkey, sizeof(pubkey)); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey)); outl = 65; - VG_UNDEF(pubkeyo, 65); - CHECK(secp256k1_ec_pubkey_serialize(ctx, pubkeyo, &outl, &pubkey, SECP256K1_EC_UNCOMPRESSED) == 1); - VG_CHECK(pubkeyo, outl); + SECP256K1_CHECKMEM_UNDEFINE(pubkeyo, 65); + CHECK(secp256k1_ec_pubkey_serialize(CTX, pubkeyo, &outl, &pubkey, SECP256K1_EC_UNCOMPRESSED) == 1); + SECP256K1_CHECKMEM_CHECK(pubkeyo, outl); CHECK(outl == 65); CHECK(pubkeyo[0] == 4); CHECK(secp256k1_memcmp_var(&pubkeyo[1], input, 64) == 0); @@ -5491,19 +5664,19 @@ void ec_pubkey_parse_pointtest(const unsigned char *input, int xvalid, int yvali /* These cases must fail to parse. */ memset(&pubkey, 0xfe, sizeof(pubkey)); ecount = 0; - VG_UNDEF(&pubkey, sizeof(pubkey)); - CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, pubkeyclen) == 0); - VG_CHECK(&pubkey, sizeof(pubkey)); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, pubkeyc, pubkeyclen) == 0); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey)); CHECK(ecount == 0); - CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0); + CHECK(secp256k1_pubkey_load(CTX, &ge, &pubkey) == 0); CHECK(ecount == 1); } } } - secp256k1_context_set_illegal_callback(ctx, NULL, NULL); + secp256k1_context_set_illegal_callback(CTX, NULL, NULL); } -void run_ec_pubkey_parse_test(void) { +static void run_ec_pubkey_parse_test(void) { #define SECP256K1_EC_PARSE_TEST_NVALID (12) const unsigned char valid[SECP256K1_EC_PARSE_TEST_NVALID][64] = { { @@ -5692,29 +5865,29 @@ void run_ec_pubkey_parse_test(void) { int32_t ecount2; ecount = 0; /* Nothing should be reading this far into pubkeyc. */ - VG_UNDEF(&pubkeyc[65], 1); - secp256k1_context_set_illegal_callback(ctx, counting_illegal_callback_fn, &ecount); + SECP256K1_CHECKMEM_UNDEFINE(&pubkeyc[65], 1); + secp256k1_context_set_illegal_callback(CTX, counting_illegal_callback_fn, &ecount); /* Zero length claimed, fail, zeroize, no illegal arg error. */ memset(&pubkey, 0xfe, sizeof(pubkey)); ecount = 0; - VG_UNDEF(shortkey, 2); - VG_UNDEF(&pubkey, sizeof(pubkey)); - CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, shortkey, 0) == 0); - VG_CHECK(&pubkey, sizeof(pubkey)); + SECP256K1_CHECKMEM_UNDEFINE(shortkey, 2); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, shortkey, 0) == 0); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey)); CHECK(ecount == 0); - CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0); + CHECK(secp256k1_pubkey_load(CTX, &ge, &pubkey) == 0); CHECK(ecount == 1); /* Length one claimed, fail, zeroize, no illegal arg error. */ for (i = 0; i < 256 ; i++) { memset(&pubkey, 0xfe, sizeof(pubkey)); ecount = 0; shortkey[0] = i; - VG_UNDEF(&shortkey[1], 1); - VG_UNDEF(&pubkey, sizeof(pubkey)); - CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, shortkey, 1) == 0); - VG_CHECK(&pubkey, sizeof(pubkey)); + SECP256K1_CHECKMEM_UNDEFINE(&shortkey[1], 1); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, shortkey, 1) == 0); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey)); CHECK(ecount == 0); - CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0); + CHECK(secp256k1_pubkey_load(CTX, &ge, &pubkey) == 0); CHECK(ecount == 1); } /* Length two claimed, fail, zeroize, no illegal arg error. */ @@ -5723,102 +5896,102 @@ void run_ec_pubkey_parse_test(void) { ecount = 0; shortkey[0] = i & 255; shortkey[1] = i >> 8; - VG_UNDEF(&pubkey, sizeof(pubkey)); - CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, shortkey, 2) == 0); - VG_CHECK(&pubkey, sizeof(pubkey)); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, shortkey, 2) == 0); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey)); CHECK(ecount == 0); - CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0); + CHECK(secp256k1_pubkey_load(CTX, &ge, &pubkey) == 0); CHECK(ecount == 1); } memset(&pubkey, 0xfe, sizeof(pubkey)); ecount = 0; - VG_UNDEF(&pubkey, sizeof(pubkey)); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey)); /* 33 bytes claimed on otherwise valid input starting with 0x04, fail, zeroize output, no illegal arg error. */ - CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, 33) == 0); - VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, pubkeyc, 33) == 0); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey)); CHECK(ecount == 0); - CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0); + CHECK(secp256k1_pubkey_load(CTX, &ge, &pubkey) == 0); CHECK(ecount == 1); /* NULL pubkey, illegal arg error. Pubkey isn't rewritten before this step, since it's NULL into the parser. */ - CHECK(secp256k1_ec_pubkey_parse(ctx, NULL, pubkeyc, 65) == 0); + CHECK(secp256k1_ec_pubkey_parse(CTX, NULL, pubkeyc, 65) == 0); CHECK(ecount == 2); /* NULL input string. Illegal arg and zeroize output. */ memset(&pubkey, 0xfe, sizeof(pubkey)); ecount = 0; - VG_UNDEF(&pubkey, sizeof(pubkey)); - CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, NULL, 65) == 0); - VG_CHECK(&pubkey, sizeof(pubkey)); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, NULL, 65) == 0); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey)); CHECK(ecount == 1); - CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0); + CHECK(secp256k1_pubkey_load(CTX, &ge, &pubkey) == 0); CHECK(ecount == 2); /* 64 bytes claimed on input starting with 0x04, fail, zeroize output, no illegal arg error. */ memset(&pubkey, 0xfe, sizeof(pubkey)); ecount = 0; - VG_UNDEF(&pubkey, sizeof(pubkey)); - CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, 64) == 0); - VG_CHECK(&pubkey, sizeof(pubkey)); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, pubkeyc, 64) == 0); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey)); CHECK(ecount == 0); - CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0); + CHECK(secp256k1_pubkey_load(CTX, &ge, &pubkey) == 0); CHECK(ecount == 1); /* 66 bytes claimed, fail, zeroize output, no illegal arg error. */ memset(&pubkey, 0xfe, sizeof(pubkey)); ecount = 0; - VG_UNDEF(&pubkey, sizeof(pubkey)); - CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, 66) == 0); - VG_CHECK(&pubkey, sizeof(pubkey)); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, pubkeyc, 66) == 0); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey)); CHECK(ecount == 0); - CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0); + CHECK(secp256k1_pubkey_load(CTX, &ge, &pubkey) == 0); CHECK(ecount == 1); /* Valid parse. */ memset(&pubkey, 0, sizeof(pubkey)); ecount = 0; - VG_UNDEF(&pubkey, sizeof(pubkey)); - CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, 65) == 1); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, pubkeyc, 65) == 1); CHECK(secp256k1_ec_pubkey_parse(secp256k1_context_static, &pubkey, pubkeyc, 65) == 1); - VG_CHECK(&pubkey, sizeof(pubkey)); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey)); CHECK(ecount == 0); - VG_UNDEF(&ge, sizeof(ge)); - CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 1); - VG_CHECK(&ge.x, sizeof(ge.x)); - VG_CHECK(&ge.y, sizeof(ge.y)); - VG_CHECK(&ge.infinity, sizeof(ge.infinity)); + SECP256K1_CHECKMEM_UNDEFINE(&ge, sizeof(ge)); + CHECK(secp256k1_pubkey_load(CTX, &ge, &pubkey) == 1); + SECP256K1_CHECKMEM_CHECK(&ge.x, sizeof(ge.x)); + SECP256K1_CHECKMEM_CHECK(&ge.y, sizeof(ge.y)); + SECP256K1_CHECKMEM_CHECK(&ge.infinity, sizeof(ge.infinity)); ge_equals_ge(&secp256k1_ge_const_g, &ge); CHECK(ecount == 0); /* secp256k1_ec_pubkey_serialize illegal args. */ ecount = 0; len = 65; - CHECK(secp256k1_ec_pubkey_serialize(ctx, NULL, &len, &pubkey, SECP256K1_EC_UNCOMPRESSED) == 0); + CHECK(secp256k1_ec_pubkey_serialize(CTX, NULL, &len, &pubkey, SECP256K1_EC_UNCOMPRESSED) == 0); CHECK(ecount == 1); CHECK(len == 0); - CHECK(secp256k1_ec_pubkey_serialize(ctx, sout, NULL, &pubkey, SECP256K1_EC_UNCOMPRESSED) == 0); + CHECK(secp256k1_ec_pubkey_serialize(CTX, sout, NULL, &pubkey, SECP256K1_EC_UNCOMPRESSED) == 0); CHECK(ecount == 2); len = 65; - VG_UNDEF(sout, 65); - CHECK(secp256k1_ec_pubkey_serialize(ctx, sout, &len, NULL, SECP256K1_EC_UNCOMPRESSED) == 0); - VG_CHECK(sout, 65); + SECP256K1_CHECKMEM_UNDEFINE(sout, 65); + CHECK(secp256k1_ec_pubkey_serialize(CTX, sout, &len, NULL, SECP256K1_EC_UNCOMPRESSED) == 0); + SECP256K1_CHECKMEM_CHECK(sout, 65); CHECK(ecount == 3); CHECK(len == 0); len = 65; - CHECK(secp256k1_ec_pubkey_serialize(ctx, sout, &len, &pubkey, ~0) == 0); + CHECK(secp256k1_ec_pubkey_serialize(CTX, sout, &len, &pubkey, ~0) == 0); CHECK(ecount == 4); CHECK(len == 0); len = 65; - VG_UNDEF(sout, 65); - CHECK(secp256k1_ec_pubkey_serialize(ctx, sout, &len, &pubkey, SECP256K1_EC_UNCOMPRESSED) == 1); - VG_CHECK(sout, 65); + SECP256K1_CHECKMEM_UNDEFINE(sout, 65); + CHECK(secp256k1_ec_pubkey_serialize(CTX, sout, &len, &pubkey, SECP256K1_EC_UNCOMPRESSED) == 1); + SECP256K1_CHECKMEM_CHECK(sout, 65); CHECK(ecount == 4); CHECK(len == 65); /* Multiple illegal args. Should still set arg error only once. */ ecount = 0; ecount2 = 11; - CHECK(secp256k1_ec_pubkey_parse(ctx, NULL, NULL, 65) == 0); + CHECK(secp256k1_ec_pubkey_parse(CTX, NULL, NULL, 65) == 0); CHECK(ecount == 1); /* Does the illegal arg callback actually change the behavior? */ - secp256k1_context_set_illegal_callback(ctx, uncounting_illegal_callback_fn, &ecount2); - CHECK(secp256k1_ec_pubkey_parse(ctx, NULL, NULL, 65) == 0); + secp256k1_context_set_illegal_callback(CTX, uncounting_illegal_callback_fn, &ecount2); + CHECK(secp256k1_ec_pubkey_parse(CTX, NULL, NULL, 65) == 0); CHECK(ecount == 1); CHECK(ecount2 == 10); - secp256k1_context_set_illegal_callback(ctx, NULL, NULL); + secp256k1_context_set_illegal_callback(CTX, NULL, NULL); /* Try a bunch of prefabbed points with all possible encodings. */ for (i = 0; i < SECP256K1_EC_PARSE_TEST_NVALID; i++) { ec_pubkey_parse_pointtest(valid[i], 1, 1); @@ -5831,7 +6004,7 @@ void run_ec_pubkey_parse_test(void) { } } -void run_eckey_edge_case_test(void) { +static void run_eckey_edge_case_test(void) { const unsigned char orderc[32] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, @@ -5849,65 +6022,65 @@ void run_eckey_edge_case_test(void) { size_t len; int32_t ecount; /* Group order is too large, reject. */ - CHECK(secp256k1_ec_seckey_verify(ctx, orderc) == 0); - VG_UNDEF(&pubkey, sizeof(pubkey)); - CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, orderc) == 0); - VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_seckey_verify(CTX, orderc) == 0); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey, orderc) == 0); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey)); CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); /* Maximum value is too large, reject. */ memset(ctmp, 255, 32); - CHECK(secp256k1_ec_seckey_verify(ctx, ctmp) == 0); + CHECK(secp256k1_ec_seckey_verify(CTX, ctmp) == 0); memset(&pubkey, 1, sizeof(pubkey)); - VG_UNDEF(&pubkey, sizeof(pubkey)); - CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, ctmp) == 0); - VG_CHECK(&pubkey, sizeof(pubkey)); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey, ctmp) == 0); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey)); CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); /* Zero is too small, reject. */ memset(ctmp, 0, 32); - CHECK(secp256k1_ec_seckey_verify(ctx, ctmp) == 0); + CHECK(secp256k1_ec_seckey_verify(CTX, ctmp) == 0); memset(&pubkey, 1, sizeof(pubkey)); - VG_UNDEF(&pubkey, sizeof(pubkey)); - CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, ctmp) == 0); - VG_CHECK(&pubkey, sizeof(pubkey)); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey, ctmp) == 0); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey)); CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); /* One must be accepted. */ ctmp[31] = 0x01; - CHECK(secp256k1_ec_seckey_verify(ctx, ctmp) == 1); + CHECK(secp256k1_ec_seckey_verify(CTX, ctmp) == 1); memset(&pubkey, 0, sizeof(pubkey)); - VG_UNDEF(&pubkey, sizeof(pubkey)); - CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, ctmp) == 1); - VG_CHECK(&pubkey, sizeof(pubkey)); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey, ctmp) == 1); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey)); CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0); pubkey_one = pubkey; /* Group order + 1 is too large, reject. */ memcpy(ctmp, orderc, 32); ctmp[31] = 0x42; - CHECK(secp256k1_ec_seckey_verify(ctx, ctmp) == 0); + CHECK(secp256k1_ec_seckey_verify(CTX, ctmp) == 0); memset(&pubkey, 1, sizeof(pubkey)); - VG_UNDEF(&pubkey, sizeof(pubkey)); - CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, ctmp) == 0); - VG_CHECK(&pubkey, sizeof(pubkey)); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey, ctmp) == 0); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey)); CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); /* -1 must be accepted. */ ctmp[31] = 0x40; - CHECK(secp256k1_ec_seckey_verify(ctx, ctmp) == 1); + CHECK(secp256k1_ec_seckey_verify(CTX, ctmp) == 1); memset(&pubkey, 0, sizeof(pubkey)); - VG_UNDEF(&pubkey, sizeof(pubkey)); - CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, ctmp) == 1); - VG_CHECK(&pubkey, sizeof(pubkey)); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey, ctmp) == 1); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey)); CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0); pubkey_negone = pubkey; /* Tweak of zero leaves the value unchanged. */ memset(ctmp2, 0, 32); - CHECK(secp256k1_ec_seckey_tweak_add(ctx, ctmp, ctmp2) == 1); + CHECK(secp256k1_ec_seckey_tweak_add(CTX, ctmp, ctmp2) == 1); CHECK(secp256k1_memcmp_var(orderc, ctmp, 31) == 0 && ctmp[31] == 0x40); memcpy(&pubkey2, &pubkey, sizeof(pubkey)); - CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, ctmp2) == 1); + CHECK(secp256k1_ec_pubkey_tweak_add(CTX, &pubkey, ctmp2) == 1); CHECK(secp256k1_memcmp_var(&pubkey, &pubkey2, sizeof(pubkey)) == 0); /* Multiply tweak of zero zeroizes the output. */ - CHECK(secp256k1_ec_seckey_tweak_mul(ctx, ctmp, ctmp2) == 0); + CHECK(secp256k1_ec_seckey_tweak_mul(CTX, ctmp, ctmp2) == 0); CHECK(secp256k1_memcmp_var(zeros, ctmp, 32) == 0); - CHECK(secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, ctmp2) == 0); + CHECK(secp256k1_ec_pubkey_tweak_mul(CTX, &pubkey, ctmp2) == 0); CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(pubkey)) == 0); memcpy(&pubkey, &pubkey2, sizeof(pubkey)); /* If seckey_tweak_add or seckey_tweak_mul are called with an overflowing @@ -5915,31 +6088,31 @@ void run_eckey_edge_case_test(void) { memcpy(ctmp, orderc, 32); memset(ctmp2, 0, 32); ctmp2[31] = 0x01; - CHECK(secp256k1_ec_seckey_verify(ctx, ctmp2) == 1); - CHECK(secp256k1_ec_seckey_verify(ctx, ctmp) == 0); - CHECK(secp256k1_ec_seckey_tweak_add(ctx, ctmp, ctmp2) == 0); + CHECK(secp256k1_ec_seckey_verify(CTX, ctmp2) == 1); + CHECK(secp256k1_ec_seckey_verify(CTX, ctmp) == 0); + CHECK(secp256k1_ec_seckey_tweak_add(CTX, ctmp, ctmp2) == 0); CHECK(secp256k1_memcmp_var(zeros, ctmp, 32) == 0); memcpy(ctmp, orderc, 32); - CHECK(secp256k1_ec_seckey_tweak_mul(ctx, ctmp, ctmp2) == 0); + CHECK(secp256k1_ec_seckey_tweak_mul(CTX, ctmp, ctmp2) == 0); CHECK(secp256k1_memcmp_var(zeros, ctmp, 32) == 0); /* If seckey_tweak_add or seckey_tweak_mul are called with an overflowing tweak, the seckey is zeroized. */ memcpy(ctmp, orderc, 32); ctmp[31] = 0x40; - CHECK(secp256k1_ec_seckey_tweak_add(ctx, ctmp, orderc) == 0); + CHECK(secp256k1_ec_seckey_tweak_add(CTX, ctmp, orderc) == 0); CHECK(secp256k1_memcmp_var(zeros, ctmp, 32) == 0); memcpy(ctmp, orderc, 32); ctmp[31] = 0x40; - CHECK(secp256k1_ec_seckey_tweak_mul(ctx, ctmp, orderc) == 0); + CHECK(secp256k1_ec_seckey_tweak_mul(CTX, ctmp, orderc) == 0); CHECK(secp256k1_memcmp_var(zeros, ctmp, 32) == 0); memcpy(ctmp, orderc, 32); ctmp[31] = 0x40; /* If pubkey_tweak_add or pubkey_tweak_mul are called with an overflowing tweak, the pubkey is zeroized. */ - CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, orderc) == 0); + CHECK(secp256k1_ec_pubkey_tweak_add(CTX, &pubkey, orderc) == 0); CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(pubkey)) == 0); memcpy(&pubkey, &pubkey2, sizeof(pubkey)); - CHECK(secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, orderc) == 0); + CHECK(secp256k1_ec_pubkey_tweak_mul(CTX, &pubkey, orderc) == 0); CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(pubkey)) == 0); memcpy(&pubkey, &pubkey2, sizeof(pubkey)); /* If the resulting key in secp256k1_ec_seckey_tweak_add and @@ -5949,145 +6122,145 @@ void run_eckey_edge_case_test(void) { ctmp[31] = 0x40; memset(ctmp2, 0, 32); ctmp2[31] = 1; - CHECK(secp256k1_ec_seckey_tweak_add(ctx, ctmp2, ctmp) == 0); + CHECK(secp256k1_ec_seckey_tweak_add(CTX, ctmp2, ctmp) == 0); CHECK(secp256k1_memcmp_var(zeros, ctmp2, 32) == 0); ctmp2[31] = 1; - CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, ctmp2) == 0); + CHECK(secp256k1_ec_pubkey_tweak_add(CTX, &pubkey, ctmp2) == 0); CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(pubkey)) == 0); memcpy(&pubkey, &pubkey2, sizeof(pubkey)); /* Tweak computation wraps and results in a key of 1. */ ctmp2[31] = 2; - CHECK(secp256k1_ec_seckey_tweak_add(ctx, ctmp2, ctmp) == 1); + CHECK(secp256k1_ec_seckey_tweak_add(CTX, ctmp2, ctmp) == 1); CHECK(secp256k1_memcmp_var(ctmp2, zeros, 31) == 0 && ctmp2[31] == 1); ctmp2[31] = 2; - CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, ctmp2) == 1); + CHECK(secp256k1_ec_pubkey_tweak_add(CTX, &pubkey, ctmp2) == 1); ctmp2[31] = 1; - CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey2, ctmp2) == 1); + CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey2, ctmp2) == 1); CHECK(secp256k1_memcmp_var(&pubkey, &pubkey2, sizeof(pubkey)) == 0); /* Tweak mul * 2 = 1+1. */ - CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, ctmp2) == 1); + CHECK(secp256k1_ec_pubkey_tweak_add(CTX, &pubkey, ctmp2) == 1); ctmp2[31] = 2; - CHECK(secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey2, ctmp2) == 1); + CHECK(secp256k1_ec_pubkey_tweak_mul(CTX, &pubkey2, ctmp2) == 1); CHECK(secp256k1_memcmp_var(&pubkey, &pubkey2, sizeof(pubkey)) == 0); /* Test argument errors. */ ecount = 0; - secp256k1_context_set_illegal_callback(ctx, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(CTX, counting_illegal_callback_fn, &ecount); CHECK(ecount == 0); /* Zeroize pubkey on parse error. */ memset(&pubkey, 0, 32); - CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, ctmp2) == 0); + CHECK(secp256k1_ec_pubkey_tweak_add(CTX, &pubkey, ctmp2) == 0); CHECK(ecount == 1); CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(pubkey)) == 0); memcpy(&pubkey, &pubkey2, sizeof(pubkey)); memset(&pubkey2, 0, 32); - CHECK(secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey2, ctmp2) == 0); + CHECK(secp256k1_ec_pubkey_tweak_mul(CTX, &pubkey2, ctmp2) == 0); CHECK(ecount == 2); CHECK(secp256k1_memcmp_var(&pubkey2, zeros, sizeof(pubkey2)) == 0); /* Plain argument errors. */ ecount = 0; - CHECK(secp256k1_ec_seckey_verify(ctx, ctmp) == 1); + CHECK(secp256k1_ec_seckey_verify(CTX, ctmp) == 1); CHECK(ecount == 0); - CHECK(secp256k1_ec_seckey_verify(ctx, NULL) == 0); + CHECK(secp256k1_ec_seckey_verify(CTX, NULL) == 0); CHECK(ecount == 1); ecount = 0; memset(ctmp2, 0, 32); ctmp2[31] = 4; - CHECK(secp256k1_ec_pubkey_tweak_add(ctx, NULL, ctmp2) == 0); + CHECK(secp256k1_ec_pubkey_tweak_add(CTX, NULL, ctmp2) == 0); CHECK(ecount == 1); - CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, NULL) == 0); + CHECK(secp256k1_ec_pubkey_tweak_add(CTX, &pubkey, NULL) == 0); CHECK(ecount == 2); ecount = 0; memset(ctmp2, 0, 32); ctmp2[31] = 4; - CHECK(secp256k1_ec_pubkey_tweak_mul(ctx, NULL, ctmp2) == 0); + CHECK(secp256k1_ec_pubkey_tweak_mul(CTX, NULL, ctmp2) == 0); CHECK(ecount == 1); - CHECK(secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, NULL) == 0); + CHECK(secp256k1_ec_pubkey_tweak_mul(CTX, &pubkey, NULL) == 0); CHECK(ecount == 2); ecount = 0; memset(ctmp2, 0, 32); - CHECK(secp256k1_ec_seckey_tweak_add(ctx, NULL, ctmp2) == 0); + CHECK(secp256k1_ec_seckey_tweak_add(CTX, NULL, ctmp2) == 0); CHECK(ecount == 1); - CHECK(secp256k1_ec_seckey_tweak_add(ctx, ctmp, NULL) == 0); + CHECK(secp256k1_ec_seckey_tweak_add(CTX, ctmp, NULL) == 0); CHECK(ecount == 2); ecount = 0; memset(ctmp2, 0, 32); ctmp2[31] = 1; - CHECK(secp256k1_ec_seckey_tweak_mul(ctx, NULL, ctmp2) == 0); + CHECK(secp256k1_ec_seckey_tweak_mul(CTX, NULL, ctmp2) == 0); CHECK(ecount == 1); - CHECK(secp256k1_ec_seckey_tweak_mul(ctx, ctmp, NULL) == 0); + CHECK(secp256k1_ec_seckey_tweak_mul(CTX, ctmp, NULL) == 0); CHECK(ecount == 2); ecount = 0; - CHECK(secp256k1_ec_pubkey_create(ctx, NULL, ctmp) == 0); + CHECK(secp256k1_ec_pubkey_create(CTX, NULL, ctmp) == 0); CHECK(ecount == 1); memset(&pubkey, 1, sizeof(pubkey)); - CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, NULL) == 0); + CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey, NULL) == 0); CHECK(ecount == 2); CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); /* secp256k1_ec_pubkey_combine tests. */ ecount = 0; pubkeys[0] = &pubkey_one; - VG_UNDEF(&pubkeys[0], sizeof(secp256k1_pubkey *)); - VG_UNDEF(&pubkeys[1], sizeof(secp256k1_pubkey *)); - VG_UNDEF(&pubkeys[2], sizeof(secp256k1_pubkey *)); + SECP256K1_CHECKMEM_UNDEFINE(&pubkeys[0], sizeof(secp256k1_pubkey *)); + SECP256K1_CHECKMEM_UNDEFINE(&pubkeys[1], sizeof(secp256k1_pubkey *)); + SECP256K1_CHECKMEM_UNDEFINE(&pubkeys[2], sizeof(secp256k1_pubkey *)); memset(&pubkey, 255, sizeof(secp256k1_pubkey)); - VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey)); - CHECK(secp256k1_ec_pubkey_combine(ctx, &pubkey, pubkeys, 0) == 0); - VG_CHECK(&pubkey, sizeof(secp256k1_pubkey)); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(secp256k1_ec_pubkey_combine(CTX, &pubkey, pubkeys, 0) == 0); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(secp256k1_pubkey)); CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); CHECK(ecount == 1); - CHECK(secp256k1_ec_pubkey_combine(ctx, NULL, pubkeys, 1) == 0); + CHECK(secp256k1_ec_pubkey_combine(CTX, NULL, pubkeys, 1) == 0); CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); CHECK(ecount == 2); memset(&pubkey, 255, sizeof(secp256k1_pubkey)); - VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey)); - CHECK(secp256k1_ec_pubkey_combine(ctx, &pubkey, NULL, 1) == 0); - VG_CHECK(&pubkey, sizeof(secp256k1_pubkey)); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(secp256k1_ec_pubkey_combine(CTX, &pubkey, NULL, 1) == 0); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(secp256k1_pubkey)); CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); CHECK(ecount == 3); pubkeys[0] = &pubkey_negone; memset(&pubkey, 255, sizeof(secp256k1_pubkey)); - VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey)); - CHECK(secp256k1_ec_pubkey_combine(ctx, &pubkey, pubkeys, 1) == 1); - VG_CHECK(&pubkey, sizeof(secp256k1_pubkey)); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(secp256k1_ec_pubkey_combine(CTX, &pubkey, pubkeys, 1) == 1); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(secp256k1_pubkey)); CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0); CHECK(ecount == 3); len = 33; - CHECK(secp256k1_ec_pubkey_serialize(ctx, ctmp, &len, &pubkey, SECP256K1_EC_COMPRESSED) == 1); - CHECK(secp256k1_ec_pubkey_serialize(ctx, ctmp2, &len, &pubkey_negone, SECP256K1_EC_COMPRESSED) == 1); + CHECK(secp256k1_ec_pubkey_serialize(CTX, ctmp, &len, &pubkey, SECP256K1_EC_COMPRESSED) == 1); + CHECK(secp256k1_ec_pubkey_serialize(CTX, ctmp2, &len, &pubkey_negone, SECP256K1_EC_COMPRESSED) == 1); CHECK(secp256k1_memcmp_var(ctmp, ctmp2, 33) == 0); /* Result is infinity. */ pubkeys[0] = &pubkey_one; pubkeys[1] = &pubkey_negone; memset(&pubkey, 255, sizeof(secp256k1_pubkey)); - VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey)); - CHECK(secp256k1_ec_pubkey_combine(ctx, &pubkey, pubkeys, 2) == 0); - VG_CHECK(&pubkey, sizeof(secp256k1_pubkey)); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(secp256k1_ec_pubkey_combine(CTX, &pubkey, pubkeys, 2) == 0); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(secp256k1_pubkey)); CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); CHECK(ecount == 3); /* Passes through infinity but comes out one. */ pubkeys[2] = &pubkey_one; memset(&pubkey, 255, sizeof(secp256k1_pubkey)); - VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey)); - CHECK(secp256k1_ec_pubkey_combine(ctx, &pubkey, pubkeys, 3) == 1); - VG_CHECK(&pubkey, sizeof(secp256k1_pubkey)); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(secp256k1_ec_pubkey_combine(CTX, &pubkey, pubkeys, 3) == 1); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(secp256k1_pubkey)); CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0); CHECK(ecount == 3); len = 33; - CHECK(secp256k1_ec_pubkey_serialize(ctx, ctmp, &len, &pubkey, SECP256K1_EC_COMPRESSED) == 1); - CHECK(secp256k1_ec_pubkey_serialize(ctx, ctmp2, &len, &pubkey_one, SECP256K1_EC_COMPRESSED) == 1); + CHECK(secp256k1_ec_pubkey_serialize(CTX, ctmp, &len, &pubkey, SECP256K1_EC_COMPRESSED) == 1); + CHECK(secp256k1_ec_pubkey_serialize(CTX, ctmp2, &len, &pubkey_one, SECP256K1_EC_COMPRESSED) == 1); CHECK(secp256k1_memcmp_var(ctmp, ctmp2, 33) == 0); /* Adds to two. */ pubkeys[1] = &pubkey_one; memset(&pubkey, 255, sizeof(secp256k1_pubkey)); - VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey)); - CHECK(secp256k1_ec_pubkey_combine(ctx, &pubkey, pubkeys, 2) == 1); - VG_CHECK(&pubkey, sizeof(secp256k1_pubkey)); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(secp256k1_ec_pubkey_combine(CTX, &pubkey, pubkeys, 2) == 1); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(secp256k1_pubkey)); CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0); CHECK(ecount == 3); - secp256k1_context_set_illegal_callback(ctx, NULL, NULL); + secp256k1_context_set_illegal_callback(CTX, NULL, NULL); } -void run_eckey_negate_test(void) { +static void run_eckey_negate_test(void) { unsigned char seckey[32]; unsigned char seckey_tmp[32]; @@ -6095,20 +6268,20 @@ void run_eckey_negate_test(void) { memcpy(seckey_tmp, seckey, 32); /* Verify negation changes the key and changes it back */ - CHECK(secp256k1_ec_seckey_negate(ctx, seckey) == 1); + CHECK(secp256k1_ec_seckey_negate(CTX, seckey) == 1); CHECK(secp256k1_memcmp_var(seckey, seckey_tmp, 32) != 0); - CHECK(secp256k1_ec_seckey_negate(ctx, seckey) == 1); + CHECK(secp256k1_ec_seckey_negate(CTX, seckey) == 1); CHECK(secp256k1_memcmp_var(seckey, seckey_tmp, 32) == 0); /* Check that privkey alias gives same result */ - CHECK(secp256k1_ec_seckey_negate(ctx, seckey) == 1); - CHECK(secp256k1_ec_privkey_negate(ctx, seckey_tmp) == 1); + CHECK(secp256k1_ec_seckey_negate(CTX, seckey) == 1); + CHECK(secp256k1_ec_privkey_negate(CTX, seckey_tmp) == 1); CHECK(secp256k1_memcmp_var(seckey, seckey_tmp, 32) == 0); /* Negating all 0s fails */ memset(seckey, 0, 32); memset(seckey_tmp, 0, 32); - CHECK(secp256k1_ec_seckey_negate(ctx, seckey) == 0); + CHECK(secp256k1_ec_seckey_negate(CTX, seckey) == 0); /* Check that seckey is not modified */ CHECK(secp256k1_memcmp_var(seckey, seckey_tmp, 32) == 0); @@ -6118,18 +6291,18 @@ void run_eckey_negate_test(void) { random_scalar_order_b32(seckey); memset(seckey, 0xFF, 16); memset(seckey_tmp, 0, 32); - CHECK(secp256k1_ec_seckey_negate(ctx, seckey) == 0); + CHECK(secp256k1_ec_seckey_negate(CTX, seckey) == 0); CHECK(secp256k1_memcmp_var(seckey, seckey_tmp, 32) == 0); } -void random_sign(secp256k1_scalar *sigr, secp256k1_scalar *sigs, const secp256k1_scalar *key, const secp256k1_scalar *msg, int *recid) { +static void random_sign(secp256k1_scalar *sigr, secp256k1_scalar *sigs, const secp256k1_scalar *key, const secp256k1_scalar *msg, int *recid) { secp256k1_scalar nonce; do { random_scalar_order_test(&nonce); - } while(!secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, sigr, sigs, key, msg, &nonce, recid)); + } while(!secp256k1_ecdsa_sig_sign(&CTX->ecmult_gen_ctx, sigr, sigs, key, msg, &nonce, recid)); } -void test_ecdsa_sign_verify(void) { +static void test_ecdsa_sign_verify(void) { secp256k1_gej pubj; secp256k1_ge pub; secp256k1_scalar one; @@ -6139,7 +6312,7 @@ void test_ecdsa_sign_verify(void) { int recid; random_scalar_order_test(&msg); random_scalar_order_test(&key); - secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pubj, &key); + secp256k1_ecmult_gen(&CTX->ecmult_gen_ctx, &pubj, &key); secp256k1_ge_set_gej(&pub, &pubj); getrec = secp256k1_testrand_bits(1); /* The specific way in which this conditional is written sidesteps a potential bug in clang. @@ -6156,9 +6329,9 @@ void test_ecdsa_sign_verify(void) { CHECK(!secp256k1_ecdsa_sig_verify(&sigr, &sigs, &pub, &msg)); } -void run_ecdsa_sign_verify(void) { +static void run_ecdsa_sign_verify(void) { int i; - for (i = 0; i < 10*count; i++) { + for (i = 0; i < 10*COUNT; i++) { test_ecdsa_sign_verify(); } } @@ -6210,12 +6383,12 @@ static int nonce_function_test_retry(unsigned char *nonce32, const unsigned char return nonce_function_rfc6979(nonce32, msg32, key32, algo16, data, counter - 5); } -int is_empty_signature(const secp256k1_ecdsa_signature *sig) { +static int is_empty_signature(const secp256k1_ecdsa_signature *sig) { static const unsigned char res[sizeof(secp256k1_ecdsa_signature)] = {0}; return secp256k1_memcmp_var(sig, res, sizeof(secp256k1_ecdsa_signature)) == 0; } -void test_ecdsa_end_to_end(void) { +static void test_ecdsa_end_to_end(void) { unsigned char extra[32] = {0x00}; unsigned char privkey[32]; unsigned char message[32]; @@ -6241,24 +6414,24 @@ void test_ecdsa_end_to_end(void) { } /* Construct and verify corresponding public key. */ - CHECK(secp256k1_ec_seckey_verify(ctx, privkey) == 1); - CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, privkey) == 1); + CHECK(secp256k1_ec_seckey_verify(CTX, privkey) == 1); + CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey, privkey) == 1); /* Verify exporting and importing public key. */ - CHECK(secp256k1_ec_pubkey_serialize(ctx, pubkeyc, &pubkeyclen, &pubkey, secp256k1_testrand_bits(1) == 1 ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED)); + CHECK(secp256k1_ec_pubkey_serialize(CTX, pubkeyc, &pubkeyclen, &pubkey, secp256k1_testrand_bits(1) == 1 ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED)); memset(&pubkey, 0, sizeof(pubkey)); - CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, pubkeyclen) == 1); + CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, pubkeyc, pubkeyclen) == 1); /* Verify negation changes the key and changes it back */ memcpy(&pubkey_tmp, &pubkey, sizeof(pubkey)); - CHECK(secp256k1_ec_pubkey_negate(ctx, &pubkey_tmp) == 1); + CHECK(secp256k1_ec_pubkey_negate(CTX, &pubkey_tmp) == 1); CHECK(secp256k1_memcmp_var(&pubkey_tmp, &pubkey, sizeof(pubkey)) != 0); - CHECK(secp256k1_ec_pubkey_negate(ctx, &pubkey_tmp) == 1); + CHECK(secp256k1_ec_pubkey_negate(CTX, &pubkey_tmp) == 1); CHECK(secp256k1_memcmp_var(&pubkey_tmp, &pubkey, sizeof(pubkey)) == 0); /* Verify private key import and export. */ - CHECK(ec_privkey_export_der(ctx, seckey, &seckeylen, privkey, secp256k1_testrand_bits(1) == 1)); - CHECK(ec_privkey_import_der(ctx, privkey2, seckey, seckeylen) == 1); + CHECK(ec_privkey_export_der(CTX, seckey, &seckeylen, privkey, secp256k1_testrand_bits(1) == 1)); + CHECK(ec_privkey_import_der(CTX, privkey2, seckey, seckeylen) == 1); CHECK(secp256k1_memcmp_var(privkey, privkey2, 32) == 0); /* Optionally tweak the keys using addition. */ @@ -6271,17 +6444,17 @@ void test_ecdsa_end_to_end(void) { secp256k1_pubkey pubkey2; secp256k1_testrand256_test(rnd); memcpy(privkey_tmp, privkey, 32); - ret1 = secp256k1_ec_seckey_tweak_add(ctx, privkey, rnd); - ret2 = secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, rnd); + ret1 = secp256k1_ec_seckey_tweak_add(CTX, privkey, rnd); + ret2 = secp256k1_ec_pubkey_tweak_add(CTX, &pubkey, rnd); /* Check that privkey alias gives same result */ - ret3 = secp256k1_ec_privkey_tweak_add(ctx, privkey_tmp, rnd); + ret3 = secp256k1_ec_privkey_tweak_add(CTX, privkey_tmp, rnd); CHECK(ret1 == ret2); CHECK(ret2 == ret3); if (ret1 == 0) { return; } CHECK(secp256k1_memcmp_var(privkey, privkey_tmp, 32) == 0); - CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey2, privkey) == 1); + CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey2, privkey) == 1); CHECK(secp256k1_memcmp_var(&pubkey, &pubkey2, sizeof(pubkey)) == 0); } @@ -6295,29 +6468,29 @@ void test_ecdsa_end_to_end(void) { secp256k1_pubkey pubkey2; secp256k1_testrand256_test(rnd); memcpy(privkey_tmp, privkey, 32); - ret1 = secp256k1_ec_seckey_tweak_mul(ctx, privkey, rnd); - ret2 = secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, rnd); + ret1 = secp256k1_ec_seckey_tweak_mul(CTX, privkey, rnd); + ret2 = secp256k1_ec_pubkey_tweak_mul(CTX, &pubkey, rnd); /* Check that privkey alias gives same result */ - ret3 = secp256k1_ec_privkey_tweak_mul(ctx, privkey_tmp, rnd); + ret3 = secp256k1_ec_privkey_tweak_mul(CTX, privkey_tmp, rnd); CHECK(ret1 == ret2); CHECK(ret2 == ret3); if (ret1 == 0) { return; } CHECK(secp256k1_memcmp_var(privkey, privkey_tmp, 32) == 0); - CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey2, privkey) == 1); + CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey2, privkey) == 1); CHECK(secp256k1_memcmp_var(&pubkey, &pubkey2, sizeof(pubkey)) == 0); } /* Sign. */ - CHECK(secp256k1_ecdsa_sign(ctx, &signature[0], message, privkey, NULL, NULL) == 1); - CHECK(secp256k1_ecdsa_sign(ctx, &signature[4], message, privkey, NULL, NULL) == 1); - CHECK(secp256k1_ecdsa_sign(ctx, &signature[1], message, privkey, NULL, extra) == 1); + CHECK(secp256k1_ecdsa_sign(CTX, &signature[0], message, privkey, NULL, NULL) == 1); + CHECK(secp256k1_ecdsa_sign(CTX, &signature[4], message, privkey, NULL, NULL) == 1); + CHECK(secp256k1_ecdsa_sign(CTX, &signature[1], message, privkey, NULL, extra) == 1); extra[31] = 1; - CHECK(secp256k1_ecdsa_sign(ctx, &signature[2], message, privkey, NULL, extra) == 1); + CHECK(secp256k1_ecdsa_sign(CTX, &signature[2], message, privkey, NULL, extra) == 1); extra[31] = 0; extra[0] = 1; - CHECK(secp256k1_ecdsa_sign(ctx, &signature[3], message, privkey, NULL, extra) == 1); + CHECK(secp256k1_ecdsa_sign(CTX, &signature[3], message, privkey, NULL, extra) == 1); CHECK(secp256k1_memcmp_var(&signature[0], &signature[4], sizeof(signature[0])) == 0); CHECK(secp256k1_memcmp_var(&signature[0], &signature[1], sizeof(signature[0])) != 0); CHECK(secp256k1_memcmp_var(&signature[0], &signature[2], sizeof(signature[0])) != 0); @@ -6326,41 +6499,41 @@ void test_ecdsa_end_to_end(void) { CHECK(secp256k1_memcmp_var(&signature[1], &signature[3], sizeof(signature[0])) != 0); CHECK(secp256k1_memcmp_var(&signature[2], &signature[3], sizeof(signature[0])) != 0); /* Verify. */ - CHECK(secp256k1_ecdsa_verify(ctx, &signature[0], message, &pubkey) == 1); - CHECK(secp256k1_ecdsa_verify(ctx, &signature[1], message, &pubkey) == 1); - CHECK(secp256k1_ecdsa_verify(ctx, &signature[2], message, &pubkey) == 1); - CHECK(secp256k1_ecdsa_verify(ctx, &signature[3], message, &pubkey) == 1); + CHECK(secp256k1_ecdsa_verify(CTX, &signature[0], message, &pubkey) == 1); + CHECK(secp256k1_ecdsa_verify(CTX, &signature[1], message, &pubkey) == 1); + CHECK(secp256k1_ecdsa_verify(CTX, &signature[2], message, &pubkey) == 1); + CHECK(secp256k1_ecdsa_verify(CTX, &signature[3], message, &pubkey) == 1); /* Test lower-S form, malleate, verify and fail, test again, malleate again */ - CHECK(!secp256k1_ecdsa_signature_normalize(ctx, NULL, &signature[0])); - secp256k1_ecdsa_signature_load(ctx, &r, &s, &signature[0]); + CHECK(!secp256k1_ecdsa_signature_normalize(CTX, NULL, &signature[0])); + secp256k1_ecdsa_signature_load(CTX, &r, &s, &signature[0]); secp256k1_scalar_negate(&s, &s); secp256k1_ecdsa_signature_save(&signature[5], &r, &s); - CHECK(secp256k1_ecdsa_verify(ctx, &signature[5], message, &pubkey) == 0); - CHECK(secp256k1_ecdsa_signature_normalize(ctx, NULL, &signature[5])); - CHECK(secp256k1_ecdsa_signature_normalize(ctx, &signature[5], &signature[5])); - CHECK(!secp256k1_ecdsa_signature_normalize(ctx, NULL, &signature[5])); - CHECK(!secp256k1_ecdsa_signature_normalize(ctx, &signature[5], &signature[5])); - CHECK(secp256k1_ecdsa_verify(ctx, &signature[5], message, &pubkey) == 1); + CHECK(secp256k1_ecdsa_verify(CTX, &signature[5], message, &pubkey) == 0); + CHECK(secp256k1_ecdsa_signature_normalize(CTX, NULL, &signature[5])); + CHECK(secp256k1_ecdsa_signature_normalize(CTX, &signature[5], &signature[5])); + CHECK(!secp256k1_ecdsa_signature_normalize(CTX, NULL, &signature[5])); + CHECK(!secp256k1_ecdsa_signature_normalize(CTX, &signature[5], &signature[5])); + CHECK(secp256k1_ecdsa_verify(CTX, &signature[5], message, &pubkey) == 1); secp256k1_scalar_negate(&s, &s); secp256k1_ecdsa_signature_save(&signature[5], &r, &s); - CHECK(!secp256k1_ecdsa_signature_normalize(ctx, NULL, &signature[5])); - CHECK(secp256k1_ecdsa_verify(ctx, &signature[5], message, &pubkey) == 1); + CHECK(!secp256k1_ecdsa_signature_normalize(CTX, NULL, &signature[5])); + CHECK(secp256k1_ecdsa_verify(CTX, &signature[5], message, &pubkey) == 1); CHECK(secp256k1_memcmp_var(&signature[5], &signature[0], 64) == 0); /* Serialize/parse DER and verify again */ - CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, sig, &siglen, &signature[0]) == 1); + CHECK(secp256k1_ecdsa_signature_serialize_der(CTX, sig, &siglen, &signature[0]) == 1); memset(&signature[0], 0, sizeof(signature[0])); - CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &signature[0], sig, siglen) == 1); - CHECK(secp256k1_ecdsa_verify(ctx, &signature[0], message, &pubkey) == 1); + CHECK(secp256k1_ecdsa_signature_parse_der(CTX, &signature[0], sig, siglen) == 1); + CHECK(secp256k1_ecdsa_verify(CTX, &signature[0], message, &pubkey) == 1); /* Serialize/destroy/parse DER and verify again. */ siglen = 74; - CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, sig, &siglen, &signature[0]) == 1); + CHECK(secp256k1_ecdsa_signature_serialize_der(CTX, sig, &siglen, &signature[0]) == 1); sig[secp256k1_testrand_int(siglen)] += 1 + secp256k1_testrand_int(255); - CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &signature[0], sig, siglen) == 0 || - secp256k1_ecdsa_verify(ctx, &signature[0], message, &pubkey) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(CTX, &signature[0], sig, siglen) == 0 || + secp256k1_ecdsa_verify(CTX, &signature[0], message, &pubkey) == 0); } -void test_random_pubkeys(void) { +static void test_random_pubkeys(void) { secp256k1_ge elem; secp256k1_ge elem2; unsigned char in[65]; @@ -6420,7 +6593,7 @@ void test_random_pubkeys(void) { } } -void run_pubkey_comparison(void) { +static void run_pubkey_comparison(void) { unsigned char pk1_ser[33] = { 0x02, 0x58, 0x84, 0xb3, 0xa2, 0x4b, 0x97, 0x37, 0x88, 0x92, 0x38, 0xa6, 0x26, 0x62, 0x52, 0x35, 0x11, @@ -6435,55 +6608,55 @@ void run_pubkey_comparison(void) { secp256k1_pubkey pk2; int32_t ecount = 0; - CHECK(secp256k1_ec_pubkey_parse(ctx, &pk1, pk1_ser, sizeof(pk1_ser)) == 1); - CHECK(secp256k1_ec_pubkey_parse(ctx, &pk2, pk2_ser, sizeof(pk2_ser)) == 1); + CHECK(secp256k1_ec_pubkey_parse(CTX, &pk1, pk1_ser, sizeof(pk1_ser)) == 1); + CHECK(secp256k1_ec_pubkey_parse(CTX, &pk2, pk2_ser, sizeof(pk2_ser)) == 1); - secp256k1_context_set_illegal_callback(ctx, counting_illegal_callback_fn, &ecount); - CHECK(secp256k1_ec_pubkey_cmp(ctx, NULL, &pk2) < 0); + secp256k1_context_set_illegal_callback(CTX, counting_illegal_callback_fn, &ecount); + CHECK(secp256k1_ec_pubkey_cmp(CTX, NULL, &pk2) < 0); CHECK(ecount == 1); - CHECK(secp256k1_ec_pubkey_cmp(ctx, &pk1, NULL) > 0); + CHECK(secp256k1_ec_pubkey_cmp(CTX, &pk1, NULL) > 0); CHECK(ecount == 2); - CHECK(secp256k1_ec_pubkey_cmp(ctx, &pk1, &pk2) < 0); - CHECK(secp256k1_ec_pubkey_cmp(ctx, &pk2, &pk1) > 0); - CHECK(secp256k1_ec_pubkey_cmp(ctx, &pk1, &pk1) == 0); - CHECK(secp256k1_ec_pubkey_cmp(ctx, &pk2, &pk2) == 0); + CHECK(secp256k1_ec_pubkey_cmp(CTX, &pk1, &pk2) < 0); + CHECK(secp256k1_ec_pubkey_cmp(CTX, &pk2, &pk1) > 0); + CHECK(secp256k1_ec_pubkey_cmp(CTX, &pk1, &pk1) == 0); + CHECK(secp256k1_ec_pubkey_cmp(CTX, &pk2, &pk2) == 0); CHECK(ecount == 2); { secp256k1_pubkey pk_tmp; memset(&pk_tmp, 0, sizeof(pk_tmp)); /* illegal pubkey */ - CHECK(secp256k1_ec_pubkey_cmp(ctx, &pk_tmp, &pk2) < 0); + CHECK(secp256k1_ec_pubkey_cmp(CTX, &pk_tmp, &pk2) < 0); CHECK(ecount == 3); - CHECK(secp256k1_ec_pubkey_cmp(ctx, &pk_tmp, &pk_tmp) == 0); + CHECK(secp256k1_ec_pubkey_cmp(CTX, &pk_tmp, &pk_tmp) == 0); CHECK(ecount == 5); - CHECK(secp256k1_ec_pubkey_cmp(ctx, &pk2, &pk_tmp) > 0); + CHECK(secp256k1_ec_pubkey_cmp(CTX, &pk2, &pk_tmp) > 0); CHECK(ecount == 6); } - secp256k1_context_set_illegal_callback(ctx, NULL, NULL); + secp256k1_context_set_illegal_callback(CTX, NULL, NULL); /* Make pk2 the same as pk1 but with 3 rather than 2. Note that in * an uncompressed encoding, these would have the opposite ordering */ pk1_ser[0] = 3; - CHECK(secp256k1_ec_pubkey_parse(ctx, &pk2, pk1_ser, sizeof(pk1_ser)) == 1); - CHECK(secp256k1_ec_pubkey_cmp(ctx, &pk1, &pk2) < 0); - CHECK(secp256k1_ec_pubkey_cmp(ctx, &pk2, &pk1) > 0); + CHECK(secp256k1_ec_pubkey_parse(CTX, &pk2, pk1_ser, sizeof(pk1_ser)) == 1); + CHECK(secp256k1_ec_pubkey_cmp(CTX, &pk1, &pk2) < 0); + CHECK(secp256k1_ec_pubkey_cmp(CTX, &pk2, &pk1) > 0); } -void run_random_pubkeys(void) { +static void run_random_pubkeys(void) { int i; - for (i = 0; i < 10*count; i++) { + for (i = 0; i < 10*COUNT; i++) { test_random_pubkeys(); } } -void run_ecdsa_end_to_end(void) { +static void run_ecdsa_end_to_end(void) { int i; - for (i = 0; i < 64*count; i++) { + for (i = 0; i < 64*COUNT; i++) { test_ecdsa_end_to_end(); } } -int test_ecdsa_der_parse(const unsigned char *sig, size_t siglen, int certainly_der, int certainly_not_der) { +static int test_ecdsa_der_parse(const unsigned char *sig, size_t siglen, int certainly_der, int certainly_not_der) { static const unsigned char zeroes[32] = {0}; int ret = 0; @@ -6500,23 +6673,23 @@ int test_ecdsa_der_parse(const unsigned char *sig, size_t siglen, int certainly_ size_t len_der_lax = 2048; int parsed_der_lax = 0, valid_der_lax = 0, roundtrips_der_lax = 0; - parsed_der = secp256k1_ecdsa_signature_parse_der(ctx, &sig_der, sig, siglen); + parsed_der = secp256k1_ecdsa_signature_parse_der(CTX, &sig_der, sig, siglen); if (parsed_der) { - ret |= (!secp256k1_ecdsa_signature_serialize_compact(ctx, compact_der, &sig_der)) << 0; + ret |= (!secp256k1_ecdsa_signature_serialize_compact(CTX, compact_der, &sig_der)) << 0; valid_der = (secp256k1_memcmp_var(compact_der, zeroes, 32) != 0) && (secp256k1_memcmp_var(compact_der + 32, zeroes, 32) != 0); } if (valid_der) { - ret |= (!secp256k1_ecdsa_signature_serialize_der(ctx, roundtrip_der, &len_der, &sig_der)) << 1; + ret |= (!secp256k1_ecdsa_signature_serialize_der(CTX, roundtrip_der, &len_der, &sig_der)) << 1; roundtrips_der = (len_der == siglen) && secp256k1_memcmp_var(roundtrip_der, sig, siglen) == 0; } - parsed_der_lax = ecdsa_signature_parse_der_lax(ctx, &sig_der_lax, sig, siglen); + parsed_der_lax = ecdsa_signature_parse_der_lax(CTX, &sig_der_lax, sig, siglen); if (parsed_der_lax) { - ret |= (!secp256k1_ecdsa_signature_serialize_compact(ctx, compact_der_lax, &sig_der_lax)) << 10; + ret |= (!secp256k1_ecdsa_signature_serialize_compact(CTX, compact_der_lax, &sig_der_lax)) << 10; valid_der_lax = (secp256k1_memcmp_var(compact_der_lax, zeroes, 32) != 0) && (secp256k1_memcmp_var(compact_der_lax + 32, zeroes, 32) != 0); } if (valid_der_lax) { - ret |= (!secp256k1_ecdsa_signature_serialize_der(ctx, roundtrip_der_lax, &len_der_lax, &sig_der_lax)) << 11; + ret |= (!secp256k1_ecdsa_signature_serialize_der(CTX, roundtrip_der_lax, &len_der_lax, &sig_der_lax)) << 11; roundtrips_der_lax = (len_der_lax == siglen) && secp256k1_memcmp_var(roundtrip_der_lax, sig, siglen) == 0; } @@ -6728,9 +6901,9 @@ static void random_ber_signature(unsigned char *sig, size_t *len, int* certainly CHECK(tlen == *len); } -void run_ecdsa_der_parse(void) { +static void run_ecdsa_der_parse(void) { int i,j; - for (i = 0; i < 200 * count; i++) { + for (i = 0; i < 200 * COUNT; i++) { unsigned char buffer[2048]; size_t buflen = 0; int certainly_der = 0; @@ -6760,7 +6933,7 @@ void run_ecdsa_der_parse(void) { } /* Tests several edge cases. */ -void test_ecdsa_edge_cases(void) { +static void test_ecdsa_edge_cases(void) { int t; secp256k1_ecdsa_signature sig; @@ -6774,7 +6947,7 @@ void test_ecdsa_edge_cases(void) { secp256k1_scalar_negate(&ss, &ss); secp256k1_scalar_inverse(&ss, &ss); secp256k1_scalar_set_int(&sr, 1); - secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &keyj, &sr); + secp256k1_ecmult_gen(&CTX->ecmult_gen_ctx, &keyj, &sr); secp256k1_ge_set_gej(&key, &keyj); msg = ss; CHECK(secp256k1_ecdsa_sig_verify(&sr, &ss, &key, &msg) == 0); @@ -6957,71 +7130,71 @@ void test_ecdsa_edge_cases(void) { 0x65, 0xdf, 0xdd, 0x31, 0xb9, 0x3e, 0x29, 0xa9, }; ecount = 0; - secp256k1_context_set_illegal_callback(ctx, counting_illegal_callback_fn, &ecount); - CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, key, precomputed_nonce_function, nonce) == 0); - CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, key, precomputed_nonce_function, nonce2) == 0); + secp256k1_context_set_illegal_callback(CTX, counting_illegal_callback_fn, &ecount); + CHECK(secp256k1_ecdsa_sign(CTX, &sig, msg, key, precomputed_nonce_function, nonce) == 0); + CHECK(secp256k1_ecdsa_sign(CTX, &sig, msg, key, precomputed_nonce_function, nonce2) == 0); msg[31] = 0xaa; - CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, key, precomputed_nonce_function, nonce) == 1); + CHECK(secp256k1_ecdsa_sign(CTX, &sig, msg, key, precomputed_nonce_function, nonce) == 1); CHECK(ecount == 0); - CHECK(secp256k1_ecdsa_sign(ctx, NULL, msg, key, precomputed_nonce_function, nonce2) == 0); + CHECK(secp256k1_ecdsa_sign(CTX, NULL, msg, key, precomputed_nonce_function, nonce2) == 0); CHECK(ecount == 1); - CHECK(secp256k1_ecdsa_sign(ctx, &sig, NULL, key, precomputed_nonce_function, nonce2) == 0); + CHECK(secp256k1_ecdsa_sign(CTX, &sig, NULL, key, precomputed_nonce_function, nonce2) == 0); CHECK(ecount == 2); - CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, NULL, precomputed_nonce_function, nonce2) == 0); + CHECK(secp256k1_ecdsa_sign(CTX, &sig, msg, NULL, precomputed_nonce_function, nonce2) == 0); CHECK(ecount == 3); - CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, key, precomputed_nonce_function, nonce2) == 1); - CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, key) == 1); - CHECK(secp256k1_ecdsa_verify(ctx, NULL, msg, &pubkey) == 0); + CHECK(secp256k1_ecdsa_sign(CTX, &sig, msg, key, precomputed_nonce_function, nonce2) == 1); + CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey, key) == 1); + CHECK(secp256k1_ecdsa_verify(CTX, NULL, msg, &pubkey) == 0); CHECK(ecount == 4); - CHECK(secp256k1_ecdsa_verify(ctx, &sig, NULL, &pubkey) == 0); + CHECK(secp256k1_ecdsa_verify(CTX, &sig, NULL, &pubkey) == 0); CHECK(ecount == 5); - CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg, NULL) == 0); + CHECK(secp256k1_ecdsa_verify(CTX, &sig, msg, NULL) == 0); CHECK(ecount == 6); - CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg, &pubkey) == 1); + CHECK(secp256k1_ecdsa_verify(CTX, &sig, msg, &pubkey) == 1); CHECK(ecount == 6); - CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, NULL) == 0); + CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey, NULL) == 0); CHECK(ecount == 7); /* That pubkeyload fails via an ARGCHECK is a little odd but makes sense because pubkeys are an opaque data type. */ - CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg, &pubkey) == 0); + CHECK(secp256k1_ecdsa_verify(CTX, &sig, msg, &pubkey) == 0); CHECK(ecount == 8); siglen = 72; - CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, NULL, &siglen, &sig) == 0); + CHECK(secp256k1_ecdsa_signature_serialize_der(CTX, NULL, &siglen, &sig) == 0); CHECK(ecount == 9); - CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, signature, NULL, &sig) == 0); + CHECK(secp256k1_ecdsa_signature_serialize_der(CTX, signature, NULL, &sig) == 0); CHECK(ecount == 10); - CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, signature, &siglen, NULL) == 0); + CHECK(secp256k1_ecdsa_signature_serialize_der(CTX, signature, &siglen, NULL) == 0); CHECK(ecount == 11); - CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, signature, &siglen, &sig) == 1); + CHECK(secp256k1_ecdsa_signature_serialize_der(CTX, signature, &siglen, &sig) == 1); CHECK(ecount == 11); - CHECK(secp256k1_ecdsa_signature_parse_der(ctx, NULL, signature, siglen) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(CTX, NULL, signature, siglen) == 0); CHECK(ecount == 12); - CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, NULL, siglen) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(CTX, &sig, NULL, siglen) == 0); CHECK(ecount == 13); - CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, signature, siglen) == 1); + CHECK(secp256k1_ecdsa_signature_parse_der(CTX, &sig, signature, siglen) == 1); CHECK(ecount == 13); siglen = 10; /* Too little room for a signature does not fail via ARGCHECK. */ - CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, signature, &siglen, &sig) == 0); + CHECK(secp256k1_ecdsa_signature_serialize_der(CTX, signature, &siglen, &sig) == 0); CHECK(ecount == 13); ecount = 0; - CHECK(secp256k1_ecdsa_signature_normalize(ctx, NULL, NULL) == 0); + CHECK(secp256k1_ecdsa_signature_normalize(CTX, NULL, NULL) == 0); CHECK(ecount == 1); - CHECK(secp256k1_ecdsa_signature_serialize_compact(ctx, NULL, &sig) == 0); + CHECK(secp256k1_ecdsa_signature_serialize_compact(CTX, NULL, &sig) == 0); CHECK(ecount == 2); - CHECK(secp256k1_ecdsa_signature_serialize_compact(ctx, signature, NULL) == 0); + CHECK(secp256k1_ecdsa_signature_serialize_compact(CTX, signature, NULL) == 0); CHECK(ecount == 3); - CHECK(secp256k1_ecdsa_signature_serialize_compact(ctx, signature, &sig) == 1); + CHECK(secp256k1_ecdsa_signature_serialize_compact(CTX, signature, &sig) == 1); CHECK(ecount == 3); - CHECK(secp256k1_ecdsa_signature_parse_compact(ctx, NULL, signature) == 0); + CHECK(secp256k1_ecdsa_signature_parse_compact(CTX, NULL, signature) == 0); CHECK(ecount == 4); - CHECK(secp256k1_ecdsa_signature_parse_compact(ctx, &sig, NULL) == 0); + CHECK(secp256k1_ecdsa_signature_parse_compact(CTX, &sig, NULL) == 0); CHECK(ecount == 5); - CHECK(secp256k1_ecdsa_signature_parse_compact(ctx, &sig, signature) == 1); + CHECK(secp256k1_ecdsa_signature_parse_compact(CTX, &sig, signature) == 1); CHECK(ecount == 5); memset(signature, 255, 64); - CHECK(secp256k1_ecdsa_signature_parse_compact(ctx, &sig, signature) == 0); + CHECK(secp256k1_ecdsa_signature_parse_compact(CTX, &sig, signature) == 0); CHECK(ecount == 5); - secp256k1_context_set_illegal_callback(ctx, NULL, NULL); + secp256k1_context_set_illegal_callback(CTX, NULL, NULL); } /* Nonce function corner cases. */ @@ -7038,33 +7211,33 @@ void test_ecdsa_edge_cases(void) { msg[31] = 1; /* High key results in signature failure. */ memset(key, 0xFF, 32); - CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, key, NULL, extra) == 0); + CHECK(secp256k1_ecdsa_sign(CTX, &sig, msg, key, NULL, extra) == 0); CHECK(is_empty_signature(&sig)); /* Zero key results in signature failure. */ memset(key, 0, 32); - CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, key, NULL, extra) == 0); + CHECK(secp256k1_ecdsa_sign(CTX, &sig, msg, key, NULL, extra) == 0); CHECK(is_empty_signature(&sig)); /* Nonce function failure results in signature failure. */ key[31] = 1; - CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, key, nonce_function_test_fail, extra) == 0); + CHECK(secp256k1_ecdsa_sign(CTX, &sig, msg, key, nonce_function_test_fail, extra) == 0); CHECK(is_empty_signature(&sig)); /* The retry loop successfully makes its way to the first good value. */ - CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, key, nonce_function_test_retry, extra) == 1); + CHECK(secp256k1_ecdsa_sign(CTX, &sig, msg, key, nonce_function_test_retry, extra) == 1); CHECK(!is_empty_signature(&sig)); - CHECK(secp256k1_ecdsa_sign(ctx, &sig2, msg, key, nonce_function_rfc6979, extra) == 1); + CHECK(secp256k1_ecdsa_sign(CTX, &sig2, msg, key, nonce_function_rfc6979, extra) == 1); CHECK(!is_empty_signature(&sig2)); CHECK(secp256k1_memcmp_var(&sig, &sig2, sizeof(sig)) == 0); /* The default nonce function is deterministic. */ - CHECK(secp256k1_ecdsa_sign(ctx, &sig2, msg, key, NULL, extra) == 1); + CHECK(secp256k1_ecdsa_sign(CTX, &sig2, msg, key, NULL, extra) == 1); CHECK(!is_empty_signature(&sig2)); CHECK(secp256k1_memcmp_var(&sig, &sig2, sizeof(sig)) == 0); /* The default nonce function changes output with different messages. */ for(i = 0; i < 256; i++) { int j; msg[0] = i; - CHECK(secp256k1_ecdsa_sign(ctx, &sig2, msg, key, NULL, extra) == 1); + CHECK(secp256k1_ecdsa_sign(CTX, &sig2, msg, key, NULL, extra) == 1); CHECK(!is_empty_signature(&sig2)); - secp256k1_ecdsa_signature_load(ctx, &sr[i], &ss, &sig2); + secp256k1_ecdsa_signature_load(CTX, &sr[i], &ss, &sig2); for (j = 0; j < i; j++) { CHECK(!secp256k1_scalar_eq(&sr[i], &sr[j])); } @@ -7075,9 +7248,9 @@ void test_ecdsa_edge_cases(void) { for(i = 256; i < 512; i++) { int j; key[0] = i - 256; - CHECK(secp256k1_ecdsa_sign(ctx, &sig2, msg, key, NULL, extra) == 1); + CHECK(secp256k1_ecdsa_sign(CTX, &sig2, msg, key, NULL, extra) == 1); CHECK(!is_empty_signature(&sig2)); - secp256k1_ecdsa_signature_load(ctx, &sr[i], &ss, &sig2); + secp256k1_ecdsa_signature_load(CTX, &sr[i], &ss, &sig2); for (j = 0; j < i; j++) { CHECK(!secp256k1_scalar_eq(&sr[i], &sr[j])); } @@ -7092,18 +7265,18 @@ void test_ecdsa_edge_cases(void) { unsigned char nonce2[32]; unsigned char nonce3[32]; unsigned char nonce4[32]; - VG_UNDEF(nonce,32); - VG_UNDEF(nonce2,32); - VG_UNDEF(nonce3,32); - VG_UNDEF(nonce4,32); + SECP256K1_CHECKMEM_UNDEFINE(nonce,32); + SECP256K1_CHECKMEM_UNDEFINE(nonce2,32); + SECP256K1_CHECKMEM_UNDEFINE(nonce3,32); + SECP256K1_CHECKMEM_UNDEFINE(nonce4,32); CHECK(nonce_function_rfc6979(nonce, zeros, zeros, NULL, NULL, 0) == 1); - VG_CHECK(nonce,32); + SECP256K1_CHECKMEM_CHECK(nonce,32); CHECK(nonce_function_rfc6979(nonce2, zeros, zeros, zeros, NULL, 0) == 1); - VG_CHECK(nonce2,32); + SECP256K1_CHECKMEM_CHECK(nonce2,32); CHECK(nonce_function_rfc6979(nonce3, zeros, zeros, NULL, (void *)zeros, 0) == 1); - VG_CHECK(nonce3,32); + SECP256K1_CHECKMEM_CHECK(nonce3,32); CHECK(nonce_function_rfc6979(nonce4, zeros, zeros, zeros, (void *)zeros, 0) == 1); - VG_CHECK(nonce4,32); + SECP256K1_CHECKMEM_CHECK(nonce4,32); CHECK(secp256k1_memcmp_var(nonce, nonce2, 32) != 0); CHECK(secp256k1_memcmp_var(nonce, nonce3, 32) != 0); CHECK(secp256k1_memcmp_var(nonce, nonce4, 32) != 0); @@ -7123,13 +7296,13 @@ void test_ecdsa_edge_cases(void) { 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x41, }; size_t outlen = 300; - CHECK(!ec_privkey_export_der(ctx, privkey, &outlen, seckey, 0)); + CHECK(!ec_privkey_export_der(CTX, privkey, &outlen, seckey, 0)); outlen = 300; - CHECK(!ec_privkey_export_der(ctx, privkey, &outlen, seckey, 1)); + CHECK(!ec_privkey_export_der(CTX, privkey, &outlen, seckey, 1)); } } -void run_ecdsa_edge_cases(void) { +static void run_ecdsa_edge_cases(void) { test_ecdsa_edge_cases(); } @@ -7149,7 +7322,7 @@ void run_ecdsa_edge_cases(void) { # include "modules/schnorrsig/tests_impl.h" #endif -void run_secp256k1_memczero_test(void) { +static void run_secp256k1_memczero_test(void) { unsigned char buf1[6] = {1, 2, 3, 4, 5, 6}; unsigned char buf2[sizeof(buf1)]; @@ -7164,7 +7337,7 @@ void run_secp256k1_memczero_test(void) { CHECK(secp256k1_memcmp_var(buf1, buf2, sizeof(buf1)) == 0); } -void run_secp256k1_byteorder_tests(void) { +static void run_secp256k1_byteorder_tests(void) { const uint32_t x = 0xFF03AB45; const unsigned char x_be[4] = {0xFF, 0x03, 0xAB, 0x45}; unsigned char buf[4]; @@ -7177,7 +7350,7 @@ void run_secp256k1_byteorder_tests(void) { CHECK(x == x_); } -void int_cmov_test(void) { +static void int_cmov_test(void) { int r = INT_MAX; int a = 0; @@ -7202,7 +7375,7 @@ void int_cmov_test(void) { } -void fe_cmov_test(void) { +static void fe_cmov_test(void) { static const secp256k1_fe zero = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0); static const secp256k1_fe one = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1); static const secp256k1_fe max = SECP256K1_FE_CONST( @@ -7232,7 +7405,7 @@ void fe_cmov_test(void) { CHECK(secp256k1_memcmp_var(&r, &one, sizeof(r)) == 0); } -void fe_storage_cmov_test(void) { +static void fe_storage_cmov_test(void) { static const secp256k1_fe_storage zero = SECP256K1_FE_STORAGE_CONST(0, 0, 0, 0, 0, 0, 0, 0); static const secp256k1_fe_storage one = SECP256K1_FE_STORAGE_CONST(0, 0, 0, 0, 0, 0, 0, 1); static const secp256k1_fe_storage max = SECP256K1_FE_STORAGE_CONST( @@ -7262,7 +7435,7 @@ void fe_storage_cmov_test(void) { CHECK(secp256k1_memcmp_var(&r, &one, sizeof(r)) == 0); } -void scalar_cmov_test(void) { +static void scalar_cmov_test(void) { static const secp256k1_scalar zero = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0); static const secp256k1_scalar one = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 1); static const secp256k1_scalar max = SECP256K1_SCALAR_CONST( @@ -7292,7 +7465,7 @@ void scalar_cmov_test(void) { CHECK(secp256k1_memcmp_var(&r, &one, sizeof(r)) == 0); } -void ge_storage_cmov_test(void) { +static void ge_storage_cmov_test(void) { static const secp256k1_ge_storage zero = SECP256K1_GE_STORAGE_CONST(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); static const secp256k1_ge_storage one = SECP256K1_GE_STORAGE_CONST(0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1); static const secp256k1_ge_storage max = SECP256K1_GE_STORAGE_CONST( @@ -7324,7 +7497,7 @@ void ge_storage_cmov_test(void) { CHECK(secp256k1_memcmp_var(&r, &one, sizeof(r)) == 0); } -void run_cmov_tests(void) { +static void run_cmov_tests(void) { int_cmov_test(); fe_cmov_test(); fe_storage_cmov_test(); @@ -7343,41 +7516,61 @@ int main(int argc, char **argv) { /* find iteration count */ if (argc > 1) { - count = strtol(argv[1], NULL, 0); + COUNT = strtol(argv[1], NULL, 0); } else { const char* env = getenv("SECP256K1_TEST_ITERS"); if (env && strlen(env) > 0) { - count = strtol(env, NULL, 0); + COUNT = strtol(env, NULL, 0); } } - if (count <= 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); + printf("test count = %i\n", COUNT); /* find random seed */ secp256k1_testrand_init(argc > 2 ? argv[2] : NULL); - /* initialize */ - run_selftest_tests(); - run_context_tests(0); - run_context_tests(1); - run_scratch_tests(); + /*** Setup test environment ***/ - ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + /* Create a global context available to all tests */ + CTX = secp256k1_context_create(SECP256K1_CONTEXT_NONE); /* Randomize the context only with probability 15/16 to make sure we test without context randomization from time to time. TODO Reconsider this when recalibrating the tests. */ if (secp256k1_testrand_bits(4)) { unsigned char rand32[32]; secp256k1_testrand256(rand32); - CHECK(secp256k1_context_randomize(ctx, rand32)); + CHECK(secp256k1_context_randomize(CTX, rand32)); } + /* Make a writable copy of secp256k1_context_static in order to test the effect of API functions + that write to the context. The API does not support cloning the static context, so we use + memcpy instead. The user is not supposed to copy a context but we should still ensure that + the API functions handle copies of the static context gracefully. */ + STATIC_CTX = malloc(sizeof(*secp256k1_context_static)); + CHECK(STATIC_CTX != NULL); + memcpy(STATIC_CTX, secp256k1_context_static, sizeof(secp256k1_context)); + CHECK(!secp256k1_context_is_proper(STATIC_CTX)); + + /*** Run actual tests ***/ + /* selftest tests */ + run_selftest_tests(); + + /* context tests */ + run_proper_context_tests(0); run_proper_context_tests(1); + run_static_context_tests(0); run_static_context_tests(1); + run_deprecated_context_flags_test(); + + /* scratch tests */ + run_scratch_tests(); + + /* randomness tests */ run_rand_bits(); run_rand_int(); + /* integer arithmetic tests */ #ifdef SECP256K1_WIDEMUL_INT128 run_int128_tests(); #endif @@ -7385,6 +7578,7 @@ int main(int argc, char **argv) { run_modinv_tests(); run_inverse_tests(); + /* hash tests */ run_sha256_known_output_tests(); run_sha256_counter_tests(); run_hmac_sha256_tests(); @@ -7437,6 +7631,7 @@ int main(int argc, char **argv) { #endif /* ecdsa tests */ + run_ec_illegal_argument_tests(); run_pubkey_comparison(); run_random_pubkeys(); run_ecdsa_der_parse(); @@ -7463,10 +7658,11 @@ int main(int argc, char **argv) { run_cmov_tests(); - secp256k1_testrand_finish(); + /*** Tear down test environment ***/ + free(STATIC_CTX); + secp256k1_context_destroy(CTX); - /* shutdown */ - secp256k1_context_destroy(ctx); + secp256k1_testrand_finish(); printf("no problems found\n"); return 0; diff --git a/src/secp256k1/src/tests_exhaustive.c b/src/secp256k1/src/tests_exhaustive.c index c001dcb80b..86b9334cae 100644 --- a/src/secp256k1/src/tests_exhaustive.c +++ b/src/secp256k1/src/tests_exhaustive.c @@ -4,10 +4,6 @@ * file COPYING or https://www.opensource.org/licenses/mit-license.php.* ***********************************************************************/ -#if defined HAVE_CONFIG_H -#include "libsecp256k1-config.h" -#endif - #include <stdio.h> #include <stdlib.h> #include <time.h> @@ -28,7 +24,7 @@ static int count = 2; /** stolen from tests.c */ -void ge_equals_ge(const secp256k1_ge *a, const secp256k1_ge *b) { +static void ge_equals_ge(const secp256k1_ge *a, const secp256k1_ge *b) { CHECK(a->infinity == b->infinity); if (a->infinity) { return; @@ -37,7 +33,7 @@ void ge_equals_ge(const secp256k1_ge *a, const secp256k1_ge *b) { CHECK(secp256k1_fe_equal_var(&a->y, &b->y)); } -void ge_equals_gej(const secp256k1_ge *a, const secp256k1_gej *b) { +static void ge_equals_gej(const secp256k1_ge *a, const secp256k1_gej *b) { secp256k1_fe z2s; secp256k1_fe u1, u2, s1, s2; CHECK(a->infinity == b->infinity); @@ -54,7 +50,7 @@ void ge_equals_gej(const secp256k1_ge *a, const secp256k1_gej *b) { CHECK(secp256k1_fe_equal_var(&s1, &s2)); } -void random_fe(secp256k1_fe *x) { +static void random_fe(secp256k1_fe *x) { unsigned char bin[32]; do { secp256k1_testrand256(bin); @@ -74,7 +70,7 @@ SECP256K1_INLINE static int skip_section(uint64_t* iter) { return ((((uint32_t)*iter ^ (*iter >> 32)) * num_cores) >> 32) != this_core; } -int secp256k1_nonce_function_smallint(unsigned char *nonce32, const unsigned char *msg32, +static int secp256k1_nonce_function_smallint(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int attempt) { secp256k1_scalar s; @@ -94,7 +90,7 @@ int secp256k1_nonce_function_smallint(unsigned char *nonce32, const unsigned cha return 1; } -void test_exhaustive_endomorphism(const secp256k1_ge *group) { +static void test_exhaustive_endomorphism(const secp256k1_ge *group) { int i; for (i = 0; i < EXHAUSTIVE_TEST_ORDER; i++) { secp256k1_ge res; @@ -103,7 +99,7 @@ void test_exhaustive_endomorphism(const secp256k1_ge *group) { } } -void test_exhaustive_addition(const secp256k1_ge *group, const secp256k1_gej *groupj) { +static void test_exhaustive_addition(const secp256k1_ge *group, const secp256k1_gej *groupj) { int i, j; uint64_t iter = 0; @@ -163,7 +159,7 @@ void test_exhaustive_addition(const secp256k1_ge *group, const secp256k1_gej *gr } } -void test_exhaustive_ecmult(const secp256k1_ge *group, const secp256k1_gej *groupj) { +static void test_exhaustive_ecmult(const secp256k1_ge *group, const secp256k1_gej *groupj) { int i, j, r_log; uint64_t iter = 0; for (r_log = 1; r_log < EXHAUSTIVE_TEST_ORDER; r_log++) { @@ -199,7 +195,7 @@ static int ecmult_multi_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t return 1; } -void test_exhaustive_ecmult_multi(const secp256k1_context *ctx, const secp256k1_ge *group) { +static void test_exhaustive_ecmult_multi(const secp256k1_context *ctx, const secp256k1_ge *group) { int i, j, k, x, y; uint64_t iter = 0; secp256k1_scratch *scratch = secp256k1_scratch_create(&ctx->error_callback, 4096); @@ -229,7 +225,7 @@ void test_exhaustive_ecmult_multi(const secp256k1_context *ctx, const secp256k1_ secp256k1_scratch_destroy(&ctx->error_callback, scratch); } -void r_from_k(secp256k1_scalar *r, const secp256k1_ge *group, int k, int* overflow) { +static void r_from_k(secp256k1_scalar *r, const secp256k1_ge *group, int k, int* overflow) { secp256k1_fe x; unsigned char x_bin[32]; k %= EXHAUSTIVE_TEST_ORDER; @@ -239,7 +235,7 @@ void r_from_k(secp256k1_scalar *r, const secp256k1_ge *group, int k, int* overfl secp256k1_scalar_set_b32(r, x_bin, overflow); } -void test_exhaustive_verify(const secp256k1_context *ctx, const secp256k1_ge *group) { +static void test_exhaustive_verify(const secp256k1_context *ctx, const secp256k1_ge *group) { int s, r, msg, key; uint64_t iter = 0; for (s = 1; s < EXHAUSTIVE_TEST_ORDER; s++) { @@ -292,7 +288,7 @@ void test_exhaustive_verify(const secp256k1_context *ctx, const secp256k1_ge *gr } } -void test_exhaustive_sign(const secp256k1_context *ctx, const secp256k1_ge *group) { +static void test_exhaustive_sign(const secp256k1_context *ctx, const secp256k1_ge *group) { int i, j, k; uint64_t iter = 0; diff --git a/src/secp256k1/src/util.h b/src/secp256k1/src/util.h index 864baaee4d..e75c5ad552 100644 --- a/src/secp256k1/src/util.h +++ b/src/secp256k1/src/util.h @@ -7,10 +7,6 @@ #ifndef SECP256K1_UTIL_H #define SECP256K1_UTIL_H -#if defined HAVE_CONFIG_H -#include "libsecp256k1-config.h" -#endif - #include <stdlib.h> #include <stdint.h> #include <stdio.h> @@ -101,25 +97,6 @@ static const secp256k1_callback default_error_callback = { #define VERIFY_SETUP(stmt) #endif -/* Define `VG_UNDEF` and `VG_CHECK` when VALGRIND is defined */ -#if !defined(VG_CHECK) -# if defined(VALGRIND) -# include <valgrind/memcheck.h> -# define VG_UNDEF(x,y) VALGRIND_MAKE_MEM_UNDEFINED((x),(y)) -# define VG_CHECK(x,y) VALGRIND_CHECK_MEM_IS_DEFINED((x),(y)) -# else -# define VG_UNDEF(x,y) -# define VG_CHECK(x,y) -# endif -#endif - -/* Like `VG_CHECK` but on VERIFY only */ -#if defined(VERIFY) -#define VG_CHECK_VERIFY(x,y) VG_CHECK((x), (y)) -#else -#define VG_CHECK_VERIFY(x,y) -#endif - static SECP256K1_INLINE void *checked_malloc(const secp256k1_callback* cb, size_t size) { void *ret = malloc(size); if (ret == NULL) { @@ -274,7 +251,7 @@ static SECP256K1_INLINE int secp256k1_ctz32_var_debruijn(uint32_t x) { 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]; + return debruijn[(uint32_t)((x & -x) * 0x04D7651FU) >> 27]; } /* Determine the number of trailing zero bits in a (non-zero) 64-bit x. @@ -287,7 +264,7 @@ static SECP256K1_INLINE int secp256k1_ctz64_var_debruijn(uint64_t x) { 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]; + return debruijn[(uint64_t)((x & -x) * 0x022FDD63CC95386DU) >> 58]; } /* Determine the number of trailing zero bits in a (non-zero) 32-bit x. */ diff --git a/src/shutdown.cpp b/src/shutdown.cpp index 57d6d2325d..2fffc0663c 100644 --- a/src/shutdown.cpp +++ b/src/shutdown.cpp @@ -27,7 +27,7 @@ bool AbortNode(const std::string& strMessage, bilingual_str user_message) if (user_message.empty()) { user_message = _("A fatal internal error occurred, see debug.log for details"); } - AbortError(user_message); + InitError(user_message); StartShutdown(); return false; } diff --git a/src/test/blockmanager_tests.cpp b/src/test/blockmanager_tests.cpp index d5825d0d7e..2118f476cd 100644 --- a/src/test/blockmanager_tests.cpp +++ b/src/test/blockmanager_tests.cpp @@ -21,7 +21,7 @@ BOOST_FIXTURE_TEST_SUITE(blockmanager_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(blockmanager_find_block_pos) { const auto params {CreateChainParams(ArgsManager{}, CBaseChainParams::MAIN)}; - BlockManager blockman {}; + BlockManager blockman{{}}; CChain chain {}; // simulate adding a genesis block normally BOOST_CHECK_EQUAL(blockman.SaveBlockToDisk(params->GenesisBlock(), 0, chain, *params, nullptr).nPos, BLOCK_SERIALIZATION_HEADER_SIZE); diff --git a/src/test/settings_tests.cpp b/src/test/settings_tests.cpp index 0feb68b9b1..ad12c46561 100644 --- a/src/test/settings_tests.cpp +++ b/src/test/settings_tests.cpp @@ -80,7 +80,7 @@ BOOST_AUTO_TEST_CASE(ReadWrite) BOOST_CHECK(values.empty()); BOOST_CHECK(errors.empty()); - // Check duplicate keys not allowed + // Check duplicate keys not allowed and that values returns empty if a duplicate is found. WriteText(path, R"({ "dupe": "string", "dupe": "dupe" @@ -88,6 +88,7 @@ BOOST_AUTO_TEST_CASE(ReadWrite) BOOST_CHECK(!util::ReadSettings(path, values, errors)); std::vector<std::string> dup_keys = {strprintf("Found duplicate key dupe in settings file %s", fs::PathToString(path))}; BOOST_CHECK_EQUAL_COLLECTIONS(errors.begin(), errors.end(), dup_keys.begin(), dup_keys.end()); + BOOST_CHECK(values.empty()); // Check non-kv json files not allowed WriteText(path, R"("non-kv")"); diff --git a/src/test/system_tests.cpp b/src/test/system_tests.cpp index f2dc6ee739..c0a2566959 100644 --- a/src/test/system_tests.cpp +++ b/src/test/system_tests.cpp @@ -34,6 +34,13 @@ BOOST_AUTO_TEST_CASE(dummy) BOOST_AUTO_TEST_CASE(run_command) { +#ifdef WIN32 + // https://www.winehq.org/pipermail/wine-devel/2008-September/069387.html + auto hntdll = GetModuleHandleA("ntdll.dll"); + assert(hntdll); + const bool wine_runtime = GetProcAddress(hntdll, "wine_get_version"); +#endif + { const UniValue result = RunCommandParseJSON(""); BOOST_CHECK(result.isNull()); @@ -51,21 +58,27 @@ BOOST_AUTO_TEST_CASE(run_command) } { // An invalid command is handled by Boost +#ifdef WIN32 + const int expected_error{wine_runtime ? 6 : 2}; +#else + const int expected_error{2}; +#endif BOOST_CHECK_EXCEPTION(RunCommandParseJSON("invalid_command"), boost::process::process_error, [&](const boost::process::process_error& e) { BOOST_CHECK(std::string(e.what()).find("RunCommandParseJSON error:") == std::string::npos); - BOOST_CHECK_EQUAL(e.code().value(), 2); + BOOST_CHECK_EQUAL(e.code().value(), expected_error); return true; }); } { // Return non-zero exit code, no output to stderr #ifdef WIN32 - const std::string command{"cmd.exe /c call"}; + const std::string command{"cmd.exe /c exit 1"}; #else const std::string command{"false"}; #endif BOOST_CHECK_EXCEPTION(RunCommandParseJSON(command), std::runtime_error, [&](const std::runtime_error& e) { - BOOST_CHECK(std::string(e.what()).find(strprintf("RunCommandParseJSON error: process(%s) returned 1: \n", command)) != std::string::npos); + const std::string what{e.what()}; + BOOST_CHECK(what.find(strprintf("RunCommandParseJSON error: process(%s) returned 1: \n", command)) != std::string::npos); return true; }); } @@ -73,7 +86,7 @@ BOOST_AUTO_TEST_CASE(run_command) // Return non-zero exit code, with error message for stderr #ifdef WIN32 const std::string command{"cmd.exe /c dir nosuchfile"}; - const std::string expected{"File Not Found"}; + const std::string expected{wine_runtime ? "File not found." : "File Not Found"}; #else const std::string command{"ls nosuchfile"}; const std::string expected{"No such file or directory"}; diff --git a/src/test/translation_tests.cpp b/src/test/translation_tests.cpp new file mode 100644 index 0000000000..bda5dfd099 --- /dev/null +++ b/src/test/translation_tests.cpp @@ -0,0 +1,21 @@ +// Copyright (c) 2023 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 <tinyformat.h> +#include <util/translation.h> + +#include <boost/test/unit_test.hpp> + +BOOST_AUTO_TEST_SUITE(translation_tests) + +BOOST_AUTO_TEST_CASE(translation_namedparams) +{ + bilingual_str arg{"original", "translated"}; + bilingual_str format{"original [%s]", "translated [%s]"}; + bilingual_str result{strprintf(format, arg)}; + BOOST_CHECK_EQUAL(result.original, "original [original]"); + BOOST_CHECK_EQUAL(result.translated, "translated [translated]"); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index 4e0000cb3d..58593c9d5b 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -184,7 +184,7 @@ ChainTestingSetup::ChainTestingSetup(const std::string& chainName, const std::ve .adjusted_time_callback = GetAdjustedTime, .check_block_index = true, }; - m_node.chainman = std::make_unique<ChainstateManager>(chainman_opts); + m_node.chainman = std::make_unique<ChainstateManager>(chainman_opts, node::BlockManager::Options{}); m_node.chainman->m_blockman.m_block_tree_db = std::make_unique<CBlockTreeDB>(DBParams{ .path = m_args.GetDataDirNet() / "blocks" / "index", .cache_bytes = static_cast<size_t>(m_cache_sizes.block_tree_db), diff --git a/src/test/validation_chainstatemanager_tests.cpp b/src/test/validation_chainstatemanager_tests.cpp index 78301c7c14..688aafdd46 100644 --- a/src/test/validation_chainstatemanager_tests.cpp +++ b/src/test/validation_chainstatemanager_tests.cpp @@ -381,7 +381,7 @@ struct SnapshotTestSetup : TestChain100Setup { // For robustness, ensure the old manager is destroyed before creating a // new one. m_node.chainman.reset(); - m_node.chainman.reset(new ChainstateManager(chainman_opts)); + m_node.chainman = std::make_unique<ChainstateManager>(chainman_opts, node::BlockManager::Options{}); } return *Assert(m_node.chainman); } @@ -474,9 +474,10 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_loadblockindex, TestChain100Setup) //! Ensure that snapshot chainstates initialize properly when found on disk. BOOST_FIXTURE_TEST_CASE(chainstatemanager_snapshot_init, SnapshotTestSetup) { - this->SetupSnapshot(); - ChainstateManager& chainman = *Assert(m_node.chainman); + Chainstate& bg_chainstate = chainman.ActiveChainstate(); + + this->SetupSnapshot(); fs::path snapshot_chainstate_dir = *node::FindSnapshotChainstateDir(); BOOST_CHECK(fs::exists(snapshot_chainstate_dir)); @@ -489,6 +490,20 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_snapshot_init, SnapshotTestSetup) auto all_chainstates = chainman.GetAll(); BOOST_CHECK_EQUAL(all_chainstates.size(), 2); + // "Rewind" the background chainstate so that its tip is not at the + // base block of the snapshot - this is so after simulating a node restart, + // it will initialize instead of attempting to complete validation. + // + // Note that this is not a realistic use of DisconnectTip(). + DisconnectedBlockTransactions unused_pool; + BlockValidationState unused_state; + { + LOCK2(::cs_main, bg_chainstate.MempoolMutex()); + BOOST_CHECK(bg_chainstate.DisconnectTip(unused_state, &unused_pool)); + unused_pool.clear(); // to avoid queuedTx assertion errors on teardown + } + BOOST_CHECK_EQUAL(bg_chainstate.m_chain.Height(), 109); + // Test that simulating a shutdown (resetting ChainstateManager) and then performing // chainstate reinitializing successfully cleans up the background-validation // chainstate data, and we end up with a single chainstate that is at tip. @@ -520,10 +535,160 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_snapshot_init, SnapshotTestSetup) // chainstate. for (Chainstate* cs : chainman_restarted.GetAll()) { if (cs != &chainman_restarted.ActiveChainstate()) { - BOOST_CHECK_EQUAL(cs->m_chain.Height(), 110); + BOOST_CHECK_EQUAL(cs->m_chain.Height(), 109); } } } } +BOOST_FIXTURE_TEST_CASE(chainstatemanager_snapshot_completion, SnapshotTestSetup) +{ + this->SetupSnapshot(); + + ChainstateManager& chainman = *Assert(m_node.chainman); + Chainstate& active_cs = chainman.ActiveChainstate(); + auto tip_cache_before_complete = active_cs.m_coinstip_cache_size_bytes; + auto db_cache_before_complete = active_cs.m_coinsdb_cache_size_bytes; + + SnapshotCompletionResult res; + auto mock_shutdown = [](bilingual_str msg) {}; + + fs::path snapshot_chainstate_dir = *node::FindSnapshotChainstateDir(); + BOOST_CHECK(fs::exists(snapshot_chainstate_dir)); + BOOST_CHECK_EQUAL(snapshot_chainstate_dir, gArgs.GetDataDirNet() / "chainstate_snapshot"); + + BOOST_CHECK(chainman.IsSnapshotActive()); + const uint256 snapshot_tip_hash = WITH_LOCK(chainman.GetMutex(), + return chainman.ActiveTip()->GetBlockHash()); + + res = WITH_LOCK(::cs_main, + return chainman.MaybeCompleteSnapshotValidation(mock_shutdown)); + BOOST_CHECK_EQUAL(res, SnapshotCompletionResult::SUCCESS); + + WITH_LOCK(::cs_main, BOOST_CHECK(chainman.IsSnapshotValidated())); + BOOST_CHECK(chainman.IsSnapshotActive()); + + // Cache should have been rebalanced and reallocated to the "only" remaining + // chainstate. + BOOST_CHECK(active_cs.m_coinstip_cache_size_bytes > tip_cache_before_complete); + BOOST_CHECK(active_cs.m_coinsdb_cache_size_bytes > db_cache_before_complete); + + auto all_chainstates = chainman.GetAll(); + BOOST_CHECK_EQUAL(all_chainstates.size(), 1); + BOOST_CHECK_EQUAL(all_chainstates[0], &active_cs); + + // Trying completion again should return false. + res = WITH_LOCK(::cs_main, + return chainman.MaybeCompleteSnapshotValidation(mock_shutdown)); + BOOST_CHECK_EQUAL(res, SnapshotCompletionResult::SKIPPED); + + // The invalid snapshot path should not have been used. + fs::path snapshot_invalid_dir = gArgs.GetDataDirNet() / "chainstate_snapshot_INVALID"; + BOOST_CHECK(!fs::exists(snapshot_invalid_dir)); + // chainstate_snapshot should still exist. + BOOST_CHECK(fs::exists(snapshot_chainstate_dir)); + + // Test that simulating a shutdown (resetting ChainstateManager) and then performing + // chainstate reinitializing successfully cleans up the background-validation + // chainstate data, and we end up with a single chainstate that is at tip. + ChainstateManager& chainman_restarted = this->SimulateNodeRestart(); + + BOOST_TEST_MESSAGE("Performing Load/Verify/Activate of chainstate"); + + // This call reinitializes the chainstates, and should clean up the now unnecessary + // background-validation leveldb contents. + this->LoadVerifyActivateChainstate(); + + BOOST_CHECK(!fs::exists(snapshot_invalid_dir)); + // chainstate_snapshot should now *not* exist. + BOOST_CHECK(!fs::exists(snapshot_chainstate_dir)); + + const Chainstate& active_cs2 = chainman_restarted.ActiveChainstate(); + + { + LOCK(chainman_restarted.GetMutex()); + BOOST_CHECK_EQUAL(chainman_restarted.GetAll().size(), 1); + BOOST_CHECK(!chainman_restarted.IsSnapshotActive()); + BOOST_CHECK(!chainman_restarted.IsSnapshotValidated()); + BOOST_CHECK(active_cs2.m_coinstip_cache_size_bytes > tip_cache_before_complete); + BOOST_CHECK(active_cs2.m_coinsdb_cache_size_bytes > db_cache_before_complete); + + BOOST_CHECK_EQUAL(chainman_restarted.ActiveTip()->GetBlockHash(), snapshot_tip_hash); + BOOST_CHECK_EQUAL(chainman_restarted.ActiveHeight(), 210); + } + + BOOST_TEST_MESSAGE( + "Ensure we can mine blocks on top of the \"new\" IBD chainstate"); + mineBlocks(10); + { + LOCK(chainman_restarted.GetMutex()); + BOOST_CHECK_EQUAL(chainman_restarted.ActiveHeight(), 220); + } +} + +BOOST_FIXTURE_TEST_CASE(chainstatemanager_snapshot_completion_hash_mismatch, SnapshotTestSetup) +{ + auto chainstates = this->SetupSnapshot(); + Chainstate& validation_chainstate = *std::get<0>(chainstates); + ChainstateManager& chainman = *Assert(m_node.chainman); + SnapshotCompletionResult res; + auto mock_shutdown = [](bilingual_str msg) {}; + + // Test tampering with the IBD UTXO set with an extra coin to ensure it causes + // snapshot completion to fail. + CCoinsViewCache& ibd_coins = WITH_LOCK(::cs_main, + return validation_chainstate.CoinsTip()); + Coin badcoin; + badcoin.out.nValue = InsecureRand32(); + badcoin.nHeight = 1; + badcoin.out.scriptPubKey.assign(InsecureRandBits(6), 0); + uint256 txid = InsecureRand256(); + ibd_coins.AddCoin(COutPoint(txid, 0), std::move(badcoin), false); + + fs::path snapshot_chainstate_dir = gArgs.GetDataDirNet() / "chainstate_snapshot"; + BOOST_CHECK(fs::exists(snapshot_chainstate_dir)); + + res = WITH_LOCK(::cs_main, + return chainman.MaybeCompleteSnapshotValidation(mock_shutdown)); + BOOST_CHECK_EQUAL(res, SnapshotCompletionResult::HASH_MISMATCH); + + auto all_chainstates = chainman.GetAll(); + BOOST_CHECK_EQUAL(all_chainstates.size(), 1); + BOOST_CHECK_EQUAL(all_chainstates[0], &validation_chainstate); + BOOST_CHECK_EQUAL(&chainman.ActiveChainstate(), &validation_chainstate); + + fs::path snapshot_invalid_dir = gArgs.GetDataDirNet() / "chainstate_snapshot_INVALID"; + BOOST_CHECK(fs::exists(snapshot_invalid_dir)); + + // Test that simulating a shutdown (resetting ChainstateManager) and then performing + // chainstate reinitializing successfully loads only the fully-validated + // chainstate data, and we end up with a single chainstate that is at tip. + ChainstateManager& chainman_restarted = this->SimulateNodeRestart(); + + BOOST_TEST_MESSAGE("Performing Load/Verify/Activate of chainstate"); + + // This call reinitializes the chainstates, and should clean up the now unnecessary + // background-validation leveldb contents. + this->LoadVerifyActivateChainstate(); + + BOOST_CHECK(fs::exists(snapshot_invalid_dir)); + BOOST_CHECK(!fs::exists(snapshot_chainstate_dir)); + + { + LOCK(::cs_main); + BOOST_CHECK_EQUAL(chainman_restarted.GetAll().size(), 1); + BOOST_CHECK(!chainman_restarted.IsSnapshotActive()); + BOOST_CHECK(!chainman_restarted.IsSnapshotValidated()); + BOOST_CHECK_EQUAL(chainman_restarted.ActiveHeight(), 210); + } + + BOOST_TEST_MESSAGE( + "Ensure we can mine blocks on top of the \"new\" IBD chainstate"); + mineBlocks(10); + { + LOCK(::cs_main); + BOOST_CHECK_EQUAL(chainman_restarted.ActiveHeight(), 220); + } +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/txdb.cpp b/src/txdb.cpp index 7257fb4959..15351a4355 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -6,11 +6,11 @@ #include <txdb.h> #include <chain.h> +#include <logging.h> #include <pow.h> #include <random.h> #include <shutdown.h> #include <uint256.h> -#include <util/system.h> #include <util/translation.h> #include <util/vector.h> diff --git a/src/util/exception.cpp b/src/util/exception.cpp new file mode 100644 index 0000000000..d961f0540f --- /dev/null +++ b/src/util/exception.cpp @@ -0,0 +1,41 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2023 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 <util/exception.h> + +#include <logging.h> +#include <tinyformat.h> + +#include <exception> +#include <iostream> +#include <string> +#include <typeinfo> + +#ifdef WIN32 +#include <windows.h> +#endif // WIN32 + +static std::string FormatException(const std::exception* pex, std::string_view thread_name) +{ +#ifdef WIN32 + char pszModule[MAX_PATH] = ""; + GetModuleFileNameA(nullptr, pszModule, sizeof(pszModule)); +#else + const char* pszModule = "bitcoin"; +#endif + if (pex) + return strprintf( + "EXCEPTION: %s \n%s \n%s in %s \n", typeid(*pex).name(), pex->what(), pszModule, thread_name); + else + return strprintf( + "UNKNOWN EXCEPTION \n%s in %s \n", pszModule, thread_name); +} + +void PrintExceptionContinue(const std::exception* pex, std::string_view thread_name) +{ + std::string message = FormatException(pex, thread_name); + LogPrintf("\n\n************************\n%s\n", message); + tfm::format(std::cerr, "\n\n************************\n%s\n", message); +} diff --git a/src/util/exception.h b/src/util/exception.h new file mode 100644 index 0000000000..946eb2644c --- /dev/null +++ b/src/util/exception.h @@ -0,0 +1,14 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2023 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_UTIL_EXCEPTION_H +#define BITCOIN_UTIL_EXCEPTION_H + +#include <exception> +#include <string_view> + +void PrintExceptionContinue(const std::exception* pex, std::string_view thread_name); + +#endif // BITCOIN_UTIL_EXCEPTION_H diff --git a/src/util/settings.cpp b/src/util/settings.cpp index 2b25b7f0e1..a2e30098dc 100644 --- a/src/util/settings.cpp +++ b/src/util/settings.cpp @@ -99,6 +99,8 @@ bool ReadSettings(const fs::path& path, std::map<std::string, SettingsValue>& va auto inserted = values.emplace(in_keys[i], in_values[i]); if (!inserted.second) { errors.emplace_back(strprintf("Found duplicate key %s in settings file %s", in_keys[i], fs::PathToString(path))); + values.clear(); + break; } } return errors.empty(); diff --git a/src/util/strencodings.cpp b/src/util/strencodings.cpp index 03459dcf20..a54f408496 100644 --- a/src/util/strencodings.cpp +++ b/src/util/strencodings.cpp @@ -95,8 +95,8 @@ std::optional<std::vector<Byte>> TryParseHex(std::string_view str) } return vch; } -template std::vector<std::byte> ParseHex(std::string_view); -template std::vector<uint8_t> ParseHex(std::string_view); +template std::optional<std::vector<std::byte>> TryParseHex(std::string_view); +template std::optional<std::vector<uint8_t>> TryParseHex(std::string_view); bool SplitHostPort(std::string_view in, uint16_t& portOut, std::string& hostOut) { diff --git a/src/util/system.cpp b/src/util/system.cpp index 58afd264ae..98e89f82e7 100644 --- a/src/util/system.cpp +++ b/src/util/system.cpp @@ -438,27 +438,6 @@ const fs::path& ArgsManager::GetDataDir(bool net_specific) const return path; } -void ArgsManager::EnsureDataDir() const -{ - /** - * "/wallets" subdirectories are created in all **new** - * datadirs, because wallet code will create new wallets in the "wallets" - * subdirectory only if exists already, otherwise it will create them in - * the top-level datadir where they could interfere with other files. - * Wallet init code currently avoids creating "wallets" directories itself - * for backwards compatibility, but this be changed in the future and - * wallet code here could go away (#16220). - */ - auto path{GetDataDir(false)}; - if (!fs::exists(path)) { - fs::create_directories(path / "wallets"); - } - path = GetDataDir(true); - if (!fs::exists(path)) { - fs::create_directories(path / "wallets"); - } -} - void ArgsManager::ClearPathCache() { LOCK(cs_args); @@ -502,25 +481,6 @@ bool ArgsManager::IsArgSet(const std::string& strArg) const return !GetSetting(strArg).isNull(); } -bool ArgsManager::InitSettings(std::string& error) -{ - EnsureDataDir(); - if (!GetSettingsPath()) { - return true; // Do nothing if settings file disabled. - } - - std::vector<std::string> errors; - if (!ReadSettingsFile(&errors)) { - error = strprintf("Failed loading settings file:\n%s\n", MakeUnorderedList(errors)); - return false; - } - if (!WriteSettingsFile(&errors)) { - error = strprintf("Failed saving settings file:\n%s\n", MakeUnorderedList(errors)); - return false; - } - return true; -} - bool ArgsManager::GetSettingsPath(fs::path* filepath, bool temp, bool backup) const { fs::path settings = GetPathArg("-settings", BITCOIN_SETTINGS_FILENAME); @@ -832,29 +792,6 @@ std::string HelpMessageOpt(const std::string &option, const std::string &message std::string("\n\n"); } -static std::string FormatException(const std::exception* pex, std::string_view thread_name) -{ -#ifdef WIN32 - char pszModule[MAX_PATH] = ""; - GetModuleFileNameA(nullptr, pszModule, sizeof(pszModule)); -#else - const char* pszModule = "bitcoin"; -#endif - if (pex) - return strprintf( - "EXCEPTION: %s \n%s \n%s in %s \n", typeid(*pex).name(), pex->what(), pszModule, thread_name); - else - return strprintf( - "UNKNOWN EXCEPTION \n%s in %s \n", pszModule, thread_name); -} - -void PrintExceptionContinue(const std::exception* pex, std::string_view thread_name) -{ - std::string message = FormatException(pex, thread_name); - LogPrintf("\n\n************************\n%s\n", message); - tfm::format(std::cerr, "\n\n************************\n%s\n", message); -} - fs::path GetDefaultDataDir() { // Windows: C:\Users\Username\AppData\Roaming\Bitcoin diff --git a/src/util/system.h b/src/util/system.h index 3eb0a0f2b8..7292262bea 100644 --- a/src/util/system.h +++ b/src/util/system.h @@ -19,12 +19,10 @@ #include <fs.h> #include <logging.h> #include <sync.h> -#include <tinyformat.h> #include <util/settings.h> #include <util/time.h> #include <any> -#include <exception> #include <map> #include <optional> #include <set> @@ -45,15 +43,6 @@ extern const char * const BITCOIN_SETTINGS_FILENAME; void SetupEnvironment(); bool SetupNetworking(); -template<typename... Args> -bool error(const char* fmt, const Args&... args) -{ - LogPrintf("ERROR: %s\n", tfm::format(fmt, args...)); - return false; -} - -void PrintExceptionContinue(const std::exception* pex, std::string_view thread_name); - /** * Ensure file contents are fully committed to disk, using a platform-specific * feature analogous to fsync(). @@ -435,13 +424,6 @@ protected: std::optional<unsigned int> GetArgFlags(const std::string& name) const; /** - * Read and update settings file with saved settings. This needs to be - * called after SelectParams() because the settings file location is - * network-specific. - */ - bool InitSettings(std::string& error); - - /** * Get settings file path, or return false if read-write settings were * disabled with -nosettings. */ @@ -480,12 +462,6 @@ protected: */ void LogArgs() const; - /** - * If datadir does not exist, create it along with wallets/ - * subdirectory(s). - */ - void EnsureDataDir() const; - private: /** * Get data directory path diff --git a/src/util/thread.cpp b/src/util/thread.cpp index e9f611bc50..f380d29f7a 100644 --- a/src/util/thread.cpp +++ b/src/util/thread.cpp @@ -5,7 +5,7 @@ #include <util/thread.h> #include <logging.h> -#include <util/system.h> +#include <util/exception.h> #include <util/threadnames.h> #include <exception> diff --git a/src/util/time.h b/src/util/time.h index d45baaa378..fcf85c1e03 100644 --- a/src/util/time.h +++ b/src/util/time.h @@ -8,7 +8,7 @@ #include <compat/compat.h> -#include <chrono> +#include <chrono> // IWYU pragma: export #include <cstdint> #include <string> diff --git a/src/util/translation.h b/src/util/translation.h index 05e7da0b5a..d2b49d00b0 100644 --- a/src/util/translation.h +++ b/src/util/translation.h @@ -47,11 +47,24 @@ inline bilingual_str operator+(bilingual_str lhs, const bilingual_str& rhs) /** Mark a bilingual_str as untranslated */ inline bilingual_str Untranslated(std::string original) { return {original, original}; } +// Provide an overload of tinyformat::format which can take bilingual_str arguments. namespace tinyformat { +inline std::string TranslateArg(const bilingual_str& arg, bool translated) +{ + return translated ? arg.translated : arg.original; +} + +template <typename T> +inline T const& TranslateArg(const T& arg, bool translated) +{ + return arg; +} + template <typename... Args> bilingual_str format(const bilingual_str& fmt, const Args&... args) { - return bilingual_str{format(fmt.original, args...), format(fmt.translated, args...)}; + return bilingual_str{format(fmt.original, TranslateArg(args, false)...), + format(fmt.translated, TranslateArg(args, true)...)}; } } // namespace tinyformat diff --git a/src/validation.cpp b/src/validation.cpp index 0674454883..5b3a099dfc 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -10,7 +10,6 @@ #include <arith_uint256.h> #include <chain.h> -#include <chainparams.h> #include <checkqueue.h> #include <consensus/amount.h> #include <consensus/consensus.h> @@ -22,6 +21,7 @@ #include <flatfile.h> #include <fs.h> #include <hash.h> +#include <kernel/chainparams.h> #include <kernel/mempool_entry.h> #include <logging.h> #include <logging/timer.h> @@ -2478,12 +2478,12 @@ bool Chainstate::FlushStateToDisk( } } } - const auto nNow = GetTime<std::chrono::microseconds>(); + const auto nNow{SteadyClock::now()}; // Avoid writing/flushing immediately after startup. - if (m_last_write.count() == 0) { + if (m_last_write == decltype(m_last_write){}) { m_last_write = nNow; } - if (m_last_flush.count() == 0) { + if (m_last_flush == decltype(m_last_flush){}) { m_last_flush = nNow; } // The cache is large and we're within 10% and 10 MiB of the limit, but we have time now (not in the middle of a block processing). @@ -2544,7 +2544,7 @@ bool Chainstate::FlushStateToDisk( m_last_flush = nNow; full_flush_completed = true; TRACE5(utxocache, flush, - (int64_t)(GetTimeMicros() - nNow.count()), // in microseconds (µs) + int64_t{Ticks<std::chrono::microseconds>(SteadyClock::now() - nNow)}, (uint32_t)mode, (uint64_t)coins_count, (uint64_t)coins_mem_usage, @@ -2875,6 +2875,14 @@ bool Chainstate::ConnectTip(BlockValidationState& state, CBlockIndex* pindexNew, Ticks<SecondsDouble>(time_total), Ticks<MillisecondsDouble>(time_total) / num_blocks_total); + // If we are the background validation chainstate, check to see if we are done + // validating the snapshot (i.e. our tip has reached the snapshot's base block). + if (this != &m_chainman.ActiveChainstate()) { + // This call may set `m_disabled`, which is referenced immediately afterwards in + // ActivateBestChain, so that we stop connecting blocks past the snapshot base. + m_chainman.MaybeCompleteSnapshotValidation(); + } + connectTrace.BlockConnected(pindexNew, std::move(pthisBlock)); return true; } @@ -3097,6 +3105,14 @@ bool Chainstate::ActivateBestChain(BlockValidationState& state, std::shared_ptr< // we use m_chainstate_mutex to enforce mutual exclusion so that only one caller may execute this function at a time LOCK(m_chainstate_mutex); + // Belt-and-suspenders check that we aren't attempting to advance the background + // chainstate past the snapshot base block. + if (WITH_LOCK(::cs_main, return m_disabled)) { + LogPrintf("m_disabled is set - this chainstate should not be in operation. " /* Continued */ + "Please report this as a bug. %s\n", PACKAGE_BUGREPORT); + return false; + } + CBlockIndex *pindexMostWork = nullptr; CBlockIndex *pindexNewTip = nullptr; int nStopAtHeight = gArgs.GetIntArg("-stopatheight", DEFAULT_STOPATHEIGHT); @@ -3147,6 +3163,15 @@ bool Chainstate::ActivateBestChain(BlockValidationState& state, std::shared_ptr< assert(trace.pblock && trace.pindex); GetMainSignals().BlockConnected(trace.pblock, trace.pindex); } + + // This will have been toggled in + // ActivateBestChainStep -> ConnectTip -> MaybeCompleteSnapshotValidation, + // if at all, so we should catch it here. + // + // Break this do-while to ensure we don't advance past the base snapshot. + if (m_disabled) { + break; + } } while (!m_chain.Tip() || (starting_tip && CBlockIndexWorkComparator()(m_chain.Tip(), starting_tip))); if (!blocks_connected) return true; @@ -3167,6 +3192,11 @@ bool Chainstate::ActivateBestChain(BlockValidationState& state, std::shared_ptr< if (nStopAtHeight && pindexNewTip && pindexNewTip->nHeight >= nStopAtHeight) StartShutdown(); + if (WITH_LOCK(::cs_main, return m_disabled)) { + // Background chainstate has reached the snapshot base block, so exit. + break; + } + // We check shutdown only after giving ActivateBestChainStep a chance to run once so that we // never shutdown before connecting the genesis block during LoadChainTip(). Previously this // caused an assert() failure during shutdown in such cases as the UTXO DB flushing checks @@ -4372,6 +4402,8 @@ bool ChainstateManager::LoadBlockIndex() assert(any_chain([](auto chainstate) { return !chainstate->reliesOnAssumedValid(); })); first_assumed_valid_height = block->nHeight; + LogPrintf("Saw first assumedvalid block at height %d (%s)\n", + first_assumed_valid_height, block->ToString()); break; } } @@ -4908,12 +4940,8 @@ std::vector<Chainstate*> ChainstateManager::GetAll() LOCK(::cs_main); std::vector<Chainstate*> out; - if (!IsSnapshotValidated() && m_ibd_chainstate) { - out.push_back(m_ibd_chainstate.get()); - } - - if (m_snapshot_chainstate) { - out.push_back(m_snapshot_chainstate.get()); + for (Chainstate* cs : {m_ibd_chainstate.get(), m_snapshot_chainstate.get()}) { + if (this->IsUsable(cs)) out.push_back(cs); } return out; @@ -5099,6 +5127,19 @@ static void FlushSnapshotToDisk(CCoinsViewCache& coins_cache, bool snapshot_load coins_cache.Flush(); } +struct StopHashingException : public std::exception +{ + const char* what() const throw() override + { + return "ComputeUTXOStats interrupted by shutdown."; + } +}; + +static void SnapshotUTXOHashBreakpoint() +{ + if (ShutdownRequested()) throw StopHashingException(); +} + bool ChainstateManager::PopulateAndValidateSnapshot( Chainstate& snapshot_chainstate, AutoFile& coins_file, @@ -5222,13 +5263,18 @@ bool ChainstateManager::PopulateAndValidateSnapshot( assert(coins_cache.GetBestBlock() == base_blockhash); - auto breakpoint_fnc = [] { /* TODO insert breakpoint here? */ }; - // As above, okay to immediately release cs_main here since no other context knows // about the snapshot_chainstate. CCoinsViewDB* snapshot_coinsdb = WITH_LOCK(::cs_main, return &snapshot_chainstate.CoinsDB()); - const std::optional<CCoinsStats> maybe_stats = ComputeUTXOStats(CoinStatsHashType::HASH_SERIALIZED, snapshot_coinsdb, m_blockman, breakpoint_fnc); + std::optional<CCoinsStats> maybe_stats; + + try { + maybe_stats = ComputeUTXOStats( + CoinStatsHashType::HASH_SERIALIZED, snapshot_coinsdb, m_blockman, SnapshotUTXOHashBreakpoint); + } catch (StopHashingException const&) { + return false; + } if (!maybe_stats.has_value()) { LogPrintf("[snapshot] failed to generate coins stats\n"); return false; @@ -5296,6 +5342,149 @@ bool ChainstateManager::PopulateAndValidateSnapshot( return true; } +// Currently, this function holds cs_main for its duration, which could be for +// multiple minutes due to the ComputeUTXOStats call. This hold is necessary +// because we need to avoid advancing the background validation chainstate +// farther than the snapshot base block - and this function is also invoked +// from within ConnectTip, i.e. from within ActivateBestChain, so cs_main is +// held anyway. +// +// Eventually (TODO), we could somehow separate this function's runtime from +// maintenance of the active chain, but that will either require +// +// (i) setting `m_disabled` immediately and ensuring all chainstate accesses go +// through IsUsable() checks, or +// +// (ii) giving each chainstate its own lock instead of using cs_main for everything. +SnapshotCompletionResult ChainstateManager::MaybeCompleteSnapshotValidation( + std::function<void(bilingual_str)> shutdown_fnc) +{ + AssertLockHeld(cs_main); + if (m_ibd_chainstate.get() == &this->ActiveChainstate() || + !this->IsUsable(m_snapshot_chainstate.get()) || + !this->IsUsable(m_ibd_chainstate.get()) || + !m_ibd_chainstate->m_chain.Tip()) { + // Nothing to do - this function only applies to the background + // validation chainstate. + return SnapshotCompletionResult::SKIPPED; + } + const int snapshot_tip_height = this->ActiveHeight(); + const int snapshot_base_height = *Assert(this->GetSnapshotBaseHeight()); + const CBlockIndex& index_new = *Assert(m_ibd_chainstate->m_chain.Tip()); + + if (index_new.nHeight < snapshot_base_height) { + // Background IBD not complete yet. + return SnapshotCompletionResult::SKIPPED; + } + + assert(SnapshotBlockhash()); + uint256 snapshot_blockhash = *Assert(SnapshotBlockhash()); + + auto handle_invalid_snapshot = [&]() EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { + bilingual_str user_error = strprintf(_( + "%s failed to validate the -assumeutxo snapshot state. " + "This indicates a hardware problem, or a bug in the software, or a " + "bad software modification that allowed an invalid snapshot to be " + "loaded. As a result of this, the node will shut down and stop using any " + "state that was built on the snapshot, resetting the chain height " + "from %d to %d. On the next " + "restart, the node will resume syncing from %d " + "without using any snapshot data. " + "Please report this incident to %s, including how you obtained the snapshot. " + "The invalid snapshot chainstate has been left on disk in case it is " + "helpful in diagnosing the issue that caused this error."), + PACKAGE_NAME, snapshot_tip_height, snapshot_base_height, snapshot_base_height, PACKAGE_BUGREPORT + ); + + LogPrintf("[snapshot] !!! %s\n", user_error.original); + LogPrintf("[snapshot] deleting snapshot, reverting to validated chain, and stopping node\n"); + + m_active_chainstate = m_ibd_chainstate.get(); + m_snapshot_chainstate->m_disabled = true; + assert(!this->IsUsable(m_snapshot_chainstate.get())); + assert(this->IsUsable(m_ibd_chainstate.get())); + + m_snapshot_chainstate->InvalidateCoinsDBOnDisk(); + + shutdown_fnc(user_error); + }; + + if (index_new.GetBlockHash() != snapshot_blockhash) { + LogPrintf("[snapshot] supposed base block %s does not match the " /* Continued */ + "snapshot base block %s (height %d). Snapshot is not valid.", + index_new.ToString(), snapshot_blockhash.ToString(), snapshot_base_height); + handle_invalid_snapshot(); + return SnapshotCompletionResult::BASE_BLOCKHASH_MISMATCH; + } + + assert(index_new.nHeight == snapshot_base_height); + + int curr_height = m_ibd_chainstate->m_chain.Height(); + + assert(snapshot_base_height == curr_height); + assert(snapshot_base_height == index_new.nHeight); + assert(this->IsUsable(m_snapshot_chainstate.get())); + assert(this->GetAll().size() == 2); + + CCoinsViewDB& ibd_coins_db = m_ibd_chainstate->CoinsDB(); + m_ibd_chainstate->ForceFlushStateToDisk(); + + auto maybe_au_data = ExpectedAssumeutxo(curr_height, m_options.chainparams); + if (!maybe_au_data) { + LogPrintf("[snapshot] assumeutxo data not found for height " /* Continued */ + "(%d) - refusing to validate snapshot\n", curr_height); + handle_invalid_snapshot(); + return SnapshotCompletionResult::MISSING_CHAINPARAMS; + } + + const AssumeutxoData& au_data = *maybe_au_data; + std::optional<CCoinsStats> maybe_ibd_stats; + LogPrintf("[snapshot] computing UTXO stats for background chainstate to validate " /* Continued */ + "snapshot - this could take a few minutes\n"); + try { + maybe_ibd_stats = ComputeUTXOStats( + CoinStatsHashType::HASH_SERIALIZED, + &ibd_coins_db, + m_blockman, + SnapshotUTXOHashBreakpoint); + } catch (StopHashingException const&) { + return SnapshotCompletionResult::STATS_FAILED; + } + + // XXX note that this function is slow and will hold cs_main for potentially minutes. + if (!maybe_ibd_stats) { + LogPrintf("[snapshot] failed to generate stats for validation coins db\n"); + // While this isn't a problem with the snapshot per se, this condition + // prevents us from validating the snapshot, so we should shut down and let the + // user handle the issue manually. + handle_invalid_snapshot(); + return SnapshotCompletionResult::STATS_FAILED; + } + const auto& ibd_stats = *maybe_ibd_stats; + + // Compare the background validation chainstate's UTXO set hash against the hard-coded + // assumeutxo hash we expect. + // + // TODO: For belt-and-suspenders, we could cache the UTXO set + // hash for the snapshot when it's loaded in its chainstate's leveldb. We could then + // reference that here for an additional check. + if (AssumeutxoHash{ibd_stats.hashSerialized} != au_data.hash_serialized) { + LogPrintf("[snapshot] hash mismatch: actual=%s, expected=%s\n", + ibd_stats.hashSerialized.ToString(), + au_data.hash_serialized.ToString()); + handle_invalid_snapshot(); + return SnapshotCompletionResult::HASH_MISMATCH; + } + + LogPrintf("[snapshot] snapshot beginning at %s has been fully validated\n", + snapshot_blockhash.ToString()); + + m_ibd_chainstate->m_disabled = true; + this->MaybeRebalanceCaches(); + + return SnapshotCompletionResult::SUCCESS; +} + Chainstate& ChainstateManager::ActiveChainstate() const { LOCK(::cs_main); @@ -5312,17 +5501,22 @@ bool ChainstateManager::IsSnapshotActive() const void ChainstateManager::MaybeRebalanceCaches() { AssertLockHeld(::cs_main); - if (m_ibd_chainstate && !m_snapshot_chainstate) { + bool ibd_usable = this->IsUsable(m_ibd_chainstate.get()); + bool snapshot_usable = this->IsUsable(m_snapshot_chainstate.get()); + assert(ibd_usable || snapshot_usable); + + if (ibd_usable && !snapshot_usable) { LogPrintf("[snapshot] allocating all cache to the IBD chainstate\n"); // Allocate everything to the IBD chainstate. m_ibd_chainstate->ResizeCoinsCaches(m_total_coinstip_cache, m_total_coinsdb_cache); } - else if (m_snapshot_chainstate && !m_ibd_chainstate) { + else if (snapshot_usable && !ibd_usable) { + // If background validation has completed and snapshot is our active chain... LogPrintf("[snapshot] allocating all cache to the snapshot chainstate\n"); // Allocate everything to the snapshot chainstate. m_snapshot_chainstate->ResizeCoinsCaches(m_total_coinstip_cache, m_total_coinsdb_cache); } - else if (m_ibd_chainstate && m_snapshot_chainstate) { + else if (ibd_usable && snapshot_usable) { // If both chainstates exist, determine who needs more cache based on IBD status. // // Note: shrink caches first so that we don't inadvertently overwhelm available memory. @@ -5361,7 +5555,9 @@ static ChainstateManager::Options&& Flatten(ChainstateManager::Options&& opts) return std::move(opts); } -ChainstateManager::ChainstateManager(Options options) : m_options{Flatten(std::move(options))} {} +ChainstateManager::ChainstateManager(Options options, node::BlockManager::Options blockman_options) + : m_options{Flatten(std::move(options))}, + m_blockman{std::move(blockman_options)} {} ChainstateManager::~ChainstateManager() { @@ -5414,3 +5610,141 @@ bool IsBIP30Unspendable(const CBlockIndex& block_index) return (block_index.nHeight==91722 && block_index.GetBlockHash() == uint256S("0x00000000000271a2dc26e7667f8419f2e15416dc6955e5a6c6cdf3f2574dd08e")) || (block_index.nHeight==91812 && block_index.GetBlockHash() == uint256S("0x00000000000af0aed4792b1acee3d966af36cf5def14935db8de83d6f9306f2f")); } + +void Chainstate::InvalidateCoinsDBOnDisk() +{ + AssertLockHeld(::cs_main); + // Should never be called on a non-snapshot chainstate. + assert(m_from_snapshot_blockhash); + auto storage_path_maybe = this->CoinsDB().StoragePath(); + // Should never be called with a non-existent storage path. + assert(storage_path_maybe); + fs::path snapshot_datadir = *storage_path_maybe; + + // Coins views no longer usable. + m_coins_views.reset(); + + auto invalid_path = snapshot_datadir + "_INVALID"; + std::string dbpath = fs::PathToString(snapshot_datadir); + std::string target = fs::PathToString(invalid_path); + LogPrintf("[snapshot] renaming snapshot datadir %s to %s\n", dbpath, target); + + // The invalid snapshot datadir is simply moved and not deleted because we may + // want to do forensics later during issue investigation. The user is instructed + // accordingly in MaybeCompleteSnapshotValidation(). + try { + fs::rename(snapshot_datadir, invalid_path); + } catch (const fs::filesystem_error& e) { + auto src_str = fs::PathToString(snapshot_datadir); + auto dest_str = fs::PathToString(invalid_path); + + LogPrintf("%s: error renaming file '%s' -> '%s': %s\n", + __func__, src_str, dest_str, e.what()); + AbortNode(strprintf( + "Rename of '%s' -> '%s' failed. " + "You should resolve this by manually moving or deleting the invalid " + "snapshot directory %s, otherwise you will encounter the same error again " + "on the next startup.", + src_str, dest_str, src_str)); + } +} + +const CBlockIndex* ChainstateManager::GetSnapshotBaseBlock() const +{ + const auto blockhash_op = this->SnapshotBlockhash(); + if (!blockhash_op) return nullptr; + return Assert(m_blockman.LookupBlockIndex(*blockhash_op)); +} + +std::optional<int> ChainstateManager::GetSnapshotBaseHeight() const +{ + const CBlockIndex* base = this->GetSnapshotBaseBlock(); + return base ? std::make_optional(base->nHeight) : std::nullopt; +} + +bool ChainstateManager::ValidatedSnapshotCleanup() +{ + AssertLockHeld(::cs_main); + auto get_storage_path = [](auto& chainstate) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) -> std::optional<fs::path> { + if (!(chainstate && chainstate->HasCoinsViews())) { + return {}; + } + return chainstate->CoinsDB().StoragePath(); + }; + std::optional<fs::path> ibd_chainstate_path_maybe = get_storage_path(m_ibd_chainstate); + std::optional<fs::path> snapshot_chainstate_path_maybe = get_storage_path(m_snapshot_chainstate); + + if (!this->IsSnapshotValidated()) { + // No need to clean up. + return false; + } + // If either path doesn't exist, that means at least one of the chainstates + // is in-memory, in which case we can't do on-disk cleanup. You'd better be + // in a unittest! + if (!ibd_chainstate_path_maybe || !snapshot_chainstate_path_maybe) { + LogPrintf("[snapshot] snapshot chainstate cleanup cannot happen with " /* Continued */ + "in-memory chainstates. You are testing, right?\n"); + return false; + } + + const auto& snapshot_chainstate_path = *snapshot_chainstate_path_maybe; + const auto& ibd_chainstate_path = *ibd_chainstate_path_maybe; + + // Since we're going to be moving around the underlying leveldb filesystem content + // for each chainstate, make sure that the chainstates (and their constituent + // CoinsViews members) have been destructed first. + // + // The caller of this method will be responsible for reinitializing chainstates + // if they want to continue operation. + this->ResetChainstates(); + + // No chainstates should be considered usable. + assert(this->GetAll().size() == 0); + + LogPrintf("[snapshot] deleting background chainstate directory (now unnecessary) (%s)\n", + fs::PathToString(ibd_chainstate_path)); + + fs::path tmp_old{ibd_chainstate_path + "_todelete"}; + + auto rename_failed_abort = []( + fs::path p_old, + fs::path p_new, + const fs::filesystem_error& err) { + LogPrintf("%s: error renaming file (%s): %s\n", + __func__, fs::PathToString(p_old), err.what()); + AbortNode(strprintf( + "Rename of '%s' -> '%s' failed. " + "Cannot clean up the background chainstate leveldb directory.", + fs::PathToString(p_old), fs::PathToString(p_new))); + }; + + try { + fs::rename(ibd_chainstate_path, tmp_old); + } catch (const fs::filesystem_error& e) { + rename_failed_abort(ibd_chainstate_path, tmp_old, e); + throw; + } + + LogPrintf("[snapshot] moving snapshot chainstate (%s) to " /* Continued */ + "default chainstate directory (%s)\n", + fs::PathToString(snapshot_chainstate_path), fs::PathToString(ibd_chainstate_path)); + + try { + fs::rename(snapshot_chainstate_path, ibd_chainstate_path); + } catch (const fs::filesystem_error& e) { + rename_failed_abort(snapshot_chainstate_path, ibd_chainstate_path, e); + throw; + } + + if (!DeleteCoinsDBFromDisk(tmp_old, /*is_snapshot=*/false)) { + // No need to AbortNode because once the unneeded bg chainstate data is + // moved, it will not interfere with subsequent initialization. + LogPrintf("Deletion of %s failed. Please remove it manually, as the " /* Continued */ + "directory is now unnecessary.\n", + fs::PathToString(tmp_old)); + } else { + LogPrintf("[snapshot] deleted background chainstate directory (%s)\n", + fs::PathToString(ibd_chainstate_path)); + } + return true; +} diff --git a/src/validation.h b/src/validation.h index 067d2ea6d2..53e9a7ee50 100644 --- a/src/validation.h +++ b/src/validation.h @@ -13,17 +13,18 @@ #include <arith_uint256.h> #include <attributes.h> #include <chain.h> -#include <chainparams.h> -#include <kernel/chainstatemanager_opts.h> #include <consensus/amount.h> #include <deploymentstatus.h> #include <fs.h> +#include <kernel/chainparams.h> +#include <kernel/chainstatemanager_opts.h> #include <kernel/cs_main.h> // IWYU pragma: export #include <node/blockstorage.h> #include <policy/feerate.h> #include <policy/packages.h> #include <policy/policy.h> #include <script/script_error.h> +#include <shutdown.h> #include <sync.h> #include <txdb.h> #include <txmempool.h> // For CTxMemPool::cs @@ -493,6 +494,19 @@ protected: //! Manages the UTXO set, which is a reflection of the contents of `m_chain`. std::unique_ptr<CoinsViews> m_coins_views; + //! This toggle exists for use when doing background validation for UTXO + //! snapshots. + //! + //! In the expected case, it is set once the background validation chain reaches the + //! same height as the base of the snapshot and its UTXO set is found to hash to + //! the expected assumeutxo value. It signals that we should no longer connect + //! blocks to the background chainstate. When set on the background validation + //! chainstate, it signifies that we have fully validated the snapshot chainstate. + //! + //! In the unlikely case that the snapshot chainstate is found to be invalid, this + //! is set to true on the snapshot chainstate. + bool m_disabled GUARDED_BY(::cs_main) {false}; + public: //! Reference to a BlockManager instance which itself is shared across all //! Chainstate instances. @@ -560,15 +574,15 @@ public: CCoinsViewCache& CoinsTip() EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { AssertLockHeld(::cs_main); - assert(m_coins_views->m_cacheview); - return *m_coins_views->m_cacheview.get(); + Assert(m_coins_views); + return *Assert(m_coins_views->m_cacheview); } //! @returns A reference to the on-disk UTXO set database. CCoinsViewDB& CoinsDB() EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { AssertLockHeld(::cs_main); - return m_coins_views->m_dbview; + return Assert(m_coins_views)->m_dbview; } //! @returns A pointer to the mempool. @@ -582,12 +596,15 @@ public: CCoinsViewErrorCatcher& CoinsErrorCatcher() EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { AssertLockHeld(::cs_main); - return m_coins_views->m_catcherview; + return Assert(m_coins_views)->m_catcherview; } //! Destructs all objects related to accessing the UTXO set. void ResetCoinsViews() { m_coins_views.reset(); } + //! Does this chainstate have a UTXO set attached? + bool HasCoinsViews() const { return (bool)m_coins_views; } + //! The cache size of the on-disk coins view. size_t m_coinsdb_cache_size_bytes{0}; @@ -667,6 +684,12 @@ public: * May not be called with cs_main held. May not be called in a * validationinterface callback. * + * Note that if this is called while a snapshot chainstate is active, and if + * it is called on a background chainstate whose tip has reached the base block + * of the snapshot, its execution will take *MINUTES* while it hashes the + * background UTXO set to verify the assumeutxo value the snapshot was activated + * with. `cs_main` will be held during this time. + * * @returns true unless a system error occurred */ bool ActivateBestChain( @@ -745,6 +768,12 @@ public: std::string ToString() EXCLUSIVE_LOCKS_REQUIRED(::cs_main); + //! Indirection necessary to make lock annotations work with an optional mempool. + RecursiveMutex* MempoolMutex() const LOCK_RETURNED(m_mempool->cs) + { + return m_mempool ? &m_mempool->cs : nullptr; + } + private: bool ActivateBestChainStep(BlockValidationState& state, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool->cs); bool ConnectTip(BlockValidationState& state, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions& disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool->cs); @@ -758,12 +787,6 @@ private: void CheckForkWarningConditions() EXCLUSIVE_LOCKS_REQUIRED(cs_main); void InvalidChainFound(CBlockIndex* pindexNew) EXCLUSIVE_LOCKS_REQUIRED(cs_main); - //! Indirection necessary to make lock annotations work with an optional mempool. - RecursiveMutex* MempoolMutex() const LOCK_RETURNED(m_mempool->cs) - { - return m_mempool ? &m_mempool->cs : nullptr; - } - /** * Make mempool consistent after a reorg, by re-adding or recursively erasing * disconnected block transactions from the mempool, and also removing any @@ -785,12 +808,40 @@ private: void UpdateTip(const CBlockIndex* pindexNew) EXCLUSIVE_LOCKS_REQUIRED(::cs_main); - std::chrono::microseconds m_last_write{0}; - std::chrono::microseconds m_last_flush{0}; + SteadyClock::time_point m_last_write{}; + SteadyClock::time_point m_last_flush{}; + + /** + * In case of an invalid snapshot, rename the coins leveldb directory so + * that it can be examined for issue diagnosis. + */ + void InvalidateCoinsDBOnDisk() EXCLUSIVE_LOCKS_REQUIRED(::cs_main); friend ChainstateManager; }; + +enum class SnapshotCompletionResult { + SUCCESS, + SKIPPED, + + // Expected assumeutxo configuration data is not found for the height of the + // base block. + MISSING_CHAINPARAMS, + + // Failed to generate UTXO statistics (to check UTXO set hash) for the background + // chainstate. + STATS_FAILED, + + // The UTXO set hash of the background validation chainstate does not match + // the one expected by assumeutxo chainparams. + HASH_MISMATCH, + + // The blockhash of the current tip of the background validation chainstate does + // not match the one expected by the snapshot chainstate. + BASE_BLOCKHASH_MISMATCH, +}; + /** * Provides an interface for creating and interacting with one or two * chainstates: an IBD chainstate generated by downloading blocks, and @@ -860,10 +911,6 @@ private: //! that call. Chainstate* m_active_chainstate GUARDED_BY(::cs_main) {nullptr}; - //! If true, the assumed-valid chainstate has been fully validated - //! by the background validation chainstate. - bool m_snapshot_validated GUARDED_BY(::cs_main){false}; - CBlockIndex* m_best_invalid GUARDED_BY(::cs_main){nullptr}; //! Internal helper for ActivateSnapshot(). @@ -889,10 +936,26 @@ private: /** Most recent headers presync progress update, for rate-limiting. */ std::chrono::time_point<std::chrono::steady_clock> m_last_presync_update GUARDED_BY(::cs_main) {}; + //! Returns nullptr if no snapshot has been loaded. + const CBlockIndex* GetSnapshotBaseBlock() const EXCLUSIVE_LOCKS_REQUIRED(::cs_main); + + //! Return the height of the base block of the snapshot in use, if one exists, else + //! nullopt. + std::optional<int> GetSnapshotBaseHeight() const EXCLUSIVE_LOCKS_REQUIRED(::cs_main); + + //! Return true if a chainstate is considered usable. + //! + //! This is false when a background validation chainstate has completed its + //! validation of an assumed-valid chainstate, or when a snapshot + //! chainstate has been found to be invalid. + bool IsUsable(const Chainstate* const cs) const EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { + return cs && !cs->m_disabled; + } + public: using Options = kernel::ChainstateManagerOpts; - explicit ChainstateManager(Options options); + explicit ChainstateManager(Options options, node::BlockManager::Options blockman_options); const CChainParams& GetParams() const { return m_options.chainparams; } const Consensus::Params& GetConsensus() const { return m_options.chainparams.GetConsensus(); } @@ -976,6 +1039,18 @@ public: [[nodiscard]] bool ActivateSnapshot( AutoFile& coins_file, const node::SnapshotMetadata& metadata, bool in_memory); + //! Once the background validation chainstate has reached the height which + //! is the base of the UTXO snapshot in use, compare its coins to ensure + //! they match those expected by the snapshot. + //! + //! If the coins match (expected), then mark the validation chainstate for + //! deletion and continue using the snapshot chainstate as active. + //! Otherwise, revert to using the ibd chainstate and shutdown. + SnapshotCompletionResult MaybeCompleteSnapshotValidation( + std::function<void(bilingual_str)> shutdown_fnc = + [](bilingual_str msg) { AbortNode(msg.original, msg); }) + EXCLUSIVE_LOCKS_REQUIRED(::cs_main); + //! The most-work chain. Chainstate& ActiveChainstate() const; CChain& ActiveChain() const EXCLUSIVE_LOCKS_REQUIRED(GetMutex()) { return ActiveChainstate().m_chain; } @@ -1000,7 +1075,10 @@ public: std::optional<uint256> SnapshotBlockhash() const; //! Is there a snapshot in use and has it been fully validated? - bool IsSnapshotValidated() const EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { return m_snapshot_validated; } + bool IsSnapshotValidated() const EXCLUSIVE_LOCKS_REQUIRED(::cs_main) + { + return m_snapshot_chainstate && m_ibd_chainstate && m_ibd_chainstate->m_disabled; + } /** * Process an incoming block. This only returns after the best known valid @@ -1080,6 +1158,17 @@ public: Chainstate& ActivateExistingSnapshot(CTxMemPool* mempool, uint256 base_blockhash) EXCLUSIVE_LOCKS_REQUIRED(::cs_main); + //! If we have validated a snapshot chain during this runtime, copy its + //! chainstate directory over to the main `chainstate` location, completing + //! validation of the snapshot. + //! + //! If the cleanup succeeds, the caller will need to ensure chainstates are + //! reinitialized, since ResetChainstates() will be called before leveldb + //! directories are moved or deleted. + //! + //! @sa node/chainstate:LoadChainstate() + bool ValidatedSnapshotCleanup() EXCLUSIVE_LOCKS_REQUIRED(::cs_main); + ~ChainstateManager(); }; diff --git a/src/wallet/coinselection.cpp b/src/wallet/coinselection.cpp index e6ba89627c..9cf61dbe51 100644 --- a/src/wallet/coinselection.cpp +++ b/src/wallet/coinselection.cpp @@ -333,12 +333,9 @@ std::optional<SelectionResult> KnapsackSolver(std::vector<OutputGroup>& groups, ******************************************************************************/ -void OutputGroup::Insert(const COutput& output, size_t ancestors, size_t descendants, bool positive_only) { - // Filter for positive only here before adding the coin - if (positive_only && output.GetEffectiveValue() <= 0) return; - +void OutputGroup::Insert(const std::shared_ptr<COutput>& output, size_t ancestors, size_t descendants) { m_outputs.push_back(output); - COutput& coin = m_outputs.back(); + auto& coin = *m_outputs.back(); fee += coin.GetFee(); @@ -358,8 +355,8 @@ void OutputGroup::Insert(const COutput& output, size_t ancestors, size_t descend // coin itself; thus, this value is counted as the max, not the sum m_descendants = std::max(m_descendants, descendants); - if (output.input_bytes > 0) { - m_weight += output.input_bytes * WITNESS_SCALE_FACTOR; + if (output->input_bytes > 0) { + m_weight += output->input_bytes * WITNESS_SCALE_FACTOR; } } @@ -375,7 +372,22 @@ CAmount OutputGroup::GetSelectionAmount() const return m_subtract_fee_outputs ? m_value : effective_value; } -CAmount GetSelectionWaste(const std::set<COutput>& inputs, CAmount change_cost, CAmount target, bool use_effective_value) +void OutputGroupTypeMap::Push(const OutputGroup& group, OutputType type, bool insert_positive, bool insert_mixed) +{ + if (group.m_outputs.empty()) return; + + Groups& groups = groups_by_type[type]; + if (insert_positive && group.GetSelectionAmount() > 0) { + groups.positive_group.emplace_back(group); + all_groups.positive_group.emplace_back(group); + } + if (insert_mixed) { + groups.mixed_group.emplace_back(group); + all_groups.mixed_group.emplace_back(group); + } +} + +CAmount GetSelectionWaste(const std::set<std::shared_ptr<COutput>>& inputs, CAmount change_cost, CAmount target, bool use_effective_value) { // This function should not be called with empty inputs as that would mean the selection failed assert(!inputs.empty()); @@ -383,7 +395,8 @@ CAmount GetSelectionWaste(const std::set<COutput>& inputs, CAmount change_cost, // Always consider the cost of spending an input now vs in the future. CAmount waste = 0; CAmount selected_effective_value = 0; - for (const COutput& coin : inputs) { + for (const auto& coin_ptr : inputs) { + const COutput& coin = *coin_ptr; waste += coin.GetFee() - coin.long_term_fee; selected_effective_value += use_effective_value ? coin.GetEffectiveValue() : coin.txout.nValue; } @@ -431,12 +444,12 @@ CAmount SelectionResult::GetWaste() const CAmount SelectionResult::GetSelectedValue() const { - return std::accumulate(m_selected_inputs.cbegin(), m_selected_inputs.cend(), CAmount{0}, [](CAmount sum, const auto& coin) { return sum + coin.txout.nValue; }); + return std::accumulate(m_selected_inputs.cbegin(), m_selected_inputs.cend(), CAmount{0}, [](CAmount sum, const auto& coin) { return sum + coin->txout.nValue; }); } CAmount SelectionResult::GetSelectedEffectiveValue() const { - return std::accumulate(m_selected_inputs.cbegin(), m_selected_inputs.cend(), CAmount{0}, [](CAmount sum, const auto& coin) { return sum + coin.GetEffectiveValue(); }); + return std::accumulate(m_selected_inputs.cbegin(), m_selected_inputs.cend(), CAmount{0}, [](CAmount sum, const auto& coin) { return sum + coin->GetEffectiveValue(); }); } void SelectionResult::Clear() @@ -455,14 +468,14 @@ void SelectionResult::AddInput(const OutputGroup& group) m_weight += group.m_weight; } -void SelectionResult::AddInputs(const std::set<COutput>& inputs, bool subtract_fee_outputs) +void SelectionResult::AddInputs(const std::set<std::shared_ptr<COutput>>& inputs, bool subtract_fee_outputs) { // As it can fail, combine inputs first InsertInputs(inputs); m_use_effective = !subtract_fee_outputs; m_weight += std::accumulate(inputs.cbegin(), inputs.cend(), 0, [](int sum, const auto& coin) { - return sum + std::max(coin.input_bytes, 0) * WITNESS_SCALE_FACTOR; + return sum + std::max(coin->input_bytes, 0) * WITNESS_SCALE_FACTOR; }); } @@ -480,14 +493,14 @@ void SelectionResult::Merge(const SelectionResult& other) m_weight += other.m_weight; } -const std::set<COutput>& SelectionResult::GetInputSet() const +const std::set<std::shared_ptr<COutput>>& SelectionResult::GetInputSet() const { return m_selected_inputs; } -std::vector<COutput> SelectionResult::GetShuffledInputVector() const +std::vector<std::shared_ptr<COutput>> SelectionResult::GetShuffledInputVector() const { - std::vector<COutput> coins(m_selected_inputs.begin(), m_selected_inputs.end()); + std::vector<std::shared_ptr<COutput>> coins(m_selected_inputs.begin(), m_selected_inputs.end()); Shuffle(coins.begin(), coins.end(), FastRandomContext()); return coins; } diff --git a/src/wallet/coinselection.h b/src/wallet/coinselection.h index 9ff2011ce3..5a7b748be1 100644 --- a/src/wallet/coinselection.h +++ b/src/wallet/coinselection.h @@ -7,6 +7,7 @@ #include <consensus/amount.h> #include <consensus/consensus.h> +#include <outputtype.h> #include <policy/feerate.h> #include <primitives/transaction.h> #include <random.h> @@ -152,6 +153,11 @@ struct CoinSelectionParams { * associated with the same address. This helps reduce privacy leaks resulting from address * reuse. Dust outputs are not eligible to be added to output groups and thus not considered. */ bool m_avoid_partial_spends = false; + /** + * When true, allow unsafe coins to be selected during Coin Selection. This may spend unconfirmed outputs: + * 1) Received from other wallets, 2) replacing other txs, 3) that have been replaced. + */ + bool m_include_unsafe_inputs = false; CoinSelectionParams(FastRandomContext& rng_fast, size_t change_output_size, size_t change_spend_size, CAmount min_change_target, CFeeRate effective_feerate, @@ -192,13 +198,18 @@ struct CoinEligibilityFilter CoinEligibilityFilter(int conf_mine, int conf_theirs, uint64_t max_ancestors) : conf_mine(conf_mine), conf_theirs(conf_theirs), max_ancestors(max_ancestors), max_descendants(max_ancestors) {} CoinEligibilityFilter(int conf_mine, int conf_theirs, uint64_t max_ancestors, uint64_t max_descendants) : conf_mine(conf_mine), conf_theirs(conf_theirs), max_ancestors(max_ancestors), max_descendants(max_descendants) {} CoinEligibilityFilter(int conf_mine, int conf_theirs, uint64_t max_ancestors, uint64_t max_descendants, bool include_partial) : conf_mine(conf_mine), conf_theirs(conf_theirs), max_ancestors(max_ancestors), max_descendants(max_descendants), m_include_partial_groups(include_partial) {} + + bool operator<(const CoinEligibilityFilter& other) const { + return std::tie(conf_mine, conf_theirs, max_ancestors, max_descendants, m_include_partial_groups) + < std::tie(other.conf_mine, other.conf_theirs, other.max_ancestors, other.max_descendants, other.m_include_partial_groups); + } }; /** A group of UTXOs paid to the same output script. */ struct OutputGroup { /** The list of UTXOs contained in this output group. */ - std::vector<COutput> m_outputs; + std::vector<std::shared_ptr<COutput>> m_outputs; /** Whether the UTXOs were sent by the wallet to itself. This is relevant because we may want at * least a certain number of confirmations on UTXOs received from outside wallets while trusting * our own UTXOs more. */ @@ -216,8 +227,6 @@ struct OutputGroup CAmount effective_value{0}; /** The fee to spend these UTXOs at the effective feerate. */ CAmount fee{0}; - /** The target feerate of the transaction we're trying to build. */ - CFeeRate m_effective_feerate{0}; /** The fee to spend these UTXOs at the long term feerate. */ CAmount long_term_fee{0}; /** The feerate for spending a created change output eventually (i.e. not urgently, and thus at @@ -232,16 +241,39 @@ struct OutputGroup OutputGroup() {} OutputGroup(const CoinSelectionParams& params) : - m_effective_feerate(params.m_effective_feerate), m_long_term_feerate(params.m_long_term_feerate), m_subtract_fee_outputs(params.m_subtract_fee_outputs) {} - void Insert(const COutput& output, size_t ancestors, size_t descendants, bool positive_only); + void Insert(const std::shared_ptr<COutput>& output, size_t ancestors, size_t descendants); bool EligibleForSpending(const CoinEligibilityFilter& eligibility_filter) const; CAmount GetSelectionAmount() const; }; +struct Groups { + // Stores 'OutputGroup' containing only positive UTXOs (value > 0). + std::vector<OutputGroup> positive_group; + // Stores 'OutputGroup' which may contain both positive and negative UTXOs. + std::vector<OutputGroup> mixed_group; +}; + +/** Stores several 'Groups' whose were mapped by output type. */ +struct OutputGroupTypeMap +{ + // Maps output type to output groups. + std::map<OutputType, Groups> groups_by_type; + // All inserted groups, no type distinction. + Groups all_groups; + + // Based on the insert flag; appends group to the 'mixed_group' and, if value > 0, to the 'positive_group'. + // This affects both; the groups filtered by type and the overall groups container. + void Push(const OutputGroup& group, OutputType type, bool insert_positive, bool insert_mixed); + // Different output types count + size_t TypesCount() { return groups_by_type.size(); } +}; + +typedef std::map<CoinEligibilityFilter, OutputGroupTypeMap> FilteredOutputGroups; + /** Compute the waste for this result given the cost of change * and the opportunity cost of spending these inputs now vs in the future. * If change exists, waste = change_cost + inputs * (effective_feerate - long_term_feerate) @@ -259,7 +291,7 @@ struct OutputGroup * @param[in] use_effective_value Whether to use the input's effective value (when true) or the real value (when false). * @return The waste */ -[[nodiscard]] CAmount GetSelectionWaste(const std::set<COutput>& inputs, CAmount change_cost, CAmount target, bool use_effective_value = true); +[[nodiscard]] CAmount GetSelectionWaste(const std::set<std::shared_ptr<COutput>>& inputs, CAmount change_cost, CAmount target, bool use_effective_value = true); /** Choose a random change target for each transaction to make it harder to fingerprint the Core @@ -292,7 +324,7 @@ struct SelectionResult { private: /** Set of inputs selected by the algorithm to use in the transaction */ - std::set<COutput> m_selected_inputs; + std::set<std::shared_ptr<COutput>> m_selected_inputs; /** The target the algorithm selected for. Equal to the recipient amount plus non-input fees */ CAmount m_target; /** The algorithm used to produce this result */ @@ -329,7 +361,7 @@ public: void Clear(); void AddInput(const OutputGroup& group); - void AddInputs(const std::set<COutput>& inputs, bool subtract_fee_outputs); + void AddInputs(const std::set<std::shared_ptr<COutput>>& inputs, bool subtract_fee_outputs); /** Calculates and stores the waste for this selection via GetSelectionWaste */ void ComputeAndSetWaste(const CAmount min_viable_change, const CAmount change_cost, const CAmount change_fee); @@ -344,9 +376,9 @@ public: void Merge(const SelectionResult& other); /** Get m_selected_inputs */ - const std::set<COutput>& GetInputSet() const; + const std::set<std::shared_ptr<COutput>>& GetInputSet() const; /** Get the vector of COutputs that will be used to fill in a CTransaction's vin */ - std::vector<COutput> GetShuffledInputVector() const; + std::vector<std::shared_ptr<COutput>> GetShuffledInputVector() const; bool operator<(SelectionResult other) const; diff --git a/src/wallet/rpc/backup.cpp b/src/wallet/rpc/backup.cpp index 744537cfbd..09cfc07bc2 100644 --- a/src/wallet/rpc/backup.cpp +++ b/src/wallet/rpc/backup.cpp @@ -1760,7 +1760,8 @@ RPCHelpMan listdescriptors() {RPCResult::Type::NUM, "", "Range start inclusive"}, {RPCResult::Type::NUM, "", "Range end inclusive"}, }}, - {RPCResult::Type::NUM, "next", /*optional=*/true, "The next index to generate addresses from; defined only for ranged descriptors"}, + {RPCResult::Type::NUM, "next", /*optional=*/true, "Same as next_index field. Kept for compatibility reason."}, + {RPCResult::Type::NUM, "next_index", /*optional=*/true, "The next index to generate addresses from; defined only for ranged descriptors"}, }}, }} }}, @@ -1837,6 +1838,7 @@ RPCHelpMan listdescriptors() range.push_back(info.range->second - 1); spk.pushKV("range", range); spk.pushKV("next", info.next_index); + spk.pushKV("next_index", info.next_index); } descriptors.push_back(spk); } diff --git a/src/wallet/spend.cpp b/src/wallet/spend.cpp index 865f6e41a6..5771d33b7a 100644 --- a/src/wallet/spend.cpp +++ b/src/wallet/spend.cpp @@ -404,28 +404,34 @@ std::map<CTxDestination, std::vector<COutput>> ListCoins(const CWallet& wallet) return result; } -std::vector<OutputGroup> GroupOutputs(const CWallet& wallet, const std::vector<COutput>& outputs, const CoinSelectionParams& coin_sel_params, const CoinEligibilityFilter& filter, bool positive_only) +FilteredOutputGroups GroupOutputs(const CWallet& wallet, + const CoinsResult& coins, + const CoinSelectionParams& coin_sel_params, + const std::vector<SelectionFilter>& filters) { - std::vector<OutputGroup> groups_out; + FilteredOutputGroups filtered_groups; if (!coin_sel_params.m_avoid_partial_spends) { - // Allowing partial spends means no grouping. Each COutput gets its own OutputGroup. - for (const COutput& output : outputs) { - // Skip outputs we cannot spend - if (!output.spendable) continue; - - size_t ancestors, descendants; - wallet.chain().getTransactionAncestry(output.outpoint.hash, ancestors, descendants); - - // Make an OutputGroup containing just this output - OutputGroup group{coin_sel_params}; - group.Insert(output, ancestors, descendants, positive_only); - - // Check the OutputGroup's eligibility. Only add the eligible ones. - if (positive_only && group.GetSelectionAmount() <= 0) continue; - if (group.m_outputs.size() > 0 && group.EligibleForSpending(filter)) groups_out.push_back(group); + // Allowing partial spends means no grouping. Each COutput gets its own OutputGroup + for (const auto& [type, outputs] : coins.coins) { + for (const COutput& output : outputs) { + // Get mempool info + size_t ancestors, descendants; + wallet.chain().getTransactionAncestry(output.outpoint.hash, ancestors, descendants); + + // Create a new group per output and add it to the all groups vector + OutputGroup group(coin_sel_params); + group.Insert(std::make_shared<COutput>(output), ancestors, descendants); + + // Each filter maps to a different set of groups + for (const auto& sel_filter : filters) { + const auto& filter = sel_filter.filter; + if (!group.EligibleForSpending(filter)) continue; + filtered_groups[filter].Push(group, type, /*insert_positive=*/true, /*insert_mixed=*/true); + } + } } - return groups_out; + return filtered_groups; } // We want to combine COutputs that have the same scriptPubKey into single OutputGroups @@ -434,16 +440,11 @@ std::vector<OutputGroup> GroupOutputs(const CWallet& wallet, const std::vector<C // For each COutput, we check if the scriptPubKey is in the map, and if it is, the COutput is added // to the last OutputGroup in the vector for the scriptPubKey. When the last OutputGroup has // OUTPUT_GROUP_MAX_ENTRIES COutputs, a new OutputGroup is added to the end of the vector. - std::map<CScript, std::vector<OutputGroup>> spk_to_groups_map; - for (const auto& output : outputs) { - // Skip outputs we cannot spend - if (!output.spendable) continue; - - size_t ancestors, descendants; - wallet.chain().getTransactionAncestry(output.outpoint.hash, ancestors, descendants); - CScript spk = output.txout.scriptPubKey; - - std::vector<OutputGroup>& groups = spk_to_groups_map[spk]; + typedef std::map<std::pair<CScript, OutputType>, std::vector<OutputGroup>> ScriptPubKeyToOutgroup; + const auto& insert_output = [&]( + const std::shared_ptr<COutput>& output, OutputType type, size_t ancestors, size_t descendants, + ScriptPubKeyToOutgroup& groups_map) { + std::vector<OutputGroup>& groups = groups_map[std::make_pair(output->txout.scriptPubKey,type)]; if (groups.size() == 0) { // No OutputGroups for this scriptPubKey yet, add one @@ -462,42 +463,68 @@ std::vector<OutputGroup> GroupOutputs(const CWallet& wallet, const std::vector<C group = &groups.back(); } - // Add the output to group - group->Insert(output, ancestors, descendants, positive_only); - } - - // Now we go through the entire map and pull out the OutputGroups - for (const auto& spk_and_groups_pair: spk_to_groups_map) { - const std::vector<OutputGroup>& groups_per_spk= spk_and_groups_pair.second; + group->Insert(output, ancestors, descendants); + }; - // Go through the vector backwards. This allows for the first item we deal with being the partial group. - for (auto group_it = groups_per_spk.rbegin(); group_it != groups_per_spk.rend(); group_it++) { - const OutputGroup& group = *group_it; + ScriptPubKeyToOutgroup spk_to_groups_map; + ScriptPubKeyToOutgroup spk_to_positive_groups_map; + for (const auto& [type, outs] : coins.coins) { + for (const COutput& output : outs) { + size_t ancestors, descendants; + wallet.chain().getTransactionAncestry(output.outpoint.hash, ancestors, descendants); - // Don't include partial groups if there are full groups too and we don't want partial groups - if (group_it == groups_per_spk.rbegin() && groups_per_spk.size() > 1 && !filter.m_include_partial_groups) { - continue; + const auto& shared_output = std::make_shared<COutput>(output); + // Filter for positive only before adding the output + if (output.GetEffectiveValue() > 0) { + insert_output(shared_output, type, ancestors, descendants, spk_to_positive_groups_map); } - // Check the OutputGroup's eligibility. Only add the eligible ones. - if (positive_only && group.GetSelectionAmount() <= 0) continue; - if (group.m_outputs.size() > 0 && group.EligibleForSpending(filter)) groups_out.push_back(group); + // 'All' groups + insert_output(shared_output, type, ancestors, descendants, spk_to_groups_map); } } - return groups_out; + // Now we go through the entire maps and pull out the OutputGroups + const auto& push_output_groups = [&](const ScriptPubKeyToOutgroup& groups_map, bool positive_only) { + for (const auto& [script, groups] : groups_map) { + // Go through the vector backwards. This allows for the first item we deal with being the partial group. + for (auto group_it = groups.rbegin(); group_it != groups.rend(); group_it++) { + const OutputGroup& group = *group_it; + + // Each filter maps to a different set of groups + for (const auto& sel_filter : filters) { + const auto& filter = sel_filter.filter; + if (!group.EligibleForSpending(filter)) continue; + + // Don't include partial groups if there are full groups too and we don't want partial groups + if (group_it == groups.rbegin() && groups.size() > 1 && !filter.m_include_partial_groups) { + continue; + } + + OutputType type = script.second; + // Either insert the group into the positive-only groups or the mixed ones. + filtered_groups[filter].Push(group, type, positive_only, /*insert_mixed=*/!positive_only); + } + } + } + }; + + push_output_groups(spk_to_groups_map, /*positive_only=*/ false); + push_output_groups(spk_to_positive_groups_map, /*positive_only=*/ true); + + return filtered_groups; } // Returns true if the result contains an error and the message is not empty static bool HasErrorMsg(const util::Result<SelectionResult>& res) { return !util::ErrorString(res).empty(); } -util::Result<SelectionResult> AttemptSelection(const CWallet& wallet, const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, const CoinsResult& available_coins, +util::Result<SelectionResult> AttemptSelection(const CAmount& nTargetValue, OutputGroupTypeMap& groups, const CoinSelectionParams& coin_selection_params, bool allow_mixed_output_types) { // Run coin selection on each OutputType and compute the Waste Metric std::vector<SelectionResult> results; - for (const auto& it : available_coins.coins) { - auto result{ChooseSelectionResult(wallet, nTargetValue, eligibility_filter, it.second, coin_selection_params)}; + for (auto& [type, group] : groups.groups_by_type) { + auto result{ChooseSelectionResult(nTargetValue, group, coin_selection_params)}; // If any specific error message appears here, then something particularly wrong happened. if (HasErrorMsg(result)) return result; // So let's return the specific error. // Append the favorable result. @@ -510,32 +537,30 @@ util::Result<SelectionResult> AttemptSelection(const CWallet& wallet, const CAmo // If we can't fund the transaction from any individual OutputType, run coin selection one last time // over all available coins, which would allow mixing. // If TypesCount() <= 1, there is nothing to mix. - if (allow_mixed_output_types && available_coins.TypesCount() > 1) { - return ChooseSelectionResult(wallet, nTargetValue, eligibility_filter, available_coins.All(), coin_selection_params); + if (allow_mixed_output_types && groups.TypesCount() > 1) { + return ChooseSelectionResult(nTargetValue, groups.all_groups, coin_selection_params); } // Either mixing is not allowed and we couldn't find a solution from any single OutputType, or mixing was allowed and we still couldn't // find a solution using all available coins return util::Error(); }; -util::Result<SelectionResult> ChooseSelectionResult(const CWallet& wallet, const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, const std::vector<COutput>& available_coins, const CoinSelectionParams& coin_selection_params) +util::Result<SelectionResult> ChooseSelectionResult(const CAmount& nTargetValue, Groups& groups, const CoinSelectionParams& coin_selection_params) { // Vector of results. We will choose the best one based on waste. std::vector<SelectionResult> results; - std::vector<OutputGroup> positive_groups = GroupOutputs(wallet, available_coins, coin_selection_params, eligibility_filter, /*positive_only=*/true); - if (auto bnb_result{SelectCoinsBnB(positive_groups, nTargetValue, coin_selection_params.m_cost_of_change)}) { + if (auto bnb_result{SelectCoinsBnB(groups.positive_group, nTargetValue, coin_selection_params.m_cost_of_change)}) { results.push_back(*bnb_result); } // The knapsack solver has some legacy behavior where it will spend dust outputs. We retain this behavior, so don't filter for positive only here. - std::vector<OutputGroup> all_groups = GroupOutputs(wallet, available_coins, coin_selection_params, eligibility_filter, /*positive_only=*/false); - if (auto knapsack_result{KnapsackSolver(all_groups, nTargetValue, coin_selection_params.m_min_change_target, coin_selection_params.rng_fast)}) { + if (auto knapsack_result{KnapsackSolver(groups.mixed_group, nTargetValue, coin_selection_params.m_min_change_target, coin_selection_params.rng_fast)}) { knapsack_result->ComputeAndSetWaste(coin_selection_params.min_viable_change, coin_selection_params.m_cost_of_change, coin_selection_params.m_change_fee); results.push_back(*knapsack_result); } - if (auto srd_result{SelectCoinsSRD(positive_groups, nTargetValue, coin_selection_params.rng_fast)}) { + if (auto srd_result{SelectCoinsSRD(groups.positive_group, nTargetValue, coin_selection_params.rng_fast)}) { srd_result->ComputeAndSetWaste(coin_selection_params.min_viable_change, coin_selection_params.m_cost_of_change, coin_selection_params.m_change_fee); results.push_back(*srd_result); } @@ -592,7 +617,7 @@ util::Result<SelectionResult> SelectCoins(const CWallet& wallet, CoinsResult& av } // Start wallet Coin Selection procedure - auto op_selection_result = AutomaticCoinSelection(wallet, available_coins, selection_target, coin_control, coin_selection_params); + auto op_selection_result = AutomaticCoinSelection(wallet, available_coins, selection_target, coin_selection_params); if (!op_selection_result) return op_selection_result; // If needed, add preset inputs to the automatic coin selection result @@ -607,12 +632,7 @@ util::Result<SelectionResult> SelectCoins(const CWallet& wallet, CoinsResult& av return op_selection_result; } -struct SelectionFilter { - CoinEligibilityFilter filter; - bool allow_mixed_output_types{true}; -}; - -util::Result<SelectionResult> AutomaticCoinSelection(const CWallet& wallet, CoinsResult& available_coins, const CAmount& value_to_select, const CCoinControl& coin_control, const CoinSelectionParams& coin_selection_params) +util::Result<SelectionResult> AutomaticCoinSelection(const CWallet& wallet, CoinsResult& available_coins, const CAmount& value_to_select, const CoinSelectionParams& coin_selection_params) { unsigned int limit_ancestor_count = 0; unsigned int limit_descendant_count = 0; @@ -621,12 +641,10 @@ util::Result<SelectionResult> AutomaticCoinSelection(const CWallet& wallet, Coin const size_t max_descendants = (size_t)std::max<int64_t>(1, limit_descendant_count); const bool fRejectLongChains = gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS); - // form groups from remaining coins; note that preset coins will not - // automatically have their associated (same address) coins included - if (coin_control.m_avoid_partial_spends && available_coins.Size() > OUTPUT_GROUP_MAX_ENTRIES) { - // Cases where we have 101+ outputs all pointing to the same destination may result in - // privacy leaks as they will potentially be deterministically sorted. We solve that by - // explicitly shuffling the outputs before processing + // Cases where we have 101+ outputs all pointing to the same destination may result in + // privacy leaks as they will potentially be deterministically sorted. We solve that by + // explicitly shuffling the outputs before processing + if (coin_selection_params.m_avoid_partial_spends && available_coins.Size() > OUTPUT_GROUP_MAX_ENTRIES) { available_coins.Shuffle(coin_selection_params.rng_fast); } @@ -653,7 +671,7 @@ util::Result<SelectionResult> AutomaticCoinSelection(const CWallet& wallet, Coin ordered_filters.push_back({CoinEligibilityFilter(0, 1, max_ancestors-1, max_descendants-1, /*include_partial=*/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) { + if (coin_selection_params.m_include_unsafe_inputs) { ordered_filters.push_back({CoinEligibilityFilter(/*conf_mine=*/0, /*conf_theirs*/0, max_ancestors-1, max_descendants-1, /*include_partial=*/true)}); } // Try with unlimited ancestors/descendants. The transaction will still need to meet @@ -666,12 +684,17 @@ util::Result<SelectionResult> AutomaticCoinSelection(const CWallet& wallet, Coin } } + // Group outputs and map them by coin eligibility filter + FilteredOutputGroups filtered_groups = GroupOutputs(wallet, available_coins, coin_selection_params, ordered_filters); + // Walk-through the filters until the solution gets found. // If no solution is found, return the first detailed error (if any). // future: add "error level" so the worst one can be picked instead. std::vector<util::Result<SelectionResult>> res_detailed_errors; for (const auto& select_filter : ordered_filters) { - if (auto res{AttemptSelection(wallet, value_to_select, select_filter.filter, available_coins, + auto it = filtered_groups.find(select_filter.filter); + if (it == filtered_groups.end()) continue; + if (auto res{AttemptSelection(value_to_select, it->second, coin_selection_params, select_filter.allow_mixed_output_types)}) { return res; // result found } else { @@ -779,6 +802,7 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal( CoinSelectionParams coin_selection_params{rng_fast}; // Parameters for coin selection, init with dummy coin_selection_params.m_avoid_partial_spends = coin_control.m_avoid_partial_spends; + coin_selection_params.m_include_unsafe_inputs = coin_control.m_include_unsafe_inputs; // Set the long term feerate estimate to the wallet's consolidate feerate coin_selection_params.m_long_term_feerate = wallet.m_consolidate_feerate; @@ -933,7 +957,7 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal( } // Shuffle selected coins and fill in final vin - std::vector<COutput> selected_coins = result.GetShuffledInputVector(); + std::vector<std::shared_ptr<COutput>> selected_coins = result.GetShuffledInputVector(); // The sequence number is set to non-maxint so that DiscourageFeeSniping // works. @@ -945,7 +969,7 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal( // behavior." const uint32_t nSequence{coin_control.m_signal_bip125_rbf.value_or(wallet.m_signal_rbf) ? MAX_BIP125_RBF_SEQUENCE : CTxIn::MAX_SEQUENCE_NONFINAL}; for (const auto& coin : selected_coins) { - txNew.vin.push_back(CTxIn(coin.outpoint, CScript(), nSequence)); + txNew.vin.push_back(CTxIn(coin->outpoint, CScript(), nSequence)); } DiscourageFeeSniping(txNew, rng_fast, wallet.chain(), wallet.GetLastBlockHash(), wallet.GetLastBlockHeight()); diff --git a/src/wallet/spend.h b/src/wallet/spend.h index ad122b7ef3..78c2c5f22b 100644 --- a/src/wallet/spend.h +++ b/src/wallet/spend.h @@ -106,17 +106,27 @@ const CTxOut& FindNonChangeParentOutput(const CWallet& wallet, const COutPoint& */ std::map<CTxDestination, std::vector<COutput>> ListCoins(const CWallet& wallet) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet); -std::vector<OutputGroup> GroupOutputs(const CWallet& wallet, const std::vector<COutput>& outputs, const CoinSelectionParams& coin_sel_params, const CoinEligibilityFilter& filter, bool positive_only); +struct SelectionFilter { + CoinEligibilityFilter filter; + bool allow_mixed_output_types{true}; +}; + +/** +* Group coins by the provided filters. +*/ +FilteredOutputGroups GroupOutputs(const CWallet& wallet, + const CoinsResult& coins, + const CoinSelectionParams& coin_sel_params, + const std::vector<SelectionFilter>& filters); + /** * Attempt to find a valid input set that preserves privacy by not mixing OutputTypes. * `ChooseSelectionResult()` will be called on each OutputType individually and the best * the solution (according to the waste metric) will be chosen. If a valid input cannot be found from any * single OutputType, fallback to running `ChooseSelectionResult()` over all available coins. * - * param@[in] wallet The wallet which provides solving data for the coins * param@[in] nTargetValue The target value - * param@[in] eligilibity_filter A filter containing rules for which coins are allowed to be included in this selection - * param@[in] available_coins The struct of coins, organized by OutputType, available for selection prior to filtering + * param@[in] groups The grouped outputs mapped by coin eligibility filters * param@[in] coin_selection_params Parameters for the coin selection * param@[in] allow_mixed_output_types Relax restriction that SelectionResults must be of the same OutputType * returns If successful, a SelectionResult containing the input set @@ -124,7 +134,7 @@ std::vector<OutputGroup> GroupOutputs(const CWallet& wallet, const std::vector<C * or (2) an specific error message if there was something particularly wrong (e.g. a selection * result that surpassed the tx max weight size). */ -util::Result<SelectionResult> AttemptSelection(const CWallet& wallet, const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, const CoinsResult& available_coins, +util::Result<SelectionResult> AttemptSelection(const CAmount& nTargetValue, OutputGroupTypeMap& groups, const CoinSelectionParams& coin_selection_params, bool allow_mixed_output_types); /** @@ -132,23 +142,20 @@ util::Result<SelectionResult> AttemptSelection(const CWallet& wallet, const CAmo * Multiple coin selection algorithms will be run and the input set that produces the least waste * (according to the waste metric) will be chosen. * - * param@[in] wallet The wallet which provides solving data for the coins * param@[in] nTargetValue The target value - * param@[in] eligilibity_filter A filter containing rules for which coins are allowed to be included in this selection - * param@[in] available_coins The struct of coins, organized by OutputType, available for selection prior to filtering + * param@[in] groups The struct containing the outputs grouped by script and divided by (1) positive only outputs and (2) all outputs (positive + negative). * param@[in] coin_selection_params Parameters for the coin selection * returns If successful, a SelectionResult containing the input set * If failed, returns (1) an empty error message if the target was not reached (general "Insufficient funds") * or (2) an specific error message if there was something particularly wrong (e.g. a selection * result that surpassed the tx max weight size). */ -util::Result<SelectionResult> ChooseSelectionResult(const CWallet& wallet, const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, const std::vector<COutput>& available_coins, - const CoinSelectionParams& coin_selection_params); +util::Result<SelectionResult> ChooseSelectionResult(const CAmount& nTargetValue, Groups& groups, const CoinSelectionParams& coin_selection_params); // User manually selected inputs that must be part of the transaction struct PreSelectedInputs { - std::set<COutput> coins; + std::set<std::shared_ptr<COutput>> coins; // If subtract fee from outputs is disabled, the 'total_amount' // will be the sum of each output effective value // instead of the sum of the outputs amount @@ -161,7 +168,7 @@ struct PreSelectedInputs } else { total_amount += output.GetEffectiveValue(); } - coins.insert(output); + coins.insert(std::make_shared<COutput>(output)); } }; @@ -184,7 +191,7 @@ util::Result<PreSelectedInputs> FetchSelectedInputs(const CWallet& wallet, const * or (2) an specific error message if there was something particularly wrong (e.g. a selection * result that surpassed the tx max weight size). */ -util::Result<SelectionResult> AutomaticCoinSelection(const CWallet& wallet, CoinsResult& available_coins, const CAmount& nTargetValue, const CCoinControl& coin_control, +util::Result<SelectionResult> AutomaticCoinSelection(const CWallet& wallet, CoinsResult& available_coins, const CAmount& nTargetValue, const CoinSelectionParams& coin_selection_params) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet); /** diff --git a/src/wallet/sqlite.cpp b/src/wallet/sqlite.cpp index 4af49db609..8d8a7ab2a2 100644 --- a/src/wallet/sqlite.cpp +++ b/src/wallet/sqlite.cpp @@ -23,9 +23,6 @@ namespace wallet { static constexpr int32_t WALLET_SCHEMA_VERSION = 0; -static GlobalMutex g_sqlite_mutex; -static int g_sqlite_count GUARDED_BY(g_sqlite_mutex) = 0; - static void ErrorLogCallback(void* arg, int code, const char* msg) { // From sqlite3_config() documentation for the SQLITE_CONFIG_LOG option: @@ -83,6 +80,9 @@ static void SetPragma(sqlite3* db, const std::string& key, const std::string& va } } +Mutex SQLiteDatabase::g_sqlite_mutex; +int SQLiteDatabase::g_sqlite_count = 0; + SQLiteDatabase::SQLiteDatabase(const fs::path& dir_path, const fs::path& file_path, const DatabaseOptions& options, bool mock) : WalletDatabase(), m_mock(mock), m_dir_path(fs::PathToString(dir_path)), m_file_path(fs::PathToString(file_path)), m_use_unsafe_sync(options.use_unsafe_sync) { @@ -145,6 +145,8 @@ SQLiteDatabase::~SQLiteDatabase() void SQLiteDatabase::Cleanup() noexcept { + AssertLockNotHeld(g_sqlite_mutex); + Close(); LOCK(g_sqlite_mutex); diff --git a/src/wallet/sqlite.h b/src/wallet/sqlite.h index c6745d7a7e..5745a1d4cf 100644 --- a/src/wallet/sqlite.h +++ b/src/wallet/sqlite.h @@ -5,6 +5,7 @@ #ifndef BITCOIN_WALLET_SQLITE_H #define BITCOIN_WALLET_SQLITE_H +#include <sync.h> #include <wallet/db.h> #include <sqlite3.h> @@ -69,7 +70,16 @@ private: const std::string m_file_path; - void Cleanup() noexcept; + /** + * This mutex protects SQLite initialization and shutdown. + * sqlite3_config() and sqlite3_shutdown() are not thread-safe (sqlite3_initialize() is). + * Concurrent threads that execute SQLiteDatabase::SQLiteDatabase() should have just one + * of them do the init and the rest wait for it to complete before all can proceed. + */ + static Mutex g_sqlite_mutex; + static int g_sqlite_count GUARDED_BY(g_sqlite_mutex); + + void Cleanup() noexcept EXCLUSIVE_LOCKS_REQUIRED(!g_sqlite_mutex); public: SQLiteDatabase() = delete; diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp index 1c731b95e5..8f626addde 100644 --- a/src/wallet/test/coinselector_tests.cpp +++ b/src/wallet/test/coinselector_tests.cpp @@ -29,7 +29,7 @@ BOOST_FIXTURE_TEST_SUITE(coinselector_tests, WalletTestingSetup) // we repeat those tests this many times and only complain if all iterations of the test fail #define RANDOM_REPEATS 5 -typedef std::set<COutput> CoinSet; +typedef std::set<std::shared_ptr<COutput>> CoinSet; static const CoinEligibilityFilter filter_standard(1, 6, 0); static const CoinEligibilityFilter filter_confirmed(1, 1, 0); @@ -53,7 +53,7 @@ static void add_coin(const CAmount& nValue, int nInput, SelectionResult& result) tx.nLockTime = nextLockTime++; // so all transactions get different hashes COutput output(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, /*input_bytes=*/ -1, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false, /*fees=*/ 0); OutputGroup group; - group.Insert(output, /*ancestors=*/ 0, /*descendants=*/ 0, /*positive_only=*/ true); + group.Insert(std::make_shared<COutput>(output), /*ancestors=*/ 0, /*descendants=*/ 0); result.AddInput(group); } @@ -65,7 +65,7 @@ static void add_coin(const CAmount& nValue, int nInput, CoinSet& set, CAmount fe tx.nLockTime = nextLockTime++; // so all transactions get different hashes COutput coin(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, /*input_bytes=*/ 148, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false, fee); coin.long_term_fee = long_term_fee; - set.insert(coin); + set.insert(std::make_shared<COutput>(coin)); } static void add_coin(CoinsResult& available_coins, CWallet& wallet, const CAmount& nValue, CFeeRate feerate = CFeeRate(0), int nAge = 6*24, bool fIsFromMe = false, int nInput =0, bool spendable = false) @@ -94,10 +94,10 @@ static bool EquivalentResult(const SelectionResult& a, const SelectionResult& b) std::vector<CAmount> a_amts; std::vector<CAmount> b_amts; for (const auto& coin : a.GetInputSet()) { - a_amts.push_back(coin.txout.nValue); + a_amts.push_back(coin->txout.nValue); } for (const auto& coin : b.GetInputSet()) { - b_amts.push_back(coin.txout.nValue); + b_amts.push_back(coin->txout.nValue); } std::sort(a_amts.begin(), a_amts.end()); std::sort(b_amts.begin(), b_amts.end()); @@ -110,8 +110,8 @@ static bool EquivalentResult(const SelectionResult& a, const SelectionResult& b) static bool EqualResult(const SelectionResult& a, const SelectionResult& b) { std::pair<CoinSet::iterator, CoinSet::iterator> ret = std::mismatch(a.GetInputSet().begin(), a.GetInputSet().end(), b.GetInputSet().begin(), - [](const COutput& a, const COutput& b) { - return a.outpoint == b.outpoint; + [](const std::shared_ptr<COutput>& a, const std::shared_ptr<COutput>& b) { + return a->outpoint == b->outpoint; }); return ret.first == a.GetInputSet().end() && ret.second == b.GetInputSet().end(); } @@ -134,12 +134,12 @@ inline std::vector<OutputGroup>& GroupCoins(const std::vector<COutput>& availabl static_groups.clear(); for (auto& coin : available_coins) { static_groups.emplace_back(); - static_groups.back().Insert(coin, /*ancestors=*/ 0, /*descendants=*/ 0, /*positive_only=*/ false); + static_groups.back().Insert(std::make_shared<COutput>(coin), /*ancestors=*/ 0, /*descendants=*/ 0); } return static_groups; } -inline std::vector<OutputGroup>& KnapsackGroupOutputs(const std::vector<COutput>& available_coins, CWallet& wallet, const CoinEligibilityFilter& filter) +inline std::vector<OutputGroup>& KnapsackGroupOutputs(const CoinsResult& available_coins, CWallet& wallet, const CoinEligibilityFilter& filter) { FastRandomContext rand{}; CoinSelectionParams coin_selection_params{ @@ -153,9 +153,9 @@ inline std::vector<OutputGroup>& KnapsackGroupOutputs(const std::vector<COutput> /*tx_noinputs_size=*/ 0, /*avoid_partial=*/ false, }; - static std::vector<OutputGroup> static_groups; - static_groups = GroupOutputs(wallet, available_coins, coin_selection_params, filter, /*positive_only=*/false); - return static_groups; + static OutputGroupTypeMap static_groups; + static_groups = GroupOutputs(wallet, available_coins, coin_selection_params, {{filter}})[filter]; + return static_groups.all_groups.mixed_group; } // Branch and bound coin selection tests @@ -418,25 +418,25 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test) available_coins.Clear(); // with an empty wallet we can't even pay one cent - BOOST_CHECK(!KnapsackSolver(KnapsackGroupOutputs(available_coins.All(), *wallet, filter_standard), 1 * CENT, CENT)); + BOOST_CHECK(!KnapsackSolver(KnapsackGroupOutputs(available_coins, *wallet, filter_standard), 1 * CENT, CENT)); add_coin(available_coins, *wallet, 1*CENT, CFeeRate(0), 4); // add a new 1 cent coin // with a new 1 cent coin, we still can't find a mature 1 cent - BOOST_CHECK(!KnapsackSolver(KnapsackGroupOutputs(available_coins.All(), *wallet, filter_standard), 1 * CENT, CENT)); + BOOST_CHECK(!KnapsackSolver(KnapsackGroupOutputs(available_coins, *wallet, filter_standard), 1 * CENT, CENT)); // but we can find a new 1 cent - const auto result1 = KnapsackSolver(KnapsackGroupOutputs(available_coins.All(), *wallet, filter_confirmed), 1 * CENT, CENT); + const auto result1 = KnapsackSolver(KnapsackGroupOutputs(available_coins, *wallet, filter_confirmed), 1 * CENT, CENT); BOOST_CHECK(result1); BOOST_CHECK_EQUAL(result1->GetSelectedValue(), 1 * CENT); add_coin(available_coins, *wallet, 2*CENT); // add a mature 2 cent coin // we can't make 3 cents of mature coins - BOOST_CHECK(!KnapsackSolver(KnapsackGroupOutputs(available_coins.All(), *wallet, filter_standard), 3 * CENT, CENT)); + BOOST_CHECK(!KnapsackSolver(KnapsackGroupOutputs(available_coins, *wallet, filter_standard), 3 * CENT, CENT)); // we can make 3 cents of new coins - const auto result2 = KnapsackSolver(KnapsackGroupOutputs(available_coins.All(), *wallet, filter_confirmed), 3 * CENT, CENT); + const auto result2 = KnapsackSolver(KnapsackGroupOutputs(available_coins, *wallet, filter_confirmed), 3 * CENT, CENT); BOOST_CHECK(result2); BOOST_CHECK_EQUAL(result2->GetSelectedValue(), 3 * CENT); @@ -447,38 +447,38 @@ 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(!KnapsackSolver(KnapsackGroupOutputs(available_coins.All(), *wallet, filter_standard), 38 * CENT, CENT)); + BOOST_CHECK(!KnapsackSolver(KnapsackGroupOutputs(available_coins, *wallet, filter_standard), 38 * CENT, CENT)); // we can't even make 37 cents if we don't allow new coins even if they're from us - BOOST_CHECK(!KnapsackSolver(KnapsackGroupOutputs(available_coins.All(), *wallet, filter_standard_extra), 38 * CENT, CENT)); + BOOST_CHECK(!KnapsackSolver(KnapsackGroupOutputs(available_coins, *wallet, filter_standard_extra), 38 * CENT, CENT)); // but we can make 37 cents if we accept new coins from ourself - const auto result3 = KnapsackSolver(KnapsackGroupOutputs(available_coins.All(), *wallet, filter_standard), 37 * CENT, CENT); + const auto result3 = KnapsackSolver(KnapsackGroupOutputs(available_coins, *wallet, filter_standard), 37 * CENT, CENT); BOOST_CHECK(result3); BOOST_CHECK_EQUAL(result3->GetSelectedValue(), 37 * CENT); // and we can make 38 cents if we accept all new coins - const auto result4 = KnapsackSolver(KnapsackGroupOutputs(available_coins.All(), *wallet, filter_confirmed), 38 * CENT, CENT); + const auto result4 = KnapsackSolver(KnapsackGroupOutputs(available_coins, *wallet, filter_confirmed), 38 * CENT, CENT); BOOST_CHECK(result4); BOOST_CHECK_EQUAL(result4->GetSelectedValue(), 38 * CENT); // try making 34 cents from 1,2,5,10,20 - we can't do it exactly - const auto result5 = KnapsackSolver(KnapsackGroupOutputs(available_coins.All(), *wallet, filter_confirmed), 34 * CENT, CENT); + const auto result5 = KnapsackSolver(KnapsackGroupOutputs(available_coins, *wallet, filter_confirmed), 34 * CENT, CENT); BOOST_CHECK(result5); BOOST_CHECK_EQUAL(result5->GetSelectedValue(), 35 * CENT); // but 35 cents is closest BOOST_CHECK_EQUAL(result5->GetInputSet().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 - const auto result6 = KnapsackSolver(KnapsackGroupOutputs(available_coins.All(), *wallet, filter_confirmed), 7 * CENT, CENT); + const auto result6 = KnapsackSolver(KnapsackGroupOutputs(available_coins, *wallet, filter_confirmed), 7 * CENT, CENT); BOOST_CHECK(result6); BOOST_CHECK_EQUAL(result6->GetSelectedValue(), 7 * CENT); BOOST_CHECK_EQUAL(result6->GetInputSet().size(), 2U); // when we try making 8 cents, the smaller coins (1,2,5) are exactly enough. - const auto result7 = KnapsackSolver(KnapsackGroupOutputs(available_coins.All(), *wallet, filter_confirmed), 8 * CENT, CENT); + const auto result7 = KnapsackSolver(KnapsackGroupOutputs(available_coins, *wallet, filter_confirmed), 8 * CENT, CENT); BOOST_CHECK(result7); BOOST_CHECK(result7->GetSelectedValue() == 8 * CENT); BOOST_CHECK_EQUAL(result7->GetInputSet().size(), 3U); // when we try making 9 cents, no subset of smaller coins is enough, and we get the next bigger coin (10) - const auto result8 = KnapsackSolver(KnapsackGroupOutputs(available_coins.All(), *wallet, filter_confirmed), 9 * CENT, CENT); + const auto result8 = KnapsackSolver(KnapsackGroupOutputs(available_coins, *wallet, filter_confirmed), 9 * CENT, CENT); BOOST_CHECK(result8); BOOST_CHECK_EQUAL(result8->GetSelectedValue(), 10 * CENT); BOOST_CHECK_EQUAL(result8->GetInputSet().size(), 1U); @@ -493,12 +493,12 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test) add_coin(available_coins, *wallet, 30*CENT); // now we have 6+7+8+20+30 = 71 cents total // check that we have 71 and not 72 - const auto result9 = KnapsackSolver(KnapsackGroupOutputs(available_coins.All(), *wallet, filter_confirmed), 71 * CENT, CENT); + const auto result9 = KnapsackSolver(KnapsackGroupOutputs(available_coins, *wallet, filter_confirmed), 71 * CENT, CENT); BOOST_CHECK(result9); - BOOST_CHECK(!KnapsackSolver(KnapsackGroupOutputs(available_coins.All(), *wallet, filter_confirmed), 72 * CENT, CENT)); + BOOST_CHECK(!KnapsackSolver(KnapsackGroupOutputs(available_coins, *wallet, filter_confirmed), 72 * CENT, CENT)); // 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 - const auto result10 = KnapsackSolver(KnapsackGroupOutputs(available_coins.All(), *wallet, filter_confirmed), 16 * CENT, CENT); + const auto result10 = KnapsackSolver(KnapsackGroupOutputs(available_coins, *wallet, filter_confirmed), 16 * CENT, CENT); BOOST_CHECK(result10); BOOST_CHECK_EQUAL(result10->GetSelectedValue(), 20 * CENT); // we should get 20 in one coin BOOST_CHECK_EQUAL(result10->GetInputSet().size(), 1U); @@ -506,7 +506,7 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test) add_coin(available_coins, *wallet, 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 - const auto result11 = KnapsackSolver(KnapsackGroupOutputs(available_coins.All(), *wallet, filter_confirmed), 16 * CENT, CENT); + const auto result11 = KnapsackSolver(KnapsackGroupOutputs(available_coins, *wallet, filter_confirmed), 16 * CENT, CENT); BOOST_CHECK(result11); BOOST_CHECK_EQUAL(result11->GetSelectedValue(), 18 * CENT); // we should get 18 in 3 coins BOOST_CHECK_EQUAL(result11->GetInputSet().size(), 3U); @@ -514,13 +514,13 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test) add_coin(available_coins, *wallet, 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 - const auto result12 = KnapsackSolver(KnapsackGroupOutputs(available_coins.All(), *wallet, filter_confirmed), 16 * CENT, CENT); + const auto result12 = KnapsackSolver(KnapsackGroupOutputs(available_coins, *wallet, filter_confirmed), 16 * CENT, CENT); BOOST_CHECK(result12); BOOST_CHECK_EQUAL(result12->GetSelectedValue(), 18 * CENT); // we should get 18 in 1 coin BOOST_CHECK_EQUAL(result12->GetInputSet().size(), 1U); // because in the event of a tie, the biggest coin wins // now try making 11 cents. we should get 5+6 - const auto result13 = KnapsackSolver(KnapsackGroupOutputs(available_coins.All(), *wallet, filter_confirmed), 11 * CENT, CENT); + const auto result13 = KnapsackSolver(KnapsackGroupOutputs(available_coins, *wallet, filter_confirmed), 11 * CENT, CENT); BOOST_CHECK(result13); BOOST_CHECK_EQUAL(result13->GetSelectedValue(), 11 * CENT); BOOST_CHECK_EQUAL(result13->GetInputSet().size(), 2U); @@ -530,12 +530,12 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test) add_coin(available_coins, *wallet, 2*COIN); add_coin(available_coins, *wallet, 3*COIN); add_coin(available_coins, *wallet, 4*COIN); // now we have 5+6+7+8+18+20+30+100+200+300+400 = 1094 cents - const auto result14 = KnapsackSolver(KnapsackGroupOutputs(available_coins.All(), *wallet, filter_confirmed), 95 * CENT, CENT); + const auto result14 = KnapsackSolver(KnapsackGroupOutputs(available_coins, *wallet, filter_confirmed), 95 * CENT, CENT); BOOST_CHECK(result14); BOOST_CHECK_EQUAL(result14->GetSelectedValue(), 1 * COIN); // we should get 1 BTC in 1 coin BOOST_CHECK_EQUAL(result14->GetInputSet().size(), 1U); - const auto result15 = KnapsackSolver(KnapsackGroupOutputs(available_coins.All(), *wallet, filter_confirmed), 195 * CENT, CENT); + const auto result15 = KnapsackSolver(KnapsackGroupOutputs(available_coins, *wallet, filter_confirmed), 195 * CENT, CENT); BOOST_CHECK(result15); BOOST_CHECK_EQUAL(result15->GetSelectedValue(), 2 * COIN); // we should get 2 BTC in 1 coin BOOST_CHECK_EQUAL(result15->GetInputSet().size(), 1U); @@ -551,7 +551,7 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test) // try making 1 * CENT from the 1.5 * CENT // we'll get change smaller than CENT whatever happens, so can expect CENT exactly - const auto result16 = KnapsackSolver(KnapsackGroupOutputs(available_coins.All(), *wallet, filter_confirmed), CENT, CENT); + const auto result16 = KnapsackSolver(KnapsackGroupOutputs(available_coins, *wallet, filter_confirmed), CENT, CENT); BOOST_CHECK(result16); BOOST_CHECK_EQUAL(result16->GetSelectedValue(), CENT); @@ -559,7 +559,7 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test) add_coin(available_coins, *wallet, 1111*CENT); // try making 1 from 0.1 + 0.2 + 0.3 + 0.4 + 0.5 + 1111 = 1112.5 - const auto result17 = KnapsackSolver(KnapsackGroupOutputs(available_coins.All(), *wallet, filter_confirmed), 1 * CENT, CENT); + const auto result17 = KnapsackSolver(KnapsackGroupOutputs(available_coins, *wallet, filter_confirmed), 1 * CENT, CENT); BOOST_CHECK(result17); BOOST_CHECK_EQUAL(result17->GetSelectedValue(), 1 * CENT); // we should get the exact amount @@ -568,7 +568,7 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test) add_coin(available_coins, *wallet, CENT * 7 / 10); // and try again to make 1.0 * CENT - const auto result18 = KnapsackSolver(KnapsackGroupOutputs(available_coins.All(), *wallet, filter_confirmed), 1 * CENT, CENT); + const auto result18 = KnapsackSolver(KnapsackGroupOutputs(available_coins, *wallet, filter_confirmed), 1 * CENT, CENT); BOOST_CHECK(result18); BOOST_CHECK_EQUAL(result18->GetSelectedValue(), 1 * CENT); // we should get the exact amount @@ -578,7 +578,7 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test) for (int j = 0; j < 20; j++) add_coin(available_coins, *wallet, 50000 * COIN); - const auto result19 = KnapsackSolver(KnapsackGroupOutputs(available_coins.All(), *wallet, filter_confirmed), 500000 * COIN, CENT); + const auto result19 = KnapsackSolver(KnapsackGroupOutputs(available_coins, *wallet, filter_confirmed), 500000 * COIN, CENT); BOOST_CHECK(result19); BOOST_CHECK_EQUAL(result19->GetSelectedValue(), 500000 * COIN); // we should get the exact amount BOOST_CHECK_EQUAL(result19->GetInputSet().size(), 10U); // in ten coins @@ -592,7 +592,7 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test) add_coin(available_coins, *wallet, CENT * 6 / 10); add_coin(available_coins, *wallet, CENT * 7 / 10); add_coin(available_coins, *wallet, 1111 * CENT); - const auto result20 = KnapsackSolver(KnapsackGroupOutputs(available_coins.All(), *wallet, filter_confirmed), 1 * CENT, CENT); + const auto result20 = KnapsackSolver(KnapsackGroupOutputs(available_coins, *wallet, filter_confirmed), 1 * CENT, CENT); BOOST_CHECK(result20); BOOST_CHECK_EQUAL(result20->GetSelectedValue(), 1111 * CENT); // we get the bigger coin BOOST_CHECK_EQUAL(result20->GetInputSet().size(), 1U); @@ -603,7 +603,7 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test) add_coin(available_coins, *wallet, CENT * 6 / 10); add_coin(available_coins, *wallet, CENT * 8 / 10); add_coin(available_coins, *wallet, 1111 * CENT); - const auto result21 = KnapsackSolver(KnapsackGroupOutputs(available_coins.All(), *wallet, filter_confirmed), CENT, CENT); + const auto result21 = KnapsackSolver(KnapsackGroupOutputs(available_coins, *wallet, filter_confirmed), CENT, CENT); BOOST_CHECK(result21); BOOST_CHECK_EQUAL(result21->GetSelectedValue(), CENT); // we should get the exact amount BOOST_CHECK_EQUAL(result21->GetInputSet().size(), 2U); // in two coins 0.4+0.6 @@ -615,13 +615,13 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test) add_coin(available_coins, *wallet, CENT * 100); // trying to make 100.01 from these three coins - const auto result22 = KnapsackSolver(KnapsackGroupOutputs(available_coins.All(), *wallet, filter_confirmed), CENT * 10001 / 100, CENT); + const auto result22 = KnapsackSolver(KnapsackGroupOutputs(available_coins, *wallet, filter_confirmed), CENT * 10001 / 100, CENT); BOOST_CHECK(result22); BOOST_CHECK_EQUAL(result22->GetSelectedValue(), CENT * 10105 / 100); // we should get all coins BOOST_CHECK_EQUAL(result22->GetInputSet().size(), 3U); // but if we try to make 99.9, we should take the bigger of the two small coins to avoid small change - const auto result23 = KnapsackSolver(KnapsackGroupOutputs(available_coins.All(), *wallet, filter_confirmed), CENT * 9990 / 100, CENT); + const auto result23 = KnapsackSolver(KnapsackGroupOutputs(available_coins, *wallet, filter_confirmed), CENT * 9990 / 100, CENT); BOOST_CHECK(result23); BOOST_CHECK_EQUAL(result23->GetSelectedValue(), 101 * CENT); BOOST_CHECK_EQUAL(result23->GetInputSet().size(), 2U); @@ -636,7 +636,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++) { - const auto result24 = KnapsackSolver(KnapsackGroupOutputs(available_coins.All(), *wallet, filter_confirmed), 2000, CENT); + const auto result24 = KnapsackSolver(KnapsackGroupOutputs(available_coins, *wallet, filter_confirmed), 2000, CENT); BOOST_CHECK(result24); if (amt - 2000 < CENT) { @@ -727,7 +727,7 @@ BOOST_AUTO_TEST_CASE(ApproximateBestSubset) add_coin(available_coins, *wallet, 1000 * COIN); add_coin(available_coins, *wallet, 3 * COIN); - const auto result = KnapsackSolver(KnapsackGroupOutputs(available_coins.All(), *wallet, filter_standard), 1003 * COIN, CENT, rand); + const auto result = KnapsackSolver(KnapsackGroupOutputs(available_coins, *wallet, filter_standard), 1003 * COIN, CENT, rand); BOOST_CHECK(result); BOOST_CHECK_EQUAL(result->GetSelectedValue(), 1003 * COIN); BOOST_CHECK_EQUAL(result->GetInputSet().size(), 2U); @@ -943,7 +943,7 @@ static util::Result<SelectionResult> select_coins(const CAmount& target, const C static bool has_coin(const CoinSet& set, CAmount amount) { - return std::any_of(set.begin(), set.end(), [&](const auto& coin) { return coin.GetEffectiveValue() == amount; }); + return std::any_of(set.begin(), set.end(), [&](const auto& coin) { return coin->GetEffectiveValue() == amount; }); } BOOST_AUTO_TEST_CASE(check_max_weight) diff --git a/src/wallet/test/fuzz/coinselection.cpp b/src/wallet/test/fuzz/coinselection.cpp index 1a0708c43a..304190eec1 100644 --- a/src/wallet/test/fuzz/coinselection.cpp +++ b/src/wallet/test/fuzz/coinselection.cpp @@ -29,8 +29,10 @@ static void GroupCoins(FuzzedDataProvider& fuzzed_data_provider, const std::vect auto output_group = OutputGroup(coin_params); bool valid_outputgroup{false}; for (auto& coin : coins) { - output_group.Insert(coin, /*ancestors=*/0, /*descendants=*/0, positive_only); - // If positive_only was specified, nothing may have been inserted, leading to an empty output group + if (!positive_only || (positive_only && coin.GetEffectiveValue() > 0)) { + output_group.Insert(std::make_shared<COutput>(coin), /*ancestors=*/0, /*descendants=*/0); + } + // If positive_only was specified, nothing was inserted, leading to an empty output group // that would be invalid for the BnB algorithm valid_outputgroup = !positive_only || output_group.GetSelectionAmount() > 0; if (valid_outputgroup && fuzzed_data_provider.ConsumeBool()) { diff --git a/src/wallet/test/group_outputs_tests.cpp b/src/wallet/test/group_outputs_tests.cpp new file mode 100644 index 0000000000..283e87989c --- /dev/null +++ b/src/wallet/test/group_outputs_tests.cpp @@ -0,0 +1,234 @@ +// Copyright (c) 2022 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php. + +#include <test/util/setup_common.h> + +#include <wallet/coinselection.h> +#include <wallet/spend.h> +#include <wallet/wallet.h> + +#include <boost/test/unit_test.hpp> + +namespace wallet { +BOOST_FIXTURE_TEST_SUITE(group_outputs_tests, TestingSetup) + +static int nextLockTime = 0; + +static std::shared_ptr<CWallet> NewWallet(const node::NodeContext& m_node) +{ + std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", CreateMockWalletDatabase()); + wallet->LoadWallet(); + LOCK(wallet->cs_wallet); + wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS); + wallet->SetupDescriptorScriptPubKeyMans(); + return wallet; +} + +static void addCoin(CoinsResult& coins, + CWallet& wallet, + const CTxDestination& dest, + const CAmount& nValue, + bool is_from_me, + CFeeRate fee_rate = CFeeRate(0), + int depth = 6) +{ + CMutableTransaction tx; + tx.nLockTime = nextLockTime++; // so all transactions get different hashes + tx.vout.resize(1); + tx.vout[0].nValue = nValue; + tx.vout[0].scriptPubKey = GetScriptForDestination(dest); + + const uint256& txid = tx.GetHash(); + LOCK(wallet.cs_wallet); + auto ret = wallet.mapWallet.emplace(std::piecewise_construct, std::forward_as_tuple(txid), std::forward_as_tuple(MakeTransactionRef(std::move(tx)), TxStateInactive{})); + assert(ret.second); + CWalletTx& wtx = (*ret.first).second; + const auto& txout = wtx.tx->vout.at(0); + coins.Add(*Assert(OutputTypeFromDestination(dest)), + {COutPoint(wtx.GetHash(), 0), + txout, + depth, + CalculateMaximumSignedInputSize(txout, &wallet, /*coin_control=*/nullptr), + /*spendable=*/ true, + /*solvable=*/ true, + /*safe=*/ true, + wtx.GetTxTime(), + is_from_me, + fee_rate}); +} + + CoinSelectionParams makeSelectionParams(FastRandomContext& rand, bool avoid_partial_spends) +{ + return CoinSelectionParams{ + rand, + /*change_output_size=*/ 0, + /*change_spend_size=*/ 0, + /*min_change_target=*/ CENT, + /*effective_feerate=*/ CFeeRate(0), + /*long_term_feerate=*/ CFeeRate(0), + /*discard_feerate=*/ CFeeRate(0), + /*tx_noinputs_size=*/ 0, + /*avoid_partial=*/ avoid_partial_spends, + }; +} + +class GroupVerifier +{ +public: + std::shared_ptr<CWallet> wallet{nullptr}; + CoinsResult coins_pool; + FastRandomContext rand; + + void GroupVerify(const OutputType type, + const CoinEligibilityFilter& filter, + bool avoid_partial_spends, + bool positive_only, + int expected_size) + { + OutputGroupTypeMap groups = GroupOutputs(*wallet, coins_pool, makeSelectionParams(rand, avoid_partial_spends), {{filter}})[filter]; + std::vector<OutputGroup>& groups_out = positive_only ? groups.groups_by_type[type].positive_group : + groups.groups_by_type[type].mixed_group; + BOOST_CHECK_EQUAL(groups_out.size(), expected_size); + } + + void GroupAndVerify(const OutputType type, + const CoinEligibilityFilter& filter, + int expected_with_partial_spends_size, + int expected_without_partial_spends_size, + bool positive_only) + { + // First avoid partial spends + GroupVerify(type, filter, /*avoid_partial_spends=*/false, positive_only, expected_with_partial_spends_size); + // Second don't avoid partial spends + GroupVerify(type, filter, /*avoid_partial_spends=*/true, positive_only, expected_without_partial_spends_size); + } +}; + +BOOST_AUTO_TEST_CASE(outputs_grouping_tests) +{ + const auto& wallet = NewWallet(m_node); + GroupVerifier group_verifier; + group_verifier.wallet = wallet; + + const CoinEligibilityFilter& BASIC_FILTER{1, 6, 0}; + + // ################################################################################# + // 10 outputs from different txs going to the same script + // 1) if partial spends is enabled --> must not be grouped + // 2) if partial spends is not enabled --> must be grouped into a single OutputGroup + // ################################################################################# + + unsigned long GROUP_SIZE = 10; + const CTxDestination dest = *Assert(wallet->GetNewDestination(OutputType::BECH32, "")); + for (unsigned long i = 0; i < GROUP_SIZE; i++) { + addCoin(group_verifier.coins_pool, *wallet, dest, 10 * COIN, /*is_from_me=*/true); + } + + group_verifier.GroupAndVerify(OutputType::BECH32, + BASIC_FILTER, + /*expected_with_partial_spends_size=*/ GROUP_SIZE, + /*expected_without_partial_spends_size=*/ 1, + /*positive_only=*/ true); + + // #################################################################################### + // 3) 10 more UTXO are added with a different script --> must be grouped into a single + // group for avoid partial spends and 10 different output groups for partial spends + // #################################################################################### + + const CTxDestination dest2 = *Assert(wallet->GetNewDestination(OutputType::BECH32, "")); + for (unsigned long i = 0; i < GROUP_SIZE; i++) { + addCoin(group_verifier.coins_pool, *wallet, dest2, 5 * COIN, /*is_from_me=*/true); + } + + group_verifier.GroupAndVerify(OutputType::BECH32, + BASIC_FILTER, + /*expected_with_partial_spends_size=*/ GROUP_SIZE * 2, + /*expected_without_partial_spends_size=*/ 2, + /*positive_only=*/ true); + + // ################################################################################ + // 4) Now add a negative output --> which will be skipped if "positive_only" is set + // ################################################################################ + + const CTxDestination dest3 = *Assert(wallet->GetNewDestination(OutputType::BECH32, "")); + addCoin(group_verifier.coins_pool, *wallet, dest3, 1, true, CFeeRate(100)); + BOOST_CHECK(group_verifier.coins_pool.coins[OutputType::BECH32].back().GetEffectiveValue() <= 0); + + // First expect no changes with "positive_only" enabled + group_verifier.GroupAndVerify(OutputType::BECH32, + BASIC_FILTER, + /*expected_with_partial_spends_size=*/ GROUP_SIZE * 2, + /*expected_without_partial_spends_size=*/ 2, + /*positive_only=*/ true); + + // Then expect changes with "positive_only" disabled + group_verifier.GroupAndVerify(OutputType::BECH32, + BASIC_FILTER, + /*expected_with_partial_spends_size=*/ GROUP_SIZE * 2 + 1, + /*expected_without_partial_spends_size=*/ 3, + /*positive_only=*/ false); + + + // ############################################################################## + // 5) Try to add a non-eligible UTXO (due not fulfilling the min depth target for + // "not mine" UTXOs) --> it must not be added to any group + // ############################################################################## + + const CTxDestination dest4 = *Assert(wallet->GetNewDestination(OutputType::BECH32, "")); + addCoin(group_verifier.coins_pool, *wallet, dest4, 6 * COIN, + /*is_from_me=*/false, CFeeRate(0), /*depth=*/5); + + // Expect no changes from this round and the previous one (point 4) + group_verifier.GroupAndVerify(OutputType::BECH32, + BASIC_FILTER, + /*expected_with_partial_spends_size=*/ GROUP_SIZE * 2 + 1, + /*expected_without_partial_spends_size=*/ 3, + /*positive_only=*/ false); + + + // ############################################################################## + // 6) Try to add a non-eligible UTXO (due not fulfilling the min depth target for + // "mine" UTXOs) --> it must not be added to any group + // ############################################################################## + + const CTxDestination dest5 = *Assert(wallet->GetNewDestination(OutputType::BECH32, "")); + addCoin(group_verifier.coins_pool, *wallet, dest5, 6 * COIN, + /*is_from_me=*/true, CFeeRate(0), /*depth=*/0); + + // Expect no changes from this round and the previous one (point 5) + group_verifier.GroupAndVerify(OutputType::BECH32, + BASIC_FILTER, + /*expected_with_partial_spends_size=*/ GROUP_SIZE * 2 + 1, + /*expected_without_partial_spends_size=*/ 3, + /*positive_only=*/ false); + + // ########################################################################################### + // 7) Surpass the OUTPUT_GROUP_MAX_ENTRIES and verify that a second partial group gets created + // ########################################################################################### + + const CTxDestination dest7 = *Assert(wallet->GetNewDestination(OutputType::BECH32, "")); + uint16_t NUM_SINGLE_ENTRIES = 101; + for (unsigned long i = 0; i < NUM_SINGLE_ENTRIES; i++) { // OUTPUT_GROUP_MAX_ENTRIES{100} + addCoin(group_verifier.coins_pool, *wallet, dest7, 9 * COIN, /*is_from_me=*/true); + } + + // Exclude partial groups only adds one more group to the previous test case (point 6) + int PREVIOUS_ROUND_COUNT = GROUP_SIZE * 2 + 1; + group_verifier.GroupAndVerify(OutputType::BECH32, + BASIC_FILTER, + /*expected_with_partial_spends_size=*/ PREVIOUS_ROUND_COUNT + NUM_SINGLE_ENTRIES, + /*expected_without_partial_spends_size=*/ 4, + /*positive_only=*/ false); + + // Include partial groups should add one more group inside the "avoid partial spends" count + const CoinEligibilityFilter& avoid_partial_groups_filter{1, 6, 0, 0, /*include_partial=*/ true}; + group_verifier.GroupAndVerify(OutputType::BECH32, + avoid_partial_groups_filter, + /*expected_with_partial_spends_size=*/ PREVIOUS_ROUND_COUNT + NUM_SINGLE_ENTRIES, + /*expected_without_partial_spends_size=*/ 5, + /*positive_only=*/ false); +} + +BOOST_AUTO_TEST_SUITE_END() +} // end namespace wallet diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index e5c03849af..61851ce4ba 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1648,8 +1648,8 @@ bool DummySignInput(const SigningProvider& provider, CTxIn &tx_in, const CTxOut const CScript& scriptPubKey = txout.scriptPubKey; SignatureData sigdata; - // Use max sig if watch only inputs were used or if this particular input is an external input - // to ensure a sufficient fee is attained for the requested feerate. + // Use max sig if watch only inputs were used, if this particular input is an external input, + // or if this wallet uses an external signer, to ensure a sufficient fee is attained for the requested feerate. const bool use_max_sig = coin_control && (coin_control->fAllowWatchOnly || coin_control->IsExternalSelected(tx_in.prevout) || !can_grind_r); if (!ProduceSignature(provider, use_max_sig ? DUMMY_MAXIMUM_SIGNATURE_CREATOR : DUMMY_SIGNATURE_CREATOR, scriptPubKey, sigdata)) { return false; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 16eee1e050..e8c18dbb67 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -592,12 +592,6 @@ public: bool SubmitTxMemoryPoolAndRelay(CWalletTx& wtx, std::string& err_string, bool relay) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); - bool DummySignTx(CMutableTransaction &txNew, const std::set<CTxOut> &txouts, const CCoinControl* coin_control = nullptr) const - { - std::vector<CTxOut> v_txouts(txouts.size()); - std::copy(txouts.begin(), txouts.end(), v_txouts.begin()); - return DummySignTx(txNew, v_txouts, coin_control); - } bool DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut> &txouts, const CCoinControl* coin_control = nullptr) const; bool ImportScripts(const std::set<CScript> scripts, int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); diff --git a/test/functional/feature_abortnode.py b/test/functional/feature_abortnode.py index 32cf4a47f4..fa1bb6506a 100755 --- a/test/functional/feature_abortnode.py +++ b/test/functional/feature_abortnode.py @@ -19,7 +19,6 @@ class AbortNodeTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 - self.rpc_timeout = 240 def setup_network(self): self.setup_nodes() @@ -41,7 +40,7 @@ class AbortNodeTest(BitcoinTestFramework): # Check that node0 aborted self.log.info("Waiting for crash") - self.nodes[0].wait_until_stopped(timeout=200) + self.nodes[0].wait_until_stopped(timeout=5) self.log.info("Node crashed - now verifying restart fails") self.nodes[0].assert_start_raises_init_error() diff --git a/test/functional/interface_rest.py b/test/functional/interface_rest.py index 0dddc8946a..aafee7e87c 100755 --- a/test/functional/interface_rest.py +++ b/test/functional/interface_rest.py @@ -347,6 +347,34 @@ class RESTTest (BitcoinTestFramework): assert_equal(json_obj[tx]['spentby'], txs[i + 1:i + 2]) assert_equal(json_obj[tx]['depends'], txs[i - 1:i]) + # Check the mempool response for explicit parameters + json_obj = self.test_rest_request("/mempool/contents", query_params={"verbose": "true", "mempool_sequence": "false"}) + assert_equal(json_obj, raw_mempool_verbose) + + # Check the mempool response for not verbose + json_obj = self.test_rest_request("/mempool/contents", query_params={"verbose": "false"}) + raw_mempool = self.nodes[0].getrawmempool(verbose=False) + + assert_equal(json_obj, raw_mempool) + + # Check the mempool response for sequence + json_obj = self.test_rest_request("/mempool/contents", query_params={"verbose": "false", "mempool_sequence": "true"}) + raw_mempool = self.nodes[0].getrawmempool(verbose=False, mempool_sequence=True) + + assert_equal(json_obj, raw_mempool) + + # Check for error response if verbose=true and mempool_sequence=true + resp = self.test_rest_request("/mempool/contents", ret_type=RetType.OBJ, status=400, query_params={"verbose": "true", "mempool_sequence": "true"}) + assert_equal(resp.read().decode('utf-8').strip(), 'Verbose results cannot contain mempool sequence values. (hint: set "verbose=false")') + + # Check for error response if verbose is not "true" or "false" + resp = self.test_rest_request("/mempool/contents", ret_type=RetType.OBJ, status=400, query_params={"verbose": "TRUE"}) + assert_equal(resp.read().decode('utf-8').strip(), 'The "verbose" query parameter must be either "true" or "false".') + + # Check for error response if mempool_sequence is not "true" or "false" + resp = self.test_rest_request("/mempool/contents", ret_type=RetType.OBJ, status=400, query_params={"verbose": "false", "mempool_sequence": "TRUE"}) + assert_equal(resp.read().decode('utf-8').strip(), 'The "mempool_sequence" query parameter must be either "true" or "false".') + # Now mine the transactions newblockhash = self.generate(self.nodes[1], 1) diff --git a/test/functional/mempool_sigoplimit.py b/test/functional/mempool_sigoplimit.py new file mode 100755 index 0000000000..87934da681 --- /dev/null +++ b/test/functional/mempool_sigoplimit.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python3 +# Copyright (c) 2023 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 sigop limit mempool policy (`-bytespersigop` parameter)""" +from math import ceil + +from test_framework.messages import ( + COutPoint, + CTransaction, + CTxIn, + CTxInWitness, + CTxOut, + WITNESS_SCALE_FACTOR, +) +from test_framework.script import ( + CScript, + OP_CHECKMULTISIG, + OP_CHECKSIG, + OP_ENDIF, + OP_FALSE, + OP_IF, + OP_RETURN, + OP_TRUE, +) +from test_framework.script_util import ( + script_to_p2wsh_script, +) +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import ( + assert_equal, + assert_greater_than_or_equal, +) +from test_framework.wallet import MiniWallet + + +DEFAULT_BYTES_PER_SIGOP = 20 # default setting + + +class BytesPerSigOpTest(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 1 + # allow large datacarrier output to pad transactions + self.extra_args = [['-datacarriersize=100000']] + + def create_p2wsh_spending_tx(self, witness_script, output_script): + """Create a 1-input-1-output P2WSH spending transaction with only the + witness script in the witness stack and the given output script.""" + # create P2WSH address and fund it via MiniWallet first + txid, vout = self.wallet.send_to( + from_node=self.nodes[0], + scriptPubKey=script_to_p2wsh_script(witness_script), + amount=1000000, + ) + + # create spending transaction + tx = CTransaction() + tx.vin = [CTxIn(COutPoint(int(txid, 16), vout))] + tx.wit.vtxinwit = [CTxInWitness()] + tx.wit.vtxinwit[0].scriptWitness.stack = [bytes(witness_script)] + tx.vout = [CTxOut(500000, output_script)] + return tx + + def test_sigops_limit(self, bytes_per_sigop, num_sigops): + sigop_equivalent_vsize = ceil(num_sigops * bytes_per_sigop / WITNESS_SCALE_FACTOR) + self.log.info(f"- {num_sigops} sigops (equivalent size of {sigop_equivalent_vsize} vbytes)") + + # create a template tx with the specified sigop cost in the witness script + # (note that the sigops count even though being in a branch that's not executed) + num_multisigops = num_sigops // 20 + num_singlesigops = num_sigops % 20 + witness_script = CScript( + [OP_FALSE, OP_IF] + + [OP_CHECKMULTISIG]*num_multisigops + + [OP_CHECKSIG]*num_singlesigops + + [OP_ENDIF, OP_TRUE] + ) + # use a 256-byte data-push as lower bound in the output script, in order + # to avoid having to compensate for tx size changes caused by varying + # length serialization sizes (both for scriptPubKey and data-push lengths) + tx = self.create_p2wsh_spending_tx(witness_script, CScript([OP_RETURN, b'X'*256])) + + # bump the tx to reach the sigop-limit equivalent size by padding the datacarrier output + assert_greater_than_or_equal(sigop_equivalent_vsize, tx.get_vsize()) + vsize_to_pad = sigop_equivalent_vsize - tx.get_vsize() + tx.vout[0].scriptPubKey = CScript([OP_RETURN, b'X'*(256+vsize_to_pad)]) + assert_equal(sigop_equivalent_vsize, tx.get_vsize()) + + res = self.nodes[0].testmempoolaccept([tx.serialize().hex()])[0] + assert_equal(res['allowed'], True) + assert_equal(res['vsize'], sigop_equivalent_vsize) + + # increase the tx's vsize to be right above the sigop-limit equivalent size + # => tx's vsize in mempool should also grow accordingly + tx.vout[0].scriptPubKey = CScript([OP_RETURN, b'X'*(256+vsize_to_pad+1)]) + res = self.nodes[0].testmempoolaccept([tx.serialize().hex()])[0] + assert_equal(res['allowed'], True) + assert_equal(res['vsize'], sigop_equivalent_vsize+1) + + # decrease the tx's vsize to be right below the sigop-limit equivalent size + # => tx's vsize in mempool should stick at the sigop-limit equivalent + # bytes level, as it is higher than the tx's serialized vsize + # (the maximum of both is taken) + tx.vout[0].scriptPubKey = CScript([OP_RETURN, b'X'*(256+vsize_to_pad-1)]) + res = self.nodes[0].testmempoolaccept([tx.serialize().hex()])[0] + assert_equal(res['allowed'], True) + assert_equal(res['vsize'], sigop_equivalent_vsize) + + def run_test(self): + self.wallet = MiniWallet(self.nodes[0]) + + for bytes_per_sigop in (DEFAULT_BYTES_PER_SIGOP, 43, 81, 165, 327, 649, 1072): + if bytes_per_sigop == DEFAULT_BYTES_PER_SIGOP: + self.log.info(f"Test default sigops limit setting ({bytes_per_sigop} bytes per sigop)...") + else: + bytespersigop_parameter = f"-bytespersigop={bytes_per_sigop}" + self.log.info(f"Test sigops limit setting {bytespersigop_parameter}...") + self.restart_node(0, extra_args=[bytespersigop_parameter] + self.extra_args[0]) + + for num_sigops in (69, 101, 142, 183, 222): + self.test_sigops_limit(bytes_per_sigop, num_sigops) + + +if __name__ == '__main__': + BytesPerSigOpTest().main() diff --git a/test/functional/mining_getblocktemplate_longpoll.py b/test/functional/mining_getblocktemplate_longpoll.py index ec492f9e72..53182eb79e 100755 --- a/test/functional/mining_getblocktemplate_longpoll.py +++ b/test/functional/mining_getblocktemplate_longpoll.py @@ -4,7 +4,6 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test longpolling with getblocktemplate.""" -from decimal import Decimal import random import threading @@ -47,9 +46,9 @@ class GetBlockTemplateLPTest(BitcoinTestFramework): thr.join(5) # wait 5 seconds or until thread exits assert thr.is_alive() - miniwallets = [MiniWallet(node) for node in self.nodes] + self.miniwallet = MiniWallet(self.nodes[0]) self.log.info("Test that longpoll will terminate if another node generates a block") - self.generate(miniwallets[1], 1) # generate a block on another node + self.generate(self.nodes[1], 1) # generate a block on another node # check that thread will exit now that new transaction entered mempool thr.join(5) # wait 5 seconds or until thread exits assert not thr.is_alive() @@ -57,18 +56,15 @@ class GetBlockTemplateLPTest(BitcoinTestFramework): self.log.info("Test that longpoll will terminate if we generate a block ourselves") thr = LongpollThread(self.nodes[0]) thr.start() - self.generate(miniwallets[0], 1) # generate a block on own node + self.generate(self.nodes[0], 1) # generate a block on own node thr.join(5) # wait 5 seconds or until thread exits assert not thr.is_alive() self.log.info("Test that introducing a new transaction into the mempool will terminate the longpoll") thr = LongpollThread(self.nodes[0]) thr.start() - # generate a random transaction and submit it - min_relay_fee = self.nodes[0].getnetworkinfo()["relayfee"] - fee_rate = min_relay_fee + Decimal('0.00000010') * random.randint(0,20) - miniwallets[0].send_self_transfer(from_node=random.choice(self.nodes), - fee_rate=fee_rate) + # generate a transaction and submit it + self.miniwallet.send_self_transfer(from_node=random.choice(self.nodes)) # after one minute, every 10 seconds the mempool is probed, so in 80 seconds it should have returned thr.join(60 + 20) assert not thr.is_alive() diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py index 7a0cedb1f5..6022042c11 100755 --- a/test/functional/rpc_blockchain.py +++ b/test/functional/rpc_blockchain.py @@ -69,6 +69,7 @@ class BlockchainTest(BitcoinTestFramework): def run_test(self): self.wallet = MiniWallet(self.nodes[0]) + self._test_prune_disk_space() self.mine_chain() self._test_max_future_block_time() self.restart_node( @@ -100,6 +101,13 @@ class BlockchainTest(BitcoinTestFramework): self.generate(self.wallet, 1) assert_equal(self.nodes[0].getblockchaininfo()['blocks'], HEIGHT) + def _test_prune_disk_space(self): + self.log.info("Test that a manually pruned node does not run into " + "integer overflow on first start up") + self.restart_node(0, extra_args=["-prune=1"]) + self.log.info("Avoid warning when assumed chain size is enough") + self.restart_node(0, extra_args=["-prune=123456789"]) + def _test_max_future_block_time(self): self.stop_node(0) self.log.info("A block tip of more than MAX_FUTURE_BLOCK_TIME in the future raises an error") diff --git a/test/functional/rpc_getblockfrompeer.py b/test/functional/rpc_getblockfrompeer.py index dddc779763..2f093bebff 100755 --- a/test/functional/rpc_getblockfrompeer.py +++ b/test/functional/rpc_getblockfrompeer.py @@ -24,14 +24,19 @@ from test_framework.util import ( class GetBlockFromPeerTest(BitcoinTestFramework): def set_test_params(self): - self.num_nodes = 2 + self.num_nodes = 3 + self.extra_args = [ + [], + [], + ["-fastprune", "-prune=1"] + ] def setup_network(self): self.setup_nodes() - def check_for_block(self, hash): + def check_for_block(self, node, hash): try: - self.nodes[0].getblock(hash) + self.nodes[node].getblock(hash) return True except JSONRPCException: return False @@ -48,7 +53,7 @@ class GetBlockFromPeerTest(BitcoinTestFramework): self.log.info("Connect nodes to sync headers") self.connect_nodes(0, 1) - self.sync_blocks() + self.sync_blocks(self.nodes[0:2]) self.log.info("Node 0 should only have the header for node 1's block 3") x = next(filter(lambda x: x['hash'] == short_tip, self.nodes[0].getchaintips())) @@ -81,7 +86,7 @@ class GetBlockFromPeerTest(BitcoinTestFramework): self.log.info("Successful fetch") result = self.nodes[0].getblockfrompeer(short_tip, peer_0_peer_1_id) - self.wait_until(lambda: self.check_for_block(short_tip), timeout=1) + self.wait_until(lambda: self.check_for_block(node=0, hash=short_tip), timeout=1) assert_equal(result, {}) self.log.info("Don't fetch blocks we already have") @@ -111,6 +116,40 @@ class GetBlockFromPeerTest(BitcoinTestFramework): error_msg = "In prune mode, only blocks that the node has already synced previously can be fetched from a peer" assert_raises_rpc_error(-1, error_msg, self.nodes[1].getblockfrompeer, blockhash, node1_interface_id) + self.log.info("Connect pruned node") + # We need to generate more blocks to be able to prune + self.connect_nodes(0, 2) + pruned_node = self.nodes[2] + self.generate(self.nodes[0], 400, sync_fun=self.no_op) + self.sync_blocks([self.nodes[0], pruned_node]) + pruneheight = pruned_node.pruneblockchain(300) + assert_equal(pruneheight, 248) + # Ensure the block is actually pruned + pruned_block = self.nodes[0].getblockhash(2) + assert_raises_rpc_error(-1, "Block not available (pruned data)", pruned_node.getblock, pruned_block) + + self.log.info("Fetch pruned block") + peers = pruned_node.getpeerinfo() + assert_equal(len(peers), 1) + pruned_node_peer_0_id = peers[0]["id"] + result = pruned_node.getblockfrompeer(pruned_block, pruned_node_peer_0_id) + self.wait_until(lambda: self.check_for_block(node=2, hash=pruned_block), timeout=1) + assert_equal(result, {}) + + self.log.info("Fetched block persists after next pruning event") + self.generate(self.nodes[0], 250, sync_fun=self.no_op) + self.sync_blocks([self.nodes[0], pruned_node]) + pruneheight += 251 + assert_equal(pruned_node.pruneblockchain(700), pruneheight) + assert_equal(pruned_node.getblock(pruned_block)["hash"], "36c56c5b5ebbaf90d76b0d1a074dcb32d42abab75b7ec6fa0ffd9b4fbce8f0f7") + + self.log.info("Fetched block can be pruned again when prune height exceeds the height of the tip at the time when the block was fetched") + self.generate(self.nodes[0], 250, sync_fun=self.no_op) + self.sync_blocks([self.nodes[0], pruned_node]) + pruneheight += 250 + assert_equal(pruned_node.pruneblockchain(1000), pruneheight) + assert_raises_rpc_error(-1, "Block not available (pruned data)", pruned_node.getblock, pruned_block) + if __name__ == '__main__': GetBlockFromPeerTest().main() diff --git a/test/functional/rpc_invalid_address_message.py b/test/functional/rpc_invalid_address_message.py index 0c29efb85a..fd282a9bc1 100755 --- a/test/functional/rpc_invalid_address_message.py +++ b/test/functional/rpc_invalid_address_message.py @@ -64,7 +64,7 @@ class InvalidAddressErrorMessageTest(BitcoinTestFramework): def test_validateaddress(self): # Invalid Bech32 self.check_invalid(BECH32_INVALID_SIZE, 'Invalid Bech32 address data size') - self.check_invalid(BECH32_INVALID_PREFIX, 'Not a valid Bech32 or Base58 encoding') + self.check_invalid(BECH32_INVALID_PREFIX, 'Invalid or unsupported Segwit (Bech32) or Base58 encoding.') self.check_invalid(BECH32_INVALID_BECH32, 'Version 1+ witness address must use Bech32m checksum') self.check_invalid(BECH32_INVALID_BECH32M, 'Version 0 witness address must use Bech32 checksum') self.check_invalid(BECH32_INVALID_VERSION, 'Invalid Bech32 address witness version') @@ -84,16 +84,16 @@ class InvalidAddressErrorMessageTest(BitcoinTestFramework): self.check_valid(BECH32_VALID_MULTISIG) # Invalid Base58 - self.check_invalid(BASE58_INVALID_PREFIX, 'Invalid prefix for Base58-encoded address') - self.check_invalid(BASE58_INVALID_CHECKSUM, 'Invalid checksum or length of Base58 address') - self.check_invalid(BASE58_INVALID_LENGTH, 'Invalid checksum or length of Base58 address') + self.check_invalid(BASE58_INVALID_PREFIX, 'Invalid or unsupported Base58-encoded address.') + self.check_invalid(BASE58_INVALID_CHECKSUM, 'Invalid checksum or length of Base58 address (P2PKH or P2SH)') + self.check_invalid(BASE58_INVALID_LENGTH, 'Invalid checksum or length of Base58 address (P2PKH or P2SH)') # Valid Base58 self.check_valid(BASE58_VALID) # Invalid address format - self.check_invalid(INVALID_ADDRESS, 'Not a valid Bech32 or Base58 encoding') - self.check_invalid(INVALID_ADDRESS_2, 'Not a valid Bech32 or Base58 encoding') + self.check_invalid(INVALID_ADDRESS, 'Invalid or unsupported Segwit (Bech32) or Base58 encoding.') + self.check_invalid(INVALID_ADDRESS_2, 'Invalid or unsupported Segwit (Bech32) or Base58 encoding.') node = self.nodes[0] @@ -106,9 +106,9 @@ class InvalidAddressErrorMessageTest(BitcoinTestFramework): node = self.nodes[0] assert_raises_rpc_error(-5, "Invalid Bech32 address data size", node.getaddressinfo, BECH32_INVALID_SIZE) - assert_raises_rpc_error(-5, "Not a valid Bech32 or Base58 encoding", node.getaddressinfo, BECH32_INVALID_PREFIX) - assert_raises_rpc_error(-5, "Invalid prefix for Base58-encoded address", node.getaddressinfo, BASE58_INVALID_PREFIX) - assert_raises_rpc_error(-5, "Not a valid Bech32 or Base58 encoding", node.getaddressinfo, INVALID_ADDRESS) + assert_raises_rpc_error(-5, "Invalid or unsupported Segwit (Bech32) or Base58 encoding.", node.getaddressinfo, BECH32_INVALID_PREFIX) + assert_raises_rpc_error(-5, "Invalid or unsupported Base58-encoded address.", node.getaddressinfo, BASE58_INVALID_PREFIX) + assert_raises_rpc_error(-5, "Invalid or unsupported Segwit (Bech32) or Base58 encoding.", node.getaddressinfo, INVALID_ADDRESS) def run_test(self): self.test_validateaddress() diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py index 58a80e37a2..76886cc0ea 100755 --- a/test/functional/rpc_psbt.py +++ b/test/functional/rpc_psbt.py @@ -4,7 +4,6 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test the Partially Signed Transaction RPCs. """ - from decimal import Decimal from itertools import product @@ -27,6 +26,7 @@ from test_framework.psbt import ( PSBT_IN_SHA256, PSBT_IN_HASH160, PSBT_IN_HASH256, + PSBT_IN_NON_WITNESS_UTXO, PSBT_IN_WITNESS_UTXO, PSBT_OUT_TAP_TREE, ) @@ -59,13 +59,16 @@ class PSBTTest(BitcoinTestFramework): ["-walletrbf=0", "-changetype=legacy"], [] ] + # whitelist peers to speed up tx relay / mempool sync + for args in self.extra_args: + args.append("-whitelist=noban@127.0.0.1") self.supports_cli = False def skip_test_if_missing_module(self): self.skip_if_no_wallet() - # TODO: Re-enable this test with segwit v1 def test_utxo_conversion(self): + self.log.info("Check that non-witness UTXOs are removed for segwit v1+ inputs") mining_node = self.nodes[2] offline_node = self.nodes[0] online_node = self.nodes[1] @@ -77,34 +80,41 @@ class PSBTTest(BitcoinTestFramework): # Create watchonly on online_node online_node.createwallet(wallet_name='wonline', disable_private_keys=True) wonline = online_node.get_wallet_rpc('wonline') - w2 = online_node.get_wallet_rpc('') + w2 = online_node.get_wallet_rpc(self.default_wallet_name) # Mine a transaction that credits the offline address - offline_addr = offline_node.getnewaddress(address_type="p2sh-segwit") - online_addr = w2.getnewaddress(address_type="p2sh-segwit") + offline_addr = offline_node.getnewaddress(address_type="bech32m") + online_addr = w2.getnewaddress(address_type="bech32m") wonline.importaddress(offline_addr, "", False) - mining_node.sendtoaddress(address=offline_addr, amount=1.0) - self.generate(mining_node, nblocks=1) + mining_wallet = mining_node.get_wallet_rpc(self.default_wallet_name) + mining_wallet.sendtoaddress(address=offline_addr, amount=1.0) + self.generate(mining_node, nblocks=1, sync_fun=lambda: self.sync_all([online_node, mining_node])) - # Construct an unsigned PSBT on the online node (who doesn't know the output is Segwit, so will include a non-witness UTXO) + # Construct an unsigned PSBT on the online node utxos = wonline.listunspent(addresses=[offline_addr]) raw = wonline.createrawtransaction([{"txid":utxos[0]["txid"], "vout":utxos[0]["vout"]}],[{online_addr:0.9999}]) psbt = wonline.walletprocesspsbt(online_node.converttopsbt(raw))["psbt"] - assert "non_witness_utxo" in mining_node.decodepsbt(psbt)["inputs"][0] + assert not "not_witness_utxo" in mining_node.decodepsbt(psbt)["inputs"][0] + + # add non-witness UTXO manually + psbt_new = PSBT.from_base64(psbt) + prev_tx = wonline.gettransaction(utxos[0]["txid"])["hex"] + psbt_new.i[0].map[PSBT_IN_NON_WITNESS_UTXO] = bytes.fromhex(prev_tx) + assert "non_witness_utxo" in mining_node.decodepsbt(psbt_new.to_base64())["inputs"][0] - # Have the offline node sign the PSBT (which will update the UTXO to segwit) - signed_psbt = offline_node.walletprocesspsbt(psbt)["psbt"] - assert "witness_utxo" in mining_node.decodepsbt(signed_psbt)["inputs"][0] + # Have the offline node sign the PSBT (which will remove the non-witness UTXO) + signed_psbt = offline_node.walletprocesspsbt(psbt_new.to_base64())["psbt"] + assert not "non_witness_utxo" in mining_node.decodepsbt(signed_psbt)["inputs"][0] # Make sure we can mine the resulting transaction txid = mining_node.sendrawtransaction(mining_node.finalizepsbt(signed_psbt)["hex"]) - self.generate(mining_node, 1) + self.generate(mining_node, nblocks=1, sync_fun=lambda: self.sync_all([online_node, mining_node])) assert_equal(online_node.gettxout(txid,0)["confirmations"], 1) wonline.unloadwallet() # Reconnect - self.connect_nodes(0, 1) + self.connect_nodes(1, 0) self.connect_nodes(0, 2) def test_input_confs_control(self): @@ -571,8 +581,8 @@ class PSBTTest(BitcoinTestFramework): for i, signer in enumerate(signers): self.nodes[2].unloadwallet("wallet{}".format(i)) - # TODO: Re-enable this for segwit v1 - # self.test_utxo_conversion() + if self.options.descriptors: + self.test_utxo_conversion() self.test_input_confs_control() diff --git a/test/functional/test_framework/p2p.py b/test/functional/test_framework/p2p.py index c5768177bd..2433e52671 100755 --- a/test/functional/test_framework/p2p.py +++ b/test/functional/test_framework/p2p.py @@ -464,7 +464,7 @@ class P2PInterface(P2PConnection): def wait_for_connect(self, timeout=60): test_function = lambda: self.is_connected - wait_until_helper(test_function, timeout=timeout, lock=p2p_lock) + self.wait_until(test_function, timeout=timeout, check_connected=False) def wait_for_disconnect(self, timeout=60): test_function = lambda: not self.is_connected diff --git a/test/functional/test_framework/psbt.py b/test/functional/test_framework/psbt.py index 3a5b4ec74d..1eff4a250e 100644 --- a/test/functional/test_framework/psbt.py +++ b/test/functional/test_framework/psbt.py @@ -105,8 +105,8 @@ class PSBT: def deserialize(self, f): assert f.read(5) == b"psbt\xff" self.g = from_binary(PSBTMap, f) - assert 0 in self.g.map - self.tx = from_binary(CTransaction, self.g.map[0]) + assert PSBT_GLOBAL_UNSIGNED_TX in self.g.map + self.tx = from_binary(CTransaction, self.g.map[PSBT_GLOBAL_UNSIGNED_TX]) self.i = [from_binary(PSBTMap, f) for _ in self.tx.vin] self.o = [from_binary(PSBTMap, f) for _ in self.tx.vout] return self @@ -115,8 +115,8 @@ class PSBT: assert isinstance(self.g, PSBTMap) assert isinstance(self.i, list) and all(isinstance(x, PSBTMap) for x in self.i) assert isinstance(self.o, list) and all(isinstance(x, PSBTMap) for x in self.o) - assert 0 in self.g.map - tx = from_binary(CTransaction, self.g.map[0]) + assert PSBT_GLOBAL_UNSIGNED_TX in self.g.map + tx = from_binary(CTransaction, self.g.map[PSBT_GLOBAL_UNSIGNED_TX]) assert len(tx.vin) == len(self.i) assert len(tx.vout) == len(self.o) @@ -130,7 +130,7 @@ class PSBT: for m in self.i + self.o: m.map.clear() - self.g = PSBTMap(map={0: self.g.map[0]}) + self.g = PSBTMap(map={PSBT_GLOBAL_UNSIGNED_TX: self.g.map[PSBT_GLOBAL_UNSIGNED_TX]}) def to_base64(self): return base64.b64encode(self.serialize()).decode("utf8") diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index 9620951a16..66a23b443c 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -120,8 +120,6 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): self.disable_autoconnect = True self.set_test_params() assert self.wallet_names is None or len(self.wallet_names) <= self.num_nodes - if self.options.timeout_factor == 0 : - self.options.timeout_factor = 99999 self.rpc_timeout = int(self.rpc_timeout * self.options.timeout_factor) # optionally, increase timeout by a factor def main(self): @@ -193,7 +191,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): help="run nodes under the valgrind memory error detector: expect at least a ~10x slowdown. valgrind 3.14 or later required. Forces --nosandbox.") parser.add_argument("--randomseed", type=int, help="set a random seed for deterministically reproducing a previous test run") - parser.add_argument('--timeout-factor', dest="timeout_factor", type=float, default=1.0, help='adjust test timeouts by a factor. Setting it to 0 disables all timeouts') + parser.add_argument("--timeout-factor", dest="timeout_factor", type=float, help="adjust test timeouts by a factor. Setting it to 0 disables all timeouts") self.add_options(parser) # Running TestShell in a Jupyter notebook causes an additional -f argument @@ -201,6 +199,9 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): # source: https://stackoverflow.com/questions/48796169/how-to-fix-ipykernel-launcher-py-error-unrecognized-arguments-in-jupyter/56349168#56349168 parser.add_argument("-f", "--fff", help="a dummy argument to fool ipython", default="1") self.options = parser.parse_args() + if self.options.timeout_factor == 0: + self.options.timeout_factor = 99999 + self.options.timeout_factor = self.options.timeout_factor or (4 if self.options.valgrind else 1) self.options.previous_releases_path = previous_releases_path config = configparser.ConfigParser() diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 1d560706c2..f8869ae6bc 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -323,6 +323,7 @@ BASE_SCRIPTS = [ 'mempool_compatibility.py', 'mempool_accept_wtxid.py', 'mempool_dust.py', + 'mempool_sigoplimit.py', 'rpc_deriveaddresses.py', 'rpc_deriveaddresses.py --usecli', 'p2p_ping.py', diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py index 52022a2eee..53ac01686a 100755 --- a/test/functional/wallet_basic.py +++ b/test/functional/wallet_basic.py @@ -632,7 +632,7 @@ class WalletTest(BitcoinTestFramework): assert_equal(total_txs, len(self.nodes[0].listtransactions("*", 99999))) # Test getaddressinfo on external address. Note that these addresses are taken from disablewallet.py - assert_raises_rpc_error(-5, "Invalid prefix for Base58-encoded address", self.nodes[0].getaddressinfo, "3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy") + assert_raises_rpc_error(-5, "Invalid or unsupported Base58-encoded address.", self.nodes[0].getaddressinfo, "3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy") address_info = self.nodes[0].getaddressinfo("mneYUmWYsuk7kySiURxCi3AGxrAqZxLgPZ") assert_equal(address_info['address'], "mneYUmWYsuk7kySiURxCi3AGxrAqZxLgPZ") assert_equal(address_info["scriptPubKey"], "76a9144e3854046c7bd1594ac904e4793b6a45b36dea0988ac") diff --git a/test/functional/wallet_listdescriptors.py b/test/functional/wallet_listdescriptors.py index fb2156bda1..c5479089c6 100755 --- a/test/functional/wallet_listdescriptors.py +++ b/test/functional/wallet_listdescriptors.py @@ -54,7 +54,7 @@ class ListDescriptorsTest(BitcoinTestFramework): assert_equal(4, len([d for d in result['descriptors'] if d['internal']])) for item in result['descriptors']: assert item['desc'] != '' - assert item['next'] == 0 + assert item['next_index'] == 0 assert item['range'] == [0, 0] assert item['timestamp'] is not None @@ -78,7 +78,8 @@ class ListDescriptorsTest(BitcoinTestFramework): 'timestamp': TIME_GENESIS_BLOCK, 'active': False, 'range': [0, 0], - 'next': 0}, + 'next': 0, + 'next_index': 0}, ], } assert_equal(expected, wallet.listdescriptors()) @@ -92,7 +93,8 @@ class ListDescriptorsTest(BitcoinTestFramework): 'timestamp': TIME_GENESIS_BLOCK, 'active': False, 'range': [0, 0], - 'next': 0}, + 'next': 0, + 'next_index': 0}, ], } assert_equal(expected_private, wallet.listdescriptors(True)) diff --git a/test/lint/run-lint-format-strings.py b/test/lint/run-lint-format-strings.py index 57eefb00f2..91915f05f9 100755 --- a/test/lint/run-lint-format-strings.py +++ b/test/lint/run-lint-format-strings.py @@ -17,6 +17,7 @@ FALSE_POSITIVES = [ ("src/index/base.cpp", "FatalError(const char* fmt, const Args&... args)"), ("src/netbase.cpp", "LogConnectFailure(bool manual_connection, const char* fmt, const Args&... args)"), ("src/clientversion.cpp", "strprintf(_(COPYRIGHT_HOLDERS).translated, COPYRIGHT_HOLDERS_SUBSTITUTION)"), + ("src/test/translation_tests.cpp", "strprintf(format, arg)"), ("src/validationinterface.cpp", "LogPrint(BCLog::VALIDATION, fmt \"\\n\", __VA_ARGS__)"), ("src/wallet/wallet.h", "WalletLogPrintf(std::string fmt, Params... parameters)"), ("src/wallet/wallet.h", "LogPrintf((\"%s \" + fmt).c_str(), GetDisplayName(), parameters...)"), |