Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F13115063
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
519 KB
Subscribers
None
View Options
This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index bd9479d42..ec400ea5d 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,548 +1,549 @@
# Copyright (c) 2017 The Bitcoin developers
cmake_minimum_required(VERSION 3.12)
project(bitcoind)
set(CMAKE_CXX_STANDARD 14)
# Default visibility is hidden on all targets.
set(CMAKE_C_VISIBILITY_PRESET hidden)
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
option(BUILD_BITCOIN_WALLET "Activate the wallet functionality" ON)
option(BUILD_BITCOIN_ZMQ "Activate the ZeroMQ functionalities" ON)
option(BUILD_BITCOIN_SEEDER "Build bitcoin-seeder" ON)
option(BUILD_BITCOIN_CLI "Build bitcoin-cli" ON)
option(BUILD_BITCOIN_TX "Build bitcoin-tx" ON)
option(BUILD_BITCOIN_QT "Build bitcoin-qt" ON)
option(BUILD_LIBBITCOINCONSENSUS "Build the bitcoinconsenus shared library" ON)
option(ENABLE_HARDENING "Harden the executables" ON)
option(ENABLE_REDUCE_EXPORTS "Reduce the amount of exported symbols" OFF)
option(ENABLE_STATIC_LIBSTDCXX "Statically link libstdc++" OFF)
option(ENABLE_GLIBC_BACK_COMPAT "Enable Glibc compatibility features" OFF)
option(ENABLE_QRCODE "Enable QR code display" ON)
option(ENABLE_UPNP "Enable UPnP support" ON)
option(START_WITH_UPNP "Make UPnP the default to map ports" OFF)
# Allow usage of sanitizers by setting ECM_ENABLE_SANITIZERS
if(ENABLE_SANITIZERS)
set(ECM_ENABLE_SANITIZERS ${ENABLE_SANITIZERS})
find_package(ECM NO_MODULE)
if(ECM_MODULE_PATH)
list(APPEND CMAKE_MODULE_PATH ${ECM_MODULE_PATH})
include(ECMEnableSanitizers)
else()
message(FATAL_ERROR
"ECM is required to enable the sanitizers (https://api.kde.org/ecm/index.html)"
)
endif()
endif()
# Cmake uses the CMAKE_BUILD_TYPE variable to select the build configuration.
# By default it supports more configurations that needed for Bitcoin ABC, and
# all the releases types set NDEBUG which is unwanted as it disables the assert
# completely.
# Remove the -DNDEBUG flag from the CFLAGS/CXXFLAGS in all the configurations
include(AddCompilerFlags)
remove_compiler_flags(-DNDEBUG)
# Overrides the flags for the Debug build type
# This mimics the autotools behavior by setting the CFLAGS to '-g -O2`, which
# are not well suited for debugging.
# FIXME: update CFLAGS with better debug oriented optimization flags
set(CMAKE_C_FLAGS_DEBUG "-g -O2")
# Prefer -g3, defaults to -g if unavailable
add_cxx_compiler_flag_with_fallback(CMAKE_CXX_FLAGS_DEBUG -g3 -g)
# Prefer -Og, defaults to -O0 if unavailable
add_cxx_compiler_flag_with_fallback(CMAKE_CXX_FLAGS_DEBUG -Og -O0)
# Define the debugging symbols DEBUG and DEBUG_LOCKORDER when the Debug build
# type is selected.
string(APPEND CMAKE_CXX_FLAGS_DEBUG " -DDEBUG -DDEBUG_LOCKORDER")
# Add -ftrapv when building in Debug
add_compiler_flags_to_var(CMAKE_CXX_FLAGS_DEBUG CXX -ftrapv)
# Ensure that WINDRES_PREPROC is enabled when using windres.
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
# Ensure that WINDRES_PREPROC is enabled when using windres.
list(APPEND CMAKE_RC_FLAGS "-DWINDRES_PREPROC")
# Build all static so there is no dll file to distribute.
add_compiler_flags(-static)
endif()
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
add_definitions(-DMAC_OSX)
endif()
if(ENABLE_REDUCE_EXPORTS)
# Default visibility is set by CMAKE_<LANG>_VISIBILITY_PRESET, but this
# doesn't tell if the visibility set is effective.
# Check if the flag -fvisibility=hidden is supported, as using the hidden
# visibility is a requirement to reduce exports.
check_compiler_flag(HAS_CXX_FVISIBILITY CXX -fvisibility=hidden)
if(NOT HAS_CXX_FVISIBILITY)
message(FATAL_ERROR "Cannot set default symbol visibility. Use -DENABLE_REDUCE_EXPORTS=OFF.")
endif()
# Also hide symbols from static libraries
add_linker_flags(-Wl,--exclude-libs,ALL)
endif()
# Enable statically linking libstdc++
if(ENABLE_STATIC_LIBSTDCXX)
add_linker_flags(-static-libstdc++)
endif()
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
if(ENABLE_HARDENING)
# Enable stack protection
add_cxx_compiler_flags(-fstack-protector-all -Wstack-protector)
# Enable some buffer overflow checking
add_compiler_flags(-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2)
# Enable ASLR (these flags are primarily targeting MinGw)
add_linker_flags(-Wl,--dynamicbase -Wl,--nxcompat -Wl,--high-entropy-va)
# Make the relocated sections read-only
add_linker_flags(-Wl,-z,relro -Wl,-z,now)
# CMake provides the POSITION_INDEPENDENT_CODE property to set PIC/PIE.
# Unfortunately setting the -pie linker flag this way require CMake >= 3.14,
# which is not widely distributed at the time of writing.
# FIXME: remove the fallback case when cmake >= 3.14 get enforced.
if(POLICY CMP0083)
cmake_policy(SET CMP0083 NEW)
include(CheckPIESupported)
check_pie_supported()
elseif(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Windows")
add_compiler_flags(-fPIE)
add_linker_flags(-pie)
endif()
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
# MinGw provides its own libssp for stack smashing protection
link_libraries(ssp)
endif()
endif()
# Enable warning
add_c_compiler_flags(-Wnested-externs -Wstrict-prototypes)
add_compiler_flags(
-Wall
-Wextra
-Wformat
-Wvla
-Wformat-security
-Wcast-align
-Wunused-parameter
-Wmissing-braces
-Wthread-safety-analysis
-Wshadow
-Wrange-loop-analysis
-Wredundant-decls
-Wredundant-move
)
option(EXTRA_WARNINGS "Enable extra warnings" OFF)
if(EXTRA_WARNINGS)
add_cxx_compiler_flags(-Wsuggest-override)
else()
add_compiler_flags(-Wno-unused-parameter)
add_compiler_flags(-Wno-implicit-fallthrough)
endif()
# Create a target for OpenSSL
include(BrewHelper)
find_brew_prefix(OPENSSL_ROOT_DIR openssl)
find_package(OpenSSL REQUIRED)
# libtool style configure
add_subdirectory(config)
# libraries
add_subdirectory(crypto)
add_subdirectory(leveldb)
add_subdirectory(secp256k1)
add_subdirectory(univalue)
# Find the git root, and returns the full path to the .git/logs/HEAD file if
# it exists.
function(find_git_head_logs_file RESULT)
find_package(Git)
if(GIT_FOUND)
execute_process(
COMMAND "${GIT_EXECUTABLE}" "rev-parse" "--show-toplevel"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
OUTPUT_VARIABLE GIT_ROOT
RESULT_VARIABLE GIT_RESULT
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(GIT_RESULT EQUAL 0)
set(GIT_LOGS_DIR "${GIT_ROOT}/.git/logs")
set(GIT_HEAD_LOGS_FILE "${GIT_LOGS_DIR}/HEAD")
# If the .git/logs/HEAD does not exist, create it
if(NOT EXISTS "${GIT_HEAD_LOGS_FILE}")
file(MAKE_DIRECTORY "${GIT_LOGS_DIR}")
file(TOUCH "${GIT_HEAD_LOGS_FILE}")
endif()
set(${RESULT} "${GIT_HEAD_LOGS_FILE}" PARENT_SCOPE)
endif()
endif()
endfunction()
find_git_head_logs_file(GIT_HEAD_LOGS_FILE)
set(OBJ_DIR "${CMAKE_CURRENT_BINARY_DIR}/obj")
file(MAKE_DIRECTORY "${OBJ_DIR}")
set(BUILD_HEADER "${OBJ_DIR}/build.h")
set(BUILD_HEADER_TMP "${BUILD_HEADER}.tmp")
add_custom_command(
DEPENDS
"${GIT_HEAD_LOGS_FILE}"
"${CMAKE_SOURCE_DIR}/share/genbuild.sh"
OUTPUT
"${BUILD_HEADER}"
COMMAND
"${CMAKE_SOURCE_DIR}/share/genbuild.sh"
"${BUILD_HEADER_TMP}"
"${CMAKE_SOURCE_DIR}"
COMMAND
${CMAKE_COMMAND} -E copy_if_different "${BUILD_HEADER_TMP}" "${BUILD_HEADER}"
COMMAND
${CMAKE_COMMAND} -E remove "${BUILD_HEADER_TMP}"
)
# Because the Bitcoin ABc source code is disorganised, we
# end up with a bunch of libraries without any apparent
# cohesive structure. This is inherited from Bitcoin Core
# and reflecting this.
# TODO: Improve the structure once cmake is rocking.
# Various completely unrelated features shared by all executables.
add_library(util
chainparamsbase.cpp
clientversion.cpp
compat/glibc_sanity.cpp
compat/glibcxx_sanity.cpp
compat/strnlen.cpp
fs.cpp
logging.cpp
random.cpp
rcu.cpp
rpc/protocol.cpp
rpc/util.cpp
support/cleanse.cpp
support/lockedpool.cpp
sync.cpp
threadinterrupt.cpp
uint256.cpp
util/system.cpp
util/moneystr.cpp
util/strencodings.cpp
util/time.cpp
util/bytevectorhash.cpp
# obj/build.h
"${BUILD_HEADER}"
)
target_compile_definitions(util PUBLIC HAVE_CONFIG_H HAVE_BUILD_INFO)
target_include_directories(util
PUBLIC
.
# To access the config/ and obj/ directories
${CMAKE_CURRENT_BINARY_DIR}
)
if(ENABLE_GLIBC_BACK_COMPAT)
# glibc absorbed clock_gettime in 2.17. librt (its previous location) is
# safe to link in anyway for back-compat.
find_library(RT_LIBRARY rt)
target_link_libraries(util ${RT_LIBRARY})
# Wrap some glibc functions with ours
add_linker_flags(-Wl,--wrap=__divmoddi4)
add_linker_flags(-Wl,--wrap=log2f)
if(NOT HAVE_LARGE_FILE_SUPPORT)
add_linker_flags(-Wl,--wrap=fcntl -Wl,--wrap=fcntl64)
endif()
target_sources(util PRIVATE compat/glibc_compat.cpp)
endif()
# Enable LFS (Large File Support) on targets that don't have it natively.
if(NOT HAVE_LARGE_FILE_SUPPORT)
add_compiler_flags(-D_FILE_OFFSET_BITS=64)
add_linker_flags(-Wl,--large-address-aware)
endif()
# Target specific configs
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
set(Boost_USE_STATIC_LIBS ON)
set(Boost_THREADAPI win32)
find_package(SHLWAPI REQUIRED)
target_link_libraries(util ${SHLWAPI_LIBRARY})
target_include_directories(util PUBLIC ${SHLWAPI_INCLUDE_DIR})
find_library(WS2_32_LIBRARY NAMES ws2_32)
target_link_libraries(util ${WS2_32_LIBRARY})
target_compile_definitions(util PUBLIC BOOST_THREAD_USE_LIB)
endif()
# Boost packages
set(BOOST_PACKAGES_REQUIRED chrono filesystem thread)
function(prepend var prefix)
set(listVar "")
foreach(f ${ARGN})
list(APPEND listVar "${prefix}${f}")
endforeach(f)
set(${var} "${listVar}" PARENT_SCOPE)
endfunction(prepend)
prepend(BOOST_LIBRARIES "Boost::" ${BOOST_PACKAGES_REQUIRED})
find_package(Boost 1.58 REQUIRED ${BOOST_PACKAGES_REQUIRED})
target_link_libraries(util univalue crypto ${BOOST_LIBRARIES})
# Make sure boost uses std::atomic (it doesn't before 1.63)
target_compile_definitions(util PUBLIC BOOST_SP_USE_STD_ATOMIC BOOST_AC_USE_STD_ATOMIC)
# More completely unrelated features shared by all executables.
# Because nothing says this is different from util than "common"
add_library(common
amount.cpp
base58.cpp
cashaddr.cpp
cashaddrenc.cpp
chainparams.cpp
config.cpp
consensus/merkle.cpp
coins.cpp
compressor.cpp
feerate.cpp
globals.cpp
core_read.cpp
core_write.cpp
key.cpp
key_io.cpp
keystore.cpp
netaddress.cpp
netbase.cpp
primitives/block.cpp
protocol.cpp
scheduler.cpp
warnings.cpp
)
target_link_libraries(common util secp256k1)
# script library
add_library(script
script/bitfield.cpp
script/descriptor.cpp
script/interpreter.cpp
script/ismine.cpp
script/script.cpp
script/script_error.cpp
script/sigencoding.cpp
script/sign.cpp
script/standard.cpp
)
target_link_libraries(script common)
# libbitcoinconsensus
add_library(bitcoinconsensus
arith_uint256.cpp
hash.cpp
primitives/transaction.cpp
pubkey.cpp
uint256.cpp
util/strencodings.cpp
)
target_link_libraries(bitcoinconsensus script)
include(InstallationHelper)
if(BUILD_LIBBITCOINCONSENSUS)
target_compile_definitions(bitcoinconsensus
PUBLIC
BUILD_BITCOIN_INTERNAL
HAVE_CONSENSUS_LIB
)
install_shared_library(bitcoinconsensus
script/bitcoinconsensus.cpp
PUBLIC_HEADER script/bitcoinconsensus.h
)
endif()
# Bitcoin server facilities
add_library(server
addrdb.cpp
addrman.cpp
avalanche.cpp
banman.cpp
bloom.cpp
blockencodings.cpp
blockfilter.cpp
chain.cpp
checkpoints.cpp
config.cpp
consensus/activation.cpp
consensus/tx_verify.cpp
+ dbwrapper.cpp
flatfile.cpp
globals.cpp
httprpc.cpp
httpserver.cpp
index/base.cpp
index/txindex.cpp
init.cpp
interfaces/handler.cpp
interfaces/node.cpp
- dbwrapper.cpp
merkleblock.cpp
miner.cpp
net.cpp
net_processing.cpp
noui.cpp
+ outputtype.cpp
policy/fees.cpp
policy/policy.cpp
pow.cpp
rest.cpp
rpc/abc.cpp
rpc/blockchain.cpp
rpc/command.cpp
rpc/jsonrpcrequest.cpp
rpc/mining.cpp
rpc/misc.cpp
rpc/net.cpp
rpc/rawtransaction.cpp
rpc/server.cpp
script/scriptcache.cpp
script/sigcache.cpp
timedata.cpp
torcontrol.cpp
txdb.cpp
txmempool.cpp
ui_interface.cpp
validation.cpp
validationinterface.cpp
)
# This require libevent
find_package(Event REQUIRED)
target_include_directories(server PRIVATE leveldb/helpers/memenv)
target_link_libraries(server
Event
bitcoinconsensus
leveldb
memenv
)
if(ENABLE_UPNP)
target_include_directories(server PUBLIC ${MINIUPNPC_INCLUDE_DIR})
target_link_libraries(server ${MINIUPNPC_LIBRARY})
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
find_library(IPHLPAPI_LIBRARY NAMES iphlpapi)
if(NOT IPHLPAPI_LIBRARY)
message(FATAL_ERROR "Lib iphlpapi is missing")
endif()
target_link_libraries(server ${IPHLPAPI_LIBRARY})
target_compile_definitions(server
PUBLIC -DSTATICLIB
PUBLIC -DMINIUPNP_STATICLIB
)
endif()
endif()
# Test suite.
add_subdirectory(test)
# Benchmark suite.
add_subdirectory(bench)
# Wallet
if(BUILD_BITCOIN_WALLET)
add_subdirectory(wallet)
target_link_libraries(server wallet)
endif()
# ZeroMQ
if(BUILD_BITCOIN_ZMQ)
add_subdirectory(zmq)
target_link_libraries(server zmq)
endif()
# RPC client support
add_library(rpcclient rpc/client.cpp)
target_link_libraries(rpcclient univalue util)
# bitcoin-seeder
if(BUILD_BITCOIN_SEEDER)
add_subdirectory(seeder)
endif()
include(BinaryTest)
# bitcoin-cli
if(BUILD_BITCOIN_CLI)
add_executable(bitcoin-cli bitcoin-cli.cpp)
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
target_sources(bitcoin-cli PRIVATE bitcoin-cli-res.rc)
endif()
target_link_libraries(bitcoin-cli common rpcclient Event)
add_to_symbols_check(bitcoin-cli)
add_to_security_check(bitcoin-cli)
install_target(bitcoin-cli)
endif()
# bitcoin-tx
if(BUILD_BITCOIN_TX)
add_executable(bitcoin-tx bitcoin-tx.cpp)
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
target_sources(bitcoin-tx PRIVATE bitcoin-tx-res.rc)
endif()
target_link_libraries(bitcoin-tx bitcoinconsensus)
add_to_symbols_check(bitcoin-tx)
add_to_security_check(bitcoin-tx)
install_target(bitcoin-tx)
endif()
# bitcoind
add_executable(bitcoind bitcoind.cpp)
target_link_libraries(bitcoind server)
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
target_sources(bitcoind PRIVATE bitcoind-res.rc)
endif()
add_to_symbols_check(bitcoind)
add_to_security_check(bitcoind)
install_target(bitcoind)
# Bitcoin-qt
if(BUILD_BITCOIN_QT)
add_subdirectory(qt)
endif()
diff --git a/src/Makefile.am b/src/Makefile.am
index 3ba39fc30..571f4dc0c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,685 +1,687 @@
# Copyright (c) 2013-2016 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
DIST_SUBDIRS = secp256k1 univalue
AM_LDFLAGS = $(PTHREAD_CFLAGS) $(LIBTOOL_LDFLAGS) $(HARDENED_LDFLAGS) $(SANITIZER_LDFLAGS)
AM_CXXFLAGS = $(DEBUG_CXXFLAGS) $(HARDENED_CXXFLAGS) $(WARN_CXXFLAGS) $(NOWARN_CXXFLAGS) $(ERROR_CXXFLAGS) $(SANITIZER_CXXFLAGS)
AM_CPPFLAGS = $(DEBUG_CPPFLAGS) $(HARDENED_CPPFLAGS)
AM_LIBTOOLFLAGS = --preserve-dup-deps
EXTRA_LIBRARIES =
if EMBEDDED_UNIVALUE
LIBUNIVALUE = univalue/libunivalue.la
$(LIBUNIVALUE): $(wildcard univalue/lib/*) $(wildcard univalue/include/*)
$(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F)
else
LIBUNIVALUE = $(UNIVALUE_LIBS)
endif
BITCOIN_INCLUDES=-I$(builddir) $(BDB_CPPFLAGS) $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS) $(CRYPTO_CFLAGS) $(SSL_CFLAGS)
BITCOIN_INCLUDES += -I$(srcdir)/secp256k1/include
BITCOIN_INCLUDES += $(UNIVALUE_CFLAGS)
BITCOIN_SEEDER_INCLUDES = -I$(srcdir)/seeder
BITCOIN_SEEDER_INCLUDES += $(BITCOIN_INCLUDES)
LIBBITCOIN_SERVER=libbitcoin_server.a
LIBBITCOIN_COMMON=libbitcoin_common.a
LIBBITCOIN_CONSENSUS=libbitcoin_consensus.a
LIBBITCOIN_CLI=libbitcoin_cli.a
LIBBITCOIN_UTIL=libbitcoin_util.a
LIBBITCOIN_CRYPTO_BASE=crypto/libbitcoin_crypto_base.a
LIBBITCOINQT=qt/libbitcoinqt.a
LIBSECP256K1=secp256k1/libsecp256k1.la
if ENABLE_ZMQ
LIBBITCOIN_ZMQ=libbitcoin_zmq.a
endif
if BUILD_BITCOIN_LIBS
LIBBITCOINCONSENSUS=libbitcoinconsensus.la
endif
if BUILD_BITCOIN_SEEDER
LIBBITCOIN_SEEDER=libbitcoin_seeder.a
endif
if ENABLE_WALLET
LIBBITCOIN_WALLET=libbitcoin_wallet.a
endif
LIBBITCOIN_CRYPTO= $(LIBBITCOIN_CRYPTO_BASE)
if ENABLE_SSE41
LIBBITCOIN_CRYPTO_SSE41 = crypto/libbitcoin_crypto_sse41.a
LIBBITCOIN_CRYPTO += $(LIBBITCOIN_CRYPTO_SSE41)
endif
if ENABLE_AVX2
LIBBITCOIN_CRYPTO_AVX2 = crypto/libbitcoin_crypto_avx2.a
LIBBITCOIN_CRYPTO += $(LIBBITCOIN_CRYPTO_AVX2)
endif
if ENABLE_SHANI
LIBBITCOIN_CRYPTO_SHANI = crypto/libbitcoin_crypto_shani.a
LIBBITCOIN_CRYPTO += $(LIBBITCOIN_CRYPTO_SHANI)
endif
$(LIBSECP256K1): $(wildcard secp256k1/src/*) $(wildcard secp256k1/include/*)
$(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F)
# Make is not made aware of per-object dependencies to avoid limiting building parallelization
# But to build the less dependent modules first, we manually select their order here:
EXTRA_LIBRARIES += \
$(LIBBITCOIN_CRYPTO) \
$(LIBBITCOIN_UTIL) \
$(LIBBITCOIN_COMMON) \
$(LIBBITCOIN_CONSENSUS) \
$(LIBBITCOIN_SERVER) \
$(LIBBITCOIN_CLI) \
$(LIBBITCOIN_SEEDER) \
$(LIBBITCOIN_WALLET) \
$(LIBBITCOIN_ZMQ)
lib_LTLIBRARIES = $(LIBBITCOINCONSENSUS)
bin_PROGRAMS =
noinst_PROGRAMS =
TESTS =
BENCHMARKS =
if BUILD_BITCOIND
bin_PROGRAMS += bitcoind
endif
if BUILD_BITCOIN_SEEDER
bin_PROGRAMS += bitcoin-seeder
endif
if BUILD_BITCOIN_UTILS
bin_PROGRAMS += bitcoin-cli bitcoin-tx
endif
.PHONY: FORCE check-symbols check-security
# bitcoin core #
BITCOIN_CORE_H = \
addrdb.h \
addrman.h \
avalanche.h \
banman.h \
base58.h \
bloom.h \
blockencodings.h \
blockfileinfo.h \
blockfilter.h \
blockindexworkcomparator.h \
blockstatus.h \
blockvalidity.h \
cashaddr.h \
cashaddrenc.h \
chain.h \
chainparams.h \
chainparamsbase.h \
chainparamsconstants.h \
chainparamsseeds.h \
checkpoints.h \
checkqueue.h \
clientversion.h \
coins.h \
compat.h \
compat/assumptions.h \
compat/byteswap.h \
compat/endian.h \
compat/sanity.h \
compat/setenv.h \
compressor.h \
config.h \
consensus/activation.h \
consensus/consensus.h \
consensus/tx_verify.h \
core_io.h \
core_memusage.h \
cuckoocache.h \
flatfile.h \
fs.h \
globals.h \
httprpc.h \
httpserver.h \
index/base.h \
index/txindex.h \
indirectmap.h \
init.h \
interfaces/handler.h \
interfaces/node.h \
interfaces/wallet.h \
key.h \
key_io.h \
keystore.h \
dbwrapper.h \
limitedmap.h \
logging.h \
memusage.h \
merkleblock.h \
miner.h \
net.h \
net_processing.h \
netaddress.h \
netbase.h \
netmessagemaker.h \
noui.h \
+ outputtype.h \
policy/fees.h \
policy/policy.h \
pow.h \
protocol.h \
radix.h \
random.h \
rcu.h \
reverse_iterator.h \
reverselock.h \
rpc/blockchain.h \
rpc/client.h \
rpc/command.h \
rpc/jsonrpcrequest.h \
rpc/mining.h \
rpc/misc.h \
rpc/protocol.h \
rpc/rawtransaction.h \
rpc/server.h \
rpc/register.h \
rpc/util.h \
rwcollection.h \
scheduler.h \
script/descriptor.h \
script/ismine.h \
script/scriptcache.h \
script/sigcache.h \
script/sign.h \
script/standard.h \
streams.h \
support/allocators/secure.h \
support/allocators/zeroafterfree.h \
support/cleanse.h \
support/events.h \
support/lockedpool.h \
sync.h \
threadsafety.h \
threadinterrupt.h \
timedata.h \
torcontrol.h \
txdb.h \
txmempool.h \
ui_interface.h \
undo.h \
util/system.h \
util/moneystr.h \
util/time.h \
util/bitmanip.h \
util/bytevectorhash.h \
validation.h \
validationinterface.h \
versionbits.h \
walletinitinterface.h \
wallet/coincontrol.h \
wallet/coinselection.h \
wallet/crypter.h \
wallet/db.h \
wallet/finaltx.h \
wallet/rpcdump.h \
wallet/fees.h \
wallet/rpcwallet.h \
wallet/wallet.h \
wallet/walletdb.h \
wallet/walletutil.h \
warnings.h \
zmq/zmqabstractnotifier.h \
zmq/zmqconfig.h\
zmq/zmqnotificationinterface.h \
zmq/zmqpublishnotifier.h
obj/build.h: FORCE
@$(MKDIR_P) "$(builddir)/obj"
@$(top_srcdir)/share/genbuild.sh "$(abs_top_builddir)/src/obj/build.h" \
"$(abs_top_srcdir)"
libbitcoin_util_a-clientversion.$(OBJEXT): obj/build.h
# server: shared between bitcoind and bitcoin-qt
libbitcoin_server_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS)
libbitcoin_server_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_server_a_SOURCES = \
addrdb.cpp \
addrman.cpp \
avalanche.cpp \
banman.cpp \
bloom.cpp \
blockencodings.cpp \
blockfilter.cpp \
chain.cpp \
checkpoints.cpp \
config.cpp \
consensus/activation.cpp \
consensus/tx_verify.cpp \
flatfile.cpp \
globals.cpp \
httprpc.cpp \
httpserver.cpp \
index/base.cpp \
index/txindex.cpp \
init.cpp \
interfaces/handler.cpp \
interfaces/node.cpp \
dbwrapper.cpp \
merkleblock.cpp \
miner.cpp \
net.cpp \
net_processing.cpp \
noui.cpp \
+ outputtype.cpp \
policy/fees.cpp \
policy/policy.cpp \
pow.cpp \
rest.cpp \
rpc/abc.cpp \
rpc/blockchain.cpp \
rpc/command.cpp \
rpc/jsonrpcrequest.cpp \
rpc/mining.cpp \
rpc/misc.cpp \
rpc/net.cpp \
rpc/rawtransaction.cpp \
rpc/server.cpp \
script/scriptcache.cpp \
script/sigcache.cpp \
timedata.cpp \
torcontrol.cpp \
txdb.cpp \
txmempool.cpp \
ui_interface.cpp \
validation.cpp \
validationinterface.cpp \
$(BITCOIN_CORE_H)
if ENABLE_ZMQ
libbitcoin_zmq_a_CPPFLAGS = $(BITCOIN_INCLUDES) $(ZMQ_CFLAGS)
libbitcoin_zmq_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_zmq_a_SOURCES = \
zmq/zmqabstractnotifier.cpp \
zmq/zmqnotificationinterface.cpp \
zmq/zmqpublishnotifier.cpp
endif
# wallet: shared between bitcoind and bitcoin-qt, but only linked
# when wallet enabled
libbitcoin_wallet_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
libbitcoin_wallet_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_wallet_a_SOURCES = \
interfaces/wallet.cpp \
wallet/coincontrol.cpp \
wallet/crypter.cpp \
wallet/coinselection.cpp \
wallet/db.cpp \
wallet/finaltx.cpp \
wallet/fees.cpp \
wallet/init.cpp \
wallet/rpcdump.cpp \
wallet/rpcwallet.cpp \
wallet/wallet.cpp \
wallet/walletdb.cpp \
wallet/walletutil.cpp \
$(BITCOIN_CORE_H)
# crypto primitives library
crypto_libbitcoin_crypto_base_a_CPPFLAGS = $(AM_CPPFLAGS)
crypto_libbitcoin_crypto_base_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
crypto_libbitcoin_crypto_base_a_SOURCES = \
crypto/aes.cpp \
crypto/aes.h \
crypto/chacha20.h \
crypto/chacha20.cpp \
crypto/common.h \
crypto/hmac_sha256.cpp \
crypto/hmac_sha256.h \
crypto/hmac_sha512.cpp \
crypto/hmac_sha512.h \
crypto/ripemd160.cpp \
crypto/ripemd160.h \
crypto/sha1.cpp \
crypto/sha1.h \
crypto/sha256.cpp \
crypto/sha256.h \
crypto/sha512.cpp \
crypto/sha512.h \
crypto/siphash.cpp \
crypto/siphash.h
if USE_ASM
crypto_libbitcoin_crypto_base_a_SOURCES += crypto/sha256_sse4.cpp
endif
crypto_libbitcoin_crypto_sse41_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
crypto_libbitcoin_crypto_sse41_a_CPPFLAGS = $(AM_CPPFLAGS)
crypto_libbitcoin_crypto_sse41_a_CXXFLAGS += $(SSE41_CXXFLAGS)
crypto_libbitcoin_crypto_sse41_a_CPPFLAGS += -DENABLE_SSE41
crypto_libbitcoin_crypto_sse41_a_SOURCES = crypto/sha256_sse41.cpp
crypto_libbitcoin_crypto_avx2_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
crypto_libbitcoin_crypto_avx2_a_CPPFLAGS = $(AM_CPPFLAGS)
crypto_libbitcoin_crypto_avx2_a_CXXFLAGS += $(AVX2_CXXFLAGS)
crypto_libbitcoin_crypto_avx2_a_CPPFLAGS += -DENABLE_AVX2
crypto_libbitcoin_crypto_avx2_a_SOURCES = crypto/sha256_avx2.cpp
crypto_libbitcoin_crypto_shani_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
crypto_libbitcoin_crypto_shani_a_CPPFLAGS = $(AM_CPPFLAGS)
crypto_libbitcoin_crypto_shani_a_CXXFLAGS += $(SHANI_CXXFLAGS)
crypto_libbitcoin_crypto_shani_a_CPPFLAGS += -DENABLE_SHANI
crypto_libbitcoin_crypto_shani_a_SOURCES = crypto/sha256_shani.cpp
# consensus: shared between all executables that validate any consensus rules.
libbitcoin_consensus_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
libbitcoin_consensus_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_consensus_a_SOURCES = \
amount.h \
arith_uint256.cpp \
arith_uint256.h \
consensus/merkle.cpp \
consensus/merkle.h \
consensus/params.h \
consensus/validation.h \
feerate.h \
hash.cpp \
hash.h \
prevector.h \
primitives/block.cpp \
primitives/block.h \
primitives/transaction.cpp \
primitives/transaction.h \
primitives/txid.h \
pubkey.cpp \
pubkey.h \
script/bitcoinconsensus.cpp \
script/bitfield.cpp \
script/bitfield.h \
script/sighashtype.h \
script/interpreter.cpp \
script/interpreter.h \
script/script.cpp \
script/script.h \
script/script_error.cpp \
script/script_error.h \
script/script_flags.h \
script/sigencoding.cpp \
script/sigencoding.h \
serialize.h \
span.h \
tinyformat.h \
uint256.cpp \
uint256.h \
util/strencodings.cpp \
util/strencodings.h \
version.h
# common: shared between bitcoind, and bitcoin-qt and non-server tools
libbitcoin_common_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
libbitcoin_common_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_common_a_SOURCES = \
amount.cpp \
base58.cpp \
cashaddr.cpp \
cashaddrenc.cpp \
chainparams.cpp \
config.cpp \
coins.cpp \
compressor.cpp \
feerate.cpp \
globals.cpp \
core_read.cpp \
core_write.cpp \
key.cpp \
key_io.cpp \
keystore.cpp \
netaddress.cpp \
netbase.cpp \
protocol.cpp \
scheduler.cpp \
script/descriptor.cpp \
script/ismine.cpp \
script/sign.cpp \
script/standard.cpp \
warnings.cpp \
$(BITCOIN_CORE_H)
# util: shared between all executables.
# This library *must* be included to make sure that the glibc
# backward-compatibility objects and their sanity checks are linked.
libbitcoin_util_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
libbitcoin_util_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_util_a_SOURCES = \
support/lockedpool.cpp \
chainparamsbase.cpp \
clientversion.cpp \
compat/glibc_sanity.cpp \
compat/glibcxx_sanity.cpp \
compat/strnlen.cpp \
fs.cpp \
logging.cpp \
random.cpp \
rcu.cpp \
rpc/protocol.cpp \
rpc/util.cpp \
support/cleanse.cpp \
sync.cpp \
threadinterrupt.cpp \
uint256.cpp \
uint256.h \
util/system.cpp \
util/moneystr.cpp \
util/strencodings.cpp \
util/time.cpp \
util/bytevectorhash.cpp \
$(BITCOIN_CORE_H)
if GLIBC_BACK_COMPAT
libbitcoin_util_a_SOURCES += compat/glibc_compat.cpp
AM_LDFLAGS += $(COMPAT_LDFLAGS)
endif
# cli: shared between bitcoin-cli and bitcoin-qt
libbitcoin_cli_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
libbitcoin_cli_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_cli_a_SOURCES = \
rpc/client.cpp \
$(BITCOIN_CORE_H)
# seeder library
libbitcoin_seeder_a_CPPFLAGS = $(AM_CPPFLAGS) $(PIE_FLAGS) $(BITCOIN_SEEDER_INCLUDES)
libbitcoin_seeder_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_seeder_a_SOURCES = \
seeder/bitcoin.cpp \
seeder/bitcoin.h \
seeder/db.cpp \
seeder/db.h \
seeder/dns.cpp \
seeder/dns.h \
seeder/util.h
nodist_libbitcoin_util_a_SOURCES = $(srcdir)/obj/build.h
#
# bitcoind binary #
bitcoind_SOURCES = bitcoind.cpp
bitcoind_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
bitcoind_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
bitcoind_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
if TARGET_WINDOWS
bitcoind_SOURCES += bitcoind-res.rc
endif
bitcoind_LDADD = \
$(LIBBITCOIN_SERVER) \
$(LIBBITCOIN_WALLET) \
$(LIBBITCOIN_COMMON) \
$(LIBUNIVALUE) \
$(LIBBITCOIN_UTIL) \
$(LIBBITCOIN_ZMQ) \
$(LIBBITCOIN_CONSENSUS) \
$(LIBBITCOIN_CRYPTO) \
$(LIBLEVELDB) \
$(LIBLEVELDB_SSE42) \
$(LIBMEMENV) \
$(LIBSECP256K1)
bitcoind_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(ZMQ_LIBS)
# bitcoin-cli binary #
bitcoin_cli_SOURCES = bitcoin-cli.cpp
bitcoin_cli_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(EVENT_CFLAGS)
bitcoin_cli_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
bitcoin_cli_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
if TARGET_WINDOWS
bitcoin_cli_SOURCES += bitcoin-cli-res.rc
endif
bitcoin_cli_LDADD = \
$(LIBBITCOIN_CLI) \
$(LIBUNIVALUE) \
$(LIBBITCOIN_UTIL) \
$(LIBBITCOIN_CRYPTO)
bitcoin_cli_LDADD += $(BOOST_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(EVENT_LIBS)
#
# bitcoin-seeder binary #
bitcoin_seeder_SOURCES = seeder/main.cpp
bitcoin_seeder_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_SEEDER_INCLUDES)
bitcoin_seeder_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
bitcoin_seeder_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
bitcoin_seeder_LDADD = \
$(LIBBITCOIN_SEEDER) \
$(LIBBITCOIN_COMMON) \
$(LIBBITCOIN_UTIL) \
$(LIBBITCOIN_CRYPTO)
bitcoin_seeder_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
#
# bitcoin-tx binary #
bitcoin_tx_SOURCES = bitcoin-tx.cpp
bitcoin_tx_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
bitcoin_tx_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
bitcoin_tx_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
if TARGET_WINDOWS
bitcoin_tx_SOURCES += bitcoin-tx-res.rc
endif
bitcoin_tx_LDADD = \
$(LIBUNIVALUE) \
$(LIBBITCOIN_COMMON) \
$(LIBBITCOIN_UTIL) \
$(LIBBITCOIN_CONSENSUS) \
$(LIBBITCOIN_CRYPTO) \
$(LIBSECP256K1)
bitcoin_tx_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
#
# bitcoinconsensus library #
if BUILD_BITCOIN_LIBS
include_HEADERS = script/bitcoinconsensus.h
libbitcoinconsensus_la_SOURCES = $(crypto_libbitcoin_crypto_base_a_SOURCES) $(libbitcoin_consensus_a_SOURCES)
if GLIBC_BACK_COMPAT
libbitcoinconsensus_la_SOURCES += compat/glibc_compat.cpp
endif
libbitcoinconsensus_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined $(RELDFLAGS)
libbitcoinconsensus_la_LIBADD = $(LIBSECP256K1)
libbitcoinconsensus_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(builddir)/obj -I$(srcdir)/secp256k1/include -DBUILD_BITCOIN_INTERNAL
libbitcoinconsensus_la_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
endif
#
CTAES_DIST = crypto/ctaes/bench.c
CTAES_DIST += crypto/ctaes/ctaes.c
CTAES_DIST += crypto/ctaes/ctaes.h
CTAES_DIST += crypto/ctaes/README.md
CTAES_DIST += crypto/ctaes/test.c
CLEANFILES = $(EXTRA_LIBRARIES)
CLEANFILES += *.gcda *.gcno
CLEANFILES += compat/*.gcda compat/*.gcno
CLEANFILES += consensus/*.gcda consensus/*.gcno
CLEANFILES += crypto/*.gcda crypto/*.gcno
CLEANFILES += policy/*.gcda policy/*.gcno
CLEANFILES += primitives/*.gcda primitives/*.gcno
CLEANFILES += script/*.gcda script/*.gcno
CLEANFILES += support/*.gcda support/*.gcno
CLEANFILES += univalue/*.gcda univalue/*.gcno
CLEANFILES += wallet/*.gcda wallet/*.gcno
CLEANFILES += wallet/test/*.gcda wallet/test/*.gcno
CLEANFILES += zmq/*.gcda zmq/*.gcno
CLEANFILES += obj/build.h
EXTRA_DIST = $(CTAES_DIST)
config/bitcoin-config.h: config/stamp-h1
@$(MAKE) -C $(top_builddir) $(subdir)/$(@)
config/stamp-h1: $(top_srcdir)/$(subdir)/config/bitcoin-config.h.in $(top_builddir)/config.status
$(AM_V_at)$(MAKE) -C $(top_builddir) $(subdir)/$(@)
$(top_srcdir)/$(subdir)/config/bitcoin-config.h.in: $(am__configure_deps)
$(AM_V_at)$(MAKE) -C $(top_srcdir) $(subdir)/config/bitcoin-config.h.in
clean-local:
-$(MAKE) -C secp256k1 clean
-$(MAKE) -C univalue clean
-rm -f leveldb/*/*.gcda leveldb/*/*.gcno leveldb/helpers/memenv/*.gcda leveldb/helpers/memenv/*.gcno
-rm -rf test/__pycache__
.rc.o:
@test -f $(WINDRES)
## FIXME: How to get the appropriate modulename_CPPFLAGS in here?
$(AM_V_GEN) $(WINDRES) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(CPPFLAGS) -DWINDRES_PREPROC -i $< -o $@
.mm.o:
$(AM_V_CXX) $(OBJCXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CXXFLAGS) $(QT_INCLUDES) $(AM_CXXFLAGS) $(PIE_FLAGS) $(CXXFLAGS) -c -o $@ $<
check-symbols: $(bin_PROGRAMS)
if GLIBC_BACK_COMPAT
@echo "Checking glibc back compat..."
$(AM_V_at) READELF=$(READELF) CPPFILT=$(CPPFILT) $(top_srcdir)/contrib/devtools/symbol-check.py < $(bin_PROGRAMS)
endif
check-security: $(bin_PROGRAMS)
if HARDEN
@echo "Checking binary security..."
$(AM_V_at) READELF=$(READELF) OBJDUMP=$(OBJDUMP) $(top_srcdir)/contrib/devtools/security-check.py < $(bin_PROGRAMS)
endif
%.pb.cc %.pb.h: %.proto
@test -f $(PROTOC)
$(AM_V_GEN) $(PROTOC) --cpp_out=$(@D) --proto_path=$(<D) $<
if EMBEDDED_LEVELDB
include Makefile.leveldb.include
endif
if ENABLE_TESTS
include Makefile.test.include
endif
if ENABLE_BENCH
include Makefile.bench.include
endif
if ENABLE_QT
include Makefile.qt.include
endif
if ENABLE_QT_TESTS
include Makefile.qttest.include
endif
diff --git a/src/outputtype.cpp b/src/outputtype.cpp
new file mode 100644
index 000000000..e66dfed3d
--- /dev/null
+++ b/src/outputtype.cpp
@@ -0,0 +1,60 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2017 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 <outputtype.h>
+
+#include <keystore.h>
+#include <pubkey.h>
+#include <script/script.h>
+#include <script/standard.h>
+
+#include <cassert>
+#include <string>
+
+static const std::string OUTPUT_TYPE_STRING_LEGACY = "legacy";
+
+bool ParseOutputType(const std::string &type, OutputType &output_type) {
+ if (type == OUTPUT_TYPE_STRING_LEGACY) {
+ output_type = OutputType::LEGACY;
+ return true;
+ }
+ return false;
+}
+
+const std::string &FormatOutputType(OutputType type) {
+ switch (type) {
+ case OutputType::LEGACY:
+ return OUTPUT_TYPE_STRING_LEGACY;
+ default:
+ assert(false);
+ }
+}
+
+CTxDestination GetDestinationForKey(const CPubKey &key, OutputType type) {
+ switch (type) {
+ case OutputType::LEGACY:
+ return key.GetID();
+ default:
+ assert(false);
+ }
+}
+
+std::vector<CTxDestination> GetAllDestinationsForKey(const CPubKey &key) {
+ return std::vector<CTxDestination>{key.GetID()};
+}
+
+CTxDestination AddAndGetDestinationForScript(CKeyStore &keystore,
+ const CScript &script,
+ OutputType type) {
+ // Add script to keystore
+ keystore.AddCScript(script);
+ // Note that scripts over 520 bytes are not yet supported.
+ switch (type) {
+ case OutputType::LEGACY:
+ return CScriptID(script);
+ default:
+ assert(false);
+ }
+}
diff --git a/src/outputtype.h b/src/outputtype.h
new file mode 100644
index 000000000..8ff6e7c2b
--- /dev/null
+++ b/src/outputtype.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2017 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_OUTPUTTYPE_H
+#define BITCOIN_OUTPUTTYPE_H
+
+#include <keystore.h>
+#include <script/standard.h>
+
+#include <string>
+#include <vector>
+
+enum class OutputType {
+ LEGACY,
+
+ /**
+ * Special output type for change outputs only. Automatically choose type
+ * based on address type setting and the types other of non-change outputs.
+ */
+ CHANGE_AUTO,
+};
+
+bool ParseOutputType(const std::string &str, OutputType &output_type);
+const std::string &FormatOutputType(OutputType type);
+
+/**
+ * Get a destination of the requested type (if possible) to the specified key.
+ * The caller must make sure LearnRelatedScripts has been called beforehand.
+ */
+CTxDestination GetDestinationForKey(const CPubKey &key, OutputType);
+
+/**
+ * Get all destinations (potentially) supported by the wallet for the given key.
+ */
+std::vector<CTxDestination> GetAllDestinationsForKey(const CPubKey &key);
+
+/**
+ * Get a destination of the requested type (if possible) to the specified
+ * script. This function will automatically add the script (and any other
+ * necessary scripts) to the keystore.
+ */
+CTxDestination AddAndGetDestinationForScript(CKeyStore &keystore,
+ const CScript &script, OutputType);
+
+#endif // BITCOIN_OUTPUTTYPE_H
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index e63e35df9..89f81ea95 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -1,556 +1,562 @@
// Copyright (c) 2010 Satoshi Nakamoto
// Copyright (c) 2009-2016 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 <chain.h>
#include <clientversion.h>
#include <config.h>
#include <core_io.h>
#include <httpserver.h>
#include <init.h>
#include <key_io.h>
#include <logging.h>
#include <net.h>
#include <netbase.h>
+#include <outputtype.h>
#include <rpc/blockchain.h>
#include <rpc/misc.h>
#include <rpc/server.h>
#include <rpc/util.h>
#include <timedata.h>
#include <util/strencodings.h>
#include <util/system.h>
#include <validation.h>
#ifdef ENABLE_WALLET
#include <wallet/rpcwallet.h>
#include <wallet/wallet.h>
#include <wallet/walletdb.h>
#endif
#include <warnings.h>
#include <univalue.h>
#include <cstdint>
#ifdef HAVE_MALLOC_INFO
#include <malloc.h>
#endif
static UniValue validateaddress(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"validateaddress \"address\"\n"
"\nReturn information about the given bitcoin address.\n"
"\nArguments:\n"
"1. \"address\" (string, required) The bitcoin "
"address to validate\n"
"\nResult:\n"
"{\n"
" \"isvalid\" : true|false, (boolean) If the address is "
"valid or not. If not, this is the only property returned.\n"
" \"address\" : \"address\", (string) The bitcoin address "
"validated\n"
" \"scriptPubKey\" : \"hex\", (string) The hex encoded "
"scriptPubKey generated by the address\n"
" \"isscript\" : true|false, (boolean) If the key is a "
"script\n"
"}\n"
"\nExamples:\n" +
HelpExampleCli("validateaddress",
"\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"") +
HelpExampleRpc("validateaddress",
"\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\""));
}
CTxDestination dest =
DecodeDestination(request.params[0].get_str(), config.GetChainParams());
bool isValid = IsValidDestination(dest);
UniValue ret(UniValue::VOBJ);
ret.pushKV("isvalid", isValid);
if (isValid) {
if (ret["address"].isNull()) {
std::string currentAddress = EncodeDestination(dest, config);
ret.pushKV("address", currentAddress);
CScript scriptPubKey = GetScriptForDestination(dest);
ret.pushKV("scriptPubKey",
HexStr(scriptPubKey.begin(), scriptPubKey.end()));
UniValue detail = DescribeAddress(dest);
ret.pushKVs(detail);
}
}
return ret;
}
// Needed even with !ENABLE_WALLET, to pass (ignored) pointers around
class CWallet;
static UniValue createmultisig(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() < 2 ||
request.params.size() > 2) {
std::string msg =
"createmultisig nrequired [\"key\",...]\n"
"\nCreates a multi-signature address with n signature of m keys "
"required.\n"
"It returns a json object with the address and redeemScript.\n"
"\nArguments:\n"
"1. nrequired (numeric, required) The number of required "
"signatures out of the n keys.\n"
"2. \"keys\" (string, required) A json array of hex-encoded "
"public keys\n"
" [\n"
" \"key\" (string) The hex-encoded public key\n"
" ,...\n"
" ]\n"
"\nResult:\n"
"{\n"
" \"address\":\"multisigaddress\", (string) The value of the new "
"multisig address.\n"
" \"redeemScript\":\"script\" (string) The string value of "
"the hex-encoded redemption script.\n"
"}\n"
"\nExamples:\n"
"\nCreate a multisig address from 2 public keys\n" +
HelpExampleCli("createmultisig",
"2 "
"\"["
"\\\"03789ed0bb717d88f7d321a368d905e7430207ebbd82bd3"
"42cf11ae157a7ace5fd\\\","
"\\\"03dbc6764b8884a92e871274b87583e6d5c2a58819473e1"
"7e107ef3f6aa5a61626\\\"]\"") +
"\nAs a json rpc call\n" +
HelpExampleRpc("createmultisig",
"2, "
"\"["
"\\\"03789ed0bb717d88f7d321a368d905e7430207ebbd82bd3"
"42cf11ae157a7ace5fd\\\","
"\\\"03dbc6764b8884a92e871274b87583e6d5c2a58819473e1"
"7e107ef3f6aa5a61626\\\"]\"");
throw std::runtime_error(msg);
}
int required = request.params[0].get_int();
// Get the public keys
const UniValue &keys = request.params[1].get_array();
std::vector<CPubKey> pubkeys;
for (size_t i = 0; i < keys.size(); ++i) {
if (IsHex(keys[i].get_str()) && (keys[i].get_str().length() == 66 ||
keys[i].get_str().length() == 130)) {
pubkeys.push_back(HexToPubKey(keys[i].get_str()));
} else {
throw JSONRPCError(
RPC_INVALID_ADDRESS_OR_KEY,
strprintf("Invalid public key: %s\n", keys[i].get_str()));
}
}
+ // Get the output type
+ OutputType output_type = OutputType::LEGACY;
+
// Construct using pay-to-script-hash:
- CScript inner = CreateMultisigRedeemscript(required, pubkeys);
- CScriptID innerID(inner);
+ const CScript inner = CreateMultisigRedeemscript(required, pubkeys);
+ CBasicKeyStore keystore;
+ const CTxDestination dest =
+ AddAndGetDestinationForScript(keystore, inner, output_type);
UniValue result(UniValue::VOBJ);
- result.pushKV("address", EncodeDestination(innerID, config));
+ result.pushKV("address", EncodeDestination(dest, config));
result.pushKV("redeemScript", HexStr(inner.begin(), inner.end()));
return result;
}
static UniValue verifymessage(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 3) {
throw std::runtime_error(
"verifymessage \"address\" \"signature\" \"message\"\n"
"\nVerify a signed message\n"
"\nArguments:\n"
"1. \"address\" (string, required) The bitcoin address to "
"use for the signature.\n"
"2. \"signature\" (string, required) The signature provided "
"by the signer in base 64 encoding (see signmessage).\n"
"3. \"message\" (string, required) The message that was "
"signed.\n"
"\nResult:\n"
"true|false (boolean) If the signature is verified or not.\n"
"\nExamples:\n"
"\nUnlock the wallet for 30 seconds\n" +
HelpExampleCli("walletpassphrase", "\"mypassphrase\" 30") +
"\nCreate the signature\n" +
HelpExampleCli(
"signmessage",
"\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"my message\"") +
"\nVerify the signature\n" +
HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4"
"XX\" \"signature\" \"my "
"message\"") +
"\nAs json rpc\n" +
HelpExampleRpc("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4"
"XX\", \"signature\", \"my "
"message\""));
}
LOCK(cs_main);
std::string strAddress = request.params[0].get_str();
std::string strSign = request.params[1].get_str();
std::string strMessage = request.params[2].get_str();
CTxDestination destination =
DecodeDestination(strAddress, config.GetChainParams());
if (!IsValidDestination(destination)) {
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
}
const CKeyID *keyID = boost::get<CKeyID>(&destination);
if (!keyID) {
throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
}
bool fInvalid = false;
std::vector<uint8_t> vchSig = DecodeBase64(strSign.c_str(), &fInvalid);
if (fInvalid) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Malformed base64 encoding");
}
CHashWriter ss(SER_GETHASH, 0);
ss << strMessageMagic;
ss << strMessage;
CPubKey pubkey;
if (!pubkey.RecoverCompact(ss.GetHash(), vchSig)) {
return false;
}
return (pubkey.GetID() == *keyID);
}
static UniValue signmessagewithprivkey(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 2) {
throw std::runtime_error(
"signmessagewithprivkey \"privkey\" \"message\"\n"
"\nSign a message with the private key of an address\n"
"\nArguments:\n"
"1. \"privkey\" (string, required) The private key to sign "
"the message with.\n"
"2. \"message\" (string, required) The message to create a "
"signature of.\n"
"\nResult:\n"
"\"signature\" (string) The signature of the message "
"encoded in base 64\n"
"\nExamples:\n"
"\nCreate the signature\n" +
HelpExampleCli("signmessagewithprivkey",
"\"privkey\" \"my message\"") +
"\nVerify the signature\n" +
HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4"
"XX\" \"signature\" \"my "
"message\"") +
"\nAs json rpc\n" +
HelpExampleRpc("signmessagewithprivkey",
"\"privkey\", \"my message\""));
}
std::string strPrivkey = request.params[0].get_str();
std::string strMessage = request.params[1].get_str();
CKey key = DecodeSecret(strPrivkey);
if (!key.IsValid()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key");
}
CHashWriter ss(SER_GETHASH, 0);
ss << strMessageMagic;
ss << strMessage;
std::vector<uint8_t> vchSig;
if (!key.SignCompact(ss.GetHash(), vchSig)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed");
}
return EncodeBase64(vchSig.data(), vchSig.size());
}
static UniValue setmocktime(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"setmocktime timestamp\n"
"\nSet the local time to given timestamp (-regtest only)\n"
"\nArguments:\n"
"1. timestamp (integer, required) Unix seconds-since-epoch "
"timestamp\n"
" Pass 0 to go back to using the system time.");
}
if (!config.GetChainParams().MineBlocksOnDemand()) {
throw std::runtime_error(
"setmocktime for regression testing (-regtest mode) only");
}
// For now, don't change mocktime if we're in the middle of validation, as
// this could have an effect on mempool time-based eviction, as well as
// IsInitialBlockDownload().
// TODO: figure out the right way to synchronize around mocktime, and
// ensure all call sites of GetTime() are accessing this safely.
LOCK(cs_main);
RPCTypeCheck(request.params, {UniValue::VNUM});
SetMockTime(request.params[0].get_int64());
return NullUniValue;
}
static UniValue RPCLockedMemoryInfo() {
LockedPool::Stats stats = LockedPoolManager::Instance().stats();
UniValue obj(UniValue::VOBJ);
obj.pushKV("used", uint64_t(stats.used));
obj.pushKV("free", uint64_t(stats.free));
obj.pushKV("total", uint64_t(stats.total));
obj.pushKV("locked", uint64_t(stats.locked));
obj.pushKV("chunks_used", uint64_t(stats.chunks_used));
obj.pushKV("chunks_free", uint64_t(stats.chunks_free));
return obj;
}
#ifdef HAVE_MALLOC_INFO
static std::string RPCMallocInfo() {
char *ptr = nullptr;
size_t size = 0;
FILE *f = open_memstream(&ptr, &size);
if (f) {
malloc_info(0, f);
fclose(f);
if (ptr) {
std::string rv(ptr, size);
free(ptr);
return rv;
}
}
return "";
}
#endif
static UniValue getmemoryinfo(const Config &config,
const JSONRPCRequest &request) {
/* Please, avoid using the word "pool" here in the RPC interface or help,
* as users will undoubtedly confuse it with the other "memory pool"
*/
if (request.fHelp || request.params.size() > 1) {
throw std::runtime_error(
"getmemoryinfo (\"mode\")\n"
"Returns an object containing information about memory usage.\n"
"Arguments:\n"
"1. \"mode\" determines what kind of information is returned. This "
"argument is optional, the default mode is \"stats\".\n"
" - \"stats\" returns general statistics about memory usage in "
"the daemon.\n"
" - \"mallocinfo\" returns an XML string describing low-level "
"heap state (only available if compiled with glibc 2.10+).\n"
"\nResult (mode \"stats\"):\n"
"{\n"
" \"locked\": { (json object) Information about "
"locked memory manager\n"
" \"used\": xxxxx, (numeric) Number of bytes used\n"
" \"free\": xxxxx, (numeric) Number of bytes available "
"in current arenas\n"
" \"total\": xxxxxxx, (numeric) Total number of bytes "
"managed\n"
" \"locked\": xxxxxx, (numeric) Amount of bytes that "
"succeeded locking. If this number is smaller than total, locking "
"pages failed at some point and key data could be swapped to "
"disk.\n"
" \"chunks_used\": xxxxx, (numeric) Number allocated chunks\n"
" \"chunks_free\": xxxxx, (numeric) Number unused chunks\n"
" }\n"
"}\n"
"\nResult (mode \"mallocinfo\"):\n"
"\"<malloc version=\"1\">...\"\n"
"\nExamples:\n" +
HelpExampleCli("getmemoryinfo", "") +
HelpExampleRpc("getmemoryinfo", ""));
}
std::string mode =
request.params[0].isNull() ? "stats" : request.params[0].get_str();
if (mode == "stats") {
UniValue obj(UniValue::VOBJ);
obj.pushKV("locked", RPCLockedMemoryInfo());
return obj;
} else if (mode == "mallocinfo") {
#ifdef HAVE_MALLOC_INFO
return RPCMallocInfo();
#else
throw JSONRPCError(
RPC_INVALID_PARAMETER,
"mallocinfo is only available when compiled with glibc 2.10+");
#endif
} else {
throw JSONRPCError(RPC_INVALID_PARAMETER, "unknown mode " + mode);
}
}
static void EnableOrDisableLogCategories(UniValue cats, bool enable) {
cats = cats.get_array();
for (size_t i = 0; i < cats.size(); ++i) {
std::string cat = cats[i].get_str();
bool success;
if (enable) {
success = GetLogger().EnableCategory(cat);
} else {
success = GetLogger().DisableCategory(cat);
}
if (!success) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"unknown logging category " + cat);
}
}
}
static UniValue logging(const Config &config, const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() > 2) {
throw std::runtime_error(
"logging ( <include> <exclude> )\n"
"Gets and sets the logging configuration.\n"
"When called without an argument, returns the list of categories "
"with status that are currently being debug logged or not.\n"
"When called with arguments, adds or removes categories from debug "
"logging and return the lists above.\n"
"The arguments are evaluated in order \"include\", \"exclude\".\n"
"If an item is both included and excluded, it will thus end up "
"being excluded.\n"
"The valid logging categories are: " +
ListLogCategories() +
"\n"
"In addition, the following are available as category names with "
"special meanings:\n"
" - \"all\", \"1\" : represent all logging categories.\n"
" - \"none\", \"0\" : even if other logging categories are "
"specified, ignore all of them.\n"
"\nArguments:\n"
"1. \"include\" (array of strings, optional) A json array "
"of categories to add debug logging\n"
" [\n"
" \"category\" (string) the valid logging category\n"
" ,...\n"
" ]\n"
"2. \"exclude\" (array of strings, optional) A json array "
"of categories to remove debug logging\n"
" [\n"
" \"category\" (string) the valid logging category\n"
" ,...\n"
" ]\n"
"\nResult:\n"
"{ (json object where keys are the logging "
"categories, and values indicates its status\n"
" \"category\": 0|1, (numeric) if being debug logged or not. "
"0:inactive, 1:active\n"
" ...\n"
"}\n"
"\nExamples:\n" +
HelpExampleCli("logging", "\"[\\\"all\\\"]\" \"[\\\"http\\\"]\"") +
HelpExampleRpc("logging", "[\"all\"], \"[libevent]\""));
}
uint32_t original_log_categories = GetLogger().GetCategoryMask();
if (request.params[0].isArray()) {
EnableOrDisableLogCategories(request.params[0], true);
}
if (request.params[1].isArray()) {
EnableOrDisableLogCategories(request.params[1], false);
}
uint32_t updated_log_categories = GetLogger().GetCategoryMask();
uint32_t changed_log_categories =
original_log_categories ^ updated_log_categories;
/**
* Update libevent logging if BCLog::LIBEVENT has changed.
* If the library version doesn't allow it, UpdateHTTPServerLogging()
* returns false, in which case we should clear the BCLog::LIBEVENT flag.
* Throw an error if the user has explicitly asked to change only the
* libevent flag and it failed.
*/
if (changed_log_categories & BCLog::LIBEVENT) {
if (!UpdateHTTPServerLogging(
GetLogger().WillLogCategory(BCLog::LIBEVENT))) {
GetLogger().DisableCategory(BCLog::LIBEVENT);
if (changed_log_categories == BCLog::LIBEVENT) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"libevent logging cannot be updated when "
"using libevent before v2.1.1.");
}
}
}
UniValue result(UniValue::VOBJ);
std::vector<CLogCategoryActive> vLogCatActive = ListActiveLogCategories();
for (const auto &logCatActive : vLogCatActive) {
result.pushKV(logCatActive.category, logCatActive.active);
}
return result;
}
static UniValue echo(const Config &config, const JSONRPCRequest &request) {
if (request.fHelp) {
throw std::runtime_error(
"echo|echojson \"message\" ...\n"
"\nSimply echo back the input arguments. This command is for "
"testing.\n"
"\nThe difference between echo and echojson is that echojson has "
"argument conversion enabled in the client-side table in"
"bitcoin-cli and the GUI. There is no server-side difference.");
}
return request.params;
}
static UniValue getinfo_deprecated(const Config &config,
const JSONRPCRequest &request) {
throw JSONRPCError(RPC_METHOD_NOT_FOUND,
"getinfo\n"
"\nThis call was removed in version 0.19.8. Use the "
"appropriate fields from:\n"
"- getblockchaininfo: blocks, difficulty, chain\n"
"- getnetworkinfo: version, protocolversion, "
"timeoffset, connections, proxy, relayfee, warnings\n"
"- getwalletinfo: balance, keypoololdest, keypoolsize, "
"paytxfee, unlocked_until, walletversion\n"
"\nbitcoin-cli has the option -getinfo to collect and "
"format these in the old format.");
}
// clang-format off
static const ContextFreeRPCCommand commands[] = {
// category name actor (function) argNames
// ------------------- ------------------------ ---------------------- ----------
{ "control", "getmemoryinfo", getmemoryinfo, {"mode"} },
{ "control", "logging", logging, {"include", "exclude"} },
{ "util", "validateaddress", validateaddress, {"address"} }, /* uses wallet if enabled */
{ "util", "createmultisig", createmultisig, {"nrequired","keys"} },
{ "util", "verifymessage", verifymessage, {"address","signature","message"} },
{ "util", "signmessagewithprivkey", signmessagewithprivkey, {"privkey","message"} },
/* Not shown in help */
{ "hidden", "setmocktime", setmocktime, {"timestamp"}},
{ "hidden", "echo", echo, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}},
{ "hidden", "echojson", echo, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}},
{ "hidden", "getinfo", getinfo_deprecated, {}},
};
// clang-format on
void RegisterMiscRPCCommands(CRPCTable &t) {
for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) {
t.appendCommand(commands[vcidx].name, &commands[vcidx]);
}
}
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 084db1879..63d9cabbb 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -1,5516 +1,5514 @@
// Copyright (c) 2010 Satoshi Nakamoto
// Copyright (c) 2009-2016 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 <amount.h>
#include <chain.h>
#include <chainparams.h> // for GetConsensus.
#include <config.h>
#include <consensus/validation.h>
#include <core_io.h>
#include <key_io.h>
#include <net.h>
+#include <outputtype.h>
#include <policy/fees.h>
#include <policy/policy.h>
#include <rpc/mining.h>
#include <rpc/misc.h>
#include <rpc/rawtransaction.h>
#include <rpc/server.h>
#include <rpc/util.h>
#include <timedata.h>
#include <util/moneystr.h>
#include <util/system.h>
#include <validation.h>
#include <wallet/coincontrol.h>
#include <wallet/rpcwallet.h>
#include <wallet/wallet.h>
#include <wallet/walletdb.h>
#include <wallet/walletutil.h>
// Input src/init.h (not wallet/init.h) for StartShutdown
#include <init.h>
#include <univalue.h>
#include <event2/http.h>
#include <functional>
static const std::string WALLET_ENDPOINT_BASE = "/wallet/";
static std::string urlDecode(const std::string &urlEncoded) {
std::string res;
if (!urlEncoded.empty()) {
char *decoded = evhttp_uridecode(urlEncoded.c_str(), false, nullptr);
if (decoded) {
res = std::string(decoded);
free(decoded);
}
}
return res;
}
bool GetWalletNameFromJSONRPCRequest(const JSONRPCRequest &request,
std::string &wallet_name) {
if (request.URI.substr(0, WALLET_ENDPOINT_BASE.size()) ==
WALLET_ENDPOINT_BASE) {
// wallet endpoint was used
wallet_name =
urlDecode(request.URI.substr(WALLET_ENDPOINT_BASE.size()));
return true;
}
return false;
}
std::shared_ptr<CWallet>
GetWalletForJSONRPCRequest(const JSONRPCRequest &request) {
std::string wallet_name;
if (GetWalletNameFromJSONRPCRequest(request, wallet_name)) {
std::shared_ptr<CWallet> pwallet = GetWallet(wallet_name);
if (!pwallet) {
throw JSONRPCError(
RPC_WALLET_NOT_FOUND,
"Requested wallet does not exist or is not loaded");
}
return pwallet;
}
std::vector<std::shared_ptr<CWallet>> wallets = GetWallets();
return wallets.size() == 1 || (request.fHelp && wallets.size() > 0)
? wallets[0]
: nullptr;
}
std::string HelpRequiringPassphrase(CWallet *const pwallet) {
return pwallet && pwallet->IsCrypted() ? "\nRequires wallet passphrase to "
"be set with walletpassphrase "
"call."
: "";
}
bool EnsureWalletIsAvailable(CWallet *const pwallet, bool avoidException) {
if (pwallet) {
return true;
}
if (avoidException) {
return false;
}
if (!HasWallets()) {
throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found (wallet "
"method is disabled because "
"no wallet is loaded)");
}
throw JSONRPCError(RPC_WALLET_NOT_SPECIFIED,
"Wallet file not specified (must request wallet RPC "
"through /wallet/<filename> uri-path).");
}
void EnsureWalletIsUnlocked(CWallet *const pwallet) {
if (pwallet->IsLocked()) {
throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the "
"wallet passphrase with "
"walletpassphrase first.");
}
}
static void WalletTxToJSON(const CWalletTx &wtx, UniValue &entry)
EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
int confirms = wtx.GetDepthInMainChain();
entry.pushKV("confirmations", confirms);
if (wtx.IsCoinBase()) {
entry.pushKV("generated", true);
}
if (confirms > 0) {
entry.pushKV("blockhash", wtx.hashBlock.GetHex());
entry.pushKV("blockindex", wtx.nIndex);
entry.pushKV("blocktime",
LookupBlockIndex(wtx.hashBlock)->GetBlockTime());
} else {
entry.pushKV("trusted", wtx.IsTrusted());
}
uint256 hash = wtx.GetId();
entry.pushKV("txid", hash.GetHex());
UniValue conflicts(UniValue::VARR);
for (const uint256 &conflict : wtx.GetConflicts()) {
conflicts.push_back(conflict.GetHex());
}
entry.pushKV("walletconflicts", conflicts);
entry.pushKV("time", wtx.GetTxTime());
entry.pushKV("timereceived", (int64_t)wtx.nTimeReceived);
for (const std::pair<const std::string, std::string> &item : wtx.mapValue) {
entry.pushKV(item.first, item.second);
}
}
static std::string LabelFromValue(const UniValue &value) {
std::string label = value.get_str();
if (label == "*") {
throw JSONRPCError(RPC_WALLET_INVALID_LABEL_NAME, "Invalid label name");
}
return label;
}
static UniValue getnewaddress(const Config &config,
const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() > 2) {
throw std::runtime_error(
"getnewaddress ( \"label\" )\n"
"\nReturns a new Bitcoin address for receiving payments.\n"
"If 'label' is specified, it is added to the address book \n"
"so payments received with the address will be associated with "
"'label'.\n"
"\nArguments:\n"
"1. \"label\" (string, optional) The label name for the "
"address to be linked to. If not provided, the default label \"\" "
"is used. It can also be set to the empty string \"\" to represent "
"the default label. The label does not need to exist, it will be "
"created if there is no label by the given name.\n"
"\nResult:\n"
"\"address\" (string) The new bitcoin address\n"
"\nExamples:\n" +
HelpExampleRpc("getnewaddress", ""));
}
LOCK2(cs_main, pwallet->cs_wallet);
// Parse the label first so we don't generate a key if there's an error
std::string label;
if (!request.params[0].isNull()) {
label = LabelFromValue(request.params[0]);
}
OutputType output_type = pwallet->m_default_address_type;
if (!request.params[1].isNull()) {
if (!ParseOutputType(request.params[1].get_str(), output_type)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
strprintf("Unknown address type '%s'",
request.params[1].get_str()));
}
}
if (!pwallet->IsLocked()) {
pwallet->TopUpKeyPool();
}
// Generate a new key that is added to wallet
CPubKey newKey;
if (!pwallet->GetKeyFromPool(newKey)) {
throw JSONRPCError(
RPC_WALLET_KEYPOOL_RAN_OUT,
"Error: Keypool ran out, please call keypoolrefill first");
}
pwallet->LearnRelatedScripts(newKey, output_type);
CTxDestination dest = GetDestinationForKey(newKey, output_type);
pwallet->SetAddressBook(dest, label, "receive");
return EncodeDestination(dest, config);
}
CTxDestination GetLabelDestination(CWallet *const pwallet,
const std::string &label,
bool bForceNew = false) {
CTxDestination dest;
if (!pwallet->GetLabelDestination(dest, label, bForceNew)) {
throw JSONRPCError(
RPC_WALLET_KEYPOOL_RAN_OUT,
"Error: Keypool ran out, please call keypoolrefill first");
}
return dest;
}
static UniValue getlabeladdress(const Config &config,
const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (!IsDeprecatedRPCEnabled(gArgs, "accounts") &&
request.strMethod == "getaccountaddress") {
if (request.fHelp) {
throw std::runtime_error(
"getaccountaddress (Deprecated, will be removed in v0.21. To "
"use this command, start bitcoind with "
"-deprecatedrpc=accounts)");
}
throw JSONRPCError(
RPC_METHOD_DEPRECATED,
"getaccountaddress is deprecated and will be removed in v0.21. To "
"use this command, start bitcoind with -deprecatedrpc=accounts.");
}
if (request.fHelp || request.params.size() < 1 ||
request.params.size() > 2) {
throw std::runtime_error(
"getlabeladdress \"label\" ( force ) \n"
"\nReturns the default receiving address for this label. This will "
"reset to a fresh address once there's a transaction that spends "
"to it.\n"
"\nArguments:\n"
"1. \"label\" (string, required) The label for the "
"address. It can also be set to the empty string \"\" to represent "
"the default label.\n"
"2. \"force\" (bool, optional) Whether the label should be "
"created if it does not yet exist. If False, the RPC will return "
"an error if called with a label that doesn't exist.\n"
" Defaults to false (unless the "
"getaccountaddress method alias is being called, in which case "
"defaults to true for backwards compatibility).\n"
"\nResult:\n"
"\"address\" (string) The current receiving address for "
"the label.\n"
"\nExamples:\n" +
HelpExampleCli("getlabeladdress", "") +
HelpExampleCli("getlabeladdress", "\"\"") +
HelpExampleCli("getlabeladdress", "\"mylabel\"") +
HelpExampleRpc("getlabeladdress", "\"mylabel\""));
}
LOCK2(cs_main, pwallet->cs_wallet);
// Parse the label first so we don't generate a key if there's an error
std::string label = LabelFromValue(request.params[0]);
bool force = request.strMethod == "getaccountaddress";
if (!request.params[1].isNull()) {
force = request.params[1].get_bool();
}
bool label_found = false;
for (const std::pair<const CTxDestination, CAddressBookData> &item :
pwallet->mapAddressBook) {
if (item.second.name == label) {
label_found = true;
break;
}
}
if (!force && !label_found) {
throw JSONRPCError(RPC_WALLET_INVALID_LABEL_NAME,
std::string("No addresses with label " + label));
}
UniValue ret(UniValue::VSTR);
ret = EncodeDestination(GetLabelDestination(pwallet, label), config);
return ret;
}
static UniValue getrawchangeaddress(const Config &config,
const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() > 1) {
throw std::runtime_error(
"getrawchangeaddress\n"
"\nReturns a new Bitcoin address, for receiving change.\n"
"This is for use with raw transactions, NOT normal use.\n"
"\nResult:\n"
"\"address\" (string) The address\n"
"\nExamples:\n" +
HelpExampleCli("getrawchangeaddress", "") +
HelpExampleRpc("getrawchangeaddress", ""));
}
LOCK2(cs_main, pwallet->cs_wallet);
if (!pwallet->IsLocked()) {
pwallet->TopUpKeyPool();
}
OutputType output_type =
pwallet->m_default_change_type != OutputType::CHANGE_AUTO
? pwallet->m_default_change_type
: pwallet->m_default_address_type;
if (!request.params[0].isNull()) {
if (!ParseOutputType(request.params[0].get_str(), output_type)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
strprintf("Unknown address type '%s'",
request.params[0].get_str()));
}
}
CReserveKey reservekey(pwallet);
CPubKey vchPubKey;
if (!reservekey.GetReservedKey(vchPubKey, true)) {
throw JSONRPCError(
RPC_WALLET_KEYPOOL_RAN_OUT,
"Error: Keypool ran out, please call keypoolrefill first");
}
reservekey.KeepKey();
pwallet->LearnRelatedScripts(vchPubKey, output_type);
CTxDestination dest = GetDestinationForKey(vchPubKey, output_type);
return EncodeDestination(dest, config);
}
static UniValue setlabel(const Config &config, const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (!IsDeprecatedRPCEnabled(gArgs, "accounts") &&
request.strMethod == "setaccount") {
if (request.fHelp) {
throw std::runtime_error(
"setaccount (Deprecated, will be removed in v0.21. To use this "
"command, start bitcoind with -deprecatedrpc=accounts)");
}
throw JSONRPCError(
RPC_METHOD_DEPRECATED,
"setaccount is deprecated and will be removed in v0.21. To use "
"this command, start bitcoind with -deprecatedrpc=accounts.");
}
if (request.fHelp || request.params.size() != 2) {
throw std::runtime_error(
"setlabel \"address\" \"label\"\n"
"\nSets the label associated with the given address.\n"
"\nArguments:\n"
"1. \"address\" (string, required) The bitcoin address to "
"be associated with a label.\n"
"2. \"label\" (string, required) The label to assign to "
"the address.\n"
"\nExamples:\n" +
HelpExampleCli("setlabel",
"\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"tabby\"") +
HelpExampleRpc(
"setlabel",
"\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"tabby\""));
}
LOCK2(cs_main, pwallet->cs_wallet);
CTxDestination dest =
DecodeDestination(request.params[0].get_str(), config.GetChainParams());
if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Invalid Bitcoin address");
}
std::string label = LabelFromValue(request.params[1]);
if (IsMine(*pwallet, dest)) {
// Detect when changing the label of an address that is the receiving
// address of another label. If so, delete the account record for it.
// Labels, unlike addresses, can be deleted, and if we wouldn't do this,
// the record would stick around forever.
if (pwallet->mapAddressBook.count(dest)) {
std::string old_label = pwallet->mapAddressBook[dest].name;
if (old_label != label &&
dest == GetLabelDestination(pwallet, old_label)) {
pwallet->DeleteLabel(old_label);
}
}
pwallet->SetAddressBook(dest, label, "receive");
} else {
pwallet->SetAddressBook(dest, label, "send");
}
return NullUniValue;
}
static UniValue getaccount(const Config &config,
const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (!IsDeprecatedRPCEnabled(gArgs, "accounts")) {
if (request.fHelp) {
throw std::runtime_error(
"getaccount (Deprecated, will be removed in v0.21. To use this "
"command, start bitcoind with -deprecatedrpc=accounts)");
}
throw JSONRPCError(
RPC_METHOD_DEPRECATED,
"getaccount is deprecated and will be removed in v0.21. To use "
"this command, start bitcoind with -deprecatedrpc=accounts.");
}
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"getaccount \"address\"\n"
"\nDEPRECATED. Returns the account associated with the given "
"address.\n"
"\nArguments:\n"
"1. \"address\" (string, required) The bitcoin address for "
"account lookup.\n"
"\nResult:\n"
"\"accountname\" (string) the account address\n"
"\nExamples:\n" +
HelpExampleCli("getaccount",
"\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\"") +
HelpExampleRpc("getaccount",
"\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\""));
}
LOCK2(cs_main, pwallet->cs_wallet);
CTxDestination dest =
DecodeDestination(request.params[0].get_str(), config.GetChainParams());
if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Invalid Bitcoin address");
}
std::string strAccount;
std::map<CTxDestination, CAddressBookData>::iterator mi =
pwallet->mapAddressBook.find(dest);
if (mi != pwallet->mapAddressBook.end() && !(*mi).second.name.empty()) {
strAccount = (*mi).second.name;
}
return strAccount;
}
static UniValue getaddressesbyaccount(const Config &config,
const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (!IsDeprecatedRPCEnabled(gArgs, "accounts")) {
if (request.fHelp) {
throw std::runtime_error(
"getaddressbyaccount (Deprecated, will be removed in v0.21. To "
"use this command, start bitcoind with "
"-deprecatedrpc=accounts)");
}
throw JSONRPCError(RPC_METHOD_DEPRECATED,
"getaddressesbyaccount is deprecated and will be "
"removed in v0.21. To use this command, start "
"bitcoind with -deprecatedrpc=accounts.");
}
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"getaddressesbyaccount \"account\"\n"
"\nDEPRECATED. Returns the list of addresses for the given "
"account.\n"
"\nArguments:\n"
"1. \"account\" (string, required) The account name.\n"
"\nResult:\n"
"[ (json array of string)\n"
" \"address\" (string) a bitcoin address associated with "
"the given account\n"
" ,...\n"
"]\n"
"\nExamples:\n" +
HelpExampleCli("getaddressesbyaccount", "\"tabby\"") +
HelpExampleRpc("getaddressesbyaccount", "\"tabby\""));
}
LOCK2(cs_main, pwallet->cs_wallet);
std::string strAccount = LabelFromValue(request.params[0]);
// Find all addresses that have the given account
UniValue ret(UniValue::VARR);
for (const std::pair<const CTxDestination, CAddressBookData> &item :
pwallet->mapAddressBook) {
const CTxDestination &dest = item.first;
const std::string &strName = item.second.name;
if (strName == strAccount) {
ret.push_back(EncodeDestination(dest, config));
}
}
return ret;
}
static CTransactionRef SendMoney(CWallet *const pwallet,
const CTxDestination &address, Amount nValue,
bool fSubtractFeeFromAmount,
mapValue_t mapValue, std::string fromAccount) {
Amount curBalance = pwallet->GetBalance();
// Check amount
if (nValue <= Amount::zero()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount");
}
if (nValue > curBalance) {
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds");
}
if (pwallet->GetBroadcastTransactions() && !g_connman) {
throw JSONRPCError(
RPC_CLIENT_P2P_DISABLED,
"Error: Peer-to-peer functionality missing or disabled");
}
// Parse Bitcoin address
CScript scriptPubKey = GetScriptForDestination(address);
// Create and send the transaction
CReserveKey reservekey(pwallet);
Amount nFeeRequired;
std::string strError;
std::vector<CRecipient> vecSend;
int nChangePosRet = -1;
CRecipient recipient = {scriptPubKey, nValue, fSubtractFeeFromAmount};
vecSend.push_back(recipient);
CCoinControl coinControl;
CTransactionRef tx;
if (!pwallet->CreateTransaction(vecSend, tx, reservekey, nFeeRequired,
nChangePosRet, strError, coinControl)) {
if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance) {
strError = strprintf("Error: This transaction requires a "
"transaction fee of at least %s",
FormatMoney(nFeeRequired));
}
throw JSONRPCError(RPC_WALLET_ERROR, strError);
}
CValidationState state;
if (!pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */,
std::move(fromAccount), reservekey,
g_connman.get(), state)) {
strError =
strprintf("Error: The transaction was rejected! Reason given: %s",
FormatStateMessage(state));
throw JSONRPCError(RPC_WALLET_ERROR, strError);
}
return tx;
}
static UniValue sendtoaddress(const Config &config,
const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() < 2 ||
request.params.size() > 5) {
throw std::runtime_error(
"sendtoaddress \"address\" amount ( \"comment\" \"comment_to\" "
"subtractfeefromamount )\n"
"\nSend an amount to a given address.\n" +
HelpRequiringPassphrase(pwallet) +
"\nArguments:\n"
"1. \"address\" (string, required) The bitcoin address "
"to send to.\n"
"2. \"amount\" (numeric or string, required) The "
"amount in " +
CURRENCY_UNIT +
" to send. eg 0.1\n"
"3. \"comment\" (string, optional) A comment used to "
"store what the transaction is for. \n"
" This is not part of the transaction, "
"just kept in your wallet.\n"
"4. \"comment_to\" (string, optional) A comment to store "
"the name of the person or organization \n"
" to which you're sending the "
"transaction. This is not part of the \n"
" transaction, just kept in your "
"wallet.\n"
"5. subtractfeefromamount (boolean, optional, default=false) The "
"fee will be deducted from the amount being sent.\n"
" The recipient will receive less "
"bitcoins than you enter in the amount field.\n"
"\nResult:\n"
"\"txid\" (string) The transaction id.\n"
"\nExamples:\n" +
HelpExampleCli("sendtoaddress",
"\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1") +
HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvay"
"dd\" 0.1 \"donation\" \"seans "
"outpost\"") +
HelpExampleCli(
"sendtoaddress",
"\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 \"\" \"\" true") +
HelpExampleRpc("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvay"
"dd\", 0.1, \"donation\", \"seans "
"outpost\""));
}
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
LOCK2(cs_main, pwallet->cs_wallet);
CTxDestination dest =
DecodeDestination(request.params[0].get_str(), config.GetChainParams());
if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
}
// Amount
Amount nAmount = AmountFromValue(request.params[1]);
if (nAmount <= Amount::zero()) {
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send");
}
// Wallet comments
mapValue_t mapValue;
if (!request.params[2].isNull() && !request.params[2].get_str().empty()) {
mapValue["comment"] = request.params[2].get_str();
}
if (!request.params[3].isNull() && !request.params[3].get_str().empty()) {
mapValue["to"] = request.params[3].get_str();
}
bool fSubtractFeeFromAmount = false;
if (!request.params[4].isNull()) {
fSubtractFeeFromAmount = request.params[4].get_bool();
}
EnsureWalletIsUnlocked(pwallet);
CTransactionRef tx =
SendMoney(pwallet, dest, nAmount, fSubtractFeeFromAmount,
std::move(mapValue), {} /* fromAccount */);
return tx->GetId().GetHex();
}
static UniValue listaddressgroupings(const Config &config,
const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() != 0) {
throw std::runtime_error(
"listaddressgroupings\n"
"\nLists groups of addresses which have had their common "
"ownership\n"
"made public by common use as inputs or as the resulting change\n"
"in past transactions\n"
"\nResult:\n"
"[\n"
" [\n"
" [\n"
" \"address\", (string) The bitcoin address\n"
" amount, (numeric) The amount in " +
CURRENCY_UNIT +
"\n"
" \"label\" (string, optional) The label\n"
" ]\n"
" ,...\n"
" ]\n"
" ,...\n"
"]\n"
"\nExamples:\n" +
HelpExampleCli("listaddressgroupings", "") +
HelpExampleRpc("listaddressgroupings", ""));
}
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
LOCK2(cs_main, pwallet->cs_wallet);
UniValue jsonGroupings(UniValue::VARR);
std::map<CTxDestination, Amount> balances = pwallet->GetAddressBalances();
for (const std::set<CTxDestination> &grouping :
pwallet->GetAddressGroupings()) {
UniValue jsonGrouping(UniValue::VARR);
for (const CTxDestination &address : grouping) {
UniValue addressInfo(UniValue::VARR);
addressInfo.push_back(EncodeDestination(address, config));
addressInfo.push_back(ValueFromAmount(balances[address]));
if (pwallet->mapAddressBook.find(address) !=
pwallet->mapAddressBook.end()) {
addressInfo.push_back(
pwallet->mapAddressBook.find(address)->second.name);
}
jsonGrouping.push_back(addressInfo);
}
jsonGroupings.push_back(jsonGrouping);
}
return jsonGroupings;
}
static UniValue signmessage(const Config &config,
const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() != 2) {
throw std::runtime_error(
"signmessage \"address\" \"message\"\n"
"\nSign a message with the private key of an address" +
HelpRequiringPassphrase(pwallet) +
"\n"
"\nArguments:\n"
"1. \"address\" (string, required) The bitcoin address to "
"use for the private key.\n"
"2. \"message\" (string, required) The message to create a "
"signature of.\n"
"\nResult:\n"
"\"signature\" (string) The signature of the message "
"encoded in base 64\n"
"\nExamples:\n"
"\nUnlock the wallet for 30 seconds\n" +
HelpExampleCli("walletpassphrase", "\"mypassphrase\" 30") +
"\nCreate the signature\n" +
HelpExampleCli(
"signmessage",
"\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"my message\"") +
"\nVerify the signature\n" +
HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4"
"XX\" \"signature\" \"my "
"message\"") +
"\nAs json rpc\n" +
HelpExampleRpc(
"signmessage",
"\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"my message\""));
}
LOCK2(cs_main, pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet);
std::string strAddress = request.params[0].get_str();
std::string strMessage = request.params[1].get_str();
CTxDestination dest =
DecodeDestination(strAddress, config.GetChainParams());
if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
}
const CKeyID *keyID = boost::get<CKeyID>(&dest);
if (!keyID) {
throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
}
CKey key;
if (!pwallet->GetKey(*keyID, key)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available");
}
CHashWriter ss(SER_GETHASH, 0);
ss << strMessageMagic;
ss << strMessage;
std::vector<uint8_t> vchSig;
if (!key.SignCompact(ss.GetHash(), vchSig)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed");
}
return EncodeBase64(vchSig.data(), vchSig.size());
}
static UniValue getreceivedbyaddress(const Config &config,
const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() < 1 ||
request.params.size() > 2) {
throw std::runtime_error(
"getreceivedbyaddress \"address\" ( minconf )\n"
"\nReturns the total amount received by the given address in "
"transactions with at least minconf confirmations.\n"
"\nArguments:\n"
"1. \"address\" (string, required) The bitcoin address for "
"transactions.\n"
"2. minconf (numeric, optional, default=1) Only "
"include transactions confirmed at least this many times.\n"
"\nResult:\n"
"amount (numeric) The total amount in " +
CURRENCY_UNIT +
" received at this address.\n"
"\nExamples:\n"
"\nThe amount from transactions with at least 1 confirmation\n" +
HelpExampleCli("getreceivedbyaddress",
"\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\"") +
"\nThe amount including unconfirmed transactions, zero "
"confirmations\n" +
HelpExampleCli("getreceivedbyaddress",
"\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" 0") +
"\nThe amount with at least 6 confirmations\n" +
HelpExampleCli("getreceivedbyaddress",
"\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" 6") +
"\nAs a json rpc call\n" +
HelpExampleRpc("getreceivedbyaddress",
"\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", 6"));
}
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
LOCK2(cs_main, pwallet->cs_wallet);
// Bitcoin address
CTxDestination dest =
DecodeDestination(request.params[0].get_str(), config.GetChainParams());
if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Invalid Bitcoin address");
}
CScript scriptPubKey = GetScriptForDestination(dest);
if (!IsMine(*pwallet, scriptPubKey)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Address not found in wallet");
}
// Minimum confirmations
int nMinDepth = 1;
if (!request.params[1].isNull()) {
nMinDepth = request.params[1].get_int();
}
// Tally
Amount nAmount = Amount::zero();
for (const std::pair<const TxId, CWalletTx> &pairWtx : pwallet->mapWallet) {
const CWalletTx &wtx = pairWtx.second;
CValidationState state;
if (wtx.IsCoinBase() ||
!ContextualCheckTransactionForCurrentBlock(
config.GetChainParams().GetConsensus(), *wtx.tx, state)) {
continue;
}
for (const CTxOut &txout : wtx.tx->vout) {
if (txout.scriptPubKey == scriptPubKey) {
if (wtx.GetDepthInMainChain() >= nMinDepth) {
nAmount += txout.nValue;
}
}
}
}
return ValueFromAmount(nAmount);
}
static UniValue getreceivedbylabel(const Config &config,
const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (!IsDeprecatedRPCEnabled(gArgs, "accounts") &&
request.strMethod == "getreceivedbyaccount") {
if (request.fHelp) {
throw std::runtime_error(
"getreceivedbyaccount (Deprecated, will be removed in v0.21. "
"To use this command, start bitcoind with "
"-deprecatedrpc=accounts)");
}
throw JSONRPCError(RPC_METHOD_DEPRECATED,
"getreceivedbyaccount is deprecated and will be "
"removed in v0.21. To use this command, start "
"bitcoind with -deprecatedrpc=accounts.");
}
if (request.fHelp || request.params.size() < 1 ||
request.params.size() > 2) {
throw std::runtime_error(
"getreceivedbylabel \"label\" ( minconf )\n"
"\nReturns the total amount received by addresses with <label> in "
"transactions with at least [minconf] confirmations.\n"
"\nArguments:\n"
"1. \"label\" (string, required) The selected label, may be "
"the default label using \"\".\n"
"2. minconf (numeric, optional, default=1) Only include "
"transactions confirmed at least this many times.\n"
"\nResult:\n"
"amount (numeric) The total amount in " +
CURRENCY_UNIT +
" received for this label.\n"
"\nExamples:\n"
"\nAmount received by the default label with at least 1 "
"confirmation\n" +
HelpExampleCli("getreceivedbylabel", "\"\"") +
"\nAmount received at the tabby label including unconfirmed "
"amounts with zero confirmations\n" +
HelpExampleCli("getreceivedbylabel", "\"tabby\" 0") +
"\nThe amount with at least 6 confirmations\n" +
HelpExampleCli("getreceivedbylabel", "\"tabby\" 6") +
"\nAs a json rpc call\n" +
HelpExampleRpc("getreceivedbylabel", "\"tabby\", 6"));
}
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
LOCK2(cs_main, pwallet->cs_wallet);
// Minimum confirmations
int nMinDepth = 1;
if (!request.params[1].isNull()) {
nMinDepth = request.params[1].get_int();
}
// Get the set of pub keys assigned to label
std::string label = LabelFromValue(request.params[0]);
std::set<CTxDestination> setAddress = pwallet->GetLabelAddresses(label);
// Tally
Amount nAmount = Amount::zero();
for (const std::pair<const TxId, CWalletTx> &pairWtx : pwallet->mapWallet) {
const CWalletTx &wtx = pairWtx.second;
CValidationState state;
if (wtx.IsCoinBase() ||
!ContextualCheckTransactionForCurrentBlock(
config.GetChainParams().GetConsensus(), *wtx.tx, state)) {
continue;
}
for (const CTxOut &txout : wtx.tx->vout) {
CTxDestination address;
if (ExtractDestination(txout.scriptPubKey, address) &&
IsMine(*pwallet, address) && setAddress.count(address)) {
if (wtx.GetDepthInMainChain() >= nMinDepth) {
nAmount += txout.nValue;
}
}
}
}
return ValueFromAmount(nAmount);
}
static UniValue getbalance(const Config &config,
const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp ||
(request.params.size() > 3 &&
IsDeprecatedRPCEnabled(gArgs, "accounts")) ||
(request.params.size() != 0 &&
!IsDeprecatedRPCEnabled(gArgs, "accounts"))) {
throw std::runtime_error(
"getbalance ( \"account\" minconf include_watchonly )\n"
"\nIf account is not specified, returns the server's total "
"available balance.\n"
"The available balance is what the wallet considers currently "
"spendable,\n"
"and is thus affected by options which limit spendability such as "
"-spendzeroconfchange.\n"
"If account is specified (DEPRECATED), returns the balance in the "
"account.\n"
"Note that the account \"\" is not the same as leaving the "
"parameter out.\n"
"The server total may be different to the balance in the default "
"\"\" account.\n"
"\nArguments:\n"
"1. \"account\" (string, optional) DEPRECATED. This "
"argument will be removed in v0.21. \n"
" To use this deprecated argument, start "
"bitcoind with -deprecatedrpc=accounts. The account "
"string may be given as a\n"
" specific account name to find the balance "
"associated with wallet keys in\n"
" a named account, or as the empty string "
"(\"\") to find the balance\n"
" associated with wallet keys not in any named "
"account, or as \"*\" to find\n"
" the balance associated with all wallet keys "
"regardless of account.\n"
" When this option is specified, it calculates "
"the balance in a different\n"
" way than when it is not specified, and which "
"can count spends twice when\n"
" there are conflicting pending transactions "
"temporarily resulting in low\n"
" or even negative balances.\n"
" In general, account balance calculation is "
"not considered reliable and\n"
" has resulted in confusing outcomes, so it is "
"recommended to avoid passing\n"
" this argument.\n"
"2. minconf (numeric, optional, default=1) DEPRECATED. "
"Only valid when an account is specified. This argument will be "
"removed in V0.21. To use this deprecated argument, start bitcoind "
"with -deprecatedrpc=accounts. Only include transactions confirmed "
"at least this many times.\n"
"3. include_watchonly (bool, optional, default=false) DEPRECATED. "
"Only valid when an account is specified. This argument will be "
"removed in V0.21. To use this deprecated argument, start bitcoind "
"with -deprecatedrpc=accounts. Also include balance in watch-only "
"addresses (see 'importaddress')\n"
"\nResult:\n"
"amount (numeric) The total amount in " +
CURRENCY_UNIT +
" received for this account.\n"
"\nExamples:\n"
"\nThe total amount in the wallet with 1 or more confirmations\n" +
HelpExampleCli("getbalance", "") +
"\nThe total amount in the wallet at least 6 blocks confirmed\n" +
HelpExampleCli("getbalance", "\"*\" 6") + "\nAs a json rpc call\n" +
HelpExampleRpc("getbalance", "\"*\", 6"));
}
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
LOCK2(cs_main, pwallet->cs_wallet);
if (IsDeprecatedRPCEnabled(gArgs, "accounts")) {
const UniValue &account_value = request.params[0];
const UniValue &minconf = request.params[1];
const UniValue &include_watchonly = request.params[2];
if (account_value.isNull()) {
if (!minconf.isNull()) {
throw JSONRPCError(
RPC_INVALID_PARAMETER,
"getbalance minconf option is only currently supported if "
"an account is specified");
}
if (!include_watchonly.isNull()) {
throw JSONRPCError(
RPC_INVALID_PARAMETER,
"getbalance include_watchonly option is only currently "
"supported if an account is specified");
}
return ValueFromAmount(pwallet->GetBalance());
}
const std::string &account_param = account_value.get_str();
const std::string *account =
account_param != "*" ? &account_param : nullptr;
int nMinDepth = 1;
if (!minconf.isNull()) {
nMinDepth = minconf.get_int();
}
isminefilter filter = ISMINE_SPENDABLE;
if (!include_watchonly.isNull() && include_watchonly.get_bool()) {
filter = filter | ISMINE_WATCH_ONLY;
}
return ValueFromAmount(
pwallet->GetLegacyBalance(filter, nMinDepth, account));
}
return ValueFromAmount(pwallet->GetBalance());
}
static UniValue getunconfirmedbalance(const Config &config,
const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() > 0) {
throw std::runtime_error(
"getunconfirmedbalance\n"
"Returns the server's total unconfirmed balance\n");
}
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
LOCK2(cs_main, pwallet->cs_wallet);
return ValueFromAmount(pwallet->GetUnconfirmedBalance());
}
static UniValue movecmd(const Config &config, const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (!IsDeprecatedRPCEnabled(gArgs, "accounts")) {
if (request.fHelp) {
throw std::runtime_error(
"move (Deprecated, will be removed in v0.21. To use this "
"command, start bitcoind with -deprecatedrpc=accounts)");
}
throw JSONRPCError(
RPC_METHOD_DEPRECATED,
"move is deprecated and will be removed in v0.21. To use this "
"command, start bitcoind with -deprecatedrpc=accounts.");
}
if (request.fHelp || request.params.size() < 3 ||
request.params.size() > 5) {
throw std::runtime_error(
"move \"fromaccount\" \"toaccount\" amount ( minconf \"comment\" "
")\n"
"\nDEPRECATED. Move a specified amount from one account in your "
"wallet to another.\n"
"\nArguments:\n"
"1. \"fromaccount\" (string, required) The name of the account "
"to move funds from. May be the default account using \"\".\n"
"2. \"toaccount\" (string, required) The name of the account "
"to move funds to. May be the default account using \"\".\n"
"3. amount (numeric) Quantity of " +
CURRENCY_UNIT +
" to move between accounts.\n"
"4. (dummy) (numeric, optional) Ignored. Remains for "
"backward compatibility.\n"
"5. \"comment\" (string, optional) An optional comment, "
"stored in the wallet only.\n"
"\nResult:\n"
"true|false (boolean) true if successful.\n"
"\nExamples:\n"
"\nMove 0.01 " +
CURRENCY_UNIT +
" from the default account to the account named tabby\n" +
HelpExampleCli("move", "\"\" \"tabby\" 0.01") + "\nMove 0.01 " +
CURRENCY_UNIT +
" timotei to akiko with a comment and funds have 6 "
"confirmations\n" +
HelpExampleCli("move",
"\"timotei\" \"akiko\" 0.01 6 \"happy birthday!\"") +
"\nAs a json rpc call\n" +
HelpExampleRpc(
"move",
"\"timotei\", \"akiko\", 0.01, 6, \"happy birthday!\""));
}
LOCK2(cs_main, pwallet->cs_wallet);
std::string strFrom = LabelFromValue(request.params[0]);
std::string strTo = LabelFromValue(request.params[1]);
Amount nAmount = AmountFromValue(request.params[2]);
if (nAmount <= Amount::zero()) {
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send");
}
if (!request.params[3].isNull()) {
// Unused parameter, used to be nMinDepth, keep type-checking it though.
(void)request.params[3].get_int();
}
std::string strComment;
if (!request.params[4].isNull()) {
strComment = request.params[4].get_str();
}
if (!pwallet->AccountMove(strFrom, strTo, nAmount, strComment)) {
throw JSONRPCError(RPC_DATABASE_ERROR, "database error");
}
return true;
}
static UniValue sendfrom(const Config &config, const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() < 3 ||
request.params.size() > 6) {
throw std::runtime_error(
"sendfrom \"fromaccount\" \"toaddress\" amount ( minconf "
"\"comment\" \"comment_to\" )\n"
"\nDEPRECATED (use sendtoaddress). Sent an amount from an account "
"to a bitcoin address." +
HelpRequiringPassphrase(pwallet) +
"\n"
"\nArguments:\n"
"1. \"fromaccount\" (string, required) The name of the "
"account to send funds from. May be the default account using "
"\"\".\n"
" Specifying an account does not influence "
"coin selection, but it does associate the newly created\n"
" transaction with the account, so the "
"account's balance computation and transaction history can "
"reflect\n"
" the spend.\n"
"2. \"toaddress\" (string, required) The bitcoin address "
"to send funds to.\n"
"3. amount (numeric or string, required) The amount "
"in " +
CURRENCY_UNIT +
" (transaction fee is added on top).\n"
"4. minconf (numeric, optional, default=1) Only use "
"funds with at least this many confirmations.\n"
"5. \"comment\" (string, optional) A comment used to "
"store what the transaction is for. \n"
" This is not part of the "
"transaction, just kept in your wallet.\n"
"6. \"comment_to\" (string, optional) An optional comment "
"to store the name of the person or organization \n"
" to which you're sending the "
"transaction. This is not part of the transaction, \n"
" it is just kept in your "
"wallet.\n"
"\nResult:\n"
"\"txid\" (string) The transaction id.\n"
"\nExamples:\n"
"\nSend 0.01 " +
CURRENCY_UNIT +
" from the default account to the address, must have at least 1 "
"confirmation\n" +
HelpExampleCli("sendfrom",
"\"\" \"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.01") +
"\nSend 0.01 from the tabby account to the given address, funds "
"must have at least 6 confirmations\n" +
HelpExampleCli("sendfrom",
"\"tabby\" \"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" "
"0.01 6 \"donation\" \"seans outpost\"") +
"\nAs a json rpc call\n" +
HelpExampleRpc("sendfrom",
"\"tabby\", \"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", "
"0.01, 6, \"donation\", \"seans outpost\""));
}
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
LOCK2(cs_main, pwallet->cs_wallet);
std::string label = LabelFromValue(request.params[0]);
CTxDestination dest =
DecodeDestination(request.params[1].get_str(), config.GetChainParams());
if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Invalid Bitcoin address");
}
Amount nAmount = AmountFromValue(request.params[2]);
if (nAmount <= Amount::zero()) {
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send");
}
int nMinDepth = 1;
if (!request.params[3].isNull()) {
nMinDepth = request.params[3].get_int();
}
mapValue_t mapValue;
if (!request.params[4].isNull() && !request.params[4].get_str().empty()) {
mapValue["comment"] = request.params[4].get_str();
}
if (!request.params[5].isNull() && !request.params[5].get_str().empty()) {
mapValue["to"] = request.params[5].get_str();
}
EnsureWalletIsUnlocked(pwallet);
// Check funds
Amount nBalance =
pwallet->GetLegacyBalance(ISMINE_SPENDABLE, nMinDepth, &label);
if (nAmount > nBalance) {
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS,
"Account has insufficient funds");
}
CTransactionRef tx = SendMoney(pwallet, dest, nAmount, false,
std::move(mapValue), std::move(label));
return tx->GetId().GetHex();
}
static UniValue sendmany(const Config &config, const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
std::string help_text;
if (!IsDeprecatedRPCEnabled(gArgs, "accounts")) {
help_text =
"sendmany \"fromaccount\" {\"address\":amount,...} ( minconf "
"\"comment\" [\"address\",...] )\n"
"\nSend multiple times. Amounts are double-precision floating "
"point numbers.\n"
"Note that the \"fromaccount\" argument has been removed in V0.20. "
"To use this RPC with a \"fromaccount\" argument, restart\n"
"bitcoind with -deprecatedrpc=accounts\n" +
HelpRequiringPassphrase(pwallet) +
"\n"
"\nArguments:\n"
"1. \"dummy\" (string, required) Must be set to \"\" "
"for backwards compatibility.\n"
"2. \"amounts\" (string, required) A json object with "
"addresses and amounts\n"
" {\n"
" \"address\":amount (numeric or string) The bitcoin "
"address is the key, the numeric amount (can be string) in " +
CURRENCY_UNIT +
" is the value\n"
" ,...\n"
" }\n"
"3. minconf (numeric, optional, default=1) Only "
"use the balance confirmed at least this many times.\n"
"4. \"comment\" (string, optional) A comment\n"
"5. subtractfeefrom (array, optional) A json array with "
"addresses.\n"
" The fee will be equally deducted from "
"the amount of each selected address.\n"
" Those recipients will receive less "
"bitcoins than you enter in their corresponding amount field.\n"
" If no addresses are specified here, "
"the sender pays the fee.\n"
" [\n"
" \"address\" (string) Subtract fee from this "
"address\n"
" ,...\n"
" ]\n"
"\nResult:\n"
"\"txid\" (string) The transaction id for the "
"send. Only 1 transaction is created regardless of \n"
" the number of addresses.\n"
"\nExamples:\n"
"\nSend two amounts to two different addresses:\n" +
HelpExampleCli("sendmany",
"\"\" "
"\"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,"
"\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}"
"\"") +
"\nSend two amounts to two different addresses setting the "
"confirmation and comment:\n" +
HelpExampleCli("sendmany",
"\"\" "
"\"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,"
"\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" "
"6 \"testing\"") +
"\nSend two amounts to two different addresses, subtract fee from "
"amount:\n" +
HelpExampleCli("sendmany",
"\"\" "
"\"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,"
"\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" "
"1 \"\" "
"\"[\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\","
"\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\"]\"") +
"\nAs a json rpc call\n" +
HelpExampleRpc("sendmany",
"\"\", "
"\"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,"
"\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\","
" 6, \"testing\"");
} else {
help_text =
"sendmany \"\" \"fromaccount\" {\"address\":amount,...} ( "
"minconf \"comment\" [\"address\",...])\n"
"\nSend multiple times. Amounts are double-precision floating "
"point numbers." +
HelpRequiringPassphrase(pwallet) +
"\n"
"\nArguments:\n"
"1. \"fromaccount\" (string, required) DEPRECATED. The "
"account to send the funds from. Should be \"\" for the default "
"account\n"
"2. \"amounts\" (string, required) A json object with "
"addresses and amounts\n"
" {\n"
" \"address\":amount (numeric or string) The bitcoin "
"address is the key, the numeric amount (can be string) in " +
CURRENCY_UNIT +
" is the value\n"
" ,...\n"
" }\n"
"3. minconf (numeric, optional, default=1) Only "
"use the balance confirmed at least this many times.\n"
"4. \"comment\" (string, optional) A comment\n"
"5. subtractfeefrom (array, optional) A json array with "
"addresses.\n"
" The fee will be equally deducted from "
"the amount of each selected address.\n"
" Those recipients will receive less "
"bitcoins than you enter in their corresponding amount field.\n"
" If no addresses are specified here, "
"the sender pays the fee.\n"
" [\n"
" \"address\" (string) Subtract fee from this "
"address\n"
" ,...\n"
" ]\n"
"\nResult:\n"
"\"txid\" (string) The transaction id for the "
"send. Only 1 transaction is created regardless of \n"
" the number of addresses.\n"
"\nExamples:\n"
"\nSend two amounts to two different addresses:\n" +
HelpExampleCli("sendmany",
"\"\" "
"\"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,"
"\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}"
"\"") +
"\nSend two amounts to two different addresses setting the "
"confirmation and comment:\n" +
HelpExampleCli("sendmany",
"\"\" "
"\"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,"
"\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" "
"6 \"testing\"") +
"\nSend two amounts to two different addresses, subtract fee from "
"amount:\n" +
HelpExampleCli("sendmany",
"\"\" "
"\"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,"
"\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" "
"1 \"\" "
"\"[\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\","
"\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\"]\"") +
"\nAs a json rpc call\n" +
HelpExampleRpc("sendmany",
"\"\", "
"\"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,"
"\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\","
" 6, \"testing\"");
}
if (request.fHelp || request.params.size() < 2 ||
request.params.size() > 5) {
throw std::runtime_error(help_text);
}
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
LOCK2(cs_main, pwallet->cs_wallet);
if (pwallet->GetBroadcastTransactions() && !g_connman) {
throw JSONRPCError(
RPC_CLIENT_P2P_DISABLED,
"Error: Peer-to-peer functionality missing or disabled");
}
if (!IsDeprecatedRPCEnabled(gArgs, "accounts") &&
!request.params[0].get_str().empty()) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Dummy value must be set to \"\"");
}
std::string strAccount = LabelFromValue(request.params[0]);
UniValue sendTo = request.params[1].get_obj();
int nMinDepth = 1;
if (!request.params[2].isNull()) {
nMinDepth = request.params[2].get_int();
}
mapValue_t mapValue;
if (!request.params[3].isNull() && !request.params[3].get_str().empty()) {
mapValue["comment"] = request.params[3].get_str();
}
UniValue subtractFeeFromAmount(UniValue::VARR);
if (!request.params[4].isNull()) {
subtractFeeFromAmount = request.params[4].get_array();
}
std::set<CTxDestination> destinations;
std::vector<CRecipient> vecSend;
Amount totalAmount = Amount::zero();
std::vector<std::string> keys = sendTo.getKeys();
for (const std::string &name_ : keys) {
CTxDestination dest = DecodeDestination(name_, config.GetChainParams());
if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
std::string("Invalid Bitcoin address: ") +
name_);
}
if (destinations.count(dest)) {
throw JSONRPCError(
RPC_INVALID_PARAMETER,
std::string("Invalid parameter, duplicated address: ") + name_);
}
destinations.insert(dest);
CScript scriptPubKey = GetScriptForDestination(dest);
Amount nAmount = AmountFromValue(sendTo[name_]);
if (nAmount <= Amount::zero()) {
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send");
}
totalAmount += nAmount;
bool fSubtractFeeFromAmount = false;
for (size_t idx = 0; idx < subtractFeeFromAmount.size(); idx++) {
const UniValue &addr = subtractFeeFromAmount[idx];
if (addr.get_str() == name_) {
fSubtractFeeFromAmount = true;
}
}
CRecipient recipient = {scriptPubKey, nAmount, fSubtractFeeFromAmount};
vecSend.push_back(recipient);
}
EnsureWalletIsUnlocked(pwallet);
// Check funds
Amount nBalance =
pwallet->GetLegacyBalance(ISMINE_SPENDABLE, nMinDepth, &strAccount);
if (totalAmount > nBalance) {
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS,
"Account has insufficient funds");
}
// Shuffle recipient list
std::shuffle(vecSend.begin(), vecSend.end(), FastRandomContext());
// Send
CReserveKey keyChange(pwallet);
Amount nFeeRequired = Amount::zero();
int nChangePosRet = -1;
std::string strFailReason;
CTransactionRef tx;
CCoinControl coinControl;
bool fCreated =
pwallet->CreateTransaction(vecSend, tx, keyChange, nFeeRequired,
nChangePosRet, strFailReason, coinControl);
if (!fCreated) {
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason);
}
CValidationState state;
if (!pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */,
std::move(strAccount), keyChange,
g_connman.get(), state)) {
strFailReason = strprintf("Transaction commit failed:: %s",
FormatStateMessage(state));
throw JSONRPCError(RPC_WALLET_ERROR, strFailReason);
}
return tx->GetId().GetHex();
}
static UniValue addmultisigaddress(const Config &config,
const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() < 2 ||
request.params.size() > 3) {
std::string msg =
"addmultisigaddress nrequired [\"key\",...] ( \"label\" )\n"
"\nAdd a 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"
"If 'label' is specified (DEPRECATED), assign address to that "
"label.\n"
"\nArguments:\n"
"1. nrequired (numeric, required) The number of required "
"signatures out of the n keys or addresses.\n"
"2. \"keys\" (string, required) A json array of bitcoin "
"addresses or hex-encoded public keys\n"
" [\n"
" \"address\" (string) bitcoin address or hex-encoded "
"public key\n"
" ...,\n"
" ]\n"
"3. \"label\" (string, optional) A label to "
"assign the addresses to.\n"
"\nResult:\n"
"{\n"
" \"address\":\"multisigaddress\", (string) The value of the "
"new multisig address.\n"
" \"redeemScript\":\"script\" (string) The string value "
"of the hex-encoded redemption script.\n"
"}\n"
"\nExamples:\n"
"\nAdd a multisig address from 2 addresses\n" +
HelpExampleCli("addmultisigaddress",
"2 "
"\"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\","
"\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"") +
"\nAs json rpc call\n" +
HelpExampleRpc("addmultisigaddress",
"2, "
"\"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\","
"\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"");
throw std::runtime_error(msg);
}
LOCK2(cs_main, pwallet->cs_wallet);
std::string label;
if (!request.params[2].isNull()) {
label = LabelFromValue(request.params[2]);
}
int required = request.params[0].get_int();
// Get the public keys
const UniValue &keys_or_addrs = request.params[1].get_array();
std::vector<CPubKey> pubkeys;
for (size_t 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(config.GetChainParams(), pwallet,
keys_or_addrs[i].get_str()));
}
}
OutputType output_type = pwallet->m_default_address_type;
// Construct using pay-to-script-hash:
CScript inner = CreateMultisigRedeemscript(required, pubkeys);
- pwallet->AddCScript(inner);
-
CTxDestination dest =
- pwallet->AddAndGetDestinationForScript(inner, output_type);
-
+ AddAndGetDestinationForScript(*pwallet, inner, output_type);
pwallet->SetAddressBook(dest, label, "send");
UniValue result(UniValue::VOBJ);
result.pushKV("address", EncodeDestination(dest, config));
result.pushKV("redeemScript", HexStr(inner.begin(), inner.end()));
return result;
}
struct tallyitem {
Amount nAmount{Amount::zero()};
int nConf{std::numeric_limits<int>::max()};
std::vector<uint256> txids;
bool fIsWatchonly{false};
tallyitem() {}
};
static UniValue ListReceived(const Config &config, CWallet *const pwallet,
const UniValue ¶ms, bool by_label)
EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
// Minimum confirmations
int nMinDepth = 1;
if (!params[0].isNull()) {
nMinDepth = params[0].get_int();
}
// Whether to include empty labels
bool fIncludeEmpty = false;
if (!params[1].isNull()) {
fIncludeEmpty = params[1].get_bool();
}
isminefilter filter = ISMINE_SPENDABLE;
if (!params[2].isNull() && params[2].get_bool()) {
filter = filter | ISMINE_WATCH_ONLY;
}
bool has_filtered_address = false;
CTxDestination filtered_address = CNoDestination();
if (!by_label && params.size() > 3) {
if (!IsValidDestinationString(params[3].get_str(),
config.GetChainParams())) {
throw JSONRPCError(RPC_WALLET_ERROR,
"address_filter parameter was invalid");
}
filtered_address =
DecodeDestination(params[3].get_str(), config.GetChainParams());
has_filtered_address = true;
}
// Tally
std::map<CTxDestination, tallyitem> mapTally;
for (const std::pair<const TxId, CWalletTx> &pairWtx : pwallet->mapWallet) {
const CWalletTx &wtx = pairWtx.second;
CValidationState state;
if (wtx.IsCoinBase() ||
!ContextualCheckTransactionForCurrentBlock(
config.GetChainParams().GetConsensus(), *wtx.tx, state)) {
continue;
}
int nDepth = wtx.GetDepthInMainChain();
if (nDepth < nMinDepth) {
continue;
}
for (const CTxOut &txout : wtx.tx->vout) {
CTxDestination address;
if (!ExtractDestination(txout.scriptPubKey, address)) {
continue;
}
if (has_filtered_address && !(filtered_address == address)) {
continue;
}
isminefilter mine = IsMine(*pwallet, address);
if (!(mine & filter)) {
continue;
}
tallyitem &item = mapTally[address];
item.nAmount += txout.nValue;
item.nConf = std::min(item.nConf, nDepth);
item.txids.push_back(wtx.GetId());
if (mine & ISMINE_WATCH_ONLY) {
item.fIsWatchonly = true;
}
}
}
// Reply
UniValue ret(UniValue::VARR);
std::map<std::string, tallyitem> label_tally;
// Create mapAddressBook iterator
// If we aren't filtering, go from begin() to end()
auto start = pwallet->mapAddressBook.begin();
auto end = pwallet->mapAddressBook.end();
// If we are filtering, find() the applicable entry
if (has_filtered_address) {
start = pwallet->mapAddressBook.find(filtered_address);
if (start != end) {
end = std::next(start);
}
}
for (auto item_it = start; item_it != end; ++item_it) {
const CTxDestination &address = item_it->first;
const std::string &label = item_it->second.name;
std::map<CTxDestination, tallyitem>::iterator it =
mapTally.find(address);
if (it == mapTally.end() && !fIncludeEmpty) {
continue;
}
Amount nAmount = Amount::zero();
int nConf = std::numeric_limits<int>::max();
bool fIsWatchonly = false;
if (it != mapTally.end()) {
nAmount = (*it).second.nAmount;
nConf = (*it).second.nConf;
fIsWatchonly = (*it).second.fIsWatchonly;
}
if (by_label) {
tallyitem &_item = label_tally[label];
_item.nAmount += nAmount;
_item.nConf = std::min(_item.nConf, nConf);
_item.fIsWatchonly = fIsWatchonly;
} else {
UniValue obj(UniValue::VOBJ);
if (fIsWatchonly) {
obj.pushKV("involvesWatchonly", true);
}
obj.pushKV("address", EncodeDestination(address, config));
obj.pushKV("account", label);
obj.pushKV("amount", ValueFromAmount(nAmount));
obj.pushKV("confirmations",
(nConf == std::numeric_limits<int>::max() ? 0 : nConf));
obj.pushKV("label", label);
UniValue transactions(UniValue::VARR);
if (it != mapTally.end()) {
for (const uint256 &_item : (*it).second.txids) {
transactions.push_back(_item.GetHex());
}
}
obj.pushKV("txids", transactions);
ret.push_back(obj);
}
}
if (by_label) {
for (const auto &entry : label_tally) {
Amount nAmount = entry.second.nAmount;
int nConf = entry.second.nConf;
UniValue obj(UniValue::VOBJ);
if (entry.second.fIsWatchonly) {
obj.pushKV("involvesWatchonly", true);
}
obj.pushKV("account", entry.first);
obj.pushKV("amount", ValueFromAmount(nAmount));
obj.pushKV("confirmations",
(nConf == std::numeric_limits<int>::max() ? 0 : nConf));
obj.pushKV("label", entry.first);
ret.push_back(obj);
}
}
return ret;
}
static UniValue listreceivedbyaddress(const Config &config,
const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() > 4) {
throw std::runtime_error(
"listreceivedbyaddress ( minconf include_empty include_watchonly "
"address_filter )\n"
"\nList balances by receiving address.\n"
"\nArguments:\n"
"1. minconf (numeric, optional, default=1) The minimum "
"number of confirmations before payments are included.\n"
"2. include_empty (bool, optional, default=false) Whether to "
"include addresses that haven't received any payments.\n"
"3. include_watchonly (bool, optional, default=false) Whether to "
"include watch-only addresses (see 'importaddress').\n"
"4. address_filter (string, optional) If present, only return "
"information on this address.\n"
"\nResult:\n"
"[\n"
" {\n"
" \"involvesWatchonly\" : true, (bool) Only returned if "
"imported addresses were involved in transaction\n"
" \"address\" : \"receivingaddress\", (string) The receiving "
"address\n"
" \"account\" : \"accountname\", (string) DEPRECATED. "
"Backwards compatible alias for label.\n \"\".\n"
" \"amount\" : x.xxx, (numeric) The total "
"amount in " +
CURRENCY_UNIT +
" received by the address\n"
" \"confirmations\" : n, (numeric) The number of "
"confirmations of the most recent transaction included\n"
" \"label\" : \"label\", (string) The label of "
"the receiving address. The default label is \"\".\n"
" \"txids\": [\n"
" \"txid\", (string) The ids of "
"transactions received with the address \n"
" ...\n"
" ]\n"
" }\n"
" ,...\n"
"]\n"
"\nExamples:\n" +
HelpExampleCli("listreceivedbyaddress", "") +
HelpExampleCli("listreceivedbyaddress", "6 true") +
HelpExampleRpc("listreceivedbyaddress", "6, true, true") +
HelpExampleRpc(
"listreceivedbyaddress",
"6, true, true, \"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\""));
}
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
LOCK2(cs_main, pwallet->cs_wallet);
return ListReceived(config, pwallet, request.params, false);
}
static UniValue listreceivedbylabel(const Config &config,
const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (!IsDeprecatedRPCEnabled(gArgs, "accounts") &&
request.strMethod == "listreceivedbyaccount") {
if (request.fHelp) {
throw std::runtime_error(
"listreceivedbyaccount (Deprecated, will be removed in v0.21. "
"To use this command, start bitcoind with "
"-deprecatedrpc=accounts)");
}
throw JSONRPCError(RPC_METHOD_DEPRECATED,
"listreceivedbyaccount is deprecated and will be "
"removed in v0.21. To use this command, start "
"bitcoind with -deprecatedrpc=accounts.");
}
if (request.fHelp || request.params.size() > 3) {
throw std::runtime_error(
"listreceivedbylabel ( minconf include_empty include_watchonly)\n"
"\nList received transactions by label.\n"
"\nArguments:\n"
"1. minconf (numeric, optional, default=1) The minimum "
"number of confirmations before payments are included.\n"
"2. include_empty (bool, optional, default=false) Whether to "
"include labels that haven't received any payments.\n"
"3. include_watchonly (bool, optional, default=false) Whether to "
"include watch-only addresses (see 'importaddress').\n"
"\nResult:\n"
"[\n"
" {\n"
" \"involvesWatchonly\" : true, (bool) Only returned if "
"imported addresses were involved in transaction\n"
" \"account\" : \"accountname\", (string) DEPRECATED. "
"Backwards compatible alias for label.\n"
" \"amount\" : x.xxx, (numeric) The total amount "
"received by addresses with this label\n"
" \"confirmations\" : n, (numeric) The number of "
"confirmations of the most recent transaction included\n"
" \"label\" : \"label\" (string) The label of the "
"receiving address. The default label is \"\".\n"
" }\n"
" ,...\n"
"]\n"
"\nExamples:\n" +
HelpExampleCli("listreceivedbylabel", "") +
HelpExampleCli("listreceivedbylabel", "6 true") +
HelpExampleRpc("listreceivedbylabel", "6, true, true"));
}
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
LOCK2(cs_main, pwallet->cs_wallet);
return ListReceived(config, pwallet, request.params, true);
}
static void MaybePushAddress(UniValue &entry, const CTxDestination &dest) {
if (IsValidDestination(dest)) {
entry.pushKV("address", EncodeDestination(dest, GetConfig()));
}
}
/**
* List transactions based on the given criteria.
*
* @param pwallet The wallet.
* @param wtx The wallet transaction.
* @param strAccount The account, if any, or "*" for all.
* @param nMinDepth The minimum confirmation depth.
* @param fLong Whether to include the JSON version of the transaction.
* @param ret The UniValue into which the result is stored.
* @param filter The "is mine" filter bool.
*/
static void ListTransactions(CWallet *const pwallet, const CWalletTx &wtx,
const std::string &strAccount, int nMinDepth,
bool fLong, UniValue &ret,
const isminefilter &filter)
EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
Amount nFee;
std::string strSentAccount;
std::list<COutputEntry> listReceived;
std::list<COutputEntry> listSent;
wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount, filter);
bool fAllAccounts = (strAccount == std::string("*"));
bool involvesWatchonly = wtx.IsFromMe(ISMINE_WATCH_ONLY);
// Sent
if ((!listSent.empty() || nFee != Amount::zero()) &&
(fAllAccounts || strAccount == strSentAccount)) {
for (const COutputEntry &s : listSent) {
UniValue entry(UniValue::VOBJ);
if (involvesWatchonly ||
(::IsMine(*pwallet, s.destination) & ISMINE_WATCH_ONLY)) {
entry.pushKV("involvesWatchonly", true);
}
if (IsDeprecatedRPCEnabled(gArgs, "accounts")) {
entry.pushKV("account", strSentAccount);
}
MaybePushAddress(entry, s.destination);
entry.pushKV("category", "send");
entry.pushKV("amount", ValueFromAmount(-s.amount));
if (pwallet->mapAddressBook.count(s.destination)) {
entry.pushKV("label",
pwallet->mapAddressBook[s.destination].name);
}
entry.pushKV("vout", s.vout);
entry.pushKV("fee", ValueFromAmount(-1 * nFee));
if (fLong) {
WalletTxToJSON(wtx, entry);
}
entry.pushKV("abandoned", wtx.isAbandoned());
ret.push_back(entry);
}
}
// Received
if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth) {
for (const COutputEntry &r : listReceived) {
std::string account;
if (pwallet->mapAddressBook.count(r.destination)) {
account = pwallet->mapAddressBook[r.destination].name;
}
if (fAllAccounts || (account == strAccount)) {
UniValue entry(UniValue::VOBJ);
if (involvesWatchonly ||
(::IsMine(*pwallet, r.destination) & ISMINE_WATCH_ONLY)) {
entry.pushKV("involvesWatchonly", true);
}
if (IsDeprecatedRPCEnabled(gArgs, "accounts")) {
entry.pushKV("account", account);
}
MaybePushAddress(entry, r.destination);
if (wtx.IsCoinBase()) {
if (wtx.GetDepthInMainChain() < 1) {
entry.pushKV("category", "orphan");
} else if (wtx.IsImmatureCoinBase()) {
entry.pushKV("category", "immature");
} else {
entry.pushKV("category", "generate");
}
} else {
entry.pushKV("category", "receive");
}
entry.pushKV("amount", ValueFromAmount(r.amount));
if (pwallet->mapAddressBook.count(r.destination)) {
entry.pushKV("label", account);
}
entry.pushKV("vout", r.vout);
if (fLong) {
WalletTxToJSON(wtx, entry);
}
ret.push_back(entry);
}
}
}
}
static void AcentryToJSON(const CAccountingEntry &acentry,
const std::string &strAccount, UniValue &ret) {
bool fAllAccounts = (strAccount == std::string("*"));
if (fAllAccounts || acentry.strAccount == strAccount) {
UniValue entry(UniValue::VOBJ);
entry.pushKV("account", acentry.strAccount);
entry.pushKV("category", "move");
entry.pushKV("time", acentry.nTime);
entry.pushKV("amount", ValueFromAmount(acentry.nCreditDebit));
if (IsDeprecatedRPCEnabled(gArgs, "accounts")) {
entry.pushKV("otheraccount", acentry.strOtherAccount);
}
entry.pushKV("comment", acentry.strComment);
ret.push_back(entry);
}
}
UniValue listtransactions(const Config &config, const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
std::string help_text{};
if (!IsDeprecatedRPCEnabled(gArgs, "accounts")) {
help_text =
"listtransactions (dummy count skip include_watchonly)\n"
"\nReturns up to 'count' most recent transactions skipping the "
"first 'from' transactions for account 'account'.\n"
"Note that the \"account\" argument and \"otheraccount\" return "
"value have been removed in V0.20. To use this RPC with an "
"\"account\" argument, restart\n"
"bitcoind with -deprecatedrpc=accounts\n"
"\nArguments:\n"
"1. \"dummy\" (string, optional) If set, should be \"*\" for "
"backwards compatibility.\n"
"2. count (numeric, optional, default=10) The number of "
"transactions to return\n"
"3. skip (numeric, optional, default=0) The number of "
"transactions to skip\n"
"4. include_watchonly (bool, optional, default=false) Include "
"transactions to watch-only addresses (see 'importaddress')\n"
"\nResult:\n"
"[\n"
" {\n"
" \"address\":\"address\", (string) The bitcoin address of "
"the transaction.\n"
" \"category\":\"send|receive\", (string) The transaction "
"category.\n"
" \"amount\": x.xxx, (numeric) The amount in " +
CURRENCY_UNIT +
". This is negative for the 'send' category, and is positive\n"
" for the 'receive' "
"category,\n"
" \"label\": \"label\", (string) A comment for the "
"address/transaction, if any\n"
" \"vout\": n, (numeric) the vout value\n"
" \"fee\": x.xxx, (numeric) The amount of the fee "
"in " +
CURRENCY_UNIT +
". This is negative and only available for the \n"
" 'send' category of "
"transactions.\n"
" \"confirmations\": n, (numeric) The number of "
"confirmations for the transaction. Negative confirmations "
"indicate the\n"
" transaction conflicts "
"with the block chain\n"
" \"trusted\": xxx, (bool) Whether we consider the "
"outputs of this unconfirmed transaction safe to spend.\n"
" \"blockhash\": \"hashvalue\", (string) The block hash "
"containing the transaction.\n"
" \"blockindex\": n, (numeric) The index of the "
"transaction in the block that includes it.\n"
" \"blocktime\": xxx, (numeric) The block time in "
"seconds since epoch (1 Jan 1970 GMT).\n"
" \"txid\": \"transactionid\", (string) The transaction id.\n"
" \"time\": xxx, (numeric) The transaction time in "
"seconds since epoch (midnight Jan 1 1970 GMT).\n"
" \"timereceived\": xxx, (numeric) The time received in "
"seconds since epoch (midnight Jan 1 1970 GMT).\n"
" \"comment\": \"...\", (string) If a comment is "
"associated with the transaction.\n"
" \"abandoned\": xxx (bool) 'true' if the transaction "
"has been abandoned (inputs are respendable). Only available for "
"the \n"
" 'send' category of "
"transactions.\n"
" }\n"
"]\n"
"\nExamples:\n"
"\nList the most recent 10 transactions in the systems\n" +
HelpExampleCli("listtransactions", "") +
"\nList transactions 100 to 120\n" +
HelpExampleCli("listtransactions", "\"*\" 20 100") +
"\nAs a json rpc call\n" +
HelpExampleRpc("listtransactions", "\"*\", 20, 100");
} else {
help_text =
"listtransactions ( \"account\" count skip include_watchonly)\n"
"\nReturns up to 'count' most recent transactions skipping the "
"first 'from' transactions for account 'account'.\n"
"\nArguments:\n"
"1. \"account\" (string, optional) DEPRECATED. This argument "
"will be removed in V0.21. The account name. Should be \"*\".\n"
"2. count (numeric, optional, default=10) The number of "
"transactions to return\n"
"3. skip (numeric, optional, default=0) The number of "
"transactions to skip\n"
"4. include_watchonly (bool, optional, default=false) Include "
"transactions to watch-only addresses (see 'importaddress')\n"
"\nResult:\n"
"[\n"
" {\n"
" \"account\":\"accountname\", (string) DEPRECATED. This "
"field will be removed in V0.21. The account name associated with "
"the transaction. \n"
" It will be \"\" "
"for the default account.\n"
" \"address\":\"address\", (string) The bitcoin address of "
"the transaction. Not present for \n"
" move transactions "
"(category = move).\n"
" \"category\":\"send|receive|move\", (string) The transaction "
"category. 'move' is a local (off blockchain)\n"
" transaction "
"between accounts, and not associated with an address,\n"
" transaction id or "
"block. 'send' and 'receive' transactions are \n"
" associated with "
"an address, transaction id and block details\n"
" \"amount\": x.xxx, (numeric) The amount in " +
CURRENCY_UNIT +
". This is negative for the 'send' category, and for the\n"
" 'move' category for "
"moves outbound. It is positive for the 'receive' category,\n"
" and for the 'move' "
"category for inbound funds.\n"
" \"label\": \"label\", (string) A comment for the "
"address/transaction, if any\n"
" \"vout\": n, (numeric) the vout value\n"
" \"fee\": x.xxx, (numeric) The amount of the fee "
"in " +
CURRENCY_UNIT +
". This is negative and only available for the \n"
" 'send' category of "
"transactions.\n"
" \"confirmations\": n, (numeric) The number of "
"confirmations for the transaction. Available for 'send' and \n"
" 'receive' category of "
"transactions. Negative confirmations indicate the\n"
" transaction conflicts "
"with the block chain\n"
" \"trusted\": xxx, (bool) Whether we consider the "
"outputs of this unconfirmed transaction safe to spend.\n"
" \"blockhash\": \"hashvalue\", (string) The block hash "
"containing the transaction. Available for 'send' and 'receive'\n"
" category of "
"transactions.\n"
" \"blockindex\": n, (numeric) The index of the "
"transaction in the block that includes it. Available for 'send' "
"and 'receive'\n"
" category of "
"transactions.\n"
" \"blocktime\": xxx, (numeric) The block time in "
"seconds since epoch (1 Jan 1970 GMT).\n"
" \"txid\": \"transactionid\", (string) The transaction id. "
"Available for 'send' and 'receive' category of transactions.\n"
" \"time\": xxx, (numeric) The transaction time in "
"seconds since epoch (midnight Jan 1 1970 GMT).\n"
" \"timereceived\": xxx, (numeric) The time received in "
"seconds since epoch (midnight Jan 1 1970 GMT). Available \n"
" for 'send' and "
"'receive' category of transactions.\n"
" \"comment\": \"...\", (string) If a comment is "
"associated with the transaction.\n"
" \"otheraccount\": \"accountname\", (string) DEPRECATED. This "
"field will be removed in V0.21. For the 'move' category of "
"transactions, the account the funds came \n"
" from (for receiving "
"funds, positive amounts), or went to (for sending funds,\n"
" negative amounts).\n"
" \"abandoned\": xxx (bool) 'true' if the transaction "
"has been abandoned (inputs are respendable). Only available for "
"the \n"
" 'send' category of "
"transactions.\n"
" }\n"
"]\n"
"\nExamples:\n"
"\nList the most recent 10 transactions in the systems\n" +
HelpExampleCli("listtransactions", "") +
"\nList transactions 100 to 120\n" +
HelpExampleCli("listtransactions", "\"*\" 20 100") +
"\nAs a json rpc call\n" +
HelpExampleRpc("listtransactions", "\"*\", 20, 100");
}
if (request.fHelp || request.params.size() > 4) {
throw std::runtime_error(help_text);
}
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
LOCK2(cs_main, pwallet->cs_wallet);
std::string strAccount = "*";
if (!request.params[0].isNull()) {
strAccount = request.params[0].get_str();
if (!IsDeprecatedRPCEnabled(gArgs, "accounts") && strAccount != "*") {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Dummy value must be set to \"*\"");
}
}
int nCount = 10;
if (!request.params[1].isNull()) {
nCount = request.params[1].get_int();
}
int nFrom = 0;
if (!request.params[2].isNull()) {
nFrom = request.params[2].get_int();
}
isminefilter filter = ISMINE_SPENDABLE;
if (!request.params[3].isNull() && request.params[3].get_bool()) {
filter = filter | ISMINE_WATCH_ONLY;
}
if (nCount < 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count");
}
if (nFrom < 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative from");
}
UniValue ret(UniValue::VARR);
const CWallet::TxItems &txOrdered = pwallet->wtxOrdered;
// iterate backwards until we have nCount items to return:
for (CWallet::TxItems::const_reverse_iterator it = txOrdered.rbegin();
it != txOrdered.rend(); ++it) {
CWalletTx *const pwtx = (*it).second.first;
if (pwtx != nullptr) {
ListTransactions(pwallet, *pwtx, strAccount, 0, true, ret, filter);
}
if (IsDeprecatedRPCEnabled(gArgs, "accounts")) {
CAccountingEntry *const pacentry = (*it).second.second;
if (pacentry != nullptr) {
AcentryToJSON(*pacentry, strAccount, ret);
}
}
if ((int)ret.size() >= (nCount + nFrom)) {
break;
}
}
// ret is newest to oldest
if (nFrom > (int)ret.size()) {
nFrom = ret.size();
}
if ((nFrom + nCount) > (int)ret.size()) {
nCount = ret.size() - nFrom;
}
std::vector<UniValue> arrTmp = ret.getValues();
std::vector<UniValue>::iterator first = arrTmp.begin();
std::advance(first, nFrom);
std::vector<UniValue>::iterator last = arrTmp.begin();
std::advance(last, nFrom + nCount);
if (last != arrTmp.end()) {
arrTmp.erase(last, arrTmp.end());
}
if (first != arrTmp.begin()) {
arrTmp.erase(arrTmp.begin(), first);
}
// Return oldest to newest
std::reverse(arrTmp.begin(), arrTmp.end());
ret.clear();
ret.setArray();
ret.push_backV(arrTmp);
return ret;
}
static UniValue listaccounts(const Config &config,
const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (!IsDeprecatedRPCEnabled(gArgs, "accounts")) {
if (request.fHelp) {
throw std::runtime_error(
"listaccounts (Deprecated, will be removed in v0.21. To use "
"this command, start bitcoind with -deprecatedrpc=accounts)");
}
throw JSONRPCError(
RPC_METHOD_DEPRECATED,
"listaccounts is deprecated and will be removed in v0.21. To use "
"this command, start bitcoind with -deprecatedrpc=accounts.");
}
if (request.fHelp || request.params.size() > 2) {
throw std::runtime_error(
"listaccounts ( minconf include_watchonly)\n"
"\nDEPRECATED. Returns Object that has account names as keys, "
"account balances as values.\n"
"\nArguments:\n"
"1. minconf (numeric, optional, default=1) Only "
"include transactions with at least this many confirmations\n"
"2. include_watchonly (bool, optional, default=false) Include "
"balances in watch-only addresses (see 'importaddress')\n"
"\nResult:\n"
"{ (json object where keys are account names, "
"and values are numeric balances\n"
" \"account\": x.xxx, (numeric) The property name is the account "
"name, and the value is the total balance for the account.\n"
" ...\n"
"}\n"
"\nExamples:\n"
"\nList account balances where there at least 1 confirmation\n" +
HelpExampleCli("listaccounts", "") +
"\nList account balances including zero confirmation "
"transactions\n" +
HelpExampleCli("listaccounts", "0") +
"\nList account balances for 6 or more confirmations\n" +
HelpExampleCli("listaccounts", "6") + "\nAs json rpc call\n" +
HelpExampleRpc("listaccounts", "6"));
}
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
LOCK2(cs_main, pwallet->cs_wallet);
int nMinDepth = 1;
if (!request.params[0].isNull()) {
nMinDepth = request.params[0].get_int();
}
isminefilter includeWatchonly = ISMINE_SPENDABLE;
if (!request.params[1].isNull() && request.params[1].get_bool()) {
includeWatchonly = includeWatchonly | ISMINE_WATCH_ONLY;
}
std::map<std::string, Amount> mapAccountBalances;
for (const std::pair<const CTxDestination, CAddressBookData> &entry :
pwallet->mapAddressBook) {
// This address belongs to me
if (IsMine(*pwallet, entry.first) & includeWatchonly) {
mapAccountBalances[entry.second.name] = Amount::zero();
}
}
for (const std::pair<const TxId, CWalletTx> &pairWtx : pwallet->mapWallet) {
const CWalletTx &wtx = pairWtx.second;
Amount nFee;
std::string strSentAccount;
std::list<COutputEntry> listReceived;
std::list<COutputEntry> listSent;
int nDepth = wtx.GetDepthInMainChain();
if (wtx.IsImmatureCoinBase() || nDepth < 0) {
continue;
}
wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount,
includeWatchonly);
mapAccountBalances[strSentAccount] -= nFee;
for (const COutputEntry &s : listSent) {
mapAccountBalances[strSentAccount] -= s.amount;
}
if (nDepth >= nMinDepth) {
for (const COutputEntry &r : listReceived) {
if (pwallet->mapAddressBook.count(r.destination)) {
mapAccountBalances[pwallet->mapAddressBook[r.destination]
.name] += r.amount;
} else {
mapAccountBalances[""] += r.amount;
}
}
}
}
const std::list<CAccountingEntry> &acentries = pwallet->laccentries;
for (const CAccountingEntry &entry : acentries) {
mapAccountBalances[entry.strAccount] += entry.nCreditDebit;
}
UniValue ret(UniValue::VOBJ);
for (const std::pair<const std::string, Amount> &accountBalance :
mapAccountBalances) {
ret.pushKV(accountBalance.first,
ValueFromAmount(accountBalance.second));
}
return ret;
}
static UniValue listsinceblock(const Config &config,
const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() > 4) {
throw std::runtime_error(
"listsinceblock ( \"blockhash\" target_confirmations "
"include_watchonly include_removed )\n"
"\nGet all transactions in blocks since block [blockhash], or all "
"transactions if omitted.\n"
"If \"blockhash\" is no longer a part of the main chain, "
"transactions from the fork point onward are included.\n"
"Additionally, if include_removed is set, transactions affecting "
"the wallet which were removed are returned in the \"removed\" "
"array.\n"
"\nArguments:\n"
"1. \"blockhash\" (string, optional) The block hash to "
"list transactions since\n"
"2. target_confirmations: (numeric, optional, default=1) Return "
"the nth block hash from the main chain. e.g. 1 would mean the "
"best block hash. Note: this is not used as a filter, but only "
"affects [lastblock] in the return value\n"
"3. include_watchonly: (bool, optional, default=false) "
"Include transactions to watch-only addresses (see "
"'importaddress')\n"
"4. include_removed: (bool, optional, default=true) Show "
"transactions that were removed due to a reorg in the \"removed\" "
"array\n"
" (not "
"guaranteed to work on pruned nodes)\n"
"\nResult:\n"
"{\n"
" \"transactions\": [\n"
" \"account\":\"accountname\", (string) DEPRECATED. This "
"field will be removed in V0.21. To see this deprecated field, "
"start bitcoind with -deprecatedrpc=accounts. The "
"account name associated with the transaction. Will be \"\" for "
"the default account.\n"
" \"address\":\"address\", (string) The bitcoin address of "
"the transaction. Not present for move transactions (category = "
"move).\n"
" \"category\":\"send|receive\", (string) The transaction "
"category. 'send' has negative amounts, 'receive' has positive "
"amounts.\n"
" \"amount\": x.xxx, (numeric) The amount in " +
CURRENCY_UNIT +
". This is negative for the 'send' category, and for the 'move' "
"category for moves \n"
" outbound. It is "
"positive for the 'receive' category, and for the 'move' category "
"for inbound funds.\n"
" \"vout\" : n, (numeric) the vout value\n"
" \"fee\": x.xxx, (numeric) The amount of the fee "
"in " +
CURRENCY_UNIT +
". This is negative and only available for the 'send' category of "
"transactions.\n"
" \"confirmations\": n, (numeric) The number of "
"confirmations for the transaction. Available for 'send' and "
"'receive' category of transactions.\n"
" When it's < 0, it means "
"the transaction conflicted that many blocks ago.\n"
" \"blockhash\": \"hashvalue\", (string) The block hash "
"containing the transaction. Available for 'send' and 'receive' "
"category of transactions.\n"
" \"blockindex\": n, (numeric) The index of the "
"transaction in the block that includes it. Available for 'send' "
"and 'receive' category of transactions.\n"
" \"blocktime\": xxx, (numeric) The block time in "
"seconds since epoch (1 Jan 1970 GMT).\n"
" \"txid\": \"transactionid\", (string) The transaction id. "
"Available for 'send' and 'receive' category of transactions.\n"
" \"time\": xxx, (numeric) The transaction time in "
"seconds since epoch (Jan 1 1970 GMT).\n"
" \"timereceived\": xxx, (numeric) The time received in "
"seconds since epoch (Jan 1 1970 GMT). Available for 'send' and "
"'receive' category of transactions.\n"
" \"abandoned\": xxx, (bool) 'true' if the transaction "
"has been abandoned (inputs are respendable). Only available for "
"the 'send' category of transactions.\n"
" \"comment\": \"...\", (string) If a comment is "
"associated with the transaction.\n"
" \"label\" : \"label\" (string) A comment for the "
"address/transaction, if any\n"
" \"to\": \"...\", (string) If a comment to is "
"associated with the transaction.\n"
" ],\n"
" \"removed\": [\n"
" <structure is the same as \"transactions\" above, only "
"present if include_removed=true>\n"
" Note: transactions that were re-added in the active chain "
"will appear as-is in this array, and may thus have a positive "
"confirmation count.\n"
" ],\n"
" \"lastblock\": \"lastblockhash\" (string) The hash of the "
"block (target_confirmations-1) from the best block on the main "
"chain. This is typically used to feed back into listsinceblock "
"the next time you call it. So you would generally use a "
"target_confirmations of say 6, so you will be continually "
"re-notified of transactions until they've reached 6 confirmations "
"plus any new ones\n"
"}\n"
"\nExamples:\n" +
HelpExampleCli("listsinceblock", "") +
HelpExampleCli("listsinceblock", "\"000000000000000bacf66f7497b7dc4"
"5ef753ee9a7d38571037cdb1a57f663ad"
"\" 6") +
HelpExampleRpc("listsinceblock", "\"000000000000000bacf66f7497b7dc4"
"5ef753ee9a7d38571037cdb1a57f663ad"
"\", 6"));
}
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
LOCK2(cs_main, pwallet->cs_wallet);
// Block index of the specified block or the common ancestor, if the block
// provided was in a deactivated chain.
const CBlockIndex *pindex = nullptr;
// Block index of the specified block, even if it's in a deactivated chain.
const CBlockIndex *paltindex = nullptr;
int target_confirms = 1;
isminefilter filter = ISMINE_SPENDABLE;
if (!request.params[0].isNull() && !request.params[0].get_str().empty()) {
uint256 blockId;
blockId.SetHex(request.params[0].get_str());
paltindex = pindex = LookupBlockIndex(blockId);
if (!pindex) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
if (chainActive[pindex->nHeight] != pindex) {
// the block being asked for is a part of a deactivated chain;
// we don't want to depend on its perceived height in the block
// chain, we want to instead use the last common ancestor
pindex = chainActive.FindFork(pindex);
}
}
if (!request.params[1].isNull()) {
target_confirms = request.params[1].get_int();
if (target_confirms < 1) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter");
}
}
if (!request.params[2].isNull() && request.params[2].get_bool()) {
filter = filter | ISMINE_WATCH_ONLY;
}
bool include_removed =
(request.params[3].isNull() || request.params[3].get_bool());
int depth = pindex ? (1 + chainActive.Height() - pindex->nHeight) : -1;
UniValue transactions(UniValue::VARR);
for (const std::pair<const TxId, CWalletTx> &pairWtx : pwallet->mapWallet) {
CWalletTx tx = pairWtx.second;
if (depth == -1 || tx.GetDepthInMainChain() < depth) {
ListTransactions(pwallet, tx, "*", 0, true, transactions, filter);
}
}
const Consensus::Params ¶ms = config.GetChainParams().GetConsensus();
// when a reorg'd block is requested, we also list any relevant transactions
// in the blocks of the chain that was detached
UniValue removed(UniValue::VARR);
while (include_removed && paltindex && paltindex != pindex) {
CBlock block;
if (!ReadBlockFromDisk(block, paltindex, params)) {
throw JSONRPCError(RPC_INTERNAL_ERROR,
"Can't read block from disk");
}
for (const CTransactionRef &tx : block.vtx) {
auto it = pwallet->mapWallet.find(tx->GetId());
if (it != pwallet->mapWallet.end()) {
// We want all transactions regardless of confirmation count to
// appear here, even negative confirmation ones, hence the big
// negative.
ListTransactions(pwallet, it->second, "*", -100000000, true,
removed, filter);
}
}
paltindex = paltindex->pprev;
}
CBlockIndex *pblockLast =
chainActive[chainActive.Height() + 1 - target_confirms];
uint256 lastblock = pblockLast ? pblockLast->GetBlockHash() : uint256();
UniValue ret(UniValue::VOBJ);
ret.pushKV("transactions", transactions);
if (include_removed) {
ret.pushKV("removed", removed);
}
ret.pushKV("lastblock", lastblock.GetHex());
return ret;
}
static UniValue gettransaction(const Config &config,
const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() < 1 ||
request.params.size() > 2) {
throw std::runtime_error(
"gettransaction \"txid\" ( include_watchonly )\n"
"\nGet detailed information about in-wallet transaction <txid>\n"
"\nArguments:\n"
"1. \"txid\" (string, required) The transaction "
"id\n"
"2. \"include_watchonly\" (bool, optional, default=false) "
"Whether to include watch-only addresses in balance calculation "
"and details[]\n"
"\nResult:\n"
"{\n"
" \"amount\" : x.xxx, (numeric) The transaction amount "
"in " +
CURRENCY_UNIT +
"\n"
" \"fee\": x.xxx, (numeric) The amount of the fee in " +
CURRENCY_UNIT +
". This is negative and only available for the \n"
" 'send' category of transactions.\n"
" \"confirmations\" : n, (numeric) The number of "
"confirmations\n"
" \"blockhash\" : \"hash\", (string) The block hash\n"
" \"blockindex\" : xx, (numeric) The index of the "
"transaction in the block that includes it\n"
" \"blocktime\" : ttt, (numeric) The time in seconds since "
"epoch (1 Jan 1970 GMT)\n"
" \"txid\" : \"transactionid\", (string) The transaction id.\n"
" \"time\" : ttt, (numeric) The transaction time in "
"seconds since epoch (1 Jan 1970 GMT)\n"
" \"timereceived\" : ttt, (numeric) The time received in "
"seconds since epoch (1 Jan 1970 GMT)\n"
" \"bip125-replaceable\": \"yes|no|unknown\", (string) Whether "
"this transaction could be replaced due to BIP125 "
"(replace-by-fee);\n"
" may be unknown "
"for unconfirmed transactions not in the mempool\n"
" \"details\" : [\n"
" {\n"
" \"account\" : \"accountname\", (string) DEPRECATED. "
"This field will be removed in a V0.21. To see this deprecated "
"field, start bitcoind with -deprecatedrpc=accounts. The account "
"name involved in the transaction, can be \"\" for the default "
"account.\n"
" \"address\" : \"address\", (string) The bitcoin "
"address involved in the transaction\n"
" \"category\" : \"send|receive\", (string) The category, "
"either 'send' or 'receive'\n"
" \"amount\" : x.xxx, (numeric) The amount "
"in " +
CURRENCY_UNIT +
"\n"
" \"label\" : \"label\", "
"(string) A comment for the address/transaction, "
"if any\n"
" \"vout\" : n, "
"(numeric) the vout value\n"
" \"fee\": x.xxx, "
"(numeric) The amount of the fee in " +
CURRENCY_UNIT +
". This is negative and only available for the \n"
" 'send' category of "
"transactions.\n"
" \"abandoned\": xxx (bool) 'true' if the "
"transaction has been abandoned (inputs are respendable). Only "
"available for the \n"
" 'send' category of "
"transactions.\n"
" }\n"
" ,...\n"
" ],\n"
" \"hex\" : \"data\" (string) Raw data for transaction\n"
"}\n"
"\nExamples:\n" +
HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e211"
"5b9345e16c5cf302fc80e9d5fbf5d48d"
"\"") +
HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e211"
"5b9345e16c5cf302fc80e9d5fbf5d48d"
"\" true") +
HelpExampleRpc("gettransaction", "\"1075db55d416d3ca199f55b6084e211"
"5b9345e16c5cf302fc80e9d5fbf5d48d"
"\""));
}
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
LOCK2(cs_main, pwallet->cs_wallet);
TxId txid;
txid.SetHex(request.params[0].get_str());
isminefilter filter = ISMINE_SPENDABLE;
if (!request.params[1].isNull() && request.params[1].get_bool()) {
filter = filter | ISMINE_WATCH_ONLY;
}
UniValue entry(UniValue::VOBJ);
auto it = pwallet->mapWallet.find(txid);
if (it == pwallet->mapWallet.end()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Invalid or non-wallet transaction id");
}
const CWalletTx &wtx = it->second;
Amount nCredit = wtx.GetCredit(filter);
Amount nDebit = wtx.GetDebit(filter);
Amount nNet = nCredit - nDebit;
Amount nFee = (wtx.IsFromMe(filter) ? wtx.tx->GetValueOut() - nDebit
: Amount::zero());
entry.pushKV("amount", ValueFromAmount(nNet - nFee));
if (wtx.IsFromMe(filter)) {
entry.pushKV("fee", ValueFromAmount(nFee));
}
WalletTxToJSON(wtx, entry);
UniValue details(UniValue::VARR);
ListTransactions(pwallet, wtx, "*", 0, false, details, filter);
entry.pushKV("details", details);
std::string strHex = EncodeHexTx(*wtx.tx, RPCSerializationFlags());
entry.pushKV("hex", strHex);
return entry;
}
static UniValue abandontransaction(const Config &config,
const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"abandontransaction \"txid\"\n"
"\nMark in-wallet transaction <txid> as abandoned\n"
"This will mark this transaction and all its in-wallet descendants "
"as abandoned which will allow\n"
"for their inputs to be respent. It can be used to replace "
"\"stuck\" or evicted transactions.\n"
"It only works on transactions which are not included in a block "
"and are not currently in the mempool.\n"
"It has no effect on transactions which are already abandoned.\n"
"\nArguments:\n"
"1. \"txid\" (string, required) The transaction id\n"
"\nResult:\n"
"\nExamples:\n" +
HelpExampleCli("abandontransaction", "\"1075db55d416d3ca199f55b6084"
"e2115b9345e16c5cf302fc80e9d5f"
"bf5d48d\"") +
HelpExampleRpc("abandontransaction", "\"1075db55d416d3ca199f55b6084"
"e2115b9345e16c5cf302fc80e9d5f"
"bf5d48d\""));
}
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
LOCK2(cs_main, pwallet->cs_wallet);
TxId txid;
txid.SetHex(request.params[0].get_str());
if (!pwallet->mapWallet.count(txid)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Invalid or non-wallet transaction id");
}
if (!pwallet->AbandonTransaction(txid)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Transaction not eligible for abandonment");
}
return NullUniValue;
}
static UniValue backupwallet(const Config &config,
const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"backupwallet \"destination\"\n"
"\nSafely copies current wallet file to destination, which can be "
"a directory or a path with filename.\n"
"\nArguments:\n"
"1. \"destination\" (string) The destination directory or file\n"
"\nExamples:\n" +
HelpExampleCli("backupwallet", "\"backup.dat\"") +
HelpExampleRpc("backupwallet", "\"backup.dat\""));
}
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
LOCK2(cs_main, pwallet->cs_wallet);
std::string strDest = request.params[0].get_str();
if (!pwallet->BackupWallet(strDest)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!");
}
return NullUniValue;
}
static UniValue keypoolrefill(const Config &config,
const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() > 1) {
throw std::runtime_error(
"keypoolrefill ( newsize )\n"
"\nFills the keypool." +
HelpRequiringPassphrase(pwallet) +
"\n"
"\nArguments\n"
"1. newsize (numeric, optional, default=100) "
"The new keypool size\n"
"\nExamples:\n" +
HelpExampleCli("keypoolrefill", "") +
HelpExampleRpc("keypoolrefill", ""));
}
LOCK2(cs_main, pwallet->cs_wallet);
// 0 is interpreted by TopUpKeyPool() as the default keypool size given by
// -keypool
unsigned int kpSize = 0;
if (!request.params[0].isNull()) {
if (request.params[0].get_int() < 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Invalid parameter, expected valid size.");
}
kpSize = (unsigned int)request.params[0].get_int();
}
EnsureWalletIsUnlocked(pwallet);
pwallet->TopUpKeyPool(kpSize);
if (pwallet->GetKeyPoolSize() < kpSize) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool.");
}
return NullUniValue;
}
static void LockWallet(CWallet *pWallet) {
LOCK(pWallet->cs_wallet);
pWallet->nRelockTime = 0;
pWallet->Lock();
}
static UniValue walletpassphrase(const Config &config,
const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (pwallet->IsCrypted() && (request.fHelp || request.params.size() != 2)) {
throw std::runtime_error(
"walletpassphrase \"passphrase\" timeout\n"
"\nStores the wallet decryption key in memory for 'timeout' "
"seconds.\n"
"This is needed prior to performing transactions related to "
"private keys such as sending bitcoins\n"
"\nArguments:\n"
"1. \"passphrase\" (string, required) The wallet passphrase\n"
"2. timeout (numeric, required) The time to keep the "
"decryption key in seconds; capped at 100000000 (~3 years).\n"
"\nNote:\n"
"Issuing the walletpassphrase command while the wallet is already "
"unlocked will set a new unlock\n"
"time that overrides the old one.\n"
"\nExamples:\n"
"\nUnlock the wallet for 60 seconds\n" +
HelpExampleCli("walletpassphrase", "\"my pass phrase\" 60") +
"\nLock the wallet again (before 60 seconds)\n" +
HelpExampleCli("walletlock", "") + "\nAs json rpc call\n" +
HelpExampleRpc("walletpassphrase", "\"my pass phrase\", 60"));
}
LOCK2(cs_main, pwallet->cs_wallet);
if (request.fHelp) {
return true;
}
if (!pwallet->IsCrypted()) {
throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE,
"Error: running with an unencrypted wallet, but "
"walletpassphrase was called.");
}
// Note that the walletpassphrase is stored in request.params[0] which is
// not mlock()ed
SecureString strWalletPass;
strWalletPass.reserve(100);
// TODO: get rid of this .c_str() by implementing
// SecureString::operator=(std::string)
// Alternately, find a way to make request.params[0] mlock()'d to begin
// with.
strWalletPass = request.params[0].get_str().c_str();
// Get the timeout
int64_t nSleepTime = request.params[1].get_int64();
// Timeout cannot be negative, otherwise it will relock immediately
if (nSleepTime < 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Timeout cannot be negative.");
}
// Clamp timeout
// larger values trigger a macos/libevent bug?
constexpr int64_t MAX_SLEEP_TIME = 100000000;
if (nSleepTime > MAX_SLEEP_TIME) {
nSleepTime = MAX_SLEEP_TIME;
}
if (strWalletPass.length() > 0) {
if (!pwallet->Unlock(strWalletPass)) {
throw JSONRPCError(
RPC_WALLET_PASSPHRASE_INCORRECT,
"Error: The wallet passphrase entered was incorrect.");
}
} else {
throw std::runtime_error(
"walletpassphrase <passphrase> <timeout>\n"
"Stores the wallet decryption key in memory for "
"<timeout> seconds.");
}
pwallet->TopUpKeyPool();
pwallet->nRelockTime = GetTime() + nSleepTime;
RPCRunLater(strprintf("lockwallet(%s)", pwallet->GetName()),
std::bind(LockWallet, pwallet), nSleepTime);
return NullUniValue;
}
static UniValue walletpassphrasechange(const Config &config,
const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (pwallet->IsCrypted() && (request.fHelp || request.params.size() != 2)) {
throw std::runtime_error(
"walletpassphrasechange \"oldpassphrase\" \"newpassphrase\"\n"
"\nChanges the wallet passphrase from 'oldpassphrase' to "
"'newpassphrase'.\n"
"\nArguments:\n"
"1. \"oldpassphrase\" (string) The current passphrase\n"
"2. \"newpassphrase\" (string) The new passphrase\n"
"\nExamples:\n" +
HelpExampleCli("walletpassphrasechange",
"\"old one\" \"new one\"") +
HelpExampleRpc("walletpassphrasechange",
"\"old one\", \"new one\""));
}
LOCK2(cs_main, pwallet->cs_wallet);
if (request.fHelp) {
return true;
}
if (!pwallet->IsCrypted()) {
throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE,
"Error: running with an unencrypted wallet, but "
"walletpassphrasechange was called.");
}
// TODO: get rid of these .c_str() calls by implementing
// SecureString::operator=(std::string)
// Alternately, find a way to make request.params[0] mlock()'d to begin
// with.
SecureString strOldWalletPass;
strOldWalletPass.reserve(100);
strOldWalletPass = request.params[0].get_str().c_str();
SecureString strNewWalletPass;
strNewWalletPass.reserve(100);
strNewWalletPass = request.params[1].get_str().c_str();
if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1) {
throw std::runtime_error(
"walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
"Changes the wallet passphrase from <oldpassphrase> to "
"<newpassphrase>.");
}
if (!pwallet->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass)) {
throw JSONRPCError(
RPC_WALLET_PASSPHRASE_INCORRECT,
"Error: The wallet passphrase entered was incorrect.");
}
return NullUniValue;
}
static UniValue walletlock(const Config &config,
const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (pwallet->IsCrypted() && (request.fHelp || request.params.size() != 0)) {
throw std::runtime_error(
"walletlock\n"
"\nRemoves the wallet encryption key from memory, locking the "
"wallet.\n"
"After calling this method, you will need to call walletpassphrase "
"again\n"
"before being able to call any methods which require the wallet to "
"be unlocked.\n"
"\nExamples:\n"
"\nSet the passphrase for 2 minutes to perform a transaction\n" +
HelpExampleCli("walletpassphrase", "\"my pass phrase\" 120") +
"\nPerform a send (requires passphrase set)\n" +
HelpExampleCli("sendtoaddress",
"\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 1.0") +
"\nClear the passphrase since we are done before 2 minutes is "
"up\n" +
HelpExampleCli("walletlock", "") + "\nAs json rpc call\n" +
HelpExampleRpc("walletlock", ""));
}
LOCK2(cs_main, pwallet->cs_wallet);
if (request.fHelp) {
return true;
}
if (!pwallet->IsCrypted()) {
throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE,
"Error: running with an unencrypted wallet, but "
"walletlock was called.");
}
pwallet->Lock();
pwallet->nRelockTime = 0;
return NullUniValue;
}
static UniValue encryptwallet(const Config &config,
const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (!pwallet->IsCrypted() &&
(request.fHelp || request.params.size() != 1)) {
throw std::runtime_error(
"encryptwallet \"passphrase\"\n"
"\nEncrypts the wallet with 'passphrase'. This is for first time "
"encryption.\n"
"After this, any calls that interact with private keys such as "
"sending or signing \n"
"will require the passphrase to be set prior the making these "
"calls.\n"
"Use the walletpassphrase call for this, and then walletlock "
"call.\n"
"If the wallet is already encrypted, use the "
"walletpassphrasechange call.\n"
"Note that this will shutdown the server.\n"
"\nArguments:\n"
"1. \"passphrase\" (string) The pass phrase to encrypt the "
"wallet with. It must be at least 1 character, but should be "
"long.\n"
"\nExamples:\n"
"\nEncrypt your wallet\n" +
HelpExampleCli("encryptwallet", "\"my pass phrase\"") +
"\nNow set the passphrase to use the wallet, such as for signing "
"or sending bitcoin\n" +
HelpExampleCli("walletpassphrase", "\"my pass phrase\"") +
"\nNow we can do something like sign\n" +
HelpExampleCli("signmessage", "\"address\" \"test message\"") +
"\nNow lock the wallet again by removing the passphrase\n" +
HelpExampleCli("walletlock", "") + "\nAs a json rpc call\n" +
HelpExampleRpc("encryptwallet", "\"my pass phrase\""));
}
LOCK2(cs_main, pwallet->cs_wallet);
if (request.fHelp) {
return true;
}
if (pwallet->IsCrypted()) {
throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE,
"Error: running with an encrypted wallet, but "
"encryptwallet was called.");
}
// TODO: get rid of this .c_str() by implementing
// SecureString::operator=(std::string)
// Alternately, find a way to make request.params[0] mlock()'d to begin
// with.
SecureString strWalletPass;
strWalletPass.reserve(100);
strWalletPass = request.params[0].get_str().c_str();
if (strWalletPass.length() < 1) {
throw std::runtime_error("encryptwallet <passphrase>\n"
"Encrypts the wallet with <passphrase>.");
}
if (!pwallet->EncryptWallet(strWalletPass)) {
throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED,
"Error: Failed to encrypt the wallet.");
}
// 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:
StartShutdown();
return "wallet encrypted; Bitcoin server stopping, restart to run with "
"encrypted wallet. The keypool has been flushed and a new HD seed "
"was generated (if you are using HD). You need to make a new "
"backup.";
}
static UniValue lockunspent(const Config &config,
const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() < 1 ||
request.params.size() > 2) {
throw std::runtime_error(
"lockunspent unlock ([{\"txid\":\"txid\",\"vout\":n},...])\n"
"\nUpdates list of temporarily unspendable outputs.\n"
"Temporarily lock (unlock=false) or unlock (unlock=true) specified "
"transaction outputs.\n"
"If no transaction outputs are specified when unlocking then all "
"current locked transaction outputs are unlocked.\n"
"A locked transaction output will not be chosen by automatic coin "
"selection, when spending bitcoins.\n"
"Locks are stored in memory only. Nodes start with zero locked "
"outputs, and the locked output list\n"
"is always cleared (by virtue of process exit) when a node stops "
"or fails.\n"
"Also see the listunspent call\n"
"\nArguments:\n"
"1. unlock (boolean, required) Whether to unlock (true) "
"or lock (false) the specified transactions\n"
"2. \"transactions\" (string, optional) A json array of objects. "
"Each object the txid (string) vout (numeric)\n"
" [ (json array of json objects)\n"
" {\n"
" \"txid\":\"id\", (string) The transaction id\n"
" \"vout\": n (numeric) The output number\n"
" }\n"
" ,...\n"
" ]\n"
"\nResult:\n"
"true|false (boolean) Whether the command was successful or "
"not\n"
"\nExamples:\n"
"\nList the unspent transactions\n" +
HelpExampleCli("listunspent", "") +
"\nLock an unspent transaction\n" +
HelpExampleCli("lockunspent", "false "
"\"[{\\\"txid\\\":"
"\\\"a08e6907dbbd3d809776dbfc5d82e371"
"b764ed838b5655e72f463568df1aadf0\\\""
",\\\"vout\\\":1}]\"") +
"\nList the locked transactions\n" +
HelpExampleCli("listlockunspent", "") +
"\nUnlock the transaction again\n" +
HelpExampleCli("lockunspent", "true "
"\"[{\\\"txid\\\":"
"\\\"a08e6907dbbd3d809776dbfc5d82e371"
"b764ed838b5655e72f463568df1aadf0\\\""
",\\\"vout\\\":1}]\"") +
"\nAs a json rpc call\n" +
HelpExampleRpc("lockunspent", "false, "
"\"[{\\\"txid\\\":"
"\\\"a08e6907dbbd3d809776dbfc5d82e371"
"b764ed838b5655e72f463568df1aadf0\\\""
",\\\"vout\\\":1}]\""));
}
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
LOCK2(cs_main, pwallet->cs_wallet);
RPCTypeCheckArgument(request.params[0], UniValue::VBOOL);
bool fUnlock = request.params[0].get_bool();
if (request.params[1].isNull()) {
if (fUnlock) {
pwallet->UnlockAllCoins();
}
return true;
}
RPCTypeCheckArgument(request.params[1], UniValue::VARR);
const UniValue &output_params = request.params[1];
// Create and validate the COutPoints first.
std::vector<COutPoint> outputs;
outputs.reserve(output_params.size());
for (size_t idx = 0; idx < output_params.size(); idx++) {
const UniValue &o = output_params[idx].get_obj();
RPCTypeCheckObj(o, {
{"txid", UniValueType(UniValue::VSTR)},
{"vout", UniValueType(UniValue::VNUM)},
});
const std::string &strTxId = find_value(o, "txid").get_str();
if (!IsHex(strTxId)) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Invalid parameter, expected hex txid");
}
const int nOutput = find_value(o, "vout").get_int();
if (nOutput < 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Invalid parameter, vout must be positive");
}
const TxId txid(uint256S(strTxId));
const auto it = pwallet->mapWallet.find(txid);
if (it == pwallet->mapWallet.end()) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Invalid parameter, unknown transaction");
}
const COutPoint output(txid, nOutput);
const CWalletTx &trans = it->second;
if (output.GetN() >= trans.tx->vout.size()) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Invalid parameter, vout index out of bounds");
}
if (pwallet->IsSpent(output)) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Invalid parameter, expected unspent output");
}
const bool is_locked = pwallet->IsLockedCoin(output);
if (fUnlock && !is_locked) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Invalid parameter, expected locked output");
}
if (!fUnlock && is_locked) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Invalid parameter, output already locked");
}
outputs.push_back(output);
}
// Atomically set (un)locked status for the outputs.
for (const COutPoint &output : outputs) {
if (fUnlock) {
pwallet->UnlockCoin(output);
} else {
pwallet->LockCoin(output);
}
}
return true;
}
static UniValue listlockunspent(const Config &config,
const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() > 0) {
throw std::runtime_error(
"listlockunspent\n"
"\nReturns list of temporarily unspendable outputs.\n"
"See the lockunspent call to lock and unlock transactions for "
"spending.\n"
"\nResult:\n"
"[\n"
" {\n"
" \"txid\" : \"transactionid\", (string) The transaction id "
"locked\n"
" \"vout\" : n (numeric) The vout value\n"
" }\n"
" ,...\n"
"]\n"
"\nExamples:\n"
"\nList the unspent transactions\n" +
HelpExampleCli("listunspent", "") +
"\nLock an unspent transaction\n" +
HelpExampleCli("lockunspent", "false "
"\"[{\\\"txid\\\":"
"\\\"a08e6907dbbd3d809776dbfc5d82e371"
"b764ed838b5655e72f463568df1aadf0\\\""
",\\\"vout\\\":1}]\"") +
"\nList the locked transactions\n" +
HelpExampleCli("listlockunspent", "") +
"\nUnlock the transaction again\n" +
HelpExampleCli("lockunspent", "true "
"\"[{\\\"txid\\\":"
"\\\"a08e6907dbbd3d809776dbfc5d82e371"
"b764ed838b5655e72f463568df1aadf0\\\""
",\\\"vout\\\":1}]\"") +
"\nAs a json rpc call\n" + HelpExampleRpc("listlockunspent", ""));
}
LOCK2(cs_main, pwallet->cs_wallet);
std::vector<COutPoint> vOutpts;
pwallet->ListLockedCoins(vOutpts);
UniValue ret(UniValue::VARR);
for (const COutPoint &output : vOutpts) {
UniValue o(UniValue::VOBJ);
o.pushKV("txid", output.GetTxId().GetHex());
o.pushKV("vout", int(output.GetN()));
ret.push_back(o);
}
return ret;
}
static UniValue settxfee(const Config &config, const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() < 1 ||
request.params.size() > 1) {
throw std::runtime_error(
"settxfee amount\n"
"\nSet the transaction fee per kB for this wallet. Overrides the "
"global -paytxfee command line parameter.\n"
"\nArguments:\n"
"1. amount (numeric or string, required) The transaction "
"fee in " +
CURRENCY_UNIT +
"/kB\n"
"\nResult\n"
"true|false (boolean) Returns true if successful\n"
"\nExamples:\n" +
HelpExampleCli("settxfee", "0.00001") +
HelpExampleRpc("settxfee", "0.00001"));
}
LOCK2(cs_main, pwallet->cs_wallet);
Amount nAmount = AmountFromValue(request.params[0]);
pwallet->m_pay_tx_fee = CFeeRate(nAmount, 1000);
return true;
}
static UniValue getwalletinfo(const Config &config,
const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() != 0) {
throw std::runtime_error(
"getwalletinfo\n"
"Returns an object containing various wallet state info.\n"
"\nResult:\n"
"{\n"
" \"walletname\": xxxxx, (string) the wallet name\n"
" \"walletversion\": xxxxx, (numeric) the wallet "
"version\n"
" \"balance\": xxxxxxx, (numeric) the total "
"confirmed balance of the wallet in " +
CURRENCY_UNIT +
"\n"
" \"unconfirmed_balance\": xxx, (numeric) "
"the total unconfirmed balance of the wallet in " +
CURRENCY_UNIT +
"\n"
" \"immature_balance\": xxxxxx, (numeric) "
"the total immature balance of the wallet in " +
CURRENCY_UNIT +
"\n"
" \"txcount\": xxxxxxx, (numeric) the total number "
"of transactions in the wallet\n"
" \"keypoololdest\": xxxxxx, (numeric) the timestamp "
"(seconds since Unix epoch) of the oldest pre-generated key in the "
"key pool\n"
" \"keypoolsize\": xxxx, (numeric) how many new keys "
"are pre-generated (only counts external keys)\n"
" \"keypoolsize_hd_internal\": xxxx, (numeric) 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)\n"
" \"unlocked_until\": ttt, (numeric) the timestamp in "
"seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is "
"unlocked for transfers, or 0 if the wallet is locked\n"
" \"paytxfee\": x.xxxx, (numeric) the transaction "
"fee configuration, set in " +
CURRENCY_UNIT +
"/kB\n"
" \"hdseedid\": \"<hash160>\" (string, optional) the "
"Hash160 of the HD seed (only present when HD is enabled)\n"
" \"hdmasterkeyid\": \"<hash160>\" (string, optional) alias "
"for hdseedid retained for backwards-compatibility. Will be "
"removed in V0.21.\n"
"}\n"
"\nExamples:\n" +
HelpExampleCli("getwalletinfo", "") +
HelpExampleRpc("getwalletinfo", ""));
}
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
LOCK2(cs_main, pwallet->cs_wallet);
UniValue obj(UniValue::VOBJ);
size_t kpExternalSize = pwallet->KeypoolCountExternalKeys();
obj.pushKV("walletname", pwallet->GetName());
obj.pushKV("walletversion", pwallet->GetVersion());
obj.pushKV("balance", ValueFromAmount(pwallet->GetBalance()));
obj.pushKV("unconfirmed_balance",
ValueFromAmount(pwallet->GetUnconfirmedBalance()));
obj.pushKV("immature_balance",
ValueFromAmount(pwallet->GetImmatureBalance()));
obj.pushKV("txcount", (int)pwallet->mapWallet.size());
obj.pushKV("keypoololdest", pwallet->GetOldestKeyPoolTime());
obj.pushKV("keypoolsize", (int64_t)kpExternalSize);
CKeyID seed_id = pwallet->GetHDChain().seed_id;
if (!seed_id.IsNull() && pwallet->CanSupportFeature(FEATURE_HD_SPLIT)) {
obj.pushKV("keypoolsize_hd_internal",
int64_t(pwallet->GetKeyPoolSize() - kpExternalSize));
}
if (pwallet->IsCrypted()) {
obj.pushKV("unlocked_until", pwallet->nRelockTime);
}
obj.pushKV("paytxfee", ValueFromAmount(pwallet->m_pay_tx_fee.GetFeePerK()));
if (!seed_id.IsNull()) {
obj.pushKV("hdseedid", seed_id.GetHex());
obj.pushKV("hdmasterkeyid", seed_id.GetHex());
}
return obj;
}
static UniValue listwallets(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 0) {
throw std::runtime_error(
"listwallets\n"
"Returns a list of currently loaded wallets.\n"
"For full information on the wallet, use \"getwalletinfo\"\n"
"\nResult:\n"
"[ (json array of strings)\n"
" \"walletname\" (string) the wallet name\n"
" ...\n"
"]\n"
"\nExamples:\n" +
HelpExampleCli("listwallets", "") +
HelpExampleRpc("listwallets", ""));
}
UniValue obj(UniValue::VARR);
for (const std::shared_ptr<CWallet> &wallet : GetWallets()) {
if (!EnsureWalletIsAvailable(wallet.get(), request.fHelp)) {
return NullUniValue;
}
LOCK(wallet->cs_wallet);
obj.push_back(wallet->GetName());
}
return obj;
}
static UniValue loadwallet(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"loadwallet \"filename\"\n"
"\nLoads a wallet from a wallet file or directory."
"\nNote that all wallet command-line options used when starting "
"bitcoind will be"
"\napplied to the new wallet (eg -zapwallettxes, upgradewallet, "
"rescan, etc).\n"
"\nArguments:\n"
"1. \"filename\" (string, required) The wallet directory or "
".dat file.\n"
"\nResult:\n"
"{\n"
" \"name\" : <wallet_name>, (string) The wallet name if "
"loaded successfully.\n"
" \"warning\" : <warning>, (string) Warning message if "
"wallet was not loaded cleanly.\n"
"}\n"
"\nExamples:\n" +
HelpExampleCli("loadwallet", "\"test.dat\"") +
HelpExampleRpc("loadwallet", "\"test.dat\""));
}
const CChainParams &chainParams = config.GetChainParams();
std::string wallet_file = request.params[0].get_str();
std::string error;
fs::path wallet_path = fs::absolute(wallet_file, GetWalletDir());
if (fs::symlink_status(wallet_path).type() == fs::file_not_found) {
throw JSONRPCError(RPC_WALLET_NOT_FOUND,
"Wallet " + wallet_file + " not found.");
} else if (fs::is_directory(wallet_path)) {
// The given filename is a directory. Check that there's a wallet.dat
// file.
fs::path wallet_dat_file = wallet_path / "wallet.dat";
if (fs::symlink_status(wallet_dat_file).type() == fs::file_not_found) {
throw JSONRPCError(RPC_WALLET_NOT_FOUND,
"Directory " + wallet_file +
" does not contain a wallet.dat file.");
}
}
std::string warning;
if (!CWallet::Verify(chainParams, wallet_file, false, error, warning)) {
throw JSONRPCError(RPC_WALLET_ERROR,
"Wallet file verification failed: " + error);
}
std::shared_ptr<CWallet> const wallet = CWallet::CreateWalletFromFile(
chainParams, wallet_file, fs::absolute(wallet_file, GetWalletDir()));
if (!wallet) {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet loading failed.");
}
AddWallet(wallet);
wallet->postInitProcess();
UniValue obj(UniValue::VOBJ);
obj.pushKV("name", wallet->GetName());
obj.pushKV("warning", warning);
return obj;
}
static UniValue createwallet(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"createwallet \"wallet_name\"\n"
"\nCreates and loads a new wallet.\n"
"\nArguments:\n"
"1. \"wallet_name\" (string, required) The name for the new "
"wallet. If this is a path, the wallet will be created at the path "
"location.\n"
"\nResult:\n"
"{\n"
" \"name\" : <wallet_name>, (string) The wallet name if "
"created successfully. If the wallet was created using a full "
"path, the wallet_name will be the full path.\n"
" \"warning\" : <warning>, (string) Warning message if "
"wallet was not loaded cleanly.\n"
"}\n"
"\nExamples:\n" +
HelpExampleCli("createwallet", "\"testwallet\"") +
HelpExampleRpc("createwallet", "\"testwallet\""));
}
const CChainParams &chainParams = config.GetChainParams();
std::string wallet_name = request.params[0].get_str();
std::string error;
std::string warning;
fs::path wallet_path = fs::absolute(wallet_name, GetWalletDir());
if (fs::symlink_status(wallet_path).type() != fs::file_not_found) {
throw JSONRPCError(RPC_WALLET_ERROR,
"Wallet " + wallet_name + " already exists.");
}
// Wallet::Verify will check if we're trying to create a wallet with a
// duplicate name.
if (!CWallet::Verify(chainParams, wallet_name, false, error, warning)) {
throw JSONRPCError(RPC_WALLET_ERROR,
"Wallet file verification failed: " + error);
}
std::shared_ptr<CWallet> const wallet = CWallet::CreateWalletFromFile(
chainParams, wallet_name, fs::absolute(wallet_name, GetWalletDir()));
if (!wallet) {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet creation failed.");
}
AddWallet(wallet);
wallet->postInitProcess();
UniValue obj(UniValue::VOBJ);
obj.pushKV("name", wallet->GetName());
obj.pushKV("warning", warning);
return obj;
}
static UniValue unloadwallet(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() > 1) {
throw std::runtime_error(
"unloadwallet ( \"wallet_name\" )\n"
"Unloads the wallet referenced by the request endpoint otherwise "
"unloads the wallet specified in the argument.\n"
"Specifying the wallet name on a wallet endpoint is invalid."
"\nArguments:\n"
"1. \"wallet_name\" (string, optional) The name of the wallet "
"to unload.\n"
"\nExamples:\n" +
HelpExampleCli("unloadwallet", "wallet_name") +
HelpExampleRpc("unloadwallet", "wallet_name"));
}
std::string wallet_name;
if (GetWalletNameFromJSONRPCRequest(request, wallet_name)) {
if (!request.params[0].isNull()) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Cannot unload the requested wallet");
}
} else {
wallet_name = request.params[0].get_str();
}
std::shared_ptr<CWallet> wallet = GetWallet(wallet_name);
if (!wallet) {
throw JSONRPCError(RPC_WALLET_NOT_FOUND,
"Requested wallet does not exist or is not loaded");
}
// Release the "main" shared pointer and prevent further notifications.
// Note that any attempt to load the same wallet would fail until the wallet
// is destroyed (see CheckUniqueFileid).
if (!RemoveWallet(wallet)) {
throw JSONRPCError(RPC_MISC_ERROR, "Requested wallet already unloaded");
}
UnregisterValidationInterface(wallet.get());
// The wallet can be in use so it's not possible to explicitly unload here.
// Just notify the unload intent so that all shared pointers are released.
// The wallet will be destroyed once the last shared pointer is released.
wallet->NotifyUnload();
// There's no point in waiting for the wallet to unload.
// At this point this method should never fail. The unloading could only
// fail due to an unexpected error which would cause a process termination.
return NullUniValue;
}
static UniValue resendwallettransactions(const Config &config,
const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() != 0) {
throw std::runtime_error(
"resendwallettransactions\n"
"Immediately re-broadcast unconfirmed wallet transactions to all "
"peers.\n"
"Intended only for testing; the wallet code periodically "
"re-broadcasts\n"
"automatically.\n"
"Returns an RPC error if -walletbroadcast is set to false.\n"
"Returns array of transaction ids that were re-broadcast.\n");
}
if (!g_connman) {
throw JSONRPCError(
RPC_CLIENT_P2P_DISABLED,
"Error: Peer-to-peer functionality missing or disabled");
}
LOCK2(cs_main, pwallet->cs_wallet);
if (!pwallet->GetBroadcastTransactions()) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet transaction "
"broadcasting is disabled with "
"-walletbroadcast");
}
std::vector<uint256> txids =
pwallet->ResendWalletTransactionsBefore(GetTime(), g_connman.get());
UniValue result(UniValue::VARR);
for (const uint256 &txid : txids) {
result.push_back(txid.ToString());
}
return result;
}
static UniValue listunspent(const Config &config,
const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() > 5) {
throw std::runtime_error(
"listunspent ( minconf maxconf [\"addresses\",...] "
"[include_unsafe] [query_options])\n"
"\nReturns array of unspent transaction outputs\n"
"with between minconf and maxconf (inclusive) confirmations.\n"
"Optionally filter to only include txouts paid to specified "
"addresses.\n"
"\nArguments:\n"
"1. minconf (numeric, optional, default=1) The minimum "
"confirmations to filter\n"
"2. maxconf (numeric, optional, default=9999999) The "
"maximum confirmations to filter\n"
"3. \"addresses\" (string) A json array of bitcoin addresses "
"to filter\n"
" [\n"
" \"address\" (string) bitcoin address\n"
" ,...\n"
" ]\n"
"4. include_unsafe (bool, optional, default=true) Include outputs "
"that are not safe to spend\n"
" See description of \"safe\" attribute below.\n"
"5. query_options (json, optional) JSON with query options\n"
" {\n"
" \"minimumAmount\" (numeric or string, default=0) Minimum "
"value of each UTXO in " +
CURRENCY_UNIT +
"\n"
" \"maximumAmount\" (numeric or string, default=unlimited) "
"Maximum value of each UTXO in " +
CURRENCY_UNIT +
"\n"
" \"maximumCount\" (numeric or string, default=unlimited) "
"Maximum number of UTXOs\n"
" \"minimumSumAmount\" (numeric or string, default=unlimited) "
"Minimum sum value of all UTXOs in " +
CURRENCY_UNIT +
"\n"
" }\n"
"\nResult\n"
"[ (array of json object)\n"
" {\n"
" \"txid\" : \"txid\", (string) the transaction id \n"
" \"vout\" : n, (numeric) the vout value\n"
" \"address\" : \"address\", (string) the bitcoin address\n"
" \"label\" : \"label\", (string) The associated label, "
"or \"\" for the default label\n"
" \"account\" : \"account\", (string) DEPRECATED. This field "
"will be removed in v0.21. To see this deprecated field, start "
"bitcoind with -deprecatedrpc=accounts. The associated account, or "
"\"\" for the default account\n"
" \"scriptPubKey\" : \"key\", (string) the script key\n"
" \"amount\" : x.xxx, (numeric) the transaction output "
"amount in " +
CURRENCY_UNIT +
"\n"
" \"confirmations\" : n, (numeric) The number of "
"confirmations\n"
" \"redeemScript\" : n (string) The redeemScript if "
"scriptPubKey is P2SH\n"
" \"spendable\" : xxx, (bool) Whether we have the "
"private keys to spend this output\n"
" \"solvable\" : xxx, (bool) Whether we know how to "
"spend this output, ignoring the lack of keys\n"
" \"safe\" : xxx (bool) Whether this output is "
"considered safe to spend. Unconfirmed transactions\n"
" from outside keys are considered "
"unsafe and are not eligible for spending by\n"
" fundrawtransaction and "
"sendtoaddress.\n"
" }\n"
" ,...\n"
"]\n"
"\nExamples\n" +
HelpExampleCli("listunspent", "") +
HelpExampleCli("listunspent",
"6 9999999 "
"\"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\","
"\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"") +
HelpExampleRpc("listunspent",
"6, 9999999 "
"\"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\","
"\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"") +
HelpExampleCli(
"listunspent",
"6 9999999 '[]' true '{ \"minimumAmount\": 0.005 }'") +
HelpExampleRpc(
"listunspent",
"6, 9999999, [] , true, { \"minimumAmount\": 0.005 } "));
}
int nMinDepth = 1;
if (!request.params[0].isNull()) {
RPCTypeCheckArgument(request.params[0], UniValue::VNUM);
nMinDepth = request.params[0].get_int();
}
int nMaxDepth = 9999999;
if (!request.params[1].isNull()) {
RPCTypeCheckArgument(request.params[1], UniValue::VNUM);
nMaxDepth = request.params[1].get_int();
}
std::set<CTxDestination> destinations;
if (!request.params[2].isNull()) {
RPCTypeCheckArgument(request.params[2], UniValue::VARR);
UniValue inputs = request.params[2].get_array();
for (size_t idx = 0; idx < inputs.size(); idx++) {
const UniValue &input = inputs[idx];
CTxDestination dest =
DecodeDestination(input.get_str(), config.GetChainParams());
if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
std::string("Invalid Bitcoin address: ") +
input.get_str());
}
if (!destinations.insert(dest).second) {
throw JSONRPCError(
RPC_INVALID_PARAMETER,
std::string("Invalid parameter, duplicated address: ") +
input.get_str());
}
}
}
bool include_unsafe = true;
if (!request.params[3].isNull()) {
RPCTypeCheckArgument(request.params[3], UniValue::VBOOL);
include_unsafe = request.params[3].get_bool();
}
Amount nMinimumAmount = Amount::zero();
Amount nMaximumAmount = MAX_MONEY;
Amount nMinimumSumAmount = MAX_MONEY;
uint64_t nMaximumCount = 0;
if (!request.params[4].isNull()) {
const UniValue &options = request.params[4].get_obj();
if (options.exists("minimumAmount")) {
nMinimumAmount = AmountFromValue(options["minimumAmount"]);
}
if (options.exists("maximumAmount")) {
nMaximumAmount = AmountFromValue(options["maximumAmount"]);
}
if (options.exists("minimumSumAmount")) {
nMinimumSumAmount = AmountFromValue(options["minimumSumAmount"]);
}
if (options.exists("maximumCount")) {
nMaximumCount = options["maximumCount"].get_int64();
}
}
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
UniValue results(UniValue::VARR);
std::vector<COutput> vecOutputs;
LOCK2(cs_main, pwallet->cs_wallet);
pwallet->AvailableCoins(vecOutputs, !include_unsafe, nullptr,
nMinimumAmount, nMaximumAmount, nMinimumSumAmount,
nMaximumCount, nMinDepth, nMaxDepth);
for (const COutput &out : vecOutputs) {
CTxDestination address;
const CScript &scriptPubKey = out.tx->tx->vout[out.i].scriptPubKey;
bool fValidAddress = ExtractDestination(scriptPubKey, address);
if (destinations.size() &&
(!fValidAddress || !destinations.count(address))) {
continue;
}
UniValue entry(UniValue::VOBJ);
entry.pushKV("txid", out.tx->GetId().GetHex());
entry.pushKV("vout", out.i);
if (fValidAddress) {
entry.pushKV("address", EncodeDestination(address, config));
if (pwallet->mapAddressBook.count(address)) {
entry.pushKV("label", pwallet->mapAddressBook[address].name);
if (IsDeprecatedRPCEnabled(gArgs, "accounts")) {
entry.pushKV("account",
pwallet->mapAddressBook[address].name);
}
}
if (scriptPubKey.IsPayToScriptHash()) {
const CScriptID &hash = boost::get<CScriptID>(address);
CScript redeemScript;
if (pwallet->GetCScript(hash, redeemScript)) {
entry.pushKV("redeemScript", HexStr(redeemScript.begin(),
redeemScript.end()));
}
}
}
entry.pushKV("scriptPubKey",
HexStr(scriptPubKey.begin(), scriptPubKey.end()));
entry.pushKV("amount", ValueFromAmount(out.tx->tx->vout[out.i].nValue));
entry.pushKV("confirmations", out.nDepth);
entry.pushKV("spendable", out.fSpendable);
entry.pushKV("solvable", out.fSolvable);
entry.pushKV("safe", out.fSafe);
results.push_back(entry);
}
return results;
}
void FundTransaction(CWallet *const pwallet, CMutableTransaction &tx,
Amount &fee_out, int &change_position, UniValue options) {
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
CCoinControl coinControl;
change_position = -1;
bool lockUnspents = false;
UniValue subtractFeeFromOutputs;
std::set<int> setSubtractFeeFromOutputs;
if (!options.isNull()) {
if (options.type() == UniValue::VBOOL) {
// backward compatibility bool only fallback
coinControl.fAllowWatchOnly = options.get_bool();
} else {
RPCTypeCheckArgument(options, UniValue::VOBJ);
RPCTypeCheckObj(
options,
{
{"changeAddress", UniValueType(UniValue::VSTR)},
{"changePosition", UniValueType(UniValue::VNUM)},
{"includeWatching", UniValueType(UniValue::VBOOL)},
{"lockUnspents", UniValueType(UniValue::VBOOL)},
// will be checked below
{"feeRate", UniValueType()},
{"subtractFeeFromOutputs", UniValueType(UniValue::VARR)},
},
true, true);
if (options.exists("changeAddress")) {
CTxDestination dest = DecodeDestination(
options["changeAddress"].get_str(), pwallet->chainParams);
if (!IsValidDestination(dest)) {
throw JSONRPCError(
RPC_INVALID_ADDRESS_OR_KEY,
"changeAddress must be a valid bitcoin address");
}
coinControl.destChange = dest;
}
if (options.exists("changePosition")) {
change_position = options["changePosition"].get_int();
}
if (options.exists("includeWatching")) {
coinControl.fAllowWatchOnly =
options["includeWatching"].get_bool();
}
if (options.exists("lockUnspents")) {
lockUnspents = options["lockUnspents"].get_bool();
}
if (options.exists("feeRate")) {
coinControl.m_feerate =
CFeeRate(AmountFromValue(options["feeRate"]));
coinControl.fOverrideFeeRate = true;
}
if (options.exists("subtractFeeFromOutputs")) {
subtractFeeFromOutputs =
options["subtractFeeFromOutputs"].get_array();
}
}
}
if (tx.vout.size() == 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"TX must have at least one output");
}
if (change_position != -1 &&
(change_position < 0 ||
(unsigned int)change_position > tx.vout.size())) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"changePosition out of bounds");
}
for (size_t idx = 0; idx < subtractFeeFromOutputs.size(); idx++) {
int pos = subtractFeeFromOutputs[idx].get_int();
if (setSubtractFeeFromOutputs.count(pos)) {
throw JSONRPCError(
RPC_INVALID_PARAMETER,
strprintf("Invalid parameter, duplicated position: %d", pos));
}
if (pos < 0) {
throw JSONRPCError(
RPC_INVALID_PARAMETER,
strprintf("Invalid parameter, negative position: %d", pos));
}
if (pos >= int(tx.vout.size())) {
throw JSONRPCError(
RPC_INVALID_PARAMETER,
strprintf("Invalid parameter, position too large: %d", pos));
}
setSubtractFeeFromOutputs.insert(pos);
}
std::string strFailReason;
if (!pwallet->FundTransaction(tx, fee_out, change_position, strFailReason,
lockUnspents, setSubtractFeeFromOutputs,
coinControl)) {
throw JSONRPCError(RPC_WALLET_ERROR, strFailReason);
}
}
static UniValue fundrawtransaction(const Config &config,
const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() < 1 ||
request.params.size() > 2) {
throw std::runtime_error(
"fundrawtransaction \"hexstring\" ( options )\n"
"\nAdd inputs to a transaction until it has enough in value to "
"meet its out value.\n"
"This will not modify existing inputs, and will add at most one "
"change output to the outputs.\n"
"No existing outputs will be modified unless "
"\"subtractFeeFromOutputs\" is specified.\n"
"Note that inputs which were signed may need to be resigned after "
"completion since in/outputs have been added.\n"
"The inputs added will not be signed, use "
"signrawtransactionwithkey or signrawtransactionwithwallet for "
"that.\n"
"Note that all existing inputs must have their previous output "
"transaction be in the wallet.\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"
"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"
"\nArguments:\n"
"1. \"hexstring\" (string, required) The hex string of "
"the raw transaction\n"
"2. options (object, optional)\n"
" {\n"
" \"changeAddress\" (string, optional, default pool "
"address) The bitcoin address to receive the change\n"
" \"changePosition\" (numeric, optional, default "
"random) The index of the change output\n"
" \"includeWatching\" (boolean, optional, default "
"false) Also select inputs which are watch only\n"
" \"lockUnspents\" (boolean, optional, default "
"false) Lock selected unspent outputs\n"
" \"feeRate\" (numeric, optional, default not "
"set: makes wallet determine the fee) Set a specific fee rate in " +
CURRENCY_UNIT +
"/kB\n"
" \"subtractFeeFromOutputs\" (array, optional) A json array of "
"integers.\n"
" The fee will be equally deducted "
"from the amount of each specified output.\n"
" The outputs are specified by their "
"zero-based index, before any change output is added.\n"
" Those recipients will receive less "
"bitcoins than you enter in their corresponding amount field.\n"
" If no outputs are specified here, "
"the sender pays the fee.\n"
" [vout_index,...]\n"
" }\n"
" for backward compatibility: passing in a "
"true instead of an object will result in "
"{\"includeWatching\":true}\n"
"\nResult:\n"
"{\n"
" \"hex\": \"value\", (string) The resulting raw "
"transaction (hex-encoded string)\n"
" \"fee\": n, (numeric) Fee in " +
CURRENCY_UNIT +
" the resulting transaction pays\n"
" \"changepos\": n (numeric) The position of the added "
"change output, or -1\n"
"}\n"
"\nExamples:\n"
"\nCreate a transaction with no inputs\n" +
HelpExampleCli("createrawtransaction",
"\"[]\" \"{\\\"myaddress\\\":0.01}\"") +
"\nAdd sufficient unsigned inputs to meet the output value\n" +
HelpExampleCli("fundrawtransaction", "\"rawtransactionhex\"") +
"\nSign the transaction\n" +
HelpExampleCli("signrawtransactionwithwallet",
"\"fundedtransactionhex\"") +
"\nSend the transaction\n" +
HelpExampleCli("sendrawtransaction", "\"signedtransactionhex\""));
}
RPCTypeCheck(request.params, {UniValue::VSTR, UniValueType()});
// parse hex string from parameter
CMutableTransaction tx;
if (!DecodeHexTx(tx, request.params[0].get_str())) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
}
Amount fee;
int change_position;
FundTransaction(pwallet, tx, fee, change_position, request.params[1]);
UniValue result(UniValue::VOBJ);
result.pushKV("hex", EncodeHexTx(CTransaction(tx)));
result.pushKV("fee", ValueFromAmount(fee));
result.pushKV("changepos", change_position);
return result;
}
UniValue signrawtransactionwithwallet(const Config &config,
const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() < 1 ||
request.params.size() > 3) {
throw std::runtime_error(
"signrawtransactionwithwallet \"hexstring\" ( "
"[{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\","
"\"redeemScript\":\"hex\"},...] sighashtype )\n"
"\nSign inputs for raw transaction (serialized, hex-encoded).\n"
"The second optional argument (may be null) is an array of "
"previous transaction outputs that\n"
"this transaction depends on but may not yet be in the block "
"chain.\n" +
HelpRequiringPassphrase(pwallet) +
"\n"
"\nArguments:\n"
"1. \"hexstring\" (string, required) The "
"transaction hex string\n"
"2. \"prevtxs\" (string, optional) An json "
"array of previous dependent transaction outputs\n"
" [ (json array of json objects, "
"or 'null' if none provided)\n"
" {\n"
" \"txid\":\"id\", (string, required) The "
"transaction id\n"
" \"vout\":n, (numeric, required) The "
"output number\n"
" \"scriptPubKey\": \"hex\", (string, required) script "
"key\n"
" \"redeemScript\": \"hex\", (string, required for "
"P2SH) redeem script\n"
" \"amount\": value (numeric, required) The "
"amount spent\n"
" }\n"
" ,...\n"
" ]\n"
"3. \"sighashtype\" (string, optional, "
"default=ALL) The signature hash type. Must be one of\n"
" \"ALL|FORKID\"\n"
" \"NONE|FORKID\"\n"
" \"SINGLE|FORKID\"\n"
" \"ALL|FORKID|ANYONECANPAY\"\n"
" \"NONE|FORKID|ANYONECANPAY\"\n"
" \"SINGLE|FORKID|ANYONECANPAY\"\n"
"\nResult:\n"
"{\n"
" \"hex\" : \"value\", (string) The hex-encoded "
"raw transaction with signature(s)\n"
" \"complete\" : true|false, (boolean) If the "
"transaction has a complete set of signatures\n"
" \"errors\" : [ (json array of objects) "
"Script verification errors (if there are any)\n"
" {\n"
" \"txid\" : \"hash\", (string) The hash of the "
"referenced, previous transaction\n"
" \"vout\" : n, (numeric) The index of the "
"output to spent and used as input\n"
" \"scriptSig\" : \"hex\", (string) The hex-encoded "
"signature script\n"
" \"sequence\" : n, (numeric) Script sequence "
"number\n"
" \"error\" : \"text\" (string) Verification or "
"signing error related to the input\n"
" }\n"
" ,...\n"
" ]\n"
"}\n"
"\nExamples:\n" +
HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"") +
HelpExampleRpc("signrawtransactionwithwallet", "\"myhex\""));
}
RPCTypeCheck(request.params,
{UniValue::VSTR, UniValue::VARR, UniValue::VSTR}, true);
CMutableTransaction mtx;
if (!DecodeHexTx(mtx, request.params[0].get_str())) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
}
// Sign the transaction
LOCK2(cs_main, pwallet->cs_wallet);
return SignTransaction(mtx, request.params[1], pwallet, false,
request.params[2]);
}
UniValue generate(const Config &config, const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() < 1 ||
request.params.size() > 2) {
throw std::runtime_error(
"generate nblocks ( maxtries )\n"
"\nMine up to nblocks blocks immediately (before the RPC call "
"returns) to an address in the wallet.\n"
"\nArguments:\n"
"1. nblocks (numeric, required) How many blocks are generated "
"immediately.\n"
"2. maxtries (numeric, optional) How many iterations to try "
"(default = 1000000).\n"
"\nResult:\n"
"[ blockhashes ] (array) hashes of blocks generated\n"
"\nExamples:\n"
"\nGenerate 11 blocks\n" +
HelpExampleCli("generate", "11"));
}
int num_generate = request.params[0].get_int();
uint64_t max_tries = 1000000;
if (!request.params[1].isNull()) {
max_tries = request.params[1].get_int();
}
std::shared_ptr<CReserveScript> coinbase_script;
pwallet->GetScriptForMining(coinbase_script);
// If the keypool is exhausted, no script is returned at all. Catch this.
if (!coinbase_script) {
throw JSONRPCError(
RPC_WALLET_KEYPOOL_RAN_OUT,
"Error: Keypool ran out, please call keypoolrefill first");
}
// throw an error if no script was provided
if (coinbase_script->reserveScript.empty()) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "No coinbase script available");
}
return generateBlocks(config, coinbase_script, num_generate, max_tries,
true);
}
UniValue rescanblockchain(const Config &config, const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() > 2) {
throw std::runtime_error(
"rescanblockchain (\"start_height\") (\"stop_height\")\n"
"\nRescan the local blockchain for wallet related transactions.\n"
"\nArguments:\n"
"1. \"start_height\" (numeric, optional) block height where the "
"rescan should start\n"
"2. \"stop_height\" (numeric, optional) the last block height "
"that should be scanned\n"
"\nResult:\n"
"{\n"
" \"start_height\" (numeric) The block height where the "
"rescan has started. If omitted, rescan started from the genesis "
"block.\n"
" \"stop_height\" (numeric) The height of the last rescanned "
"block. If omitted, rescan stopped at the chain tip.\n"
"}\n"
"\nExamples:\n" +
HelpExampleCli("rescanblockchain", "100000 120000") +
HelpExampleRpc("rescanblockchain", "100000 120000"));
}
WalletRescanReserver reserver(pwallet);
if (!reserver.reserve()) {
throw JSONRPCError(
RPC_WALLET_ERROR,
"Wallet is currently rescanning. Abort existing rescan or wait.");
}
CBlockIndex *pindexStart = nullptr;
CBlockIndex *pindexStop = nullptr;
CBlockIndex *pChainTip = nullptr;
{
LOCK(cs_main);
pindexStart = chainActive.Genesis();
pChainTip = chainActive.Tip();
if (!request.params[0].isNull()) {
pindexStart = chainActive[request.params[0].get_int()];
if (!pindexStart) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Invalid start_height");
}
}
if (!request.params[1].isNull()) {
pindexStop = chainActive[request.params[1].get_int()];
if (!pindexStop) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Invalid stop_height");
} else if (pindexStop->nHeight < pindexStart->nHeight) {
throw JSONRPCError(
RPC_INVALID_PARAMETER,
"stop_height must be greater than start_height");
}
}
}
// We can't rescan beyond non-pruned blocks, stop and throw an error
if (fPruneMode) {
LOCK(cs_main);
CBlockIndex *block = pindexStop ? pindexStop : pChainTip;
while (block && block->nHeight >= pindexStart->nHeight) {
if (!block->nStatus.hasData()) {
throw JSONRPCError(RPC_MISC_ERROR,
"Can't rescan beyond pruned data. Use RPC "
"call getblockchaininfo to determine your "
"pruned height.");
}
block = block->pprev;
}
}
CBlockIndex *stopBlock = pwallet->ScanForWalletTransactions(
pindexStart, pindexStop, reserver, true);
if (!stopBlock) {
if (pwallet->IsAbortingRescan()) {
throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted.");
}
// if we got a nullptr returned, ScanForWalletTransactions did rescan up
// to the requested stopindex
stopBlock = pindexStop ? pindexStop : pChainTip;
} else {
throw JSONRPCError(RPC_MISC_ERROR,
"Rescan failed. Potentially corrupted data files.");
}
UniValue response(UniValue::VOBJ);
response.pushKV("start_height", pindexStart->nHeight);
response.pushKV("stop_height", stopBlock->nHeight);
return response;
}
class DescribeWalletAddressVisitor : public boost::static_visitor<UniValue> {
public:
CWallet *const pwallet;
void ProcessSubScript(const CScript &subscript, UniValue &obj,
bool include_addresses = false) const {
// Always present: script type and redeemscript
txnouttype which_type;
std::vector<std::vector<uint8_t>> solutions_data;
Solver(subscript, which_type, solutions_data);
obj.pushKV("script", GetTxnOutputType(which_type));
obj.pushKV("hex", HexStr(subscript.begin(), subscript.end()));
CTxDestination embedded;
UniValue a(UniValue::VARR);
if (ExtractDestination(subscript, embedded)) {
// Only when the script corresponds to an address.
UniValue subobj(UniValue::VOBJ);
UniValue detail = DescribeAddress(embedded);
subobj.pushKVs(detail);
UniValue wallet_detail = boost::apply_visitor(*this, embedded);
subobj.pushKVs(wallet_detail);
subobj.pushKV("address", EncodeDestination(embedded, GetConfig()));
subobj.pushKV("scriptPubKey",
HexStr(subscript.begin(), subscript.end()));
// Always report the pubkey at the top level, so that
// `getnewaddress()['pubkey']` always works.
if (subobj.exists("pubkey")) {
obj.pushKV("pubkey", subobj["pubkey"]);
}
obj.pushKV("embedded", std::move(subobj));
if (include_addresses) {
a.push_back(EncodeDestination(embedded, GetConfig()));
}
} else if (which_type == TX_MULTISIG) {
// Also report some information on multisig scripts (which do not
// have a corresponding address).
// TODO: abstract out the common functionality between this logic
// and ExtractDestinations.
obj.pushKV("sigsrequired", solutions_data[0][0]);
UniValue pubkeys(UniValue::VARR);
for (size_t i = 1; i < solutions_data.size() - 1; ++i) {
CPubKey key(solutions_data[i].begin(), solutions_data[i].end());
if (include_addresses) {
a.push_back(EncodeDestination(key.GetID(), GetConfig()));
}
pubkeys.push_back(HexStr(key.begin(), key.end()));
}
obj.pushKV("pubkeys", std::move(pubkeys));
}
// The "addresses" field is confusing because it refers to public keys
// using their P2PKH address. For that reason, only add the 'addresses'
// field when needed for backward compatibility. New applications can
// use the 'pubkeys' field for inspecting multisig participants.
if (include_addresses) {
obj.pushKV("addresses", std::move(a));
}
}
explicit DescribeWalletAddressVisitor(CWallet *_pwallet)
: pwallet(_pwallet) {}
UniValue operator()(const CNoDestination &dest) const {
return UniValue(UniValue::VOBJ);
}
UniValue operator()(const CKeyID &keyID) const {
UniValue obj(UniValue::VOBJ);
CPubKey vchPubKey;
if (pwallet && pwallet->GetPubKey(keyID, vchPubKey)) {
obj.pushKV("pubkey", HexStr(vchPubKey));
obj.pushKV("iscompressed", vchPubKey.IsCompressed());
}
return obj;
}
UniValue operator()(const CScriptID &scriptID) const {
UniValue obj(UniValue::VOBJ);
CScript subscript;
if (pwallet && pwallet->GetCScript(scriptID, subscript)) {
ProcessSubScript(subscript, obj, true);
}
return obj;
}
};
static UniValue DescribeWalletAddress(CWallet *pwallet,
const CTxDestination &dest) {
UniValue ret(UniValue::VOBJ);
UniValue detail = DescribeAddress(dest);
ret.pushKVs(detail);
ret.pushKVs(
boost::apply_visitor(DescribeWalletAddressVisitor(pwallet), dest));
return ret;
}
/** Convert CAddressBookData to JSON record. */
static UniValue AddressBookDataToJSON(const CAddressBookData &data,
const bool verbose) {
UniValue ret(UniValue::VOBJ);
if (verbose) {
ret.pushKV("name", data.name);
}
ret.pushKV("purpose", data.purpose);
return ret;
}
UniValue getaddressinfo(const Config &config, const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"getaddressinfo \"address\"\n"
"\nReturn information about the given bitcoin address. Some "
"information requires the address\n"
"to be in the wallet.\n"
"\nArguments:\n"
"1. \"address\" (string, required) The bitcoin "
"address to get the information of.\n"
"\nResult:\n"
"{\n"
" \"address\" : \"address\", (string) The bitcoin address "
"validated\n"
" \"scriptPubKey\" : \"hex\", (string) The hex encoded "
"scriptPubKey generated by the address\n"
" \"ismine\" : true|false, (boolean) If the address is "
"yours or not\n"
" \"iswatchonly\" : true|false, (boolean) If the address is "
"watchonly\n"
" \"isscript\" : true|false, (boolean) If the key is a "
"script\n"
" \"script\" : \"type\" (string, optional) The output "
"script type. Only if \"isscript\" is true and the redeemscript is "
"known. Possible types: nonstandard, pubkey, pubkeyhash, "
"scripthash, multisig, nulldata\n"
" \"hex\" : \"hex\", (string, optional) The "
"redeemscript for the p2sh address\n"
" \"pubkeys\" (string, optional) Array of "
"pubkeys associated with the known redeemscript (only if "
"\"script\" is \"multisig\")\n"
" [\n"
" \"pubkey\"\n"
" ,...\n"
" ]\n"
" \"sigsrequired\" : xxxxx (numeric, optional) Number of "
"signatures required to spend multisig output (only if \"script\" "
"is \"multisig\")\n"
" \"pubkey\" : \"publickeyhex\", (string, optional) The hex "
"value of the raw public key, for single-key addresses (possibly "
"embedded in P2SH or P2WSH)\n"
" \"embedded\" : {...}, (object, optional) Information "
"about the address embedded in P2SH or P2WSH, if relevant and "
"known. It includes all getaddressinfo output fields for the "
"embedded address, excluding metadata (\"timestamp\", "
"\"hdkeypath\", \"hdseedid\") and relation to the wallet "
"(\"ismine\", \"iswatchonly\", \"account\").\n"
" \"iscompressed\" : true|false, (boolean) If the address is "
"compressed\n"
" \"label\" : \"label\" (string) The label associated "
"with the address, \"\" is the default account\n"
" \"account\" : \"account\" (string) DEPRECATED. This "
"field will be removed in V0.21. To see this deprecated field, "
"start bitcoind with -deprecatedrpc=accounts. The account "
"associated with the address, \"\" is the default account\n"
" \"timestamp\" : timestamp, (number, optional) The creation "
"time of the key if available in seconds since epoch (Jan 1 1970 "
"GMT)\n"
" \"hdkeypath\" : \"keypath\" (string, optional) The HD "
"keypath if the key is HD and available\n"
" \"hdseedid\" : \"<hash160>\" (string, optional) The "
"Hash160 of the HD seed\n"
" \"hdmasterkeyid\" : \"<hash160>\" (string, optional) alias for "
"hdseedid maintained for backwards compatibility. Will be removed "
"in V0.21.\n"
" \"labels\" (object) Array of labels "
"associated with the address.\n"
" [\n"
" { (json object of label data)\n"
" \"name\": \"labelname\" (string) The label\n"
" \"purpose\": \"string\" (string) Purpose of address "
"(\"send\" for sending address, \"receive\" for receiving "
"address)\n"
" },...\n"
" ]\n"
"}\n"
"\nExamples:\n" +
HelpExampleCli("getaddressinfo",
"\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"") +
HelpExampleRpc("getaddressinfo",
"\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\""));
}
LOCK(pwallet->cs_wallet);
UniValue ret(UniValue::VOBJ);
CTxDestination dest =
DecodeDestination(request.params[0].get_str(), config.GetChainParams());
// Make sure the destination is valid
if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
}
std::string currentAddress = EncodeDestination(dest, config);
ret.pushKV("address", currentAddress);
CScript scriptPubKey = GetScriptForDestination(dest);
ret.pushKV("scriptPubKey",
HexStr(scriptPubKey.begin(), scriptPubKey.end()));
isminetype mine = IsMine(*pwallet, dest);
ret.pushKV("ismine", bool(mine & ISMINE_SPENDABLE));
ret.pushKV("iswatchonly", bool(mine & ISMINE_WATCH_ONLY));
UniValue detail = DescribeWalletAddress(pwallet, dest);
ret.pushKVs(detail);
if (pwallet->mapAddressBook.count(dest)) {
ret.pushKV("label", pwallet->mapAddressBook[dest].name);
if (IsDeprecatedRPCEnabled(gArgs, "accounts")) {
ret.pushKV("account", pwallet->mapAddressBook[dest].name);
}
}
const CKeyMetadata *meta = nullptr;
CKeyID key_id = GetKeyForDestination(*pwallet, dest);
if (!key_id.IsNull()) {
auto it = pwallet->mapKeyMetadata.find(key_id);
if (it != pwallet->mapKeyMetadata.end()) {
meta = &it->second;
}
}
if (!meta) {
auto it = pwallet->m_script_metadata.find(CScriptID(scriptPubKey));
if (it != pwallet->m_script_metadata.end()) {
meta = &it->second;
}
}
if (meta) {
ret.pushKV("timestamp", meta->nCreateTime);
if (!meta->hdKeypath.empty()) {
ret.pushKV("hdkeypath", meta->hdKeypath);
ret.pushKV("hdseedid", meta->hd_seed_id.GetHex());
ret.pushKV("hdmasterkeyid", meta->hd_seed_id.GetHex());
}
}
// Currently only one label can be associated with an address, return an
// array so the API remains stable if we allow multiple labels to be
// associated with an address.
UniValue labels(UniValue::VARR);
std::map<CTxDestination, CAddressBookData>::iterator mi =
pwallet->mapAddressBook.find(dest);
if (mi != pwallet->mapAddressBook.end()) {
labels.push_back(AddressBookDataToJSON(mi->second, true));
}
ret.pushKV("labels", std::move(labels));
return ret;
}
UniValue getaddressesbylabel(const Config &config,
const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
"getaddressesbylabel \"label\"\n"
"\nReturns the list of addresses assigned the specified label.\n"
"\nArguments:\n"
"1. \"label\" (string, required) The label.\n"
"\nResult:\n"
"{ (json object with addresses as keys)\n"
" \"address\": { (json object with information about address)\n"
" \"purpose\": \"string\" (string) Purpose of address "
"(\"send\" for sending address, \"receive\" for receiving "
"address)\n"
" },...\n"
"}\n"
"\nExamples:\n" +
HelpExampleCli("getaddressesbylabel", "\"tabby\"") +
HelpExampleRpc("getaddressesbylabel", "\"tabby\""));
LOCK(pwallet->cs_wallet);
std::string label = LabelFromValue(request.params[0]);
// Find all addresses that have the given label
UniValue ret(UniValue::VOBJ);
for (const std::pair<const CTxDestination, CAddressBookData> &item :
pwallet->mapAddressBook) {
if (item.second.name == label) {
ret.pushKV(EncodeDestination(item.first, config),
AddressBookDataToJSON(item.second, false));
}
}
if (ret.empty()) {
throw JSONRPCError(RPC_WALLET_INVALID_LABEL_NAME,
std::string("No addresses with label " + label));
}
return ret;
}
UniValue listlabels(const Config &config, const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() > 1)
throw std::runtime_error(
"listlabels ( \"purpose\" )\n"
"\nReturns the list of all labels, or labels that are assigned to "
"addresses with a specific purpose.\n"
"\nArguments:\n"
"1. \"purpose\" (string, optional) Address purpose to list "
"labels for ('send','receive'). An empty string is the same as not "
"providing this argument.\n"
"\nResult:\n"
"[ (json array of string)\n"
" \"label\", (string) Label name\n"
" ...\n"
"]\n"
"\nExamples:\n"
"\nList all labels\n" +
HelpExampleCli("listlabels", "") +
"\nList labels that have receiving addresses\n" +
HelpExampleCli("listlabels", "receive") +
"\nList labels that have sending addresses\n" +
HelpExampleCli("listlabels", "send") + "\nAs json rpc call\n" +
HelpExampleRpc("listlabels", "receive"));
LOCK(pwallet->cs_wallet);
std::string purpose;
if (!request.params[0].isNull()) {
purpose = request.params[0].get_str();
}
// Add to a set to sort by label name, then insert into Univalue array
std::set<std::string> label_set;
for (const std::pair<const CTxDestination, CAddressBookData> &entry :
pwallet->mapAddressBook) {
if (purpose.empty() || entry.second.purpose == purpose) {
label_set.insert(entry.second.name);
}
}
UniValue ret(UniValue::VARR);
for (const std::string &name : label_set) {
ret.push_back(name);
}
return ret;
}
static UniValue sethdseed(const Config &config, const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() > 2) {
throw std::runtime_error(
"sethdseed ( \"newkeypool\" \"seed\" )\n"
"\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.\n" +
HelpRequiringPassphrase(pwallet) +
"\nArguments:\n"
"1. \"newkeypool\" (boolean, optional, 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.\n"
"2. \"seed\" (string, optional) The WIF private key "
"to use as the new HD seed; if not provided a random seed will be "
"used.\n"
" The seed value can be retrieved "
"using the dumpwallet command. It is the private key marked "
"hdseed=1\n"
"\nExamples:\n" +
HelpExampleCli("sethdseed", "") +
HelpExampleCli("sethdseed", "false") +
HelpExampleCli("sethdseed", "true \"wifkey\"") +
HelpExampleRpc("sethdseed", "true, \"wifkey\""));
}
if (IsInitialBlockDownload()) {
throw JSONRPCError(
RPC_CLIENT_IN_INITIAL_DOWNLOAD,
"Cannot set a new HD seed while still in Initial Block Download");
}
LOCK2(cs_main, pwallet->cs_wallet);
// Do not do anything to non-HD wallets
if (!pwallet->IsHDEnabled()) {
throw JSONRPCError(
RPC_WALLET_ERROR,
"Cannot set a HD seed on a non-HD wallet. Start with "
"-upgradewallet 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 = pwallet->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(*pwallet, 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 = pwallet->DeriveNewSeed(key);
}
pwallet->SetHDSeed(master_pub_key);
if (flush_key_pool) {
pwallet->NewKeyPool();
}
return NullUniValue;
}
static bool ParseHDKeypath(std::string keypath_str,
std::vector<uint32_t> &keypath) {
std::stringstream ss(keypath_str);
std::string item;
bool first = true;
while (std::getline(ss, item, '/')) {
if (item.compare("m") == 0) {
if (first) {
first = false;
continue;
}
return false;
}
// Finds whether it is hardened
uint32_t path = 0;
size_t pos = item.find("'");
if (pos != std::string::npos) {
// The hardened tick can only be in the last index of the string
if (pos != item.size() - 1) {
return false;
}
path |= 0x80000000;
// Drop the last character which is the hardened tick
item = item.substr(0, item.size() - 1);
}
// Ensure this is only numbers
if (item.find_first_not_of("0123456789") != std::string::npos) {
return false;
}
uint32_t number;
ParseUInt32(item, &number);
path |= number;
keypath.push_back(path);
first = false;
}
return true;
}
static void
AddKeypathToMap(const CWallet *pwallet, const CKeyID &keyID,
std::map<CPubKey, std::vector<uint32_t>> &hd_keypaths) {
CPubKey vchPubKey;
if (!pwallet->GetPubKey(keyID, vchPubKey)) {
return;
}
CKeyMetadata meta;
auto it = pwallet->mapKeyMetadata.find(keyID);
if (it != pwallet->mapKeyMetadata.end()) {
meta = it->second;
}
std::vector<uint32_t> keypath;
if (!meta.hdKeypath.empty()) {
if (!ParseHDKeypath(meta.hdKeypath, keypath)) {
throw JSONRPCError(RPC_INTERNAL_ERROR,
"Internal keypath is broken");
}
// Get the proper master key id
CKey key;
pwallet->GetKey(meta.hd_seed_id, key);
CExtKey masterKey;
masterKey.SetSeed(key.begin(), key.size());
// Add to map
keypath.insert(keypath.begin(),
ReadLE32(masterKey.key.GetPubKey().GetID().begin()));
} else {
// Single pubkeys get the master fingerprint of themselves
keypath.insert(keypath.begin(), ReadLE32(vchPubKey.GetID().begin()));
}
hd_keypaths.emplace(vchPubKey, keypath);
}
bool FillPSBT(const CWallet *pwallet, PartiallySignedTransaction &psbtx,
const CTransaction *txConst, SigHashType sighash_type, bool sign,
bool bip32derivs) {
LOCK(pwallet->cs_wallet);
// Get all of the previous transactions
bool complete = true;
for (size_t i = 0; i < txConst->vin.size(); ++i) {
const CTxIn &txin = txConst->vin[i];
PSBTInput &input = psbtx.inputs.at(i);
// If we don't know about this input, skip it and let someone else deal
// with it
const TxId &txid = txin.prevout.GetTxId();
const auto &it = pwallet->mapWallet.find(txid);
if (it != pwallet->mapWallet.end()) {
const CWalletTx &wtx = it->second;
CTxOut utxo = wtx.tx->vout[txin.prevout.GetN()];
input.utxo = utxo;
}
// Get the Sighash type
if (sign && input.sighash_type.getRawSigHashType() > 0 &&
input.sighash_type != sighash_type) {
throw JSONRPCError(
RPC_DESERIALIZATION_ERROR,
"Specified sighash and sighash in PSBT do not match.");
}
SignatureData sigdata;
if (sign) {
complete &= SignPSBTInput(*pwallet, *psbtx.tx, input, sigdata, i,
sighash_type);
} else {
complete &=
SignPSBTInput(PublicOnlySigningProvider(pwallet), *psbtx.tx,
input, sigdata, i, sighash_type);
}
// Get public key paths
if (bip32derivs) {
for (const auto &pubkey_it : sigdata.misc_pubkeys) {
AddKeypathToMap(pwallet, pubkey_it.first, input.hd_keypaths);
}
}
}
// Fill in the bip32 keypaths and redeemscripts for the outputs so that
// hardware wallets can identify change
for (size_t i = 0; i < txConst->vout.size(); ++i) {
const CTxOut &out = txConst->vout.at(i);
PSBTOutput &psbt_out = psbtx.outputs.at(i);
// Dummy tx so we can use ProduceSignature to get stuff out
CMutableTransaction dummy_tx;
dummy_tx.vin.push_back(CTxIn());
dummy_tx.vout.push_back(CTxOut());
// Fill a SignatureData with output info
SignatureData sigdata;
psbt_out.FillSignatureData(sigdata);
MutableTransactionSignatureCreator creator(
psbtx.tx.get_ptr(), 0, out.nValue, SigHashType().withForkId());
ProduceSignature(*pwallet, creator, out.scriptPubKey, sigdata);
psbt_out.FromSignatureData(sigdata);
// Get public key paths
if (bip32derivs) {
for (const auto &pubkey_it : sigdata.misc_pubkeys) {
AddKeypathToMap(pwallet, pubkey_it.first, psbt_out.hd_keypaths);
}
}
}
return complete;
}
static UniValue walletprocesspsbt(const Config &config,
const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() < 1 ||
request.params.size() > 4) {
throw std::runtime_error(
"walletprocesspsbt \"psbt\" ( sign \"sighashtype\" bip32derivs )\n"
"\nUpdate a PSBT with input information from our wallet and then "
"sign inputs\n"
"that we can sign for.\n" +
HelpRequiringPassphrase(pwallet) +
"\n"
"\nArguments:\n"
"1. \"psbt\" (string, required) The "
"transaction base64 string\n"
"2. sign (boolean, optional, "
"default=true) Also sign the transaction when updating\n"
"3. \"sighashtype\" (string, optional, "
"default=ALL|FORKID) The signature hash type to sign with if not "
"specified by the PSBT. Must be one of\n"
" \"ALL|FORKID\"\n"
" \"NONE|FORKID\"\n"
" \"SINGLE|FORKID\"\n"
" \"ALL|FORKID|ANYONECANPAY\"\n"
" \"NONE|FORKID|ANYONECANPAY\"\n"
" \"SINGLE|FORKID|ANYONECANPAY\"\n"
"4. bip32derivs (boolean, optiona, "
"default=false) If true, includes the BIP 32 derivation paths for "
"public keys if we know them\n"
"\nResult:\n"
"{\n"
" \"psbt\" : \"value\", (string) The base64-encoded "
"partially signed transaction\n"
" \"complete\" : true|false, (boolean) If the transaction has a "
"complete set of signatures\n"
" ]\n"
"}\n"
"\nExamples:\n" +
HelpExampleCli("walletprocesspsbt", "\"psbt\""));
}
RPCTypeCheck(request.params,
{UniValue::VSTR, UniValue::VBOOL, UniValue::VSTR});
// Unserialize the transaction
PartiallySignedTransaction psbtx;
std::string error;
if (!DecodePSBT(psbtx, request.params[0].get_str(), error)) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR,
strprintf("TX decode failed %s", error));
}
// Get the sighash type
SigHashType nHashType = ParseSighashString(request.params[2]);
if (!nHashType.hasForkId()) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Signature must use SIGHASH_FORKID");
}
// Use CTransaction for the constant parts of the
// transaction to avoid rehashing.
const CTransaction txConst(*psbtx.tx);
// Fill transaction with our data and also sign
bool sign =
request.params[1].isNull() ? true : request.params[1].get_bool();
bool bip32derivs =
request.params[3].isNull() ? false : request.params[3].get_bool();
bool complete =
FillPSBT(pwallet, psbtx, &txConst, nHashType, sign, bip32derivs);
UniValue result(UniValue::VOBJ);
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << psbtx;
result.pushKV("psbt", EncodeBase64((uint8_t *)ssTx.data(), ssTx.size()));
result.pushKV("complete", complete);
return result;
}
static UniValue walletcreatefundedpsbt(const Config &config,
const JSONRPCRequest &request) {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() < 2 ||
request.params.size() > 5) {
throw std::runtime_error(
"walletcreatefundedpsbt [{\"txid\":\"id\",\"vout\":n},...] "
"[{\"address\":amount},{\"data\":\"hex\"},...] ( locktime ) ( "
"options bip32derivs )\n"
"\nCreates and funds a transaction in the Partially Signed "
"Transaction format. Inputs will be added if supplied inputs are "
"not enough\n"
"Implements the Creator and Updater roles.\n"
"\nArguments:\n"
"1. \"inputs\" (array, required) A json array of "
"json objects\n"
" [\n"
" {\n"
" \"txid\":\"id\", (string, required) The transaction "
"id\n"
" \"vout\":n, (numeric, required) The output "
"number\n"
" \"sequence\":n (numeric, optional) The sequence "
"number\n"
" } \n"
" ,...\n"
" ]\n"
"2. \"outputs\" (array, required) a json array with "
"outputs (key-value pairs)\n"
" [\n"
" {\n"
" \"address\": x.xxx, (obj, optional) A key-value pair. "
"The key (string) is the bitcoin address, the value (float or "
"string) is the amount in " +
CURRENCY_UNIT +
"\n"
" },\n"
" {\n"
" \"data\": \"hex\" (obj, optional) A key-value pair. "
"The key must be \"data\", the value is hex encoded data\n"
" }\n"
" ,... More key-value pairs of the above "
"form. For compatibility reasons, a dictionary, which holds the "
"key-value pairs directly, is also\n"
" accepted as second parameter.\n"
" ]\n"
"3. locktime (numeric, optional, default=0) Raw "
"locktime. Non-0 value also locktime-activates inputs\n"
"4. options (object, optional)\n"
" {\n"
" \"changeAddress\" (string, optional, default pool "
"address) The bitcoin address to receive the change\n"
" \"changePosition\" (numeric, optional, default "
"random) The index of the change output\n"
" \"change_type\" (string, optional) The output "
"type to use. Only valid if changeAddress is not specified. "
"Options are \"legacy\", \"p2sh-segwit\", and \"bech32\". Default "
"is set by -changetype.\n"
" \"includeWatching\" (boolean, optional, default "
"false) Also select inputs which are watch only\n"
" \"lockUnspents\" (boolean, optional, default "
"false) Lock selected unspent outputs\n"
" \"feeRate\" (numeric, optional, default not "
"set: makes wallet determine the fee) Set a specific fee rate in " +
CURRENCY_UNIT +
"/kB\n"
" \"subtractFeeFromOutputs\" (array, optional) A json array of "
"integers.\n"
" The fee will be equally deducted "
"from the amount of each specified output.\n"
" The outputs are specified by their "
"zero-based index, before any change output is added.\n"
" Those recipients will receive less "
"bitcoins than you enter in their corresponding amount field.\n"
" If no outputs are specified here, "
"the sender pays the fee.\n"
" [vout_index,...]\n"
" }\n"
"5. bip32derivs (boolean, optiona, "
"default=false) If true, includes the BIP 32 derivation paths for "
"public keys if we know them\n"
"\nResult:\n"
"{\n"
" \"psbt\": \"value\", (string) The resulting raw "
"transaction (base64-encoded string)\n"
" \"fee\": n, (numeric) Fee in " +
CURRENCY_UNIT +
" the resulting transaction pays\n"
" \"changepos\": n (numeric) The position of the added "
"change output, or -1\n"
"}\n"
"\nExamples:\n"
"\nCreate a transaction with no inputs\n" +
HelpExampleCli("walletcreatefundedpsbt",
"\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" "
"\"[{\\\"data\\\":\\\"00010203\\\"}]\""));
}
RPCTypeCheck(request.params,
{UniValue::VARR,
UniValueType(), // ARR or OBJ, checked later
UniValue::VNUM, UniValue::VOBJ},
true);
Amount fee;
int change_position;
CMutableTransaction rawTx =
ConstructTransaction(config.GetChainParams(), request.params[0],
request.params[1], request.params[2]);
FundTransaction(pwallet, rawTx, fee, change_position, request.params[3]);
// Make a blank psbt
PartiallySignedTransaction psbtx;
psbtx.tx = rawTx;
for (size_t i = 0; i < rawTx.vin.size(); ++i) {
psbtx.inputs.push_back(PSBTInput());
}
for (size_t i = 0; i < rawTx.vout.size(); ++i) {
psbtx.outputs.push_back(PSBTOutput());
}
// Use CTransaction for the constant parts of the
// transaction to avoid rehashing.
const CTransaction txConst(*psbtx.tx);
// Fill transaction with out data but don't sign
bool bip32derivs =
request.params[4].isNull() ? false : request.params[4].get_bool();
FillPSBT(pwallet, psbtx, &txConst, SigHashType().withForkId(), false,
bip32derivs);
// Serialize the PSBT
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << psbtx;
UniValue result(UniValue::VOBJ);
result.pushKV("psbt", EncodeBase64((uint8_t *)ssTx.data(), ssTx.size()));
result.pushKV("fee", ValueFromAmount(fee));
result.pushKV("changepos", change_position);
return result;
}
// clang-format off
static const ContextFreeRPCCommand commands[] = {
// category name actor (function) argNames
// ------------------- ------------------------ ---------------------- ----------
{ "rawtransactions", "fundrawtransaction", fundrawtransaction, {"hexstring","options"} },
{ "wallet", "walletprocesspsbt", walletprocesspsbt, {"psbt","sign","sighashtype","bip32derivs"} },
{ "wallet", "walletcreatefundedpsbt", walletcreatefundedpsbt, {"inputs","outputs","locktime","options","bip32derivs"} },
{ "hidden", "resendwallettransactions", resendwallettransactions, {} },
{ "wallet", "abandontransaction", abandontransaction, {"txid"} },
{ "wallet", "addmultisigaddress", addmultisigaddress, {"nrequired","keys","label|account"} },
{ "wallet", "backupwallet", backupwallet, {"destination"} },
{ "wallet", "createwallet", createwallet, {"wallet_name"} },
{ "wallet", "encryptwallet", encryptwallet, {"passphrase"} },
{ "wallet", "getaddressinfo", getaddressinfo, {"address"} },
{ "wallet", "getbalance", getbalance, {"account","minconf","include_watchonly"} },
{ "wallet", "getnewaddress", getnewaddress, {"label|account", "address_type"} },
{ "wallet", "getrawchangeaddress", getrawchangeaddress, {"address_type"} },
{ "wallet", "getreceivedbyaddress", getreceivedbyaddress, {"address","minconf"} },
{ "wallet", "gettransaction", gettransaction, {"txid","include_watchonly"} },
{ "wallet", "getunconfirmedbalance", getunconfirmedbalance, {} },
{ "wallet", "getwalletinfo", getwalletinfo, {} },
{ "wallet", "keypoolrefill", keypoolrefill, {"newsize"} },
{ "wallet", "listaddressgroupings", listaddressgroupings, {} },
{ "wallet", "listlockunspent", listlockunspent, {} },
{ "wallet", "listreceivedbyaddress", listreceivedbyaddress, {"minconf","include_empty","include_watchonly","address_filter"} },
{ "wallet", "listsinceblock", listsinceblock, {"blockhash","target_confirmations","include_watchonly","include_removed"} },
{ "wallet", "listtransactions", listtransactions, {"account|dummy","count","skip","include_watchonly"} },
{ "wallet", "listunspent", listunspent, {"minconf","maxconf","addresses","include_unsafe","query_options"} },
{ "wallet", "listwallets", listwallets, {} },
{ "wallet", "loadwallet", loadwallet, {"filename"} },
{ "wallet", "lockunspent", lockunspent, {"unlock","transactions"} },
{ "wallet", "rescanblockchain", rescanblockchain, {"start_height", "stop_height"} },
{ "wallet", "sethdseed", sethdseed, {"newkeypool","seed"} },
{ "wallet", "sendfrom", sendfrom, {"fromaccount","toaddress","amount","minconf","comment","comment_to"} },
{ "wallet", "sendmany", sendmany, {"fromaccount|dummy","amounts","minconf","comment","subtractfeefrom"} },
{ "wallet", "sendtoaddress", sendtoaddress, {"address","amount","comment","comment_to","subtractfeefromamount"} },
{ "wallet", "settxfee", settxfee, {"amount"} },
{ "wallet", "signmessage", signmessage, {"address","message"} },
{ "wallet", "signrawtransactionwithwallet", signrawtransactionwithwallet, {"hextring","prevtxs","sighashtype"} },
{ "wallet", "unloadwallet", unloadwallet, {"wallet_name"} },
{ "wallet", "walletlock", walletlock, {} },
{ "wallet", "walletpassphrasechange", walletpassphrasechange, {"oldpassphrase","newpassphrase"} },
{ "wallet", "walletpassphrase", walletpassphrase, {"passphrase","timeout"} },
/** Account functions (deprecated) */
{ "wallet", "getaccountaddress", getlabeladdress, {"account"} },
{ "wallet", "getaccount", getaccount, {"address"} },
{ "wallet", "getaddressesbyaccount", getaddressesbyaccount, {"account"} },
{ "wallet", "getreceivedbyaccount", getreceivedbylabel, {"account","minconf"} },
{ "wallet", "listaccounts", listaccounts, {"minconf","include_watchonly"} },
{ "wallet", "listreceivedbyaccount", listreceivedbylabel, {"minconf","include_empty","include_watchonly"} },
{ "wallet", "setaccount", setlabel, {"address","account"} },
{ "wallet", "move", movecmd, {"fromaccount","toaccount","amount","minconf","comment"} },
/** Label functions (to replace non-balance account functions) */
{ "wallet", "getlabeladdress", getlabeladdress, {"label","force"} },
{ "wallet", "getaddressesbylabel", getaddressesbylabel, {"label"} },
{ "wallet", "getreceivedbylabel", getreceivedbylabel, {"label","minconf"} },
{ "wallet", "listlabels", listlabels, {"purpose"} },
{ "wallet", "listreceivedbylabel", listreceivedbylabel, {"minconf","include_empty","include_watchonly"} },
{ "wallet", "setlabel", setlabel, {"address","label"} },
{ "generating", "generate", generate, {"nblocks","maxtries"} },
};
// clang-format on
void RegisterWalletRPCCommands(CRPCTable &t) {
for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) {
t.appendCommand(commands[vcidx].name, &commands[vcidx]);
}
}
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index d0b4bd507..dc4bbe91d 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -1,4871 +1,4828 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2016 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 <wallet/wallet.h>
#include <chain.h>
#include <checkpoints.h>
#include <config.h>
#include <consensus/consensus.h>
#include <consensus/validation.h>
#include <fs.h>
#include <init.h>
#include <key.h>
#include <key_io.h>
#include <keystore.h>
#include <net.h>
#include <policy/policy.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <random.h>
#include <script/script.h>
#include <script/sighashtype.h>
#include <script/sign.h>
#include <timedata.h>
#include <txmempool.h>
#include <ui_interface.h>
#include <util/moneystr.h>
#include <util/system.h>
#include <validation.h>
#include <wallet/coincontrol.h>
#include <wallet/coinselection.h>
#include <wallet/fees.h>
#include <wallet/finaltx.h>
#include <wallet/walletutil.h>
#include <boost/algorithm/string/replace.hpp>
#include <algorithm>
#include <cassert>
#include <future>
static CCriticalSection cs_wallets;
static std::vector<std::shared_ptr<CWallet>> vpwallets GUARDED_BY(cs_wallets);
bool AddWallet(const std::shared_ptr<CWallet> &wallet) {
LOCK(cs_wallets);
assert(wallet);
std::vector<std::shared_ptr<CWallet>>::const_iterator i =
std::find(vpwallets.begin(), vpwallets.end(), wallet);
if (i != vpwallets.end()) {
return false;
}
vpwallets.push_back(wallet);
return true;
}
bool RemoveWallet(const std::shared_ptr<CWallet> &wallet) {
LOCK(cs_wallets);
assert(wallet);
std::vector<std::shared_ptr<CWallet>>::iterator i =
std::find(vpwallets.begin(), vpwallets.end(), wallet);
if (i == vpwallets.end()) {
return false;
}
vpwallets.erase(i);
return true;
}
bool HasWallets() {
LOCK(cs_wallets);
return !vpwallets.empty();
}
std::vector<std::shared_ptr<CWallet>> GetWallets() {
LOCK(cs_wallets);
return vpwallets;
}
std::shared_ptr<CWallet> GetWallet(const std::string &name) {
LOCK(cs_wallets);
for (const std::shared_ptr<CWallet> &wallet : vpwallets) {
if (wallet->GetName() == name) {
return wallet;
}
}
return nullptr;
}
// Custom deleter for shared_ptr<CWallet>.
static void ReleaseWallet(CWallet *wallet) {
LogPrintf("Releasing wallet %s\n", wallet->GetName());
wallet->BlockUntilSyncedToCurrentChain();
wallet->Flush();
delete wallet;
}
static const size_t OUTPUT_GROUP_MAX_ENTRIES = 10;
const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000;
const uint256 CMerkleTx::ABANDON_HASH(uint256S(
"0000000000000000000000000000000000000000000000000000000000000001"));
/** @defgroup mapWallet
*
* @{
*/
std::string COutput::ToString() const {
return strprintf("COutput(%s, %d, %d) [%s]", tx->GetId().ToString(), i,
nDepth, FormatMoney(tx->tx->vout[i].nValue));
}
class CAffectedKeysVisitor : public boost::static_visitor<void> {
private:
const CKeyStore &keystore;
std::vector<CKeyID> &vKeys;
public:
CAffectedKeysVisitor(const CKeyStore &keystoreIn,
std::vector<CKeyID> &vKeysIn)
: keystore(keystoreIn), vKeys(vKeysIn) {}
void Process(const CScript &script) {
txnouttype type;
std::vector<CTxDestination> vDest;
int nRequired;
if (ExtractDestinations(script, type, vDest, nRequired)) {
for (const CTxDestination &dest : vDest) {
boost::apply_visitor(*this, dest);
}
}
}
void operator()(const CKeyID &keyId) {
if (keystore.HaveKey(keyId)) {
vKeys.push_back(keyId);
}
}
void operator()(const CScriptID &scriptId) {
CScript script;
if (keystore.GetCScript(scriptId, script)) {
Process(script);
}
}
void operator()(const CNoDestination &none) {}
};
const CWalletTx *CWallet::GetWalletTx(const TxId &txid) const {
LOCK(cs_wallet);
std::map<TxId, CWalletTx>::const_iterator it = mapWallet.find(txid);
if (it == mapWallet.end()) {
return nullptr;
}
return &(it->second);
}
CPubKey CWallet::GenerateNewKey(WalletBatch &batch, bool internal) {
// mapKeyMetadata
AssertLockHeld(cs_wallet);
// default to compressed public keys if we want 0.6.0 wallets
bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY);
CKey secret;
// Create new metadata
int64_t nCreationTime = GetTime();
CKeyMetadata metadata(nCreationTime);
// use HD key derivation if HD was enabled during wallet creation
if (IsHDEnabled()) {
DeriveNewChildKey(
batch, metadata, secret,
(CanSupportFeature(FEATURE_HD_SPLIT) ? internal : false));
} else {
secret.MakeNewKey(fCompressed);
}
// Compressed public keys were introduced in version 0.6.0
if (fCompressed) {
SetMinVersion(FEATURE_COMPRPUBKEY);
}
CPubKey pubkey = secret.GetPubKey();
assert(secret.VerifyPubKey(pubkey));
mapKeyMetadata[pubkey.GetID()] = metadata;
UpdateTimeFirstKey(nCreationTime);
if (!AddKeyPubKeyWithDB(batch, secret, pubkey)) {
throw std::runtime_error(std::string(__func__) + ": AddKey failed");
}
return pubkey;
}
void CWallet::DeriveNewChildKey(WalletBatch &batch, CKeyMetadata &metadata,
CKey &secret, bool internal) {
// for now we use a fixed keypath scheme of m/0'/0'/k
// seed (256bit)
CKey seed;
// hd master key
CExtKey masterKey;
// key at m/0'
CExtKey accountKey;
// key at m/0'/0' (external) or m/0'/1' (internal)
CExtKey chainChildKey;
// key at m/0'/0'/<n>'
CExtKey childKey;
// try to get the seed
if (!GetKey(hdChain.seed_id, seed)) {
throw std::runtime_error(std::string(__func__) + ": seed not found");
}
masterKey.SetSeed(seed.begin(), seed.size());
// derive m/0'
// use hardened derivation (child keys >= 0x80000000 are hardened after
// bip32)
masterKey.Derive(accountKey, BIP32_HARDENED_KEY_LIMIT);
// derive m/0'/0' (external chain) OR m/0'/1' (internal chain)
assert(internal ? CanSupportFeature(FEATURE_HD_SPLIT) : true);
accountKey.Derive(chainChildKey,
BIP32_HARDENED_KEY_LIMIT + (internal ? 1 : 0));
// derive child key at next index, skip keys already known to the wallet
do {
// always derive hardened keys
// childIndex | BIP32_HARDENED_KEY_LIMIT = derive childIndex in hardened
// child-index-range
// example: 1 | BIP32_HARDENED_KEY_LIMIT == 0x80000001 == 2147483649
if (internal) {
chainChildKey.Derive(childKey, hdChain.nInternalChainCounter |
BIP32_HARDENED_KEY_LIMIT);
metadata.hdKeypath = "m/0'/1'/" +
std::to_string(hdChain.nInternalChainCounter) +
"'";
hdChain.nInternalChainCounter++;
} else {
chainChildKey.Derive(childKey, hdChain.nExternalChainCounter |
BIP32_HARDENED_KEY_LIMIT);
metadata.hdKeypath = "m/0'/0'/" +
std::to_string(hdChain.nExternalChainCounter) +
"'";
hdChain.nExternalChainCounter++;
}
} while (HaveKey(childKey.key.GetPubKey().GetID()));
secret = childKey.key;
metadata.hd_seed_id = hdChain.seed_id;
// update the chain model in the database
if (!batch.WriteHDChain(hdChain)) {
throw std::runtime_error(std::string(__func__) +
": Writing HD chain model failed");
}
}
bool CWallet::AddKeyPubKeyWithDB(WalletBatch &batch, const CKey &secret,
const CPubKey &pubkey) {
// mapKeyMetadata
AssertLockHeld(cs_wallet);
// CCryptoKeyStore has no concept of wallet databases, but calls
// AddCryptedKey
// which is overridden below. To avoid flushes, the database handle is
// tunneled through to it.
bool needsDB = !encrypted_batch;
if (needsDB) {
encrypted_batch = &batch;
}
if (!CCryptoKeyStore::AddKeyPubKey(secret, pubkey)) {
if (needsDB) {
encrypted_batch = nullptr;
}
return false;
}
if (needsDB) {
encrypted_batch = nullptr;
}
// Check if we need to remove from watch-only.
CScript script;
script = GetScriptForDestination(pubkey.GetID());
if (HaveWatchOnly(script)) {
RemoveWatchOnly(script);
}
script = GetScriptForRawPubKey(pubkey);
if (HaveWatchOnly(script)) {
RemoveWatchOnly(script);
}
if (IsCrypted()) {
return true;
}
return batch.WriteKey(pubkey, secret.GetPrivKey(),
mapKeyMetadata[pubkey.GetID()]);
}
bool CWallet::AddKeyPubKey(const CKey &secret, const CPubKey &pubkey) {
WalletBatch batch(*database);
return CWallet::AddKeyPubKeyWithDB(batch, secret, pubkey);
}
bool CWallet::AddCryptedKey(const CPubKey &vchPubKey,
const std::vector<uint8_t> &vchCryptedSecret) {
if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret)) {
return false;
}
LOCK(cs_wallet);
if (encrypted_batch) {
return encrypted_batch->WriteCryptedKey(
vchPubKey, vchCryptedSecret, mapKeyMetadata[vchPubKey.GetID()]);
}
return WalletBatch(*database).WriteCryptedKey(
vchPubKey, vchCryptedSecret, mapKeyMetadata[vchPubKey.GetID()]);
}
void CWallet::LoadKeyMetadata(const CKeyID &keyID, const CKeyMetadata &meta) {
// mapKeyMetadata
AssertLockHeld(cs_wallet);
UpdateTimeFirstKey(meta.nCreateTime);
mapKeyMetadata[keyID] = meta;
}
void CWallet::LoadScriptMetadata(const CScriptID &script_id,
const CKeyMetadata &meta) {
// m_script_metadata
AssertLockHeld(cs_wallet);
UpdateTimeFirstKey(meta.nCreateTime);
m_script_metadata[script_id] = meta;
}
bool CWallet::LoadCryptedKey(const CPubKey &vchPubKey,
const std::vector<uint8_t> &vchCryptedSecret) {
return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret);
}
/**
* Update wallet first key creation time. This should be called whenever keys
* are added to the wallet, with the oldest key creation time.
*/
void CWallet::UpdateTimeFirstKey(int64_t nCreateTime) {
AssertLockHeld(cs_wallet);
if (nCreateTime <= 1) {
// Cannot determine birthday information, so set the wallet birthday to
// the beginning of time.
nTimeFirstKey = 1;
} else if (!nTimeFirstKey || nCreateTime < nTimeFirstKey) {
nTimeFirstKey = nCreateTime;
}
}
bool CWallet::AddCScript(const CScript &redeemScript) {
if (!CCryptoKeyStore::AddCScript(redeemScript)) {
return false;
}
return WalletBatch(*database).WriteCScript(Hash160(redeemScript),
redeemScript);
}
bool CWallet::LoadCScript(const CScript &redeemScript) {
/**
* A sanity check was added in pull #3843 to avoid adding redeemScripts that
* never can be redeemed. However, old wallets may still contain these. Do
* not add them to the wallet and warn.
*/
if (redeemScript.size() > MAX_SCRIPT_ELEMENT_SIZE) {
std::string strAddr =
EncodeDestination(CScriptID(redeemScript), GetConfig());
LogPrintf("%s: Warning: This wallet contains a redeemScript of size %i "
"which exceeds maximum size %i thus can never be redeemed. "
"Do not use address %s.\n",
__func__, redeemScript.size(), MAX_SCRIPT_ELEMENT_SIZE,
strAddr);
return true;
}
return CCryptoKeyStore::AddCScript(redeemScript);
}
bool CWallet::AddWatchOnly(const CScript &dest) {
if (!CCryptoKeyStore::AddWatchOnly(dest)) {
return false;
}
const CKeyMetadata &meta = m_script_metadata[CScriptID(dest)];
UpdateTimeFirstKey(meta.nCreateTime);
NotifyWatchonlyChanged(true);
return WalletBatch(*database).WriteWatchOnly(dest, meta);
}
bool CWallet::AddWatchOnly(const CScript &dest, int64_t nCreateTime) {
m_script_metadata[CScriptID(dest)].nCreateTime = nCreateTime;
return AddWatchOnly(dest);
}
bool CWallet::RemoveWatchOnly(const CScript &dest) {
AssertLockHeld(cs_wallet);
if (!CCryptoKeyStore::RemoveWatchOnly(dest)) {
return false;
}
if (!HaveWatchOnly()) {
NotifyWatchonlyChanged(false);
}
return WalletBatch(*database).EraseWatchOnly(dest);
}
bool CWallet::LoadWatchOnly(const CScript &dest) {
return CCryptoKeyStore::AddWatchOnly(dest);
}
bool CWallet::Unlock(const SecureString &strWalletPassphrase) {
CCrypter crypter;
CKeyingMaterial _vMasterKey;
LOCK(cs_wallet);
for (const MasterKeyMap::value_type &pMasterKey : mapMasterKeys) {
if (!crypter.SetKeyFromPassphrase(
strWalletPassphrase, pMasterKey.second.vchSalt,
pMasterKey.second.nDeriveIterations,
pMasterKey.second.nDerivationMethod)) {
return false;
}
if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, _vMasterKey)) {
// try another master key
continue;
}
if (CCryptoKeyStore::Unlock(_vMasterKey)) {
return true;
}
}
return false;
}
bool CWallet::ChangeWalletPassphrase(
const SecureString &strOldWalletPassphrase,
const SecureString &strNewWalletPassphrase) {
bool fWasLocked = IsLocked();
LOCK(cs_wallet);
Lock();
CCrypter crypter;
CKeyingMaterial _vMasterKey;
for (MasterKeyMap::value_type &pMasterKey : mapMasterKeys) {
if (!crypter.SetKeyFromPassphrase(
strOldWalletPassphrase, pMasterKey.second.vchSalt,
pMasterKey.second.nDeriveIterations,
pMasterKey.second.nDerivationMethod)) {
return false;
}
if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, _vMasterKey)) {
return false;
}
if (CCryptoKeyStore::Unlock(_vMasterKey)) {
int64_t nStartTime = GetTimeMillis();
crypter.SetKeyFromPassphrase(strNewWalletPassphrase,
pMasterKey.second.vchSalt,
pMasterKey.second.nDeriveIterations,
pMasterKey.second.nDerivationMethod);
pMasterKey.second.nDeriveIterations = static_cast<unsigned int>(
pMasterKey.second.nDeriveIterations *
(100 / ((double)(GetTimeMillis() - nStartTime))));
nStartTime = GetTimeMillis();
crypter.SetKeyFromPassphrase(strNewWalletPassphrase,
pMasterKey.second.vchSalt,
pMasterKey.second.nDeriveIterations,
pMasterKey.second.nDerivationMethod);
pMasterKey.second.nDeriveIterations =
(pMasterKey.second.nDeriveIterations +
static_cast<unsigned int>(
pMasterKey.second.nDeriveIterations * 100 /
double(GetTimeMillis() - nStartTime))) /
2;
if (pMasterKey.second.nDeriveIterations < 25000) {
pMasterKey.second.nDeriveIterations = 25000;
}
LogPrintf(
"Wallet passphrase changed to an nDeriveIterations of %i\n",
pMasterKey.second.nDeriveIterations);
if (!crypter.SetKeyFromPassphrase(
strNewWalletPassphrase, pMasterKey.second.vchSalt,
pMasterKey.second.nDeriveIterations,
pMasterKey.second.nDerivationMethod)) {
return false;
}
if (!crypter.Encrypt(_vMasterKey,
pMasterKey.second.vchCryptedKey)) {
return false;
}
WalletBatch(*database).WriteMasterKey(pMasterKey.first,
pMasterKey.second);
if (fWasLocked) {
Lock();
}
return true;
}
}
return false;
}
void CWallet::ChainStateFlushed(const CBlockLocator &loc) {
WalletBatch batch(*database);
batch.WriteBestBlock(loc);
}
void CWallet::SetMinVersion(enum WalletFeature nVersion, WalletBatch *batch_in,
bool fExplicit) {
// nWalletVersion
LOCK(cs_wallet);
if (nWalletVersion >= nVersion) {
return;
}
// When doing an explicit upgrade, if we pass the max version permitted,
// upgrade all the way.
if (fExplicit && nVersion > nWalletMaxVersion) {
nVersion = FEATURE_LATEST;
}
nWalletVersion = nVersion;
if (nVersion > nWalletMaxVersion) {
nWalletMaxVersion = nVersion;
}
WalletBatch *batch = batch_in ? batch_in : new WalletBatch(*database);
if (nWalletVersion > 40000) {
batch->WriteMinVersion(nWalletVersion);
}
if (!batch_in) {
delete batch;
}
}
bool CWallet::SetMaxVersion(int nVersion) {
// nWalletVersion, nWalletMaxVersion
LOCK(cs_wallet);
// Cannot downgrade below current version
if (nWalletVersion > nVersion) {
return false;
}
nWalletMaxVersion = nVersion;
return true;
}
std::set<TxId> CWallet::GetConflicts(const TxId &txid) const {
std::set<TxId> result;
AssertLockHeld(cs_wallet);
std::map<TxId, CWalletTx>::const_iterator it = mapWallet.find(txid);
if (it == mapWallet.end()) {
return result;
}
const CWalletTx &wtx = it->second;
std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range;
for (const CTxIn &txin : wtx.tx->vin) {
if (mapTxSpends.count(txin.prevout) <= 1) {
// No conflict if zero or one spends.
continue;
}
range = mapTxSpends.equal_range(txin.prevout);
for (TxSpends::const_iterator _it = range.first; _it != range.second;
++_it) {
result.insert(_it->second);
}
}
return result;
}
bool CWallet::HasWalletSpend(const TxId &txid) const {
AssertLockHeld(cs_wallet);
auto iter = mapTxSpends.lower_bound(COutPoint(txid, 0));
return (iter != mapTxSpends.end() && iter->first.GetTxId() == txid);
}
void CWallet::Flush(bool shutdown) {
database->Flush(shutdown);
}
void CWallet::SyncMetaData(
std::pair<TxSpends::iterator, TxSpends::iterator> range) {
// We want all the wallet transactions in range to have the same metadata as
// the oldest (smallest nOrderPos).
// So: find smallest nOrderPos:
int nMinOrderPos = std::numeric_limits<int>::max();
const CWalletTx *copyFrom = nullptr;
for (TxSpends::iterator it = range.first; it != range.second; ++it) {
const CWalletTx *wtx = &mapWallet.at(it->second);
if (wtx->nOrderPos < nMinOrderPos) {
nMinOrderPos = wtx->nOrderPos;
copyFrom = wtx;
}
}
// Now copy data from copyFrom to rest:
for (TxSpends::iterator it = range.first; it != range.second; ++it) {
const TxId &txid = it->second;
CWalletTx *copyTo = &mapWallet.at(txid);
if (copyFrom == copyTo) {
continue;
}
assert(
copyFrom &&
"Oldest wallet transaction in range assumed to have been found.");
if (!copyFrom->IsEquivalentTo(*copyTo)) {
continue;
}
copyTo->mapValue = copyFrom->mapValue;
copyTo->vOrderForm = copyFrom->vOrderForm;
// fTimeReceivedIsTxTime not copied on purpose nTimeReceived not copied
// on purpose.
copyTo->nTimeSmart = copyFrom->nTimeSmart;
copyTo->fFromMe = copyFrom->fFromMe;
copyTo->strFromAccount = copyFrom->strFromAccount;
// nOrderPos not copied on purpose cached members not copied on purpose.
}
}
/**
* Outpoint is spent if any non-conflicted transaction, spends it:
*/
bool CWallet::IsSpent(const COutPoint &outpoint) const {
std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range =
mapTxSpends.equal_range(outpoint);
for (TxSpends::const_iterator it = range.first; it != range.second; ++it) {
const TxId &wtxid = it->second;
std::map<TxId, CWalletTx>::const_iterator mit = mapWallet.find(wtxid);
if (mit != mapWallet.end()) {
int depth = mit->second.GetDepthInMainChain();
if (depth > 0 || (depth == 0 && !mit->second.isAbandoned())) {
// Spent
return true;
}
}
}
return false;
}
void CWallet::AddToSpends(const COutPoint &outpoint, const TxId &wtxid) {
mapTxSpends.insert(std::make_pair(outpoint, wtxid));
std::pair<TxSpends::iterator, TxSpends::iterator> range;
range = mapTxSpends.equal_range(outpoint);
SyncMetaData(range);
}
void CWallet::AddToSpends(const TxId &wtxid) {
auto it = mapWallet.find(wtxid);
assert(it != mapWallet.end());
CWalletTx &thisTx = it->second;
// Coinbases don't spend anything!
if (thisTx.IsCoinBase()) {
return;
}
for (const CTxIn &txin : thisTx.tx->vin) {
AddToSpends(txin.prevout, wtxid);
}
}
bool CWallet::EncryptWallet(const SecureString &strWalletPassphrase) {
if (IsCrypted()) {
return false;
}
CKeyingMaterial _vMasterKey;
_vMasterKey.resize(WALLET_CRYPTO_KEY_SIZE);
GetStrongRandBytes(&_vMasterKey[0], WALLET_CRYPTO_KEY_SIZE);
CMasterKey kMasterKey;
kMasterKey.vchSalt.resize(WALLET_CRYPTO_SALT_SIZE);
GetStrongRandBytes(&kMasterKey.vchSalt[0], WALLET_CRYPTO_SALT_SIZE);
CCrypter crypter;
int64_t nStartTime = GetTimeMillis();
crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, 25000,
kMasterKey.nDerivationMethod);
kMasterKey.nDeriveIterations = static_cast<unsigned int>(
2500000 / double(GetTimeMillis() - nStartTime));
nStartTime = GetTimeMillis();
crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt,
kMasterKey.nDeriveIterations,
kMasterKey.nDerivationMethod);
kMasterKey.nDeriveIterations =
(kMasterKey.nDeriveIterations +
static_cast<unsigned int>(kMasterKey.nDeriveIterations * 100 /
double(GetTimeMillis() - nStartTime))) /
2;
if (kMasterKey.nDeriveIterations < 25000) {
kMasterKey.nDeriveIterations = 25000;
}
LogPrintf("Encrypting Wallet with an nDeriveIterations of %i\n",
kMasterKey.nDeriveIterations);
if (!crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt,
kMasterKey.nDeriveIterations,
kMasterKey.nDerivationMethod)) {
return false;
}
if (!crypter.Encrypt(_vMasterKey, kMasterKey.vchCryptedKey)) {
return false;
}
{
LOCK(cs_wallet);
mapMasterKeys[++nMasterKeyMaxID] = kMasterKey;
assert(!encrypted_batch);
encrypted_batch = new WalletBatch(*database);
if (!encrypted_batch->TxnBegin()) {
delete encrypted_batch;
encrypted_batch = nullptr;
return false;
}
encrypted_batch->WriteMasterKey(nMasterKeyMaxID, kMasterKey);
if (!EncryptKeys(_vMasterKey)) {
encrypted_batch->TxnAbort();
delete encrypted_batch;
// We now probably have half of our keys encrypted in memory, and
// half not... die and let the user reload the unencrypted wallet.
assert(false);
}
// Encryption was introduced in version 0.4.0
SetMinVersion(FEATURE_WALLETCRYPT, encrypted_batch, true);
if (!encrypted_batch->TxnCommit()) {
delete encrypted_batch;
// We now have keys encrypted in memory, but not on disk...
// die to avoid confusion and let the user reload the unencrypted
// wallet.
assert(false);
}
delete encrypted_batch;
encrypted_batch = nullptr;
Lock();
Unlock(strWalletPassphrase);
// If we are using HD, replace the HD seed with a new one
if (IsHDEnabled()) {
SetHDSeed(GenerateNewSeed());
}
NewKeyPool();
Lock();
// Need to completely rewrite the wallet file; if we don't, bdb might
// keep bits of the unencrypted private key in slack space in the
// database file.
database->Rewrite();
}
NotifyStatusChanged(this);
return true;
}
DBErrors CWallet::ReorderTransactions() {
LOCK(cs_wallet);
WalletBatch batch(*database);
// Old wallets didn't have any defined order for transactions. Probably a
// bad idea to change the output of this.
// First: get all CWalletTx and CAccountingEntry into a sorted-by-time
// multimap.
TxItems txByTime;
for (auto &entry : mapWallet) {
CWalletTx *wtx = &entry.second;
txByTime.insert(
std::make_pair(wtx->nTimeReceived, TxPair(wtx, nullptr)));
}
std::list<CAccountingEntry> acentries;
batch.ListAccountCreditDebit("", acentries);
for (CAccountingEntry &entry : acentries) {
txByTime.insert(std::make_pair(entry.nTime, TxPair(nullptr, &entry)));
}
nOrderPosNext = 0;
std::vector<int64_t> nOrderPosOffsets;
for (TxItems::iterator it = txByTime.begin(); it != txByTime.end(); ++it) {
CWalletTx *const pwtx = (*it).second.first;
CAccountingEntry *const pacentry = (*it).second.second;
int64_t &nOrderPos =
(pwtx != nullptr) ? pwtx->nOrderPos : pacentry->nOrderPos;
if (nOrderPos == -1) {
nOrderPos = nOrderPosNext++;
nOrderPosOffsets.push_back(nOrderPos);
if (pwtx) {
if (!batch.WriteTx(*pwtx)) {
return DBErrors::LOAD_FAIL;
}
} else if (!batch.WriteAccountingEntry(pacentry->nEntryNo,
*pacentry)) {
return DBErrors::LOAD_FAIL;
}
} else {
int64_t nOrderPosOff = 0;
for (const int64_t &nOffsetStart : nOrderPosOffsets) {
if (nOrderPos >= nOffsetStart) {
++nOrderPosOff;
}
}
nOrderPos += nOrderPosOff;
nOrderPosNext = std::max(nOrderPosNext, nOrderPos + 1);
if (!nOrderPosOff) {
continue;
}
// Since we're changing the order, write it back.
if (pwtx) {
if (!batch.WriteTx(*pwtx)) {
return DBErrors::LOAD_FAIL;
}
} else if (!batch.WriteAccountingEntry(pacentry->nEntryNo,
*pacentry)) {
return DBErrors::LOAD_FAIL;
}
}
}
batch.WriteOrderPosNext(nOrderPosNext);
return DBErrors::LOAD_OK;
}
int64_t CWallet::IncOrderPosNext(WalletBatch *batch) {
// nOrderPosNext
AssertLockHeld(cs_wallet);
int64_t nRet = nOrderPosNext++;
if (batch) {
batch->WriteOrderPosNext(nOrderPosNext);
} else {
WalletBatch(*database).WriteOrderPosNext(nOrderPosNext);
}
return nRet;
}
bool CWallet::AccountMove(std::string strFrom, std::string strTo,
const Amount nAmount, std::string strComment) {
WalletBatch batch(*database);
if (!batch.TxnBegin()) {
return false;
}
int64_t nNow = GetAdjustedTime();
// Debit
CAccountingEntry debit;
debit.nOrderPos = IncOrderPosNext(&batch);
debit.strAccount = strFrom;
debit.nCreditDebit = -nAmount;
debit.nTime = nNow;
debit.strOtherAccount = strTo;
debit.strComment = strComment;
AddAccountingEntry(debit, &batch);
// Credit
CAccountingEntry credit;
credit.nOrderPos = IncOrderPosNext(&batch);
credit.strAccount = strTo;
credit.nCreditDebit = nAmount;
credit.nTime = nNow;
credit.strOtherAccount = strFrom;
credit.strComment = strComment;
AddAccountingEntry(credit, &batch);
return batch.TxnCommit();
}
bool CWallet::GetLabelDestination(CTxDestination &dest,
const std::string &label, bool bForceNew) {
WalletBatch batch(*database);
CAccount account;
batch.ReadAccount(label, account);
if (!bForceNew) {
if (!account.vchPubKey.IsValid()) {
bForceNew = true;
} else {
// Check if the current key has been used (TODO: check other
// addresses with the same key)
CScript scriptPubKey = GetScriptForDestination(GetDestinationForKey(
account.vchPubKey, m_default_address_type));
for (std::map<TxId, CWalletTx>::iterator it = mapWallet.begin();
it != mapWallet.end() && account.vchPubKey.IsValid(); ++it) {
for (const CTxOut &txout : (*it).second.tx->vout) {
if (txout.scriptPubKey == scriptPubKey) {
bForceNew = true;
break;
}
}
}
}
}
// Generate a new key
if (bForceNew) {
if (!GetKeyFromPool(account.vchPubKey, false)) {
return false;
}
LearnRelatedScripts(account.vchPubKey, m_default_address_type);
dest = GetDestinationForKey(account.vchPubKey, m_default_address_type);
SetAddressBook(dest, label, "receive");
batch.WriteAccount(label, account);
} else {
dest = GetDestinationForKey(account.vchPubKey, m_default_address_type);
}
return true;
}
void CWallet::MarkDirty() {
LOCK(cs_wallet);
for (std::pair<const TxId, CWalletTx> &item : mapWallet) {
item.second.MarkDirty();
}
}
bool CWallet::AddToWallet(const CWalletTx &wtxIn, bool fFlushOnClose) {
LOCK(cs_wallet);
WalletBatch batch(*database, "r+", fFlushOnClose);
const TxId &txid = wtxIn.GetId();
// Inserts only if not already there, returns tx inserted or tx found.
std::pair<std::map<TxId, CWalletTx>::iterator, bool> ret =
mapWallet.insert(std::make_pair(txid, wtxIn));
CWalletTx &wtx = (*ret.first).second;
wtx.BindWallet(this);
bool fInsertedNew = ret.second;
if (fInsertedNew) {
wtx.nTimeReceived = GetAdjustedTime();
wtx.nOrderPos = IncOrderPosNext(&batch);
wtxOrdered.insert(std::make_pair(wtx.nOrderPos, TxPair(&wtx, nullptr)));
wtx.nTimeSmart = ComputeTimeSmart(wtx);
AddToSpends(txid);
}
bool fUpdated = false;
if (!fInsertedNew) {
// Merge
if (!wtxIn.hashUnset() && wtxIn.hashBlock != wtx.hashBlock) {
wtx.hashBlock = wtxIn.hashBlock;
fUpdated = true;
}
// If no longer abandoned, update
if (wtxIn.hashBlock.IsNull() && wtx.isAbandoned()) {
wtx.hashBlock = wtxIn.hashBlock;
fUpdated = true;
}
if (wtxIn.nIndex != -1 && (wtxIn.nIndex != wtx.nIndex)) {
wtx.nIndex = wtxIn.nIndex;
fUpdated = true;
}
if (wtxIn.fFromMe && wtxIn.fFromMe != wtx.fFromMe) {
wtx.fFromMe = wtxIn.fFromMe;
fUpdated = true;
}
}
//// debug print
LogPrintf("AddToWallet %s %s%s\n", wtxIn.GetId().ToString(),
(fInsertedNew ? "new" : ""), (fUpdated ? "update" : ""));
// Write to disk
if ((fInsertedNew || fUpdated) && !batch.WriteTx(wtx)) {
return false;
}
// Break debit/credit balance caches:
wtx.MarkDirty();
// Notify UI of new or updated transaction.
NotifyTransactionChanged(this, txid, fInsertedNew ? CT_NEW : CT_UPDATED);
// Notify an external script when a wallet transaction comes in or is
// updated.
std::string strCmd = gArgs.GetArg("-walletnotify", "");
if (!strCmd.empty()) {
boost::replace_all(strCmd, "%s", wtxIn.GetId().GetHex());
std::thread t(runCommand, strCmd);
// Thread runs free.
t.detach();
}
return true;
}
void CWallet::LoadToWallet(const CWalletTx &wtxIn) {
const TxId &txid = wtxIn.GetId();
CWalletTx &wtx = mapWallet.emplace(txid, wtxIn).first->second;
wtx.BindWallet(this);
wtxOrdered.insert(std::make_pair(wtx.nOrderPos, TxPair(&wtx, nullptr)));
AddToSpends(txid);
for (const CTxIn &txin : wtx.tx->vin) {
auto it = mapWallet.find(txin.prevout.GetTxId());
if (it != mapWallet.end()) {
CWalletTx &prevtx = it->second;
if (prevtx.nIndex == -1 && !prevtx.hashUnset()) {
MarkConflicted(prevtx.hashBlock, wtx.GetId());
}
}
}
}
/**
* Add a transaction to the wallet, or update it. pIndex and posInBlock should
* be set when the transaction was known to be included in a block. When pIndex
* == nullptr, then wallet state is not updated in AddToWallet, but
* notifications happen and cached balances are marked dirty.
*
* If fUpdate is true, existing transactions will be updated.
* TODO: One exception to this is that the abandoned state is cleared under the
* assumption that any further notification of a transaction that was considered
* abandoned is an indication that it is not safe to be considered abandoned.
* Abandoned state should probably be more carefully tracked via different
* posInBlock signals or by checking mempool presence when necessary.
*/
bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef &ptx,
const CBlockIndex *pIndex,
int posInBlock, bool fUpdate) {
const CTransaction &tx = *ptx;
AssertLockHeld(cs_wallet);
if (pIndex != nullptr) {
for (const CTxIn &txin : tx.vin) {
std::pair<TxSpends::const_iterator, TxSpends::const_iterator>
range = mapTxSpends.equal_range(txin.prevout);
while (range.first != range.second) {
if (range.first->second != tx.GetId()) {
LogPrintf("Transaction %s (in block %s) conflicts with "
"wallet transaction %s (both spend %s:%i)\n",
tx.GetId().ToString(),
pIndex->GetBlockHash().ToString(),
range.first->second.ToString(),
range.first->first.GetTxId().ToString(),
range.first->first.GetN());
MarkConflicted(pIndex->GetBlockHash(), range.first->second);
}
range.first++;
}
}
}
bool fExisted = mapWallet.count(tx.GetId()) != 0;
if (fExisted && !fUpdate) {
return false;
}
if (fExisted || IsMine(tx) || IsFromMe(tx)) {
/**
* Check if any keys in the wallet keypool that were supposed to be
* unused have appeared in a new transaction. If so, remove those keys
* from the keypool. This can happen when restoring an old wallet backup
* that does not contain the mostly recently created transactions from
* newer versions of the wallet.
*/
// loop though all outputs
for (const CTxOut &txout : tx.vout) {
// extract addresses and check if they match with an unused keypool
// key
std::vector<CKeyID> vAffected;
CAffectedKeysVisitor(*this, vAffected).Process(txout.scriptPubKey);
for (const CKeyID &keyid : vAffected) {
std::map<CKeyID, int64_t>::const_iterator mi =
m_pool_key_to_index.find(keyid);
if (mi != m_pool_key_to_index.end()) {
LogPrintf("%s: Detected a used keypool key, mark all "
"keypool key up to this key as used\n",
__func__);
MarkReserveKeysAsUsed(mi->second);
if (!TopUpKeyPool()) {
LogPrintf(
"%s: Topping up keypool failed (locked wallet)\n",
__func__);
}
}
}
}
CWalletTx wtx(this, ptx);
// Get merkle branch if transaction was found in a block
if (pIndex != nullptr) {
wtx.SetMerkleBranch(pIndex, posInBlock);
}
return AddToWallet(wtx, false);
}
return false;
}
bool CWallet::TransactionCanBeAbandoned(const TxId &txid) const {
LOCK2(cs_main, cs_wallet);
const CWalletTx *wtx = GetWalletTx(txid);
return wtx && !wtx->isAbandoned() && wtx->GetDepthInMainChain() == 0 &&
!wtx->InMempool();
}
bool CWallet::AbandonTransaction(const TxId &txid) {
LOCK2(cs_main, cs_wallet);
WalletBatch batch(*database, "r+");
std::set<TxId> todo;
std::set<TxId> done;
// Can't mark abandoned if confirmed or in mempool
auto it = mapWallet.find(txid);
assert(it != mapWallet.end());
CWalletTx &origtx = it->second;
if (origtx.GetDepthInMainChain() != 0 || origtx.InMempool()) {
return false;
}
todo.insert(txid);
while (!todo.empty()) {
const TxId now = *todo.begin();
todo.erase(now);
done.insert(now);
it = mapWallet.find(now);
assert(it != mapWallet.end());
CWalletTx &wtx = it->second;
int currentconfirm = wtx.GetDepthInMainChain();
// If the orig tx was not in block, none of its spends can be.
assert(currentconfirm <= 0);
// If (currentconfirm < 0) {Tx and spends are already conflicted, no
// need to abandon}
if (currentconfirm == 0 && !wtx.isAbandoned()) {
// If the orig tx was not in block/mempool, none of its spends can
// be in mempool.
assert(!wtx.InMempool());
wtx.nIndex = -1;
wtx.setAbandoned();
wtx.MarkDirty();
batch.WriteTx(wtx);
NotifyTransactionChanged(this, wtx.GetId(), CT_UPDATED);
// Iterate over all its outputs, and mark transactions in the wallet
// that spend them abandoned too.
TxSpends::const_iterator iter =
mapTxSpends.lower_bound(COutPoint(now, 0));
while (iter != mapTxSpends.end() && iter->first.GetTxId() == now) {
if (!done.count(iter->second)) {
todo.insert(iter->second);
}
iter++;
}
// If a transaction changes 'conflicted' state, that changes the
// balance available of the outputs it spends. So force those to be
// recomputed.
for (const CTxIn &txin : wtx.tx->vin) {
auto it2 = mapWallet.find(txin.prevout.GetTxId());
if (it2 != mapWallet.end()) {
it2->second.MarkDirty();
}
}
}
}
return true;
}
void CWallet::MarkConflicted(const uint256 &hashBlock, const TxId &txid) {
LOCK2(cs_main, cs_wallet);
int conflictconfirms = 0;
CBlockIndex *pindex = LookupBlockIndex(hashBlock);
if (pindex && chainActive.Contains(pindex)) {
conflictconfirms = -(chainActive.Height() - pindex->nHeight + 1);
}
// If number of conflict confirms cannot be determined, this means that the
// block is still unknown or not yet part of the main chain, for example
// when loading the wallet during a reindex. Do nothing in that case.
if (conflictconfirms >= 0) {
return;
}
// Do not flush the wallet here for performance reasons.
WalletBatch batch(*database, "r+", false);
std::set<TxId> todo;
std::set<TxId> done;
todo.insert(txid);
while (!todo.empty()) {
const TxId now = *todo.begin();
todo.erase(now);
done.insert(now);
auto it = mapWallet.find(now);
assert(it != mapWallet.end());
CWalletTx &wtx = it->second;
int currentconfirm = wtx.GetDepthInMainChain();
if (conflictconfirms < currentconfirm) {
// Block is 'more conflicted' than current confirm; update.
// Mark transaction as conflicted with this block.
wtx.nIndex = -1;
wtx.hashBlock = hashBlock;
wtx.MarkDirty();
batch.WriteTx(wtx);
// Iterate over all its outputs, and mark transactions in the wallet
// that spend them conflicted too.
TxSpends::const_iterator iter =
mapTxSpends.lower_bound(COutPoint(now, 0));
while (iter != mapTxSpends.end() && iter->first.GetTxId() == now) {
if (!done.count(iter->second)) {
todo.insert(iter->second);
}
iter++;
}
// If a transaction changes 'conflicted' state, that changes the
// balance available of the outputs it spends. So force those to be
// recomputed.
for (const CTxIn &txin : wtx.tx->vin) {
auto it2 = mapWallet.find(txin.prevout.GetTxId());
if (it2 != mapWallet.end()) {
it2->second.MarkDirty();
}
}
}
}
}
void CWallet::SyncTransaction(const CTransactionRef &ptx,
const CBlockIndex *pindex, int posInBlock) {
const CTransaction &tx = *ptx;
if (!AddToWalletIfInvolvingMe(ptx, pindex, posInBlock, true)) {
// Not one of ours
return;
}
// If a transaction changes 'conflicted' state, that changes the balance
// available of the outputs it spends. So force those to be
// recomputed, also:
for (const CTxIn &txin : tx.vin) {
auto it = mapWallet.find(txin.prevout.GetTxId());
if (it != mapWallet.end()) {
it->second.MarkDirty();
}
}
}
void CWallet::TransactionAddedToMempool(const CTransactionRef &ptx) {
LOCK2(cs_main, cs_wallet);
SyncTransaction(ptx);
auto it = mapWallet.find(ptx->GetId());
if (it != mapWallet.end()) {
it->second.fInMempool = true;
}
}
void CWallet::TransactionRemovedFromMempool(const CTransactionRef &ptx) {
LOCK(cs_wallet);
auto it = mapWallet.find(ptx->GetId());
if (it != mapWallet.end()) {
it->second.fInMempool = false;
}
}
void CWallet::BlockConnected(
const std::shared_ptr<const CBlock> &pblock, const CBlockIndex *pindex,
const std::vector<CTransactionRef> &vtxConflicted) {
LOCK2(cs_main, cs_wallet);
// TODO: Temporarily ensure that mempool removals are notified before
// connected transactions. This shouldn't matter, but the abandoned state of
// transactions in our wallet is currently cleared when we receive another
// notification and there is a race condition where notification of a
// connected conflict might cause an outside process to abandon a
// transaction and then have it inadvertently cleared by the notification
// that the conflicted transaction was evicted.
for (const CTransactionRef &ptx : vtxConflicted) {
SyncTransaction(ptx);
TransactionRemovedFromMempool(ptx);
}
for (size_t i = 0; i < pblock->vtx.size(); i++) {
SyncTransaction(pblock->vtx[i], pindex, i);
TransactionRemovedFromMempool(pblock->vtx[i]);
}
m_last_block_processed = pindex;
}
void CWallet::BlockDisconnected(const std::shared_ptr<const CBlock> &pblock) {
LOCK2(cs_main, cs_wallet);
for (const CTransactionRef &ptx : pblock->vtx) {
SyncTransaction(ptx);
}
}
void CWallet::BlockUntilSyncedToCurrentChain() {
AssertLockNotHeld(cs_main);
AssertLockNotHeld(cs_wallet);
{
// Skip the queue-draining stuff if we know we're caught up with
// chainActive.Tip()...
// We could also take cs_wallet here, and call m_last_block_processed
// protected by cs_wallet instead of cs_main, but as long as we need
// cs_main here anyway, it's easier to just call it cs_main-protected.
LOCK(cs_main);
const CBlockIndex *initialChainTip = chainActive.Tip();
if (m_last_block_processed &&
m_last_block_processed->GetAncestor(initialChainTip->nHeight) ==
initialChainTip) {
return;
}
}
// ...otherwise put a callback in the validation interface queue and wait
// for the queue to drain enough to execute it (indicating we are caught up
// at least with the time we entered this function).
SyncWithValidationInterfaceQueue();
}
isminetype CWallet::IsMine(const CTxIn &txin) const {
LOCK(cs_wallet);
std::map<TxId, CWalletTx>::const_iterator mi =
mapWallet.find(txin.prevout.GetTxId());
if (mi != mapWallet.end()) {
const CWalletTx &prev = (*mi).second;
if (txin.prevout.GetN() < prev.tx->vout.size()) {
return IsMine(prev.tx->vout[txin.prevout.GetN()]);
}
}
return ISMINE_NO;
}
// Note that this function doesn't distinguish between a 0-valued input, and a
// not-"is mine" (according to the filter) input.
Amount CWallet::GetDebit(const CTxIn &txin, const isminefilter &filter) const {
LOCK(cs_wallet);
std::map<TxId, CWalletTx>::const_iterator mi =
mapWallet.find(txin.prevout.GetTxId());
if (mi != mapWallet.end()) {
const CWalletTx &prev = (*mi).second;
if (txin.prevout.GetN() < prev.tx->vout.size()) {
if (IsMine(prev.tx->vout[txin.prevout.GetN()]) & filter) {
return prev.tx->vout[txin.prevout.GetN()].nValue;
}
}
}
return Amount::zero();
}
isminetype CWallet::IsMine(const CTxOut &txout) const {
return ::IsMine(*this, txout.scriptPubKey);
}
Amount CWallet::GetCredit(const CTxOut &txout,
const isminefilter &filter) const {
if (!MoneyRange(txout.nValue)) {
throw std::runtime_error(std::string(__func__) +
": value out of range");
}
return (IsMine(txout) & filter) ? txout.nValue : Amount::zero();
}
bool CWallet::IsChange(const CTxOut &txout) const {
// TODO: fix handling of 'change' outputs. The assumption is that any
// payment to a script that is ours, but is not in the address book is
// change. That assumption is likely to break when we implement
// multisignature wallets that return change back into a
// multi-signature-protected address; a better way of identifying which
// outputs are 'the send' and which are 'the change' will need to be
// implemented (maybe extend CWalletTx to remember which output, if any, was
// change).
if (::IsMine(*this, txout.scriptPubKey)) {
CTxDestination address;
if (!ExtractDestination(txout.scriptPubKey, address)) {
return true;
}
LOCK(cs_wallet);
if (!mapAddressBook.count(address)) {
return true;
}
}
return false;
}
Amount CWallet::GetChange(const CTxOut &txout) const {
if (!MoneyRange(txout.nValue)) {
throw std::runtime_error(std::string(__func__) +
": value out of range");
}
return (IsChange(txout) ? txout.nValue : Amount::zero());
}
bool CWallet::IsMine(const CTransaction &tx) const {
for (const CTxOut &txout : tx.vout) {
if (IsMine(txout)) {
return true;
}
}
return false;
}
bool CWallet::IsFromMe(const CTransaction &tx) const {
return GetDebit(tx, ISMINE_ALL) > Amount::zero();
}
Amount CWallet::GetDebit(const CTransaction &tx,
const isminefilter &filter) const {
Amount nDebit = Amount::zero();
for (const CTxIn &txin : tx.vin) {
nDebit += GetDebit(txin, filter);
if (!MoneyRange(nDebit)) {
throw std::runtime_error(std::string(__func__) +
": value out of range");
}
}
return nDebit;
}
bool CWallet::IsAllFromMe(const CTransaction &tx,
const isminefilter &filter) const {
LOCK(cs_wallet);
for (const CTxIn &txin : tx.vin) {
auto mi = mapWallet.find(txin.prevout.GetTxId());
if (mi == mapWallet.end()) {
// Any unknown inputs can't be from us.
return false;
}
const CWalletTx &prev = (*mi).second;
if (txin.prevout.GetN() >= prev.tx->vout.size()) {
// Invalid input!
return false;
}
if (!(IsMine(prev.tx->vout[txin.prevout.GetN()]) & filter)) {
return false;
}
}
return true;
}
Amount CWallet::GetCredit(const CTransaction &tx,
const isminefilter &filter) const {
Amount nCredit = Amount::zero();
for (const CTxOut &txout : tx.vout) {
nCredit += GetCredit(txout, filter);
if (!MoneyRange(nCredit)) {
throw std::runtime_error(std::string(__func__) +
": value out of range");
}
}
return nCredit;
}
Amount CWallet::GetChange(const CTransaction &tx) const {
Amount nChange = Amount::zero();
for (const CTxOut &txout : tx.vout) {
nChange += GetChange(txout);
if (!MoneyRange(nChange)) {
throw std::runtime_error(std::string(__func__) +
": value out of range");
}
}
return nChange;
}
CPubKey CWallet::GenerateNewSeed() {
CKey key;
key.MakeNewKey(true);
return DeriveNewSeed(key);
}
CPubKey CWallet::DeriveNewSeed(const CKey &key) {
int64_t nCreationTime = GetTime();
CKeyMetadata metadata(nCreationTime);
// Calculate the seed
CPubKey seed = key.GetPubKey();
assert(key.VerifyPubKey(seed));
// Set the hd keypath to "s" -> Seed, refers the seed to itself
metadata.hdKeypath = "s";
metadata.hd_seed_id = seed.GetID();
LOCK(cs_wallet);
// mem store the metadata
mapKeyMetadata[seed.GetID()] = metadata;
// Write the key&metadata to the database
if (!AddKeyPubKey(key, seed)) {
throw std::runtime_error(std::string(__func__) +
": AddKeyPubKey failed");
}
return seed;
}
void CWallet::SetHDSeed(const CPubKey &seed) {
LOCK(cs_wallet);
// Store the keyid (hash160) together with the child index counter in the
// database as a hdchain object.
CHDChain newHdChain;
newHdChain.nVersion = CanSupportFeature(FEATURE_HD_SPLIT)
? CHDChain::VERSION_HD_CHAIN_SPLIT
: CHDChain::VERSION_HD_BASE;
newHdChain.seed_id = seed.GetID();
SetHDChain(newHdChain, false);
}
void CWallet::SetHDChain(const CHDChain &chain, bool memonly) {
LOCK(cs_wallet);
if (!memonly && !WalletBatch(*database).WriteHDChain(chain)) {
throw std::runtime_error(std::string(__func__) +
": writing chain failed");
}
hdChain = chain;
}
bool CWallet::IsHDEnabled() const {
return !hdChain.seed_id.IsNull();
}
int64_t CWalletTx::GetTxTime() const {
int64_t n = nTimeSmart;
return n ? n : nTimeReceived;
}
// Helper for producing a max-sized low-S signature (eg 72 bytes)
bool CWallet::DummySignInput(CTxIn &tx_in, const CTxOut &txout) const {
// Fill in dummy signatures for fee calculation.
const CScript &scriptPubKey = txout.scriptPubKey;
SignatureData sigdata;
if (!ProduceSignature(*this, DUMMY_SIGNATURE_CREATOR, scriptPubKey,
sigdata)) {
return false;
}
UpdateInput(tx_in, sigdata);
return true;
}
// Helper for producing a bunch of max-sized low-S signatures (eg 72 bytes)
bool CWallet::DummySignTx(CMutableTransaction &txNew,
const std::vector<CTxOut> &txouts) const {
// Fill in dummy signatures for fee calculation.
int nIn = 0;
for (const auto &txout : txouts) {
if (!DummySignInput(txNew.vin[nIn], txout)) {
return false;
}
nIn++;
}
return true;
}
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx,
const CWallet *wallet) {
std::vector<CTxOut> txouts;
// Look up the inputs. We should have already checked that this transaction
// IsAllFromMe(ISMINE_SPENDABLE), so every input should already be in our
// wallet, with a valid index into the vout array, and the ability to sign.
for (auto &input : tx.vin) {
const auto mi = wallet->mapWallet.find(input.prevout.GetTxId());
if (mi == wallet->mapWallet.end()) {
return -1;
}
assert(input.prevout.GetN() < mi->second.tx->vout.size());
txouts.emplace_back(mi->second.tx->vout[input.prevout.GetN()]);
}
return CalculateMaximumSignedTxSize(tx, wallet, txouts);
}
// txouts needs to be in the order of tx.vin
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx,
const CWallet *wallet,
const std::vector<CTxOut> &txouts) {
CMutableTransaction txNew(tx);
if (!wallet->DummySignTx(txNew, txouts)) {
// This should never happen, because IsAllFromMe(ISMINE_SPENDABLE)
// implies that we can sign for every input.
return -1;
}
return GetVirtualTransactionSize(CTransaction(txNew));
}
int CalculateMaximumSignedInputSize(const CTxOut &txout,
const CWallet *wallet) {
CMutableTransaction txn;
txn.vin.push_back(CTxIn(COutPoint()));
if (!wallet->DummySignInput(txn.vin[0], txout)) {
// This should never happen, because IsAllFromMe(ISMINE_SPENDABLE)
// implies that we can sign for every input.
return -1;
}
return GetVirtualTransactionInputSize(txn.vin[0]);
}
void CWalletTx::GetAmounts(std::list<COutputEntry> &listReceived,
std::list<COutputEntry> &listSent, Amount &nFee,
std::string &strSentAccount,
const isminefilter &filter) const {
nFee = Amount::zero();
listReceived.clear();
listSent.clear();
strSentAccount = strFromAccount;
// Compute fee:
Amount nDebit = GetDebit(filter);
// debit>0 means we signed/sent this transaction.
if (nDebit > Amount::zero()) {
Amount nValueOut = tx->GetValueOut();
nFee = (nDebit - nValueOut);
}
// Sent/received.
for (unsigned int i = 0; i < tx->vout.size(); ++i) {
const CTxOut &txout = tx->vout[i];
isminetype fIsMine = pwallet->IsMine(txout);
// Only need to handle txouts if AT LEAST one of these is true:
// 1) they debit from us (sent)
// 2) the output is to us (received)
if (nDebit > Amount::zero()) {
// Don't report 'change' txouts
if (pwallet->IsChange(txout)) {
continue;
}
} else if (!(fIsMine & filter)) {
continue;
}
// In either case, we need to get the destination address.
CTxDestination address;
if (!ExtractDestination(txout.scriptPubKey, address) &&
!txout.scriptPubKey.IsUnspendable()) {
LogPrintf("CWalletTx::GetAmounts: Unknown transaction type found, "
"txid %s\n",
this->GetId().ToString());
address = CNoDestination();
}
COutputEntry output = {address, txout.nValue, (int)i};
// If we are debited by the transaction, add the output as a "sent"
// entry.
if (nDebit > Amount::zero()) {
listSent.push_back(output);
}
// If we are receiving the output, add it as a "received" entry.
if (fIsMine & filter) {
listReceived.push_back(output);
}
}
}
/**
* Scan active chain for relevant transactions after importing keys. This should
* be called whenever new keys are added to the wallet, with the oldest key
* creation time.
*
* @return Earliest timestamp that could be successfully scanned from. Timestamp
* returned will be higher than startTime if relevant blocks could not be read.
*/
int64_t CWallet::RescanFromTime(int64_t startTime,
const WalletRescanReserver &reserver,
bool update) {
// Find starting block. May be null if nCreateTime is greater than the
// highest blockchain timestamp, in which case there is nothing that needs
// to be scanned.
CBlockIndex *startBlock = nullptr;
{
LOCK(cs_main);
startBlock =
chainActive.FindEarliestAtLeast(startTime - TIMESTAMP_WINDOW);
LogPrintf("%s: Rescanning last %i blocks\n", __func__,
startBlock ? chainActive.Height() - startBlock->nHeight + 1
: 0);
}
if (startBlock) {
const CBlockIndex *const failedBlock =
ScanForWalletTransactions(startBlock, nullptr, reserver, update);
if (failedBlock) {
return failedBlock->GetBlockTimeMax() + TIMESTAMP_WINDOW + 1;
}
}
return startTime;
}
/**
* Scan the block chain (starting in pindexStart) for transactions from or to
* us. If fUpdate is true, found transactions that already exist in the wallet
* will be updated.
*
* Returns null if scan was successful. Otherwise, if a complete rescan was not
* possible (due to pruning or corruption), returns pointer to the most recent
* block that could not be scanned.
*
* If pindexStop is not a nullptr, the scan will stop at the block-index
* defined by pindexStop
*
* Caller needs to make sure pindexStop (and the optional pindexStart) are on
* the main chain after to the addition of any new keys you want to detect
* transactions for.
*/
CBlockIndex *CWallet::ScanForWalletTransactions(
CBlockIndex *pindexStart, CBlockIndex *pindexStop,
const WalletRescanReserver &reserver, bool fUpdate) {
int64_t nNow = GetTime();
assert(reserver.isReserved());
if (pindexStop) {
assert(pindexStop->nHeight >= pindexStart->nHeight);
}
CBlockIndex *pindex = pindexStart;
CBlockIndex *ret = nullptr;
if (pindex) {
LogPrintf("Rescan started from block %d...\n", pindex->nHeight);
}
{
fAbortRescan = false;
// Show rescan progress in GUI as dialog or on splashscreen, if -rescan
// on startup.
ShowProgress(_("Rescanning..."), 0);
CBlockIndex *tip = nullptr;
double progress_begin;
double progress_end;
{
LOCK(cs_main);
progress_begin =
GuessVerificationProgress(chainParams.TxData(), pindex);
if (pindexStop == nullptr) {
tip = chainActive.Tip();
progress_end =
GuessVerificationProgress(chainParams.TxData(), tip);
} else {
progress_end =
GuessVerificationProgress(chainParams.TxData(), pindexStop);
}
}
double progress_current = progress_begin;
while (pindex && !fAbortRescan && !ShutdownRequested()) {
if (pindex->nHeight % 100 == 0 &&
progress_end - progress_begin > 0.0) {
ShowProgress(
_("Rescanning..."),
std::max(
1,
std::min(99, (int)((progress_current - progress_begin) /
(progress_end - progress_begin) *
100))));
}
if (GetTime() >= nNow + 60) {
nNow = GetTime();
LogPrintf("Still rescanning. At block %d. Progress=%f\n",
pindex->nHeight, progress_current);
}
CBlock block;
if (ReadBlockFromDisk(block, pindex, chainParams.GetConsensus())) {
LOCK2(cs_main, cs_wallet);
if (pindex && !chainActive.Contains(pindex)) {
// Abort scan if current block is no longer active, to
// prevent marking transactions as coming from the wrong
// block.
ret = pindex;
break;
}
for (size_t posInBlock = 0; posInBlock < block.vtx.size();
++posInBlock) {
AddToWalletIfInvolvingMe(block.vtx[posInBlock], pindex,
posInBlock, fUpdate);
}
} else {
ret = pindex;
}
if (pindex == pindexStop) {
break;
}
{
LOCK(cs_main);
pindex = chainActive.Next(pindex);
progress_current =
GuessVerificationProgress(chainParams.TxData(), pindex);
if (pindexStop == nullptr && tip != chainActive.Tip()) {
tip = chainActive.Tip();
// in case the tip has changed, update progress max
progress_end =
GuessVerificationProgress(chainParams.TxData(), tip);
}
}
}
if (pindex && fAbortRescan) {
LogPrintf("Rescan aborted at block %d. Progress=%f\n",
pindex->nHeight, progress_current);
} else if (pindex && ShutdownRequested()) {
LogPrintf("Rescan interrupted by shutdown request at block %d. "
"Progress=%f\n",
pindex->nHeight, progress_current);
}
// Hide progress dialog in GUI.
ShowProgress(_("Rescanning..."), 100);
}
return ret;
}
void CWallet::ReacceptWalletTransactions() {
// If transactions aren't being broadcasted, don't let them into local
// mempool either.
if (!fBroadcastTransactions) {
return;
}
LOCK2(cs_main, cs_wallet);
std::map<int64_t, CWalletTx *> mapSorted;
// Sort pending wallet transactions based on their initial wallet insertion
// order.
for (std::pair<const TxId, CWalletTx> &item : mapWallet) {
const TxId &wtxid = item.first;
CWalletTx &wtx = item.second;
assert(wtx.GetId() == wtxid);
int nDepth = wtx.GetDepthInMainChain();
if (!wtx.IsCoinBase() && (nDepth == 0 && !wtx.isAbandoned())) {
mapSorted.insert(std::make_pair(wtx.nOrderPos, &wtx));
}
}
// Try to add wallet transactions to memory pool.
for (const std::pair<const int64_t, CWalletTx *> &item : mapSorted) {
CWalletTx &wtx = *(item.second);
CValidationState state;
wtx.AcceptToMemoryPool(maxTxFee, state);
}
}
bool CWalletTx::RelayWalletTransaction(CConnman *connman) {
assert(pwallet->GetBroadcastTransactions());
if (IsCoinBase() || isAbandoned() || GetDepthInMainChain() != 0) {
return false;
}
CValidationState state;
// GetDepthInMainChain already catches known conflicts.
if (InMempool() || AcceptToMemoryPool(maxTxFee, state)) {
LogPrintf("Relaying wtx %s\n", GetId().ToString());
if (connman) {
CInv inv(MSG_TX, GetId());
connman->ForEachNode(
[&inv](CNode *pnode) { pnode->PushInventory(inv); });
return true;
}
}
return false;
}
std::set<TxId> CWalletTx::GetConflicts() const {
std::set<TxId> result;
if (pwallet != nullptr) {
const TxId &txid = GetId();
result = pwallet->GetConflicts(txid);
result.erase(txid);
}
return result;
}
Amount CWalletTx::GetDebit(const isminefilter &filter) const {
if (tx->vin.empty()) {
return Amount::zero();
}
Amount debit = Amount::zero();
if (filter & ISMINE_SPENDABLE) {
if (fDebitCached) {
debit += nDebitCached;
} else {
nDebitCached = pwallet->GetDebit(*tx, ISMINE_SPENDABLE);
fDebitCached = true;
debit += nDebitCached;
}
}
if (filter & ISMINE_WATCH_ONLY) {
if (fWatchDebitCached) {
debit += nWatchDebitCached;
} else {
nWatchDebitCached = pwallet->GetDebit(*tx, ISMINE_WATCH_ONLY);
fWatchDebitCached = true;
debit += Amount(nWatchDebitCached);
}
}
return debit;
}
Amount CWalletTx::GetCredit(const isminefilter &filter) const {
// Must wait until coinbase is safely deep enough in the chain before
// valuing it.
if (IsImmatureCoinBase()) {
return Amount::zero();
}
Amount credit = Amount::zero();
if (filter & ISMINE_SPENDABLE) {
// GetBalance can assume transactions in mapWallet won't change.
if (fCreditCached) {
credit += nCreditCached;
} else {
nCreditCached = pwallet->GetCredit(*tx, ISMINE_SPENDABLE);
fCreditCached = true;
credit += nCreditCached;
}
}
if (filter & ISMINE_WATCH_ONLY) {
if (fWatchCreditCached) {
credit += nWatchCreditCached;
} else {
nWatchCreditCached = pwallet->GetCredit(*tx, ISMINE_WATCH_ONLY);
fWatchCreditCached = true;
credit += nWatchCreditCached;
}
}
return credit;
}
Amount CWalletTx::GetImmatureCredit(bool fUseCache) const {
if (IsImmatureCoinBase() && IsInMainChain()) {
if (fUseCache && fImmatureCreditCached) {
return nImmatureCreditCached;
}
nImmatureCreditCached = pwallet->GetCredit(*tx, ISMINE_SPENDABLE);
fImmatureCreditCached = true;
return nImmatureCreditCached;
}
return Amount::zero();
}
Amount CWalletTx::GetAvailableCredit(bool fUseCache) const {
if (pwallet == nullptr) {
return Amount::zero();
}
// Must wait until coinbase is safely deep enough in the chain before
// valuing it.
if (IsImmatureCoinBase()) {
return Amount::zero();
}
if (fUseCache && fAvailableCreditCached) {
return nAvailableCreditCached;
}
Amount nCredit = Amount::zero();
const TxId &txid = GetId();
for (uint32_t i = 0; i < tx->vout.size(); i++) {
if (!pwallet->IsSpent(COutPoint(txid, i))) {
const CTxOut &txout = tx->vout[i];
nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE);
if (!MoneyRange(nCredit)) {
throw std::runtime_error(std::string(__func__) +
" : value out of range");
}
}
}
nAvailableCreditCached = nCredit;
fAvailableCreditCached = true;
return nCredit;
}
Amount CWalletTx::GetImmatureWatchOnlyCredit(const bool fUseCache) const {
if (IsImmatureCoinBase() && IsInMainChain()) {
if (fUseCache && fImmatureWatchCreditCached) {
return nImmatureWatchCreditCached;
}
nImmatureWatchCreditCached = pwallet->GetCredit(*tx, ISMINE_WATCH_ONLY);
fImmatureWatchCreditCached = true;
return nImmatureWatchCreditCached;
}
return Amount::zero();
}
Amount CWalletTx::GetAvailableWatchOnlyCredit(const bool fUseCache) const {
if (pwallet == nullptr) {
return Amount::zero();
}
// Must wait until coinbase is safely deep enough in the chain before
// valuing it.
if (IsCoinBase() && GetBlocksToMaturity() > 0) {
return Amount::zero();
}
if (fUseCache && fAvailableWatchCreditCached) {
return nAvailableWatchCreditCached;
}
Amount nCredit = Amount::zero();
const TxId &txid = GetId();
for (uint32_t i = 0; i < tx->vout.size(); i++) {
if (!pwallet->IsSpent(COutPoint(txid, i))) {
const CTxOut &txout = tx->vout[i];
nCredit += pwallet->GetCredit(txout, ISMINE_WATCH_ONLY);
if (!MoneyRange(nCredit)) {
throw std::runtime_error(std::string(__func__) +
": value out of range");
}
}
}
nAvailableWatchCreditCached = nCredit;
fAvailableWatchCreditCached = true;
return nCredit;
}
Amount CWalletTx::GetChange() const {
if (fChangeCached) {
return nChangeCached;
}
nChangeCached = pwallet->GetChange(*tx);
fChangeCached = true;
return nChangeCached;
}
bool CWalletTx::InMempool() const {
return fInMempool;
}
bool CWalletTx::IsTrusted() const {
// Quick answer in most cases
if (!CheckFinalTx(*tx)) {
return false;
}
int nDepth = GetDepthInMainChain();
if (nDepth >= 1) {
return true;
}
if (nDepth < 0) {
return false;
}
// using wtx's cached debit
if (!pwallet->m_spend_zero_conf_change || !IsFromMe(ISMINE_ALL)) {
return false;
}
// Don't trust unconfirmed transactions from us unless they are in the
// mempool.
if (!InMempool()) {
return false;
}
// Trusted if all inputs are from us and are in the mempool:
for (const CTxIn &txin : tx->vin) {
// Transactions not sent by us: not trusted
const CWalletTx *parent = pwallet->GetWalletTx(txin.prevout.GetTxId());
if (parent == nullptr) {
return false;
}
const CTxOut &parentOut = parent->tx->vout[txin.prevout.GetN()];
if (pwallet->IsMine(parentOut) != ISMINE_SPENDABLE) {
return false;
}
}
return true;
}
bool CWalletTx::IsEquivalentTo(const CWalletTx &_tx) const {
CMutableTransaction tx1{*this->tx};
CMutableTransaction tx2{*_tx.tx};
for (auto &txin : tx1.vin) {
txin.scriptSig = CScript();
}
for (auto &txin : tx2.vin) {
txin.scriptSig = CScript();
}
return CTransaction(tx1) == CTransaction(tx2);
}
std::vector<uint256>
CWallet::ResendWalletTransactionsBefore(int64_t nTime, CConnman *connman) {
std::vector<uint256> result;
LOCK(cs_wallet);
// Sort them in chronological order
std::multimap<unsigned int, CWalletTx *> mapSorted;
for (std::pair<const TxId, CWalletTx> &item : mapWallet) {
CWalletTx &wtx = item.second;
// Don't rebroadcast if newer than nTime:
if (wtx.nTimeReceived > nTime) {
continue;
}
mapSorted.insert(std::make_pair(wtx.nTimeReceived, &wtx));
}
for (const std::pair<const unsigned int, CWalletTx *> &item : mapSorted) {
CWalletTx &wtx = *item.second;
if (wtx.RelayWalletTransaction(connman)) {
result.push_back(wtx.GetId());
}
}
return result;
}
void CWallet::ResendWalletTransactions(int64_t nBestBlockTime,
CConnman *connman) {
// Do this infrequently and randomly to avoid giving away that these are our
// transactions.
if (GetTime() < nNextResend || !fBroadcastTransactions) {
return;
}
bool fFirst = (nNextResend == 0);
nNextResend = GetTime() + GetRand(30 * 60);
if (fFirst) {
return;
}
// Only do it if there's been a new block since last time
if (nBestBlockTime < nLastResend) {
return;
}
nLastResend = GetTime();
// Rebroadcast unconfirmed txes older than 5 minutes before the last block
// was found:
std::vector<uint256> relayed =
ResendWalletTransactionsBefore(nBestBlockTime - 5 * 60, connman);
if (!relayed.empty()) {
LogPrintf("%s: rebroadcast %u unconfirmed transactions\n", __func__,
relayed.size());
}
}
/** @} */ // end of mapWallet
/**
* @defgroup Actions
*
* @{
*/
Amount CWallet::GetBalance() const {
LOCK2(cs_main, cs_wallet);
Amount nTotal = Amount::zero();
for (const auto &entry : mapWallet) {
const CWalletTx *pcoin = &entry.second;
if (pcoin->IsTrusted()) {
nTotal += pcoin->GetAvailableCredit();
}
}
return nTotal;
}
Amount CWallet::GetUnconfirmedBalance() const {
LOCK2(cs_main, cs_wallet);
Amount nTotal = Amount::zero();
for (const auto &entry : mapWallet) {
const CWalletTx *pcoin = &entry.second;
if (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 &&
pcoin->InMempool()) {
nTotal += pcoin->GetAvailableCredit();
}
}
return nTotal;
}
Amount CWallet::GetImmatureBalance() const {
LOCK2(cs_main, cs_wallet);
Amount nTotal = Amount::zero();
for (const auto &entry : mapWallet) {
const CWalletTx *pcoin = &entry.second;
nTotal += pcoin->GetImmatureCredit();
}
return nTotal;
}
Amount CWallet::GetWatchOnlyBalance() const {
LOCK2(cs_main, cs_wallet);
Amount nTotal = Amount::zero();
for (const auto &entry : mapWallet) {
const CWalletTx *pcoin = &entry.second;
if (pcoin->IsTrusted()) {
nTotal += pcoin->GetAvailableWatchOnlyCredit();
}
}
return nTotal;
}
Amount CWallet::GetUnconfirmedWatchOnlyBalance() const {
LOCK2(cs_main, cs_wallet);
Amount nTotal = Amount::zero();
for (const auto &entry : mapWallet) {
const CWalletTx *pcoin = &entry.second;
if (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 &&
pcoin->InMempool()) {
nTotal += pcoin->GetAvailableWatchOnlyCredit();
}
}
return nTotal;
}
Amount CWallet::GetImmatureWatchOnlyBalance() const {
LOCK2(cs_main, cs_wallet);
Amount nTotal = Amount::zero();
for (const auto &entry : mapWallet) {
const CWalletTx *pcoin = &entry.second;
nTotal += pcoin->GetImmatureWatchOnlyCredit();
}
return nTotal;
}
// Calculate total balance in a different way from GetBalance. The biggest
// difference is that GetBalance sums up all unspent TxOuts paying to the
// wallet, while this sums up both spent and unspent TxOuts paying to the
// wallet, and then subtracts the values of TxIns spending from the wallet. This
// also has fewer restrictions on which unconfirmed transactions are considered
// trusted.
Amount CWallet::GetLegacyBalance(const isminefilter &filter, int minDepth,
const std::string *account) const {
LOCK2(cs_main, cs_wallet);
Amount balance = Amount::zero();
for (const auto &entry : mapWallet) {
const CWalletTx &wtx = entry.second;
const int depth = wtx.GetDepthInMainChain();
if (depth < 0 || !CheckFinalTx(*wtx.tx) || wtx.IsImmatureCoinBase()) {
continue;
}
// Loop through tx outputs and add incoming payments. For outgoing txs,
// treat change outputs specially, as part of the amount debited.
Amount debit = wtx.GetDebit(filter);
const bool outgoing = debit > Amount::zero();
for (const CTxOut &out : wtx.tx->vout) {
if (outgoing && IsChange(out)) {
debit -= out.nValue;
} else if (IsMine(out) & filter && depth >= minDepth &&
(!account ||
*account == GetLabelName(out.scriptPubKey))) {
balance += out.nValue;
}
}
// For outgoing txs, subtract amount debited.
if (outgoing && (!account || *account == wtx.strFromAccount)) {
balance -= debit;
}
}
if (account) {
balance += WalletBatch(*database).GetAccountCreditDebit(*account);
}
return balance;
}
Amount CWallet::GetAvailableBalance(const CCoinControl *coinControl) const {
LOCK2(cs_main, cs_wallet);
Amount balance = Amount::zero();
std::vector<COutput> vCoins;
AvailableCoins(vCoins, true, coinControl);
for (const COutput &out : vCoins) {
if (out.fSpendable) {
balance += out.tx->tx->vout[out.i].nValue;
}
}
return balance;
}
void CWallet::AvailableCoins(std::vector<COutput> &vCoins, bool fOnlySafe,
const CCoinControl *coinControl,
const Amount nMinimumAmount,
const Amount nMaximumAmount,
const Amount nMinimumSumAmount,
const uint64_t nMaximumCount, const int nMinDepth,
const int nMaxDepth) const {
AssertLockHeld(cs_main);
AssertLockHeld(cs_wallet);
vCoins.clear();
Amount nTotal = Amount::zero();
for (const auto &entry : mapWallet) {
const TxId &wtxid = entry.first;
const CWalletTx *pcoin = &entry.second;
if (!CheckFinalTx(*pcoin->tx)) {
continue;
}
if (pcoin->IsImmatureCoinBase()) {
continue;
}
int nDepth = pcoin->GetDepthInMainChain();
if (nDepth < 0) {
continue;
}
// We should not consider coins which aren't at least in our mempool.
// It's possible for these to be conflicted via ancestors which we may
// never be able to detect.
if (nDepth == 0 && !pcoin->InMempool()) {
continue;
}
bool safeTx = pcoin->IsTrusted();
// Bitcoin-ABC: Removed check that prevents consideration of coins from
// transactions that are replacing other transactions. This check based
// on pcoin->mapValue.count("replaces_txid") which was not being set
// anywhere.
// Similarly, we should not consider coins from transactions that have
// been replaced. In the example above, we would want to prevent
// creation of a transaction A' spending an output of A, because if
// transaction B were initially confirmed, conflicting with A and A', we
// wouldn't want to the user to create a transaction D intending to
// replace A', but potentially resulting in a scenario where A, A', and
// D could all be accepted (instead of just B and D, or just A and A'
// like the user would want).
// Bitcoin-ABC: retained this check as 'replaced_by_txid' is still set
// in the wallet code.
if (nDepth == 0 && pcoin->mapValue.count("replaced_by_txid")) {
safeTx = false;
}
if (fOnlySafe && !safeTx) {
continue;
}
if (nDepth < nMinDepth || nDepth > nMaxDepth) {
continue;
}
for (uint32_t i = 0; i < pcoin->tx->vout.size(); i++) {
if (pcoin->tx->vout[i].nValue < nMinimumAmount ||
pcoin->tx->vout[i].nValue > nMaximumAmount) {
continue;
}
const COutPoint outpoint(wtxid, i);
if (coinControl && coinControl->HasSelected() &&
!coinControl->fAllowOtherInputs &&
!coinControl->IsSelected(outpoint)) {
continue;
}
if (IsLockedCoin(outpoint)) {
continue;
}
if (IsSpent(outpoint)) {
continue;
}
isminetype mine = IsMine(pcoin->tx->vout[i]);
if (mine == ISMINE_NO) {
continue;
}
bool fSpendableIn = ((mine & ISMINE_SPENDABLE) != ISMINE_NO) ||
(coinControl && coinControl->fAllowWatchOnly &&
(mine & ISMINE_WATCH_SOLVABLE) != ISMINE_NO);
bool fSolvableIn =
(mine & (ISMINE_SPENDABLE | ISMINE_WATCH_SOLVABLE)) !=
ISMINE_NO;
vCoins.push_back(
COutput(pcoin, i, nDepth, fSpendableIn, fSolvableIn, safeTx));
// Checks the sum amount of all UTXO's.
if (nMinimumSumAmount != MAX_MONEY) {
nTotal += pcoin->tx->vout[i].nValue;
if (nTotal >= nMinimumSumAmount) {
return;
}
}
// Checks the maximum number of UTXO's.
if (nMaximumCount > 0 && vCoins.size() >= nMaximumCount) {
return;
}
}
}
}
std::map<CTxDestination, std::vector<COutput>> CWallet::ListCoins() const {
AssertLockHeld(cs_main);
AssertLockHeld(cs_wallet);
std::map<CTxDestination, std::vector<COutput>> result;
std::vector<COutput> availableCoins;
AvailableCoins(availableCoins);
for (const auto &coin : availableCoins) {
CTxDestination address;
if (coin.fSpendable &&
ExtractDestination(
FindNonChangeParentOutput(*coin.tx->tx, coin.i).scriptPubKey,
address)) {
result[address].emplace_back(std::move(coin));
}
}
std::vector<COutPoint> lockedCoins;
ListLockedCoins(lockedCoins);
for (const auto &output : lockedCoins) {
auto it = mapWallet.find(output.GetTxId());
if (it != mapWallet.end()) {
int depth = it->second.GetDepthInMainChain();
if (depth >= 0 && output.GetN() < it->second.tx->vout.size() &&
IsMine(it->second.tx->vout[output.GetN()]) ==
ISMINE_SPENDABLE) {
CTxDestination address;
if (ExtractDestination(
FindNonChangeParentOutput(*it->second.tx, output.GetN())
.scriptPubKey,
address)) {
result[address].emplace_back(
&it->second, output.GetN(), depth, true /* spendable */,
true /* solvable */, false /* safe */);
}
}
}
}
return result;
}
const CTxOut &CWallet::FindNonChangeParentOutput(const CTransaction &tx,
int output) const {
const CTransaction *ptx = &tx;
int n = output;
while (IsChange(ptx->vout[n]) && ptx->vin.size() > 0) {
const COutPoint &prevout = ptx->vin[0].prevout;
auto it = mapWallet.find(prevout.GetTxId());
if (it == mapWallet.end() ||
it->second.tx->vout.size() <= prevout.GetN() ||
!IsMine(it->second.tx->vout[prevout.GetN()])) {
break;
}
ptx = it->second.tx.get();
n = prevout.GetN();
}
return ptx->vout[n];
}
bool CWallet::SelectCoinsMinConf(
const Amount nTargetValue, const CoinEligibilityFilter &eligibility_filter,
std::vector<OutputGroup> groups, std::set<CInputCoin> &setCoinsRet,
Amount &nValueRet, const CoinSelectionParams &coin_selection_params,
bool &bnb_used) const {
setCoinsRet.clear();
nValueRet = Amount::zero();
std::vector<OutputGroup> utxo_pool;
if (coin_selection_params.use_bnb) {
// Get long term estimate
CCoinControl temp;
temp.m_confirm_target = 1008;
CFeeRate long_term_feerate = GetMinimumFeeRate(*this, temp, g_mempool);
// Calculate cost of change
Amount cost_of_change =
dustRelayFee.GetFee(coin_selection_params.change_spend_size) +
coin_selection_params.effective_fee.GetFee(
coin_selection_params.change_output_size);
// Filter by the min conf specs and add to utxo_pool and calculate
// effective value
for (OutputGroup &group : groups) {
if (!group.EligibleForSpending(eligibility_filter)) {
continue;
}
group.fee = Amount::zero();
group.long_term_fee = Amount::zero();
group.effective_value = Amount::zero();
for (auto it = group.m_outputs.begin();
it != group.m_outputs.end();) {
const CInputCoin &coin = *it;
Amount effective_value =
coin.txout.nValue -
(coin.m_input_bytes < 0
? Amount::zero()
: coin_selection_params.effective_fee.GetFee(
coin.m_input_bytes));
// Only include outputs that are positive effective value (i.e.
// not dust)
if (effective_value > Amount::zero()) {
group.fee +=
coin.m_input_bytes < 0
? Amount::zero()
: coin_selection_params.effective_fee.GetFee(
coin.m_input_bytes);
group.long_term_fee +=
coin.m_input_bytes < 0
? Amount::zero()
: long_term_feerate.GetFee(coin.m_input_bytes);
group.effective_value += effective_value;
++it;
} else {
it = group.Discard(coin);
}
}
if (group.effective_value > Amount::zero()) {
utxo_pool.push_back(group);
}
}
// Calculate the fees for things that aren't inputs
Amount not_input_fees = coin_selection_params.effective_fee.GetFee(
coin_selection_params.tx_noinputs_size);
bnb_used = true;
return SelectCoinsBnB(utxo_pool, nTargetValue, cost_of_change,
setCoinsRet, nValueRet, not_input_fees);
} else {
// Filter by the min conf specs and add to utxo_pool
for (const OutputGroup &group : groups) {
if (!group.EligibleForSpending(eligibility_filter)) {
continue;
}
utxo_pool.push_back(group);
}
bnb_used = false;
return KnapsackSolver(nTargetValue, utxo_pool, setCoinsRet, nValueRet);
}
}
bool CWallet::SelectCoins(const std::vector<COutput> &vAvailableCoins,
const Amount nTargetValue,
std::set<CInputCoin> &setCoinsRet, Amount &nValueRet,
const CCoinControl &coin_control,
CoinSelectionParams &coin_selection_params,
bool &bnb_used) const {
std::vector<COutput> vCoins(vAvailableCoins);
// coin control -> return all selected outputs (we want all selected to go
// into the transaction for sure)
if (coin_control.HasSelected() && !coin_control.fAllowOtherInputs) {
// We didn't use BnB here, so set it to false.
bnb_used = false;
for (const COutput &out : vCoins) {
if (!out.fSpendable) {
continue;
}
nValueRet += out.tx->tx->vout[out.i].nValue;
setCoinsRet.insert(out.GetInputCoin());
}
return (nValueRet >= nTargetValue);
}
// Calculate value from preset inputs and store them.
std::set<CInputCoin> setPresetCoins;
Amount nValueFromPresetInputs = Amount::zero();
std::vector<COutPoint> vPresetInputs;
coin_control.ListSelected(vPresetInputs);
for (const COutPoint &outpoint : vPresetInputs) {
// For now, don't use BnB if preset inputs are selected. TODO: Enable
// this later
bnb_used = false;
coin_selection_params.use_bnb = false;
std::map<TxId, CWalletTx>::const_iterator it =
mapWallet.find(outpoint.GetTxId());
if (it == mapWallet.end()) {
// TODO: Allow non-wallet inputs
return false;
}
const CWalletTx *pcoin = &it->second;
// Clearly invalid input, fail.
if (pcoin->tx->vout.size() <= outpoint.GetN()) {
return false;
}
// Just to calculate the marginal byte size
nValueFromPresetInputs += pcoin->tx->vout[outpoint.GetN()].nValue;
setPresetCoins.insert(CInputCoin(pcoin->tx, outpoint.GetN()));
}
// Remove preset inputs from vCoins
for (std::vector<COutput>::iterator it = vCoins.begin();
it != vCoins.end() && coin_control.HasSelected();) {
if (setPresetCoins.count(it->GetInputCoin())) {
it = vCoins.erase(it);
} else {
++it;
}
}
// form groups from remaining coins; note that preset coins will not
// automatically have their associated (same address) coins included
if (coin_control.m_avoid_partial_spends &&
vCoins.size() > OUTPUT_GROUP_MAX_ENTRIES) {
// Cases where we have 11+ outputs all pointing to the same destination
// may result in privacy leaks as they will potentially be
// deterministically sorted. We solve that by explicitly shuffling the
// outputs before processing
Shuffle(vCoins.begin(), vCoins.end(), FastRandomContext());
}
std::vector<OutputGroup> groups =
GroupOutputs(vCoins, !coin_control.m_avoid_partial_spends);
size_t max_ancestors = std::max<size_t>(
1, gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT));
size_t max_descendants = std::max<size_t>(
1, gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT));
bool fRejectLongChains = gArgs.GetBoolArg(
"-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS);
bool res =
nTargetValue <= nValueFromPresetInputs ||
SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs,
CoinEligibilityFilter(1, 6, 0), groups, setCoinsRet,
nValueRet, coin_selection_params, bnb_used) ||
SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs,
CoinEligibilityFilter(1, 1, 0), groups, setCoinsRet,
nValueRet, coin_selection_params, bnb_used) ||
(m_spend_zero_conf_change &&
SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs,
CoinEligibilityFilter(0, 1, 2), groups, setCoinsRet,
nValueRet, coin_selection_params, bnb_used)) ||
(m_spend_zero_conf_change &&
SelectCoinsMinConf(
nTargetValue - nValueFromPresetInputs,
CoinEligibilityFilter(0, 1, std::min<size_t>(4, max_ancestors / 3),
std::min<size_t>(4, max_descendants / 3)),
groups, setCoinsRet, nValueRet, coin_selection_params,
bnb_used)) ||
(m_spend_zero_conf_change &&
SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs,
CoinEligibilityFilter(0, 1, max_ancestors / 2,
max_descendants / 2),
groups, setCoinsRet, nValueRet,
coin_selection_params, bnb_used)) ||
(m_spend_zero_conf_change &&
SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs,
CoinEligibilityFilter(0, 1, max_ancestors - 1,
max_descendants - 1),
groups, setCoinsRet, nValueRet,
coin_selection_params, bnb_used)) ||
(m_spend_zero_conf_change && !fRejectLongChains &&
SelectCoinsMinConf(
nTargetValue - nValueFromPresetInputs,
CoinEligibilityFilter(0, 1, std::numeric_limits<uint64_t>::max()),
groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
// Because SelectCoinsMinConf clears the setCoinsRet, we now add the
// possible inputs to the coinset.
util::insert(setCoinsRet, setPresetCoins);
// Add preset inputs to the total value selected.
nValueRet += nValueFromPresetInputs;
return res;
}
bool CWallet::SignTransaction(CMutableTransaction &tx) {
// sign the new tx
int nIn = 0;
for (CTxIn &input : tx.vin) {
auto mi = mapWallet.find(input.prevout.GetTxId());
if (mi == mapWallet.end() ||
input.prevout.GetN() >= mi->second.tx->vout.size()) {
return false;
}
const CScript &scriptPubKey =
mi->second.tx->vout[input.prevout.GetN()].scriptPubKey;
const Amount amount = mi->second.tx->vout[input.prevout.GetN()].nValue;
SignatureData sigdata;
SigHashType sigHashType = SigHashType().withForkId();
if (!ProduceSignature(*this,
MutableTransactionSignatureCreator(
&tx, nIn, amount, sigHashType),
scriptPubKey, sigdata)) {
return false;
}
UpdateInput(input, sigdata);
nIn++;
}
return true;
}
bool CWallet::FundTransaction(CMutableTransaction &tx, Amount &nFeeRet,
int &nChangePosInOut, std::string &strFailReason,
bool lockUnspents,
const std::set<int> &setSubtractFeeFromOutputs,
CCoinControl coinControl) {
std::vector<CRecipient> vecSend;
// Turn the txout set into a CRecipient vector.
for (size_t idx = 0; idx < tx.vout.size(); idx++) {
const CTxOut &txOut = tx.vout[idx];
CRecipient recipient = {txOut.scriptPubKey, txOut.nValue,
setSubtractFeeFromOutputs.count(idx) == 1};
vecSend.push_back(recipient);
}
coinControl.fAllowOtherInputs = true;
for (const CTxIn &txin : tx.vin) {
coinControl.Select(txin.prevout);
}
// Acquire the locks to prevent races to the new locked unspents between the
// CreateTransaction call and LockCoin calls (when lockUnspents is true).
LOCK2(cs_main, cs_wallet);
CReserveKey reservekey(this);
CTransactionRef tx_new;
if (!CreateTransaction(vecSend, tx_new, reservekey, nFeeRet,
nChangePosInOut, strFailReason, coinControl,
false)) {
return false;
}
if (nChangePosInOut != -1) {
tx.vout.insert(tx.vout.begin() + nChangePosInOut,
tx_new->vout[nChangePosInOut]);
// We don't have the normal Create/Commit cycle, and don't want to
// risk reusing change, so just remove the key from the keypool
// here.
reservekey.KeepKey();
}
// Copy output sizes from new transaction; they may have had the fee
// subtracted from them.
for (size_t idx = 0; idx < tx.vout.size(); idx++) {
tx.vout[idx].nValue = tx_new->vout[idx].nValue;
}
// Add new txins (keeping original txin scriptSig/order)
for (const CTxIn &txin : tx_new->vin) {
if (!coinControl.IsSelected(txin.prevout)) {
tx.vin.push_back(txin);
if (lockUnspents) {
LockCoin(txin.prevout);
}
}
}
return true;
}
OutputType
CWallet::TransactionChangeType(OutputType change_type,
const std::vector<CRecipient> &vecSend) {
// If -changetype is specified, always use that change type.
if (change_type != OutputType::CHANGE_AUTO) {
return change_type;
}
// if m_default_address_type is legacy, use legacy address as change.
if (m_default_address_type == OutputType::LEGACY) {
return OutputType::LEGACY;
}
// else use m_default_address_type for change
return m_default_address_type;
}
bool CWallet::CreateTransaction(const std::vector<CRecipient> &vecSend,
CTransactionRef &tx, CReserveKey &reservekey,
Amount &nFeeRet, int &nChangePosInOut,
std::string &strFailReason,
const CCoinControl &coinControl, bool sign) {
Amount nValue = Amount::zero();
int nChangePosRequest = nChangePosInOut;
unsigned int nSubtractFeeFromAmount = 0;
for (const auto &recipient : vecSend) {
if (nValue < Amount::zero() || recipient.nAmount < Amount::zero()) {
strFailReason = _("Transaction amounts must not be negative");
return false;
}
nValue += recipient.nAmount;
if (recipient.fSubtractFeeFromAmount) {
nSubtractFeeFromAmount++;
}
}
if (vecSend.empty()) {
strFailReason = _("Transaction must have at least one recipient");
return false;
}
CMutableTransaction txNew;
// Discourage fee sniping.
//
// For a large miner the value of the transactions in the best block and the
// mempool can exceed the cost of deliberately attempting to mine two blocks
// to orphan the current best block. By setting nLockTime such that only the
// next block can include the transaction, we discourage this practice as
// the height restricted and limited blocksize gives miners considering fee
// sniping fewer options for pulling off this attack.
//
// A simple way to think about this is from the wallet's point of view we
// always want the blockchain to move forward. By setting nLockTime this way
// we're basically making the statement that we only want this transaction
// to appear in the next block; we don't want to potentially encourage
// reorgs by allowing transactions to appear at lower heights than the next
// block in forks of the best chain.
//
// Of course, the subsidy is high enough, and transaction volume low enough,
// that fee sniping isn't a problem yet, but by implementing a fix now we
// ensure code won't be written that makes assumptions about nLockTime that
// preclude a fix later.
txNew.nLockTime = chainActive.Height();
// Secondly occasionally randomly pick a nLockTime even further back, so
// that transactions that are delayed after signing for whatever reason,
// e.g. high-latency mix networks and some CoinJoin implementations, have
// better privacy.
if (GetRandInt(10) == 0) {
txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100));
}
assert(txNew.nLockTime <= (unsigned int)chainActive.Height());
assert(txNew.nLockTime < LOCKTIME_THRESHOLD);
{
std::set<CInputCoin> setCoins;
LOCK2(cs_main, cs_wallet);
std::vector<COutput> vAvailableCoins;
AvailableCoins(vAvailableCoins, true, &coinControl);
// Parameters for coin selection, init with dummy
CoinSelectionParams coin_selection_params;
// Create change script that will be used if we need change
// TODO: pass in scriptChange instead of reservekey so
// change transaction isn't always pay-to-bitcoin-address
CScript scriptChange;
// coin control: send change to custom address
if (!boost::get<CNoDestination>(&coinControl.destChange)) {
scriptChange = GetScriptForDestination(coinControl.destChange);
// no coin control: send change to newly generated address
} else {
// Note: We use a new key here to keep it from being obvious
// which side is the change.
// The drawback is that by not reusing a previous key, the
// change may be lost if a backup is restored, if the backup
// doesn't have the new private key for the change. If we
// reused the old key, it would be possible to add code to look
// for and rediscover unknown transactions that were written
// with keys of ours to recover post-backup change.
// Reserve a new key pair from key pool
CPubKey vchPubKey;
bool ret;
ret = reservekey.GetReservedKey(vchPubKey, true);
if (!ret) {
strFailReason =
_("Keypool ran out, please call keypoolrefill first");
return false;
}
const OutputType change_type = TransactionChangeType(
coinControl.m_change_type ? *coinControl.m_change_type
: m_default_change_type,
vecSend);
LearnRelatedScripts(vchPubKey, change_type);
scriptChange = GetScriptForDestination(
GetDestinationForKey(vchPubKey, change_type));
}
CTxOut change_prototype_txout(Amount::zero(), scriptChange);
coin_selection_params.change_output_size =
GetSerializeSize(change_prototype_txout, SER_DISK, 0);
// Get the fee rate to use effective values in coin selection
CFeeRate nFeeRateNeeded =
GetMinimumFeeRate(*this, coinControl, g_mempool);
nFeeRet = Amount::zero();
bool pick_new_inputs = true;
Amount nValueIn = Amount::zero();
// BnB selector is the only selector used when this is true.
// That should only happen on the first pass through the loop.
// If we are doing subtract fee from recipient, then don't use BnB
coin_selection_params.use_bnb = nSubtractFeeFromAmount == 0;
// Start with no fee and loop until there is enough fee
while (true) {
nChangePosInOut = nChangePosRequest;
txNew.vin.clear();
txNew.vout.clear();
bool fFirst = true;
Amount nValueToSelect = nValue;
if (nSubtractFeeFromAmount == 0) {
nValueToSelect += nFeeRet;
}
// Static vsize overhead + outputs vsize. 4 nVersion, 4 nLocktime, 1
// input count, 1 output count
coin_selection_params.tx_noinputs_size = 10;
// vouts to the payees
for (const auto &recipient : vecSend) {
CTxOut txout(recipient.nAmount, recipient.scriptPubKey);
if (recipient.fSubtractFeeFromAmount) {
assert(nSubtractFeeFromAmount != 0);
// Subtract fee equally from each selected recipient.
txout.nValue -= nFeeRet / int(nSubtractFeeFromAmount);
// First receiver pays the remainder not divisible by output
// count.
if (fFirst) {
fFirst = false;
txout.nValue -= nFeeRet % int(nSubtractFeeFromAmount);
}
}
// Include the fee cost for outputs. Note this is only used for
// BnB right now
coin_selection_params.tx_noinputs_size +=
::GetSerializeSize(txout, SER_NETWORK, PROTOCOL_VERSION);
if (IsDust(txout, dustRelayFee)) {
if (recipient.fSubtractFeeFromAmount &&
nFeeRet > Amount::zero()) {
if (txout.nValue < Amount::zero()) {
strFailReason = _("The transaction amount is "
"too small to pay the fee");
} else {
strFailReason =
_("The transaction amount is too small to "
"send after the fee has been deducted");
}
} else {
strFailReason = _("Transaction amount too small");
}
return false;
}
txNew.vout.push_back(txout);
}
// Choose coins to use
bool bnb_used;
if (pick_new_inputs) {
nValueIn = Amount::zero();
setCoins.clear();
coin_selection_params.change_spend_size =
CalculateMaximumSignedInputSize(change_prototype_txout,
this);
coin_selection_params.effective_fee = nFeeRateNeeded;
if (!SelectCoins(vAvailableCoins, nValueToSelect, setCoins,
nValueIn, coinControl, coin_selection_params,
bnb_used)) {
// If BnB was used, it was the first pass. No longer the
// first pass and continue loop with knapsack.
if (bnb_used) {
coin_selection_params.use_bnb = false;
continue;
} else {
strFailReason = _("Insufficient funds");
return false;
}
}
}
const Amount nChange = nValueIn - nValueToSelect;
if (nChange > Amount::zero()) {
// Fill a vout to ourself.
CTxOut newTxOut(nChange, scriptChange);
// Never create dust outputs; if we would, just add the dust to
// the fee.
// The nChange when BnB is used is always going to go to fees.
if (IsDust(newTxOut, dustRelayFee) || bnb_used) {
nChangePosInOut = -1;
nFeeRet += nChange;
} else {
if (nChangePosInOut == -1) {
// Insert change txn at random position:
nChangePosInOut = GetRandInt(txNew.vout.size() + 1);
} else if ((unsigned int)nChangePosInOut >
txNew.vout.size()) {
strFailReason = _("Change index out of range");
return false;
}
std::vector<CTxOut>::iterator position =
txNew.vout.begin() + nChangePosInOut;
txNew.vout.insert(position, newTxOut);
}
} else {
nChangePosInOut = -1;
}
// Dummy fill vin for maximum size estimation
//
for (const auto &coin : setCoins) {
txNew.vin.push_back(CTxIn(coin.outpoint, CScript()));
}
CTransaction txNewConst(txNew);
int nBytes = CalculateMaximumSignedTxSize(txNewConst, this);
if (nBytes < 0) {
strFailReason = _("Signing transaction failed");
return false;
}
Amount nFeeNeeded =
GetMinimumFee(*this, nBytes, coinControl, g_mempool);
// If we made it here and we aren't even able to meet the relay fee
// on the next pass, give up because we must be at the maximum
// allowed fee.
if (nFeeNeeded < ::minRelayTxFee.GetFee(nBytes)) {
strFailReason = _("Transaction too large for fee policy");
return false;
}
if (nFeeRet >= nFeeNeeded) {
// Reduce fee to only the needed amount if possible. This
// prevents potential overpayment in fees if the coins selected
// to meet nFeeNeeded result in a transaction that requires less
// fee than the prior iteration.
// If we have no change and a big enough excess fee, then try to
// construct transaction again only without picking new inputs.
// We now know we only need the smaller fee (because of reduced
// tx size) and so we should add a change output. Only try this
// once.
if (nChangePosInOut == -1 && nSubtractFeeFromAmount == 0 &&
pick_new_inputs) {
// Add 2 as a buffer in case increasing # of outputs changes
// compact size
unsigned int tx_size_with_change =
nBytes + coin_selection_params.change_output_size + 2;
Amount fee_needed_with_change = GetMinimumFee(
*this, tx_size_with_change, coinControl, g_mempool);
Amount minimum_value_for_change =
GetDustThreshold(change_prototype_txout, dustRelayFee);
if (nFeeRet >=
fee_needed_with_change + minimum_value_for_change) {
pick_new_inputs = false;
nFeeRet = fee_needed_with_change;
continue;
}
}
// If we have change output already, just increase it
if (nFeeRet > nFeeNeeded && nChangePosInOut != -1 &&
nSubtractFeeFromAmount == 0) {
Amount extraFeePaid = nFeeRet - nFeeNeeded;
std::vector<CTxOut>::iterator change_position =
txNew.vout.begin() + nChangePosInOut;
change_position->nValue += extraFeePaid;
nFeeRet -= extraFeePaid;
}
// Done, enough fee included.
break;
} else if (!pick_new_inputs) {
// This shouldn't happen, we should have had enough excess fee
// to pay for the new output and still meet nFeeNeeded.
// Or we should have just subtracted fee from recipients and
// nFeeNeeded should not have changed.
strFailReason =
_("Transaction fee and change calculation failed");
return false;
}
// Try to reduce change to include necessary fee.
if (nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) {
Amount additionalFeeNeeded = nFeeNeeded - nFeeRet;
std::vector<CTxOut>::iterator change_position =
txNew.vout.begin() + nChangePosInOut;
// Only reduce change if remaining amount is still a large
// enough output.
if (change_position->nValue >=
MIN_FINAL_CHANGE + additionalFeeNeeded) {
change_position->nValue -= additionalFeeNeeded;
nFeeRet += additionalFeeNeeded;
// Done, able to increase fee from change.
break;
}
}
// If subtracting fee from recipients, we now know what fee we
// need to subtract, we have no reason to reselect inputs.
if (nSubtractFeeFromAmount > 0) {
pick_new_inputs = false;
}
// Include more fee and try again.
nFeeRet = nFeeNeeded;
coin_selection_params.use_bnb = false;
continue;
}
if (nChangePosInOut == -1) {
// Return any reserved key if we don't have change
reservekey.ReturnKey();
}
// Shuffle selected coins and fill in final vin
txNew.vin.clear();
std::vector<CInputCoin> selected_coins(setCoins.begin(),
setCoins.end());
Shuffle(selected_coins.begin(), selected_coins.end(),
FastRandomContext());
// Note how the sequence number is set to non-maxint so that
// the nLockTime set above actually works.
for (const auto &coin : selected_coins) {
txNew.vin.push_back(
CTxIn(coin.outpoint, CScript(),
std::numeric_limits<uint32_t>::max() - 1));
}
if (sign) {
SigHashType sigHashType = SigHashType().withForkId();
int nIn = 0;
for (const auto &coin : selected_coins) {
const CScript &scriptPubKey = coin.txout.scriptPubKey;
SignatureData sigdata;
if (!ProduceSignature(
*this,
MutableTransactionSignatureCreator(
&txNew, nIn, coin.txout.nValue, sigHashType),
scriptPubKey, sigdata)) {
strFailReason = _("Signing transaction failed");
return false;
}
UpdateInput(txNew.vin.at(nIn), sigdata);
nIn++;
}
}
// Return the constructed transaction data.
tx = MakeTransactionRef(std::move(txNew));
// Limit size.
if (tx->GetTotalSize() >= MAX_STANDARD_TX_SIZE) {
strFailReason = _("Transaction too large");
return false;
}
}
if (gArgs.GetBoolArg("-walletrejectlongchains",
DEFAULT_WALLET_REJECT_LONG_CHAINS)) {
// Lastly, ensure this tx will pass the mempool's chain limits.
LockPoints lp;
CTxMemPoolEntry entry(tx, Amount::zero(), 0, 0, 0, Amount::zero(),
false, 0, lp);
CTxMemPool::setEntries setAncestors;
size_t nLimitAncestors =
gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT);
size_t nLimitAncestorSize =
gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) *
1000;
size_t nLimitDescendants =
gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT);
size_t nLimitDescendantSize =
gArgs.GetArg("-limitdescendantsize",
DEFAULT_DESCENDANT_SIZE_LIMIT) *
1000;
std::string errString;
LOCK(::g_mempool.cs);
if (!g_mempool.CalculateMemPoolAncestors(
entry, setAncestors, nLimitAncestors, nLimitAncestorSize,
nLimitDescendants, nLimitDescendantSize, errString)) {
strFailReason = _("Transaction has too long of a mempool chain");
return false;
}
}
return true;
}
/**
* Call after CreateTransaction unless you want to abort
*/
bool CWallet::CommitTransaction(
CTransactionRef tx, mapValue_t mapValue,
std::vector<std::pair<std::string, std::string>> orderForm,
std::string fromAccount, CReserveKey &reservekey, CConnman *connman,
CValidationState &state) {
LOCK2(cs_main, cs_wallet);
CWalletTx wtxNew(this, std::move(tx));
wtxNew.mapValue = std::move(mapValue);
wtxNew.vOrderForm = std::move(orderForm);
wtxNew.strFromAccount = std::move(fromAccount);
wtxNew.fTimeReceivedIsTxTime = true;
wtxNew.fFromMe = true;
LogPrintfToBeContinued("CommitTransaction:\n%s", wtxNew.tx->ToString());
// Take key pair from key pool so it won't be used again.
reservekey.KeepKey();
// Add tx to wallet, because if it has change it's also ours, otherwise just
// for transaction history.
AddToWallet(wtxNew);
// Notify that old coins are spent.
for (const CTxIn &txin : wtxNew.tx->vin) {
CWalletTx &coin = mapWallet.at(txin.prevout.GetTxId());
coin.BindWallet(this);
NotifyTransactionChanged(this, coin.GetId(), CT_UPDATED);
}
// Get the inserted-CWalletTx from mapWallet so that the
// fInMempool flag is cached properly
CWalletTx &wtx = mapWallet.at(wtxNew.GetId());
if (fBroadcastTransactions) {
// Broadcast
if (!wtx.AcceptToMemoryPool(maxTxFee, state)) {
LogPrintf("CommitTransaction(): Transaction cannot be broadcast "
"immediately, %s\n",
FormatStateMessage(state));
// TODO: if we expect the failure to be long term or permanent,
// instead delete wtx from the wallet and return failure.
} else {
wtx.RelayWalletTransaction(connman);
}
}
return true;
}
void CWallet::ListAccountCreditDebit(const std::string &strAccount,
std::list<CAccountingEntry> &entries) {
WalletBatch batch(*database);
return batch.ListAccountCreditDebit(strAccount, entries);
}
bool CWallet::AddAccountingEntry(const CAccountingEntry &acentry) {
WalletBatch batch(*database);
return AddAccountingEntry(acentry, &batch);
}
bool CWallet::AddAccountingEntry(const CAccountingEntry &acentry,
WalletBatch *batch) {
if (!batch->WriteAccountingEntry(++nAccountingEntryNumber, acentry)) {
return false;
}
laccentries.push_back(acentry);
CAccountingEntry &entry = laccentries.back();
wtxOrdered.insert(std::make_pair(entry.nOrderPos, TxPair(nullptr, &entry)));
return true;
}
DBErrors CWallet::LoadWallet(bool &fFirstRunRet) {
LOCK2(cs_main, cs_wallet);
fFirstRunRet = false;
DBErrors nLoadWalletRet = WalletBatch(*database, "cr+").LoadWallet(this);
if (nLoadWalletRet == DBErrors::NEED_REWRITE) {
if (database->Rewrite("\x04pool")) {
setInternalKeyPool.clear();
setExternalKeyPool.clear();
m_pool_key_to_index.clear();
// Note: can't top-up keypool here, because wallet is locked.
// User will be prompted to unlock wallet the next operation
// that requires a new key.
}
}
{
LOCK(cs_KeyStore);
// This wallet is in its first run if all of these are empty
fFirstRunRet = mapKeys.empty() && mapCryptedKeys.empty() &&
mapWatchKeys.empty() && setWatchOnly.empty() &&
mapScripts.empty();
}
if (nLoadWalletRet != DBErrors::LOAD_OK) {
return nLoadWalletRet;
}
return DBErrors::LOAD_OK;
}
DBErrors CWallet::ZapSelectTx(std::vector<TxId> &txIdsIn,
std::vector<TxId> &txIdsOut) {
// mapWallet
AssertLockHeld(cs_wallet);
DBErrors nZapSelectTxRet =
WalletBatch(*database, "cr+").ZapSelectTx(txIdsIn, txIdsOut);
for (const TxId &txid : txIdsOut) {
mapWallet.erase(txid);
}
if (nZapSelectTxRet == DBErrors::NEED_REWRITE) {
if (database->Rewrite("\x04pool")) {
setInternalKeyPool.clear();
setExternalKeyPool.clear();
m_pool_key_to_index.clear();
// Note: can't top-up keypool here, because wallet is locked.
// User will be prompted to unlock wallet the next operation
// that requires a new key.
}
}
if (nZapSelectTxRet != DBErrors::LOAD_OK) {
return nZapSelectTxRet;
}
MarkDirty();
return DBErrors::LOAD_OK;
}
DBErrors CWallet::ZapWalletTx(std::vector<CWalletTx> &vWtx) {
DBErrors nZapWalletTxRet = WalletBatch(*database, "cr+").ZapWalletTx(vWtx);
if (nZapWalletTxRet == DBErrors::NEED_REWRITE) {
if (database->Rewrite("\x04pool")) {
LOCK(cs_wallet);
setInternalKeyPool.clear();
setExternalKeyPool.clear();
m_pool_key_to_index.clear();
// Note: can't top-up keypool here, because wallet is locked.
// User will be prompted to unlock wallet the next operation
// that requires a new key.
}
}
if (nZapWalletTxRet != DBErrors::LOAD_OK) {
return nZapWalletTxRet;
}
return DBErrors::LOAD_OK;
}
bool CWallet::SetAddressBook(const CTxDestination &address,
const std::string &strName,
const std::string &strPurpose) {
bool fUpdated = false;
{
// mapAddressBook
LOCK(cs_wallet);
std::map<CTxDestination, CAddressBookData>::iterator mi =
mapAddressBook.find(address);
fUpdated = mi != mapAddressBook.end();
mapAddressBook[address].name = strName;
// Update purpose only if requested.
if (!strPurpose.empty()) {
mapAddressBook[address].purpose = strPurpose;
}
}
NotifyAddressBookChanged(this, address, strName,
::IsMine(*this, address) != ISMINE_NO, strPurpose,
(fUpdated ? CT_UPDATED : CT_NEW));
if (!strPurpose.empty() &&
!WalletBatch(*database).WritePurpose(address, strPurpose)) {
return false;
}
return WalletBatch(*database).WriteName(address, strName);
}
bool CWallet::DelAddressBook(const CTxDestination &address) {
{
// mapAddressBook
LOCK(cs_wallet);
// Delete destdata tuples associated with address.
for (const std::pair<const std::string, std::string> &item :
mapAddressBook[address].destdata) {
WalletBatch(*database).EraseDestData(address, item.first);
}
mapAddressBook.erase(address);
}
NotifyAddressBookChanged(this, address, "",
::IsMine(*this, address) != ISMINE_NO, "",
CT_DELETED);
WalletBatch(*database).ErasePurpose(address);
return WalletBatch(*database).EraseName(address);
}
const std::string &CWallet::GetLabelName(const CScript &scriptPubKey) const {
CTxDestination address;
if (ExtractDestination(scriptPubKey, address) &&
!scriptPubKey.IsUnspendable()) {
auto mi = mapAddressBook.find(address);
if (mi != mapAddressBook.end()) {
return mi->second.name;
}
}
// A scriptPubKey that doesn't have an entry in the address book is
// associated with the default label ("").
const static std::string DEFAULT_LABEL_NAME;
return DEFAULT_LABEL_NAME;
}
/**
* Mark old keypool keys as used, and generate all new keys.
*/
bool CWallet::NewKeyPool() {
LOCK(cs_wallet);
WalletBatch batch(*database);
for (const int64_t nIndex : setInternalKeyPool) {
batch.ErasePool(nIndex);
}
setInternalKeyPool.clear();
for (const int64_t nIndex : setExternalKeyPool) {
batch.ErasePool(nIndex);
}
setExternalKeyPool.clear();
for (int64_t nIndex : set_pre_split_keypool) {
batch.ErasePool(nIndex);
}
set_pre_split_keypool.clear();
m_pool_key_to_index.clear();
if (!TopUpKeyPool()) {
return false;
}
LogPrintf("CWallet::NewKeyPool rewrote keypool\n");
return true;
}
size_t CWallet::KeypoolCountExternalKeys() {
// setExternalKeyPool
AssertLockHeld(cs_wallet);
return setExternalKeyPool.size() + set_pre_split_keypool.size();
}
void CWallet::LoadKeyPool(int64_t nIndex, const CKeyPool &keypool) {
AssertLockHeld(cs_wallet);
if (keypool.m_pre_split) {
set_pre_split_keypool.insert(nIndex);
} else if (keypool.fInternal) {
setInternalKeyPool.insert(nIndex);
} else {
setExternalKeyPool.insert(nIndex);
}
m_max_keypool_index = std::max(m_max_keypool_index, nIndex);
m_pool_key_to_index[keypool.vchPubKey.GetID()] = nIndex;
// If no metadata exists yet, create a default with the pool key's
// creation time. Note that this may be overwritten by actually
// stored metadata for that key later, which is fine.
CKeyID keyid = keypool.vchPubKey.GetID();
if (mapKeyMetadata.count(keyid) == 0) {
mapKeyMetadata[keyid] = CKeyMetadata(keypool.nTime);
}
}
bool CWallet::TopUpKeyPool(unsigned int kpSize) {
LOCK(cs_wallet);
if (IsLocked()) {
return false;
}
// Top up key pool
unsigned int nTargetSize;
if (kpSize > 0) {
nTargetSize = kpSize;
} else {
nTargetSize = std::max<int64_t>(
gArgs.GetArg("-keypool", DEFAULT_KEYPOOL_SIZE), 0);
}
// count amount of available keys (internal, external)
// make sure the keypool of external and internal keys fits the user
// selected target (-keypool)
int64_t missingExternal = std::max<int64_t>(
std::max<int64_t>(nTargetSize, 1) - setExternalKeyPool.size(), 0);
int64_t missingInternal = std::max<int64_t>(
std::max<int64_t>(nTargetSize, 1) - setInternalKeyPool.size(), 0);
if (!IsHDEnabled() || !CanSupportFeature(FEATURE_HD_SPLIT)) {
// don't create extra internal keys
missingInternal = 0;
}
bool internal = false;
WalletBatch batch(*database);
for (int64_t i = missingInternal + missingExternal; i--;) {
if (i < missingInternal) {
internal = true;
}
// How in the hell did you use so many keys?
assert(m_max_keypool_index < std::numeric_limits<int64_t>::max());
int64_t index = ++m_max_keypool_index;
CPubKey pubkey(GenerateNewKey(batch, internal));
if (!batch.WritePool(index, CKeyPool(pubkey, internal))) {
throw std::runtime_error(std::string(__func__) +
": writing generated key failed");
}
if (internal) {
setInternalKeyPool.insert(index);
} else {
setExternalKeyPool.insert(index);
}
m_pool_key_to_index[pubkey.GetID()] = index;
}
if (missingInternal + missingExternal > 0) {
LogPrintf(
"keypool added %d keys (%d internal), size=%u (%u internal)\n",
missingInternal + missingExternal, missingInternal,
setInternalKeyPool.size() + setExternalKeyPool.size() +
set_pre_split_keypool.size(),
setInternalKeyPool.size());
}
return true;
}
bool CWallet::ReserveKeyFromKeyPool(int64_t &nIndex, CKeyPool &keypool,
bool fRequestedInternal) {
nIndex = -1;
keypool.vchPubKey = CPubKey();
LOCK(cs_wallet);
if (!IsLocked()) {
TopUpKeyPool();
}
bool fReturningInternal = IsHDEnabled() &&
CanSupportFeature(FEATURE_HD_SPLIT) &&
fRequestedInternal;
bool use_split_keypool = set_pre_split_keypool.empty();
std::set<int64_t> &setKeyPool =
use_split_keypool
? (fReturningInternal ? setInternalKeyPool : setExternalKeyPool)
: set_pre_split_keypool;
// Get the oldest key
if (setKeyPool.empty()) {
return false;
}
WalletBatch batch(*database);
auto it = setKeyPool.begin();
nIndex = *it;
setKeyPool.erase(it);
if (!batch.ReadPool(nIndex, keypool)) {
throw std::runtime_error(std::string(__func__) + ": read failed");
}
if (!HaveKey(keypool.vchPubKey.GetID())) {
throw std::runtime_error(std::string(__func__) +
": unknown key in key pool");
}
// If the key was pre-split keypool, we don't care about what type it is
if (use_split_keypool && keypool.fInternal != fReturningInternal) {
throw std::runtime_error(std::string(__func__) +
": keypool entry misclassified");
}
if (!keypool.vchPubKey.IsValid()) {
throw std::runtime_error(std::string(__func__) +
": keypool entry invalid");
}
m_pool_key_to_index.erase(keypool.vchPubKey.GetID());
LogPrintf("keypool reserve %d\n", nIndex);
return true;
}
void CWallet::KeepKey(int64_t nIndex) {
// Remove from key pool.
WalletBatch batch(*database);
batch.ErasePool(nIndex);
LogPrintf("keypool keep %d\n", nIndex);
}
void CWallet::ReturnKey(int64_t nIndex, bool fInternal, const CPubKey &pubkey) {
// Return to key pool
{
LOCK(cs_wallet);
if (fInternal) {
setInternalKeyPool.insert(nIndex);
} else if (!set_pre_split_keypool.empty()) {
set_pre_split_keypool.insert(nIndex);
} else {
setExternalKeyPool.insert(nIndex);
}
m_pool_key_to_index[pubkey.GetID()] = nIndex;
}
LogPrintf("keypool return %d\n", nIndex);
}
bool CWallet::GetKeyFromPool(CPubKey &result, bool internal) {
CKeyPool keypool;
LOCK(cs_wallet);
int64_t nIndex;
if (!ReserveKeyFromKeyPool(nIndex, keypool, internal)) {
if (IsLocked()) {
return false;
}
WalletBatch batch(*database);
result = GenerateNewKey(batch, internal);
return true;
}
KeepKey(nIndex);
result = keypool.vchPubKey;
return true;
}
static int64_t GetOldestKeyTimeInPool(const std::set<int64_t> &setKeyPool,
WalletBatch &batch) {
if (setKeyPool.empty()) {
return GetTime();
}
CKeyPool keypool;
int64_t nIndex = *(setKeyPool.begin());
if (!batch.ReadPool(nIndex, keypool)) {
throw std::runtime_error(std::string(__func__) +
": read oldest key in keypool failed");
}
assert(keypool.vchPubKey.IsValid());
return keypool.nTime;
}
int64_t CWallet::GetOldestKeyPoolTime() {
LOCK(cs_wallet);
WalletBatch batch(*database);
// load oldest key from keypool, get time and return
int64_t oldestKey = GetOldestKeyTimeInPool(setExternalKeyPool, batch);
if (IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT)) {
oldestKey = std::max(GetOldestKeyTimeInPool(setInternalKeyPool, batch),
oldestKey);
if (!set_pre_split_keypool.empty()) {
oldestKey =
std::max(GetOldestKeyTimeInPool(set_pre_split_keypool, batch),
oldestKey);
}
}
return oldestKey;
}
std::map<CTxDestination, Amount> CWallet::GetAddressBalances() {
std::map<CTxDestination, Amount> balances;
LOCK(cs_wallet);
for (const auto &walletEntry : mapWallet) {
const CWalletTx *pcoin = &walletEntry.second;
if (!pcoin->IsTrusted()) {
continue;
}
if (pcoin->IsImmatureCoinBase()) {
continue;
}
int nDepth = pcoin->GetDepthInMainChain();
if (nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? 0 : 1)) {
continue;
}
for (uint32_t i = 0; i < pcoin->tx->vout.size(); i++) {
CTxDestination addr;
if (!IsMine(pcoin->tx->vout[i])) {
continue;
}
if (!ExtractDestination(pcoin->tx->vout[i].scriptPubKey, addr)) {
continue;
}
Amount n = IsSpent(COutPoint(walletEntry.first, i))
? Amount::zero()
: pcoin->tx->vout[i].nValue;
if (!balances.count(addr)) {
balances[addr] = Amount::zero();
}
balances[addr] += n;
}
}
return balances;
}
std::set<std::set<CTxDestination>> CWallet::GetAddressGroupings() {
// mapWallet
AssertLockHeld(cs_wallet);
std::set<std::set<CTxDestination>> groupings;
std::set<CTxDestination> grouping;
for (const auto &walletEntry : mapWallet) {
const CWalletTx *pcoin = &walletEntry.second;
if (pcoin->tx->vin.size() > 0) {
bool any_mine = false;
// Group all input addresses with each other.
for (const auto &txin : pcoin->tx->vin) {
CTxDestination address;
// If this input isn't mine, ignore it.
if (!IsMine(txin)) {
continue;
}
if (!ExtractDestination(mapWallet.at(txin.prevout.GetTxId())
.tx->vout[txin.prevout.GetN()]
.scriptPubKey,
address)) {
continue;
}
grouping.insert(address);
any_mine = true;
}
// Group change with input addresses.
if (any_mine) {
for (const auto &txout : pcoin->tx->vout) {
if (IsChange(txout)) {
CTxDestination txoutAddr;
if (!ExtractDestination(txout.scriptPubKey,
txoutAddr)) {
continue;
}
grouping.insert(txoutAddr);
}
}
}
if (grouping.size() > 0) {
groupings.insert(grouping);
grouping.clear();
}
}
// Group lone addrs by themselves.
for (const auto &txout : pcoin->tx->vout) {
if (IsMine(txout)) {
CTxDestination address;
if (!ExtractDestination(txout.scriptPubKey, address)) {
continue;
}
grouping.insert(address);
groupings.insert(grouping);
grouping.clear();
}
}
}
// A set of pointers to groups of addresses.
std::set<std::set<CTxDestination> *> uniqueGroupings;
// Map addresses to the unique group containing it.
std::map<CTxDestination, std::set<CTxDestination> *> setmap;
for (std::set<CTxDestination> _grouping : groupings) {
// Make a set of all the groups hit by this new group.
std::set<std::set<CTxDestination> *> hits;
std::map<CTxDestination, std::set<CTxDestination> *>::iterator it;
for (const CTxDestination &address : _grouping) {
if ((it = setmap.find(address)) != setmap.end()) {
hits.insert((*it).second);
}
}
// Merge all hit groups into a new single group and delete old groups.
std::set<CTxDestination> *merged =
new std::set<CTxDestination>(_grouping);
for (std::set<CTxDestination> *hit : hits) {
merged->insert(hit->begin(), hit->end());
uniqueGroupings.erase(hit);
delete hit;
}
uniqueGroupings.insert(merged);
// Update setmap.
for (const CTxDestination &element : *merged) {
setmap[element] = merged;
}
}
std::set<std::set<CTxDestination>> ret;
for (const std::set<CTxDestination> *uniqueGrouping : uniqueGroupings) {
ret.insert(*uniqueGrouping);
delete uniqueGrouping;
}
return ret;
}
std::set<CTxDestination>
CWallet::GetLabelAddresses(const std::string &label) const {
LOCK(cs_wallet);
std::set<CTxDestination> result;
for (const std::pair<const CTxDestination, CAddressBookData> &item :
mapAddressBook) {
const CTxDestination &address = item.first;
const std::string &strName = item.second.name;
if (strName == label) {
result.insert(address);
}
}
return result;
}
void CWallet::DeleteLabel(const std::string &label) {
WalletBatch batch(*database);
batch.EraseAccount(label);
}
bool CReserveKey::GetReservedKey(CPubKey &pubkey, bool internal) {
if (nIndex == -1) {
CKeyPool keypool;
if (!pwallet->ReserveKeyFromKeyPool(nIndex, keypool, internal)) {
return false;
}
vchPubKey = keypool.vchPubKey;
fInternal = keypool.fInternal;
}
assert(vchPubKey.IsValid());
pubkey = vchPubKey;
return true;
}
void CReserveKey::KeepKey() {
if (nIndex != -1) {
pwallet->KeepKey(nIndex);
}
nIndex = -1;
vchPubKey = CPubKey();
}
void CReserveKey::ReturnKey() {
if (nIndex != -1) {
pwallet->ReturnKey(nIndex, fInternal, vchPubKey);
}
nIndex = -1;
vchPubKey = CPubKey();
}
void CWallet::MarkReserveKeysAsUsed(int64_t keypool_id) {
AssertLockHeld(cs_wallet);
bool internal = setInternalKeyPool.count(keypool_id);
if (!internal) {
assert(setExternalKeyPool.count(keypool_id) ||
set_pre_split_keypool.count(keypool_id));
}
std::set<int64_t> *setKeyPool =
internal ? &setInternalKeyPool
: (set_pre_split_keypool.empty() ? &setExternalKeyPool
: &set_pre_split_keypool);
auto it = setKeyPool->begin();
WalletBatch batch(*database);
while (it != std::end(*setKeyPool)) {
const int64_t &index = *(it);
if (index > keypool_id) {
// set*KeyPool is ordered
break;
}
CKeyPool keypool;
if (batch.ReadPool(index, keypool)) {
// TODO: This should be unnecessary
m_pool_key_to_index.erase(keypool.vchPubKey.GetID());
}
LearnAllRelatedScripts(keypool.vchPubKey);
batch.ErasePool(index);
LogPrintf("keypool index %d removed\n", index);
it = setKeyPool->erase(it);
}
}
void CWallet::GetScriptForMining(std::shared_ptr<CReserveScript> &script) {
std::shared_ptr<CReserveKey> rKey = std::make_shared<CReserveKey>(this);
CPubKey pubkey;
if (!rKey->GetReservedKey(pubkey)) {
return;
}
script = rKey;
script->reserveScript = CScript() << ToByteVector(pubkey) << OP_CHECKSIG;
}
void CWallet::LockCoin(const COutPoint &output) {
// setLockedCoins
AssertLockHeld(cs_wallet);
setLockedCoins.insert(output);
}
void CWallet::UnlockCoin(const COutPoint &output) {
// setLockedCoins
AssertLockHeld(cs_wallet);
setLockedCoins.erase(output);
}
void CWallet::UnlockAllCoins() {
// setLockedCoins
AssertLockHeld(cs_wallet);
setLockedCoins.clear();
}
bool CWallet::IsLockedCoin(const COutPoint &outpoint) const {
// setLockedCoins
AssertLockHeld(cs_wallet);
return setLockedCoins.count(outpoint) > 0;
}
void CWallet::ListLockedCoins(std::vector<COutPoint> &vOutpts) const {
// setLockedCoins
AssertLockHeld(cs_wallet);
for (COutPoint outpoint : setLockedCoins) {
vOutpts.push_back(outpoint);
}
}
/** @} */ // end of Actions
void CWallet::GetKeyBirthTimes(
std::map<CTxDestination, int64_t> &mapKeyBirth) const {
// mapKeyMetadata
AssertLockHeld(cs_wallet);
mapKeyBirth.clear();
// Get birth times for keys with metadata.
for (const auto &entry : mapKeyMetadata) {
if (entry.second.nCreateTime) {
mapKeyBirth[entry.first] = entry.second.nCreateTime;
}
}
// Map in which we'll infer heights of other keys the tip can be
// reorganized; use a 144-block safety margin.
CBlockIndex *pindexMax =
chainActive[std::max(0, chainActive.Height() - 144)];
std::map<CKeyID, CBlockIndex *> mapKeyFirstBlock;
for (const CKeyID &keyid : GetKeys()) {
if (mapKeyBirth.count(keyid) == 0) {
mapKeyFirstBlock[keyid] = pindexMax;
}
}
// 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.
std::vector<CKeyID> vAffected;
for (const auto &entry : mapWallet) {
// iterate over all wallet transactions...
const CWalletTx &wtx = entry.second;
CBlockIndex *pindex = LookupBlockIndex(wtx.hashBlock);
if (pindex && chainActive.Contains(pindex)) {
// ... which are already in a block
int nHeight = pindex->nHeight;
for (const CTxOut &txout : wtx.tx->vout) {
// Iterate over all their outputs...
CAffectedKeysVisitor(*this, vAffected)
.Process(txout.scriptPubKey);
for (const CKeyID &keyid : vAffected) {
// ... and all their affected keys.
std::map<CKeyID, CBlockIndex *>::iterator rit =
mapKeyFirstBlock.find(keyid);
if (rit != mapKeyFirstBlock.end() &&
nHeight < rit->second->nHeight) {
rit->second = pindex;
}
}
vAffected.clear();
}
}
}
// Extract block timestamps for those keys.
for (const auto &entry : mapKeyFirstBlock) {
// block times can be 2h off
mapKeyBirth[entry.first] =
entry.second->GetBlockTime() - TIMESTAMP_WINDOW;
}
}
/**
* Compute smart timestamp for a transaction being added to the wallet.
*
* Logic:
* - If sending a transaction, assign its timestamp to the current time.
* - If receiving a transaction outside a block, assign its timestamp to the
* current time.
* - If receiving a block with a future timestamp, assign all its (not already
* known) transactions' timestamps to the current time.
* - If receiving a block with a past timestamp, before the most recent known
* transaction (that we care about), assign all its (not already known)
* transactions' timestamps to the same timestamp as that most-recent-known
* transaction.
* - If receiving a block with a past timestamp, but after the most recent known
* transaction, assign all its (not already known) transactions' timestamps to
* the block time.
*
* For more information see CWalletTx::nTimeSmart,
* https://bitcointalk.org/?topic=54527, or
* https://github.com/bitcoin/bitcoin/pull/1393.
*/
unsigned int CWallet::ComputeTimeSmart(const CWalletTx &wtx) const {
unsigned int nTimeSmart = wtx.nTimeReceived;
if (!wtx.hashUnset()) {
if (const CBlockIndex *pindex = LookupBlockIndex(wtx.hashBlock)) {
int64_t latestNow = wtx.nTimeReceived;
int64_t latestEntry = 0;
// Tolerate times up to the last timestamp in the wallet not more
// than 5 minutes into the future
int64_t latestTolerated = latestNow + 300;
const TxItems &txOrdered = wtxOrdered;
for (auto it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) {
CWalletTx *const pwtx = it->second.first;
if (pwtx == &wtx) {
continue;
}
CAccountingEntry *const pacentry = it->second.second;
int64_t nSmartTime;
if (pwtx) {
nSmartTime = pwtx->nTimeSmart;
if (!nSmartTime) {
nSmartTime = pwtx->nTimeReceived;
}
} else {
nSmartTime = pacentry->nTime;
}
if (nSmartTime <= latestTolerated) {
latestEntry = nSmartTime;
if (nSmartTime > latestNow) {
latestNow = nSmartTime;
}
break;
}
}
int64_t blocktime = pindex->GetBlockTime();
nTimeSmart = std::max(latestEntry, std::min(blocktime, latestNow));
} else {
LogPrintf("%s: found %s in block %s not in index\n", __func__,
wtx.GetId().ToString(), wtx.hashBlock.ToString());
}
}
return nTimeSmart;
}
bool CWallet::AddDestData(const CTxDestination &dest, const std::string &key,
const std::string &value) {
if (boost::get<CNoDestination>(&dest)) {
return false;
}
mapAddressBook[dest].destdata.insert(std::make_pair(key, value));
return WalletBatch(*database).WriteDestData(dest, key, value);
}
bool CWallet::EraseDestData(const CTxDestination &dest,
const std::string &key) {
if (!mapAddressBook[dest].destdata.erase(key)) {
return false;
}
return WalletBatch(*database).EraseDestData(dest, key);
}
void CWallet::LoadDestData(const CTxDestination &dest, const std::string &key,
const std::string &value) {
mapAddressBook[dest].destdata.insert(std::make_pair(key, value));
}
bool CWallet::GetDestData(const CTxDestination &dest, const std::string &key,
std::string *value) const {
std::map<CTxDestination, CAddressBookData>::const_iterator i =
mapAddressBook.find(dest);
if (i != mapAddressBook.end()) {
CAddressBookData::StringMap::const_iterator j =
i->second.destdata.find(key);
if (j != i->second.destdata.end()) {
if (value) {
*value = j->second;
}
return true;
}
}
return false;
}
std::vector<std::string>
CWallet::GetDestValues(const std::string &prefix) const {
LOCK(cs_wallet);
std::vector<std::string> values;
for (const auto &address : mapAddressBook) {
for (const auto &data : address.second.destdata) {
if (!data.first.compare(0, prefix.size(), prefix)) {
values.emplace_back(data.second);
}
}
}
return values;
}
bool CWallet::Verify(const CChainParams &chainParams, std::string wallet_file,
bool salvage_wallet, std::string &error_string,
std::string &warning_string) {
// Do some checking on wallet path. It should be either a:
//
// 1. Path where a directory can be created.
// 2. Path to an existing directory.
// 3. Path to a symlink to a directory.
// 4. For backwards compatibility, the name of a data file in -walletdir.
LOCK(cs_wallets);
fs::path wallet_path = fs::absolute(wallet_file, GetWalletDir());
fs::file_type path_type = fs::symlink_status(wallet_path).type();
if (!(path_type == fs::file_not_found || path_type == fs::directory_file ||
(path_type == fs::symlink_file && fs::is_directory(wallet_path)) ||
(path_type == fs::regular_file &&
fs::path(wallet_file).filename() == wallet_file))) {
error_string =
strprintf("Invalid -wallet path '%s'. -wallet path should point to "
"a directory where wallet.dat and "
"database/log.?????????? files can be stored, a location "
"where such a directory could be created, "
"or (for backwards compatibility) the name of an "
"existing data file in -walletdir (%s)",
wallet_file, GetWalletDir());
return false;
}
// Make sure that the wallet path doesn't clash with an existing wallet path
for (auto wallet : GetWallets()) {
if (fs::absolute(wallet->GetName(), GetWalletDir()) == wallet_path) {
error_string = strprintf("Error loading wallet %s. Duplicate "
"-wallet filename specified.",
wallet_file);
return false;
}
}
try {
if (!WalletBatch::VerifyEnvironment(wallet_path, error_string)) {
return false;
}
} catch (const fs::filesystem_error &e) {
error_string =
strprintf("Error loading wallet %s. %s", wallet_file, e.what());
return false;
}
if (salvage_wallet) {
// Recover readable keypairs:
CWallet dummyWallet(chainParams, "dummy",
WalletDatabase::CreateDummy());
std::string backup_filename;
if (!WalletBatch::Recover(
wallet_path, static_cast<void *>(&dummyWallet),
WalletBatch::RecoverKeysOnlyFilter, backup_filename)) {
return false;
}
}
return WalletBatch::VerifyDatabaseFile(wallet_path, warning_string,
error_string);
}
void CWallet::MarkPreSplitKeys() {
WalletBatch batch(*database);
for (auto it = setExternalKeyPool.begin();
it != setExternalKeyPool.end();) {
int64_t index = *it;
CKeyPool keypool;
if (!batch.ReadPool(index, keypool)) {
throw std::runtime_error(std::string(__func__) +
": read keypool entry failed");
}
keypool.m_pre_split = true;
if (!batch.WritePool(index, keypool)) {
throw std::runtime_error(std::string(__func__) +
": writing modified keypool entry failed");
}
set_pre_split_keypool.insert(index);
it = setExternalKeyPool.erase(it);
}
}
std::shared_ptr<CWallet>
CWallet::CreateWalletFromFile(const CChainParams &chainParams,
const std::string &name, const fs::path &path) {
const std::string &walletFile = name;
// Needed to restore wallet transaction meta data after -zapwallettxes
std::vector<CWalletTx> vWtx;
if (gArgs.GetBoolArg("-zapwallettxes", false)) {
uiInterface.InitMessage(_("Zapping all transactions from wallet..."));
std::unique_ptr<CWallet> tempWallet = std::make_unique<CWallet>(
chainParams, name, WalletDatabase::Create(path));
DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx);
if (nZapWalletRet != DBErrors::LOAD_OK) {
InitError(
strprintf(_("Error loading %s: Wallet corrupted"), walletFile));
return nullptr;
}
}
uiInterface.InitMessage(_("Loading wallet..."));
int64_t nStart = GetTimeMillis();
bool fFirstRun = true;
// TODO: Can't use std::make_shared because we need a custom deleter but
// should be possible to use std::allocate_shared.
std::shared_ptr<CWallet> walletInstance(
new CWallet(chainParams, name, WalletDatabase::Create(path)),
ReleaseWallet);
DBErrors nLoadWalletRet = walletInstance->LoadWallet(fFirstRun);
if (nLoadWalletRet != DBErrors::LOAD_OK) {
if (nLoadWalletRet == DBErrors::CORRUPT) {
InitError(
strprintf(_("Error loading %s: Wallet corrupted"), walletFile));
return nullptr;
}
if (nLoadWalletRet == DBErrors::NONCRITICAL_ERROR) {
InitWarning(strprintf(
_("Error reading %s! All keys read correctly, but transaction "
"data"
" or address book entries might be missing or incorrect."),
walletFile));
} else if (nLoadWalletRet == DBErrors::TOO_NEW) {
InitError(strprintf(
_("Error loading %s: Wallet requires newer version of %s"),
walletFile, _(PACKAGE_NAME)));
return nullptr;
} else if (nLoadWalletRet == DBErrors::NEED_REWRITE) {
InitError(strprintf(
_("Wallet needed to be rewritten: restart %s to complete"),
_(PACKAGE_NAME)));
return nullptr;
} else {
InitError(strprintf(_("Error loading %s"), walletFile));
return nullptr;
}
}
int prev_version = walletInstance->nWalletVersion;
if (gArgs.GetBoolArg("-upgradewallet", fFirstRun)) {
int nMaxVersion = gArgs.GetArg("-upgradewallet", 0);
// The -upgradewallet without argument case
if (nMaxVersion == 0) {
LogPrintf("Performing wallet upgrade to %i\n", FEATURE_LATEST);
nMaxVersion = CLIENT_VERSION;
// permanently upgrade the wallet immediately
walletInstance->SetMinVersion(FEATURE_LATEST);
} else {
LogPrintf("Allowing wallet upgrade up to %i\n", nMaxVersion);
}
if (nMaxVersion < walletInstance->GetVersion()) {
InitError(_("Cannot downgrade wallet"));
return nullptr;
}
walletInstance->SetMaxVersion(nMaxVersion);
}
// Upgrade to HD if explicit upgrade
if (gArgs.GetBoolArg("-upgradewallet", false)) {
LOCK(walletInstance->cs_wallet);
// Do not upgrade versions to any version between HD_SPLIT and
// FEATURE_PRE_SPLIT_KEYPOOL unless already supporting HD_SPLIT
int max_version = walletInstance->nWalletVersion;
if (!walletInstance->CanSupportFeature(FEATURE_HD_SPLIT) &&
max_version >= FEATURE_HD_SPLIT &&
max_version < FEATURE_PRE_SPLIT_KEYPOOL) {
InitError(
_("Cannot upgrade a non HD split wallet without upgrading to "
"support pre split keypool. Please use -upgradewallet=200300 "
"or -upgradewallet with no version specified."));
return nullptr;
}
bool hd_upgrade = false;
bool split_upgrade = false;
if (walletInstance->CanSupportFeature(FEATURE_HD) &&
!walletInstance->IsHDEnabled()) {
LogPrintf("Upgrading wallet to HD\n");
walletInstance->SetMinVersion(FEATURE_HD);
// generate a new master key
CPubKey masterPubKey = walletInstance->GenerateNewSeed();
walletInstance->SetHDSeed(masterPubKey);
hd_upgrade = true;
}
// Upgrade to HD chain split if necessary
if (walletInstance->CanSupportFeature(FEATURE_HD_SPLIT)) {
LogPrintf("Upgrading wallet to use HD chain split\n");
walletInstance->SetMinVersion(FEATURE_PRE_SPLIT_KEYPOOL);
split_upgrade = FEATURE_HD_SPLIT > prev_version;
}
// Mark all keys currently in the keypool as pre-split
if (split_upgrade) {
walletInstance->MarkPreSplitKeys();
}
// Regenerate the keypool if upgraded to HD
if (hd_upgrade) {
if (!walletInstance->TopUpKeyPool()) {
InitError(_("Unable to generate keys") += "\n");
return nullptr;
}
}
}
if (fFirstRun) {
// Ensure this wallet.dat can only be opened by clients supporting
// HD with chain split and expects no default key.
if (!gArgs.GetBoolArg("-usehd", true)) {
InitError(strprintf(_("Error creating %s: You can't create non-HD "
"wallets with this version."),
walletFile));
return nullptr;
}
walletInstance->SetMinVersion(FEATURE_LATEST);
// Generate a new seed
CPubKey seed = walletInstance->GenerateNewSeed();
walletInstance->SetHDSeed(seed);
// Top up the keypool
if (!walletInstance->TopUpKeyPool()) {
InitError(_("Unable to generate initial keys") += "\n");
return nullptr;
}
walletInstance->ChainStateFlushed(chainActive.GetLocator());
} else if (gArgs.IsArgSet("-usehd")) {
bool useHD = gArgs.GetBoolArg("-usehd", true);
if (walletInstance->IsHDEnabled() && !useHD) {
InitError(
strprintf(_("Error loading %s: You can't disable HD on an "
"already existing HD wallet"),
walletFile));
return nullptr;
}
if (!walletInstance->IsHDEnabled() && useHD) {
InitError(strprintf(_("Error loading %s: You can't enable HD on an "
"already existing non-HD wallet"),
walletFile));
return nullptr;
}
}
if (gArgs.IsArgSet("-mintxfee")) {
Amount n = Amount::zero();
if (!ParseMoney(gArgs.GetArg("-mintxfee", ""), n) ||
n == Amount::zero()) {
InitError(AmountErrMsg("mintxfee", gArgs.GetArg("-mintxfee", "")));
return nullptr;
}
if (n > HIGH_TX_FEE_PER_KB) {
InitWarning(AmountHighWarn("-mintxfee") + " " +
_("This is the minimum transaction fee you pay on "
"every transaction."));
}
walletInstance->m_min_fee = CFeeRate(n);
}
if (gArgs.IsArgSet("-fallbackfee")) {
Amount nFeePerK = Amount::zero();
if (!ParseMoney(gArgs.GetArg("-fallbackfee", ""), nFeePerK)) {
InitError(
strprintf(_("Invalid amount for -fallbackfee=<amount>: '%s'"),
gArgs.GetArg("-fallbackfee", "")));
return nullptr;
}
if (nFeePerK > HIGH_TX_FEE_PER_KB) {
InitWarning(AmountHighWarn("-fallbackfee") + " " +
_("This is the transaction fee you may pay when fee "
"estimates are not available."));
}
walletInstance->m_fallback_fee = CFeeRate(nFeePerK);
// disable fallback fee in case value was set to 0, enable if non-null
// value
walletInstance->m_allow_fallback_fee = (nFeePerK != Amount::zero());
}
if (gArgs.IsArgSet("-paytxfee")) {
Amount nFeePerK = Amount::zero();
if (!ParseMoney(gArgs.GetArg("-paytxfee", ""), nFeePerK)) {
InitError(AmountErrMsg("paytxfee", gArgs.GetArg("-paytxfee", "")));
return nullptr;
}
if (nFeePerK > HIGH_TX_FEE_PER_KB) {
InitWarning(AmountHighWarn("-paytxfee") + " " +
_("This is the transaction fee you will pay if you "
"send a transaction."));
}
walletInstance->m_pay_tx_fee = CFeeRate(nFeePerK, 1000);
if (walletInstance->m_pay_tx_fee < ::minRelayTxFee) {
InitError(strprintf(_("Invalid amount for -paytxfee=<amount>: '%s' "
"(must be at least %s)"),
gArgs.GetArg("-paytxfee", ""),
::minRelayTxFee.ToString()));
return nullptr;
}
}
walletInstance->m_spend_zero_conf_change =
gArgs.GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE);
walletInstance->m_default_address_type = DEFAULT_ADDRESS_TYPE;
walletInstance->m_default_change_type = DEFAULT_CHANGE_TYPE;
LogPrintf(" wallet %15dms\n", GetTimeMillis() - nStart);
// Try to top up keypool. No-op if the wallet is locked.
walletInstance->TopUpKeyPool();
LOCK(cs_main);
CBlockIndex *pindexRescan = chainActive.Genesis();
if (!gArgs.GetBoolArg("-rescan", false)) {
WalletBatch batch(*walletInstance->database);
CBlockLocator locator;
if (batch.ReadBestBlock(locator)) {
pindexRescan = FindForkInGlobalIndex(chainActive, locator);
}
}
walletInstance->m_last_block_processed = chainActive.Tip();
if (chainActive.Tip() && chainActive.Tip() != pindexRescan) {
// We can't rescan beyond non-pruned blocks, stop and throw an error.
// This might happen if a user uses an old wallet within a pruned node
// or if he ran -disablewallet for a longer time, then decided to
// re-enable.
if (fPruneMode) {
CBlockIndex *block = chainActive.Tip();
while (block && block->pprev && block->pprev->nStatus.hasData() &&
block->pprev->nTx > 0 && pindexRescan != block) {
block = block->pprev;
}
if (pindexRescan != block) {
InitError(_("Prune: last wallet synchronisation goes beyond "
"pruned data. You need to -reindex (download the "
"whole blockchain again in case of pruned node)"));
return nullptr;
}
}
uiInterface.InitMessage(_("Rescanning..."));
LogPrintf("Rescanning last %i blocks (from block %i)...\n",
chainActive.Height() - pindexRescan->nHeight,
pindexRescan->nHeight);
// No need to read and scan block if block was created before our wallet
// birthday (as adjusted for block time variability)
while (pindexRescan && walletInstance->nTimeFirstKey &&
(pindexRescan->GetBlockTime() <
(walletInstance->nTimeFirstKey - TIMESTAMP_WINDOW))) {
pindexRescan = chainActive.Next(pindexRescan);
}
nStart = GetTimeMillis();
{
WalletRescanReserver reserver(walletInstance.get());
if (!reserver.reserve()) {
InitError(
_("Failed to rescan the wallet during initialization"));
return nullptr;
}
walletInstance->ScanForWalletTransactions(pindexRescan, nullptr,
reserver, true);
}
LogPrintf(" rescan %15dms\n", GetTimeMillis() - nStart);
walletInstance->ChainStateFlushed(chainActive.GetLocator());
walletInstance->database->IncrementUpdateCounter();
// Restore wallet transaction metadata after -zapwallettxes=1
if (gArgs.GetBoolArg("-zapwallettxes", false) &&
gArgs.GetArg("-zapwallettxes", "1") != "2") {
WalletBatch batch(*walletInstance->database);
for (const CWalletTx &wtxOld : vWtx) {
const TxId txid = wtxOld.GetId();
std::map<TxId, CWalletTx>::iterator mi =
walletInstance->mapWallet.find(txid);
if (mi != walletInstance->mapWallet.end()) {
const CWalletTx *copyFrom = &wtxOld;
CWalletTx *copyTo = &mi->second;
copyTo->mapValue = copyFrom->mapValue;
copyTo->vOrderForm = copyFrom->vOrderForm;
copyTo->nTimeReceived = copyFrom->nTimeReceived;
copyTo->nTimeSmart = copyFrom->nTimeSmart;
copyTo->fFromMe = copyFrom->fFromMe;
copyTo->strFromAccount = copyFrom->strFromAccount;
copyTo->nOrderPos = copyFrom->nOrderPos;
batch.WriteTx(*copyTo);
}
}
}
}
uiInterface.LoadWallet(walletInstance);
// Register with the validation interface. It's ok to do this after rescan
// since we're still holding cs_main.
RegisterValidationInterface(walletInstance.get());
walletInstance->SetBroadcastTransactions(
gArgs.GetBoolArg("-walletbroadcast", DEFAULT_WALLETBROADCAST));
LOCK(walletInstance->cs_wallet);
LogPrintf("setKeyPool.size() = %u\n", walletInstance->GetKeyPoolSize());
LogPrintf("mapWallet.size() = %u\n", walletInstance->mapWallet.size());
LogPrintf("mapAddressBook.size() = %u\n",
walletInstance->mapAddressBook.size());
return walletInstance;
}
void CWallet::postInitProcess() {
// Add wallet transactions that aren't already in a block to mempool.
// Do this here as mempool requires genesis block to be loaded.
ReacceptWalletTransactions();
}
bool CWallet::BackupWallet(const std::string &strDest) {
return database->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;
}
CWalletKey::CWalletKey(int64_t nExpires) {
nTimeCreated = (nExpires ? GetTime() : 0);
nTimeExpires = nExpires;
}
void CMerkleTx::SetMerkleBranch(const CBlockIndex *pindex, int posInBlock) {
// Update the tx's hashBlock
hashBlock = pindex->GetBlockHash();
// Set the position of the transaction in the block.
nIndex = posInBlock;
}
int CMerkleTx::GetDepthInMainChain() const {
if (hashUnset()) {
return 0;
}
AssertLockHeld(cs_main);
// Find the block it claims to be in.
CBlockIndex *pindex = LookupBlockIndex(hashBlock);
if (!pindex || !chainActive.Contains(pindex)) {
return 0;
}
return ((nIndex == -1) ? (-1) : 1) *
(chainActive.Height() - pindex->nHeight + 1);
}
int CMerkleTx::GetBlocksToMaturity() const {
if (!IsCoinBase()) {
return 0;
}
return std::max(0, (COINBASE_MATURITY + 1) - GetDepthInMainChain());
}
bool CMerkleTx::IsImmatureCoinBase() const {
// note GetBlocksToMaturity is 0 for non-coinbase tx
return GetBlocksToMaturity() > 0;
}
bool CWalletTx::AcceptToMemoryPool(const Amount nAbsurdFee,
CValidationState &state) {
// Quick check to avoid re-setting fInMempool to false
if (g_mempool.exists(tx->GetId())) {
return false;
}
// We must set fInMempool here - while it will be re-set to true by the
// entered-mempool callback, if we did not there would be a race where a
// user could call sendmoney in a loop and hit spurious out of funds errors
// because we think that this newly generated transaction's change is
// unavailable as we're not yet aware that it is in the mempool.
bool ret = ::AcceptToMemoryPool(
GetConfig(), g_mempool, state, tx, true /* fLimitFree */,
nullptr /* pfMissingInputs */, false /* fOverrideMempoolLimit */,
nAbsurdFee);
fInMempool = ret;
return ret;
}
-static const std::string OUTPUT_TYPE_STRING_LEGACY = "legacy";
-
-bool ParseOutputType(const std::string &type, OutputType &output_type) {
- if (type == OUTPUT_TYPE_STRING_LEGACY) {
- output_type = OutputType::LEGACY;
- return true;
- }
- return false;
-}
-
-const std::string &FormatOutputType(OutputType type) {
- switch (type) {
- case OutputType::LEGACY:
- return OUTPUT_TYPE_STRING_LEGACY;
- default:
- assert(false);
- }
-}
-
void CWallet::LearnRelatedScripts(const CPubKey &key, OutputType type) {
// Nothing to do...
}
void CWallet::LearnAllRelatedScripts(const CPubKey &key) {
// Nothing to do...
}
-CTxDestination GetDestinationForKey(const CPubKey &key, OutputType type) {
- switch (type) {
- case OutputType::LEGACY:
- return key.GetID();
- default:
- assert(false);
- }
-}
-
-std::vector<CTxDestination> GetAllDestinationsForKey(const CPubKey &key) {
- return std::vector<CTxDestination>{key.GetID()};
-}
-
-CTxDestination CWallet::AddAndGetDestinationForScript(const CScript &script,
- OutputType type) {
- // Note that scripts over 520 bytes are not yet supported.
- switch (type) {
- case OutputType::LEGACY:
- return CScriptID(script);
- default:
- assert(false);
- }
-}
-
std::vector<OutputGroup>
CWallet::GroupOutputs(const std::vector<COutput> &outputs,
bool single_coin) const {
std::vector<OutputGroup> groups;
std::map<CTxDestination, OutputGroup> gmap;
CTxDestination dst;
for (const auto &output : outputs) {
if (output.fSpendable) {
CInputCoin input_coin = output.GetInputCoin();
size_t ancestors, descendants;
g_mempool.GetTransactionAncestry(output.tx->GetId(), ancestors,
descendants);
if (!single_coin &&
ExtractDestination(output.tx->tx->vout[output.i].scriptPubKey,
dst)) {
// Limit output groups to no more than 10 entries, to protect
// against inadvertently creating a too-large transaction
// when using -avoidpartialspends
if (gmap[dst].m_outputs.size() >= OUTPUT_GROUP_MAX_ENTRIES) {
groups.push_back(gmap[dst]);
gmap.erase(dst);
}
gmap[dst].Insert(input_coin, output.nDepth,
output.tx->IsFromMe(ISMINE_ALL), ancestors,
descendants);
} else {
groups.emplace_back(input_coin, output.nDepth,
output.tx->IsFromMe(ISMINE_ALL), ancestors,
descendants);
}
}
}
if (!single_coin) {
for (const auto &it : gmap) {
groups.push_back(it.second);
}
}
return groups;
}
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index ed2b5635f..a13ee1db9 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -1,1460 +1,1427 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2016 The Bitcoin Core developers
// Copyright (c) 2018 The Bitcoin developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_WALLET_WALLET_H
#define BITCOIN_WALLET_WALLET_H
#include <amount.h>
+#include <outputtype.h>
#include <script/ismine.h>
#include <script/sign.h>
#include <streams.h>
#include <tinyformat.h>
#include <ui_interface.h>
#include <util/strencodings.h>
#include <util/system.h>
#include <validationinterface.h>
#include <wallet/coinselection.h>
#include <wallet/crypter.h>
#include <wallet/rpcwallet.h>
#include <wallet/walletdb.h>
#include <algorithm>
#include <atomic>
#include <cstdint>
#include <map>
#include <set>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>
bool AddWallet(const std::shared_ptr<CWallet> &wallet);
bool RemoveWallet(const std::shared_ptr<CWallet> &wallet);
bool HasWallets();
std::vector<std::shared_ptr<CWallet>> GetWallets();
std::shared_ptr<CWallet> GetWallet(const std::string &name);
//! Default for -keypool
static const unsigned int DEFAULT_KEYPOOL_SIZE = 1000;
//! -paytxfee default
constexpr Amount DEFAULT_PAY_TX_FEE = Amount::zero();
//! -fallbackfee default
static const Amount DEFAULT_FALLBACK_FEE(20000 * SATOSHI);
//! -mintxfee default
static const Amount DEFAULT_TRANSACTION_MINFEE_PER_KB = 1000 * SATOSHI;
//! minimum recommended increment for BIP 125 replacement txs
static const Amount WALLET_INCREMENTAL_RELAY_FEE(5000 * SATOSHI);
//! Default for -spendzeroconfchange
static const bool DEFAULT_SPEND_ZEROCONF_CHANGE = true;
//! Default for -walletrejectlongchains
static const bool DEFAULT_WALLET_REJECT_LONG_CHAINS = false;
//! Default for -avoidpartialspends
static const bool DEFAULT_AVOIDPARTIALSPENDS = false;
static const bool DEFAULT_WALLETBROADCAST = true;
static const bool DEFAULT_DISABLE_WALLET = false;
class CBlockIndex;
class CChainParams;
class CCoinControl;
class COutput;
class CReserveKey;
class CScript;
class CTxMemPool;
class CWalletTx;
/** (client) version numbers for particular wallet features */
enum WalletFeature {
// the earliest version new wallets supports (only useful for
// getwalletinfo's clientversion output)
FEATURE_BASE = 10500,
// wallet encryption
FEATURE_WALLETCRYPT = 40000,
// compressed public keys
FEATURE_COMPRPUBKEY = 60000,
// Hierarchical key derivation after BIP32 (HD Wallet)
FEATURE_HD = 130000,
// Wallet with HD chain split (change outputs will use m/0'/1'/k)
FEATURE_HD_SPLIT = 160300,
// Wallet without a default key written
FEATURE_NO_DEFAULT_KEY = 190700,
// Upgraded to HD SPLIT and can have a pre-split keypool
FEATURE_PRE_SPLIT_KEYPOOL = 200300,
FEATURE_LATEST = FEATURE_PRE_SPLIT_KEYPOOL,
};
-enum class OutputType {
- LEGACY,
-
- /**
- * Special output type for change outputs only. Automatically choose type
- * based on address type setting and the types other of non-change outputs
- * (see -changetype option documentation and implementation in
- * CWallet::TransactionChangeType for details).
- */
- CHANGE_AUTO,
-};
-
//! Default for -addresstype
constexpr OutputType DEFAULT_ADDRESS_TYPE{OutputType::LEGACY};
//! Default for -changetype
constexpr OutputType DEFAULT_CHANGE_TYPE{OutputType::CHANGE_AUTO};
/** A key pool entry */
class CKeyPool {
public:
int64_t nTime;
CPubKey vchPubKey;
// for change outputs
bool fInternal;
// For keys generated before keypool split upgrade
bool m_pre_split;
CKeyPool();
CKeyPool(const CPubKey &vchPubKeyIn, bool internalIn);
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream &s, Operation ser_action) {
int nVersion = s.GetVersion();
if (!(s.GetType() & SER_GETHASH)) {
READWRITE(nVersion);
}
READWRITE(nTime);
READWRITE(vchPubKey);
if (ser_action.ForRead()) {
try {
READWRITE(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 {
READWRITE(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;
}
} else {
READWRITE(fInternal);
READWRITE(m_pre_split);
}
}
};
/** Address book data */
class CAddressBookData {
public:
std::string name;
std::string purpose;
CAddressBookData() : purpose("unknown") {}
typedef std::map<std::string, std::string> StringMap;
StringMap destdata;
};
struct CRecipient {
CScript scriptPubKey;
Amount nAmount;
bool fSubtractFeeFromAmount;
};
typedef std::map<std::string, std::string> mapValue_t;
static inline void ReadOrderPos(int64_t &nOrderPos, mapValue_t &mapValue) {
if (!mapValue.count("n")) {
// TODO: calculate elsewhere
nOrderPos = -1;
return;
}
nOrderPos = atoi64(mapValue["n"].c_str());
}
static inline void WriteOrderPos(const int64_t &nOrderPos,
mapValue_t &mapValue) {
if (nOrderPos == -1) {
return;
}
mapValue["n"] = i64tostr(nOrderPos);
}
struct COutputEntry {
CTxDestination destination;
Amount amount;
int vout;
};
/** A transaction with a merkle branch linking it to the block chain. */
class CMerkleTx {
private:
/** Constant used in hashBlock to indicate tx has been abandoned */
static const uint256 ABANDON_HASH;
public:
CTransactionRef tx;
uint256 hashBlock;
/**
* An nIndex == -1 means that hashBlock (in nonzero) refers to the earliest
* block in the chain we know this or any in-wallet dependency conflicts
* with. Older clients interpret nIndex == -1 as unconfirmed for backward
* compatibility.
*/
int nIndex;
CMerkleTx() {
SetTx(MakeTransactionRef());
Init();
}
explicit CMerkleTx(CTransactionRef arg) {
SetTx(std::move(arg));
Init();
}
void Init() {
hashBlock = uint256();
nIndex = -1;
}
void SetTx(CTransactionRef arg) { tx = std::move(arg); }
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream &s, Operation ser_action) {
// For compatibility with older versions.
std::vector<uint256> vMerkleBranch;
READWRITE(tx);
READWRITE(hashBlock);
READWRITE(vMerkleBranch);
READWRITE(nIndex);
}
void SetMerkleBranch(const CBlockIndex *pIndex, int posInBlock);
/**
* Return depth of transaction in blockchain:
* <0 : conflicts with a transaction this deep in the blockchain
* 0 : in memory pool, waiting to be included in a block
* >=1 : this many blocks deep in the main chain
*/
int GetDepthInMainChain() const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
bool IsInMainChain() const EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
return GetDepthInMainChain() > 0;
}
/**
* @return number of blocks to maturity for this transaction:
* 0 : is not a coinbase transaction, or is a mature coinbase transaction
* >0 : is a coinbase transaction which matures in this many blocks
*/
int GetBlocksToMaturity() const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
bool hashUnset() const {
return (hashBlock.IsNull() || hashBlock == ABANDON_HASH);
}
bool isAbandoned() const { return (hashBlock == ABANDON_HASH); }
void setAbandoned() { hashBlock = ABANDON_HASH; }
TxId GetId() const { return tx->GetId(); }
bool IsCoinBase() const { return tx->IsCoinBase(); }
bool IsImmatureCoinBase() const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
};
// Get the marginal bytes of spending the specified output
int CalculateMaximumSignedInputSize(const CTxOut &txout,
const CWallet *pwallet);
/**
* A transaction with a bunch of additional info that only the owner cares
* about. It includes any unrecorded transactions needed to link it back to the
* block chain.
*/
class CWalletTx : public CMerkleTx {
private:
const CWallet *pwallet;
public:
/**
* Key/value map with information about the transaction.
*
* The following keys can be read and written through the map and are
* serialized in the wallet database:
*
* "comment", "to" - comment strings provided to sendtoaddress,
* sendfrom, sendmany wallet RPCs
* "replaces_txid" - txid (as HexStr) of transaction replaced by
* bumpfee on transaction created by bumpfee
* "replaced_by_txid" - txid (as HexStr) of transaction created by
* bumpfee on transaction replaced by bumpfee
* "from", "message" - obsolete fields that could be set in UI prior to
* 2011 (removed in commit 4d9b223)
*
* The following keys are serialized in the wallet database, but shouldn't
* be read or written through the map (they will be temporarily added and
* removed from the map during serialization):
*
* "fromaccount" - serialized strFromAccount value
* "n" - serialized nOrderPos value
* "timesmart" - serialized nTimeSmart value
* "spent" - serialized vfSpent value that existed prior to
* 2014 (removed in commit 93a18a3)
*/
mapValue_t mapValue;
std::vector<std::pair<std::string, std::string>> vOrderForm;
unsigned int fTimeReceivedIsTxTime;
//!< time received by this node
unsigned int nTimeReceived;
/**
* Stable timestamp that never changes, and reflects the order a transaction
* was added to the wallet. Timestamp is based on the block time for a
* transaction added as part of a block, or else the time when the
* transaction was received if it wasn't part of a block, with the timestamp
* adjusted in both cases so timestamp order matches the order transactions
* were added to the wallet. More details can be found in
* CWallet::ComputeTimeSmart().
*/
unsigned int nTimeSmart;
/**
* From me flag is set to 1 for transactions that were created by the wallet
* on this bitcoin node, and set to 0 for transactions that were created
* externally and came in through the network or sendrawtransaction RPC.
*/
char fFromMe;
std::string strFromAccount;
//!< position in ordered transaction list
int64_t nOrderPos;
// memory only
mutable bool fDebitCached;
mutable bool fCreditCached;
mutable bool fImmatureCreditCached;
mutable bool fAvailableCreditCached;
mutable bool fWatchDebitCached;
mutable bool fWatchCreditCached;
mutable bool fImmatureWatchCreditCached;
mutable bool fAvailableWatchCreditCached;
mutable bool fChangeCached;
mutable bool fInMempool;
mutable Amount nDebitCached;
mutable Amount nCreditCached;
mutable Amount nImmatureCreditCached;
mutable Amount nAvailableCreditCached;
mutable Amount nWatchDebitCached;
mutable Amount nWatchCreditCached;
mutable Amount nImmatureWatchCreditCached;
mutable Amount nAvailableWatchCreditCached;
mutable Amount nChangeCached;
CWalletTx(const CWallet *pwalletIn, CTransactionRef arg)
: CMerkleTx(std::move(arg)) {
Init(pwalletIn);
}
void Init(const CWallet *pwalletIn) {
pwallet = pwalletIn;
mapValue.clear();
vOrderForm.clear();
fTimeReceivedIsTxTime = false;
nTimeReceived = 0;
nTimeSmart = 0;
fFromMe = false;
strFromAccount.clear();
fDebitCached = false;
fCreditCached = false;
fImmatureCreditCached = false;
fAvailableCreditCached = false;
fWatchDebitCached = false;
fWatchCreditCached = false;
fImmatureWatchCreditCached = false;
fAvailableWatchCreditCached = false;
fChangeCached = false;
fInMempool = false;
nDebitCached = Amount::zero();
nCreditCached = Amount::zero();
nImmatureCreditCached = Amount::zero();
nAvailableCreditCached = Amount::zero();
nWatchDebitCached = Amount::zero();
nWatchCreditCached = Amount::zero();
nAvailableWatchCreditCached = Amount::zero();
nImmatureWatchCreditCached = Amount::zero();
nChangeCached = Amount::zero();
nOrderPos = -1;
}
template <typename Stream> void Serialize(Stream &s) const {
char fSpent = false;
mapValue_t mapValueCopy = mapValue;
mapValueCopy["fromaccount"] = strFromAccount;
WriteOrderPos(nOrderPos, mapValueCopy);
if (nTimeSmart) {
mapValueCopy["timesmart"] = strprintf("%u", nTimeSmart);
}
s << static_cast<const CMerkleTx &>(*this);
//!< Used to be vtxPrev
std::vector<CMerkleTx> vUnused;
s << vUnused << mapValueCopy << vOrderForm << fTimeReceivedIsTxTime
<< nTimeReceived << fFromMe << fSpent;
}
template <typename Stream> void Unserialize(Stream &s) {
Init(nullptr);
char fSpent;
s >> static_cast<CMerkleTx &>(*this);
//!< Used to be vtxPrev
std::vector<CMerkleTx> vUnused;
s >> vUnused >> mapValue >> vOrderForm >> fTimeReceivedIsTxTime >>
nTimeReceived >> fFromMe >> fSpent;
strFromAccount = std::move(mapValue["fromaccount"]);
ReadOrderPos(nOrderPos, mapValue);
nTimeSmart = mapValue.count("timesmart")
? (unsigned int)atoi64(mapValue["timesmart"])
: 0;
mapValue.erase("fromaccount");
mapValue.erase("spent");
mapValue.erase("n");
mapValue.erase("timesmart");
}
//! make sure balances are recalculated
void MarkDirty() {
fCreditCached = false;
fAvailableCreditCached = false;
fImmatureCreditCached = false;
fWatchDebitCached = false;
fWatchCreditCached = false;
fAvailableWatchCreditCached = false;
fImmatureWatchCreditCached = false;
fDebitCached = false;
fChangeCached = false;
}
void BindWallet(CWallet *pwalletIn) {
pwallet = pwalletIn;
MarkDirty();
}
//! filter decides which addresses will count towards the debit
Amount GetDebit(const isminefilter &filter) const;
Amount GetCredit(const isminefilter &filter) const
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
Amount GetImmatureCredit(bool fUseCache = true) const
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
Amount GetAvailableCredit(bool fUseCache = true) const
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
Amount GetImmatureWatchOnlyCredit(const bool fUseCache = true) const
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
Amount GetAvailableWatchOnlyCredit(const bool fUseCache = true) const
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
Amount GetChange() const;
// Get the marginal bytes if spending the specified output from this
// transaction
int GetSpendSize(unsigned int out) const {
return CalculateMaximumSignedInputSize(tx->vout[out], pwallet);
}
void GetAmounts(std::list<COutputEntry> &listReceived,
std::list<COutputEntry> &listSent, Amount &nFee,
std::string &strSentAccount,
const isminefilter &filter) const;
bool IsFromMe(const isminefilter &filter) const {
return GetDebit(filter) > Amount::zero();
}
// True if only scriptSigs are different
bool IsEquivalentTo(const CWalletTx &tx) const;
bool InMempool() const;
bool IsTrusted() const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
int64_t GetTxTime() const;
// RelayWalletTransaction may only be called if fBroadcastTransactions!
bool RelayWalletTransaction(CConnman *connman)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/**
* Pass this transaction to the mempool. Fails if absolute fee exceeds
* absurd fee.
*/
bool AcceptToMemoryPool(const Amount nAbsurdFee, CValidationState &state)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
std::set<TxId> GetConflicts() const;
};
class COutput {
public:
const CWalletTx *tx;
int i;
int nDepth;
/**
* Pre-computed estimated size of this output as a fully-signed input in a
* transaction. Can be -1 if it could not be calculated.
*/
int nInputBytes;
/** Whether we have the private keys to spend this output */
bool fSpendable;
/** Whether we know how to spend this output, ignoring the lack of keys */
bool fSolvable;
/**
* Whether this output is considered safe to spend. Unconfirmed transactions
* from outside keys are considered unsafe and will not be used to fund new
* spending transactions.
*/
bool fSafe;
COutput(const CWalletTx *txIn, int iIn, int nDepthIn, bool fSpendableIn,
bool fSolvableIn, bool fSafeIn) {
tx = txIn;
i = iIn;
nDepth = nDepthIn;
fSpendable = fSpendableIn;
fSolvable = fSolvableIn;
fSafe = fSafeIn;
nInputBytes = -1;
// If known and signable by the given wallet, compute nInputBytes
// Failure will keep this value -1
if (fSpendable && tx) {
nInputBytes = tx->GetSpendSize(i);
}
}
std::string ToString() const;
inline CInputCoin GetInputCoin() const {
return CInputCoin(tx->tx, i, nInputBytes);
}
};
/** Private key that includes an expiration date in case it never gets used. */
class CWalletKey {
public:
CPrivKey vchPrivKey;
int64_t nTimeCreated;
int64_t nTimeExpires;
std::string strComment;
//! todo: add something to note what created it (user, getnewaddress,
//! change) maybe should have a map<string, string> property map
explicit CWalletKey(int64_t nExpires = 0);
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream &s, Operation ser_action) {
int nVersion = s.GetVersion();
if (!(s.GetType() & SER_GETHASH)) READWRITE(nVersion);
READWRITE(vchPrivKey);
READWRITE(nTimeCreated);
READWRITE(nTimeExpires);
READWRITE(LIMITED_STRING(strComment, 65536));
}
};
/**
* DEPRECATED Internal transfers.
* Database key is acentry<account><counter>.
*/
class CAccountingEntry {
public:
std::string strAccount;
Amount nCreditDebit;
int64_t nTime;
std::string strOtherAccount;
std::string strComment;
mapValue_t mapValue;
//!< position in ordered transaction list
int64_t nOrderPos;
uint64_t nEntryNo;
CAccountingEntry() { SetNull(); }
void SetNull() {
nCreditDebit = Amount::zero();
nTime = 0;
strAccount.clear();
strOtherAccount.clear();
strComment.clear();
nOrderPos = -1;
nEntryNo = 0;
}
template <typename Stream> void Serialize(Stream &s) const {
int nVersion = s.GetVersion();
if (!(s.GetType() & SER_GETHASH)) {
s << nVersion;
}
//! Note: strAccount is serialized as part of the key, not here.
s << nCreditDebit << nTime << strOtherAccount;
mapValue_t mapValueCopy = mapValue;
WriteOrderPos(nOrderPos, mapValueCopy);
std::string strCommentCopy = strComment;
if (!mapValueCopy.empty() || !_ssExtra.empty()) {
CDataStream ss(s.GetType(), s.GetVersion());
ss.insert(ss.begin(), '\0');
ss << mapValueCopy;
ss.insert(ss.end(), _ssExtra.begin(), _ssExtra.end());
strCommentCopy.append(ss.str());
}
s << strCommentCopy;
}
template <typename Stream> void Unserialize(Stream &s) {
int nVersion = s.GetVersion();
if (!(s.GetType() & SER_GETHASH)) {
s >> nVersion;
}
//! Note: strAccount is serialized as part of the key, not here.
s >> nCreditDebit >> nTime >> LIMITED_STRING(strOtherAccount, 65536) >>
LIMITED_STRING(strComment, 65536);
size_t nSepPos = strComment.find('\0');
mapValue.clear();
if (std::string::npos != nSepPos) {
CDataStream ss(std::vector<char>(strComment.begin() + nSepPos + 1,
strComment.end()),
s.GetType(), s.GetVersion());
ss >> mapValue;
_ssExtra = std::vector<char>(ss.begin(), ss.end());
}
ReadOrderPos(nOrderPos, mapValue);
if (std::string::npos != nSepPos) {
strComment.erase(nSepPos);
}
mapValue.erase("n");
}
private:
std::vector<char> _ssExtra;
};
struct CoinSelectionParams {
bool use_bnb = true;
size_t change_output_size = 0;
size_t change_spend_size = 0;
CFeeRate effective_fee = CFeeRate(Amount::zero());
size_t tx_noinputs_size = 0;
CoinSelectionParams(bool use_bnb_, size_t change_output_size_,
size_t change_spend_size_, CFeeRate effective_fee_,
size_t tx_noinputs_size_)
: use_bnb(use_bnb_), change_output_size(change_output_size_),
change_spend_size(change_spend_size_), effective_fee(effective_fee_),
tx_noinputs_size(tx_noinputs_size_) {}
CoinSelectionParams() {}
};
// forward declarations for ScanForWalletTransactions/RescanFromTime
class WalletRescanReserver;
/**
* A CWallet is an extension of a keystore, which also maintains a set of
* transactions and balances, and provides the ability to create new
* transactions.
*/
class CWallet final : public CCryptoKeyStore, public CValidationInterface {
private:
static std::atomic<bool> fFlushScheduled;
std::atomic<bool> fAbortRescan{false};
// controlled by WalletRescanReserver
std::atomic<bool> fScanningWallet{false};
std::mutex mutexScanning;
friend class WalletRescanReserver;
WalletBatch *encrypted_batch = nullptr;
//! the current wallet version: clients below this version are not able to
//! load the wallet
int nWalletVersion = FEATURE_BASE;
//! the maximum wallet format version: memory-only variable that specifies
//! to what version this wallet may be upgraded
int nWalletMaxVersion = FEATURE_BASE;
int64_t nNextResend = 0;
int64_t nLastResend = 0;
bool fBroadcastTransactions = false;
/**
* Used to keep track of spent outpoints, and detect and report conflicts
* (double-spends or mutated transactions where the mutant gets mined).
*/
typedef std::multimap<COutPoint, TxId> TxSpends;
TxSpends mapTxSpends;
void AddToSpends(const COutPoint &outpoint, const TxId &wtxid);
void AddToSpends(const TxId &wtxid);
/**
* Mark a transaction (and its in-wallet descendants) as conflicting with a
* particular block.
*/
void MarkConflicted(const uint256 &hashBlock, const TxId &txid);
void SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator>);
/**
* Used by TransactionAddedToMemorypool/BlockConnected/Disconnected.
* Should be called with pindexBlock and posInBlock if this is for a
* transaction that is included in a block.
*/
void SyncTransaction(const CTransactionRef &tx,
const CBlockIndex *pindex = nullptr,
int posInBlock = 0)
EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/* the HD chain data model (external chain counters) */
CHDChain hdChain;
/* HD derive new child key (on internal or external chain) */
void DeriveNewChildKey(WalletBatch &batch, CKeyMetadata &metadata,
CKey &secret, bool internal = false)
EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
std::set<int64_t> setInternalKeyPool;
std::set<int64_t> setExternalKeyPool;
std::set<int64_t> set_pre_split_keypool;
int64_t m_max_keypool_index = 0;
std::map<CKeyID, int64_t> m_pool_key_to_index;
int64_t nTimeFirstKey = 0;
/**
* 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) override
EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/**
* Wallet filename from wallet=<path> command line or config option.
* Used in debug logs and to send RPCs to the right wallet instance when
* more than one wallet is loaded.
*/
std::string m_name;
/** Internal database handle. */
std::unique_ptr<WalletDatabase> database;
/**
* The following is used to keep track of how far behind the wallet is
* from the chain sync, and to allow clients to block on us being caught up.
*
* Note that this is *not* how far we've processed, we may need some rescan
* to have seen all transactions in the chain, but is only used to track
* live BlockConnected callbacks.
*
* Protected by cs_main (see BlockUntilSyncedToCurrentChain)
*/
const CBlockIndex *m_last_block_processed = nullptr;
public:
const CChainParams &chainParams;
/*
* Main wallet lock.
* This lock protects all the fields added by CWallet.
*/
mutable CCriticalSection cs_wallet;
/**
* Get database handle used by this wallet. Ideally this function would not
* be necessary.
*/
WalletDatabase &GetDBHandle() { return *database; }
/**
* Select a set of coins such that nValueRet >= nTargetValue and at least
* all coins from coinControl are selected; Never select unconfirmed coins
* if they are not ours.
*/
bool SelectCoins(const std::vector<COutput> &vAvailableCoins,
const Amount nTargetValue,
std::set<CInputCoin> &setCoinsRet, Amount &nValueRet,
const CCoinControl &coin_control,
CoinSelectionParams &coin_selection_params,
bool &bnb_used) const;
/**
* Get a name for this wallet for logging/debugging purposes.
*/
std::string GetName() const { return m_name; }
void LoadKeyPool(int64_t nIndex, const CKeyPool &keypool)
EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void MarkPreSplitKeys();
// Map from Key ID to key metadata.
std::map<CKeyID, CKeyMetadata> mapKeyMetadata;
// Map from Script ID to key metadata (for watch-only keys).
std::map<CScriptID, CKeyMetadata> m_script_metadata;
typedef std::map<unsigned int, CMasterKey> MasterKeyMap;
MasterKeyMap mapMasterKeys;
unsigned int nMasterKeyMaxID = 0;
/** Construct wallet with specified name and database implementation. */
CWallet(const CChainParams &chainParamsIn, std::string name,
std::unique_ptr<WalletDatabase> databaseIn)
: m_name(std::move(name)), database(std::move(databaseIn)),
chainParams(chainParamsIn) {}
~CWallet() {
delete encrypted_batch;
encrypted_batch = nullptr;
}
std::map<TxId, CWalletTx> mapWallet;
std::list<CAccountingEntry> laccentries;
typedef std::pair<CWalletTx *, CAccountingEntry *> TxPair;
typedef std::multimap<int64_t, TxPair> TxItems;
TxItems wtxOrdered;
int64_t nOrderPosNext = 0;
uint64_t nAccountingEntryNumber = 0;
std::map<CTxDestination, CAddressBookData> mapAddressBook;
std::set<COutPoint> setLockedCoins;
const CWalletTx *GetWalletTx(const TxId &txid) const;
//! check whether we are allowed to upgrade (or already support) to the
//! named feature
bool CanSupportFeature(enum WalletFeature wf) const
EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) {
AssertLockHeld(cs_wallet);
return nWalletMaxVersion >= wf;
}
/**
* populate vCoins with vector of available COutputs.
*/
void AvailableCoins(std::vector<COutput> &vCoins, bool fOnlySafe = true,
const CCoinControl *coinControl = nullptr,
const Amount nMinimumAmount = SATOSHI,
const Amount nMaximumAmount = MAX_MONEY,
const Amount nMinimumSumAmount = MAX_MONEY,
const uint64_t nMaximumCount = 0,
const int nMinDepth = 0,
const int nMaxDepth = 9999999) const
EXCLUSIVE_LOCKS_REQUIRED(cs_main, cs_wallet);
/**
* Return list of available coins and locked coins grouped by non-change
* output address.
*/
std::map<CTxDestination, std::vector<COutput>> ListCoins() const
EXCLUSIVE_LOCKS_REQUIRED(cs_main, cs_wallet);
/**
* Find non-change parent output.
*/
const CTxOut &FindNonChangeParentOutput(const CTransaction &tx,
int output) const;
/**
* Shuffle and select coins until nTargetValue is reached while avoiding
* small change; This method is stochastic for some inputs and upon
* completion the coin set and corresponding actual target value is
* assembled.
*/
bool SelectCoinsMinConf(const Amount nTargetValue,
const CoinEligibilityFilter &eligibility_filter,
std::vector<OutputGroup> groups,
std::set<CInputCoin> &setCoinsRet,
Amount &nValueRet,
const CoinSelectionParams &coin_selection_params,
bool &bnb_used) const;
bool IsSpent(const COutPoint &outpoint) const
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
std::vector<OutputGroup> GroupOutputs(const std::vector<COutput> &outputs,
bool single_coin) const;
bool IsLockedCoin(const COutPoint &outpoint) const
EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void LockCoin(const COutPoint &output) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void UnlockCoin(const COutPoint &output)
EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void UnlockAllCoins() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void ListLockedCoins(std::vector<COutPoint> &vOutpts) const
EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/*
* Rescan abort properties
*/
void AbortRescan() { fAbortRescan = true; }
bool IsAbortingRescan() { return fAbortRescan; }
bool IsScanning() { return fScanningWallet; }
/**
* keystore implementation
* Generate a new key
*/
CPubKey GenerateNewKey(WalletBatch &batch, bool internal = false)
EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Adds a key to the store, and saves it to disk.
bool AddKeyPubKey(const CKey &key, const CPubKey &pubkey) override
EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool AddKeyPubKeyWithDB(WalletBatch &batch, const CKey &key,
const CPubKey &pubkey)
EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Adds a key to the store, without saving it to disk (used by LoadWallet)
bool LoadKey(const CKey &key, const CPubKey &pubkey) {
return CCryptoKeyStore::AddKeyPubKey(key, pubkey);
}
//! Load metadata (used by LoadWallet)
void LoadKeyMetadata(const CKeyID &keyID, const CKeyMetadata &metadata)
EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void LoadScriptMetadata(const CScriptID &script_id,
const CKeyMetadata &metadata)
EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool LoadMinVersion(int nVersion) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) {
AssertLockHeld(cs_wallet);
nWalletVersion = nVersion;
nWalletMaxVersion = std::max(nWalletMaxVersion, nVersion);
return true;
}
void UpdateTimeFirstKey(int64_t nCreateTime)
EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Adds an encrypted key to the store, and saves it to disk.
bool AddCryptedKey(const CPubKey &vchPubKey,
const std::vector<uint8_t> &vchCryptedSecret) override;
//! Adds an encrypted key to the store, without saving it to disk (used by
//! LoadWallet)
bool LoadCryptedKey(const CPubKey &vchPubKey,
const std::vector<uint8_t> &vchCryptedSecret);
bool AddCScript(const CScript &redeemScript) override;
bool LoadCScript(const CScript &redeemScript);
//! Adds a destination data tuple to the store, and saves it to disk
bool AddDestData(const CTxDestination &dest, const std::string &key,
const std::string &value);
//! Erases a destination data tuple in the store and on disk
bool EraseDestData(const CTxDestination &dest, const std::string &key);
//! Adds a destination data tuple to the store, without saving it to disk
void LoadDestData(const CTxDestination &dest, const std::string &key,
const std::string &value);
//! Look up a destination data tuple in the store, return true if found
//! false otherwise
bool GetDestData(const CTxDestination &dest, const std::string &key,
std::string *value) const;
//! Get all destination values matching a prefix.
std::vector<std::string> GetDestValues(const std::string &prefix) const;
//! Adds a watch-only address to the store, and saves it to disk.
bool AddWatchOnly(const CScript &dest, int64_t nCreateTime)
EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool RemoveWatchOnly(const CScript &dest) override
EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Adds a watch-only address to the store, without saving it to disk (used
//! by LoadWallet)
bool LoadWatchOnly(const CScript &dest);
//! Holds a timestamp at which point the wallet is scheduled (externally) to
//! be relocked. Caller must arrange for actual relocking to occur via
//! Lock().
int64_t nRelockTime = 0;
bool Unlock(const SecureString &strWalletPassphrase);
bool ChangeWalletPassphrase(const SecureString &strOldWalletPassphrase,
const SecureString &strNewWalletPassphrase);
bool EncryptWallet(const SecureString &strWalletPassphrase);
void GetKeyBirthTimes(std::map<CTxDestination, int64_t> &mapKeyBirth) const
EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
unsigned int ComputeTimeSmart(const CWalletTx &wtx) const;
/**
* Increment the next transaction order id
* @return next transaction order id
*/
int64_t IncOrderPosNext(WalletBatch *batch = nullptr)
EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
DBErrors ReorderTransactions();
bool AccountMove(std::string strFrom, std::string strTo,
const Amount nAmount, std::string strComment = "")
EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool GetLabelDestination(CTxDestination &dest, const std::string &label,
bool bForceNew = false);
void MarkDirty();
bool AddToWallet(const CWalletTx &wtxIn, bool fFlushOnClose = true);
void LoadToWallet(const CWalletTx &wtxIn);
void TransactionAddedToMempool(const CTransactionRef &tx) override;
void
BlockConnected(const std::shared_ptr<const CBlock> &pblock,
const CBlockIndex *pindex,
const std::vector<CTransactionRef> &vtxConflicted) override;
void
BlockDisconnected(const std::shared_ptr<const CBlock> &pblock) override;
bool AddToWalletIfInvolvingMe(const CTransactionRef &tx,
const CBlockIndex *pIndex, int posInBlock,
bool fUpdate)
EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
int64_t RescanFromTime(int64_t startTime,
const WalletRescanReserver &reserver, bool update);
CBlockIndex *ScanForWalletTransactions(CBlockIndex *pindexStart,
CBlockIndex *pindexStop,
const WalletRescanReserver &reserver,
bool fUpdate = false);
void TransactionRemovedFromMempool(const CTransactionRef &ptx) override;
void ReacceptWalletTransactions();
void ResendWalletTransactions(int64_t nBestBlockTime,
CConnman *connman) override
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
// ResendWalletTransactionsBefore may only be called if
// fBroadcastTransactions!
std::vector<uint256> ResendWalletTransactionsBefore(int64_t nTime,
CConnman *connman)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
Amount GetBalance() const;
Amount GetUnconfirmedBalance() const;
Amount GetImmatureBalance() const;
Amount GetWatchOnlyBalance() const;
Amount GetUnconfirmedWatchOnlyBalance() const;
Amount GetImmatureWatchOnlyBalance() const;
Amount GetLegacyBalance(const isminefilter &filter, int minDepth,
const std::string *account) const;
Amount GetAvailableBalance(const CCoinControl *coinControl = nullptr) const;
OutputType TransactionChangeType(OutputType change_type,
const std::vector<CRecipient> &vecSend);
/**
* Insert additional inputs into the transaction by calling
* CreateTransaction();
*/
bool FundTransaction(CMutableTransaction &tx, Amount &nFeeRet,
int &nChangePosInOut, std::string &strFailReason,
bool lockUnspents,
const std::set<int> &setSubtractFeeFromOutputs,
CCoinControl coinControl);
bool SignTransaction(CMutableTransaction &tx)
EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/**
* Create a new transaction paying the recipients with a set of coins
* selected by SelectCoins(); Also create the change output, when needed
* @note passing nChangePosInOut as -1 will result in setting a random
* position
*/
bool CreateTransaction(const std::vector<CRecipient> &vecSend,
CTransactionRef &tx, CReserveKey &reservekey,
Amount &nFeeRet, int &nChangePosInOut,
std::string &strFailReason,
const CCoinControl &coin_control, bool sign = true);
bool CommitTransaction(
CTransactionRef tx, mapValue_t mapValue,
std::vector<std::pair<std::string, std::string>> orderForm,
std::string fromAccount, CReserveKey &reservekey, CConnman *connman,
CValidationState &state);
void ListAccountCreditDebit(const std::string &strAccount,
std::list<CAccountingEntry> &entries);
bool AddAccountingEntry(const CAccountingEntry &);
bool AddAccountingEntry(const CAccountingEntry &, WalletBatch *batch);
bool DummySignTx(CMutableTransaction &txNew,
const std::set<CTxOut> &txouts) const {
std::vector<CTxOut> v_txouts(txouts.size());
std::copy(txouts.begin(), txouts.end(), v_txouts.begin());
return DummySignTx(txNew, v_txouts);
}
bool DummySignTx(CMutableTransaction &txNew,
const std::vector<CTxOut> &txouts) const;
bool DummySignInput(CTxIn &tx_in, const CTxOut &txout) const;
CFeeRate m_pay_tx_fee{DEFAULT_PAY_TX_FEE};
bool m_spend_zero_conf_change{DEFAULT_SPEND_ZEROCONF_CHANGE};
// will be defined via chainparams
bool m_allow_fallback_fee{true};
// Override with -mintxfee
CFeeRate m_min_fee{DEFAULT_TRANSACTION_MINFEE_PER_KB};
/**
* If fee estimation does not have enough data to provide estimates, use
* this fee instead. Has no effect if not using fee estimation Override with
* -fallbackfee
*/
CFeeRate m_fallback_fee{DEFAULT_FALLBACK_FEE};
OutputType m_default_address_type{DEFAULT_ADDRESS_TYPE};
OutputType m_default_change_type{DEFAULT_CHANGE_TYPE};
bool NewKeyPool();
size_t KeypoolCountExternalKeys() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool TopUpKeyPool(unsigned int kpSize = 0);
/**
* 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);
void KeepKey(int64_t nIndex);
void ReturnKey(int64_t nIndex, bool fInternal, const CPubKey &pubkey);
bool GetKeyFromPool(CPubKey &key, bool internal = false);
int64_t GetOldestKeyPoolTime();
/**
* Marks all keys in the keypool up to and including reserve_key as used.
*/
void MarkReserveKeysAsUsed(int64_t keypool_id)
EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
const std::map<CKeyID, int64_t> &GetAllReserveKeys() const {
return m_pool_key_to_index;
}
std::set<std::set<CTxDestination>> GetAddressGroupings()
EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
std::map<CTxDestination, Amount> GetAddressBalances()
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
std::set<CTxDestination> GetLabelAddresses(const std::string &label) const;
void DeleteLabel(const std::string &label);
isminetype IsMine(const CTxIn &txin) const;
/**
* Returns amount of debit if the input matches the filter, otherwise
* returns 0
*/
Amount GetDebit(const CTxIn &txin, const isminefilter &filter) const;
isminetype IsMine(const CTxOut &txout) const;
Amount GetCredit(const CTxOut &txout, const isminefilter &filter) const;
bool IsChange(const CTxOut &txout) const;
Amount GetChange(const CTxOut &txout) const;
bool IsMine(const CTransaction &tx) const;
/** should probably be renamed to IsRelevantToMe */
bool IsFromMe(const CTransaction &tx) const;
Amount GetDebit(const CTransaction &tx, const isminefilter &filter) const;
/** Returns whether all of the inputs match the filter */
bool IsAllFromMe(const CTransaction &tx, const isminefilter &filter) const;
Amount GetCredit(const CTransaction &tx, const isminefilter &filter) const;
Amount GetChange(const CTransaction &tx) const;
void ChainStateFlushed(const CBlockLocator &loc) override;
DBErrors LoadWallet(bool &fFirstRunRet);
DBErrors ZapWalletTx(std::vector<CWalletTx> &vWtx);
DBErrors ZapSelectTx(std::vector<TxId> &txIdsIn,
std::vector<TxId> &txIdsOut)
EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool SetAddressBook(const CTxDestination &address,
const std::string &strName, const std::string &purpose);
bool DelAddressBook(const CTxDestination &address);
const std::string &GetLabelName(const CScript &scriptPubKey) const;
void GetScriptForMining(std::shared_ptr<CReserveScript> &script);
unsigned int GetKeyPoolSize() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) {
// set{Ex,In}ternalKeyPool
AssertLockHeld(cs_wallet);
return setInternalKeyPool.size() + setExternalKeyPool.size();
}
//! signify that a particular wallet feature is now used. this may change
//! nWalletVersion and nWalletMaxVersion if those are lower
void SetMinVersion(enum WalletFeature, WalletBatch *batch_in = nullptr,
bool fExplicit = false);
//! change which version we're allowed to upgrade to (note that this does
//! not immediately imply upgrading to that format)
bool SetMaxVersion(int nVersion);
//! get the current wallet format (the oldest client version guaranteed to
//! understand this wallet)
int GetVersion() {
LOCK(cs_wallet);
return nWalletVersion;
}
//! Get wallet transactions that conflict with given transaction (spend same
//! outputs)
std::set<TxId> GetConflicts(const TxId &txid) const;
//! Check if a given transaction has any of its outputs spent by another
//! transaction in the wallet
bool HasWalletSpend(const TxId &txid) const
EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Flush wallet (bitdb flush)
void Flush(bool shutdown = false);
/** Wallet is about to be unloaded */
boost::signals2::signal<void()> NotifyUnload;
/**
* Address book entry changed.
* @note called with lock cs_wallet held.
*/
boost::signals2::signal<void(CWallet *wallet, const CTxDestination &address,
const std::string &label, bool isMine,
const std::string &purpose, ChangeType status)>
NotifyAddressBookChanged;
/**
* Wallet transaction added, removed or updated.
* @note called with lock cs_wallet held.
*/
boost::signals2::signal<void(CWallet *wallet, const TxId &txid,
ChangeType status)>
NotifyTransactionChanged;
/** Show progress e.g. for rescan */
boost::signals2::signal<void(const std::string &title, int nProgress)>
ShowProgress;
/** Watch-only address added */
boost::signals2::signal<void(bool fHaveWatchOnly)> NotifyWatchonlyChanged;
/** Inquire whether this wallet broadcasts transactions. */
bool GetBroadcastTransactions() const { return fBroadcastTransactions; }
/** Set whether this wallet broadcasts transactions. */
void SetBroadcastTransactions(bool broadcast) {
fBroadcastTransactions = broadcast;
}
/** Return whether transaction can be abandoned */
bool TransactionCanBeAbandoned(const TxId &txid) const;
/**
* Mark a transaction (and it in-wallet descendants) as abandoned so its
* inputs may be respent.
*/
bool AbandonTransaction(const TxId &txid);
//! Verify wallet naming and perform salvage on the wallet if required
static bool Verify(const CChainParams &chainParams, std::string wallet_file,
bool salvage_wallet, std::string &error_string,
std::string &warning_string);
/**
* Initializes the wallet, returns a new CWallet instance or a null pointer
* in case of an error.
*/
static std::shared_ptr<CWallet>
CreateWalletFromFile(const CChainParams &chainParams,
const std::string &name, const fs::path &path);
/**
* Wallet post-init setup
* Gives the wallet a chance to register repetitive tasks and complete
* post-init tasks
*/
void postInitProcess();
bool BackupWallet(const std::string &strDest);
/* Set the HD chain model (chain child index counters) */
void SetHDChain(const CHDChain &chain, bool memonly);
const CHDChain &GetHDChain() const { return hdChain; }
/* Returns true if HD is enabled */
bool IsHDEnabled() 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);
/**
* Blocks until the wallet state is up-to-date to /at least/ the current
* chain at the time this function is entered.
* Obviously holding cs_main/cs_wallet when going into this call may cause
* deadlock
*/
void BlockUntilSyncedToCurrentChain() LOCKS_EXCLUDED(cs_main, cs_wallet);
/**
* 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 CBasicKeyStore 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);
-
- /**
- * Get a destination of the requested type (if possible) to the specified
- * script. This function will automatically add the necessary scripts to the
- * wallet.
- */
- CTxDestination AddAndGetDestinationForScript(const CScript &script,
- OutputType);
};
/** A key allocated from the key pool. */
class CReserveKey final : public CReserveScript {
protected:
CWallet *pwallet;
int64_t nIndex{-1};
CPubKey vchPubKey;
bool fInternal{false};
public:
explicit CReserveKey(CWallet *pwalletIn) { pwallet = pwalletIn; }
CReserveKey(const CReserveKey &) = delete;
CReserveKey &operator=(const CReserveKey &) = delete;
~CReserveKey() { ReturnKey(); }
void ReturnKey();
bool GetReservedKey(CPubKey &pubkey, bool internal = false);
void KeepKey();
void KeepScript() override { KeepKey(); }
};
/**
* DEPRECATED Account information.
* Stored in wallet with key "acc"+string account name.
*/
class CAccount {
public:
CPubKey vchPubKey;
CAccount() { SetNull(); }
void SetNull() { vchPubKey = CPubKey(); }
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream &s, Operation ser_action) {
int nVersion = s.GetVersion();
if (!(s.GetType() & SER_GETHASH)) {
READWRITE(nVersion);
}
READWRITE(vchPubKey);
}
};
-bool ParseOutputType(const std::string &str, OutputType &output_type);
-const std::string &FormatOutputType(OutputType type);
-
-/**
- * Get a destination of the requested type (if possible) to the specified key.
- * The caller must make sure LearnRelatedScripts has been called beforehand.
- */
-CTxDestination GetDestinationForKey(const CPubKey &key, OutputType);
-
-/**
- * Get all destinations (potentially) supported by the wallet for the given key.
- */
-std::vector<CTxDestination> GetAllDestinationsForKey(const CPubKey &key);
-
/** RAII object to check and reserve a wallet rescan */
class WalletRescanReserver {
private:
CWallet *m_wallet;
bool m_could_reserve;
public:
explicit WalletRescanReserver(CWallet *w)
: m_wallet(w), m_could_reserve(false) {}
bool reserve() {
assert(!m_could_reserve);
std::lock_guard<std::mutex> lock(m_wallet->mutexScanning);
if (m_wallet->fScanningWallet) {
return false;
}
m_wallet->fScanningWallet = true;
m_could_reserve = true;
return true;
}
bool isReserved() const {
return (m_could_reserve && m_wallet->fScanningWallet);
}
~WalletRescanReserver() {
std::lock_guard<std::mutex> lock(m_wallet->mutexScanning);
if (m_could_reserve) {
m_wallet->fScanningWallet = false;
}
}
};
// Calculate the size of the transaction assuming all signatures are max size
// Use DummySignatureCreator, which inserts 72 byte signatures everywhere.
// NOTE: this requires that all inputs must be in mapWallet (eg the tx should
// be IsAllFromMe).
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx,
const CWallet *wallet);
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx,
const CWallet *wallet,
const std::vector<CTxOut> &txouts);
#endif // BITCOIN_WALLET_WALLET_H
diff --git a/test/functional/rpc_createmultisig.py b/test/functional/rpc_createmultisig.py
new file mode 100755
index 000000000..739989c16
--- /dev/null
+++ b/test/functional/rpc_createmultisig.py
@@ -0,0 +1,103 @@
+#!/usr/bin/env python3
+# Copyright (c) 2015-2017 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Test transaction signing using the signrawtransaction* RPCs."""
+
+from test_framework.test_framework import BitcoinTestFramework
+import decimal
+
+
+class RpcCreateMultiSigTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.num_nodes = 3
+
+ def get_keys(self):
+ node0, node1, node2 = self.nodes
+ self.add = [node1.getnewaddress() for _ in range(self.nkeys)]
+ self.pub = [node1.getaddressinfo(a)["pubkey"] for a in self.add]
+ self.priv = [node1.dumpprivkey(a) for a in self.add]
+ self.final = node2.getnewaddress()
+
+ def run_test(self):
+ node0, node1, node2 = self.nodes
+
+ # 50 BCH each, rest will be 25 BCH each
+ node0.generate(149)
+ self.sync_all()
+
+ self.moved = 0
+ for self.nkeys in [3, 5]:
+ for self.nsigs in [2, 3]:
+ self.get_keys()
+ self.do_multisig()
+
+ self.checkbalances()
+
+ def checkbalances(self):
+ node0, node1, node2 = self.nodes
+ node0.generate(100)
+ self.sync_all()
+
+ bal0 = node0.getbalance()
+ bal1 = node1.getbalance()
+ bal2 = node2.getbalance()
+
+ height = node0.getblockchaininfo()["blocks"]
+ assert 150 < height < 350
+ total = 149 * 50 + (height - 149 - 100) * 25
+ assert bal1 == 0
+ assert bal2 == self.moved
+ assert bal0 + bal1 + bal2 == total
+
+ def do_multisig(self):
+ node0, node1, node2 = self.nodes
+
+ msig = node2.createmultisig(self.nsigs, self.pub)
+ madd = msig["address"]
+ mredeem = msig["redeemScript"]
+
+ # compare against addmultisigaddress
+ msigw = node1.addmultisigaddress(self.nsigs, self.pub, None)
+ maddw = msigw["address"]
+ mredeemw = msigw["redeemScript"]
+ # addmultisigiaddress and createmultisig work the same
+ assert maddw == madd
+ assert mredeemw == mredeem
+
+ txid = node0.sendtoaddress(madd, 40)
+
+ tx = node0.getrawtransaction(txid, True)
+ vout = [v["n"] for v in tx["vout"]
+ if madd in v["scriptPubKey"].get("addresses", [])]
+ assert len(vout) == 1
+ vout = vout[0]
+ scriptPubKey = tx["vout"][vout]["scriptPubKey"]["hex"]
+ value = tx["vout"][vout]["value"]
+ prevtxs = [{"txid": txid, "vout": vout, "scriptPubKey": scriptPubKey,
+ "redeemScript": mredeem, "amount": value}]
+
+ node0.generate(1)
+
+ outval = value - decimal.Decimal("0.00001000")
+ rawtx = node2.createrawtransaction(
+ [{"txid": txid, "vout": vout}], [{self.final: outval}])
+
+ rawtx2 = node2.signrawtransactionwithkey(
+ rawtx, self.priv[0:self.nsigs-1], prevtxs)
+ rawtx3 = node2.signrawtransactionwithkey(
+ rawtx2["hex"], [self.priv[-1]], prevtxs)
+
+ self.moved += outval
+ tx = node0.sendrawtransaction(rawtx3["hex"], True)
+ blk = node0.generate(1)[0]
+ assert tx in node0.getblock(blk)["tx"]
+
+ txinfo = node0.getrawtransaction(tx, True, blk)
+ self.log.info("n/m={}/{} size={}".format(self.nsigs,
+ self.nkeys, txinfo["size"]))
+
+
+if __name__ == '__main__':
+ RpcCreateMultiSigTest().main()
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Mar 2, 09:32 (1 d, 4 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5187215
Default Alt Text
(519 KB)
Attached To
rABC Bitcoin ABC
Event Timeline
Log In to Comment