aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am26
-rw-r--r--src/Makefile.bench.include4
-rw-r--r--src/Makefile.test.include2
-rw-r--r--src/bench/bech32.cpp37
-rw-r--r--src/bench/bench_bitcoin.cpp13
-rw-r--r--src/bench/block_assemble.cpp116
-rw-r--r--src/bench/coin_selection.cpp26
-rw-r--r--src/bench/prevector.cpp6
-rw-r--r--src/bench/verify_script.cpp1
-rw-r--r--src/bitcoin-tx.cpp46
-rw-r--r--src/bitcoind.cpp3
-rw-r--r--src/chainparamsbase.cpp1
-rw-r--r--src/compat/glibc_compat.cpp45
-rw-r--r--src/core_io.h5
-rw-r--r--src/core_read.cpp41
-rw-r--r--src/core_write.cpp23
-rw-r--r--src/crypto/sha256.cpp99
-rw-r--r--src/crypto/sha256_avx2.cpp4
-rw-r--r--src/crypto/sha256_shani.cpp359
-rw-r--r--src/crypto/sha256_sse41.cpp4
-rw-r--r--src/httprpc.cpp8
-rw-r--r--src/index/base.cpp2
-rw-r--r--src/index/txindex.cpp2
-rw-r--r--src/init.cpp98
-rw-r--r--src/init.h4
-rw-r--r--src/interfaces/handler.cpp2
-rw-r--r--src/interfaces/node.cpp1
-rw-r--r--src/interfaces/wallet.cpp7
-rw-r--r--src/interfaces/wallet.h5
-rw-r--r--src/keystore.h21
-rw-r--r--src/merkleblock.h6
-rw-r--r--src/miner.cpp4
-rw-r--r--src/net.cpp21
-rw-r--r--src/net.h13
-rw-r--r--src/net_processing.cpp151
-rw-r--r--src/net_processing.h40
-rw-r--r--src/netaddress.cpp10
-rw-r--r--src/netaddress.h2
-rw-r--r--src/netbase.cpp8
-rw-r--r--src/outputtype.cpp101
-rw-r--r--src/outputtype.h49
-rw-r--r--src/policy/policy.cpp2
-rw-r--r--src/pubkey.h1
-rw-r--r--src/qt/README.md8
-rw-r--r--src/qt/addressbookpage.cpp5
-rw-r--r--src/qt/bantablemodel.cpp2
-rw-r--r--src/qt/bitcoin.cpp55
-rw-r--r--src/qt/bitcoingui.cpp93
-rw-r--r--src/qt/bitcoingui.h103
-rw-r--r--src/qt/coincontroldialog.cpp10
-rw-r--r--src/qt/guiconstants.h2
-rw-r--r--src/qt/guiutil.cpp32
-rw-r--r--src/qt/guiutil.h2
-rw-r--r--src/qt/macdockiconhandler.mm8
-rw-r--r--src/qt/networkstyle.cpp4
-rw-r--r--src/qt/openuridialog.cpp2
-rw-r--r--src/qt/optionsdialog.cpp14
-rw-r--r--src/qt/paymentrequestplus.cpp2
-rw-r--r--src/qt/paymentserver.cpp15
-rw-r--r--src/qt/peertablemodel.cpp5
-rw-r--r--src/qt/platformstyle.cpp2
-rw-r--r--src/qt/receiverequestdialog.cpp3
-rw-r--r--src/qt/rpcconsole.cpp14
-rw-r--r--src/qt/rpcconsole.h1
-rw-r--r--src/qt/sendcoinsentry.cpp2
-rw-r--r--src/qt/signverifymessagedialog.cpp3
-rw-r--r--src/qt/splashscreen.cpp1
-rw-r--r--src/qt/test/test_main.cpp7
-rw-r--r--src/qt/test/wallettests.cpp13
-rw-r--r--src/qt/transactiondesc.cpp10
-rw-r--r--src/qt/transactionrecord.cpp8
-rw-r--r--src/qt/transactionrecord.h4
-rw-r--r--src/qt/transactiontablemodel.cpp9
-rw-r--r--src/qt/transactionview.cpp4
-rw-r--r--src/qt/walletframe.cpp1
-rw-r--r--src/qt/walletmodel.cpp8
-rw-r--r--src/qt/walletmodel.h4
-rw-r--r--src/qt/winshutdownmonitor.cpp4
-rw-r--r--src/qt/winshutdownmonitor.h2
-rw-r--r--src/rpc/blockchain.cpp291
-rw-r--r--src/rpc/client.cpp17
-rw-r--r--src/rpc/mining.cpp17
-rw-r--r--src/rpc/misc.cpp24
-rw-r--r--src/rpc/rawtransaction.cpp739
-rw-r--r--src/rpc/rawtransaction.h3
-rw-r--r--src/rpc/server.cpp4
-rw-r--r--src/script/bitcoinconsensus.cpp2
-rw-r--r--src/script/ismine.cpp12
-rw-r--r--src/script/ismine.h6
-rw-r--r--src/script/sign.cpp580
-rw-r--r--src/script/sign.h595
-rw-r--r--src/script/standard.cpp10
-rw-r--r--src/script/standard.h1
-rw-r--r--src/serialize.h4
-rw-r--r--src/shutdown.cpp23
-rw-r--r--src/shutdown.h13
-rw-r--r--src/test/cuckoocache_tests.cpp2
-rw-r--r--src/test/dbwrapper_tests.cpp14
-rw-r--r--src/test/denialofservice_tests.cpp82
-rw-r--r--src/test/net_tests.cpp2
-rw-r--r--src/test/netbase_tests.cpp4
-rw-r--r--src/test/script_p2sh_tests.cpp2
-rw-r--r--src/test/script_standard_tests.cpp249
-rw-r--r--src/test/script_tests.cpp228
-rw-r--r--src/test/streams_tests.cpp23
-rw-r--r--src/test/test_bitcoin.cpp39
-rw-r--r--src/test/test_bitcoin.h6
-rw-r--r--src/test/transaction_tests.cpp17
-rw-r--r--src/test/util_tests.cpp8
-rw-r--r--src/torcontrol.cpp4
-rw-r--r--src/txdb.cpp4
-rw-r--r--src/txmempool.cpp4
-rw-r--r--src/txmempool.h2
-rw-r--r--src/util.h11
-rw-r--r--src/utilmemory.h19
-rw-r--r--src/validation.cpp75
-rw-r--r--src/validation.h36
-rw-r--r--src/validationinterface.cpp11
-rw-r--r--src/validationinterface.h7
-rw-r--r--src/wallet/crypter.h6
-rw-r--r--src/wallet/db.cpp5
-rw-r--r--src/wallet/init.cpp3
-rw-r--r--src/wallet/rpcdump.cpp2
-rw-r--r--src/wallet/rpcwallet.cpp681
-rw-r--r--src/wallet/rpcwallet.h3
-rw-r--r--src/wallet/test/psbt_wallet_tests.cpp74
-rw-r--r--src/wallet/test/wallet_tests.cpp6
-rw-r--r--src/wallet/wallet.cpp299
-rw-r--r--src/wallet/wallet.h82
-rw-r--r--src/zmq/zmqnotificationinterface.cpp13
-rw-r--r--src/zmq/zmqnotificationinterface.h6
-rw-r--r--src/zmq/zmqrpc.cpp61
-rw-r--r--src/zmq/zmqrpc.h12
133 files changed, 4641 insertions, 1683 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index e03c21f16e..2603dbae56 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -52,8 +52,12 @@ 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/*)
+$(LIBSECP256K1): $(wildcard secp256k1/src/*.h) $(wildcard secp256k1/src/*.c) $(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
@@ -135,6 +139,7 @@ BITCOIN_CORE_H = \
netbase.h \
netmessagemaker.h \
noui.h \
+ outputtype.h \
policy/feerate.h \
policy/fees.h \
policy/policy.h \
@@ -157,6 +162,7 @@ BITCOIN_CORE_H = \
script/sigcache.h \
script/sign.h \
script/standard.h \
+ shutdown.h \
streams.h \
support/allocators/secure.h \
support/allocators/zeroafterfree.h \
@@ -173,6 +179,7 @@ BITCOIN_CORE_H = \
ui_interface.h \
undo.h \
util.h \
+ utilmemory.h \
utilmoneystr.h \
utiltime.h \
validation.h \
@@ -193,7 +200,8 @@ BITCOIN_CORE_H = \
zmq/zmqabstractnotifier.h \
zmq/zmqconfig.h\
zmq/zmqnotificationinterface.h \
- zmq/zmqpublishnotifier.h
+ zmq/zmqpublishnotifier.h \
+ zmq/zmqrpc.h
obj/build.h: FORCE
@@ -224,6 +232,7 @@ libbitcoin_server_a_SOURCES = \
net.cpp \
net_processing.cpp \
noui.cpp \
+ outputtype.cpp \
policy/fees.cpp \
policy/policy.cpp \
policy/rbf.cpp \
@@ -235,7 +244,9 @@ libbitcoin_server_a_SOURCES = \
rpc/net.cpp \
rpc/rawtransaction.cpp \
rpc/server.cpp \
+ rpc/util.cpp \
script/sigcache.cpp \
+ shutdown.cpp \
timedata.cpp \
torcontrol.cpp \
txdb.cpp \
@@ -252,7 +263,8 @@ libbitcoin_zmq_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_zmq_a_SOURCES = \
zmq/zmqabstractnotifier.cpp \
zmq/zmqnotificationinterface.cpp \
- zmq/zmqpublishnotifier.cpp
+ zmq/zmqpublishnotifier.cpp \
+ zmq/zmqrpc.cpp
endif
@@ -313,6 +325,12 @@ 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)
@@ -392,7 +410,6 @@ libbitcoin_util_a_SOURCES = \
logging.cpp \
random.cpp \
rpc/protocol.cpp \
- rpc/util.cpp \
support/cleanse.cpp \
sync.cpp \
threadinterrupt.cpp \
@@ -404,6 +421,7 @@ libbitcoin_util_a_SOURCES = \
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
diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include
index bc2c46f228..f5293585a0 100644
--- a/src/Makefile.bench.include
+++ b/src/Makefile.bench.include
@@ -15,6 +15,7 @@ bench_bench_bitcoin_SOURCES = \
bench/bench_bitcoin.cpp \
bench/bench.cpp \
bench/bench.h \
+ bench/block_assemble.cpp \
bench/checkblock.cpp \
bench/checkqueue.cpp \
bench/examples.cpp \
@@ -25,6 +26,7 @@ bench_bench_bitcoin_SOURCES = \
bench/mempool_eviction.cpp \
bench/verify_script.cpp \
bench/base58.cpp \
+ bench/bech32.cpp \
bench/lockedpool.cpp \
bench/prevector.cpp
@@ -33,8 +35,8 @@ nodist_bench_bench_bitcoin_SOURCES = $(GENERATED_BENCH_FILES)
bench_bench_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(EVENT_CLFAGS) $(EVENT_PTHREADS_CFLAGS) -I$(builddir)/bench/
bench_bench_bitcoin_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
bench_bench_bitcoin_LDADD = \
- $(LIBBITCOIN_SERVER) \
$(LIBBITCOIN_WALLET) \
+ $(LIBBITCOIN_SERVER) \
$(LIBBITCOIN_COMMON) \
$(LIBBITCOIN_UTIL) \
$(LIBBITCOIN_CONSENSUS) \
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index a4d31795ec..02f2063504 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -94,6 +94,7 @@ BITCOIN_TESTS =\
if ENABLE_WALLET
BITCOIN_TESTS += \
wallet/test/accounting_tests.cpp \
+ wallet/test/psbt_wallet_tests.cpp \
wallet/test/wallet_tests.cpp \
wallet/test/wallet_crypto_tests.cpp \
wallet/test/coinselector_tests.cpp
@@ -137,6 +138,7 @@ test_test_bitcoin_fuzzy_LDADD = \
$(LIBBITCOIN_CRYPTO) \
$(LIBBITCOIN_CRYPTO_SSE41) \
$(LIBBITCOIN_CRYPTO_AVX2) \
+ $(LIBBITCOIN_CRYPTO_SHANI) \
$(LIBSECP256K1)
test_test_bitcoin_fuzzy_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
diff --git a/src/bench/bech32.cpp b/src/bench/bech32.cpp
new file mode 100644
index 0000000000..8b80e17391
--- /dev/null
+++ b/src/bench/bech32.cpp
@@ -0,0 +1,37 @@
+// Copyright (c) 2018 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 <bench/bench.h>
+
+#include <validation.h>
+#include <bech32.h>
+#include <utilstrencodings.h>
+
+#include <vector>
+#include <string>
+
+
+static void Bech32Encode(benchmark::State& state)
+{
+ std::vector<uint8_t> v = ParseHex("c97f5a67ec381b760aeaf67573bc164845ff39a3bb26a1cee401ac67243b48db");
+ std::vector<unsigned char> tmp = {0};
+ tmp.reserve(1 + 32 * 8 / 5);
+ ConvertBits<8, 5, true>([&](unsigned char c) { tmp.push_back(c); }, v.begin(), v.end());
+ while (state.KeepRunning()) {
+ bech32::Encode("bc", tmp);
+ }
+}
+
+
+static void Bech32Decode(benchmark::State& state)
+{
+ std::string addr = "bc1qkallence7tjawwvy0dwt4twc62qjgaw8f4vlhyd006d99f09";
+ while (state.KeepRunning()) {
+ bech32::Decode(addr);
+ }
+}
+
+
+BENCHMARK(Bech32Encode, 800 * 1000);
+BENCHMARK(Bech32Decode, 800 * 1000);
diff --git a/src/bench/bench_bitcoin.cpp b/src/bench/bench_bitcoin.cpp
index f3302bfe5a..92befdd593 100644
--- a/src/bench/bench_bitcoin.cpp
+++ b/src/bench/bench_bitcoin.cpp
@@ -38,6 +38,14 @@ static void SetupBenchArgs()
gArgs.AddArg("-help", "", false, OptionsCategory::HIDDEN);
}
+static fs::path SetDataDir()
+{
+ fs::path ret = fs::temp_directory_path() / "bench_bitcoin" / fs::unique_path();
+ fs::create_directories(ret);
+ gArgs.ForceSetArg("-datadir", ret.string());
+ return ret;
+}
+
int main(int argc, char** argv)
{
SetupBenchArgs();
@@ -53,6 +61,9 @@ int main(int argc, char** argv)
return EXIT_SUCCESS;
}
+ // Set the datadir after parsing the bench options
+ const fs::path bench_datadir{SetDataDir()};
+
SHA256AutoDetect();
RandomInit();
ECC_Start();
@@ -80,6 +91,8 @@ int main(int argc, char** argv)
benchmark::BenchRunner::RunAll(*printer, evaluations, scaling_factor, regex_filter, is_list_only);
+ fs::remove_all(bench_datadir);
+
ECC_Stop();
return EXIT_SUCCESS;
diff --git a/src/bench/block_assemble.cpp b/src/bench/block_assemble.cpp
new file mode 100644
index 0000000000..36fa175a76
--- /dev/null
+++ b/src/bench/block_assemble.cpp
@@ -0,0 +1,116 @@
+// Copyright (c) 2011-2017 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <bench/bench.h>
+#include <chainparams.h>
+#include <coins.h>
+#include <consensus/merkle.h>
+#include <consensus/validation.h>
+#include <miner.h>
+#include <policy/policy.h>
+#include <pow.h>
+#include <scheduler.h>
+#include <txdb.h>
+#include <txmempool.h>
+#include <utiltime.h>
+#include <validation.h>
+#include <validationinterface.h>
+
+#include <boost/thread.hpp>
+
+#include <list>
+#include <vector>
+
+static std::shared_ptr<CBlock> PrepareBlock(const CScript& coinbase_scriptPubKey)
+{
+ auto block = std::make_shared<CBlock>(
+ BlockAssembler{Params()}
+ .CreateNewBlock(coinbase_scriptPubKey, /* fMineWitnessTx */ true)
+ ->block);
+
+ block->nTime = ::chainActive.Tip()->GetMedianTimePast() + 1;
+ block->hashMerkleRoot = BlockMerkleRoot(*block);
+
+ return block;
+}
+
+
+static CTxIn MineBlock(const CScript& coinbase_scriptPubKey)
+{
+ auto block = PrepareBlock(coinbase_scriptPubKey);
+
+ while (!CheckProofOfWork(block->GetHash(), block->nBits, Params().GetConsensus())) {
+ assert(++block->nNonce);
+ }
+
+ bool processed{ProcessNewBlock(Params(), block, true, nullptr)};
+ assert(processed);
+
+ return CTxIn{block->vtx[0]->GetHash(), 0};
+}
+
+
+static void AssembleBlock(benchmark::State& state)
+{
+ const std::vector<unsigned char> op_true{OP_TRUE};
+ CScriptWitness witness;
+ witness.stack.push_back(op_true);
+
+ uint256 witness_program;
+ CSHA256().Write(&op_true[0], op_true.size()).Finalize(witness_program.begin());
+
+ const CScript SCRIPT_PUB{CScript(OP_0) << std::vector<unsigned char>{witness_program.begin(), witness_program.end()}};
+
+ // Switch to regtest so we can mine faster
+ // Also segwit is active, so we can include witness transactions
+ SelectParams(CBaseChainParams::REGTEST);
+
+ InitScriptExecutionCache();
+
+ boost::thread_group thread_group;
+ CScheduler scheduler;
+ {
+ ::pblocktree.reset(new CBlockTreeDB(1 << 20, true));
+ ::pcoinsdbview.reset(new CCoinsViewDB(1 << 23, true));
+ ::pcoinsTip.reset(new CCoinsViewCache(pcoinsdbview.get()));
+
+ const CChainParams& chainparams = Params();
+ thread_group.create_thread(boost::bind(&CScheduler::serviceQueue, &scheduler));
+ GetMainSignals().RegisterBackgroundSignalScheduler(scheduler);
+ LoadGenesisBlock(chainparams);
+ CValidationState state;
+ ActivateBestChain(state, chainparams);
+ assert(::chainActive.Tip() != nullptr);
+ const bool witness_enabled{IsWitnessEnabled(::chainActive.Tip(), chainparams.GetConsensus())};
+ assert(witness_enabled);
+ }
+
+ // Collect some loose transactions that spend the coinbases of our mined blocks
+ constexpr size_t NUM_BLOCKS{200};
+ std::array<CTransactionRef, NUM_BLOCKS - COINBASE_MATURITY + 1> txs;
+ for (size_t b{0}; b < NUM_BLOCKS; ++b) {
+ CMutableTransaction tx;
+ tx.vin.push_back(MineBlock(SCRIPT_PUB));
+ tx.vin.back().scriptWitness = witness;
+ tx.vout.emplace_back(1337, SCRIPT_PUB);
+ if (NUM_BLOCKS - b >= COINBASE_MATURITY)
+ txs.at(b) = MakeTransactionRef(tx);
+ }
+ for (const auto& txr : txs) {
+ CValidationState state;
+ bool ret{::AcceptToMemoryPool(::mempool, state, txr, nullptr /* pfMissingInputs */, nullptr /* plTxnReplaced */, false /* bypass_limits */, /* nAbsurdFee */ 0)};
+ assert(ret);
+ }
+
+ while (state.KeepRunning()) {
+ PrepareBlock(SCRIPT_PUB);
+ }
+
+ thread_group.interrupt_all();
+ thread_group.join_all();
+ GetMainSignals().FlushBackgroundCallbacks();
+ GetMainSignals().UnregisterBackgroundSignalScheduler();
+}
+
+BENCHMARK(AssembleBlock, 700);
diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp
index 64ec056c4d..f3180809b5 100644
--- a/src/bench/coin_selection.cpp
+++ b/src/bench/coin_selection.cpp
@@ -34,31 +34,25 @@ static void addCoin(const CAmount& nValue, const CWallet& wallet, std::vector<CO
static void CoinSelection(benchmark::State& state)
{
const CWallet wallet("dummy", WalletDatabase::CreateDummy());
- std::vector<COutput> vCoins;
LOCK(wallet.cs_wallet);
- while (state.KeepRunning()) {
- // Add coins.
- for (int i = 0; i < 1000; i++)
- addCoin(1000 * COIN, wallet, vCoins);
- addCoin(3 * COIN, wallet, vCoins);
+ // Add coins.
+ std::vector<COutput> vCoins;
+ for (int i = 0; i < 1000; ++i) {
+ addCoin(1000 * COIN, wallet, vCoins);
+ }
+ addCoin(3 * COIN, wallet, vCoins);
+ const CoinEligibilityFilter filter_standard(1, 6, 0);
+ const CoinSelectionParams coin_selection_params(true, 34, 148, CFeeRate(0), 0);
+ while (state.KeepRunning()) {
std::set<CInputCoin> setCoinsRet;
CAmount nValueRet;
bool bnb_used;
- CoinEligibilityFilter filter_standard(1, 6, 0);
- CoinSelectionParams coin_selection_params(false, 34, 148, CFeeRate(0), 0);
- bool success = wallet.SelectCoinsMinConf(1003 * COIN, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)
- || wallet.SelectCoinsMinConf(1003 * COIN, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used);
+ bool success = wallet.SelectCoinsMinConf(1003 * COIN, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used);
assert(success);
assert(nValueRet == 1003 * COIN);
assert(setCoinsRet.size() == 2);
-
- // Empty wallet.
- for (COutput& output : vCoins) {
- delete output.tx;
- }
- vCoins.clear();
}
}
diff --git a/src/bench/prevector.cpp b/src/bench/prevector.cpp
index 3cfad1b2c4..09c7020848 100644
--- a/src/bench/prevector.cpp
+++ b/src/bench/prevector.cpp
@@ -42,7 +42,7 @@ static void PrevectorClear(benchmark::State& state)
t0.resize(28);
t0.clear();
t1.resize(29);
- t0.clear();
+ t1.clear();
}
}
}
@@ -64,11 +64,11 @@ static void PrevectorResize(benchmark::State& state)
#define PREVECTOR_TEST(name, nontrivops, trivops) \
static void Prevector ## name ## Nontrivial(benchmark::State& state) { \
- PrevectorResize<nontrivial_t>(state); \
+ Prevector ## name<nontrivial_t>(state); \
} \
BENCHMARK(Prevector ## name ## Nontrivial, nontrivops); \
static void Prevector ## name ## Trivial(benchmark::State& state) { \
- PrevectorResize<trivial_t>(state); \
+ Prevector ## name<trivial_t>(state); \
} \
BENCHMARK(Prevector ## name ## Trivial, trivops);
diff --git a/src/bench/verify_script.cpp b/src/bench/verify_script.cpp
index 4100519d48..ae60588c2d 100644
--- a/src/bench/verify_script.cpp
+++ b/src/bench/verify_script.cpp
@@ -9,6 +9,7 @@
#endif
#include <script/script.h>
#include <script/sign.h>
+#include <script/standard.h>
#include <streams.h>
#include <array>
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index e6eb723cf4..181e2bb1bc 100644
--- a/src/bitcoin-tx.cpp
+++ b/src/bitcoin-tx.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2009-2017 The Bitcoin Core developers
+// Copyright (c) 2009-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -193,18 +193,18 @@ static CAmount ExtractAndValidateValue(const std::string& strValue)
static void MutateTxVersion(CMutableTransaction& tx, const std::string& cmdVal)
{
- int64_t newVersion = atoi64(cmdVal);
- if (newVersion < 1 || newVersion > CTransaction::MAX_STANDARD_VERSION)
- throw std::runtime_error("Invalid TX version requested");
+ int64_t newVersion;
+ if (!ParseInt64(cmdVal, &newVersion) || newVersion < 1 || newVersion > CTransaction::MAX_STANDARD_VERSION)
+ throw std::runtime_error("Invalid TX version requested: '" + cmdVal + "'");
tx.nVersion = (int) newVersion;
}
static void MutateTxLocktime(CMutableTransaction& tx, const std::string& cmdVal)
{
- int64_t newLocktime = atoi64(cmdVal);
- if (newLocktime < 0LL || newLocktime > 0xffffffffLL)
- throw std::runtime_error("Invalid TX locktime requested");
+ int64_t newLocktime;
+ if (!ParseInt64(cmdVal, &newLocktime) || newLocktime < 0LL || newLocktime > 0xffffffffLL)
+ throw std::runtime_error("Invalid TX locktime requested: '" + cmdVal + "'");
tx.nLockTime = (unsigned int) newLocktime;
}
@@ -212,8 +212,8 @@ static void MutateTxLocktime(CMutableTransaction& tx, const std::string& cmdVal)
static void MutateTxRBFOptIn(CMutableTransaction& tx, const std::string& strInIdx)
{
// parse requested index
- int inIdx = atoi(strInIdx);
- if (inIdx < 0 || inIdx >= (int)tx.vin.size()) {
+ int64_t inIdx;
+ if (!ParseInt64(strInIdx, &inIdx) || inIdx < 0 || inIdx >= static_cast<int64_t>(tx.vin.size())) {
throw std::runtime_error("Invalid TX input index '" + strInIdx + "'");
}
@@ -248,10 +248,10 @@ static void MutateTxAddInput(CMutableTransaction& tx, const std::string& strInpu
static const unsigned int maxVout = MAX_BLOCK_WEIGHT / (WITNESS_SCALE_FACTOR * minTxOutSz);
// extract and validate vout
- std::string strVout = vStrInputParts[1];
- int vout = atoi(strVout);
- if ((vout < 0) || (vout > (int)maxVout))
- throw std::runtime_error("invalid TX input vout");
+ const std::string& strVout = vStrInputParts[1];
+ int64_t vout;
+ if (!ParseInt64(strVout, &vout) || vout < 0 || vout > static_cast<int64_t>(maxVout))
+ throw std::runtime_error("invalid TX input vout '" + strVout + "'");
// extract the optional sequence number
uint32_t nSequenceIn=std::numeric_limits<unsigned int>::max();
@@ -481,10 +481,9 @@ static void MutateTxAddOutScript(CMutableTransaction& tx, const std::string& str
static void MutateTxDelInput(CMutableTransaction& tx, const std::string& strInIdx)
{
// parse requested deletion index
- int inIdx = atoi(strInIdx);
- if (inIdx < 0 || inIdx >= (int)tx.vin.size()) {
- std::string strErr = "Invalid TX input index '" + strInIdx + "'";
- throw std::runtime_error(strErr.c_str());
+ int64_t inIdx;
+ if (!ParseInt64(strInIdx, &inIdx) || inIdx < 0 || inIdx >= static_cast<int64_t>(tx.vin.size())) {
+ throw std::runtime_error("Invalid TX input index '" + strInIdx + "'");
}
// delete input from transaction
@@ -494,10 +493,9 @@ static void MutateTxDelInput(CMutableTransaction& tx, const std::string& strInId
static void MutateTxDelOutput(CMutableTransaction& tx, const std::string& strOutIdx)
{
// parse requested deletion index
- int outIdx = atoi(strOutIdx);
- if (outIdx < 0 || outIdx >= (int)tx.vout.size()) {
- std::string strErr = "Invalid TX output index '" + strOutIdx + "'";
- throw std::runtime_error(strErr.c_str());
+ int64_t outIdx;
+ if (!ParseInt64(strOutIdx, &outIdx) || outIdx < 0 || outIdx >= static_cast<int64_t>(tx.vout.size())) {
+ throw std::runtime_error("Invalid TX output index '" + strOutIdx + "'");
}
// delete output from transaction
@@ -593,7 +591,7 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr)
uint256 txid = ParseHashStr(prevOut["txid"].get_str(), "txid");
- int nOut = atoi(prevOut["vout"].getValStr());
+ const int nOut = prevOut["vout"].get_int();
if (nOut < 0)
throw std::runtime_error("vout must be positive");
@@ -645,13 +643,11 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr)
const CScript& prevPubKey = coin.out.scriptPubKey;
const CAmount& amount = coin.out.nValue;
- SignatureData sigdata;
+ SignatureData sigdata = DataFromTransaction(mergedTx, i, coin.out);
// Only sign SIGHASH_SINGLE if there's a corresponding output:
if (!fHashSingle || (i < mergedTx.vout.size()))
ProduceSignature(keystore, MutableTransactionSignatureCreator(&mergedTx, i, amount, nHashType), prevPubKey, sigdata);
- // ... and merge in other signatures:
- sigdata = CombineSignatures(prevPubKey, MutableTransactionSignatureChecker(&mergedTx, i, amount), sigdata, DataFromTransaction(txv, i));
UpdateInput(txin, sigdata);
}
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index 4b9abb2a1b..494a925a79 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2017 The Bitcoin Core developers
+// Copyright (c) 2009-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -14,6 +14,7 @@
#include <rpc/server.h>
#include <init.h>
#include <noui.h>
+#include <shutdown.h>
#include <util.h>
#include <httpserver.h>
#include <httprpc.h>
diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp
index 787d8d8f6a..da9f3e3209 100644
--- a/src/chainparamsbase.cpp
+++ b/src/chainparamsbase.cpp
@@ -7,6 +7,7 @@
#include <tinyformat.h>
#include <util.h>
+#include <utilmemory.h>
#include <assert.h>
diff --git a/src/compat/glibc_compat.cpp b/src/compat/glibc_compat.cpp
index 55da5ef63f..b1cbe9f72a 100644
--- a/src/compat/glibc_compat.cpp
+++ b/src/compat/glibc_compat.cpp
@@ -7,6 +7,7 @@
#endif
#include <cstddef>
+#include <cstdint>
#if defined(HAVE_SYS_SELECT_H)
#include <sys/select.h>
@@ -27,3 +28,47 @@ extern "C" FDELT_TYPE __fdelt_warn(FDELT_TYPE a)
return a / __NFDBITS;
}
extern "C" FDELT_TYPE __fdelt_chk(FDELT_TYPE) __attribute__((weak, alias("__fdelt_warn")));
+
+#if defined(__i386__) || defined(__arm__)
+
+extern "C" int64_t __udivmoddi4(uint64_t u, uint64_t v, uint64_t* rp);
+
+extern "C" int64_t __wrap___divmoddi4(int64_t u, int64_t v, int64_t* rp)
+{
+ int32_t c1 = 0, c2 = 0;
+ int64_t uu = u, vv = v;
+ int64_t w;
+ int64_t r;
+
+ if (uu < 0) {
+ c1 = ~c1, c2 = ~c2, uu = -uu;
+ }
+ if (vv < 0) {
+ c1 = ~c1, vv = -vv;
+ }
+
+ w = __udivmoddi4(uu, vv, (uint64_t*)&r);
+ if (c1)
+ w = -w;
+ if (c2)
+ r = -r;
+
+ *rp = r;
+ return w;
+}
+#endif
+
+extern "C" float log2f_old(float x);
+#ifdef __i386__
+__asm(".symver log2f_old,log2f@GLIBC_2.1");
+#elif defined(__amd64__)
+__asm(".symver log2f_old,log2f@GLIBC_2.2.5");
+#elif defined(__arm__)
+__asm(".symver log2f_old,log2f@GLIBC_2.4");
+#elif defined(__aarch64__)
+__asm(".symver log2f_old,log2f@GLIBC_2.17");
+#endif
+extern "C" float __wrap_log2f(float x)
+{
+ return log2f_old(x);
+}
diff --git a/src/core_io.h b/src/core_io.h
index 1d87d21d40..ebddcc2a04 100644
--- a/src/core_io.h
+++ b/src/core_io.h
@@ -14,6 +14,7 @@ class CBlock;
class CScript;
class CTransaction;
struct CMutableTransaction;
+struct PartiallySignedTransaction;
class uint256;
class UniValue;
@@ -24,12 +25,16 @@ bool DecodeHexTx(CMutableTransaction& tx, const std::string& hex_tx, bool try_no
bool DecodeHexBlk(CBlock&, const std::string& strHexBlk);
uint256 ParseHashStr(const std::string&, const std::string& strName);
std::vector<unsigned char> ParseHexUV(const UniValue& v, const std::string& strName);
+bool DecodePSBT(PartiallySignedTransaction& psbt, const std::string& base64_tx, std::string& error);
+int ParseSighashString(const UniValue& sighash);
// core_write.cpp
UniValue ValueFromAmount(const CAmount& amount);
std::string FormatScript(const CScript& script);
std::string EncodeHexTx(const CTransaction& tx, const int serializeFlags = 0);
+std::string SighashToStr(unsigned char sighash_type);
void ScriptPubKeyToUniv(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex);
+void ScriptToUniv(const CScript& script, UniValue& out, bool include_address);
void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, bool include_hex = true, int serialize_flags = 0);
#endif // BITCOIN_CORE_IO_H
diff --git a/src/core_read.cpp b/src/core_read.cpp
index 4d851610ef..067e1b91bd 100644
--- a/src/core_read.cpp
+++ b/src/core_read.cpp
@@ -7,6 +7,7 @@
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <script/script.h>
+#include <script/sign.h>
#include <serialize.h>
#include <streams.h>
#include <univalue.h>
@@ -160,6 +161,23 @@ bool DecodeHexBlk(CBlock& block, const std::string& strHexBlk)
return true;
}
+bool DecodePSBT(PartiallySignedTransaction& psbt, const std::string& base64_tx, std::string& error)
+{
+ std::vector<unsigned char> tx_data = DecodeBase64(base64_tx.c_str());
+ CDataStream ss_data(tx_data, SER_NETWORK, PROTOCOL_VERSION);
+ try {
+ ss_data >> psbt;
+ if (!ss_data.empty()) {
+ error = "extra data after PSBT";
+ return false;
+ }
+ } catch (const std::exception& e) {
+ error = e.what();
+ return false;
+ }
+ return true;
+}
+
uint256 ParseHashStr(const std::string& strHex, const std::string& strName)
{
if (!IsHex(strHex)) // Note: IsHex("") is false
@@ -179,3 +197,26 @@ std::vector<unsigned char> ParseHexUV(const UniValue& v, const std::string& strN
throw std::runtime_error(strName + " must be hexadecimal string (not '" + strHex + "')");
return ParseHex(strHex);
}
+
+int ParseSighashString(const UniValue& sighash)
+{
+ int hash_type = SIGHASH_ALL;
+ if (!sighash.isNull()) {
+ static std::map<std::string, int> map_sighash_values = {
+ {std::string("ALL"), int(SIGHASH_ALL)},
+ {std::string("ALL|ANYONECANPAY"), int(SIGHASH_ALL|SIGHASH_ANYONECANPAY)},
+ {std::string("NONE"), int(SIGHASH_NONE)},
+ {std::string("NONE|ANYONECANPAY"), int(SIGHASH_NONE|SIGHASH_ANYONECANPAY)},
+ {std::string("SINGLE"), int(SIGHASH_SINGLE)},
+ {std::string("SINGLE|ANYONECANPAY"), int(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY)},
+ };
+ std::string strHashType = sighash.get_str();
+ const auto& it = map_sighash_values.find(strHashType);
+ if (it != map_sighash_values.end()) {
+ hash_type = it->second;
+ } else {
+ throw std::runtime_error(strHashType + " is not a valid sighash parameter.");
+ }
+ }
+ return hash_type;
+}
diff --git a/src/core_write.cpp b/src/core_write.cpp
index ee6737201b..1272266235 100644
--- a/src/core_write.cpp
+++ b/src/core_write.cpp
@@ -70,6 +70,13 @@ const std::map<unsigned char, std::string> mapSigHashTypes = {
{static_cast<unsigned char>(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY), std::string("SINGLE|ANYONECANPAY")},
};
+std::string SighashToStr(unsigned char sighash_type)
+{
+ const auto& it = mapSigHashTypes.find(sighash_type);
+ if (it == mapSigHashTypes.end()) return "";
+ return it->second;
+}
+
/**
* Create the assembly string representation of a CScript object.
* @param[in] script CScript object to convert into the asm string representation.
@@ -128,6 +135,22 @@ std::string EncodeHexTx(const CTransaction& tx, const int serializeFlags)
return HexStr(ssTx.begin(), ssTx.end());
}
+void ScriptToUniv(const CScript& script, UniValue& out, bool include_address)
+{
+ out.pushKV("asm", ScriptToAsmStr(script));
+ out.pushKV("hex", HexStr(script.begin(), script.end()));
+
+ std::vector<std::vector<unsigned char>> solns;
+ txnouttype type;
+ Solver(script, type, solns);
+ out.pushKV("type", GetTxnOutputType(type));
+
+ CTxDestination address;
+ if (include_address && ExtractDestination(script, address)) {
+ out.pushKV("address", EncodeDestination(address));
+ }
+}
+
void ScriptPubKeyToUniv(const CScript& scriptPubKey,
UniValue& out, bool fIncludeHex)
{
diff --git a/src/crypto/sha256.cpp b/src/crypto/sha256.cpp
index 100d0d1fac..fbdbef0bc6 100644
--- a/src/crypto/sha256.cpp
+++ b/src/crypto/sha256.cpp
@@ -9,7 +9,7 @@
#include <string.h>
#include <atomic>
-#if defined(__x86_64__) || defined(__amd64__)
+#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__)
#if defined(USE_ASM)
#include <cpuid.h>
namespace sha256_sse4
@@ -29,6 +29,16 @@ namespace sha256d64_avx2
void Transform_8way(unsigned char* out, const unsigned char* in);
}
+namespace sha256d64_shani
+{
+void Transform_2way(unsigned char* out, const unsigned char* in);
+}
+
+namespace sha256_shani
+{
+void Transform(uint32_t* s, const unsigned char* chunk, size_t blocks);
+}
+
// Internal implementation code.
namespace
{
@@ -448,6 +458,7 @@ void TransformD64Wrapper(unsigned char* out, const unsigned char* in)
TransformType Transform = sha256::Transform;
TransformD64Type TransformD64 = sha256::TransformD64;
+TransformD64Type TransformD64_2way = nullptr;
TransformD64Type TransformD64_4way = nullptr;
TransformD64Type TransformD64_8way = nullptr;
@@ -512,6 +523,13 @@ bool SelfTest() {
TransformD64(out, data + 1);
if (!std::equal(out, out + 32, result_d64)) return false;
+ // Test TransformD64_2way, if available.
+ if (TransformD64_2way) {
+ unsigned char out[64];
+ TransformD64_2way(out, data + 1);
+ if (!std::equal(out, out + 64, result_d64)) return false;
+ }
+
// Test TransformD64_4way, if available.
if (TransformD64_4way) {
unsigned char out[128];
@@ -534,7 +552,19 @@ bool SelfTest() {
// We can't use cpuid.h's __get_cpuid as it does not support subleafs.
void inline cpuid(uint32_t leaf, uint32_t subleaf, uint32_t& a, uint32_t& b, uint32_t& c, uint32_t& d)
{
+#ifdef __GNUC__
+ __cpuid_count(leaf, subleaf, a, b, c, d);
+#else
__asm__ ("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "0"(leaf), "2"(subleaf));
+#endif
+}
+
+/** Check whether the OS has enabled AVX registers. */
+bool AVXEnabled()
+{
+ uint32_t a, d;
+ __asm__("xgetbv" : "=a"(a), "=d"(d) : "c"(0));
+ return (a & 6) == 6;
}
#endif
} // namespace
@@ -544,27 +574,64 @@ std::string SHA256AutoDetect()
{
std::string ret = "standard";
#if defined(USE_ASM) && (defined(__x86_64__) || defined(__amd64__) || defined(__i386__))
+ bool have_sse4 = false;
+ bool have_xsave = false;
+ bool have_avx = false;
+ bool have_avx2 = false;
+ bool have_shani = false;
+ bool enabled_avx = false;
+
+ (void)AVXEnabled;
+ (void)have_sse4;
+ (void)have_avx;
+ (void)have_xsave;
+ (void)have_avx2;
+ (void)have_shani;
+ (void)enabled_avx;
+
uint32_t eax, ebx, ecx, edx;
cpuid(1, 0, eax, ebx, ecx, edx);
- if ((ecx >> 19) & 1) {
+ have_sse4 = (ecx >> 19) & 1;
+ have_xsave = (ecx >> 27) & 1;
+ have_avx = (ecx >> 28) & 1;
+ if (have_xsave && have_avx) {
+ enabled_avx = AVXEnabled();
+ }
+ if (have_sse4) {
+ cpuid(7, 0, eax, ebx, ecx, edx);
+ have_avx2 = (ebx >> 5) & 1;
+ have_shani = (ebx >> 29) & 1;
+ }
+
+#if defined(ENABLE_SHANI) && !defined(BUILD_BITCOIN_INTERNAL)
+ if (have_shani) {
+ Transform = sha256_shani::Transform;
+ TransformD64 = TransformD64Wrapper<sha256_shani::Transform>;
+ TransformD64_2way = sha256d64_shani::Transform_2way;
+ ret = "shani(1way,2way)";
+ have_sse4 = false; // Disable SSE4/AVX2;
+ have_avx2 = false;
+ }
+#endif
+
+ if (have_sse4) {
#if defined(__x86_64__) || defined(__amd64__)
Transform = sha256_sse4::Transform;
TransformD64 = TransformD64Wrapper<sha256_sse4::Transform>;
+ ret = "sse4(1way)";
#endif
#if defined(ENABLE_SSE41) && !defined(BUILD_BITCOIN_INTERNAL)
TransformD64_4way = sha256d64_sse41::Transform_4way;
- ret = "sse4(1way+4way)";
-#if defined(ENABLE_AVX2) && !defined(BUILD_BITCOIN_INTERNAL)
- cpuid(7, 0, eax, ebx, ecx, edx);
- if ((ebx >> 5) & 1) {
- TransformD64_8way = sha256d64_avx2::Transform_8way;
- ret += ",avx2(8way)";
- }
-#endif
-#else
- ret = "sse4";
+ ret += ",sse41(4way)";
#endif
}
+
+#if defined(ENABLE_AVX2) && !defined(BUILD_BITCOIN_INTERNAL)
+ if (have_avx2 && have_avx && enabled_avx) {
+ TransformD64_8way = sha256d64_avx2::Transform_8way;
+ ret += ",avx2(8way)";
+ }
+#endif
#endif
assert(SelfTest());
@@ -646,6 +713,14 @@ void SHA256D64(unsigned char* out, const unsigned char* in, size_t blocks)
blocks -= 4;
}
}
+ if (TransformD64_2way) {
+ while (blocks >= 2) {
+ TransformD64_2way(out, in);
+ out += 64;
+ in += 128;
+ blocks -= 2;
+ }
+ }
while (blocks) {
TransformD64(out, in);
out += 32;
diff --git a/src/crypto/sha256_avx2.cpp b/src/crypto/sha256_avx2.cpp
index b338b06927..068e0e5ff6 100644
--- a/src/crypto/sha256_avx2.cpp
+++ b/src/crypto/sha256_avx2.cpp
@@ -1,11 +1,7 @@
#ifdef ENABLE_AVX2
#include <stdint.h>
-#if defined(_MSC_VER)
#include <immintrin.h>
-#elif defined(__GNUC__)
-#include <x86intrin.h>
-#endif
#include <crypto/sha256.h>
#include <crypto/common.h>
diff --git a/src/crypto/sha256_shani.cpp b/src/crypto/sha256_shani.cpp
new file mode 100644
index 0000000000..e561da42c5
--- /dev/null
+++ b/src/crypto/sha256_shani.cpp
@@ -0,0 +1,359 @@
+// Copyright (c) 2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+//
+// Based on https://github.com/noloader/SHA-Intrinsics/blob/master/sha256-x86.c,
+// Written and placed in public domain by Jeffrey Walton.
+// Based on code from Intel, and by Sean Gulley for the miTLS project.
+
+#ifdef ENABLE_SHANI
+
+#include <stdint.h>
+#include <immintrin.h>
+
+#include <crypto/common.h>
+
+
+namespace {
+
+const __m128i MASK = _mm_set_epi64x(0x0c0d0e0f08090a0bULL, 0x0405060700010203ULL);
+const __m128i INIT0 = _mm_set_epi64x(0x6a09e667bb67ae85ull, 0x510e527f9b05688cull);
+const __m128i INIT1 = _mm_set_epi64x(0x3c6ef372a54ff53aull, 0x1f83d9ab5be0cd19ull);
+
+void inline __attribute__((always_inline)) QuadRound(__m128i& state0, __m128i& state1, uint64_t k1, uint64_t k0)
+{
+ const __m128i msg = _mm_set_epi64x(k1, k0);
+ state1 = _mm_sha256rnds2_epu32(state1, state0, msg);
+ state0 = _mm_sha256rnds2_epu32(state0, state1, _mm_shuffle_epi32(msg, 0x0e));
+}
+
+void inline __attribute__((always_inline)) QuadRound(__m128i& state0, __m128i& state1, __m128i m, uint64_t k1, uint64_t k0)
+{
+ const __m128i msg = _mm_add_epi32(m, _mm_set_epi64x(k1, k0));
+ state1 = _mm_sha256rnds2_epu32(state1, state0, msg);
+ state0 = _mm_sha256rnds2_epu32(state0, state1, _mm_shuffle_epi32(msg, 0x0e));
+}
+
+void inline __attribute__((always_inline)) ShiftMessageA(__m128i& m0, __m128i m1)
+{
+ m0 = _mm_sha256msg1_epu32(m0, m1);
+}
+
+void inline __attribute__((always_inline)) ShiftMessageC(__m128i& m0, __m128i m1, __m128i& m2)
+{
+ m2 = _mm_sha256msg2_epu32(_mm_add_epi32(m2, _mm_alignr_epi8(m1, m0, 4)), m1);
+}
+
+void inline __attribute__((always_inline)) ShiftMessageB(__m128i& m0, __m128i m1, __m128i& m2)
+{
+ ShiftMessageC(m0, m1, m2);
+ ShiftMessageA(m0, m1);
+}
+
+void inline __attribute__((always_inline)) Shuffle(__m128i& s0, __m128i& s1)
+{
+ const __m128i t1 = _mm_shuffle_epi32(s0, 0xB1);
+ const __m128i t2 = _mm_shuffle_epi32(s1, 0x1B);
+ s0 = _mm_alignr_epi8(t1, t2, 0x08);
+ s1 = _mm_blend_epi16(t2, t1, 0xF0);
+}
+
+void inline __attribute__((always_inline)) Unshuffle(__m128i& s0, __m128i& s1)
+{
+ const __m128i t1 = _mm_shuffle_epi32(s0, 0x1B);
+ const __m128i t2 = _mm_shuffle_epi32(s1, 0xB1);
+ s0 = _mm_blend_epi16(t1, t2, 0xF0);
+ s1 = _mm_alignr_epi8(t2, t1, 0x08);
+}
+
+__m128i inline __attribute__((always_inline)) Load(const unsigned char* in)
+{
+ return _mm_shuffle_epi8(_mm_loadu_si128((const __m128i*)in), MASK);
+}
+
+void inline __attribute__((always_inline)) Save(unsigned char* out, __m128i s)
+{
+ _mm_storeu_si128((__m128i*)out, _mm_shuffle_epi8(s, MASK));
+}
+}
+
+namespace sha256_shani {
+void Transform(uint32_t* s, const unsigned char* chunk, size_t blocks)
+{
+ __m128i m0, m1, m2, m3, s0, s1, so0, so1;
+
+ /* Load state */
+ s0 = _mm_loadu_si128((const __m128i*)s);
+ s1 = _mm_loadu_si128((const __m128i*)(s + 4));
+ Shuffle(s0, s1);
+
+ while (blocks--) {
+ /* Remember old state */
+ so0 = s0;
+ so1 = s1;
+
+ /* Load data and transform */
+ m0 = Load(chunk);
+ QuadRound(s0, s1, m0, 0xe9b5dba5b5c0fbcfull, 0x71374491428a2f98ull);
+ m1 = Load(chunk + 16);
+ QuadRound(s0, s1, m1, 0xab1c5ed5923f82a4ull, 0x59f111f13956c25bull);
+ ShiftMessageA(m0, m1);
+ m2 = Load(chunk + 32);
+ QuadRound(s0, s1, m2, 0x550c7dc3243185beull, 0x12835b01d807aa98ull);
+ ShiftMessageA(m1, m2);
+ m3 = Load(chunk + 48);
+ QuadRound(s0, s1, m3, 0xc19bf1749bdc06a7ull, 0x80deb1fe72be5d74ull);
+ ShiftMessageB(m2, m3, m0);
+ QuadRound(s0, s1, m0, 0x240ca1cc0fc19dc6ull, 0xefbe4786E49b69c1ull);
+ ShiftMessageB(m3, m0, m1);
+ QuadRound(s0, s1, m1, 0x76f988da5cb0a9dcull, 0x4a7484aa2de92c6full);
+ ShiftMessageB(m0, m1, m2);
+ QuadRound(s0, s1, m2, 0xbf597fc7b00327c8ull, 0xa831c66d983e5152ull);
+ ShiftMessageB(m1, m2, m3);
+ QuadRound(s0, s1, m3, 0x1429296706ca6351ull, 0xd5a79147c6e00bf3ull);
+ ShiftMessageB(m2, m3, m0);
+ QuadRound(s0, s1, m0, 0x53380d134d2c6dfcull, 0x2e1b213827b70a85ull);
+ ShiftMessageB(m3, m0, m1);
+ QuadRound(s0, s1, m1, 0x92722c8581c2c92eull, 0x766a0abb650a7354ull);
+ ShiftMessageB(m0, m1, m2);
+ QuadRound(s0, s1, m2, 0xc76c51A3c24b8b70ull, 0xa81a664ba2bfe8a1ull);
+ ShiftMessageB(m1, m2, m3);
+ QuadRound(s0, s1, m3, 0x106aa070f40e3585ull, 0xd6990624d192e819ull);
+ ShiftMessageB(m2, m3, m0);
+ QuadRound(s0, s1, m0, 0x34b0bcb52748774cull, 0x1e376c0819a4c116ull);
+ ShiftMessageB(m3, m0, m1);
+ QuadRound(s0, s1, m1, 0x682e6ff35b9cca4full, 0x4ed8aa4a391c0cb3ull);
+ ShiftMessageC(m0, m1, m2);
+ QuadRound(s0, s1, m2, 0x8cc7020884c87814ull, 0x78a5636f748f82eeull);
+ ShiftMessageC(m1, m2, m3);
+ QuadRound(s0, s1, m3, 0xc67178f2bef9A3f7ull, 0xa4506ceb90befffaull);
+
+ /* Combine with old state */
+ s0 = _mm_add_epi32(s0, so0);
+ s1 = _mm_add_epi32(s1, so1);
+
+ /* Advance */
+ chunk += 64;
+ }
+
+ Unshuffle(s0, s1);
+ _mm_storeu_si128((__m128i*)s, s0);
+ _mm_storeu_si128((__m128i*)(s + 4), s1);
+}
+}
+
+namespace sha256d64_shani {
+
+void Transform_2way(unsigned char* out, const unsigned char* in)
+{
+ __m128i am0, am1, am2, am3, as0, as1, aso0, aso1;
+ __m128i bm0, bm1, bm2, bm3, bs0, bs1, bso0, bso1;
+
+ /* Transform 1 */
+ bs0 = as0 = INIT0;
+ bs1 = as1 = INIT1;
+ am0 = Load(in);
+ bm0 = Load(in + 64);
+ QuadRound(as0, as1, am0, 0xe9b5dba5b5c0fbcfull, 0x71374491428a2f98ull);
+ QuadRound(bs0, bs1, bm0, 0xe9b5dba5b5c0fbcfull, 0x71374491428a2f98ull);
+ am1 = Load(in + 16);
+ bm1 = Load(in + 80);
+ QuadRound(as0, as1, am1, 0xab1c5ed5923f82a4ull, 0x59f111f13956c25bull);
+ QuadRound(bs0, bs1, bm1, 0xab1c5ed5923f82a4ull, 0x59f111f13956c25bull);
+ ShiftMessageA(am0, am1);
+ ShiftMessageA(bm0, bm1);
+ am2 = Load(in + 32);
+ bm2 = Load(in + 96);
+ QuadRound(as0, as1, am2, 0x550c7dc3243185beull, 0x12835b01d807aa98ull);
+ QuadRound(bs0, bs1, bm2, 0x550c7dc3243185beull, 0x12835b01d807aa98ull);
+ ShiftMessageA(am1, am2);
+ ShiftMessageA(bm1, bm2);
+ am3 = Load(in + 48);
+ bm3 = Load(in + 112);
+ QuadRound(as0, as1, am3, 0xc19bf1749bdc06a7ull, 0x80deb1fe72be5d74ull);
+ QuadRound(bs0, bs1, bm3, 0xc19bf1749bdc06a7ull, 0x80deb1fe72be5d74ull);
+ ShiftMessageB(am2, am3, am0);
+ ShiftMessageB(bm2, bm3, bm0);
+ QuadRound(as0, as1, am0, 0x240ca1cc0fc19dc6ull, 0xefbe4786E49b69c1ull);
+ QuadRound(bs0, bs1, bm0, 0x240ca1cc0fc19dc6ull, 0xefbe4786E49b69c1ull);
+ ShiftMessageB(am3, am0, am1);
+ ShiftMessageB(bm3, bm0, bm1);
+ QuadRound(as0, as1, am1, 0x76f988da5cb0a9dcull, 0x4a7484aa2de92c6full);
+ QuadRound(bs0, bs1, bm1, 0x76f988da5cb0a9dcull, 0x4a7484aa2de92c6full);
+ ShiftMessageB(am0, am1, am2);
+ ShiftMessageB(bm0, bm1, bm2);
+ QuadRound(as0, as1, am2, 0xbf597fc7b00327c8ull, 0xa831c66d983e5152ull);
+ QuadRound(bs0, bs1, bm2, 0xbf597fc7b00327c8ull, 0xa831c66d983e5152ull);
+ ShiftMessageB(am1, am2, am3);
+ ShiftMessageB(bm1, bm2, bm3);
+ QuadRound(as0, as1, am3, 0x1429296706ca6351ull, 0xd5a79147c6e00bf3ull);
+ QuadRound(bs0, bs1, bm3, 0x1429296706ca6351ull, 0xd5a79147c6e00bf3ull);
+ ShiftMessageB(am2, am3, am0);
+ ShiftMessageB(bm2, bm3, bm0);
+ QuadRound(as0, as1, am0, 0x53380d134d2c6dfcull, 0x2e1b213827b70a85ull);
+ QuadRound(bs0, bs1, bm0, 0x53380d134d2c6dfcull, 0x2e1b213827b70a85ull);
+ ShiftMessageB(am3, am0, am1);
+ ShiftMessageB(bm3, bm0, bm1);
+ QuadRound(as0, as1, am1, 0x92722c8581c2c92eull, 0x766a0abb650a7354ull);
+ QuadRound(bs0, bs1, bm1, 0x92722c8581c2c92eull, 0x766a0abb650a7354ull);
+ ShiftMessageB(am0, am1, am2);
+ ShiftMessageB(bm0, bm1, bm2);
+ QuadRound(as0, as1, am2, 0xc76c51A3c24b8b70ull, 0xa81a664ba2bfe8a1ull);
+ QuadRound(bs0, bs1, bm2, 0xc76c51A3c24b8b70ull, 0xa81a664ba2bfe8a1ull);
+ ShiftMessageB(am1, am2, am3);
+ ShiftMessageB(bm1, bm2, bm3);
+ QuadRound(as0, as1, am3, 0x106aa070f40e3585ull, 0xd6990624d192e819ull);
+ QuadRound(bs0, bs1, bm3, 0x106aa070f40e3585ull, 0xd6990624d192e819ull);
+ ShiftMessageB(am2, am3, am0);
+ ShiftMessageB(bm2, bm3, bm0);
+ QuadRound(as0, as1, am0, 0x34b0bcb52748774cull, 0x1e376c0819a4c116ull);
+ QuadRound(bs0, bs1, bm0, 0x34b0bcb52748774cull, 0x1e376c0819a4c116ull);
+ ShiftMessageB(am3, am0, am1);
+ ShiftMessageB(bm3, bm0, bm1);
+ QuadRound(as0, as1, am1, 0x682e6ff35b9cca4full, 0x4ed8aa4a391c0cb3ull);
+ QuadRound(bs0, bs1, bm1, 0x682e6ff35b9cca4full, 0x4ed8aa4a391c0cb3ull);
+ ShiftMessageC(am0, am1, am2);
+ ShiftMessageC(bm0, bm1, bm2);
+ QuadRound(as0, as1, am2, 0x8cc7020884c87814ull, 0x78a5636f748f82eeull);
+ QuadRound(bs0, bs1, bm2, 0x8cc7020884c87814ull, 0x78a5636f748f82eeull);
+ ShiftMessageC(am1, am2, am3);
+ ShiftMessageC(bm1, bm2, bm3);
+ QuadRound(as0, as1, am3, 0xc67178f2bef9A3f7ull, 0xa4506ceb90befffaull);
+ QuadRound(bs0, bs1, bm3, 0xc67178f2bef9A3f7ull, 0xa4506ceb90befffaull);
+ as0 = _mm_add_epi32(as0, INIT0);
+ bs0 = _mm_add_epi32(bs0, INIT0);
+ as1 = _mm_add_epi32(as1, INIT1);
+ bs1 = _mm_add_epi32(bs1, INIT1);
+
+ /* Transform 2 */
+ aso0 = as0;
+ bso0 = bs0;
+ aso1 = as1;
+ bso1 = bs1;
+ QuadRound(as0, as1, 0xe9b5dba5b5c0fbcfull, 0x71374491c28a2f98ull);
+ QuadRound(bs0, bs1, 0xe9b5dba5b5c0fbcfull, 0x71374491c28a2f98ull);
+ QuadRound(as0, as1, 0xab1c5ed5923f82a4ull, 0x59f111f13956c25bull);
+ QuadRound(bs0, bs1, 0xab1c5ed5923f82a4ull, 0x59f111f13956c25bull);
+ QuadRound(as0, as1, 0x550c7dc3243185beull, 0x12835b01d807aa98ull);
+ QuadRound(bs0, bs1, 0x550c7dc3243185beull, 0x12835b01d807aa98ull);
+ QuadRound(as0, as1, 0xc19bf3749bdc06a7ull, 0x80deb1fe72be5d74ull);
+ QuadRound(bs0, bs1, 0xc19bf3749bdc06a7ull, 0x80deb1fe72be5d74ull);
+ QuadRound(as0, as1, 0x240cf2540fe1edc6ull, 0xf0fe4786649b69c1ull);
+ QuadRound(bs0, bs1, 0x240cf2540fe1edc6ull, 0xf0fe4786649b69c1ull);
+ QuadRound(as0, as1, 0x16f988fa61b9411eull, 0x6cc984be4fe9346full);
+ QuadRound(bs0, bs1, 0x16f988fa61b9411eull, 0x6cc984be4fe9346full);
+ QuadRound(as0, as1, 0xb9d99ec7b019fc65ull, 0xa88e5a6df2c65152ull);
+ QuadRound(bs0, bs1, 0xb9d99ec7b019fc65ull, 0xa88e5a6df2c65152ull);
+ QuadRound(as0, as1, 0xc7353eb0fdb1232bull, 0xe70eeaa09a1231c3ull);
+ QuadRound(bs0, bs1, 0xc7353eb0fdb1232bull, 0xe70eeaa09a1231c3ull);
+ QuadRound(as0, as1, 0xdc1eeefd5a0f118full, 0xcb976d5f3069bad5ull);
+ QuadRound(bs0, bs1, 0xdc1eeefd5a0f118full, 0xcb976d5f3069bad5ull);
+ QuadRound(as0, as1, 0xe15d5b1658f4ca9dull, 0xde0b7a040a35b689ull);
+ QuadRound(bs0, bs1, 0xe15d5b1658f4ca9dull, 0xde0b7a040a35b689ull);
+ QuadRound(as0, as1, 0x6fab9537a507ea32ull, 0x37088980007f3e86ull);
+ QuadRound(bs0, bs1, 0x6fab9537a507ea32ull, 0x37088980007f3e86ull);
+ QuadRound(as0, as1, 0xc0bbbe37cdaa3b6dull, 0x0d8cd6f117406110ull);
+ QuadRound(bs0, bs1, 0xc0bbbe37cdaa3b6dull, 0x0d8cd6f117406110ull);
+ QuadRound(as0, as1, 0x6fd15ca70b02e931ull, 0xdb48a36383613bdaull);
+ QuadRound(bs0, bs1, 0x6fd15ca70b02e931ull, 0xdb48a36383613bdaull);
+ QuadRound(as0, as1, 0x6d4378906ed41a95ull, 0x31338431521afacaull);
+ QuadRound(bs0, bs1, 0x6d4378906ed41a95ull, 0x31338431521afacaull);
+ QuadRound(as0, as1, 0x532fb63cb5c9a0e6ull, 0x9eccabbdc39c91f2ull);
+ QuadRound(bs0, bs1, 0x532fb63cb5c9a0e6ull, 0x9eccabbdc39c91f2ull);
+ QuadRound(as0, as1, 0x4c191d76a4954b68ull, 0x07237ea3d2c741c6ull);
+ QuadRound(bs0, bs1, 0x4c191d76a4954b68ull, 0x07237ea3d2c741c6ull);
+ as0 = _mm_add_epi32(as0, aso0);
+ bs0 = _mm_add_epi32(bs0, bso0);
+ as1 = _mm_add_epi32(as1, aso1);
+ bs1 = _mm_add_epi32(bs1, bso1);
+
+ /* Extract hash */
+ Unshuffle(as0, as1);
+ Unshuffle(bs0, bs1);
+ am0 = as0;
+ bm0 = bs0;
+ am1 = as1;
+ bm1 = bs1;
+
+ /* Transform 3 */
+ bs0 = as0 = INIT0;
+ bs1 = as1 = INIT1;
+ QuadRound(as0, as1, am0, 0xe9b5dba5B5c0fbcfull, 0x71374491428a2f98ull);
+ QuadRound(bs0, bs1, bm0, 0xe9b5dba5B5c0fbcfull, 0x71374491428a2f98ull);
+ QuadRound(as0, as1, am1, 0xab1c5ed5923f82a4ull, 0x59f111f13956c25bull);
+ QuadRound(bs0, bs1, bm1, 0xab1c5ed5923f82a4ull, 0x59f111f13956c25bull);
+ ShiftMessageA(am0, am1);
+ ShiftMessageA(bm0, bm1);
+ bm2 = am2 = _mm_set_epi64x(0x0ull, 0x80000000ull);
+ QuadRound(as0, as1, 0x550c7dc3243185beull, 0x12835b015807aa98ull);
+ QuadRound(bs0, bs1, 0x550c7dc3243185beull, 0x12835b015807aa98ull);
+ ShiftMessageA(am1, am2);
+ ShiftMessageA(bm1, bm2);
+ bm3 = am3 = _mm_set_epi64x(0x10000000000ull, 0x0ull);
+ QuadRound(as0, as1, 0xc19bf2749bdc06a7ull, 0x80deb1fe72be5d74ull);
+ QuadRound(bs0, bs1, 0xc19bf2749bdc06a7ull, 0x80deb1fe72be5d74ull);
+ ShiftMessageB(am2, am3, am0);
+ ShiftMessageB(bm2, bm3, bm0);
+ QuadRound(as0, as1, am0, 0x240ca1cc0fc19dc6ull, 0xefbe4786e49b69c1ull);
+ QuadRound(bs0, bs1, bm0, 0x240ca1cc0fc19dc6ull, 0xefbe4786e49b69c1ull);
+ ShiftMessageB(am3, am0, am1);
+ ShiftMessageB(bm3, bm0, bm1);
+ QuadRound(as0, as1, am1, 0x76f988da5cb0a9dcull, 0x4a7484aa2de92c6full);
+ QuadRound(bs0, bs1, bm1, 0x76f988da5cb0a9dcull, 0x4a7484aa2de92c6full);
+ ShiftMessageB(am0, am1, am2);
+ ShiftMessageB(bm0, bm1, bm2);
+ QuadRound(as0, as1, am2, 0xbf597fc7b00327c8ull, 0xa831c66d983e5152ull);
+ QuadRound(bs0, bs1, bm2, 0xbf597fc7b00327c8ull, 0xa831c66d983e5152ull);
+ ShiftMessageB(am1, am2, am3);
+ ShiftMessageB(bm1, bm2, bm3);
+ QuadRound(as0, as1, am3, 0x1429296706ca6351ull, 0xd5a79147c6e00bf3ull);
+ QuadRound(bs0, bs1, bm3, 0x1429296706ca6351ull, 0xd5a79147c6e00bf3ull);
+ ShiftMessageB(am2, am3, am0);
+ ShiftMessageB(bm2, bm3, bm0);
+ QuadRound(as0, as1, am0, 0x53380d134d2c6dfcull, 0x2e1b213827b70a85ull);
+ QuadRound(bs0, bs1, bm0, 0x53380d134d2c6dfcull, 0x2e1b213827b70a85ull);
+ ShiftMessageB(am3, am0, am1);
+ ShiftMessageB(bm3, bm0, bm1);
+ QuadRound(as0, as1, am1, 0x92722c8581c2c92eull, 0x766a0abb650a7354ull);
+ QuadRound(bs0, bs1, bm1, 0x92722c8581c2c92eull, 0x766a0abb650a7354ull);
+ ShiftMessageB(am0, am1, am2);
+ ShiftMessageB(bm0, bm1, bm2);
+ QuadRound(as0, as1, am2, 0xc76c51a3c24b8b70ull, 0xa81a664ba2bfe8A1ull);
+ QuadRound(bs0, bs1, bm2, 0xc76c51a3c24b8b70ull, 0xa81a664ba2bfe8A1ull);
+ ShiftMessageB(am1, am2, am3);
+ ShiftMessageB(bm1, bm2, bm3);
+ QuadRound(as0, as1, am3, 0x106aa070f40e3585ull, 0xd6990624d192e819ull);
+ QuadRound(bs0, bs1, bm3, 0x106aa070f40e3585ull, 0xd6990624d192e819ull);
+ ShiftMessageB(am2, am3, am0);
+ ShiftMessageB(bm2, bm3, bm0);
+ QuadRound(as0, as1, am0, 0x34b0bcb52748774cull, 0x1e376c0819a4c116ull);
+ QuadRound(bs0, bs1, bm0, 0x34b0bcb52748774cull, 0x1e376c0819a4c116ull);
+ ShiftMessageB(am3, am0, am1);
+ ShiftMessageB(bm3, bm0, bm1);
+ QuadRound(as0, as1, am1, 0x682e6ff35b9cca4full, 0x4ed8aa4a391c0cb3ull);
+ QuadRound(bs0, bs1, bm1, 0x682e6ff35b9cca4full, 0x4ed8aa4a391c0cb3ull);
+ ShiftMessageC(am0, am1, am2);
+ ShiftMessageC(bm0, bm1, bm2);
+ QuadRound(as0, as1, am2, 0x8cc7020884c87814ull, 0x78a5636f748f82eeull);
+ QuadRound(bs0, bs1, bm2, 0x8cc7020884c87814ull, 0x78a5636f748f82eeull);
+ ShiftMessageC(am1, am2, am3);
+ ShiftMessageC(bm1, bm2, bm3);
+ QuadRound(as0, as1, am3, 0xc67178f2bef9a3f7ull, 0xa4506ceb90befffaull);
+ QuadRound(bs0, bs1, bm3, 0xc67178f2bef9a3f7ull, 0xa4506ceb90befffaull);
+ as0 = _mm_add_epi32(as0, INIT0);
+ bs0 = _mm_add_epi32(bs0, INIT0);
+ as1 = _mm_add_epi32(as1, INIT1);
+ bs1 = _mm_add_epi32(bs1, INIT1);
+
+ /* Extract hash into out */
+ Unshuffle(as0, as1);
+ Unshuffle(bs0, bs1);
+ Save(out, as0);
+ Save(out + 16, as1);
+ Save(out + 32, bs0);
+ Save(out + 48, bs1);
+}
+
+}
+
+#endif
diff --git a/src/crypto/sha256_sse41.cpp b/src/crypto/sha256_sse41.cpp
index be71dd8fb8..adca870e2d 100644
--- a/src/crypto/sha256_sse41.cpp
+++ b/src/crypto/sha256_sse41.cpp
@@ -1,11 +1,7 @@
#ifdef ENABLE_SSE41
#include <stdint.h>
-#if defined(_MSC_VER)
#include <immintrin.h>
-#elif defined(__GNUC__)
-#include <x86intrin.h>
-#endif
#include <crypto/sha256.h>
#include <crypto/common.h>
diff --git a/src/httprpc.cpp b/src/httprpc.cpp
index de2437943e..97b152b11b 100644
--- a/src/httprpc.cpp
+++ b/src/httprpc.cpp
@@ -215,7 +215,7 @@ static bool InitRPCAuthentication()
{
if (gArgs.GetArg("-rpcpassword", "") == "")
{
- LogPrintf("No rpcpassword set - using random cookie authentication\n");
+ LogPrintf("No rpcpassword set - using random cookie authentication.\n");
if (!GenerateAuthCookie(&strRPCUserColonPass)) {
uiInterface.ThreadSafeMessageBox(
_("Error: A fatal internal error occurred, see debug.log for details"), // Same message as AbortNode
@@ -223,9 +223,13 @@ static bool InitRPCAuthentication()
return false;
}
} else {
- LogPrintf("Config options rpcuser and rpcpassword will soon be deprecated. Locally-run instances may remove rpcuser to use cookie-based auth, or may be replaced with rpcauth. Please see share/rpcuser for rpcauth auth generation.\n");
+ LogPrintf("Config options rpcuser and rpcpassword will soon be deprecated. Locally-run instances may remove rpcuser to use cookie-based auth, or may be replaced with rpcauth. Please see share/rpcauth for rpcauth auth generation.\n");
strRPCUserColonPass = gArgs.GetArg("-rpcuser", "") + ":" + gArgs.GetArg("-rpcpassword", "");
}
+ if (gArgs.GetArg("-rpcauth","") != "")
+ {
+ LogPrintf("Using rpcauth authentication.\n");
+ }
return true;
}
diff --git a/src/index/base.cpp b/src/index/base.cpp
index 738166dc94..788f7adccd 100644
--- a/src/index/base.cpp
+++ b/src/index/base.cpp
@@ -4,7 +4,7 @@
#include <chainparams.h>
#include <index/base.h>
-#include <init.h>
+#include <shutdown.h>
#include <tinyformat.h>
#include <ui_interface.h>
#include <util.h>
diff --git a/src/index/txindex.cpp b/src/index/txindex.cpp
index e106b9b420..c85030e18e 100644
--- a/src/index/txindex.cpp
+++ b/src/index/txindex.cpp
@@ -3,7 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <index/txindex.h>
-#include <init.h>
+#include <shutdown.h>
#include <ui_interface.h>
#include <util.h>
#include <validation.h>
diff --git a/src/init.cpp b/src/init.cpp
index 1b5507703c..c7ea40817d 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2017 The Bitcoin Core developers
+// Copyright (c) 2009-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -35,6 +35,7 @@
#include <script/standard.h>
#include <script/sigcache.h>
#include <scheduler.h>
+#include <shutdown.h>
#include <timedata.h>
#include <txdb.h>
#include <txmempool.h>
@@ -62,6 +63,7 @@
#if ENABLE_ZMQ
#include <zmq/zmqnotificationinterface.h>
+#include <zmq/zmqrpc.h>
#endif
bool fFeeEstimatesInitialized = false;
@@ -99,10 +101,6 @@ void DummyWalletInit::AddWalletOptions() const
const WalletInitInterface& g_wallet_init_interface = DummyWalletInit();
#endif
-#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
@@ -126,7 +124,7 @@ static const char* FEE_ESTIMATES_FILENAME="fee_estimates.dat";
// created by AppInit() or the Qt main() function.
//
// A clean exit happens when StartShutdown() or the SIGTERM
-// signal handler sets fRequestShutdown, which makes main thread's
+// signal handler sets ShutdownRequested(), which makes main thread's
// WaitForShutdown() interrupts the thread group.
// And then, WaitForShutdown() makes all other on-going threads
// in the thread group join the main thread.
@@ -135,21 +133,10 @@ static const char* FEE_ESTIMATES_FILENAME="fee_estimates.dat";
// threads have exited.
//
// Shutdown for Qt is very similar, only it uses a QTimer to detect
-// fRequestShutdown getting set, and then does the normal Qt
+// ShutdownRequested() getting set, and then does the normal Qt
// shutdown thing.
//
-std::atomic<bool> fRequestShutdown(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
@@ -279,10 +266,10 @@ void Shutdown()
g_wallet_init_interface.Stop();
#if ENABLE_ZMQ
- if (pzmqNotificationInterface) {
- UnregisterValidationInterface(pzmqNotificationInterface);
- delete pzmqNotificationInterface;
- pzmqNotificationInterface = nullptr;
+ if (g_zmq_notification_interface) {
+ UnregisterValidationInterface(g_zmq_notification_interface);
+ delete g_zmq_notification_interface;
+ g_zmq_notification_interface = nullptr;
}
#endif
@@ -310,7 +297,7 @@ void Shutdown()
#ifndef WIN32
static void HandleSIGTERM(int)
{
- fRequestShutdown = true;
+ StartShutdown();
}
static void HandleSIGHUP(int)
@@ -320,7 +307,7 @@ static void HandleSIGHUP(int)
#else
static BOOL WINAPI consoleCtrlHandler(DWORD dwCtrlType)
{
- fRequestShutdown = true;
+ StartShutdown();
Sleep(INFINITE);
return true;
}
@@ -377,7 +364,7 @@ void SetupServerArgs()
gArgs.AddArg("-datadir=<dir>", "Specify data directory", false, OptionsCategory::OPTIONS);
gArgs.AddArg("-dbbatchsize", strprintf("Maximum database write batch size in bytes (default: %u)", nDefaultDbBatchSize), true, OptionsCategory::OPTIONS);
gArgs.AddArg("-dbcache=<n>", strprintf("Set database cache size in megabytes (%d to %d, default: %d)", nMinDbCache, nMaxDbCache, nDefaultDbCache), false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-debuglogfile=<file>", strprintf("Specify location of debug log file. Relative paths will be prefixed by a net-specific datadir location. (default: %s)", DEFAULT_DEBUGLOGFILE), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-debuglogfile=<file>", strprintf("Specify location of debug log file. Relative paths will be prefixed by a net-specific datadir location. (0 to disable; default: %s)", DEFAULT_DEBUGLOGFILE), false, OptionsCategory::OPTIONS);
gArgs.AddArg("-feefilter", strprintf("Tell other nodes to filter invs to us by our mempool min fee (default: %u)", DEFAULT_FEEFILTER), true, OptionsCategory::OPTIONS);
gArgs.AddArg("-includeconf=<file>", "Specify additional configuration file, relative to the -datadir path (only useable from configuration file, not command line)", false, OptionsCategory::OPTIONS);
gArgs.AddArg("-loadblock=<file>", "Imports blocks from external blk000??.dat file on startup", false, OptionsCategory::OPTIONS);
@@ -405,11 +392,11 @@ void SetupServerArgs()
#endif
gArgs.AddArg("-txindex", strprintf("Maintain a full transaction index, used by the getrawtransaction rpc call (default: %u)", DEFAULT_TXINDEX), false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-addnode=<ip>", "Add a node to connect to and attempt to keep the connection open (see the `addnode` RPC command help for more info)", false, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-addnode=<ip>", "Add a node to connect to and attempt to keep the connection open (see the `addnode` RPC command help for more info). This option can be specified multiple times to add multiple nodes.", false, OptionsCategory::CONNECTION);
gArgs.AddArg("-banscore=<n>", strprintf("Threshold for disconnecting misbehaving peers (default: %u)", DEFAULT_BANSCORE_THRESHOLD), false, OptionsCategory::CONNECTION);
gArgs.AddArg("-bantime=<n>", strprintf("Number of seconds to keep misbehaving peers from reconnecting (default: %u)", DEFAULT_MISBEHAVING_BANTIME), false, OptionsCategory::CONNECTION);
gArgs.AddArg("-bind=<addr>", "Bind to given address and always listen on it. Use [host]:port notation for IPv6", false, OptionsCategory::CONNECTION);
- gArgs.AddArg("-connect=<ip>", "Connect only to the specified node(s); -connect=0 disables automatic connections (the rules for this peer are the same as for -addnode)", false, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-connect=<ip>", "Connect only to the specified node; -connect=0 disables automatic connections (the rules for this peer are the same as for -addnode). This option can be specified multiple times to connect to multiple nodes.", false, OptionsCategory::CONNECTION);
gArgs.AddArg("-discover", "Discover own IP addresses (default: 1 when listening and no -externalip or -proxy)", false, OptionsCategory::CONNECTION);
gArgs.AddArg("-dns", strprintf("Allow DNS lookups for -addnode, -seednode and -connect (default: %u)", DEFAULT_NAME_LOOKUP), false, OptionsCategory::CONNECTION);
gArgs.AddArg("-dnsseed", "Query for peer addresses via DNS lookup, if low on addresses (default: 1 unless -connect used)", false, OptionsCategory::CONNECTION);
@@ -424,13 +411,13 @@ void SetupServerArgs()
gArgs.AddArg("-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), false, OptionsCategory::CONNECTION);
gArgs.AddArg("-maxuploadtarget=<n>", strprintf("Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = no limit (default: %d)", DEFAULT_MAX_UPLOAD_TARGET), false, OptionsCategory::CONNECTION);
gArgs.AddArg("-onion=<ip:port>", "Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: -proxy)", false, OptionsCategory::CONNECTION);
- gArgs.AddArg("-onlynet=<net>", "Only connect to nodes in network <net> (ipv4, ipv6 or onion)", false, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-onlynet=<net>", "Make outgoing connections only through network <net> (ipv4, ipv6 or onion). Incoming connections are not affected by this option. This option can be specified multiple times to allow multiple networks.", false, OptionsCategory::CONNECTION);
gArgs.AddArg("-peerbloomfilters", strprintf("Support filtering of blocks and transaction with bloom filters (default: %u)", DEFAULT_PEERBLOOMFILTERS), false, OptionsCategory::CONNECTION);
gArgs.AddArg("-permitbaremultisig", strprintf("Relay non-P2SH multisig (default: %u)", DEFAULT_PERMIT_BAREMULTISIG), false, OptionsCategory::CONNECTION);
gArgs.AddArg("-port=<port>", strprintf("Listen for connections on <port> (default: %u or testnet: %u)", defaultChainParams->GetDefaultPort(), testnetChainParams->GetDefaultPort()), false, OptionsCategory::CONNECTION);
gArgs.AddArg("-proxy=<ip:port>", "Connect through SOCKS5 proxy", false, OptionsCategory::CONNECTION);
gArgs.AddArg("-proxyrandomize", strprintf("Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)", DEFAULT_PROXYRANDOMIZE), false, OptionsCategory::CONNECTION);
- gArgs.AddArg("-seednode=<ip>", "Connect to a node to retrieve peer addresses, and disconnect", false, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-seednode=<ip>", "Connect to a node to retrieve peer addresses, and disconnect. This option can be specified multiple times to connect to multiple nodes.", false, OptionsCategory::CONNECTION);
gArgs.AddArg("-timeout=<n>", strprintf("Specify connection timeout in milliseconds (minimum: 1, default: %d)", DEFAULT_CONNECT_TIMEOUT), false, OptionsCategory::CONNECTION);
gArgs.AddArg("-torcontrol=<ip>:<port>", strprintf("Tor control port to use if onion listening enabled (default: %s)", DEFAULT_TOR_CONTROL), false, OptionsCategory::CONNECTION);
gArgs.AddArg("-torpassword=<pass>", "Tor control port password (default: empty)", false, OptionsCategory::CONNECTION);
@@ -489,7 +476,7 @@ void SetupServerArgs()
gArgs.AddArg("-maxtxfee=<amt>", 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)), false, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-printpriority", strprintf("Log transaction fee per kB when mining blocks (default: %u)", DEFAULT_PRINTPRIORITY), true, OptionsCategory::DEBUG_TEST);
- gArgs.AddArg("-printtoconsole", "Send trace/debug info to console instead of debug.log file", false, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-printtoconsole", "Send trace/debug info to console (default: 1 when no -daemon. To disable logging to file, set debuglogfile=0)", false, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-shrinkdebugfile", "Shrink debug.log file on client startup (default: 1 when no -debug)", false, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-uacomment=<cmt>", "Append comment to the user agent string", false, OptionsCategory::DEBUG_TEST);
@@ -514,7 +501,7 @@ void SetupServerArgs()
gArgs.AddArg("-rest", strprintf("Accept public REST requests (default: %u)", DEFAULT_REST_ENABLE), false, OptionsCategory::RPC);
gArgs.AddArg("-rpcallowip=<ip>", "Allow JSON-RPC connections from specified source. Valid for <ip> 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", false, OptionsCategory::RPC);
- gArgs.AddArg("-rpcauth=<userpw>", "Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. The client then connects normally using the rpcuser=<USERNAME>/rpcpassword=<PASSWORD> pair of arguments. This option can be specified multiple times", false, OptionsCategory::RPC);
+ gArgs.AddArg("-rpcauth=<userpw>", "Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcauth. The client then connects normally using the rpcuser=<USERNAME>/rpcpassword=<PASSWORD> pair of arguments. This option can be specified multiple times", false, OptionsCategory::RPC);
gArgs.AddArg("-rpcbind=<addr>[:port]", "Bind to given address to listen for JSON-RPC connections. This option is ignored unless -rpcallowip is also passed. Port is optional and overrides -rpcport. Use [host]:port notation for IPv6. This option can be specified multiple times (default: 127.0.0.1 and ::1 i.e., localhost, or if -rpcallowip has been specified, 0.0.0.0 and :: i.e., all addresses)", false, OptionsCategory::RPC);
gArgs.AddArg("-rpccookiefile=<loc>", "Location of the auth cookie. Relative paths will be prefixed by a net-specific datadir location. (default: data dir)", false, OptionsCategory::RPC);
gArgs.AddArg("-rpcpassword=<pw>", "Password for JSON-RPC connections", false, OptionsCategory::RPC);
@@ -713,7 +700,7 @@ static void ThreadImport(std::vector<fs::path> vImportFiles)
if (gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
LoadMempool();
}
- g_is_mempool_loaded = !fRequestShutdown;
+ g_is_mempool_loaded = !ShutdownRequested();
}
/** Sanity checks
@@ -961,7 +948,8 @@ bool AppInitParameterInteraction()
nMaxConnections = std::max(nUserMaxConnections, 0);
// Trim requested connection counts, to fit into system limitations
- nMaxConnections = std::max(std::min(nMaxConnections, FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS), 0);
+ // <int> in std::min<int>(...) to work around FreeBSD compilation issue described in #2695
+ nMaxConnections = std::max(std::min<int>(nMaxConnections, 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."));
@@ -1134,8 +1122,6 @@ bool AppInitParameterInteraction()
if (gArgs.GetBoolArg("-peerbloomfilters", DEFAULT_PEERBLOOMFILTERS))
nLocalServices = ServiceFlags(nLocalServices | NODE_BLOOM);
- g_enable_bip61 = gArgs.GetBoolArg("-enablebip61", DEFAULT_ENABLE_BIP61);
-
if (gArgs.GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) < 0)
return InitError("rpcserialversion must be non-negative.");
@@ -1291,6 +1277,9 @@ bool AppInitMain()
*/
RegisterAllCoreRPCCommands(tableRPC);
g_wallet_init_interface.RegisterRPC(tableRPC);
+#if ENABLE_ZMQ
+ RegisterZMQRPCCommands(tableRPC);
+#endif
/* Start the RPC server already. It will be started in "warmup" mode
* and not really process calls already (but it will signify connections
@@ -1304,8 +1293,6 @@ bool AppInitMain()
return InitError(_("Unable to start HTTP server. See debug log for details."));
}
- int64_t nStart;
-
// ********************************************************* Step 5: verify wallet database integrity
if (!g_wallet_init_interface.Verify()) return false;
@@ -1319,7 +1306,7 @@ bool AppInitMain()
g_connman = std::unique_ptr<CConnman>(new CConnman(GetRand(std::numeric_limits<uint64_t>::max()), GetRand(std::numeric_limits<uint64_t>::max())));
CConnman& connman = *g_connman;
- peerLogic.reset(new PeerLogicValidation(&connman, scheduler));
+ peerLogic.reset(new PeerLogicValidation(&connman, scheduler, gArgs.GetBoolArg("-enablebip61", DEFAULT_ENABLE_BIP61)));
RegisterValidationInterface(peerLogic.get());
// sanitize comments per BIP-0014, format user agent and check total size
@@ -1357,7 +1344,7 @@ bool AppInitMain()
// -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);
+ SetLimited(NET_ONION);
if (proxyArg != "" && proxyArg != "0") {
CService proxyAddr;
if (!Lookup(proxyArg.c_str(), proxyAddr, 9050, fNameLookup)) {
@@ -1370,9 +1357,9 @@ bool AppInitMain()
SetProxy(NET_IPV4, addrProxy);
SetProxy(NET_IPV6, addrProxy);
- SetProxy(NET_TOR, addrProxy);
+ SetProxy(NET_ONION, addrProxy);
SetNameProxy(addrProxy);
- SetLimited(NET_TOR, false); // by default, -proxy sets onion as reachable, unless -noonion later
+ SetLimited(NET_ONION, false); // by default, -proxy sets onion as reachable, unless -noonion later
}
// -onion can be used to set only a proxy for .onion, or override normal proxy for .onion addresses
@@ -1381,7 +1368,7 @@ bool AppInitMain()
std::string onionArg = gArgs.GetArg("-onion", "");
if (onionArg != "") {
if (onionArg == "0") { // Handle -noonion/-onion=0
- SetLimited(NET_TOR); // set onions as unreachable
+ SetLimited(NET_ONION); // set onions as unreachable
} else {
CService onionProxy;
if (!Lookup(onionArg.c_str(), onionProxy, 9050, fNameLookup)) {
@@ -1390,8 +1377,8 @@ bool AppInitMain()
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);
+ SetProxy(NET_ONION, addrOnion);
+ SetLimited(NET_ONION, false);
}
}
@@ -1409,10 +1396,10 @@ bool AppInitMain()
}
#if ENABLE_ZMQ
- pzmqNotificationInterface = CZMQNotificationInterface::Create();
+ g_zmq_notification_interface = CZMQNotificationInterface::Create();
- if (pzmqNotificationInterface) {
- RegisterValidationInterface(pzmqNotificationInterface);
+ if (g_zmq_notification_interface) {
+ RegisterValidationInterface(g_zmq_notification_interface);
}
#endif
uint64_t nMaxOutboundLimit = 0; //unlimited unless -maxuploadtarget is set
@@ -1449,7 +1436,7 @@ bool AppInitMain()
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));
bool fLoaded = false;
- while (!fLoaded && !fRequestShutdown) {
+ while (!fLoaded && !ShutdownRequested()) {
bool fReset = fReindex;
std::string strLoadError;
@@ -1457,8 +1444,8 @@ bool AppInitMain()
LOCK(cs_main);
- nStart = GetTimeMillis();
do {
+ const int64_t load_block_index_start_time = GetTimeMillis();
try {
UnloadBlockIndex();
pcoinsTip.reset();
@@ -1476,7 +1463,7 @@ bool AppInitMain()
CleanupBlockRevFiles();
}
- if (fRequestShutdown) break;
+ if (ShutdownRequested()) break;
// LoadBlockIndex will load fHavePruned if we've ever removed a
// block file from disk.
@@ -1581,9 +1568,10 @@ bool AppInitMain()
}
fLoaded = true;
+ LogPrintf(" block index %15dms\n", GetTimeMillis() - load_block_index_start_time);
} while(false);
- if (!fLoaded && !fRequestShutdown) {
+ if (!fLoaded && !ShutdownRequested()) {
// first suggest a reindex
if (!fReset) {
bool fRet = uiInterface.ThreadSafeQuestion(
@@ -1592,7 +1580,7 @@ bool AppInitMain()
"", CClientUIInterface::MSG_ERROR | CClientUIInterface::BTN_ABORT);
if (fRet) {
fReindex = true;
- fRequestShutdown = false;
+ AbortShutdown();
} else {
LogPrintf("Aborted block database rebuild. Exiting.\n");
return false;
@@ -1606,14 +1594,10 @@ bool AppInitMain()
// 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)
- {
+ if (ShutdownRequested()) {
LogPrintf("Shutdown requested. Exiting.\n");
return false;
}
- if (fLoaded) {
- 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);
diff --git a/src/init.h b/src/init.h
index 5423a042a6..0c85d3c9dc 100644
--- a/src/init.h
+++ b/src/init.h
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2017 The Bitcoin Core developers
+// Copyright (c) 2009-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -21,8 +21,6 @@ namespace boost
class thread_group;
} // namespace boost
-void StartShutdown();
-bool ShutdownRequested();
/** Interrupt threads */
void Interrupt();
void Shutdown();
diff --git a/src/interfaces/handler.cpp b/src/interfaces/handler.cpp
index 8e45faa2a5..80f461f4d3 100644
--- a/src/interfaces/handler.cpp
+++ b/src/interfaces/handler.cpp
@@ -4,7 +4,7 @@
#include <interfaces/handler.h>
-#include <util.h>
+#include <utilmemory.h>
#include <boost/signals2/connection.hpp>
#include <utility>
diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp
index 4189ff7497..db371d104e 100644
--- a/src/interfaces/node.cpp
+++ b/src/interfaces/node.cpp
@@ -21,6 +21,7 @@
#include <primitives/block.h>
#include <rpc/server.h>
#include <scheduler.h>
+#include <shutdown.h>
#include <sync.h>
#include <txmempool.h>
#include <ui_interface.h>
diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp
index 3029dbe8e3..b42c9b63df 100644
--- a/src/interfaces/wallet.cpp
+++ b/src/interfaces/wallet.cpp
@@ -93,7 +93,6 @@ WalletTxStatus MakeWalletTxStatus(const CWalletTx& wtx)
result.block_height = (block ? block->nHeight : std::numeric_limits<int>::max()),
result.blocks_to_maturity = wtx.GetBlocksToMaturity();
result.depth_in_main_chain = wtx.GetDepthInMainChain();
- result.request_count = wtx.GetRequestCount();
result.time_received = wtx.nTimeReceived;
result.lock_time = wtx.tx->nLockTime;
result.is_final = CheckFinalTx(*wtx.tx);
@@ -339,7 +338,7 @@ public:
result.immature_balance = m_wallet.GetImmatureBalance();
result.have_watch_only = m_wallet.HaveWatchOnly();
if (result.have_watch_only) {
- result.watch_only_balance = m_wallet.GetWatchOnlyBalance();
+ result.watch_only_balance = m_wallet.GetBalance(ISMINE_WATCH_ONLY);
result.unconfirmed_watch_only_balance = m_wallet.GetUnconfirmedWatchOnlyBalance();
result.immature_watch_only_balance = m_wallet.GetImmatureWatchOnlyBalance();
}
@@ -429,6 +428,10 @@ public:
bool hdEnabled() override { return m_wallet.IsHDEnabled(); }
OutputType getDefaultAddressType() override { return m_wallet.m_default_address_type; }
OutputType getDefaultChangeType() override { return m_wallet.m_default_change_type; }
+ std::unique_ptr<Handler> handleUnload(UnloadFn fn) override
+ {
+ return MakeHandler(m_wallet.NotifyUnload.connect(fn));
+ }
std::unique_ptr<Handler> handleShowProgress(ShowProgressFn fn) override
{
return MakeHandler(m_wallet.ShowProgress.connect(fn));
diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h
index 82ae0b14b5..96e742eaca 100644
--- a/src/interfaces/wallet.h
+++ b/src/interfaces/wallet.h
@@ -242,6 +242,10 @@ public:
// Get default change type.
virtual OutputType getDefaultChangeType() = 0;
+ //! Register handler for unload message.
+ using UnloadFn = std::function<void()>;
+ virtual std::unique_ptr<Handler> handleUnload(UnloadFn fn) = 0;
+
//! Register handler for show progress messages.
using ShowProgressFn = std::function<void(const std::string& title, int progress)>;
virtual std::unique_ptr<Handler> handleShowProgress(ShowProgressFn fn) = 0;
@@ -342,7 +346,6 @@ struct WalletTxStatus
int block_height;
int blocks_to_maturity;
int depth_in_main_chain;
- int request_count;
unsigned int time_received;
uint32_t lock_time;
bool is_final;
diff --git a/src/keystore.h b/src/keystore.h
index cd5ded9203..f64024c7e7 100644
--- a/src/keystore.h
+++ b/src/keystore.h
@@ -38,21 +38,21 @@ public:
virtual bool HaveWatchOnly() const =0;
};
-typedef std::map<CKeyID, CKey> KeyMap;
-typedef std::map<CKeyID, CPubKey> WatchKeyMap;
-typedef std::map<CScriptID, CScript > ScriptMap;
-typedef std::set<CScript> WatchOnlySet;
-
/** Basic key store, that keeps keys in an address->secret map */
class CBasicKeyStore : public CKeyStore
{
protected:
mutable CCriticalSection cs_KeyStore;
- KeyMap mapKeys;
- WatchKeyMap mapWatchKeys;
- ScriptMap mapScripts;
- WatchOnlySet setWatchOnly;
+ using KeyMap = std::map<CKeyID, CKey>;
+ using WatchKeyMap = std::map<CKeyID, CPubKey>;
+ using ScriptMap = std::map<CScriptID, CScript>;
+ using WatchOnlySet = std::set<CScript>;
+
+ KeyMap mapKeys GUARDED_BY(cs_KeyStore);
+ WatchKeyMap mapWatchKeys GUARDED_BY(cs_KeyStore);
+ ScriptMap mapScripts GUARDED_BY(cs_KeyStore);
+ WatchOnlySet setWatchOnly GUARDED_BY(cs_KeyStore);
void ImplicitlyLearnRelatedKeyScripts(const CPubKey& pubkey) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
@@ -74,9 +74,6 @@ public:
bool HaveWatchOnly() const override;
};
-typedef std::vector<unsigned char, secure_allocator<unsigned char> > CKeyingMaterial;
-typedef std::map<CKeyID, std::pair<CPubKey, std::vector<unsigned char> > > CryptedKeyMap;
-
/** Return the CKeyID of the key involved in a script (if there is a unique one). */
CKeyID GetKeyForDestination(const CKeyStore& store, const CTxDestination& dest);
diff --git a/src/merkleblock.h b/src/merkleblock.h
index 0976e21c3a..984e33a961 100644
--- a/src/merkleblock.h
+++ b/src/merkleblock.h
@@ -115,6 +115,12 @@ public:
* returns the merkle root, or 0 in case of failure
*/
uint256 ExtractMatches(std::vector<uint256> &vMatch, std::vector<unsigned int> &vnIndex);
+
+ /** Get number of transactions the merkle proof is indicating for cross-reference with
+ * local blockchain knowledge.
+ */
+ unsigned int GetNumTransactions() const { return nTransactions; };
+
};
diff --git a/src/miner.cpp b/src/miner.cpp
index d4527a1d67..738ccad1b9 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -209,7 +209,7 @@ bool BlockAssembler::TestPackage(uint64_t packageSize, int64_t packageSigOpsCost
// segwit activation)
bool BlockAssembler::TestPackageTransactions(const CTxMemPool::setEntries& package)
{
- for (const CTxMemPool::txiter it : package) {
+ for (CTxMemPool::txiter it : package) {
if (!IsFinalTx(it->GetTx(), nHeight, nLockTimeCutoff))
return false;
if (!fIncludeWitness && it->GetTx().HasWitness())
@@ -241,7 +241,7 @@ int BlockAssembler::UpdatePackagesForAdded(const CTxMemPool::setEntries& already
indexed_modified_transaction_set &mapModifiedTx)
{
int nDescendantsUpdated = 0;
- for (const CTxMemPool::txiter it : alreadyAdded) {
+ for (CTxMemPool::txiter it : alreadyAdded) {
CTxMemPool::setEntries descendants;
mempool.CalculateDescendants(it, descendants);
// Insert all descendants (not yet in block) into the modified set
diff --git a/src/net.cpp b/src/net.cpp
index 55043ffe30..34dc4aec5c 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -2042,7 +2042,7 @@ void CConnman::ThreadMessageHandler()
// Send messages
{
LOCK(pnode->cs_sendProcessing);
- m_msgproc->SendMessages(pnode, flagInterruptMsgProc);
+ m_msgproc->SendMessages(pnode);
}
if (flagInterruptMsgProc)
@@ -2254,7 +2254,8 @@ bool CConnman::InitBinds(const std::vector<CService>& binds, const std::vector<C
if (binds.empty() && whiteBinds.empty()) {
struct in_addr inaddr_any;
inaddr_any.s_addr = INADDR_ANY;
- fBound |= Bind(CService(in6addr_any, GetListenPort()), BF_NONE);
+ struct in6_addr inaddr6_any = IN6ADDR_ANY_INIT;
+ fBound |= Bind(CService(inaddr6_any, GetListenPort()), BF_NONE);
fBound |= Bind(CService(inaddr_any, GetListenPort()), !fBound ? BF_REPORT_ERROR : BF_NONE);
}
return fBound;
@@ -2864,8 +2865,20 @@ bool CConnman::ForNode(NodeId id, std::function<bool(CNode* pnode)> func)
return found != nullptr && NodeFullyConnected(found) && func(found);
}
-int64_t PoissonNextSend(int64_t nNow, int average_interval_seconds) {
- return nNow + (int64_t)(log1p(GetRand(1ULL << 48) * -0.0000000000000035527136788 /* -1/2^48 */) * average_interval_seconds * -1000000.0 + 0.5);
+int64_t CConnman::PoissonNextSendInbound(int64_t now, int average_interval_seconds)
+{
+ if (m_next_send_inv_to_incoming < now) {
+ // If this function were called from multiple threads simultaneously
+ // it would possible that both update the next send variable, and return a different result to their caller.
+ // This is not possible in practice as only the net processing thread invokes this function.
+ m_next_send_inv_to_incoming = PoissonNextSend(now, average_interval_seconds);
+ }
+ return m_next_send_inv_to_incoming;
+}
+
+int64_t PoissonNextSend(int64_t now, int average_interval_seconds)
+{
+ return now + (int64_t)(log1p(GetRand(1ULL << 48) * -0.0000000000000035527136788 /* -1/2^48 */) * average_interval_seconds * -1000000.0 + 0.5);
}
CSipHasher CConnman::GetDeterministicRandomizer(uint64_t id) const
diff --git a/src/net.h b/src/net.h
index 1bcc3cbb44..607b2fc53c 100644
--- a/src/net.h
+++ b/src/net.h
@@ -310,6 +310,13 @@ public:
unsigned int GetReceiveFloodSize() const;
void WakeMessageHandler();
+
+ /** Attempts to obfuscate tx time through exponentially distributed emitting.
+ Works assuming that a single interval is used.
+ Variable intervals will result in privacy decrease.
+ */
+ int64_t PoissonNextSendInbound(int64_t now, int average_interval_seconds);
+
private:
struct ListenSocket {
SOCKET socket;
@@ -434,6 +441,8 @@ private:
* This takes the place of a feeler connection */
std::atomic_bool m_try_another_outbound_peer;
+ std::atomic<int64_t> m_next_send_inv_to_incoming{0};
+
friend struct CConnmanTest;
};
extern std::unique_ptr<CConnman> g_connman;
@@ -466,7 +475,7 @@ class NetEventsInterface
{
public:
virtual bool ProcessMessages(CNode* pnode, std::atomic<bool>& interrupt) = 0;
- virtual bool SendMessages(CNode* pnode, std::atomic<bool>& interrupt) = 0;
+ virtual bool SendMessages(CNode* pnode) = 0;
virtual void InitializeNode(CNode* pnode) = 0;
virtual void FinalizeNode(NodeId id, bool& update_connection_time) = 0;
@@ -863,6 +872,6 @@ public:
/** Return a timestamp in the future (in microseconds) for exponentially distributed events. */
-int64_t PoissonNextSend(int64_t nNow, int average_interval_seconds);
+int64_t PoissonNextSend(int64_t now, int average_interval_seconds);
#endif // BITCOIN_NET_H
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index de456e87f4..a0136675f3 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -11,7 +11,6 @@
#include <chainparams.h>
#include <consensus/validation.h>
#include <hash.h>
-#include <init.h>
#include <validation.h>
#include <merkleblock.h>
#include <netmessagemaker.h>
@@ -36,17 +35,34 @@
# error "Bitcoin cannot be compiled without assertions."
#endif
-std::atomic<int64_t> nTimeBestReceived(0); // Used only to inform the wallet of when we last received a block
-bool g_enable_bip61 = DEFAULT_ENABLE_BIP61;
-
-struct IteratorComparator
-{
- template<typename I>
- bool operator()(const I& a, const I& b) const
- {
- return &(*a) < &(*b);
- }
-};
+/** Expiration time for orphan transactions in seconds */
+static constexpr int64_t ORPHAN_TX_EXPIRE_TIME = 20 * 60;
+/** Minimum time between orphan transactions expire time checks in seconds */
+static constexpr int64_t ORPHAN_TX_EXPIRE_INTERVAL = 5 * 60;
+/** Headers download timeout expressed in microseconds
+ * Timeout = base + per_header * (expected number of headers) */
+static constexpr int64_t HEADERS_DOWNLOAD_TIMEOUT_BASE = 15 * 60 * 1000000; // 15 minutes
+static constexpr int64_t HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER = 1000; // 1ms/header
+/** Protect at least this many outbound peers from disconnection due to slow/
+ * behind headers chain.
+ */
+static constexpr int32_t MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT = 4;
+/** Timeout for (unprotected) outbound peers to sync to our chainwork, in seconds */
+static constexpr int64_t CHAIN_SYNC_TIMEOUT = 20 * 60; // 20 minutes
+/** How frequently to check for stale tips, in seconds */
+static constexpr int64_t STALE_CHECK_INTERVAL = 10 * 60; // 10 minutes
+/** How frequently to check for extra outbound peers and disconnect, in seconds */
+static constexpr int64_t EXTRA_PEER_CHECK_INTERVAL = 45;
+/** Minimum time an outbound-peer-eviction candidate must be connected for, in order to evict, in seconds */
+static constexpr int64_t MINIMUM_CONNECT_TIME = 30;
+/** SHA256("main address relay")[0:8] */
+static constexpr uint64_t RANDOMIZER_ID_ADDRESS_RELAY = 0x3cac0035b5866b90ULL;
+/// Age after which a stale block will no longer be served if requested as
+/// protection against fingerprinting. Set to one month, denominated in seconds.
+static constexpr int STALE_RELAY_AGE_LIMIT = 30 * 24 * 60 * 60;
+/// Age after which a block is considered historical for purposes of rate
+/// limiting block relay. Set to one week, denominated in seconds.
+static constexpr int HISTORICAL_BLOCK_AGE = 7 * 24 * 60 * 60;
struct COrphanTx {
// When modifying, adapt the copy of this definition in tests/DoS_tests.
@@ -56,21 +72,26 @@ struct COrphanTx {
};
static CCriticalSection g_cs_orphans;
std::map<uint256, COrphanTx> mapOrphanTransactions GUARDED_BY(g_cs_orphans);
-std::map<COutPoint, std::set<std::map<uint256, COrphanTx>::iterator, IteratorComparator>> mapOrphanTransactionsByPrev GUARDED_BY(g_cs_orphans);
-void EraseOrphansFor(NodeId peer);
-static size_t vExtraTxnForCompactIt GUARDED_BY(g_cs_orphans) = 0;
-static std::vector<std::pair<uint256, CTransactionRef>> vExtraTxnForCompact GUARDED_BY(g_cs_orphans);
-
-static const uint64_t RANDOMIZER_ID_ADDRESS_RELAY = 0x3cac0035b5866b90ULL; // SHA256("main address relay")[0:8]
+void EraseOrphansFor(NodeId peer);
-/// Age after which a stale block will no longer be served if requested as
-/// protection against fingerprinting. Set to one month, denominated in seconds.
-static const int STALE_RELAY_AGE_LIMIT = 30 * 24 * 60 * 60;
-
-/// Age after which a block is considered historical for purposes of rate
-/// limiting block relay. Set to one week, denominated in seconds.
-static const int HISTORICAL_BLOCK_AGE = 7 * 24 * 60 * 60;
+/** Increase a node's misbehavior score. */
+void Misbehaving(NodeId nodeid, int howmuch, const std::string& message="");
+
+/** Average delay between local address broadcasts in seconds. */
+static constexpr unsigned int AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL = 24 * 60 * 60;
+/** Average delay between peer address broadcasts in seconds. */
+static const unsigned int AVG_ADDRESS_BROADCAST_INTERVAL = 30;
+/** Average delay between trickled inventory transmissions in seconds.
+ * Blocks and whitelisted receivers bypass this, outbound peers get half this delay. */
+static const unsigned int INVENTORY_BROADCAST_INTERVAL = 5;
+/** Maximum number of inventory items to send per transmission.
+ * Limits the impact of low-fee transaction floods. */
+static constexpr unsigned int INVENTORY_BROADCAST_MAX = 7 * INVENTORY_BROADCAST_INTERVAL;
+/** Average delay between feefilter broadcasts in seconds. */
+static constexpr unsigned int AVG_FEEFILTER_BROADCAST_INTERVAL = 10 * 60;
+/** Maximum feefilter broadcast delay after significant change. */
+static constexpr unsigned int MAX_FEEFILTER_CHANGE_DELAY = 5 * 60;
// Internal stuff
namespace {
@@ -138,10 +159,24 @@ namespace {
MapRelay mapRelay;
/** Expiration-time ordered list of (expire time, relay map entry) pairs, protected by cs_main). */
std::deque<std::pair<int64_t, MapRelay::iterator>> vRelayExpiration;
+
+ std::atomic<int64_t> nTimeBestReceived(0); // Used only to inform the wallet of when we last received a block
+
+ struct IteratorComparator
+ {
+ template<typename I>
+ bool operator()(const I& a, const I& b) const
+ {
+ return &(*a) < &(*b);
+ }
+ };
+ std::map<COutPoint, std::set<std::map<uint256, COrphanTx>::iterator, IteratorComparator>> mapOrphanTransactionsByPrev GUARDED_BY(g_cs_orphans);
+
+ static size_t vExtraTxnForCompactIt GUARDED_BY(g_cs_orphans) = 0;
+ static std::vector<std::pair<uint256, CTransactionRef>> vExtraTxnForCompact GUARDED_BY(g_cs_orphans);
} // namespace
namespace {
-
struct CBlockReject {
unsigned char chRejectCode;
std::string strRejectReason;
@@ -268,10 +303,10 @@ struct CNodeState {
};
/** Map maintaining per-node state. Requires cs_main. */
-std::map<NodeId, CNodeState> mapNodeState;
+static std::map<NodeId, CNodeState> mapNodeState;
// Requires cs_main.
-CNodeState *State(NodeId pnode) {
+static CNodeState *State(NodeId pnode) {
std::map<NodeId, CNodeState>::iterator it = mapNodeState.find(pnode);
if (it == mapNodeState.end())
return nullptr;
@@ -665,10 +700,10 @@ bool AddOrphanTx(const CTransactionRef& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRE
// large transaction with a missing parent then we assume
// it will rebroadcast it later, after the parent transaction(s)
// have been mined or received.
- // 100 orphans, each of which is at most 99,999 bytes big is
+ // 100 orphans, each of which is at most 100,000 bytes big is
// at most 10 megabytes of orphans and somewhat more byprev index (in the worst case):
unsigned int sz = GetTransactionWeight(*tx);
- if (sz >= MAX_STANDARD_TX_WEIGHT)
+ if (sz > MAX_STANDARD_TX_WEIGHT)
{
LogPrint(BCLog::MEMPOOL, "ignoring large orphan tx (size: %u, hash: %s)\n", sz, hash.ToString());
return false;
@@ -810,7 +845,9 @@ static bool BlockRequestAllowed(const CBlockIndex* pindex, const Consensus::Para
(GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, consensusParams) < STALE_RELAY_AGE_LIMIT);
}
-PeerLogicValidation::PeerLogicValidation(CConnman* connmanIn, CScheduler &scheduler) : connman(connmanIn), m_stale_tip_check_time(0) {
+PeerLogicValidation::PeerLogicValidation(CConnman* connmanIn, CScheduler &scheduler, bool enable_bip61)
+ : connman(connmanIn), m_stale_tip_check_time(0), m_enable_bip61(enable_bip61) {
+
// Initialize global variables that cannot be constructed at startup.
recentRejects.reset(new CRollingBloomFilter(120000, 0.000001));
@@ -867,7 +904,7 @@ static uint256 most_recent_block_hash;
static bool fWitnessesPresentInMostRecentCompactBlock;
/**
- * Maintain state about the best-seen block and fast-announce a compact block
+ * Maintain state about the best-seen block and fast-announce a compact block
* to compatible peers.
*/
void PeerLogicValidation::NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& pblock) {
@@ -912,7 +949,7 @@ void PeerLogicValidation::NewPoWValidBlock(const CBlockIndex *pindex, const std:
}
/**
- * Update our best height and announce any block hashes which weren't previously
+ * Update our best height and announce any block hashes which weren't previously
* in chainActive to our peers.
*/
void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {
@@ -948,7 +985,7 @@ void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CB
}
/**
- * Handle invalid block rejection and consequent peer banning, maintain which
+ * Handle invalid block rejection and consequent peer banning, maintain which
* peers announce compact blocks.
*/
void PeerLogicValidation::BlockChecked(const CBlock& block, const CValidationState& state) {
@@ -1071,7 +1108,7 @@ static void RelayAddress(const CAddress& addr, bool fReachable, CConnman* connma
connman->ForEachNodeThen(std::move(sortfunc), std::move(pushfunc));
}
-void static ProcessGetBlockData(CNode* pfrom, const CChainParams& chainparams, const CInv& inv, CConnman* connman, const std::atomic<bool>& interruptMsgProc)
+void static ProcessGetBlockData(CNode* pfrom, const CChainParams& chainparams, const CInv& inv, CConnman* connman)
{
bool send = false;
std::shared_ptr<const CBlock> a_recent_block;
@@ -1265,9 +1302,6 @@ void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnm
if (!push) {
vNotFound.push_back(inv);
}
-
- // Track requests for our stuff.
- GetMainSignals().Inventory(inv.hash);
}
} // release cs_main
@@ -1275,7 +1309,7 @@ void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnm
const CInv &inv = *it;
if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_CMPCT_BLOCK || inv.type == MSG_WITNESS_BLOCK) {
it++;
- ProcessGetBlockData(pfrom, chainparams, inv, connman, interruptMsgProc);
+ ProcessGetBlockData(pfrom, chainparams, inv, connman);
}
}
@@ -1538,7 +1572,7 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve
return true;
}
-bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams, CConnman* connman, const std::atomic<bool>& interruptMsgProc)
+bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams, CConnman* connman, const std::atomic<bool>& interruptMsgProc, bool enable_bip61)
{
LogPrint(BCLog::NET, "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->GetId());
if (gArgs.IsArgSet("-dropmessagestest") && GetRand(gArgs.GetArg("-dropmessagestest", 0)) == 0)
@@ -1592,7 +1626,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// Each connection can only send one version message
if (pfrom->nVersion != 0)
{
- if (g_enable_bip61) {
+ if (enable_bip61) {
connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_DUPLICATE, std::string("Duplicate version message")));
}
LOCK(cs_main);
@@ -1623,7 +1657,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
if (!pfrom->fInbound && !pfrom->fFeeler && !pfrom->m_manual_connection && !HasAllDesirableServiceFlags(nServices))
{
LogPrint(BCLog::NET, "peer=%d does not offer the expected services (%08x offered, %08x expected); disconnecting\n", pfrom->GetId(), nServices, GetDesirableServiceFlags(nServices));
- if (g_enable_bip61) {
+ if (enable_bip61) {
connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_NONSTANDARD,
strprintf("Expected to offer services %08x", GetDesirableServiceFlags(nServices))));
}
@@ -1646,7 +1680,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
{
// disconnect from peers older than this proto version
LogPrint(BCLog::NET, "peer=%d using obsolete version %i; disconnecting\n", pfrom->GetId(), nVersion);
- if (g_enable_bip61) {
+ if (enable_bip61) {
connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE,
strprintf("Version must be %d or greater", MIN_PEER_PROTO_VERSION)));
}
@@ -1966,9 +2000,6 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
pfrom->AskFor(inv);
}
}
-
- // Track requests for our stuff
- GetMainSignals().Inventory(inv.hash);
}
}
@@ -2347,7 +2378,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
LogPrint(BCLog::MEMPOOLREJ, "%s from peer=%d was not accepted: %s\n", tx.GetHash().ToString(),
pfrom->GetId(),
FormatStateMessage(state));
- if (g_enable_bip61 && state.GetRejectCode() > 0 && state.GetRejectCode() < REJECT_INTERNAL) { // Never send AcceptToMemoryPool's internal codes over P2P
+ if (enable_bip61 && state.GetRejectCode() > 0 && state.GetRejectCode() < REJECT_INTERNAL) { // Never send AcceptToMemoryPool's internal codes over P2P
connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::REJECT, strCommand, (unsigned char)state.GetRejectCode(),
state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash));
}
@@ -2532,7 +2563,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
} // cs_main
if (fProcessBLOCKTXN)
- return ProcessMessage(pfrom, NetMsgType::BLOCKTXN, blockTxnMsg, nTimeReceived, chainparams, connman, interruptMsgProc);
+ return ProcessMessage(pfrom, NetMsgType::BLOCKTXN, blockTxnMsg, nTimeReceived, chainparams, connman, interruptMsgProc, enable_bip61);
if (fRevertToHeaderProcessing) {
// Headers received from HB compact block peers are permitted to be
@@ -2918,12 +2949,12 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
return true;
}
-static bool SendRejectsAndCheckIfBanned(CNode* pnode, CConnman* connman)
+static bool SendRejectsAndCheckIfBanned(CNode* pnode, CConnman* connman, bool enable_bip61)
{
AssertLockHeld(cs_main);
CNodeState &state = *State(pnode->GetId());
- if (g_enable_bip61) {
+ if (enable_bip61) {
for (const CBlockReject& reject : state.rejects) {
connman->PushMessage(pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, std::string(NetMsgType::BLOCK), reject.chRejectCode, reject.strRejectReason, reject.hashBlock));
}
@@ -3025,7 +3056,7 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
bool fRet = false;
try
{
- fRet = ProcessMessage(pfrom, strCommand, vRecv, msg.nTime, chainparams, connman, interruptMsgProc);
+ fRet = ProcessMessage(pfrom, strCommand, vRecv, msg.nTime, chainparams, connman, interruptMsgProc, m_enable_bip61);
if (interruptMsgProc)
return false;
if (!pfrom->vRecvGetData.empty())
@@ -3033,7 +3064,7 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
}
catch (const std::ios_base::failure& e)
{
- if (g_enable_bip61) {
+ if (m_enable_bip61) {
connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_MALFORMED, std::string("error parsing message")));
}
if (strstr(e.what(), "end of data"))
@@ -3067,7 +3098,7 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
}
LOCK(cs_main);
- SendRejectsAndCheckIfBanned(pfrom, connman);
+ SendRejectsAndCheckIfBanned(pfrom, connman, m_enable_bip61);
return fMoreWork;
}
@@ -3202,6 +3233,7 @@ void PeerLogicValidation::CheckForStaleTipAndEvictPeers(const Consensus::Params
}
}
+namespace {
class CompareInvMempoolOrder
{
CTxMemPool *mp;
@@ -3218,8 +3250,9 @@ public:
return mp->CompareDepthAndScore(*b, *a);
}
};
+}
-bool PeerLogicValidation::SendMessages(CNode* pto, std::atomic<bool>& interruptMsgProc)
+bool PeerLogicValidation::SendMessages(CNode* pto)
{
const Consensus::Params& consensusParams = Params().GetConsensus();
{
@@ -3263,7 +3296,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto, std::atomic<bool>& interruptM
if (!lockMain)
return true;
- if (SendRejectsAndCheckIfBanned(pto, connman))
+ if (SendRejectsAndCheckIfBanned(pto, connman, m_enable_bip61))
return true;
CNodeState &state = *State(pto->GetId());
@@ -3497,8 +3530,12 @@ bool PeerLogicValidation::SendMessages(CNode* pto, std::atomic<bool>& interruptM
bool fSendTrickle = pto->fWhitelisted;
if (pto->nNextInvSend < nNow) {
fSendTrickle = true;
- // Use half the delay for outbound peers, as there is less privacy concern for them.
- pto->nNextInvSend = PoissonNextSend(nNow, INVENTORY_BROADCAST_INTERVAL >> !pto->fInbound);
+ if (pto->fInbound) {
+ pto->nNextInvSend = connman->PoissonNextSendInbound(nNow, INVENTORY_BROADCAST_INTERVAL);
+ } else {
+ // Use half the delay for outbound peers, as there is less privacy concern for them.
+ pto->nNextInvSend = PoissonNextSend(nNow, INVENTORY_BROADCAST_INTERVAL >> 1);
+ }
}
// Time to send but the peer has requested we not relay transactions.
diff --git a/src/net_processing.h b/src/net_processing.h
index b0b905d922..4dab0ada2b 100644
--- a/src/net_processing.h
+++ b/src/net_processing.h
@@ -12,40 +12,17 @@
/** Default for -maxorphantx, maximum number of orphan transactions kept in memory */
static const unsigned int DEFAULT_MAX_ORPHAN_TRANSACTIONS = 100;
-/** Expiration time for orphan transactions in seconds */
-static const int64_t ORPHAN_TX_EXPIRE_TIME = 20 * 60;
-/** Minimum time between orphan transactions expire time checks in seconds */
-static const int64_t ORPHAN_TX_EXPIRE_INTERVAL = 5 * 60;
/** Default number of orphan+recently-replaced txn to keep around for block reconstruction */
static const unsigned int DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN = 100;
-/** Headers download timeout expressed in microseconds
- * Timeout = base + per_header * (expected number of headers) */
-static constexpr int64_t HEADERS_DOWNLOAD_TIMEOUT_BASE = 15 * 60 * 1000000; // 15 minutes
-static constexpr int64_t HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER = 1000; // 1ms/header
-/** Protect at least this many outbound peers from disconnection due to slow/
- * behind headers chain.
- */
-static constexpr int32_t MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT = 4;
-/** Timeout for (unprotected) outbound peers to sync to our chainwork, in seconds */
-static constexpr int64_t CHAIN_SYNC_TIMEOUT = 20 * 60; // 20 minutes
-/** How frequently to check for stale tips, in seconds */
-static constexpr int64_t STALE_CHECK_INTERVAL = 10 * 60; // 10 minutes
-/** How frequently to check for extra outbound peers and disconnect, in seconds */
-static constexpr int64_t EXTRA_PEER_CHECK_INTERVAL = 45;
-/** Minimum time an outbound-peer-eviction candidate must be connected for, in order to evict, in seconds */
-static constexpr int64_t MINIMUM_CONNECT_TIME = 30;
-
/** Default for BIP61 (sending reject messages) */
static constexpr bool DEFAULT_ENABLE_BIP61 = true;
-/** Enable BIP61 (sending reject messages) */
-extern bool g_enable_bip61;
class PeerLogicValidation final : public CValidationInterface, public NetEventsInterface {
private:
CConnman* const connman;
public:
- explicit PeerLogicValidation(CConnman* connman, CScheduler &scheduler);
+ explicit PeerLogicValidation(CConnman* connman, CScheduler &scheduler, bool enable_bip61);
/**
* Overridden from CValidationInterface.
@@ -68,16 +45,20 @@ public:
void InitializeNode(CNode* pnode) override;
/** Handle removal of a peer by updating various state and removing it from mapNodeState */
void FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTime) override;
- /** Process protocol messages received from a given node */
+ /**
+ * Process protocol messages received from a given node
+ *
+ * @param[in] pfrom The node which we have received messages from.
+ * @param[in] interrupt Interrupt condition for processing threads
+ */
bool ProcessMessages(CNode* pfrom, std::atomic<bool>& interrupt) override;
/**
* Send queued protocol messages to be sent to a give node.
*
* @param[in] pto The node which we are sending messages to.
- * @param[in] interrupt Interrupt condition for processing threads
* @return True if there is more work to be done
*/
- bool SendMessages(CNode* pto, std::atomic<bool>& interrupt) override;
+ bool SendMessages(CNode* pto) override EXCLUSIVE_LOCKS_REQUIRED(pto->cs_sendProcessing);
/** Consider evicting an outbound peer based on the amount of time they've been behind our tip */
void ConsiderEviction(CNode *pto, int64_t time_in_seconds);
@@ -88,6 +69,9 @@ public:
private:
int64_t m_stale_tip_check_time; //! Next time to check for stale tip
+
+ /** Enable BIP61 (sending reject messages) */
+ const bool m_enable_bip61;
};
struct CNodeStateStats {
@@ -99,7 +83,5 @@ struct CNodeStateStats {
/** Get statistics from node state */
bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats);
-/** Increase a node's misbehavior score. */
-void Misbehaving(NodeId nodeid, int howmuch, const std::string& message="");
#endif // BITCOIN_NET_PROCESSING_H
diff --git a/src/netaddress.cpp b/src/netaddress.cpp
index 18d5948f85..5ccbabd03d 100644
--- a/src/netaddress.cpp
+++ b/src/netaddress.cpp
@@ -246,7 +246,7 @@ enum Network CNetAddr::GetNetwork() const
return NET_IPV4;
if (IsTor())
- return NET_TOR;
+ return NET_ONION;
return NET_IPV6;
}
@@ -355,7 +355,7 @@ std::vector<unsigned char> CNetAddr::GetGroup() const
}
else if (IsTor())
{
- nClass = NET_TOR;
+ nClass = NET_ONION;
nStartByte = 6;
nBits = 4;
}
@@ -433,11 +433,11 @@ int CNetAddr::GetReachabilityFrom(const CNetAddr *paddrPartner) const
case NET_IPV4: return REACH_IPV4;
case NET_IPV6: return fTunnel ? REACH_IPV6_WEAK : REACH_IPV6_STRONG; // only prefer giving our IPv6 address if it's not tunnelled
}
- case NET_TOR:
+ case NET_ONION:
switch(ourNet) {
default: return REACH_DEFAULT;
case NET_IPV4: return REACH_IPV4; // Tor users can connect to IPv4 as well
- case NET_TOR: return REACH_PRIVATE;
+ case NET_ONION: return REACH_PRIVATE;
}
case NET_TEREDO:
switch(ourNet) {
@@ -454,7 +454,7 @@ int CNetAddr::GetReachabilityFrom(const CNetAddr *paddrPartner) const
case NET_TEREDO: return REACH_TEREDO;
case NET_IPV6: return REACH_IPV6_WEAK;
case NET_IPV4: return REACH_IPV4;
- case NET_TOR: return REACH_PRIVATE; // either from Tor, or don't care about our address
+ case NET_ONION: return REACH_PRIVATE; // either from Tor, or don't care about our address
}
}
}
diff --git a/src/netaddress.h b/src/netaddress.h
index f8f2ab99ff..966bef8cdf 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -22,7 +22,7 @@ enum Network
NET_UNROUTABLE = 0,
NET_IPV4,
NET_IPV6,
- NET_TOR,
+ NET_ONION,
NET_INTERNAL,
NET_MAX,
diff --git a/src/netbase.cpp b/src/netbase.cpp
index 15f9016be8..4ce63cb0ec 100644
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -41,7 +41,11 @@ enum Network ParseNetwork(std::string net) {
boost::to_lower(net);
if (net == "ipv4") return NET_IPV4;
if (net == "ipv6") return NET_IPV6;
- if (net == "tor" || net == "onion") return NET_TOR;
+ if (net == "onion") return NET_ONION;
+ if (net == "tor") {
+ LogPrintf("Warning: net name 'tor' is deprecated and will be removed in the future. You should use 'onion' instead.\n");
+ return NET_ONION;
+ }
return NET_UNROUTABLE;
}
@@ -50,7 +54,7 @@ std::string GetNetworkName(enum Network net) {
{
case NET_IPV4: return "ipv4";
case NET_IPV6: return "ipv6";
- case NET_TOR: return "onion";
+ case NET_ONION: return "onion";
default: return "";
}
}
diff --git a/src/outputtype.cpp b/src/outputtype.cpp
new file mode 100644
index 0000000000..3ff28bf9c2
--- /dev/null
+++ b/src/outputtype.cpp
@@ -0,0 +1,101 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2017 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <outputtype.h>
+
+#include <keystore.h>
+#include <pubkey.h>
+#include <script/script.h>
+#include <script/standard.h>
+
+#include <assert.h>
+#include <string>
+
+static const std::string OUTPUT_TYPE_STRING_LEGACY = "legacy";
+static const std::string OUTPUT_TYPE_STRING_P2SH_SEGWIT = "p2sh-segwit";
+static const std::string OUTPUT_TYPE_STRING_BECH32 = "bech32";
+
+bool ParseOutputType(const std::string& type, OutputType& output_type)
+{
+ if (type == OUTPUT_TYPE_STRING_LEGACY) {
+ output_type = OutputType::LEGACY;
+ return true;
+ } else if (type == OUTPUT_TYPE_STRING_P2SH_SEGWIT) {
+ output_type = OutputType::P2SH_SEGWIT;
+ return true;
+ } else if (type == OUTPUT_TYPE_STRING_BECH32) {
+ output_type = OutputType::BECH32;
+ return true;
+ }
+ return false;
+}
+
+const std::string& FormatOutputType(OutputType type)
+{
+ switch (type) {
+ case OutputType::LEGACY: return OUTPUT_TYPE_STRING_LEGACY;
+ case OutputType::P2SH_SEGWIT: return OUTPUT_TYPE_STRING_P2SH_SEGWIT;
+ case OutputType::BECH32: return OUTPUT_TYPE_STRING_BECH32;
+ default: assert(false);
+ }
+}
+
+CTxDestination GetDestinationForKey(const CPubKey& key, OutputType type)
+{
+ switch (type) {
+ case OutputType::LEGACY: return key.GetID();
+ case OutputType::P2SH_SEGWIT:
+ case OutputType::BECH32: {
+ if (!key.IsCompressed()) return key.GetID();
+ CTxDestination witdest = WitnessV0KeyHash(key.GetID());
+ CScript witprog = GetScriptForDestination(witdest);
+ if (type == OutputType::P2SH_SEGWIT) {
+ return CScriptID(witprog);
+ } else {
+ return witdest;
+ }
+ }
+ default: assert(false);
+ }
+}
+
+std::vector<CTxDestination> GetAllDestinationsForKey(const CPubKey& key)
+{
+ CKeyID keyid = key.GetID();
+ if (key.IsCompressed()) {
+ CTxDestination segwit = WitnessV0KeyHash(keyid);
+ CTxDestination p2sh = CScriptID(GetScriptForDestination(segwit));
+ return std::vector<CTxDestination>{std::move(keyid), std::move(p2sh), std::move(segwit)};
+ } else {
+ return std::vector<CTxDestination>{std::move(keyid)};
+ }
+}
+
+CTxDestination AddAndGetDestinationForScript(CKeyStore& keystore, const CScript& script, OutputType type)
+{
+ // Add script to keystore
+ keystore.AddCScript(script);
+ // Note that scripts over 520 bytes are not yet supported.
+ switch (type) {
+ case OutputType::LEGACY:
+ return CScriptID(script);
+ case OutputType::P2SH_SEGWIT:
+ case OutputType::BECH32: {
+ CTxDestination witdest = WitnessV0ScriptHash(script);
+ CScript witprog = GetScriptForDestination(witdest);
+ // Check if the resulting program is solvable (i.e. doesn't use an uncompressed key)
+ if (!IsSolvable(keystore, witprog)) return CScriptID(script);
+ // Add the redeemscript, so that P2WSH and P2SH-P2WSH outputs are recognized as ours.
+ keystore.AddCScript(witprog);
+ if (type == OutputType::BECH32) {
+ return witdest;
+ } else {
+ return CScriptID(witprog);
+ }
+ }
+ default: assert(false);
+ }
+}
+
diff --git a/src/outputtype.h b/src/outputtype.h
new file mode 100644
index 0000000000..21623e3b49
--- /dev/null
+++ b/src/outputtype.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2017 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_OUTPUTTYPE_H
+#define BITCOIN_OUTPUTTYPE_H
+
+#include <keystore.h>
+#include <script/standard.h>
+
+#include <string>
+#include <vector>
+
+enum class OutputType {
+ LEGACY,
+ P2SH_SEGWIT,
+ BECH32,
+
+ /**
+ * Special output type for change outputs only. Automatically choose type
+ * based on address type setting and the types other of non-change outputs
+ * (see -changetype option documentation and implementation in
+ * CWallet::TransactionChangeType for details).
+ */
+ CHANGE_AUTO,
+};
+
+bool ParseOutputType(const std::string& str, OutputType& output_type);
+const std::string& FormatOutputType(OutputType type);
+
+/**
+ * Get a destination of the requested type (if possible) to the specified key.
+ * The caller must make sure LearnRelatedScripts has been called beforehand.
+ */
+CTxDestination GetDestinationForKey(const CPubKey& key, OutputType);
+
+/** Get all destinations (potentially) supported by the wallet for the given key. */
+std::vector<CTxDestination> GetAllDestinationsForKey(const CPubKey& key);
+
+/**
+ * Get a destination of the requested type (if possible) to the specified script.
+ * This function will automatically add the script (and any other
+ * necessary scripts) to the keystore.
+ */
+CTxDestination AddAndGetDestinationForScript(CKeyStore& keystore, const CScript& script, OutputType);
+
+#endif // BITCOIN_OUTPUTTYPE_H
+
diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp
index aac3fe5c14..3a592e40d3 100644
--- a/src/policy/policy.cpp
+++ b/src/policy/policy.cpp
@@ -88,7 +88,7 @@ bool IsStandardTx(const CTransaction& tx, std::string& reason)
// computing signature hashes is O(ninputs*txsize). Limiting transactions
// to MAX_STANDARD_TX_WEIGHT mitigates CPU exhaustion attacks.
unsigned int sz = GetTransactionWeight(tx);
- if (sz >= MAX_STANDARD_TX_WEIGHT) {
+ if (sz > MAX_STANDARD_TX_WEIGHT) {
reason = "tx-size";
return false;
}
diff --git a/src/pubkey.h b/src/pubkey.h
index bb254547c8..0985273f34 100644
--- a/src/pubkey.h
+++ b/src/pubkey.h
@@ -107,6 +107,7 @@ public:
//! Simple read-only vector-like interface to the pubkey data.
unsigned int size() const { return GetLen(vch[0]); }
+ const unsigned char* data() const { return vch; }
const unsigned char* begin() const { return vch; }
const unsigned char* end() const { return vch + size(); }
const unsigned char& operator[](unsigned int pos) const { return vch[pos]; }
diff --git a/src/qt/README.md b/src/qt/README.md
index bf8139666c..3ec538b4f4 100644
--- a/src/qt/README.md
+++ b/src/qt/README.md
@@ -1,6 +1,6 @@
This directory contains the BitcoinQT graphical user interface (GUI). It uses the cross-platform framework [Qt](https://www1.qt.io/developers/).
-The current precise version for Qt 5 is specified in [qt.mk](/depends/packages/qt.mk). Qt 4 is also supported (see [#8263](https://github.com/bitcoin/bitcoin/issues/8263)).
+The current precise version for Qt 5 is specified in [qt.mk](/depends/packages/qt.mk).
## Compile and run
@@ -16,7 +16,7 @@ To run:
### forms
-Contains [Designer UI](http://doc.qt.io/qt-5.9/designer-using-a-ui-file.html) files. They are created with [Qt Creator](#using-qt-creator-as-ide), but can be edited using any text editor.
+Contains [Designer UI](https://doc.qt.io/qt-5.9/designer-using-a-ui-file.html) files. They are created with [Qt Creator](#using-qt-creator-as-ide), but can be edited using any text editor.
### locale
@@ -36,7 +36,7 @@ Represents the main window of the Bitcoin UI.
### \*model.(h/cpp)
-The model. When it has a corresponding controller, it generally inherits from [QAbstractTableModel](http://doc.qt.io/qt-5/qabstracttablemodel.html). Models that are used by controllers as helpers inherit from other Qt classes like [QValidator](http://doc.qt.io/qt-5/qvalidator.html).
+The model. When it has a corresponding controller, it generally inherits from [QAbstractTableModel](https://doc.qt.io/qt-5/qabstracttablemodel.html). Models that are used by controllers as helpers inherit from other Qt classes like [QValidator](https://doc.qt.io/qt-5/qvalidator.html).
ClientModel is used by the main application `bitcoingui` and several models like `peertablemodel`.
@@ -46,7 +46,7 @@ A controller. `:NAMEpage.cpp` generally includes `:NAMEmodel.h` and `forms/:NAME
### \*dialog.(h/cpp)
-Various dialogs, e.g. to open a URL. Inherit from [QDialog](http://doc.qt.io/qt-4.8/qdialog.html).
+Various dialogs, e.g. to open a URL. Inherit from [QDialog](https://doc.qt.io/qt-5/qdialog.html).
### paymentserver.(h/cpp)
diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp
index f2ddbf259b..d51069d922 100644
--- a/src/qt/addressbookpage.cpp
+++ b/src/qt/addressbookpage.cpp
@@ -160,13 +160,8 @@ void AddressBookPage::setModel(AddressTableModel *_model)
ui->tableView->sortByColumn(0, Qt::AscendingOrder);
// Set column widths
-#if QT_VERSION < 0x050000
- ui->tableView->horizontalHeader()->setResizeMode(AddressTableModel::Label, QHeaderView::Stretch);
- ui->tableView->horizontalHeader()->setResizeMode(AddressTableModel::Address, QHeaderView::ResizeToContents);
-#else
ui->tableView->horizontalHeader()->setSectionResizeMode(AddressTableModel::Label, QHeaderView::Stretch);
ui->tableView->horizontalHeader()->setSectionResizeMode(AddressTableModel::Address, QHeaderView::ResizeToContents);
-#endif
connect(ui->tableView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
this, SLOT(selectionChanged()));
diff --git a/src/qt/bantablemodel.cpp b/src/qt/bantablemodel.cpp
index 26cb03c2c7..aa0d4a31d3 100644
--- a/src/qt/bantablemodel.cpp
+++ b/src/qt/bantablemodel.cpp
@@ -52,9 +52,7 @@ public:
node.getBanned(banMap);
cachedBanlist.clear();
-#if QT_VERSION >= 0x040700
cachedBanlist.reserve(banMap.size());
-#endif
for (const auto& entry : banMap)
{
CCombinedBan banEntry;
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 31d9f936e7..3454d3421e 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -26,7 +26,6 @@
#include <qt/walletmodel.h>
#endif
-#include <init.h>
#include <interfaces/handler.h>
#include <interfaces/node.h>
#include <rpc/server.h>
@@ -55,13 +54,6 @@
#if defined(QT_STATICPLUGIN)
#include <QtPlugin>
-#if QT_VERSION < 0x050000
-Q_IMPORT_PLUGIN(qcncodecs)
-Q_IMPORT_PLUGIN(qjpcodecs)
-Q_IMPORT_PLUGIN(qtwcodecs)
-Q_IMPORT_PLUGIN(qkrcodecs)
-Q_IMPORT_PLUGIN(qtaccessiblewidgets)
-#else
#if QT_VERSION < 0x050400
Q_IMPORT_PLUGIN(AccessibleFactory)
#endif
@@ -73,11 +65,6 @@ Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin);
Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin);
#endif
#endif
-#endif
-
-#if QT_VERSION < 0x050000
-#include <QTextCodec>
-#endif
// Declare meta types used for QMetaObject::invokeMethod
Q_DECLARE_METATYPE(bool*)
@@ -151,16 +138,6 @@ static void initTranslations(QTranslator &qtTranslatorBase, QTranslator &qtTrans
}
/* qDebug() message handler --> debug.log */
-#if QT_VERSION < 0x050000
-void DebugMessageHandler(QtMsgType type, const char *msg)
-{
- if (type == QtDebugMsg) {
- LogPrint(BCLog::QT, "GUI: %s\n", msg);
- } else {
- LogPrintf("GUI: %s\n", msg);
- }
-}
-#else
void DebugMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString &msg)
{
Q_UNUSED(context);
@@ -170,7 +147,6 @@ void DebugMessageHandler(QtMsgType type, const QMessageLogContext& context, cons
LogPrintf("GUI: %s\n", msg.toStdString());
}
}
-#endif
/** Class encapsulating Bitcoin Core startup and shutdown.
* Allows running startup and shutdown in a different thread from the UI thread.
@@ -238,6 +214,7 @@ public Q_SLOTS:
/// Handle runaway exceptions. Shows a message box with the problem and quits the program.
void handleRunawayException(const QString &message);
void addWallet(WalletModel* walletModel);
+ void removeWallet();
Q_SIGNALS:
void requestedInitialize();
@@ -467,11 +444,22 @@ void BitcoinApplication::addWallet(WalletModel* walletModel)
connect(walletModel, SIGNAL(coinsSent(WalletModel*, SendCoinsRecipient, QByteArray)),
paymentServer, SLOT(fetchPaymentACK(WalletModel*, const SendCoinsRecipient&, QByteArray)));
+ connect(walletModel, SIGNAL(unload()), this, SLOT(removeWallet()));
m_wallet_models.push_back(walletModel);
#endif
}
+void BitcoinApplication::removeWallet()
+{
+#ifdef ENABLE_WALLET
+ WalletModel* walletModel = static_cast<WalletModel*>(sender());
+ m_wallet_models.erase(std::find(m_wallet_models.begin(), m_wallet_models.end(), walletModel));
+ window->removeWallet(walletModel);
+ walletModel->deleteLater();
+#endif
+}
+
void BitcoinApplication::initializeResult(bool success)
{
qDebug() << __func__ << ": Initialization result: " << success;
@@ -491,8 +479,10 @@ void BitcoinApplication::initializeResult(bool success)
#ifdef ENABLE_WALLET
m_handler_load_wallet = m_node.handleLoadWallet([this](std::unique_ptr<interfaces::Wallet> wallet) {
- QMetaObject::invokeMethod(this, "addWallet", Qt::QueuedConnection,
- Q_ARG(WalletModel*, new WalletModel(std::move(wallet), m_node, platformStyle, optionsModel)));
+ WalletModel* wallet_model = new WalletModel(std::move(wallet), m_node, platformStyle, optionsModel, nullptr);
+ // Fix wallet model thread affinity.
+ wallet_model->moveToThread(thread());
+ QMetaObject::invokeMethod(this, "addWallet", Qt::QueuedConnection, Q_ARG(WalletModel*, wallet_model));
});
for (auto& wallet : m_node.getWallets()) {
@@ -572,12 +562,6 @@ int main(int argc, char *argv[])
// Do not refer to data directory yet, this can be overridden by Intro::pickDataDirectory
/// 1. Basic Qt initialization (not dependent on parameters or configuration)
-#if QT_VERSION < 0x050000
- // Internal string conversion is all UTF-8
- QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8"));
- QTextCodec::setCodecForCStrings(QTextCodec::codecForTr());
-#endif
-
Q_INIT_RESOURCE(bitcoin);
Q_INIT_RESOURCE(bitcoin_locale);
@@ -708,17 +692,12 @@ int main(int argc, char *argv[])
/// 9. Main GUI initialization
// Install global event filter that makes sure that long tooltips can be word-wrapped
app.installEventFilter(new GUIUtil::ToolTipToRichTextFilter(TOOLTIP_WRAP_THRESHOLD, &app));
-#if QT_VERSION < 0x050000
- // Install qDebug() message handler to route to debug.log
- qInstallMsgHandler(DebugMessageHandler);
-#else
#if defined(Q_OS_WIN)
// Install global event filter for processing Windows session related Windows messages (WM_QUERYENDSESSION and WM_ENDSESSION)
qApp->installNativeEventFilter(new WinShutdownMonitor());
#endif
// Install qDebug() message handler to route to debug.log
qInstallMessageHandler(DebugMessageHandler);
-#endif
// Allow parameter interaction before we create the options model
app.parameterSetup();
// Load GUI settings from QSettings
@@ -739,7 +718,7 @@ int main(int argc, char *argv[])
// so the GUI thread won't be held up.
if (node->baseInitialize()) {
app.requestInitialize();
-#if defined(Q_OS_WIN) && QT_VERSION >= 0x050000
+#if defined(Q_OS_WIN)
WinShutdownMonitor::registerShutdownBlockReason(QObject::tr("%1 didn't yet exit safely...").arg(QObject::tr(PACKAGE_NAME)), (HWND)app.getMainWinId());
#endif
app.exec();
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 9f5ea02e14..2438361a58 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -29,7 +29,6 @@
#endif
#include <chainparams.h>
-#include <init.h>
#include <interfaces/handler.h>
#include <interfaces/node.h>
#include <ui_interface.h>
@@ -55,14 +54,8 @@
#include <QStyle>
#include <QTimer>
#include <QToolBar>
-#include <QVBoxLayout>
-
-#if QT_VERSION < 0x050000
-#include <QTextDocument>
-#include <QUrl>
-#else
#include <QUrlQuery>
-#endif
+#include <QVBoxLayout>
const std::string BitcoinGUI::DEFAULT_UIPLATFORM =
#if defined(Q_OS_MAC)
@@ -76,50 +69,7 @@ const std::string BitcoinGUI::DEFAULT_UIPLATFORM =
BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformStyle, const NetworkStyle *networkStyle, QWidget *parent) :
QMainWindow(parent),
- enableWallet(false),
m_node(node),
- clientModel(0),
- walletFrame(0),
- unitDisplayControl(0),
- labelWalletEncryptionIcon(0),
- labelWalletHDStatusIcon(0),
- labelProxyIcon(0),
- connectionsControl(0),
- labelBlocksIcon(0),
- progressBarLabel(0),
- progressBar(0),
- progressDialog(0),
- appMenuBar(0),
- appToolBar(0),
- overviewAction(0),
- historyAction(0),
- quitAction(0),
- sendCoinsAction(0),
- sendCoinsMenuAction(0),
- usedSendingAddressesAction(0),
- usedReceivingAddressesAction(0),
- signMessageAction(0),
- verifyMessageAction(0),
- aboutAction(0),
- receiveCoinsAction(0),
- receiveCoinsMenuAction(0),
- optionsAction(0),
- toggleHideAction(0),
- encryptWalletAction(0),
- backupWalletAction(0),
- changePassphraseAction(0),
- aboutQtAction(0),
- openRPCConsoleAction(0),
- openAction(0),
- showHelpMessageAction(0),
- trayIcon(0),
- trayIconMenu(0),
- notificator(0),
- rpcConsole(0),
- helpMessageDialog(0),
- modalOverlay(0),
- prevBlocks(0),
- spinnerFrame(0),
platformStyle(_platformStyle)
{
QSettings settings;
@@ -147,12 +97,6 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty
#endif
setWindowTitle(windowTitle);
-#if defined(Q_OS_MAC) && QT_VERSION < 0x050000
- // This property is not implemented in Qt 5. Setting it has no effect.
- // A replacement API (QtMacUnifiedToolBar) is available in QtMacExtras.
- setUnifiedTitleAndToolBarOnMac(true);
-#endif
-
rpcConsole = new RPCConsole(node, _platformStyle, 0);
helpMessageDialog = new HelpMessageDialog(node, this, false);
#ifdef ENABLE_WALLET
@@ -229,7 +173,7 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty
// Override style sheet for progress bar for styles that have a segmented progress bar,
// as they make the text unreadable (workaround for issue #1071)
- // See https://qt-project.org/doc/qt-4.8/gallery.html
+ // See https://doc.qt.io/qt-5/gallery.html
QString curStyle = QApplication::style()->metaObject()->className();
if(curStyle == "QWindowsStyle" || curStyle == "QWindowsXPStyle")
{
@@ -477,6 +421,16 @@ void BitcoinGUI::createToolBars()
m_wallet_selector = new QComboBox();
connect(m_wallet_selector, SIGNAL(currentIndexChanged(int)), this, SLOT(setCurrentWalletBySelectorIndex(int)));
+
+ m_wallet_selector_label = new QLabel();
+ m_wallet_selector_label->setText(tr("Wallet:") + " ");
+ m_wallet_selector_label->setBuddy(m_wallet_selector);
+
+ m_wallet_selector_label_action = appToolBar->addWidget(m_wallet_selector_label);
+ m_wallet_selector_action = appToolBar->addWidget(m_wallet_selector);
+
+ m_wallet_selector_label_action->setVisible(false);
+ m_wallet_selector_action->setVisible(false);
#endif
}
}
@@ -556,16 +510,29 @@ bool BitcoinGUI::addWallet(WalletModel *walletModel)
setWalletActionsEnabled(true);
m_wallet_selector->addItem(display_name, name);
if (m_wallet_selector->count() == 2) {
- m_wallet_selector_label = new QLabel();
- m_wallet_selector_label->setText(tr("Wallet:") + " ");
- m_wallet_selector_label->setBuddy(m_wallet_selector);
- appToolBar->addWidget(m_wallet_selector_label);
- appToolBar->addWidget(m_wallet_selector);
+ m_wallet_selector_label_action->setVisible(true);
+ m_wallet_selector_action->setVisible(true);
}
rpcConsole->addWallet(walletModel);
return walletFrame->addWallet(walletModel);
}
+bool BitcoinGUI::removeWallet(WalletModel* walletModel)
+{
+ if (!walletFrame) return false;
+ QString name = walletModel->getWalletName();
+ int index = m_wallet_selector->findData(name);
+ m_wallet_selector->removeItem(index);
+ if (m_wallet_selector->count() == 0) {
+ setWalletActionsEnabled(false);
+ } else if (m_wallet_selector->count() == 1) {
+ m_wallet_selector_label_action->setVisible(false);
+ m_wallet_selector_action->setVisible(false);
+ }
+ rpcConsole->removeWallet(walletModel);
+ return walletFrame->removeWallet(name);
+}
+
bool BitcoinGUI::setCurrentWallet(const QString& name)
{
if(!walletFrame)
diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h
index 89c1c73a79..4deeb325b3 100644
--- a/src/qt/bitcoingui.h
+++ b/src/qt/bitcoingui.h
@@ -70,9 +70,10 @@ public:
functionality.
*/
bool addWallet(WalletModel *walletModel);
+ bool removeWallet(WalletModel* walletModel);
void removeAllWallets();
#endif // ENABLE_WALLET
- bool enableWallet;
+ bool enableWallet = false;
protected:
void changeEvent(QEvent *e);
@@ -86,56 +87,58 @@ private:
interfaces::Node& m_node;
std::unique_ptr<interfaces::Handler> m_handler_message_box;
std::unique_ptr<interfaces::Handler> m_handler_question;
- ClientModel *clientModel;
- WalletFrame *walletFrame;
-
- UnitDisplayStatusBarControl *unitDisplayControl;
- QLabel *labelWalletEncryptionIcon;
- QLabel *labelWalletHDStatusIcon;
- QLabel *labelProxyIcon;
- QLabel *connectionsControl;
- QLabel *labelBlocksIcon;
- QLabel *progressBarLabel;
- QProgressBar *progressBar;
- QProgressDialog *progressDialog;
-
- QMenuBar *appMenuBar;
- QToolBar *appToolBar;
- QAction *overviewAction;
- QAction *historyAction;
- QAction *quitAction;
- QAction *sendCoinsAction;
- QAction *sendCoinsMenuAction;
- QAction *usedSendingAddressesAction;
- QAction *usedReceivingAddressesAction;
- QAction *signMessageAction;
- QAction *verifyMessageAction;
- QAction *aboutAction;
- QAction *receiveCoinsAction;
- QAction *receiveCoinsMenuAction;
- QAction *optionsAction;
- QAction *toggleHideAction;
- QAction *encryptWalletAction;
- QAction *backupWalletAction;
- QAction *changePassphraseAction;
- QAction *aboutQtAction;
- QAction *openRPCConsoleAction;
- QAction *openAction;
- QAction *showHelpMessageAction;
-
- QLabel *m_wallet_selector_label;
- QComboBox *m_wallet_selector;
-
- QSystemTrayIcon *trayIcon;
- QMenu *trayIconMenu;
- Notificator *notificator;
- RPCConsole *rpcConsole;
- HelpMessageDialog *helpMessageDialog;
- ModalOverlay *modalOverlay;
+ ClientModel* clientModel = nullptr;
+ WalletFrame* walletFrame = nullptr;
+
+ UnitDisplayStatusBarControl* unitDisplayControl = nullptr;
+ QLabel* labelWalletEncryptionIcon = nullptr;
+ QLabel* labelWalletHDStatusIcon = nullptr;
+ QLabel* labelProxyIcon = nullptr;
+ QLabel* connectionsControl = nullptr;
+ QLabel* labelBlocksIcon = nullptr;
+ QLabel* progressBarLabel = nullptr;
+ QProgressBar* progressBar = nullptr;
+ QProgressDialog* progressDialog = nullptr;
+
+ QMenuBar* appMenuBar = nullptr;
+ QToolBar* appToolBar = nullptr;
+ QAction* overviewAction = nullptr;
+ QAction* historyAction = nullptr;
+ QAction* quitAction = nullptr;
+ QAction* sendCoinsAction = nullptr;
+ QAction* sendCoinsMenuAction = nullptr;
+ QAction* usedSendingAddressesAction = nullptr;
+ QAction* usedReceivingAddressesAction = nullptr;
+ QAction* signMessageAction = nullptr;
+ QAction* verifyMessageAction = nullptr;
+ QAction* aboutAction = nullptr;
+ QAction* receiveCoinsAction = nullptr;
+ QAction* receiveCoinsMenuAction = nullptr;
+ QAction* optionsAction = nullptr;
+ QAction* toggleHideAction = nullptr;
+ QAction* encryptWalletAction = nullptr;
+ QAction* backupWalletAction = nullptr;
+ QAction* changePassphraseAction = nullptr;
+ QAction* aboutQtAction = nullptr;
+ QAction* openRPCConsoleAction = nullptr;
+ QAction* openAction = nullptr;
+ QAction* showHelpMessageAction = nullptr;
+ QAction* m_wallet_selector_label_action = nullptr;
+ QAction* m_wallet_selector_action = nullptr;
+
+ QLabel *m_wallet_selector_label = nullptr;
+ QComboBox* m_wallet_selector = nullptr;
+
+ QSystemTrayIcon* trayIcon = nullptr;
+ QMenu* trayIconMenu = nullptr;
+ Notificator* notificator = nullptr;
+ RPCConsole* rpcConsole = nullptr;
+ HelpMessageDialog* helpMessageDialog = nullptr;
+ ModalOverlay* modalOverlay = nullptr;
/** Keep track of previous number of blocks, to detect progress */
- int prevBlocks;
- int spinnerFrame;
+ int prevBlocks = 0;
+ int spinnerFrame = 0;
const PlatformStyle *platformStyle;
@@ -254,7 +257,7 @@ private Q_SLOTS:
/** Simply calls showNormalIfMinimized(true) for use in SLOT() macro */
void toggleHidden();
- /** called by a timer to check if fRequestShutdown has been set **/
+ /** called by a timer to check if ShutdownRequested() has been set **/
void detectShutdown();
/** Show progress dialog e.g. for verifychain */
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index b08de27041..9b6480a915 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -109,11 +109,7 @@ CoinControlDialog::CoinControlDialog(const PlatformStyle *_platformStyle, QWidge
connect(ui->treeWidget, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(viewItemChanged(QTreeWidgetItem*, int)));
// click on header
-#if QT_VERSION < 0x050000
- ui->treeWidget->header()->setClickable(true);
-#else
ui->treeWidget->header()->setSectionsClickable(true);
-#endif
connect(ui->treeWidget->header(), SIGNAL(sectionClicked(int)), this, SLOT(headerSectionClicked(int)));
// ok button
@@ -122,10 +118,6 @@ CoinControlDialog::CoinControlDialog(const PlatformStyle *_platformStyle, QWidge
// (un)select all
connect(ui->pushButtonSelectAll, SIGNAL(clicked()), this, SLOT(buttonSelectAllClicked()));
- // change coin control first column label due Qt4 bug.
- // see https://github.com/bitcoin/bitcoin/issues/5716
- ui->treeWidget->headerItem()->setText(COLUMN_CHECKBOX, QString());
-
ui->treeWidget->setColumnWidth(COLUMN_CHECKBOX, 84);
ui->treeWidget->setColumnWidth(COLUMN_AMOUNT, 110);
ui->treeWidget->setColumnWidth(COLUMN_LABEL, 190);
@@ -392,13 +384,11 @@ void CoinControlDialog::viewItemChanged(QTreeWidgetItem* item, int column)
// TODO: Remove this temporary qt5 fix after Qt5.3 and Qt5.4 are no longer used.
// Fixed in Qt5.5 and above: https://bugreports.qt.io/browse/QTBUG-43473
-#if QT_VERSION >= 0x050000
else if (column == COLUMN_CHECKBOX && item->childCount() > 0)
{
if (item->checkState(COLUMN_CHECKBOX) == Qt::PartiallyChecked && item->child(0)->checkState(COLUMN_CHECKBOX) == Qt::PartiallyChecked)
item->setCheckState(COLUMN_CHECKBOX, Qt::Checked);
}
-#endif
}
// shows count of locked unspent outputs
diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h
index 1d21d8c766..ff47653fb7 100644
--- a/src/qt/guiconstants.h
+++ b/src/qt/guiconstants.h
@@ -27,8 +27,6 @@ static const bool DEFAULT_SPLASHSCREEN = true;
#define COLOR_BAREADDRESS QColor(140, 140, 140)
/* Transaction list -- TX status decoration - open until date */
#define COLOR_TX_STATUS_OPENUNTILDATE QColor(64, 64, 255)
-/* Transaction list -- TX status decoration - offline */
-#define COLOR_TX_STATUS_OFFLINE QColor(192, 192, 192)
/* Transaction list -- TX status decoration - danger, tx needs attention */
#define COLOR_TX_STATUS_DANGER QColor(200, 100, 100)
/* Transaction list -- TX status decoration - default color */
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index 563f930dec..acd9f7b35c 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -53,13 +53,9 @@
#include <QSettings>
#include <QTextDocument> // for Qt::mightBeRichText
#include <QThread>
+#include <QUrlQuery>
#include <QMouseEvent>
-#if QT_VERSION < 0x050000
-#include <QUrl>
-#else
-#include <QUrlQuery>
-#endif
#if QT_VERSION >= 0x50200
#include <QFontDatabase>
@@ -95,11 +91,7 @@ QFont fixedPitchFont()
return QFontDatabase::systemFont(QFontDatabase::FixedFont);
#else
QFont font("Monospace");
-#if QT_VERSION >= 0x040800
font.setStyleHint(QFont::Monospace);
-#else
- font.setStyleHint(QFont::TypeWriter);
-#endif
return font;
#endif
}
@@ -127,12 +119,10 @@ void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent)
parent->setFocusProxy(widget);
widget->setFont(fixedPitchFont());
-#if QT_VERSION >= 0x040700
// We don't want translators to use own addresses in translations
// and this is the only place, where this address is supplied.
widget->setPlaceholderText(QObject::tr("Enter a Bitcoin address (e.g. %1)").arg(
QString::fromStdString(DummyAddress(Params()))));
-#endif
widget->setValidator(new BitcoinAddressEntryValidator(parent));
widget->setCheckValidator(new BitcoinAddressCheckValidator(parent));
}
@@ -151,12 +141,8 @@ bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out)
}
rv.amount = 0;
-#if QT_VERSION < 0x050000
- QList<QPair<QString, QString> > items = uri.queryItems();
-#else
QUrlQuery uriQuery(uri);
QList<QPair<QString, QString> > items = uriQuery.queryItems();
-#endif
for (QList<QPair<QString, QString> >::iterator i = items.begin(); i != items.end(); i++)
{
bool fShouldReturnFalse = false;
@@ -242,11 +228,7 @@ bool isDust(interfaces::Node& node, const QString& address, const CAmount& amoun
QString HtmlEscape(const QString& str, bool fMultiLine)
{
-#if QT_VERSION < 0x050000
- QString escaped = Qt::escape(str);
-#else
QString escaped = str.toHtmlEscaped();
-#endif
if(fMultiLine)
{
escaped = escaped.replace("\n", "<br>\n");
@@ -287,11 +269,7 @@ QString getSaveFileName(QWidget *parent, const QString &caption, const QString &
QString myDir;
if(dir.isEmpty()) // Default to user documents location
{
-#if QT_VERSION < 0x050000
- myDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation);
-#else
myDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
-#endif
}
else
{
@@ -337,11 +315,7 @@ QString getOpenFileName(QWidget *parent, const QString &caption, const QString &
QString myDir;
if(dir.isEmpty()) // Default to user documents location
{
-#if QT_VERSION < 0x050000
- myDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation);
-#else
myDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
-#endif
}
else
{
@@ -495,11 +469,7 @@ void TableViewLastColumnResizingFixer::disconnectViewHeadersSignals()
// Refactored here for readability.
void TableViewLastColumnResizingFixer::setViewHeaderResizeMode(int logicalIndex, QHeaderView::ResizeMode resizeMode)
{
-#if QT_VERSION < 0x050000
- tableView->horizontalHeader()->setResizeMode(logicalIndex, resizeMode);
-#else
tableView->horizontalHeader()->setSectionResizeMode(logicalIndex, resizeMode);
-#endif
}
void TableViewLastColumnResizingFixer::resizeColumn(int nColumnIndex, int width)
diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h
index 4a26964098..e965a91d18 100644
--- a/src/qt/guiutil.h
+++ b/src/qt/guiutil.h
@@ -233,7 +233,7 @@ namespace GUIUtil
void mouseReleaseEvent(QMouseEvent *event);
};
-#if defined(Q_OS_MAC) && QT_VERSION >= 0x050000
+#if defined(Q_OS_MAC)
// workaround for Qt OSX Bug:
// https://bugreports.qt-project.org/browse/QTBUG-15631
// QProgressBar uses around 10% CPU even when app is in background
diff --git a/src/qt/macdockiconhandler.mm b/src/qt/macdockiconhandler.mm
index 9e7de0f98f..a0b62ae000 100644
--- a/src/qt/macdockiconhandler.mm
+++ b/src/qt/macdockiconhandler.mm
@@ -14,10 +14,6 @@
#include <objc/objc.h>
#include <objc/message.h>
-#if QT_VERSION < 0x050000
-extern void qt_mac_set_dock_menu(QMenu *);
-#endif
-
static MacDockIconHandler *s_instance = nullptr;
bool dockClickHandler(id self,SEL _cmd,...) {
@@ -54,9 +50,7 @@ MacDockIconHandler::MacDockIconHandler() : QObject()
this->m_dummyWidget = new QWidget();
this->m_dockMenu = new QMenu(this->m_dummyWidget);
this->setMainWindow(nullptr);
-#if QT_VERSION < 0x050000
- qt_mac_set_dock_menu(this->m_dockMenu);
-#elif QT_VERSION >= 0x050200
+#if QT_VERSION >= 0x050200
this->m_dockMenu->setAsDockMenu();
#endif
[pool release];
diff --git a/src/qt/networkstyle.cpp b/src/qt/networkstyle.cpp
index 2816633b0f..c4b732e3e0 100644
--- a/src/qt/networkstyle.cpp
+++ b/src/qt/networkstyle.cpp
@@ -68,11 +68,7 @@ NetworkStyle::NetworkStyle(const QString &_appName, const int iconColorHueShift,
}
//convert back to QPixmap
-#if QT_VERSION >= 0x040700
pixmap.convertFromImage(img);
-#else
- pixmap = QPixmap::fromImage(img);
-#endif
}
appIcon = QIcon(pixmap);
diff --git a/src/qt/openuridialog.cpp b/src/qt/openuridialog.cpp
index 751cfa8b43..d211f908c8 100644
--- a/src/qt/openuridialog.cpp
+++ b/src/qt/openuridialog.cpp
@@ -15,9 +15,7 @@ OpenURIDialog::OpenURIDialog(QWidget *parent) :
ui(new Ui::OpenURIDialog)
{
ui->setupUi(this);
-#if QT_VERSION >= 0x040700
ui->uriEdit->setPlaceholderText("bitcoin:");
-#endif
}
OpenURIDialog::~OpenURIDialog()
diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp
index 108aa4f99c..a57343f036 100644
--- a/src/qt/optionsdialog.cpp
+++ b/src/qt/optionsdialog.cpp
@@ -97,28 +97,16 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) :
/** check if the locale name consists of 2 parts (language_country) */
if(langStr.contains("_"))
{
-#if QT_VERSION >= 0x040800
/** display language strings as "native language - native country (locale name)", e.g. "Deutsch - Deutschland (de)" */
ui->lang->addItem(locale.nativeLanguageName() + QString(" - ") + locale.nativeCountryName() + QString(" (") + langStr + QString(")"), QVariant(langStr));
-#else
- /** display language strings as "language - country (locale name)", e.g. "German - Germany (de)" */
- ui->lang->addItem(QLocale::languageToString(locale.language()) + QString(" - ") + QLocale::countryToString(locale.country()) + QString(" (") + langStr + QString(")"), QVariant(langStr));
-#endif
}
else
{
-#if QT_VERSION >= 0x040800
/** display language strings as "native language (locale name)", e.g. "Deutsch (de)" */
ui->lang->addItem(locale.nativeLanguageName() + QString(" (") + langStr + QString(")"), QVariant(langStr));
-#else
- /** display language strings as "language (locale name)", e.g. "German (de)" */
- ui->lang->addItem(QLocale::languageToString(locale.language()) + QString(" (") + langStr + QString(")"), QVariant(langStr));
-#endif
}
}
-#if QT_VERSION >= 0x040700
ui->thirdPartyTxUrls->setPlaceholderText("https://example.com/tx/%s");
-#endif
ui->unit->setModel(new BitcoinUnits(this));
@@ -343,7 +331,7 @@ void OptionsDialog::updateDefaultProxyNets()
strDefaultProxyGUI = ui->proxyIp->text() + ":" + ui->proxyPort->text();
(strProxy == strDefaultProxyGUI.toStdString()) ? ui->proxyReachIPv6->setChecked(true) : ui->proxyReachIPv6->setChecked(false);
- model->node().getProxy(NET_TOR, proxy);
+ model->node().getProxy(NET_ONION, proxy);
strProxy = proxy.proxy.ToStringIP() + ":" + proxy.proxy.ToStringPort();
strDefaultProxyGUI = ui->proxyIp->text() + ":" + ui->proxyPort->text();
(strProxy == strDefaultProxyGUI.toStdString()) ? ui->proxyReachTor->setChecked(true) : ui->proxyReachTor->setChecked(false);
diff --git a/src/qt/paymentrequestplus.cpp b/src/qt/paymentrequestplus.cpp
index b0ef475b35..dfeb70d669 100644
--- a/src/qt/paymentrequestplus.cpp
+++ b/src/qt/paymentrequestplus.cpp
@@ -97,12 +97,10 @@ bool PaymentRequestPlus::getMerchant(X509_STORE* certStore, QString& merchant) c
qWarning() << "PaymentRequestPlus::getMerchant: Payment request: certificate expired or not yet active: " << qCert;
return false;
}
-#if QT_VERSION >= 0x050000
if (qCert.isBlacklisted()) {
qWarning() << "PaymentRequestPlus::getMerchant: Payment request: certificate blacklisted: " << qCert;
return false;
}
-#endif
const unsigned char *data = (const unsigned char *)certChain.certificate(i).data();
X509 *cert = d2i_X509(nullptr, &data, certChain.certificate(i).size());
if (cert)
diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp
index 59bb5d5bb6..e5e6430959 100644
--- a/src/qt/paymentserver.cpp
+++ b/src/qt/paymentserver.cpp
@@ -41,12 +41,7 @@
#include <QSslSocket>
#include <QStringList>
#include <QTextDocument>
-
-#if QT_VERSION < 0x050000
-#include <QUrl>
-#else
#include <QUrlQuery>
-#endif
const int BITCOIN_IPC_CONNECT_TIMEOUT = 1000; // milliseconds
const QString BITCOIN_IPC_PREFIX("bitcoin:");
@@ -100,11 +95,7 @@ static QList<QString> savedPaymentRequests;
static void ReportInvalidCertificate(const QSslCertificate& cert)
{
-#if QT_VERSION < 0x050000
- qDebug() << QString("%1: Payment server found an invalid certificate: ").arg(__func__) << cert.serialNumber() << cert.subjectInfo(QSslCertificate::CommonName) << cert.subjectInfo(QSslCertificate::OrganizationalUnitName);
-#else
qDebug() << QString("%1: Payment server found an invalid certificate: ").arg(__func__) << cert.serialNumber() << cert.subjectInfo(QSslCertificate::CommonName) << cert.subjectInfo(QSslCertificate::DistinguishedNameQualifier) << cert.subjectInfo(QSslCertificate::OrganizationalUnitName);
-#endif
}
//
@@ -157,13 +148,11 @@ void PaymentServer::LoadRootCAs(X509_STORE* _store)
continue;
}
-#if QT_VERSION >= 0x050000
// Blacklisted certificate
if (cert.isBlacklisted()) {
ReportInvalidCertificate(cert);
continue;
}
-#endif
QByteArray certData = cert.toDer();
const unsigned char *data = (const unsigned char *)certData.data();
@@ -413,11 +402,7 @@ void PaymentServer::handleURIOrFile(const QString& s)
}
else if (s.startsWith(BITCOIN_IPC_PREFIX, Qt::CaseInsensitive)) // bitcoin: URI
{
-#if QT_VERSION < 0x050000
- QUrl uri(s);
-#else
QUrlQuery uri((QUrl(s)));
-#endif
if (uri.hasQueryItem("r")) // payment request URI
{
QByteArray temp;
diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp
index 7e318e3035..1c90504e9e 100644
--- a/src/qt/peertablemodel.cpp
+++ b/src/qt/peertablemodel.cpp
@@ -64,9 +64,7 @@ public:
interfaces::Node::NodesStats nodes_stats;
node.getNodesStats(nodes_stats);
-#if QT_VERSION >= 0x040700
cachedNodeStats.reserve(nodes_stats.size());
-#endif
for (auto& node_stats : nodes_stats)
{
CNodeCombinedStats stats;
@@ -162,7 +160,8 @@ QVariant PeerTableModel::data(const QModelIndex &index, int role) const
case NetNodeId:
return (qint64)rec->nodeStats.nodeid;
case Address:
- return QString::fromStdString(rec->nodeStats.addrName);
+ // prepend to peer address down-arrow symbol for inbound connection and up-arrow for outbound connection
+ return QString(rec->nodeStats.fInbound ? "↓ " : "↑ ") + QString::fromStdString(rec->nodeStats.addrName);
case Subversion:
return QString::fromStdString(rec->nodeStats.cleanSubVer);
case Ping:
diff --git a/src/qt/platformstyle.cpp b/src/qt/platformstyle.cpp
index fce71f661a..a3a10aac18 100644
--- a/src/qt/platformstyle.cpp
+++ b/src/qt/platformstyle.cpp
@@ -46,7 +46,7 @@ void MakeSingleColorImage(QImage& img, const QColor& colorbase)
QIcon ColorizeIcon(const QIcon& ico, const QColor& colorbase)
{
QIcon new_ico;
- for (const QSize sz : ico.availableSizes())
+ for (const QSize& sz : ico.availableSizes())
{
QImage img(ico.pixmap(sz).toImage());
MakeSingleColorImage(img, colorbase);
diff --git a/src/qt/receiverequestdialog.cpp b/src/qt/receiverequestdialog.cpp
index 75146e2214..f9610f2d3b 100644
--- a/src/qt/receiverequestdialog.cpp
+++ b/src/qt/receiverequestdialog.cpp
@@ -16,9 +16,6 @@
#include <QMimeData>
#include <QMouseEvent>
#include <QPixmap>
-#if QT_VERSION < 0x050000
-#include <QUrl>
-#endif
#if defined(HAVE_CONFIG_H)
#include <config/bitcoin-config.h> /* for USE_QRCODE */
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index 4550ae9396..f222357f27 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -40,10 +40,6 @@
#include <QTimer>
#include <QStringList>
-#if QT_VERSION < 0x050000
-#include <QUrl>
-#endif
-
// TODO: add a scrollback limit, as there is currently none
// TODO: make it possible to filter out categories (esp debug messages when implemented)
// TODO: receive errors and debug messages through ClientModel
@@ -713,6 +709,16 @@ void RPCConsole::addWallet(WalletModel * const walletModel)
ui->WalletSelectorLabel->setVisible(true);
}
}
+
+void RPCConsole::removeWallet(WalletModel * const walletModel)
+{
+ const QString name = walletModel->getWalletName();
+ ui->WalletSelector->removeItem(ui->WalletSelector->findData(name));
+ if (ui->WalletSelector->count() == 2) {
+ ui->WalletSelector->setVisible(false);
+ ui->WalletSelectorLabel->setVisible(false);
+ }
+}
#endif
static QString categoryClass(int category)
diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h
index a53c4c24f9..0a1a469934 100644
--- a/src/qt/rpcconsole.h
+++ b/src/qt/rpcconsole.h
@@ -48,6 +48,7 @@ public:
void setClientModel(ClientModel *model);
void addWallet(WalletModel * const walletModel);
+ void removeWallet(WalletModel* const walletModel);
enum MessageClass {
MC_ERROR,
diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp
index 977425f7e3..e8c85bc2a1 100644
--- a/src/qt/sendcoinsentry.cpp
+++ b/src/qt/sendcoinsentry.cpp
@@ -32,9 +32,7 @@ SendCoinsEntry::SendCoinsEntry(const PlatformStyle *_platformStyle, QWidget *par
if (platformStyle->getUseExtraSpacing())
ui->payToLayout->setSpacing(4);
-#if QT_VERSION >= 0x040700
ui->addAsLabel->setPlaceholderText(tr("Enter a label for this address to add it to your address book"));
-#endif
// normal bitcoin address field
GUIUtil::setupAddressWidget(ui->payTo, this);
diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp
index 94a3ad7987..223b39dc86 100644
--- a/src/qt/signverifymessagedialog.cpp
+++ b/src/qt/signverifymessagedialog.cpp
@@ -10,7 +10,6 @@
#include <qt/platformstyle.h>
#include <qt/walletmodel.h>
-#include <init.h>
#include <key_io.h>
#include <validation.h> // For strMessageMagic
#include <wallet/wallet.h>
@@ -37,9 +36,7 @@ SignVerifyMessageDialog::SignVerifyMessageDialog(const PlatformStyle *_platformS
ui->verifyMessageButton_VM->setIcon(platformStyle->SingleColorIcon(":/icons/transaction_0"));
ui->clearButton_VM->setIcon(platformStyle->SingleColorIcon(":/icons/remove"));
-#if QT_VERSION >= 0x040700
ui->signatureOut_SM->setPlaceholderText(tr("Click \"Sign Message\" to generate signature"));
-#endif
GUIUtil::setupAddressWidget(ui->addressIn_SM, this);
GUIUtil::setupAddressWidget(ui->addressIn_VM, this);
diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp
index 4d972b431c..831ef68cab 100644
--- a/src/qt/splashscreen.cpp
+++ b/src/qt/splashscreen.cpp
@@ -11,7 +11,6 @@
#include <qt/networkstyle.h>
#include <clientversion.h>
-#include <init.h>
#include <interfaces/handler.h>
#include <interfaces/node.h>
#include <interfaces/wallet.h>
diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp
index 56d4d3e457..f0ac43a58c 100644
--- a/src/qt/test/test_main.cpp
+++ b/src/qt/test/test_main.cpp
@@ -26,12 +26,6 @@
#if defined(QT_STATICPLUGIN)
#include <QtPlugin>
-#if QT_VERSION < 0x050000
-Q_IMPORT_PLUGIN(qcncodecs)
-Q_IMPORT_PLUGIN(qjpcodecs)
-Q_IMPORT_PLUGIN(qtwcodecs)
-Q_IMPORT_PLUGIN(qkrcodecs)
-#else
#if defined(QT_QPA_PLATFORM_MINIMAL)
Q_IMPORT_PLUGIN(QMinimalIntegrationPlugin);
#endif
@@ -43,7 +37,6 @@ Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin);
Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin);
#endif
#endif
-#endif
extern void noui_connect();
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
index 33c49dc7cb..c314dadde4 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -87,17 +87,6 @@ QModelIndex FindTx(const QAbstractItemModel& model, const uint256& txid)
return {};
}
-//! Request context menu (call method that is public in qt5, but protected in qt4).
-void RequestContextMenu(QWidget* widget)
-{
- class Qt4Hack : public QWidget
- {
- public:
- using QWidget::customContextMenuRequested;
- };
- static_cast<Qt4Hack*>(widget)->customContextMenuRequested({});
-}
-
//! Invoke bumpfee on txid and check results.
void BumpFee(TransactionView& view, const uint256& txid, bool expectDisabled, std::string expectError, bool cancel)
{
@@ -110,7 +99,7 @@ void BumpFee(TransactionView& view, const uint256& txid, bool expectDisabled, st
QAction* action = view.findChild<QAction*>("bumpFeeAction");
table->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
action->setEnabled(expectDisabled);
- RequestContextMenu(table);
+ table->customContextMenuRequested({});
QCOMPARE(action->isEnabled(), !expectDisabled);
action->setEnabled(true);
diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp
index 2cb446c459..8297f75799 100644
--- a/src/qt/transactiondesc.cpp
+++ b/src/qt/transactiondesc.cpp
@@ -37,8 +37,6 @@ QString TransactionDesc::FormatTxStatus(const interfaces::WalletTx& wtx, const i
int nDepth = status.depth_in_main_chain;
if (nDepth < 0)
return tr("conflicted with a transaction with %1 confirmations").arg(-nDepth);
- else if (adjustedTime - status.time_received > 2 * 60 && status.request_count == 0)
- return tr("%1/offline").arg(nDepth);
else if (nDepth == 0)
return tr("0/unconfirmed, %1").arg((inMempool ? tr("in memory pool") : tr("not in memory pool"))) + (status.is_abandoned ? ", "+tr("abandoned") : "");
else if (nDepth < 6)
@@ -68,14 +66,6 @@ QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wall
CAmount nNet = nCredit - nDebit;
strHTML += "<b>" + tr("Status") + ":</b> " + FormatTxStatus(wtx, status, inMempool, numBlocks, adjustedTime);
- int nRequests = status.request_count;
- if (nRequests != -1)
- {
- if (nRequests == 0)
- strHTML += tr(", has not been successfully broadcast yet");
- else if (nRequests > 0)
- strHTML += tr(", broadcast through %n node(s)", "", nRequests);
- }
strHTML += "<br>";
strHTML += "<b>" + tr("Date") + ":</b> " + (nTime ? GUIUtil::dateTimeStr(nTime) : "") + "<br>";
diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp
index b6ed66ad96..65f5e87d15 100644
--- a/src/qt/transactionrecord.cpp
+++ b/src/qt/transactionrecord.cpp
@@ -195,10 +195,6 @@ void TransactionRecord::updateStatus(const interfaces::WalletTxStatus& wtx, int
if (wtx.is_in_main_chain)
{
status.matures_in = wtx.blocks_to_maturity;
-
- // Check if the block was requested by anyone
- if (adjustedTime - wtx.time_received > 2 * 60 && wtx.request_count == 0)
- status.status = TransactionStatus::MaturesWarning;
}
else
{
@@ -216,10 +212,6 @@ void TransactionRecord::updateStatus(const interfaces::WalletTxStatus& wtx, int
{
status.status = TransactionStatus::Conflicted;
}
- else if (adjustedTime - wtx.time_received > 2 * 60 && wtx.request_count == 0)
- {
- status.status = TransactionStatus::Offline;
- }
else if (status.depth == 0)
{
status.status = TransactionStatus::Unconfirmed;
diff --git a/src/qt/transactionrecord.h b/src/qt/transactionrecord.h
index 62961434ed..a6424e74fa 100644
--- a/src/qt/transactionrecord.h
+++ b/src/qt/transactionrecord.h
@@ -25,7 +25,7 @@ class TransactionStatus
public:
TransactionStatus():
countsForBalance(false), sortKey(""),
- matures_in(0), status(Offline), depth(0), open_for(0), cur_num_blocks(-1)
+ matures_in(0), status(Unconfirmed), depth(0), open_for(0), cur_num_blocks(-1)
{ }
enum Status {
@@ -33,14 +33,12 @@ public:
/// Normal (sent/received) transactions
OpenUntilDate, /**< Transaction not yet final, waiting for date */
OpenUntilBlock, /**< Transaction not yet final, waiting for block */
- Offline, /**< Not sent to any other nodes **/
Unconfirmed, /**< Not yet mined into a block **/
Confirming, /**< Confirmed, but waiting for the recommended number of confirmations **/
Conflicted, /**< Conflicts with other transaction or mempool **/
Abandoned, /**< Abandoned from the wallet **/
/// Generated (mined) transactions
Immature, /**< Mined but waiting for maturity */
- MaturesWarning, /**< Transaction will likely not mature because no nodes have confirmed */
NotAccepted /**< Mined but not accepted */
};
diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp
index 46169a91d1..63a4afe191 100644
--- a/src/qt/transactiontablemodel.cpp
+++ b/src/qt/transactiontablemodel.cpp
@@ -286,9 +286,6 @@ QString TransactionTableModel::formatTxStatus(const TransactionRecord *wtx) cons
case TransactionStatus::OpenUntilDate:
status = tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx->status.open_for));
break;
- case TransactionStatus::Offline:
- status = tr("Offline");
- break;
case TransactionStatus::Unconfirmed:
status = tr("Unconfirmed");
break;
@@ -307,9 +304,6 @@ QString TransactionTableModel::formatTxStatus(const TransactionRecord *wtx) cons
case TransactionStatus::Immature:
status = tr("Immature (%1 confirmations, will be available after %2)").arg(wtx->status.depth).arg(wtx->status.depth + wtx->status.matures_in);
break;
- case TransactionStatus::MaturesWarning:
- status = tr("This block was not received by any other nodes and will probably not be accepted!");
- break;
case TransactionStatus::NotAccepted:
status = tr("Generated but not accepted");
break;
@@ -447,8 +441,6 @@ QVariant TransactionTableModel::txStatusDecoration(const TransactionRecord *wtx)
case TransactionStatus::OpenUntilBlock:
case TransactionStatus::OpenUntilDate:
return COLOR_TX_STATUS_OPENUNTILDATE;
- case TransactionStatus::Offline:
- return COLOR_TX_STATUS_OFFLINE;
case TransactionStatus::Unconfirmed:
return QIcon(":/icons/transaction_0");
case TransactionStatus::Abandoned:
@@ -471,7 +463,6 @@ QVariant TransactionTableModel::txStatusDecoration(const TransactionRecord *wtx)
int part = (wtx->status.depth * 4 / total) + 1;
return QIcon(QString(":/icons/transaction_%1").arg(part));
}
- case TransactionStatus::MaturesWarning:
case TransactionStatus::NotAccepted:
return QIcon(":/icons/transaction_0");
default:
diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp
index aa6444245a..e60a387934 100644
--- a/src/qt/transactionview.cpp
+++ b/src/qt/transactionview.cpp
@@ -95,15 +95,11 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
hlayout->addWidget(typeWidget);
search_widget = new QLineEdit(this);
-#if QT_VERSION >= 0x040700
search_widget->setPlaceholderText(tr("Enter address, transaction id, or label to search"));
-#endif
hlayout->addWidget(search_widget);
amountWidget = new QLineEdit(this);
-#if QT_VERSION >= 0x040700
amountWidget->setPlaceholderText(tr("Min amount"));
-#endif
if (platformStyle->getUseExtraSpacing()) {
amountWidget->setFixedWidth(97);
} else {
diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp
index eb0eba21ef..c5a13f61f4 100644
--- a/src/qt/walletframe.cpp
+++ b/src/qt/walletframe.cpp
@@ -94,6 +94,7 @@ bool WalletFrame::removeWallet(const QString &name)
WalletView *walletView = mapWalletViews.take(name);
walletStack->removeWidget(walletView);
+ delete walletView;
return true;
}
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index 3418b1f1a9..389acf0a95 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -364,6 +364,12 @@ bool WalletModel::changePassphrase(const SecureString &oldPass, const SecureStri
}
// Handlers for core signals
+static void NotifyUnload(WalletModel* walletModel)
+{
+ qDebug() << "NotifyUnload";
+ QMetaObject::invokeMethod(walletModel, "unload", Qt::QueuedConnection);
+}
+
static void NotifyKeyStoreStatusChanged(WalletModel *walletmodel)
{
qDebug() << "NotifyKeyStoreStatusChanged";
@@ -411,6 +417,7 @@ static void NotifyWatchonlyChanged(WalletModel *walletmodel, bool fHaveWatchonly
void WalletModel::subscribeToCoreSignals()
{
// Connect signals to wallet
+ m_handler_unload = m_wallet->handleUnload(boost::bind(&NotifyUnload, this));
m_handler_status_changed = m_wallet->handleStatusChanged(boost::bind(&NotifyKeyStoreStatusChanged, this));
m_handler_address_book_changed = m_wallet->handleAddressBookChanged(boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4, _5));
m_handler_transaction_changed = m_wallet->handleTransactionChanged(boost::bind(NotifyTransactionChanged, this, _1, _2));
@@ -421,6 +428,7 @@ void WalletModel::subscribeToCoreSignals()
void WalletModel::unsubscribeFromCoreSignals()
{
// Disconnect signals from wallet
+ m_handler_unload->disconnect();
m_handler_status_changed->disconnect();
m_handler_address_book_changed->disconnect();
m_handler_transaction_changed->disconnect();
diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h
index 9173fcae52..35ededb121 100644
--- a/src/qt/walletmodel.h
+++ b/src/qt/walletmodel.h
@@ -208,6 +208,7 @@ public:
AddressTableModel* getAddressTableModel() const { return addressTableModel; }
private:
std::unique_ptr<interfaces::Wallet> m_wallet;
+ std::unique_ptr<interfaces::Handler> m_handler_unload;
std::unique_ptr<interfaces::Handler> m_handler_status_changed;
std::unique_ptr<interfaces::Handler> m_handler_address_book_changed;
std::unique_ptr<interfaces::Handler> m_handler_transaction_changed;
@@ -261,6 +262,9 @@ Q_SIGNALS:
// Watch-only address added
void notifyWatchonlyChanged(bool fHaveWatchonly);
+ // Signal that wallet is about to be removed
+ void unload();
+
public Q_SLOTS:
/* Wallet status might have changed */
void updateStatus();
diff --git a/src/qt/winshutdownmonitor.cpp b/src/qt/winshutdownmonitor.cpp
index 1e7a76efc0..122d6f0b12 100644
--- a/src/qt/winshutdownmonitor.cpp
+++ b/src/qt/winshutdownmonitor.cpp
@@ -4,8 +4,8 @@
#include <qt/winshutdownmonitor.h>
-#if defined(Q_OS_WIN) && QT_VERSION >= 0x050000
-#include <init.h>
+#if defined(Q_OS_WIN)
+#include <shutdown.h>
#include <util.h>
#include <windows.h>
diff --git a/src/qt/winshutdownmonitor.h b/src/qt/winshutdownmonitor.h
index 0bed55a2c6..c8a523a538 100644
--- a/src/qt/winshutdownmonitor.h
+++ b/src/qt/winshutdownmonitor.h
@@ -9,7 +9,6 @@
#include <QByteArray>
#include <QString>
-#if QT_VERSION >= 0x050000
#include <windef.h> // for HWND
#include <QAbstractNativeEventFilter>
@@ -24,6 +23,5 @@ public:
static void registerShutdownBlockReason(const QString& strReason, const HWND& mainWinId);
};
#endif
-#endif
#endif // BITCOIN_QT_WINSHUTDOWNMONITOR_H
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 68279d7ffd..012e3e3ac1 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -6,6 +6,8 @@
#include <rpc/blockchain.h>
#include <amount.h>
+#include <base58.h>
+#include <chain.h>
#include <chainparams.h>
#include <checkpoints.h>
#include <coins.h>
@@ -13,6 +15,7 @@
#include <validation.h>
#include <core_io.h>
#include <index/txindex.h>
+#include <key_io.h>
#include <policy/feerate.h>
#include <policy/policy.h>
#include <primitives/transaction.h>
@@ -27,6 +30,7 @@
#include <validationinterface.h>
#include <warnings.h>
+#include <assert.h>
#include <stdint.h>
#include <univalue.h>
@@ -853,7 +857,7 @@ static void ApplyStats(CCoinsStats &stats, CHashWriter& ss, const uint256& hash,
ss << hash;
ss << VARINT(outputs.begin()->second.nHeight * 2 + outputs.begin()->second.fCoinBase ? 1u : 0u);
stats.nTransactions++;
- for (const auto output : outputs) {
+ for (const auto& output : outputs) {
ss << VARINT(output.first + 1);
ss << output.second.out.scriptPubKey;
ss << VARINT(output.second.out.nValue, VarIntMode::NONNEGATIVE_SIGNED);
@@ -1920,6 +1924,290 @@ static UniValue savemempool(const JSONRPCRequest& request)
return NullUniValue;
}
+//! Search for a given set of pubkey scripts
+bool FindScriptPubKey(std::atomic<int>& scan_progress, const std::atomic<bool>& should_abort, int64_t& count, CCoinsViewCursor* cursor, const std::set<CScript>& needles, std::map<COutPoint, Coin>& out_results) {
+ scan_progress = 0;
+ count = 0;
+ while (cursor->Valid()) {
+ COutPoint key;
+ Coin coin;
+ if (!cursor->GetKey(key) || !cursor->GetValue(coin)) return false;
+ if (++count % 8192 == 0) {
+ boost::this_thread::interruption_point();
+ if (should_abort) {
+ // allow to abort the scan via the abort reference
+ return false;
+ }
+ }
+ if (count % 256 == 0) {
+ // update progress reference every 256 item
+ uint32_t high = 0x100 * *key.hash.begin() + *(key.hash.begin() + 1);
+ scan_progress = (int)(high * 100.0 / 65536.0 + 0.5);
+ }
+ if (needles.count(coin.out.scriptPubKey)) {
+ out_results.emplace(key, coin);
+ }
+ cursor->Next();
+ }
+ scan_progress = 100;
+ return true;
+}
+
+/** RAII object to prevent concurrency issue when scanning the txout set */
+static std::mutex g_utxosetscan;
+static std::atomic<int> g_scan_progress;
+static std::atomic<bool> g_scan_in_progress;
+static std::atomic<bool> g_should_abort_scan;
+class CoinsViewScanReserver
+{
+private:
+ bool m_could_reserve;
+public:
+ explicit CoinsViewScanReserver() : m_could_reserve(false) {}
+
+ bool reserve() {
+ assert (!m_could_reserve);
+ std::lock_guard<std::mutex> lock(g_utxosetscan);
+ if (g_scan_in_progress) {
+ return false;
+ }
+ g_scan_in_progress = true;
+ m_could_reserve = true;
+ return true;
+ }
+
+ ~CoinsViewScanReserver() {
+ if (m_could_reserve) {
+ std::lock_guard<std::mutex> lock(g_utxosetscan);
+ g_scan_in_progress = false;
+ }
+ }
+};
+
+static const char *g_default_scantxoutset_script_types[] = { "P2PKH", "P2SH_P2WPKH", "P2WPKH" };
+
+enum class OutputScriptType {
+ UNKNOWN,
+ P2PK,
+ P2PKH,
+ P2SH_P2WPKH,
+ P2WPKH
+};
+
+static inline OutputScriptType GetOutputScriptTypeFromString(const std::string& outputtype)
+{
+ if (outputtype == "P2PK") return OutputScriptType::P2PK;
+ else if (outputtype == "P2PKH") return OutputScriptType::P2PKH;
+ else if (outputtype == "P2SH_P2WPKH") return OutputScriptType::P2SH_P2WPKH;
+ else if (outputtype == "P2WPKH") return OutputScriptType::P2WPKH;
+ else return OutputScriptType::UNKNOWN;
+}
+
+CTxDestination GetDestinationForKey(const CPubKey& key, OutputScriptType type)
+{
+ switch (type) {
+ case OutputScriptType::P2PKH: return key.GetID();
+ case OutputScriptType::P2SH_P2WPKH:
+ case OutputScriptType::P2WPKH: {
+ if (!key.IsCompressed()) return key.GetID();
+ CTxDestination witdest = WitnessV0KeyHash(key.GetID());
+ if (type == OutputScriptType::P2SH_P2WPKH) {
+ CScript witprog = GetScriptForDestination(witdest);
+ return CScriptID(witprog);
+ } else {
+ return witdest;
+ }
+ }
+ default: assert(false);
+ }
+}
+
+UniValue scantxoutset(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
+ throw std::runtime_error(
+ "scantxoutset <action> ( <scanobjects> )\n"
+ "\nScans the unspent transaction output set for possible entries that matches common scripts of given public keys.\n"
+ "Using addresses as scanobjects will _not_ detect unspent P2PK txouts\n"
+ "\nArguments:\n"
+ "1. \"action\" (string, required) The action to execute\n"
+ " \"start\" for starting a scan\n"
+ " \"abort\" for aborting the current scan (returns true when abort was successful)\n"
+ " \"status\" for progress report (in %) of the current scan\n"
+ "2. \"scanobjects\" (array, optional) Array of scan objects (only one object type per scan object allowed)\n"
+ " [\n"
+ " { \"address\" : \"<address>\" }, (string, optional) Bitcoin address\n"
+ " { \"script\" : \"<scriptPubKey>\" }, (string, optional) HEX encoded script (scriptPubKey)\n"
+ " { \"pubkey\" : (object, optional) Public key\n"
+ " {\n"
+ " \"pubkey\" : \"<pubkey\">, (string, required) HEX encoded public key\n"
+ " \"script_types\" : [ ... ], (array, optional) Array of script-types to derive from the pubkey (possible values: \"P2PK\", \"P2PKH\", \"P2SH-P2WPKH\", \"P2WPKH\")\n"
+ " }\n"
+ " },\n"
+ " ]\n"
+ "\nResult:\n"
+ "{\n"
+ " \"unspents\": [\n"
+ " {\n"
+ " \"txid\" : \"transactionid\", (string) The transaction id\n"
+ " \"vout\": n, (numeric) the vout value\n"
+ " \"scriptPubKey\" : \"script\", (string) the script key\n"
+ " \"amount\" : x.xxx, (numeric) The total amount in " + CURRENCY_UNIT + " of the unspent output\n"
+ " \"height\" : n, (numeric) Height of the unspent transaction output\n"
+ " }\n"
+ " ,...], \n"
+ " \"total_amount\" : x.xxx, (numeric) The total amount of all found unspent outputs in " + CURRENCY_UNIT + "\n"
+ "]\n"
+ );
+
+ RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR});
+
+ UniValue result(UniValue::VOBJ);
+ if (request.params[0].get_str() == "status") {
+ CoinsViewScanReserver reserver;
+ if (reserver.reserve()) {
+ // no scan in progress
+ return NullUniValue;
+ }
+ result.pushKV("progress", g_scan_progress);
+ return result;
+ } else if (request.params[0].get_str() == "abort") {
+ CoinsViewScanReserver reserver;
+ if (reserver.reserve()) {
+ // reserve was possible which means no scan was running
+ return false;
+ }
+ // set the abort flag
+ g_should_abort_scan = true;
+ return true;
+ } else if (request.params[0].get_str() == "start") {
+ CoinsViewScanReserver reserver;
+ if (!reserver.reserve()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Scan already in progress, use action \"abort\" or \"status\"");
+ }
+ std::set<CScript> needles;
+ CAmount total_in = 0;
+
+ // loop through the scan objects
+ for (const UniValue& scanobject : request.params[1].get_array().getValues()) {
+ if (!scanobject.isObject()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid scan object");
+ }
+ UniValue address_uni = find_value(scanobject, "address");
+ UniValue pubkey_uni = find_value(scanobject, "pubkey");
+ UniValue script_uni = find_value(scanobject, "script");
+
+ // make sure only one object type is present
+ if (1 != !address_uni.isNull() + !pubkey_uni.isNull() + !script_uni.isNull()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Only one object type is allowed per scan object");
+ } else if (!address_uni.isNull() && !address_uni.isStr()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Scanobject \"address\" must contain a single string as value");
+ } else if (!pubkey_uni.isNull() && !pubkey_uni.isObject()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Scanobject \"pubkey\" must contain an object as value");
+ } else if (!script_uni.isNull() && !script_uni.isStr()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Scanobject \"script\" must contain a single string as value");
+ } else if (address_uni.isStr()) {
+ // type: address
+ // decode destination and derive the scriptPubKey
+ // add the script to the scan containers
+ CTxDestination dest = DecodeDestination(address_uni.get_str());
+ if (!IsValidDestination(dest)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
+ }
+ CScript script = GetScriptForDestination(dest);
+ assert(!script.empty());
+ needles.insert(script);
+ } else if (pubkey_uni.isObject()) {
+ // type: pubkey
+ // derive script(s) according to the script_type parameter
+ UniValue script_types_uni = find_value(pubkey_uni, "script_types");
+ UniValue pubkeydata_uni = find_value(pubkey_uni, "pubkey");
+
+ // check the script types and use the default if not provided
+ if (!script_types_uni.isNull() && !script_types_uni.isArray()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "script_types must be an array");
+ } else if (script_types_uni.isNull()) {
+ // use the default script types
+ script_types_uni = UniValue(UniValue::VARR);
+ for (const char *t : g_default_scantxoutset_script_types) {
+ script_types_uni.push_back(t);
+ }
+ }
+
+ // check the acctual pubkey
+ if (!pubkeydata_uni.isStr() || !IsHex(pubkeydata_uni.get_str())) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Public key must be hex encoded");
+ }
+ CPubKey pubkey(ParseHexV(pubkeydata_uni, "pubkey"));
+ if (!pubkey.IsFullyValid()) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid public key");
+ }
+
+ // loop through the script types and derive the script
+ for (const UniValue& script_type_uni : script_types_uni.get_array().getValues()) {
+ OutputScriptType script_type = GetOutputScriptTypeFromString(script_type_uni.get_str());
+ if (script_type == OutputScriptType::UNKNOWN) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid script type");
+ CScript script;
+ if (script_type == OutputScriptType::P2PK) {
+ // support legacy P2PK scripts
+ script << ToByteVector(pubkey) << OP_CHECKSIG;
+ } else {
+ script = GetScriptForDestination(GetDestinationForKey(pubkey, script_type));
+ }
+ assert(!script.empty());
+ needles.insert(script);
+ }
+ } else if (script_uni.isStr()) {
+ // type: script
+ // check and add the script to the scan containers (needles array)
+ CScript script(ParseHexV(script_uni, "script"));
+ // TODO: check script: max length, has OP, is unspenable etc.
+ needles.insert(script);
+ }
+ }
+
+ // Scan the unspent transaction output set for inputs
+ UniValue unspents(UniValue::VARR);
+ std::vector<CTxOut> input_txos;
+ std::map<COutPoint, Coin> coins;
+ g_should_abort_scan = false;
+ g_scan_progress = 0;
+ int64_t count = 0;
+ std::unique_ptr<CCoinsViewCursor> pcursor;
+ {
+ LOCK(cs_main);
+ FlushStateToDisk();
+ pcursor = std::unique_ptr<CCoinsViewCursor>(pcoinsdbview->Cursor());
+ assert(pcursor);
+ }
+ bool res = FindScriptPubKey(g_scan_progress, g_should_abort_scan, count, pcursor.get(), needles, coins);
+ result.pushKV("success", res);
+ result.pushKV("searched_items", count);
+
+ for (const auto& it : coins) {
+ const COutPoint& outpoint = it.first;
+ const Coin& coin = it.second;
+ const CTxOut& txo = coin.out;
+ input_txos.push_back(txo);
+ total_in += txo.nValue;
+
+ UniValue unspent(UniValue::VOBJ);
+ unspent.pushKV("txid", outpoint.hash.GetHex());
+ unspent.pushKV("vout", (int32_t)outpoint.n);
+ unspent.pushKV("scriptPubKey", HexStr(txo.scriptPubKey.begin(), txo.scriptPubKey.end()));
+ unspent.pushKV("amount", ValueFromAmount(txo.nValue));
+ unspent.pushKV("height", (int32_t)coin.nHeight);
+
+ unspents.push_back(unspent);
+ }
+ result.pushKV("unspents", unspents);
+ result.pushKV("total_amount", ValueFromAmount(total_in));
+ } else {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid command");
+ }
+ return result;
+}
+
static const CRPCCommand commands[] =
{ // category name actor (function) argNames
// --------------------- ------------------------ ----------------------- ----------
@@ -1945,6 +2233,7 @@ static const CRPCCommand commands[] =
{ "blockchain", "verifychain", &verifychain, {"checklevel","nblocks"} },
{ "blockchain", "preciousblock", &preciousblock, {"blockhash"} },
+ { "blockchain", "scantxoutset", &scantxoutset, {"action", "scanobjects"} },
/* Not shown in help */
{ "hidden", "invalidateblock", &invalidateblock, {"blockhash"} },
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index 0f35fd3770..593c59ca5e 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -78,6 +78,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "sendmany", 4, "subtractfeefrom" },
{ "sendmany", 5 , "replaceable" },
{ "sendmany", 6 , "conf_target" },
+ { "scantxoutset", 1, "scanobjects" },
{ "addmultisigaddress", 0, "nrequired" },
{ "addmultisigaddress", 1, "keys" },
{ "createmultisig", 0, "nrequired" },
@@ -109,6 +110,22 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "combinerawtransaction", 0, "txs" },
{ "fundrawtransaction", 1, "options" },
{ "fundrawtransaction", 2, "iswitness" },
+ { "walletcreatefundedpsbt", 0, "inputs" },
+ { "walletcreatefundedpsbt", 1, "outputs" },
+ { "walletcreatefundedpsbt", 2, "locktime" },
+ { "walletcreatefundedpsbt", 3, "replaceable" },
+ { "walletcreatefundedpsbt", 4, "options" },
+ { "walletcreatefundedpsbt", 5, "bip32derivs" },
+ { "walletprocesspsbt", 1, "sign" },
+ { "walletprocesspsbt", 3, "bip32derivs" },
+ { "createpsbt", 0, "inputs" },
+ { "createpsbt", 1, "outputs" },
+ { "createpsbt", 2, "locktime" },
+ { "createpsbt", 3, "replaceable" },
+ { "combinepsbt", 0, "txs"},
+ { "finalizepsbt", 1, "extract"},
+ { "converttopsbt", 1, "permitsigdata"},
+ { "converttopsbt", 2, "iswitness"},
{ "gettxout", 1, "n" },
{ "gettxout", 2, "include_mempool" },
{ "gettxoutproof", 0, "txids" },
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 85b864e6b9..1f1044d80b 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -1,5 +1,5 @@
// Copyright (c) 2010 Satoshi Nakamoto
-// Copyright (c) 2009-2017 The Bitcoin Core developers
+// Copyright (c) 2009-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -10,7 +10,6 @@
#include <consensus/params.h>
#include <consensus/validation.h>
#include <core_io.h>
-#include <init.h>
#include <validation.h>
#include <key_io.h>
#include <miner.h>
@@ -20,6 +19,7 @@
#include <rpc/blockchain.h>
#include <rpc/mining.h>
#include <rpc/server.h>
+#include <shutdown.h>
#include <txmempool.h>
#include <util.h>
#include <utilstrencodings.h>
@@ -725,7 +725,6 @@ static UniValue submitblock(const JSONRPCRequest& request)
}
uint256 hash = block.GetHash();
- bool fBlockPresent = false;
{
LOCK(cs_main);
const CBlockIndex* pindex = LookupBlockIndex(hash);
@@ -736,8 +735,6 @@ static UniValue submitblock(const JSONRPCRequest& request)
if (pindex->nStatus & BLOCK_FAILED_MASK) {
return "duplicate-invalid";
}
- // Otherwise, we might only have the header - process the block before returning
- fBlockPresent = true;
}
}
@@ -749,13 +746,15 @@ static UniValue submitblock(const JSONRPCRequest& request)
}
}
+ bool new_block;
submitblock_StateCatcher sc(block.GetHash());
RegisterValidationInterface(&sc);
- bool fAccepted = ProcessNewBlock(Params(), blockptr, true, nullptr);
+ bool accepted = ProcessNewBlock(Params(), blockptr, /* fForceProcessing */ true, /* fNewBlock */ &new_block);
UnregisterValidationInterface(&sc);
- if (fBlockPresent) {
- if (fAccepted && !sc.found) {
- return "duplicate-inconclusive";
+ if (!new_block) {
+ if (!accepted) {
+ // TODO Maybe pass down fNewBlock to AcceptBlockHeader, so it is properly set to true in this case?
+ return "invalid";
}
return "duplicate";
}
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index 6772784d3d..09812bb980 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -7,12 +7,12 @@
#include <clientversion.h>
#include <core_io.h>
#include <crypto/ripemd160.h>
-#include <init.h>
#include <key_io.h>
#include <validation.h>
#include <httpserver.h>
#include <net.h>
#include <netbase.h>
+#include <outputtype.h>
#include <rpc/blockchain.h>
#include <rpc/server.h>
#include <rpc/util.h>
@@ -78,7 +78,7 @@ static UniValue validateaddress(const JSONRPCRequest& request)
ret.pushKV("address", currentAddress);
CScript scriptPubKey = GetScriptForDestination(dest);
- ret.pushKV("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()));;
+ ret.pushKV("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()));
UniValue detail = DescribeAddress(dest);
ret.pushKVs(detail);
@@ -92,9 +92,9 @@ class CWallet;
static UniValue createmultisig(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() < 2 || request.params.size() > 2)
+ if (request.fHelp || request.params.size() < 2 || request.params.size() > 3)
{
- std::string msg = "createmultisig nrequired [\"key\",...]\n"
+ std::string msg = "createmultisig nrequired [\"key\",...] ( \"address_type\" )\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"
@@ -104,6 +104,7 @@ static UniValue createmultisig(const JSONRPCRequest& request)
" \"key\" (string) The hex-encoded public key\n"
" ,...\n"
" ]\n"
+ "3. \"address_type\" (string, optional) The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\". Default is legacy.\n"
"\nResult:\n"
"{\n"
@@ -134,12 +135,21 @@ static UniValue createmultisig(const JSONRPCRequest& request)
}
}
+ // Get the output type
+ OutputType output_type = OutputType::LEGACY;
+ if (!request.params[2].isNull()) {
+ if (!ParseOutputType(request.params[2].get_str(), output_type)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[2].get_str()));
+ }
+ }
+
// Construct using pay-to-script-hash:
- CScript inner = CreateMultisigRedeemscript(required, pubkeys);
- CScriptID innerID(inner);
+ const CScript inner = CreateMultisigRedeemscript(required, pubkeys);
+ CBasicKeyStore keystore;
+ const CTxDestination dest = AddAndGetDestinationForScript(keystore, inner, output_type);
UniValue result(UniValue::VOBJ);
- result.pushKV("address", EncodeDestination(innerID));
+ result.pushKV("address", EncodeDestination(dest));
result.pushKV("redeemScript", HexStr(inner.begin(), inner.end()));
return result;
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 3b3f43edea..bb94e11fea 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -5,10 +5,10 @@
#include <chain.h>
#include <coins.h>
+#include <compat/byteswap.h>
#include <consensus/validation.h>
#include <core_io.h>
#include <index/txindex.h>
-#include <init.h>
#include <keystore.h>
#include <validation.h>
#include <validationinterface.h>
@@ -307,7 +307,7 @@ static UniValue verifytxoutproof(const JSONRPCRequest& request)
"\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"
+ "[\"txid\"] (array, strings) The txid(s) which the proof commits to, or empty array if the proof can not be validated.\n"
);
CDataStream ssMB(ParseHexV(request.params[0], "proof"), SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS);
@@ -324,89 +324,39 @@ static UniValue verifytxoutproof(const JSONRPCRequest& request)
LOCK(cs_main);
const CBlockIndex* pindex = LookupBlockIndex(merkleBlock.header.GetHash());
- if (!pindex || !chainActive.Contains(pindex)) {
+ if (!pindex || !chainActive.Contains(pindex) || pindex->nTx == 0) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain");
}
- for (const uint256& hash : vMatch)
- res.push_back(hash.GetHex());
+ // Check if proof is valid, only add results if so
+ if (pindex->nTx == merkleBlock.txn.GetNumTransactions()) {
+ for (const uint256& hash : vMatch) {
+ res.push_back(hash.GetHex());
+ }
+ }
+
return res;
}
-static UniValue createrawtransaction(const JSONRPCRequest& request)
+CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf)
{
- if (request.fHelp || request.params.size() < 2 || request.params.size() > 4) {
- throw std::runtime_error(
- // clang-format off
- "createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] [{\"address\":amount},{\"data\":\"hex\"},...] ( locktime ) ( replaceable )\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\" (array, required) a json array with outputs (key-value pairs)\n"
- " [\n"
- " {\n"
- " \"address\": x.xxx, (obj, optional) A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT + "\n"
- " },\n"
- " {\n"
- " \"data\": \"hex\" (obj, optional) A key-value pair. The key must be \"data\", the value is hex encoded data\n"
- " }\n"
- " ,... More key-value pairs of the above form. For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n"
- " accepted as second parameter.\n"
- " ]\n"
- "3. locktime (numeric, optional, default=0) Raw locktime. Non-0 value also locktime-activates inputs\n"
- "4. replaceable (boolean, optional, default=false) Marks this transaction as BIP125 replaceable.\n"
- " Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible.\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\\\"}]\"")
- // clang-format on
- );
- }
-
- RPCTypeCheck(request.params, {
- UniValue::VARR,
- UniValueType(), // ARR or OBJ, checked later
- UniValue::VNUM,
- UniValue::VBOOL
- }, true
- );
- if (request.params[0].isNull() || request.params[1].isNull())
+ if (inputs_in.isNull() || outputs_in.isNull())
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, arguments 1 and 2 must be non-null");
- UniValue inputs = request.params[0].get_array();
- const bool outputs_is_obj = request.params[1].isObject();
- UniValue outputs = outputs_is_obj ?
- request.params[1].get_obj() :
- request.params[1].get_array();
+ UniValue inputs = inputs_in.get_array();
+ const bool outputs_is_obj = outputs_in.isObject();
+ UniValue outputs = outputs_is_obj ? outputs_in.get_obj() : outputs_in.get_array();
CMutableTransaction rawTx;
- if (!request.params[2].isNull()) {
- int64_t nLockTime = request.params[2].get_int64();
+ if (!locktime.isNull()) {
+ int64_t nLockTime = locktime.get_int64();
if (nLockTime < 0 || nLockTime > std::numeric_limits<uint32_t>::max())
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, locktime out of range");
rawTx.nLockTime = nLockTime;
}
- bool rbfOptIn = request.params[3].isTrue();
+ bool rbfOptIn = rbf.isTrue();
for (unsigned int idx = 0; idx < inputs.size(); idx++) {
const UniValue& input = inputs[idx];
@@ -486,10 +436,71 @@ static UniValue createrawtransaction(const JSONRPCRequest& request)
}
}
- if (!request.params[3].isNull() && rbfOptIn != SignalsOptInRBF(rawTx)) {
+ if (!rbf.isNull() && rbfOptIn != SignalsOptInRBF(rawTx)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter combination: Sequence number(s) contradict replaceable option");
}
+ return rawTx;
+}
+
+static UniValue createrawtransaction(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() < 2 || request.params.size() > 4) {
+ throw std::runtime_error(
+ // clang-format off
+ "createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] [{\"address\":amount},{\"data\":\"hex\"},...] ( locktime ) ( replaceable )\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\" (array, required) a json array with outputs (key-value pairs)\n"
+ " [\n"
+ " {\n"
+ " \"address\": x.xxx, (obj, optional) A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT + "\n"
+ " },\n"
+ " {\n"
+ " \"data\": \"hex\" (obj, optional) A key-value pair. The key must be \"data\", the value is hex encoded data\n"
+ " }\n"
+ " ,... More key-value pairs of the above form. For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n"
+ " accepted as second parameter.\n"
+ " ]\n"
+ "3. locktime (numeric, optional, default=0) Raw locktime. Non-0 value also locktime-activates inputs\n"
+ "4. replaceable (boolean, optional, default=false) Marks this transaction as BIP125 replaceable.\n"
+ " Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible.\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\\\"}]\"")
+ // clang-format on
+ );
+ }
+
+ RPCTypeCheck(request.params, {
+ UniValue::VARR,
+ UniValueType(), // ARR or OBJ, checked later
+ UniValue::VNUM,
+ UniValue::VBOOL
+ }, true
+ );
+
+ CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], request.params[3]);
+
return EncodeHexTx(rawTx);
}
@@ -637,9 +648,7 @@ static UniValue decodescript(const JSONRPCRequest& request)
} else {
// Scripts that are not fit for P2WPKH are encoded as P2WSH.
// Newer segwit program versions should be considered when then become available.
- uint256 scriptHash;
- CSHA256().Write(script.data(), script.size()).Finalize(scriptHash.begin());
- segwitScr = GetScriptForDestination(WitnessV0ScriptHash(scriptHash));
+ segwitScr = GetScriptForDestination(WitnessV0ScriptHash(script));
}
ScriptPubKeyToUniv(segwitScr, sr, true);
sr.pushKV("p2sh-segwit", EncodeDestination(CScriptID(segwitScr)));
@@ -736,17 +745,15 @@ static UniValue combinerawtransaction(const JSONRPCRequest& request)
if (coin.IsSpent()) {
throw JSONRPCError(RPC_VERIFY_ERROR, "Input not found or already spent");
}
- const CScript& prevPubKey = coin.out.scriptPubKey;
- const CAmount& amount = coin.out.nValue;
-
SignatureData 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));
+ sigdata.MergeSignatureData(DataFromTransaction(txv, i, coin.out));
}
}
+ ProduceSignature(DUMMY_SIGNING_PROVIDER, MutableTransactionSignatureCreator(&mergedTx, i, coin.out.nValue, 1), coin.out.scriptPubKey, sigdata);
UpdateInput(txin, sigdata);
}
@@ -811,7 +818,7 @@ UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival
}
Coin newcoin;
newcoin.out.scriptPubKey = scriptPubKey;
- newcoin.out.nValue = 0;
+ newcoin.out.nValue = MAX_MONEY;
if (prevOut.exists("amount")) {
newcoin.out.nValue = AmountFromValue(find_value(prevOut, "amount"));
}
@@ -838,23 +845,7 @@ UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival
}
}
- int nHashType = SIGHASH_ALL;
- if (!hashType.isNull()) {
- static std::map<std::string, int> mapSigHashValues = {
- {std::string("ALL"), int(SIGHASH_ALL)},
- {std::string("ALL|ANYONECANPAY"), int(SIGHASH_ALL|SIGHASH_ANYONECANPAY)},
- {std::string("NONE"), int(SIGHASH_NONE)},
- {std::string("NONE|ANYONECANPAY"), int(SIGHASH_NONE|SIGHASH_ANYONECANPAY)},
- {std::string("SINGLE"), int(SIGHASH_SINGLE)},
- {std::string("SINGLE|ANYONECANPAY"), int(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY)},
- };
- std::string strHashType = hashType.get_str();
- if (mapSigHashValues.count(strHashType)) {
- nHashType = mapSigHashValues[strHashType];
- } else {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid sighash param");
- }
- }
+ int nHashType = ParseSighashString(hashType);
bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
@@ -875,15 +866,19 @@ UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival
const CScript& prevPubKey = coin.out.scriptPubKey;
const CAmount& amount = coin.out.nValue;
- SignatureData sigdata;
+ SignatureData sigdata = DataFromTransaction(mtx, i, coin.out);
// Only sign SIGHASH_SINGLE if there's a corresponding output:
if (!fHashSingle || (i < mtx.vout.size())) {
ProduceSignature(*keystore, MutableTransactionSignatureCreator(&mtx, i, amount, nHashType), prevPubKey, sigdata);
}
- sigdata = CombineSignatures(prevPubKey, TransactionSignatureChecker(&txConst, i, amount), sigdata, DataFromTransaction(mtx, i));
UpdateInput(txin, sigdata);
+ // amount must be specified for valid segwit signature
+ if (amount == MAX_MONEY && !txin.scriptWitness.IsNull()) {
+ throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing amount for %s", coin.out.ToString()));
+ }
+
ScriptError serror = SCRIPT_ERR_OK;
if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), &serror)) {
if (serror == SCRIPT_ERR_INVALID_STACK_OPERATION) {
@@ -1259,6 +1254,553 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request)
return result;
}
+static std::string WriteHDKeypath(std::vector<uint32_t>& keypath)
+{
+ std::string keypath_str = "m";
+ for (uint32_t num : keypath) {
+ keypath_str += "/";
+ bool hardened = false;
+ if (num & 0x80000000) {
+ hardened = true;
+ num &= ~0x80000000;
+ }
+
+ keypath_str += std::to_string(num);
+ if (hardened) {
+ keypath_str += "'";
+ }
+ }
+ return keypath_str;
+}
+
+UniValue decodepsbt(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 1)
+ throw std::runtime_error(
+ "decodepsbt \"psbt\"\n"
+ "\nReturn a JSON object representing the serialized, base64-encoded partially signed Bitcoin transaction.\n"
+
+ "\nArguments:\n"
+ "1. \"psbt\" (string, required) The PSBT base64 string\n"
+
+ "\nResult:\n"
+ "{\n"
+ " \"tx\" : { (json object) The decoded network-serialized unsigned transaction.\n"
+ " ... The layout is the same as the output of decoderawtransaction.\n"
+ " },\n"
+ " \"unknown\" : { (json object) The unknown global fields\n"
+ " \"key\" : \"value\" (key-value pair) An unknown key-value pair\n"
+ " ...\n"
+ " },\n"
+ " \"inputs\" : [ (array of json objects)\n"
+ " {\n"
+ " \"non_witness_utxo\" : { (json object, optional) Decoded network transaction for non-witness UTXOs\n"
+ " ...\n"
+ " },\n"
+ " \"witness_utxo\" : { (json object, optional) Transaction output for witness UTXOs\n"
+ " \"amount\" : x.xxx, (numeric) The value in " + CURRENCY_UNIT + "\n"
+ " \"scriptPubKey\" : { (json object)\n"
+ " \"asm\" : \"asm\", (string) The asm\n"
+ " \"hex\" : \"hex\", (string) The hex\n"
+ " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n"
+ " \"address\" : \"address\" (string) Bitcoin address if there is one\n"
+ " }\n"
+ " },\n"
+ " \"partial_signatures\" : { (json object, optional)\n"
+ " \"pubkey\" : \"signature\", (string) The public key and signature that corresponds to it.\n"
+ " ,...\n"
+ " }\n"
+ " \"sighash\" : \"type\", (string, optional) The sighash type to be used\n"
+ " \"redeem_script\" : { (json object, optional)\n"
+ " \"asm\" : \"asm\", (string) The asm\n"
+ " \"hex\" : \"hex\", (string) The hex\n"
+ " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n"
+ " }\n"
+ " \"witness_script\" : { (json object, optional)\n"
+ " \"asm\" : \"asm\", (string) The asm\n"
+ " \"hex\" : \"hex\", (string) The hex\n"
+ " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n"
+ " }\n"
+ " \"bip32_derivs\" : { (json object, optional)\n"
+ " \"pubkey\" : { (json object, optional) The public key with the derivation path as the value.\n"
+ " \"master_fingerprint\" : \"fingerprint\" (string) The fingerprint of the master key\n"
+ " \"path\" : \"path\", (string) The path\n"
+ " }\n"
+ " ,...\n"
+ " }\n"
+ " \"final_scriptsig\" : { (json object, optional)\n"
+ " \"asm\" : \"asm\", (string) The asm\n"
+ " \"hex\" : \"hex\", (string) The hex\n"
+ " }\n"
+ " \"final_scriptwitness\": [\"hex\", ...] (array of string) hex-encoded witness data (if any)\n"
+ " \"unknown\" : { (json object) The unknown global fields\n"
+ " \"key\" : \"value\" (key-value pair) An unknown key-value pair\n"
+ " ...\n"
+ " },\n"
+ " }\n"
+ " ,...\n"
+ " ]\n"
+ " \"outputs\" : [ (array of json objects)\n"
+ " {\n"
+ " \"redeem_script\" : { (json object, optional)\n"
+ " \"asm\" : \"asm\", (string) The asm\n"
+ " \"hex\" : \"hex\", (string) The hex\n"
+ " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n"
+ " }\n"
+ " \"witness_script\" : { (json object, optional)\n"
+ " \"asm\" : \"asm\", (string) The asm\n"
+ " \"hex\" : \"hex\", (string) The hex\n"
+ " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n"
+ " }\n"
+ " \"bip32_derivs\" : [ (array of json objects, optional)\n"
+ " {\n"
+ " \"pubkey\" : \"pubkey\", (string) The public key this path corresponds to\n"
+ " \"master_fingerprint\" : \"fingerprint\" (string) The fingerprint of the master key\n"
+ " \"path\" : \"path\", (string) The path\n"
+ " }\n"
+ " }\n"
+ " ,...\n"
+ " ],\n"
+ " \"unknown\" : { (json object) The unknown global fields\n"
+ " \"key\" : \"value\" (key-value pair) An unknown key-value pair\n"
+ " ...\n"
+ " },\n"
+ " }\n"
+ " ,...\n"
+ " ]\n"
+ " \"fee\" : fee (numeric, optional) The transaction fee paid if all UTXOs slots in the PSBT have been filled.\n"
+ "}\n"
+
+ "\nExamples:\n"
+ + HelpExampleCli("decodepsbt", "\"psbt\"")
+ );
+
+ RPCTypeCheck(request.params, {UniValue::VSTR});
+
+ // Unserialize the transactions
+ PartiallySignedTransaction psbtx;
+ std::string error;
+ if (!DecodePSBT(psbtx, request.params[0].get_str(), error)) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
+ }
+
+ UniValue result(UniValue::VOBJ);
+
+ // Add the decoded tx
+ UniValue tx_univ(UniValue::VOBJ);
+ TxToUniv(CTransaction(*psbtx.tx), uint256(), tx_univ, false);
+ result.pushKV("tx", tx_univ);
+
+ // Unknown data
+ UniValue unknowns(UniValue::VOBJ);
+ for (auto entry : psbtx.unknown) {
+ unknowns.pushKV(HexStr(entry.first), HexStr(entry.second));
+ }
+ result.pushKV("unknown", unknowns);
+
+ // inputs
+ CAmount total_in = 0;
+ bool have_all_utxos = true;
+ UniValue inputs(UniValue::VARR);
+ for (unsigned int i = 0; i < psbtx.inputs.size(); ++i) {
+ const PSBTInput& input = psbtx.inputs[i];
+ UniValue in(UniValue::VOBJ);
+ // UTXOs
+ if (!input.witness_utxo.IsNull()) {
+ const CTxOut& txout = input.witness_utxo;
+
+ UniValue out(UniValue::VOBJ);
+
+ out.pushKV("amount", ValueFromAmount(txout.nValue));
+ total_in += txout.nValue;
+
+ UniValue o(UniValue::VOBJ);
+ ScriptToUniv(txout.scriptPubKey, o, true);
+ out.pushKV("scriptPubKey", o);
+ in.pushKV("witness_utxo", out);
+ } else if (input.non_witness_utxo) {
+ UniValue non_wit(UniValue::VOBJ);
+ TxToUniv(*input.non_witness_utxo, uint256(), non_wit, false);
+ in.pushKV("non_witness_utxo", non_wit);
+ total_in += input.non_witness_utxo->vout[psbtx.tx->vin[i].prevout.n].nValue;
+ } else {
+ have_all_utxos = false;
+ }
+
+ // Partial sigs
+ if (!input.partial_sigs.empty()) {
+ UniValue partial_sigs(UniValue::VOBJ);
+ for (const auto& sig : input.partial_sigs) {
+ partial_sigs.pushKV(HexStr(sig.second.first), HexStr(sig.second.second));
+ }
+ in.pushKV("partial_signatures", partial_sigs);
+ }
+
+ // Sighash
+ if (input.sighash_type > 0) {
+ in.pushKV("sighash", SighashToStr((unsigned char)input.sighash_type));
+ }
+
+ // Redeem script and witness script
+ if (!input.redeem_script.empty()) {
+ UniValue r(UniValue::VOBJ);
+ ScriptToUniv(input.redeem_script, r, false);
+ in.pushKV("redeem_script", r);
+ }
+ if (!input.witness_script.empty()) {
+ UniValue r(UniValue::VOBJ);
+ ScriptToUniv(input.witness_script, r, false);
+ in.pushKV("witness_script", r);
+ }
+
+ // keypaths
+ if (!input.hd_keypaths.empty()) {
+ UniValue keypaths(UniValue::VARR);
+ for (auto entry : input.hd_keypaths) {
+ UniValue keypath(UniValue::VOBJ);
+ keypath.pushKV("pubkey", HexStr(entry.first));
+
+ uint32_t fingerprint = entry.second.at(0);
+ keypath.pushKV("master_fingerprint", strprintf("%08x", bswap_32(fingerprint)));
+
+ entry.second.erase(entry.second.begin());
+ keypath.pushKV("path", WriteHDKeypath(entry.second));
+ keypaths.push_back(keypath);
+ }
+ in.pushKV("bip32_derivs", keypaths);
+ }
+
+ // Final scriptSig and scriptwitness
+ if (!input.final_script_sig.empty()) {
+ UniValue scriptsig(UniValue::VOBJ);
+ scriptsig.pushKV("asm", ScriptToAsmStr(input.final_script_sig, true));
+ scriptsig.pushKV("hex", HexStr(input.final_script_sig));
+ in.pushKV("final_scriptSig", scriptsig);
+ }
+ if (!input.final_script_witness.IsNull()) {
+ UniValue txinwitness(UniValue::VARR);
+ for (const auto& item : input.final_script_witness.stack) {
+ txinwitness.push_back(HexStr(item.begin(), item.end()));
+ }
+ in.pushKV("final_scriptwitness", txinwitness);
+ }
+
+ // Unknown data
+ if (input.unknown.size() > 0) {
+ UniValue unknowns(UniValue::VOBJ);
+ for (auto entry : input.unknown) {
+ unknowns.pushKV(HexStr(entry.first), HexStr(entry.second));
+ }
+ in.pushKV("unknown", unknowns);
+ }
+
+ inputs.push_back(in);
+ }
+ result.pushKV("inputs", inputs);
+
+ // outputs
+ CAmount output_value = 0;
+ UniValue outputs(UniValue::VARR);
+ for (unsigned int i = 0; i < psbtx.outputs.size(); ++i) {
+ const PSBTOutput& output = psbtx.outputs[i];
+ UniValue out(UniValue::VOBJ);
+ // Redeem script and witness script
+ if (!output.redeem_script.empty()) {
+ UniValue r(UniValue::VOBJ);
+ ScriptToUniv(output.redeem_script, r, false);
+ out.pushKV("redeem_script", r);
+ }
+ if (!output.witness_script.empty()) {
+ UniValue r(UniValue::VOBJ);
+ ScriptToUniv(output.witness_script, r, false);
+ out.pushKV("witness_script", r);
+ }
+
+ // keypaths
+ if (!output.hd_keypaths.empty()) {
+ UniValue keypaths(UniValue::VARR);
+ for (auto entry : output.hd_keypaths) {
+ UniValue keypath(UniValue::VOBJ);
+ keypath.pushKV("pubkey", HexStr(entry.first));
+
+ uint32_t fingerprint = entry.second.at(0);
+ keypath.pushKV("master_fingerprint", strprintf("%08x", bswap_32(fingerprint)));
+
+ entry.second.erase(entry.second.begin());
+ keypath.pushKV("path", WriteHDKeypath(entry.second));
+ keypaths.push_back(keypath);
+ }
+ out.pushKV("bip32_derivs", keypaths);
+ }
+
+ // Unknown data
+ if (output.unknown.size() > 0) {
+ UniValue unknowns(UniValue::VOBJ);
+ for (auto entry : output.unknown) {
+ unknowns.pushKV(HexStr(entry.first), HexStr(entry.second));
+ }
+ out.pushKV("unknown", unknowns);
+ }
+
+ outputs.push_back(out);
+
+ // Fee calculation
+ output_value += psbtx.tx->vout[i].nValue;
+ }
+ result.pushKV("outputs", outputs);
+ if (have_all_utxos) {
+ result.pushKV("fee", ValueFromAmount(total_in - output_value));
+ }
+
+ return result;
+}
+
+UniValue combinepsbt(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 1)
+ throw std::runtime_error(
+ "combinepsbt [\"psbt\",...]\n"
+ "\nCombine multiple partially signed Bitcoin transactions into one transaction.\n"
+ "Implements the Combiner role.\n"
+ "\nArguments:\n"
+ "1. \"txs\" (string) A json array of base64 strings of partially signed transactions\n"
+ " [\n"
+ " \"psbt\" (string) A base64 string of a PSBT\n"
+ " ,...\n"
+ " ]\n"
+
+ "\nResult:\n"
+ " \"psbt\" (string) The base64-encoded partially signed transaction\n"
+ "\nExamples:\n"
+ + HelpExampleCli("combinepsbt", "[\"mybase64_1\", \"mybase64_2\", \"mybase64_3\"]")
+ );
+
+ RPCTypeCheck(request.params, {UniValue::VARR}, true);
+
+ // Unserialize the transactions
+ std::vector<PartiallySignedTransaction> psbtxs;
+ UniValue txs = request.params[0].get_array();
+ for (unsigned int i = 0; i < txs.size(); ++i) {
+ PartiallySignedTransaction psbtx;
+ std::string error;
+ if (!DecodePSBT(psbtx, txs[i].get_str(), error)) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
+ }
+ psbtxs.push_back(psbtx);
+ }
+
+ PartiallySignedTransaction merged_psbt(psbtxs[0]); // Copy the first one
+
+ // Merge
+ for (auto it = std::next(psbtxs.begin()); it != psbtxs.end(); ++it) {
+ if (*it != merged_psbt) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "PSBTs do not refer to the same transactions.");
+ }
+ merged_psbt.Merge(*it);
+ }
+ if (!merged_psbt.IsSane()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Merged PSBT is inconsistent");
+ }
+
+ UniValue result(UniValue::VOBJ);
+ CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
+ ssTx << merged_psbt;
+ return EncodeBase64((unsigned char*)ssTx.data(), ssTx.size());
+}
+
+UniValue finalizepsbt(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
+ throw std::runtime_error(
+ "finalizepsbt \"psbt\" ( extract )\n"
+ "Finalize the inputs of a PSBT. If the transaction is fully signed, it will produce a\n"
+ "network serialized transaction which can be broadcast with sendrawtransaction. Otherwise a PSBT will be\n"
+ "created which has the final_scriptSig and final_scriptWitness fields filled for inputs that are complete.\n"
+ "Implements the Finalizer and Extractor roles.\n"
+ "\nArguments:\n"
+ "1. \"psbt\" (string) A base64 string of a PSBT\n"
+ "2. \"extract\" (boolean, optional, default=true) If true and the transaction is complete, \n"
+ " extract and return the complete transaction in normal network serialization instead of the PSBT.\n"
+
+ "\nResult:\n"
+ "{\n"
+ " \"psbt\" : \"value\", (string) The base64-encoded partially signed transaction if not extracted\n"
+ " \"hex\" : \"value\", (string) The hex-encoded network transaction if extracted\n"
+ " \"complete\" : true|false, (boolean) If the transaction has a complete set of signatures\n"
+ " ]\n"
+ "}\n"
+
+ "\nExamples:\n"
+ + HelpExampleCli("finalizepsbt", "\"psbt\"")
+ );
+
+ RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL}, true);
+
+ // Unserialize the transactions
+ PartiallySignedTransaction psbtx;
+ std::string error;
+ if (!DecodePSBT(psbtx, request.params[0].get_str(), error)) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
+ }
+
+ // Get all of the previous transactions
+ bool complete = true;
+ for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
+ PSBTInput& input = psbtx.inputs.at(i);
+
+ SignatureData sigdata;
+ complete &= SignPSBTInput(DUMMY_SIGNING_PROVIDER, *psbtx.tx, input, sigdata, i, 1);
+ }
+
+ UniValue result(UniValue::VOBJ);
+ CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
+ bool extract = request.params[1].isNull() || (!request.params[1].isNull() && request.params[1].get_bool());
+ if (complete && extract) {
+ CMutableTransaction mtx(*psbtx.tx);
+ for (unsigned int i = 0; i < mtx.vin.size(); ++i) {
+ mtx.vin[i].scriptSig = psbtx.inputs[i].final_script_sig;
+ mtx.vin[i].scriptWitness = psbtx.inputs[i].final_script_witness;
+ }
+ ssTx << mtx;
+ result.push_back(Pair("hex", HexStr(ssTx.begin(), ssTx.end())));
+ } else {
+ ssTx << psbtx;
+ result.push_back(Pair("psbt", EncodeBase64((unsigned char*)ssTx.data(), ssTx.size())));
+ }
+ result.push_back(Pair("complete", complete));
+
+ return result;
+}
+
+UniValue createpsbt(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() < 2 || request.params.size() > 4)
+ throw std::runtime_error(
+ "createpsbt [{\"txid\":\"id\",\"vout\":n},...] [{\"address\":amount},{\"data\":\"hex\"},...] ( locktime ) ( replaceable )\n"
+ "\nCreates a transaction in the Partially Signed Transaction format.\n"
+ "Implements the Creator role.\n"
+ "\nArguments:\n"
+ "1. \"inputs\" (array, required) A json array of json objects\n"
+ " [\n"
+ " {\n"
+ " \"txid\":\"id\", (string, required) The transaction id\n"
+ " \"vout\":n, (numeric, required) The output number\n"
+ " \"sequence\":n (numeric, optional) The sequence number\n"
+ " } \n"
+ " ,...\n"
+ " ]\n"
+ "2. \"outputs\" (array, required) a json array with outputs (key-value pairs)\n"
+ " [\n"
+ " {\n"
+ " \"address\": x.xxx, (obj, optional) A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT + "\n"
+ " },\n"
+ " {\n"
+ " \"data\": \"hex\" (obj, optional) A key-value pair. The key must be \"data\", the value is hex encoded data\n"
+ " }\n"
+ " ,... More key-value pairs of the above form. For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n"
+ " accepted as second parameter.\n"
+ " ]\n"
+ "3. locktime (numeric, optional, default=0) Raw locktime. Non-0 value also locktime-activates inputs\n"
+ "4. replaceable (boolean, optional, default=false) Marks this transaction as BIP125 replaceable.\n"
+ " Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible.\n"
+ "\nResult:\n"
+ " \"psbt\" (string) The resulting raw transaction (base64-encoded string)\n"
+ "\nExamples:\n"
+ + HelpExampleCli("createpsbt", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"data\\\":\\\"00010203\\\"}]\"")
+ );
+
+
+ RPCTypeCheck(request.params, {
+ UniValue::VARR,
+ UniValueType(), // ARR or OBJ, checked later
+ UniValue::VNUM,
+ UniValue::VBOOL,
+ }, true
+ );
+
+ CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], request.params[3]);
+
+ // Make a blank psbt
+ PartiallySignedTransaction psbtx;
+ psbtx.tx = rawTx;
+ for (unsigned int i = 0; i < rawTx.vin.size(); ++i) {
+ psbtx.inputs.push_back(PSBTInput());
+ }
+ for (unsigned int i = 0; i < rawTx.vout.size(); ++i) {
+ psbtx.outputs.push_back(PSBTOutput());
+ }
+
+ // Serialize the PSBT
+ CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
+ ssTx << psbtx;
+
+ return EncodeBase64((unsigned char*)ssTx.data(), ssTx.size());
+}
+
+UniValue converttopsbt(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
+ throw std::runtime_error(
+ "converttopsbt \"hexstring\" ( permitsigdata iswitness )\n"
+ "\nConverts a network serialized transaction to a PSBT. This should be used only with createrawtransaction and fundrawtransaction\n"
+ "createpsbt and walletcreatefundedpsbt should be used for new applications.\n"
+ "\nArguments:\n"
+ "1. \"hexstring\" (string, required) The hex string of a raw transaction\n"
+ "2. permitsigdata (boolean, optional, default=false) If true, any signatures in the input will be discarded and conversion.\n"
+ " will continue. If false, RPC will fail if any signatures are present.\n"
+ "3. iswitness (boolean, optional) Whether the transaction hex is a serialized witness transaction.\n"
+ " If iswitness is not present, heuristic tests will be used in decoding. If true, only witness deserializaion\n"
+ " will be tried. If false, only non-witness deserialization wil be tried. Only has an effect if\n"
+ " permitsigdata is true.\n"
+ "\nResult:\n"
+ " \"psbt\" (string) The resulting raw transaction (base64-encoded string)\n"
+ "\nExamples:\n"
+ "\nCreate a transaction\n"
+ + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"data\\\":\\\"00010203\\\"}]\"") +
+ "\nConvert the transaction to a PSBT\n"
+ + HelpExampleCli("converttopsbt", "\"rawtransaction\"")
+ );
+
+
+ RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL, UniValue::VBOOL}, true);
+
+ // parse hex string from parameter
+ CMutableTransaction tx;
+ bool permitsigdata = request.params[1].isNull() ? false : request.params[1].get_bool();
+ bool witness_specified = !request.params[2].isNull();
+ bool iswitness = witness_specified ? request.params[2].get_bool() : false;
+ bool try_witness = permitsigdata ? (witness_specified ? iswitness : true) : false;
+ bool try_no_witness = permitsigdata ? (witness_specified ? !iswitness : true) : true;
+ if (!DecodeHexTx(tx, request.params[0].get_str(), try_no_witness, try_witness)) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
+ }
+
+ // Remove all scriptSigs and scriptWitnesses from inputs
+ for (CTxIn& input : tx.vin) {
+ if ((!input.scriptSig.empty() || !input.scriptWitness.IsNull()) && (request.params[1].isNull() || (!request.params[1].isNull() && request.params[1].get_bool()))) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Inputs must not have scriptSigs and scriptWitnesses");
+ }
+ input.scriptSig.clear();
+ input.scriptWitness.SetNull();
+ }
+
+ // Make a blank psbt
+ PartiallySignedTransaction psbtx;
+ psbtx.tx = tx;
+ for (unsigned int i = 0; i < tx.vin.size(); ++i) {
+ psbtx.inputs.push_back(PSBTInput());
+ }
+ for (unsigned int i = 0; i < tx.vout.size(); ++i) {
+ psbtx.outputs.push_back(PSBTOutput());
+ }
+
+ // Serialize the PSBT
+ CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
+ ssTx << psbtx;
+
+ return EncodeBase64((unsigned char*)ssTx.data(), ssTx.size());
+}
+
static const CRPCCommand commands[] =
{ // category name actor (function) argNames
// --------------------- ------------------------ ----------------------- ----------
@@ -1271,6 +1813,11 @@ static const CRPCCommand commands[] =
{ "rawtransactions", "signrawtransaction", &signrawtransaction, {"hexstring","prevtxs","privkeys","sighashtype"} }, /* uses wallet if enabled */
{ "rawtransactions", "signrawtransactionwithkey", &signrawtransactionwithkey, {"hexstring","privkeys","prevtxs","sighashtype"} },
{ "rawtransactions", "testmempoolaccept", &testmempoolaccept, {"rawtxs","allowhighfees"} },
+ { "rawtransactions", "decodepsbt", &decodepsbt, {"psbt"} },
+ { "rawtransactions", "combinepsbt", &combinepsbt, {"txs"} },
+ { "rawtransactions", "finalizepsbt", &finalizepsbt, {"psbt", "extract"} },
+ { "rawtransactions", "createpsbt", &createpsbt, {"inputs","outputs","locktime","replaceable"} },
+ { "rawtransactions", "converttopsbt", &converttopsbt, {"hexstring","permitsigdata","iswitness"} },
{ "blockchain", "gettxoutproof", &gettxoutproof, {"txids", "blockhash"} },
{ "blockchain", "verifytxoutproof", &verifytxoutproof, {"proof"} },
diff --git a/src/rpc/rawtransaction.h b/src/rpc/rawtransaction.h
index ec9d1f2cf0..52dccc90e8 100644
--- a/src/rpc/rawtransaction.h
+++ b/src/rpc/rawtransaction.h
@@ -12,4 +12,7 @@ class UniValue;
/** Sign a transaction with the given keystore and previous transactions */
UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxs, CBasicKeyStore *keystore, bool tempKeystore, const UniValue& hashType);
+/** Create a transaction from univalue parameters */
+CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf);
+
#endif // BITCOIN_RPC_RAWTRANSACTION_H
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp
index 10040b1255..b420e9d8b3 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -1,14 +1,14 @@
// Copyright (c) 2010 Satoshi Nakamoto
-// Copyright (c) 2009-2017 The Bitcoin Core developers
+// Copyright (c) 2009-2018 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 <fs.h>
-#include <init.h>
#include <key_io.h>
#include <random.h>
+#include <shutdown.h>
#include <sync.h>
#include <ui_interface.h>
#include <util.h>
diff --git a/src/script/bitcoinconsensus.cpp b/src/script/bitcoinconsensus.cpp
index 8cc44b675f..e2370c5e50 100644
--- a/src/script/bitcoinconsensus.cpp
+++ b/src/script/bitcoinconsensus.cpp
@@ -81,7 +81,7 @@ static int verify_script(const unsigned char *scriptPubKey, unsigned int scriptP
unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err)
{
if (!verify_flags(flags)) {
- return bitcoinconsensus_ERR_INVALID_FLAGS;
+ return set_error(err, bitcoinconsensus_ERR_INVALID_FLAGS);
}
try {
TxInputStream stream(SER_NETWORK, PROTOCOL_VERSION, txTo, txToLen);
diff --git a/src/script/ismine.cpp b/src/script/ismine.cpp
index 43dd9e582e..8c26866483 100644
--- a/src/script/ismine.cpp
+++ b/src/script/ismine.cpp
@@ -38,7 +38,7 @@ enum class IsMineResult
NO = 0, //! Not ours
WATCH_ONLY = 1, //! Included in watch-only balance
SPENDABLE = 2, //! Included in all balances
- INVALID = 3, //! Not spendable by anyone
+ INVALID = 3, //! Not spendable by anyone (uncompressed pubkey in segwit, P2SH inside P2SH or witness, witness inside witness)
};
bool PermitsUncompressed(IsMineSigVersion sigversion)
@@ -173,12 +173,10 @@ IsMineResult IsMineInner(const CKeyStore& keystore, const CScript& scriptPubKey,
} // namespace
-isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey, bool& isInvalid)
+isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey)
{
- isInvalid = false;
switch (IsMineInner(keystore, scriptPubKey, IsMineSigVersion::TOP)) {
case IsMineResult::INVALID:
- isInvalid = true;
case IsMineResult::NO:
return ISMINE_NO;
case IsMineResult::WATCH_ONLY:
@@ -189,12 +187,6 @@ isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey, bool&
assert(false);
}
-isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey)
-{
- bool isInvalid = false;
- return IsMine(keystore, scriptPubKey, isInvalid);
-}
-
isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest)
{
CScript script = GetScriptForDestination(dest);
diff --git a/src/script/ismine.h b/src/script/ismine.h
index a15768aecb..4246da49fe 100644
--- a/src/script/ismine.h
+++ b/src/script/ismine.h
@@ -24,12 +24,6 @@ enum isminetype
/** used for bitflags of isminetype */
typedef uint8_t isminefilter;
-/* isInvalid becomes true when the script is found invalid by consensus or policy. This will terminate the recursion
- * and return ISMINE_NO immediately, as an invalid script should never be considered as "mine". This is needed as
- * different SIGVERSION may have different network rules. Currently the only use of isInvalid is indicate uncompressed
- * keys in SigVersion::WITNESS_V0 script, but could also be used in similar cases in the future
- */
-isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey, bool& isInvalid);
isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey);
isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest);
diff --git a/src/script/sign.cpp b/src/script/sign.cpp
index 6dbfbda029..f1ac1f411a 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -33,27 +33,58 @@ bool MutableTransactionSignatureCreator::CreateSig(const SigningProvider& provid
return true;
}
-static bool Sign1(const SigningProvider& provider, const CKeyID& address, const BaseSignatureCreator& creator, const CScript& scriptCode, std::vector<valtype>& ret, SigVersion sigversion)
+static bool GetCScript(const SigningProvider& provider, const SignatureData& sigdata, const CScriptID& scriptid, CScript& script)
{
- std::vector<unsigned char> vchSig;
- if (!creator.CreateSig(provider, vchSig, address, scriptCode, sigversion))
- return false;
- ret.push_back(vchSig);
- return true;
+ if (provider.GetCScript(scriptid, script)) {
+ return true;
+ }
+ // Look for scripts in SignatureData
+ if (CScriptID(sigdata.redeem_script) == scriptid) {
+ script = sigdata.redeem_script;
+ return true;
+ } else if (CScriptID(sigdata.witness_script) == scriptid) {
+ script = sigdata.witness_script;
+ return true;
+ }
+ return false;
}
-static bool SignN(const SigningProvider& provider, const std::vector<valtype>& multisigdata, const BaseSignatureCreator& creator, const CScript& scriptCode, std::vector<valtype>& ret, SigVersion sigversion)
+static bool GetPubKey(const SigningProvider& provider, SignatureData& sigdata, const CKeyID& address, CPubKey& pubkey)
{
- int nSigned = 0;
- int nRequired = multisigdata.front()[0];
- for (unsigned int i = 1; i < multisigdata.size()-1 && nSigned < nRequired; i++)
- {
- const valtype& pubkey = multisigdata[i];
- CKeyID keyID = CPubKey(pubkey).GetID();
- if (Sign1(provider, keyID, creator, scriptCode, ret, sigversion))
- ++nSigned;
+ if (provider.GetPubKey(address, pubkey)) {
+ sigdata.misc_pubkeys.emplace(pubkey.GetID(), pubkey);
+ return true;
+ }
+ // Look for pubkey in all partial sigs
+ const auto it = sigdata.signatures.find(address);
+ if (it != sigdata.signatures.end()) {
+ pubkey = it->second.first;
+ return true;
+ }
+ // Look for pubkey in pubkey list
+ const auto& pk_it = sigdata.misc_pubkeys.find(address);
+ if (pk_it != sigdata.misc_pubkeys.end()) {
+ pubkey = pk_it->second;
+ return true;
}
- return nSigned==nRequired;
+ return false;
+}
+
+static bool CreateSig(const BaseSignatureCreator& creator, SignatureData& sigdata, const SigningProvider& provider, std::vector<unsigned char>& sig_out, const CKeyID& keyid, const CScript& scriptcode, SigVersion sigversion)
+{
+ const auto it = sigdata.signatures.find(keyid);
+ if (it != sigdata.signatures.end()) {
+ sig_out = it->second.second;
+ return true;
+ }
+ CPubKey pubkey;
+ GetPubKey(provider, sigdata, keyid, pubkey);
+ if (creator.CreateSig(provider, sig_out, keyid, scriptcode, sigversion)) {
+ auto i = sigdata.signatures.emplace(keyid, SigPair(pubkey, sig_out));
+ assert(i.second);
+ return true;
+ }
+ return false;
}
/**
@@ -63,17 +94,17 @@ static bool SignN(const SigningProvider& provider, const std::vector<valtype>& m
* Returns false if scriptPubKey could not be completely satisfied.
*/
static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator& creator, const CScript& scriptPubKey,
- std::vector<valtype>& ret, txnouttype& whichTypeRet, SigVersion sigversion)
+ std::vector<valtype>& ret, txnouttype& whichTypeRet, SigVersion sigversion, SignatureData& sigdata)
{
CScript scriptRet;
uint160 h160;
ret.clear();
+ std::vector<unsigned char> sig;
std::vector<valtype> vSolutions;
if (!Solver(scriptPubKey, whichTypeRet, vSolutions))
return false;
- CKeyID keyID;
switch (whichTypeRet)
{
case TX_NONSTANDARD:
@@ -81,37 +112,47 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
case TX_WITNESS_UNKNOWN:
return false;
case TX_PUBKEY:
- keyID = CPubKey(vSolutions[0]).GetID();
- return Sign1(provider, keyID, creator, scriptPubKey, ret, sigversion);
- case TX_PUBKEYHASH:
- keyID = CKeyID(uint160(vSolutions[0]));
- if (!Sign1(provider, keyID, creator, scriptPubKey, ret, sigversion))
- return false;
- else
- {
- CPubKey vch;
- provider.GetPubKey(keyID, vch);
- ret.push_back(ToByteVector(vch));
- }
+ if (!CreateSig(creator, sigdata, provider, sig, CPubKey(vSolutions[0]).GetID(), scriptPubKey, sigversion)) return false;
+ ret.push_back(std::move(sig));
+ return true;
+ case TX_PUBKEYHASH: {
+ CKeyID keyID = CKeyID(uint160(vSolutions[0]));
+ if (!CreateSig(creator, sigdata, provider, sig, keyID, scriptPubKey, sigversion)) return false;
+ ret.push_back(std::move(sig));
+ CPubKey pubkey;
+ GetPubKey(provider, sigdata, keyID, pubkey);
+ ret.push_back(ToByteVector(pubkey));
return true;
+ }
case TX_SCRIPTHASH:
- if (provider.GetCScript(uint160(vSolutions[0]), scriptRet)) {
+ if (GetCScript(provider, sigdata, uint160(vSolutions[0]), scriptRet)) {
ret.push_back(std::vector<unsigned char>(scriptRet.begin(), scriptRet.end()));
return true;
}
return false;
- case TX_MULTISIG:
+ case TX_MULTISIG: {
+ size_t required = vSolutions.front()[0];
ret.push_back(valtype()); // workaround CHECKMULTISIG bug
- return (SignN(provider, vSolutions, creator, scriptPubKey, ret, sigversion));
-
+ for (size_t i = 1; i < vSolutions.size() - 1; ++i) {
+ CPubKey pubkey = CPubKey(vSolutions[i]);
+ if (ret.size() < required + 1 && CreateSig(creator, sigdata, provider, sig, pubkey.GetID(), scriptPubKey, sigversion)) {
+ ret.push_back(std::move(sig));
+ }
+ }
+ bool ok = ret.size() == required + 1;
+ for (size_t i = 0; i + ret.size() < required + 1; ++i) {
+ ret.push_back(valtype());
+ }
+ return ok;
+ }
case TX_WITNESS_V0_KEYHASH:
ret.push_back(vSolutions[0]);
return true;
case TX_WITNESS_V0_SCRIPTHASH:
CRIPEMD160().Write(&vSolutions[0][0], vSolutions[0].size()).Finalize(h160.begin());
- if (provider.GetCScript(h160, scriptRet)) {
+ if (GetCScript(provider, sigdata, h160, scriptRet)) {
ret.push_back(std::vector<unsigned char>(scriptRet.begin(), scriptRet.end()));
return true;
}
@@ -139,9 +180,11 @@ static CScript PushAll(const std::vector<valtype>& values)
bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreator& creator, const CScript& fromPubKey, SignatureData& sigdata)
{
+ if (sigdata.complete) return true;
+
std::vector<valtype> result;
txnouttype whichType;
- bool solved = SignStep(provider, creator, fromPubKey, result, whichType, SigVersion::BASE);
+ bool solved = SignStep(provider, creator, fromPubKey, result, whichType, SigVersion::BASE, sigdata);
bool P2SH = false;
CScript subscript;
sigdata.scriptWitness.stack.clear();
@@ -152,7 +195,8 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato
// the final scriptSig is the signatures from that
// and then the serialized subscript:
subscript = CScript(result[0].begin(), result[0].end());
- solved = solved && SignStep(provider, creator, subscript, result, whichType, SigVersion::BASE) && whichType != TX_SCRIPTHASH;
+ sigdata.redeem_script = subscript;
+ solved = solved && SignStep(provider, creator, subscript, result, whichType, SigVersion::BASE, sigdata) && whichType != TX_SCRIPTHASH;
P2SH = true;
}
@@ -161,18 +205,23 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato
CScript witnessscript;
witnessscript << OP_DUP << OP_HASH160 << ToByteVector(result[0]) << OP_EQUALVERIFY << OP_CHECKSIG;
txnouttype subType;
- solved = solved && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0);
+ solved = solved && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0, sigdata);
sigdata.scriptWitness.stack = result;
+ sigdata.witness = true;
result.clear();
}
else if (solved && whichType == TX_WITNESS_V0_SCRIPTHASH)
{
CScript witnessscript(result[0].begin(), result[0].end());
+ sigdata.witness_script = witnessscript;
txnouttype subType;
- solved = solved && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0) && subType != TX_SCRIPTHASH && subType != TX_WITNESS_V0_SCRIPTHASH && subType != TX_WITNESS_V0_KEYHASH;
+ solved = solved && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0, sigdata) && subType != TX_SCRIPTHASH && subType != TX_WITNESS_V0_SCRIPTHASH && subType != TX_WITNESS_V0_KEYHASH;
result.push_back(std::vector<unsigned char>(witnessscript.begin(), witnessscript.end()));
sigdata.scriptWitness.stack = result;
+ sigdata.witness = true;
result.clear();
+ } else if (solved && whichType == TX_WITNESS_UNKNOWN) {
+ sigdata.witness = true;
}
if (P2SH) {
@@ -181,99 +230,55 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato
sigdata.scriptSig = PushAll(result);
// Test solution
- return solved && VerifyScript(sigdata.scriptSig, fromPubKey, &sigdata.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker());
+ sigdata.complete = solved && VerifyScript(sigdata.scriptSig, fromPubKey, &sigdata.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker());
+ return sigdata.complete;
}
-SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn)
+bool SignPSBTInput(const SigningProvider& provider, const CMutableTransaction& tx, PSBTInput& input, SignatureData& sigdata, int index, int sighash)
{
- SignatureData data;
- assert(tx.vin.size() > nIn);
- data.scriptSig = tx.vin[nIn].scriptSig;
- data.scriptWitness = tx.vin[nIn].scriptWitness;
- return data;
-}
-
-void UpdateInput(CTxIn& input, const SignatureData& data)
-{
- input.scriptSig = data.scriptSig;
- input.scriptWitness = data.scriptWitness;
-}
+ // if this input has a final scriptsig or scriptwitness, don't do anything with it
+ if (!input.final_script_sig.empty() || !input.final_script_witness.IsNull()) {
+ return true;
+ }
-bool SignSignature(const SigningProvider &provider, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType)
-{
- assert(nIn < txTo.vin.size());
+ // Fill SignatureData with input info
+ input.FillSignatureData(sigdata);
- MutableTransactionSignatureCreator creator(&txTo, nIn, amount, nHashType);
+ // Get UTXO
+ CTxOut utxo;
+ if (input.non_witness_utxo) {
+ utxo = input.non_witness_utxo->vout[tx.vin[index].prevout.n];
+ } else if (!input.witness_utxo.IsNull()) {
+ utxo = input.witness_utxo;
+ } else {
+ return false;
+ }
- SignatureData sigdata;
- bool ret = ProduceSignature(provider, creator, fromPubKey, sigdata);
- UpdateInput(txTo.vin.at(nIn), sigdata);
- return ret;
+ MutableTransactionSignatureCreator creator(&tx, index, utxo.nValue, sighash);
+ bool sig_complete = ProduceSignature(provider, creator, utxo.scriptPubKey, sigdata);
+ input.FromSignatureData(sigdata);
+ return sig_complete;
}
-bool SignSignature(const SigningProvider &provider, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType)
+class SignatureExtractorChecker final : public BaseSignatureChecker
{
- assert(nIn < txTo.vin.size());
- CTxIn& txin = txTo.vin[nIn];
- assert(txin.prevout.n < txFrom.vout.size());
- const CTxOut& txout = txFrom.vout[txin.prevout.n];
+private:
+ SignatureData& sigdata;
+ BaseSignatureChecker& checker;
- return SignSignature(provider, txout.scriptPubKey, txTo, nIn, txout.nValue, nHashType);
-}
+public:
+ SignatureExtractorChecker(SignatureData& sigdata, BaseSignatureChecker& checker) : sigdata(sigdata), checker(checker) {}
+ bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override;
+};
-static std::vector<valtype> CombineMultisig(const CScript& scriptPubKey, const BaseSignatureChecker& checker,
- const std::vector<valtype>& vSolutions,
- const std::vector<valtype>& sigs1, const std::vector<valtype>& sigs2, SigVersion sigversion)
+bool SignatureExtractorChecker::CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const
{
- // Combine all the signatures we've got:
- std::set<valtype> allsigs;
- for (const valtype& v : sigs1)
- {
- if (!v.empty())
- allsigs.insert(v);
- }
- for (const valtype& v : sigs2)
- {
- if (!v.empty())
- allsigs.insert(v);
- }
-
- // Build a map of pubkey -> signature by matching sigs to pubkeys:
- assert(vSolutions.size() > 1);
- unsigned int nSigsRequired = vSolutions.front()[0];
- unsigned int nPubKeys = vSolutions.size()-2;
- std::map<valtype, valtype> sigs;
- for (const valtype& sig : allsigs)
- {
- for (unsigned int i = 0; i < nPubKeys; i++)
- {
- const valtype& pubkey = vSolutions[i+1];
- if (sigs.count(pubkey))
- continue; // Already got a sig for this pubkey
-
- if (checker.CheckSig(sig, pubkey, scriptPubKey, sigversion))
- {
- sigs[pubkey] = sig;
- break;
- }
- }
- }
- // Now build a merged CScript:
- unsigned int nSigsHave = 0;
- std::vector<valtype> result; result.push_back(valtype()); // pop-one-too-many workaround
- for (unsigned int i = 0; i < nPubKeys && nSigsHave < nSigsRequired; i++)
- {
- if (sigs.count(vSolutions[i+1]))
- {
- result.push_back(sigs[vSolutions[i+1]]);
- ++nSigsHave;
- }
+ if (checker.CheckSig(scriptSig, vchPubKey, scriptCode, sigversion)) {
+ CPubKey pubkey(vchPubKey);
+ sigdata.signatures.emplace(pubkey.GetID(), SigPair(pubkey, scriptSig));
+ return true;
}
- // Fill any missing with OP_0:
- for (unsigned int i = nSigsHave; i < nSigsRequired; i++)
- result.push_back(valtype());
-
- return result;
+ return false;
}
namespace
@@ -298,89 +303,115 @@ struct Stacks
};
}
-static Stacks CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker,
- const txnouttype txType, const std::vector<valtype>& vSolutions,
- Stacks sigs1, Stacks sigs2, SigVersion sigversion)
+// Extracts signatures and scripts from incomplete scriptSigs. Please do not extend this, use PSBT instead
+SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn, const CTxOut& txout)
{
- switch (txType)
- {
- case TX_NONSTANDARD:
- case TX_NULL_DATA:
- case TX_WITNESS_UNKNOWN:
- // Don't know anything about this, assume bigger one is correct:
- if (sigs1.script.size() >= sigs2.script.size())
- return sigs1;
- return sigs2;
- case TX_PUBKEY:
- case TX_PUBKEYHASH:
- // Signatures are bigger than placeholders or empty scripts:
- if (sigs1.script.empty() || sigs1.script[0].empty())
- return sigs2;
- return sigs1;
- case TX_WITNESS_V0_KEYHASH:
- // Signatures are bigger than placeholders or empty scripts:
- if (sigs1.witness.empty() || sigs1.witness[0].empty())
- return sigs2;
- return sigs1;
- case TX_SCRIPTHASH:
- if (sigs1.script.empty() || sigs1.script.back().empty())
- return sigs2;
- else if (sigs2.script.empty() || sigs2.script.back().empty())
- return sigs1;
- else
- {
- // Recur to combine:
- valtype spk = sigs1.script.back();
- CScript pubKey2(spk.begin(), spk.end());
-
- txnouttype txType2;
- std::vector<std::vector<unsigned char> > vSolutions2;
- Solver(pubKey2, txType2, vSolutions2);
- sigs1.script.pop_back();
- sigs2.script.pop_back();
- Stacks result = CombineSignatures(pubKey2, checker, txType2, vSolutions2, sigs1, sigs2, sigversion);
- result.script.push_back(spk);
- return result;
- }
- case TX_MULTISIG:
- return Stacks(CombineMultisig(scriptPubKey, checker, vSolutions, sigs1.script, sigs2.script, sigversion));
- case TX_WITNESS_V0_SCRIPTHASH:
- if (sigs1.witness.empty() || sigs1.witness.back().empty())
- return sigs2;
- else if (sigs2.witness.empty() || sigs2.witness.back().empty())
- return sigs1;
- else
- {
- // Recur to combine:
- CScript pubKey2(sigs1.witness.back().begin(), sigs1.witness.back().end());
- txnouttype txType2;
- std::vector<valtype> vSolutions2;
- Solver(pubKey2, txType2, vSolutions2);
- sigs1.witness.pop_back();
- sigs1.script = sigs1.witness;
- sigs1.witness.clear();
- sigs2.witness.pop_back();
- sigs2.script = sigs2.witness;
- sigs2.witness.clear();
- Stacks result = CombineSignatures(pubKey2, checker, txType2, vSolutions2, sigs1, sigs2, SigVersion::WITNESS_V0);
- result.witness = result.script;
- result.script.clear();
- result.witness.push_back(valtype(pubKey2.begin(), pubKey2.end()));
- return result;
+ SignatureData data;
+ assert(tx.vin.size() > nIn);
+ data.scriptSig = tx.vin[nIn].scriptSig;
+ data.scriptWitness = tx.vin[nIn].scriptWitness;
+ Stacks stack(data);
+
+ // Get signatures
+ MutableTransactionSignatureChecker tx_checker(&tx, nIn, txout.nValue);
+ SignatureExtractorChecker extractor_checker(data, tx_checker);
+ if (VerifyScript(data.scriptSig, txout.scriptPubKey, &data.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, extractor_checker)) {
+ data.complete = true;
+ return data;
+ }
+
+ // Get scripts
+ txnouttype script_type;
+ std::vector<std::vector<unsigned char>> solutions;
+ Solver(txout.scriptPubKey, script_type, solutions);
+ SigVersion sigversion = SigVersion::BASE;
+ CScript next_script = txout.scriptPubKey;
+
+ if (script_type == TX_SCRIPTHASH && !stack.script.empty() && !stack.script.back().empty()) {
+ // Get the redeemScript
+ CScript redeem_script(stack.script.back().begin(), stack.script.back().end());
+ data.redeem_script = redeem_script;
+ next_script = std::move(redeem_script);
+
+ // Get redeemScript type
+ Solver(next_script, script_type, solutions);
+ stack.script.pop_back();
+ }
+ if (script_type == TX_WITNESS_V0_SCRIPTHASH && !stack.witness.empty() && !stack.witness.back().empty()) {
+ // Get the witnessScript
+ CScript witness_script(stack.witness.back().begin(), stack.witness.back().end());
+ data.witness_script = witness_script;
+ next_script = std::move(witness_script);
+
+ // Get witnessScript type
+ Solver(next_script, script_type, solutions);
+ stack.witness.pop_back();
+ stack.script = std::move(stack.witness);
+ stack.witness.clear();
+ sigversion = SigVersion::WITNESS_V0;
+ }
+ if (script_type == TX_MULTISIG && !stack.script.empty()) {
+ // Build a map of pubkey -> signature by matching sigs to pubkeys:
+ assert(solutions.size() > 1);
+ unsigned int num_pubkeys = solutions.size()-2;
+ unsigned int last_success_key = 0;
+ for (const valtype& sig : stack.script) {
+ for (unsigned int i = last_success_key; i < num_pubkeys; ++i) {
+ const valtype& pubkey = solutions[i+1];
+ // We either have a signature for this pubkey, or we have found a signature and it is valid
+ if (data.signatures.count(CPubKey(pubkey).GetID()) || extractor_checker.CheckSig(sig, pubkey, next_script, sigversion)) {
+ last_success_key = i + 1;
+ break;
+ }
+ }
}
- default:
- return Stacks();
}
+
+ return data;
}
-SignatureData CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker,
- const SignatureData& scriptSig1, const SignatureData& scriptSig2)
+void UpdateInput(CTxIn& input, const SignatureData& data)
{
- txnouttype txType;
- std::vector<std::vector<unsigned char> > vSolutions;
- Solver(scriptPubKey, txType, vSolutions);
+ input.scriptSig = data.scriptSig;
+ input.scriptWitness = data.scriptWitness;
+}
+
+void SignatureData::MergeSignatureData(SignatureData sigdata)
+{
+ if (complete) return;
+ if (sigdata.complete) {
+ *this = std::move(sigdata);
+ return;
+ }
+ if (redeem_script.empty() && !sigdata.redeem_script.empty()) {
+ redeem_script = sigdata.redeem_script;
+ }
+ if (witness_script.empty() && !sigdata.witness_script.empty()) {
+ witness_script = sigdata.witness_script;
+ }
+ signatures.insert(std::make_move_iterator(sigdata.signatures.begin()), std::make_move_iterator(sigdata.signatures.end()));
+}
+
+bool SignSignature(const SigningProvider &provider, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType)
+{
+ assert(nIn < txTo.vin.size());
- return CombineSignatures(scriptPubKey, checker, txType, vSolutions, Stacks(scriptSig1), Stacks(scriptSig2), SigVersion::BASE).Output();
+ MutableTransactionSignatureCreator creator(&txTo, nIn, amount, nHashType);
+
+ SignatureData sigdata;
+ bool ret = ProduceSignature(provider, creator, fromPubKey, sigdata);
+ UpdateInput(txTo.vin.at(nIn), sigdata);
+ return ret;
+}
+
+bool SignSignature(const SigningProvider &provider, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType)
+{
+ assert(nIn < txTo.vin.size());
+ CTxIn& txin = txTo.vin[nIn];
+ assert(txin.prevout.n < txFrom.vout.size());
+ const CTxOut& txout = txFrom.vout[txin.prevout.n];
+
+ return SignSignature(provider, txout.scriptPubKey, txTo, nIn, txout.nValue, nHashType);
}
namespace {
@@ -416,6 +447,7 @@ public:
}
const BaseSignatureCreator& DUMMY_SIGNATURE_CREATOR = DummySignatureCreator();
+const SigningProvider& DUMMY_SIGNING_PROVIDER = SigningProvider();
bool IsSolvable(const SigningProvider& provider, const CScript& script)
{
@@ -434,3 +466,161 @@ bool IsSolvable(const SigningProvider& provider, const CScript& script)
}
return false;
}
+
+
+bool PartiallySignedTransaction::IsNull() const
+{
+ return !tx && inputs.empty() && outputs.empty() && unknown.empty();
+}
+
+void PartiallySignedTransaction::Merge(const PartiallySignedTransaction& psbt)
+{
+ for (unsigned int i = 0; i < inputs.size(); ++i) {
+ inputs[i].Merge(psbt.inputs[i]);
+ }
+ for (unsigned int i = 0; i < outputs.size(); ++i) {
+ outputs[i].Merge(psbt.outputs[i]);
+ }
+}
+
+bool PartiallySignedTransaction::IsSane() const
+{
+ for (PSBTInput input : inputs) {
+ if (!input.IsSane()) return false;
+ }
+ return true;
+}
+
+bool PSBTInput::IsNull() const
+{
+ return !non_witness_utxo && witness_utxo.IsNull() && partial_sigs.empty() && unknown.empty() && hd_keypaths.empty() && redeem_script.empty() && witness_script.empty();
+}
+
+void PSBTInput::FillSignatureData(SignatureData& sigdata) const
+{
+ if (!final_script_sig.empty()) {
+ sigdata.scriptSig = final_script_sig;
+ sigdata.complete = true;
+ }
+ if (!final_script_witness.IsNull()) {
+ sigdata.scriptWitness = final_script_witness;
+ sigdata.complete = true;
+ }
+ if (sigdata.complete) {
+ return;
+ }
+
+ sigdata.signatures.insert(partial_sigs.begin(), partial_sigs.end());
+ if (!redeem_script.empty()) {
+ sigdata.redeem_script = redeem_script;
+ }
+ if (!witness_script.empty()) {
+ sigdata.witness_script = witness_script;
+ }
+ for (const auto& key_pair : hd_keypaths) {
+ sigdata.misc_pubkeys.emplace(key_pair.first.GetID(), key_pair.first);
+ }
+}
+
+void PSBTInput::FromSignatureData(const SignatureData& sigdata)
+{
+ if (sigdata.complete) {
+ partial_sigs.clear();
+ hd_keypaths.clear();
+ redeem_script.clear();
+ witness_script.clear();
+
+ if (!sigdata.scriptSig.empty()) {
+ final_script_sig = sigdata.scriptSig;
+ }
+ if (!sigdata.scriptWitness.IsNull()) {
+ final_script_witness = sigdata.scriptWitness;
+ }
+ return;
+ }
+
+ partial_sigs.insert(sigdata.signatures.begin(), sigdata.signatures.end());
+ if (redeem_script.empty() && !sigdata.redeem_script.empty()) {
+ redeem_script = sigdata.redeem_script;
+ }
+ if (witness_script.empty() && !sigdata.witness_script.empty()) {
+ witness_script = sigdata.witness_script;
+ }
+}
+
+void PSBTInput::Merge(const PSBTInput& input)
+{
+ if (!non_witness_utxo && input.non_witness_utxo) non_witness_utxo = input.non_witness_utxo;
+ if (witness_utxo.IsNull() && !input.witness_utxo.IsNull()) {
+ witness_utxo = input.witness_utxo;
+ non_witness_utxo = nullptr; // Clear out any non-witness utxo when we set a witness one.
+ }
+
+ partial_sigs.insert(input.partial_sigs.begin(), input.partial_sigs.end());
+ hd_keypaths.insert(input.hd_keypaths.begin(), input.hd_keypaths.end());
+ unknown.insert(input.unknown.begin(), input.unknown.end());
+
+ if (redeem_script.empty() && !input.redeem_script.empty()) redeem_script = input.redeem_script;
+ if (witness_script.empty() && !input.witness_script.empty()) witness_script = input.witness_script;
+ if (final_script_sig.empty() && !input.final_script_sig.empty()) final_script_sig = input.final_script_sig;
+ if (final_script_witness.IsNull() && !input.final_script_witness.IsNull()) final_script_witness = input.final_script_witness;
+}
+
+bool PSBTInput::IsSane() const
+{
+ // Cannot have both witness and non-witness utxos
+ if (!witness_utxo.IsNull() && non_witness_utxo) return false;
+
+ // If we have a witness_script or a scriptWitness, we must also have a witness utxo
+ if (!witness_script.empty() && witness_utxo.IsNull()) return false;
+ if (!final_script_witness.IsNull() && witness_utxo.IsNull()) return false;
+
+ return true;
+}
+
+void PSBTOutput::FillSignatureData(SignatureData& sigdata) const
+{
+ if (!redeem_script.empty()) {
+ sigdata.redeem_script = redeem_script;
+ }
+ if (!witness_script.empty()) {
+ sigdata.witness_script = witness_script;
+ }
+ for (const auto& key_pair : hd_keypaths) {
+ sigdata.misc_pubkeys.emplace(key_pair.first.GetID(), key_pair.first);
+ }
+}
+
+void PSBTOutput::FromSignatureData(const SignatureData& sigdata)
+{
+ if (redeem_script.empty() && !sigdata.redeem_script.empty()) {
+ redeem_script = sigdata.redeem_script;
+ }
+ if (witness_script.empty() && !sigdata.witness_script.empty()) {
+ witness_script = sigdata.witness_script;
+ }
+}
+
+bool PSBTOutput::IsNull() const
+{
+ return redeem_script.empty() && witness_script.empty() && hd_keypaths.empty() && unknown.empty();
+}
+
+void PSBTOutput::Merge(const PSBTOutput& output)
+{
+ hd_keypaths.insert(output.hd_keypaths.begin(), output.hd_keypaths.end());
+ unknown.insert(output.unknown.begin(), output.unknown.end());
+
+ if (redeem_script.empty() && !output.redeem_script.empty()) redeem_script = output.redeem_script;
+ if (witness_script.empty() && !output.witness_script.empty()) witness_script = output.witness_script;
+}
+
+bool PublicOnlySigningProvider::GetCScript(const CScriptID &scriptid, CScript& script) const
+{
+ return m_provider->GetCScript(scriptid, script);
+}
+
+bool PublicOnlySigningProvider::GetPubKey(const CKeyID &address, CPubKey& pubkey) const
+{
+ return m_provider->GetPubKey(address, pubkey);
+}
diff --git a/src/script/sign.h b/src/script/sign.h
index 8ef0306bfe..e3a6196b28 100644
--- a/src/script/sign.h
+++ b/src/script/sign.h
@@ -6,7 +6,11 @@
#ifndef BITCOIN_SCRIPT_SIGN_H
#define BITCOIN_SCRIPT_SIGN_H
+#include <boost/optional.hpp>
+#include <hash.h>
+#include <pubkey.h>
#include <script/interpreter.h>
+#include <streams.h>
class CKey;
class CKeyID;
@@ -21,9 +25,22 @@ class SigningProvider
{
public:
virtual ~SigningProvider() {}
- virtual bool GetCScript(const CScriptID &scriptid, CScript& script) const =0;
- virtual bool GetPubKey(const CKeyID &address, CPubKey& pubkey) const =0;
- virtual bool GetKey(const CKeyID &address, CKey& key) const =0;
+ virtual bool GetCScript(const CScriptID &scriptid, CScript& script) const { return false; }
+ virtual bool GetPubKey(const CKeyID &address, CPubKey& pubkey) const { return false; }
+ virtual bool GetKey(const CKeyID &address, CKey& key) const { return false; }
+};
+
+extern const SigningProvider& DUMMY_SIGNING_PROVIDER;
+
+class PublicOnlySigningProvider : public SigningProvider
+{
+private:
+ const SigningProvider* m_provider;
+
+public:
+ PublicOnlySigningProvider(const SigningProvider* provider) : m_provider(provider) {}
+ bool GetCScript(const CScriptID &scriptid, CScript& script) const;
+ bool GetPubKey(const CKeyID &address, CPubKey& pubkey) const;
};
/** Interface for signature creators. */
@@ -53,12 +70,572 @@ public:
/** A signature creator that just produces 72-byte empty signatures. */
extern const BaseSignatureCreator& DUMMY_SIGNATURE_CREATOR;
+typedef std::pair<CPubKey, std::vector<unsigned char>> SigPair;
+
+// This struct contains information from a transaction input and also contains signatures for that input.
+// The information contained here can be used to create a signature and is also filled by ProduceSignature
+// in order to construct final scriptSigs and scriptWitnesses.
struct SignatureData {
- CScript scriptSig;
- CScriptWitness scriptWitness;
+ bool complete = false; ///< Stores whether the scriptSig and scriptWitness are complete
+ bool witness = false; ///< Stores whether the input this SigData corresponds to is a witness input
+ CScript scriptSig; ///< The scriptSig of an input. Contains complete signatures or the traditional partial signatures format
+ CScript redeem_script; ///< The redeemScript (if any) for the input
+ CScript witness_script; ///< The witnessScript (if any) for the input. witnessScripts are used in P2WSH outputs.
+ CScriptWitness scriptWitness; ///< The scriptWitness of an input. Contains complete signatures or the traditional partial signatures format. scriptWitness is part of a transaction input per BIP 144.
+ std::map<CKeyID, SigPair> signatures; ///< BIP 174 style partial signatures for the input. May contain all signatures necessary for producing a final scriptSig or scriptWitness.
+ std::map<CKeyID, CPubKey> misc_pubkeys;
SignatureData() {}
explicit SignatureData(const CScript& script) : scriptSig(script) {}
+ void MergeSignatureData(SignatureData sigdata);
+};
+
+// Magic bytes
+static constexpr uint8_t PSBT_MAGIC_BYTES[5] = {'p', 's', 'b', 't', 0xff};
+
+// Global types
+static constexpr uint8_t PSBT_GLOBAL_UNSIGNED_TX = 0x00;
+
+// Input types
+static constexpr uint8_t PSBT_IN_NON_WITNESS_UTXO = 0x00;
+static constexpr uint8_t PSBT_IN_WITNESS_UTXO = 0x01;
+static constexpr uint8_t PSBT_IN_PARTIAL_SIG = 0x02;
+static constexpr uint8_t PSBT_IN_SIGHASH = 0x03;
+static constexpr uint8_t PSBT_IN_REDEEMSCRIPT = 0x04;
+static constexpr uint8_t PSBT_IN_WITNESSSCRIPT = 0x05;
+static constexpr uint8_t PSBT_IN_BIP32_DERIVATION = 0x06;
+static constexpr uint8_t PSBT_IN_SCRIPTSIG = 0x07;
+static constexpr uint8_t PSBT_IN_SCRIPTWITNESS = 0x08;
+
+// Output types
+static constexpr uint8_t PSBT_OUT_REDEEMSCRIPT = 0x00;
+static constexpr uint8_t PSBT_OUT_WITNESSSCRIPT = 0x01;
+static constexpr uint8_t PSBT_OUT_BIP32_DERIVATION = 0x02;
+
+// The separator is 0x00. Reading this in means that the unserializer can interpret it
+// as a 0 length key which indicates that this is the separator. The separator has no value.
+static constexpr uint8_t PSBT_SEPARATOR = 0x00;
+
+// Takes a stream and multiple arguments and serializes them into a vector and then into the stream
+// The resulting output into the stream has the total serialized length of all of the objects followed by all objects concatenated with each other.
+template<typename Stream, typename... X>
+void SerializeToVector(Stream& s, const X&... args)
+{
+ std::vector<unsigned char> ret;
+ CVectorWriter ss(SER_NETWORK, PROTOCOL_VERSION, ret, 0);
+ SerializeMany(ss, args...);
+ s << ret;
+}
+
+// Takes a stream and multiple arguments and unserializes them first as a vector then each object individually in the order provided in the arguments
+template<typename Stream, typename... X>
+void UnserializeFromVector(Stream& s, X&... args)
+{
+ std::vector<unsigned char> data;
+ s >> data;
+ CDataStream ss(data, SER_NETWORK, PROTOCOL_VERSION);
+ UnserializeMany(ss, args...);
+ if (!ss.eof()) {
+ throw std::ios_base::failure("Size of value was not the stated size");
+ }
+}
+
+// Deserialize HD keypaths into a map
+template<typename Stream>
+void DeserializeHDKeypaths(Stream& s, const std::vector<unsigned char>& key, std::map<CPubKey, std::vector<uint32_t>>& hd_keypaths)
+{
+ // Make sure that the key is the size of pubkey + 1
+ if (key.size() != CPubKey::PUBLIC_KEY_SIZE + 1 && key.size() != CPubKey::COMPRESSED_PUBLIC_KEY_SIZE + 1) {
+ throw std::ios_base::failure("Size of key was not the expected size for the type BIP32 keypath");
+ }
+ // Read in the pubkey from key
+ CPubKey pubkey(key.begin() + 1, key.end());
+ if (!pubkey.IsFullyValid()) {
+ throw std::ios_base::failure("Invalid pubkey");
+ }
+ if (hd_keypaths.count(pubkey) > 0) {
+ throw std::ios_base::failure("Duplicate Key, pubkey derivation path already provided");
+ }
+
+ // Read in key path
+ uint64_t value_len = ReadCompactSize(s);
+ std::vector<uint32_t> keypath;
+ for (unsigned int i = 0; i < value_len; i += sizeof(uint32_t)) {
+ uint32_t index;
+ s >> index;
+ keypath.push_back(index);
+ }
+
+ // Add to map
+ hd_keypaths.emplace(pubkey, keypath);
+}
+
+// Serialize HD keypaths to a stream from a map
+template<typename Stream>
+void SerializeHDKeypaths(Stream& s, const std::map<CPubKey, std::vector<uint32_t>>& hd_keypaths, uint8_t type)
+{
+ for (auto keypath_pair : hd_keypaths) {
+ SerializeToVector(s, type, MakeSpan(keypath_pair.first));
+ WriteCompactSize(s, keypath_pair.second.size() * sizeof(uint32_t));
+ for (auto& path : keypath_pair.second) {
+ s << path;
+ }
+ }
+}
+
+/** A structure for PSBTs which contain per-input information */
+struct PSBTInput
+{
+ CTransactionRef non_witness_utxo;
+ CTxOut witness_utxo;
+ CScript redeem_script;
+ CScript witness_script;
+ CScript final_script_sig;
+ CScriptWitness final_script_witness;
+ std::map<CPubKey, std::vector<uint32_t>> hd_keypaths;
+ std::map<CKeyID, SigPair> partial_sigs;
+ std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown;
+ int sighash_type = 0;
+
+ bool IsNull() const;
+ void FillSignatureData(SignatureData& sigdata) const;
+ void FromSignatureData(const SignatureData& sigdata);
+ void Merge(const PSBTInput& input);
+ bool IsSane() const;
+ PSBTInput() {}
+
+ template <typename Stream>
+ inline void Serialize(Stream& s) const {
+ // Write the utxo
+ // If there is a non-witness utxo, then don't add the witness one.
+ if (non_witness_utxo) {
+ SerializeToVector(s, PSBT_IN_NON_WITNESS_UTXO);
+ SerializeToVector(s, non_witness_utxo);
+ } else if (!witness_utxo.IsNull()) {
+ SerializeToVector(s, PSBT_IN_WITNESS_UTXO);
+ SerializeToVector(s, witness_utxo);
+ }
+
+ if (final_script_sig.empty() && final_script_witness.IsNull()) {
+ // Write any partial signatures
+ for (auto sig_pair : partial_sigs) {
+ SerializeToVector(s, PSBT_IN_PARTIAL_SIG, MakeSpan(sig_pair.second.first));
+ s << sig_pair.second.second;
+ }
+
+ // Write the sighash type
+ if (sighash_type > 0) {
+ SerializeToVector(s, PSBT_IN_SIGHASH);
+ SerializeToVector(s, sighash_type);
+ }
+
+ // Write the redeem script
+ if (!redeem_script.empty()) {
+ SerializeToVector(s, PSBT_IN_REDEEMSCRIPT);
+ s << redeem_script;
+ }
+
+ // Write the witness script
+ if (!witness_script.empty()) {
+ SerializeToVector(s, PSBT_IN_WITNESSSCRIPT);
+ s << witness_script;
+ }
+
+ // Write any hd keypaths
+ SerializeHDKeypaths(s, hd_keypaths, PSBT_IN_BIP32_DERIVATION);
+ }
+
+ // Write script sig
+ if (!final_script_sig.empty()) {
+ SerializeToVector(s, PSBT_IN_SCRIPTSIG);
+ s << final_script_sig;
+ }
+ // write script witness
+ if (!final_script_witness.IsNull()) {
+ SerializeToVector(s, PSBT_IN_SCRIPTWITNESS);
+ SerializeToVector(s, final_script_witness.stack);
+ }
+
+ // Write unknown things
+ for (auto& entry : unknown) {
+ s << entry.first;
+ s << entry.second;
+ }
+
+ s << PSBT_SEPARATOR;
+ }
+
+
+ template <typename Stream>
+ inline void Unserialize(Stream& s) {
+ // Read loop
+ while(!s.empty()) {
+ // Read
+ std::vector<unsigned char> key;
+ s >> key;
+
+ // the key is empty if that was actually a separator byte
+ // This is a special case for key lengths 0 as those are not allowed (except for separator)
+ if (key.empty()) return;
+
+ // First byte of key is the type
+ unsigned char type = key[0];
+
+ // Do stuff based on type
+ switch(type) {
+ case PSBT_IN_NON_WITNESS_UTXO:
+ if (non_witness_utxo) {
+ throw std::ios_base::failure("Duplicate Key, input non-witness utxo already provided");
+ }
+ UnserializeFromVector(s, non_witness_utxo);
+ break;
+ case PSBT_IN_WITNESS_UTXO:
+ if (!witness_utxo.IsNull()) {
+ throw std::ios_base::failure("Duplicate Key, input witness utxo already provided");
+ }
+ UnserializeFromVector(s, witness_utxo);
+ break;
+ case PSBT_IN_PARTIAL_SIG:
+ {
+ // Make sure that the key is the size of pubkey + 1
+ if (key.size() != CPubKey::PUBLIC_KEY_SIZE + 1 && key.size() != CPubKey::COMPRESSED_PUBLIC_KEY_SIZE + 1) {
+ throw std::ios_base::failure("Size of key was not the expected size for the type partial signature pubkey");
+ }
+ // Read in the pubkey from key
+ CPubKey pubkey(key.begin() + 1, key.end());
+ if (!pubkey.IsFullyValid()) {
+ throw std::ios_base::failure("Invalid pubkey");
+ }
+ if (partial_sigs.count(pubkey.GetID()) > 0) {
+ throw std::ios_base::failure("Duplicate Key, input partial signature for pubkey already provided");
+ }
+
+ // Read in the signature from value
+ std::vector<unsigned char> sig;
+ s >> sig;
+
+ // Add to list
+ partial_sigs.emplace(pubkey.GetID(), SigPair(pubkey, std::move(sig)));
+ break;
+ }
+ case PSBT_IN_SIGHASH:
+ if (sighash_type > 0) {
+ throw std::ios_base::failure("Duplicate Key, input sighash type already provided");
+ }
+ UnserializeFromVector(s, sighash_type);
+ break;
+ case PSBT_IN_REDEEMSCRIPT:
+ {
+ if (!redeem_script.empty()) {
+ throw std::ios_base::failure("Duplicate Key, input redeemScript already provided");
+ }
+ s >> redeem_script;
+ break;
+ }
+ case PSBT_IN_WITNESSSCRIPT:
+ {
+ if (!witness_script.empty()) {
+ throw std::ios_base::failure("Duplicate Key, input witnessScript already provided");
+ }
+ s >> witness_script;
+ break;
+ }
+ case PSBT_IN_BIP32_DERIVATION:
+ {
+ DeserializeHDKeypaths(s, key, hd_keypaths);
+ break;
+ }
+ case PSBT_IN_SCRIPTSIG:
+ {
+ if (!final_script_sig.empty()) {
+ throw std::ios_base::failure("Duplicate Key, input final scriptSig already provided");
+ }
+ s >> final_script_sig;
+ break;
+ }
+ case PSBT_IN_SCRIPTWITNESS:
+ {
+ if (!final_script_witness.IsNull()) {
+ throw std::ios_base::failure("Duplicate Key, input final scriptWitness already provided");
+ }
+ UnserializeFromVector(s, final_script_witness.stack);
+ break;
+ }
+ // Unknown stuff
+ default:
+ if (unknown.count(key) > 0) {
+ throw std::ios_base::failure("Duplicate Key, key for unknown value already provided");
+ }
+ // Read in the value
+ std::vector<unsigned char> val_bytes;
+ s >> val_bytes;
+ unknown.emplace(std::move(key), std::move(val_bytes));
+ break;
+ }
+ }
+ }
+
+ template <typename Stream>
+ PSBTInput(deserialize_type, Stream& s) {
+ Unserialize(s);
+ }
+};
+
+/** A structure for PSBTs which contains per output information */
+struct PSBTOutput
+{
+ CScript redeem_script;
+ CScript witness_script;
+ std::map<CPubKey, std::vector<uint32_t>> hd_keypaths;
+ std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown;
+
+ bool IsNull() const;
+ void FillSignatureData(SignatureData& sigdata) const;
+ void FromSignatureData(const SignatureData& sigdata);
+ void Merge(const PSBTOutput& output);
+ bool IsSane() const;
+ PSBTOutput() {}
+
+ template <typename Stream>
+ inline void Serialize(Stream& s) const {
+ // Write the redeem script
+ if (!redeem_script.empty()) {
+ SerializeToVector(s, PSBT_OUT_REDEEMSCRIPT);
+ s << redeem_script;
+ }
+
+ // Write the witness script
+ if (!witness_script.empty()) {
+ SerializeToVector(s, PSBT_OUT_WITNESSSCRIPT);
+ s << witness_script;
+ }
+
+ // Write any hd keypaths
+ SerializeHDKeypaths(s, hd_keypaths, PSBT_OUT_BIP32_DERIVATION);
+
+ // Write unknown things
+ for (auto& entry : unknown) {
+ s << entry.first;
+ s << entry.second;
+ }
+
+ s << PSBT_SEPARATOR;
+ }
+
+
+ template <typename Stream>
+ inline void Unserialize(Stream& s) {
+ // Read loop
+ while(!s.empty()) {
+ // Read
+ std::vector<unsigned char> key;
+ s >> key;
+
+ // the key is empty if that was actually a separator byte
+ // This is a special case for key lengths 0 as those are not allowed (except for separator)
+ if (key.empty()) return;
+
+ // First byte of key is the type
+ unsigned char type = key[0];
+
+ // Do stuff based on type
+ switch(type) {
+ case PSBT_OUT_REDEEMSCRIPT:
+ {
+ if (!redeem_script.empty()) {
+ throw std::ios_base::failure("Duplicate Key, output redeemScript already provided");
+ }
+ s >> redeem_script;
+ break;
+ }
+ case PSBT_OUT_WITNESSSCRIPT:
+ {
+ if (!witness_script.empty()) {
+ throw std::ios_base::failure("Duplicate Key, output witnessScript already provided");
+ }
+ s >> witness_script;
+ break;
+ }
+ case PSBT_OUT_BIP32_DERIVATION:
+ {
+ DeserializeHDKeypaths(s, key, hd_keypaths);
+ break;
+ }
+ // Unknown stuff
+ default: {
+ if (unknown.count(key) > 0) {
+ throw std::ios_base::failure("Duplicate Key, key for unknown value already provided");
+ }
+ // Read in the value
+ std::vector<unsigned char> val_bytes;
+ s >> val_bytes;
+ unknown.emplace(std::move(key), std::move(val_bytes));
+ break;
+ }
+ }
+ }
+ }
+
+ template <typename Stream>
+ PSBTOutput(deserialize_type, Stream& s) {
+ Unserialize(s);
+ }
+};
+
+/** A version of CTransaction with the PSBT format*/
+struct PartiallySignedTransaction
+{
+ boost::optional<CMutableTransaction> tx;
+ std::vector<PSBTInput> inputs;
+ std::vector<PSBTOutput> outputs;
+ std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown;
+
+ bool IsNull() const;
+ void Merge(const PartiallySignedTransaction& psbt);
+ bool IsSane() const;
+ PartiallySignedTransaction() {}
+ PartiallySignedTransaction(const PartiallySignedTransaction& psbt_in) : tx(psbt_in.tx), inputs(psbt_in.inputs), outputs(psbt_in.outputs), unknown(psbt_in.unknown) {}
+
+ // Only checks if they refer to the same transaction
+ friend bool operator==(const PartiallySignedTransaction& a, const PartiallySignedTransaction &b)
+ {
+ return a.tx->GetHash() == b.tx->GetHash();
+ }
+ friend bool operator!=(const PartiallySignedTransaction& a, const PartiallySignedTransaction &b)
+ {
+ return !(a == b);
+ }
+
+ template <typename Stream>
+ inline void Serialize(Stream& s) const {
+
+ // magic bytes
+ s << PSBT_MAGIC_BYTES;
+
+ // unsigned tx flag
+ SerializeToVector(s, PSBT_GLOBAL_UNSIGNED_TX);
+
+ // Write serialized tx to a stream
+ SerializeToVector(s, *tx);
+
+ // Write the unknown things
+ for (auto& entry : unknown) {
+ s << entry.first;
+ s << entry.second;
+ }
+
+ // Separator
+ s << PSBT_SEPARATOR;
+
+ // Write inputs
+ for (const PSBTInput& input : inputs) {
+ s << input;
+ }
+ // Write outputs
+ for (const PSBTOutput& output : outputs) {
+ s << output;
+ }
+ }
+
+
+ template <typename Stream>
+ inline void Unserialize(Stream& s) {
+ // Read the magic bytes
+ uint8_t magic[5];
+ s >> magic;
+ if (!std::equal(magic, magic + 5, PSBT_MAGIC_BYTES)) {
+ throw std::ios_base::failure("Invalid PSBT magic bytes");
+ }
+
+ // Read global data
+ while(!s.empty()) {
+ // Read
+ std::vector<unsigned char> key;
+ s >> key;
+
+ // the key is empty if that was actually a separator byte
+ // This is a special case for key lengths 0 as those are not allowed (except for separator)
+ if (key.empty()) break;
+
+ // First byte of key is the type
+ unsigned char type = key[0];
+
+ // Do stuff based on type
+ switch(type) {
+ case PSBT_GLOBAL_UNSIGNED_TX:
+ {
+ if (tx) {
+ throw std::ios_base::failure("Duplicate Key, unsigned tx already provided");
+ }
+ CMutableTransaction mtx;
+ UnserializeFromVector(s, mtx);
+ tx = std::move(mtx);
+ // Make sure that all scriptSigs and scriptWitnesses are empty
+ for (const CTxIn& txin : tx->vin) {
+ if (!txin.scriptSig.empty() || !txin.scriptWitness.IsNull()) {
+ throw std::ios_base::failure("Unsigned tx does not have empty scriptSigs and scriptWitnesses.");
+ }
+ }
+ break;
+ }
+ // Unknown stuff
+ default: {
+ if (unknown.count(key) > 0) {
+ throw std::ios_base::failure("Duplicate Key, key for unknown value already provided");
+ }
+ // Read in the value
+ std::vector<unsigned char> val_bytes;
+ s >> val_bytes;
+ unknown.emplace(std::move(key), std::move(val_bytes));
+ }
+ }
+ }
+
+ // Make sure that we got an unsigned tx
+ if (!tx) {
+ throw std::ios_base::failure("No unsigned transcation was provided");
+ }
+
+ // Read input data
+ unsigned int i = 0;
+ while (!s.empty() && i < tx->vin.size()) {
+ PSBTInput input;
+ s >> input;
+ inputs.push_back(input);
+
+ // Make sure the non-witness utxo matches the outpoint
+ if (input.non_witness_utxo && input.non_witness_utxo->GetHash() != tx->vin[i].prevout.hash) {
+ throw std::ios_base::failure("Non-witness UTXO does not match outpoint hash");
+ }
+ ++i;
+ }
+ // Make sure that the number of inputs matches the number of inputs in the transaction
+ if (inputs.size() != tx->vin.size()) {
+ throw std::ios_base::failure("Inputs provided does not match the number of inputs in transaction.");
+ }
+
+ // Read output data
+ i = 0;
+ while (!s.empty() && i < tx->vout.size()) {
+ PSBTOutput output;
+ s >> output;
+ outputs.push_back(output);
+ ++i;
+ }
+ // Make sure that the number of outputs matches the number of outputs in the transaction
+ if (outputs.size() != tx->vout.size()) {
+ throw std::ios_base::failure("Outputs provided does not match the number of outputs in transaction.");
+ }
+ // Sanity check
+ if (!IsSane()) {
+ throw std::ios_base::failure("PSBT is not sane.");
+ }
+ }
+
+ template <typename Stream>
+ PartiallySignedTransaction(deserialize_type, Stream& s) {
+ Unserialize(s);
+ }
};
/** Produce a script signature using a generic signature creator. */
@@ -68,11 +645,11 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato
bool SignSignature(const SigningProvider &provider, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType);
bool SignSignature(const SigningProvider &provider, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType);
-/** Combine two script signatures using a generic signature checker, intelligently, possibly with OP_0 placeholders. */
-SignatureData CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker, const SignatureData& scriptSig1, const SignatureData& scriptSig2);
+/** Signs a PSBTInput */
+bool SignPSBTInput(const SigningProvider& provider, const CMutableTransaction& tx, PSBTInput& input, SignatureData& sigdata, int index, int sighash = 1);
-/** Extract signature data from a transaction, and insert it. */
-SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn);
+/** Extract signature data from a transaction input, and insert it. */
+SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn, const CTxOut& txout);
void UpdateInput(CTxIn& input, const SignatureData& data);
/* Check whether we know how to sign for an output like this, assuming we
diff --git a/src/script/standard.cpp b/src/script/standard.cpp
index d9269d6147..f0b2c62a91 100644
--- a/src/script/standard.cpp
+++ b/src/script/standard.cpp
@@ -5,6 +5,7 @@
#include <script/standard.h>
+#include <crypto/sha256.h>
#include <pubkey.h>
#include <script/script.h>
#include <util.h>
@@ -18,6 +19,11 @@ unsigned nMaxDatacarrierBytes = MAX_OP_RETURN_RELAY;
CScriptID::CScriptID(const CScript& in) : uint160(Hash160(in.begin(), in.end())) {}
+WitnessV0ScriptHash::WitnessV0ScriptHash(const CScript& in)
+{
+ CSHA256().Write(in.data(), in.size()).Finalize(begin());
+}
+
const char* GetTxnOutputType(txnouttype t)
{
switch (t)
@@ -329,9 +335,7 @@ CScript GetScriptForWitness(const CScript& redeemscript)
return GetScriptForDestination(WitnessV0KeyHash(vSolutions[0]));
}
}
- uint256 hash;
- CSHA256().Write(&redeemscript[0], redeemscript.size()).Finalize(hash.begin());
- return GetScriptForDestination(WitnessV0ScriptHash(hash));
+ return GetScriptForDestination(WitnessV0ScriptHash(redeemscript));
}
bool IsValidDestination(const CTxDestination& dest) {
diff --git a/src/script/standard.h b/src/script/standard.h
index 4922b7236b..1380030871 100644
--- a/src/script/standard.h
+++ b/src/script/standard.h
@@ -77,6 +77,7 @@ struct WitnessV0ScriptHash : public uint256
{
WitnessV0ScriptHash() : uint256() {}
explicit WitnessV0ScriptHash(const uint256& hash) : uint256(hash) {}
+ explicit WitnessV0ScriptHash(const CScript& script);
using uint256::uint256;
};
diff --git a/src/serialize.h b/src/serialize.h
index e54c7483d2..df3b47ba87 100644
--- a/src/serialize.h
+++ b/src/serialize.h
@@ -189,7 +189,9 @@ template<typename X> const X& ReadWriteAsHelper(const X& x) { return x; }
SerializationOp(s, CSerActionUnserialize()); \
}
+#ifndef CHAR_EQUALS_INT8
template<typename Stream> inline void Serialize(Stream& s, char a ) { ser_writedata8(s, a); } // TODO Get rid of bare char
+#endif
template<typename Stream> inline void Serialize(Stream& s, int8_t a ) { ser_writedata8(s, a); }
template<typename Stream> inline void Serialize(Stream& s, uint8_t a ) { ser_writedata8(s, a); }
template<typename Stream> inline void Serialize(Stream& s, int16_t a ) { ser_writedata16(s, a); }
@@ -205,7 +207,9 @@ template<typename Stream, int N> inline void Serialize(Stream& s, const unsigned
template<typename Stream> inline void Serialize(Stream& s, const Span<const unsigned char>& span) { s.write(CharCast(span.data()), span.size()); }
template<typename Stream> inline void Serialize(Stream& s, const Span<unsigned char>& span) { s.write(CharCast(span.data()), span.size()); }
+#ifndef CHAR_EQUALS_INT8
template<typename Stream> inline void Unserialize(Stream& s, char& a ) { a = ser_readdata8(s); } // TODO Get rid of bare char
+#endif
template<typename Stream> inline void Unserialize(Stream& s, int8_t& a ) { a = ser_readdata8(s); }
template<typename Stream> inline void Unserialize(Stream& s, uint8_t& a ) { a = ser_readdata8(s); }
template<typename Stream> inline void Unserialize(Stream& s, int16_t& a ) { a = ser_readdata16(s); }
diff --git a/src/shutdown.cpp b/src/shutdown.cpp
new file mode 100644
index 0000000000..dec497d8ec
--- /dev/null
+++ b/src/shutdown.cpp
@@ -0,0 +1,23 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2018 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 <shutdown.h>
+
+#include <atomic>
+
+static std::atomic<bool> fRequestShutdown(false);
+
+void StartShutdown()
+{
+ fRequestShutdown = true;
+}
+void AbortShutdown()
+{
+ fRequestShutdown = false;
+}
+bool ShutdownRequested()
+{
+ return fRequestShutdown;
+}
diff --git a/src/shutdown.h b/src/shutdown.h
new file mode 100644
index 0000000000..3ed851c789
--- /dev/null
+++ b/src/shutdown.h
@@ -0,0 +1,13 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2018 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_SHUTDOWN_H
+#define BITCOIN_SHUTDOWN_H
+
+void StartShutdown();
+void AbortShutdown();
+bool ShutdownRequested();
+
+#endif
diff --git a/src/test/cuckoocache_tests.cpp b/src/test/cuckoocache_tests.cpp
index 857ab8a1b7..2edc7c16d4 100644
--- a/src/test/cuckoocache_tests.cpp
+++ b/src/test/cuckoocache_tests.cpp
@@ -114,7 +114,7 @@ static double normalize_hit_rate(double hits, double load)
return hits * std::max(load, 1.0);
}
-/** Check the hit rate on loads ranging from 0.1 to 2.0 */
+/** Check the hit rate on loads ranging from 0.1 to 1.6 */
BOOST_AUTO_TEST_CASE(cuckoocache_hit_rate_ok)
{
/** Arbitrarily selected Hit Rate threshold that happens to work for this test
diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp
index 6df5aec9c9..fac7418cba 100644
--- a/src/test/dbwrapper_tests.cpp
+++ b/src/test/dbwrapper_tests.cpp
@@ -27,7 +27,7 @@ BOOST_AUTO_TEST_CASE(dbwrapper)
{
// Perform tests both obfuscated and non-obfuscated.
for (bool obfuscate : {false, true}) {
- fs::path ph = fs::temp_directory_path() / fs::unique_path();
+ fs::path ph = SetDataDir(std::string("dbwrapper").append(obfuscate ? "_true" : "_false"));
CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate);
char key = 'k';
uint256 in = InsecureRand256();
@@ -47,7 +47,7 @@ BOOST_AUTO_TEST_CASE(dbwrapper_batch)
{
// Perform tests both obfuscated and non-obfuscated.
for (bool obfuscate : {false, true}) {
- fs::path ph = fs::temp_directory_path() / fs::unique_path();
+ fs::path ph = SetDataDir(std::string("dbwrapper_batch").append(obfuscate ? "_true" : "_false"));
CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate);
char key = 'i';
@@ -83,7 +83,7 @@ BOOST_AUTO_TEST_CASE(dbwrapper_iterator)
{
// Perform tests both obfuscated and non-obfuscated.
for (bool obfuscate : {false, true}) {
- fs::path ph = fs::temp_directory_path() / fs::unique_path();
+ fs::path ph = SetDataDir(std::string("dbwrapper_iterator").append(obfuscate ? "_true" : "_false"));
CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate);
// The two keys are intentionally chosen for ordering
@@ -123,7 +123,7 @@ BOOST_AUTO_TEST_CASE(dbwrapper_iterator)
BOOST_AUTO_TEST_CASE(existing_data_no_obfuscate)
{
// We're going to share this fs::path between two wrappers
- fs::path ph = fs::temp_directory_path() / fs::unique_path();
+ fs::path ph = SetDataDir("existing_data_no_obfuscate");
create_directories(ph);
// Set up a non-obfuscated wrapper to write some initial data.
@@ -164,7 +164,7 @@ BOOST_AUTO_TEST_CASE(existing_data_no_obfuscate)
BOOST_AUTO_TEST_CASE(existing_data_reindex)
{
// We're going to share this fs::path between two wrappers
- fs::path ph = fs::temp_directory_path() / fs::unique_path();
+ fs::path ph = SetDataDir("existing_data_reindex");
create_directories(ph);
// Set up a non-obfuscated wrapper to write some initial data.
@@ -199,7 +199,7 @@ BOOST_AUTO_TEST_CASE(existing_data_reindex)
BOOST_AUTO_TEST_CASE(iterator_ordering)
{
- fs::path ph = fs::temp_directory_path() / fs::unique_path();
+ fs::path ph = SetDataDir("iterator_ordering");
CDBWrapper dbw(ph, (1 << 20), true, false, false);
for (int x=0x00; x<256; ++x) {
uint8_t key = x;
@@ -277,7 +277,7 @@ BOOST_AUTO_TEST_CASE(iterator_string_ordering)
{
char buf[10];
- fs::path ph = fs::temp_directory_path() / fs::unique_path();
+ fs::path ph = SetDataDir("iterator_string_ordering");
CDBWrapper dbw(ph, (1 << 20), true, false, false);
for (int x=0x00; x<10; ++x) {
for (int y = 0; y < 10; y++) {
diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp
index e5f914ba8a..49037adb9a 100644
--- a/src/test/denialofservice_tests.cpp
+++ b/src/test/denialofservice_tests.cpp
@@ -24,6 +24,8 @@
extern bool AddOrphanTx(const CTransactionRef& tx, NodeId peer);
extern void EraseOrphansFor(NodeId peer);
extern unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans);
+extern void Misbehaving(NodeId nodeid, int howmuch, const std::string& message="");
+
struct COrphanTx {
CTransactionRef tx;
NodeId fromPeer;
@@ -54,7 +56,6 @@ BOOST_FIXTURE_TEST_SUITE(denialofservice_tests, TestingSetup)
// work.
BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
{
- std::atomic<bool> interruptDummy(false);
// Mock an outbound peer
CAddress addr1(ip(0xa0b0c001), NODE_NONE);
@@ -66,25 +67,40 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
dummyNode1.fSuccessfullyConnected = true;
// This test requires that we have a chain with non-zero work.
- LOCK(cs_main);
- BOOST_CHECK(chainActive.Tip() != nullptr);
- BOOST_CHECK(chainActive.Tip()->nChainWork > 0);
+ {
+ LOCK(cs_main);
+ BOOST_CHECK(chainActive.Tip() != nullptr);
+ BOOST_CHECK(chainActive.Tip()->nChainWork > 0);
+ }
// Test starts here
- LOCK(dummyNode1.cs_sendProcessing);
- peerLogic->SendMessages(&dummyNode1, interruptDummy); // should result in getheaders
- LOCK(dummyNode1.cs_vSend);
- BOOST_CHECK(dummyNode1.vSendMsg.size() > 0);
- dummyNode1.vSendMsg.clear();
+ {
+ LOCK2(cs_main, dummyNode1.cs_sendProcessing);
+ peerLogic->SendMessages(&dummyNode1); // should result in getheaders
+ }
+ {
+ LOCK2(cs_main, dummyNode1.cs_vSend);
+ BOOST_CHECK(dummyNode1.vSendMsg.size() > 0);
+ dummyNode1.vSendMsg.clear();
+ }
int64_t nStartTime = GetTime();
// Wait 21 minutes
SetMockTime(nStartTime+21*60);
- peerLogic->SendMessages(&dummyNode1, interruptDummy); // should result in getheaders
- BOOST_CHECK(dummyNode1.vSendMsg.size() > 0);
+ {
+ LOCK2(cs_main, dummyNode1.cs_sendProcessing);
+ peerLogic->SendMessages(&dummyNode1); // should result in getheaders
+ }
+ {
+ LOCK2(cs_main, dummyNode1.cs_vSend);
+ BOOST_CHECK(dummyNode1.vSendMsg.size() > 0);
+ }
// Wait 3 more minutes
SetMockTime(nStartTime+24*60);
- peerLogic->SendMessages(&dummyNode1, interruptDummy); // should result in disconnect
+ {
+ LOCK2(cs_main, dummyNode1.cs_sendProcessing);
+ peerLogic->SendMessages(&dummyNode1); // should result in disconnect
+ }
BOOST_CHECK(dummyNode1.fDisconnect == true);
SetMockTime(0);
@@ -177,7 +193,6 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
BOOST_AUTO_TEST_CASE(DoS_banning)
{
- std::atomic<bool> interruptDummy(false);
connman->ClearBanned();
CAddress addr1(ip(0xa0b0c001), NODE_NONE);
@@ -190,8 +205,10 @@ BOOST_AUTO_TEST_CASE(DoS_banning)
LOCK(cs_main);
Misbehaving(dummyNode1.GetId(), 100); // Should get banned
}
- LOCK(dummyNode1.cs_sendProcessing);
- peerLogic->SendMessages(&dummyNode1, interruptDummy);
+ {
+ LOCK2(cs_main, dummyNode1.cs_sendProcessing);
+ peerLogic->SendMessages(&dummyNode1);
+ }
BOOST_CHECK(connman->IsBanned(addr1));
BOOST_CHECK(!connman->IsBanned(ip(0xa0b0c001|0x0000ff00))); // Different IP, not banned
@@ -205,15 +222,20 @@ BOOST_AUTO_TEST_CASE(DoS_banning)
LOCK(cs_main);
Misbehaving(dummyNode2.GetId(), 50);
}
- LOCK(dummyNode2.cs_sendProcessing);
- peerLogic->SendMessages(&dummyNode2, interruptDummy);
+ {
+ LOCK2(cs_main, dummyNode2.cs_sendProcessing);
+ peerLogic->SendMessages(&dummyNode2);
+ }
BOOST_CHECK(!connman->IsBanned(addr2)); // 2 not banned yet...
BOOST_CHECK(connman->IsBanned(addr1)); // ... but 1 still should be
{
LOCK(cs_main);
Misbehaving(dummyNode2.GetId(), 50);
}
- peerLogic->SendMessages(&dummyNode2, interruptDummy);
+ {
+ LOCK2(cs_main, dummyNode2.cs_sendProcessing);
+ peerLogic->SendMessages(&dummyNode2);
+ }
BOOST_CHECK(connman->IsBanned(addr2));
bool dummy;
@@ -223,7 +245,6 @@ BOOST_AUTO_TEST_CASE(DoS_banning)
BOOST_AUTO_TEST_CASE(DoS_banscore)
{
- std::atomic<bool> interruptDummy(false);
connman->ClearBanned();
gArgs.ForceSetArg("-banscore", "111"); // because 11 is my favorite number
@@ -237,20 +258,28 @@ BOOST_AUTO_TEST_CASE(DoS_banscore)
LOCK(cs_main);
Misbehaving(dummyNode1.GetId(), 100);
}
- LOCK(dummyNode1.cs_sendProcessing);
- peerLogic->SendMessages(&dummyNode1, interruptDummy);
+ {
+ LOCK2(cs_main, dummyNode1.cs_sendProcessing);
+ peerLogic->SendMessages(&dummyNode1);
+ }
BOOST_CHECK(!connman->IsBanned(addr1));
{
LOCK(cs_main);
Misbehaving(dummyNode1.GetId(), 10);
}
- peerLogic->SendMessages(&dummyNode1, interruptDummy);
+ {
+ LOCK2(cs_main, dummyNode1.cs_sendProcessing);
+ peerLogic->SendMessages(&dummyNode1);
+ }
BOOST_CHECK(!connman->IsBanned(addr1));
{
LOCK(cs_main);
Misbehaving(dummyNode1.GetId(), 1);
}
- peerLogic->SendMessages(&dummyNode1, interruptDummy);
+ {
+ LOCK2(cs_main, dummyNode1.cs_sendProcessing);
+ peerLogic->SendMessages(&dummyNode1);
+ }
BOOST_CHECK(connman->IsBanned(addr1));
gArgs.ForceSetArg("-banscore", std::to_string(DEFAULT_BANSCORE_THRESHOLD));
@@ -260,7 +289,6 @@ BOOST_AUTO_TEST_CASE(DoS_banscore)
BOOST_AUTO_TEST_CASE(DoS_bantime)
{
- std::atomic<bool> interruptDummy(false);
connman->ClearBanned();
int64_t nStartTime = GetTime();
@@ -277,8 +305,10 @@ BOOST_AUTO_TEST_CASE(DoS_bantime)
LOCK(cs_main);
Misbehaving(dummyNode.GetId(), 100);
}
- LOCK(dummyNode.cs_sendProcessing);
- peerLogic->SendMessages(&dummyNode, interruptDummy);
+ {
+ LOCK2(cs_main, dummyNode.cs_sendProcessing);
+ peerLogic->SendMessages(&dummyNode);
+ }
BOOST_CHECK(connman->IsBanned(addr));
SetMockTime(nStartTime+60*60);
diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp
index 42e615ab0c..2a77e0e1ec 100644
--- a/src/test/net_tests.cpp
+++ b/src/test/net_tests.cpp
@@ -89,6 +89,7 @@ BOOST_AUTO_TEST_CASE(cnode_listen_port)
BOOST_AUTO_TEST_CASE(caddrdb_read)
{
+ SetDataDir("caddrdb_read");
CAddrManUncorrupted addrmanUncorrupted;
addrmanUncorrupted.MakeDeterministic();
@@ -134,6 +135,7 @@ BOOST_AUTO_TEST_CASE(caddrdb_read)
BOOST_AUTO_TEST_CASE(caddrdb_read_corrupted)
{
+ SetDataDir("caddrdb_read_corrupted");
CAddrManCorrupted addrmanCorrupted;
addrmanCorrupted.MakeDeterministic();
diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp
index 027214e512..bc90e5ae09 100644
--- a/src/test/netbase_tests.cpp
+++ b/src/test/netbase_tests.cpp
@@ -39,7 +39,7 @@ BOOST_AUTO_TEST_CASE(netbase_networks)
BOOST_CHECK(ResolveIP("::1").GetNetwork() == NET_UNROUTABLE);
BOOST_CHECK(ResolveIP("8.8.8.8").GetNetwork() == NET_IPV4);
BOOST_CHECK(ResolveIP("2001::8888").GetNetwork() == NET_IPV6);
- BOOST_CHECK(ResolveIP("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").GetNetwork() == NET_TOR);
+ BOOST_CHECK(ResolveIP("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").GetNetwork() == NET_ONION);
BOOST_CHECK(CreateInternal("foo.com").GetNetwork() == NET_INTERNAL);
}
@@ -293,7 +293,7 @@ BOOST_AUTO_TEST_CASE(netbase_getgroup)
BOOST_CHECK(ResolveIP("64:FF9B::102:304").GetGroup() == std::vector<unsigned char>({(unsigned char)NET_IPV4, 1, 2})); // RFC6052
BOOST_CHECK(ResolveIP("2002:102:304:9999:9999:9999:9999:9999").GetGroup() == std::vector<unsigned char>({(unsigned char)NET_IPV4, 1, 2})); // RFC3964
BOOST_CHECK(ResolveIP("2001:0:9999:9999:9999:9999:FEFD:FCFB").GetGroup() == std::vector<unsigned char>({(unsigned char)NET_IPV4, 1, 2})); // RFC4380
- BOOST_CHECK(ResolveIP("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").GetGroup() == std::vector<unsigned char>({(unsigned char)NET_TOR, 239})); // Tor
+ BOOST_CHECK(ResolveIP("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").GetGroup() == std::vector<unsigned char>({(unsigned char)NET_ONION, 239})); // Tor
BOOST_CHECK(ResolveIP("2001:470:abcd:9999:9999:9999:9999:9999").GetGroup() == std::vector<unsigned char>({(unsigned char)NET_IPV6, 32, 1, 4, 112, 175})); //he.net
BOOST_CHECK(ResolveIP("2001:2001:9999:9999:9999:9999:9999:9999").GetGroup() == std::vector<unsigned char>({(unsigned char)NET_IPV6, 32, 1, 32, 1})); //IPv6
diff --git a/src/test/script_p2sh_tests.cpp b/src/test/script_p2sh_tests.cpp
index 803a673fab..e224df6704 100644
--- a/src/test/script_p2sh_tests.cpp
+++ b/src/test/script_p2sh_tests.cpp
@@ -309,7 +309,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard)
// vout[5/6] are non-standard because they exceed MAX_P2SH_SIGOPS
CScript sixteenSigops; sixteenSigops << OP_16 << OP_CHECKMULTISIG;
keystore.AddCScript(sixteenSigops);
- txFrom.vout[5].scriptPubKey = GetScriptForDestination(CScriptID(fifteenSigops));
+ txFrom.vout[5].scriptPubKey = GetScriptForDestination(CScriptID(sixteenSigops));
txFrom.vout[5].nValue = 5000;
CScript twentySigops; twentySigops << OP_CHECKMULTISIG;
keystore.AddCScript(twentySigops);
diff --git a/src/test/script_standard_tests.cpp b/src/test/script_standard_tests.cpp
index 7ab0978228..7d4734986a 100644
--- a/src/test/script_standard_tests.cpp
+++ b/src/test/script_standard_tests.cpp
@@ -398,106 +398,149 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
CScript scriptPubKey;
isminetype result;
- bool isInvalid;
// P2PK compressed
{
CBasicKeyStore keystore;
- scriptPubKey.clear();
- scriptPubKey << ToByteVector(pubkeys[0]) << OP_CHECKSIG;
+ scriptPubKey = GetScriptForRawPubKey(pubkeys[0]);
// Keystore does not have key
- result = IsMine(keystore, scriptPubKey, isInvalid);
+ result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_NO);
- BOOST_CHECK(!isInvalid);
// Keystore has key
keystore.AddKey(keys[0]);
- result = IsMine(keystore, scriptPubKey, isInvalid);
+ result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
- BOOST_CHECK(!isInvalid);
}
// P2PK uncompressed
{
CBasicKeyStore keystore;
- scriptPubKey.clear();
- scriptPubKey << ToByteVector(uncompressedPubkey) << OP_CHECKSIG;
+ scriptPubKey = GetScriptForRawPubKey(uncompressedPubkey);
// Keystore does not have key
- result = IsMine(keystore, scriptPubKey, isInvalid);
+ result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_NO);
- BOOST_CHECK(!isInvalid);
// Keystore has key
keystore.AddKey(uncompressedKey);
- result = IsMine(keystore, scriptPubKey, isInvalid);
+ result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
- BOOST_CHECK(!isInvalid);
}
// P2PKH compressed
{
CBasicKeyStore keystore;
- scriptPubKey.clear();
- scriptPubKey << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG;
+ scriptPubKey = GetScriptForDestination(pubkeys[0].GetID());
// Keystore does not have key
- result = IsMine(keystore, scriptPubKey, isInvalid);
+ result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_NO);
- BOOST_CHECK(!isInvalid);
// Keystore has key
keystore.AddKey(keys[0]);
- result = IsMine(keystore, scriptPubKey, isInvalid);
+ result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
- BOOST_CHECK(!isInvalid);
}
// P2PKH uncompressed
{
CBasicKeyStore keystore;
- scriptPubKey.clear();
- scriptPubKey << OP_DUP << OP_HASH160 << ToByteVector(uncompressedPubkey.GetID()) << OP_EQUALVERIFY << OP_CHECKSIG;
+ scriptPubKey = GetScriptForDestination(uncompressedPubkey.GetID());
// Keystore does not have key
- result = IsMine(keystore, scriptPubKey, isInvalid);
+ result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_NO);
- BOOST_CHECK(!isInvalid);
// Keystore has key
keystore.AddKey(uncompressedKey);
- result = IsMine(keystore, scriptPubKey, isInvalid);
+ result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
- BOOST_CHECK(!isInvalid);
}
// P2SH
{
CBasicKeyStore keystore;
- CScript redeemScript;
- redeemScript << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG;
-
- scriptPubKey.clear();
- scriptPubKey << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL;
+ CScript redeemScript = GetScriptForDestination(pubkeys[0].GetID());
+ scriptPubKey = GetScriptForDestination(CScriptID(redeemScript));
// Keystore does not have redeemScript or key
- result = IsMine(keystore, scriptPubKey, isInvalid);
+ result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_NO);
- BOOST_CHECK(!isInvalid);
// Keystore has redeemScript but no key
keystore.AddCScript(redeemScript);
- result = IsMine(keystore, scriptPubKey, isInvalid);
+ result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_NO);
- BOOST_CHECK(!isInvalid);
// Keystore has redeemScript and key
keystore.AddKey(keys[0]);
- result = IsMine(keystore, scriptPubKey, isInvalid);
+ result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
- BOOST_CHECK(!isInvalid);
+ }
+
+ // (P2PKH inside) P2SH inside P2SH (invalid)
+ {
+ CBasicKeyStore keystore;
+
+ CScript redeemscript_inner = GetScriptForDestination(pubkeys[0].GetID());
+ CScript redeemscript = GetScriptForDestination(CScriptID(redeemscript_inner));
+ scriptPubKey = GetScriptForDestination(CScriptID(redeemscript));
+
+ keystore.AddCScript(redeemscript);
+ keystore.AddCScript(redeemscript_inner);
+ keystore.AddCScript(scriptPubKey);
+ keystore.AddKey(keys[0]);
+ result = IsMine(keystore, scriptPubKey);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+ }
+
+ // (P2PKH inside) P2SH inside P2WSH (invalid)
+ {
+ CBasicKeyStore keystore;
+
+ CScript redeemscript = GetScriptForDestination(pubkeys[0].GetID());
+ CScript witnessscript = GetScriptForDestination(CScriptID(redeemscript));
+ scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessscript));
+
+ keystore.AddCScript(witnessscript);
+ keystore.AddCScript(redeemscript);
+ keystore.AddCScript(scriptPubKey);
+ keystore.AddKey(keys[0]);
+ result = IsMine(keystore, scriptPubKey);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+ }
+
+ // P2WPKH inside P2WSH (invalid)
+ {
+ CBasicKeyStore keystore;
+
+ CScript witnessscript = GetScriptForDestination(WitnessV0KeyHash(pubkeys[0].GetID()));
+ scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessscript));
+
+ keystore.AddCScript(witnessscript);
+ keystore.AddCScript(scriptPubKey);
+ keystore.AddKey(keys[0]);
+ result = IsMine(keystore, scriptPubKey);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+ }
+
+ // (P2PKH inside) P2WSH inside P2WSH (invalid)
+ {
+ CBasicKeyStore keystore;
+
+ CScript witnessscript_inner = GetScriptForDestination(pubkeys[0].GetID());
+ CScript witnessscript = GetScriptForDestination(WitnessV0ScriptHash(witnessscript_inner));
+ scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessscript));
+
+ keystore.AddCScript(witnessscript_inner);
+ keystore.AddCScript(witnessscript);
+ keystore.AddCScript(scriptPubKey);
+ keystore.AddKey(keys[0]);
+ result = IsMine(keystore, scriptPubKey);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
}
// P2WPKH compressed
@@ -505,14 +548,12 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
CBasicKeyStore keystore;
keystore.AddKey(keys[0]);
- scriptPubKey.clear();
- scriptPubKey << OP_0 << ToByteVector(pubkeys[0].GetID());
+ scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(pubkeys[0].GetID()));
// Keystore implicitly has key and P2SH redeemScript
keystore.AddCScript(scriptPubKey);
- result = IsMine(keystore, scriptPubKey, isInvalid);
+ result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
- BOOST_CHECK(!isInvalid);
}
// P2WPKH uncompressed
@@ -520,56 +561,45 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
CBasicKeyStore keystore;
keystore.AddKey(uncompressedKey);
- scriptPubKey.clear();
- scriptPubKey << OP_0 << ToByteVector(uncompressedPubkey.GetID());
+ scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(uncompressedPubkey.GetID()));
// Keystore has key, but no P2SH redeemScript
- result = IsMine(keystore, scriptPubKey, isInvalid);
+ result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_NO);
- BOOST_CHECK(!isInvalid);
// Keystore has key and P2SH redeemScript
keystore.AddCScript(scriptPubKey);
- result = IsMine(keystore, scriptPubKey, isInvalid);
+ result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_NO);
- BOOST_CHECK(isInvalid);
}
// scriptPubKey multisig
{
CBasicKeyStore keystore;
- scriptPubKey.clear();
- scriptPubKey << OP_2 <<
- ToByteVector(uncompressedPubkey) <<
- ToByteVector(pubkeys[1]) <<
- OP_2 << OP_CHECKMULTISIG;
+ scriptPubKey = GetScriptForMultisig(2, {uncompressedPubkey, pubkeys[1]});
// Keystore does not have any keys
- result = IsMine(keystore, scriptPubKey, isInvalid);
+ result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_NO);
- BOOST_CHECK(!isInvalid);
// Keystore has 1/2 keys
keystore.AddKey(uncompressedKey);
- result = IsMine(keystore, scriptPubKey, isInvalid);
+ result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_NO);
- BOOST_CHECK(!isInvalid);
// Keystore has 2/2 keys
keystore.AddKey(keys[1]);
- result = IsMine(keystore, scriptPubKey, isInvalid);
+ result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_NO);
- BOOST_CHECK(!isInvalid);
// Keystore has 2/2 keys and the script
keystore.AddCScript(scriptPubKey);
- result = IsMine(keystore, scriptPubKey, isInvalid);
+ result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_NO);
- BOOST_CHECK(!isInvalid);
}
// P2SH multisig
@@ -578,25 +608,17 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
keystore.AddKey(uncompressedKey);
keystore.AddKey(keys[1]);
- CScript redeemScript;
- redeemScript << OP_2 <<
- ToByteVector(uncompressedPubkey) <<
- ToByteVector(pubkeys[1]) <<
- OP_2 << OP_CHECKMULTISIG;
-
- scriptPubKey.clear();
- scriptPubKey << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL;
+ CScript redeemScript = GetScriptForMultisig(2, {uncompressedPubkey, pubkeys[1]});
+ scriptPubKey = GetScriptForDestination(CScriptID(redeemScript));
// Keystore has no redeemScript
- result = IsMine(keystore, scriptPubKey, isInvalid);
+ result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_NO);
- BOOST_CHECK(!isInvalid);
// Keystore has redeemScript
keystore.AddCScript(redeemScript);
- result = IsMine(keystore, scriptPubKey, isInvalid);
+ result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
- BOOST_CHECK(!isInvalid);
}
// P2WSH multisig with compressed keys
@@ -605,35 +627,22 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
keystore.AddKey(keys[0]);
keystore.AddKey(keys[1]);
- CScript witnessScript;
- witnessScript << OP_2 <<
- ToByteVector(pubkeys[0]) <<
- ToByteVector(pubkeys[1]) <<
- OP_2 << OP_CHECKMULTISIG;
-
- uint256 scriptHash;
- CSHA256().Write(&witnessScript[0], witnessScript.size())
- .Finalize(scriptHash.begin());
-
- scriptPubKey.clear();
- scriptPubKey << OP_0 << ToByteVector(scriptHash);
+ CScript witnessScript = GetScriptForMultisig(2, {pubkeys[0], pubkeys[1]});
+ scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessScript));
// Keystore has keys, but no witnessScript or P2SH redeemScript
- result = IsMine(keystore, scriptPubKey, isInvalid);
+ result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_NO);
- BOOST_CHECK(!isInvalid);
// Keystore has keys and witnessScript, but no P2SH redeemScript
keystore.AddCScript(witnessScript);
- result = IsMine(keystore, scriptPubKey, isInvalid);
+ result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_NO);
- BOOST_CHECK(!isInvalid);
// Keystore has keys, witnessScript, P2SH redeemScript
keystore.AddCScript(scriptPubKey);
- result = IsMine(keystore, scriptPubKey, isInvalid);
+ result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
- BOOST_CHECK(!isInvalid);
}
// P2WSH multisig with uncompressed key
@@ -642,75 +651,47 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
keystore.AddKey(uncompressedKey);
keystore.AddKey(keys[1]);
- CScript witnessScript;
- witnessScript << OP_2 <<
- ToByteVector(uncompressedPubkey) <<
- ToByteVector(pubkeys[1]) <<
- OP_2 << OP_CHECKMULTISIG;
-
- uint256 scriptHash;
- CSHA256().Write(&witnessScript[0], witnessScript.size())
- .Finalize(scriptHash.begin());
-
- scriptPubKey.clear();
- scriptPubKey << OP_0 << ToByteVector(scriptHash);
+ CScript witnessScript = GetScriptForMultisig(2, {uncompressedPubkey, pubkeys[1]});
+ scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessScript));
// Keystore has keys, but no witnessScript or P2SH redeemScript
- result = IsMine(keystore, scriptPubKey, isInvalid);
+ result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_NO);
- BOOST_CHECK(!isInvalid);
// Keystore has keys and witnessScript, but no P2SH redeemScript
keystore.AddCScript(witnessScript);
- result = IsMine(keystore, scriptPubKey, isInvalid);
+ result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_NO);
- BOOST_CHECK(!isInvalid);
// Keystore has keys, witnessScript, P2SH redeemScript
keystore.AddCScript(scriptPubKey);
- result = IsMine(keystore, scriptPubKey, isInvalid);
+ result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_NO);
- BOOST_CHECK(isInvalid);
}
// P2WSH multisig wrapped in P2SH
{
CBasicKeyStore keystore;
- CScript witnessScript;
- witnessScript << OP_2 <<
- ToByteVector(pubkeys[0]) <<
- ToByteVector(pubkeys[1]) <<
- OP_2 << OP_CHECKMULTISIG;
-
- uint256 scriptHash;
- CSHA256().Write(&witnessScript[0], witnessScript.size())
- .Finalize(scriptHash.begin());
-
- CScript redeemScript;
- redeemScript << OP_0 << ToByteVector(scriptHash);
-
- scriptPubKey.clear();
- scriptPubKey << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL;
+ CScript witnessScript = GetScriptForMultisig(2, {pubkeys[0], pubkeys[1]});
+ CScript redeemScript = GetScriptForDestination(WitnessV0ScriptHash(witnessScript));
+ scriptPubKey = GetScriptForDestination(CScriptID(redeemScript));
// Keystore has no witnessScript, P2SH redeemScript, or keys
- result = IsMine(keystore, scriptPubKey, isInvalid);
+ result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_NO);
- BOOST_CHECK(!isInvalid);
// Keystore has witnessScript and P2SH redeemScript, but no keys
keystore.AddCScript(redeemScript);
keystore.AddCScript(witnessScript);
- result = IsMine(keystore, scriptPubKey, isInvalid);
+ result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_NO);
- BOOST_CHECK(!isInvalid);
// Keystore has keys, witnessScript, P2SH redeemScript
keystore.AddKey(keys[0]);
keystore.AddKey(keys[1]);
- result = IsMine(keystore, scriptPubKey, isInvalid);
+ result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
- BOOST_CHECK(!isInvalid);
}
// OP_RETURN
@@ -721,9 +702,8 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
scriptPubKey.clear();
scriptPubKey << OP_RETURN << ToByteVector(pubkeys[0]);
- result = IsMine(keystore, scriptPubKey, isInvalid);
+ result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_NO);
- BOOST_CHECK(!isInvalid);
}
// witness unspendable
@@ -734,9 +714,8 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
scriptPubKey.clear();
scriptPubKey << OP_0 << ToByteVector(ParseHex("aabb"));
- result = IsMine(keystore, scriptPubKey, isInvalid);
+ result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_NO);
- BOOST_CHECK(!isInvalid);
}
// witness unknown
@@ -747,9 +726,8 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
scriptPubKey.clear();
scriptPubKey << OP_16 << ToByteVector(ParseHex("aabb"));
- result = IsMine(keystore, scriptPubKey, isInvalid);
+ result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_NO);
- BOOST_CHECK(!isInvalid);
}
// Nonstandard
@@ -760,9 +738,8 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
scriptPubKey.clear();
scriptPubKey << OP_9 << OP_ADD << OP_11 << OP_EQUAL;
- result = IsMine(keystore, scriptPubKey, isInvalid);
+ result = IsMine(keystore, scriptPubKey);
BOOST_CHECK_EQUAL(result, ISMINE_NO);
- BOOST_CHECK(!isInvalid);
}
}
diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp
index c05e60996d..543b21e8a6 100644
--- a/src/test/script_tests.cpp
+++ b/src/test/script_tests.cpp
@@ -1161,10 +1161,19 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23)
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_INVALID_STACK_OPERATION, ScriptErrorString(err));
}
+/* Wrapper around ProduceSignature to combine two scriptsigs */
+SignatureData CombineSignatures(const CTxOut& txout, const CMutableTransaction& tx, const SignatureData& scriptSig1, const SignatureData& scriptSig2)
+{
+ SignatureData data;
+ data.MergeSignatureData(scriptSig1);
+ data.MergeSignatureData(scriptSig2);
+ ProduceSignature(DUMMY_SIGNING_PROVIDER, MutableTransactionSignatureCreator(&tx, 0, txout.nValue), txout.scriptPubKey, data);
+ return data;
+}
+
BOOST_AUTO_TEST_CASE(script_combineSigs)
{
- // Test the CombineSignatures function
- CAmount amount = 0;
+ // Test the ProduceSignature's ability to combine signatures function
CBasicKeyStore keystore;
std::vector<CKey> keys;
std::vector<CPubKey> pubkeys;
@@ -1180,52 +1189,51 @@ BOOST_AUTO_TEST_CASE(script_combineSigs)
CMutableTransaction txFrom = BuildCreditingTransaction(GetScriptForDestination(keys[0].GetPubKey().GetID()));
CMutableTransaction txTo = BuildSpendingTransaction(CScript(), CScriptWitness(), txFrom);
CScript& scriptPubKey = txFrom.vout[0].scriptPubKey;
- CScript& scriptSig = txTo.vin[0].scriptSig;
+ SignatureData scriptSig;
SignatureData empty;
- SignatureData combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), empty, empty);
+ SignatureData combined = CombineSignatures(txFrom.vout[0], txTo, empty, empty);
BOOST_CHECK(combined.scriptSig.empty());
// Single signature case:
SignSignature(keystore, txFrom, txTo, 0, SIGHASH_ALL); // changes scriptSig
- combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(scriptSig), empty);
- BOOST_CHECK(combined.scriptSig == scriptSig);
- combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), empty, SignatureData(scriptSig));
- BOOST_CHECK(combined.scriptSig == scriptSig);
- CScript scriptSigCopy = scriptSig;
+ scriptSig = DataFromTransaction(txTo, 0, txFrom.vout[0]);
+ combined = CombineSignatures(txFrom.vout[0], txTo, scriptSig, empty);
+ BOOST_CHECK(combined.scriptSig == scriptSig.scriptSig);
+ combined = CombineSignatures(txFrom.vout[0], txTo, empty, scriptSig);
+ BOOST_CHECK(combined.scriptSig == scriptSig.scriptSig);
+ SignatureData scriptSigCopy = scriptSig;
// Signing again will give a different, valid signature:
SignSignature(keystore, txFrom, txTo, 0, SIGHASH_ALL);
- combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(scriptSigCopy), SignatureData(scriptSig));
- BOOST_CHECK(combined.scriptSig == scriptSigCopy || combined.scriptSig == scriptSig);
+ scriptSig = DataFromTransaction(txTo, 0, txFrom.vout[0]);
+ combined = CombineSignatures(txFrom.vout[0], txTo, scriptSigCopy, scriptSig);
+ BOOST_CHECK(combined.scriptSig == scriptSigCopy.scriptSig || combined.scriptSig == scriptSig.scriptSig);
// P2SH, single-signature case:
CScript pkSingle; pkSingle << ToByteVector(keys[0].GetPubKey()) << OP_CHECKSIG;
keystore.AddCScript(pkSingle);
scriptPubKey = GetScriptForDestination(CScriptID(pkSingle));
SignSignature(keystore, txFrom, txTo, 0, SIGHASH_ALL);
- combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(scriptSig), empty);
- BOOST_CHECK(combined.scriptSig == scriptSig);
- combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), empty, SignatureData(scriptSig));
- BOOST_CHECK(combined.scriptSig == scriptSig);
+ scriptSig = DataFromTransaction(txTo, 0, txFrom.vout[0]);
+ combined = CombineSignatures(txFrom.vout[0], txTo, scriptSig, empty);
+ BOOST_CHECK(combined.scriptSig == scriptSig.scriptSig);
+ combined = CombineSignatures(txFrom.vout[0], txTo, empty, scriptSig);
+ BOOST_CHECK(combined.scriptSig == scriptSig.scriptSig);
scriptSigCopy = scriptSig;
SignSignature(keystore, txFrom, txTo, 0, SIGHASH_ALL);
- combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(scriptSigCopy), SignatureData(scriptSig));
- BOOST_CHECK(combined.scriptSig == scriptSigCopy || combined.scriptSig == scriptSig);
- // dummy scriptSigCopy with placeholder, should always choose non-placeholder:
- scriptSigCopy = CScript() << OP_0 << std::vector<unsigned char>(pkSingle.begin(), pkSingle.end());
- combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(scriptSigCopy), SignatureData(scriptSig));
- BOOST_CHECK(combined.scriptSig == scriptSig);
- combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(scriptSig), SignatureData(scriptSigCopy));
- BOOST_CHECK(combined.scriptSig == scriptSig);
+ scriptSig = DataFromTransaction(txTo, 0, txFrom.vout[0]);
+ combined = CombineSignatures(txFrom.vout[0], txTo, scriptSigCopy, scriptSig);
+ BOOST_CHECK(combined.scriptSig == scriptSigCopy.scriptSig || combined.scriptSig == scriptSig.scriptSig);
// Hardest case: Multisig 2-of-3
scriptPubKey = GetScriptForMultisig(2, pubkeys);
keystore.AddCScript(scriptPubKey);
SignSignature(keystore, txFrom, txTo, 0, SIGHASH_ALL);
- combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(scriptSig), empty);
- BOOST_CHECK(combined.scriptSig == scriptSig);
- combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), empty, SignatureData(scriptSig));
- BOOST_CHECK(combined.scriptSig == scriptSig);
+ scriptSig = DataFromTransaction(txTo, 0, txFrom.vout[0]);
+ combined = CombineSignatures(txFrom.vout[0], txTo, scriptSig, empty);
+ BOOST_CHECK(combined.scriptSig == scriptSig.scriptSig);
+ combined = CombineSignatures(txFrom.vout[0], txTo, empty, scriptSig);
+ BOOST_CHECK(combined.scriptSig == scriptSig.scriptSig);
// A couple of partially-signed versions:
std::vector<unsigned char> sig1;
@@ -1252,22 +1260,28 @@ BOOST_AUTO_TEST_CASE(script_combineSigs)
CScript complete12 = CScript() << OP_0 << sig1 << sig2;
CScript complete13 = CScript() << OP_0 << sig1 << sig3;
CScript complete23 = CScript() << OP_0 << sig2 << sig3;
-
- combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial1a), SignatureData(partial1b));
+ SignatureData partial1_sigs;
+ partial1_sigs.signatures.emplace(keys[0].GetPubKey().GetID(), SigPair(keys[0].GetPubKey(), sig1));
+ SignatureData partial2_sigs;
+ partial2_sigs.signatures.emplace(keys[1].GetPubKey().GetID(), SigPair(keys[1].GetPubKey(), sig2));
+ SignatureData partial3_sigs;
+ partial3_sigs.signatures.emplace(keys[2].GetPubKey().GetID(), SigPair(keys[2].GetPubKey(), sig3));
+
+ combined = CombineSignatures(txFrom.vout[0], txTo, partial1_sigs, partial1_sigs);
BOOST_CHECK(combined.scriptSig == partial1a);
- combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial1a), SignatureData(partial2a));
+ combined = CombineSignatures(txFrom.vout[0], txTo, partial1_sigs, partial2_sigs);
BOOST_CHECK(combined.scriptSig == complete12);
- combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial2a), SignatureData(partial1a));
+ combined = CombineSignatures(txFrom.vout[0], txTo, partial2_sigs, partial1_sigs);
BOOST_CHECK(combined.scriptSig == complete12);
- combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial1b), SignatureData(partial2b));
+ combined = CombineSignatures(txFrom.vout[0], txTo, partial1_sigs, partial2_sigs);
BOOST_CHECK(combined.scriptSig == complete12);
- combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial3b), SignatureData(partial1b));
+ combined = CombineSignatures(txFrom.vout[0], txTo, partial3_sigs, partial1_sigs);
BOOST_CHECK(combined.scriptSig == complete13);
- combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial2a), SignatureData(partial3a));
+ combined = CombineSignatures(txFrom.vout[0], txTo, partial2_sigs, partial3_sigs);
BOOST_CHECK(combined.scriptSig == complete23);
- combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial3b), SignatureData(partial2b));
+ combined = CombineSignatures(txFrom.vout[0], txTo, partial3_sigs, partial2_sigs);
BOOST_CHECK(combined.scriptSig == complete23);
- combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial3b), SignatureData(partial3a));
+ combined = CombineSignatures(txFrom.vout[0], txTo, partial3_sigs, partial3_sigs);
BOOST_CHECK(combined.scriptSig == partial3c);
}
@@ -1481,4 +1495,146 @@ BOOST_AUTO_TEST_CASE(script_can_append_self)
BOOST_CHECK(s == d);
}
+
+#if defined(HAVE_CONSENSUS_LIB)
+
+/* Test simple (successful) usage of bitcoinconsensus_verify_script */
+BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_returns_true)
+{
+ unsigned int libconsensus_flags = 0;
+ int nIn = 0;
+
+ CScript scriptPubKey;
+ CScript scriptSig;
+ CScriptWitness wit;
+
+ scriptPubKey << OP_1;
+ CTransaction creditTx = BuildCreditingTransaction(scriptPubKey, 1);
+ CTransaction spendTx = BuildSpendingTransaction(scriptSig, wit, creditTx);
+
+ CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
+ stream << spendTx;
+
+ bitcoinconsensus_error err;
+ int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), (const unsigned char*)&stream[0], stream.size(), nIn, libconsensus_flags, &err);
+ BOOST_CHECK_EQUAL(result, 1);
+ BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_OK);
+}
+
+/* Test bitcoinconsensus_verify_script returns invalid tx index err*/
+BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_tx_index_err)
+{
+ unsigned int libconsensus_flags = 0;
+ int nIn = 3;
+
+ CScript scriptPubKey;
+ CScript scriptSig;
+ CScriptWitness wit;
+
+ scriptPubKey << OP_EQUAL;
+ CTransaction creditTx = BuildCreditingTransaction(scriptPubKey, 1);
+ CTransaction spendTx = BuildSpendingTransaction(scriptSig, wit, creditTx);
+
+ CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
+ stream << spendTx;
+
+ bitcoinconsensus_error err;
+ int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), (const unsigned char*)&stream[0], stream.size(), nIn, libconsensus_flags, &err);
+ BOOST_CHECK_EQUAL(result, 0);
+ BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_TX_INDEX);
+}
+
+/* Test bitcoinconsensus_verify_script returns tx size mismatch err*/
+BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_tx_size)
+{
+ unsigned int libconsensus_flags = 0;
+ int nIn = 0;
+
+ CScript scriptPubKey;
+ CScript scriptSig;
+ CScriptWitness wit;
+
+ scriptPubKey << OP_EQUAL;
+ CTransaction creditTx = BuildCreditingTransaction(scriptPubKey, 1);
+ CTransaction spendTx = BuildSpendingTransaction(scriptSig, wit, creditTx);
+
+ CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
+ stream << spendTx;
+
+ bitcoinconsensus_error err;
+ int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), (const unsigned char*)&stream[0], stream.size() * 2, nIn, libconsensus_flags, &err);
+ BOOST_CHECK_EQUAL(result, 0);
+ BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_TX_SIZE_MISMATCH);
+}
+
+/* Test bitcoinconsensus_verify_script returns invalid tx serialization error */
+BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_tx_serialization)
+{
+ unsigned int libconsensus_flags = 0;
+ int nIn = 0;
+
+ CScript scriptPubKey;
+ CScript scriptSig;
+ CScriptWitness wit;
+
+ scriptPubKey << OP_EQUAL;
+ CTransaction creditTx = BuildCreditingTransaction(scriptPubKey, 1);
+ CTransaction spendTx = BuildSpendingTransaction(scriptSig, wit, creditTx);
+
+ CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
+ stream << 0xffffffff;
+
+ bitcoinconsensus_error err;
+ int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), (const unsigned char*)&stream[0], stream.size(), nIn, libconsensus_flags, &err);
+ BOOST_CHECK_EQUAL(result, 0);
+ BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_TX_DESERIALIZE);
+}
+
+/* Test bitcoinconsensus_verify_script returns amount required error */
+BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_amount_required_err)
+{
+ unsigned int libconsensus_flags = bitcoinconsensus_SCRIPT_FLAGS_VERIFY_WITNESS;
+ int nIn = 0;
+
+ CScript scriptPubKey;
+ CScript scriptSig;
+ CScriptWitness wit;
+
+ scriptPubKey << OP_EQUAL;
+ CTransaction creditTx = BuildCreditingTransaction(scriptPubKey, 1);
+ CTransaction spendTx = BuildSpendingTransaction(scriptSig, wit, creditTx);
+
+ CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
+ stream << spendTx;
+
+ bitcoinconsensus_error err;
+ int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), (const unsigned char*)&stream[0], stream.size(), nIn, libconsensus_flags, &err);
+ BOOST_CHECK_EQUAL(result, 0);
+ BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_AMOUNT_REQUIRED);
+}
+
+/* Test bitcoinconsensus_verify_script returns invalid flags err */
+BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_invalid_flags)
+{
+ unsigned int libconsensus_flags = 1 << 3;
+ int nIn = 0;
+
+ CScript scriptPubKey;
+ CScript scriptSig;
+ CScriptWitness wit;
+
+ scriptPubKey << OP_EQUAL;
+ CTransaction creditTx = BuildCreditingTransaction(scriptPubKey, 1);
+ CTransaction spendTx = BuildSpendingTransaction(scriptSig, wit, creditTx);
+
+ CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
+ stream << spendTx;
+
+ bitcoinconsensus_error err;
+ int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), (const unsigned char*)&stream[0], stream.size(), nIn, libconsensus_flags, &err);
+ BOOST_CHECK_EQUAL(result, 0);
+ BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_INVALID_FLAGS);
+}
+
+#endif
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/streams_tests.cpp b/src/test/streams_tests.cpp
index 5d057108b1..e1e77f7c92 100644
--- a/src/test/streams_tests.cpp
+++ b/src/test/streams_tests.cpp
@@ -6,11 +6,8 @@
#include <support/allocators/zeroafterfree.h>
#include <test/test_bitcoin.h>
-#include <boost/assign/std/vector.hpp> // for 'operator+=()'
#include <boost/test/unit_test.hpp>
-using namespace boost::assign; // bring 'operator+=()' into scope
-
BOOST_FIXTURE_TEST_SUITE(streams_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(streams_vector_writer)
@@ -80,14 +77,17 @@ BOOST_AUTO_TEST_CASE(streams_serializedata_xor)
// Degenerate case
- key += '\x00','\x00';
+ key.push_back('\x00');
+ key.push_back('\x00');
ds.Xor(key);
BOOST_CHECK_EQUAL(
std::string(expected_xor.begin(), expected_xor.end()),
std::string(ds.begin(), ds.end()));
- in += '\x0f','\xf0';
- expected_xor += '\xf0','\x0f';
+ in.push_back('\x0f');
+ in.push_back('\xf0');
+ expected_xor.push_back('\xf0');
+ expected_xor.push_back('\x0f');
// Single character key
@@ -95,7 +95,7 @@ BOOST_AUTO_TEST_CASE(streams_serializedata_xor)
ds.insert(ds.begin(), in.begin(), in.end());
key.clear();
- key += '\xff';
+ key.push_back('\xff');
ds.Xor(key);
BOOST_CHECK_EQUAL(
std::string(expected_xor.begin(), expected_xor.end()),
@@ -105,14 +105,17 @@ BOOST_AUTO_TEST_CASE(streams_serializedata_xor)
in.clear();
expected_xor.clear();
- in += '\xf0','\x0f';
- expected_xor += '\x0f','\x00';
+ in.push_back('\xf0');
+ in.push_back('\x0f');
+ expected_xor.push_back('\x0f');
+ expected_xor.push_back('\x00');
ds.clear();
ds.insert(ds.begin(), in.begin(), in.end());
key.clear();
- key += '\xff','\x0f';
+ key.push_back('\xff');
+ key.push_back('\x0f');
ds.Xor(key);
BOOST_CHECK_EQUAL(
diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp
index e9814edc23..b35b21335e 100644
--- a/src/test/test_bitcoin.cpp
+++ b/src/test/test_bitcoin.cpp
@@ -46,35 +46,43 @@ std::ostream& operator<<(std::ostream& os, const uint256& num)
}
BasicTestingSetup::BasicTestingSetup(const std::string& chainName)
+ : m_path_root(fs::temp_directory_path() / "test_bitcoin" / strprintf("%lu_%i", (unsigned long)GetTime(), (int)(InsecureRandRange(1 << 30))))
{
- SHA256AutoDetect();
- RandomInit();
- ECC_Start();
- SetupEnvironment();
- SetupNetworking();
- InitSignatureCache();
- InitScriptExecutionCache();
- fCheckBlockIndex = true;
- SelectParams(chainName);
- noui_connect();
+ SHA256AutoDetect();
+ RandomInit();
+ ECC_Start();
+ SetupEnvironment();
+ SetupNetworking();
+ InitSignatureCache();
+ InitScriptExecutionCache();
+ fCheckBlockIndex = true;
+ SelectParams(chainName);
+ noui_connect();
}
BasicTestingSetup::~BasicTestingSetup()
{
- ECC_Stop();
+ fs::remove_all(m_path_root);
+ ECC_Stop();
+}
+
+fs::path BasicTestingSetup::SetDataDir(const std::string& name)
+{
+ fs::path ret = m_path_root / name;
+ fs::create_directories(ret);
+ gArgs.ForceSetArg("-datadir", ret.string());
+ return ret;
}
TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(chainName)
{
+ SetDataDir("tempdir");
const CChainParams& chainparams = Params();
// Ideally we'd move all the RPC tests to the functional testing framework
// instead of unit tests, but for now we need these here.
RegisterAllCoreRPCCommands(tableRPC);
ClearDatadirCache();
- pathTemp = fs::temp_directory_path() / strprintf("test_bitcoin_%lu_%i", (unsigned long)GetTime(), (int)(InsecureRandRange(1 << 30)));
- fs::create_directories(pathTemp);
- gArgs.ForceSetArg("-datadir", pathTemp.string());
// We have to run a scheduler thread to prevent ActivateBestChain
// from blocking due to queue overrun.
@@ -99,7 +107,7 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
threadGroup.create_thread(&ThreadScriptCheck);
g_connman = std::unique_ptr<CConnman>(new CConnman(0x1337, 0x1337)); // Deterministic randomness for tests.
connman = g_connman.get();
- peerLogic.reset(new PeerLogicValidation(connman, scheduler));
+ peerLogic.reset(new PeerLogicValidation(connman, scheduler, /*enable_bip61=*/true));
}
TestingSetup::~TestingSetup()
@@ -114,7 +122,6 @@ TestingSetup::~TestingSetup()
pcoinsTip.reset();
pcoinsdbview.reset();
pblocktree.reset();
- fs::remove_all(pathTemp);
}
TestChain100Setup::TestChain100Setup() : TestingSetup(CBaseChainParams::REGTEST)
diff --git a/src/test/test_bitcoin.h b/src/test/test_bitcoin.h
index d013613de2..88b2d37e87 100644
--- a/src/test/test_bitcoin.h
+++ b/src/test/test_bitcoin.h
@@ -45,6 +45,11 @@ struct BasicTestingSetup {
explicit BasicTestingSetup(const std::string& chainName = CBaseChainParams::MAIN);
~BasicTestingSetup();
+
+ fs::path SetDataDir(const std::string& name);
+
+private:
+ const fs::path m_path_root;
};
/** Testing setup that configures a complete environment.
@@ -59,7 +64,6 @@ struct CConnmanTest {
class PeerLogicValidation;
struct TestingSetup: public BasicTestingSetup {
- fs::path pathTemp;
boost::thread_group threadGroup;
CConnman* connman;
CScheduler scheduler;
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index 65c5b8ea1d..45dc0e3571 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -494,6 +494,15 @@ BOOST_AUTO_TEST_CASE(test_big_witness_transaction) {
threadGroup.join_all();
}
+SignatureData CombineSignatures(const CMutableTransaction& input1, const CMutableTransaction& input2, const CTransactionRef tx)
+{
+ SignatureData sigdata;
+ sigdata = DataFromTransaction(input1, 0, tx->vout[0]);
+ sigdata.MergeSignatureData(DataFromTransaction(input2, 0, tx->vout[0]));
+ ProduceSignature(DUMMY_SIGNING_PROVIDER, MutableTransactionSignatureCreator(&input1, 0, tx->vout[0].nValue), tx->vout[0].scriptPubKey, sigdata);
+ return sigdata;
+}
+
BOOST_AUTO_TEST_CASE(test_witness)
{
CBasicKeyStore keystore, keystore2;
@@ -629,7 +638,7 @@ BOOST_AUTO_TEST_CASE(test_witness)
CreateCreditAndSpend(keystore2, scriptMulti, output2, input2, false);
CheckWithFlag(output2, input2, 0, false);
BOOST_CHECK(*output1 == *output2);
- UpdateInput(input1.vin[0], CombineSignatures(output1->vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0, output1->vout[0].nValue), DataFromTransaction(input1, 0), DataFromTransaction(input2, 0)));
+ UpdateInput(input1.vin[0], CombineSignatures(input1, input2, output1));
CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true);
// P2SH 2-of-2 multisig
@@ -640,7 +649,7 @@ BOOST_AUTO_TEST_CASE(test_witness)
CheckWithFlag(output2, input2, 0, true);
CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH, false);
BOOST_CHECK(*output1 == *output2);
- UpdateInput(input1.vin[0], CombineSignatures(output1->vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0, output1->vout[0].nValue), DataFromTransaction(input1, 0), DataFromTransaction(input2, 0)));
+ UpdateInput(input1.vin[0], CombineSignatures(input1, input2, output1));
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true);
CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true);
@@ -652,7 +661,7 @@ BOOST_AUTO_TEST_CASE(test_witness)
CheckWithFlag(output2, input2, 0, true);
CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false);
BOOST_CHECK(*output1 == *output2);
- UpdateInput(input1.vin[0], CombineSignatures(output1->vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0, output1->vout[0].nValue), DataFromTransaction(input1, 0), DataFromTransaction(input2, 0)));
+ UpdateInput(input1.vin[0], CombineSignatures(input1, input2, output1));
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true);
CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true);
@@ -664,7 +673,7 @@ BOOST_AUTO_TEST_CASE(test_witness)
CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH, true);
CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false);
BOOST_CHECK(*output1 == *output2);
- UpdateInput(input1.vin[0], CombineSignatures(output1->vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0, output1->vout[0].nValue), DataFromTransaction(input1, 0), DataFromTransaction(input2, 0)));
+ UpdateInput(input1.vin[0], CombineSignatures(input1, input2, output1));
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true);
CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true);
}
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index 611ccc9b77..d535f74e91 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -1100,7 +1100,7 @@ static void TestOtherProcess(fs::path dirname, std::string lockname, int fd)
BOOST_AUTO_TEST_CASE(test_LockDirectory)
{
- fs::path dirname = fs::temp_directory_path() / fs::unique_path();
+ fs::path dirname = SetDataDir("test_LockDirectory") / fs::unique_path();
const std::string lockname = ".lock";
#ifndef WIN32
// Revert SIGCHLD to default, otherwise boost.test will catch and fail on
@@ -1188,12 +1188,12 @@ BOOST_AUTO_TEST_CASE(test_LockDirectory)
BOOST_AUTO_TEST_CASE(test_DirIsWritable)
{
- // Should be able to write to the system tmp dir.
- fs::path tmpdirname = fs::temp_directory_path();
+ // Should be able to write to the data dir.
+ fs::path tmpdirname = SetDataDir("test_DirIsWritable");
BOOST_CHECK_EQUAL(DirIsWritable(tmpdirname), true);
// Should not be able to write to a non-existent dir.
- tmpdirname = fs::temp_directory_path() / fs::unique_path();
+ tmpdirname = tmpdirname / fs::unique_path();
BOOST_CHECK_EQUAL(DirIsWritable(tmpdirname), false);
fs::create_directory(tmpdirname);
diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp
index 1791bfd7f7..1f42ab8fa8 100644
--- a/src/torcontrol.cpp
+++ b/src/torcontrol.cpp
@@ -528,8 +528,8 @@ void TorController::auth_cb(TorControlConnection& _conn, const TorControlReply&
if (gArgs.GetArg("-onion", "") == "") {
CService resolved(LookupNumeric("127.0.0.1", 9050));
proxyType addrOnion = proxyType(resolved, true);
- SetProxy(NET_TOR, addrOnion);
- SetLimited(NET_TOR, false);
+ SetProxy(NET_ONION, addrOnion);
+ SetLimited(NET_ONION, false);
}
// Finally - now create the service
diff --git a/src/txdb.cpp b/src/txdb.cpp
index b1d5879c83..3635d0ab4b 100644
--- a/src/txdb.cpp
+++ b/src/txdb.cpp
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2017 The Bitcoin Core developers
+// Copyright (c) 2009-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -9,10 +9,10 @@
#include <hash.h>
#include <random.h>
#include <pow.h>
+#include <shutdown.h>
#include <uint256.h>
#include <util.h>
#include <ui_interface.h>
-#include <init.h>
#include <stdint.h>
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 1c6dba0c9c..8090172e3f 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -69,12 +69,12 @@ void CTxMemPool::UpdateForDescendants(txiter updateIt, cacheMap &cachedDescendan
setAllDescendants.insert(cit);
stageEntries.erase(cit);
const setEntries &setChildren = GetMemPoolChildren(cit);
- for (const txiter childEntry : setChildren) {
+ for (txiter childEntry : setChildren) {
cacheMap::iterator cacheIt = cachedDescendants.find(childEntry);
if (cacheIt != cachedDescendants.end()) {
// We've already calculated this one, just add the entries for this set
// but don't traverse again.
- for (const txiter cacheEntry : cacheIt->second) {
+ for (txiter cacheEntry : cacheIt->second) {
setAllDescendants.insert(cacheEntry);
}
} else if (!setAllDescendants.count(childEntry)) {
diff --git a/src/txmempool.h b/src/txmempool.h
index bda812b42f..ebfcf36e11 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -486,7 +486,7 @@ public:
mutable CCriticalSection cs;
indexed_transaction_set mapTx GUARDED_BY(cs);
- typedef indexed_transaction_set::nth_index<0>::type::iterator txiter;
+ using txiter = indexed_transaction_set::nth_index<0>::type::const_iterator;
std::vector<std::pair<uint256, txiter> > vTxHashes; //!< All tx witness hashes/entries in mapTx, in random order
struct CompareIteratorByHash {
diff --git a/src/util.h b/src/util.h
index 8094d72d6b..f8bcc0192c 100644
--- a/src/util.h
+++ b/src/util.h
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2017 The Bitcoin Core developers
+// Copyright (c) 2009-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -20,11 +20,11 @@
#include <sync.h>
#include <tinyformat.h>
#include <utiltime.h>
+#include <utilmemory.h>
#include <atomic>
#include <exception>
#include <map>
-#include <memory>
#include <set>
#include <stdint.h>
#include <string>
@@ -346,13 +346,6 @@ template <typename Callable> void TraceThread(const char* name, Callable func)
std::string CopyrightHolders(const std::string& strPrefix);
-//! Substitute for C++14 std::make_unique.
-template <typename T, typename... Args>
-std::unique_ptr<T> MakeUnique(Args&&... args)
-{
- return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
-}
-
/**
* On platforms that support it, tell the kernel the calling thread is
* CPU-intensive and non-interactive. See SCHED_BATCH in sched(7) for details.
diff --git a/src/utilmemory.h b/src/utilmemory.h
new file mode 100644
index 0000000000..e71fe92284
--- /dev/null
+++ b/src/utilmemory.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2018 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_UTILMEMORY_H
+#define BITCOIN_UTILMEMORY_H
+
+#include <memory>
+#include <utility>
+
+//! Substitute for C++14 std::make_unique.
+template <typename T, typename... Args>
+std::unique_ptr<T> MakeUnique(Args&&... args)
+{
+ return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
+}
+
+#endif
diff --git a/src/validation.cpp b/src/validation.cpp
index 1027780f38..a30f1fd0ce 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2017 The Bitcoin Core developers
+// Copyright (c) 2009-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -17,7 +17,6 @@
#include <cuckoocache.h>
#include <hash.h>
#include <index/txindex.h>
-#include <init.h>
#include <policy/fees.h>
#include <policy/policy.h>
#include <policy/rbf.h>
@@ -29,6 +28,7 @@
#include <script/script.h>
#include <script/sigcache.h>
#include <script/standard.h>
+#include <shutdown.h>
#include <timedata.h>
#include <tinyformat.h>
#include <txdb.h>
@@ -157,7 +157,7 @@ public:
std::multimap<CBlockIndex*, CBlockIndex*> mapBlocksUnlinked;
CBlockIndex *pindexBestInvalid = nullptr;
- bool LoadBlockIndex(const Consensus::Params& consensus_params, CBlockTreeDB& blocktree);
+ bool LoadBlockIndex(const Consensus::Params& consensus_params, CBlockTreeDB& blocktree) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock);
@@ -165,8 +165,8 @@ public:
* If a block header hasn't already been seen, call CheckBlockHeader on it, ensure
* that it doesn't descend from an invalid block, and then add it to mapBlockIndex.
*/
- bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex);
- bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const CDiskBlockPos* dbp, bool* fNewBlock);
+ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const CDiskBlockPos* dbp, bool* fNewBlock) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
// Block (dis)connection on a given view:
DisconnectResult DisconnectBlock(const CBlock& block, const CBlockIndex* pindex, CCoinsViewCache& view);
@@ -177,9 +177,9 @@ public:
bool DisconnectTip(CValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions *disconnectpool);
// Manual block validity manipulation:
- bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex *pindex);
- bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex);
- bool ResetBlockFailureFlags(CBlockIndex *pindex);
+ bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main);
+ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ bool ResetBlockFailureFlags(CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
bool ReplayBlocks(const CChainParams& params, CCoinsView* view);
bool RewindBlockIndex(const CChainParams& params);
@@ -193,9 +193,9 @@ private:
bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace);
bool ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions &disconnectpool);
- CBlockIndex* AddToBlockIndex(const CBlockHeader& block);
+ CBlockIndex* AddToBlockIndex(const CBlockHeader& block) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Create a new block index entry for a given block hash */
- CBlockIndex * InsertBlockIndex(const uint256& hash);
+ CBlockIndex* InsertBlockIndex(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/**
* Make various assertions about the state of the block index.
*
@@ -204,11 +204,11 @@ private:
void CheckBlockIndex(const Consensus::Params& consensusParams);
void InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state);
- CBlockIndex* FindMostWorkChain();
- void ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pindexNew, const CDiskBlockPos& pos, const Consensus::Params& consensusParams);
+ CBlockIndex* FindMostWorkChain() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ void ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pindexNew, const CDiskBlockPos& pos, const Consensus::Params& consensusParams) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- bool RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& inputs, const CChainParams& params);
+ bool RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& inputs, const CChainParams& params) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
} g_chainstate;
@@ -651,7 +651,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
view.SetBackend(viewMemPool);
// do all inputs exist?
- for (const CTxIn txin : tx.vin) {
+ for (const CTxIn& txin : tx.vin) {
if (!pcoinsTip->HaveCoinInCache(txin.prevout)) {
coins_to_uncache.push_back(txin.prevout);
}
@@ -806,13 +806,11 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
// be increased is also an easy-to-reason about way to prevent
// DoS attacks via replacements.
//
- // The mining code doesn't (currently) take children into
- // account (CPFP) so we only consider the feerates of
- // transactions being directly replaced, not their indirect
- // descendants. While that does mean high feerate children are
- // ignored when deciding whether or not to replace, we do
- // require the replacement to pay more overall fees too,
- // mitigating most cases.
+ // We only consider the feerates of transactions being directly
+ // replaced, not their indirect descendants. While that does
+ // mean high feerate children are ignored when deciding whether
+ // or not to replace, we do require the replacement to pay more
+ // overall fees too, mitigating most cases.
CFeeRate oldFeeRate(mi->GetModifiedFee(), mi->GetTxSize());
if (newFeeRate <= oldFeeRate)
{
@@ -957,7 +955,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
}
// Remove conflicting transactions from the mempool
- for (const CTxMemPool::txiter it : allConflicting)
+ for (CTxMemPool::txiter it : allConflicting)
{
LogPrint(BCLog::MEMPOOL, "replacing tx %s with %s for %s BTC additional fees, %d delta bytes\n",
it->GetTx().GetHash().ToString(),
@@ -2645,7 +2643,7 @@ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainPar
return true;
}
-static void NotifyHeaderTip() {
+static void NotifyHeaderTip() LOCKS_EXCLUDED(cs_main) {
bool fNotify = false;
bool fInitialBlockDownload = false;
static CBlockIndex* pindexHeaderOld = nullptr;
@@ -3395,7 +3393,7 @@ bool CChainState::AcceptBlockHeader(const CBlockHeader& block, CValidationState&
return error("%s: Consensus::ContextualCheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state));
// If the previous block index isn't valid, determine if it descends from any block which
- // has been found invalid (g_failed_blocks), then mark pindexPrev and any blocks
+ // has been found invalid (m_failed_blocks), then mark pindexPrev and any blocks
// between them as failed.
if (!pindexPrev->IsValid(BLOCK_VALID_SCRIPTS)) {
for (const CBlockIndex* failedit : m_failed_blocks) {
@@ -3508,7 +3506,6 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CVali
// request; don't process these.
if (pindex->nChainWork < nMinimumChainWork) return true;
}
- if (fNewBlock) *fNewBlock = true;
if (!CheckBlock(block, state, chainparams.GetConsensus()) ||
!ContextualCheckBlock(block, state, chainparams.GetConsensus(), pindex->pprev)) {
@@ -3525,6 +3522,7 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CVali
GetMainSignals().NewPoWValidBlock(pindex, pblock);
// Write block to history file
+ if (fNewBlock) *fNewBlock = true;
try {
CDiskBlockPos blockPos = SaveBlockToDisk(block, pindex->nHeight, chainparams, dbp);
if (blockPos.IsNull()) {
@@ -3729,6 +3727,15 @@ static void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfte
int count=0;
if (nCurrentUsage + nBuffer >= nPruneTarget) {
+ // On a prune event, the chainstate DB is flushed.
+ // To avoid excessive prune events negating the benefit of high dbcache
+ // values, we should not prune too rapidly.
+ // So when pruning in IBD, increase the buffer a bit to avoid a re-prune too soon.
+ if (IsInitialBlockDownload()) {
+ // Since this is only relevant during IBD, we use a fixed 10%
+ nBuffer += nPruneTarget / 10;
+ }
+
for (int fileNumber = 0; fileNumber < nLastBlockFile; fileNumber++) {
nBytesToPrune = vinfoBlockFile[fileNumber].nSize + vinfoBlockFile[fileNumber].nUndoSize;
@@ -3826,7 +3833,7 @@ CBlockIndex * CChainState::InsertBlockIndex(const uint256& hash)
bool CChainState::LoadBlockIndex(const Consensus::Params& consensus_params, CBlockTreeDB& blocktree)
{
- if (!blocktree.LoadBlockIndexGuts(consensus_params, [this](const uint256& hash){ return this->InsertBlockIndex(hash); }))
+ if (!blocktree.LoadBlockIndexGuts(consensus_params, [this](const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return this->InsertBlockIndex(hash); }))
return false;
boost::this_thread::interruption_point();
@@ -3876,7 +3883,7 @@ bool CChainState::LoadBlockIndex(const Consensus::Params& consensus_params, CBlo
return true;
}
-bool static LoadBlockIndexDB(const CChainParams& chainparams)
+bool static LoadBlockIndexDB(const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
if (!g_chainstate.LoadBlockIndex(chainparams.GetConsensus(), *pblocktree))
return false;
@@ -3984,14 +3991,13 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
nCheckLevel = std::max(0, std::min(4, nCheckLevel));
LogPrintf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel);
CCoinsViewCache coins(coinsview);
- CBlockIndex* pindexState = chainActive.Tip();
+ CBlockIndex* pindex;
CBlockIndex* pindexFailure = nullptr;
int nGoodTransactions = 0;
CValidationState state;
int reportDone = 0;
LogPrintf("[0%%]..."); /* Continued */
- for (CBlockIndex* pindex = chainActive.Tip(); pindex && pindex->pprev; pindex = pindex->pprev)
- {
+ for (pindex = chainActive.Tip(); pindex && pindex->pprev; pindex = pindex->pprev) {
boost::this_thread::interruption_point();
int percentageDone = std::max(1, std::min(99, (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * (nCheckLevel >= 4 ? 50 : 100))));
if (reportDone < percentageDone/10) {
@@ -4025,13 +4031,12 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
}
}
// check level 3: check for inconsistencies during memory-only disconnect of tip blocks
- if (nCheckLevel >= 3 && pindex == pindexState && (coins.DynamicMemoryUsage() + pcoinsTip->DynamicMemoryUsage()) <= nCoinCacheUsage) {
+ if (nCheckLevel >= 3 && (coins.DynamicMemoryUsage() + pcoinsTip->DynamicMemoryUsage()) <= nCoinCacheUsage) {
assert(coins.GetBestBlock() == pindex->GetBlockHash());
DisconnectResult res = g_chainstate.DisconnectBlock(block, pindex, coins);
if (res == DISCONNECT_FAILED) {
return error("VerifyDB(): *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
}
- pindexState = pindex->pprev;
if (res == DISCONNECT_UNCLEAN) {
nGoodTransactions = 0;
pindexFailure = pindex;
@@ -4045,9 +4050,11 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
if (pindexFailure)
return error("VerifyDB(): *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", chainActive.Height() - pindexFailure->nHeight + 1, nGoodTransactions);
+ // store block count as we move pindex at check level >= 4
+ int block_count = chainActive.Height() - pindex->nHeight;
+
// check level 4: try reconnecting blocks
if (nCheckLevel >= 4) {
- CBlockIndex *pindex = pindexState;
while (pindex != chainActive.Tip()) {
boost::this_thread::interruption_point();
uiInterface.ShowProgress(_("Verifying blocks..."), std::max(1, std::min(99, 100 - (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * 50))), false);
@@ -4061,7 +4068,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
}
LogPrintf("[DONE].\n");
- LogPrintf("No coin database inconsistencies in last %i blocks (%i transactions)\n", chainActive.Height() - pindexState->nHeight, nGoodTransactions);
+ LogPrintf("No coin database inconsistencies in last %i blocks (%i transactions)\n", block_count, nGoodTransactions);
return true;
}
diff --git a/src/validation.h b/src/validation.h
index b9c2f6c023..869f847cdb 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -104,20 +104,6 @@ static const unsigned int DATABASE_WRITE_INTERVAL = 60 * 60;
static const unsigned int DATABASE_FLUSH_INTERVAL = 24 * 60 * 60;
/** Maximum length of reject messages. */
static const unsigned int MAX_REJECT_MESSAGE_LENGTH = 111;
-/** Average delay between local address broadcasts in seconds. */
-static const unsigned int AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL = 24 * 60 * 60;
-/** Average delay between peer address broadcasts in seconds. */
-static const unsigned int AVG_ADDRESS_BROADCAST_INTERVAL = 30;
-/** Average delay between trickled inventory transmissions in seconds.
- * Blocks and whitelisted receivers bypass this, outbound peers get half this delay. */
-static const unsigned int INVENTORY_BROADCAST_INTERVAL = 5;
-/** Maximum number of inventory items to send per transmission.
- * Limits the impact of low-fee transaction floods. */
-static const unsigned int INVENTORY_BROADCAST_MAX = 7 * INVENTORY_BROADCAST_INTERVAL;
-/** Average delay between feefilter broadcasts in seconds. */
-static const unsigned int AVG_FEEFILTER_BROADCAST_INTERVAL = 10 * 60;
-/** Maximum feefilter broadcast delay after significant change. */
-static const unsigned int MAX_FEEFILTER_CHANGE_DELAY = 5 * 60;
/** Block download timeout base, expressed in millionths of the block interval (i.e. 10 min) */
static const int64_t BLOCK_DOWNLOAD_TIMEOUT_BASE = 1000000;
/** Additional block download timeout per parallel downloading peer (i.e. 5 min) */
@@ -233,7 +219,7 @@ static const uint64_t MIN_DISK_SPACE_FOR_BLOCK_FILES = 550 * 1024 * 1024;
* Note that we guarantee that either the proof-of-work is valid on pblock, or
* (and possibly also) BlockChecked will have been called.
*
- * May not be called with cs_main held. May not be called in a
+ * May not be called in a
* validationinterface callback.
*
* @param[in] pblock The block we want to process.
@@ -241,12 +227,12 @@ static const uint64_t MIN_DISK_SPACE_FOR_BLOCK_FILES = 550 * 1024 * 1024;
* @param[out] fNewBlock A boolean which is set to indicate if the block was first received via this call
* @return True if state.IsValid()
*/
-bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<const CBlock> pblock, bool fForceProcessing, bool* fNewBlock);
+bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<const CBlock> pblock, bool fForceProcessing, bool* fNewBlock) LOCKS_EXCLUDED(cs_main);
/**
* Process incoming block headers.
*
- * May not be called with cs_main held. May not be called in a
+ * May not be called in a
* validationinterface callback.
*
* @param[in] block The block headers themselves
@@ -255,7 +241,7 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<cons
* @param[out] ppindex If set, the pointer will be set to point to the last new block index object for the given headers
* @param[out] first_invalid First header that fails validation, if one exists
*/
-bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& block, CValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex=nullptr, CBlockHeader *first_invalid=nullptr);
+bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& block, CValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex = nullptr, CBlockHeader* first_invalid = nullptr) LOCKS_EXCLUDED(cs_main);
/** Check whether enough disk space is available for an incoming block */
bool CheckDiskSpace(uint64_t nAdditionalBytes = 0, bool blocks_dir = false);
@@ -269,7 +255,7 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB
bool LoadGenesisBlock(const CChainParams& chainparams);
/** Load the block tree and coins database from disk,
* initializing state if we're running with -reindex. */
-bool LoadBlockIndex(const CChainParams& chainparams);
+bool LoadBlockIndex(const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Update the chain tip based on database information. */
bool LoadChainTip(const CChainParams& chainparams);
/** Unload database information */
@@ -413,8 +399,8 @@ bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const CBlockIndex* pindex
/** Context-independent validity checks */
bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW = true, bool fCheckMerkleRoot = true);
-/** Check a block is completely valid from start to finish (only works on top of our current best block, with cs_main held) */
-bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, const CBlock& block, CBlockIndex* pindexPrev, bool fCheckPOW = true, bool fCheckMerkleRoot = true);
+/** Check a block is completely valid from start to finish (only works on top of our current best block) */
+bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, const CBlock& block, CBlockIndex* pindexPrev, bool fCheckPOW = true, bool fCheckMerkleRoot = true) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Check whether witness commitments are required for block. */
bool IsWitnessEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& params);
@@ -454,16 +440,16 @@ CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& loc
/** Mark a block as precious and reorganize.
*
- * May not be called with cs_main held. May not be called in a
+ * May not be called in a
* validationinterface callback.
*/
-bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex *pindex);
+bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex *pindex) LOCKS_EXCLUDED(cs_main);
/** Mark a block as invalid. */
-bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex);
+bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Remove invalidity status from a block and its descendants. */
-bool ResetBlockFailureFlags(CBlockIndex *pindex);
+bool ResetBlockFailureFlags(CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** The currently-connected chain of blocks (protected by cs_main). */
extern CChain& chainActive;
diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp
index f328d2d14b..aff4c44cea 100644
--- a/src/validationinterface.cpp
+++ b/src/validationinterface.cpp
@@ -5,7 +5,6 @@
#include <validationinterface.h>
-#include <init.h>
#include <primitives/block.h>
#include <scheduler.h>
#include <sync.h>
@@ -26,7 +25,6 @@ struct MainSignalsInstance {
boost::signals2::signal<void (const std::shared_ptr<const CBlock> &)> BlockDisconnected;
boost::signals2::signal<void (const CTransactionRef &)> TransactionRemovedFromMempool;
boost::signals2::signal<void (const CBlockLocator &)> ChainStateFlushed;
- boost::signals2::signal<void (const uint256 &)> Inventory;
boost::signals2::signal<void (int64_t nBestBlockTime, CConnman* connman)> Broadcast;
boost::signals2::signal<void (const CBlock&, const CValidationState&)> BlockChecked;
boost::signals2::signal<void (const CBlockIndex *, const std::shared_ptr<const CBlock>&)> NewPoWValidBlock;
@@ -81,7 +79,6 @@ void RegisterValidationInterface(CValidationInterface* pwalletIn) {
g_signals.m_internals->BlockDisconnected.connect(boost::bind(&CValidationInterface::BlockDisconnected, pwalletIn, _1));
g_signals.m_internals->TransactionRemovedFromMempool.connect(boost::bind(&CValidationInterface::TransactionRemovedFromMempool, pwalletIn, _1));
g_signals.m_internals->ChainStateFlushed.connect(boost::bind(&CValidationInterface::ChainStateFlushed, pwalletIn, _1));
- g_signals.m_internals->Inventory.connect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1));
g_signals.m_internals->Broadcast.connect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1, _2));
g_signals.m_internals->BlockChecked.connect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2));
g_signals.m_internals->NewPoWValidBlock.connect(boost::bind(&CValidationInterface::NewPoWValidBlock, pwalletIn, _1, _2));
@@ -90,7 +87,6 @@ void RegisterValidationInterface(CValidationInterface* pwalletIn) {
void UnregisterValidationInterface(CValidationInterface* pwalletIn) {
g_signals.m_internals->BlockChecked.disconnect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2));
g_signals.m_internals->Broadcast.disconnect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1, _2));
- g_signals.m_internals->Inventory.disconnect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1));
g_signals.m_internals->ChainStateFlushed.disconnect(boost::bind(&CValidationInterface::ChainStateFlushed, pwalletIn, _1));
g_signals.m_internals->TransactionAddedToMempool.disconnect(boost::bind(&CValidationInterface::TransactionAddedToMempool, pwalletIn, _1));
g_signals.m_internals->BlockConnected.disconnect(boost::bind(&CValidationInterface::BlockConnected, pwalletIn, _1, _2, _3));
@@ -106,7 +102,6 @@ void UnregisterAllValidationInterfaces() {
}
g_signals.m_internals->BlockChecked.disconnect_all_slots();
g_signals.m_internals->Broadcast.disconnect_all_slots();
- g_signals.m_internals->Inventory.disconnect_all_slots();
g_signals.m_internals->ChainStateFlushed.disconnect_all_slots();
g_signals.m_internals->TransactionAddedToMempool.disconnect_all_slots();
g_signals.m_internals->BlockConnected.disconnect_all_slots();
@@ -172,12 +167,6 @@ void CMainSignals::ChainStateFlushed(const CBlockLocator &locator) {
});
}
-void CMainSignals::Inventory(const uint256 &hash) {
- m_internals->m_schedulerClient.AddToProcessQueue([hash, this] {
- m_internals->Inventory(hash);
- });
-}
-
void CMainSignals::Broadcast(int64_t nBestBlockTime, CConnman* connman) {
m_internals->Broadcast(nBestBlockTime, connman);
}
diff --git a/src/validationinterface.h b/src/validationinterface.h
index 0ca82235da..42cc2e9a20 100644
--- a/src/validationinterface.h
+++ b/src/validationinterface.h
@@ -117,12 +117,6 @@ protected:
* Called on a background thread.
*/
virtual void ChainStateFlushed(const CBlockLocator &locator) {}
- /**
- * Notifies listeners about an inventory item being seen on the network.
- *
- * Called on a background thread.
- */
- virtual void Inventory(const uint256 &hash) {}
/** Tells listeners to broadcast their data. */
virtual void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) {}
/**
@@ -173,7 +167,6 @@ public:
void BlockConnected(const std::shared_ptr<const CBlock> &, const CBlockIndex *pindex, const std::shared_ptr<const std::vector<CTransactionRef>> &);
void BlockDisconnected(const std::shared_ptr<const CBlock> &);
void ChainStateFlushed(const CBlockLocator &);
- void Inventory(const uint256 &);
void Broadcast(int64_t nBestBlockTime, CConnman* connman);
void BlockChecked(const CBlock&, const CValidationState&);
void NewPoWValidBlock(const CBlockIndex *, const std::shared_ptr<const CBlock>&);
diff --git a/src/wallet/crypter.h b/src/wallet/crypter.h
index 4c0c8ff5ec..52842cd978 100644
--- a/src/wallet/crypter.h
+++ b/src/wallet/crypter.h
@@ -116,7 +116,7 @@ class CCryptoKeyStore : public CBasicKeyStore
{
private:
- CKeyingMaterial vMasterKey;
+ CKeyingMaterial vMasterKey GUARDED_BY(cs_KeyStore);
//! if fUseCrypto is true, mapKeys must be empty
//! if fUseCrypto is false, vMasterKey must be empty
@@ -126,13 +126,15 @@ private:
bool fDecryptionThoroughlyChecked;
protected:
+ using CryptedKeyMap = std::map<CKeyID, std::pair<CPubKey, std::vector<unsigned char>>>;
+
bool SetCrypted();
//! will encrypt previously unencrypted keys
bool EncryptKeys(CKeyingMaterial& vMasterKeyIn);
bool Unlock(const CKeyingMaterial& vMasterKeyIn);
- CryptedKeyMap mapCryptedKeys;
+ CryptedKeyMap mapCryptedKeys GUARDED_BY(cs_KeyStore);
public:
CCryptoKeyStore() : fUseCrypto(false), fDecryptionThoroughlyChecked(false)
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp
index 410dd5009f..01b8eacccb 100644
--- a/src/wallet/db.cpp
+++ b/src/wallet/db.cpp
@@ -694,8 +694,10 @@ void BerkeleyEnvironment::Flush(bool fShutdown)
if (mapFileUseCount.empty()) {
dbenv->log_archive(&listp, DB_ARCH_REMOVE);
Close();
- if (!fMockDb)
+ if (!fMockDb) {
fs::remove_all(fs::path(strPath) / "database");
+ }
+ g_dbenvs.erase(strPath);
}
}
}
@@ -794,5 +796,6 @@ void BerkeleyDatabase::Flush(bool shutdown)
{
if (!IsDummy()) {
env->Flush(shutdown);
+ if (shutdown) env = nullptr;
}
}
diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp
index daeff1d0e8..076134cdd1 100644
--- a/src/wallet/init.cpp
+++ b/src/wallet/init.cpp
@@ -7,6 +7,7 @@
#include <init.h>
#include <net.h>
#include <scheduler.h>
+#include <outputtype.h>
#include <util.h>
#include <utilmoneystr.h>
#include <validation.h>
@@ -200,7 +201,7 @@ bool WalletInit::Verify() const
// Keep track of each wallet absolute path to detect duplicates.
std::set<fs::path> wallet_paths;
- for (const auto wallet_file : wallet_files) {
+ for (const auto& wallet_file : wallet_files) {
fs::path wallet_path = fs::absolute(wallet_file, GetWalletDir());
if (!wallet_paths.insert(wallet_path).second) {
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index d09af1dbd1..882ddbbe4e 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -441,7 +441,7 @@ UniValue importpubkey(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 4)
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
throw std::runtime_error(
"importpubkey \"pubkey\" ( \"label\" rescan )\n"
"\nAdds a public key (in hex) that can be watched as if it were in your wallet but cannot be used to spend. Requires a new wallet backup.\n"
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index fd9e23cb64..78ea5eac92 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -1,5 +1,5 @@
// Copyright (c) 2010 Satoshi Nakamoto
-// Copyright (c) 2009-2017 The Bitcoin Core developers
+// Copyright (c) 2009-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -11,6 +11,7 @@
#include <validation.h>
#include <key_io.h>
#include <net.h>
+#include <outputtype.h>
#include <policy/feerate.h>
#include <policy/fees.h>
#include <policy/policy.h>
@@ -20,6 +21,7 @@
#include <rpc/server.h>
#include <rpc/util.h>
#include <script/sign.h>
+#include <shutdown.h>
#include <timedata.h>
#include <util.h>
#include <utilmoneystr.h>
@@ -30,8 +32,6 @@
#include <wallet/walletdb.h>
#include <wallet/walletutil.h>
-#include <init.h> // For StartShutdown
-
#include <stdint.h>
#include <univalue.h>
@@ -40,12 +40,21 @@
static const std::string WALLET_ENDPOINT_BASE = "/wallet/";
-std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& request)
+bool GetWalletNameFromJSONRPCRequest(const JSONRPCRequest& request, std::string& wallet_name)
{
if (request.URI.substr(0, WALLET_ENDPOINT_BASE.size()) == WALLET_ENDPOINT_BASE) {
// wallet endpoint was used
- std::string requestedWallet = urlDecode(request.URI.substr(WALLET_ENDPOINT_BASE.size()));
- std::shared_ptr<CWallet> pwallet = GetWallet(requestedWallet);
+ wallet_name = urlDecode(request.URI.substr(WALLET_ENDPOINT_BASE.size()));
+ return true;
+ }
+ return false;
+}
+
+std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& request)
+{
+ std::string wallet_name;
+ if (GetWalletNameFromJSONRPCRequest(request, wallet_name)) {
+ std::shared_ptr<CWallet> pwallet = GetWallet(wallet_name);
if (!pwallet) throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Requested wallet does not exist or is not loaded");
return pwallet;
}
@@ -66,11 +75,6 @@ bool EnsureWalletIsAvailable(CWallet * const pwallet, bool avoidException)
if (pwallet) return true;
if (avoidException) return false;
if (!HasWallets()) {
- // Note: It isn't currently possible to trigger this error because
- // wallet RPC methods aren't registered unless a wallet is loaded. But
- // this error is being kept as a precaution, because it's possible in
- // the future that wallet RPC methods might get or remain registered
- // when no wallets are loaded.
throw JSONRPCError(
RPC_METHOD_NOT_FOUND, "Method not found (wallet method is disabled because no wallet is loaded)");
}
@@ -849,8 +853,9 @@ static UniValue getbalance(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || (request.params.size() > 3 && IsDeprecatedRPCEnabled("accounts")) || (request.params.size() != 0 && !IsDeprecatedRPCEnabled("accounts")))
+ if (request.fHelp || (request.params.size() > 3 ))
throw std::runtime_error(
+ (IsDeprecatedRPCEnabled("accounts") ? std::string(
"getbalance ( \"account\" minconf include_watchonly )\n"
"\nIf account is not specified, returns the server's total available balance.\n"
"The available balance is what the wallet considers currently spendable, and is\n"
@@ -872,8 +877,17 @@ static UniValue getbalance(const JSONRPCRequest& request)
" balances. In general, account balance calculation is not considered\n"
" reliable and has resulted in confusing outcomes, so it is recommended to\n"
" avoid passing this argument.\n"
- "2. minconf (numeric, optional, default=1) DEPRECATED. Only valid when an account is specified. This argument will be removed in V0.18. To use this deprecated argument, start bitcoind with -deprecatedrpc=accounts. Only include transactions confirmed at least this many times.\n"
- "3. include_watchonly (bool, optional, default=false) DEPRECATED. Only valid when an account is specified. This argument will be removed in V0.18. To use this deprecated argument, start bitcoind with -deprecatedrpc=accounts. Also include balance in watch-only addresses (see 'importaddress')\n"
+ "2. minconf (numeric, optional) Only include transactions confirmed at least this many times. \n"
+ " The default is 1 if an account is provided or 0 if no account is provided\n")
+ : std::string(
+ "getbalance ( \"(dummy)\" minconf include_watchonly )\n"
+ "\nReturns the total available balance.\n"
+ "The available balance is what the wallet considers currently spendable, and is\n"
+ "thus affected by options which limit spendability such as -spendzeroconfchange.\n"
+ "\nArguments:\n"
+ "1. (dummy) (string, optional) Remains for backward compatibility. Must be excluded or set to \"*\".\n"
+ "2. minconf (numeric, optional, default=0) Only include transactions confirmed at least this many times.\n")) +
+ "3. include_watchonly (bool, optional, default=false) Also include balance in watch-only addresses (see 'importaddress')\n"
"\nResult:\n"
"amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this account.\n"
"\nExamples:\n"
@@ -891,38 +905,35 @@ static UniValue getbalance(const JSONRPCRequest& request)
LOCK2(cs_main, pwallet->cs_wallet);
- if (IsDeprecatedRPCEnabled("accounts")) {
- const UniValue& account_value = request.params[0];
- const UniValue& minconf = request.params[1];
- const UniValue& include_watchonly = request.params[2];
+ const UniValue& account_value = request.params[0];
- if (account_value.isNull()) {
- if (!minconf.isNull()) {
- throw JSONRPCError(RPC_INVALID_PARAMETER,
- "getbalance minconf option is only currently supported if an account is specified");
- }
- if (!include_watchonly.isNull()) {
- throw JSONRPCError(RPC_INVALID_PARAMETER,
- "getbalance include_watchonly option is only currently supported if an account is specified");
- }
- return ValueFromAmount(pwallet->GetBalance());
- }
+ int min_depth = 0;
+ if (IsDeprecatedRPCEnabled("accounts") && !account_value.isNull()) {
+ // Default min_depth to 1 when an account is provided.
+ min_depth = 1;
+ }
+ if (!request.params[1].isNull()) {
+ min_depth = request.params[1].get_int();
+ }
+
+ isminefilter filter = ISMINE_SPENDABLE;
+ if (!request.params[2].isNull() && request.params[2].get_bool()) {
+ filter = filter | ISMINE_WATCH_ONLY;
+ }
+
+ if (!account_value.isNull()) {
const std::string& account_param = account_value.get_str();
const std::string* account = account_param != "*" ? &account_param : nullptr;
- int nMinDepth = 1;
- if (!minconf.isNull())
- nMinDepth = minconf.get_int();
- isminefilter filter = ISMINE_SPENDABLE;
- if(!include_watchonly.isNull())
- if(include_watchonly.get_bool())
- filter = filter | ISMINE_WATCH_ONLY;
-
- return ValueFromAmount(pwallet->GetLegacyBalance(filter, nMinDepth, account));
+ if (!IsDeprecatedRPCEnabled("accounts") && account_param != "*") {
+ throw JSONRPCError(RPC_METHOD_DEPRECATED, "dummy first argument must be excluded or set to \"*\".");
+ } else if (IsDeprecatedRPCEnabled("accounts")) {
+ return ValueFromAmount(pwallet->GetLegacyBalance(filter, min_depth, account));
+ }
}
- return ValueFromAmount(pwallet->GetBalance());
+ return ValueFromAmount(pwallet->GetBalance(filter, min_depth));
}
static UniValue getunconfirmedbalance(const JSONRPCRequest &request)
@@ -1017,6 +1028,14 @@ static UniValue sendfrom(const JSONRPCRequest& request)
return NullUniValue;
}
+ if (!IsDeprecatedRPCEnabled("accounts")) {
+ if (request.fHelp) {
+ throw std::runtime_error("sendfrom (Deprecated, will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts)");
+ }
+ throw JSONRPCError(RPC_METHOD_DEPRECATED, "sendfrom is deprecated and will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts.");
+ }
+
+
if (request.fHelp || request.params.size() < 3 || request.params.size() > 6)
throw std::runtime_error(
"sendfrom \"fromaccount\" \"toaddress\" amount ( minconf \"comment\" \"comment_to\" )\n"
@@ -1255,9 +1274,11 @@ static UniValue sendmany(const JSONRPCRequest& request)
EnsureWalletIsUnlocked(pwallet);
// Check funds
- CAmount nBalance = pwallet->GetLegacyBalance(ISMINE_SPENDABLE, nMinDepth, &strAccount);
- if (totalAmount > nBalance)
+ if (IsDeprecatedRPCEnabled("accounts") && totalAmount > pwallet->GetLegacyBalance(ISMINE_SPENDABLE, nMinDepth, &strAccount)) {
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
+ } else if (!IsDeprecatedRPCEnabled("accounts") && totalAmount > pwallet->GetLegacyBalance(ISMINE_SPENDABLE, nMinDepth, nullptr)) {
+ throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Wallet has insufficient funds");
+ }
// Shuffle recipient list
std::shuffle(vecSend.begin(), vecSend.end(), FastRandomContext());
@@ -1349,8 +1370,7 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request)
// Construct using pay-to-script-hash:
CScript inner = CreateMultisigRedeemscript(required, pubkeys);
- pwallet->AddCScript(inner);
- CTxDestination dest = pwallet->AddAndGetDestinationForScript(inner, output_type);
+ CTxDestination dest = AddAndGetDestinationForScript(*pwallet, inner, output_type);
pwallet->SetAddressBook(dest, label, "send");
UniValue result(UniValue::VOBJ);
@@ -3053,7 +3073,7 @@ static UniValue listwallets(const JSONRPCRequest& request)
return obj;
}
-UniValue loadwallet(const JSONRPCRequest& request)
+static UniValue loadwallet(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
@@ -3078,6 +3098,12 @@ UniValue loadwallet(const JSONRPCRequest& request)
fs::path wallet_path = fs::absolute(wallet_file, GetWalletDir());
if (fs::symlink_status(wallet_path).type() == fs::file_not_found) {
throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Wallet " + wallet_file + " not found.");
+ } else if (fs::is_directory(wallet_path)) {
+ // The given filename is a directory. Check that there's a wallet.dat file.
+ fs::path wallet_dat_file = wallet_path / "wallet.dat";
+ if (fs::symlink_status(wallet_dat_file).type() == fs::file_not_found) {
+ throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Directory " + wallet_file + " does not contain a wallet.dat file.");
+ }
}
std::string warning;
@@ -3100,7 +3126,7 @@ UniValue loadwallet(const JSONRPCRequest& request)
return obj;
}
-UniValue createwallet(const JSONRPCRequest& request)
+static UniValue createwallet(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
@@ -3147,6 +3173,55 @@ UniValue createwallet(const JSONRPCRequest& request)
return obj;
}
+static UniValue unloadwallet(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() > 1) {
+ throw std::runtime_error(
+ "unloadwallet ( \"wallet_name\" )\n"
+ "Unloads the wallet referenced by the request endpoint otherwise unloads the wallet specified in the argument.\n"
+ "Specifying the wallet name on a wallet endpoint is invalid."
+ "\nArguments:\n"
+ "1. \"wallet_name\" (string, optional) The name of the wallet to unload.\n"
+ "\nExamples:\n"
+ + HelpExampleCli("unloadwallet", "wallet_name")
+ + HelpExampleRpc("unloadwallet", "wallet_name")
+ );
+ }
+
+ std::string wallet_name;
+ if (GetWalletNameFromJSONRPCRequest(request, wallet_name)) {
+ if (!request.params[0].isNull()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot unload the requested wallet");
+ }
+ } else {
+ wallet_name = request.params[0].get_str();
+ }
+
+ std::shared_ptr<CWallet> wallet = GetWallet(wallet_name);
+ if (!wallet) {
+ throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Requested wallet does not exist or is not loaded");
+ }
+
+ // Release the "main" shared pointer and prevent further notifications.
+ // Note that any attempt to load the same wallet would fail until the wallet
+ // is destroyed (see CheckUniqueFileid).
+ if (!RemoveWallet(wallet)) {
+ throw JSONRPCError(RPC_MISC_ERROR, "Requested wallet already unloaded");
+ }
+ UnregisterValidationInterface(wallet.get());
+
+ // The wallet can be in use so it's not possible to explicitly unload here.
+ // Just notify the unload intent so that all shared pointers are released.
+ // The wallet will be destroyed once the last shared pointer is released.
+ wallet->NotifyUnload();
+
+ // There's no point in waiting for the wallet to unload.
+ // At this point this method should never fail. The unloading could only
+ // fail due to an unexpected error which would cause a process termination.
+
+ return NullUniValue;
+}
+
static UniValue resendwallettransactions(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
@@ -3357,95 +3432,25 @@ static UniValue listunspent(const JSONRPCRequest& request)
return results;
}
-static UniValue fundrawtransaction(const JSONRPCRequest& request)
+void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& fee_out, int& change_position, UniValue options)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
- throw std::runtime_error(
- "fundrawtransaction \"hexstring\" ( options iswitness )\n"
- "\nAdd inputs to a transaction until it has enough in value to meet its out value.\n"
- "This will not modify existing inputs, and will add at most one change output to the outputs.\n"
- "No existing outputs will be modified unless \"subtractFeeFromOutputs\" is specified.\n"
- "Note that inputs which were signed may need to be resigned after completion since in/outputs have been added.\n"
- "The inputs added will not be signed, use signrawtransaction for that.\n"
- "Note that all existing inputs must have their previous output transaction be in the wallet.\n"
- "Note that all inputs selected must be of standard form and P2SH scripts must be\n"
- "in the wallet using importaddress or addmultisigaddress (to calculate fees).\n"
- "You can see whether this is the case by checking the \"solvable\" field in the listunspent output.\n"
- "Only pay-to-pubkey, multisig, and P2SH versions thereof are currently supported for watch-only\n"
- "\nArguments:\n"
- "1. \"hexstring\" (string, required) The hex string of the raw transaction\n"
- "2. options (object, optional)\n"
- " {\n"
- " \"changeAddress\" (string, optional, default pool address) The bitcoin address to receive the change\n"
- " \"changePosition\" (numeric, optional, default random) The index of the change output\n"
- " \"change_type\" (string, optional) The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\". Default is set by -changetype.\n"
- " \"includeWatching\" (boolean, optional, default false) Also select inputs which are watch only\n"
- " \"lockUnspents\" (boolean, optional, default false) Lock selected unspent outputs\n"
- " \"feeRate\" (numeric, optional, default not set: makes wallet determine the fee) Set a specific fee rate in " + CURRENCY_UNIT + "/kB\n"
- " \"subtractFeeFromOutputs\" (array, optional) A json array of integers.\n"
- " The fee will be equally deducted from the amount of each specified output.\n"
- " The outputs are specified by their zero-based index, before any change output is added.\n"
- " Those recipients will receive less bitcoins than you enter in their corresponding amount field.\n"
- " If no outputs are specified here, the sender pays the fee.\n"
- " [vout_index,...]\n"
- " \"replaceable\" (boolean, optional) Marks this transaction as BIP125 replaceable.\n"
- " Allows this transaction to be replaced by a transaction with higher fees\n"
- " \"conf_target\" (numeric, optional) Confirmation target (in blocks)\n"
- " \"estimate_mode\" (string, optional, default=UNSET) The fee estimate mode, must be one of:\n"
- " \"UNSET\"\n"
- " \"ECONOMICAL\"\n"
- " \"CONSERVATIVE\"\n"
- " }\n"
- " for backward compatibility: passing in a true instead of an object will result in {\"includeWatching\":true}\n"
- "3. iswitness (boolean, optional) Whether the transaction hex is a serialized witness transaction \n"
- " If iswitness is not present, heuristic tests will be used in decoding\n"
-
- "\nResult:\n"
- "{\n"
- " \"hex\": \"value\", (string) The resulting raw transaction (hex-encoded string)\n"
- " \"fee\": n, (numeric) Fee in " + CURRENCY_UNIT + " the resulting transaction pays\n"
- " \"changepos\": n (numeric) The position of the added change output, or -1\n"
- "}\n"
- "\nExamples:\n"
- "\nCreate a transaction with no inputs\n"
- + HelpExampleCli("createrawtransaction", "\"[]\" \"{\\\"myaddress\\\":0.01}\"") +
- "\nAdd sufficient unsigned inputs to meet the output value\n"
- + HelpExampleCli("fundrawtransaction", "\"rawtransactionhex\"") +
- "\nSign the transaction\n"
- + HelpExampleCli("signrawtransaction", "\"fundedtransactionhex\"") +
- "\nSend the transaction\n"
- + HelpExampleCli("sendrawtransaction", "\"signedtransactionhex\"")
- );
-
- RPCTypeCheck(request.params, {UniValue::VSTR});
-
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
CCoinControl coinControl;
- int changePosition = -1;
+ change_position = -1;
bool lockUnspents = false;
UniValue subtractFeeFromOutputs;
std::set<int> setSubtractFeeFromOutputs;
- if (!request.params[1].isNull()) {
- if (request.params[1].type() == UniValue::VBOOL) {
+ if (!options.isNull()) {
+ if (options.type() == UniValue::VBOOL) {
// backward compatibility bool only fallback
- coinControl.fAllowWatchOnly = request.params[1].get_bool();
+ coinControl.fAllowWatchOnly = options.get_bool();
}
else {
- RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VOBJ, UniValue::VBOOL});
-
- UniValue options = request.params[1];
-
+ RPCTypeCheckArgument(options, UniValue::VOBJ);
RPCTypeCheckObj(options,
{
{"changeAddress", UniValueType(UniValue::VSTR)},
@@ -3472,7 +3477,7 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
}
if (options.exists("changePosition"))
- changePosition = options["changePosition"].get_int();
+ change_position = options["changePosition"].get_int();
if (options.exists("change_type")) {
if (options.exists("changeAddress")) {
@@ -3519,18 +3524,10 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
}
}
- // parse hex string from parameter
- CMutableTransaction tx;
- bool try_witness = request.params[2].isNull() ? true : request.params[2].get_bool();
- bool try_no_witness = request.params[2].isNull() ? true : !request.params[2].get_bool();
- if (!DecodeHexTx(tx, request.params[0].get_str(), try_no_witness, try_witness)) {
- throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
- }
-
if (tx.vout.size() == 0)
throw JSONRPCError(RPC_INVALID_PARAMETER, "TX must have at least one output");
- if (changePosition != -1 && (changePosition < 0 || (unsigned int)changePosition > tx.vout.size()))
+ if (change_position != -1 && (change_position < 0 || (unsigned int)change_position > tx.vout.size()))
throw JSONRPCError(RPC_INVALID_PARAMETER, "changePosition out of bounds");
for (unsigned int idx = 0; idx < subtractFeeFromOutputs.size(); idx++) {
@@ -3544,17 +3541,98 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
setSubtractFeeFromOutputs.insert(pos);
}
- CAmount nFeeOut;
std::string strFailReason;
- if (!pwallet->FundTransaction(tx, nFeeOut, changePosition, strFailReason, lockUnspents, setSubtractFeeFromOutputs, coinControl)) {
+ if (!pwallet->FundTransaction(tx, fee_out, change_position, strFailReason, lockUnspents, setSubtractFeeFromOutputs, coinControl)) {
throw JSONRPCError(RPC_WALLET_ERROR, strFailReason);
}
+}
+
+static UniValue fundrawtransaction(const JSONRPCRequest& request)
+{
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ CWallet* const pwallet = wallet.get();
+
+ if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
+ return NullUniValue;
+ }
+
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
+ throw std::runtime_error(
+ "fundrawtransaction \"hexstring\" ( options iswitness )\n"
+ "\nAdd inputs to a transaction until it has enough in value to meet its out value.\n"
+ "This will not modify existing inputs, and will add at most one change output to the outputs.\n"
+ "No existing outputs will be modified unless \"subtractFeeFromOutputs\" is specified.\n"
+ "Note that inputs which were signed may need to be resigned after completion since in/outputs have been added.\n"
+ "The inputs added will not be signed, use signrawtransaction for that.\n"
+ "Note that all existing inputs must have their previous output transaction be in the wallet.\n"
+ "Note that all inputs selected must be of standard form and P2SH scripts must be\n"
+ "in the wallet using importaddress or addmultisigaddress (to calculate fees).\n"
+ "You can see whether this is the case by checking the \"solvable\" field in the listunspent output.\n"
+ "Only pay-to-pubkey, multisig, and P2SH versions thereof are currently supported for watch-only\n"
+ "\nArguments:\n"
+ "1. \"hexstring\" (string, required) The hex string of the raw transaction\n"
+ "2. options (object, optional)\n"
+ " {\n"
+ " \"changeAddress\" (string, optional, default pool address) The bitcoin address to receive the change\n"
+ " \"changePosition\" (numeric, optional, default random) The index of the change output\n"
+ " \"change_type\" (string, optional) The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\". Default is set by -changetype.\n"
+ " \"includeWatching\" (boolean, optional, default false) Also select inputs which are watch only\n"
+ " \"lockUnspents\" (boolean, optional, default false) Lock selected unspent outputs\n"
+ " \"feeRate\" (numeric, optional, default not set: makes wallet determine the fee) Set a specific fee rate in " + CURRENCY_UNIT + "/kB\n"
+ " \"subtractFeeFromOutputs\" (array, optional) A json array of integers.\n"
+ " The fee will be equally deducted from the amount of each specified output.\n"
+ " The outputs are specified by their zero-based index, before any change output is added.\n"
+ " Those recipients will receive less bitcoins than you enter in their corresponding amount field.\n"
+ " If no outputs are specified here, the sender pays the fee.\n"
+ " [vout_index,...]\n"
+ " \"replaceable\" (boolean, optional) Marks this transaction as BIP125 replaceable.\n"
+ " Allows this transaction to be replaced by a transaction with higher fees\n"
+ " \"conf_target\" (numeric, optional) Confirmation target (in blocks)\n"
+ " \"estimate_mode\" (string, optional, default=UNSET) The fee estimate mode, must be one of:\n"
+ " \"UNSET\"\n"
+ " \"ECONOMICAL\"\n"
+ " \"CONSERVATIVE\"\n"
+ " }\n"
+ " for backward compatibility: passing in a true instead of an object will result in {\"includeWatching\":true}\n"
+ "3. iswitness (boolean, optional) Whether the transaction hex is a serialized witness transaction \n"
+ " If iswitness is not present, heuristic tests will be used in decoding\n"
+
+ "\nResult:\n"
+ "{\n"
+ " \"hex\": \"value\", (string) The resulting raw transaction (hex-encoded string)\n"
+ " \"fee\": n, (numeric) Fee in " + CURRENCY_UNIT + " the resulting transaction pays\n"
+ " \"changepos\": n (numeric) The position of the added change output, or -1\n"
+ "}\n"
+ "\nExamples:\n"
+ "\nCreate a transaction with no inputs\n"
+ + HelpExampleCli("createrawtransaction", "\"[]\" \"{\\\"myaddress\\\":0.01}\"") +
+ "\nAdd sufficient unsigned inputs to meet the output value\n"
+ + HelpExampleCli("fundrawtransaction", "\"rawtransactionhex\"") +
+ "\nSign the transaction\n"
+ + HelpExampleCli("signrawtransaction", "\"fundedtransactionhex\"") +
+ "\nSend the transaction\n"
+ + HelpExampleCli("sendrawtransaction", "\"signedtransactionhex\"")
+ );
+
+ RPCTypeCheck(request.params, {UniValue::VSTR, UniValueType(), UniValue::VBOOL});
+
+ // parse hex string from parameter
+ CMutableTransaction tx;
+ bool try_witness = request.params[2].isNull() ? true : request.params[2].get_bool();
+ bool try_no_witness = request.params[2].isNull() ? true : !request.params[2].get_bool();
+ if (!DecodeHexTx(tx, request.params[0].get_str(), try_no_witness, try_witness)) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
+ }
+
+ CAmount fee;
+ int change_position;
+ FundTransaction(pwallet, tx, fee, change_position, request.params[1]);
UniValue result(UniValue::VOBJ);
result.pushKV("hex", EncodeHexTx(tx));
- result.pushKV("changepos", changePosition);
- result.pushKV("fee", ValueFromAmount(nFeeOut));
+ result.pushKV("fee", ValueFromAmount(fee));
+ result.pushKV("changepos", change_position);
return result;
}
@@ -4325,6 +4403,332 @@ UniValue sethdseed(const JSONRPCRequest& request)
return NullUniValue;
}
+bool ParseHDKeypath(std::string keypath_str, std::vector<uint32_t>& keypath)
+{
+ std::stringstream ss(keypath_str);
+ std::string item;
+ bool first = true;
+ while (std::getline(ss, item, '/')) {
+ if (item.compare("m") == 0) {
+ if (first) {
+ first = false;
+ continue;
+ }
+ return false;
+ }
+ // Finds whether it is hardened
+ uint32_t path = 0;
+ size_t pos = item.find("'");
+ if (pos != std::string::npos) {
+ // The hardened tick can only be in the last index of the string
+ if (pos != item.size() - 1) {
+ return false;
+ }
+ path |= 0x80000000;
+ item = item.substr(0, item.size() - 1); // Drop the last character which is the hardened tick
+ }
+
+ // Ensure this is only numbers
+ if (item.find_first_not_of( "0123456789" ) != std::string::npos) {
+ return false;
+ }
+ uint32_t number;
+ ParseUInt32(item, &number);
+ path |= number;
+
+ keypath.push_back(path);
+ first = false;
+ }
+ return true;
+}
+
+void AddKeypathToMap(const CWallet* pwallet, const CKeyID& keyID, std::map<CPubKey, std::vector<uint32_t>>& hd_keypaths)
+{
+ CPubKey vchPubKey;
+ if (!pwallet->GetPubKey(keyID, vchPubKey)) {
+ return;
+ }
+ CKeyMetadata meta;
+ auto it = pwallet->mapKeyMetadata.find(keyID);
+ if (it != pwallet->mapKeyMetadata.end()) {
+ meta = it->second;
+ }
+ std::vector<uint32_t> keypath;
+ if (!meta.hdKeypath.empty()) {
+ if (!ParseHDKeypath(meta.hdKeypath, keypath)) {
+ throw JSONRPCError(RPC_INTERNAL_ERROR, "Internal keypath is broken");
+ }
+ // Get the proper master key id
+ CKey key;
+ pwallet->GetKey(meta.hd_seed_id, key);
+ CExtKey masterKey;
+ masterKey.SetSeed(key.begin(), key.size());
+ // Add to map
+ keypath.insert(keypath.begin(), ReadLE32(masterKey.key.GetPubKey().GetID().begin()));
+ } else { // Single pubkeys get the master fingerprint of themselves
+ keypath.insert(keypath.begin(), ReadLE32(vchPubKey.GetID().begin()));
+ }
+ hd_keypaths.emplace(vchPubKey, keypath);
+}
+
+bool FillPSBT(const CWallet* pwallet, PartiallySignedTransaction& psbtx, const CTransaction* txConst, int sighash_type, bool sign, bool bip32derivs)
+{
+ LOCK(pwallet->cs_wallet);
+ // Get all of the previous transactions
+ bool complete = true;
+ for (unsigned int i = 0; i < txConst->vin.size(); ++i) {
+ const CTxIn& txin = txConst->vin[i];
+ PSBTInput& input = psbtx.inputs.at(i);
+
+ // If we don't know about this input, skip it and let someone else deal with it
+ const uint256& txhash = txin.prevout.hash;
+ const auto& it = pwallet->mapWallet.find(txhash);
+ if (it != pwallet->mapWallet.end()) {
+ const CWalletTx& wtx = it->second;
+ CTxOut utxo = wtx.tx->vout[txin.prevout.n];
+ input.non_witness_utxo = wtx.tx;
+ input.witness_utxo = utxo;
+ }
+
+ // Get the Sighash type
+ if (sign && input.sighash_type > 0 && input.sighash_type != sighash_type) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Specified Sighash and sighash in PSBT do not match.");
+ }
+
+ SignatureData sigdata;
+ if (sign) {
+ complete &= SignPSBTInput(*pwallet, *psbtx.tx, input, sigdata, i, sighash_type);
+ } else {
+ complete &= SignPSBTInput(PublicOnlySigningProvider(pwallet), *psbtx.tx, input, sigdata, i, sighash_type);
+ }
+
+ // Drop the unnecessary UTXO
+ if (sigdata.witness) {
+ input.non_witness_utxo = nullptr;
+ } else {
+ input.witness_utxo.SetNull();
+ }
+
+ // Get public key paths
+ if (bip32derivs) {
+ for (const auto& pubkey_it : sigdata.misc_pubkeys) {
+ AddKeypathToMap(pwallet, pubkey_it.first, input.hd_keypaths);
+ }
+ }
+ }
+
+ // Fill in the bip32 keypaths and redeemscripts for the outputs so that hardware wallets can identify change
+ for (unsigned int i = 0; i < txConst->vout.size(); ++i) {
+ const CTxOut& out = txConst->vout.at(i);
+ PSBTOutput& psbt_out = psbtx.outputs.at(i);
+
+ // Dummy tx so we can use ProduceSignature to get stuff out
+ CMutableTransaction dummy_tx;
+ dummy_tx.vin.push_back(CTxIn());
+ dummy_tx.vout.push_back(CTxOut());
+
+ // Fill a SignatureData with output info
+ SignatureData sigdata;
+ psbt_out.FillSignatureData(sigdata);
+
+ MutableTransactionSignatureCreator creator(psbtx.tx.get_ptr(), 0, out.nValue, 1);
+ ProduceSignature(*pwallet, creator, out.scriptPubKey, sigdata);
+ psbt_out.FromSignatureData(sigdata);
+
+ // Get public key paths
+ if (bip32derivs) {
+ for (const auto& pubkey_it : sigdata.misc_pubkeys) {
+ AddKeypathToMap(pwallet, pubkey_it.first, psbt_out.hd_keypaths);
+ }
+ }
+ }
+ return complete;
+}
+
+UniValue walletprocesspsbt(const JSONRPCRequest& request)
+{
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ CWallet* const pwallet = wallet.get();
+
+ if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
+ return NullUniValue;
+ }
+
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 4)
+ throw std::runtime_error(
+ "walletprocesspsbt \"psbt\" ( sign \"sighashtype\" bip32derivs )\n"
+ "\nUpdate a PSBT with input information from our wallet and then sign inputs\n"
+ "that we can sign for.\n"
+ + HelpRequiringPassphrase(pwallet) + "\n"
+
+ "\nArguments:\n"
+ "1. \"psbt\" (string, required) The transaction base64 string\n"
+ "2. sign (boolean, optional, default=true) Also sign the transaction when updating\n"
+ "3. \"sighashtype\" (string, optional, default=ALL) The signature hash type to sign with if not specified by the PSBT. Must be one of\n"
+ " \"ALL\"\n"
+ " \"NONE\"\n"
+ " \"SINGLE\"\n"
+ " \"ALL|ANYONECANPAY\"\n"
+ " \"NONE|ANYONECANPAY\"\n"
+ " \"SINGLE|ANYONECANPAY\"\n"
+ "4. bip32derivs (boolean, optiona, default=false) If true, includes the BIP 32 derivation paths for public keys if we know them\n"
+
+ "\nResult:\n"
+ "{\n"
+ " \"psbt\" : \"value\", (string) The base64-encoded partially signed transaction\n"
+ " \"complete\" : true|false, (boolean) If the transaction has a complete set of signatures\n"
+ " ]\n"
+ "}\n"
+
+ "\nExamples:\n"
+ + HelpExampleCli("walletprocesspsbt", "\"psbt\"")
+ );
+
+ RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL, UniValue::VSTR});
+
+ // Unserialize the transaction
+ PartiallySignedTransaction psbtx;
+ std::string error;
+ if (!DecodePSBT(psbtx, request.params[0].get_str(), error)) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
+ }
+
+ // Get the sighash type
+ int nHashType = ParseSighashString(request.params[2]);
+
+ // Use CTransaction for the constant parts of the
+ // transaction to avoid rehashing.
+ const CTransaction txConst(*psbtx.tx);
+
+ // Fill transaction with our data and also sign
+ bool sign = request.params[1].isNull() ? true : request.params[1].get_bool();
+ bool bip32derivs = request.params[3].isNull() ? false : request.params[3].get_bool();
+ bool complete = FillPSBT(pwallet, psbtx, &txConst, nHashType, sign, bip32derivs);
+
+ UniValue result(UniValue::VOBJ);
+ CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
+ ssTx << psbtx;
+ result.push_back(Pair("psbt", EncodeBase64((unsigned char*)ssTx.data(), ssTx.size())));
+ result.push_back(Pair("complete", complete));
+
+ return result;
+}
+
+UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
+{
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ CWallet* const pwallet = wallet.get();
+
+ if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
+ return NullUniValue;
+ }
+
+ if (request.fHelp || request.params.size() < 2 || request.params.size() > 6)
+ throw std::runtime_error(
+ "walletcreatefundedpsbt [{\"txid\":\"id\",\"vout\":n},...] [{\"address\":amount},{\"data\":\"hex\"},...] ( locktime ) ( replaceable ) ( options bip32derivs )\n"
+ "\nCreates and funds a transaction in the Partially Signed Transaction format. Inputs will be added if supplied inputs are not enough\n"
+ "Implements the Creator and Updater roles.\n"
+ "\nArguments:\n"
+ "1. \"inputs\" (array, required) A json array of json objects\n"
+ " [\n"
+ " {\n"
+ " \"txid\":\"id\", (string, required) The transaction id\n"
+ " \"vout\":n, (numeric, required) The output number\n"
+ " \"sequence\":n (numeric, optional) The sequence number\n"
+ " } \n"
+ " ,...\n"
+ " ]\n"
+ "2. \"outputs\" (array, required) a json array with outputs (key-value pairs)\n"
+ " [\n"
+ " {\n"
+ " \"address\": x.xxx, (obj, optional) A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT + "\n"
+ " },\n"
+ " {\n"
+ " \"data\": \"hex\" (obj, optional) A key-value pair. The key must be \"data\", the value is hex encoded data\n"
+ " }\n"
+ " ,... More key-value pairs of the above form. For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n"
+ " accepted as second parameter.\n"
+ " ]\n"
+ "3. locktime (numeric, optional, default=0) Raw locktime. Non-0 value also locktime-activates inputs\n"
+ "4. replaceable (boolean, optional, default=false) Marks this transaction as BIP125 replaceable.\n"
+ " Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible.\n"
+ "5. options (object, optional)\n"
+ " {\n"
+ " \"changeAddress\" (string, optional, default pool address) The bitcoin address to receive the change\n"
+ " \"changePosition\" (numeric, optional, default random) The index of the change output\n"
+ " \"change_type\" (string, optional) The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\". Default is set by -changetype.\n"
+ " \"includeWatching\" (boolean, optional, default false) Also select inputs which are watch only\n"
+ " \"lockUnspents\" (boolean, optional, default false) Lock selected unspent outputs\n"
+ " \"feeRate\" (numeric, optional, default not set: makes wallet determine the fee) Set a specific fee rate in " + CURRENCY_UNIT + "/kB\n"
+ " \"subtractFeeFromOutputs\" (array, optional) A json array of integers.\n"
+ " The fee will be equally deducted from the amount of each specified output.\n"
+ " The outputs are specified by their zero-based index, before any change output is added.\n"
+ " Those recipients will receive less bitcoins than you enter in their corresponding amount field.\n"
+ " If no outputs are specified here, the sender pays the fee.\n"
+ " [vout_index,...]\n"
+ " \"replaceable\" (boolean, optional) Marks this transaction as BIP125 replaceable.\n"
+ " Allows this transaction to be replaced by a transaction with higher fees\n"
+ " \"conf_target\" (numeric, optional) Confirmation target (in blocks)\n"
+ " \"estimate_mode\" (string, optional, default=UNSET) The fee estimate mode, must be one of:\n"
+ " \"UNSET\"\n"
+ " \"ECONOMICAL\"\n"
+ " \"CONSERVATIVE\"\n"
+ " }\n"
+ "6. bip32derivs (boolean, optiona, default=false) If true, includes the BIP 32 derivation paths for public keys if we know them\n"
+ "\nResult:\n"
+ "{\n"
+ " \"psbt\": \"value\", (string) The resulting raw transaction (base64-encoded string)\n"
+ " \"fee\": n, (numeric) Fee in " + CURRENCY_UNIT + " the resulting transaction pays\n"
+ " \"changepos\": n (numeric) The position of the added change output, or -1\n"
+ "}\n"
+ "\nExamples:\n"
+ "\nCreate a transaction with no inputs\n"
+ + HelpExampleCli("walletcreatefundedpsbt", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"data\\\":\\\"00010203\\\"}]\"")
+ );
+
+ RPCTypeCheck(request.params, {
+ UniValue::VARR,
+ UniValueType(), // ARR or OBJ, checked later
+ UniValue::VNUM,
+ UniValue::VBOOL,
+ UniValue::VOBJ
+ }, true
+ );
+
+ CAmount fee;
+ int change_position;
+ CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], request.params[3]);
+ FundTransaction(pwallet, rawTx, fee, change_position, request.params[4]);
+
+ // Make a blank psbt
+ PartiallySignedTransaction psbtx;
+ psbtx.tx = rawTx;
+ for (unsigned int i = 0; i < rawTx.vin.size(); ++i) {
+ psbtx.inputs.push_back(PSBTInput());
+ }
+ for (unsigned int i = 0; i < rawTx.vout.size(); ++i) {
+ psbtx.outputs.push_back(PSBTOutput());
+ }
+
+ // Use CTransaction for the constant parts of the
+ // transaction to avoid rehashing.
+ const CTransaction txConst(*psbtx.tx);
+
+ // Fill transaction with out data but don't sign
+ bool bip32derivs = request.params[5].isNull() ? false : request.params[5].get_bool();
+ FillPSBT(pwallet, psbtx, &txConst, 1, false, bip32derivs);
+
+ // Serialize the PSBT
+ CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
+ ssTx << psbtx;
+
+ UniValue result(UniValue::VOBJ);
+ result.pushKV("psbt", EncodeBase64((unsigned char*)ssTx.data(), ssTx.size()));
+ result.pushKV("fee", ValueFromAmount(fee));
+ result.pushKV("changepos", change_position);
+ return result;
+}
+
extern UniValue abortrescan(const JSONRPCRequest& request); // in rpcdump.cpp
extern UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp
extern UniValue importprivkey(const JSONRPCRequest& request);
@@ -4341,6 +4745,8 @@ static const CRPCCommand commands[] =
{ // category name actor (function) argNames
// --------------------- ------------------------ ----------------------- ----------
{ "rawtransactions", "fundrawtransaction", &fundrawtransaction, {"hexstring","options","iswitness"} },
+ { "wallet", "walletprocesspsbt", &walletprocesspsbt, {"psbt","sign","sighashtype","bip32derivs"} },
+ { "wallet", "walletcreatefundedpsbt", &walletcreatefundedpsbt, {"inputs","outputs","locktime","replaceable","options","bip32derivs"} },
{ "hidden", "resendwallettransactions", &resendwallettransactions, {} },
{ "wallet", "abandontransaction", &abandontransaction, {"txid"} },
{ "wallet", "abortrescan", &abortrescan, {} },
@@ -4353,7 +4759,7 @@ static const CRPCCommand commands[] =
{ "wallet", "dumpwallet", &dumpwallet, {"filename"} },
{ "wallet", "encryptwallet", &encryptwallet, {"passphrase"} },
{ "wallet", "getaddressinfo", &getaddressinfo, {"address"} },
- { "wallet", "getbalance", &getbalance, {"account","minconf","include_watchonly"} },
+ { "wallet", "getbalance", &getbalance, {"account|dummy","minconf","include_watchonly"} },
{ "wallet", "getnewaddress", &getnewaddress, {"label|account","address_type"} },
{ "wallet", "getrawchangeaddress", &getrawchangeaddress, {"address_type"} },
{ "wallet", "getreceivedbyaddress", &getreceivedbyaddress, {"address","minconf"} },
@@ -4376,12 +4782,12 @@ static const CRPCCommand commands[] =
{ "wallet", "listwallets", &listwallets, {} },
{ "wallet", "loadwallet", &loadwallet, {"filename"} },
{ "wallet", "lockunspent", &lockunspent, {"unlock","transactions"} },
- { "wallet", "sendfrom", &sendfrom, {"fromaccount","toaddress","amount","minconf","comment","comment_to"} },
{ "wallet", "sendmany", &sendmany, {"fromaccount|dummy","amounts","minconf","comment","subtractfeefrom","replaceable","conf_target","estimate_mode"} },
{ "wallet", "sendtoaddress", &sendtoaddress, {"address","amount","comment","comment_to","subtractfeefromamount","replaceable","conf_target","estimate_mode"} },
{ "wallet", "settxfee", &settxfee, {"amount"} },
{ "wallet", "signmessage", &signmessage, {"address","message"} },
{ "wallet", "signrawtransactionwithwallet", &signrawtransactionwithwallet, {"hexstring","prevtxs","sighashtype"} },
+ { "wallet", "unloadwallet", &unloadwallet, {"wallet_name"} },
{ "wallet", "walletlock", &walletlock, {} },
{ "wallet", "walletpassphrasechange", &walletpassphrasechange, {"oldpassphrase","newpassphrase"} },
{ "wallet", "walletpassphrase", &walletpassphrase, {"passphrase","timeout"} },
@@ -4397,6 +4803,7 @@ static const CRPCCommand commands[] =
{ "wallet", "listaccounts", &listaccounts, {"minconf","include_watchonly"} },
{ "wallet", "listreceivedbyaccount", &listreceivedbylabel, {"minconf","include_empty","include_watchonly"} },
{ "wallet", "setaccount", &setlabel, {"address","account"} },
+ { "wallet", "sendfrom", &sendfrom, {"fromaccount","toaddress","amount","minconf","comment","comment_to"} },
{ "wallet", "move", &movecmd, {"fromaccount","toaccount","amount","minconf","comment"} },
/** Label functions (to replace non-balance account functions) */
diff --git a/src/wallet/rpcwallet.h b/src/wallet/rpcwallet.h
index b841f3e424..64556b5824 100644
--- a/src/wallet/rpcwallet.h
+++ b/src/wallet/rpcwallet.h
@@ -11,6 +11,8 @@ class CRPCTable;
class CWallet;
class JSONRPCRequest;
class UniValue;
+struct PartiallySignedTransaction;
+class CTransaction;
void RegisterWalletRPCCommands(CRPCTable &t);
@@ -28,4 +30,5 @@ bool EnsureWalletIsAvailable(CWallet *, bool avoidException);
UniValue getaddressinfo(const JSONRPCRequest& request);
UniValue signrawtransactionwithwallet(const JSONRPCRequest& request);
+bool FillPSBT(const CWallet* pwallet, PartiallySignedTransaction& psbtx, const CTransaction* txConst, int sighash_type = 1, bool sign = true, bool bip32derivs = false);
#endif //BITCOIN_WALLET_RPCWALLET_H
diff --git a/src/wallet/test/psbt_wallet_tests.cpp b/src/wallet/test/psbt_wallet_tests.cpp
new file mode 100644
index 0000000000..a74ca85349
--- /dev/null
+++ b/src/wallet/test/psbt_wallet_tests.cpp
@@ -0,0 +1,74 @@
+// 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.
+
+#include <key_io.h>
+#include <script/sign.h>
+#include <utilstrencodings.h>
+#include <wallet/rpcwallet.h>
+#include <wallet/wallet.h>
+#include <univalue.h>
+
+#include <boost/test/unit_test.hpp>
+#include <test/test_bitcoin.h>
+#include <wallet/test/wallet_test_fixture.h>
+
+BOOST_FIXTURE_TEST_SUITE(psbt_wallet_tests, WalletTestingSetup)
+
+BOOST_AUTO_TEST_CASE(psbt_updater_test)
+{
+ // Create prevtxs and add to wallet
+ CDataStream s_prev_tx1(ParseHex("0200000000010158e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd7501000000171600145f275f436b09a8cc9a2eb2a2f528485c68a56323feffffff02d8231f1b0100000017a914aed962d6654f9a2b36608eb9d64d2b260db4f1118700c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e88702483045022100a22edcc6e5bc511af4cc4ae0de0fcd75c7e04d8c1c3a8aa9d820ed4b967384ec02200642963597b9b1bc22c75e9f3e117284a962188bf5e8a74c895089046a20ad770121035509a48eb623e10aace8bfd0212fdb8a8e5af3c94b0b133b95e114cab89e4f7965000000"), SER_NETWORK, PROTOCOL_VERSION);
+ CTransactionRef prev_tx1;
+ s_prev_tx1 >> prev_tx1;
+ CWalletTx prev_wtx1(&m_wallet, prev_tx1);
+ m_wallet.mapWallet.emplace(prev_wtx1.GetHash(), std::move(prev_wtx1));
+
+ CDataStream s_prev_tx2(ParseHex("0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000"), SER_NETWORK, PROTOCOL_VERSION);
+ CTransactionRef prev_tx2;
+ s_prev_tx2 >> prev_tx2;
+ CWalletTx prev_wtx2(&m_wallet, prev_tx2);
+ m_wallet.mapWallet.emplace(prev_wtx2.GetHash(), std::move(prev_wtx2));
+
+ // Add scripts
+ CScript rs1;
+ CDataStream s_rs1(ParseHex("475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae"), SER_NETWORK, PROTOCOL_VERSION);
+ s_rs1 >> rs1;
+ m_wallet.AddCScript(rs1);
+
+ CScript rs2;
+ CDataStream s_rs2(ParseHex("2200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903"), SER_NETWORK, PROTOCOL_VERSION);
+ s_rs2 >> rs2;
+ m_wallet.AddCScript(rs2);
+
+ CScript ws1;
+ CDataStream s_ws1(ParseHex("47522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae"), SER_NETWORK, PROTOCOL_VERSION);
+ s_ws1 >> ws1;
+ m_wallet.AddCScript(ws1);
+
+ // Add hd seed
+ CKey key = DecodeSecret("5KSSJQ7UNfFGwVgpCZDSHm5rVNhMFcFtvWM3zQ8mW4qNDEN7LFd"); // Mainnet and uncompressed form of cUkG8i1RFfWGWy5ziR11zJ5V4U4W3viSFCfyJmZnvQaUsd1xuF3T
+ CPubKey master_pub_key = m_wallet.DeriveNewSeed(key);
+ m_wallet.SetHDSeed(master_pub_key);
+ m_wallet.NewKeyPool();
+
+ // Call FillPSBT
+ PartiallySignedTransaction psbtx;
+ CDataStream ssData(ParseHex("70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f000000000000000000"), SER_NETWORK, PROTOCOL_VERSION);
+ ssData >> psbtx;
+
+ // Use CTransaction for the constant parts of the
+ // transaction to avoid rehashing.
+ const CTransaction txConst(*psbtx.tx);
+
+ // Fill transaction with our data
+ FillPSBT(&m_wallet, psbtx, &txConst, 1, false, true);
+
+ // Get the final tx
+ CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
+ ssTx << psbtx;
+ std::string final_hex = HexStr(ssTx.begin(), ssTx.end());
+ BOOST_CHECK_EQUAL(final_hex, "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000104475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae2206029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f10d90c6a4f000000800000008000000080220602dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d710d90c6a4f0000008000000080010000800001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e88701042200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903010547522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae2206023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7310d90c6a4f000000800000008003000080220603089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc10d90c6a4f00000080000000800200008000220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000");
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 03754154fc..a946b565f1 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -130,6 +130,8 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
LOCK(cs_main);
+ std::string backup_file = (SetDataDir("importwallet_rescan") / "wallet.backup").string();
+
// Import key into wallet and call dumpwallet to create backup file.
{
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>("dummy", WalletDatabase::CreateDummy());
@@ -139,7 +141,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
JSONRPCRequest request;
request.params.setArray();
- request.params.push_back((pathTemp / "wallet.backup").string());
+ request.params.push_back(backup_file);
AddWallet(wallet);
::dumpwallet(request);
RemoveWallet(wallet);
@@ -152,7 +154,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
JSONRPCRequest request;
request.params.setArray();
- request.params.push_back((pathTemp / "wallet.backup").string());
+ request.params.push_back(backup_file);
AddWallet(wallet);
::importwallet(request);
RemoveWallet(wallet);
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index d0c8096bb0..da585a0603 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -11,7 +11,6 @@
#include <consensus/consensus.h>
#include <consensus/validation.h>
#include <fs.h>
-#include <init.h>
#include <key.h>
#include <key_io.h>
#include <keystore.h>
@@ -23,6 +22,7 @@
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <script/script.h>
+#include <shutdown.h>
#include <timedata.h>
#include <txmempool.h>
#include <utilmoneystr.h>
@@ -79,6 +79,15 @@ std::shared_ptr<CWallet> GetWallet(const std::string& name)
return nullptr;
}
+// Custom deleter for shared_ptr<CWallet>.
+static void ReleaseWallet(CWallet* wallet)
+{
+ LogPrintf("Releasing wallet %s\n", wallet->GetName());
+ wallet->BlockUntilSyncedToCurrentChain();
+ wallet->Flush();
+ delete wallet;
+}
+
const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000;
const uint256 CMerkleTx::ABANDON_HASH(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
@@ -544,7 +553,7 @@ void CWallet::SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator> ran
for (TxSpends::iterator it = range.first; it != range.second; ++it) {
const CWalletTx* wtx = &mapWallet.at(it->second);
if (wtx->nOrderPos < nMinOrderPos) {
- nMinOrderPos = wtx->nOrderPos;;
+ nMinOrderPos = wtx->nOrderPos;
copyFrom = wtx;
}
}
@@ -600,6 +609,8 @@ void CWallet::AddToSpends(const COutPoint& outpoint, const uint256& wtxid)
{
mapTxSpends.insert(std::make_pair(outpoint, wtxid));
+ setLockedCoins.erase(outpoint);
+
std::pair<TxSpends::iterator, TxSpends::iterator> range;
range = mapTxSpends.equal_range(outpoint);
SyncMetaData(range);
@@ -1017,19 +1028,6 @@ bool CWallet::LoadToWallet(const CWalletTx& wtxIn)
return true;
}
-/**
- * Add a transaction to the wallet, or update it. pIndex and posInBlock should
- * be set when the transaction was known to be included in a block. When
- * pIndex == nullptr, then wallet state is not updated in AddToWallet, but
- * notifications happen and cached balances are marked dirty.
- *
- * If fUpdate is true, existing transactions will be updated.
- * TODO: One exception to this is that the abandoned state is cleared under the
- * assumption that any further notification of a transaction that was considered
- * abandoned is an indication that it is not safe to be considered abandoned.
- * Abandoned state should probably be more carefully tracked via different
- * posInBlock signals or by checking mempool presence when necessary.
- */
bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate)
{
const CTransaction& tx = *ptx;
@@ -1096,6 +1094,16 @@ bool CWallet::TransactionCanBeAbandoned(const uint256& hashTx) const
return wtx && !wtx->isAbandoned() && wtx->GetDepthInMainChain() == 0 && !wtx->InMempool();
}
+void CWallet::MarkInputsDirty(const CTransactionRef& tx)
+{
+ for (const CTxIn& txin : tx->vin) {
+ auto it = mapWallet.find(txin.prevout.hash);
+ if (it != mapWallet.end()) {
+ it->second.MarkDirty();
+ }
+ }
+}
+
bool CWallet::AbandonTransaction(const uint256& hashTx)
{
LOCK2(cs_main, cs_wallet);
@@ -1135,7 +1143,7 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
batch.WriteTx(wtx);
NotifyTransactionChanged(this, wtx.GetHash(), CT_UPDATED);
// Iterate over all its outputs, and mark transactions in the wallet that spend them abandoned too
- TxSpends::const_iterator iter = mapTxSpends.lower_bound(COutPoint(hashTx, 0));
+ TxSpends::const_iterator iter = mapTxSpends.lower_bound(COutPoint(now, 0));
while (iter != mapTxSpends.end() && iter->first.hash == now) {
if (!done.count(iter->second)) {
todo.insert(iter->second);
@@ -1144,13 +1152,7 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
}
// If a transaction changes 'conflicted' state, that changes the balance
// available of the outputs it spends. So force those to be recomputed
- for (const CTxIn& txin : wtx.tx->vin)
- {
- auto it = mapWallet.find(txin.prevout.hash);
- if (it != mapWallet.end()) {
- it->second.MarkDirty();
- }
- }
+ MarkInputsDirty(wtx.tx);
}
}
@@ -1206,31 +1208,19 @@ void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx)
}
// If a transaction changes 'conflicted' state, that changes the balance
// available of the outputs it spends. So force those to be recomputed
- for (const CTxIn& txin : wtx.tx->vin) {
- auto it = mapWallet.find(txin.prevout.hash);
- if (it != mapWallet.end()) {
- it->second.MarkDirty();
- }
- }
+ MarkInputsDirty(wtx.tx);
}
}
}
-void CWallet::SyncTransaction(const CTransactionRef& ptx, const CBlockIndex *pindex, int posInBlock) {
- const CTransaction& tx = *ptx;
-
- if (!AddToWalletIfInvolvingMe(ptx, pindex, posInBlock, true))
+void CWallet::SyncTransaction(const CTransactionRef& ptx, const CBlockIndex *pindex, int posInBlock, bool update_tx) {
+ if (!AddToWalletIfInvolvingMe(ptx, pindex, posInBlock, update_tx))
return; // Not one of ours
// If a transaction changes 'conflicted' state, that changes the balance
// available of the outputs it spends. So force those to be
// recomputed, also:
- for (const CTxIn& txin : tx.vin) {
- auto it = mapWallet.find(txin.prevout.hash);
- if (it != mapWallet.end()) {
- it->second.MarkDirty();
- }
- }
+ MarkInputsDirty(ptx);
}
void CWallet::TransactionAddedToMempool(const CTransactionRef& ptx) {
@@ -1296,7 +1286,7 @@ void CWallet::BlockUntilSyncedToCurrentChain() {
LOCK(cs_main);
const CBlockIndex* initialChainTip = chainActive.Tip();
- if (m_last_block_processed->GetAncestor(initialChainTip->nHeight) == initialChainTip) {
+ if (m_last_block_processed && m_last_block_processed->GetAncestor(initialChainTip->nHeight) == initialChainTip) {
return;
}
}
@@ -1521,45 +1511,6 @@ int64_t CWalletTx::GetTxTime() const
return n ? n : nTimeReceived;
}
-int CWalletTx::GetRequestCount() const
-{
- // Returns -1 if it wasn't being tracked
- int nRequests = -1;
- {
- LOCK(pwallet->cs_wallet);
- if (IsCoinBase())
- {
- // Generated block
- if (!hashUnset())
- {
- std::map<uint256, int>::const_iterator mi = pwallet->mapRequestCount.find(hashBlock);
- if (mi != pwallet->mapRequestCount.end())
- nRequests = (*mi).second;
- }
- }
- else
- {
- // Did anyone request this transaction?
- std::map<uint256, int>::const_iterator mi = pwallet->mapRequestCount.find(GetHash());
- if (mi != pwallet->mapRequestCount.end())
- {
- nRequests = (*mi).second;
-
- // How about the block it's in?
- if (nRequests == 0 && !hashUnset())
- {
- std::map<uint256, int>::const_iterator _mi = pwallet->mapRequestCount.find(hashBlock);
- if (_mi != pwallet->mapRequestCount.end())
- nRequests = (*_mi).second;
- else
- nRequests = 1; // If it's in someone else's block it must have got out
- }
- }
- }
- }
- return nRequests;
-}
-
// Helper for producing a max-sized low-S signature (eg 72 bytes)
bool CWallet::DummySignInput(CTxIn &tx_in, const CTxOut &txout) const
{
@@ -1786,7 +1737,7 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlock
break;
}
for (size_t posInBlock = 0; posInBlock < block.vtx.size(); ++posInBlock) {
- AddToWalletIfInvolvingMe(block.vtx[posInBlock], pindex, posInBlock, fUpdate);
+ SyncTransaction(block.vtx[posInBlock], pindex, posInBlock, fUpdate);
}
} else {
ret = pindex;
@@ -1957,7 +1908,7 @@ CAmount CWalletTx::GetImmatureCredit(bool fUseCache) const
return 0;
}
-CAmount CWalletTx::GetAvailableCredit(bool fUseCache) const
+CAmount CWalletTx::GetAvailableCredit(bool fUseCache, const isminefilter& filter) const
{
if (pwallet == nullptr)
return 0;
@@ -1966,8 +1917,20 @@ CAmount CWalletTx::GetAvailableCredit(bool fUseCache) const
if (IsCoinBase() && GetBlocksToMaturity() > 0)
return 0;
- if (fUseCache && fAvailableCreditCached)
- return nAvailableCreditCached;
+ CAmount* cache = nullptr;
+ bool* cache_used = nullptr;
+
+ if (filter == ISMINE_SPENDABLE) {
+ cache = &nAvailableCreditCached;
+ cache_used = &fAvailableCreditCached;
+ } else if (filter == ISMINE_WATCH_ONLY) {
+ cache = &nAvailableWatchCreditCached;
+ cache_used = &fAvailableWatchCreditCached;
+ }
+
+ if (fUseCache && cache_used && *cache_used) {
+ return *cache;
+ }
CAmount nCredit = 0;
uint256 hashTx = GetHash();
@@ -1976,14 +1939,16 @@ CAmount CWalletTx::GetAvailableCredit(bool fUseCache) const
if (!pwallet->IsSpent(hashTx, i))
{
const CTxOut &txout = tx->vout[i];
- nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE);
+ nCredit += pwallet->GetCredit(txout, filter);
if (!MoneyRange(nCredit))
throw std::runtime_error(std::string(__func__) + " : value out of range");
}
}
- nAvailableCreditCached = nCredit;
- fAvailableCreditCached = true;
+ if (cache) {
+ *cache = nCredit;
+ *cache_used = true;
+ }
return nCredit;
}
@@ -2001,35 +1966,6 @@ CAmount CWalletTx::GetImmatureWatchOnlyCredit(const bool fUseCache) const
return 0;
}
-CAmount CWalletTx::GetAvailableWatchOnlyCredit(const bool fUseCache) const
-{
- if (pwallet == nullptr)
- return 0;
-
- // Must wait until coinbase is safely deep enough in the chain before valuing it
- if (IsCoinBase() && GetBlocksToMaturity() > 0)
- return 0;
-
- if (fUseCache && fAvailableWatchCreditCached)
- return nAvailableWatchCreditCached;
-
- CAmount nCredit = 0;
- for (unsigned int i = 0; i < tx->vout.size(); i++)
- {
- if (!pwallet->IsSpent(GetHash(), i))
- {
- const CTxOut &txout = tx->vout[i];
- nCredit += pwallet->GetCredit(txout, ISMINE_WATCH_ONLY);
- if (!MoneyRange(nCredit))
- throw std::runtime_error(std::string(__func__) + ": value out of range");
- }
- }
-
- nAvailableWatchCreditCached = nCredit;
- fAvailableWatchCreditCached = true;
- return nCredit;
-}
-
CAmount CWalletTx::GetChange() const
{
if (fChangeCached)
@@ -2143,7 +2079,7 @@ void CWallet::ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman
*/
-CAmount CWallet::GetBalance() const
+CAmount CWallet::GetBalance(const isminefilter& filter, const int min_depth) const
{
CAmount nTotal = 0;
{
@@ -2151,8 +2087,9 @@ CAmount CWallet::GetBalance() const
for (const auto& entry : mapWallet)
{
const CWalletTx* pcoin = &entry.second;
- if (pcoin->IsTrusted())
- nTotal += pcoin->GetAvailableCredit();
+ if (pcoin->IsTrusted() && pcoin->GetDepthInMainChain() >= min_depth) {
+ nTotal += pcoin->GetAvailableCredit(true, filter);
+ }
}
}
@@ -2188,22 +2125,6 @@ CAmount CWallet::GetImmatureBalance() const
return nTotal;
}
-CAmount CWallet::GetWatchOnlyBalance() const
-{
- CAmount nTotal = 0;
- {
- LOCK2(cs_main, cs_wallet);
- for (const auto& entry : mapWallet)
- {
- const CWalletTx* pcoin = &entry.second;
- if (pcoin->IsTrusted())
- nTotal += pcoin->GetAvailableWatchOnlyCredit();
- }
- }
-
- return nTotal;
-}
-
CAmount CWallet::GetUnconfirmedWatchOnlyBalance() const
{
CAmount nTotal = 0;
@@ -2213,7 +2134,7 @@ CAmount CWallet::GetUnconfirmedWatchOnlyBalance() const
{
const CWalletTx* pcoin = &entry.second;
if (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 && pcoin->InMempool())
- nTotal += pcoin->GetAvailableWatchOnlyCredit();
+ nTotal += pcoin->GetAvailableCredit(true, ISMINE_WATCH_ONLY);
}
}
return nTotal;
@@ -3067,7 +2988,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransac
tx = MakeTransactionRef(std::move(txNew));
// Limit size
- if (GetTransactionWeight(*tx) >= MAX_STANDARD_TX_WEIGHT)
+ if (GetTransactionWeight(*tx) > MAX_STANDARD_TX_WEIGHT)
{
strFailReason = _("Transaction too large");
return false;
@@ -3134,9 +3055,6 @@ bool CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve
}
}
- // Track how many getdata requests our transaction gets
- mapRequestCount[wtxNew.GetHash()] = 0;
-
// Get the inserted-CWalletTx from mapWallet so that the
// fInMempool flag is cached properly
CWalletTx& wtx = mapWallet.at(wtxNew.GetHash());
@@ -3199,8 +3117,11 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
}
}
- // This wallet is in its first run if all of these are empty
- fFirstRunRet = mapKeys.empty() && mapCryptedKeys.empty() && mapWatchKeys.empty() && setWatchOnly.empty() && mapScripts.empty();
+ {
+ LOCK(cs_KeyStore);
+ // This wallet is in its first run if all of these are empty
+ fFirstRunRet = mapKeys.empty() && mapCryptedKeys.empty() && mapWatchKeys.empty() && setWatchOnly.empty() && mapScripts.empty();
+ }
if (nLoadWalletRet != DBErrors::LOAD_OK)
return nLoadWalletRet;
@@ -4066,7 +3987,9 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name,
int64_t nStart = GetTimeMillis();
bool fFirstRun = true;
- std::shared_ptr<CWallet> walletInstance = std::make_shared<CWallet>(name, WalletDatabase::Create(path));
+ // TODO: Can't use std::make_shared because we need a custom deleter but
+ // should be possible to use std::allocate_shared.
+ std::shared_ptr<CWallet> walletInstance(new CWallet(name, WalletDatabase::Create(path)), ReleaseWallet);
DBErrors nLoadWalletRet = walletInstance->LoadWallet(fFirstRun);
if (nLoadWalletRet != DBErrors::LOAD_OK)
{
@@ -4095,8 +4018,6 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name,
}
}
- uiInterface.LoadWallet(walletInstance);
-
int prev_version = walletInstance->nWalletVersion;
if (gArgs.GetBoolArg("-upgradewallet", fFirstRun))
{
@@ -4346,6 +4267,8 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name,
}
}
+ uiInterface.LoadWallet(walletInstance);
+
// Register with the validation interface. It's ok to do this after rescan since we're still holding cs_main.
RegisterValidationInterface(walletInstance.get());
@@ -4403,7 +4326,7 @@ void CMerkleTx::SetMerkleBranch(const CBlockIndex* pindex, int posInBlock)
nIndex = posInBlock;
}
-int CMerkleTx::GetDepthInMainChain(const CBlockIndex* &pindexRet) const
+int CMerkleTx::GetDepthInMainChain() const
{
if (hashUnset())
return 0;
@@ -4415,7 +4338,6 @@ int CMerkleTx::GetDepthInMainChain(const CBlockIndex* &pindexRet) const
if (!pindex || !chainActive.Contains(pindex))
return 0;
- pindexRet = pindex;
return ((nIndex == -1) ? (-1) : 1) * (chainActive.Height() - pindex->nHeight + 1);
}
@@ -4440,35 +4362,6 @@ bool CWalletTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState&
return ret;
}
-static const std::string OUTPUT_TYPE_STRING_LEGACY = "legacy";
-static const std::string OUTPUT_TYPE_STRING_P2SH_SEGWIT = "p2sh-segwit";
-static const std::string OUTPUT_TYPE_STRING_BECH32 = "bech32";
-
-bool ParseOutputType(const std::string& type, OutputType& output_type)
-{
- if (type == OUTPUT_TYPE_STRING_LEGACY) {
- output_type = OutputType::LEGACY;
- return true;
- } else if (type == OUTPUT_TYPE_STRING_P2SH_SEGWIT) {
- output_type = OutputType::P2SH_SEGWIT;
- return true;
- } else if (type == OUTPUT_TYPE_STRING_BECH32) {
- output_type = OutputType::BECH32;
- return true;
- }
- return false;
-}
-
-const std::string& FormatOutputType(OutputType type)
-{
- switch (type) {
- case OutputType::LEGACY: return OUTPUT_TYPE_STRING_LEGACY;
- case OutputType::P2SH_SEGWIT: return OUTPUT_TYPE_STRING_P2SH_SEGWIT;
- case OutputType::BECH32: return OUTPUT_TYPE_STRING_BECH32;
- default: assert(false);
- }
-}
-
void CWallet::LearnRelatedScripts(const CPubKey& key, OutputType type)
{
if (key.IsCompressed() && (type == OutputType::P2SH_SEGWIT || type == OutputType::BECH32)) {
@@ -4486,59 +4379,3 @@ void CWallet::LearnAllRelatedScripts(const CPubKey& key)
LearnRelatedScripts(key, OutputType::P2SH_SEGWIT);
}
-CTxDestination GetDestinationForKey(const CPubKey& key, OutputType type)
-{
- switch (type) {
- case OutputType::LEGACY: return key.GetID();
- case OutputType::P2SH_SEGWIT:
- case OutputType::BECH32: {
- if (!key.IsCompressed()) return key.GetID();
- CTxDestination witdest = WitnessV0KeyHash(key.GetID());
- CScript witprog = GetScriptForDestination(witdest);
- if (type == OutputType::P2SH_SEGWIT) {
- return CScriptID(witprog);
- } else {
- return witdest;
- }
- }
- default: assert(false);
- }
-}
-
-std::vector<CTxDestination> GetAllDestinationsForKey(const CPubKey& key)
-{
- CKeyID keyid = key.GetID();
- if (key.IsCompressed()) {
- CTxDestination segwit = WitnessV0KeyHash(keyid);
- CTxDestination p2sh = CScriptID(GetScriptForDestination(segwit));
- return std::vector<CTxDestination>{std::move(keyid), std::move(p2sh), std::move(segwit)};
- } else {
- return std::vector<CTxDestination>{std::move(keyid)};
- }
-}
-
-CTxDestination CWallet::AddAndGetDestinationForScript(const CScript& script, OutputType type)
-{
- // Note that scripts over 520 bytes are not yet supported.
- switch (type) {
- case OutputType::LEGACY:
- return CScriptID(script);
- case OutputType::P2SH_SEGWIT:
- case OutputType::BECH32: {
- WitnessV0ScriptHash hash;
- CSHA256().Write(script.data(), script.size()).Finalize(hash.begin());
- CTxDestination witdest = hash;
- CScript witprog = GetScriptForDestination(witdest);
- // Check if the resulting program is solvable (i.e. doesn't use an uncompressed key)
- if (!IsSolvable(*this, witprog)) return CScriptID(script);
- // Add the redeemscript, so that P2WSH and P2SH-P2WSH outputs are recognized as ours.
- AddCScript(witprog);
- if (type == OutputType::BECH32) {
- return witdest;
- } else {
- return CScriptID(witprog);
- }
- }
- default: assert(false);
- }
-}
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index aacbdf81d6..2e53ca0c55 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -7,6 +7,7 @@
#define BITCOIN_WALLET_WALLET_H
#include <amount.h>
+#include <outputtype.h>
#include <policy/feerate.h>
#include <streams.h>
#include <tinyformat.h>
@@ -93,20 +94,6 @@ enum WalletFeature
FEATURE_LATEST = FEATURE_PRE_SPLIT_KEYPOOL
};
-enum class OutputType {
- LEGACY,
- P2SH_SEGWIT,
- BECH32,
-
- /**
- * Special output type for change outputs only. Automatically choose type
- * based on address type setting and the types other of non-change outputs
- * (see -changetype option documentation and implementation in
- * CWallet::TransactionChangeType for details).
- */
- CHANGE_AUTO,
-};
-
//! Default for -addresstype
constexpr OutputType DEFAULT_ADDRESS_TYPE{OutputType::P2SH_SEGWIT};
@@ -267,9 +254,8 @@ public:
* 0 : in memory pool, waiting to be included in a block
* >=1 : this many blocks deep in the main chain
*/
- int GetDepthInMainChain(const CBlockIndex* &pindexRet) const;
- int GetDepthInMainChain() const { const CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet); }
- bool IsInMainChain() const { const CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet) > 0; }
+ int GetDepthInMainChain() const;
+ bool IsInMainChain() const { return GetDepthInMainChain() > 0; }
int GetBlocksToMaturity() const;
bool hashUnset() const { return (hashBlock.IsNull() || hashBlock == ABANDON_HASH); }
bool isAbandoned() const { return (hashBlock == ABANDON_HASH); }
@@ -460,9 +446,8 @@ public:
CAmount GetDebit(const isminefilter& filter) const;
CAmount GetCredit(const isminefilter& filter) const;
CAmount GetImmatureCredit(bool fUseCache=true) const;
- CAmount GetAvailableCredit(bool fUseCache=true) const;
+ CAmount GetAvailableCredit(bool fUseCache=true, const isminefilter& filter=ISMINE_SPENDABLE) const;
CAmount GetImmatureWatchOnlyCredit(const bool fUseCache=true) const;
- CAmount GetAvailableWatchOnlyCredit(const bool fUseCache=true) const;
CAmount GetChange() const;
// Get the marginal bytes if spending the specified output from this transaction
@@ -486,7 +471,6 @@ public:
bool IsTrusted() const;
int64_t GetTxTime() const;
- int GetRequestCount() const;
// RelayWalletTransaction may only be called if fBroadcastTransactions!
bool RelayWalletTransaction(CConnman* connman);
@@ -704,14 +688,32 @@ private:
void AddToSpends(const COutPoint& outpoint, const uint256& wtxid);
void AddToSpends(const uint256& wtxid);
+ /**
+ * Add a transaction to the wallet, or update it. pIndex and posInBlock should
+ * be set when the transaction was known to be included in a block. When
+ * pIndex == nullptr, then wallet state is not updated in AddToWallet, but
+ * notifications happen and cached balances are marked dirty.
+ *
+ * If fUpdate is true, existing transactions will be updated.
+ * TODO: One exception to this is that the abandoned state is cleared under the
+ * assumption that any further notification of a transaction that was considered
+ * abandoned is an indication that it is not safe to be considered abandoned.
+ * Abandoned state should probably be more carefully tracked via different
+ * posInBlock signals or by checking mempool presence when necessary.
+ */
+ bool AddToWalletIfInvolvingMe(const CTransactionRef& tx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+
/* Mark a transaction (and its in-wallet descendants) as conflicting with a particular block. */
void MarkConflicted(const uint256& hashBlock, const uint256& hashTx);
+ /* Mark a transaction's inputs dirty, thus forcing the outputs to be recomputed */
+ void MarkInputsDirty(const CTransactionRef& tx);
+
void SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator>);
- /* Used by TransactionAddedToMemorypool/BlockConnected/Disconnected.
+ /* Used by TransactionAddedToMemorypool/BlockConnected/Disconnected/ScanForWalletTransactions.
* Should be called with pindexBlock and posInBlock if this is for a transaction that is included in a block. */
- void SyncTransaction(const CTransactionRef& tx, const CBlockIndex *pindex = nullptr, int posInBlock = 0) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ void SyncTransaction(const CTransactionRef& tx, const CBlockIndex *pindex = nullptr, int posInBlock = 0, bool update_tx = true) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/* the HD chain data model (external chain counters) */
CHDChain hdChain;
@@ -820,7 +822,6 @@ public:
int64_t nOrderPosNext = 0;
uint64_t nAccountingEntryNumber = 0;
- std::map<uint256, int> mapRequestCount;
std::map<CTxDestination, CAddressBookData> mapAddressBook;
@@ -936,7 +937,6 @@ public:
void TransactionAddedToMempool(const CTransactionRef& tx) override;
void BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex *pindex, const std::vector<CTransactionRef>& vtxConflicted) override;
void BlockDisconnected(const std::shared_ptr<const CBlock>& pblock) override;
- bool AddToWalletIfInvolvingMe(const CTransactionRef& tx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
int64_t RescanFromTime(int64_t startTime, const WalletRescanReserver& reserver, bool update);
CBlockIndex* ScanForWalletTransactions(CBlockIndex* pindexStart, CBlockIndex* pindexStop, const WalletRescanReserver& reserver, bool fUpdate = false);
void TransactionRemovedFromMempool(const CTransactionRef &ptx) override;
@@ -944,10 +944,9 @@ public:
void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) override;
// ResendWalletTransactionsBefore may only be called if fBroadcastTransactions!
std::vector<uint256> ResendWalletTransactionsBefore(int64_t nTime, CConnman* connman);
- CAmount GetBalance() const;
+ CAmount GetBalance(const isminefilter& filter=ISMINE_SPENDABLE, const int min_depth=0) const;
CAmount GetUnconfirmedBalance() const;
CAmount GetImmatureBalance() const;
- CAmount GetWatchOnlyBalance() const;
CAmount GetUnconfirmedWatchOnlyBalance() const;
CAmount GetImmatureWatchOnlyBalance() const;
CAmount GetLegacyBalance(const isminefilter& filter, int minDepth, const std::string* account) const;
@@ -1064,16 +1063,6 @@ public:
const std::string& GetLabelName(const CScript& scriptPubKey) const;
- void Inventory(const uint256 &hash) override
- {
- {
- LOCK(cs_wallet);
- std::map<uint256, int>::iterator mi = mapRequestCount.find(hash);
- if (mi != mapRequestCount.end())
- (*mi).second++;
- }
- }
-
void GetScriptForMining(std::shared_ptr<CReserveScript> &script);
unsigned int GetKeyPoolSize() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet)
@@ -1100,6 +1089,9 @@ public:
//! Flush wallet (bitdb flush)
void Flush(bool shutdown=false);
+ /** Wallet is about to be unloaded */
+ boost::signals2::signal<void ()> NotifyUnload;
+
/**
* Address book entry changed.
* @note called with lock cs_wallet held.
@@ -1191,12 +1183,6 @@ public:
*/
void LearnAllRelatedScripts(const CPubKey& key);
- /**
- * Get a destination of the requested type (if possible) to the specified script.
- * This function will automatically add the necessary scripts to the wallet.
- */
- CTxDestination AddAndGetDestinationForScript(const CScript& script, OutputType);
-
/** Whether a given output is spendable by this wallet */
bool OutputEligibleForSpending(const COutput& output, const CoinEligibilityFilter& eligibility_filter) const;
};
@@ -1263,18 +1249,6 @@ public:
}
};
-bool ParseOutputType(const std::string& str, OutputType& output_type);
-const std::string& FormatOutputType(OutputType type);
-
-/**
- * Get a destination of the requested type (if possible) to the specified key.
- * The caller must make sure LearnRelatedScripts has been called beforehand.
- */
-CTxDestination GetDestinationForKey(const CPubKey& key, OutputType);
-
-/** Get all destinations (potentially) supported by the wallet for the given key. */
-std::vector<CTxDestination> GetAllDestinationsForKey(const CPubKey& key);
-
/** RAII object to check and reserve a wallet rescan */
class WalletRescanReserver
{
diff --git a/src/zmq/zmqnotificationinterface.cpp b/src/zmq/zmqnotificationinterface.cpp
index 68b425fa08..8cbc969972 100644
--- a/src/zmq/zmqnotificationinterface.cpp
+++ b/src/zmq/zmqnotificationinterface.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2015-2017 The Bitcoin Core developers
+// Copyright (c) 2015-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -29,6 +29,15 @@ CZMQNotificationInterface::~CZMQNotificationInterface()
}
}
+std::list<const CZMQAbstractNotifier*> CZMQNotificationInterface::GetActiveNotifiers() const
+{
+ std::list<const CZMQAbstractNotifier*> result;
+ for (const auto* n : notifiers) {
+ result.push_back(n);
+ }
+ return result;
+}
+
CZMQNotificationInterface* CZMQNotificationInterface::Create()
{
CZMQNotificationInterface* notificationInterface = nullptr;
@@ -180,3 +189,5 @@ void CZMQNotificationInterface::BlockDisconnected(const std::shared_ptr<const CB
TransactionAddedToMempool(ptx);
}
}
+
+CZMQNotificationInterface* g_zmq_notification_interface = nullptr;
diff --git a/src/zmq/zmqnotificationinterface.h b/src/zmq/zmqnotificationinterface.h
index dee926ea5e..a0cc26a162 100644
--- a/src/zmq/zmqnotificationinterface.h
+++ b/src/zmq/zmqnotificationinterface.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2015-2017 The Bitcoin Core developers
+// Copyright (c) 2015-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -18,6 +18,8 @@ class CZMQNotificationInterface final : public CValidationInterface
public:
virtual ~CZMQNotificationInterface();
+ std::list<const CZMQAbstractNotifier*> GetActiveNotifiers() const;
+
static CZMQNotificationInterface* Create();
protected:
@@ -37,4 +39,6 @@ private:
std::list<CZMQAbstractNotifier*> notifiers;
};
+extern CZMQNotificationInterface* g_zmq_notification_interface;
+
#endif // BITCOIN_ZMQ_ZMQNOTIFICATIONINTERFACE_H
diff --git a/src/zmq/zmqrpc.cpp b/src/zmq/zmqrpc.cpp
new file mode 100644
index 0000000000..4f88bf4eb9
--- /dev/null
+++ b/src/zmq/zmqrpc.cpp
@@ -0,0 +1,61 @@
+// Copyright (c) 2018 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 <zmq/zmqrpc.h>
+
+#include <rpc/server.h>
+#include <zmq/zmqabstractnotifier.h>
+#include <zmq/zmqnotificationinterface.h>
+
+#include <univalue.h>
+
+namespace {
+
+UniValue getzmqnotifications(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 0) {
+ throw std::runtime_error(
+ "getzmqnotifications\n"
+ "\nReturns information about the active ZeroMQ notifications.\n"
+ "\nResult:\n"
+ "[\n"
+ " { (json object)\n"
+ " \"type\": \"pubhashtx\", (string) Type of notification\n"
+ " \"address\": \"...\" (string) Address of the publisher\n"
+ " },\n"
+ " ...\n"
+ "]\n"
+ "\nExamples:\n"
+ + HelpExampleCli("getzmqnotifications", "")
+ + HelpExampleRpc("getzmqnotifications", "")
+ );
+ }
+
+ UniValue result(UniValue::VARR);
+ if (g_zmq_notification_interface != nullptr) {
+ for (const auto* n : g_zmq_notification_interface->GetActiveNotifiers()) {
+ UniValue obj(UniValue::VOBJ);
+ obj.pushKV("type", n->GetType());
+ obj.pushKV("address", n->GetAddress());
+ result.push_back(obj);
+ }
+ }
+
+ return result;
+}
+
+const CRPCCommand commands[] =
+{ // category name actor (function) argNames
+ // ----------------- ------------------------ ----------------------- ----------
+ { "zmq", "getzmqnotifications", &getzmqnotifications, {} },
+};
+
+} // anonymous namespace
+
+void RegisterZMQRPCCommands(CRPCTable& t)
+{
+ for (const auto& c : commands) {
+ t.appendCommand(c.name, &c);
+ }
+}
diff --git a/src/zmq/zmqrpc.h b/src/zmq/zmqrpc.h
new file mode 100644
index 0000000000..5a810a16fb
--- /dev/null
+++ b/src/zmq/zmqrpc.h
@@ -0,0 +1,12 @@
+// Copyright (c) 2018 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_ZMQ_ZMQRPC_H
+#define BITCOIN_ZMQ_ZMQRPC_H
+
+class CRPCTable;
+
+void RegisterZMQRPCCommands(CRPCTable& t);
+
+#endif // BITCOIN_ZMQ_ZMRRPC_H