From d7d37c944809f8f957e7ad043b540feb4f25503c Mon Sep 17 00:00:00 2001 From: Matt Whitlock Date: Sun, 17 Sep 2023 10:29:27 -0400 Subject: [PATCH] support linking against system-installed leveldb and libsecp256k1 - Abort if runtime leveldb != compiled-against leveldb. Originally based on 22.0-fix_build_without_leveldb.patch. --- configure.ac | 90 ++++++++++++++++++++++++++++++++++++++- src/Makefile.am | 13 +++++- src/Makefile.test.include | 2 + src/dbwrapper.cpp | 27 +++++++++++- src/dbwrapper.h | 8 ++++ src/kernel/checks.cpp | 7 +++ 6 files changed, 143 insertions(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac index d39006be33..ee7b092131 100644 --- a/configure.ac +++ b/configure.ac @@ -1291,6 +1291,41 @@ if test "$enable_fuzz_binary" = "yes"; then ]])]) fi +dnl Check for libsecp256k1, only if explicitly requested +AC_ARG_WITH([system-libsecp256k1], + [AS_HELP_STRING([[--with-system-libsecp256k1[=PKG]]], + [build using system-installed libsecp256k1 instead of bundled, passing PKG (default: libsecp256k1) to pkg-config (default is no; DANGEROUS; NOT SUPPORTED)])], + [AS_IF([test "x$withval" = xyes], [with_system_libsecp256k1=libsecp256k1])], + [with_system_libsecp256k1=no]) +AM_CONDITIONAL([EMBEDDED_LIBSECP256K1],[test "x$with_system_libsecp256k1" = xno]) +AM_COND_IF([EMBEDDED_LIBSECP256K1], [ + libsecp256k1_CFLAGS='-I$(srcdir)/secp256k1/include' + libsecp256k1_LIBS='secp256k1/libsecp256k1.la' +], [ + saved_CPPFLAGS=$CPPFLAGS + saved_LIBS=$LIBS + PKG_CHECK_MODULES([libsecp256k1], [$with_system_libsecp256k1]) + CPPFLAGS="$libsecp256k1_CFLAGS $CPPFLAGS" + LIBS="$libsecp256k1_LIBS $LIBS" + missing_modules= + AC_DEFUN([CHECK_MODULE], [ + AC_CHECK_HEADER([secp256k1_$1.h], + [AC_CHECK_FUNCS([$2], [], [missing_modules="${missing_modules} $1"])], + [missing_modules="${missing_modules} $1"]) + ]) + CHECK_MODULE([ellswift], [secp256k1_ellswift_encode]) + CHECK_MODULE([extrakeys], [secp256k1_xonly_pubkey_parse]) + CHECK_MODULE([recovery], [secp256k1_ecdsa_recover]) + CHECK_MODULE([schnorrsig], [secp256k1_schnorrsig_verify]) + AS_IF([test -n "${missing_modules}"], [ + AC_MSG_ERROR([system-installed libsecp256k1 does not support these required modules:${missing_modules}]) + ]) + CPPFLAGS=$saved_CPPFLAGS + LIBS=$saved_LIBS +]) +AC_SUBST(libsecp256k1_CFLAGS) +AC_SUBST(libsecp256k1_LIBS) + if test "$enable_wallet" != "no"; then dnl Check for libdb_cxx only if wallet enabled if test "$use_bdb" != "no"; then @@ -1350,6 +1385,55 @@ if test "$build_bitcoind$bitcoin_enable_qt$use_bench$use_tests" = "nononono"; th use_zmq=no fi +dnl Check for leveldb, only if explicitly requested +AC_ARG_WITH([system-leveldb], + [AS_HELP_STRING([--with-system-leveldb], + [Build with system LevelDB (default is no; DANGEROUS; NOT SUPPORTED)])], + [system_leveldb=$withval], + [system_leveldb=no]) +AC_ARG_VAR([leveldb_CFLAGS],[C compiler flags for system-leveldb]) +AC_ARG_VAR([leveldb_LIBS],[linker flags for system-leveldb]) +AS_IF([test x$system_leveldb != xno],[ + TEMP_CPPFLAGS="$CPPFLAGS" + TEMP_LIBS="$LIBS" + CPPFLAGS="$leveldb_CFLAGS" + LIBS="$leveldb_LIBS" + AC_SEARCH_LIBS([leveldb_open],[leveldb],[leveldb_LIBS="$LIBS"], + [AC_MSG_ERROR([leveldb library not found; using --with-system-leveldb is not supported anyway])]) + AC_CHECK_HEADER([leveldb/filter_policy.h],[], + [AC_MSG_ERROR([LevelDB headers not found; using --with-system-leveldb is not supported anyway])]) + AC_CHECK_HEADER([leveldb/helpers/memenv.h],[], + [AC_MSG_ERROR([LevelDB headers not found; using --with-system-leveldb is not supported anyway])]) + + AC_MSG_CHECKING([for library containing leveldb::NewMemEnv]) + for searchlib in "" "-lmemenv" ERR; do + if test "x$searchlib" = "xERR"; then + AC_MSG_RESULT([no]) + AC_MSG_ERROR([LevelDB's memenv helper not found; using --with-system-leveldb is not supported anyway]) + fi + LIBS="$searchlib $leveldb_LIBS" + AC_LINK_IFELSE([AC_LANG_PROGRAM([ + #include + #include + ],[ + leveldb::Env *myenv = leveldb::NewMemEnv(leveldb::Env::Default()); + delete myenv; + ]) + ],[ + AC_MSG_RESULT([$searchlib]) + break + ]) + done + leveldb_LIBS="$LIBS" + LIBS="$TEMP_LIBS" + CPPFLAGS="$TEMP_CPPFLAGS" +],[ + AC_DEFINE([EMBEDDED_LEVELDB],[1],[Define to use the bundled LevelDB sources]) +]) +AM_CONDITIONAL([EMBEDDED_LEVELDB],[test x$system_leveldb = xno]) +AC_SUBST(leveldb_CFLAGS) +AC_SUBST(leveldb_LIBS) + dnl Check for libminiupnpc (optional) if test "$use_upnp" != "no"; then TEMP_CPPFLAGS="$CPPFLAGS" @@ -1897,8 +1981,10 @@ CPPFLAGS="$CPPFLAGS_TEMP" if test -n "$use_sanitizers"; then export SECP_CFLAGS="$SECP_CFLAGS $SANITIZER_CFLAGS" fi -ac_configure_args="${ac_configure_args} --disable-shared --with-pic --enable-benchmark=no --enable-module-recovery --disable-module-ecdh" -AC_CONFIG_SUBDIRS([src/secp256k1]) +AM_COND_IF([EMBEDDED_LIBSECP256K1],[ + ac_configure_args="${ac_configure_args} --disable-shared --with-pic --enable-benchmark=no --enable-module-recovery --disable-module-ecdh" + AC_CONFIG_SUBDIRS([src/secp256k1]) +]) AC_OUTPUT diff --git a/src/Makefile.am b/src/Makefile.am index b5d5c4652a..91dec582e2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -24,7 +24,7 @@ check_PROGRAMS = TESTS = BENCHMARKS = -BITCOIN_INCLUDES=-I$(builddir) -I$(srcdir)/$(MINISKETCH_INCLUDE_DIR_INT) -I$(srcdir)/secp256k1/include -I$(srcdir)/$(UNIVALUE_INCLUDE_DIR_INT) +BITCOIN_INCLUDES=-I$(builddir) -I$(srcdir)/$(MINISKETCH_INCLUDE_DIR_INT) $(libsecp256k1_CFLAGS) -I$(srcdir)/$(UNIVALUE_INCLUDE_DIR_INT) LIBBITCOIN_NODE=libbitcoin_node.a LIBBITCOIN_COMMON=libbitcoin_common.a @@ -33,7 +33,11 @@ LIBBITCOIN_CLI=libbitcoin_cli.a LIBBITCOIN_UTIL=libbitcoin_util.a LIBBITCOIN_CRYPTO_BASE=crypto/libbitcoin_crypto_base.la LIBBITCOINQT=qt/libbitcoinqt.a +if EMBEDDED_LIBSECP256K1 LIBSECP256K1=secp256k1/libsecp256k1.la +else +LIBSECP256K1=$(libsecp256k1_LIBS) +endif if ENABLE_ZMQ LIBBITCOIN_ZMQ=libbitcoin_zmq.a @@ -68,8 +72,10 @@ LIBBITCOIN_CRYPTO += $(LIBBITCOIN_CRYPTO_ARM_SHANI) endif noinst_LTLIBRARIES += $(LIBBITCOIN_CRYPTO) +if EMBEDDED_LIBSECP256K1 $(LIBSECP256K1): $(wildcard secp256k1/src/*.h) $(wildcard secp256k1/src/*.c) $(wildcard secp256k1/include/*) $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F) +endif # Make is not made aware of per-object dependencies to avoid limiting building parallelization # But to build the less dependent modules first, we manually select their order here: @@ -1132,8 +1138,13 @@ endif include Makefile.minisketch.include +if EMBEDDED_LEVELDB include Makefile.crc32c.include include Makefile.leveldb.include +else +LEVELDB_CPPFLAGS = $(leveldb_CFLAGS) +LIBLEVELDB = $(leveldb_LIBS) +endif include Makefile.test_util.include include Makefile.test_fuzz.include diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 9f9bdbbd0c..3585e16bbe 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -435,7 +435,9 @@ if ENABLE_BENCH $(BENCH_BINARY) -sanity-check -priority-level=high endif endif +if EMBEDDED_LIBSECP256K1 $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C secp256k1 check +endif if ENABLE_TESTS UNIVALUE_TESTS = univalue/test/object univalue/test/unitester diff --git a/src/dbwrapper.cpp b/src/dbwrapper.cpp index 775496e21b..1304b2a2b3 100644 --- a/src/dbwrapper.cpp +++ b/src/dbwrapper.cpp @@ -22,7 +22,11 @@ #include #include #include -#include +#if EMBEDDED_LEVELDB +# include +#else +# include +#endif #include #include #include @@ -51,6 +55,27 @@ static void HandleError(const leveldb::Status& status) throw dbwrapper_error(errmsg); } +#if !EMBEDDED_LEVELDB +#include +#include +#include +bool dbwrapper_SanityCheck() +{ + unsigned long header_version = (leveldb::kMajorVersion << 16) | leveldb::kMinorVersion; + unsigned long library_version = (leveldb_major_version() << 16) | leveldb_minor_version(); + + if (header_version != library_version) { + InitError(Untranslated(strprintf("Compiled with LevelDB %d.%d, but linked with LevelDB %d.%d (incompatible).", + leveldb::kMajorVersion, leveldb::kMinorVersion, + leveldb_major_version(), leveldb_minor_version() + ))); + return false; + } + + return true; +} +#endif + class CBitcoinLevelDBLogger : public leveldb::Logger { public: // This code is adapted from posix_logger.h, which is why it is using vsprintf. diff --git a/src/dbwrapper.h b/src/dbwrapper.h index 63c2f99d2a..406d03f1ea 100644 --- a/src/dbwrapper.h +++ b/src/dbwrapper.h @@ -5,6 +5,10 @@ #ifndef BITCOIN_DBWRAPPER_H #define BITCOIN_DBWRAPPER_H +#if defined(HAVE_CONFIG_H) +#include +#endif + #include #include #include @@ -46,6 +50,10 @@ struct DBParams { DBOptions options{}; }; +#if !EMBEDDED_LEVELDB +bool dbwrapper_SanityCheck(); +#endif + class dbwrapper_error : public std::runtime_error { public: diff --git a/src/kernel/checks.cpp b/src/kernel/checks.cpp index bf8a2ec74c..640deb2415 100644 --- a/src/kernel/checks.cpp +++ b/src/kernel/checks.cpp @@ -4,6 +4,7 @@ #include +#include #include #include #include @@ -15,6 +16,12 @@ namespace kernel { util::Result SanityChecks(const Context&) { +#if !EMBEDDED_LEVELDB + if (!dbwrapper_SanityCheck()) { + return util::Error{Untranslated("Database sanity check failure. Aborting.")}; + } +#endif + if (!ECC_InitSanityCheck()) { return util::Error{Untranslated("Elliptic curve cryptography sanity check failure. Aborting.")}; } -- 2.44.0