From 6fa30ee1368859e750c78c96c6a8756e3d009862 Mon Sep 17 00:00:00 2001 From: josibake Date: Sat, 29 Jun 2024 15:03:39 +0200 Subject: [PATCH] Squashed 'src/secp256k1/' changes from 4af241b320..ac59105026 ac59105026 ci: enable silentpayments module fc6c7f0abb tests: add BIP-352 test vectors 0a3e59b5d2 silentpayments: add benchmark for `scan_outputs` 6812e3f292 silentpayments: add examples/silentpayments.c c1eef5a0db silentpayments: add recipient light client support 2e8791ae2f silentpayments: add recipient scanning routine d7dff22516 silentpayments: add opaque data type `public_data` e29a4f3b74 silentpayments: add recipient label support b90c219944 silentpayments: add sender routine 81e6b8dcf6 silentpayments: implement output pubkey creation 7f7fb99f33 silentpayments: implement shared secret creation b0866a2912 silentpayments: add sortable recipient struct 01d6e461a5 doc: add module description for silentpayments b52dcb0bc5 build: add skeleton for new silentpayments (BIP352) module REVERT: 4af241b320 Merge bitcoin-core/secp256k1#1535: build: Replace hardcoded "auto" value with default one REVERT: f473c959f0 Merge bitcoin-core/secp256k1#1543: cmake: Do not modify build types when integrating by downstream project REVERT: d403eea484 Merge bitcoin-core/secp256k1#1546: cmake: Rename `SECP256K1_LATE_CFLAGS` and switch to Bitcoin Core's approach REVERT: d7ae25ce6f Merge bitcoin-core/secp256k1#1550: fix: typos in secp256k1.c REVERT: 0e2fadb20c fix: typos in secp256k1.c REVERT: 69b2192ad4 Merge bitcoin-core/secp256k1#1545: cmake: Do not set `CTEST_TEST_TARGET_ALIAS` REVERT: 5dd637f3cf Merge bitcoin-core/secp256k1#1548: README: mention ellswift module REVERT: 7454a53736 README: mention ellswift module REVERT: 4706be2cd0 cmake: Reimplement `SECP256K1_APPEND_CFLAGS` using Bitcoin Core approach REVERT: c2764dbb99 cmake: Rename `SECP256K1_LATE_CFLAGS` to `SECP256K1_APPEND_CFLAGS` REVERT: f87a3589f4 cmake: Do not set `CTEST_TEST_TARGET_ALIAS` REVERT: 158f9e5eae cmake: Do not modify build types when integrating by downstream project REVERT: 35c0fdc86b Merge bitcoin-core/secp256k1#1529: cmake: Fix cache issue when integrating by downstream project REVERT: 4392f0f717 Merge bitcoin-core/secp256k1#1533: tests: refactor: tidy up util functions (#1491) REVERT: bedffd53d8 Merge bitcoin-core/secp256k1#1488: ci: Add native macOS arm64 job REVERT: 4b8d5eeacf Merge bitcoin-core/secp256k1#1532: cmake: Disable eager MSan in ctime_tests REVERT: f55703ba49 autotools: Delete unneeded compiler test REVERT: 396e885886 autotools: Align MSan checking code with CMake's implementation REVERT: abde59f52d cmake: Report more compiler details in summary REVERT: 7abf979a43 cmake: Disable `ctime_tests` if build with `-fsanitize=memory` REVERT: 4d9645bee0 cmake: Remove "AUTO" value of `SECP256K1_ECMULT_GEN_KB` option REVERT: a06805ee74 cmake: Remove "AUTO" value of `SECP256K1_ECMULT_WINDOW_SIZE` option REVERT: 26b94ee92a autotools: Remove "auto" value of `--with-ecmult-gen-kb` option REVERT: 122dbaeb37 autotools: Remove "auto" value of `--with-ecmult-window` option REVERT: e73f6f8fd9 tests: refactor: drop `secp256k1_` prefix from testrand.h functions REVERT: 0ee7453a99 tests: refactor: add `testutil_` prefix to testutil.h functions REVERT: 0c6bc76dcd tests: refactor: move `random_` helpers from tests.c to testutil.h REVERT: 0fef8479be tests: refactor: rename `random_field_element_magnitude` -> `random_fe_magnitude` REVERT: 59db007f0f tests: refactor: rename `random_group_element_...` -> `random_ge_...` REVERT: ec4c002faa cmake: Simplify `PROJECT_IS_TOP_LEVEL` emulation REVERT: cae9a7ad14 cmake: Do not set emulated PROJECT_IS_TOP_LEVEL as cache variable REVERT: 218f0cc93b ci: Add native macOS arm64 job git-subtree-dir: src/secp256k1 git-subtree-split: ac591050262d9d00b629943d598f62b47e1ca7ae --- .cirrus.yml | 7 +- .github/workflows/ci.yml | 97 +- .gitignore | 1 + CMakeLists.txt | 105 +- Makefile.am | 15 + README.md | 1 - build-aux/m4/bitcoin_secp.m4 | 8 +- ci/ci.sh | 3 +- cmake/AllTargetsCompileOptions.cmake | 12 + cmake/CheckMemorySanitizer.cmake | 18 - configure.ac | 42 +- examples/silentpayments.c | 417 ++ include/secp256k1_silentpayments.h | 331 ++ src/bench.c | 24 +- src/modules/ecdh/tests_impl.h | 6 +- src/modules/ellswift/tests_impl.h | 38 +- src/modules/extrakeys/tests_impl.h | 36 +- src/modules/recovery/tests_impl.h | 8 +- .../schnorrsig/tests_exhaustive_impl.h | 6 +- src/modules/schnorrsig/tests_impl.h | 50 +- .../silentpayments/Makefile.am.include | 5 + src/modules/silentpayments/bench_impl.h | 103 + src/modules/silentpayments/main_impl.h | 588 +++ src/modules/silentpayments/tests_impl.h | 276 ++ src/modules/silentpayments/vectors.h | 3900 +++++++++++++++++ src/secp256k1.c | 8 +- src/testrand.h | 22 +- src/testrand_impl.h | 42 +- src/tests.c | 466 +- src/tests_exhaustive.c | 10 +- src/testutil.h | 121 +- tools/bech32m.py | 135 + tools/ripemd160.py | 130 + tools/tests_silentpayments_generate.py | 303 ++ 34 files changed, 6771 insertions(+), 563 deletions(-) create mode 100644 cmake/AllTargetsCompileOptions.cmake delete mode 100644 cmake/CheckMemorySanitizer.cmake create mode 100644 examples/silentpayments.c create mode 100644 include/secp256k1_silentpayments.h create mode 100644 src/modules/silentpayments/Makefile.am.include create mode 100644 src/modules/silentpayments/bench_impl.h create mode 100644 src/modules/silentpayments/main_impl.h create mode 100644 src/modules/silentpayments/tests_impl.h create mode 100644 src/modules/silentpayments/vectors.h create mode 100644 tools/bech32m.py create mode 100644 tools/ripemd160.py create mode 100755 tools/tests_silentpayments_generate.py diff --git a/.cirrus.yml b/.cirrus.yml index 4bd15511a4d186..19360b68f1aa33 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -10,8 +10,8 @@ env: MAKEFLAGS: -j4 BUILD: check ### secp256k1 config - ECMULTWINDOW: 15 - ECMULTGENKB: 22 + ECMULTWINDOW: auto + ECMULTGENKB: auto ASM: no WIDEMUL: auto WITH_VALGRIND: yes @@ -22,6 +22,7 @@ env: RECOVERY: no SCHNORRSIG: no ELLSWIFT: no + SILENTPAYMENTS: no ### test options SECP256K1_TEST_ITERS: BENCH: yes @@ -68,6 +69,7 @@ task: RECOVERY: yes SCHNORRSIG: yes ELLSWIFT: yes + SILENTPAYMENTS: yes matrix: # Currently only gcc-snapshot, the other compilers are tested on GHA with QEMU - env: { CC: 'gcc-snapshot' } @@ -84,6 +86,7 @@ task: RECOVERY: yes SCHNORRSIG: yes ELLSWIFT: yes + SILENTPAYMENTS: yes WRAPPER_CMD: 'valgrind --error-exitcode=42' SECP256K1_TEST_ITERS: 2 matrix: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ade94e1eec7921..dcea3288242a11 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,8 +21,8 @@ env: MAKEFLAGS: '-j4' BUILD: 'check' ### secp256k1 config - ECMULTWINDOW: 15 - ECMULTGENKB: 22 + ECMULTWINDOW: 'auto' + ECMULTGENKB: 'auto' ASM: 'no' WIDEMUL: 'auto' WITH_VALGRIND: 'yes' @@ -33,6 +33,7 @@ env: RECOVERY: 'no' SCHNORRSIG: 'no' ELLSWIFT: 'no' + SILENTPAYMENTS: 'no' ### test options SECP256K1_TEST_ITERS: BENCH: 'yes' @@ -71,18 +72,18 @@ jobs: matrix: configuration: - env_vars: { WIDEMUL: 'int64', RECOVERY: 'yes' } - - env_vars: { WIDEMUL: 'int64', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes' } + - env_vars: { WIDEMUL: 'int64', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', SILENTPAYMENTS: 'yes' } - env_vars: { WIDEMUL: 'int128' } - env_vars: { WIDEMUL: 'int128_struct', ELLSWIFT: 'yes' } - env_vars: { WIDEMUL: 'int128', RECOVERY: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes' } - - env_vars: { WIDEMUL: 'int128', ECDH: 'yes', SCHNORRSIG: 'yes' } + - env_vars: { WIDEMUL: 'int128', ECDH: 'yes', SCHNORRSIG: 'yes', SILENTPAYMENTS: 'yes' } - env_vars: { WIDEMUL: 'int128', ASM: 'x86_64', ELLSWIFT: 'yes' } - env_vars: { RECOVERY: 'yes', SCHNORRSIG: 'yes' } - - env_vars: { CTIMETESTS: 'no', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', CPPFLAGS: '-DVERIFY' } + - env_vars: { CTIMETESTS: 'no', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', SILENTPAYMENTS: 'yes', CPPFLAGS: '-DVERIFY' } - env_vars: { BUILD: 'distcheck', WITH_VALGRIND: 'no', CTIMETESTS: 'no', BENCH: 'no' } - env_vars: { CPPFLAGS: '-DDETERMINISTIC' } - env_vars: { CFLAGS: '-O0', CTIMETESTS: 'no' } - - env_vars: { CFLAGS: '-O1', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes' } + - env_vars: { CFLAGS: '-O1', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', SILENTPAYMENTS: 'yes' } - env_vars: { ECMULTGENKB: 2, ECMULTWINDOW: 2 } - env_vars: { ECMULTGENKB: 86, ECMULTWINDOW: 4 } cc: @@ -141,6 +142,7 @@ jobs: RECOVERY: 'yes' SCHNORRSIG: 'yes' ELLSWIFT: 'yes' + SILENTPAYMENTS: 'yes' CC: ${{ matrix.cc }} steps: @@ -185,6 +187,7 @@ jobs: RECOVERY: 'yes' SCHNORRSIG: 'yes' ELLSWIFT: 'yes' + SILENTPAYMENTS: 'yes' CTIMETESTS: 'no' steps: @@ -236,6 +239,7 @@ jobs: RECOVERY: 'yes' SCHNORRSIG: 'yes' ELLSWIFT: 'yes' + SILENTPAYMENTS: 'yes' CTIMETESTS: 'no' steps: @@ -281,6 +285,7 @@ jobs: RECOVERY: 'yes' SCHNORRSIG: 'yes' ELLSWIFT: 'yes' + SILENTPAYMENTS: 'yes' CTIMETESTS: 'no' strategy: @@ -336,6 +341,7 @@ jobs: RECOVERY: 'yes' SCHNORRSIG: 'yes' ELLSWIFT: 'yes' + SILENTPAYMENTS: 'yes' CTIMETESTS: 'no' steps: @@ -388,6 +394,7 @@ jobs: RECOVERY: 'yes' SCHNORRSIG: 'yes' ELLSWIFT: 'yes' + SILENTPAYMENTS: 'yes' CTIMETESTS: 'no' SECP256K1_TEST_ITERS: 2 @@ -439,6 +446,7 @@ jobs: RECOVERY: 'yes' SCHNORRSIG: 'yes' ELLSWIFT: 'yes' + SILENTPAYMENTS: 'yes' CTIMETESTS: 'no' CFLAGS: '-fsanitize=undefined,address -g' UBSAN_OPTIONS: 'print_stacktrace=1:halt_on_error=1' @@ -503,6 +511,7 @@ jobs: RECOVERY: 'yes' SCHNORRSIG: 'yes' ELLSWIFT: 'yes' + SILENTPAYMENTS: 'yes' CC: 'clang' SECP256K1_TEST_ITERS: 32 ASM: 'no' @@ -549,6 +558,7 @@ jobs: RECOVERY: 'yes' SCHNORRSIG: 'yes' ELLSWIFT: 'yes' + SILENTPAYMENTS: 'yes' CTIMETESTS: 'no' strategy: @@ -591,10 +601,10 @@ jobs: run: env if: ${{ always() }} - x86_64-macos-native: - name: "x86_64: macOS Monterey, Valgrind" + macos-native: + name: "x86_64: macOS Monterey" # See: https://github.com/actions/runner-images#available-images. - runs-on: macos-12 + runs-on: macos-12 # Use M1 once available https://github.com/github/roadmap/issues/528 env: CC: 'clang' @@ -605,14 +615,14 @@ jobs: fail-fast: false matrix: env_vars: - - { WIDEMUL: 'int64', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes' } + - { WIDEMUL: 'int64', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', SILENTPAYMENTS: 'yes' } - { WIDEMUL: 'int128_struct', ECMULTGENKB: 2, ECMULTWINDOW: 4 } - - { WIDEMUL: 'int128', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes' } + - { WIDEMUL: 'int128', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', SILENTPAYMENTS: 'yes' } - { WIDEMUL: 'int128', RECOVERY: 'yes' } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes' } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', CC: 'gcc' } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', WRAPPER_CMD: 'valgrind --error-exitcode=42', SECP256K1_TEST_ITERS: 2 } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', CC: 'gcc', WRAPPER_CMD: 'valgrind --error-exitcode=42', SECP256K1_TEST_ITERS: 2 } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', SILENTPAYMENTS: 'yes' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', SILENTPAYMENTS: 'yes', CC: 'gcc' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', SILENTPAYMENTS: 'yes', WRAPPER_CMD: 'valgrind --error-exitcode=42', SECP256K1_TEST_ITERS: 2 } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', SILENTPAYMENTS: 'yes', CC: 'gcc', WRAPPER_CMD: 'valgrind --error-exitcode=42', SECP256K1_TEST_ITERS: 2 } - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', CPPFLAGS: '-DVERIFY', CTIMETESTS: 'no' } - BUILD: 'distcheck' @@ -650,62 +660,6 @@ jobs: run: env if: ${{ always() }} - arm64-macos-native: - name: "ARM64: macOS Sonoma" - # See: https://github.com/actions/runner-images#available-images. - runs-on: macos-14 - - env: - CC: 'clang' - HOMEBREW_NO_AUTO_UPDATE: 1 - HOMEBREW_NO_INSTALL_CLEANUP: 1 - WITH_VALGRIND: 'no' - CTIMETESTS: 'no' - - strategy: - fail-fast: false - matrix: - env_vars: - - { WIDEMUL: 'int64', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes' } - - { WIDEMUL: 'int128_struct', ECMULTGENPRECISION: 2, ECMULTWINDOW: 4 } - - { WIDEMUL: 'int128', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes' } - - { WIDEMUL: 'int128', RECOVERY: 'yes' } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes' } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', CC: 'gcc' } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', CPPFLAGS: '-DVERIFY' } - - BUILD: 'distcheck' - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Install Homebrew packages - run: | - brew install automake libtool gcc - ln -s $(brew --prefix gcc)/bin/gcc-?? /usr/local/bin/gcc - - - name: CI script - env: ${{ matrix.env_vars }} - run: ./ci/ci.sh - - - run: cat tests.log || true - if: ${{ always() }} - - run: cat noverify_tests.log || true - if: ${{ always() }} - - run: cat exhaustive_tests.log || true - if: ${{ always() }} - - run: cat ctime_tests.log || true - if: ${{ always() }} - - run: cat bench.log || true - if: ${{ always() }} - - run: cat config.log || true - if: ${{ always() }} - - run: cat test_env.log || true - if: ${{ always() }} - - name: CI env - run: env - if: ${{ always() }} - win64-native: name: ${{ matrix.configuration.job_name }} # See: https://github.com/actions/runner-images#available-images. @@ -780,6 +734,7 @@ jobs: RECOVERY: 'yes' SCHNORRSIG: 'yes' ELLSWIFT: 'yes' + SILENTPAYMENTS: 'yes' steps: - name: Checkout diff --git a/.gitignore b/.gitignore index 574902b8b5e456..9a5be287dbfec5 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ ctime_tests ecdh_example ecdsa_example schnorr_example +silentpayments_example *.exe *.so *.a diff --git a/CMakeLists.txt b/CMakeLists.txt index 7e3465a75b2ec4..f0b30f7b4c6a5c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,14 +18,15 @@ project(libsecp256k1 ) if(CMAKE_VERSION VERSION_LESS 3.21) - # Emulates CMake 3.21+ behavior. - if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) - set(PROJECT_IS_TOP_LEVEL ON) - set(${PROJECT_NAME}_IS_TOP_LEVEL ON) + get_directory_property(parent_directory PARENT_DIRECTORY) + if(parent_directory) + set(PROJECT_IS_TOP_LEVEL OFF CACHE INTERNAL "Emulates CMake 3.21+ behavior.") + set(${PROJECT_NAME}_IS_TOP_LEVEL OFF CACHE INTERNAL "Emulates CMake 3.21+ behavior.") else() - set(PROJECT_IS_TOP_LEVEL OFF) - set(${PROJECT_NAME}_IS_TOP_LEVEL OFF) + set(PROJECT_IS_TOP_LEVEL ON CACHE INTERNAL "Emulates CMake 3.21+ behavior.") + set(${PROJECT_NAME}_IS_TOP_LEVEL ON CACHE INTERNAL "Emulates CMake 3.21+ behavior.") endif() + unset(parent_directory) endif() # The library version is based on libtool versioning of the ABI. The set of @@ -59,9 +60,14 @@ option(SECP256K1_ENABLE_MODULE_RECOVERY "Enable ECDSA pubkey recovery module." O option(SECP256K1_ENABLE_MODULE_EXTRAKEYS "Enable extrakeys module." ON) option(SECP256K1_ENABLE_MODULE_SCHNORRSIG "Enable schnorrsig module." ON) option(SECP256K1_ENABLE_MODULE_ELLSWIFT "Enable ElligatorSwift module." ON) +option(SECP256K1_ENABLE_MODULE_SILENTPAYMENTS "Enable Silent Payments module." OFF) # Processing must be done in a topological sorting of the dependency graph # (dependent module first). +if(SECP256K1_ENABLE_MODULE_SILENTPAYMENTS) + add_compile_definitions(ENABLE_MODULE_SILENTPAYMENTS=1) +endif() + if(SECP256K1_ENABLE_MODULE_ELLSWIFT) add_compile_definitions(ENABLE_MODULE_ELLSWIFT=1) endif() @@ -91,15 +97,21 @@ if(SECP256K1_USE_EXTERNAL_DEFAULT_CALLBACKS) add_compile_definitions(USE_EXTERNAL_DEFAULT_CALLBACKS=1) endif() -set(SECP256K1_ECMULT_WINDOW_SIZE 15 CACHE STRING "Window size for ecmult precomputation for verification, specified as integer in range [2..24]. The default value is a reasonable setting for desktop machines (currently 15). [default=15]") -set_property(CACHE SECP256K1_ECMULT_WINDOW_SIZE PROPERTY STRINGS 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24) +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_compile_definitions(ECMULT_WINDOW_SIZE=${SECP256K1_ECMULT_WINDOW_SIZE}) -set(SECP256K1_ECMULT_GEN_KB 22 CACHE STRING "The size of the precomputed table for signing in multiples of 1024 bytes (on typical platforms). Larger values result in possibly better signing or key generation performance at the cost of a larger table. Valid choices are 2, 22, 86. The default value is a reasonable setting for desktop machines (currently 22). [default=22]") -set_property(CACHE SECP256K1_ECMULT_GEN_KB PROPERTY STRINGS 2 22 86) +set(SECP256K1_ECMULT_GEN_KB "AUTO" CACHE STRING "The size of the precomputed table for signing in multiples of 1024 bytes (on typical platforms). Larger values result in possibly better signing or key generation performance at the cost of a larger table. Valid choices are 2, 22, 86. \"AUTO\" is a reasonable setting for desktop machines (currently 22). [default=AUTO]") +set_property(CACHE SECP256K1_ECMULT_GEN_KB PROPERTY STRINGS "AUTO" 2 22 86) check_string_option_value(SECP256K1_ECMULT_GEN_KB) +if(SECP256K1_ECMULT_GEN_KB STREQUAL "AUTO") + set(SECP256K1_ECMULT_GEN_KB 22) +endif() if(SECP256K1_ECMULT_GEN_KB EQUAL 2) add_compile_definitions(COMB_BLOCKS=2) add_compile_definitions(COMB_TEETH=5) @@ -207,25 +219,23 @@ mark_as_advanced( CMAKE_SHARED_LINKER_FLAGS_COVERAGE ) -if(PROJECT_IS_TOP_LEVEL) - get_property(is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) - set(default_build_type "RelWithDebInfo") - if(is_multi_config) - set(CMAKE_CONFIGURATION_TYPES "${default_build_type}" "Release" "Debug" "MinSizeRel" "Coverage" CACHE STRING - "Supported configuration types." +get_property(is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +set(default_build_type "RelWithDebInfo") +if(is_multi_config) + set(CMAKE_CONFIGURATION_TYPES "${default_build_type}" "Release" "Debug" "MinSizeRel" "Coverage" CACHE STRING + "Supported configuration types." + FORCE + ) +else() + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY + STRINGS "${default_build_type}" "Release" "Debug" "MinSizeRel" "Coverage" + ) + if(NOT CMAKE_BUILD_TYPE) + 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 ) - else() - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY - STRINGS "${default_build_type}" "Release" "Debug" "MinSizeRel" "Coverage" - ) - if(NOT CMAKE_BUILD_TYPE) - 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() endif() endif() @@ -258,17 +268,10 @@ endif() set(CMAKE_C_VISIBILITY_PRESET hidden) -set(print_msan_notice) -if(SECP256K1_BUILD_CTIME_TESTS) - include(CheckMemorySanitizer) - check_memory_sanitizer(msan_enabled) - if(msan_enabled) - try_append_c_flags(-fno-sanitize-memory-param-retval) - set(print_msan_notice YES) - endif() - unset(msan_enabled) -endif() - +# 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) @@ -276,16 +279,14 @@ if(SECP256K1_BUILD_BENCHMARK OR SECP256K1_BUILD_TESTS OR SECP256K1_BUILD_EXHAUST enable_testing() endif() -set(SECP256K1_APPEND_CFLAGS "" CACHE STRING "Compiler flags that are appended to the command line after all other flags added by the build system. This variable is intended for debugging and special builds.") -if(SECP256K1_APPEND_CFLAGS) - # Appending to this low-level rule variable is the only way to - # guarantee that the flags appear at the end of the command line. - string(APPEND CMAKE_C_COMPILE_OBJECT " ${SECP256K1_APPEND_CFLAGS}") -endif() +set(SECP256K1_LATE_CFLAGS "" CACHE STRING "Compiler flags that are added to the command line after all other flags added by the build system.") +include(AllTargetsCompileOptions) add_subdirectory(src) +all_targets_compile_options(src "${SECP256K1_LATE_CFLAGS}") if(SECP256K1_BUILD_EXAMPLES) add_subdirectory(examples) + all_targets_compile_options(examples "${SECP256K1_LATE_CFLAGS}") endif() message("\n") @@ -305,6 +306,7 @@ message(" ECDSA pubkey recovery ............... ${SECP256K1_ENABLE_MODULE_RECOV message(" extrakeys ........................... ${SECP256K1_ENABLE_MODULE_EXTRAKEYS}") message(" schnorrsig .......................... ${SECP256K1_ENABLE_MODULE_SCHNORRSIG}") message(" ElligatorSwift ...................... ${SECP256K1_ENABLE_MODULE_ELLSWIFT}") +message(" Silent Payments ..................... ${SECP256K1_ENABLE_MODULE_SILENTPAYMENTS}") message("Parameters:") message(" ecmult window size .................. ${SECP256K1_ECMULT_WINDOW_SIZE}") message(" ecmult gen table size ............... ${SECP256K1_ECMULT_GEN_KB} KiB") @@ -336,7 +338,7 @@ 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_ID} ${CMAKE_C_COMPILER_VERSION}, ${CMAKE_C_COMPILER}") +message("C compiler ............................ ${CMAKE_C_COMPILER}") message("CFLAGS ................................ ${CMAKE_C_FLAGS}") get_directory_property(compile_options COMPILE_OPTIONS) string(REPLACE ";" " " compile_options "${compile_options}") @@ -359,17 +361,10 @@ else() message(" - LDFLAGS for executables ............ ${CMAKE_EXE_LINKER_FLAGS_DEBUG}") message(" - LDFLAGS for shared libraries ....... ${CMAKE_SHARED_LINKER_FLAGS_DEBUG}") endif() -if(SECP256K1_APPEND_CFLAGS) - message("SECP256K1_APPEND_CFLAGS ............... ${SECP256K1_APPEND_CFLAGS}") -endif() -message("") -if(print_msan_notice) - message( - "Note:\n" - " MemorySanitizer detected, tried to add -fno-sanitize-memory-param-retval to compile options\n" - " to avoid false positives in ctime_tests. Pass -DSECP256K1_BUILD_CTIME_TESTS=OFF to avoid this.\n" - ) +if(SECP256K1_LATE_CFLAGS) + message("SECP256K1_LATE_CFLAGS ................. ${SECP256K1_LATE_CFLAGS}") endif() +message("\n") if(SECP256K1_EXPERIMENTAL) message( " ******\n" diff --git a/Makefile.am b/Makefile.am index 322e44eaab93d6..cd5535643c9961 100644 --- a/Makefile.am +++ b/Makefile.am @@ -184,6 +184,17 @@ schnorr_example_LDFLAGS += -lbcrypt endif TESTS += schnorr_example endif +if ENABLE_MODULE_SILENTPAYMENTS +noinst_PROGRAMS += silentpayments_example +silentpayments_example_SOURCES = examples/silentpayments.c +silentpayments_example_CPPFLAGS = -I$(top_srcdir)/include -DSECP256K1_STATIC +silentpayments_example_LDADD = libsecp256k1.la +silentpayments_example_LDFLAGS = -static +if BUILD_WINDOWS +silentpayments_example_LDFLAGS += -lbcrypt +endif +TESTS += silentpayments_example +endif endif ### Precomputed tables @@ -273,3 +284,7 @@ endif if ENABLE_MODULE_ELLSWIFT include src/modules/ellswift/Makefile.am.include endif + +if ENABLE_MODULE_SILENTPAYMENTS +include src/modules/silentpayments/Makefile.am.include +endif diff --git a/README.md b/README.md index e8d4123ab92df9..6e88eb4ecbcd49 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,6 @@ Features: * Optional module for public key recovery. * Optional module for ECDH key exchange. * Optional module for Schnorr signatures according to [BIP-340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki). -* Optional module for ElligatorSwift key exchange according to [BIP-324](https://github.com/bitcoin/bips/blob/master/bip-0324.mediawiki). Implementation details ---------------------- diff --git a/build-aux/m4/bitcoin_secp.m4 b/build-aux/m4/bitcoin_secp.m4 index 048267fa6eeccc..fee2d7b4db3863 100644 --- a/build-aux/m4/bitcoin_secp.m4 +++ b/build-aux/m4/bitcoin_secp.m4 @@ -50,14 +50,10 @@ AC_MSG_CHECKING(whether MemorySanitizer is enabled) AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ #if defined(__has_feature) # if __has_feature(memory_sanitizer) - /* MemorySanitizer is enabled. */ - # elif - # error "MemorySanitizer is disabled." + # error "MemorySanitizer is enabled." # endif - #else - # error "__has_feature is not defined." #endif - ]])], [msan_enabled=yes], [msan_enabled=no]) + ]])], [msan_enabled=no], [msan_enabled=yes]) AC_MSG_RESULT([$msan_enabled]) ]) diff --git a/ci/ci.sh b/ci/ci.sh index c7d2e9e4ea4ea1..0d0b1dbbb88586 100755 --- a/ci/ci.sh +++ b/ci/ci.sh @@ -13,7 +13,7 @@ print_environment() { # does not rely on bash. for var in WERROR_CFLAGS MAKEFLAGS BUILD \ ECMULTWINDOW ECMULTGENKB ASM WIDEMUL WITH_VALGRIND EXTRAFLAGS \ - EXPERIMENTAL ECDH RECOVERY SCHNORRSIG ELLSWIFT \ + EXPERIMENTAL ECDH RECOVERY SCHNORRSIG ELLSWIFT SILENTPAYMENTS \ SECP256K1_TEST_ITERS BENCH SECP256K1_BENCH_ITERS CTIMETESTS\ EXAMPLES \ HOST WRAPPER_CMD \ @@ -77,6 +77,7 @@ esac --with-ecmult-gen-kb="$ECMULTGENKB" \ --enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" \ --enable-module-ellswift="$ELLSWIFT" \ + --enable-module-silentpayments="$SILENTPAYMENTS" \ --enable-module-schnorrsig="$SCHNORRSIG" \ --enable-examples="$EXAMPLES" \ --enable-ctime-tests="$CTIMETESTS" \ diff --git a/cmake/AllTargetsCompileOptions.cmake b/cmake/AllTargetsCompileOptions.cmake new file mode 100644 index 00000000000000..6e420e0fdee264 --- /dev/null +++ b/cmake/AllTargetsCompileOptions.cmake @@ -0,0 +1,12 @@ +# Add compile options to all targets added in the subdirectory. +function(all_targets_compile_options dir options) + get_directory_property(targets DIRECTORY ${dir} BUILDSYSTEM_TARGETS) + separate_arguments(options) + set(compiled_target_types STATIC_LIBRARY SHARED_LIBRARY OBJECT_LIBRARY EXECUTABLE) + foreach(target ${targets}) + get_target_property(type ${target} TYPE) + if(type IN_LIST compiled_target_types) + target_compile_options(${target} PRIVATE ${options}) + endif() + endforeach() +endfunction() diff --git a/cmake/CheckMemorySanitizer.cmake b/cmake/CheckMemorySanitizer.cmake deleted file mode 100644 index d9ef681e658119..00000000000000 --- a/cmake/CheckMemorySanitizer.cmake +++ /dev/null @@ -1,18 +0,0 @@ -include_guard(GLOBAL) -include(CheckCSourceCompiles) - -function(check_memory_sanitizer output) - set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) - check_c_source_compiles(" - #if defined(__has_feature) - # if __has_feature(memory_sanitizer) - /* MemorySanitizer is enabled. */ - # elif - # error \"MemorySanitizer is disabled.\" - # endif - #else - # error \"__has_feature is not defined.\" - #endif - " HAVE_MSAN) - set(${output} ${HAVE_MSAN} PARENT_SCOPE) -endfunction() diff --git a/configure.ac b/configure.ac index adae1897872d70..b2e262078779a9 100644 --- a/configure.ac +++ b/configure.ac @@ -188,6 +188,10 @@ AC_ARG_ENABLE(module_ellswift, AS_HELP_STRING([--enable-module-ellswift],[enable ElligatorSwift module [default=yes]]), [], [SECP_SET_DEFAULT([enable_module_ellswift], [yes], [yes])]) +AC_ARG_ENABLE(module_silentpayments, + AS_HELP_STRING([--enable-module-silentpayments],[enable Silent Payments module [default=no]]), [], + [SECP_SET_DEFAULT([enable_module_silentpayments], [no], [yes])]) + AC_ARG_ENABLE(external_default_callbacks, AS_HELP_STRING([--enable-external-default-callbacks],[enable external default callback functions [default=no]]), [], [SECP_SET_DEFAULT([enable_external_default_callbacks], [no], [no])]) @@ -203,22 +207,22 @@ AC_ARG_WITH([test-override-wide-multiply], [] ,[set_widemul=$withval], [set_wide AC_ARG_WITH([asm], [AS_HELP_STRING([--with-asm=x86_64|arm32|no|auto], [assembly to use (experimental: arm32) [default=auto]])],[req_asm=$withval], [req_asm=auto]) -AC_ARG_WITH([ecmult-window], [AS_HELP_STRING([--with-ecmult-window=SIZE], +AC_ARG_WITH([ecmult-window], [AS_HELP_STRING([--with-ecmult-window=SIZE|auto], [window size for ecmult precomputation for verification, specified as integer in range [2..24].] [Larger values result in possibly better performance at the cost of an exponentially larger precomputed table.] [The table will store 2^(SIZE-1) * 64 bytes of data but can be larger in memory due to platform-specific padding and alignment.] [A window size larger than 15 will require you delete the prebuilt precomputed_ecmult.c file so that it can be rebuilt.] [For very large window sizes, use "make -j 1" to reduce memory use during compilation.] -[The default value is a reasonable setting for desktop machines (currently 15). [default=15]] +["auto" is a reasonable setting for desktop machines (currently 15). [default=auto]] )], -[set_ecmult_window=$withval], [set_ecmult_window=15]) +[req_ecmult_window=$withval], [req_ecmult_window=auto]) -AC_ARG_WITH([ecmult-gen-kb], [AS_HELP_STRING([--with-ecmult-gen-kb=2|22|86], +AC_ARG_WITH([ecmult-gen-kb], [AS_HELP_STRING([--with-ecmult-gen-kb=2|22|86|auto], [The size of the precomputed table for signing in multiples of 1024 bytes (on typical platforms).] [Larger values result in possibly better signing/keygeneration performance at the cost of a larger table.] -[The default value is a reasonable setting for desktop machines (currently 22). [default=22]] +["auto" is a reasonable setting for desktop machines (currently 22). [default=auto]] )], -[set_ecmult_gen_kb=$withval], [set_ecmult_gen_kb=22]) +[req_ecmult_gen_kb=$withval], [req_ecmult_gen_kb=auto]) AC_ARG_WITH([valgrind], [AS_HELP_STRING([--with-valgrind=yes|no|auto], [Build with extra checks for running inside Valgrind [default=auto]] @@ -248,7 +252,7 @@ if test x"$enable_ctime_tests" = x"auto"; then fi print_msan_notice=no -if test x"$enable_ctime_tests" = x"yes"; then +if test x"$enable_ctime_tests" = x"yes" && test x"$GCC" = x"yes"; then SECP_MSAN_CHECK # MSan on Clang >=16 reports unitialized memory in function parameters and return values, even if # the uninitalized variable is never actually "used". This is called "eager" checking, and it's @@ -349,7 +353,14 @@ auto) ;; esac -error_window_size=['window size for ecmult precomputation not an integer in range [2..24]'] +# Set ecmult window size +if test x"$req_ecmult_window" = x"auto"; then + set_ecmult_window=15 +else + set_ecmult_window=$req_ecmult_window +fi + +error_window_size=['window size for ecmult precomputation not an integer in range [2..24] or "auto"'] case $set_ecmult_window in ''|*[[!0-9]]*) # no valid integer @@ -364,6 +375,13 @@ case $set_ecmult_window in ;; esac +# Set ecmult gen kb +if test x"$req_ecmult_gen_kb" = x"auto"; then + set_ecmult_gen_kb=22 +else + set_ecmult_gen_kb=$req_ecmult_gen_kb +fi + case $set_ecmult_gen_kb in 2) SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DCOMB_BLOCKS=2 -DCOMB_TEETH=5" @@ -375,7 +393,7 @@ case $set_ecmult_gen_kb in SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DCOMB_BLOCKS=43 -DCOMB_TEETH=6" ;; *) - AC_MSG_ERROR(['ecmult gen table size not 2, 22 or 86']) + AC_MSG_ERROR(['ecmult gen table size not 2, 22, 86 or "auto"']) ;; esac @@ -394,6 +412,10 @@ SECP_CFLAGS="$SECP_CFLAGS $WERROR_CFLAGS" # Processing must be done in a reverse topological sorting of the dependency graph # (dependent module first). +if test x"$enable_module_silentpayments" = x"yes"; then + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_SILENTPAYMENTS=1" +fi + if test x"$enable_module_ellswift" = x"yes"; then SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_ELLSWIFT=1" fi @@ -450,6 +472,7 @@ AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"ye AM_CONDITIONAL([ENABLE_MODULE_EXTRAKEYS], [test x"$enable_module_extrakeys" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_SCHNORRSIG], [test x"$enable_module_schnorrsig" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_ELLSWIFT], [test x"$enable_module_ellswift" = x"yes"]) +AM_CONDITIONAL([ENABLE_MODULE_SILENTPAYMENTS], [test x"$enable_module_silentpayments" = x"yes"]) AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$enable_external_asm" = x"yes"]) AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm32"]) AM_CONDITIONAL([BUILD_WINDOWS], [test "$build_windows" = "yes"]) @@ -472,6 +495,7 @@ echo " module recovery = $enable_module_recovery" echo " module extrakeys = $enable_module_extrakeys" echo " module schnorrsig = $enable_module_schnorrsig" echo " module ellswift = $enable_module_ellswift" +echo " module silentpayments = $enable_module_silentpayments" echo echo " asm = $set_asm" echo " ecmult window size = $set_ecmult_window" diff --git a/examples/silentpayments.c b/examples/silentpayments.c new file mode 100644 index 00000000000000..f866faff87718d --- /dev/null +++ b/examples/silentpayments.c @@ -0,0 +1,417 @@ +/************************************************************************* + * Written in 2024 by josibake * + * To the extent possible under law, the author(s) have dedicated all * + * copyright and related and neighboring rights to the software in this * + * file to the public domain worldwide. This software is distributed * + * without any warranty. For the CC0 Public Domain Dedication, see * + * EXAMPLES_COPYING or https://creativecommons.org/publicdomain/zero/1.0 * + *************************************************************************/ + +#include +#include +#include + +#include +#include +#include + +#include "examples_util.h" + +/* Static data for Bob and Carol's silent payment addresses. + * This consists of a scan key for each and the addresse data for each + */ +static unsigned char smallest_outpoint[36] = { + 0x16,0x9e,0x1e,0x83,0xe9,0x30,0x85,0x33,0x91, + 0xbc,0x6f,0x35,0xf6,0x05,0xc6,0x75,0x4c,0xfe, + 0xad,0x57,0xcf,0x83,0x87,0x63,0x9d,0x3b,0x40, + 0x96,0xc5,0x4f,0x18,0xf4,0x00,0x00,0x00,0x00 +}; +static unsigned char bob_scan_key[32] = { + 0xa8,0x90,0x54,0xc9,0x5b,0xe3,0xc3,0x01, + 0x56,0x65,0x74,0xf2,0xaa,0x93,0xad,0xe0, + 0x51,0x85,0x09,0x03,0xa6,0x9c,0xbd,0xd1, + 0xd4,0x7e,0xae,0x26,0x3d,0x7b,0xc0,0x31 +}; +static unsigned char bob_spend_pubkey[33] = { + 0x02,0xee,0x97,0xdf,0x83,0xb2,0x54,0x6a, + 0xf5,0xa7,0xd0,0x62,0x15,0xd9,0x8b,0xcb, + 0x63,0x7f,0xe0,0x5d,0xd0,0xfa,0x37,0x3b, + 0xd8,0x20,0xe6,0x64,0xd3,0x72,0xde,0x9a,0x01 +}; +static unsigned char bob_address[2][33] = { + { + 0x02,0x15,0x40,0xae,0xa8,0x97,0x54,0x7a, + 0xd4,0x39,0xb4,0xe0,0xf6,0x09,0xe5,0xf0, + 0xfa,0x63,0xde,0x89,0xab,0x11,0xed,0xe3, + 0x1e,0x8c,0xde,0x4b,0xe2,0x19,0x42,0x5f,0x23 + }, + { + 0x02,0x3e,0xff,0xf8,0x18,0x51,0x65,0xea, + 0x63,0xa9,0x92,0xb3,0x9f,0x31,0xd8,0xfd, + 0x8e,0x0e,0x64,0xae,0xf9,0xd3,0x88,0x07, + 0x34,0x97,0x37,0x14,0xa5,0x3d,0x83,0x11,0x8d + } +}; +static unsigned char carol_scan_key[32] = { + 0x04,0xb2,0xa4,0x11,0x63,0x5c,0x09,0x77, + 0x59,0xaa,0xcd,0x0f,0x00,0x5a,0x4c,0x82, + 0xc8,0xc9,0x28,0x62,0xc6,0xfc,0x28,0x4b, + 0x80,0xb8,0xef,0xeb,0xc2,0x0c,0x3d,0x17 +}; +static unsigned char carol_address[2][33] = { + { + 0x03,0xbb,0xc6,0x3f,0x12,0x74,0x5d,0x3b, + 0x9e,0x9d,0x24,0xc6,0xcd,0x7a,0x1e,0xfe, + 0xba,0xd0,0xa7,0xf4,0x69,0x23,0x2f,0xbe, + 0xcf,0x31,0xfb,0xa7,0xb4,0xf7,0xdd,0xed,0xa8 + }, + { + 0x03,0x81,0xeb,0x9a,0x9a,0x9e,0xc7,0x39, + 0xd5,0x27,0xc1,0x63,0x1b,0x31,0xb4,0x21, + 0x56,0x6f,0x5c,0x2a,0x47,0xb4,0xab,0x5b, + 0x1f,0x6a,0x68,0x6d,0xfb,0x68,0xea,0xb7,0x16 + } +}; + +/* Labels + * The structs and call back function are for demonstration only and not optimized. + * In a production usecase, it is expected that the caller will be using a much more performant + * method for storing and querying labels. + */ + +struct label_cache_entry { + secp256k1_pubkey label; + unsigned char label_tweak[32]; +}; + +struct labels_cache { + const secp256k1_context *ctx; + size_t entries_used; + struct label_cache_entry entries[5]; +}; + +const unsigned char* label_lookup(const secp256k1_pubkey* key, const void* cache_ptr) { + const struct labels_cache* cache = (const struct labels_cache*)cache_ptr; + size_t i; + for (i = 0; i < cache->entries_used; i++) { + if (secp256k1_ec_pubkey_cmp(cache->ctx, &cache->entries[i].label, key) == 0) { + return cache->entries[i].label_tweak; + } + } + return NULL; +} + +int main(void) { + enum { N_TX_INPUTS = 2, N_TX_OUTPUTS = 3 }; + unsigned char randomize[32]; + unsigned char xonly_print[32]; + secp256k1_xonly_pubkey tx_inputs[N_TX_INPUTS]; + secp256k1_xonly_pubkey tx_outputs[N_TX_OUTPUTS]; + int ret; + size_t i; + /* Before we can call actual API functions, we need to create a "context". */ + secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + if (!fill_random(randomize, sizeof(randomize))) { + printf("Failed to generate randomness\n"); + return 1; + } + /* Randomizing the context is recommended to protect against side-channel + * leakage See `secp256k1_context_randomize` in secp256k1.h for more + * information about it. This should never fail. */ + ret = secp256k1_context_randomize(ctx, randomize); + assert(ret); + + /*** Sending ***/ + { + secp256k1_keypair sender_seckeys[N_TX_INPUTS]; + const secp256k1_keypair *sender_seckey_ptrs[N_TX_INPUTS]; + secp256k1_silentpayments_recipient recipients[N_TX_OUTPUTS]; + const secp256k1_silentpayments_recipient *recipient_ptrs[N_TX_OUTPUTS]; + secp256k1_xonly_pubkey *generated_output_ptrs[N_TX_OUTPUTS]; + char* address_amounts[N_TX_OUTPUTS] = {"1.0 BTC", "2.0 BTC", "3.0 BTC"}; + unsigned char (*sp_addresses[N_TX_OUTPUTS])[2][33]; + + /*** Generate private keys for the sender *** + * + * In this example, only taproot inputs are used but the function can be called with + * a mix of taproot seckeys and plain seckeys. Taproot seckeys are passed as keypairs + * to allow the sending function to check if the private keys need to be negated without needing + * to do an expensive pubkey generation. This is not needed for plain seckeys since there is no need + * for negation. + * + * The public key from each input keypair is saved in the `tx_inputs` array. This array will be used + * later in the example to represent the public keys the recipient will extracte from the + * transaction inputs. + */ + + for (i = 0; i < 2; i++) { + /* If the secret key is zero or out of range (bigger than secp256k1's + * order), we try to sample a new key. Note that the probability of this + * happening is negligible. */ + while (1) { + unsigned char seckey[32]; + if (!fill_random(seckey, sizeof(seckey))) { + printf("Failed to generate randomness\n"); + return 1; + } + /* Try to create a keypair with a valid context, it should only fail if + * the secret key is zero or out of range. */ + if (secp256k1_keypair_create(ctx, &sender_seckeys[i], seckey)) { + sender_seckey_ptrs[i] = &sender_seckeys[i]; + ret = secp256k1_keypair_xonly_pub(ctx, &tx_inputs[i], NULL, &sender_seckeys[i]); + assert(ret); + break; + } else { + printf("Failed to create keypair\n"); + return 1; + } + } + } + /*** Create the recipient objects ***/ + + /* Alice is sending to Bob and Carol in this transaction: + * + * 1. One output to Bob's labelled address + * 2. Two outputs for Carol (1.0 and 3.0 bitcoin) + * + * Alice creates the recipient objects and adds the index of the original ordering (i.e. the ordering + * of the `sp_addresses` array) to each object. This index is used to return the generated outputs in + * the original ordering so that Alice can match up the generated outputs with the correct amounts. + * + * Note: to create multiple outputs for Carol, Alice simply passes her silent payment + * address mutltiple times. + */ + sp_addresses[0] = &carol_address; /* : 1.0 BTC */ + sp_addresses[1] = &bob_address; /* : 2.0 BTC */ + sp_addresses[2] = &carol_address; /* : 3.0 BTC */ + for (i = 0; i < N_TX_OUTPUTS; i++) { + ret = secp256k1_ec_pubkey_parse(ctx, &recipients[i].scan_pubkey, (*(sp_addresses[i]))[0], 33); + assert(ret); + ret = secp256k1_ec_pubkey_parse(ctx, &recipients[i].spend_pubkey, (*(sp_addresses[i]))[1], 33); + assert(ret); + recipients[i].index = i; + recipient_ptrs[i] = &recipients[i]; + } + for (i = 0; i < N_TX_OUTPUTS; i++) { + generated_output_ptrs[i] = &tx_outputs[i]; + } + ret = secp256k1_silentpayments_sender_create_outputs(ctx, + generated_output_ptrs, + recipient_ptrs, N_TX_OUTPUTS, + smallest_outpoint, + sender_seckey_ptrs, N_TX_INPUTS, + NULL, 0 + ); + assert(ret); + printf("Alice created the following outputs for Bob and Carol: \n"); + for (i = 0; i < N_TX_OUTPUTS; i++) { + printf(" "); + printf("%s : ", address_amounts[i]); + secp256k1_xonly_pubkey_serialize(ctx, xonly_print, &tx_outputs[i]); + print_hex(xonly_print, sizeof(xonly_print)); + } + /* 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. + * + * Here we are preventing these writes from being optimized out, as any good compiler + * will remove any writes that aren't used. */ + for (i = 0; i < N_TX_INPUTS; i++) { + secure_erase(&sender_seckeys[i], sizeof(sender_seckeys[i])); + } + } + + /*** Receiving ***/ + { + /*** Transaction data *** + * + * Here we create a few global variables to represent the transaction data: + * + * 1. The transaction inputs, `tx_input_ptrs` + * 2. The transaction outputs, `tx_output_ptrs` + * + * These will be used to demonstrate scanning as a full node and scanning as a light client. + */ + const secp256k1_xonly_pubkey *tx_input_ptrs[N_TX_INPUTS]; + const secp256k1_xonly_pubkey *tx_output_ptrs[N_TX_OUTPUTS]; + unsigned char light_client_data33[33]; + + for (i = 0; i < N_TX_INPUTS; i++) { + tx_input_ptrs[i] = &tx_inputs[i]; + } + for (i = 0; i < N_TX_OUTPUTS; i++) { + tx_output_ptrs[i] = &tx_outputs[i]; + } + + /*** Scanning with labels as a full node (Bob) *** + * + * Since Bob has access to the full transaction, scanning is simple: + * + * 1. Collect the relevant data from the transaction inputs and call + * `secp256k1_silentpayments_recipient_public_data_create` + * 2. Call `secp256k1_silentpayments_recipient_scan_outputs` + * + * Additionally, since Bob has access to the full transaction outputs when scanning its easy for him + * to scan with labels, as demonstrated below. For efficient scanning, Bob keeps a cache of + * every label he has previously used and uses a callback to check if a potential label exists + * in his cache. Since the labels are created using an incremental integer `m`, if Bob ever + * forgets how many labels he has previously used, he can pregenerate a large number of + * labels (e.g. 0..100_000) and use that while scanning. + */ + { + secp256k1_silentpayments_found_output found_outputs[N_TX_OUTPUTS]; + secp256k1_silentpayments_found_output *found_output_ptrs[N_TX_OUTPUTS]; + secp256k1_silentpayments_public_data public_data; + secp256k1_pubkey spend_pubkey; + size_t n_found_outputs; + unsigned int m = 1; + struct labels_cache labels_cache; + + for (i = 0; i < N_TX_OUTPUTS; i++) { + found_output_ptrs[i] = &found_outputs[i]; + } + + /* In this contrived example, our label context needs the secp256k1 context because our lookup function + * is calling `secp256k1_ec_pubkey_cmp`. In practice, this context can be anything the lookup function needs. + */ + labels_cache.ctx = ctx; + + /* Load Bob's spend public key */ + ret = secp256k1_ec_pubkey_parse(ctx, &spend_pubkey, bob_spend_pubkey, 33); + + /* Add an entry to the cache. This implies Bob has previously called `secp256k1_silentpayments_recipient_create_labelled_spend_pubkey` + * and used the labelled spend pubkey to encode a labelled silent payments address. + */ + ret = secp256k1_silentpayments_recipient_create_label_tweak(ctx, + &labels_cache.entries[0].label, + labels_cache.entries[0].label_tweak, + bob_scan_key, + m + ); + assert(ret); + labels_cache.entries_used = 1; + + /* Bob collects the data from the transaction inputs and creates a `secp256k1_silentpayments_public_data` object. + * He uses this for his own scanning and also serializes the `public_data` object to send to light clients. We will + * use this later for Carol, who is scanning as a light client. Note, anyone can create and vend these `public_data` + * objecs, i.e. you don't need to be a silent payments wallet, just someone interested in vending this data to light + * clients, e.g. a wallet service provider. In our example, Bob is scanning for himself but also sharing this data + * with light clients. + */ + ret = secp256k1_silentpayments_recipient_public_data_create(ctx, + &public_data, + smallest_outpoint, + tx_input_ptrs, N_TX_INPUTS, + NULL, 0 /* null because no eligible plain pubkey inputs were found in the tx */ + ); + assert(ret); + /* Save the `public_data` output. This combines the `input_hash` scalar and public key sum by multiplying `input_hash * A_sum`. + * The output is then saved as a 33 byte compressed key. Storing it this way saves 32 bytes for the light client because + * now it can be send as a 33 byte compressed public key instead of 33 bytes for A_sum and 32 bytes for input_hash. + */ + ret = secp256k1_silentpayments_recipient_public_data_serialize(ctx, light_client_data33, &public_data); + assert(ret); + + /* Scan the transaction */ + n_found_outputs = 0; + ret = secp256k1_silentpayments_recipient_scan_outputs(ctx, + found_output_ptrs, &n_found_outputs, + tx_output_ptrs, N_TX_OUTPUTS, + bob_scan_key, + &public_data, + &spend_pubkey, + label_lookup, &labels_cache /* NULL, NULL if scanning without labels */ + ); + assert(n_found_outputs == 1); + printf("\n"); + printf("Bob found the following outputs: \n"); + for (i = 0; i < n_found_outputs; i++) { + printf(" "); + secp256k1_xonly_pubkey_serialize(ctx, xonly_print, &found_outputs[i].output); + print_hex(xonly_print, sizeof(xonly_print)); + } + } + + /*** Scanning as a light client (Carol) *** + * + * Being a light client, Carol likely does not have access to the transaction outputs. This + * means she will need to first generate an output, check if it exists in the UTXO set (e.g. + * BIP158 or some other means of querying) and only proceed to check the next output (by + * incrementing `k`) if the first output exists. + * + * For the transaction inputs, she needs the 33 byte compressed public key which is `input_hash * A_sum`. + */ + { + /* In practice, Carol wouldn't know the number of outputs ahead of time but we are cheating here + * to keep the example simple. + */ + unsigned char ser_found_outputs[2][32]; + unsigned char shared_secret[33]; + secp256k1_pubkey spend_pubkey; + secp256k1_silentpayments_public_data public_data; + size_t n_found_outputs; + + /* Load Carol's spend public key */ + ret = secp256k1_ec_pubkey_parse(ctx, &spend_pubkey, carol_address[1], 33); + assert(ret); + + /* Scan, one output at a time, using the light client data from earlier */ + ret = secp256k1_silentpayments_recipient_public_data_parse(ctx, &public_data, light_client_data33); + assert(ret); + ret = secp256k1_silentpayments_recipient_create_shared_secret(ctx, + shared_secret, + carol_scan_key, + &public_data + ); + assert(ret); + n_found_outputs = 0; + { + int found = 0; + size_t k = 0; + secp256k1_xonly_pubkey potential_output; + + while(1) { + + ret = secp256k1_silentpayments_recipient_create_output_pubkey(ctx, + &potential_output, + shared_secret, + &spend_pubkey, + k + ); + assert(ret); + /* At this point, we check that the utxo exists with a light client protocol. + * For this example, we'll just iterate through the list of transaction outputs + */ + found = 0; + for (i = 0; i < N_TX_OUTPUTS; i++) { + if (secp256k1_xonly_pubkey_cmp(ctx, &potential_output, &tx_outputs[i]) == 0) { + secp256k1_xonly_pubkey_serialize(ctx, ser_found_outputs[n_found_outputs], &potential_output); + /* If found, create a new output with k++ and check again */ + found = 1; + n_found_outputs++; + k++; + break; + } + } + /* If we generate an output and it does not exist in the UTXO set, + * we are done scanning this transaction */ + if (!found) { + break; + } + } + } + + printf("\n"); + printf("Carol found the following outputs: \n"); + for (i = 0; i < n_found_outputs; i++) { + printf(" "); + print_hex(ser_found_outputs[i], 32); + } + } + } + + /* This will clear everything from the context and free the memory */ + secp256k1_context_destroy(ctx); + return 0; +} diff --git a/include/secp256k1_silentpayments.h b/include/secp256k1_silentpayments.h new file mode 100644 index 00000000000000..e01fec75b9412e --- /dev/null +++ b/include/secp256k1_silentpayments.h @@ -0,0 +1,331 @@ +#ifndef SECP256K1_SILENTPAYMENTS_H +#define SECP256K1_SILENTPAYMENTS_H + +#include "secp256k1.h" +#include "secp256k1_extrakeys.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* This module provides an implementation for Silent Payments, as specified in BIP352. + * This particularly involves the creation of input tweak data by summing up private + * or public keys and the derivation of a shared secret using Elliptic Curve Diffie-Hellman. + * Combined are either: + * - spender's private keys and recipient's public key (a * B, sender side) + * - spender's public keys and recipient's private key (A * b, recipient side) + * With this result, the necessary key material for ultimately creating/scanning + * or spending Silent Payment outputs can be determined. + * + * Note that this module is _not_ a full implementation of BIP352, as it + * inherently doesn't deal with higher-level concepts like addresses, output + * script types or transactions. The intent is to provide a module for abstracting away + * the elliptic-curve operations required for the protocol. For any wallet software already + * using libsecp256k1, this API should provide all the functions needed for a Silent Payments + * implementation without requiring any further elliptic-curve operations from the wallet. + */ + +/* This struct serves as an In param for passing the silent payment address data. + * The index field is for when more than one address is being sent to in a transaction. Index is + * set based on the original ordering of the addresses and used to return the generated outputs + * matching the original ordering. When more than one recipient is used the recipient array + * will be sorted in place as part of generating the outputs, but the generated outputs will be + * returned in the original ordering specified by the index to ensure the caller is able + * to match up the generated outputs to the correct silent payment address (e.g. to be able to + * assign the correct amounts to the correct generated outputs in the final transaction). + */ +typedef struct { + secp256k1_pubkey scan_pubkey; + secp256k1_pubkey spend_pubkey; + size_t index; +} secp256k1_silentpayments_recipient; + +/** Create Silent Payment outputs for recipient(s). + * + * Given a list of n private keys a_1...a_n (one for each silent payment + * eligible input to spend), a serialized outpoint, and a list of recipients, + * create the taproot outputs: + * + * a_sum = a_1 + a_2 + ... + a_n + * input_hash = hash(outpoint_smallest || (a_sum * G)) + * taproot_output = B_spend + hash(a_sum * input_hash * B_scan || k) * G + * + * If necessary, the private keys are negated to enforce the right y-parity. + * For that reason, the private keys have to be passed in via two different parameter + * pairs, depending on whether the seckeys correspond to x-only outputs or not. + * + * Returns: 1 if creation of outputs was successful. 0 if an error occured. + * Args: ctx: pointer to a context object + * Out: generated_outputs: pointer to an array of pointers to xonly pubkeys, one per recipient. + * The order of outputs here matches the original ordering of the + * recipients array. + * In: recipients: pointer to an array of pointers to silent payment recipients, + * where each recipient is a scan public key, a spend public key, and + * an index indicating its position in the original ordering. + * The recipient array will be sorted in place, but generated outputs + * are saved in the `generated_outputs` array to match the ordering + * from the index field. This ensures the caller is able to match the + * generated outputs to the correct silent payment addresses. The same + * recipient can be passed multiple times to create multiple + * outputs for the same recipient. + * n_recipients: the number of recipients. This is equal to the total + * number of outputs to be generated as each recipient may passed + * multiple times to generate multiple outputs for the same recipient + * outpoint_smallest36: serialized smallest outpoint + * taproot_seckeys: pointer to an array of pointers to 32-byte private keys + * of taproot inputs (can be NULL if no private keys of + * taproot inputs are used) + * n_taproot_seckeys: the number of sender's taproot input private keys + * plain_seckeys: pointer to an array of pointers to 32-byte private keys + * of non-taproot inputs (can be NULL if no private keys of + * non-taproot inputs are used) + * n_plain_seckeys: the number of sender's non-taproot input private keys + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_sender_create_outputs( + const secp256k1_context *ctx, + secp256k1_xonly_pubkey **generated_outputs, + const secp256k1_silentpayments_recipient **recipients, + size_t n_recipients, + const unsigned char *outpoint_smallest36, + const secp256k1_keypair * const *taproot_seckeys, + size_t n_taproot_seckeys, + const unsigned char * const *plain_seckeys, + size_t n_plain_seckeys +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5); + +/** Create Silent Payment label tweak and label. + * + * Given a recipient's scan key b_scan and a label integer m, calculate + * the corresponding label tweak and label: + * + * label_tweak = hash(b_scan || m) + * label = label_tweak * G + * + * Returns: 1 if label tweak and label creation was successful. 0 if an error occured. + * Args: ctx: pointer to a context object + * Out: label: pointer to the resulting label public key + * label_tweak32: pointer to the 32 byte label tweak + * In: recipient_scan_key: pointer to the recipient's scan key + * m: label integer (0 is used for change outputs) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_recipient_create_label_tweak( + const secp256k1_context *ctx, + secp256k1_pubkey *label, + unsigned char *label_tweak32, + const unsigned char *recipient_scan_key, + unsigned int m +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Create Silent Payment labelled spend public key. + * + * Given a recipient's spend public key B_spend and a label, calculate + * the corresponding serialized labelled spend public key: + * + * B_m = B_spend + label + * + * The result is used by the recipient to create a Silent Payment address, consisting + * of the serialized and concatenated scan public key and (labelled) spend public key each. + * + * Returns: 1 if labelled spend public key creation was successful. 0 if an error occured. + * Args: ctx: pointer to a context object + * Out: labelled_spend_pubkey: pointer to the resulting labelled spend public key + * In: recipient_spend_pubkey: pointer to the recipient's spend pubkey + * label: pointer to the the recipient's label public key + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_recipient_create_labelled_spend_pubkey( + const secp256k1_context *ctx, + secp256k1_pubkey *labelled_spend_pubkey, + const secp256k1_pubkey *recipient_spend_pubkey, + const secp256k1_pubkey *label +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Opaque data structure that holds silent payments public input data. + * + * This structure does not contain secret data. Guaranteed to be 98 bytes in size. It can be safely + * copied/moved. Created with `secp256k1_silentpayments_public_data_create`. Can be serialized as + * a compressed public key using `secp256k1_silentpayments_public_data_serialize`. The serialization + * is intended for sending the public input data to light clients. Light clients can use this + * serialization with `secp256k1_silentpayments_public_data_parse`. + */ +typedef struct { + unsigned char data[98]; +} secp256k1_silentpayments_public_data; + +/** Compute Silent Payment public data from input public keys and transaction inputs. + * + * Given a list of n public keys A_1...A_n (one for each silent payment + * eligible input to spend) and a serialized outpoint_smallest, compute + * the corresponding input public tweak data: + * + * A_sum = A_1 + A_2 + ... + A_n + * input_hash = hash(outpoint_lowest || A_sum) + * + * The public keys have to be passed in via two different parameter pairs, + * one for regular and one for x-only public keys, in order to avoid the need + * of users converting to a common pubkey format before calling this function. + * The resulting data is can be used for scanning on the recipient side, or stored + * in an index for late use (e.g. wallet rescanning, vending data to light clients). + * + * If calling this function for scanning, the reciever must provide an output param + * for the `input_hash`. If calling this function for simply aggregating the inputs + * for later use, the caller can save the result with `silentpayments_public_data_serialize`. + * + * Returns: 1 if tweak data creation was successful. 0 if an error occured. + * Args: ctx: pointer to a context object + * Out: public_data: pointer to public_data object containing the summed public key and + * input_hash. + * In: outpoint_smallest36: serialized smallest outpoint + * xonly_pubkeys: pointer to an array of pointers to taproot x-only + * public keys (can be NULL if no taproot inputs are used) + * n_xonly_pubkeys: the number of taproot input public keys + * plain_pubkeys: pointer to an array of pointers to non-taproot + * public keys (can be NULL if no non-taproot inputs are used) + * n_plain_pubkeys: the number of non-taproot input public keys + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_recipient_public_data_create( + const secp256k1_context *ctx, + secp256k1_silentpayments_public_data *public_data, + const unsigned char *outpoint_smallest36, + const secp256k1_xonly_pubkey * const *xonly_pubkeys, + size_t n_xonly_pubkeys, + const secp256k1_pubkey * const *plain_pubkeys, + size_t n_plain_pubkeys +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Serialize a silentpayments_public_data object into a 33-byte sequence. + * + * Returns: 1 always. + * + * Args: ctx: pointer to a context object. + * Out: output33: pointer to a 33-byte array to place the serialized `silentpayments_public_data` in. + * In: public_data: pointer to an initialized silentpayments_public_data object. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_recipient_public_data_serialize( + const secp256k1_context *ctx, + unsigned char *output33, + const secp256k1_silentpayments_public_data *public_data +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Parse a 33-byte sequence into a silent_payments_public_data object. + * + * Returns: 1 if the data was able to be parsed. + * 0 if the sequence is invalid (e.g. does not represent a valid public key). + * + * Args: ctx: pointer to a context object. + * Out: public_data: pointer to a silentpayments_public_data object. If 1 is returned, it is set to a + * parsed version of input33. + * In: input33: pointer to a serialized silentpayments_public_data. + */ + +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_recipient_public_data_parse( + const secp256k1_context *ctx, + secp256k1_silentpayments_public_data *public_data, + const unsigned char *input33 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Scan for Silent Payment transaction outputs. + * + * Given a input public sum, an input_hash, a recipient's spend public key B_spend, and the relevant transaction + * outputs, scan for outputs belong to the recipient and return the tweak(s) needed for spending + * the output(s). An optional label_lookup callback function and label_context can be passed if the + * recipient uses labels. This allows for checking if a label exists in the recipients label cache + * and retrieving the label tweak during scanning. + * + * Returns: 1 if output scanning was successful. 0 if an error occured. + * Args: ctx: pointer to a context object + * Out: found_outputs: pointer to an array of pointers to found output objects. The found outputs + * array MUST be initialized to be the same length as the tx_outputs array + * n_found_outputs: pointer to an integer indicating the final size of the found outputs array. + * This number represents the number of outputs found while scanning (0 if + * none are found) + * In: tx_outputs: pointer to the tx's x-only public key outputs + * n_tx_outputs: the number of tx_outputs being scanned + * recipient_scan_key: pointer to the recipient's scan key + * public_data: pointer to the input public key sum (optionaly, with the `input_hash` + * multiplied in, see `_recipient_public_data_create`). + * recipient_spend_pubkey: pointer to the recipient's spend pubkey + * label_lookup: pointer to a callback function for looking up a label value. This fucntion + * takes a label pubkey as an argument and returns a pointer to the label tweak + * if the label exists, otherwise returns a nullptr (NULL if labels are not used) + * label_context: pointer to a label context object (NULL if labels are not used) + */ + +typedef const unsigned char* (*secp256k1_silentpayments_label_lookup)(const secp256k1_pubkey*, const void*); +typedef struct { + secp256k1_xonly_pubkey output; + unsigned char tweak[32]; + int found_with_label; + secp256k1_pubkey label; +} secp256k1_silentpayments_found_output; + +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_recipient_scan_outputs( + const secp256k1_context *ctx, + secp256k1_silentpayments_found_output **found_outputs, + size_t *n_found_outputs, + const secp256k1_xonly_pubkey * const *tx_outputs, + size_t n_tx_outputs, + const unsigned char *recipient_scan_key, + const secp256k1_silentpayments_public_data *public_data, + const secp256k1_pubkey *recipient_spend_pubkey, + const secp256k1_silentpayments_label_lookup label_lookup, + const void *label_context +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) + SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8); + +/** Create Silent Payment shared secret. + * + * Given the public input data (A_tweaked = input_hash * A_sum), calculate the shared secret using ECDH: + * + * shared_secret = b_scan * A_tweaked [Recipient, Light client scenario] + * + * The resulting shared secret is needed as input for creating silent payments + * outputs belonging to the same recipient scan public key. + * + * Returns: 1 if shared secret creation was successful. 0 if an error occured. + * Args: ctx: pointer to a context object + * Out: shared_secret33: pointer to the resulting 33-byte shared secret + * In: recipient_scan_key: pointer to the recipient's scan key + * public_data: pointer to the input public key sum, tweaked with the input_hash + * (see `_recipient_public_data_create`) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_recipient_create_shared_secret( + const secp256k1_context *ctx, + unsigned char *shared_secret33, + const unsigned char *recipient_scan_key, + const secp256k1_silentpayments_public_data *public_data +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Create Silent Payment output public key. + * + * Given a shared_secret, a public key B_spend, and an output counter k, + * calculate the corresponding output public key: + * + * P_output_xonly = B_spend + hash(shared_secret || ser_32(k)) * G + * + * This function is used by the recipient when scanning for outputs without access to the + * transaction outputs (e.g. using BIP158 block filters). + * When scanning with this function, it is the scanners responsibility to determine if the generated + * output exists in a block before proceeding to the next value of `k`. + * + * Returns: 1 if output creation was successful. 0 if an error occured. + * Args: ctx: pointer to a context object + * Out: P_output_xonly: pointer to the resulting output x-only pubkey + * In: shared_secret33: shared secret, derived from either sender's + * or recipient's perspective with routines from above + * recipient_spend_pubkey: pointer to the recipient's spend pubkey (labelled or unlabelled) + * k: output counter (initially set to 0, must be incremented for each + * additional output created or after each output found when scanning) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_recipient_create_output_pubkey( + const secp256k1_context *ctx, + secp256k1_xonly_pubkey *P_output_xonly, + const unsigned char *shared_secret33, + const secp256k1_pubkey *recipient_spend_pubkey, + unsigned int k +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +#ifdef __cplusplus +} +#endif + +#endif /* SECP256K1_SILENTPAYMENTS_H */ diff --git a/src/bench.c b/src/bench.c index 1127df67ae0ee6..db11e18f761757 100644 --- a/src/bench.c +++ b/src/bench.c @@ -63,6 +63,10 @@ static void help(int default_iters) { printf(" ellswift_ecdh : ECDH on ElligatorSwift keys\n"); #endif +#ifdef ENABLE_MODULE_SILENTPAYMENTS + printf(" silentpayments : Silent payments recipient scanning\n"); +#endif + printf("\n"); } @@ -165,6 +169,10 @@ static void bench_keygen_run(void *arg, int iters) { # include "modules/ellswift/bench_impl.h" #endif +#ifdef ENABLE_MODULE_SILENTPAYMENTS +# include "modules/silentpayments/bench_impl.h" +#endif + int main(int argc, char** argv) { int i; secp256k1_pubkey pubkey; @@ -179,7 +187,7 @@ int main(int argc, char** argv) { char* valid_args[] = {"ecdsa", "verify", "ecdsa_verify", "sign", "ecdsa_sign", "ecdh", "recover", "ecdsa_recover", "schnorrsig", "schnorrsig_verify", "schnorrsig_sign", "ec", "keygen", "ec_keygen", "ellswift", "encode", "ellswift_encode", "decode", - "ellswift_decode", "ellswift_keygen", "ellswift_ecdh"}; + "ellswift_decode", "ellswift_keygen", "ellswift_ecdh", "silentpayments"}; size_t valid_args_size = sizeof(valid_args)/sizeof(valid_args[0]); int invalid_args = have_invalid_args(argc, argv, valid_args, valid_args_size); @@ -231,6 +239,14 @@ int main(int argc, char** argv) { } #endif +#ifndef ENABLE_MODULE_SILENTPAYMENTS + if (have_flag(argc, argv, "silentpayments")) { + fprintf(stderr, "./bench: silentpayments module not enabled.\n"); + fprintf(stderr, "Use ./configure --enable-module-silentpayments.\n\n"); + return 1; + } +#endif + /* ECDSA benchmark */ data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); @@ -275,5 +291,11 @@ int main(int argc, char** argv) { run_ellswift_bench(iters, argc, argv); #endif +#ifdef ENABLE_MODULE_SILENTPAYMENTS + /* SilentPayments benchmarks */ + run_silentpayments_bench(iters, argc, argv); +#endif + + return 0; } diff --git a/src/modules/ecdh/tests_impl.h b/src/modules/ecdh/tests_impl.h index 3c3acdaf8c3290..6be96eacbe7a14 100644 --- a/src/modules/ecdh/tests_impl.h +++ b/src/modules/ecdh/tests_impl.h @@ -56,7 +56,7 @@ static void test_ecdh_generator_basepoint(void) { size_t point_ser_len = sizeof(point_ser); secp256k1_scalar s; - testutil_random_scalar_order(&s); + random_scalar_order(&s); secp256k1_scalar_get_b32(s_b32, &s); CHECK(secp256k1_ec_pubkey_create(CTX, &point[0], s_one) == 1); @@ -95,7 +95,7 @@ static void test_bad_scalar(void) { secp256k1_pubkey point; /* Create random point */ - testutil_random_scalar_order(&rand); + random_scalar_order(&rand); secp256k1_scalar_get_b32(s_rand, &rand); CHECK(secp256k1_ec_pubkey_create(CTX, &point, s_rand) == 1); @@ -127,7 +127,7 @@ static void test_result_basepoint(void) { CHECK(secp256k1_ecdh(CTX, out_base, &point, s_one, NULL, NULL) == 1); for (i = 0; i < 2 * COUNT; i++) { - testutil_random_scalar_order(&rand); + random_scalar_order(&rand); secp256k1_scalar_get_b32(s, &rand); secp256k1_scalar_inverse(&rand, &rand); secp256k1_scalar_get_b32(s_inv, &rand); diff --git a/src/modules/ellswift/tests_impl.h b/src/modules/ellswift/tests_impl.h index ed5658f34c5340..f96e3a1268bfa9 100644 --- a/src/modules/ellswift/tests_impl.h +++ b/src/modules/ellswift/tests_impl.h @@ -229,9 +229,9 @@ void run_ellswift_tests(void) { secp256k1_ge g, g2; secp256k1_pubkey pubkey, pubkey2; /* Generate random public key and random randomizer. */ - testutil_random_ge_test(&g); + random_group_element_test(&g); secp256k1_pubkey_save(&pubkey, &g); - testrand256(rnd32); + secp256k1_testrand256(rnd32); /* Convert the public key to ElligatorSwift and back. */ secp256k1_ellswift_encode(CTX, ell64, &pubkey, rnd32); secp256k1_ellswift_decode(CTX, &pubkey2, ell64); @@ -249,8 +249,8 @@ void run_ellswift_tests(void) { unsigned char ell64[64]; int ret; /* Generate random secret key and random randomizer. */ - if (i & 1) testrand256_test(auxrnd32); - testutil_random_scalar_order_test(&sec); + if (i & 1) secp256k1_testrand256_test(auxrnd32); + random_scalar_order_test(&sec); secp256k1_scalar_get_b32(sec32, &sec); /* Construct ElligatorSwift-encoded public keys for that key. */ ret = secp256k1_ellswift_create(CTX, ell64, sec32, (i & 1) ? auxrnd32 : NULL); @@ -271,11 +271,11 @@ void run_ellswift_tests(void) { secp256k1_pubkey pub; int ret; /* Generate random secret key. */ - testutil_random_scalar_order_test(&sec); + random_scalar_order_test(&sec); secp256k1_scalar_get_b32(sec32, &sec); /* Generate random ElligatorSwift encoding for the remote key and decode it. */ - testrand256_test(ell64); - testrand256_test(ell64 + 32); + secp256k1_testrand256_test(ell64); + secp256k1_testrand256_test(ell64 + 32); secp256k1_ellswift_decode(CTX, &pub, ell64); secp256k1_pubkey_load(CTX, &dec, &pub); secp256k1_gej_set_ge(&decj, &dec); @@ -313,18 +313,18 @@ void run_ellswift_tests(void) { data = NULL; } else { hash_function = secp256k1_ellswift_xdh_hash_function_prefix; - testrand256_test(prefix64); - testrand256_test(prefix64 + 32); + secp256k1_testrand256_test(prefix64); + secp256k1_testrand256_test(prefix64 + 32); data = prefix64; } /* Generate random secret keys and random randomizers. */ - testrand256_test(auxrnd32a); - testrand256_test(auxrnd32b); - testutil_random_scalar_order_test(&seca); + secp256k1_testrand256_test(auxrnd32a); + secp256k1_testrand256_test(auxrnd32b); + random_scalar_order_test(&seca); /* Draw secb uniformly at random to make sure that the secret keys * differ */ - testutil_random_scalar_order(&secb); + random_scalar_order(&secb); secp256k1_scalar_get_b32(sec32a, &seca); secp256k1_scalar_get_b32(sec32b, &secb); @@ -349,13 +349,13 @@ void run_ellswift_tests(void) { /* Verify that the shared secret doesn't match if other side's public key is incorrect. */ /* For A (using a bad public key for B): */ memcpy(ell64b_bad, ell64b, sizeof(ell64a_bad)); - testrand_flip(ell64b_bad, sizeof(ell64b_bad)); + secp256k1_testrand_flip(ell64b_bad, sizeof(ell64b_bad)); ret = secp256k1_ellswift_xdh(CTX, share32_bad, ell64a, ell64b_bad, sec32a, 0, hash_function, data); CHECK(ret); /* Mismatching encodings don't get detected by secp256k1_ellswift_xdh. */ CHECK(secp256k1_memcmp_var(share32_bad, share32a, 32) != 0); /* For B (using a bad public key for A): */ memcpy(ell64a_bad, ell64a, sizeof(ell64a_bad)); - testrand_flip(ell64a_bad, sizeof(ell64a_bad)); + secp256k1_testrand_flip(ell64a_bad, sizeof(ell64a_bad)); ret = secp256k1_ellswift_xdh(CTX, share32_bad, ell64a_bad, ell64b, sec32b, 1, hash_function, data); CHECK(ret); CHECK(secp256k1_memcmp_var(share32_bad, share32b, 32) != 0); @@ -363,12 +363,12 @@ void run_ellswift_tests(void) { /* Verify that the shared secret doesn't match if the private key is incorrect. */ /* For A: */ memcpy(sec32a_bad, sec32a, sizeof(sec32a_bad)); - testrand_flip(sec32a_bad, sizeof(sec32a_bad)); + secp256k1_testrand_flip(sec32a_bad, sizeof(sec32a_bad)); ret = secp256k1_ellswift_xdh(CTX, share32_bad, ell64a, ell64b, sec32a_bad, 0, hash_function, data); CHECK(!ret || secp256k1_memcmp_var(share32_bad, share32a, 32) != 0); /* For B: */ memcpy(sec32b_bad, sec32b, sizeof(sec32b_bad)); - testrand_flip(sec32b_bad, sizeof(sec32b_bad)); + secp256k1_testrand_flip(sec32b_bad, sizeof(sec32b_bad)); ret = secp256k1_ellswift_xdh(CTX, share32_bad, ell64a, ell64b, sec32b_bad, 1, hash_function, data); CHECK(!ret || secp256k1_memcmp_var(share32_bad, share32b, 32) != 0); @@ -376,7 +376,7 @@ void run_ellswift_tests(void) { /* Verify that the shared secret doesn't match when a different encoding of the same public key is used. */ /* For A (changing B's public key): */ memcpy(auxrnd32b_bad, auxrnd32b, sizeof(auxrnd32b_bad)); - testrand_flip(auxrnd32b_bad, sizeof(auxrnd32b_bad)); + secp256k1_testrand_flip(auxrnd32b_bad, sizeof(auxrnd32b_bad)); ret = secp256k1_ellswift_create(CTX, ell64b_bad, sec32b, auxrnd32b_bad); CHECK(ret); ret = secp256k1_ellswift_xdh(CTX, share32_bad, ell64a, ell64b_bad, sec32a, 0, hash_function, data); @@ -384,7 +384,7 @@ void run_ellswift_tests(void) { CHECK(secp256k1_memcmp_var(share32_bad, share32a, 32) != 0); /* For B (changing A's public key): */ memcpy(auxrnd32a_bad, auxrnd32a, sizeof(auxrnd32a_bad)); - testrand_flip(auxrnd32a_bad, sizeof(auxrnd32a_bad)); + secp256k1_testrand_flip(auxrnd32a_bad, sizeof(auxrnd32a_bad)); ret = secp256k1_ellswift_create(CTX, ell64a_bad, sec32a, auxrnd32a_bad); CHECK(ret); ret = secp256k1_ellswift_xdh(CTX, share32_bad, ell64a_bad, ell64b, sec32b, 1, hash_function, data); diff --git a/src/modules/extrakeys/tests_impl.h b/src/modules/extrakeys/tests_impl.h index ab4ef4a74b5c09..45521d1742d57b 100644 --- a/src/modules/extrakeys/tests_impl.h +++ b/src/modules/extrakeys/tests_impl.h @@ -23,9 +23,9 @@ static void test_xonly_pubkey(void) { int pk_parity; int i; - testrand256(sk); + secp256k1_testrand256(sk); memset(ones32, 0xFF, 32); - testrand256(xy_sk); + 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); @@ -95,7 +95,7 @@ static void test_xonly_pubkey(void) { * the curve) then xonly_pubkey_parse should fail as well. */ for (i = 0; i < COUNT; i++) { unsigned char rand33[33]; - testrand256(&rand33[1]); + secp256k1_testrand256(&rand33[1]); rand33[0] = SECP256K1_TAG_PUBKEY_EVEN; if (!secp256k1_ec_pubkey_parse(CTX, &pk, rand33, 33)) { memset(&xonly_pk, 1, sizeof(xonly_pk)); @@ -152,8 +152,8 @@ static void test_xonly_pubkey_tweak(void) { int i; memset(overflows, 0xff, sizeof(overflows)); - testrand256(tweak); - testrand256(sk); + 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); @@ -190,7 +190,7 @@ static void test_xonly_pubkey_tweak(void) { /* Invalid pk with a valid tweak */ memset(&internal_xonly_pk, 0, sizeof(internal_xonly_pk)); - testrand256(tweak); + secp256k1_testrand256(tweak); CHECK_ILLEGAL(CTX, secp256k1_xonly_pubkey_tweak_add(CTX, &output_pk, &internal_xonly_pk, tweak)); CHECK(secp256k1_memcmp_var(&output_pk, zeros64, sizeof(output_pk)) == 0); } @@ -209,8 +209,8 @@ static void test_xonly_pubkey_tweak_check(void) { unsigned char tweak[32]; memset(overflows, 0xff, sizeof(overflows)); - testrand256(tweak); - testrand256(sk); + 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); @@ -256,7 +256,7 @@ static void test_xonly_pubkey_tweak_recursive(void) { unsigned char tweak[N_PUBKEYS - 1][32]; int i; - testrand256(sk); + secp256k1_testrand256(sk); CHECK(secp256k1_ec_pubkey_create(CTX, &pk[0], sk) == 1); /* Add tweaks */ for (i = 0; i < N_PUBKEYS - 1; i++) { @@ -292,7 +292,7 @@ static void test_keypair(void) { memset(overflows, 0xFF, sizeof(overflows)); /* Test keypair_create */ - testrand256(sk); + secp256k1_testrand256(sk); CHECK(secp256k1_keypair_create(CTX, &keypair, sk) == 1); CHECK(secp256k1_memcmp_var(zeros96, &keypair, sizeof(keypair)) != 0); CHECK(secp256k1_keypair_create(CTX, &keypair, sk) == 1); @@ -311,7 +311,7 @@ static void test_keypair(void) { CHECK(secp256k1_memcmp_var(zeros96, &keypair, sizeof(keypair)) == 0); /* Test keypair_pub */ - testrand256(sk); + secp256k1_testrand256(sk); CHECK(secp256k1_keypair_create(CTX, &keypair, sk) == 1); CHECK(secp256k1_keypair_pub(CTX, &pk, &keypair) == 1); CHECK_ILLEGAL(CTX, secp256k1_keypair_pub(CTX, NULL, &keypair)); @@ -330,7 +330,7 @@ static void test_keypair(void) { CHECK(secp256k1_memcmp_var(&pk, &pk_tmp, sizeof(pk)) == 0); /** Test keypair_xonly_pub **/ - testrand256(sk); + secp256k1_testrand256(sk); CHECK(secp256k1_keypair_create(CTX, &keypair, sk) == 1); CHECK(secp256k1_keypair_xonly_pub(CTX, &xonly_pk, &pk_parity, &keypair) == 1); CHECK_ILLEGAL(CTX, secp256k1_keypair_xonly_pub(CTX, NULL, &pk_parity, &keypair)); @@ -353,7 +353,7 @@ static void test_keypair(void) { CHECK(pk_parity == pk_parity_tmp); /* Test keypair_seckey */ - testrand256(sk); + secp256k1_testrand256(sk); CHECK(secp256k1_keypair_create(CTX, &keypair, sk) == 1); CHECK(secp256k1_keypair_sec(CTX, sk_tmp, &keypair) == 1); CHECK_ILLEGAL(CTX, secp256k1_keypair_sec(CTX, NULL, &keypair)); @@ -381,8 +381,8 @@ static void test_keypair_add(void) { int i; CHECK(sizeof(zeros96) == sizeof(keypair)); - testrand256(sk); - testrand256(tweak); + secp256k1_testrand256(sk); + secp256k1_testrand256(tweak); memset(overflows, 0xFF, 32); CHECK(secp256k1_keypair_create(CTX, &keypair, sk) == 1); @@ -407,7 +407,7 @@ static void test_keypair_add(void) { for (i = 0; i < COUNT; i++) { secp256k1_scalar scalar_tweak; secp256k1_keypair keypair_tmp; - testrand256(sk); + secp256k1_testrand256(sk); 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 = @@ -423,7 +423,7 @@ static void test_keypair_add(void) { /* Invalid keypair with a valid tweak */ memset(&keypair, 0, sizeof(keypair)); - testrand256(tweak); + secp256k1_testrand256(tweak); CHECK_ILLEGAL(CTX, secp256k1_keypair_xonly_tweak_add(CTX, &keypair, tweak)); CHECK(secp256k1_memcmp_var(&keypair, zeros96, sizeof(keypair)) == 0); /* Only seckey part of keypair invalid */ @@ -446,7 +446,7 @@ static void test_keypair_add(void) { unsigned char sk32[32]; int pk_parity; - testrand256(tweak); + 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); diff --git a/src/modules/recovery/tests_impl.h b/src/modules/recovery/tests_impl.h index 7a28a3ce65124a..728ccfed8d2a5a 100644 --- a/src/modules/recovery/tests_impl.h +++ b/src/modules/recovery/tests_impl.h @@ -25,7 +25,7 @@ static int recovery_test_nonce_function(unsigned char *nonce32, const unsigned c } /* On the next run, return a valid nonce, but flip a coin as to whether or not to fail signing. */ memset(nonce32, 1, 32); - return testrand_bits(1); + return secp256k1_testrand_bits(1); } static void test_ecdsa_recovery_api(void) { @@ -106,8 +106,8 @@ static void test_ecdsa_recovery_end_to_end(void) { /* Generate a random key and message. */ { secp256k1_scalar msg, key; - testutil_random_scalar_order_test(&msg); - testutil_random_scalar_order_test(&key); + random_scalar_order_test(&msg); + random_scalar_order_test(&key); secp256k1_scalar_get_b32(privkey, &key); secp256k1_scalar_get_b32(message, &msg); } @@ -141,7 +141,7 @@ static void test_ecdsa_recovery_end_to_end(void) { 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); - sig[testrand_bits(6)] += 1 + testrand_int(255); + 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); diff --git a/src/modules/schnorrsig/tests_exhaustive_impl.h b/src/modules/schnorrsig/tests_exhaustive_impl.h index 601b54975d0119..bc31d8110720b9 100644 --- a/src/modules/schnorrsig/tests_exhaustive_impl.h +++ b/src/modules/schnorrsig/tests_exhaustive_impl.h @@ -104,7 +104,7 @@ static void test_exhaustive_schnorrsig_verify(const secp256k1_context *ctx, cons while (e_count_done < EXHAUSTIVE_TEST_ORDER) { secp256k1_scalar e; unsigned char msg32[32]; - testrand256(msg32); + secp256k1_testrand256(msg32); secp256k1_schnorrsig_challenge(&e, sig64, msg32, sizeof(msg32), pk32); /* Only do work if we hit a challenge we haven't tried before. */ if (!e_done[e]) { @@ -120,7 +120,7 @@ static void test_exhaustive_schnorrsig_verify(const secp256k1_context *ctx, cons expect_valid = actual_k != -1 && s != EXHAUSTIVE_TEST_ORDER && (s == (actual_k + actual_d * e) % EXHAUSTIVE_TEST_ORDER); } else { - testrand256(sig64 + 32); + secp256k1_testrand256(sig64 + 32); expect_valid = 0; } valid = secp256k1_schnorrsig_verify(ctx, sig64, msg32, sizeof(msg32), &pubkeys[d - 1]); @@ -161,7 +161,7 @@ static void test_exhaustive_schnorrsig_sign(const secp256k1_context *ctx, unsign /* Generate random messages until all challenges have been tried. */ while (e_count_done < EXHAUSTIVE_TEST_ORDER) { secp256k1_scalar e; - testrand256(msg32); + secp256k1_testrand256(msg32); secp256k1_schnorrsig_challenge(&e, xonly_pubkey_bytes[k - 1], msg32, sizeof(msg32), xonly_pubkey_bytes[d - 1]); /* Only do work if we hit a challenge we haven't tried before. */ if (!e_done[e]) { diff --git a/src/modules/schnorrsig/tests_impl.h b/src/modules/schnorrsig/tests_impl.h index aa4fc3827071a7..8ada90a87b0d67 100644 --- a/src/modules/schnorrsig/tests_impl.h +++ b/src/modules/schnorrsig/tests_impl.h @@ -15,7 +15,7 @@ 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); - testrand_flip(args[n_flip], n_bytes); + secp256k1_testrand_flip(args[n_flip], n_bytes); CHECK(nonce_function_bip340(nonces[1], args[0], msglen, args[1], args[2], args[3], algolen, args[4]) == 1); CHECK(secp256k1_memcmp_var(nonces[0], nonces[1], 32) != 0); } @@ -50,10 +50,10 @@ static void run_nonce_function_bip340_tests(void) { secp256k1_nonce_function_bip340_sha256_tagged_aux(&sha_optimized); test_sha256_eq(&sha, &sha_optimized); - testrand256(msg); - testrand256(key); - testrand256(pk); - testrand256(aux_rand); + secp256k1_testrand256(msg); + secp256k1_testrand256(key); + secp256k1_testrand256(pk); + secp256k1_testrand256(aux_rand); /* Check that a bitflip in an argument results in different nonces. */ args[0] = msg; @@ -76,12 +76,12 @@ static void run_nonce_function_bip340_tests(void) { CHECK(nonce_function_bip340(nonce, msg, msglen, key, pk, NULL, 0, NULL) == 0); CHECK(nonce_function_bip340(nonce, msg, msglen, key, pk, algo, algolen, NULL) == 1); /* Other algo is fine */ - testrand_bytes_test(algo, algolen); + 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++) { unsigned char nonce2[32]; - uint32_t offset = testrand_int(msglen - 1); + uint32_t offset = secp256k1_testrand_int(msglen - 1); size_t msglen_tmp = (msglen + offset) % msglen; size_t algolen_tmp; @@ -90,7 +90,7 @@ static void run_nonce_function_bip340_tests(void) { CHECK(secp256k1_memcmp_var(nonce, nonce2, 32) != 0); /* Different algolen gives different nonce */ - offset = testrand_int(algolen - 1); + offset = secp256k1_testrand_int(algolen - 1); algolen_tmp = (algolen + offset) % algolen; CHECK(nonce_function_bip340(nonce2, msg, msglen, key, pk, algo, algolen_tmp, NULL) == 1); CHECK(secp256k1_memcmp_var(nonce, nonce2, 32) != 0); @@ -116,10 +116,10 @@ static void test_schnorrsig_api(void) { secp256k1_schnorrsig_extraparams extraparams = SECP256K1_SCHNORRSIG_EXTRAPARAMS_INIT; secp256k1_schnorrsig_extraparams invalid_extraparams = {{ 0 }, NULL, NULL}; - testrand256(sk1); - testrand256(sk2); - testrand256(sk3); - testrand256(msg); + 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); @@ -813,8 +813,8 @@ static void test_schnorrsig_sign(void) { secp256k1_schnorrsig_extraparams extraparams = SECP256K1_SCHNORRSIG_EXTRAPARAMS_INIT; unsigned char aux_rand[32]; - testrand256(sk); - testrand256(aux_rand); + 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); @@ -861,12 +861,12 @@ static void test_schnorrsig_sign_verify(void) { secp256k1_xonly_pubkey pk; secp256k1_scalar s; - testrand256(sk); + secp256k1_testrand256(sk); CHECK(secp256k1_keypair_create(CTX, &keypair, sk)); CHECK(secp256k1_keypair_xonly_pub(CTX, &pk, NULL, &keypair)); for (i = 0; i < N_SIGS; i++) { - testrand256(msg[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)); } @@ -874,19 +874,19 @@ static void test_schnorrsig_sign_verify(void) { { /* Flip a few bits in the signature and in the message and check that * verify and verify_batch (TODO) fail */ - size_t sig_idx = testrand_int(N_SIGS); - size_t byte_idx = testrand_bits(5); - unsigned char xorbyte = testrand_int(254)+1; + size_t sig_idx = secp256k1_testrand_int(N_SIGS); + 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)); sig[sig_idx][byte_idx] ^= xorbyte; - byte_idx = testrand_bits(5); + 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)); sig[sig_idx][32+byte_idx] ^= xorbyte; - byte_idx = testrand_bits(5); + 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)); msg[sig_idx][byte_idx] ^= xorbyte; @@ -916,9 +916,9 @@ static void test_schnorrsig_sign_verify(void) { { /* Test varying message lengths */ unsigned char msg_large[32 * 8]; - uint32_t msglen = testrand_int(sizeof(msg_large)); + uint32_t msglen = secp256k1_testrand_int(sizeof(msg_large)); for (i = 0; i < sizeof(msg_large); i += 32) { - testrand256(&msg_large[i]); + 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); @@ -942,7 +942,7 @@ static void test_schnorrsig_taproot(void) { unsigned char sig[64]; /* Create output key */ - testrand256(sk); + secp256k1_testrand256(sk); 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 */ @@ -952,7 +952,7 @@ static void test_schnorrsig_taproot(void) { CHECK(secp256k1_xonly_pubkey_serialize(CTX, output_pk_bytes, &output_pk) == 1); /* Key spend */ - testrand256(msg); + secp256k1_testrand256(msg); 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); diff --git a/src/modules/silentpayments/Makefile.am.include b/src/modules/silentpayments/Makefile.am.include new file mode 100644 index 00000000000000..d0c51dc3373119 --- /dev/null +++ b/src/modules/silentpayments/Makefile.am.include @@ -0,0 +1,5 @@ +include_HEADERS += include/secp256k1_silentpayments.h +noinst_HEADERS += src/modules/silentpayments/main_impl.h +noinst_HEADERS += src/modules/silentpayments/bench_impl.h +noinst_HEADERS += src/modules/silentpayments/tests_impl.h +noinst_HEADERS += src/modules/silentpayments/vectors.h diff --git a/src/modules/silentpayments/bench_impl.h b/src/modules/silentpayments/bench_impl.h new file mode 100644 index 00000000000000..13fac6e5f59ecf --- /dev/null +++ b/src/modules/silentpayments/bench_impl.h @@ -0,0 +1,103 @@ +/*********************************************************************** + * Copyright (c) 2024 josibake * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_SILENTPAYMENTS_BENCH_H +#define SECP256K1_MODULE_SILENTPAYMENTS_BENCH_H + +#include "../../../include/secp256k1_silentpayments.h" + +typedef struct { + secp256k1_context *ctx; + secp256k1_pubkey spend_pubkey; + unsigned char scan_key[32]; + secp256k1_silentpayments_public_data public_data; + secp256k1_xonly_pubkey tx_outputs[4]; + secp256k1_silentpayments_found_output found_outputs[4]; + unsigned char scalar[32]; +} bench_silentpayments_data; + +const unsigned char* label_lookup(const secp256k1_pubkey* key, const void* cache_ptr) { + (void)key; + (void)cache_ptr; + return NULL; +} + +static void bench_silentpayments_scan_setup(void* arg) { + int i; + bench_silentpayments_data *data = (bench_silentpayments_data*)arg; + const unsigned char tx_outputs[4][32] = { + {0x84,0x17,0x92,0xc3,0x3c,0x9d,0xc6,0x19,0x3e,0x76,0x74,0x41,0x34,0x12,0x5d,0x40,0xad,0xd8,0xf2,0xf4,0xa9,0x64,0x75,0xf2,0x8b,0xa1,0x50,0xbe,0x03,0x2d,0x64,0xe8}, + {0x2e,0x84,0x7b,0xb0,0x1d,0x1b,0x49,0x1d,0xa5,0x12,0xdd,0xd7,0x60,0xb8,0x50,0x96,0x17,0xee,0x38,0x05,0x70,0x03,0xd6,0x11,0x5d,0x00,0xba,0x56,0x24,0x51,0x32,0x3a}, + {0xf2,0x07,0x16,0x2b,0x1a,0x7a,0xbc,0x51,0xc4,0x20,0x17,0xbe,0xf0,0x55,0xe9,0xec,0x1e,0xfc,0x3d,0x35,0x67,0xcb,0x72,0x03,0x57,0xe2,0xb8,0x43,0x25,0xdb,0x33,0xac}, + {0xe9,0x76,0xa5,0x8f,0xbd,0x38,0xae,0xb4,0xe6,0x09,0x3d,0x4d,0xf0,0x2e,0x9c,0x1d,0xe0,0xc4,0x51,0x3a,0xe0,0xc5,0x88,0xce,0xf6,0x8c,0xda,0x5b,0x2f,0x88,0x34,0xca} + }; + const unsigned char spend_pubkey[33] = { + 0x02,0xee,0x97,0xdf,0x83,0xb2,0x54,0x6a, + 0xf5,0xa7,0xd0,0x62,0x15,0xd9,0x8b,0xcb, + 0x63,0x7f,0xe0,0x5d,0xd0,0xfa,0x37,0x3b, + 0xd8,0x20,0xe6,0x64,0xd3,0x72,0xde,0x9a,0x01 + }; + const unsigned char scan_key[32] = { + 0xa8,0x90,0x54,0xc9,0x5b,0xe3,0xc3,0x01, + 0x56,0x65,0x74,0xf2,0xaa,0x93,0xad,0xe0, + 0x51,0x85,0x09,0x03,0xa6,0x9c,0xbd,0xd1, + 0xd4,0x7e,0xae,0x26,0x3d,0x7b,0xc0,0x31 + }; + secp256k1_keypair input_keypair; + secp256k1_pubkey input_pubkey; + unsigned char input_pubkey33[33]; + size_t pubkeylen = 33; + + for (i = 0; i < 32; i++) { + data->scalar[i] = i + 1; + } + for (i = 0; i < 4; i++) { + CHECK(secp256k1_xonly_pubkey_parse(data->ctx, &data->tx_outputs[i], tx_outputs[i])); + } + CHECK(secp256k1_keypair_create(data->ctx, &input_keypair, data->scalar)); + CHECK(secp256k1_keypair_pub(data->ctx, &input_pubkey, &input_keypair)); + CHECK(secp256k1_ec_pubkey_serialize(data->ctx, input_pubkey33, &pubkeylen, &input_pubkey, SECP256K1_EC_COMPRESSED)); + CHECK(secp256k1_silentpayments_recipient_public_data_parse(data->ctx, &data->public_data, input_pubkey33)); + CHECK(secp256k1_ec_pubkey_parse(data->ctx, &data->spend_pubkey, spend_pubkey, pubkeylen)); + memcpy(data->scan_key, scan_key, 32); +} + +static void bench_silentpayments_scan(void* arg, int iters) { + int i; + size_t n_found = 0; + secp256k1_silentpayments_found_output *found_output_ptrs[4]; + const secp256k1_xonly_pubkey *tx_output_ptrs[4]; + bench_silentpayments_data *data = (bench_silentpayments_data*)arg; + + for (i = 0; i < 4; i++) { + found_output_ptrs[i] = &data->found_outputs[i]; + tx_output_ptrs[i] = &data->tx_outputs[i]; + } + for (i = 0; i < iters; i++) { + CHECK(secp256k1_silentpayments_recipient_scan_outputs(data->ctx, + found_output_ptrs, &n_found, + tx_output_ptrs, 4, + data->scan_key, + &data->public_data, + &data->spend_pubkey, + label_lookup, NULL) + ); + } +} + +static void run_silentpayments_bench(int iters, int argc, char** argv) { + bench_silentpayments_data data; + int d = argc == 1; + + /* create a context with no capabilities */ + data.ctx = secp256k1_context_create(SECP256K1_FLAGS_TYPE_CONTEXT); + + if (d || have_flag(argc, argv, "silentpayments")) run_benchmark("silentpayments", bench_silentpayments_scan, bench_silentpayments_scan_setup, NULL, &data, 10, iters); + + secp256k1_context_destroy(data.ctx); +} + +#endif /* SECP256K1_MODULE_SILENTPAYMENTS_BENCH_H */ diff --git a/src/modules/silentpayments/main_impl.h b/src/modules/silentpayments/main_impl.h new file mode 100644 index 00000000000000..e595e79697d918 --- /dev/null +++ b/src/modules/silentpayments/main_impl.h @@ -0,0 +1,588 @@ +/*********************************************************************** + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_SILENTPAYMENTS_MAIN_H +#define SECP256K1_MODULE_SILENTPAYMENTS_MAIN_H + +#include "../../../include/secp256k1.h" +#include "../../../include/secp256k1_ecdh.h" +#include "../../../include/secp256k1_extrakeys.h" +#include "../../../include/secp256k1_silentpayments.h" + +/** Sort an array of silent payment recipients. This is used to group recipients by scan pubkey to + * ensure the correct values of k are used when creating multiple outputs for a recipient. */ +static int secp256k1_silentpayments_recipient_sort_cmp(const void* pk1, const void* pk2, void *ctx) { + return secp256k1_ec_pubkey_cmp((secp256k1_context *)ctx, + &(*(const secp256k1_silentpayments_recipient **)pk1)->scan_pubkey, + &(*(const secp256k1_silentpayments_recipient **)pk2)->scan_pubkey + ); +} + +int secp256k1_silentpayments_recipient_sort(const secp256k1_context* ctx, const secp256k1_silentpayments_recipient **recipients, size_t n_recipients) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(recipients != NULL); + + /* Suppress wrong warning (fixed in MSVC 19.33) */ + #if defined(_MSC_VER) && (_MSC_VER < 1933) + #pragma warning(push) + #pragma warning(disable: 4090) + #endif + + secp256k1_hsort(recipients, n_recipients, sizeof(*recipients), secp256k1_silentpayments_recipient_sort_cmp, (void *)ctx); + + #if defined(_MSC_VER) && (_MSC_VER < 1933) + #pragma warning(pop) + #endif + + return 1; +} + +/** Set hash state to the BIP340 tagged hash midstate for "BIP0352/Inputs". */ +static void secp256k1_silentpayments_sha256_init_inputs(secp256k1_sha256* hash) { + secp256k1_sha256_initialize(hash); + hash->s[0] = 0xd4143ffcul; + hash->s[1] = 0x012ea4b5ul; + hash->s[2] = 0x36e21c8ful; + hash->s[3] = 0xf7ec7b54ul; + hash->s[4] = 0x4dd4e2acul; + hash->s[5] = 0x9bcaa0a4ul; + hash->s[6] = 0xe244899bul; + hash->s[7] = 0xcd06903eul; + + hash->bytes = 64; +} + +static void secp256k1_silentpayments_calculate_input_hash(unsigned char *input_hash, const unsigned char *outpoint_smallest36, secp256k1_ge *pubkey_sum) { + secp256k1_sha256 hash; + unsigned char pubkey_sum_ser[33]; + size_t ser_size; + int ser_ret; + + secp256k1_silentpayments_sha256_init_inputs(&hash); + secp256k1_sha256_write(&hash, outpoint_smallest36, 36); + ser_ret = secp256k1_eckey_pubkey_serialize(pubkey_sum, pubkey_sum_ser, &ser_size, 1); + VERIFY_CHECK(ser_ret && ser_size == sizeof(pubkey_sum_ser)); + (void)ser_ret; + secp256k1_sha256_write(&hash, pubkey_sum_ser, sizeof(pubkey_sum_ser)); + secp256k1_sha256_finalize(&hash, input_hash); +} + +/* secp256k1_ecdh expects a hash function to be passed in or uses its default + * hashing function. We don't want to hash the ECDH result yet (it will be + * hashed later with a counter `k`), so we define a custom function which simply + * returns the pubkey without hashing. + */ +static int secp256k1_silentpayments_ecdh_return_pubkey(unsigned char *output, const unsigned char *x32, const unsigned char *y32, void *data) { + secp256k1_ge point; + secp256k1_fe x, y; + size_t ser_size; + int ser_ret; + + (void)data; + /* Parse point as group element */ + if (!secp256k1_fe_set_b32_limit(&x, x32) || !secp256k1_fe_set_b32_limit(&y, y32)) { + return 0; + } + secp256k1_ge_set_xy(&point, &x, &y); + + /* Serialize as compressed pubkey */ + ser_ret = secp256k1_eckey_pubkey_serialize(&point, output, &ser_size, 1); + VERIFY_CHECK(ser_ret && ser_size == 33); + (void)ser_ret; + + return 1; +} + +static int secp256k1_silentpayments_create_shared_secret(const secp256k1_context *ctx, unsigned char *shared_secret33, const unsigned char *secret_component, const secp256k1_pubkey *public_component, const unsigned char *input_hash) { + unsigned char tweaked_secret_component[32]; + /* Sanity check inputs */ + ARG_CHECK(shared_secret33 != NULL); + memset(shared_secret33, 0, 33); + ARG_CHECK(public_component != NULL); + ARG_CHECK(secret_component != NULL); + + /* Tweak secret component with input hash, if available */ + memcpy(tweaked_secret_component, secret_component, 32); + if (input_hash != NULL) { + if (!secp256k1_ec_seckey_tweak_mul(ctx, tweaked_secret_component, input_hash)) { + return 0; + } + } + + /* Compute shared_secret = tweaked_secret_component * Public_component */ + if (!secp256k1_ecdh(ctx, shared_secret33, public_component, tweaked_secret_component, secp256k1_silentpayments_ecdh_return_pubkey, NULL)) { + return 0; + } + + return 1; +} + +/** Set hash state to the BIP340 tagged hash midstate for "BIP0352/SharedSecret". */ +static void secp256k1_silentpayments_sha256_init_sharedsecret(secp256k1_sha256* hash) { + secp256k1_sha256_initialize(hash); + hash->s[0] = 0x88831537ul; + hash->s[1] = 0x5127079bul; + hash->s[2] = 0x69c2137bul; + hash->s[3] = 0xab0303e6ul; + hash->s[4] = 0x98fa21faul; + hash->s[5] = 0x4a888523ul; + hash->s[6] = 0xbd99daabul; + hash->s[7] = 0xf25e5e0aul; + + hash->bytes = 64; +} + +static void secp256k1_silentpayments_create_t_k(secp256k1_scalar *t_k_scalar, const unsigned char *shared_secret33, unsigned int k) { + secp256k1_sha256 hash; + unsigned char hash_ser[32]; + unsigned char k_serialized[4]; + + /* Compute t_k = hash(shared_secret || ser_32(k)) [sha256 with tag "BIP0352/SharedSecret"] */ + secp256k1_silentpayments_sha256_init_sharedsecret(&hash); + secp256k1_sha256_write(&hash, shared_secret33, 33); + secp256k1_write_be32(k_serialized, k); + secp256k1_sha256_write(&hash, k_serialized, sizeof(k_serialized)); + secp256k1_sha256_finalize(&hash, hash_ser); + secp256k1_scalar_set_b32(t_k_scalar, hash_ser, NULL); +} + +static int secp256k1_silentpayments_create_output_pubkey(const secp256k1_context *ctx, secp256k1_xonly_pubkey *P_output_xonly, const unsigned char *shared_secret33, const secp256k1_pubkey *recipient_spend_pubkey, unsigned int k) { + secp256k1_ge P_output_ge; + secp256k1_scalar t_k_scalar; + + /* Sanity check inputs */ + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(P_output_xonly != NULL); + ARG_CHECK(shared_secret33 != NULL); + ARG_CHECK(recipient_spend_pubkey != NULL); + + /* Calculate and return P_output_xonly = B_spend + t_k * G */ + secp256k1_silentpayments_create_t_k(&t_k_scalar, shared_secret33, k); + secp256k1_pubkey_load(ctx, &P_output_ge, recipient_spend_pubkey); + + /* This will fail if B_spend + t_k*G is the point at infinity */ + if (!secp256k1_eckey_pubkey_tweak_add(&P_output_ge, &t_k_scalar)) { + return 0; + } + secp256k1_xonly_pubkey_save(P_output_xonly, &P_output_ge); + + return 1; +} + +int secp256k1_silentpayments_sender_create_outputs( + const secp256k1_context *ctx, + secp256k1_xonly_pubkey **generated_outputs, + const secp256k1_silentpayments_recipient **recipients, + size_t n_recipients, + const unsigned char *outpoint_smallest36, + const secp256k1_keypair * const *taproot_seckeys, + size_t n_taproot_seckeys, + const unsigned char * const *plain_seckeys, + size_t n_plain_seckeys +) { + size_t i, k; + secp256k1_scalar a_sum_scalar, addend; + secp256k1_ge A_sum_ge; + secp256k1_gej A_sum_gej; + unsigned char input_hash[32]; + unsigned char a_sum[32]; + unsigned char shared_secret[33]; + secp256k1_silentpayments_recipient last_recipient; + + /* Sanity check inputs. */ + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(generated_outputs != NULL); + ARG_CHECK(recipients != NULL); + ARG_CHECK(n_recipients >= 1); + ARG_CHECK(plain_seckeys == NULL || n_plain_seckeys >= 1); + ARG_CHECK(taproot_seckeys == NULL || n_taproot_seckeys >= 1); + ARG_CHECK((plain_seckeys != NULL) || (taproot_seckeys != NULL)); + ARG_CHECK((n_plain_seckeys + n_taproot_seckeys) >= 1); + ARG_CHECK(outpoint_smallest36 != NULL); + /* ensure the index field is set correctly */ + for (i = 0; i < n_recipients; i++) { + ARG_CHECK(recipients[i]->index == i); + } + + /* Compute input private keys sum: a_sum = a_1 + a_2 + ... + a_n */ + a_sum_scalar = secp256k1_scalar_zero; + for (i = 0; i < n_plain_seckeys; i++) { + if (!secp256k1_scalar_set_b32_seckey(&addend, plain_seckeys[i])) { + return 0; + } + secp256k1_scalar_add(&a_sum_scalar, &a_sum_scalar, &addend); + } + /* private keys used for taproot outputs have to be negated if they resulted in an odd point */ + for (i = 0; i < n_taproot_seckeys; i++) { + secp256k1_ge addend_point; + if (!secp256k1_keypair_load(ctx, &addend, &addend_point, taproot_seckeys[i])) { + return 0; + } + if (secp256k1_fe_is_odd(&addend_point.y)) { + secp256k1_scalar_negate(&addend, &addend); + } + + secp256k1_scalar_add(&a_sum_scalar, &a_sum_scalar, &addend); + } + /* If the caller passes in seckeys that sum up to zero, there is nothing + * we can do here except have the caller try again with different seckeys, + * e.g. run coin selection again */ + if (secp256k1_scalar_is_zero(&a_sum_scalar)) { + return 0; + } + secp256k1_scalar_get_b32(a_sum, &a_sum_scalar); + + /* Compute input_hash = hash(outpoint_L || (a_sum * G)) */ + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &A_sum_gej, &a_sum_scalar); + secp256k1_ge_set_gej(&A_sum_ge, &A_sum_gej); + secp256k1_silentpayments_calculate_input_hash(input_hash, outpoint_smallest36, &A_sum_ge); + secp256k1_silentpayments_recipient_sort(ctx, recipients, n_recipients); + last_recipient = *recipients[0]; + k = 0; + for (i = 0; i < n_recipients; i++) { + if ((secp256k1_ec_pubkey_cmp(ctx, &last_recipient.scan_pubkey, &recipients[i]->scan_pubkey) != 0) || (i == 0)) { + /* if we are on a different scan pubkey, its time to recreate the the shared secret and reset k to 0 */ + if (!secp256k1_silentpayments_create_shared_secret(ctx, shared_secret, a_sum, &recipients[i]->scan_pubkey, input_hash)) { + return 0; + } + k = 0; + } + if (!secp256k1_silentpayments_create_output_pubkey(ctx, generated_outputs[recipients[i]->index], shared_secret, &recipients[i]->spend_pubkey, k)) { + return 0; + } + k++; + last_recipient = *recipients[i]; + } + return 1; +} + +/** Set hash state to the BIP340 tagged hash midstate for "BIP0352/Label". */ +static void secp256k1_silentpayments_sha256_init_label(secp256k1_sha256* hash) { + secp256k1_sha256_initialize(hash); + hash->s[0] = 0x26b95d63ul; + hash->s[1] = 0x8bf1b740ul; + hash->s[2] = 0x10a5986ful; + hash->s[3] = 0x06a387a5ul; + hash->s[4] = 0x2d1c1c30ul; + hash->s[5] = 0xd035951aul; + hash->s[6] = 0x2d7f0f96ul; + hash->s[7] = 0x29e3e0dbul; + + hash->bytes = 64; +} + +int secp256k1_silentpayments_recipient_create_label_tweak(const secp256k1_context *ctx, secp256k1_pubkey *label, unsigned char *label_tweak32, const unsigned char *recipient_scan_key, unsigned int m) { + secp256k1_sha256 hash; + unsigned char m_serialized[4]; + + /* Sanity check inputs. */ + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(label != NULL); + ARG_CHECK(label_tweak32 != NULL); + ARG_CHECK(recipient_scan_key != NULL); + + /* Compute label_tweak = hash(ser_256(b_scan) || ser_32(m)) [sha256 with tag "BIP0352/Label"] */ + secp256k1_silentpayments_sha256_init_label(&hash); + secp256k1_sha256_write(&hash, recipient_scan_key, 32); + secp256k1_write_be32(m_serialized, m); + secp256k1_sha256_write(&hash, m_serialized, sizeof(m_serialized)); + secp256k1_sha256_finalize(&hash, label_tweak32); + + /* Compute label = label_tweak * G */ + if (!secp256k1_ec_pubkey_create(ctx, label, label_tweak32)) { + return 0; + } + + return 1; +} + +int secp256k1_silentpayments_recipient_create_labelled_spend_pubkey(const secp256k1_context *ctx, secp256k1_pubkey *labelled_spend_pubkey, const secp256k1_pubkey *recipient_spend_pubkey, const secp256k1_pubkey *label) { + secp256k1_ge B_m, label_addend; + secp256k1_gej result_gej; + secp256k1_ge result_ge; + + /* Sanity check inputs. */ + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(labelled_spend_pubkey != NULL); + ARG_CHECK(recipient_spend_pubkey != NULL); + ARG_CHECK(label != NULL); + + /* Calculate B_m = B_spend + label */ + secp256k1_pubkey_load(ctx, &B_m, recipient_spend_pubkey); + secp256k1_pubkey_load(ctx, &label_addend, label); + secp256k1_gej_set_ge(&result_gej, &B_m); + secp256k1_gej_add_ge_var(&result_gej, &result_gej, &label_addend, NULL); + + /* Serialize B_m */ + secp256k1_ge_set_gej(&result_ge, &result_gej); + secp256k1_pubkey_save(labelled_spend_pubkey, &result_ge); + + return 1; +} + +int secp256k1_silentpayments_recipient_public_data_create( + const secp256k1_context *ctx, + secp256k1_silentpayments_public_data *public_data, + const unsigned char *outpoint_smallest36, + const secp256k1_xonly_pubkey * const *xonly_pubkeys, + size_t n_xonly_pubkeys, + const secp256k1_pubkey * const *plain_pubkeys, + size_t n_plain_pubkeys +) { + size_t i; + size_t pubkeylen = 65; + secp256k1_pubkey A_sum; + secp256k1_ge A_sum_ge, addend; + secp256k1_gej A_sum_gej; + unsigned char input_hash_local[32]; + + /* Sanity check inputs */ + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(public_data != NULL); + ARG_CHECK(plain_pubkeys == NULL || n_plain_pubkeys >= 1); + ARG_CHECK(xonly_pubkeys == NULL || n_xonly_pubkeys >= 1); + ARG_CHECK((plain_pubkeys != NULL) || (xonly_pubkeys != NULL)); + ARG_CHECK((n_plain_pubkeys + n_xonly_pubkeys) >= 1); + ARG_CHECK(outpoint_smallest36 != NULL); + memset(input_hash_local, 0, 32); + + /* Compute input public keys sum: A_sum = A_1 + A_2 + ... + A_n */ + secp256k1_gej_set_infinity(&A_sum_gej); + for (i = 0; i < n_plain_pubkeys; i++) { + if (!secp256k1_pubkey_load(ctx, &addend, plain_pubkeys[i])) { + return 0; + } + secp256k1_gej_add_ge_var(&A_sum_gej, &A_sum_gej, &addend, NULL); + } + for (i = 0; i < n_xonly_pubkeys; i++) { + if (!secp256k1_xonly_pubkey_load(ctx, &addend, xonly_pubkeys[i])) { + return 0; + } + secp256k1_gej_add_ge_var(&A_sum_gej, &A_sum_gej, &addend, NULL); + } + /* If the caller passes in all valid public keys but the public keys + * sum to 0 (the point at infinity), we can't do anything except tell + * the caller to try again with a different set of input public keys, + * e.g. skip the current transaction and move to the next */ + if (secp256k1_gej_is_infinity(&A_sum_gej)) { + return 0; + } + secp256k1_ge_set_gej(&A_sum_ge, &A_sum_gej); + + /* Compute input_hash = hash(outpoint_L || A_sum) */ + secp256k1_silentpayments_calculate_input_hash(input_hash_local, outpoint_smallest36, &A_sum_ge); + secp256k1_pubkey_save(&A_sum, &A_sum_ge); + /* serialize the public_data struct */ + public_data->data[0] = 0; + secp256k1_ec_pubkey_serialize(ctx, &public_data->data[1], &pubkeylen, &A_sum, SECP256K1_EC_UNCOMPRESSED); + memcpy(&public_data->data[1 + pubkeylen], input_hash_local, 32); + return 1; +} + +static int secp256k1_silentpayments_recipient_public_data_load(const secp256k1_context *ctx, secp256k1_pubkey *pubkey, unsigned char *input_hash, const secp256k1_silentpayments_public_data *public_data) { + int combined; + size_t pubkeylen = 65; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(pubkey != NULL); + ARG_CHECK(public_data != NULL); + + combined = (int)public_data->data[0]; + ARG_CHECK(combined == 0 || combined == 1); + if (combined) { + ARG_CHECK(combined == 1 && input_hash == NULL); + } else { + ARG_CHECK(combined == 0 && input_hash != NULL); + memcpy(input_hash, &public_data->data[1 + pubkeylen], 32); + } + if (!secp256k1_ec_pubkey_parse(ctx, pubkey, &public_data->data[1], pubkeylen)) { + return 0; + } + return 1; +} + +int secp256k1_silentpayments_recipient_public_data_serialize(const secp256k1_context *ctx, unsigned char *output33, const secp256k1_silentpayments_public_data *public_data) { + secp256k1_pubkey pubkey; + unsigned char input_hash[32]; + size_t pubkeylen = 33; + + ARG_CHECK(public_data->data[0] == 0); + if (!secp256k1_silentpayments_recipient_public_data_load(ctx, &pubkey, input_hash, public_data)) { + return 0; + } + if (!secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, input_hash)) { + return 0; + } + secp256k1_ec_pubkey_serialize(ctx, output33, &pubkeylen, &pubkey, SECP256K1_EC_COMPRESSED); + return 1; +} + +int secp256k1_silentpayments_recipient_public_data_parse(const secp256k1_context *ctx, secp256k1_silentpayments_public_data *public_data, const unsigned char *input33) { + size_t inputlen = 33; + size_t pubkeylen = 65; + secp256k1_pubkey pubkey; + + ARG_CHECK(public_data != NULL); + ARG_CHECK(input33 != NULL); + if (!secp256k1_ec_pubkey_parse(ctx, &pubkey, input33, inputlen)) { + return 0; + } + public_data->data[0] = 1; + secp256k1_ec_pubkey_serialize(ctx, &public_data->data[1], &pubkeylen, &pubkey, SECP256K1_EC_UNCOMPRESSED); + memset(&public_data->data[1 + pubkeylen], 0, 32); + return 1; +} + +int secp256k1_silentpayments_recipient_scan_outputs( + const secp256k1_context *ctx, + secp256k1_silentpayments_found_output **found_outputs, size_t *n_found_outputs, + const secp256k1_xonly_pubkey * const *tx_outputs, size_t n_tx_outputs, + const unsigned char *recipient_scan_key, + const secp256k1_silentpayments_public_data *public_data, + const secp256k1_pubkey *recipient_spend_pubkey, + const secp256k1_silentpayments_label_lookup label_lookup, + const void *label_context +) { + secp256k1_scalar t_k_scalar; + secp256k1_ge label_ge, recipient_spend_pubkey_ge; + secp256k1_pubkey A_sum; + secp256k1_xonly_pubkey P_output_xonly; + unsigned char shared_secret[33]; + unsigned char label_tweak32[32]; + const unsigned char *label_tweak = label_tweak32; + size_t i, k, n_found, found_idx; + int found, combined; + + /* Sanity check inputs */ + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(found_outputs != NULL); + ARG_CHECK(n_found_outputs != NULL); + ARG_CHECK(tx_outputs != NULL); + ARG_CHECK(n_tx_outputs >= 1); + ARG_CHECK(recipient_scan_key != NULL); + ARG_CHECK(public_data != NULL); + combined = (int)public_data->data[0]; + { + unsigned char input_hash[32]; + unsigned char *input_hash_ptr; + if (combined) { + input_hash_ptr = NULL; + } else { + memset(input_hash, 0, 32); + input_hash_ptr = input_hash; + } + if (!secp256k1_silentpayments_recipient_public_data_load(ctx, &A_sum, input_hash_ptr, public_data)) { + return 0; + } + secp256k1_pubkey_load(ctx, &recipient_spend_pubkey_ge, recipient_spend_pubkey); + if (!secp256k1_silentpayments_create_shared_secret(ctx, shared_secret, recipient_scan_key, &A_sum, input_hash_ptr)) { + return 0; + } + } + + found_idx = 0; + n_found = 0; + k = 0; + while (1) { + secp256k1_ge P_output_ge = recipient_spend_pubkey_ge; + /* Calculate t_k = hash(shared_secret || ser_32(k)) */ + secp256k1_silentpayments_create_t_k(&t_k_scalar, shared_secret, k); + + /* Calculate P_output = B_spend + t_k * G */ + if (!secp256k1_eckey_pubkey_tweak_add(&P_output_ge, &t_k_scalar)) { + return 0; + } + + found = 0; + secp256k1_xonly_pubkey_save(&P_output_xonly, &P_output_ge); + for (i = 0; i < n_tx_outputs; i++) { + if (secp256k1_xonly_pubkey_cmp(ctx, &P_output_xonly, tx_outputs[i]) == 0) { + label_tweak = NULL; + found = 1; + found_idx = i; + break; + } + + /* If not found, proceed to check for labels (if the labels cache is present) */ + if (label_lookup != NULL) { + secp256k1_pubkey label_pubkey; + secp256k1_ge P_output_negated_ge, tx_output_ge; + secp256k1_gej tx_output_gej, label_gej; + + secp256k1_xonly_pubkey_load(ctx, &tx_output_ge, tx_outputs[i]); + secp256k1_gej_set_ge(&tx_output_gej, &tx_output_ge); + secp256k1_ge_neg(&P_output_negated_ge, &P_output_ge); + /* Negate the generated output and calculate first scan label candidate: + * label1 = tx_output - P_output */ + secp256k1_gej_add_ge_var(&label_gej, &tx_output_gej, &P_output_negated_ge, NULL); + secp256k1_ge_set_gej(&label_ge, &label_gej); + secp256k1_pubkey_save(&label_pubkey, &label_ge); + label_tweak = label_lookup(&label_pubkey, label_context); + if (label_tweak != NULL) { + found = 1; + found_idx = i; + break; + } + + secp256k1_gej_neg(&label_gej, &tx_output_gej); + /* If not found, negate the tx_output and calculate second scan label candidate: + * label2 = -tx_output - P_output */ + secp256k1_gej_add_ge_var(&label_gej, &label_gej, &P_output_negated_ge, NULL); + secp256k1_ge_set_gej(&label_ge, &label_gej); + secp256k1_pubkey_save(&label_pubkey, &label_ge); + label_tweak = label_lookup(&label_pubkey, label_context); + if (label_tweak != NULL) { + found = 1; + found_idx = i; + break; + } + } + } + if (found) { + found_outputs[n_found]->output = *tx_outputs[found_idx]; + secp256k1_scalar_get_b32(found_outputs[n_found]->tweak, &t_k_scalar); + if (label_lookup != NULL && label_tweak != NULL) { + found_outputs[n_found]->found_with_label = 1; + if (!secp256k1_ec_seckey_tweak_add(ctx, found_outputs[n_found]->tweak, label_tweak)) { + return 0; + } + secp256k1_pubkey_save(&found_outputs[n_found]->label, &label_ge); + } else { + found_outputs[n_found]->found_with_label = 0; + /* TODO: instead of using the tx_output, set the label with a properly invalid pubkey */ + secp256k1_pubkey_save(&found_outputs[n_found]->label, &P_output_ge); + } + /* Set everything for the next round of scanning */ + label_tweak = label_tweak32; + n_found++; + k++; + } else { + break; + } + } + *n_found_outputs = n_found; + return 1; +} + +int secp256k1_silentpayments_recipient_create_shared_secret(const secp256k1_context *ctx, unsigned char *shared_secret33, const unsigned char *recipient_scan_key, const secp256k1_silentpayments_public_data *public_data) { + secp256k1_pubkey A_tweaked; + /* Sanity check inputs */ + ARG_CHECK(shared_secret33 != NULL); + ARG_CHECK(recipient_scan_key != NULL); + ARG_CHECK(public_data != NULL); + ARG_CHECK(public_data->data[0] == 1); + if (!secp256k1_silentpayments_recipient_public_data_load(ctx, &A_tweaked, NULL, public_data)) { + return 0; + } + return secp256k1_silentpayments_create_shared_secret(ctx, shared_secret33, recipient_scan_key, &A_tweaked, NULL); +} + +int secp256k1_silentpayments_recipient_create_output_pubkey(const secp256k1_context *ctx, secp256k1_xonly_pubkey *P_output_xonly, const unsigned char *shared_secret33, const secp256k1_pubkey *recipient_spend_pubkey, unsigned int k) +{ + return secp256k1_silentpayments_create_output_pubkey(ctx, P_output_xonly, shared_secret33, recipient_spend_pubkey, k); +} + + +#endif diff --git a/src/modules/silentpayments/tests_impl.h b/src/modules/silentpayments/tests_impl.h new file mode 100644 index 00000000000000..d12606ce6acf3b --- /dev/null +++ b/src/modules/silentpayments/tests_impl.h @@ -0,0 +1,276 @@ +/*********************************************************************** + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_SILENTPAYMENTS_TESTS_H +#define SECP256K1_MODULE_SILENTPAYMENTS_TESTS_H + +#include "../../../include/secp256k1_silentpayments.h" +#include "../../../src/modules/silentpayments/vectors.h" + +struct label_cache_entry { + secp256k1_pubkey label; + unsigned char label_tweak[32]; +}; +struct labels_cache { + const secp256k1_context *ctx; + size_t entries_used; + struct label_cache_entry entries[10]; +}; +struct labels_cache labels_cache; +const unsigned char* label_lookup(const secp256k1_pubkey* key, const void* cache_ptr) { + const struct labels_cache* cache = (const struct labels_cache*)cache_ptr; + size_t i; + for (i = 0; i < cache->entries_used; i++) { + if (secp256k1_ec_pubkey_cmp(cache->ctx, &cache->entries[i].label, key) == 0) { + return cache->entries[i].label_tweak; + } + } + return NULL; +} + +void run_silentpayments_test_vector_send(const struct bip352_test_vector *test) { + secp256k1_silentpayments_recipient recipients[MAX_OUTPUTS_PER_TEST_CASE]; + const secp256k1_silentpayments_recipient *recipient_ptrs[MAX_OUTPUTS_PER_TEST_CASE]; + secp256k1_xonly_pubkey generated_outputs[MAX_OUTPUTS_PER_TEST_CASE]; + secp256k1_xonly_pubkey *generated_output_ptrs[MAX_OUTPUTS_PER_TEST_CASE]; + secp256k1_keypair taproot_keypairs[MAX_INPUTS_PER_TEST_CASE]; + secp256k1_keypair const *taproot_keypair_ptrs[MAX_INPUTS_PER_TEST_CASE]; + unsigned char const *plain_seckeys[MAX_INPUTS_PER_TEST_CASE]; + unsigned char created_output[32]; + size_t i, j, k; + int match, ret; + + /* Check that sender creates expected outputs */ + for (i = 0; i < test->num_outputs; i++) { + CHECK(secp256k1_ec_pubkey_parse(CTX, &recipients[i].scan_pubkey, test->recipient_pubkeys[i].scan_pubkey, 33)); + CHECK(secp256k1_ec_pubkey_parse(CTX, &recipients[i].spend_pubkey, test->recipient_pubkeys[i].spend_pubkey, 33)); + recipients[i].index = i; + recipient_ptrs[i] = &recipients[i]; + generated_output_ptrs[i] = &generated_outputs[i]; + } + for (i = 0; i < test->num_plain_inputs; i++) { + plain_seckeys[i] = test->plain_seckeys[i]; + } + for (i = 0; i < test->num_taproot_inputs; i++) { + CHECK(secp256k1_keypair_create(CTX, &taproot_keypairs[i], test->taproot_seckeys[i])); + taproot_keypair_ptrs[i] = &taproot_keypairs[i]; + } + ret = secp256k1_silentpayments_sender_create_outputs(CTX, + generated_output_ptrs, + recipient_ptrs, + test->num_outputs, + test->outpoint_smallest, + test->num_taproot_inputs > 0 ? taproot_keypair_ptrs : NULL, test->num_taproot_inputs, + test->num_plain_inputs > 0 ? plain_seckeys : NULL, test->num_plain_inputs + ); + /* If we are unable to create outputs, e.g., the input keys sum to zero, check that the + * expected number of recipient outputs for this test case is zero + */ + if (!ret) { + CHECK(test->num_recipient_outputs == 0); + return; + } + + match = 0; + for (i = 0; i < test->num_output_sets; i++) { + size_t n_matches = 0; + for (j = 0; j < test->num_outputs; j++) { + CHECK(secp256k1_xonly_pubkey_serialize(CTX, created_output, &generated_outputs[j])); + /* Loop over both lists to ensure tests don't fail due to different orderings of outputs */ + for (k = 0; k < test->num_recipient_outputs; k++) { + if (secp256k1_memcmp_var(created_output, test->recipient_outputs[i][k], 32) == 0) { + n_matches++; + break; + } + } + } + if (n_matches == test->num_recipient_outputs) { + match = 1; + break; + } + } + CHECK(match); +} + +void run_silentpayments_test_vector_receive(const struct bip352_test_vector *test) { + secp256k1_pubkey plain_pubkeys_objs[MAX_INPUTS_PER_TEST_CASE]; + secp256k1_xonly_pubkey xonly_pubkeys_objs[MAX_INPUTS_PER_TEST_CASE]; + secp256k1_xonly_pubkey tx_output_objs[MAX_OUTPUTS_PER_TEST_CASE]; + secp256k1_silentpayments_found_output found_output_objs[MAX_OUTPUTS_PER_TEST_CASE]; + secp256k1_pubkey const *plain_pubkeys[MAX_INPUTS_PER_TEST_CASE]; + secp256k1_xonly_pubkey const *xonly_pubkeys[MAX_INPUTS_PER_TEST_CASE]; + secp256k1_xonly_pubkey const *tx_outputs[MAX_OUTPUTS_PER_TEST_CASE]; + secp256k1_silentpayments_found_output *found_outputs[MAX_OUTPUTS_PER_TEST_CASE]; + unsigned char found_outputs_light_client[MAX_OUTPUTS_PER_TEST_CASE][32]; + secp256k1_pubkey recipient_scan_pubkey; + secp256k1_pubkey recipient_spend_pubkey; + size_t i,j; + int match, ret; + size_t n_found = 0; + unsigned char found_output[32]; + unsigned char found_signatures[10][64]; + secp256k1_silentpayments_public_data public_data, public_data_index; + unsigned char shared_secret_lightclient[33]; + unsigned char light_client_data[33]; + + + /* prepare the inputs */ + { + for (i = 0; i < test->num_plain_inputs; i++) { + CHECK(secp256k1_ec_pubkey_parse(CTX, &plain_pubkeys_objs[i], test->plain_pubkeys[i], 33)); + plain_pubkeys[i] = &plain_pubkeys_objs[i]; + } + for (i = 0; i < test->num_taproot_inputs; i++) { + CHECK(secp256k1_xonly_pubkey_parse(CTX, &xonly_pubkeys_objs[i], test->xonly_pubkeys[i])); + xonly_pubkeys[i] = &xonly_pubkeys_objs[i]; + } + ret = secp256k1_silentpayments_recipient_public_data_create(CTX, &public_data, + test->outpoint_smallest, + test->num_taproot_inputs > 0 ? xonly_pubkeys : NULL, test->num_taproot_inputs, + test->num_plain_inputs > 0 ? plain_pubkeys : NULL, test->num_plain_inputs + ); + /* If we are unable to create the public_data object, e.g., the input public keys sum to + * zero, check that the expected number of recipient outputs for this test case is zero + */ + if (!ret) { + CHECK(test->num_found_output_pubkeys == 0); + return; + } + } + /* prepare the outputs */ + { + for (i = 0; i < test->num_to_scan_outputs; i++) { + CHECK(secp256k1_xonly_pubkey_parse(CTX, &tx_output_objs[i], test->to_scan_outputs[i])); + tx_outputs[i] = &tx_output_objs[i]; + } + for (i = 0; i < test->num_found_output_pubkeys; i++) { + found_outputs[i] = &found_output_objs[i]; + } + } + + /* scan / spend pubkeys are not in the given data of the recipient part, so let's compute them */ + CHECK(secp256k1_ec_pubkey_create(CTX, &recipient_scan_pubkey, test->scan_seckey)); + CHECK(secp256k1_ec_pubkey_create(CTX, &recipient_spend_pubkey, test->spend_seckey)); + + /* create labels cache */ + labels_cache.ctx = CTX; + labels_cache.entries_used = 0; + for (i = 0; i < test->num_labels; i++) { + unsigned int m = test->label_integers[i]; + struct label_cache_entry *cache_entry = &labels_cache.entries[labels_cache.entries_used]; + CHECK(secp256k1_silentpayments_recipient_create_label_tweak(CTX, &cache_entry->label, cache_entry->label_tweak, test->scan_seckey, m)); + labels_cache.entries_used++; + } + CHECK(secp256k1_silentpayments_recipient_scan_outputs(CTX, + found_outputs, &n_found, + tx_outputs, test->num_to_scan_outputs, + test->scan_seckey, + &public_data, + &recipient_spend_pubkey, + label_lookup, &labels_cache) + ); + for (i = 0; i < n_found; i++) { + unsigned char full_seckey[32]; + secp256k1_keypair keypair; + unsigned char signature[64]; + const unsigned char msg32[32] = /* sha256("message") */ + {0xab,0x53,0x0a,0x13,0xe4,0x59,0x14,0x98,0x2b,0x79,0xf9,0xb7,0xe3,0xfb,0xa9,0x94, + 0xcf,0xd1,0xf3,0xfb,0x22,0xf7,0x1c,0xea,0x1a,0xfb,0xf0,0x2b,0x46,0x0c,0x6d,0x1d}; + const unsigned char aux32[32] = /* sha256("random auxiliary data") */ + {0x0b,0x3f,0xdd,0xfd,0x67,0xbf,0x76,0xae,0x76,0x39,0xee,0x73,0x5b,0x70,0xff,0x15, + 0x83,0xfd,0x92,0x48,0xc0,0x57,0xd2,0x86,0x07,0xa2,0x15,0xf4,0x0b,0x0a,0x3e,0xcc}; + memcpy(&full_seckey, test->spend_seckey, 32); + CHECK(secp256k1_ec_seckey_tweak_add(CTX, full_seckey, found_outputs[i]->tweak)); + CHECK(secp256k1_keypair_create(CTX, &keypair, full_seckey)); + CHECK(secp256k1_schnorrsig_sign32(CTX, signature, msg32, &keypair, aux32)); + memcpy(found_signatures[i], signature, 64); + } + + /* compare expected and scanned outputs (including calculated seckey tweaks and signatures) */ + match = 0; + for (i = 0; i < n_found; i++) { + CHECK(secp256k1_xonly_pubkey_serialize(CTX, found_output, &found_outputs[i]->output)); + for (j = 0; j < test->num_found_output_pubkeys; j++) { + if (secp256k1_memcmp_var(&found_output, test->found_output_pubkeys[j], 32) == 0) { + CHECK(secp256k1_memcmp_var(found_outputs[i]->tweak, test->found_seckey_tweaks[j], 32) == 0); + CHECK(secp256k1_memcmp_var(found_signatures[i], test->found_signatures[j], 64) == 0); + match = 1; + break; + } + } + CHECK(match); + } + CHECK(n_found == test->num_found_output_pubkeys); + /* Scan as a light client + * it is not recommended to use labels as a light client so here we are only + * running this on tests that do not involve labels. Primarily, this test is to + * ensure that _recipient_created_shared_secret and _create_shared_secret are the same + */ + if (test->num_labels == 0) { + CHECK(secp256k1_silentpayments_recipient_public_data_serialize(CTX, light_client_data, &public_data)); + CHECK(secp256k1_silentpayments_recipient_public_data_parse(CTX, &public_data_index, light_client_data)); + CHECK(secp256k1_silentpayments_recipient_create_shared_secret(CTX, shared_secret_lightclient, test->scan_seckey, &public_data_index)); + n_found = 0; + { + int found = 0; + size_t k = 0; + secp256k1_xonly_pubkey potential_output; + + while(1) { + + CHECK(secp256k1_silentpayments_recipient_create_output_pubkey(CTX, + &potential_output, + shared_secret_lightclient, + &recipient_spend_pubkey, + k + )); + /* At this point, we check that the utxo exists with a light client protocol. + * For this example, we'll just iterate through the list of pubkeys */ + found = 0; + for (i = 0; i < test->num_to_scan_outputs; i++) { + if (secp256k1_xonly_pubkey_cmp(CTX, &potential_output, tx_outputs[i]) == 0) { + secp256k1_xonly_pubkey_serialize(CTX, found_outputs_light_client[n_found], &potential_output); + found = 1; + n_found++; + k++; + break; + } + } + if (!found) { + break; + } + } + } + CHECK(n_found == test->num_found_output_pubkeys); + for (i = 0; i < n_found; i++) { + match = 0; + for (j = 0; j < test->num_found_output_pubkeys; j++) { + if (secp256k1_memcmp_var(&found_outputs_light_client[i], test->found_output_pubkeys[j], 32) == 0) { + match = 1; + break; + } + } + CHECK(match); + } + } +} + +void run_silentpayments_test_vectors(void) { + size_t i; + + + for (i = 0; i < sizeof(bip352_test_vectors) / sizeof(bip352_test_vectors[0]); i++) { + const struct bip352_test_vector *test = &bip352_test_vectors[i]; + run_silentpayments_test_vector_send(test); + run_silentpayments_test_vector_receive(test); + } +} + +void run_silentpayments_tests(void) { + run_silentpayments_test_vectors(); + /* TODO: add a few manual tests here, that target the ECC-related parts of silent payments */ +} + +#endif diff --git a/src/modules/silentpayments/vectors.h b/src/modules/silentpayments/vectors.h new file mode 100644 index 00000000000000..d0bf4dbc03e530 --- /dev/null +++ b/src/modules/silentpayments/vectors.h @@ -0,0 +1,3900 @@ +/* Note: this file was autogenerated using tests_silentpayments_generate.py. Do not edit. */ +#define SECP256K1_SILENTPAYMENTS_NUMBER_TESTVECTORS (25) + +#define MAX_INPUTS_PER_TEST_CASE 3 +#define MAX_OUTPUTS_PER_TEST_CASE 4 +#define MAX_PERMUTATIONS_PER_SENDING_TEST_CASE 12 + +struct bip352_recipient_addressdata { + unsigned char scan_pubkey[33]; + unsigned char spend_pubkey[33]; +}; + +struct bip352_test_vector { + /* Inputs (private keys / public keys + smallest outpoint) */ + size_t num_plain_inputs; + unsigned char plain_seckeys[MAX_INPUTS_PER_TEST_CASE][32]; + unsigned char plain_pubkeys[MAX_INPUTS_PER_TEST_CASE][33]; + + size_t num_taproot_inputs; + unsigned char taproot_seckeys[MAX_INPUTS_PER_TEST_CASE][32]; + unsigned char xonly_pubkeys[MAX_INPUTS_PER_TEST_CASE][32]; + + unsigned char outpoint_smallest[36]; + + /* Given sender data (pubkeys encoded per output address to send to) */ + size_t num_outputs; + struct bip352_recipient_addressdata recipient_pubkeys[MAX_OUTPUTS_PER_TEST_CASE]; + + /* Expected sender data */ + size_t num_output_sets; + size_t num_recipient_outputs; + unsigned char recipient_outputs[MAX_PERMUTATIONS_PER_SENDING_TEST_CASE][MAX_OUTPUTS_PER_TEST_CASE][32]; + + /* Given recipient data */ + unsigned char scan_seckey[32]; + unsigned char spend_seckey[32]; + size_t num_to_scan_outputs; + unsigned char to_scan_outputs[MAX_OUTPUTS_PER_TEST_CASE][32]; + size_t num_labels; + unsigned int label_integers[MAX_OUTPUTS_PER_TEST_CASE]; + + /* Expected recipient data */ + size_t num_found_output_pubkeys; + unsigned char found_output_pubkeys[MAX_OUTPUTS_PER_TEST_CASE][32]; + unsigned char found_seckey_tweaks[MAX_OUTPUTS_PER_TEST_CASE][32]; + unsigned char found_signatures[MAX_OUTPUTS_PER_TEST_CASE][64]; +}; + +static const struct bip352_test_vector bip352_test_vectors[SECP256K1_SILENTPAYMENTS_NUMBER_TESTVECTORS] = { + /* ----- Simple send: two inputs (1) ----- */ + { + 2, + { /* input plain seckeys */ + {0xea,0xdc,0x78,0x16,0x5f,0xf1,0xf8,0xea,0x94,0xad,0x7c,0xfd,0xc5,0x49,0x90,0x73,0x8a,0x4c,0x53,0xf6,0xe0,0x50,0x7b,0x42,0x15,0x42,0x01,0xb8,0xe5,0xdf,0xf3,0xb1}, + {0x93,0xf5,0xed,0x90,0x7a,0xd5,0xb2,0xbd,0xbb,0xdc,0xb5,0xd9,0x11,0x6e,0xbc,0x0a,0x4e,0x1f,0x92,0xf9,0x10,0xd5,0x26,0x02,0x37,0xfa,0x45,0xa9,0x40,0x8a,0xad,0x16}, + "" + }, + { /* input plain pubkeys */ + {0x02,0x5a,0x1e,0x61,0xf8,0x98,0x17,0x30,0x40,0xe2,0x06,0x16,0xd4,0x3e,0x9f,0x49,0x6f,0xba,0x90,0x33,0x8a,0x39,0xfa,0xa1,0xed,0x98,0xfc,0xba,0xee,0xe4,0xdd,0x9b,0xe5}, + {0x03,0xbd,0x85,0x68,0x5d,0x03,0xd1,0x11,0x69,0x9b,0x15,0xd0,0x46,0x31,0x9f,0xeb,0xe7,0x7f,0x8d,0xe5,0x28,0x6e,0x9e,0x51,0x27,0x03,0xcd,0xee,0x1b,0xf3,0xbe,0x37,0x92}, + "" + }, + 0, + { /* input taproot seckeys */ + "", + "", + "" + }, + { /* input x-only pubkeys */ + "", + "", + "" + }, + /* smallest outpoint */ + {0x16,0x9e,0x1e,0x83,0xe9,0x30,0x85,0x33,0x91,0xbc,0x6f,0x35,0xf6,0x05,0xc6,0x75,0x4c,0xfe,0xad,0x57,0xcf,0x83,0x87,0x63,0x9d,0x3b,0x40,0x96,0xc5,0x4f,0x18,0xf4,0x00,0x00,0x00,0x00}, + 1, + { /* recipient pubkeys (address data) */ + { + {0x02,0x20,0xbc,0xfa,0xc5,0xb9,0x9e,0x04,0xad,0x1a,0x06,0xdd,0xfb,0x01,0x6e,0xe1,0x35,0x82,0x60,0x9d,0x60,0xb6,0x29,0x1e,0x98,0xd0,0x1a,0x9b,0xc9,0xa1,0x6c,0x96,0xd4}, + {0x02,0x5c,0xc9,0x85,0x6d,0x6f,0x83,0x75,0x35,0x0e,0x12,0x39,0x78,0xda,0xac,0x20,0x0c,0x26,0x0c,0xb5,0xb5,0xae,0x83,0x10,0x6c,0xab,0x90,0x48,0x4d,0xcd,0x8f,0xcf,0x36}, + }, + { + "", + "", + }, + { + "", + "", + }, + { + "", + "", + } + }, + 1, + 1, + { /* recipient outputs */ + { + {0x3e,0x9f,0xce,0x73,0xd4,0xe7,0x7a,0x48,0x09,0x90,0x8e,0x3c,0x3a,0x2e,0x54,0xee,0x14,0x7b,0x93,0x12,0xdc,0x50,0x44,0xa1,0x93,0xd1,0xfc,0x85,0xde,0x46,0xe3,0xc1}, + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + }, + /* recipient data (scan and spend seckeys) */ + {0x0f,0x69,0x4e,0x06,0x80,0x28,0xa7,0x17,0xf8,0xaf,0x6b,0x94,0x11,0xf9,0xa1,0x33,0xdd,0x35,0x65,0x25,0x87,0x14,0xcc,0x22,0x65,0x94,0xb3,0x4d,0xb9,0x0c,0x1f,0x2c}, + {0x9d,0x6a,0xd8,0x55,0xce,0x34,0x17,0xef,0x84,0xe8,0x36,0x89,0x2e,0x5a,0x56,0x39,0x2b,0xfb,0xa0,0x5f,0xa5,0xd9,0x7c,0xce,0xa3,0x0e,0x26,0x6f,0x54,0x0e,0x08,0xb3}, + 1, + { /* outputs to scan */ + {0x3e,0x9f,0xce,0x73,0xd4,0xe7,0x7a,0x48,0x09,0x90,0x8e,0x3c,0x3a,0x2e,0x54,0xee,0x14,0x7b,0x93,0x12,0xdc,0x50,0x44,0xa1,0x93,0xd1,0xfc,0x85,0xde,0x46,0xe3,0xc1}, + "", + "", + "" + }, + 0, {0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}, /* labels */ + /* expected output data (pubkeys and seckey tweaks) */ + 1, + { + {0x3e,0x9f,0xce,0x73,0xd4,0xe7,0x7a,0x48,0x09,0x90,0x8e,0x3c,0x3a,0x2e,0x54,0xee,0x14,0x7b,0x93,0x12,0xdc,0x50,0x44,0xa1,0x93,0xd1,0xfc,0x85,0xde,0x46,0xe3,0xc1}, + "", + "", + "" + }, + { + {0xf4,0x38,0xb4,0x01,0x79,0xa3,0xc4,0x26,0x2d,0xe1,0x29,0x86,0xc0,0xe6,0xcc,0xe0,0x63,0x40,0x07,0xcd,0xc7,0x9c,0x1d,0xcd,0x3e,0x20,0xb9,0xeb,0xc2,0xe7,0xee,0xf6}, + "", + "", + "" + }, + { + {0x74,0xf8,0x5b,0x85,0x63,0x37,0xfb,0xe8,0x37,0x64,0x3b,0x86,0xf4,0x62,0x11,0x81,0x59,0xf9,0x3a,0xc4,0xac,0xc2,0x67,0x15,0x22,0xf2,0x7e,0x8f,0x67,0xb0,0x79,0x95,0x91,0x95,0xcc,0xc7,0xa5,0xdb,0xee,0x39,0x6d,0x29,0x09,0xf5,0xd6,0x80,0xd6,0xe3,0x0c,0xda,0x73,0x59,0xaa,0x27,0x55,0x82,0x25,0x09,0xb7,0x0d,0x6b,0x06,0x87,0xa1}, + "", + "", + "" + } + }, + + /* ----- Simple send: two inputs, order reversed (2) ----- */ + { + 2, + { /* input plain seckeys */ + {0xea,0xdc,0x78,0x16,0x5f,0xf1,0xf8,0xea,0x94,0xad,0x7c,0xfd,0xc5,0x49,0x90,0x73,0x8a,0x4c,0x53,0xf6,0xe0,0x50,0x7b,0x42,0x15,0x42,0x01,0xb8,0xe5,0xdf,0xf3,0xb1}, + {0x93,0xf5,0xed,0x90,0x7a,0xd5,0xb2,0xbd,0xbb,0xdc,0xb5,0xd9,0x11,0x6e,0xbc,0x0a,0x4e,0x1f,0x92,0xf9,0x10,0xd5,0x26,0x02,0x37,0xfa,0x45,0xa9,0x40,0x8a,0xad,0x16}, + "" + }, + { /* input plain pubkeys */ + {0x02,0x5a,0x1e,0x61,0xf8,0x98,0x17,0x30,0x40,0xe2,0x06,0x16,0xd4,0x3e,0x9f,0x49,0x6f,0xba,0x90,0x33,0x8a,0x39,0xfa,0xa1,0xed,0x98,0xfc,0xba,0xee,0xe4,0xdd,0x9b,0xe5}, + {0x03,0xbd,0x85,0x68,0x5d,0x03,0xd1,0x11,0x69,0x9b,0x15,0xd0,0x46,0x31,0x9f,0xeb,0xe7,0x7f,0x8d,0xe5,0x28,0x6e,0x9e,0x51,0x27,0x03,0xcd,0xee,0x1b,0xf3,0xbe,0x37,0x92}, + "" + }, + 0, + { /* input taproot seckeys */ + "", + "", + "" + }, + { /* input x-only pubkeys */ + "", + "", + "" + }, + /* smallest outpoint */ + {0x16,0x9e,0x1e,0x83,0xe9,0x30,0x85,0x33,0x91,0xbc,0x6f,0x35,0xf6,0x05,0xc6,0x75,0x4c,0xfe,0xad,0x57,0xcf,0x83,0x87,0x63,0x9d,0x3b,0x40,0x96,0xc5,0x4f,0x18,0xf4,0x00,0x00,0x00,0x00}, + 1, + { /* recipient pubkeys (address data) */ + { + {0x02,0x20,0xbc,0xfa,0xc5,0xb9,0x9e,0x04,0xad,0x1a,0x06,0xdd,0xfb,0x01,0x6e,0xe1,0x35,0x82,0x60,0x9d,0x60,0xb6,0x29,0x1e,0x98,0xd0,0x1a,0x9b,0xc9,0xa1,0x6c,0x96,0xd4}, + {0x02,0x5c,0xc9,0x85,0x6d,0x6f,0x83,0x75,0x35,0x0e,0x12,0x39,0x78,0xda,0xac,0x20,0x0c,0x26,0x0c,0xb5,0xb5,0xae,0x83,0x10,0x6c,0xab,0x90,0x48,0x4d,0xcd,0x8f,0xcf,0x36}, + }, + { + "", + "", + }, + { + "", + "", + }, + { + "", + "", + } + }, + 1, + 1, + { /* recipient outputs */ + { + {0x3e,0x9f,0xce,0x73,0xd4,0xe7,0x7a,0x48,0x09,0x90,0x8e,0x3c,0x3a,0x2e,0x54,0xee,0x14,0x7b,0x93,0x12,0xdc,0x50,0x44,0xa1,0x93,0xd1,0xfc,0x85,0xde,0x46,0xe3,0xc1}, + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + }, + /* recipient data (scan and spend seckeys) */ + {0x0f,0x69,0x4e,0x06,0x80,0x28,0xa7,0x17,0xf8,0xaf,0x6b,0x94,0x11,0xf9,0xa1,0x33,0xdd,0x35,0x65,0x25,0x87,0x14,0xcc,0x22,0x65,0x94,0xb3,0x4d,0xb9,0x0c,0x1f,0x2c}, + {0x9d,0x6a,0xd8,0x55,0xce,0x34,0x17,0xef,0x84,0xe8,0x36,0x89,0x2e,0x5a,0x56,0x39,0x2b,0xfb,0xa0,0x5f,0xa5,0xd9,0x7c,0xce,0xa3,0x0e,0x26,0x6f,0x54,0x0e,0x08,0xb3}, + 1, + { /* outputs to scan */ + {0x3e,0x9f,0xce,0x73,0xd4,0xe7,0x7a,0x48,0x09,0x90,0x8e,0x3c,0x3a,0x2e,0x54,0xee,0x14,0x7b,0x93,0x12,0xdc,0x50,0x44,0xa1,0x93,0xd1,0xfc,0x85,0xde,0x46,0xe3,0xc1}, + "", + "", + "" + }, + 0, {0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}, /* labels */ + /* expected output data (pubkeys and seckey tweaks) */ + 1, + { + {0x3e,0x9f,0xce,0x73,0xd4,0xe7,0x7a,0x48,0x09,0x90,0x8e,0x3c,0x3a,0x2e,0x54,0xee,0x14,0x7b,0x93,0x12,0xdc,0x50,0x44,0xa1,0x93,0xd1,0xfc,0x85,0xde,0x46,0xe3,0xc1}, + "", + "", + "" + }, + { + {0xf4,0x38,0xb4,0x01,0x79,0xa3,0xc4,0x26,0x2d,0xe1,0x29,0x86,0xc0,0xe6,0xcc,0xe0,0x63,0x40,0x07,0xcd,0xc7,0x9c,0x1d,0xcd,0x3e,0x20,0xb9,0xeb,0xc2,0xe7,0xee,0xf6}, + "", + "", + "" + }, + { + {0x74,0xf8,0x5b,0x85,0x63,0x37,0xfb,0xe8,0x37,0x64,0x3b,0x86,0xf4,0x62,0x11,0x81,0x59,0xf9,0x3a,0xc4,0xac,0xc2,0x67,0x15,0x22,0xf2,0x7e,0x8f,0x67,0xb0,0x79,0x95,0x91,0x95,0xcc,0xc7,0xa5,0xdb,0xee,0x39,0x6d,0x29,0x09,0xf5,0xd6,0x80,0xd6,0xe3,0x0c,0xda,0x73,0x59,0xaa,0x27,0x55,0x82,0x25,0x09,0xb7,0x0d,0x6b,0x06,0x87,0xa1}, + "", + "", + "" + } + }, + + /* ----- Simple send: two inputs from the same transaction (3) ----- */ + { + 2, + { /* input plain seckeys */ + {0xea,0xdc,0x78,0x16,0x5f,0xf1,0xf8,0xea,0x94,0xad,0x7c,0xfd,0xc5,0x49,0x90,0x73,0x8a,0x4c,0x53,0xf6,0xe0,0x50,0x7b,0x42,0x15,0x42,0x01,0xb8,0xe5,0xdf,0xf3,0xb1}, + {0x93,0xf5,0xed,0x90,0x7a,0xd5,0xb2,0xbd,0xbb,0xdc,0xb5,0xd9,0x11,0x6e,0xbc,0x0a,0x4e,0x1f,0x92,0xf9,0x10,0xd5,0x26,0x02,0x37,0xfa,0x45,0xa9,0x40,0x8a,0xad,0x16}, + "" + }, + { /* input plain pubkeys */ + {0x02,0x5a,0x1e,0x61,0xf8,0x98,0x17,0x30,0x40,0xe2,0x06,0x16,0xd4,0x3e,0x9f,0x49,0x6f,0xba,0x90,0x33,0x8a,0x39,0xfa,0xa1,0xed,0x98,0xfc,0xba,0xee,0xe4,0xdd,0x9b,0xe5}, + {0x03,0xbd,0x85,0x68,0x5d,0x03,0xd1,0x11,0x69,0x9b,0x15,0xd0,0x46,0x31,0x9f,0xeb,0xe7,0x7f,0x8d,0xe5,0x28,0x6e,0x9e,0x51,0x27,0x03,0xcd,0xee,0x1b,0xf3,0xbe,0x37,0x92}, + "" + }, + 0, + { /* input taproot seckeys */ + "", + "", + "" + }, + { /* input x-only pubkeys */ + "", + "", + "" + }, + /* smallest outpoint */ + {0x16,0x9e,0x1e,0x83,0xe9,0x30,0x85,0x33,0x91,0xbc,0x6f,0x35,0xf6,0x05,0xc6,0x75,0x4c,0xfe,0xad,0x57,0xcf,0x83,0x87,0x63,0x9d,0x3b,0x40,0x96,0xc5,0x4f,0x18,0xf4,0x03,0x00,0x00,0x00}, + 1, + { /* recipient pubkeys (address data) */ + { + {0x02,0x20,0xbc,0xfa,0xc5,0xb9,0x9e,0x04,0xad,0x1a,0x06,0xdd,0xfb,0x01,0x6e,0xe1,0x35,0x82,0x60,0x9d,0x60,0xb6,0x29,0x1e,0x98,0xd0,0x1a,0x9b,0xc9,0xa1,0x6c,0x96,0xd4}, + {0x02,0x5c,0xc9,0x85,0x6d,0x6f,0x83,0x75,0x35,0x0e,0x12,0x39,0x78,0xda,0xac,0x20,0x0c,0x26,0x0c,0xb5,0xb5,0xae,0x83,0x10,0x6c,0xab,0x90,0x48,0x4d,0xcd,0x8f,0xcf,0x36}, + }, + { + "", + "", + }, + { + "", + "", + }, + { + "", + "", + } + }, + 1, + 1, + { /* recipient outputs */ + { + {0x79,0xe7,0x1b,0xaa,0x2b,0xa3,0xfc,0x66,0x39,0x6d,0xe3,0xa0,0x4f,0x16,0x8c,0x7b,0xf2,0x4d,0x68,0x70,0xec,0x88,0xca,0x87,0x77,0x54,0x79,0x0c,0x1d,0xb3,0x57,0xb6}, + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + }, + /* recipient data (scan and spend seckeys) */ + {0x0f,0x69,0x4e,0x06,0x80,0x28,0xa7,0x17,0xf8,0xaf,0x6b,0x94,0x11,0xf9,0xa1,0x33,0xdd,0x35,0x65,0x25,0x87,0x14,0xcc,0x22,0x65,0x94,0xb3,0x4d,0xb9,0x0c,0x1f,0x2c}, + {0x9d,0x6a,0xd8,0x55,0xce,0x34,0x17,0xef,0x84,0xe8,0x36,0x89,0x2e,0x5a,0x56,0x39,0x2b,0xfb,0xa0,0x5f,0xa5,0xd9,0x7c,0xce,0xa3,0x0e,0x26,0x6f,0x54,0x0e,0x08,0xb3}, + 1, + { /* outputs to scan */ + {0x79,0xe7,0x1b,0xaa,0x2b,0xa3,0xfc,0x66,0x39,0x6d,0xe3,0xa0,0x4f,0x16,0x8c,0x7b,0xf2,0x4d,0x68,0x70,0xec,0x88,0xca,0x87,0x77,0x54,0x79,0x0c,0x1d,0xb3,0x57,0xb6}, + "", + "", + "" + }, + 0, {0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}, /* labels */ + /* expected output data (pubkeys and seckey tweaks) */ + 1, + { + {0x79,0xe7,0x1b,0xaa,0x2b,0xa3,0xfc,0x66,0x39,0x6d,0xe3,0xa0,0x4f,0x16,0x8c,0x7b,0xf2,0x4d,0x68,0x70,0xec,0x88,0xca,0x87,0x77,0x54,0x79,0x0c,0x1d,0xb3,0x57,0xb6}, + "", + "", + "" + }, + { + {0x48,0x51,0x45,0x5b,0xfb,0xe1,0xab,0x4f,0x80,0x15,0x65,0x70,0xaa,0x45,0x06,0x32,0x01,0xaa,0x5c,0x9e,0x1b,0x1d,0xcd,0x29,0xf0,0xf8,0xc3,0x3d,0x10,0xbf,0x77,0xae}, + "", + "", + "" + }, + { + {0x10,0x33,0x2e,0xea,0x80,0x8b,0x6a,0x13,0xf7,0x00,0x59,0xa8,0xa7,0x31,0x95,0x80,0x8d,0xb7,0x82,0x01,0x29,0x07,0xf5,0xba,0x32,0xb6,0xea,0xe6,0x6a,0x2f,0x66,0xb4,0xf6,0x51,0x47,0xe2,0xb9,0x68,0xa1,0x67,0x8c,0x5f,0x73,0xd5,0x7d,0x5d,0x19,0x5d,0xba,0xf6,0x67,0xb6,0x06,0xff,0x80,0xc8,0x49,0x0e,0xac,0x1f,0x3b,0x71,0x06,0x57}, + "", + "", + "" + } + }, + + /* ----- Simple send: two inputs from the same transaction, order reversed (4) ----- */ + { + 2, + { /* input plain seckeys */ + {0xea,0xdc,0x78,0x16,0x5f,0xf1,0xf8,0xea,0x94,0xad,0x7c,0xfd,0xc5,0x49,0x90,0x73,0x8a,0x4c,0x53,0xf6,0xe0,0x50,0x7b,0x42,0x15,0x42,0x01,0xb8,0xe5,0xdf,0xf3,0xb1}, + {0x93,0xf5,0xed,0x90,0x7a,0xd5,0xb2,0xbd,0xbb,0xdc,0xb5,0xd9,0x11,0x6e,0xbc,0x0a,0x4e,0x1f,0x92,0xf9,0x10,0xd5,0x26,0x02,0x37,0xfa,0x45,0xa9,0x40,0x8a,0xad,0x16}, + "" + }, + { /* input plain pubkeys */ + {0x02,0x5a,0x1e,0x61,0xf8,0x98,0x17,0x30,0x40,0xe2,0x06,0x16,0xd4,0x3e,0x9f,0x49,0x6f,0xba,0x90,0x33,0x8a,0x39,0xfa,0xa1,0xed,0x98,0xfc,0xba,0xee,0xe4,0xdd,0x9b,0xe5}, + {0x03,0xbd,0x85,0x68,0x5d,0x03,0xd1,0x11,0x69,0x9b,0x15,0xd0,0x46,0x31,0x9f,0xeb,0xe7,0x7f,0x8d,0xe5,0x28,0x6e,0x9e,0x51,0x27,0x03,0xcd,0xee,0x1b,0xf3,0xbe,0x37,0x92}, + "" + }, + 0, + { /* input taproot seckeys */ + "", + "", + "" + }, + { /* input x-only pubkeys */ + "", + "", + "" + }, + /* smallest outpoint */ + {0x8d,0xd4,0xf5,0xfb,0xd5,0xe9,0x80,0xfc,0x02,0xf3,0x5c,0x6c,0xe1,0x45,0x93,0x5b,0x11,0xe2,0x84,0x60,0x5b,0xf5,0x99,0xa1,0x3c,0x6d,0x41,0x5d,0xb5,0x5d,0x07,0xa1,0x03,0x00,0x00,0x00}, + 1, + { /* recipient pubkeys (address data) */ + { + {0x02,0x20,0xbc,0xfa,0xc5,0xb9,0x9e,0x04,0xad,0x1a,0x06,0xdd,0xfb,0x01,0x6e,0xe1,0x35,0x82,0x60,0x9d,0x60,0xb6,0x29,0x1e,0x98,0xd0,0x1a,0x9b,0xc9,0xa1,0x6c,0x96,0xd4}, + {0x02,0x5c,0xc9,0x85,0x6d,0x6f,0x83,0x75,0x35,0x0e,0x12,0x39,0x78,0xda,0xac,0x20,0x0c,0x26,0x0c,0xb5,0xb5,0xae,0x83,0x10,0x6c,0xab,0x90,0x48,0x4d,0xcd,0x8f,0xcf,0x36}, + }, + { + "", + "", + }, + { + "", + "", + }, + { + "", + "", + } + }, + 1, + 1, + { /* recipient outputs */ + { + {0xf4,0xc2,0xda,0x80,0x7f,0x89,0xcb,0x15,0x01,0xf1,0xa7,0x73,0x22,0xa8,0x95,0xac,0xfb,0x93,0xc2,0x8e,0x08,0xed,0x27,0x24,0xd2,0xbe,0xb8,0xe4,0x45,0x39,0xba,0x38}, + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + }, + /* recipient data (scan and spend seckeys) */ + {0x0f,0x69,0x4e,0x06,0x80,0x28,0xa7,0x17,0xf8,0xaf,0x6b,0x94,0x11,0xf9,0xa1,0x33,0xdd,0x35,0x65,0x25,0x87,0x14,0xcc,0x22,0x65,0x94,0xb3,0x4d,0xb9,0x0c,0x1f,0x2c}, + {0x9d,0x6a,0xd8,0x55,0xce,0x34,0x17,0xef,0x84,0xe8,0x36,0x89,0x2e,0x5a,0x56,0x39,0x2b,0xfb,0xa0,0x5f,0xa5,0xd9,0x7c,0xce,0xa3,0x0e,0x26,0x6f,0x54,0x0e,0x08,0xb3}, + 1, + { /* outputs to scan */ + {0xf4,0xc2,0xda,0x80,0x7f,0x89,0xcb,0x15,0x01,0xf1,0xa7,0x73,0x22,0xa8,0x95,0xac,0xfb,0x93,0xc2,0x8e,0x08,0xed,0x27,0x24,0xd2,0xbe,0xb8,0xe4,0x45,0x39,0xba,0x38}, + "", + "", + "" + }, + 0, {0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}, /* labels */ + /* expected output data (pubkeys and seckey tweaks) */ + 1, + { + {0xf4,0xc2,0xda,0x80,0x7f,0x89,0xcb,0x15,0x01,0xf1,0xa7,0x73,0x22,0xa8,0x95,0xac,0xfb,0x93,0xc2,0x8e,0x08,0xed,0x27,0x24,0xd2,0xbe,0xb8,0xe4,0x45,0x39,0xba,0x38}, + "", + "", + "" + }, + { + {0xab,0x0c,0x9b,0x87,0x18,0x1b,0xf5,0x27,0x87,0x9f,0x48,0xdb,0x9f,0x14,0xa0,0x22,0x33,0x61,0x9b,0x98,0x6f,0x8e,0x8f,0x2d,0x5d,0x40,0x8c,0xe6,0x8a,0x70,0x9f,0x51}, + "", + "", + "" + }, + { + {0x39,0x8a,0x97,0x90,0x86,0x57,0x91,0xa9,0xdb,0x41,0xa8,0x01,0x5a,0xfa,0xd3,0xa4,0x7d,0x60,0xfe,0xc5,0x08,0x6c,0x50,0x55,0x78,0x06,0xa4,0x9a,0x1b,0xc0,0x38,0x80,0x86,0x32,0xb8,0xfe,0x67,0x9a,0x7b,0xb6,0x5f,0xc6,0xb4,0x55,0xbe,0x99,0x45,0x02,0xee,0xd8,0x49,0xf1,0xda,0x37,0x29,0xcd,0x94,0x8f,0xc7,0xbe,0x73,0xd6,0x72,0x95}, + "", + "", + "" + } + }, + + /* ----- Outpoint ordering byte-lexicographically vs. vout-integer (5) ----- */ + { + 2, + { /* input plain seckeys */ + {0xea,0xdc,0x78,0x16,0x5f,0xf1,0xf8,0xea,0x94,0xad,0x7c,0xfd,0xc5,0x49,0x90,0x73,0x8a,0x4c,0x53,0xf6,0xe0,0x50,0x7b,0x42,0x15,0x42,0x01,0xb8,0xe5,0xdf,0xf3,0xb1}, + {0x93,0xf5,0xed,0x90,0x7a,0xd5,0xb2,0xbd,0xbb,0xdc,0xb5,0xd9,0x11,0x6e,0xbc,0x0a,0x4e,0x1f,0x92,0xf9,0x10,0xd5,0x26,0x02,0x37,0xfa,0x45,0xa9,0x40,0x8a,0xad,0x16}, + "" + }, + { /* input plain pubkeys */ + {0x02,0x5a,0x1e,0x61,0xf8,0x98,0x17,0x30,0x40,0xe2,0x06,0x16,0xd4,0x3e,0x9f,0x49,0x6f,0xba,0x90,0x33,0x8a,0x39,0xfa,0xa1,0xed,0x98,0xfc,0xba,0xee,0xe4,0xdd,0x9b,0xe5}, + {0x03,0xbd,0x85,0x68,0x5d,0x03,0xd1,0x11,0x69,0x9b,0x15,0xd0,0x46,0x31,0x9f,0xeb,0xe7,0x7f,0x8d,0xe5,0x28,0x6e,0x9e,0x51,0x27,0x03,0xcd,0xee,0x1b,0xf3,0xbe,0x37,0x92}, + "" + }, + 0, + { /* input taproot seckeys */ + "", + "", + "" + }, + { /* input x-only pubkeys */ + "", + "", + "" + }, + /* smallest outpoint */ + {0x16,0x9e,0x1e,0x83,0xe9,0x30,0x85,0x33,0x91,0xbc,0x6f,0x35,0xf6,0x05,0xc6,0x75,0x4c,0xfe,0xad,0x57,0xcf,0x83,0x87,0x63,0x9d,0x3b,0x40,0x96,0xc5,0x4f,0x18,0xf4,0x00,0x01,0x00,0x00}, + 1, + { /* recipient pubkeys (address data) */ + { + {0x02,0x20,0xbc,0xfa,0xc5,0xb9,0x9e,0x04,0xad,0x1a,0x06,0xdd,0xfb,0x01,0x6e,0xe1,0x35,0x82,0x60,0x9d,0x60,0xb6,0x29,0x1e,0x98,0xd0,0x1a,0x9b,0xc9,0xa1,0x6c,0x96,0xd4}, + {0x02,0x5c,0xc9,0x85,0x6d,0x6f,0x83,0x75,0x35,0x0e,0x12,0x39,0x78,0xda,0xac,0x20,0x0c,0x26,0x0c,0xb5,0xb5,0xae,0x83,0x10,0x6c,0xab,0x90,0x48,0x4d,0xcd,0x8f,0xcf,0x36}, + }, + { + "", + "", + }, + { + "", + "", + }, + { + "", + "", + } + }, + 1, + 1, + { /* recipient outputs */ + { + {0xa8,0x5e,0xf8,0x70,0x13,0x94,0xb5,0x17,0xa4,0xb3,0x52,0x17,0xc4,0xbd,0x37,0xac,0x01,0xeb,0xee,0xd4,0xb0,0x08,0xf8,0xd0,0x87,0x9f,0x9e,0x09,0xba,0x95,0x31,0x9c}, + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + }, + /* recipient data (scan and spend seckeys) */ + {0x0f,0x69,0x4e,0x06,0x80,0x28,0xa7,0x17,0xf8,0xaf,0x6b,0x94,0x11,0xf9,0xa1,0x33,0xdd,0x35,0x65,0x25,0x87,0x14,0xcc,0x22,0x65,0x94,0xb3,0x4d,0xb9,0x0c,0x1f,0x2c}, + {0x9d,0x6a,0xd8,0x55,0xce,0x34,0x17,0xef,0x84,0xe8,0x36,0x89,0x2e,0x5a,0x56,0x39,0x2b,0xfb,0xa0,0x5f,0xa5,0xd9,0x7c,0xce,0xa3,0x0e,0x26,0x6f,0x54,0x0e,0x08,0xb3}, + 1, + { /* outputs to scan */ + {0xa8,0x5e,0xf8,0x70,0x13,0x94,0xb5,0x17,0xa4,0xb3,0x52,0x17,0xc4,0xbd,0x37,0xac,0x01,0xeb,0xee,0xd4,0xb0,0x08,0xf8,0xd0,0x87,0x9f,0x9e,0x09,0xba,0x95,0x31,0x9c}, + "", + "", + "" + }, + 0, {0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}, /* labels */ + /* expected output data (pubkeys and seckey tweaks) */ + 1, + { + {0xa8,0x5e,0xf8,0x70,0x13,0x94,0xb5,0x17,0xa4,0xb3,0x52,0x17,0xc4,0xbd,0x37,0xac,0x01,0xeb,0xee,0xd4,0xb0,0x08,0xf8,0xd0,0x87,0x9f,0x9e,0x09,0xba,0x95,0x31,0x9c}, + "", + "", + "" + }, + { + {0xc8,0xac,0x02,0x92,0x99,0x7b,0x5b,0xca,0x98,0xb3,0xeb,0xd9,0x9a,0x57,0xe2,0x53,0x07,0x11,0x37,0x55,0x0f,0x27,0x04,0x52,0xcd,0x3d,0xf8,0xa3,0xe2,0x26,0x6d,0x36}, + "", + "", + "" + }, + { + {0xc0,0x36,0xee,0x38,0xbf,0xe4,0x6a,0xba,0x03,0x23,0x43,0x39,0xae,0x72,0x19,0xb3,0x1b,0x82,0x4b,0x52,0xef,0x9d,0x5c,0xe0,0x58,0x10,0xa0,0xd6,0xf6,0x23,0x30,0xde,0xdc,0x2b,0x55,0x65,0x25,0x78,0xaa,0x5b,0xda,0xbf,0x93,0x0f,0xae,0x94,0x1a,0xcd,0x83,0x9d,0x5a,0x66,0xf8,0xfc,0xe7,0xca,0xa9,0x71,0x0c,0xcb,0x44,0x6b,0xdd,0xd1}, + "", + "", + "" + } + }, + + /* ----- Single recipient: multiple UTXOs from the same public key (6) ----- */ + { + 2, + { /* input plain seckeys */ + {0xea,0xdc,0x78,0x16,0x5f,0xf1,0xf8,0xea,0x94,0xad,0x7c,0xfd,0xc5,0x49,0x90,0x73,0x8a,0x4c,0x53,0xf6,0xe0,0x50,0x7b,0x42,0x15,0x42,0x01,0xb8,0xe5,0xdf,0xf3,0xb1}, + {0xea,0xdc,0x78,0x16,0x5f,0xf1,0xf8,0xea,0x94,0xad,0x7c,0xfd,0xc5,0x49,0x90,0x73,0x8a,0x4c,0x53,0xf6,0xe0,0x50,0x7b,0x42,0x15,0x42,0x01,0xb8,0xe5,0xdf,0xf3,0xb1}, + "" + }, + { /* input plain pubkeys */ + {0x02,0x5a,0x1e,0x61,0xf8,0x98,0x17,0x30,0x40,0xe2,0x06,0x16,0xd4,0x3e,0x9f,0x49,0x6f,0xba,0x90,0x33,0x8a,0x39,0xfa,0xa1,0xed,0x98,0xfc,0xba,0xee,0xe4,0xdd,0x9b,0xe5}, + {0x02,0x5a,0x1e,0x61,0xf8,0x98,0x17,0x30,0x40,0xe2,0x06,0x16,0xd4,0x3e,0x9f,0x49,0x6f,0xba,0x90,0x33,0x8a,0x39,0xfa,0xa1,0xed,0x98,0xfc,0xba,0xee,0xe4,0xdd,0x9b,0xe5}, + "" + }, + 0, + { /* input taproot seckeys */ + "", + "", + "" + }, + { /* input x-only pubkeys */ + "", + "", + "" + }, + /* smallest outpoint */ + {0x16,0x9e,0x1e,0x83,0xe9,0x30,0x85,0x33,0x91,0xbc,0x6f,0x35,0xf6,0x05,0xc6,0x75,0x4c,0xfe,0xad,0x57,0xcf,0x83,0x87,0x63,0x9d,0x3b,0x40,0x96,0xc5,0x4f,0x18,0xf4,0x00,0x00,0x00,0x00}, + 1, + { /* recipient pubkeys (address data) */ + { + {0x02,0x20,0xbc,0xfa,0xc5,0xb9,0x9e,0x04,0xad,0x1a,0x06,0xdd,0xfb,0x01,0x6e,0xe1,0x35,0x82,0x60,0x9d,0x60,0xb6,0x29,0x1e,0x98,0xd0,0x1a,0x9b,0xc9,0xa1,0x6c,0x96,0xd4}, + {0x02,0x5c,0xc9,0x85,0x6d,0x6f,0x83,0x75,0x35,0x0e,0x12,0x39,0x78,0xda,0xac,0x20,0x0c,0x26,0x0c,0xb5,0xb5,0xae,0x83,0x10,0x6c,0xab,0x90,0x48,0x4d,0xcd,0x8f,0xcf,0x36}, + }, + { + "", + "", + }, + { + "", + "", + }, + { + "", + "", + } + }, + 1, + 1, + { /* recipient outputs */ + { + {0x54,0x8a,0xe5,0x5c,0x8e,0xec,0x1e,0x73,0x6e,0x8d,0x3e,0x52,0x0f,0x01,0x1f,0x1f,0x42,0xa5,0x6d,0x16,0x61,0x16,0xad,0x21,0x0b,0x39,0x37,0x59,0x9f,0x87,0xf5,0x66}, + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + }, + /* recipient data (scan and spend seckeys) */ + {0x0f,0x69,0x4e,0x06,0x80,0x28,0xa7,0x17,0xf8,0xaf,0x6b,0x94,0x11,0xf9,0xa1,0x33,0xdd,0x35,0x65,0x25,0x87,0x14,0xcc,0x22,0x65,0x94,0xb3,0x4d,0xb9,0x0c,0x1f,0x2c}, + {0x9d,0x6a,0xd8,0x55,0xce,0x34,0x17,0xef,0x84,0xe8,0x36,0x89,0x2e,0x5a,0x56,0x39,0x2b,0xfb,0xa0,0x5f,0xa5,0xd9,0x7c,0xce,0xa3,0x0e,0x26,0x6f,0x54,0x0e,0x08,0xb3}, + 1, + { /* outputs to scan */ + {0x54,0x8a,0xe5,0x5c,0x8e,0xec,0x1e,0x73,0x6e,0x8d,0x3e,0x52,0x0f,0x01,0x1f,0x1f,0x42,0xa5,0x6d,0x16,0x61,0x16,0xad,0x21,0x0b,0x39,0x37,0x59,0x9f,0x87,0xf5,0x66}, + "", + "", + "" + }, + 0, {0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}, /* labels */ + /* expected output data (pubkeys and seckey tweaks) */ + 1, + { + {0x54,0x8a,0xe5,0x5c,0x8e,0xec,0x1e,0x73,0x6e,0x8d,0x3e,0x52,0x0f,0x01,0x1f,0x1f,0x42,0xa5,0x6d,0x16,0x61,0x16,0xad,0x21,0x0b,0x39,0x37,0x59,0x9f,0x87,0xf5,0x66}, + "", + "", + "" + }, + { + {0xf0,0x32,0x69,0x5e,0x26,0x36,0x61,0x9e,0xfa,0x52,0x3f,0xff,0xaa,0x9e,0xf9,0x3c,0x88,0x02,0x29,0x91,0x81,0xfd,0x04,0x61,0x91,0x3c,0x1b,0x8d,0xaf,0x97,0x84,0xcd}, + "", + "", + "" + }, + { + {0xf2,0x38,0x38,0x6c,0x5d,0x5e,0x54,0x44,0xf8,0xd2,0xc7,0x5a,0xab,0xbc,0xb2,0x8c,0x34,0x6f,0x20,0x8c,0x76,0xf6,0x08,0x23,0xf5,0xde,0x3b,0x67,0xb7,0x9e,0x0e,0xc7,0x2e,0xa5,0xde,0x2d,0x7c,0xae,0xc3,0x14,0xe0,0x97,0x1d,0x34,0x54,0xf1,0x22,0xdd,0xa3,0x42,0xb3,0xee,0xde,0x01,0xb3,0x85,0x7e,0x83,0x65,0x4e,0x36,0xb2,0x5f,0x76}, + "", + "", + "" + } + }, + + /* ----- Single recipient: taproot only inputs with even y-values (7) ----- */ + { + 0, + { /* input plain seckeys */ + "", + "", + "" + }, + { /* input plain pubkeys */ + "", + "", + "" + }, + 2, + { /* input taproot seckeys */ + {0xea,0xdc,0x78,0x16,0x5f,0xf1,0xf8,0xea,0x94,0xad,0x7c,0xfd,0xc5,0x49,0x90,0x73,0x8a,0x4c,0x53,0xf6,0xe0,0x50,0x7b,0x42,0x15,0x42,0x01,0xb8,0xe5,0xdf,0xf3,0xb1}, + {0xfc,0x87,0x16,0xa9,0x7a,0x48,0xba,0x9a,0x05,0xa9,0x8a,0xe4,0x7b,0x5c,0xd2,0x01,0xa2,0x5a,0x7f,0xd5,0xd8,0xb7,0x3c,0x20,0x3c,0x5f,0x7b,0x6b,0x6b,0x3b,0x6a,0xd7}, + "" + }, + { /* input x-only pubkeys */ + {0x5a,0x1e,0x61,0xf8,0x98,0x17,0x30,0x40,0xe2,0x06,0x16,0xd4,0x3e,0x9f,0x49,0x6f,0xba,0x90,0x33,0x8a,0x39,0xfa,0xa1,0xed,0x98,0xfc,0xba,0xee,0xe4,0xdd,0x9b,0xe5}, + {0x78,0x2e,0xeb,0x91,0x34,0x31,0xca,0x6e,0x9b,0x8c,0x2f,0xd8,0x0a,0x5f,0x72,0xed,0x20,0x24,0xef,0x72,0xa3,0xc6,0xfb,0x10,0x26,0x3c,0x37,0x99,0x37,0x32,0x33,0x38}, + "" + }, + /* smallest outpoint */ + {0x16,0x9e,0x1e,0x83,0xe9,0x30,0x85,0x33,0x91,0xbc,0x6f,0x35,0xf6,0x05,0xc6,0x75,0x4c,0xfe,0xad,0x57,0xcf,0x83,0x87,0x63,0x9d,0x3b,0x40,0x96,0xc5,0x4f,0x18,0xf4,0x00,0x00,0x00,0x00}, + 1, + { /* recipient pubkeys (address data) */ + { + {0x02,0x20,0xbc,0xfa,0xc5,0xb9,0x9e,0x04,0xad,0x1a,0x06,0xdd,0xfb,0x01,0x6e,0xe1,0x35,0x82,0x60,0x9d,0x60,0xb6,0x29,0x1e,0x98,0xd0,0x1a,0x9b,0xc9,0xa1,0x6c,0x96,0xd4}, + {0x02,0x5c,0xc9,0x85,0x6d,0x6f,0x83,0x75,0x35,0x0e,0x12,0x39,0x78,0xda,0xac,0x20,0x0c,0x26,0x0c,0xb5,0xb5,0xae,0x83,0x10,0x6c,0xab,0x90,0x48,0x4d,0xcd,0x8f,0xcf,0x36}, + }, + { + "", + "", + }, + { + "", + "", + }, + { + "", + "", + } + }, + 1, + 1, + { /* recipient outputs */ + { + {0xde,0x88,0xbe,0xa8,0xe7,0xff,0xc9,0xce,0x1a,0xf3,0x0d,0x11,0x32,0xf9,0x10,0x32,0x3c,0x50,0x51,0x85,0xae,0xc8,0xea,0xe3,0x61,0x67,0x04,0x21,0xe7,0x49,0xa1,0xfb}, + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + }, + /* recipient data (scan and spend seckeys) */ + {0x0f,0x69,0x4e,0x06,0x80,0x28,0xa7,0x17,0xf8,0xaf,0x6b,0x94,0x11,0xf9,0xa1,0x33,0xdd,0x35,0x65,0x25,0x87,0x14,0xcc,0x22,0x65,0x94,0xb3,0x4d,0xb9,0x0c,0x1f,0x2c}, + {0x9d,0x6a,0xd8,0x55,0xce,0x34,0x17,0xef,0x84,0xe8,0x36,0x89,0x2e,0x5a,0x56,0x39,0x2b,0xfb,0xa0,0x5f,0xa5,0xd9,0x7c,0xce,0xa3,0x0e,0x26,0x6f,0x54,0x0e,0x08,0xb3}, + 1, + { /* outputs to scan */ + {0xde,0x88,0xbe,0xa8,0xe7,0xff,0xc9,0xce,0x1a,0xf3,0x0d,0x11,0x32,0xf9,0x10,0x32,0x3c,0x50,0x51,0x85,0xae,0xc8,0xea,0xe3,0x61,0x67,0x04,0x21,0xe7,0x49,0xa1,0xfb}, + "", + "", + "" + }, + 0, {0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}, /* labels */ + /* expected output data (pubkeys and seckey tweaks) */ + 1, + { + {0xde,0x88,0xbe,0xa8,0xe7,0xff,0xc9,0xce,0x1a,0xf3,0x0d,0x11,0x32,0xf9,0x10,0x32,0x3c,0x50,0x51,0x85,0xae,0xc8,0xea,0xe3,0x61,0x67,0x04,0x21,0xe7,0x49,0xa1,0xfb}, + "", + "", + "" + }, + { + {0x3f,0xb9,0xce,0x5c,0xe1,0x74,0x6c,0xed,0x10,0x3c,0x8e,0xd2,0x54,0xe8,0x1f,0x66,0x90,0x76,0x46,0x37,0xdd,0xbc,0x87,0x6e,0xc1,0xf9,0xb3,0xdd,0xab,0x77,0x6b,0x03}, + "", + "", + "" + }, + { + {0xc5,0xac,0xd2,0x5a,0x8f,0x02,0x1a,0x41,0x92,0xf9,0x3b,0xc3,0x44,0x03,0xfd,0x8b,0x76,0x48,0x46,0x13,0x46,0x63,0x36,0xfb,0x25,0x9c,0x72,0xd0,0x4c,0x16,0x98,0x24,0xf2,0x69,0x0c,0xa3,0x4e,0x96,0xce,0xe8,0x6b,0x69,0xf3,0x76,0xc8,0x37,0x70,0x03,0x26,0x8f,0xda,0x56,0xfe,0xeb,0x1b,0x87,0x3e,0x57,0x83,0xd7,0xe1,0x9b,0xcc,0xa5}, + "", + "", + "" + } + }, + + /* ----- Single recipient: taproot only with mixed even/odd y-values (8) ----- */ + { + 0, + { /* input plain seckeys */ + "", + "", + "" + }, + { /* input plain pubkeys */ + "", + "", + "" + }, + 2, + { /* input taproot seckeys */ + {0xea,0xdc,0x78,0x16,0x5f,0xf1,0xf8,0xea,0x94,0xad,0x7c,0xfd,0xc5,0x49,0x90,0x73,0x8a,0x4c,0x53,0xf6,0xe0,0x50,0x7b,0x42,0x15,0x42,0x01,0xb8,0xe5,0xdf,0xf3,0xb1}, + {0x1d,0x37,0x78,0x7c,0x2b,0x71,0x16,0xee,0x98,0x3e,0x9f,0x9c,0x13,0x26,0x9d,0xf2,0x90,0x91,0xb3,0x91,0xc0,0x4d,0xb9,0x42,0x39,0xe0,0xd2,0xbc,0x21,0x82,0xc3,0xbf}, + "" + }, + { /* input x-only pubkeys */ + {0x5a,0x1e,0x61,0xf8,0x98,0x17,0x30,0x40,0xe2,0x06,0x16,0xd4,0x3e,0x9f,0x49,0x6f,0xba,0x90,0x33,0x8a,0x39,0xfa,0xa1,0xed,0x98,0xfc,0xba,0xee,0xe4,0xdd,0x9b,0xe5}, + {0x8c,0x8d,0x23,0xd4,0x76,0x4f,0xef,0xfc,0xd5,0xe7,0x2e,0x38,0x08,0x02,0x54,0x0f,0xa0,0xf8,0x8e,0x3d,0x62,0xad,0x5e,0x0b,0x47,0x95,0x5f,0x74,0xd7,0xb2,0x83,0xc4}, + "" + }, + /* smallest outpoint */ + {0x16,0x9e,0x1e,0x83,0xe9,0x30,0x85,0x33,0x91,0xbc,0x6f,0x35,0xf6,0x05,0xc6,0x75,0x4c,0xfe,0xad,0x57,0xcf,0x83,0x87,0x63,0x9d,0x3b,0x40,0x96,0xc5,0x4f,0x18,0xf4,0x00,0x00,0x00,0x00}, + 1, + { /* recipient pubkeys (address data) */ + { + {0x02,0x20,0xbc,0xfa,0xc5,0xb9,0x9e,0x04,0xad,0x1a,0x06,0xdd,0xfb,0x01,0x6e,0xe1,0x35,0x82,0x60,0x9d,0x60,0xb6,0x29,0x1e,0x98,0xd0,0x1a,0x9b,0xc9,0xa1,0x6c,0x96,0xd4}, + {0x02,0x5c,0xc9,0x85,0x6d,0x6f,0x83,0x75,0x35,0x0e,0x12,0x39,0x78,0xda,0xac,0x20,0x0c,0x26,0x0c,0xb5,0xb5,0xae,0x83,0x10,0x6c,0xab,0x90,0x48,0x4d,0xcd,0x8f,0xcf,0x36}, + }, + { + "", + "", + }, + { + "", + "", + }, + { + "", + "", + } + }, + 1, + 1, + { /* recipient outputs */ + { + {0x77,0xca,0xb7,0xdd,0x12,0xb1,0x02,0x59,0xee,0x82,0xc6,0xea,0x4b,0x50,0x97,0x74,0xe3,0x3e,0x70,0x78,0xe7,0x13,0x8f,0x56,0x80,0x92,0x24,0x1b,0xf2,0x6b,0x99,0xf1}, + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + }, + /* recipient data (scan and spend seckeys) */ + {0x0f,0x69,0x4e,0x06,0x80,0x28,0xa7,0x17,0xf8,0xaf,0x6b,0x94,0x11,0xf9,0xa1,0x33,0xdd,0x35,0x65,0x25,0x87,0x14,0xcc,0x22,0x65,0x94,0xb3,0x4d,0xb9,0x0c,0x1f,0x2c}, + {0x9d,0x6a,0xd8,0x55,0xce,0x34,0x17,0xef,0x84,0xe8,0x36,0x89,0x2e,0x5a,0x56,0x39,0x2b,0xfb,0xa0,0x5f,0xa5,0xd9,0x7c,0xce,0xa3,0x0e,0x26,0x6f,0x54,0x0e,0x08,0xb3}, + 1, + { /* outputs to scan */ + {0x77,0xca,0xb7,0xdd,0x12,0xb1,0x02,0x59,0xee,0x82,0xc6,0xea,0x4b,0x50,0x97,0x74,0xe3,0x3e,0x70,0x78,0xe7,0x13,0x8f,0x56,0x80,0x92,0x24,0x1b,0xf2,0x6b,0x99,0xf1}, + "", + "", + "" + }, + 0, {0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}, /* labels */ + /* expected output data (pubkeys and seckey tweaks) */ + 1, + { + {0x77,0xca,0xb7,0xdd,0x12,0xb1,0x02,0x59,0xee,0x82,0xc6,0xea,0x4b,0x50,0x97,0x74,0xe3,0x3e,0x70,0x78,0xe7,0x13,0x8f,0x56,0x80,0x92,0x24,0x1b,0xf2,0x6b,0x99,0xf1}, + "", + "", + "" + }, + { + {0xf5,0x38,0x25,0x08,0x60,0x97,0x71,0x06,0x8e,0xd0,0x79,0xb2,0x4e,0x1f,0x72,0xe4,0xa1,0x7e,0xe6,0xd1,0xc9,0x79,0x06,0x6b,0xf1,0xd4,0xe2,0xa5,0x67,0x6f,0x09,0xd4}, + "", + "", + "" + }, + { + {0xff,0x65,0x83,0x3b,0x8f,0xd1,0xed,0x3e,0xf9,0xd0,0x44,0x3b,0x4f,0x70,0x2b,0x45,0xa3,0xf2,0xdd,0x45,0x7b,0xa2,0x47,0x68,0x7e,0x82,0x07,0x74,0x5c,0x3b,0xe9,0xd2,0xbd,0xad,0x0a,0xb3,0xf0,0x71,0x18,0xf8,0xb2,0xef,0xc6,0xa0,0x4b,0x95,0xf7,0xb3,0xe2,0x18,0xda,0xf8,0xa6,0x41,0x37,0xec,0x91,0xbd,0x2f,0xc6,0x7f,0xc1,0x37,0xa5}, + "", + "", + "" + } + }, + + /* ----- Single recipient: taproot input with even y-value and non-taproot input (9) ----- */ + { + 1, + { /* input plain seckeys */ + {0x8d,0x47,0x51,0xf6,0xe8,0xa3,0x58,0x68,0x80,0xfb,0x66,0xc1,0x9a,0xe2,0x77,0x96,0x9b,0xd5,0xaa,0x06,0xf6,0x1c,0x4e,0xe2,0xf1,0xe2,0x48,0x6e,0xfd,0xf6,0x66,0xd3}, + "", + "" + }, + { /* input plain pubkeys */ + {0x03,0xe0,0xec,0x4f,0x64,0xb3,0xfa,0x2e,0x46,0x3c,0xcf,0xcf,0x4e,0x85,0x6e,0x37,0xd5,0xe1,0xe2,0x02,0x75,0xbc,0x89,0xec,0x1d,0xef,0x9e,0xb0,0x98,0xef,0xf1,0xf8,0x5d}, + "", + "" + }, + 1, + { /* input taproot seckeys */ + {0xea,0xdc,0x78,0x16,0x5f,0xf1,0xf8,0xea,0x94,0xad,0x7c,0xfd,0xc5,0x49,0x90,0x73,0x8a,0x4c,0x53,0xf6,0xe0,0x50,0x7b,0x42,0x15,0x42,0x01,0xb8,0xe5,0xdf,0xf3,0xb1}, + "", + "" + }, + { /* input x-only pubkeys */ + {0x5a,0x1e,0x61,0xf8,0x98,0x17,0x30,0x40,0xe2,0x06,0x16,0xd4,0x3e,0x9f,0x49,0x6f,0xba,0x90,0x33,0x8a,0x39,0xfa,0xa1,0xed,0x98,0xfc,0xba,0xee,0xe4,0xdd,0x9b,0xe5}, + "", + "" + }, + /* smallest outpoint */ + {0x16,0x9e,0x1e,0x83,0xe9,0x30,0x85,0x33,0x91,0xbc,0x6f,0x35,0xf6,0x05,0xc6,0x75,0x4c,0xfe,0xad,0x57,0xcf,0x83,0x87,0x63,0x9d,0x3b,0x40,0x96,0xc5,0x4f,0x18,0xf4,0x00,0x00,0x00,0x00}, + 1, + { /* recipient pubkeys (address data) */ + { + {0x02,0x20,0xbc,0xfa,0xc5,0xb9,0x9e,0x04,0xad,0x1a,0x06,0xdd,0xfb,0x01,0x6e,0xe1,0x35,0x82,0x60,0x9d,0x60,0xb6,0x29,0x1e,0x98,0xd0,0x1a,0x9b,0xc9,0xa1,0x6c,0x96,0xd4}, + {0x02,0x5c,0xc9,0x85,0x6d,0x6f,0x83,0x75,0x35,0x0e,0x12,0x39,0x78,0xda,0xac,0x20,0x0c,0x26,0x0c,0xb5,0xb5,0xae,0x83,0x10,0x6c,0xab,0x90,0x48,0x4d,0xcd,0x8f,0xcf,0x36}, + }, + { + "", + "", + }, + { + "", + "", + }, + { + "", + "", + } + }, + 1, + 1, + { /* recipient outputs */ + { + {0x30,0x52,0x3c,0xca,0x96,0xb2,0xa9,0xae,0x3c,0x98,0xbe,0xb5,0xe6,0x0f,0x7d,0x19,0x0e,0xc5,0xbc,0x79,0xb2,0xd1,0x1a,0x0b,0x2d,0x4d,0x09,0xa6,0x08,0xc4,0x48,0xf0}, + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + }, + /* recipient data (scan and spend seckeys) */ + {0x0f,0x69,0x4e,0x06,0x80,0x28,0xa7,0x17,0xf8,0xaf,0x6b,0x94,0x11,0xf9,0xa1,0x33,0xdd,0x35,0x65,0x25,0x87,0x14,0xcc,0x22,0x65,0x94,0xb3,0x4d,0xb9,0x0c,0x1f,0x2c}, + {0x9d,0x6a,0xd8,0x55,0xce,0x34,0x17,0xef,0x84,0xe8,0x36,0x89,0x2e,0x5a,0x56,0x39,0x2b,0xfb,0xa0,0x5f,0xa5,0xd9,0x7c,0xce,0xa3,0x0e,0x26,0x6f,0x54,0x0e,0x08,0xb3}, + 1, + { /* outputs to scan */ + {0x30,0x52,0x3c,0xca,0x96,0xb2,0xa9,0xae,0x3c,0x98,0xbe,0xb5,0xe6,0x0f,0x7d,0x19,0x0e,0xc5,0xbc,0x79,0xb2,0xd1,0x1a,0x0b,0x2d,0x4d,0x09,0xa6,0x08,0xc4,0x48,0xf0}, + "", + "", + "" + }, + 0, {0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}, /* labels */ + /* expected output data (pubkeys and seckey tweaks) */ + 1, + { + {0x30,0x52,0x3c,0xca,0x96,0xb2,0xa9,0xae,0x3c,0x98,0xbe,0xb5,0xe6,0x0f,0x7d,0x19,0x0e,0xc5,0xbc,0x79,0xb2,0xd1,0x1a,0x0b,0x2d,0x4d,0x09,0xa6,0x08,0xc4,0x48,0xf0}, + "", + "", + "" + }, + { + {0xb4,0x00,0x17,0x86,0x5c,0x79,0xb1,0xfc,0xbe,0xd6,0x88,0x96,0x79,0x1b,0xe9,0x31,0x86,0xd0,0x8f,0x47,0xe4,0x16,0xb2,0x89,0xb8,0xc0,0x63,0x77,0x7e,0x14,0xe8,0xdf}, + "", + "", + "" + }, + { + {0xd1,0xed,0xee,0xa2,0x8c,0xf1,0x03,0x3b,0xcb,0x3d,0x89,0x37,0x6c,0xab,0xaa,0xaa,0x28,0x86,0xcb,0xd8,0xfd,0xa1,0x12,0xb5,0xc6,0x1c,0xc9,0x0a,0x4e,0x7f,0x18,0x78,0xbd,0xd6,0x21,0x80,0xb0,0x7d,0x1d,0xfc,0x8f,0xfe,0xe1,0x86,0x3c,0x52,0x5a,0x0c,0x7b,0x5b,0xcd,0x41,0x31,0x83,0x28,0x2c,0xfd,0xa7,0x56,0xcb,0x65,0x78,0x72,0x66}, + "", + "", + "" + } + }, + + /* ----- Single recipient: taproot input with odd y-value and non-taproot input (10) ----- */ + { + 1, + { /* input plain seckeys */ + {0x8d,0x47,0x51,0xf6,0xe8,0xa3,0x58,0x68,0x80,0xfb,0x66,0xc1,0x9a,0xe2,0x77,0x96,0x9b,0xd5,0xaa,0x06,0xf6,0x1c,0x4e,0xe2,0xf1,0xe2,0x48,0x6e,0xfd,0xf6,0x66,0xd3}, + "", + "" + }, + { /* input plain pubkeys */ + {0x03,0xe0,0xec,0x4f,0x64,0xb3,0xfa,0x2e,0x46,0x3c,0xcf,0xcf,0x4e,0x85,0x6e,0x37,0xd5,0xe1,0xe2,0x02,0x75,0xbc,0x89,0xec,0x1d,0xef,0x9e,0xb0,0x98,0xef,0xf1,0xf8,0x5d}, + "", + "" + }, + 1, + { /* input taproot seckeys */ + {0x1d,0x37,0x78,0x7c,0x2b,0x71,0x16,0xee,0x98,0x3e,0x9f,0x9c,0x13,0x26,0x9d,0xf2,0x90,0x91,0xb3,0x91,0xc0,0x4d,0xb9,0x42,0x39,0xe0,0xd2,0xbc,0x21,0x82,0xc3,0xbf}, + "", + "" + }, + { /* input x-only pubkeys */ + {0x8c,0x8d,0x23,0xd4,0x76,0x4f,0xef,0xfc,0xd5,0xe7,0x2e,0x38,0x08,0x02,0x54,0x0f,0xa0,0xf8,0x8e,0x3d,0x62,0xad,0x5e,0x0b,0x47,0x95,0x5f,0x74,0xd7,0xb2,0x83,0xc4}, + "", + "" + }, + /* smallest outpoint */ + {0x16,0x9e,0x1e,0x83,0xe9,0x30,0x85,0x33,0x91,0xbc,0x6f,0x35,0xf6,0x05,0xc6,0x75,0x4c,0xfe,0xad,0x57,0xcf,0x83,0x87,0x63,0x9d,0x3b,0x40,0x96,0xc5,0x4f,0x18,0xf4,0x00,0x00,0x00,0x00}, + 1, + { /* recipient pubkeys (address data) */ + { + {0x02,0x20,0xbc,0xfa,0xc5,0xb9,0x9e,0x04,0xad,0x1a,0x06,0xdd,0xfb,0x01,0x6e,0xe1,0x35,0x82,0x60,0x9d,0x60,0xb6,0x29,0x1e,0x98,0xd0,0x1a,0x9b,0xc9,0xa1,0x6c,0x96,0xd4}, + {0x02,0x5c,0xc9,0x85,0x6d,0x6f,0x83,0x75,0x35,0x0e,0x12,0x39,0x78,0xda,0xac,0x20,0x0c,0x26,0x0c,0xb5,0xb5,0xae,0x83,0x10,0x6c,0xab,0x90,0x48,0x4d,0xcd,0x8f,0xcf,0x36}, + }, + { + "", + "", + }, + { + "", + "", + }, + { + "", + "", + } + }, + 1, + 1, + { /* recipient outputs */ + { + {0x35,0x93,0x58,0xf5,0x9e,0xe9,0xe9,0xee,0xc3,0xf0,0x0b,0xdf,0x48,0x82,0x57,0x0f,0xd5,0xc1,0x82,0xe4,0x51,0xaa,0x26,0x50,0xb7,0x88,0x54,0x4a,0xff,0x01,0x2a,0x3a}, + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + }, + /* recipient data (scan and spend seckeys) */ + {0x0f,0x69,0x4e,0x06,0x80,0x28,0xa7,0x17,0xf8,0xaf,0x6b,0x94,0x11,0xf9,0xa1,0x33,0xdd,0x35,0x65,0x25,0x87,0x14,0xcc,0x22,0x65,0x94,0xb3,0x4d,0xb9,0x0c,0x1f,0x2c}, + {0x9d,0x6a,0xd8,0x55,0xce,0x34,0x17,0xef,0x84,0xe8,0x36,0x89,0x2e,0x5a,0x56,0x39,0x2b,0xfb,0xa0,0x5f,0xa5,0xd9,0x7c,0xce,0xa3,0x0e,0x26,0x6f,0x54,0x0e,0x08,0xb3}, + 1, + { /* outputs to scan */ + {0x35,0x93,0x58,0xf5,0x9e,0xe9,0xe9,0xee,0xc3,0xf0,0x0b,0xdf,0x48,0x82,0x57,0x0f,0xd5,0xc1,0x82,0xe4,0x51,0xaa,0x26,0x50,0xb7,0x88,0x54,0x4a,0xff,0x01,0x2a,0x3a}, + "", + "", + "" + }, + 0, {0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}, /* labels */ + /* expected output data (pubkeys and seckey tweaks) */ + 1, + { + {0x35,0x93,0x58,0xf5,0x9e,0xe9,0xe9,0xee,0xc3,0xf0,0x0b,0xdf,0x48,0x82,0x57,0x0f,0xd5,0xc1,0x82,0xe4,0x51,0xaa,0x26,0x50,0xb7,0x88,0x54,0x4a,0xff,0x01,0x2a,0x3a}, + "", + "", + "" + }, + { + {0xa2,0xf9,0xdd,0x05,0xd1,0xd3,0x98,0x34,0x7c,0x88,0x5d,0x9c,0x61,0xa6,0x4d,0x18,0xa2,0x64,0xde,0x6d,0x49,0xce,0xa4,0x32,0x6b,0xaf,0xc2,0x79,0x1d,0x62,0x7f,0xa7}, + "", + "", + "" + }, + { + {0x96,0x03,0x8a,0xd2,0x33,0xd8,0xbe,0xfe,0x34,0x25,0x73,0xa6,0xe5,0x48,0x28,0xd8,0x63,0x47,0x1f,0xb2,0xaf,0xba,0xd5,0x75,0xcc,0x65,0x27,0x1a,0x2a,0x64,0x94,0x80,0xea,0x14,0x91,0x2b,0x6a,0xbb,0xd3,0xfb,0xf9,0x2e,0xfc,0x19,0x28,0xc0,0x36,0xf6,0xe3,0xee,0xf9,0x27,0x10,0x5a,0xf4,0xec,0x1d,0xd5,0x7c,0xb9,0x09,0xf3,0x60,0xb8}, + "", + "", + "" + } + }, + + /* ----- Multiple outputs: multiple outputs, same recipient (11) ----- */ + { + 2, + { /* input plain seckeys */ + {0xea,0xdc,0x78,0x16,0x5f,0xf1,0xf8,0xea,0x94,0xad,0x7c,0xfd,0xc5,0x49,0x90,0x73,0x8a,0x4c,0x53,0xf6,0xe0,0x50,0x7b,0x42,0x15,0x42,0x01,0xb8,0xe5,0xdf,0xf3,0xb1}, + {0x03,0x78,0xe9,0x56,0x85,0xb7,0x45,0x65,0xfa,0x56,0x75,0x1b,0x84,0xa3,0x2d,0xfd,0x18,0x54,0x5d,0x10,0xd6,0x91,0x64,0x1b,0x83,0x72,0xe3,0x21,0x64,0xfa,0xd6,0x6a}, + "" + }, + { /* input plain pubkeys */ + {0x02,0x5a,0x1e,0x61,0xf8,0x98,0x17,0x30,0x40,0xe2,0x06,0x16,0xd4,0x3e,0x9f,0x49,0x6f,0xba,0x90,0x33,0x8a,0x39,0xfa,0xa1,0xed,0x98,0xfc,0xba,0xee,0xe4,0xdd,0x9b,0xe5}, + {0x03,0x78,0x2e,0xeb,0x91,0x34,0x31,0xca,0x6e,0x9b,0x8c,0x2f,0xd8,0x0a,0x5f,0x72,0xed,0x20,0x24,0xef,0x72,0xa3,0xc6,0xfb,0x10,0x26,0x3c,0x37,0x99,0x37,0x32,0x33,0x38}, + "" + }, + 0, + { /* input taproot seckeys */ + "", + "", + "" + }, + { /* input x-only pubkeys */ + "", + "", + "" + }, + /* smallest outpoint */ + {0x16,0x9e,0x1e,0x83,0xe9,0x30,0x85,0x33,0x91,0xbc,0x6f,0x35,0xf6,0x05,0xc6,0x75,0x4c,0xfe,0xad,0x57,0xcf,0x83,0x87,0x63,0x9d,0x3b,0x40,0x96,0xc5,0x4f,0x18,0xf4,0x00,0x00,0x00,0x00}, + 2, + { /* recipient pubkeys (address data) */ + { + {0x02,0x20,0xbc,0xfa,0xc5,0xb9,0x9e,0x04,0xad,0x1a,0x06,0xdd,0xfb,0x01,0x6e,0xe1,0x35,0x82,0x60,0x9d,0x60,0xb6,0x29,0x1e,0x98,0xd0,0x1a,0x9b,0xc9,0xa1,0x6c,0x96,0xd4}, + {0x02,0x5c,0xc9,0x85,0x6d,0x6f,0x83,0x75,0x35,0x0e,0x12,0x39,0x78,0xda,0xac,0x20,0x0c,0x26,0x0c,0xb5,0xb5,0xae,0x83,0x10,0x6c,0xab,0x90,0x48,0x4d,0xcd,0x8f,0xcf,0x36}, + }, + { + {0x02,0x20,0xbc,0xfa,0xc5,0xb9,0x9e,0x04,0xad,0x1a,0x06,0xdd,0xfb,0x01,0x6e,0xe1,0x35,0x82,0x60,0x9d,0x60,0xb6,0x29,0x1e,0x98,0xd0,0x1a,0x9b,0xc9,0xa1,0x6c,0x96,0xd4}, + {0x02,0x5c,0xc9,0x85,0x6d,0x6f,0x83,0x75,0x35,0x0e,0x12,0x39,0x78,0xda,0xac,0x20,0x0c,0x26,0x0c,0xb5,0xb5,0xae,0x83,0x10,0x6c,0xab,0x90,0x48,0x4d,0xcd,0x8f,0xcf,0x36}, + }, + { + "", + "", + }, + { + "", + "", + } + }, + 1, + 2, + { /* recipient outputs */ + { + {0xe9,0x76,0xa5,0x8f,0xbd,0x38,0xae,0xb4,0xe6,0x09,0x3d,0x4d,0xf0,0x2e,0x9c,0x1d,0xe0,0xc4,0x51,0x3a,0xe0,0xc5,0x88,0xce,0xf6,0x8c,0xda,0x5b,0x2f,0x88,0x34,0xca}, + {0xf2,0x07,0x16,0x2b,0x1a,0x7a,0xbc,0x51,0xc4,0x20,0x17,0xbe,0xf0,0x55,0xe9,0xec,0x1e,0xfc,0x3d,0x35,0x67,0xcb,0x72,0x03,0x57,0xe2,0xb8,0x43,0x25,0xdb,0x33,0xac}, + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + }, + /* recipient data (scan and spend seckeys) */ + {0x0f,0x69,0x4e,0x06,0x80,0x28,0xa7,0x17,0xf8,0xaf,0x6b,0x94,0x11,0xf9,0xa1,0x33,0xdd,0x35,0x65,0x25,0x87,0x14,0xcc,0x22,0x65,0x94,0xb3,0x4d,0xb9,0x0c,0x1f,0x2c}, + {0x9d,0x6a,0xd8,0x55,0xce,0x34,0x17,0xef,0x84,0xe8,0x36,0x89,0x2e,0x5a,0x56,0x39,0x2b,0xfb,0xa0,0x5f,0xa5,0xd9,0x7c,0xce,0xa3,0x0e,0x26,0x6f,0x54,0x0e,0x08,0xb3}, + 2, + { /* outputs to scan */ + {0xe9,0x76,0xa5,0x8f,0xbd,0x38,0xae,0xb4,0xe6,0x09,0x3d,0x4d,0xf0,0x2e,0x9c,0x1d,0xe0,0xc4,0x51,0x3a,0xe0,0xc5,0x88,0xce,0xf6,0x8c,0xda,0x5b,0x2f,0x88,0x34,0xca}, + {0xf2,0x07,0x16,0x2b,0x1a,0x7a,0xbc,0x51,0xc4,0x20,0x17,0xbe,0xf0,0x55,0xe9,0xec,0x1e,0xfc,0x3d,0x35,0x67,0xcb,0x72,0x03,0x57,0xe2,0xb8,0x43,0x25,0xdb,0x33,0xac}, + "", + "" + }, + 0, {0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}, /* labels */ + /* expected output data (pubkeys and seckey tweaks) */ + 2, + { + {0xe9,0x76,0xa5,0x8f,0xbd,0x38,0xae,0xb4,0xe6,0x09,0x3d,0x4d,0xf0,0x2e,0x9c,0x1d,0xe0,0xc4,0x51,0x3a,0xe0,0xc5,0x88,0xce,0xf6,0x8c,0xda,0x5b,0x2f,0x88,0x34,0xca}, + {0xf2,0x07,0x16,0x2b,0x1a,0x7a,0xbc,0x51,0xc4,0x20,0x17,0xbe,0xf0,0x55,0xe9,0xec,0x1e,0xfc,0x3d,0x35,0x67,0xcb,0x72,0x03,0x57,0xe2,0xb8,0x43,0x25,0xdb,0x33,0xac}, + "", + "" + }, + { + {0xd9,0x7e,0x44,0x2d,0x11,0x0c,0x0b,0xdd,0x31,0x16,0x1a,0x7b,0xb6,0xe7,0x86,0x2e,0x03,0x8d,0x02,0xa0,0x9b,0x14,0x84,0xdf,0xbb,0x46,0x3f,0x2e,0x0f,0x7c,0x92,0x30}, + {0x33,0xce,0x08,0x5c,0x3c,0x11,0xea,0xad,0x13,0x69,0x4a,0xae,0x3c,0x20,0x30,0x1a,0x6c,0x83,0x38,0x2e,0xc8,0x9a,0x7c,0xde,0x96,0xc6,0x79,0x9e,0x2f,0x88,0x80,0x5a}, + "", + "" + }, + { + {0x29,0xbd,0x25,0xd0,0xf8,0x08,0xd7,0xfc,0xd2,0xaa,0x6d,0x5e,0xd2,0x06,0x05,0x38,0x99,0x19,0x83,0x97,0x50,0x6c,0x30,0x1b,0x21,0x8a,0x9e,0x47,0xa3,0xd7,0x07,0x0a,0xf0,0x3e,0x90,0x3f,0xf7,0x18,0x97,0x8d,0x50,0xd1,0xb6,0xb9,0xaf,0x8c,0xc0,0xe3,0x13,0xd8,0x4e,0xda,0x5d,0x5b,0x1e,0x8e,0x85,0xe5,0x51,0x6d,0x63,0x0b,0xbe,0xb9}, + {0x33,0x56,0x67,0xca,0x6c,0xae,0x7a,0x26,0x43,0x8f,0x5c,0xfd,0xd7,0x3b,0x3d,0x48,0xfa,0x83,0x2f,0xa9,0x76,0x85,0x21,0xd7,0xd5,0x44,0x5f,0x22,0xc2,0x03,0xab,0x0d,0x74,0xed,0x85,0x08,0x8f,0x27,0xd2,0x99,0x59,0xba,0x62,0x7a,0x45,0x09,0x99,0x66,0x76,0xf4,0x7d,0xf8,0xff,0x28,0x4d,0x29,0x25,0x67,0xb1,0xbe,0xef,0x0e,0x39,0x12}, + "", + "" + } + }, + + /* ----- Multiple outputs: multiple outputs, multiple recipients (12) ----- */ + { + 2, + { /* input plain seckeys */ + {0xea,0xdc,0x78,0x16,0x5f,0xf1,0xf8,0xea,0x94,0xad,0x7c,0xfd,0xc5,0x49,0x90,0x73,0x8a,0x4c,0x53,0xf6,0xe0,0x50,0x7b,0x42,0x15,0x42,0x01,0xb8,0xe5,0xdf,0xf3,0xb1}, + {0x03,0x78,0xe9,0x56,0x85,0xb7,0x45,0x65,0xfa,0x56,0x75,0x1b,0x84,0xa3,0x2d,0xfd,0x18,0x54,0x5d,0x10,0xd6,0x91,0x64,0x1b,0x83,0x72,0xe3,0x21,0x64,0xfa,0xd6,0x6a}, + "" + }, + { /* input plain pubkeys */ + {0x02,0x5a,0x1e,0x61,0xf8,0x98,0x17,0x30,0x40,0xe2,0x06,0x16,0xd4,0x3e,0x9f,0x49,0x6f,0xba,0x90,0x33,0x8a,0x39,0xfa,0xa1,0xed,0x98,0xfc,0xba,0xee,0xe4,0xdd,0x9b,0xe5}, + {0x03,0x78,0x2e,0xeb,0x91,0x34,0x31,0xca,0x6e,0x9b,0x8c,0x2f,0xd8,0x0a,0x5f,0x72,0xed,0x20,0x24,0xef,0x72,0xa3,0xc6,0xfb,0x10,0x26,0x3c,0x37,0x99,0x37,0x32,0x33,0x38}, + "" + }, + 0, + { /* input taproot seckeys */ + "", + "", + "" + }, + { /* input x-only pubkeys */ + "", + "", + "" + }, + /* smallest outpoint */ + {0x16,0x9e,0x1e,0x83,0xe9,0x30,0x85,0x33,0x91,0xbc,0x6f,0x35,0xf6,0x05,0xc6,0x75,0x4c,0xfe,0xad,0x57,0xcf,0x83,0x87,0x63,0x9d,0x3b,0x40,0x96,0xc5,0x4f,0x18,0xf4,0x00,0x00,0x00,0x00}, + 3, + { /* recipient pubkeys (address data) */ + { + {0x02,0x20,0xbc,0xfa,0xc5,0xb9,0x9e,0x04,0xad,0x1a,0x06,0xdd,0xfb,0x01,0x6e,0xe1,0x35,0x82,0x60,0x9d,0x60,0xb6,0x29,0x1e,0x98,0xd0,0x1a,0x9b,0xc9,0xa1,0x6c,0x96,0xd4}, + {0x02,0x5c,0xc9,0x85,0x6d,0x6f,0x83,0x75,0x35,0x0e,0x12,0x39,0x78,0xda,0xac,0x20,0x0c,0x26,0x0c,0xb5,0xb5,0xae,0x83,0x10,0x6c,0xab,0x90,0x48,0x4d,0xcd,0x8f,0xcf,0x36}, + }, + { + {0x02,0x06,0x2d,0x49,0xff,0xc0,0x27,0x87,0xd5,0x86,0xc6,0x08,0xdf,0xbe,0xc1,0x84,0xaa,0x91,0xa6,0x59,0x7d,0x97,0xb4,0x63,0xea,0x5c,0x6b,0xab,0xd9,0xd1,0x7a,0x95,0xa3}, + {0x03,0x81,0xeb,0x9a,0x9a,0x9e,0xc7,0x39,0xd5,0x27,0xc1,0x63,0x1b,0x31,0xb4,0x21,0x56,0x6f,0x5c,0x2a,0x47,0xb4,0xab,0x5b,0x1f,0x6a,0x68,0x6d,0xfb,0x68,0xea,0xb7,0x16}, + }, + { + {0x02,0x06,0x2d,0x49,0xff,0xc0,0x27,0x87,0xd5,0x86,0xc6,0x08,0xdf,0xbe,0xc1,0x84,0xaa,0x91,0xa6,0x59,0x7d,0x97,0xb4,0x63,0xea,0x5c,0x6b,0xab,0xd9,0xd1,0x7a,0x95,0xa3}, + {0x03,0x81,0xeb,0x9a,0x9a,0x9e,0xc7,0x39,0xd5,0x27,0xc1,0x63,0x1b,0x31,0xb4,0x21,0x56,0x6f,0x5c,0x2a,0x47,0xb4,0xab,0x5b,0x1f,0x6a,0x68,0x6d,0xfb,0x68,0xea,0xb7,0x16}, + }, + { + "", + "", + } + }, + 1, + 3, + { /* recipient outputs */ + { + {0x2e,0x84,0x7b,0xb0,0x1d,0x1b,0x49,0x1d,0xa5,0x12,0xdd,0xd7,0x60,0xb8,0x50,0x96,0x17,0xee,0x38,0x05,0x70,0x03,0xd6,0x11,0x5d,0x00,0xba,0x56,0x24,0x51,0x32,0x3a}, + {0x84,0x17,0x92,0xc3,0x3c,0x9d,0xc6,0x19,0x3e,0x76,0x74,0x41,0x34,0x12,0x5d,0x40,0xad,0xd8,0xf2,0xf4,0xa9,0x64,0x75,0xf2,0x8b,0xa1,0x50,0xbe,0x03,0x2d,0x64,0xe8}, + {0xf2,0x07,0x16,0x2b,0x1a,0x7a,0xbc,0x51,0xc4,0x20,0x17,0xbe,0xf0,0x55,0xe9,0xec,0x1e,0xfc,0x3d,0x35,0x67,0xcb,0x72,0x03,0x57,0xe2,0xb8,0x43,0x25,0xdb,0x33,0xac}, + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + }, + /* recipient data (scan and spend seckeys) */ + {0x06,0x0b,0x75,0x1d,0x78,0x92,0x14,0x90,0x06,0xed,0x7b,0x98,0x60,0x69,0x55,0xa2,0x9f,0xe2,0x84,0xa1,0xe9,0x00,0x07,0x0c,0x09,0x71,0xf5,0xfb,0x93,0xdb,0xf4,0x22}, + {0x99,0x02,0xc3,0xc5,0x6e,0x84,0x00,0x2a,0x7c,0xd4,0x10,0x11,0x3a,0x9a,0xb2,0x1d,0x14,0x2b,0xe7,0xf5,0x3c,0xf5,0x20,0x07,0x20,0xbb,0x01,0x31,0x4c,0x5e,0xb9,0x20}, + 3, + { /* outputs to scan */ + {0x2e,0x84,0x7b,0xb0,0x1d,0x1b,0x49,0x1d,0xa5,0x12,0xdd,0xd7,0x60,0xb8,0x50,0x96,0x17,0xee,0x38,0x05,0x70,0x03,0xd6,0x11,0x5d,0x00,0xba,0x56,0x24,0x51,0x32,0x3a}, + {0x84,0x17,0x92,0xc3,0x3c,0x9d,0xc6,0x19,0x3e,0x76,0x74,0x41,0x34,0x12,0x5d,0x40,0xad,0xd8,0xf2,0xf4,0xa9,0x64,0x75,0xf2,0x8b,0xa1,0x50,0xbe,0x03,0x2d,0x64,0xe8}, + {0xf2,0x07,0x16,0x2b,0x1a,0x7a,0xbc,0x51,0xc4,0x20,0x17,0xbe,0xf0,0x55,0xe9,0xec,0x1e,0xfc,0x3d,0x35,0x67,0xcb,0x72,0x03,0x57,0xe2,0xb8,0x43,0x25,0xdb,0x33,0xac}, + "" + }, + 0, {0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}, /* labels */ + /* expected output data (pubkeys and seckey tweaks) */ + 2, + { + {0x2e,0x84,0x7b,0xb0,0x1d,0x1b,0x49,0x1d,0xa5,0x12,0xdd,0xd7,0x60,0xb8,0x50,0x96,0x17,0xee,0x38,0x05,0x70,0x03,0xd6,0x11,0x5d,0x00,0xba,0x56,0x24,0x51,0x32,0x3a}, + {0x84,0x17,0x92,0xc3,0x3c,0x9d,0xc6,0x19,0x3e,0x76,0x74,0x41,0x34,0x12,0x5d,0x40,0xad,0xd8,0xf2,0xf4,0xa9,0x64,0x75,0xf2,0x8b,0xa1,0x50,0xbe,0x03,0x2d,0x64,0xe8}, + "", + "" + }, + { + {0x72,0xcd,0x08,0x2c,0xcc,0xb6,0x33,0xbf,0x85,0x24,0x0a,0x83,0x49,0x4b,0x32,0xdc,0x94,0x3a,0x4d,0x05,0x64,0x7a,0x66,0x86,0xd2,0x3a,0xd4,0xca,0x59,0xc0,0xeb,0xe4}, + {0x2f,0x17,0xea,0x87,0x3a,0x00,0x47,0xfc,0x01,0xba,0x80,0x10,0xfe,0xf0,0x96,0x9e,0x76,0xd0,0xe4,0x28,0x3f,0x60,0x0d,0x48,0xf7,0x35,0x09,0x8b,0x1f,0xee,0x6e,0xb9}, + "", + "" + }, + { + {0x38,0x74,0x5f,0x3d,0x9f,0x5e,0xef,0x0b,0x1c,0xfb,0x17,0xca,0x31,0x4e,0xfa,0x8c,0x52,0x1e,0xfa,0xb2,0x8a,0x23,0xaa,0x20,0xec,0x5e,0x3a,0xbb,0x56,0x1d,0x42,0x80,0x4d,0x53,0x99,0x06,0xdc,0xe6,0x0c,0x4e,0xe7,0x97,0x79,0x66,0x18,0x4e,0x6f,0x2c,0xab,0x1f,0xaa,0x0e,0x53,0x77,0xce,0xb7,0x14,0x8e,0xc5,0x21,0x8b,0x4e,0x78,0x78}, + {0xc2,0x6f,0x4e,0x3c,0xf3,0x71,0xb9,0x0b,0x84,0x0f,0x48,0xea,0x0e,0x76,0x1b,0x5e,0xc3,0x18,0x83,0xed,0x55,0x71,0x9f,0x9e,0xf0,0x6a,0x90,0xe2,0x82,0xd8,0x5f,0x56,0x57,0x90,0xab,0x78,0x0a,0x3f,0x49,0x1b,0xc2,0x66,0x8c,0xc6,0x4e,0x94,0x4d,0xca,0x84,0x9d,0x10,0x22,0xa8,0x78,0xcd,0xad,0xb8,0xd1,0x68,0xb8,0xda,0x4a,0x6d,0xa3}, + "", + "" + } + }, + + /* ----- Receiving with labels: label with even parity (13) ----- */ + { + 2, + { /* input plain seckeys */ + {0xea,0xdc,0x78,0x16,0x5f,0xf1,0xf8,0xea,0x94,0xad,0x7c,0xfd,0xc5,0x49,0x90,0x73,0x8a,0x4c,0x53,0xf6,0xe0,0x50,0x7b,0x42,0x15,0x42,0x01,0xb8,0xe5,0xdf,0xf3,0xb1}, + {0x03,0x78,0xe9,0x56,0x85,0xb7,0x45,0x65,0xfa,0x56,0x75,0x1b,0x84,0xa3,0x2d,0xfd,0x18,0x54,0x5d,0x10,0xd6,0x91,0x64,0x1b,0x83,0x72,0xe3,0x21,0x64,0xfa,0xd6,0x6a}, + "" + }, + { /* input plain pubkeys */ + {0x02,0x5a,0x1e,0x61,0xf8,0x98,0x17,0x30,0x40,0xe2,0x06,0x16,0xd4,0x3e,0x9f,0x49,0x6f,0xba,0x90,0x33,0x8a,0x39,0xfa,0xa1,0xed,0x98,0xfc,0xba,0xee,0xe4,0xdd,0x9b,0xe5}, + {0x03,0x78,0x2e,0xeb,0x91,0x34,0x31,0xca,0x6e,0x9b,0x8c,0x2f,0xd8,0x0a,0x5f,0x72,0xed,0x20,0x24,0xef,0x72,0xa3,0xc6,0xfb,0x10,0x26,0x3c,0x37,0x99,0x37,0x32,0x33,0x38}, + "" + }, + 0, + { /* input taproot seckeys */ + "", + "", + "" + }, + { /* input x-only pubkeys */ + "", + "", + "" + }, + /* smallest outpoint */ + {0x16,0x9e,0x1e,0x83,0xe9,0x30,0x85,0x33,0x91,0xbc,0x6f,0x35,0xf6,0x05,0xc6,0x75,0x4c,0xfe,0xad,0x57,0xcf,0x83,0x87,0x63,0x9d,0x3b,0x40,0x96,0xc5,0x4f,0x18,0xf4,0x00,0x00,0x00,0x00}, + 1, + { /* recipient pubkeys (address data) */ + { + {0x02,0x20,0xbc,0xfa,0xc5,0xb9,0x9e,0x04,0xad,0x1a,0x06,0xdd,0xfb,0x01,0x6e,0xe1,0x35,0x82,0x60,0x9d,0x60,0xb6,0x29,0x1e,0x98,0xd0,0x1a,0x9b,0xc9,0xa1,0x6c,0x96,0xd4}, + {0x02,0x59,0x35,0x2a,0xdd,0x83,0x7b,0x66,0x86,0xe8,0xd2,0x2b,0x87,0x01,0x78,0x14,0xa4,0x6b,0x3a,0xd3,0x08,0x70,0x21,0x67,0xc6,0x5b,0xd5,0xc8,0x59,0x9c,0xd2,0x8d,0x1c}, + }, + { + "", + "", + }, + { + "", + "", + }, + { + "", + "", + } + }, + 1, + 1, + { /* recipient outputs */ + { + {0xd0,0x14,0xd4,0x86,0x0f,0x67,0xd6,0x07,0xd6,0x0b,0x1a,0xf7,0x0e,0x0e,0xe2,0x36,0xb9,0x96,0x58,0xb6,0x1b,0xb7,0x69,0x83,0x2a,0xcb,0xbe,0x87,0xc3,0x74,0x43,0x9a}, + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + }, + /* recipient data (scan and spend seckeys) */ + {0x0f,0x69,0x4e,0x06,0x80,0x28,0xa7,0x17,0xf8,0xaf,0x6b,0x94,0x11,0xf9,0xa1,0x33,0xdd,0x35,0x65,0x25,0x87,0x14,0xcc,0x22,0x65,0x94,0xb3,0x4d,0xb9,0x0c,0x1f,0x2c}, + {0x9d,0x6a,0xd8,0x55,0xce,0x34,0x17,0xef,0x84,0xe8,0x36,0x89,0x2e,0x5a,0x56,0x39,0x2b,0xfb,0xa0,0x5f,0xa5,0xd9,0x7c,0xce,0xa3,0x0e,0x26,0x6f,0x54,0x0e,0x08,0xb3}, + 1, + { /* outputs to scan */ + {0xd0,0x14,0xd4,0x86,0x0f,0x67,0xd6,0x07,0xd6,0x0b,0x1a,0xf7,0x0e,0x0e,0xe2,0x36,0xb9,0x96,0x58,0xb6,0x1b,0xb7,0x69,0x83,0x2a,0xcb,0xbe,0x87,0xc3,0x74,0x43,0x9a}, + "", + "", + "" + }, + 3, {2, 3, 1001337, 0xffffffff}, /* labels */ + /* expected output data (pubkeys and seckey tweaks) */ + 1, + { + {0xd0,0x14,0xd4,0x86,0x0f,0x67,0xd6,0x07,0xd6,0x0b,0x1a,0xf7,0x0e,0x0e,0xe2,0x36,0xb9,0x96,0x58,0xb6,0x1b,0xb7,0x69,0x83,0x2a,0xcb,0xbe,0x87,0xc3,0x74,0x43,0x9a}, + "", + "", + "" + }, + { + {0x51,0xd4,0xe9,0xd0,0xd4,0x82,0xb5,0x70,0x01,0x09,0xb4,0xb2,0xe1,0x6f,0xf5,0x08,0x26,0x9b,0x03,0xd8,0x00,0x19,0x2a,0x04,0x3d,0x61,0xdc,0xa4,0xa0,0xa7,0x2a,0x52}, + "", + "", + "" + }, + { + {0xc3,0x0f,0xa6,0x3b,0xad,0x6f,0x0a,0x31,0x7f,0x39,0xa7,0x73,0xa5,0xcb,0xf0,0xb0,0xf8,0x19,0x3c,0x71,0xdf,0xeb,0xba,0x05,0xee,0x6a,0xe4,0xed,0x28,0xe3,0x77,0x5e,0x6e,0x04,0xc3,0xea,0x70,0xa8,0x37,0x03,0xbb,0x88,0x81,0x22,0x85,0x5d,0xc8,0x94,0xca,0xb6,0x16,0x92,0xe7,0xfd,0x10,0xc9,0xb3,0x49,0x4d,0x47,0x9a,0x60,0x78,0x5e}, + "", + "", + "" + } + }, + + /* ----- Receiving with labels: label with odd parity (14) ----- */ + { + 2, + { /* input plain seckeys */ + {0xea,0xdc,0x78,0x16,0x5f,0xf1,0xf8,0xea,0x94,0xad,0x7c,0xfd,0xc5,0x49,0x90,0x73,0x8a,0x4c,0x53,0xf6,0xe0,0x50,0x7b,0x42,0x15,0x42,0x01,0xb8,0xe5,0xdf,0xf3,0xb1}, + {0x03,0x78,0xe9,0x56,0x85,0xb7,0x45,0x65,0xfa,0x56,0x75,0x1b,0x84,0xa3,0x2d,0xfd,0x18,0x54,0x5d,0x10,0xd6,0x91,0x64,0x1b,0x83,0x72,0xe3,0x21,0x64,0xfa,0xd6,0x6a}, + "" + }, + { /* input plain pubkeys */ + {0x02,0x5a,0x1e,0x61,0xf8,0x98,0x17,0x30,0x40,0xe2,0x06,0x16,0xd4,0x3e,0x9f,0x49,0x6f,0xba,0x90,0x33,0x8a,0x39,0xfa,0xa1,0xed,0x98,0xfc,0xba,0xee,0xe4,0xdd,0x9b,0xe5}, + {0x03,0x78,0x2e,0xeb,0x91,0x34,0x31,0xca,0x6e,0x9b,0x8c,0x2f,0xd8,0x0a,0x5f,0x72,0xed,0x20,0x24,0xef,0x72,0xa3,0xc6,0xfb,0x10,0x26,0x3c,0x37,0x99,0x37,0x32,0x33,0x38}, + "" + }, + 0, + { /* input taproot seckeys */ + "", + "", + "" + }, + { /* input x-only pubkeys */ + "", + "", + "" + }, + /* smallest outpoint */ + {0x16,0x9e,0x1e,0x83,0xe9,0x30,0x85,0x33,0x91,0xbc,0x6f,0x35,0xf6,0x05,0xc6,0x75,0x4c,0xfe,0xad,0x57,0xcf,0x83,0x87,0x63,0x9d,0x3b,0x40,0x96,0xc5,0x4f,0x18,0xf4,0x00,0x00,0x00,0x00}, + 1, + { /* recipient pubkeys (address data) */ + { + {0x02,0x20,0xbc,0xfa,0xc5,0xb9,0x9e,0x04,0xad,0x1a,0x06,0xdd,0xfb,0x01,0x6e,0xe1,0x35,0x82,0x60,0x9d,0x60,0xb6,0x29,0x1e,0x98,0xd0,0x1a,0x9b,0xc9,0xa1,0x6c,0x96,0xd4}, + {0x02,0x08,0xa1,0x44,0xa1,0x84,0x33,0xa8,0x3f,0x63,0x3c,0x82,0x2c,0x1b,0xf5,0xee,0x4c,0x8c,0x8e,0x24,0x60,0x1d,0x6c,0xa7,0x5e,0x20,0xa7,0xdc,0x57,0xa0,0xff,0x92,0x80}, + }, + { + "", + "", + }, + { + "", + "", + }, + { + "", + "", + } + }, + 1, + 1, + { /* recipient outputs */ + { + {0x67,0x62,0x6a,0xeb,0xb3,0xc4,0x30,0x7c,0xf0,0xf6,0xc3,0x9c,0xa2,0x32,0x47,0x59,0x8f,0xab,0xf6,0x75,0xab,0x78,0x32,0x92,0xeb,0x2f,0x81,0xae,0x75,0xad,0x1f,0x8c}, + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + }, + /* recipient data (scan and spend seckeys) */ + {0x0f,0x69,0x4e,0x06,0x80,0x28,0xa7,0x17,0xf8,0xaf,0x6b,0x94,0x11,0xf9,0xa1,0x33,0xdd,0x35,0x65,0x25,0x87,0x14,0xcc,0x22,0x65,0x94,0xb3,0x4d,0xb9,0x0c,0x1f,0x2c}, + {0x9d,0x6a,0xd8,0x55,0xce,0x34,0x17,0xef,0x84,0xe8,0x36,0x89,0x2e,0x5a,0x56,0x39,0x2b,0xfb,0xa0,0x5f,0xa5,0xd9,0x7c,0xce,0xa3,0x0e,0x26,0x6f,0x54,0x0e,0x08,0xb3}, + 1, + { /* outputs to scan */ + {0x67,0x62,0x6a,0xeb,0xb3,0xc4,0x30,0x7c,0xf0,0xf6,0xc3,0x9c,0xa2,0x32,0x47,0x59,0x8f,0xab,0xf6,0x75,0xab,0x78,0x32,0x92,0xeb,0x2f,0x81,0xae,0x75,0xad,0x1f,0x8c}, + "", + "", + "" + }, + 3, {2, 3, 1001337, 0xffffffff}, /* labels */ + /* expected output data (pubkeys and seckey tweaks) */ + 1, + { + {0x67,0x62,0x6a,0xeb,0xb3,0xc4,0x30,0x7c,0xf0,0xf6,0xc3,0x9c,0xa2,0x32,0x47,0x59,0x8f,0xab,0xf6,0x75,0xab,0x78,0x32,0x92,0xeb,0x2f,0x81,0xae,0x75,0xad,0x1f,0x8c}, + "", + "", + "" + }, + { + {0x60,0x24,0xae,0x21,0x48,0x76,0x35,0x6b,0x8d,0x91,0x77,0x16,0xe7,0x70,0x7d,0x26,0x7a,0xe1,0x6a,0x0f,0xdb,0x07,0xde,0x2a,0x78,0x6b,0x74,0xa7,0xbb,0xcd,0xde,0xad}, + "", + "", + "" + }, + { + {0xa8,0x6d,0x55,0x4d,0x0d,0x6b,0x7a,0xa0,0x90,0x71,0x55,0xf7,0xe0,0xb4,0x7f,0x01,0x82,0x75,0x24,0x72,0xff,0xfa,0xed,0xdd,0x68,0xda,0x90,0xe9,0x9b,0x94,0x02,0xf1,0x66,0xfd,0x9b,0x33,0x03,0x9c,0x30,0x2c,0x71,0x15,0x09,0x8d,0x97,0x1c,0x13,0x99,0xe6,0x7c,0x19,0xe9,0xe4,0xde,0x18,0x0b,0x10,0xea,0x0b,0x9d,0x6f,0x0d,0xb8,0x32}, + "", + "", + "" + } + }, + + /* ----- Receiving with labels: large label integer (15) ----- */ + { + 2, + { /* input plain seckeys */ + {0xea,0xdc,0x78,0x16,0x5f,0xf1,0xf8,0xea,0x94,0xad,0x7c,0xfd,0xc5,0x49,0x90,0x73,0x8a,0x4c,0x53,0xf6,0xe0,0x50,0x7b,0x42,0x15,0x42,0x01,0xb8,0xe5,0xdf,0xf3,0xb1}, + {0x03,0x78,0xe9,0x56,0x85,0xb7,0x45,0x65,0xfa,0x56,0x75,0x1b,0x84,0xa3,0x2d,0xfd,0x18,0x54,0x5d,0x10,0xd6,0x91,0x64,0x1b,0x83,0x72,0xe3,0x21,0x64,0xfa,0xd6,0x6a}, + "" + }, + { /* input plain pubkeys */ + {0x02,0x5a,0x1e,0x61,0xf8,0x98,0x17,0x30,0x40,0xe2,0x06,0x16,0xd4,0x3e,0x9f,0x49,0x6f,0xba,0x90,0x33,0x8a,0x39,0xfa,0xa1,0xed,0x98,0xfc,0xba,0xee,0xe4,0xdd,0x9b,0xe5}, + {0x03,0x78,0x2e,0xeb,0x91,0x34,0x31,0xca,0x6e,0x9b,0x8c,0x2f,0xd8,0x0a,0x5f,0x72,0xed,0x20,0x24,0xef,0x72,0xa3,0xc6,0xfb,0x10,0x26,0x3c,0x37,0x99,0x37,0x32,0x33,0x38}, + "" + }, + 0, + { /* input taproot seckeys */ + "", + "", + "" + }, + { /* input x-only pubkeys */ + "", + "", + "" + }, + /* smallest outpoint */ + {0x16,0x9e,0x1e,0x83,0xe9,0x30,0x85,0x33,0x91,0xbc,0x6f,0x35,0xf6,0x05,0xc6,0x75,0x4c,0xfe,0xad,0x57,0xcf,0x83,0x87,0x63,0x9d,0x3b,0x40,0x96,0xc5,0x4f,0x18,0xf4,0x00,0x00,0x00,0x00}, + 1, + { /* recipient pubkeys (address data) */ + { + {0x02,0x20,0xbc,0xfa,0xc5,0xb9,0x9e,0x04,0xad,0x1a,0x06,0xdd,0xfb,0x01,0x6e,0xe1,0x35,0x82,0x60,0x9d,0x60,0xb6,0x29,0x1e,0x98,0xd0,0x1a,0x9b,0xc9,0xa1,0x6c,0x96,0xd4}, + {0x03,0xd8,0x50,0x92,0xbb,0xe3,0x46,0x8f,0x68,0x4c,0xe1,0xd8,0xa2,0xa6,0x6e,0xbe,0xc9,0x6a,0x9e,0x6e,0x09,0xe7,0x11,0x07,0x20,0xa5,0xd5,0xfa,0xa4,0xaa,0x78,0x80,0xd0}, + }, + { + "", + "", + }, + { + "", + "", + }, + { + "", + "", + } + }, + 1, + 1, + { /* recipient outputs */ + { + {0x7e,0xfa,0x60,0xce,0x78,0xac,0x34,0x3d,0xf8,0xa0,0x13,0xa2,0x02,0x7c,0x6c,0x5e,0xf2,0x9f,0x95,0x02,0xed,0xcb,0xd7,0x69,0xd2,0xc2,0x17,0x17,0xfe,0xcc,0x59,0x51}, + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + }, + /* recipient data (scan and spend seckeys) */ + {0x0f,0x69,0x4e,0x06,0x80,0x28,0xa7,0x17,0xf8,0xaf,0x6b,0x94,0x11,0xf9,0xa1,0x33,0xdd,0x35,0x65,0x25,0x87,0x14,0xcc,0x22,0x65,0x94,0xb3,0x4d,0xb9,0x0c,0x1f,0x2c}, + {0x9d,0x6a,0xd8,0x55,0xce,0x34,0x17,0xef,0x84,0xe8,0x36,0x89,0x2e,0x5a,0x56,0x39,0x2b,0xfb,0xa0,0x5f,0xa5,0xd9,0x7c,0xce,0xa3,0x0e,0x26,0x6f,0x54,0x0e,0x08,0xb3}, + 1, + { /* outputs to scan */ + {0x7e,0xfa,0x60,0xce,0x78,0xac,0x34,0x3d,0xf8,0xa0,0x13,0xa2,0x02,0x7c,0x6c,0x5e,0xf2,0x9f,0x95,0x02,0xed,0xcb,0xd7,0x69,0xd2,0xc2,0x17,0x17,0xfe,0xcc,0x59,0x51}, + "", + "", + "" + }, + 3, {2, 3, 1001337, 0xffffffff}, /* labels */ + /* expected output data (pubkeys and seckey tweaks) */ + 1, + { + {0x7e,0xfa,0x60,0xce,0x78,0xac,0x34,0x3d,0xf8,0xa0,0x13,0xa2,0x02,0x7c,0x6c,0x5e,0xf2,0x9f,0x95,0x02,0xed,0xcb,0xd7,0x69,0xd2,0xc2,0x17,0x17,0xfe,0xcc,0x59,0x51}, + "", + "", + "" + }, + { + {0xe3,0x36,0xb9,0x23,0x30,0xc3,0x30,0x30,0x28,0x5c,0xe4,0x2e,0x41,0x15,0xad,0x92,0xd5,0x19,0x79,0x13,0xc8,0x8e,0x06,0xb9,0x07,0x2b,0x4a,0x9b,0x47,0xc6,0x64,0xa2}, + "", + "", + "" + }, + { + {0xc9,0xe8,0x0d,0xd3,0xbd,0xd2,0x5c,0xa2,0xd3,0x52,0xce,0x77,0x51,0x0f,0x1a,0xed,0x37,0xba,0x35,0x09,0xdc,0x8c,0xc0,0x67,0x7f,0x2d,0x7c,0x2d,0xd0,0x40,0x90,0x70,0x79,0x50,0xce,0x9d,0xd6,0xc8,0x3d,0x2a,0x42,0x80,0x63,0x06,0x3a,0xff,0x5c,0x04,0xf1,0x74,0x4e,0x33,0x4f,0x66,0x1f,0x2f,0xc0,0x1b,0x4e,0xf8,0x0b,0x50,0xf7,0x39}, + "", + "", + "" + } + }, + + /* ----- Multiple outputs with labels: un-labeled and labeled address; same recipient (16) ----- */ + { + 2, + { /* input plain seckeys */ + {0xea,0xdc,0x78,0x16,0x5f,0xf1,0xf8,0xea,0x94,0xad,0x7c,0xfd,0xc5,0x49,0x90,0x73,0x8a,0x4c,0x53,0xf6,0xe0,0x50,0x7b,0x42,0x15,0x42,0x01,0xb8,0xe5,0xdf,0xf3,0xb1}, + {0x03,0x78,0xe9,0x56,0x85,0xb7,0x45,0x65,0xfa,0x56,0x75,0x1b,0x84,0xa3,0x2d,0xfd,0x18,0x54,0x5d,0x10,0xd6,0x91,0x64,0x1b,0x83,0x72,0xe3,0x21,0x64,0xfa,0xd6,0x6a}, + "" + }, + { /* input plain pubkeys */ + {0x02,0x5a,0x1e,0x61,0xf8,0x98,0x17,0x30,0x40,0xe2,0x06,0x16,0xd4,0x3e,0x9f,0x49,0x6f,0xba,0x90,0x33,0x8a,0x39,0xfa,0xa1,0xed,0x98,0xfc,0xba,0xee,0xe4,0xdd,0x9b,0xe5}, + {0x03,0x78,0x2e,0xeb,0x91,0x34,0x31,0xca,0x6e,0x9b,0x8c,0x2f,0xd8,0x0a,0x5f,0x72,0xed,0x20,0x24,0xef,0x72,0xa3,0xc6,0xfb,0x10,0x26,0x3c,0x37,0x99,0x37,0x32,0x33,0x38}, + "" + }, + 0, + { /* input taproot seckeys */ + "", + "", + "" + }, + { /* input x-only pubkeys */ + "", + "", + "" + }, + /* smallest outpoint */ + {0x16,0x9e,0x1e,0x83,0xe9,0x30,0x85,0x33,0x91,0xbc,0x6f,0x35,0xf6,0x05,0xc6,0x75,0x4c,0xfe,0xad,0x57,0xcf,0x83,0x87,0x63,0x9d,0x3b,0x40,0x96,0xc5,0x4f,0x18,0xf4,0x00,0x00,0x00,0x00}, + 2, + { /* recipient pubkeys (address data) */ + { + {0x02,0x20,0xbc,0xfa,0xc5,0xb9,0x9e,0x04,0xad,0x1a,0x06,0xdd,0xfb,0x01,0x6e,0xe1,0x35,0x82,0x60,0x9d,0x60,0xb6,0x29,0x1e,0x98,0xd0,0x1a,0x9b,0xc9,0xa1,0x6c,0x96,0xd4}, + {0x03,0xa6,0x73,0x94,0x99,0xdc,0x66,0x7d,0x30,0x8b,0xae,0xfe,0xa4,0xde,0x0c,0x4a,0x85,0xcc,0x72,0xae,0xce,0x18,0x1b,0xc0,0x57,0x12,0xd3,0x91,0x96,0x62,0x61,0x0f,0xf1}, + }, + { + {0x02,0x20,0xbc,0xfa,0xc5,0xb9,0x9e,0x04,0xad,0x1a,0x06,0xdd,0xfb,0x01,0x6e,0xe1,0x35,0x82,0x60,0x9d,0x60,0xb6,0x29,0x1e,0x98,0xd0,0x1a,0x9b,0xc9,0xa1,0x6c,0x96,0xd4}, + {0x02,0x5c,0xc9,0x85,0x6d,0x6f,0x83,0x75,0x35,0x0e,0x12,0x39,0x78,0xda,0xac,0x20,0x0c,0x26,0x0c,0xb5,0xb5,0xae,0x83,0x10,0x6c,0xab,0x90,0x48,0x4d,0xcd,0x8f,0xcf,0x36}, + }, + { + "", + "", + }, + { + "", + "", + } + }, + 2, + 2, + { /* recipient outputs */ + { + {0x39,0xf4,0x26,0x24,0xd5,0xc3,0x2a,0x77,0xfd,0xa8,0x0f,0xf0,0xac,0xee,0x26,0x9a,0xfe,0xc6,0x01,0xd3,0x79,0x18,0x03,0xe8,0x02,0x52,0xae,0x04,0xe4,0xff,0xcf,0x4c}, + {0xf2,0x07,0x16,0x2b,0x1a,0x7a,0xbc,0x51,0xc4,0x20,0x17,0xbe,0xf0,0x55,0xe9,0xec,0x1e,0xfc,0x3d,0x35,0x67,0xcb,0x72,0x03,0x57,0xe2,0xb8,0x43,0x25,0xdb,0x33,0xac}, + "", + "" + }, + { + {0x83,0xdc,0x94,0x4e,0x61,0x60,0x31,0x37,0x29,0x48,0x29,0xae,0xd5,0x6c,0x74,0xc9,0xb0,0x87,0xd8,0x0f,0x2c,0x02,0x1b,0x98,0xa7,0xfa,0xe5,0x79,0x90,0x00,0x69,0x6c}, + {0xe9,0x76,0xa5,0x8f,0xbd,0x38,0xae,0xb4,0xe6,0x09,0x3d,0x4d,0xf0,0x2e,0x9c,0x1d,0xe0,0xc4,0x51,0x3a,0xe0,0xc5,0x88,0xce,0xf6,0x8c,0xda,0x5b,0x2f,0x88,0x34,0xca}, + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + }, + /* recipient data (scan and spend seckeys) */ + {0x0f,0x69,0x4e,0x06,0x80,0x28,0xa7,0x17,0xf8,0xaf,0x6b,0x94,0x11,0xf9,0xa1,0x33,0xdd,0x35,0x65,0x25,0x87,0x14,0xcc,0x22,0x65,0x94,0xb3,0x4d,0xb9,0x0c,0x1f,0x2c}, + {0x9d,0x6a,0xd8,0x55,0xce,0x34,0x17,0xef,0x84,0xe8,0x36,0x89,0x2e,0x5a,0x56,0x39,0x2b,0xfb,0xa0,0x5f,0xa5,0xd9,0x7c,0xce,0xa3,0x0e,0x26,0x6f,0x54,0x0e,0x08,0xb3}, + 2, + { /* outputs to scan */ + {0x39,0xf4,0x26,0x24,0xd5,0xc3,0x2a,0x77,0xfd,0xa8,0x0f,0xf0,0xac,0xee,0x26,0x9a,0xfe,0xc6,0x01,0xd3,0x79,0x18,0x03,0xe8,0x02,0x52,0xae,0x04,0xe4,0xff,0xcf,0x4c}, + {0xf2,0x07,0x16,0x2b,0x1a,0x7a,0xbc,0x51,0xc4,0x20,0x17,0xbe,0xf0,0x55,0xe9,0xec,0x1e,0xfc,0x3d,0x35,0x67,0xcb,0x72,0x03,0x57,0xe2,0xb8,0x43,0x25,0xdb,0x33,0xac}, + "", + "" + }, + 1, {1, 0xffffffff, 0xffffffff, 0xffffffff}, /* labels */ + /* expected output data (pubkeys and seckey tweaks) */ + 2, + { + {0x39,0xf4,0x26,0x24,0xd5,0xc3,0x2a,0x77,0xfd,0xa8,0x0f,0xf0,0xac,0xee,0x26,0x9a,0xfe,0xc6,0x01,0xd3,0x79,0x18,0x03,0xe8,0x02,0x52,0xae,0x04,0xe4,0xff,0xcf,0x4c}, + {0xf2,0x07,0x16,0x2b,0x1a,0x7a,0xbc,0x51,0xc4,0x20,0x17,0xbe,0xf0,0x55,0xe9,0xec,0x1e,0xfc,0x3d,0x35,0x67,0xcb,0x72,0x03,0x57,0xe2,0xb8,0x43,0x25,0xdb,0x33,0xac}, + "", + "" + }, + { + {0x43,0x10,0x0f,0x89,0xf1,0xa6,0xbf,0x10,0x08,0x1c,0x92,0xb4,0x73,0xff,0xc5,0x7c,0xea,0xc7,0xdb,0xed,0x60,0x0b,0x6a,0xba,0x9b,0xb3,0x97,0x6f,0x17,0xdb,0xb9,0x14}, + {0x33,0xce,0x08,0x5c,0x3c,0x11,0xea,0xad,0x13,0x69,0x4a,0xae,0x3c,0x20,0x30,0x1a,0x6c,0x83,0x38,0x2e,0xc8,0x9a,0x7c,0xde,0x96,0xc6,0x79,0x9e,0x2f,0x88,0x80,0x5a}, + "", + "" + }, + { + {0x15,0xc9,0x25,0x09,0xb6,0x7a,0x6c,0x21,0x1e,0xbb,0x4a,0x51,0xb7,0x52,0x8d,0x06,0x66,0xe6,0x72,0x0d,0xe2,0x34,0x3b,0x2e,0x92,0xcf,0xb9,0x79,0x42,0xca,0x14,0x69,0x3c,0x1f,0x1f,0xdc,0x84,0x51,0xac,0xfd,0xb2,0x64,0x40,0x39,0xf8,0xf5,0xc7,0x61,0x14,0x80,0x7f,0xdc,0x3d,0x3a,0x00,0x2d,0x8a,0x46,0xaf,0xab,0x67,0x56,0xbd,0x75}, + {0x33,0x56,0x67,0xca,0x6c,0xae,0x7a,0x26,0x43,0x8f,0x5c,0xfd,0xd7,0x3b,0x3d,0x48,0xfa,0x83,0x2f,0xa9,0x76,0x85,0x21,0xd7,0xd5,0x44,0x5f,0x22,0xc2,0x03,0xab,0x0d,0x74,0xed,0x85,0x08,0x8f,0x27,0xd2,0x99,0x59,0xba,0x62,0x7a,0x45,0x09,0x99,0x66,0x76,0xf4,0x7d,0xf8,0xff,0x28,0x4d,0x29,0x25,0x67,0xb1,0xbe,0xef,0x0e,0x39,0x12}, + "", + "" + } + }, + + /* ----- Multiple outputs with labels: multiple outputs for labeled address; same recipient (17) ----- */ + { + 2, + { /* input plain seckeys */ + {0xea,0xdc,0x78,0x16,0x5f,0xf1,0xf8,0xea,0x94,0xad,0x7c,0xfd,0xc5,0x49,0x90,0x73,0x8a,0x4c,0x53,0xf6,0xe0,0x50,0x7b,0x42,0x15,0x42,0x01,0xb8,0xe5,0xdf,0xf3,0xb1}, + {0x03,0x78,0xe9,0x56,0x85,0xb7,0x45,0x65,0xfa,0x56,0x75,0x1b,0x84,0xa3,0x2d,0xfd,0x18,0x54,0x5d,0x10,0xd6,0x91,0x64,0x1b,0x83,0x72,0xe3,0x21,0x64,0xfa,0xd6,0x6a}, + "" + }, + { /* input plain pubkeys */ + {0x02,0x5a,0x1e,0x61,0xf8,0x98,0x17,0x30,0x40,0xe2,0x06,0x16,0xd4,0x3e,0x9f,0x49,0x6f,0xba,0x90,0x33,0x8a,0x39,0xfa,0xa1,0xed,0x98,0xfc,0xba,0xee,0xe4,0xdd,0x9b,0xe5}, + {0x03,0x78,0x2e,0xeb,0x91,0x34,0x31,0xca,0x6e,0x9b,0x8c,0x2f,0xd8,0x0a,0x5f,0x72,0xed,0x20,0x24,0xef,0x72,0xa3,0xc6,0xfb,0x10,0x26,0x3c,0x37,0x99,0x37,0x32,0x33,0x38}, + "" + }, + 0, + { /* input taproot seckeys */ + "", + "", + "" + }, + { /* input x-only pubkeys */ + "", + "", + "" + }, + /* smallest outpoint */ + {0x16,0x9e,0x1e,0x83,0xe9,0x30,0x85,0x33,0x91,0xbc,0x6f,0x35,0xf6,0x05,0xc6,0x75,0x4c,0xfe,0xad,0x57,0xcf,0x83,0x87,0x63,0x9d,0x3b,0x40,0x96,0xc5,0x4f,0x18,0xf4,0x00,0x00,0x00,0x00}, + 2, + { /* recipient pubkeys (address data) */ + { + {0x02,0x20,0xbc,0xfa,0xc5,0xb9,0x9e,0x04,0xad,0x1a,0x06,0xdd,0xfb,0x01,0x6e,0xe1,0x35,0x82,0x60,0x9d,0x60,0xb6,0x29,0x1e,0x98,0xd0,0x1a,0x9b,0xc9,0xa1,0x6c,0x96,0xd4}, + {0x03,0xa6,0x73,0x94,0x99,0xdc,0x66,0x7d,0x30,0x8b,0xae,0xfe,0xa4,0xde,0x0c,0x4a,0x85,0xcc,0x72,0xae,0xce,0x18,0x1b,0xc0,0x57,0x12,0xd3,0x91,0x96,0x62,0x61,0x0f,0xf1}, + }, + { + {0x02,0x20,0xbc,0xfa,0xc5,0xb9,0x9e,0x04,0xad,0x1a,0x06,0xdd,0xfb,0x01,0x6e,0xe1,0x35,0x82,0x60,0x9d,0x60,0xb6,0x29,0x1e,0x98,0xd0,0x1a,0x9b,0xc9,0xa1,0x6c,0x96,0xd4}, + {0x03,0xa6,0x73,0x94,0x99,0xdc,0x66,0x7d,0x30,0x8b,0xae,0xfe,0xa4,0xde,0x0c,0x4a,0x85,0xcc,0x72,0xae,0xce,0x18,0x1b,0xc0,0x57,0x12,0xd3,0x91,0x96,0x62,0x61,0x0f,0xf1}, + }, + { + "", + "", + }, + { + "", + "", + } + }, + 1, + 2, + { /* recipient outputs */ + { + {0x39,0xf4,0x26,0x24,0xd5,0xc3,0x2a,0x77,0xfd,0xa8,0x0f,0xf0,0xac,0xee,0x26,0x9a,0xfe,0xc6,0x01,0xd3,0x79,0x18,0x03,0xe8,0x02,0x52,0xae,0x04,0xe4,0xff,0xcf,0x4c}, + {0x83,0xdc,0x94,0x4e,0x61,0x60,0x31,0x37,0x29,0x48,0x29,0xae,0xd5,0x6c,0x74,0xc9,0xb0,0x87,0xd8,0x0f,0x2c,0x02,0x1b,0x98,0xa7,0xfa,0xe5,0x79,0x90,0x00,0x69,0x6c}, + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + }, + /* recipient data (scan and spend seckeys) */ + {0x0f,0x69,0x4e,0x06,0x80,0x28,0xa7,0x17,0xf8,0xaf,0x6b,0x94,0x11,0xf9,0xa1,0x33,0xdd,0x35,0x65,0x25,0x87,0x14,0xcc,0x22,0x65,0x94,0xb3,0x4d,0xb9,0x0c,0x1f,0x2c}, + {0x9d,0x6a,0xd8,0x55,0xce,0x34,0x17,0xef,0x84,0xe8,0x36,0x89,0x2e,0x5a,0x56,0x39,0x2b,0xfb,0xa0,0x5f,0xa5,0xd9,0x7c,0xce,0xa3,0x0e,0x26,0x6f,0x54,0x0e,0x08,0xb3}, + 2, + { /* outputs to scan */ + {0x39,0xf4,0x26,0x24,0xd5,0xc3,0x2a,0x77,0xfd,0xa8,0x0f,0xf0,0xac,0xee,0x26,0x9a,0xfe,0xc6,0x01,0xd3,0x79,0x18,0x03,0xe8,0x02,0x52,0xae,0x04,0xe4,0xff,0xcf,0x4c}, + {0x83,0xdc,0x94,0x4e,0x61,0x60,0x31,0x37,0x29,0x48,0x29,0xae,0xd5,0x6c,0x74,0xc9,0xb0,0x87,0xd8,0x0f,0x2c,0x02,0x1b,0x98,0xa7,0xfa,0xe5,0x79,0x90,0x00,0x69,0x6c}, + "", + "" + }, + 1, {1, 0xffffffff, 0xffffffff, 0xffffffff}, /* labels */ + /* expected output data (pubkeys and seckey tweaks) */ + 2, + { + {0x39,0xf4,0x26,0x24,0xd5,0xc3,0x2a,0x77,0xfd,0xa8,0x0f,0xf0,0xac,0xee,0x26,0x9a,0xfe,0xc6,0x01,0xd3,0x79,0x18,0x03,0xe8,0x02,0x52,0xae,0x04,0xe4,0xff,0xcf,0x4c}, + {0x83,0xdc,0x94,0x4e,0x61,0x60,0x31,0x37,0x29,0x48,0x29,0xae,0xd5,0x6c,0x74,0xc9,0xb0,0x87,0xd8,0x0f,0x2c,0x02,0x1b,0x98,0xa7,0xfa,0xe5,0x79,0x90,0x00,0x69,0x6c}, + "", + "" + }, + { + {0x43,0x10,0x0f,0x89,0xf1,0xa6,0xbf,0x10,0x08,0x1c,0x92,0xb4,0x73,0xff,0xc5,0x7c,0xea,0xc7,0xdb,0xed,0x60,0x0b,0x6a,0xba,0x9b,0xb3,0x97,0x6f,0x17,0xdb,0xb9,0x14}, + {0x9d,0x5f,0xd3,0xb9,0x1c,0xac,0x9d,0xdf,0xea,0x6f,0xc2,0xe6,0xf9,0x38,0x6f,0x68,0x0e,0x6c,0xee,0x62,0x3c,0xda,0x02,0xf5,0x37,0x06,0x30,0x6c,0x08,0x1d,0xe8,0x7f}, + "", + "" + }, + { + {0x15,0xc9,0x25,0x09,0xb6,0x7a,0x6c,0x21,0x1e,0xbb,0x4a,0x51,0xb7,0x52,0x8d,0x06,0x66,0xe6,0x72,0x0d,0xe2,0x34,0x3b,0x2e,0x92,0xcf,0xb9,0x79,0x42,0xca,0x14,0x69,0x3c,0x1f,0x1f,0xdc,0x84,0x51,0xac,0xfd,0xb2,0x64,0x40,0x39,0xf8,0xf5,0xc7,0x61,0x14,0x80,0x7f,0xdc,0x3d,0x3a,0x00,0x2d,0x8a,0x46,0xaf,0xab,0x67,0x56,0xbd,0x75}, + {0xdb,0x0d,0xfa,0xcc,0x98,0xb6,0xa6,0xfc,0xc6,0x7c,0xc4,0x63,0x1f,0x08,0x0b,0x1c,0xa3,0x8c,0x60,0xd8,0xc3,0x97,0xf2,0xf1,0x98,0x43,0xf8,0xf9,0x5e,0xc9,0x15,0x94,0xb2,0x4e,0x47,0xc5,0xbd,0x39,0x48,0x0a,0x86,0x1c,0x12,0x09,0xf7,0xe3,0x14,0x5c,0x44,0x03,0x71,0xf9,0x19,0x1f,0xb9,0x6e,0x32,0x46,0x90,0x10,0x1e,0xac,0x8e,0x8e}, + "", + "" + } + }, + + /* ----- Multiple outputs with labels: un-labeled, labeled, and multiple outputs for labeled address; same recipients (18) ----- */ + { + 2, + { /* input plain seckeys */ + {0xea,0xdc,0x78,0x16,0x5f,0xf1,0xf8,0xea,0x94,0xad,0x7c,0xfd,0xc5,0x49,0x90,0x73,0x8a,0x4c,0x53,0xf6,0xe0,0x50,0x7b,0x42,0x15,0x42,0x01,0xb8,0xe5,0xdf,0xf3,0xb1}, + {0x03,0x78,0xe9,0x56,0x85,0xb7,0x45,0x65,0xfa,0x56,0x75,0x1b,0x84,0xa3,0x2d,0xfd,0x18,0x54,0x5d,0x10,0xd6,0x91,0x64,0x1b,0x83,0x72,0xe3,0x21,0x64,0xfa,0xd6,0x6a}, + "" + }, + { /* input plain pubkeys */ + {0x02,0x5a,0x1e,0x61,0xf8,0x98,0x17,0x30,0x40,0xe2,0x06,0x16,0xd4,0x3e,0x9f,0x49,0x6f,0xba,0x90,0x33,0x8a,0x39,0xfa,0xa1,0xed,0x98,0xfc,0xba,0xee,0xe4,0xdd,0x9b,0xe5}, + {0x03,0x78,0x2e,0xeb,0x91,0x34,0x31,0xca,0x6e,0x9b,0x8c,0x2f,0xd8,0x0a,0x5f,0x72,0xed,0x20,0x24,0xef,0x72,0xa3,0xc6,0xfb,0x10,0x26,0x3c,0x37,0x99,0x37,0x32,0x33,0x38}, + "" + }, + 0, + { /* input taproot seckeys */ + "", + "", + "" + }, + { /* input x-only pubkeys */ + "", + "", + "" + }, + /* smallest outpoint */ + {0x16,0x9e,0x1e,0x83,0xe9,0x30,0x85,0x33,0x91,0xbc,0x6f,0x35,0xf6,0x05,0xc6,0x75,0x4c,0xfe,0xad,0x57,0xcf,0x83,0x87,0x63,0x9d,0x3b,0x40,0x96,0xc5,0x4f,0x18,0xf4,0x00,0x00,0x00,0x00}, + 4, + { /* recipient pubkeys (address data) */ + { + {0x02,0x20,0xbc,0xfa,0xc5,0xb9,0x9e,0x04,0xad,0x1a,0x06,0xdd,0xfb,0x01,0x6e,0xe1,0x35,0x82,0x60,0x9d,0x60,0xb6,0x29,0x1e,0x98,0xd0,0x1a,0x9b,0xc9,0xa1,0x6c,0x96,0xd4}, + {0x02,0x5c,0xc9,0x85,0x6d,0x6f,0x83,0x75,0x35,0x0e,0x12,0x39,0x78,0xda,0xac,0x20,0x0c,0x26,0x0c,0xb5,0xb5,0xae,0x83,0x10,0x6c,0xab,0x90,0x48,0x4d,0xcd,0x8f,0xcf,0x36}, + }, + { + {0x02,0x20,0xbc,0xfa,0xc5,0xb9,0x9e,0x04,0xad,0x1a,0x06,0xdd,0xfb,0x01,0x6e,0xe1,0x35,0x82,0x60,0x9d,0x60,0xb6,0x29,0x1e,0x98,0xd0,0x1a,0x9b,0xc9,0xa1,0x6c,0x96,0xd4}, + {0x03,0xa6,0x73,0x94,0x99,0xdc,0x66,0x7d,0x30,0x8b,0xae,0xfe,0xa4,0xde,0x0c,0x4a,0x85,0xcc,0x72,0xae,0xce,0x18,0x1b,0xc0,0x57,0x12,0xd3,0x91,0x96,0x62,0x61,0x0f,0xf1}, + }, + { + {0x02,0x20,0xbc,0xfa,0xc5,0xb9,0x9e,0x04,0xad,0x1a,0x06,0xdd,0xfb,0x01,0x6e,0xe1,0x35,0x82,0x60,0x9d,0x60,0xb6,0x29,0x1e,0x98,0xd0,0x1a,0x9b,0xc9,0xa1,0x6c,0x96,0xd4}, + {0x02,0x44,0xba,0xa5,0xcf,0x5d,0xb4,0x44,0xa9,0xe9,0x22,0x83,0x2f,0xf2,0xc8,0x87,0x16,0xb5,0x66,0xa8,0x5d,0x62,0xe8,0x23,0x5a,0xeb,0xd9,0x18,0x84,0xd4,0xf6,0x49,0x42}, + }, + { + {0x02,0x20,0xbc,0xfa,0xc5,0xb9,0x9e,0x04,0xad,0x1a,0x06,0xdd,0xfb,0x01,0x6e,0xe1,0x35,0x82,0x60,0x9d,0x60,0xb6,0x29,0x1e,0x98,0xd0,0x1a,0x9b,0xc9,0xa1,0x6c,0x96,0xd4}, + {0x02,0x44,0xba,0xa5,0xcf,0x5d,0xb4,0x44,0xa9,0xe9,0x22,0x83,0x2f,0xf2,0xc8,0x87,0x16,0xb5,0x66,0xa8,0x5d,0x62,0xe8,0x23,0x5a,0xeb,0xd9,0x18,0x84,0xd4,0xf6,0x49,0x42}, + } + }, + 12, + 4, + { /* recipient outputs */ + { + {0x00,0x6a,0x02,0xc3,0x08,0xcc,0xdb,0xf3,0xac,0x49,0xf0,0x63,0x8f,0x6d,0xe1,0x28,0xf8,0x75,0xdb,0x5a,0x21,0x30,0x95,0xcf,0x11,0x2b,0x3b,0x77,0x72,0x24,0x72,0xae}, + {0x39,0xf4,0x26,0x24,0xd5,0xc3,0x2a,0x77,0xfd,0xa8,0x0f,0xf0,0xac,0xee,0x26,0x9a,0xfe,0xc6,0x01,0xd3,0x79,0x18,0x03,0xe8,0x02,0x52,0xae,0x04,0xe4,0xff,0xcf,0x4c}, + {0xae,0x1a,0x78,0x0c,0x04,0x23,0x7b,0xd5,0x77,0x28,0x3c,0x3d,0xdb,0x2e,0x49,0x97,0x67,0xc3,0x21,0x41,0x60,0xd5,0xa6,0xb0,0x76,0x7e,0x6b,0x8c,0x27,0x8b,0xd7,0x01}, + {0xca,0x64,0xab,0xe1,0xe0,0xf7,0x37,0x82,0x3f,0xb9,0xa9,0x4f,0x59,0x7e,0xed,0x41,0x8f,0xb2,0xdf,0x77,0xb1,0x31,0x7e,0x26,0xb8,0x81,0xa1,0x4b,0xb5,0x94,0xfa,0xaa} + }, + { + {0x00,0x6a,0x02,0xc3,0x08,0xcc,0xdb,0xf3,0xac,0x49,0xf0,0x63,0x8f,0x6d,0xe1,0x28,0xf8,0x75,0xdb,0x5a,0x21,0x30,0x95,0xcf,0x11,0x2b,0x3b,0x77,0x72,0x24,0x72,0xae}, + {0x3e,0xdf,0x1f,0xf6,0x65,0x7c,0x6e,0x69,0x56,0x88,0x11,0xbd,0x72,0x6a,0x7a,0x7f,0x48,0x04,0x93,0xaa,0x42,0x16,0x1a,0xcf,0xe8,0xdd,0x4f,0x44,0x52,0x1f,0x99,0xed}, + {0x7e,0xe1,0x54,0x3e,0xd5,0xd1,0x23,0xff,0xa6,0x6f,0xbe,0xbc,0x12,0x8c,0x02,0x01,0x73,0xeb,0x49,0x0d,0x5f,0xa2,0xba,0x30,0x6e,0x0c,0x95,0x73,0xa7,0x7d,0xb8,0xf3}, + {0xca,0x64,0xab,0xe1,0xe0,0xf7,0x37,0x82,0x3f,0xb9,0xa9,0x4f,0x59,0x7e,0xed,0x41,0x8f,0xb2,0xdf,0x77,0xb1,0x31,0x7e,0x26,0xb8,0x81,0xa1,0x4b,0xb5,0x94,0xfa,0xaa} + }, + { + {0x00,0x6a,0x02,0xc3,0x08,0xcc,0xdb,0xf3,0xac,0x49,0xf0,0x63,0x8f,0x6d,0xe1,0x28,0xf8,0x75,0xdb,0x5a,0x21,0x30,0x95,0xcf,0x11,0x2b,0x3b,0x77,0x72,0x24,0x72,0xae}, + {0x7e,0xe1,0x54,0x3e,0xd5,0xd1,0x23,0xff,0xa6,0x6f,0xbe,0xbc,0x12,0x8c,0x02,0x01,0x73,0xeb,0x49,0x0d,0x5f,0xa2,0xba,0x30,0x6e,0x0c,0x95,0x73,0xa7,0x7d,0xb8,0xf3}, + {0x83,0xdc,0x94,0x4e,0x61,0x60,0x31,0x37,0x29,0x48,0x29,0xae,0xd5,0x6c,0x74,0xc9,0xb0,0x87,0xd8,0x0f,0x2c,0x02,0x1b,0x98,0xa7,0xfa,0xe5,0x79,0x90,0x00,0x69,0x6c}, + {0xae,0x1a,0x78,0x0c,0x04,0x23,0x7b,0xd5,0x77,0x28,0x3c,0x3d,0xdb,0x2e,0x49,0x97,0x67,0xc3,0x21,0x41,0x60,0xd5,0xa6,0xb0,0x76,0x7e,0x6b,0x8c,0x27,0x8b,0xd7,0x01} + }, + { + {0x39,0xf4,0x26,0x24,0xd5,0xc3,0x2a,0x77,0xfd,0xa8,0x0f,0xf0,0xac,0xee,0x26,0x9a,0xfe,0xc6,0x01,0xd3,0x79,0x18,0x03,0xe8,0x02,0x52,0xae,0x04,0xe4,0xff,0xcf,0x4c}, + {0x3c,0x54,0x44,0x49,0x44,0xd1,0x76,0x43,0x76,0x44,0x37,0x8c,0x23,0xef,0xb9,0x99,0xab,0x6a,0xb1,0xca,0xcd,0xfe,0x1d,0xc1,0x53,0x7b,0x60,0x7e,0x3d,0xf3,0x30,0xe2}, + {0xca,0x64,0xab,0xe1,0xe0,0xf7,0x37,0x82,0x3f,0xb9,0xa9,0x4f,0x59,0x7e,0xed,0x41,0x8f,0xb2,0xdf,0x77,0xb1,0x31,0x7e,0x26,0xb8,0x81,0xa1,0x4b,0xb5,0x94,0xfa,0xaa}, + {0xf4,0x56,0x9f,0xc5,0xf6,0x9c,0x10,0xf0,0x08,0x2c,0xfb,0xb8,0xe0,0x72,0xe6,0x26,0x6e,0xc5,0x5f,0x69,0xfb,0xa8,0xcf,0xfc,0xa4,0xcb,0xb4,0xc1,0x44,0xb7,0xe5,0x9b} + }, + { + {0x39,0xf4,0x26,0x24,0xd5,0xc3,0x2a,0x77,0xfd,0xa8,0x0f,0xf0,0xac,0xee,0x26,0x9a,0xfe,0xc6,0x01,0xd3,0x79,0x18,0x03,0xe8,0x02,0x52,0xae,0x04,0xe4,0xff,0xcf,0x4c}, + {0xae,0x1a,0x78,0x0c,0x04,0x23,0x7b,0xd5,0x77,0x28,0x3c,0x3d,0xdb,0x2e,0x49,0x97,0x67,0xc3,0x21,0x41,0x60,0xd5,0xa6,0xb0,0x76,0x7e,0x6b,0x8c,0x27,0x8b,0xd7,0x01}, + {0xf2,0x07,0x16,0x2b,0x1a,0x7a,0xbc,0x51,0xc4,0x20,0x17,0xbe,0xf0,0x55,0xe9,0xec,0x1e,0xfc,0x3d,0x35,0x67,0xcb,0x72,0x03,0x57,0xe2,0xb8,0x43,0x25,0xdb,0x33,0xac}, + {0xf4,0x56,0x9f,0xc5,0xf6,0x9c,0x10,0xf0,0x08,0x2c,0xfb,0xb8,0xe0,0x72,0xe6,0x26,0x6e,0xc5,0x5f,0x69,0xfb,0xa8,0xcf,0xfc,0xa4,0xcb,0xb4,0xc1,0x44,0xb7,0xe5,0x9b} + }, + { + {0x3c,0x54,0x44,0x49,0x44,0xd1,0x76,0x43,0x76,0x44,0x37,0x8c,0x23,0xef,0xb9,0x99,0xab,0x6a,0xb1,0xca,0xcd,0xfe,0x1d,0xc1,0x53,0x7b,0x60,0x7e,0x3d,0xf3,0x30,0xe2}, + {0x60,0x2e,0x10,0xe6,0x94,0x41,0x07,0xc9,0xb4,0x8b,0xd8,0x85,0xb4,0x93,0x67,0x65,0x78,0xc9,0x35,0x72,0x32,0x87,0xe0,0xab,0x2f,0x8b,0x7f,0x13,0x68,0x62,0x56,0x8e}, + {0x7e,0xe1,0x54,0x3e,0xd5,0xd1,0x23,0xff,0xa6,0x6f,0xbe,0xbc,0x12,0x8c,0x02,0x01,0x73,0xeb,0x49,0x0d,0x5f,0xa2,0xba,0x30,0x6e,0x0c,0x95,0x73,0xa7,0x7d,0xb8,0xf3}, + {0xca,0x64,0xab,0xe1,0xe0,0xf7,0x37,0x82,0x3f,0xb9,0xa9,0x4f,0x59,0x7e,0xed,0x41,0x8f,0xb2,0xdf,0x77,0xb1,0x31,0x7e,0x26,0xb8,0x81,0xa1,0x4b,0xb5,0x94,0xfa,0xaa} + }, + { + {0x3c,0x54,0x44,0x49,0x44,0xd1,0x76,0x43,0x76,0x44,0x37,0x8c,0x23,0xef,0xb9,0x99,0xab,0x6a,0xb1,0xca,0xcd,0xfe,0x1d,0xc1,0x53,0x7b,0x60,0x7e,0x3d,0xf3,0x30,0xe2}, + {0x7e,0xe1,0x54,0x3e,0xd5,0xd1,0x23,0xff,0xa6,0x6f,0xbe,0xbc,0x12,0x8c,0x02,0x01,0x73,0xeb,0x49,0x0d,0x5f,0xa2,0xba,0x30,0x6e,0x0c,0x95,0x73,0xa7,0x7d,0xb8,0xf3}, + {0x83,0xdc,0x94,0x4e,0x61,0x60,0x31,0x37,0x29,0x48,0x29,0xae,0xd5,0x6c,0x74,0xc9,0xb0,0x87,0xd8,0x0f,0x2c,0x02,0x1b,0x98,0xa7,0xfa,0xe5,0x79,0x90,0x00,0x69,0x6c}, + {0xf4,0x56,0x9f,0xc5,0xf6,0x9c,0x10,0xf0,0x08,0x2c,0xfb,0xb8,0xe0,0x72,0xe6,0x26,0x6e,0xc5,0x5f,0x69,0xfb,0xa8,0xcf,0xfc,0xa4,0xcb,0xb4,0xc1,0x44,0xb7,0xe5,0x9b} + }, + { + {0x3e,0xdf,0x1f,0xf6,0x65,0x7c,0x6e,0x69,0x56,0x88,0x11,0xbd,0x72,0x6a,0x7a,0x7f,0x48,0x04,0x93,0xaa,0x42,0x16,0x1a,0xcf,0xe8,0xdd,0x4f,0x44,0x52,0x1f,0x99,0xed}, + {0x7e,0xe1,0x54,0x3e,0xd5,0xd1,0x23,0xff,0xa6,0x6f,0xbe,0xbc,0x12,0x8c,0x02,0x01,0x73,0xeb,0x49,0x0d,0x5f,0xa2,0xba,0x30,0x6e,0x0c,0x95,0x73,0xa7,0x7d,0xb8,0xf3}, + {0xf2,0x07,0x16,0x2b,0x1a,0x7a,0xbc,0x51,0xc4,0x20,0x17,0xbe,0xf0,0x55,0xe9,0xec,0x1e,0xfc,0x3d,0x35,0x67,0xcb,0x72,0x03,0x57,0xe2,0xb8,0x43,0x25,0xdb,0x33,0xac}, + {0xf4,0x56,0x9f,0xc5,0xf6,0x9c,0x10,0xf0,0x08,0x2c,0xfb,0xb8,0xe0,0x72,0xe6,0x26,0x6e,0xc5,0x5f,0x69,0xfb,0xa8,0xcf,0xfc,0xa4,0xcb,0xb4,0xc1,0x44,0xb7,0xe5,0x9b} + }, + { + {0x3e,0xdf,0x1f,0xf6,0x65,0x7c,0x6e,0x69,0x56,0x88,0x11,0xbd,0x72,0x6a,0x7a,0x7f,0x48,0x04,0x93,0xaa,0x42,0x16,0x1a,0xcf,0xe8,0xdd,0x4f,0x44,0x52,0x1f,0x99,0xed}, + {0xca,0x64,0xab,0xe1,0xe0,0xf7,0x37,0x82,0x3f,0xb9,0xa9,0x4f,0x59,0x7e,0xed,0x41,0x8f,0xb2,0xdf,0x77,0xb1,0x31,0x7e,0x26,0xb8,0x81,0xa1,0x4b,0xb5,0x94,0xfa,0xaa}, + {0xe9,0x76,0xa5,0x8f,0xbd,0x38,0xae,0xb4,0xe6,0x09,0x3d,0x4d,0xf0,0x2e,0x9c,0x1d,0xe0,0xc4,0x51,0x3a,0xe0,0xc5,0x88,0xce,0xf6,0x8c,0xda,0x5b,0x2f,0x88,0x34,0xca}, + {0xf4,0x56,0x9f,0xc5,0xf6,0x9c,0x10,0xf0,0x08,0x2c,0xfb,0xb8,0xe0,0x72,0xe6,0x26,0x6e,0xc5,0x5f,0x69,0xfb,0xa8,0xcf,0xfc,0xa4,0xcb,0xb4,0xc1,0x44,0xb7,0xe5,0x9b} + }, + { + {0x60,0x2e,0x10,0xe6,0x94,0x41,0x07,0xc9,0xb4,0x8b,0xd8,0x85,0xb4,0x93,0x67,0x65,0x78,0xc9,0x35,0x72,0x32,0x87,0xe0,0xab,0x2f,0x8b,0x7f,0x13,0x68,0x62,0x56,0x8e}, + {0x7e,0xe1,0x54,0x3e,0xd5,0xd1,0x23,0xff,0xa6,0x6f,0xbe,0xbc,0x12,0x8c,0x02,0x01,0x73,0xeb,0x49,0x0d,0x5f,0xa2,0xba,0x30,0x6e,0x0c,0x95,0x73,0xa7,0x7d,0xb8,0xf3}, + {0xae,0x1a,0x78,0x0c,0x04,0x23,0x7b,0xd5,0x77,0x28,0x3c,0x3d,0xdb,0x2e,0x49,0x97,0x67,0xc3,0x21,0x41,0x60,0xd5,0xa6,0xb0,0x76,0x7e,0x6b,0x8c,0x27,0x8b,0xd7,0x01}, + {0xf2,0x07,0x16,0x2b,0x1a,0x7a,0xbc,0x51,0xc4,0x20,0x17,0xbe,0xf0,0x55,0xe9,0xec,0x1e,0xfc,0x3d,0x35,0x67,0xcb,0x72,0x03,0x57,0xe2,0xb8,0x43,0x25,0xdb,0x33,0xac} + }, + { + {0x60,0x2e,0x10,0xe6,0x94,0x41,0x07,0xc9,0xb4,0x8b,0xd8,0x85,0xb4,0x93,0x67,0x65,0x78,0xc9,0x35,0x72,0x32,0x87,0xe0,0xab,0x2f,0x8b,0x7f,0x13,0x68,0x62,0x56,0x8e}, + {0xae,0x1a,0x78,0x0c,0x04,0x23,0x7b,0xd5,0x77,0x28,0x3c,0x3d,0xdb,0x2e,0x49,0x97,0x67,0xc3,0x21,0x41,0x60,0xd5,0xa6,0xb0,0x76,0x7e,0x6b,0x8c,0x27,0x8b,0xd7,0x01}, + {0xca,0x64,0xab,0xe1,0xe0,0xf7,0x37,0x82,0x3f,0xb9,0xa9,0x4f,0x59,0x7e,0xed,0x41,0x8f,0xb2,0xdf,0x77,0xb1,0x31,0x7e,0x26,0xb8,0x81,0xa1,0x4b,0xb5,0x94,0xfa,0xaa}, + {0xe9,0x76,0xa5,0x8f,0xbd,0x38,0xae,0xb4,0xe6,0x09,0x3d,0x4d,0xf0,0x2e,0x9c,0x1d,0xe0,0xc4,0x51,0x3a,0xe0,0xc5,0x88,0xce,0xf6,0x8c,0xda,0x5b,0x2f,0x88,0x34,0xca} + }, + { + {0x83,0xdc,0x94,0x4e,0x61,0x60,0x31,0x37,0x29,0x48,0x29,0xae,0xd5,0x6c,0x74,0xc9,0xb0,0x87,0xd8,0x0f,0x2c,0x02,0x1b,0x98,0xa7,0xfa,0xe5,0x79,0x90,0x00,0x69,0x6c}, + {0xae,0x1a,0x78,0x0c,0x04,0x23,0x7b,0xd5,0x77,0x28,0x3c,0x3d,0xdb,0x2e,0x49,0x97,0x67,0xc3,0x21,0x41,0x60,0xd5,0xa6,0xb0,0x76,0x7e,0x6b,0x8c,0x27,0x8b,0xd7,0x01}, + {0xe9,0x76,0xa5,0x8f,0xbd,0x38,0xae,0xb4,0xe6,0x09,0x3d,0x4d,0xf0,0x2e,0x9c,0x1d,0xe0,0xc4,0x51,0x3a,0xe0,0xc5,0x88,0xce,0xf6,0x8c,0xda,0x5b,0x2f,0x88,0x34,0xca}, + {0xf4,0x56,0x9f,0xc5,0xf6,0x9c,0x10,0xf0,0x08,0x2c,0xfb,0xb8,0xe0,0x72,0xe6,0x26,0x6e,0xc5,0x5f,0x69,0xfb,0xa8,0xcf,0xfc,0xa4,0xcb,0xb4,0xc1,0x44,0xb7,0xe5,0x9b} + }, + }, + /* recipient data (scan and spend seckeys) */ + {0x0f,0x69,0x4e,0x06,0x80,0x28,0xa7,0x17,0xf8,0xaf,0x6b,0x94,0x11,0xf9,0xa1,0x33,0xdd,0x35,0x65,0x25,0x87,0x14,0xcc,0x22,0x65,0x94,0xb3,0x4d,0xb9,0x0c,0x1f,0x2c}, + {0x9d,0x6a,0xd8,0x55,0xce,0x34,0x17,0xef,0x84,0xe8,0x36,0x89,0x2e,0x5a,0x56,0x39,0x2b,0xfb,0xa0,0x5f,0xa5,0xd9,0x7c,0xce,0xa3,0x0e,0x26,0x6f,0x54,0x0e,0x08,0xb3}, + 4, + { /* outputs to scan */ + {0x00,0x6a,0x02,0xc3,0x08,0xcc,0xdb,0xf3,0xac,0x49,0xf0,0x63,0x8f,0x6d,0xe1,0x28,0xf8,0x75,0xdb,0x5a,0x21,0x30,0x95,0xcf,0x11,0x2b,0x3b,0x77,0x72,0x24,0x72,0xae}, + {0x39,0xf4,0x26,0x24,0xd5,0xc3,0x2a,0x77,0xfd,0xa8,0x0f,0xf0,0xac,0xee,0x26,0x9a,0xfe,0xc6,0x01,0xd3,0x79,0x18,0x03,0xe8,0x02,0x52,0xae,0x04,0xe4,0xff,0xcf,0x4c}, + {0xae,0x1a,0x78,0x0c,0x04,0x23,0x7b,0xd5,0x77,0x28,0x3c,0x3d,0xdb,0x2e,0x49,0x97,0x67,0xc3,0x21,0x41,0x60,0xd5,0xa6,0xb0,0x76,0x7e,0x6b,0x8c,0x27,0x8b,0xd7,0x01}, + {0xca,0x64,0xab,0xe1,0xe0,0xf7,0x37,0x82,0x3f,0xb9,0xa9,0x4f,0x59,0x7e,0xed,0x41,0x8f,0xb2,0xdf,0x77,0xb1,0x31,0x7e,0x26,0xb8,0x81,0xa1,0x4b,0xb5,0x94,0xfa,0xaa} + }, + 2, {1, 1337, 0xffffffff, 0xffffffff}, /* labels */ + /* expected output data (pubkeys and seckey tweaks) */ + 4, + { + {0x00,0x6a,0x02,0xc3,0x08,0xcc,0xdb,0xf3,0xac,0x49,0xf0,0x63,0x8f,0x6d,0xe1,0x28,0xf8,0x75,0xdb,0x5a,0x21,0x30,0x95,0xcf,0x11,0x2b,0x3b,0x77,0x72,0x24,0x72,0xae}, + {0x39,0xf4,0x26,0x24,0xd5,0xc3,0x2a,0x77,0xfd,0xa8,0x0f,0xf0,0xac,0xee,0x26,0x9a,0xfe,0xc6,0x01,0xd3,0x79,0x18,0x03,0xe8,0x02,0x52,0xae,0x04,0xe4,0xff,0xcf,0x4c}, + {0xae,0x1a,0x78,0x0c,0x04,0x23,0x7b,0xd5,0x77,0x28,0x3c,0x3d,0xdb,0x2e,0x49,0x97,0x67,0xc3,0x21,0x41,0x60,0xd5,0xa6,0xb0,0x76,0x7e,0x6b,0x8c,0x27,0x8b,0xd7,0x01}, + {0xca,0x64,0xab,0xe1,0xe0,0xf7,0x37,0x82,0x3f,0xb9,0xa9,0x4f,0x59,0x7e,0xed,0x41,0x8f,0xb2,0xdf,0x77,0xb1,0x31,0x7e,0x26,0xb8,0x81,0xa1,0x4b,0xb5,0x94,0xfa,0xaa} + }, + { + {0x4e,0x33,0x52,0xfb,0xe0,0x50,0x5c,0x25,0xe7,0x18,0xd9,0x60,0x07,0xc2,0x59,0xef,0x08,0xdb,0x34,0xf8,0xc8,0x44,0xe4,0xff,0x74,0x2d,0x98,0x55,0xff,0x03,0x80,0x5a}, + {0x43,0x10,0x0f,0x89,0xf1,0xa6,0xbf,0x10,0x08,0x1c,0x92,0xb4,0x73,0xff,0xc5,0x7c,0xea,0xc7,0xdb,0xed,0x60,0x0b,0x6a,0xba,0x9b,0xb3,0x97,0x6f,0x17,0xdb,0xb9,0x14}, + {0xbf,0x70,0x9f,0x98,0xd4,0x41,0x8f,0x8a,0x67,0xe7,0x38,0x15,0x4a,0xe4,0x88,0x18,0xda,0xd4,0x46,0x89,0xcd,0x37,0xfb,0xc0,0x70,0x89,0x1a,0x39,0x6d,0xd1,0xc6,0x33}, + {0x73,0x6f,0x05,0xe4,0xe3,0x07,0x2c,0x3b,0x86,0x56,0xbe,0xde,0xf2,0xe9,0xbf,0x54,0xcb,0xca,0xa2,0xb6,0xfe,0x53,0x20,0xd3,0xe8,0x6f,0x5b,0x96,0x87,0x4d,0xda,0x71} + }, + { + {0x6e,0xea,0xe1,0xea,0x9e,0xb8,0x26,0xe3,0xd0,0xe8,0x12,0xf6,0x59,0x37,0x10,0x0e,0x08,0x36,0xea,0x18,0x8c,0x04,0xf3,0x6f,0xab,0xc4,0x98,0x1e,0xda,0x29,0xde,0x8d,0x3d,0x35,0x29,0x39,0x0a,0x0a,0x8b,0x3d,0x83,0x0f,0x7b,0xca,0x4f,0x5e,0xae,0x59,0x94,0xb9,0x78,0x8d,0xda,0xf0,0x5a,0xd2,0x59,0xff,0xe2,0x6d,0x86,0x14,0x4b,0x4b}, + {0x15,0xc9,0x25,0x09,0xb6,0x7a,0x6c,0x21,0x1e,0xbb,0x4a,0x51,0xb7,0x52,0x8d,0x06,0x66,0xe6,0x72,0x0d,0xe2,0x34,0x3b,0x2e,0x92,0xcf,0xb9,0x79,0x42,0xca,0x14,0x69,0x3c,0x1f,0x1f,0xdc,0x84,0x51,0xac,0xfd,0xb2,0x64,0x40,0x39,0xf8,0xf5,0xc7,0x61,0x14,0x80,0x7f,0xdc,0x3d,0x3a,0x00,0x2d,0x8a,0x46,0xaf,0xab,0x67,0x56,0xbd,0x75}, + {0x42,0xa1,0x9f,0xd8,0xa6,0x3d,0xde,0x18,0x24,0x96,0x6a,0x95,0xd6,0x5a,0x28,0x20,0x3e,0x63,0x1e,0x49,0xbf,0x96,0xca,0x5d,0xae,0x1b,0x39,0x0e,0x7a,0x0a,0xce,0x2c,0xc8,0x70,0x9c,0x9b,0x0c,0x57,0x15,0x04,0x70,0x32,0xf5,0x7f,0x53,0x6a,0x3c,0x80,0x27,0x3c,0xbe,0xcf,0x4c,0x05,0xbe,0x0b,0x54,0x56,0xc1,0x83,0xfa,0x12,0x2c,0x06}, + {0x2e,0x61,0xbb,0x3d,0x79,0x41,0x8e,0xcf,0x55,0xf6,0x88,0x47,0xcf,0x12,0x1b,0xfc,0x12,0xd3,0x97,0xb3,0x9d,0x1d,0xa8,0x64,0x32,0x46,0xb2,0xf0,0xa9,0xb9,0x6c,0x3d,0xaa,0x4b,0xfe,0x96,0x51,0xbe,0xb5,0xc9,0xce,0x20,0xe1,0xf2,0x92,0x82,0xc4,0x56,0x64,0x00,0xa4,0xb4,0x5e,0xe6,0x65,0x7e,0xc3,0xb1,0x8f,0xdc,0x55,0x4d,0xa0,0xb4} + } + }, + + /* ----- Single recipient: use silent payments for sender change (19) ----- */ + { + 2, + { /* input plain seckeys */ + {0xea,0xdc,0x78,0x16,0x5f,0xf1,0xf8,0xea,0x94,0xad,0x7c,0xfd,0xc5,0x49,0x90,0x73,0x8a,0x4c,0x53,0xf6,0xe0,0x50,0x7b,0x42,0x15,0x42,0x01,0xb8,0xe5,0xdf,0xf3,0xb1}, + {0x03,0x78,0xe9,0x56,0x85,0xb7,0x45,0x65,0xfa,0x56,0x75,0x1b,0x84,0xa3,0x2d,0xfd,0x18,0x54,0x5d,0x10,0xd6,0x91,0x64,0x1b,0x83,0x72,0xe3,0x21,0x64,0xfa,0xd6,0x6a}, + "" + }, + { /* input plain pubkeys */ + {0x02,0x5a,0x1e,0x61,0xf8,0x98,0x17,0x30,0x40,0xe2,0x06,0x16,0xd4,0x3e,0x9f,0x49,0x6f,0xba,0x90,0x33,0x8a,0x39,0xfa,0xa1,0xed,0x98,0xfc,0xba,0xee,0xe4,0xdd,0x9b,0xe5}, + {0x03,0x78,0x2e,0xeb,0x91,0x34,0x31,0xca,0x6e,0x9b,0x8c,0x2f,0xd8,0x0a,0x5f,0x72,0xed,0x20,0x24,0xef,0x72,0xa3,0xc6,0xfb,0x10,0x26,0x3c,0x37,0x99,0x37,0x32,0x33,0x38}, + "" + }, + 0, + { /* input taproot seckeys */ + "", + "", + "" + }, + { /* input x-only pubkeys */ + "", + "", + "" + }, + /* smallest outpoint */ + {0x16,0x9e,0x1e,0x83,0xe9,0x30,0x85,0x33,0x91,0xbc,0x6f,0x35,0xf6,0x05,0xc6,0x75,0x4c,0xfe,0xad,0x57,0xcf,0x83,0x87,0x63,0x9d,0x3b,0x40,0x96,0xc5,0x4f,0x18,0xf4,0x00,0x00,0x00,0x00}, + 2, + { /* recipient pubkeys (address data) */ + { + {0x02,0x20,0xbc,0xfa,0xc5,0xb9,0x9e,0x04,0xad,0x1a,0x06,0xdd,0xfb,0x01,0x6e,0xe1,0x35,0x82,0x60,0x9d,0x60,0xb6,0x29,0x1e,0x98,0xd0,0x1a,0x9b,0xc9,0xa1,0x6c,0x96,0xd4}, + {0x02,0x5c,0xc9,0x85,0x6d,0x6f,0x83,0x75,0x35,0x0e,0x12,0x39,0x78,0xda,0xac,0x20,0x0c,0x26,0x0c,0xb5,0xb5,0xae,0x83,0x10,0x6c,0xab,0x90,0x48,0x4d,0xcd,0x8f,0xcf,0x36}, + }, + { + {0x03,0xb4,0xcc,0x0b,0x09,0x0b,0x6f,0x49,0xa6,0x84,0x55,0x88,0x52,0xdb,0x60,0xee,0x5e,0xb1,0xc5,0xf7,0x43,0x52,0x83,0x9c,0x3d,0x18,0xa8,0xfc,0x04,0xef,0x73,0x54,0xe0}, + {0x03,0xec,0xd4,0x3b,0x9f,0xda,0xd4,0x84,0xff,0x57,0x27,0x8b,0x21,0x87,0x8b,0x84,0x42,0x76,0xce,0x39,0x06,0x22,0xd0,0x3d,0xd0,0xcf,0xb4,0x28,0x8b,0x7e,0x02,0xa6,0xf5}, + }, + { + "", + "", + }, + { + "", + "", + } + }, + 1, + 2, + { /* recipient outputs */ + { + {0xbe,0x36,0x8e,0x28,0x97,0x9d,0x95,0x02,0x45,0xd7,0x42,0x89,0x1a,0xe6,0x06,0x40,0x20,0xba,0x54,0x8c,0x1e,0x2e,0x65,0xa6,0x39,0xa8,0xbb,0x06,0x75,0xd9,0x5c,0xff}, + {0xf2,0x07,0x16,0x2b,0x1a,0x7a,0xbc,0x51,0xc4,0x20,0x17,0xbe,0xf0,0x55,0xe9,0xec,0x1e,0xfc,0x3d,0x35,0x67,0xcb,0x72,0x03,0x57,0xe2,0xb8,0x43,0x25,0xdb,0x33,0xac}, + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + }, + /* recipient data (scan and spend seckeys) */ + {0x11,0xb7,0xa8,0x2e,0x06,0xca,0x26,0x48,0xd5,0xfd,0xed,0x23,0x66,0x47,0x80,0x78,0xec,0x4f,0xc9,0xdc,0x1d,0x8f,0xf4,0x87,0x51,0x82,0x26,0xf2,0x29,0xd7,0x68,0xfd}, + {0xb8,0xf8,0x73,0x88,0xcb,0xb4,0x19,0x34,0xc5,0x0d,0xac,0xa0,0x18,0x90,0x1b,0x00,0x07,0x0a,0x5f,0xf6,0xcc,0x25,0xa7,0xe9,0xe7,0x16,0xa9,0xd5,0xb9,0xe4,0xd6,0x64}, + 2, + { /* outputs to scan */ + {0xbe,0x36,0x8e,0x28,0x97,0x9d,0x95,0x02,0x45,0xd7,0x42,0x89,0x1a,0xe6,0x06,0x40,0x20,0xba,0x54,0x8c,0x1e,0x2e,0x65,0xa6,0x39,0xa8,0xbb,0x06,0x75,0xd9,0x5c,0xff}, + {0xf2,0x07,0x16,0x2b,0x1a,0x7a,0xbc,0x51,0xc4,0x20,0x17,0xbe,0xf0,0x55,0xe9,0xec,0x1e,0xfc,0x3d,0x35,0x67,0xcb,0x72,0x03,0x57,0xe2,0xb8,0x43,0x25,0xdb,0x33,0xac}, + "", + "" + }, + 1, {0, 0xffffffff, 0xffffffff, 0xffffffff}, /* labels */ + /* expected output data (pubkeys and seckey tweaks) */ + 1, + { + {0xbe,0x36,0x8e,0x28,0x97,0x9d,0x95,0x02,0x45,0xd7,0x42,0x89,0x1a,0xe6,0x06,0x40,0x20,0xba,0x54,0x8c,0x1e,0x2e,0x65,0xa6,0x39,0xa8,0xbb,0x06,0x75,0xd9,0x5c,0xff}, + "", + "", + "" + }, + { + {0x80,0xcd,0x76,0x7e,0xd2,0x0b,0xd0,0xbb,0x7d,0x8e,0xa5,0xe8,0x03,0xf8,0xc3,0x81,0x29,0x3a,0x62,0xe8,0xa0,0x73,0xcf,0x46,0xfb,0x00,0x81,0xda,0x46,0xe6,0x4e,0x1f}, + "", + "", + "" + }, + { + {0x7f,0xbd,0x50,0x74,0xcf,0x13,0x77,0x27,0x31,0x55,0xee,0xfa,0xfc,0x7c,0x33,0x0c,0xb6,0x1b,0x31,0xda,0x25,0x2f,0x22,0x20,0x6a,0xc2,0x75,0x30,0xd2,0xb2,0x56,0x70,0x40,0xd9,0xaf,0x78,0x08,0x34,0x2e,0xd4,0xa0,0x95,0x98,0xc2,0x6d,0x83,0x07,0x44,0x6e,0x4e,0xd7,0x70,0x79,0xe6,0xa2,0xe6,0x1f,0xea,0x73,0x6e,0x44,0xda,0x5f,0x5a}, + "", + "", + "" + } + }, + + /* ----- Single recipient: taproot input with NUMS point (20) ----- */ + { + 0, + { /* input plain seckeys */ + "", + "", + "" + }, + { /* input plain pubkeys */ + "", + "", + "" + }, + 1, + { /* input taproot seckeys */ + {0xfc,0x87,0x16,0xa9,0x7a,0x48,0xba,0x9a,0x05,0xa9,0x8a,0xe4,0x7b,0x5c,0xd2,0x01,0xa2,0x5a,0x7f,0xd5,0xd8,0xb7,0x3c,0x20,0x3c,0x5f,0x7b,0x6b,0x6b,0x3b,0x6a,0xd7}, + "", + "" + }, + { /* input x-only pubkeys */ + {0x78,0x2e,0xeb,0x91,0x34,0x31,0xca,0x6e,0x9b,0x8c,0x2f,0xd8,0x0a,0x5f,0x72,0xed,0x20,0x24,0xef,0x72,0xa3,0xc6,0xfb,0x10,0x26,0x3c,0x37,0x99,0x37,0x32,0x33,0x38}, + "", + "" + }, + /* smallest outpoint */ + {0x16,0x9e,0x1e,0x83,0xe9,0x30,0x85,0x33,0x91,0xbc,0x6f,0x35,0xf6,0x05,0xc6,0x75,0x4c,0xfe,0xad,0x57,0xcf,0x83,0x87,0x63,0x9d,0x3b,0x40,0x96,0xc5,0x4f,0x18,0xf4,0x00,0x00,0x00,0x00}, + 1, + { /* recipient pubkeys (address data) */ + { + {0x02,0x20,0xbc,0xfa,0xc5,0xb9,0x9e,0x04,0xad,0x1a,0x06,0xdd,0xfb,0x01,0x6e,0xe1,0x35,0x82,0x60,0x9d,0x60,0xb6,0x29,0x1e,0x98,0xd0,0x1a,0x9b,0xc9,0xa1,0x6c,0x96,0xd4}, + {0x02,0x5c,0xc9,0x85,0x6d,0x6f,0x83,0x75,0x35,0x0e,0x12,0x39,0x78,0xda,0xac,0x20,0x0c,0x26,0x0c,0xb5,0xb5,0xae,0x83,0x10,0x6c,0xab,0x90,0x48,0x4d,0xcd,0x8f,0xcf,0x36}, + }, + { + "", + "", + }, + { + "", + "", + }, + { + "", + "", + } + }, + 1, + 1, + { /* recipient outputs */ + { + {0x79,0xe7,0x98,0x97,0xc5,0x29,0x35,0xbf,0xd9,0x7f,0xc6,0xe0,0x76,0xa6,0x43,0x1a,0x0c,0x75,0x43,0xca,0x8c,0x31,0xe0,0xfc,0x3c,0xf7,0x19,0xbb,0x57,0x2c,0x84,0x2d}, + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + }, + /* recipient data (scan and spend seckeys) */ + {0x0f,0x69,0x4e,0x06,0x80,0x28,0xa7,0x17,0xf8,0xaf,0x6b,0x94,0x11,0xf9,0xa1,0x33,0xdd,0x35,0x65,0x25,0x87,0x14,0xcc,0x22,0x65,0x94,0xb3,0x4d,0xb9,0x0c,0x1f,0x2c}, + {0x9d,0x6a,0xd8,0x55,0xce,0x34,0x17,0xef,0x84,0xe8,0x36,0x89,0x2e,0x5a,0x56,0x39,0x2b,0xfb,0xa0,0x5f,0xa5,0xd9,0x7c,0xce,0xa3,0x0e,0x26,0x6f,0x54,0x0e,0x08,0xb3}, + 1, + { /* outputs to scan */ + {0x79,0xe7,0x98,0x97,0xc5,0x29,0x35,0xbf,0xd9,0x7f,0xc6,0xe0,0x76,0xa6,0x43,0x1a,0x0c,0x75,0x43,0xca,0x8c,0x31,0xe0,0xfc,0x3c,0xf7,0x19,0xbb,0x57,0x2c,0x84,0x2d}, + "", + "", + "" + }, + 0, {0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}, /* labels */ + /* expected output data (pubkeys and seckey tweaks) */ + 1, + { + {0x79,0xe7,0x98,0x97,0xc5,0x29,0x35,0xbf,0xd9,0x7f,0xc6,0xe0,0x76,0xa6,0x43,0x1a,0x0c,0x75,0x43,0xca,0x8c,0x31,0xe0,0xfc,0x3c,0xf7,0x19,0xbb,0x57,0x2c,0x84,0x2d}, + "", + "", + "" + }, + { + {0x3d,0xde,0xc3,0x23,0x26,0x09,0xd3,0x48,0xd6,0xb8,0xb5,0x31,0x23,0xb4,0xf4,0x0f,0x6d,0x4f,0x53,0x98,0xca,0x58,0x6f,0x08,0x7b,0x04,0x16,0xec,0x3b,0x85,0x14,0x96}, + "", + "", + "" + }, + { + {0xd7,0xd0,0x6e,0x3a,0xfb,0x68,0x36,0x30,0x31,0xe4,0xeb,0x18,0x03,0x5c,0x46,0xce,0xae,0x41,0xbd,0xbe,0xbe,0x78,0x88,0xa4,0x75,0x4b,0xc9,0x84,0x8c,0x59,0x64,0x36,0x86,0x9a,0xea,0xec,0xff,0x05,0x27,0x64,0x9a,0x1f,0x45,0x8b,0x71,0xc9,0xce,0xec,0xec,0x10,0xb5,0x35,0xc0,0x9d,0x01,0xd7,0x20,0x22,0x9a,0xa2,0x28,0x54,0x77,0x06}, + "", + "", + "" + } + }, + + /* ----- Pubkey extraction from malleated p2pkh (21) ----- */ + { + 3, + { /* input plain seckeys */ + {0xea,0xdc,0x78,0x16,0x5f,0xf1,0xf8,0xea,0x94,0xad,0x7c,0xfd,0xc5,0x49,0x90,0x73,0x8a,0x4c,0x53,0xf6,0xe0,0x50,0x7b,0x42,0x15,0x42,0x01,0xb8,0xe5,0xdf,0xf3,0xb1}, + {0x03,0x78,0xe9,0x56,0x85,0xb7,0x45,0x65,0xfa,0x56,0x75,0x1b,0x84,0xa3,0x2d,0xfd,0x18,0x54,0x5d,0x10,0xd6,0x91,0x64,0x1b,0x83,0x72,0xe3,0x21,0x64,0xfa,0xd6,0x6a}, + {0x72,0xb8,0xae,0x09,0x17,0x5c,0xa7,0x97,0x7f,0x04,0x99,0x3e,0x65,0x1d,0x88,0x68,0x1e,0xd9,0x32,0xdf,0xb9,0x2c,0x51,0x58,0xcd,0xf0,0x16,0x1d,0xd2,0x3f,0xda,0x6e} + }, + { /* input plain pubkeys */ + {0x02,0x5a,0x1e,0x61,0xf8,0x98,0x17,0x30,0x40,0xe2,0x06,0x16,0xd4,0x3e,0x9f,0x49,0x6f,0xba,0x90,0x33,0x8a,0x39,0xfa,0xa1,0xed,0x98,0xfc,0xba,0xee,0xe4,0xdd,0x9b,0xe5}, + {0x03,0x78,0x2e,0xeb,0x91,0x34,0x31,0xca,0x6e,0x9b,0x8c,0x2f,0xd8,0x0a,0x5f,0x72,0xed,0x20,0x24,0xef,0x72,0xa3,0xc6,0xfb,0x10,0x26,0x3c,0x37,0x99,0x37,0x32,0x33,0x38}, + {0x02,0xe0,0xec,0x4f,0x64,0xb3,0xfa,0x2e,0x46,0x3c,0xcf,0xcf,0x4e,0x85,0x6e,0x37,0xd5,0xe1,0xe2,0x02,0x75,0xbc,0x89,0xec,0x1d,0xef,0x9e,0xb0,0x98,0xef,0xf1,0xf8,0x5d} + }, + 0, + { /* input taproot seckeys */ + "", + "", + "" + }, + { /* input x-only pubkeys */ + "", + "", + "" + }, + /* smallest outpoint */ + {0x16,0x9e,0x1e,0x83,0xe9,0x30,0x85,0x33,0x91,0xbc,0x6f,0x35,0xf6,0x05,0xc6,0x75,0x4c,0xfe,0xad,0x57,0xcf,0x83,0x87,0x63,0x9d,0x3b,0x40,0x96,0xc5,0x4f,0x18,0xf4,0x00,0x00,0x00,0x00}, + 1, + { /* recipient pubkeys (address data) */ + { + {0x02,0x20,0xbc,0xfa,0xc5,0xb9,0x9e,0x04,0xad,0x1a,0x06,0xdd,0xfb,0x01,0x6e,0xe1,0x35,0x82,0x60,0x9d,0x60,0xb6,0x29,0x1e,0x98,0xd0,0x1a,0x9b,0xc9,0xa1,0x6c,0x96,0xd4}, + {0x02,0x5c,0xc9,0x85,0x6d,0x6f,0x83,0x75,0x35,0x0e,0x12,0x39,0x78,0xda,0xac,0x20,0x0c,0x26,0x0c,0xb5,0xb5,0xae,0x83,0x10,0x6c,0xab,0x90,0x48,0x4d,0xcd,0x8f,0xcf,0x36}, + }, + { + "", + "", + }, + { + "", + "", + }, + { + "", + "", + } + }, + 1, + 1, + { /* recipient outputs */ + { + {0x46,0x12,0xcd,0xbf,0x84,0x5c,0x66,0xc7,0x51,0x1d,0x70,0xaa,0xb4,0xd9,0xae,0xd1,0x1e,0x49,0xe4,0x8c,0xdb,0x8d,0x79,0x9d,0x78,0x71,0x01,0xcd,0xd0,0xd5,0x3e,0x4f}, + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + }, + /* recipient data (scan and spend seckeys) */ + {0x0f,0x69,0x4e,0x06,0x80,0x28,0xa7,0x17,0xf8,0xaf,0x6b,0x94,0x11,0xf9,0xa1,0x33,0xdd,0x35,0x65,0x25,0x87,0x14,0xcc,0x22,0x65,0x94,0xb3,0x4d,0xb9,0x0c,0x1f,0x2c}, + {0x9d,0x6a,0xd8,0x55,0xce,0x34,0x17,0xef,0x84,0xe8,0x36,0x89,0x2e,0x5a,0x56,0x39,0x2b,0xfb,0xa0,0x5f,0xa5,0xd9,0x7c,0xce,0xa3,0x0e,0x26,0x6f,0x54,0x0e,0x08,0xb3}, + 1, + { /* outputs to scan */ + {0x46,0x12,0xcd,0xbf,0x84,0x5c,0x66,0xc7,0x51,0x1d,0x70,0xaa,0xb4,0xd9,0xae,0xd1,0x1e,0x49,0xe4,0x8c,0xdb,0x8d,0x79,0x9d,0x78,0x71,0x01,0xcd,0xd0,0xd5,0x3e,0x4f}, + "", + "", + "" + }, + 0, {0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}, /* labels */ + /* expected output data (pubkeys and seckey tweaks) */ + 1, + { + {0x46,0x12,0xcd,0xbf,0x84,0x5c,0x66,0xc7,0x51,0x1d,0x70,0xaa,0xb4,0xd9,0xae,0xd1,0x1e,0x49,0xe4,0x8c,0xdb,0x8d,0x79,0x9d,0x78,0x71,0x01,0xcd,0xd0,0xd5,0x3e,0x4f}, + "", + "", + "" + }, + { + {0x10,0xbd,0xe9,0x78,0x1d,0xef,0x20,0xd7,0x70,0x1e,0x76,0x03,0xef,0x1b,0x1e,0x5e,0x71,0xc6,0x7b,0xae,0x71,0x54,0x81,0x88,0x14,0xe3,0xc8,0x1e,0xf5,0xb1,0xa3,0xd3}, + "", + "", + "" + }, + { + {0x61,0x37,0x96,0x9f,0x81,0x0e,0x9e,0x8e,0xf6,0xc9,0x75,0x50,0x10,0xe8,0x08,0xf5,0xdd,0x1a,0xed,0x70,0x58,0x82,0xe4,0x4d,0x7f,0x0a,0xe6,0x4e,0xb0,0xc5,0x09,0xec,0x8b,0x62,0xa0,0x67,0x1b,0xee,0x0d,0x59,0x14,0xac,0x27,0xd2,0xc4,0x63,0x44,0x3e,0x28,0xe9,0x99,0xd8,0x2d,0xc3,0xd3,0xa4,0x91,0x9f,0x09,0x38,0x72,0xd9,0x47,0xbb}, + "", + "", + "" + } + }, + + /* ----- P2PKH and P2WPKH Uncompressed Keys are skipped (22) ----- */ + { + 1, + { /* input plain seckeys */ + {0xea,0xdc,0x78,0x16,0x5f,0xf1,0xf8,0xea,0x94,0xad,0x7c,0xfd,0xc5,0x49,0x90,0x73,0x8a,0x4c,0x53,0xf6,0xe0,0x50,0x7b,0x42,0x15,0x42,0x01,0xb8,0xe5,0xdf,0xf3,0xb1}, + "", + "" + }, + { /* input plain pubkeys */ + {0x02,0x5a,0x1e,0x61,0xf8,0x98,0x17,0x30,0x40,0xe2,0x06,0x16,0xd4,0x3e,0x9f,0x49,0x6f,0xba,0x90,0x33,0x8a,0x39,0xfa,0xa1,0xed,0x98,0xfc,0xba,0xee,0xe4,0xdd,0x9b,0xe5}, + "", + "" + }, + 0, + { /* input taproot seckeys */ + "", + "", + "" + }, + { /* input x-only pubkeys */ + "", + "", + "" + }, + /* smallest outpoint */ + {0x16,0x9e,0x1e,0x83,0xe9,0x30,0x85,0x33,0x91,0xbc,0x6f,0x35,0xf6,0x05,0xc6,0x75,0x4c,0xfe,0xad,0x57,0xcf,0x83,0x87,0x63,0x9d,0x3b,0x40,0x96,0xc5,0x4f,0x18,0xf4,0x00,0x00,0x00,0x00}, + 1, + { /* recipient pubkeys (address data) */ + { + {0x02,0x20,0xbc,0xfa,0xc5,0xb9,0x9e,0x04,0xad,0x1a,0x06,0xdd,0xfb,0x01,0x6e,0xe1,0x35,0x82,0x60,0x9d,0x60,0xb6,0x29,0x1e,0x98,0xd0,0x1a,0x9b,0xc9,0xa1,0x6c,0x96,0xd4}, + {0x02,0x5c,0xc9,0x85,0x6d,0x6f,0x83,0x75,0x35,0x0e,0x12,0x39,0x78,0xda,0xac,0x20,0x0c,0x26,0x0c,0xb5,0xb5,0xae,0x83,0x10,0x6c,0xab,0x90,0x48,0x4d,0xcd,0x8f,0xcf,0x36}, + }, + { + "", + "", + }, + { + "", + "", + }, + { + "", + "", + } + }, + 1, + 1, + { /* recipient outputs */ + { + {0x67,0xfe,0xe2,0x77,0xda,0x9e,0x85,0x42,0xb5,0xd2,0xe6,0xf3,0x2d,0x66,0x0a,0x9b,0xbd,0x3f,0x0e,0x10,0x7c,0x2d,0x53,0x63,0x8a,0xb1,0xd8,0x69,0x08,0x88,0x82,0xd6}, + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + }, + /* recipient data (scan and spend seckeys) */ + {0x0f,0x69,0x4e,0x06,0x80,0x28,0xa7,0x17,0xf8,0xaf,0x6b,0x94,0x11,0xf9,0xa1,0x33,0xdd,0x35,0x65,0x25,0x87,0x14,0xcc,0x22,0x65,0x94,0xb3,0x4d,0xb9,0x0c,0x1f,0x2c}, + {0x9d,0x6a,0xd8,0x55,0xce,0x34,0x17,0xef,0x84,0xe8,0x36,0x89,0x2e,0x5a,0x56,0x39,0x2b,0xfb,0xa0,0x5f,0xa5,0xd9,0x7c,0xce,0xa3,0x0e,0x26,0x6f,0x54,0x0e,0x08,0xb3}, + 1, + { /* outputs to scan */ + {0x67,0xfe,0xe2,0x77,0xda,0x9e,0x85,0x42,0xb5,0xd2,0xe6,0xf3,0x2d,0x66,0x0a,0x9b,0xbd,0x3f,0x0e,0x10,0x7c,0x2d,0x53,0x63,0x8a,0xb1,0xd8,0x69,0x08,0x88,0x82,0xd6}, + "", + "", + "" + }, + 0, {0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}, /* labels */ + /* expected output data (pubkeys and seckey tweaks) */ + 1, + { + {0x67,0xfe,0xe2,0x77,0xda,0x9e,0x85,0x42,0xb5,0xd2,0xe6,0xf3,0x2d,0x66,0x0a,0x9b,0xbd,0x3f,0x0e,0x10,0x7c,0x2d,0x53,0x63,0x8a,0xb1,0xd8,0x69,0x08,0x88,0x82,0xd6}, + "", + "", + "" + }, + { + {0x68,0x8f,0xa3,0xae,0xb9,0x7d,0x2a,0x46,0xae,0x87,0xb0,0x35,0x91,0x92,0x1c,0x2e,0xaf,0x4b,0x50,0x5e,0xb0,0xdd,0xca,0x27,0x33,0xc9,0x47,0x01,0xe0,0x10,0x60,0xcf}, + "", + "", + "" + }, + { + {0x72,0xe7,0xad,0x57,0x3a,0xc2,0x32,0x55,0xd4,0x65,0x1d,0x5b,0x03,0x26,0xa2,0x00,0x49,0x65,0x88,0xac,0xb7,0xa4,0x89,0x4b,0x22,0x09,0x22,0x36,0xd5,0xed,0xa6,0xa0,0xa9,0xa4,0xd8,0x42,0x9b,0x02,0x2c,0x22,0x19,0x08,0x1f,0xef,0xce,0x5b,0x33,0x79,0x5c,0xae,0x48,0x8d,0x10,0xf5,0xea,0x94,0x38,0x84,0x9e,0xd8,0x35,0x36,0x24,0xf2}, + "", + "", + "" + } + }, + + /* ----- Skip invalid P2SH inputs (23) ----- */ + { + 1, + { /* input plain seckeys */ + {0xea,0xdc,0x78,0x16,0x5f,0xf1,0xf8,0xea,0x94,0xad,0x7c,0xfd,0xc5,0x49,0x90,0x73,0x8a,0x4c,0x53,0xf6,0xe0,0x50,0x7b,0x42,0x15,0x42,0x01,0xb8,0xe5,0xdf,0xf3,0xb1}, + "", + "" + }, + { /* input plain pubkeys */ + {0x02,0x5a,0x1e,0x61,0xf8,0x98,0x17,0x30,0x40,0xe2,0x06,0x16,0xd4,0x3e,0x9f,0x49,0x6f,0xba,0x90,0x33,0x8a,0x39,0xfa,0xa1,0xed,0x98,0xfc,0xba,0xee,0xe4,0xdd,0x9b,0xe5}, + "", + "" + }, + 0, + { /* input taproot seckeys */ + "", + "", + "" + }, + { /* input x-only pubkeys */ + "", + "", + "" + }, + /* smallest outpoint */ + {0x16,0x9e,0x1e,0x83,0xe9,0x30,0x85,0x33,0x91,0xbc,0x6f,0x35,0xf6,0x05,0xc6,0x75,0x4c,0xfe,0xad,0x57,0xcf,0x83,0x87,0x63,0x9d,0x3b,0x40,0x96,0xc5,0x4f,0x18,0xf4,0x00,0x00,0x00,0x00}, + 1, + { /* recipient pubkeys (address data) */ + { + {0x02,0x20,0xbc,0xfa,0xc5,0xb9,0x9e,0x04,0xad,0x1a,0x06,0xdd,0xfb,0x01,0x6e,0xe1,0x35,0x82,0x60,0x9d,0x60,0xb6,0x29,0x1e,0x98,0xd0,0x1a,0x9b,0xc9,0xa1,0x6c,0x96,0xd4}, + {0x02,0x5c,0xc9,0x85,0x6d,0x6f,0x83,0x75,0x35,0x0e,0x12,0x39,0x78,0xda,0xac,0x20,0x0c,0x26,0x0c,0xb5,0xb5,0xae,0x83,0x10,0x6c,0xab,0x90,0x48,0x4d,0xcd,0x8f,0xcf,0x36}, + }, + { + "", + "", + }, + { + "", + "", + }, + { + "", + "", + } + }, + 1, + 1, + { /* recipient outputs */ + { + {0x67,0xfe,0xe2,0x77,0xda,0x9e,0x85,0x42,0xb5,0xd2,0xe6,0xf3,0x2d,0x66,0x0a,0x9b,0xbd,0x3f,0x0e,0x10,0x7c,0x2d,0x53,0x63,0x8a,0xb1,0xd8,0x69,0x08,0x88,0x82,0xd6}, + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + }, + /* recipient data (scan and spend seckeys) */ + {0x0f,0x69,0x4e,0x06,0x80,0x28,0xa7,0x17,0xf8,0xaf,0x6b,0x94,0x11,0xf9,0xa1,0x33,0xdd,0x35,0x65,0x25,0x87,0x14,0xcc,0x22,0x65,0x94,0xb3,0x4d,0xb9,0x0c,0x1f,0x2c}, + {0x9d,0x6a,0xd8,0x55,0xce,0x34,0x17,0xef,0x84,0xe8,0x36,0x89,0x2e,0x5a,0x56,0x39,0x2b,0xfb,0xa0,0x5f,0xa5,0xd9,0x7c,0xce,0xa3,0x0e,0x26,0x6f,0x54,0x0e,0x08,0xb3}, + 1, + { /* outputs to scan */ + {0x67,0xfe,0xe2,0x77,0xda,0x9e,0x85,0x42,0xb5,0xd2,0xe6,0xf3,0x2d,0x66,0x0a,0x9b,0xbd,0x3f,0x0e,0x10,0x7c,0x2d,0x53,0x63,0x8a,0xb1,0xd8,0x69,0x08,0x88,0x82,0xd6}, + "", + "", + "" + }, + 0, {0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}, /* labels */ + /* expected output data (pubkeys and seckey tweaks) */ + 1, + { + {0x67,0xfe,0xe2,0x77,0xda,0x9e,0x85,0x42,0xb5,0xd2,0xe6,0xf3,0x2d,0x66,0x0a,0x9b,0xbd,0x3f,0x0e,0x10,0x7c,0x2d,0x53,0x63,0x8a,0xb1,0xd8,0x69,0x08,0x88,0x82,0xd6}, + "", + "", + "" + }, + { + {0x68,0x8f,0xa3,0xae,0xb9,0x7d,0x2a,0x46,0xae,0x87,0xb0,0x35,0x91,0x92,0x1c,0x2e,0xaf,0x4b,0x50,0x5e,0xb0,0xdd,0xca,0x27,0x33,0xc9,0x47,0x01,0xe0,0x10,0x60,0xcf}, + "", + "", + "" + }, + { + {0x72,0xe7,0xad,0x57,0x3a,0xc2,0x32,0x55,0xd4,0x65,0x1d,0x5b,0x03,0x26,0xa2,0x00,0x49,0x65,0x88,0xac,0xb7,0xa4,0x89,0x4b,0x22,0x09,0x22,0x36,0xd5,0xed,0xa6,0xa0,0xa9,0xa4,0xd8,0x42,0x9b,0x02,0x2c,0x22,0x19,0x08,0x1f,0xef,0xce,0x5b,0x33,0x79,0x5c,0xae,0x48,0x8d,0x10,0xf5,0xea,0x94,0x38,0x84,0x9e,0xd8,0x35,0x36,0x24,0xf2}, + "", + "", + "" + } + }, + + /* ----- Recipient ignores unrelated outputs (24) ----- */ + { + 1, + { /* input plain seckeys */ + {0x03,0x78,0xe9,0x56,0x85,0xb7,0x45,0x65,0xfa,0x56,0x75,0x1b,0x84,0xa3,0x2d,0xfd,0x18,0x54,0x5d,0x10,0xd6,0x91,0x64,0x1b,0x83,0x72,0xe3,0x21,0x64,0xfa,0xd6,0x6a}, + "", + "" + }, + { /* input plain pubkeys */ + {0x03,0x78,0x2e,0xeb,0x91,0x34,0x31,0xca,0x6e,0x9b,0x8c,0x2f,0xd8,0x0a,0x5f,0x72,0xed,0x20,0x24,0xef,0x72,0xa3,0xc6,0xfb,0x10,0x26,0x3c,0x37,0x99,0x37,0x32,0x33,0x38}, + "", + "" + }, + 1, + { /* input taproot seckeys */ + {0xea,0xdc,0x78,0x16,0x5f,0xf1,0xf8,0xea,0x94,0xad,0x7c,0xfd,0xc5,0x49,0x90,0x73,0x8a,0x4c,0x53,0xf6,0xe0,0x50,0x7b,0x42,0x15,0x42,0x01,0xb8,0xe5,0xdf,0xf3,0xb1}, + "", + "" + }, + { /* input x-only pubkeys */ + {0x5a,0x1e,0x61,0xf8,0x98,0x17,0x30,0x40,0xe2,0x06,0x16,0xd4,0x3e,0x9f,0x49,0x6f,0xba,0x90,0x33,0x8a,0x39,0xfa,0xa1,0xed,0x98,0xfc,0xba,0xee,0xe4,0xdd,0x9b,0xe5}, + "", + "" + }, + /* smallest outpoint */ + {0x16,0x9e,0x1e,0x83,0xe9,0x30,0x85,0x33,0x91,0xbc,0x6f,0x35,0xf6,0x05,0xc6,0x75,0x4c,0xfe,0xad,0x57,0xcf,0x83,0x87,0x63,0x9d,0x3b,0x40,0x96,0xc5,0x4f,0x18,0xf4,0x00,0x00,0x00,0x00}, + 1, + { /* recipient pubkeys (address data) */ + { + {0x02,0x06,0x2d,0x49,0xff,0xc0,0x27,0x87,0xd5,0x86,0xc6,0x08,0xdf,0xbe,0xc1,0x84,0xaa,0x91,0xa6,0x59,0x7d,0x97,0xb4,0x63,0xea,0x5c,0x6b,0xab,0xd9,0xd1,0x7a,0x95,0xa3}, + {0x03,0x81,0xeb,0x9a,0x9a,0x9e,0xc7,0x39,0xd5,0x27,0xc1,0x63,0x1b,0x31,0xb4,0x21,0x56,0x6f,0x5c,0x2a,0x47,0xb4,0xab,0x5b,0x1f,0x6a,0x68,0x6d,0xfb,0x68,0xea,0xb7,0x16}, + }, + { + "", + "", + }, + { + "", + "", + }, + { + "", + "", + } + }, + 1, + 1, + { /* recipient outputs */ + { + {0x84,0x17,0x92,0xc3,0x3c,0x9d,0xc6,0x19,0x3e,0x76,0x74,0x41,0x34,0x12,0x5d,0x40,0xad,0xd8,0xf2,0xf4,0xa9,0x64,0x75,0xf2,0x8b,0xa1,0x50,0xbe,0x03,0x2d,0x64,0xe8}, + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + }, + /* recipient data (scan and spend seckeys) */ + {0x0f,0x69,0x4e,0x06,0x80,0x28,0xa7,0x17,0xf8,0xaf,0x6b,0x94,0x11,0xf9,0xa1,0x33,0xdd,0x35,0x65,0x25,0x87,0x14,0xcc,0x22,0x65,0x94,0xb3,0x4d,0xb9,0x0c,0x1f,0x2c}, + {0x9d,0x6a,0xd8,0x55,0xce,0x34,0x17,0xef,0x84,0xe8,0x36,0x89,0x2e,0x5a,0x56,0x39,0x2b,0xfb,0xa0,0x5f,0xa5,0xd9,0x7c,0xce,0xa3,0x0e,0x26,0x6f,0x54,0x0e,0x08,0xb3}, + 2, + { /* outputs to scan */ + {0x84,0x17,0x92,0xc3,0x3c,0x9d,0xc6,0x19,0x3e,0x76,0x74,0x41,0x34,0x12,0x5d,0x40,0xad,0xd8,0xf2,0xf4,0xa9,0x64,0x75,0xf2,0x8b,0xa1,0x50,0xbe,0x03,0x2d,0x64,0xe8}, + {0x78,0x2e,0xeb,0x91,0x34,0x31,0xca,0x6e,0x9b,0x8c,0x2f,0xd8,0x0a,0x5f,0x72,0xed,0x20,0x24,0xef,0x72,0xa3,0xc6,0xfb,0x10,0x26,0x3c,0x37,0x99,0x37,0x32,0x33,0x38}, + "", + "" + }, + 0, {0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}, /* labels */ + /* expected output data (pubkeys and seckey tweaks) */ + 0, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + } + }, + + /* ----- Input keys sum up to zero / point at infinity: sending fails, receiver skips tx (25) ----- */ + { + 2, + { /* input plain seckeys */ + {0xa6,0xdf,0x6a,0x0b,0xb4,0x48,0x99,0x2a,0x30,0x1d,0xf4,0x25,0x8e,0x06,0xa8,0x9f,0xe7,0xcf,0x71,0x46,0xf5,0x9a,0xc3,0xbd,0x5f,0xf2,0x60,0x83,0xac,0xb2,0x2c,0xeb}, + {0x59,0x20,0x95,0xf4,0x4b,0xb7,0x66,0xd5,0xcf,0xe2,0x0b,0xda,0x71,0xf9,0x57,0x5e,0xd2,0xdf,0x6b,0x9f,0xb9,0xad,0xdc,0x7e,0x5f,0xdf,0xfe,0x09,0x23,0x84,0x14,0x56}, + "" + }, + { /* input plain pubkeys */ + {0x02,0x55,0x7e,0xf3,0xe5,0x5b,0x0a,0x52,0x48,0x9b,0x44,0x54,0xc1,0x16,0x9e,0x06,0xbd,0xea,0x43,0x68,0x7a,0x69,0xc1,0xf1,0x90,0xeb,0x50,0x78,0x16,0x44,0xab,0x69,0x75}, + {0x03,0x55,0x7e,0xf3,0xe5,0x5b,0x0a,0x52,0x48,0x9b,0x44,0x54,0xc1,0x16,0x9e,0x06,0xbd,0xea,0x43,0x68,0x7a,0x69,0xc1,0xf1,0x90,0xeb,0x50,0x78,0x16,0x44,0xab,0x69,0x75}, + "" + }, + 0, + { /* input taproot seckeys */ + "", + "", + "" + }, + { /* input x-only pubkeys */ + "", + "", + "" + }, + /* smallest outpoint */ + {0x4e,0x17,0x94,0x8a,0xbc,0xde,0x5c,0x24,0x71,0xc0,0x45,0x0f,0xc4,0x18,0x54,0x56,0x6e,0x3c,0x67,0xf2,0x06,0xf4,0xaf,0x80,0xae,0x16,0x5e,0xb2,0x47,0x61,0x28,0x3a,0x00,0x00,0x00,0x00}, + 1, + { /* recipient pubkeys (address data) */ + { + {0x02,0xc6,0x04,0x7f,0x94,0x41,0xed,0x7d,0x6d,0x30,0x45,0x40,0x6e,0x95,0xc0,0x7c,0xd8,0x5c,0x77,0x8e,0x4b,0x8c,0xef,0x3c,0xa7,0xab,0xac,0x09,0xb9,0x5c,0x70,0x9e,0xe5}, + {0x02,0x79,0xbe,0x66,0x7e,0xf9,0xdc,0xbb,0xac,0x55,0xa0,0x62,0x95,0xce,0x87,0x0b,0x07,0x02,0x9b,0xfc,0xdb,0x2d,0xce,0x28,0xd9,0x59,0xf2,0x81,0x5b,0x16,0xf8,0x17,0x98}, + }, + { + "", + "", + }, + { + "", + "", + }, + { + "", + "", + } + }, + 1, + 0, + { /* recipient outputs */ + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + }, + /* recipient data (scan and spend seckeys) */ + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02}, + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01}, + 1, + { /* outputs to scan */ + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, + "", + "", + "" + }, + 0, {0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}, /* labels */ + /* expected output data (pubkeys and seckey tweaks) */ + 0, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + }, + { + "", + "", + "", + "" + } + } + +}; diff --git a/src/secp256k1.c b/src/secp256k1.c index 72d725a74e0fd6..494e0eb56d71e6 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -76,7 +76,7 @@ const secp256k1_context *secp256k1_context_no_precomp = &secp256k1_context_stati /* 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. Functions that need specific + * 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) { @@ -544,7 +544,7 @@ static int secp256k1_ecdsa_sign_inner(const secp256k1_context* ctx, secp256k1_sc break; } is_nonce_valid = secp256k1_scalar_set_b32_seckey(&non, nonce32); - /* The nonce is still secret here, but it being invalid is less likely than 1:2^255. */ + /* The nonce is still secret here, but it being invalid is is less likely than 1:2^255. */ secp256k1_declassify(ctx, &is_nonce_valid, sizeof(is_nonce_valid)); if (is_nonce_valid) { ret = secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, r, s, &sec, &msg, &non, recid); @@ -833,3 +833,7 @@ int secp256k1_tagged_sha256(const secp256k1_context* ctx, unsigned char *hash32, #ifdef ENABLE_MODULE_ELLSWIFT # include "modules/ellswift/main_impl.h" #endif + +#ifdef ENABLE_MODULE_SILENTPAYMENTS +# include "modules/silentpayments/main_impl.h" +#endif diff --git a/src/testrand.h b/src/testrand.h index 3c1ed3d45dccf9..721099d0392599 100644 --- a/src/testrand.h +++ b/src/testrand.h @@ -12,37 +12,37 @@ /* A non-cryptographic RNG used only for test infrastructure. */ /** Seed the pseudorandom number generator for testing. */ -SECP256K1_INLINE static void testrand_seed(const unsigned char *seed16); +SECP256K1_INLINE static void secp256k1_testrand_seed(const unsigned char *seed16); /** Generate a pseudorandom number in the range [0..2**32-1]. */ -SECP256K1_INLINE static uint32_t testrand32(void); +SECP256K1_INLINE static uint32_t secp256k1_testrand32(void); /** Generate a pseudorandom number in the range [0..2**64-1]. */ -SECP256K1_INLINE static uint64_t testrand64(void); +SECP256K1_INLINE static uint64_t secp256k1_testrand64(void); /** Generate a pseudorandom number in the range [0..2**bits-1]. Bits must be 1 or * more. */ -SECP256K1_INLINE static uint64_t testrand_bits(int bits); +SECP256K1_INLINE static uint64_t secp256k1_testrand_bits(int bits); /** Generate a pseudorandom number in the range [0..range-1]. */ -static uint32_t testrand_int(uint32_t range); +static uint32_t secp256k1_testrand_int(uint32_t range); /** Generate a pseudorandom 32-byte array. */ -static void testrand256(unsigned char *b32); +static void secp256k1_testrand256(unsigned char *b32); /** Generate a pseudorandom 32-byte array with long sequences of zero and one bits. */ -static void testrand256_test(unsigned char *b32); +static void secp256k1_testrand256_test(unsigned char *b32); /** Generate pseudorandom bytes with long sequences of zero and one bits. */ -static void testrand_bytes_test(unsigned char *bytes, size_t len); +static void secp256k1_testrand_bytes_test(unsigned char *bytes, size_t len); /** Flip a single random bit in a byte array */ -static void testrand_flip(unsigned char *b, size_t len); +static void secp256k1_testrand_flip(unsigned char *b, size_t len); /** Initialize the test RNG using (hex encoded) array up to 16 bytes, or randomly if hexseed is NULL. */ -static void testrand_init(const char* hexseed); +static void secp256k1_testrand_init(const char* hexseed); /** Print final test information. */ -static void testrand_finish(void); +static void secp256k1_testrand_finish(void); #endif /* SECP256K1_TESTRAND_H */ diff --git a/src/testrand_impl.h b/src/testrand_impl.h index 07564f7f3f8a1c..fe9762043556d3 100644 --- a/src/testrand_impl.h +++ b/src/testrand_impl.h @@ -17,7 +17,7 @@ static uint64_t secp256k1_test_state[4]; -SECP256K1_INLINE static void testrand_seed(const unsigned char *seed16) { +SECP256K1_INLINE static void secp256k1_testrand_seed(const unsigned char *seed16) { static const unsigned char PREFIX[19] = "secp256k1 test init"; unsigned char out32[32]; secp256k1_sha256 hash; @@ -40,7 +40,7 @@ SECP256K1_INLINE static uint64_t rotl(const uint64_t x, int k) { return (x << k) | (x >> (64 - k)); } -SECP256K1_INLINE static uint64_t testrand64(void) { +SECP256K1_INLINE static uint64_t secp256k1_testrand64(void) { /* Test-only Xoshiro256++ RNG. See https://prng.di.unimi.it/ */ const uint64_t result = rotl(secp256k1_test_state[0] + secp256k1_test_state[3], 23) + secp256k1_test_state[0]; const uint64_t t = secp256k1_test_state[1] << 17; @@ -53,16 +53,16 @@ SECP256K1_INLINE static uint64_t testrand64(void) { return result; } -SECP256K1_INLINE static uint64_t testrand_bits(int bits) { +SECP256K1_INLINE static uint64_t secp256k1_testrand_bits(int bits) { if (bits == 0) return 0; - return testrand64() >> (64 - bits); + return secp256k1_testrand64() >> (64 - bits); } -SECP256K1_INLINE static uint32_t testrand32(void) { - return testrand64() >> 32; +SECP256K1_INLINE static uint32_t secp256k1_testrand32(void) { + return secp256k1_testrand64() >> 32; } -static uint32_t testrand_int(uint32_t range) { +static uint32_t secp256k1_testrand_int(uint32_t range) { uint32_t mask = 0; uint32_t range_copy; /* Reduce range by 1, changing its meaning to "maximum value". */ @@ -76,15 +76,15 @@ static uint32_t testrand_int(uint32_t range) { } /* Generation loop. */ while (1) { - uint32_t val = testrand64() & mask; + uint32_t val = secp256k1_testrand64() & mask; if (val <= range) return val; } } -static void testrand256(unsigned char *b32) { +static void secp256k1_testrand256(unsigned char *b32) { int i; for (i = 0; i < 4; ++i) { - uint64_t val = testrand64(); + uint64_t val = secp256k1_testrand64(); b32[0] = val; b32[1] = val >> 8; b32[2] = val >> 16; @@ -97,14 +97,14 @@ static void testrand256(unsigned char *b32) { } } -static void testrand_bytes_test(unsigned char *bytes, size_t len) { +static void secp256k1_testrand_bytes_test(unsigned char *bytes, size_t len) { size_t bits = 0; memset(bytes, 0, len); while (bits < len * 8) { int now; uint32_t val; - now = 1 + (testrand_bits(6) * testrand_bits(5) + 16) / 31; - val = testrand_bits(1); + now = 1 + (secp256k1_testrand_bits(6) * secp256k1_testrand_bits(5) + 16) / 31; + val = secp256k1_testrand_bits(1); while (now > 0 && bits < len * 8) { bytes[bits / 8] |= val << (bits % 8); now--; @@ -113,15 +113,15 @@ static void testrand_bytes_test(unsigned char *bytes, size_t len) { } } -static void testrand256_test(unsigned char *b32) { - testrand_bytes_test(b32, 32); +static void secp256k1_testrand256_test(unsigned char *b32) { + secp256k1_testrand_bytes_test(b32, 32); } -static void testrand_flip(unsigned char *b, size_t len) { - b[testrand_int(len)] ^= (1 << testrand_bits(3)); +static void secp256k1_testrand_flip(unsigned char *b, size_t len) { + b[secp256k1_testrand_int(len)] ^= (1 << secp256k1_testrand_bits(3)); } -static void testrand_init(const char* hexseed) { +static void secp256k1_testrand_init(const char* hexseed) { unsigned char seed16[16] = {0}; if (hexseed && strlen(hexseed) != 0) { int pos = 0; @@ -155,12 +155,12 @@ static void testrand_init(const char* hexseed) { } printf("random seed = %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", seed16[0], seed16[1], seed16[2], seed16[3], seed16[4], seed16[5], seed16[6], seed16[7], seed16[8], seed16[9], seed16[10], seed16[11], seed16[12], seed16[13], seed16[14], seed16[15]); - testrand_seed(seed16); + secp256k1_testrand_seed(seed16); } -static void testrand_finish(void) { +static void secp256k1_testrand_finish(void) { unsigned char run32[32]; - testrand256(run32); + secp256k1_testrand256(run32); printf("random run = %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", run32[0], run32[1], run32[2], run32[3], run32[4], run32[5], run32[6], run32[7], run32[8], run32[9], run32[10], run32[11], run32[12], run32[13], run32[14], run32[15]); } diff --git a/src/tests.c b/src/tests.c index 6b401e52c0e56e..6314fcedea6fe9 100644 --- a/src/tests.c +++ b/src/tests.c @@ -96,6 +96,122 @@ static void uncounting_illegal_callback_fn(const char* str, void* data) { (*p)--; } +static void random_field_element_magnitude(secp256k1_fe *fe, int m) { + secp256k1_fe zero; + int n = secp256k1_testrand_int(m + 1); + secp256k1_fe_normalize(fe); + if (n == 0) { + return; + } + secp256k1_fe_clear(&zero); + secp256k1_fe_negate(&zero, &zero, 0); + secp256k1_fe_mul_int_unchecked(&zero, n - 1); + secp256k1_fe_add(fe, &zero); +#ifdef VERIFY + CHECK(fe->magnitude == n); +#endif +} + +static void random_fe_test(secp256k1_fe *x) { + unsigned char bin[32]; + do { + secp256k1_testrand256_test(bin); + if (secp256k1_fe_set_b32_limit(x, bin)) { + return; + } + } while(1); +} + +static void random_fe_non_zero_test(secp256k1_fe *fe) { + do { + random_fe_test(fe); + } while(secp256k1_fe_is_zero(fe)); +} + +static void random_fe_magnitude(secp256k1_fe *fe) { + random_field_element_magnitude(fe, 8); +} + +static void random_ge_x_magnitude(secp256k1_ge *ge) { + random_field_element_magnitude(&ge->x, SECP256K1_GE_X_MAGNITUDE_MAX); +} + +static void random_ge_y_magnitude(secp256k1_ge *ge) { + random_field_element_magnitude(&ge->y, SECP256K1_GE_Y_MAGNITUDE_MAX); +} + +static void random_gej_x_magnitude(secp256k1_gej *gej) { + random_field_element_magnitude(&gej->x, SECP256K1_GEJ_X_MAGNITUDE_MAX); +} + +static void random_gej_y_magnitude(secp256k1_gej *gej) { + random_field_element_magnitude(&gej->y, SECP256K1_GEJ_Y_MAGNITUDE_MAX); +} + +static void random_gej_z_magnitude(secp256k1_gej *gej) { + random_field_element_magnitude(&gej->z, SECP256K1_GEJ_Z_MAGNITUDE_MAX); +} + +static void random_group_element_test(secp256k1_ge *ge) { + secp256k1_fe fe; + do { + random_fe_test(&fe); + if (secp256k1_ge_set_xo_var(ge, &fe, secp256k1_testrand_bits(1))) { + secp256k1_fe_normalize(&ge->y); + break; + } + } while(1); + ge->infinity = 0; +} + +static void random_group_element_jacobian_test(secp256k1_gej *gej, const secp256k1_ge *ge) { + secp256k1_fe z2, z3; + random_fe_non_zero_test(&gej->z); + secp256k1_fe_sqr(&z2, &gej->z); + secp256k1_fe_mul(&z3, &z2, &gej->z); + secp256k1_fe_mul(&gej->x, &ge->x, &z2); + secp256k1_fe_mul(&gej->y, &ge->y, &z3); + gej->infinity = ge->infinity; +} + +static void random_gej_test(secp256k1_gej *gej) { + secp256k1_ge ge; + random_group_element_test(&ge); + random_group_element_jacobian_test(gej, &ge); +} + +static void random_scalar_order_test(secp256k1_scalar *num) { + do { + unsigned char b32[32]; + int overflow = 0; + secp256k1_testrand256_test(b32); + secp256k1_scalar_set_b32(num, b32, &overflow); + if (overflow || secp256k1_scalar_is_zero(num)) { + continue; + } + break; + } while(1); +} + +static void random_scalar_order(secp256k1_scalar *num) { + do { + unsigned char b32[32]; + int overflow = 0; + secp256k1_testrand256(b32); + secp256k1_scalar_set_b32(num, b32, &overflow); + if (overflow || secp256k1_scalar_is_zero(num)) { + continue; + } + break; + } while(1); +} + +static void random_scalar_order_b32(unsigned char *b32) { + secp256k1_scalar num; + random_scalar_order(&num); + secp256k1_scalar_get_b32(b32, &num); +} + static void run_xoshiro256pp_tests(void) { { size_t i; @@ -117,9 +233,9 @@ static void run_xoshiro256pp_tests(void) { 0x4C, 0xCC, 0xC1, 0x18, 0xB2, 0xD8, 0x8F, 0xEF, 0x43, 0x26, 0x15, 0x57, 0x37, 0x00, 0xEF, 0x30, }; - testrand_seed(seed16); + secp256k1_testrand_seed(seed16); for (i = 0; i < 17; i++) { - testrand256(buf32); + secp256k1_testrand256(buf32); } CHECK(secp256k1_memcmp_var(buf32, buf32_expected, sizeof(buf32)) == 0); } @@ -328,14 +444,14 @@ static void run_proper_context_tests(int use_prealloc) { CHECK(context_eq(my_ctx, my_ctx_fresh)); /*** attempt to use them ***/ - testutil_random_scalar_order_test(&msg); - testutil_random_scalar_order_test(&key); + random_scalar_order_test(&msg); + random_scalar_order_test(&key); secp256k1_ecmult_gen(&my_ctx->ecmult_gen_ctx, &pubj, &key); secp256k1_ge_set_gej(&pub, &pubj); /* obtain a working nonce */ do { - testutil_random_scalar_order_test(&nonce); + random_scalar_order_test(&nonce); } while(!secp256k1_ecdsa_sig_sign(&my_ctx->ecmult_gen_ctx, &sigr, &sigs, &key, &msg, &nonce, NULL)); /* try signing */ @@ -492,7 +608,7 @@ static void run_sha256_known_output_tests(void) { CHECK(secp256k1_memcmp_var(out, outputs[i], 32) == 0); /* 2. Run: split the input bytestrings randomly before writing */ if (strlen(inputs[i]) > 0) { - int split = testrand_int(strlen(inputs[i])); + int split = secp256k1_testrand_int(strlen(inputs[i])); secp256k1_sha256_initialize(&hasher); j = repeat[i]; while (j > 0) { @@ -653,7 +769,7 @@ static void run_hmac_sha256_tests(void) { secp256k1_hmac_sha256_finalize(&hasher, out); CHECK(secp256k1_memcmp_var(out, outputs[i], 32) == 0); if (strlen(inputs[i]) > 0) { - int split = testrand_int(strlen(inputs[i])); + int split = secp256k1_testrand_int(strlen(inputs[i])); secp256k1_hmac_sha256_initialize(&hasher, (const unsigned char*)(keys[i]), strlen(keys[i])); secp256k1_hmac_sha256_write(&hasher, (const unsigned char*)(inputs[i]), split); secp256k1_hmac_sha256_write(&hasher, (const unsigned char*)(inputs[i] + split), strlen(inputs[i]) - split); @@ -853,7 +969,7 @@ static void signed30_to_uint16(uint16_t* out, const secp256k1_modinv32_signed30* static void mutate_sign_signed30(secp256k1_modinv32_signed30* x) { int i; for (i = 0; i < 16; ++i) { - int pos = testrand_bits(3); + int pos = secp256k1_testrand_bits(3); if (x->v[pos] > 0 && x->v[pos + 1] <= 0x3fffffff) { x->v[pos] -= 0x40000000; x->v[pos + 1] += 1; @@ -945,7 +1061,7 @@ 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) { - int pos = testrand_bits(2); + int pos = secp256k1_testrand_bits(2); if (x->v[pos] > 0 && x->v[pos + 1] <= M62) { x->v[pos] -= (M62 + 1); x->v[pos + 1] += 1; @@ -1658,8 +1774,8 @@ static void run_modinv_tests(void) { /* generate random xd and md, so that md is odd, md>1, xd 256) { now = 256 - i; } @@ -2078,7 +2194,7 @@ static void scalar_test(void) { secp256k1_scalar b; int i; /* Test add_bit. */ - int bit = testrand_bits(8); + int bit = secp256k1_testrand_bits(8); secp256k1_scalar_set_int(&b, 1); CHECK(secp256k1_scalar_is_one(&b)); for (i = 0; i < bit; i++) { @@ -2171,7 +2287,7 @@ static void run_scalar_set_b32_seckey_tests(void) { secp256k1_scalar s2; /* Usually set_b32 and set_b32_seckey give the same result */ - testutil_random_scalar_order_b32(b32); + random_scalar_order_b32(b32); secp256k1_scalar_set_b32(&s1, b32, NULL); CHECK(secp256k1_scalar_set_b32_seckey(&s2, b32) == 1); CHECK(secp256k1_scalar_eq(&s1, &s2) == 1); @@ -2832,7 +2948,7 @@ static void run_scalar_tests(void) { static void random_fe_non_square(secp256k1_fe *ns) { secp256k1_fe r; - testutil_random_fe_non_zero(ns); + random_fe_non_zero(ns); if (secp256k1_fe_sqrt(&r, ns)) { secp256k1_fe_negate(ns, ns, 1); } @@ -3009,12 +3125,12 @@ static void run_field_misc(void) { for (i = 0; i < 1000 * COUNT; i++) { secp256k1_fe_storage xs, ys, zs; if (i & 1) { - testutil_random_fe(&x); + random_fe(&x); } else { - testutil_random_fe_test(&x); + random_fe_test(&x); } - testutil_random_fe_non_zero(&y); - v = testrand_bits(15); + 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 */ @@ -3152,14 +3268,14 @@ static void run_fe_mul(void) { int i; for (i = 0; i < 100 * COUNT; ++i) { secp256k1_fe a, b, c, d; - testutil_random_fe(&a); - testutil_random_fe_magnitude(&a, 8); - testutil_random_fe(&b); - testutil_random_fe_magnitude(&b, 8); - testutil_random_fe_test(&c); - testutil_random_fe_magnitude(&c, 8); - testutil_random_fe_test(&d); - testutil_random_fe_magnitude(&d, 8); + random_fe(&a); + random_fe_magnitude(&a); + random_fe(&b); + random_fe_magnitude(&b); + random_fe_test(&c); + random_fe_magnitude(&c); + random_fe_test(&d); + random_fe_magnitude(&d); test_fe_mul(&a, &a, 1); test_fe_mul(&c, &c, 1); test_fe_mul(&a, &b, 0); @@ -3181,7 +3297,7 @@ static void run_sqr(void) { secp256k1_fe_normalize(&x); /* Check that (x+y)*(x-y) = x^2 - y*2 for some random values y */ - testutil_random_fe_test(&y); + random_fe_test(&y); lhs = x; secp256k1_fe_add(&lhs, &y); /* lhs = x+y */ @@ -3235,7 +3351,7 @@ static void run_sqrt(void) { int j; random_fe_non_square(&ns); for (j = 0; j < COUNT; j++) { - testutil_random_fe(&x); + random_fe(&x); secp256k1_fe_sqr(&s, &x); CHECK(secp256k1_fe_is_square_var(&s)); test_sqrt(&s, &x); @@ -3549,7 +3665,7 @@ static 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) { - (testrand ? testrand256_test : testrand256)(b32); + (testrand ? secp256k1_testrand256_test : secp256k1_testrand256)(b32); secp256k1_scalar_set_b32(&x_scalar, b32, NULL); secp256k1_fe_set_b32_mod(&x_fe, b32); for (var = 0; var <= 1; ++var) { @@ -3615,8 +3731,8 @@ static void test_hsort(size_t element_len) { /* Test hsort with array of random length n */ for (i = 0; i < COUNT; i++) { - int n = testrand_int(NUM); - testrand_bytes_test(elements, n*element_len); + int n = secp256k1_testrand_int(NUM); + secp256k1_testrand_bytes_test(elements, n*element_len); secp256k1_hsort(elements, n, element_len, test_hsort_cmp, &data); test_hsort_is_sorted(elements, n, element_len); } @@ -3676,7 +3792,7 @@ static void test_ge(void) { for (i = 0; i < runs; i++) { int j, k; secp256k1_ge g; - testutil_random_ge_test(&g); + random_group_element_test(&g); if (i >= runs - 2) { secp256k1_ge_mul_lambda(&g, &ge[1]); CHECK(!secp256k1_ge_eq_var(&g, &ge[1])); @@ -3689,15 +3805,15 @@ static void test_ge(void) { secp256k1_ge_neg(&ge[3 + 4 * i], &g); secp256k1_ge_neg(&ge[4 + 4 * i], &g); secp256k1_gej_set_ge(&gej[1 + 4 * i], &ge[1 + 4 * i]); - testutil_random_ge_jacobian_test(&gej[2 + 4 * i], &ge[2 + 4 * i]); + random_group_element_jacobian_test(&gej[2 + 4 * i], &ge[2 + 4 * i]); secp256k1_gej_set_ge(&gej[3 + 4 * i], &ge[3 + 4 * i]); - testutil_random_ge_jacobian_test(&gej[4 + 4 * i], &ge[4 + 4 * i]); + random_group_element_jacobian_test(&gej[4 + 4 * i], &ge[4 + 4 * i]); for (j = 0; j < 4; j++) { - testutil_random_ge_x_magnitude(&ge[1 + j + 4 * i]); - testutil_random_ge_y_magnitude(&ge[1 + j + 4 * i]); - testutil_random_gej_x_magnitude(&gej[1 + j + 4 * i]); - testutil_random_gej_y_magnitude(&gej[1 + j + 4 * i]); - testutil_random_gej_z_magnitude(&gej[1 + j + 4 * i]); + random_ge_x_magnitude(&ge[1 + j + 4 * i]); + random_ge_y_magnitude(&ge[1 + j + 4 * i]); + random_gej_x_magnitude(&gej[1 + j + 4 * i]); + random_gej_y_magnitude(&gej[1 + j + 4 * i]); + random_gej_z_magnitude(&gej[1 + j + 4 * i]); } for (j = 0; j < 4; ++j) { @@ -3712,14 +3828,14 @@ static void test_ge(void) { } /* Generate random zf, and zfi2 = 1/zf^2, zfi3 = 1/zf^3 */ - testutil_random_fe_non_zero_test(&zf); - testutil_random_fe_magnitude(&zf, 8); + random_fe_non_zero_test(&zf); + random_fe_magnitude(&zf); secp256k1_fe_inv_var(&zfi3, &zf); secp256k1_fe_sqr(&zfi2, &zfi3); secp256k1_fe_mul(&zfi3, &zfi3, &zfi2); /* Generate random r */ - testutil_random_fe_non_zero_test(&r); + random_fe_non_zero_test(&r); for (i1 = 0; i1 < 1 + 4 * runs; i1++) { int i2; @@ -3749,8 +3865,8 @@ static void test_ge(void) { secp256k1_ge ge2_zfi = ge[i2]; /* the second term with x and y rescaled for z = 1/zf */ secp256k1_fe_mul(&ge2_zfi.x, &ge2_zfi.x, &zfi2); secp256k1_fe_mul(&ge2_zfi.y, &ge2_zfi.y, &zfi3); - testutil_random_ge_x_magnitude(&ge2_zfi); - testutil_random_ge_y_magnitude(&ge2_zfi); + random_ge_x_magnitude(&ge2_zfi); + random_ge_y_magnitude(&ge2_zfi); secp256k1_gej_add_zinv_var(&resj, &gej[i1], &ge2_zfi, &zf); CHECK(secp256k1_gej_eq_ge_var(&resj, &ref)); } @@ -3806,7 +3922,7 @@ static void test_ge(void) { gej_shuffled[i] = gej[i]; } for (i = 0; i < 4 * runs + 1; i++) { - int swap = i + testrand_int(4 * runs + 1 - i); + int swap = i + secp256k1_testrand_int(4 * runs + 1 - i); if (swap != i) { secp256k1_gej t = gej_shuffled[i]; gej_shuffled[i] = gej_shuffled[swap]; @@ -3826,7 +3942,7 @@ static void test_ge(void) { secp256k1_ge_set_all_gej_var(ge_set_all, gej, 4 * runs + 1); for (i = 0; i < 4 * runs + 1; i++) { secp256k1_fe s; - testutil_random_fe_non_zero(&s); + random_fe_non_zero(&s); secp256k1_gej_rescale(&gej[i], &s); CHECK(secp256k1_gej_eq_ge_var(&gej[i], &ge_set_all[i])); } @@ -3859,7 +3975,7 @@ static void test_ge(void) { /* Test batch gej -> ge conversion with many infinities. */ for (i = 0; i < 4 * runs + 1; i++) { int odd; - testutil_random_ge_test(&ge[i]); + random_group_element_test(&ge[i]); odd = secp256k1_fe_is_odd(&ge[i].x); CHECK(odd == 0 || odd == 1); /* randomly set half the points to infinity */ @@ -3896,7 +4012,7 @@ static void test_intialized_inf(void) { secp256k1_fe zinv; /* Test that adding P+(-P) results in a fully initialized infinity*/ - testutil_random_ge_test(&p); + random_group_element_test(&p); secp256k1_gej_set_ge(&pj, &p); secp256k1_gej_neg(&npj, &pj); @@ -4009,14 +4125,14 @@ static void run_gej(void) { secp256k1_gej_set_infinity(&b); test_gej_cmov(&a, &b); - testutil_random_gej_test(&a); + random_gej_test(&a); test_gej_cmov(&a, &b); test_gej_cmov(&b, &a); b = a; test_gej_cmov(&a, &b); - testutil_random_gej_test(&b); + random_gej_test(&b); test_gej_cmov(&a, &b); test_gej_cmov(&b, &a); } @@ -4024,12 +4140,12 @@ static void run_gej(void) { /* Tests for secp256k1_gej_eq_var */ for (i = 0; i < COUNT; i++) { secp256k1_fe fe; - testutil_random_gej_test(&a); - testutil_random_gej_test(&b); + random_gej_test(&a); + random_gej_test(&b); CHECK(!secp256k1_gej_eq_var(&a, &b)); b = a; - testutil_random_fe_non_zero_test(&fe); + random_fe_non_zero_test(&fe); secp256k1_gej_rescale(&a, &fe); CHECK(secp256k1_gej_eq_var(&a, &b)); } @@ -4046,7 +4162,7 @@ static void test_ec_combine(void) { int i; for (i = 1; i <= 6; i++) { secp256k1_scalar s; - testutil_random_scalar_order_test(&s); + random_scalar_order_test(&s); secp256k1_scalar_add(&sum, &sum, &s); secp256k1_ecmult_gen(&CTX->ecmult_gen_ctx, &Qj, &s); secp256k1_ge_set_gej(&Q, &Qj); @@ -4106,7 +4222,7 @@ static void run_group_decompress(void) { int i; for (i = 0; i < COUNT * 4; i++) { secp256k1_fe fe; - testutil_random_fe_test(&fe); + random_fe_test(&fe); test_group_decompress(&fe); } } @@ -4254,7 +4370,7 @@ static void test_point_times_order(const secp256k1_gej *point) { secp256k1_ge res3; unsigned char pub[65]; size_t psize = 65; - testutil_random_scalar_order_test(&x); + random_scalar_order_test(&x); secp256k1_scalar_negate(&nx, &x); secp256k1_ecmult(&res1, point, &x, &x); /* calc res1 = x * point + x * G; */ secp256k1_ecmult(&res2, point, &nx, &nx); /* calc res2 = (order - x) * point + (order - x) * G; */ @@ -4315,13 +4431,13 @@ static void test_ecmult_target(const secp256k1_scalar* target, int mode) { secp256k1_gej pj, p1j, p2j, ptj; /* Generate random n1,n2 such that n1+n2 = -target. */ - testutil_random_scalar_order_test(&n1); + random_scalar_order_test(&n1); secp256k1_scalar_add(&n2, &n1, target); secp256k1_scalar_negate(&n2, &n2); /* Generate a random input point. */ if (mode != 0) { - testutil_random_ge_test(&p); + random_group_element_test(&p); secp256k1_gej_set_ge(&pj, &p); } @@ -4413,8 +4529,8 @@ static void ecmult_const_commutativity(void) { secp256k1_gej res2; secp256k1_ge mid1; secp256k1_ge mid2; - testutil_random_scalar_order_test(&a); - testutil_random_scalar_order_test(&b); + random_scalar_order_test(&a); + random_scalar_order_test(&b); secp256k1_ecmult_const(&res1, &secp256k1_ge_const_g, &a); secp256k1_ecmult_const(&res2, &secp256k1_ge_const_g, &b); @@ -4435,9 +4551,9 @@ static void ecmult_const_mult_zero_one(void) { secp256k1_ge point; secp256k1_ge inf; - testutil_random_scalar_order_test(&s); + random_scalar_order_test(&s); secp256k1_scalar_negate(&negone, &secp256k1_scalar_one); - testutil_random_ge_test(&point); + random_group_element_test(&point); secp256k1_ge_set_infinity(&inf); /* 0*point */ @@ -4490,7 +4606,7 @@ static void ecmult_const_edges(void) { secp256k1_scalar_add(&q, &q, &scalars_near_split_bounds[i - 1]); secp256k1_scalar_add(&q, &q, &scalars_near_split_bounds[i - 1]); } - testutil_random_ge_test(&point); + random_group_element_test(&point); secp256k1_ecmult_const(&res, &point, &q); ecmult_const_check_result(&point, &q, &res); } @@ -4507,12 +4623,12 @@ static void ecmult_const_mult_xonly(void) { secp256k1_scalar q; int res; /* Random base point. */ - testutil_random_ge_test(&base); + random_group_element_test(&base); /* Random scalar to multiply it with. */ - testutil_random_scalar_order_test(&q); + random_scalar_order_test(&q); /* If i is odd, n=d*base.x for random non-zero d */ if (i & 1) { - testutil_random_fe_non_zero_test(&d); + random_fe_non_zero_test(&d); secp256k1_fe_mul(&n, &base.x, &d); } else { n = base.x; @@ -4534,14 +4650,14 @@ static void ecmult_const_mult_xonly(void) { secp256k1_fe x, n, d, r; int res; secp256k1_scalar q; - testutil_random_scalar_order_test(&q); + random_scalar_order_test(&q); /* Generate random X coordinate not on the curve. */ do { - testutil_random_fe_test(&x); + random_fe_test(&x); } while (secp256k1_ge_x_on_curve_var(&x)); /* If i is odd, n=d*x for random non-zero d. */ if (i & 1) { - testutil_random_fe_non_zero_test(&d); + random_fe_non_zero_test(&d); secp256k1_fe_mul(&n, &x, &d); } else { n = x; @@ -4624,10 +4740,10 @@ static void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi for (ncount = 0; ncount < COUNT; ncount++) { secp256k1_ge ptg; secp256k1_gej ptgj; - testutil_random_scalar_order(&sc[0]); - testutil_random_scalar_order(&sc[1]); + random_scalar_order(&sc[0]); + random_scalar_order(&sc[1]); - testutil_random_ge_test(&ptg); + random_group_element_test(&ptg); secp256k1_gej_set_ge(&ptgj, &ptg); pt[0] = ptg; pt[1] = secp256k1_ge_const_g; @@ -4664,7 +4780,7 @@ static void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi for (j = 0; j < 3; j++) { for (i = 0; i < 32; i++) { - testutil_random_scalar_order(&sc[i]); + random_scalar_order(&sc[i]); secp256k1_ge_set_infinity(&pt[i]); } CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &secp256k1_scalar_zero, ecmult_multi_callback, &data, sizes[j])); @@ -4673,7 +4789,7 @@ static void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi for (j = 0; j < 3; j++) { for (i = 0; i < 32; i++) { - testutil_random_ge_test(&ptg); + random_group_element_test(&ptg); pt[i] = ptg; secp256k1_scalar_set_int(&sc[i], 0); } @@ -4682,9 +4798,9 @@ static void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi } for (j = 0; j < 3; j++) { - testutil_random_ge_test(&ptg); + random_group_element_test(&ptg); for (i = 0; i < 16; i++) { - testutil_random_scalar_order(&sc[2*i]); + random_scalar_order(&sc[2*i]); secp256k1_scalar_negate(&sc[2*i + 1], &sc[2*i]); pt[2 * i] = ptg; pt[2 * i + 1] = ptg; @@ -4693,9 +4809,9 @@ static void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &secp256k1_scalar_zero, ecmult_multi_callback, &data, sizes[j])); CHECK(secp256k1_gej_is_infinity(&r)); - testutil_random_scalar_order(&sc[0]); + random_scalar_order(&sc[0]); for (i = 0; i < 16; i++) { - testutil_random_ge_test(&ptg); + random_group_element_test(&ptg); sc[2*i] = sc[0]; sc[2*i+1] = sc[0]; @@ -4707,13 +4823,13 @@ static void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi CHECK(secp256k1_gej_is_infinity(&r)); } - testutil_random_ge_test(&ptg); + random_group_element_test(&ptg); secp256k1_scalar_set_int(&sc[0], 0); pt[0] = ptg; for (i = 1; i < 32; i++) { pt[i] = ptg; - testutil_random_scalar_order(&sc[i]); + random_scalar_order(&sc[i]); secp256k1_scalar_add(&sc[0], &sc[0], &sc[i]); secp256k1_scalar_negate(&sc[i], &sc[i]); } @@ -4727,11 +4843,11 @@ static void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi size_t i; secp256k1_gej_set_infinity(&r); - testutil_random_scalar_order(&sc[0]); + random_scalar_order(&sc[0]); for (i = 0; i < 20; i++) { secp256k1_ge ptg; sc[i] = sc[0]; - testutil_random_ge_test(&ptg); + random_group_element_test(&ptg); pt[i] = ptg; secp256k1_gej_add_ge_var(&r, &r, &pt[i], NULL); } @@ -4749,9 +4865,9 @@ static void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi secp256k1_scalar rs; secp256k1_scalar_set_int(&rs, 0); - testutil_random_ge_test(&ptg); + random_group_element_test(&ptg); for (i = 0; i < 20; i++) { - testutil_random_scalar_order(&sc[i]); + random_scalar_order(&sc[i]); pt[i] = ptg; secp256k1_scalar_add(&rs, &rs, &sc[i]); } @@ -4764,8 +4880,8 @@ static void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi /* Sanity check that zero scalars don't cause problems */ for (ncount = 0; ncount < 20; ncount++) { - testutil_random_scalar_order(&sc[ncount]); - testutil_random_ge_test(&pt[ncount]); + random_scalar_order(&sc[ncount]); + random_group_element_test(&pt[ncount]); } secp256k1_scalar_clear(&sc[0]); @@ -4786,7 +4902,7 @@ static void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi secp256k1_ge ptg; secp256k1_gej ptgj; - testutil_random_ge_test(&ptg); + random_group_element_test(&ptg); secp256k1_gej_set_ge(&ptgj, &ptg); for(t0i = 0; t0i < TOP; t0i++) { @@ -4857,29 +4973,29 @@ static int test_ecmult_multi_random(secp256k1_scratch *scratch) { int i; /* Which multiplication function to use */ - int fn = testrand_int(3); + int fn = secp256k1_testrand_int(3); secp256k1_ecmult_multi_func ecmult_multi = fn == 0 ? secp256k1_ecmult_multi_var : fn == 1 ? secp256k1_ecmult_strauss_batch_single : secp256k1_ecmult_pippenger_batch_single; /* Simulate exponentially distributed num. */ - int num_bits = 2 + testrand_int(6); + int num_bits = 2 + secp256k1_testrand_int(6); /* Number of (scalar, point) inputs (excluding g). */ - int num = testrand_int((1 << num_bits) + 1); + int num = secp256k1_testrand_int((1 << num_bits) + 1); /* Number of those which are nonzero. */ - int num_nonzero = testrand_int(num + 1); + int num_nonzero = secp256k1_testrand_int(num + 1); /* Whether we're aiming to create an input with nonzero expected result. */ - int nonzero_result = testrand_bits(1); + int nonzero_result = secp256k1_testrand_bits(1); /* Whether we will provide nonzero g multiplicand. In some cases our hand * is forced here based on num_nonzero and nonzero_result. */ int g_nonzero = num_nonzero == 0 ? nonzero_result : num_nonzero == 1 && !nonzero_result ? 1 : - (int)testrand_bits(1); + (int)secp256k1_testrand_bits(1); /* Which g_scalar pointer to pass into ecmult_multi(). */ - const secp256k1_scalar* g_scalar_ptr = (g_nonzero || testrand_bits(1)) ? &g_scalar : NULL; + const secp256k1_scalar* g_scalar_ptr = (g_nonzero || secp256k1_testrand_bits(1)) ? &g_scalar : NULL; /* How many EC multiplications were performed in this function. */ int mults = 0; /* How many randomization steps to apply to the input list. */ - int rands = (int)testrand_bits(3); + int rands = (int)secp256k1_testrand_bits(3); if (rands > num_nonzero) rands = num_nonzero; secp256k1_gej_set_infinity(&expected); @@ -4888,11 +5004,11 @@ static int test_ecmult_multi_random(secp256k1_scratch *scratch) { if (g_nonzero) { /* If g_nonzero, set g_scalar to nonzero value r. */ - testutil_random_scalar_order_test(&g_scalar); + random_scalar_order_test(&g_scalar); if (!nonzero_result) { /* If expected=0 is desired, add a (a*r, -(1/a)*g) term to compensate. */ CHECK(num_nonzero > filled); - testutil_random_scalar_order_test(&sc_tmp); + random_scalar_order_test(&sc_tmp); secp256k1_scalar_mul(&scalars[filled], &sc_tmp, &g_scalar); secp256k1_scalar_inverse_var(&sc_tmp, &sc_tmp); secp256k1_scalar_negate(&sc_tmp, &sc_tmp); @@ -4904,8 +5020,8 @@ static int test_ecmult_multi_random(secp256k1_scratch *scratch) { if (nonzero_result && filled < num_nonzero) { /* If a nonzero result is desired, and there is space, add a random nonzero term. */ - testutil_random_scalar_order_test(&scalars[filled]); - testutil_random_ge_test(&ge_tmp); + random_scalar_order_test(&scalars[filled]); + random_group_element_test(&ge_tmp); secp256k1_gej_set_ge(&gejs[filled], &ge_tmp); ++filled; } @@ -4924,12 +5040,12 @@ static int test_ecmult_multi_random(secp256k1_scratch *scratch) { /* Add entries to scalars,gejs so that there are num of them. All the added entries * either have scalar=0 or point=infinity, so these do not change the expected result. */ while (filled < num) { - if (testrand_bits(1)) { + if (secp256k1_testrand_bits(1)) { secp256k1_gej_set_infinity(&gejs[filled]); - testutil_random_scalar_order_test(&scalars[filled]); + random_scalar_order_test(&scalars[filled]); } else { secp256k1_scalar_set_int(&scalars[filled], 0); - testutil_random_ge_test(&ge_tmp); + random_group_element_test(&ge_tmp); secp256k1_gej_set_ge(&gejs[filled], &ge_tmp); } ++filled; @@ -4943,7 +5059,7 @@ static int test_ecmult_multi_random(secp256k1_scratch *scratch) { secp256k1_scalar v, iv; /* Shuffle the entries. */ for (j = 0; j < num_nonzero; ++j) { - int k = testrand_int(num_nonzero - j); + int k = secp256k1_testrand_int(num_nonzero - j); if (k != 0) { secp256k1_gej gej = gejs[j]; secp256k1_scalar sc = scalars[j]; @@ -4963,7 +5079,7 @@ static int test_ecmult_multi_random(secp256k1_scratch *scratch) { } /* Transform the last input: a*P -> (v*a) * ((1/v)*P). */ CHECK(num_nonzero >= 1); - testutil_random_scalar_order_test(&v); + random_scalar_order_test(&v); secp256k1_scalar_inverse(&iv, &v); secp256k1_scalar_mul(&scalars[num_nonzero - 1], &scalars[num_nonzero - 1], &v); secp256k1_ecmult(&gejs[num_nonzero - 1], &gejs[num_nonzero - 1], &iv, NULL); @@ -4972,7 +5088,7 @@ static int test_ecmult_multi_random(secp256k1_scratch *scratch) { /* Shuffle all entries (0..num-1). */ for (i = 0; i < num; ++i) { - int j = testrand_int(num - i); + int j = secp256k1_testrand_int(num - i); if (j != 0) { secp256k1_gej gej = gejs[i]; secp256k1_scalar sc = scalars[i]; @@ -5002,8 +5118,8 @@ static void test_ecmult_multi_batch_single(secp256k1_ecmult_multi_func ecmult_mu ecmult_multi_data data; secp256k1_scratch *scratch_empty; - testutil_random_ge_test(&pt); - testutil_random_scalar_order(&sc); + random_group_element_test(&pt); + random_scalar_order(&sc); data.sc = ≻ data.pt = &pt; @@ -5034,7 +5150,7 @@ static void test_secp256k1_pippenger_bucket_window_inv(void) { * for a given scratch space. */ static void test_ecmult_multi_pippenger_max_points(void) { - size_t scratch_size = testrand_bits(8); + 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; size_t n_points_supported; @@ -5128,15 +5244,15 @@ static void test_ecmult_multi_batching(void) { secp256k1_gej_set_infinity(&r2); /* Get random scalars and group elements and compute result */ - testutil_random_scalar_order(&scG); + random_scalar_order(&scG); secp256k1_ecmult(&r2, &r2, &secp256k1_scalar_zero, &scG); for(i = 0; i < n_points; i++) { secp256k1_ge ptg; secp256k1_gej ptgj; - testutil_random_ge_test(&ptg); + random_group_element_test(&ptg); secp256k1_gej_set_ge(&ptgj, &ptg); pt[i] = ptg; - testutil_random_scalar_order(&sc[i]); + random_scalar_order(&sc[i]); secp256k1_ecmult(&ptgj, &ptgj, &sc[i], NULL); secp256k1_gej_add_var(&r2, &r2, &ptgj, NULL); } @@ -5348,7 +5464,7 @@ static void run_wnaf(void) { test_fixed_wnaf_small(); /* Random tests */ for (i = 0; i < COUNT; i++) { - testutil_random_scalar_order(&n); + random_scalar_order(&n); test_wnaf(&n, 4+(i%10)); test_fixed_wnaf(&n, 4 + (i % 10)); } @@ -5529,9 +5645,9 @@ static void test_ecmult_gen_blind(void) { secp256k1_gej pgej2; secp256k1_ge p; secp256k1_ge pge; - testutil_random_scalar_order_test(&key); + random_scalar_order_test(&key); secp256k1_ecmult_gen(&CTX->ecmult_gen_ctx, &pgej, &key); - testrand256(seed32); + secp256k1_testrand256(seed32); b = CTX->ecmult_gen_ctx.scalar_offset; p = CTX->ecmult_gen_ctx.ge_offset; secp256k1_ecmult_gen_blind(&CTX->ecmult_gen_ctx, seed32); @@ -5625,7 +5741,7 @@ static void run_endomorphism_tests(void) { for (i = 0; i < 100U * COUNT; ++i) { secp256k1_scalar full; - testutil_random_scalar_order_test(&full); + random_scalar_order_test(&full); test_scalar_split(&full); } for (i = 0; i < sizeof(scalars_near_split_bounds) / sizeof(scalars_near_split_bounds[0]); ++i) { @@ -6211,7 +6327,7 @@ static void run_eckey_negate_test(void) { unsigned char seckey[32]; unsigned char seckey_tmp[32]; - testutil_random_scalar_order_b32(seckey); + random_scalar_order_b32(seckey); memcpy(seckey_tmp, seckey, 32); /* Verify negation changes the key and changes it back */ @@ -6235,7 +6351,7 @@ static void run_eckey_negate_test(void) { /* Negating an overflowing seckey fails and the seckey is zeroed. In this * test, the seckey has 16 random bytes to ensure that ec_seckey_negate * doesn't just set seckey to a constant value in case of failure. */ - testutil_random_scalar_order_b32(seckey); + random_scalar_order_b32(seckey); memset(seckey, 0xFF, 16); memset(seckey_tmp, 0, 32); CHECK(secp256k1_ec_seckey_negate(CTX, seckey) == 0); @@ -6245,7 +6361,7 @@ static void run_eckey_negate_test(void) { static void random_sign(secp256k1_scalar *sigr, secp256k1_scalar *sigs, const secp256k1_scalar *key, const secp256k1_scalar *msg, int *recid) { secp256k1_scalar nonce; do { - testutil_random_scalar_order_test(&nonce); + random_scalar_order_test(&nonce); } while(!secp256k1_ecdsa_sig_sign(&CTX->ecmult_gen_ctx, sigr, sigs, key, msg, &nonce, recid)); } @@ -6257,11 +6373,11 @@ static void test_ecdsa_sign_verify(void) { secp256k1_scalar sigr, sigs; int getrec; int recid; - testutil_random_scalar_order_test(&msg); - testutil_random_scalar_order_test(&key); + random_scalar_order_test(&msg); + random_scalar_order_test(&key); secp256k1_ecmult_gen(&CTX->ecmult_gen_ctx, &pubj, &key); secp256k1_ge_set_gej(&pub, &pubj); - getrec = testrand_bits(1); + getrec = secp256k1_testrand_bits(1); /* The specific way in which this conditional is written sidesteps a potential bug in clang. See the commit messages of the commit that introduced this comment for details. */ if (getrec) { @@ -6354,8 +6470,8 @@ static void test_ecdsa_end_to_end(void) { /* Generate a random key and message. */ { secp256k1_scalar msg, key; - testutil_random_scalar_order_test(&msg); - testutil_random_scalar_order_test(&key); + random_scalar_order_test(&msg); + random_scalar_order_test(&key); secp256k1_scalar_get_b32(privkey, &key); secp256k1_scalar_get_b32(message, &msg); } @@ -6365,7 +6481,7 @@ static void test_ecdsa_end_to_end(void) { CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey, privkey) == 1); /* Verify exporting and importing public key. */ - CHECK(secp256k1_ec_pubkey_serialize(CTX, pubkeyc, &pubkeyclen, &pubkey, 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); @@ -6377,19 +6493,19 @@ static void test_ecdsa_end_to_end(void) { 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, testrand_bits(1) == 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. */ - if (testrand_int(3) == 0) { + if (secp256k1_testrand_int(3) == 0) { int ret1; int ret2; int ret3; unsigned char rnd[32]; unsigned char privkey_tmp[32]; secp256k1_pubkey pubkey2; - testrand256_test(rnd); + 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); @@ -6406,14 +6522,14 @@ static void test_ecdsa_end_to_end(void) { } /* Optionally tweak the keys using multiplication. */ - if (testrand_int(3) == 0) { + if (secp256k1_testrand_int(3) == 0) { int ret1; int ret2; int ret3; unsigned char rnd[32]; unsigned char privkey_tmp[32]; secp256k1_pubkey pubkey2; - testrand256_test(rnd); + 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); @@ -6475,7 +6591,7 @@ static void test_ecdsa_end_to_end(void) { /* Serialize/destroy/parse DER and verify again. */ siglen = 74; CHECK(secp256k1_ecdsa_signature_serialize_der(CTX, sig, &siglen, &signature[0]) == 1); - sig[testrand_int(siglen)] += 1 + testrand_int(255); + 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); } @@ -6485,23 +6601,23 @@ static void test_random_pubkeys(void) { secp256k1_ge elem2; unsigned char in[65]; /* Generate some randomly sized pubkeys. */ - size_t len = testrand_bits(2) == 0 ? 65 : 33; - if (testrand_bits(2) == 0) { - len = testrand_bits(6); + size_t len = secp256k1_testrand_bits(2) == 0 ? 65 : 33; + if (secp256k1_testrand_bits(2) == 0) { + len = secp256k1_testrand_bits(6); } if (len == 65) { - in[0] = testrand_bits(1) ? 4 : (testrand_bits(1) ? 6 : 7); + in[0] = secp256k1_testrand_bits(1) ? 4 : (secp256k1_testrand_bits(1) ? 6 : 7); } else { - in[0] = testrand_bits(1) ? 2 : 3; + in[0] = secp256k1_testrand_bits(1) ? 2 : 3; } - if (testrand_bits(3) == 0) { - in[0] = testrand_bits(8); + if (secp256k1_testrand_bits(3) == 0) { + in[0] = secp256k1_testrand_bits(8); } if (len > 1) { - testrand256(&in[1]); + secp256k1_testrand256(&in[1]); } if (len > 33) { - testrand256(&in[33]); + secp256k1_testrand256(&in[33]); } if (secp256k1_eckey_pubkey_parse(&elem, in, len)) { unsigned char out[65]; @@ -6523,7 +6639,7 @@ static void test_random_pubkeys(void) { CHECK(secp256k1_eckey_pubkey_parse(&elem2, in, size)); CHECK(secp256k1_ge_eq_var(&elem2, &elem)); /* Check that the X9.62 hybrid type is checked. */ - in[0] = testrand_bits(1) ? 6 : 7; + in[0] = secp256k1_testrand_bits(1) ? 6 : 7; res = secp256k1_eckey_pubkey_parse(&elem2, in, size); if (firstb == 2 || firstb == 3) { if (in[0] == firstb + 4) { @@ -6602,7 +6718,7 @@ static void permute(size_t *arr, size_t n) { size_t i; for (i = n - 1; i >= 1; i--) { size_t tmp, j; - j = testrand_int(i + 1); + j = secp256k1_testrand_int(i + 1); tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; @@ -6612,7 +6728,7 @@ static void permute(size_t *arr, size_t n) { static void rand_pk(secp256k1_pubkey *pk) { unsigned char seckey[32]; secp256k1_keypair keypair; - testrand256(seckey); + secp256k1_testrand256(seckey); CHECK(secp256k1_keypair_create(CTX, &keypair, seckey) == 1); CHECK(secp256k1_keypair_pub(CTX, pk, &keypair) == 1); } @@ -6828,27 +6944,27 @@ static void assign_big_endian(unsigned char *ptr, size_t ptrlen, uint32_t val) { static void damage_array(unsigned char *sig, size_t *len) { int pos; - int action = testrand_bits(3); + int action = secp256k1_testrand_bits(3); if (action < 1 && *len > 3) { /* Delete a byte. */ - pos = testrand_int(*len); + pos = secp256k1_testrand_int(*len); memmove(sig + pos, sig + pos + 1, *len - pos - 1); (*len)--; return; } else if (action < 2 && *len < 2048) { /* Insert a byte. */ - pos = testrand_int(1 + *len); + pos = secp256k1_testrand_int(1 + *len); memmove(sig + pos + 1, sig + pos, *len - pos); - sig[pos] = testrand_bits(8); + sig[pos] = secp256k1_testrand_bits(8); (*len)++; return; } else if (action < 4) { /* Modify a byte. */ - sig[testrand_int(*len)] += 1 + testrand_int(255); + sig[secp256k1_testrand_int(*len)] += 1 + secp256k1_testrand_int(255); return; } else { /* action < 8 */ /* Modify a bit. */ - sig[testrand_int(*len)] ^= 1 << testrand_bits(3); + sig[secp256k1_testrand_int(*len)] ^= 1 << secp256k1_testrand_bits(3); return; } } @@ -6861,23 +6977,23 @@ static void random_ber_signature(unsigned char *sig, size_t *len, int* certainly int n; *len = 0; - der = testrand_bits(2) == 0; + der = secp256k1_testrand_bits(2) == 0; *certainly_der = der; *certainly_not_der = 0; - indet = der ? 0 : testrand_int(10) == 0; + indet = der ? 0 : secp256k1_testrand_int(10) == 0; for (n = 0; n < 2; n++) { /* We generate two classes of numbers: nlow==1 "low" ones (up to 32 bytes), nlow==0 "high" ones (32 bytes with 129 top bits set, or larger than 32 bytes) */ - nlow[n] = der ? 1 : (testrand_bits(3) != 0); + nlow[n] = der ? 1 : (secp256k1_testrand_bits(3) != 0); /* The length of the number in bytes (the first byte of which will always be nonzero) */ - nlen[n] = nlow[n] ? testrand_int(33) : 32 + testrand_int(200) * testrand_bits(3) / 8; + nlen[n] = nlow[n] ? secp256k1_testrand_int(33) : 32 + secp256k1_testrand_int(200) * secp256k1_testrand_bits(3) / 8; CHECK(nlen[n] <= 232); /* The top bit of the number. */ - nhbit[n] = (nlow[n] == 0 && nlen[n] == 32) ? 1 : (nlen[n] == 0 ? 0 : testrand_bits(1)); + nhbit[n] = (nlow[n] == 0 && nlen[n] == 32) ? 1 : (nlen[n] == 0 ? 0 : secp256k1_testrand_bits(1)); /* The top byte of the number (after the potential hardcoded 16 0xFF characters for "high" 32 bytes numbers) */ - nhbyte[n] = nlen[n] == 0 ? 0 : (nhbit[n] ? 128 + testrand_bits(7) : 1 + testrand_int(127)); + nhbyte[n] = nlen[n] == 0 ? 0 : (nhbit[n] ? 128 + secp256k1_testrand_bits(7) : 1 + secp256k1_testrand_int(127)); /* The number of zero bytes in front of the number (which is 0 or 1 in case of DER, otherwise we extend up to 300 bytes) */ - nzlen[n] = der ? ((nlen[n] == 0 || nhbit[n]) ? 1 : 0) : (nlow[n] ? testrand_int(3) : testrand_int(300 - nlen[n]) * testrand_bits(3) / 8); + nzlen[n] = der ? ((nlen[n] == 0 || nhbit[n]) ? 1 : 0) : (nlow[n] ? secp256k1_testrand_int(3) : secp256k1_testrand_int(300 - nlen[n]) * secp256k1_testrand_bits(3) / 8); if (nzlen[n] > ((nlen[n] == 0 || nhbit[n]) ? 1 : 0)) { *certainly_not_der = 1; } @@ -6886,7 +7002,7 @@ static void random_ber_signature(unsigned char *sig, size_t *len, int* certainly nlenlen[n] = nlen[n] + nzlen[n] < 128 ? 0 : (nlen[n] + nzlen[n] < 256 ? 1 : 2); if (!der) { /* nlenlen[n] max 127 bytes */ - int add = testrand_int(127 - nlenlen[n]) * testrand_bits(4) * testrand_bits(4) / 256; + int add = secp256k1_testrand_int(127 - nlenlen[n]) * secp256k1_testrand_bits(4) * secp256k1_testrand_bits(4) / 256; nlenlen[n] += add; if (add != 0) { *certainly_not_der = 1; @@ -6900,7 +7016,7 @@ static void random_ber_signature(unsigned char *sig, size_t *len, int* certainly CHECK(tlen <= 856); /* The length of the garbage inside the tuple. */ - elen = (der || indet) ? 0 : testrand_int(980 - tlen) * testrand_bits(3) / 8; + elen = (der || indet) ? 0 : secp256k1_testrand_int(980 - tlen) * secp256k1_testrand_bits(3) / 8; if (elen != 0) { *certainly_not_der = 1; } @@ -6908,7 +7024,7 @@ static void random_ber_signature(unsigned char *sig, size_t *len, int* certainly CHECK(tlen <= 980); /* The length of the garbage after the end of the tuple. */ - glen = der ? 0 : testrand_int(990 - tlen) * testrand_bits(3) / 8; + glen = der ? 0 : secp256k1_testrand_int(990 - tlen) * secp256k1_testrand_bits(3) / 8; if (glen != 0) { *certainly_not_der = 1; } @@ -6923,7 +7039,7 @@ static void random_ber_signature(unsigned char *sig, size_t *len, int* certainly } else { int tlenlen = tlen < 128 ? 0 : (tlen < 256 ? 1 : 2); if (!der) { - int add = testrand_int(127 - tlenlen) * testrand_bits(4) * testrand_bits(4) / 256; + int add = secp256k1_testrand_int(127 - tlenlen) * secp256k1_testrand_bits(4) * secp256k1_testrand_bits(4) / 256; tlenlen += add; if (add != 0) { *certainly_not_der = 1; @@ -6974,13 +7090,13 @@ static void random_ber_signature(unsigned char *sig, size_t *len, int* certainly nlen[n]--; } /* Generate remaining random bytes of number */ - testrand_bytes_test(sig + *len, nlen[n]); + secp256k1_testrand_bytes_test(sig + *len, nlen[n]); *len += nlen[n]; nlen[n] = 0; } /* Generate random garbage inside tuple. */ - testrand_bytes_test(sig + *len, elen); + secp256k1_testrand_bytes_test(sig + *len, elen); *len += elen; /* Generate end-of-contents bytes. */ @@ -6992,7 +7108,7 @@ static void random_ber_signature(unsigned char *sig, size_t *len, int* certainly CHECK(tlen + glen <= 1121); /* Generate random garbage outside tuple. */ - testrand_bytes_test(sig + *len, glen); + secp256k1_testrand_bytes_test(sig + *len, glen); *len += glen; tlen += glen; CHECK(tlen <= 1121); @@ -7431,6 +7547,10 @@ static void run_ecdsa_wycheproof(void) { # include "modules/ellswift/tests_impl.h" #endif +#ifdef ENABLE_MODULE_SILENTPAYMENTS +# include "modules/silentpayments/tests_impl.h" +#endif + static void run_secp256k1_memczero_test(void) { unsigned char buf1[6] = {1, 2, 3, 4, 5, 6}; unsigned char buf2[sizeof(buf1)]; @@ -7655,7 +7775,7 @@ int main(int argc, char **argv) { run_xoshiro256pp_tests(); /* find random seed */ - testrand_init(argc > 2 ? argv[2] : NULL); + secp256k1_testrand_init(argc > 2 ? argv[2] : NULL); /*** Setup test environment ***/ @@ -7664,9 +7784,9 @@ int main(int argc, char **argv) { /* 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 (testrand_bits(4)) { + if (secp256k1_testrand_bits(4)) { unsigned char rand32[32]; - testrand256(rand32); + secp256k1_testrand256(rand32); CHECK(secp256k1_context_randomize(CTX, rand32)); } /* Make a writable copy of secp256k1_context_static in order to test the effect of API functions @@ -7783,6 +7903,10 @@ int main(int argc, char **argv) { run_ellswift_tests(); #endif +#ifdef ENABLE_MODULE_SILENTPAYMENTS + run_silentpayments_tests(); +#endif + /* util tests */ run_secp256k1_memczero_test(); run_secp256k1_byteorder_tests(); @@ -7793,7 +7917,7 @@ int main(int argc, char **argv) { free(STATIC_CTX); secp256k1_context_destroy(CTX); - testrand_finish(); + secp256k1_testrand_finish(); printf("no problems found\n"); return 0; diff --git a/src/tests_exhaustive.c b/src/tests_exhaustive.c index 6efa88982efbc8..5843b3e1f5280b 100644 --- a/src/tests_exhaustive.c +++ b/src/tests_exhaustive.c @@ -171,7 +171,7 @@ static void test_exhaustive_ecmult(const secp256k1_ge *group, const secp256k1_ge CHECK(secp256k1_fe_equal(&tmpf, &group[(i * j) % EXHAUSTIVE_TEST_ORDER].x)); /* Test secp256k1_ecmult_const_xonly with all curve X coordinates, with random xd. */ - testutil_random_fe_non_zero(&xd); + random_fe_non_zero(&xd); secp256k1_fe_mul(&xn, &xd, &group[i].x); ret = secp256k1_ecmult_const_xonly(&tmpf, &xn, &xd, &ng, 0); CHECK(ret); @@ -375,7 +375,7 @@ int main(int argc, char** argv) { printf("test count = %i\n", count); /* find random seed */ - testrand_init(argc > 2 ? argv[2] : NULL); + secp256k1_testrand_init(argc > 2 ? argv[2] : NULL); /* set up split processing */ if (argc > 4) { @@ -395,7 +395,7 @@ int main(int argc, char** argv) { while (count--) { /* Build context */ ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); - testrand256(rand32); + secp256k1_testrand256(rand32); CHECK(secp256k1_context_randomize(ctx, rand32)); /* Generate the entire group */ @@ -408,7 +408,7 @@ int main(int argc, char** argv) { /* Set a different random z-value for each Jacobian point, except z=1 is used in the last iteration. */ secp256k1_fe z; - testutil_random_fe(&z); + random_fe(&z); secp256k1_gej_rescale(&groupj[i], &z); } @@ -459,7 +459,7 @@ int main(int argc, char** argv) { secp256k1_context_destroy(ctx); } - testrand_finish(); + secp256k1_testrand_finish(); printf("no problems found\n"); return 0; diff --git a/src/testutil.h b/src/testutil.h index 8296a5fb99ffe4..4e2cb7d5b3b0cf 100644 --- a/src/testutil.h +++ b/src/testutil.h @@ -7,136 +7,23 @@ #define SECP256K1_TESTUTIL_H #include "field.h" -#include "group.h" #include "testrand.h" #include "util.h" -static void testutil_random_fe(secp256k1_fe *x) { +static void random_fe(secp256k1_fe *x) { unsigned char bin[32]; do { - testrand256(bin); + secp256k1_testrand256(bin); if (secp256k1_fe_set_b32_limit(x, bin)) { return; } } while(1); } -static void testutil_random_fe_non_zero(secp256k1_fe *nz) { +static void random_fe_non_zero(secp256k1_fe *nz) { do { - testutil_random_fe(nz); + random_fe(nz); } while (secp256k1_fe_is_zero(nz)); } -static void testutil_random_fe_magnitude(secp256k1_fe *fe, int m) { - secp256k1_fe zero; - int n = testrand_int(m + 1); - secp256k1_fe_normalize(fe); - if (n == 0) { - return; - } - secp256k1_fe_clear(&zero); - secp256k1_fe_negate(&zero, &zero, 0); - secp256k1_fe_mul_int_unchecked(&zero, n - 1); - secp256k1_fe_add(fe, &zero); -#ifdef VERIFY - CHECK(fe->magnitude == n); -#endif -} - -static void testutil_random_fe_test(secp256k1_fe *x) { - unsigned char bin[32]; - do { - testrand256_test(bin); - if (secp256k1_fe_set_b32_limit(x, bin)) { - return; - } - } while(1); -} - -static void testutil_random_fe_non_zero_test(secp256k1_fe *fe) { - do { - testutil_random_fe_test(fe); - } while(secp256k1_fe_is_zero(fe)); -} - -static void testutil_random_ge_x_magnitude(secp256k1_ge *ge) { - testutil_random_fe_magnitude(&ge->x, SECP256K1_GE_X_MAGNITUDE_MAX); -} - -static void testutil_random_ge_y_magnitude(secp256k1_ge *ge) { - testutil_random_fe_magnitude(&ge->y, SECP256K1_GE_Y_MAGNITUDE_MAX); -} - -static void testutil_random_gej_x_magnitude(secp256k1_gej *gej) { - testutil_random_fe_magnitude(&gej->x, SECP256K1_GEJ_X_MAGNITUDE_MAX); -} - -static void testutil_random_gej_y_magnitude(secp256k1_gej *gej) { - testutil_random_fe_magnitude(&gej->y, SECP256K1_GEJ_Y_MAGNITUDE_MAX); -} - -static void testutil_random_gej_z_magnitude(secp256k1_gej *gej) { - testutil_random_fe_magnitude(&gej->z, SECP256K1_GEJ_Z_MAGNITUDE_MAX); -} - -static void testutil_random_ge_test(secp256k1_ge *ge) { - secp256k1_fe fe; - do { - testutil_random_fe_test(&fe); - if (secp256k1_ge_set_xo_var(ge, &fe, testrand_bits(1))) { - secp256k1_fe_normalize(&ge->y); - break; - } - } while(1); - ge->infinity = 0; -} - -static void testutil_random_ge_jacobian_test(secp256k1_gej *gej, const secp256k1_ge *ge) { - secp256k1_fe z2, z3; - testutil_random_fe_non_zero_test(&gej->z); - secp256k1_fe_sqr(&z2, &gej->z); - secp256k1_fe_mul(&z3, &z2, &gej->z); - secp256k1_fe_mul(&gej->x, &ge->x, &z2); - secp256k1_fe_mul(&gej->y, &ge->y, &z3); - gej->infinity = ge->infinity; -} - -static void testutil_random_gej_test(secp256k1_gej *gej) { - secp256k1_ge ge; - testutil_random_ge_test(&ge); - testutil_random_ge_jacobian_test(gej, &ge); -} - -static void testutil_random_scalar_order_test(secp256k1_scalar *num) { - do { - unsigned char b32[32]; - int overflow = 0; - testrand256_test(b32); - secp256k1_scalar_set_b32(num, b32, &overflow); - if (overflow || secp256k1_scalar_is_zero(num)) { - continue; - } - break; - } while(1); -} - -static void testutil_random_scalar_order(secp256k1_scalar *num) { - do { - unsigned char b32[32]; - int overflow = 0; - testrand256(b32); - secp256k1_scalar_set_b32(num, b32, &overflow); - if (overflow || secp256k1_scalar_is_zero(num)) { - continue; - } - break; - } while(1); -} - -static void testutil_random_scalar_order_b32(unsigned char *b32) { - secp256k1_scalar num; - testutil_random_scalar_order(&num); - secp256k1_scalar_get_b32(b32, &num); -} - #endif /* SECP256K1_TESTUTIL_H */ diff --git a/tools/bech32m.py b/tools/bech32m.py new file mode 100644 index 00000000000000..795e1538639c40 --- /dev/null +++ b/tools/bech32m.py @@ -0,0 +1,135 @@ +# Copyright (c) 2017, 2020 Pieter Wuille +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +"""Reference implementation for Bech32/Bech32m and segwit addresses.""" + + +from enum import Enum + +class Encoding(Enum): + """Enumeration type to list the various supported encodings.""" + BECH32 = 1 + BECH32M = 2 + +CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" +BECH32M_CONST = 0x2bc830a3 + +def bech32_polymod(values): + """Internal function that computes the Bech32 checksum.""" + generator = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3] + chk = 1 + for value in values: + top = chk >> 25 + chk = (chk & 0x1ffffff) << 5 ^ value + for i in range(5): + chk ^= generator[i] if ((top >> i) & 1) else 0 + return chk + + +def bech32_hrp_expand(hrp): + """Expand the HRP into values for checksum computation.""" + return [ord(x) >> 5 for x in hrp] + [0] + [ord(x) & 31 for x in hrp] + + +def bech32_verify_checksum(hrp, data): + """Verify a checksum given HRP and converted data characters.""" + const = bech32_polymod(bech32_hrp_expand(hrp) + data) + if const == 1: + return Encoding.BECH32 + if const == BECH32M_CONST: + return Encoding.BECH32M + return None + +def bech32_create_checksum(hrp, data, spec): + """Compute the checksum values given HRP and data.""" + values = bech32_hrp_expand(hrp) + data + const = BECH32M_CONST if spec == Encoding.BECH32M else 1 + polymod = bech32_polymod(values + [0, 0, 0, 0, 0, 0]) ^ const + return [(polymod >> 5 * (5 - i)) & 31 for i in range(6)] + + +def bech32_encode(hrp, data, spec): + """Compute a Bech32 string given HRP and data values.""" + combined = data + bech32_create_checksum(hrp, data, spec) + return hrp + '1' + ''.join([CHARSET[d] for d in combined]) + +def bech32_decode(bech): + """Validate a Bech32/Bech32m string, and determine HRP and data.""" + if ((any(ord(x) < 33 or ord(x) > 126 for x in bech)) or + (bech.lower() != bech and bech.upper() != bech)): + return (None, None, None) + bech = bech.lower() + pos = bech.rfind('1') + + # remove the requirement that bech32m be less than 90 chars + if pos < 1 or pos + 7 > len(bech): + return (None, None, None) + if not all(x in CHARSET for x in bech[pos+1:]): + return (None, None, None) + hrp = bech[:pos] + data = [CHARSET.find(x) for x in bech[pos+1:]] + spec = bech32_verify_checksum(hrp, data) + if spec is None: + return (None, None, None) + return (hrp, data[:-6], spec) + +def convertbits(data, frombits, tobits, pad=True): + """General power-of-2 base conversion.""" + acc = 0 + bits = 0 + ret = [] + maxv = (1 << tobits) - 1 + max_acc = (1 << (frombits + tobits - 1)) - 1 + for value in data: + if value < 0 or (value >> frombits): + return None + acc = ((acc << frombits) | value) & max_acc + bits += frombits + while bits >= tobits: + bits -= tobits + ret.append((acc >> bits) & maxv) + if pad: + if bits: + ret.append((acc << (tobits - bits)) & maxv) + elif bits >= frombits or ((acc << (tobits - bits)) & maxv): + return None + return ret + + +def decode(hrp, addr): + """Decode a segwit address.""" + hrpgot, data, spec = bech32_decode(addr) + if hrpgot != hrp: + return (None, None) + decoded = convertbits(data[1:], 5, 8, False) + if decoded is None or len(decoded) < 2: + return (None, None) + if data[0] > 16: + return (None, None) + return (data[0], decoded) + + +def encode(hrp, witver, witprog): + """Encode a segwit address.""" + spec = Encoding.BECH32 if witver == 0 else Encoding.BECH32M + ret = bech32_encode(hrp, [witver] + convertbits(witprog, 8, 5), spec) + if decode(hrp, ret) == (None, None): + return None + return ret diff --git a/tools/ripemd160.py b/tools/ripemd160.py new file mode 100644 index 00000000000000..12801364b406a3 --- /dev/null +++ b/tools/ripemd160.py @@ -0,0 +1,130 @@ +# Copyright (c) 2021 Pieter Wuille +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test-only pure Python RIPEMD160 implementation.""" + +import unittest + +# Message schedule indexes for the left path. +ML = [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8, + 3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12, + 1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2, + 4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13 +] + +# Message schedule indexes for the right path. +MR = [ + 5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12, + 6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2, + 15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13, + 8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14, + 12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11 +] + +# Rotation counts for the left path. +RL = [ + 11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8, + 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12, + 11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5, + 11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12, + 9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6 +] + +# Rotation counts for the right path. +RR = [ + 8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6, + 9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11, + 9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5, + 15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8, + 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11 +] + +# K constants for the left path. +KL = [0, 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xa953fd4e] + +# K constants for the right path. +KR = [0x50a28be6, 0x5c4dd124, 0x6d703ef3, 0x7a6d76e9, 0] + + +def fi(x, y, z, i): + """The f1, f2, f3, f4, and f5 functions from the specification.""" + if i == 0: + return x ^ y ^ z + elif i == 1: + return (x & y) | (~x & z) + elif i == 2: + return (x | ~y) ^ z + elif i == 3: + return (x & z) | (y & ~z) + elif i == 4: + return x ^ (y | ~z) + else: + assert False + + +def rol(x, i): + """Rotate the bottom 32 bits of x left by i bits.""" + return ((x << i) | ((x & 0xffffffff) >> (32 - i))) & 0xffffffff + + +def compress(h0, h1, h2, h3, h4, block): + """Compress state (h0, h1, h2, h3, h4) with block.""" + # Left path variables. + al, bl, cl, dl, el = h0, h1, h2, h3, h4 + # Right path variables. + ar, br, cr, dr, er = h0, h1, h2, h3, h4 + # Message variables. + x = [int.from_bytes(block[4*i:4*(i+1)], 'little') for i in range(16)] + + # Iterate over the 80 rounds of the compression. + for j in range(80): + rnd = j >> 4 + # Perform left side of the transformation. + al = rol(al + fi(bl, cl, dl, rnd) + x[ML[j]] + KL[rnd], RL[j]) + el + al, bl, cl, dl, el = el, al, bl, rol(cl, 10), dl + # Perform right side of the transformation. + ar = rol(ar + fi(br, cr, dr, 4 - rnd) + x[MR[j]] + KR[rnd], RR[j]) + er + ar, br, cr, dr, er = er, ar, br, rol(cr, 10), dr + + # Compose old state, left transform, and right transform into new state. + return h1 + cl + dr, h2 + dl + er, h3 + el + ar, h4 + al + br, h0 + bl + cr + + +def ripemd160(data): + """Compute the RIPEMD-160 hash of data.""" + # Initialize state. + state = (0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0) + # Process full 64-byte blocks in the input. + for b in range(len(data) >> 6): + state = compress(*state, data[64*b:64*(b+1)]) + # Construct final blocks (with padding and size). + pad = b"\x80" + b"\x00" * ((119 - len(data)) & 63) + fin = data[len(data) & ~63:] + pad + (8 * len(data)).to_bytes(8, 'little') + # Process final blocks. + for b in range(len(fin) >> 6): + state = compress(*state, fin[64*b:64*(b+1)]) + # Produce output. + return b"".join((h & 0xffffffff).to_bytes(4, 'little') for h in state) + + +class TestFrameworkKey(unittest.TestCase): + def test_ripemd160(self): + """RIPEMD-160 test vectors.""" + # See https://homes.esat.kuleuven.be/~bosselae/ripemd160.html + for msg, hexout in [ + (b"", "9c1185a5c5e9fc54612808977ee8f548b2258d31"), + (b"a", "0bdc9d2d256b3ee9daae347be6f4dc835a467ffe"), + (b"abc", "8eb208f7e05d987a9b044a8e98c6b087f15a0bfc"), + (b"message digest", "5d0689ef49d2fae572b881b123a85ffa21595f36"), + (b"abcdefghijklmnopqrstuvwxyz", + "f71c27109c692c1b56bbdceb5b9d2865b3708dbc"), + (b"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + "12a053384a9c0c88e405a06c27dcf49ada62eb2b"), + (b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", + "b0e20b6e3116640286ed3a87a5713079b21f5189"), + (b"1234567890" * 8, "9b752e45573d4b39f4dbd3323cab82bf63326bfb"), + (b"a" * 1000000, "52783243c1697bdbe16d37f97f68f08325dc1528") + ]: + self.assertEqual(ripemd160(msg).hex(), hexout) diff --git a/tools/tests_silentpayments_generate.py b/tools/tests_silentpayments_generate.py new file mode 100755 index 00000000000000..de50a55ba006d7 --- /dev/null +++ b/tools/tests_silentpayments_generate.py @@ -0,0 +1,303 @@ +#!/usr/bin/env python3 +import hashlib +import json +import sys + +import bech32m +import ripemd160 + +NUMS_H = bytes.fromhex("50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0") +MAX_INPUTS_PER_TEST_CASE = 3 +MAX_OUTPUTS_PER_TEST_CASE = 4 +MAX_PERMUTATIONS_PER_SENDING_TEST_CASE = 12 + +def sha256(s): + return hashlib.sha256(s).digest() + +def hash160(s): + return ripemd160.ripemd160(sha256(s)) + +def smallest_outpoint(outpoints): + serialized_outpoints = [bytes.fromhex(txid)[::-1] + n.to_bytes(4, 'little') for txid, n in outpoints] + return sorted(serialized_outpoints)[0] + +def decode_silent_payments_address(address): + _, data = bech32m.decode("sp", address) + data = bytes(data) # convert from list to bytes + assert len(data) == 66 + return data[:33], data[33:] + +def is_p2tr(s): # OP_1 OP_PUSHBYTES_32 <32 bytes> + return (len(s) == 34) and (s[0] == 0x51) and (s[1] == 0x20) + +def is_p2wpkh(s): # OP_0 OP_PUSHBYTES_20 <20 bytes> + return (len(s) == 22) and (s[0] == 0x00) and (s[1] == 0x14) + +def is_p2sh(s): # OP_HASH160 OP_PUSHBYTES_20 <20 bytes> OP_EQUAL + return (len(s) == 23) and (s[0] == 0xA9) and (s[1] == 0x14) and (s[-1] == 0x87) + +def is_p2pkh(s): # OP_DUP OP_HASH160 OP_PUSHBYTES_20 <20 bytes> OP_EQUALVERIFY OP_CHECKSIG + return (len(s) == 25) and (s[0] == 0x76) and (s[1] == 0xA9) and (s[2] == 0x14) and \ + (s[-2] == 0x88) and (s[-1] == 0xAC) + +def get_pubkey_from_input(spk, script_sig, witness): + # build witness stack from raw witness data + witness_stack = [] + no_witness_items = 0 + if len(witness) > 0: + no_witness_items = witness[0] + witness = witness[1:] + for i in range(no_witness_items): + item_len = witness[0] + witness_stack.append(witness[1:item_len+1]) + witness = witness[item_len+1:] + + if is_p2pkh(spk): + spk_pkh = spk[3:3 + 20] + for i in range(len(script_sig), 0, -1): + if i - 33 >= 0: + pk = script_sig[i - 33:i] + if hash160(pk) == spk_pkh: + return pk + elif is_p2sh(spk) and is_p2wpkh(script_sig[1:]): + pubkey = witness_stack[-1] + if len(pubkey) == 33: + return pubkey + elif is_p2wpkh(spk): + # the witness must contain two items and the second item is the pubkey + pubkey = witness_stack[-1] + if len(pubkey) == 33: + return pubkey + elif is_p2tr(spk): + if len(witness_stack) > 1 and witness_stack[-1][0] == 0x50: + witness_stack.pop() + if len(witness_stack) > 1: # script-path spend? + control_block = witness_stack[-1] + internal_key = control_block[1:33] + if internal_key == NUMS_H: # skip + return b'' + return spk[2:] + + return b'' + +def to_c_array(x): + if x == "": + return "" + s = ',0x'.join(a+b for a,b in zip(x[::2], x[1::2])) + return "0x" + s + +def emit_key_material(comment, keys, include_count=False): + global out + if include_count: + out += f" {len(keys)}," + "\n" + out += f" {{ /* {comment} */" + "\n" + for i in range(MAX_INPUTS_PER_TEST_CASE): + out += " " + if i < len(keys): + out += "{" + out += to_c_array(keys[i]) + out += "}" + else: + out += '""' + if i != MAX_INPUTS_PER_TEST_CASE - 1: + out += ',' + out += "\n" + out += " },\n" + +def emit_recipient_addr_material(recipient_addresses): + global out + out += f" {len(recipient_addresses)}," + "\n" + out += " { /* recipient pubkeys (address data) */\n" + for i in range(MAX_OUTPUTS_PER_TEST_CASE): + out += " {\n" + if i < len(recipient_addresses): + B_scan, B_spend = decode_silent_payments_address(recipient_addresses[i]) + out += " {" + out += to_c_array(B_scan.hex()) + out += "},\n" + out += " {" + out += to_c_array(B_spend.hex()) + out += "},\n" + else: + out += ' "",\n' + out += ' "",\n' + out += " }" + if i != MAX_OUTPUTS_PER_TEST_CASE - 1: + out += ',' + out += "\n" + out += " },\n" + +def emit_sending_outputs(comment, output_sets, include_count=False): + global out + if include_count: + out += f" {len(output_sets)}," + "\n" + out += f" {len(output_sets[0])}," + "\n" + if comment: + out += f" {{ /* {comment} */" + "\n" + else: + out += " {\n" + for i in range(MAX_PERMUTATIONS_PER_SENDING_TEST_CASE): + if i < len(output_sets): + emit_outputs(comment=None, outputs=output_sets[i], include_count=False, spacing=12) + else: + emit_outputs(comment=None, outputs=[], include_count=False, spacing=12) + out += " }" + out += "," + out += "\n" + +def emit_outputs(comment, outputs, include_count=False, last=False, spacing=8): + global out + if include_count: + out += spacing*" " + f"{len(outputs)}," + "\n" + if comment: + out += spacing*" " + f"{{ /* {comment} */" + "\n" + else: + out += spacing*" " + "{\n" + for i in range(MAX_OUTPUTS_PER_TEST_CASE): + if i < len(outputs): + out += spacing*" " + " {" + out += to_c_array(outputs[i]) + out += "}" + else: + out += spacing*' ' + ' ""' + if i != MAX_OUTPUTS_PER_TEST_CASE - 1: + out += ',' + out += "\n" + out += spacing*" " + "}" + if not last: + out += "," + out += "\n" + +filename_input = sys.argv[1] +with open(filename_input) as f: + test_vectors = json.load(f) + +out = "" +num_vectors = 0 + +for test_nr, test_vector in enumerate(test_vectors): + # determine input private and public keys, grouped into plain and taproot/x-only + input_plain_seckeys = [] + input_taproot_seckeys = [] + input_plain_pubkeys = [] + input_xonly_pubkeys = [] + outpoints = [] + for i in test_vector['sending'][0]['given']['vin']: + pub_key = get_pubkey_from_input(bytes.fromhex(i['prevout']['scriptPubKey']['hex']), + bytes.fromhex(i['scriptSig']), bytes.fromhex(i['txinwitness'])) + if len(pub_key) == 33: # regular input + input_plain_seckeys.append(i['private_key']) + input_plain_pubkeys.append(pub_key.hex()) + elif len(pub_key) == 32: # taproot input + input_taproot_seckeys.append(i['private_key']) + input_xonly_pubkeys.append(pub_key.hex()) + outpoints.append((i['txid'], i['vout'])) + if len(input_plain_pubkeys) == 0 and len(input_xonly_pubkeys) == 0: + continue + + num_vectors += 1 + out += f" /* ----- {test_vector['comment']} ({num_vectors}) ----- */\n" + out += " {\n" + + outpoint_L = smallest_outpoint(outpoints).hex() + emit_key_material("input plain seckeys", input_plain_seckeys, include_count=True) + emit_key_material("input plain pubkeys", input_plain_pubkeys) + emit_key_material("input taproot seckeys", input_taproot_seckeys, include_count=True) + emit_key_material("input x-only pubkeys", input_xonly_pubkeys) + out += " /* smallest outpoint */\n" + out += " {" + out += to_c_array(outpoint_L) + out += "},\n" + + # emit recipient pubkeys (address data) + emit_recipient_addr_material(test_vector['sending'][0]['given']['recipients']) + # emit recipient outputs + emit_sending_outputs("recipient outputs", test_vector['sending'][0]['expected']['outputs'], include_count=True) + + # emit recipient scan/spend seckeys + recv_test_given = test_vector['receiving'][0]['given'] + recv_test_expected = test_vector['receiving'][0]['expected'] + out += " /* recipient data (scan and spend seckeys) */\n" + out += " {" + f"{to_c_array(recv_test_given['key_material']['scan_priv_key'])}" + "},\n" + out += " {" + f"{to_c_array(recv_test_given['key_material']['spend_priv_key'])}" + "},\n" + + # emit recipient to-scan outputs, labels and expected-found outputs + emit_outputs("outputs to scan", recv_test_given['outputs'], include_count=True) + labels = recv_test_given['labels'] + out += f" {len(labels)}, " + "{" + for i in range(4): + if i < len(labels): + out += f"{labels[i]}" + else: + out += "0xffffffff" + if i != 3: + out += ", " + out += "}, /* labels */\n" + expected_pubkeys = [o['pub_key'] for o in recv_test_expected['outputs']] + expected_tweaks = [o['priv_key_tweak'] for o in recv_test_expected['outputs']] + expected_signatures = [o['signature'] for o in recv_test_expected['outputs']] + out += " /* expected output data (pubkeys and seckey tweaks) */\n" + emit_outputs("", expected_pubkeys, include_count=True) + emit_outputs("", expected_tweaks) + emit_outputs("", expected_signatures, last=True) + + out += " }" + if test_nr != len(test_vectors)-1: + out += "," + out += "\n\n" + +STRUCT_DEFINITIONS = f""" +#define MAX_INPUTS_PER_TEST_CASE {MAX_INPUTS_PER_TEST_CASE} +#define MAX_OUTPUTS_PER_TEST_CASE {MAX_OUTPUTS_PER_TEST_CASE} +#define MAX_PERMUTATIONS_PER_SENDING_TEST_CASE {MAX_PERMUTATIONS_PER_SENDING_TEST_CASE} + +struct bip352_recipient_addressdata {{ + unsigned char scan_pubkey[33]; + unsigned char spend_pubkey[33]; +}}; + +struct bip352_test_vector {{ + /* Inputs (private keys / public keys + smallest outpoint) */ + size_t num_plain_inputs; + unsigned char plain_seckeys[MAX_INPUTS_PER_TEST_CASE][32]; + unsigned char plain_pubkeys[MAX_INPUTS_PER_TEST_CASE][33]; + + size_t num_taproot_inputs; + unsigned char taproot_seckeys[MAX_INPUTS_PER_TEST_CASE][32]; + unsigned char xonly_pubkeys[MAX_INPUTS_PER_TEST_CASE][32]; + + unsigned char outpoint_smallest[36]; + + /* Given sender data (pubkeys encoded per output address to send to) */ + size_t num_outputs; + struct bip352_recipient_addressdata recipient_pubkeys[MAX_OUTPUTS_PER_TEST_CASE]; + + /* Expected sender data */ + size_t num_output_sets; + size_t num_recipient_outputs; + unsigned char recipient_outputs[MAX_PERMUTATIONS_PER_SENDING_TEST_CASE][MAX_OUTPUTS_PER_TEST_CASE][32]; + + /* Given recipient data */ + unsigned char scan_seckey[32]; + unsigned char spend_seckey[32]; + size_t num_to_scan_outputs; + unsigned char to_scan_outputs[MAX_OUTPUTS_PER_TEST_CASE][32]; + size_t num_labels; + unsigned int label_integers[MAX_OUTPUTS_PER_TEST_CASE]; + + /* Expected recipient data */ + size_t num_found_output_pubkeys; + unsigned char found_output_pubkeys[MAX_OUTPUTS_PER_TEST_CASE][32]; + unsigned char found_seckey_tweaks[MAX_OUTPUTS_PER_TEST_CASE][32]; + unsigned char found_signatures[MAX_OUTPUTS_PER_TEST_CASE][64]; +}}; +""" + +print("/* Note: this file was autogenerated using tests_silentpayments_generate.py. Do not edit. */") +print(f"#define SECP256K1_SILENTPAYMENTS_NUMBER_TESTVECTORS ({num_vectors})") + +print(STRUCT_DEFINITIONS) + +print("static const struct bip352_test_vector bip352_test_vectors[SECP256K1_SILENTPAYMENTS_NUMBER_TESTVECTORS] = {") +print(out, end='') +print("};")