mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-03-07 14:25:09 -05:00
Merge 6f271d7bd0
into 85f96b01b7
This commit is contained in:
commit
a34605f378
48 changed files with 2764 additions and 90 deletions
src
CMakeLists.txtbatchverify.cppbatchverify.h
script
secp256k1
.gitignoreCHANGELOG.mdCMakeLists.txtMakefile.amREADME.md
ci
configure.acdoc
speedup-batch.md
speedup-batch
examples
include
src
CMakeLists.txtbench.cbench.hecmult_impl.hscalar_impl.hsecp256k1.ctests.cutil.h
modules
batch
extrakeys
schnorrsig
test
validation.cppvalidation.h
|
@ -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
46
src/batchverify.cpp
Normal 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
27
src/batchverify.h
Normal 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
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
1
src/secp256k1/.gitignore
vendored
1
src/secp256k1/.gitignore
vendored
|
@ -12,6 +12,7 @@ ecdsa_example
|
|||
schnorr_example
|
||||
ellswift_example
|
||||
musig_example
|
||||
batch_example
|
||||
*.exe
|
||||
*.so
|
||||
*.a
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
82
src/secp256k1/ci/cirrus.sh
Executable 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
|
|
@ -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"
|
||||
|
|
15
src/secp256k1/doc/speedup-batch.md
Normal file
15
src/secp256k1/doc/speedup-batch.md
Normal file
|
@ -0,0 +1,15 @@
|
|||
# Schnorrsig Batch Verification Speedup
|
||||
|
||||

|
||||
|
||||
# Tweak Pubkey Check Batch Verification Speedup
|
||||
|
||||

|
||||
|
||||
Build steps
|
||||
-----------
|
||||
To generate the above graphs on your local machine:
|
||||
|
||||
$ cd doc/speedup-batch
|
||||
$ make
|
||||
$ make speedup-batch.png
|
1
src/secp256k1/doc/speedup-batch/.gitignore
vendored
Normal file
1
src/secp256k1/doc/speedup-batch/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
*.dat
|
23
src/secp256k1/doc/speedup-batch/Makefile
Normal file
23
src/secp256k1/doc/speedup-batch/Makefile
Normal 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
|
13
src/secp256k1/doc/speedup-batch/bench.sh
Executable file
13
src/secp256k1/doc/speedup-batch/bench.sh
Executable 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"
|
137
src/secp256k1/doc/speedup-batch/bench_output.txt
Normal file
137
src/secp256k1/doc/speedup-batch/bench_output.txt
Normal 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
|
127
src/secp256k1/doc/speedup-batch/bench_output.txt.log
Normal file
127
src/secp256k1/doc/speedup-batch/bench_output.txt.log
Normal 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 =
|
41
src/secp256k1/doc/speedup-batch/plot.gp
Normal file
41
src/secp256k1/doc/speedup-batch/plot.gp
Normal 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
|
BIN
src/secp256k1/doc/speedup-batch/schnorrsig-speedup-batch.png
Normal file
BIN
src/secp256k1/doc/speedup-batch/schnorrsig-speedup-batch.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 8 KiB |
BIN
src/secp256k1/doc/speedup-batch/tweakcheck-speedup-batch.png
Normal file
BIN
src/secp256k1/doc/speedup-batch/tweakcheck-speedup-batch.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 8.4 KiB |
181
src/secp256k1/examples/batch.c
Normal file
181
src/secp256k1/examples/batch.c
Normal 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;
|
||||
}
|
110
src/secp256k1/include/secp256k1_batch.h
Normal file
110
src/secp256k1/include/secp256k1_batch.h
Normal 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 */
|
42
src/secp256k1/include/secp256k1_schnorrsig_batch.h
Normal file
42
src/secp256k1/include/secp256k1_schnorrsig_batch.h
Normal 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 */
|
50
src/secp256k1/include/secp256k1_tweak_check_batch.h
Normal file
50
src/secp256k1/include/secp256k1_tweak_check_batch.h
Normal 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 */
|
|
@ -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()
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
3
src/secp256k1/src/modules/batch/Makefile.am.include
Normal file
3
src/secp256k1/src/modules/batch/Makefile.am.include
Normal 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
|
207
src/secp256k1/src/modules/batch/main_impl.h
Normal file
207
src/secp256k1/src/modules/batch/main_impl.h
Normal 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 */
|
214
src/secp256k1/src/modules/batch/tests_impl.h
Normal file
214
src/secp256k1/src/modules/batch/tests_impl.h
Normal 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 */
|
|
@ -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
|
||||
|
|
151
src/secp256k1/src/modules/extrakeys/batch_add_impl.h
Normal file
151
src/secp256k1/src/modules/extrakeys/batch_add_impl.h
Normal 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 */
|
165
src/secp256k1/src/modules/extrakeys/batch_add_tests_impl.h
Normal file
165
src/secp256k1/src/modules/extrakeys/batch_add_tests_impl.h
Normal 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 */
|
139
src/secp256k1/src/modules/extrakeys/bench_impl.h
Normal file
139
src/secp256k1/src/modules/extrakeys/bench_impl.h
Normal 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 */
|
|
@ -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
|
||||
|
|
158
src/secp256k1/src/modules/schnorrsig/batch_add_impl.h
Normal file
158
src/secp256k1/src/modules/schnorrsig/batch_add_impl.h
Normal 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 */
|
313
src/secp256k1/src/modules/schnorrsig/batch_add_tests_impl.h
Normal file
313
src/secp256k1/src/modules/schnorrsig/batch_add_tests_impl.h
Normal 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 */
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Reference in a new issue