Merge bitcoin/bitcoin#29015: kernel: Streamline util library

c7376babd1 doc: Clarify distinction between util and common libraries in libraries.md (Ryan Ofsky)
4f74c59334 util: Move util/string.h functions to util namespace (Ryan Ofsky)
4d05d3f3b4 util: add TransactionError includes and namespace declarations (Ryan Ofsky)
680eafdc74 util: move fees.h and error.h to common/messages.h (Ryan Ofsky)
02e62c6c9a common: Add PSBTError enum (Ryan Ofsky)
0d44c44ae3 util: move error.h TransactionError enum to node/types.h (Ryan Ofsky)
9bcce2608d util: move spanparsing.h to script/parsing.h (Ryan Ofsky)
6dd2ad4792 util: move spanparsing.h Split functions to string.h (Ryan Ofsky)
23cc8ddff4 util: move HexStr and HexDigit from util to crypto (TheCharlatan)
6861f954f8 util: move util/message to common/signmessage (Ryan Ofsky)
cc5f29fbea build: move memory_cleanse from util to crypto (Ryan Ofsky)
5b9309420c build: move chainparamsbase from util to common (Ryan Ofsky)
ffa27af24d test: Add check-deps.sh script to check for unexpected library dependencies (Ryan Ofsky)

Pull request description:

  Remove `fees.h`, `errors.h`, and `spanparsing.h` from the util library. Specifically:

  - Move `Split` functions from `util/spanparsing.h` to `util/string.h`, using `util` namespace for clarity.
  - Move remaining spanparsing functions to `script/parsing.h` since they are used for descriptor and miniscript parsing.
  - Combine `util/fees.h` and `util/errors.h` into `common/messages.h` so there is a place for simple functions that generate user messages to live, and these functions are not part of the util library.

  Motivation for this change is that the util library is a dependency of the kernel, and we should remove functionality from util that shouldn't be called by kernel code or kernel applications. These changes should also improve code organization and make functions easier to discover. Some of these same moves are (or were) part of #28690, but did not help with code organization, or made it worse, so it is better to move them and clean them up in the same PR so code only has to change one time.

ACKs for top commit:
  achow101:
    ACK c7376babd1
  TheCharlatan:
    Re-ACK c7376babd1
  hebasto:
    re-ACK c7376babd1.

Tree-SHA512: 5bcef16c1255463b1b69270548711e7ff78ca0dd34e300b95e3ca1ce52ceb34f83d9ddb2839e83800ba36b200de30396e504bbb04fa02c6d0c24a16d06ae523d
This commit is contained in:
Ava Chow
2024-06-12 17:12:54 -04:00
121 changed files with 1023 additions and 526 deletions

203
contrib/devtools/check-deps.sh Executable file
View File

@@ -0,0 +1,203 @@
#!/usr/bin/env bash
export LC_ALL=C
set -Eeuo pipefail
# Declare paths to libraries
declare -A LIBS
LIBS[cli]="libbitcoin_cli.a"
LIBS[common]="libbitcoin_common.a"
LIBS[consensus]="libbitcoin_consensus.a"
LIBS[crypto]="crypto/.libs/libbitcoin_crypto_base.a crypto/.libs/libbitcoin_crypto_x86_shani.a crypto/.libs/libbitcoin_crypto_sse41.a crypto/.libs/libbitcoin_crypto_avx2.a"
LIBS[node]="libbitcoin_node.a"
LIBS[util]="libbitcoin_util.a"
LIBS[wallet]="libbitcoin_wallet.a"
LIBS[wallet_tool]="libbitcoin_wallet_tool.a"
# Declare allowed dependencies "X Y" where X is allowed to depend on Y. This
# list is taken from doc/design/libraries.md.
ALLOWED_DEPENDENCIES=(
"cli common"
"cli util"
"common consensus"
"common crypto"
"common util"
"consensus crypto"
"node common"
"node consensus"
"node crypto"
"node kernel"
"node util"
"util crypto"
"wallet common"
"wallet crypto"
"wallet util"
"wallet_tool util"
"wallet_tool wallet"
)
# Add minor dependencies omitted from doc/design/libraries.md to keep the
# dependency diagram simple.
ALLOWED_DEPENDENCIES+=(
"wallet consensus"
"wallet_tool common"
"wallet_tool crypto"
)
# Declare list of known errors that should be suppressed.
declare -A SUPPRESS
# init.cpp file currently calls Berkeley DB sanity check function on startup, so
# there is an undocumented dependency of the node library on the wallet library.
SUPPRESS["libbitcoin_node_a-init.o libbitcoin_wallet_a-bdb.o _ZN6wallet27BerkeleyDatabaseSanityCheckEv"]=1
# init/common.cpp file calls InitError and InitWarning from interface_ui which
# is currently part of the node library. interface_ui should just be part of the
# common library instead, and is moved in
# https://github.com/bitcoin/bitcoin/issues/10102
SUPPRESS["libbitcoin_common_a-common.o libbitcoin_node_a-interface_ui.o _Z11InitWarningRK13bilingual_str"]=1
SUPPRESS["libbitcoin_common_a-common.o libbitcoin_node_a-interface_ui.o _Z9InitErrorRK13bilingual_str"]=1
# rpc/external_signer.cpp adds defines node RPC methods but is built as part of the
# common library. It should be moved to the node library instead.
SUPPRESS["libbitcoin_common_a-external_signer.o libbitcoin_node_a-server.o _ZN9CRPCTable13appendCommandERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEPK11CRPCCommand"]=1
usage() {
echo "Usage: $(basename "${BASH_SOURCE[0]}") [BUILD_DIR]"
}
# Output makefile targets, converting library .a paths to libtool .la targets
lib_targets() {
for lib in "${!LIBS[@]}"; do
for lib_path in ${LIBS[$lib]}; do
# shellcheck disable=SC2001
sed 's:/.libs/\(.*\)\.a$:/\1.la:g' <<<"$lib_path"
done
done
}
# Extract symbol names and object names and write to text files
extract_symbols() {
local temp_dir="$1"
for lib in "${!LIBS[@]}"; do
for lib_path in ${LIBS[$lib]}; do
nm -o "$lib_path" | grep ' T ' | awk '{print $3, $1}' >> "${temp_dir}/${lib}_exports.txt"
nm -o "$lib_path" | grep ' U ' | awk '{print $3, $1}' >> "${temp_dir}/${lib}_imports.txt"
awk '{print $1}' "${temp_dir}/${lib}_exports.txt" | sort -u > "${temp_dir}/${lib}_exported_symbols.txt"
awk '{print $1}' "${temp_dir}/${lib}_imports.txt" | sort -u > "${temp_dir}/${lib}_imported_symbols.txt"
done
done
}
# Lookup object name(s) corresponding to symbol name in text file
obj_names() {
local symbol="$1"
local txt_file="$2"
sed -n "s/^$symbol [^:]\\+:\\([^:]\\+\\):[^:]*\$/\\1/p" "$txt_file" | sort -u
}
# Iterate through libraries and find disallowed dependencies
check_libraries() {
local temp_dir="$1"
local result=0
for src in "${!LIBS[@]}"; do
for dst in "${!LIBS[@]}"; do
if [ "$src" != "$dst" ] && ! is_allowed "$src" "$dst"; then
if ! check_disallowed "$src" "$dst" "$temp_dir"; then
result=1
fi
fi
done
done
check_not_suppressed
return $result
}
# Return whether src library is allowed to depend on dst.
is_allowed() {
local src="$1"
local dst="$2"
for allowed in "${ALLOWED_DEPENDENCIES[@]}"; do
if [ "$src $dst" = "$allowed" ]; then
return 0
fi
done
return 1
}
# Return whether src library imports any symbols from dst, assuming src is not
# allowed to depend on dst.
check_disallowed() {
local src="$1"
local dst="$2"
local temp_dir="$3"
local result=0
# Loop over symbol names exported by dst and imported by src
while read symbol; do
local dst_obj
dst_obj=$(obj_names "$symbol" "${temp_dir}/${dst}_exports.txt")
while read src_obj; do
if ! check_suppress "$src_obj" "$dst_obj" "$symbol"; then
echo "Error: $src_obj depends on $dst_obj symbol '$(c++filt "$symbol")', can suppess with:"
echo " SUPPRESS[\"$src_obj $dst_obj $symbol\"]=1"
result=1
fi
done < <(obj_names "$symbol" "${temp_dir}/${src}_imports.txt")
done < <(comm -12 "${temp_dir}/${dst}_exported_symbols.txt" "${temp_dir}/${src}_imported_symbols.txt")
return $result
}
# Declare array to track errors which were suppressed.
declare -A SUPPRESSED
# Return whether error should be suppressed and record suppresssion in
# SUPPRESSED array.
check_suppress() {
local src_obj="$1"
local dst_obj="$2"
local symbol="$3"
for suppress in "${!SUPPRESS[@]}"; do
read suppress_src suppress_dst suppress_pattern <<<"$suppress"
if [[ "$src_obj" == "$suppress_src" && "$dst_obj" == "$suppress_dst" && "$symbol" =~ $suppress_pattern ]]; then
SUPPRESSED["$suppress"]=1
return 0
fi
done
return 1
}
# Warn about error which were supposed to be suppress, but were not encountered.
check_not_suppressed() {
for suppress in "${!SUPPRESS[@]}"; do
if [[ ! -v SUPPRESSED[$suppress] ]]; then
echo >&2 "Warning: suppression '$suppress' was ignored, consider deleting."
fi
done
}
# Check arguments.
if [ "$#" = 0 ]; then
BUILD_DIR="$(dirname "${BASH_SOURCE[0]}")/../../src"
elif [ "$#" = 1 ]; then
BUILD_DIR="$1"
else
echo >&2 "Error: wrong number of arguments."
usage >&2
exit 1
fi
if [ ! -f "$BUILD_DIR/Makefile" ]; then
echo >&2 "Error: directory '$BUILD_DIR' does not contain a makefile, please specify path to build directory for library targets."
usage >&2
exit 1
fi
# Build libraries and run checks.
cd "$BUILD_DIR"
# shellcheck disable=SC2046
make -j"$(nproc)" $(lib_targets)
TEMP_DIR="$(mktemp -d)"
extract_symbols "$TEMP_DIR"
if check_libraries "$TEMP_DIR"; then
echo "Success! No unexpected dependencies were detected."
else
echo >&2 "Error: Unexpected dependencies were detected. Check previous output."
fi
rm -r "$TEMP_DIR"