0
0
Fork 0
mirror of https://github.com/bitcoin/bitcoin.git synced 2025-03-07 14:25:09 -05:00
This commit is contained in:
Fabian Jahr 2025-01-31 21:50:19 +01:00 committed by GitHub
commit a34605f378
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
48 changed files with 2764 additions and 90 deletions

View file

@ -39,6 +39,8 @@ set(SECP256K1_DISABLE_SHARED ON CACHE BOOL "" FORCE)
set(SECP256K1_ENABLE_MODULE_ECDH OFF CACHE BOOL "" FORCE)
set(SECP256K1_ENABLE_MODULE_RECOVERY ON CACHE BOOL "" FORCE)
set(SECP256K1_ENABLE_MODULE_MUSIG OFF CACHE BOOL "" FORCE)
set(SECP256K1_EXPERIMENTAL ON CACHE BOOL "" FORCE)
set(SECP256K1_ENABLE_MODULE_BATCH ON CACHE BOOL "" FORCE)
set(SECP256K1_BUILD_BENCHMARK OFF CACHE BOOL "" FORCE)
set(SECP256K1_BUILD_TESTS ${BUILD_TESTS} CACHE BOOL "" FORCE)
set(SECP256K1_BUILD_EXHAUSTIVE_TESTS ${BUILD_TESTS} CACHE BOOL "" FORCE)
@ -108,6 +110,7 @@ endif()
add_library(bitcoin_common STATIC EXCLUDE_FROM_ALL
addresstype.cpp
base58.cpp
batchverify.cpp
bech32.cpp
chain.cpp
chainparams.cpp

46
src/batchverify.cpp Normal file
View file

@ -0,0 +1,46 @@
// Copyright (c) 2024 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <batchverify.h>
#include <logging.h>
#include <pubkey.h>
#include <random.h>
#include <sync.h>
#include <secp256k1.h>
#include <secp256k1_batch.h>
#include <secp256k1_schnorrsig_batch.h>
BatchSchnorrVerifier::BatchSchnorrVerifier() {
unsigned char rnd[16];
GetRandBytes(rnd);
// This is the maximum number of scalar-point pairs on the batch for which
// Strauss' algorithm, which is used in the secp256k1 implementation, is
// still efficient.
const size_t max_batch_size{106};
secp256k1_batch* batch{secp256k1_batch_create(secp256k1_context_static, max_batch_size, rnd)};
m_batch = batch;
}
BatchSchnorrVerifier::~BatchSchnorrVerifier() {
(void)secp256k1_batch_destroy(secp256k1_context_static, m_batch);
}
bool BatchSchnorrVerifier::Add(const Span<const unsigned char> sig, const XOnlyPubKey& pubkey, const uint256& sighash) {
LOCK(m_batch_mutex);
if (secp256k1_batch_usable(secp256k1_context_static, m_batch) == 0) {
LogPrintf("ERROR: BatchSchnorrVerifier m_batch unusable\n");
return false;
}
secp256k1_xonly_pubkey pubkey_parsed;
(void)secp256k1_xonly_pubkey_parse(secp256k1_context_static, &pubkey_parsed, pubkey.data());
return secp256k1_batch_add_schnorrsig(secp256k1_context_static, m_batch, sig.data(), sighash.begin(), 32, &pubkey_parsed);
}
bool BatchSchnorrVerifier::Verify() {
LOCK(m_batch_mutex);
return secp256k1_batch_verify(secp256k1_context_static, m_batch);
}

27
src/batchverify.h Normal file
View file

@ -0,0 +1,27 @@
// Copyright (c) 2024 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_BATCHVERIFY_H
#define BITCOIN_BATCHVERIFY_H
#include <pubkey.h>
#include <sync.h>
#include <secp256k1/include/secp256k1_batch.h>
#include <secp256k1/include/secp256k1_schnorrsig.h>
class BatchSchnorrVerifier {
private:
secp256k1_batch* m_batch GUARDED_BY(m_batch_mutex);
mutable Mutex m_batch_mutex;
public:
BatchSchnorrVerifier();
~BatchSchnorrVerifier();
bool Add(const Span<const unsigned char> sig, const XOnlyPubKey& pubkey, const uint256& sighash) EXCLUSIVE_LOCKS_REQUIRED(!m_batch_mutex);
bool Verify() EXCLUSIVE_LOCKS_REQUIRED(!m_batch_mutex);
};
#endif // BITCOIN_BATCHVERIFY_H

View file

@ -5,6 +5,7 @@
#include <script/sigcache.h>
#include <batchverify.h>
#include <crypto/sha256.h>
#include <logging.h>
#include <pubkey.h>
@ -82,3 +83,13 @@ bool CachingTransactionSignatureChecker::VerifySchnorrSignature(Span<const unsig
if (store) m_signature_cache.Set(entry);
return true;
}
bool BatchingCachingTransactionSignatureChecker::VerifySchnorrSignature(Span<const unsigned char> sig, const XOnlyPubKey& pubkey, const uint256& sighash) const
{
uint256 entry;
SignatureCache& signature_cache{GetSigCache()};
signature_cache.ComputeEntrySchnorr(entry, sighash, sig, pubkey);
if (signature_cache.Get(entry, !GetStore())) return true;
return m_batch->Add(sig, pubkey, sighash);
}

View file

@ -18,6 +18,7 @@
#include <shared_mutex>
#include <vector>
class BatchSchnorrVerifier;
class CPubKey;
class CTransaction;
class XOnlyPubKey;
@ -67,10 +68,25 @@ private:
SignatureCache& m_signature_cache;
public:
CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, bool storeIn, SignatureCache& signature_cache, PrecomputedTransactionData& txdataIn) : TransactionSignatureChecker(txToIn, nInIn, amountIn, txdataIn, MissingDataBehavior::ASSERT_FAIL), store(storeIn), m_signature_cache(signature_cache) {}
bool GetStore() const { return store; }
SignatureCache& GetSigCache() const { return m_signature_cache; }
CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, bool storeIn, SignatureCache& signature_cache, PrecomputedTransactionData& txdataIn) : TransactionSignatureChecker(txToIn, nInIn, amountIn, txdataIn, MissingDataBehavior::ASSERT_FAIL), store(storeIn), m_signature_cache(signature_cache) {}
bool VerifyECDSASignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const override;
bool VerifySchnorrSignature(Span<const unsigned char> sig, const XOnlyPubKey& pubkey, const uint256& sighash) const override;
};
[[nodiscard]] bool InitSignatureCache(size_t max_size_bytes);
class BatchingCachingTransactionSignatureChecker : public CachingTransactionSignatureChecker
{
private:
BatchSchnorrVerifier* m_batch;
public:
BatchingCachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, bool storeIn, SignatureCache& signature_cache, PrecomputedTransactionData& txdataIn, BatchSchnorrVerifier* batchIn) : CachingTransactionSignatureChecker(txToIn, nInIn, amountIn, storeIn, signature_cache, txdataIn), m_batch(batchIn) {}
bool VerifySchnorrSignature(Span<const unsigned char> sig, const XOnlyPubKey& pubkey, const uint256& sighash) const override;
};
#endif // BITCOIN_SCRIPT_SIGCACHE_H

View file

@ -12,6 +12,7 @@ ecdsa_example
schnorr_example
ellswift_example
musig_example
batch_example
*.exe
*.so
*.a

View file

@ -5,6 +5,8 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [0.6.0] - 2024-11-04
#### Added
@ -162,6 +164,7 @@ This version was in fact never released.
The number was given by the build system since the introduction of autotools in Jan 2014 (ea0fe5a5bf0c04f9cc955b2966b614f5f378c6f6).
Therefore, this version number does not uniquely identify a set of source files.
[unreleased]: https://github.com/bitcoin-core/secp256k1/compare/v0.6.0...HEAD
[0.6.0]: https://github.com/bitcoin-core/secp256k1/compare/v0.5.1...v0.6.0
[0.5.1]: https://github.com/bitcoin-core/secp256k1/compare/v0.5.0...v0.5.1
[0.5.0]: https://github.com/bitcoin-core/secp256k1/compare/v0.4.1...v0.5.0

View file

@ -7,7 +7,7 @@ project(libsecp256k1
# The package (a.k.a. release) version is based on semantic versioning 2.0.0 of
# the API. All changes in experimental modules are treated as
# backwards-compatible and therefore at most increase the minor version.
VERSION 0.6.0
VERSION 0.6.1
DESCRIPTION "Optimized C library for ECDSA signatures and secret/public key operations on curve secp256k1."
HOMEPAGE_URL "https://github.com/bitcoin-core/secp256k1"
LANGUAGES C
@ -32,7 +32,7 @@ endif()
# All changes in experimental modules are treated as if they don't affect the
# interface and therefore only increase the revision.
set(${PROJECT_NAME}_LIB_VERSION_CURRENT 5)
set(${PROJECT_NAME}_LIB_VERSION_REVISION 0)
set(${PROJECT_NAME}_LIB_VERSION_REVISION 1)
set(${PROJECT_NAME}_LIB_VERSION_AGE 0)
#=============================
@ -55,11 +55,12 @@ option(SECP256K1_INSTALL "Enable installation." ${PROJECT_IS_TOP_LEVEL})
## Modules
# We declare all options before processing them, to make sure we can express
# dependendencies while processing.
# dependencies while processing.
option(SECP256K1_ENABLE_MODULE_ECDH "Enable ECDH module." ON)
option(SECP256K1_ENABLE_MODULE_RECOVERY "Enable ECDSA pubkey recovery module." OFF)
option(SECP256K1_ENABLE_MODULE_EXTRAKEYS "Enable extrakeys module." ON)
option(SECP256K1_ENABLE_MODULE_SCHNORRSIG "Enable schnorrsig module." ON)
option(SECP256K1_ENABLE_MODULE_BATCH "Enable batch module." OFF)
option(SECP256K1_ENABLE_MODULE_MUSIG "Enable musig module." ON)
option(SECP256K1_ENABLE_MODULE_ELLSWIFT "Enable ElligatorSwift module." ON)
@ -69,6 +70,18 @@ if(SECP256K1_ENABLE_MODULE_ELLSWIFT)
add_compile_definitions(ENABLE_MODULE_ELLSWIFT=1)
endif()
option(SECP256K1_EXPERIMENTAL "Allow experimental configuration options." OFF)
if(SECP256K1_ENABLE_MODULE_BATCH)
if(NOT SECP256K1_EXPERIMENTAL)
message(FATAL_ERROR "Schnorrsig batch validation is experimental. Use -DSECP256K1_EXPERIMENTAL=ON to allow.")
endif()
if(DEFINED SECP256K1_ENABLE_MODULE_SCHNORRSIG AND NOT SECP256K1_ENABLE_MODULE_SCHNORRSIG)
message(FATAL_ERROR "Module dependency error: You have disabled the schnorrsig module explicitly, but it is required by the Schnorrsig batch validation module.")
endif()
set(SECP256K1_ENABLE_MODULE_SCHNORRSIG ON)
add_compile_definitions(ENABLE_MODULE_BATCH=1)
endif()
if(SECP256K1_ENABLE_MODULE_MUSIG)
if(DEFINED SECP256K1_ENABLE_MODULE_SCHNORRSIG AND NOT SECP256K1_ENABLE_MODULE_SCHNORRSIG)
message(FATAL_ERROR "Module dependency error: You have disabled the schnorrsig module explicitly, but it is required by the musig module.")
@ -156,7 +169,6 @@ elseif(SECP256K1_ASM)
endif()
endif()
option(SECP256K1_EXPERIMENTAL "Allow experimental configuration options." OFF)
if(NOT SECP256K1_EXPERIMENTAL)
if(SECP256K1_ASM STREQUAL "arm32")
message(FATAL_ERROR "ARM32 assembly is experimental. Use -DSECP256K1_EXPERIMENTAL=ON to allow.")
@ -325,6 +337,7 @@ message(" ECDH ................................ ${SECP256K1_ENABLE_MODULE_ECDH}
message(" ECDSA pubkey recovery ............... ${SECP256K1_ENABLE_MODULE_RECOVERY}")
message(" extrakeys ........................... ${SECP256K1_ENABLE_MODULE_EXTRAKEYS}")
message(" schnorrsig .......................... ${SECP256K1_ENABLE_MODULE_SCHNORRSIG}")
message(" batch ............................... ${SECP256K1_ENABLE_MODULE_BATCH}")
message(" musig ............................... ${SECP256K1_ENABLE_MODULE_MUSIG}")
message(" ElligatorSwift ...................... ${SECP256K1_ENABLE_MODULE_ELLSWIFT}")
message("Parameters:")

View file

@ -183,6 +183,17 @@ if BUILD_WINDOWS
schnorr_example_LDFLAGS += -lbcrypt
endif
TESTS += schnorr_example
if ENABLE_MODULE_BATCH
noinst_PROGRAMS += batch_example
batch_example_SOURCES = examples/batch.c
batch_example_CPPFLAGS = -I$(top_srcdir)/include
batch_example_LDADD = libsecp256k1.la
batch_example_LDFLAGS = -static
if BUILD_WINDOWS
batch_example_LDFLAGS += -lbcrypt
endif
TESTS += batch_example
endif
endif
if ENABLE_MODULE_ELLSWIFT
noinst_PROGRAMS += ellswift_example
@ -300,3 +311,7 @@ endif
if ENABLE_MODULE_ELLSWIFT
include src/modules/ellswift/Makefile.am.include
endif
if ENABLE_MODULE_BATCH
include src/modules/batch/Makefile.am.include
endif

View file

@ -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 Batch Verification (experimental).
Implementation details
----------------------

82
src/secp256k1/ci/cirrus.sh Executable file
View file

@ -0,0 +1,82 @@
#!/bin/sh
set -e
set -x
export LC_ALL=C
# Start persistent wineserver if necessary.
# This speeds up jobs with many invocations of wine (e.g., ./configure with MSVC) tremendously.
case "$WRAPPER_CMD" in
*wine*)
# This is apparently only reliable when we run a dummy command such as "hh.exe" afterwards.
wineserver -p && wine hh.exe
;;
esac
env >> test_env.log
$CC -v || true
valgrind --version || true
$WRAPPER_CMD --version || true
./autogen.sh
./configure \
--enable-experimental="$EXPERIMENTAL" \
--with-test-override-wide-multiply="$WIDEMUL" --with-asm="$ASM" \
--with-ecmult-window="$ECMULTWINDOW" \
--with-ecmult-gen-precision="$ECMULTGENPRECISION" \
--enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" \
--enable-module-schnorrsig="$SCHNORRSIG" \
--enable-module-batch="$BATCH" \
--enable-examples="$EXAMPLES" \
--with-valgrind="$WITH_VALGRIND" \
--host="$HOST" $EXTRAFLAGS
# We have set "-j<n>" in MAKEFLAGS.
make
# Print information about binaries so that we can see that the architecture is correct
file *tests* || true
file bench* || true
file .libs/* || true
# This tells `make check` to wrap test invocations.
export LOG_COMPILER="$WRAPPER_CMD"
make "$BUILD"
if [ "$BENCH" = "yes" ]
then
# Using the local `libtool` because on macOS the system's libtool has nothing to do with GNU libtool
EXEC='./libtool --mode=execute'
if [ -n "$WRAPPER_CMD" ]
then
EXEC="$EXEC $WRAPPER_CMD"
fi
{
$EXEC ./bench_ecmult
$EXEC ./bench_internal
$EXEC ./bench
} >> bench.log 2>&1
fi
if [ "$CTIMETEST" = "yes" ]
then
./libtool --mode=execute valgrind --error-exitcode=42 ./valgrind_ctime_test > valgrind_ctime_test.log 2>&1
fi
# Rebuild precomputed files (if not cross-compiling).
if [ -z "$HOST" ]
then
make clean-precomp
make precomp
fi
# Shutdown wineserver again
wineserver -k || true
# Check that no repo files have been modified by the build.
# (This fails for example if the precomp files need to be updated in the repo.)
git diff --exit-code

View file

@ -5,8 +5,8 @@ AC_PREREQ([2.60])
# backwards-compatible and therefore at most increase the minor version.
define(_PKG_VERSION_MAJOR, 0)
define(_PKG_VERSION_MINOR, 6)
define(_PKG_VERSION_PATCH, 0)
define(_PKG_VERSION_IS_RELEASE, true)
define(_PKG_VERSION_PATCH, 1)
define(_PKG_VERSION_IS_RELEASE, false)
# The library version is based on libtool versioning of the ABI. The set of
# rules for updating the version can be found here:
@ -14,7 +14,7 @@ define(_PKG_VERSION_IS_RELEASE, true)
# All changes in experimental modules are treated as if they don't affect the
# interface and therefore only increase the revision.
define(_LIB_VERSION_CURRENT, 5)
define(_LIB_VERSION_REVISION, 0)
define(_LIB_VERSION_REVISION, 1)
define(_LIB_VERSION_AGE, 0)
AC_INIT([libsecp256k1],m4_join([.], _PKG_VERSION_MAJOR, _PKG_VERSION_MINOR, _PKG_VERSION_PATCH)m4_if(_PKG_VERSION_IS_RELEASE, [true], [], [-dev]),[https://github.com/bitcoin-core/secp256k1/issues],[libsecp256k1],[https://github.com/bitcoin-core/secp256k1])
@ -192,6 +192,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_batch,
AS_HELP_STRING([--enable-module-batch],[enable batch verification module (experimental) [default=no]]), [],
[SECP_SET_DEFAULT([enable_module_batch], [no], [yes])])
AC_ARG_ENABLE(external_default_callbacks,
AS_HELP_STRING([--enable-external-default-callbacks],[enable external default callback functions [default=no]]), [],
[SECP_SET_DEFAULT([enable_external_default_callbacks], [no], [no])])
@ -254,8 +258,8 @@ fi
print_msan_notice=no
if test x"$enable_ctime_tests" = x"yes"; then
SECP_MSAN_CHECK
# MSan on Clang >=16 reports unitialized memory in function parameters and return values, even if
# the uninitalized variable is never actually "used". This is called "eager" checking, and it's
# MSan on Clang >=16 reports uninitialized memory in function parameters and return values, even if
# the uninitialized variable is never actually "used". This is called "eager" checking, and it's
# sounds like good idea for normal use of MSan. However, it yields many false positives in the
# ctime_tests because many return values depend on secret (i.e., "uninitialized") values, and
# we're only interested in detecting branches (which count as "uses") on secret data.
@ -430,6 +434,10 @@ if test x"$enable_module_ecdh" = x"yes"; then
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_ECDH=1"
fi
if test x"$enable_module_batch" = x"yes"; then
AC_DEFINE(ENABLE_MODULE_BATCH, 1, [Define this symbol to enable the batch verification module])
fi
if test x"$enable_external_default_callbacks" = x"yes"; then
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DUSE_EXTERNAL_DEFAULT_CALLBACKS=1"
fi
@ -442,6 +450,9 @@ if test x"$enable_experimental" = x"no"; then
if test x"$set_asm" = x"arm32"; then
AC_MSG_ERROR([ARM32 assembly is experimental. Use --enable-experimental to allow.])
fi
if test x"$enable_module_batch" = x"yes"; then
AC_MSG_ERROR([batch verification module is experimental. Use --enable-experimental to allow.])
fi
fi
###
@ -463,6 +474,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_BATCH], [test x"$enable_module_batch" = 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"])
@ -486,6 +498,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 batch = $enable_module_batch"
echo
echo " asm = $set_asm"
echo " ecmult window size = $set_ecmult_window"

View file

@ -0,0 +1,15 @@
# Schnorrsig Batch Verification Speedup
![Speedup over single verification](speedup-batch/schnorrsig-speedup-batch.png)
# Tweak Pubkey Check Batch Verification Speedup
![Speedup over single verification](speedup-batch/tweakcheck-speedup-batch.png)
Build steps
-----------
To generate the above graphs on your local machine:
$ cd doc/speedup-batch
$ make
$ make speedup-batch.png

View file

@ -0,0 +1 @@
*.dat

View file

@ -0,0 +1,23 @@
schnorrsig_data = schnorrsig_batch.dat schnorrsig_single.dat
tweak_data = tweak_batch.dat tweak_single.dat
bench_output.txt: bench.sh
SECP256K1_BENCH_ITERS=500000 ./bench.sh bench_output.txt
schnorrsig_batch.dat: bench_output.txt
cat bench_output.txt | grep -v "schnorrsig_batch_verify_1 " | awk '{ gsub(/ /,""); print }' | awk -F, 'match($$0, /schnorrsig_batch_verify_([0-9]+)/, arr) {print arr[1] " " $$3}' > schnorrsig_batch.dat
schnorrsig_single.dat: bench_output.txt
cat bench_output.txt | awk '{ gsub(/ /,""); print }' | awk -F, 'match($$0, /schnorrsig_verify/) {print $$3}' > schnorrsig_single.dat
tweak_batch.dat: bench_output.txt
cat bench_output.txt | grep -v "tweak_check_batch_verify_1 " | awk '{ gsub(/ /,""); print }' | awk -F, 'match($$0, /tweak_check_batch_verify_([0-9]+)/, arr) {print arr[1] " " $$3}' > tweak_batch.dat
tweak_single.dat: bench_output.txt
cat bench_output.txt | awk '{ gsub(/ /,""); print }' | awk -F, 'match($$0, /tweak_add_check/) {print $$3}' > tweak_single.dat
speedup-batch.png: $(schnorrsig_data) $(tweak_data) plot.gp
gnuplot plot.gp
clean:
rm *.log *.txt *.dat *.png

View file

@ -0,0 +1,13 @@
#!/bin/bash
output_file=$1
cur_dir=$(pwd)
cd ../../
echo "HEAD: $(git rev-parse --short HEAD)" > "$cur_dir/$output_file.log"
make clean
./autogen.sh
./configure --enable-experimental --enable-module-batch --enable-module-schnorrsig >> "$cur_dir/$output_file.log"
make -j
./bench schnorrsig > "$cur_dir/$output_file"
./bench extrakeys >> "$cur_dir/$output_file"

View file

@ -0,0 +1,137 @@
Benchmark , Min(us) , Avg(us) , Max(us)
schnorrsig_sign , 50.4 , 50.5 , 50.7
schnorrsig_verify , 89.1 , 89.2 , 89.3
schnorrsig_batch_verify_1 , 104.0 , 104.0 , 104.0
schnorrsig_batch_verify_2 , 89.0 , 89.1 , 89.1
schnorrsig_batch_verify_3 , 84.1 , 84.1 , 84.1
schnorrsig_batch_verify_4 , 81.5 , 81.5 , 81.5
schnorrsig_batch_verify_5 , 79.9 , 79.9 , 79.9
schnorrsig_batch_verify_7 , 78.0 , 78.1 , 78.3
schnorrsig_batch_verify_9 , 77.0 , 77.0 , 77.1
schnorrsig_batch_verify_11 , 76.2 , 76.3 , 76.3
schnorrsig_batch_verify_14 , 75.6 , 75.6 , 75.6
schnorrsig_batch_verify_17 , 75.2 , 75.2 , 75.2
schnorrsig_batch_verify_21 , 74.8 , 74.8 , 74.8
schnorrsig_batch_verify_26 , 74.5 , 74.6 , 74.9
schnorrsig_batch_verify_32 , 74.3 , 74.5 , 74.7
schnorrsig_batch_verify_39 , 74.1 , 74.1 , 74.1
schnorrsig_batch_verify_47 , 73.9 , 73.9 , 73.9
schnorrsig_batch_verify_57 , 74.5 , 74.5 , 74.5
schnorrsig_batch_verify_69 , 74.3 , 74.3 , 74.5
schnorrsig_batch_verify_83 , 74.1 , 74.1 , 74.2
schnorrsig_batch_verify_100 , 73.9 , 74.0 , 74.1
schnorrsig_batch_verify_121 , 74.1 , 74.1 , 74.2
schnorrsig_batch_verify_146 , 73.9 , 73.9 , 74.0
schnorrsig_batch_verify_176 , 74.0 , 74.2 , 74.5
schnorrsig_batch_verify_212 , 73.9 , 74.1 , 74.1
schnorrsig_batch_verify_255 , 74.0 , 74.0 , 74.1
schnorrsig_batch_verify_307 , 73.9 , 74.0 , 74.1
schnorrsig_batch_verify_369 , 73.9 , 73.9 , 73.9
schnorrsig_batch_verify_443 , 73.9 , 74.1 , 74.3
schnorrsig_batch_verify_532 , 74.0 , 74.0 , 74.1
schnorrsig_batch_verify_639 , 73.9 , 74.0 , 74.0
schnorrsig_batch_verify_767 , 73.9 , 73.9 , 73.9
schnorrsig_batch_verify_921 , 74.0 , 74.0 , 74.1
schnorrsig_batch_verify_1106 , 73.9 , 73.9 , 73.9
schnorrsig_batch_verify_1328 , 73.9 , 74.1 , 74.2
schnorrsig_batch_verify_1594 , 74.0 , 74.1 , 74.1
schnorrsig_batch_verify_1913 , 74.0 , 74.0 , 74.0
schnorrsig_batch_verify_2296 , 74.0 , 74.0 , 74.0
schnorrsig_batch_verify_2756 , 73.9 , 74.0 , 74.1
schnorrsig_batch_verify_3308 , 74.1 , 74.1 , 74.2
schnorrsig_batch_verify_3970 , 74.1 , 74.2 , 74.4
schnorrsig_batch_verify_4765 , 74.0 , 74.1 , 74.2
schnorrsig_batch_verify_5719 , 74.0 , 74.1 , 74.1
schnorrsig_batch_verify_6863 , 74.0 , 74.1 , 74.1
schnorrsig_batch_verify_8236 , 74.0 , 74.1 , 74.1
schnorrsig_batch_verify_9884 , 74.0 , 74.1 , 74.3
schnorrsig_batch_verify_11861 , 74.0 , 74.0 , 74.1
schnorrsig_batch_verify_14234 , 73.9 , 74.0 , 74.1
schnorrsig_batch_verify_17081 , 73.9 , 73.9 , 73.9
schnorrsig_batch_verify_20498 , 73.9 , 74.0 , 74.0
schnorrsig_batch_verify_24598 , 73.9 , 74.0 , 74.1
schnorrsig_batch_verify_29518 , 73.9 , 74.0 , 74.1
schnorrsig_batch_verify_35422 , 73.9 , 73.9 , 73.9
schnorrsig_batch_verify_42507 , 73.9 , 74.0 , 74.0
schnorrsig_batch_verify_51009 , 73.9 , 74.1 , 74.3
schnorrsig_batch_verify_61211 , 73.9 , 73.9 , 74.0
schnorrsig_batch_verify_73454 , 73.9 , 74.0 , 74.3
schnorrsig_batch_verify_88145 , 73.9 , 74.0 , 74.1
schnorrsig_batch_verify_105775 , 74.0 , 74.1 , 74.1
schnorrsig_batch_verify_126931 , 73.9 , 74.0 , 74.1
schnorrsig_batch_verify_152318 , 73.9 , 73.9 , 74.0
schnorrsig_batch_verify_182782 , 73.9 , 73.9 , 74.0
schnorrsig_batch_verify_219339 , 73.9 , 73.9 , 74.0
schnorrsig_batch_verify_263207 , 74.0 , 74.1 , 74.3
schnorrsig_batch_verify_315849 , 73.9 , 74.0 , 74.0
schnorrsig_batch_verify_379019 , 73.9 , 73.9 , 73.9
schnorrsig_batch_verify_454823 , 74.0 , 74.0 , 74.0
Benchmark , Min(us) , Avg(us) , Max(us)
tweak_add_check , 64.7 , 64.7 , 65.0
tweak_check_batch_verify_1 , 69.7 , 69.8 , 69.8
tweak_check_batch_verify_2 , 57.2 , 57.2 , 57.3
tweak_check_batch_verify_3 , 52.0 , 52.1 , 52.2
tweak_check_batch_verify_4 , 49.4 , 49.5 , 49.5
tweak_check_batch_verify_5 , 47.9 , 47.9 , 47.9
tweak_check_batch_verify_7 , 46.1 , 46.1 , 46.2
tweak_check_batch_verify_9 , 45.2 , 45.2 , 45.4
tweak_check_batch_verify_11 , 44.5 , 44.6 , 44.6
tweak_check_batch_verify_14 , 43.9 , 43.9 , 43.9
tweak_check_batch_verify_17 , 43.5 , 43.5 , 43.5
tweak_check_batch_verify_21 , 43.1 , 43.1 , 43.1
tweak_check_batch_verify_26 , 42.8 , 42.8 , 42.8
tweak_check_batch_verify_32 , 42.5 , 42.6 , 42.6
tweak_check_batch_verify_39 , 42.3 , 42.4 , 42.4
tweak_check_batch_verify_47 , 42.2 , 42.2 , 42.2
tweak_check_batch_verify_57 , 42.1 , 42.2 , 42.3
tweak_check_batch_verify_69 , 42.0 , 42.1 , 42.1
tweak_check_batch_verify_83 , 41.9 , 41.9 , 41.9
tweak_check_batch_verify_100 , 41.8 , 41.9 , 41.9
tweak_check_batch_verify_121 , 42.1 , 42.1 , 42.1
tweak_check_batch_verify_146 , 42.0 , 42.0 , 42.0
tweak_check_batch_verify_176 , 41.9 , 41.9 , 42.0
tweak_check_batch_verify_212 , 41.8 , 41.9 , 41.9
tweak_check_batch_verify_255 , 41.9 , 41.9 , 41.9
tweak_check_batch_verify_307 , 41.8 , 41.9 , 41.9
tweak_check_batch_verify_369 , 41.9 , 42.0 , 42.1
tweak_check_batch_verify_443 , 41.9 , 41.9 , 41.9
tweak_check_batch_verify_532 , 41.9 , 41.9 , 41.9
tweak_check_batch_verify_639 , 41.9 , 41.9 , 42.0
tweak_check_batch_verify_767 , 41.9 , 41.9 , 41.9
tweak_check_batch_verify_921 , 41.9 , 41.9 , 41.9
tweak_check_batch_verify_1106 , 41.9 , 41.9 , 41.9
tweak_check_batch_verify_1328 , 41.9 , 41.9 , 42.0
tweak_check_batch_verify_1594 , 41.9 , 41.9 , 42.0
tweak_check_batch_verify_1913 , 41.9 , 41.9 , 41.9
tweak_check_batch_verify_2296 , 41.9 , 41.9 , 41.9
tweak_check_batch_verify_2756 , 41.8 , 41.9 , 41.9
tweak_check_batch_verify_3308 , 41.9 , 41.9 , 42.0
tweak_check_batch_verify_3970 , 41.9 , 41.9 , 41.9
tweak_check_batch_verify_4765 , 41.8 , 41.9 , 41.9
tweak_check_batch_verify_5719 , 41.9 , 42.0 , 42.1
tweak_check_batch_verify_6863 , 42.0 , 42.0 , 42.0
tweak_check_batch_verify_8236 , 42.0 , 42.0 , 42.0
tweak_check_batch_verify_9884 , 41.9 , 41.9 , 42.0
tweak_check_batch_verify_11861 , 41.9 , 42.0 , 42.1
tweak_check_batch_verify_14234 , 41.9 , 42.0 , 42.0
tweak_check_batch_verify_17081 , 41.8 , 41.9 , 41.9
tweak_check_batch_verify_20498 , 41.8 , 41.9 , 41.9
tweak_check_batch_verify_24598 , 41.8 , 41.9 , 41.9
tweak_check_batch_verify_29518 , 41.9 , 41.9 , 41.9
tweak_check_batch_verify_35422 , 41.9 , 41.9 , 41.9
tweak_check_batch_verify_42507 , 41.8 , 41.8 , 41.9
tweak_check_batch_verify_51009 , 41.9 , 41.9 , 41.9
tweak_check_batch_verify_61211 , 41.8 , 41.8 , 41.8
tweak_check_batch_verify_73454 , 41.8 , 42.0 , 42.2
tweak_check_batch_verify_88145 , 41.9 , 41.9 , 41.9
tweak_check_batch_verify_105775 , 41.8 , 41.8 , 41.8
tweak_check_batch_verify_126931 , 41.8 , 41.9 , 41.9
tweak_check_batch_verify_152318 , 41.8 , 41.9 , 42.0
tweak_check_batch_verify_182782 , 41.9 , 41.9 , 41.9
tweak_check_batch_verify_219339 , 41.9 , 42.0 , 42.0
tweak_check_batch_verify_263207 , 41.9 , 42.0 , 42.1
tweak_check_batch_verify_315849 , 41.9 , 41.9 , 41.9
tweak_check_batch_verify_379019 , 41.9 , 41.9 , 42.0
tweak_check_batch_verify_454823 , 41.9 , 41.9 , 41.9

View file

@ -0,0 +1,127 @@
HEAD: 6ddb0d0c
checking build system type... x86_64-pc-linux-gnu
checking host system type... x86_64-pc-linux-gnu
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for a thread-safe mkdir -p... /usr/bin/mkdir -p
checking for gawk... gawk
checking whether make sets $(MAKE)... yes
checking whether make supports nested variables... yes
checking whether make supports nested variables... (cached) yes
checking for gcc... gcc
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables...
checking whether we are cross compiling... no
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether gcc accepts -g... yes
checking for gcc option to accept ISO C89... none needed
checking whether gcc understands -c and -o together... yes
checking whether make supports the include directive... yes (GNU style)
checking dependency style of gcc... gcc3
checking dependency style of gcc... gcc3
checking for ar... ar
checking the archiver (ar) interface... ar
checking how to print strings... printf
checking for a sed that does not truncate output... /usr/bin/sed
checking for grep that handles long lines and -e... /usr/bin/grep
checking for egrep... /usr/bin/grep -E
checking for fgrep... /usr/bin/grep -F
checking for ld used by gcc... /usr/bin/ld
checking if the linker (/usr/bin/ld) is GNU ld... yes
checking for BSD- or MS-compatible name lister (nm)... /usr/bin/nm -B
checking the name lister (/usr/bin/nm -B) interface... BSD nm
checking whether ln -s works... yes
checking the maximum length of command line arguments... 1572864
checking how to convert x86_64-pc-linux-gnu file names to x86_64-pc-linux-gnu format... func_convert_file_noop
checking how to convert x86_64-pc-linux-gnu file names to toolchain format... func_convert_file_noop
checking for /usr/bin/ld option to reload object files... -r
checking for objdump... objdump
checking how to recognize dependent libraries... pass_all
checking for dlltool... no
checking how to associate runtime and link libraries... printf %s\n
checking for archiver @FILE support... @
checking for strip... strip
checking for ranlib... ranlib
checking command to parse /usr/bin/nm -B output from gcc object... ok
checking for sysroot... no
checking for a working dd... /usr/bin/dd
checking how to truncate binary pipes... /usr/bin/dd bs=4096 count=1
checking for mt... mt
checking if mt is a manifest tool... no
checking how to run the C preprocessor... gcc -E
checking for ANSI C header files... yes
checking for sys/types.h... yes
checking for sys/stat.h... yes
checking for stdlib.h... yes
checking for string.h... yes
checking for memory.h... yes
checking for strings.h... yes
checking for inttypes.h... yes
checking for stdint.h... yes
checking for unistd.h... yes
checking for dlfcn.h... yes
checking for objdir... .libs
checking if gcc supports -fno-rtti -fno-exceptions... no
checking for gcc option to produce PIC... -fPIC -DPIC
checking if gcc PIC flag -fPIC -DPIC works... yes
checking if gcc static flag -static works... yes
checking if gcc supports -c -o file.o... yes
checking if gcc supports -c -o file.o... (cached) yes
checking whether the gcc linker (/usr/bin/ld -m elf_x86_64) supports shared libraries... yes
checking whether -lc should be explicitly linked in... no
checking dynamic linker characteristics... GNU/Linux ld.so
checking how to hardcode library paths into programs... immediate
checking whether stripping libraries is possible... yes
checking if libtool supports shared libraries... yes
checking whether to build shared libraries... yes
checking whether to build static libraries... yes
checking if gcc supports -Werror=unknown-warning-option... no
checking if gcc supports -std=c89 -pedantic -Wno-long-long -Wnested-externs -Wshadow -Wstrict-prototypes -Wundef... yes
checking if gcc supports -Wno-overlength-strings... yes
checking if gcc supports -Wall... yes
checking if gcc supports -Wno-unused-function... yes
checking if gcc supports -Wextra... yes
checking if gcc supports -Wcast-align... yes
checking if gcc supports -Wcast-align=strict... yes
checking if gcc supports -Wconditional-uninitialized... no
checking if gcc supports -fvisibility=hidden... yes
checking for valgrind support... yes
checking for x86_64 assembly availability... yes
configure: ******
configure: WARNING: experimental build
configure: Experimental features do not have stable APIs or properties, and may not be safe for production use.
configure: Building batch verification module: yes
configure: ******
checking that generated files are newer than configure... done
configure: creating ./config.status
config.status: creating Makefile
config.status: creating libsecp256k1.pc
config.status: creating src/libsecp256k1-config.h
config.status: src/libsecp256k1-config.h is unchanged
config.status: executing depfiles commands
config.status: executing libtool commands
Build Options:
with external callbacks = no
with benchmarks = yes
with tests = yes
with coverage = no
with examples = no
module ecdh = no
module recovery = no
module extrakeys = yes
module schnorrsig = yes
module batch = yes
asm = x86_64
ecmult window size = 15
ecmult gen prec. bits = 4
valgrind = yes
CC = gcc
CPPFLAGS =
SECP_CFLAGS = -O2 -std=c89 -pedantic -Wno-long-long -Wnested-externs -Wshadow -Wstrict-prototypes -Wundef -Wno-overlength-strings -Wall -Wno-unused-function -Wextra -Wcast-align -Wcast-align=strict -fvisibility=hidden
CFLAGS = -g -O2
LDFLAGS =

View file

@ -0,0 +1,41 @@
set style line 80 lt rgb "#808080"
set style line 81 lt 0
set style line 81 lt rgb "#808080"
set grid back linestyle 81
set border 3 back linestyle 80
set xtics nomirror
set ytics nomirror
set style line 1 lt rgb "#A00000" lw 2 pt 1
set style line 2 lt rgb "#00A000" lw 2 pt 6
set style line 3 lt rgb "#5060D0" lw 2 pt 2
set style line 4 lt rgb "#F25900" lw 2 pt 9
set key bottom right
set autoscale
unset log
unset label
set xtic auto
set ytic auto
set title "Batch signature verification in libsecp256k1"
set xlabel "Number of signatures (logarithmic)"
set ylabel "Verification time per signature in us"
set grid
set logscale x
set mxtics 10
# Generate graph of Schnorr signature benchmark
schnorrsig_single_val=system("cat schnorrsig_single.dat")
set xrange [1.1:]
set xtics add ("2" 2)
set yrange [0.9:]
set ytics -1,0.1,3
set ylabel "Speedup over single verification"
set term png size 800,600
set output 'schnorrsig-speedup-batch.png'
plot "schnorrsig_batch.dat" using 1:(schnorrsig_single_val/$2) with points title "" ls 1
# Generate graph of tweaked x-only pubkey check benchmark
set title "Batch tweaked x-only pubkey check in libsecp256k1"
set xlabel "Number of tweak checks (logarithmic)"
tweak_single_val=system("cat tweak_single.dat")
set output 'tweakcheck-speedup-batch.png'
plot "tweak_batch.dat" using 1:(tweak_single_val/$2) with points title "" ls 1

Binary file not shown.

After

(image error) Size: 8 KiB

Binary file not shown.

After

(image error) Size: 8.4 KiB

View file

@ -0,0 +1,181 @@
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <secp256k1.h>
#include <secp256k1_batch.h>
#include <secp256k1_schnorrsig_batch.h>
#include <secp256k1_tweak_check_batch.h>
#include "random.h"
/* key pair data */
unsigned char sk[32];
secp256k1_keypair keypair;
secp256k1_xonly_pubkey pk;
/* schnorrsig verification data */
#define N_SIGS 10
unsigned char msg[N_SIGS][32];
unsigned char sig[N_SIGS][64];
/* xonly pubkey tweak checks data */
#define N_CHECKS 10
unsigned char tweaked_pubkey[N_CHECKS][32];
int tweaked_pk_parity[N_CHECKS];
unsigned char tweak[N_CHECKS][32];
/* 2*N_SIGS since one schnorrsig creates two scalar-point pairs in batch
* whereas one tweak check creates one scalar-point pair in batch */
#define N_TERMS (N_CHECKS + 2*N_SIGS)
/* generate key pair required for sign and verify */
int create_keypair(secp256k1_context *ctx) {
while(1) {
if (!fill_random(sk, sizeof(sk))) {
printf("Failed to generate randomness\n");
return 1;
}
if (secp256k1_keypair_create(ctx, &keypair, sk)) {
break;
}
}
if (!secp256k1_keypair_xonly_pub(ctx, &pk, NULL, &keypair)) {
return 0;
}
return 1;
}
/* create valid schnorrsigs for N_SIGS random messages */
int generate_schnorrsigs(secp256k1_context *ctx) {
size_t i;
for (i = 0; i < N_SIGS; i++) {
if(!fill_random(msg[i], sizeof(msg[i]))) {
printf("Failed to generate randomness\n");
return 1;
}
assert(secp256k1_schnorrsig_sign32(ctx, sig[i], msg[i], &keypair, NULL));
assert(secp256k1_schnorrsig_verify(ctx, sig[i], msg[i], sizeof(msg[i]), &pk));
}
return 1;
}
/* create valid N_CHECKS number of xonly pukey tweak checks */
int generate_xonlypub_tweak_checks(secp256k1_context *ctx) {
secp256k1_pubkey output_pk;
secp256k1_xonly_pubkey output_xonly_pk;
size_t i;
for (i = 0; i < N_CHECKS; i++) {
if (!fill_random(tweak[i], sizeof(tweak[i]))) {
printf("Failed to generate randomness\n");
return 1;
}
assert(secp256k1_xonly_pubkey_tweak_add(ctx, &output_pk, &pk, tweak[i]));
assert(secp256k1_xonly_pubkey_from_pubkey(ctx, &output_xonly_pk, &tweaked_pk_parity[i], &output_pk));
assert(secp256k1_xonly_pubkey_serialize(ctx, tweaked_pubkey[i], &output_xonly_pk));
assert(secp256k1_xonly_pubkey_tweak_add_check(ctx, tweaked_pubkey[i], tweaked_pk_parity[i], &pk, tweak[i]));
}
return 1;
}
int main(void) {
int ret;
size_t i;
/* batch object uses secp256k1_context only for the error callback function
* here, we create secp256k1_context that can sign and verify, only to generate
* input data (schnorrsigs, tweak checks) required for the batch */
secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
secp256k1_batch *batch;
unsigned char auxiliary_rand[16];
/* Generate 16 bytes of randomness to use during batch creation. */
if (!fill_random(auxiliary_rand, sizeof(auxiliary_rand))) {
printf("Failed to generate randomness\n");
return 1;
}
batch = secp256k1_batch_create(ctx, N_TERMS, auxiliary_rand);
assert(ctx != NULL);
assert(batch != NULL);
/* key pair generation */
printf("Creating a key pair.........................");
if(!create_keypair(ctx)) {
printf("FAILED\n");
return 1;
}
printf("ok\n");
/* create schnorrsigs for N_SIGS random messages */
printf("Signing messages............................");
if(!generate_schnorrsigs(ctx)) {
printf("FAILED\n");
return 1;
}
printf("ok\n");
printf("Adding signatures to the batch object.......");
for (i = 0; i < N_SIGS; i++) {
/* It is recommended to check the validity of the batch before adding a
* new input (schnorrsig/tweak check) to it. The `secp256k1_batch_add_` APIs
* won't add any new input to invalid batch since the final `secp256k1_batch_verify`
* API call will fail even if the new input is valid. */
if(secp256k1_batch_usable(ctx, batch)) {
ret = secp256k1_batch_add_schnorrsig(ctx, batch, sig[i], msg[i], sizeof(msg[i]), &pk);
} else {
printf("INVALID BATCH\n");
return 1;
}
if(!ret) {
printf("FAILED\n");
return 1;
}
}
printf("ok\n");
printf("Generating xonlypub tweak checks............");
if(!generate_xonlypub_tweak_checks(ctx)) {
printf("FAILED\n");
return 1;
}
printf("ok\n");
printf("Adding tweak checks to the batch object.....");
for (i = 0; i < N_CHECKS; i++) {
/* It is recommended to check the validity of the batch before adding a
* new input (schnorrsig/tweak check) to it. The `secp256k1_batch_add_` APIs
* won't add any new input to invalid batch since the final `secp256k1_batch_verify`
* API call will fail even if the new input is valid. */
if(secp256k1_batch_usable(ctx, batch)) {
ret = secp256k1_batch_add_xonlypub_tweak_check(ctx, batch, tweaked_pubkey[i], tweaked_pk_parity[i], &pk, tweak[i]);
} else {
printf("INVALID BATCH\n");
return 1;
}
if(!ret) {
printf("FAILED\n");
return 1;
}
}
printf("ok\n");
printf("Verifying the batch object..................");
if(!secp256k1_batch_verify(ctx, batch)) {
printf("FAILED\n");
return 1;
}
printf("ok\n");
secp256k1_batch_destroy(ctx, batch);
secp256k1_context_destroy(ctx);
return 0;
}

View file

@ -0,0 +1,110 @@
#ifndef SECP256K1_BATCH_H
#define SECP256K1_BATCH_H
#include "secp256k1.h"
#ifdef __cplusplus
extern "C" {
#endif
/** This module implements a Batch Verification object that supports:
*
* 1. Schnorr signatures compliant with Bitcoin Improvement Proposal 340
* "Schnorr Signatures for secp256k1"
* (https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki).
*
* 2. Taproot commitments compliant with Bitcoin Improvemtn Proposal 341
* "Taproot: SegWit version 1 spending rules"
* (https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki).
*/
/** Opaque data structure that holds information required for the batch verification.
*
* The purpose of this structure is to store elliptic curve points, their scalar
* coefficients, and scalar coefficient of generator point participating in Multi-Scalar
* Point Multiplication computation, which is done by `secp256k1_ecmult_strauss_batch_internal`
*/
typedef struct secp256k1_batch_struct secp256k1_batch;
/** Create a secp256k1 batch object object (in dynamically allocated memory).
*
* This function uses malloc to allocate memory. It is guaranteed that malloc is
* called at most twice for every call of this function.
*
* Returns: a newly created batch object.
* Args: ctx: an existing `secp256k1_context` object. Not to be confused
* with the batch object object that this function creates.
* In: max_terms: Max number of (scalar, curve point) pairs that the batch
* object can store.
* 1. `batch_add_schnorrsig` - adds two scalar-point pairs to the batch
* 2. `batch_add_xonpub_tweak_check` - adds one scalar-point pair to the batch
* Hence, for adding n schnorrsigs and m tweak checks, `max_terms`
* should be set to 2*n + m.
* aux_rand16: 16 bytes of fresh randomness. While recommended to provide
* this, it is only supplemental to security and can be NULL. A
* NULL argument is treated the same as an all-zero one.
*/
SECP256K1_API secp256k1_batch* secp256k1_batch_create(
const secp256k1_context* ctx,
size_t max_terms,
const unsigned char *aux_rand16
) SECP256K1_ARG_NONNULL(1) SECP256K1_WARN_UNUSED_RESULT;
/** Destroy a secp256k1 batch object (created in dynamically allocated memory).
*
* The batch object's pointer may not be used afterwards.
*
* Args: ctx: a secp256k1 context object.
* batch: an existing batch object to destroy, constructed
* using `secp256k1_batch_create`
*/
SECP256K1_API void secp256k1_batch_destroy(
const secp256k1_context* ctx,
secp256k1_batch* batch
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2);
/** Checks if a batch can be used by the `secp256k1_batch_add_*` APIs.
*
* Returns: 1: batch can be used by `secp256k1_batch_add_*` APIs.
* 0: batch cannot be used by `secp256k1_batch_add_*` APIs.
*
* Args: ctx: a secp256k1 context object (can be initialized for none).
* batch: a secp256k1 batch object that contains a set of schnorrsigs/tweaks.
*
* You are advised to check if `secp256k1_batch_usable` returns 1 before calling
* any `secp256k1_batch_add_*` API. We recommend this because `secp256k1_batch_add_*`
* will fail in two cases:
* - case 1: unparsable input (schnorrsig or tweak check)
* - case 2: unusable (or invalid) batch
* Calling `secp256k1_batch_usable` beforehand helps eliminate case 2 if
* `secp256k1_batch_add_*` fails.
*
* If you ignore the above advice, all the secp256k1_batch APIs will still
* work correctly. It simply makes it hard to understand the reason behind
* `secp256k1_batch_add_*` failure (if occurs).
*/
SECP256K1_API int secp256k1_batch_usable(
const secp256k1_context *ctx,
const secp256k1_batch *batch
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2);
/** Verify the set of schnorr signatures or tweaked pubkeys present in the secp256k1_batch.
*
* Returns: 1: every schnorrsig/tweak (in batch) is valid
* 0: atleaset one of the schnorrsig/tweak (in batch) is invalid
*
* In particular, returns 1 if the batch object is empty (does not contain any schnorrsigs/tweaks).
*
* Args: ctx: a secp256k1 context object (can be initialized for none).
* batch: a secp256k1 batch object that contains a set of schnorrsigs/tweaks.
*/
SECP256K1_API int secp256k1_batch_verify(
const secp256k1_context *ctx,
secp256k1_batch *batch
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2);
#ifdef __cplusplus
}
#endif
#endif /* SECP256K1_BATCH_H */

View file

@ -0,0 +1,42 @@
#ifndef SECP256K1_SCHNORRSIG_BATCH_H
#define SECP256K1_SCHNORRSIG_BATCH_H
#include "secp256k1.h"
#include "secp256k1_schnorrsig.h"
#include "secp256k1_batch.h"
#ifdef __cplusplus
extern "C" {
#endif
/** This header file implements batch verification functionality for Schnorr
* signature (see include/secp256k1_schnorrsig.h).
*/
/** Adds a Schnorr signature to the batch object (secp256k1_batch)
* defined in the Batch module (see include/secp256k1_batch.h).
*
* Returns: 1: successfully added the signature to the batch
* 0: unparseable signature or unusable batch (according to
* secp256k1_batch_usable).
* Args: ctx: a secp256k1 context object (can be initialized for none).
* batch: a secp256k1 batch object created using `secp256k1_batch_create`.
* In: sig64: pointer to the 64-byte signature to verify.
* msg: the message being verified. Can only be NULL if msglen is 0.
* msglen: length of the message.
* pubkey: pointer to an x-only public key to verify with (cannot be NULL).
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_batch_add_schnorrsig(
const secp256k1_context* ctx,
secp256k1_batch *batch,
const unsigned char *sig64,
const unsigned char *msg,
size_t msglen,
const secp256k1_xonly_pubkey *pubkey
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(6);
#ifdef __cplusplus
}
#endif
#endif /* SECP256K1_SCHNORRSIG_BATCH_H */

View file

@ -0,0 +1,50 @@
#ifndef SECP256K1_TWEAK_CHECK_BATCH_H
#define SECP256K1_TWEAK_CHECK_BATCH_H
#include "secp256k1.h"
#include "secp256k1_extrakeys.h"
#include "secp256k1_batch.h"
#ifdef __cplusplus
extern "C" {
#endif
/** This header file implements batch verification functionality for
* x-only tweaked public key check (see include/secp256k1_extrakeys.h).
*/
/** Adds a x-only tweaked pubkey check to the batch object (secp256k1_batch)
* defined in the Batch module (see include/secp256k1_batch.h).
*
* The tweaked pubkey is represented by its 32-byte x-only serialization and
* its pk_parity, which can both be obtained by converting the result of
* tweak_add to a secp256k1_xonly_pubkey.
*
* Returns: 1: successfully added the tweaked pubkey check to the batch
* 0: unparseable tweaked pubkey check or unusable batch (according to
* secp256k1_batch_usable).
* Args: ctx: pointer to a context object initialized for verification.
* batch: a secp256k1 batch object created using `secp256k1_batch_create`.
* In: tweaked_pubkey32: pointer to a serialized xonly_pubkey.
* tweaked_pk_parity: the parity of the tweaked pubkey (whose serialization
* is passed in as tweaked_pubkey32). This must match the
* pk_parity value that is returned when calling
* secp256k1_xonly_pubkey_from_pubkey with the tweaked pubkey, or
* the final secp256k1_batch_verify on this batch will fail.
* internal_pubkey: pointer to an x-only public key object to apply the tweak to.
* tweak32: pointer to a 32-byte tweak.
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_batch_add_xonlypub_tweak_check(
const secp256k1_context* ctx,
secp256k1_batch *batch,
const unsigned char *tweaked_pubkey32,
int tweaked_pk_parity,
const secp256k1_xonly_pubkey *internal_pubkey,
const unsigned char *tweak32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6);
#ifdef __cplusplus
}
#endif
#endif /* SECP256K1_TWEAK_CHECK_BATCH_H */

View file

@ -132,6 +132,11 @@ if(SECP256K1_INSTALL)
if(SECP256K1_ENABLE_MODULE_SCHNORRSIG)
list(APPEND ${PROJECT_NAME}_headers "${PROJECT_SOURCE_DIR}/include/secp256k1_schnorrsig.h")
endif()
if(SECP256K1_ENABLE_MODULE_BATCH)
list(APPEND ${PROJECT_NAME}_headers "${PROJECT_SOURCE_DIR}/include/secp256k1_batch.h")
list(APPEND ${PROJECT_NAME}_headers "${PROJECT_SOURCE_DIR}/include/secp256k1_schnorrsig_batch.h")
list(APPEND ${PROJECT_NAME}_headers "${PROJECT_SOURCE_DIR}/include/secp256k1_tweak_check_batch.h")
endif()
if(SECP256K1_ENABLE_MODULE_MUSIG)
list(APPEND ${PROJECT_NAME}_headers "${PROJECT_SOURCE_DIR}/include/secp256k1_musig.h")
endif()

View file

@ -42,17 +42,27 @@ static void help(int default_iters) {
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");
# ifdef ENABLE_MODULE_BATCH
printf(" schnorrsig_batch_verify : Batch verification of Schnorr signatures\n");
# endif
#endif
#ifdef ENABLE_MODULE_EXTRAKEYS
printf(" tweak_add_check : Checks if tweaked x-only pubkey is valid\n");
# ifdef ENABLE_MODULE_BATCH
printf(" tweak_check_batch_verify : Batch verification of tweaked x-only pubkeys check\n");
# endif
#endif
#ifdef ENABLE_MODULE_ELLSWIFT
@ -157,6 +167,10 @@ static void bench_keygen_run(void *arg, int iters) {
# include "modules/recovery/bench_impl.h"
#endif
#ifdef ENABLE_MODULE_EXTRAKEYS
# include "modules/extrakeys/bench_impl.h"
#endif
#ifdef ENABLE_MODULE_SCHNORRSIG
# include "modules/schnorrsig/bench_impl.h"
#endif
@ -179,7 +193,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",
"batch_verify", "schnorrsig_batch_verify", "extrakeys", "tweak_add_check", "tweak_check_batch_verify"};
size_t valid_args_size = sizeof(valid_args)/sizeof(valid_args[0]);
int invalid_args = have_invalid_args(argc, argv, valid_args, valid_args_size);
@ -221,6 +236,14 @@ int main(int argc, char** argv) {
}
#endif
#ifndef ENABLE_MODULE_BATCH
if (have_flag(argc, argv, "batch_verify") || have_flag(argc, argv, "schnorrsig_batch_verify") || have_flag(argc, argv, "tweak_check_batch_verify")) {
fprintf(stderr, "./bench: Schnorr signatures module not enabled.\n");
fprintf(stderr, "Use ./configure --enable-module-schnorrsig.\n\n");
return 1;
}
#endif
#ifndef ENABLE_MODULE_ELLSWIFT
if (have_flag(argc, argv, "ellswift") || have_flag(argc, argv, "ellswift_encode") || have_flag(argc, argv, "ellswift_decode") ||
have_flag(argc, argv, "encode") || have_flag(argc, argv, "decode") || have_flag(argc, argv, "ellswift_keygen") ||
@ -265,6 +288,11 @@ int main(int argc, char** argv) {
run_recovery_bench(iters, argc, argv);
#endif
#ifdef ENABLE_MODULE_EXTRAKEYS
/* Extrakeys benchmarks */
run_extrakeys_bench(iters, argc, argv);
#endif
#ifdef ENABLE_MODULE_SCHNORRSIG
/* Schnorr signature benchmarks */
run_schnorrsig_bench(iters, argc, argv);

View file

@ -120,7 +120,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("%-35s, ", name);
print_number(min * FP_MULT / iter);
printf(" , ");
print_number(((sum * FP_MULT) / count) / iter);
@ -181,7 +181,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("%-35s,%-15s,%-15s,%-15s\n", bench_str, min_str, avg_str, max_str);
printf("\n");
}

View file

@ -358,16 +358,27 @@ static void secp256k1_ecmult(secp256k1_gej *r, const secp256k1_gej *a, const sec
secp256k1_ecmult_strauss_wnaf(&state, r, 1, a, na, ng);
}
static size_t secp256k1_strauss_scratch_size(size_t n_points) {
static const size_t point_size = (sizeof(secp256k1_ge) + sizeof(secp256k1_fe)) * ECMULT_TABLE_SIZE(WINDOW_A) + sizeof(struct secp256k1_strauss_point_state) + sizeof(secp256k1_gej) + sizeof(secp256k1_scalar);
return n_points*point_size;
/** Allocate strauss state on the scratch space */
static int secp256k1_strauss_scratch_alloc_state(const secp256k1_callback* error_callback, secp256k1_scratch *scratch, struct secp256k1_strauss_state *state, size_t n_points) {
const size_t scratch_checkpoint = secp256k1_scratch_checkpoint(error_callback, scratch);
/* We allocate three objects on the scratch space. If these allocations
* change, make sure to check if this affects STRAUSS_SCRATCH_OBJECTS
* constant and strauss_scratch_size. */
state->aux = (secp256k1_fe*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_fe));
state->pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge));
state->ps = (struct secp256k1_strauss_point_state*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(struct secp256k1_strauss_point_state));
if (state->aux == NULL || state->pre_a == NULL || state->ps == NULL) {
secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
return 0;
}
return 1;
}
static int secp256k1_ecmult_strauss_batch(const secp256k1_callback* error_callback, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points, size_t cb_offset) {
secp256k1_gej* points;
secp256k1_scalar* scalars;
/** Run ecmult_strauss_wnaf on the given points and scalars */
static int secp256k1_ecmult_strauss_batch_internal(const secp256k1_callback* error_callback, secp256k1_scratch *scratch, secp256k1_gej *r, secp256k1_scalar *scalars, secp256k1_gej *points, const secp256k1_scalar *inp_g_sc, size_t n_points) {
struct secp256k1_strauss_state state;
size_t i;
const size_t scratch_checkpoint = secp256k1_scratch_checkpoint(error_callback, scratch);
secp256k1_gej_set_infinity(r);
@ -375,16 +386,30 @@ static int secp256k1_ecmult_strauss_batch(const secp256k1_callback* error_callba
return 1;
}
/* We allocate STRAUSS_SCRATCH_OBJECTS objects on the scratch space. If these
* allocations change, make sure to update the STRAUSS_SCRATCH_OBJECTS
* constant and strauss_scratch_size accordingly. */
if(!secp256k1_strauss_scratch_alloc_state(error_callback, scratch, &state, n_points)) {
return 0;
}
secp256k1_ecmult_strauss_wnaf(&state, r, n_points, points, scalars, inp_g_sc);
secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
return 1;
}
/** Run ecmult_strauss_wnaf on the given points and scalars. Returns 0 if the
* scratch space is empty. `n_points` number of scalars and points are
* extracted from `cbdata` using `cb` and stored on the scratch space.
*/
static int secp256k1_ecmult_strauss_batch(const secp256k1_callback* error_callback, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points, size_t cb_offset) {
secp256k1_gej* points;
secp256k1_scalar* scalars;
size_t i;
const size_t scratch_checkpoint = secp256k1_scratch_checkpoint(error_callback, scratch);
/* We allocate STRAUSS_SCRATCH_OBJECTS objects on the scratch space in
* total. If these allocations change, make sure to update the
* STRAUSS_SCRATCH_OBJECTS constant and strauss_scratch_size accordingly. */
points = (secp256k1_gej*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(secp256k1_gej));
scalars = (secp256k1_scalar*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(secp256k1_scalar));
state.aux = (secp256k1_fe*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_fe));
state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge));
state.ps = (struct secp256k1_strauss_point_state*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(struct secp256k1_strauss_point_state));
if (points == NULL || scalars == NULL || state.aux == NULL || state.pre_a == NULL || state.ps == NULL) {
if (points == NULL || scalars == NULL) {
secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
return 0;
}
@ -397,20 +422,30 @@ static int secp256k1_ecmult_strauss_batch(const secp256k1_callback* error_callba
}
secp256k1_gej_set_ge(&points[i], &point);
}
secp256k1_ecmult_strauss_wnaf(&state, r, n_points, points, scalars, inp_g_sc);
secp256k1_ecmult_strauss_batch_internal(error_callback, scratch, r, scalars, points, inp_g_sc, n_points);
secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
return 1;
}
/** Return the scratch size that is allocated by a call to strauss_batch
* (ignoring padding required for alignment). */
static size_t secp256k1_strauss_scratch_size(size_t n_points) {
static const size_t point_size = (sizeof(secp256k1_ge) + sizeof(secp256k1_fe)) * ECMULT_TABLE_SIZE(WINDOW_A) + sizeof(struct secp256k1_strauss_point_state) + sizeof(secp256k1_gej) + sizeof(secp256k1_scalar);
return n_points*point_size;
}
/** Return the maximum number of points that can be provided to strauss_batch
* with a given scratch space. */
static size_t secp256k1_strauss_max_points(const secp256k1_callback* error_callback, secp256k1_scratch *scratch) {
return secp256k1_scratch_max_allocation(error_callback, scratch, STRAUSS_SCRATCH_OBJECTS) / secp256k1_strauss_scratch_size(1);
}
/* Wrapper for secp256k1_ecmult_multi_func interface */
static int secp256k1_ecmult_strauss_batch_single(const secp256k1_callback* error_callback, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) {
return secp256k1_ecmult_strauss_batch(error_callback, scratch, r, inp_g_sc, cb, cbdata, n, 0);
}
static size_t secp256k1_strauss_max_points(const secp256k1_callback* error_callback, secp256k1_scratch *scratch) {
return secp256k1_scratch_max_allocation(error_callback, scratch, STRAUSS_SCRATCH_OBJECTS) / secp256k1_strauss_scratch_size(1);
}
/** Convert a number to WNAF notation.
* The number becomes represented by sum(2^{wi} * wnaf[i], i=0..WNAF_SIZE(w)+1) - return_val.
* It has the following guarantees:

View file

@ -0,0 +1,3 @@
include_HEADERS += include/secp256k1_batch.h
noinst_HEADERS += src/modules/batch/main_impl.h
noinst_HEADERS += src/modules/batch/tests_impl.h

View file

@ -0,0 +1,207 @@
#ifndef SECP256K1_MODULE_BATCH_MAIN_H
#define SECP256K1_MODULE_BATCH_MAIN_H
#include "../../../include/secp256k1_batch.h"
/* Maximum number of scalar-point pairs on the batch
* for which `secp256k1_batch_verify` remains efficient */
#define STRAUSS_MAX_TERMS_PER_BATCH 106
/* Assume two batch objects (batch1 and batch2) and we call
* `batch_add_tweak_check` on batch1 and `batch_add_schnorrsig` on batch2.
* In this case, the same randomizer will be generated if the input bytes to
* batch1 and batch2 are the same (even though we use different `batch_add_` funcs).
* Including this tag during randomizer generation (to differentiate btw
* `batch_add_` funcs) will prevent such mishaps. */
enum batch_add_type {schnorrsig = 1, tweak_check = 2};
/** Opaque data structure that holds information required for the batch verification.
*
* Members:
* data: scratch space object that contains points (_gej) and their
* respective scalars. To be used in Multi-Scalar Multiplication
* algorithms such as Strauss and Pippenger.
* scalars: pointer to scalars allocated on the scratch space.
* points: pointer to points allocated on the scratch space.
* sc_g: scalar corresponding to the generator point (G) in Multi-Scalar
* Multiplication equation.
* sha256: contains hash of all the inputs (schnorrsig/tweaks) present in
* the batch object, expect the first input. Used for generating a random secp256k1_scalar
* for each term added by secp256k1_batch_add_*.
* sha256: contains hash of all inputs (except the first one) present in the batch.
* `secp256k1_batch_add_` APIs use these for randomizing the scalar (i.e., multiplying
* it with a newly generated scalar) before adding it to the batch.
* len: number of scalar-point pairs present in the batch.
* capacity: max number of scalar-point pairs that the batch can hold.
* result: tells whether the given set of inputs (schnorrsigs or tweak checks) is valid
* or invalid. 1 = valid and 0 = invalid. By default, this is set to 1
* during batch object creation (i.e., `secp256k1_batch_create`).
*
* The following struct name is typdef as secp256k1_batch (in include/secp256k1_batch.h).
*/
struct secp256k1_batch_struct{
secp256k1_scratch *data;
secp256k1_scalar *scalars;
secp256k1_gej *points;
secp256k1_scalar sc_g;
secp256k1_sha256 sha256;
size_t len;
size_t capacity;
int result;
};
static size_t secp256k1_batch_scratch_size(int max_terms) {
size_t ret = secp256k1_strauss_scratch_size(max_terms) + STRAUSS_SCRATCH_OBJECTS*16;
VERIFY_CHECK(ret != 0);
return ret;
}
/** Clears the scalar and points allocated on the batch object's scratch space */
static void secp256k1_batch_scratch_clear(secp256k1_batch* batch) {
secp256k1_scalar_clear(&batch->sc_g);
/* setting the len = 0 will suffice (instead of clearing the memory)
* since, there are no secrets stored on the scratch space */
batch->len = 0;
}
/** Allocates space for `batch->capacity` number of scalars and points on batch
* object's scratch space */
static int secp256k1_batch_scratch_alloc(const secp256k1_callback* error_callback, secp256k1_batch* batch) {
size_t checkpoint = secp256k1_scratch_checkpoint(error_callback, batch->data);
size_t count = batch->capacity;
VERIFY_CHECK(count > 0);
batch->scalars = (secp256k1_scalar*)secp256k1_scratch_alloc(error_callback, batch->data, count*sizeof(secp256k1_scalar));
batch->points = (secp256k1_gej*)secp256k1_scratch_alloc(error_callback, batch->data, count*sizeof(secp256k1_gej));
/* If scalar or point allocation fails, restore scratch space to previous state */
if (batch->scalars == NULL || batch->points == NULL) {
secp256k1_scratch_apply_checkpoint(error_callback, batch->data, checkpoint);
return 0;
}
return 1;
}
/* Initializes SHA256 with fixed midstate. This midstate was computed by applying
* SHA256 to SHA256("BIP0340/batch")||SHA256("BIP0340/batch"). */
static void secp256k1_batch_sha256_tagged(secp256k1_sha256 *sha) {
secp256k1_sha256_initialize(sha);
sha->s[0] = 0x79e3e0d2ul;
sha->s[1] = 0x12284f32ul;
sha->s[2] = 0xd7d89e1cul;
sha->s[3] = 0x6491ea9aul;
sha->s[4] = 0xad823b2ful;
sha->s[5] = 0xfacfe0b6ul;
sha->s[6] = 0x342b78baul;
sha->s[7] = 0x12ece87cul;
sha->bytes = 64;
}
secp256k1_batch* secp256k1_batch_create(const secp256k1_context* ctx, size_t max_terms, const unsigned char *aux_rand16) {
size_t batch_size;
secp256k1_batch* batch;
size_t batch_scratch_size;
unsigned char zeros[16] = {0};
/* max number of scalar-point pairs on scratch up to which Strauss multi multiplication is efficient */
if (max_terms > STRAUSS_MAX_TERMS_PER_BATCH) {
max_terms = STRAUSS_MAX_TERMS_PER_BATCH;
}
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(max_terms != 0);
batch_size = sizeof(secp256k1_batch);
batch = (secp256k1_batch *)checked_malloc(&ctx->error_callback, batch_size);
batch_scratch_size = secp256k1_batch_scratch_size(max_terms);
if (batch != NULL) {
/* create scratch space inside batch object, if that fails return NULL*/
batch->data = secp256k1_scratch_create(&ctx->error_callback, batch_scratch_size);
if (batch->data == NULL) {
return NULL;
}
/* allocate memeory for `max_terms` number of scalars and points on scratch space */
batch->capacity = max_terms;
if (!secp256k1_batch_scratch_alloc(&ctx->error_callback, batch)) {
/* if scratch memory allocation fails, free all the previous the allocated memory
and return NULL */
secp256k1_scratch_destroy(&ctx->error_callback, batch->data);
free(batch);
return NULL;
}
/* set remaining data members */
secp256k1_scalar_clear(&batch->sc_g);
secp256k1_batch_sha256_tagged(&batch->sha256);
if (aux_rand16 != NULL) {
secp256k1_sha256_write(&batch->sha256, aux_rand16, 16);
} else {
/* use 16 bytes of 0x0000...000, if no fresh randomness provided */
secp256k1_sha256_write(&batch->sha256, zeros, 16);
}
batch->len = 0;
batch->result = 1;
}
return batch;
}
void secp256k1_batch_destroy(const secp256k1_context *ctx, secp256k1_batch *batch) {
VERIFY_CHECK(ctx != NULL);
if (batch != NULL) {
if(batch->data != NULL) {
/* can't destroy a scratch space with non-zero size */
secp256k1_scratch_apply_checkpoint(&ctx->error_callback, batch->data, 0);
secp256k1_scratch_destroy(&ctx->error_callback, batch->data);
}
free(batch);
}
}
int secp256k1_batch_usable(const secp256k1_context *ctx, const secp256k1_batch *batch) {
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(batch != NULL);
return batch->result;
}
/** verifies the inputs (schnorrsig or tweak_check) by performing multi-scalar point
* multiplication on the scalars (`batch->scalars`) and points (`batch->points`)
* present in the batch. Uses `secp256k1_ecmult_strauss_batch_internal` to perform
* the multi-multiplication.
*
* Fails if:
* 0 != -(s1 + a2*s2 + ... + au*su)G
* + R1 + a2*R2 + ... + au*Ru + e1*P1 + (a2*e2)P2 + ... + (au*eu)Pu.
*/
int secp256k1_batch_verify(const secp256k1_context *ctx, secp256k1_batch *batch) {
secp256k1_gej resj;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(batch != NULL);
if(batch->result == 0) {
return 0;
}
if (batch->len > 0) {
int strauss_ret = secp256k1_ecmult_strauss_batch_internal(&ctx->error_callback, batch->data, &resj, batch->scalars, batch->points, &batch->sc_g, batch->len);
(void)strauss_ret;
int mid_res = secp256k1_gej_is_infinity(&resj);
/* `_strauss_batch_internal` should not fail due to insufficient memory.
* `batch_create` will allocate memeory needed by `_strauss_batch_internal`. */
VERIFY_CHECK(strauss_ret != 0);
batch->result = batch->result && mid_res;
secp256k1_batch_scratch_clear(batch);
}
return batch->result;
}
#endif /* SECP256K1_MODULE_BATCH_MAIN_H */

View file

@ -0,0 +1,214 @@
#ifndef SECP256K1_MODULE_BATCH_TESTS_H
#define SECP256K1_MODULE_BATCH_TESTS_H
#include "../../../include/secp256k1_batch.h"
#ifdef ENABLE_MODULE_SCHNORRSIG
#include "../../../include/secp256k1_schnorrsig.h"
#include "../../../include/secp256k1_schnorrsig_batch.h"
#endif
#ifdef ENABLE_MODULE_EXTRAKEYS
#include "../../../include/secp256k1_extrakeys.h"
#include "../../../include/secp256k1_tweak_check_batch.h"
#endif
/* Tests for the equality of two sha256 structs. This function only produces a
* correct result if an integer multiple of 64 many bytes have been written
* into the hash functions. */
void test_batch_sha256_eq(const secp256k1_sha256 *sha1, const secp256k1_sha256 *sha2) {
/* Is buffer fully consumed? */
CHECK((sha1->bytes & 0x3F) == 0);
CHECK(sha1->bytes == sha2->bytes);
CHECK(secp256k1_memcmp_var(sha1->s, sha2->s, sizeof(sha1->s)) == 0);
}
/* Checks that hash initialized by secp256k1_batch_sha256_tagged has the
* expected state. */
void test_batch_sha256_tagged(void) {
unsigned char tag[13] = "BIP0340/batch";
secp256k1_sha256 sha;
secp256k1_sha256 sha_optimized;
secp256k1_sha256_initialize_tagged(&sha, (unsigned char *) tag, sizeof(tag));
secp256k1_batch_sha256_tagged(&sha_optimized);
test_batch_sha256_eq(&sha, &sha_optimized);
}
#define N_SIGS 10
#define N_TWK_CHECKS 10
#define N_TERMS (N_TWK_CHECKS + 2*N_SIGS)
void test_batch_api(void) {
secp256k1_batch *batch_none;
secp256k1_batch *batch_sign;
secp256k1_batch *batch_vrfy;
secp256k1_batch *batch_both;
secp256k1_batch *batch_sttc;
unsigned char aux_rand16[32];
int ecount;
#ifdef ENABLE_MODULE_EXTRAKEYS
unsigned char sk[32];
secp256k1_keypair keypair;
secp256k1_xonly_pubkey pk;
/* xonly pubkey tweak checks data */
unsigned char tweaked_pk[N_TWK_CHECKS][32];
int tweaked_pk_parity[N_TWK_CHECKS];
unsigned char tweak[N_TWK_CHECKS][32];
secp256k1_pubkey tmp_pk;
secp256k1_xonly_pubkey tmp_xonly_pk;
size_t i;
#endif
#ifdef ENABLE_MODULE_SCHNORRSIG
/* schnorr verification data */
unsigned char msg[N_SIGS][32];
unsigned char sig[N_SIGS][64];
#endif
/* context and batch setup */
secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY);
secp256k1_context *both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
secp256k1_context *sttc = malloc(sizeof(*secp256k1_context_no_precomp));
memcpy(sttc, secp256k1_context_no_precomp, sizeof(secp256k1_context));
secp256k1_context_set_error_callback(none, counting_callback_fn, &ecount);
secp256k1_context_set_error_callback(sign, counting_callback_fn, &ecount);
secp256k1_context_set_error_callback(vrfy, counting_callback_fn, &ecount);
secp256k1_context_set_error_callback(both, counting_callback_fn, &ecount);
secp256k1_context_set_error_callback(sttc, counting_callback_fn, &ecount);
secp256k1_context_set_illegal_callback(none, counting_callback_fn, &ecount);
secp256k1_context_set_illegal_callback(sign, counting_callback_fn, &ecount);
secp256k1_context_set_illegal_callback(vrfy, counting_callback_fn, &ecount);
secp256k1_context_set_illegal_callback(both, counting_callback_fn, &ecount);
secp256k1_context_set_illegal_callback(sttc, counting_callback_fn, &ecount);
/* 16 byte auxiliary randomness */
testrand256(aux_rand16);
memset(&aux_rand16[16], 0, 16);
#ifdef ENABLE_MODULE_EXTRAKEYS
/* generate keypair data */
testrand256(sk);
CHECK(secp256k1_keypair_create(sign, &keypair, sk) == 1);
CHECK(secp256k1_keypair_xonly_pub(sign, &pk, NULL, &keypair) == 1);
/* generate N_TWK_CHECKS tweak check data (tweaked_pk, tweaked_pk_parity, tweak) */
for (i = 0; i < N_TWK_CHECKS; i++) {
testrand256(tweak[i]);
CHECK(secp256k1_xonly_pubkey_tweak_add(vrfy, &tmp_pk, &pk, tweak[i]));
CHECK(secp256k1_xonly_pubkey_from_pubkey(vrfy, &tmp_xonly_pk, &tweaked_pk_parity[i], &tmp_pk));
CHECK(secp256k1_xonly_pubkey_serialize(vrfy, tweaked_pk[i], &tmp_xonly_pk));
CHECK(secp256k1_xonly_pubkey_tweak_add_check(vrfy, tweaked_pk[i], tweaked_pk_parity[i], &pk, tweak[i]));
}
#endif
#ifdef ENABLE_MODULE_SCHNORRSIG
/* generate N_SIGS schnorr verify data (msg, sig) */
for (i = 0; i < N_SIGS; i++) {
testrand256(msg[i]);
CHECK(secp256k1_schnorrsig_sign32(sign, sig[i], msg[i], &keypair, NULL) == 1);
CHECK(secp256k1_schnorrsig_verify(vrfy, sig[i], msg[i], sizeof(msg[i]), &pk));
}
#endif
/** main test body **/
/* batch_create tests */
ecount = 0;
batch_none = secp256k1_batch_create(none, 1, NULL);
CHECK(batch_none != NULL);
CHECK(ecount == 0);
/* 2*N_SIGS since one schnorrsig creates two scalar-point pair in batch */
batch_sign = secp256k1_batch_create(sign, 2*N_SIGS, NULL);
CHECK(batch_sign != NULL);
CHECK(ecount == 0);
batch_vrfy = secp256k1_batch_create(vrfy, N_TWK_CHECKS - 1, aux_rand16);
CHECK(batch_vrfy != NULL);
CHECK(ecount == 0);
batch_both = secp256k1_batch_create(both, N_TERMS/4, aux_rand16);
CHECK(batch_both != NULL);
CHECK(ecount == 0);
/* ARG_CHECK(max_terms != 0) in `batch_create` should fail*/
batch_sttc = secp256k1_batch_create(sttc, 0, NULL);
CHECK(batch_sttc == NULL);
CHECK(ecount == 1);
#ifdef ENABLE_MODULE_SCHNORRSIG
ecount = 0;
for (i = 0; i < N_SIGS; i++) {
CHECK(secp256k1_batch_usable(sign, batch_sign) == 1);
CHECK(ecount == 0);
CHECK(secp256k1_batch_add_schnorrsig(sign, batch_sign, sig[i], msg[i], sizeof(msg[i]), &pk) == 1);
CHECK(ecount == 0);
}
#endif
#ifdef ENABLE_MODULE_EXTRAKEYS
ecount = 0;
for (i = 0; i < N_TWK_CHECKS; i++) {
CHECK(secp256k1_batch_usable(vrfy, batch_vrfy));
CHECK(ecount == 0);
CHECK(secp256k1_batch_add_xonlypub_tweak_check(vrfy, batch_vrfy, tweaked_pk[i], tweaked_pk_parity[i], &pk, tweak[i]));
CHECK(ecount == 0);
}
#endif
#if defined(ENABLE_MODULE_SCHNORRSIG) && defined(ENABLE_MODULE_EXTRAKEYS)
/* secp256k1_batch_add_tests for batch_both */
ecount = 0;
for (i = 0; i < N_SIGS; i++) {
CHECK(secp256k1_batch_usable(both, batch_both) == 1);
CHECK(ecount == 0);
CHECK(secp256k1_batch_add_schnorrsig(both, batch_both, sig[i], msg[i], sizeof(msg[i]), &pk) == 1);
CHECK(ecount == 0);
}
for (i = 0; i < N_TWK_CHECKS; i++) {
CHECK(secp256k1_batch_usable(both, batch_both));
CHECK(ecount == 0);
CHECK(secp256k1_batch_add_xonlypub_tweak_check(both, batch_both, tweaked_pk[i], tweaked_pk_parity[i], &pk, tweak[i]));
CHECK(ecount == 0);
}
#endif
/* batch_verify tests */
ecount = 0;
CHECK(secp256k1_batch_verify(none, batch_none) == 1);
CHECK(ecount == 0);
CHECK(secp256k1_batch_verify(sign, batch_sign) == 1);
CHECK(ecount == 0);
CHECK(secp256k1_batch_verify(vrfy, batch_vrfy) == 1);
CHECK(ecount == 0);
CHECK(secp256k1_batch_verify(both, batch_both) == 1);
CHECK(ecount == 0);
CHECK(secp256k1_batch_verify(sttc, NULL) == 0);
CHECK(ecount == 1);
ecount = 0;
secp256k1_batch_destroy(none, batch_none);
CHECK(ecount == 0);
secp256k1_batch_destroy(sign, batch_sign);
CHECK(ecount == 0);
secp256k1_batch_destroy(vrfy, batch_vrfy);
CHECK(ecount == 0);
secp256k1_batch_destroy(both, batch_both);
CHECK(ecount == 0);
secp256k1_batch_destroy(sttc, NULL);
CHECK(ecount == 0);
secp256k1_context_destroy(none);
secp256k1_context_destroy(sign);
secp256k1_context_destroy(vrfy);
secp256k1_context_destroy(both);
secp256k1_context_destroy(sttc);
}
#undef N_SIGS
#undef N_TWK_CHECKS
#undef N_TERMS
void run_batch_tests(void) {
test_batch_api();
test_batch_sha256_tagged();
}
#endif /* SECP256K1_MODULE_BATCH_TESTS_H */

View file

@ -1,4 +1,11 @@
include_HEADERS += include/secp256k1_extrakeys.h
if ENABLE_MODULE_BATCH
include_HEADERS += include/secp256k1_tweak_check_batch.h
endif
noinst_HEADERS += src/modules/extrakeys/tests_impl.h
noinst_HEADERS += src/modules/extrakeys/tests_exhaustive_impl.h
noinst_HEADERS += src/modules/extrakeys/main_impl.h
if ENABLE_MODULE_BATCH
noinst_HEADERS += src/modules/extrakeys/batch_add_impl.h
noinst_HEADERS += src/modules/extrakeys/batch_add_tests_impl.h
endif

View file

@ -0,0 +1,151 @@
#ifndef SECP256K1_MODULE_EXTRAKEYS_BATCH_ADD_IMPL_H
#define SECP256K1_MODULE_EXTRAKEYS_BATCH_ADD_IMPL_H
#include "../../../include/secp256k1_extrakeys.h"
#include "../../../include/secp256k1_tweak_check_batch.h"
#include "..//batch/main_impl.h"
/* The number of scalar-point pairs allocated on the scratch space
* by `secp256k1_batch_add_xonlypub_tweak_check` */
#define BATCH_TWEAK_CHECK_SCRATCH_OBJS 1
/** Computes a 16-byte deterministic randomizer by
* SHA256(batch_add_tag || tweaked pubkey || parity || tweak || internal pubkey) */
static void secp256k1_batch_xonlypub_tweak_randomizer_gen(unsigned char *randomizer32, secp256k1_sha256 *sha256, const unsigned char *tweaked_pubkey32, const unsigned char *tweaked_pk_parity, const unsigned char *internal_pk33, const unsigned char *tweak32) {
secp256k1_sha256 sha256_cpy;
unsigned char batch_add_type = (unsigned char) tweak_check;
secp256k1_sha256_write(sha256, &batch_add_type, sizeof(batch_add_type));
/* add tweaked pubkey check data to sha object */
secp256k1_sha256_write(sha256, tweaked_pubkey32, 32);
secp256k1_sha256_write(sha256, tweaked_pk_parity, 1);
secp256k1_sha256_write(sha256, tweak32, 32);
secp256k1_sha256_write(sha256, internal_pk33, 33);
/* generate randomizer */
sha256_cpy = *sha256;
secp256k1_sha256_finalize(&sha256_cpy, randomizer32);
/* 16 byte randomizer is sufficient */
memset(randomizer32, 0, 16);
}
static int secp256k1_batch_xonlypub_tweak_randomizer_set(const secp256k1_context* ctx, secp256k1_batch *batch, secp256k1_scalar *r, const unsigned char *tweaked_pubkey32, int tweaked_pk_parity, const secp256k1_xonly_pubkey *internal_pubkey,const unsigned char *tweak32) {
unsigned char randomizer[32];
unsigned char internal_buf[33];
size_t internal_buflen = sizeof(internal_buf);
unsigned char parity = (unsigned char) tweaked_pk_parity;
int overflow;
/* t = 2^127 */
secp256k1_scalar t = SECP256K1_SCALAR_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x80000000, 0x00000000, 0x00000000, 0x00000000);
/* We use compressed serialization here. If we would use
* xonly_pubkey serialization and a user would wrongly memcpy
* normal secp256k1_pubkeys into xonly_pubkeys then the randomizer
* would be the same for two different pubkeys. */
if (!secp256k1_ec_pubkey_serialize(ctx, internal_buf, &internal_buflen, (const secp256k1_pubkey *) internal_pubkey, SECP256K1_EC_COMPRESSED)) {
return 0;
}
secp256k1_batch_xonlypub_tweak_randomizer_gen(randomizer, &batch->sha256, tweaked_pubkey32, &parity, internal_buf, tweak32);
secp256k1_scalar_set_b32(r, randomizer, &overflow);
/* Shift scalar to range [-2^127, 2^127-1] */
secp256k1_scalar_negate(&t, &t);
secp256k1_scalar_add(r, r, &t);
VERIFY_CHECK(overflow == 0);
return 1;
}
/** Adds the given x-only tweaked public key check to the batch.
*
* Updates the batch object by:
* 1. adding the point P-Q to the scratch space
* -> the point is of type `secp256k1_gej`
* 2. adding the scalar ai to the scratch space
* -> ai is the scalar coefficient of P-Q (in multi multiplication)
* 3. incrementing sc_g (scalar of G) by ai.tweak
*
* Conventions used above:
* -> Q (tweaked pubkey) = EC point where parity(y) = tweaked_pk_parity
* and x = tweaked_pubkey32
* -> P (internal pubkey) = internal pubkey
* -> ai (randomizer) = sha256_tagged(batch_add_tag || tweaked_pubkey32 ||
* tweaked_pk_parity || tweak32 || pubkey)
* -> tweak (challenge) = tweak32
*
* This function is based on `secp256k1_xonly_pubkey_tweak_add_check`.
*/
int secp256k1_batch_add_xonlypub_tweak_check(const secp256k1_context* ctx, secp256k1_batch *batch, const unsigned char *tweaked_pubkey32, int tweaked_pk_parity, const secp256k1_xonly_pubkey *internal_pubkey,const unsigned char *tweak32) {
secp256k1_scalar tweak;
secp256k1_scalar ai;
secp256k1_ge pk;
secp256k1_ge q;
secp256k1_gej tmpj;
secp256k1_fe qx;
int overflow;
size_t i;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(batch != NULL);
ARG_CHECK(internal_pubkey != NULL);
ARG_CHECK(tweaked_pubkey32 != NULL);
ARG_CHECK(tweak32 != NULL);
if(batch->result == 0) {
return 0;
}
if (!secp256k1_fe_set_b32_limit(&qx, tweaked_pubkey32)) {
return 0;
}
secp256k1_scalar_set_b32(&tweak, tweak32, &overflow);
if (overflow) {
return 0;
}
if (!secp256k1_xonly_pubkey_load(ctx, &pk, internal_pubkey)) {
return 0;
}
/* if insufficient space in batch, verify the inputs (stored in curr batch) and
* save the result. This extends the batch capacity since `secp256k1_batch_verify`
* clears the batch after verification. */
if (batch->capacity - batch->len < BATCH_TWEAK_CHECK_SCRATCH_OBJS) {
secp256k1_batch_verify(ctx, batch);
}
i = batch->len;
/* append point P-Q to the scratch space */
if (!secp256k1_ge_set_xo_var(&q, &qx, tweaked_pk_parity)) {
return 0;
}
if (!secp256k1_ge_is_in_correct_subgroup(&q)) {
return 0;
}
secp256k1_ge_neg(&q, &q);
secp256k1_gej_set_ge(&tmpj, &q);
secp256k1_gej_add_ge_var(&tmpj, &tmpj, &pk, NULL);
batch->points[i] = tmpj;
/* Compute ai (randomizer) */
if (batch->len == 0) {
/* set randomizer as 1 for the first term in batch */
ai = secp256k1_scalar_one;
} else if(!secp256k1_batch_xonlypub_tweak_randomizer_set(ctx, batch, &ai, tweaked_pubkey32, tweaked_pk_parity, internal_pubkey, tweak32)) {
return 0;
}
/* append scalar ai to scratch space */
batch->scalars[i] = ai;
/* increment scalar of G by ai.tweak */
secp256k1_scalar_mul(&tweak, &tweak, &ai);
secp256k1_scalar_add(&batch->sc_g, &batch->sc_g, &tweak);
batch->len += 1;
return 1;
}
#endif /* SECP256K1_MODULE_EXTRAKEYS_BATCH_ADD_IMPL_H */

View file

@ -0,0 +1,165 @@
#ifndef SECP256K1_MODULE_EXTRAKEYS_BATCH_ADD_TESTS_IMPL_H
#define SECP256K1_MODULE_EXTRAKEYS_BATCH_ADD_TESTS_IMPL_H
#include "../../../include/secp256k1_extrakeys.h"
#include "../../../include/secp256k1_batch.h"
#include "../../../include/secp256k1_tweak_check_batch.h"
/* Checks that a bit flip in the n_flip-th argument (that has n_bytes many
* bytes) changes the hash function */
void batch_xonlypub_tweak_randomizer_gen_bitflip(secp256k1_sha256 *sha, unsigned char **args, size_t n_flip, size_t n_bytes) {
unsigned char randomizers[2][32];
secp256k1_sha256 sha_cpy;
sha_cpy = *sha;
secp256k1_batch_xonlypub_tweak_randomizer_gen(randomizers[0], &sha_cpy, args[0], args[1], args[2], args[3]);
testrand_flip(args[n_flip], n_bytes);
sha_cpy = *sha;
secp256k1_batch_xonlypub_tweak_randomizer_gen(randomizers[1], &sha_cpy, args[0], args[1], args[2], args[3]);
CHECK(secp256k1_memcmp_var(randomizers[0], randomizers[1], 32) != 0);
}
void run_batch_xonlypub_tweak_randomizer_gen_tests(void) {
secp256k1_sha256 sha;
size_t n_checks = 20;
unsigned char tweaked_pk[32];
unsigned char tweaked_pk_parity;
unsigned char tweak[32];
unsigned char internal_pk[33];
unsigned char *args[4];
size_t i; /* loops through n_checks */
int j; /* loops through count */
secp256k1_batch_sha256_tagged(&sha);
for (i = 0; i < n_checks; i++) {
uint8_t temp_rand;
/* generate i-th tweak check data */
testrand256(tweaked_pk);
tweaked_pk_parity = (unsigned char) testrand_int(2);
testrand256(tweak);
testrand256(&internal_pk[1]);
temp_rand = testrand_int(2) + 2; /* randomly choose 2 or 3 */
internal_pk[0] = (unsigned char)temp_rand;
/* check bitflip in any argument results in generates randomizers */
args[0] = tweaked_pk;
args[1] = &tweaked_pk_parity;
args[2] = internal_pk;
args[3] = tweak;
for (j = 0; j < COUNT; j++) {
batch_xonlypub_tweak_randomizer_gen_bitflip(&sha, args, 0, 32);
batch_xonlypub_tweak_randomizer_gen_bitflip(&sha, args, 1, 1);
batch_xonlypub_tweak_randomizer_gen_bitflip(&sha, args, 2, 33);
batch_xonlypub_tweak_randomizer_gen_bitflip(&sha, args, 3, 32);
}
/* write i-th tweak check data to the sha object
* this is required for generating the next randomizer */
secp256k1_sha256_write(&sha, tweaked_pk, 32);
secp256k1_sha256_write(&sha, &tweaked_pk_parity, 1);
secp256k1_sha256_write(&sha, tweak, 32);
secp256k1_sha256_write(&sha, internal_pk, 33);
}
}
void test_batch_add_xonlypub_tweak_api(void) {
unsigned char sk[32];
secp256k1_keypair keypair;
secp256k1_xonly_pubkey pk;
/* xonly pubkey tweak checks data */
unsigned char tweaked_pk[32];
int tweaked_pk_parity;
unsigned char tweak[32];
secp256k1_pubkey tmp_pk;
secp256k1_xonly_pubkey tmp_xonly_pk;
unsigned char overflows[32];
/** setup **/
secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY);
secp256k1_batch *batch1 = secp256k1_batch_create(none, 1, NULL);
/* batch2 is used when batch_add_xonlypub_tweak is expected to fail */
secp256k1_batch *batch2 = secp256k1_batch_create(none, 1, NULL);
int ecount;
secp256k1_context_set_error_callback(none, counting_callback_fn, &ecount);
secp256k1_context_set_error_callback(sign, counting_callback_fn, &ecount);
secp256k1_context_set_error_callback(vrfy, counting_callback_fn, &ecount);
secp256k1_context_set_illegal_callback(none, counting_callback_fn, &ecount);
secp256k1_context_set_illegal_callback(sign, counting_callback_fn, &ecount);
secp256k1_context_set_illegal_callback(vrfy, counting_callback_fn, &ecount);
/** generate keypair data **/
testrand256(sk);
CHECK(secp256k1_keypair_create(sign, &keypair, sk) == 1);
CHECK(secp256k1_keypair_xonly_pub(sign, &pk, NULL, &keypair) == 1);
memset(overflows, 0xFF, sizeof(overflows));
/** generate tweak check data (tweaked_pk, tweaked_pk_parity, tweak) **/
testrand256(tweak);
CHECK(secp256k1_xonly_pubkey_tweak_add(vrfy, &tmp_pk, &pk, tweak));
CHECK(secp256k1_xonly_pubkey_from_pubkey(vrfy, &tmp_xonly_pk, &tweaked_pk_parity, &tmp_pk));
CHECK(secp256k1_xonly_pubkey_serialize(vrfy, tweaked_pk, &tmp_xonly_pk));
CHECK(secp256k1_xonly_pubkey_tweak_add_check(vrfy, tweaked_pk, tweaked_pk_parity, &pk, tweak));
CHECK(batch1 != NULL);
CHECK(batch2 != NULL);
/** main test body **/
ecount = 0;
CHECK(secp256k1_batch_add_xonlypub_tweak_check(none, batch1, tweaked_pk, tweaked_pk_parity, &pk, tweak) == 1);
CHECK(ecount == 0);
CHECK(secp256k1_batch_verify(none, batch1) == 1);
CHECK(ecount == 0);
CHECK(secp256k1_batch_add_xonlypub_tweak_check(none, batch2, NULL, tweaked_pk_parity, &pk, tweak) == 0);
CHECK(ecount == 1);
CHECK(secp256k1_batch_add_xonlypub_tweak_check(none, batch2, tweaked_pk, tweaked_pk_parity, NULL, tweak) == 0);
CHECK(ecount == 2);
CHECK(secp256k1_batch_add_xonlypub_tweak_check(none, batch2, tweaked_pk, tweaked_pk_parity, &pk, NULL) == 0);
CHECK(ecount == 3);
CHECK(secp256k1_batch_add_xonlypub_tweak_check(none, NULL, tweaked_pk, tweaked_pk_parity, &pk, tweak) == 0);
CHECK(ecount == 4);
/** overflowing tweak not allowed **/
CHECK(secp256k1_batch_add_xonlypub_tweak_check(none, batch2, tweaked_pk, tweaked_pk_parity, &pk, overflows) == 0);
CHECK(ecount == 4);
/** x-coordinate of tweaked pubkey should be less than prime order **/
CHECK(secp256k1_batch_add_xonlypub_tweak_check(none, batch2, overflows, tweaked_pk_parity, &pk, tweak) == 0);
CHECK(ecount == 4);
/** batch_verify should fail for incorrect tweak **/
ecount = 0;
CHECK(secp256k1_batch_usable(none, batch2));
CHECK(secp256k1_batch_add_xonlypub_tweak_check(none, batch2, tweaked_pk, !tweaked_pk_parity, &pk, tweak) == 1);
CHECK(ecount == 0);
CHECK(secp256k1_batch_verify(none, batch2) == 0);
CHECK(ecount == 0);
/** batch_add_ should ignore unusable batch object (i.e, batch->result = 0) **/
ecount = 0;
CHECK(secp256k1_batch_usable(none, batch2) == 0);
CHECK(ecount == 0);
CHECK(secp256k1_batch_add_xonlypub_tweak_check(none, batch2, tweaked_pk, tweaked_pk_parity, &pk, tweak) == 0);
CHECK(ecount == 0);
ecount = 0;
secp256k1_batch_destroy(none, batch1);
CHECK(ecount == 0);
secp256k1_batch_destroy(none, batch2);
CHECK(ecount == 0);
secp256k1_context_destroy(none);
secp256k1_context_destroy(sign);
secp256k1_context_destroy(vrfy);
}
void run_batch_add_xonlypub_tweak_tests(void) {
run_batch_xonlypub_tweak_randomizer_gen_tests();
test_batch_add_xonlypub_tweak_api();
}
#endif /* SECP256K1_MODULE_EXTRAKEYS_BATCH_ADD_TESTS_IMPL_H */

View file

@ -0,0 +1,139 @@
#ifndef SECP256K1_MODULE_EXTRAKEYS_BENCH_H
#define SECP256K1_MODULE_EXTRAKEYS_BENCH_H
#include "../../../include/secp256k1_extrakeys.h"
#ifdef ENABLE_MODULE_BATCH
# include "../../../include/secp256k1_batch.h"
# include "../../../include/secp256k1_tweak_check_batch.h"
#endif
typedef struct {
secp256k1_context *ctx;
#ifdef ENABLE_MODULE_BATCH
secp256k1_batch *batch;
/* number of tweak checks to batch verify.
* it varies from 1 to iters with 20% increments */
int n;
#endif
const secp256k1_keypair **keypairs;
const unsigned char **pks;
const unsigned char **tweaked_pks;
const int **tweaked_pk_parities;
const unsigned char **tweaks;
} bench_tweak_check_data;
void bench_xonly_pubkey_tweak_add_check(void* arg, int iters) {
bench_tweak_check_data *data = (bench_tweak_check_data *)arg;
int i;
for (i = 0; i < iters; i++) {
secp256k1_xonly_pubkey pk;
CHECK(secp256k1_xonly_pubkey_parse(data->ctx, &pk, data->pks[i]) == 1);
CHECK(secp256k1_xonly_pubkey_tweak_add_check(data->ctx, data->tweaked_pks[i], *data->tweaked_pk_parities[i], &pk, data->tweaks[i]) == 1);
}
}
#ifdef ENABLE_MODULE_BATCH
void bench_xonly_pubkey_tweak_add_check_n(void* arg, int iters) {
bench_tweak_check_data *data = (bench_tweak_check_data *)arg;
int i, j;
for (j = 0; j < iters/data->n; j++) {
for (i = 0; i < data->n; i++) {
secp256k1_xonly_pubkey pk;
CHECK(secp256k1_xonly_pubkey_parse(data->ctx, &pk, data->pks[j+i]) == 1);
CHECK(secp256k1_batch_usable(data->ctx, data->batch) == 1);
CHECK(secp256k1_batch_add_xonlypub_tweak_check(data->ctx, data->batch, data->tweaked_pks[j+i], *data->tweaked_pk_parities[j+i], &pk, data->tweaks[j+i]) == 1);
}
CHECK(secp256k1_batch_verify(data->ctx, data->batch) == 1);
}
}
#endif
void run_extrakeys_bench(int iters, int argc, char** argv) {
int i;
bench_tweak_check_data data;
int d = argc == 1;
data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
data.keypairs = (const secp256k1_keypair **)malloc(iters * sizeof(secp256k1_keypair *));
data.pks = (const unsigned char **)malloc(iters * sizeof(unsigned char *));
data.tweaked_pks = (const unsigned char **)malloc(iters * sizeof(unsigned char *));
data.tweaked_pk_parities = (const int **)malloc(iters * sizeof(int *));
data.tweaks = (const unsigned char **)malloc(iters * sizeof(unsigned char *));
#ifdef ENABLE_MODULE_BATCH
data.batch = secp256k1_batch_create(data.ctx, iters, NULL);
CHECK(data.batch != NULL);
#endif
for (i = 0; i < iters; i++) {
unsigned char sk[32];
unsigned char *tweaked_pk_char = (unsigned char *)malloc(32);
int *tweaked_pk_parity = (int *)malloc(sizeof(int)); /*todo: use sizeof(*twk_parity) instead?*/
unsigned char *tweak = (unsigned char *)malloc(32);
secp256k1_keypair *keypair = (secp256k1_keypair *)malloc(sizeof(*keypair));
unsigned char *pk_char = (unsigned char *)malloc(32);
secp256k1_xonly_pubkey pk;
secp256k1_pubkey output_pk;
secp256k1_xonly_pubkey output_pk_xonly;
tweak[0] = sk[0] = i;
tweak[1] = sk[1] = i >> 8;
tweak[2] = sk[2] = i >> 16;
tweak[3] = sk[3] = i >> 24;
memset(&tweak[4], 't', 28);
memset(&sk[4], 's', 28);
data.keypairs[i] = keypair;
data.pks[i] = pk_char;
data.tweaked_pks[i] = tweaked_pk_char;
data.tweaked_pk_parities[i] = tweaked_pk_parity;
data.tweaks[i] = tweak;
CHECK(secp256k1_keypair_create(data.ctx, keypair, sk));
CHECK(secp256k1_keypair_xonly_pub(data.ctx, &pk, NULL, keypair));
CHECK(secp256k1_xonly_pubkey_tweak_add(data.ctx, &output_pk, &pk, tweak));
CHECK(secp256k1_xonly_pubkey_from_pubkey(data.ctx, &output_pk_xonly, tweaked_pk_parity, &output_pk));
CHECK(secp256k1_xonly_pubkey_serialize(data.ctx, tweaked_pk_char, &output_pk_xonly) == 1);
CHECK(secp256k1_xonly_pubkey_serialize(data.ctx, pk_char, &pk) == 1);
}
if (d || have_flag(argc, argv, "extrakeys") || have_flag(argc, argv, "tweak_add_check")) run_benchmark("tweak_add_check", bench_xonly_pubkey_tweak_add_check, NULL, NULL, (void *) &data, 10, iters);
#ifdef ENABLE_MODULE_BATCH
if (d || have_flag(argc, argv, "extrakeys") || have_flag(argc, argv, "batch_verify") || have_flag(argc, argv, "tweak_check_batch_verify")) {
for (i = 1; i <= iters; i = (int)(i*1.2 + 1)) {
char name[64];
int divisible_iters;
sprintf(name, "tweak_check_batch_verify_%d", (int) i);
data.n = i;
divisible_iters = iters - (iters % data.n);
run_benchmark(name, bench_xonly_pubkey_tweak_add_check_n, NULL, NULL, (void *) &data, 3, divisible_iters);
fflush(stdout);
}
}
#endif
for (i = 0; i < iters; i++) {
free((void *)data.keypairs[i]);
free((void *)data.pks[i]);
free((void *)data.tweaked_pks[i]);
free((void *)data.tweaked_pk_parities[i]);
free((void *)data.tweaks[i]);
}
/* Casting to (void *) avoids a stupid warning in MSVC. */
free((void *)data.keypairs);
free((void *)data.pks);
free((void *)data.tweaked_pks);
free((void *)data.tweaked_pk_parities);
free((void *)data.tweaks);
#ifdef ENABLE_MODULE_BATCH
secp256k1_batch_destroy(data.ctx, data.batch);
#endif
secp256k1_context_destroy(data.ctx);
}
#endif /* SECP256K1_MODULE_EXTRAKEYS_BENCH_H */

View file

@ -1,5 +1,12 @@
include_HEADERS += include/secp256k1_schnorrsig.h
if ENABLE_MODULE_BATCH
include_HEADERS += include/secp256k1_schnorrsig_batch.h
endif
noinst_HEADERS += src/modules/schnorrsig/main_impl.h
noinst_HEADERS += src/modules/schnorrsig/tests_impl.h
noinst_HEADERS += src/modules/schnorrsig/tests_exhaustive_impl.h
noinst_HEADERS += src/modules/schnorrsig/bench_impl.h
if ENABLE_MODULE_BATCH
noinst_HEADERS += src/modules/schnorrsig/batch_add_impl.h
noinst_HEADERS += src/modules/schnorrsig/batch_add_tests_impl.h
endif

View file

@ -0,0 +1,158 @@
#ifndef SECP256K1_MODULE_SCHNORRSIG_BATCH_ADD_IMPL_H
#define SECP256K1_MODULE_SCHNORRSIG_BATCH_ADD_IMPL_H
#include "../../../include/secp256k1_schnorrsig.h"
#include "../../../include/secp256k1_schnorrsig_batch.h"
#include "../batch/main_impl.h"
/* The number of scalar-point pairs allocated on the scratch space
* by `secp256k1_batch_add_schnorrsig` */
#define BATCH_SCHNORRSIG_SCRATCH_OBJS 2
/** Computes a 16-byte deterministic randomizer by
* SHA256(batch_add_tag || sig || msg || compressed pubkey) */
static void secp256k1_batch_schnorrsig_randomizer_gen(unsigned char *randomizer32, secp256k1_sha256 *sha256, const unsigned char *sig64, const unsigned char *msg, size_t msglen, const unsigned char *compressed_pk33) {
secp256k1_sha256 sha256_cpy;
unsigned char batch_add_type = (unsigned char) schnorrsig;
secp256k1_sha256_write(sha256, &batch_add_type, sizeof(batch_add_type));
/* add schnorrsig data to sha256 object */
secp256k1_sha256_write(sha256, sig64, 64);
secp256k1_sha256_write(sha256, msg, msglen);
secp256k1_sha256_write(sha256, compressed_pk33, 33);
/* generate randomizer */
sha256_cpy = *sha256;
secp256k1_sha256_finalize(&sha256_cpy, randomizer32);
/* 16 byte randomizer is sufficient */
memset(randomizer32, 0, 16);
}
static int secp256k1_batch_schnorrsig_randomizer_set(const secp256k1_context *ctx, secp256k1_batch *batch, secp256k1_scalar *r, const unsigned char *sig64, const unsigned char *msg, size_t msglen, const secp256k1_xonly_pubkey *pubkey) {
unsigned char randomizer[32];
unsigned char buf[33];
size_t buflen = sizeof(buf);
int overflow;
/* t = 2^127 */
secp256k1_scalar t = SECP256K1_SCALAR_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x80000000, 0x00000000, 0x00000000, 0x00000000);
/* We use compressed serialization here. If we would use
* xonly_pubkey serialization and a user would wrongly memcpy
* normal secp256k1_pubkeys into xonly_pubkeys then the randomizer
* would be the same for two different pubkeys. */
if (!secp256k1_ec_pubkey_serialize(ctx, buf, &buflen, (const secp256k1_pubkey *) pubkey, SECP256K1_EC_COMPRESSED)) {
return 0;
}
secp256k1_batch_schnorrsig_randomizer_gen(randomizer, &batch->sha256, sig64, msg, msglen, buf);
secp256k1_scalar_set_b32(r, randomizer, &overflow);
/* Shift scalar to range [-2^127, 2^127-1] */
secp256k1_scalar_negate(&t, &t);
secp256k1_scalar_add(r, r, &t);
VERIFY_CHECK(overflow == 0);
return 1;
}
/** Adds the given schnorr signature to the batch.
*
* Updates the batch object by:
* 1. adding the points R and P to the scratch space
* -> both the points are of type `secp256k1_gej`
* 2. adding the scalars ai and ai.e to the scratch space
* -> ai is the scalar coefficient of R (in multi multiplication)
* -> ai.e is the scalar coefficient of P (in multi multiplication)
* 3. incrementing sc_g (scalar of G) by -ai.s
*
* Conventions used above:
* -> R (nonce commitment) = EC point whose y = even and x = sig64[0:32]
* -> P (public key) = pubkey
* -> ai (randomizer) = sha256_tagged(batch_add_tag || sig64 || msg || pubkey)
* -> e (challenge) = sha256_tagged(sig64[0:32] || pk.x || msg)
* -> s = sig64[32:64]
*
* This function is based on `secp256k1_schnorrsig_verify`.
*/
int secp256k1_batch_add_schnorrsig(const secp256k1_context* ctx, secp256k1_batch *batch, const unsigned char *sig64, const unsigned char *msg, size_t msglen, const secp256k1_xonly_pubkey *pubkey) {
secp256k1_scalar s;
secp256k1_scalar e;
secp256k1_scalar ai;
secp256k1_ge pk;
secp256k1_fe rx;
secp256k1_ge r;
unsigned char buf[32];
int overflow;
size_t i;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(batch != NULL);
ARG_CHECK(sig64 != NULL);
ARG_CHECK(msg != NULL || msglen == 0);
ARG_CHECK(pubkey != NULL);
if (batch->result == 0) {
return 0;
}
if (!secp256k1_fe_set_b32_limit(&rx, &sig64[0])) {
return 0;
}
secp256k1_scalar_set_b32(&s, &sig64[32], &overflow);
if (overflow) {
return 0;
}
if (!secp256k1_xonly_pubkey_load(ctx, &pk, pubkey)) {
return 0;
}
/* if insufficient space in batch, verify the inputs (stored in curr batch) and
* save the result. This extends the batch capacity since `secp256k1_batch_verify`
* clears the batch after verification. */
if (batch->capacity - batch->len < BATCH_SCHNORRSIG_SCRATCH_OBJS) {
secp256k1_batch_verify(ctx, batch);
}
i = batch->len;
/* append point R to the scratch space */
if (!secp256k1_ge_set_xo_var(&r, &rx, 0)) {
return 0;
}
if (!secp256k1_ge_is_in_correct_subgroup(&r)) {
return 0;
}
secp256k1_gej_set_ge(&batch->points[i], &r);
/* append point P to the scratch space */
secp256k1_gej_set_ge(&batch->points[i+1], &pk);
/* compute e (challenge) */
secp256k1_fe_get_b32(buf, &pk.x);
secp256k1_schnorrsig_challenge(&e, &sig64[0], msg, msglen, buf);
/* compute ai (randomizer) */
if (batch->len == 0) {
/* don't generate a randomizer for the first term in the batch to improve
* the computation speed. hence, set the randomizer to 1. */
ai = secp256k1_scalar_one;
} else if (!secp256k1_batch_schnorrsig_randomizer_set(ctx, batch, &ai, sig64, msg, msglen, pubkey)) {
return 0;
}
/* append scalars ai and ai.e to scratch space (order shouldn't change) */
batch->scalars[i] = ai;
secp256k1_scalar_mul(&e, &e, &ai);
batch->scalars[i+1] = e;
/* increment scalar of G by -ai.s */
secp256k1_scalar_mul(&s, &s, &ai);
secp256k1_scalar_negate(&s, &s);
secp256k1_scalar_add(&batch->sc_g, &batch->sc_g, &s);
batch->len += 2;
return 1;
}
#endif /* SECP256K1_MODULE_SCHNORRSIG_BATCH_ADD_IMPL_H */

View file

@ -0,0 +1,313 @@
#ifndef SECP256K1_MODULE_SCHNORRSIG_BATCH_ADD_TESTS_IMPL_H
#define SECP256K1_MODULE_SCHNORRSIG_BATCH_ADD_TESTS_IMPL_H
#include "../../../include/secp256k1_schnorrsig.h"
#include "../../../include/secp256k1_batch.h"
#include "../../../include/secp256k1_schnorrsig_batch.h"
/* Checks that a bit flip in the n_flip-th argument (that has n_bytes many
* bytes) changes the hash function */
void batch_schnorrsig_randomizer_gen_bitflip(secp256k1_sha256 *sha, unsigned char **args, size_t n_flip, size_t n_bytes, size_t msglen) {
unsigned char randomizers[2][32];
secp256k1_sha256 sha_cpy;
sha_cpy = *sha;
secp256k1_batch_schnorrsig_randomizer_gen(randomizers[0], &sha_cpy, args[0], args[1], msglen, args[2]);
testrand_flip(args[n_flip], n_bytes);
sha_cpy = *sha;
secp256k1_batch_schnorrsig_randomizer_gen(randomizers[1], &sha_cpy, args[0], args[1], msglen, args[2]);
CHECK(secp256k1_memcmp_var(randomizers[0], randomizers[1], 32) != 0);
}
void run_batch_schnorrsig_randomizer_gen_tests(void) {
secp256k1_sha256 sha;
size_t n_sigs = 20;
unsigned char msg[32];
size_t msglen = sizeof(msg);
unsigned char sig[64];
unsigned char compressed_pk[33];
unsigned char *args[3];
size_t i; /* loops through n_sigs */
int j; /* loops through count */
secp256k1_batch_sha256_tagged(&sha);
for (i = 0; i < n_sigs; i++) {
uint8_t temp_rand;
unsigned char randomizer[32];
/* batch_schnorrsig_randomizer_gen func modifies the sha object passed
* so, pass the copied obj instead of original */
secp256k1_sha256 sha_cpy;
/* generate i-th schnorrsig verify data */
testrand256(msg);
testrand256(&sig[0]);
testrand256(&sig[32]);
testrand256(&compressed_pk[1]);
temp_rand = testrand_int(2) + 2; /* randomly choose 2 or 3 */
compressed_pk[0] = (unsigned char)temp_rand;
/* check that bitflip in an argument results in different nonces */
args[0] = sig;
args[1] = msg;
args[2] = compressed_pk;
for (j = 0; j < COUNT; j++) {
batch_schnorrsig_randomizer_gen_bitflip(&sha, args, 0, 64, msglen);
batch_schnorrsig_randomizer_gen_bitflip(&sha, args, 1, 32, msglen);
batch_schnorrsig_randomizer_gen_bitflip(&sha, args, 2, 33, msglen);
}
/* different msglen should generate different randomizers */
sha_cpy = sha;
secp256k1_batch_schnorrsig_randomizer_gen(randomizer, &sha_cpy, sig, msg, msglen, compressed_pk);
for (j = 0; j < COUNT; j++) {
unsigned char randomizer2[32];
uint32_t offset = testrand_int(msglen - 1);
size_t msglen_tmp = (msglen + offset) % msglen;
sha_cpy = sha;
secp256k1_batch_schnorrsig_randomizer_gen(randomizer2, &sha_cpy, sig, msg, msglen_tmp, compressed_pk);
CHECK(secp256k1_memcmp_var(randomizer, randomizer2, 32) != 0);
}
/* write i-th schnorrsig verify data to the sha object
* this is required for generating the next randomizer */
secp256k1_sha256_write(&sha, sig, 64);
secp256k1_sha256_write(&sha, msg, msglen);
secp256k1_sha256_write(&sha, compressed_pk, 33);
}
}
/* Helper for function test_schnorrsig_sign_batch_verify
* Checks that batch_verify fails after flipping random byte. */
void test_schnorrsig_sign_verify_check_batch(secp256k1_batch *batch, unsigned char *sig64, unsigned char *msg, size_t msglen, secp256k1_xonly_pubkey *pk) {
int ret;
CHECK(secp256k1_batch_usable(CTX, batch));
/* filling a random byte (in msg or sig) can cause the following:
* 1. unparsable msg or sig - here, batch_add_schnorrsig fails and batch_verify passes
* 2. invalid schnorr eqn - here, batch_verify fails and batch_add_schnorrsig passes
*/
ret = secp256k1_batch_add_schnorrsig(CTX, batch, sig64, msg, msglen, pk);
if (ret == 0) {
CHECK(secp256k1_batch_verify(CTX, batch) == 1);
} else if (ret == 1) {
CHECK(secp256k1_batch_verify(CTX, batch) == 0);
}
}
#define N_SIGS 3
#define ONE_SIG 1
/* Creates N_SIGS valid signatures and verifies them with batch_verify.
* Then flips some bits and checks that verification now fails. This is a
* variation of `test_schnorrsig_sign_verify` (in schnorrsig/tests_impl.h) */
void test_schnorrsig_sign_batch_verify(void) {
unsigned char sk[32];
unsigned char msg[N_SIGS][32];
unsigned char sig[N_SIGS][64];
size_t i;
secp256k1_keypair keypair;
secp256k1_xonly_pubkey pk;
secp256k1_scalar s;
secp256k1_batch *batch[N_SIGS + 1];
secp256k1_batch *batch_fail1;
secp256k1_batch *batch_fail2;
/* batch[0] will be used where batch_add and batch_verify
* are expected to succed */
batch[0] = secp256k1_batch_create(CTX, 2*N_SIGS, NULL);
for (i = 0; i < N_SIGS; i++) {
batch[i+1] = secp256k1_batch_create(CTX, 2*ONE_SIG, NULL);
}
batch_fail1 = secp256k1_batch_create(CTX, 2*ONE_SIG, NULL);
batch_fail2 = secp256k1_batch_create(CTX, 2*ONE_SIG, NULL);
testrand256(sk);
CHECK(secp256k1_keypair_create(CTX, &keypair, sk));
CHECK(secp256k1_keypair_xonly_pub(CTX, &pk, NULL, &keypair));
for (i = 0; i < N_SIGS; i++) {
testrand256(msg[i]);
CHECK(secp256k1_schnorrsig_sign32(CTX, sig[i], msg[i], &keypair, NULL));
CHECK(secp256k1_batch_usable(CTX, batch[0]));
CHECK(secp256k1_batch_add_schnorrsig(CTX, batch[0], sig[i], msg[i], sizeof(msg[i]), &pk));
}
CHECK(secp256k1_batch_verify(CTX, batch[0]));
{
/* Flip a few bits in the signature and in the message and check that
* verify and verify_batch (TODO) fail */
size_t sig_idx = testrand_int(N_SIGS);
size_t byte_idx = testrand_bits(5);
unsigned char xorbyte = testrand_int(254)+1;
sig[sig_idx][byte_idx] ^= xorbyte;
test_schnorrsig_sign_verify_check_batch(batch[1], sig[sig_idx], msg[sig_idx], sizeof(msg[sig_idx]), &pk);
sig[sig_idx][byte_idx] ^= xorbyte;
byte_idx = testrand_bits(5);
sig[sig_idx][32+byte_idx] ^= xorbyte;
test_schnorrsig_sign_verify_check_batch(batch[2], sig[sig_idx], msg[sig_idx], sizeof(msg[sig_idx]), &pk);
sig[sig_idx][32+byte_idx] ^= xorbyte;
byte_idx = testrand_bits(5);
msg[sig_idx][byte_idx] ^= xorbyte;
test_schnorrsig_sign_verify_check_batch(batch[3], sig[sig_idx], msg[sig_idx], sizeof(msg[sig_idx]), &pk);
msg[sig_idx][byte_idx] ^= xorbyte;
/* Check that above bitflips have been reversed correctly */
CHECK(secp256k1_schnorrsig_verify(CTX, sig[sig_idx], msg[sig_idx], sizeof(msg[sig_idx]), &pk));
}
/* Test overflowing s */
CHECK(secp256k1_schnorrsig_sign32(CTX, sig[0], msg[0], &keypair, NULL));
CHECK(secp256k1_batch_add_schnorrsig(CTX, batch[0], sig[0], msg[0], sizeof(msg[0]), &pk) == 1);
memset(&sig[0][32], 0xFF, 32);
CHECK(secp256k1_batch_add_schnorrsig(CTX, batch[0], sig[0], msg[0], sizeof(msg[0]), &pk) == 0);
/* Test negative s */
CHECK(secp256k1_schnorrsig_sign32(CTX, sig[0], msg[0], &keypair, NULL));
CHECK(secp256k1_batch_add_schnorrsig(CTX, batch[0], sig[0], msg[0], sizeof(msg[0]), &pk) == 1);
secp256k1_scalar_set_b32(&s, &sig[0][32], NULL);
secp256k1_scalar_negate(&s, &s);
secp256k1_scalar_get_b32(&sig[0][32], &s);
CHECK(secp256k1_batch_add_schnorrsig(CTX, batch_fail1, sig[0], msg[0], sizeof(msg[0]), &pk) == 1);
CHECK(secp256k1_batch_verify(CTX, batch_fail1) == 0);
/* The empty message can be signed & verified */
CHECK(secp256k1_schnorrsig_sign_custom(CTX, sig[0], NULL, 0, &keypair, NULL) == 1);
CHECK(secp256k1_batch_usable(CTX, batch[0]) == 1);
CHECK(secp256k1_batch_add_schnorrsig(CTX, batch[0], sig[0], NULL, 0, &pk) == 1);
CHECK(secp256k1_batch_verify(CTX, batch[0]) == 1);
{
/* Test varying message lengths */
unsigned char msg_large[32 * 8];
uint32_t msglen = testrand_int(sizeof(msg_large));
for (i = 0; i < sizeof(msg_large); i += 32) {
testrand256(&msg_large[i]);
}
CHECK(secp256k1_schnorrsig_sign_custom(CTX, sig[0], msg_large, msglen, &keypair, NULL) == 1);
CHECK(secp256k1_batch_usable(CTX, batch[0]) == 1);
CHECK(secp256k1_batch_add_schnorrsig(CTX, batch[0], sig[0], msg_large, msglen, &pk) == 1);
CHECK(secp256k1_batch_verify(CTX, batch[0]) == 1);
/* batch_add fails for a random wrong message length */
msglen = (msglen + (sizeof(msg_large) - 1)) % sizeof(msg_large);
CHECK(secp256k1_batch_usable(CTX, batch_fail2) == 1);
CHECK(secp256k1_batch_add_schnorrsig(CTX, batch_fail2, sig[0], msg_large, msglen, &pk) == 1);
CHECK(secp256k1_batch_verify(CTX, batch_fail2) == 0);
}
/* Destroy the batch objects */
for (i = 0; i < N_SIGS+1; i++) {
secp256k1_batch_destroy(CTX, batch[i]);
}
secp256k1_batch_destroy(CTX, batch_fail1);
secp256k1_batch_destroy(CTX, batch_fail2);
}
#undef N_SIGS
/* ONE_SIG is undefined after `test_batch_add_schnorrsig_api` */
void test_batch_add_schnorrsig_api(void) {
unsigned char sk[32];
secp256k1_keypair keypair;
secp256k1_xonly_pubkey pk;
secp256k1_xonly_pubkey zero_pk;
unsigned char msg[32];
unsigned char sig[64];
unsigned char nullmsg_sig[64];
/** setup **/
secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY);
secp256k1_batch *batch1 = secp256k1_batch_create(none, 2*ONE_SIG, NULL);
/* batch2 is used when batch_add_schnorrsig is expected to fail */
secp256k1_batch *batch2 = secp256k1_batch_create(none, 2*ONE_SIG, NULL);
int ecount;
secp256k1_context_set_error_callback(none, counting_callback_fn, &ecount);
secp256k1_context_set_error_callback(sign, counting_callback_fn, &ecount);
secp256k1_context_set_error_callback(vrfy, counting_callback_fn, &ecount);
secp256k1_context_set_illegal_callback(none, counting_callback_fn, &ecount);
secp256k1_context_set_illegal_callback(sign, counting_callback_fn, &ecount);
secp256k1_context_set_illegal_callback(vrfy, counting_callback_fn, &ecount);
/** generate keypair data **/
testrand256(sk);
CHECK(secp256k1_keypair_create(sign, &keypair, sk) == 1);
CHECK(secp256k1_keypair_xonly_pub(sign, &pk, NULL, &keypair) == 1);
memset(&zero_pk, 0, sizeof(zero_pk));
/** generate a signature **/
testrand256(msg);
CHECK(secp256k1_schnorrsig_sign32(sign, sig, msg, &keypair, NULL) == 1);
CHECK(secp256k1_schnorrsig_verify(vrfy, sig, msg, sizeof(msg), &pk));
CHECK(batch1 != NULL);
CHECK(batch2 != NULL);
/** main test body **/
ecount = 0;
CHECK(secp256k1_batch_add_schnorrsig(none, batch1, sig, msg, sizeof(msg), &pk) == 1);
CHECK(ecount == 0);
CHECK(secp256k1_batch_verify(none, batch1) == 1);
CHECK(ecount == 0);
CHECK(secp256k1_batch_add_schnorrsig(none, batch2, NULL, msg, sizeof(msg), &pk) == 0);
CHECK(ecount == 1);
CHECK(secp256k1_batch_add_schnorrsig(none, batch2, sig, NULL, sizeof(msg), &pk) == 0);
CHECK(ecount == 2);
CHECK(secp256k1_batch_add_schnorrsig(none, batch2, sig, msg, sizeof(msg), NULL) == 0);
CHECK(ecount == 3);
CHECK(secp256k1_batch_add_schnorrsig(none, batch2, sig, msg, sizeof(msg), &zero_pk) == 0);
CHECK(ecount == 4);
CHECK(secp256k1_batch_add_schnorrsig(none, NULL, sig, msg, sizeof(msg), &pk) == 0);
CHECK(ecount == 5);
/** NULL msg with valid signature **/
ecount = 0;
CHECK(secp256k1_schnorrsig_sign_custom(sign, nullmsg_sig, NULL, 0, &keypair, NULL) == 1);
CHECK(secp256k1_batch_usable(none, batch1) == 1);
CHECK(secp256k1_batch_add_schnorrsig(none, batch1, nullmsg_sig, NULL, 0, &pk) == 1);
CHECK(ecount == 0);
CHECK(secp256k1_batch_verify(none, batch1) == 1);
/** NULL msg with invalid signature **/
CHECK(secp256k1_batch_usable(none, batch2) == 1);
CHECK(secp256k1_batch_add_schnorrsig(none, batch2, sig, NULL, 0, &pk) == 1);
CHECK(ecount == 0);
CHECK(secp256k1_batch_verify(none, batch2) == 0);
/** batch_add_ should ignore unusable batch object (i.e, batch->result = 0) **/
ecount = 0;
CHECK(secp256k1_batch_usable(none, batch2) == 0);
CHECK(ecount == 0);
CHECK(secp256k1_batch_add_schnorrsig(none, batch2, sig, msg, sizeof(msg), &pk) == 0);
CHECK(ecount == 0);
ecount = 0;
secp256k1_batch_destroy(CTX, batch1);
CHECK(ecount == 0);
secp256k1_batch_destroy(CTX, batch2);
CHECK(ecount == 0);
secp256k1_context_destroy(none);
secp256k1_context_destroy(sign);
secp256k1_context_destroy(vrfy);
}
#undef ONE_SIG
void run_batch_add_schnorrsig_tests(void) {
int i;
run_batch_schnorrsig_randomizer_gen_tests();
test_batch_add_schnorrsig_api();
for (i = 0; i < COUNT; i++) {
test_schnorrsig_sign_batch_verify();
}
}
#endif /* SECP256K1_MODULE_SCHNORRSIG_BATCH_ADD_TESTS_IMPL_H */

View file

@ -8,12 +8,21 @@
#define SECP256K1_MODULE_SCHNORRSIG_BENCH_H
#include "../../../include/secp256k1_schnorrsig.h"
#ifdef ENABLE_MODULE_BATCH
# include "../../../include/secp256k1_batch.h"
# include "../../../include/secp256k1_schnorrsig_batch.h"
#endif
#define MSGLEN 32
typedef struct {
secp256k1_context *ctx;
#ifdef ENABLE_MODULE_BATCH
secp256k1_batch *batch;
/* number of signatures to batch verify.
* it varies from 1 to iters with 20% increments */
int n;
#endif
const secp256k1_keypair **keypairs;
const unsigned char **pk;
@ -45,7 +54,24 @@ static void bench_schnorrsig_verify(void* arg, int iters) {
}
}
static void run_schnorrsig_bench(int iters, int argc, char** argv) {
#ifdef ENABLE_MODULE_BATCH
void bench_schnorrsig_verify_n(void* arg, int iters) {
bench_schnorrsig_data *data = (bench_schnorrsig_data *)arg;
int i, j;
for (j = 0; j < iters/data->n; j++) {
for (i = 0; i < data->n; i++) {
secp256k1_xonly_pubkey pk;
CHECK(secp256k1_xonly_pubkey_parse(data->ctx, &pk, data->pk[j+i]) == 1);
CHECK(secp256k1_batch_usable(data->ctx, data->batch) == 1);
CHECK(secp256k1_batch_add_schnorrsig(data->ctx, data->batch, data->sigs[j+i], data->msgs[j+i], MSGLEN, &pk) == 1);
}
CHECK(secp256k1_batch_verify(data->ctx, data->batch) == 1);
}
}
#endif
void run_schnorrsig_bench(int iters, int argc, char** argv) {
int i;
bench_schnorrsig_data data;
int d = argc == 1;
@ -55,6 +81,10 @@ static void run_schnorrsig_bench(int iters, int argc, char** argv) {
data.pk = (const unsigned char **)malloc(iters * sizeof(unsigned char *));
data.msgs = (const unsigned char **)malloc(iters * sizeof(unsigned char *));
data.sigs = (const unsigned char **)malloc(iters * sizeof(unsigned char *));
#ifdef ENABLE_MODULE_BATCH
data.batch = secp256k1_batch_create(data.ctx, 2*iters, NULL);
CHECK(data.batch != NULL);
#endif
CHECK(MSGLEN >= 4);
for (i = 0; i < iters; i++) {
@ -84,6 +114,20 @@ static void run_schnorrsig_bench(int iters, int argc, char** argv) {
if (d || have_flag(argc, argv, "schnorrsig") || have_flag(argc, argv, "sign") || have_flag(argc, argv, "schnorrsig_sign")) run_benchmark("schnorrsig_sign", bench_schnorrsig_sign, NULL, NULL, (void *) &data, 10, iters);
if (d || have_flag(argc, argv, "schnorrsig") || have_flag(argc, argv, "verify") || have_flag(argc, argv, "schnorrsig_verify")) run_benchmark("schnorrsig_verify", bench_schnorrsig_verify, NULL, NULL, (void *) &data, 10, iters);
#ifdef ENABLE_MODULE_BATCH
if (d || have_flag(argc, argv, "schnorrsig") || have_flag(argc, argv, "batch_verify") || have_flag(argc, argv, "schnorrsig_batch_verify")) {
for (i = 1; i <= iters; i = (int)(i*1.2 + 1)) {
char name[64];
int divisible_iters;
sprintf(name, "schnorrsig_batch_verify_%d", (int) i);
data.n = i;
divisible_iters = iters - (iters % data.n);
run_benchmark(name, bench_schnorrsig_verify_n, NULL, NULL, (void *) &data, 3, divisible_iters);
fflush(stdout);
}
}
#endif
for (i = 0; i < iters; i++) {
free((void *)data.keypairs[i]);
@ -98,6 +142,9 @@ static void run_schnorrsig_bench(int iters, int argc, char** argv) {
free((void *)data.msgs);
free((void *)data.sigs);
#ifdef ENABLE_MODULE_BATCH
secp256k1_batch_destroy(data.ctx, data.batch);
#endif
secp256k1_context_destroy(data.ctx);
}

View file

@ -8,6 +8,10 @@
#define SECP256K1_MODULE_SCHNORRSIG_TESTS_H
#include "../../../include/secp256k1_schnorrsig.h"
#ifdef ENABLE_MODULE_BATCH
# include "../../../include/secp256k1_batch.h"
# include "../../../include/secp256k1_schnorrsig_batch.h"
#endif
/* Checks that a bit flip in the n_flip-th argument (that has n_bytes many
* bytes) changes the hash function
@ -193,7 +197,7 @@ static void test_schnorrsig_bip_vectors_check_signing(const unsigned char *sk, c
}
/* Helper function for schnorrsig_bip_vectors
* Checks that both verify and verify_batch (TODO) return the same value as expected. */
* Checks that schnorrsig_verify returns the same value as expected. */
static void test_schnorrsig_bip_vectors_check_verify(const unsigned char *pk_serialized, const unsigned char *msg, size_t msglen, const unsigned char *sig, int expected) {
secp256k1_xonly_pubkey pk;
@ -201,6 +205,23 @@ static void test_schnorrsig_bip_vectors_check_verify(const unsigned char *pk_ser
CHECK(expected == secp256k1_schnorrsig_verify(CTX, sig, msg, msglen, &pk));
}
#ifdef ENABLE_MODULE_BATCH
/* Helper function for schnorrsig_bip_vectors
* Checks that batch_verify return the same value as expected. */
void test_schnorrsig_bip_vectors_check_batch_verify(const unsigned char *pk_serialized, const unsigned char *msg32, const unsigned char *sig, int add_expected, int verify_expected) {
secp256k1_xonly_pubkey pk;
secp256k1_batch *batch;
CHECK(secp256k1_xonly_pubkey_parse(CTX, &pk, pk_serialized));
batch = secp256k1_batch_create(CTX, 2, NULL);
CHECK(batch != NULL);
CHECK(secp256k1_batch_usable(CTX, batch) == 1);
CHECK(add_expected == secp256k1_batch_add_schnorrsig(CTX, batch, sig, msg32, 32, &pk));
CHECK(verify_expected == secp256k1_batch_verify(CTX, batch));
secp256k1_batch_destroy(CTX, batch);
}
#endif
/* Test vectors according to BIP-340 ("Schnorr Signatures for secp256k1"). See
* https://github.com/bitcoin/bips/blob/master/bip-0340/test-vectors.csv. */
static void test_schnorrsig_bip_vectors(void) {
@ -242,6 +263,9 @@ static void test_schnorrsig_bip_vectors(void) {
};
test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sizeof(msg), sig);
test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 1);
#ifdef ENABLE_MODULE_BATCH
test_schnorrsig_bip_vectors_check_batch_verify(pk, msg, sig, 1, 1);
#endif
}
{
/* Test vector 1 */
@ -281,6 +305,9 @@ static void test_schnorrsig_bip_vectors(void) {
};
test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sizeof(msg), sig);
test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 1);
#ifdef ENABLE_MODULE_BATCH
test_schnorrsig_bip_vectors_check_batch_verify(pk, msg, sig, 1, 1);
#endif
}
{
/* Test vector 2 */
@ -320,6 +347,9 @@ static void test_schnorrsig_bip_vectors(void) {
};
test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sizeof(msg), sig);
test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 1);
#ifdef ENABLE_MODULE_BATCH
test_schnorrsig_bip_vectors_check_batch_verify(pk, msg, sig, 1, 1);
#endif
}
{
/* Test vector 3 */
@ -359,6 +389,9 @@ static void test_schnorrsig_bip_vectors(void) {
};
test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sizeof(msg), sig);
test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 1);
#ifdef ENABLE_MODULE_BATCH
test_schnorrsig_bip_vectors_check_batch_verify(pk, msg, sig, 1, 1);
#endif
}
{
/* Test vector 4 */
@ -385,6 +418,9 @@ static void test_schnorrsig_bip_vectors(void) {
0x06, 0x0B, 0x07, 0xD2, 0x83, 0x08, 0xD7, 0xF4
};
test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 1);
#ifdef ENABLE_MODULE_BATCH
test_schnorrsig_bip_vectors_check_batch_verify(pk, msg, sig, 1, 1);
#endif
}
{
/* Test vector 5 */
@ -423,6 +459,12 @@ static void test_schnorrsig_bip_vectors(void) {
0xBE, 0xAF, 0xA3, 0x4B, 0x1A, 0xC5, 0x53, 0xE2
};
test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 0);
#ifdef ENABLE_MODULE_BATCH
/* batch_add_schnorrsig adds converts sig[0:32] to point R such
* that R.y is always even. This test vector has R.y = odd, so
* batch_add_schnorrsig returns 1 and batch_verify returns 0. */
test_schnorrsig_bip_vectors_check_batch_verify(pk, msg, sig, 1, 0);
#endif
}
{
/* Test vector 7 */
@ -449,6 +491,12 @@ static void test_schnorrsig_bip_vectors(void) {
0xAA, 0xEA, 0x51, 0x34, 0xFC, 0xCD, 0xB2, 0xBD
};
test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 0);
#ifdef ENABLE_MODULE_BATCH
/* batch_add_schnorrsig does not verify the schnorr eqn.
* This test vector negated message, so batch_add_schnorrsig
* returns 1 and batch_verify returns 0. */
test_schnorrsig_bip_vectors_check_batch_verify(pk, msg, sig, 1, 0);
#endif
}
{
/* Test vector 8 */
@ -475,6 +523,12 @@ static void test_schnorrsig_bip_vectors(void) {
0x18, 0x34, 0xFF, 0x0D, 0x0C, 0x2E, 0x6D, 0xA6
};
test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 0);
#ifdef ENABLE_MODULE_BATCH
/* batch_add_schnorrsig does not verify the schnorr eqn.
* This test vector negated s (sig[32:64]), so batch_add_schnorrsig
* returns 1 and batch_verify returns 0. */
test_schnorrsig_bip_vectors_check_batch_verify(pk, msg, sig, 1, 0);
#endif
}
{
/* Test vector 9 */
@ -501,6 +555,12 @@ static void test_schnorrsig_bip_vectors(void) {
0xB6, 0x5C, 0x64, 0x25, 0xBD, 0x18, 0x60, 0x51
};
test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 0);
#ifdef ENABLE_MODULE_BATCH
/* batch_add_schnorrsig fails since R.x = 0.
* batch_verify passes because the batch is empty
* (prev batch_add failed so nothing was added to the batch)*/
test_schnorrsig_bip_vectors_check_batch_verify(pk, msg, sig, 0, 1);
#endif
}
{
/* Test vector 10 */
@ -527,6 +587,12 @@ static void test_schnorrsig_bip_vectors(void) {
0x37, 0x80, 0xD5, 0xA1, 0x83, 0x7C, 0xF1, 0x97
};
test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 0);
#ifdef ENABLE_MODULE_BATCH
/* batch_add_schnorrsig passes since R.x = 1.
* batch_verify fails since R (with R.x = 1 & R.y = even) does not
* lie on libsecp256k1 */
test_schnorrsig_bip_vectors_check_batch_verify(pk, msg, sig, 1, 0);
#endif
}
{
/* Test vector 11 */
@ -553,6 +619,11 @@ static void test_schnorrsig_bip_vectors(void) {
0xA7, 0x9D, 0x5F, 0x7F, 0xC4, 0x07, 0xD3, 0x9B
};
test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 0);
#ifdef ENABLE_MODULE_BATCH
/* batch_add fails since R.x is an invalid x-coordinate (not on curve)
* batch_verify passes since the batch is empty */
test_schnorrsig_bip_vectors_check_batch_verify(pk, msg, sig, 0, 1);
#endif
}
{
/* Test vector 12 */
@ -579,6 +650,11 @@ static void test_schnorrsig_bip_vectors(void) {
0xA7, 0x9D, 0x5F, 0x7F, 0xC4, 0x07, 0xD3, 0x9B
};
test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 0);
#ifdef ENABLE_MODULE_BATCH
/* batch_add fails since R.x = field modulo `p`
* batch_verify passes since the batch is empty */
test_schnorrsig_bip_vectors_check_batch_verify(pk, msg, sig, 0, 1);
#endif
}
{
/* Test vector 13 */
@ -605,6 +681,11 @@ static void test_schnorrsig_bip_vectors(void) {
0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41
};
test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 0);
#ifdef ENABLE_MODULE_BATCH
/* batch_add fails since s (sig[32:64]) = curve order `n`
* batch_verify passes since the batch is empty */
test_schnorrsig_bip_vectors_check_batch_verify(pk, msg, sig, 0, 1);
#endif
}
{
/* Test vector 14 */
@ -851,8 +932,10 @@ static void test_schnorrsig_sign(void) {
#define N_SIGS 3
/* Creates N_SIGS valid signatures and verifies them with verify and
* verify_batch (TODO). Then flips some bits and checks that verification now
* fails. */
static void test_schnorrsig_sign_verify(void) {
* batch_verify. Then flips some bits and checks that verification now
* fails. The batch_verify variation of this test is implemented as
* test_schnorrsig_sign_batch_verify (in schnorrsig/batch_add_tests_impl.h) */
void test_schnorrsig_sign_verify(void) {
unsigned char sk[32];
unsigned char msg[N_SIGS][32];
unsigned char sig[N_SIGS][64];

View file

@ -90,11 +90,11 @@ static void secp256k1_scalar_split_lambda_verify(const secp256k1_scalar *r1, con
#endif
/*
* Both lambda and beta are primitive cube roots of unity. That is lamba^3 == 1 mod n and
* Both lambda and beta are primitive cube roots of unity. That is lambda^3 == 1 mod n and
* beta^3 == 1 mod p, where n is the curve order and p is the field order.
*
* Furthermore, because (X^3 - 1) = (X - 1)(X^2 + X + 1), the primitive cube roots of unity are
* roots of X^2 + X + 1. Therefore lambda^2 + lamba == -1 mod n and beta^2 + beta == -1 mod p.
* roots of X^2 + X + 1. Therefore lambda^2 + lambda == -1 mod n and beta^2 + beta == -1 mod p.
* (The other primitive cube roots of unity are lambda^2 and beta^2 respectively.)
*
* Let l = -1/2 + i*sqrt(3)/2, the complex root of X^2 + X + 1. We can define a ring

View file

@ -829,3 +829,13 @@ int secp256k1_tagged_sha256(const secp256k1_context* ctx, unsigned char *hash32,
#ifdef ENABLE_MODULE_ELLSWIFT
# include "modules/ellswift/main_impl.h"
#endif
#ifdef ENABLE_MODULE_BATCH
# include "modules/batch/main_impl.h"
# ifdef ENABLE_MODULE_EXTRAKEYS
# include "modules/extrakeys/batch_add_impl.h"
# endif
# ifdef ENABLE_MODULE_SCHNORRSIG
# include "modules/schnorrsig/batch_add_impl.h"
# endif
#endif

View file

@ -4857,38 +4857,15 @@ static void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi
}
}
static int test_ecmult_multi_random(secp256k1_scratch *scratch) {
/* Large random test for ecmult_multi_* functions which exercises:
* - Few or many inputs (0 up to 128, roughly exponentially distributed).
* - Few or many 0*P or a*INF inputs (roughly uniformly distributed).
* - Including or excluding an nonzero a*G term (or such a term at all).
* - Final expected result equal to infinity or not (roughly 50%).
* - ecmult_multi_var, ecmult_strauss_single_batch, ecmult_pippenger_single_batch
*/
/* These 4 variables define the eventual input to the ecmult_multi function.
* g_scalar is the G scalar fed to it (or NULL, possibly, if g_scalar=0), and
* scalars[0..filled-1] and gejs[0..filled-1] are the scalars and points
* which form its normal inputs. */
int filled = 0;
secp256k1_scalar g_scalar = secp256k1_scalar_zero;
secp256k1_scalar scalars[128];
secp256k1_gej gejs[128];
/* The expected result, and the computed result. */
secp256k1_gej expected, computed;
/** helper function used by `test_ecmult_multi_random` and `test_ecmult_strauss_batch_internal_random`
* to generate inputs (scalars, points, g_scalar) for multi-scalar point multiplication */
void ecmult_multi_random_generate_inp(secp256k1_gej *expected, secp256k1_scalar *g_scalar, secp256k1_scalar *scalars, secp256k1_gej *gejs, int *inp_len, int *nonzero_inp_len, int *is_g_nonzero, int *mults_performed) {
/* Temporaries. */
secp256k1_scalar sc_tmp;
secp256k1_ge ge_tmp;
/* Variables needed for the actual input to ecmult_multi. */
secp256k1_ge ges[128];
ecmult_multi_data data;
int i;
/* Which multiplication function to use */
int fn = testrand_int(3);
secp256k1_ecmult_multi_func ecmult_multi = fn == 0 ? secp256k1_ecmult_multi_var :
fn == 1 ? secp256k1_ecmult_strauss_batch_single :
secp256k1_ecmult_pippenger_batch_single;
int filled = 0;
/* Simulate exponentially distributed num. */
int num_bits = 2 + testrand_int(6);
/* Number of (scalar, point) inputs (excluding g). */
@ -4903,25 +4880,25 @@ static int test_ecmult_multi_random(secp256k1_scratch *scratch) {
num_nonzero == 1 && !nonzero_result ? 1 :
(int)testrand_bits(1);
/* Which g_scalar pointer to pass into ecmult_multi(). */
const secp256k1_scalar* g_scalar_ptr = (g_nonzero || testrand_bits(1)) ? &g_scalar : NULL;
secp256k1_scalar* g_scalar_ptr = (g_nonzero || testrand_bits(1)) ? g_scalar : NULL;
/* How many EC multiplications were performed in this function. */
int mults = 0;
/* How many randomization steps to apply to the input list. */
int rands = (int)testrand_bits(3);
if (rands > num_nonzero) rands = num_nonzero;
secp256k1_gej_set_infinity(&expected);
secp256k1_gej_set_infinity(expected);
secp256k1_gej_set_infinity(&gejs[0]);
secp256k1_scalar_set_int(&scalars[0], 0);
if (g_nonzero) {
/* If g_nonzero, set g_scalar to nonzero value r. */
testutil_random_scalar_order_test(&g_scalar);
testutil_random_scalar_order_test(g_scalar);
if (!nonzero_result) {
/* If expected=0 is desired, add a (a*r, -(1/a)*g) term to compensate. */
CHECK(num_nonzero > filled);
testutil_random_scalar_order_test(&sc_tmp);
secp256k1_scalar_mul(&scalars[filled], &sc_tmp, &g_scalar);
secp256k1_scalar_mul(&scalars[filled], &sc_tmp, g_scalar);
secp256k1_scalar_inverse_var(&sc_tmp, &sc_tmp);
secp256k1_scalar_negate(&sc_tmp, &sc_tmp);
secp256k1_ecmult_gen(&CTX->ecmult_gen_ctx, &gejs[filled], &sc_tmp);
@ -4941,7 +4918,7 @@ static int test_ecmult_multi_random(secp256k1_scratch *scratch) {
if (nonzero_result) {
/* Compute the expected result using normal ecmult. */
CHECK(filled <= 1);
secp256k1_ecmult(&expected, &gejs[0], &scalars[0], &g_scalar);
secp256k1_ecmult(expected, &gejs[0], &scalars[0], g_scalar);
mults += filled + g_nonzero;
}
@ -5011,6 +4988,54 @@ static int test_ecmult_multi_random(secp256k1_scratch *scratch) {
}
}
/* number of (scalars, points) inputs generated */
*inp_len = filled;
/* number of non-zero (scalars, points) inputs */
*nonzero_inp_len = num_nonzero;
/* ptr to g_scalar*/
g_scalar = g_scalar_ptr;
/* is mulciplicand of g nonzero? */
*is_g_nonzero = g_nonzero;
/* number of mults performed in this function */
*mults_performed += mults;
}
int test_ecmult_multi_random(secp256k1_scratch *scratch) {
/* Large random test for ecmult_multi_* functions which exercises:
* - Few or many inputs (0 up to 128, roughly exponentially distributed).
* - Few or many 0*P or a*INF inputs (roughly uniformly distributed).
* - Including or excluding an nonzero a*G term (or such a term at all).
* - Final expected result equal to infinity or not (roughly 50%).
* - ecmult_multi_var, ecmult_strauss_single_batch, ecmult_pippenger_single_batch
*/
/* These 4 variables define the eventual input to the ecmult_multi function.
* g_scalar is the G scalar fed to it (or NULL, possibly, if g_scalar=0), and
* scalars[0..filled-1] and gejs[0..filled-1] are the scalars and points
* which form its normal inputs. */
int filled = 0;
secp256k1_scalar g_scalar = secp256k1_scalar_zero;
secp256k1_scalar *g_scalar_ptr = &g_scalar;
secp256k1_scalar scalars[128];
secp256k1_gej gejs[128];
/* The expected result, and the computed result. */
secp256k1_gej expected, computed;
/* Variables needed for the actual input to ecmult_multi. */
secp256k1_ge ges[128];
ecmult_multi_data data;
/* How many EC multiplications were performed in this function. */
int mults = 0;
int g_nonzero, num_nonzero;
/* Which multiplication function to use */
int fn = testrand_int(3);
secp256k1_ecmult_multi_func ecmult_multi = fn == 0 ? secp256k1_ecmult_multi_var :
fn == 1 ? secp256k1_ecmult_strauss_batch_single :
secp256k1_ecmult_pippenger_batch_single;
/* generate inputs and their ecmult_multi output */
ecmult_multi_random_generate_inp(&expected, g_scalar_ptr, scalars, gejs, &filled, &num_nonzero, &g_nonzero, &mults);
/* Compute affine versions of all inputs. */
secp256k1_ge_set_all_gej_var(ges, gejs, filled);
/* Invoke ecmult_multi code. */
@ -5023,7 +5048,60 @@ static int test_ecmult_multi_random(secp256k1_scratch *scratch) {
return mults;
}
static void test_ecmult_multi_batch_single(secp256k1_ecmult_multi_func ecmult_multi) {
int test_ecmult_strauss_batch_internal_random(secp256k1_scratch *scratch) {
/* Large random test for `ecmult_strauss_batch_internal`. This test is
* very similar to `test_ecmult_multi_random`. */
/* These 4 variables define the eventual input to the ecmult_multi function.
* g_scalar is the G scalar fed to it (or NULL, possibly, if g_scalar=0), and
* scalars[0..filled-1] and gejs[0..filled-1] are the scalars and points
* which form its normal inputs. */
int filled = 0;
secp256k1_scalar g_scalar = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0);
secp256k1_scalar *g_scalar_ptr = &g_scalar;
secp256k1_scalar scalars[128];
secp256k1_gej gejs[128];
/* The expected result, and the computed result. */
secp256k1_gej expected, computed;
/* How many EC multiplications were performed in this function. */
int mults = 0;
int g_nonzero, num_nonzero;
secp256k1_scalar *scratch_scalars;
secp256k1_gej *scratch_points;
size_t checkpoint = secp256k1_scratch_checkpoint(&CTX->error_callback, scratch);
int i;
/* generate inputs and their ecmult_multi output */
ecmult_multi_random_generate_inp(&expected, g_scalar_ptr, scalars, gejs, &filled, &num_nonzero, &g_nonzero, &mults);
/* allocate inputs on the scratch space */
scratch_scalars = (secp256k1_scalar*)secp256k1_scratch_alloc(&CTX->error_callback, scratch, filled*sizeof(secp256k1_scalar));
scratch_points = (secp256k1_gej*)secp256k1_scratch_alloc(&CTX->error_callback, scratch, filled*sizeof(secp256k1_gej));
/* If scalar or point allocation fails, restore scratch space to previous state */
if (scratch_scalars == NULL || scratch_points == NULL) {
secp256k1_scratch_apply_checkpoint(&CTX->error_callback, scratch, checkpoint);
return 0;
}
/* copy the scalar and points to the scratch space */
for (i = 0; i < filled; i++) {
scratch_scalars[i] = scalars[i];
scratch_points[i] = gejs[i];
}
CHECK(secp256k1_ecmult_strauss_batch_internal(&CTX->error_callback, scratch, &computed, scratch_scalars, scratch_points, g_scalar_ptr, filled));
mults += num_nonzero + g_nonzero;
/* Compare with expected result. */
secp256k1_gej_neg(&computed, &computed);
secp256k1_gej_add_var(&computed, &computed, &expected, NULL);
CHECK(secp256k1_gej_is_infinity(&computed));
secp256k1_scratch_apply_checkpoint(&CTX->error_callback, scratch, checkpoint);
return mults;
}
void test_ecmult_multi_batch_single(secp256k1_ecmult_multi_func ecmult_multi) {
secp256k1_scalar sc;
secp256k1_ge pt;
secp256k1_gej r;
@ -5209,7 +5287,9 @@ static void test_ecmult_multi_batching(void) {
static void run_ecmult_multi_tests(void) {
secp256k1_scratch *scratch;
int64_t todo = (int64_t)320 * COUNT;
int64_t todo_multi = (int64_t)320 * COUNT;
/* todo: what should be the intial val of `todo_strauss_internal` */
int64_t todo_strauss_internal = (int64_t)320 * COUNT;
test_secp256k1_pippenger_bucket_window_inv();
test_ecmult_multi_pippenger_max_points();
@ -5220,8 +5300,11 @@ static void run_ecmult_multi_tests(void) {
test_ecmult_multi_batch_single(secp256k1_ecmult_pippenger_batch_single);
test_ecmult_multi(scratch, secp256k1_ecmult_strauss_batch_single);
test_ecmult_multi_batch_single(secp256k1_ecmult_strauss_batch_single);
while (todo > 0) {
todo -= test_ecmult_multi_random(scratch);
while (todo_multi > 0) {
todo_multi -= test_ecmult_multi_random(scratch);
}
while (todo_strauss_internal > 0) {
todo_strauss_internal -= test_ecmult_strauss_batch_internal_random(scratch);
}
secp256k1_scratch_destroy(&CTX->error_callback, scratch);
@ -7441,10 +7524,16 @@ static void run_ecdsa_wycheproof(void) {
#ifdef ENABLE_MODULE_EXTRAKEYS
# include "modules/extrakeys/tests_impl.h"
# ifdef ENABLE_MODULE_BATCH
# include "modules/extrakeys/batch_add_tests_impl.h"
# endif
#endif
#ifdef ENABLE_MODULE_SCHNORRSIG
# include "modules/schnorrsig/tests_impl.h"
# ifdef ENABLE_MODULE_BATCH
# include "modules/schnorrsig/batch_add_tests_impl.h"
# endif
#endif
#ifdef ENABLE_MODULE_MUSIG
@ -7455,7 +7544,11 @@ static void run_ecdsa_wycheproof(void) {
# include "modules/ellswift/tests_impl.h"
#endif
static void run_secp256k1_memczero_test(void) {
#ifdef ENABLE_MODULE_BATCH
# include "modules/batch/tests_impl.h"
#endif
void run_secp256k1_memczero_test(void) {
unsigned char buf1[6] = {1, 2, 3, 4, 5, 6};
unsigned char buf2[sizeof(buf1)];
@ -7809,10 +7902,16 @@ int main(int argc, char **argv) {
#ifdef ENABLE_MODULE_EXTRAKEYS
run_extrakeys_tests();
# ifdef ENABLE_MODULE_BATCH
run_batch_add_xonlypub_tweak_tests();
# endif
#endif
#ifdef ENABLE_MODULE_SCHNORRSIG
run_schnorrsig_tests();
# ifdef ENABLE_MODULE_BATCH
run_batch_add_schnorrsig_tests();
# endif
#endif
#ifdef ENABLE_MODULE_MUSIG
@ -7823,6 +7922,10 @@ int main(int argc, char **argv) {
run_ellswift_tests();
#endif
#ifdef ENABLE_MODULE_BATCH
run_batch_tests();
#endif
/* util tests */
run_secp256k1_memczero_test();
run_secp256k1_is_zero_array_test();

View file

@ -232,7 +232,7 @@ static SECP256K1_INLINE void secp256k1_memclear(void *ptr, size_t len) {
* As best as we can tell, this is sufficient to break any optimisations that
* might try to eliminate "superfluous" memsets.
* This method is used in memzero_explicit() the Linux kernel, too. Its advantage is that it
* is pretty efficient, because the compiler can still implement the memset() efficently,
* is pretty efficient, because the compiler can still implement the memset() efficiently,
* just not remove it entirely. See "Dead Store Elimination (Still) Considered Harmful" by
* Yang et al. (USENIX Security 2017) for more background.
*/

View file

@ -24,7 +24,8 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
const CCoinsViewCache& inputs, unsigned int flags, bool cacheSigStore,
bool cacheFullScriptStore, PrecomputedTransactionData& txdata,
ValidationCache& validation_cache,
std::vector<CScriptCheck>* pvChecks) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
std::vector<CScriptCheck>* pvChecks,
BatchSchnorrVerifier* batch = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
BOOST_AUTO_TEST_SUITE(txvalidationcache_tests)

View file

@ -8,6 +8,7 @@
#include <validation.h>
#include <arith_uint256.h>
#include <batchverify.h>
#include <chain.h>
#include <checkqueue.h>
#include <clientversion.h>
@ -140,7 +141,8 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
const CCoinsViewCache& inputs, unsigned int flags, bool cacheSigStore,
bool cacheFullScriptStore, PrecomputedTransactionData& txdata,
ValidationCache& validation_cache,
std::vector<CScriptCheck>* pvChecks = nullptr)
std::vector<CScriptCheck>* pvChecks = nullptr,
BatchSchnorrVerifier* batch = nullptr)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
bool CheckFinalTxAtTip(const CBlockIndex& active_chain_tip, const CTransaction& tx)
@ -2117,6 +2119,19 @@ std::optional<std::pair<ScriptError, std::string>> CScriptCheck::operator()() {
const CScript &scriptSig = ptxTo->vin[nIn].scriptSig;
const CScriptWitness *witness = &ptxTo->vin[nIn].scriptWitness;
ScriptError error{SCRIPT_ERR_UNKNOWN_ERROR};
if (m_batch) {
if(VerifyScript(scriptSig, m_tx_out.scriptPubKey, witness, nFlags, BatchingCachingTransactionSignatureChecker(ptxTo, nIn, m_tx_out.nValue, cacheStore, *m_signature_cache, *txdata, m_batch), &error)) {
return std::nullopt;
} else {
auto debug_str = strprintf("input %i of %s (wtxid %s), spending %s:%i", nIn, ptxTo->GetHash().ToString(), ptxTo->GetWitnessHash().ToString(), ptxTo->vin[nIn].prevout.hash.ToString(), ptxTo->vin[nIn].prevout.n);
// TODO: This input may not be the cause of the error. So we want some
// other type of error here that at least shows that the error occured
// in a batch context.
return std::make_pair(error, std::move(debug_str));
}
}
if (VerifyScript(scriptSig, m_tx_out.scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, m_tx_out.nValue, cacheStore, *m_signature_cache, *txdata), &error)) {
return std::nullopt;
} else {
@ -2164,7 +2179,8 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
const CCoinsViewCache& inputs, unsigned int flags, bool cacheSigStore,
bool cacheFullScriptStore, PrecomputedTransactionData& txdata,
ValidationCache& validation_cache,
std::vector<CScriptCheck>* pvChecks)
std::vector<CScriptCheck>* pvChecks,
BatchSchnorrVerifier* batch)
{
if (tx.IsCoinBase()) return true;
@ -2208,7 +2224,7 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
// spent being checked as a part of CScriptCheck.
// Verify signature
CScriptCheck check(txdata.m_spent_outputs[i], tx, validation_cache.m_signature_cache, i, flags, cacheSigStore, &txdata);
CScriptCheck check(txdata.m_spent_outputs[i], tx, validation_cache.m_signature_cache, i, flags, cacheSigStore, &txdata, batch);
if (pvChecks) {
pvChecks->emplace_back(std::move(check));
} else if (auto result = check(); result.has_value()) {
@ -2222,7 +2238,7 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
// non-upgraded nodes by banning CONSENSUS-failing
// data providers.
CScriptCheck check2(txdata.m_spent_outputs[i], tx, validation_cache.m_signature_cache, i,
flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheSigStore, &txdata);
flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheSigStore, &txdata, batch);
auto mandatory_result = check2();
if (!mandatory_result.has_value()) {
return state.Invalid(TxValidationResult::TX_NOT_STANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(result->first)), result->second);
@ -2643,6 +2659,9 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
int nInputs = 0;
int64_t nSigOpsCost = 0;
blockundo.vtxundo.reserve(block.vtx.size() - 1);
BatchSchnorrVerifier batch{};
for (unsigned int i = 0; i < block.vtx.size(); i++)
{
if (!state.IsValid()) break;
@ -2698,7 +2717,7 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
std::vector<CScriptCheck> vChecks;
bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */
TxValidationState tx_state;
if (fScriptChecks && !CheckInputScripts(tx, tx_state, view, flags, fCacheResults, fCacheResults, txsdata[i], m_chainman.m_validation_cache, parallel_script_checks ? &vChecks : nullptr)) {
if (fScriptChecks && !CheckInputScripts(tx, tx_state, view, flags, fCacheResults, fCacheResults, txsdata[i], m_chainman.m_validation_cache, parallel_script_checks ? &vChecks : nullptr, &batch)) {
// Any transaction validation failure in ConnectBlock is a block consensus failure
state.Invalid(BlockValidationResult::BLOCK_CONSENSUS,
tx_state.GetRejectReason(), tx_state.GetDebugMessage());
@ -2735,6 +2754,12 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
LogInfo("Block validation error: %s", state.ToString());
return false;
}
if (!batch.Verify()) {
LogPrintf("ERROR: %s: Batch verification failed\n", __func__);
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "block-batch-verify-failed");
}
const auto time_4{SteadyClock::now()};
m_chainman.time_verify += time_4 - time_2;
LogDebug(BCLog::BENCH, " - Verify %u txins: %.2fms (%.3fms/txin) [%.2fs (%.2fms/blk)]\n", nInputs - 1,

View file

@ -46,6 +46,7 @@
#include <utility>
#include <vector>
class BatchSchnorrVerifier;
class Chainstate;
class CTxMemPool;
class ChainstateManager;
@ -337,10 +338,11 @@ private:
bool cacheStore;
PrecomputedTransactionData *txdata;
SignatureCache* m_signature_cache;
BatchSchnorrVerifier* m_batch;
public:
CScriptCheck(const CTxOut& outIn, const CTransaction& txToIn, SignatureCache& signature_cache, unsigned int nInIn, unsigned int nFlagsIn, bool cacheIn, PrecomputedTransactionData* txdataIn) :
m_tx_out(outIn), ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), cacheStore(cacheIn), txdata(txdataIn), m_signature_cache(&signature_cache) { }
CScriptCheck(const CTxOut& outIn, const CTransaction& txToIn, SignatureCache& signature_cache, unsigned int nInIn, unsigned int nFlagsIn, bool cacheIn, PrecomputedTransactionData* txdataIn, BatchSchnorrVerifier* batchIn = nullptr) :
m_tx_out(outIn), ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), cacheStore(cacheIn), txdata(txdataIn), m_signature_cache(&signature_cache), m_batch(batchIn) { }
CScriptCheck(const CScriptCheck&) = delete;
CScriptCheck& operator=(const CScriptCheck&) = delete;