Files
secp256k1-zkp/configure.ac

803 lines
34 KiB
Plaintext
Raw Normal View History

2014-01-17 22:52:33 -05:00
AC_PREREQ([2.60])
# 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.
define(_PKG_VERSION_MAJOR, 0)
define(_PKG_VERSION_MINOR, 1)
define(_PKG_VERSION_PATCH, 0)
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:
# https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
# All changes in experimental modules are treated as if they don't affect the
# interface and therefore only increase the revision.
define(_LIB_VERSION_CURRENT, 0)
define(_LIB_VERSION_REVISION, 0)
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])
AC_CONFIG_AUX_DIR([autotools-aux])
AC_CONFIG_MACRO_DIR([autotools-aux/m4])
2014-01-17 22:52:33 -05:00
AC_CANONICAL_HOST
# Require Automake 1.11.2 for AM_PROG_AR
AM_INIT_AUTOMAKE([1.11.2 foreign subdir-objects])
2014-01-17 22:52:33 -05:00
# Make the compilation flags quiet unless V=1 is used.
2014-06-19 22:36:24 -04:00
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
if test "${CFLAGS+set}" = "set"; then
CFLAGS_overridden=yes
else
CFLAGS_overridden=no
fi
AC_PROG_CC
AM_PROG_AS
AM_PROG_AR
# Clear some cache variables as a workaround for a bug that appears due to a bad
# interaction between AM_PROG_AR and LT_INIT when combining MSVC's archiver lib.exe.
# https://debbugs.gnu.org/cgi/bugreport.cgi?bug=54421
AS_UNSET(ac_cv_prog_AR)
AS_UNSET(ac_cv_prog_ac_ct_AR)
LT_INIT([win32-dll])
2014-01-17 22:52:33 -05:00
build_windows=no
2014-01-17 22:52:33 -05:00
case $host_os in
*darwin*)
if test x$cross_compiling != xyes; then
configure: replace AC_PATH_PROG to AC_CHECK_PROG Bitcoin Core's `configure` script uses `AC_CHECK_PROG` to find brew in the `PATH` [1]. If found, this macro will set `BREW=brew`. When building with dependencies however the `BREW` variable is set to `no` on macOS via `depends/<host_prefix>/share/config.site` [2] and this overrides `AC_CHECK_PROG` results [3]. Ideally, secp256k1's `configure` script should follow the same logic but this is not what happens because secp256k1's `configure` uses `AC_PATH_PROG` instead which respects preset variable values (in this case for variable `BREW`) only if they are a valid path (i.e., they match `[\\/*] | ?:[\\/]*` [4]), and `no` is not a path. This commit changes `AC_PATH_PROG` to `AC_CHECK_PROG` to be consistent with Core's `AC_CHECK_PROG`. Both of these macros are supposed to find executables in the `PATH` but the difference is that former is supposed to return the full path whereas the latter is supposed to find only the program. As a result, the latter will accept even non-paths `no` as an override. Not knowing the full path is not an issue for the `configure` script because it will only execute `BREW` immediately afterwards, which works fine without the full path. (In particular, `PATH` cannot have changed in between [5].) [1] https://github.com/bitcoin/bitcoin/blob/master/configure.ac#L684 [2] https://github.com/bitcoin/bitcoin/blob/master/depends/config.site.in#L73-L76 [3] https://github.com/autotools-mirror/autoconf/blob/6d38e9fa2b39b3c3a8e4d6d7da38c59909d3f39d/lib/autoconf/programs.m4#L47 [4] https://github.com/autotools-mirror/autoconf/blob/6d38e9fa2b39b3c3a8e4d6d7da38c59909d3f39d/lib/autoconf/programs.m4#L127 [5] [3ab1178](https://github.com/bitcoin-core/secp256k1/commit/3ab1178d54029745219d67e6c305df4d7564e278)
2021-06-15 19:33:57 +03:00
AC_CHECK_PROG([BREW], brew, brew)
if test x$BREW = xbrew; then
# These Homebrew packages may be keg-only, meaning that they won't be found
# in expected paths because they may conflict with system files. Ask
# Homebrew where each one is located, then adjust paths accordingly.
if $BREW list --versions valgrind >/dev/null; then
valgrind_prefix=$($BREW --prefix valgrind 2>/dev/null)
VALGRIND_CPPFLAGS="-I$valgrind_prefix/include"
fi
else
configure: replace AC_PATH_PROG to AC_CHECK_PROG Bitcoin Core's `configure` script uses `AC_CHECK_PROG` to find brew in the `PATH` [1]. If found, this macro will set `BREW=brew`. When building with dependencies however the `BREW` variable is set to `no` on macOS via `depends/<host_prefix>/share/config.site` [2] and this overrides `AC_CHECK_PROG` results [3]. Ideally, secp256k1's `configure` script should follow the same logic but this is not what happens because secp256k1's `configure` uses `AC_PATH_PROG` instead which respects preset variable values (in this case for variable `BREW`) only if they are a valid path (i.e., they match `[\\/*] | ?:[\\/]*` [4]), and `no` is not a path. This commit changes `AC_PATH_PROG` to `AC_CHECK_PROG` to be consistent with Core's `AC_CHECK_PROG`. Both of these macros are supposed to find executables in the `PATH` but the difference is that former is supposed to return the full path whereas the latter is supposed to find only the program. As a result, the latter will accept even non-paths `no` as an override. Not knowing the full path is not an issue for the `configure` script because it will only execute `BREW` immediately afterwards, which works fine without the full path. (In particular, `PATH` cannot have changed in between [5].) [1] https://github.com/bitcoin/bitcoin/blob/master/configure.ac#L684 [2] https://github.com/bitcoin/bitcoin/blob/master/depends/config.site.in#L73-L76 [3] https://github.com/autotools-mirror/autoconf/blob/6d38e9fa2b39b3c3a8e4d6d7da38c59909d3f39d/lib/autoconf/programs.m4#L47 [4] https://github.com/autotools-mirror/autoconf/blob/6d38e9fa2b39b3c3a8e4d6d7da38c59909d3f39d/lib/autoconf/programs.m4#L127 [5] [3ab1178](https://github.com/bitcoin-core/secp256k1/commit/3ab1178d54029745219d67e6c305df4d7564e278)
2021-06-15 19:33:57 +03:00
AC_CHECK_PROG([PORT], port, port)
# If homebrew isn't installed and macports is, add the macports default paths
# as a last resort.
configure: replace AC_PATH_PROG to AC_CHECK_PROG Bitcoin Core's `configure` script uses `AC_CHECK_PROG` to find brew in the `PATH` [1]. If found, this macro will set `BREW=brew`. When building with dependencies however the `BREW` variable is set to `no` on macOS via `depends/<host_prefix>/share/config.site` [2] and this overrides `AC_CHECK_PROG` results [3]. Ideally, secp256k1's `configure` script should follow the same logic but this is not what happens because secp256k1's `configure` uses `AC_PATH_PROG` instead which respects preset variable values (in this case for variable `BREW`) only if they are a valid path (i.e., they match `[\\/*] | ?:[\\/]*` [4]), and `no` is not a path. This commit changes `AC_PATH_PROG` to `AC_CHECK_PROG` to be consistent with Core's `AC_CHECK_PROG`. Both of these macros are supposed to find executables in the `PATH` but the difference is that former is supposed to return the full path whereas the latter is supposed to find only the program. As a result, the latter will accept even non-paths `no` as an override. Not knowing the full path is not an issue for the `configure` script because it will only execute `BREW` immediately afterwards, which works fine without the full path. (In particular, `PATH` cannot have changed in between [5].) [1] https://github.com/bitcoin/bitcoin/blob/master/configure.ac#L684 [2] https://github.com/bitcoin/bitcoin/blob/master/depends/config.site.in#L73-L76 [3] https://github.com/autotools-mirror/autoconf/blob/6d38e9fa2b39b3c3a8e4d6d7da38c59909d3f39d/lib/autoconf/programs.m4#L47 [4] https://github.com/autotools-mirror/autoconf/blob/6d38e9fa2b39b3c3a8e4d6d7da38c59909d3f39d/lib/autoconf/programs.m4#L127 [5] [3ab1178](https://github.com/bitcoin-core/secp256k1/commit/3ab1178d54029745219d67e6c305df4d7564e278)
2021-06-15 19:33:57 +03:00
if test x$PORT = xport; then
CPPFLAGS="$CPPFLAGS -isystem /opt/local/include"
LDFLAGS="$LDFLAGS -L/opt/local/lib"
fi
fi
fi
;;
cygwin*|mingw*)
build_windows=yes
;;
esac
# Try if some desirable compiler flags are supported and append them to SECP_CFLAGS.
#
# These are our own flags, so we append them to our own SECP_CFLAGS variable (instead of CFLAGS) as
# recommended in the automake manual (Section "Flag Variables Ordering"). CFLAGS belongs to the user
# and we are not supposed to touch it. In the Makefile, we will need to ensure that SECP_CFLAGS
# is prepended to CFLAGS when invoking the compiler so that the user always has the last word (flag).
#
# Another advantage of not touching CFLAGS is that the contents of CFLAGS will be picked up by
# libtool for compiling helper executables. For example, when compiling for Windows, libtool will
# generate entire wrapper executables (instead of simple wrapper scripts as on Unix) to ensure
# proper operation of uninstalled programs linked by libtool against the uninstalled shared library.
# These executables are compiled from C source file for which our flags may not be appropriate,
# e.g., -std=c89 flag has lead to undesirable warnings in the past.
#
# TODO We should analogously not touch CPPFLAGS and LDFLAGS but currently there are no issues.
AC_DEFUN([SECP_TRY_APPEND_DEFAULT_CFLAGS], [
2022-03-09 21:06:43 +01:00
# GCC and compatible (incl. clang)
if test "x$GCC" = "xyes"; then
# Try to append -Werror to CFLAGS temporarily. Otherwise checks for some unsupported
# flags will succeed.
# Note that failure to append -Werror does not necessarily mean that -Werror is not
# supported. The compiler may already be warning about something unrelated, for example
# about some path issue. If that is the case, -Werror cannot be used because all
# of those warnings would be turned into errors.
2022-03-09 21:06:43 +01:00
SECP_TRY_APPEND_DEFAULT_CFLAGS_saved_CFLAGS="$CFLAGS"
SECP_TRY_APPEND_CFLAGS([-Werror], CFLAGS)
2022-03-09 21:06:43 +01:00
SECP_TRY_APPEND_CFLAGS([-std=c89 -pedantic -Wno-long-long -Wnested-externs -Wshadow -Wstrict-prototypes -Wundef], $1) # GCC >= 3.0, -Wlong-long is implied by -pedantic.
SECP_TRY_APPEND_CFLAGS([-Wno-overlength-strings], $1) # GCC >= 4.2, -Woverlength-strings is implied by -pedantic.
SECP_TRY_APPEND_CFLAGS([-Wall], $1) # GCC >= 2.95 and probably many other compilers
SECP_TRY_APPEND_CFLAGS([-Wno-unused-function], $1) # GCC >= 3.0, -Wunused-function is implied by -Wall.
SECP_TRY_APPEND_CFLAGS([-Wextra], $1) # GCC >= 3.4, this is the newer name of -W, which we don't use because older GCCs will warn about unused functions.
SECP_TRY_APPEND_CFLAGS([-Wcast-align], $1) # GCC >= 2.95
SECP_TRY_APPEND_CFLAGS([-Wcast-align=strict], $1) # GCC >= 8.0
SECP_TRY_APPEND_CFLAGS([-Wconditional-uninitialized], $1) # Clang >= 3.0 only
SECP_TRY_APPEND_CFLAGS([-Wreserved-identifier], $1) # Clang >= 13.0 only
SECP_TRY_APPEND_CFLAGS([-Wtrailing-whitespace=any], $1) # GCC >= 15.0
SECP_TRY_APPEND_CFLAGS([-Wleading-whitespace=spaces], $1) # GCC >= 15.0
2022-03-09 21:06:43 +01:00
CFLAGS="$SECP_TRY_APPEND_DEFAULT_CFLAGS_saved_CFLAGS"
fi
# MSVC
# Assume MSVC if we're building for Windows but not with GCC or compatible;
# libtool makes the same assumption internally.
# Note that "/opt" and "-opt" are equivalent for MSVC; we use "-opt" because "/opt" looks like a path.
if test x"$GCC" != x"yes" && test x"$build_windows" = x"yes"; then
2023-05-24 16:57:32 +01:00
SECP_TRY_APPEND_CFLAGS([-W3], $1) # Production quality warning level.
SECP_TRY_APPEND_CFLAGS([-wd4146], $1) # Disable warning C4146 "unary minus operator applied to unsigned type, result still unsigned".
SECP_TRY_APPEND_CFLAGS([-wd4244], $1) # Disable warning C4244 "'conversion' conversion from 'type1' to 'type2', possible loss of data".
SECP_TRY_APPEND_CFLAGS([-wd4267], $1) # Disable warning C4267 "'var' : conversion from 'size_t' to 'type', possible loss of data".
# Eliminate deprecation warnings for the older, less secure functions.
CPPFLAGS="-D_CRT_SECURE_NO_WARNINGS $CPPFLAGS"
2022-03-09 21:06:43 +01:00
fi
])
SECP_TRY_APPEND_DEFAULT_CFLAGS(SECP_CFLAGS)
###
### Define config arguments
###
# In dev mode, we enable all binaries and modules by default but individual options can still be overridden explicitly.
# Check for dev mode first because SECP_SET_DEFAULT needs enable_dev_mode set.
AC_ARG_ENABLE(dev_mode, [], [],
[enable_dev_mode=no])
2014-01-17 22:52:33 -05:00
AC_ARG_ENABLE(benchmark,
AS_HELP_STRING([--enable-benchmark],[compile benchmark [default=yes]]), [],
[SECP_SET_DEFAULT([enable_benchmark], [yes], [yes])])
2014-01-17 22:52:33 -05:00
AC_ARG_ENABLE(coverage,
AS_HELP_STRING([--enable-coverage],[enable coverage analysis support [default=no]]), [],
[SECP_SET_DEFAULT([enable_coverage], [no], [no])])
2014-01-17 22:52:33 -05:00
AC_ARG_ENABLE(tests,
AS_HELP_STRING([--enable-tests],[compile tests [default=yes]]), [],
[SECP_SET_DEFAULT([enable_tests], [yes], [yes])])
2014-01-17 22:52:33 -05:00
2022-12-06 23:12:15 -05:00
AC_ARG_ENABLE(ctime_tests,
AS_HELP_STRING([--enable-ctime-tests],[compile constant-time tests [default=yes if valgrind enabled]]), [],
[SECP_SET_DEFAULT([enable_ctime_tests], [auto], [auto])])
2015-11-26 00:06:41 +01:00
AC_ARG_ENABLE(experimental,
AS_HELP_STRING([--enable-experimental],[allow experimental configure options [default=no]]), [],
[SECP_SET_DEFAULT([enable_experimental], [no], [yes])])
2015-11-26 00:06:41 +01:00
AC_ARG_ENABLE(exhaustive_tests,
AS_HELP_STRING([--enable-exhaustive-tests],[compile exhaustive tests [default=yes]]), [],
[SECP_SET_DEFAULT([enable_exhaustive_tests], [yes], [yes])])
AC_ARG_ENABLE(examples,
AS_HELP_STRING([--enable-examples],[compile the examples [default=no]]), [],
[SECP_SET_DEFAULT([enable_examples], [no], [yes])])
AC_ARG_ENABLE(module_ecdh,
AS_HELP_STRING([--enable-module-ecdh],[enable ECDH module [default=yes]]), [],
[SECP_SET_DEFAULT([enable_module_ecdh], [yes], [yes])])
AC_ARG_ENABLE(module_recovery,
AS_HELP_STRING([--enable-module-recovery],[enable ECDSA pubkey recovery module [default=no]]), [],
[SECP_SET_DEFAULT([enable_module_recovery], [no], [yes])])
AC_ARG_ENABLE(module_extrakeys,
AS_HELP_STRING([--enable-module-extrakeys],[enable extrakeys module [default=yes]]), [],
[SECP_SET_DEFAULT([enable_module_extrakeys], [yes], [yes])])
AC_ARG_ENABLE(module_schnorrsig,
AS_HELP_STRING([--enable-module-schnorrsig],[enable schnorrsig module [default=yes]]), [],
[SECP_SET_DEFAULT([enable_module_schnorrsig], [yes], [yes])])
AC_ARG_ENABLE(module_musig,
AS_HELP_STRING([--enable-module-musig],[enable MuSig2 module [default=yes]]), [],
[SECP_SET_DEFAULT([enable_module_musig], [yes], [yes])])
AC_ARG_ENABLE(module_schnorrsig_halfagg,
AS_HELP_STRING([--enable-module-schnorrsig-halfagg],[enable schnorrsig half-aggregation module (experimental) [default=no]]), [],
[SECP_SET_DEFAULT([enable_module_schnorrsig_halfagg], [no], [yes])])
AC_ARG_ENABLE(module_ellswift,
AS_HELP_STRING([--enable-module-ellswift],[enable ElligatorSwift module [default=yes]]), [],
[SECP_SET_DEFAULT([enable_module_ellswift], [yes], [yes])])
2016-07-07 00:47:41 +02:00
AC_ARG_ENABLE(module_generator,
AS_HELP_STRING([--enable-module-generator],[enable NUMS generator module [default=no]]),
[],
[SECP_SET_DEFAULT([enable_module_generator], [no], [yes])])
Pedersen commitments, borromean ring signatures, and ZK range proofs. This commit adds three new cryptosystems to libsecp256k1: Pedersen commitments are a system for making blinded commitments to a value. Functionally they work like: commit_b,v = H(blind_b || value_v), except they are additively homorphic, e.g. C(b1, v1) - C(b2, v2) = C(b1 - b2, v1 - v2) and C(b1, v1) - C(b1, v1) = 0, etc. The commitments themselves are EC points, serialized as 33 bytes. In addition to the commit function this implementation includes utility functions for verifying that a set of commitments sums to zero, and for picking blinding factors that sum to zero. If the blinding factors are uniformly random, pedersen commitments have information theoretic privacy. Borromean ring signatures are a novel efficient ring signature construction for AND/OR admissions policies (the code here implements an AND of ORs, each of any size). This construction requires 32 bytes of signature per pubkey used plus 32 bytes of constant overhead. With these you can construct signatures like "Given pubkeys A B C D E F G, the signer knows the discrete logs satisifying (A || B) & (C || D || E) & (F || G)". ZK range proofs allow someone to prove a pedersen commitment is in a particular range (e.g. [0..2^64)) without revealing the specific value. The construction here is based on the above borromean ring signature and uses a radix-4 encoding and other optimizations to maximize efficiency. It also supports encoding proofs with a non-private base-10 exponent and minimum-value to allow trading off secrecy for size and speed (or just avoiding wasting space keeping data private that was already public due to external constraints). A proof for a 32-bit mantissa takes 2564 bytes, but 2048 bytes of this can be used to communicate a private message to a receiver who shares a secret random seed with the prover. Also: get rid of precomputed H tables (Pieter Wuille)
2015-08-05 19:04:14 +02:00
AC_ARG_ENABLE(module_rangeproof,
AS_HELP_STRING([--enable-module-rangeproof],[enable Pedersen / zero-knowledge range proofs module [default=no]]),
[],
[SECP_SET_DEFAULT([enable_module_rangeproof], [no], [yes])])
Pedersen commitments, borromean ring signatures, and ZK range proofs. This commit adds three new cryptosystems to libsecp256k1: Pedersen commitments are a system for making blinded commitments to a value. Functionally they work like: commit_b,v = H(blind_b || value_v), except they are additively homorphic, e.g. C(b1, v1) - C(b2, v2) = C(b1 - b2, v1 - v2) and C(b1, v1) - C(b1, v1) = 0, etc. The commitments themselves are EC points, serialized as 33 bytes. In addition to the commit function this implementation includes utility functions for verifying that a set of commitments sums to zero, and for picking blinding factors that sum to zero. If the blinding factors are uniformly random, pedersen commitments have information theoretic privacy. Borromean ring signatures are a novel efficient ring signature construction for AND/OR admissions policies (the code here implements an AND of ORs, each of any size). This construction requires 32 bytes of signature per pubkey used plus 32 bytes of constant overhead. With these you can construct signatures like "Given pubkeys A B C D E F G, the signer knows the discrete logs satisifying (A || B) & (C || D || E) & (F || G)". ZK range proofs allow someone to prove a pedersen commitment is in a particular range (e.g. [0..2^64)) without revealing the specific value. The construction here is based on the above borromean ring signature and uses a radix-4 encoding and other optimizations to maximize efficiency. It also supports encoding proofs with a non-private base-10 exponent and minimum-value to allow trading off secrecy for size and speed (or just avoiding wasting space keeping data private that was already public due to external constraints). A proof for a 32-bit mantissa takes 2564 bytes, but 2048 bytes of this can be used to communicate a private message to a receiver who shares a secret random seed with the prover. Also: get rid of precomputed H tables (Pieter Wuille)
2015-08-05 19:04:14 +02:00
AC_ARG_ENABLE(module_surjectionproof,
AS_HELP_STRING([--enable-module-surjectionproof],[enable surjection proof module [default=no]]),
[],
[SECP_SET_DEFAULT([enable_module_surjectionproof], [no], [yes])])
AC_ARG_ENABLE(reduced_surjection_proof_size,
AS_HELP_STRING([--enable-reduced-surjection-proof-size],[use reduced surjection proof size (disabling parsing and verification) [default=no]]),
[],
[SECP_SET_DEFAULT([enable_reduced_surjection_proof_size], [no], [no])])
AC_ARG_ENABLE(module_whitelist,
AS_HELP_STRING([--enable-module-whitelist],[enable key whitelisting module [default=no]]),
[],
[SECP_SET_DEFAULT([enable_module_whitelist], [no], [yes])])
AC_ARG_ENABLE(module_ecdsa_s2c,
AS_HELP_STRING([--enable-module-ecdsa-s2c],[enable ECDSA sign-to-contract module [default=no]]),
[],
[SECP_SET_DEFAULT([enable_module_ecdsa_s2c], [no], [yes])])
AC_ARG_ENABLE(module_bppp,
AS_HELP_STRING([--enable-module-bppp],[enable Bulletproofs++ module (experimental)]),
[],
[SECP_SET_DEFAULT([enable_module_bppp], [no], [yes])])
AC_ARG_ENABLE(module_ecdsa-adaptor,
AS_HELP_STRING([--enable-module-ecdsa-adaptor],[enable ECDSA adaptor module [default=no]]),
[],
[SECP_SET_DEFAULT([enable_module_ecdsa_adaptor], [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])])
2026-08-31 00:05:16 +02:00
AC_ARG_ENABLE(module_frost,
AS_HELP_STRING([--enable-module-frost],[enable FROST module (experimental)]),
[],
[SECP_SET_DEFAULT([enable_module_frost], [no], [yes])])
chilldkg: Phase 0 - module scaffolding and build wiring Add an empty, experimental `chilldkg` module as the foundation for a ChillDKG implementation (distributed key generation for FROST) per the bip-frost-dkg BIP draft (v0.3.0-dev): https://github.com/BlockstreamResearch/bip-frost-dkg The module lives in src/modules/chilldkg/ (separate from the frost module, per the implementation plan in .idea/docs/ chilldkg-implementation-plan.md: FROST signing (BIP 445) and ChillDKG are separate BIPs with separate reference repos, test vectors and review cycles; the dependency between them is one-way bytes). New files: - include/secp256k1_chilldkg.h: public header skeleton with the same "EXTREMELY DANGEROUS / work in progress" warning style as secp256k1_frost.h, plus a note that the BIP is a draft and tagged hashes/wire formats may change. No API yet (Phase 3+). - src/modules/chilldkg/main_impl.h: implementation skeleton including the public header. - src/modules/chilldkg/tests_impl.h: trivial scaffolding unit test (chilldkg_scaffolding_test) registered via the tests_chilldkg[] CASE1 array used by this repo's unit-test framework. - src/modules/chilldkg/Makefile.am.include: autotools file list, mirroring the frost module's. - src/modules/chilldkg/chilldkg.md: module doc stub (purpose, draft status, dependency on the schnorrsig and ecdh modules). Build wiring (mirrors the frost module exactly): - configure.ac: --enable-module-chilldkg (default no, experimental gate), dependency errors when schnorrsig or ecdh are explicitly disabled, AM_CONDITIONAL(ENABLE_MODULE_CHILLDKG), summary line. - Makefile.am: include src/modules/chilldkg/Makefile.am.include under ENABLE_MODULE_CHILLDKG. - src/secp256k1.c: guarded include of modules/chilldkg/main_impl.h after the frost module. - src/tests.c: guarded include of tests_impl.h and MAKE_TEST_MODULE(chilldkg) registration. - CMakeLists.txt: SECP256K1_ENABLE_MODULE_CHILLDKG option (OFF) + summary line. - src/CMakeLists.txt: dependency checks on SECP256K1_ENABLE_MODULE_SCHNORRSIG and SECP256K1_ENABLE_MODULE_ECDH, ENABLE_MODULE_CHILLDKG=1 compile definition, public header export. Verified: - ./autogen.sh && ./configure --enable-experimental --enable-module-chilldkg --enable-module-schnorrsig --enable-module-ecdh && make check: PASS 3/3 (tests, noverify_tests, exhaustive_tests). - configure fails with a clear error when schnorrsig or ecdh are disabled, or when experimental is not enabled. - CMake build with SECP256K1_ENABLE_MODULE_CHILLDKG=ON: ctest 345/345 passed; dependency errors fire correctly when schnorrsig/ecdh OFF.
2026-08-31 01:37:22 +02:00
AC_ARG_ENABLE(module_chilldkg,
AS_HELP_STRING([--enable-module-chilldkg],[enable ChillDKG module (experimental)]),
[],
[SECP_SET_DEFAULT([enable_module_chilldkg], [no], [yes])])
iceberg: add the Iceberg threshold-MuSig module Port the experimental Iceberg module from the benchmark-iceberg tree (github.com/furszy/benchmark-iceberg, sources/secp256k1-kmp/native/ secp256k1) into this repo. Iceberg is a threshold scheme that lets a group of parties stand in for a single MuSig2 (BIP 327) participant: the group produces one ordinary MuSig2 public nonce and one ordinary MuSig2 partial signature, so cosigners cannot tell a group is involved and need no changes. Nonces are derived from a caller-chosen per-session label (sid32) rather than stored, so no signer holds a secret nonce between rounds; labels are public but must never be reused. A quorum of 2t-1 members (of whom up to t-1 may be corrupt) is needed in each round, so the threshold is at most half the group rounded up; combined with the scheme's other constraints the smallest usable group is 2-of-4. See doc/iceberg.md and the module header for the full usage notes. Module layout (src/modules/iceberg/, layered bottom-up, each layer may only use the ones above it -- that ordering is also the constant-time story): - scalar_poly.{h,_impl.h}: secret-carrying polynomial arithmetic, keeping secrets away from inversions (documented in the header). - rss.{h,_impl.h}: replicated secret sharing evaluation. - vpss.{h,_impl.h}: verifiable public shares; variable-time by design, sees only participant indices and published points. - keygen_impl.h: distributed key generation producing one share per member. - session_impl.h: nonce_gen/nonce_agg and partial_sign/ partial_sig_agg producing plain MuSig2 objects. - tests_impl.h: 28 tests including the shipped vectors.h vector suite and dealer known-answer tests. - bench_impl.h: benchmark definitions (wired in a follow-up commit). Public headers: include/secp256k1_iceberg.h (installed) and include/secp256k1_iceberg_dealer.h (in-tree only: a trusted dealer is not part of the shipped API, but tests, benchmarks and the example need to deal shares). Content adaptations relative to the source tree (the only changes to the ported code): three secp256k1_musig_nonce_process call sites in tests_impl.h gained a NULL adaptor argument, because this repo's musig is the zkp variant whose public nonce_process takes an optional adaptor point. All musig internals the module uses (ge_parse_ext, ge_serialize_ext, keyaggcoef, aggnonce_load, pubnonce_save, partial_sig_save, nonce_process_internal) are identical in both trees, as are all core headers the module touches; nothing else needed adaptation. Build wiring mirrors the chilldkg module: - configure.ac: --enable-module-iceberg (default no, experimental gate), hard dependency on the musig module with a configure error if musig is explicitly disabled (musig itself pulls in schnorrsig), AM_CONDITIONAL(ENABLE_MODULE_ICEBERG), summary line. - Makefile.am: include src/modules/iceberg/Makefile.am.include under the conditional. - src/secp256k1.c: guarded include of modules/iceberg/main_impl.h after the chilldkg block (musig is included earlier, so its internals are in scope). - src/tests.c: module test registration via MAKE_TEST_MODULE(iceberg). - CMakeLists.txt / src/CMakeLists.txt: SECP256K1_ENABLE_MODULE_ICEBERG option (OFF) with a dependency check on SECP256K1_ENABLE_MODULE_MUSIG (placed before the musig block so the force-enable takes effect), ENABLE_MODULE_ICEBERG=1 compile definition, public header export, summary line. Verified: ./configure --enable-experimental --enable-module-iceberg && make check passes; ./tests --target=iceberg runs the full module suite (28/28); CMake build + ctest pass; the musig dependency error fires correctly in both build systems.
2026-08-31 12:24:48 +02:00
AC_ARG_ENABLE(module_iceberg,
AS_HELP_STRING([--enable-module-iceberg],[enable Iceberg threshold-MuSig module (experimental)]),
[],
[SECP_SET_DEFAULT([enable_module_iceberg], [no], [yes])])
prefractal: add the nested FROST+MuSig2 module (API, implementation, wiring) Adds `prefractal`, an experimental module that lets a FROST t-of-n group occupy ONE participant slot of an ordinary MuSig2 (BIP 327) session. Each member computes s_i = k1_i + b_frost*b_musig*k2_i + e*a*lambda_i*g*gacc*d_i and the group publishes one ordinary MuSig2 public nonce and one ordinary MuSig2 partial signature, so cosigners need no support for it and cannot tell a group is involved. Four public functions, all sessionless (every call takes its session parameters explicitly, so there are no new opaque types, magics or *_SIZE constants to keep synchronised): secp256k1_prefractal_nonce_agg group wire nonce + unscaled aggnonce secp256k1_prefractal_sign one member's partial signature secp256k1_prefractal_partial_sig_verify identifiable abort secp256k1_prefractal_partial_sig_agg sum -> musig partial signature Three deliberate deviations from BIP 445, all documented in the public header: 1. b_frost does not commit to the message. The target protocols publish the group's wire nonce before the message exists, so a message-committing coefficient could not be computed in round one and rebuilt later. The outer b_musig does commit to the message and multiplies this one, so the product still binds it. Same trade the iceberg module makes, for the same reason. The preimage is BIP 445's with the message dropped and the group key carried in full rather than x-only, since it is used as a full point downstream. 2. There is NO g_frost factor. Stock FROST normalises its threshold key to even Y (g_times_gacc_parity = gacc_parity ^ pk_odd, frost/session_impl.h :664) because it produces a BIP 340 x-only signature. Here the threshold key is an inner participant of the outer key aggregation and is used as a full point, so all key-side parity normalisation happens once, at the aggregate level, off the OUTER keyagg cache. Note this is NOT implied by the tweak cache being the identity: with an identity cache g_frost is still -1 for every odd-Y group key, i.e. about half of them. Importing frost's key-side parity here would yield a signer that works for even-Y groups and fails for odd-Y ones. 3. The FROST tweak cache must be the identity (tacc == 0, gacc_parity == 0). Checked in sign and partial_sig_verify, not only in partial_sig_agg, so the key a member signs under is tied to the cache that was validated; sign and verify additionally require thresh_pk to equal the cache's own key so the two arguments cannot disagree. The verification equation lives in one helper used both by sign's BIP 445 self-check and by partial_sig_verify, so the two cannot drift apart. Build wiring. Three files order their module blocks differently and the constraints point in opposite directions: - src/secp256k1.c: the include goes AFTER frost and musig, because the module calls their static internals. - src/CMakeLists.txt: the block goes BEFORE both, because its set() calls are only observed by blocks that run later. - configure.ac: the block likewise goes before the musig block, NOT at iceberg's position further down. configure.ac orders musig and frost ahead of iceberg, and iceberg's late enable_module_musig=yes is harmless only because musig defaults to yes. frost defaults to no, so a late force-enable would leave -DENABLE_MODULE_FROST=1 unemitted while AM_CONDITIONAL still observed the mutation - a library whose secp256k1.c never included frost, built alongside frost's own sources. frost is also the first default-OFF module anything depends on, which breaks the dependency-guard idiom used everywhere else in both build systems: the existing "DEFINED X AND NOT X" (CMake) and "x$X = xno" (autotools) tests read as "the user disabled it explicitly" only for default-ON modules, and are true by default for a default-OFF one. Since neither build system can distinguish an explicit disable from the default once both are in the cache, enabling prefractal simply implies frost; the guard is kept for musig, where it still means what it says. The CMake block additionally lifts both dependencies into the parent scope so the top-level configuration summary reports what was actually built rather than printing "frost OFF" while compiling frost in. Verified on both build systems: cmake -B build -DSECP256K1_ENABLE_MODULE_PREFRACTAL=ON -DSECP256K1_BUILD_TESTS=ON -> musig/frost/prefractal all ON, tests pass, 4 prefractal symbols exported cmake -B build -DSECP256K1_BUILD_TESTS=ON -> prefractal OFF, default build unchanged, tests pass ./configure --enable-experimental --enable-module-prefractal && make && make check -> frost=yes forced on, -DENABLE_MODULE_FROST=1 emitted, 3/3 pass ./configure --enable-module-prefractal -> correctly refused: "Prefractal module is experimental" tests_impl.h is a placeholder here so the module links; the real suite lands next.
2026-09-04 00:44:43 +02:00
AC_ARG_ENABLE(module_prefractal,
AS_HELP_STRING([--enable-module-prefractal],[enable Prefractal nested FROST+MuSig2 module (experimental)]),
[],
[SECP_SET_DEFAULT([enable_module_prefractal], [no], [yes])])
build: wire the frost_enrollment module into both build systems Second of six commits adding the frost_enrollment module. This one is scaffolding only: the five entry points are stubs that validate their pointer arguments, zero their outputs and return 0. What is being verified here is that the module configures, compiles, links, exports its symbols and registers its test module in both build systems -- so that the next commit changes nothing but arithmetic. Ordering is the one thing in this commit that can go silently wrong, and it goes wrong in opposite directions in the two build systems: - configure.ac executes its `if` blocks in file order, and enable_module_frost defaults to no (configure.ac:243). A block placed after the frost block at :601 that sets enable_module_frost=yes flips the variable too late: AM_CONDITIONAL goes true, so the header is installed and the Makefile fragment is pulled in, but -DENABLE_MODULE_FROST=1 is never appended, so src/secp256k1.c never includes frost's implementation and every secp256k1_frost_* symbol fails to link. The new block therefore goes ahead of both the frost block and prefractal's, which documents the same trap. - src/CMakeLists.txt processes dependents FIRST, so the same block goes above the FROST block there, beside prefractal's. Verified rather than assumed: configuring with ONLY --enable-module-frost-enrollment emits -DENABLE_MODULE_FROST=1 alongside -DENABLE_MODULE_FROST_ENROLLMENT=1, and the CMake summary prints "frost ON" for the same configuration -- the latter is what the PARENT_SCOPE lift buys, since the summary runs after add_subdirectory(src) and would otherwise report a module it is compiling in as OFF. The dependency guard is prefractal's implies-frost idiom, copied verbatim along with its reasoning. frost is default-OFF, so the `test x"$enable_module_frost" = x"no"` / `DEFINED X AND NOT X` guard every other module uses -- which reads as "the user disabled it explicitly" for a default-ON dependency -- is true by default here and cannot tell an explicit --disable-module-frost from the default once both are in the cache. Enabling frost-enrollment simply implies frost, with no error. The one frost-module change in the whole series is in this commit: src/modules/frost/session.h gains a declaration for secp256k1_frost_sort_ids, which is defined at session_impl.h:517 and declared nowhere. The params hash needs it to canonicalize identifier order. Prefractal reaches frost's statics through translation-unit ordering alone; rather than inherit reuse-by-link-order, this declares the function where keygen.h:48 already declares derive_pubshare_at, so the reuse goes through an interface. No behavior change: it is a declaration for an existing static definition in the same TU. CI wiring is two files, and skipping either half fails quietly: - ci/ci.sh gets FROST_ENROLLMENT in the reproduction header's variable list and --enable-module-frost-enrollment="$FROST_ENROLLMENT" after the prefractal line. - .github/workflows/ci.yml gets FROST_ENROLLMENT at every PREFRACTAL site: the global default, 11 inline matrix entries and 10 job-level env blocks. Without the default, ci.sh runs under set -eux with an empty $FROST_ENROLLMENT, passes --enable-module-frost-enrollment="", `test x"" = x"yes"` is false, and the module is off in all of CI while ci.sh visibly has the plumbing. Verified programmatically over the parsed workflow: across the 106 effective job contexts, PREFRACTAL and FROST_ENROLLMENT now agree in every single one (45 set to yes, no mismatches), no context sets FROST_ENROLLMENT without FROST or without EXPERIMENTAL, and no context leaves it undefined. ci.sh passes sh -n. The stub test is not a placeholder that has to be deleted later: every entry point must reject an empty helper set and leave its output zeroed, which is true of the stubs and stays true of the finished implementation, so it doubles as the check that all five symbols are reachable from the test binary. Verification. Autotools: ./autogen.sh, then a frost-enrollment-only configure and a full configure with frost, chilldkg, iceberg, prefractal and frost-enrollment all on -- both build with zero warnings under the project's -Werror-grade flag set, ./tests and ./exhaustive_tests exit 0, and `./tests -l` lists the frost_enrollment module. CMake: configure with -DSECP256K1_EXPERIMENTAL=ON -DSECP256K1_ENABLE_MODULE_FROST_ENROLLMENT=ON builds clean and ctest passes 391 tests. nm shows the five new symbols exported from libsecp256k1.so; tools/symbol-check.py could not be run here because python3-lief is not installed in this environment, but all five carry the required secp256k1_ prefix. make dist succeeds and the tarball carries src/modules/frost_enrollment/frost_enrollment.md alongside the other module documents. One unrelated observation from this build: a stale src/ctime_tests-ctime_tests.o left over from an earlier configure with a different module set will fail to link, because automake does not track CPPFLAGS changes across reconfigures. make clean between configurations with different module sets, not a fault in this change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 03:53:03 +02:00
AC_ARG_ENABLE(module_frost_enrollment,
AS_HELP_STRING([--enable-module-frost-enrollment],[enable FROST enrollment module (experimental)]),
[],
[SECP_SET_DEFAULT([enable_module_frost_enrollment], [no], [yes])])
# Test-only override of the (autodetected by the C code) "widemul" setting.
2022-07-27 11:09:51 -04:00
# Legal values are:
# * int64 (for [u]int64_t),
# * int128 (for [unsigned] __int128),
# * int128_struct (for int128 implemented as a structure),
# * and auto (the default).
AC_ARG_WITH([test-override-wide-multiply], [] ,[set_widemul=$withval], [set_widemul=auto])
2014-01-17 22:52:33 -05:00
2023-05-11 10:03:23 +01:00
AC_ARG_WITH([asm], [AS_HELP_STRING([--with-asm=x86_64|arm32|no|auto],
[assembly to use (experimental: arm32) [default=auto]])],[req_asm=$withval], [req_asm=auto])
2014-12-12 16:20:47 +01:00
AC_ARG_WITH([ecmult-window], [AS_HELP_STRING([--with-ecmult-window=SIZE],
[window size for ecmult precomputation for verification, specified as integer in range [2..24].]
[Larger values result in possibly better performance at the cost of an exponentially larger precomputed table.]
2020-09-25 20:06:36 -07:00
[The table will store 2^(SIZE-1) * 64 bytes of data but can be larger in memory due to platform-specific padding and alignment.]
[A window size larger than 15 will require you delete the prebuilt precomputed_ecmult.c file so that it can be rebuilt.]
[For very large window sizes, use "make -j 1" to reduce memory use during compilation.]
[The default value is a reasonable setting for desktop machines (currently 15). [default=15]]
)],
[set_ecmult_window=$withval], [set_ecmult_window=15])
AC_ARG_WITH([ecmult-gen-kb], [AS_HELP_STRING([--with-ecmult-gen-kb=2|22|86],
[The size of the precomputed table for signing in multiples of 1024 bytes (on typical platforms).]
[Larger values result in possibly better signing/keygeneration performance at the cost of a larger table.]
[The default value is a reasonable setting for desktop machines (currently 86). [default=86]]
)],
[set_ecmult_gen_kb=$withval], [set_ecmult_gen_kb=86])
AC_ARG_WITH([valgrind], [AS_HELP_STRING([--with-valgrind=yes|no|auto],
[Build with extra checks for running inside Valgrind [default=auto]]
)],
[req_valgrind=$withval], [req_valgrind=auto])
###
### Handle config options (except for modules)
###
if test x"$req_valgrind" = x"no"; then
enable_valgrind=no
else
SECP_VALGRIND_CHECK
if test x"$has_valgrind" != x"yes"; then
if test x"$req_valgrind" = x"yes"; then
AC_MSG_ERROR([Valgrind support explicitly requested but valgrind/memcheck.h header not available])
fi
enable_valgrind=no
else
enable_valgrind=yes
fi
fi
2022-12-06 23:12:15 -05:00
if test x"$enable_ctime_tests" = x"auto"; then
enable_ctime_tests=$enable_valgrind
fi
print_msan_notice=no
if test x"$enable_ctime_tests" = x"yes"; then
SECP_MSAN_CHECK
# 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.
if test x"$msan_enabled" = x"yes"; then
SECP_TRY_APPEND_CFLAGS([-fno-sanitize-memory-param-retval], SECP_CFLAGS)
print_msan_notice=yes
fi
fi
if test x"$enable_coverage" = x"yes"; then
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DCOVERAGE=1"
SECP_CFLAGS="-O0 --coverage $SECP_CFLAGS"
# If coverage is enabled, and the user has not overridden CFLAGS,
# override Autoconf's value "-g -O2" with "-g". Otherwise we'd end up
# with "-O0 --coverage -g -O2".
if test "$CFLAGS_overridden" = "no"; then
CFLAGS="-g"
fi
LDFLAGS="--coverage $LDFLAGS"
else
# Most likely the CFLAGS already contain -O2 because that is autoconf's default.
# We still add it here because passing it twice is not an issue, and handling
# this case would just add unnecessary complexity (see #896).
SECP_CFLAGS="-O2 $SECP_CFLAGS"
fi
AC_MSG_CHECKING([for __builtin_popcount])
AC_LINK_IFELSE([AC_LANG_SOURCE([[void myfunc() {__builtin_popcount(0);}]])],
[ AC_MSG_RESULT([yes]); SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DHAVE_BUILTIN_POPCOUNT=1"],
[ AC_MSG_RESULT([no])
])
2015-08-05 16:17:50 +02:00
AC_MSG_CHECKING([for __builtin_clzll])
AC_LINK_IFELSE([AC_LANG_SOURCE([[void myfunc() { __builtin_clzll(1);}]])],
[ AC_MSG_RESULT([yes]); SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DHAVE_BUILTIN_CLZLL=1"],
2015-08-05 16:17:50 +02:00
[ AC_MSG_RESULT([no])
])
2014-12-12 16:20:47 +01:00
if test x"$req_asm" = x"auto"; then
2023-05-12 10:50:18 +01:00
SECP_X86_64_ASM_CHECK
if test x"$has_x86_64_asm" = x"yes"; then
2014-12-12 16:20:47 +01:00
set_asm=x86_64
fi
if test x"$set_asm" = x; then
set_asm=no
2014-01-17 22:52:33 -05:00
fi
2014-12-12 16:20:47 +01:00
else
set_asm=$req_asm
case $set_asm in
x86_64)
2023-05-12 10:50:18 +01:00
SECP_X86_64_ASM_CHECK
if test x"$has_x86_64_asm" != x"yes"; then
AC_MSG_ERROR([x86_64 assembly requested but not available])
2014-12-12 16:20:47 +01:00
fi
;;
2023-05-11 10:03:23 +01:00
arm32)
SECP_ARM32_ASM_CHECK
if test x"$has_arm32_asm" != x"yes"; then
AC_MSG_ERROR([ARM32 assembly requested but not available])
fi
;;
2014-12-12 16:20:47 +01:00
no)
;;
*)
AC_MSG_ERROR([invalid assembly selection])
2014-12-12 16:20:47 +01:00
;;
esac
fi
2014-01-17 22:52:33 -05:00
# Select assembly
enable_external_asm=no
2014-12-12 16:20:47 +01:00
case $set_asm in
x86_64)
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DUSE_ASM_X86_64=1"
2014-12-12 16:20:47 +01:00
;;
2023-05-11 10:03:23 +01:00
arm32)
enable_external_asm=yes
;;
2014-12-12 16:20:47 +01:00
no)
;;
*)
AC_MSG_ERROR([invalid assembly selection])
2014-12-12 16:20:47 +01:00
;;
esac
if test x"$enable_external_asm" = x"yes"; then
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DUSE_EXTERNAL_ASM=1"
fi
# Select wide multiplication implementation
case $set_widemul in
2022-07-27 11:09:51 -04:00
int128_struct)
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DUSE_FORCE_WIDEMUL_INT128_STRUCT=1"
2022-07-27 11:09:51 -04:00
;;
int128)
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DUSE_FORCE_WIDEMUL_INT128=1"
2014-01-17 22:52:33 -05:00
;;
int64)
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DUSE_FORCE_WIDEMUL_INT64=1"
2014-01-17 22:52:33 -05:00
;;
auto)
2014-01-17 22:52:33 -05:00
;;
*)
AC_MSG_ERROR([invalid wide multiplication implementation])
2014-01-17 22:52:33 -05:00
;;
esac
error_window_size=['window size for ecmult precomputation not an integer in range [2..24]']
case $set_ecmult_window in
''|*[[!0-9]]*)
# no valid integer
AC_MSG_ERROR($error_window_size)
;;
*)
if test "$set_ecmult_window" -lt 2 -o "$set_ecmult_window" -gt 24 ; then
# not in range
AC_MSG_ERROR($error_window_size)
fi
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DECMULT_WINDOW_SIZE=$set_ecmult_window"
;;
esac
case $set_ecmult_gen_kb in
2)
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DCOMB_BLOCKS=2 -DCOMB_TEETH=5"
;;
22)
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DCOMB_BLOCKS=11 -DCOMB_TEETH=6"
;;
86)
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DCOMB_BLOCKS=43 -DCOMB_TEETH=6"
;;
*)
AC_MSG_ERROR(['ecmult gen table size not 2, 22 or 86'])
;;
esac
if test x"$enable_valgrind" = x"yes"; then
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES $VALGRIND_CPPFLAGS -DVALGRIND"
fi
# Add -Werror and similar flags passed from the outside (for testing, e.g., in CI).
# We don't want to set the user variable CFLAGS in CI because this would disable
# autoconf's logic for setting default CFLAGS, which we would like to test in CI.
SECP_CFLAGS="$SECP_CFLAGS $WERROR_CFLAGS"
###
### Handle module options
###
# Processing must be done in a reverse topological sorting of the dependency graph
# (dependent module first).
if test x"$enable_module_schnorrsig_halfagg" = x"yes"; then
if test x"$enable_module_schnorrsig" = x"no"; then
AC_MSG_ERROR([Module dependency error: You have disabled the schnorrsig module explicitly, but it is required by the schnorrsig_halfagg module.])
fi
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_SCHNORRSIG_HALFAGG=1"
enable_module_schnorrsig=yes
fi
if test x"$enable_module_bppp" = x"yes"; then
if test x"$enable_module_generator" = x"no"; then
AC_MSG_ERROR([Module dependency error: You have disabled the generator module explicitly, but it is required by the bppp module.])
fi
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_BPPP=1"
enable_module_generator=yes
fi
if test x"$enable_module_ecdsa_s2c" = x"yes"; then
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_ECDSA_S2C=1"
fi
if test x"$enable_module_ecdsa_adaptor" = x"yes"; then
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_ECDSA_ADAPTOR=1"
fi
if test x"$enable_module_whitelist" = x"yes"; then
if test x"$enable_module_rangeproof" = x"no"; then
AC_MSG_ERROR([Module dependency error: You have disabled the rangeproof module explicitly, but it is required by the whitelist module.])
fi
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_WHITELIST=1"
enable_module_rangeproof=yes
fi
if test x"$enable_module_surjectionproof" = x"yes"; then
if test x"$enable_module_rangeproof" = x"no"; then
AC_MSG_ERROR([Module dependency error: You have disabled the rangeproof module explicitly, but it is required by the surjectionproof module.])
fi
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_SURJECTIONPROOF=1"
enable_module_rangeproof=yes
2016-07-07 00:47:41 +02:00
fi
Pedersen commitments, borromean ring signatures, and ZK range proofs. This commit adds three new cryptosystems to libsecp256k1: Pedersen commitments are a system for making blinded commitments to a value. Functionally they work like: commit_b,v = H(blind_b || value_v), except they are additively homorphic, e.g. C(b1, v1) - C(b2, v2) = C(b1 - b2, v1 - v2) and C(b1, v1) - C(b1, v1) = 0, etc. The commitments themselves are EC points, serialized as 33 bytes. In addition to the commit function this implementation includes utility functions for verifying that a set of commitments sums to zero, and for picking blinding factors that sum to zero. If the blinding factors are uniformly random, pedersen commitments have information theoretic privacy. Borromean ring signatures are a novel efficient ring signature construction for AND/OR admissions policies (the code here implements an AND of ORs, each of any size). This construction requires 32 bytes of signature per pubkey used plus 32 bytes of constant overhead. With these you can construct signatures like "Given pubkeys A B C D E F G, the signer knows the discrete logs satisifying (A || B) & (C || D || E) & (F || G)". ZK range proofs allow someone to prove a pedersen commitment is in a particular range (e.g. [0..2^64)) without revealing the specific value. The construction here is based on the above borromean ring signature and uses a radix-4 encoding and other optimizations to maximize efficiency. It also supports encoding proofs with a non-private base-10 exponent and minimum-value to allow trading off secrecy for size and speed (or just avoiding wasting space keeping data private that was already public due to external constraints). A proof for a 32-bit mantissa takes 2564 bytes, but 2048 bytes of this can be used to communicate a private message to a receiver who shares a secret random seed with the prover. Also: get rid of precomputed H tables (Pieter Wuille)
2015-08-05 19:04:14 +02:00
if test x"$enable_module_rangeproof" = x"yes"; then
if test x"$enable_module_generator" = x"no"; then
AC_MSG_ERROR([Module dependency error: You have disabled the generator module explicitly, but it is required by the rangeproof module.])
fi
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_RANGEPROOF=1"
enable_module_generator=yes
Pedersen commitments, borromean ring signatures, and ZK range proofs. This commit adds three new cryptosystems to libsecp256k1: Pedersen commitments are a system for making blinded commitments to a value. Functionally they work like: commit_b,v = H(blind_b || value_v), except they are additively homorphic, e.g. C(b1, v1) - C(b2, v2) = C(b1 - b2, v1 - v2) and C(b1, v1) - C(b1, v1) = 0, etc. The commitments themselves are EC points, serialized as 33 bytes. In addition to the commit function this implementation includes utility functions for verifying that a set of commitments sums to zero, and for picking blinding factors that sum to zero. If the blinding factors are uniformly random, pedersen commitments have information theoretic privacy. Borromean ring signatures are a novel efficient ring signature construction for AND/OR admissions policies (the code here implements an AND of ORs, each of any size). This construction requires 32 bytes of signature per pubkey used plus 32 bytes of constant overhead. With these you can construct signatures like "Given pubkeys A B C D E F G, the signer knows the discrete logs satisifying (A || B) & (C || D || E) & (F || G)". ZK range proofs allow someone to prove a pedersen commitment is in a particular range (e.g. [0..2^64)) without revealing the specific value. The construction here is based on the above borromean ring signature and uses a radix-4 encoding and other optimizations to maximize efficiency. It also supports encoding proofs with a non-private base-10 exponent and minimum-value to allow trading off secrecy for size and speed (or just avoiding wasting space keeping data private that was already public due to external constraints). A proof for a 32-bit mantissa takes 2564 bytes, but 2048 bytes of this can be used to communicate a private message to a receiver who shares a secret random seed with the prover. Also: get rid of precomputed H tables (Pieter Wuille)
2015-08-05 19:04:14 +02:00
fi
if test x"$enable_module_generator" = x"yes"; then
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_GENERATOR=1"
fi
if test x"$enable_module_ellswift" = x"yes"; then
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_ELLSWIFT=1"
fi
build: wire the frost_enrollment module into both build systems Second of six commits adding the frost_enrollment module. This one is scaffolding only: the five entry points are stubs that validate their pointer arguments, zero their outputs and return 0. What is being verified here is that the module configures, compiles, links, exports its symbols and registers its test module in both build systems -- so that the next commit changes nothing but arithmetic. Ordering is the one thing in this commit that can go silently wrong, and it goes wrong in opposite directions in the two build systems: - configure.ac executes its `if` blocks in file order, and enable_module_frost defaults to no (configure.ac:243). A block placed after the frost block at :601 that sets enable_module_frost=yes flips the variable too late: AM_CONDITIONAL goes true, so the header is installed and the Makefile fragment is pulled in, but -DENABLE_MODULE_FROST=1 is never appended, so src/secp256k1.c never includes frost's implementation and every secp256k1_frost_* symbol fails to link. The new block therefore goes ahead of both the frost block and prefractal's, which documents the same trap. - src/CMakeLists.txt processes dependents FIRST, so the same block goes above the FROST block there, beside prefractal's. Verified rather than assumed: configuring with ONLY --enable-module-frost-enrollment emits -DENABLE_MODULE_FROST=1 alongside -DENABLE_MODULE_FROST_ENROLLMENT=1, and the CMake summary prints "frost ON" for the same configuration -- the latter is what the PARENT_SCOPE lift buys, since the summary runs after add_subdirectory(src) and would otherwise report a module it is compiling in as OFF. The dependency guard is prefractal's implies-frost idiom, copied verbatim along with its reasoning. frost is default-OFF, so the `test x"$enable_module_frost" = x"no"` / `DEFINED X AND NOT X` guard every other module uses -- which reads as "the user disabled it explicitly" for a default-ON dependency -- is true by default here and cannot tell an explicit --disable-module-frost from the default once both are in the cache. Enabling frost-enrollment simply implies frost, with no error. The one frost-module change in the whole series is in this commit: src/modules/frost/session.h gains a declaration for secp256k1_frost_sort_ids, which is defined at session_impl.h:517 and declared nowhere. The params hash needs it to canonicalize identifier order. Prefractal reaches frost's statics through translation-unit ordering alone; rather than inherit reuse-by-link-order, this declares the function where keygen.h:48 already declares derive_pubshare_at, so the reuse goes through an interface. No behavior change: it is a declaration for an existing static definition in the same TU. CI wiring is two files, and skipping either half fails quietly: - ci/ci.sh gets FROST_ENROLLMENT in the reproduction header's variable list and --enable-module-frost-enrollment="$FROST_ENROLLMENT" after the prefractal line. - .github/workflows/ci.yml gets FROST_ENROLLMENT at every PREFRACTAL site: the global default, 11 inline matrix entries and 10 job-level env blocks. Without the default, ci.sh runs under set -eux with an empty $FROST_ENROLLMENT, passes --enable-module-frost-enrollment="", `test x"" = x"yes"` is false, and the module is off in all of CI while ci.sh visibly has the plumbing. Verified programmatically over the parsed workflow: across the 106 effective job contexts, PREFRACTAL and FROST_ENROLLMENT now agree in every single one (45 set to yes, no mismatches), no context sets FROST_ENROLLMENT without FROST or without EXPERIMENTAL, and no context leaves it undefined. ci.sh passes sh -n. The stub test is not a placeholder that has to be deleted later: every entry point must reject an empty helper set and leave its output zeroed, which is true of the stubs and stays true of the finished implementation, so it doubles as the check that all five symbols are reachable from the test binary. Verification. Autotools: ./autogen.sh, then a frost-enrollment-only configure and a full configure with frost, chilldkg, iceberg, prefractal and frost-enrollment all on -- both build with zero warnings under the project's -Werror-grade flag set, ./tests and ./exhaustive_tests exit 0, and `./tests -l` lists the frost_enrollment module. CMake: configure with -DSECP256K1_EXPERIMENTAL=ON -DSECP256K1_ENABLE_MODULE_FROST_ENROLLMENT=ON builds clean and ctest passes 391 tests. nm shows the five new symbols exported from libsecp256k1.so; tools/symbol-check.py could not be run here because python3-lief is not installed in this environment, but all five carry the required secp256k1_ prefix. make dist succeeds and the tarball carries src/modules/frost_enrollment/frost_enrollment.md alongside the other module documents. One unrelated observation from this build: a stale src/ctime_tests-ctime_tests.o left over from an earlier configure with a different module set will fail to link, because automake does not track CPPFLAGS changes across reconfigures. make clean between configurations with different module sets, not a fault in this change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 03:53:03 +02:00
# Like the prefractal block below, this must stay ahead of the frost block
# further down: the enable_module_frost assignment here is only observed by
# blocks that run after it.
if test x"$enable_module_frost_enrollment" = x"yes"; then
# frost defaults to no, so the "you disabled it explicitly" guard every
# other block uses would reject every frost-enrollment build; enabling
# frost-enrollment simply implies frost. See the prefractal block below for
# the full reasoning.
enable_module_frost=yes
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_FROST_ENROLLMENT=1"
fi
prefractal: add the nested FROST+MuSig2 module (API, implementation, wiring) Adds `prefractal`, an experimental module that lets a FROST t-of-n group occupy ONE participant slot of an ordinary MuSig2 (BIP 327) session. Each member computes s_i = k1_i + b_frost*b_musig*k2_i + e*a*lambda_i*g*gacc*d_i and the group publishes one ordinary MuSig2 public nonce and one ordinary MuSig2 partial signature, so cosigners need no support for it and cannot tell a group is involved. Four public functions, all sessionless (every call takes its session parameters explicitly, so there are no new opaque types, magics or *_SIZE constants to keep synchronised): secp256k1_prefractal_nonce_agg group wire nonce + unscaled aggnonce secp256k1_prefractal_sign one member's partial signature secp256k1_prefractal_partial_sig_verify identifiable abort secp256k1_prefractal_partial_sig_agg sum -> musig partial signature Three deliberate deviations from BIP 445, all documented in the public header: 1. b_frost does not commit to the message. The target protocols publish the group's wire nonce before the message exists, so a message-committing coefficient could not be computed in round one and rebuilt later. The outer b_musig does commit to the message and multiplies this one, so the product still binds it. Same trade the iceberg module makes, for the same reason. The preimage is BIP 445's with the message dropped and the group key carried in full rather than x-only, since it is used as a full point downstream. 2. There is NO g_frost factor. Stock FROST normalises its threshold key to even Y (g_times_gacc_parity = gacc_parity ^ pk_odd, frost/session_impl.h :664) because it produces a BIP 340 x-only signature. Here the threshold key is an inner participant of the outer key aggregation and is used as a full point, so all key-side parity normalisation happens once, at the aggregate level, off the OUTER keyagg cache. Note this is NOT implied by the tweak cache being the identity: with an identity cache g_frost is still -1 for every odd-Y group key, i.e. about half of them. Importing frost's key-side parity here would yield a signer that works for even-Y groups and fails for odd-Y ones. 3. The FROST tweak cache must be the identity (tacc == 0, gacc_parity == 0). Checked in sign and partial_sig_verify, not only in partial_sig_agg, so the key a member signs under is tied to the cache that was validated; sign and verify additionally require thresh_pk to equal the cache's own key so the two arguments cannot disagree. The verification equation lives in one helper used both by sign's BIP 445 self-check and by partial_sig_verify, so the two cannot drift apart. Build wiring. Three files order their module blocks differently and the constraints point in opposite directions: - src/secp256k1.c: the include goes AFTER frost and musig, because the module calls their static internals. - src/CMakeLists.txt: the block goes BEFORE both, because its set() calls are only observed by blocks that run later. - configure.ac: the block likewise goes before the musig block, NOT at iceberg's position further down. configure.ac orders musig and frost ahead of iceberg, and iceberg's late enable_module_musig=yes is harmless only because musig defaults to yes. frost defaults to no, so a late force-enable would leave -DENABLE_MODULE_FROST=1 unemitted while AM_CONDITIONAL still observed the mutation - a library whose secp256k1.c never included frost, built alongside frost's own sources. frost is also the first default-OFF module anything depends on, which breaks the dependency-guard idiom used everywhere else in both build systems: the existing "DEFINED X AND NOT X" (CMake) and "x$X = xno" (autotools) tests read as "the user disabled it explicitly" only for default-ON modules, and are true by default for a default-OFF one. Since neither build system can distinguish an explicit disable from the default once both are in the cache, enabling prefractal simply implies frost; the guard is kept for musig, where it still means what it says. The CMake block additionally lifts both dependencies into the parent scope so the top-level configuration summary reports what was actually built rather than printing "frost OFF" while compiling frost in. Verified on both build systems: cmake -B build -DSECP256K1_ENABLE_MODULE_PREFRACTAL=ON -DSECP256K1_BUILD_TESTS=ON -> musig/frost/prefractal all ON, tests pass, 4 prefractal symbols exported cmake -B build -DSECP256K1_BUILD_TESTS=ON -> prefractal OFF, default build unchanged, tests pass ./configure --enable-experimental --enable-module-prefractal && make && make check -> frost=yes forced on, -DENABLE_MODULE_FROST=1 emitted, 3/3 pass ./configure --enable-module-prefractal -> correctly refused: "Prefractal module is experimental" tests_impl.h is a placeholder here so the module links; the real suite lands next.
2026-09-04 00:44:43 +02:00
# This block must stay ahead of the musig and frost blocks below. The
# enable_module_* assignments here are only observed by blocks that run after
# them, and frost defaults to "no": placing this at the iceberg block's
# position (further down) would leave -DENABLE_MODULE_FROST=1 unemitted while
# AM_CONDITIONAL still saw the mutation, which builds frost's sources into a
# library whose secp256k1.c never included them.
if test x"$enable_module_prefractal" = x"yes"; then
# musig defaults to yes, so "no" here really does mean the user disabled it.
if test x"$enable_module_musig" = x"no"; then
AC_MSG_ERROR([Module dependency error: You have disabled the musig module explicitly, but it is required by the prefractal module.])
fi
# frost defaults to no, so the same test would reject every prefractal build.
# SECP_SET_DEFAULT only runs in AC_ARG_ENABLE's action-if-not-given branch,
# so telling an explicit --disable-module-frost from the default would mean
# changing frost's own declaration; enabling prefractal simply implies frost
# instead. The CMake block does the same, for the same reason.
enable_module_frost=yes
enable_module_musig=yes
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_PREFRACTAL=1"
fi
if test x"$enable_module_musig" = x"yes"; then
if test x"$enable_module_schnorrsig" = x"no"; then
AC_MSG_ERROR([Module dependency error: You have disabled the schnorrsig module explicitly, but it is required by the musig module.])
fi
enable_module_schnorrsig=yes
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_MUSIG=1"
fi
if test x"$enable_module_schnorrsig" = x"yes"; then
if test x"$enable_module_extrakeys" = x"no"; then
AC_MSG_ERROR([Module dependency error: You have disabled the extrakeys module explicitly, but it is required by the schnorrsig module.])
fi
enable_module_extrakeys=yes
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_SCHNORRSIG=1"
fi
if test x"$enable_module_extrakeys" = x"yes"; then
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_EXTRAKEYS=1"
fi
if test x"$enable_module_recovery" = x"yes"; then
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_RECOVERY=1"
fi
if test x"$enable_module_ecdh" = x"yes"; then
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_ECDH=1"
fi
2026-08-31 00:05:16 +02:00
if test x"$enable_module_frost" = x"yes"; then
if test x"$enable_module_schnorrsig" = x"no"; then
AC_MSG_ERROR([Module dependency error: You have disabled the schnorrsig module explicitly, but it is required by the frost module.])
fi
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_FROST=1"
enable_module_schnorrsig=yes
fi
chilldkg: Phase 0 - module scaffolding and build wiring Add an empty, experimental `chilldkg` module as the foundation for a ChillDKG implementation (distributed key generation for FROST) per the bip-frost-dkg BIP draft (v0.3.0-dev): https://github.com/BlockstreamResearch/bip-frost-dkg The module lives in src/modules/chilldkg/ (separate from the frost module, per the implementation plan in .idea/docs/ chilldkg-implementation-plan.md: FROST signing (BIP 445) and ChillDKG are separate BIPs with separate reference repos, test vectors and review cycles; the dependency between them is one-way bytes). New files: - include/secp256k1_chilldkg.h: public header skeleton with the same "EXTREMELY DANGEROUS / work in progress" warning style as secp256k1_frost.h, plus a note that the BIP is a draft and tagged hashes/wire formats may change. No API yet (Phase 3+). - src/modules/chilldkg/main_impl.h: implementation skeleton including the public header. - src/modules/chilldkg/tests_impl.h: trivial scaffolding unit test (chilldkg_scaffolding_test) registered via the tests_chilldkg[] CASE1 array used by this repo's unit-test framework. - src/modules/chilldkg/Makefile.am.include: autotools file list, mirroring the frost module's. - src/modules/chilldkg/chilldkg.md: module doc stub (purpose, draft status, dependency on the schnorrsig and ecdh modules). Build wiring (mirrors the frost module exactly): - configure.ac: --enable-module-chilldkg (default no, experimental gate), dependency errors when schnorrsig or ecdh are explicitly disabled, AM_CONDITIONAL(ENABLE_MODULE_CHILLDKG), summary line. - Makefile.am: include src/modules/chilldkg/Makefile.am.include under ENABLE_MODULE_CHILLDKG. - src/secp256k1.c: guarded include of modules/chilldkg/main_impl.h after the frost module. - src/tests.c: guarded include of tests_impl.h and MAKE_TEST_MODULE(chilldkg) registration. - CMakeLists.txt: SECP256K1_ENABLE_MODULE_CHILLDKG option (OFF) + summary line. - src/CMakeLists.txt: dependency checks on SECP256K1_ENABLE_MODULE_SCHNORRSIG and SECP256K1_ENABLE_MODULE_ECDH, ENABLE_MODULE_CHILLDKG=1 compile definition, public header export. Verified: - ./autogen.sh && ./configure --enable-experimental --enable-module-chilldkg --enable-module-schnorrsig --enable-module-ecdh && make check: PASS 3/3 (tests, noverify_tests, exhaustive_tests). - configure fails with a clear error when schnorrsig or ecdh are disabled, or when experimental is not enabled. - CMake build with SECP256K1_ENABLE_MODULE_CHILLDKG=ON: ctest 345/345 passed; dependency errors fire correctly when schnorrsig/ecdh OFF.
2026-08-31 01:37:22 +02:00
if test x"$enable_module_chilldkg" = x"yes"; then
if test x"$enable_module_schnorrsig" = x"no"; then
AC_MSG_ERROR([Module dependency error: You have disabled the schnorrsig module explicitly, but it is required by the chilldkg module.])
fi
if test x"$enable_module_ecdh" = x"no"; then
AC_MSG_ERROR([Module dependency error: You have disabled the ecdh module explicitly, but it is required by the chilldkg module.])
fi
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_CHILLDKG=1"
enable_module_schnorrsig=yes
enable_module_ecdh=yes
fi
iceberg: add the Iceberg threshold-MuSig module Port the experimental Iceberg module from the benchmark-iceberg tree (github.com/furszy/benchmark-iceberg, sources/secp256k1-kmp/native/ secp256k1) into this repo. Iceberg is a threshold scheme that lets a group of parties stand in for a single MuSig2 (BIP 327) participant: the group produces one ordinary MuSig2 public nonce and one ordinary MuSig2 partial signature, so cosigners cannot tell a group is involved and need no changes. Nonces are derived from a caller-chosen per-session label (sid32) rather than stored, so no signer holds a secret nonce between rounds; labels are public but must never be reused. A quorum of 2t-1 members (of whom up to t-1 may be corrupt) is needed in each round, so the threshold is at most half the group rounded up; combined with the scheme's other constraints the smallest usable group is 2-of-4. See doc/iceberg.md and the module header for the full usage notes. Module layout (src/modules/iceberg/, layered bottom-up, each layer may only use the ones above it -- that ordering is also the constant-time story): - scalar_poly.{h,_impl.h}: secret-carrying polynomial arithmetic, keeping secrets away from inversions (documented in the header). - rss.{h,_impl.h}: replicated secret sharing evaluation. - vpss.{h,_impl.h}: verifiable public shares; variable-time by design, sees only participant indices and published points. - keygen_impl.h: distributed key generation producing one share per member. - session_impl.h: nonce_gen/nonce_agg and partial_sign/ partial_sig_agg producing plain MuSig2 objects. - tests_impl.h: 28 tests including the shipped vectors.h vector suite and dealer known-answer tests. - bench_impl.h: benchmark definitions (wired in a follow-up commit). Public headers: include/secp256k1_iceberg.h (installed) and include/secp256k1_iceberg_dealer.h (in-tree only: a trusted dealer is not part of the shipped API, but tests, benchmarks and the example need to deal shares). Content adaptations relative to the source tree (the only changes to the ported code): three secp256k1_musig_nonce_process call sites in tests_impl.h gained a NULL adaptor argument, because this repo's musig is the zkp variant whose public nonce_process takes an optional adaptor point. All musig internals the module uses (ge_parse_ext, ge_serialize_ext, keyaggcoef, aggnonce_load, pubnonce_save, partial_sig_save, nonce_process_internal) are identical in both trees, as are all core headers the module touches; nothing else needed adaptation. Build wiring mirrors the chilldkg module: - configure.ac: --enable-module-iceberg (default no, experimental gate), hard dependency on the musig module with a configure error if musig is explicitly disabled (musig itself pulls in schnorrsig), AM_CONDITIONAL(ENABLE_MODULE_ICEBERG), summary line. - Makefile.am: include src/modules/iceberg/Makefile.am.include under the conditional. - src/secp256k1.c: guarded include of modules/iceberg/main_impl.h after the chilldkg block (musig is included earlier, so its internals are in scope). - src/tests.c: module test registration via MAKE_TEST_MODULE(iceberg). - CMakeLists.txt / src/CMakeLists.txt: SECP256K1_ENABLE_MODULE_ICEBERG option (OFF) with a dependency check on SECP256K1_ENABLE_MODULE_MUSIG (placed before the musig block so the force-enable takes effect), ENABLE_MODULE_ICEBERG=1 compile definition, public header export, summary line. Verified: ./configure --enable-experimental --enable-module-iceberg && make check passes; ./tests --target=iceberg runs the full module suite (28/28); CMake build + ctest pass; the musig dependency error fires correctly in both build systems.
2026-08-31 12:24:48 +02:00
if test x"$enable_module_iceberg" = x"yes"; then
if test x"$enable_module_musig" = x"no"; then
AC_MSG_ERROR([Module dependency error: You have disabled the musig module explicitly, but it is required by the iceberg module.])
fi
enable_module_musig=yes
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_ICEBERG=1"
fi
if test x"$enable_external_default_callbacks" = x"yes"; then
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DUSE_EXTERNAL_DEFAULT_CALLBACKS=1"
fi
if test x"$enable_reduced_surjection_proof_size" = x"yes"; then
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DUSE_REDUCED_SURJECTION_PROOF_SIZE=1"
fi
###
### Check for --enable-experimental if necessary
###
if test x"$enable_experimental" = x"no"; then
# The order of the following tests matters. If the user enables a dependent
# module (which automatically enables the module dependencies) we want to
# print an error for the dependent module, not the module dependency. Hence,
# we first test dependent modules.
if test x"$enable_module_schnorrsig_halfagg" = x"yes"; then
AC_MSG_ERROR([Schnorrsig Half-Aggregation module is experimental. Use --enable-experimental to allow.])
fi
2023-02-06 13:53:02 -08:00
if test x"$enable_module_bppp" = x"yes"; then
AC_MSG_ERROR([Bulletproofs++ module is experimental. Use --enable-experimental to allow.])
2022-08-27 15:02:44 +00:00
fi
if test x"$enable_module_ecdsa_adaptor" = x"yes"; then
AC_MSG_ERROR([ecdsa adaptor signatures module is experimental. Use --enable-experimental to allow.])
fi
if test x"$enable_module_ecdsa_s2c" = x"yes"; then
AC_MSG_ERROR([ECDSA sign-to-contract module module is experimental. Use --enable-experimental to allow.])
fi
if test x"$enable_module_whitelist" = x"yes"; then
AC_MSG_ERROR([Key whitelisting module is experimental. Use --enable-experimental to allow.])
fi
if test x"$enable_module_surjectionproof" = x"yes"; then
AC_MSG_ERROR([Surjection proof module is experimental. Use --enable-experimental to allow.])
fi
if test x"$enable_module_rangeproof" = x"yes"; then
AC_MSG_ERROR([Range proof module is experimental. Use --enable-experimental to allow.])
fi
if test x"$enable_module_generator" = x"yes"; then
AC_MSG_ERROR([NUMS generator module is experimental. Use --enable-experimental to allow.])
fi
2023-05-11 10:03:23 +01:00
if test x"$set_asm" = x"arm32"; then
AC_MSG_ERROR([ARM32 assembly is experimental. Use --enable-experimental to allow.])
fi
prefractal: add the nested FROST+MuSig2 module (API, implementation, wiring) Adds `prefractal`, an experimental module that lets a FROST t-of-n group occupy ONE participant slot of an ordinary MuSig2 (BIP 327) session. Each member computes s_i = k1_i + b_frost*b_musig*k2_i + e*a*lambda_i*g*gacc*d_i and the group publishes one ordinary MuSig2 public nonce and one ordinary MuSig2 partial signature, so cosigners need no support for it and cannot tell a group is involved. Four public functions, all sessionless (every call takes its session parameters explicitly, so there are no new opaque types, magics or *_SIZE constants to keep synchronised): secp256k1_prefractal_nonce_agg group wire nonce + unscaled aggnonce secp256k1_prefractal_sign one member's partial signature secp256k1_prefractal_partial_sig_verify identifiable abort secp256k1_prefractal_partial_sig_agg sum -> musig partial signature Three deliberate deviations from BIP 445, all documented in the public header: 1. b_frost does not commit to the message. The target protocols publish the group's wire nonce before the message exists, so a message-committing coefficient could not be computed in round one and rebuilt later. The outer b_musig does commit to the message and multiplies this one, so the product still binds it. Same trade the iceberg module makes, for the same reason. The preimage is BIP 445's with the message dropped and the group key carried in full rather than x-only, since it is used as a full point downstream. 2. There is NO g_frost factor. Stock FROST normalises its threshold key to even Y (g_times_gacc_parity = gacc_parity ^ pk_odd, frost/session_impl.h :664) because it produces a BIP 340 x-only signature. Here the threshold key is an inner participant of the outer key aggregation and is used as a full point, so all key-side parity normalisation happens once, at the aggregate level, off the OUTER keyagg cache. Note this is NOT implied by the tweak cache being the identity: with an identity cache g_frost is still -1 for every odd-Y group key, i.e. about half of them. Importing frost's key-side parity here would yield a signer that works for even-Y groups and fails for odd-Y ones. 3. The FROST tweak cache must be the identity (tacc == 0, gacc_parity == 0). Checked in sign and partial_sig_verify, not only in partial_sig_agg, so the key a member signs under is tied to the cache that was validated; sign and verify additionally require thresh_pk to equal the cache's own key so the two arguments cannot disagree. The verification equation lives in one helper used both by sign's BIP 445 self-check and by partial_sig_verify, so the two cannot drift apart. Build wiring. Three files order their module blocks differently and the constraints point in opposite directions: - src/secp256k1.c: the include goes AFTER frost and musig, because the module calls their static internals. - src/CMakeLists.txt: the block goes BEFORE both, because its set() calls are only observed by blocks that run later. - configure.ac: the block likewise goes before the musig block, NOT at iceberg's position further down. configure.ac orders musig and frost ahead of iceberg, and iceberg's late enable_module_musig=yes is harmless only because musig defaults to yes. frost defaults to no, so a late force-enable would leave -DENABLE_MODULE_FROST=1 unemitted while AM_CONDITIONAL still observed the mutation - a library whose secp256k1.c never included frost, built alongside frost's own sources. frost is also the first default-OFF module anything depends on, which breaks the dependency-guard idiom used everywhere else in both build systems: the existing "DEFINED X AND NOT X" (CMake) and "x$X = xno" (autotools) tests read as "the user disabled it explicitly" only for default-ON modules, and are true by default for a default-OFF one. Since neither build system can distinguish an explicit disable from the default once both are in the cache, enabling prefractal simply implies frost; the guard is kept for musig, where it still means what it says. The CMake block additionally lifts both dependencies into the parent scope so the top-level configuration summary reports what was actually built rather than printing "frost OFF" while compiling frost in. Verified on both build systems: cmake -B build -DSECP256K1_ENABLE_MODULE_PREFRACTAL=ON -DSECP256K1_BUILD_TESTS=ON -> musig/frost/prefractal all ON, tests pass, 4 prefractal symbols exported cmake -B build -DSECP256K1_BUILD_TESTS=ON -> prefractal OFF, default build unchanged, tests pass ./configure --enable-experimental --enable-module-prefractal && make && make check -> frost=yes forced on, -DENABLE_MODULE_FROST=1 emitted, 3/3 pass ./configure --enable-module-prefractal -> correctly refused: "Prefractal module is experimental" tests_impl.h is a placeholder here so the module links; the real suite lands next.
2026-09-04 00:44:43 +02:00
if test x"$enable_module_prefractal" = x"yes"; then
AC_MSG_ERROR([Prefractal module is experimental. Use --enable-experimental to allow.])
fi
build: wire the frost_enrollment module into both build systems Second of six commits adding the frost_enrollment module. This one is scaffolding only: the five entry points are stubs that validate their pointer arguments, zero their outputs and return 0. What is being verified here is that the module configures, compiles, links, exports its symbols and registers its test module in both build systems -- so that the next commit changes nothing but arithmetic. Ordering is the one thing in this commit that can go silently wrong, and it goes wrong in opposite directions in the two build systems: - configure.ac executes its `if` blocks in file order, and enable_module_frost defaults to no (configure.ac:243). A block placed after the frost block at :601 that sets enable_module_frost=yes flips the variable too late: AM_CONDITIONAL goes true, so the header is installed and the Makefile fragment is pulled in, but -DENABLE_MODULE_FROST=1 is never appended, so src/secp256k1.c never includes frost's implementation and every secp256k1_frost_* symbol fails to link. The new block therefore goes ahead of both the frost block and prefractal's, which documents the same trap. - src/CMakeLists.txt processes dependents FIRST, so the same block goes above the FROST block there, beside prefractal's. Verified rather than assumed: configuring with ONLY --enable-module-frost-enrollment emits -DENABLE_MODULE_FROST=1 alongside -DENABLE_MODULE_FROST_ENROLLMENT=1, and the CMake summary prints "frost ON" for the same configuration -- the latter is what the PARENT_SCOPE lift buys, since the summary runs after add_subdirectory(src) and would otherwise report a module it is compiling in as OFF. The dependency guard is prefractal's implies-frost idiom, copied verbatim along with its reasoning. frost is default-OFF, so the `test x"$enable_module_frost" = x"no"` / `DEFINED X AND NOT X` guard every other module uses -- which reads as "the user disabled it explicitly" for a default-ON dependency -- is true by default here and cannot tell an explicit --disable-module-frost from the default once both are in the cache. Enabling frost-enrollment simply implies frost, with no error. The one frost-module change in the whole series is in this commit: src/modules/frost/session.h gains a declaration for secp256k1_frost_sort_ids, which is defined at session_impl.h:517 and declared nowhere. The params hash needs it to canonicalize identifier order. Prefractal reaches frost's statics through translation-unit ordering alone; rather than inherit reuse-by-link-order, this declares the function where keygen.h:48 already declares derive_pubshare_at, so the reuse goes through an interface. No behavior change: it is a declaration for an existing static definition in the same TU. CI wiring is two files, and skipping either half fails quietly: - ci/ci.sh gets FROST_ENROLLMENT in the reproduction header's variable list and --enable-module-frost-enrollment="$FROST_ENROLLMENT" after the prefractal line. - .github/workflows/ci.yml gets FROST_ENROLLMENT at every PREFRACTAL site: the global default, 11 inline matrix entries and 10 job-level env blocks. Without the default, ci.sh runs under set -eux with an empty $FROST_ENROLLMENT, passes --enable-module-frost-enrollment="", `test x"" = x"yes"` is false, and the module is off in all of CI while ci.sh visibly has the plumbing. Verified programmatically over the parsed workflow: across the 106 effective job contexts, PREFRACTAL and FROST_ENROLLMENT now agree in every single one (45 set to yes, no mismatches), no context sets FROST_ENROLLMENT without FROST or without EXPERIMENTAL, and no context leaves it undefined. ci.sh passes sh -n. The stub test is not a placeholder that has to be deleted later: every entry point must reject an empty helper set and leave its output zeroed, which is true of the stubs and stays true of the finished implementation, so it doubles as the check that all five symbols are reachable from the test binary. Verification. Autotools: ./autogen.sh, then a frost-enrollment-only configure and a full configure with frost, chilldkg, iceberg, prefractal and frost-enrollment all on -- both build with zero warnings under the project's -Werror-grade flag set, ./tests and ./exhaustive_tests exit 0, and `./tests -l` lists the frost_enrollment module. CMake: configure with -DSECP256K1_EXPERIMENTAL=ON -DSECP256K1_ENABLE_MODULE_FROST_ENROLLMENT=ON builds clean and ctest passes 391 tests. nm shows the five new symbols exported from libsecp256k1.so; tools/symbol-check.py could not be run here because python3-lief is not installed in this environment, but all five carry the required secp256k1_ prefix. make dist succeeds and the tarball carries src/modules/frost_enrollment/frost_enrollment.md alongside the other module documents. One unrelated observation from this build: a stale src/ctime_tests-ctime_tests.o left over from an earlier configure with a different module set will fail to link, because automake does not track CPPFLAGS changes across reconfigures. make clean between configurations with different module sets, not a fault in this change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 03:53:03 +02:00
if test x"$enable_module_frost_enrollment" = x"yes"; then
AC_MSG_ERROR([FROST enrollment module is experimental. Use --enable-experimental to allow.])
fi
2026-08-31 00:05:16 +02:00
if test x"$enable_module_frost" = x"yes"; then
AC_MSG_ERROR([FROST module is experimental. Use --enable-experimental to allow.])
fi
chilldkg: Phase 0 - module scaffolding and build wiring Add an empty, experimental `chilldkg` module as the foundation for a ChillDKG implementation (distributed key generation for FROST) per the bip-frost-dkg BIP draft (v0.3.0-dev): https://github.com/BlockstreamResearch/bip-frost-dkg The module lives in src/modules/chilldkg/ (separate from the frost module, per the implementation plan in .idea/docs/ chilldkg-implementation-plan.md: FROST signing (BIP 445) and ChillDKG are separate BIPs with separate reference repos, test vectors and review cycles; the dependency between them is one-way bytes). New files: - include/secp256k1_chilldkg.h: public header skeleton with the same "EXTREMELY DANGEROUS / work in progress" warning style as secp256k1_frost.h, plus a note that the BIP is a draft and tagged hashes/wire formats may change. No API yet (Phase 3+). - src/modules/chilldkg/main_impl.h: implementation skeleton including the public header. - src/modules/chilldkg/tests_impl.h: trivial scaffolding unit test (chilldkg_scaffolding_test) registered via the tests_chilldkg[] CASE1 array used by this repo's unit-test framework. - src/modules/chilldkg/Makefile.am.include: autotools file list, mirroring the frost module's. - src/modules/chilldkg/chilldkg.md: module doc stub (purpose, draft status, dependency on the schnorrsig and ecdh modules). Build wiring (mirrors the frost module exactly): - configure.ac: --enable-module-chilldkg (default no, experimental gate), dependency errors when schnorrsig or ecdh are explicitly disabled, AM_CONDITIONAL(ENABLE_MODULE_CHILLDKG), summary line. - Makefile.am: include src/modules/chilldkg/Makefile.am.include under ENABLE_MODULE_CHILLDKG. - src/secp256k1.c: guarded include of modules/chilldkg/main_impl.h after the frost module. - src/tests.c: guarded include of tests_impl.h and MAKE_TEST_MODULE(chilldkg) registration. - CMakeLists.txt: SECP256K1_ENABLE_MODULE_CHILLDKG option (OFF) + summary line. - src/CMakeLists.txt: dependency checks on SECP256K1_ENABLE_MODULE_SCHNORRSIG and SECP256K1_ENABLE_MODULE_ECDH, ENABLE_MODULE_CHILLDKG=1 compile definition, public header export. Verified: - ./autogen.sh && ./configure --enable-experimental --enable-module-chilldkg --enable-module-schnorrsig --enable-module-ecdh && make check: PASS 3/3 (tests, noverify_tests, exhaustive_tests). - configure fails with a clear error when schnorrsig or ecdh are disabled, or when experimental is not enabled. - CMake build with SECP256K1_ENABLE_MODULE_CHILLDKG=ON: ctest 345/345 passed; dependency errors fire correctly when schnorrsig/ecdh OFF.
2026-08-31 01:37:22 +02:00
if test x"$enable_module_chilldkg" = x"yes"; then
AC_MSG_ERROR([ChillDKG module is experimental. Use --enable-experimental to allow.])
fi
iceberg: add the Iceberg threshold-MuSig module Port the experimental Iceberg module from the benchmark-iceberg tree (github.com/furszy/benchmark-iceberg, sources/secp256k1-kmp/native/ secp256k1) into this repo. Iceberg is a threshold scheme that lets a group of parties stand in for a single MuSig2 (BIP 327) participant: the group produces one ordinary MuSig2 public nonce and one ordinary MuSig2 partial signature, so cosigners cannot tell a group is involved and need no changes. Nonces are derived from a caller-chosen per-session label (sid32) rather than stored, so no signer holds a secret nonce between rounds; labels are public but must never be reused. A quorum of 2t-1 members (of whom up to t-1 may be corrupt) is needed in each round, so the threshold is at most half the group rounded up; combined with the scheme's other constraints the smallest usable group is 2-of-4. See doc/iceberg.md and the module header for the full usage notes. Module layout (src/modules/iceberg/, layered bottom-up, each layer may only use the ones above it -- that ordering is also the constant-time story): - scalar_poly.{h,_impl.h}: secret-carrying polynomial arithmetic, keeping secrets away from inversions (documented in the header). - rss.{h,_impl.h}: replicated secret sharing evaluation. - vpss.{h,_impl.h}: verifiable public shares; variable-time by design, sees only participant indices and published points. - keygen_impl.h: distributed key generation producing one share per member. - session_impl.h: nonce_gen/nonce_agg and partial_sign/ partial_sig_agg producing plain MuSig2 objects. - tests_impl.h: 28 tests including the shipped vectors.h vector suite and dealer known-answer tests. - bench_impl.h: benchmark definitions (wired in a follow-up commit). Public headers: include/secp256k1_iceberg.h (installed) and include/secp256k1_iceberg_dealer.h (in-tree only: a trusted dealer is not part of the shipped API, but tests, benchmarks and the example need to deal shares). Content adaptations relative to the source tree (the only changes to the ported code): three secp256k1_musig_nonce_process call sites in tests_impl.h gained a NULL adaptor argument, because this repo's musig is the zkp variant whose public nonce_process takes an optional adaptor point. All musig internals the module uses (ge_parse_ext, ge_serialize_ext, keyaggcoef, aggnonce_load, pubnonce_save, partial_sig_save, nonce_process_internal) are identical in both trees, as are all core headers the module touches; nothing else needed adaptation. Build wiring mirrors the chilldkg module: - configure.ac: --enable-module-iceberg (default no, experimental gate), hard dependency on the musig module with a configure error if musig is explicitly disabled (musig itself pulls in schnorrsig), AM_CONDITIONAL(ENABLE_MODULE_ICEBERG), summary line. - Makefile.am: include src/modules/iceberg/Makefile.am.include under the conditional. - src/secp256k1.c: guarded include of modules/iceberg/main_impl.h after the chilldkg block (musig is included earlier, so its internals are in scope). - src/tests.c: module test registration via MAKE_TEST_MODULE(iceberg). - CMakeLists.txt / src/CMakeLists.txt: SECP256K1_ENABLE_MODULE_ICEBERG option (OFF) with a dependency check on SECP256K1_ENABLE_MODULE_MUSIG (placed before the musig block so the force-enable takes effect), ENABLE_MODULE_ICEBERG=1 compile definition, public header export, summary line. Verified: ./configure --enable-experimental --enable-module-iceberg && make check passes; ./tests --target=iceberg runs the full module suite (28/28); CMake build + ctest pass; the musig dependency error fires correctly in both build systems.
2026-08-31 12:24:48 +02:00
if test x"$enable_module_iceberg" = x"yes"; then
AC_MSG_ERROR([Iceberg module is experimental. Use --enable-experimental to allow.])
fi
2015-11-26 00:06:41 +01:00
fi
# Check for concurrency support (tests only)
if test "x$enable_tests" != x"no"; then
AC_CHECK_HEADERS([sys/types.h sys/wait.h unistd.h])
AS_IF([test "x$ac_cv_header_sys_types_h" = xyes && test "x$ac_cv_header_sys_wait_h" = xyes &&
test "x$ac_cv_header_unistd_h" = xyes], [TEST_DEFINES="-DSUPPORTS_CONCURRENCY=1"], TEST_DEFINES="")
AC_SUBST(TEST_DEFINES)
fi
###
### Generate output
###
2014-05-07 06:10:08 +00:00
AC_CONFIG_FILES([Makefile libsecp256k1.pc])
AC_SUBST(SECP_CFLAGS)
AC_SUBST(SECP_CONFIG_DEFINES)
AM_CONDITIONAL([ENABLE_COVERAGE], [test x"$enable_coverage" = x"yes"])
AM_CONDITIONAL([USE_TESTS], [test x"$enable_tests" != x"no"])
2022-12-06 23:12:15 -05:00
AM_CONDITIONAL([USE_CTIME_TESTS], [test x"$enable_ctime_tests" = x"yes"])
AM_CONDITIONAL([USE_EXHAUSTIVE_TESTS], [test x"$enable_exhaustive_tests" != x"no"])
AM_CONDITIONAL([USE_EXAMPLES], [test x"$enable_examples" != x"no"])
AM_CONDITIONAL([USE_BENCHMARK], [test x"$enable_benchmark" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_EXTRAKEYS], [test x"$enable_module_extrakeys" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_SCHNORRSIG], [test x"$enable_module_schnorrsig" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_MUSIG], [test x"$enable_module_musig" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_ELLSWIFT], [test x"$enable_module_ellswift" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_GENERATOR], [test x"$enable_module_generator" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_RANGEPROOF], [test x"$enable_module_rangeproof" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_SURJECTIONPROOF], [test x"$enable_module_surjectionproof" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_WHITELIST], [test x"$enable_module_whitelist" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_ECDSA_S2C], [test x"$enable_module_ecdsa_s2c" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_ECDSA_ADAPTOR], [test x"$enable_module_ecdsa_adaptor" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_BPPP], [test x"$enable_module_bppp" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_SCHNORRSIG_HALFAGG], [test x"$enable_module_schnorrsig_halfagg" = x"yes"])
2026-08-31 00:05:16 +02:00
AM_CONDITIONAL([ENABLE_MODULE_FROST], [test x"$enable_module_frost" = x"yes"])
chilldkg: Phase 0 - module scaffolding and build wiring Add an empty, experimental `chilldkg` module as the foundation for a ChillDKG implementation (distributed key generation for FROST) per the bip-frost-dkg BIP draft (v0.3.0-dev): https://github.com/BlockstreamResearch/bip-frost-dkg The module lives in src/modules/chilldkg/ (separate from the frost module, per the implementation plan in .idea/docs/ chilldkg-implementation-plan.md: FROST signing (BIP 445) and ChillDKG are separate BIPs with separate reference repos, test vectors and review cycles; the dependency between them is one-way bytes). New files: - include/secp256k1_chilldkg.h: public header skeleton with the same "EXTREMELY DANGEROUS / work in progress" warning style as secp256k1_frost.h, plus a note that the BIP is a draft and tagged hashes/wire formats may change. No API yet (Phase 3+). - src/modules/chilldkg/main_impl.h: implementation skeleton including the public header. - src/modules/chilldkg/tests_impl.h: trivial scaffolding unit test (chilldkg_scaffolding_test) registered via the tests_chilldkg[] CASE1 array used by this repo's unit-test framework. - src/modules/chilldkg/Makefile.am.include: autotools file list, mirroring the frost module's. - src/modules/chilldkg/chilldkg.md: module doc stub (purpose, draft status, dependency on the schnorrsig and ecdh modules). Build wiring (mirrors the frost module exactly): - configure.ac: --enable-module-chilldkg (default no, experimental gate), dependency errors when schnorrsig or ecdh are explicitly disabled, AM_CONDITIONAL(ENABLE_MODULE_CHILLDKG), summary line. - Makefile.am: include src/modules/chilldkg/Makefile.am.include under ENABLE_MODULE_CHILLDKG. - src/secp256k1.c: guarded include of modules/chilldkg/main_impl.h after the frost module. - src/tests.c: guarded include of tests_impl.h and MAKE_TEST_MODULE(chilldkg) registration. - CMakeLists.txt: SECP256K1_ENABLE_MODULE_CHILLDKG option (OFF) + summary line. - src/CMakeLists.txt: dependency checks on SECP256K1_ENABLE_MODULE_SCHNORRSIG and SECP256K1_ENABLE_MODULE_ECDH, ENABLE_MODULE_CHILLDKG=1 compile definition, public header export. Verified: - ./autogen.sh && ./configure --enable-experimental --enable-module-chilldkg --enable-module-schnorrsig --enable-module-ecdh && make check: PASS 3/3 (tests, noverify_tests, exhaustive_tests). - configure fails with a clear error when schnorrsig or ecdh are disabled, or when experimental is not enabled. - CMake build with SECP256K1_ENABLE_MODULE_CHILLDKG=ON: ctest 345/345 passed; dependency errors fire correctly when schnorrsig/ecdh OFF.
2026-08-31 01:37:22 +02:00
AM_CONDITIONAL([ENABLE_MODULE_CHILLDKG], [test x"$enable_module_chilldkg" = x"yes"])
iceberg: add the Iceberg threshold-MuSig module Port the experimental Iceberg module from the benchmark-iceberg tree (github.com/furszy/benchmark-iceberg, sources/secp256k1-kmp/native/ secp256k1) into this repo. Iceberg is a threshold scheme that lets a group of parties stand in for a single MuSig2 (BIP 327) participant: the group produces one ordinary MuSig2 public nonce and one ordinary MuSig2 partial signature, so cosigners cannot tell a group is involved and need no changes. Nonces are derived from a caller-chosen per-session label (sid32) rather than stored, so no signer holds a secret nonce between rounds; labels are public but must never be reused. A quorum of 2t-1 members (of whom up to t-1 may be corrupt) is needed in each round, so the threshold is at most half the group rounded up; combined with the scheme's other constraints the smallest usable group is 2-of-4. See doc/iceberg.md and the module header for the full usage notes. Module layout (src/modules/iceberg/, layered bottom-up, each layer may only use the ones above it -- that ordering is also the constant-time story): - scalar_poly.{h,_impl.h}: secret-carrying polynomial arithmetic, keeping secrets away from inversions (documented in the header). - rss.{h,_impl.h}: replicated secret sharing evaluation. - vpss.{h,_impl.h}: verifiable public shares; variable-time by design, sees only participant indices and published points. - keygen_impl.h: distributed key generation producing one share per member. - session_impl.h: nonce_gen/nonce_agg and partial_sign/ partial_sig_agg producing plain MuSig2 objects. - tests_impl.h: 28 tests including the shipped vectors.h vector suite and dealer known-answer tests. - bench_impl.h: benchmark definitions (wired in a follow-up commit). Public headers: include/secp256k1_iceberg.h (installed) and include/secp256k1_iceberg_dealer.h (in-tree only: a trusted dealer is not part of the shipped API, but tests, benchmarks and the example need to deal shares). Content adaptations relative to the source tree (the only changes to the ported code): three secp256k1_musig_nonce_process call sites in tests_impl.h gained a NULL adaptor argument, because this repo's musig is the zkp variant whose public nonce_process takes an optional adaptor point. All musig internals the module uses (ge_parse_ext, ge_serialize_ext, keyaggcoef, aggnonce_load, pubnonce_save, partial_sig_save, nonce_process_internal) are identical in both trees, as are all core headers the module touches; nothing else needed adaptation. Build wiring mirrors the chilldkg module: - configure.ac: --enable-module-iceberg (default no, experimental gate), hard dependency on the musig module with a configure error if musig is explicitly disabled (musig itself pulls in schnorrsig), AM_CONDITIONAL(ENABLE_MODULE_ICEBERG), summary line. - Makefile.am: include src/modules/iceberg/Makefile.am.include under the conditional. - src/secp256k1.c: guarded include of modules/iceberg/main_impl.h after the chilldkg block (musig is included earlier, so its internals are in scope). - src/tests.c: module test registration via MAKE_TEST_MODULE(iceberg). - CMakeLists.txt / src/CMakeLists.txt: SECP256K1_ENABLE_MODULE_ICEBERG option (OFF) with a dependency check on SECP256K1_ENABLE_MODULE_MUSIG (placed before the musig block so the force-enable takes effect), ENABLE_MODULE_ICEBERG=1 compile definition, public header export, summary line. Verified: ./configure --enable-experimental --enable-module-iceberg && make check passes; ./tests --target=iceberg runs the full module suite (28/28); CMake build + ctest pass; the musig dependency error fires correctly in both build systems.
2026-08-31 12:24:48 +02:00
AM_CONDITIONAL([ENABLE_MODULE_ICEBERG], [test x"$enable_module_iceberg" = x"yes"])
prefractal: add the nested FROST+MuSig2 module (API, implementation, wiring) Adds `prefractal`, an experimental module that lets a FROST t-of-n group occupy ONE participant slot of an ordinary MuSig2 (BIP 327) session. Each member computes s_i = k1_i + b_frost*b_musig*k2_i + e*a*lambda_i*g*gacc*d_i and the group publishes one ordinary MuSig2 public nonce and one ordinary MuSig2 partial signature, so cosigners need no support for it and cannot tell a group is involved. Four public functions, all sessionless (every call takes its session parameters explicitly, so there are no new opaque types, magics or *_SIZE constants to keep synchronised): secp256k1_prefractal_nonce_agg group wire nonce + unscaled aggnonce secp256k1_prefractal_sign one member's partial signature secp256k1_prefractal_partial_sig_verify identifiable abort secp256k1_prefractal_partial_sig_agg sum -> musig partial signature Three deliberate deviations from BIP 445, all documented in the public header: 1. b_frost does not commit to the message. The target protocols publish the group's wire nonce before the message exists, so a message-committing coefficient could not be computed in round one and rebuilt later. The outer b_musig does commit to the message and multiplies this one, so the product still binds it. Same trade the iceberg module makes, for the same reason. The preimage is BIP 445's with the message dropped and the group key carried in full rather than x-only, since it is used as a full point downstream. 2. There is NO g_frost factor. Stock FROST normalises its threshold key to even Y (g_times_gacc_parity = gacc_parity ^ pk_odd, frost/session_impl.h :664) because it produces a BIP 340 x-only signature. Here the threshold key is an inner participant of the outer key aggregation and is used as a full point, so all key-side parity normalisation happens once, at the aggregate level, off the OUTER keyagg cache. Note this is NOT implied by the tweak cache being the identity: with an identity cache g_frost is still -1 for every odd-Y group key, i.e. about half of them. Importing frost's key-side parity here would yield a signer that works for even-Y groups and fails for odd-Y ones. 3. The FROST tweak cache must be the identity (tacc == 0, gacc_parity == 0). Checked in sign and partial_sig_verify, not only in partial_sig_agg, so the key a member signs under is tied to the cache that was validated; sign and verify additionally require thresh_pk to equal the cache's own key so the two arguments cannot disagree. The verification equation lives in one helper used both by sign's BIP 445 self-check and by partial_sig_verify, so the two cannot drift apart. Build wiring. Three files order their module blocks differently and the constraints point in opposite directions: - src/secp256k1.c: the include goes AFTER frost and musig, because the module calls their static internals. - src/CMakeLists.txt: the block goes BEFORE both, because its set() calls are only observed by blocks that run later. - configure.ac: the block likewise goes before the musig block, NOT at iceberg's position further down. configure.ac orders musig and frost ahead of iceberg, and iceberg's late enable_module_musig=yes is harmless only because musig defaults to yes. frost defaults to no, so a late force-enable would leave -DENABLE_MODULE_FROST=1 unemitted while AM_CONDITIONAL still observed the mutation - a library whose secp256k1.c never included frost, built alongside frost's own sources. frost is also the first default-OFF module anything depends on, which breaks the dependency-guard idiom used everywhere else in both build systems: the existing "DEFINED X AND NOT X" (CMake) and "x$X = xno" (autotools) tests read as "the user disabled it explicitly" only for default-ON modules, and are true by default for a default-OFF one. Since neither build system can distinguish an explicit disable from the default once both are in the cache, enabling prefractal simply implies frost; the guard is kept for musig, where it still means what it says. The CMake block additionally lifts both dependencies into the parent scope so the top-level configuration summary reports what was actually built rather than printing "frost OFF" while compiling frost in. Verified on both build systems: cmake -B build -DSECP256K1_ENABLE_MODULE_PREFRACTAL=ON -DSECP256K1_BUILD_TESTS=ON -> musig/frost/prefractal all ON, tests pass, 4 prefractal symbols exported cmake -B build -DSECP256K1_BUILD_TESTS=ON -> prefractal OFF, default build unchanged, tests pass ./configure --enable-experimental --enable-module-prefractal && make && make check -> frost=yes forced on, -DENABLE_MODULE_FROST=1 emitted, 3/3 pass ./configure --enable-module-prefractal -> correctly refused: "Prefractal module is experimental" tests_impl.h is a placeholder here so the module links; the real suite lands next.
2026-09-04 00:44:43 +02:00
AM_CONDITIONAL([ENABLE_MODULE_PREFRACTAL], [test x"$enable_module_prefractal" = x"yes"])
build: wire the frost_enrollment module into both build systems Second of six commits adding the frost_enrollment module. This one is scaffolding only: the five entry points are stubs that validate their pointer arguments, zero their outputs and return 0. What is being verified here is that the module configures, compiles, links, exports its symbols and registers its test module in both build systems -- so that the next commit changes nothing but arithmetic. Ordering is the one thing in this commit that can go silently wrong, and it goes wrong in opposite directions in the two build systems: - configure.ac executes its `if` blocks in file order, and enable_module_frost defaults to no (configure.ac:243). A block placed after the frost block at :601 that sets enable_module_frost=yes flips the variable too late: AM_CONDITIONAL goes true, so the header is installed and the Makefile fragment is pulled in, but -DENABLE_MODULE_FROST=1 is never appended, so src/secp256k1.c never includes frost's implementation and every secp256k1_frost_* symbol fails to link. The new block therefore goes ahead of both the frost block and prefractal's, which documents the same trap. - src/CMakeLists.txt processes dependents FIRST, so the same block goes above the FROST block there, beside prefractal's. Verified rather than assumed: configuring with ONLY --enable-module-frost-enrollment emits -DENABLE_MODULE_FROST=1 alongside -DENABLE_MODULE_FROST_ENROLLMENT=1, and the CMake summary prints "frost ON" for the same configuration -- the latter is what the PARENT_SCOPE lift buys, since the summary runs after add_subdirectory(src) and would otherwise report a module it is compiling in as OFF. The dependency guard is prefractal's implies-frost idiom, copied verbatim along with its reasoning. frost is default-OFF, so the `test x"$enable_module_frost" = x"no"` / `DEFINED X AND NOT X` guard every other module uses -- which reads as "the user disabled it explicitly" for a default-ON dependency -- is true by default here and cannot tell an explicit --disable-module-frost from the default once both are in the cache. Enabling frost-enrollment simply implies frost, with no error. The one frost-module change in the whole series is in this commit: src/modules/frost/session.h gains a declaration for secp256k1_frost_sort_ids, which is defined at session_impl.h:517 and declared nowhere. The params hash needs it to canonicalize identifier order. Prefractal reaches frost's statics through translation-unit ordering alone; rather than inherit reuse-by-link-order, this declares the function where keygen.h:48 already declares derive_pubshare_at, so the reuse goes through an interface. No behavior change: it is a declaration for an existing static definition in the same TU. CI wiring is two files, and skipping either half fails quietly: - ci/ci.sh gets FROST_ENROLLMENT in the reproduction header's variable list and --enable-module-frost-enrollment="$FROST_ENROLLMENT" after the prefractal line. - .github/workflows/ci.yml gets FROST_ENROLLMENT at every PREFRACTAL site: the global default, 11 inline matrix entries and 10 job-level env blocks. Without the default, ci.sh runs under set -eux with an empty $FROST_ENROLLMENT, passes --enable-module-frost-enrollment="", `test x"" = x"yes"` is false, and the module is off in all of CI while ci.sh visibly has the plumbing. Verified programmatically over the parsed workflow: across the 106 effective job contexts, PREFRACTAL and FROST_ENROLLMENT now agree in every single one (45 set to yes, no mismatches), no context sets FROST_ENROLLMENT without FROST or without EXPERIMENTAL, and no context leaves it undefined. ci.sh passes sh -n. The stub test is not a placeholder that has to be deleted later: every entry point must reject an empty helper set and leave its output zeroed, which is true of the stubs and stays true of the finished implementation, so it doubles as the check that all five symbols are reachable from the test binary. Verification. Autotools: ./autogen.sh, then a frost-enrollment-only configure and a full configure with frost, chilldkg, iceberg, prefractal and frost-enrollment all on -- both build with zero warnings under the project's -Werror-grade flag set, ./tests and ./exhaustive_tests exit 0, and `./tests -l` lists the frost_enrollment module. CMake: configure with -DSECP256K1_EXPERIMENTAL=ON -DSECP256K1_ENABLE_MODULE_FROST_ENROLLMENT=ON builds clean and ctest passes 391 tests. nm shows the five new symbols exported from libsecp256k1.so; tools/symbol-check.py could not be run here because python3-lief is not installed in this environment, but all five carry the required secp256k1_ prefix. make dist succeeds and the tarball carries src/modules/frost_enrollment/frost_enrollment.md alongside the other module documents. One unrelated observation from this build: a stale src/ctime_tests-ctime_tests.o left over from an earlier configure with a different module set will fail to link, because automake does not track CPPFLAGS changes across reconfigures. make clean between configurations with different module sets, not a fault in this change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 03:53:03 +02:00
AM_CONDITIONAL([ENABLE_MODULE_FROST_ENROLLMENT], [test x"$enable_module_frost_enrollment" = x"yes"])
AM_CONDITIONAL([USE_REDUCED_SURJECTION_PROOF_SIZE], [test x"$use_reduced_surjection_proof_size" = x"yes"])
AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$enable_external_asm" = x"yes"])
2023-05-11 10:03:23 +01:00
AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm32"])
AM_CONDITIONAL([BUILD_WINDOWS], [test "$build_windows" = "yes"])
AC_SUBST(LIB_VERSION_CURRENT, _LIB_VERSION_CURRENT)
AC_SUBST(LIB_VERSION_REVISION, _LIB_VERSION_REVISION)
AC_SUBST(LIB_VERSION_AGE, _LIB_VERSION_AGE)
2014-01-17 22:52:33 -05:00
AC_OUTPUT
# The order in which all modules are listed here should be considered the
# canonical order. This order (or, when appropriate, its reserve) should be used
# everywhere we list or process modules, i.e., here and in other build system
# files and docs.
echo
echo "Build Options:"
echo " with external callbacks = $enable_external_default_callbacks"
echo " with benchmarks = $enable_benchmark"
echo " with tests = $enable_tests"
echo " with exhaustive tests = $enable_exhaustive_tests"
2022-12-06 23:12:15 -05:00
echo " with ctime tests = $enable_ctime_tests"
echo " with coverage = $enable_coverage"
echo " with examples = $enable_examples"
echo " module ecdh = $enable_module_ecdh"
echo " module recovery = $enable_module_recovery"
echo " module extrakeys = $enable_module_extrakeys"
echo " module schnorrsig = $enable_module_schnorrsig"
echo " module musig = $enable_module_musig"
echo " module ellswift = $enable_module_ellswift"
# libsecp256k1-zkp modules, in the order they were added to the libsecp256k1-zkp
echo " module generator = $enable_module_generator"
echo " module rangeproof = $enable_module_rangeproof"
echo " module surjectionproof = $enable_module_surjectionproof"
if test x"$enable_module_surjectionproof" = x"yes" && test x"$enable_reduced_surjection_proof_size" = x"yes"; then
echo " reduced proof size = $enable_reduced_surjection_proof_size"
fi
echo " module whitelist = $enable_module_whitelist"
echo " module ecdsa-s2c = $enable_module_ecdsa_s2c"
echo " module ecdsa-adaptor = $enable_module_ecdsa_adaptor"
2023-02-06 13:53:02 -08:00
echo " module bppp = $enable_module_bppp"
echo " module schnorrsig-halfagg = $enable_module_schnorrsig_halfagg"
2026-08-31 00:05:16 +02:00
echo " module frost = $enable_module_frost"
chilldkg: Phase 0 - module scaffolding and build wiring Add an empty, experimental `chilldkg` module as the foundation for a ChillDKG implementation (distributed key generation for FROST) per the bip-frost-dkg BIP draft (v0.3.0-dev): https://github.com/BlockstreamResearch/bip-frost-dkg The module lives in src/modules/chilldkg/ (separate from the frost module, per the implementation plan in .idea/docs/ chilldkg-implementation-plan.md: FROST signing (BIP 445) and ChillDKG are separate BIPs with separate reference repos, test vectors and review cycles; the dependency between them is one-way bytes). New files: - include/secp256k1_chilldkg.h: public header skeleton with the same "EXTREMELY DANGEROUS / work in progress" warning style as secp256k1_frost.h, plus a note that the BIP is a draft and tagged hashes/wire formats may change. No API yet (Phase 3+). - src/modules/chilldkg/main_impl.h: implementation skeleton including the public header. - src/modules/chilldkg/tests_impl.h: trivial scaffolding unit test (chilldkg_scaffolding_test) registered via the tests_chilldkg[] CASE1 array used by this repo's unit-test framework. - src/modules/chilldkg/Makefile.am.include: autotools file list, mirroring the frost module's. - src/modules/chilldkg/chilldkg.md: module doc stub (purpose, draft status, dependency on the schnorrsig and ecdh modules). Build wiring (mirrors the frost module exactly): - configure.ac: --enable-module-chilldkg (default no, experimental gate), dependency errors when schnorrsig or ecdh are explicitly disabled, AM_CONDITIONAL(ENABLE_MODULE_CHILLDKG), summary line. - Makefile.am: include src/modules/chilldkg/Makefile.am.include under ENABLE_MODULE_CHILLDKG. - src/secp256k1.c: guarded include of modules/chilldkg/main_impl.h after the frost module. - src/tests.c: guarded include of tests_impl.h and MAKE_TEST_MODULE(chilldkg) registration. - CMakeLists.txt: SECP256K1_ENABLE_MODULE_CHILLDKG option (OFF) + summary line. - src/CMakeLists.txt: dependency checks on SECP256K1_ENABLE_MODULE_SCHNORRSIG and SECP256K1_ENABLE_MODULE_ECDH, ENABLE_MODULE_CHILLDKG=1 compile definition, public header export. Verified: - ./autogen.sh && ./configure --enable-experimental --enable-module-chilldkg --enable-module-schnorrsig --enable-module-ecdh && make check: PASS 3/3 (tests, noverify_tests, exhaustive_tests). - configure fails with a clear error when schnorrsig or ecdh are disabled, or when experimental is not enabled. - CMake build with SECP256K1_ENABLE_MODULE_CHILLDKG=ON: ctest 345/345 passed; dependency errors fire correctly when schnorrsig/ecdh OFF.
2026-08-31 01:37:22 +02:00
echo " module chilldkg = $enable_module_chilldkg"
iceberg: add the Iceberg threshold-MuSig module Port the experimental Iceberg module from the benchmark-iceberg tree (github.com/furszy/benchmark-iceberg, sources/secp256k1-kmp/native/ secp256k1) into this repo. Iceberg is a threshold scheme that lets a group of parties stand in for a single MuSig2 (BIP 327) participant: the group produces one ordinary MuSig2 public nonce and one ordinary MuSig2 partial signature, so cosigners cannot tell a group is involved and need no changes. Nonces are derived from a caller-chosen per-session label (sid32) rather than stored, so no signer holds a secret nonce between rounds; labels are public but must never be reused. A quorum of 2t-1 members (of whom up to t-1 may be corrupt) is needed in each round, so the threshold is at most half the group rounded up; combined with the scheme's other constraints the smallest usable group is 2-of-4. See doc/iceberg.md and the module header for the full usage notes. Module layout (src/modules/iceberg/, layered bottom-up, each layer may only use the ones above it -- that ordering is also the constant-time story): - scalar_poly.{h,_impl.h}: secret-carrying polynomial arithmetic, keeping secrets away from inversions (documented in the header). - rss.{h,_impl.h}: replicated secret sharing evaluation. - vpss.{h,_impl.h}: verifiable public shares; variable-time by design, sees only participant indices and published points. - keygen_impl.h: distributed key generation producing one share per member. - session_impl.h: nonce_gen/nonce_agg and partial_sign/ partial_sig_agg producing plain MuSig2 objects. - tests_impl.h: 28 tests including the shipped vectors.h vector suite and dealer known-answer tests. - bench_impl.h: benchmark definitions (wired in a follow-up commit). Public headers: include/secp256k1_iceberg.h (installed) and include/secp256k1_iceberg_dealer.h (in-tree only: a trusted dealer is not part of the shipped API, but tests, benchmarks and the example need to deal shares). Content adaptations relative to the source tree (the only changes to the ported code): three secp256k1_musig_nonce_process call sites in tests_impl.h gained a NULL adaptor argument, because this repo's musig is the zkp variant whose public nonce_process takes an optional adaptor point. All musig internals the module uses (ge_parse_ext, ge_serialize_ext, keyaggcoef, aggnonce_load, pubnonce_save, partial_sig_save, nonce_process_internal) are identical in both trees, as are all core headers the module touches; nothing else needed adaptation. Build wiring mirrors the chilldkg module: - configure.ac: --enable-module-iceberg (default no, experimental gate), hard dependency on the musig module with a configure error if musig is explicitly disabled (musig itself pulls in schnorrsig), AM_CONDITIONAL(ENABLE_MODULE_ICEBERG), summary line. - Makefile.am: include src/modules/iceberg/Makefile.am.include under the conditional. - src/secp256k1.c: guarded include of modules/iceberg/main_impl.h after the chilldkg block (musig is included earlier, so its internals are in scope). - src/tests.c: module test registration via MAKE_TEST_MODULE(iceberg). - CMakeLists.txt / src/CMakeLists.txt: SECP256K1_ENABLE_MODULE_ICEBERG option (OFF) with a dependency check on SECP256K1_ENABLE_MODULE_MUSIG (placed before the musig block so the force-enable takes effect), ENABLE_MODULE_ICEBERG=1 compile definition, public header export, summary line. Verified: ./configure --enable-experimental --enable-module-iceberg && make check passes; ./tests --target=iceberg runs the full module suite (28/28); CMake build + ctest pass; the musig dependency error fires correctly in both build systems.
2026-08-31 12:24:48 +02:00
echo " module iceberg = $enable_module_iceberg"
prefractal: add the nested FROST+MuSig2 module (API, implementation, wiring) Adds `prefractal`, an experimental module that lets a FROST t-of-n group occupy ONE participant slot of an ordinary MuSig2 (BIP 327) session. Each member computes s_i = k1_i + b_frost*b_musig*k2_i + e*a*lambda_i*g*gacc*d_i and the group publishes one ordinary MuSig2 public nonce and one ordinary MuSig2 partial signature, so cosigners need no support for it and cannot tell a group is involved. Four public functions, all sessionless (every call takes its session parameters explicitly, so there are no new opaque types, magics or *_SIZE constants to keep synchronised): secp256k1_prefractal_nonce_agg group wire nonce + unscaled aggnonce secp256k1_prefractal_sign one member's partial signature secp256k1_prefractal_partial_sig_verify identifiable abort secp256k1_prefractal_partial_sig_agg sum -> musig partial signature Three deliberate deviations from BIP 445, all documented in the public header: 1. b_frost does not commit to the message. The target protocols publish the group's wire nonce before the message exists, so a message-committing coefficient could not be computed in round one and rebuilt later. The outer b_musig does commit to the message and multiplies this one, so the product still binds it. Same trade the iceberg module makes, for the same reason. The preimage is BIP 445's with the message dropped and the group key carried in full rather than x-only, since it is used as a full point downstream. 2. There is NO g_frost factor. Stock FROST normalises its threshold key to even Y (g_times_gacc_parity = gacc_parity ^ pk_odd, frost/session_impl.h :664) because it produces a BIP 340 x-only signature. Here the threshold key is an inner participant of the outer key aggregation and is used as a full point, so all key-side parity normalisation happens once, at the aggregate level, off the OUTER keyagg cache. Note this is NOT implied by the tweak cache being the identity: with an identity cache g_frost is still -1 for every odd-Y group key, i.e. about half of them. Importing frost's key-side parity here would yield a signer that works for even-Y groups and fails for odd-Y ones. 3. The FROST tweak cache must be the identity (tacc == 0, gacc_parity == 0). Checked in sign and partial_sig_verify, not only in partial_sig_agg, so the key a member signs under is tied to the cache that was validated; sign and verify additionally require thresh_pk to equal the cache's own key so the two arguments cannot disagree. The verification equation lives in one helper used both by sign's BIP 445 self-check and by partial_sig_verify, so the two cannot drift apart. Build wiring. Three files order their module blocks differently and the constraints point in opposite directions: - src/secp256k1.c: the include goes AFTER frost and musig, because the module calls their static internals. - src/CMakeLists.txt: the block goes BEFORE both, because its set() calls are only observed by blocks that run later. - configure.ac: the block likewise goes before the musig block, NOT at iceberg's position further down. configure.ac orders musig and frost ahead of iceberg, and iceberg's late enable_module_musig=yes is harmless only because musig defaults to yes. frost defaults to no, so a late force-enable would leave -DENABLE_MODULE_FROST=1 unemitted while AM_CONDITIONAL still observed the mutation - a library whose secp256k1.c never included frost, built alongside frost's own sources. frost is also the first default-OFF module anything depends on, which breaks the dependency-guard idiom used everywhere else in both build systems: the existing "DEFINED X AND NOT X" (CMake) and "x$X = xno" (autotools) tests read as "the user disabled it explicitly" only for default-ON modules, and are true by default for a default-OFF one. Since neither build system can distinguish an explicit disable from the default once both are in the cache, enabling prefractal simply implies frost; the guard is kept for musig, where it still means what it says. The CMake block additionally lifts both dependencies into the parent scope so the top-level configuration summary reports what was actually built rather than printing "frost OFF" while compiling frost in. Verified on both build systems: cmake -B build -DSECP256K1_ENABLE_MODULE_PREFRACTAL=ON -DSECP256K1_BUILD_TESTS=ON -> musig/frost/prefractal all ON, tests pass, 4 prefractal symbols exported cmake -B build -DSECP256K1_BUILD_TESTS=ON -> prefractal OFF, default build unchanged, tests pass ./configure --enable-experimental --enable-module-prefractal && make && make check -> frost=yes forced on, -DENABLE_MODULE_FROST=1 emitted, 3/3 pass ./configure --enable-module-prefractal -> correctly refused: "Prefractal module is experimental" tests_impl.h is a placeholder here so the module links; the real suite lands next.
2026-09-04 00:44:43 +02:00
echo " module prefractal = $enable_module_prefractal"
build: wire the frost_enrollment module into both build systems Second of six commits adding the frost_enrollment module. This one is scaffolding only: the five entry points are stubs that validate their pointer arguments, zero their outputs and return 0. What is being verified here is that the module configures, compiles, links, exports its symbols and registers its test module in both build systems -- so that the next commit changes nothing but arithmetic. Ordering is the one thing in this commit that can go silently wrong, and it goes wrong in opposite directions in the two build systems: - configure.ac executes its `if` blocks in file order, and enable_module_frost defaults to no (configure.ac:243). A block placed after the frost block at :601 that sets enable_module_frost=yes flips the variable too late: AM_CONDITIONAL goes true, so the header is installed and the Makefile fragment is pulled in, but -DENABLE_MODULE_FROST=1 is never appended, so src/secp256k1.c never includes frost's implementation and every secp256k1_frost_* symbol fails to link. The new block therefore goes ahead of both the frost block and prefractal's, which documents the same trap. - src/CMakeLists.txt processes dependents FIRST, so the same block goes above the FROST block there, beside prefractal's. Verified rather than assumed: configuring with ONLY --enable-module-frost-enrollment emits -DENABLE_MODULE_FROST=1 alongside -DENABLE_MODULE_FROST_ENROLLMENT=1, and the CMake summary prints "frost ON" for the same configuration -- the latter is what the PARENT_SCOPE lift buys, since the summary runs after add_subdirectory(src) and would otherwise report a module it is compiling in as OFF. The dependency guard is prefractal's implies-frost idiom, copied verbatim along with its reasoning. frost is default-OFF, so the `test x"$enable_module_frost" = x"no"` / `DEFINED X AND NOT X` guard every other module uses -- which reads as "the user disabled it explicitly" for a default-ON dependency -- is true by default here and cannot tell an explicit --disable-module-frost from the default once both are in the cache. Enabling frost-enrollment simply implies frost, with no error. The one frost-module change in the whole series is in this commit: src/modules/frost/session.h gains a declaration for secp256k1_frost_sort_ids, which is defined at session_impl.h:517 and declared nowhere. The params hash needs it to canonicalize identifier order. Prefractal reaches frost's statics through translation-unit ordering alone; rather than inherit reuse-by-link-order, this declares the function where keygen.h:48 already declares derive_pubshare_at, so the reuse goes through an interface. No behavior change: it is a declaration for an existing static definition in the same TU. CI wiring is two files, and skipping either half fails quietly: - ci/ci.sh gets FROST_ENROLLMENT in the reproduction header's variable list and --enable-module-frost-enrollment="$FROST_ENROLLMENT" after the prefractal line. - .github/workflows/ci.yml gets FROST_ENROLLMENT at every PREFRACTAL site: the global default, 11 inline matrix entries and 10 job-level env blocks. Without the default, ci.sh runs under set -eux with an empty $FROST_ENROLLMENT, passes --enable-module-frost-enrollment="", `test x"" = x"yes"` is false, and the module is off in all of CI while ci.sh visibly has the plumbing. Verified programmatically over the parsed workflow: across the 106 effective job contexts, PREFRACTAL and FROST_ENROLLMENT now agree in every single one (45 set to yes, no mismatches), no context sets FROST_ENROLLMENT without FROST or without EXPERIMENTAL, and no context leaves it undefined. ci.sh passes sh -n. The stub test is not a placeholder that has to be deleted later: every entry point must reject an empty helper set and leave its output zeroed, which is true of the stubs and stays true of the finished implementation, so it doubles as the check that all five symbols are reachable from the test binary. Verification. Autotools: ./autogen.sh, then a frost-enrollment-only configure and a full configure with frost, chilldkg, iceberg, prefractal and frost-enrollment all on -- both build with zero warnings under the project's -Werror-grade flag set, ./tests and ./exhaustive_tests exit 0, and `./tests -l` lists the frost_enrollment module. CMake: configure with -DSECP256K1_EXPERIMENTAL=ON -DSECP256K1_ENABLE_MODULE_FROST_ENROLLMENT=ON builds clean and ctest passes 391 tests. nm shows the five new symbols exported from libsecp256k1.so; tools/symbol-check.py could not be run here because python3-lief is not installed in this environment, but all five carry the required secp256k1_ prefix. make dist succeeds and the tarball carries src/modules/frost_enrollment/frost_enrollment.md alongside the other module documents. One unrelated observation from this build: a stale src/ctime_tests-ctime_tests.o left over from an earlier configure with a different module set will fail to link, because automake does not track CPPFLAGS changes across reconfigures. make clean between configurations with different module sets, not a fault in this change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 03:53:03 +02:00
echo " module frost-enrollment = $enable_module_frost_enrollment"
echo
echo " asm = $set_asm"
echo " ecmult window size = $set_ecmult_window"
echo " ecmult gen table size = $set_ecmult_gen_kb KiB"
# Hide test-only options unless they're used.
if test x"$set_widemul" != xauto; then
echo " wide multiplication = $set_widemul"
fi
echo
echo " valgrind = $enable_valgrind"
echo " CC = $CC"
echo " CPPFLAGS = $CPPFLAGS"
echo " SECP_CFLAGS = $SECP_CFLAGS"
echo " CFLAGS = $CFLAGS"
echo " LDFLAGS = $LDFLAGS"
if test x"$print_msan_notice" = x"yes"; then
echo
echo "Note:"
echo " MemorySanitizer detected, tried to add -fno-sanitize-memory-param-retval to SECP_CFLAGS"
echo " to avoid false positives in ctime_tests. Pass --disable-ctime-tests to avoid this."
fi
if test x"$enable_experimental" = x"yes"; then
echo
echo "WARNING: Experimental build"
echo " Experimental features do not have stable APIs or properties, and may not be safe for"
echo " production use."
fi