diff --git a/doc/release-notes.md b/doc/release-notes.md
index 51b6903d1..aaf63802b 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -1,10 +1,10 @@
Bitcoin ABC version 0.18.3 is now available from:
This release includes the following features and fixes:
- Remove support for Qt4
- Upgrade reproducible build to us Qt 5.9.6
- Improve SHA256 performance using SSE4.1, AVX2 and/or SHA if available.
- Add a mode argument to the `getmemoryinfo` RPC call to query `malloc_info` from the system if available.
-
+ - Disable safe mode by default
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index e185be76e..8e2bb5b1c 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,292 +1,293 @@
# Copyright (c) 2017 The Bitcoin developers
cmake_minimum_required(VERSION 3.5)
project(BitcoinABC)
set(CMAKE_CXX_STANDARD 11)
# 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)
# Ensure that WINDRES_PREPROC is enabled when using windres.
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
list(APPEND CMAKE_RC_FLAGS "-DWINDRES_PREPROC")
endif()
# Enable warning
include(AddCompilerFlags)
add_c_compiler_flag(-Wnested-externs -Wstrict-prototypes)
add_compiler_flag(
-Wall
-Wextra
-Wformat
-Wvla
-Wformat-security
-Wcast-align
-Wunused-parameter
# FIXME: Activating this flag cause cmake to fail on leveldb.
# -Wthread-safety-analysis
-Wshadow
)
option(EXTRA_WARNINGS "Enable extra warnings" OFF)
if(EXTRA_WARNINGS)
add_cxx_compiler_flag(-Wsuggest-override)
else()
add_compiler_flag(-Wno-unused-parameter)
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)
# Because the Bitcoin ABc source code is disorganised, we
# end up with a bunch of libraries without any aparent
# 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
rpc/protocol.cpp
support/cleanse.cpp
support/lockedpool.cpp
sync.cpp
threadinterrupt.cpp
uint256.cpp
util.cpp
utilmoneystr.cpp
utilstrencodings.cpp
utiltime.cpp
)
target_compile_definitions(util PUBLIC HAVE_CONFIG_H)
target_include_directories(util
PUBLIC
.
# To access the config.
${CMAKE_CURRENT_BINARY_DIR}
)
# 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 program_options 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
dstencode.cpp
feerate.cpp
globals.cpp
core_read.cpp
core_write.cpp
key.cpp
keystore.cpp
netaddress.cpp
netbase.cpp
primitives/block.cpp
protocol.cpp
scheduler.cpp
script/sign.cpp
script/standard.cpp
warnings.cpp
)
target_link_libraries(common util secp256k1)
# libbitcoinconsensus
add_library(bitcoinconsensus
arith_uint256.cpp
hash.cpp
primitives/transaction.cpp
pubkey.cpp
script/bitcoinconsensus.cpp
script/interpreter.cpp
script/script.cpp
script/script_error.cpp
script/sigencoding.cpp
uint256.cpp
utilstrencodings.cpp
)
target_link_libraries(bitcoinconsensus common)
# Bitcoin server facilities
add_library(server
addrman.cpp
addrdb.cpp
bloom.cpp
blockencodings.cpp
chain.cpp
checkpoints.cpp
config.cpp
consensus/activation.cpp
consensus/tx_verify.cpp
globals.cpp
httprpc.cpp
httpserver.cpp
init.cpp
dbwrapper.cpp
merkleblock.cpp
miner.cpp
net.cpp
net_processing.cpp
noui.cpp
policy/fees.cpp
policy/policy.cpp
pow.cpp
rest.cpp
rpc/abc.cpp
rpc/blockchain.cpp
rpc/jsonrpcrequest.cpp
rpc/mining.cpp
rpc/misc.cpp
rpc/net.cpp
rpc/rawtransaction.cpp
+ rpc/safemode.cpp
rpc/server.cpp
script/scriptcache.cpp
script/sigcache.cpp
script/ismine.cpp
timedata.cpp
torcontrol.cpp
txdb.cpp
txmempool.cpp
ui_interface.cpp
validation.cpp
validationinterface.cpp
versionbits.cpp
)
# This require libevent
find_package(Event REQUIRED)
target_include_directories(server PRIVATE leveldb/helpers/memenv)
target_link_libraries(server
Event
bitcoinconsensus
leveldb
memenv
)
# Test suite.
add_subdirectory(test)
# 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()
# 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)
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)
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()
# Bitcoin-qt
if(BUILD_BITCOIN_QT)
add_subdirectory(qt)
endif()
diff --git a/src/Makefile.am b/src/Makefile.am
index a19f6278d..c2b47c72a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,636 +1,638 @@
# 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)
AM_CXXFLAGS = $(HARDENED_CXXFLAGS) $(ERROR_CXXFLAGS)
AM_CPPFLAGS = $(HARDENED_CPPFLAGS)
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) -I$(builddir)/obj $(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 \
base58.h \
bloom.h \
blockencodings.h \
blockfileinfo.h \
blockindexworkcomparator.h \
blockstatus.h \
blockvalidity.h \
cashaddr.h \
cashaddrenc.h \
chain.h \
chainparams.h \
chainparamsbase.h \
chainparamsseeds.h \
checkpoints.h \
checkqueue.h \
clientversion.h \
coins.h \
compat.h \
compat/byteswap.h \
compat/endian.h \
compat/sanity.h \
compressor.h \
config.h \
consensus/activation.h \
consensus/consensus.h \
consensus/tx_verify.h \
core_io.h \
core_memusage.h \
cuckoocache.h \
diskblockpos.h \
dstencode.h \
fs.h \
globals.h \
httprpc.h \
httpserver.h \
indirectmap.h \
init.h \
key.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 \
policy/fees.h \
policy/policy.h \
pow.h \
protocol.h \
random.h \
reverselock.h \
rpc/blockchain.h \
rpc/client.h \
rpc/jsonrpcrequest.h \
rpc/mining.h \
rpc/misc.h \
rpc/protocol.h \
+ rpc/safemode.h \
rpc/server.h \
rpc/tojson.h \
rpc/register.h \
scheduler.h \
script/scriptcache.h \
script/sigcache.h \
script/sign.h \
script/standard.h \
script/ismine.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.h \
utilmoneystr.h \
utiltime.h \
validation.h \
validationinterface.h \
versionbits.h \
wallet/coincontrol.h \
wallet/crypter.h \
wallet/db.h \
wallet/finaltx.h \
wallet/rpcdump.h \
wallet/fees.h \
wallet/init.h \
wallet/rpcwallet.h \
wallet/wallet.h \
wallet/walletdb.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 = \
addrman.cpp \
addrdb.cpp \
bloom.cpp \
blockencodings.cpp \
chain.cpp \
checkpoints.cpp \
config.cpp \
consensus/activation.cpp \
consensus/tx_verify.cpp \
globals.cpp \
httprpc.cpp \
httpserver.cpp \
init.cpp \
dbwrapper.cpp \
merkleblock.cpp \
miner.cpp \
net.cpp \
net_processing.cpp \
noui.cpp \
policy/fees.cpp \
policy/policy.cpp \
pow.cpp \
rest.cpp \
rpc/abc.cpp \
rpc/blockchain.cpp \
rpc/jsonrpcrequest.cpp \
rpc/mining.cpp \
rpc/misc.cpp \
rpc/net.cpp \
rpc/rawtransaction.cpp \
+ rpc/safemode.cpp \
rpc/server.cpp \
script/scriptcache.cpp \
script/sigcache.cpp \
script/ismine.cpp \
timedata.cpp \
torcontrol.cpp \
txdb.cpp \
txmempool.cpp \
ui_interface.cpp \
validation.cpp \
validationinterface.cpp \
versionbits.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 = \
wallet/crypter.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 \
$(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
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/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 \
tinyformat.h \
uint256.cpp \
uint256.h \
utilstrencodings.cpp \
utilstrencodings.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 \
dstencode.cpp \
feerate.cpp \
globals.cpp \
core_read.cpp \
core_write.cpp \
key.cpp \
keystore.cpp \
netaddress.cpp \
netbase.cpp \
protocol.cpp \
scheduler.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 \
rpc/protocol.cpp \
support/cleanse.cpp \
sync.cpp \
threadinterrupt.cpp \
uint256.cpp \
uint256.h \
util.cpp \
utilmoneystr.cpp \
utilstrencodings.cpp \
utiltime.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/compat.h \
seeder/db.cpp \
seeder/db.h \
seeder/dns.cpp \
seeder/dns.h \
seeder/strlcpy.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_COMMON) \
$(LIBUNIVALUE) \
$(LIBBITCOIN_UTIL) \
$(LIBBITCOIN_WALLET) \
$(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
DISTCLEANFILES = obj/build.h
EXTRA_DIST = $(CTAES_DIST)
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=$(
#include
#include
#ifndef WIN32
#include
#endif
#include
#include
#include
#include
#include
#include
#if ENABLE_ZMQ
#include "zmq/zmqnotificationinterface.h"
#endif
bool fFeeEstimatesInitialized = false;
static const bool DEFAULT_PROXYRANDOMIZE = true;
static const bool DEFAULT_REST_ENABLE = false;
-static const bool DEFAULT_DISABLE_SAFEMODE = false;
static const bool DEFAULT_STOPAFTERBLOCKIMPORT = false;
std::unique_ptr g_connman;
std::unique_ptr peerLogic;
#if ENABLE_ZMQ
static CZMQNotificationInterface *pzmqNotificationInterface = nullptr;
#endif
#ifdef WIN32
// Win32 LevelDB doesn't use filedescriptors, and the ones used for accessing
// block files don't count towards the fd_set size limit anyway.
#define MIN_CORE_FILEDESCRIPTORS 0
#else
#define MIN_CORE_FILEDESCRIPTORS 150
#endif
static const char *FEE_ESTIMATES_FILENAME = "fee_estimates.dat";
//////////////////////////////////////////////////////////////////////////////
//
// Shutdown
//
//
// Thread management and startup/shutdown:
//
// The network-processing threads are all part of a thread group created by
// AppInit() or the Qt main() function.
//
// A clean exit happens when StartShutdown() or the SIGTERM signal handler sets
// fRequestShutdown, which triggers the DetectShutdownThread(), which interrupts
// the main thread group. DetectShutdownThread() then exits, which causes
// AppInit() to continue (it .joins the shutdown thread). Shutdown() is then
// called to clean up database connections, and stop other threads that should
// only be stopped after the main network-processing threads have exited.
//
// Note that if running -daemon the parent process returns from AppInit2 before
// adding any threads to the threadGroup, so .join_all() returns immediately and
// the parent exits from main().
//
// Shutdown for Qt is very similar, only it uses a QTimer to detect
// fRequestShutdown getting set, and then does the normal Qt shutdown thing.
//
std::atomic fRequestShutdown(false);
std::atomic fDumpMempoolLater(false);
void StartShutdown() {
fRequestShutdown = true;
}
bool ShutdownRequested() {
return fRequestShutdown;
}
/**
* This is a minimally invasive approach to shutdown on LevelDB read errors from
* the chainstate, while keeping user interface out of the common library, which
* is shared between bitcoind, and bitcoin-qt and non-server tools.
*/
class CCoinsViewErrorCatcher final : public CCoinsViewBacked {
public:
CCoinsViewErrorCatcher(CCoinsView *view) : CCoinsViewBacked(view) {}
bool GetCoin(const COutPoint &outpoint, Coin &coin) const override {
try {
return CCoinsViewBacked::GetCoin(outpoint, coin);
} catch (const std::runtime_error &e) {
uiInterface.ThreadSafeMessageBox(
_("Error reading from database, shutting down."), "",
CClientUIInterface::MSG_ERROR);
LogPrintf("Error reading from database: %s\n", e.what());
// Starting the shutdown sequence and returning false to the caller
// would be interpreted as 'entry not found' (as opposed to unable
// to read data), and could lead to invalid interpretation. Just
// exit immediately, as we can't continue anyway, and all writes
// should be atomic.
abort();
}
}
// Writes do not need similar protection, as failure to write is handled by
// the caller.
};
static CCoinsViewErrorCatcher *pcoinscatcher = nullptr;
static std::unique_ptr globalVerifyHandle;
void Interrupt(boost::thread_group &threadGroup) {
InterruptHTTPServer();
InterruptHTTPRPC();
InterruptRPC();
InterruptREST();
InterruptTorControl();
InterruptMapPort();
if (g_connman) {
g_connman->Interrupt();
}
threadGroup.interrupt_all();
}
void Shutdown() {
LogPrintf("%s: In progress...\n", __func__);
static CCriticalSection cs_Shutdown;
TRY_LOCK(cs_Shutdown, lockShutdown);
if (!lockShutdown) {
return;
}
/// Note: Shutdown() must be able to handle cases in which AppInit2() failed
/// part of the way, for example if the data directory was found to be
/// locked. Be sure that anything that writes files or flushes caches only
/// does this if the respective module was initialized.
RenameThread("bitcoin-shutoff");
mempool.AddTransactionsUpdated(1);
StopHTTPRPC();
StopREST();
StopRPC();
StopHTTPServer();
#ifdef ENABLE_WALLET
for (CWalletRef pwallet : vpwallets) {
pwallet->Flush(false);
}
#endif
StopMapPort();
// Because these depend on each-other, we make sure that neither can be
// using the other before destroying them.
UnregisterValidationInterface(peerLogic.get());
g_connman->Stop();
peerLogic.reset();
g_connman.reset();
StopTorControl();
if (fDumpMempoolLater &&
gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
DumpMempool();
}
if (fFeeEstimatesInitialized) {
fs::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME;
CAutoFile est_fileout(fsbridge::fopen(est_path, "wb"), SER_DISK,
CLIENT_VERSION);
if (!est_fileout.IsNull()) {
mempool.WriteFeeEstimates(est_fileout);
} else {
LogPrintf("%s: Failed to write fee estimates to %s\n", __func__,
est_path.string());
}
fFeeEstimatesInitialized = false;
}
{
LOCK(cs_main);
if (pcoinsTip != nullptr) {
FlushStateToDisk();
}
delete pcoinsTip;
pcoinsTip = nullptr;
delete pcoinscatcher;
pcoinscatcher = nullptr;
delete pcoinsdbview;
pcoinsdbview = nullptr;
delete pblocktree;
pblocktree = nullptr;
}
#ifdef ENABLE_WALLET
for (CWalletRef pwallet : vpwallets) {
pwallet->Flush(true);
}
#endif
#if ENABLE_ZMQ
if (pzmqNotificationInterface) {
UnregisterValidationInterface(pzmqNotificationInterface);
delete pzmqNotificationInterface;
pzmqNotificationInterface = nullptr;
}
#endif
#ifndef WIN32
try {
fs::remove(GetPidFile());
} catch (const fs::filesystem_error &e) {
LogPrintf("%s: Unable to remove pidfile: %s\n", __func__, e.what());
}
#endif
UnregisterAllValidationInterfaces();
#ifdef ENABLE_WALLET
for (CWalletRef pwallet : vpwallets) {
delete pwallet;
}
vpwallets.clear();
#endif
globalVerifyHandle.reset();
ECC_Stop();
LogPrintf("%s: done\n", __func__);
}
/**
* Signal handlers are very limited in what they are allowed to do, so:
*/
void HandleSIGTERM(int) {
fRequestShutdown = true;
}
void HandleSIGHUP(int) {
GetLogger().fReopenDebugLog = true;
}
void OnRPCStarted() {
uiInterface.NotifyBlockTip.connect(&RPCNotifyBlockChange);
}
void OnRPCStopped() {
uiInterface.NotifyBlockTip.disconnect(&RPCNotifyBlockChange);
RPCNotifyBlockChange(false, nullptr);
g_best_block_cv.notify_all();
LogPrint(BCLog::RPC, "RPC stopped.\n");
}
-void OnRPCPreCommand(const ContextFreeRPCCommand &cmd) {
- // Observe safe mode.
- std::string strWarning = GetWarnings("rpc");
- if (strWarning != "" &&
- !gArgs.GetBoolArg("-disablesafemode", DEFAULT_DISABLE_SAFEMODE) &&
- !cmd.okSafeMode) {
- throw JSONRPCError(RPC_FORBIDDEN_BY_SAFE_MODE,
- std::string("Safe mode: ") + strWarning);
- }
-}
-
std::string HelpMessage(HelpMessageMode mode) {
const auto defaultBaseParams =
CreateBaseChainParams(CBaseChainParams::MAIN);
const auto testnetBaseParams =
CreateBaseChainParams(CBaseChainParams::TESTNET);
const auto defaultChainParams = CreateChainParams(CBaseChainParams::MAIN);
const auto testnetChainParams =
CreateChainParams(CBaseChainParams::TESTNET);
const bool showDebug = gArgs.GetBoolArg("-help-debug", false);
// When adding new options to the categories, please keep and ensure
// alphabetical ordering. Do not translate _(...) -help-debug options, Many
// technical terms, and only a very small audience, so is unnecessary stress
// to translators.
std::string strUsage = HelpMessageGroup(_("Options:"));
strUsage += HelpMessageOpt("-?", _("Print this help message and exit"));
strUsage += HelpMessageOpt("-version", _("Print version and exit"));
strUsage += HelpMessageOpt(
"-alertnotify=",
_("Execute command when a relevant alert is received or we see a "
"really long fork (%s in cmd is replaced by message)"));
strUsage += HelpMessageOpt("-blocknotify=",
_("Execute command when the best block changes "
"(%s in cmd is replaced by block hash)"));
if (showDebug) {
strUsage += HelpMessageOpt(
"-blocksonly",
strprintf(
_("Whether to operate in a blocks only mode (default: %d)"),
DEFAULT_BLOCKSONLY));
}
strUsage += HelpMessageOpt(
"-assumevalid=",
strprintf(
_("If this block is in the chain assume that it and its ancestors "
"are valid and potentially skip their script verification (0 to "
"verify all, default: %s, testnet: %s)"),
defaultChainParams->GetConsensus().defaultAssumeValid.GetHex(),
testnetChainParams->GetConsensus().defaultAssumeValid.GetHex()));
strUsage += HelpMessageOpt(
"-conf=", strprintf(_("Specify configuration file (default: %s)"),
BITCOIN_CONF_FILENAME));
if (mode == HMM_BITCOIND) {
#if HAVE_DECL_DAEMON
strUsage += HelpMessageOpt(
"-daemon",
_("Run in the background as a daemon and accept commands"));
#endif
}
strUsage += HelpMessageOpt("-datadir=", _("Specify data directory"));
if (showDebug) {
strUsage += HelpMessageOpt(
"-dbbatchsize",
strprintf(
"Maximum database write batch size in bytes (default: %u)",
nDefaultDbBatchSize));
}
strUsage += HelpMessageOpt(
"-dbcache=",
strprintf(
_("Set database cache size in megabytes (%d to %d, default: %d)"),
nMinDbCache, nMaxDbCache, nDefaultDbCache));
if (showDebug) {
strUsage += HelpMessageOpt(
"-feefilter", strprintf("Tell other nodes to filter invs to us by "
"our mempool min fee (default: %d)",
DEFAULT_FEEFILTER));
}
strUsage += HelpMessageOpt(
"-loadblock=",
_("Imports blocks from external blk000??.dat file on startup"));
strUsage += HelpMessageOpt(
"-maxorphantx=", strprintf(_("Keep at most unconnectable "
"transactions in memory (default: %u)"),
DEFAULT_MAX_ORPHAN_TRANSACTIONS));
strUsage += HelpMessageOpt("-maxmempool=",
strprintf(_("Keep the transaction memory pool "
"below megabytes (default: %u)"),
DEFAULT_MAX_MEMPOOL_SIZE));
strUsage +=
HelpMessageOpt("-mempoolexpiry=",
strprintf(_("Do not keep transactions in the mempool "
"longer than hours (default: %u)"),
DEFAULT_MEMPOOL_EXPIRY));
if (showDebug) {
strUsage += HelpMessageOpt(
"-minimumchainwork=",
strprintf(
"Minimum work assumed to exist on a valid chain in hex "
"(default: %s, testnet: %s)",
defaultChainParams->GetConsensus().nMinimumChainWork.GetHex(),
testnetChainParams->GetConsensus().nMinimumChainWork.GetHex()));
}
strUsage +=
HelpMessageOpt("-persistmempool",
strprintf(_("Whether to save the mempool on shutdown "
"and load on restart (default: %u)"),
DEFAULT_PERSIST_MEMPOOL));
strUsage += HelpMessageOpt(
"-blockreconstructionextratxn=",
strprintf(_("Extra transactions to keep in memory for compact block "
"reconstructions (default: %u)"),
DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN));
strUsage += HelpMessageOpt(
"-par=",
strprintf(_("Set the number of script verification threads (%u to %d, "
"0 = auto, <0 = leave that many cores free, default: %d)"),
-GetNumCores(), MAX_SCRIPTCHECK_THREADS,
DEFAULT_SCRIPTCHECK_THREADS));
#ifndef WIN32
strUsage += HelpMessageOpt(
"-pid=",
strprintf(_("Specify pid file (default: %s)"), BITCOIN_PID_FILENAME));
#endif
strUsage += HelpMessageOpt(
"-prune=",
strprintf(
_("Reduce storage requirements by enabling pruning (deleting) of "
"old blocks. This allows the pruneblockchain RPC to be called to "
"delete specific blocks, and enables automatic pruning of old "
"blocks if a target size in MiB is provided. This mode is "
"incompatible with -txindex and -rescan. "
"Warning: Reverting this setting requires re-downloading the "
"entire blockchain. "
"(default: 0 = disable pruning blocks, 1 = allow manual pruning "
"via RPC, >%u = automatically prune block files to stay under "
"the specified target size in MiB)"),
MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024));
strUsage += HelpMessageOpt(
"-reindex-chainstate",
_("Rebuild chain state from the currently indexed blocks"));
strUsage +=
HelpMessageOpt("-reindex", _("Rebuild chain state and block index from "
"the blk*.dat files on disk"));
#ifndef WIN32
strUsage += HelpMessageOpt(
"-sysperms",
_("Create new files with system default permissions, instead of umask "
"077 (only effective with disabled wallet functionality)"));
#endif
strUsage += HelpMessageOpt(
"-txindex", strprintf(_("Maintain a full transaction index, used by "
"the getrawtransaction rpc call (default: %d)"),
DEFAULT_TXINDEX));
strUsage += HelpMessageOpt(
"-usecashaddr", _("Use Cash Address for destination encoding instead "
"of base58 (activate by default on Jan, 14)"));
strUsage += HelpMessageGroup(_("Connection options:"));
strUsage += HelpMessageOpt(
"-addnode=",
_("Add a node to connect to and attempt to keep the connection open "
"(see the `addnode` RPC command help for more info)"));
strUsage += HelpMessageOpt(
"-banscore=",
strprintf(
_("Threshold for disconnecting misbehaving peers (default: %u)"),
DEFAULT_BANSCORE_THRESHOLD));
strUsage += HelpMessageOpt(
"-bantime=", strprintf(_("Number of seconds to keep misbehaving "
"peers from reconnecting (default: %u)"),
DEFAULT_MISBEHAVING_BANTIME));
strUsage += HelpMessageOpt("-bind=",
_("Bind to given address and always listen on "
"it. Use [host]:port notation for IPv6"));
strUsage += HelpMessageOpt(
"-connect=", _("Connect only to the specified node(s); -connect=0 "
"disables automatic connections (the rules for this "
"peer are the same as for -addnode)"));
strUsage += HelpMessageOpt("-discover",
_("Discover own IP addresses (default: 1 when "
"listening and no -externalip or -proxy)"));
strUsage += HelpMessageOpt(
"-dns",
_("Allow DNS lookups for -addnode, -seednode and -connect") + " " +
strprintf(_("(default: %d)"), DEFAULT_NAME_LOOKUP));
strUsage += HelpMessageOpt(
"-dnsseed", _("Query for peer addresses via DNS lookup, if low on "
"addresses (default: 1 unless -connect/-noconnect)"));
strUsage += HelpMessageOpt("-externalip=",
_("Specify your own public address"));
strUsage += HelpMessageOpt(
"-forcednsseed",
strprintf(
_("Always query for peer addresses via DNS lookup (default: %d)"),
DEFAULT_FORCEDNSSEED));
strUsage +=
HelpMessageOpt("-listen", _("Accept connections from outside (default: "
"1 if no -proxy or -connect/-noconnect)"));
strUsage += HelpMessageOpt(
"-listenonion",
strprintf(_("Automatically create Tor hidden service (default: %d)"),
DEFAULT_LISTEN_ONION));
strUsage += HelpMessageOpt(
"-maxconnections=",
strprintf(_("Maintain at most connections to peers (default: %u)"),
DEFAULT_MAX_PEER_CONNECTIONS));
strUsage +=
HelpMessageOpt("-maxreceivebuffer=",
strprintf(_("Maximum per-connection receive buffer, "
"*1000 bytes (default: %u)"),
DEFAULT_MAXRECEIVEBUFFER));
strUsage += HelpMessageOpt(
"-maxsendbuffer=", strprintf(_("Maximum per-connection send buffer, "
"*1000 bytes (default: %u)"),
DEFAULT_MAXSENDBUFFER));
strUsage += HelpMessageOpt(
"-maxtimeadjustment",
strprintf(_("Maximum allowed median peer time offset adjustment. Local "
"perspective of time may be influenced by peers forward or "
"backward by this amount. (default: %u seconds)"),
DEFAULT_MAX_TIME_ADJUSTMENT));
strUsage +=
HelpMessageOpt("-onion=",
strprintf(_("Use separate SOCKS5 proxy to reach peers "
"via Tor hidden services (default: %s)"),
"-proxy"));
strUsage += HelpMessageOpt(
"-onlynet=",
_("Only connect to nodes in network (ipv4, ipv6 or onion)"));
strUsage +=
HelpMessageOpt("-permitbaremultisig",
strprintf(_("Relay non-P2SH multisig (default: %d)"),
DEFAULT_PERMIT_BAREMULTISIG));
strUsage += HelpMessageOpt(
"-peerbloomfilters",
strprintf(_("Support filtering of blocks and transaction with bloom "
"filters (default: %d)"),
DEFAULT_PEERBLOOMFILTERS));
strUsage += HelpMessageOpt(
"-port=",
strprintf(
_("Listen for connections on (default: %u or testnet: %u)"),
defaultChainParams->GetDefaultPort(),
testnetChainParams->GetDefaultPort()));
strUsage +=
HelpMessageOpt("-proxy=", _("Connect through SOCKS5 proxy"));
strUsage += HelpMessageOpt(
"-proxyrandomize",
strprintf(_("Randomize credentials for every proxy connection. This "
"enables Tor stream isolation (default: %d)"),
DEFAULT_PROXYRANDOMIZE));
strUsage += HelpMessageOpt(
"-seednode=",
_("Connect to a node to retrieve peer addresses, and disconnect"));
strUsage += HelpMessageOpt(
"-timeout=", strprintf(_("Specify connection timeout in "
"milliseconds (minimum: 1, default: %d)"),
DEFAULT_CONNECT_TIMEOUT));
strUsage += HelpMessageOpt("-torcontrol=:",
strprintf(_("Tor control port to use if onion "
"listening enabled (default: %s)"),
DEFAULT_TOR_CONTROL));
strUsage += HelpMessageOpt("-torpassword=",
_("Tor control port password (default: empty)"));
#ifdef USE_UPNP
#if USE_UPNP
strUsage +=
HelpMessageOpt("-upnp", _("Use UPnP to map the listening port "
"(default: 1 when listening and no -proxy)"));
#else
strUsage += HelpMessageOpt(
"-upnp",
strprintf(_("Use UPnP to map the listening port (default: %u)"), 0));
#endif
#endif
strUsage +=
HelpMessageOpt("-whitebind=",
_("Bind to given address and whitelist peers connecting "
"to it. Use [host]:port notation for IPv6"));
strUsage += HelpMessageOpt(
"-whitelist=",
_("Whitelist peers connecting from the given IP address (e.g. 1.2.3.4) "
"or CIDR notated network (e.g. 1.2.3.0/24). Can be specified "
"multiple times.") +
" " + _("Whitelisted peers cannot be DoS banned and their "
"transactions are always relayed, even if they are already "
"in the mempool, useful e.g. for a gateway"));
strUsage += HelpMessageOpt(
"-whitelistrelay",
strprintf(_("Accept relayed transactions received from whitelisted "
"peers even when not relaying transactions (default: %d)"),
DEFAULT_WHITELISTRELAY));
strUsage += HelpMessageOpt(
"-whitelistforcerelay",
strprintf(_("Force relay of transactions from whitelisted peers even "
"if they violate local relay policy (default: %d)"),
DEFAULT_WHITELISTFORCERELAY));
strUsage += HelpMessageOpt(
"-maxuploadtarget=",
strprintf(_("Tries to keep outbound traffic under the given target (in "
"MiB per 24h), 0 = no limit (default: %d)"),
DEFAULT_MAX_UPLOAD_TARGET));
#ifdef ENABLE_WALLET
strUsage += GetWalletHelpString(showDebug);
#endif
#if ENABLE_ZMQ
strUsage += HelpMessageGroup(_("ZeroMQ notification options:"));
strUsage += HelpMessageOpt("-zmqpubhashblock=",
_("Enable publish hash block in "));
strUsage +=
HelpMessageOpt("-zmqpubhashtx=",
_("Enable publish hash transaction in "));
strUsage += HelpMessageOpt("-zmqpubrawblock=",
_("Enable publish raw block in "));
strUsage +=
HelpMessageOpt("-zmqpubrawtx=",
_("Enable publish raw transaction in "));
#endif
strUsage += HelpMessageGroup(_("Debugging/Testing options:"));
strUsage += HelpMessageOpt("-uacomment=",
_("Append comment to the user agent string"));
if (showDebug) {
strUsage += HelpMessageOpt(
"-checkblocks=",
strprintf(
_("How many blocks to check at startup (default: %u, 0 = all)"),
DEFAULT_CHECKBLOCKS));
strUsage +=
HelpMessageOpt("-checklevel=",
strprintf(_("How thorough the block verification of "
"-checkblocks is (0-4, default: %u)"),
DEFAULT_CHECKLEVEL));
strUsage += HelpMessageOpt(
"-checkblockindex",
strprintf("Do a full consistency check for mapBlockIndex, "
"setBlockIndexCandidates, chainActive and "
"mapBlocksUnlinked occasionally. Also sets -checkmempool "
"(default: %u)",
defaultChainParams->DefaultConsistencyChecks()));
strUsage += HelpMessageOpt(
"-checkmempool=",
strprintf("Run checks every transactions (default: %u)",
defaultChainParams->DefaultConsistencyChecks()));
strUsage += HelpMessageOpt(
"-checkpoints", strprintf("Only accept block chain matching "
"built-in checkpoints (default: %d)",
DEFAULT_CHECKPOINTS_ENABLED));
strUsage += HelpMessageOpt(
"-disablesafemode", strprintf("Disable safemode, override a real "
"safe mode event (default: %d)",
DEFAULT_DISABLE_SAFEMODE));
strUsage += HelpMessageOpt(
"-testsafemode",
strprintf("Force safe mode (default: %d)", DEFAULT_TESTSAFEMODE));
strUsage +=
HelpMessageOpt("-dropmessagestest=",
"Randomly drop 1 of every network messages");
strUsage +=
HelpMessageOpt("-fuzzmessagestest=",
"Randomly fuzz 1 of every network messages");
strUsage += HelpMessageOpt(
"-stopafterblockimport",
strprintf(
"Stop running after importing blocks from disk (default: %d)",
DEFAULT_STOPAFTERBLOCKIMPORT));
strUsage += HelpMessageOpt(
"-stopatheight", strprintf("Stop running after reaching the given "
"height in the main chain (default: %u)",
DEFAULT_STOPATHEIGHT));
strUsage += HelpMessageOpt(
"-limitancestorcount=",
strprintf("Do not accept transactions if number of in-mempool "
"ancestors is or more (default: %u)",
DEFAULT_ANCESTOR_LIMIT));
strUsage +=
HelpMessageOpt("-limitancestorsize=",
strprintf("Do not accept transactions whose size "
"with all in-mempool ancestors exceeds "
" kilobytes (default: %u)",
DEFAULT_ANCESTOR_SIZE_LIMIT));
strUsage += HelpMessageOpt(
"-limitdescendantcount=",
strprintf("Do not accept transactions if any ancestor would have "
" or more in-mempool descendants (default: %u)",
DEFAULT_DESCENDANT_LIMIT));
strUsage += HelpMessageOpt(
"-limitdescendantsize=",
strprintf("Do not accept transactions if any ancestor would have "
"more than kilobytes of in-mempool descendants "
"(default: %u).",
DEFAULT_DESCENDANT_SIZE_LIMIT));
}
strUsage += HelpMessageOpt(
"-debug=",
strprintf(_("Output debugging information (default: %u, supplying "
" is optional)"),
0) +
". " + _("If is not supplied or if = 1, "
"output all debugging information.") +
_(" can be:") + " " + ListLogCategories() + ".");
strUsage += HelpMessageOpt(
"-debugexclude=",
strprintf(_("Exclude debugging information for a category. Can be used "
"in conjunction with -debug=1 to output debug logs for all "
"categories except one or more specified categories.")));
if (showDebug) {
strUsage += HelpMessageOpt(
"-nodebug", "Turn off debugging messages, same as -debug=0");
}
strUsage += HelpMessageOpt(
"-help-debug",
_("Show all debugging options (usage: --help -help-debug)"));
strUsage += HelpMessageOpt(
"-logips",
strprintf(_("Include IP addresses in debug output (default: %d)"),
DEFAULT_LOGIPS));
strUsage += HelpMessageOpt(
"-logtimestamps",
strprintf(_("Prepend debug output with timestamp (default: %d)"),
DEFAULT_LOGTIMESTAMPS));
if (showDebug) {
strUsage += HelpMessageOpt(
"-logtimemicros",
strprintf(
"Add microsecond precision to debug timestamps (default: %d)",
DEFAULT_LOGTIMEMICROS));
strUsage += HelpMessageOpt(
"-mocktime=",
"Replace actual time with seconds since epoch (default: 0)");
strUsage += HelpMessageOpt(
"-limitfreerelay=",
strprintf("Continuously rate-limit free transactions to *1000 "
"bytes per minute (default: %u)",
DEFAULT_LIMITFREERELAY));
strUsage +=
HelpMessageOpt("-relaypriority",
strprintf("Require high priority for relaying free "
"or low-fee transactions (default: %d)",
DEFAULT_RELAYPRIORITY));
strUsage += HelpMessageOpt(
"-maxsigcachesize=",
strprintf("Limit size of signature cache to MiB (default: %u)",
DEFAULT_MAX_SIG_CACHE_SIZE));
strUsage += HelpMessageOpt(
"-maxscriptcachesize=",
strprintf("Limit size of script cache to MiB (default: %u)",
DEFAULT_MAX_SCRIPT_CACHE_SIZE));
strUsage += HelpMessageOpt(
"-maxtipage=",
strprintf("Maximum tip age in seconds to consider node in initial "
"block download (default: %u)",
DEFAULT_MAX_TIP_AGE));
}
strUsage += HelpMessageOpt(
"-excessutxocharge=",
strprintf(_("Fees (in %s/kB) to charge per utxo created for"
"relaying, and mining (default: %s)"),
CURRENCY_UNIT, FormatMoney(DEFAULT_UTXO_FEE)));
strUsage += HelpMessageOpt(
"-minrelaytxfee=",
strprintf(
_("Fees (in %s/kB) smaller than this are considered zero fee for "
"relaying, mining and transaction creation (default: %s)"),
CURRENCY_UNIT, FormatMoney(DEFAULT_MIN_RELAY_TX_FEE_PER_KB)));
strUsage += HelpMessageOpt(
"-maxtxfee=",
strprintf(_("Maximum total fees (in %s) to use in a single wallet "
"transaction or raw transaction; setting this too low may "
"abort large transactions (default: %s)"),
CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MAXFEE)));
strUsage += HelpMessageOpt(
"-printtoconsole",
_("Send trace/debug info to console instead of debug.log file"));
if (showDebug) {
strUsage += HelpMessageOpt(
"-printpriority", strprintf("Log transaction priority and fee per "
"kB when mining blocks (default: %d)",
DEFAULT_PRINTPRIORITY));
}
strUsage += HelpMessageOpt("-shrinkdebugfile",
_("Shrink debug.log file on client startup "
"(default: 1 when no -debug)"));
AppendParamsHelpMessages(strUsage, showDebug);
strUsage += HelpMessageGroup(_("Node relay options:"));
if (showDebug) {
strUsage += HelpMessageOpt(
"-acceptnonstdtxn",
strprintf(
"Relay and mine \"non-standard\" transactions (%sdefault: %u)",
"testnet/regtest only; ",
defaultChainParams->RequireStandard()));
strUsage +=
HelpMessageOpt("-excessiveblocksize=",
strprintf(_("Do not accept blocks larger than this "
"limit, in bytes (default: %d)"),
DEFAULT_MAX_BLOCK_SIZE));
strUsage += HelpMessageOpt(
"-dustrelayfee=",
strprintf("Fee rate (in %s/kB) used to defined dust, the value of "
"an output such that it will cost about 1/3 of its value "
"in fees at this fee rate to spend it. (default: %s)",
CURRENCY_UNIT, FormatMoney(DUST_RELAY_TX_FEE)));
}
strUsage +=
HelpMessageOpt("-bytespersigop",
strprintf(_("Equivalent bytes per sigop in transactions "
"for relay and mining (default: %u)"),
DEFAULT_BYTES_PER_SIGOP));
strUsage += HelpMessageOpt(
"-datacarrier",
strprintf(_("Relay and mine data carrier transactions (default: %d)"),
DEFAULT_ACCEPT_DATACARRIER));
strUsage += HelpMessageOpt(
"-datacarriersize",
strprintf(_("Maximum size of data in data carrier transactions we "
"relay and mine (default: %u)"),
MAX_OP_RETURN_RELAY));
strUsage += HelpMessageGroup(_("Block creation options:"));
strUsage += HelpMessageOpt(
"-blockmaxsize=",
strprintf(_("Set maximum block size in bytes (default: %d)"),
DEFAULT_MAX_GENERATED_BLOCK_SIZE));
strUsage += HelpMessageOpt(
"-blockprioritypercentage=",
strprintf(_("Set maximum percentage of a block reserved to "
"high-priority/low-fee transactions (default: %d)"),
DEFAULT_BLOCK_PRIORITY_PERCENTAGE));
strUsage += HelpMessageOpt(
"-blockmintxfee=",
strprintf(_("Set lowest fee rate (in %s/kB) for transactions to be "
"included in block creation. (default: %s)"),
CURRENCY_UNIT, FormatMoney(DEFAULT_BLOCK_MIN_TX_FEE_PER_KB)));
if (showDebug) {
strUsage +=
HelpMessageOpt("-blockversion=",
"Override block version to test forking scenarios");
}
strUsage += HelpMessageGroup(_("RPC server options:"));
strUsage += HelpMessageOpt("-server",
_("Accept command line and JSON-RPC commands"));
strUsage += HelpMessageOpt(
"-rest", strprintf(_("Accept public REST requests (default: %d)"),
DEFAULT_REST_ENABLE));
strUsage += HelpMessageOpt(
"-rpcbind=",
_("Bind to given address to listen for JSON-RPC connections. Use "
"[host]:port notation for IPv6. This option can be specified "
"multiple times (default: bind to all interfaces)"));
strUsage +=
HelpMessageOpt("-rpccookiefile=",
_("Location of the auth cookie (default: data dir)"));
strUsage += HelpMessageOpt("-rpcuser=",
_("Username for JSON-RPC connections"));
strUsage += HelpMessageOpt("-rpcpassword=",
_("Password for JSON-RPC connections"));
strUsage += HelpMessageOpt(
"-rpcauth=",
_("Username and hashed password for JSON-RPC connections. The field "
" comes in the format: :$. A canonical "
"python script is included in share/rpcuser. The client then "
"connects normally using the "
"rpcuser=/rpcpassword= pair of arguments. This "
"option can be specified multiple times"));
strUsage += HelpMessageOpt(
"-rpcport=",
strprintf(_("Listen for JSON-RPC connections on (default: %u or "
"testnet: %u)"),
defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort()));
strUsage += HelpMessageOpt(
"-rpcallowip=",
_("Allow JSON-RPC connections from specified source. Valid for "
"are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. "
"1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). This "
"option can be specified multiple times"));
strUsage += HelpMessageOpt(
"-rpcthreads=",
strprintf(
_("Set the number of threads to service RPC calls (default: %d)"),
DEFAULT_HTTP_THREADS));
strUsage += HelpMessageOpt(
"-rpccorsdomain=value",
"Domain from which to accept cross origin requests (browser enforced)");
if (showDebug) {
strUsage += HelpMessageOpt(
"-rpcworkqueue=", strprintf("Set the depth of the work queue to "
"service RPC calls (default: %d)",
DEFAULT_HTTP_WORKQUEUE));
strUsage += HelpMessageOpt(
"-rpcservertimeout=",
strprintf("Timeout during HTTP requests (default: %d)",
DEFAULT_HTTP_SERVER_TIMEOUT));
}
return strUsage;
}
std::string LicenseInfo() {
const std::string URL_SOURCE_CODE =
"";
const std::string URL_WEBSITE = "";
return CopyrightHolders(
strprintf(_("Copyright (C) %i-%i"), 2009, COPYRIGHT_YEAR) +
" ") +
"\n" + "\n" +
strprintf(_("Please contribute if you find %s useful. "
"Visit %s for further information about the software."),
PACKAGE_NAME, URL_WEBSITE) +
"\n" + strprintf(_("The source code is available from %s."),
URL_SOURCE_CODE) +
"\n" + "\n" + _("This is experimental software.") + "\n" +
strprintf(_("Distributed under the MIT software license, see the "
"accompanying file %s or %s"),
"COPYING", "") +
"\n" + "\n" +
strprintf(_("This product includes software developed by the "
"OpenSSL Project for use in the OpenSSL Toolkit %s and "
"cryptographic software written by Eric Young and UPnP "
"software written by Thomas Bernard."),
"") +
"\n";
}
static void BlockNotifyCallback(bool initialSync,
const CBlockIndex *pBlockIndex) {
if (initialSync || !pBlockIndex) {
return;
}
std::string strCmd = gArgs.GetArg("-blocknotify", "");
if (!strCmd.empty()) {
boost::replace_all(strCmd, "%s", pBlockIndex->GetBlockHash().GetHex());
std::thread t(runCommand, strCmd);
// thread runs free
t.detach();
}
}
static bool fHaveGenesis = false;
static CWaitableCriticalSection cs_GenesisWait;
static CConditionVariable condvar_GenesisWait;
static void BlockNotifyGenesisWait(bool, const CBlockIndex *pBlockIndex) {
if (pBlockIndex != nullptr) {
{
WaitableLock lock_GenesisWait(cs_GenesisWait);
fHaveGenesis = true;
}
condvar_GenesisWait.notify_all();
}
}
struct CImportingNow {
CImportingNow() {
assert(fImporting == false);
fImporting = true;
}
~CImportingNow() {
assert(fImporting == true);
fImporting = false;
}
};
// If we're using -prune with -reindex, then delete block files that will be
// ignored by the reindex. Since reindexing works by starting at block file 0
// and looping until a blockfile is missing, do the same here to delete any
// later block files after a gap. Also delete all rev files since they'll be
// rewritten by the reindex anyway. This ensures that vinfoBlockFile is in sync
// with what's actually on disk by the time we start downloading, so that
// pruning works correctly.
void CleanupBlockRevFiles() {
std::map mapBlockFiles;
// Glob all blk?????.dat and rev?????.dat files from the blocks directory.
// Remove the rev files immediately and insert the blk file paths into an
// ordered map keyed by block file index.
LogPrintf("Removing unusable blk?????.dat and rev?????.dat files for "
"-reindex with -prune\n");
fs::path blocksdir = GetDataDir() / "blocks";
for (fs::directory_iterator it(blocksdir); it != fs::directory_iterator();
it++) {
if (is_regular_file(*it) &&
it->path().filename().string().length() == 12 &&
it->path().filename().string().substr(8, 4) == ".dat") {
if (it->path().filename().string().substr(0, 3) == "blk") {
mapBlockFiles[it->path().filename().string().substr(3, 5)] =
it->path();
} else if (it->path().filename().string().substr(0, 3) == "rev") {
remove(it->path());
}
}
}
// Remove all block files that aren't part of a contiguous set starting at
// zero by walking the ordered map (keys are block file indices) by keeping
// a separate counter. Once we hit a gap (or if 0 doesn't exist) start
// removing block files.
int nContigCounter = 0;
for (const std::pair &item : mapBlockFiles) {
if (atoi(item.first) == nContigCounter) {
nContigCounter++;
continue;
}
remove(item.second);
}
}
void ThreadImport(const Config &config, std::vector vImportFiles) {
RenameThread("bitcoin-loadblk");
{
CImportingNow imp;
// -reindex
if (fReindex) {
int nFile = 0;
while (true) {
CDiskBlockPos pos(nFile, 0);
if (!fs::exists(GetBlockPosFilename(pos, "blk"))) {
// No block files left to reindex
break;
}
FILE *file = OpenBlockFile(pos, true);
if (!file) {
// This error is logged in OpenBlockFile
break;
}
LogPrintf("Reindexing block file blk%05u.dat...\n",
(unsigned int)nFile);
LoadExternalBlockFile(config, file, &pos);
nFile++;
}
pblocktree->WriteReindexing(false);
fReindex = false;
LogPrintf("Reindexing finished\n");
// To avoid ending up in a situation without genesis block, re-try
// initializing (no-op if reindexing worked):
InitBlockIndex(config);
}
// hardcoded $DATADIR/bootstrap.dat
fs::path pathBootstrap = GetDataDir() / "bootstrap.dat";
if (fs::exists(pathBootstrap)) {
FILE *file = fsbridge::fopen(pathBootstrap, "rb");
if (file) {
fs::path pathBootstrapOld = GetDataDir() / "bootstrap.dat.old";
LogPrintf("Importing bootstrap.dat...\n");
LoadExternalBlockFile(config, file);
RenameOver(pathBootstrap, pathBootstrapOld);
} else {
LogPrintf("Warning: Could not open bootstrap file %s\n",
pathBootstrap.string());
}
}
// -loadblock=
for (const fs::path &path : vImportFiles) {
FILE *file = fsbridge::fopen(path, "rb");
if (file) {
LogPrintf("Importing blocks file %s...\n", path.string());
LoadExternalBlockFile(config, file);
} else {
LogPrintf("Warning: Could not open blocks file %s\n",
path.string());
}
}
// scan for better chains in the block chain database, that are not yet
// connected in the active best chain
CValidationState state;
if (!ActivateBestChain(config, state)) {
LogPrintf("Failed to connect best block");
StartShutdown();
}
if (gArgs.GetBoolArg("-stopafterblockimport",
DEFAULT_STOPAFTERBLOCKIMPORT)) {
LogPrintf("Stopping after block import\n");
StartShutdown();
}
} // End scope of CImportingNow
if (gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
LoadMempool(config);
fDumpMempoolLater = !fRequestShutdown;
}
}
/** Sanity checks
* Ensure that Bitcoin is running in a usable environment with all
* necessary library support.
*/
bool InitSanityCheck(void) {
if (!ECC_InitSanityCheck()) {
InitError(
"Elliptic curve cryptography sanity check failure. Aborting.");
return false;
}
if (!glibc_sanity_test() || !glibcxx_sanity_test()) {
return false;
}
if (!Random_SanityCheck()) {
InitError("OS cryptographic RNG sanity check failure. Aborting.");
return false;
}
return true;
}
static bool AppInitServers(Config &config,
HTTPRPCRequestProcessor &httpRPCRequestProcessor,
boost::thread_group &threadGroup) {
RPCServerSignals::OnStarted(&OnRPCStarted);
RPCServerSignals::OnStopped(&OnRPCStopped);
- RPCServerSignals::OnPreCommand(&OnRPCPreCommand);
if (!InitHTTPServer(config)) {
return false;
}
if (!StartRPC()) {
return false;
}
if (!StartHTTPRPC(config, httpRPCRequestProcessor)) {
return false;
}
if (gArgs.GetBoolArg("-rest", DEFAULT_REST_ENABLE) && !StartREST()) {
return false;
}
if (!StartHTTPServer()) {
return false;
}
return true;
}
// Parameter interaction based on rules
void InitParameterInteraction() {
// when specifying an explicit binding address, you want to listen on it
// even when -connect or -proxy is specified.
if (gArgs.IsArgSet("-bind")) {
if (gArgs.SoftSetBoolArg("-listen", true)) {
LogPrintf(
"%s: parameter interaction: -bind set -> setting -listen=1\n",
__func__);
}
}
if (gArgs.IsArgSet("-whitebind")) {
if (gArgs.SoftSetBoolArg("-listen", true)) {
LogPrintf("%s: parameter interaction: -whitebind set -> setting "
"-listen=1\n",
__func__);
}
}
if (gArgs.IsArgSet("-connect")) {
// when only connecting to trusted nodes, do not seed via DNS, or listen
// by default.
if (gArgs.SoftSetBoolArg("-dnsseed", false)) {
LogPrintf("%s: parameter interaction: -connect set -> setting "
"-dnsseed=0\n",
__func__);
}
if (gArgs.SoftSetBoolArg("-listen", false)) {
LogPrintf("%s: parameter interaction: -connect set -> setting "
"-listen=0\n",
__func__);
}
}
if (gArgs.IsArgSet("-proxy")) {
// to protect privacy, do not listen by default if a default proxy
// server is specified.
if (gArgs.SoftSetBoolArg("-listen", false)) {
LogPrintf(
"%s: parameter interaction: -proxy set -> setting -listen=0\n",
__func__);
}
// to protect privacy, do not use UPNP when a proxy is set. The user may
// still specify -listen=1 to listen locally, so don't rely on this
// happening through -listen below.
if (gArgs.SoftSetBoolArg("-upnp", false)) {
LogPrintf(
"%s: parameter interaction: -proxy set -> setting -upnp=0\n",
__func__);
}
// to protect privacy, do not discover addresses by default
if (gArgs.SoftSetBoolArg("-discover", false)) {
LogPrintf("%s: parameter interaction: -proxy set -> setting "
"-discover=0\n",
__func__);
}
}
if (!gArgs.GetBoolArg("-listen", DEFAULT_LISTEN)) {
// do not map ports or try to retrieve public IP when not listening
// (pointless)
if (gArgs.SoftSetBoolArg("-upnp", false)) {
LogPrintf(
"%s: parameter interaction: -listen=0 -> setting -upnp=0\n",
__func__);
}
if (gArgs.SoftSetBoolArg("-discover", false)) {
LogPrintf(
"%s: parameter interaction: -listen=0 -> setting -discover=0\n",
__func__);
}
if (gArgs.SoftSetBoolArg("-listenonion", false)) {
LogPrintf("%s: parameter interaction: -listen=0 -> setting "
"-listenonion=0\n",
__func__);
}
}
if (gArgs.IsArgSet("-externalip")) {
// if an explicit public IP is specified, do not try to find others
if (gArgs.SoftSetBoolArg("-discover", false)) {
LogPrintf("%s: parameter interaction: -externalip set -> setting "
"-discover=0\n",
__func__);
}
}
// disable whitelistrelay in blocksonly mode
if (gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY)) {
if (gArgs.SoftSetBoolArg("-whitelistrelay", false)) {
LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting "
"-whitelistrelay=0\n",
__func__);
}
}
// Forcing relay from whitelisted hosts implies we will accept relays from
// them in the first place.
if (gArgs.GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) {
if (gArgs.SoftSetBoolArg("-whitelistrelay", true)) {
LogPrintf("%s: parameter interaction: -whitelistforcerelay=1 -> "
"setting -whitelistrelay=1\n",
__func__);
}
}
}
static std::string ResolveErrMsg(const char *const optname,
const std::string &strBind) {
return strprintf(_("Cannot resolve -%s address: '%s'"), optname, strBind);
}
void InitLogging() {
BCLog::Logger &logger = GetLogger();
logger.fPrintToConsole = gArgs.GetBoolArg("-printtoconsole", false);
logger.fLogTimestamps =
gArgs.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS);
logger.fLogTimeMicros =
gArgs.GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS);
fLogIPs = gArgs.GetBoolArg("-logips", DEFAULT_LOGIPS);
LogPrintf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
LogPrintf("%s version %s\n", CLIENT_NAME, FormatFullVersion());
}
namespace { // Variables internal to initialization process only
int nMaxConnections;
int nUserMaxConnections;
int nFD;
ServiceFlags nLocalServices = NODE_NETWORK;
} // namespace
[[noreturn]] static void new_handler_terminate() {
// Rather than throwing std::bad-alloc if allocation fails, terminate
// immediately to (try to) avoid chain corruption. Since LogPrintf may
// itself allocate memory, set the handler directly to terminate first.
std::set_new_handler(std::terminate);
LogPrintf("Error: Out of memory. Terminating.\n");
// The log was successful, terminate now.
std::terminate();
};
bool AppInitBasicSetup() {
// Step 1: setup
#ifdef _MSC_VER
// Turn off Microsoft heap dump noise
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
_CrtSetReportFile(_CRT_WARN, CreateFileA("NUL", GENERIC_WRITE, 0, nullptr,
OPEN_EXISTING, 0, 0));
#endif
#if _MSC_VER >= 1400
// Disable confusing "helpful" text message on abort, Ctrl-C
_set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
#endif
#ifdef WIN32
// Enable Data Execution Prevention (DEP)
// Minimum supported OS versions: WinXP SP3, WinVista >= SP1, Win Server 2008
// A failure is non-critical and needs no further attention!
#ifndef PROCESS_DEP_ENABLE
// We define this here, because GCCs winbase.h limits this to _WIN32_WINNT >=
// 0x0601 (Windows 7), which is not correct. Can be removed, when GCCs winbase.h
// is fixed!
#define PROCESS_DEP_ENABLE 0x00000001
#endif
typedef BOOL(WINAPI * PSETPROCDEPPOL)(DWORD);
PSETPROCDEPPOL setProcDEPPol = (PSETPROCDEPPOL)GetProcAddress(
GetModuleHandleA("Kernel32.dll"), "SetProcessDEPPolicy");
if (setProcDEPPol != nullptr) {
setProcDEPPol(PROCESS_DEP_ENABLE);
}
#endif
if (!SetupNetworking()) {
return InitError("Initializing networking failed");
}
#ifndef WIN32
if (!gArgs.GetBoolArg("-sysperms", false)) {
umask(077);
}
// Clean shutdown on SIGTERM
struct sigaction sa;
sa.sa_handler = HandleSIGTERM;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(SIGTERM, &sa, nullptr);
sigaction(SIGINT, &sa, nullptr);
// Reopen debug.log on SIGHUP
struct sigaction sa_hup;
sa_hup.sa_handler = HandleSIGHUP;
sigemptyset(&sa_hup.sa_mask);
sa_hup.sa_flags = 0;
sigaction(SIGHUP, &sa_hup, nullptr);
// Ignore SIGPIPE, otherwise it will bring the daemon down if the client
// closes unexpectedly
signal(SIGPIPE, SIG_IGN);
#endif
std::set_new_handler(new_handler_terminate);
return true;
}
bool AppInitParameterInteraction(Config &config, RPCServer &rpcServer) {
const CChainParams &chainparams = config.GetChainParams();
// Step 2: parameter interactions
// also see: InitParameterInteraction()
// if using block pruning, then disallow txindex
if (gArgs.GetArg("-prune", 0)) {
if (gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
return InitError(_("Prune mode is incompatible with -txindex."));
}
}
// if space reserved for high priority transactions is misconfigured
// stop program execution and warn the user with a proper error message
const int64_t blkprio = gArgs.GetArg("-blockprioritypercentage",
DEFAULT_BLOCK_PRIORITY_PERCENTAGE);
if (!config.SetBlockPriorityPercentage(blkprio)) {
return InitError(_("Block priority percentage has to belong to the "
"[0..100] interval."));
}
// -bind and -whitebind can't be set when not listening
size_t nUserBind =
gArgs.GetArgs("-bind").size() + gArgs.GetArgs("-whitebind").size();
if (nUserBind != 0 && !gArgs.GetBoolArg("-listen", DEFAULT_LISTEN)) {
return InitError(
"Cannot set -bind or -whitebind together with -listen=0");
}
// Make sure enough file descriptors are available
int nBind = std::max(nUserBind, size_t(1));
nUserMaxConnections =
gArgs.GetArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS);
nMaxConnections = std::max(nUserMaxConnections, 0);
// Trim requested connection counts, to fit into system limitations
nMaxConnections =
std::max(std::min(nMaxConnections,
(int)(FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS -
MAX_ADDNODE_CONNECTIONS)),
0);
nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS +
MAX_ADDNODE_CONNECTIONS);
if (nFD < MIN_CORE_FILEDESCRIPTORS) {
return InitError(_("Not enough file descriptors available."));
}
nMaxConnections =
std::min(nFD - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS,
nMaxConnections);
if (nMaxConnections < nUserMaxConnections) {
InitWarning(strprintf(_("Reducing -maxconnections from %d to %d, "
"because of system limitations."),
nUserMaxConnections, nMaxConnections));
}
// Step 3: parameter-to-internal-flags
if (gArgs.IsArgSet("-debug")) {
// Special-case: if -debug=0/-nodebug is set, turn off debugging
// messages
const std::vector &categories = gArgs.GetArgs("-debug");
if (find(categories.begin(), categories.end(), std::string("0")) ==
categories.end()) {
for (const auto &cat : categories) {
BCLog::LogFlags flag;
if (!GetLogCategory(flag, cat)) {
InitWarning(
strprintf(_("Unsupported logging category %s=%s."),
"-debug", cat));
}
GetLogger().EnableCategory(flag);
}
}
}
// Now remove the logging categories which were explicitly excluded
for (const std::string &cat : gArgs.GetArgs("-debugexclude")) {
BCLog::LogFlags flag;
if (!GetLogCategory(flag, cat)) {
InitWarning(strprintf(_("Unsupported logging category %s=%s."),
"-debugexclude", cat));
}
GetLogger().DisableCategory(flag);
}
// Check for -debugnet
if (gArgs.GetBoolArg("-debugnet", false)) {
InitWarning(
_("Unsupported argument -debugnet ignored, use -debug=net."));
}
// Check for -socks - as this is a privacy risk to continue, exit here
if (gArgs.IsArgSet("-socks")) {
return InitError(
_("Unsupported argument -socks found. Setting SOCKS version isn't "
"possible anymore, only SOCKS5 proxies are supported."));
}
// Check for -tor - as this is a privacy risk to continue, exit here
if (gArgs.GetBoolArg("-tor", false)) {
return InitError(_("Unsupported argument -tor found, use -onion."));
}
if (gArgs.GetBoolArg("-benchmark", false)) {
InitWarning(
_("Unsupported argument -benchmark ignored, use -debug=bench."));
}
if (gArgs.GetBoolArg("-whitelistalwaysrelay", false)) {
InitWarning(_("Unsupported argument -whitelistalwaysrelay ignored, use "
"-whitelistrelay and/or -whitelistforcerelay."));
}
if (gArgs.IsArgSet("-blockminsize")) {
InitWarning("Unsupported argument -blockminsize ignored.");
}
// Checkmempool and checkblockindex default to true in regtest mode
int ratio = std::min(
std::max(
gArgs.GetArg("-checkmempool",
chainparams.DefaultConsistencyChecks() ? 1 : 0),
0),
1000000);
if (ratio != 0) {
mempool.setSanityCheck(1.0 / ratio);
}
fCheckBlockIndex = gArgs.GetBoolArg("-checkblockindex",
chainparams.DefaultConsistencyChecks());
fCheckpointsEnabled =
gArgs.GetBoolArg("-checkpoints", DEFAULT_CHECKPOINTS_ENABLED);
hashAssumeValid = uint256S(
gArgs.GetArg("-assumevalid",
chainparams.GetConsensus().defaultAssumeValid.GetHex()));
if (!hashAssumeValid.IsNull()) {
LogPrintf("Assuming ancestors of block %s have valid signatures.\n",
hashAssumeValid.GetHex());
} else {
LogPrintf("Validating signatures for all blocks.\n");
}
if (gArgs.IsArgSet("-minimumchainwork")) {
const std::string minChainWorkStr =
gArgs.GetArg("-minimumchainwork", "");
if (!IsHexNumber(minChainWorkStr)) {
return InitError(strprintf(
"Invalid non-hex (%s) minimum chain work value specified",
minChainWorkStr));
}
nMinimumChainWork = UintToArith256(uint256S(minChainWorkStr));
} else {
nMinimumChainWork =
UintToArith256(chainparams.GetConsensus().nMinimumChainWork);
}
LogPrintf("Setting nMinimumChainWork=%s\n", nMinimumChainWork.GetHex());
if (nMinimumChainWork <
UintToArith256(chainparams.GetConsensus().nMinimumChainWork)) {
LogPrintf("Warning: nMinimumChainWork set below default value of %s\n",
chainparams.GetConsensus().nMinimumChainWork.GetHex());
}
// mempool limits
int64_t nMempoolSizeMax =
gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
int64_t nMempoolSizeMin =
gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) *
1000 * 40;
if (nMempoolSizeMax < 0 || nMempoolSizeMax < nMempoolSizeMin) {
return InitError(strprintf(_("-maxmempool must be at least %d MB"),
std::ceil(nMempoolSizeMin / 1000000.0)));
}
// -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency
nScriptCheckThreads = gArgs.GetArg("-par", DEFAULT_SCRIPTCHECK_THREADS);
if (nScriptCheckThreads <= 0) {
nScriptCheckThreads += GetNumCores();
}
if (nScriptCheckThreads <= 1) {
nScriptCheckThreads = 0;
} else if (nScriptCheckThreads > MAX_SCRIPTCHECK_THREADS) {
nScriptCheckThreads = MAX_SCRIPTCHECK_THREADS;
}
// Configure excessive block size.
const uint64_t nProposedExcessiveBlockSize =
gArgs.GetArg("-excessiveblocksize", DEFAULT_MAX_BLOCK_SIZE);
if (!config.SetMaxBlockSize(nProposedExcessiveBlockSize)) {
return InitError(
_("Excessive block size must be > 1,000,000 bytes (1MB)"));
}
// Check blockmaxsize does not exceed maximum accepted block size.
const uint64_t nProposedMaxGeneratedBlockSize =
gArgs.GetArg("-blockmaxsize", DEFAULT_MAX_GENERATED_BLOCK_SIZE);
if (nProposedMaxGeneratedBlockSize > config.GetMaxBlockSize()) {
auto msg = _("Max generated block size (blockmaxsize) cannot exceed "
"the excessive block size (excessiveblocksize)");
return InitError(msg);
}
// block pruning; get the amount of disk space (in MiB) to allot for block &
// undo files
int64_t nPruneArg = gArgs.GetArg("-prune", 0);
if (nPruneArg < 0) {
return InitError(
_("Prune cannot be configured with a negative value."));
}
nPruneTarget = (uint64_t)nPruneArg * 1024 * 1024;
if (nPruneArg == 1) {
// manual pruning: -prune=1
LogPrintf("Block pruning enabled. Use RPC call "
"pruneblockchain(height) to manually prune block and undo "
"files.\n");
nPruneTarget = std::numeric_limits::max();
fPruneMode = true;
} else if (nPruneTarget) {
if (nPruneTarget < MIN_DISK_SPACE_FOR_BLOCK_FILES) {
return InitError(
strprintf(_("Prune configured below the minimum of %d MiB. "
"Please use a higher number."),
MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024));
}
LogPrintf("Prune configured to target %uMiB on disk for block and undo "
"files.\n",
nPruneTarget / 1024 / 1024);
fPruneMode = true;
}
RegisterAllRPCCommands(config, rpcServer, tableRPC);
#ifdef ENABLE_WALLET
RegisterWalletRPCCommands(tableRPC);
RegisterDumpRPCCommands(tableRPC);
#endif
nConnectTimeout = gArgs.GetArg("-timeout", DEFAULT_CONNECT_TIMEOUT);
if (nConnectTimeout <= 0) {
nConnectTimeout = DEFAULT_CONNECT_TIMEOUT;
}
// Obtain the amount to charge excess UTXO
if (gArgs.IsArgSet("-excessutxocharge")) {
Amount n = Amount::zero();
auto parsed = ParseMoney(gArgs.GetArg("-excessutxocharge", ""), n);
if (!parsed || Amount::zero() > n) {
return InitError(AmountErrMsg(
"excessutxocharge", gArgs.GetArg("-excessutxocharge", "")));
}
config.SetExcessUTXOCharge(n);
} else {
config.SetExcessUTXOCharge(DEFAULT_UTXO_FEE);
}
// Fee-per-kilobyte amount considered the same as "free". If you are mining,
// be careful setting this: if you set it to zero then a transaction spammer
// can cheaply fill blocks using 1-satoshi-fee transactions. It should be
// set above the real cost to you of processing a transaction.
if (gArgs.IsArgSet("-minrelaytxfee")) {
Amount n = Amount::zero();
auto parsed = ParseMoney(gArgs.GetArg("-minrelaytxfee", ""), n);
if (!parsed || Amount::zero() == n) {
return InitError(AmountErrMsg("minrelaytxfee",
gArgs.GetArg("-minrelaytxfee", "")));
}
// High fee check is done afterward in WalletParameterInteraction()
config.SetMinFeePerKB(CFeeRate(n));
} else {
config.SetMinFeePerKB(CFeeRate(DEFAULT_MIN_RELAY_TX_FEE_PER_KB));
}
// Sanity check argument for min fee for including tx in block
// TODO: Harmonize which arguments need sanity checking and where that
// happens.
if (gArgs.IsArgSet("-blockmintxfee")) {
Amount n = Amount::zero();
if (!ParseMoney(gArgs.GetArg("-blockmintxfee", ""), n)) {
return InitError(AmountErrMsg("blockmintxfee",
gArgs.GetArg("-blockmintxfee", "")));
}
}
// Feerate used to define dust. Shouldn't be changed lightly as old
// implementations may inadvertently create non-standard transactions.
if (gArgs.IsArgSet("-dustrelayfee")) {
Amount n = Amount::zero();
auto parsed = ParseMoney(gArgs.GetArg("-dustrelayfee", ""), n);
if (!parsed || Amount::zero() == n) {
return InitError(AmountErrMsg("dustrelayfee",
gArgs.GetArg("-dustrelayfee", "")));
}
dustRelayFee = CFeeRate(n);
}
fRequireStandard =
!gArgs.GetBoolArg("-acceptnonstdtxn", !chainparams.RequireStandard());
if (chainparams.RequireStandard() && !fRequireStandard) {
return InitError(
strprintf("acceptnonstdtxn is not currently supported for %s chain",
chainparams.NetworkIDString()));
}
nBytesPerSigOp = gArgs.GetArg("-bytespersigop", nBytesPerSigOp);
#ifdef ENABLE_WALLET
if (!WalletParameterInteraction()) {
return false;
}
#endif
fIsBareMultisigStd =
gArgs.GetBoolArg("-permitbaremultisig", DEFAULT_PERMIT_BAREMULTISIG);
fAcceptDatacarrier =
gArgs.GetBoolArg("-datacarrier", DEFAULT_ACCEPT_DATACARRIER);
// Option to startup with mocktime set (used for regression testing):
SetMockTime(gArgs.GetArg("-mocktime", 0)); // SetMockTime(0) is a no-op
if (gArgs.GetBoolArg("-peerbloomfilters", DEFAULT_PEERBLOOMFILTERS)) {
nLocalServices = ServiceFlags(nLocalServices | NODE_BLOOM);
}
// Signal Bitcoin Cash support.
// TODO: remove some time after the hardfork when no longer needed
// to differentiate the network nodes.
nLocalServices = ServiceFlags(nLocalServices | NODE_BITCOIN_CASH);
nMaxTipAge = gArgs.GetArg("-maxtipage", DEFAULT_MAX_TIP_AGE);
return true;
}
static bool LockDataDirectory(bool probeOnly) {
std::string strDataDir = GetDataDir().string();
// Make sure only a single Bitcoin process is using the data directory.
fs::path pathLockFile = GetDataDir() / ".lock";
// empty lock file; created if it doesn't exist.
FILE *file = fsbridge::fopen(pathLockFile, "a");
if (file) {
fclose(file);
}
try {
static boost::interprocess::file_lock lock(
pathLockFile.string().c_str());
if (!lock.try_lock()) {
return InitError(
strprintf(_("Cannot obtain a lock on data directory %s. %s is "
"probably already running."),
strDataDir, _(PACKAGE_NAME)));
}
if (probeOnly) {
lock.unlock();
}
} catch (const boost::interprocess::interprocess_exception &e) {
return InitError(strprintf(_("Cannot obtain a lock on data directory "
"%s. %s is probably already running.") +
" %s.",
strDataDir, _(PACKAGE_NAME), e.what()));
}
return true;
}
bool AppInitSanityChecks() {
// Step 4: sanity checks
// Initialize elliptic curve code
std::string sha256_algo = SHA256AutoDetect();
LogPrintf("Using the '%s' SHA256 implementation\n", sha256_algo);
RandomInit();
ECC_Start();
globalVerifyHandle.reset(new ECCVerifyHandle());
// Sanity check
if (!InitSanityCheck()) {
return InitError(strprintf(
_("Initialization sanity check failed. %s is shutting down."),
_(PACKAGE_NAME)));
}
// Probe the data directory lock to give an early error message, if possible
return LockDataDirectory(true);
}
bool AppInitMain(Config &config,
HTTPRPCRequestProcessor &httpRPCRequestProcessor,
boost::thread_group &threadGroup, CScheduler &scheduler) {
const CChainParams &chainparams = config.GetChainParams();
// Step 4a: application initialization
// After daemonization get the data directory lock again and hold on to it
// until exit. This creates a slight window for a race condition to happen,
// however this condition is harmless: it will at most make us exit without
// printing a message to console.
if (!LockDataDirectory(false)) {
// Detailed error printed inside LockDataDirectory
return false;
}
#ifndef WIN32
CreatePidFile(GetPidFile(), getpid());
#endif
BCLog::Logger &logger = GetLogger();
bool default_shrinkdebugfile = logger.DefaultShrinkDebugFile();
if (gArgs.GetBoolArg("-shrinkdebugfile", default_shrinkdebugfile)) {
// Do this first since it both loads a bunch of debug.log into memory,
// and because this needs to happen before any other debug.log printing.
logger.ShrinkDebugFile();
}
if (logger.fPrintToDebugLog) {
logger.OpenDebugLog();
}
if (!logger.fLogTimestamps) {
LogPrintf("Startup time: %s\n",
DateTimeStrFormat("%Y-%m-%d %H:%M:%S", GetTime()));
}
LogPrintf("Default data directory %s\n", GetDefaultDataDir().string());
LogPrintf("Using data directory %s\n", GetDataDir().string());
LogPrintf(
"Using config file %s\n",
GetConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)).string());
LogPrintf("Using at most %i automatic connections (%i file descriptors "
"available)\n",
nMaxConnections, nFD);
InitSignatureCache();
InitScriptExecutionCache();
LogPrintf("Using %u threads for script verification\n",
nScriptCheckThreads);
if (nScriptCheckThreads) {
for (int i = 0; i < nScriptCheckThreads - 1; i++) {
threadGroup.create_thread(&ThreadScriptCheck);
}
}
// Start the lightweight task scheduler thread
CScheduler::Function serviceLoop =
boost::bind(&CScheduler::serviceQueue, &scheduler);
threadGroup.create_thread(boost::bind(&TraceThread,
"scheduler", serviceLoop));
/**
* Start the RPC server. It will be started in "warmup" mode and not
* process calls yet (but it will verify that the server is there and will
* be ready later). Warmup mode will be completed when initialisation is
* finished.
*/
if (gArgs.GetBoolArg("-server", false)) {
uiInterface.InitMessage.connect(SetRPCWarmupStatus);
if (!AppInitServers(config, httpRPCRequestProcessor, threadGroup)) {
return InitError(
_("Unable to start HTTP server. See debug log for details."));
}
}
// Step 5: verify wallet database integrity
#ifdef ENABLE_WALLET
if (!WalletVerify(chainparams)) {
return false;
}
#endif
// Step 6: network initialization
// Note that we absolutely cannot open any actual connections
// until the very end ("start node") as the UTXO/block state
// is not yet setup and may end up being set up twice if we
// need to reindex later.
assert(!g_connman);
g_connman = std::unique_ptr(
new CConnman(config, GetRand(std::numeric_limits::max()),
GetRand(std::numeric_limits::max())));
CConnman &connman = *g_connman;
peerLogic.reset(new PeerLogicValidation(&connman, scheduler));
RegisterValidationInterface(peerLogic.get());
if (gArgs.IsArgSet("-onlynet")) {
std::set nets;
for (const std::string &snet : gArgs.GetArgs("-onlynet")) {
enum Network net = ParseNetwork(snet);
if (net == NET_UNROUTABLE) {
return InitError(strprintf(
_("Unknown network specified in -onlynet: '%s'"), snet));
}
nets.insert(net);
}
for (int n = 0; n < NET_MAX; n++) {
enum Network net = (enum Network)n;
if (!nets.count(net)) SetLimited(net);
}
}
// Check for host lookup allowed before parsing any network related
// parameters
fNameLookup = gArgs.GetBoolArg("-dns", DEFAULT_NAME_LOOKUP);
bool proxyRandomize =
gArgs.GetBoolArg("-proxyrandomize", DEFAULT_PROXYRANDOMIZE);
// -proxy sets a proxy for all outgoing network traffic
// -noproxy (or -proxy=0) as well as the empty string can be used to not set
// a proxy, this is the default
std::string proxyArg = gArgs.GetArg("-proxy", "");
SetLimited(NET_TOR);
if (proxyArg != "" && proxyArg != "0") {
CService proxyAddr;
if (!Lookup(proxyArg.c_str(), proxyAddr, 9050, fNameLookup)) {
return InitError(strprintf(
_("Invalid -proxy address or hostname: '%s'"), proxyArg));
}
proxyType addrProxy = proxyType(proxyAddr, proxyRandomize);
if (!addrProxy.IsValid()) {
return InitError(strprintf(
_("Invalid -proxy address or hostname: '%s'"), proxyArg));
}
SetProxy(NET_IPV4, addrProxy);
SetProxy(NET_IPV6, addrProxy);
SetProxy(NET_TOR, addrProxy);
SetNameProxy(addrProxy);
// by default, -proxy sets onion as reachable, unless -noonion later
SetLimited(NET_TOR, false);
}
// -onion can be used to set only a proxy for .onion, or override normal
// proxy for .onion addresses.
// -noonion (or -onion=0) disables connecting to .onion entirely. An empty
// string is used to not override the onion proxy (in which case it defaults
// to -proxy set above, or none)
std::string onionArg = gArgs.GetArg("-onion", "");
if (onionArg != "") {
if (onionArg == "0") { // Handle -noonion/-onion=0
SetLimited(NET_TOR); // set onions as unreachable
} else {
CService onionProxy;
if (!Lookup(onionArg.c_str(), onionProxy, 9050, fNameLookup)) {
return InitError(strprintf(
_("Invalid -onion address or hostname: '%s'"), onionArg));
}
proxyType addrOnion = proxyType(onionProxy, proxyRandomize);
if (!addrOnion.IsValid()) {
return InitError(strprintf(
_("Invalid -onion address or hostname: '%s'"), onionArg));
}
SetProxy(NET_TOR, addrOnion);
SetLimited(NET_TOR, false);
}
}
// see Step 2: parameter interactions for more information about these
fListen = gArgs.GetBoolArg("-listen", DEFAULT_LISTEN);
fDiscover = gArgs.GetBoolArg("-discover", true);
fRelayTxes = !gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY);
for (const std::string &strAddr : gArgs.GetArgs("-externalip")) {
CService addrLocal;
if (Lookup(strAddr.c_str(), addrLocal, GetListenPort(), fNameLookup) &&
addrLocal.IsValid()) {
AddLocal(addrLocal, LOCAL_MANUAL);
} else {
return InitError(ResolveErrMsg("externalip", strAddr));
}
}
#if ENABLE_ZMQ
pzmqNotificationInterface = CZMQNotificationInterface::Create();
if (pzmqNotificationInterface) {
RegisterValidationInterface(pzmqNotificationInterface);
}
#endif
// unlimited unless -maxuploadtarget is set
uint64_t nMaxOutboundLimit = 0;
uint64_t nMaxOutboundTimeframe = MAX_UPLOAD_TIMEFRAME;
if (gArgs.IsArgSet("-maxuploadtarget")) {
nMaxOutboundLimit =
gArgs.GetArg("-maxuploadtarget", DEFAULT_MAX_UPLOAD_TARGET) * 1024 *
1024;
}
// Step 7: load block chain
fReindex = gArgs.GetBoolArg("-reindex", false);
bool fReindexChainState = gArgs.GetBoolArg("-reindex-chainstate", false);
// cache size calculations
int64_t nTotalCache = (gArgs.GetArg("-dbcache", nDefaultDbCache) << 20);
// total cache cannot be less than nMinDbCache
nTotalCache = std::max(nTotalCache, nMinDbCache << 20);
// total cache cannot be greater than nMaxDbcache
nTotalCache = std::min(nTotalCache, nMaxDbCache << 20);
int64_t nBlockTreeDBCache = nTotalCache / 8;
nBlockTreeDBCache = std::min(nBlockTreeDBCache,
(gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX)
? nMaxBlockDBAndTxIndexCache
: nMaxBlockDBCache)
<< 20);
nTotalCache -= nBlockTreeDBCache;
// use 25%-50% of the remainder for disk cache
int64_t nCoinDBCache =
std::min(nTotalCache / 2, (nTotalCache / 4) + (1 << 23));
// cap total coins db cache
nCoinDBCache = std::min(nCoinDBCache, nMaxCoinsDBCache << 20);
nTotalCache -= nCoinDBCache;
// the rest goes to in-memory cache
nCoinCacheUsage = nTotalCache;
int64_t nMempoolSizeMax =
gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
LogPrintf("Cache configuration:\n");
LogPrintf("* Using %.1fMiB for block index database\n",
nBlockTreeDBCache * (1.0 / 1024 / 1024));
LogPrintf("* Using %.1fMiB for chain state database\n",
nCoinDBCache * (1.0 / 1024 / 1024));
LogPrintf("* Using %.1fMiB for in-memory UTXO set (plus up to %.1fMiB of "
"unused mempool space)\n",
nCoinCacheUsage * (1.0 / 1024 / 1024),
nMempoolSizeMax * (1.0 / 1024 / 1024));
int64_t nStart = 0;
bool fLoaded = false;
while (!fLoaded && !fRequestShutdown) {
bool fReset = fReindex;
std::string strLoadError;
uiInterface.InitMessage(_("Loading block index..."));
nStart = GetTimeMillis();
do {
try {
UnloadBlockIndex();
delete pcoinsTip;
delete pcoinsdbview;
delete pcoinscatcher;
delete pblocktree;
pblocktree =
new CBlockTreeDB(nBlockTreeDBCache, false, fReindex);
pcoinsdbview = new CCoinsViewDB(nCoinDBCache, false,
fReindex || fReindexChainState);
pcoinscatcher = new CCoinsViewErrorCatcher(pcoinsdbview);
if (fReindex) {
pblocktree->WriteReindexing(true);
// If we're reindexing in prune mode, wipe away unusable
// block files and all undo data files
if (fPruneMode) {
CleanupBlockRevFiles();
}
} else if (!pcoinsdbview->Upgrade()) {
strLoadError = _("Error upgrading chainstate database");
break;
}
if (fRequestShutdown) {
break;
}
if (!LoadBlockIndex(config)) {
strLoadError = _("Error loading block database");
break;
}
// If the loaded chain has a wrong genesis, bail out immediately
// (we're likely using a testnet datadir, or the other way
// around).
if (!mapBlockIndex.empty() &&
mapBlockIndex.count(
chainparams.GetConsensus().hashGenesisBlock) == 0) {
return InitError(_("Incorrect or no genesis block found. "
"Wrong datadir for network?"));
}
// Initialize the block index (no-op if non-empty database was
// already loaded)
if (!InitBlockIndex(config)) {
strLoadError = _("Error initializing block database");
break;
}
// Check for changed -txindex state
if (fTxIndex != gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
strLoadError = _("You need to rebuild the database using "
"-reindex-chainstate to change -txindex");
break;
}
// Check for changed -prune state. What we are concerned about
// is a user who has pruned blocks in the past, but is now
// trying to run unpruned.
if (fHavePruned && !fPruneMode) {
strLoadError =
_("You need to rebuild the database using -reindex to "
"go back to unpruned mode. This will redownload the "
"entire blockchain");
break;
}
if (!ReplayBlocks(config, pcoinsdbview)) {
strLoadError =
_("Unable to replay blocks. You will need to rebuild "
"the database using -reindex-chainstate.");
break;
}
pcoinsTip = new CCoinsViewCache(pcoinscatcher);
LoadChainTip(chainparams);
if (!fReindex && chainActive.Tip() != nullptr) {
uiInterface.InitMessage(_("Rewinding blocks..."));
if (!RewindBlockIndex(config)) {
strLoadError = _("Unable to rewind the database to a "
"pre-fork state. You will need to "
"redownload the blockchain");
break;
}
}
uiInterface.InitMessage(_("Verifying blocks..."));
if (fHavePruned &&
gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS) >
MIN_BLOCKS_TO_KEEP) {
LogPrintf("Prune: pruned datadir may not have more than %d "
"blocks; only checking available blocks",
MIN_BLOCKS_TO_KEEP);
}
{
LOCK(cs_main);
CBlockIndex *tip = chainActive.Tip();
RPCNotifyBlockChange(true, tip);
if (tip &&
tip->nTime >
GetAdjustedTime() + MAX_FUTURE_BLOCK_TIME) {
strLoadError =
_("The block database contains a block which "
"appears to be from the future. "
"This may be due to your computer's date and "
"time being set incorrectly. "
"Only rebuild the block database if you are sure "
"that your computer's date and time are correct");
break;
}
}
if (!CVerifyDB().VerifyDB(
config, pcoinsdbview,
gArgs.GetArg("-checklevel", DEFAULT_CHECKLEVEL),
gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS))) {
strLoadError = _("Corrupted block database detected");
break;
}
} catch (const std::exception &e) {
LogPrintf("%s\n", e.what());
strLoadError = _("Error opening block database");
break;
}
fLoaded = true;
} while (false);
if (!fLoaded && !fRequestShutdown) {
// first suggest a reindex
if (!fReset) {
bool fRet = uiInterface.ThreadSafeQuestion(
strLoadError + ".\n\n" +
_("Do you want to rebuild the block database now?"),
strLoadError + ".\nPlease restart with -reindex or "
"-reindex-chainstate to recover.",
"",
CClientUIInterface::MSG_ERROR |
CClientUIInterface::BTN_ABORT);
if (fRet) {
fReindex = true;
fRequestShutdown = false;
} else {
LogPrintf("Aborted block database rebuild. Exiting.\n");
return false;
}
} else {
return InitError(strLoadError);
}
}
}
// As LoadBlockIndex can take several minutes, it's possible the user
// requested to kill the GUI during the last operation. If so, exit.
// As the program has not fully started yet, Shutdown() is possibly
// overkill.
if (fRequestShutdown) {
LogPrintf("Shutdown requested. Exiting.\n");
return false;
}
LogPrintf(" block index %15dms\n", GetTimeMillis() - nStart);
fs::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME;
CAutoFile est_filein(fsbridge::fopen(est_path, "rb"), SER_DISK,
CLIENT_VERSION);
// Allowed to fail as this file IS missing on first startup.
if (!est_filein.IsNull()) {
mempool.ReadFeeEstimates(est_filein);
}
fFeeEstimatesInitialized = true;
// Encoded addresses using cashaddr instead of base58
// Activates by default on Jan, 14
config.SetCashAddrEncoding(
gArgs.GetBoolArg("-usecashaddr", GetAdjustedTime() > 1515900000));
// Step 8: load wallet
#ifdef ENABLE_WALLET
if (!InitLoadWallet(chainparams)) {
return false;
}
#else
LogPrintf("No wallet support compiled in!\n");
#endif
// Step 9: data directory maintenance
// if pruning, unset the service bit and perform the initial blockstore
// prune after any wallet rescanning has taken place.
if (fPruneMode) {
LogPrintf("Unsetting NODE_NETWORK on prune mode\n");
nLocalServices = ServiceFlags(nLocalServices & ~NODE_NETWORK);
if (!fReindex) {
uiInterface.InitMessage(_("Pruning blockstore..."));
PruneAndFlush();
}
}
// Step 10: import blocks
if (!CheckDiskSpace()) {
return false;
}
// Either install a handler to notify us when genesis activates, or set
// fHaveGenesis directly.
// No locking, as this happens before any background thread is started.
if (chainActive.Tip() == nullptr) {
uiInterface.NotifyBlockTip.connect(BlockNotifyGenesisWait);
} else {
fHaveGenesis = true;
}
if (gArgs.IsArgSet("-blocknotify")) {
uiInterface.NotifyBlockTip.connect(BlockNotifyCallback);
}
std::vector vImportFiles;
for (const std::string &strFile : gArgs.GetArgs("-loadblock")) {
vImportFiles.push_back(strFile);
}
threadGroup.create_thread(
boost::bind(&ThreadImport, std::ref(config), vImportFiles));
// Wait for genesis block to be processed
{
WaitableLock lock(cs_GenesisWait);
while (!fHaveGenesis) {
condvar_GenesisWait.wait(lock);
}
uiInterface.NotifyBlockTip.disconnect(BlockNotifyGenesisWait);
}
// Step 11: start node
//// debug print
LogPrintf("mapBlockIndex.size() = %u\n", mapBlockIndex.size());
LogPrintf("nBestHeight = %d\n", chainActive.Height());
if (gArgs.GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION)) {
StartTorControl();
}
Discover();
// Map ports with UPnP
if (gArgs.GetBoolArg("-upnp", DEFAULT_UPNP)) {
StartMapPort();
}
CConnman::Options connOptions;
connOptions.nLocalServices = nLocalServices;
connOptions.nMaxConnections = nMaxConnections;
connOptions.nMaxOutbound =
std::min(MAX_OUTBOUND_CONNECTIONS, connOptions.nMaxConnections);
connOptions.nMaxAddnode = MAX_ADDNODE_CONNECTIONS;
connOptions.nMaxFeeler = 1;
connOptions.nBestHeight = chainActive.Height();
connOptions.uiInterface = &uiInterface;
connOptions.m_msgproc = peerLogic.get();
connOptions.nSendBufferMaxSize =
1000 * gArgs.GetArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER);
connOptions.nReceiveFloodSize =
1000 * gArgs.GetArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER);
connOptions.m_added_nodes = gArgs.GetArgs("-addnode");
connOptions.nMaxOutboundTimeframe = nMaxOutboundTimeframe;
connOptions.nMaxOutboundLimit = nMaxOutboundLimit;
for (const std::string &strBind : gArgs.GetArgs("-bind")) {
CService addrBind;
if (!Lookup(strBind.c_str(), addrBind, GetListenPort(), false)) {
return InitError(ResolveErrMsg("bind", strBind));
}
connOptions.vBinds.push_back(addrBind);
}
for (const std::string &strBind : gArgs.GetArgs("-whitebind")) {
CService addrBind;
if (!Lookup(strBind.c_str(), addrBind, 0, false)) {
return InitError(ResolveErrMsg("whitebind", strBind));
}
if (addrBind.GetPort() == 0) {
return InitError(strprintf(
_("Need to specify a port with -whitebind: '%s'"), strBind));
}
connOptions.vWhiteBinds.push_back(addrBind);
}
for (const auto &net : gArgs.GetArgs("-whitelist")) {
CSubNet subnet;
LookupSubNet(net.c_str(), subnet);
if (!subnet.IsValid()) {
return InitError(strprintf(
_("Invalid netmask specified in -whitelist: '%s'"), net));
}
connOptions.vWhitelistedRange.push_back(subnet);
}
connOptions.vSeedNodes = gArgs.GetArgs("-seednode");
// Initiate outbound connections unless connect=0
connOptions.m_use_addrman_outgoing = !gArgs.IsArgSet("-connect");
if (!connOptions.m_use_addrman_outgoing) {
const auto connect = gArgs.GetArgs("-connect");
if (connect.size() != 1 || connect[0] != "0") {
connOptions.m_specified_outgoing = connect;
}
}
if (!connman.Start(scheduler, connOptions)) {
return false;
}
// Step 12: finished
SetRPCWarmupFinished();
uiInterface.InitMessage(_("Done loading"));
#ifdef ENABLE_WALLET
for (CWalletRef pwallet : vpwallets) {
pwallet->postInitProcess(scheduler);
}
#endif
return !fRequestShutdown;
}
diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp
index 0edb08ad8..34659a5e0 100644
--- a/src/qt/test/rpcnestedtests.cpp
+++ b/src/qt/test/rpcnestedtests.cpp
@@ -1,221 +1,221 @@
// Copyright (c) 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 "rpcnestedtests.h"
#include "chainparams.h"
#include "config.h"
#include "consensus/validation.h"
#include "fs.h"
#include "rpc/register.h"
#include "rpc/server.h"
#include "rpcconsole.h"
#include "test/testutil.h"
#include "univalue.h"
#include "util.h"
#include "validation.h"
#include
#include
static UniValue rpcNestedTest_rpc(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp) {
return "help message";
}
return request.params.write(0, 0);
}
static const ContextFreeRPCCommand vRPCCommands[] = {
- {"test", "rpcNestedTest", rpcNestedTest_rpc, true, {}},
+ {"test", "rpcNestedTest", &rpcNestedTest_rpc, {}},
};
void RPCNestedTests::rpcNestedTests() {
UniValue jsonRPCError;
// Do some test setup could be moved to a more generic place when we add
// more tests on QT level
const Config &config = GetConfig();
RegisterAllContextFreeRPCCommands(tableRPC);
tableRPC.appendCommand("rpcNestedTest", &vRPCCommands[0]);
ClearDatadirCache();
std::string path =
QDir::tempPath().toStdString() + "/" +
strprintf("test_bitcoin_qt_%lu_%i", (unsigned long)GetTime(),
(int)(GetRand(100000)));
QDir dir(QString::fromStdString(path));
dir.mkpath(".");
gArgs.ForceSetArg("-datadir", path);
// mempool.setSanityCheck(1.0);
pblocktree = new CBlockTreeDB(1 << 20, true);
pcoinsdbview = new CCoinsViewDB(1 << 23, true);
pcoinsTip = new CCoinsViewCache(pcoinsdbview);
InitBlockIndex(config);
{
CValidationState state;
bool ok = ActivateBestChain(config, state);
QVERIFY(ok);
}
SetRPCWarmupFinished();
std::string result;
std::string result2;
std::string filtered;
// Simple result filtering with path.
RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo()[chain]",
&filtered);
QVERIFY(result == "main");
QVERIFY(filtered == "getblockchaininfo()[chain]");
// Simple 2 level nesting.
RPCConsole::RPCExecuteCommandLine(result, "getblock(getbestblockhash())");
RPCConsole::RPCExecuteCommandLine(
result, "getblock(getblock(getbestblockhash())[hash], true)");
// 4 level nesting with whitespace, filtering path and boolean parameter.
RPCConsole::RPCExecuteCommandLine(
result, "getblock( getblock( getblock(getbestblockhash())[hash] "
")[hash], true)");
RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo");
QVERIFY(result.substr(0, 1) == "{");
RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo()");
QVERIFY(result.substr(0, 1) == "{");
// Whitespace at the end will be tolerated.
RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo ");
QVERIFY(result.substr(0, 1) == "{");
// Quote path identifier are allowed, but look after a child contaning the
// quotes in the key.
(RPCConsole::RPCExecuteCommandLine(result,
"getblockchaininfo()[\"chain\"]"));
QVERIFY(result == "null");
// parameter not in brackets are allowed.
(RPCConsole::RPCExecuteCommandLine(result, "createrawtransaction [] {} 0"));
// Parameter in brackets are allowed.
(RPCConsole::RPCExecuteCommandLine(result2,
"createrawtransaction([],{},0)"));
QVERIFY(result == result2);
// Whitespace between parametres is allowed.
(RPCConsole::RPCExecuteCommandLine(
result2, "createrawtransaction( [], {} , 0 )"));
QVERIFY(result == result2);
RPCConsole::RPCExecuteCommandLine(
result, "getblock(getbestblockhash())[tx][0]", &filtered);
QVERIFY(result ==
"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b");
QVERIFY(filtered == "getblock(getbestblockhash())[tx][0]");
RPCConsole::RPCParseCommandLine(result, "importprivkey", false, &filtered);
QVERIFY(filtered == "importprivkey(…)");
RPCConsole::RPCParseCommandLine(result, "signmessagewithprivkey abc", false,
&filtered);
QVERIFY(filtered == "signmessagewithprivkey(…)");
RPCConsole::RPCParseCommandLine(result, "signmessagewithprivkey abc,def",
false, &filtered);
QVERIFY(filtered == "signmessagewithprivkey(…)");
RPCConsole::RPCParseCommandLine(result, "signrawtransaction(abc)", false,
&filtered);
QVERIFY(filtered == "signrawtransaction(…)");
RPCConsole::RPCParseCommandLine(result, "walletpassphrase(help())", false,
&filtered);
QVERIFY(filtered == "walletpassphrase(…)");
RPCConsole::RPCParseCommandLine(
result, "walletpassphrasechange(help(walletpassphrasechange(abc)))",
false, &filtered);
QVERIFY(filtered == "walletpassphrasechange(…)");
RPCConsole::RPCParseCommandLine(result, "help(encryptwallet(abc, def))",
false, &filtered);
QVERIFY(filtered == "help(encryptwallet(…))");
RPCConsole::RPCParseCommandLine(result, "help(importprivkey())", false,
&filtered);
QVERIFY(filtered == "help(importprivkey(…))");
RPCConsole::RPCParseCommandLine(result, "help(importprivkey(help()))",
false, &filtered);
QVERIFY(filtered == "help(importprivkey(…))");
RPCConsole::RPCParseCommandLine(
result, "help(importprivkey(abc), walletpassphrase(def))", false,
&filtered);
QVERIFY(filtered == "help(importprivkey(…), walletpassphrase(…))");
RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest");
QVERIFY(result == "[]");
RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest ''");
QVERIFY(result == "[\"\"]");
RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest \"\"");
QVERIFY(result == "[\"\"]");
RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest '' abc");
QVERIFY(result == "[\"\",\"abc\"]");
RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest abc '' abc");
QVERIFY(result == "[\"abc\",\"\",\"abc\"]");
RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest abc abc");
QVERIFY(result == "[\"abc\",\"abc\"]");
RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest abc\t\tabc");
QVERIFY(result == "[\"abc\",\"abc\"]");
RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest(abc )");
QVERIFY(result == "[\"abc\"]");
RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest( abc )");
QVERIFY(result == "[\"abc\"]");
RPCConsole::RPCExecuteCommandLine(result,
"rpcNestedTest( abc , cba )");
QVERIFY(result == "[\"abc\",\"cba\"]");
#if QT_VERSION >= 0x050300
// do the QVERIFY_EXCEPTION_THROWN checks only with Qt5.3 and higher
// (QVERIFY_EXCEPTION_THROWN was introduced in Qt5.3)
// invalid syntax
QVERIFY_EXCEPTION_THROWN(
RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo() .\n"),
std::runtime_error);
// invalid syntax
QVERIFY_EXCEPTION_THROWN(
RPCConsole::RPCExecuteCommandLine(
result, "getblockchaininfo() getblockchaininfo()"),
std::runtime_error);
// tolerate non closing brackets if we have no arguments.
(RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo("));
// tolerate non command brackts
(RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo()()()"));
// invalid argument
QVERIFY_EXCEPTION_THROWN(
RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo(True)"),
UniValue);
// method not found
QVERIFY_EXCEPTION_THROWN(
RPCConsole::RPCExecuteCommandLine(result, "a(getblockchaininfo(True))"),
UniValue);
// don't tollerate empty arguments when using ,
QVERIFY_EXCEPTION_THROWN(
RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest abc,,abc"),
std::runtime_error);
// don't tollerate empty arguments when using ,
QVERIFY_EXCEPTION_THROWN(
RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest(abc,,abc)"),
std::runtime_error);
// don't tollerate empty arguments when using ,
QVERIFY_EXCEPTION_THROWN(
RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest(abc,,)"),
std::runtime_error);
#endif
UnloadBlockIndex();
delete pcoinsTip;
pcoinsTip = nullptr;
delete pcoinsdbview;
pcoinsdbview = nullptr;
delete pblocktree;
pblocktree = nullptr;
fs::remove_all(fs::path(path));
}
diff --git a/src/rpc/abc.cpp b/src/rpc/abc.cpp
index 1d9fca5d4..99c01ef62 100644
--- a/src/rpc/abc.cpp
+++ b/src/rpc/abc.cpp
@@ -1,92 +1,92 @@
// Copyright (c) 2017 The Bitcoin developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "config.h"
#include "consensus/consensus.h"
#include "rpc/server.h"
#include "utilstrencodings.h"
#include "validation.h"
#include
#include
static UniValue getexcessiveblock(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 0) {
throw std::runtime_error(
"getexcessiveblock\n"
"\nReturn the excessive block size."
"\nResult\n"
" excessiveBlockSize (integer) block size in bytes\n"
"\nExamples:\n" +
HelpExampleCli("getexcessiveblock", "") +
HelpExampleRpc("getexcessiveblock", ""));
}
UniValue ret(UniValue::VOBJ);
ret.push_back(Pair("excessiveBlockSize", config.GetMaxBlockSize()));
return ret;
}
static UniValue setexcessiveblock(Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"setexcessiveblock blockSize\n"
"\nSet the excessive block size. Excessive blocks will not be used "
"in the active chain or relayed. This discourages the propagation "
"of blocks that you consider excessively large."
"\nResult\n"
" blockSize (integer) excessive block size in bytes\n"
"\nExamples:\n" +
HelpExampleCli("setexcessiveblock", "") +
HelpExampleRpc("setexcessiveblock", ""));
}
uint64_t ebs = 0;
if (request.params[0].isNum()) {
ebs = request.params[0].get_int64();
} else {
std::string temp = request.params[0].get_str();
if (temp[0] == '-') {
boost::throw_exception(boost::bad_lexical_cast());
}
ebs = boost::lexical_cast(temp);
}
// Do not allow maxBlockSize to be set below historic 1MB limit
if (ebs <= LEGACY_MAX_BLOCK_SIZE) {
throw JSONRPCError(
RPC_INVALID_PARAMETER,
std::string(
"Invalid parameter, excessiveblock must be larger than ") +
std::to_string(LEGACY_MAX_BLOCK_SIZE));
}
// Set the new max block size.
if (!config.SetMaxBlockSize(ebs)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Unexpected error");
}
// settingsToUserAgentString();
std::ostringstream ret;
ret << "Excessive Block set to " << ebs << " bytes.";
return UniValue(ret.str());
}
// clang-format off
static const ContextFreeRPCCommand commands[] = {
- // category name actor (function) okSafeMode
+ // category name actor (function) argNames
// ------------------- ------------------------ ---------------------- ----------
- { "network", "getexcessiveblock", getexcessiveblock, true, {}},
- { "network", "setexcessiveblock", setexcessiveblock, true, {"maxBlockSize"}},
+ { "network", "getexcessiveblock", getexcessiveblock, {}},
+ { "network", "setexcessiveblock", setexcessiveblock, {"maxBlockSize"}},
};
// clang-format on
void RegisterABCRPCCommands(CRPCTable &t) {
for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) {
t.appendCommand(commands[vcidx].name, &commands[vcidx]);
}
}
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index e077dc460..b4cb8073b 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -1,1702 +1,1702 @@
// 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 "rpc/blockchain.h"
#include "amount.h"
#include "chain.h"
#include "chainparams.h"
#include "checkpoints.h"
#include "coins.h"
#include "config.h"
#include "consensus/validation.h"
#include "hash.h"
#include "policy/policy.h"
#include "primitives/transaction.h"
#include "rpc/server.h"
#include "rpc/tojson.h"
#include "streams.h"
#include "sync.h"
#include "txdb.h"
#include "txmempool.h"
#include "util.h"
#include "utilstrencodings.h"
#include "validation.h"
#include // boost::thread::interrupt
#include
#include
#include
struct CUpdatedBlock {
uint256 hash;
int height;
};
static std::mutex cs_blockchange;
static std::condition_variable cond_blockchange;
static CUpdatedBlock latestblock;
static double GetDifficultyFromBits(uint32_t nBits) {
int nShift = (nBits >> 24) & 0xff;
double dDiff = 0x0000ffff / double(nBits & 0x00ffffff);
while (nShift < 29) {
dDiff *= 256.0;
nShift++;
}
while (nShift > 29) {
dDiff /= 256.0;
nShift--;
}
return dDiff;
}
double GetDifficulty(const CBlockIndex *blockindex) {
// Floating point number that is a multiple of the minimum difficulty,
// minimum difficulty = 1.0.
if (blockindex == nullptr) {
return 1.0;
}
return GetDifficultyFromBits(blockindex->nBits);
}
UniValue blockheaderToJSON(const CBlockIndex *blockindex) {
UniValue result(UniValue::VOBJ);
result.push_back(Pair("hash", blockindex->GetBlockHash().GetHex()));
int confirmations = -1;
// Only report confirmations if the block is on the main chain
if (chainActive.Contains(blockindex)) {
confirmations = chainActive.Height() - blockindex->nHeight + 1;
}
result.push_back(Pair("confirmations", confirmations));
result.push_back(Pair("height", blockindex->nHeight));
result.push_back(Pair("version", blockindex->nVersion));
result.push_back(
Pair("versionHex", strprintf("%08x", blockindex->nVersion)));
result.push_back(Pair("merkleroot", blockindex->hashMerkleRoot.GetHex()));
result.push_back(Pair("time", int64_t(blockindex->nTime)));
result.push_back(
Pair("mediantime", int64_t(blockindex->GetMedianTimePast())));
result.push_back(Pair("nonce", uint64_t(blockindex->nNonce)));
result.push_back(Pair("bits", strprintf("%08x", blockindex->nBits)));
result.push_back(Pair("difficulty", GetDifficulty(blockindex)));
result.push_back(Pair("chainwork", blockindex->nChainWork.GetHex()));
if (blockindex->pprev) {
result.push_back(Pair("previousblockhash",
blockindex->pprev->GetBlockHash().GetHex()));
}
CBlockIndex *pnext = chainActive.Next(blockindex);
if (pnext) {
result.push_back(Pair("nextblockhash", pnext->GetBlockHash().GetHex()));
}
return result;
}
UniValue blockToJSON(const Config &config, const CBlock &block,
const CBlockIndex *blockindex, bool txDetails) {
UniValue result(UniValue::VOBJ);
result.push_back(Pair("hash", blockindex->GetBlockHash().GetHex()));
int confirmations = -1;
// Only report confirmations if the block is on the main chain
if (chainActive.Contains(blockindex)) {
confirmations = chainActive.Height() - blockindex->nHeight + 1;
}
result.push_back(Pair("confirmations", confirmations));
result.push_back(Pair(
"size", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION)));
result.push_back(Pair("height", blockindex->nHeight));
result.push_back(Pair("version", block.nVersion));
result.push_back(Pair("versionHex", strprintf("%08x", block.nVersion)));
result.push_back(Pair("merkleroot", block.hashMerkleRoot.GetHex()));
UniValue txs(UniValue::VARR);
for (const auto &tx : block.vtx) {
if (txDetails) {
UniValue objTx(UniValue::VOBJ);
TxToJSON(config, *tx, uint256(), objTx);
txs.push_back(objTx);
} else {
txs.push_back(tx->GetId().GetHex());
}
}
result.push_back(Pair("tx", txs));
result.push_back(Pair("time", block.GetBlockTime()));
result.push_back(
Pair("mediantime", int64_t(blockindex->GetMedianTimePast())));
result.push_back(Pair("nonce", uint64_t(block.nNonce)));
result.push_back(Pair("bits", strprintf("%08x", block.nBits)));
result.push_back(Pair("difficulty", GetDifficulty(blockindex)));
result.push_back(Pair("chainwork", blockindex->nChainWork.GetHex()));
if (blockindex->pprev) {
result.push_back(Pair("previousblockhash",
blockindex->pprev->GetBlockHash().GetHex()));
}
CBlockIndex *pnext = chainActive.Next(blockindex);
if (pnext) {
result.push_back(Pair("nextblockhash", pnext->GetBlockHash().GetHex()));
}
return result;
}
UniValue getblockcount(const Config &config, const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 0) {
throw std::runtime_error(
"getblockcount\n"
"\nReturns the number of blocks in the longest blockchain.\n"
"\nResult:\n"
"n (numeric) The current block count\n"
"\nExamples:\n" +
HelpExampleCli("getblockcount", "") +
HelpExampleRpc("getblockcount", ""));
}
LOCK(cs_main);
return chainActive.Height();
}
UniValue getbestblockhash(const Config &config, const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 0) {
throw std::runtime_error(
"getbestblockhash\n"
"\nReturns the hash of the best (tip) block in the "
"longest blockchain.\n"
"\nResult:\n"
"\"hex\" (string) the block hash hex encoded\n"
"\nExamples:\n" +
HelpExampleCli("getbestblockhash", "") +
HelpExampleRpc("getbestblockhash", ""));
}
LOCK(cs_main);
return chainActive.Tip()->GetBlockHash().GetHex();
}
void RPCNotifyBlockChange(bool ibd, const CBlockIndex *pindex) {
if (pindex) {
std::lock_guard lock(cs_blockchange);
latestblock.hash = pindex->GetBlockHash();
latestblock.height = pindex->nHeight;
}
cond_blockchange.notify_all();
}
UniValue waitfornewblock(const Config &config, const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() > 1) {
throw std::runtime_error(
"waitfornewblock (timeout)\n"
"\nWaits for a specific new block and returns "
"useful info about it.\n"
"\nReturns the current block on timeout or exit.\n"
"\nArguments:\n"
"1. timeout (int, optional, default=0) Time in "
"milliseconds to wait for a response. 0 indicates "
"no timeout.\n"
"\nResult:\n"
"{ (json object)\n"
" \"hash\" : { (string) The blockhash\n"
" \"height\" : { (int) Block height\n"
"}\n"
"\nExamples:\n" +
HelpExampleCli("waitfornewblock", "1000") +
HelpExampleRpc("waitfornewblock", "1000"));
}
int timeout = 0;
if (request.params.size() > 0) {
timeout = request.params[0].get_int();
}
CUpdatedBlock block;
{
std::unique_lock lock(cs_blockchange);
block = latestblock;
if (timeout) {
cond_blockchange.wait_for(
lock, std::chrono::milliseconds(timeout), [&block] {
return latestblock.height != block.height ||
latestblock.hash != block.hash || !IsRPCRunning();
});
} else {
cond_blockchange.wait(lock, [&block] {
return latestblock.height != block.height ||
latestblock.hash != block.hash || !IsRPCRunning();
});
}
block = latestblock;
}
UniValue ret(UniValue::VOBJ);
ret.push_back(Pair("hash", block.hash.GetHex()));
ret.push_back(Pair("height", block.height));
return ret;
}
UniValue waitforblock(const Config &config, const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() < 1 ||
request.params.size() > 2) {
throw std::runtime_error(
"waitforblock (timeout)\n"
"\nWaits for a specific new block and returns useful info about "
"it.\n"
"\nReturns the current block on timeout or exit.\n"
"\nArguments:\n"
"1. \"blockhash\" (required, string) Block hash to wait for.\n"
"2. timeout (int, optional, default=0) Time in milliseconds "
"to wait for a response. 0 indicates no timeout.\n"
"\nResult:\n"
"{ (json object)\n"
" \"hash\" : { (string) The blockhash\n"
" \"height\" : { (int) Block height\n"
"}\n"
"\nExamples:\n" +
HelpExampleCli("waitforblock", "\"0000000000079f8ef3d2c688c244eb7a4"
"570b24c9ed7b4a8c619eb02596f8862\", "
"1000") +
HelpExampleRpc("waitforblock", "\"0000000000079f8ef3d2c688c244eb7a4"
"570b24c9ed7b4a8c619eb02596f8862\", "
"1000"));
}
int timeout = 0;
uint256 hash = uint256S(request.params[0].get_str());
if (request.params.size() > 1) {
timeout = request.params[1].get_int();
}
CUpdatedBlock block;
{
std::unique_lock lock(cs_blockchange);
if (timeout) {
cond_blockchange.wait_for(
lock, std::chrono::milliseconds(timeout), [&hash] {
return latestblock.hash == hash || !IsRPCRunning();
});
} else {
cond_blockchange.wait(lock, [&hash] {
return latestblock.hash == hash || !IsRPCRunning();
});
}
block = latestblock;
}
UniValue ret(UniValue::VOBJ);
ret.push_back(Pair("hash", block.hash.GetHex()));
ret.push_back(Pair("height", block.height));
return ret;
}
UniValue waitforblockheight(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() < 1 ||
request.params.size() > 2) {
throw std::runtime_error(
"waitforblockheight (timeout)\n"
"\nWaits for (at least) block height and returns the height and "
"hash\n"
"of the current tip.\n"
"\nReturns the current block on timeout or exit.\n"
"\nArguments:\n"
"1. height (required, int) Block height to wait for (int)\n"
"2. timeout (int, optional, default=0) Time in milliseconds to "
"wait for a response. 0 indicates no timeout.\n"
"\nResult:\n"
"{ (json object)\n"
" \"hash\" : { (string) The blockhash\n"
" \"height\" : { (int) Block height\n"
"}\n"
"\nExamples:\n" +
HelpExampleCli("waitforblockheight", "\"100\", 1000") +
HelpExampleRpc("waitforblockheight", "\"100\", 1000"));
}
int timeout = 0;
int height = request.params[0].get_int();
if (request.params.size() > 1) {
timeout = request.params[1].get_int();
}
CUpdatedBlock block;
{
std::unique_lock lock(cs_blockchange);
if (timeout) {
cond_blockchange.wait_for(
lock, std::chrono::milliseconds(timeout), [&height] {
return latestblock.height >= height || !IsRPCRunning();
});
} else {
cond_blockchange.wait(lock, [&height] {
return latestblock.height >= height || !IsRPCRunning();
});
}
block = latestblock;
}
UniValue ret(UniValue::VOBJ);
ret.push_back(Pair("hash", block.hash.GetHex()));
ret.push_back(Pair("height", block.height));
return ret;
}
UniValue getdifficulty(const Config &config, const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 0) {
throw std::runtime_error("getdifficulty\n"
"\nReturns the proof-of-work difficulty as a "
"multiple of the minimum difficulty.\n"
"\nResult:\n"
"n.nnn (numeric) the proof-of-work "
"difficulty as a multiple of the minimum "
"difficulty.\n"
"\nExamples:\n" +
HelpExampleCli("getdifficulty", "") +
HelpExampleRpc("getdifficulty", ""));
}
LOCK(cs_main);
return GetDifficulty(chainActive.Tip());
}
std::string EntryDescriptionString() {
return " \"size\" : n, (numeric) transaction size.\n"
" \"fee\" : n, (numeric) transaction fee in " +
CURRENCY_UNIT +
"\n"
" \"modifiedfee\" : n, (numeric) transaction fee with fee "
"deltas used for mining priority\n"
" \"time\" : n, (numeric) local time transaction "
"entered pool in seconds since 1 Jan 1970 GMT\n"
" \"height\" : n, (numeric) block height when "
"transaction entered pool\n"
" \"startingpriority\" : n, (numeric) DEPRECATED. Priority when "
"transaction entered pool\n"
" \"currentpriority\" : n, (numeric) DEPRECATED. Transaction "
"priority now\n"
" \"descendantcount\" : n, (numeric) number of in-mempool "
"descendant transactions (including this one)\n"
" \"descendantsize\" : n, (numeric) virtual transaction size "
"of in-mempool descendants (including this one)\n"
" \"descendantfees\" : n, (numeric) modified fees (see above) "
"of in-mempool descendants (including this one)\n"
" \"ancestorcount\" : n, (numeric) number of in-mempool "
"ancestor transactions (including this one)\n"
" \"ancestorsize\" : n, (numeric) virtual transaction size "
"of in-mempool ancestors (including this one)\n"
" \"ancestorfees\" : n, (numeric) modified fees (see above) "
"of in-mempool ancestors (including this one)\n"
" \"depends\" : [ (array) unconfirmed transactions "
"used as inputs for this transaction\n"
" \"transactionid\", (string) parent transaction id\n"
" ... ]\n";
}
void entryToJSON(UniValue &info, const CTxMemPoolEntry &e) {
AssertLockHeld(mempool.cs);
info.push_back(Pair("size", (int)e.GetTxSize()));
info.push_back(Pair("fee", ValueFromAmount(e.GetFee())));
info.push_back(Pair("modifiedfee", ValueFromAmount(e.GetModifiedFee())));
info.push_back(Pair("time", e.GetTime()));
info.push_back(Pair("height", (int)e.GetHeight()));
info.push_back(Pair("startingpriority", e.GetPriority(e.GetHeight())));
info.push_back(
Pair("currentpriority", e.GetPriority(chainActive.Height())));
info.push_back(Pair("descendantcount", e.GetCountWithDescendants()));
info.push_back(Pair("descendantsize", e.GetSizeWithDescendants()));
info.push_back(
Pair("descendantfees", e.GetModFeesWithDescendants() / SATOSHI));
info.push_back(Pair("ancestorcount", e.GetCountWithAncestors()));
info.push_back(Pair("ancestorsize", e.GetSizeWithAncestors()));
info.push_back(Pair("ancestorfees", e.GetModFeesWithAncestors() / SATOSHI));
const CTransaction &tx = e.GetTx();
std::set setDepends;
for (const CTxIn &txin : tx.vin) {
if (mempool.exists(txin.prevout.GetTxId())) {
setDepends.insert(txin.prevout.GetTxId().ToString());
}
}
UniValue depends(UniValue::VARR);
for (const std::string &dep : setDepends) {
depends.push_back(dep);
}
info.push_back(Pair("depends", depends));
}
UniValue mempoolToJSON(bool fVerbose = false) {
if (fVerbose) {
LOCK(mempool.cs);
UniValue o(UniValue::VOBJ);
for (const CTxMemPoolEntry &e : mempool.mapTx) {
const uint256 &txid = e.GetTx().GetId();
UniValue info(UniValue::VOBJ);
entryToJSON(info, e);
o.push_back(Pair(txid.ToString(), info));
}
return o;
} else {
std::vector vtxids;
mempool.queryHashes(vtxids);
UniValue a(UniValue::VARR);
for (const uint256 &txid : vtxids) {
a.push_back(txid.ToString());
}
return a;
}
}
UniValue getrawmempool(const Config &config, const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() > 1) {
throw std::runtime_error(
"getrawmempool ( verbose )\n"
"\nReturns all transaction ids in memory pool as a json array of "
"string transaction ids.\n"
"\nArguments:\n"
"1. verbose (boolean, optional, default=false) True for a json "
"object, false for array of transaction ids\n"
"\nResult: (for verbose = false):\n"
"[ (json array of string)\n"
" \"transactionid\" (string) The transaction id\n"
" ,...\n"
"]\n"
"\nResult: (for verbose = true):\n"
"{ (json object)\n"
" \"transactionid\" : { (json object)\n" +
EntryDescriptionString() + " }, ...\n"
"}\n"
"\nExamples:\n" +
HelpExampleCli("getrawmempool", "true") +
HelpExampleRpc("getrawmempool", "true"));
}
bool fVerbose = false;
if (request.params.size() > 0) {
fVerbose = request.params[0].get_bool();
}
return mempoolToJSON(fVerbose);
}
UniValue getmempoolancestors(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() < 1 ||
request.params.size() > 2) {
throw std::runtime_error(
"getmempoolancestors txid (verbose)\n"
"\nIf txid is in the mempool, returns all in-mempool ancestors.\n"
"\nArguments:\n"
"1. \"txid\" (string, required) The transaction id "
"(must be in mempool)\n"
"2. verbose (boolean, optional, default=false) "
"True for a json object, false for array of transaction ids\n"
"\nResult (for verbose=false):\n"
"[ (json array of strings)\n"
" \"transactionid\" (string) The transaction id of an "
"in-mempool ancestor transaction\n"
" ,...\n"
"]\n"
"\nResult (for verbose=true):\n"
"{ (json object)\n"
" \"transactionid\" : { (json object)\n" +
EntryDescriptionString() + " }, ...\n"
"}\n"
"\nExamples:\n" +
HelpExampleCli("getmempoolancestors", "\"mytxid\"") +
HelpExampleRpc("getmempoolancestors", "\"mytxid\""));
}
bool fVerbose = false;
if (request.params.size() > 1) {
fVerbose = request.params[1].get_bool();
}
uint256 hash = ParseHashV(request.params[0], "parameter 1");
LOCK(mempool.cs);
CTxMemPool::txiter it = mempool.mapTx.find(hash);
if (it == mempool.mapTx.end()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Transaction not in mempool");
}
CTxMemPool::setEntries setAncestors;
uint64_t noLimit = std::numeric_limits::max();
std::string dummy;
mempool.CalculateMemPoolAncestors(*it, setAncestors, noLimit, noLimit,
noLimit, noLimit, dummy, false);
if (!fVerbose) {
UniValue o(UniValue::VARR);
for (CTxMemPool::txiter ancestorIt : setAncestors) {
o.push_back(ancestorIt->GetTx().GetId().ToString());
}
return o;
} else {
UniValue o(UniValue::VOBJ);
for (CTxMemPool::txiter ancestorIt : setAncestors) {
const CTxMemPoolEntry &e = *ancestorIt;
const uint256 &_hash = e.GetTx().GetId();
UniValue info(UniValue::VOBJ);
entryToJSON(info, e);
o.push_back(Pair(_hash.ToString(), info));
}
return o;
}
}
UniValue getmempooldescendants(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() < 1 ||
request.params.size() > 2) {
throw std::runtime_error(
"getmempooldescendants txid (verbose)\n"
"\nIf txid is in the mempool, returns all in-mempool descendants.\n"
"\nArguments:\n"
"1. \"txid\" (string, required) The transaction id "
"(must be in mempool)\n"
"2. verbose (boolean, optional, default=false) "
"True for a json object, false for array of transaction ids\n"
"\nResult (for verbose=false):\n"
"[ (json array of strings)\n"
" \"transactionid\" (string) The transaction id of an "
"in-mempool descendant transaction\n"
" ,...\n"
"]\n"
"\nResult (for verbose=true):\n"
"{ (json object)\n"
" \"transactionid\" : { (json object)\n" +
EntryDescriptionString() + " }, ...\n"
"}\n"
"\nExamples:\n" +
HelpExampleCli("getmempooldescendants", "\"mytxid\"") +
HelpExampleRpc("getmempooldescendants", "\"mytxid\""));
}
bool fVerbose = false;
if (request.params.size() > 1) fVerbose = request.params[1].get_bool();
uint256 hash = ParseHashV(request.params[0], "parameter 1");
LOCK(mempool.cs);
CTxMemPool::txiter it = mempool.mapTx.find(hash);
if (it == mempool.mapTx.end()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Transaction not in mempool");
}
CTxMemPool::setEntries setDescendants;
mempool.CalculateDescendants(it, setDescendants);
// CTxMemPool::CalculateDescendants will include the given tx
setDescendants.erase(it);
if (!fVerbose) {
UniValue o(UniValue::VARR);
for (CTxMemPool::txiter descendantIt : setDescendants) {
o.push_back(descendantIt->GetTx().GetId().ToString());
}
return o;
} else {
UniValue o(UniValue::VOBJ);
for (CTxMemPool::txiter descendantIt : setDescendants) {
const CTxMemPoolEntry &e = *descendantIt;
const uint256 &_hash = e.GetTx().GetId();
UniValue info(UniValue::VOBJ);
entryToJSON(info, e);
o.push_back(Pair(_hash.ToString(), info));
}
return o;
}
}
UniValue getmempoolentry(const Config &config, const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"getmempoolentry txid\n"
"\nReturns mempool data for given transaction\n"
"\nArguments:\n"
"1. \"txid\" (string, required) "
"The transaction id (must be in mempool)\n"
"\nResult:\n"
"{ (json object)\n" +
EntryDescriptionString() + "}\n"
"\nExamples:\n" +
HelpExampleCli("getmempoolentry", "\"mytxid\"") +
HelpExampleRpc("getmempoolentry", "\"mytxid\""));
}
uint256 hash = ParseHashV(request.params[0], "parameter 1");
LOCK(mempool.cs);
CTxMemPool::txiter it = mempool.mapTx.find(hash);
if (it == mempool.mapTx.end()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Transaction not in mempool");
}
const CTxMemPoolEntry &e = *it;
UniValue info(UniValue::VOBJ);
entryToJSON(info, e);
return info;
}
UniValue getblockhash(const Config &config, const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"getblockhash height\n"
"\nReturns hash of block in best-block-chain at height provided.\n"
"\nArguments:\n"
"1. height (numeric, required) The height index\n"
"\nResult:\n"
"\"hash\" (string) The block hash\n"
"\nExamples:\n" +
HelpExampleCli("getblockhash", "1000") +
HelpExampleRpc("getblockhash", "1000"));
}
LOCK(cs_main);
int nHeight = request.params[0].get_int();
if (nHeight < 0 || nHeight > chainActive.Height()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range");
}
CBlockIndex *pblockindex = chainActive[nHeight];
return pblockindex->GetBlockHash().GetHex();
}
UniValue getblockheader(const Config &config, const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() < 1 ||
request.params.size() > 2) {
throw std::runtime_error(
"getblockheader \"hash\" ( verbose )\n"
"\nIf verbose is false, returns a string that is serialized, "
"hex-encoded data for blockheader 'hash'.\n"
"If verbose is true, returns an Object with information about "
"blockheader .\n"
"\nArguments:\n"
"1. \"hash\" (string, required) The block hash\n"
"2. verbose (boolean, optional, default=true) true for a "
"json object, false for the hex encoded data\n"
"\nResult (for verbose = true):\n"
"{\n"
" \"hash\" : \"hash\", (string) the block hash (same as "
"provided)\n"
" \"confirmations\" : n, (numeric) The number of confirmations, "
"or -1 if the block is not on the main chain\n"
" \"height\" : n, (numeric) The block height or index\n"
" \"version\" : n, (numeric) The block version\n"
" \"versionHex\" : \"00000000\", (string) The block version "
"formatted in hexadecimal\n"
" \"merkleroot\" : \"xxxx\", (string) The merkle root\n"
" \"time\" : ttt, (numeric) The block time in seconds "
"since epoch (Jan 1 1970 GMT)\n"
" \"mediantime\" : ttt, (numeric) The median block time in "
"seconds since epoch (Jan 1 1970 GMT)\n"
" \"nonce\" : n, (numeric) The nonce\n"
" \"bits\" : \"1d00ffff\", (string) The bits\n"
" \"difficulty\" : x.xxx, (numeric) The difficulty\n"
" \"chainwork\" : \"0000...1f3\" (string) Expected number of "
"hashes required to produce the current chain (in hex)\n"
" \"previousblockhash\" : \"hash\", (string) The hash of the "
"previous block\n"
" \"nextblockhash\" : \"hash\", (string) The hash of the "
"next block\n"
"}\n"
"\nResult (for verbose=false):\n"
"\"data\" (string) A string that is serialized, "
"hex-encoded data for block 'hash'.\n"
"\nExamples:\n" +
HelpExampleCli("getblockheader", "\"00000000c937983704a73af28acdec3"
"7b049d214adbda81d7e2a3dd146f6ed09"
"\"") +
HelpExampleRpc("getblockheader", "\"00000000c937983704a73af28acdec3"
"7b049d214adbda81d7e2a3dd146f6ed09"
"\""));
}
LOCK(cs_main);
std::string strHash = request.params[0].get_str();
uint256 hash(uint256S(strHash));
bool fVerbose = true;
if (request.params.size() > 1) {
fVerbose = request.params[1].get_bool();
}
if (mapBlockIndex.count(hash) == 0) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
CBlockIndex *pblockindex = mapBlockIndex[hash];
if (!fVerbose) {
CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION);
ssBlock << pblockindex->GetBlockHeader();
std::string strHex = HexStr(ssBlock.begin(), ssBlock.end());
return strHex;
}
return blockheaderToJSON(pblockindex);
}
UniValue getblock(const Config &config, const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() < 1 ||
request.params.size() > 2) {
throw std::runtime_error(
"getblock \"blockhash\" ( verbose )\n"
"\nIf verbose is false, returns a string that is serialized, "
"hex-encoded data for block 'hash'.\n"
"If verbose is true, returns an Object with information about "
"block .\n"
"\nArguments:\n"
"1. \"blockhash\" (string, required) The block hash\n"
"2. verbose (boolean, optional, default=true) true "
"for a json object, false for the hex encoded data\n"
"\nResult (for verbose = true):\n"
"{\n"
" \"hash\" : \"hash\", (string) the block hash (same as "
"provided)\n"
" \"confirmations\" : n, (numeric) The number of confirmations, "
"or -1 if the block is not on the main chain\n"
" \"size\" : n, (numeric) The block size\n"
" \"height\" : n, (numeric) The block height or index\n"
" \"version\" : n, (numeric) The block version\n"
" \"versionHex\" : \"00000000\", (string) The block version "
"formatted in hexadecimal\n"
" \"merkleroot\" : \"xxxx\", (string) The merkle root\n"
" \"tx\" : [ (array of string) The transaction ids\n"
" \"transactionid\" (string) The transaction id\n"
" ,...\n"
" ],\n"
" \"time\" : ttt, (numeric) The block time in seconds "
"since epoch (Jan 1 1970 GMT)\n"
" \"mediantime\" : ttt, (numeric) The median block time in "
"seconds since epoch (Jan 1 1970 GMT)\n"
" \"nonce\" : n, (numeric) The nonce\n"
" \"bits\" : \"1d00ffff\", (string) The bits\n"
" \"difficulty\" : x.xxx, (numeric) The difficulty\n"
" \"chainwork\" : \"xxxx\", (string) Expected number of hashes "
"required to produce the chain up to this block (in hex)\n"
" \"previousblockhash\" : \"hash\", (string) The hash of the "
"previous block\n"
" \"nextblockhash\" : \"hash\" (string) The hash of the "
"next block\n"
"}\n"
"\nResult (for verbose=false):\n"
"\"data\" (string) A string that is serialized, "
"hex-encoded data for block 'hash'.\n"
"\nExamples:\n" +
HelpExampleCli("getblock", "\"00000000c937983704a73af28acdec37b049d"
"214adbda81d7e2a3dd146f6ed09\"") +
HelpExampleRpc("getblock", "\"00000000c937983704a73af28acdec37b049d"
"214adbda81d7e2a3dd146f6ed09\""));
}
LOCK(cs_main);
std::string strHash = request.params[0].get_str();
uint256 hash(uint256S(strHash));
bool fVerbose = true;
if (request.params.size() > 1) {
fVerbose = request.params[1].get_bool();
}
if (mapBlockIndex.count(hash) == 0) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
CBlock block;
CBlockIndex *pblockindex = mapBlockIndex[hash];
if (fHavePruned && !pblockindex->nStatus.hasData() &&
pblockindex->nTx > 0) {
throw JSONRPCError(RPC_MISC_ERROR, "Block not available (pruned data)");
}
if (!ReadBlockFromDisk(block, pblockindex, config)) {
// Block not found on disk. This could be because we have the block
// header in our index but don't have the block (for example if a
// non-whitelisted node sends us an unrequested long chain of valid
// blocks, we add the headers to our index, but don't accept the block).
throw JSONRPCError(RPC_MISC_ERROR, "Block not found on disk");
}
if (!fVerbose) {
CDataStream ssBlock(SER_NETWORK,
PROTOCOL_VERSION | RPCSerializationFlags());
ssBlock << block;
std::string strHex = HexStr(ssBlock.begin(), ssBlock.end());
return strHex;
}
return blockToJSON(config, block, pblockindex);
}
struct CCoinsStats {
int nHeight;
uint256 hashBlock;
uint64_t nTransactions;
uint64_t nTransactionOutputs;
uint64_t nBogoSize;
uint256 hashSerialized;
uint64_t nDiskSize;
Amount nTotalAmount;
CCoinsStats()
: nHeight(0), nTransactions(0), nTransactionOutputs(0), nBogoSize(0),
nDiskSize(0), nTotalAmount() {}
};
static void ApplyStats(CCoinsStats &stats, CHashWriter &ss, const uint256 &hash,
const std::map &outputs) {
assert(!outputs.empty());
ss << hash;
ss << VARINT(outputs.begin()->second.GetHeight() * 2 +
outputs.begin()->second.IsCoinBase());
stats.nTransactions++;
for (const auto output : outputs) {
ss << VARINT(output.first + 1);
ss << output.second.GetTxOut().scriptPubKey;
ss << VARINT(output.second.GetTxOut().nValue / SATOSHI);
stats.nTransactionOutputs++;
stats.nTotalAmount += output.second.GetTxOut().nValue;
stats.nBogoSize +=
32 /* txid */ + 4 /* vout index */ + 4 /* height + coinbase */ +
8 /* amount */ + 2 /* scriptPubKey len */ +
output.second.GetTxOut().scriptPubKey.size() /* scriptPubKey */;
}
ss << VARINT(0);
}
//! Calculate statistics about the unspent transaction output set
static bool GetUTXOStats(CCoinsView *view, CCoinsStats &stats) {
std::unique_ptr pcursor(view->Cursor());
CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
stats.hashBlock = pcursor->GetBestBlock();
{
LOCK(cs_main);
stats.nHeight = mapBlockIndex.find(stats.hashBlock)->second->nHeight;
}
ss << stats.hashBlock;
uint256 prevkey;
std::map outputs;
while (pcursor->Valid()) {
boost::this_thread::interruption_point();
COutPoint key;
Coin coin;
if (pcursor->GetKey(key) && pcursor->GetValue(coin)) {
if (!outputs.empty() && key.GetTxId() != prevkey) {
ApplyStats(stats, ss, prevkey, outputs);
outputs.clear();
}
prevkey = key.GetTxId();
outputs[key.GetN()] = std::move(coin);
} else {
return error("%s: unable to read value", __func__);
}
pcursor->Next();
}
if (!outputs.empty()) {
ApplyStats(stats, ss, prevkey, outputs);
}
stats.hashSerialized = ss.GetHash();
stats.nDiskSize = view->EstimateSize();
return true;
}
UniValue pruneblockchain(const Config &config, const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"pruneblockchain\n"
"\nArguments:\n"
"1. \"height\" (numeric, required) The block height to prune "
"up to. May be set to a discrete height, or a unix timestamp\n"
" to prune blocks whose block time is at least 2 "
"hours older than the provided timestamp.\n"
"\nResult:\n"
"n (numeric) Height of the last block pruned.\n"
"\nExamples:\n" +
HelpExampleCli("pruneblockchain", "1000") +
HelpExampleRpc("pruneblockchain", "1000"));
}
if (!fPruneMode) {
throw JSONRPCError(
RPC_MISC_ERROR,
"Cannot prune blocks because node is not in prune mode.");
}
LOCK(cs_main);
int heightParam = request.params[0].get_int();
if (heightParam < 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative block height.");
}
// Height value more than a billion is too high to be a block height, and
// too low to be a block time (corresponds to timestamp from Sep 2001).
if (heightParam > 1000000000) {
// Add a 2 hour buffer to include blocks which might have had old
// timestamps
CBlockIndex *pindex =
chainActive.FindEarliestAtLeast(heightParam - TIMESTAMP_WINDOW);
if (!pindex) {
throw JSONRPCError(
RPC_INVALID_PARAMETER,
"Could not find block with at least the specified timestamp.");
}
heightParam = pindex->nHeight;
}
unsigned int height = (unsigned int)heightParam;
unsigned int chainHeight = (unsigned int)chainActive.Height();
if (chainHeight < config.GetChainParams().PruneAfterHeight()) {
throw JSONRPCError(RPC_MISC_ERROR,
"Blockchain is too short for pruning.");
} else if (height > chainHeight) {
throw JSONRPCError(
RPC_INVALID_PARAMETER,
"Blockchain is shorter than the attempted prune height.");
} else if (height > chainHeight - MIN_BLOCKS_TO_KEEP) {
LogPrint(BCLog::RPC, "Attempt to prune blocks close to the tip. "
"Retaining the minimum number of blocks.");
height = chainHeight - MIN_BLOCKS_TO_KEEP;
}
PruneBlockFilesManual(height);
return uint64_t(height);
}
UniValue gettxoutsetinfo(const Config &config, const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 0) {
throw std::runtime_error(
"gettxoutsetinfo\n"
"\nReturns statistics about the unspent transaction output set.\n"
"Note this call may take some time.\n"
"\nResult:\n"
"{\n"
" \"height\":n, (numeric) The current block height (index)\n"
" \"bestblock\": \"hex\", (string) the best block hash hex\n"
" \"transactions\": n, (numeric) The number of transactions\n"
" \"txouts\": n, (numeric) The number of output "
"transactions\n"
" \"bogosize\": n, (numeric) A database-independent "
"metric for UTXO set size\n"
" \"hash_serialized\": \"hash\", (string) The serialized hash\n"
" \"disk_size\": n, (numeric) The estimated size of the "
"chainstate on disk\n"
" \"total_amount\": x.xxx (numeric) The total amount\n"
"}\n"
"\nExamples:\n" +
HelpExampleCli("gettxoutsetinfo", "") +
HelpExampleRpc("gettxoutsetinfo", ""));
}
UniValue ret(UniValue::VOBJ);
CCoinsStats stats;
FlushStateToDisk();
if (GetUTXOStats(pcoinsdbview, stats)) {
ret.push_back(Pair("height", int64_t(stats.nHeight)));
ret.push_back(Pair("bestblock", stats.hashBlock.GetHex()));
ret.push_back(Pair("transactions", int64_t(stats.nTransactions)));
ret.push_back(Pair("txouts", int64_t(stats.nTransactionOutputs)));
ret.push_back(Pair("bogosize", int64_t(stats.nBogoSize)));
ret.push_back(Pair("hash_serialized", stats.hashSerialized.GetHex()));
ret.push_back(Pair("disk_size", stats.nDiskSize));
ret.push_back(
Pair("total_amount", ValueFromAmount(stats.nTotalAmount)));
} else {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
}
return ret;
}
UniValue gettxout(const Config &config, const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() < 2 ||
request.params.size() > 3) {
throw std::runtime_error(
"gettxout \"txid\" n ( include_mempool )\n"
"\nReturns details about an unspent transaction output.\n"
"\nArguments:\n"
"1. \"txid\" (string, required) The transaction id\n"
"2. \"n\" (numeric, required) vout number\n"
"3. \"include_mempool\" (boolean, optional) Whether to include "
"the mempool. Default: true."
" Note that an unspent output that is spent in the mempool "
"won't appear.\n"
"\nResult:\n"
"{\n"
" \"bestblock\" : \"hash\", (string) the block hash\n"
" \"confirmations\" : n, (numeric) The number of "
"confirmations\n"
" \"value\" : x.xxx, (numeric) The transaction value "
"in " +
CURRENCY_UNIT +
"\n"
" \"scriptPubKey\" : { (json object)\n"
" \"asm\" : \"code\", (string) \n"
" \"hex\" : \"hex\", (string) \n"
" \"reqSigs\" : n, (numeric) Number of required "
"signatures\n"
" \"type\" : \"pubkeyhash\", (string) The type, eg pubkeyhash\n"
" \"addresses\" : [ (array of string) array of "
"bitcoin addresses\n"
" \"address\" (string) bitcoin address\n"
" ,...\n"
" ]\n"
" },\n"
" \"coinbase\" : true|false (boolean) Coinbase or not\n"
"}\n"
"\nExamples:\n"
"\nGet unspent transactions\n" +
HelpExampleCli("listunspent", "") + "\nView the details\n" +
HelpExampleCli("gettxout", "\"txid\" 1") +
"\nAs a json rpc call\n" +
HelpExampleRpc("gettxout", "\"txid\", 1"));
}
LOCK(cs_main);
UniValue ret(UniValue::VOBJ);
std::string strHash = request.params[0].get_str();
uint256 hash(uint256S(strHash));
int n = request.params[1].get_int();
COutPoint out(hash, n);
bool fMempool = true;
if (request.params.size() > 2) {
fMempool = request.params[2].get_bool();
}
Coin coin;
if (fMempool) {
LOCK(mempool.cs);
CCoinsViewMemPool view(pcoinsTip, mempool);
if (!view.GetCoin(out, coin) || mempool.isSpent(out)) {
// TODO: this should be done by the CCoinsViewMemPool
return NullUniValue;
}
} else {
if (!pcoinsTip->GetCoin(out, coin)) {
return NullUniValue;
}
}
BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock());
CBlockIndex *pindex = it->second;
ret.push_back(Pair("bestblock", pindex->GetBlockHash().GetHex()));
if (coin.GetHeight() == MEMPOOL_HEIGHT) {
ret.push_back(Pair("confirmations", 0));
} else {
ret.push_back(Pair("confirmations",
int64_t(pindex->nHeight - coin.GetHeight() + 1)));
}
ret.push_back(Pair("value", ValueFromAmount(coin.GetTxOut().nValue)));
UniValue o(UniValue::VOBJ);
ScriptPubKeyToJSON(config, coin.GetTxOut().scriptPubKey, o, true);
ret.push_back(Pair("scriptPubKey", o));
ret.push_back(Pair("coinbase", coin.IsCoinBase()));
return ret;
}
UniValue verifychain(const Config &config, const JSONRPCRequest &request) {
int nCheckLevel = gArgs.GetArg("-checklevel", DEFAULT_CHECKLEVEL);
int nCheckDepth = gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS);
if (request.fHelp || request.params.size() > 2) {
throw std::runtime_error(
"verifychain ( checklevel nblocks )\n"
"\nVerifies blockchain database.\n"
"\nArguments:\n"
"1. checklevel (numeric, optional, 0-4, default=" +
strprintf("%d", nCheckLevel) +
") How thorough the block verification is.\n"
"2. nblocks (numeric, optional, default=" +
strprintf("%d", nCheckDepth) +
", 0=all) The number of blocks to check.\n"
"\nResult:\n"
"true|false (boolean) Verified or not\n"
"\nExamples:\n" +
HelpExampleCli("verifychain", "") +
HelpExampleRpc("verifychain", ""));
}
LOCK(cs_main);
if (request.params.size() > 0) {
nCheckLevel = request.params[0].get_int();
}
if (request.params.size() > 1) {
nCheckDepth = request.params[1].get_int();
}
return CVerifyDB().VerifyDB(config, pcoinsTip, nCheckLevel, nCheckDepth);
}
/** Implementation of IsSuperMajority with better feedback */
static UniValue SoftForkMajorityDesc(int version, CBlockIndex *pindex,
const Consensus::Params &consensusParams) {
UniValue rv(UniValue::VOBJ);
bool activated = false;
switch (version) {
case 2:
activated = pindex->nHeight >= consensusParams.BIP34Height;
break;
case 3:
activated = pindex->nHeight >= consensusParams.BIP66Height;
break;
case 4:
activated = pindex->nHeight >= consensusParams.BIP65Height;
break;
case 5:
activated = pindex->nHeight >= consensusParams.CSVHeight;
break;
}
rv.push_back(Pair("status", activated));
return rv;
}
static UniValue SoftForkDesc(const std::string &name, int version,
CBlockIndex *pindex,
const Consensus::Params &consensusParams) {
UniValue rv(UniValue::VOBJ);
rv.push_back(Pair("id", name));
rv.push_back(Pair("version", version));
rv.push_back(
Pair("reject", SoftForkMajorityDesc(version, pindex, consensusParams)));
return rv;
}
UniValue getblockchaininfo(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 0) {
throw std::runtime_error(
"getblockchaininfo\n"
"Returns an object containing various state info regarding "
"blockchain processing.\n"
"\nResult:\n"
"{\n"
" \"chain\": \"xxxx\", (string) current network name as "
"defined in BIP70 (main, test, regtest)\n"
" \"blocks\": xxxxxx, (numeric) the current number of "
"blocks processed in the server\n"
" \"headers\": xxxxxx, (numeric) the current number of "
"headers we have validated\n"
" \"bestblockhash\": \"...\", (string) the hash of the currently "
"best block\n"
" \"difficulty\": xxxxxx, (numeric) the current difficulty\n"
" \"mediantime\": xxxxxx, (numeric) median time for the "
"current best block\n"
" \"verificationprogress\": xxxx, (numeric) estimate of "
"verification progress [0..1]\n"
" \"chainwork\": \"xxxx\" (string) total amount of work in "
"active chain, in hexadecimal\n"
" \"pruned\": xx, (boolean) if the blocks are subject "
"to pruning\n"
" \"pruneheight\": xxxxxx, (numeric) lowest-height complete "
"block stored\n"
" \"softforks\": [ (array) status of softforks in "
"progress\n"
" {\n"
" \"id\": \"xxxx\", (string) name of softfork\n"
" \"version\": xx, (numeric) block version\n"
" \"reject\": { (object) progress toward "
"rejecting pre-softfork blocks\n"
" \"status\": xx, (boolean) true if threshold "
"reached\n"
" },\n"
" }, ...\n"
" ]\n"
"}\n"
"\nExamples:\n" +
HelpExampleCli("getblockchaininfo", "") +
HelpExampleRpc("getblockchaininfo", ""));
}
LOCK(cs_main);
UniValue obj(UniValue::VOBJ);
obj.push_back(Pair("chain", config.GetChainParams().NetworkIDString()));
obj.push_back(Pair("blocks", int(chainActive.Height())));
obj.push_back(
Pair("headers", pindexBestHeader ? pindexBestHeader->nHeight : -1));
obj.push_back(
Pair("bestblockhash", chainActive.Tip()->GetBlockHash().GetHex()));
obj.push_back(Pair("difficulty", double(GetDifficulty(chainActive.Tip()))));
obj.push_back(
Pair("mediantime", int64_t(chainActive.Tip()->GetMedianTimePast())));
obj.push_back(
Pair("verificationprogress",
GuessVerificationProgress(config.GetChainParams().TxData(),
chainActive.Tip())));
obj.push_back(Pair("chainwork", chainActive.Tip()->nChainWork.GetHex()));
obj.push_back(Pair("pruned", fPruneMode));
const Consensus::Params &consensusParams =
config.GetChainParams().GetConsensus();
CBlockIndex *tip = chainActive.Tip();
UniValue softforks(UniValue::VARR);
softforks.push_back(SoftForkDesc("bip34", 2, tip, consensusParams));
softforks.push_back(SoftForkDesc("bip66", 3, tip, consensusParams));
softforks.push_back(SoftForkDesc("bip65", 4, tip, consensusParams));
softforks.push_back(SoftForkDesc("csv", 5, tip, consensusParams));
obj.push_back(Pair("softforks", softforks));
if (fPruneMode) {
CBlockIndex *block = chainActive.Tip();
while (block && block->pprev && block->pprev->nStatus.hasData()) {
block = block->pprev;
}
obj.push_back(Pair("pruneheight", block->nHeight));
}
return obj;
}
/** Comparison function for sorting the getchaintips heads. */
struct CompareBlocksByHeight {
bool operator()(const CBlockIndex *a, const CBlockIndex *b) const {
// Make sure that unequal blocks with the same height do not compare
// equal. Use the pointers themselves to make a distinction.
if (a->nHeight != b->nHeight) {
return (a->nHeight > b->nHeight);
}
return a < b;
}
};
UniValue getchaintips(const Config &config, const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 0) {
throw std::runtime_error(
"getchaintips\n"
"Return information about all known tips in the block tree,"
" including the main chain as well as orphaned branches.\n"
"\nResult:\n"
"[\n"
" {\n"
" \"height\": xxxx, (numeric) height of the chain tip\n"
" \"hash\": \"xxxx\", (string) block hash of the tip\n"
" \"branchlen\": 0 (numeric) zero for main chain\n"
" \"status\": \"active\" (string) \"active\" for the main "
"chain\n"
" },\n"
" {\n"
" \"height\": xxxx,\n"
" \"hash\": \"xxxx\",\n"
" \"branchlen\": 1 (numeric) length of branch "
"connecting the tip to the main chain\n"
" \"status\": \"xxxx\" (string) status of the chain "
"(active, valid-fork, valid-headers, headers-only, invalid)\n"
" }\n"
"]\n"
"Possible values for status:\n"
"1. \"invalid\" This branch contains at least one "
"invalid block\n"
"2. \"headers-only\" Not all blocks for this branch are "
"available, but the headers are valid\n"
"3. \"valid-headers\" All blocks are available for this "
"branch, but they were never fully validated\n"
"4. \"valid-fork\" This branch is not part of the "
"active chain, but is fully validated\n"
"5. \"active\" This is the tip of the active main "
"chain, which is certainly valid\n"
"\nExamples:\n" +
HelpExampleCli("getchaintips", "") +
HelpExampleRpc("getchaintips", ""));
}
LOCK(cs_main);
/**
* Idea: the set of chain tips is chainActive.tip, plus orphan blocks which
* do not have another orphan building off of them.
* Algorithm:
* - Make one pass through mapBlockIndex, picking out the orphan blocks,
* and also storing a set of the orphan block's pprev pointers.
* - Iterate through the orphan blocks. If the block isn't pointed to by
* another orphan, it is a chain tip.
* - add chainActive.Tip()
*/
std::set setTips;
std::set setOrphans;
std::set setPrevs;
for (const std::pair &item : mapBlockIndex) {
if (!chainActive.Contains(item.second)) {
setOrphans.insert(item.second);
setPrevs.insert(item.second->pprev);
}
}
for (std::set::iterator it = setOrphans.begin();
it != setOrphans.end(); ++it) {
if (setPrevs.erase(*it) == 0) {
setTips.insert(*it);
}
}
// Always report the currently active tip.
setTips.insert(chainActive.Tip());
/* Construct the output array. */
UniValue res(UniValue::VARR);
for (const CBlockIndex *block : setTips) {
UniValue obj(UniValue::VOBJ);
obj.push_back(Pair("height", block->nHeight));
obj.push_back(Pair("hash", block->phashBlock->GetHex()));
const int branchLen =
block->nHeight - chainActive.FindFork(block)->nHeight;
obj.push_back(Pair("branchlen", branchLen));
std::string status;
if (chainActive.Contains(block)) {
// This block is part of the currently active chain.
status = "active";
} else if (block->nStatus.isInvalid()) {
// This block or one of its ancestors is invalid.
status = "invalid";
} else if (block->nChainTx == 0) {
// This block cannot be connected because full block data for it or
// one of its parents is missing.
status = "headers-only";
} else if (block->IsValid(BlockValidity::SCRIPTS)) {
// This block is fully validated, but no longer part of the active
// chain. It was probably the active block once, but was
// reorganized.
status = "valid-fork";
} else if (block->IsValid(BlockValidity::TREE)) {
// The headers for this block are valid, but it has not been
// validated. It was probably never part of the most-work chain.
status = "valid-headers";
} else {
// No clue.
status = "unknown";
}
obj.push_back(Pair("status", status));
res.push_back(obj);
}
return res;
}
UniValue mempoolInfoToJSON() {
UniValue ret(UniValue::VOBJ);
ret.push_back(Pair("size", (int64_t)mempool.size()));
ret.push_back(Pair("bytes", (int64_t)mempool.GetTotalTxSize()));
ret.push_back(Pair("usage", (int64_t)mempool.DynamicMemoryUsage()));
size_t maxmempool =
gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
ret.push_back(Pair("maxmempool", (int64_t)maxmempool));
ret.push_back(
Pair("mempoolminfee",
ValueFromAmount(mempool.GetMinFee(maxmempool).GetFeePerK())));
return ret;
}
UniValue getmempoolinfo(const Config &config, const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 0) {
throw std::runtime_error(
"getmempoolinfo\n"
"\nReturns details on the active state of the TX memory pool.\n"
"\nResult:\n"
"{\n"
" \"size\": xxxxx, (numeric) Current tx count\n"
" \"bytes\": xxxxx, (numeric) Transaction size.\n"
" \"usage\": xxxxx, (numeric) Total memory usage for "
"the mempool\n"
" \"maxmempool\": xxxxx, (numeric) Maximum memory usage "
"for the mempool\n"
" \"mempoolminfee\": xxxxx (numeric) Minimum fee for tx to "
"be accepted\n"
"}\n"
"\nExamples:\n" +
HelpExampleCli("getmempoolinfo", "") +
HelpExampleRpc("getmempoolinfo", ""));
}
return mempoolInfoToJSON();
}
UniValue preciousblock(const Config &config, const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"preciousblock \"blockhash\"\n"
"\nTreats a block as if it were received before others with the "
"same work.\n"
"\nA later preciousblock call can override the effect of an "
"earlier one.\n"
"\nThe effects of preciousblock are not retained across restarts.\n"
"\nArguments:\n"
"1. \"blockhash\" (string, required) the hash of the block to "
"mark as precious\n"
"\nResult:\n"
"\nExamples:\n" +
HelpExampleCli("preciousblock", "\"blockhash\"") +
HelpExampleRpc("preciousblock", "\"blockhash\""));
}
std::string strHash = request.params[0].get_str();
uint256 hash(uint256S(strHash));
CBlockIndex *pblockindex;
{
LOCK(cs_main);
if (mapBlockIndex.count(hash) == 0) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
pblockindex = mapBlockIndex[hash];
}
CValidationState state;
PreciousBlock(config, state, pblockindex);
if (!state.IsValid()) {
throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason());
}
return NullUniValue;
}
UniValue invalidateblock(const Config &config, const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"invalidateblock \"blockhash\"\n"
"\nPermanently marks a block as invalid, as if it "
"violated a consensus rule.\n"
"\nArguments:\n"
"1. \"blockhash\" (string, required) the hash of "
"the block to mark as invalid\n"
"\nResult:\n"
"\nExamples:\n" +
HelpExampleCli("invalidateblock", "\"blockhash\"") +
HelpExampleRpc("invalidateblock", "\"blockhash\""));
}
std::string strHash = request.params[0].get_str();
uint256 hash(uint256S(strHash));
CValidationState state;
{
LOCK(cs_main);
if (mapBlockIndex.count(hash) == 0) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
CBlockIndex *pblockindex = mapBlockIndex[hash];
InvalidateBlock(config, state, pblockindex);
}
if (state.IsValid()) {
ActivateBestChain(config, state);
}
if (!state.IsValid()) {
throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason());
}
return NullUniValue;
}
UniValue reconsiderblock(const Config &config, const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"reconsiderblock \"blockhash\"\n"
"\nRemoves invalidity status of a block and its descendants, "
"reconsider them for activation.\n"
"This can be used to undo the effects of invalidateblock.\n"
"\nArguments:\n"
"1. \"blockhash\" (string, required) the hash of the block to "
"reconsider\n"
"\nResult:\n"
"\nExamples:\n" +
HelpExampleCli("reconsiderblock", "\"blockhash\"") +
HelpExampleRpc("reconsiderblock", "\"blockhash\""));
}
std::string strHash = request.params[0].get_str();
uint256 hash(uint256S(strHash));
{
LOCK(cs_main);
if (mapBlockIndex.count(hash) == 0) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
CBlockIndex *pblockindex = mapBlockIndex[hash];
ResetBlockFailureFlags(pblockindex);
}
CValidationState state;
ActivateBestChain(config, state);
if (!state.IsValid()) {
throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason());
}
return NullUniValue;
}
UniValue getchaintxstats(const Config &config, const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() > 2) {
throw std::runtime_error(
"getchaintxstats ( nblocks blockhash )\n"
"\nCompute statistics about the total number and rate of "
"transactions in the chain.\n"
"\nArguments:\n"
"1. nblocks (numeric, optional) Size of the window in number "
"of blocks (default: one month).\n"
"2. \"blockhash\" (string, optional) The hash of the block that "
"ends the window.\n"
"\nResult:\n"
"{\n"
" \"time\": xxxxx, (numeric) The timestamp for the "
"final block in the window in UNIX format.\n"
" \"txcount\": xxxxx, (numeric) The total number of "
"transactions in the chain up to that point.\n"
" \"window_block_count\": xxxxx, (numeric) Size of the window in "
"number of blocks.\n"
" \"window_tx_count\": xxxxx, (numeric) The number of "
"transactions in the window. Only returned if "
"\"window_block_count\" is > 0.\n"
" \"window_interval\": xxxxx, (numeric) The elapsed time in "
"the window in seconds. Only returned if \"window_block_count\" is "
"> 0.\n"
" \"txrate\": x.xx, (numeric) The average rate of "
"transactions per second in the window. Only returned if "
"\"window_interval\" is > 0.\n"
"}\n"
"\nExamples:\n" +
HelpExampleCli("getchaintxstats", "") +
HelpExampleRpc("getchaintxstats", "2016"));
}
const CBlockIndex *pindex;
// By default: 1 month
int blockcount = 30 * 24 * 60 * 60 /
config.GetChainParams().GetConsensus().nPowTargetSpacing;
bool havehash = !request.params[1].isNull();
uint256 hash;
if (havehash) {
hash = uint256S(request.params[1].get_str());
}
{
LOCK(cs_main);
if (havehash) {
auto it = mapBlockIndex.find(hash);
if (it == mapBlockIndex.end()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Block not found");
}
pindex = it->second;
if (!chainActive.Contains(pindex)) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Block is not in main chain");
}
} else {
pindex = chainActive.Tip();
}
}
assert(pindex != nullptr);
if (request.params[0].isNull()) {
blockcount = std::max(0, std::min(blockcount, pindex->nHeight - 1));
} else {
blockcount = request.params[0].get_int();
if (blockcount < 0 ||
(blockcount > 0 && blockcount >= pindex->nHeight)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block count: "
"should be between 0 and "
"the block's height - 1");
}
}
const CBlockIndex *pindexPast =
pindex->GetAncestor(pindex->nHeight - blockcount);
int nTimeDiff =
pindex->GetMedianTimePast() - pindexPast->GetMedianTimePast();
int nTxDiff = pindex->nChainTx - pindexPast->nChainTx;
UniValue ret(UniValue::VOBJ);
ret.push_back(Pair("time", int64_t(pindex->nTime)));
ret.push_back(Pair("txcount", int64_t(pindex->nChainTx)));
ret.push_back(Pair("window_block_count", blockcount));
if (blockcount > 0) {
ret.push_back(Pair("window_tx_count", nTxDiff));
ret.push_back(Pair("window_interval", nTimeDiff));
if (nTimeDiff > 0) {
ret.push_back(Pair("txrate", double(nTxDiff) / nTimeDiff));
}
}
return ret;
}
// clang-format off
static const ContextFreeRPCCommand commands[] = {
- // category name actor (function) okSafe argNames
- // ------------------- ------------------------ ---------------------- ------ ----------
- { "blockchain", "getblockchaininfo", getblockchaininfo, true, {} },
- { "blockchain", "getchaintxstats", &getchaintxstats, true, {"nblocks", "blockhash"} },
- { "blockchain", "getbestblockhash", getbestblockhash, true, {} },
- { "blockchain", "getblockcount", getblockcount, true, {} },
- { "blockchain", "getblock", getblock, true, {"blockhash","verbose"} },
- { "blockchain", "getblockhash", getblockhash, true, {"height"} },
- { "blockchain", "getblockheader", getblockheader, true, {"blockhash","verbose"} },
- { "blockchain", "getchaintips", getchaintips, true, {} },
- { "blockchain", "getdifficulty", getdifficulty, true, {} },
- { "blockchain", "getmempoolancestors", getmempoolancestors, true, {"txid","verbose"} },
- { "blockchain", "getmempooldescendants", getmempooldescendants, true, {"txid","verbose"} },
- { "blockchain", "getmempoolentry", getmempoolentry, true, {"txid"} },
- { "blockchain", "getmempoolinfo", getmempoolinfo, true, {} },
- { "blockchain", "getrawmempool", getrawmempool, true, {"verbose"} },
- { "blockchain", "gettxout", gettxout, true, {"txid","n","include_mempool"} },
- { "blockchain", "gettxoutsetinfo", gettxoutsetinfo, true, {} },
- { "blockchain", "pruneblockchain", pruneblockchain, true, {"height"} },
- { "blockchain", "verifychain", verifychain, true, {"checklevel","nblocks"} },
- { "blockchain", "preciousblock", preciousblock, true, {"blockhash"} },
+ // category name actor (function) argNames
+ // ------------------- ------------------------ ---------------------- ----------
+ { "blockchain", "getblockchaininfo", getblockchaininfo, {} },
+ { "blockchain", "getchaintxstats", &getchaintxstats, {"nblocks", "blockhash"} },
+ { "blockchain", "getbestblockhash", getbestblockhash, {} },
+ { "blockchain", "getblockcount", getblockcount, {} },
+ { "blockchain", "getblock", getblock, {"blockhash","verbose"} },
+ { "blockchain", "getblockhash", getblockhash, {"height"} },
+ { "blockchain", "getblockheader", getblockheader, {"blockhash","verbose"} },
+ { "blockchain", "getchaintips", getchaintips, {} },
+ { "blockchain", "getdifficulty", getdifficulty, {} },
+ { "blockchain", "getmempoolancestors", getmempoolancestors, {"txid","verbose"} },
+ { "blockchain", "getmempooldescendants", getmempooldescendants, {"txid","verbose"} },
+ { "blockchain", "getmempoolentry", getmempoolentry, {"txid"} },
+ { "blockchain", "getmempoolinfo", getmempoolinfo, {} },
+ { "blockchain", "getrawmempool", getrawmempool, {"verbose"} },
+ { "blockchain", "gettxout", gettxout, {"txid","n","include_mempool"} },
+ { "blockchain", "gettxoutsetinfo", gettxoutsetinfo, {} },
+ { "blockchain", "pruneblockchain", pruneblockchain, {"height"} },
+ { "blockchain", "verifychain", verifychain, {"checklevel","nblocks"} },
+ { "blockchain", "preciousblock", preciousblock, {"blockhash"} },
/* Not shown in help */
- { "hidden", "invalidateblock", invalidateblock, true, {"blockhash"} },
- { "hidden", "reconsiderblock", reconsiderblock, true, {"blockhash"} },
- { "hidden", "waitfornewblock", waitfornewblock, true, {"timeout"} },
- { "hidden", "waitforblock", waitforblock, true, {"blockhash","timeout"} },
- { "hidden", "waitforblockheight", waitforblockheight, true, {"height","timeout"} },
+ { "hidden", "invalidateblock", invalidateblock, {"blockhash"} },
+ { "hidden", "reconsiderblock", reconsiderblock, {"blockhash"} },
+ { "hidden", "waitfornewblock", waitfornewblock, {"timeout"} },
+ { "hidden", "waitforblock", waitforblock, {"blockhash","timeout"} },
+ { "hidden", "waitforblockheight", waitforblockheight, {"height","timeout"} },
};
// clang-format on
void RegisterBlockchainRPCCommands(CRPCTable &t) {
for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) {
t.appendCommand(commands[vcidx].name, &commands[vcidx]);
}
}
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 2848aed4d..8cc67c70c 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -1,835 +1,835 @@
// 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 "rpc/mining.h"
#include "amount.h"
#include "blockvalidity.h"
#include "chain.h"
#include "chainparams.h"
#include "config.h"
#include "consensus/consensus.h"
#include "consensus/params.h"
#include "consensus/validation.h"
#include "core_io.h"
#include "dstencode.h"
#include "init.h"
#include "miner.h"
#include "net.h"
#include "policy/policy.h"
#include "pow.h"
#include "rpc/blockchain.h"
#include "rpc/server.h"
#include "txmempool.h"
#include "util.h"
#include "utilstrencodings.h"
#include "validation.h"
#include "validationinterface.h"
#include
#include
#include
/**
* Return average network hashes per second based on the last 'lookup' blocks,
* or from the last difficulty change if 'lookup' is nonpositive. If 'height' is
* nonnegative, compute the estimate at the time when a given block was found.
*/
static UniValue GetNetworkHashPS(int lookup, int height) {
CBlockIndex *pb = chainActive.Tip();
if (height >= 0 && height < chainActive.Height()) {
pb = chainActive[height];
}
if (pb == nullptr || !pb->nHeight) {
return 0;
}
// If lookup is -1, then use blocks since last difficulty change.
if (lookup <= 0) {
lookup = pb->nHeight %
Params().GetConsensus().DifficultyAdjustmentInterval() +
1;
}
// If lookup is larger than chain, then set it to chain length.
if (lookup > pb->nHeight) {
lookup = pb->nHeight;
}
CBlockIndex *pb0 = pb;
int64_t minTime = pb0->GetBlockTime();
int64_t maxTime = minTime;
for (int i = 0; i < lookup; i++) {
pb0 = pb0->pprev;
int64_t time = pb0->GetBlockTime();
minTime = std::min(time, minTime);
maxTime = std::max(time, maxTime);
}
// In case there's a situation where minTime == maxTime, we don't want a
// divide by zero exception.
if (minTime == maxTime) {
return 0;
}
arith_uint256 workDiff = pb->nChainWork - pb0->nChainWork;
int64_t timeDiff = maxTime - minTime;
return workDiff.getdouble() / timeDiff;
}
static UniValue getnetworkhashps(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() > 2) {
throw std::runtime_error(
"getnetworkhashps ( nblocks height )\n"
"\nReturns the estimated network hashes per second based on the "
"last n blocks.\n"
"Pass in [blocks] to override # of blocks, -1 specifies since last "
"difficulty change.\n"
"Pass in [height] to estimate the network speed at the time when a "
"certain block was found.\n"
"\nArguments:\n"
"1. nblocks (numeric, optional, default=120) The number of "
"blocks, or -1 for blocks since last difficulty change.\n"
"2. height (numeric, optional, default=-1) To estimate at the "
"time of the given height.\n"
"\nResult:\n"
"x (numeric) Hashes per second estimated\n"
"\nExamples:\n" +
HelpExampleCli("getnetworkhashps", "") +
HelpExampleRpc("getnetworkhashps", ""));
}
LOCK(cs_main);
return GetNetworkHashPS(
request.params.size() > 0 ? request.params[0].get_int() : 120,
request.params.size() > 1 ? request.params[1].get_int() : -1);
}
UniValue generateBlocks(const Config &config,
std::shared_ptr coinbaseScript,
int nGenerate, uint64_t nMaxTries, bool keepScript) {
static const int nInnerLoopCount = 0x100000;
int nHeightStart = 0;
int nHeightEnd = 0;
int nHeight = 0;
{
// Don't keep cs_main locked.
LOCK(cs_main);
nHeightStart = chainActive.Height();
nHeight = nHeightStart;
nHeightEnd = nHeightStart + nGenerate;
}
unsigned int nExtraNonce = 0;
UniValue blockHashes(UniValue::VARR);
while (nHeight < nHeightEnd) {
std::unique_ptr pblocktemplate(
BlockAssembler(config).CreateNewBlock(
coinbaseScript->reserveScript));
if (!pblocktemplate.get()) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block");
}
CBlock *pblock = &pblocktemplate->block;
{
LOCK(cs_main);
IncrementExtraNonce(config, pblock, chainActive.Tip(), nExtraNonce);
}
while (nMaxTries > 0 && pblock->nNonce < nInnerLoopCount &&
!CheckProofOfWork(pblock->GetHash(), pblock->nBits, config)) {
++pblock->nNonce;
--nMaxTries;
}
if (nMaxTries == 0) {
break;
}
if (pblock->nNonce == nInnerLoopCount) {
continue;
}
std::shared_ptr shared_pblock =
std::make_shared(*pblock);
if (!ProcessNewBlock(config, shared_pblock, true, nullptr)) {
throw JSONRPCError(RPC_INTERNAL_ERROR,
"ProcessNewBlock, block not accepted");
}
++nHeight;
blockHashes.push_back(pblock->GetHash().GetHex());
// Mark script as important because it was used at least for one
// coinbase output if the script came from the wallet.
if (keepScript) {
coinbaseScript->KeepScript();
}
}
return blockHashes;
}
static UniValue generatetoaddress(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() < 2 ||
request.params.size() > 3) {
throw std::runtime_error(
"generatetoaddress nblocks address (maxtries)\n"
"\nMine blocks immediately to a specified address (before the RPC "
"call returns)\n"
"\nArguments:\n"
"1. nblocks (numeric, required) How many blocks are generated "
"immediately.\n"
"2. address (string, required) The address to send the newly "
"generated bitcoin to.\n"
"3. 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 to myaddress\n" +
HelpExampleCli("generatetoaddress", "11 \"myaddress\""));
}
int nGenerate = request.params[0].get_int();
uint64_t nMaxTries = 1000000;
if (request.params.size() > 2) {
nMaxTries = request.params[2].get_int();
}
CTxDestination destination =
DecodeDestination(request.params[1].get_str(), config.GetChainParams());
if (!IsValidDestination(destination)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Error: Invalid address");
}
std::shared_ptr coinbaseScript =
std::make_shared();
coinbaseScript->reserveScript = GetScriptForDestination(destination);
return generateBlocks(config, coinbaseScript, nGenerate, nMaxTries, false);
}
static UniValue getmininginfo(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 0) {
throw std::runtime_error(
"getmininginfo\n"
"\nReturns a json object containing mining-related information."
"\nResult:\n"
"{\n"
" \"blocks\": nnn, (numeric) The current block\n"
" \"currentblocksize\": nnn, (numeric) The last block size\n"
" \"currentblocktx\": nnn, (numeric) The last block "
"transaction\n"
" \"difficulty\": xxx.xxxxx (numeric) The current difficulty\n"
" \"errors\": \"...\" (string) Current errors\n"
" \"networkhashps\": nnn, (numeric) The network hashes per "
"second\n"
" \"pooledtx\": n (numeric) The size of the mempool\n"
" \"chain\": \"xxxx\", (string) current network name as "
"defined in BIP70 (main, test, regtest)\n"
"}\n"
"\nExamples:\n" +
HelpExampleCli("getmininginfo", "") +
HelpExampleRpc("getmininginfo", ""));
}
LOCK(cs_main);
UniValue obj(UniValue::VOBJ);
obj.push_back(Pair("blocks", int(chainActive.Height())));
obj.push_back(Pair("currentblocksize", uint64_t(nLastBlockSize)));
obj.push_back(Pair("currentblocktx", uint64_t(nLastBlockTx)));
obj.push_back(Pair("difficulty", double(GetDifficulty(chainActive.Tip()))));
obj.push_back(
Pair("blockprioritypercentage",
uint8_t(gArgs.GetArg("-blockprioritypercentage",
DEFAULT_BLOCK_PRIORITY_PERCENTAGE))));
obj.push_back(Pair("errors", GetWarnings("statusbar")));
obj.push_back(Pair("networkhashps", getnetworkhashps(config, request)));
obj.push_back(Pair("pooledtx", uint64_t(mempool.size())));
obj.push_back(Pair("chain", config.GetChainParams().NetworkIDString()));
return obj;
}
// NOTE: Unlike wallet RPC (which use BCH values), mining RPCs follow GBT (BIP
// 22) in using satoshi amounts
static UniValue prioritisetransaction(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 3) {
throw std::runtime_error(
"prioritisetransaction \n"
"Accepts the transaction into mined blocks at a higher (or lower) "
"priority\n"
"\nArguments:\n"
"1. \"txid\" (string, required) The transaction id.\n"
"2. priority_delta (numeric, required) The priority to add or "
"subtract.\n"
" The transaction selection algorithm considers "
"the tx as it would have a higher priority.\n"
" (priority of a transaction is calculated: "
"coinage * value_in_satoshis / txsize) \n"
"3. fee_delta (numeric, required) The fee value (in satoshis) "
"to add (or subtract, if negative).\n"
" The fee is not actually paid, only the "
"algorithm for selecting transactions into a block\n"
" considers the transaction as it would have paid "
"a higher (or lower) fee.\n"
"\nResult:\n"
"true (boolean) Returns true\n"
"\nExamples:\n" +
HelpExampleCli("prioritisetransaction", "\"txid\" 0.0 10000") +
HelpExampleRpc("prioritisetransaction", "\"txid\", 0.0, 10000"));
}
LOCK(cs_main);
uint256 hash = ParseHashStr(request.params[0].get_str(), "txid");
Amount nAmount = request.params[2].get_int64() * SATOSHI;
mempool.PrioritiseTransaction(hash, request.params[0].get_str(),
request.params[1].get_real(), nAmount);
return true;
}
// NOTE: Assumes a conclusive result; if result is inconclusive, it must be
// handled by caller
static UniValue BIP22ValidationResult(const Config &config,
const CValidationState &state) {
if (state.IsValid()) {
return NullUniValue;
}
std::string strRejectReason = state.GetRejectReason();
if (state.IsError()) {
throw JSONRPCError(RPC_VERIFY_ERROR, strRejectReason);
}
if (state.IsInvalid()) {
if (strRejectReason.empty()) {
return "rejected";
}
return strRejectReason;
}
// Should be impossible.
return "valid?";
}
static UniValue getblocktemplate(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() > 1) {
throw std::runtime_error(
"getblocktemplate ( TemplateRequest )\n"
"\nIf the request parameters include a 'mode' key, that is used to "
"explicitly select between the default 'template' request or a "
"'proposal'.\n"
"It returns data needed to construct a block to work on.\n"
"For full specification, see BIPs 22, 23, 9, and 145:\n"
" "
"https://github.com/bitcoin/bips/blob/master/bip-0022.mediawiki\n"
" "
"https://github.com/bitcoin/bips/blob/master/bip-0023.mediawiki\n"
" "
"https://github.com/bitcoin/bips/blob/master/"
"bip-0009.mediawiki#getblocktemplate_changes\n"
" "
"https://github.com/bitcoin/bips/blob/master/bip-0145.mediawiki\n"
"\nArguments:\n"
"1. template_request (json object, optional) A json object "
"in the following spec\n"
" {\n"
" \"mode\":\"template\" (string, optional) This must be "
"set to \"template\", \"proposal\" (see BIP 23), or omitted\n"
" \"capabilities\":[ (array, optional) A list of "
"strings\n"
" \"support\" (string) client side supported "
"feature, 'longpoll', 'coinbasetxn', 'coinbasevalue', 'proposal', "
"'serverlist', 'workid'\n"
" ,...\n"
" ]\n"
" }\n"
"\n"
"\nResult:\n"
"{\n"
" \"version\" : n, (numeric) The preferred "
"block version\n"
" \"previousblockhash\" : \"xxxx\", (string) The hash of "
"current highest block\n"
" \"transactions\" : [ (array) contents of "
"non-coinbase transactions that should be included in the next "
"block\n"
" {\n"
" \"data\" : \"xxxx\", (string) transaction "
"data encoded in hexadecimal (byte-for-byte)\n"
" \"txid\" : \"xxxx\", (string) transaction id "
"encoded in little-endian hexadecimal\n"
" \"hash\" : \"xxxx\", (string) hash encoded "
"in little-endian hexadecimal (including witness data)\n"
" \"depends\" : [ (array) array of numbers "
"\n"
" n (numeric) transactions "
"before this one (by 1-based index in 'transactions' list) that "
"must be present in the final block if this one is\n"
" ,...\n"
" ],\n"
" \"fee\": n, (numeric) difference in "
"value between transaction inputs and outputs (in Satoshis); for "
"coinbase transactions, this is a negative Number of the total "
"collected block fees (ie, not including the block subsidy); if "
"key is not present, fee is unknown and clients MUST NOT assume "
"there isn't one\n"
" \"sigops\" : n, (numeric) total SigOps "
"cost, as counted for purposes of block limits; if key is not "
"present, sigop cost is unknown and clients MUST NOT assume it is "
"zero\n"
" \"required\" : true|false (boolean) if provided and "
"true, this transaction must be in the final block\n"
" }\n"
" ,...\n"
" ],\n"
" \"coinbaseaux\" : { (json object) data that "
"should be included in the coinbase's scriptSig content\n"
" \"flags\" : \"xx\" (string) key name is to "
"be ignored, and value included in scriptSig\n"
" },\n"
" \"coinbasevalue\" : n, (numeric) maximum allowable "
"input to coinbase transaction, including the generation award and "
"transaction fees (in Satoshis)\n"
" \"coinbasetxn\" : { ... }, (json object) information "
"for coinbase transaction\n"
" \"target\" : \"xxxx\", (string) The hash target\n"
" \"mintime\" : xxx, (numeric) The minimum "
"timestamp appropriate for next block time in seconds since epoch "
"(Jan 1 1970 GMT)\n"
" \"mutable\" : [ (array of string) list of "
"ways the block template may be changed \n"
" \"value\" (string) A way the block "
"template may be changed, e.g. 'time', 'transactions', "
"'prevblock'\n"
" ,...\n"
" ],\n"
" \"noncerange\" : \"00000000ffffffff\",(string) A range of valid "
"nonces\n"
" \"sigoplimit\" : n, (numeric) limit of sigops "
"in blocks\n"
" \"sizelimit\" : n, (numeric) limit of block "
"size\n"
" \"curtime\" : ttt, (numeric) current timestamp "
"in seconds since epoch (Jan 1 1970 GMT)\n"
" \"bits\" : \"xxxxxxxx\", (string) compressed "
"target of next block\n"
" \"height\" : n (numeric) The height of the "
"next block\n"
"}\n"
"\nExamples:\n" +
HelpExampleCli("getblocktemplate", "") +
HelpExampleRpc("getblocktemplate", ""));
}
LOCK(cs_main);
std::string strMode = "template";
UniValue lpval = NullUniValue;
std::set setClientRules;
if (request.params.size() > 0) {
const UniValue &oparam = request.params[0].get_obj();
const UniValue &modeval = find_value(oparam, "mode");
if (modeval.isStr()) {
strMode = modeval.get_str();
} else if (modeval.isNull()) {
/* Do nothing */
} else {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode");
}
lpval = find_value(oparam, "longpollid");
if (strMode == "proposal") {
const UniValue &dataval = find_value(oparam, "data");
if (!dataval.isStr()) {
throw JSONRPCError(RPC_TYPE_ERROR,
"Missing data String key for proposal");
}
CBlock block;
if (!DecodeHexBlk(block, dataval.get_str())) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR,
"Block decode failed");
}
uint256 hash = block.GetHash();
BlockMap::iterator mi = mapBlockIndex.find(hash);
if (mi != mapBlockIndex.end()) {
CBlockIndex *pindex = mi->second;
if (pindex->IsValid(BlockValidity::SCRIPTS)) {
return "duplicate";
}
if (pindex->nStatus.isInvalid()) {
return "duplicate-invalid";
}
return "duplicate-inconclusive";
}
CBlockIndex *const pindexPrev = chainActive.Tip();
// TestBlockValidity only supports blocks built on the current Tip
if (block.hashPrevBlock != pindexPrev->GetBlockHash()) {
return "inconclusive-not-best-prevblk";
}
CValidationState state;
BlockValidationOptions validationOptions =
BlockValidationOptions(false, true);
TestBlockValidity(config, state, block, pindexPrev,
validationOptions);
return BIP22ValidationResult(config, state);
}
}
if (strMode != "template") {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode");
}
if (!g_connman) {
throw JSONRPCError(
RPC_CLIENT_P2P_DISABLED,
"Error: Peer-to-peer functionality missing or disabled");
}
if (g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL) == 0) {
throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED,
"Bitcoin is not connected!");
}
if (IsInitialBlockDownload()) {
throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD,
"Bitcoin is downloading blocks...");
}
static unsigned int nTransactionsUpdatedLast;
if (!lpval.isNull()) {
// Wait to respond until either the best block changes, OR a minute has
// passed and there are more transactions
uint256 hashWatchedChain;
std::chrono::steady_clock::time_point checktxtime;
unsigned int nTransactionsUpdatedLastLP;
if (lpval.isStr()) {
// Format:
std::string lpstr = lpval.get_str();
hashWatchedChain.SetHex(lpstr.substr(0, 64));
nTransactionsUpdatedLastLP = atoi64(lpstr.substr(64));
} else {
// NOTE: Spec does not specify behaviour for non-string longpollid,
// but this makes testing easier
hashWatchedChain = chainActive.Tip()->GetBlockHash();
nTransactionsUpdatedLastLP = nTransactionsUpdatedLast;
}
// Release the wallet and main lock while waiting
LEAVE_CRITICAL_SECTION(cs_main);
{
checktxtime =
std::chrono::steady_clock::now() + std::chrono::minutes(1);
WaitableLock lock(g_best_block_mutex);
while (g_best_block == hashWatchedChain && IsRPCRunning()) {
if (g_best_block_cv.wait_until(lock, checktxtime) ==
std::cv_status::timeout) {
// Timeout: Check transactions for update
if (mempool.GetTransactionsUpdated() !=
nTransactionsUpdatedLastLP) {
break;
}
checktxtime += std::chrono::seconds(10);
}
}
}
ENTER_CRITICAL_SECTION(cs_main);
if (!IsRPCRunning()) {
throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Shutting down");
}
// TODO: Maybe recheck connections/IBD and (if something wrong) send an
// expires-immediately template to stop miners?
}
// Update block
static CBlockIndex *pindexPrev;
static int64_t nStart;
static std::unique_ptr pblocktemplate;
if (pindexPrev != chainActive.Tip() ||
(mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast &&
GetTime() - nStart > 5)) {
// Clear pindexPrev so future calls make a new block, despite any
// failures from here on
pindexPrev = nullptr;
// Store the pindexBest used before CreateNewBlock, to avoid races
nTransactionsUpdatedLast = mempool.GetTransactionsUpdated();
CBlockIndex *pindexPrevNew = chainActive.Tip();
nStart = GetTime();
// Create new block
CScript scriptDummy = CScript() << OP_TRUE;
pblocktemplate = BlockAssembler(config).CreateNewBlock(scriptDummy);
if (!pblocktemplate) {
throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory");
}
// Need to update only after we know CreateNewBlock succeeded
pindexPrev = pindexPrevNew;
}
// pointer for convenience
CBlock *pblock = &pblocktemplate->block;
// Update nTime
UpdateTime(pblock, config, pindexPrev);
pblock->nNonce = 0;
UniValue aCaps(UniValue::VARR);
aCaps.push_back("proposal");
UniValue transactions(UniValue::VARR);
std::map setTxIndex;
int i = 0;
for (const auto &it : pblock->vtx) {
const CTransaction &tx = *it;
uint256 txId = tx.GetId();
setTxIndex[txId] = i++;
if (tx.IsCoinBase()) {
continue;
}
UniValue entry(UniValue::VOBJ);
entry.push_back(Pair("data", EncodeHexTx(tx)));
entry.push_back(Pair("txid", txId.GetHex()));
entry.push_back(Pair("hash", tx.GetHash().GetHex()));
UniValue deps(UniValue::VARR);
for (const CTxIn &in : tx.vin) {
if (setTxIndex.count(in.prevout.GetTxId())) {
deps.push_back(setTxIndex[in.prevout.GetTxId()]);
}
}
entry.push_back(Pair("depends", deps));
int index_in_template = i - 1;
entry.push_back(
Pair("fee", pblocktemplate->vTxFees[index_in_template] / SATOSHI));
int64_t nTxSigOps = pblocktemplate->vTxSigOpsCount[index_in_template];
entry.push_back(Pair("sigops", nTxSigOps));
transactions.push_back(entry);
}
UniValue aux(UniValue::VOBJ);
aux.push_back(
Pair("flags", HexStr(COINBASE_FLAGS.begin(), COINBASE_FLAGS.end())));
arith_uint256 hashTarget = arith_uint256().SetCompact(pblock->nBits);
UniValue aMutable(UniValue::VARR);
aMutable.push_back("time");
aMutable.push_back("transactions");
aMutable.push_back("prevblock");
UniValue result(UniValue::VOBJ);
result.push_back(Pair("capabilities", aCaps));
result.push_back(Pair("version", pblock->nVersion));
result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex()));
result.push_back(Pair("transactions", transactions));
result.push_back(Pair("coinbaseaux", aux));
result.push_back(Pair("coinbasevalue",
int64_t(pblock->vtx[0]->vout[0].nValue / SATOSHI)));
result.push_back(Pair("longpollid",
chainActive.Tip()->GetBlockHash().GetHex() +
i64tostr(nTransactionsUpdatedLast)));
result.push_back(Pair("target", hashTarget.GetHex()));
result.push_back(
Pair("mintime", int64_t(pindexPrev->GetMedianTimePast()) + 1));
result.push_back(Pair("mutable", aMutable));
result.push_back(Pair("noncerange", "00000000ffffffff"));
// FIXME: Allow for mining block greater than 1M.
result.push_back(
Pair("sigoplimit", GetMaxBlockSigOpsCount(DEFAULT_MAX_BLOCK_SIZE)));
result.push_back(Pair("sizelimit", DEFAULT_MAX_BLOCK_SIZE));
result.push_back(Pair("curtime", pblock->GetBlockTime()));
result.push_back(Pair("bits", strprintf("%08x", pblock->nBits)));
result.push_back(Pair("height", int64_t(pindexPrev->nHeight) + 1));
return result;
}
class submitblock_StateCatcher : public CValidationInterface {
public:
uint256 hash;
bool found;
CValidationState state;
submitblock_StateCatcher(const uint256 &hashIn)
: hash(hashIn), found(false), state() {}
protected:
void BlockChecked(const CBlock &block,
const CValidationState &stateIn) override {
if (block.GetHash() != hash) {
return;
}
found = true;
state = stateIn;
}
};
static UniValue submitblock(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() < 1 ||
request.params.size() > 2) {
throw std::runtime_error(
"submitblock \"hexdata\" ( \"jsonparametersobject\" )\n"
"\nAttempts to submit new block to network.\n"
"The 'jsonparametersobject' parameter is currently ignored.\n"
"See https://en.bitcoin.it/wiki/BIP_0022 for full specification.\n"
"\nArguments\n"
"1. \"hexdata\" (string, required) the hex-encoded block "
"data to submit\n"
"2. \"parameters\" (string, optional) object of optional "
"parameters\n"
" {\n"
" \"workid\" : \"id\" (string, optional) if the server "
"provided a workid, it MUST be included with submissions\n"
" }\n"
"\nResult:\n"
"\nExamples:\n" +
HelpExampleCli("submitblock", "\"mydata\"") +
HelpExampleRpc("submitblock", "\"mydata\""));
}
std::shared_ptr blockptr = std::make_shared();
CBlock &block = *blockptr;
if (!DecodeHexBlk(block, request.params[0].get_str())) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed");
}
if (block.vtx.empty() || !block.vtx[0]->IsCoinBase()) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR,
"Block does not start with a coinbase");
}
uint256 hash = block.GetHash();
bool fBlockPresent = false;
{
LOCK(cs_main);
BlockMap::iterator mi = mapBlockIndex.find(hash);
if (mi != mapBlockIndex.end()) {
CBlockIndex *pindex = mi->second;
if (pindex->IsValid(BlockValidity::SCRIPTS)) {
return "duplicate";
}
if (pindex->nStatus.isInvalid()) {
return "duplicate-invalid";
}
// Otherwise, we might only have the header - process the block
// before returning
fBlockPresent = true;
}
}
submitblock_StateCatcher sc(block.GetHash());
RegisterValidationInterface(&sc);
bool fAccepted = ProcessNewBlock(config, blockptr, true, nullptr);
UnregisterValidationInterface(&sc);
if (fBlockPresent) {
if (fAccepted && !sc.found) {
return "duplicate-inconclusive";
}
return "duplicate";
}
if (!sc.found) {
return "inconclusive";
}
return BIP22ValidationResult(config, sc.state);
}
static UniValue estimatefee(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"estimatefee nblocks\n"
"\nEstimates the approximate fee per kilobyte needed for a "
"transaction to begin\n"
"confirmation within nblocks blocks.\n"
"\nArguments:\n"
"1. nblocks (numeric, required)\n"
"\nResult:\n"
"n (numeric) estimated fee-per-kilobyte\n"
"\n"
"A negative value is returned if not enough transactions and "
"blocks\n"
"have been observed to make an estimate.\n"
"-1 is always returned for nblocks == 1 as it is impossible to "
"calculate\n"
"a fee that is high enough to get reliably included in the next "
"block.\n"
"\nExample:\n" +
HelpExampleCli("estimatefee", "6"));
}
RPCTypeCheck(request.params, {UniValue::VNUM});
int nBlocks = request.params[0].get_int();
if (nBlocks < 1) {
nBlocks = 1;
}
CFeeRate feeRate = mempool.estimateFee(nBlocks);
if (feeRate == CFeeRate(Amount::zero())) {
return -1.0;
}
return ValueFromAmount(feeRate.GetFeePerK());
}
// clang-format off
static const ContextFreeRPCCommand commands[] = {
- // category name actor (function) okSafeMode
+ // category name actor (function) argNames
// ---------- ------------------------ ---------------------- ----------
- {"mining", "getnetworkhashps", getnetworkhashps, true, {"nblocks", "height"}},
- {"mining", "getmininginfo", getmininginfo, true, {}},
- {"mining", "prioritisetransaction", prioritisetransaction, true, {"txid", "priority_delta", "fee_delta"}},
- {"mining", "getblocktemplate", getblocktemplate, true, {"template_request"}},
- {"mining", "submitblock", submitblock, true, {"hexdata", "parameters"}},
+ {"mining", "getnetworkhashps", getnetworkhashps, {"nblocks", "height"}},
+ {"mining", "getmininginfo", getmininginfo, {}},
+ {"mining", "prioritisetransaction", prioritisetransaction, {"txid", "priority_delta", "fee_delta"}},
+ {"mining", "getblocktemplate", getblocktemplate, {"template_request"}},
+ {"mining", "submitblock", submitblock, {"hexdata", "parameters"}},
- {"generating", "generatetoaddress", generatetoaddress, true, {"nblocks", "address", "maxtries"}},
+ {"generating", "generatetoaddress", generatetoaddress, {"nblocks", "address", "maxtries"}},
- {"util", "estimatefee", estimatefee, true, {"nblocks"}},
+ {"util", "estimatefee", estimatefee, {"nblocks"}},
};
// clang-format on
void RegisterMiningRPCCommands(CRPCTable &t) {
for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
t.appendCommand(commands[vcidx].name, &commands[vcidx]);
}
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index ed7aa41f7..f1e4e5206 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -1,697 +1,697 @@
// 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 "rpc/misc.h"
#include "base58.h"
#include "clientversion.h"
#include "config.h"
#include "dstencode.h"
#include "init.h"
#include "net.h"
#include "netbase.h"
#include "rpc/blockchain.h"
#include "rpc/server.h"
#include "timedata.h"
#include "util.h"
#include "utilstrencodings.h"
#include "validation.h"
#ifdef ENABLE_WALLET
#include "wallet/rpcwallet.h"
#include "wallet/wallet.h"
#include "wallet/walletdb.h"
#endif
#include
#include
#ifdef HAVE_MALLOC_INFO
#include
#endif
/**
* @note Do not add or change anything in the information returned by this
* method. `getinfo` exists for backwards-compatibility only. It combines
* information from wildly different sources in the program, which is a mess,
* and is thus planned to be deprecated eventually.
*
* Based on the source of the information, new information should be added to:
* - `getblockchaininfo`,
* - `getnetworkinfo` or
* - `getwalletinfo`
*
* Or alternatively, create a specific query method for the information.
**/
static UniValue getinfo(const Config &config, const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 0) {
throw std::runtime_error(
"getinfo\n"
"\nDEPRECATED. Returns an object containing various state info.\n"
"\nResult:\n"
"{\n"
" \"version\": xxxxx, (numeric) the server version\n"
" \"protocolversion\": xxxxx, (numeric) the protocol version\n"
" \"walletversion\": xxxxx, (numeric) the wallet version\n"
" \"balance\": xxxxxxx, (numeric) the total bitcoin "
"balance of the wallet\n"
" \"blocks\": xxxxxx, (numeric) the current number of "
"blocks processed in the server\n"
" \"timeoffset\": xxxxx, (numeric) the time offset\n"
" \"connections\": xxxxx, (numeric) the number of "
"connections\n"
" \"proxy\": \"host:port\", (string, optional) the proxy used "
"by the server\n"
" \"difficulty\": xxxxxx, (numeric) the current difficulty\n"
" \"testnet\": true|false, (boolean) if the server is using "
"testnet or not\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\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 set "
"in " +
CURRENCY_UNIT + "/kB\n"
" \"relayfee\": x.xxxx, (numeric) minimum "
"relay fee for non-free transactions in " +
CURRENCY_UNIT +
"/kB\n"
" \"errors\": \"...\" (string) any error messages\n"
"}\n"
"\nExamples:\n" +
HelpExampleCli("getinfo", "") + HelpExampleRpc("getinfo", ""));
}
#ifdef ENABLE_WALLET
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
LOCK2(cs_main, pwallet ? &pwallet->cs_wallet : nullptr);
#else
LOCK(cs_main);
#endif
proxyType proxy;
GetProxy(NET_IPV4, proxy);
UniValue obj(UniValue::VOBJ);
obj.push_back(Pair("version", CLIENT_VERSION));
obj.push_back(Pair("protocolversion", PROTOCOL_VERSION));
#ifdef ENABLE_WALLET
if (pwallet) {
obj.push_back(Pair("walletversion", pwallet->GetVersion()));
obj.push_back(Pair("balance", ValueFromAmount(pwallet->GetBalance())));
}
#endif
obj.push_back(Pair("blocks", (int)chainActive.Height()));
obj.push_back(Pair("timeoffset", GetTimeOffset()));
if (g_connman) {
obj.push_back(
Pair("connections",
(int)g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL)));
}
obj.push_back(Pair("proxy", (proxy.IsValid() ? proxy.proxy.ToStringIPPort()
: std::string())));
obj.push_back(Pair("difficulty", double(GetDifficulty(chainActive.Tip()))));
obj.push_back(Pair("testnet",
config.GetChainParams().NetworkIDString() ==
CBaseChainParams::TESTNET));
#ifdef ENABLE_WALLET
if (pwallet) {
obj.push_back(Pair("keypoololdest", pwallet->GetOldestKeyPoolTime()));
obj.push_back(Pair("keypoolsize", (int)pwallet->GetKeyPoolSize()));
}
if (pwallet && pwallet->IsCrypted()) {
obj.push_back(Pair("unlocked_until", pwallet->nRelockTime));
}
obj.push_back(Pair("paytxfee", ValueFromAmount(payTxFee.GetFeePerK())));
#endif
obj.push_back(Pair("relayfee",
ValueFromAmount(config.GetMinFeePerKB().GetFeePerK())));
obj.push_back(Pair("errors", GetWarnings("statusbar")));
return obj;
}
#ifdef ENABLE_WALLET
class DescribeAddressVisitor : public boost::static_visitor {
public:
CWallet *const pwallet;
DescribeAddressVisitor(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;
obj.push_back(Pair("isscript", false));
if (pwallet && pwallet->GetPubKey(keyID, vchPubKey)) {
obj.push_back(Pair("pubkey", HexStr(vchPubKey)));
obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
}
return obj;
}
UniValue operator()(const CScriptID &scriptID) const {
UniValue obj(UniValue::VOBJ);
CScript subscript;
obj.push_back(Pair("isscript", true));
if (pwallet && pwallet->GetCScript(scriptID, subscript)) {
std::vector addresses;
txnouttype whichType;
int nRequired;
ExtractDestinations(subscript, whichType, addresses, nRequired);
obj.push_back(Pair("script", GetTxnOutputType(whichType)));
obj.push_back(
Pair("hex", HexStr(subscript.begin(), subscript.end())));
UniValue a(UniValue::VARR);
for (const CTxDestination &addr : addresses) {
a.push_back(EncodeDestination(addr));
}
obj.push_back(Pair("addresses", a));
if (whichType == TX_MULTISIG) {
obj.push_back(Pair("sigsrequired", nRequired));
}
}
return obj;
}
};
#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"
" \"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"
" \"pubkey\" : \"publickeyhex\", (string) The hex value of the "
"raw public key\n"
" \"iscompressed\" : true|false, (boolean) If the address is "
"compressed\n"
" \"account\" : \"account\" (string) DEPRECATED. 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"
" \"hdmasterkeyid\" : \"\" (string, optional) The "
"Hash160 of the HD master pubkey\n"
"}\n"
"\nExamples:\n" +
HelpExampleCli("validateaddress",
"\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"") +
HelpExampleRpc("validateaddress",
"\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\""));
}
#ifdef ENABLE_WALLET
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
LOCK2(cs_main, pwallet ? &pwallet->cs_wallet : nullptr);
#else
LOCK(cs_main);
#endif
CTxDestination dest =
DecodeDestination(request.params[0].get_str(), config.GetChainParams());
bool isValid = IsValidDestination(dest);
UniValue ret(UniValue::VOBJ);
ret.push_back(Pair("isvalid", isValid));
if (isValid) {
std::string currentAddress = EncodeDestination(dest);
ret.push_back(Pair("address", currentAddress));
CScript scriptPubKey = GetScriptForDestination(dest);
ret.push_back(Pair("scriptPubKey",
HexStr(scriptPubKey.begin(), scriptPubKey.end())));
#ifdef ENABLE_WALLET
isminetype mine = pwallet ? IsMine(*pwallet, dest) : ISMINE_NO;
ret.push_back(Pair("ismine", (mine & ISMINE_SPENDABLE) ? true : false));
ret.push_back(
Pair("iswatchonly", (mine & ISMINE_WATCH_ONLY) ? true : false));
UniValue detail =
boost::apply_visitor(DescribeAddressVisitor(pwallet), dest);
ret.pushKVs(detail);
if (pwallet && pwallet->mapAddressBook.count(dest)) {
ret.push_back(Pair("account", pwallet->mapAddressBook[dest].name));
}
if (pwallet) {
const auto &meta = pwallet->mapKeyMetadata;
const CKeyID *keyID = boost::get(&dest);
auto it = keyID ? meta.find(*keyID) : meta.end();
if (it == meta.end()) {
it = meta.find(CScriptID(scriptPubKey));
}
if (it != meta.end()) {
ret.push_back(Pair("timestamp", it->second.nCreateTime));
if (!it->second.hdKeypath.empty()) {
ret.push_back(Pair("hdkeypath", it->second.hdKeypath));
ret.push_back(Pair("hdmasterkeyid",
it->second.hdMasterKeyID.GetHex()));
}
}
}
#endif
}
return ret;
}
// Needed even with !ENABLE_WALLET, to pass (ignored) pointers around
class CWallet;
/**
* Used by addmultisigaddress / createmultisig:
*/
CScript createmultisig_redeemScript(CWallet *const pwallet,
const UniValue ¶ms) {
int nRequired = params[0].get_int();
const UniValue &keys = params[1].get_array();
// Gather public keys
if (nRequired < 1) {
throw std::runtime_error(
"a multisignature address must require at least one key to redeem");
}
if ((int)keys.size() < nRequired) {
throw std::runtime_error(
strprintf("not enough keys supplied "
"(got %u keys, but need at least %d to redeem)",
keys.size(), nRequired));
}
if (keys.size() > 16) {
throw std::runtime_error(
"Number of addresses involved in the "
"multisignature address creation > 16\nReduce the "
"number");
}
std::vector pubkeys;
pubkeys.resize(keys.size());
for (size_t i = 0; i < keys.size(); i++) {
const std::string &ks = keys[i].get_str();
#ifdef ENABLE_WALLET
// Case 1: Bitcoin address and we have full public key:
if (pwallet) {
CTxDestination dest = DecodeDestination(ks, pwallet->chainParams);
if (IsValidDestination(dest)) {
const CKeyID *keyID = boost::get(&dest);
if (!keyID) {
throw std::runtime_error(
strprintf("%s does not refer to a key", ks));
}
CPubKey vchPubKey;
if (!pwallet->GetPubKey(*keyID, vchPubKey)) {
throw std::runtime_error(
strprintf("no full public key for address %s", ks));
}
if (!vchPubKey.IsFullyValid()) {
throw std::runtime_error(" Invalid public key: " + ks);
}
pubkeys[i] = vchPubKey;
continue;
}
}
#endif
// Case 2: hex public key
if (IsHex(ks)) {
CPubKey vchPubKey(ParseHex(ks));
if (!vchPubKey.IsFullyValid()) {
throw std::runtime_error(" Invalid public key: " + ks);
}
pubkeys[i] = vchPubKey;
} else {
throw std::runtime_error(" Invalid public key: " + ks);
}
}
CScript result = GetScriptForMultisig(nRequired, pubkeys);
if (result.size() > MAX_SCRIPT_ELEMENT_SIZE) {
throw std::runtime_error(
strprintf("redeemScript exceeds size limit: %d > %d", result.size(),
MAX_SCRIPT_ELEMENT_SIZE));
}
return result;
}
static UniValue createmultisig(const Config &config,
const JSONRPCRequest &request) {
#ifdef ENABLE_WALLET
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
#else
CWallet *const pwallet = nullptr;
#endif
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 or addresses.\n"
"2. \"keys\" (string, required) A json array of keys which "
"are bitcoin addresses or hex-encoded public keys\n"
" [\n"
" \"key\" (string) bitcoin address or 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 addresses\n" +
HelpExampleCli("createmultisig",
"2 "
"\"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\","
"\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"") +
"\nAs a json rpc call\n" +
HelpExampleRpc("createmultisig",
"2, "
"\"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\","
"\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"");
throw std::runtime_error(msg);
}
// Construct using pay-to-script-hash:
CScript inner = createmultisig_redeemScript(pwallet, request.params);
CScriptID innerID(inner);
UniValue result(UniValue::VOBJ);
result.push_back(Pair("address", EncodeDestination(innerID)));
result.push_back(Pair("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(&destination);
if (!keyID) {
throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
}
bool fInvalid = false;
std::vector 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();
CBitcoinSecret vchSecret;
bool fGood = vchSecret.SetString(strPrivkey);
if (!fGood) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key");
}
CKey key = vchSecret.GetKey();
if (!key.IsValid()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Private key outside allowed range");
}
CHashWriter ss(SER_GETHASH, 0);
ss << strMessageMagic;
ss << strMessage;
std::vector vchSig;
if (!key.SignCompact(ss.GetHash(), vchSig)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed");
}
return EncodeBase64(&vchSig[0], 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
// IsCurrentForFeeEstimation() and IsInitialBlockDownload().
// TODO: figure out the right way to synchronize around mocktime, and
// ensure all callsites 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.push_back(Pair("used", uint64_t(stats.used)));
obj.push_back(Pair("free", uint64_t(stats.free)));
obj.push_back(Pair("total", uint64_t(stats.total)));
obj.push_back(Pair("locked", uint64_t(stats.locked)));
obj.push_back(Pair("chunks_used", uint64_t(stats.chunks_used)));
obj.push_back(Pair("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"
"\"...\"\n"
"\nExamples:\n" +
HelpExampleCli("getmemoryinfo", "") +
HelpExampleRpc("getmemoryinfo", ""));
}
std::string mode = (request.params.size() < 1 || request.params[0].isNull())
? "stats"
: request.params[0].get_str();
if (mode == "stats") {
UniValue obj(UniValue::VOBJ);
obj.push_back(Pair("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 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;
}
// clang-format off
static const ContextFreeRPCCommand commands[] = {
- // category name actor (function) okSafeMode
+ // category name actor (function) argNames
// ------------------- ------------------------ ---------------------- ----------
- { "control", "getinfo", getinfo, true, {} }, /* uses wallet if enabled */
- { "control", "getmemoryinfo", getmemoryinfo, true, {"mode"} },
- { "util", "validateaddress", validateaddress, true, {"address"} }, /* uses wallet if enabled */
- { "util", "createmultisig", createmultisig, true, {"nrequired","keys"} },
- { "util", "verifymessage", verifymessage, true, {"address","signature","message"} },
- { "util", "signmessagewithprivkey", signmessagewithprivkey, true, {"privkey","message"} },
+ { "control", "getinfo", getinfo, {} }, /* uses wallet if enabled */
+ { "control", "getmemoryinfo", getmemoryinfo, {"mode"} },
+ { "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, true, {"timestamp"}},
- { "hidden", "echo", echo, true, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}},
- { "hidden", "echojson", echo, true, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}},
+ { "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"}},
};
// 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/rpc/net.cpp b/src/rpc/net.cpp
index 7ebe6f935..5b0cfa66b 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -1,802 +1,802 @@
// 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 "rpc/server.h"
#include "chainparams.h"
#include "clientversion.h"
#include "config.h"
#include "net.h"
#include "net_processing.h"
#include "netbase.h"
#include "policy/policy.h"
#include "protocol.h"
#include "sync.h"
#include "timedata.h"
#include "ui_interface.h"
#include "util.h"
#include "utilstrencodings.h"
#include "validation.h"
#include "version.h"
#include
static UniValue getconnectioncount(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 0) {
throw std::runtime_error(
"getconnectioncount\n"
"\nReturns the number of connections to other nodes.\n"
"\nResult:\n"
"n (numeric) The connection count\n"
"\nExamples:\n" +
HelpExampleCli("getconnectioncount", "") +
HelpExampleRpc("getconnectioncount", ""));
}
if (!g_connman) {
throw JSONRPCError(
RPC_CLIENT_P2P_DISABLED,
"Error: Peer-to-peer functionality missing or disabled");
}
return int(g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL));
}
static UniValue ping(const Config &config, const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 0) {
throw std::runtime_error(
"ping\n"
"\nRequests that a ping be sent to all other nodes, to measure "
"ping time.\n"
"Results provided in getpeerinfo, pingtime and pingwait fields are "
"decimal seconds.\n"
"Ping command is handled in queue with all other commands, so it "
"measures processing backlog, not just network ping.\n"
"\nExamples:\n" +
HelpExampleCli("ping", "") + HelpExampleRpc("ping", ""));
}
if (!g_connman) {
throw JSONRPCError(
RPC_CLIENT_P2P_DISABLED,
"Error: Peer-to-peer functionality missing or disabled");
}
// Request that each node send a ping during next message processing pass
g_connman->ForEachNode([](CNode *pnode) { pnode->fPingQueued = true; });
return NullUniValue;
}
static UniValue getpeerinfo(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 0) {
throw std::runtime_error(
"getpeerinfo\n"
"\nReturns data about each connected network node as a json array "
"of objects.\n"
"\nResult:\n"
"[\n"
" {\n"
" \"id\": n, (numeric) Peer index\n"
" \"addr\":\"host:port\", (string) The ip address and port "
"of the peer\n"
" \"addrbind\":\"ip:port\", (string) Bind address of the "
"connection to the peer\n"
" \"addrlocal\":\"ip:port\", (string) Local address as "
"reported by the peer\n"
" \"services\":\"xxxxxxxxxxxxxxxx\", (string) The services "
"offered\n"
" \"relaytxes\":true|false, (boolean) Whether peer has asked "
"us to relay transactions to it\n"
" \"lastsend\": ttt, (numeric) The time in seconds "
"since epoch (Jan 1 1970 GMT) of the last send\n"
" \"lastrecv\": ttt, (numeric) The time in seconds "
"since epoch (Jan 1 1970 GMT) of the last receive\n"
" \"bytessent\": n, (numeric) The total bytes sent\n"
" \"bytesrecv\": n, (numeric) The total bytes "
"received\n"
" \"conntime\": ttt, (numeric) The connection time in "
"seconds since epoch (Jan 1 1970 GMT)\n"
" \"timeoffset\": ttt, (numeric) The time offset in "
"seconds\n"
" \"pingtime\": n, (numeric) ping time (if "
"available)\n"
" \"minping\": n, (numeric) minimum observed ping "
"time (if any at all)\n"
" \"pingwait\": n, (numeric) ping wait (if "
"non-zero)\n"
" \"version\": v, (numeric) The peer version, such "
"as 7001\n"
" \"subver\": \"/Satoshi:0.8.5/\", (string) The string "
"version\n"
" \"inbound\": true|false, (boolean) Inbound (true) or "
"Outbound (false)\n"
" \"addnode\": true|false, (boolean) Whether connection was "
"due to addnode/-connect or if it was an automatic/inbound "
"connection\n"
" \"startingheight\": n, (numeric) The starting height "
"(block) of the peer\n"
" \"banscore\": n, (numeric) The ban score\n"
" \"synced_headers\": n, (numeric) The last header we "
"have in common with this peer\n"
" \"synced_blocks\": n, (numeric) The last block we have "
"in common with this peer\n"
" \"inflight\": [\n"
" n, (numeric) The heights of blocks "
"we're currently asking from this peer\n"
" ...\n"
" ],\n"
" \"whitelisted\": true|false, (boolean) Whether the peer is "
"whitelisted\n"
" \"bytessent_per_msg\": {\n"
" \"addr\": n, (numeric) The total bytes sent "
"aggregated by message type\n"
" ...\n"
" },\n"
" \"bytesrecv_per_msg\": {\n"
" \"addr\": n, (numeric) The total bytes "
"received aggregated by message type\n"
" ...\n"
" }\n"
" }\n"
" ,...\n"
"]\n"
"\nExamples:\n" +
HelpExampleCli("getpeerinfo", "") +
HelpExampleRpc("getpeerinfo", ""));
}
if (!g_connman) {
throw JSONRPCError(
RPC_CLIENT_P2P_DISABLED,
"Error: Peer-to-peer functionality missing or disabled");
}
std::vector vstats;
g_connman->GetNodeStats(vstats);
UniValue ret(UniValue::VARR);
for (const CNodeStats &stats : vstats) {
UniValue obj(UniValue::VOBJ);
CNodeStateStats statestats;
bool fStateStats = GetNodeStateStats(stats.nodeid, statestats);
obj.push_back(Pair("id", stats.nodeid));
obj.push_back(Pair("addr", stats.addrName));
if (!(stats.addrLocal.empty())) {
obj.push_back(Pair("addrlocal", stats.addrLocal));
}
if (stats.addrBind.IsValid()) {
obj.push_back(Pair("addrbind", stats.addrBind.ToString()));
}
obj.push_back(Pair("services", strprintf("%016x", stats.nServices)));
obj.push_back(Pair("relaytxes", stats.fRelayTxes));
obj.push_back(Pair("lastsend", stats.nLastSend));
obj.push_back(Pair("lastrecv", stats.nLastRecv));
obj.push_back(Pair("bytessent", stats.nSendBytes));
obj.push_back(Pair("bytesrecv", stats.nRecvBytes));
obj.push_back(Pair("conntime", stats.nTimeConnected));
obj.push_back(Pair("timeoffset", stats.nTimeOffset));
if (stats.dPingTime > 0.0) {
obj.push_back(Pair("pingtime", stats.dPingTime));
}
if (stats.dMinPing < std::numeric_limits::max() / 1e6) {
obj.push_back(Pair("minping", stats.dMinPing));
}
if (stats.dPingWait > 0.0) {
obj.push_back(Pair("pingwait", stats.dPingWait));
}
obj.push_back(Pair("version", stats.nVersion));
// Use the sanitized form of subver here, to avoid tricksy remote peers
// from corrupting or modifying the JSON output by putting special
// characters in their ver message.
obj.push_back(Pair("subver", stats.cleanSubVer));
obj.push_back(Pair("inbound", stats.fInbound));
obj.push_back(Pair("addnode", stats.m_manual_connection));
obj.push_back(Pair("startingheight", stats.nStartingHeight));
if (fStateStats) {
obj.push_back(Pair("banscore", statestats.nMisbehavior));
obj.push_back(Pair("synced_headers", statestats.nSyncHeight));
obj.push_back(Pair("synced_blocks", statestats.nCommonHeight));
UniValue heights(UniValue::VARR);
for (int height : statestats.vHeightInFlight) {
heights.push_back(height);
}
obj.push_back(Pair("inflight", heights));
}
obj.push_back(Pair("whitelisted", stats.fWhitelisted));
UniValue sendPerMsgCmd(UniValue::VOBJ);
for (const mapMsgCmdSize::value_type &i : stats.mapSendBytesPerMsgCmd) {
if (i.second > 0) {
sendPerMsgCmd.push_back(Pair(i.first, i.second));
}
}
obj.push_back(Pair("bytessent_per_msg", sendPerMsgCmd));
UniValue recvPerMsgCmd(UniValue::VOBJ);
for (const mapMsgCmdSize::value_type &i : stats.mapRecvBytesPerMsgCmd) {
if (i.second > 0) {
recvPerMsgCmd.push_back(Pair(i.first, i.second));
}
}
obj.push_back(Pair("bytesrecv_per_msg", recvPerMsgCmd));
ret.push_back(obj);
}
return ret;
}
static UniValue addnode(const Config &config, const JSONRPCRequest &request) {
std::string strCommand;
if (request.params.size() == 2) {
strCommand = request.params[1].get_str();
}
if (request.fHelp || request.params.size() != 2 ||
(strCommand != "onetry" && strCommand != "add" &&
strCommand != "remove")) {
throw std::runtime_error(
"addnode \"node\" \"add|remove|onetry\"\n"
"\nAttempts add or remove a node from the addnode list.\n"
"Or try a connection to a node once.\n"
"Nodes added using addnode (or -connect) are protected from DoS "
"disconnection and are not required to be\n"
"full nodes/support SegWit as other outbound peers are (though "
"such peers will not be synced from).\n"
"\nArguments:\n"
"1. \"node\" (string, required) The node (see getpeerinfo for "
"nodes)\n"
"2. \"command\" (string, required) 'add' to add a node to the "
"list, 'remove' to remove a node from the list, 'onetry' to try a "
"connection to the node once\n"
"\nExamples:\n" +
HelpExampleCli("addnode", "\"192.168.0.6:8333\" \"onetry\"") +
HelpExampleRpc("addnode", "\"192.168.0.6:8333\", \"onetry\""));
}
if (!g_connman) {
throw JSONRPCError(
RPC_CLIENT_P2P_DISABLED,
"Error: Peer-to-peer functionality missing or disabled");
}
std::string strNode = request.params[0].get_str();
if (strCommand == "onetry") {
CAddress addr;
g_connman->OpenNetworkConnection(addr, false, nullptr, strNode.c_str(),
false, false, true);
return NullUniValue;
}
if ((strCommand == "add") && (!g_connman->AddNode(strNode))) {
throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED,
"Error: Node already added");
} else if ((strCommand == "remove") &&
(!g_connman->RemoveAddedNode(strNode))) {
throw JSONRPCError(RPC_CLIENT_NODE_NOT_ADDED,
"Error: Node has not been added.");
}
return NullUniValue;
}
static UniValue disconnectnode(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() == 0 ||
request.params.size() >= 3) {
throw std::runtime_error(
"disconnectnode \"[address]\" [nodeid]\n"
"\nImmediately disconnects from the specified peer node.\n"
"\nStrictly one out of 'address' and 'nodeid' can be provided to "
"identify the node.\n"
"\nTo disconnect by nodeid, either set 'address' to the empty "
"string, or call using the named 'nodeid' argument only.\n"
"\nArguments:\n"
"1. \"address\" (string, optional) The IP address/port of the "
"node\n"
"2. \"nodeid\" (number, optional) The node ID (see "
"getpeerinfo for node IDs)\n"
"\nExamples:\n" +
HelpExampleCli("disconnectnode", "\"192.168.0.6:8333\"") +
HelpExampleCli("disconnectnode", "\"\" 1") +
HelpExampleRpc("disconnectnode", "\"192.168.0.6:8333\"") +
HelpExampleRpc("disconnectnode", "\"\", 1"));
}
if (!g_connman) {
throw JSONRPCError(
RPC_CLIENT_P2P_DISABLED,
"Error: Peer-to-peer functionality missing or disabled");
}
bool success;
const UniValue &address_arg = request.params[0];
const UniValue &id_arg =
request.params.size() < 2 ? NullUniValue : request.params[1];
if (!address_arg.isNull() && id_arg.isNull()) {
/* handle disconnect-by-address */
success = g_connman->DisconnectNode(address_arg.get_str());
} else if (!id_arg.isNull() &&
(address_arg.isNull() ||
(address_arg.isStr() && address_arg.get_str().empty()))) {
/* handle disconnect-by-id */
NodeId nodeid = (NodeId)id_arg.get_int64();
success = g_connman->DisconnectNode(nodeid);
} else {
throw JSONRPCError(
RPC_INVALID_PARAMS,
"Only one of address and nodeid should be provided.");
}
if (!success) {
throw JSONRPCError(RPC_CLIENT_NODE_NOT_CONNECTED,
"Node not found in connected nodes");
}
return NullUniValue;
}
static UniValue getaddednodeinfo(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() > 1) {
throw std::runtime_error(
"getaddednodeinfo ( \"node\" )\n"
"\nReturns information about the given added node, or all added "
"nodes\n"
"(note that onetry addnodes are not listed here)\n"
"\nArguments:\n"
"1. \"node\" (string, optional) If provided, return information "
"about this specific node, otherwise all nodes are returned.\n"
"\nResult:\n"
"[\n"
" {\n"
" \"addednode\" : \"192.168.0.201\", (string) The node ip "
"address or name (as provided to addnode)\n"
" \"connected\" : true|false, (boolean) If connected\n"
" \"addresses\" : [ (list of objects) Only "
"when connected = true\n"
" {\n"
" \"address\" : \"192.168.0.201:8333\", (string) The "
"bitcoin server IP and port we're connected to\n"
" \"connected\" : \"outbound\" (string) "
"connection, inbound or outbound\n"
" }\n"
" ]\n"
" }\n"
" ,...\n"
"]\n"
"\nExamples:\n" +
HelpExampleCli("getaddednodeinfo", "\"192.168.0.201\"") +
HelpExampleRpc("getaddednodeinfo", "\"192.168.0.201\""));
}
if (!g_connman) {
throw JSONRPCError(
RPC_CLIENT_P2P_DISABLED,
"Error: Peer-to-peer functionality missing or disabled");
}
std::vector vInfo = g_connman->GetAddedNodeInfo();
if (request.params.size() == 1) {
bool found = false;
for (const AddedNodeInfo &info : vInfo) {
if (info.strAddedNode == request.params[0].get_str()) {
vInfo.assign(1, info);
found = true;
break;
}
}
if (!found) {
throw JSONRPCError(RPC_CLIENT_NODE_NOT_ADDED,
"Error: Node has not been added.");
}
}
UniValue ret(UniValue::VARR);
for (const AddedNodeInfo &info : vInfo) {
UniValue obj(UniValue::VOBJ);
obj.push_back(Pair("addednode", info.strAddedNode));
obj.push_back(Pair("connected", info.fConnected));
UniValue addresses(UniValue::VARR);
if (info.fConnected) {
UniValue address(UniValue::VOBJ);
address.push_back(Pair("address", info.resolvedAddress.ToString()));
address.push_back(
Pair("connected", info.fInbound ? "inbound" : "outbound"));
addresses.push_back(address);
}
obj.push_back(Pair("addresses", addresses));
ret.push_back(obj);
}
return ret;
}
static UniValue getnettotals(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() > 0) {
throw std::runtime_error(
"getnettotals\n"
"\nReturns information about network traffic, including bytes in, "
"bytes out,\n"
"and current time.\n"
"\nResult:\n"
"{\n"
" \"totalbytesrecv\": n, (numeric) Total bytes received\n"
" \"totalbytessent\": n, (numeric) Total bytes sent\n"
" \"timemillis\": t, (numeric) Current UNIX time in "
"milliseconds\n"
" \"uploadtarget\":\n"
" {\n"
" \"timeframe\": n, (numeric) Length of "
"the measuring timeframe in seconds\n"
" \"target\": n, (numeric) Target in "
"bytes\n"
" \"target_reached\": true|false, (boolean) True if "
"target is reached\n"
" \"serve_historical_blocks\": true|false, (boolean) True if "
"serving historical blocks\n"
" \"bytes_left_in_cycle\": t, (numeric) Bytes "
"left in current time cycle\n"
" \"time_left_in_cycle\": t (numeric) Seconds "
"left in current time cycle\n"
" }\n"
"}\n"
"\nExamples:\n" +
HelpExampleCli("getnettotals", "") +
HelpExampleRpc("getnettotals", ""));
}
if (!g_connman) {
throw JSONRPCError(
RPC_CLIENT_P2P_DISABLED,
"Error: Peer-to-peer functionality missing or disabled");
}
UniValue obj(UniValue::VOBJ);
obj.push_back(Pair("totalbytesrecv", g_connman->GetTotalBytesRecv()));
obj.push_back(Pair("totalbytessent", g_connman->GetTotalBytesSent()));
obj.push_back(Pair("timemillis", GetTimeMillis()));
UniValue outboundLimit(UniValue::VOBJ);
outboundLimit.push_back(
Pair("timeframe", g_connman->GetMaxOutboundTimeframe()));
outboundLimit.push_back(Pair("target", g_connman->GetMaxOutboundTarget()));
outboundLimit.push_back(
Pair("target_reached", g_connman->OutboundTargetReached(false)));
outboundLimit.push_back(Pair("serve_historical_blocks",
!g_connman->OutboundTargetReached(true)));
outboundLimit.push_back(
Pair("bytes_left_in_cycle", g_connman->GetOutboundTargetBytesLeft()));
outboundLimit.push_back(
Pair("time_left_in_cycle", g_connman->GetMaxOutboundTimeLeftInCycle()));
obj.push_back(Pair("uploadtarget", outboundLimit));
return obj;
}
static UniValue GetNetworksInfo() {
UniValue networks(UniValue::VARR);
for (int n = 0; n < NET_MAX; ++n) {
enum Network network = static_cast(n);
if (network == NET_UNROUTABLE || network == NET_INTERNAL) {
continue;
}
proxyType proxy;
UniValue obj(UniValue::VOBJ);
GetProxy(network, proxy);
obj.push_back(Pair("name", GetNetworkName(network)));
obj.push_back(Pair("limited", IsLimited(network)));
obj.push_back(Pair("reachable", IsReachable(network)));
obj.push_back(Pair("proxy",
proxy.IsValid() ? proxy.proxy.ToStringIPPort()
: std::string()));
obj.push_back(
Pair("proxy_randomize_credentials", proxy.randomize_credentials));
networks.push_back(obj);
}
return networks;
}
static UniValue getnetworkinfo(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 0) {
throw std::runtime_error(
"getnetworkinfo\n"
"Returns an object containing various state info regarding P2P "
"networking.\n"
"\nResult:\n"
"{\n"
" \"version\": xxxxx, (numeric) the server "
"version\n"
" \"subversion\": \"/Satoshi:x.x.x/\", (string) the server "
"subversion string\n"
" \"protocolversion\": xxxxx, (numeric) the protocol "
"version\n"
" \"localservices\": \"xxxxxxxxxxxxxxxx\", (string) the services "
"we offer to the network\n"
" \"localrelay\": true|false, (bool) true if "
"transaction relay is requested from peers\n"
" \"timeoffset\": xxxxx, (numeric) the time "
"offset\n"
" \"connections\": xxxxx, (numeric) the number "
"of connections\n"
" \"networkactive\": true|false, (bool) whether p2p "
"networking is enabled\n"
" \"networks\": [ (array) information "
"per network\n"
" {\n"
" \"name\": \"xxx\", (string) network "
"(ipv4, ipv6 or onion)\n"
" \"limited\": true|false, (boolean) is the "
"network limited using -onlynet?\n"
" \"reachable\": true|false, (boolean) is the "
"network reachable?\n"
" \"proxy\": \"host:port\" (string) the proxy "
"that is used for this network, or empty if none\n"
" \"proxy_randomize_credentials\": true|false, (string) "
"Whether randomized credentials are used\n"
" }\n"
" ,...\n"
" ],\n"
" \"relayfee\": x.xxxxxxxx, (numeric) minimum "
"relay fee for non-free transactions in " +
CURRENCY_UNIT +
"/kB\n"
" \"excessutxocharge\": x.xxxxxxxx, (numeric) minimum "
"charge for excess utxos in " +
CURRENCY_UNIT + "\n"
" \"localaddresses\": [ "
"(array) list of local addresses\n"
" {\n"
" \"address\": \"xxxx\", "
"(string) network address\n"
" \"port\": xxx, "
"(numeric) network port\n"
" \"score\": xxx "
"(numeric) relative score\n"
" }\n"
" ,...\n"
" ]\n"
" \"warnings\": \"...\" "
"(string) any network warnings\n"
"}\n"
"\nExamples:\n" +
HelpExampleCli("getnetworkinfo", "") +
HelpExampleRpc("getnetworkinfo", ""));
}
LOCK(cs_main);
UniValue obj(UniValue::VOBJ);
obj.push_back(Pair("version", CLIENT_VERSION));
obj.push_back(Pair("subversion", userAgent(config)));
obj.push_back(Pair("protocolversion", PROTOCOL_VERSION));
if (g_connman) {
obj.push_back(Pair("localservices",
strprintf("%016x", g_connman->GetLocalServices())));
}
obj.push_back(Pair("localrelay", fRelayTxes));
obj.push_back(Pair("timeoffset", GetTimeOffset()));
if (g_connman) {
obj.push_back(Pair("networkactive", g_connman->GetNetworkActive()));
obj.push_back(Pair("connections", int(g_connman->GetNodeCount(
CConnman::CONNECTIONS_ALL))));
}
obj.push_back(Pair("networks", GetNetworksInfo()));
obj.push_back(Pair("relayfee",
ValueFromAmount(config.GetMinFeePerKB().GetFeePerK())));
obj.push_back(Pair("excessutxocharge",
ValueFromAmount(config.GetExcessUTXOCharge())));
UniValue localAddresses(UniValue::VARR);
{
LOCK(cs_mapLocalHost);
for (const std::pair &item : mapLocalHost) {
UniValue rec(UniValue::VOBJ);
rec.push_back(Pair("address", item.first.ToString()));
rec.push_back(Pair("port", item.second.nPort));
rec.push_back(Pair("score", item.second.nScore));
localAddresses.push_back(rec);
}
}
obj.push_back(Pair("localaddresses", localAddresses));
obj.push_back(Pair("warnings", GetWarnings("statusbar")));
return obj;
}
static UniValue setban(const Config &config, const JSONRPCRequest &request) {
std::string strCommand;
if (request.params.size() >= 2) {
strCommand = request.params[1].get_str();
}
if (request.fHelp || request.params.size() < 2 ||
(strCommand != "add" && strCommand != "remove")) {
throw std::runtime_error(
"setban \"subnet\" \"add|remove\" (bantime) (absolute)\n"
"\nAttempts add or remove a IP/Subnet from the banned list.\n"
"\nArguments:\n"
"1. \"subnet\" (string, required) The IP/Subnet (see "
"getpeerinfo for nodes ip) with a optional netmask (default is /32 "
"= single ip)\n"
"2. \"command\" (string, required) 'add' to add a IP/Subnet "
"to the list, 'remove' to remove a IP/Subnet from the list\n"
"3. \"bantime\" (numeric, optional) time in seconds how long "
"(or until when if [absolute] is set) the ip is banned (0 or empty "
"means using the default time of 24h which can also be overwritten "
"by the -bantime startup argument)\n"
"4. \"absolute\" (boolean, optional) If set, the bantime must "
"be a absolute timestamp in seconds since epoch (Jan 1 1970 GMT)\n"
"\nExamples:\n" +
HelpExampleCli("setban", "\"192.168.0.6\" \"add\" 86400") +
HelpExampleCli("setban", "\"192.168.0.0/24\" \"add\"") +
HelpExampleRpc("setban", "\"192.168.0.6\", \"add\", 86400"));
}
if (!g_connman) {
throw JSONRPCError(
RPC_CLIENT_P2P_DISABLED,
"Error: Peer-to-peer functionality missing or disabled");
}
CSubNet subNet;
CNetAddr netAddr;
bool isSubnet = false;
if (request.params[0].get_str().find("/") != std::string::npos) {
isSubnet = true;
}
if (!isSubnet) {
CNetAddr resolved;
LookupHost(request.params[0].get_str().c_str(), resolved, false);
netAddr = resolved;
} else {
LookupSubNet(request.params[0].get_str().c_str(), subNet);
}
if (!(isSubnet ? subNet.IsValid() : netAddr.IsValid())) {
throw JSONRPCError(RPC_CLIENT_INVALID_IP_OR_SUBNET,
"Error: Invalid IP/Subnet");
}
if (strCommand == "add") {
if (isSubnet ? g_connman->IsBanned(subNet)
: g_connman->IsBanned(netAddr)) {
throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED,
"Error: IP/Subnet already banned");
}
// Use standard bantime if not specified.
int64_t banTime = 0;
if (request.params.size() >= 3 && !request.params[2].isNull()) {
banTime = request.params[2].get_int64();
}
bool absolute = false;
if (request.params.size() == 4 && request.params[3].isTrue()) {
absolute = true;
}
isSubnet
? g_connman->Ban(subNet, BanReasonManuallyAdded, banTime, absolute)
: g_connman->Ban(netAddr, BanReasonManuallyAdded, banTime,
absolute);
} else if (strCommand == "remove") {
if (!(isSubnet ? g_connman->Unban(subNet)
: g_connman->Unban(netAddr))) {
throw JSONRPCError(RPC_CLIENT_INVALID_IP_OR_SUBNET,
"Error: Unban failed. Requested address/subnet "
"was not previously banned.");
}
}
return NullUniValue;
}
static UniValue listbanned(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 0) {
throw std::runtime_error("listbanned\n"
"\nList all banned IPs/Subnets.\n"
"\nExamples:\n" +
HelpExampleCli("listbanned", "") +
HelpExampleRpc("listbanned", ""));
}
if (!g_connman) {
throw JSONRPCError(
RPC_CLIENT_P2P_DISABLED,
"Error: Peer-to-peer functionality missing or disabled");
}
banmap_t banMap;
g_connman->GetBanned(banMap);
UniValue bannedAddresses(UniValue::VARR);
for (banmap_t::iterator it = banMap.begin(); it != banMap.end(); it++) {
CBanEntry banEntry = (*it).second;
UniValue rec(UniValue::VOBJ);
rec.push_back(Pair("address", (*it).first.ToString()));
rec.push_back(Pair("banned_until", banEntry.nBanUntil));
rec.push_back(Pair("ban_created", banEntry.nCreateTime));
rec.push_back(Pair("ban_reason", banEntry.banReasonToString()));
bannedAddresses.push_back(rec);
}
return bannedAddresses;
}
static UniValue clearbanned(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 0) {
throw std::runtime_error("clearbanned\n"
"\nClear all banned IPs.\n"
"\nExamples:\n" +
HelpExampleCli("clearbanned", "") +
HelpExampleRpc("clearbanned", ""));
}
if (!g_connman) {
throw JSONRPCError(
RPC_CLIENT_P2P_DISABLED,
"Error: Peer-to-peer functionality missing or disabled");
}
g_connman->ClearBanned();
return NullUniValue;
}
static UniValue setnetworkactive(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"setnetworkactive true|false\n"
"\nDisable/enable all p2p network activity.\n"
"\nArguments:\n"
"1. \"state\" (boolean, required) true to "
"enable networking, false to disable\n");
}
if (!g_connman) {
throw JSONRPCError(
RPC_CLIENT_P2P_DISABLED,
"Error: Peer-to-peer functionality missing or disabled");
}
g_connman->SetNetworkActive(request.params[0].get_bool());
return g_connman->GetNetworkActive();
}
// clang-format off
static const ContextFreeRPCCommand commands[] = {
- // category name actor (function) okSafeMode
+ // category name actor (function) argNames
// ------------------- ------------------------ ---------------------- ----------
- { "network", "getconnectioncount", getconnectioncount, true, {} },
- { "network", "ping", ping, true, {} },
- { "network", "getpeerinfo", getpeerinfo, true, {} },
- { "network", "addnode", addnode, true, {"node","command"} },
- { "network", "disconnectnode", disconnectnode, true, {"address", "nodeid"} },
- { "network", "getaddednodeinfo", getaddednodeinfo, true, {"node"} },
- { "network", "getnettotals", getnettotals, true, {} },
- { "network", "getnetworkinfo", getnetworkinfo, true, {} },
- { "network", "setban", setban, true, {"subnet", "command", "bantime", "absolute"} },
- { "network", "listbanned", listbanned, true, {} },
- { "network", "clearbanned", clearbanned, true, {} },
- { "network", "setnetworkactive", setnetworkactive, true, {"state"} },
+ { "network", "getconnectioncount", getconnectioncount, {} },
+ { "network", "ping", ping, {} },
+ { "network", "getpeerinfo", getpeerinfo, {} },
+ { "network", "addnode", addnode, {"node","command"} },
+ { "network", "disconnectnode", disconnectnode, {"address", "nodeid"} },
+ { "network", "getaddednodeinfo", getaddednodeinfo, {"node"} },
+ { "network", "getnettotals", getnettotals, {} },
+ { "network", "getnetworkinfo", getnetworkinfo, {} },
+ { "network", "setban", setban, {"subnet", "command", "bantime", "absolute"} },
+ { "network", "listbanned", listbanned, {} },
+ { "network", "clearbanned", clearbanned, {} },
+ { "network", "setnetworkactive", setnetworkactive, {"state"} },
};
// clang-format on
void RegisterNetRPCCommands(CRPCTable &t) {
for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) {
t.appendCommand(commands[vcidx].name, &commands[vcidx]);
}
}
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 3f0925df9..7f46ce8e1 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -1,1202 +1,1205 @@
// 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 "base58.h"
#include "chain.h"
#include "coins.h"
#include "config.h"
#include "consensus/validation.h"
#include "core_io.h"
#include "dstencode.h"
#include "init.h"
#include "keystore.h"
#include "merkleblock.h"
#include "net.h"
#include "policy/policy.h"
#include "primitives/transaction.h"
+#include "rpc/safemode.h"
#include "rpc/server.h"
#include "rpc/tojson.h"
#include "script/script.h"
#include "script/script_error.h"
#include "script/sign.h"
#include "script/standard.h"
#include "txmempool.h"
#include "uint256.h"
#include "utilstrencodings.h"
#include "validation.h"
#ifdef ENABLE_WALLET
#include "wallet/rpcwallet.h"
#include "wallet/wallet.h"
#endif
#include
#include
void ScriptPubKeyToJSON(const Config &config, const CScript &scriptPubKey,
UniValue &out, bool fIncludeHex) {
txnouttype type;
std::vector addresses;
int nRequired;
out.push_back(Pair("asm", ScriptToAsmStr(scriptPubKey)));
if (fIncludeHex) {
out.push_back(
Pair("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end())));
}
if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired)) {
out.push_back(Pair("type", GetTxnOutputType(type)));
return;
}
out.push_back(Pair("reqSigs", nRequired));
out.push_back(Pair("type", GetTxnOutputType(type)));
UniValue a(UniValue::VARR);
for (const CTxDestination &addr : addresses) {
a.push_back(EncodeDestination(addr));
}
out.push_back(Pair("addresses", a));
}
void TxToJSON(const Config &config, const CTransaction &tx,
const uint256 hashBlock, UniValue &entry) {
entry.push_back(Pair("txid", tx.GetId().GetHex()));
entry.push_back(Pair("hash", tx.GetHash().GetHex()));
entry.push_back(Pair(
"size", int(::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION))));
entry.push_back(Pair("version", tx.nVersion));
entry.push_back(Pair("locktime", int64_t(tx.nLockTime)));
UniValue vin(UniValue::VARR);
for (unsigned int i = 0; i < tx.vin.size(); i++) {
const CTxIn &txin = tx.vin[i];
UniValue in(UniValue::VOBJ);
if (tx.IsCoinBase()) {
in.push_back(Pair("coinbase", HexStr(txin.scriptSig.begin(),
txin.scriptSig.end())));
} else {
in.push_back(Pair("txid", txin.prevout.GetTxId().GetHex()));
in.push_back(Pair("vout", int64_t(txin.prevout.GetN())));
UniValue o(UniValue::VOBJ);
o.push_back(Pair("asm", ScriptToAsmStr(txin.scriptSig, true)));
o.push_back(Pair(
"hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())));
in.push_back(Pair("scriptSig", o));
}
in.push_back(Pair("sequence", int64_t(txin.nSequence)));
vin.push_back(in);
}
entry.push_back(Pair("vin", vin));
UniValue vout(UniValue::VARR);
for (unsigned int i = 0; i < tx.vout.size(); i++) {
const CTxOut &txout = tx.vout[i];
UniValue out(UniValue::VOBJ);
out.push_back(Pair("value", ValueFromAmount(txout.nValue)));
out.push_back(Pair("n", int64_t(i)));
UniValue o(UniValue::VOBJ);
ScriptPubKeyToJSON(config, txout.scriptPubKey, o, true);
out.push_back(Pair("scriptPubKey", o));
vout.push_back(out);
}
entry.push_back(Pair("vout", vout));
if (!hashBlock.IsNull()) {
entry.push_back(Pair("blockhash", hashBlock.GetHex()));
BlockMap::iterator mi = mapBlockIndex.find(hashBlock);
if (mi != mapBlockIndex.end() && (*mi).second) {
CBlockIndex *pindex = (*mi).second;
if (chainActive.Contains(pindex)) {
entry.push_back(
Pair("confirmations",
1 + chainActive.Height() - pindex->nHeight));
entry.push_back(Pair("time", pindex->GetBlockTime()));
entry.push_back(Pair("blocktime", pindex->GetBlockTime()));
} else {
entry.push_back(Pair("confirmations", 0));
}
}
}
}
static UniValue getrawtransaction(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() < 1 ||
request.params.size() > 2) {
throw std::runtime_error(
"getrawtransaction \"txid\" ( verbose )\n"
"\nNOTE: By default this function only works for mempool "
"transactions. If the -txindex option is\n"
"enabled, it also works for blockchain transactions.\n"
"DEPRECATED: for now, it also works for transactions with unspent "
"outputs.\n"
"\nReturn the raw transaction data.\n"
"\nIf verbose is 'true', returns an Object with information about "
"'txid'.\n"
"If verbose is 'false' or omitted, returns a string that is "
"serialized, hex-encoded data for 'txid'.\n"
"\nArguments:\n"
"1. \"txid\" (string, required) The transaction id\n"
"2. verbose (bool, optional, default=false) If false, return "
"a string, otherwise return a json object\n"
"\nResult (if verbose is not set or set to false):\n"
"\"data\" (string) The serialized, hex-encoded data for "
"'txid'\n"
"\nResult (if verbose is set to true):\n"
"{\n"
" \"hex\" : \"data\", (string) The serialized, hex-encoded "
"data for 'txid'\n"
" \"txid\" : \"id\", (string) The transaction id (same as "
"provided)\n"
" \"hash\" : \"id\", (string) The transaction hash "
"(differs from txid for witness transactions)\n"
" \"size\" : n, (numeric) The serialized transaction "
"size\n"
" \"version\" : n, (numeric) The version\n"
" \"locktime\" : ttt, (numeric) The lock time\n"
" \"vin\" : [ (array of json objects)\n"
" {\n"
" \"txid\": \"id\", (string) The transaction id\n"
" \"vout\": n, (numeric) \n"
" \"scriptSig\": { (json object) The script\n"
" \"asm\": \"asm\", (string) asm\n"
" \"hex\": \"hex\" (string) hex\n"
" },\n"
" \"sequence\": n (numeric) The script sequence number\n"
" }\n"
" ,...\n"
" ],\n"
" \"vout\" : [ (array of json objects)\n"
" {\n"
" \"value\" : x.xxx, (numeric) The value in " +
CURRENCY_UNIT +
"\n"
" \"n\" : n, (numeric) index\n"
" \"scriptPubKey\" : { (json object)\n"
" \"asm\" : \"asm\", (string) the asm\n"
" \"hex\" : \"hex\", (string) the hex\n"
" \"reqSigs\" : n, (numeric) The required sigs\n"
" \"type\" : \"pubkeyhash\", (string) The type, eg "
"'pubkeyhash'\n"
" \"addresses\" : [ (json array of string)\n"
" \"address\" (string) bitcoin address\n"
" ,...\n"
" ]\n"
" }\n"
" }\n"
" ,...\n"
" ],\n"
" \"blockhash\" : \"hash\", (string) the block hash\n"
" \"confirmations\" : n, (numeric) The confirmations\n"
" \"time\" : ttt, (numeric) The transaction time in "
"seconds since epoch (Jan 1 1970 GMT)\n"
" \"blocktime\" : ttt (numeric) The block time in seconds "
"since epoch (Jan 1 1970 GMT)\n"
"}\n"
"\nExamples:\n" +
HelpExampleCli("getrawtransaction", "\"mytxid\"") +
HelpExampleCli("getrawtransaction", "\"mytxid\" true") +
HelpExampleRpc("getrawtransaction", "\"mytxid\", true"));
}
LOCK(cs_main);
TxId txid = TxId(ParseHashV(request.params[0], "parameter 1"));
// Accept either a bool (true) or a num (>=1) to indicate verbose output.
bool fVerbose = false;
if (request.params.size() > 1) {
if (request.params[1].isNum()) {
if (request.params[1].get_int() != 0) {
fVerbose = true;
}
} else if (request.params[1].isBool()) {
if (request.params[1].isTrue()) {
fVerbose = true;
}
} else {
throw JSONRPCError(
RPC_TYPE_ERROR,
"Invalid type provided. Verbose parameter must be a boolean.");
}
}
CTransactionRef tx;
uint256 hashBlock;
if (!GetTransaction(config, txid, tx, hashBlock, true)) {
throw JSONRPCError(
RPC_INVALID_ADDRESS_OR_KEY,
std::string(fTxIndex ? "No such mempool or blockchain transaction"
: "No such mempool transaction. Use -txindex "
"to enable blockchain transaction queries") +
". Use gettransaction for wallet transactions.");
}
std::string strHex = EncodeHexTx(*tx, RPCSerializationFlags());
if (!fVerbose) {
return strHex;
}
UniValue result(UniValue::VOBJ);
result.push_back(Pair("hex", strHex));
TxToJSON(config, *tx, hashBlock, result);
return result;
}
static UniValue gettxoutproof(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp ||
(request.params.size() != 1 && request.params.size() != 2)) {
throw std::runtime_error(
"gettxoutproof [\"txid\",...] ( blockhash )\n"
"\nReturns a hex-encoded proof that \"txid\" was included in a "
"block.\n"
"\nNOTE: By default this function only works sometimes. This is "
"when there is an\n"
"unspent output in the utxo for this transaction. To make it "
"always work,\n"
"you need to maintain a transaction index, using the -txindex "
"command line option or\n"
"specify the block in which the transaction is included manually "
"(by blockhash).\n"
"\nArguments:\n"
"1. \"txids\" (string) A json array of txids to filter\n"
" [\n"
" \"txid\" (string) A transaction hash\n"
" ,...\n"
" ]\n"
"2. \"blockhash\" (string, optional) If specified, looks for "
"txid in the block with this hash\n"
"\nResult:\n"
"\"data\" (string) A string that is a serialized, "
"hex-encoded data for the proof.\n");
}
std::set setTxIds;
TxId oneTxId;
UniValue txids = request.params[0].get_array();
for (unsigned int idx = 0; idx < txids.size(); idx++) {
const UniValue &utxid = txids[idx];
if (utxid.get_str().length() != 64 || !IsHex(utxid.get_str())) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
std::string("Invalid txid ") + utxid.get_str());
}
TxId txid(uint256S(utxid.get_str()));
if (setTxIds.count(txid)) {
throw JSONRPCError(
RPC_INVALID_PARAMETER,
std::string("Invalid parameter, duplicated txid: ") +
utxid.get_str());
}
setTxIds.insert(txid);
oneTxId = txid;
}
LOCK(cs_main);
CBlockIndex *pblockindex = nullptr;
uint256 hashBlock;
if (request.params.size() > 1) {
hashBlock = uint256S(request.params[1].get_str());
if (!mapBlockIndex.count(hashBlock)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
pblockindex = mapBlockIndex[hashBlock];
} else {
// Loop through txids and try to find which block they're in. Exit loop
// once a block is found.
for (const auto &txid : setTxIds) {
const Coin &coin = AccessByTxid(*pcoinsTip, txid);
if (!coin.IsSpent()) {
pblockindex = chainActive[coin.GetHeight()];
break;
}
}
}
if (pblockindex == nullptr) {
CTransactionRef tx;
if (!GetTransaction(config, oneTxId, tx, hashBlock, false) ||
hashBlock.IsNull()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Transaction not yet in block");
}
if (!mapBlockIndex.count(hashBlock)) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Transaction index corrupt");
}
pblockindex = mapBlockIndex[hashBlock];
}
CBlock block;
if (!ReadBlockFromDisk(block, pblockindex, config)) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk");
}
unsigned int ntxFound = 0;
for (const auto &tx : block.vtx) {
if (setTxIds.count(tx->GetId())) {
ntxFound++;
}
}
if (ntxFound != setTxIds.size()) {
throw JSONRPCError(
RPC_INVALID_ADDRESS_OR_KEY,
"Not all transactions found in specified or retrieved block");
}
CDataStream ssMB(SER_NETWORK, PROTOCOL_VERSION);
CMerkleBlock mb(block, setTxIds);
ssMB << mb;
std::string strHex = HexStr(ssMB.begin(), ssMB.end());
return strHex;
}
static UniValue verifytxoutproof(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"verifytxoutproof \"proof\"\n"
"\nVerifies that a proof points to a transaction in a block, "
"returning the transaction it commits to\n"
"and throwing an RPC error if the block is not in our best chain\n"
"\nArguments:\n"
"1. \"proof\" (string, required) The hex-encoded proof "
"generated by gettxoutproof\n"
"\nResult:\n"
"[\"txid\"] (array, strings) The txid(s) which the proof "
"commits to, or empty array if the proof is invalid\n");
}
CDataStream ssMB(ParseHexV(request.params[0], "proof"), SER_NETWORK,
PROTOCOL_VERSION);
CMerkleBlock merkleBlock;
ssMB >> merkleBlock;
UniValue res(UniValue::VARR);
std::vector vMatch;
std::vector vIndex;
if (merkleBlock.txn.ExtractMatches(vMatch, vIndex) !=
merkleBlock.header.hashMerkleRoot) {
return res;
}
LOCK(cs_main);
if (!mapBlockIndex.count(merkleBlock.header.GetHash()) ||
!chainActive.Contains(mapBlockIndex[merkleBlock.header.GetHash()])) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Block not found in chain");
}
for (const uint256 &hash : vMatch) {
res.push_back(hash.GetHex());
}
return res;
}
static UniValue createrawtransaction(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() < 2 ||
request.params.size() > 3) {
throw std::runtime_error(
"createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] "
"{\"address\":amount,\"data\":\"hex\",...} ( locktime )\n"
"\nCreate a transaction spending the given inputs and creating new "
"outputs.\n"
"Outputs can be addresses or data.\n"
"Returns hex-encoded raw transaction.\n"
"Note that the transaction's inputs are not signed, and\n"
"it is not stored in the wallet or transmitted to the network.\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\" (object, required) a json object "
"with outputs\n"
" {\n"
" \"address\": x.xxx, (numeric or string, required) The "
"key is the bitcoin address, the numeric value (can be string) is "
"the " +
CURRENCY_UNIT +
" amount\n"
" \"data\": \"hex\" (string, required) The key is "
"\"data\", the value is hex encoded data\n"
" ,...\n"
" }\n"
"3. locktime (numeric, optional, default=0) Raw "
"locktime. Non-0 value also locktime-activates inputs\n"
"\nResult:\n"
"\"transaction\" (string) hex string of the "
"transaction\n"
"\nExamples:\n" +
HelpExampleCli("createrawtransaction",
"\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" "
"\"{\\\"address\\\":0.01}\"") +
HelpExampleCli("createrawtransaction",
"\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" "
"\"{\\\"data\\\":\\\"00010203\\\"}\"") +
HelpExampleRpc("createrawtransaction",
"\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", "
"\"{\\\"address\\\":0.01}\"") +
HelpExampleRpc("createrawtransaction",
"\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", "
"\"{\\\"data\\\":\\\"00010203\\\"}\""));
}
RPCTypeCheck(request.params,
{UniValue::VARR, UniValue::VOBJ, UniValue::VNUM}, true);
if (request.params[0].isNull() || request.params[1].isNull()) {
throw JSONRPCError(
RPC_INVALID_PARAMETER,
"Invalid parameter, arguments 1 and 2 must be non-null");
}
UniValue inputs = request.params[0].get_array();
UniValue sendTo = request.params[1].get_obj();
CMutableTransaction rawTx;
if (request.params.size() > 2 && !request.params[2].isNull()) {
int64_t nLockTime = request.params[2].get_int64();
if (nLockTime < 0 || nLockTime > std::numeric_limits::max()) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Invalid parameter, locktime out of range");
}
rawTx.nLockTime = nLockTime;
}
for (size_t idx = 0; idx < inputs.size(); idx++) {
const UniValue &input = inputs[idx];
const UniValue &o = input.get_obj();
uint256 txid = ParseHashO(o, "txid");
const UniValue &vout_v = find_value(o, "vout");
if (vout_v.isNull()) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Invalid parameter, missing vout key");
}
if (!vout_v.isNum()) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Invalid parameter, vout must be a number");
}
int nOutput = vout_v.get_int();
if (nOutput < 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Invalid parameter, vout must be positive");
}
uint32_t nSequence =
(rawTx.nLockTime ? std::numeric_limits::max() - 1
: std::numeric_limits::max());
// Set the sequence number if passed in the parameters object.
const UniValue &sequenceObj = find_value(o, "sequence");
if (sequenceObj.isNum()) {
int64_t seqNr64 = sequenceObj.get_int64();
if (seqNr64 < 0 || seqNr64 > std::numeric_limits::max()) {
throw JSONRPCError(
RPC_INVALID_PARAMETER,
"Invalid parameter, sequence number is out of range");
}
nSequence = uint32_t(seqNr64);
}
CTxIn in(COutPoint(txid, nOutput), CScript(), nSequence);
rawTx.vin.push_back(in);
}
std::set destinations;
std::vector addrList = sendTo.getKeys();
for (const std::string &name_ : addrList) {
if (name_ == "data") {
std::vector data =
ParseHexV(sendTo[name_].getValStr(), "Data");
CTxOut out(Amount::zero(), CScript() << OP_RETURN << data);
rawTx.vout.push_back(out);
} else {
CTxDestination destination =
DecodeDestination(name_, config.GetChainParams());
if (!IsValidDestination(destination)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
std::string("Invalid Bitcoin address: ") +
name_);
}
if (!destinations.insert(destination).second) {
throw JSONRPCError(
RPC_INVALID_PARAMETER,
std::string("Invalid parameter, duplicated address: ") +
name_);
}
CScript scriptPubKey = GetScriptForDestination(destination);
Amount nAmount = AmountFromValue(sendTo[name_]);
CTxOut out(nAmount, scriptPubKey);
rawTx.vout.push_back(out);
}
}
return EncodeHexTx(CTransaction(rawTx));
}
static UniValue decoderawtransaction(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"decoderawtransaction \"hexstring\"\n"
"\nReturn a JSON object representing the serialized, hex-encoded "
"transaction.\n"
"\nArguments:\n"
"1. \"hexstring\" (string, required) The transaction hex "
"string\n"
"\nResult:\n"
"{\n"
" \"txid\" : \"id\", (string) The transaction id\n"
" \"hash\" : \"id\", (string) The transaction hash "
"(differs from txid for witness transactions)\n"
" \"size\" : n, (numeric) The transaction size\n"
" \"version\" : n, (numeric) The version\n"
" \"locktime\" : ttt, (numeric) The lock time\n"
" \"vin\" : [ (array of json objects)\n"
" {\n"
" \"txid\": \"id\", (string) The transaction id\n"
" \"vout\": n, (numeric) The output number\n"
" \"scriptSig\": { (json object) The script\n"
" \"asm\": \"asm\", (string) asm\n"
" \"hex\": \"hex\" (string) hex\n"
" },\n"
" \"sequence\": n (numeric) The script sequence number\n"
" }\n"
" ,...\n"
" ],\n"
" \"vout\" : [ (array of json objects)\n"
" {\n"
" \"value\" : x.xxx, (numeric) The value in " +
CURRENCY_UNIT +
"\n"
" \"n\" : n, (numeric) index\n"
" \"scriptPubKey\" : { (json object)\n"
" \"asm\" : \"asm\", (string) the asm\n"
" \"hex\" : \"hex\", (string) the hex\n"
" \"reqSigs\" : n, (numeric) The required sigs\n"
" \"type\" : \"pubkeyhash\", (string) The type, eg "
"'pubkeyhash'\n"
" \"addresses\" : [ (json array of string)\n"
" \"12tvKAXCxZjSmdNbao16dKXC8tRWfcF5oc\" (string) "
"bitcoin address\n"
" ,...\n"
" ]\n"
" }\n"
" }\n"
" ,...\n"
" ],\n"
"}\n"
"\nExamples:\n" +
HelpExampleCli("decoderawtransaction", "\"hexstring\"") +
HelpExampleRpc("decoderawtransaction", "\"hexstring\""));
}
LOCK(cs_main);
RPCTypeCheck(request.params, {UniValue::VSTR});
CMutableTransaction mtx;
if (!DecodeHexTx(mtx, request.params[0].get_str())) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
}
UniValue result(UniValue::VOBJ);
TxToJSON(config, CTransaction(std::move(mtx)), uint256(), result);
return result;
}
static UniValue decodescript(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"decodescript \"hexstring\"\n"
"\nDecode a hex-encoded script.\n"
"\nArguments:\n"
"1. \"hexstring\" (string) the hex encoded script\n"
"\nResult:\n"
"{\n"
" \"asm\":\"asm\", (string) Script public key\n"
" \"hex\":\"hex\", (string) hex encoded public key\n"
" \"type\":\"type\", (string) The output type\n"
" \"reqSigs\": n, (numeric) The required signatures\n"
" \"addresses\": [ (json array of string)\n"
" \"address\" (string) bitcoin address\n"
" ,...\n"
" ],\n"
" \"p2sh\",\"address\" (string) address of P2SH script wrapping "
"this redeem script (not returned if the script is already a "
"P2SH).\n"
"}\n"
"\nExamples:\n" +
HelpExampleCli("decodescript", "\"hexstring\"") +
HelpExampleRpc("decodescript", "\"hexstring\""));
}
RPCTypeCheck(request.params, {UniValue::VSTR});
UniValue r(UniValue::VOBJ);
CScript script;
if (request.params[0].get_str().size() > 0) {
std::vector scriptData(
ParseHexV(request.params[0], "argument"));
script = CScript(scriptData.begin(), scriptData.end());
} else {
// Empty scripts are valid.
}
ScriptPubKeyToJSON(config, script, r, false);
UniValue type;
type = find_value(r, "type");
if (type.isStr() && type.get_str() != "scripthash") {
// P2SH cannot be wrapped in a P2SH. If this script is already a P2SH,
// don't return the address for a P2SH of the P2SH.
r.push_back(Pair("p2sh", EncodeDestination(CScriptID(script))));
}
return r;
}
/**
* Pushes a JSON object for script verification or signing errors to vErrorsRet.
*/
static void TxInErrorToJSON(const CTxIn &txin, UniValue &vErrorsRet,
const std::string &strMessage) {
UniValue entry(UniValue::VOBJ);
entry.push_back(Pair("txid", txin.prevout.GetTxId().ToString()));
entry.push_back(Pair("vout", uint64_t(txin.prevout.GetN())));
entry.push_back(Pair("scriptSig",
HexStr(txin.scriptSig.begin(), txin.scriptSig.end())));
entry.push_back(Pair("sequence", uint64_t(txin.nSequence)));
entry.push_back(Pair("error", strMessage));
vErrorsRet.push_back(entry);
}
static UniValue signrawtransaction(const Config &config,
const JSONRPCRequest &request) {
#ifdef ENABLE_WALLET
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
#endif
if (request.fHelp || request.params.size() < 1 ||
request.params.size() > 4) {
throw std::runtime_error(
"signrawtransaction \"hexstring\" ( "
"[{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\","
"\"redeemScript\":\"hex\"},...] [\"privatekey1\",...] 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"
"The third optional argument (may be null) is an array of "
"base58-encoded private\n"
"keys that, if given, will be the only keys used to sign the "
"transaction.\n"
#ifdef ENABLE_WALLET
+ HelpRequiringPassphrase(pwallet) +
"\n"
#endif
"\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 "
"or P2WSH) redeem script\n"
" \"amount\": value (numeric, required) The "
"amount spent\n"
" }\n"
" ,...\n"
" ]\n"
"3. \"privkeys\" (string, optional) A json array of "
"base58-encoded private keys for signing\n"
" [ (json array of strings, or 'null' if none "
"provided)\n"
" \"privatekey\" (string) private key in base58-encoding\n"
" ,...\n"
" ]\n"
"4. \"sighashtype\" (string, optional, default=ALL) The "
"signature hash type. Must be one of\n"
" \"ALL\"\n"
" \"NONE\"\n"
" \"SINGLE\"\n"
" \"ALL|ANYONECANPAY\"\n"
" \"NONE|ANYONECANPAY\"\n"
" \"SINGLE|ANYONECANPAY\"\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("signrawtransaction", "\"myhex\"") +
HelpExampleRpc("signrawtransaction", "\"myhex\""));
}
+ ObserveSafeMode();
#ifdef ENABLE_WALLET
LOCK2(cs_main, pwallet ? &pwallet->cs_wallet : nullptr);
#else
LOCK(cs_main);
#endif
RPCTypeCheck(
request.params,
{UniValue::VSTR, UniValue::VARR, UniValue::VARR, UniValue::VSTR}, true);
std::vector txData(ParseHexV(request.params[0], "argument 1"));
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
std::vector txVariants;
while (!ssData.empty()) {
try {
CMutableTransaction tx;
ssData >> tx;
txVariants.push_back(tx);
} catch (const std::exception &) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
}
}
if (txVariants.empty()) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Missing transaction");
}
// mergedTx will end up with all the signatures; it starts as a clone of the
// rawtx:
CMutableTransaction mergedTx(txVariants[0]);
// Fetch previous transactions (inputs):
CCoinsView viewDummy;
CCoinsViewCache view(&viewDummy);
{
LOCK(mempool.cs);
CCoinsViewCache &viewChain = *pcoinsTip;
CCoinsViewMemPool viewMempool(&viewChain, mempool);
// Temporarily switch cache backend to db+mempool view.
view.SetBackend(viewMempool);
for (const CTxIn &txin : mergedTx.vin) {
// Load entries from viewChain into view; can fail.
view.AccessCoin(txin.prevout);
}
// Switch back to avoid locking mempool for too long.
view.SetBackend(viewDummy);
}
bool fGivenKeys = false;
CBasicKeyStore tempKeystore;
if (request.params.size() > 2 && !request.params[2].isNull()) {
fGivenKeys = true;
UniValue keys = request.params[2].get_array();
for (size_t idx = 0; idx < keys.size(); idx++) {
UniValue k = keys[idx];
CBitcoinSecret vchSecret;
bool fGood = vchSecret.SetString(k.get_str());
if (!fGood) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Invalid private key");
}
CKey key = vchSecret.GetKey();
if (!key.IsValid()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Private key outside allowed range");
}
tempKeystore.AddKey(key);
}
}
#ifdef ENABLE_WALLET
else if (pwallet) {
EnsureWalletIsUnlocked(pwallet);
}
#endif
// Add previous txouts given in the RPC call:
if (request.params.size() > 1 && !request.params[1].isNull()) {
UniValue prevTxs = request.params[1].get_array();
for (size_t idx = 0; idx < prevTxs.size(); idx++) {
const UniValue &p = prevTxs[idx];
if (!p.isObject()) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR,
"expected object with "
"{\"txid'\",\"vout\",\"scriptPubKey\"}");
}
UniValue prevOut = p.get_obj();
RPCTypeCheckObj(prevOut,
{
{"txid", UniValueType(UniValue::VSTR)},
{"vout", UniValueType(UniValue::VNUM)},
{"scriptPubKey", UniValueType(UniValue::VSTR)},
// "amount" is also required but check is done
// below due to UniValue::VNUM erroneously
// not accepting quoted numerics
// (which are valid JSON)
});
uint256 txid = ParseHashO(prevOut, "txid");
int nOut = find_value(prevOut, "vout").get_int();
if (nOut < 0) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR,
"vout must be positive");
}
COutPoint out(txid, nOut);
std::vector pkData(ParseHexO(prevOut, "scriptPubKey"));
CScript scriptPubKey(pkData.begin(), pkData.end());
{
const Coin &coin = view.AccessCoin(out);
if (!coin.IsSpent() &&
coin.GetTxOut().scriptPubKey != scriptPubKey) {
std::string err("Previous output scriptPubKey mismatch:\n");
err = err + ScriptToAsmStr(coin.GetTxOut().scriptPubKey) +
"\nvs:\n" + ScriptToAsmStr(scriptPubKey);
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err);
}
CTxOut txout;
txout.scriptPubKey = scriptPubKey;
txout.nValue = Amount::zero();
if (prevOut.exists("amount")) {
txout.nValue =
AmountFromValue(find_value(prevOut, "amount"));
} else {
// amount param is required in replay-protected txs.
// Note that we must check for its presence here rather
// than use RPCTypeCheckObj() above, since UniValue::VNUM
// parser incorrectly parses numerics with quotes, eg
// "3.12" as a string when JSON allows it to also parse
// as numeric. And we have to accept numerics with quotes
// because our own dogfood (our rpc results) always
// produces decimal numbers that are quoted
// eg getbalance returns "3.14152" rather than 3.14152
throw JSONRPCError(RPC_INVALID_PARAMETER, "Missing amount");
}
view.AddCoin(out, Coin(txout, 1, false), true);
}
// If redeemScript given and not using the local wallet (private
// keys given), add redeemScript to the tempKeystore so it can be
// signed:
if (fGivenKeys && scriptPubKey.IsPayToScriptHash()) {
RPCTypeCheckObj(
prevOut,
{
{"txid", UniValueType(UniValue::VSTR)},
{"vout", UniValueType(UniValue::VNUM)},
{"scriptPubKey", UniValueType(UniValue::VSTR)},
{"redeemScript", UniValueType(UniValue::VSTR)},
});
UniValue v = find_value(prevOut, "redeemScript");
if (!v.isNull()) {
std::vector rsData(ParseHexV(v, "redeemScript"));
CScript redeemScript(rsData.begin(), rsData.end());
tempKeystore.AddCScript(redeemScript);
}
}
}
}
#ifdef ENABLE_WALLET
const CKeyStore &keystore =
((fGivenKeys || !pwallet) ? tempKeystore : *pwallet);
#else
const CKeyStore &keystore = tempKeystore;
#endif
SigHashType sigHashType = SigHashType().withForkId();
if (request.params.size() > 3 && !request.params[3].isNull()) {
static std::map mapSigHashValues = {
{"ALL", SIGHASH_ALL},
{"ALL|ANYONECANPAY", SIGHASH_ALL | SIGHASH_ANYONECANPAY},
{"ALL|FORKID", SIGHASH_ALL | SIGHASH_FORKID},
{"ALL|FORKID|ANYONECANPAY",
SIGHASH_ALL | SIGHASH_FORKID | SIGHASH_ANYONECANPAY},
{"NONE", SIGHASH_NONE},
{"NONE|ANYONECANPAY", SIGHASH_NONE | SIGHASH_ANYONECANPAY},
{"NONE|FORKID", SIGHASH_NONE | SIGHASH_FORKID},
{"NONE|FORKID|ANYONECANPAY",
SIGHASH_NONE | SIGHASH_FORKID | SIGHASH_ANYONECANPAY},
{"SINGLE", SIGHASH_SINGLE},
{"SINGLE|ANYONECANPAY", SIGHASH_SINGLE | SIGHASH_ANYONECANPAY},
{"SINGLE|FORKID", SIGHASH_SINGLE | SIGHASH_FORKID},
{"SINGLE|FORKID|ANYONECANPAY",
SIGHASH_SINGLE | SIGHASH_FORKID | SIGHASH_ANYONECANPAY},
};
std::string strHashType = request.params[3].get_str();
if (!mapSigHashValues.count(strHashType)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid sighash param");
}
sigHashType = SigHashType(mapSigHashValues[strHashType]);
if (!sigHashType.hasForkId()) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Signature must use SIGHASH_FORKID");
}
}
// Script verification errors.
UniValue vErrors(UniValue::VARR);
// Use CTransaction for the constant parts of the transaction to avoid
// rehashing.
const CTransaction txConst(mergedTx);
// Sign what we can:
for (size_t i = 0; i < mergedTx.vin.size(); i++) {
CTxIn &txin = mergedTx.vin[i];
const Coin &coin = view.AccessCoin(txin.prevout);
if (coin.IsSpent()) {
TxInErrorToJSON(txin, vErrors, "Input not found or already spent");
continue;
}
const CScript &prevPubKey = coin.GetTxOut().scriptPubKey;
const Amount amount = coin.GetTxOut().nValue;
SignatureData sigdata;
// Only sign SIGHASH_SINGLE if there's a corresponding output:
if ((sigHashType.getBaseType() != BaseSigHashType::SINGLE) ||
(i < mergedTx.vout.size())) {
ProduceSignature(MutableTransactionSignatureCreator(
&keystore, &mergedTx, i, amount, sigHashType),
prevPubKey, sigdata);
}
// ... and merge in other signatures:
for (const CMutableTransaction &txv : txVariants) {
if (txv.vin.size() > i) {
sigdata = CombineSignatures(
prevPubKey,
TransactionSignatureChecker(&txConst, i, amount), sigdata,
DataFromTransaction(txv, i));
}
}
UpdateTransaction(mergedTx, i, sigdata);
ScriptError serror = SCRIPT_ERR_OK;
if (!VerifyScript(
txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS,
TransactionSignatureChecker(&txConst, i, amount), &serror)) {
TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror));
}
}
bool fComplete = vErrors.empty();
UniValue result(UniValue::VOBJ);
result.push_back(Pair("hex", EncodeHexTx(CTransaction(mergedTx))));
result.push_back(Pair("complete", fComplete));
if (!vErrors.empty()) {
result.push_back(Pair("errors", vErrors));
}
return result;
}
static UniValue sendrawtransaction(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() < 1 ||
request.params.size() > 2) {
throw std::runtime_error(
"sendrawtransaction \"hexstring\" ( allowhighfees )\n"
"\nSubmits raw transaction (serialized, hex-encoded) to local node "
"and network.\n"
"\nAlso see createrawtransaction and signrawtransaction calls.\n"
"\nArguments:\n"
"1. \"hexstring\" (string, required) The hex string of the raw "
"transaction)\n"
"2. allowhighfees (boolean, optional, default=false) Allow high "
"fees\n"
"\nResult:\n"
"\"hex\" (string) The transaction hash in hex\n"
"\nExamples:\n"
"\nCreate a transaction\n" +
HelpExampleCli("createrawtransaction",
"\"[{\\\"txid\\\" : "
"\\\"mytxid\\\",\\\"vout\\\":0}]\" "
"\"{\\\"myaddress\\\":0.01}\"") +
"Sign the transaction, and get back the hex\n" +
HelpExampleCli("signrawtransaction", "\"myhex\"") +
"\nSend the transaction (signed hex)\n" +
HelpExampleCli("sendrawtransaction", "\"signedhex\"") +
"\nAs a json rpc call\n" +
HelpExampleRpc("sendrawtransaction", "\"signedhex\""));
}
+ ObserveSafeMode();
LOCK(cs_main);
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL});
// parse hex string from parameter
CMutableTransaction mtx;
if (!DecodeHexTx(mtx, request.params[0].get_str())) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
}
CTransactionRef tx(MakeTransactionRef(std::move(mtx)));
const uint256 &txid = tx->GetId();
bool fLimitFree = false;
Amount nMaxRawTxFee = maxTxFee;
if (request.params.size() > 1 && request.params[1].get_bool()) {
nMaxRawTxFee = Amount::zero();
}
CCoinsViewCache &view = *pcoinsTip;
bool fHaveChain = false;
for (size_t o = 0; !fHaveChain && o < tx->vout.size(); o++) {
const Coin &existingCoin = view.AccessCoin(COutPoint(txid, o));
fHaveChain = !existingCoin.IsSpent();
}
bool fHaveMempool = mempool.exists(txid);
if (!fHaveMempool && !fHaveChain) {
// Push to local node and sync with wallets.
CValidationState state;
bool fMissingInputs;
if (!AcceptToMemoryPool(config, mempool, state, std::move(tx),
fLimitFree, &fMissingInputs, false,
nMaxRawTxFee)) {
if (state.IsInvalid()) {
throw JSONRPCError(RPC_TRANSACTION_REJECTED,
strprintf("%i: %s", state.GetRejectCode(),
state.GetRejectReason()));
} else {
if (fMissingInputs) {
throw JSONRPCError(RPC_TRANSACTION_ERROR, "Missing inputs");
}
throw JSONRPCError(RPC_TRANSACTION_ERROR,
state.GetRejectReason());
}
}
} else if (fHaveChain) {
throw JSONRPCError(RPC_TRANSACTION_ALREADY_IN_CHAIN,
"transaction already in block chain");
}
if (!g_connman) {
throw JSONRPCError(
RPC_CLIENT_P2P_DISABLED,
"Error: Peer-to-peer functionality missing or disabled");
}
CInv inv(MSG_TX, txid);
g_connman->ForEachNode([&inv](CNode *pnode) { pnode->PushInventory(inv); });
return txid.GetHex();
}
// clang-format off
static const ContextFreeRPCCommand commands[] = {
- // category name actor (function) okSafeMode
+ // category name actor (function) argNames
// ------------------- ------------------------ ---------------------- ----------
- { "rawtransactions", "getrawtransaction", getrawtransaction, true, {"txid","verbose"} },
- { "rawtransactions", "createrawtransaction", createrawtransaction, true, {"inputs","outputs","locktime"} },
- { "rawtransactions", "decoderawtransaction", decoderawtransaction, true, {"hexstring"} },
- { "rawtransactions", "decodescript", decodescript, true, {"hexstring"} },
- { "rawtransactions", "sendrawtransaction", sendrawtransaction, false, {"hexstring","allowhighfees"} },
- { "rawtransactions", "signrawtransaction", signrawtransaction, false, {"hexstring","prevtxs","privkeys","sighashtype"} }, /* uses wallet if enabled */
-
- { "blockchain", "gettxoutproof", gettxoutproof, true, {"txids", "blockhash"} },
- { "blockchain", "verifytxoutproof", verifytxoutproof, true, {"proof"} },
+ { "rawtransactions", "getrawtransaction", getrawtransaction, {"txid","verbose"} },
+ { "rawtransactions", "createrawtransaction", createrawtransaction, {"inputs","outputs","locktime"} },
+ { "rawtransactions", "decoderawtransaction", decoderawtransaction, {"hexstring"} },
+ { "rawtransactions", "decodescript", decodescript, {"hexstring"} },
+ { "rawtransactions", "sendrawtransaction", sendrawtransaction, {"hexstring","allowhighfees"} },
+ { "rawtransactions", "signrawtransaction", signrawtransaction, {"hexstring","prevtxs","privkeys","sighashtype"} }, /* uses wallet if enabled */
+
+ { "blockchain", "gettxoutproof", gettxoutproof, {"txids", "blockhash"} },
+ { "blockchain", "verifytxoutproof", verifytxoutproof, {"proof"} },
};
// clang-format on
void RegisterRawTransactionRPCCommands(CRPCTable &t) {
for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) {
t.appendCommand(commands[vcidx].name, &commands[vcidx]);
}
}
diff --git a/src/rpc/safemode.cpp b/src/rpc/safemode.cpp
new file mode 100644
index 000000000..0a507df8b
--- /dev/null
+++ b/src/rpc/safemode.cpp
@@ -0,0 +1,14 @@
+#include "safemode.h"
+
+#include "rpc/protocol.h"
+#include "util.h"
+#include "warnings.h"
+
+void ObserveSafeMode() {
+ std::string warning = GetWarnings("rpc");
+ if (warning != "" &&
+ !gArgs.GetBoolArg("-disablesafemode", DEFAULT_DISABLE_SAFEMODE)) {
+ throw JSONRPCError(RPC_FORBIDDEN_BY_SAFE_MODE,
+ std::string("Safe mode: ") + warning);
+ }
+}
diff --git a/src/rpc/safemode.h b/src/rpc/safemode.h
new file mode 100644
index 000000000..8466d6b2f
--- /dev/null
+++ b/src/rpc/safemode.h
@@ -0,0 +1,12 @@
+// Copyright (c) 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_RPC_SAFEMODE_H
+#define BITCOIN_RPC_SAFEMODE_H
+
+static const bool DEFAULT_DISABLE_SAFEMODE = true;
+
+void ObserveSafeMode();
+
+#endif // BITCOIN_RPC_SAFEMODE_H
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp
index a78c4364b..dbeb64f7e 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -1,576 +1,571 @@
// 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 "rpc/server.h"
#include "base58.h"
#include "config.h"
#include "fs.h"
#include "init.h"
#include "random.h"
#include "sync.h"
#include "ui_interface.h"
#include "util.h"
#include "utilstrencodings.h"
#include
#include // for to_upper()
#include
#include
#include // for unique_ptr
#include
#include
static bool fRPCRunning = false;
static bool fRPCInWarmup = true;
static std::string rpcWarmupStatus("RPC server started");
static CCriticalSection cs_rpcWarmup;
/* Timer-creating functions */
static RPCTimerInterface *timerInterface = nullptr;
/* Map of name to timer. */
static std::map> deadlineTimers;
UniValue RPCServer::ExecuteCommand(Config &config,
const JSONRPCRequest &request) const {
// Return immediately if in warmup
{
LOCK(cs_rpcWarmup);
if (fRPCInWarmup) {
throw JSONRPCError(RPC_IN_WARMUP, rpcWarmupStatus);
}
}
// TODO Only call tableRPC.execute() if no context-sensitive RPC command
// exists
// Check if context-free RPC method is valid and execute it
return tableRPC.execute(config, request);
}
static struct CRPCSignals {
boost::signals2::signal Started;
boost::signals2::signal Stopped;
boost::signals2::signal PreCommand;
} g_rpcSignals;
void RPCServerSignals::OnStarted(std::function slot) {
g_rpcSignals.Started.connect(slot);
}
void RPCServerSignals::OnStopped(std::function slot) {
g_rpcSignals.Stopped.connect(slot);
}
-void RPCServerSignals::OnPreCommand(
- std::function slot) {
- g_rpcSignals.PreCommand.connect(boost::bind(slot, _1));
-}
-
void RPCTypeCheck(const UniValue ¶ms,
const std::list &typesExpected,
bool fAllowNull) {
unsigned int i = 0;
for (UniValue::VType t : typesExpected) {
if (params.size() <= i) {
break;
}
const UniValue &v = params[i];
if (!(fAllowNull && v.isNull())) {
RPCTypeCheckArgument(v, t);
}
i++;
}
}
void RPCTypeCheckArgument(const UniValue &value, UniValue::VType typeExpected) {
if (value.type() != typeExpected) {
throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Expected type %s, got %s",
uvTypeName(typeExpected),
uvTypeName(value.type())));
}
}
void RPCTypeCheckObj(const UniValue &o,
const std::map &typesExpected,
bool fAllowNull, bool fStrict) {
for (const auto &t : typesExpected) {
const UniValue &v = find_value(o, t.first);
if (!fAllowNull && v.isNull()) {
throw JSONRPCError(RPC_TYPE_ERROR,
strprintf("Missing %s", t.first));
}
if (!(t.second.typeAny || v.type() == t.second.type ||
(fAllowNull && v.isNull()))) {
std::string err = strprintf("Expected type %s for %s, got %s",
uvTypeName(t.second.type), t.first,
uvTypeName(v.type()));
throw JSONRPCError(RPC_TYPE_ERROR, err);
}
}
if (fStrict) {
for (const std::string &k : o.getKeys()) {
if (typesExpected.count(k) == 0) {
std::string err = strprintf("Unexpected key %s", k);
throw JSONRPCError(RPC_TYPE_ERROR, err);
}
}
}
}
Amount AmountFromValue(const UniValue &value) {
if (!value.isNum() && !value.isStr()) {
throw JSONRPCError(RPC_TYPE_ERROR, "Amount is not a number or string");
}
int64_t n;
if (!ParseFixedPoint(value.getValStr(), 8, &n)) {
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount");
}
Amount amt = n * SATOSHI;
if (!MoneyRange(amt)) {
throw JSONRPCError(RPC_TYPE_ERROR, "Amount out of range");
}
return amt;
}
UniValue ValueFromAmount(const Amount amount) {
bool sign = amount < Amount::zero();
Amount n_abs(sign ? -amount : amount);
int64_t quotient = n_abs / COIN;
int64_t remainder = (n_abs % COIN) / SATOSHI;
return UniValue(UniValue::VNUM, strprintf("%s%d.%08d", sign ? "-" : "",
quotient, remainder));
}
uint256 ParseHashV(const UniValue &v, std::string strName) {
std::string strHex;
if (v.isStr()) {
strHex = v.get_str();
}
// Note: IsHex("") is false
if (!IsHex(strHex)) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
strName + " must be hexadecimal string (not '" +
strHex + "')");
}
if (strHex.length() != 64) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
strprintf("%s must be of length %d (not %d)",
strName, 64, strHex.length()));
}
uint256 result;
result.SetHex(strHex);
return result;
}
uint256 ParseHashO(const UniValue &o, std::string strKey) {
return ParseHashV(find_value(o, strKey), strKey);
}
std::vector ParseHexV(const UniValue &v, std::string strName) {
std::string strHex;
if (v.isStr()) {
strHex = v.get_str();
}
if (!IsHex(strHex)) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
strName + " must be hexadecimal string (not '" +
strHex + "')");
}
return ParseHex(strHex);
}
std::vector ParseHexO(const UniValue &o, std::string strKey) {
return ParseHexV(find_value(o, strKey), strKey);
}
/**
* Note: This interface may still be subject to change.
*/
std::string CRPCTable::help(Config &config, const std::string &strCommand,
const JSONRPCRequest &helpreq) const {
std::string strRet;
std::string category;
std::set setDone;
std::vector>
vCommands;
for (std::map::const_iterator
mi = mapCommands.begin();
mi != mapCommands.end(); ++mi) {
vCommands.push_back(
std::make_pair(mi->second->category + mi->first, mi->second));
}
sort(vCommands.begin(), vCommands.end());
JSONRPCRequest jreq(helpreq);
jreq.fHelp = true;
jreq.params = UniValue();
for (const std::pair &command :
vCommands) {
const ContextFreeRPCCommand *pcmd = command.second;
std::string strMethod = pcmd->name;
// We already filter duplicates, but these deprecated screw up the sort
// order
if (strMethod.find("label") != std::string::npos) {
continue;
}
if ((strCommand != "" || pcmd->category == "hidden") &&
strMethod != strCommand) {
continue;
}
jreq.strMethod = strMethod;
try {
if (setDone.insert(pcmd).second) {
pcmd->call(config, jreq);
}
} catch (const std::exception &e) {
// Help text is returned in an exception
std::string strHelp = std::string(e.what());
if (strCommand == "") {
if (strHelp.find('\n') != std::string::npos) {
strHelp = strHelp.substr(0, strHelp.find('\n'));
}
if (category != pcmd->category) {
if (!category.empty()) {
strRet += "\n";
}
category = pcmd->category;
std::string firstLetter = category.substr(0, 1);
boost::to_upper(firstLetter);
strRet +=
"== " + firstLetter + category.substr(1) + " ==\n";
}
}
strRet += strHelp + "\n";
}
}
if (strRet == "") {
strRet = strprintf("help: unknown command: %s\n", strCommand);
}
strRet = strRet.substr(0, strRet.size() - 1);
return strRet;
}
static UniValue help(Config &config, const JSONRPCRequest &jsonRequest) {
if (jsonRequest.fHelp || jsonRequest.params.size() > 1) {
throw std::runtime_error(
"help ( \"command\" )\n"
"\nList all commands, or get help for a specified command.\n"
"\nArguments:\n"
"1. \"command\" (string, optional) The command to get help on\n"
"\nResult:\n"
"\"text\" (string) The help text\n");
}
std::string strCommand;
if (jsonRequest.params.size() > 0) {
strCommand = jsonRequest.params[0].get_str();
}
return tableRPC.help(config, strCommand, jsonRequest);
}
static UniValue stop(const Config &config, const JSONRPCRequest &jsonRequest) {
// Accept the deprecated and ignored 'detach' boolean argument
if (jsonRequest.fHelp || jsonRequest.params.size() > 1) {
throw std::runtime_error("stop\n"
"\nStop Bitcoin server.");
}
// Event loop will exit after current HTTP requests have been handled, so
// this reply will get back to the client.
StartShutdown();
return "Bitcoin server stopping";
}
static UniValue uptime(const Config &config,
const JSONRPCRequest &jsonRequest) {
if (jsonRequest.fHelp || jsonRequest.params.size() > 1) {
throw std::runtime_error("uptime\n"
"\nReturns the total uptime of the server.\n"
"\nResult:\n"
"ttt (numeric) The number of seconds "
"that the server has been running\n"
"\nExamples:\n" +
HelpExampleCli("uptime", "") +
HelpExampleRpc("uptime", ""));
}
return GetTime() - GetStartupTime();
}
/**
* Call Table
*/
// clang-format off
static const ContextFreeRPCCommand vRPCCommands[] = {
- // category name actor (function) okSafe argNames
- // ------------------- ------------------------ ---------------------- ------ ----------
+ // category name actor (function) argNames
+ // ------------------- ------------------------ ---------------------- ----------
/* Overall control/query calls */
- { "control", "help", help, true, {"command"} },
- { "control", "stop", stop, true, {} },
- { "control", "uptime", uptime, true, {} },
+ { "control", "help", help, {"command"} },
+ { "control", "stop", stop, {} },
+ { "control", "uptime", uptime, {} },
};
// clang-format on
CRPCTable::CRPCTable() {
unsigned int vcidx;
for (vcidx = 0; vcidx < (sizeof(vRPCCommands) / sizeof(vRPCCommands[0]));
vcidx++) {
const ContextFreeRPCCommand *pcmd;
pcmd = &vRPCCommands[vcidx];
mapCommands[pcmd->name] = pcmd;
}
}
const ContextFreeRPCCommand *CRPCTable::
operator[](const std::string &name) const {
std::map::const_iterator it =
mapCommands.find(name);
if (it == mapCommands.end()) {
return nullptr;
}
return (*it).second;
}
bool CRPCTable::appendCommand(const std::string &name,
const ContextFreeRPCCommand *pcmd) {
if (IsRPCRunning()) {
return false;
}
// don't allow overwriting for now
std::map::const_iterator it =
mapCommands.find(name);
if (it != mapCommands.end()) {
return false;
}
mapCommands[name] = pcmd;
return true;
}
bool StartRPC() {
LogPrint(BCLog::RPC, "Starting RPC\n");
fRPCRunning = true;
g_rpcSignals.Started();
return true;
}
void InterruptRPC() {
LogPrint(BCLog::RPC, "Interrupting RPC\n");
// Interrupt e.g. running longpolls
fRPCRunning = false;
}
void StopRPC() {
LogPrint(BCLog::RPC, "Stopping RPC\n");
deadlineTimers.clear();
DeleteAuthCookie();
g_rpcSignals.Stopped();
}
bool IsRPCRunning() {
return fRPCRunning;
}
void SetRPCWarmupStatus(const std::string &newStatus) {
LOCK(cs_rpcWarmup);
rpcWarmupStatus = newStatus;
}
void SetRPCWarmupFinished() {
LOCK(cs_rpcWarmup);
assert(fRPCInWarmup);
fRPCInWarmup = false;
}
bool RPCIsInWarmup(std::string *outStatus) {
LOCK(cs_rpcWarmup);
if (outStatus) {
*outStatus = rpcWarmupStatus;
}
return fRPCInWarmup;
}
static UniValue JSONRPCExecOne(Config &config, RPCServer &rpcServer,
JSONRPCRequest jreq, const UniValue &req) {
UniValue rpc_result(UniValue::VOBJ);
try {
jreq.parse(req);
UniValue result = rpcServer.ExecuteCommand(config, jreq);
rpc_result = JSONRPCReplyObj(result, NullUniValue, jreq.id);
} catch (const UniValue &objError) {
rpc_result = JSONRPCReplyObj(NullUniValue, objError, jreq.id);
} catch (const std::exception &e) {
rpc_result = JSONRPCReplyObj(
NullUniValue, JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
}
return rpc_result;
}
std::string JSONRPCExecBatch(Config &config, RPCServer &rpcServer,
const JSONRPCRequest &jreq, const UniValue &vReq) {
UniValue ret(UniValue::VARR);
for (size_t i = 0; i < vReq.size(); i++) {
ret.push_back(JSONRPCExecOne(config, rpcServer, jreq, vReq[i]));
}
return ret.write() + "\n";
}
/**
* Process named arguments into a vector of positional arguments, based on the
* passed-in specification for the RPC call's arguments.
*/
static inline JSONRPCRequest
transformNamedArguments(const JSONRPCRequest &in,
const std::vector &argNames) {
JSONRPCRequest out = in;
out.params = UniValue(UniValue::VARR);
// Build a map of parameters, and remove ones that have been processed, so
// that we can throw a focused error if there is an unknown one.
const std::vector &keys = in.params.getKeys();
const std::vector