Squashed 'src/secp256k1/' changes from bd0287d650..d2d04864ef
What changed, and why it matters
This commit updates the secp256k1 cryptographic library inside Bitcoin Core to a newer version. The main change is adding support for a new optional feature called 'Silent Payments' (BIP352), which is a privacy-enhancing way to receive Bitcoin payments. The update also includes several smaller fixes and documentation updates, such as correcting a technical limit in a field-element comparison function, adding a safety check that x-only public keys must have even Y coordinates, and terminating a nonce-generation loop at a fixed maximum iteration count. There is no direct evidence in the commit message or diff that this is an emergency security patch for an active vulnerability; it reads as a routine feature and maintenance subtree update.
Treat this as a normal dependency update. Review the upstream libsecp256k1 release notes for the referenced range (bd0287d650..d2d04864ef) to confirm whether any of the included changes were security fixes. Run the existing Bitcoin Core and secp256k1 test suites, including the new Silent Payments vectors, before deploying. If Silent Payments support is not desired, it can be disabled at build time via --disable-module-silentpayments or -DSECP256K1_ENABLE_MODULE_SILENTPAYMENTS=OFF.
Security signals we found
New cryptographic module added (Silent Payments / BIP352)
Field-element equality check magnitude bound corrected from 31 to 30
X-only public key even-Y invariant enforced in extrakeys module
RFC6979 nonce loop bounded at UINT_MAX
SECURITY.md contact/key rotation
No vendor security disclosure or CVE referenced in commit
Evidence from the diff
The commit is a git subtree squash merge of upstream libsecp256k1 changes from bd0287d650 to d2d04864ef. Key technical items: (1) New optional ENABLE_MODULE_SILENTPAYMENTS module implementing BIP352 sending/receiving, with public header, implementation, tests, benchmarks, examples, and build-system integration. (2) Field serialization refactor to write by word using secp256k1_write_be32/be64 helpers. (3) Correction of fe_equal’s documented and verified magnitude bound for argument b from 31 to 30. (4) Addition of an invariant check in extrakeys that x-only public keys have an even Y coordinate in VERIFY builds. (5) RFC6979 nonce loop termination at UINT_MAX. (6) SECURITY.md update removing Jonas Nick and adding Sebastian Falbesoner/theStack. (7) CI/homebrew trust invocation addition. No CVE, advisory, or vendor security disclosure is present in the supplied materials.
Changed components
src/secp256k1 subtreeSilent Payments module (new)secp256k1 field implementationsecp256k1 extrakeys modulesecp256k1 nonce generationbuild system (CMake/Autotools)CI workflowsSECURITY.mdInspect captured patch +13939 / −134
diff --git a/.github/actions/install-homebrew-valgrind/action.yml b/.github/actions/install-homebrew-valgrind/action.yml
index e9aa6150..c4e0b5d9 100644
--- a/.github/actions/install-homebrew-valgrind/action.yml
+++ b/.github/actions/install-homebrew-valgrind/action.yml
@@ -5,6 +5,7 @@ runs:
steps:
- run: |
brew tap LouisBrunner/valgrind
+ brew trust --formula LouisBrunner/valgrind/valgrind
brew fetch --HEAD LouisBrunner/valgrind/valgrind
echo "CI_HOMEBREW_CELLAR_VALGRIND=$(brew --cellar valgrind)" >> "$GITHUB_ENV"
shell: bash
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 9b836cd6..3371fe31 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -39,6 +39,7 @@ env:
SCHNORRSIG: 'no'
MUSIG: 'no'
ELLSWIFT: 'no'
+ SILENTPAYMENTS: 'no'
### test options
SECP256K1_TEST_ITERS: 64
BENCH: 'yes'
@@ -96,18 +97,18 @@ jobs:
matrix:
configuration:
- env_vars: { WIDEMUL: 'int64', RECOVERY: 'yes' }
- - env_vars: { WIDEMUL: 'int64', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes' }
+ - env_vars: { WIDEMUL: 'int64', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', SILENTPAYMENTS: 'yes' }
- env_vars: { WIDEMUL: 'int128' }
- env_vars: { WIDEMUL: 'int128_struct', ELLSWIFT: 'yes' }
- env_vars: { WIDEMUL: 'int128', RECOVERY: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes' }
- - env_vars: { WIDEMUL: 'int128', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes' }
+ - env_vars: { WIDEMUL: 'int128', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', SILENTPAYMENTS: 'yes' }
- env_vars: { WIDEMUL: 'int128', ASM: 'x86_64', ELLSWIFT: 'yes' }
- env_vars: { RECOVERY: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes' }
- - env_vars: { CTIMETESTS: 'no', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', CPPFLAGS: '-DVERIFY' }
+ - env_vars: { CTIMETESTS: 'no', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: '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', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes' }
+ - env_vars: { CFLAGS: '-O1', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', SILENTPAYMENTS: 'yes' }
- env_vars: { ECMULTGENKB: 2, ECMULTWINDOW: 2 }
- env_vars: { ECMULTGENKB: 86, ECMULTWINDOW: 4 }
cc:
@@ -160,6 +161,7 @@ jobs:
SCHNORRSIG: 'yes'
MUSIG: 'yes'
ELLSWIFT: 'yes'
+ SILENTPAYMENTS: 'yes'
CC: ${{ matrix.cc }}
steps:
@@ -188,6 +190,7 @@ jobs:
SCHNORRSIG: 'yes'
MUSIG: 'yes'
ELLSWIFT: 'yes'
+ SILENTPAYMENTS: 'yes'
CTIMETESTS: 'no'
steps:
@@ -218,6 +221,7 @@ jobs:
SCHNORRSIG: 'yes'
MUSIG: 'yes'
ELLSWIFT: 'yes'
+ SILENTPAYMENTS: 'yes'
CTIMETESTS: 'no'
steps:
@@ -239,6 +243,7 @@ jobs:
SCHNORRSIG: 'yes'
MUSIG: 'yes'
ELLSWIFT: 'yes'
+ SILENTPAYMENTS: 'yes'
CTIMETESTS: 'no'
CC: ${{ matrix.cc }}
@@ -279,6 +284,7 @@ jobs:
SCHNORRSIG: 'yes'
MUSIG: 'yes'
ELLSWIFT: 'yes'
+ SILENTPAYMENTS: 'yes'
CTIMETESTS: 'no'
steps:
@@ -324,6 +330,7 @@ jobs:
SCHNORRSIG: 'yes'
MUSIG: 'yes'
ELLSWIFT: 'yes'
+ SILENTPAYMENTS: 'yes'
CTIMETESTS: 'no'
SECP256K1_TEST_ITERS: 2
@@ -353,6 +360,7 @@ jobs:
SCHNORRSIG: 'yes'
MUSIG: 'yes'
ELLSWIFT: 'yes'
+ SILENTPAYMENTS: 'yes'
CTIMETESTS: 'no'
CFLAGS: '-fsanitize=undefined,address -g'
UBSAN_OPTIONS: 'print_stacktrace=1:halt_on_error=1'
@@ -399,6 +407,7 @@ jobs:
SCHNORRSIG: 'yes'
MUSIG: 'yes'
ELLSWIFT: 'yes'
+ SILENTPAYMENTS: 'yes'
CC: ${{ matrix.cc }}
SECP256K1_TEST_ITERS: 32
ASM: 'no'
@@ -424,6 +433,7 @@ jobs:
SCHNORRSIG: 'yes'
MUSIG: 'yes'
ELLSWIFT: 'yes'
+ SILENTPAYMENTS: 'yes'
CTIMETESTS: 'no'
strategy:
@@ -456,14 +466,14 @@ jobs:
fail-fast: false
matrix:
env_vars:
- - { WIDEMUL: 'int64', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes' }
+ - { WIDEMUL: 'int64', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', SILENTPAYMENTS: 'yes' }
- { WIDEMUL: 'int128_struct', ECMULTGENKB: 2, ECMULTWINDOW: 4 }
- - { WIDEMUL: 'int128', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes' }
+ - { WIDEMUL: 'int128', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', SILENTPAYMENTS: 'yes' }
- { WIDEMUL: 'int128', RECOVERY: 'yes' }
- - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes' }
- - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', CC: 'gcc' }
- - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', WRAPPER_CMD: 'valgrind --error-exitcode=42', SECP256K1_TEST_ITERS: 2 }
- - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', CC: 'gcc', WRAPPER_CMD: 'valgrind --error-exitcode=42', SECP256K1_TEST_ITERS: 2 }
+ - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', SILENTPAYMENTS: 'yes' }
+ - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', SILENTPAYMENTS: 'yes', CC: 'gcc' }
+ - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', SILENTPAYMENTS: 'yes', WRAPPER_CMD: 'valgrind --error-exitcode=42', SECP256K1_TEST_ITERS: 2 }
+ - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', SILENTPAYMENTS: 'yes', CC: 'gcc', WRAPPER_CMD: 'valgrind --error-exitcode=42', SECP256K1_TEST_ITERS: 2 }
- { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', CPPFLAGS: '-DVERIFY', CTIMETESTS: 'no' }
- BUILD: 'distcheck'
@@ -513,13 +523,13 @@ jobs:
fail-fast: false
matrix:
env_vars:
- - { WIDEMUL: 'int64', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes' }
+ - { WIDEMUL: 'int64', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', SILENTPAYMENTS: 'yes' }
- { WIDEMUL: 'int128_struct', ECMULTGENKB: 2, ECMULTWINDOW: 4 }
- - { WIDEMUL: 'int128', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes' }
+ - { WIDEMUL: 'int128', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', SILENTPAYMENTS: 'yes' }
- { WIDEMUL: 'int128', RECOVERY: 'yes' }
- - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes' }
- - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', CC: 'gcc' }
- - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', CPPFLAGS: '-DVERIFY' }
+ - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', SILENTPAYMENTS: 'yes' }
+ - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', SILENTPAYMENTS: 'yes', CC: 'gcc' }
+ - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', SILENTPAYMENTS: 'yes', CPPFLAGS: '-DVERIFY' }
- BUILD: 'distcheck'
steps:
@@ -631,6 +641,7 @@ jobs:
SCHNORRSIG: 'yes'
MUSIG: 'yes'
ELLSWIFT: 'yes'
+ SILENTPAYMENTS: 'yes'
steps:
- *CHECKOUT
diff --git a/.gitignore b/.gitignore
index fbc311d7..60d9464e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,6 +12,7 @@ ecdsa_example
schnorr_example
ellswift_example
musig_example
+silentpayments_example
*.exe
*.so
*.a
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a84305c3..09ab67d2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -52,6 +52,7 @@ option(SECP256K1_ENABLE_MODULE_EXTRAKEYS "Enable extrakeys module." ON)
option(SECP256K1_ENABLE_MODULE_SCHNORRSIG "Enable schnorrsig module." ON)
option(SECP256K1_ENABLE_MODULE_MUSIG "Enable musig module." ON)
option(SECP256K1_ENABLE_MODULE_ELLSWIFT "Enable ElligatorSwift module." ON)
+option(SECP256K1_ENABLE_MODULE_SILENTPAYMENTS "Enable Silent Payments module." ON)
option(SECP256K1_USE_EXTERNAL_DEFAULT_CALLBACKS "Enable external default callback functions." OFF)
if(SECP256K1_USE_EXTERNAL_DEFAULT_CALLBACKS)
@@ -289,6 +290,7 @@ message(" extrakeys ........................... ${SECP256K1_ENABLE_MODULE_EXTRA
message(" schnorrsig .......................... ${SECP256K1_ENABLE_MODULE_SCHNORRSIG}")
message(" musig ............................... ${SECP256K1_ENABLE_MODULE_MUSIG}")
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")
diff --git a/Makefile.am b/Makefile.am
index 07d7a2ba..02588edc 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -210,6 +210,17 @@ musig_example_LDFLAGS += -lbcrypt
endif
TESTS += musig_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
@@ -253,6 +264,10 @@ maintainer-clean-local: clean-precomp
### (see the comments in the previous section for detailed rationale)
TESTVECTORS = src/wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.h
+if ENABLE_MODULE_SILENTPAYMENTS
+TESTVECTORS += src/modules/silentpayments/vectors.h
+endif
+
if ENABLE_MODULE_ECDH
TESTVECTORS += src/wycheproof/ecdh_secp256k1_test.h
endif
@@ -265,6 +280,10 @@ src/wycheproof/ecdh_secp256k1_test.h:
mkdir -p $(@D)
python3 $(top_srcdir)/tools/tests_wycheproof_generate_ecdh.py $(top_srcdir)/src/wycheproof/ecdh_secp256k1_test.json > $@
+src/modules/silentpayments/vectors.h:
+ mkdir -p $(@D)
+ python3 $(top_srcdir)/tools/tests_silentpayments_generate.py $(top_srcdir)/src/modules/silentpayments/bip352_send_and_receive_test_vectors.json > $@
+
testvectors: $(TESTVECTORS)
BUILT_SOURCES += $(TESTVECTORS)
@@ -287,9 +306,11 @@ EXTRA_DIST += sage/secp256k1_params.sage
EXTRA_DIST += sage/weierstrass_prover.sage
EXTRA_DIST += src/wycheproof/WYCHEPROOF_COPYING
EXTRA_DIST += src/wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.json
+EXTRA_DIST += src/modules/silentpayments/bip352_send_and_receive_test_vectors.json
EXTRA_DIST += src/wycheproof/ecdh_secp256k1_test.json
EXTRA_DIST += tools/tests_wycheproof_generate_ecdsa.py
EXTRA_DIST += tools/tests_wycheproof_generate_ecdh.py
+EXTRA_DIST += tools/tests_silentpayments_generate.py
if ENABLE_MODULE_ECDH
include src/modules/ecdh/Makefile.am.include
@@ -314,3 +335,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 90edae1a..fc64d40e 100644
--- a/README.md
+++ b/README.md
@@ -22,6 +22,7 @@ Features:
* 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).
* Optional module for MuSig2 Schnorr multi-signatures according to [BIP-327](https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki).
+* Optional module for Silent Payments sending and receiving according to [BIP-352](https://github.com/bitcoin/bips/blob/master/bip-0352.mediawiki).
Implementation details
----------------------
@@ -81,20 +82,21 @@ This can be done with the following steps:
```
4. Check out the latest release tag, e.g.
```
- git checkout v0.6.0
+ git checkout v0.7.1
```
5. Use git to verify the GPG signature:
```
- % git tag -v v0.6.0 | grep -C 3 'Good signature'
+ % git tag -v v0.7.1 | grep -C 3 'Good signature'
- gpg: Signature made Mon 04 Nov 2024 12:14:44 PM EST
- gpg: using RSA key 4BBB845A6F5A65A69DFAEC234861DBF262123605
- gpg: Good signature from "Jonas Nick <jonas@n-ck.net>" [unknown]
- gpg: aka "Jonas Nick <jonasd.nick@gmail.com>" [unknown]
+ gpg: Signature made Mon 26 Jan 2026 07:42:46 PM UTC
+ gpg: using RSA key 2840EAABF4BC9F0FFD716AFAFBAFCC46DE2D3FE2
+ gpg: Good signature from "Pieter Wuille <pieter@wuille.net>" [unknown]
+ gpg: aka "Pieter Wuille <pieter.wuille@gmail.com>" [full]
+ gpg: aka "[jpeg image of size 5996]" [undefined]
gpg: WARNING: This key is not certified with a trusted signature!
gpg: There is no indication that the signature belongs to the owner.
- Primary key fingerprint: 36C7 1A37 C9D9 88BD E825 08D9 B1A7 0E4F 8DCD 0366
- Subkey fingerprint: 4BBB 845A 6F5A 65A6 9DFA EC23 4861 DBF2 6212 3605
+ Primary key fingerprint: 133E AC17 9436 F14A 5CF1 B794 860F EB80 4E66 9320
+ Subkey fingerprint: 2840 EAAB F4BC 9F0F FD71 6AFA FBAF CC46 DE2D 3FE2
```
Building with Autotools
@@ -150,6 +152,7 @@ Usage examples can be found in the [examples](examples) directory. To compile th
* [Deriving a shared secret (ECDH) example](examples/ecdh.c)
* [ElligatorSwift key exchange example](examples/ellswift.c)
* [MuSig2 Schnorr multi-signatures example](examples/musig.c)
+ * [Silent Payments send and receive example](examples/silentpayments.c)
To compile the examples, make sure the corresponding modules are enabled.
diff --git a/SECURITY.md b/SECURITY.md
index b515cc1c..a31c53bb 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -6,10 +6,10 @@ To report security issues send an email to secp256k1-security@bitcoincore.org (n
The following keys may be used to communicate sensitive information to developers:
-| Name | Fingerprint |
-|------|-------------|
-| Pieter Wuille | 133E AC17 9436 F14A 5CF1 B794 860F EB80 4E66 9320 |
-| Jonas Nick | 36C7 1A37 C9D9 88BD E825 08D9 B1A7 0E4F 8DCD 0366 |
-| Tim Ruffing | 09E0 3F87 1092 E40E 106E 902B 33BC 86AB 80FF 5516 |
+| Name | Fingerprint |
+|----------------------|----------------------------------------------------|
+| Pieter Wuille | 133E AC17 9436 F14A 5CF1 B794 860F EB80 4E66 9320 |
+| Tim Ruffing | 09E0 3F87 1092 E40E 106E 902B 33BC 86AB 80FF 5516 |
+| Sebastian Falbesoner | 6A8F 9C26 6528 E25A EB1D 7731 C237 1D91 CB71 6EA7 |
You can import a key by running the following command with that individual’s fingerprint: `gpg --keyserver hkps://keys.openpgp.org --recv-keys "<fingerprint>"` Ensure that you put quotes around fingerprints containing spaces.
diff --git a/ci/ci.sh b/ci/ci.sh
index 515c14cd..d948c638 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 EXTRAKEYS MUSIG SCHNORRSIG ELLSWIFT \
+ EXPERIMENTAL ECDH RECOVERY EXTRAKEYS MUSIG SCHNORRSIG ELLSWIFT SILENTPAYMENTS \
SECP256K1_TEST_ITERS BENCH SECP256K1_BENCH_ITERS CTIMETESTS SYMBOL_CHECK \
EXAMPLES \
HOST WRAPPER_CMD \
@@ -64,6 +64,7 @@ fi
--enable-module-extrakeys="$EXTRAKEYS" \
--enable-module-schnorrsig="$SCHNORRSIG" \
--enable-module-musig="$MUSIG" \
+ --enable-module-silentpayments="$SILENTPAYMENTS" \
--enable-examples="$EXAMPLES" \
--enable-ctime-tests="$CTIMETESTS" \
--with-valgrind="$WITH_VALGRIND" \
diff --git a/configure.ac b/configure.ac
index a21447ce..258437f0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -193,6 +193,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=yes]]), [],
+ [SECP_SET_DEFAULT([enable_module_silentpayments], [yes], [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])])
@@ -399,6 +403,14 @@ 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
+ if test x"$enable_module_extrakeys" = x"no"; then
+ AC_MSG_ERROR([Module dependency error: You have disabled the extrakeys module explicitly, but it is required by the silentpayments module.])
+ fi
+ enable_module_extrakeys=yes
+ 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
@@ -472,6 +484,7 @@ AM_CONDITIONAL([ENABLE_MODULE_EXTRAKEYS], [test x"$enable_module_extrakeys" = x"
AM_CONDITIONAL([ENABLE_MODULE_SCHNORRSIG], [test x"$enable_module_schnorrsig" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_MUSIG], [test x"$enable_module_musig" = 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"])
@@ -496,6 +509,7 @@ echo " module extrakeys = $enable_module_extrakeys"
echo " module schnorrsig = $enable_module_schnorrsig"
echo " module musig = $enable_module_musig"
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/CMakeLists.txt b/examples/CMakeLists.txt
index 808917c4..83547ee2 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -31,3 +31,7 @@ endif()
if(SECP256K1_ENABLE_MODULE_MUSIG)
add_example(musig)
endif()
+
+if(SECP256K1_ENABLE_MODULE_SILENTPAYMENTS)
+ add_example(silentpayments)
+endif()
diff --git a/examples/silentpayments.c b/examples/silentpayments.c
new file mode 100644
index 00000000..c282a3b3
--- /dev/null
+++ b/examples/silentpayments.c
@@ -0,0 +1,465 @@
+/*************************************************************************
+ * 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 <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <secp256k1_extrakeys.h>
+#include <secp256k1_silentpayments.h>
+
+#include "examples_util.h"
+
+#define N_INPUTS 2
+#define N_OUTPUTS 3
+
+/* Static data for Bob and Carol's Silent Payments addresses */
+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_key[32] = {
+ 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
+};
+static unsigned char bob_scan_and_spend_pubkeys[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, 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
+ }
+};
+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 callback function are implemented here as a demonstration
+ * of how the label lookup callback is meant to query a label cache and return
+ * the label tweak when a match is found. This is for demonstration purposes
+ * only and not optimized. In production, it is expected that the
+ * caller will be using a much more performant data structure for storing and
+ * querying labels, like e.g. a hash table with O(1) lookup cost.
+ *
+ * Recipients not using labels can ignore these steps and simply pass `NULL`
+ * for the label_lookup and label_context arguments:
+ *
+ * secp256k1_silentpayments_recipient_scan_outputs(..., NULL, NULL);
+ */
+
+struct label_cache_entry {
+ unsigned char label[33];
+ unsigned char label_tweak[32];
+};
+
+struct labels_cache {
+ size_t entries_used;
+ struct label_cache_entry entries[5];
+};
+
+const unsigned char* label_lookup(
+ const unsigned char* label33,
+ 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 (memcmp(cache->entries[i].label, label33, 33) == 0) {
+ return cache->entries[i].label_tweak;
+ }
+ }
+ return NULL;
+}
+
+int main(void) {
+ unsigned char randomize[32];
+ unsigned char serialized_xonly[32];
+ secp256k1_xonly_pubkey tx_inputs[N_INPUTS];
+ const secp256k1_xonly_pubkey *tx_input_ptrs[N_INPUTS];
+ secp256k1_xonly_pubkey tx_outputs[N_OUTPUTS];
+ secp256k1_xonly_pubkey *tx_output_ptrs[N_OUTPUTS];
+ secp256k1_silentpayments_found_output found_outputs[N_OUTPUTS];
+ secp256k1_silentpayments_found_output *found_output_ptrs[N_OUTPUTS];
+ secp256k1_silentpayments_prevouts_summary prevouts_summary;
+ secp256k1_pubkey unlabeled_spend_pubkey;
+ struct labels_cache bob_labels_cache;
+ unsigned char bob_address[2][33];
+ int ret;
+ size_t i;
+ uint32_t n_found_outputs;
+
+ /* 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 EXIT_FAILURE;
+ }
+ /* 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);
+
+ /* Set up the pointer arrays. These will be used for sending and scanning. */
+ for (i = 0; i < N_INPUTS; i++) {
+ tx_input_ptrs[i] = &tx_inputs[i];
+ }
+ for (i = 0; i < N_OUTPUTS; i++) {
+ tx_output_ptrs[i] = &tx_outputs[i];
+ found_output_ptrs[i] = &found_outputs[i];
+ }
+
+ /*** Create a labeled Silent Payments address (Bob) ***/
+ {
+ size_t len = 33;
+ secp256k1_silentpayments_label label;
+ secp256k1_pubkey labeled_spend_pubkey;
+ /* Per BIP-352, the label integer value m = 0 is reserved for the receiver's own
+ * change, so only values m >= 1 must be used for publishing addresses. */
+ uint32_t m = 1;
+
+ /* Load Bob's spend public key */
+ ret = secp256k1_ec_pubkey_parse(ctx,
+ &unlabeled_spend_pubkey,
+ bob_scan_and_spend_pubkeys[1],
+ 33
+ );
+ assert(ret);
+ /* Create a (label_tweak, label) pair and add them to the labels cache.
+ *
+ * Bob MUST keep track of all of the labels he has used by adding them
+ * to the labels cache. Otherwise, he will not find outputs sent to his
+ * labeled addresses when scanning. */
+ ret = secp256k1_silentpayments_recipient_label_create(ctx,
+ &label,
+ bob_labels_cache.entries[0].label_tweak,
+ bob_scan_key,
+ m
+ );
+ if (!ret) {
+ printf("Something went wrong, event with negligible probability happened.\n");
+ return EXIT_FAILURE;
+ }
+ ret = secp256k1_silentpayments_recipient_label_serialize(ctx, bob_labels_cache.entries[0].label, &label);
+ assert(ret);
+ bob_labels_cache.entries_used = 1;
+ /* Now that the labels cache has been updated, Bob creates his labeled
+ * Silent Payments address and publishes it.
+ */
+ ret = secp256k1_silentpayments_recipient_create_labeled_spend_pubkey(ctx, &labeled_spend_pubkey, &unlabeled_spend_pubkey, &label);
+ if (!ret) {
+ printf("Something went wrong, event with negligible probability happened.\n");
+ return EXIT_FAILURE;
+ }
+ memcpy(bob_address[0], bob_scan_and_spend_pubkeys[0], 33);
+ ret = secp256k1_ec_pubkey_serialize(ctx,
+ bob_address[1],
+ &len,
+ &labeled_spend_pubkey,
+ SECP256K1_EC_COMPRESSED
+ );
+ assert(ret);
+ }
+ /*** Sending (Alice) ***/
+ {
+ secp256k1_keypair sender_keypairs[N_INPUTS];
+ const secp256k1_keypair *sender_keypair_ptrs[N_INPUTS];
+ secp256k1_silentpayments_recipient recipients[N_OUTPUTS];
+ const secp256k1_silentpayments_recipient *recipient_ptrs[N_OUTPUTS];
+ /* 2D array for holding multiple public key pairs. The second index, i.e., [2],
+ * is to represent the spend and scan public keys. */
+ unsigned char (*sp_addresses[N_OUTPUTS])[2][33];
+ unsigned char seckey[32];
+
+ /*** Generate secret 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 secret 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 extract from the transaction inputs.
+ *
+ * If the secret key is zero or out of range (bigger than secp256k1's
+ * order), fail. Note that the probability of this happening is
+ * negligible. */
+ for (i = 0; i < N_INPUTS; i++) {
+ if (!fill_random(seckey, sizeof(seckey))) {
+ printf("Failed to generate randomness\n");
+ return EXIT_FAILURE;
+ }
+ /* 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_keypairs[i], seckey)) {
+ sender_keypair_ptrs[i] = &sender_keypairs[i];
+ ret = secp256k1_keypair_xonly_pub(
+ ctx,
+ &tx_inputs[i],
+ NULL,
+ &sender_keypairs[i]
+ );
+ assert(ret);
+ } else {
+ printf("Failed to create keypair\n");
+ return EXIT_FAILURE;
+ }
+ }
+ /*** Create the recipient objects ***/
+
+ /* Alice is sending to Bob and Carol in this transaction:
+ *
+ * 1. One output to Bob's labeled address
+ * 2. Two outputs for Carol
+ *
+ * To create multiple outputs for Carol, Alice simply passes Carol's
+ * Silent Payments address multiple times.
+ */
+ sp_addresses[0] = &carol_address;
+ sp_addresses[1] = &bob_address;
+ sp_addresses[2] = &carol_address;
+ for (i = 0; i < N_OUTPUTS; i++) {
+ ret = secp256k1_ec_pubkey_parse(ctx,
+ &recipients[i].scan_pubkey,
+ (*(sp_addresses[i]))[0],
+ 33
+ );
+ ret &= secp256k1_ec_pubkey_parse(ctx,
+ &recipients[i].spend_pubkey,
+ (*(sp_addresses[i]))[1],
+ 33
+ );
+ if (!ret) {
+ printf("Something went wrong, this is not a valid Silent Payments address.\n");
+ return EXIT_FAILURE;
+ }
+
+ /* Alice creates the recipient objects and adds the index of the
+ * original ordering (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.
+ */
+ recipients[i].index = i;
+ recipient_ptrs[i] = &recipients[i];
+ }
+ ret = secp256k1_silentpayments_sender_create_outputs(ctx,
+ tx_output_ptrs,
+ recipient_ptrs, N_OUTPUTS,
+ smallest_outpoint,
+ sender_keypair_ptrs, N_INPUTS,
+ NULL, 0
+ );
+ if (!ret) {
+ printf("Something went wrong, a recipient provided an invalid address.\n");
+ return EXIT_FAILURE;
+ }
+ printf("Alice created the following outputs for Bob and Carol:\n");
+ for (i = 0; i < N_OUTPUTS; i++) {
+ printf(" ");
+ ret = secp256k1_xonly_pubkey_serialize(ctx,
+ serialized_xonly,
+ &tx_outputs[i]
+ );
+ assert(ret);
+ print_hex(serialized_xonly, sizeof(serialized_xonly));
+ }
+ /* 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. */
+ secure_erase(seckey, sizeof(seckey));
+ for (i = 0; i < N_INPUTS; i++) {
+ secure_erase(&sender_keypairs[i], sizeof(sender_keypairs[i]));
+ }
+ }
+
+ /*** Receiving ***/
+ {
+ {
+ /*** Scanning as a full node (Bob) ***
+ *
+ * Since Bob has access to the full transaction, scanning is simple:
+ *
+ * 1. Collect the relevant prevouts from the transaction and call
+ * `secp256k1_silentpayments_recipient_prevouts_summary_create`
+ * 2. Call `secp256k1_silentpayments_recipient_scan_outputs`
+ */
+ ret = secp256k1_silentpayments_recipient_prevouts_summary_create(ctx,
+ &prevouts_summary,
+ smallest_outpoint,
+ tx_input_ptrs, N_INPUTS,
+ NULL, 0
+ );
+ if (!ret) {
+ /* We need to always check that the prevouts data object is valid
+ * before proceeding.
+ */
+ printf("This transaction is not valid for Silent Payments, skipping.\n");
+ return EXIT_SUCCESS;
+ }
+
+ /* Scan the transaction */
+ n_found_outputs = 0;
+ ret = secp256k1_silentpayments_recipient_scan_outputs(ctx,
+ found_output_ptrs, &n_found_outputs,
+ (const secp256k1_xonly_pubkey**)tx_output_ptrs, N_OUTPUTS,
+ bob_scan_key,
+ &prevouts_summary,
+ &unlabeled_spend_pubkey,
+ label_lookup, &bob_labels_cache /* NULL, NULL for no labels */
+ );
+ if (!ret) {
+ printf("This transaction is not valid for Silent Payments, skipping.\n");
+ return EXIT_SUCCESS;
+ }
+ if (n_found_outputs > 0) {
+ secp256k1_keypair kp;
+ secp256k1_xonly_pubkey xonly_output;
+ unsigned char full_seckey[32];
+
+ printf("\n");
+ printf("Bob found the following outputs: \n");
+ for (i = 0; i < n_found_outputs; i++) {
+ printf(" ");
+ ret = secp256k1_xonly_pubkey_serialize(ctx,
+ serialized_xonly,
+ &found_outputs[i].output
+ );
+ assert(ret);
+ print_hex(serialized_xonly, sizeof(serialized_xonly));
+
+ /* Verify that this output is spendable by Bob by reconstructing the full
+ * secret key for the xonly output.
+ *
+ * This is done by adding the tweak from the transaction to Bob's spend key.
+ * If the output was sent to a labeled address, the label tweak has already
+ * been added to the tweak in `secp256k1_silentpayments_found_output`.
+ *
+ * To verify that we are able to sign for this output, it is sufficient to
+ * check that the public key generated from `full_seckey` matches the output
+ * in the transaction. For a full example on signing for a taproot ouput,
+ * see `examples/schnorr.c`.
+ */
+ memcpy(&full_seckey, &bob_spend_key, 32);
+ ret = secp256k1_ec_seckey_tweak_add(ctx, full_seckey, found_outputs[i].tweak);
+ ret &= secp256k1_keypair_create(ctx, &kp, full_seckey);
+ ret &= secp256k1_keypair_xonly_pub(
+ ctx,
+ &xonly_output,
+ NULL,
+ &kp
+ );
+ /* We assert here because the only way the seckey_tweak_add operation can fail
+ * is if the tweak is the negation of Bob's spend key.
+ *
+ * We also assert that the generated public key matches the transaction output,
+ * as it should be impossible for a mismatch at this point considering the
+ * scanning function completed without errors and indicated found outputs.
+ */
+ assert(ret);
+ assert(secp256k1_xonly_pubkey_cmp(ctx, &xonly_output, &found_outputs[i].output) == 0);
+ secure_erase(full_seckey, sizeof(full_seckey));
+ }
+ } else {
+ printf("Bob did not find any outputs in this transaction.\n");
+ }
+ }
+ {
+ /*** Scanning as a full node (Carol) ***/
+ /* TODO: switch this part to light client scanning once it is supported */
+
+ /* Load Carol's spend public key. */
+ ret = secp256k1_ec_pubkey_parse(ctx,
+ &unlabeled_spend_pubkey,
+ carol_address[1],
+ 33
+ );
+ assert(ret);
+
+ n_found_outputs = 0;
+ ret = secp256k1_silentpayments_recipient_scan_outputs(ctx,
+ found_output_ptrs, &n_found_outputs,
+ (const secp256k1_xonly_pubkey**)tx_output_ptrs, N_OUTPUTS,
+ carol_scan_key,
+ &prevouts_summary,
+ &unlabeled_spend_pubkey,
+ NULL, NULL /* NULL, NULL for no labels */
+ );
+ if (!ret) {
+ printf("This transaction is not valid for Silent Payments, skipping.\n");
+ return EXIT_SUCCESS;
+ }
+ if (n_found_outputs > 0) {
+ /* Carol would spend these outputs the same as Bob, by tweaking her
+ * spend key with the tweak corresponding to the found output. See above
+ * for an example for Bob's outputs. */
+ printf("\n");
+ printf("Carol found the following outputs: \n");
+ for (i = 0; i < n_found_outputs; i++) {
+ printf(" ");
+ ret = secp256k1_xonly_pubkey_serialize(ctx,
+ serialized_xonly,
+ &found_outputs[i].output
+ );
+ assert(ret);
+ print_hex(serialized_xonly, sizeof(serialized_xonly));
+ }
+ } else {
+ printf("Carol did not find any outputs in this transaction.\n");
+ }
+ }
+ }
+
+ /* This will clear everything from the context and free the memory */
+ secp256k1_context_destroy(ctx);
+ return EXIT_SUCCESS;
+}
diff --git a/include/secp256k1_silentpayments.h b/include/secp256k1_silentpayments.h
new file mode 100644
index 00000000..256e95ac
--- /dev/null
+++ b/include/secp256k1_silentpayments.h
@@ -0,0 +1,399 @@
+#ifndef SECP256K1_SILENTPAYMENTS_H
+#define SECP256K1_SILENTPAYMENTS_H
+
+#include <stdint.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 secret or public keys and the derivation of a shared secret using
+ * Elliptic Curve Diffie-Hellman. Combined are either:
+ * - spender's secret keys and recipient's public key (a * B, sender side)
+ * - spender's public keys and recipient's secret key (A * b, recipient side)
+ * With this result, the necessary key material for ultimately creating/scanning
+ * or spending Silent Payments 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.
+ */
+
+/* Maximum number of Silent Payments recipients per group (i.e.
+ * recipients sharing the same scan public key) as per BIP-352 */
+#define SECP256K1_SILENTPAYMENTS_RECIPIENT_GROUP_LIMIT 2323
+
+/** The data from a single recipient address
+ *
+ * This struct serves as an input argument to `silentpayments_sender_create_outputs`.
+ *
+ * `index` must be set to the position (starting with 0) of this recipient in the
+ * `recipients` array passed to `silentpayments_sender_create_outputs`. It is
+ * used to map the returned generated outputs back to the original recipient.
+ *
+ * Note:
+ * The spend public key named `spend_pubkey` may have been optionally tweaked with
+ * a label by the recipient. Whether `spend_pubkey` has actually been tagged with
+ * a label is irrelevant for the sender. As a documentation convention in this API,
+ * `unlabeled_spend_pubkey` is used to indicate when the unlabeled spend public key
+ * must be used.
+ */
+typedef struct secp256k1_silentpayments_recipient {
+ secp256k1_pubkey scan_pubkey;
+ secp256k1_pubkey spend_pubkey;
+ size_t index;
+} secp256k1_silentpayments_recipient;
+
+/** Create Silent Payments outputs for recipient(s).
+ *
+ * Given a list of n secret keys a_1...a_n (one for each Silent Payments
+ * eligible input to spend), a serialized outpoint, and a list of recipients,
+ * create the taproot outputs. Inputs with conditional branches or multiple
+ * public keys are excluded from Silent Payments eligible inputs; see BIP352
+ * for more information.
+ *
+ * `outpoint_smallest36` refers to the smallest outpoint lexicographically
+ * from the transaction inputs (both Silent Payments eligible and non-eligible
+ * inputs). This value MUST be the smallest outpoint out of all of the
+ * transaction inputs, otherwise the recipient will be unable to find the
+ * payment. Determining the smallest outpoint from the list of transaction
+ * inputs is the responsibility of the caller. It is strongly recommended
+ * that implementations ensure they are doing this correctly by using the
+ * test vectors from BIP352.
+ *
+ * When creating more than one generated output, all of the generated outputs
+ * MUST be included in the final transaction. Dropping any of the generated
+ * outputs from the final transaction may make all or some of the outputs
+ * unfindable by the recipient.
+ *
+ * Returns: 1 if creation of outputs was successful.
+ * 0 on failure. This is expected only with an adversarially chosen
+ * recipient spend key. Specifically, failure occurs when:
+ * - Input secret keys sum to 0
+ * (negligible probability if at least one of the input secret
+ * keys is uniformly random and independent of all other keys)
+ * - A hash output is not a valid scalar (negligible probability
+ * per hash evaluation)
+ * - Any group (i.e. recipients sharing the same scan public key) exceeds
+ * the protocol limit SECP256K1_SILENTPAYMENTS_RECIPIENT_GROUP_LIMIT
+ *
+ * Args: ctx: pointer to a context object
+ * (not secp256k1_context_static).
+ * Out: generated_outputs: pointer to an array of pointers to xonly public keys,
+ * one per recipient.
+ * The outputs are ordered to match the original
+ * ordering of the recipient objects, i.e.,
+ * `generated_outputs[0]` is the generated output
+ * for the `secp256k1_silentpayments_recipient` object
+ * with index = 0.
+ * In: recipients: pointer to an array of pointers to Silent Payments
+ * 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 grouped by scan public key
+ * in place (as specified in BIP0352), but generated
+ * outputs are saved in the `generated_outputs` array
+ * to match the original ordering (using the index
+ * field). This ensures the caller is able to match
+ * the generated outputs to the correct Silent
+ * Payments addresses. The same recipient can be
+ * passed multiple times to create multiple outputs
+ * for the same recipient.
+ * n_recipients: the size of the recipients array.
+ * outpoint_smallest36: serialized (36-byte) smallest outpoint
+ * (lexicographically) from the transaction inputs
+ * keypairs: pointer to an array of pointers to taproot
+ * keypair inputs (can be NULL if no secret keys
+ * of taproot inputs are used)
+ * n_keypairs: the size of the keypairs array.
+ * seckeys: pointer to an array of pointers to 32-byte
+ * secret keys of non-taproot inputs (can be NULL
+ * if no secret keys of non-taproot inputs are
+ * used)
+ * n_seckeys: the size of the seckeys array.
+ */
+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 *keypairs,
+ size_t n_keypairs,
+ const unsigned char * const *seckeys,
+ size_t n_seckeys
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5);
+
+/** Opaque data structure that holds a Silent Payments label.
+ *
+ * Guaranteed to be 68 bytes in size. Serialized and parsed with
+ * `secp256k1_silentpayments_recipient_label_serialize` and
+ * `secp256k1_silentpayments_recipient_label_parse`.
+ */
+typedef struct secp256k1_silentpayments_label {
+ unsigned char data[68];
+} secp256k1_silentpayments_label;
+
+/** Parse a Silent Payments label.
+ *
+ * Returns: 1 when the label could be parsed, 0 otherwise.
+ * Args: ctx: pointer to a context object
+ * Out: label: pointer to a label object
+ * In: in33: pointer to the 33-byte label to be parsed
+ */
+SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_recipient_label_parse(
+ const secp256k1_context *ctx,
+ secp256k1_silentpayments_label *label,
+ const unsigned char *in33
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
+
+/** Serialize a Silent Payments label
+ *
+ * Returns: 1 always
+ * Args: ctx: pointer to a context object
+ * Out: out33: pointer to a 33-byte array to store the serialized label
+ * In: label: pointer to the label
+ */
+SECP256K1_API int secp256k1_silentpayments_recipient_label_serialize(
+ const secp256k1_context *ctx,
+ unsigned char *out33,
+ const secp256k1_silentpayments_label *label
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
+
+/** Create Silent Payments label tweak and label.
+ *
+ * Given a recipient's 32 byte scan key and a label integer m, calculate the
+ * corresponding label tweak and label:
+ *
+ * label_tweak = hash(scan_key || m)
+ * label = label_tweak * G
+ *
+ * Returns: 1 if label tweak and label creation was successful.
+ * 0 if scan_key32 is invalid or the hash output label_tweak32 is
+ * not a valid scalar (negligible probability per hash evaluation).
+ *
+ * WARNING: Creating a large number of labels may significantly degrade
+ * scanning performance in certain Silent Payments wallet implementations,
+ * such as light clients. The scanning function provided in this module,
+ * which is designed for full nodes, performs consistently even with hundreds
+ * of thousands of labels. Other implementations may not share this property
+ * or may be unable to use it due to lacking full transaction data.
+ *
+ * To maximize wallet interoperability, it is recommended to create only
+ * the change label (m = 0) and avoid distributing labeled addresses.
+ *
+ * Args: ctx: pointer to a context object
+ * (not secp256k1_context_static)
+ * Out: label: pointer to the resulting label
+ * label_tweak32: pointer to the 32 byte label tweak
+ * In: scan_key32: pointer to the recipient's 32 byte scan key
+ * m: integer for the m-th label (0 is used for change outputs)
+ */
+SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_recipient_label_create(
+ const secp256k1_context *ctx,
+ secp256k1_silentpayments_label *label,
+ unsigned char *label_tweak32,
+ const unsigned char *scan_key32,
+ uint32_t m
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
+
+/** Create Silent Payments labeled spend public key.
+ *
+ * Given a recipient's spend public key and a label, calculate the
+ * corresponding labeled spend public key:
+ *
+ * labeled_spend_pubkey = unlabeled_spend_pubkey + label
+ *
+ * The result is used by the recipient to create a Silent Payments address,
+ * consisting of the serialized and concatenated scan public key and
+ * (labeled) spend public key.
+ *
+ * Returns: 1 if labeled spend public key creation was successful.
+ * 0 if spend pubkey and label sum to zero (negligible probability for
+ * labels created according to BIP352).
+ *
+ * Args: ctx: pointer to a context object
+ * Out: labeled_spend_pubkey: pointer to the resulting labeled spend public key
+ * In: unlabeled_spend_pubkey: pointer to the recipient's unlabeled spend public key
+ * label: pointer to the recipient's label
+ */
+SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_recipient_create_labeled_spend_pubkey(
+ const secp256k1_context *ctx,
+ secp256k1_pubkey *labeled_spend_pubkey,
+ const secp256k1_pubkey *unlabeled_spend_pubkey,
+ const secp256k1_silentpayments_label *label
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
+
+/** Opaque data structure that holds Silent Payments prevouts summary data.
+ *
+ * The exact representation of data inside is implementation defined and not
+ * guaranteed to be portable between different platforms or versions. It is
+ * however guaranteed to be 101 bytes in size, and can be safely copied/moved.
+ * This structure does not contain secret data. It can be created with
+ * `secp256k1_silentpayments_recipient_prevouts_summary_create`.
+ */
+typedef struct secp256k1_silentpayments_prevouts_summary {
+ unsigned char data[101];
+} secp256k1_silentpayments_prevouts_summary;
+
+/** Compute Silent Payments prevouts summary from prevout public keys and transaction
+ * inputs.
+ *
+ * Given a list of n public keys A_1...A_n (one for each Silent Payments
+ * eligible input to spend) and a serialized outpoint_smallest36, create a
+ * `prevouts_summary` object. This object summarizes the prevout data from the
+ * transaction inputs needed for scanning.
+ *
+ * `outpoint_smallest36` refers to the smallest outpoint lexicographically
+ * from the transaction inputs (both Silent Payments eligible and non-eligible
+ * inputs). This value MUST be the smallest outpoint out of all of the
+ * transaction inputs, otherwise the recipient will be unable to find the
+ * payment.
+ *
+ * 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 public key format before calling this function.
+ * The resulting data can be used for scanning on the recipient side.
+ *
+ * Returns: 1 if prevouts summary creation was successful.
+ * 0 if the transaction is not a Silent Payments transaction.
+ *
+ * Args: ctx: pointer to a context object
+ * Out: prevouts_summary: pointer to prevouts_summary object containing the
+ * summed public key and input_hash.
+ * In: outpoint_smallest36: serialized smallest outpoint (lexicographically)
+ * from the transaction inputs
+ * 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 size of the xonly_pubkeys array.
+ * pubkeys: pointer to an array of pointers to non-taproot
+ * public keys (can be NULL if no non-taproot
+ * inputs are used)
+ * n_pubkeys: the size of the pubkeys array.
+ */
+SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_recipient_prevouts_summary_create(
+ const secp256k1_context *ctx,
+ secp256k1_silentpayments_prevouts_summary *prevouts_summary,
+ const unsigned char *outpoint_smallest36,
+ const secp256k1_xonly_pubkey * const *xonly_pubkeys,
+ size_t n_xonly_pubkeys,
+ const secp256k1_pubkey * const *pubkeys,
+ size_t n_pubkeys
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
+
+/** Type of callback function for label lookups
+ *
+ * A function of this type will be used to retrieve the label tweak for a given
+ * label during scanning. A typical implementation will perform a lookup in a
+ * key-value store called the "label cache".
+ *
+ * For creating the label cache data,
+ * `secp256k1_silentpayments_recipient_label_create` and
+ * `secp256k1_silentpayments_recipient_label_serialize` can be used.
+ *
+ * Returns: pointer to the 32-byte label tweak if there is a match.
+ * NULL pointer if there is no match.
+ *
+ * In: label: pointer to the serialized 33-byte label to check
+ * (computed during scanning)
+ * label_context: pointer to the recipient's label cache.
+ */
+typedef const unsigned char* (*secp256k1_silentpayments_label_lookup)(const unsigned char* label33, const void* label_context);
+
+/** Found outputs struct
+ *
+ * Struct for holding a found output along with data needed to spend it later.
+ *
+ * output: the x-only public key for the taproot output
+ * tweak: the 32-byte tweak needed to spend the output
+ * found_with_label: boolean value to indicate if the output was sent to a
+ * labeled address. If true, label will be set to a valid value.
+ * label: the label used. If found_with_label = false, this is set to
+ * an invalid value.
+ */
+typedef struct secp256k1_silentpayments_found_output {
+ secp256k1_xonly_pubkey output;
+ unsigned char tweak[32];
+ int found_with_label;
+ secp256k1_silentpayments_label label;
+} secp256k1_silentpayments_found_output;
+
+/** Scan for Silent Payments transaction outputs.
+ *
+ * Given a prevouts_summary object, a recipient's 32 byte scan key and spend public key,
+ * and the relevant transaction outputs, scan for outputs belonging 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.
+ *
+ * If used, the `label_lookup` function must return a pointer to a 32-byte label
+ * tweak if the label is found, or NULL otherwise. The returned pointer must remain
+ * valid until the next call to `label_lookup` or until the function returns,
+ * whichever comes first. It is not retained beyond that.
+ *
+ * For creating the label cache, `secp256k1_silentpayments_recipient_label_create`
+ * and `secp256k1_silentpayments_recipient_label_serialize` can be used.
+ *
+ * Note:
+ * Scanning is bounded by SECP256K1_SILENTPAYMENTS_RECIPIENT_GROUP_LIMIT and may
+ * miss outputs if a transaction contains more outputs for a single scan public
+ * key group than this limit.
+ *
+ * Returns: 1 if output scanning was successful.
+ * 0 if the transaction is not a Silent Payments transaction,
+ * or if the arguments are invalid.
+ *
+ * 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
+ * have 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). Can't be larger than
+ * SECP256K1_SILENTPAYMENTS_RECIPIENT_GROUP_LIMIT.
+ * In: tx_outputs: pointer to the transaction's x-only public key outputs,
+ * in their original transaction (vout) order
+ * n_tx_outputs: the size of the tx_outputs array.
+ * scan_key32: pointer to the recipient's 32 byte scan key. The scan
+ * key is valid if it passes secp256k1_ec_seckey_verify
+ * prevouts_summary: pointer to the transaction prevouts summary data (see
+ * `secp256k1_silentpayments_recipient_prevouts_summary_create`).
+ * unlabeled_spend_pubkey: pointer to the recipient's unlabeled spend public key
+ * label_lookup: pointer to a callback function for looking up
+ * a label value. This function takes a serialized 33-byte
+ * label as an argument and returns a pointer to the
+ * 32-byte label tweak if the label exists, otherwise
+ * returns a NULL pointer (NULL if labels are not
+ * used)
+ * label_context: pointer to a label context object (NULL if
+ * labels are not used or context is not needed)
+ */
+SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_recipient_scan_outputs(
+ const secp256k1_context *ctx,
+ secp256k1_silentpayments_found_output **found_outputs,
+ uint32_t *n_found_outputs,
+ const secp256k1_xonly_pubkey * const *tx_outputs,
+ size_t n_tx_outputs,
+ const unsigned char *scan_key32,
+ const secp256k1_silentpayments_prevouts_summary *prevouts_summary,
+ const secp256k1_pubkey *unlabeled_spend_pubkey,
+ 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);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SECP256K1_SILENTPAYMENTS_H */
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index a45eeb93..911804f8 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -21,6 +21,15 @@ if(SECP256K1_ENABLE_MODULE_MUSIG)
set_property(TARGET secp256k1 APPEND PROPERTY PUBLIC_HEADER ${PROJECT_SOURCE_DIR}/include/secp256k1_musig.h)
endif()
+if(SECP256K1_ENABLE_MODULE_SILENTPAYMENTS)
+ if(DEFINED SECP256K1_ENABLE_MODULE_EXTRAKEYS AND NOT SECP256K1_ENABLE_MODULE_EXTRAKEYS)
+ message(FATAL_ERROR "Module dependency error: You have disabled the extrakeys module explicitly, but it is required by the silent payments module.")
+ endif()
+ set(SECP256K1_ENABLE_MODULE_EXTRAKEYS ON)
+ add_compile_definitions(ENABLE_MODULE_SILENTPAYMENTS=1)
+ set_property(TARGET secp256k1 APPEND PROPERTY PUBLIC_HEADER ${PROJECT_SOURCE_DIR}/include/secp256k1_silentpayments.h)
+endif()
+
if(SECP256K1_ENABLE_MODULE_SCHNORRSIG)
if(DEFINED SECP256K1_ENABLE_MODULE_EXTRAKEYS AND NOT SECP256K1_ENABLE_MODULE_EXTRAKEYS)
message(FATAL_ERROR "Module dependency error: You have disabled the extrakeys module explicitly, but it is required by the schnorrsig module.")
diff --git a/src/bench.c b/src/bench.c
index f561ad1c..ac2054a5 100644
--- a/src/bench.c
+++ b/src/bench.c
@@ -32,6 +32,10 @@ static void help(const char *executable_path, int default_iters) {
printf(" - ElligatorSwift (optional module)\n");
#endif
+#ifdef ENABLE_MODULE_SILENTPAYMENTS
+ printf(" - Silent payments (optional module)\n");
+#endif
+
printf("\n");
printf("The default number of iterations for each benchmark is %d. This can be\n", default_iters);
printf("customized using the SECP256K1_BENCH_ITERS environment variable.\n");
@@ -39,33 +43,39 @@ static void help(const char *executable_path, int default_iters) {
printf("Usage: %s [args]\n", executable_path);
printf("By default, all benchmarks will be run.\n");
printf("args:\n");
- printf(" help : display this help and exit\n");
- printf(" ecdsa : all ECDSA algorithms--sign, verify, recovery (if enabled)\n");
- printf(" ecdsa_sign : ECDSA siging algorithm\n");
- printf(" ecdsa_verify : ECDSA verification algorithm\n");
- printf(" ec : all EC public key algorithms (keygen)\n");
- printf(" ec_keygen : EC public key generation\n");
+ printf(" help : display this help and exit\n");
+ printf(" ecdsa : all ECDSA algorithms--sign, verify, recovery (if enabled)\n");
+ printf(" ecdsa_sign : ECDSA siging algorithm\n");
+ printf(" ecdsa_verify : ECDSA verification algorithm\n");
+ printf(" ec : all EC public key algorithms (keygen)\n");
+ printf(" ec_keygen : EC public key generation\n");
#ifdef ENABLE_MODULE_RECOVERY
- printf(" ecdsa_recover : ECDSA public key recovery algorithm\n");
+ printf(" ecdsa_recover : ECDSA public key recovery algorithm\n");
#endif
#ifdef ENABLE_MODULE_ECDH
- printf(" ecdh : ECDH key exchange algorithm\n");
+ printf(" ecdh : ECDH key exchange algorithm\n");
#endif
#ifdef ENABLE_MODULE_SCHNORRSIG
- printf(" schnorrsig : all Schnorr signature algorithms (sign, verify)\n");
- printf(" schnorrsig_sign : Schnorr sigining algorithm\n");
- printf(" schnorrsig_verify : Schnorr verification algorithm\n");
+ printf(" schnorrsig : all Schnorr signature algorithms (sign, verify)\n");
+ printf(" schnorrsig_sign : Schnorr sigining algorithm\n");
+ printf(" schnorrsig_verify : Schnorr verification algorithm\n");
#endif
#ifdef ENABLE_MODULE_ELLSWIFT
- printf(" ellswift : all ElligatorSwift benchmarks (encode, decode, keygen, ecdh)\n");
- printf(" ellswift_encode : ElligatorSwift encoding\n");
- printf(" ellswift_decode : ElligatorSwift decoding\n");
- printf(" ellswift_keygen : ElligatorSwift key generation\n");
- printf(" ellswift_ecdh : ECDH on ElligatorSwift keys\n");
+ printf(" ellswift : all ElligatorSwift benchmarks (encode, decode, keygen, ecdh)\n");
+ printf(" ellswift_encode : ElligatorSwift encoding\n");
+ printf(" ellswift_decode : ElligatorSwift decoding\n");
+ printf(" ellswift_keygen : ElligatorSwift key generation\n");
+ printf(" ellswift_ecdh : ECDH on ElligatorSwift keys\n");
+#endif
+
+#ifdef ENABLE_MODULE_SILENTPAYMENTS
+ printf(" silentpayments : all Silent payments benchmarks (scan_nomatch, scan_worstcase)\n");
+ printf(" silentpayments_scan_nomatch : Silent payments scanning common case (no match)\n");
+ printf(" silentpayments_scan_worstcase : Silent payments scanning worst case (block-sized tx, all match)\n");
#endif
printf("\n");
@@ -170,6 +180,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;
@@ -182,7 +196,8 @@ 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",
+ "silentpayments_scan_nomatch", "silentpayments_scan_worstcase"};
int invalid_args = have_invalid_args(argc, argv, valid_args, ARRAY_SIZE(valid_args));
int default_iters = 20000;
@@ -240,6 +255,15 @@ int main(int argc, char** argv) {
}
#endif
+#ifndef ENABLE_MODULE_SILENTPAYMENTS
+ if (have_flag(argc, argv, "silentpayments") || have_flag(argc, argv, "silentpayments_scan_nomatch") ||
+ have_flag(argc, argv, "silentpayments_scan_worstcase")) {
+ fprintf(stderr, "./bench: silentpayments module not enabled.\n");
+ fprintf(stderr, "See README.md for configuration instructions.\n\n");
+ return EXIT_FAILURE;
+ }
+#endif
+
/* ECDSA benchmark */
data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
@@ -284,5 +308,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 EXIT_SUCCESS;
}
diff --git a/src/bench.h b/src/bench.h
index f88277aa..948e8db5 100644
--- a/src/bench.h
+++ b/src/bench.h
@@ -71,7 +71,7 @@ static void print_number(const int64_t x) {
buffer[--ptr] = '-';
g++;
}
- printf("%5.*s", g, &buffer[ptr]); /* Prints integer part */
+ printf("%8.*s", g, &buffer[ptr]); /* Prints integer part */
printf("%-*s", FP_EXP, &buffer[ptr + g]); /* Prints fractional part */
}
@@ -100,7 +100,7 @@ static void run_benchmark(char *name, void (*benchmark)(void*, int), void (*setu
sum += total;
}
/* ',' is used as a column delimiter */
- printf("%-30s, ", name);
+ printf("%-40s, ", name);
print_number(min * FP_MULT / iter);
printf(" , ");
print_number(((sum * FP_MULT) / count) / iter);
@@ -167,7 +167,7 @@ static void print_output_table_header_row(void) {
char* min_str = " Min(us) "; /* center alignment */
char* avg_str = " Avg(us) ";
char* max_str = " Max(us) ";
- printf("%-30s,%-15s,%-15s,%-15s\n", bench_str, min_str, avg_str, max_str);
+ printf("%-40s,%-18s,%-18s,%-18s\n", bench_str, min_str, avg_str, max_str);
printf("\n");
}
diff --git a/src/ctime_tests.c b/src/ctime_tests.c
index 8a885ca2..0d201d20 100644
--- a/src/ctime_tests.c
+++ b/src/ctime_tests.c
@@ -40,6 +40,10 @@
#include "../include/secp256k1_ellswift.h"
#endif
+#ifdef ENABLE_MODULE_SILENTPAYMENTS
+#include "../include/secp256k1_silentpayments.h"
+#endif
+
#if defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic warning "-Wunused-function"
@@ -99,6 +103,27 @@ static void run_tests(secp256k1_context *ctx, unsigned char *key) {
unsigned char ellswift[64];
static const unsigned char prefix[64] = {'t', 'e', 's', 't'};
#endif
+#ifdef ENABLE_MODULE_SILENTPAYMENTS
+ secp256k1_xonly_pubkey generated_output;
+ secp256k1_xonly_pubkey *generated_outputs[1];
+ secp256k1_silentpayments_recipient recipient;
+ secp256k1_silentpayments_label label;
+ const secp256k1_silentpayments_recipient *recipients[1];
+ unsigned char outpoint_smallest[36] = { 0 };
+ secp256k1_keypair sp_keypair;
+ const secp256k1_keypair *sp_keypairs[1];
+ const unsigned char *sp_seckeys[1];
+ secp256k1_silentpayments_found_output found_outputs[1];
+ secp256k1_silentpayments_found_output *found_outputs_ptrs[1];
+ uint32_t n_found_outputs;
+ const secp256k1_xonly_pubkey *tx_outputs[1];
+ secp256k1_silentpayments_prevouts_summary prevouts_summary;
+ unsigned char label_tweak[32] = { 0 };
+ secp256k1_xonly_pubkey sp_xonly_pubkey;
+ const secp256k1_xonly_pubkey *sp_xonly_pubkeys[1];
+ secp256k1_pubkey sp_pubkey;
+ const secp256k1_pubkey *sp_pubkeys[1];
+#endif
for (i = 0; i < 32; i++) {
msg[i] = i + 1;
@@ -268,6 +293,59 @@ static void run_tests(secp256k1_context *ctx, unsigned char *key) {
CHECK(ret == 1);
}
+#endif
+
+#ifdef ENABLE_MODULE_SILENTPAYMENTS
+ SECP256K1_CHECKMEM_DEFINE(key, 32);
+
+ generated_outputs[0] = &generated_output;
+
+ /* Initialize recipient */
+ CHECK(secp256k1_ec_pubkey_create(ctx, &recipient.scan_pubkey, key));
+ key[31] ^= 1;
+ CHECK(secp256k1_ec_pubkey_create(ctx, &recipient.spend_pubkey, key));
+ key[31] ^= (1 << 1);
+ recipient.index = 0;
+ recipients[0] = &recipient;
+
+ /* Set up secret keys */
+ SECP256K1_CHECKMEM_UNDEFINE(key, 32);
+ ret = secp256k1_keypair_create(ctx, &sp_keypair, key);
+ SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
+ CHECK(ret);
+ key[31] ^= (1 << 2);
+ sp_keypairs[0] = &sp_keypair;
+ sp_seckeys[0] = key;
+
+ ret = secp256k1_silentpayments_sender_create_outputs(ctx, generated_outputs, recipients, 1, outpoint_smallest, sp_keypairs, 1, sp_seckeys, 1);
+ CHECK(ret == 1);
+
+ ret = secp256k1_silentpayments_recipient_label_create(ctx, &label, label_tweak, key, 0);
+ key[31] ^= (1 << 3);
+ SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
+ CHECK(ret == 1);
+
+ CHECK(secp256k1_keypair_xonly_pub(ctx, &sp_xonly_pubkey, NULL, &sp_keypair));
+ SECP256K1_CHECKMEM_DEFINE(&sp_xonly_pubkey, sizeof(sp_xonly_pubkey));
+ sp_xonly_pubkeys[0] = &sp_xonly_pubkey;
+ ret = secp256k1_ec_pubkey_create(ctx, &sp_pubkey, sp_seckeys[0]);
+ SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
+ CHECK(ret == 1);
+ SECP256K1_CHECKMEM_DEFINE(&sp_pubkey, sizeof(sp_pubkey));
+ sp_pubkeys[0] = &sp_pubkey;
+
+ ret = secp256k1_silentpayments_recipient_prevouts_summary_create(ctx, &prevouts_summary, outpoint_smallest, sp_xonly_pubkeys, 1, sp_pubkeys, 1);
+ CHECK(ret == 1);
+
+ tx_outputs[0] = generated_outputs[0];
+ found_outputs_ptrs[0] = &found_outputs[0];
+ n_found_outputs = 1;
+ SECP256K1_CHECKMEM_DEFINE(&recipient.spend_pubkey, sizeof(recipient.spend_pubkey));
+ /* It is sufficient to check _recipient_scan_outputs without a label lookup function, since the shared secret is created once (which is where the constant timeness matters)
+ * and then reused for the rest of the scanning logic.
+ */
+ CHECK(secp256k1_silentpayments_recipient_scan_outputs(ctx, found_outputs_ptrs, &n_found_outputs, tx_outputs, 1, key, &prevouts_summary, &recipient.spend_pubkey, NULL, NULL));
+
#endif
}
diff --git a/src/field.h b/src/field.h
index 945029ec..8b25d99b 100644
--- a/src/field.h
+++ b/src/field.h
@@ -166,7 +166,7 @@ static int secp256k1_fe_is_odd(const secp256k1_fe *a);
/** Determine whether two field elements are equal.
*
* On input, a and b must be valid field elements with magnitudes not exceeding
- * 1 and 31, respectively.
+ * 1 and 30, respectively.
* Returns a = b (mod p).
*/
static int secp256k1_fe_equal(const secp256k1_fe *a, const secp256k1_fe *b);
diff --git a/src/field_10x26_impl.h b/src/field_10x26_impl.h
index aa45434d..abd17aa4 100644
--- a/src/field_10x26_impl.h
+++ b/src/field_10x26_impl.h
@@ -303,38 +303,14 @@ static int secp256k1_fe_impl_set_b32_limit(secp256k1_fe *r, const unsigned char
/** Convert a field element to a 32-byte big endian value. Requires the input to be normalized */
static void secp256k1_fe_impl_get_b32(unsigned char *r, const secp256k1_fe *a) {
- r[0] = (a->n[9] >> 14) & 0xff;
- r[1] = (a->n[9] >> 6) & 0xff;
- r[2] = ((a->n[9] & 0x3F) << 2) | ((a->n[8] >> 24) & 0x3);
- r[3] = (a->n[8] >> 16) & 0xff;
- r[4] = (a->n[8] >> 8) & 0xff;
- r[5] = a->n[8] & 0xff;
- r[6] = (a->n[7] >> 18) & 0xff;
- r[7] = (a->n[7] >> 10) & 0xff;
- r[8] = (a->n[7] >> 2) & 0xff;
- r[9] = ((a->n[7] & 0x3) << 6) | ((a->n[6] >> 20) & 0x3f);
- r[10] = (a->n[6] >> 12) & 0xff;
- r[11] = (a->n[6] >> 4) & 0xff;
- r[12] = ((a->n[6] & 0xf) << 4) | ((a->n[5] >> 22) & 0xf);
- r[13] = (a->n[5] >> 14) & 0xff;
- r[14] = (a->n[5] >> 6) & 0xff;
- r[15] = ((a->n[5] & 0x3f) << 2) | ((a->n[4] >> 24) & 0x3);
- r[16] = (a->n[4] >> 16) & 0xff;
- r[17] = (a->n[4] >> 8) & 0xff;
- r[18] = a->n[4] & 0xff;
- r[19] = (a->n[3] >> 18) & 0xff;
- r[20] = (a->n[3] >> 10) & 0xff;
- r[21] = (a->n[3] >> 2) & 0xff;
- r[22] = ((a->n[3] & 0x3) << 6) | ((a->n[2] >> 20) & 0x3f);
- r[23] = (a->n[2] >> 12) & 0xff;
- r[24] = (a->n[2] >> 4) & 0xff;
- r[25] = ((a->n[2] & 0xf) << 4) | ((a->n[1] >> 22) & 0xf);
- r[26] = (a->n[1] >> 14) & 0xff;
- r[27] = (a->n[1] >> 6) & 0xff;
- r[28] = ((a->n[1] & 0x3f) << 2) | ((a->n[0] >> 24) & 0x3);
- r[29] = (a->n[0] >> 16) & 0xff;
- r[30] = (a->n[0] >> 8) & 0xff;
- r[31] = a->n[0] & 0xff;
+ secp256k1_write_be32(&r[0], (a->n[9] << 10) | (a->n[8] >> 16));
+ secp256k1_write_be32(&r[4], (a->n[8] << 16) | (a->n[7] >> 10));
+ secp256k1_write_be32(&r[8], (a->n[7] << 22) | (a->n[6] >> 4));
+ secp256k1_write_be32(&r[12], (a->n[6] << 28) | (a->n[5] << 2) | (a->n[4] >> 24));
+ secp256k1_write_be32(&r[16], (a->n[4] << 8) | (a->n[3] >> 18));
+ secp256k1_write_be32(&r[20], (a->n[3] << 14) | (a->n[2] >> 12));
+ secp256k1_write_be32(&r[24], (a->n[2] << 20) | (a->n[1] >> 6));
+ secp256k1_write_be32(&r[28], (a->n[1] << 26) | a->n[0]);
}
SECP256K1_INLINE static void secp256k1_fe_impl_negate_unchecked(secp256k1_fe *r, const secp256k1_fe *a, int m) {
diff --git a/src/field_5x52_impl.h b/src/field_5x52_impl.h
index 0e0e2d65..6a2987ef 100644
--- a/src/field_5x52_impl.h
+++ b/src/field_5x52_impl.h
@@ -269,38 +269,10 @@ static int secp256k1_fe_impl_set_b32_limit(secp256k1_fe *r, const unsigned char
/** Convert a field element to a 32-byte big endian value. Requires the input to be normalized */
static void secp256k1_fe_impl_get_b32(unsigned char *r, const secp256k1_fe *a) {
- r[0] = (a->n[4] >> 40) & 0xFF;
- r[1] = (a->n[4] >> 32) & 0xFF;
- r[2] = (a->n[4] >> 24) & 0xFF;
- r[3] = (a->n[4] >> 16) & 0xFF;
- r[4] = (a->n[4] >> 8) & 0xFF;
- r[5] = a->n[4] & 0xFF;
- r[6] = (a->n[3] >> 44) & 0xFF;
- r[7] = (a->n[3] >> 36) & 0xFF;
- r[8] = (a->n[3] >> 28) & 0xFF;
- r[9] = (a->n[3] >> 20) & 0xFF;
- r[10] = (a->n[3] >> 12) & 0xFF;
- r[11] = (a->n[3] >> 4) & 0xFF;
- r[12] = ((a->n[2] >> 48) & 0xF) | ((a->n[3] & 0xF) << 4);
- r[13] = (a->n[2] >> 40) & 0xFF;
- r[14] = (a->n[2] >> 32) & 0xFF;
- r[15] = (a->n[2] >> 24) & 0xFF;
- r[16] = (a->n[2] >> 16) & 0xFF;
- r[17] = (a->n[2] >> 8) & 0xFF;
- r[18] = a->n[2] & 0xFF;
- r[19] = (a->n[1] >> 44) & 0xFF;
- r[20] = (a->n[1] >> 36) & 0xFF;
- r[21] = (a->n[1] >> 28) & 0xFF;
- r[22] = (a->n[1] >> 20) & 0xFF;
- r[23] = (a->n[1] >> 12) & 0xFF;
- r[24] = (a->n[1] >> 4) & 0xFF;
- r[25] = ((a->n[0] >> 48) & 0xF) | ((a->n[1] & 0xF) << 4);
- r[26] = (a->n[0] >> 40) & 0xFF;
- r[27] = (a->n[0] >> 32) & 0xFF;
- r[28] = (a->n[0] >> 24) & 0xFF;
- r[29] = (a->n[0] >> 16) & 0xFF;
- r[30] = (a->n[0] >> 8) & 0xFF;
- r[31] = a->n[0] & 0xFF;
+ secp256k1_write_be64(&r[0], (a->n[4] << 16) | (a->n[3] >> 36));
+ secp256k1_write_be64(&r[8], (a->n[3] << 28) | (a->n[2] >> 24));
+ secp256k1_write_be64(&r[16], (a->n[2] << 40) | (a->n[1] >> 12));
+ secp256k1_write_be64(&r[24], (a->n[1] << 52) | a->n[0]);
}
SECP256K1_INLINE static void secp256k1_fe_impl_negate_unchecked(secp256k1_fe *r, const secp256k1_fe *a, int m) {
diff --git a/src/field_impl.h b/src/field_impl.h
index 7aa7de43..19af6aa2 100644
--- a/src/field_impl.h
+++ b/src/field_impl.h
@@ -27,7 +27,7 @@ SECP256K1_INLINE static int secp256k1_fe_equal(const secp256k1_fe *a, const secp
SECP256K1_FE_VERIFY(a);
SECP256K1_FE_VERIFY(b);
SECP256K1_FE_VERIFY_MAGNITUDE(a, 1);
- SECP256K1_FE_VERIFY_MAGNITUDE(b, 31);
+ SECP256K1_FE_VERIFY_MAGNITUDE(b, 30);
secp256k1_fe_negate(&na, a, 1);
secp256k1_fe_add(&na, b);
diff --git a/src/modules/extrakeys/main_impl.h b/src/modules/extrakeys/main_impl.h
index 0c7e2667..163ea281 100644
--- a/src/modules/extrakeys/main_impl.h
+++ b/src/modules/extrakeys/main_impl.h
@@ -16,6 +16,13 @@ static SECP256K1_INLINE int secp256k1_xonly_pubkey_load(const secp256k1_context*
}
static SECP256K1_INLINE void secp256k1_xonly_pubkey_save(secp256k1_xonly_pubkey *pubkey, secp256k1_ge *ge) {
+#ifdef VERIFY
+ /* ensure that the group element's Y coordinate is even, as per definition of x-only public keys */
+ secp256k1_fe y = ge->y;
+ secp256k1_fe_normalize_var(&y);
+ VERIFY_CHECK(!secp256k1_fe_is_odd(&y));
+#endif
+
secp256k1_pubkey_save((secp256k1_pubkey *) pubkey, ge);
}
diff --git a/src/modules/silentpayments/Makefile.am.include b/src/modules/silentpayments/Makefile.am.include
new file mode 100644
index 00000000..d0c51dc3
--- /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 00000000..cf0d789a
--- /dev/null
+++ b/src/modules/silentpayments/bench_impl.h
@@ -0,0 +1,227 @@
+/***********************************************************************
+ * 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"
+
+#include "../../util.h"
+
+/* maximum non-coinbase taproot outputs per block: largest N for 1-in-N-out P2TR transaction
+ * that has a vsize <= (1_000_000 - 81 - 64) [https://bitcoin.stackexchange.com/a/122952]
+ * (needed for constructing the "worst-case scanning attack", where a single
+ * tx fills up a full bock of taproot outputs that all go to the same scankey group) */
+#define MAX_P2TR_OUTPUTS_PER_BLOCK 23250
+
+#define SP_BENCH_MAX_INPUTS 1
+#define SP_BENCH_MAX_OUTPUTS MAX_P2TR_OUTPUTS_PER_BLOCK
+
+typedef struct {
+ secp256k1_context *ctx;
+ secp256k1_pubkey spend_pubkey;
+ unsigned char scan_key[32];
+ secp256k1_xonly_pubkey *tx_outputs;
+ secp256k1_xonly_pubkey **tx_outputs_ptrs;
+ secp256k1_xonly_pubkey tx_inputs[SP_BENCH_MAX_INPUTS];
+ const secp256k1_xonly_pubkey *tx_inputs_ptrs[SP_BENCH_MAX_INPUTS];
+ secp256k1_silentpayments_found_output *found_outputs;
+ secp256k1_silentpayments_found_output **found_outputs_ptrs;
+ unsigned char smallest_outpoint[36];
+ unsigned char label[33];
+ unsigned char label_tweak[32];
+ int num_outputs;
+ int num_matches;
+} bench_silentpayments_data;
+
+const unsigned char* label_lookup(const unsigned char* key, const void* cache_ptr) {
+ bench_silentpayments_data *data = (bench_silentpayments_data*)cache_ptr;
+ if (secp256k1_memcmp_var(key, data->label, 33) == 0) {
+ return data->label_tweak;
+ }
+ return NULL;
+}
+
+static void bench_silentpayments_scan_setup(void* arg) {
+ int i;
+ bench_silentpayments_data *data = (bench_silentpayments_data*)arg;
+ const 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,
+ };
+ 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
+ };
+ unsigned char scalar[32];
+ secp256k1_keypair input_keypair;
+ size_t pubkeylen = 33;
+
+ for (i = 0; i < 32; i++) {
+ scalar[i] = i + 1;
+ }
+ /* Create the first input public key for the full scan from the scalar. */
+ CHECK(secp256k1_keypair_create(data->ctx, &input_keypair, scalar));
+ CHECK(secp256k1_keypair_xonly_pub(data->ctx, &data->tx_inputs[0], NULL, &input_keypair));
+ data->tx_inputs_ptrs[0] = &data->tx_inputs[0];
+ CHECK(secp256k1_ec_pubkey_parse(data->ctx, &data->spend_pubkey, spend_pubkey, pubkeylen));
+ memcpy(data->scan_key, scan_key, 32);
+ memcpy(data->smallest_outpoint, smallest_outpoint, 36);
+
+ /* prepare transaction outputs for the "worst-case scanning attack",
+ * can be used for typical scanning scenarios as well */
+ {
+ secp256k1_silentpayments_recipient *recipients = malloc(sizeof(secp256k1_silentpayments_recipient) * data->num_outputs);
+ const secp256k1_silentpayments_recipient **recipients_ptrs = malloc(sizeof(secp256k1_silentpayments_recipient*) * data->num_outputs);
+ const secp256k1_keypair *keypairs_ptrs[SP_BENCH_MAX_INPUTS];
+ secp256k1_pubkey scan_pubkey, other_scan_pubkey;
+ unsigned char other_scan_seckey[32] = {99};
+ secp256k1_silentpayments_label label;
+ secp256k1_pubkey labeled_spend_pubkey;
+ int index_first_k;
+
+ CHECK(data->num_outputs <= SP_BENCH_MAX_OUTPUTS);
+ CHECK(data->num_matches <= data->num_outputs);
+ index_first_k = data->num_outputs - data->num_matches;
+
+ CHECK(secp256k1_ec_pubkey_create(data->ctx, &scan_pubkey, data->scan_key));
+ CHECK(secp256k1_ec_pubkey_create(data->ctx, &other_scan_pubkey, other_scan_seckey));
+
+ CHECK(secp256k1_silentpayments_recipient_label_create(data->ctx, &label, data->label_tweak, data->scan_key, 0));
+ CHECK(secp256k1_silentpayments_recipient_label_serialize(data->ctx, data->label, &label));
+ CHECK(secp256k1_silentpayments_recipient_create_labeled_spend_pubkey(data->ctx,
+ &labeled_spend_pubkey, &data->spend_pubkey, &label));
+
+ data->tx_outputs = malloc(sizeof(secp256k1_xonly_pubkey) * data->num_outputs);
+ data->tx_outputs_ptrs = malloc(sizeof(secp256k1_xonly_pubkey*) * data->num_outputs);
+ data->found_outputs = malloc(sizeof(secp256k1_silentpayments_found_output) * data->num_outputs);
+ data->found_outputs_ptrs = malloc(sizeof(secp256k1_silentpayments_found_output*) * data->num_outputs);
+
+ keypairs_ptrs[0] = &input_keypair;
+ for (i = 0; i < data->num_outputs; i++) {
+ data->tx_outputs_ptrs[i] = &data->tx_outputs[i];
+ recipients_ptrs[i] = &recipients[i];
+ recipients[i].spend_pubkey = labeled_spend_pubkey;
+ if (i >= index_first_k) {
+ recipients[i].scan_pubkey = scan_pubkey;
+ } else {
+ unsigned char tweak[32] = {0};
+ tweak[31] = 1;
+ /* tweak non-match scan pubkey in order to create single-recipient groups
+ * (we want to avoid running into the recipient group protocol limit) */
+ CHECK(secp256k1_ec_pubkey_tweak_add(data->ctx, &other_scan_pubkey, tweak));
+ recipients[i].scan_pubkey = other_scan_pubkey;
+ }
+ recipients[i].index = i;
+ }
+ CHECK(secp256k1_silentpayments_sender_create_outputs(data->ctx, data->tx_outputs_ptrs, recipients_ptrs,
+ data->num_outputs, data->smallest_outpoint, keypairs_ptrs, SP_BENCH_MAX_INPUTS, NULL, 0));
+
+ for (i = 0; i < data->num_outputs; i++) {
+ data->found_outputs_ptrs[i] = &data->found_outputs[i];
+ }
+ /* reverse outputs within k group to simulate worst-case */
+ for (i = 0; i < data->num_matches / 2; i++) {
+ int pos = index_first_k + i;
+ secp256k1_xonly_pubkey *tmp = data->tx_outputs_ptrs[pos];
+ data->tx_outputs_ptrs[pos] = data->tx_outputs_ptrs[data->num_outputs - i - 1];
+ data->tx_outputs_ptrs[data->num_outputs - i - 1] = tmp;
+ }
+
+ free(recipients_ptrs);
+ free(recipients);
+ }
+}
+
+static void bench_silentpayments_scan_teardown(void* arg, int iters) {
+ bench_silentpayments_data *data = (bench_silentpayments_data*)arg;
+ (void)iters;
+
+ free(data->tx_outputs);
+ free(data->tx_outputs_ptrs);
+ free(data->found_outputs);
+ free(data->found_outputs_ptrs);
+}
+
+static void bench_silentpayments_scan(void* arg, int iters) {
+ bench_silentpayments_data *data = (bench_silentpayments_data*)arg;
+ secp256k1_silentpayments_prevouts_summary prevouts_summary;
+ uint32_t n_found = 0;
+ int i;
+ const secp256k1_silentpayments_label_lookup label_lookup_fn = label_lookup;
+ const void *label_context = data;
+
+ CHECK(data->num_outputs <= SP_BENCH_MAX_OUTPUTS);
+
+ for (i = 0; i < iters; i++) {
+ CHECK(secp256k1_silentpayments_recipient_prevouts_summary_create(data->ctx, &prevouts_summary,
+ data->smallest_outpoint, data->tx_inputs_ptrs, SP_BENCH_MAX_INPUTS, NULL, 0));
+ CHECK(secp256k1_silentpayments_recipient_scan_outputs(data->ctx,
+ data->found_outputs_ptrs, &n_found,
+ (const secp256k1_xonly_pubkey**)data->tx_outputs_ptrs, data->num_outputs,
+ data->scan_key, &prevouts_summary, &data->spend_pubkey,
+ label_lookup_fn, label_context)
+ );
+ CHECK(n_found == (uint32_t)data->num_matches);
+ }
+}
+
+static void run_silentpayments_bench(int iters, int argc, char** argv) {
+ bench_silentpayments_data data;
+ int d = argc == 1;
+
+ data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
+
+ if (d || have_flag(argc, argv, "silentpayments") || have_flag(argc, argv, "silentpayments_scan_nomatch")) {
+ const int num_outputs_bench[] = {2, 5, 10, 100, 1000, 2323, MAX_P2TR_OUTPUTS_PER_BLOCK};
+ size_t o;
+ for (o = 0; o < ARRAY_SIZE(num_outputs_bench); o++) {
+ const int num_outputs = num_outputs_bench[o];
+ char str[64];
+ data.num_outputs = num_outputs;
+ data.num_matches = 0;
+ sprintf(str, "silentpayments_scan_nomatch_N=%i", num_outputs);
+ /* Don't run these slow benchmarks with low iterations (as used e.g. in CI) to prevent slow down */
+ if (iters <= 2 && num_outputs > 10) {
+ printf("Skipping benchmark \"%s\" due to SECP256K1_BENCH_ITERS <= 2\n", str);
+ } else {
+ run_benchmark(str, bench_silentpayments_scan, bench_silentpayments_scan_setup, bench_silentpayments_scan_teardown, &data, 10, num_outputs <= 10 ? iters : 1);
+ }
+ }
+ }
+
+ if (d || have_flag(argc, argv, "silentpayments") || have_flag(argc, argv, "silentpayments_scan_worstcase")) {
+ size_t k;
+ const int num_matches_bench[] = {10, 100, 1000, 2323};
+ for (k = 0; k < ARRAY_SIZE(num_matches_bench); k++) {
+ const int num_matches = num_matches_bench[k];
+ char str[64];
+ data.num_outputs = MAX_P2TR_OUTPUTS_PER_BLOCK;
+ data.num_matches = num_matches;
+ sprintf(str, "silentpayments_scan_worstcase_K=%i", num_matches);
+ /* Don't run these slow benchmarks with low iterations (as used e.g. in CI) to prevent slow down */
+ if (iters <= 2) {
+ printf("Skipping benchmark \"%s\" due to SECP256K1_BENCH_ITERS <= 2\n", str);
+ } else {
+ run_benchmark(str, bench_silentpayments_scan, bench_silentpayments_scan_setup, bench_silentpayments_scan_teardown, &data, 3, 1);
+ }
+ }
+ }
+
+ secp256k1_context_destroy(data.ctx);
+}
+
+#endif /* SECP256K1_MODULE_SILENTPAYMENTS_BENCH_H */
diff --git a/src/modules/silentpayments/bip352_send_and_receive_test_vectors.json b/src/modules/silentpayments/bip352_send_and_receive_test_vectors.json
new file mode 100644
index 00000000..3a189757
--- /dev/null
+++ b/src/modules/silentpayments/bip352_send_and_receive_test_vectors.json
@@ -0,0 +1,5729 @@
+[
+ {
+ "comment": "Simple send: two inputs",
+ "sending": [
+ {
+ "given": {
+ "vin": [
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 0,
+ "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac"
+ }
+ },
+ "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1"
+ },
+ {
+ "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d",
+ "vout": 0,
+ "scriptSig": "48304602210086783ded73e961037e77d49d9deee4edc2b23136e9728d56e4491c80015c3a63022100fda4c0f21ea18de29edbce57f7134d613e044ee150a89e2e64700de2d4e83d4e2103bd85685d03d111699b15d046319febe77f8de5286e9e512703cdee1bf3be3792",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a914d9317c66f54ff0a152ec50b1d19c25be50c8e15988ac"
+ }
+ },
+ "private_key": "93f5ed907ad5b2bdbbdcb5d9116ebc0a4e1f92f910d5260237fa45a9408aad16"
+ }
+ ],
+ "recipients": [
+ {
+ "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv",
+ "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4",
+ "spend_pub_key": "025cc9856d6f8375350e123978daac200c260cb5b5ae83106cab90484dcd8fcf36"
+ }
+ ]
+ },
+ "expected": {
+ "outputs": [
+ [
+ "3e9fce73d4e77a4809908e3c3a2e54ee147b9312dc5044a193d1fc85de46e3c1"
+ ]
+ ],
+ "shared_secrets": [
+ "028158aff7d61ea66b2fa7f555bc3c5937d1debbde16423d630f9aa7943e14d80d"
+ ],
+ "input_private_key_sum": "7ed265a6dac7aba8508a32d6d6b84c7f1dbd0a0941dd01088d69e8d556345f86",
+ "input_pub_keys": [
+ "025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "03bd85685d03d111699b15d046319febe77f8de5286e9e512703cdee1bf3be3792"
+ ]
+ }
+ }
+ ],
+ "receiving": [
+ {
+ "given": {
+ "vin": [
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 0,
+ "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac"
+ }
+ }
+ },
+ {
+ "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d",
+ "vout": 0,
+ "scriptSig": "48304602210086783ded73e961037e77d49d9deee4edc2b23136e9728d56e4491c80015c3a63022100fda4c0f21ea18de29edbce57f7134d613e044ee150a89e2e64700de2d4e83d4e2103bd85685d03d111699b15d046319febe77f8de5286e9e512703cdee1bf3be3792",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a914d9317c66f54ff0a152ec50b1d19c25be50c8e15988ac"
+ }
+ }
+ }
+ ],
+ "outputs": [
+ "3e9fce73d4e77a4809908e3c3a2e54ee147b9312dc5044a193d1fc85de46e3c1"
+ ],
+ "key_material": {
+ "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3",
+ "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c"
+ },
+ "labels": []
+ },
+ "expected": {
+ "addresses": [
+ "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv"
+ ],
+ "outputs": [
+ {
+ "priv_key_tweak": "f438b40179a3c4262de12986c0e6cce0634007cdc79c1dcd3e20b9ebc2e7eef6",
+ "pub_key": "3e9fce73d4e77a4809908e3c3a2e54ee147b9312dc5044a193d1fc85de46e3c1",
+ "signature": "74f85b856337fbe837643b86f462118159f93ac4acc2671522f27e8f67b079959195ccc7a5dbee396d2909f5d680d6e30cda7359aa2755822509b70d6b0687a1"
+ }
+ ],
+ "tweak": "024ac253c216532e961988e2a8ce266a447c894c781e52ef6cee902361db960004",
+ "shared_secret": "028158aff7d61ea66b2fa7f555bc3c5937d1debbde16423d630f9aa7943e14d80d",
+ "input_pub_key_sum": "032562c1ab2d6bd45d7ca4d78f569999e5333dffd3ac5263924fd00d00dedc4bee"
+ }
+ }
+ ]
+ },
+ {
+ "comment": "Simple send: two inputs, order reversed",
+ "sending": [
+ {
+ "given": {
+ "vin": [
+ {
+ "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d",
+ "vout": 0,
+ "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac"
+ }
+ },
+ "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1"
+ },
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 0,
+ "scriptSig": "48304602210086783ded73e961037e77d49d9deee4edc2b23136e9728d56e4491c80015c3a63022100fda4c0f21ea18de29edbce57f7134d613e044ee150a89e2e64700de2d4e83d4e2103bd85685d03d111699b15d046319febe77f8de5286e9e512703cdee1bf3be3792",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a914d9317c66f54ff0a152ec50b1d19c25be50c8e15988ac"
+ }
+ },
+ "private_key": "93f5ed907ad5b2bdbbdcb5d9116ebc0a4e1f92f910d5260237fa45a9408aad16"
+ }
+ ],
+ "recipients": [
+ {
+ "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv",
+ "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4",
+ "spend_pub_key": "025cc9856d6f8375350e123978daac200c260cb5b5ae83106cab90484dcd8fcf36"
+ }
+ ]
+ },
+ "expected": {
+ "outputs": [
+ [
+ "3e9fce73d4e77a4809908e3c3a2e54ee147b9312dc5044a193d1fc85de46e3c1"
+ ]
+ ],
+ "shared_secrets": [
+ "028158aff7d61ea66b2fa7f555bc3c5937d1debbde16423d630f9aa7943e14d80d"
+ ],
+ "input_private_key_sum": "7ed265a6dac7aba8508a32d6d6b84c7f1dbd0a0941dd01088d69e8d556345f86",
+ "input_pub_keys": [
+ "025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "03bd85685d03d111699b15d046319febe77f8de5286e9e512703cdee1bf3be3792"
+ ]
+ }
+ }
+ ],
+ "receiving": [
+ {
+ "given": {
+ "vin": [
+ {
+ "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d",
+ "vout": 0,
+ "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac"
+ }
+ }
+ },
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 0,
+ "scriptSig": "48304602210086783ded73e961037e77d49d9deee4edc2b23136e9728d56e4491c80015c3a63022100fda4c0f21ea18de29edbce57f7134d613e044ee150a89e2e64700de2d4e83d4e2103bd85685d03d111699b15d046319febe77f8de5286e9e512703cdee1bf3be3792",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a914d9317c66f54ff0a152ec50b1d19c25be50c8e15988ac"
+ }
+ }
+ }
+ ],
+ "outputs": [
+ "3e9fce73d4e77a4809908e3c3a2e54ee147b9312dc5044a193d1fc85de46e3c1"
+ ],
+ "key_material": {
+ "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3",
+ "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c"
+ },
+ "labels": []
+ },
+ "expected": {
+ "addresses": [
+ "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv"
+ ],
+ "outputs": [
+ {
+ "priv_key_tweak": "f438b40179a3c4262de12986c0e6cce0634007cdc79c1dcd3e20b9ebc2e7eef6",
+ "pub_key": "3e9fce73d4e77a4809908e3c3a2e54ee147b9312dc5044a193d1fc85de46e3c1",
+ "signature": "74f85b856337fbe837643b86f462118159f93ac4acc2671522f27e8f67b079959195ccc7a5dbee396d2909f5d680d6e30cda7359aa2755822509b70d6b0687a1"
+ }
+ ],
+ "tweak": "024ac253c216532e961988e2a8ce266a447c894c781e52ef6cee902361db960004",
+ "shared_secret": "028158aff7d61ea66b2fa7f555bc3c5937d1debbde16423d630f9aa7943e14d80d",
+ "input_pub_key_sum": "032562c1ab2d6bd45d7ca4d78f569999e5333dffd3ac5263924fd00d00dedc4bee"
+ }
+ }
+ ]
+ },
+ {
+ "comment": "Simple send: two inputs from the same transaction",
+ "sending": [
+ {
+ "given": {
+ "vin": [
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 3,
+ "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac"
+ }
+ },
+ "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1"
+ },
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 7,
+ "scriptSig": "48304602210086783ded73e961037e77d49d9deee4edc2b23136e9728d56e4491c80015c3a63022100fda4c0f21ea18de29edbce57f7134d613e044ee150a89e2e64700de2d4e83d4e2103bd85685d03d111699b15d046319febe77f8de5286e9e512703cdee1bf3be3792",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a914d9317c66f54ff0a152ec50b1d19c25be50c8e15988ac"
+ }
+ },
+ "private_key": "93f5ed907ad5b2bdbbdcb5d9116ebc0a4e1f92f910d5260237fa45a9408aad16"
+ }
+ ],
+ "recipients": [
+ {
+ "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv",
+ "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4",
+ "spend_pub_key": "025cc9856d6f8375350e123978daac200c260cb5b5ae83106cab90484dcd8fcf36"
+ }
+ ]
+ },
+ "expected": {
+ "outputs": [
+ [
+ "79e71baa2ba3fc66396de3a04f168c7bf24d6870ec88ca877754790c1db357b6"
+ ]
+ ],
+ "shared_secrets": [
+ "03aa707f7b5e94b448abd28aa217e3d7a7cc6bb07f1a8d07be4de91bf7b1417469"
+ ],
+ "input_private_key_sum": "7ed265a6dac7aba8508a32d6d6b84c7f1dbd0a0941dd01088d69e8d556345f86",
+ "input_pub_keys": [
+ "025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "03bd85685d03d111699b15d046319febe77f8de5286e9e512703cdee1bf3be3792"
+ ]
+ }
+ }
+ ],
+ "receiving": [
+ {
+ "given": {
+ "vin": [
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 3,
+ "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac"
+ }
+ }
+ },
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 7,
+ "scriptSig": "48304602210086783ded73e961037e77d49d9deee4edc2b23136e9728d56e4491c80015c3a63022100fda4c0f21ea18de29edbce57f7134d613e044ee150a89e2e64700de2d4e83d4e2103bd85685d03d111699b15d046319febe77f8de5286e9e512703cdee1bf3be3792",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a914d9317c66f54ff0a152ec50b1d19c25be50c8e15988ac"
+ }
+ }
+ }
+ ],
+ "outputs": [
+ "79e71baa2ba3fc66396de3a04f168c7bf24d6870ec88ca877754790c1db357b6"
+ ],
+ "key_material": {
+ "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3",
+ "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c"
+ },
+ "labels": []
+ },
+ "expected": {
+ "addresses": [
+ "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv"
+ ],
+ "outputs": [
+ {
+ "priv_key_tweak": "4851455bfbe1ab4f80156570aa45063201aa5c9e1b1dcd29f0f8c33d10bf77ae",
+ "pub_key": "79e71baa2ba3fc66396de3a04f168c7bf24d6870ec88ca877754790c1db357b6",
+ "signature": "10332eea808b6a13f70059a8a73195808db782012907f5ba32b6eae66a2f66b4f65147e2b968a1678c5f73d57d5d195dbaf667b606ff80c8490eac1f3b710657"
+ }
+ ],
+ "tweak": "03aeea547819c08413974e2ab2b12212e007166bb2058f88b009e082b9b4914a58",
+ "shared_secret": "03aa707f7b5e94b448abd28aa217e3d7a7cc6bb07f1a8d07be4de91bf7b1417469",
+ "input_pub_key_sum": "032562c1ab2d6bd45d7ca4d78f569999e5333dffd3ac5263924fd00d00dedc4bee"
+ }
+ }
+ ]
+ },
+ {
+ "comment": "Simple send: two inputs from the same transaction, order reversed",
+ "sending": [
+ {
+ "given": {
+ "vin": [
+ {
+ "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d",
+ "vout": 7,
+ "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac"
+ }
+ },
+ "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1"
+ },
+ {
+ "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d",
+ "vout": 3,
+ "scriptSig": "48304602210086783ded73e961037e77d49d9deee4edc2b23136e9728d56e4491c80015c3a63022100fda4c0f21ea18de29edbce57f7134d613e044ee150a89e2e64700de2d4e83d4e2103bd85685d03d111699b15d046319febe77f8de5286e9e512703cdee1bf3be3792",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a914d9317c66f54ff0a152ec50b1d19c25be50c8e15988ac"
+ }
+ },
+ "private_key": "93f5ed907ad5b2bdbbdcb5d9116ebc0a4e1f92f910d5260237fa45a9408aad16"
+ }
+ ],
+ "recipients": [
+ {
+ "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv",
+ "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4",
+ "spend_pub_key": "025cc9856d6f8375350e123978daac200c260cb5b5ae83106cab90484dcd8fcf36"
+ }
+ ]
+ },
+ "expected": {
+ "outputs": [
+ [
+ "f4c2da807f89cb1501f1a77322a895acfb93c28e08ed2724d2beb8e44539ba38"
+ ]
+ ],
+ "shared_secrets": [
+ "03054f5c84b07182ba2a2e10a35e088778f95c04f059f4574b024c372eb8ce5468"
+ ],
+ "input_private_key_sum": "7ed265a6dac7aba8508a32d6d6b84c7f1dbd0a0941dd01088d69e8d556345f86",
+ "input_pub_keys": [
+ "025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "03bd85685d03d111699b15d046319febe77f8de5286e9e512703cdee1bf3be3792"
+ ]
+ }
+ }
+ ],
+ "receiving": [
+ {
+ "given": {
+ "vin": [
+ {
+ "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d",
+ "vout": 7,
+ "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac"
+ }
+ }
+ },
+ {
+ "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d",
+ "vout": 3,
+ "scriptSig": "48304602210086783ded73e961037e77d49d9deee4edc2b23136e9728d56e4491c80015c3a63022100fda4c0f21ea18de29edbce57f7134d613e044ee150a89e2e64700de2d4e83d4e2103bd85685d03d111699b15d046319febe77f8de5286e9e512703cdee1bf3be3792",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a914d9317c66f54ff0a152ec50b1d19c25be50c8e15988ac"
+ }
+ }
+ }
+ ],
+ "outputs": [
+ "f4c2da807f89cb1501f1a77322a895acfb93c28e08ed2724d2beb8e44539ba38"
+ ],
+ "key_material": {
+ "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3",
+ "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c"
+ },
+ "labels": []
+ },
+ "expected": {
+ "addresses": [
+ "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv"
+ ],
+ "outputs": [
+ {
+ "priv_key_tweak": "ab0c9b87181bf527879f48db9f14a02233619b986f8e8f2d5d408ce68a709f51",
+ "pub_key": "f4c2da807f89cb1501f1a77322a895acfb93c28e08ed2724d2beb8e44539ba38",
+ "signature": "398a9790865791a9db41a8015afad3a47d60fec5086c50557806a49a1bc038808632b8fe679a7bb65fc6b455be994502eed849f1da3729cd948fc7be73d67295"
+ }
+ ],
+ "tweak": "024cad5180a093d3af0f49f586bdf37f890920178e68e80561ed53351d0fa499ad",
+ "shared_secret": "03054f5c84b07182ba2a2e10a35e088778f95c04f059f4574b024c372eb8ce5468",
+ "input_pub_key_sum": "032562c1ab2d6bd45d7ca4d78f569999e5333dffd3ac5263924fd00d00dedc4bee"
+ }
+ }
+ ]
+ },
+ {
+ "comment": "Outpoint ordering byte-lexicographically vs. vout-integer",
+ "sending": [
+ {
+ "given": {
+ "vin": [
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 1,
+ "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac"
+ }
+ },
+ "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1"
+ },
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 256,
+ "scriptSig": "48304602210086783ded73e961037e77d49d9deee4edc2b23136e9728d56e4491c80015c3a63022100fda4c0f21ea18de29edbce57f7134d613e044ee150a89e2e64700de2d4e83d4e2103bd85685d03d111699b15d046319febe77f8de5286e9e512703cdee1bf3be3792",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a914d9317c66f54ff0a152ec50b1d19c25be50c8e15988ac"
+ }
+ },
+ "private_key": "93f5ed907ad5b2bdbbdcb5d9116ebc0a4e1f92f910d5260237fa45a9408aad16"
+ }
+ ],
+ "recipients": [
+ {
+ "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv",
+ "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4",
+ "spend_pub_key": "025cc9856d6f8375350e123978daac200c260cb5b5ae83106cab90484dcd8fcf36"
+ }
+ ]
+ },
+ "expected": {
+ "outputs": [
+ [
+ "a85ef8701394b517a4b35217c4bd37ac01ebeed4b008f8d0879f9e09ba95319c"
+ ]
+ ],
+ "shared_secrets": [
+ "02cb25a6e7c9b7c6d550e0413da63834678465b5e80853a51d0335d318296ac182"
+ ],
+ "input_private_key_sum": "7ed265a6dac7aba8508a32d6d6b84c7f1dbd0a0941dd01088d69e8d556345f86",
+ "input_pub_keys": [
+ "025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "03bd85685d03d111699b15d046319febe77f8de5286e9e512703cdee1bf3be3792"
+ ]
+ }
+ }
+ ],
+ "receiving": [
+ {
+ "given": {
+ "vin": [
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 1,
+ "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac"
+ }
+ }
+ },
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 256,
+ "scriptSig": "48304602210086783ded73e961037e77d49d9deee4edc2b23136e9728d56e4491c80015c3a63022100fda4c0f21ea18de29edbce57f7134d613e044ee150a89e2e64700de2d4e83d4e2103bd85685d03d111699b15d046319febe77f8de5286e9e512703cdee1bf3be3792",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a914d9317c66f54ff0a152ec50b1d19c25be50c8e15988ac"
+ }
+ }
+ }
+ ],
+ "outputs": [
+ "a85ef8701394b517a4b35217c4bd37ac01ebeed4b008f8d0879f9e09ba95319c"
+ ],
+ "key_material": {
+ "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3",
+ "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c"
+ },
+ "labels": []
+ },
+ "expected": {
+ "addresses": [
+ "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv"
+ ],
+ "outputs": [
+ {
+ "priv_key_tweak": "c8ac0292997b5bca98b3ebd99a57e253071137550f270452cd3df8a3e2266d36",
+ "pub_key": "a85ef8701394b517a4b35217c4bd37ac01ebeed4b008f8d0879f9e09ba95319c",
+ "signature": "c036ee38bfe46aba03234339ae7219b31b824b52ef9d5ce05810a0d6f62330dedc2b55652578aa5bdabf930fae941acd839d5a66f8fce7caa9710ccb446bddd1"
+ }
+ ],
+ "tweak": "031f9a80d0938cf980b51f7cc4fad713d49037f430646dff129c0570d75a40d8f0",
+ "shared_secret": "02cb25a6e7c9b7c6d550e0413da63834678465b5e80853a51d0335d318296ac182",
+ "input_pub_key_sum": "032562c1ab2d6bd45d7ca4d78f569999e5333dffd3ac5263924fd00d00dedc4bee"
+ }
+ }
+ ]
+ },
+ {
+ "comment": "Single recipient: multiple UTXOs from the same public key",
+ "sending": [
+ {
+ "given": {
+ "vin": [
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 0,
+ "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac"
+ }
+ },
+ "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1"
+ },
+ {
+ "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d",
+ "vout": 0,
+ "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac"
+ }
+ },
+ "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1"
+ }
+ ],
+ "recipients": [
+ {
+ "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv",
+ "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4",
+ "spend_pub_key": "025cc9856d6f8375350e123978daac200c260cb5b5ae83106cab90484dcd8fcf36"
+ }
+ ]
+ },
+ "expected": {
+ "outputs": [
+ [
+ "548ae55c8eec1e736e8d3e520f011f1f42a56d166116ad210b3937599f87f566"
+ ]
+ ],
+ "shared_secrets": [
+ "02f6b40ff17f4010fe732ac4b0f2f211281aa09c9a5fb41f1c151ec2606fee9ec2"
+ ],
+ "input_private_key_sum": "d5b8f02cbfe3f1d5295af9fb8a9320e859e9cb07115856486ab1a4e4fb89a621",
+ "input_pub_keys": [
+ "025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5"
+ ]
+ }
+ }
+ ],
+ "receiving": [
+ {
+ "given": {
+ "vin": [
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 0,
+ "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac"
+ }
+ }
+ },
+ {
+ "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d",
+ "vout": 0,
+ "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac"
+ }
+ }
+ }
+ ],
+ "outputs": [
+ "548ae55c8eec1e736e8d3e520f011f1f42a56d166116ad210b3937599f87f566"
+ ],
+ "key_material": {
+ "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3",
+ "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c"
+ },
+ "labels": []
+ },
+ "expected": {
+ "addresses": [
+ "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv"
+ ],
+ "outputs": [
+ {
+ "priv_key_tweak": "f032695e2636619efa523fffaa9ef93c8802299181fd0461913c1b8daf9784cd",
+ "pub_key": "548ae55c8eec1e736e8d3e520f011f1f42a56d166116ad210b3937599f87f566",
+ "signature": "f238386c5d5e5444f8d2c75aabbcb28c346f208c76f60823f5de3b67b79e0ec72ea5de2d7caec314e0971d3454f122dda342b3eede01b3857e83654e36b25f76"
+ }
+ ],
+ "tweak": "0319949463fc6a2368d999a2a6a2bcb2dbf64a2ac6e00b3ba5659780c860a6d9e0",
+ "shared_secret": "02f6b40ff17f4010fe732ac4b0f2f211281aa09c9a5fb41f1c151ec2606fee9ec2",
+ "input_pub_key_sum": "03e40664e222ba71e29b80efc907fa22a3c6c64f45e89dbb8511dc7a3712b0a186"
+ }
+ }
+ ]
+ },
+ {
+ "comment": "Single recipient: taproot only inputs with even y-values",
+ "sending": [
+ {
+ "given": {
+ "vin": [
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 0,
+ "scriptSig": "",
+ "txinwitness": "0140c459b671370d12cfb5acee76da7e3ba7cc29b0b4653e3af8388591082660137d087fdc8e89a612cd5d15be0febe61fc7cdcf3161a26e599a4514aa5c3e86f47b",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "51205a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5"
+ }
+ },
+ "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1"
+ },
+ {
+ "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d",
+ "vout": 0,
+ "scriptSig": "",
+ "txinwitness": "0140bd1e708f92dbeaf24a6b8dd22e59c6274355424d62baea976b449e220fd75b13578e262ab11b7aa58e037f0c6b0519b66803b7d9decaa1906dedebfb531c56c1",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "5120782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338"
+ }
+ },
+ "private_key": "fc8716a97a48ba9a05a98ae47b5cd201a25a7fd5d8b73c203c5f7b6b6b3b6ad7"
+ }
+ ],
+ "recipients": [
+ {
+ "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv",
+ "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4",
+ "spend_pub_key": "025cc9856d6f8375350e123978daac200c260cb5b5ae83106cab90484dcd8fcf36"
+ }
+ ]
+ },
+ "expected": {
+ "outputs": [
+ [
+ "de88bea8e7ffc9ce1af30d1132f910323c505185aec8eae361670421e749a1fb"
+ ]
+ ],
+ "shared_secrets": [
+ "02de9719785c6d09f71571dadf44bca59edba2af3e689c65cbc3bb5a4a387732ef"
+ ],
+ "input_private_key_sum": "e7638ebfda3ab3849a5707e240a6627671f7f6e609bf172691cf1e9780e51d47",
+ "input_pub_keys": [
+ "025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "02782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338"
+ ]
+ }
+ }
+ ],
+ "receiving": [
+ {
+ "given": {
+ "vin": [
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 0,
+ "scriptSig": "",
+ "txinwitness": "0140c459b671370d12cfb5acee76da7e3ba7cc29b0b4653e3af8388591082660137d087fdc8e89a612cd5d15be0febe61fc7cdcf3161a26e599a4514aa5c3e86f47b",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "51205a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5"
+ }
+ }
+ },
+ {
+ "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d",
+ "vout": 0,
+ "scriptSig": "",
+ "txinwitness": "0140bd1e708f92dbeaf24a6b8dd22e59c6274355424d62baea976b449e220fd75b13578e262ab11b7aa58e037f0c6b0519b66803b7d9decaa1906dedebfb531c56c1",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "5120782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338"
+ }
+ }
+ }
+ ],
+ "outputs": [
+ "de88bea8e7ffc9ce1af30d1132f910323c505185aec8eae361670421e749a1fb"
+ ],
+ "key_material": {
+ "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3",
+ "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c"
+ },
+ "labels": []
+ },
+ "expected": {
+ "addresses": [
+ "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv"
+ ],
+ "outputs": [
+ {
+ "priv_key_tweak": "3fb9ce5ce1746ced103c8ed254e81f6690764637ddbc876ec1f9b3ddab776b03",
+ "pub_key": "de88bea8e7ffc9ce1af30d1132f910323c505185aec8eae361670421e749a1fb",
+ "signature": "c5acd25a8f021a4192f93bc34403fd8b76484613466336fb259c72d04c169824f2690ca34e96cee86b69f376c8377003268fda56feeb1b873e5783d7e19bcca5"
+ }
+ ],
+ "tweak": "02dc59cc8e8873b65c1dd5c416d4fbeb647372c329bd84a70c05b310e222e2c183",
+ "shared_secret": "02de9719785c6d09f71571dadf44bca59edba2af3e689c65cbc3bb5a4a387732ef",
+ "input_pub_key_sum": "038180a2125f9d6dd116e1a6139be4d72fd5057dab6aaabaa5654817c11baeb3ba"
+ }
+ }
+ ]
+ },
+ {
+ "comment": "Single recipient: taproot only with mixed even/odd y-values",
+ "sending": [
+ {
+ "given": {
+ "vin": [
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 0,
+ "scriptSig": "",
+ "txinwitness": "0140c459b671370d12cfb5acee76da7e3ba7cc29b0b4653e3af8388591082660137d087fdc8e89a612cd5d15be0febe61fc7cdcf3161a26e599a4514aa5c3e86f47b",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "51205a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5"
+ }
+ },
+ "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1"
+ },
+ {
+ "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d",
+ "vout": 0,
+ "scriptSig": "",
+ "txinwitness": "01400a4d0dca6293f40499394d7eefe14a1de11e0e3454f51de2e802592abf5ee549042a1b1a8fb2e149ee9dd3f086c1b69b2f182565ab6ecf599b1ec9ebadfda6c5",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "51208c8d23d4764feffcd5e72e380802540fa0f88e3d62ad5e0b47955f74d7b283c4"
+ }
+ },
+ "private_key": "1d37787c2b7116ee983e9f9c13269df29091b391c04db94239e0d2bc2182c3bf"
+ }
+ ],
+ "recipients": [
+ {
+ "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv",
+ "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4",
+ "spend_pub_key": "025cc9856d6f8375350e123978daac200c260cb5b5ae83106cab90484dcd8fcf36"
+ }
+ ]
+ },
+ "expected": {
+ "outputs": [
+ [
+ "77cab7dd12b10259ee82c6ea4b509774e33e7078e7138f568092241bf26b99f1"
+ ]
+ ],
+ "shared_secrets": [
+ "030e7f5ca4bf109fc35c8c2d878f756c891ac04c456cc5f0b05fcec4d3b2b1beb2"
+ ],
+ "input_private_key_sum": "cda4ff9a3480e1fbfc6edd61b222f280f9baa0652002c1ffdb612efcc45d2ff2",
+ "input_pub_keys": [
+ "025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "028c8d23d4764feffcd5e72e380802540fa0f88e3d62ad5e0b47955f74d7b283c4"
+ ]
+ }
+ }
+ ],
+ "receiving": [
+ {
+ "given": {
+ "vin": [
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 0,
+ "scriptSig": "",
+ "txinwitness": "0140c459b671370d12cfb5acee76da7e3ba7cc29b0b4653e3af8388591082660137d087fdc8e89a612cd5d15be0febe61fc7cdcf3161a26e599a4514aa5c3e86f47b",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "51205a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5"
+ }
+ }
+ },
+ {
+ "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d",
+ "vout": 0,
+ "scriptSig": "",
+ "txinwitness": "01400a4d0dca6293f40499394d7eefe14a1de11e0e3454f51de2e802592abf5ee549042a1b1a8fb2e149ee9dd3f086c1b69b2f182565ab6ecf599b1ec9ebadfda6c5",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "51208c8d23d4764feffcd5e72e380802540fa0f88e3d62ad5e0b47955f74d7b283c4"
+ }
+ }
+ }
+ ],
+ "outputs": [
+ "77cab7dd12b10259ee82c6ea4b509774e33e7078e7138f568092241bf26b99f1"
+ ],
+ "key_material": {
+ "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3",
+ "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c"
+ },
+ "labels": []
+ },
+ "expected": {
+ "addresses": [
+ "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv"
+ ],
+ "outputs": [
+ {
+ "priv_key_tweak": "f5382508609771068ed079b24e1f72e4a17ee6d1c979066bf1d4e2a5676f09d4",
+ "pub_key": "77cab7dd12b10259ee82c6ea4b509774e33e7078e7138f568092241bf26b99f1",
+ "signature": "ff65833b8fd1ed3ef9d0443b4f702b45a3f2dd457ba247687e8207745c3be9d2bdad0ab3f07118f8b2efc6a04b95f7b3e218daf8a64137ec91bd2fc67fc137a5"
+ }
+ ],
+ "tweak": "03b990f5b1d90ea8fd4bdd5c856a9dfe17035d196958062e2c6cb4c99e413f3548",
+ "shared_secret": "030e7f5ca4bf109fc35c8c2d878f756c891ac04c456cc5f0b05fcec4d3b2b1beb2",
+ "input_pub_key_sum": "020f0ab50f420ab1249bc2a21659c607f2873400853035aad0ca6d0ded04d62623"
+ }
+ }
+ ]
+ },
+ {
+ "comment": "Single recipient: taproot input with even y-value and non-taproot input",
+ "sending": [
+ {
+ "given": {
+ "vin": [
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 0,
+ "scriptSig": "",
+ "txinwitness": "0140c459b671370d12cfb5acee76da7e3ba7cc29b0b4653e3af8388591082660137d087fdc8e89a612cd5d15be0febe61fc7cdcf3161a26e599a4514aa5c3e86f47b",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "51205a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5"
+ }
+ },
+ "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1"
+ },
+ {
+ "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d",
+ "vout": 0,
+ "scriptSig": "463044021f24e010c6e475814740ba24c8cf9362c4db1276b7f46a7b1e63473159a80ec30221008198e8ece7b7f88e6c6cc6bb8c86f9f00b7458222a8c91addf6e1577bcf7697e2103e0ec4f64b3fa2e463ccfcf4e856e37d5e1e20275bc89ec1def9eb098eff1f85d",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a9148cbc7dfe44f1579bff3340bbef1eddeaeb1fc97788ac"
+ }
+ },
+ "private_key": "8d4751f6e8a3586880fb66c19ae277969bd5aa06f61c4ee2f1e2486efdf666d3"
+ }
+ ],
+ "recipients": [
+ {
+ "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv",
+ "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4",
+ "spend_pub_key": "025cc9856d6f8375350e123978daac200c260cb5b5ae83106cab90484dcd8fcf36"
+ }
+ ]
+ },
+ "expected": {
+ "outputs": [
+ [
+ "30523cca96b2a9ae3c98beb5e60f7d190ec5bc79b2d11a0b2d4d09a608c448f0"
+ ]
+ ],
+ "shared_secrets": [
+ "021cd92ff153e638d0a97bcd11fafc81c321b111f5ba1efff593371b7b688efdd3"
+ ],
+ "input_private_key_sum": "7823ca0d4895515315a8e3bf602c080b6b732117272429e94751eb9b13a01943",
+ "input_pub_keys": [
+ "025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "03e0ec4f64b3fa2e463ccfcf4e856e37d5e1e20275bc89ec1def9eb098eff1f85d"
+ ]
+ }
+ }
+ ],
+ "receiving": [
+ {
+ "given": {
+ "vin": [
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 0,
+ "scriptSig": "",
+ "txinwitness": "0140c459b671370d12cfb5acee76da7e3ba7cc29b0b4653e3af8388591082660137d087fdc8e89a612cd5d15be0febe61fc7cdcf3161a26e599a4514aa5c3e86f47b",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "51205a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5"
+ }
+ }
+ },
+ {
+ "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d",
+ "vout": 0,
+ "scriptSig": "463044021f24e010c6e475814740ba24c8cf9362c4db1276b7f46a7b1e63473159a80ec30221008198e8ece7b7f88e6c6cc6bb8c86f9f00b7458222a8c91addf6e1577bcf7697e2103e0ec4f64b3fa2e463ccfcf4e856e37d5e1e20275bc89ec1def9eb098eff1f85d",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a9148cbc7dfe44f1579bff3340bbef1eddeaeb1fc97788ac"
+ }
+ }
+ }
+ ],
+ "outputs": [
+ "30523cca96b2a9ae3c98beb5e60f7d190ec5bc79b2d11a0b2d4d09a608c448f0"
+ ],
+ "key_material": {
+ "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3",
+ "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c"
+ },
+ "labels": []
+ },
+ "expected": {
+ "addresses": [
+ "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv"
+ ],
+ "outputs": [
+ {
+ "priv_key_tweak": "b40017865c79b1fcbed68896791be93186d08f47e416b289b8c063777e14e8df",
+ "pub_key": "30523cca96b2a9ae3c98beb5e60f7d190ec5bc79b2d11a0b2d4d09a608c448f0",
+ "signature": "d1edeea28cf1033bcb3d89376cabaaaa2886cbd8fda112b5c61cc90a4e7f1878bdd62180b07d1dfc8ffee1863c525a0c7b5bcd413183282cfda756cb65787266"
+ }
+ ],
+ "tweak": "0233c2a447b8b244e4ffcfb59fe365eaa3bb22288b31e2113b9998861f40d4d6da",
+ "shared_secret": "021cd92ff153e638d0a97bcd11fafc81c321b111f5ba1efff593371b7b688efdd3",
+ "input_pub_key_sum": "031ecda9c64faaa6cd57c9f3d7c62bcfc0763c2627ed8dc0e2c3018e9ff37a0bf0"
+ }
+ }
+ ]
+ },
+ {
+ "comment": "Single recipient: taproot input with odd y-value and non-taproot input",
+ "sending": [
+ {
+ "given": {
+ "vin": [
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 0,
+ "scriptSig": "",
+ "txinwitness": "01400a4d0dca6293f40499394d7eefe14a1de11e0e3454f51de2e802592abf5ee549042a1b1a8fb2e149ee9dd3f086c1b69b2f182565ab6ecf599b1ec9ebadfda6c5",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "51208c8d23d4764feffcd5e72e380802540fa0f88e3d62ad5e0b47955f74d7b283c4"
+ }
+ },
+ "private_key": "1d37787c2b7116ee983e9f9c13269df29091b391c04db94239e0d2bc2182c3bf"
+ },
+ {
+ "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d",
+ "vout": 0,
+ "scriptSig": "463044021f24e010c6e475814740ba24c8cf9362c4db1276b7f46a7b1e63473159a80ec30221008198e8ece7b7f88e6c6cc6bb8c86f9f00b7458222a8c91addf6e1577bcf7697e2103e0ec4f64b3fa2e463ccfcf4e856e37d5e1e20275bc89ec1def9eb098eff1f85d",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a9148cbc7dfe44f1579bff3340bbef1eddeaeb1fc97788ac"
+ }
+ },
+ "private_key": "8d4751f6e8a3586880fb66c19ae277969bd5aa06f61c4ee2f1e2486efdf666d3"
+ }
+ ],
+ "recipients": [
+ {
+ "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv",
+ "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4",
+ "spend_pub_key": "025cc9856d6f8375350e123978daac200c260cb5b5ae83106cab90484dcd8fcf36"
+ }
+ ]
+ },
+ "expected": {
+ "outputs": [
+ [
+ "359358f59ee9e9eec3f00bdf4882570fd5c182e451aa2650b788544aff012a3a"
+ ]
+ ],
+ "shared_secrets": [
+ "03d9437eb3676cf5cc00feebe68bc44c4567332e4b89788dec9eceb3779054442b"
+ ],
+ "input_private_key_sum": "700fd97abd324179e8bcc72587bbd9a40b43f67535ce95a0b80175b2dc73a314",
+ "input_pub_keys": [
+ "028c8d23d4764feffcd5e72e380802540fa0f88e3d62ad5e0b47955f74d7b283c4",
+ "03e0ec4f64b3fa2e463ccfcf4e856e37d5e1e20275bc89ec1def9eb098eff1f85d"
+ ]
+ }
+ }
+ ],
+ "receiving": [
+ {
+ "given": {
+ "vin": [
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 0,
+ "scriptSig": "",
+ "txinwitness": "01400a4d0dca6293f40499394d7eefe14a1de11e0e3454f51de2e802592abf5ee549042a1b1a8fb2e149ee9dd3f086c1b69b2f182565ab6ecf599b1ec9ebadfda6c5",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "51208c8d23d4764feffcd5e72e380802540fa0f88e3d62ad5e0b47955f74d7b283c4"
+ }
+ }
+ },
+ {
+ "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d",
+ "vout": 0,
+ "scriptSig": "463044021f24e010c6e475814740ba24c8cf9362c4db1276b7f46a7b1e63473159a80ec30221008198e8ece7b7f88e6c6cc6bb8c86f9f00b7458222a8c91addf6e1577bcf7697e2103e0ec4f64b3fa2e463ccfcf4e856e37d5e1e20275bc89ec1def9eb098eff1f85d",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a9148cbc7dfe44f1579bff3340bbef1eddeaeb1fc97788ac"
+ }
+ }
+ }
+ ],
+ "outputs": [
+ "359358f59ee9e9eec3f00bdf4882570fd5c182e451aa2650b788544aff012a3a"
+ ],
+ "key_material": {
+ "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3",
+ "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c"
+ },
+ "labels": []
+ },
+ "expected": {
+ "addresses": [
+ "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv"
+ ],
+ "outputs": [
+ {
+ "priv_key_tweak": "a2f9dd05d1d398347c885d9c61a64d18a264de6d49cea4326bafc2791d627fa7",
+ "pub_key": "359358f59ee9e9eec3f00bdf4882570fd5c182e451aa2650b788544aff012a3a",
+ "signature": "96038ad233d8befe342573a6e54828d863471fb2afbad575cc65271a2a649480ea14912b6abbd3fbf92efc1928c036f6e3eef927105af4ec1dd57cb909f360b8"
+ }
+ ],
+ "tweak": "02d4e4f2c4cdb71c9c39a700a9ee1a0fc05b98362a441183f5770af7d6e2b3038c",
+ "shared_secret": "03d9437eb3676cf5cc00feebe68bc44c4567332e4b89788dec9eceb3779054442b",
+ "input_pub_key_sum": "03bc118b1c8178915b716d6137633722c71adfe721551ec7b3938054691de6a2b9"
+ }
+ }
+ ]
+ },
+ {
+ "comment": "Multiple outputs: multiple outputs, same recipient",
+ "sending": [
+ {
+ "given": {
+ "vin": [
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 0,
+ "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac"
+ }
+ },
+ "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1"
+ },
+ {
+ "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d",
+ "vout": 0,
+ "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a9147cdd63cc408564188e8e472640e921c7c90e651d88ac"
+ }
+ },
+ "private_key": "0378e95685b74565fa56751b84a32dfd18545d10d691641b8372e32164fad66a"
+ }
+ ],
+ "recipients": [
+ {
+ "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv",
+ "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4",
+ "spend_pub_key": "025cc9856d6f8375350e123978daac200c260cb5b5ae83106cab90484dcd8fcf36"
+ },
+ {
+ "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv",
+ "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4",
+ "spend_pub_key": "025cc9856d6f8375350e123978daac200c260cb5b5ae83106cab90484dcd8fcf36"
+ }
+ ]
+ },
+ "expected": {
+ "outputs": [
+ [
+ "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca",
+ "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac"
+ ]
+ ],
+ "shared_secrets": [
+ "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413",
+ "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413"
+ ],
+ "input_private_key_sum": "ee55616ce5a93e508f03f21949ecbe70a2a0b107b6e1df5d98b4e4da4adaca1b",
+ "input_pub_keys": [
+ "025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "03782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338"
+ ]
+ }
+ }
+ ],
+ "receiving": [
+ {
+ "given": {
+ "vin": [
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 0,
+ "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac"
+ }
+ }
+ },
+ {
+ "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d",
+ "vout": 0,
+ "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a9147cdd63cc408564188e8e472640e921c7c90e651d88ac"
+ }
+ }
+ }
+ ],
+ "outputs": [
+ "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca",
+ "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac"
+ ],
+ "key_material": {
+ "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3",
+ "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c"
+ },
+ "labels": []
+ },
+ "expected": {
+ "addresses": [
+ "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv"
+ ],
+ "outputs": [
+ {
+ "priv_key_tweak": "d97e442d110c0bdd31161a7bb6e7862e038d02a09b1484dfbb463f2e0f7c9230",
+ "pub_key": "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca",
+ "signature": "29bd25d0f808d7fcd2aa6d5ed206053899198397506c301b218a9e47a3d7070af03e903ff718978d50d1b6b9af8cc0e313d84eda5d5b1e8e85e5516d630bbeb9"
+ },
+ {
+ "priv_key_tweak": "33ce085c3c11eaad13694aae3c20301a6c83382ec89a7cde96c6799e2f88805a",
+ "pub_key": "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac",
+ "signature": "335667ca6cae7a26438f5cfdd73b3d48fa832fa9768521d7d5445f22c203ab0d74ed85088f27d29959ba627a4509996676f47df8ff284d292567b1beef0e3912"
+ }
+ ],
+ "tweak": "0314bec14463d6c0181083d607fecfba67bb83f95915f6f247975ec566d5642ee8",
+ "shared_secret": "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413",
+ "input_pub_key_sum": "03853f51bef283502181e93238c8708ae27235dc51ae45a0c4053987c52fc6428b"
+ }
+ }
+ ]
+ },
+ {
+ "comment": "Multiple outputs: multiple outputs, multiple recipients",
+ "sending": [
+ {
+ "given": {
+ "vin": [
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 0,
+ "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac"
+ }
+ },
+ "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1"
+ },
+ {
+ "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d",
+ "vout": 0,
+ "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a9147cdd63cc408564188e8e472640e921c7c90e651d88ac"
+ }
+ },
+ "private_key": "0378e95685b74565fa56751b84a32dfd18545d10d691641b8372e32164fad66a"
+ }
+ ],
+ "recipients": [
+ {
+ "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv",
+ "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4",
+ "spend_pub_key": "025cc9856d6f8375350e123978daac200c260cb5b5ae83106cab90484dcd8fcf36"
+ },
+ {
+ "address": "sp1qqgrz6j0lcqnc04vxccydl0kpsj4frfje0ktmgcl2t346hkw30226xqupawdf48k8882j0strrvcmgg2kdawz53a54dd376ngdhak364hzcmynqtn",
+ "scan_pub_key": "02062d49ffc02787d586c608dfbec184aa91a6597d97b463ea5c6babd9d17a95a3",
+ "spend_pub_key": "0381eb9a9a9ec739d527c1631b31b421566f5c2a47b4ab5b1f6a686dfb68eab716"
+ },
+ {
+ "address": "sp1qqgrz6j0lcqnc04vxccydl0kpsj4frfje0ktmgcl2t346hkw30226xqupawdf48k8882j0strrvcmgg2kdawz53a54dd376ngdhak364hzcmynqtn",
+ "scan_pub_key": "02062d49ffc02787d586c608dfbec184aa91a6597d97b463ea5c6babd9d17a95a3",
+ "spend_pub_key": "0381eb9a9a9ec739d527c1631b31b421566f5c2a47b4ab5b1f6a686dfb68eab716"
+ }
+ ]
+ },
+ "expected": {
+ "outputs": [
+ [
+ "2e847bb01d1b491da512ddd760b8509617ee38057003d6115d00ba562451323a",
+ "841792c33c9dc6193e76744134125d40add8f2f4a96475f28ba150be032d64e8",
+ "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac"
+ ]
+ ],
+ "shared_secrets": [
+ "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413",
+ "03dd5fd04d3c8863be750a1bd7474df06161461d38d3ce1397a5c78cee112cdcd2",
+ "03dd5fd04d3c8863be750a1bd7474df06161461d38d3ce1397a5c78cee112cdcd2"
+ ],
+ "input_private_key_sum": "ee55616ce5a93e508f03f21949ecbe70a2a0b107b6e1df5d98b4e4da4adaca1b",
+ "input_pub_keys": [
+ "025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "03782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338"
+ ]
+ }
+ }
+ ],
+ "receiving": [
+ {
+ "given": {
+ "vin": [
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 0,
+ "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac"
+ }
+ }
+ },
+ {
+ "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d",
+ "vout": 0,
+ "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a9147cdd63cc408564188e8e472640e921c7c90e651d88ac"
+ }
+ }
+ }
+ ],
+ "outputs": [
+ "2e847bb01d1b491da512ddd760b8509617ee38057003d6115d00ba562451323a",
+ "841792c33c9dc6193e76744134125d40add8f2f4a96475f28ba150be032d64e8",
+ "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac"
+ ],
+ "key_material": {
+ "spend_priv_key": "9902c3c56e84002a7cd410113a9ab21d142be7f53cf5200720bb01314c5eb920",
+ "scan_priv_key": "060b751d7892149006ed7b98606955a29fe284a1e900070c0971f5fb93dbf422"
+ },
+ "labels": []
+ },
+ "expected": {
+ "addresses": [
+ "sp1qqgrz6j0lcqnc04vxccydl0kpsj4frfje0ktmgcl2t346hkw30226xqupawdf48k8882j0strrvcmgg2kdawz53a54dd376ngdhak364hzcmynqtn"
+ ],
+ "outputs": [
+ {
+ "priv_key_tweak": "72cd082cccb633bf85240a83494b32dc943a4d05647a6686d23ad4ca59c0ebe4",
+ "pub_key": "2e847bb01d1b491da512ddd760b8509617ee38057003d6115d00ba562451323a",
+ "signature": "38745f3d9f5eef0b1cfb17ca314efa8c521efab28a23aa20ec5e3abb561d42804d539906dce60c4ee7977966184e6f2cab1faa0e5377ceb7148ec5218b4e7878"
+ },
+ {
+ "priv_key_tweak": "2f17ea873a0047fc01ba8010fef0969e76d0e4283f600d48f735098b1fee6eb9",
+ "pub_key": "841792c33c9dc6193e76744134125d40add8f2f4a96475f28ba150be032d64e8",
+ "signature": "c26f4e3cf371b90b840f48ea0e761b5ec31883ed55719f9ef06a90e282d85f565790ab780a3f491bc2668cc64e944dca849d1022a878cdadb8d168b8da4a6da3"
+ }
+ ],
+ "tweak": "0314bec14463d6c0181083d607fecfba67bb83f95915f6f247975ec566d5642ee8",
+ "shared_secret": "03dd5fd04d3c8863be750a1bd7474df06161461d38d3ce1397a5c78cee112cdcd2",
+ "input_pub_key_sum": "03853f51bef283502181e93238c8708ae27235dc51ae45a0c4053987c52fc6428b"
+ }
+ }
+ ]
+ },
+ {
+ "comment": "Receiving with labels: label with even parity",
+ "sending": [
+ {
+ "given": {
+ "vin": [
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 0,
+ "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac"
+ }
+ },
+ "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1"
+ },
+ {
+ "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d",
+ "vout": 0,
+ "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a9147cdd63cc408564188e8e472640e921c7c90e651d88ac"
+ }
+ },
+ "private_key": "0378e95685b74565fa56751b84a32dfd18545d10d691641b8372e32164fad66a"
+ }
+ ],
+ "recipients": [
+ {
+ "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjex54dmqmmv6rw353tsuqhs99ydvadxzrsy9nuvk74epvee55drs734pqq",
+ "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4",
+ "spend_pub_key": "0259352add837b6686e8d22b87017814a46b3ad308702167c65bd5c8599cd28d1c"
+ }
+ ]
+ },
+ "expected": {
+ "outputs": [
+ [
+ "d014d4860f67d607d60b1af70e0ee236b99658b61bb769832acbbe87c374439a"
+ ]
+ ],
+ "shared_secrets": [
+ "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413"
+ ],
+ "input_private_key_sum": "ee55616ce5a93e508f03f21949ecbe70a2a0b107b6e1df5d98b4e4da4adaca1b",
+ "input_pub_keys": [
+ "025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "03782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338"
+ ]
+ }
+ }
+ ],
+ "receiving": [
+ {
+ "given": {
+ "vin": [
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 0,
+ "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac"
+ }
+ }
+ },
+ {
+ "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d",
+ "vout": 0,
+ "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a9147cdd63cc408564188e8e472640e921c7c90e651d88ac"
+ }
+ }
+ }
+ ],
+ "outputs": [
+ "d014d4860f67d607d60b1af70e0ee236b99658b61bb769832acbbe87c374439a"
+ ],
+ "key_material": {
+ "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3",
+ "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c"
+ },
+ "labels": [
+ 2,
+ 3,
+ 1001337
+ ]
+ },
+ "expected": {
+ "addresses": [
+ "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv",
+ "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjex54dmqmmv6rw353tsuqhs99ydvadxzrsy9nuvk74epvee55drs734pqq",
+ "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqsg59z2rppn4qlkx0yz9sdltmjv3j8zgcqadjn4ug98m3t6plujsq9qvu5n",
+ "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgq7c2zfthc6x3a5yecwc52nxa0kfd20xuz08zyrjpfw4l2j257yq6qgnkdh5"
+ ],
+ "outputs": [
+ {
+ "priv_key_tweak": "51d4e9d0d482b5700109b4b2e16ff508269b03d800192a043d61dca4a0a72a52",
+ "pub_key": "d014d4860f67d607d60b1af70e0ee236b99658b61bb769832acbbe87c374439a",
+ "signature": "c30fa63bad6f0a317f39a773a5cbf0b0f8193c71dfebba05ee6ae4ed28e3775e6e04c3ea70a83703bb888122855dc894cab61692e7fd10c9b3494d479a60785e"
+ }
+ ],
+ "tweak": "0314bec14463d6c0181083d607fecfba67bb83f95915f6f247975ec566d5642ee8",
+ "shared_secret": "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413",
+ "input_pub_key_sum": "03853f51bef283502181e93238c8708ae27235dc51ae45a0c4053987c52fc6428b"
+ }
+ }
+ ]
+ },
+ {
+ "comment": "Receiving with labels: label with odd parity",
+ "sending": [
+ {
+ "given": {
+ "vin": [
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 0,
+ "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac"
+ }
+ },
+ "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1"
+ },
+ {
+ "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d",
+ "vout": 0,
+ "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a9147cdd63cc408564188e8e472640e921c7c90e651d88ac"
+ }
+ },
+ "private_key": "0378e95685b74565fa56751b84a32dfd18545d10d691641b8372e32164fad66a"
+ }
+ ],
+ "recipients": [
+ {
+ "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqsg59z2rppn4qlkx0yz9sdltmjv3j8zgcqadjn4ug98m3t6plujsq9qvu5n",
+ "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4",
+ "spend_pub_key": "0208a144a18433a83f633c822c1bf5ee4c8c8e24601d6ca75e20a7dc57a0ff9280"
+ }
+ ]
+ },
+ "expected": {
+ "outputs": [
+ [
+ "67626aebb3c4307cf0f6c39ca23247598fabf675ab783292eb2f81ae75ad1f8c"
+ ]
+ ],
+ "shared_secrets": [
+ "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413"
+ ],
+ "input_private_key_sum": "ee55616ce5a93e508f03f21949ecbe70a2a0b107b6e1df5d98b4e4da4adaca1b",
+ "input_pub_keys": [
+ "025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "03782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338"
+ ]
+ }
+ }
+ ],
+ "receiving": [
+ {
+ "given": {
+ "vin": [
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 0,
+ "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac"
+ }
+ }
+ },
+ {
+ "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d",
+ "vout": 0,
+ "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a9147cdd63cc408564188e8e472640e921c7c90e651d88ac"
+ }
+ }
+ }
+ ],
+ "outputs": [
+ "67626aebb3c4307cf0f6c39ca23247598fabf675ab783292eb2f81ae75ad1f8c"
+ ],
+ "key_material": {
+ "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3",
+ "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c"
+ },
+ "labels": [
+ 2,
+ 3,
+ 1001337
+ ]
+ },
+ "expected": {
+ "addresses": [
+ "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv",
+ "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjex54dmqmmv6rw353tsuqhs99ydvadxzrsy9nuvk74epvee55drs734pqq",
+ "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqsg59z2rppn4qlkx0yz9sdltmjv3j8zgcqadjn4ug98m3t6plujsq9qvu5n",
+ "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgq7c2zfthc6x3a5yecwc52nxa0kfd20xuz08zyrjpfw4l2j257yq6qgnkdh5"
+ ],
+ "outputs": [
+ {
+ "priv_key_tweak": "6024ae214876356b8d917716e7707d267ae16a0fdb07de2a786b74a7bbcddead",
+ "pub_key": "67626aebb3c4307cf0f6c39ca23247598fabf675ab783292eb2f81ae75ad1f8c",
+ "signature": "a86d554d0d6b7aa0907155f7e0b47f0182752472fffaeddd68da90e99b9402f166fd9b33039c302c7115098d971c1399e67c19e9e4de180b10ea0b9d6f0db832"
+ }
+ ],
+ "tweak": "0314bec14463d6c0181083d607fecfba67bb83f95915f6f247975ec566d5642ee8",
+ "shared_secret": "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413",
+ "input_pub_key_sum": "03853f51bef283502181e93238c8708ae27235dc51ae45a0c4053987c52fc6428b"
+ }
+ }
+ ]
+ },
+ {
+ "comment": "Receiving with labels: large label integer",
+ "sending": [
+ {
+ "given": {
+ "vin": [
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 0,
+ "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac"
+ }
+ },
+ "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1"
+ },
+ {
+ "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d",
+ "vout": 0,
+ "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a9147cdd63cc408564188e8e472640e921c7c90e651d88ac"
+ }
+ },
+ "private_key": "0378e95685b74565fa56751b84a32dfd18545d10d691641b8372e32164fad66a"
+ }
+ ],
+ "recipients": [
+ {
+ "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgq7c2zfthc6x3a5yecwc52nxa0kfd20xuz08zyrjpfw4l2j257yq6qgnkdh5",
+ "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4",
+ "spend_pub_key": "03d85092bbe3468f684ce1d8a2a66ebec96a9e6e09e7110720a5d5faa4aa7880d0"
+ }
+ ]
+ },
+ "expected": {
+ "outputs": [
+ [
+ "7efa60ce78ac343df8a013a2027c6c5ef29f9502edcbd769d2c21717fecc5951"
+ ]
+ ],
+ "shared_secrets": [
+ "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413"
+ ],
+ "input_private_key_sum": "ee55616ce5a93e508f03f21949ecbe70a2a0b107b6e1df5d98b4e4da4adaca1b",
+ "input_pub_keys": [
+ "025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "03782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338"
+ ]
+ }
+ }
+ ],
+ "receiving": [
+ {
+ "given": {
+ "vin": [
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 0,
+ "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac"
+ }
+ }
+ },
+ {
+ "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d",
+ "vout": 0,
+ "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a9147cdd63cc408564188e8e472640e921c7c90e651d88ac"
+ }
+ }
+ }
+ ],
+ "outputs": [
+ "7efa60ce78ac343df8a013a2027c6c5ef29f9502edcbd769d2c21717fecc5951"
+ ],
+ "key_material": {
+ "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3",
+ "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c"
+ },
+ "labels": [
+ 2,
+ 3,
+ 1001337
+ ]
+ },
+ "expected": {
+ "addresses": [
+ "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv",
+ "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjex54dmqmmv6rw353tsuqhs99ydvadxzrsy9nuvk74epvee55drs734pqq",
+ "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqsg59z2rppn4qlkx0yz9sdltmjv3j8zgcqadjn4ug98m3t6plujsq9qvu5n",
+ "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgq7c2zfthc6x3a5yecwc52nxa0kfd20xuz08zyrjpfw4l2j257yq6qgnkdh5"
+ ],
+ "outputs": [
+ {
+ "priv_key_tweak": "e336b92330c33030285ce42e4115ad92d5197913c88e06b9072b4a9b47c664a2",
+ "pub_key": "7efa60ce78ac343df8a013a2027c6c5ef29f9502edcbd769d2c21717fecc5951",
+ "signature": "c9e80dd3bdd25ca2d352ce77510f1aed37ba3509dc8cc0677f2d7c2dd04090707950ce9dd6c83d2a428063063aff5c04f1744e334f661f2fc01b4ef80b50f739"
+ }
+ ],
+ "tweak": "0314bec14463d6c0181083d607fecfba67bb83f95915f6f247975ec566d5642ee8",
+ "shared_secret": "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413",
+ "input_pub_key_sum": "03853f51bef283502181e93238c8708ae27235dc51ae45a0c4053987c52fc6428b"
+ }
+ }
+ ]
+ },
+ {
+ "comment": "Multiple outputs with labels: un-labeled and labeled address; same recipient",
+ "sending": [
+ {
+ "given": {
+ "vin": [
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 0,
+ "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac"
+ }
+ },
+ "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1"
+ },
+ {
+ "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d",
+ "vout": 0,
+ "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a9147cdd63cc408564188e8e472640e921c7c90e651d88ac"
+ }
+ },
+ "private_key": "0378e95685b74565fa56751b84a32dfd18545d10d691641b8372e32164fad66a"
+ }
+ ],
+ "recipients": [
+ {
+ "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqaxww2fnhrx05cghth75n0qcj59e3e2anscr0q9wyknjxtxycg07y3pevyj",
+ "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4",
+ "spend_pub_key": "03a6739499dc667d308baefea4de0c4a85cc72aece181bc05712d3919662610ff1"
+ },
+ {
+ "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv",
+ "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4",
+ "spend_pub_key": "025cc9856d6f8375350e123978daac200c260cb5b5ae83106cab90484dcd8fcf36"
+ }
+ ]
+ },
+ "expected": {
+ "outputs": [
+ [
+ "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c",
+ "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac"
+ ],
+ [
+ "83dc944e61603137294829aed56c74c9b087d80f2c021b98a7fae5799000696c",
+ "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca"
+ ]
+ ],
+ "shared_secrets": [
+ "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413",
+ "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413"
+ ],
+ "input_private_key_sum": "ee55616ce5a93e508f03f21949ecbe70a2a0b107b6e1df5d98b4e4da4adaca1b",
+ "input_pub_keys": [
+ "025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "03782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338"
+ ]
+ }
+ }
+ ],
+ "receiving": [
+ {
+ "given": {
+ "vin": [
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 0,
+ "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac"
+ }
+ }
+ },
+ {
+ "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d",
+ "vout": 0,
+ "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a9147cdd63cc408564188e8e472640e921c7c90e651d88ac"
+ }
+ }
+ }
+ ],
+ "outputs": [
+ "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c",
+ "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac"
+ ],
+ "key_material": {
+ "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3",
+ "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c"
+ },
+ "labels": [
+ 1
+ ]
+ },
+ "expected": {
+ "addresses": [
+ "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv",
+ "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqaxww2fnhrx05cghth75n0qcj59e3e2anscr0q9wyknjxtxycg07y3pevyj"
+ ],
+ "outputs": [
+ {
+ "priv_key_tweak": "43100f89f1a6bf10081c92b473ffc57ceac7dbed600b6aba9bb3976f17dbb914",
+ "pub_key": "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c",
+ "signature": "15c92509b67a6c211ebb4a51b7528d0666e6720de2343b2e92cfb97942ca14693c1f1fdc8451acfdb2644039f8f5c76114807fdc3d3a002d8a46afab6756bd75"
+ },
+ {
+ "priv_key_tweak": "33ce085c3c11eaad13694aae3c20301a6c83382ec89a7cde96c6799e2f88805a",
+ "pub_key": "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac",
+ "signature": "335667ca6cae7a26438f5cfdd73b3d48fa832fa9768521d7d5445f22c203ab0d74ed85088f27d29959ba627a4509996676f47df8ff284d292567b1beef0e3912"
+ }
+ ],
+ "tweak": "0314bec14463d6c0181083d607fecfba67bb83f95915f6f247975ec566d5642ee8",
+ "shared_secret": "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413",
+ "input_pub_key_sum": "03853f51bef283502181e93238c8708ae27235dc51ae45a0c4053987c52fc6428b"
+ }
+ }
+ ]
+ },
+ {
+ "comment": "Multiple outputs with labels: multiple outputs for labeled address; same recipient",
+ "sending": [
+ {
+ "given": {
+ "vin": [
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 0,
+ "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac"
+ }
+ },
+ "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1"
+ },
+ {
+ "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d",
+ "vout": 0,
+ "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a9147cdd63cc408564188e8e472640e921c7c90e651d88ac"
+ }
+ },
+ "private_key": "0378e95685b74565fa56751b84a32dfd18545d10d691641b8372e32164fad66a"
+ }
+ ],
+ "recipients": [
+ {
+ "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqaxww2fnhrx05cghth75n0qcj59e3e2anscr0q9wyknjxtxycg07y3pevyj",
+ "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4",
+ "spend_pub_key": "03a6739499dc667d308baefea4de0c4a85cc72aece181bc05712d3919662610ff1"
+ },
+ {
+ "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqaxww2fnhrx05cghth75n0qcj59e3e2anscr0q9wyknjxtxycg07y3pevyj",
+ "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4",
+ "spend_pub_key": "03a6739499dc667d308baefea4de0c4a85cc72aece181bc05712d3919662610ff1"
+ }
+ ]
+ },
+ "expected": {
+ "outputs": [
+ [
+ "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c",
+ "83dc944e61603137294829aed56c74c9b087d80f2c021b98a7fae5799000696c"
+ ]
+ ],
+ "shared_secrets": [
+ "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413",
+ "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413"
+ ],
+ "input_private_key_sum": "ee55616ce5a93e508f03f21949ecbe70a2a0b107b6e1df5d98b4e4da4adaca1b",
+ "input_pub_keys": [
+ "025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "03782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338"
+ ]
+ }
+ }
+ ],
+ "receiving": [
+ {
+ "given": {
+ "vin": [
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 0,
+ "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac"
+ }
+ }
+ },
+ {
+ "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d",
+ "vout": 0,
+ "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a9147cdd63cc408564188e8e472640e921c7c90e651d88ac"
+ }
+ }
+ }
+ ],
+ "outputs": [
+ "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c",
+ "83dc944e61603137294829aed56c74c9b087d80f2c021b98a7fae5799000696c"
+ ],
+ "key_material": {
+ "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3",
+ "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c"
+ },
+ "labels": [
+ 1
+ ]
+ },
+ "expected": {
+ "addresses": [
+ "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv",
+ "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqaxww2fnhrx05cghth75n0qcj59e3e2anscr0q9wyknjxtxycg07y3pevyj"
+ ],
+ "outputs": [
+ {
+ "priv_key_tweak": "43100f89f1a6bf10081c92b473ffc57ceac7dbed600b6aba9bb3976f17dbb914",
+ "pub_key": "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c",
+ "signature": "15c92509b67a6c211ebb4a51b7528d0666e6720de2343b2e92cfb97942ca14693c1f1fdc8451acfdb2644039f8f5c76114807fdc3d3a002d8a46afab6756bd75"
+ },
+ {
+ "priv_key_tweak": "9d5fd3b91cac9ddfea6fc2e6f9386f680e6cee623cda02f53706306c081de87f",
+ "pub_key": "83dc944e61603137294829aed56c74c9b087d80f2c021b98a7fae5799000696c",
+ "signature": "db0dfacc98b6a6fcc67cc4631f080b1ca38c60d8c397f2f19843f8f95ec91594b24e47c5bd39480a861c1209f7e3145c440371f9191fb96e324690101eac8e8e"
+ }
+ ],
+ "tweak": "0314bec14463d6c0181083d607fecfba67bb83f95915f6f247975ec566d5642ee8",
+ "shared_secret": "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413",
+ "input_pub_key_sum": "03853f51bef283502181e93238c8708ae27235dc51ae45a0c4053987c52fc6428b"
+ }
+ }
+ ]
+ },
+ {
+ "comment": "Multiple outputs with labels: un-labeled, labeled, and multiple outputs for labeled address; same recipients",
+ "sending": [
+ {
+ "given": {
+ "vin": [
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 0,
+ "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac"
+ }
+ },
+ "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1"
+ },
+ {
+ "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d",
+ "vout": 0,
+ "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a9147cdd63cc408564188e8e472640e921c7c90e651d88ac"
+ }
+ },
+ "private_key": "0378e95685b74565fa56751b84a32dfd18545d10d691641b8372e32164fad66a"
+ }
+ ],
+ "recipients": [
+ {
+ "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv",
+ "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4",
+ "spend_pub_key": "025cc9856d6f8375350e123978daac200c260cb5b5ae83106cab90484dcd8fcf36"
+ },
+ {
+ "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqaxww2fnhrx05cghth75n0qcj59e3e2anscr0q9wyknjxtxycg07y3pevyj",
+ "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4",
+ "spend_pub_key": "03a6739499dc667d308baefea4de0c4a85cc72aece181bc05712d3919662610ff1"
+ },
+ {
+ "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjyh2ju7hd5gj57jg5r9lev3pckk4n2shtzaq34467erzzdfajfggty6aa5",
+ "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4",
+ "spend_pub_key": "0244baa5cf5db444a9e922832ff2c88716b566a85d62e8235aebd91884d4f64942"
+ },
+ {
+ "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjyh2ju7hd5gj57jg5r9lev3pckk4n2shtzaq34467erzzdfajfggty6aa5",
+ "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4",
+ "spend_pub_key": "0244baa5cf5db444a9e922832ff2c88716b566a85d62e8235aebd91884d4f64942"
+ }
+ ]
+ },
+ "expected": {
+ "outputs": [
+ [
+ "006a02c308ccdbf3ac49f0638f6de128f875db5a213095cf112b3b77722472ae",
+ "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c",
+ "ae1a780c04237bd577283c3ddb2e499767c3214160d5a6b0767e6b8c278bd701",
+ "ca64abe1e0f737823fb9a94f597eed418fb2df77b1317e26b881a14bb594faaa"
+ ],
+ [
+ "006a02c308ccdbf3ac49f0638f6de128f875db5a213095cf112b3b77722472ae",
+ "3edf1ff6657c6e69568811bd726a7a7f480493aa42161acfe8dd4f44521f99ed",
+ "7ee1543ed5d123ffa66fbebc128c020173eb490d5fa2ba306e0c9573a77db8f3",
+ "ca64abe1e0f737823fb9a94f597eed418fb2df77b1317e26b881a14bb594faaa"
+ ],
+ [
+ "006a02c308ccdbf3ac49f0638f6de128f875db5a213095cf112b3b77722472ae",
+ "7ee1543ed5d123ffa66fbebc128c020173eb490d5fa2ba306e0c9573a77db8f3",
+ "83dc944e61603137294829aed56c74c9b087d80f2c021b98a7fae5799000696c",
+ "ae1a780c04237bd577283c3ddb2e499767c3214160d5a6b0767e6b8c278bd701"
+ ],
+ [
+ "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c",
+ "3c54444944d176437644378c23efb999ab6ab1cacdfe1dc1537b607e3df330e2",
+ "ca64abe1e0f737823fb9a94f597eed418fb2df77b1317e26b881a14bb594faaa",
+ "f4569fc5f69c10f0082cfbb8e072e6266ec55f69fba8cffca4cbb4c144b7e59b"
+ ],
+ [
+ "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c",
+ "ae1a780c04237bd577283c3ddb2e499767c3214160d5a6b0767e6b8c278bd701",
+ "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac",
+ "f4569fc5f69c10f0082cfbb8e072e6266ec55f69fba8cffca4cbb4c144b7e59b"
+ ],
+ [
+ "3c54444944d176437644378c23efb999ab6ab1cacdfe1dc1537b607e3df330e2",
+ "602e10e6944107c9b48bd885b493676578c935723287e0ab2f8b7f136862568e",
+ "7ee1543ed5d123ffa66fbebc128c020173eb490d5fa2ba306e0c9573a77db8f3",
+ "ca64abe1e0f737823fb9a94f597eed418fb2df77b1317e26b881a14bb594faaa"
+ ],
+ [
+ "3c54444944d176437644378c23efb999ab6ab1cacdfe1dc1537b607e3df330e2",
+ "7ee1543ed5d123ffa66fbebc128c020173eb490d5fa2ba306e0c9573a77db8f3",
+ "83dc944e61603137294829aed56c74c9b087d80f2c021b98a7fae5799000696c",
+ "f4569fc5f69c10f0082cfbb8e072e6266ec55f69fba8cffca4cbb4c144b7e59b"
+ ],
+ [
+ "3edf1ff6657c6e69568811bd726a7a7f480493aa42161acfe8dd4f44521f99ed",
+ "7ee1543ed5d123ffa66fbebc128c020173eb490d5fa2ba306e0c9573a77db8f3",
+ "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac",
+ "f4569fc5f69c10f0082cfbb8e072e6266ec55f69fba8cffca4cbb4c144b7e59b"
+ ],
+ [
+ "3edf1ff6657c6e69568811bd726a7a7f480493aa42161acfe8dd4f44521f99ed",
+ "ca64abe1e0f737823fb9a94f597eed418fb2df77b1317e26b881a14bb594faaa",
+ "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca",
+ "f4569fc5f69c10f0082cfbb8e072e6266ec55f69fba8cffca4cbb4c144b7e59b"
+ ],
+ [
+ "602e10e6944107c9b48bd885b493676578c935723287e0ab2f8b7f136862568e",
+ "7ee1543ed5d123ffa66fbebc128c020173eb490d5fa2ba306e0c9573a77db8f3",
+ "ae1a780c04237bd577283c3ddb2e499767c3214160d5a6b0767e6b8c278bd701",
+ "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac"
+ ],
+ [
+ "602e10e6944107c9b48bd885b493676578c935723287e0ab2f8b7f136862568e",
+ "ae1a780c04237bd577283c3ddb2e499767c3214160d5a6b0767e6b8c278bd701",
+ "ca64abe1e0f737823fb9a94f597eed418fb2df77b1317e26b881a14bb594faaa",
+ "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca"
+ ],
+ [
+ "83dc944e61603137294829aed56c74c9b087d80f2c021b98a7fae5799000696c",
+ "ae1a780c04237bd577283c3ddb2e499767c3214160d5a6b0767e6b8c278bd701",
+ "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca",
+ "f4569fc5f69c10f0082cfbb8e072e6266ec55f69fba8cffca4cbb4c144b7e59b"
+ ]
+ ],
+ "shared_secrets": [
+ "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413",
+ "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413",
+ "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413",
+ "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413"
+ ],
+ "input_private_key_sum": "ee55616ce5a93e508f03f21949ecbe70a2a0b107b6e1df5d98b4e4da4adaca1b",
+ "input_pub_keys": [
+ "025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "03782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338"
+ ]
+ }
+ }
+ ],
+ "receiving": [
+ {
+ "given": {
+ "vin": [
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 0,
+ "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac"
+ }
+ }
+ },
+ {
+ "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d",
+ "vout": 0,
+ "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a9147cdd63cc408564188e8e472640e921c7c90e651d88ac"
+ }
+ }
+ }
+ ],
+ "outputs": [
+ "006a02c308ccdbf3ac49f0638f6de128f875db5a213095cf112b3b77722472ae",
+ "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c",
+ "ae1a780c04237bd577283c3ddb2e499767c3214160d5a6b0767e6b8c278bd701",
+ "ca64abe1e0f737823fb9a94f597eed418fb2df77b1317e26b881a14bb594faaa"
+ ],
+ "key_material": {
+ "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3",
+ "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c"
+ },
+ "labels": [
+ 1,
+ 1337
+ ]
+ },
+ "expected": {
+ "addresses": [
+ "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv",
+ "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqaxww2fnhrx05cghth75n0qcj59e3e2anscr0q9wyknjxtxycg07y3pevyj",
+ "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjyh2ju7hd5gj57jg5r9lev3pckk4n2shtzaq34467erzzdfajfggty6aa5"
+ ],
+ "outputs": [
+ {
+ "priv_key_tweak": "4e3352fbe0505c25e718d96007c259ef08db34f8c844e4ff742d9855ff03805a",
+ "pub_key": "006a02c308ccdbf3ac49f0638f6de128f875db5a213095cf112b3b77722472ae",
+ "signature": "6eeae1ea9eb826e3d0e812f65937100e0836ea188c04f36fabc4981eda29de8d3d3529390a0a8b3d830f7bca4f5eae5994b9788ddaf05ad259ffe26d86144b4b"
+ },
+ {
+ "priv_key_tweak": "43100f89f1a6bf10081c92b473ffc57ceac7dbed600b6aba9bb3976f17dbb914",
+ "pub_key": "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c",
+ "signature": "15c92509b67a6c211ebb4a51b7528d0666e6720de2343b2e92cfb97942ca14693c1f1fdc8451acfdb2644039f8f5c76114807fdc3d3a002d8a46afab6756bd75"
+ },
+ {
+ "priv_key_tweak": "bf709f98d4418f8a67e738154ae48818dad44689cd37fbc070891a396dd1c633",
+ "pub_key": "ae1a780c04237bd577283c3ddb2e499767c3214160d5a6b0767e6b8c278bd701",
+ "signature": "42a19fd8a63dde1824966a95d65a28203e631e49bf96ca5dae1b390e7a0ace2cc8709c9b0c5715047032f57f536a3c80273cbecf4c05be0b5456c183fa122c06"
+ },
+ {
+ "priv_key_tweak": "736f05e4e3072c3b8656bedef2e9bf54cbcaa2b6fe5320d3e86f5b96874dda71",
+ "pub_key": "ca64abe1e0f737823fb9a94f597eed418fb2df77b1317e26b881a14bb594faaa",
+ "signature": "2e61bb3d79418ecf55f68847cf121bfc12d397b39d1da8643246b2f0a9b96c3daa4bfe9651beb5c9ce20e1f29282c4566400a4b45ee6657ec3b18fdc554da0b4"
+ }
+ ],
+ "tweak": "0314bec14463d6c0181083d607fecfba67bb83f95915f6f247975ec566d5642ee8",
+ "shared_secret": "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413",
+ "input_pub_key_sum": "03853f51bef283502181e93238c8708ae27235dc51ae45a0c4053987c52fc6428b"
+ }
+ }
+ ]
+ },
+ {
+ "comment": "Single recipient: use silent payments for sender change",
+ "sending": [
+ {
+ "given": {
+ "vin": [
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 0,
+ "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac"
+ }
+ },
+ "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1"
+ },
+ {
+ "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d",
+ "vout": 0,
+ "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a9147cdd63cc408564188e8e472640e921c7c90e651d88ac"
+ }
+ },
+ "private_key": "0378e95685b74565fa56751b84a32dfd18545d10d691641b8372e32164fad66a"
+ }
+ ],
+ "recipients": [
+ {
+ "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv",
+ "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4",
+ "spend_pub_key": "025cc9856d6f8375350e123978daac200c260cb5b5ae83106cab90484dcd8fcf36"
+ },
+ {
+ "address": "sp1qqw6vczcfpdh5nf5y2ky99kmqae0tr30hgdfg88parz50cp80wd2wqqlv6saelkk5snl4wfutyxrchpzzwm8rjp3z6q7apna59z9huq4x754e5atr",
+ "scan_pub_key": "03b4cc0b090b6f49a684558852db60ee5eb1c5f74352839c3d18a8fc04ef7354e0",
+ "spend_pub_key": "03ecd43b9fdad484ff57278b21878b844276ce390622d03dd0cfb4288b7e02a6f5"
+ }
+ ]
+ },
+ "expected": {
+ "outputs": [
+ [
+ "be368e28979d950245d742891ae6064020ba548c1e2e65a639a8bb0675d95cff",
+ "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac"
+ ]
+ ],
+ "shared_secrets": [
+ "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413",
+ "037d12c02c3aed482658a28b8d1be030dac1daf995551491d74c00543af98572fb"
+ ],
+ "input_private_key_sum": "ee55616ce5a93e508f03f21949ecbe70a2a0b107b6e1df5d98b4e4da4adaca1b",
+ "input_pub_keys": [
+ "025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "03782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338"
+ ]
+ }
+ }
+ ],
+ "receiving": [
+ {
+ "given": {
+ "vin": [
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 0,
+ "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac"
+ }
+ }
+ },
+ {
+ "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d",
+ "vout": 0,
+ "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a9147cdd63cc408564188e8e472640e921c7c90e651d88ac"
+ }
+ }
+ }
+ ],
+ "outputs": [
+ "be368e28979d950245d742891ae6064020ba548c1e2e65a639a8bb0675d95cff",
+ "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac"
+ ],
+ "key_material": {
+ "spend_priv_key": "b8f87388cbb41934c50daca018901b00070a5ff6cc25a7e9e716a9d5b9e4d664",
+ "scan_priv_key": "11b7a82e06ca2648d5fded2366478078ec4fc9dc1d8ff487518226f229d768fd"
+ },
+ "labels": [
+ 0
+ ]
+ },
+ "expected": {
+ "addresses": [
+ "sp1qqw6vczcfpdh5nf5y2ky99kmqae0tr30hgdfg88parz50cp80wd2wqqauj52ymtc4xdkmx3tgyhrsemg2g3303xk2gtzfy8h8ejet8fz8jcw23zua",
+ "sp1qqw6vczcfpdh5nf5y2ky99kmqae0tr30hgdfg88parz50cp80wd2wqqlv6saelkk5snl4wfutyxrchpzzwm8rjp3z6q7apna59z9huq4x754e5atr"
+ ],
+ "outputs": [
+ {
+ "priv_key_tweak": "80cd767ed20bd0bb7d8ea5e803f8c381293a62e8a073cf46fb0081da46e64e1f",
+ "pub_key": "be368e28979d950245d742891ae6064020ba548c1e2e65a639a8bb0675d95cff",
+ "signature": "7fbd5074cf1377273155eefafc7c330cb61b31da252f22206ac27530d2b2567040d9af7808342ed4a09598c26d8307446e4ed77079e6a2e61fea736e44da5f5a"
+ }
+ ],
+ "tweak": "0314bec14463d6c0181083d607fecfba67bb83f95915f6f247975ec566d5642ee8",
+ "shared_secret": "037d12c02c3aed482658a28b8d1be030dac1daf995551491d74c00543af98572fb",
+ "input_pub_key_sum": "03853f51bef283502181e93238c8708ae27235dc51ae45a0c4053987c52fc6428b"
+ }
+ },
+ {
+ "given": {
+ "vin": [
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 0,
+ "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac"
+ }
+ }
+ },
+ {
+ "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d",
+ "vout": 0,
+ "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a9147cdd63cc408564188e8e472640e921c7c90e651d88ac"
+ }
+ }
+ }
+ ],
+ "outputs": [
+ "be368e28979d950245d742891ae6064020ba548c1e2e65a639a8bb0675d95cff",
+ "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac"
+ ],
+ "key_material": {
+ "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3",
+ "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c"
+ },
+ "labels": []
+ },
+ "expected": {
+ "addresses": [
+ "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv"
+ ],
+ "outputs": [
+ {
+ "priv_key_tweak": "33ce085c3c11eaad13694aae3c20301a6c83382ec89a7cde96c6799e2f88805a",
+ "pub_key": "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac",
+ "signature": "335667ca6cae7a26438f5cfdd73b3d48fa832fa9768521d7d5445f22c203ab0d74ed85088f27d29959ba627a4509996676f47df8ff284d292567b1beef0e3912"
+ }
+ ],
+ "tweak": "0314bec14463d6c0181083d607fecfba67bb83f95915f6f247975ec566d5642ee8",
+ "shared_secret": "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413",
+ "input_pub_key_sum": "03853f51bef283502181e93238c8708ae27235dc51ae45a0c4053987c52fc6428b"
+ }
+ }
+ ]
+ },
+ {
+ "comment": "Single recipient: taproot input with NUMS point",
+ "sending": [
+ {
+ "given": {
+ "vin": [
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 0,
+ "scriptSig": "",
+ "txinwitness": "0440c459b671370d12cfb5acee76da7e3ba7cc29b0b4653e3af8388591082660137d087fdc8e89a612cd5d15be0febe61fc7cdcf3161a26e599a4514aa5c3e86f47b22205a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5ac21c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac00150",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "5120da6f0595ecb302bbe73e2f221f05ab10f336b06817d36fd28fc6691725ddaa85"
+ }
+ },
+ "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1"
+ },
+ {
+ "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d",
+ "vout": 0,
+ "scriptSig": "",
+ "txinwitness": "0140bd1e708f92dbeaf24a6b8dd22e59c6274355424d62baea976b449e220fd75b13578e262ab11b7aa58e037f0c6b0519b66803b7d9decaa1906dedebfb531c56c1",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "5120782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338"
+ }
+ },
+ "private_key": "fc8716a97a48ba9a05a98ae47b5cd201a25a7fd5d8b73c203c5f7b6b6b3b6ad7"
+ },
+ {
+ "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d",
+ "vout": 1,
+ "scriptSig": "",
+ "txinwitness": "0340268d31a9276f6380107d5321cafa6d9e8e5ea39204318fdc8206b31507c891c3bbcea3c99e2208d73bd127a8e8c5f1e45a54f1bd217205414ddb566ab7eda0092220e0ec4f64b3fa2e463ccfcf4e856e37d5e1e20275bc89ec1def9eb098eff1f85dac21c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "51200a3c9365ceb131f89b0a4feb6896ebd67bb15a98c31eaa3da143bb955a0f3fcb"
+ }
+ },
+ "private_key": "8d4751f6e8a3586880fb66c19ae277969bd5aa06f61c4ee2f1e2486efdf666d3"
+ }
+ ],
+ "recipients": [
+ {
+ "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv",
+ "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4",
+ "spend_pub_key": "025cc9856d6f8375350e123978daac200c260cb5b5ae83106cab90484dcd8fcf36"
+ }
+ ]
+ },
+ "expected": {
+ "outputs": [
+ [
+ "79e79897c52935bfd97fc6e076a6431a0c7543ca8c31e0fc3cf719bb572c842d"
+ ]
+ ],
+ "shared_secrets": [
+ "036f040608cd1e5ee79c54e78bea85904c895591f547beae080d0c5f6946c2730d"
+ ],
+ "input_private_key_sum": "fc8716a97a48ba9a05a98ae47b5cd201a25a7fd5d8b73c203c5f7b6b6b3b6ad7",
+ "input_pub_keys": [
+ "02782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338"
+ ]
+ }
+ }
+ ],
+ "receiving": [
+ {
+ "given": {
+ "vin": [
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 0,
+ "scriptSig": "",
+ "txinwitness": "0440c459b671370d12cfb5acee76da7e3ba7cc29b0b4653e3af8388591082660137d087fdc8e89a612cd5d15be0febe61fc7cdcf3161a26e599a4514aa5c3e86f47b22205a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5ac21c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac00150",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "5120da6f0595ecb302bbe73e2f221f05ab10f336b06817d36fd28fc6691725ddaa85"
+ }
+ }
+ },
+ {
+ "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d",
+ "vout": 0,
+ "scriptSig": "",
+ "txinwitness": "0140bd1e708f92dbeaf24a6b8dd22e59c6274355424d62baea976b449e220fd75b13578e262ab11b7aa58e037f0c6b0519b66803b7d9decaa1906dedebfb531c56c1",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "5120782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338"
+ }
+ }
+ },
+ {
+ "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d",
+ "vout": 1,
+ "scriptSig": "",
+ "txinwitness": "0340268d31a9276f6380107d5321cafa6d9e8e5ea39204318fdc8206b31507c891c3bbcea3c99e2208d73bd127a8e8c5f1e45a54f1bd217205414ddb566ab7eda0092220e0ec4f64b3fa2e463ccfcf4e856e37d5e1e20275bc89ec1def9eb098eff1f85dac21c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "51200a3c9365ceb131f89b0a4feb6896ebd67bb15a98c31eaa3da143bb955a0f3fcb"
+ }
+ }
+ }
+ ],
+ "outputs": [
+ "79e79897c52935bfd97fc6e076a6431a0c7543ca8c31e0fc3cf719bb572c842d"
+ ],
+ "key_material": {
+ "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3",
+ "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c"
+ },
+ "labels": []
+ },
+ "expected": {
+ "addresses": [
+ "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv"
+ ],
+ "outputs": [
+ {
+ "priv_key_tweak": "3ddec3232609d348d6b8b53123b4f40f6d4f5398ca586f087b0416ec3b851496",
+ "pub_key": "79e79897c52935bfd97fc6e076a6431a0c7543ca8c31e0fc3cf719bb572c842d",
+ "signature": "d7d06e3afb68363031e4eb18035c46ceae41bdbebe7888a4754bc9848c596436869aeaecff0527649a1f458b71c9ceecec10b535c09d01d720229aa228547706"
+ }
+ ],
+ "tweak": "02213b872c9a6ee28a0d861384a1b3e3ec7257f4855ed09b4323e3899f3b028989",
+ "shared_secret": "036f040608cd1e5ee79c54e78bea85904c895591f547beae080d0c5f6946c2730d",
+ "input_pub_key_sum": "02782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338"
+ }
+ }
+ ]
+ },
+ {
+ "comment": "Pubkey extraction from malleated p2pkh",
+ "sending": [
+ {
+ "given": {
+ "vin": [
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 0,
+ "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac"
+ }
+ },
+ "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1"
+ },
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 1,
+ "scriptSig": "0075473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a9147cdd63cc408564188e8e472640e921c7c90e651d88ac"
+ }
+ },
+ "private_key": "0378e95685b74565fa56751b84a32dfd18545d10d691641b8372e32164fad66a"
+ },
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 2,
+ "scriptSig": "5163473045022100e7d26e77290b37128f5215ade25b9b908ce87cc9a4d498908b5bb8fd6daa1b8d022002568c3a8226f4f0436510283052bfb780b76f3fe4aa60c4c5eb118e43b187372102e0ec4f64b3fa2e463ccfcf4e856e37d5e1e20275bc89ec1def9eb098eff1f85d67483046022100c0d3c851d3bd562ae93d56bcefd735ea57c027af46145a4d5e9cac113bfeb0c2022100ee5b2239af199fa9b7aa1d98da83a29d0a2cf1e4f29e2f37134ce386d51c544c2102ad0f26ddc7b3fcc340155963b3051b85289c1869612ecb290184ac952e2864ec68",
+ "txinwitness": "",
+ "prevout": {
+ "scriptPubKey": {
+ "hex": "76a914c82c5ec473cbc6c86e5ef410e36f9495adcf979988ac"
+ }
+ },
+ "private_key": "72b8ae09175ca7977f04993e651d88681ed932dfb92c5158cdf0161dd23fda6e"
+ }
+ ],
+ "recipients": [
+ {
+ "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv",
+ "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4",
+ "spend_pub_key": "025cc9856d6f8375350e123978daac200c260cb5b5ae83106cab90484dcd8fcf36"
+ }
+ ]
+ },
+ "expected": {
+ "outputs": [
+ [
+ "4612cdbf845c66c7511d70aab4d9aed11e49e48cdb8d799d787101cdd0d53e4f"
+ ]
+ ],
+ "shared_secrets": [
+ "034773b97ccad9791cb4213964ff9896ccd6581ee69345de5d114786d9d86b03a2"
+ ],
+ "input_private_key_sum": "610e0f75fd05e5e80e088b57af0a46da06cb0700c0c5907aa6d29c6b4ce46348",
+ "input_pub_keys": [
+ "025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5",
+ "03782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338",
+ "02e0ec4f64b3fa2e463ccfcf4e856e37d5e1e20275bc89ec1def9eb098eff1f85d"
+ ]
+ }
+ }
+ ],
+ "receiving": [
+ {
+ "given": {
+ "vin": [
+ {
+ "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16",
+ "vout": 0,
+ Why this scored 28/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.