mirror of
https://github.com/Retropex/bitcoin.git
synced 2025-05-12 19:20:42 +02:00
Merge bitcoin/bitcoin#28710: Remove the legacy wallet and BDB dependency
de054df6dc
contrib: Remove legacy wallet RPCs from bash completions (Ava Chow)5dff04a1bb
legacy spkm: Make IsMine() and CanProvide() private and migration only (Ava Chow)c0f3f3264f
wallet: Remove unused db functions (Ava Chow)83af1a3cca
wallet: Delete LegacySPKM (Ava Chow)8ede6dea0c
wallet, rpc: Remove legacy wallet only RPCs (Ava Chow)4de3cec28d
test: rpcs disabled for descriptor wallets will be removed (Ava Chow)84f671b01d
test: Run multisig script limit test (Ava Chow)810476f31e
test: Remove unused options and variables, correct comments (Ava Chow)04a7a7a28c
build, wallet, doc: Remove BDB (Ava Chow) Pull request description: The final step of #20160. A bare minimum of legacy wallet code is kept in order to perform wallet migration. Migration of legacy wallets uses the independent BDB parser and a minimal `LegacyDataSPKM` that allows the legacy data to be loaded so that the migration can be completed. BDB has been removed as a dependency and documentation have been updated to reflect that. ACKs for top commit: Sjors: re-ACKde054df6dc
maflcko: re-ACKde054df6dc
🔗 w0xlt: reACKde054df6dc
rkrux: Concept ACKde054df6dc
Tree-SHA512: 16a6c265bc1ada5e7a5ef9b95f0ff65015672ca46d9a43b7e10d60e9e085052e9bbfe01ac3e494cc606afb652a1b476b10e434d13e9877b67d2cb0196a9bd190
This commit is contained in:
commit
efac285a0d
2
.github/ci-test-each-commit-exec.sh
vendored
2
.github/ci-test-each-commit-exec.sh
vendored
@ -12,7 +12,7 @@ set -o errexit -o pipefail -o xtrace
|
||||
echo "Running test-one-commit on $( git log -1 )"
|
||||
|
||||
# Use clang++, because it is a bit faster and uses less memory than g++
|
||||
CC=clang CXX=clang++ cmake -B build -DWERROR=ON -DWITH_ZMQ=ON -DBUILD_GUI=ON -DBUILD_BENCH=ON -DBUILD_FUZZ_BINARY=ON -DWITH_BDB=ON -DWITH_USDT=ON -DCMAKE_CXX_FLAGS='-Wno-error=unused-member-function'
|
||||
CC=clang CXX=clang++ cmake -B build -DWERROR=ON -DWITH_ZMQ=ON -DBUILD_GUI=ON -DBUILD_BENCH=ON -DBUILD_FUZZ_BINARY=ON -DWITH_USDT=ON -DCMAKE_CXX_FLAGS='-Wno-error=unused-member-function'
|
||||
|
||||
cmake --build build -j "$( nproc )" && ctest --output-on-failure --stop-on-failure --test-dir build -j "$( nproc )"
|
||||
|
||||
|
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@ -77,7 +77,7 @@ jobs:
|
||||
git config user.name "CI"
|
||||
- run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install clang ccache build-essential cmake ninja-build pkgconf python3-zmq libevent-dev libboost-dev libsqlite3-dev libdb++-dev systemtap-sdt-dev libzmq3-dev qt6-base-dev qt6-tools-dev qt6-l10n-tools libqrencode-dev -y
|
||||
sudo apt-get install clang ccache build-essential cmake ninja-build pkgconf python3-zmq libevent-dev libboost-dev libsqlite3-dev systemtap-sdt-dev libzmq3-dev qt6-base-dev qt6-tools-dev qt6-l10n-tools libqrencode-dev -y
|
||||
- name: Compile and run tests
|
||||
run: |
|
||||
# Run tests on commits after the last merge commit and before the PR head commit
|
||||
@ -175,7 +175,7 @@ jobs:
|
||||
job-type: [standard, fuzz]
|
||||
include:
|
||||
- job-type: standard
|
||||
generate-options: '-DBUILD_GUI=ON -DWITH_BDB=ON -DWITH_ZMQ=ON -DBUILD_BENCH=ON -DWERROR=ON'
|
||||
generate-options: '-DBUILD_GUI=ON -DWITH_ZMQ=ON -DBUILD_BENCH=ON -DWERROR=ON'
|
||||
job-name: 'Windows native, VS 2022'
|
||||
- job-type: fuzz
|
||||
generate-options: '-DVCPKG_MANIFEST_NO_DEFAULT_FEATURES=ON -DVCPKG_MANIFEST_FEATURES="wallet" -DBUILD_GUI=OFF -DBUILD_FOR_FUZZING=ON -DWERROR=ON'
|
||||
|
@ -110,22 +110,6 @@ if(ENABLE_WALLET)
|
||||
find_package(SQLite3 3.7.17 REQUIRED)
|
||||
endif()
|
||||
endif()
|
||||
option(WITH_BDB "Enable Berkeley DB (BDB) wallet support." OFF)
|
||||
cmake_dependent_option(WARN_INCOMPATIBLE_BDB "Warn when using a Berkeley DB (BDB) version other than 4.8." ON "WITH_BDB" OFF)
|
||||
if(WITH_BDB)
|
||||
find_package(BerkeleyDB 4.8 MODULE REQUIRED)
|
||||
set(USE_BDB ON)
|
||||
if(NOT BerkeleyDB_VERSION VERSION_EQUAL 4.8)
|
||||
message(WARNING "Found Berkeley DB (BDB) other than 4.8.\n"
|
||||
"BDB (legacy) wallets opened by this build will not be portable!"
|
||||
)
|
||||
if(WARN_INCOMPATIBLE_BDB)
|
||||
message(WARNING "If this is intended, pass \"-DWARN_INCOMPATIBLE_BDB=OFF\".\n"
|
||||
"Passing \"-DWITH_BDB=OFF\" will suppress this warning."
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
cmake_dependent_option(BUILD_WALLET_TOOL "Build bitcoin-wallet tool." ${BUILD_TESTS} "ENABLE_WALLET" OFF)
|
||||
|
||||
option(REDUCE_EXPORTS "Attempt to reduce exported symbols in the resulting executables." OFF)
|
||||
@ -677,9 +661,6 @@ message(" bitcoin-chainstate (experimental) ... ${BUILD_UTIL_CHAINSTATE}")
|
||||
message(" libbitcoinkernel (experimental) ..... ${BUILD_KERNEL_LIB}")
|
||||
message("Optional features:")
|
||||
message(" wallet support ...................... ${ENABLE_WALLET}")
|
||||
if(ENABLE_WALLET)
|
||||
message(" - legacy wallets (Berkeley DB) ..... ${WITH_BDB}")
|
||||
endif()
|
||||
message(" external signer ..................... ${ENABLE_EXTERNAL_SIGNER}")
|
||||
message(" ZeroMQ .............................. ${WITH_ZMQ}")
|
||||
if(ENABLE_IPC)
|
||||
|
@ -78,8 +78,6 @@
|
||||
"BUILD_WALLET_TOOL": "ON",
|
||||
"ENABLE_EXTERNAL_SIGNER": "ON",
|
||||
"ENABLE_WALLET": "ON",
|
||||
"WARN_INCOMPATIBLE_BDB": "OFF",
|
||||
"WITH_BDB": "ON",
|
||||
"ENABLE_IPC": "ON",
|
||||
"WITH_QRENCODE": "ON",
|
||||
"WITH_USDT": "ON",
|
||||
|
@ -20,11 +20,11 @@ fi
|
||||
|
||||
export CONTAINER_NAME=ci_native_asan
|
||||
export APT_LLVM_V="20"
|
||||
export PACKAGES="systemtap-sdt-dev clang-${APT_LLVM_V} llvm-${APT_LLVM_V} libclang-rt-${APT_LLVM_V}-dev python3-zmq qt6-base-dev qt6-tools-dev qt6-l10n-tools libevent-dev libboost-dev libdb5.3++-dev libzmq3-dev libqrencode-dev libsqlite3-dev ${BPFCC_PACKAGE}"
|
||||
export PACKAGES="systemtap-sdt-dev clang-${APT_LLVM_V} llvm-${APT_LLVM_V} libclang-rt-${APT_LLVM_V}-dev python3-zmq qt6-base-dev qt6-tools-dev qt6-l10n-tools libevent-dev libboost-dev libzmq3-dev libqrencode-dev libsqlite3-dev ${BPFCC_PACKAGE}"
|
||||
export NO_DEPENDS=1
|
||||
export GOAL="install"
|
||||
export BITCOIN_CONFIG="\
|
||||
-DWITH_USDT=ON -DWITH_ZMQ=ON -DWITH_BDB=ON -DWARN_INCOMPATIBLE_BDB=OFF -DBUILD_GUI=ON \
|
||||
-DWITH_USDT=ON -DWITH_ZMQ=ON -DBUILD_GUI=ON \
|
||||
-DSANITIZERS=address,float-divide-by-zero,integer,undefined \
|
||||
-DCMAKE_C_COMPILER=clang-${APT_LLVM_V} \
|
||||
-DCMAKE_CXX_COMPILER=clang++-${APT_LLVM_V} \
|
||||
|
@ -14,8 +14,7 @@ export MSAN_AND_LIBCXX_FLAGS="${MSAN_FLAGS} ${LIBCXX_FLAGS}"
|
||||
|
||||
export CONTAINER_NAME="ci_native_fuzz_msan"
|
||||
export PACKAGES="ninja-build"
|
||||
# BDB generates false-positives and will be removed in future
|
||||
export DEP_OPTS="DEBUG=1 NO_BDB=1 NO_QT=1 CC=clang CXX=clang++ CFLAGS='${MSAN_FLAGS}' CXXFLAGS='${MSAN_AND_LIBCXX_FLAGS}'"
|
||||
export DEP_OPTS="DEBUG=1 NO_QT=1 CC=clang CXX=clang++ CFLAGS='${MSAN_FLAGS}' CXXFLAGS='${MSAN_AND_LIBCXX_FLAGS}'"
|
||||
export GOAL="all"
|
||||
# Setting CMAKE_{C,CXX}_FLAGS_DEBUG flags to an empty string ensures that the flags set in MSAN_FLAGS remain unaltered.
|
||||
# _FORTIFY_SOURCE is not compatible with MSAN.
|
||||
|
@ -14,8 +14,7 @@ export MSAN_AND_LIBCXX_FLAGS="${MSAN_FLAGS} ${LIBCXX_FLAGS}"
|
||||
|
||||
export CONTAINER_NAME="ci_native_msan"
|
||||
export PACKAGES="ninja-build"
|
||||
# BDB generates false-positives and will be removed in future
|
||||
export DEP_OPTS="DEBUG=1 NO_BDB=1 NO_QT=1 CC=clang CXX=clang++ CFLAGS='${MSAN_FLAGS}' CXXFLAGS='${MSAN_AND_LIBCXX_FLAGS}'"
|
||||
export DEP_OPTS="DEBUG=1 NO_QT=1 CC=clang CXX=clang++ CFLAGS='${MSAN_FLAGS}' CXXFLAGS='${MSAN_AND_LIBCXX_FLAGS}'"
|
||||
export GOAL="install"
|
||||
# Setting CMAKE_{C,CXX}_FLAGS_DEBUG flags to an empty string ensures that the flags set in MSAN_FLAGS remain unaltered.
|
||||
# _FORTIFY_SOURCE is not compatible with MSAN.
|
||||
|
@ -10,7 +10,7 @@ export CI_IMAGE_NAME_TAG="mirror.gcr.io/ubuntu:24.04"
|
||||
export CONTAINER_NAME=ci_native_tidy
|
||||
export TIDY_LLVM_V="20"
|
||||
export APT_LLVM_V="${TIDY_LLVM_V}"
|
||||
export PACKAGES="clang-${TIDY_LLVM_V} libclang-${TIDY_LLVM_V}-dev llvm-${TIDY_LLVM_V}-dev libomp-${TIDY_LLVM_V}-dev clang-tidy-${TIDY_LLVM_V} jq libevent-dev libboost-dev libzmq3-dev systemtap-sdt-dev qt6-base-dev qt6-tools-dev qt6-l10n-tools libqrencode-dev libsqlite3-dev libdb++-dev"
|
||||
export PACKAGES="clang-${TIDY_LLVM_V} libclang-${TIDY_LLVM_V}-dev llvm-${TIDY_LLVM_V}-dev libomp-${TIDY_LLVM_V}-dev clang-tidy-${TIDY_LLVM_V} jq libevent-dev libboost-dev libzmq3-dev systemtap-sdt-dev qt6-base-dev qt6-tools-dev qt6-l10n-tools libqrencode-dev libsqlite3-dev"
|
||||
export NO_DEPENDS=1
|
||||
export RUN_UNIT_TESTS=false
|
||||
export RUN_FUNCTIONAL_TESTS=false
|
||||
@ -19,7 +19,7 @@ export RUN_CHECK_DEPS=true
|
||||
export RUN_TIDY=true
|
||||
export GOAL="install"
|
||||
export BITCOIN_CONFIG="\
|
||||
-DWITH_ZMQ=ON -DBUILD_GUI=ON -DBUILD_BENCH=ON -DWITH_USDT=ON -DWITH_BDB=ON -DWARN_INCOMPATIBLE_BDB=OFF \
|
||||
-DWITH_ZMQ=ON -DBUILD_GUI=ON -DBUILD_BENCH=ON -DWITH_USDT=ON \
|
||||
-DCMAKE_C_COMPILER=clang-${TIDY_LLVM_V} \
|
||||
-DCMAKE_CXX_COMPILER=clang++-${TIDY_LLVM_V} \
|
||||
-DCMAKE_C_FLAGS_RELWITHDEBINFO='-O0 -g0' \
|
||||
|
@ -8,12 +8,12 @@ export LC_ALL=C.UTF-8
|
||||
|
||||
export CI_IMAGE_NAME_TAG="mirror.gcr.io/ubuntu:24.04"
|
||||
export CONTAINER_NAME=ci_native_valgrind
|
||||
export PACKAGES="valgrind python3-zmq libevent-dev libboost-dev libdb5.3++-dev libzmq3-dev libsqlite3-dev"
|
||||
export PACKAGES="valgrind python3-zmq libevent-dev libboost-dev libzmq3-dev libsqlite3-dev"
|
||||
export USE_VALGRIND=1
|
||||
export NO_DEPENDS=1
|
||||
export TEST_RUNNER_EXTRA="--exclude feature_init,rpc_bind,feature_bind_extra" # feature_init excluded for now, see https://github.com/bitcoin/bitcoin/issues/30011 ; bind tests excluded for now, see https://github.com/bitcoin/bitcoin/issues/17765#issuecomment-602068547
|
||||
export GOAL="install"
|
||||
# TODO enable GUI
|
||||
export BITCOIN_CONFIG="\
|
||||
-DWITH_ZMQ=ON -DWITH_BDB=ON -DWARN_INCOMPATIBLE_BDB=OFF -DBUILD_GUI=OFF \
|
||||
-DWITH_ZMQ=ON -DBUILD_GUI=OFF \
|
||||
"
|
||||
|
@ -123,9 +123,6 @@
|
||||
/* Define to 1 if strerror_r returns char *. */
|
||||
#cmakedefine STRERROR_R_CHAR_P 1
|
||||
|
||||
/* Define if BDB support should be compiled in */
|
||||
#cmakedefine USE_BDB 1
|
||||
|
||||
/* Define if dbus support should be compiled in */
|
||||
#cmakedefine USE_DBUS 1
|
||||
|
||||
|
@ -1,133 +0,0 @@
|
||||
# Copyright (c) 2023-present The Bitcoin Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or https://opensource.org/license/mit/.
|
||||
|
||||
#[=======================================================================[
|
||||
FindBerkeleyDB
|
||||
--------------
|
||||
|
||||
Finds the Berkeley DB headers and library.
|
||||
|
||||
Imported Targets
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
This module provides imported target ``BerkeleyDB::BerkeleyDB``, if
|
||||
Berkeley DB has been found.
|
||||
|
||||
Result Variables
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
This module defines the following variables:
|
||||
|
||||
``BerkeleyDB_FOUND``
|
||||
"True" if Berkeley DB found.
|
||||
|
||||
``BerkeleyDB_VERSION``
|
||||
The MAJOR.MINOR version of Berkeley DB found.
|
||||
|
||||
#]=======================================================================]
|
||||
|
||||
set(_BerkeleyDB_homebrew_prefix)
|
||||
if(CMAKE_HOST_APPLE)
|
||||
find_program(HOMEBREW_EXECUTABLE brew)
|
||||
if(HOMEBREW_EXECUTABLE)
|
||||
# The Homebrew package manager installs the berkeley-db* packages as
|
||||
# "keg-only", which means they are not symlinked into the default prefix.
|
||||
# To find such a package, the find_path() and find_library() commands
|
||||
# need additional path hints that are computed by Homebrew itself.
|
||||
execute_process(
|
||||
COMMAND ${HOMEBREW_EXECUTABLE} --prefix berkeley-db@4
|
||||
OUTPUT_VARIABLE _BerkeleyDB_homebrew_prefix
|
||||
ERROR_QUIET
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_path(BerkeleyDB_INCLUDE_DIR
|
||||
NAMES db_cxx.h
|
||||
HINTS ${_BerkeleyDB_homebrew_prefix}/include
|
||||
PATH_SUFFIXES 4.8 48 db4.8 4 db4 5.3 db5.3 5 db5
|
||||
)
|
||||
mark_as_advanced(BerkeleyDB_INCLUDE_DIR)
|
||||
unset(_BerkeleyDB_homebrew_prefix)
|
||||
|
||||
if(NOT BerkeleyDB_LIBRARY)
|
||||
if(VCPKG_TARGET_TRIPLET)
|
||||
# The vcpkg package manager installs the berkeleydb package with the same name
|
||||
# of release and debug libraries. Therefore, the default search paths set by
|
||||
# vcpkg's toolchain file cannot be used to search libraries as the debug one
|
||||
# will always be found.
|
||||
set(CMAKE_FIND_USE_CMAKE_PATH FALSE)
|
||||
endif()
|
||||
|
||||
get_filename_component(_BerkeleyDB_lib_hint "${BerkeleyDB_INCLUDE_DIR}" DIRECTORY)
|
||||
|
||||
find_library(BerkeleyDB_LIBRARY_RELEASE
|
||||
NAMES db_cxx-4.8 db4_cxx db48 db_cxx-5.3 db_cxx-5 db_cxx libdb48
|
||||
NAMES_PER_DIR
|
||||
HINTS ${_BerkeleyDB_lib_hint}
|
||||
PATH_SUFFIXES lib
|
||||
)
|
||||
mark_as_advanced(BerkeleyDB_LIBRARY_RELEASE)
|
||||
|
||||
find_library(BerkeleyDB_LIBRARY_DEBUG
|
||||
NAMES db_cxx-4.8 db4_cxx db48 db_cxx-5.3 db_cxx-5 db_cxx libdb48
|
||||
NAMES_PER_DIR
|
||||
HINTS ${_BerkeleyDB_lib_hint}
|
||||
PATH_SUFFIXES debug/lib
|
||||
)
|
||||
mark_as_advanced(BerkeleyDB_LIBRARY_DEBUG)
|
||||
|
||||
unset(_BerkeleyDB_lib_hint)
|
||||
unset(CMAKE_FIND_USE_CMAKE_PATH)
|
||||
|
||||
include(SelectLibraryConfigurations)
|
||||
select_library_configurations(BerkeleyDB)
|
||||
# The select_library_configurations() command sets BerkeleyDB_FOUND, but we
|
||||
# want the one from the find_package_handle_standard_args() command below.
|
||||
unset(BerkeleyDB_FOUND)
|
||||
endif()
|
||||
|
||||
if(BerkeleyDB_INCLUDE_DIR)
|
||||
file(STRINGS "${BerkeleyDB_INCLUDE_DIR}/db.h" _BerkeleyDB_version_strings REGEX "^#define[\t ]+DB_VERSION_(MAJOR|MINOR|PATCH)[ \t]+[0-9]+.*")
|
||||
string(REGEX REPLACE ".*#define[\t ]+DB_VERSION_MAJOR[ \t]+([0-9]+).*" "\\1" _BerkeleyDB_version_major "${_BerkeleyDB_version_strings}")
|
||||
string(REGEX REPLACE ".*#define[\t ]+DB_VERSION_MINOR[ \t]+([0-9]+).*" "\\1" _BerkeleyDB_version_minor "${_BerkeleyDB_version_strings}")
|
||||
string(REGEX REPLACE ".*#define[\t ]+DB_VERSION_PATCH[ \t]+([0-9]+).*" "\\1" _BerkeleyDB_version_patch "${_BerkeleyDB_version_strings}")
|
||||
unset(_BerkeleyDB_version_strings)
|
||||
# The MAJOR.MINOR.PATCH version will be logged in the following find_package_handle_standard_args() command.
|
||||
set(_BerkeleyDB_full_version ${_BerkeleyDB_version_major}.${_BerkeleyDB_version_minor}.${_BerkeleyDB_version_patch})
|
||||
set(BerkeleyDB_VERSION ${_BerkeleyDB_version_major}.${_BerkeleyDB_version_minor})
|
||||
unset(_BerkeleyDB_version_major)
|
||||
unset(_BerkeleyDB_version_minor)
|
||||
unset(_BerkeleyDB_version_patch)
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(BerkeleyDB
|
||||
REQUIRED_VARS BerkeleyDB_LIBRARY BerkeleyDB_INCLUDE_DIR
|
||||
VERSION_VAR _BerkeleyDB_full_version
|
||||
)
|
||||
unset(_BerkeleyDB_full_version)
|
||||
|
||||
if(BerkeleyDB_FOUND AND NOT TARGET BerkeleyDB::BerkeleyDB)
|
||||
add_library(BerkeleyDB::BerkeleyDB UNKNOWN IMPORTED)
|
||||
set_target_properties(BerkeleyDB::BerkeleyDB PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${BerkeleyDB_INCLUDE_DIR}"
|
||||
)
|
||||
if(BerkeleyDB_LIBRARY_RELEASE)
|
||||
set_property(TARGET BerkeleyDB::BerkeleyDB APPEND PROPERTY
|
||||
IMPORTED_CONFIGURATIONS RELEASE
|
||||
)
|
||||
set_target_properties(BerkeleyDB::BerkeleyDB PROPERTIES
|
||||
IMPORTED_LOCATION_RELEASE "${BerkeleyDB_LIBRARY_RELEASE}"
|
||||
)
|
||||
endif()
|
||||
if(BerkeleyDB_LIBRARY_DEBUG)
|
||||
set_property(TARGET BerkeleyDB::BerkeleyDB APPEND PROPERTY
|
||||
IMPORTED_CONFIGURATIONS DEBUG)
|
||||
set_target_properties(BerkeleyDB::BerkeleyDB PROPERTIES
|
||||
IMPORTED_LOCATION_DEBUG "${BerkeleyDB_LIBRARY_DEBUG}"
|
||||
)
|
||||
endif()
|
||||
endif()
|
@ -39,7 +39,7 @@ _bitcoin_cli() {
|
||||
|
||||
if ((cword > 4)); then
|
||||
case ${words[cword-4]} in
|
||||
importaddress|listtransactions|setban)
|
||||
listtransactions|setban)
|
||||
COMPREPLY=( $( compgen -W "true false" -- "$cur" ) )
|
||||
return 0
|
||||
;;
|
||||
@ -52,10 +52,7 @@ _bitcoin_cli() {
|
||||
|
||||
if ((cword > 3)); then
|
||||
case ${words[cword-3]} in
|
||||
addmultisigaddress)
|
||||
return 0
|
||||
;;
|
||||
getbalance|gettxout|importaddress|importpubkey|importprivkey|listreceivedbyaddress|listsinceblock)
|
||||
getbalance|gettxout|listreceivedbyaddress|listsinceblock)
|
||||
COMPREPLY=( $( compgen -W "true false" -- "$cur" ) )
|
||||
return 0
|
||||
;;
|
||||
@ -80,7 +77,7 @@ _bitcoin_cli() {
|
||||
fi
|
||||
|
||||
case "$prev" in
|
||||
backupwallet|dumpwallet|importwallet)
|
||||
backupwallet)
|
||||
_filedir
|
||||
return 0
|
||||
;;
|
||||
|
@ -41,9 +41,6 @@ ALLOWED_DEPENDENCIES+=(
|
||||
|
||||
# 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["init.cpp.o bdb.cpp.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
|
||||
|
@ -14,44 +14,12 @@
|
||||
# Note that suppressions may depend on OS and/or library versions.
|
||||
# Tested on aarch64 and x86_64 with Ubuntu Noble system libs, using clang-16
|
||||
# and GCC, without gui.
|
||||
{
|
||||
Suppress libdb warning - https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=662917
|
||||
Memcheck:Cond
|
||||
obj:*/libdb_cxx-*.so
|
||||
fun:__log_put
|
||||
}
|
||||
{
|
||||
Suppress libdb warning
|
||||
Memcheck:Param
|
||||
pwrite64(buf)
|
||||
fun:pwrite
|
||||
fun:__os_io
|
||||
}
|
||||
{
|
||||
Suppress libdb warning
|
||||
Memcheck:Cond
|
||||
fun:__log_putr.isra.1
|
||||
}
|
||||
{
|
||||
Suppress libdb warning
|
||||
Memcheck:Param
|
||||
pwrite64(buf)
|
||||
...
|
||||
obj:*/libdb_cxx-*.so
|
||||
}
|
||||
{
|
||||
Suppress uninitialized bytes warning in compat code
|
||||
Memcheck:Param
|
||||
ioctl(TCSET{S,SW,SF})
|
||||
fun:tcsetattr
|
||||
}
|
||||
{
|
||||
Suppress libdb warning
|
||||
Memcheck:Leak
|
||||
fun:malloc
|
||||
...
|
||||
obj:*/libdb_cxx-*.so
|
||||
}
|
||||
{
|
||||
Suppress leaks on shutdown
|
||||
Memcheck:Leak
|
||||
|
@ -36,7 +36,6 @@ NO_BOOST ?=
|
||||
NO_LIBEVENT ?=
|
||||
NO_QT ?=
|
||||
NO_QR ?=
|
||||
NO_BDB ?=
|
||||
NO_WALLET ?=
|
||||
NO_ZMQ ?=
|
||||
NO_USDT ?=
|
||||
@ -159,8 +158,7 @@ qrencode_packages_$(NO_QR) = $(qrencode_$(host_os)_packages)
|
||||
qt_packages_$(NO_QT) = $(qt_packages) $(qt_$(host_os)_packages) $(qt_$(host_arch)_$(host_os)_packages) $(qrencode_packages_)
|
||||
qt_native_packages_$(NO_QT) = $(qt_native_packages)
|
||||
|
||||
bdb_packages_$(NO_BDB) = $(bdb_packages)
|
||||
wallet_packages_$(NO_WALLET) = $(bdb_packages_) $(sqlite_packages)
|
||||
wallet_packages_$(NO_WALLET) = $(sqlite_packages)
|
||||
|
||||
zmq_packages_$(NO_ZMQ) = $(zmq_packages)
|
||||
multiprocess_packages_$(MULTIPROCESS) = $(multiprocess_packages)
|
||||
@ -232,7 +230,6 @@ $(host_prefix)/toolchain.cmake : toolchain.cmake.in $(host_prefix)/.stamp_$(fina
|
||||
-e 's|@qrencode_packages@|$(qrencode_packages_)|' \
|
||||
-e 's|@zmq_packages@|$(zmq_packages_)|' \
|
||||
-e 's|@wallet_packages@|$(wallet_packages_)|' \
|
||||
-e 's|@bdb_packages@|$(bdb_packages_)|' \
|
||||
-e 's|@usdt_packages@|$(usdt_packages_)|' \
|
||||
-e 's|@multiprocess@|$(MULTIPROCESS)|' \
|
||||
$< > $@
|
||||
|
@ -79,7 +79,6 @@ The following can be set when running make: `make FOO=bar`
|
||||
- `NO_QR`: Don't download/build/cache packages needed for enabling qrencode
|
||||
- `NO_ZMQ`: Don't download/build/cache packages needed for enabling ZeroMQ
|
||||
- `NO_WALLET`: Don't download/build/cache libs needed to enable the wallet (SQLite)
|
||||
- `NO_BDB`: Don't download/build/cache BerkeleyDB
|
||||
- `NO_USDT`: Don't download/build/cache packages needed for enabling USDT tracepoints
|
||||
- `MULTIPROCESS`: Build libmultiprocess (experimental)
|
||||
- `DEBUG`: Disable some optimizations and enable more runtime checking
|
||||
|
@ -1,33 +0,0 @@
|
||||
package=bdb
|
||||
$(package)_version=4.8.30
|
||||
$(package)_download_path=https://download.oracle.com/berkeley-db
|
||||
$(package)_file_name=db-$($(package)_version).NC.tar.gz
|
||||
$(package)_sha256_hash=12edc0df75bf9abd7f82f821795bcee50f42cb2e5f76a6a281b85732798364ef
|
||||
$(package)_build_subdir=build_unix
|
||||
$(package)_patches=clang_cxx_11.patch
|
||||
|
||||
define $(package)_set_vars
|
||||
$(package)_config_opts=--disable-shared --enable-cxx --disable-replication --enable-option-checking
|
||||
$(package)_config_opts_mingw32=--enable-mingw
|
||||
$(package)_cflags+=-Wno-error=implicit-function-declaration -Wno-error=format-security -Wno-error=implicit-int
|
||||
$(package)_cppflags_freebsd=-D_XOPEN_SOURCE=600 -D__BSD_VISIBLE=1
|
||||
$(package)_cppflags_netbsd=-D_XOPEN_SOURCE=600
|
||||
$(package)_cppflags_mingw32=-DUNICODE -D_UNICODE
|
||||
endef
|
||||
|
||||
define $(package)_preprocess_cmds
|
||||
patch -p1 < $($(package)_patch_dir)/clang_cxx_11.patch && \
|
||||
cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub dist
|
||||
endef
|
||||
|
||||
define $(package)_config_cmds
|
||||
../dist/$($(package)_autoconf)
|
||||
endef
|
||||
|
||||
define $(package)_build_cmds
|
||||
$(MAKE) libdb_cxx-4.8.a libdb-4.8.a
|
||||
endef
|
||||
|
||||
define $(package)_stage_cmds
|
||||
$(MAKE) DESTDIR=$($(package)_staging_dir) install_lib install_include
|
||||
endef
|
@ -15,7 +15,6 @@ ifneq ($(host),$(build))
|
||||
qt_native_packages := native_qt
|
||||
endif
|
||||
|
||||
bdb_packages=bdb
|
||||
sqlite_packages=sqlite
|
||||
|
||||
zmq_packages=zeromq
|
||||
|
@ -1,147 +0,0 @@
|
||||
commit 3311d68f11d1697565401eee6efc85c34f022ea7
|
||||
Author: fanquake <fanquake@gmail.com>
|
||||
Date: Mon Aug 17 20:03:56 2020 +0800
|
||||
|
||||
Fix C++11 compatibility
|
||||
|
||||
diff --git a/dbinc/atomic.h b/dbinc/atomic.h
|
||||
index 0034dcc..7c11d4a 100644
|
||||
--- a/dbinc/atomic.h
|
||||
+++ b/dbinc/atomic.h
|
||||
@@ -70,7 +70,7 @@ typedef struct {
|
||||
* These have no memory barriers; the caller must include them when necessary.
|
||||
*/
|
||||
#define atomic_read(p) ((p)->value)
|
||||
-#define atomic_init(p, val) ((p)->value = (val))
|
||||
+#define atomic_init_db(p, val) ((p)->value = (val))
|
||||
|
||||
#ifdef HAVE_ATOMIC_SUPPORT
|
||||
|
||||
@@ -144,7 +144,7 @@ typedef LONG volatile *interlocked_val;
|
||||
#define atomic_inc(env, p) __atomic_inc(p)
|
||||
#define atomic_dec(env, p) __atomic_dec(p)
|
||||
#define atomic_compare_exchange(env, p, o, n) \
|
||||
- __atomic_compare_exchange((p), (o), (n))
|
||||
+ __atomic_compare_exchange_db((p), (o), (n))
|
||||
static inline int __atomic_inc(db_atomic_t *p)
|
||||
{
|
||||
int temp;
|
||||
@@ -176,7 +176,7 @@ static inline int __atomic_dec(db_atomic_t *p)
|
||||
* http://gcc.gnu.org/onlinedocs/gcc-4.1.0/gcc/Atomic-Builtins.html
|
||||
* which configure could be changed to use.
|
||||
*/
|
||||
-static inline int __atomic_compare_exchange(
|
||||
+static inline int __atomic_compare_exchange_db(
|
||||
db_atomic_t *p, atomic_value_t oldval, atomic_value_t newval)
|
||||
{
|
||||
atomic_value_t was;
|
||||
@@ -206,7 +206,7 @@ static inline int __atomic_compare_exchange(
|
||||
#define atomic_dec(env, p) (--(p)->value)
|
||||
#define atomic_compare_exchange(env, p, oldval, newval) \
|
||||
(DB_ASSERT(env, atomic_read(p) == (oldval)), \
|
||||
- atomic_init(p, (newval)), 1)
|
||||
+ atomic_init_db(p, (newval)), 1)
|
||||
#else
|
||||
#define atomic_inc(env, p) __atomic_inc(env, p)
|
||||
#define atomic_dec(env, p) __atomic_dec(env, p)
|
||||
diff --git a/mp/mp_fget.c b/mp/mp_fget.c
|
||||
index 5fdee5a..0b75f57 100644
|
||||
--- a/mp/mp_fget.c
|
||||
+++ b/mp/mp_fget.c
|
||||
@@ -617,7 +617,7 @@ alloc: /* Allocate a new buffer header and data space. */
|
||||
|
||||
/* Initialize enough so we can call __memp_bhfree. */
|
||||
alloc_bhp->flags = 0;
|
||||
- atomic_init(&alloc_bhp->ref, 1);
|
||||
+ atomic_init_db(&alloc_bhp->ref, 1);
|
||||
#ifdef DIAGNOSTIC
|
||||
if ((uintptr_t)alloc_bhp->buf & (sizeof(size_t) - 1)) {
|
||||
__db_errx(env,
|
||||
@@ -911,7 +911,7 @@ alloc: /* Allocate a new buffer header and data space. */
|
||||
MVCC_MPROTECT(bhp->buf, mfp->stat.st_pagesize,
|
||||
PROT_READ);
|
||||
|
||||
- atomic_init(&alloc_bhp->ref, 1);
|
||||
+ atomic_init_db(&alloc_bhp->ref, 1);
|
||||
MUTEX_LOCK(env, alloc_bhp->mtx_buf);
|
||||
alloc_bhp->priority = bhp->priority;
|
||||
alloc_bhp->pgno = bhp->pgno;
|
||||
diff --git a/mp/mp_mvcc.c b/mp/mp_mvcc.c
|
||||
index 34467d2..f05aa0c 100644
|
||||
--- a/mp/mp_mvcc.c
|
||||
+++ b/mp/mp_mvcc.c
|
||||
@@ -276,7 +276,7 @@ __memp_bh_freeze(dbmp, infop, hp, bhp, need_frozenp)
|
||||
#else
|
||||
memcpy(frozen_bhp, bhp, SSZA(BH, buf));
|
||||
#endif
|
||||
- atomic_init(&frozen_bhp->ref, 0);
|
||||
+ atomic_init_db(&frozen_bhp->ref, 0);
|
||||
if (mutex != MUTEX_INVALID)
|
||||
frozen_bhp->mtx_buf = mutex;
|
||||
else if ((ret = __mutex_alloc(env, MTX_MPOOL_BH,
|
||||
@@ -428,7 +428,7 @@ __memp_bh_thaw(dbmp, infop, hp, frozen_bhp, alloc_bhp)
|
||||
#endif
|
||||
alloc_bhp->mtx_buf = mutex;
|
||||
MUTEX_LOCK(env, alloc_bhp->mtx_buf);
|
||||
- atomic_init(&alloc_bhp->ref, 1);
|
||||
+ atomic_init_db(&alloc_bhp->ref, 1);
|
||||
F_CLR(alloc_bhp, BH_FROZEN);
|
||||
}
|
||||
|
||||
diff --git a/mp/mp_region.c b/mp/mp_region.c
|
||||
index e6cece9..ddbe906 100644
|
||||
--- a/mp/mp_region.c
|
||||
+++ b/mp/mp_region.c
|
||||
@@ -224,7 +224,7 @@ __memp_init(env, dbmp, reginfo_off, htab_buckets, max_nreg)
|
||||
MTX_MPOOL_FILE_BUCKET, 0, &htab[i].mtx_hash)) != 0)
|
||||
return (ret);
|
||||
SH_TAILQ_INIT(&htab[i].hash_bucket);
|
||||
- atomic_init(&htab[i].hash_page_dirty, 0);
|
||||
+ atomic_init_db(&htab[i].hash_page_dirty, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -269,7 +269,7 @@ __memp_init(env, dbmp, reginfo_off, htab_buckets, max_nreg)
|
||||
hp->mtx_hash = (mtx_base == MUTEX_INVALID) ? MUTEX_INVALID :
|
||||
mtx_base + i;
|
||||
SH_TAILQ_INIT(&hp->hash_bucket);
|
||||
- atomic_init(&hp->hash_page_dirty, 0);
|
||||
+ atomic_init_db(&hp->hash_page_dirty, 0);
|
||||
#ifdef HAVE_STATISTICS
|
||||
hp->hash_io_wait = 0;
|
||||
hp->hash_frozen = hp->hash_thawed = hp->hash_frozen_freed = 0;
|
||||
diff --git a/mutex/mut_method.c b/mutex/mut_method.c
|
||||
index 2588763..5c6d516 100644
|
||||
--- a/mutex/mut_method.c
|
||||
+++ b/mutex/mut_method.c
|
||||
@@ -426,7 +426,7 @@ atomic_compare_exchange(env, v, oldval, newval)
|
||||
MUTEX_LOCK(env, mtx);
|
||||
ret = atomic_read(v) == oldval;
|
||||
if (ret)
|
||||
- atomic_init(v, newval);
|
||||
+ atomic_init_db(v, newval);
|
||||
MUTEX_UNLOCK(env, mtx);
|
||||
|
||||
return (ret);
|
||||
diff --git a/mutex/mut_tas.c b/mutex/mut_tas.c
|
||||
index f3922e0..e40fcdf 100644
|
||||
--- a/mutex/mut_tas.c
|
||||
+++ b/mutex/mut_tas.c
|
||||
@@ -46,7 +46,7 @@ __db_tas_mutex_init(env, mutex, flags)
|
||||
|
||||
#ifdef HAVE_SHARED_LATCHES
|
||||
if (F_ISSET(mutexp, DB_MUTEX_SHARED))
|
||||
- atomic_init(&mutexp->sharecount, 0);
|
||||
+ atomic_init_db(&mutexp->sharecount, 0);
|
||||
else
|
||||
#endif
|
||||
if (MUTEX_INIT(&mutexp->tas)) {
|
||||
@@ -486,7 +486,7 @@ __db_tas_mutex_unlock(env, mutex)
|
||||
F_CLR(mutexp, DB_MUTEX_LOCKED);
|
||||
/* Flush flag update before zeroing count */
|
||||
MEMBAR_EXIT();
|
||||
- atomic_init(&mutexp->sharecount, 0);
|
||||
+ atomic_init_db(&mutexp->sharecount, 0);
|
||||
} else {
|
||||
DB_ASSERT(env, sharecount > 0);
|
||||
MEMBAR_EXIT();
|
@ -144,13 +144,6 @@ else()
|
||||
set(ENABLE_WALLET ON CACHE BOOL "")
|
||||
endif()
|
||||
|
||||
set(bdb_packages @bdb_packages@)
|
||||
if("${wallet_packages}" STREQUAL "" OR "${bdb_packages}" STREQUAL "")
|
||||
set(WITH_BDB OFF CACHE BOOL "")
|
||||
else()
|
||||
set(WITH_BDB ON CACHE BOOL "")
|
||||
endif()
|
||||
|
||||
set(usdt_packages @usdt_packages@)
|
||||
if("${usdt_packages}" STREQUAL "")
|
||||
set(WITH_USDT OFF CACHE BOOL "")
|
||||
|
@ -34,33 +34,6 @@ Skip if you don't intend to use descriptor wallets.
|
||||
pkg install sqlite3
|
||||
```
|
||||
|
||||
###### Legacy Wallet Support
|
||||
BerkeleyDB is only required if legacy wallet support is required.
|
||||
|
||||
It is required to use Berkeley DB 4.8. You **cannot** use the BerkeleyDB library
|
||||
from ports. However, you can build DB 4.8 yourself [using depends](/depends).
|
||||
|
||||
```bash
|
||||
pkg install gmake
|
||||
gmake -C depends NO_BOOST=1 NO_LIBEVENT=1 NO_QT=1 NO_ZMQ=1 NO_USDT=1
|
||||
```
|
||||
|
||||
When the build is complete, the Berkeley DB installation location will be displayed:
|
||||
|
||||
```
|
||||
to: /path/to/bitcoin/depends/x86_64-unknown-freebsd[release-number]
|
||||
```
|
||||
|
||||
Finally, set `BDB_PREFIX` to this path according to your shell:
|
||||
|
||||
```
|
||||
csh: setenv BDB_PREFIX [path displayed above]
|
||||
```
|
||||
|
||||
```
|
||||
sh/bash: export BDB_PREFIX=[path displayed above]
|
||||
```
|
||||
|
||||
#### GUI Dependencies
|
||||
###### Qt6
|
||||
|
||||
@ -107,20 +80,13 @@ pkg install python3 databases/py-sqlite3 net/py-pyzmq
|
||||
There are many ways to configure Bitcoin Core, here are a few common examples:
|
||||
|
||||
##### Descriptor Wallet and GUI:
|
||||
This disables legacy wallet support and enables the GUI, assuming `sqlite` and `qt` are installed.
|
||||
This enables the GUI, assuming `sqlite` and `qt` are installed.
|
||||
```bash
|
||||
cmake -B build -DWITH_BDB=OFF -DBUILD_GUI=ON
|
||||
cmake -B build -DBUILD_GUI=ON
|
||||
```
|
||||
|
||||
Run `cmake -B build -LH` to see the full list of available options.
|
||||
|
||||
##### Descriptor & Legacy Wallet. No GUI:
|
||||
This enables support for both wallet types, assuming
|
||||
`sqlite3` and `db4` are both installed.
|
||||
```bash
|
||||
cmake -B build -DBerkeleyDB_INCLUDE_DIR:PATH="${BDB_PREFIX}/include" -DWITH_BDB=ON
|
||||
```
|
||||
|
||||
##### No Wallet or GUI
|
||||
```bash
|
||||
cmake -B build -DENABLE_WALLET=OFF
|
||||
|
@ -55,14 +55,6 @@ It is not necessary to build wallet functionality to run bitcoind or the GUI.
|
||||
pkgin install sqlite3
|
||||
```
|
||||
|
||||
###### Legacy Wallet Support
|
||||
|
||||
`db4` is required to enable support for legacy wallets.
|
||||
|
||||
```bash
|
||||
pkgin install db4
|
||||
```
|
||||
|
||||
#### GUI Dependencies
|
||||
###### Qt6
|
||||
|
||||
|
@ -26,35 +26,13 @@ git clone https://github.com/bitcoin/bitcoin.git
|
||||
#### Wallet Dependencies
|
||||
|
||||
It is not necessary to build wallet functionality to run either `bitcoind` or `bitcoin-qt`.
|
||||
SQLite is required to build the wallet.
|
||||
|
||||
###### Descriptor Wallet Support
|
||||
|
||||
SQLite is required to support [descriptor wallets](descriptors.md).
|
||||
|
||||
``` bash
|
||||
pkg_add sqlite3
|
||||
```
|
||||
|
||||
###### Legacy Wallet Support
|
||||
BerkeleyDB is only required to support legacy wallets.
|
||||
|
||||
It is recommended to use Berkeley DB 4.8. You cannot use the BerkeleyDB library
|
||||
from ports. However you can build it yourself, [using depends](/depends).
|
||||
|
||||
Refer to [depends/README.md](/depends/README.md) for detailed instructions.
|
||||
|
||||
```bash
|
||||
gmake -C depends NO_BOOST=1 NO_LIBEVENT=1 NO_QT=1 NO_ZMQ=1 NO_USDT=1
|
||||
...
|
||||
to: /path/to/bitcoin/depends/*-unknown-openbsd*
|
||||
```
|
||||
|
||||
Then set `BDB_PREFIX`:
|
||||
|
||||
```bash
|
||||
export BDB_PREFIX="[path displayed above]"
|
||||
```
|
||||
|
||||
#### GUI Dependencies
|
||||
###### Qt6
|
||||
|
||||
@ -108,13 +86,6 @@ cmake -B build -DBUILD_GUI=ON
|
||||
|
||||
Run `cmake -B build -LH` to see the full list of available options.
|
||||
|
||||
##### Descriptor & Legacy Wallet. No GUI:
|
||||
This enables support for both wallet types:
|
||||
|
||||
```bash
|
||||
cmake -B build -DBerkeleyDB_INCLUDE_DIR:PATH="${BDB_PREFIX}/include" -DWITH_BDB=ON
|
||||
```
|
||||
|
||||
### 2. Compile
|
||||
|
||||
```bash
|
||||
|
@ -74,14 +74,6 @@ It is not necessary to build wallet functionality to run `bitcoind` or `bitcoin
|
||||
macOS ships with a useable `sqlite` package, meaning you don't need to
|
||||
install anything.
|
||||
|
||||
###### Legacy Wallet Support
|
||||
|
||||
`berkeley-db@4` is only required to support for legacy wallets.
|
||||
Skip if you don't intend to use legacy wallets.
|
||||
|
||||
``` bash
|
||||
brew install berkeley-db@4
|
||||
```
|
||||
---
|
||||
|
||||
#### GUI Dependencies
|
||||
@ -160,14 +152,6 @@ It is required that you have `python` and `zip` installed.
|
||||
|
||||
There are many ways to configure Bitcoin Core, here are a few common examples:
|
||||
|
||||
##### Wallet (BDB + SQlite) Support, No GUI:
|
||||
|
||||
If `berkeley-db@4` or `sqlite` are not installed, this will throw an error.
|
||||
|
||||
``` bash
|
||||
cmake -B build -DWITH_BDB=ON
|
||||
```
|
||||
|
||||
##### Wallet (only SQlite) and GUI Support:
|
||||
|
||||
This enables the GUI.
|
||||
|
@ -54,10 +54,6 @@ SQLite is required for the descriptor wallet:
|
||||
|
||||
sudo apt install libsqlite3-dev
|
||||
|
||||
Berkeley DB is only required for the legacy wallet. Ubuntu and Debian have their own `libdb-dev` and `libdb++-dev` packages,
|
||||
but these will install Berkeley DB 5.3 or later. This will break binary wallet compatibility with the distributed
|
||||
executables, which are based on BerkeleyDB 4.8. Otherwise, you can build Berkeley DB [yourself](#berkeley-db).
|
||||
|
||||
To build Bitcoin Core without wallet, see [*Disable-wallet mode*](#disable-wallet-mode)
|
||||
|
||||
ZMQ dependencies (provides ZMQ API):
|
||||
@ -109,10 +105,6 @@ SQLite is required for the descriptor wallet:
|
||||
|
||||
sudo dnf install sqlite-devel
|
||||
|
||||
Berkeley DB is only required for the legacy wallet. Fedora releases have only `libdb-devel` and `libdb-cxx-devel` packages, but these will install
|
||||
Berkeley DB 5.3 or later. This will break binary wallet compatibility with the distributed executables, which
|
||||
are based on Berkeley DB 4.8. Otherwise, you can build Berkeley DB [yourself](#berkeley-db).
|
||||
|
||||
To build Bitcoin Core without wallet, see [*Disable-wallet mode*](#disable-wallet-mode)
|
||||
|
||||
ZMQ dependencies (provides ZMQ API):
|
||||
@ -153,27 +145,6 @@ See [dependencies.md](dependencies.md) for a complete overview, and
|
||||
[depends](/depends/README.md) on how to compile them yourself, if you wish to
|
||||
not use the packages of your Linux distribution.
|
||||
|
||||
### Berkeley DB
|
||||
|
||||
The legacy wallet uses Berkeley DB. To ensure backwards compatibility it is
|
||||
recommended to use Berkeley DB 4.8. If you have to build it yourself, and don't
|
||||
want to use any other libraries built in depends, you can do:
|
||||
```bash
|
||||
make -C depends NO_BOOST=1 NO_LIBEVENT=1 NO_QT=1 NO_ZMQ=1 NO_USDT=1
|
||||
...
|
||||
to: /path/to/bitcoin/depends/x86_64-pc-linux-gnu
|
||||
```
|
||||
and configure using the following:
|
||||
```bash
|
||||
export BDB_PREFIX="/path/to/bitcoin/depends/x86_64-pc-linux-gnu"
|
||||
|
||||
cmake -B build -DBerkeleyDB_INCLUDE_DIR:PATH="${BDB_PREFIX}/include" -DWITH_BDB=ON
|
||||
```
|
||||
|
||||
**Note**: Make sure that `BDB_PREFIX` is an absolute path.
|
||||
|
||||
**Note**: You only need Berkeley DB if the legacy wallet is enabled (see [*Disable-wallet mode*](#disable-wallet-mode)).
|
||||
|
||||
Disable-wallet mode
|
||||
--------------------
|
||||
When the intention is to only run a P2P node, without a wallet, Bitcoin Core can
|
||||
@ -181,7 +152,7 @@ be compiled in disable-wallet mode with:
|
||||
|
||||
cmake -B build -DENABLE_WALLET=OFF
|
||||
|
||||
In this case there is no dependency on SQLite or Berkeley DB.
|
||||
In this case there is no dependency on SQLite.
|
||||
|
||||
Mining is also possible in disable-wallet mode using the `getblocktemplate` RPC call.
|
||||
|
||||
@ -204,4 +175,3 @@ This example lists the steps necessary to setup and build a command line only di
|
||||
ctest --test-dir build
|
||||
./build/bin/bitcoind
|
||||
|
||||
If you intend to work with legacy Berkeley DB wallets, see [Berkeley DB](#berkeley-db) section.
|
||||
|
@ -32,7 +32,6 @@ Bitcoin Core requires one of the following compilers.
|
||||
| [qrencode](../depends/packages/qrencode.mk) (gui) | [link](https://fukuchi.org/works/qrencode/) | [4.1.1](https://github.com/bitcoin/bitcoin/pull/27312) | N/A | No |
|
||||
| [Qt](../depends/packages/qt.mk) (gui) | [link](https://download.qt.io/archive/qt/) | [6.7.3](https://github.com/bitcoin/bitcoin/pull/30997) | [6.2](https://github.com/bitcoin/bitcoin/pull/30997) | No |
|
||||
| [ZeroMQ](../depends/packages/zeromq.mk) (notifications) | [link](https://github.com/zeromq/libzmq/releases) | [4.3.4](https://github.com/bitcoin/bitcoin/pull/23956) | 4.0.0 | No |
|
||||
| [Berkeley DB](../depends/packages/bdb.mk) (legacy wallet) | [link](https://www.oracle.com/technetwork/database/database-technologies/berkeleydb/downloads/index.html) | 4.8.30 | 4.8.x | No |
|
||||
| [SQLite](../depends/packages/sqlite.mk) (wallet) | [link](https://sqlite.org) | [3.38.5](https://github.com/bitcoin/bitcoin/pull/25378) | [3.7.17](https://github.com/bitcoin/bitcoin/pull/19077) | No |
|
||||
| Python (scripts, tests) | [link](https://www.python.org) | N/A | [3.10](https://github.com/bitcoin/bitcoin/pull/30527) | No |
|
||||
| [systemtap](../depends/packages/systemtap.mk) ([tracing](tracing.md)) | [link](https://sourceware.org/systemtap/) | [4.8](https://github.com/bitcoin/bitcoin/pull/26945)| N/A | No |
|
||||
|
@ -11,8 +11,6 @@ Supporting RPCs are:
|
||||
addresses.
|
||||
- `listunspent` outputs a specialized descriptor for the reported unspent outputs.
|
||||
- `getaddressinfo` outputs a descriptor for solvable addresses (since v0.18).
|
||||
- `importmulti` takes as input descriptors to import into a legacy wallet
|
||||
(since v0.18).
|
||||
- `generatetodescriptor` takes as input a descriptor and generates coins to it
|
||||
(`regtest` only, since v0.19).
|
||||
- `utxoupdatepsbt` takes as input descriptors to add information to the psbt
|
||||
@ -319,5 +317,5 @@ roughly 1 in a trillion chance of not detecting the errors.
|
||||
|
||||
All RPCs in Bitcoin Core will include the checksum in their output. Only
|
||||
certain RPCs require checksums on input, including `deriveaddresses` and
|
||||
`importmulti`. The checksum for a descriptor without one can be computed
|
||||
`importdescriptors`. The checksum for a descriptor without one can be computed
|
||||
using the `getdescriptorinfo` RPC.
|
||||
|
24
doc/files.md
24
doc/files.md
@ -8,14 +8,14 @@
|
||||
|
||||
- [Multi-wallet environment](#multi-wallet-environment)
|
||||
|
||||
- [Berkeley DB database based wallets](#berkeley-db-database-based-wallets)
|
||||
|
||||
- [SQLite database based wallets](#sqlite-database-based-wallets)
|
||||
|
||||
- [GUI settings](#gui-settings)
|
||||
|
||||
- [Legacy subdirectories and files](#legacy-subdirectories-and-files)
|
||||
|
||||
- [Berkeley DB database based wallets](#berkeley-db-database-based-wallets)
|
||||
|
||||
- [Notes](#notes)
|
||||
|
||||
## Data directory location
|
||||
@ -75,7 +75,7 @@ Subdirectory | File(s) | Description
|
||||
|
||||
## Multi-wallet environment
|
||||
|
||||
Wallets are Berkeley DB (BDB) or SQLite databases.
|
||||
Wallets are SQLite databases.
|
||||
|
||||
1. Each user-defined wallet named "wallet_name" resides in the `wallets/wallet_name/` subdirectory.
|
||||
|
||||
@ -88,15 +88,6 @@ Wallets are Berkeley DB (BDB) or SQLite databases.
|
||||
5. Any copy or backup of the wallet should be done through a `backupwallet` call in order to update and lock the wallet, preventing any file corruption caused by updates during the copy.
|
||||
|
||||
|
||||
### Berkeley DB database based wallets
|
||||
|
||||
Subdirectory | File(s) | Description
|
||||
-------------|-------------------|-------------
|
||||
`database/` | BDB logging files | Part of BDB environment; created at start and deleted on shutdown; a user *must keep it as safe* as personal wallet `wallet.dat`
|
||||
`./` | `db.log` | BDB error file
|
||||
`./` | `wallet.dat` | Personal wallet (a BDB database) with keys and transactions
|
||||
`./` | `.walletlock` | BDB wallet lock file
|
||||
|
||||
### SQLite database based wallets
|
||||
|
||||
Subdirectory | File | Description
|
||||
@ -123,6 +114,15 @@ Path | Description | Repository notes
|
||||
`addr.dat` | Peer IP address BDB database; replaced by `peers.dat` in [0.7.0](https://github.com/bitcoin/bitcoin/blob/master/doc/release-notes/release-notes-0.7.0.md) | [PR #1198](https://github.com/bitcoin/bitcoin/pull/1198), [`928d3a01`](https://github.com/bitcoin/bitcoin/commit/928d3a011cc66c7f907c4d053f674ea77dc611cc)
|
||||
`onion_private_key` | Cached Tor onion service private key for `-listenonion` option. Was used for Tor v2 services; replaced by `onion_v3_private_key` in [0.21.0](https://github.com/bitcoin/bitcoin/blob/master/doc/release-notes/release-notes-0.21.0.md) | [PR #19954](https://github.com/bitcoin/bitcoin/pull/19954)
|
||||
|
||||
### Berkeley DB database based wallets
|
||||
|
||||
Subdirectory | File(s) | Description
|
||||
-------------|-------------------|-------------
|
||||
`database/` | BDB logging files | Part of BDB environment; created at start and deleted on shutdown; a user *must keep it as safe* as personal wallet `wallet.dat`
|
||||
`./` | `db.log` | BDB error file
|
||||
`./` | `wallet.dat` | Personal wallet (a BDB database) with keys and transactions
|
||||
`./` | `.walletlock` | BDB wallet lock file
|
||||
|
||||
## Notes
|
||||
|
||||
<a name="note1">1</a>. The `/` (slash, U+002F) is used as the platform-independent path component separator in this document.
|
||||
|
@ -52,8 +52,6 @@ During the generation of the build system only essential build options are enabl
|
||||
|
||||
Run `cmake -B build -LH` to see the full list of available options. GUI tools, such as `ccmake` and `cmake-gui`, can be also helpful.
|
||||
|
||||
If you do need the wallet enabled (`-DENABLE_WALLET=ON`), it is common for devs to use your system bdb version for the wallet, so you don't have to find a copy of bdb 4.8. Wallets from such a build will be incompatible with any release binary (and vice versa), so use with caution on mainnet.
|
||||
|
||||
### Make use of your threads with `-j`
|
||||
|
||||
If you have multiple threads on your machine, you can utilize all of them with:
|
||||
|
51
doc/psbt.md
51
doc/psbt.md
@ -97,54 +97,3 @@ hardware implementations will typically implement multiple roles simultaneously.
|
||||
#### Multisig with multiple Bitcoin Core instances
|
||||
|
||||
For a quick start see [Basic M-of-N multisig example using descriptor wallets and PSBTs](./descriptors.md#basic-multisig-example).
|
||||
If you are using legacy wallets feel free to continue with the example provided here.
|
||||
|
||||
Alice, Bob, and Carol want to create a 2-of-3 multisig address. They're all using
|
||||
Bitcoin Core. We assume their wallets only contain the multisig funds. In case
|
||||
they also have a personal wallet, this can be accomplished through the
|
||||
multiwallet feature - possibly resulting in a need to add `-rpcwallet=name` to
|
||||
the command line in case `bitcoin-cli` is used.
|
||||
|
||||
Setup:
|
||||
- All three call `getnewaddress` to create a new address; call these addresses
|
||||
*Aalice*, *Abob*, and *Acarol*.
|
||||
- All three call `getaddressinfo "X"`, with *X* their respective address, and
|
||||
remember the corresponding public keys. Call these public keys *Kalice*,
|
||||
*Kbob*, and *Kcarol*.
|
||||
- All three now run `addmultisigaddress 2 ["Kalice","Kbob","Kcarol"]` to teach
|
||||
their wallet about the multisig script. Call the address produced by this
|
||||
command *Amulti*. They may be required to explicitly specify the same
|
||||
addresstype option each, to avoid constructing different versions due to
|
||||
differences in configuration.
|
||||
- They also run `importaddress "Amulti" "" false` to make their wallets treat
|
||||
payments to *Amulti* as contributing to the watch-only balance.
|
||||
- Others can verify the produced address by running
|
||||
`createmultisig 2 ["Kalice","Kbob","Kcarol"]`, and expecting *Amulti* as
|
||||
output. Again, it may be necessary to explicitly specify the addresstype
|
||||
in order to get a result that matches. This command won't enable them to
|
||||
initiate transactions later, however.
|
||||
- They can now give out *Amulti* as address others can pay to.
|
||||
|
||||
Later, when *V* BTC has been received on *Amulti*, and Bob and Carol want to
|
||||
move the coins in their entirety to address *Asend*, with no change. Alice
|
||||
does not need to be involved.
|
||||
- One of them - let's assume Carol here - initiates the creation. She runs
|
||||
`walletcreatefundedpsbt [] {"Asend":V} 0 {"subtractFeeFromOutputs":[0], "includeWatching":true}`.
|
||||
We call the resulting PSBT *P*. *P* does not contain any signatures.
|
||||
- Carol needs to sign the transaction herself. In order to do so, she runs
|
||||
`walletprocesspsbt "P"`, and gives the resulting PSBT *P2* to Bob.
|
||||
- Bob inspects the PSBT using `decodepsbt "P2"` to determine if the transaction
|
||||
has indeed just the expected input, and an output to *Asend*, and the fee is
|
||||
reasonable. If he agrees, he calls `walletprocesspsbt "P2"` to sign. The
|
||||
resulting PSBT *P3* contains both Carol's and Bob's signature.
|
||||
- Now anyone can call `finalizepsbt "P3"` to extract a fully signed transaction
|
||||
*T*.
|
||||
- Finally anyone can broadcast the transaction using `sendrawtransaction "T"`.
|
||||
|
||||
In case there are more signers, it may be advantageous to let them all sign in
|
||||
parallel, rather than passing the PSBT from one signer to the next one. In the
|
||||
above example this would translate to Carol handing a copy of *P* to each signer
|
||||
separately. They can then all invoke `walletprocesspsbt "P"`, and end up with
|
||||
their individually-signed PSBT structures. They then all send those back to
|
||||
Carol (or anyone) who can combine them using `combinepsbt`. The last two steps
|
||||
(`finalizepsbt` and `sendrawtransaction`) remain unchanged.
|
||||
|
@ -57,7 +57,7 @@ static void WalletMigration(benchmark::Bench& bench)
|
||||
mtx.vout.emplace_back(COIN, GetScriptForDestination(dest));
|
||||
mtx.vout.emplace_back(COIN, scripts_watch_only.at(j % NUM_WATCH_ONLY_ADDR));
|
||||
mtx.vin.resize(2);
|
||||
wallet->AddToWallet(MakeTransactionRef(mtx), TxStateInactive{}, /*update_wtx=*/nullptr, /*fFlushOnClose=*/false, /*rescanning_old_block=*/true);
|
||||
wallet->AddToWallet(MakeTransactionRef(mtx), TxStateInactive{}, /*update_wtx=*/nullptr, /*rescanning_old_block=*/true);
|
||||
batch.WriteKey(pubkey, key.GetPrivKey(), CKeyMetadata());
|
||||
}
|
||||
|
||||
|
@ -41,7 +41,6 @@ static void SetupWalletToolArgs(ArgsManager& argsman)
|
||||
argsman.AddArg("-descriptors", "Create descriptors wallet. Only for 'create'", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
||||
argsman.AddArg("-legacy", "Create legacy wallet. Only for 'create'", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
||||
argsman.AddArg("-printtoconsole", "Send trace/debug info to console (default: 1 when no -debug is true, 0 otherwise).", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
|
||||
argsman.AddArg("-withinternalbdb", "Use the internal Berkeley DB parser when dumping a Berkeley DB wallet file (default: false)", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
|
||||
|
||||
argsman.AddCommand("info", "Get wallet info");
|
||||
argsman.AddCommand("create", "Create new wallet file");
|
||||
|
@ -47,13 +47,9 @@ void DummyWalletInit::AddWalletOptions(ArgsManager& argsman) const
|
||||
"-walletdir=<dir>",
|
||||
"-walletnotify=<cmd>",
|
||||
"-walletrbf",
|
||||
"-dblogsize=<n>",
|
||||
"-flushwallet",
|
||||
"-privdb",
|
||||
"-walletrejectlongchains",
|
||||
"-walletcrosschain",
|
||||
"-unsafesqlitesync",
|
||||
"-swapbdbendian",
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -107,9 +107,6 @@ public:
|
||||
//! Return whether wallet has private key.
|
||||
virtual bool isSpendable(const CTxDestination& dest) = 0;
|
||||
|
||||
//! Return whether wallet has watch only keys.
|
||||
virtual bool haveWatchOnly() = 0;
|
||||
|
||||
//! Add or update address.
|
||||
virtual bool setAddressBook(const CTxDestination& dest, const std::string& name, const std::optional<wallet::AddressPurpose>& purpose) = 0;
|
||||
|
||||
@ -282,9 +279,6 @@ public:
|
||||
// Remove wallet.
|
||||
virtual void remove() = 0;
|
||||
|
||||
//! Return whether is a legacy wallet
|
||||
virtual bool isLegacy() = 0;
|
||||
|
||||
//! Register handler for unload message.
|
||||
using UnloadFn = std::function<void()>;
|
||||
virtual std::unique_ptr<Handler> handleUnload(UnloadFn fn) = 0;
|
||||
|
@ -195,28 +195,10 @@ OverviewPage::~OverviewPage()
|
||||
void OverviewPage::setBalance(const interfaces::WalletBalances& balances)
|
||||
{
|
||||
BitcoinUnit unit = walletModel->getOptionsModel()->getDisplayUnit();
|
||||
if (walletModel->wallet().isLegacy()) {
|
||||
if (walletModel->wallet().privateKeysDisabled()) {
|
||||
ui->labelBalance->setText(BitcoinUnits::formatWithPrivacy(unit, balances.watch_only_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
|
||||
ui->labelUnconfirmed->setText(BitcoinUnits::formatWithPrivacy(unit, balances.unconfirmed_watch_only_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
|
||||
ui->labelImmature->setText(BitcoinUnits::formatWithPrivacy(unit, balances.immature_watch_only_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
|
||||
ui->labelTotal->setText(BitcoinUnits::formatWithPrivacy(unit, balances.watch_only_balance + balances.unconfirmed_watch_only_balance + balances.immature_watch_only_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
|
||||
} else {
|
||||
ui->labelBalance->setText(BitcoinUnits::formatWithPrivacy(unit, balances.balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
|
||||
ui->labelUnconfirmed->setText(BitcoinUnits::formatWithPrivacy(unit, balances.unconfirmed_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
|
||||
ui->labelImmature->setText(BitcoinUnits::formatWithPrivacy(unit, balances.immature_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
|
||||
ui->labelTotal->setText(BitcoinUnits::formatWithPrivacy(unit, balances.balance + balances.unconfirmed_balance + balances.immature_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
|
||||
ui->labelWatchAvailable->setText(BitcoinUnits::formatWithPrivacy(unit, balances.watch_only_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
|
||||
ui->labelWatchPending->setText(BitcoinUnits::formatWithPrivacy(unit, balances.unconfirmed_watch_only_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
|
||||
ui->labelWatchImmature->setText(BitcoinUnits::formatWithPrivacy(unit, balances.immature_watch_only_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
|
||||
ui->labelWatchTotal->setText(BitcoinUnits::formatWithPrivacy(unit, balances.watch_only_balance + balances.unconfirmed_watch_only_balance + balances.immature_watch_only_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
|
||||
}
|
||||
} else {
|
||||
ui->labelBalance->setText(BitcoinUnits::formatWithPrivacy(unit, balances.balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
|
||||
ui->labelUnconfirmed->setText(BitcoinUnits::formatWithPrivacy(unit, balances.unconfirmed_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
|
||||
ui->labelImmature->setText(BitcoinUnits::formatWithPrivacy(unit, balances.immature_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
|
||||
ui->labelTotal->setText(BitcoinUnits::formatWithPrivacy(unit, balances.balance + balances.unconfirmed_balance + balances.immature_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
|
||||
}
|
||||
ui->labelBalance->setText(BitcoinUnits::formatWithPrivacy(unit, balances.balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
|
||||
ui->labelUnconfirmed->setText(BitcoinUnits::formatWithPrivacy(unit, balances.unconfirmed_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
|
||||
ui->labelImmature->setText(BitcoinUnits::formatWithPrivacy(unit, balances.immature_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
|
||||
ui->labelTotal->setText(BitcoinUnits::formatWithPrivacy(unit, balances.balance + balances.unconfirmed_balance + balances.immature_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
|
||||
// only show immature (newly mined) balance if it's non-zero, so as not to complicate things
|
||||
// for the non-mining users
|
||||
bool showImmature = balances.immature_balance != 0;
|
||||
@ -281,11 +263,7 @@ void OverviewPage::setWalletModel(WalletModel *model)
|
||||
|
||||
connect(model->getOptionsModel(), &OptionsModel::displayUnitChanged, this, &OverviewPage::updateDisplayUnit);
|
||||
|
||||
interfaces::Wallet& wallet = model->wallet();
|
||||
updateWatchOnlyLabels(wallet.haveWatchOnly() && !wallet.privateKeysDisabled());
|
||||
connect(model, &WalletModel::notifyWatchonlyChanged, [this](bool showWatchOnly) {
|
||||
updateWatchOnlyLabels(showWatchOnly && !walletModel->wallet().privateKeysDisabled());
|
||||
});
|
||||
updateWatchOnlyLabels(false);
|
||||
}
|
||||
|
||||
// update the display unit, to not use the default ("BTC")
|
||||
|
@ -72,9 +72,6 @@ namespace {
|
||||
|
||||
// don't add private key handling cmd's to the history
|
||||
const QStringList historyFilter = QStringList()
|
||||
<< "importprivkey"
|
||||
<< "importmulti"
|
||||
<< "sethdseed"
|
||||
<< "signmessagewithprivkey"
|
||||
<< "signrawtransactionwithkey"
|
||||
<< "walletpassphrase"
|
||||
|
@ -711,9 +711,6 @@ void SendCoinsDialog::setBalance(const interfaces::WalletBalances& balances)
|
||||
CAmount balance = balances.balance;
|
||||
if (model->wallet().hasExternalSigner()) {
|
||||
ui->labelBalanceName->setText(tr("External balance:"));
|
||||
} else if (model->wallet().isLegacy() && model->wallet().privateKeysDisabled()) {
|
||||
balance = balances.watch_only_balance;
|
||||
ui->labelBalanceName->setText(tr("Watch-only balance:"));
|
||||
}
|
||||
ui->labelBalance->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), balance));
|
||||
}
|
||||
|
@ -85,8 +85,6 @@ void RPCNestedTests::rpcNestedTests()
|
||||
QVERIFY(result == "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b");
|
||||
QVERIFY(filtered == "getblock(getbestblockhash())[tx][0]");
|
||||
|
||||
RPCConsole::RPCParseCommandLine(nullptr, result, "importprivkey", false, &filtered);
|
||||
QVERIFY(filtered == "importprivkey(…)");
|
||||
RPCConsole::RPCParseCommandLine(nullptr, result, "signmessagewithprivkey abc", false, &filtered);
|
||||
QVERIFY(filtered == "signmessagewithprivkey(…)");
|
||||
RPCConsole::RPCParseCommandLine(nullptr, result, "signmessagewithprivkey abc,def", false, &filtered);
|
||||
@ -99,12 +97,6 @@ void RPCNestedTests::rpcNestedTests()
|
||||
QVERIFY(filtered == "walletpassphrasechange(…)");
|
||||
RPCConsole::RPCParseCommandLine(nullptr, result, "help(encryptwallet(abc, def))", false, &filtered);
|
||||
QVERIFY(filtered == "help(encryptwallet(…))");
|
||||
RPCConsole::RPCParseCommandLine(nullptr, result, "help(importprivkey())", false, &filtered);
|
||||
QVERIFY(filtered == "help(importprivkey(…))");
|
||||
RPCConsole::RPCParseCommandLine(nullptr, result, "help(importprivkey(help()))", false, &filtered);
|
||||
QVERIFY(filtered == "help(importprivkey(…))");
|
||||
RPCConsole::RPCParseCommandLine(nullptr, result, "help(importprivkey(abc), walletpassphrase(def))", false, &filtered);
|
||||
QVERIFY(filtered == "help(importprivkey(…), walletpassphrase(…))");
|
||||
|
||||
RPCConsole::RPCExecuteCommandLine(m_node, result, "rpcNestedTest");
|
||||
QVERIFY(result == "[]");
|
||||
|
@ -241,8 +241,8 @@ void TransactionView::setModel(WalletModel *_model)
|
||||
}
|
||||
}
|
||||
|
||||
// show/hide column Watch-only
|
||||
updateWatchOnlyColumn(_model->wallet().haveWatchOnly());
|
||||
// hide column Watch-only
|
||||
updateWatchOnlyColumn(false);
|
||||
|
||||
// Watch-only signal
|
||||
connect(_model, &WalletModel::notifyWatchonlyChanged, this, &TransactionView::updateWatchOnlyColumn);
|
||||
@ -368,8 +368,6 @@ void TransactionView::exportClicked()
|
||||
// name, column, role
|
||||
writer.setModel(transactionProxyModel);
|
||||
writer.addColumn(tr("Confirmed"), 0, TransactionTableModel::ConfirmedRole);
|
||||
if (model->wallet().haveWatchOnly())
|
||||
writer.addColumn(tr("Watch-only"), TransactionTableModel::Watchonly);
|
||||
writer.addColumn(tr("Date"), 0, TransactionTableModel::DateRole);
|
||||
writer.addColumn(tr("Type"), TransactionTableModel::Type, Qt::EditRole);
|
||||
writer.addColumn(tr("Label"), 0, TransactionTableModel::LabelRole);
|
||||
|
@ -45,7 +45,7 @@ WalletModel::WalletModel(std::unique_ptr<interfaces::Wallet> wallet, ClientModel
|
||||
optionsModel(client_model.getOptionsModel()),
|
||||
timer(new QTimer(this))
|
||||
{
|
||||
fHaveWatchOnly = m_wallet->haveWatchOnly();
|
||||
fHaveWatchOnly = false;
|
||||
addressTableModel = new AddressTableModel(this);
|
||||
transactionTableModel = new TransactionTableModel(platformStyle, this);
|
||||
recentRequestsTableModel = new RecentRequestsTableModel(this);
|
||||
|
@ -48,7 +48,6 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
||||
{ "sendtoaddress", 9, "fee_rate"},
|
||||
{ "sendtoaddress", 10, "verbose"},
|
||||
{ "settxfee", 0, "amount" },
|
||||
{ "sethdseed", 0, "newkeypool" },
|
||||
{ "getreceivedbyaddress", 1, "minconf" },
|
||||
{ "getreceivedbyaddress", 2, "include_immature_coinbase" },
|
||||
{ "getreceivedbylabel", 1, "minconf" },
|
||||
@ -96,8 +95,6 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
||||
{ "getdescriptoractivity", 1, "scanobjects" },
|
||||
{ "getdescriptoractivity", 2, "include_mempool" },
|
||||
{ "scantxoutset", 1, "scanobjects" },
|
||||
{ "addmultisigaddress", 0, "nrequired" },
|
||||
{ "addmultisigaddress", 1, "keys" },
|
||||
{ "createmultisig", 0, "nrequired" },
|
||||
{ "createmultisig", 1, "keys" },
|
||||
{ "listunspent", 0, "minconf" },
|
||||
@ -236,17 +233,10 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
||||
{ "simulaterawtransaction", 0, "rawtxs" },
|
||||
{ "simulaterawtransaction", 1, "options" },
|
||||
{ "simulaterawtransaction", 1, "include_watchonly"},
|
||||
{ "importprivkey", 2, "rescan" },
|
||||
{ "importaddress", 2, "rescan" },
|
||||
{ "importaddress", 3, "p2sh" },
|
||||
{ "importpubkey", 2, "rescan" },
|
||||
{ "importmempool", 1, "options" },
|
||||
{ "importmempool", 1, "apply_fee_delta_priority" },
|
||||
{ "importmempool", 1, "use_current_time" },
|
||||
{ "importmempool", 1, "apply_unbroadcast_set" },
|
||||
{ "importmulti", 0, "requests" },
|
||||
{ "importmulti", 1, "options" },
|
||||
{ "importmulti", 1, "rescan" },
|
||||
{ "importdescriptors", 0, "requests" },
|
||||
{ "listdescriptors", 0, "private" },
|
||||
{ "verifychain", 0, "checklevel" },
|
||||
|
@ -75,14 +75,12 @@ const std::vector<std::string> RPC_COMMANDS_NOT_SAFE_FOR_FUZZING{
|
||||
"addnode", // avoid DNS lookups
|
||||
"addpeeraddress", // avoid DNS lookups
|
||||
"dumptxoutset", // avoid writing to disk
|
||||
"dumpwallet", // avoid writing to disk
|
||||
"enumeratesigners",
|
||||
"echoipc", // avoid assertion failure (Assertion `"EnsureAnyNodeContext(request.context).init" && check' failed.)
|
||||
"generatetoaddress", // avoid prohibitively slow execution (when `num_blocks` is large)
|
||||
"generatetodescriptor", // avoid prohibitively slow execution (when `nblocks` is large)
|
||||
"gettxoutproof", // avoid prohibitively slow execution
|
||||
"importmempool", // avoid reading from disk
|
||||
"importwallet", // avoid reading from disk
|
||||
"loadtxoutset", // avoid reading from disk
|
||||
"loadwallet", // avoid reading from disk
|
||||
"savemempool", // disabled as a precautionary measure: may take a file path argument in the future
|
||||
|
@ -181,7 +181,7 @@ BOOST_AUTO_TEST_CASE(parse_hex)
|
||||
result = TryParseHex<uint8_t>("12 34 56 78").value();
|
||||
BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end());
|
||||
|
||||
// Leading space must be supported (used in BerkeleyEnvironment::Salvage)
|
||||
// Leading space must be supported
|
||||
expected = {0x89, 0x34, 0x56, 0x78};
|
||||
result = ParseHex(" 89 34 56 78");
|
||||
BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end());
|
||||
|
@ -44,8 +44,3 @@ target_link_libraries(bitcoin_wallet
|
||||
Boost::headers
|
||||
$<TARGET_NAME_IF_EXISTS:USDT::headers>
|
||||
)
|
||||
|
||||
if(USE_BDB)
|
||||
target_sources(bitcoin_wallet PRIVATE bdb.cpp)
|
||||
target_link_libraries(bitcoin_wallet PUBLIC BerkeleyDB::BerkeleyDB)
|
||||
endif()
|
||||
|
@ -1,972 +0,0 @@
|
||||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||
// Copyright (c) 2009-present The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <compat/compat.h>
|
||||
#include <logging.h>
|
||||
#include <util/fs.h>
|
||||
#include <util/time.h>
|
||||
#include <wallet/bdb.h>
|
||||
#include <wallet/db.h>
|
||||
|
||||
#include <sync.h>
|
||||
#include <util/check.h>
|
||||
#include <util/fs_helpers.h>
|
||||
#include <util/strencodings.h>
|
||||
#include <util/translation.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <db_cxx.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
// Windows may not define S_IRUSR or S_IWUSR. We define both
|
||||
// here, with the same values as glibc (see stat.h).
|
||||
#ifdef WIN32
|
||||
#ifndef S_IRUSR
|
||||
#define S_IRUSR 0400
|
||||
#define S_IWUSR 0200
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static_assert(BDB_DB_FILE_ID_LEN == DB_FILE_ID_LEN, "DB_FILE_ID_LEN should be 20.");
|
||||
|
||||
namespace wallet {
|
||||
namespace {
|
||||
|
||||
//! Make sure database has a unique fileid within the environment. If it
|
||||
//! doesn't, throw an error. BDB caches do not work properly when more than one
|
||||
//! open database has the same fileid (values written to one database may show
|
||||
//! up in reads to other databases).
|
||||
//!
|
||||
//! BerkeleyDB generates unique fileids by default
|
||||
//! (https://docs.oracle.com/cd/E17275_01/html/programmer_reference/program_copy.html),
|
||||
//! so bitcoin should never create different databases with the same fileid, but
|
||||
//! this error can be triggered if users manually copy database files.
|
||||
void CheckUniqueFileid(const BerkeleyEnvironment& env, const std::string& filename, Db& db, WalletDatabaseFileId& fileid)
|
||||
{
|
||||
if (env.IsMock()) return;
|
||||
|
||||
int ret = db.get_mpf()->get_fileid(fileid.value);
|
||||
if (ret != 0) {
|
||||
throw std::runtime_error(strprintf("BerkeleyDatabase: Can't open database %s (get_fileid failed with %d)", filename, ret));
|
||||
}
|
||||
|
||||
for (const auto& item : env.m_fileids) {
|
||||
if (fileid == item.second && &fileid != &item.second) {
|
||||
throw std::runtime_error(strprintf("BerkeleyDatabase: Can't open database %s (duplicates fileid %s from %s)", filename,
|
||||
HexStr(item.second.value), item.first));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RecursiveMutex cs_db;
|
||||
std::map<std::string, std::weak_ptr<BerkeleyEnvironment>> g_dbenvs GUARDED_BY(cs_db); //!< Map from directory name to db environment.
|
||||
} // namespace
|
||||
|
||||
static constexpr auto REVERSE_BYTE_ORDER{std::endian::native == std::endian::little ? 4321 : 1234};
|
||||
|
||||
bool WalletDatabaseFileId::operator==(const WalletDatabaseFileId& rhs) const
|
||||
{
|
||||
return memcmp(value, &rhs.value, sizeof(value)) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param[in] env_directory Path to environment directory
|
||||
* @return A shared pointer to the BerkeleyEnvironment object for the wallet directory, never empty because ~BerkeleyEnvironment
|
||||
* erases the weak pointer from the g_dbenvs map.
|
||||
* @post A new BerkeleyEnvironment weak pointer is inserted into g_dbenvs if the directory path key was not already in the map.
|
||||
*/
|
||||
std::shared_ptr<BerkeleyEnvironment> GetBerkeleyEnv(const fs::path& env_directory, bool use_shared_memory)
|
||||
{
|
||||
LOCK(cs_db);
|
||||
auto inserted = g_dbenvs.emplace(fs::PathToString(env_directory), std::weak_ptr<BerkeleyEnvironment>());
|
||||
if (inserted.second) {
|
||||
auto env = std::make_shared<BerkeleyEnvironment>(env_directory, use_shared_memory);
|
||||
inserted.first->second = env;
|
||||
return env;
|
||||
}
|
||||
return inserted.first->second.lock();
|
||||
}
|
||||
|
||||
//
|
||||
// BerkeleyBatch
|
||||
//
|
||||
|
||||
void BerkeleyEnvironment::Close()
|
||||
{
|
||||
if (!fDbEnvInit)
|
||||
return;
|
||||
|
||||
fDbEnvInit = false;
|
||||
|
||||
for (auto& db : m_databases) {
|
||||
BerkeleyDatabase& database = db.second.get();
|
||||
assert(database.m_refcount <= 0);
|
||||
if (database.m_db) {
|
||||
database.m_db->close(0);
|
||||
database.m_db.reset();
|
||||
}
|
||||
}
|
||||
|
||||
FILE* error_file = nullptr;
|
||||
dbenv->get_errfile(&error_file);
|
||||
|
||||
int ret = dbenv->close(0);
|
||||
if (ret != 0)
|
||||
LogPrintf("BerkeleyEnvironment::Close: Error %d closing database environment: %s\n", ret, DbEnv::strerror(ret));
|
||||
if (!fMockDb)
|
||||
DbEnv(uint32_t{0}).remove(strPath.c_str(), 0);
|
||||
|
||||
if (error_file) fclose(error_file);
|
||||
|
||||
UnlockDirectory(fs::PathFromString(strPath), ".walletlock");
|
||||
}
|
||||
|
||||
void BerkeleyEnvironment::Reset()
|
||||
{
|
||||
dbenv.reset(new DbEnv(DB_CXX_NO_EXCEPTIONS));
|
||||
fDbEnvInit = false;
|
||||
fMockDb = false;
|
||||
}
|
||||
|
||||
BerkeleyEnvironment::BerkeleyEnvironment(const fs::path& dir_path, bool use_shared_memory) : strPath(fs::PathToString(dir_path)), m_use_shared_memory(use_shared_memory)
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
BerkeleyEnvironment::~BerkeleyEnvironment()
|
||||
{
|
||||
LOCK(cs_db);
|
||||
g_dbenvs.erase(strPath);
|
||||
Close();
|
||||
}
|
||||
|
||||
bool BerkeleyEnvironment::Open(bilingual_str& err)
|
||||
{
|
||||
if (fDbEnvInit) {
|
||||
return true;
|
||||
}
|
||||
|
||||
fs::path pathIn = fs::PathFromString(strPath);
|
||||
TryCreateDirectories(pathIn);
|
||||
if (util::LockDirectory(pathIn, ".walletlock") != util::LockResult::Success) {
|
||||
LogPrintf("Cannot obtain a lock on wallet directory %s. Another instance may be using it.\n", strPath);
|
||||
err = strprintf(_("Error initializing wallet database environment %s!"), fs::quoted(fs::PathToString(Directory())));
|
||||
return false;
|
||||
}
|
||||
|
||||
fs::path pathLogDir = pathIn / "database";
|
||||
TryCreateDirectories(pathLogDir);
|
||||
fs::path pathErrorFile = pathIn / "db.log";
|
||||
LogPrintf("BerkeleyEnvironment::Open: LogDir=%s ErrorFile=%s\n", fs::PathToString(pathLogDir), fs::PathToString(pathErrorFile));
|
||||
|
||||
unsigned int nEnvFlags = 0;
|
||||
if (!m_use_shared_memory) {
|
||||
nEnvFlags |= DB_PRIVATE;
|
||||
}
|
||||
|
||||
dbenv->set_lg_dir(fs::PathToString(pathLogDir).c_str());
|
||||
dbenv->set_cachesize(0, 0x100000, 1); // 1 MiB should be enough for just the wallet
|
||||
dbenv->set_lg_bsize(0x10000);
|
||||
dbenv->set_lg_max(1048576);
|
||||
dbenv->set_lk_max_locks(40000);
|
||||
dbenv->set_lk_max_objects(40000);
|
||||
dbenv->set_errfile(fsbridge::fopen(pathErrorFile, "a")); /// debug
|
||||
dbenv->set_flags(DB_AUTO_COMMIT, 1);
|
||||
dbenv->set_flags(DB_TXN_WRITE_NOSYNC, 1);
|
||||
dbenv->log_set_config(DB_LOG_AUTO_REMOVE, 1);
|
||||
int ret = dbenv->open(strPath.c_str(),
|
||||
DB_CREATE |
|
||||
DB_INIT_LOCK |
|
||||
DB_INIT_LOG |
|
||||
DB_INIT_MPOOL |
|
||||
DB_INIT_TXN |
|
||||
DB_THREAD |
|
||||
DB_RECOVER |
|
||||
nEnvFlags,
|
||||
S_IRUSR | S_IWUSR);
|
||||
if (ret != 0) {
|
||||
LogPrintf("BerkeleyEnvironment::Open: Error %d opening database environment: %s\n", ret, DbEnv::strerror(ret));
|
||||
int ret2 = dbenv->close(0);
|
||||
if (ret2 != 0) {
|
||||
LogPrintf("BerkeleyEnvironment::Open: Error %d closing failed database environment: %s\n", ret2, DbEnv::strerror(ret2));
|
||||
}
|
||||
Reset();
|
||||
err = strprintf(_("Error initializing wallet database environment %s!"), fs::quoted(fs::PathToString(Directory())));
|
||||
if (ret == DB_RUNRECOVERY) {
|
||||
err += Untranslated(" ") + _("This error could occur if this wallet was not shutdown cleanly and was last loaded using a build with a newer version of Berkeley DB. If so, please use the software that last loaded this wallet");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fDbEnvInit = true;
|
||||
fMockDb = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
//! Construct an in-memory mock Berkeley environment for testing
|
||||
BerkeleyEnvironment::BerkeleyEnvironment() : m_use_shared_memory(false)
|
||||
{
|
||||
Reset();
|
||||
|
||||
LogDebug(BCLog::WALLETDB, "BerkeleyEnvironment::MakeMock\n");
|
||||
|
||||
dbenv->set_cachesize(1, 0, 1);
|
||||
dbenv->set_lg_bsize(10485760 * 4);
|
||||
dbenv->set_lg_max(10485760);
|
||||
dbenv->set_lk_max_locks(10000);
|
||||
dbenv->set_lk_max_objects(10000);
|
||||
dbenv->set_flags(DB_AUTO_COMMIT, 1);
|
||||
dbenv->log_set_config(DB_LOG_IN_MEMORY, 1);
|
||||
int ret = dbenv->open(nullptr,
|
||||
DB_CREATE |
|
||||
DB_INIT_LOCK |
|
||||
DB_INIT_LOG |
|
||||
DB_INIT_MPOOL |
|
||||
DB_INIT_TXN |
|
||||
DB_THREAD |
|
||||
DB_PRIVATE,
|
||||
S_IRUSR | S_IWUSR);
|
||||
if (ret > 0) {
|
||||
throw std::runtime_error(strprintf("BerkeleyEnvironment::MakeMock: Error %d opening database environment.", ret));
|
||||
}
|
||||
|
||||
fDbEnvInit = true;
|
||||
fMockDb = true;
|
||||
}
|
||||
|
||||
/** RAII class that automatically cleanses its data on destruction */
|
||||
class SafeDbt final
|
||||
{
|
||||
Dbt m_dbt;
|
||||
|
||||
public:
|
||||
// construct Dbt with internally-managed data
|
||||
SafeDbt();
|
||||
// construct Dbt with provided data
|
||||
SafeDbt(void* data, size_t size);
|
||||
~SafeDbt();
|
||||
|
||||
// delegate to Dbt
|
||||
const void* get_data() const;
|
||||
uint32_t get_size() const;
|
||||
|
||||
// conversion operator to access the underlying Dbt
|
||||
operator Dbt*();
|
||||
};
|
||||
|
||||
SafeDbt::SafeDbt()
|
||||
{
|
||||
m_dbt.set_flags(DB_DBT_MALLOC);
|
||||
}
|
||||
|
||||
SafeDbt::SafeDbt(void* data, size_t size)
|
||||
: m_dbt(data, size)
|
||||
{
|
||||
}
|
||||
|
||||
SafeDbt::~SafeDbt()
|
||||
{
|
||||
if (m_dbt.get_data() != nullptr) {
|
||||
// Clear memory, e.g. in case it was a private key
|
||||
memory_cleanse(m_dbt.get_data(), m_dbt.get_size());
|
||||
// under DB_DBT_MALLOC, data is malloced by the Dbt, but must be
|
||||
// freed by the caller.
|
||||
// https://docs.oracle.com/cd/E17275_01/html/api_reference/C/dbt.html
|
||||
if (m_dbt.get_flags() & DB_DBT_MALLOC) {
|
||||
free(m_dbt.get_data());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const void* SafeDbt::get_data() const
|
||||
{
|
||||
return m_dbt.get_data();
|
||||
}
|
||||
|
||||
uint32_t SafeDbt::get_size() const
|
||||
{
|
||||
return m_dbt.get_size();
|
||||
}
|
||||
|
||||
SafeDbt::operator Dbt*()
|
||||
{
|
||||
return &m_dbt;
|
||||
}
|
||||
|
||||
static std::span<const std::byte> SpanFromDbt(const SafeDbt& dbt)
|
||||
{
|
||||
return {reinterpret_cast<const std::byte*>(dbt.get_data()), dbt.get_size()};
|
||||
}
|
||||
|
||||
BerkeleyDatabase::BerkeleyDatabase(std::shared_ptr<BerkeleyEnvironment> env, fs::path filename, const DatabaseOptions& options) :
|
||||
WalletDatabase(),
|
||||
env(std::move(env)),
|
||||
m_byteswap(options.require_format == DatabaseFormat::BERKELEY_SWAP),
|
||||
m_filename(std::move(filename)),
|
||||
m_max_log_mb(options.max_log_mb)
|
||||
{
|
||||
auto inserted = this->env->m_databases.emplace(m_filename, std::ref(*this));
|
||||
assert(inserted.second);
|
||||
}
|
||||
|
||||
bool BerkeleyDatabase::Verify(bilingual_str& errorStr)
|
||||
{
|
||||
fs::path walletDir = env->Directory();
|
||||
fs::path file_path = walletDir / m_filename;
|
||||
|
||||
LogPrintf("Using BerkeleyDB version %s\n", BerkeleyDatabaseVersion());
|
||||
LogPrintf("Using wallet %s\n", fs::PathToString(file_path));
|
||||
|
||||
if (!env->Open(errorStr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fs::exists(file_path))
|
||||
{
|
||||
assert(m_refcount == 0);
|
||||
|
||||
Db db(env->dbenv.get(), 0);
|
||||
const std::string strFile = fs::PathToString(m_filename);
|
||||
int result = db.verify(strFile.c_str(), nullptr, nullptr, 0);
|
||||
if (result != 0) {
|
||||
errorStr = strprintf(_("%s corrupt. Try using the wallet tool bitcoin-wallet to salvage or restoring a backup."), fs::quoted(fs::PathToString(file_path)));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// also return true if files does not exists
|
||||
return true;
|
||||
}
|
||||
|
||||
void BerkeleyEnvironment::CheckpointLSN(const std::string& strFile)
|
||||
{
|
||||
dbenv->txn_checkpoint(0, 0, 0);
|
||||
if (fMockDb)
|
||||
return;
|
||||
dbenv->lsn_reset(strFile.c_str(), 0);
|
||||
}
|
||||
|
||||
BerkeleyDatabase::~BerkeleyDatabase()
|
||||
{
|
||||
if (env) {
|
||||
LOCK(cs_db);
|
||||
env->CloseDb(m_filename);
|
||||
assert(!m_db);
|
||||
size_t erased = env->m_databases.erase(m_filename);
|
||||
assert(erased == 1);
|
||||
env->m_fileids.erase(fs::PathToString(m_filename));
|
||||
}
|
||||
}
|
||||
|
||||
BerkeleyBatch::BerkeleyBatch(BerkeleyDatabase& database, const bool read_only, bool fFlushOnCloseIn) : m_database(database)
|
||||
{
|
||||
database.AddRef();
|
||||
database.Open();
|
||||
fReadOnly = read_only;
|
||||
fFlushOnClose = fFlushOnCloseIn;
|
||||
env = database.env.get();
|
||||
pdb = database.m_db.get();
|
||||
strFile = fs::PathToString(database.m_filename);
|
||||
}
|
||||
|
||||
void BerkeleyDatabase::Open()
|
||||
{
|
||||
unsigned int nFlags = DB_THREAD | DB_CREATE;
|
||||
|
||||
{
|
||||
LOCK(cs_db);
|
||||
bilingual_str open_err;
|
||||
if (!env->Open(open_err))
|
||||
throw std::runtime_error("BerkeleyDatabase: Failed to open database environment.");
|
||||
|
||||
if (m_db == nullptr) {
|
||||
int ret;
|
||||
std::unique_ptr<Db> pdb_temp = std::make_unique<Db>(env->dbenv.get(), 0);
|
||||
const std::string strFile = fs::PathToString(m_filename);
|
||||
|
||||
bool fMockDb = env->IsMock();
|
||||
if (fMockDb) {
|
||||
DbMpoolFile* mpf = pdb_temp->get_mpf();
|
||||
ret = mpf->set_flags(DB_MPOOL_NOFILE, 1);
|
||||
if (ret != 0) {
|
||||
throw std::runtime_error(strprintf("BerkeleyDatabase: Failed to configure for no temp file backing for database %s", strFile));
|
||||
}
|
||||
}
|
||||
|
||||
if (m_byteswap) {
|
||||
pdb_temp->set_lorder(REVERSE_BYTE_ORDER);
|
||||
}
|
||||
|
||||
ret = pdb_temp->open(nullptr, // Txn pointer
|
||||
fMockDb ? nullptr : strFile.c_str(), // Filename
|
||||
fMockDb ? strFile.c_str() : "main", // Logical db name
|
||||
DB_BTREE, // Database type
|
||||
nFlags, // Flags
|
||||
0);
|
||||
|
||||
if (ret != 0) {
|
||||
throw std::runtime_error(strprintf("BerkeleyDatabase: Error %d, can't open database %s", ret, strFile));
|
||||
}
|
||||
|
||||
// Call CheckUniqueFileid on the containing BDB environment to
|
||||
// avoid BDB data consistency bugs that happen when different data
|
||||
// files in the same environment have the same fileid.
|
||||
CheckUniqueFileid(*env, strFile, *pdb_temp, this->env->m_fileids[strFile]);
|
||||
|
||||
m_db.reset(pdb_temp.release());
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BerkeleyBatch::Flush()
|
||||
{
|
||||
if (activeTxn)
|
||||
return;
|
||||
|
||||
// Flush database activity from memory pool to disk log
|
||||
unsigned int nMinutes = 0;
|
||||
if (fReadOnly)
|
||||
nMinutes = 1;
|
||||
|
||||
if (env) { // env is nullptr for dummy databases (i.e. in tests). Don't actually flush if env is nullptr so we don't segfault
|
||||
env->dbenv->txn_checkpoint(nMinutes ? m_database.m_max_log_mb * 1024 : 0, nMinutes, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void BerkeleyDatabase::IncrementUpdateCounter()
|
||||
{
|
||||
++nUpdateCounter;
|
||||
}
|
||||
|
||||
BerkeleyBatch::~BerkeleyBatch()
|
||||
{
|
||||
Close();
|
||||
m_database.RemoveRef();
|
||||
}
|
||||
|
||||
void BerkeleyBatch::Close()
|
||||
{
|
||||
if (!pdb)
|
||||
return;
|
||||
if (activeTxn)
|
||||
activeTxn->abort();
|
||||
activeTxn = nullptr;
|
||||
pdb = nullptr;
|
||||
|
||||
if (fFlushOnClose)
|
||||
Flush();
|
||||
}
|
||||
|
||||
void BerkeleyEnvironment::CloseDb(const fs::path& filename)
|
||||
{
|
||||
{
|
||||
LOCK(cs_db);
|
||||
auto it = m_databases.find(filename);
|
||||
assert(it != m_databases.end());
|
||||
BerkeleyDatabase& database = it->second.get();
|
||||
if (database.m_db) {
|
||||
// Close the database handle
|
||||
database.m_db->close(0);
|
||||
database.m_db.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BerkeleyEnvironment::ReloadDbEnv()
|
||||
{
|
||||
// Make sure that no Db's are in use
|
||||
AssertLockNotHeld(cs_db);
|
||||
std::unique_lock<RecursiveMutex> lock(cs_db);
|
||||
m_db_in_use.wait(lock, [this](){
|
||||
for (auto& db : m_databases) {
|
||||
if (db.second.get().m_refcount > 0) return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
std::vector<fs::path> filenames;
|
||||
filenames.reserve(m_databases.size());
|
||||
for (const auto& it : m_databases) {
|
||||
filenames.push_back(it.first);
|
||||
}
|
||||
// Close the individual Db's
|
||||
for (const fs::path& filename : filenames) {
|
||||
CloseDb(filename);
|
||||
}
|
||||
// Reset the environment
|
||||
Flush(true); // This will flush and close the environment
|
||||
Reset();
|
||||
bilingual_str open_err;
|
||||
Open(open_err);
|
||||
}
|
||||
|
||||
DbTxn* BerkeleyEnvironment::TxnBegin(int flags)
|
||||
{
|
||||
DbTxn* ptxn = nullptr;
|
||||
int ret = dbenv->txn_begin(nullptr, &ptxn, flags);
|
||||
if (!ptxn || ret != 0)
|
||||
return nullptr;
|
||||
return ptxn;
|
||||
}
|
||||
|
||||
bool BerkeleyDatabase::Rewrite(const char* pszSkip)
|
||||
{
|
||||
while (true) {
|
||||
{
|
||||
LOCK(cs_db);
|
||||
const std::string strFile = fs::PathToString(m_filename);
|
||||
if (m_refcount <= 0) {
|
||||
// Flush log data to the dat file
|
||||
env->CloseDb(m_filename);
|
||||
env->CheckpointLSN(strFile);
|
||||
m_refcount = -1;
|
||||
|
||||
bool fSuccess = true;
|
||||
LogPrintf("BerkeleyBatch::Rewrite: Rewriting %s...\n", strFile);
|
||||
std::string strFileRes = strFile + ".rewrite";
|
||||
{ // surround usage of db with extra {}
|
||||
BerkeleyBatch db(*this, true);
|
||||
std::unique_ptr<Db> pdbCopy = std::make_unique<Db>(env->dbenv.get(), 0);
|
||||
|
||||
if (m_byteswap) {
|
||||
pdbCopy->set_lorder(REVERSE_BYTE_ORDER);
|
||||
}
|
||||
|
||||
int ret = pdbCopy->open(nullptr, // Txn pointer
|
||||
strFileRes.c_str(), // Filename
|
||||
"main", // Logical db name
|
||||
DB_BTREE, // Database type
|
||||
DB_CREATE, // Flags
|
||||
0);
|
||||
if (ret > 0) {
|
||||
LogPrintf("BerkeleyBatch::Rewrite: Can't create database file %s\n", strFileRes);
|
||||
fSuccess = false;
|
||||
}
|
||||
|
||||
std::unique_ptr<DatabaseCursor> cursor = db.GetNewCursor();
|
||||
if (cursor) {
|
||||
while (fSuccess) {
|
||||
DataStream ssKey{};
|
||||
DataStream ssValue{};
|
||||
DatabaseCursor::Status ret1 = cursor->Next(ssKey, ssValue);
|
||||
if (ret1 == DatabaseCursor::Status::DONE) {
|
||||
break;
|
||||
} else if (ret1 == DatabaseCursor::Status::FAIL) {
|
||||
fSuccess = false;
|
||||
break;
|
||||
}
|
||||
if (pszSkip &&
|
||||
strncmp((const char*)ssKey.data(), pszSkip, std::min(ssKey.size(), strlen(pszSkip))) == 0)
|
||||
continue;
|
||||
if (strncmp((const char*)ssKey.data(), "\x07version", 8) == 0) {
|
||||
// Update version:
|
||||
ssValue.clear();
|
||||
ssValue << CLIENT_VERSION;
|
||||
}
|
||||
Dbt datKey(ssKey.data(), ssKey.size());
|
||||
Dbt datValue(ssValue.data(), ssValue.size());
|
||||
int ret2 = pdbCopy->put(nullptr, &datKey, &datValue, DB_NOOVERWRITE);
|
||||
if (ret2 > 0)
|
||||
fSuccess = false;
|
||||
}
|
||||
cursor.reset();
|
||||
}
|
||||
if (fSuccess) {
|
||||
db.Close();
|
||||
env->CloseDb(m_filename);
|
||||
if (pdbCopy->close(0))
|
||||
fSuccess = false;
|
||||
} else {
|
||||
pdbCopy->close(0);
|
||||
}
|
||||
}
|
||||
if (fSuccess) {
|
||||
Db dbA(env->dbenv.get(), 0);
|
||||
if (dbA.remove(strFile.c_str(), nullptr, 0))
|
||||
fSuccess = false;
|
||||
Db dbB(env->dbenv.get(), 0);
|
||||
if (dbB.rename(strFileRes.c_str(), nullptr, strFile.c_str(), 0))
|
||||
fSuccess = false;
|
||||
}
|
||||
if (!fSuccess)
|
||||
LogPrintf("BerkeleyBatch::Rewrite: Failed to rewrite database file %s\n", strFileRes);
|
||||
return fSuccess;
|
||||
}
|
||||
}
|
||||
UninterruptibleSleep(std::chrono::milliseconds{100});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void BerkeleyEnvironment::Flush(bool fShutdown)
|
||||
{
|
||||
const auto start{SteadyClock::now()};
|
||||
// Flush log data to the actual data file on all files that are not in use
|
||||
LogDebug(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: [%s] Flush(%s)%s\n", strPath, fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started");
|
||||
if (!fDbEnvInit)
|
||||
return;
|
||||
{
|
||||
LOCK(cs_db);
|
||||
bool no_dbs_accessed = true;
|
||||
for (auto& db_it : m_databases) {
|
||||
const fs::path& filename = db_it.first;
|
||||
int nRefCount = db_it.second.get().m_refcount;
|
||||
if (nRefCount < 0) continue;
|
||||
const std::string strFile = fs::PathToString(filename);
|
||||
LogDebug(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: Flushing %s (refcount = %d)...\n", strFile, nRefCount);
|
||||
if (nRefCount == 0) {
|
||||
// Move log data to the dat file
|
||||
CloseDb(filename);
|
||||
LogDebug(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: %s checkpoint\n", strFile);
|
||||
dbenv->txn_checkpoint(0, 0, 0);
|
||||
LogDebug(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: %s detach\n", strFile);
|
||||
if (!fMockDb)
|
||||
dbenv->lsn_reset(strFile.c_str(), 0);
|
||||
LogDebug(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: %s closed\n", strFile);
|
||||
nRefCount = -1;
|
||||
} else {
|
||||
no_dbs_accessed = false;
|
||||
}
|
||||
}
|
||||
LogDebug(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: Flush(%s)%s took %15dms\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started", Ticks<std::chrono::milliseconds>(SteadyClock::now() - start));
|
||||
if (fShutdown) {
|
||||
char** listp;
|
||||
if (no_dbs_accessed) {
|
||||
dbenv->log_archive(&listp, DB_ARCH_REMOVE);
|
||||
Close();
|
||||
if (!fMockDb) {
|
||||
fs::remove_all(fs::PathFromString(strPath) / "database");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool BerkeleyDatabase::PeriodicFlush()
|
||||
{
|
||||
// Don't flush if we can't acquire the lock.
|
||||
TRY_LOCK(cs_db, lockDb);
|
||||
if (!lockDb) return false;
|
||||
|
||||
// Don't flush if any databases are in use
|
||||
for (auto& it : env->m_databases) {
|
||||
if (it.second.get().m_refcount > 0) return false;
|
||||
}
|
||||
|
||||
// Don't flush if there haven't been any batch writes for this database.
|
||||
if (m_refcount < 0) return false;
|
||||
|
||||
const std::string strFile = fs::PathToString(m_filename);
|
||||
LogDebug(BCLog::WALLETDB, "Flushing %s\n", strFile);
|
||||
const auto start{SteadyClock::now()};
|
||||
|
||||
// Flush wallet file so it's self contained
|
||||
env->CloseDb(m_filename);
|
||||
env->CheckpointLSN(strFile);
|
||||
m_refcount = -1;
|
||||
|
||||
LogDebug(BCLog::WALLETDB, "Flushed %s %dms\n", strFile, Ticks<std::chrono::milliseconds>(SteadyClock::now() - start));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BerkeleyDatabase::Backup(const std::string& strDest) const
|
||||
{
|
||||
const std::string strFile = fs::PathToString(m_filename);
|
||||
while (true)
|
||||
{
|
||||
{
|
||||
LOCK(cs_db);
|
||||
if (m_refcount <= 0)
|
||||
{
|
||||
// Flush log data to the dat file
|
||||
env->CloseDb(m_filename);
|
||||
env->CheckpointLSN(strFile);
|
||||
|
||||
// Copy wallet file
|
||||
fs::path pathSrc = env->Directory() / m_filename;
|
||||
fs::path pathDest(fs::PathFromString(strDest));
|
||||
if (fs::is_directory(pathDest))
|
||||
pathDest /= m_filename;
|
||||
|
||||
try {
|
||||
if (fs::exists(pathDest) && fs::equivalent(pathSrc, pathDest)) {
|
||||
LogPrintf("cannot backup to wallet source file %s\n", fs::PathToString(pathDest));
|
||||
return false;
|
||||
}
|
||||
|
||||
fs::copy_file(pathSrc, pathDest, fs::copy_options::overwrite_existing);
|
||||
LogPrintf("copied %s to %s\n", strFile, fs::PathToString(pathDest));
|
||||
return true;
|
||||
} catch (const fs::filesystem_error& e) {
|
||||
LogWarning("error copying %s to %s - %s\n", strFile, fs::PathToString(pathDest), e.code().message());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
UninterruptibleSleep(std::chrono::milliseconds{100});
|
||||
}
|
||||
}
|
||||
|
||||
void BerkeleyDatabase::Flush()
|
||||
{
|
||||
env->Flush(false);
|
||||
}
|
||||
|
||||
void BerkeleyDatabase::Close()
|
||||
{
|
||||
env->Flush(true);
|
||||
}
|
||||
|
||||
void BerkeleyDatabase::ReloadDbEnv()
|
||||
{
|
||||
env->ReloadDbEnv();
|
||||
}
|
||||
|
||||
BerkeleyCursor::BerkeleyCursor(BerkeleyDatabase& database, const BerkeleyBatch& batch, std::span<const std::byte> prefix)
|
||||
: m_key_prefix(prefix.begin(), prefix.end())
|
||||
{
|
||||
if (!database.m_db.get()) {
|
||||
throw std::runtime_error(STR_INTERNAL_BUG("BerkeleyDatabase does not exist"));
|
||||
}
|
||||
// Transaction argument to cursor is only needed when using the cursor to
|
||||
// write to the database. Read-only cursors do not need a txn pointer.
|
||||
int ret = database.m_db->cursor(batch.txn(), &m_cursor, 0);
|
||||
if (ret != 0) {
|
||||
throw std::runtime_error(STR_INTERNAL_BUG(strprintf("BDB Cursor could not be created. Returned %d", ret)));
|
||||
}
|
||||
}
|
||||
|
||||
DatabaseCursor::Status BerkeleyCursor::Next(DataStream& ssKey, DataStream& ssValue)
|
||||
{
|
||||
if (m_cursor == nullptr) return Status::FAIL;
|
||||
// Read at cursor
|
||||
SafeDbt datKey(m_key_prefix.data(), m_key_prefix.size());
|
||||
SafeDbt datValue;
|
||||
int ret = -1;
|
||||
if (m_first && !m_key_prefix.empty()) {
|
||||
ret = m_cursor->get(datKey, datValue, DB_SET_RANGE);
|
||||
} else {
|
||||
ret = m_cursor->get(datKey, datValue, DB_NEXT);
|
||||
}
|
||||
m_first = false;
|
||||
if (ret == DB_NOTFOUND) {
|
||||
return Status::DONE;
|
||||
}
|
||||
if (ret != 0) {
|
||||
return Status::FAIL;
|
||||
}
|
||||
|
||||
std::span<const std::byte> raw_key = SpanFromDbt(datKey);
|
||||
if (!m_key_prefix.empty() && std::mismatch(raw_key.begin(), raw_key.end(), m_key_prefix.begin(), m_key_prefix.end()).second != m_key_prefix.end()) {
|
||||
return Status::DONE;
|
||||
}
|
||||
|
||||
// Convert to streams
|
||||
ssKey.clear();
|
||||
ssKey.write(raw_key);
|
||||
ssValue.clear();
|
||||
ssValue.write(SpanFromDbt(datValue));
|
||||
return Status::MORE;
|
||||
}
|
||||
|
||||
BerkeleyCursor::~BerkeleyCursor()
|
||||
{
|
||||
if (!m_cursor) return;
|
||||
m_cursor->close();
|
||||
m_cursor = nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<DatabaseCursor> BerkeleyBatch::GetNewCursor()
|
||||
{
|
||||
if (!pdb) return nullptr;
|
||||
return std::make_unique<BerkeleyCursor>(m_database, *this);
|
||||
}
|
||||
|
||||
std::unique_ptr<DatabaseCursor> BerkeleyBatch::GetNewPrefixCursor(std::span<const std::byte> prefix)
|
||||
{
|
||||
if (!pdb) return nullptr;
|
||||
return std::make_unique<BerkeleyCursor>(m_database, *this, prefix);
|
||||
}
|
||||
|
||||
bool BerkeleyBatch::TxnBegin()
|
||||
{
|
||||
if (!pdb || activeTxn)
|
||||
return false;
|
||||
DbTxn* ptxn = env->TxnBegin(DB_TXN_WRITE_NOSYNC);
|
||||
if (!ptxn)
|
||||
return false;
|
||||
activeTxn = ptxn;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BerkeleyBatch::TxnCommit()
|
||||
{
|
||||
if (!pdb || !activeTxn)
|
||||
return false;
|
||||
int ret = activeTxn->commit(0);
|
||||
activeTxn = nullptr;
|
||||
return (ret == 0);
|
||||
}
|
||||
|
||||
bool BerkeleyBatch::TxnAbort()
|
||||
{
|
||||
if (!pdb || !activeTxn)
|
||||
return false;
|
||||
int ret = activeTxn->abort();
|
||||
activeTxn = nullptr;
|
||||
return (ret == 0);
|
||||
}
|
||||
|
||||
bool BerkeleyDatabaseSanityCheck()
|
||||
{
|
||||
int major, minor;
|
||||
DbEnv::version(&major, &minor, nullptr);
|
||||
|
||||
/* If the major version differs, or the minor version of library is *older*
|
||||
* than the header that was compiled against, flag an error.
|
||||
*/
|
||||
if (major != DB_VERSION_MAJOR || minor < DB_VERSION_MINOR) {
|
||||
LogPrintf("BerkeleyDB database version conflict: header version is %d.%d, library version is %d.%d\n",
|
||||
DB_VERSION_MAJOR, DB_VERSION_MINOR, major, minor);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string BerkeleyDatabaseVersion()
|
||||
{
|
||||
return DbEnv::version(nullptr, nullptr, nullptr);
|
||||
}
|
||||
|
||||
bool BerkeleyBatch::ReadKey(DataStream&& key, DataStream& value)
|
||||
{
|
||||
if (!pdb)
|
||||
return false;
|
||||
|
||||
SafeDbt datKey(key.data(), key.size());
|
||||
|
||||
SafeDbt datValue;
|
||||
int ret = pdb->get(activeTxn, datKey, datValue, 0);
|
||||
if (ret == 0 && datValue.get_data() != nullptr) {
|
||||
value.clear();
|
||||
value.write(SpanFromDbt(datValue));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BerkeleyBatch::WriteKey(DataStream&& key, DataStream&& value, bool overwrite)
|
||||
{
|
||||
if (!pdb)
|
||||
return false;
|
||||
if (fReadOnly)
|
||||
assert(!"Write called on database in read-only mode");
|
||||
|
||||
SafeDbt datKey(key.data(), key.size());
|
||||
|
||||
SafeDbt datValue(value.data(), value.size());
|
||||
|
||||
int ret = pdb->put(activeTxn, datKey, datValue, (overwrite ? 0 : DB_NOOVERWRITE));
|
||||
return (ret == 0);
|
||||
}
|
||||
|
||||
bool BerkeleyBatch::EraseKey(DataStream&& key)
|
||||
{
|
||||
if (!pdb)
|
||||
return false;
|
||||
if (fReadOnly)
|
||||
assert(!"Erase called on database in read-only mode");
|
||||
|
||||
SafeDbt datKey(key.data(), key.size());
|
||||
|
||||
int ret = pdb->del(activeTxn, datKey, 0);
|
||||
return (ret == 0 || ret == DB_NOTFOUND);
|
||||
}
|
||||
|
||||
bool BerkeleyBatch::HasKey(DataStream&& key)
|
||||
{
|
||||
if (!pdb)
|
||||
return false;
|
||||
|
||||
SafeDbt datKey(key.data(), key.size());
|
||||
|
||||
int ret = pdb->exists(activeTxn, datKey, 0);
|
||||
return ret == 0;
|
||||
}
|
||||
|
||||
bool BerkeleyBatch::ErasePrefix(std::span<const std::byte> prefix)
|
||||
{
|
||||
// Because this function erases records one by one, ensure that it is executed within a txn context.
|
||||
// Otherwise, consistency is at risk; it's possible that certain records are removed while others
|
||||
// remain due to an internal failure during the procedure.
|
||||
// Additionally, the Dbc::del() cursor delete call below would fail without an active transaction.
|
||||
if (!Assume(activeTxn)) return false;
|
||||
|
||||
auto cursor{std::make_unique<BerkeleyCursor>(m_database, *this)};
|
||||
// const_cast is safe below even though prefix_key is an in/out parameter,
|
||||
// because we are not using the DB_DBT_USERMEM flag, so BDB will allocate
|
||||
// and return a different output data pointer
|
||||
Dbt prefix_key{const_cast<std::byte*>(prefix.data()), static_cast<uint32_t>(prefix.size())}, prefix_value{};
|
||||
int ret{cursor->dbc()->get(&prefix_key, &prefix_value, DB_SET_RANGE)};
|
||||
for (int flag{DB_CURRENT}; ret == 0; flag = DB_NEXT) {
|
||||
SafeDbt key, value;
|
||||
ret = cursor->dbc()->get(key, value, flag);
|
||||
if (ret != 0 || key.get_size() < prefix.size() || memcmp(key.get_data(), prefix.data(), prefix.size()) != 0) break;
|
||||
ret = cursor->dbc()->del(0);
|
||||
}
|
||||
cursor.reset();
|
||||
return ret == 0 || ret == DB_NOTFOUND;
|
||||
}
|
||||
|
||||
void BerkeleyDatabase::AddRef()
|
||||
{
|
||||
LOCK(cs_db);
|
||||
if (m_refcount < 0) {
|
||||
m_refcount = 1;
|
||||
} else {
|
||||
m_refcount++;
|
||||
}
|
||||
}
|
||||
|
||||
void BerkeleyDatabase::RemoveRef()
|
||||
{
|
||||
LOCK(cs_db);
|
||||
m_refcount--;
|
||||
if (env) env->m_db_in_use.notify_all();
|
||||
}
|
||||
|
||||
std::unique_ptr<DatabaseBatch> BerkeleyDatabase::MakeBatch(bool flush_on_close)
|
||||
{
|
||||
return std::make_unique<BerkeleyBatch>(*this, false, flush_on_close);
|
||||
}
|
||||
|
||||
std::unique_ptr<BerkeleyDatabase> MakeBerkeleyDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error)
|
||||
{
|
||||
fs::path data_file = BDBDataFile(path);
|
||||
std::unique_ptr<BerkeleyDatabase> db;
|
||||
{
|
||||
LOCK(cs_db); // Lock env.m_databases until insert in BerkeleyDatabase constructor
|
||||
fs::path data_filename = data_file.filename();
|
||||
std::shared_ptr<BerkeleyEnvironment> env = GetBerkeleyEnv(data_file.parent_path(), options.use_shared_memory);
|
||||
if (env->m_databases.count(data_filename)) {
|
||||
error = Untranslated(strprintf("Refusing to load database. Data file '%s' is already loaded.", fs::PathToString(env->Directory() / data_filename)));
|
||||
status = DatabaseStatus::FAILED_ALREADY_LOADED;
|
||||
return nullptr;
|
||||
}
|
||||
db = std::make_unique<BerkeleyDatabase>(std::move(env), std::move(data_filename), options);
|
||||
}
|
||||
|
||||
if (options.verify && !db->Verify(error)) {
|
||||
status = DatabaseStatus::FAILED_VERIFY;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
status = DatabaseStatus::SUCCESS;
|
||||
return db;
|
||||
}
|
||||
} // namespace wallet
|
225
src/wallet/bdb.h
225
src/wallet/bdb.h
@ -1,225 +0,0 @@
|
||||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||
// Copyright (c) 2009-present The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef BITCOIN_WALLET_BDB_H
|
||||
#define BITCOIN_WALLET_BDB_H
|
||||
|
||||
#include <clientversion.h>
|
||||
#include <common/system.h>
|
||||
#include <serialize.h>
|
||||
#include <streams.h>
|
||||
#include <util/fs.h>
|
||||
#include <wallet/db.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
struct bilingual_str;
|
||||
|
||||
class DbEnv;
|
||||
class DbTxn;
|
||||
class Db;
|
||||
class Dbc;
|
||||
|
||||
// This constant was introduced in BDB 4.0.14 and has never changed, but there
|
||||
// is a belt-and-suspenders check in the cpp file just in case.
|
||||
#define BDB_DB_FILE_ID_LEN 20 /* Unique file ID length. */
|
||||
|
||||
namespace wallet {
|
||||
|
||||
struct WalletDatabaseFileId {
|
||||
uint8_t value[BDB_DB_FILE_ID_LEN];
|
||||
bool operator==(const WalletDatabaseFileId& rhs) const;
|
||||
};
|
||||
|
||||
class BerkeleyDatabase;
|
||||
|
||||
class BerkeleyEnvironment
|
||||
{
|
||||
private:
|
||||
bool fDbEnvInit;
|
||||
bool fMockDb;
|
||||
// Don't change into fs::path, as that can result in
|
||||
// shutdown problems/crashes caused by a static initialized internal pointer.
|
||||
std::string strPath;
|
||||
|
||||
public:
|
||||
std::unique_ptr<DbEnv> dbenv;
|
||||
std::map<fs::path, std::reference_wrapper<BerkeleyDatabase>> m_databases;
|
||||
std::unordered_map<std::string, WalletDatabaseFileId> m_fileids;
|
||||
std::condition_variable_any m_db_in_use;
|
||||
bool m_use_shared_memory;
|
||||
|
||||
explicit BerkeleyEnvironment(const fs::path& env_directory, bool use_shared_memory);
|
||||
BerkeleyEnvironment();
|
||||
~BerkeleyEnvironment();
|
||||
void Reset();
|
||||
|
||||
bool IsMock() const { return fMockDb; }
|
||||
bool IsInitialized() const { return fDbEnvInit; }
|
||||
fs::path Directory() const { return fs::PathFromString(strPath); }
|
||||
|
||||
bool Open(bilingual_str& error);
|
||||
void Close();
|
||||
void Flush(bool fShutdown);
|
||||
void CheckpointLSN(const std::string& strFile);
|
||||
|
||||
void CloseDb(const fs::path& filename);
|
||||
void ReloadDbEnv();
|
||||
|
||||
DbTxn* TxnBegin(int flags);
|
||||
};
|
||||
|
||||
/** Get BerkeleyEnvironment given a directory path. */
|
||||
std::shared_ptr<BerkeleyEnvironment> GetBerkeleyEnv(const fs::path& env_directory, bool use_shared_memory);
|
||||
|
||||
class BerkeleyBatch;
|
||||
|
||||
/** An instance of this class represents one database.
|
||||
* For BerkeleyDB this is just a (env, strFile) tuple.
|
||||
**/
|
||||
class BerkeleyDatabase : public WalletDatabase
|
||||
{
|
||||
public:
|
||||
BerkeleyDatabase() = delete;
|
||||
|
||||
/** Create DB handle to real database */
|
||||
BerkeleyDatabase(std::shared_ptr<BerkeleyEnvironment> env, fs::path filename, const DatabaseOptions& options);
|
||||
|
||||
~BerkeleyDatabase() override;
|
||||
|
||||
/** Open the database if it is not already opened. */
|
||||
void Open() override;
|
||||
|
||||
/** Rewrite the entire database on disk, with the exception of key pszSkip if non-zero
|
||||
*/
|
||||
bool Rewrite(const char* pszSkip=nullptr) override;
|
||||
|
||||
/** Indicate that a new database user has begun using the database. */
|
||||
void AddRef() override;
|
||||
/** Indicate that database user has stopped using the database and that it could be flushed or closed. */
|
||||
void RemoveRef() override;
|
||||
|
||||
/** Back up the entire database to a file.
|
||||
*/
|
||||
bool Backup(const std::string& strDest) const override;
|
||||
|
||||
/** Make sure all changes are flushed to database file.
|
||||
*/
|
||||
void Flush() override;
|
||||
/** Flush to the database file and close the database.
|
||||
* Also close the environment if no other databases are open in it.
|
||||
*/
|
||||
void Close() override;
|
||||
/* flush the wallet passively (TRY_LOCK)
|
||||
ideal to be called periodically */
|
||||
bool PeriodicFlush() override;
|
||||
|
||||
void IncrementUpdateCounter() override;
|
||||
|
||||
void ReloadDbEnv() override;
|
||||
|
||||
/** Verifies the environment and database file */
|
||||
bool Verify(bilingual_str& error);
|
||||
|
||||
/** Return path to main database filename */
|
||||
std::string Filename() override { return fs::PathToString(env->Directory() / m_filename); }
|
||||
|
||||
std::string Format() override { return "bdb"; }
|
||||
/**
|
||||
* Pointer to shared database environment.
|
||||
*
|
||||
* Normally there is only one BerkeleyDatabase object per
|
||||
* BerkeleyEnvivonment, but in the special, backwards compatible case where
|
||||
* multiple wallet BDB data files are loaded from the same directory, this
|
||||
* will point to a shared instance that gets freed when the last data file
|
||||
* is closed.
|
||||
*/
|
||||
std::shared_ptr<BerkeleyEnvironment> env;
|
||||
|
||||
/** Database pointer. This is initialized lazily and reset during flushes, so it can be null. */
|
||||
std::unique_ptr<Db> m_db;
|
||||
|
||||
// Whether to byteswap
|
||||
bool m_byteswap;
|
||||
|
||||
fs::path m_filename;
|
||||
int64_t m_max_log_mb;
|
||||
|
||||
/** Make a BerkeleyBatch connected to this database */
|
||||
std::unique_ptr<DatabaseBatch> MakeBatch(bool flush_on_close = true) override;
|
||||
};
|
||||
|
||||
class BerkeleyCursor : public DatabaseCursor
|
||||
{
|
||||
private:
|
||||
Dbc* m_cursor;
|
||||
std::vector<std::byte> m_key_prefix;
|
||||
bool m_first{true};
|
||||
|
||||
public:
|
||||
// Constructor for cursor for records matching the prefix
|
||||
// To match all records, an empty prefix may be provided.
|
||||
explicit BerkeleyCursor(BerkeleyDatabase& database, const BerkeleyBatch& batch, std::span<const std::byte> prefix = {});
|
||||
~BerkeleyCursor() override;
|
||||
|
||||
Status Next(DataStream& key, DataStream& value) override;
|
||||
Dbc* dbc() const { return m_cursor; }
|
||||
};
|
||||
|
||||
/** RAII class that provides access to a Berkeley database */
|
||||
class BerkeleyBatch : public DatabaseBatch
|
||||
{
|
||||
private:
|
||||
bool ReadKey(DataStream&& key, DataStream& value) override;
|
||||
bool WriteKey(DataStream&& key, DataStream&& value, bool overwrite = true) override;
|
||||
bool EraseKey(DataStream&& key) override;
|
||||
bool HasKey(DataStream&& key) override;
|
||||
bool ErasePrefix(std::span<const std::byte> prefix) override;
|
||||
|
||||
protected:
|
||||
Db* pdb{nullptr};
|
||||
std::string strFile;
|
||||
DbTxn* activeTxn{nullptr};
|
||||
bool fReadOnly;
|
||||
bool fFlushOnClose;
|
||||
BerkeleyEnvironment *env;
|
||||
BerkeleyDatabase& m_database;
|
||||
|
||||
public:
|
||||
explicit BerkeleyBatch(BerkeleyDatabase& database, const bool fReadOnly, bool fFlushOnCloseIn=true);
|
||||
~BerkeleyBatch() override;
|
||||
|
||||
BerkeleyBatch(const BerkeleyBatch&) = delete;
|
||||
BerkeleyBatch& operator=(const BerkeleyBatch&) = delete;
|
||||
|
||||
void Flush() override;
|
||||
void Close() override;
|
||||
|
||||
std::unique_ptr<DatabaseCursor> GetNewCursor() override;
|
||||
std::unique_ptr<DatabaseCursor> GetNewPrefixCursor(std::span<const std::byte> prefix) override;
|
||||
bool TxnBegin() override;
|
||||
bool TxnCommit() override;
|
||||
bool TxnAbort() override;
|
||||
bool HasActiveTxn() override { return activeTxn != nullptr; }
|
||||
DbTxn* txn() const { return activeTxn; }
|
||||
};
|
||||
|
||||
std::string BerkeleyDatabaseVersion();
|
||||
|
||||
/** Perform sanity check of runtime BDB version versus linked BDB version.
|
||||
*/
|
||||
bool BerkeleyDatabaseSanityCheck();
|
||||
|
||||
//! Return object giving access to Berkeley database at specified path.
|
||||
std::unique_ptr<BerkeleyDatabase> MakeBerkeleyDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error);
|
||||
} // namespace wallet
|
||||
|
||||
#endif // BITCOIN_WALLET_BDB_H
|
@ -155,8 +155,6 @@ void ReadDatabaseArgs(const ArgsManager& args, DatabaseOptions& options)
|
||||
{
|
||||
// Override current options with args values, if any were specified
|
||||
options.use_unsafe_sync = args.GetBoolArg("-unsafesqlitesync", options.use_unsafe_sync);
|
||||
options.use_shared_memory = !args.GetBoolArg("-privdb", !options.use_shared_memory);
|
||||
options.max_log_mb = args.GetIntArg("-dblogsize", options.max_log_mb);
|
||||
}
|
||||
|
||||
} // namespace wallet
|
||||
|
@ -62,7 +62,6 @@ public:
|
||||
DatabaseBatch(const DatabaseBatch&) = delete;
|
||||
DatabaseBatch& operator=(const DatabaseBatch&) = delete;
|
||||
|
||||
virtual void Flush() = 0;
|
||||
virtual void Close() = 0;
|
||||
|
||||
template <typename K, typename T>
|
||||
@ -131,7 +130,7 @@ class WalletDatabase
|
||||
{
|
||||
public:
|
||||
/** Create dummy DB handle */
|
||||
WalletDatabase() : nUpdateCounter(0) {}
|
||||
WalletDatabase() = default;
|
||||
virtual ~WalletDatabase() = default;
|
||||
|
||||
/** Open the database if it is not already opened. */
|
||||
@ -139,10 +138,6 @@ public:
|
||||
|
||||
//! Counts the number of active database users to be sure that the database is not closed while someone is using it
|
||||
std::atomic<int> m_refcount{0};
|
||||
/** Indicate the a new database user has began using the database. Increments m_refcount */
|
||||
virtual void AddRef() = 0;
|
||||
/** Indicate that database user has stopped using the database and that it could be flushed or closed. Decrement m_refcount */
|
||||
virtual void RemoveRef() = 0;
|
||||
|
||||
/** Rewrite the entire database on disk, with the exception of key pszSkip if non-zero
|
||||
*/
|
||||
@ -152,39 +147,23 @@ public:
|
||||
*/
|
||||
virtual bool Backup(const std::string& strDest) const = 0;
|
||||
|
||||
/** Make sure all changes are flushed to database file.
|
||||
*/
|
||||
virtual void Flush() = 0;
|
||||
/** Flush to the database file and close the database.
|
||||
* Also close the environment if no other databases are open in it.
|
||||
*/
|
||||
virtual void Close() = 0;
|
||||
/* flush the wallet passively (TRY_LOCK)
|
||||
ideal to be called periodically */
|
||||
virtual bool PeriodicFlush() = 0;
|
||||
|
||||
virtual void IncrementUpdateCounter() = 0;
|
||||
|
||||
virtual void ReloadDbEnv() = 0;
|
||||
|
||||
/** Return path to main database file for logs and error messages. */
|
||||
virtual std::string Filename() = 0;
|
||||
|
||||
virtual std::string Format() = 0;
|
||||
|
||||
std::atomic<unsigned int> nUpdateCounter;
|
||||
unsigned int nLastSeen{0};
|
||||
unsigned int nLastFlushed{0};
|
||||
int64_t nLastWalletUpdate{0};
|
||||
|
||||
/** Make a DatabaseBatch connected to this database */
|
||||
virtual std::unique_ptr<DatabaseBatch> MakeBatch(bool flush_on_close = true) = 0;
|
||||
virtual std::unique_ptr<DatabaseBatch> MakeBatch() = 0;
|
||||
};
|
||||
|
||||
enum class DatabaseFormat {
|
||||
SQLITE,
|
||||
BERKELEY_RO,
|
||||
BERKELEY_SWAP,
|
||||
};
|
||||
|
||||
struct DatabaseOptions {
|
||||
|
@ -48,7 +48,7 @@ static feebumper::Result PreconditionChecks(const CWallet& wallet, const CWallet
|
||||
if (require_mine) {
|
||||
// check that original tx consists entirely of our inputs
|
||||
// if not, we can't bump the fee, because the wallet has no way of knowing the value of the other inputs (thus the fee)
|
||||
isminefilter filter = wallet.GetLegacyScriptPubKeyMan() && wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) ? ISMINE_WATCH_ONLY : ISMINE_SPENDABLE;
|
||||
isminefilter filter = ISMINE_SPENDABLE;
|
||||
if (!AllInputsMine(wallet, *wtx.tx, filter)) {
|
||||
errors.emplace_back(Untranslated("Transaction contains inputs that don't belong to this wallet"));
|
||||
return feebumper::Result::WALLET_ERROR;
|
||||
|
@ -18,9 +18,6 @@
|
||||
#include <util/check.h>
|
||||
#include <util/moneystr.h>
|
||||
#include <util/translation.h>
|
||||
#ifdef USE_BDB
|
||||
#include <wallet/bdb.h>
|
||||
#endif
|
||||
#include <wallet/coincontrol.h>
|
||||
#include <wallet/wallet.h>
|
||||
#include <walletinitinterface.h>
|
||||
@ -81,15 +78,6 @@ void WalletInit::AddWalletOptions(ArgsManager& argsman) const
|
||||
#endif
|
||||
argsman.AddArg("-walletrbf", strprintf("Send transactions with full-RBF opt-in enabled (RPC only, default: %u)", DEFAULT_WALLET_RBF), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
|
||||
|
||||
#ifdef USE_BDB
|
||||
argsman.AddArg("-dblogsize=<n>", strprintf("Flush wallet database activity from memory to disk log every <n> megabytes (default: %u)", DatabaseOptions().max_log_mb), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST);
|
||||
argsman.AddArg("-flushwallet", strprintf("Run a thread to flush wallet periodically (default: %u)", DEFAULT_FLUSHWALLET), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST);
|
||||
argsman.AddArg("-privdb", strprintf("Sets the DB_PRIVATE flag in the wallet db environment (default: %u)", !DatabaseOptions().use_shared_memory), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST);
|
||||
argsman.AddArg("-swapbdbendian", "Swaps the internal endianness of BDB wallet databases (default: false)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST);
|
||||
#else
|
||||
argsman.AddHiddenArgs({"-dblogsize", "-flushwallet", "-privdb", "-swapbdbendian"});
|
||||
#endif
|
||||
|
||||
argsman.AddArg("-unsafesqlitesync", "Set SQLite synchronous=OFF to disable waiting for the database to sync to disk. This is unsafe and can cause data loss and corruption. This option is only used by tests to improve their performance (default: false)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST);
|
||||
|
||||
argsman.AddArg("-walletrejectlongchains", strprintf("Wallet will not create transactions that violate mempool chain limits (default: %u)", DEFAULT_WALLET_REJECT_LONG_CHAINS), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST);
|
||||
|
@ -175,14 +175,6 @@ public:
|
||||
LOCK(m_wallet->cs_wallet);
|
||||
return m_wallet->IsMine(dest) & ISMINE_SPENDABLE;
|
||||
}
|
||||
bool haveWatchOnly() override
|
||||
{
|
||||
auto spk_man = m_wallet->GetLegacyScriptPubKeyMan();
|
||||
if (spk_man) {
|
||||
return spk_man->HaveWatchOnly();
|
||||
}
|
||||
return false;
|
||||
};
|
||||
bool setAddressBook(const CTxDestination& dest, const std::string& name, const std::optional<AddressPurpose>& purpose) override
|
||||
{
|
||||
return m_wallet->SetAddressBook(dest, name, purpose);
|
||||
@ -407,12 +399,7 @@ public:
|
||||
result.balance = bal.m_mine_trusted;
|
||||
result.unconfirmed_balance = bal.m_mine_untrusted_pending;
|
||||
result.immature_balance = bal.m_mine_immature;
|
||||
result.have_watch_only = haveWatchOnly();
|
||||
if (result.have_watch_only) {
|
||||
result.watch_only_balance = bal.m_watchonly_trusted;
|
||||
result.unconfirmed_watch_only_balance = bal.m_watchonly_untrusted_pending;
|
||||
result.immature_watch_only_balance = bal.m_watchonly_immature;
|
||||
}
|
||||
result.have_watch_only = false;
|
||||
return result;
|
||||
}
|
||||
bool tryGetBalances(WalletBalances& balances, uint256& block_hash) override
|
||||
@ -516,7 +503,6 @@ public:
|
||||
bool hasExternalSigner() override { return m_wallet->IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER); }
|
||||
bool privateKeysDisabled() override { return m_wallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS); }
|
||||
bool taprootEnabled() override {
|
||||
if (m_wallet->IsLegacy()) return false;
|
||||
auto spk_man = m_wallet->GetScriptPubKeyMan(OutputType::BECH32M, /*internal=*/false);
|
||||
return spk_man != nullptr;
|
||||
}
|
||||
@ -526,7 +512,6 @@ public:
|
||||
{
|
||||
RemoveWallet(m_context, m_wallet, /*load_on_start=*/false);
|
||||
}
|
||||
bool isLegacy() override { return m_wallet->IsLegacy(); }
|
||||
std::unique_ptr<Handler> handleUnload(UnloadFn fn) override
|
||||
{
|
||||
return MakeSignalHandler(m_wallet->NotifyUnload.connect(fn));
|
||||
@ -593,7 +578,7 @@ public:
|
||||
m_context.scheduler = &scheduler;
|
||||
return StartWallets(m_context);
|
||||
}
|
||||
void flush() override { return FlushWallets(m_context); }
|
||||
void flush() override {}
|
||||
void stop() override { return StopWallets(m_context); }
|
||||
void setMockTime(int64_t time) override { return SetMockTime(time); }
|
||||
void schedulerMockForward(std::chrono::seconds delta) override { Assert(m_context.scheduler)->MockForward(delta); }
|
||||
|
@ -159,20 +159,9 @@ void StartWallets(WalletContext& context)
|
||||
pwallet->postInitProcess();
|
||||
}
|
||||
|
||||
// Schedule periodic wallet flushes and tx rebroadcasts
|
||||
if (context.args->GetBoolArg("-flushwallet", DEFAULT_FLUSHWALLET)) {
|
||||
context.scheduler->scheduleEvery([&context] { MaybeCompactWalletDB(context); }, 500ms);
|
||||
}
|
||||
context.scheduler->scheduleEvery([&context] { MaybeResendWalletTxs(context); }, 1min);
|
||||
}
|
||||
|
||||
void FlushWallets(WalletContext& context)
|
||||
{
|
||||
for (const std::shared_ptr<CWallet>& pwallet : GetWallets(context)) {
|
||||
pwallet->Flush();
|
||||
}
|
||||
}
|
||||
|
||||
void StopWallets(WalletContext& context)
|
||||
{
|
||||
for (const std::shared_ptr<CWallet>& pwallet : GetWallets(context)) {
|
||||
|
@ -28,9 +28,6 @@ bool LoadWallets(WalletContext& context);
|
||||
//! Complete startup of wallets.
|
||||
void StartWallets(WalletContext& context);
|
||||
|
||||
//! Flush all wallets in preparation for shutdown.
|
||||
void FlushWallets(WalletContext& context);
|
||||
|
||||
//! Stop all wallets. Wallets will be flushed first.
|
||||
void StopWallets(WalletContext& context);
|
||||
|
||||
|
@ -699,7 +699,7 @@ void BerkeleyRODatabase::Open()
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<DatabaseBatch> BerkeleyRODatabase::MakeBatch(bool flush_on_close)
|
||||
std::unique_ptr<DatabaseBatch> BerkeleyRODatabase::MakeBatch()
|
||||
{
|
||||
return std::make_unique<BerkeleyROBatch>(*this);
|
||||
}
|
||||
|
@ -35,11 +35,6 @@ public:
|
||||
/** Open the database if it is not already opened. */
|
||||
void Open() override;
|
||||
|
||||
/** Indicate the a new database user has began using the database. Increments m_refcount */
|
||||
void AddRef() override {}
|
||||
/** Indicate that database user has stopped using the database and that it could be flushed or closed. Decrement m_refcount */
|
||||
void RemoveRef() override {}
|
||||
|
||||
/** Rewrite the entire database on disk, with the exception of key pszSkip if non-zero
|
||||
*/
|
||||
bool Rewrite(const char* pszSkip = nullptr) override { return false; }
|
||||
@ -48,20 +43,10 @@ public:
|
||||
*/
|
||||
bool Backup(const std::string& strDest) const override;
|
||||
|
||||
/** Make sure all changes are flushed to database file.
|
||||
*/
|
||||
void Flush() override {}
|
||||
/** Flush to the database file and close the database.
|
||||
* Also close the environment if no other databases are open in it.
|
||||
*/
|
||||
void Close() override {}
|
||||
/* flush the wallet passively (TRY_LOCK)
|
||||
ideal to be called periodically */
|
||||
bool PeriodicFlush() override { return false; }
|
||||
|
||||
void IncrementUpdateCounter() override {}
|
||||
|
||||
void ReloadDbEnv() override {}
|
||||
|
||||
/** Return path to main database file for logs and error messages. */
|
||||
std::string Filename() override { return fs::PathToString(m_filepath); }
|
||||
@ -69,7 +54,7 @@ public:
|
||||
std::string Format() override { return "bdb_ro"; }
|
||||
|
||||
/** Make a DatabaseBatch connected to this database */
|
||||
std::unique_ptr<DatabaseBatch> MakeBatch(bool flush_on_close = true) override;
|
||||
std::unique_ptr<DatabaseBatch> MakeBatch() override;
|
||||
};
|
||||
|
||||
class BerkeleyROCursor : public DatabaseCursor
|
||||
@ -107,7 +92,6 @@ public:
|
||||
BerkeleyROBatch(const BerkeleyROBatch&) = delete;
|
||||
BerkeleyROBatch& operator=(const BerkeleyROBatch&) = delete;
|
||||
|
||||
void Flush() override {}
|
||||
void Close() override {}
|
||||
|
||||
std::unique_ptr<DatabaseCursor> GetNewCursor() override { return std::make_unique<BerkeleyROCursor>(m_database); }
|
||||
|
@ -54,8 +54,6 @@ RPCHelpMan getnewaddress()
|
||||
std::optional<OutputType> parsed = ParseOutputType(request.params[1].get_str());
|
||||
if (!parsed) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[1].get_str()));
|
||||
} else if (parsed.value() == OutputType::BECH32M && pwallet->GetLegacyScriptPubKeyMan()) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Legacy wallets cannot provide bech32m addresses");
|
||||
}
|
||||
output_type = parsed.value();
|
||||
}
|
||||
@ -101,8 +99,6 @@ RPCHelpMan getrawchangeaddress()
|
||||
std::optional<OutputType> parsed = ParseOutputType(request.params[0].get_str());
|
||||
if (!parsed) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[0].get_str()));
|
||||
} else if (parsed.value() == OutputType::BECH32M && pwallet->GetLegacyScriptPubKeyMan()) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Legacy wallets cannot provide bech32m addresses");
|
||||
}
|
||||
output_type = parsed.value();
|
||||
}
|
||||
@ -215,124 +211,6 @@ RPCHelpMan listaddressgroupings()
|
||||
};
|
||||
}
|
||||
|
||||
RPCHelpMan addmultisigaddress()
|
||||
{
|
||||
return RPCHelpMan{"addmultisigaddress",
|
||||
"\nAdd an nrequired-to-sign multisignature address to the wallet. Requires a new wallet backup.\n"
|
||||
"Each key is a Bitcoin address or hex-encoded public key.\n"
|
||||
"This functionality is only intended for use with non-watchonly addresses.\n"
|
||||
"See `importaddress` for watchonly p2sh address support.\n"
|
||||
"If 'label' is specified, assign address to that label.\n"
|
||||
"Note: This command is only compatible with legacy wallets.\n",
|
||||
{
|
||||
{"nrequired", RPCArg::Type::NUM, RPCArg::Optional::NO, "The number of required signatures out of the n keys or addresses."},
|
||||
{"keys", RPCArg::Type::ARR, RPCArg::Optional::NO, "The bitcoin addresses or hex-encoded public keys",
|
||||
{
|
||||
{"key", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "bitcoin address or hex-encoded public key"},
|
||||
},
|
||||
},
|
||||
{"label", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "A label to assign the addresses to."},
|
||||
{"address_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -addresstype"}, "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
|
||||
},
|
||||
RPCResult{
|
||||
RPCResult::Type::OBJ, "", "",
|
||||
{
|
||||
{RPCResult::Type::STR, "address", "The value of the new multisig address"},
|
||||
{RPCResult::Type::STR_HEX, "redeemScript", "The string value of the hex-encoded redemption script"},
|
||||
{RPCResult::Type::STR, "descriptor", "The descriptor for this multisig"},
|
||||
{RPCResult::Type::ARR, "warnings", /*optional=*/true, "Any warnings resulting from the creation of this multisig",
|
||||
{
|
||||
{RPCResult::Type::STR, "", ""},
|
||||
}},
|
||||
}
|
||||
},
|
||||
RPCExamples{
|
||||
"\nAdd a multisig address from 2 addresses\n"
|
||||
+ HelpExampleCli("addmultisigaddress", "2 \"[\\\"" + EXAMPLE_ADDRESS[0] + "\\\",\\\"" + EXAMPLE_ADDRESS[1] + "\\\"]\"") +
|
||||
"\nAs a JSON-RPC call\n"
|
||||
+ HelpExampleRpc("addmultisigaddress", "2, \"[\\\"" + EXAMPLE_ADDRESS[0] + "\\\",\\\"" + EXAMPLE_ADDRESS[1] + "\\\"]\"")
|
||||
},
|
||||
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
||||
{
|
||||
std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
|
||||
if (!pwallet) return UniValue::VNULL;
|
||||
|
||||
LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet);
|
||||
|
||||
LOCK2(pwallet->cs_wallet, spk_man.cs_KeyStore);
|
||||
|
||||
const std::string label{LabelFromValue(request.params[2])};
|
||||
|
||||
int required = request.params[0].getInt<int>();
|
||||
|
||||
// Get the public keys
|
||||
const UniValue& keys_or_addrs = request.params[1].get_array();
|
||||
std::vector<CPubKey> pubkeys;
|
||||
for (unsigned int i = 0; i < keys_or_addrs.size(); ++i) {
|
||||
if (IsHex(keys_or_addrs[i].get_str()) && (keys_or_addrs[i].get_str().length() == 66 || keys_or_addrs[i].get_str().length() == 130)) {
|
||||
pubkeys.push_back(HexToPubKey(keys_or_addrs[i].get_str()));
|
||||
} else {
|
||||
pubkeys.push_back(AddrToPubKey(spk_man, keys_or_addrs[i].get_str()));
|
||||
}
|
||||
}
|
||||
|
||||
OutputType output_type = pwallet->m_default_address_type;
|
||||
if (!request.params[3].isNull()) {
|
||||
std::optional<OutputType> parsed = ParseOutputType(request.params[3].get_str());
|
||||
if (!parsed) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[3].get_str()));
|
||||
} else if (parsed.value() == OutputType::BECH32M) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Bech32m multisig addresses cannot be created with legacy wallets");
|
||||
}
|
||||
output_type = parsed.value();
|
||||
}
|
||||
|
||||
// Construct multisig scripts
|
||||
FlatSigningProvider provider;
|
||||
CScript inner;
|
||||
CTxDestination dest = AddAndGetMultisigDestination(required, pubkeys, output_type, provider, inner);
|
||||
|
||||
// Import scripts into the wallet
|
||||
for (const auto& [id, script] : provider.scripts) {
|
||||
// Due to a bug in the legacy wallet, the p2sh maximum script size limit is also imposed on 'p2sh-segwit' and 'bech32' redeem scripts.
|
||||
// Even when redeem scripts over MAX_SCRIPT_ELEMENT_SIZE bytes are valid for segwit output types, we don't want to
|
||||
// enable it because:
|
||||
// 1) It introduces a compatibility-breaking change requiring downgrade protection; older wallets would be unable to interact with these "new" legacy wallets.
|
||||
// 2) Considering the ongoing deprecation of the legacy spkm, this issue adds another good reason to transition towards descriptors.
|
||||
if (script.size() > MAX_SCRIPT_ELEMENT_SIZE) throw JSONRPCError(RPC_WALLET_ERROR, "Unsupported multisig script size for legacy wallet. Upgrade to descriptors to overcome this limitation for p2sh-segwit or bech32 scripts");
|
||||
|
||||
if (!spk_man.AddCScript(script)) {
|
||||
if (CScript inner_script; spk_man.GetCScript(CScriptID(script), inner_script)) {
|
||||
CHECK_NONFATAL(inner_script == script); // Nothing to add, script already contained by the wallet
|
||||
continue;
|
||||
}
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Error importing script into the wallet"));
|
||||
}
|
||||
}
|
||||
|
||||
// Store destination in the addressbook
|
||||
pwallet->SetAddressBook(dest, label, AddressPurpose::SEND);
|
||||
|
||||
// Make the descriptor
|
||||
std::unique_ptr<Descriptor> descriptor = InferDescriptor(GetScriptForDestination(dest), spk_man);
|
||||
|
||||
UniValue result(UniValue::VOBJ);
|
||||
result.pushKV("address", EncodeDestination(dest));
|
||||
result.pushKV("redeemScript", HexStr(inner));
|
||||
result.pushKV("descriptor", descriptor->ToString());
|
||||
|
||||
UniValue warnings(UniValue::VARR);
|
||||
if (descriptor->GetOutputType() != output_type) {
|
||||
// Only warns if the user has explicitly chosen an address type we cannot generate
|
||||
warnings.push_back("Unable to make chosen address type, please ensure no uncompressed public keys are present.");
|
||||
}
|
||||
PushWarnings(warnings, result);
|
||||
|
||||
return result;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
RPCHelpMan keypoolrefill()
|
||||
{
|
||||
return RPCHelpMan{"keypoolrefill",
|
||||
@ -351,10 +229,6 @@ RPCHelpMan keypoolrefill()
|
||||
std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
|
||||
if (!pwallet) return UniValue::VNULL;
|
||||
|
||||
if (pwallet->IsLegacy() && pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet");
|
||||
}
|
||||
|
||||
LOCK(pwallet->cs_wallet);
|
||||
|
||||
// 0 is interpreted by TopUpKeyPool() as the default keypool size given by -keypool
|
||||
@ -377,38 +251,6 @@ RPCHelpMan keypoolrefill()
|
||||
};
|
||||
}
|
||||
|
||||
RPCHelpMan newkeypool()
|
||||
{
|
||||
return RPCHelpMan{"newkeypool",
|
||||
"\nEntirely clears and refills the keypool.\n"
|
||||
"WARNING: On non-HD wallets, this will require a new backup immediately, to include the new keys.\n"
|
||||
"When restoring a backup of an HD wallet created before the newkeypool command is run, funds received to\n"
|
||||
"new addresses may not appear automatically. They have not been lost, but the wallet may not find them.\n"
|
||||
"This can be fixed by running the newkeypool command on the backup and then rescanning, so the wallet\n"
|
||||
"re-generates the required keys." +
|
||||
HELP_REQUIRING_PASSPHRASE,
|
||||
{},
|
||||
RPCResult{RPCResult::Type::NONE, "", ""},
|
||||
RPCExamples{
|
||||
HelpExampleCli("newkeypool", "")
|
||||
+ HelpExampleRpc("newkeypool", "")
|
||||
},
|
||||
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
||||
{
|
||||
std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
|
||||
if (!pwallet) return UniValue::VNULL;
|
||||
|
||||
LOCK(pwallet->cs_wallet);
|
||||
|
||||
LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet, true);
|
||||
spk_man.NewKeyPool();
|
||||
|
||||
return UniValue::VNULL;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
class DescribeWalletAddressVisitor
|
||||
{
|
||||
public:
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -440,12 +440,6 @@ RPCHelpMan getbalances()
|
||||
{RPCResult::Type::STR_AMOUNT, "immature", "balance from immature coinbase outputs"},
|
||||
{RPCResult::Type::STR_AMOUNT, "used", /*optional=*/true, "(only present if avoid_reuse is set) balance from coins sent to addresses that were previously spent from (potentially privacy violating)"},
|
||||
}},
|
||||
{RPCResult::Type::OBJ, "watchonly", /*optional=*/true, "watchonly balances (not present if wallet does not watch anything)",
|
||||
{
|
||||
{RPCResult::Type::STR_AMOUNT, "trusted", "trusted balance (outputs created by the wallet or confirmed outputs)"},
|
||||
{RPCResult::Type::STR_AMOUNT, "untrusted_pending", "untrusted pending balance (outputs created by others that are in the mempool)"},
|
||||
{RPCResult::Type::STR_AMOUNT, "immature", "balance from immature coinbase outputs"},
|
||||
}},
|
||||
RESULT_LAST_PROCESSED_BLOCK,
|
||||
}
|
||||
},
|
||||
@ -479,15 +473,6 @@ RPCHelpMan getbalances()
|
||||
}
|
||||
balances.pushKV("mine", std::move(balances_mine));
|
||||
}
|
||||
auto spk_man = wallet.GetLegacyScriptPubKeyMan();
|
||||
if (spk_man && spk_man->HaveWatchOnly()) {
|
||||
UniValue balances_watchonly{UniValue::VOBJ};
|
||||
balances_watchonly.pushKV("trusted", ValueFromAmount(bal.m_watchonly_trusted));
|
||||
balances_watchonly.pushKV("untrusted_pending", ValueFromAmount(bal.m_watchonly_untrusted_pending));
|
||||
balances_watchonly.pushKV("immature", ValueFromAmount(bal.m_watchonly_immature));
|
||||
balances.pushKV("watchonly", std::move(balances_watchonly));
|
||||
}
|
||||
|
||||
AppendLastProcessedBlock(balances, wallet);
|
||||
return balances;
|
||||
},
|
||||
|
@ -752,7 +752,7 @@ RPCHelpMan fundrawtransaction()
|
||||
"All existing inputs must either have their previous output transaction be in the wallet\n"
|
||||
"or be in the UTXO set. Solving data must be provided for non-wallet inputs.\n"
|
||||
"Note that all inputs selected must be of standard form and P2SH scripts must be\n"
|
||||
"in the wallet using importaddress or addmultisigaddress (to calculate fees).\n"
|
||||
"in the wallet using importdescriptors (to calculate fees).\n"
|
||||
"You can see whether this is the case by checking the \"solvable\" field in the listunspent output.\n"
|
||||
"Only pay-to-pubkey, multisig, and P2SH versions thereof are currently supported for watch-only\n",
|
||||
{
|
||||
@ -771,7 +771,7 @@ RPCHelpMan fundrawtransaction()
|
||||
{"change_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -changetype"}, "The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", \"bech32\", and \"bech32m\"."},
|
||||
{"includeWatching", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Also select inputs which are watch only.\n"
|
||||
"Only solvable inputs can be used. Watch-only destinations are solvable if the public key and/or output script was imported,\n"
|
||||
"e.g. with 'importpubkey' or 'importmulti' with the 'pubkeys' or 'desc' field."},
|
||||
"e.g. with 'importdescriptors'."},
|
||||
{"lockUnspents", RPCArg::Type::BOOL, RPCArg::Default{false}, "Lock selected unspent outputs"},
|
||||
{"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/vB."},
|
||||
{"feeRate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_UNIT + "/kvB."},
|
||||
@ -1231,7 +1231,7 @@ RPCHelpMan send()
|
||||
{"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/vB.", RPCArgOptions{.also_positional = true}},
|
||||
{"include_watching", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Also select inputs which are watch only.\n"
|
||||
"Only solvable inputs can be used. Watch-only destinations are solvable if the public key and/or output script was imported,\n"
|
||||
"e.g. with 'importpubkey' or 'importmulti' with the 'pubkeys' or 'desc' field."},
|
||||
"e.g. with 'importdescriptors'."},
|
||||
{"inputs", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "Specify inputs instead of adding them automatically.",
|
||||
{
|
||||
{"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "", {
|
||||
@ -1351,7 +1351,7 @@ RPCHelpMan sendall()
|
||||
{"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/vB.", RPCArgOptions{.also_positional = true}},
|
||||
{"include_watching", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Also select inputs which are watch-only.\n"
|
||||
"Only solvable inputs can be used. Watch-only destinations are solvable if the public key and/or output script was imported,\n"
|
||||
"e.g. with 'importpubkey' or 'importmulti' with the 'pubkeys' or 'desc' field."},
|
||||
"e.g. with 'importdescriptors'."},
|
||||
{"inputs", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "Use exactly the specified inputs to build the transaction. Specifying inputs is incompatible with the send_max, minconf, and maxconf options.",
|
||||
{
|
||||
{"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
|
||||
|
@ -946,13 +946,13 @@ RPCHelpMan rescanblockchain()
|
||||
RPCHelpMan abortrescan()
|
||||
{
|
||||
return RPCHelpMan{"abortrescan",
|
||||
"\nStops current wallet rescan triggered by an RPC call, e.g. by an importprivkey call.\n"
|
||||
"\nStops current wallet rescan triggered by an RPC call, e.g. by an rescanblockchain call.\n"
|
||||
"Note: Use \"getwalletinfo\" to query the scanning progress.\n",
|
||||
{},
|
||||
RPCResult{RPCResult::Type::BOOL, "", "Whether the abort was successful"},
|
||||
RPCExamples{
|
||||
"\nImport a private key\n"
|
||||
+ HelpExampleCli("importprivkey", "\"mykey\"") +
|
||||
+ HelpExampleCli("rescanblockchain", "") +
|
||||
"\nAbort the running wallet rescan\n"
|
||||
+ HelpExampleCli("abortrescan", "") +
|
||||
"\nAs a JSON-RPC call\n"
|
||||
|
@ -94,28 +94,6 @@ WalletContext& EnsureWalletContext(const std::any& context)
|
||||
return *wallet_context;
|
||||
}
|
||||
|
||||
// also_create should only be set to true only when the RPC is expected to add things to a blank wallet and make it no longer blank
|
||||
LegacyScriptPubKeyMan& EnsureLegacyScriptPubKeyMan(CWallet& wallet, bool also_create)
|
||||
{
|
||||
LegacyScriptPubKeyMan* spk_man = wallet.GetLegacyScriptPubKeyMan();
|
||||
if (!spk_man && also_create) {
|
||||
spk_man = wallet.GetOrCreateLegacyScriptPubKeyMan();
|
||||
}
|
||||
if (!spk_man) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Only legacy wallets are supported by this command");
|
||||
}
|
||||
return *spk_man;
|
||||
}
|
||||
|
||||
const LegacyScriptPubKeyMan& EnsureConstLegacyScriptPubKeyMan(const CWallet& wallet)
|
||||
{
|
||||
const LegacyScriptPubKeyMan* spk_man = wallet.GetLegacyScriptPubKeyMan();
|
||||
if (!spk_man) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Only legacy wallets are supported by this command");
|
||||
}
|
||||
return *spk_man;
|
||||
}
|
||||
|
||||
std::string LabelFromValue(const UniValue& value)
|
||||
{
|
||||
static const std::string empty_string;
|
||||
|
@ -41,8 +41,6 @@ bool GetWalletNameFromJSONRPCRequest(const JSONRPCRequest& request, std::string&
|
||||
|
||||
void EnsureWalletIsUnlocked(const CWallet&);
|
||||
WalletContext& EnsureWalletContext(const std::any& context);
|
||||
LegacyScriptPubKeyMan& EnsureLegacyScriptPubKeyMan(CWallet& wallet, bool also_create = false);
|
||||
const LegacyScriptPubKeyMan& EnsureConstLegacyScriptPubKeyMan(const CWallet& wallet);
|
||||
|
||||
bool GetAvoidReuseFlag(const CWallet& wallet, const UniValue& param);
|
||||
bool ParseIncludeWatchonly(const UniValue& include_watchonly, const CWallet& wallet);
|
||||
|
@ -50,7 +50,7 @@ static RPCHelpMan getwalletinfo()
|
||||
{
|
||||
{RPCResult::Type::STR, "walletname", "the wallet name"},
|
||||
{RPCResult::Type::NUM, "walletversion", "the wallet version"},
|
||||
{RPCResult::Type::STR, "format", "the database format (bdb or sqlite)"},
|
||||
{RPCResult::Type::STR, "format", "the database format (only sqlite)"},
|
||||
{RPCResult::Type::STR_AMOUNT, "balance", "DEPRECATED. Identical to getbalances().mine.trusted"},
|
||||
{RPCResult::Type::STR_AMOUNT, "unconfirmed_balance", "DEPRECATED. Identical to getbalances().mine.untrusted_pending"},
|
||||
{RPCResult::Type::STR_AMOUNT, "immature_balance", "DEPRECATED. Identical to getbalances().mine.immature"},
|
||||
@ -60,7 +60,6 @@ static RPCHelpMan getwalletinfo()
|
||||
{RPCResult::Type::NUM, "keypoolsize_hd_internal", /*optional=*/true, "how many new keys are pre-generated for internal use (used for change outputs, only appears if the wallet is using this feature, otherwise external keys are used)"},
|
||||
{RPCResult::Type::NUM_TIME, "unlocked_until", /*optional=*/true, "the " + UNIX_EPOCH_TIME + " until which the wallet is unlocked for transfers, or 0 if the wallet is locked (only present for passphrase-encrypted wallets)"},
|
||||
{RPCResult::Type::STR_AMOUNT, "paytxfee", "the transaction fee configuration, set in " + CURRENCY_UNIT + "/kvB"},
|
||||
{RPCResult::Type::STR_HEX, "hdseedid", /*optional=*/true, "the Hash160 of the HD seed (only present when HD is enabled)"},
|
||||
{RPCResult::Type::BOOL, "private_keys_enabled", "false if privatekeys are disabled for this wallet (enforced watch-only wallet)"},
|
||||
{RPCResult::Type::BOOL, "avoid_reuse", "whether this wallet tracks clean/dirty coins in terms of reuse"},
|
||||
{RPCResult::Type::OBJ, "scanning", "current scanning details, or false if no scan is in progress",
|
||||
@ -107,14 +106,6 @@ static RPCHelpMan getwalletinfo()
|
||||
}
|
||||
obj.pushKV("keypoolsize", (int64_t)kpExternalSize);
|
||||
|
||||
LegacyScriptPubKeyMan* spk_man = pwallet->GetLegacyScriptPubKeyMan();
|
||||
if (spk_man) {
|
||||
CKeyID seed_id = spk_man->GetHDChain().seed_id;
|
||||
if (!seed_id.IsNull()) {
|
||||
obj.pushKV("hdseedid", seed_id.GetHex());
|
||||
}
|
||||
}
|
||||
|
||||
if (pwallet->CanSupportFeature(FEATURE_HD_SPLIT)) {
|
||||
obj.pushKV("keypoolsize_hd_internal", (int64_t)(pwallet->GetKeyPoolSize() - kpExternalSize));
|
||||
}
|
||||
@ -352,7 +343,7 @@ static RPCHelpMan createwallet()
|
||||
{
|
||||
{"wallet_name", RPCArg::Type::STR, RPCArg::Optional::NO, "The name for the new wallet. If this is a path, the wallet will be created at the path location."},
|
||||
{"disable_private_keys", RPCArg::Type::BOOL, RPCArg::Default{false}, "Disable the possibility of private keys (only watchonlys are possible in this mode)."},
|
||||
{"blank", RPCArg::Type::BOOL, RPCArg::Default{false}, "Create a blank wallet. A blank wallet has no keys or HD seed. One can be set using sethdseed."},
|
||||
{"blank", RPCArg::Type::BOOL, RPCArg::Default{false}, "Create a blank wallet. A blank wallet has no keys."},
|
||||
{"passphrase", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Encrypt the wallet with this passphrase."},
|
||||
{"avoid_reuse", RPCArg::Type::BOOL, RPCArg::Default{false}, "Keep track of coin reuse, and treat dirty and clean coins differently with privacy considerations in mind."},
|
||||
{"descriptors", RPCArg::Type::BOOL, RPCArg::Default{true}, "If set, must be \"true\""},
|
||||
@ -497,77 +488,6 @@ static RPCHelpMan unloadwallet()
|
||||
};
|
||||
}
|
||||
|
||||
static RPCHelpMan sethdseed()
|
||||
{
|
||||
return RPCHelpMan{"sethdseed",
|
||||
"\nSet or generate a new HD wallet seed. Non-HD wallets will not be upgraded to being a HD wallet. Wallets that are already\n"
|
||||
"HD will have a new HD seed set so that new keys added to the keypool will be derived from this new seed.\n"
|
||||
"\nNote that you will need to MAKE A NEW BACKUP of your wallet after setting the HD wallet seed." + HELP_REQUIRING_PASSPHRASE +
|
||||
"Note: This command is only compatible with legacy wallets.\n",
|
||||
{
|
||||
{"newkeypool", RPCArg::Type::BOOL, RPCArg::Default{true}, "Whether to flush old unused addresses, including change addresses, from the keypool and regenerate it.\n"
|
||||
"If true, the next address from getnewaddress and change address from getrawchangeaddress will be from this new seed.\n"
|
||||
"If false, addresses (including change addresses if the wallet already had HD Chain Split enabled) from the existing\n"
|
||||
"keypool will be used until it has been depleted."},
|
||||
{"seed", RPCArg::Type::STR, RPCArg::DefaultHint{"random seed"}, "The WIF private key to use as the new HD seed.\n"
|
||||
"The seed value can be retrieved using the dumpwallet command. It is the private key marked hdseed=1"},
|
||||
},
|
||||
RPCResult{RPCResult::Type::NONE, "", ""},
|
||||
RPCExamples{
|
||||
HelpExampleCli("sethdseed", "")
|
||||
+ HelpExampleCli("sethdseed", "false")
|
||||
+ HelpExampleCli("sethdseed", "true \"wifkey\"")
|
||||
+ HelpExampleRpc("sethdseed", "true, \"wifkey\"")
|
||||
},
|
||||
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
||||
{
|
||||
std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
|
||||
if (!pwallet) return UniValue::VNULL;
|
||||
|
||||
LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet, true);
|
||||
|
||||
if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot set a HD seed to a wallet with private keys disabled");
|
||||
}
|
||||
|
||||
LOCK2(pwallet->cs_wallet, spk_man.cs_KeyStore);
|
||||
|
||||
// Do not do anything to non-HD wallets
|
||||
if (!pwallet->CanSupportFeature(FEATURE_HD)) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot set an HD seed on a non-HD wallet. Use the upgradewallet RPC in order to upgrade a non-HD wallet to HD");
|
||||
}
|
||||
|
||||
EnsureWalletIsUnlocked(*pwallet);
|
||||
|
||||
bool flush_key_pool = true;
|
||||
if (!request.params[0].isNull()) {
|
||||
flush_key_pool = request.params[0].get_bool();
|
||||
}
|
||||
|
||||
CPubKey master_pub_key;
|
||||
if (request.params[1].isNull()) {
|
||||
master_pub_key = spk_man.GenerateNewSeed();
|
||||
} else {
|
||||
CKey key = DecodeSecret(request.params[1].get_str());
|
||||
if (!key.IsValid()) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key");
|
||||
}
|
||||
|
||||
if (HaveKey(spk_man, key)) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Already have this key (either as an HD seed or as a loose private key)");
|
||||
}
|
||||
|
||||
master_pub_key = spk_man.DeriveNewSeed(key);
|
||||
}
|
||||
|
||||
spk_man.SetHDSeed(master_pub_key);
|
||||
if (flush_key_pool) spk_man.NewKeyPool();
|
||||
|
||||
return UniValue::VNULL;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
static RPCHelpMan upgradewallet()
|
||||
{
|
||||
return RPCHelpMan{"upgradewallet",
|
||||
@ -1024,9 +944,7 @@ RPCHelpMan getnewaddress();
|
||||
RPCHelpMan getrawchangeaddress();
|
||||
RPCHelpMan setlabel();
|
||||
RPCHelpMan listaddressgroupings();
|
||||
RPCHelpMan addmultisigaddress();
|
||||
RPCHelpMan keypoolrefill();
|
||||
RPCHelpMan newkeypool();
|
||||
RPCHelpMan getaddressesbylabel();
|
||||
RPCHelpMan listlabels();
|
||||
#ifdef ENABLE_EXTERNAL_SIGNER
|
||||
@ -1034,15 +952,8 @@ RPCHelpMan walletdisplayaddress();
|
||||
#endif // ENABLE_EXTERNAL_SIGNER
|
||||
|
||||
// backup
|
||||
RPCHelpMan dumpprivkey();
|
||||
RPCHelpMan importprivkey();
|
||||
RPCHelpMan importaddress();
|
||||
RPCHelpMan importpubkey();
|
||||
RPCHelpMan dumpwallet();
|
||||
RPCHelpMan importwallet();
|
||||
RPCHelpMan importprunedfunds();
|
||||
RPCHelpMan removeprunedfunds();
|
||||
RPCHelpMan importmulti();
|
||||
RPCHelpMan importdescriptors();
|
||||
RPCHelpMan listdescriptors();
|
||||
RPCHelpMan backupwallet();
|
||||
@ -1096,15 +1007,12 @@ std::span<const CRPCCommand> GetWalletRPCCommands()
|
||||
{"rawtransactions", &fundrawtransaction},
|
||||
{"wallet", &abandontransaction},
|
||||
{"wallet", &abortrescan},
|
||||
{"wallet", &addmultisigaddress},
|
||||
{"wallet", &backupwallet},
|
||||
{"wallet", &bumpfee},
|
||||
{"wallet", &psbtbumpfee},
|
||||
{"wallet", &createwallet},
|
||||
{"wallet", &createwalletdescriptor},
|
||||
{"wallet", &restorewallet},
|
||||
{"wallet", &dumpprivkey},
|
||||
{"wallet", &dumpwallet},
|
||||
{"wallet", &encryptwallet},
|
||||
{"wallet", &getaddressesbylabel},
|
||||
{"wallet", &getaddressinfo},
|
||||
@ -1118,13 +1026,8 @@ std::span<const CRPCCommand> GetWalletRPCCommands()
|
||||
{"wallet", &getunconfirmedbalance},
|
||||
{"wallet", &getbalances},
|
||||
{"wallet", &getwalletinfo},
|
||||
{"wallet", &importaddress},
|
||||
{"wallet", &importdescriptors},
|
||||
{"wallet", &importmulti},
|
||||
{"wallet", &importprivkey},
|
||||
{"wallet", &importprunedfunds},
|
||||
{"wallet", &importpubkey},
|
||||
{"wallet", &importwallet},
|
||||
{"wallet", &keypoolrefill},
|
||||
{"wallet", &listaddressgroupings},
|
||||
{"wallet", &listdescriptors},
|
||||
@ -1140,13 +1043,11 @@ std::span<const CRPCCommand> GetWalletRPCCommands()
|
||||
{"wallet", &loadwallet},
|
||||
{"wallet", &lockunspent},
|
||||
{"wallet", &migratewallet},
|
||||
{"wallet", &newkeypool},
|
||||
{"wallet", &removeprunedfunds},
|
||||
{"wallet", &rescanblockchain},
|
||||
{"wallet", &send},
|
||||
{"wallet", &sendmany},
|
||||
{"wallet", &sendtoaddress},
|
||||
{"wallet", &sethdseed},
|
||||
{"wallet", &setlabel},
|
||||
{"wallet", &settxfee},
|
||||
{"wallet", &setwalletflag},
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -65,99 +65,6 @@ static const unsigned int DEFAULT_KEYPOOL_SIZE = 1000;
|
||||
|
||||
std::vector<CKeyID> GetAffectedKeys(const CScript& spk, const SigningProvider& provider);
|
||||
|
||||
/** A key from a CWallet's keypool
|
||||
*
|
||||
* The wallet holds one (for pre HD-split wallets) or several keypools. These
|
||||
* are sets of keys that have not yet been used to provide addresses or receive
|
||||
* change.
|
||||
*
|
||||
* The Bitcoin Core wallet was originally a collection of unrelated private
|
||||
* keys with their associated addresses. If a non-HD wallet generated a
|
||||
* key/address, gave that address out and then restored a backup from before
|
||||
* that key's generation, then any funds sent to that address would be
|
||||
* lost definitively.
|
||||
*
|
||||
* The keypool was implemented to avoid this scenario (commit: 10384941). The
|
||||
* wallet would generate a set of keys (100 by default). When a new public key
|
||||
* was required, either to give out as an address or to use in a change output,
|
||||
* it would be drawn from the keypool. The keypool would then be topped up to
|
||||
* maintain 100 keys. This ensured that as long as the wallet hadn't used more
|
||||
* than 100 keys since the previous backup, all funds would be safe, since a
|
||||
* restored wallet would be able to scan for all owned addresses.
|
||||
*
|
||||
* A keypool also allowed encrypted wallets to give out addresses without
|
||||
* having to be decrypted to generate a new private key.
|
||||
*
|
||||
* With the introduction of HD wallets (commit: f1902510), the keypool
|
||||
* essentially became an address look-ahead pool. Restoring old backups can no
|
||||
* longer definitively lose funds as long as the addresses used were from the
|
||||
* wallet's HD seed (since all private keys can be rederived from the seed).
|
||||
* However, if many addresses were used since the backup, then the wallet may
|
||||
* not know how far ahead in the HD chain to look for its addresses. The
|
||||
* keypool is used to implement a 'gap limit'. The keypool maintains a set of
|
||||
* keys (by default 1000) ahead of the last used key and scans for the
|
||||
* addresses of those keys. This avoids the risk of not seeing transactions
|
||||
* involving the wallet's addresses, or of re-using the same address.
|
||||
* In the unlikely case where none of the addresses in the `gap limit` are
|
||||
* used on-chain, the look-ahead will not be incremented to keep
|
||||
* a constant size and addresses beyond this range will not be detected by an
|
||||
* old backup. For this reason, it is not recommended to decrease keypool size
|
||||
* lower than default value.
|
||||
*
|
||||
* The HD-split wallet feature added a second keypool (commit: 02592f4c). There
|
||||
* is an external keypool (for addresses to hand out) and an internal keypool
|
||||
* (for change addresses).
|
||||
*
|
||||
* Keypool keys are stored in the wallet/keystore's keymap. The keypool data is
|
||||
* stored as sets of indexes in the wallet (setInternalKeyPool,
|
||||
* setExternalKeyPool and set_pre_split_keypool), and a map from the key to the
|
||||
* index (m_pool_key_to_index). The CKeyPool object is used to
|
||||
* serialize/deserialize the pool data to/from the database.
|
||||
*/
|
||||
class CKeyPool
|
||||
{
|
||||
public:
|
||||
//! The time at which the key was generated. Set in AddKeypoolPubKeyWithDB
|
||||
int64_t nTime;
|
||||
//! The public key
|
||||
CPubKey vchPubKey;
|
||||
//! Whether this keypool entry is in the internal keypool (for change outputs)
|
||||
bool fInternal;
|
||||
//! Whether this key was generated for a keypool before the wallet was upgraded to HD-split
|
||||
bool m_pre_split;
|
||||
|
||||
CKeyPool();
|
||||
CKeyPool(const CPubKey& vchPubKeyIn, bool internalIn);
|
||||
|
||||
template<typename Stream>
|
||||
void Serialize(Stream& s) const
|
||||
{
|
||||
s << int{259900}; // Unused field, writes the highest client version ever written
|
||||
s << nTime << vchPubKey << fInternal << m_pre_split;
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
void Unserialize(Stream& s)
|
||||
{
|
||||
s >> int{}; // Discard unused field
|
||||
s >> nTime >> vchPubKey;
|
||||
try {
|
||||
s >> fInternal;
|
||||
} catch (std::ios_base::failure&) {
|
||||
/* flag as external address if we can't read the internal boolean
|
||||
(this will be the case for any wallet before the HD chain split version) */
|
||||
fInternal = false;
|
||||
}
|
||||
try {
|
||||
s >> m_pre_split;
|
||||
} catch (std::ios_base::failure&) {
|
||||
/* flag as postsplit address if we can't read the m_pre_split boolean
|
||||
(this will be the case for any wallet that upgrades to HD chain split) */
|
||||
m_pre_split = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct WalletDestination
|
||||
{
|
||||
CTxDestination dest;
|
||||
@ -186,7 +93,7 @@ public:
|
||||
virtual bool CheckDecryptionKey(const CKeyingMaterial& master_key) { return false; }
|
||||
virtual bool Encrypt(const CKeyingMaterial& master_key, WalletBatch* batch) { return false; }
|
||||
|
||||
virtual util::Result<CTxDestination> GetReservedDestination(const OutputType type, bool internal, int64_t& index, CKeyPool& keypool) { return util::Error{Untranslated("Not supported")}; }
|
||||
virtual util::Result<CTxDestination> GetReservedDestination(const OutputType type, bool internal, int64_t& index) { return util::Error{Untranslated("Not supported")}; }
|
||||
virtual void KeepDestination(int64_t index, const OutputType& type) {}
|
||||
virtual void ReturnDestination(int64_t index, bool internal, const CTxDestination& addr) {}
|
||||
|
||||
@ -277,13 +184,11 @@ static const std::unordered_set<OutputType> LEGACY_OUTPUT_TYPES {
|
||||
OutputType::BECH32,
|
||||
};
|
||||
|
||||
class DescriptorScriptPubKeyMan;
|
||||
|
||||
// Manages the data for a LegacyScriptPubKeyMan.
|
||||
// This is the minimum necessary to load a legacy wallet so that it can be migrated.
|
||||
class LegacyDataSPKM : public ScriptPubKeyMan, public FillableSigningProvider
|
||||
{
|
||||
protected:
|
||||
private:
|
||||
using WatchOnlySet = std::set<CScript>;
|
||||
using WatchKeyMap = std::map<CKeyID, CPubKey>;
|
||||
using CryptedKeyMap = std::map<CKeyID, std::pair<CPubKey, std::vector<unsigned char>>>;
|
||||
@ -307,6 +212,9 @@ protected:
|
||||
// It may include scripts that are invalid or not actually watched by this LegacyDataSPKM.
|
||||
// Used only in migration.
|
||||
std::unordered_set<CScript, SaltedSipHasher> GetCandidateScriptPubKeys() const;
|
||||
|
||||
isminetype IsMine(const CScript& script) const override;
|
||||
bool CanProvide(const CScript& script, SignatureData& sigdata) override;
|
||||
public:
|
||||
using ScriptPubKeyMan::ScriptPubKeyMan;
|
||||
|
||||
@ -321,9 +229,6 @@ public:
|
||||
std::unordered_set<CScript, SaltedSipHasher> GetScriptPubKeys() const override;
|
||||
std::unique_ptr<SigningProvider> GetSolvingProvider(const CScript& script) const override;
|
||||
uint256 GetID() const override { return uint256::ONE; }
|
||||
// TODO: Remove IsMine when deleting LegacyScriptPubKeyMan
|
||||
isminetype IsMine(const CScript& script) const override;
|
||||
bool CanProvide(const CScript& script, SignatureData& sigdata) override;
|
||||
|
||||
// FillableSigningProvider overrides
|
||||
bool HaveKey(const CKeyID &address) const override;
|
||||
@ -331,12 +236,6 @@ public:
|
||||
bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const override;
|
||||
bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override;
|
||||
|
||||
std::set<int64_t> setInternalKeyPool GUARDED_BY(cs_KeyStore);
|
||||
std::set<int64_t> setExternalKeyPool GUARDED_BY(cs_KeyStore);
|
||||
std::set<int64_t> set_pre_split_keypool GUARDED_BY(cs_KeyStore);
|
||||
int64_t m_max_keypool_index GUARDED_BY(cs_KeyStore) = 0;
|
||||
std::map<CKeyID, int64_t> m_pool_key_to_index;
|
||||
|
||||
//! Load metadata (used by LoadWallet)
|
||||
virtual void LoadKeyMetadata(const CKeyID& keyID, const CKeyMetadata &metadata);
|
||||
virtual void LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata &metadata);
|
||||
@ -357,8 +256,6 @@ public:
|
||||
void LoadHDChain(const CHDChain& chain);
|
||||
void AddInactiveHDChain(const CHDChain& chain);
|
||||
const CHDChain& GetHDChain() const { return m_hd_chain; }
|
||||
//! Load a keypool entry
|
||||
void LoadKeyPool(int64_t nIndex, const CKeyPool &keypool);
|
||||
|
||||
//! Fetches a pubkey from mapWatchKeys if it exists there
|
||||
bool GetWatchPubKey(const CKeyID &address, CPubKey &pubkey_out) const;
|
||||
@ -377,199 +274,6 @@ public:
|
||||
bool DeleteRecordsWithDB(WalletBatch& batch);
|
||||
};
|
||||
|
||||
// Implements the full legacy wallet behavior
|
||||
class LegacyScriptPubKeyMan : public LegacyDataSPKM
|
||||
{
|
||||
private:
|
||||
WalletBatch *encrypted_batch GUARDED_BY(cs_KeyStore) = nullptr;
|
||||
|
||||
// By default, do not scan any block until keys/scripts are generated/imported
|
||||
int64_t nTimeFirstKey GUARDED_BY(cs_KeyStore) = UNKNOWN_TIME;
|
||||
|
||||
//! Number of pre-generated keys/scripts (part of the look-ahead process, used to detect payments)
|
||||
int64_t m_keypool_size GUARDED_BY(cs_KeyStore){DEFAULT_KEYPOOL_SIZE};
|
||||
|
||||
bool AddKeyPubKeyInner(const CKey& key, const CPubKey &pubkey) override;
|
||||
|
||||
/**
|
||||
* Private version of AddWatchOnly method which does not accept a
|
||||
* timestamp, and which will reset the wallet's nTimeFirstKey value to 1 if
|
||||
* the watch key did not previously have a timestamp associated with it.
|
||||
* Because this is an inherited virtual method, it is accessible despite
|
||||
* being marked private, but it is marked private anyway to encourage use
|
||||
* of the other AddWatchOnly which accepts a timestamp and sets
|
||||
* nTimeFirstKey more intelligently for more efficient rescans.
|
||||
*/
|
||||
bool AddWatchOnly(const CScript& dest) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
|
||||
bool AddWatchOnlyWithDB(WalletBatch &batch, const CScript& dest) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
|
||||
//! Adds a watch-only address to the store, and saves it to disk.
|
||||
bool AddWatchOnlyWithDB(WalletBatch &batch, const CScript& dest, int64_t create_time) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
|
||||
|
||||
//! Adds a key to the store, and saves it to disk.
|
||||
bool AddKeyPubKeyWithDB(WalletBatch &batch,const CKey& key, const CPubKey &pubkey) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
|
||||
|
||||
void AddKeypoolPubkeyWithDB(const CPubKey& pubkey, const bool internal, WalletBatch& batch);
|
||||
|
||||
//! Adds a script to the store and saves it to disk
|
||||
bool AddCScriptWithDB(WalletBatch& batch, const CScript& script);
|
||||
|
||||
/** Add a KeyOriginInfo to the wallet */
|
||||
bool AddKeyOriginWithDB(WalletBatch& batch, const CPubKey& pubkey, const KeyOriginInfo& info);
|
||||
|
||||
/* HD derive new child key (on internal or external chain) */
|
||||
void DeriveNewChildKey(WalletBatch& batch, CKeyMetadata& metadata, CKey& secret, CHDChain& hd_chain, bool internal = false) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
|
||||
|
||||
// Tracks keypool indexes to CKeyIDs of keys that have been taken out of the keypool but may be returned to it
|
||||
std::map<int64_t, CKeyID> m_index_to_reserved_key;
|
||||
|
||||
//! Fetches a key from the keypool
|
||||
bool GetKeyFromPool(CPubKey &key, const OutputType type);
|
||||
|
||||
/**
|
||||
* Reserves a key from the keypool and sets nIndex to its index
|
||||
*
|
||||
* @param[out] nIndex the index of the key in keypool
|
||||
* @param[out] keypool the keypool the key was drawn from, which could be the
|
||||
* the pre-split pool if present, or the internal or external pool
|
||||
* @param fRequestedInternal true if the caller would like the key drawn
|
||||
* from the internal keypool, false if external is preferred
|
||||
*
|
||||
* @return true if succeeded, false if failed due to empty keypool
|
||||
* @throws std::runtime_error if keypool read failed, key was invalid,
|
||||
* was not found in the wallet, or was misclassified in the internal
|
||||
* or external keypool
|
||||
*/
|
||||
bool ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRequestedInternal);
|
||||
|
||||
/**
|
||||
* Like TopUp() but adds keys for inactive HD chains.
|
||||
* Ensures that there are at least -keypool number of keys derived after the given index.
|
||||
*
|
||||
* @param seed_id the CKeyID for the HD seed.
|
||||
* @param index the index to start generating keys from
|
||||
* @param internal whether the internal chain should be used. true for internal chain, false for external chain.
|
||||
*
|
||||
* @return true if seed was found and keys were derived. false if unable to derive seeds
|
||||
*/
|
||||
bool TopUpInactiveHDChain(const CKeyID seed_id, int64_t index, bool internal);
|
||||
|
||||
bool TopUpChain(WalletBatch& batch, CHDChain& chain, unsigned int size);
|
||||
public:
|
||||
LegacyScriptPubKeyMan(WalletStorage& storage, int64_t keypool_size) : LegacyDataSPKM(storage), m_keypool_size(keypool_size) {}
|
||||
|
||||
util::Result<CTxDestination> GetNewDestination(const OutputType type) override;
|
||||
|
||||
bool Encrypt(const CKeyingMaterial& master_key, WalletBatch* batch) override;
|
||||
|
||||
util::Result<CTxDestination> GetReservedDestination(const OutputType type, bool internal, int64_t& index, CKeyPool& keypool) override;
|
||||
void KeepDestination(int64_t index, const OutputType& type) override;
|
||||
void ReturnDestination(int64_t index, bool internal, const CTxDestination&) override;
|
||||
|
||||
bool TopUp(unsigned int size = 0) override;
|
||||
|
||||
std::vector<WalletDestination> MarkUnusedAddresses(const CScript& script) override;
|
||||
|
||||
//! Upgrade stored CKeyMetadata objects to store key origin info as KeyOriginInfo
|
||||
void UpgradeKeyMetadata();
|
||||
|
||||
bool IsHDEnabled() const override;
|
||||
|
||||
bool SetupGeneration(bool force = false) override;
|
||||
|
||||
bool Upgrade(int prev_version, int new_version, bilingual_str& error) override;
|
||||
|
||||
bool HavePrivateKeys() const override;
|
||||
bool HaveCryptedKeys() const override;
|
||||
|
||||
void RewriteDB() override;
|
||||
|
||||
std::optional<int64_t> GetOldestKeyPoolTime() const override;
|
||||
size_t KeypoolCountExternalKeys() const;
|
||||
unsigned int GetKeyPoolSize() const override;
|
||||
|
||||
int64_t GetTimeFirstKey() const override;
|
||||
|
||||
std::unique_ptr<CKeyMetadata> GetMetadata(const CTxDestination& dest) const override;
|
||||
|
||||
bool CanGetAddresses(bool internal = false) const override;
|
||||
|
||||
bool SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, bilingual_str>& input_errors) const override;
|
||||
SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const override;
|
||||
std::optional<common::PSBTError> FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type = SIGHASH_DEFAULT, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr, bool finalize = true) const override;
|
||||
|
||||
uint256 GetID() const override;
|
||||
|
||||
//! Adds a key to the store, and saves it to disk.
|
||||
bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override;
|
||||
//! Adds an encrypted key to the store, and saves it to disk.
|
||||
bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
|
||||
void UpdateTimeFirstKey(int64_t nCreateTime) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
|
||||
//! Load metadata (used by LoadWallet)
|
||||
void LoadKeyMetadata(const CKeyID& keyID, const CKeyMetadata &metadata) override;
|
||||
void LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata &metadata) override;
|
||||
//! Generate a new key
|
||||
CPubKey GenerateNewKey(WalletBatch& batch, CHDChain& hd_chain, bool internal = false) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
|
||||
|
||||
/* Set the HD chain model (chain child index counters) and writes it to the database */
|
||||
void AddHDChain(const CHDChain& chain);
|
||||
|
||||
//! Remove a watch only script from the keystore
|
||||
bool RemoveWatchOnly(const CScript &dest);
|
||||
bool AddWatchOnly(const CScript& dest, int64_t nCreateTime) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
|
||||
|
||||
/* SigningProvider overrides */
|
||||
bool AddCScript(const CScript& redeemScript) override;
|
||||
|
||||
bool NewKeyPool();
|
||||
void MarkPreSplitKeys() EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
|
||||
|
||||
bool ImportScripts(const std::set<CScript> scripts, int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
|
||||
bool ImportPrivKeys(const std::map<CKeyID, CKey>& privkey_map, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
|
||||
bool ImportPubKeys(const std::vector<std::pair<CKeyID, bool>>& ordered_pubkeys, const std::map<CKeyID, CPubKey>& pubkey_map, const std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>>& key_origins, const bool add_keypool, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
|
||||
bool ImportScriptPubKeys(const std::set<CScript>& script_pub_keys, const bool have_solving_data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
|
||||
|
||||
/* Returns true if the wallet can generate new keys */
|
||||
bool CanGenerateKeys() const;
|
||||
|
||||
/* Generates a new HD seed (will not be activated) */
|
||||
CPubKey GenerateNewSeed();
|
||||
|
||||
/* Derives a new HD seed (will not be activated) */
|
||||
CPubKey DeriveNewSeed(const CKey& key);
|
||||
|
||||
/* Set the current HD seed (will reset the chain child index counters)
|
||||
Sets the seed's version based on the current wallet version (so the
|
||||
caller must ensure the current wallet version is correct before calling
|
||||
this function). */
|
||||
void SetHDSeed(const CPubKey& key);
|
||||
|
||||
/**
|
||||
* Explicitly make the wallet learn the related scripts for outputs to the
|
||||
* given key. This is purely to make the wallet file compatible with older
|
||||
* software, as FillableSigningProvider automatically does this implicitly for all
|
||||
* keys now.
|
||||
*/
|
||||
void LearnRelatedScripts(const CPubKey& key, OutputType);
|
||||
|
||||
/**
|
||||
* Same as LearnRelatedScripts, but when the OutputType is not known (and could
|
||||
* be anything).
|
||||
*/
|
||||
void LearnAllRelatedScripts(const CPubKey& key);
|
||||
|
||||
/**
|
||||
* Marks all keys in the keypool up to and including the provided key as used.
|
||||
*
|
||||
* @param keypool_id determines the last key to mark as used
|
||||
*
|
||||
* @return All affected keys
|
||||
*/
|
||||
std::vector<CKeyPool> MarkReserveKeysAsUsed(int64_t keypool_id) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
|
||||
const std::map<CKeyID, int64_t>& GetAllReserveKeys() const { return m_pool_key_to_index; }
|
||||
|
||||
std::set<CKeyID> GetKeys() const override;
|
||||
};
|
||||
|
||||
/** Wraps a LegacyScriptPubKeyMan so that it can be returned in a new unique_ptr. Does not provide privkeys */
|
||||
class LegacySigningProvider : public SigningProvider
|
||||
{
|
||||
@ -644,7 +348,7 @@ public:
|
||||
bool CheckDecryptionKey(const CKeyingMaterial& master_key) override;
|
||||
bool Encrypt(const CKeyingMaterial& master_key, WalletBatch* batch) override;
|
||||
|
||||
util::Result<CTxDestination> GetReservedDestination(const OutputType type, bool internal, int64_t& index, CKeyPool& keypool) override;
|
||||
util::Result<CTxDestination> GetReservedDestination(const OutputType type, bool internal, int64_t& index) override;
|
||||
void ReturnDestination(int64_t index, bool internal, const CTxDestination& addr) override;
|
||||
|
||||
// Tops up the descriptor cache and m_map_script_pub_keys. The cache is stored in the wallet file
|
||||
|
@ -508,8 +508,6 @@ std::map<CTxDestination, std::vector<COutput>> ListCoins(const CWallet& wallet)
|
||||
std::map<CTxDestination, std::vector<COutput>> result;
|
||||
|
||||
CCoinControl coin_control;
|
||||
// Include watch-only for LegacyScriptPubKeyMan wallets without private keys
|
||||
coin_control.fAllowWatchOnly = wallet.GetLegacyScriptPubKeyMan() && wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
|
||||
CoinFilterParams coins_params;
|
||||
coins_params.only_spendable = false;
|
||||
coins_params.skip_locked = false;
|
||||
|
@ -390,7 +390,7 @@ int SQliteExecHandler::Exec(SQLiteDatabase& database, const std::string& stateme
|
||||
return sqlite3_exec(database.m_db, statement.data(), nullptr, nullptr, nullptr);
|
||||
}
|
||||
|
||||
std::unique_ptr<DatabaseBatch> SQLiteDatabase::MakeBatch(bool flush_on_close)
|
||||
std::unique_ptr<DatabaseBatch> SQLiteDatabase::MakeBatch()
|
||||
{
|
||||
// We ignore flush_on_close because we don't do manual flushing for SQLite
|
||||
return std::make_unique<SQLiteBatch>(*this);
|
||||
|
@ -85,9 +85,6 @@ public:
|
||||
|
||||
void SetExecHandler(std::unique_ptr<SQliteExecHandler>&& handler) { m_exec_handler = std::move(handler); }
|
||||
|
||||
/* No-op. See comment on SQLiteDatabase::Flush */
|
||||
void Flush() override {}
|
||||
|
||||
void Close() override;
|
||||
|
||||
std::unique_ptr<DatabaseCursor> GetNewCursor() override;
|
||||
@ -140,10 +137,6 @@ public:
|
||||
/** Close the database */
|
||||
void Close() override;
|
||||
|
||||
/* These functions are unused */
|
||||
void AddRef() override { assert(false); }
|
||||
void RemoveRef() override { assert(false); }
|
||||
|
||||
/** Rewrite the entire database on disk */
|
||||
bool Rewrite(const char* skip = nullptr) override;
|
||||
|
||||
@ -151,25 +144,11 @@ public:
|
||||
*/
|
||||
bool Backup(const std::string& dest) const override;
|
||||
|
||||
/** No-ops
|
||||
*
|
||||
* SQLite always flushes everything to the database file after each transaction
|
||||
* (each Read/Write/Erase that we do is its own transaction unless we called
|
||||
* TxnBegin) so there is no need to have Flush or Periodic Flush.
|
||||
*
|
||||
* There is no DB env to reload, so ReloadDbEnv has nothing to do
|
||||
*/
|
||||
void Flush() override {}
|
||||
bool PeriodicFlush() override { return false; }
|
||||
void ReloadDbEnv() override {}
|
||||
|
||||
void IncrementUpdateCounter() override { ++nUpdateCounter; }
|
||||
|
||||
std::string Filename() override { return m_file_path; }
|
||||
std::string Format() override { return "sqlite"; }
|
||||
|
||||
/** Make a SQLiteBatch connected to this database */
|
||||
std::unique_ptr<DatabaseBatch> MakeBatch(bool flush_on_close = true) override;
|
||||
std::unique_ptr<DatabaseBatch> MakeBatch() override;
|
||||
|
||||
/** Return true if there is an on-going txn in this connection */
|
||||
bool HasActiveTxn();
|
||||
|
@ -2,7 +2,6 @@
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <bitcoin-build-config.h> // IWYU pragma: keep
|
||||
#include <test/fuzz/FuzzedDataProvider.h>
|
||||
#include <test/fuzz/fuzz.h>
|
||||
#include <test/fuzz/util.h>
|
||||
@ -10,7 +9,6 @@
|
||||
#include <util/fs.h>
|
||||
#include <util/time.h>
|
||||
#include <util/translation.h>
|
||||
#include <wallet/bdb.h>
|
||||
#include <wallet/db.h>
|
||||
#include <wallet/dump.h>
|
||||
#include <wallet/migrate.h>
|
||||
@ -18,13 +16,6 @@
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
// There is an inconsistency in BDB on Windows.
|
||||
// See: https://github.com/bitcoin/bitcoin/pull/26606#issuecomment-2322763212
|
||||
#undef USE_BDB_NON_MSVC
|
||||
#if defined(USE_BDB) && !defined(_MSC_VER)
|
||||
#define USE_BDB_NON_MSVC
|
||||
#endif
|
||||
|
||||
using wallet::DatabaseOptions;
|
||||
using wallet::DatabaseStatus;
|
||||
|
||||
@ -57,17 +48,10 @@ FUZZ_TARGET(wallet_bdb_parser, .init = initialize_wallet_bdb_parser)
|
||||
}
|
||||
g_setup->m_args.ForceSetArg("-dumpfile", fs::PathToString(bdb_ro_dumpfile));
|
||||
|
||||
#ifdef USE_BDB_NON_MSVC
|
||||
bool bdb_ro_err = false;
|
||||
bool bdb_ro_strict_err = false;
|
||||
#endif
|
||||
auto db{MakeBerkeleyRODatabase(wallet_path, options, status, error)};
|
||||
if (db) {
|
||||
assert(DumpWallet(g_setup->m_args, *db, error));
|
||||
} else {
|
||||
#ifdef USE_BDB_NON_MSVC
|
||||
bdb_ro_err = true;
|
||||
#endif
|
||||
if (error.original.starts_with("AutoFile::ignore: end of file") ||
|
||||
error.original.starts_with("AutoFile::read: end of file") ||
|
||||
error.original.starts_with("AutoFile::seek: ") ||
|
||||
@ -97,47 +81,8 @@ FUZZ_TARGET(wallet_bdb_parser, .init = initialize_wallet_bdb_parser)
|
||||
error.original == "Subdatabase has an unexpected name" ||
|
||||
error.original == "Unsupported BDB data file version number" ||
|
||||
error.original == "BDB builtin encryption is not supported") {
|
||||
#ifdef USE_BDB_NON_MSVC
|
||||
bdb_ro_strict_err = true;
|
||||
#endif
|
||||
} else {
|
||||
throw std::runtime_error(error.original);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_BDB_NON_MSVC
|
||||
// Try opening with BDB
|
||||
fs::path bdb_dumpfile{g_setup->m_args.GetDataDirNet() / "fuzzed_dumpfile_bdb.dump"};
|
||||
if (fs::exists(bdb_dumpfile)) { // Writing into an existing dump file will throw an exception
|
||||
remove(bdb_dumpfile);
|
||||
}
|
||||
g_setup->m_args.ForceSetArg("-dumpfile", fs::PathToString(bdb_dumpfile));
|
||||
|
||||
try {
|
||||
auto db{MakeBerkeleyDatabase(wallet_path, options, status, error)};
|
||||
if (bdb_ro_err && !db) {
|
||||
return;
|
||||
}
|
||||
assert(db);
|
||||
if (bdb_ro_strict_err) {
|
||||
// BerkeleyRO will be stricter than BDB. Ignore when those specific errors are hit.
|
||||
return;
|
||||
}
|
||||
assert(!bdb_ro_err);
|
||||
assert(DumpWallet(g_setup->m_args, *db, error));
|
||||
} catch (const std::runtime_error& e) {
|
||||
if (bdb_ro_err) return;
|
||||
throw e;
|
||||
}
|
||||
|
||||
// Make sure the dumpfiles match
|
||||
if (fs::exists(bdb_ro_dumpfile) && fs::exists(bdb_dumpfile)) {
|
||||
std::ifstream bdb_ro_dump(bdb_ro_dumpfile, std::ios_base::binary | std::ios_base::in);
|
||||
std::ifstream bdb_dump(bdb_dumpfile, std::ios_base::binary | std::ios_base::in);
|
||||
assert(std::equal(
|
||||
std::istreambuf_iterator<char>(bdb_ro_dump.rdbuf()),
|
||||
std::istreambuf_iterator<char>(),
|
||||
std::istreambuf_iterator<char>(bdb_dump.rdbuf())));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -36,25 +36,6 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
|
||||
CScript scriptPubKey;
|
||||
isminetype result;
|
||||
|
||||
// P2PK compressed - Legacy
|
||||
{
|
||||
CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
|
||||
keystore.SetupLegacyScriptPubKeyMan();
|
||||
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
|
||||
scriptPubKey = GetScriptForRawPubKey(pubkeys[0]);
|
||||
|
||||
// Keystore does not have key
|
||||
result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0);
|
||||
|
||||
// Keystore has key
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
|
||||
result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 1);
|
||||
}
|
||||
|
||||
// P2PK compressed - Descriptor
|
||||
{
|
||||
CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
|
||||
@ -67,25 +48,6 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
||||
}
|
||||
|
||||
// P2PK uncompressed - Legacy
|
||||
{
|
||||
CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
|
||||
keystore.SetupLegacyScriptPubKeyMan();
|
||||
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
|
||||
scriptPubKey = GetScriptForRawPubKey(uncompressedPubkey);
|
||||
|
||||
// Keystore does not have key
|
||||
result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0);
|
||||
|
||||
// Keystore has key
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey));
|
||||
result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 1);
|
||||
}
|
||||
|
||||
// P2PK uncompressed - Descriptor
|
||||
{
|
||||
CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
|
||||
@ -98,25 +60,6 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
||||
}
|
||||
|
||||
// P2PKH compressed - Legacy
|
||||
{
|
||||
CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
|
||||
keystore.SetupLegacyScriptPubKeyMan();
|
||||
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
|
||||
scriptPubKey = GetScriptForDestination(PKHash(pubkeys[0]));
|
||||
|
||||
// Keystore does not have key
|
||||
result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0);
|
||||
|
||||
// Keystore has key
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
|
||||
result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 1);
|
||||
}
|
||||
|
||||
// P2PKH compressed - Descriptor
|
||||
{
|
||||
CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
|
||||
@ -129,25 +72,6 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
||||
}
|
||||
|
||||
// P2PKH uncompressed - Legacy
|
||||
{
|
||||
CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
|
||||
keystore.SetupLegacyScriptPubKeyMan();
|
||||
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
|
||||
scriptPubKey = GetScriptForDestination(PKHash(uncompressedPubkey));
|
||||
|
||||
// Keystore does not have key
|
||||
result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0);
|
||||
|
||||
// Keystore has key
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey));
|
||||
result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 1);
|
||||
}
|
||||
|
||||
// P2PKH uncompressed - Descriptor
|
||||
{
|
||||
CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
|
||||
@ -160,33 +84,6 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
||||
}
|
||||
|
||||
// P2SH - Legacy
|
||||
{
|
||||
CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
|
||||
keystore.SetupLegacyScriptPubKeyMan();
|
||||
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
|
||||
|
||||
CScript redeemScript = GetScriptForDestination(PKHash(pubkeys[0]));
|
||||
scriptPubKey = GetScriptForDestination(ScriptHash(redeemScript));
|
||||
|
||||
// Keystore does not have redeemScript or key
|
||||
result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0);
|
||||
|
||||
// Keystore has redeemScript but no key
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(redeemScript));
|
||||
result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0);
|
||||
|
||||
// Keystore has redeemScript and key
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
|
||||
result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 1);
|
||||
}
|
||||
|
||||
// P2SH - Descriptor
|
||||
{
|
||||
CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
|
||||
@ -200,25 +97,6 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
||||
}
|
||||
|
||||
// (P2PKH inside) P2SH inside P2SH (invalid) - Legacy
|
||||
{
|
||||
CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
|
||||
keystore.SetupLegacyScriptPubKeyMan();
|
||||
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
|
||||
|
||||
CScript redeemscript_inner = GetScriptForDestination(PKHash(pubkeys[0]));
|
||||
CScript redeemscript = GetScriptForDestination(ScriptHash(redeemscript_inner));
|
||||
scriptPubKey = GetScriptForDestination(ScriptHash(redeemscript));
|
||||
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(redeemscript));
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(redeemscript_inner));
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(scriptPubKey));
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
|
||||
result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0);
|
||||
}
|
||||
|
||||
// (P2PKH inside) P2SH inside P2SH (invalid) - Descriptor
|
||||
{
|
||||
CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
|
||||
@ -228,25 +106,6 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
|
||||
BOOST_CHECK_EQUAL(spk_manager, nullptr);
|
||||
}
|
||||
|
||||
// (P2PKH inside) P2SH inside P2WSH (invalid) - Legacy
|
||||
{
|
||||
CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
|
||||
keystore.SetupLegacyScriptPubKeyMan();
|
||||
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
|
||||
|
||||
CScript redeemscript = GetScriptForDestination(PKHash(pubkeys[0]));
|
||||
CScript witnessscript = GetScriptForDestination(ScriptHash(redeemscript));
|
||||
scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessscript));
|
||||
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(witnessscript));
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(redeemscript));
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(scriptPubKey));
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
|
||||
result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0);
|
||||
}
|
||||
|
||||
// (P2PKH inside) P2SH inside P2WSH (invalid) - Descriptor
|
||||
{
|
||||
CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
|
||||
@ -256,23 +115,6 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
|
||||
BOOST_CHECK_EQUAL(spk_manager, nullptr);
|
||||
}
|
||||
|
||||
// P2WPKH inside P2WSH (invalid) - Legacy
|
||||
{
|
||||
CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
|
||||
keystore.SetupLegacyScriptPubKeyMan();
|
||||
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
|
||||
|
||||
CScript witnessscript = GetScriptForDestination(WitnessV0KeyHash(pubkeys[0]));
|
||||
scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessscript));
|
||||
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(witnessscript));
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(scriptPubKey));
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
|
||||
result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0);
|
||||
}
|
||||
|
||||
// P2WPKH inside P2WSH (invalid) - Descriptor
|
||||
{
|
||||
CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
|
||||
@ -282,25 +124,6 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
|
||||
BOOST_CHECK_EQUAL(spk_manager, nullptr);
|
||||
}
|
||||
|
||||
// (P2PKH inside) P2WSH inside P2WSH (invalid) - Legacy
|
||||
{
|
||||
CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
|
||||
keystore.SetupLegacyScriptPubKeyMan();
|
||||
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
|
||||
|
||||
CScript witnessscript_inner = GetScriptForDestination(PKHash(pubkeys[0]));
|
||||
CScript witnessscript = GetScriptForDestination(WitnessV0ScriptHash(witnessscript_inner));
|
||||
scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessscript));
|
||||
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(witnessscript_inner));
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(witnessscript));
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(scriptPubKey));
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
|
||||
result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0);
|
||||
}
|
||||
|
||||
// (P2PKH inside) P2WSH inside P2WSH (invalid) - Descriptor
|
||||
{
|
||||
CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
|
||||
@ -310,22 +133,6 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
|
||||
BOOST_CHECK_EQUAL(spk_manager, nullptr);
|
||||
}
|
||||
|
||||
// P2WPKH compressed - Legacy
|
||||
{
|
||||
CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
|
||||
keystore.SetupLegacyScriptPubKeyMan();
|
||||
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
|
||||
|
||||
scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(pubkeys[0]));
|
||||
|
||||
// Keystore implicitly has key and P2SH redeemScript
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(scriptPubKey));
|
||||
result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 1);
|
||||
}
|
||||
|
||||
// P2WPKH compressed - Descriptor
|
||||
{
|
||||
CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
|
||||
@ -338,27 +145,6 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
||||
}
|
||||
|
||||
// P2WPKH uncompressed - Legacy
|
||||
{
|
||||
CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
|
||||
keystore.SetupLegacyScriptPubKeyMan();
|
||||
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey));
|
||||
|
||||
scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(uncompressedPubkey));
|
||||
|
||||
// Keystore has key, but no P2SH redeemScript
|
||||
result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0);
|
||||
|
||||
// Keystore has key and P2SH redeemScript
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(scriptPubKey));
|
||||
result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0);
|
||||
}
|
||||
|
||||
// P2WPKH uncompressed (invalid) - Descriptor
|
||||
{
|
||||
CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
|
||||
@ -368,41 +154,6 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
|
||||
BOOST_CHECK_EQUAL(spk_manager, nullptr);
|
||||
}
|
||||
|
||||
// scriptPubKey multisig - Legacy
|
||||
{
|
||||
CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
|
||||
keystore.SetupLegacyScriptPubKeyMan();
|
||||
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
|
||||
|
||||
scriptPubKey = GetScriptForMultisig(2, {uncompressedPubkey, pubkeys[1]});
|
||||
|
||||
// Keystore does not have any keys
|
||||
result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0);
|
||||
|
||||
// Keystore has 1/2 keys
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey));
|
||||
|
||||
result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0);
|
||||
|
||||
// Keystore has 2/2 keys
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[1]));
|
||||
|
||||
result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0);
|
||||
|
||||
// Keystore has 2/2 keys and the script
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(scriptPubKey));
|
||||
|
||||
result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0);
|
||||
}
|
||||
|
||||
// scriptPubKey multisig - Descriptor
|
||||
{
|
||||
CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
|
||||
@ -415,29 +166,6 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
||||
}
|
||||
|
||||
// P2SH multisig - Legacy
|
||||
{
|
||||
CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
|
||||
keystore.SetupLegacyScriptPubKeyMan();
|
||||
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey));
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[1]));
|
||||
|
||||
CScript redeemScript = GetScriptForMultisig(2, {uncompressedPubkey, pubkeys[1]});
|
||||
scriptPubKey = GetScriptForDestination(ScriptHash(redeemScript));
|
||||
|
||||
// Keystore has no redeemScript
|
||||
result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0);
|
||||
|
||||
// Keystore has redeemScript
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(redeemScript));
|
||||
result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 1);
|
||||
}
|
||||
|
||||
// P2SH multisig - Descriptor
|
||||
{
|
||||
CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
|
||||
@ -452,35 +180,6 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
||||
}
|
||||
|
||||
// P2WSH multisig with compressed keys - Legacy
|
||||
{
|
||||
CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
|
||||
keystore.SetupLegacyScriptPubKeyMan();
|
||||
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[1]));
|
||||
|
||||
CScript witnessScript = GetScriptForMultisig(2, {pubkeys[0], pubkeys[1]});
|
||||
scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessScript));
|
||||
|
||||
// Keystore has keys, but no witnessScript or P2SH redeemScript
|
||||
result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0);
|
||||
|
||||
// Keystore has keys and witnessScript, but no P2SH redeemScript
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(witnessScript));
|
||||
result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0);
|
||||
|
||||
// Keystore has keys, witnessScript, P2SH redeemScript
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(scriptPubKey));
|
||||
result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 1);
|
||||
}
|
||||
|
||||
// P2WSH multisig with compressed keys - Descriptor
|
||||
{
|
||||
CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
|
||||
@ -495,35 +194,6 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
||||
}
|
||||
|
||||
// P2WSH multisig with uncompressed key - Legacy
|
||||
{
|
||||
CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
|
||||
keystore.SetupLegacyScriptPubKeyMan();
|
||||
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey));
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[1]));
|
||||
|
||||
CScript witnessScript = GetScriptForMultisig(2, {uncompressedPubkey, pubkeys[1]});
|
||||
scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessScript));
|
||||
|
||||
// Keystore has keys, but no witnessScript or P2SH redeemScript
|
||||
result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0);
|
||||
|
||||
// Keystore has keys and witnessScript, but no P2SH redeemScript
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(witnessScript));
|
||||
result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0);
|
||||
|
||||
// Keystore has keys, witnessScript, P2SH redeemScript
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(scriptPubKey));
|
||||
result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0);
|
||||
}
|
||||
|
||||
// P2WSH multisig with uncompressed key (invalid) - Descriptor
|
||||
{
|
||||
CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
|
||||
@ -534,36 +204,6 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
|
||||
BOOST_CHECK_EQUAL(spk_manager, nullptr);
|
||||
}
|
||||
|
||||
// P2WSH multisig wrapped in P2SH - Legacy
|
||||
{
|
||||
CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
|
||||
keystore.SetupLegacyScriptPubKeyMan();
|
||||
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
|
||||
|
||||
CScript witnessScript = GetScriptForMultisig(2, {pubkeys[0], pubkeys[1]});
|
||||
CScript redeemScript = GetScriptForDestination(WitnessV0ScriptHash(witnessScript));
|
||||
scriptPubKey = GetScriptForDestination(ScriptHash(redeemScript));
|
||||
|
||||
// Keystore has no witnessScript, P2SH redeemScript, or keys
|
||||
result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0);
|
||||
|
||||
// Keystore has witnessScript and P2SH redeemScript, but no keys
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(redeemScript));
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(witnessScript));
|
||||
result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0);
|
||||
|
||||
// Keystore has keys, witnessScript, P2SH redeemScript
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[1]));
|
||||
result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 1);
|
||||
}
|
||||
|
||||
// P2WSH multisig wrapped in P2SH - Descriptor
|
||||
{
|
||||
CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
|
||||
@ -640,66 +280,6 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
|
||||
result = spk_manager->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
||||
}
|
||||
|
||||
// OP_RETURN
|
||||
{
|
||||
CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
|
||||
keystore.SetupLegacyScriptPubKeyMan();
|
||||
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
|
||||
|
||||
scriptPubKey.clear();
|
||||
scriptPubKey << OP_RETURN << ToByteVector(pubkeys[0]);
|
||||
|
||||
result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0);
|
||||
}
|
||||
|
||||
// witness unspendable
|
||||
{
|
||||
CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
|
||||
keystore.SetupLegacyScriptPubKeyMan();
|
||||
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
|
||||
|
||||
scriptPubKey.clear();
|
||||
scriptPubKey << OP_0 << "aabb"_hex;
|
||||
|
||||
result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0);
|
||||
}
|
||||
|
||||
// witness unknown
|
||||
{
|
||||
CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
|
||||
keystore.SetupLegacyScriptPubKeyMan();
|
||||
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
|
||||
|
||||
scriptPubKey.clear();
|
||||
scriptPubKey << OP_16 << "aabb"_hex;
|
||||
|
||||
result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0);
|
||||
}
|
||||
|
||||
// Nonstandard
|
||||
{
|
||||
CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
|
||||
keystore.SetupLegacyScriptPubKeyMan();
|
||||
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
|
||||
|
||||
scriptPubKey.clear();
|
||||
scriptPubKey << OP_9 << OP_ADD << OP_11 << OP_EQUAL;
|
||||
|
||||
result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
@ -15,32 +15,6 @@
|
||||
namespace wallet {
|
||||
BOOST_FIXTURE_TEST_SUITE(scriptpubkeyman_tests, BasicTestingSetup)
|
||||
|
||||
// Test LegacyScriptPubKeyMan::CanProvide behavior, making sure it returns true
|
||||
// for recognized scripts even when keys may not be available for signing.
|
||||
BOOST_AUTO_TEST_CASE(CanProvide)
|
||||
{
|
||||
// Set up wallet and keyman variables.
|
||||
CWallet wallet(m_node.chain.get(), "", CreateMockableWalletDatabase());
|
||||
LegacyScriptPubKeyMan& keyman = *wallet.GetOrCreateLegacyScriptPubKeyMan();
|
||||
|
||||
// Make a 1 of 2 multisig script
|
||||
std::vector<CKey> keys(2);
|
||||
std::vector<CPubKey> pubkeys;
|
||||
for (CKey& key : keys) {
|
||||
key.MakeNewKey(true);
|
||||
pubkeys.emplace_back(key.GetPubKey());
|
||||
}
|
||||
CScript multisig_script = GetScriptForMultisig(1, pubkeys);
|
||||
CScript p2sh_script = GetScriptForDestination(ScriptHash(multisig_script));
|
||||
SignatureData data;
|
||||
|
||||
// Verify the p2sh(multisig) script is not recognized until the multisig
|
||||
// script is added to the keystore to make it solvable
|
||||
BOOST_CHECK(!keyman.CanProvide(p2sh_script, data));
|
||||
keyman.AddCScript(multisig_script);
|
||||
BOOST_CHECK(keyman.CanProvide(p2sh_script, data));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(DescriptorScriptPubKeyManTests)
|
||||
{
|
||||
std::unique_ptr<interfaces::Chain>& chain = m_node.chain;
|
||||
|
@ -76,7 +76,6 @@ public:
|
||||
explicit MockableBatch(MockableData& records, bool pass) : m_records(records), m_pass(pass) {}
|
||||
~MockableBatch() = default;
|
||||
|
||||
void Flush() override {}
|
||||
void Close() override {}
|
||||
|
||||
std::unique_ptr<DatabaseCursor> GetNewCursor() override
|
||||
@ -104,20 +103,14 @@ public:
|
||||
~MockableDatabase() = default;
|
||||
|
||||
void Open() override {}
|
||||
void AddRef() override {}
|
||||
void RemoveRef() override {}
|
||||
|
||||
bool Rewrite(const char* pszSkip=nullptr) override { return m_pass; }
|
||||
bool Backup(const std::string& strDest) const override { return m_pass; }
|
||||
void Flush() override {}
|
||||
void Close() override {}
|
||||
bool PeriodicFlush() override { return m_pass; }
|
||||
void IncrementUpdateCounter() override {}
|
||||
void ReloadDbEnv() override {}
|
||||
|
||||
std::string Filename() override { return "mockable"; }
|
||||
std::string Format() override { return "mock"; }
|
||||
std::unique_ptr<DatabaseBatch> MakeBatch(bool flush_on_close = true) override { return std::make_unique<MockableBatch>(m_records, m_pass); }
|
||||
std::unique_ptr<DatabaseBatch> MakeBatch() override { return std::make_unique<MockableBatch>(m_records, m_pass); }
|
||||
};
|
||||
|
||||
std::unique_ptr<WalletDatabase> CreateMockableWalletDatabase(MockableData records = {});
|
||||
|
@ -35,9 +35,6 @@
|
||||
using node::MAX_BLOCKFILE_SIZE;
|
||||
|
||||
namespace wallet {
|
||||
RPCHelpMan importmulti();
|
||||
RPCHelpMan dumpwallet();
|
||||
RPCHelpMan importwallet();
|
||||
|
||||
// Ensure that fee levels defined in the wallet are at least as high
|
||||
// as the default levels for node policy.
|
||||
|
@ -29,34 +29,5 @@ BOOST_AUTO_TEST_CASE(walletdb_readkeyvalue)
|
||||
BOOST_CHECK_THROW(ssValue >> dummy, std::ios_base::failure);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(walletdb_read_write_deadlock)
|
||||
{
|
||||
// Exercises a db read write operation that shouldn't deadlock.
|
||||
for (const DatabaseFormat& db_format : DATABASE_FORMATS) {
|
||||
// Context setup
|
||||
DatabaseOptions options;
|
||||
options.require_format = db_format;
|
||||
DatabaseStatus status;
|
||||
bilingual_str error_string;
|
||||
std::unique_ptr<WalletDatabase> db = MakeDatabase(m_path_root / strprintf("wallet_%d_.dat", db_format).c_str(), options, status, error_string);
|
||||
BOOST_CHECK_EQUAL(status, DatabaseStatus::SUCCESS);
|
||||
|
||||
std::shared_ptr<CWallet> wallet(new CWallet(m_node.chain.get(), "", std::move(db)));
|
||||
wallet->m_keypool_size = 4;
|
||||
|
||||
// Create legacy spkm
|
||||
LOCK(wallet->cs_wallet);
|
||||
auto legacy_spkm = wallet->GetOrCreateLegacyScriptPubKeyMan();
|
||||
BOOST_CHECK(!HasLegacyRecords(*wallet));
|
||||
BOOST_CHECK(legacy_spkm->SetupGeneration(true));
|
||||
BOOST_CHECK(HasLegacyRecords(*wallet));
|
||||
wallet->Flush();
|
||||
|
||||
// Now delete all records, which performs a read write operation.
|
||||
BOOST_CHECK(wallet->GetLegacyScriptPubKeyMan()->DeleteRecords());
|
||||
BOOST_CHECK(!HasLegacyRecords(*wallet));
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
} // namespace wallet
|
||||
|
@ -43,7 +43,7 @@ BOOST_FIXTURE_TEST_CASE(wallet_load_descriptors, TestingSetup)
|
||||
std::unique_ptr<WalletDatabase> database = CreateMockableWalletDatabase();
|
||||
{
|
||||
// Write unknown active descriptor
|
||||
WalletBatch batch(*database, false);
|
||||
WalletBatch batch(*database);
|
||||
std::string unknown_desc = "trx(tpubD6NzVbkrYhZ4Y4S7m6Y5s9GD8FqEMBy56AGphZXuagajudVZEnYyBahZMgHNCTJc2at82YX6s8JiL1Lohu5A3v1Ur76qguNH4QVQ7qYrBQx/86'/1'/0'/0/*)#8pn8tzdt";
|
||||
WalletDescriptor wallet_descriptor(std::make_shared<DummyDescriptor>(unknown_desc), 0, 0, 0, 0);
|
||||
BOOST_CHECK(batch.WriteDescriptor(uint256(), wallet_descriptor));
|
||||
@ -70,7 +70,7 @@ BOOST_FIXTURE_TEST_CASE(wallet_load_descriptors, TestingSetup)
|
||||
|
||||
{
|
||||
// Write valid descriptor with invalid ID
|
||||
WalletBatch batch(*database, false);
|
||||
WalletBatch batch(*database);
|
||||
std::string desc = "wpkh([d34db33f/84h/0h/0h]xpub6DJ2dNUysrn5Vt36jH2KLBT2i1auw1tTSSomg8PhqNiUtx8QX2SvC9nrHu81fT41fvDUnhMjEzQgXnQjKEu3oaqMSzhSrHMxyyoEAmUHQbY/0/*)#cjjspncu";
|
||||
WalletDescriptor wallet_descriptor(std::make_shared<DummyDescriptor>(desc), 0, 0, 0, 0);
|
||||
BOOST_CHECK(batch.WriteDescriptor(uint256::ONE, wallet_descriptor));
|
||||
|
@ -236,7 +236,6 @@ static void FlushAndDeleteWallet(CWallet* wallet)
|
||||
{
|
||||
const std::string name = wallet->GetName();
|
||||
wallet->WalletLogPrintf("Releasing wallet %s..\n", name);
|
||||
wallet->Flush();
|
||||
delete wallet;
|
||||
// Wallet is now released, notify WaitForDeleteWallet, if any.
|
||||
{
|
||||
@ -313,9 +312,6 @@ class FastWalletRescanFilter
|
||||
public:
|
||||
FastWalletRescanFilter(const CWallet& wallet) : m_wallet(wallet)
|
||||
{
|
||||
// fast rescanning via block filters is only supported by descriptor wallets right now
|
||||
assert(!m_wallet.IsLegacy());
|
||||
|
||||
// create initial filter with scripts from all ScriptPubKeyMans
|
||||
for (auto spkm : m_wallet.GetAllScriptPubKeyMans()) {
|
||||
auto desc_spkm{dynamic_cast<DescriptorScriptPubKeyMan*>(spkm)};
|
||||
@ -548,21 +544,6 @@ const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const
|
||||
return &(it->second);
|
||||
}
|
||||
|
||||
void CWallet::UpgradeKeyMetadata()
|
||||
{
|
||||
if (IsLocked() || IsWalletFlagSet(WALLET_FLAG_KEY_ORIGIN_METADATA)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto spk_man = GetLegacyScriptPubKeyMan();
|
||||
if (!spk_man) {
|
||||
return;
|
||||
}
|
||||
|
||||
spk_man->UpgradeKeyMetadata();
|
||||
SetWalletFlag(WALLET_FLAG_KEY_ORIGIN_METADATA);
|
||||
}
|
||||
|
||||
void CWallet::UpgradeDescriptorCache()
|
||||
{
|
||||
if (!IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS) || IsLocked() || IsWalletFlagSet(WALLET_FLAG_LAST_HARDENED_XPUB_CACHED)) {
|
||||
@ -630,8 +611,6 @@ bool CWallet::Unlock(const SecureString& strWalletPassphrase)
|
||||
continue; // try another master key
|
||||
}
|
||||
if (Unlock(plain_master_key)) {
|
||||
// Now that we've unlocked, upgrade the key metadata
|
||||
UpgradeKeyMetadata();
|
||||
// Now that we've unlocked, upgrade the descriptor cache
|
||||
UpgradeDescriptorCache();
|
||||
return true;
|
||||
@ -736,11 +715,6 @@ bool CWallet::HasWalletSpend(const CTransactionRef& tx) const
|
||||
return false;
|
||||
}
|
||||
|
||||
void CWallet::Flush()
|
||||
{
|
||||
GetDatabase().Flush();
|
||||
}
|
||||
|
||||
void CWallet::Close()
|
||||
{
|
||||
GetDatabase().Close();
|
||||
@ -895,25 +869,12 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
|
||||
// If we are using descriptors, make new descriptors with a new seed
|
||||
if (IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS) && !IsWalletFlagSet(WALLET_FLAG_BLANK_WALLET)) {
|
||||
SetupDescriptorScriptPubKeyMans();
|
||||
} else if (auto spk_man = GetLegacyScriptPubKeyMan()) {
|
||||
// if we are using HD, replace the HD seed with a new one
|
||||
if (spk_man->IsHDEnabled()) {
|
||||
if (!spk_man->SetupGeneration(true)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Lock();
|
||||
|
||||
// Need to completely rewrite the wallet file; if we don't, bdb might keep
|
||||
// Need to completely rewrite the wallet file; if we don't, the database might keep
|
||||
// bits of the unencrypted private key in slack space in the database file.
|
||||
GetDatabase().Rewrite();
|
||||
|
||||
// BDB seems to have a bad habit of writing old data into
|
||||
// slack space in .dat files; that is bad if the old data is
|
||||
// unencrypted private keys. So:
|
||||
GetDatabase().ReloadDbEnv();
|
||||
|
||||
}
|
||||
NotifyStatusChanged(this);
|
||||
|
||||
@ -1059,31 +1020,14 @@ bool CWallet::IsSpentKey(const CScript& scriptPubKey) const
|
||||
if (IsAddressPreviouslySpent(dest)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (LegacyScriptPubKeyMan* spk_man = GetLegacyScriptPubKeyMan()) {
|
||||
for (const auto& keyid : GetAffectedKeys(scriptPubKey, *spk_man)) {
|
||||
WitnessV0KeyHash wpkh_dest(keyid);
|
||||
if (IsAddressPreviouslySpent(wpkh_dest)) {
|
||||
return true;
|
||||
}
|
||||
ScriptHash sh_wpkh_dest(GetScriptForDestination(wpkh_dest));
|
||||
if (IsAddressPreviouslySpent(sh_wpkh_dest)) {
|
||||
return true;
|
||||
}
|
||||
PKHash pkh_dest(keyid);
|
||||
if (IsAddressPreviouslySpent(pkh_dest)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const TxState& state, const UpdateWalletTxFn& update_wtx, bool fFlushOnClose, bool rescanning_old_block)
|
||||
CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const TxState& state, const UpdateWalletTxFn& update_wtx, bool rescanning_old_block)
|
||||
{
|
||||
LOCK(cs_wallet);
|
||||
|
||||
WalletBatch batch(GetDatabase(), fFlushOnClose);
|
||||
WalletBatch batch(GetDatabase());
|
||||
|
||||
uint256 hash = tx->GetHash();
|
||||
|
||||
@ -1293,7 +1237,7 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, const SyncTxS
|
||||
// Block disconnection override an abandoned tx as unconfirmed
|
||||
// which means user may have to call abandontransaction again
|
||||
TxState tx_state = std::visit([](auto&& s) -> TxState { return s; }, state);
|
||||
CWalletTx* wtx = AddToWallet(MakeTransactionRef(tx), tx_state, /*update_wtx=*/nullptr, /*fFlushOnClose=*/false, rescanning_old_block);
|
||||
CWalletTx* wtx = AddToWallet(MakeTransactionRef(tx), tx_state, /*update_wtx=*/nullptr, rescanning_old_block);
|
||||
if (!wtx) {
|
||||
// Can only be nullptr if there was a db write error (missing db, read-only db or a db engine internal writing error).
|
||||
// As we only store arriving transaction in this process, and we don't want an inconsistent state, let's throw an error.
|
||||
@ -1391,8 +1335,7 @@ void CWallet::MarkConflicted(const uint256& hashBlock, int conflicting_height, c
|
||||
}
|
||||
|
||||
void CWallet::RecursiveUpdateTxState(const uint256& tx_hash, const TryUpdatingStateFn& try_updating_state) {
|
||||
// Do not flush the wallet here for performance reasons
|
||||
WalletBatch batch(GetDatabase(), false);
|
||||
WalletBatch batch(GetDatabase());
|
||||
RecursiveUpdateTxState(&batch, tx_hash, try_updating_state);
|
||||
}
|
||||
|
||||
@ -1651,11 +1594,6 @@ isminetype CWallet::IsMine(const CScript& script) const
|
||||
return res;
|
||||
}
|
||||
|
||||
// Legacy wallet
|
||||
if (LegacyScriptPubKeyMan* spkm = GetLegacyScriptPubKeyMan()) {
|
||||
return spkm->IsMine(script);
|
||||
}
|
||||
|
||||
return ISMINE_NO;
|
||||
}
|
||||
|
||||
@ -1788,59 +1726,6 @@ void CWallet::InitWalletFlags(uint64_t flags)
|
||||
if (!LoadWalletFlags(flags)) assert(false);
|
||||
}
|
||||
|
||||
bool CWallet::ImportScripts(const std::set<CScript> scripts, int64_t timestamp)
|
||||
{
|
||||
auto spk_man = GetLegacyScriptPubKeyMan();
|
||||
if (!spk_man) {
|
||||
return false;
|
||||
}
|
||||
LOCK(spk_man->cs_KeyStore);
|
||||
return spk_man->ImportScripts(scripts, timestamp);
|
||||
}
|
||||
|
||||
bool CWallet::ImportPrivKeys(const std::map<CKeyID, CKey>& privkey_map, const int64_t timestamp)
|
||||
{
|
||||
auto spk_man = GetLegacyScriptPubKeyMan();
|
||||
if (!spk_man) {
|
||||
return false;
|
||||
}
|
||||
LOCK(spk_man->cs_KeyStore);
|
||||
return spk_man->ImportPrivKeys(privkey_map, timestamp);
|
||||
}
|
||||
|
||||
bool CWallet::ImportPubKeys(const std::vector<std::pair<CKeyID, bool>>& ordered_pubkeys, const std::map<CKeyID, CPubKey>& pubkey_map, const std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>>& key_origins, const bool add_keypool, const int64_t timestamp)
|
||||
{
|
||||
auto spk_man = GetLegacyScriptPubKeyMan();
|
||||
if (!spk_man) {
|
||||
return false;
|
||||
}
|
||||
LOCK(spk_man->cs_KeyStore);
|
||||
return spk_man->ImportPubKeys(ordered_pubkeys, pubkey_map, key_origins, add_keypool, timestamp);
|
||||
}
|
||||
|
||||
bool CWallet::ImportScriptPubKeys(const std::string& label, const std::set<CScript>& script_pub_keys, const bool have_solving_data, const bool apply_label, const int64_t timestamp)
|
||||
{
|
||||
auto spk_man = GetLegacyScriptPubKeyMan();
|
||||
if (!spk_man) {
|
||||
return false;
|
||||
}
|
||||
LOCK(spk_man->cs_KeyStore);
|
||||
if (!spk_man->ImportScriptPubKeys(script_pub_keys, have_solving_data, timestamp)) {
|
||||
return false;
|
||||
}
|
||||
if (apply_label) {
|
||||
WalletBatch batch(GetDatabase());
|
||||
for (const CScript& script : script_pub_keys) {
|
||||
CTxDestination dest;
|
||||
ExtractDestination(script, dest);
|
||||
if (IsValidDestination(dest)) {
|
||||
SetAddressBookWithDB(batch, dest, label, AddressPurpose::RECEIVE);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void CWallet::MaybeUpdateBirthTime(int64_t time)
|
||||
{
|
||||
int64_t birthtime = m_birth_time.load();
|
||||
@ -1913,7 +1798,7 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
|
||||
ScanResult result;
|
||||
|
||||
std::unique_ptr<FastWalletRescanFilter> fast_rescan_filter;
|
||||
if (!IsLegacy() && chain().hasBlockFilterIndex(BlockFilterType::BASIC)) fast_rescan_filter = std::make_unique<FastWalletRescanFilter>(*this);
|
||||
if (chain().hasBlockFilterIndex(BlockFilterType::BASIC)) fast_rescan_filter = std::make_unique<FastWalletRescanFilter>(*this);
|
||||
|
||||
WalletLogPrintf("Rescan started from block %s... (%s)\n", start_block.ToString(),
|
||||
fast_rescan_filter ? "fast variant using block filters" : "slow variant inspecting all blocks");
|
||||
@ -2182,11 +2067,6 @@ void MaybeResendWalletTxs(WalletContext& context)
|
||||
}
|
||||
|
||||
|
||||
/** @defgroup Actions
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
bool CWallet::SignTransaction(CMutableTransaction& tx) const
|
||||
{
|
||||
AssertLockHeld(cs_wallet);
|
||||
@ -2559,11 +2439,6 @@ size_t CWallet::KeypoolCountExternalKeys() const
|
||||
{
|
||||
AssertLockHeld(cs_wallet);
|
||||
|
||||
auto legacy_spk_man = GetLegacyScriptPubKeyMan();
|
||||
if (legacy_spk_man) {
|
||||
return legacy_spk_man->KeypoolCountExternalKeys();
|
||||
}
|
||||
|
||||
unsigned int count = 0;
|
||||
for (auto spk_man : m_external_spk_managers) {
|
||||
count += spk_man.second->GetKeyPoolSize();
|
||||
@ -2695,13 +2570,11 @@ util::Result<CTxDestination> ReserveDestination::GetReservedDestination(bool int
|
||||
}
|
||||
|
||||
if (nIndex == -1) {
|
||||
CKeyPool keypool;
|
||||
int64_t index;
|
||||
auto op_address = m_spk_man->GetReservedDestination(type, internal, index, keypool);
|
||||
auto op_address = m_spk_man->GetReservedDestination(type, internal, index);
|
||||
if (!op_address) return op_address;
|
||||
nIndex = index;
|
||||
address = *op_address;
|
||||
fInternal = keypool.fInternal;
|
||||
}
|
||||
return address;
|
||||
}
|
||||
@ -2786,68 +2659,6 @@ void CWallet::ListLockedCoins(std::vector<COutPoint>& vOutpts) const
|
||||
}
|
||||
}
|
||||
|
||||
/** @} */ // end of Actions
|
||||
|
||||
void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64_t>& mapKeyBirth) const {
|
||||
AssertLockHeld(cs_wallet);
|
||||
mapKeyBirth.clear();
|
||||
|
||||
// map in which we'll infer heights of other keys
|
||||
std::map<CKeyID, const TxStateConfirmed*> mapKeyFirstBlock;
|
||||
TxStateConfirmed max_confirm{uint256{}, /*height=*/-1, /*index=*/-1};
|
||||
max_confirm.confirmed_block_height = GetLastBlockHeight() > 144 ? GetLastBlockHeight() - 144 : 0; // the tip can be reorganized; use a 144-block safety margin
|
||||
CHECK_NONFATAL(chain().findAncestorByHeight(GetLastBlockHash(), max_confirm.confirmed_block_height, FoundBlock().hash(max_confirm.confirmed_block_hash)));
|
||||
|
||||
{
|
||||
LegacyScriptPubKeyMan* spk_man = GetLegacyScriptPubKeyMan();
|
||||
assert(spk_man != nullptr);
|
||||
LOCK(spk_man->cs_KeyStore);
|
||||
|
||||
// get birth times for keys with metadata
|
||||
for (const auto& entry : spk_man->mapKeyMetadata) {
|
||||
if (entry.second.nCreateTime) {
|
||||
mapKeyBirth[entry.first] = entry.second.nCreateTime;
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare to infer birth heights for keys without metadata
|
||||
for (const CKeyID &keyid : spk_man->GetKeys()) {
|
||||
if (mapKeyBirth.count(keyid) == 0)
|
||||
mapKeyFirstBlock[keyid] = &max_confirm;
|
||||
}
|
||||
|
||||
// if there are no such keys, we're done
|
||||
if (mapKeyFirstBlock.empty())
|
||||
return;
|
||||
|
||||
// find first block that affects those keys, if there are any left
|
||||
for (const auto& entry : mapWallet) {
|
||||
// iterate over all wallet transactions...
|
||||
const CWalletTx &wtx = entry.second;
|
||||
if (auto* conf = wtx.state<TxStateConfirmed>()) {
|
||||
// ... which are already in a block
|
||||
for (const CTxOut &txout : wtx.tx->vout) {
|
||||
// iterate over all their outputs
|
||||
for (const auto &keyid : GetAffectedKeys(txout.scriptPubKey, *spk_man)) {
|
||||
// ... and all their affected keys
|
||||
auto rit = mapKeyFirstBlock.find(keyid);
|
||||
if (rit != mapKeyFirstBlock.end() && conf->confirmed_block_height < rit->second->confirmed_block_height) {
|
||||
rit->second = conf;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extract block timestamps for those keys
|
||||
for (const auto& entry : mapKeyFirstBlock) {
|
||||
int64_t block_time;
|
||||
CHECK_NONFATAL(chain().findBlock(entry.second->confirmed_block_hash, FoundBlock().time(block_time)));
|
||||
mapKeyBirth[entry.first] = block_time - TIMESTAMP_WINDOW; // block times can be 2h off
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute smart timestamp for a transaction being added to the wallet.
|
||||
*
|
||||
@ -3392,7 +3203,6 @@ bool CWallet::AttachChain(const std::shared_ptr<CWallet>& walletInstance, interf
|
||||
}
|
||||
walletInstance->m_attaching_chain = false;
|
||||
walletInstance->chainStateFlushed(ChainstateRole::NORMAL, chain.getTipLocator());
|
||||
walletInstance->GetDatabase().IncrementUpdateCounter();
|
||||
}
|
||||
walletInstance->m_attaching_chain = false;
|
||||
|
||||
@ -3465,21 +3275,6 @@ bool CWallet::BackupWallet(const std::string& strDest) const
|
||||
return GetDatabase().Backup(strDest);
|
||||
}
|
||||
|
||||
CKeyPool::CKeyPool()
|
||||
{
|
||||
nTime = GetTime();
|
||||
fInternal = false;
|
||||
m_pre_split = false;
|
||||
}
|
||||
|
||||
CKeyPool::CKeyPool(const CPubKey& vchPubKeyIn, bool internalIn)
|
||||
{
|
||||
nTime = GetTime();
|
||||
vchPubKey = vchPubKeyIn;
|
||||
fInternal = internalIn;
|
||||
m_pre_split = false;
|
||||
}
|
||||
|
||||
int CWallet::GetTxDepthInMainChain(const CWalletTx& wtx) const
|
||||
{
|
||||
AssertLockHeld(cs_wallet);
|
||||
@ -3616,10 +3411,6 @@ std::set<ScriptPubKeyMan*> CWallet::GetScriptPubKeyMans(const CScript& script) c
|
||||
SignatureData sigdata;
|
||||
Assume(std::all_of(spk_mans.begin(), spk_mans.end(), [&script, &sigdata](ScriptPubKeyMan* spkm) { return spkm->CanProvide(script, sigdata); }));
|
||||
|
||||
// Legacy wallet
|
||||
LegacyScriptPubKeyMan* spkm = GetLegacyScriptPubKeyMan();
|
||||
if (spkm && spkm->CanProvide(script, sigdata)) spk_mans.insert(spkm);
|
||||
|
||||
return spk_mans;
|
||||
}
|
||||
|
||||
@ -3647,10 +3438,6 @@ std::unique_ptr<SigningProvider> CWallet::GetSolvingProvider(const CScript& scri
|
||||
return it->second.at(0)->GetSolvingProvider(script);
|
||||
}
|
||||
|
||||
// Legacy wallet
|
||||
LegacyScriptPubKeyMan* spkm = GetLegacyScriptPubKeyMan();
|
||||
if (spkm && spkm->CanProvide(script, sigdata)) return spkm->GetSolvingProvider(script);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -3666,18 +3453,6 @@ std::vector<WalletDescriptor> CWallet::GetWalletDescriptors(const CScript& scrip
|
||||
return descs;
|
||||
}
|
||||
|
||||
LegacyScriptPubKeyMan* CWallet::GetLegacyScriptPubKeyMan() const
|
||||
{
|
||||
if (IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
|
||||
return nullptr;
|
||||
}
|
||||
// Legacy wallets only have one ScriptPubKeyMan which is a LegacyScriptPubKeyMan.
|
||||
// Everything in m_internal_spk_managers and m_external_spk_managers point to the same legacyScriptPubKeyMan.
|
||||
auto it = m_internal_spk_managers.find(OutputType::LEGACY);
|
||||
if (it == m_internal_spk_managers.end()) return nullptr;
|
||||
return dynamic_cast<LegacyScriptPubKeyMan*>(it->second);
|
||||
}
|
||||
|
||||
LegacyDataSPKM* CWallet::GetLegacyDataSPKM() const
|
||||
{
|
||||
if (IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
|
||||
@ -3688,12 +3463,6 @@ LegacyDataSPKM* CWallet::GetLegacyDataSPKM() const
|
||||
return dynamic_cast<LegacyDataSPKM*>(it->second);
|
||||
}
|
||||
|
||||
LegacyScriptPubKeyMan* CWallet::GetOrCreateLegacyScriptPubKeyMan()
|
||||
{
|
||||
SetupLegacyScriptPubKeyMan();
|
||||
return GetLegacyScriptPubKeyMan();
|
||||
}
|
||||
|
||||
void CWallet::AddScriptPubKeyMan(const uint256& id, std::unique_ptr<ScriptPubKeyMan> spkm_man)
|
||||
{
|
||||
// Add spkm_man to m_spk_managers before calling any method
|
||||
@ -3716,9 +3485,8 @@ void CWallet::SetupLegacyScriptPubKeyMan()
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_ptr<ScriptPubKeyMan> spk_manager = m_database->Format() == "bdb_ro" ?
|
||||
std::make_unique<LegacyDataSPKM>(*this) :
|
||||
std::make_unique<LegacyScriptPubKeyMan>(*this, m_keypool_size);
|
||||
Assert(m_database->Format() == "bdb_ro" || m_database->Format() == "mock");
|
||||
std::unique_ptr<ScriptPubKeyMan> spk_manager = std::make_unique<LegacyDataSPKM>(*this);
|
||||
|
||||
for (const auto& type : LEGACY_OUTPUT_TYPES) {
|
||||
m_internal_spk_managers[type] = spk_manager.get();
|
||||
@ -3915,11 +3683,6 @@ void CWallet::DeactivateScriptPubKeyMan(uint256 id, OutputType type, bool intern
|
||||
NotifyCanGetAddressesChanged();
|
||||
}
|
||||
|
||||
bool CWallet::IsLegacy() const
|
||||
{
|
||||
return !IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS);
|
||||
}
|
||||
|
||||
DescriptorScriptPubKeyMan* CWallet::GetDescriptorScriptPubKeyMan(const WalletDescriptor& desc) const
|
||||
{
|
||||
auto spk_man_pair = m_spk_managers.find(desc.id);
|
||||
@ -3937,11 +3700,6 @@ DescriptorScriptPubKeyMan* CWallet::GetDescriptorScriptPubKeyMan(const WalletDes
|
||||
|
||||
std::optional<bool> CWallet::IsInternalScriptPubKeyMan(ScriptPubKeyMan* spk_man) const
|
||||
{
|
||||
// Legacy script pubkey man can't be either external or internal
|
||||
if (IsLegacy()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// only active ScriptPubKeyMan can be internal
|
||||
if (!GetActiveScriptPubKeyMans().count(spk_man)) {
|
||||
return std::nullopt;
|
||||
|
@ -558,9 +558,6 @@ public:
|
||||
SteadyClock::duration ScanningDuration() const { return fScanningWallet ? SteadyClock::now() - m_scanning_start.load() : SteadyClock::duration{}; }
|
||||
double ScanningProgress() const { return fScanningWallet ? (double) m_scanning_progress : 0; }
|
||||
|
||||
//! Upgrade stored CKeyMetadata objects to store key origin info as KeyOriginInfo
|
||||
void UpgradeKeyMetadata() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
||||
|
||||
//! Upgrade DescriptorCaches
|
||||
void UpgradeDescriptorCache() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
||||
|
||||
@ -583,7 +580,6 @@ public:
|
||||
bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase);
|
||||
bool EncryptWallet(const SecureString& strWalletPassphrase);
|
||||
|
||||
void GetKeyBirthTimes(std::map<CKeyID, int64_t> &mapKeyBirth) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
||||
unsigned int ComputeTimeSmart(const CWalletTx& wtx, bool rescanning_old_block) const;
|
||||
|
||||
/**
|
||||
@ -607,7 +603,7 @@ public:
|
||||
* Add the transaction to the wallet, wrapping it up inside a CWalletTx
|
||||
* @return the recently added wtx pointer or nullptr if there was a db write error.
|
||||
*/
|
||||
CWalletTx* AddToWallet(CTransactionRef tx, const TxState& state, const UpdateWalletTxFn& update_wtx=nullptr, bool fFlushOnClose=true, bool rescanning_old_block = false);
|
||||
CWalletTx* AddToWallet(CTransactionRef tx, const TxState& state, const UpdateWalletTxFn& update_wtx=nullptr, bool rescanning_old_block = false);
|
||||
bool LoadToWallet(const uint256& hash, const UpdateWalletTxFn& fill_wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
||||
void transactionAddedToMempool(const CTransactionRef& tx) override;
|
||||
void blockConnected(ChainstateRole role, const interfaces::BlockInfo& block) override;
|
||||
@ -822,9 +818,6 @@ public:
|
||||
//! Check if a given transaction has any of its outputs spent by another transaction in the wallet
|
||||
bool HasWalletSpend(const CTransactionRef& tx) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
||||
|
||||
//! Flush wallet (bitdb flush)
|
||||
void Flush();
|
||||
|
||||
//! Close wallet database
|
||||
void Close();
|
||||
|
||||
@ -920,9 +913,6 @@ public:
|
||||
/** Loads the flags into the wallet. (used by LoadWallet) */
|
||||
bool LoadWalletFlags(uint64_t flags);
|
||||
|
||||
/** Determine if we are a legacy wallet */
|
||||
bool IsLegacy() const;
|
||||
|
||||
/** Returns a bracketed wallet name for displaying in logs, will return [default wallet] if the wallet has no name */
|
||||
std::string GetDisplayName() const override
|
||||
{
|
||||
@ -963,8 +953,6 @@ public:
|
||||
std::vector<WalletDescriptor> GetWalletDescriptors(const CScript& script) const;
|
||||
|
||||
//! Get the LegacyScriptPubKeyMan which is used for all types, internal, and external.
|
||||
LegacyScriptPubKeyMan* GetLegacyScriptPubKeyMan() const;
|
||||
LegacyScriptPubKeyMan* GetOrCreateLegacyScriptPubKeyMan();
|
||||
LegacyDataSPKM* GetLegacyDataSPKM() const;
|
||||
LegacyDataSPKM* GetOrCreateLegacyDataSPKM();
|
||||
|
||||
|
@ -18,9 +18,6 @@
|
||||
#include <util/fs.h>
|
||||
#include <util/time.h>
|
||||
#include <util/translation.h>
|
||||
#ifdef USE_BDB
|
||||
#include <wallet/bdb.h>
|
||||
#endif
|
||||
#include <wallet/migrate.h>
|
||||
#include <wallet/sqlite.h>
|
||||
#include <wallet/wallet.h>
|
||||
@ -157,11 +154,6 @@ bool WalletBatch::EraseMasterKey(unsigned int id)
|
||||
return EraseIC(std::make_pair(DBKeys::MASTER_KEY, id));
|
||||
}
|
||||
|
||||
bool WalletBatch::WriteCScript(const uint160& hash, const CScript& redeemScript)
|
||||
{
|
||||
return WriteIC(std::make_pair(DBKeys::CSCRIPT, hash), redeemScript, false);
|
||||
}
|
||||
|
||||
bool WalletBatch::WriteWatchOnly(const CScript &dest, const CKeyMetadata& keyMeta)
|
||||
{
|
||||
if (!WriteIC(std::make_pair(DBKeys::WATCHMETA, dest), keyMeta)) {
|
||||
@ -206,21 +198,6 @@ bool WalletBatch::WriteOrderPosNext(int64_t nOrderPosNext)
|
||||
return WriteIC(DBKeys::ORDERPOSNEXT, nOrderPosNext);
|
||||
}
|
||||
|
||||
bool WalletBatch::ReadPool(int64_t nPool, CKeyPool& keypool)
|
||||
{
|
||||
return m_batch->Read(std::make_pair(DBKeys::POOL, nPool), keypool);
|
||||
}
|
||||
|
||||
bool WalletBatch::WritePool(int64_t nPool, const CKeyPool& keypool)
|
||||
{
|
||||
return WriteIC(std::make_pair(DBKeys::POOL, nPool), keypool);
|
||||
}
|
||||
|
||||
bool WalletBatch::ErasePool(int64_t nPool)
|
||||
{
|
||||
return EraseIC(std::make_pair(DBKeys::POOL, nPool));
|
||||
}
|
||||
|
||||
bool WalletBatch::WriteMinVersion(int nVersion)
|
||||
{
|
||||
return WriteIC(DBKeys::MINVERSION, nVersion);
|
||||
@ -747,18 +724,6 @@ static DBErrors LoadLegacyWalletRecords(CWallet* pwallet, DatabaseBatch& batch,
|
||||
});
|
||||
result = std::max(result, watch_meta_res.m_result);
|
||||
|
||||
// Load keypool
|
||||
LoadResult pool_res = LoadRecords(pwallet, batch, DBKeys::POOL,
|
||||
[] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) {
|
||||
int64_t nIndex;
|
||||
key >> nIndex;
|
||||
CKeyPool keypool;
|
||||
value >> keypool;
|
||||
pwallet->GetOrCreateLegacyDataSPKM()->LoadKeyPool(nIndex, keypool);
|
||||
return DBErrors::LOAD_OK;
|
||||
});
|
||||
result = std::max(result, pool_res.m_result);
|
||||
|
||||
// Deal with old "wkey" and "defaultkey" records.
|
||||
// These are not actually loaded, but we need to check for them
|
||||
|
||||
@ -794,15 +759,6 @@ static DBErrors LoadLegacyWalletRecords(CWallet* pwallet, DatabaseBatch& batch,
|
||||
// Only do logging and time first key update if there were no critical errors
|
||||
pwallet->WalletLogPrintf("Legacy Wallet Keys: %u plaintext, %u encrypted, %u w/ metadata, %u total.\n",
|
||||
key_res.m_records, ckey_res.m_records, keymeta_res.m_records, key_res.m_records + ckey_res.m_records);
|
||||
|
||||
// nTimeFirstKey is only reliable if all keys have metadata
|
||||
if (pwallet->IsLegacy() && (key_res.m_records + ckey_res.m_records + watch_script_res.m_records) != (keymeta_res.m_records + watch_meta_res.m_records)) {
|
||||
auto spk_man = pwallet->GetLegacyScriptPubKeyMan();
|
||||
if (spk_man) {
|
||||
LOCK(spk_man->cs_KeyStore);
|
||||
spk_man->UpdateTimeFirstKey(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -1256,14 +1212,6 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
|
||||
if (any_unordered)
|
||||
result = pwallet->ReorderTransactions();
|
||||
|
||||
// Upgrade all of the wallet keymetadata to have the hd master key id
|
||||
// This operation is not atomic, but if it fails, updated entries are still backwards compatible with older software
|
||||
try {
|
||||
pwallet->UpgradeKeyMetadata();
|
||||
} catch (...) {
|
||||
result = DBErrors::CORRUPT;
|
||||
}
|
||||
|
||||
// Upgrade all of the descriptor caches to cache the last hardened xpub
|
||||
// This operation is not atomic, but if it fails, only new entries are added so it is backwards compatible
|
||||
try {
|
||||
@ -1319,33 +1267,6 @@ bool RunWithinTxn(WalletDatabase& database, std::string_view process_desc, const
|
||||
return RunWithinTxn(batch, process_desc, func);
|
||||
}
|
||||
|
||||
void MaybeCompactWalletDB(WalletContext& context)
|
||||
{
|
||||
static std::atomic<bool> fOneThread(false);
|
||||
if (fOneThread.exchange(true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const std::shared_ptr<CWallet>& pwallet : GetWallets(context)) {
|
||||
WalletDatabase& dbh = pwallet->GetDatabase();
|
||||
|
||||
unsigned int nUpdateCounter = dbh.nUpdateCounter;
|
||||
|
||||
if (dbh.nLastSeen != nUpdateCounter) {
|
||||
dbh.nLastSeen = nUpdateCounter;
|
||||
dbh.nLastWalletUpdate = GetTime();
|
||||
}
|
||||
|
||||
if (dbh.nLastFlushed != nUpdateCounter && GetTime() - dbh.nLastWalletUpdate >= 2) {
|
||||
if (dbh.PeriodicFlush()) {
|
||||
dbh.nLastFlushed = nUpdateCounter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fOneThread = false;
|
||||
}
|
||||
|
||||
bool WalletBatch::WriteAddressPreviouslySpent(const CTxDestination& dest, bool previously_spent)
|
||||
{
|
||||
auto key{std::make_pair(DBKeys::DESTDATA, std::make_pair(EncodeDestination(dest), std::string("used")))};
|
||||
@ -1369,11 +1290,6 @@ bool WalletBatch::EraseAddressData(const CTxDestination& dest)
|
||||
return m_batch->ErasePrefix(prefix);
|
||||
}
|
||||
|
||||
bool WalletBatch::WriteHDChain(const CHDChain& chain)
|
||||
{
|
||||
return WriteIC(DBKeys::HDCHAIN, chain);
|
||||
}
|
||||
|
||||
bool WalletBatch::WriteWalletFlags(const uint64_t flags)
|
||||
{
|
||||
return WriteIC(DBKeys::FLAGS, flags);
|
||||
|
@ -21,7 +21,6 @@ class uint256;
|
||||
struct CBlockLocator;
|
||||
|
||||
namespace wallet {
|
||||
class CKeyPool;
|
||||
class CMasterKey;
|
||||
class CWallet;
|
||||
class CWalletTx;
|
||||
@ -32,15 +31,8 @@ struct WalletContext;
|
||||
*
|
||||
* - WalletBatch is an abstract modifier object for the wallet database, and encapsulates a database
|
||||
* batch update as well as methods to act on the database. It should be agnostic to the database implementation.
|
||||
*
|
||||
* The following classes are implementation specific:
|
||||
* - BerkeleyEnvironment is an environment in which the database exists.
|
||||
* - BerkeleyDatabase represents a wallet database.
|
||||
* - BerkeleyBatch is a low-level database batch update.
|
||||
*/
|
||||
|
||||
static const bool DEFAULT_FLUSHWALLET = true;
|
||||
|
||||
/** Error statuses for the wallet database.
|
||||
* Values are in order of severity. When multiple errors occur, the most severe (highest value) will be returned.
|
||||
*/
|
||||
@ -202,10 +194,6 @@ private:
|
||||
if (!m_batch->Write(key, value, fOverwrite)) {
|
||||
return false;
|
||||
}
|
||||
m_database.IncrementUpdateCounter();
|
||||
if (m_database.nUpdateCounter % 1000 == 0) {
|
||||
m_batch->Flush();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -215,17 +203,12 @@ private:
|
||||
if (!m_batch->Erase(key)) {
|
||||
return false;
|
||||
}
|
||||
m_database.IncrementUpdateCounter();
|
||||
if (m_database.nUpdateCounter % 1000 == 0) {
|
||||
m_batch->Flush();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
explicit WalletBatch(WalletDatabase &database, bool _fFlushOnClose = true) :
|
||||
m_batch(database.MakeBatch(_fFlushOnClose)),
|
||||
m_database(database)
|
||||
explicit WalletBatch(WalletDatabase &database) :
|
||||
m_batch(database.MakeBatch())
|
||||
{
|
||||
}
|
||||
WalletBatch(const WalletBatch&) = delete;
|
||||
@ -246,8 +229,6 @@ public:
|
||||
bool WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey);
|
||||
bool EraseMasterKey(unsigned int id);
|
||||
|
||||
bool WriteCScript(const uint160& hash, const CScript& redeemScript);
|
||||
|
||||
bool WriteWatchOnly(const CScript &script, const CKeyMetadata &keymeta);
|
||||
bool EraseWatchOnly(const CScript &script);
|
||||
|
||||
@ -259,10 +240,6 @@ public:
|
||||
|
||||
bool WriteOrderPosNext(int64_t nOrderPosNext);
|
||||
|
||||
bool ReadPool(int64_t nPool, CKeyPool& keypool);
|
||||
bool WritePool(int64_t nPool, const CKeyPool& keypool);
|
||||
bool ErasePool(int64_t nPool);
|
||||
|
||||
bool WriteMinVersion(int nVersion);
|
||||
|
||||
bool WriteDescriptorKey(const uint256& desc_id, const CPubKey& pubkey, const CPrivKey& privkey);
|
||||
@ -286,9 +263,6 @@ public:
|
||||
|
||||
DBErrors LoadWallet(CWallet* pwallet);
|
||||
|
||||
//! write the hdchain model (external chain child index counter)
|
||||
bool WriteHDChain(const CHDChain& chain);
|
||||
|
||||
//! Delete records of the given types
|
||||
bool EraseRecords(const std::unordered_set<std::string>& types);
|
||||
|
||||
@ -306,7 +280,6 @@ public:
|
||||
|
||||
private:
|
||||
std::unique_ptr<DatabaseBatch> m_batch;
|
||||
WalletDatabase& m_database;
|
||||
|
||||
// External functions listening to the current db txn outcome.
|
||||
// Listeners are cleared at the end of the transaction.
|
||||
@ -327,9 +300,6 @@ private:
|
||||
*/
|
||||
bool RunWithinTxn(WalletDatabase& database, std::string_view process_desc, const std::function<bool(WalletBatch&)>& func);
|
||||
|
||||
//! Compacts BDB state so that wallet.dat is self-contained (if there are changes)
|
||||
void MaybeCompactWalletDB(WalletContext& context);
|
||||
|
||||
bool LoadKey(CWallet* pwallet, DataStream& ssKey, DataStream& ssValue, std::string& strErr);
|
||||
bool LoadCryptedKey(CWallet* pwallet, DataStream& ssKey, DataStream& ssValue, std::string& strErr);
|
||||
bool LoadEncryptionKey(CWallet* pwallet, DataStream& ssKey, DataStream& ssValue, std::string& strErr);
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <wallet/wallettool.h>
|
||||
|
||||
#include <common/args.h>
|
||||
#include <util/check.h>
|
||||
#include <util/fs.h>
|
||||
#include <util/translation.h>
|
||||
#include <wallet/dump.h>
|
||||
@ -33,12 +34,8 @@ static void WalletCreate(CWallet* wallet_instance, uint64_t wallet_creation_flag
|
||||
wallet_instance->SetMinVersion(FEATURE_LATEST);
|
||||
wallet_instance->InitWalletFlags(wallet_creation_flags);
|
||||
|
||||
if (!wallet_instance->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
|
||||
auto spk_man = wallet_instance->GetOrCreateLegacyScriptPubKeyMan();
|
||||
spk_man->SetupGeneration(false);
|
||||
} else {
|
||||
wallet_instance->SetupDescriptorScriptPubKeyMans();
|
||||
}
|
||||
Assert(wallet_instance->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS));
|
||||
wallet_instance->SetupDescriptorScriptPubKeyMans();
|
||||
|
||||
tfm::format(std::cout, "Topping up keypool...\n");
|
||||
wallet_instance->TopUpKeyPool();
|
||||
@ -168,7 +165,7 @@ bool ExecuteWalletToolFunc(const ArgsManager& args, const std::string& command)
|
||||
options.require_existing = true;
|
||||
DatabaseStatus status;
|
||||
|
||||
if (args.GetBoolArg("-withinternalbdb", false) && IsBDBFile(BDBDataFile(path))) {
|
||||
if (IsBDBFile(BDBDataFile(path))) {
|
||||
options.require_format = DatabaseFormat::BERKELEY_RO;
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,6 @@ function(create_test_config)
|
||||
endmacro()
|
||||
|
||||
set_configure_variable(ENABLE_WALLET ENABLE_WALLET)
|
||||
set_configure_variable(WITH_BDB USE_BDB)
|
||||
set_configure_variable(BUILD_CLI BUILD_BITCOIN_CLI)
|
||||
set_configure_variable(BUILD_UTIL BUILD_BITCOIN_UTIL)
|
||||
set_configure_variable(BUILD_UTIL_CHAINSTATE BUILD_BITCOIN_CHAINSTATE)
|
||||
|
@ -16,7 +16,6 @@ RPCAUTH=@abs_top_srcdir@/share/rpcauth/rpcauth.py
|
||||
[components]
|
||||
# Which components are enabled. These are commented out by cmake if they were disabled during configuration.
|
||||
@ENABLE_WALLET_TRUE@ENABLE_WALLET=true
|
||||
@USE_BDB_TRUE@USE_BDB=true
|
||||
@BUILD_BITCOIN_CLI_TRUE@ENABLE_CLI=true
|
||||
@BUILD_BITCOIN_UTIL_TRUE@ENABLE_BITCOIN_UTIL=true
|
||||
@BUILD_BITCOIN_CHAINSTATE_TRUE@ENABLE_BITCOIN_CHAINSTATE=true
|
||||
|
@ -55,7 +55,6 @@ class BIP68Test(BitcoinTestFramework):
|
||||
'-testactivationheight=csv@432',
|
||||
],
|
||||
]
|
||||
self.uses_wallet = None
|
||||
|
||||
def run_test(self):
|
||||
self.relayfee = self.nodes[0].getnetworkinfo()["relayfee"]
|
||||
|
@ -51,6 +51,7 @@ class RpcCreateMultiSigTest(BitcoinTestFramework):
|
||||
for output_type in ["bech32", "p2sh-segwit", "legacy"]:
|
||||
self.do_multisig(keys, sigs, output_type)
|
||||
|
||||
self.test_multisig_script_limit()
|
||||
self.test_mixing_uncompressed_and_compressed_keys(node0)
|
||||
self.test_sortedmulti_descriptors_bip67()
|
||||
|
||||
|
@ -74,7 +74,6 @@ class RawTransactionsTest(BitcoinTestFramework):
|
||||
# whitelist peers to speed up tx relay / mempool sync
|
||||
self.noban_tx_relay = True
|
||||
self.supports_cli = False
|
||||
self.uses_wallet = None
|
||||
|
||||
def setup_network(self):
|
||||
super().setup_network()
|
||||
|
@ -1,210 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2020-2021 The Bitcoin Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
"""
|
||||
Utilities for working directly with the wallet's BDB database file
|
||||
|
||||
This is specific to the configuration of BDB used in this project:
|
||||
- Outer database contains single subdatabase named 'main'
|
||||
- btree
|
||||
- btree internal, leaf and overflow pages
|
||||
|
||||
Each key-value pair is two entries in a btree leaf, which optionally refers to overflow pages
|
||||
if the data doesn't fit into a single page. The first entry is the key, the one that follows
|
||||
is the value. And so on. Note that the entry data is itself not in the correct order. Instead
|
||||
entry offsets are stored in the correct order and those offsets are needed to then retrieve
|
||||
the data itself. Note that this implementation currently only supports reading databases that
|
||||
are in the same endianness as the host.
|
||||
|
||||
Page format can be found in BDB source code dbinc/db_page.h
|
||||
|
||||
`db_dump -da wallet.dat` is useful to see the data in a wallet.dat BDB file
|
||||
"""
|
||||
|
||||
import struct
|
||||
|
||||
# Important constants
|
||||
PAGE_HEADER_SIZE = 26
|
||||
OUTER_META_PAGE = 0
|
||||
|
||||
# Page type values
|
||||
BTREE_INTERNAL = 3
|
||||
BTREE_LEAF = 5
|
||||
OVERFLOW_DATA = 7
|
||||
BTREE_META = 9
|
||||
|
||||
# Record type values
|
||||
RECORD_KEYDATA = 1
|
||||
RECORD_OVERFLOW_DATA = 3
|
||||
|
||||
# Some magic numbers for sanity checking
|
||||
BTREE_MAGIC = 0x053162
|
||||
DB_VERSION = 9
|
||||
SUBDATABASE_NAME = b'main'
|
||||
|
||||
# Deserializes an internal, leaf or overflow page into a dict.
|
||||
# In addition to the common page header fields, the result contains an 'entries'
|
||||
# array of dicts with the following fields, depending on the page type:
|
||||
# internal page [BTREE_INTERNAL]:
|
||||
# - 'page_num': referenced page number (used to find further pages to process)
|
||||
# leaf page [BTREE_LEAF]:
|
||||
# - 'record_type': record type, must be RECORD_KEYDATA or RECORD_OVERFLOW_DATA
|
||||
# - 'data': binary data (key or value payload), if record type is RECORD_KEYDATA
|
||||
# - 'page_num': referenced overflow page number, if record type is RECORD_OVERFLOW_DATA
|
||||
# overflow page [OVERFLOW_DATA]:
|
||||
# - 'data': binary data (part of key or value payload)
|
||||
def dump_page(data):
|
||||
page_info = {}
|
||||
page_header = data[0:26]
|
||||
_, pgno, prev_pgno, next_pgno, entries, hf_offset, level, pg_type = struct.unpack('QIIIHHBB', page_header)
|
||||
page_info['pgno'] = pgno
|
||||
page_info['prev_pgno'] = prev_pgno
|
||||
page_info['next_pgno'] = next_pgno
|
||||
page_info['hf_offset'] = hf_offset
|
||||
page_info['level'] = level
|
||||
page_info['pg_type'] = pg_type
|
||||
page_info['entry_offsets'] = struct.unpack('{}H'.format(entries), data[26:26 + entries * 2])
|
||||
page_info['entries'] = []
|
||||
|
||||
assert pg_type in (BTREE_INTERNAL, BTREE_LEAF, OVERFLOW_DATA)
|
||||
|
||||
if pg_type == OVERFLOW_DATA:
|
||||
assert entries == 1
|
||||
page_info['entries'].append({'data': data[26:26 + hf_offset]})
|
||||
return page_info
|
||||
|
||||
for i in range(0, entries):
|
||||
entry = {}
|
||||
offset = page_info['entry_offsets'][i]
|
||||
record_header = data[offset:offset + 3]
|
||||
offset += 3
|
||||
e_len, record_type = struct.unpack('HB', record_header)
|
||||
|
||||
if pg_type == BTREE_INTERNAL:
|
||||
assert record_type == RECORD_KEYDATA
|
||||
internal_record_data = data[offset:offset + 9]
|
||||
_, page_num, _ = struct.unpack('=BII', internal_record_data)
|
||||
entry['page_num'] = page_num
|
||||
elif pg_type == BTREE_LEAF:
|
||||
assert record_type in (RECORD_KEYDATA, RECORD_OVERFLOW_DATA)
|
||||
entry['record_type'] = record_type
|
||||
if record_type == RECORD_KEYDATA:
|
||||
entry['data'] = data[offset:offset + e_len]
|
||||
elif record_type == RECORD_OVERFLOW_DATA:
|
||||
overflow_record_data = data[offset:offset + 9]
|
||||
_, page_num, _ = struct.unpack('=BII', overflow_record_data)
|
||||
entry['page_num'] = page_num
|
||||
|
||||
page_info['entries'].append(entry)
|
||||
|
||||
return page_info
|
||||
|
||||
# Deserializes a btree metadata page into a dict.
|
||||
# Does a simple sanity check on the magic value, type, and version
|
||||
def dump_meta_page(page):
|
||||
# metadata page
|
||||
# general metadata
|
||||
metadata = {}
|
||||
meta_page = page[0:72]
|
||||
_, pgno, magic, version, pagesize, encrypt_alg, pg_type, metaflags, _, free, last_pgno, nparts, key_count, record_count, flags, uid = struct.unpack('QIIIIBBBBIIIIII20s', meta_page)
|
||||
metadata['pgno'] = pgno
|
||||
metadata['magic'] = magic
|
||||
metadata['version'] = version
|
||||
metadata['pagesize'] = pagesize
|
||||
metadata['encrypt_alg'] = encrypt_alg
|
||||
metadata['pg_type'] = pg_type
|
||||
metadata['metaflags'] = metaflags
|
||||
metadata['free'] = free
|
||||
metadata['last_pgno'] = last_pgno
|
||||
metadata['nparts'] = nparts
|
||||
metadata['key_count'] = key_count
|
||||
metadata['record_count'] = record_count
|
||||
metadata['flags'] = flags
|
||||
metadata['uid'] = uid.hex().encode()
|
||||
|
||||
assert magic == BTREE_MAGIC, 'bdb magic does not match bdb btree magic'
|
||||
assert pg_type == BTREE_META, 'Metadata page is not a btree metadata page'
|
||||
assert version == DB_VERSION, 'Database too new'
|
||||
|
||||
# btree metadata
|
||||
btree_meta_page = page[72:512]
|
||||
_, minkey, re_len, re_pad, root, _, crypto_magic, _, iv, chksum = struct.unpack('IIIII368sI12s16s20s', btree_meta_page)
|
||||
metadata['minkey'] = minkey
|
||||
metadata['re_len'] = re_len
|
||||
metadata['re_pad'] = re_pad
|
||||
metadata['root'] = root
|
||||
metadata['crypto_magic'] = crypto_magic
|
||||
metadata['iv'] = iv.hex().encode()
|
||||
metadata['chksum'] = chksum.hex().encode()
|
||||
|
||||
return metadata
|
||||
|
||||
# Given the dict from dump_leaf_page, get the key-value pairs and put them into a dict
|
||||
def extract_kv_pairs(page_data, pages):
|
||||
out = {}
|
||||
last_key = None
|
||||
for i, entry in enumerate(page_data['entries']):
|
||||
data = b''
|
||||
if entry['record_type'] == RECORD_KEYDATA:
|
||||
data = entry['data']
|
||||
elif entry['record_type'] == RECORD_OVERFLOW_DATA:
|
||||
next_page = entry['page_num']
|
||||
while next_page != 0:
|
||||
opage = pages[next_page]
|
||||
opage_info = dump_page(opage)
|
||||
data += opage_info['entries'][0]['data']
|
||||
next_page = opage_info['next_pgno']
|
||||
|
||||
# By virtue of these all being pairs, even number entries are keys, and odd are values
|
||||
if i % 2 == 0:
|
||||
out[entry['data']] = b''
|
||||
last_key = data
|
||||
else:
|
||||
out[last_key] = data
|
||||
return out
|
||||
|
||||
# Extract the key-value pairs of the BDB file given in filename
|
||||
def dump_bdb_kv(filename):
|
||||
# Read in the BDB file and start deserializing it
|
||||
pages = []
|
||||
with open(filename, 'rb') as f:
|
||||
# Determine pagesize first
|
||||
data = f.read(PAGE_HEADER_SIZE)
|
||||
pagesize = struct.unpack('I', data[20:24])[0]
|
||||
assert pagesize in (512, 1024, 2048, 4096, 8192, 16384, 32768, 65536)
|
||||
|
||||
# Read rest of first page
|
||||
data += f.read(pagesize - PAGE_HEADER_SIZE)
|
||||
assert len(data) == pagesize
|
||||
|
||||
# Read all remaining pages
|
||||
while len(data) > 0:
|
||||
pages.append(data)
|
||||
data = f.read(pagesize)
|
||||
|
||||
# Sanity check the meta pages, read root page
|
||||
outer_meta_info = dump_meta_page(pages[OUTER_META_PAGE])
|
||||
root_page_info = dump_page(pages[outer_meta_info['root']])
|
||||
assert root_page_info['pg_type'] == BTREE_LEAF
|
||||
assert len(root_page_info['entries']) == 2
|
||||
assert root_page_info['entries'][0]['data'] == SUBDATABASE_NAME
|
||||
assert len(root_page_info['entries'][1]['data']) == 4
|
||||
inner_meta_page = int.from_bytes(root_page_info['entries'][1]['data'], 'big')
|
||||
inner_meta_info = dump_meta_page(pages[inner_meta_page])
|
||||
|
||||
# Fetch the kv pairs from the pages
|
||||
kv = {}
|
||||
pages_to_process = [inner_meta_info['root']]
|
||||
while len(pages_to_process) > 0:
|
||||
curr_page_no = pages_to_process.pop()
|
||||
assert curr_page_no <= outer_meta_info['last_pgno']
|
||||
info = dump_page(pages[curr_page_no])
|
||||
assert info['pg_type'] in (BTREE_INTERNAL, BTREE_LEAF)
|
||||
if info['pg_type'] == BTREE_INTERNAL:
|
||||
for entry in info['entries']:
|
||||
pages_to_process.append(entry['page_num'])
|
||||
elif info['pg_type'] == BTREE_LEAF:
|
||||
info_kv = extract_kv_pairs(info, pages)
|
||||
kv = {**kv, **info_kv}
|
||||
return kv
|
@ -161,7 +161,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
|
||||
self.wallet_names = None
|
||||
# By default the wallet is not required. Set to true by skip_if_no_wallet().
|
||||
# Can also be set to None to indicate that the wallet will be used if available.
|
||||
# When False or None, we ignore wallet_names regardless of what it is.
|
||||
# When False or None, we ignore wallet_names in setup_nodes().
|
||||
self.uses_wallet = False
|
||||
# Disable ThreadOpenConnections by default, so that adding entries to
|
||||
# addrman will not result in automatic connections to them.
|
||||
@ -487,10 +487,6 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
|
||||
n.createwallet(wallet_name=wallet_name, load_on_startup=True)
|
||||
n.importprivkey(privkey=n.get_deterministic_priv_key().key, label='coinbase', rescan=True)
|
||||
|
||||
# Only enables wallet support when the module is available
|
||||
def enable_wallet_if_possible(self):
|
||||
self._requires_wallet = self.is_wallet_compiled()
|
||||
|
||||
def run_test(self):
|
||||
"""Tests must override this method to define test logic"""
|
||||
raise NotImplementedError
|
||||
@ -982,11 +978,6 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
|
||||
if not self.is_wallet_compiled():
|
||||
raise SkipTest("wallet has not been compiled.")
|
||||
|
||||
def skip_if_no_bdb(self):
|
||||
"""Skip the running test if BDB has not been compiled."""
|
||||
if not self.is_bdb_compiled():
|
||||
raise SkipTest("BDB has not been compiled.")
|
||||
|
||||
def skip_if_no_wallet_tool(self):
|
||||
"""Skip the running test if bitcoin-wallet has not been compiled."""
|
||||
if not self.is_wallet_tool_compiled():
|
||||
@ -1057,9 +1048,5 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
|
||||
"""Checks whether the USDT tracepoints were compiled."""
|
||||
return self.config["components"].getboolean("ENABLE_USDT_TRACEPOINTS")
|
||||
|
||||
def is_bdb_compiled(self):
|
||||
"""Checks whether the wallet module was compiled with BDB support."""
|
||||
return self.config["components"].getboolean("USE_BDB")
|
||||
|
||||
def has_blockfile(self, node, filenum: str):
|
||||
return (node.blocks_path/ f"blk{filenum}.dat").is_file()
|
||||
|
@ -459,7 +459,6 @@ def write_config(config_path, *, n, chain, extra_config="", disable_autoconnect=
|
||||
f.write("printtoconsole=0\n")
|
||||
f.write("natpmp=0\n")
|
||||
f.write("shrinkdebugfile=0\n")
|
||||
f.write("deprecatedrpc=create_bdb\n") # Required to run the tests
|
||||
# To improve SQLite wallet performance so that the tests don't timeout, use -unsafesqlitesync
|
||||
f.write("unsafesqlitesync=1\n")
|
||||
if disable_autoconnect:
|
||||
|
@ -29,8 +29,6 @@ import tempfile
|
||||
import re
|
||||
import logging
|
||||
|
||||
os.environ["REQUIRE_WALLET_TYPE_SET"] = "1"
|
||||
|
||||
# Minimum amount of space to run the tests.
|
||||
MIN_FREE_SPACE = 1.1 * 1024 * 1024 * 1024
|
||||
# Additional space to run an extra job.
|
||||
|
@ -20,16 +20,10 @@ from test_framework.util import (
|
||||
|
||||
|
||||
class ToolWalletTest(BitcoinTestFramework):
|
||||
def add_options(self, parser):
|
||||
parser.add_argument("--bdbro", action="store_true", help="Use the BerkeleyRO internal parser when dumping a Berkeley DB wallet file")
|
||||
parser.add_argument("--swap-bdb-endian", action="store_true",help="When making Legacy BDB wallets, always make then byte swapped internally")
|
||||
|
||||
def set_test_params(self):
|
||||
self.num_nodes = 1
|
||||
self.setup_clean_chain = True
|
||||
self.rpc_timeout = 120
|
||||
if self.options.swap_bdb_endian:
|
||||
self.extra_args = [["-swapbdbendian"]]
|
||||
|
||||
def skip_test_if_missing_module(self):
|
||||
self.skip_if_no_wallet()
|
||||
@ -37,8 +31,6 @@ class ToolWalletTest(BitcoinTestFramework):
|
||||
|
||||
def bitcoin_wallet_process(self, *args):
|
||||
default_args = ['-datadir={}'.format(self.nodes[0].datadir_path), '-chain=%s' % self.chain]
|
||||
if "dump" in args and self.options.bdbro:
|
||||
default_args.append("-withinternalbdb")
|
||||
|
||||
return subprocess.Popen(self.get_binaries().wallet_argv() + default_args + list(args), stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||
|
||||
|
@ -15,7 +15,6 @@ from test_framework.wallet_util import generate_keypair, WalletUnlock
|
||||
|
||||
|
||||
EMPTY_PASSPHRASE_MSG = "Empty string given as passphrase, wallet will not be encrypted."
|
||||
LEGACY_WALLET_MSG = "Wallet created successfully. The legacy wallet type is being deprecated and support for creating and opening legacy wallets will be removed in the future."
|
||||
|
||||
|
||||
class CreateWalletTest(BitcoinTestFramework):
|
||||
@ -27,7 +26,6 @@ class CreateWalletTest(BitcoinTestFramework):
|
||||
|
||||
def run_test(self):
|
||||
node = self.nodes[0]
|
||||
self.generate(node, 1) # Leave IBD for sethdseed
|
||||
|
||||
self.log.info("Run createwallet with invalid parameters.")
|
||||
# Run createwallet with invalid parameters. This must not prevent a new wallet with the same name from being created with the correct parameters.
|
||||
|
@ -137,18 +137,6 @@ class WalletDescriptorTest(BitcoinTestFramework):
|
||||
addr = recv_wrpc.getnewaddress()
|
||||
send_wrpc.sendtoaddress(addr, 10)
|
||||
|
||||
# Make sure things are disabled
|
||||
self.log.info("Test disabled RPCs")
|
||||
assert_raises_rpc_error(-4, "Only legacy wallets are supported by this command", recv_wrpc.rpc.importprivkey, "cVpF924EspNh8KjYsfhgY96mmxvT6DgdWiTYMtMjuM74hJaU5psW")
|
||||
assert_raises_rpc_error(-4, "Only legacy wallets are supported by this command", recv_wrpc.rpc.importpubkey, send_wrpc.getaddressinfo(send_wrpc.getnewaddress())["pubkey"])
|
||||
assert_raises_rpc_error(-4, "Only legacy wallets are supported by this command", recv_wrpc.rpc.importaddress, recv_wrpc.getnewaddress())
|
||||
assert_raises_rpc_error(-4, "Only legacy wallets are supported by this command", recv_wrpc.rpc.importmulti, [])
|
||||
assert_raises_rpc_error(-4, "Only legacy wallets are supported by this command", recv_wrpc.rpc.addmultisigaddress, 1, [recv_wrpc.getnewaddress()])
|
||||
assert_raises_rpc_error(-4, "Only legacy wallets are supported by this command", recv_wrpc.rpc.dumpprivkey, recv_wrpc.getnewaddress())
|
||||
assert_raises_rpc_error(-4, "Only legacy wallets are supported by this command", recv_wrpc.rpc.dumpwallet, 'wallet.dump')
|
||||
assert_raises_rpc_error(-4, "Only legacy wallets are supported by this command", recv_wrpc.rpc.importwallet, 'wallet.dump')
|
||||
assert_raises_rpc_error(-4, "Only legacy wallets are supported by this command", recv_wrpc.rpc.sethdseed)
|
||||
|
||||
self.log.info("Test encryption")
|
||||
# Get the master fingerprint before encrypt
|
||||
info1 = send_wrpc.getaddressinfo(send_wrpc.getnewaddress())
|
||||
|
@ -43,7 +43,6 @@ class WalletHDTest(BitcoinTestFramework):
|
||||
|
||||
# This should be enough to keep the master key and the non-HD key
|
||||
self.nodes[1].backupwallet(self.nodes[1].datadir_path / "hd.bak")
|
||||
#self.nodes[1].dumpwallet(self.nodes[1].datadir_path / "hd.dump")
|
||||
|
||||
# Derive some HD addresses and remember the last
|
||||
# Also send funds to each add
|
||||
|
@ -15,7 +15,6 @@ from test_framework.address import (
|
||||
script_to_p2sh,
|
||||
script_to_p2wsh,
|
||||
)
|
||||
from test_framework.bdb import BTREE_MAGIC
|
||||
from test_framework.descriptors import descsum_create
|
||||
from test_framework.key import ECPubKey
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
@ -33,6 +32,8 @@ from test_framework.wallet_util import (
|
||||
generate_keypair,
|
||||
)
|
||||
|
||||
BTREE_MAGIC = 0x053162
|
||||
|
||||
|
||||
class WalletMigrationTest(BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
@ -46,10 +47,14 @@ class WalletMigrationTest(BitcoinTestFramework):
|
||||
self.skip_if_no_previous_releases()
|
||||
|
||||
def setup_nodes(self):
|
||||
self.add_nodes(self.num_nodes, versions=[
|
||||
None,
|
||||
280000,
|
||||
])
|
||||
self.add_nodes(
|
||||
self.num_nodes,
|
||||
extra_args=self.extra_args,
|
||||
versions=[
|
||||
None,
|
||||
280000,
|
||||
],
|
||||
)
|
||||
self.start_nodes()
|
||||
self.init_wallet(node=0)
|
||||
|
||||
|
@ -45,7 +45,6 @@ KNOWN_VIOLATIONS = [
|
||||
"src/dbwrapper.cpp:.*vsnprintf",
|
||||
"src/test/fuzz/locale.cpp:.*setlocale",
|
||||
"src/test/util_tests.cpp:.*strtoll",
|
||||
"src/wallet/bdb.cpp:.*DbEnv::strerror", # False positive
|
||||
"src/util/syserror.cpp:.*strerror", # Outside this function use `SysErrorString`
|
||||
]
|
||||
|
||||
|
@ -5,9 +5,6 @@
|
||||
|
||||
# race (TODO fix)
|
||||
race:LoadWallet
|
||||
race:WalletBatch::WriteHDChain
|
||||
race:BerkeleyBatch
|
||||
race:BerkeleyDatabase
|
||||
race:DatabaseBatch
|
||||
race:zmq::*
|
||||
race:bitcoin-qt
|
||||
@ -25,8 +22,6 @@ race:src/qt/test/*
|
||||
deadlock:src/qt/test/*
|
||||
|
||||
# External libraries
|
||||
# https://github.com/bitcoin/bitcoin/pull/27658#issuecomment-1547639621
|
||||
deadlock:libdb
|
||||
race:libzmq
|
||||
|
||||
# Intermittent issues
|
||||
|
Loading…
Reference in New Issue
Block a user