diff options
Diffstat (limited to 'src')
203 files changed, 4694 insertions, 2685 deletions
diff --git a/src/.clang-format b/src/.clang-format index 2d2ee67035..38e19edf2c 100644 --- a/src/.clang-format +++ b/src/.clang-format @@ -12,7 +12,10 @@ AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: true BinPackParameters: false BreakBeforeBinaryOperators: false -BreakBeforeBraces: Linux +BreakBeforeBraces: Custom +BraceWrapping: + AfterClass: true + AfterFunction: true BreakBeforeTernaryOperators: false BreakConstructorInitializersBeforeComma: false ColumnLimit: 0 diff --git a/src/Makefile.am b/src/Makefile.am index 605c932120..04bd75a2a5 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -103,16 +103,18 @@ BITCOIN_CORE_H = \ fs.h \ httprpc.h \ httpserver.h \ + index/txindex.h \ indirectmap.h \ init.h \ - interface/handler.h \ - interface/node.h \ - interface/wallet.h \ + interfaces/handler.h \ + interfaces/node.h \ + interfaces/wallet.h \ key.h \ key_io.h \ keystore.h \ dbwrapper.h \ limitedmap.h \ + logging.h \ memusage.h \ merkleblock.h \ miner.h \ @@ -135,7 +137,6 @@ BITCOIN_CORE_H = \ rpc/client.h \ rpc/mining.h \ rpc/protocol.h \ - rpc/safemode.h \ rpc/server.h \ rpc/rawtransaction.h \ rpc/register.h \ @@ -172,7 +173,6 @@ BITCOIN_CORE_H = \ wallet/db.h \ wallet/feebumper.h \ wallet/fees.h \ - wallet/init.h \ wallet/rpcwallet.h \ wallet/wallet.h \ wallet/walletdb.h \ @@ -204,6 +204,7 @@ libbitcoin_server_a_SOURCES = \ consensus/tx_verify.cpp \ httprpc.cpp \ httpserver.cpp \ + index/txindex.cpp \ init.cpp \ dbwrapper.cpp \ merkleblock.cpp \ @@ -221,7 +222,6 @@ libbitcoin_server_a_SOURCES = \ rpc/misc.cpp \ rpc/net.cpp \ rpc/rawtransaction.cpp \ - rpc/safemode.cpp \ rpc/server.cpp \ script/sigcache.cpp \ timedata.cpp \ @@ -249,7 +249,7 @@ endif libbitcoin_wallet_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) libbitcoin_wallet_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_wallet_a_SOURCES = \ - interface/wallet.cpp \ + interfaces/wallet.cpp \ wallet/crypter.cpp \ wallet/db.cpp \ wallet/feebumper.cpp \ @@ -317,6 +317,7 @@ libbitcoin_consensus_a_SOURCES = \ script/script_error.cpp \ script/script_error.h \ serialize.h \ + span.h \ tinyformat.h \ uint256.cpp \ uint256.h \ @@ -362,8 +363,9 @@ libbitcoin_util_a_SOURCES = \ compat/glibcxx_sanity.cpp \ compat/strnlen.cpp \ fs.cpp \ - interface/handler.cpp \ - interface/node.cpp \ + interfaces/handler.cpp \ + interfaces/node.cpp \ + logging.cpp \ random.cpp \ rpc/protocol.cpp \ rpc/util.cpp \ diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include index 748c5b7887..3306dcf598 100644 --- a/src/Makefile.bench.include +++ b/src/Makefile.bench.include @@ -25,8 +25,6 @@ bench_bench_bitcoin_SOURCES = \ bench/verify_script.cpp \ bench/base58.cpp \ bench/lockedpool.cpp \ - bench/perf.cpp \ - bench/perf.h \ bench/prevector.cpp nodist_bench_bench_bitcoin_SOURCES = $(GENERATED_BENCH_FILES) diff --git a/src/Makefile.qttest.include b/src/Makefile.qttest.include index 1279152198..4b14212b2e 100644 --- a/src/Makefile.qttest.include +++ b/src/Makefile.qttest.include @@ -12,14 +12,17 @@ TEST_QT_MOC_CPP = \ if ENABLE_WALLET TEST_QT_MOC_CPP += \ + qt/test/moc_addressbooktests.cpp \ qt/test/moc_paymentservertests.cpp \ qt/test/moc_wallettests.cpp endif TEST_QT_H = \ + qt/test/addressbooktests.h \ qt/test/compattests.h \ qt/test/rpcnestedtests.h \ qt/test/uritests.h \ + qt/test/util.h \ qt/test/paymentrequestdata.h \ qt/test/paymentservertests.h \ qt/test/wallettests.h @@ -38,11 +41,13 @@ qt_test_test_bitcoin_qt_SOURCES = \ qt/test/rpcnestedtests.cpp \ qt/test/test_main.cpp \ qt/test/uritests.cpp \ + qt/test/util.cpp \ $(TEST_QT_H) \ $(TEST_BITCOIN_CPP) \ $(TEST_BITCOIN_H) if ENABLE_WALLET qt_test_test_bitcoin_qt_SOURCES += \ + qt/test/addressbooktests.cpp \ qt/test/paymentservertests.cpp \ qt/test/wallettests.cpp \ wallet/test/wallet_test_fixture.cpp diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 4d0819ab79..91d3a3d47c 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -2,7 +2,6 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. -TESTS += test/test_bitcoin bin_PROGRAMS += test/test_bitcoin noinst_PROGRAMS += test/test_bitcoin_fuzzy TEST_SRCDIR = test @@ -21,6 +20,11 @@ RAW_TEST_FILES = GENERATED_TEST_FILES = $(JSON_TEST_FILES:.json=.json.h) $(RAW_TEST_FILES:.raw=.raw.h) +BITCOIN_TEST_SUITE = \ + test/test_bitcoin_main.cpp \ + test/test_bitcoin.h \ + test/test_bitcoin.cpp + # test_bitcoin binary # BITCOIN_TESTS =\ test/arith_uint256_tests.cpp \ @@ -76,12 +80,10 @@ BITCOIN_TESTS =\ test/sigopcount_tests.cpp \ test/skiplist_tests.cpp \ test/streams_tests.cpp \ - test/test_bitcoin.cpp \ - test/test_bitcoin.h \ - test/test_bitcoin_main.cpp \ test/timedata_tests.cpp \ test/torcontrol_tests.cpp \ test/transaction_tests.cpp \ + test/txindex_tests.cpp \ test/txvalidation_tests.cpp \ test/txvalidationcache_tests.cpp \ test/versionbits_tests.cpp \ @@ -90,15 +92,17 @@ BITCOIN_TESTS =\ if ENABLE_WALLET BITCOIN_TESTS += \ - wallet/test/wallet_test_fixture.cpp \ - wallet/test/wallet_test_fixture.h \ wallet/test/accounting_tests.cpp \ wallet/test/wallet_tests.cpp \ - wallet/test/crypto_tests.cpp \ + wallet/test/wallet_crypto_tests.cpp \ wallet/test/coinselector_tests.cpp + +BITCOIN_TEST_SUITE += \ + wallet/test/wallet_test_fixture.cpp \ + wallet/test/wallet_test_fixture.h endif -test_test_bitcoin_SOURCES = $(BITCOIN_TESTS) $(JSON_TEST_FILES) $(RAW_TEST_FILES) +test_test_bitcoin_SOURCES = $(BITCOIN_TEST_SUITE) $(BITCOIN_TESTS) $(JSON_TEST_FILES) $(RAW_TEST_FILES) test_test_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(TESTDEFS) $(EVENT_CFLAGS) test_test_bitcoin_LDADD = if ENABLE_WALLET @@ -150,14 +154,20 @@ bitcoin_test_check: $(TEST_BINARY) FORCE bitcoin_test_clean : FORCE rm -f $(CLEAN_BITCOIN_TEST) $(test_test_bitcoin_OBJECTS) $(TEST_BINARY) -check-local: +check-local: $(BITCOIN_TESTS:.cpp=.cpp.test) @echo "Running test/util/bitcoin-util-test.py..." $(PYTHON) $(top_builddir)/test/util/bitcoin-util-test.py + @echo "Running test/util/rpcauth-test.py..." + $(PYTHON) $(top_builddir)/test/util/rpcauth-test.py $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C secp256k1 check if EMBEDDED_UNIVALUE $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C univalue check endif +%.cpp.test: %.cpp + @echo Running tests: `cat $< | grep "BOOST_FIXTURE_TEST_SUITE(\|BOOST_AUTO_TEST_SUITE(" | cut -d '(' -f 2 | cut -d ',' -f 1 | cut -d ')' -f 1` from $< + $(AM_V_at)$(TEST_BINARY) -l test_suite -t "`cat $< | grep "BOOST_FIXTURE_TEST_SUITE(\|BOOST_AUTO_TEST_SUITE(" | cut -d '(' -f 2 | cut -d ',' -f 1 | cut -d ')' -f 1`" > $<.log 2>&1 || (cat $<.log && false) + %.json.h: %.json @$(MKDIR_P) $(@D) @{ \ diff --git a/src/addrdb.cpp b/src/addrdb.cpp index e4620e63c6..59305ff187 100644 --- a/src/addrdb.cpp +++ b/src/addrdb.cpp @@ -49,7 +49,8 @@ bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data // Serialize if (!SerializeDB(fileout, data)) return false; - FileCommit(fileout.Get()); + if (!FileCommit(fileout.Get())) + return error("%s: Failed to flush file %s", __func__, pathTmp.string()); fileout.fclose(); // replace existing file, if any, with new file diff --git a/src/addrman.h b/src/addrman.h index 6dec3fe416..a36f7ea100 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -59,7 +59,7 @@ public: template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(*static_cast<CAddress*>(this)); + READWRITEAS(CAddress, *this); READWRITE(source); READWRITE(nLastSuccess); READWRITE(nAttempts); diff --git a/src/arith_uint256.cpp b/src/arith_uint256.cpp index 65de632306..c7ddb17eb0 100644 --- a/src/arith_uint256.cpp +++ b/src/arith_uint256.cpp @@ -69,16 +69,16 @@ base_uint<BITS>& base_uint<BITS>::operator*=(uint32_t b32) template <unsigned int BITS> base_uint<BITS>& base_uint<BITS>::operator*=(const base_uint& b) { - base_uint<BITS> a = *this; - *this = 0; + base_uint<BITS> a; for (int j = 0; j < WIDTH; j++) { uint64_t carry = 0; for (int i = 0; i + j < WIDTH; i++) { - uint64_t n = carry + pn[i + j] + (uint64_t)a.pn[j] * b.pn[i]; - pn[i + j] = n & 0xffffffff; + uint64_t n = carry + a.pn[i + j] + (uint64_t)pn[j] * b.pn[i]; + a.pn[i + j] = n & 0xffffffff; carry = n >> 32; } } + *this = a; return *this; } diff --git a/src/bench/bench.cpp b/src/bench/bench.cpp index 21329a5151..de3e57b04f 100644 --- a/src/bench/bench.cpp +++ b/src/bench/bench.cpp @@ -3,7 +3,6 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <bench/bench.h> -#include <bench/perf.h> #include <assert.h> #include <iostream> @@ -96,7 +95,6 @@ benchmark::BenchRunner::BenchRunner(std::string name, benchmark::BenchFunction f void benchmark::BenchRunner::RunAll(Printer& printer, uint64_t num_evals, double scaling, const std::string& filter, bool is_list_only) { - perf_init(); if (!std::ratio_less_equal<benchmark::clock::period, std::micro>::value) { std::cerr << "WARNING: Clock precision is worse than microsecond - benchmarks may be less accurate!\n"; } @@ -126,8 +124,6 @@ void benchmark::BenchRunner::RunAll(Printer& printer, uint64_t num_evals, double } printer.footer(); - - perf_fini(); } bool benchmark::State::UpdateTimer(const benchmark::time_point current_time) diff --git a/src/bench/bench_bitcoin.cpp b/src/bench/bench_bitcoin.cpp index 1d87883522..c1f3339830 100644 --- a/src/bench/bench_bitcoin.cpp +++ b/src/bench/bench_bitcoin.cpp @@ -46,7 +46,6 @@ main(int argc, char** argv) RandomInit(); ECC_Start(); SetupEnvironment(); - fPrintToDebugLog = false; // don't want to write to debug.log file int64_t evaluations = gArgs.GetArg("-evals", DEFAULT_BENCH_EVALUATIONS); std::string regex_filter = gArgs.GetArg("-filter", DEFAULT_BENCH_FILTER); diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp index 4b2a0e72fe..64ec056c4d 100644 --- a/src/bench/coin_selection.cpp +++ b/src/bench/coin_selection.cpp @@ -33,7 +33,7 @@ static void addCoin(const CAmount& nValue, const CWallet& wallet, std::vector<CO // (https://github.com/bitcoin/bitcoin/issues/7883#issuecomment-224807484) static void CoinSelection(benchmark::State& state) { - const CWallet wallet("dummy", CWalletDBWrapper::CreateDummy()); + const CWallet wallet("dummy", WalletDatabase::CreateDummy()); std::vector<COutput> vCoins; LOCK(wallet.cs_wallet); diff --git a/src/bench/mempool_eviction.cpp b/src/bench/mempool_eviction.cpp index cdda0bd9be..4c947a519f 100644 --- a/src/bench/mempool_eviction.cpp +++ b/src/bench/mempool_eviction.cpp @@ -9,16 +9,16 @@ #include <list> #include <vector> -static void AddTx(const CTransaction& tx, const CAmount& nFee, CTxMemPool& pool) +static void AddTx(const CTransactionRef& tx, const CAmount& nFee, CTxMemPool& pool) { int64_t nTime = 0; unsigned int nHeight = 1; bool spendsCoinbase = false; unsigned int sigOpCost = 4; LockPoints lp; - pool.addUnchecked(tx.GetHash(), CTxMemPoolEntry( - MakeTransactionRef(tx), nFee, nTime, nHeight, - spendsCoinbase, sigOpCost, lp)); + pool.addUnchecked(tx->GetHash(), CTxMemPoolEntry( + tx, nFee, nTime, nHeight, + spendsCoinbase, sigOpCost, lp)); } // Right now this is only testing eviction performance in an extremely small @@ -29,6 +29,7 @@ static void MempoolEviction(benchmark::State& state) CMutableTransaction tx1 = CMutableTransaction(); tx1.vin.resize(1); tx1.vin[0].scriptSig = CScript() << OP_1; + tx1.vin[0].scriptWitness.stack.push_back({1}); tx1.vout.resize(1); tx1.vout[0].scriptPubKey = CScript() << OP_1 << OP_EQUAL; tx1.vout[0].nValue = 10 * COIN; @@ -36,6 +37,7 @@ static void MempoolEviction(benchmark::State& state) CMutableTransaction tx2 = CMutableTransaction(); tx2.vin.resize(1); tx2.vin[0].scriptSig = CScript() << OP_2; + tx2.vin[0].scriptWitness.stack.push_back({2}); tx2.vout.resize(1); tx2.vout[0].scriptPubKey = CScript() << OP_2 << OP_EQUAL; tx2.vout[0].nValue = 10 * COIN; @@ -44,6 +46,7 @@ static void MempoolEviction(benchmark::State& state) tx3.vin.resize(1); tx3.vin[0].prevout = COutPoint(tx2.GetHash(), 0); tx3.vin[0].scriptSig = CScript() << OP_2; + tx3.vin[0].scriptWitness.stack.push_back({3}); tx3.vout.resize(1); tx3.vout[0].scriptPubKey = CScript() << OP_3 << OP_EQUAL; tx3.vout[0].nValue = 10 * COIN; @@ -52,8 +55,10 @@ static void MempoolEviction(benchmark::State& state) tx4.vin.resize(2); tx4.vin[0].prevout.SetNull(); tx4.vin[0].scriptSig = CScript() << OP_4; + tx4.vin[0].scriptWitness.stack.push_back({4}); tx4.vin[1].prevout.SetNull(); tx4.vin[1].scriptSig = CScript() << OP_4; + tx4.vin[1].scriptWitness.stack.push_back({4}); tx4.vout.resize(2); tx4.vout[0].scriptPubKey = CScript() << OP_4 << OP_EQUAL; tx4.vout[0].nValue = 10 * COIN; @@ -64,8 +69,10 @@ static void MempoolEviction(benchmark::State& state) tx5.vin.resize(2); tx5.vin[0].prevout = COutPoint(tx4.GetHash(), 0); tx5.vin[0].scriptSig = CScript() << OP_4; + tx5.vin[0].scriptWitness.stack.push_back({4}); tx5.vin[1].prevout.SetNull(); tx5.vin[1].scriptSig = CScript() << OP_5; + tx5.vin[1].scriptWitness.stack.push_back({5}); tx5.vout.resize(2); tx5.vout[0].scriptPubKey = CScript() << OP_5 << OP_EQUAL; tx5.vout[0].nValue = 10 * COIN; @@ -76,8 +83,10 @@ static void MempoolEviction(benchmark::State& state) tx6.vin.resize(2); tx6.vin[0].prevout = COutPoint(tx4.GetHash(), 1); tx6.vin[0].scriptSig = CScript() << OP_4; + tx6.vin[0].scriptWitness.stack.push_back({4}); tx6.vin[1].prevout.SetNull(); tx6.vin[1].scriptSig = CScript() << OP_6; + tx6.vin[1].scriptWitness.stack.push_back({6}); tx6.vout.resize(2); tx6.vout[0].scriptPubKey = CScript() << OP_6 << OP_EQUAL; tx6.vout[0].nValue = 10 * COIN; @@ -88,8 +97,10 @@ static void MempoolEviction(benchmark::State& state) tx7.vin.resize(2); tx7.vin[0].prevout = COutPoint(tx5.GetHash(), 0); tx7.vin[0].scriptSig = CScript() << OP_5; + tx7.vin[0].scriptWitness.stack.push_back({5}); tx7.vin[1].prevout = COutPoint(tx6.GetHash(), 0); tx7.vin[1].scriptSig = CScript() << OP_6; + tx7.vin[1].scriptWitness.stack.push_back({6}); tx7.vout.resize(2); tx7.vout[0].scriptPubKey = CScript() << OP_7 << OP_EQUAL; tx7.vout[0].nValue = 10 * COIN; @@ -97,15 +108,23 @@ static void MempoolEviction(benchmark::State& state) tx7.vout[1].nValue = 10 * COIN; CTxMemPool pool; + // Create transaction references outside the "hot loop" + const CTransactionRef tx1_r{MakeTransactionRef(tx1)}; + const CTransactionRef tx2_r{MakeTransactionRef(tx2)}; + const CTransactionRef tx3_r{MakeTransactionRef(tx3)}; + const CTransactionRef tx4_r{MakeTransactionRef(tx4)}; + const CTransactionRef tx5_r{MakeTransactionRef(tx5)}; + const CTransactionRef tx6_r{MakeTransactionRef(tx6)}; + const CTransactionRef tx7_r{MakeTransactionRef(tx7)}; while (state.KeepRunning()) { - AddTx(tx1, 10000LL, pool); - AddTx(tx2, 5000LL, pool); - AddTx(tx3, 20000LL, pool); - AddTx(tx4, 7000LL, pool); - AddTx(tx5, 1000LL, pool); - AddTx(tx6, 1100LL, pool); - AddTx(tx7, 9000LL, pool); + AddTx(tx1_r, 10000LL, pool); + AddTx(tx2_r, 5000LL, pool); + AddTx(tx3_r, 20000LL, pool); + AddTx(tx4_r, 7000LL, pool); + AddTx(tx5_r, 1000LL, pool); + AddTx(tx6_r, 1100LL, pool); + AddTx(tx7_r, 9000LL, pool); pool.TrimToSize(pool.DynamicMemoryUsage() * 3 / 4); pool.TrimToSize(GetVirtualTransactionSize(tx1)); } diff --git a/src/bench/perf.cpp b/src/bench/perf.cpp deleted file mode 100644 index f92d08c56e..0000000000 --- a/src/bench/perf.cpp +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) 2016-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/perf.h> - -#if defined(__i386__) || defined(__x86_64__) - -/* These architectures support querying the cycle counter - * from user space, no need for any syscall overhead. - */ -void perf_init(void) { } -void perf_fini(void) { } - -#elif defined(__linux__) - -#include <unistd.h> -#include <sys/syscall.h> -#include <linux/perf_event.h> - -static int fd = -1; -static struct perf_event_attr attr; - -void perf_init(void) -{ - attr.type = PERF_TYPE_HARDWARE; - attr.config = PERF_COUNT_HW_CPU_CYCLES; - fd = syscall(__NR_perf_event_open, &attr, 0, -1, -1, 0); -} - -void perf_fini(void) -{ - if (fd != -1) { - close(fd); - } -} - -uint64_t perf_cpucycles(void) -{ - uint64_t result = 0; - if (fd == -1 || read(fd, &result, sizeof(result)) < (ssize_t)sizeof(result)) { - return 0; - } - return result; -} - -#else /* Unhandled platform */ - -void perf_init(void) { } -void perf_fini(void) { } -uint64_t perf_cpucycles(void) { return 0; } - -#endif diff --git a/src/bench/perf.h b/src/bench/perf.h deleted file mode 100644 index 73ea8b9647..0000000000 --- a/src/bench/perf.h +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) 2016 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -/** Functions for measurement of CPU cycles */ -#ifndef BITCOIN_BENCH_PERF_H -#define BITCOIN_BENCH_PERF_H - -#include <stdint.h> - -#if defined(__i386__) - -static inline uint64_t perf_cpucycles(void) -{ - uint64_t x; - __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x)); - return x; -} - -#elif defined(__x86_64__) - -static inline uint64_t perf_cpucycles(void) -{ - uint32_t hi, lo; - __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi)); - return ((uint64_t)lo)|(((uint64_t)hi)<<32); -} -#else - -uint64_t perf_cpucycles(void); - -#endif - -void perf_init(void); -void perf_fini(void); - -#endif // BITCOIN_BENCH_PERF_H diff --git a/src/bench/verify_script.cpp b/src/bench/verify_script.cpp index 705fa368a5..4100519d48 100644 --- a/src/bench/verify_script.cpp +++ b/src/bench/verify_script.cpp @@ -71,7 +71,7 @@ static void VerifyScriptBench(benchmark::State& state) CScript scriptPubKey = CScript() << witnessversion << ToByteVector(pubkeyHash); CScript scriptSig; CScript witScriptPubkey = CScript() << OP_DUP << OP_HASH160 << ToByteVector(pubkeyHash) << OP_EQUALVERIFY << OP_CHECKSIG; - CTransaction txCredit = BuildCreditingTransaction(scriptPubKey); + const CMutableTransaction& txCredit = BuildCreditingTransaction(scriptPubKey); CMutableTransaction txSpend = BuildSpendingTransaction(scriptSig, txCredit); CScriptWitness& witness = txSpend.vin[0].scriptWitness; witness.stack.emplace_back(); diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index 618b0d16bc..26a9231308 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -114,7 +114,7 @@ static int AppInitRPC(int argc, char* argv[]) } // Check for -testnet or -regtest parameter (BaseParams() calls are only valid after this clause) try { - SelectBaseParams(ChainNameFromCommandLine()); + SelectBaseParams(gArgs.GetChainName()); } catch (const std::exception& e) { fprintf(stderr, "Error: %s\n", e.what()); return EXIT_FAILURE; diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index 65f8177daf..07ad09ea7b 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -44,7 +44,7 @@ static int AppInitRawTx(int argc, char* argv[]) // Check for -testnet or -regtest parameter (Params() calls are only valid after this clause) try { - SelectParams(ChainNameFromCommandLine()); + SelectParams(gArgs.GetChainName()); } catch (const std::exception& e) { fprintf(stderr, "Error: %s\n", e.what()); return EXIT_FAILURE; @@ -545,12 +545,10 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr) if (!findSighashFlags(nHashType, flagStr)) throw std::runtime_error("unknown sighash flag/sign option"); - std::vector<CTransaction> txVariants; - txVariants.push_back(tx); - // mergedTx will end up with all the signatures; it // starts as a clone of the raw tx: - CMutableTransaction mergedTx(txVariants[0]); + CMutableTransaction mergedTx{tx}; + const CTransaction txv{tx}; CCoinsView viewDummy; CCoinsViewCache view(&viewDummy); @@ -633,7 +631,7 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr) // Sign what we can: for (unsigned int i = 0; i < mergedTx.vin.size(); i++) { - CTxIn& txin = mergedTx.vin[i]; + const CTxIn& txin = mergedTx.vin[i]; const Coin& coin = view.AccessCoin(txin.prevout); if (coin.IsSpent()) { continue; @@ -644,11 +642,10 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr) SignatureData sigdata; // Only sign SIGHASH_SINGLE if there's a corresponding output: if (!fHashSingle || (i < mergedTx.vout.size())) - ProduceSignature(MutableTransactionSignatureCreator(&keystore, &mergedTx, i, amount, nHashType), prevPubKey, sigdata); + ProduceSignature(keystore, MutableTransactionSignatureCreator(&mergedTx, i, amount, nHashType), prevPubKey, sigdata); // ... and merge in other signatures: - for (const CTransaction& txv : txVariants) - sigdata = CombineSignatures(prevPubKey, MutableTransactionSignatureChecker(&mergedTx, i, amount), sigdata, DataFromTransaction(txv, i)); + sigdata = CombineSignatures(prevPubKey, MutableTransactionSignatureChecker(&mergedTx, i, amount), sigdata, DataFromTransaction(txv, i)); UpdateTransaction(mergedTx, i, sigdata); } diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index 58518d611f..0dc2dfbf7d 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -18,9 +18,6 @@ #include <httpserver.h> #include <httprpc.h> #include <utilstrencodings.h> -#if ENABLE_WALLET -#include <wallet/init.h> -#endif #include <walletinitinterface.h> #include <boost/thread.hpp> @@ -45,12 +42,9 @@ void WaitForShutdown() { - bool fShutdown = ShutdownRequested(); - // Tell the main threads to shutdown. - while (!fShutdown) + while (!ShutdownRequested()) { MilliSleep(200); - fShutdown = ShutdownRequested(); } Interrupt(); } @@ -63,12 +57,6 @@ bool AppInit(int argc, char* argv[]) { bool fRet = false; -#if ENABLE_WALLET - g_wallet_init_interface.reset(new WalletInit); -#else - g_wallet_init_interface.reset(new DummyWalletInit); -#endif - // // Parameters // @@ -111,7 +99,7 @@ bool AppInit(int argc, char* argv[]) } // Check for -testnet or -regtest parameter (Params() calls are only valid after this clause) try { - SelectParams(ChainNameFromCommandLine()); + SelectParams(gArgs.GetChainName()); } catch (const std::exception& e) { fprintf(stderr, "Error: %s\n", e.what()); return false; @@ -148,6 +136,10 @@ bool AppInit(int argc, char* argv[]) if (gArgs.GetBoolArg("-daemon", false)) { #if HAVE_DECL_DAEMON +#if defined(MAC_OSX) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif fprintf(stdout, "Bitcoin server starting\n"); // Daemonize @@ -155,6 +147,9 @@ bool AppInit(int argc, char* argv[]) fprintf(stderr, "Error: daemon() failed: %s\n", strerror(errno)); return false; } +#if defined(MAC_OSX) +#pragma GCC diagnostic pop +#endif #else fprintf(stderr, "Error: -daemon is not supported on this operating system\n"); return false; diff --git a/src/chainparams.cpp b/src/chainparams.cpp index adf8e6ae5b..121d95af90 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -11,7 +11,6 @@ #include <utilstrencodings.h> #include <assert.h> -#include <memory> #include <chainparamsseeds.h> @@ -76,7 +75,7 @@ public: CMainParams() { strNetworkID = "main"; consensus.nSubsidyHalvingInterval = 210000; - consensus.BIP16Height = 173805; // 00000000000000ce80a7e057163a4db1d5ad7b20fb6f598c9597b9665c8fb0d4 - April 1, 2012 + consensus.BIP16Exception = uint256S("0x00000000000002dc756eebf4f49723ed8d30cc28a5f108eb94b1ba88ac4f9c22"); consensus.BIP34Height = 227931; consensus.BIP34Hash = uint256S("0x000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8"); consensus.BIP65Height = 388381; // 000000000000000004c2b624ed5d7756c508d90fd0da2c7c679febfa6c4735f0 @@ -191,7 +190,7 @@ public: CTestNetParams() { strNetworkID = "test"; consensus.nSubsidyHalvingInterval = 210000; - consensus.BIP16Height = 514; // 00000000040b4e986385315e14bee30ad876d8b47f748025b26683116d21aa65 + consensus.BIP16Exception = uint256S("0x00000000dd30457c001f4095d208cc1296b0eed002427aa599874af7a432b105"); consensus.BIP34Height = 21111; consensus.BIP34Hash = uint256S("0x0000000023b3a96d3484e5abb3755c413e7d41500f8e2a5c3f0dd01299cd8ef8"); consensus.BIP65Height = 581885; // 00000000007f6655f22f98e72ed80d8b06dc761d5da09df0fa1dc4be4f861eb6 @@ -284,7 +283,7 @@ public: CRegTestParams() { strNetworkID = "regtest"; consensus.nSubsidyHalvingInterval = 150; - consensus.BIP16Height = 0; // always enforce P2SH BIP16 on regtest + consensus.BIP16Exception = uint256(); consensus.BIP34Height = 100000000; // BIP34 has not activated on regtest (far in the future so block v1 are not rejected in tests) consensus.BIP34Hash = uint256(); consensus.BIP65Height = 1351; // BIP65 activated on regtest (Used in rpc activation tests) diff --git a/src/chainparams.h b/src/chainparams.h index 6b1f813afb..dd029b9d5b 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -25,6 +25,12 @@ struct CCheckpointData { MapCheckpoints mapCheckpoints; }; +/** + * Holds various statistics on transactions within a chain. Used to estimate + * verification progress during chain sync. + * + * See also: CChainParams::TxData, GuessVerificationProgress. + */ struct ChainTxData { int64_t nTime; int64_t nTxCount; diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp index 726616e650..3ef9c2cfe5 100644 --- a/src/chainparamsbase.cpp +++ b/src/chainparamsbase.cpp @@ -9,7 +9,6 @@ #include <util.h> #include <assert.h> -#include <memory> const std::string CBaseChainParams::MAIN = "main"; const std::string CBaseChainParams::TESTNET = "test"; @@ -48,18 +47,5 @@ std::unique_ptr<CBaseChainParams> CreateBaseChainParams(const std::string& chain void SelectBaseParams(const std::string& chain) { globalChainBaseParams = CreateBaseChainParams(chain); -} - -std::string ChainNameFromCommandLine() -{ - bool fRegTest = gArgs.GetBoolArg("-regtest", false); - bool fTestNet = gArgs.GetBoolArg("-testnet", false); - - if (fTestNet && fRegTest) - throw std::runtime_error("Invalid combination of -regtest and -testnet."); - if (fRegTest) - return CBaseChainParams::REGTEST; - if (fTestNet) - return CBaseChainParams::TESTNET; - return CBaseChainParams::MAIN; + gArgs.SelectConfigNetwork(chain); } diff --git a/src/chainparamsbase.h b/src/chainparamsbase.h index 2cb860380e..5b11f36770 100644 --- a/src/chainparamsbase.h +++ b/src/chainparamsbase.h @@ -54,10 +54,4 @@ const CBaseChainParams& BaseParams(); /** Sets the params returned by Params() to those for the given network. */ void SelectBaseParams(const std::string& chain); -/** - * Looks for -regtest, -testnet and returns the appropriate BIP70 chain name. - * @return CBaseChainParams::MAX_NETWORK_TYPES if an invalid combination is given. CBaseChainParams::MAIN by default. - */ -std::string ChainNameFromCommandLine(); - #endif // BITCOIN_CHAINPARAMSBASE_H diff --git a/src/compat.h b/src/compat.h index 8a0f901304..920b3f776d 100644 --- a/src/compat.h +++ b/src/compat.h @@ -96,6 +96,12 @@ typedef int32_t ssize_t; size_t strnlen( const char *start, size_t max_len); #endif // HAVE_DECL_STRNLEN +#ifndef WIN32 +typedef void* sockopt_arg_type; +#else +typedef char* sockopt_arg_type; +#endif + bool static inline IsSelectableSocket(const SOCKET& s) { #ifdef WIN32 return true; diff --git a/src/compat/endian.h b/src/compat/endian.h index e5c7e50223..4f244c3930 100644 --- a/src/compat/endian.h +++ b/src/compat/endian.h @@ -19,6 +19,51 @@ #include <sys/endian.h> #endif +#ifndef HAVE_CONFIG_H +// While not technically a supported configuration, defaulting to defining these +// DECLs when we were compiled without autotools makes it easier for other build +// systems to build things like libbitcoinconsensus for strange targets. +#ifdef htobe16 +#define HAVE_DECL_HTOBE16 1 +#endif +#ifdef htole16 +#define HAVE_DECL_HTOLE16 1 +#endif +#ifdef be16toh +#define HAVE_DECL_BE16TOH 1 +#endif +#ifdef le16toh +#define HAVE_DECL_LE16TOH 1 +#endif + +#ifdef htobe32 +#define HAVE_DECL_HTOBE32 1 +#endif +#ifdef htole32 +#define HAVE_DECL_HTOLE32 1 +#endif +#ifdef be32toh +#define HAVE_DECL_BE32TOH 1 +#endif +#ifdef le32toh +#define HAVE_DECL_LE32TOH 1 +#endif + +#ifdef htobe64 +#define HAVE_DECL_HTOBE64 1 +#endif +#ifdef htole64 +#define HAVE_DECL_HTOLE64 1 +#endif +#ifdef be64toh +#define HAVE_DECL_BE64TOH 1 +#endif +#ifdef le64toh +#define HAVE_DECL_LE64TOH 1 +#endif + +#endif // HAVE_CONFIG_H + #if defined(WORDS_BIGENDIAN) #if HAVE_DECL_HTOBE16 == 0 diff --git a/src/compressor.h b/src/compressor.h index 561c8e66d0..6bd68529d4 100644 --- a/src/compressor.h +++ b/src/compressor.h @@ -9,6 +9,7 @@ #include <primitives/transaction.h> #include <script/script.h> #include <serialize.h> +#include <span.h> class CKeyID; class CPubKey; @@ -51,12 +52,12 @@ public: void Serialize(Stream &s) const { std::vector<unsigned char> compr; if (CompressScript(script, compr)) { - s << CFlatData(compr); + s << MakeSpan(compr); return; } unsigned int nSize = script.size() + nSpecialScripts; s << VARINT(nSize); - s << CFlatData(script); + s << MakeSpan(script); } template<typename Stream> @@ -65,7 +66,7 @@ public: s >> VARINT(nSize); if (nSize < nSpecialScripts) { std::vector<unsigned char> vch(GetSpecialScriptSize(nSize), 0x00); - s >> CFlatData(vch); + s >> MakeSpan(vch); DecompressScript(script, nSize, vch); return; } @@ -76,7 +77,7 @@ public: s.ignore(nSize); } else { script.resize(nSize); - s >> CFlatData(script); + s >> MakeSpan(script); } } }; diff --git a/src/consensus/params.h b/src/consensus/params.h index 4ef808c856..0559304fc2 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -49,8 +49,8 @@ struct BIP9Deployment { struct Params { uint256 hashGenesisBlock; int nSubsidyHalvingInterval; - /** Block height at which BIP16 becomes active */ - int BIP16Height; + /* Block hash that is excepted from BIP16 enforcement */ + uint256 BIP16Exception; /** Block height and hash at which BIP34 becomes active */ int BIP34Height; uint256 BIP34Hash; diff --git a/src/core_write.cpp b/src/core_write.cpp index 54b18a4931..ee6737201b 100644 --- a/src/core_write.cpp +++ b/src/core_write.cpp @@ -34,7 +34,7 @@ std::string FormatScript(const CScript& script) while (it != script.end()) { CScript::const_iterator it2 = it; std::vector<unsigned char> vch; - if (script.GetOp2(it, op, &vch)) { + if (script.GetOp(it, op, vch)) { if (op == OP_0) { ret += "0 "; continue; @@ -161,6 +161,7 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, entry.pushKV("version", tx.nVersion); entry.pushKV("size", (int)::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION)); entry.pushKV("vsize", (GetTransactionWeight(tx) + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR); + entry.pushKV("weight", GetTransactionWeight(tx)); entry.pushKV("locktime", (int64_t)tx.nLockTime); UniValue vin(UniValue::VARR); diff --git a/src/cuckoocache.h b/src/cuckoocache.h index d1de712024..15f6873961 100644 --- a/src/cuckoocache.h +++ b/src/cuckoocache.h @@ -242,14 +242,14 @@ private: */ inline std::array<uint32_t, 8> compute_hashes(const Element& e) const { - return {{(uint32_t)((hash_function.template operator()<0>(e) * (uint64_t)size) >> 32), - (uint32_t)((hash_function.template operator()<1>(e) * (uint64_t)size) >> 32), - (uint32_t)((hash_function.template operator()<2>(e) * (uint64_t)size) >> 32), - (uint32_t)((hash_function.template operator()<3>(e) * (uint64_t)size) >> 32), - (uint32_t)((hash_function.template operator()<4>(e) * (uint64_t)size) >> 32), - (uint32_t)((hash_function.template operator()<5>(e) * (uint64_t)size) >> 32), - (uint32_t)((hash_function.template operator()<6>(e) * (uint64_t)size) >> 32), - (uint32_t)((hash_function.template operator()<7>(e) * (uint64_t)size) >> 32)}}; + return {{(uint32_t)(((uint64_t)hash_function.template operator()<0>(e) * (uint64_t)size) >> 32), + (uint32_t)(((uint64_t)hash_function.template operator()<1>(e) * (uint64_t)size) >> 32), + (uint32_t)(((uint64_t)hash_function.template operator()<2>(e) * (uint64_t)size) >> 32), + (uint32_t)(((uint64_t)hash_function.template operator()<3>(e) * (uint64_t)size) >> 32), + (uint32_t)(((uint64_t)hash_function.template operator()<4>(e) * (uint64_t)size) >> 32), + (uint32_t)(((uint64_t)hash_function.template operator()<5>(e) * (uint64_t)size) >> 32), + (uint32_t)(((uint64_t)hash_function.template operator()<6>(e) * (uint64_t)size) >> 32), + (uint32_t)(((uint64_t)hash_function.template operator()<7>(e) * (uint64_t)size) >> 32)}}; } /* end diff --git a/src/dbwrapper.cpp b/src/dbwrapper.cpp index ca446c92fe..e401b5fb1b 100644 --- a/src/dbwrapper.cpp +++ b/src/dbwrapper.cpp @@ -63,7 +63,7 @@ public: assert(p <= limit); base[std::min(bufsize - 1, (int)(p - base))] = '\0'; - LogPrintf("leveldb: %s", base); + LogPrintf("leveldb: %s", base); /* Continued */ if (base != buffer) { delete[] base; } diff --git a/src/dbwrapper.h b/src/dbwrapper.h index 6f80eedc7a..2a5e0cab00 100644 --- a/src/dbwrapper.h +++ b/src/dbwrapper.h @@ -224,6 +224,9 @@ public: CDBWrapper(const fs::path& path, size_t nCacheSize, bool fMemory = false, bool fWipe = false, bool obfuscate = false); ~CDBWrapper(); + CDBWrapper(const CDBWrapper&) = delete; + CDBWrapper& operator=(const CDBWrapper&) = delete; + template <typename K, typename V> bool Read(const K& key, V& value) const { diff --git a/src/httpserver.cpp b/src/httpserver.cpp index b8b338dfbc..bd08b04c0f 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -364,8 +364,8 @@ bool InitHTTPServer() // Update libevent's log handling. Returns false if our version of // libevent doesn't support debug logging, in which case we should // clear the BCLog::LIBEVENT flag. - if (!UpdateHTTPServerLogging(logCategories & BCLog::LIBEVENT)) { - logCategories &= ~BCLog::LIBEVENT; + if (!UpdateHTTPServerLogging(g_logger->WillLogCategory(BCLog::LIBEVENT))) { + g_logger->DisableCategory(BCLog::LIBEVENT); } #ifdef WIN32 diff --git a/src/httpserver.h b/src/httpserver.h index f17be65962..8132c887b5 100644 --- a/src/httpserver.h +++ b/src/httpserver.h @@ -32,7 +32,7 @@ void InterruptHTTPServer(); /** Stop HTTP server */ void StopHTTPServer(); -/** Change logging level for libevent. Removes BCLog::LIBEVENT from logCategories if +/** Change logging level for libevent. Removes BCLog::LIBEVENT from log categories if * libevent doesn't support debug logging.*/ bool UpdateHTTPServerLogging(bool enable); diff --git a/src/index/txindex.cpp b/src/index/txindex.cpp new file mode 100644 index 0000000000..0bb553ee6a --- /dev/null +++ b/src/index/txindex.cpp @@ -0,0 +1,309 @@ +// Copyright (c) 2017-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 <chainparams.h> +#include <index/txindex.h> +#include <init.h> +#include <tinyformat.h> +#include <ui_interface.h> +#include <util.h> +#include <validation.h> +#include <warnings.h> + +constexpr int64_t SYNC_LOG_INTERVAL = 30; // seconds +constexpr int64_t SYNC_LOCATOR_WRITE_INTERVAL = 30; // seconds + +std::unique_ptr<TxIndex> g_txindex; + +template<typename... Args> +static void FatalError(const char* fmt, const Args&... args) +{ + std::string strMessage = tfm::format(fmt, args...); + SetMiscWarning(strMessage); + LogPrintf("*** %s\n", strMessage); + uiInterface.ThreadSafeMessageBox( + "Error: A fatal internal error occurred, see debug.log for details", + "", CClientUIInterface::MSG_ERROR); + StartShutdown(); +} + +TxIndex::TxIndex(std::unique_ptr<TxIndexDB> db) : + m_db(std::move(db)), m_synced(false), m_best_block_index(nullptr) +{} + +TxIndex::~TxIndex() +{ + Interrupt(); + Stop(); +} + +bool TxIndex::Init() +{ + LOCK(cs_main); + + // Attempt to migrate txindex from the old database to the new one. Even if + // chain_tip is null, the node could be reindexing and we still want to + // delete txindex records in the old database. + if (!m_db->MigrateData(*pblocktree, chainActive.GetLocator())) { + return false; + } + + CBlockLocator locator; + if (!m_db->ReadBestBlock(locator)) { + locator.SetNull(); + } + + m_best_block_index = FindForkInGlobalIndex(chainActive, locator); + m_synced = m_best_block_index.load() == chainActive.Tip(); + return true; +} + +static const CBlockIndex* NextSyncBlock(const CBlockIndex* pindex_prev) +{ + AssertLockHeld(cs_main); + + if (!pindex_prev) { + return chainActive.Genesis(); + } + + const CBlockIndex* pindex = chainActive.Next(pindex_prev); + if (pindex) { + return pindex; + } + + return chainActive.Next(chainActive.FindFork(pindex_prev)); +} + +void TxIndex::ThreadSync() +{ + const CBlockIndex* pindex = m_best_block_index.load(); + if (!m_synced) { + auto& consensus_params = Params().GetConsensus(); + + int64_t last_log_time = 0; + int64_t last_locator_write_time = 0; + while (true) { + if (m_interrupt) { + WriteBestBlock(pindex); + return; + } + + { + LOCK(cs_main); + const CBlockIndex* pindex_next = NextSyncBlock(pindex); + if (!pindex_next) { + WriteBestBlock(pindex); + m_best_block_index = pindex; + m_synced = true; + break; + } + pindex = pindex_next; + } + + int64_t current_time = GetTime(); + if (last_log_time + SYNC_LOG_INTERVAL < current_time) { + LogPrintf("Syncing txindex with block chain from height %d\n", pindex->nHeight); + last_log_time = current_time; + } + + if (last_locator_write_time + SYNC_LOCATOR_WRITE_INTERVAL < current_time) { + WriteBestBlock(pindex); + last_locator_write_time = current_time; + } + + CBlock block; + if (!ReadBlockFromDisk(block, pindex, consensus_params)) { + FatalError("%s: Failed to read block %s from disk", + __func__, pindex->GetBlockHash().ToString()); + return; + } + if (!WriteBlock(block, pindex)) { + FatalError("%s: Failed to write block %s to tx index database", + __func__, pindex->GetBlockHash().ToString()); + return; + } + } + } + + if (pindex) { + LogPrintf("txindex is enabled at height %d\n", pindex->nHeight); + } else { + LogPrintf("txindex is enabled\n"); + } +} + +bool TxIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex) +{ + CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size())); + std::vector<std::pair<uint256, CDiskTxPos>> vPos; + vPos.reserve(block.vtx.size()); + for (const auto& tx : block.vtx) { + vPos.emplace_back(tx->GetHash(), pos); + pos.nTxOffset += ::GetSerializeSize(*tx, SER_DISK, CLIENT_VERSION); + } + return m_db->WriteTxs(vPos); +} + +bool TxIndex::WriteBestBlock(const CBlockIndex* block_index) +{ + LOCK(cs_main); + if (!m_db->WriteBestBlock(chainActive.GetLocator(block_index))) { + return error("%s: Failed to write locator to disk", __func__); + } + return true; +} + +void TxIndex::BlockConnected(const std::shared_ptr<const CBlock>& block, const CBlockIndex* pindex, + const std::vector<CTransactionRef>& txn_conflicted) +{ + if (!m_synced) { + return; + } + + const CBlockIndex* best_block_index = m_best_block_index.load(); + if (!best_block_index) { + if (pindex->nHeight != 0) { + FatalError("%s: First block connected is not the genesis block (height=%d)", + __func__, pindex->nHeight); + return; + } + } else { + // Ensure block connects to an ancestor of the current best block. This should be the case + // most of the time, but may not be immediately after the the sync thread catches up and sets + // m_synced. Consider the case where there is a reorg and the blocks on the stale branch are + // in the ValidationInterface queue backlog even after the sync thread has caught up to the + // new chain tip. In this unlikely event, log a warning and let the queue clear. + if (best_block_index->GetAncestor(pindex->nHeight - 1) != pindex->pprev) { + LogPrintf("%s: WARNING: Block %s does not connect to an ancestor of " /* Continued */ + "known best chain (tip=%s); not updating txindex\n", + __func__, pindex->GetBlockHash().ToString(), + best_block_index->GetBlockHash().ToString()); + return; + } + } + + if (WriteBlock(*block, pindex)) { + m_best_block_index = pindex; + } else { + FatalError("%s: Failed to write block %s to txindex", + __func__, pindex->GetBlockHash().ToString()); + return; + } +} + +void TxIndex::ChainStateFlushed(const CBlockLocator& locator) +{ + if (!m_synced) { + return; + } + + const uint256& locator_tip_hash = locator.vHave.front(); + const CBlockIndex* locator_tip_index; + { + LOCK(cs_main); + locator_tip_index = LookupBlockIndex(locator_tip_hash); + } + + if (!locator_tip_index) { + FatalError("%s: First block (hash=%s) in locator was not found", + __func__, locator_tip_hash.ToString()); + return; + } + + // This checks that ChainStateFlushed callbacks are received after BlockConnected. The check may fail + // immediately after the the sync thread catches up and sets m_synced. Consider the case where + // there is a reorg and the blocks on the stale branch are in the ValidationInterface queue + // backlog even after the sync thread has caught up to the new chain tip. In this unlikely + // event, log a warning and let the queue clear. + const CBlockIndex* best_block_index = m_best_block_index.load(); + if (best_block_index->GetAncestor(locator_tip_index->nHeight) != locator_tip_index) { + LogPrintf("%s: WARNING: Locator contains block (hash=%s) not on known best " /* Continued */ + "chain (tip=%s); not writing txindex locator\n", + __func__, locator_tip_hash.ToString(), + best_block_index->GetBlockHash().ToString()); + return; + } + + if (!m_db->WriteBestBlock(locator)) { + error("%s: Failed to write locator to disk", __func__); + } +} + +bool TxIndex::BlockUntilSyncedToCurrentChain() +{ + AssertLockNotHeld(cs_main); + + if (!m_synced) { + return false; + } + + { + // Skip the queue-draining stuff if we know we're caught up with + // chainActive.Tip(). + LOCK(cs_main); + const CBlockIndex* chain_tip = chainActive.Tip(); + const CBlockIndex* best_block_index = m_best_block_index.load(); + if (best_block_index->GetAncestor(chain_tip->nHeight) == chain_tip) { + return true; + } + } + + LogPrintf("%s: txindex is catching up on block notifications\n", __func__); + SyncWithValidationInterfaceQueue(); + return true; +} + +bool TxIndex::FindTx(const uint256& tx_hash, uint256& block_hash, CTransactionRef& tx) const +{ + CDiskTxPos postx; + if (!m_db->ReadTxPos(tx_hash, postx)) { + return false; + } + + CAutoFile file(OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION); + if (file.IsNull()) { + return error("%s: OpenBlockFile failed", __func__); + } + CBlockHeader header; + try { + file >> header; + fseek(file.Get(), postx.nTxOffset, SEEK_CUR); + file >> tx; + } catch (const std::exception& e) { + return error("%s: Deserialize or I/O error - %s", __func__, e.what()); + } + if (tx->GetHash() != tx_hash) { + return error("%s: txid mismatch", __func__); + } + block_hash = header.GetHash(); + return true; +} + +void TxIndex::Interrupt() +{ + m_interrupt(); +} + +void TxIndex::Start() +{ + // Need to register this ValidationInterface before running Init(), so that + // callbacks are not missed if Init sets m_synced to true. + RegisterValidationInterface(this); + if (!Init()) { + FatalError("%s: txindex failed to initialize", __func__); + return; + } + + m_thread_sync = std::thread(&TraceThread<std::function<void()>>, "txindex", + std::bind(&TxIndex::ThreadSync, this)); +} + +void TxIndex::Stop() +{ + UnregisterValidationInterface(this); + + if (m_thread_sync.joinable()) { + m_thread_sync.join(); + } +} diff --git a/src/index/txindex.h b/src/index/txindex.h new file mode 100644 index 0000000000..4937bd64e9 --- /dev/null +++ b/src/index/txindex.h @@ -0,0 +1,94 @@ +// Copyright (c) 2017-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_INDEX_TXINDEX_H +#define BITCOIN_INDEX_TXINDEX_H + +#include <primitives/block.h> +#include <primitives/transaction.h> +#include <threadinterrupt.h> +#include <txdb.h> +#include <uint256.h> +#include <validationinterface.h> + +class CBlockIndex; + +/** + * TxIndex is used to look up transactions included in the blockchain by hash. + * The index is written to a LevelDB database and records the filesystem + * location of each transaction by transaction hash. + */ +class TxIndex final : public CValidationInterface +{ +private: + const std::unique_ptr<TxIndexDB> m_db; + + /// Whether the index is in sync with the main chain. The flag is flipped + /// from false to true once, after which point this starts processing + /// ValidationInterface notifications to stay in sync. + std::atomic<bool> m_synced; + + /// The last block in the chain that the TxIndex is in sync with. + std::atomic<const CBlockIndex*> m_best_block_index; + + std::thread m_thread_sync; + CThreadInterrupt m_interrupt; + + /// Initialize internal state from the database and block index. + bool Init(); + + /// Sync the tx index with the block index starting from the current best + /// block. Intended to be run in its own thread, m_thread_sync, and can be + /// interrupted with m_interrupt. Once the txindex gets in sync, the + /// m_synced flag is set and the BlockConnected ValidationInterface callback + /// takes over and the sync thread exits. + void ThreadSync(); + + /// Write update index entries for a newly connected block. + bool WriteBlock(const CBlock& block, const CBlockIndex* pindex); + + /// Write the current chain block locator to the DB. + bool WriteBestBlock(const CBlockIndex* block_index); + +protected: + void BlockConnected(const std::shared_ptr<const CBlock>& block, const CBlockIndex* pindex, + const std::vector<CTransactionRef>& txn_conflicted) override; + + void ChainStateFlushed(const CBlockLocator& locator) override; + +public: + /// Constructs the TxIndex, which becomes available to be queried. + explicit TxIndex(std::unique_ptr<TxIndexDB> db); + + /// Destructor interrupts sync thread if running and blocks until it exits. + ~TxIndex(); + + /// Blocks the current thread until the transaction index is caught up to + /// the current state of the block chain. This only blocks if the index has gotten in sync once + /// and only needs to process blocks in the ValidationInterface queue. If the index is catching + /// up from far behind, this method does not block and immediately returns false. + bool BlockUntilSyncedToCurrentChain(); + + /// Look up a transaction by hash. + /// + /// @param[in] tx_hash The hash of the transaction to be returned. + /// @param[out] block_hash The hash of the block the transaction is found in. + /// @param[out] tx The transaction itself. + /// @return true if transaction is found, false otherwise + bool FindTx(const uint256& tx_hash, uint256& block_hash, CTransactionRef& tx) const; + + void Interrupt(); + + /// Start initializes the sync state and registers the instance as a + /// ValidationInterface so that it stays in sync with blockchain updates. + void Start(); + + /// Stops the instance from staying in sync with blockchain updates. + void Stop(); +}; + +/// The global transaction index, used in GetTransaction. May be null. +extern std::unique_ptr<TxIndex> g_txindex; + +#endif // BITCOIN_INDEX_TXINDEX_H diff --git a/src/init.cpp b/src/init.cpp index 4bb2bc2c3e..6423d87026 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -19,6 +19,7 @@ #include <fs.h> #include <httpserver.h> #include <httprpc.h> +#include <index/txindex.h> #include <key.h> #include <validation.h> #include <miner.h> @@ -30,7 +31,6 @@ #include <policy/policy.h> #include <rpc/server.h> #include <rpc/register.h> -#include <rpc/safemode.h> #include <rpc/blockchain.h> #include <script/standard.h> #include <script/sigcache.h> @@ -47,7 +47,6 @@ #include <walletinitinterface.h> #include <stdint.h> #include <stdio.h> -#include <memory> #ifndef WIN32 #include <signal.h> @@ -72,7 +71,24 @@ static const bool DEFAULT_STOPAFTERBLOCKIMPORT = false; std::unique_ptr<CConnman> g_connman; std::unique_ptr<PeerLogicValidation> peerLogic; -std::unique_ptr<WalletInitInterface> g_wallet_init_interface; + +#if !(ENABLE_WALLET) +class DummyWalletInit : public WalletInitInterface { +public: + + std::string GetHelpString(bool showDebug) const override {return std::string{};} + bool ParameterInteraction() const override {return true;} + void RegisterRPC(CRPCTable &) const override {} + bool Verify() const override {return true;} + bool Open() const override {LogPrintf("No wallet support compiled in!\n"); return true;} + void Start(CScheduler& scheduler) const override {} + void Flush() const override {} + void Stop() const override {} + void Close() const override {} +}; + +const WalletInitInterface& g_wallet_init_interface = DummyWalletInit(); +#endif #if ENABLE_ZMQ static CZMQNotificationInterface* pzmqNotificationInterface = nullptr; @@ -166,6 +182,9 @@ void Interrupt() InterruptMapPort(); if (g_connman) g_connman->Interrupt(); + if (g_txindex) { + g_txindex->Interrupt(); + } } void Shutdown() @@ -187,7 +206,7 @@ void Shutdown() StopREST(); StopRPC(); StopHTTPServer(); - g_wallet_init_interface->Flush(); + g_wallet_init_interface.Flush(); StopMapPort(); // Because these depend on each-other, we make sure that neither can be @@ -196,6 +215,9 @@ void Shutdown() if (g_connman) g_connman->Stop(); peerLogic.reset(); g_connman.reset(); + if (g_txindex) { + g_txindex.reset(); + } StopTorControl(); @@ -245,7 +267,7 @@ void Shutdown() pcoinsdbview.reset(); pblocktree.reset(); } - g_wallet_init_interface->Stop(); + g_wallet_init_interface.Stop(); #if ENABLE_ZMQ if (pzmqNotificationInterface) { @@ -265,8 +287,7 @@ void Shutdown() UnregisterAllValidationInterfaces(); GetMainSignals().UnregisterBackgroundSignalScheduler(); GetMainSignals().UnregisterWithMempoolSignals(mempool); - g_wallet_init_interface->Close(); - g_wallet_init_interface.reset(); + g_wallet_init_interface.Close(); globalVerifyHandle.reset(); ECC_Stop(); LogPrintf("%s: done\n", __func__); @@ -284,7 +305,7 @@ static void HandleSIGTERM(int) static void HandleSIGHUP(int) { - fReopenDebugLog = true; + g_logger->m_reopen_file = true; } #ifndef WIN32 @@ -307,7 +328,7 @@ void OnRPCStopped() { uiInterface.NotifyBlockTip.disconnect(&RPCNotifyBlockChange); RPCNotifyBlockChange(false, nullptr); - cvBlockChange.notify_all(); + g_best_block_cv.notify_all(); LogPrint(BCLog::RPC, "RPC stopped.\n"); } @@ -409,7 +430,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-whitelist=<IP address or network>", _("Whitelist peers connecting from the given IP address (e.g. 1.2.3.4) or CIDR notated network (e.g. 1.2.3.0/24). Can be specified multiple times.") + " " + _("Whitelisted peers cannot be DoS banned and their transactions are always relayed, even if they are already in the mempool, useful e.g. for a gateway")); - strUsage += g_wallet_init_interface->GetHelpString(showDebug); + strUsage += g_wallet_init_interface.GetHelpString(showDebug); #if ENABLE_ZMQ strUsage += HelpMessageGroup(_("ZeroMQ notification options:")); @@ -426,9 +447,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-checkblockindex", strprintf("Do a full consistency check for mapBlockIndex, setBlockIndexCandidates, chainActive and mapBlocksUnlinked occasionally. (default: %u)", defaultChainParams->DefaultConsistencyChecks())); strUsage += HelpMessageOpt("-checkmempool=<n>", strprintf("Run checks every <n> transactions (default: %u)", defaultChainParams->DefaultConsistencyChecks())); strUsage += HelpMessageOpt("-checkpoints", strprintf("Disable expensive verification for known chain history (default: %u)", DEFAULT_CHECKPOINTS_ENABLED)); - strUsage += HelpMessageOpt("-disablesafemode", strprintf("Disable safemode, override a real safe mode event (default: %u)", DEFAULT_DISABLE_SAFEMODE)); strUsage += HelpMessageOpt("-deprecatedrpc=<method>", "Allows deprecated RPC method(s) to be used"); - strUsage += HelpMessageOpt("-testsafemode", strprintf("Force safe mode (default: %u)", DEFAULT_TESTSAFEMODE)); strUsage += HelpMessageOpt("-dropmessagestest=<n>", "Randomly drop 1 of every <n> network messages"); strUsage += HelpMessageOpt("-stopafterblockimport", strprintf("Stop running after importing blocks from disk (default: %u)", DEFAULT_STOPAFTERBLOCKIMPORT)); strUsage += HelpMessageOpt("-stopatheight", strprintf("Stop running after reaching the given height in the main chain (default: %u)", DEFAULT_STOPATHEIGHT)); @@ -615,6 +634,7 @@ void ThreadImport(std::vector<fs::path> vImportFiles) { const CChainParams& chainparams = Params(); RenameThread("bitcoin-loadblk"); + ScheduleBatchPriority(); { CImportingNow imp; @@ -668,7 +688,7 @@ void ThreadImport(std::vector<fs::path> vImportFiles) // scan for better chains in the block chain database, that are not yet connected in the active best chain CValidationState state; if (!ActivateBestChain(state, chainparams)) { - LogPrintf("Failed to connect best block\n"); + LogPrintf("Failed to connect best block (%s)\n", FormatStateMessage(state)); StartShutdown(); return; } @@ -786,6 +806,11 @@ void InitParameterInteraction() if (gArgs.SoftSetBoolArg("-whitelistrelay", true)) LogPrintf("%s: parameter interaction: -whitelistforcerelay=1 -> setting -whitelistrelay=1\n", __func__); } + + // Warn if network-specific options (-addnode, -connect, etc) are + // specified in default section of config file, but not overridden + // on the command line or in this network's section of the config file. + gArgs.WarnForSectionOnlyArgs(); } static std::string ResolveErrMsg(const char * const optname, const std::string& strBind) @@ -793,14 +818,28 @@ static std::string ResolveErrMsg(const char * const optname, const std::string& return strprintf(_("Cannot resolve -%s address: '%s'"), optname, strBind); } +/** + * Initialize global loggers. + * + * Note that this is called very early in the process lifetime, so you should be + * careful about what global state you rely on here. + */ void InitLogging() { - fPrintToConsole = gArgs.GetBoolArg("-printtoconsole", false); - fLogTimestamps = gArgs.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS); - fLogTimeMicros = gArgs.GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS); + g_logger->m_print_to_file = !gArgs.IsArgNegated("-debuglogfile"); + g_logger->m_file_path = AbsPathForConfigVal(gArgs.GetArg("-debuglogfile", DEFAULT_DEBUGLOGFILE)); + + // Add newlines to the logfile to distinguish this execution from the last + // one; called before console logging is set up, so this is only sent to + // debug.log. + LogPrintf("\n\n\n\n\n"); + + g_logger->m_print_to_console = gArgs.GetBoolArg("-printtoconsole", !gArgs.GetBoolArg("-daemon", false)); + g_logger->m_log_timestamps = gArgs.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS); + g_logger->m_log_time_micros = gArgs.GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS); + fLogIPs = gArgs.GetBoolArg("-logips", DEFAULT_LOGIPS); - LogPrintf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); std::string version_string = FormatFullVersion(); #ifdef DEBUG version_string += " (debug build)"; @@ -926,24 +965,18 @@ bool AppInitParameterInteraction() if (std::none_of(categories.begin(), categories.end(), [](std::string cat){return cat == "0" || cat == "none";})) { for (const auto& cat : categories) { - uint32_t flag = 0; - if (!GetLogCategory(&flag, &cat)) { + if (!g_logger->EnableCategory(cat)) { InitWarning(strprintf(_("Unsupported logging category %s=%s."), "-debug", cat)); - continue; } - logCategories |= flag; } } } // Now remove the logging categories which were explicitly excluded for (const std::string& cat : gArgs.GetArgs("-debugexclude")) { - uint32_t flag = 0; - if (!GetLogCategory(&flag, &cat)) { + if (!g_logger->DisableCategory(cat)) { InitWarning(strprintf(_("Unsupported logging category %s=%s."), "-debugexclude", cat)); - continue; } - logCategories &= ~flag; } // Check for -debugnet @@ -1076,7 +1109,7 @@ bool AppInitParameterInteraction() return InitError(strprintf("acceptnonstdtxn is not currently supported for %s chain", chainparams.NetworkIDString())); nBytesPerSigOp = gArgs.GetArg("-bytespersigop", nBytesPerSigOp); - if (!g_wallet_init_interface->ParameterInteraction()) return false; + if (!g_wallet_init_interface.ParameterInteraction()) return false; fIsBareMultisigStd = gArgs.GetBoolArg("-permitbaremultisig", DEFAULT_PERMIT_BAREMULTISIG); fAcceptDatacarrier = gArgs.GetBoolArg("-datacarrier", DEFAULT_ACCEPT_DATACARRIER); @@ -1194,19 +1227,19 @@ bool AppInitMain() #ifndef WIN32 CreatePidFile(GetPidFile(), getpid()); #endif - if (gArgs.GetBoolArg("-shrinkdebugfile", logCategories == BCLog::NONE)) { - // Do this first since it both loads a bunch of debug.log into memory, - // and because this needs to happen before any other debug.log printing - ShrinkDebugFile(); - } - - if (fPrintToDebugLog) { - if (!OpenDebugLog()) { - return InitError(strprintf("Could not open debug log file %s", GetDebugLogPath().string())); + if (g_logger->m_print_to_file) { + if (gArgs.GetBoolArg("-shrinkdebugfile", g_logger->DefaultShrinkDebugFile())) { + // Do this first since it both loads a bunch of debug.log into memory, + // and because this needs to happen before any other debug.log printing + g_logger->ShrinkDebugFile(); + } + if (!g_logger->OpenDebugLog()) { + return InitError(strprintf("Could not open debug log file %s", + g_logger->m_file_path.string())); } } - if (!fLogTimestamps) + if (!g_logger->m_log_timestamps) LogPrintf("Startup time: %s\n", FormatISO8601DateTime(GetTime())); LogPrintf("Default data directory %s\n", GetDefaultDataDir().string()); LogPrintf("Using data directory %s\n", GetDataDir().string()); @@ -1215,7 +1248,7 @@ bool AppInitMain() // Warn about relative -datadir path. if (gArgs.IsArgSet("-datadir") && !fs::path(gArgs.GetArg("-datadir", "")).is_absolute()) { - LogPrintf("Warning: relative datadir option '%s' specified, which will be interpreted relative to the " + LogPrintf("Warning: relative datadir option '%s' specified, which will be interpreted relative to the " /* Continued */ "current working directory '%s'. This is fragile, because if bitcoin is started in the future " "from a different location, it will be unable to locate the current data files. There could " "also be data loss if bitcoin is started while in a temporary directory.\n", @@ -1242,7 +1275,7 @@ bool AppInitMain() * available in the GUI RPC console even if external calls are disabled. */ RegisterAllCoreRPCCommands(tableRPC); - g_wallet_init_interface->RegisterRPC(tableRPC); + g_wallet_init_interface.RegisterRPC(tableRPC); /* Start the RPC server already. It will be started in "warmup" mode * and not really process calls already (but it will signify connections @@ -1259,7 +1292,7 @@ bool AppInitMain() int64_t nStart; // ********************************************************* Step 5: verify wallet database integrity - if (!g_wallet_init_interface->Verify()) return false; + if (!g_wallet_init_interface.Verify()) return false; // ********************************************************* Step 6: network initialization // Note that we absolutely cannot open any actual connections @@ -1383,9 +1416,10 @@ bool AppInitMain() int64_t nTotalCache = (gArgs.GetArg("-dbcache", nDefaultDbCache) << 20); nTotalCache = std::max(nTotalCache, nMinDbCache << 20); // total cache cannot be less than nMinDbCache nTotalCache = std::min(nTotalCache, nMaxDbCache << 20); // total cache cannot be greater than nMaxDbcache - int64_t nBlockTreeDBCache = nTotalCache / 8; - nBlockTreeDBCache = std::min(nBlockTreeDBCache, (gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX) ? nMaxBlockDBAndTxIndexCache : nMaxBlockDBCache) << 20); + int64_t nBlockTreeDBCache = std::min(nTotalCache / 8, nMaxBlockDBCache << 20); nTotalCache -= nBlockTreeDBCache; + int64_t nTxIndexCache = std::min(nTotalCache / 8, gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX) ? nMaxTxIndexCache << 20 : 0); + nTotalCache -= nTxIndexCache; int64_t nCoinDBCache = std::min(nTotalCache / 2, (nTotalCache / 4) + (1 << 23)); // use 25%-50% of the remainder for disk cache nCoinDBCache = std::min(nCoinDBCache, nMaxCoinsDBCache << 20); // cap total coins db cache nTotalCache -= nCoinDBCache; @@ -1393,6 +1427,9 @@ bool AppInitMain() int64_t nMempoolSizeMax = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000; LogPrintf("Cache configuration:\n"); LogPrintf("* Using %.1fMiB for block index database\n", nBlockTreeDBCache * (1.0 / 1024 / 1024)); + if (gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX)) { + LogPrintf("* Using %.1fMiB for transaction index database\n", nTxIndexCache * (1.0 / 1024 / 1024)); + } LogPrintf("* Using %.1fMiB for chain state database\n", nCoinDBCache * (1.0 / 1024 / 1024)); LogPrintf("* Using %.1fMiB for in-memory UTXO set (plus up to %.1fMiB of unused mempool space)\n", nCoinCacheUsage * (1.0 / 1024 / 1024), nMempoolSizeMax * (1.0 / 1024 / 1024)); @@ -1426,9 +1463,8 @@ bool AppInitMain() if (fRequestShutdown) break; - // LoadBlockIndex will load fTxIndex from the db, or set it if - // we're reindexing. It will also load fHavePruned if we've - // ever removed a block file from disk. + // LoadBlockIndex will load fHavePruned if we've ever removed a + // block file from disk. // Note that it also sets fReindex based on the disk flag! // From here on out fReindex and fReset mean something different! if (!LoadBlockIndex(chainparams)) { @@ -1442,12 +1478,6 @@ bool AppInitMain() return InitError(_("Incorrect or no genesis block found. Wrong datadir for network?")); } - // Check for changed -txindex state - if (fTxIndex != gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX)) { - strLoadError = _("You need to rebuild the database using -reindex to change -txindex"); - break; - } - // Check for changed -prune state. What we are concerned about is a user who has pruned blocks // in the past, but is now trying to run unpruned. if (fHavePruned && !fPruneMode) { @@ -1577,10 +1607,17 @@ bool AppInitMain() ::feeEstimator.Read(est_filein); fFeeEstimatesInitialized = true; - // ********************************************************* Step 8: load wallet - if (!g_wallet_init_interface->Open()) return false; + // ********************************************************* Step 8: start indexers + if (gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX)) { + auto txindex_db = MakeUnique<TxIndexDB>(nTxIndexCache, false, fReindex); + g_txindex = MakeUnique<TxIndex>(std::move(txindex_db)); + g_txindex->Start(); + } + + // ********************************************************* Step 9: load wallet + if (!g_wallet_init_interface.Open()) return false; - // ********************************************************* Step 9: data directory maintenance + // ********************************************************* Step 10: data directory maintenance // if pruning, unset the service bit and perform the initial blockstore prune // after any wallet rescanning has taken place. @@ -1602,7 +1639,7 @@ bool AppInitMain() nLocalServices = ServiceFlags(nLocalServices | NODE_WITNESS); } - // ********************************************************* Step 10: import blocks + // ********************************************************* Step 11: import blocks if (!CheckDiskSpace() && !CheckDiskSpace(0, true)) return false; @@ -1641,7 +1678,7 @@ bool AppInitMain() return false; } - // ********************************************************* Step 11: start node + // ********************************************************* Step 12: start node int chain_active_height; @@ -1719,12 +1756,12 @@ bool AppInitMain() return false; } - // ********************************************************* Step 12: finished + // ********************************************************* Step 13: finished SetRPCWarmupFinished(); uiInterface.InitMessage(_("Done loading")); - g_wallet_init_interface->Start(scheduler); + g_wallet_init_interface.Start(scheduler); return true; } diff --git a/src/init.h b/src/init.h index c93a210154..000c8c95e4 100644 --- a/src/init.h +++ b/src/init.h @@ -13,7 +13,7 @@ class CScheduler; class CWallet; class WalletInitInterface; -extern std::unique_ptr<WalletInitInterface> g_wallet_init_interface; +extern const WalletInitInterface& g_wallet_init_interface; namespace boost { diff --git a/src/interface/README.md b/src/interfaces/README.md index e93b91d23c..e93b91d23c 100644 --- a/src/interface/README.md +++ b/src/interfaces/README.md diff --git a/src/interface/handler.cpp b/src/interfaces/handler.cpp index 4b27f374f4..8e45faa2a5 100644 --- a/src/interface/handler.cpp +++ b/src/interfaces/handler.cpp @@ -2,15 +2,14 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include <interface/handler.h> +#include <interfaces/handler.h> #include <util.h> #include <boost/signals2/connection.hpp> -#include <memory> #include <utility> -namespace interface { +namespace interfaces { namespace { class HandlerImpl : public Handler @@ -30,4 +29,4 @@ std::unique_ptr<Handler> MakeHandler(boost::signals2::connection connection) return MakeUnique<HandlerImpl>(std::move(connection)); } -} // namespace interface +} // namespace interfaces diff --git a/src/interface/handler.h b/src/interfaces/handler.h index a76334bfbf..c4c674cac5 100644 --- a/src/interface/handler.h +++ b/src/interfaces/handler.h @@ -2,8 +2,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_INTERFACE_HANDLER_H -#define BITCOIN_INTERFACE_HANDLER_H +#ifndef BITCOIN_INTERFACES_HANDLER_H +#define BITCOIN_INTERFACES_HANDLER_H #include <memory> @@ -13,7 +13,7 @@ class connection; } // namespace signals2 } // namespace boost -namespace interface { +namespace interfaces { //! Generic interface for managing an event handler or callback function //! registered with another interface. Has a single disconnect method to cancel @@ -30,6 +30,6 @@ public: //! Return handler wrapping a boost signal connection. std::unique_ptr<Handler> MakeHandler(boost::signals2::connection connection); -} // namespace interface +} // namespace interfaces -#endif // BITCOIN_INTERFACE_HANDLER_H +#endif // BITCOIN_INTERFACES_HANDLER_H diff --git a/src/interface/node.cpp b/src/interfaces/node.cpp index f04da4e176..53d2359caf 100644 --- a/src/interface/node.cpp +++ b/src/interfaces/node.cpp @@ -2,15 +2,15 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include <interface/node.h> +#include <interfaces/node.h> #include <addrdb.h> #include <amount.h> #include <chain.h> #include <chainparams.h> #include <init.h> -#include <interface/handler.h> -#include <interface/wallet.h> +#include <interfaces/handler.h> +#include <interfaces/wallet.h> #include <net.h> #include <net_processing.h> #include <netaddress.h> @@ -43,7 +43,7 @@ #include <boost/thread/thread.hpp> #include <univalue.h> -namespace interface { +namespace interfaces { namespace { class NodeImpl : public Node @@ -60,7 +60,7 @@ class NodeImpl : public Node void initLogging() override { InitLogging(); } void initParameterInteraction() override { InitParameterInteraction(); } std::string getWarnings(const std::string& type) override { return GetWarnings(type); } - uint32_t getLogCategories() override { return ::logCategories; } + uint32_t getLogCategories() override { return g_logger->GetCategoryMask(); } bool baseInitialize() override { return AppInitBasicSetup() && AppInitParameterInteraction() && AppInitSanityChecks() && @@ -191,20 +191,6 @@ class NodeImpl : public Node } } bool getNetworkActive() override { return g_connman && g_connman->GetNetworkActive(); } - unsigned int getTxConfirmTarget() override { CHECK_WALLET(return ::nTxConfirmTarget); } - CAmount getRequiredFee(unsigned int tx_bytes) override { CHECK_WALLET(return GetRequiredFee(tx_bytes)); } - CAmount getMinimumFee(unsigned int tx_bytes, - const CCoinControl& coin_control, - int* returned_target, - FeeReason* reason) override - { - FeeCalculation fee_calc; - CAmount result; - CHECK_WALLET(result = GetMinimumFee(tx_bytes, coin_control, ::mempool, ::feeEstimator, &fee_calc)); - if (returned_target) *returned_target = fee_calc.returnedTarget; - if (reason) *reason = fee_calc.reason; - return result; - } CAmount getMaxTxFee() override { return ::maxTxFee; } CFeeRate estimateSmartFee(int num_blocks, bool conservative, int* returned_target = nullptr) override { @@ -216,9 +202,6 @@ class NodeImpl : public Node return result; } CFeeRate getDustRelayFee() override { return ::dustRelayFee; } - CFeeRate getFallbackFee() override { CHECK_WALLET(return CWallet::fallbackFee); } - CFeeRate getPayTxFee() override { CHECK_WALLET(return ::payTxFee); } - void setPayTxFee(CFeeRate rate) override { CHECK_WALLET(::payTxFee = rate); } UniValue executeRpc(const std::string& command, const UniValue& params, const std::string& uri) override { JSONRPCRequest req; @@ -239,7 +222,7 @@ class NodeImpl : public Node { #ifdef ENABLE_WALLET std::vector<std::unique_ptr<Wallet>> wallets; - for (CWalletRef wallet : ::vpwallets) { + for (CWallet* wallet : GetWallets()) { wallets.emplace_back(MakeWallet(*wallet)); } return wallets; @@ -305,4 +288,4 @@ class NodeImpl : public Node std::unique_ptr<Node> MakeNode() { return MakeUnique<NodeImpl>(); } -} // namespace interface +} // namespace interfaces diff --git a/src/interface/node.h b/src/interfaces/node.h index 6ae7f9e46c..3cebe53eb0 100644 --- a/src/interface/node.h +++ b/src/interfaces/node.h @@ -2,8 +2,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_INTERFACE_NODE_H -#define BITCOIN_INTERFACE_NODE_H +#ifndef BITCOIN_INTERFACES_NODE_H +#define BITCOIN_INTERFACES_NODE_H #include <addrdb.h> // For banmap_t #include <amount.h> // For CAmount @@ -26,11 +26,9 @@ class Coin; class RPCTimerInterface; class UniValue; class proxyType; -enum class FeeReason; struct CNodeStateStats; -namespace interface { - +namespace interfaces { class Handler; class Wallet; @@ -152,18 +150,6 @@ public: //! Get network active. virtual bool getNetworkActive() = 0; - //! Get tx confirm target. - virtual unsigned int getTxConfirmTarget() = 0; - - //! Get required fee. - virtual CAmount getRequiredFee(unsigned int tx_bytes) = 0; - - //! Get minimum fee. - virtual CAmount getMinimumFee(unsigned int tx_bytes, - const CCoinControl& coin_control, - int* returned_target, - FeeReason* reason) = 0; - //! Get max tx fee. virtual CAmount getMaxTxFee() = 0; @@ -173,15 +159,6 @@ public: //! Get dust relay fee. virtual CFeeRate getDustRelayFee() = 0; - //! Get fallback fee. - virtual CFeeRate getFallbackFee() = 0; - - //! Get pay tx fee. - virtual CFeeRate getPayTxFee() = 0; - - //! Set pay tx fee. - virtual void setPayTxFee(CFeeRate rate) = 0; - //! Execute rpc command. virtual UniValue executeRpc(const std::string& command, const UniValue& params, const std::string& uri) = 0; @@ -254,6 +231,6 @@ public: //! Return implementation of Node interface. std::unique_ptr<Node> MakeNode(); -} // namespace interface +} // namespace interfaces -#endif // BITCOIN_INTERFACE_NODE_H +#endif // BITCOIN_INTERFACES_NODE_H diff --git a/src/interface/wallet.cpp b/src/interfaces/wallet.cpp index a6bce7d3de..63b9d80a92 100644 --- a/src/interface/wallet.cpp +++ b/src/interfaces/wallet.cpp @@ -2,13 +2,15 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include <interface/wallet.h> +#include <interfaces/wallet.h> #include <amount.h> #include <chain.h> #include <consensus/validation.h> -#include <interface/handler.h> +#include <interfaces/handler.h> #include <net.h> +#include <policy/feerate.h> +#include <policy/fees.h> #include <policy/policy.h> #include <primitives/transaction.h> #include <script/ismine.h> @@ -20,11 +22,10 @@ #include <uint256.h> #include <validation.h> #include <wallet/feebumper.h> +#include <wallet/fees.h> #include <wallet/wallet.h> -#include <memory> - -namespace interface { +namespace interfaces { namespace { class PendingWalletTxImpl : public PendingWalletTx @@ -132,6 +133,7 @@ public: { return m_wallet.ChangeWalletPassphrase(old_wallet_passphrase, new_wallet_passphrase); } + void abortRescan() override { m_wallet.AbortRescan(); } bool backupWallet(const std::string& filename) override { return m_wallet.BackupWallet(filename); } std::string getWalletName() override { return m_wallet.GetName(); } bool getKeyFromPool(bool internal, CPubKey& pub_key) override @@ -150,7 +152,10 @@ public: { return m_wallet.DelAddressBook(dest); } - bool getAddress(const CTxDestination& dest, std::string* name, isminetype* is_mine) override + bool getAddress(const CTxDestination& dest, + std::string* name, + isminetype* is_mine, + std::string* purpose) override { LOCK(m_wallet.cs_wallet); auto it = m_wallet.mapAddressBook.find(dest); @@ -163,6 +168,9 @@ public: if (is_mine) { *is_mine = IsMine(m_wallet, dest); } + if (purpose) { + *purpose = it->second.purpose; + } return true; } std::vector<WalletAddress> getAddresses() override @@ -283,7 +291,7 @@ public: return result; } bool tryGetTxStatus(const uint256& txid, - interface::WalletTxStatus& tx_status, + interfaces::WalletTxStatus& tx_status, int& num_blocks, int64_t& adjusted_time) override { @@ -404,6 +412,20 @@ public: } return result; } + CAmount getRequiredFee(unsigned int tx_bytes) override { return GetRequiredFee(m_wallet, tx_bytes); } + CAmount getMinimumFee(unsigned int tx_bytes, + const CCoinControl& coin_control, + int* returned_target, + FeeReason* reason) override + { + FeeCalculation fee_calc; + CAmount result; + result = GetMinimumFee(m_wallet, tx_bytes, coin_control, ::mempool, ::feeEstimator, &fee_calc); + if (returned_target) *returned_target = fee_calc.returnedTarget; + if (reason) *reason = fee_calc.reason; + return result; + } + unsigned int getConfirmTarget() override { return m_wallet.m_confirm_target; } 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; } @@ -424,7 +446,7 @@ public: std::unique_ptr<Handler> handleTransactionChanged(TransactionChangedFn fn) override { return MakeHandler(m_wallet.NotifyTransactionChanged.connect( - [fn, this](CWallet*, const uint256& txid, ChangeType status) { fn(txid, status); })); + [fn](CWallet*, const uint256& txid, ChangeType status) { fn(txid, status); })); } std::unique_ptr<Handler> handleWatchOnlyChanged(WatchOnlyChangedFn fn) override { @@ -438,4 +460,4 @@ public: std::unique_ptr<Wallet> MakeWallet(CWallet& wallet) { return MakeUnique<WalletImpl>(wallet); } -} // namespace interface +} // namespace interfaces diff --git a/src/interface/wallet.h b/src/interfaces/wallet.h index b66ed63398..ff779cd0ad 100644 --- a/src/interface/wallet.h +++ b/src/interfaces/wallet.h @@ -2,11 +2,11 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_INTERFACE_WALLET_H -#define BITCOIN_INTERFACE_WALLET_H +#ifndef BITCOIN_INTERFACES_WALLET_H +#define BITCOIN_INTERFACES_WALLET_H #include <amount.h> // For CAmount -#include <pubkey.h> // For CTxDestination (CKeyID and CScriptID) +#include <pubkey.h> // For CKeyID and CScriptID (definitions needed in CTxDestination instantiation) #include <script/ismine.h> // For isminefilter, isminetype #include <script/standard.h> // For CTxDestination #include <support/allocators/secure.h> // For SecureString @@ -22,12 +22,14 @@ #include <vector> class CCoinControl; +class CFeeRate; class CKey; class CWallet; +enum class FeeReason; enum class OutputType; struct CRecipient; -namespace interface { +namespace interfaces { class Handler; class PendingWalletTx; @@ -65,6 +67,9 @@ public: virtual bool changeWalletPassphrase(const SecureString& old_wallet_passphrase, const SecureString& new_wallet_passphrase) = 0; + //! Abort a rescan. + virtual void abortRescan() = 0; + //! Back up wallet. virtual bool backupWallet(const std::string& filename) = 0; @@ -94,8 +99,9 @@ public: //! Look up address in wallet, return whether exists. virtual bool getAddress(const CTxDestination& dest, - std::string* name = nullptr, - isminetype* is_mine = nullptr) = 0; + std::string* name, + isminetype* is_mine, + std::string* purpose) = 0; //! Get wallet address list. virtual std::vector<WalletAddress> getAddresses() = 0; @@ -215,6 +221,18 @@ public: //! Return wallet transaction output information. virtual std::vector<WalletTxOut> getCoins(const std::vector<COutPoint>& outputs) = 0; + //! Get required fee. + virtual CAmount getRequiredFee(unsigned int tx_bytes) = 0; + + //! Get minimum fee. + virtual CAmount getMinimumFee(unsigned int tx_bytes, + const CCoinControl& coin_control, + int* returned_target, + FeeReason* reason) = 0; + + //! Get tx confirm target. + virtual unsigned int getConfirmTarget() = 0; + // Return whether HD enabled. virtual bool hdEnabled() = 0; @@ -347,6 +365,6 @@ struct WalletTxOut //! in builds where ENABLE_WALLET is false. std::unique_ptr<Wallet> MakeWallet(CWallet& wallet); -} // namespace interface +} // namespace interfaces -#endif // BITCOIN_INTERFACE_WALLET_H +#endif // BITCOIN_INTERFACES_WALLET_H diff --git a/src/keystore.h b/src/keystore.h index fa912cb195..c56e4751de 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -54,7 +54,7 @@ protected: ScriptMap mapScripts; WatchOnlySet setWatchOnly; - void ImplicitlyLearnRelatedKeyScripts(const CPubKey& pubkey); + void ImplicitlyLearnRelatedKeyScripts(const CPubKey& pubkey) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore); public: bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override; diff --git a/src/logging.cpp b/src/logging.cpp new file mode 100644 index 0000000000..10a3b18958 --- /dev/null +++ b/src/logging.cpp @@ -0,0 +1,270 @@ +// 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 <logging.h> +#include <utiltime.h> + +const char * const DEFAULT_DEBUGLOGFILE = "debug.log"; + +/** + * NOTE: the logger instances is leaked on exit. This is ugly, but will be + * cleaned up by the OS/libc. Defining a logger as a global object doesn't work + * since the order of destruction of static/global objects is undefined. + * Consider if the logger gets destroyed, and then some later destructor calls + * LogPrintf, maybe indirectly, and you get a core dump at shutdown trying to + * access the logger. When the shutdown sequence is fully audited and tested, + * explicit destruction of these objects can be implemented by changing this + * from a raw pointer to a std::unique_ptr. + * + * This method of initialization was originally introduced in + * ee3374234c60aba2cc4c5cd5cac1c0aefc2d817c. + */ +BCLog::Logger* const g_logger = new BCLog::Logger(); + +bool fLogIPs = DEFAULT_LOGIPS; + +static int FileWriteStr(const std::string &str, FILE *fp) +{ + return fwrite(str.data(), 1, str.size(), fp); +} + +bool BCLog::Logger::OpenDebugLog() +{ + std::lock_guard<std::mutex> scoped_lock(m_file_mutex); + + assert(m_fileout == nullptr); + assert(!m_file_path.empty()); + + m_fileout = fsbridge::fopen(m_file_path, "a"); + if (!m_fileout) { + return false; + } + + setbuf(m_fileout, nullptr); // unbuffered + // dump buffered messages from before we opened the log + while (!m_msgs_before_open.empty()) { + FileWriteStr(m_msgs_before_open.front(), m_fileout); + m_msgs_before_open.pop_front(); + } + + return true; +} + +void BCLog::Logger::EnableCategory(BCLog::LogFlags flag) +{ + m_categories |= flag; +} + +bool BCLog::Logger::EnableCategory(const std::string& str) +{ + BCLog::LogFlags flag; + if (!GetLogCategory(flag, str)) return false; + EnableCategory(flag); + return true; +} + +void BCLog::Logger::DisableCategory(BCLog::LogFlags flag) +{ + m_categories &= ~flag; +} + +bool BCLog::Logger::DisableCategory(const std::string& str) +{ + BCLog::LogFlags flag; + if (!GetLogCategory(flag, str)) return false; + DisableCategory(flag); + return true; +} + +bool BCLog::Logger::WillLogCategory(BCLog::LogFlags category) const +{ + return (m_categories.load(std::memory_order_relaxed) & category) != 0; +} + +bool BCLog::Logger::DefaultShrinkDebugFile() const +{ + return m_categories == BCLog::NONE; +} + +struct CLogCategoryDesc +{ + BCLog::LogFlags flag; + std::string category; +}; + +const CLogCategoryDesc LogCategories[] = +{ + {BCLog::NONE, "0"}, + {BCLog::NONE, "none"}, + {BCLog::NET, "net"}, + {BCLog::TOR, "tor"}, + {BCLog::MEMPOOL, "mempool"}, + {BCLog::HTTP, "http"}, + {BCLog::BENCH, "bench"}, + {BCLog::ZMQ, "zmq"}, + {BCLog::DB, "db"}, + {BCLog::RPC, "rpc"}, + {BCLog::ESTIMATEFEE, "estimatefee"}, + {BCLog::ADDRMAN, "addrman"}, + {BCLog::SELECTCOINS, "selectcoins"}, + {BCLog::REINDEX, "reindex"}, + {BCLog::CMPCTBLOCK, "cmpctblock"}, + {BCLog::RAND, "rand"}, + {BCLog::PRUNE, "prune"}, + {BCLog::PROXY, "proxy"}, + {BCLog::MEMPOOLREJ, "mempoolrej"}, + {BCLog::LIBEVENT, "libevent"}, + {BCLog::COINDB, "coindb"}, + {BCLog::QT, "qt"}, + {BCLog::LEVELDB, "leveldb"}, + {BCLog::ALL, "1"}, + {BCLog::ALL, "all"}, +}; + +bool GetLogCategory(BCLog::LogFlags& flag, const std::string& str) +{ + if (str == "") { + flag = BCLog::ALL; + return true; + } + for (const CLogCategoryDesc& category_desc : LogCategories) { + if (category_desc.category == str) { + flag = category_desc.flag; + return true; + } + } + return false; +} + +std::string ListLogCategories() +{ + std::string ret; + int outcount = 0; + for (const CLogCategoryDesc& category_desc : LogCategories) { + // Omit the special cases. + if (category_desc.flag != BCLog::NONE && category_desc.flag != BCLog::ALL) { + if (outcount != 0) ret += ", "; + ret += category_desc.category; + outcount++; + } + } + return ret; +} + +std::vector<CLogCategoryActive> ListActiveLogCategories() +{ + std::vector<CLogCategoryActive> ret; + for (const CLogCategoryDesc& category_desc : LogCategories) { + // Omit the special cases. + if (category_desc.flag != BCLog::NONE && category_desc.flag != BCLog::ALL) { + CLogCategoryActive catActive; + catActive.category = category_desc.category; + catActive.active = LogAcceptCategory(category_desc.flag); + ret.push_back(catActive); + } + } + return ret; +} + +std::string BCLog::Logger::LogTimestampStr(const std::string &str) +{ + std::string strStamped; + + if (!m_log_timestamps) + return str; + + if (m_started_new_line) { + int64_t nTimeMicros = GetTimeMicros(); + strStamped = FormatISO8601DateTime(nTimeMicros/1000000); + if (m_log_time_micros) { + strStamped.pop_back(); + strStamped += strprintf(".%06dZ", nTimeMicros%1000000); + } + int64_t mocktime = GetMockTime(); + if (mocktime) { + strStamped += " (mocktime: " + FormatISO8601DateTime(mocktime) + ")"; + } + strStamped += ' ' + str; + } else + strStamped = str; + + if (!str.empty() && str[str.size()-1] == '\n') + m_started_new_line = true; + else + m_started_new_line = false; + + return strStamped; +} + +int BCLog::Logger::LogPrintStr(const std::string &str) +{ + int ret = 0; // Returns total number of characters written + + std::string strTimestamped = LogTimestampStr(str); + + if (m_print_to_console) { + // print to console + ret = fwrite(strTimestamped.data(), 1, strTimestamped.size(), stdout); + fflush(stdout); + } + if (m_print_to_file) { + std::lock_guard<std::mutex> scoped_lock(m_file_mutex); + + // buffer if we haven't opened the log yet + if (m_fileout == nullptr) { + ret = strTimestamped.length(); + m_msgs_before_open.push_back(strTimestamped); + } + else + { + // reopen the log file, if requested + if (m_reopen_file) { + m_reopen_file = false; + if (fsbridge::freopen(m_file_path,"a",m_fileout) != nullptr) + setbuf(m_fileout, nullptr); // unbuffered + } + + ret = FileWriteStr(strTimestamped, m_fileout); + } + } + return ret; +} + +void BCLog::Logger::ShrinkDebugFile() +{ + // Amount of debug.log to save at end when shrinking (must fit in memory) + constexpr size_t RECENT_DEBUG_HISTORY_SIZE = 10 * 1000000; + + assert(!m_file_path.empty()); + + // Scroll debug.log if it's getting too big + FILE* file = fsbridge::fopen(m_file_path, "r"); + + // Special files (e.g. device nodes) may not have a size. + size_t log_size = 0; + try { + log_size = fs::file_size(m_file_path); + } catch (boost::filesystem::filesystem_error &) {} + + // If debug.log file is more than 10% bigger the RECENT_DEBUG_HISTORY_SIZE + // trim it down by saving only the last RECENT_DEBUG_HISTORY_SIZE bytes + if (file && log_size > 11 * (RECENT_DEBUG_HISTORY_SIZE / 10)) + { + // Restart the file with some of the end + std::vector<char> vch(RECENT_DEBUG_HISTORY_SIZE, 0); + fseek(file, -((long)vch.size()), SEEK_END); + int nBytes = fread(vch.data(), 1, vch.size(), file); + fclose(file); + + file = fsbridge::fopen(m_file_path, "w"); + if (file) + { + fwrite(vch.data(), 1, nBytes, file); + fclose(file); + } + } + else if (file != nullptr) + fclose(file); +} diff --git a/src/logging.h b/src/logging.h new file mode 100644 index 0000000000..1f2be6016a --- /dev/null +++ b/src/logging.h @@ -0,0 +1,166 @@ +// 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_LOGGING_H +#define BITCOIN_LOGGING_H + +#include <fs.h> +#include <tinyformat.h> + +#include <atomic> +#include <cstdint> +#include <list> +#include <mutex> +#include <string> +#include <vector> + +static const bool DEFAULT_LOGTIMEMICROS = false; +static const bool DEFAULT_LOGIPS = false; +static const bool DEFAULT_LOGTIMESTAMPS = true; +extern const char * const DEFAULT_DEBUGLOGFILE; + +extern bool fLogIPs; + +struct CLogCategoryActive +{ + std::string category; + bool active; +}; + +namespace BCLog { + enum LogFlags : uint32_t { + NONE = 0, + NET = (1 << 0), + TOR = (1 << 1), + MEMPOOL = (1 << 2), + HTTP = (1 << 3), + BENCH = (1 << 4), + ZMQ = (1 << 5), + DB = (1 << 6), + RPC = (1 << 7), + ESTIMATEFEE = (1 << 8), + ADDRMAN = (1 << 9), + SELECTCOINS = (1 << 10), + REINDEX = (1 << 11), + CMPCTBLOCK = (1 << 12), + RAND = (1 << 13), + PRUNE = (1 << 14), + PROXY = (1 << 15), + MEMPOOLREJ = (1 << 16), + LIBEVENT = (1 << 17), + COINDB = (1 << 18), + QT = (1 << 19), + LEVELDB = (1 << 20), + ALL = ~(uint32_t)0, + }; + + class Logger + { + private: + FILE* m_fileout = nullptr; + std::mutex m_file_mutex; + std::list<std::string> m_msgs_before_open; + + /** + * m_started_new_line is a state variable that will suppress printing of + * the timestamp when multiple calls are made that don't end in a + * newline. + */ + std::atomic_bool m_started_new_line{true}; + + /** Log categories bitfield. */ + std::atomic<uint32_t> m_categories{0}; + + std::string LogTimestampStr(const std::string& str); + + public: + bool m_print_to_console = false; + bool m_print_to_file = false; + + bool m_log_timestamps = DEFAULT_LOGTIMESTAMPS; + bool m_log_time_micros = DEFAULT_LOGTIMEMICROS; + + fs::path m_file_path; + std::atomic<bool> m_reopen_file{false}; + + /** Send a string to the log output */ + int LogPrintStr(const std::string &str); + + /** Returns whether logs will be written to any output */ + bool Enabled() const { return m_print_to_console || m_print_to_file; } + + bool OpenDebugLog(); + void ShrinkDebugFile(); + + uint32_t GetCategoryMask() const { return m_categories.load(); } + + void EnableCategory(LogFlags flag); + bool EnableCategory(const std::string& str); + void DisableCategory(LogFlags flag); + bool DisableCategory(const std::string& str); + + bool WillLogCategory(LogFlags category) const; + + bool DefaultShrinkDebugFile() const; + }; + +} // namespace BCLog + +extern BCLog::Logger* const g_logger; + +/** Return true if log accepts specified category */ +static inline bool LogAcceptCategory(BCLog::LogFlags category) +{ + return g_logger->WillLogCategory(category); +} + +/** Returns a string with the log categories. */ +std::string ListLogCategories(); + +/** Returns a vector of the active log categories. */ +std::vector<CLogCategoryActive> ListActiveLogCategories(); + +/** Return true if str parses as a log category and set the flag */ +bool GetLogCategory(BCLog::LogFlags& flag, const std::string& str); + +/** Get format string from VA_ARGS for error reporting */ +template<typename... Args> std::string FormatStringFromLogArgs(const char *fmt, const Args&... args) { return fmt; } + +static inline void MarkUsed() {} +template<typename T, typename... Args> static inline void MarkUsed(const T& t, const Args&... args) +{ + (void)t; + MarkUsed(args...); +} + +// Be conservative when using LogPrintf/error or other things which +// unconditionally log to debug.log! It should not be the case that an inbound +// peer can fill up a user's disk with debug.log entries. + +#ifdef USE_COVERAGE +#define LogPrintf(...) do { MarkUsed(__VA_ARGS__); } while(0) +#define LogPrint(category, ...) do { MarkUsed(__VA_ARGS__); } while(0) +#else +#define LogPrintf(...) do { \ + if (g_logger->Enabled()) { \ + std::string _log_msg_; /* Unlikely name to avoid shadowing variables */ \ + try { \ + _log_msg_ = tfm::format(__VA_ARGS__); \ + } catch (tinyformat::format_error &fmterr) { \ + /* Original format string will have newline so don't add one here */ \ + _log_msg_ = "Error \"" + std::string(fmterr.what()) + "\" while formatting log message: " + FormatStringFromLogArgs(__VA_ARGS__); \ + } \ + g_logger->LogPrintStr(_log_msg_); \ + } \ +} while(0) + +#define LogPrint(category, ...) do { \ + if (LogAcceptCategory((category))) { \ + LogPrintf(__VA_ARGS__); \ + } \ +} while(0) +#endif + +#endif // BITCOIN_LOGGING_H diff --git a/src/miner.cpp b/src/miner.cpp index d2be393538..0660df928c 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -27,7 +27,6 @@ #include <validationinterface.h> #include <algorithm> -#include <memory> #include <queue> #include <utility> diff --git a/src/net.cpp b/src/net.cpp index f45592ab83..0a1e268822 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -20,7 +20,6 @@ #include <ui_interface.h> #include <utilstrencodings.h> -#include <memory> #ifdef WIN32 #include <string.h> #else @@ -125,7 +124,7 @@ bool GetLocal(CService& addr, const CNetAddr *paddrPeer) return nBestScore >= 0; } -//! Convert the pnSeeds6 array into usable address objects. +//! Convert the pnSeed6 array into usable address objects. static std::vector<CAddress> convertSeed6(const std::vector<SeedSpec6> &vSeedsIn) { // It'll only connect to one or two seed nodes because once it connects, @@ -369,7 +368,7 @@ static CAddress GetBindAddress(SOCKET sock) return addr_bind; } -CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure) +CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, bool manual_connection) { if (pszDest == nullptr) { if (IsLocal(addrConnect)) @@ -396,7 +395,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo if (Lookup(pszDest, resolved, default_port, fNameLookup && !HaveNameProxy(), 256) && !resolved.empty()) { addrConnect = CAddress(resolved[GetRand(resolved.size())], NODE_NONE); if (!addrConnect.IsValid()) { - LogPrint(BCLog::NET, "Resolver returned invalid address %s for %s", addrConnect.ToString(), pszDest); + LogPrint(BCLog::NET, "Resolver returned invalid address %s for %s\n", addrConnect.ToString(), pszDest); return nullptr; } // It is possible that we already have a connection to the IP/port pszDest resolved to. @@ -433,7 +432,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo if (hSocket == INVALID_SOCKET) { return nullptr; } - connected = ConnectSocketDirectly(addrConnect, hSocket, nConnectTimeout); + connected = ConnectSocketDirectly(addrConnect, hSocket, nConnectTimeout, manual_connection); } if (!proxyConnectionFailed) { // If a connection to the node was attempted, and failure (if any) is not caused by a problem connecting to @@ -1924,23 +1923,25 @@ std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo() for (const std::string& strAddNode : lAddresses) { CService service(LookupNumeric(strAddNode.c_str(), Params().GetDefaultPort())); + AddedNodeInfo addedNode{strAddNode, CService(), false, false}; if (service.IsValid()) { // strAddNode is an IP:port auto it = mapConnected.find(service); if (it != mapConnected.end()) { - ret.push_back(AddedNodeInfo{strAddNode, service, true, it->second}); - } else { - ret.push_back(AddedNodeInfo{strAddNode, CService(), false, false}); + addedNode.resolvedAddress = service; + addedNode.fConnected = true; + addedNode.fInbound = it->second; } } else { // strAddNode is a name auto it = mapConnectedByName.find(strAddNode); if (it != mapConnectedByName.end()) { - ret.push_back(AddedNodeInfo{strAddNode, it->second.second, true, it->second.first}); - } else { - ret.push_back(AddedNodeInfo{strAddNode, CService(), false, false}); + addedNode.resolvedAddress = it->second.second; + addedNode.fConnected = true; + addedNode.fInbound = it->second.first; } } + ret.emplace_back(std::move(addedNode)); } return ret; @@ -1993,7 +1994,7 @@ void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai } else if (FindNode(std::string(pszDest))) return; - CNode* pnode = ConnectNode(addrConnect, pszDest, fCountFailure); + CNode* pnode = ConnectNode(addrConnect, pszDest, fCountFailure, manual_connection); if (!pnode) return; @@ -2089,23 +2090,16 @@ bool CConnman::BindListenPort(const CService &addrBind, std::string& strError, b LogPrintf("%s\n", strError); return false; } -#ifndef WIN32 + // Allow binding if the port is still in TIME_WAIT state after // the program was closed and restarted. - setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (void*)&nOne, sizeof(int)); -#else - setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&nOne, sizeof(int)); -#endif + setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (sockopt_arg_type)&nOne, sizeof(int)); // some systems don't have IPV6_V6ONLY but are always v6only; others do have the option // and enable it by default or not. Try to enable it, if possible. if (addrBind.IsIPv6()) { #ifdef IPV6_V6ONLY -#ifdef WIN32 - setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&nOne, sizeof(int)); -#else - setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&nOne, sizeof(int)); -#endif + setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (sockopt_arg_type)&nOne, sizeof(int)); #endif #ifdef WIN32 int nProtLevel = PROTECTION_LEVEL_UNRESTRICTED; @@ -338,7 +338,7 @@ private: CNode* FindNode(const CService& addr); bool AttemptToEvictConnection(); - CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure); + CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, bool manual_connection); bool IsWhitelistedRange(const CNetAddr &addr); void DeleteNode(CNode* pnode); diff --git a/src/net_processing.cpp b/src/net_processing.cpp index dbdae705de..cc819a01c3 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -1100,8 +1100,10 @@ void static ProcessGetBlockData(CNode* pfrom, const Consensus::Params& consensus } } // release cs_main before calling ActivateBestChain if (need_activate_chain) { - CValidationState dummy; - ActivateBestChain(dummy, Params(), a_recent_block); + CValidationState state; + if (!ActivateBestChain(state, Params(), a_recent_block)) { + LogPrint(BCLog::NET, "failed to activate chain (%s)\n", FormatStateMessage(state)); + } } LOCK(cs_main); @@ -1992,8 +1994,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr LOCK(cs_most_recent_block); a_recent_block = most_recent_block; } - CValidationState dummy; - ActivateBestChain(dummy, Params(), a_recent_block); + CValidationState state; + if (!ActivateBestChain(state, Params(), a_recent_block)) { + LogPrint(BCLog::NET, "failed to activate chain (%s)\n", FormatStateMessage(state)); + } } LOCK(cs_main); @@ -2055,7 +2059,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr const CBlockIndex* pindex = LookupBlockIndex(req.blockhash); if (!pindex || !(pindex->nStatus & BLOCK_HAVE_DATA)) { - LogPrint(BCLog::NET, "Peer %d sent us a getblocktxn for a block we don't have", pfrom->GetId()); + LogPrint(BCLog::NET, "Peer %d sent us a getblocktxn for a block we don't have\n", pfrom->GetId()); return true; } @@ -2067,7 +2071,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr // might maliciously send lots of getblocktxn requests to trigger // expensive disk reads, because it will require the peer to // actually receive all the data read from disk over the network. - LogPrint(BCLog::NET, "Peer %d sent us a getblocktxn for a block > %i deep", pfrom->GetId(), MAX_BLOCKTXN_DEPTH); + LogPrint(BCLog::NET, "Peer %d sent us a getblocktxn for a block > %i deep\n", pfrom->GetId(), MAX_BLOCKTXN_DEPTH); CInv inv; inv.type = State(pfrom->GetId())->fWantsCmpctWitness ? MSG_WITNESS_BLOCK : MSG_BLOCK; inv.hash = req.blockhash; diff --git a/src/netaddress.cpp b/src/netaddress.cpp index 4f231d73c8..18d5948f85 100644 --- a/src/netaddress.cpp +++ b/src/netaddress.cpp @@ -14,7 +14,7 @@ static const unsigned char pchOnionCat[] = {0xFD,0x87,0xD8,0x7E,0xEB,0x43}; // 0xFD + sha256("bitcoin")[0:5] static const unsigned char g_internal_prefix[] = { 0xFD, 0x6B, 0x88, 0xC0, 0x87, 0x24 }; -void CNetAddr::Init() +CNetAddr::CNetAddr() { memset(ip, 0, sizeof(ip)); scopeId = 0; @@ -67,11 +67,6 @@ bool CNetAddr::SetSpecial(const std::string &strName) return false; } -CNetAddr::CNetAddr() -{ - Init(); -} - CNetAddr::CNetAddr(const struct in_addr& ipv4Addr) { SetRaw(NET_IPV4, (const uint8_t*)&ipv4Addr); @@ -290,11 +285,6 @@ bool operator==(const CNetAddr& a, const CNetAddr& b) return (memcmp(a.ip, b.ip, 16) == 0); } -bool operator!=(const CNetAddr& a, const CNetAddr& b) -{ - return (memcmp(a.ip, b.ip, 16) != 0); -} - bool operator<(const CNetAddr& a, const CNetAddr& b) { return (memcmp(a.ip, b.ip, 16) < 0); @@ -469,14 +459,8 @@ int CNetAddr::GetReachabilityFrom(const CNetAddr *paddrPartner) const } } -void CService::Init() +CService::CService() : port(0) { - port = 0; -} - -CService::CService() -{ - Init(); } CService::CService(const CNetAddr& cip, unsigned short portIn) : CNetAddr(cip), port(portIn) @@ -525,11 +509,6 @@ bool operator==(const CService& a, const CService& b) return static_cast<CNetAddr>(a) == static_cast<CNetAddr>(b) && a.port == b.port; } -bool operator!=(const CService& a, const CService& b) -{ - return static_cast<CNetAddr>(a) != static_cast<CNetAddr>(b) || a.port != b.port; -} - bool operator<(const CService& a, const CService& b) { return static_cast<CNetAddr>(a) < static_cast<CNetAddr>(b) || (static_cast<CNetAddr>(a) == static_cast<CNetAddr>(b) && a.port < b.port); @@ -663,16 +642,16 @@ bool CSubNet::Match(const CNetAddr &addr) const static inline int NetmaskBits(uint8_t x) { switch(x) { - case 0x00: return 0; break; - case 0x80: return 1; break; - case 0xc0: return 2; break; - case 0xe0: return 3; break; - case 0xf0: return 4; break; - case 0xf8: return 5; break; - case 0xfc: return 6; break; - case 0xfe: return 7; break; - case 0xff: return 8; break; - default: return -1; break; + case 0x00: return 0; + case 0x80: return 1; + case 0xc0: return 2; + case 0xe0: return 3; + case 0xf0: return 4; + case 0xf8: return 5; + case 0xfc: return 6; + case 0xfe: return 7; + case 0xff: return 8; + default: return -1; } } @@ -724,11 +703,6 @@ bool operator==(const CSubNet& a, const CSubNet& b) return a.valid == b.valid && a.network == b.network && !memcmp(a.netmask, b.netmask, 16); } -bool operator!=(const CSubNet& a, const CSubNet& b) -{ - return !(a==b); -} - bool operator<(const CSubNet& a, const CSubNet& b) { return (a.network < b.network || (a.network == b.network && memcmp(a.netmask, b.netmask, 16) < 0)); diff --git a/src/netaddress.h b/src/netaddress.h index ad6b55eb58..f8f2ab99ff 100644 --- a/src/netaddress.h +++ b/src/netaddress.h @@ -11,6 +11,7 @@ #include <compat.h> #include <serialize.h> +#include <span.h> #include <stdint.h> #include <string> @@ -37,15 +38,16 @@ class CNetAddr public: CNetAddr(); explicit CNetAddr(const struct in_addr& ipv4Addr); - void Init(); void SetIP(const CNetAddr& ip); + private: /** * Set raw IPv4 or IPv6 address (in network byte order) * @note Only NET_IPV4 and NET_IPV6 are allowed for network. */ void SetRaw(Network network, const uint8_t *data); + public: /** * Transform an arbitrary string into a non-routable ipv6 address. * Useful for mapping resolved addresses back to their source. @@ -86,7 +88,7 @@ class CNetAddr bool GetIn6Addr(struct in6_addr* pipv6Addr) const; friend bool operator==(const CNetAddr& a, const CNetAddr& b); - friend bool operator!=(const CNetAddr& a, const CNetAddr& b); + friend bool operator!=(const CNetAddr& a, const CNetAddr& b) { return !(a == b); } friend bool operator<(const CNetAddr& a, const CNetAddr& b); ADD_SERIALIZE_METHODS; @@ -123,7 +125,7 @@ class CSubNet bool IsValid() const; friend bool operator==(const CSubNet& a, const CSubNet& b); - friend bool operator!=(const CSubNet& a, const CSubNet& b); + friend bool operator!=(const CSubNet& a, const CSubNet& b) { return !(a == b); } friend bool operator<(const CSubNet& a, const CSubNet& b); ADD_SERIALIZE_METHODS; @@ -140,19 +142,18 @@ class CSubNet class CService : public CNetAddr { protected: - unsigned short port; // host order + uint16_t port; // host order public: CService(); CService(const CNetAddr& ip, unsigned short port); CService(const struct in_addr& ipv4Addr, unsigned short port); explicit CService(const struct sockaddr_in& addr); - void Init(); unsigned short GetPort() const; bool GetSockAddr(struct sockaddr* paddr, socklen_t *addrlen) const; bool SetSockAddr(const struct sockaddr* paddr); friend bool operator==(const CService& a, const CService& b); - friend bool operator!=(const CService& a, const CService& b); + friend bool operator!=(const CService& a, const CService& b) { return !(a == b); } friend bool operator<(const CService& a, const CService& b); std::vector<unsigned char> GetKey() const; std::string ToString() const; @@ -167,10 +168,7 @@ class CService : public CNetAddr template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action) { READWRITE(ip); - unsigned short portN = htons(port); - READWRITE(FLATDATA(portN)); - if (ser_action.ForRead()) - port = ntohs(portN); + READWRITE(WrapBigEndian(port)); } }; diff --git a/src/netbase.cpp b/src/netbase.cpp index 92ac1c4c85..57835b5427 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -9,6 +9,7 @@ #include <sync.h> #include <uint256.h> #include <random.h> +#include <tinyformat.h> #include <util.h> #include <utilstrencodings.h> @@ -468,7 +469,17 @@ SOCKET CreateSocket(const CService &addrConnect) return hSocket; } -bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocket, int nTimeout) +template<typename... Args> +static void LogConnectFailure(bool manual_connection, const char* fmt, const Args&... args) { + std::string error_message = tfm::format(fmt, args...); + if (manual_connection) { + LogPrintf("%s\n", error_message); + } else { + LogPrint(BCLog::NET, "%s\n", error_message); + } +} + +bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocket, int nTimeout, bool manual_connection) { struct sockaddr_storage sockaddr; socklen_t len = sizeof(sockaddr); @@ -502,18 +513,14 @@ bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocket, i return false; } socklen_t nRetSize = sizeof(nRet); -#ifdef WIN32 - if (getsockopt(hSocket, SOL_SOCKET, SO_ERROR, (char*)(&nRet), &nRetSize) == SOCKET_ERROR) -#else - if (getsockopt(hSocket, SOL_SOCKET, SO_ERROR, &nRet, &nRetSize) == SOCKET_ERROR) -#endif + if (getsockopt(hSocket, SOL_SOCKET, SO_ERROR, (sockopt_arg_type)&nRet, &nRetSize) == SOCKET_ERROR) { LogPrintf("getsockopt() for %s failed: %s\n", addrConnect.ToString(), NetworkErrorString(WSAGetLastError())); return false; } if (nRet != 0) { - LogPrintf("connect() to %s failed after select(): %s\n", addrConnect.ToString(), NetworkErrorString(nRet)); + LogConnectFailure(manual_connection, "connect() to %s failed after select(): %s", addrConnect.ToString(), NetworkErrorString(nRet)); return false; } } @@ -523,7 +530,7 @@ bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocket, i else #endif { - LogPrintf("connect() to %s failed: %s\n", addrConnect.ToString(), NetworkErrorString(WSAGetLastError())); + LogConnectFailure(manual_connection, "connect() to %s failed: %s", addrConnect.ToString(), NetworkErrorString(WSAGetLastError())); return false; } } @@ -581,7 +588,7 @@ bool IsProxy(const CNetAddr &addr) { bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int port, const SOCKET& hSocket, int nTimeout, bool *outProxyConnectionFailed) { // first connect to proxy server - if (!ConnectSocketDirectly(proxy.proxy, hSocket, nTimeout)) { + if (!ConnectSocketDirectly(proxy.proxy, hSocket, nTimeout, true)) { if (outProxyConnectionFailed) *outProxyConnectionFailed = true; return false; @@ -601,6 +608,7 @@ bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int } return true; } + bool LookupSubNet(const char* pszName, CSubNet& ret) { std::string strSubnet(pszName); diff --git a/src/netbase.h b/src/netbase.h index c0921b6441..50d4bc54fa 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -52,7 +52,7 @@ bool Lookup(const char *pszName, std::vector<CService>& vAddr, int portDefault, CService LookupNumeric(const char *pszName, int portDefault = 0); bool LookupSubNet(const char *pszName, CSubNet& subnet); SOCKET CreateSocket(const CService &addrConnect); -bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocketRet, int nTimeout); +bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocketRet, int nTimeout, bool manual_connection); bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int port, const SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed); /** Return readable error string for a network error code */ std::string NetworkErrorString(int err); diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index c3f65fb2ab..5963bf371a 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -230,7 +230,7 @@ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) return false; // Check P2WSH standard limits - if (witnessversion == 0 && witnessprogram.size() == 32) { + if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_SCRIPTHASH_SIZE) { if (tx.vin[i].scriptWitness.stack.back().size() > MAX_STANDARD_P2WSH_SCRIPT_SIZE) return false; size_t sizeWitnessStack = tx.vin[i].scriptWitness.stack.size() - 1; diff --git a/src/primitives/block.h b/src/primitives/block.h index 5d6d44ac76..1fca55d910 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -93,7 +93,7 @@ public: template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(*static_cast<CBlockHeader*>(this)); + READWRITEAS(CBlockHeader, *this); READWRITE(vtx); } diff --git a/src/protocol.h b/src/protocol.h index a07c5ea862..3a9b2d2561 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -349,7 +349,7 @@ public: uint64_t nServicesInt = nServices; READWRITE(nServicesInt); nServices = static_cast<ServiceFlags>(nServicesInt); - READWRITE(*static_cast<CService*>(this)); + READWRITEAS(CService, *this); } // TODO: make private (improves encapsulation) diff --git a/src/qt/README.md b/src/qt/README.md index 7ffea98170..d8acf96ceb 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/). +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). Qt 4 is also supported (see [#8263](https://github.com/bitcoin/bitcoin/issues/8263)). ## 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](#use-qt-Creator-as IDE), but can be edited using any text editor. +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. ### 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](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). ClientModel is used by the main application `bitcoingui` and several models like `peertablemodel`. @@ -69,7 +69,7 @@ Represents the view to a single wallet. ## Contribute -See [CONTRIBUTING.md](/CONTRIBUTING.md) for general guidelines. Specifically for QT: +See [CONTRIBUTING.md](/CONTRIBUTING.md) for general guidelines. Specifically for Qt: * don't change `local/bitcoin_en.ts`; this happens [automatically](/doc/translation_process.md#writing-code-with-translations) @@ -83,7 +83,7 @@ Uncheck everything except Qt Creator during the installation process. Instructions for OSX: -1. Make sure you installed everything through Homebrew mentioned in the [OSX build instructions](/docs/build-osx.md) +1. Make sure you installed everything through Homebrew mentioned in the [OSX build instructions](/doc/build-osx.md) 2. Use `./configure` with the `--enable-debug` flag 3. In Qt Creator do "New Project" -> Import Project -> Import Existing Project 4. Enter "bitcoin-qt" as project name, enter src/qt as location diff --git a/src/qt/addressbookpage.h b/src/qt/addressbookpage.h index 8877d07330..ba420c5e15 100644 --- a/src/qt/addressbookpage.h +++ b/src/qt/addressbookpage.h @@ -38,7 +38,7 @@ public: ForEditing /**< Open address book for editing */ }; - explicit AddressBookPage(const PlatformStyle *platformStyle, Mode mode, Tabs tab, QWidget *parent); + explicit AddressBookPage(const PlatformStyle *platformStyle, Mode mode, Tabs tab, QWidget *parent = 0); ~AddressBookPage(); void setModel(AddressTableModel *model); diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index a9408895d9..25b615e6f8 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -7,7 +7,7 @@ #include <qt/guiutil.h> #include <qt/walletmodel.h> -#include <interface/node.h> +#include <interfaces/node.h> #include <key_io.h> #include <wallet/wallet.h> @@ -74,7 +74,7 @@ public: AddressTablePriv(AddressTableModel *_parent): parent(_parent) {} - void refreshAddressTable(interface::Wallet& wallet) + void refreshAddressTable(interfaces::Wallet& wallet) { cachedAddressTable.clear(); { @@ -159,7 +159,7 @@ public: }; AddressTableModel::AddressTableModel(WalletModel *parent) : - QAbstractTableModel(parent),walletModel(parent),priv(0) + QAbstractTableModel(parent), walletModel(parent) { columns << tr("Label") << tr("Address"); priv = new AddressTablePriv(this); @@ -266,7 +266,8 @@ bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value, } // Check for duplicate addresses to prevent accidental deletion of addresses, if you try // to paste an existing address over another address (with a different label) - if (walletModel->wallet().getAddress(newAddress)) + if (walletModel->wallet().getAddress( + newAddress, /* name= */ nullptr, /* is_mine= */ nullptr, /* purpose= */ nullptr)) { editStatus = DUPLICATE_ADDRESS; return false; @@ -351,7 +352,8 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con } // Check for duplicate addresses { - if(walletModel->wallet().getAddress(DecodeDestination(strAddress))) + if (walletModel->wallet().getAddress( + DecodeDestination(strAddress), /* name= */ nullptr, /* is_mine= */ nullptr, /* purpose= */ nullptr)) { editStatus = DUPLICATE_ADDRESS; return QString(); @@ -405,21 +407,31 @@ bool AddressTableModel::removeRows(int row, int count, const QModelIndex &parent return true; } -/* Look up label for address in address book, if not found return empty string. - */ QString AddressTableModel::labelForAddress(const QString &address) const { - { - CTxDestination destination = DecodeDestination(address.toStdString()); - std::string name; - if (walletModel->wallet().getAddress(destination, &name)) - { - return QString::fromStdString(name); - } + std::string name; + if (getAddressData(address, &name, /* purpose= */ nullptr)) { + return QString::fromStdString(name); + } + return QString(); +} + +QString AddressTableModel::purposeForAddress(const QString &address) const +{ + std::string purpose; + if (getAddressData(address, /* name= */ nullptr, &purpose)) { + return QString::fromStdString(purpose); } return QString(); } +bool AddressTableModel::getAddressData(const QString &address, + std::string* name, + std::string* purpose) const { + CTxDestination destination = DecodeDestination(address.toStdString()); + return walletModel->wallet().getAddress(destination, name, /* is_mine= */ nullptr, purpose); +} + int AddressTableModel::lookupAddress(const QString &address) const { QModelIndexList lst = match(index(0, Address, QModelIndex()), diff --git a/src/qt/addresstablemodel.h b/src/qt/addresstablemodel.h index 954f0f593e..6e1b53b049 100644 --- a/src/qt/addresstablemodel.h +++ b/src/qt/addresstablemodel.h @@ -13,7 +13,7 @@ enum class OutputType; class AddressTablePriv; class WalletModel; -namespace interface { +namespace interfaces { class Wallet; } @@ -67,10 +67,12 @@ public: */ QString addRow(const QString &type, const QString &label, const QString &address, const OutputType address_type); - /* Look up label for address in address book, if not found return empty string. - */ + /** Look up label for address in address book, if not found return empty string. */ QString labelForAddress(const QString &address) const; + /** Look up purpose for address in address book, if not found return empty string. */ + QString purposeForAddress(const QString &address) const; + /* Look up row index of an address in the model. Return -1 if not found. */ @@ -81,10 +83,13 @@ public: OutputType GetDefaultAddressType() const; private: - WalletModel *walletModel; - AddressTablePriv *priv; + WalletModel* const walletModel; + AddressTablePriv *priv = nullptr; QStringList columns; - EditStatus editStatus; + EditStatus editStatus = OK; + + /** Look up address book data given an address string. */ + bool getAddressData(const QString &address, std::string* name, std::string* purpose) const; /** Notify listeners that data changed. */ void emitDataChanged(int index); diff --git a/src/qt/bantablemodel.cpp b/src/qt/bantablemodel.cpp index cbd67d70ab..26cb03c2c7 100644 --- a/src/qt/bantablemodel.cpp +++ b/src/qt/bantablemodel.cpp @@ -8,7 +8,7 @@ #include <qt/guiconstants.h> #include <qt/guiutil.h> -#include <interface/node.h> +#include <interfaces/node.h> #include <sync.h> #include <utiltime.h> @@ -46,7 +46,7 @@ public: Qt::SortOrder sortOrder; /** Pull a full list of banned nodes from CNode into our cache */ - void refreshBanlist(interface::Node& node) + void refreshBanlist(interfaces::Node& node) { banmap_t banMap; node.getBanned(banMap); @@ -82,7 +82,7 @@ public: } }; -BanTableModel::BanTableModel(interface::Node& node, ClientModel *parent) : +BanTableModel::BanTableModel(interfaces::Node& node, ClientModel *parent) : QAbstractTableModel(parent), m_node(node), clientModel(parent) diff --git a/src/qt/bantablemodel.h b/src/qt/bantablemodel.h index 4c171e6fbe..d6c8dbf6dd 100644 --- a/src/qt/bantablemodel.h +++ b/src/qt/bantablemodel.h @@ -15,7 +15,7 @@ class ClientModel; class BanTablePriv; -namespace interface { +namespace interfaces { class Node; } @@ -45,7 +45,7 @@ class BanTableModel : public QAbstractTableModel Q_OBJECT public: - explicit BanTableModel(interface::Node& node, ClientModel *parent = 0); + explicit BanTableModel(interfaces::Node& node, ClientModel *parent = 0); ~BanTableModel(); void startAutoRefresh(); void stopAutoRefresh(); @@ -71,7 +71,7 @@ public Q_SLOTS: void refresh(); private: - interface::Node& m_node; + interfaces::Node& m_node; ClientModel *clientModel; QStringList columns; std::unique_ptr<BanTablePriv> priv; diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 30d0acb7ef..57fe4552a1 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -27,17 +27,14 @@ #endif #include <init.h> -#include <interface/handler.h> -#include <interface/node.h> +#include <interfaces/handler.h> +#include <interfaces/node.h> #include <rpc/server.h> #include <ui_interface.h> #include <uint256.h> #include <util.h> #include <warnings.h> -#ifdef ENABLE_WALLET -#include <wallet/init.h> -#endif #include <walletinitinterface.h> #include <memory> @@ -182,7 +179,7 @@ class BitcoinCore: public QObject { Q_OBJECT public: - explicit BitcoinCore(interface::Node& node); + explicit BitcoinCore(interfaces::Node& node); public Q_SLOTS: void initialize(); @@ -197,7 +194,7 @@ private: /// Pass fatal exception message to UI thread void handleRunawayException(const std::exception *e); - interface::Node& m_node; + interfaces::Node& m_node; }; /** Main Bitcoin application object */ @@ -205,7 +202,7 @@ class BitcoinApplication: public QApplication { Q_OBJECT public: - explicit BitcoinApplication(interface::Node& node, int &argc, char **argv); + explicit BitcoinApplication(interfaces::Node& node, int &argc, char **argv); ~BitcoinApplication(); #ifdef ENABLE_WALLET @@ -246,7 +243,7 @@ Q_SIGNALS: private: QThread *coreThread; - interface::Node& m_node; + interfaces::Node& m_node; OptionsModel *optionsModel; ClientModel *clientModel; BitcoinGUI *window; @@ -264,7 +261,7 @@ private: #include <qt/bitcoin.moc> -BitcoinCore::BitcoinCore(interface::Node& node) : +BitcoinCore::BitcoinCore(interfaces::Node& node) : QObject(), m_node(node) { } @@ -304,7 +301,7 @@ void BitcoinCore::shutdown() } } -BitcoinApplication::BitcoinApplication(interface::Node& node, int &argc, char **argv): +BitcoinApplication::BitcoinApplication(interfaces::Node& node, int &argc, char **argv): QApplication(argc, argv), coreThread(0), m_node(node), @@ -406,6 +403,10 @@ void BitcoinApplication::startThread() void BitcoinApplication::parameterSetup() { + // Default printtoconsole to false for the GUI. GUI programs should not + // print to the console unnecessarily. + gArgs.SoftSetBoolArg("-printtoconsole", false); + m_node.initLogging(); m_node.initParameterInteraction(); } @@ -535,7 +536,7 @@ int main(int argc, char *argv[]) { SetupEnvironment(); - std::unique_ptr<interface::Node> node = interface::MakeNode(); + std::unique_ptr<interfaces::Node> node = interfaces::MakeNode(); /// 1. Parse command-line options. These take precedence over anything else. // Command-line options take precedence: @@ -630,7 +631,7 @@ int main(int argc, char *argv[]) // Check for -testnet or -regtest parameter (Params() calls are only valid after this clause) try { - node->selectParams(ChainNameFromCommandLine()); + node->selectParams(gArgs.GetChainName()); } catch(std::exception &e) { QMessageBox::critical(0, QObject::tr(PACKAGE_NAME), QObject::tr("Error: %1").arg(e.what())); return EXIT_FAILURE; @@ -660,11 +661,6 @@ int main(int argc, char *argv[]) // Start up the payment server early, too, so impatient users that click on // bitcoin: links repeatedly have their payment requests routed to this process: app.createPaymentServer(); - - // Hook up the wallet init interface - g_wallet_init_interface.reset(new WalletInit); -#else - g_wallet_init_interface.reset(new DummyWalletInit); #endif /// 9. Main GUI initialization @@ -687,7 +683,7 @@ int main(int argc, char *argv[]) app.createOptionsModel(gArgs.GetBoolArg("-resetguisettings", false)); // Subscribe to global signals from core - std::unique_ptr<interface::Handler> handler = node->handleInitMessage(InitMessage); + std::unique_ptr<interfaces::Handler> handler = node->handleInitMessage(InitMessage); if (gArgs.GetBoolArg("-splash", DEFAULT_SPLASHSCREEN) && !gArgs.GetBoolArg("-min", false)) app.createSplashScreen(networkStyle.data()); diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 63a2a200b2..aed5374a7d 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -30,8 +30,8 @@ #include <chainparams.h> #include <init.h> -#include <interface/handler.h> -#include <interface/node.h> +#include <interfaces/handler.h> +#include <interfaces/node.h> #include <ui_interface.h> #include <util.h> @@ -74,7 +74,7 @@ const std::string BitcoinGUI::DEFAULT_UIPLATFORM = #endif ; -BitcoinGUI::BitcoinGUI(interface::Node& node, const PlatformStyle *_platformStyle, const NetworkStyle *networkStyle, QWidget *parent) : +BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformStyle, const NetworkStyle *networkStyle, QWidget *parent) : QMainWindow(parent), enableWallet(false), m_node(node), @@ -968,6 +968,11 @@ void BitcoinGUI::changeEvent(QEvent *e) QTimer::singleShot(0, this, SLOT(hide())); e->ignore(); } + else if((wsevt->oldState() & Qt::WindowMinimized) && !isMinimized()) + { + QTimer::singleShot(0, this, SLOT(show())); + e->ignore(); + } } } #endif diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 3a4b25d804..e59c71cd4f 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -33,7 +33,7 @@ class WalletModel; class HelpMessageDialog; class ModalOverlay; -namespace interface { +namespace interfaces { class Handler; class Node; } @@ -56,7 +56,7 @@ class BitcoinGUI : public QMainWindow public: static const std::string DEFAULT_UIPLATFORM; - explicit BitcoinGUI(interface::Node& node, const PlatformStyle *platformStyle, const NetworkStyle *networkStyle, QWidget *parent = 0); + explicit BitcoinGUI(interfaces::Node& node, const PlatformStyle *platformStyle, const NetworkStyle *networkStyle, QWidget *parent = 0); ~BitcoinGUI(); /** Set the client model. @@ -83,9 +83,9 @@ protected: bool eventFilter(QObject *object, QEvent *event); private: - interface::Node& m_node; - std::unique_ptr<interface::Handler> m_handler_message_box; - std::unique_ptr<interface::Handler> m_handler_question; + 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; diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 1dbfc815b6..37fd06ccc9 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -13,8 +13,8 @@ #include <chainparams.h> #include <checkpoints.h> #include <clientversion.h> -#include <interface/handler.h> -#include <interface/node.h> +#include <interfaces/handler.h> +#include <interfaces/node.h> #include <validation.h> #include <net.h> #include <txmempool.h> @@ -32,7 +32,7 @@ class CBlockIndex; static int64_t nLastHeaderTipUpdateNotification = 0; static int64_t nLastBlockTipUpdateNotification = 0; -ClientModel::ClientModel(interface::Node& node, OptionsModel *_optionsModel, QObject *parent) : +ClientModel::ClientModel(interfaces::Node& node, OptionsModel *_optionsModel, QObject *parent) : QObject(parent), m_node(node), optionsModel(_optionsModel), diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index 9faa10b87e..a609222f7d 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -17,7 +17,7 @@ class PeerTableModel; class CBlockIndex; -namespace interface { +namespace interfaces { class Handler; class Node; } @@ -46,10 +46,10 @@ class ClientModel : public QObject Q_OBJECT public: - explicit ClientModel(interface::Node& node, OptionsModel *optionsModel, QObject *parent = 0); + explicit ClientModel(interfaces::Node& node, OptionsModel *optionsModel, QObject *parent = 0); ~ClientModel(); - interface::Node& node() const { return m_node; } + interfaces::Node& node() const { return m_node; } OptionsModel *getOptionsModel(); PeerTableModel *getPeerTableModel(); BanTableModel *getBanTableModel(); @@ -75,14 +75,14 @@ public: mutable std::atomic<int64_t> cachedBestHeaderTime; private: - interface::Node& m_node; - std::unique_ptr<interface::Handler> m_handler_show_progress; - std::unique_ptr<interface::Handler> m_handler_notify_num_connections_changed; - std::unique_ptr<interface::Handler> m_handler_notify_network_active_changed; - std::unique_ptr<interface::Handler> m_handler_notify_alert_changed; - std::unique_ptr<interface::Handler> m_handler_banned_list_changed; - std::unique_ptr<interface::Handler> m_handler_notify_block_tip; - std::unique_ptr<interface::Handler> m_handler_notify_header_tip; + interfaces::Node& m_node; + std::unique_ptr<interfaces::Handler> m_handler_show_progress; + std::unique_ptr<interfaces::Handler> m_handler_notify_num_connections_changed; + std::unique_ptr<interfaces::Handler> m_handler_notify_network_active_changed; + std::unique_ptr<interfaces::Handler> m_handler_notify_alert_changed; + std::unique_ptr<interfaces::Handler> m_handler_banned_list_changed; + std::unique_ptr<interfaces::Handler> m_handler_notify_block_tip; + std::unique_ptr<interfaces::Handler> m_handler_notify_header_tip; OptionsModel *optionsModel; PeerTableModel *peerTableModel; BanTableModel *banTableModel; diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 2081d6ca08..b08de27041 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -14,7 +14,7 @@ #include <qt/walletmodel.h> #include <wallet/coincontrol.h> -#include <interface/node.h> +#include <interfaces/node.h> #include <key_io.h> #include <policy/fees.h> #include <policy/policy.h> @@ -509,7 +509,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) nBytes -= 34; // Fee - nPayFee = model->node().getMinimumFee(nBytes, *coinControl(), nullptr /* returned_target */, nullptr /* reason */); + nPayFee = model->wallet().getMinimumFee(nBytes, *coinControl(), nullptr /* returned_target */, nullptr /* reason */); if (nPayAmount > 0) { @@ -648,7 +648,7 @@ void CoinControlDialog::updateView() int nChildren = 0; for (const auto& outpair : coins.second) { const COutPoint& output = std::get<0>(outpair); - const interface::WalletTxOut& out = std::get<1>(outpair); + const interfaces::WalletTxOut& out = std::get<1>(outpair); nSum += out.txout.nValue; nChildren++; diff --git a/src/qt/editaddressdialog.cpp b/src/qt/editaddressdialog.cpp index 38411c499f..f26a31158e 100644 --- a/src/qt/editaddressdialog.cpp +++ b/src/qt/editaddressdialog.cpp @@ -109,7 +109,7 @@ void EditAddressDialog::accept() break; case AddressTableModel::DUPLICATE_ADDRESS: QMessageBox::warning(this, windowTitle(), - tr("The entered address \"%1\" is already in the address book.").arg(ui->addressEdit->text()), + getDuplicateAddressWarning(), QMessageBox::Ok, QMessageBox::Ok); break; case AddressTableModel::WALLET_UNLOCK_FAILURE: @@ -129,6 +129,25 @@ void EditAddressDialog::accept() QDialog::accept(); } +QString EditAddressDialog::getDuplicateAddressWarning() const +{ + QString dup_address = ui->addressEdit->text(); + QString existing_label = model->labelForAddress(dup_address); + QString existing_purpose = model->purposeForAddress(dup_address); + + if (existing_purpose == "receive" && + (mode == NewSendingAddress || mode == EditSendingAddress)) { + return tr( + "Address \"%1\" already exists as a receiving address with label " + "\"%2\" and so cannot be added as a sending address." + ).arg(dup_address).arg(existing_label); + } + return tr( + "The entered address \"%1\" is already in the address book with " + "label \"%2\"." + ).arg(dup_address).arg(existing_label); +} + QString EditAddressDialog::getAddress() const { return address; diff --git a/src/qt/editaddressdialog.h b/src/qt/editaddressdialog.h index 41c5d1708a..3aba74bf08 100644 --- a/src/qt/editaddressdialog.h +++ b/src/qt/editaddressdialog.h @@ -30,7 +30,7 @@ public: EditSendingAddress }; - explicit EditAddressDialog(Mode mode, QWidget *parent); + explicit EditAddressDialog(Mode mode, QWidget *parent = 0); ~EditAddressDialog(); void setModel(AddressTableModel *model); @@ -45,6 +45,9 @@ public Q_SLOTS: private: bool saveCurrentRow(); + /** Return a descriptive string when adding an already-existing address fails. */ + QString getDuplicateAddressWarning() const; + Ui::EditAddressDialog *ui; QDataWidgetMapper *mapper; Mode mode; diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 3501f97908..563f930dec 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -13,7 +13,7 @@ #include <chainparams.h> #include <primitives/transaction.h> #include <key_io.h> -#include <interface/node.h> +#include <interfaces/node.h> #include <policy/policy.h> #include <protocol.h> #include <script/script.h> @@ -232,7 +232,7 @@ QString formatBitcoinURI(const SendCoinsRecipient &info) return ret; } -bool isDust(interface::Node& node, const QString& address, const CAmount& amount) +bool isDust(interfaces::Node& node, const QString& address, const CAmount& amount) { CTxDestination dest = DecodeDestination(address.toStdString()); CScript script = GetScriptForDestination(dest); @@ -599,7 +599,7 @@ TableViewLastColumnResizingFixer::TableViewLastColumnResizingFixer(QTableView* t #ifdef WIN32 fs::path static StartupShortcutPath() { - std::string chain = ChainNameFromCommandLine(); + std::string chain = gArgs.GetChainName(); if (chain == CBaseChainParams::MAIN) return GetSpecialFolderPath(CSIDL_STARTUP) / "Bitcoin.lnk"; if (chain == CBaseChainParams::TESTNET) // Remove this special case when CBaseChainParams::TESTNET = "testnet4" @@ -697,7 +697,7 @@ fs::path static GetAutostartDir() fs::path static GetAutostartFilePath() { - std::string chain = ChainNameFromCommandLine(); + std::string chain = gArgs.GetChainName(); if (chain == CBaseChainParams::MAIN) return GetAutostartDir() / "bitcoin.desktop"; return GetAutostartDir() / strprintf("bitcoin-%s.lnk", chain); @@ -739,7 +739,7 @@ bool SetStartOnSystemStartup(bool fAutoStart) fs::ofstream optionFile(GetAutostartFilePath(), std::ios_base::out|std::ios_base::trunc); if (!optionFile.good()) return false; - std::string chain = ChainNameFromCommandLine(); + std::string chain = gArgs.GetChainName(); // Write a bitcoin.desktop file to the autostart directory: optionFile << "[Desktop Entry]\n"; optionFile << "Type=Application\n"; diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index 40037edb9d..4a26964098 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -20,7 +20,7 @@ class QValidatedLineEdit; class SendCoinsRecipient; -namespace interface +namespace interfaces { class Node; } @@ -54,7 +54,7 @@ namespace GUIUtil QString formatBitcoinURI(const SendCoinsRecipient &info); // Returns true if given address+amount meets "dust" definition - bool isDust(interface::Node& node, const QString& address, const CAmount& amount); + bool isDust(interfaces::Node& node, const QString& address, const CAmount& amount); // HTML escaping for rich text controls QString HtmlEscape(const QString& str, bool fMultiLine=false); diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp index 4eb7482018..8c00ca0363 100644 --- a/src/qt/intro.cpp +++ b/src/qt/intro.cpp @@ -12,7 +12,7 @@ #include <qt/guiutil.h> -#include <interface/node.h> +#include <interfaces/node.h> #include <util.h> #include <QFileDialog> @@ -187,7 +187,7 @@ QString Intro::getDefaultDataDirectory() return GUIUtil::boostPathToQString(GetDefaultDataDir()); } -bool Intro::pickDataDirectory(interface::Node& node) +bool Intro::pickDataDirectory(interfaces::Node& node) { QSettings settings; /* If data directory provided on command line, no need to look at settings diff --git a/src/qt/intro.h b/src/qt/intro.h index 07d2025bb6..b0937aedcb 100644 --- a/src/qt/intro.h +++ b/src/qt/intro.h @@ -13,7 +13,7 @@ static const bool DEFAULT_CHOOSE_DATADIR = false; class FreespaceChecker; -namespace interface { +namespace interfaces { class Node; } @@ -45,7 +45,7 @@ public: * @note do NOT call global GetDataDir() before calling this function, this * will cause the wrong path to be cached. */ - static bool pickDataDirectory(interface::Node& node); + static bool pickDataDirectory(interfaces::Node& node); /** * Determine default data directory for operating system. diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index 438e9e70f1..c0ddb89b40 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -13,7 +13,7 @@ #include <qt/guiutil.h> #include <qt/optionsmodel.h> -#include <interface/node.h> +#include <interfaces/node.h> #include <validation.h> // for DEFAULT_SCRIPTCHECK_THREADS and MAX_SCRIPTCHECK_THREADS #include <netbase.h> #include <txdb.h> // for -dbcache defaults diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index d8197b6ec6..cae9dace4c 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -11,7 +11,7 @@ #include <qt/bitcoinunits.h> #include <qt/guiutil.h> -#include <interface/node.h> +#include <interfaces/node.h> #include <validation.h> // For DEFAULT_SCRIPTCHECK_THREADS #include <net.h> #include <netbase.h> @@ -24,7 +24,9 @@ const char *DEFAULT_GUI_PROXY_HOST = "127.0.0.1"; -OptionsModel::OptionsModel(interface::Node& node, QObject *parent, bool resetSettings) : +static const QString GetDefaultProxyAddress(); + +OptionsModel::OptionsModel(interfaces::Node& node, QObject *parent, bool resetSettings) : QAbstractListModel(parent), m_node(node) { Init(resetSettings); @@ -121,7 +123,7 @@ void OptionsModel::Init(bool resetSettings) if (!settings.contains("fUseProxy")) settings.setValue("fUseProxy", false); if (!settings.contains("addrProxy")) - settings.setValue("addrProxy", QString("%1:%2").arg(DEFAULT_GUI_PROXY_HOST, DEFAULT_GUI_PROXY_PORT)); + settings.setValue("addrProxy", GetDefaultProxyAddress()); // Only try to set -proxy, if user has enabled fUseProxy if (settings.value("fUseProxy").toBool() && !m_node.softSetArg("-proxy", settings.value("addrProxy").toString().toStdString())) addOverriddenOption("-proxy"); @@ -131,7 +133,7 @@ void OptionsModel::Init(bool resetSettings) if (!settings.contains("fUseSeparateProxyTor")) settings.setValue("fUseSeparateProxyTor", false); if (!settings.contains("addrSeparateProxyTor")) - settings.setValue("addrSeparateProxyTor", QString("%1:%2").arg(DEFAULT_GUI_PROXY_HOST, DEFAULT_GUI_PROXY_PORT)); + settings.setValue("addrSeparateProxyTor", GetDefaultProxyAddress()); // Only try to set -onion, if user has enabled fUseSeparateProxyTor if (settings.value("fUseSeparateProxyTor").toBool() && !m_node.softSetArg("-onion", settings.value("addrSeparateProxyTor").toString().toStdString())) addOverriddenOption("-onion"); @@ -223,6 +225,11 @@ static void SetProxySetting(QSettings &settings, const QString &name, const Prox settings.setValue(name, ip_port.ip + ":" + ip_port.port); } +static const QString GetDefaultProxyAddress() +{ + return QString("%1:%2").arg(DEFAULT_GUI_PROXY_HOST).arg(DEFAULT_GUI_PROXY_PORT); +} + // read QSettings values and return them QVariant OptionsModel::data(const QModelIndex & index, int role) const { @@ -485,4 +492,16 @@ void OptionsModel::checkAndMigrate() settings.setValue(strSettingsVersionKey, CLIENT_VERSION); } + + // Overwrite the 'addrProxy' setting in case it has been set to an illegal + // default value (see issue #12623; PR #12650). + if (settings.contains("addrProxy") && settings.value("addrProxy").toString().endsWith("%2")) { + settings.setValue("addrProxy", GetDefaultProxyAddress()); + } + + // Overwrite the 'addrSeparateProxyTor' setting in case it has been set to an illegal + // default value (see issue #12623; PR #12650). + if (settings.contains("addrSeparateProxyTor") && settings.value("addrSeparateProxyTor").toString().endsWith("%2")) { + settings.setValue("addrSeparateProxyTor", GetDefaultProxyAddress()); + } } diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index 96c6b8fa45..fc1d119a71 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -9,7 +9,7 @@ #include <QAbstractListModel> -namespace interface { +namespace interfaces { class Node; } @@ -31,7 +31,7 @@ class OptionsModel : public QAbstractListModel Q_OBJECT public: - explicit OptionsModel(interface::Node& node, QObject *parent = 0, bool resetSettings = false); + explicit OptionsModel(interfaces::Node& node, QObject *parent = 0, bool resetSettings = false); enum OptionID { StartAtStartup, // bool @@ -79,10 +79,10 @@ public: void setRestartRequired(bool fRequired); bool isRestartRequired() const; - interface::Node& node() const { return m_node; } + interfaces::Node& node() const { return m_node; } private: - interface::Node& m_node; + interfaces::Node& m_node; /* Qt-only settings */ bool fHideTrayIcon; bool fMinimizeToTray; diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 76d0220648..8e8788dad3 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -21,7 +21,7 @@ #define DECORATION_SIZE 54 #define NUM_ITEMS 5 -Q_DECLARE_METATYPE(interface::WalletBalances) +Q_DECLARE_METATYPE(interfaces::WalletBalances) class TxViewDelegate : public QAbstractItemDelegate { @@ -157,7 +157,7 @@ OverviewPage::~OverviewPage() delete ui; } -void OverviewPage::setBalance(const interface::WalletBalances& balances) +void OverviewPage::setBalance(const interfaces::WalletBalances& balances) { int unit = walletModel->getOptionsModel()->getDisplayUnit(); m_balances = balances; @@ -224,10 +224,10 @@ void OverviewPage::setWalletModel(WalletModel *model) ui->listTransactions->setModelColumn(TransactionTableModel::ToAddress); // Keep up to date with wallet - interface::Wallet& wallet = model->wallet(); - interface::WalletBalances balances = wallet.getBalances(); + interfaces::Wallet& wallet = model->wallet(); + interfaces::WalletBalances balances = wallet.getBalances(); setBalance(balances); - connect(model, SIGNAL(balanceChanged(interface::WalletBalances)), this, SLOT(setBalance(interface::WalletBalances))); + connect(model, SIGNAL(balanceChanged(interfaces::WalletBalances)), this, SLOT(setBalance(interfaces::WalletBalances))); connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h index 36333536e5..d519eca43a 100644 --- a/src/qt/overviewpage.h +++ b/src/qt/overviewpage.h @@ -5,7 +5,7 @@ #ifndef BITCOIN_QT_OVERVIEWPAGE_H #define BITCOIN_QT_OVERVIEWPAGE_H -#include <interface/wallet.h> +#include <interfaces/wallet.h> #include <QWidget> #include <memory> @@ -38,7 +38,7 @@ public: void showOutOfSyncWarning(bool fShow); public Q_SLOTS: - void setBalance(const interface::WalletBalances& balances); + void setBalance(const interfaces::WalletBalances& balances); Q_SIGNALS: void transactionClicked(const QModelIndex &index); @@ -48,7 +48,7 @@ private: Ui::OverviewPage *ui; ClientModel *clientModel; WalletModel *walletModel; - interface::WalletBalances m_balances; + interfaces::WalletBalances m_balances; TxViewDelegate *txdelegate; std::unique_ptr<TransactionFilterProxy> filter; diff --git a/src/qt/paymentrequestplus.cpp b/src/qt/paymentrequestplus.cpp index 357e98a53c..b0ef475b35 100644 --- a/src/qt/paymentrequestplus.cpp +++ b/src/qt/paymentrequestplus.cpp @@ -9,7 +9,6 @@ #include <qt/paymentrequestplus.h> -#include <script/script.h> #include <util.h> #include <stdexcept> diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp index 275dba0b15..59bb5d5bb6 100644 --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -9,7 +9,7 @@ #include <qt/optionsmodel.h> #include <chainparams.h> -#include <interface/node.h> +#include <interfaces/node.h> #include <policy/policy.h> #include <key_io.h> #include <ui_interface.h> @@ -201,7 +201,7 @@ void PaymentServer::LoadRootCAs(X509_STORE* _store) // Warning: ipcSendCommandLine() is called early in init, // so don't use "Q_EMIT message()", but "QMessageBox::"! // -void PaymentServer::ipcParseCommandLine(interface::Node& node, int argc, char* argv[]) +void PaymentServer::ipcParseCommandLine(interfaces::Node& node, int argc, char* argv[]) { for (int i = 1; i < argc; i++) { @@ -760,7 +760,7 @@ void PaymentServer::handlePaymentACK(const QString& paymentACKMsg) Q_EMIT message(tr("Payment acknowledged"), paymentACKMsg, CClientUIInterface::ICON_INFORMATION | CClientUIInterface::MODAL); } -bool PaymentServer::verifyNetwork(interface::Node& node, const payments::PaymentDetails& requestDetails) +bool PaymentServer::verifyNetwork(interfaces::Node& node, const payments::PaymentDetails& requestDetails) { bool fVerified = requestDetails.network() == node.getNetwork(); if (!fVerified) { diff --git a/src/qt/paymentserver.h b/src/qt/paymentserver.h index e139899e22..511fc5bd6e 100644 --- a/src/qt/paymentserver.h +++ b/src/qt/paymentserver.h @@ -60,7 +60,7 @@ class PaymentServer : public QObject public: // Parse URIs on command line // Returns false on error - static void ipcParseCommandLine(interface::Node& node, int argc, char *argv[]); + static void ipcParseCommandLine(interfaces::Node& node, int argc, char *argv[]); // Returns true if there were URIs on the command line // which were successfully sent to an already-running @@ -87,7 +87,7 @@ public: void setOptionsModel(OptionsModel *optionsModel); // Verify that the payment request network matches the client network - static bool verifyNetwork(interface::Node& node, const payments::PaymentDetails& requestDetails); + static bool verifyNetwork(interfaces::Node& node, const payments::PaymentDetails& requestDetails); // Verify if the payment request is expired static bool verifyExpired(const payments::PaymentDetails& requestDetails); // Verify the payment request size is valid as per BIP70 diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp index f33db8e761..7e318e3035 100644 --- a/src/qt/peertablemodel.cpp +++ b/src/qt/peertablemodel.cpp @@ -8,7 +8,7 @@ #include <qt/guiconstants.h> #include <qt/guiutil.h> -#include <interface/node.h> +#include <interfaces/node.h> #include <validation.h> // for cs_main #include <sync.h> @@ -57,12 +57,12 @@ public: std::map<NodeId, int> mapNodeRows; /** Pull a full list of peers from vNodes into our cache */ - void refreshPeers(interface::Node& node) + void refreshPeers(interfaces::Node& node) { { cachedNodeStats.clear(); - interface::Node::NodesStats nodes_stats; + interfaces::Node::NodesStats nodes_stats; node.getNodesStats(nodes_stats); #if QT_VERSION >= 0x040700 cachedNodeStats.reserve(nodes_stats.size()); @@ -102,7 +102,7 @@ public: } }; -PeerTableModel::PeerTableModel(interface::Node& node, ClientModel *parent) : +PeerTableModel::PeerTableModel(interfaces::Node& node, ClientModel *parent) : QAbstractTableModel(parent), m_node(node), clientModel(parent), diff --git a/src/qt/peertablemodel.h b/src/qt/peertablemodel.h index c53462c57c..69c9744c8f 100644 --- a/src/qt/peertablemodel.h +++ b/src/qt/peertablemodel.h @@ -16,7 +16,7 @@ class ClientModel; class PeerTablePriv; -namespace interface { +namespace interfaces { class Node; } @@ -51,7 +51,7 @@ class PeerTableModel : public QAbstractTableModel Q_OBJECT public: - explicit PeerTableModel(interface::Node& node, ClientModel *parent = 0); + explicit PeerTableModel(interfaces::Node& node, ClientModel *parent = 0); ~PeerTableModel(); const CNodeCombinedStats *getNodeStats(int idx); int getRowByNodeId(NodeId nodeid); @@ -82,7 +82,7 @@ public Q_SLOTS: void refresh(); private: - interface::Node& m_node; + interfaces::Node& m_node; ClientModel *clientModel; QStringList columns; std::unique_ptr<PeerTablePriv> priv; diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 745055b944..7924840d0b 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -14,7 +14,7 @@ #include <qt/platformstyle.h> #include <qt/walletmodel.h> #include <chainparams.h> -#include <interface/node.h> +#include <interfaces/node.h> #include <netbase.h> #include <rpc/server.h> #include <rpc/client.h> @@ -85,7 +85,7 @@ class RPCExecutor : public QObject { Q_OBJECT public: - RPCExecutor(interface::Node& node) : m_node(node) {} + RPCExecutor(interfaces::Node& node) : m_node(node) {} public Q_SLOTS: void request(const QString &command, const QString &walletID); @@ -94,7 +94,7 @@ Q_SIGNALS: void reply(int category, const QString &command); private: - interface::Node& m_node; + interfaces::Node& m_node; }; /** Class for handling RPC timers @@ -153,7 +153,7 @@ public: * @param[out] pstrFilteredOut Command line, filtered to remove any sensitive data */ -bool RPCConsole::RPCParseCommandLine(interface::Node* node, std::string &strResult, const std::string &strCommand, const bool fExecute, std::string * const pstrFilteredOut, const std::string *walletID) +bool RPCConsole::RPCParseCommandLine(interfaces::Node* node, std::string &strResult, const std::string &strCommand, const bool fExecute, std::string * const pstrFilteredOut, const std::string *walletID) { std::vector< std::vector<std::string> > stack; stack.push_back(std::vector<std::string>()); @@ -451,16 +451,11 @@ void RPCExecutor::request(const QString &command, const QString &walletID) } } -RPCConsole::RPCConsole(interface::Node& node, const PlatformStyle *_platformStyle, QWidget *parent) : +RPCConsole::RPCConsole(interfaces::Node& node, const PlatformStyle *_platformStyle, QWidget *parent) : QWidget(parent), m_node(node), ui(new Ui::RPCConsole), - clientModel(0), - historyPtr(0), - platformStyle(_platformStyle), - peersTableContextMenu(0), - banTableContextMenu(0), - consoleFontSize(0) + platformStyle(_platformStyle) { ui->setupUi(this); QSettings settings; @@ -575,7 +570,7 @@ void RPCConsole::setClientModel(ClientModel *model) setNumConnections(model->getNumConnections()); connect(model, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int))); - interface::Node& node = clientModel->node(); + interfaces::Node& node = clientModel->node(); setNumBlocks(node.getNumBlocks(), QDateTime::fromTime_t(node.getLastBlockTime()), node.getVerificationProgress(), false); connect(model, SIGNAL(numBlocksChanged(int,QDateTime,double,bool)), this, SLOT(setNumBlocks(int,QDateTime,double,bool))); diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h index 8381301759..a53c4c24f9 100644 --- a/src/qt/rpcconsole.h +++ b/src/qt/rpcconsole.h @@ -19,7 +19,7 @@ class PlatformStyle; class RPCTimerInterface; class WalletModel; -namespace interface { +namespace interfaces { class Node; } @@ -38,11 +38,11 @@ class RPCConsole: public QWidget Q_OBJECT public: - explicit RPCConsole(interface::Node& node, const PlatformStyle *platformStyle, QWidget *parent); + explicit RPCConsole(interfaces::Node& node, const PlatformStyle *platformStyle, QWidget *parent); ~RPCConsole(); - static bool RPCParseCommandLine(interface::Node* node, std::string &strResult, const std::string &strCommand, bool fExecute, std::string * const pstrFilteredOut = nullptr, const std::string *walletID = nullptr); - static bool RPCExecuteCommandLine(interface::Node& node, std::string &strResult, const std::string &strCommand, std::string * const pstrFilteredOut = nullptr, const std::string *walletID = nullptr) { + static bool RPCParseCommandLine(interfaces::Node* node, std::string &strResult, const std::string &strCommand, bool fExecute, std::string * const pstrFilteredOut = nullptr, const std::string *walletID = nullptr); + static bool RPCExecuteCommandLine(interfaces::Node& node, std::string &strResult, const std::string &strCommand, std::string * const pstrFilteredOut = nullptr, const std::string *walletID = nullptr) { return RPCParseCommandLine(&node, strResult, strCommand, true, pstrFilteredOut, walletID); } @@ -144,19 +144,19 @@ private: }; - interface::Node& m_node; - Ui::RPCConsole *ui; - ClientModel *clientModel; + interfaces::Node& m_node; + Ui::RPCConsole* const ui; + ClientModel *clientModel = nullptr; QStringList history; - int historyPtr; + int historyPtr = 0; QString cmdBeforeBrowsing; QList<NodeId> cachedNodeids; - const PlatformStyle *platformStyle; - RPCTimerInterface *rpcTimerInterface; - QMenu *peersTableContextMenu; - QMenu *banTableContextMenu; - int consoleFontSize; - QCompleter *autoCompleter; + const PlatformStyle* const platformStyle; + RPCTimerInterface *rpcTimerInterface = nullptr; + QMenu *peersTableContextMenu = nullptr; + QMenu *banTableContextMenu = nullptr; + int consoleFontSize = 0; + QCompleter *autoCompleter = nullptr; QThread thread; QString m_last_wallet_id; diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index b4c1471a4f..261ab7a948 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -15,7 +15,7 @@ #include <qt/sendcoinsentry.h> #include <chainparams.h> -#include <interface/node.h> +#include <interfaces/node.h> #include <key_io.h> #include <wallet/coincontrol.h> #include <ui_interface.h> @@ -114,7 +114,7 @@ SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle, QWidget *p if (!settings.contains("nSmartFeeSliderPosition")) settings.setValue("nSmartFeeSliderPosition", 0); if (!settings.contains("nTransactionFee")) - settings.setValue("nTransactionFee", (qint64)DEFAULT_TRANSACTION_FEE); + settings.setValue("nTransactionFee", (qint64)DEFAULT_PAY_TX_FEE); if (!settings.contains("fPayOnlyMinFee")) settings.setValue("fPayOnlyMinFee", false); ui->groupFee->setId(ui->radioSmartFee, 0); @@ -149,9 +149,9 @@ void SendCoinsDialog::setModel(WalletModel *_model) } } - interface::WalletBalances balances = _model->wallet().getBalances(); + interfaces::WalletBalances balances = _model->wallet().getBalances(); setBalance(balances); - connect(_model, SIGNAL(balanceChanged(interface::WalletBalances)), this, SLOT(setBalance(interface::WalletBalances))); + connect(_model, SIGNAL(balanceChanged(interfaces::WalletBalances)), this, SLOT(setBalance(interfaces::WalletBalances))); connect(_model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); updateDisplayUnit(); @@ -175,7 +175,7 @@ void SendCoinsDialog::setModel(WalletModel *_model) connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(coinControlUpdateLabels())); connect(ui->optInRBF, SIGNAL(stateChanged(int)), this, SLOT(updateSmartFeeLabel())); connect(ui->optInRBF, SIGNAL(stateChanged(int)), this, SLOT(coinControlUpdateLabels())); - ui->customFee->setSingleStep(model->node().getRequiredFee(1000)); + ui->customFee->setSingleStep(model->wallet().getRequiredFee(1000)); updateFeeSectionControls(); updateMinFeeLabel(); updateSmartFeeLabel(); @@ -193,7 +193,7 @@ void SendCoinsDialog::setModel(WalletModel *_model) settings.remove("nSmartFeeSliderPosition"); } if (settings.value("nConfTarget").toInt() == 0) - ui->confTargetSelector->setCurrentIndex(getIndexForConfTarget(model->node().getTxConfirmTarget())); + ui->confTargetSelector->setCurrentIndex(getIndexForConfTarget(model->wallet().getConfirmTarget())); else ui->confTargetSelector->setCurrentIndex(getIndexForConfTarget(settings.value("nConfTarget").toInt())); } @@ -515,7 +515,7 @@ bool SendCoinsDialog::handlePaymentRequest(const SendCoinsRecipient &rv) return true; } -void SendCoinsDialog::setBalance(const interface::WalletBalances& balances) +void SendCoinsDialog::setBalance(const interfaces::WalletBalances& balances) { if(model && model->getOptionsModel()) { @@ -629,7 +629,7 @@ void SendCoinsDialog::useAvailableBalance(SendCoinsEntry* entry) void SendCoinsDialog::setMinimumFee() { - ui->customFee->setValue(model->node().getRequiredFee(1000)); + ui->customFee->setValue(model->wallet().getRequiredFee(1000)); } void SendCoinsDialog::updateFeeSectionControls() @@ -661,7 +661,7 @@ void SendCoinsDialog::updateMinFeeLabel() { if (model && model->getOptionsModel()) ui->checkBoxMinimumFee->setText(tr("Pay only the required fee of %1").arg( - BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), model->node().getRequiredFee(1000)) + "/kB") + BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), model->wallet().getRequiredFee(1000)) + "/kB") ); } @@ -675,7 +675,7 @@ void SendCoinsDialog::updateCoinControlState(CCoinControl& ctrl) // Avoid using global defaults when sending money from the GUI // Either custom fee will be used or if not selected, the confirmation target from dropdown box ctrl.m_confirm_target = getConfTargetForIndex(ui->confTargetSelector->currentIndex()); - ctrl.signalRbf = ui->optInRBF->isChecked(); + ctrl.m_signal_bip125_rbf = ui->optInRBF->isChecked(); } void SendCoinsDialog::updateSmartFeeLabel() @@ -687,7 +687,7 @@ void SendCoinsDialog::updateSmartFeeLabel() coin_control.m_feerate.reset(); // Explicitly use only fee estimation rate for smart fee labels int returned_target; FeeReason reason; - CFeeRate feeRate = CFeeRate(model->node().getMinimumFee(1000, coin_control, &returned_target, &reason)); + CFeeRate feeRate = CFeeRate(model->wallet().getMinimumFee(1000, coin_control, &returned_target, &reason)); ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), feeRate.GetFeePerK()) + "/kB"); diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h index 43757e3186..40a1d10c2b 100644 --- a/src/qt/sendcoinsdialog.h +++ b/src/qt/sendcoinsdialog.h @@ -51,7 +51,7 @@ public Q_SLOTS: void accept(); SendCoinsEntry *addEntry(); void updateTabsAndLabels(); - void setBalance(const interface::WalletBalances& balances); + void setBalance(const interfaces::WalletBalances& balances); Q_SIGNALS: void coinsSent(const uint256& txid); diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp index 6a961d83ea..977425f7e3 100644 --- a/src/qt/sendcoinsentry.cpp +++ b/src/qt/sendcoinsentry.cpp @@ -127,7 +127,7 @@ void SendCoinsEntry::useAvailableBalanceClicked() Q_EMIT useAvailableBalance(this); } -bool SendCoinsEntry::validate(interface::Node& node) +bool SendCoinsEntry::validate(interfaces::Node& node) { if (!model) return false; diff --git a/src/qt/sendcoinsentry.h b/src/qt/sendcoinsentry.h index 715f4cfde5..76f96c61e0 100644 --- a/src/qt/sendcoinsentry.h +++ b/src/qt/sendcoinsentry.h @@ -30,7 +30,7 @@ public: ~SendCoinsEntry(); void setModel(WalletModel *model); - bool validate(interface::Node& node); + bool validate(interfaces::Node& node); SendCoinsRecipient getValue(); /** Return whether the entry is still empty and unedited */ diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp index 2475a82ef9..4d972b431c 100644 --- a/src/qt/splashscreen.cpp +++ b/src/qt/splashscreen.cpp @@ -12,9 +12,9 @@ #include <clientversion.h> #include <init.h> -#include <interface/handler.h> -#include <interface/node.h> -#include <interface/wallet.h> +#include <interfaces/handler.h> +#include <interfaces/node.h> +#include <interfaces/wallet.h> #include <util.h> #include <ui_interface.h> #include <version.h> @@ -25,7 +25,7 @@ #include <QPainter> #include <QRadialGradient> -SplashScreen::SplashScreen(interface::Node& node, Qt::WindowFlags f, const NetworkStyle *networkStyle) : +SplashScreen::SplashScreen(interfaces::Node& node, Qt::WindowFlags f, const NetworkStyle *networkStyle) : QWidget(0, f), curAlignment(0), m_node(node) { // set reference point, paddings @@ -177,7 +177,7 @@ static void ShowProgress(SplashScreen *splash, const std::string &title, int nPr strprintf("\n%d", nProgress) + "%"); } #ifdef ENABLE_WALLET -void SplashScreen::ConnectWallet(std::unique_ptr<interface::Wallet> wallet) +void SplashScreen::ConnectWallet(std::unique_ptr<interfaces::Wallet> wallet) { m_connected_wallet_handlers.emplace_back(wallet->handleShowProgress(boost::bind(ShowProgress, this, _1, _2, false))); m_connected_wallets.emplace_back(std::move(wallet)); @@ -190,7 +190,7 @@ void SplashScreen::subscribeToCoreSignals() m_handler_init_message = m_node.handleInitMessage(boost::bind(InitMessage, this, _1)); m_handler_show_progress = m_node.handleShowProgress(boost::bind(ShowProgress, this, _1, _2, _3)); #ifdef ENABLE_WALLET - m_handler_load_wallet = m_node.handleLoadWallet([this](std::unique_ptr<interface::Wallet> wallet) { ConnectWallet(std::move(wallet)); }); + m_handler_load_wallet = m_node.handleLoadWallet([this](std::unique_ptr<interfaces::Wallet> wallet) { ConnectWallet(std::move(wallet)); }); #endif } diff --git a/src/qt/splashscreen.h b/src/qt/splashscreen.h index 419f36f641..9ef19675d8 100644 --- a/src/qt/splashscreen.h +++ b/src/qt/splashscreen.h @@ -12,7 +12,7 @@ class NetworkStyle; -namespace interface { +namespace interfaces { class Handler; class Node; class Wallet; @@ -29,7 +29,7 @@ class SplashScreen : public QWidget Q_OBJECT public: - explicit SplashScreen(interface::Node& node, Qt::WindowFlags f, const NetworkStyle *networkStyle); + explicit SplashScreen(interfaces::Node& node, Qt::WindowFlags f, const NetworkStyle *networkStyle); ~SplashScreen(); protected: @@ -52,19 +52,19 @@ private: /** Disconnect core signals to splash screen */ void unsubscribeFromCoreSignals(); /** Connect wallet signals to splash screen */ - void ConnectWallet(std::unique_ptr<interface::Wallet> wallet); + void ConnectWallet(std::unique_ptr<interfaces::Wallet> wallet); QPixmap pixmap; QString curMessage; QColor curColor; int curAlignment; - interface::Node& m_node; - std::unique_ptr<interface::Handler> m_handler_init_message; - std::unique_ptr<interface::Handler> m_handler_show_progress; - std::unique_ptr<interface::Handler> m_handler_load_wallet; - std::list<std::unique_ptr<interface::Wallet>> m_connected_wallets; - std::list<std::unique_ptr<interface::Handler>> m_connected_wallet_handlers; + interfaces::Node& m_node; + std::unique_ptr<interfaces::Handler> m_handler_init_message; + std::unique_ptr<interfaces::Handler> m_handler_show_progress; + std::unique_ptr<interfaces::Handler> m_handler_load_wallet; + std::list<std::unique_ptr<interfaces::Wallet>> m_connected_wallets; + std::list<std::unique_ptr<interfaces::Handler>> m_connected_wallet_handlers; }; #endif // BITCOIN_QT_SPLASHSCREEN_H diff --git a/src/qt/test/addressbooktests.cpp b/src/qt/test/addressbooktests.cpp new file mode 100644 index 0000000000..0c2e7ae71d --- /dev/null +++ b/src/qt/test/addressbooktests.cpp @@ -0,0 +1,143 @@ +#include <qt/test/addressbooktests.h> +#include <qt/test/util.h> +#include <test/test_bitcoin.h> + +#include <interfaces/node.h> +#include <qt/addressbookpage.h> +#include <qt/addresstablemodel.h> +#include <qt/editaddressdialog.h> +#include <qt/callback.h> +#include <qt/optionsmodel.h> +#include <qt/platformstyle.h> +#include <qt/qvalidatedlineedit.h> +#include <qt/walletmodel.h> + +#include <key.h> +#include <pubkey.h> +#include <key_io.h> +#include <wallet/wallet.h> + +#include <QTimer> +#include <QMessageBox> + +namespace +{ + +/** + * Fill the edit address dialog box with data, submit it, and ensure that + * the resulting message meets expectations. + */ +void EditAddressAndSubmit( + EditAddressDialog* dialog, + const QString& label, const QString& address, QString expected_msg) +{ + QString warning_text; + + dialog->findChild<QLineEdit*>("labelEdit")->setText(label); + dialog->findChild<QValidatedLineEdit*>("addressEdit")->setText(address); + + ConfirmMessage(&warning_text, 5); + dialog->accept(); + QCOMPARE(warning_text, expected_msg); +} + +/** + * Test adding various send addresses to the address book. + * + * There are three cases tested: + * + * - new_address: a new address which should add as a send address successfully. + * - existing_s_address: an existing sending address which won't add successfully. + * - existing_r_address: an existing receiving address which won't add successfully. + * + * In each case, verify the resulting state of the address book and optionally + * the warning message presented to the user. + */ +void TestAddAddressesToSendBook() +{ + TestChain100Setup test; + CWallet wallet("mock", WalletDatabase::CreateMock()); + bool firstRun; + wallet.LoadWallet(firstRun); + + auto build_address = [&wallet]() { + CKey key; + key.MakeNewKey(true); + CTxDestination dest(GetDestinationForKey( + key.GetPubKey(), wallet.m_default_address_type)); + + return std::make_pair(dest, QString::fromStdString(EncodeDestination(dest))); + }; + + CTxDestination r_key_dest, s_key_dest; + + // Add a preexisting "receive" entry in the address book. + QString preexisting_r_address; + QString r_label("already here (r)"); + + // Add a preexisting "send" entry in the address book. + QString preexisting_s_address; + QString s_label("already here (s)"); + + // Define a new address (which should add to the address book successfully). + QString new_address; + + std::tie(r_key_dest, preexisting_r_address) = build_address(); + std::tie(s_key_dest, preexisting_s_address) = build_address(); + std::tie(std::ignore, new_address) = build_address(); + + { + LOCK(wallet.cs_wallet); + wallet.SetAddressBook(r_key_dest, r_label.toStdString(), "receive"); + wallet.SetAddressBook(s_key_dest, s_label.toStdString(), "send"); + } + + auto check_addbook_size = [&wallet](int expected_size) { + QCOMPARE(static_cast<int>(wallet.mapAddressBook.size()), expected_size); + }; + + // We should start with the two addresses we added earlier and nothing else. + check_addbook_size(2); + + // Initialize relevant QT models. + std::unique_ptr<const PlatformStyle> platformStyle(PlatformStyle::instantiate("other")); + auto node = interfaces::MakeNode(); + OptionsModel optionsModel(*node); + AddWallet(&wallet); + WalletModel walletModel(std::move(node->getWallets()[0]), *node, platformStyle.get(), &optionsModel); + RemoveWallet(&wallet); + EditAddressDialog editAddressDialog(EditAddressDialog::NewSendingAddress); + editAddressDialog.setModel(walletModel.getAddressTableModel()); + + EditAddressAndSubmit( + &editAddressDialog, QString("uhoh"), preexisting_r_address, + QString( + "Address \"%1\" already exists as a receiving address with label " + "\"%2\" and so cannot be added as a sending address." + ).arg(preexisting_r_address).arg(r_label)); + + check_addbook_size(2); + + EditAddressAndSubmit( + &editAddressDialog, QString("uhoh, different"), preexisting_s_address, + QString( + "The entered address \"%1\" is already in the address book with " + "label \"%2\"." + ).arg(preexisting_s_address).arg(s_label)); + + check_addbook_size(2); + + // Submit a new address which should add successfully - we expect the + // warning message to be blank. + EditAddressAndSubmit( + &editAddressDialog, QString("new"), new_address, QString("")); + + check_addbook_size(3); +} + +} // namespace + +void AddressBookTests::addressBookTests() +{ + TestAddAddressesToSendBook(); +} diff --git a/src/qt/test/addressbooktests.h b/src/qt/test/addressbooktests.h new file mode 100644 index 0000000000..beeb9e76a9 --- /dev/null +++ b/src/qt/test/addressbooktests.h @@ -0,0 +1,15 @@ +#ifndef BITCOIN_QT_TEST_ADDRESSBOOKTESTS_H +#define BITCOIN_QT_TEST_ADDRESSBOOKTESTS_H + +#include <QObject> +#include <QTest> + +class AddressBookTests : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void addressBookTests(); +}; + +#endif // BITCOIN_QT_TEST_ADDRESSBOOKTESTS_H diff --git a/src/qt/test/paymentservertests.cpp b/src/qt/test/paymentservertests.cpp index 46e9d4d0a4..83484b5ce7 100644 --- a/src/qt/test/paymentservertests.cpp +++ b/src/qt/test/paymentservertests.cpp @@ -9,7 +9,7 @@ #include <amount.h> #include <chainparams.h> -#include <interface/node.h> +#include <interfaces/node.h> #include <random.h> #include <script/script.h> #include <script/standard.h> @@ -67,7 +67,7 @@ static SendCoinsRecipient handleRequest(PaymentServer* server, std::vector<unsig void PaymentServerTests::paymentServerTests() { SelectParams(CBaseChainParams::MAIN); - auto node = interface::MakeNode(); + auto node = interfaces::MakeNode(); OptionsModel optionsModel(*node); PaymentServer* server = new PaymentServer(nullptr, false); X509_STORE* caStore = X509_STORE_new(); diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp index 467107dc4c..974e7831c4 100644 --- a/src/qt/test/rpcnestedtests.cpp +++ b/src/qt/test/rpcnestedtests.cpp @@ -7,7 +7,7 @@ #include <chainparams.h> #include <consensus/validation.h> #include <fs.h> -#include <interface/node.h> +#include <interfaces/node.h> #include <validation.h> #include <rpc/register.h> #include <rpc/server.h> @@ -46,7 +46,7 @@ void RPCNestedTests::rpcNestedTests() std::string result; std::string result2; std::string filtered; - auto node = interface::MakeNode(); + auto node = interfaces::MakeNode(); RPCConsole::RPCExecuteCommandLine(*node, result, "getblockchaininfo()[chain]", &filtered); //simple result filtering with path QVERIFY(result=="main"); QVERIFY(filtered == "getblockchaininfo()[chain]"); diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp index 25ee66dc6b..56d4d3e457 100644 --- a/src/qt/test/test_main.cpp +++ b/src/qt/test/test_main.cpp @@ -13,6 +13,7 @@ #include <qt/test/compattests.h> #ifdef ENABLE_WALLET +#include <qt/test/addressbooktests.h> #include <qt/test/paymentservertests.h> #include <qt/test/wallettests.h> #endif @@ -99,6 +100,10 @@ int main(int argc, char *argv[]) if (QTest::qExec(&test5) != 0) { fInvalid = true; } + AddressBookTests test6; + if (QTest::qExec(&test6) != 0) { + fInvalid = true; + } #endif fs::remove_all(pathTemp); diff --git a/src/qt/test/util.cpp b/src/qt/test/util.cpp new file mode 100644 index 0000000000..261caaaee5 --- /dev/null +++ b/src/qt/test/util.cpp @@ -0,0 +1,22 @@ +#include <qt/callback.h> + +#include <QApplication> +#include <QMessageBox> +#include <QTimer> +#include <QString> +#include <QPushButton> +#include <QWidget> + +void ConfirmMessage(QString* text, int msec) +{ + QTimer::singleShot(msec, makeCallback([text](Callback* callback) { + for (QWidget* widget : QApplication::topLevelWidgets()) { + if (widget->inherits("QMessageBox")) { + QMessageBox* messageBox = qobject_cast<QMessageBox*>(widget); + if (text) *text = messageBox->text(); + messageBox->defaultButton()->click(); + } + } + delete callback; + }), SLOT(call())); +} diff --git a/src/qt/test/util.h b/src/qt/test/util.h new file mode 100644 index 0000000000..324386c139 --- /dev/null +++ b/src/qt/test/util.h @@ -0,0 +1,12 @@ +#ifndef BITCOIN_QT_TEST_UTIL_H +#define BITCOIN_QT_TEST_UTIL_H + +/** + * Press "Ok" button in message box dialog. + * + * @param text - Optionally store dialog text. + * @param msec - Number of miliseconds to pause before triggering the callback. + */ +void ConfirmMessage(QString* text = nullptr, int msec = 0); + +#endif // BITCOIN_QT_TEST_UTIL_H diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp index b60c5b979f..a09d98dfe5 100644 --- a/src/qt/test/wallettests.cpp +++ b/src/qt/test/wallettests.cpp @@ -1,6 +1,7 @@ #include <qt/test/wallettests.h> +#include <qt/test/util.h> -#include <interface/node.h> +#include <interfaces/node.h> #include <qt/bitcoinamountfield.h> #include <qt/callback.h> #include <qt/optionsmodel.h> @@ -35,21 +36,6 @@ namespace { -//! Press "Ok" button in message box dialog. -void ConfirmMessage(QString* text = nullptr) -{ - QTimer::singleShot(0, makeCallback([text](Callback* callback) { - for (QWidget* widget : QApplication::topLevelWidgets()) { - if (widget->inherits("QMessageBox")) { - QMessageBox* messageBox = qobject_cast<QMessageBox*>(widget); - if (text) *text = messageBox->text(); - messageBox->defaultButton()->click(); - } - } - delete callback; - }), SLOT(call())); -} - //! Press "Yes" or "Cancel" buttons in modal send confirmation dialog. void ConfirmSend(QString* text = nullptr, bool cancel = false) { @@ -158,7 +144,7 @@ void TestGUI() for (int i = 0; i < 5; ++i) { test.CreateAndProcessBlock({}, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey())); } - CWallet wallet("mock", CWalletDBWrapper::CreateMock()); + CWallet wallet("mock", WalletDatabase::CreateMock()); bool firstRun; wallet.LoadWallet(firstRun); { @@ -178,11 +164,11 @@ void TestGUI() std::unique_ptr<const PlatformStyle> platformStyle(PlatformStyle::instantiate("other")); SendCoinsDialog sendCoinsDialog(platformStyle.get()); TransactionView transactionView(platformStyle.get()); - auto node = interface::MakeNode(); + auto node = interfaces::MakeNode(); OptionsModel optionsModel(*node); - vpwallets.insert(vpwallets.begin(), &wallet); - WalletModel walletModel(std::move(node->getWallets()[0]), *node, platformStyle.get(), &optionsModel); - vpwallets.erase(vpwallets.begin()); + AddWallet(&wallet); + WalletModel walletModel(std::move(node->getWallets().back()), *node, platformStyle.get(), &optionsModel); + RemoveWallet(&wallet); sendCoinsDialog.setModel(&walletModel); transactionView.setModel(&walletModel); @@ -264,7 +250,7 @@ void TestGUI() QCOMPARE(requestTableModel->rowCount({}), currentRowCount-1); } -} +} // namespace void WalletTests::walletTests() { diff --git a/src/qt/trafficgraphwidget.cpp b/src/qt/trafficgraphwidget.cpp index ff378ed1bf..5a3b645f65 100644 --- a/src/qt/trafficgraphwidget.cpp +++ b/src/qt/trafficgraphwidget.cpp @@ -2,7 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include <interface/node.h> +#include <interfaces/node.h> #include <qt/trafficgraphwidget.h> #include <qt/clientmodel.h> diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index 409835592f..2cb446c459 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -10,7 +10,7 @@ #include <qt/transactionrecord.h> #include <consensus/consensus.h> -#include <interface/node.h> +#include <interfaces/node.h> #include <key_io.h> #include <validation.h> #include <script/script.h> @@ -23,7 +23,7 @@ #include <stdint.h> #include <string> -QString TransactionDesc::FormatTxStatus(const interface::WalletTx& wtx, const interface::WalletTxStatus& status, bool inMempool, int numBlocks, int64_t adjustedTime) +QString TransactionDesc::FormatTxStatus(const interfaces::WalletTx& wtx, const interfaces::WalletTxStatus& status, bool inMempool, int numBlocks, int64_t adjustedTime) { if (!status.is_final) { @@ -48,14 +48,14 @@ QString TransactionDesc::FormatTxStatus(const interface::WalletTx& wtx, const in } } -QString TransactionDesc::toHTML(interface::Node& node, interface::Wallet& wallet, TransactionRecord *rec, int unit) +QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wallet, TransactionRecord *rec, int unit) { int numBlocks; int64_t adjustedTime; - interface::WalletTxStatus status; - interface::WalletOrderForm orderForm; + interfaces::WalletTxStatus status; + interfaces::WalletOrderForm orderForm; bool inMempool; - interface::WalletTx wtx = wallet.getWalletTxDetails(rec->hash, status, orderForm, inMempool, numBlocks, adjustedTime); + interfaces::WalletTx wtx = wallet.getWalletTxDetails(rec->hash, status, orderForm, inMempool, numBlocks, adjustedTime); QString strHTML; @@ -102,7 +102,7 @@ QString TransactionDesc::toHTML(interface::Node& node, interface::Wallet& wallet if (IsValidDestination(address)) { std::string name; isminetype ismine; - if (wallet.getAddress(address, &name, &ismine)) + if (wallet.getAddress(address, &name, &ismine, /* purpose= */ nullptr)) { strHTML += "<b>" + tr("From") + ":</b> " + tr("unknown") + "<br>"; strHTML += "<b>" + tr("To") + ":</b> "; @@ -128,7 +128,8 @@ QString TransactionDesc::toHTML(interface::Node& node, interface::Wallet& wallet strHTML += "<b>" + tr("To") + ":</b> "; CTxDestination dest = DecodeDestination(strAddress); std::string name; - if (wallet.getAddress(dest, &name) && !name.empty()) + if (wallet.getAddress( + dest, &name, /* is_mine= */ nullptr, /* purpose= */ nullptr) && !name.empty()) strHTML += GUIUtil::HtmlEscape(name) + " "; strHTML += GUIUtil::HtmlEscape(strAddress) + "<br>"; } @@ -196,7 +197,8 @@ QString TransactionDesc::toHTML(interface::Node& node, interface::Wallet& wallet { strHTML += "<b>" + tr("To") + ":</b> "; std::string name; - if (wallet.getAddress(address, &name) && !name.empty()) + if (wallet.getAddress( + address, &name, /* is_mine= */ nullptr, /* purpose= */ nullptr) && !name.empty()) strHTML += GUIUtil::HtmlEscape(name) + " "; strHTML += GUIUtil::HtmlEscape(EncodeDestination(address)); if(toSelf == ISMINE_SPENDABLE) @@ -319,7 +321,7 @@ QString TransactionDesc::toHTML(interface::Node& node, interface::Wallet& wallet if (ExtractDestination(vout.scriptPubKey, address)) { std::string name; - if (wallet.getAddress(address, &name) && !name.empty()) + if (wallet.getAddress(address, &name, /* is_mine= */ nullptr, /* purpose= */ nullptr) && !name.empty()) strHTML += GUIUtil::HtmlEscape(name) + " "; strHTML += QString::fromStdString(EncodeDestination(address)); } diff --git a/src/qt/transactiondesc.h b/src/qt/transactiondesc.h index ea9ab28ee3..cb8453cb81 100644 --- a/src/qt/transactiondesc.h +++ b/src/qt/transactiondesc.h @@ -10,7 +10,7 @@ class TransactionRecord; -namespace interface { +namespace interfaces { class Node; class Wallet; struct WalletTx; @@ -24,12 +24,12 @@ class TransactionDesc: public QObject Q_OBJECT public: - static QString toHTML(interface::Node& node, interface::Wallet& wallet, TransactionRecord *rec, int unit); + static QString toHTML(interfaces::Node& node, interfaces::Wallet& wallet, TransactionRecord *rec, int unit); private: TransactionDesc() {} - static QString FormatTxStatus(const interface::WalletTx& wtx, const interface::WalletTxStatus& status, bool inMempool, int numBlocks, int64_t adjustedTime); + static QString FormatTxStatus(const interfaces::WalletTx& wtx, const interfaces::WalletTxStatus& status, bool inMempool, int numBlocks, int64_t adjustedTime); }; #endif // BITCOIN_QT_TRANSACTIONDESC_H diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index 60ab2d57d7..b6ed66ad96 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -5,7 +5,7 @@ #include <qt/transactionrecord.h> #include <consensus/consensus.h> -#include <interface/wallet.h> +#include <interfaces/wallet.h> #include <key_io.h> #include <timedata.h> #include <validation.h> @@ -25,7 +25,7 @@ bool TransactionRecord::showTransaction() /* * Decompose CWallet transaction to model transaction records. */ -QList<TransactionRecord> TransactionRecord::decomposeTransaction(const interface::WalletTx& wtx) +QList<TransactionRecord> TransactionRecord::decomposeTransaction(const interfaces::WalletTx& wtx) { QList<TransactionRecord> parts; int64_t nTime = wtx.time; @@ -158,7 +158,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const interface return parts; } -void TransactionRecord::updateStatus(const interface::WalletTxStatus& wtx, int numBlocks, int64_t adjustedTime) +void TransactionRecord::updateStatus(const interfaces::WalletTxStatus& wtx, int numBlocks, int64_t adjustedTime) { // Determine transaction status diff --git a/src/qt/transactionrecord.h b/src/qt/transactionrecord.h index c653584b52..62961434ed 100644 --- a/src/qt/transactionrecord.h +++ b/src/qt/transactionrecord.h @@ -11,7 +11,7 @@ #include <QList> #include <QString> -namespace interface { +namespace interfaces { class Node; class Wallet; struct WalletTx; @@ -111,7 +111,7 @@ public: /** Decompose CWallet transaction to model transaction records. */ static bool showTransaction(); - static QList<TransactionRecord> decomposeTransaction(const interface::WalletTx& wtx); + static QList<TransactionRecord> decomposeTransaction(const interfaces::WalletTx& wtx); /** @name Immutable transaction attributes @{*/ @@ -140,7 +140,7 @@ public: /** Update status from core wallet tx. */ - void updateStatus(const interface::WalletTxStatus& wtx, int numBlocks, int64_t adjustedTime); + void updateStatus(const interfaces::WalletTxStatus& wtx, int numBlocks, int64_t adjustedTime); /** Return whether a status update is needed. */ diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 2148ff1728..46169a91d1 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -14,8 +14,8 @@ #include <qt/walletmodel.h> #include <core_io.h> -#include <interface/handler.h> -#include <interface/node.h> +#include <interfaces/handler.h> +#include <interfaces/node.h> #include <validation.h> #include <sync.h> #include <uint256.h> @@ -73,7 +73,7 @@ public: /* Query entire wallet anew from core. */ - void refreshWallet(interface::Wallet& wallet) + void refreshWallet(interfaces::Wallet& wallet) { qDebug() << "TransactionTablePriv::refreshWallet"; cachedWallet.clear(); @@ -91,7 +91,7 @@ public: Call with transaction that was added, removed or changed. */ - void updateWallet(interface::Wallet& wallet, const uint256 &hash, int status, bool showTransaction) + void updateWallet(interfaces::Wallet& wallet, const uint256 &hash, int status, bool showTransaction) { qDebug() << "TransactionTablePriv::updateWallet: " + QString::fromStdString(hash.ToString()) + " " + QString::number(status); @@ -127,7 +127,7 @@ public: if(showTransaction) { // Find transaction in wallet - interface::WalletTx wtx = wallet.getWalletTx(hash); + interfaces::WalletTx wtx = wallet.getWalletTx(hash); if(!wtx.tx) { qWarning() << "TransactionTablePriv::updateWallet: Warning: Got CT_NEW, but transaction is not in wallet"; @@ -176,7 +176,7 @@ public: return cachedWallet.size(); } - TransactionRecord *index(interface::Wallet& wallet, int idx) + TransactionRecord *index(interfaces::Wallet& wallet, int idx) { if(idx >= 0 && idx < cachedWallet.size()) { @@ -189,7 +189,7 @@ public: // If a status update is needed (blocks came in since last check), // update the status of this transaction from the wallet. Otherwise, // simply re-use the cached status. - interface::WalletTxStatus wtx; + interfaces::WalletTxStatus wtx; int numBlocks; int64_t adjustedTime; if (wallet.tryGetTxStatus(rec->hash, wtx, numBlocks, adjustedTime) && rec->statusUpdateNeeded(numBlocks)) { @@ -200,12 +200,12 @@ public: return 0; } - QString describe(interface::Node& node, interface::Wallet& wallet, TransactionRecord *rec, int unit) + QString describe(interfaces::Node& node, interfaces::Wallet& wallet, TransactionRecord *rec, int unit) { return TransactionDesc::toHTML(node, wallet, rec, unit); } - QString getTxHex(interface::Wallet& wallet, TransactionRecord *rec) + QString getTxHex(interfaces::Wallet& wallet, TransactionRecord *rec) { auto tx = wallet.getTx(rec->hash); if (tx) { diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h index 57566db638..8b029be5f5 100644 --- a/src/qt/transactiontablemodel.h +++ b/src/qt/transactiontablemodel.h @@ -12,7 +12,7 @@ #include <memory> -namespace interface { +namespace interfaces { class Handler; } @@ -85,8 +85,8 @@ public: private: WalletModel *walletModel; - std::unique_ptr<interface::Handler> m_handler_transaction_changed; - std::unique_ptr<interface::Handler> m_handler_show_progress; + std::unique_ptr<interfaces::Handler> m_handler_transaction_changed; + std::unique_ptr<interfaces::Handler> m_handler_show_progress; QStringList columns; TransactionTablePriv *priv; bool fProcessingQueuedTransactions; diff --git a/src/qt/utilitydialog.cpp b/src/qt/utilitydialog.cpp index 6114ea0de1..d5b98486ae 100644 --- a/src/qt/utilitydialog.cpp +++ b/src/qt/utilitydialog.cpp @@ -19,7 +19,7 @@ #include <clientversion.h> #include <init.h> -#include <interface/node.h> +#include <interfaces/node.h> #include <util.h> #include <stdio.h> @@ -32,7 +32,7 @@ #include <QVBoxLayout> /** "Help message" or "About" dialog box */ -HelpMessageDialog::HelpMessageDialog(interface::Node& node, QWidget *parent, bool about) : +HelpMessageDialog::HelpMessageDialog(interfaces::Node& node, QWidget *parent, bool about) : QDialog(parent), ui(new Ui::HelpMessageDialog) { diff --git a/src/qt/utilitydialog.h b/src/qt/utilitydialog.h index e6ad7be5d0..f5c8af4362 100644 --- a/src/qt/utilitydialog.h +++ b/src/qt/utilitydialog.h @@ -10,7 +10,7 @@ class BitcoinGUI; -namespace interface { +namespace interfaces { class Node; } @@ -24,7 +24,7 @@ class HelpMessageDialog : public QDialog Q_OBJECT public: - explicit HelpMessageDialog(interface::Node& node, QWidget *parent, bool about); + explicit HelpMessageDialog(interfaces::Node& node, QWidget *parent, bool about); ~HelpMessageDialog(); void printToConsole(); diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index d9db437dc5..3418b1f1a9 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -12,8 +12,8 @@ #include <qt/sendcoinsdialog.h> #include <qt/transactiontablemodel.h> -#include <interface/handler.h> -#include <interface/node.h> +#include <interfaces/handler.h> +#include <interfaces/node.h> #include <key_io.h> #include <ui_interface.h> #include <util.h> // for GetBoolArg @@ -28,7 +28,7 @@ #include <QTimer> -WalletModel::WalletModel(std::unique_ptr<interface::Wallet> wallet, interface::Node& node, const PlatformStyle *platformStyle, OptionsModel *_optionsModel, QObject *parent) : +WalletModel::WalletModel(std::unique_ptr<interfaces::Wallet> wallet, interfaces::Node& node, const PlatformStyle *platformStyle, OptionsModel *_optionsModel, QObject *parent) : QObject(parent), m_wallet(std::move(wallet)), m_node(node), optionsModel(_optionsModel), addressTableModel(0), transactionTableModel(0), recentRequestsTableModel(0), @@ -70,7 +70,7 @@ void WalletModel::pollBalanceChanged() // avoids the GUI from getting stuck on periodical polls if the core is // holding the locks for a longer time - for example, during a wallet // rescan. - interface::WalletBalances new_balances; + interfaces::WalletBalances new_balances; int numBlocks = -1; if (!m_wallet->tryGetBalances(new_balances, numBlocks)) { return; @@ -89,7 +89,7 @@ void WalletModel::pollBalanceChanged() } } -void WalletModel::checkBalanceChanged(const interface::WalletBalances& new_balances) +void WalletModel::checkBalanceChanged(const interfaces::WalletBalances& new_balances) { if(new_balances.balanceChanged(m_cached_balances)) { m_cached_balances = new_balances; @@ -274,7 +274,8 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran { // Check if we have a new address or an updated label std::string name; - if (!m_wallet->getAddress(dest, &name)) + if (!m_wallet->getAddress( + dest, &name, /* is_mine= */ nullptr, /* purpose= */ nullptr)) { m_wallet->setAddressBook(dest, strLabel, "send"); } @@ -486,7 +487,7 @@ bool WalletModel::saveReceiveRequest(const std::string &sAddress, const int64_t bool WalletModel::bumpFee(uint256 hash) { CCoinControl coin_control; - coin_control.signalRbf = true; + coin_control.m_signal_bip125_rbf = true; std::vector<std::string> errors; CAmount old_fee; CAmount new_fee; diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index bc409b10ee..9173fcae52 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -13,7 +13,7 @@ #include <qt/paymentrequestplus.h> #include <qt/walletmodeltransaction.h> -#include <interface/wallet.h> +#include <interfaces/wallet.h> #include <support/allocators/secure.h> #include <map> @@ -37,9 +37,9 @@ class COutput; class CPubKey; class uint256; -namespace interface { +namespace interfaces { class Node; -} // namespace interface +} // namespace interfaces QT_BEGIN_NAMESPACE class QTimer; @@ -111,7 +111,7 @@ class WalletModel : public QObject Q_OBJECT public: - explicit WalletModel(std::unique_ptr<interface::Wallet> wallet, interface::Node& node, const PlatformStyle *platformStyle, OptionsModel *optionsModel, QObject *parent = 0); + explicit WalletModel(std::unique_ptr<interfaces::Wallet> wallet, interfaces::Node& node, const PlatformStyle *platformStyle, OptionsModel *optionsModel, QObject *parent = 0); ~WalletModel(); enum StatusCode // Returned by sendCoins @@ -198,20 +198,22 @@ public: static bool isWalletEnabled(); - interface::Node& node() const { return m_node; } - interface::Wallet& wallet() const { return *m_wallet; } + interfaces::Node& node() const { return m_node; } + interfaces::Wallet& wallet() const { return *m_wallet; } QString getWalletName() const; bool isMultiwallet(); + + AddressTableModel* getAddressTableModel() const { return addressTableModel; } private: - std::unique_ptr<interface::Wallet> m_wallet; - std::unique_ptr<interface::Handler> m_handler_status_changed; - std::unique_ptr<interface::Handler> m_handler_address_book_changed; - std::unique_ptr<interface::Handler> m_handler_transaction_changed; - std::unique_ptr<interface::Handler> m_handler_show_progress; - std::unique_ptr<interface::Handler> m_handler_watch_only_changed; - interface::Node& m_node; + std::unique_ptr<interfaces::Wallet> m_wallet; + 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; + std::unique_ptr<interfaces::Handler> m_handler_show_progress; + std::unique_ptr<interfaces::Handler> m_handler_watch_only_changed; + interfaces::Node& m_node; bool fHaveWatchOnly; bool fForceCheckBalanceChanged; @@ -225,7 +227,7 @@ private: RecentRequestsTableModel *recentRequestsTableModel; // Cache some values to be able to detect changes - interface::WalletBalances m_cached_balances; + interfaces::WalletBalances m_cached_balances; EncryptionStatus cachedEncryptionStatus; int cachedNumBlocks; @@ -233,11 +235,11 @@ private: void subscribeToCoreSignals(); void unsubscribeFromCoreSignals(); - void checkBalanceChanged(const interface::WalletBalances& new_balances); + void checkBalanceChanged(const interfaces::WalletBalances& new_balances); Q_SIGNALS: // Signal that balance in wallet changed - void balanceChanged(const interface::WalletBalances& balances); + void balanceChanged(const interfaces::WalletBalances& balances); // Encryption status of wallet changed void encryptionStatusChanged(); diff --git a/src/qt/walletmodeltransaction.cpp b/src/qt/walletmodeltransaction.cpp index 21bdfe3818..d5187d6634 100644 --- a/src/qt/walletmodeltransaction.cpp +++ b/src/qt/walletmodeltransaction.cpp @@ -4,7 +4,7 @@ #include <qt/walletmodeltransaction.h> -#include <interface/node.h> +#include <interfaces/node.h> #include <policy/policy.h> WalletModelTransaction::WalletModelTransaction(const QList<SendCoinsRecipient> &_recipients) : @@ -18,7 +18,7 @@ QList<SendCoinsRecipient> WalletModelTransaction::getRecipients() const return recipients; } -std::unique_ptr<interface::PendingWalletTx>& WalletModelTransaction::getWtx() +std::unique_ptr<interfaces::PendingWalletTx>& WalletModelTransaction::getWtx() { return wtx; } diff --git a/src/qt/walletmodeltransaction.h b/src/qt/walletmodeltransaction.h index 32fd0d2110..9f91326109 100644 --- a/src/qt/walletmodeltransaction.h +++ b/src/qt/walletmodeltransaction.h @@ -13,7 +13,7 @@ class SendCoinsRecipient; -namespace interface { +namespace interfaces { class Node; class PendingWalletTx; } @@ -26,7 +26,7 @@ public: QList<SendCoinsRecipient> getRecipients() const; - std::unique_ptr<interface::PendingWalletTx>& getWtx(); + std::unique_ptr<interfaces::PendingWalletTx>& getWtx(); unsigned int getTransactionSize(); void setTransactionFee(const CAmount& newFee); @@ -38,7 +38,7 @@ public: private: QList<SendCoinsRecipient> recipients; - std::unique_ptr<interface::PendingWalletTx> wtx; + std::unique_ptr<interfaces::PendingWalletTx> wtx; CAmount fee; }; diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp index 1505557244..d529595dec 100644 --- a/src/qt/walletview.cpp +++ b/src/qt/walletview.cpp @@ -19,7 +19,7 @@ #include <qt/transactionview.h> #include <qt/walletmodel.h> -#include <interface/node.h> +#include <interfaces/node.h> #include <ui_interface.h> #include <QAction> @@ -315,9 +315,9 @@ void WalletView::showProgress(const QString &title, int nProgress) progressDialog = new QProgressDialog(title, "", 0, 100); progressDialog->setWindowModality(Qt::ApplicationModal); progressDialog->setMinimumDuration(0); - progressDialog->setCancelButton(0); progressDialog->setAutoClose(false); progressDialog->setValue(0); + progressDialog->setCancelButtonText(tr("Cancel")); } else if (nProgress == 100) { @@ -327,8 +327,13 @@ void WalletView::showProgress(const QString &title, int nProgress) progressDialog->deleteLater(); } } - else if (progressDialog) - progressDialog->setValue(nProgress); + else if (progressDialog) { + if (progressDialog->wasCanceled()) { + getWalletModel()->wallet().abortRescan(); + } else { + progressDialog->setValue(nProgress); + } + } } void WalletView::requestedSyncWarningInfo() diff --git a/src/random.cpp b/src/random.cpp index a845526d8a..b004dcac39 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -15,7 +15,6 @@ #include <utilstrencodings.h> // for GetTime() #include <stdlib.h> -#include <limits> #include <chrono> #include <thread> diff --git a/src/rest.cpp b/src/rest.cpp index 5871b554a6..095655b3a0 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -6,6 +6,7 @@ #include <chain.h> #include <chainparams.h> #include <core_io.h> +#include <index/txindex.h> #include <primitives/block.h> #include <primitives/transaction.h> #include <validation.h> @@ -350,6 +351,10 @@ static bool rest_tx(HTTPRequest* req, const std::string& strURIPart) if (!ParseHashStr(hashStr, hash)) return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr); + if (g_txindex) { + g_txindex->BlockUntilSyncedToCurrentChain(); + } + CTransactionRef tx; uint256 hashBlock = uint256(); if (!GetTransaction(hash, tx, Params().GetConsensus(), hashBlock, true)) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 06c68ea27c..5593cc9acc 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -47,8 +47,6 @@ static std::mutex cs_blockchange; static std::condition_variable cond_blockchange; static CUpdatedBlock latestblock; -extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry); - /* Calculate the difficulty for a given block index, * or the block index of the given chain. */ @@ -360,17 +358,23 @@ UniValue getdifficulty(const JSONRPCRequest& request) std::string EntryDescriptionString() { return " \"size\" : n, (numeric) virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted.\n" - " \"fee\" : n, (numeric) transaction fee in " + CURRENCY_UNIT + "\n" - " \"modifiedfee\" : n, (numeric) transaction fee with fee deltas used for mining priority\n" + " \"fee\" : n, (numeric) transaction fee in " + CURRENCY_UNIT + " (DEPRECATED)\n" + " \"modifiedfee\" : n, (numeric) transaction fee with fee deltas used for mining priority (DEPRECATED)\n" " \"time\" : n, (numeric) local time transaction entered pool in seconds since 1 Jan 1970 GMT\n" " \"height\" : n, (numeric) block height when transaction entered pool\n" " \"descendantcount\" : n, (numeric) number of in-mempool descendant transactions (including this one)\n" " \"descendantsize\" : n, (numeric) virtual transaction size of in-mempool descendants (including this one)\n" - " \"descendantfees\" : n, (numeric) modified fees (see above) of in-mempool descendants (including this one)\n" + " \"descendantfees\" : n, (numeric) modified fees (see above) of in-mempool descendants (including this one) (DEPRECATED)\n" " \"ancestorcount\" : n, (numeric) number of in-mempool ancestor transactions (including this one)\n" " \"ancestorsize\" : n, (numeric) virtual transaction size of in-mempool ancestors (including this one)\n" - " \"ancestorfees\" : n, (numeric) modified fees (see above) of in-mempool ancestors (including this one)\n" + " \"ancestorfees\" : n, (numeric) modified fees (see above) of in-mempool ancestors (including this one) (DEPRECATED)\n" " \"wtxid\" : hash, (string) hash of serialized transaction, including witness data\n" + " \"fees\" : {\n" + " \"base\" : n, (numeric) transaction fee in " + CURRENCY_UNIT + "\n" + " \"modified\" : n, (numeric) transaction fee with fee deltas used for mining priority in " + CURRENCY_UNIT + "\n" + " \"ancestor\" : n, (numeric) modified fees (see above) of in-mempool ancestors (including this one) in " + CURRENCY_UNIT + "\n" + " \"descendant\" : n, (numeric) modified fees (see above) of in-mempool descendants (including this one) in " + CURRENCY_UNIT + "\n" + " }\n" " \"depends\" : [ (array) unconfirmed transactions used as inputs for this transaction\n" " \"transactionid\", (string) parent transaction id\n" " ... ]\n" @@ -383,6 +387,13 @@ void entryToJSON(UniValue &info, const CTxMemPoolEntry &e) { AssertLockHeld(mempool.cs); + UniValue fees(UniValue::VOBJ); + fees.pushKV("base", ValueFromAmount(e.GetFee())); + fees.pushKV("modified", ValueFromAmount(e.GetModifiedFee())); + fees.pushKV("ancestor", ValueFromAmount(e.GetModFeesWithAncestors())); + fees.pushKV("descendant", ValueFromAmount(e.GetModFeesWithDescendants())); + info.pushKV("fees", fees); + info.pushKV("size", (int)e.GetTxSize()); info.pushKV("fee", ValueFromAmount(e.GetFee())); info.pushKV("modifiedfee", ValueFromAmount(e.GetModifiedFee())); @@ -929,7 +940,7 @@ UniValue pruneblockchain(const JSONRPCRequest& request) else if (height > chainHeight) throw JSONRPCError(RPC_INVALID_PARAMETER, "Blockchain is shorter than the attempted prune height."); else if (height > chainHeight - MIN_BLOCKS_TO_KEEP) { - LogPrint(BCLog::RPC, "Attempt to prune blocks close to the tip. Retaining the minimum number of blocks."); + LogPrint(BCLog::RPC, "Attempt to prune blocks close to the tip. Retaining the minimum number of blocks.\n"); height = chainHeight - MIN_BLOCKS_TO_KEEP; } diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 01f932dbb4..34c41b3b6b 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -51,6 +51,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "listreceivedbylabel", 0, "minconf" }, { "listreceivedbylabel", 1, "include_empty" }, { "listreceivedbylabel", 2, "include_watchonly" }, + { "getlabeladdress", 1, "force" }, { "getbalance", 1, "minconf" }, { "getbalance", 2, "include_watchonly" }, { "getblockhash", 0, "height" }, diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 06882c0dfd..b4bb78e689 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -116,7 +116,7 @@ UniValue generateBlocks(std::shared_ptr<CReserveScript> coinbaseScript, int nGen } unsigned int nExtraNonce = 0; UniValue blockHashes(UniValue::VARR); - while (nHeight < nHeightEnd) + while (nHeight < nHeightEnd && !ShutdownRequested()) { std::unique_ptr<CBlockTemplate> pblocktemplate(BlockAssembler(Params()).CreateNewBlock(coinbaseScript->reserveScript)); if (!pblocktemplate.get()) @@ -235,6 +235,7 @@ UniValue prioritisetransaction(const JSONRPCRequest& request) "2. dummy (numeric, optional) API-Compatibility for previous API. Must be zero or null.\n" " DEPRECATED. For forward compatibility use named arguments and omit this parameter.\n" "3. fee_delta (numeric, required) The fee value (in satoshis) to add (or subtract, if negative).\n" + " Note, that this value is not a fee rate. It is a value to modify absolute fee of the TX.\n" " The fee is not actually paid, only the algorithm for selecting transactions into a block\n" " considers the transaction as it would have paid a higher (or lower) fee.\n" "\nResult:\n" @@ -470,10 +471,10 @@ UniValue getblocktemplate(const JSONRPCRequest& request) { checktxtime = std::chrono::steady_clock::now() + std::chrono::minutes(1); - WaitableLock lock(csBestBlock); - while (chainActive.Tip()->GetBlockHash() == hashWatchedChain && IsRPCRunning()) + WaitableLock lock(g_best_block_mutex); + while (g_best_block == hashWatchedChain && IsRPCRunning()) { - if (cvBlockChange.wait_until(lock, checktxtime) == std::cv_status::timeout) + if (g_best_block_cv.wait_until(lock, checktxtime) == std::cv_status::timeout) { // Timeout: Check transactions for update if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLastLP) diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 49e865a64a..0c93108bce 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -69,7 +69,7 @@ UniValue validateaddress(const JSONRPCRequest& request) { #ifdef ENABLE_WALLET - if (!::vpwallets.empty() && IsDeprecatedRPCEnabled("validateaddress")) { + if (HasWallets() && IsDeprecatedRPCEnabled("validateaddress")) { ret.pushKVs(getaddressinfo(request)); } #endif @@ -346,21 +346,22 @@ UniValue getmemoryinfo(const JSONRPCRequest& request) } } -uint32_t getCategoryMask(UniValue cats) { +void EnableOrDisableLogCategories(UniValue cats, bool enable) { cats = cats.get_array(); - uint32_t mask = 0; for (unsigned int i = 0; i < cats.size(); ++i) { - uint32_t flag = 0; std::string cat = cats[i].get_str(); - if (!GetLogCategory(&flag, &cat)) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "unknown logging category " + cat); + + bool success; + if (enable) { + success = g_logger->EnableCategory(cat); + } else { + success = g_logger->DisableCategory(cat); } - if (flag == BCLog::NONE) { - return 0; + + if (!success) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "unknown logging category " + cat); } - mask |= flag; } - return mask; } UniValue logging(const JSONRPCRequest& request) @@ -399,25 +400,25 @@ UniValue logging(const JSONRPCRequest& request) ); } - uint32_t originalLogCategories = logCategories; + uint32_t original_log_categories = g_logger->GetCategoryMask(); if (request.params[0].isArray()) { - logCategories |= getCategoryMask(request.params[0]); + EnableOrDisableLogCategories(request.params[0], true); } - if (request.params[1].isArray()) { - logCategories &= ~getCategoryMask(request.params[1]); + EnableOrDisableLogCategories(request.params[1], false); } + uint32_t updated_log_categories = g_logger->GetCategoryMask(); + uint32_t changed_log_categories = original_log_categories ^ updated_log_categories; // Update libevent logging if BCLog::LIBEVENT has changed. // If the library version doesn't allow it, UpdateHTTPServerLogging() returns false, // in which case we should clear the BCLog::LIBEVENT flag. // Throw an error if the user has explicitly asked to change only the libevent // flag and it failed. - uint32_t changedLogCategories = originalLogCategories ^ logCategories; - if (changedLogCategories & BCLog::LIBEVENT) { - if (!UpdateHTTPServerLogging(logCategories & BCLog::LIBEVENT)) { - logCategories &= ~BCLog::LIBEVENT; - if (changedLogCategories == BCLog::LIBEVENT) { + if (changed_log_categories & BCLog::LIBEVENT) { + if (!UpdateHTTPServerLogging(g_logger->WillLogCategory(BCLog::LIBEVENT))) { + g_logger->DisableCategory(BCLog::LIBEVENT); + if (changed_log_categories == BCLog::LIBEVENT) { throw JSONRPCError(RPC_INVALID_PARAMETER, "libevent logging cannot be updated when using libevent before v2.1.1."); } } diff --git a/src/rpc/protocol.h b/src/rpc/protocol.h index 4a265735d2..6954aed252 100644 --- a/src/rpc/protocol.h +++ b/src/rpc/protocol.h @@ -46,7 +46,6 @@ enum RPCErrorCode //! General application defined errors RPC_MISC_ERROR = -1, //!< std::exception thrown in command handling - RPC_FORBIDDEN_BY_SAFE_MODE = -2, //!< Server is in safe mode, and command is not allowed in safe mode RPC_TYPE_ERROR = -3, //!< Unexpected type was passed as parameter RPC_INVALID_ADDRESS_OR_KEY = -5, //!< Invalid address or key RPC_OUT_OF_MEMORY = -7, //!< Ran out of memory during operation @@ -88,6 +87,9 @@ enum RPCErrorCode //! Backwards compatible aliases RPC_WALLET_INVALID_ACCOUNT_NAME = RPC_WALLET_INVALID_LABEL_NAME, + + //! Unused reserved codes, kept around for backwards compatibility. Do not reuse. + RPC_FORBIDDEN_BY_SAFE_MODE = -2, //!< Server is in safe mode, and command is not allowed in safe mode }; UniValue JSONRPCRequestObj(const std::string& strMethod, const UniValue& params, const UniValue& id); diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 9d7aa58894..102ff3b443 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -7,6 +7,7 @@ #include <coins.h> #include <consensus/validation.h> #include <core_io.h> +#include <index/txindex.h> #include <init.h> #include <keystore.h> #include <validation.h> @@ -18,7 +19,6 @@ #include <policy/rbf.h> #include <primitives/transaction.h> #include <rpc/rawtransaction.h> -#include <rpc/safemode.h> #include <rpc/server.h> #include <script/script.h> #include <script/script_error.h> @@ -47,6 +47,8 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) TxToUniv(tx, uint256(), entry, true, RPCSerializationFlags()); if (!hashBlock.IsNull()) { + LOCK(cs_main); + entry.pushKV("blockhash", hashBlock.GetHex()); CBlockIndex* pindex = LookupBlockIndex(hashBlock); if (pindex) { @@ -94,6 +96,7 @@ UniValue getrawtransaction(const JSONRPCRequest& request) " \"hash\" : \"id\", (string) The transaction hash (differs from txid for witness transactions)\n" " \"size\" : n, (numeric) The serialized transaction size\n" " \"vsize\" : n, (numeric) The virtual transaction size (differs from size for witness transactions)\n" + " \"weight\" : n, (numeric) The transaction's weight (between vsize*4-3 and vsize*4)\n" " \"version\" : n, (numeric) The version\n" " \"locktime\" : ttt, (numeric) The lock time\n" " \"vin\" : [ (array of json objects)\n" @@ -140,8 +143,6 @@ UniValue getrawtransaction(const JSONRPCRequest& request) + HelpExampleCli("getrawtransaction", "\"mytxid\" true \"myblockhash\"") ); - LOCK(cs_main); - bool in_active_chain = true; uint256 hash = ParseHashV(request.params[0], "parameter 1"); CBlockIndex* blockindex = nullptr; @@ -158,6 +159,8 @@ UniValue getrawtransaction(const JSONRPCRequest& request) } if (!request.params[2].isNull()) { + LOCK(cs_main); + uint256 blockhash = ParseHashV(request.params[2], "parameter 3"); blockindex = LookupBlockIndex(blockhash); if (!blockindex) { @@ -166,6 +169,11 @@ UniValue getrawtransaction(const JSONRPCRequest& request) in_active_chain = chainActive.Contains(blockindex); } + bool f_txindex_ready = false; + if (g_txindex && !blockindex) { + f_txindex_ready = g_txindex->BlockUntilSyncedToCurrentChain(); + } + CTransactionRef tx; uint256 hash_block; if (!GetTransaction(hash, tx, Params().GetConsensus(), hash_block, true, blockindex)) { @@ -175,10 +183,12 @@ UniValue getrawtransaction(const JSONRPCRequest& request) throw JSONRPCError(RPC_MISC_ERROR, "Block not available"); } errmsg = "No such transaction found in the provided block"; + } else if (!g_txindex) { + errmsg = "No such mempool transaction. Use -txindex to enable blockchain transaction queries"; + } else if (!f_txindex_ready) { + errmsg = "No such mempool transaction. Blockchain transactions are still in the process of being indexed"; } else { - errmsg = fTxIndex - ? "No such mempool or blockchain transaction" - : "No such mempool transaction. Use -txindex to enable blockchain transaction queries"; + errmsg = "No such mempool or blockchain transaction"; } throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, errmsg + ". Use gettransaction for wallet transactions."); } @@ -228,19 +238,18 @@ UniValue gettxoutproof(const JSONRPCRequest& request) oneTxid = hash; } - LOCK(cs_main); - CBlockIndex* pblockindex = nullptr; - uint256 hashBlock; - if (!request.params[1].isNull()) - { + if (!request.params[1].isNull()) { + LOCK(cs_main); hashBlock = uint256S(request.params[1].get_str()); pblockindex = LookupBlockIndex(hashBlock); if (!pblockindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } } else { + LOCK(cs_main); + // Loop through txids and try to find which block they're in. Exit loop once a block is found. for (const auto& tx : setTxids) { const Coin& coin = AccessByTxid(*pcoinsTip, tx); @@ -251,6 +260,14 @@ UniValue gettxoutproof(const JSONRPCRequest& request) } } + + // Allow txindex to catch up if we need to query it and before we acquire cs_main. + if (g_txindex && !pblockindex) { + g_txindex->BlockUntilSyncedToCurrentChain(); + } + + LOCK(cs_main); + if (pblockindex == nullptr) { CTransactionRef tx; @@ -494,6 +511,7 @@ UniValue decoderawtransaction(const JSONRPCRequest& request) " \"hash\" : \"id\", (string) The transaction hash (differs from txid for witness transactions)\n" " \"size\" : n, (numeric) The transaction size\n" " \"vsize\" : n, (numeric) The virtual transaction size (differs from size for witness transactions)\n" + " \"weight\" : n, (numeric) The transaction's weight (between vsize*4 - 3 and vsize*4)\n" " \"version\" : n, (numeric) The version\n" " \"locktime\" : ttt, (numeric) The lock time\n" " \"vin\" : [ (array of json objects)\n" @@ -595,6 +613,38 @@ UniValue decodescript(const JSONRPCRequest& request) // P2SH cannot be wrapped in a P2SH. If this script is already a P2SH, // don't return the address for a P2SH of the P2SH. r.pushKV("p2sh", EncodeDestination(CScriptID(script))); + // P2SH and witness programs cannot be wrapped in P2WSH, if this script + // is a witness program, don't return addresses for a segwit programs. + if (type.get_str() == "pubkey" || type.get_str() == "pubkeyhash" || type.get_str() == "multisig" || type.get_str() == "nonstandard") { + txnouttype which_type; + std::vector<std::vector<unsigned char>> solutions_data; + Solver(script, which_type, solutions_data); + // Uncompressed pubkeys cannot be used with segwit checksigs. + // If the script contains an uncompressed pubkey, skip encoding of a segwit program. + if ((which_type == TX_PUBKEY) || (which_type == TX_MULTISIG)) { + for (const auto& solution : solutions_data) { + if ((solution.size() != 1) && !CPubKey(solution).IsCompressed()) { + return r; + } + } + } + UniValue sr(UniValue::VOBJ); + CScript segwitScr; + if (which_type == TX_PUBKEY) { + segwitScr = GetScriptForDestination(WitnessV0KeyHash(Hash160(solutions_data[0].begin(), solutions_data[0].end()))); + } else if (which_type == TX_PUBKEYHASH) { + segwitScr = GetScriptForDestination(WitnessV0KeyHash(solutions_data[0])); + } 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)); + } + ScriptPubKeyToUniv(segwitScr, sr, true); + sr.pushKV("p2sh-segwit", EncodeDestination(CScriptID(segwitScr))); + r.pushKV("segwit", sr); + } } return r; @@ -774,9 +824,6 @@ UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival if (is_temp_keystore && (scriptPubKey.IsPayToScriptHash() || scriptPubKey.IsPayToWitnessScriptHash())) { RPCTypeCheckObj(prevOut, { - {"txid", UniValueType(UniValue::VSTR)}, - {"vout", UniValueType(UniValue::VNUM)}, - {"scriptPubKey", UniValueType(UniValue::VSTR)}, {"redeemScript", UniValueType(UniValue::VSTR)}, }); UniValue v = find_value(prevOut, "redeemScript"); @@ -831,7 +878,7 @@ UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival SignatureData sigdata; // Only sign SIGHASH_SINGLE if there's a corresponding output: if (!fHashSingle || (i < mtx.vout.size())) { - ProduceSignature(MutableTransactionSignatureCreator(keystore, &mtx, i, amount, nHashType), prevPubKey, sigdata); + ProduceSignature(*keystore, MutableTransactionSignatureCreator(&mtx, i, amount, nHashType), prevPubKey, sigdata); } sigdata = CombineSignatures(prevPubKey, TransactionSignatureChecker(&txConst, i, amount), sigdata, DataFromTransaction(mtx, i)); @@ -1060,8 +1107,6 @@ UniValue sendrawtransaction(const JSONRPCRequest& request) + HelpExampleRpc("sendrawtransaction", "\"signedhex\"") ); - ObserveSafeMode(); - std::promise<void> promise; RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL}); @@ -1169,8 +1214,6 @@ UniValue testmempoolaccept(const JSONRPCRequest& request) ); } - ObserveSafeMode(); - RPCTypeCheck(request.params, {UniValue::VARR, UniValue::VBOOL}); if (request.params[0].get_array().size() != 1) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Array must contain exactly one raw transaction for now"); @@ -1198,7 +1241,7 @@ UniValue testmempoolaccept(const JSONRPCRequest& request) { LOCK(cs_main); test_accept_res = AcceptToMemoryPool(mempool, state, std::move(tx), &missing_inputs, - nullptr /* plTxnReplaced */, false /* bypass_limits */, max_raw_tx_fee, /* test_accpet */ true); + nullptr /* plTxnReplaced */, false /* bypass_limits */, max_raw_tx_fee, /* test_accept */ true); } result_0.pushKV("allowed", test_accept_res); if (!test_accept_res) { diff --git a/src/rpc/safemode.cpp b/src/rpc/safemode.cpp deleted file mode 100644 index 9f3a9d30b8..0000000000 --- a/src/rpc/safemode.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include <rpc/safemode.h> - -#include <rpc/protocol.h> -#include <util.h> -#include <warnings.h> - -void ObserveSafeMode() -{ - std::string warning = GetWarnings("rpc"); - if (warning != "" && !gArgs.GetBoolArg("-disablesafemode", DEFAULT_DISABLE_SAFEMODE)) { - throw JSONRPCError(RPC_FORBIDDEN_BY_SAFE_MODE, std::string("Safe mode: ") + warning); - } -} - diff --git a/src/rpc/safemode.h b/src/rpc/safemode.h deleted file mode 100644 index 8466d6b2f9..0000000000 --- a/src/rpc/safemode.h +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) 2017 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef BITCOIN_RPC_SAFEMODE_H -#define BITCOIN_RPC_SAFEMODE_H - -static const bool DEFAULT_DISABLE_SAFEMODE = true; - -void ObserveSafeMode(); - -#endif // BITCOIN_RPC_SAFEMODE_H diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 182f4a3327..e0d193fa38 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -250,6 +250,34 @@ bool static CheckMinimalPush(const valtype& data, opcodetype opcode) { return true; } +int FindAndDelete(CScript& script, const CScript& b) +{ + int nFound = 0; + if (b.empty()) + return nFound; + CScript result; + CScript::const_iterator pc = script.begin(), pc2 = script.begin(), end = script.end(); + opcodetype opcode; + do + { + result.insert(result.end(), pc2, pc); + while (static_cast<size_t>(end - pc) >= b.size() && std::equal(b.begin(), b.end(), pc)) + { + pc = pc + b.size(); + ++nFound; + } + pc2 = pc; + } + while (script.GetOp(pc, opcode)); + + if (nFound > 0) { + result.insert(result.end(), pc2, end); + script = std::move(result); + } + + return nFound; +} + bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror) { static const CScriptNum bnZero(0); @@ -891,7 +919,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& // Drop the signature in pre-segwit scripts but not segwit scripts if (sigversion == SigVersion::BASE) { - scriptCode.FindAndDelete(CScript(vchSig)); + FindAndDelete(scriptCode, CScript(vchSig)); } if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, sigversion, serror)) { @@ -955,7 +983,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& { valtype& vchSig = stacktop(-isig-k); if (sigversion == SigVersion::BASE) { - scriptCode.FindAndDelete(CScript(vchSig)); + FindAndDelete(scriptCode, CScript(vchSig)); } } @@ -1361,7 +1389,7 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, CScript scriptPubKey; if (witversion == 0) { - if (program.size() == 32) { + if (program.size() == WITNESS_V0_SCRIPTHASH_SIZE) { // Version 0 segregated witness program: SHA256(CScript) inside the program, CScript + inputs in witness if (witness.stack.size() == 0) { return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_WITNESS_EMPTY); @@ -1373,7 +1401,7 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, if (memcmp(hashScriptPubKey.begin(), program.data(), 32)) { return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH); } - } else if (program.size() == 20) { + } else if (program.size() == WITNESS_V0_KEYHASH_SIZE) { // Special case for pay-to-pubkeyhash; signature + pubkey in witness if (witness.stack.size() != 2) { return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH); // 2 items in witness @@ -1530,10 +1558,10 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C size_t static WitnessSigOps(int witversion, const std::vector<unsigned char>& witprogram, const CScriptWitness& witness, int flags) { if (witversion == 0) { - if (witprogram.size() == 20) + if (witprogram.size() == WITNESS_V0_KEYHASH_SIZE) return 1; - if (witprogram.size() == 32 && witness.stack.size() > 0) { + if (witprogram.size() == WITNESS_V0_SCRIPTHASH_SIZE && witness.stack.size() > 0) { CScript subscript(witness.stack.back().begin(), witness.stack.back().end()); return subscript.GetSigOpCount(true); } diff --git a/src/script/interpreter.h b/src/script/interpreter.h index bb7750d783..50c747900a 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -129,6 +129,10 @@ enum class SigVersion WITNESS_V0 = 1, }; +/** Signature hash sizes */ +static constexpr size_t WITNESS_V0_SCRIPTHASH_SIZE = 32; +static constexpr size_t WITNESS_V0_KEYHASH_SIZE = 20; + uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache = nullptr); class BaseSignatureChecker @@ -185,4 +189,6 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags); +int FindAndDelete(CScript& script, const CScript& b); + #endif // BITCOIN_SCRIPT_INTERPRETER_H diff --git a/src/script/ismine.cpp b/src/script/ismine.cpp index 05bc5e9bd6..fefa02fdef 100644 --- a/src/script/ismine.cpp +++ b/src/script/ismine.cpp @@ -13,34 +13,36 @@ typedef std::vector<unsigned char> valtype; -static bool HaveKeys(const std::vector<valtype>& pubkeys, const CKeyStore& keystore) -{ - for (const valtype& pubkey : pubkeys) { - CKeyID keyID = CPubKey(pubkey).GetID(); - if (!keystore.HaveKey(keyID)) return false; - } - return true; -} +namespace { -isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey, SigVersion sigversion) +/** + * This is an enum that tracks the execution context of a script, similar to + * SigVersion in script/interpreter. It is separate however because we want to + * distinguish between top-level scriptPubKey execution and P2SH redeemScript + * execution (a distinction that has no impact on consensus rules). + */ +enum class IsMineSigVersion { - bool isInvalid = false; - return IsMine(keystore, scriptPubKey, isInvalid, sigversion); -} + TOP = 0, //! scriptPubKey execution + P2SH = 1, //! P2SH redeemScript + WITNESS_V0 = 2 //! P2WSH witness script execution +}; -isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest, SigVersion sigversion) +bool PermitsUncompressed(IsMineSigVersion sigversion) { - bool isInvalid = false; - return IsMine(keystore, dest, isInvalid, sigversion); + return sigversion == IsMineSigVersion::TOP || sigversion == IsMineSigVersion::P2SH; } -isminetype IsMine(const CKeyStore &keystore, const CTxDestination& dest, bool& isInvalid, SigVersion sigversion) +bool HaveKeys(const std::vector<valtype>& pubkeys, const CKeyStore& keystore) { - CScript script = GetScriptForDestination(dest); - return IsMine(keystore, script, isInvalid, sigversion); + for (const valtype& pubkey : pubkeys) { + CKeyID keyID = CPubKey(pubkey).GetID(); + if (!keystore.HaveKey(keyID)) return false; + } + return true; } -isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool& isInvalid, SigVersion sigversion) +isminetype IsMineInner(const CKeyStore& keystore, const CScript& scriptPubKey, bool& isInvalid, IsMineSigVersion sigversion) { isInvalid = false; @@ -61,7 +63,7 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool& break; case TX_PUBKEY: keyID = CPubKey(vSolutions[0]).GetID(); - if (sigversion != SigVersion::BASE && vSolutions[0].size() != 33) { + if (!PermitsUncompressed(sigversion) && vSolutions[0].size() != 33) { isInvalid = true; return ISMINE_NO; } @@ -70,20 +72,20 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool& break; case TX_WITNESS_V0_KEYHASH: { - if (!keystore.HaveCScript(CScriptID(CScript() << OP_0 << vSolutions[0]))) { + if (sigversion == IsMineSigVersion::TOP && !keystore.HaveCScript(CScriptID(CScript() << OP_0 << vSolutions[0]))) { // We do not support bare witness outputs unless the P2SH version of it would be // acceptable as well. This protects against matching before segwit activates. // This also applies to the P2WSH case. break; } - isminetype ret = ::IsMine(keystore, GetScriptForDestination(CKeyID(uint160(vSolutions[0]))), isInvalid, SigVersion::WITNESS_V0); + isminetype ret = IsMineInner(keystore, GetScriptForDestination(CKeyID(uint160(vSolutions[0]))), isInvalid, IsMineSigVersion::WITNESS_V0); if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid)) return ret; break; } case TX_PUBKEYHASH: keyID = CKeyID(uint160(vSolutions[0])); - if (sigversion != SigVersion::BASE) { + if (!PermitsUncompressed(sigversion)) { CPubKey pubkey; if (keystore.GetPubKey(keyID, pubkey) && !pubkey.IsCompressed()) { isInvalid = true; @@ -98,7 +100,7 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool& CScriptID scriptID = CScriptID(uint160(vSolutions[0])); CScript subscript; if (keystore.GetCScript(scriptID, subscript)) { - isminetype ret = IsMine(keystore, subscript, isInvalid); + isminetype ret = IsMineInner(keystore, subscript, isInvalid, IsMineSigVersion::P2SH); if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid)) return ret; } @@ -106,7 +108,7 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool& } case TX_WITNESS_V0_SCRIPTHASH: { - if (!keystore.HaveCScript(CScriptID(CScript() << OP_0 << vSolutions[0]))) { + if (sigversion == IsMineSigVersion::TOP && !keystore.HaveCScript(CScriptID(CScript() << OP_0 << vSolutions[0]))) { break; } uint160 hash; @@ -114,7 +116,7 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool& CScriptID scriptID = CScriptID(hash); CScript subscript; if (keystore.GetCScript(scriptID, subscript)) { - isminetype ret = IsMine(keystore, subscript, isInvalid, SigVersion::WITNESS_V0); + isminetype ret = IsMineInner(keystore, subscript, isInvalid, IsMineSigVersion::WITNESS_V0); if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid)) return ret; } @@ -123,13 +125,16 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool& case TX_MULTISIG: { + // Never treat bare multisig outputs as ours (they can still be made watchonly-though) + if (sigversion == IsMineSigVersion::TOP) break; + // Only consider transactions "mine" if we own ALL the // keys involved. Multi-signature transactions that are // partially owned (somebody else has a key that can spend // them) enable spend-out-from-under-you attacks, especially // in shared-wallet situations. std::vector<valtype> keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1); - if (sigversion != SigVersion::BASE) { + if (!PermitsUncompressed(sigversion)) { for (size_t i = 0; i < keys.size(); i++) { if (keys[i].size() != 33) { isInvalid = true; @@ -146,7 +151,26 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool& if (keystore.HaveWatchOnly(scriptPubKey)) { // TODO: This could be optimized some by doing some work after the above solver SignatureData sigs; - return ProduceSignature(DummySignatureCreator(&keystore), scriptPubKey, sigs) ? ISMINE_WATCH_SOLVABLE : ISMINE_WATCH_UNSOLVABLE; + return ProduceSignature(keystore, DUMMY_SIGNATURE_CREATOR, scriptPubKey, sigs) ? ISMINE_WATCH_SOLVABLE : ISMINE_WATCH_UNSOLVABLE; } return ISMINE_NO; } + +} // namespace + +isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey, bool& isInvalid) +{ + return IsMineInner(keystore, scriptPubKey, isInvalid, IsMineSigVersion::TOP); +} + +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); + return IsMine(keystore, script); +} diff --git a/src/script/ismine.h b/src/script/ismine.h index f93a66e35a..8573bdfbd2 100644 --- a/src/script/ismine.h +++ b/src/script/ismine.h @@ -33,9 +33,8 @@ typedef uint8_t isminefilter; * 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, SigVersion = SigVersion::BASE); -isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey, SigVersion = SigVersion::BASE); -isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest, bool& isInvalid, SigVersion = SigVersion::BASE); -isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest, SigVersion = SigVersion::BASE); +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); #endif // BITCOIN_SCRIPT_ISMINE_H diff --git a/src/script/script.cpp b/src/script/script.cpp index 65e5405ebd..7f25d915a8 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -280,3 +280,55 @@ bool CScript::HasValidOps() const } return true; } + +bool GetScriptOp(CScriptBase::const_iterator& pc, CScriptBase::const_iterator end, opcodetype& opcodeRet, std::vector<unsigned char>* pvchRet) +{ + opcodeRet = OP_INVALIDOPCODE; + if (pvchRet) + pvchRet->clear(); + if (pc >= end) + return false; + + // Read instruction + if (end - pc < 1) + return false; + unsigned int opcode = *pc++; + + // Immediate operand + if (opcode <= OP_PUSHDATA4) + { + unsigned int nSize = 0; + if (opcode < OP_PUSHDATA1) + { + nSize = opcode; + } + else if (opcode == OP_PUSHDATA1) + { + if (end - pc < 1) + return false; + nSize = *pc++; + } + else if (opcode == OP_PUSHDATA2) + { + if (end - pc < 2) + return false; + nSize = ReadLE16(&pc[0]); + pc += 2; + } + else if (opcode == OP_PUSHDATA4) + { + if (end - pc < 4) + return false; + nSize = ReadLE32(&pc[0]); + pc += 4; + } + if (end - pc < 0 || (unsigned int)(end - pc) < nSize) + return false; + if (pvchRet) + pvchRet->assign(pc, pc + nSize); + pc += nSize; + } + + opcodeRet = static_cast<opcodetype>(opcode); + return true; +} diff --git a/src/script/script.h b/src/script/script.h index 591777672e..d8b7c06013 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -385,6 +385,8 @@ private: */ typedef prevector<28, unsigned char> CScriptBase; +bool GetScriptOp(CScriptBase::const_iterator& pc, CScriptBase::const_iterator end, opcodetype& opcodeRet, std::vector<unsigned char>* pvchRet); + /** Serialized script, used inside transaction inputs and outputs */ class CScript : public CScriptBase { @@ -415,7 +417,7 @@ public: template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(static_cast<CScriptBase&>(*this)); + READWRITEAS(CScriptBase, *this); } CScript& operator+=(const CScript& b) @@ -493,84 +495,16 @@ public: } - bool GetOp(iterator& pc, opcodetype& opcodeRet, std::vector<unsigned char>& vchRet) - { - // Wrapper so it can be called with either iterator or const_iterator - const_iterator pc2 = pc; - bool fRet = GetOp2(pc2, opcodeRet, &vchRet); - pc = begin() + (pc2 - begin()); - return fRet; - } - - bool GetOp(iterator& pc, opcodetype& opcodeRet) - { - const_iterator pc2 = pc; - bool fRet = GetOp2(pc2, opcodeRet, nullptr); - pc = begin() + (pc2 - begin()); - return fRet; - } - bool GetOp(const_iterator& pc, opcodetype& opcodeRet, std::vector<unsigned char>& vchRet) const { - return GetOp2(pc, opcodeRet, &vchRet); + return GetScriptOp(pc, end(), opcodeRet, &vchRet); } bool GetOp(const_iterator& pc, opcodetype& opcodeRet) const { - return GetOp2(pc, opcodeRet, nullptr); + return GetScriptOp(pc, end(), opcodeRet, nullptr); } - bool GetOp2(const_iterator& pc, opcodetype& opcodeRet, std::vector<unsigned char>* pvchRet) const - { - opcodeRet = OP_INVALIDOPCODE; - if (pvchRet) - pvchRet->clear(); - if (pc >= end()) - return false; - - // Read instruction - if (end() - pc < 1) - return false; - unsigned int opcode = *pc++; - - // Immediate operand - if (opcode <= OP_PUSHDATA4) - { - unsigned int nSize = 0; - if (opcode < OP_PUSHDATA1) - { - nSize = opcode; - } - else if (opcode == OP_PUSHDATA1) - { - if (end() - pc < 1) - return false; - nSize = *pc++; - } - else if (opcode == OP_PUSHDATA2) - { - if (end() - pc < 2) - return false; - nSize = ReadLE16(&pc[0]); - pc += 2; - } - else if (opcode == OP_PUSHDATA4) - { - if (end() - pc < 4) - return false; - nSize = ReadLE32(&pc[0]); - pc += 4; - } - if (end() - pc < 0 || (unsigned int)(end() - pc) < nSize) - return false; - if (pvchRet) - pvchRet->assign(pc, pc + nSize); - pc += nSize; - } - - opcodeRet = static_cast<opcodetype>(opcode); - return true; - } /** Encode/decode small integers: */ static int DecodeOP_N(opcodetype opcode) @@ -588,43 +522,6 @@ public: return (opcodetype)(OP_1+n-1); } - int FindAndDelete(const CScript& b) - { - int nFound = 0; - if (b.empty()) - return nFound; - CScript result; - iterator pc = begin(), pc2 = begin(); - opcodetype opcode; - do - { - result.insert(result.end(), pc2, pc); - while (static_cast<size_t>(end() - pc) >= b.size() && std::equal(b.begin(), b.end(), pc)) - { - pc = pc + b.size(); - ++nFound; - } - pc2 = pc; - } - while (GetOp(pc, opcode)); - - if (nFound > 0) { - result.insert(result.end(), pc2, end()); - *this = result; - } - - return nFound; - } - int Find(opcodetype op) const - { - int nFound = 0; - opcodetype opcode; - for (const_iterator pc = begin(); pc != end() && GetOp(pc, opcode);) - if (opcode == op) - ++nFound; - return nFound; - } - /** * Pre-version-0.6, Bitcoin always counted CHECKMULTISIGs * as 20 sigops. With pay-to-script-hash, that changed: diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 910bb39ce6..ac35f17f3e 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -14,12 +14,12 @@ typedef std::vector<unsigned char> valtype; -TransactionSignatureCreator::TransactionSignatureCreator(const SigningProvider* provider, const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : BaseSignatureCreator(provider), txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn) {} +TransactionSignatureCreator::TransactionSignatureCreator(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn) {} -bool TransactionSignatureCreator::CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& address, const CScript& scriptCode, SigVersion sigversion) const +bool TransactionSignatureCreator::CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& address, const CScript& scriptCode, SigVersion sigversion) const { CKey key; - if (!m_provider->GetKey(address, key)) + if (!provider.GetKey(address, key)) return false; // Signing with uncompressed keys is disabled in witness scripts @@ -33,16 +33,16 @@ bool TransactionSignatureCreator::CreateSig(std::vector<unsigned char>& vchSig, return true; } -static bool Sign1(const CKeyID& address, const BaseSignatureCreator& creator, const CScript& scriptCode, std::vector<valtype>& ret, SigVersion sigversion) +static bool Sign1(const SigningProvider& provider, const CKeyID& address, const BaseSignatureCreator& creator, const CScript& scriptCode, std::vector<valtype>& ret, SigVersion sigversion) { std::vector<unsigned char> vchSig; - if (!creator.CreateSig(vchSig, address, scriptCode, sigversion)) + if (!creator.CreateSig(provider, vchSig, address, scriptCode, sigversion)) return false; ret.push_back(vchSig); return true; } -static bool SignN(const std::vector<valtype>& multisigdata, const BaseSignatureCreator& creator, const CScript& scriptCode, std::vector<valtype>& ret, SigVersion sigversion) +static bool SignN(const SigningProvider& provider, const std::vector<valtype>& multisigdata, const BaseSignatureCreator& creator, const CScript& scriptCode, std::vector<valtype>& ret, SigVersion sigversion) { int nSigned = 0; int nRequired = multisigdata.front()[0]; @@ -50,7 +50,7 @@ static bool SignN(const std::vector<valtype>& multisigdata, const BaseSignatureC { const valtype& pubkey = multisigdata[i]; CKeyID keyID = CPubKey(pubkey).GetID(); - if (Sign1(keyID, creator, scriptCode, ret, sigversion)) + if (Sign1(provider, keyID, creator, scriptCode, ret, sigversion)) ++nSigned; } return nSigned==nRequired; @@ -62,7 +62,7 @@ static bool SignN(const std::vector<valtype>& multisigdata, const BaseSignatureC * unless whichTypeRet is TX_SCRIPTHASH, in which case scriptSigRet is the redemption script. * Returns false if scriptPubKey could not be completely satisfied. */ -static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptPubKey, +static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator& creator, const CScript& scriptPubKey, std::vector<valtype>& ret, txnouttype& whichTypeRet, SigVersion sigversion) { CScript scriptRet; @@ -82,20 +82,20 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP return false; case TX_PUBKEY: keyID = CPubKey(vSolutions[0]).GetID(); - return Sign1(keyID, creator, scriptPubKey, ret, sigversion); + return Sign1(provider, keyID, creator, scriptPubKey, ret, sigversion); case TX_PUBKEYHASH: keyID = CKeyID(uint160(vSolutions[0])); - if (!Sign1(keyID, creator, scriptPubKey, ret, sigversion)) + if (!Sign1(provider, keyID, creator, scriptPubKey, ret, sigversion)) return false; else { CPubKey vch; - creator.Provider().GetPubKey(keyID, vch); + provider.GetPubKey(keyID, vch); ret.push_back(ToByteVector(vch)); } return true; case TX_SCRIPTHASH: - if (creator.Provider().GetCScript(uint160(vSolutions[0]), scriptRet)) { + if (provider.GetCScript(uint160(vSolutions[0]), scriptRet)) { ret.push_back(std::vector<unsigned char>(scriptRet.begin(), scriptRet.end())); return true; } @@ -103,7 +103,7 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP case TX_MULTISIG: ret.push_back(valtype()); // workaround CHECKMULTISIG bug - return (SignN(vSolutions, creator, scriptPubKey, ret, sigversion)); + return (SignN(provider, vSolutions, creator, scriptPubKey, ret, sigversion)); case TX_WITNESS_V0_KEYHASH: ret.push_back(vSolutions[0]); @@ -111,7 +111,7 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP case TX_WITNESS_V0_SCRIPTHASH: CRIPEMD160().Write(&vSolutions[0][0], vSolutions[0].size()).Finalize(h160.begin()); - if (creator.Provider().GetCScript(h160, scriptRet)) { + if (provider.GetCScript(h160, scriptRet)) { ret.push_back(std::vector<unsigned char>(scriptRet.begin(), scriptRet.end())); return true; } @@ -137,11 +137,11 @@ static CScript PushAll(const std::vector<valtype>& values) return result; } -bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPubKey, SignatureData& sigdata) +bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreator& creator, const CScript& fromPubKey, SignatureData& sigdata) { std::vector<valtype> result; txnouttype whichType; - bool solved = SignStep(creator, fromPubKey, result, whichType, SigVersion::BASE); + bool solved = SignStep(provider, creator, fromPubKey, result, whichType, SigVersion::BASE); bool P2SH = false; CScript subscript; sigdata.scriptWitness.stack.clear(); @@ -152,7 +152,7 @@ bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPu // the final scriptSig is the signatures from that // and then the serialized subscript: subscript = CScript(result[0].begin(), result[0].end()); - solved = solved && SignStep(creator, subscript, result, whichType, SigVersion::BASE) && whichType != TX_SCRIPTHASH; + solved = solved && SignStep(provider, creator, subscript, result, whichType, SigVersion::BASE) && whichType != TX_SCRIPTHASH; P2SH = true; } @@ -161,7 +161,7 @@ bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPu CScript witnessscript; witnessscript << OP_DUP << OP_HASH160 << ToByteVector(result[0]) << OP_EQUALVERIFY << OP_CHECKSIG; txnouttype subType; - solved = solved && SignStep(creator, witnessscript, result, subType, SigVersion::WITNESS_V0); + solved = solved && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0); sigdata.scriptWitness.stack = result; result.clear(); } @@ -169,7 +169,7 @@ bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPu { CScript witnessscript(result[0].begin(), result[0].end()); txnouttype subType; - solved = solved && SignStep(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) && 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; result.clear(); @@ -210,10 +210,10 @@ bool SignSignature(const SigningProvider &provider, const CScript& fromPubKey, C assert(nIn < txTo.vin.size()); CTransaction txToConst(txTo); - TransactionSignatureCreator creator(&provider, &txToConst, nIn, amount, nHashType); + TransactionSignatureCreator creator(&txToConst, nIn, amount, nHashType); SignatureData sigdata; - bool ret = ProduceSignature(creator, fromPubKey, sigdata); + bool ret = ProduceSignature(provider, creator, fromPubKey, sigdata); UpdateTransaction(txTo, nIn, sigdata); return ret; } @@ -392,39 +392,37 @@ SignatureData CombineSignatures(const CScript& scriptPubKey, const BaseSignature namespace { /** Dummy signature checker which accepts all signatures. */ -class DummySignatureChecker : public BaseSignatureChecker +class DummySignatureChecker final : public BaseSignatureChecker { public: DummySignatureChecker() {} + bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override { return true; } +}; +const DummySignatureChecker DUMMY_CHECKER; - bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override +class DummySignatureCreator final : public BaseSignatureCreator { +public: + DummySignatureCreator() {} + const BaseSignatureChecker& Checker() const override { return DUMMY_CHECKER; } + bool CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const override { + // Create a dummy signature that is a valid DER-encoding + vchSig.assign(72, '\000'); + vchSig[0] = 0x30; + vchSig[1] = 69; + vchSig[2] = 0x02; + vchSig[3] = 33; + vchSig[4] = 0x01; + vchSig[4 + 33] = 0x02; + vchSig[5 + 33] = 32; + vchSig[6 + 33] = 0x01; + vchSig[6 + 33 + 32] = SIGHASH_ALL; return true; } }; -const DummySignatureChecker dummyChecker; -} // namespace - -const BaseSignatureChecker& DummySignatureCreator::Checker() const -{ - return dummyChecker; } -bool DummySignatureCreator::CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const -{ - // Create a dummy signature that is a valid DER-encoding - vchSig.assign(72, '\000'); - vchSig[0] = 0x30; - vchSig[1] = 69; - vchSig[2] = 0x02; - vchSig[3] = 33; - vchSig[4] = 0x01; - vchSig[4 + 33] = 0x02; - vchSig[5 + 33] = 32; - vchSig[6 + 33] = 0x01; - vchSig[6 + 33 + 32] = SIGHASH_ALL; - return true; -} +const BaseSignatureCreator& DUMMY_SIGNATURE_CREATOR = DummySignatureCreator(); bool IsSolvable(const SigningProvider& provider, const CScript& script) { @@ -432,14 +430,13 @@ bool IsSolvable(const SigningProvider& provider, const CScript& script) // if we were to have the private keys. This is just to make sure that the script is valid and that, // if found in a transaction, we would still accept and relay that transaction. In particular, // it will reject witness outputs that require signing with an uncompressed public key. - DummySignatureCreator creator(&provider); SignatureData sigs; // Make sure that STANDARD_SCRIPT_VERIFY_FLAGS includes SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, the most // important property this function is designed to test for. static_assert(STANDARD_SCRIPT_VERIFY_FLAGS & SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, "IsSolvable requires standard script flags to include WITNESS_PUBKEYTYPE"); - if (ProduceSignature(creator, script, sigs)) { + if (ProduceSignature(provider, DUMMY_SIGNATURE_CREATOR, script, sigs)) { // VerifyScript check is just defensive, and should never fail. - assert(VerifyScript(sigs.scriptSig, script, &sigs.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker())); + assert(VerifyScript(sigs.scriptSig, script, &sigs.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, DUMMY_CHECKER)); return true; } return false; diff --git a/src/script/sign.h b/src/script/sign.h index c301f0544f..cf3651c1de 100644 --- a/src/script/sign.h +++ b/src/script/sign.h @@ -26,19 +26,14 @@ public: virtual bool GetKey(const CKeyID &address, CKey& key) const =0; }; -/** Virtual base class for signature creators. */ +/** Interface for signature creators. */ class BaseSignatureCreator { -protected: - const SigningProvider* m_provider; - public: - explicit BaseSignatureCreator(const SigningProvider* provider) : m_provider(provider) {} - const SigningProvider& Provider() const { return *m_provider; } virtual ~BaseSignatureCreator() {} virtual const BaseSignatureChecker& Checker() const =0; /** Create a singular (non-script) signature. */ - virtual bool CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const =0; + virtual bool CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const =0; }; /** A signature creator for transactions. */ @@ -50,25 +45,20 @@ class TransactionSignatureCreator : public BaseSignatureCreator { const TransactionSignatureChecker checker; public: - TransactionSignatureCreator(const SigningProvider* provider, const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn=SIGHASH_ALL); + TransactionSignatureCreator(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn=SIGHASH_ALL); const BaseSignatureChecker& Checker() const override { return checker; } - bool CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const override; + bool CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const override; }; class MutableTransactionSignatureCreator : public TransactionSignatureCreator { CTransaction tx; public: - MutableTransactionSignatureCreator(const SigningProvider* provider, const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : TransactionSignatureCreator(provider, &tx, nInIn, amountIn, nHashTypeIn), tx(*txToIn) {} + MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : TransactionSignatureCreator(&tx, nInIn, amountIn, nHashTypeIn), tx(*txToIn) {} }; /** A signature creator that just produces 72-byte empty signatures. */ -class DummySignatureCreator : public BaseSignatureCreator { -public: - explicit DummySignatureCreator(const SigningProvider* provider) : BaseSignatureCreator(provider) {} - const BaseSignatureChecker& Checker() const override; - bool CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const override; -}; +extern const BaseSignatureCreator& DUMMY_SIGNATURE_CREATOR; struct SignatureData { CScript scriptSig; @@ -79,7 +69,7 @@ struct SignatureData { }; /** Produce a script signature using a generic signature creator. */ -bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& scriptPubKey, SignatureData& sigdata); +bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreator& creator, const CScript& scriptPubKey, SignatureData& sigdata); /** Produce a script signature for a transaction. */ bool SignSignature(const SigningProvider &provider, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType); diff --git a/src/script/standard.cpp b/src/script/standard.cpp index 0b9053d7fc..76778112aa 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -66,12 +66,12 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::v int witnessversion; std::vector<unsigned char> witnessprogram; if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) { - if (witnessversion == 0 && witnessprogram.size() == 20) { + if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_KEYHASH_SIZE) { typeRet = TX_WITNESS_V0_KEYHASH; vSolutionsRet.push_back(witnessprogram); return true; } - if (witnessversion == 0 && witnessprogram.size() == 32) { + if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_SCRIPTHASH_SIZE) { typeRet = TX_WITNESS_V0_SCRIPTHASH; vSolutionsRet.push_back(witnessprogram); return true; diff --git a/src/script/standard.h b/src/script/standard.h index 3b2838a5bb..4922b7236b 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -23,7 +23,7 @@ class CScriptID : public uint160 { public: CScriptID() : uint160() {} - CScriptID(const CScript& in); + explicit CScriptID(const CScript& in); CScriptID(const uint160& in) : uint160(in) {} }; diff --git a/src/serialize.h b/src/serialize.h index 247e915298..e54c7483d2 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -22,6 +22,7 @@ #include <vector> #include <prevector.h> +#include <span.h> static const unsigned int MAX_SIZE = 0x02000000; @@ -41,7 +42,7 @@ constexpr deserialize_type deserialize {}; /** * Used to bypass the rule against non-const reference to temporary - * where it makes sense with wrappers such as CFlatData or CTxDB + * where it makes sense with wrappers. */ template<typename T> inline T& REF(const T& val) @@ -78,6 +79,11 @@ template<typename Stream> inline void ser_writedata16(Stream &s, uint16_t obj) obj = htole16(obj); s.write((char*)&obj, 2); } +template<typename Stream> inline void ser_writedata16be(Stream &s, uint16_t obj) +{ + obj = htobe16(obj); + s.write((char*)&obj, 2); +} template<typename Stream> inline void ser_writedata32(Stream &s, uint32_t obj) { obj = htole32(obj); @@ -100,6 +106,12 @@ template<typename Stream> inline uint16_t ser_readdata16(Stream &s) s.read((char*)&obj, 2); return le16toh(obj); } +template<typename Stream> inline uint16_t ser_readdata16be(Stream &s) +{ + uint16_t obj; + s.read((char*)&obj, 2); + return be16toh(obj); +} template<typename Stream> inline uint32_t ser_readdata32(Stream &s) { uint32_t obj; @@ -154,7 +166,12 @@ enum SER_GETHASH = (1 << 2), }; -#define READWRITE(...) (::SerReadWriteMany(s, ser_action, __VA_ARGS__)) +//! Convert the reference base type to X, without changing constness or reference type. +template<typename X> X& ReadWriteAsHelper(X& x) { return x; } +template<typename X> const X& ReadWriteAsHelper(const X& x) { return x; } + +#define READWRITE(...) (::SerReadWriteMany(s, ser_action, __VA_ARGS__)) +#define READWRITEAS(type, obj) (::SerReadWriteMany(s, ser_action, ReadWriteAsHelper<type>(obj))) /** * Implement three methods for serializable objects. These are actually wrappers over @@ -185,6 +202,8 @@ template<typename Stream> inline void Serialize(Stream& s, float a ) { ser_wri template<typename Stream> inline void Serialize(Stream& s, double a ) { ser_writedata64(s, ser_double_to_uint64(a)); } template<typename Stream, int N> inline void Serialize(Stream& s, const char (&a)[N]) { s.write(a, N); } template<typename Stream, int N> inline void Serialize(Stream& s, const unsigned char (&a)[N]) { s.write(CharCast(a), N); } +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()); } template<typename Stream> inline void Unserialize(Stream& s, char& a ) { a = ser_readdata8(s); } // TODO Get rid of bare char template<typename Stream> inline void Unserialize(Stream& s, int8_t& a ) { a = ser_readdata8(s); } @@ -199,6 +218,7 @@ template<typename Stream> inline void Unserialize(Stream& s, float& a ) { a = template<typename Stream> inline void Unserialize(Stream& s, double& a ) { a = ser_uint64_to_double(ser_readdata64(s)); } template<typename Stream, int N> inline void Unserialize(Stream& s, char (&a)[N]) { s.read(a, N); } template<typename Stream, int N> inline void Unserialize(Stream& s, unsigned char (&a)[N]) { s.read(CharCast(a), N); } +template<typename Stream> inline void Unserialize(Stream& s, Span<unsigned char>& span) { s.read(CharCast(span.data()), span.size()); } template<typename Stream> inline void Serialize(Stream& s, bool a) { char f=a; ser_writedata8(s, f); } template<typename Stream> inline void Unserialize(Stream& s, bool& a) { char f=ser_readdata8(s); a=f; } @@ -384,67 +404,60 @@ I ReadVarInt(Stream& is) } } -#define FLATDATA(obj) CFlatData((char*)&(obj), (char*)&(obj) + sizeof(obj)) #define VARINT(obj, ...) WrapVarInt<__VA_ARGS__>(REF(obj)) #define COMPACTSIZE(obj) CCompactSize(REF(obj)) #define LIMITED_STRING(obj,n) LimitedString< n >(REF(obj)) -/** - * Wrapper for serializing arrays and POD. - */ -class CFlatData +template<VarIntMode Mode, typename I> +class CVarInt { protected: - char* pbegin; - char* pend; + I &n; public: - CFlatData(void* pbeginIn, void* pendIn) : pbegin((char*)pbeginIn), pend((char*)pendIn) { } - template <class T, class TAl> - explicit CFlatData(std::vector<T,TAl> &v) - { - pbegin = (char*)v.data(); - pend = (char*)(v.data() + v.size()); - } - template <unsigned int N, typename T, typename S, typename D> - explicit CFlatData(prevector<N, T, S, D> &v) - { - pbegin = (char*)v.data(); - pend = (char*)(v.data() + v.size()); - } - char* begin() { return pbegin; } - const char* begin() const { return pbegin; } - char* end() { return pend; } - const char* end() const { return pend; } + explicit CVarInt(I& nIn) : n(nIn) { } template<typename Stream> - void Serialize(Stream& s) const - { - s.write(pbegin, pend - pbegin); + void Serialize(Stream &s) const { + WriteVarInt<Stream,Mode,I>(s, n); } template<typename Stream> - void Unserialize(Stream& s) - { - s.read(pbegin, pend - pbegin); + void Unserialize(Stream& s) { + n = ReadVarInt<Stream,Mode,I>(s); } }; -template<VarIntMode Mode, typename I> -class CVarInt +/** Serialization wrapper class for big-endian integers. + * + * Use this wrapper around integer types that are stored in memory in native + * byte order, but serialized in big endian notation. This is only intended + * to implement serializers that are compatible with existing formats, and + * its use is not recommended for new data structures. + * + * Only 16-bit types are supported for now. + */ +template<typename I> +class BigEndian { protected: - I &n; + I& m_val; public: - explicit CVarInt(I& nIn) : n(nIn) { } + explicit BigEndian(I& val) : m_val(val) + { + static_assert(std::is_unsigned<I>::value, "BigEndian type must be unsigned integer"); + static_assert(sizeof(I) == 2 && std::numeric_limits<I>::min() == 0 && std::numeric_limits<I>::max() == std::numeric_limits<uint16_t>::max(), "Unsupported BigEndian size"); + } template<typename Stream> - void Serialize(Stream &s) const { - WriteVarInt<Stream,Mode,I>(s, n); + void Serialize(Stream& s) const + { + ser_writedata16be(s, m_val); } template<typename Stream> - void Unserialize(Stream& s) { - n = ReadVarInt<Stream,Mode,I>(s); + void Unserialize(Stream& s) + { + m_val = ser_readdata16be(s); } }; @@ -498,6 +511,9 @@ public: template<VarIntMode Mode=VarIntMode::DEFAULT, typename I> CVarInt<Mode, I> WrapVarInt(I& n) { return CVarInt<Mode, I>{n}; } +template<typename I> +BigEndian<I> WrapBigEndian(I& n) { return BigEndian<I>(n); } + /** * Forward declarations */ diff --git a/src/span.h b/src/span.h new file mode 100644 index 0000000000..707fc21918 --- /dev/null +++ b/src/span.h @@ -0,0 +1,40 @@ +// 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_SPAN_H +#define BITCOIN_SPAN_H + +#include <type_traits> +#include <cstddef> + +/** A Span is an object that can refer to a contiguous sequence of objects. + * + * It implements a subset of C++20's std::span. + */ +template<typename C> +class Span +{ + C* m_data; + std::ptrdiff_t m_size; + +public: + constexpr Span() noexcept : m_data(nullptr), m_size(0) {} + constexpr Span(C* data, std::ptrdiff_t size) noexcept : m_data(data), m_size(size) {} + + constexpr C* data() const noexcept { return m_data; } + constexpr std::ptrdiff_t size() const noexcept { return m_size; } +}; + +/** Create a span to a container exposing data() and size(). + * + * This correctly deals with constness: the returned Span's element type will be + * whatever data() returns a pointer to. If either the passed container is const, + * or its element type is const, the resulting span will have a const element type. + * + * std::span will have a constructor that implements this functionality directly. + */ +template<typename V> +constexpr Span<typename std::remove_pointer<decltype(std::declval<V>().data())>::type> MakeSpan(V& v) { return Span<typename std::remove_pointer<decltype(std::declval<V>().data())>::type>(v.data(), v.size()); } + +#endif diff --git a/src/support/lockedpool.cpp b/src/support/lockedpool.cpp index 51c337ed2f..f10fd07c63 100644 --- a/src/support/lockedpool.cpp +++ b/src/support/lockedpool.cpp @@ -27,7 +27,6 @@ #endif #include <algorithm> -#include <memory> LockedPoolManager* LockedPoolManager::_instance = nullptr; std::once_flag LockedPoolManager::init_flag; diff --git a/src/sync.cpp b/src/sync.cpp index fdf7c44e4c..6f21d498ee 100644 --- a/src/sync.cpp +++ b/src/sync.cpp @@ -81,20 +81,20 @@ static void potential_deadlock_detected(const std::pair<void*, void*>& mismatch, LogPrintf("Previous lock order was:\n"); for (const std::pair<void*, CLockLocation> & i : s2) { if (i.first == mismatch.first) { - LogPrintf(" (1)"); + LogPrintf(" (1)"); /* Continued */ } if (i.first == mismatch.second) { - LogPrintf(" (2)"); + LogPrintf(" (2)"); /* Continued */ } LogPrintf(" %s\n", i.second.ToString()); } LogPrintf("Current lock order is:\n"); for (const std::pair<void*, CLockLocation> & i : s1) { if (i.first == mismatch.first) { - LogPrintf(" (1)"); + LogPrintf(" (1)"); /* Continued */ } if (i.first == mismatch.second) { - LogPrintf(" (2)"); + LogPrintf(" (2)"); /* Continued */ } LogPrintf(" %s\n", i.second.ToString()); } diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp index 6b188a06b4..ee3650d148 100644 --- a/src/test/addrman_tests.cpp +++ b/src/test/addrman_tests.cpp @@ -98,14 +98,14 @@ BOOST_AUTO_TEST_CASE(addrman_simple) CNetAddr source = ResolveIP("252.2.2.2"); // Test: Does Addrman respond correctly when empty. - BOOST_CHECK_EQUAL(addrman.size(), 0); + BOOST_CHECK_EQUAL(addrman.size(), 0U); CAddrInfo addr_null = addrman.Select(); BOOST_CHECK_EQUAL(addr_null.ToString(), "[::]:0"); // Test: Does Addrman::Add work as expected. CService addr1 = ResolveService("250.1.1.1", 8333); BOOST_CHECK(addrman.Add(CAddress(addr1, NODE_NONE), source)); - BOOST_CHECK_EQUAL(addrman.size(), 1); + BOOST_CHECK_EQUAL(addrman.size(), 1U); CAddrInfo addr_ret1 = addrman.Select(); BOOST_CHECK_EQUAL(addr_ret1.ToString(), "250.1.1.1:8333"); @@ -113,7 +113,7 @@ BOOST_AUTO_TEST_CASE(addrman_simple) // Expected dup IP should not be added. CService addr1_dup = ResolveService("250.1.1.1", 8333); BOOST_CHECK(!addrman.Add(CAddress(addr1_dup, NODE_NONE), source)); - BOOST_CHECK_EQUAL(addrman.size(), 1); + BOOST_CHECK_EQUAL(addrman.size(), 1U); // Test: New table has one addr and we add a diff addr we should @@ -128,7 +128,7 @@ BOOST_AUTO_TEST_CASE(addrman_simple) // Test: AddrMan::Clear() should empty the new table. addrman.Clear(); - BOOST_CHECK_EQUAL(addrman.size(), 0); + BOOST_CHECK_EQUAL(addrman.size(), 0U); CAddrInfo addr_null2 = addrman.Select(); BOOST_CHECK_EQUAL(addr_null2.ToString(), "[::]:0"); @@ -146,23 +146,23 @@ BOOST_AUTO_TEST_CASE(addrman_ports) CNetAddr source = ResolveIP("252.2.2.2"); - BOOST_CHECK_EQUAL(addrman.size(), 0); + BOOST_CHECK_EQUAL(addrman.size(), 0U); // Test 7; Addr with same IP but diff port does not replace existing addr. CService addr1 = ResolveService("250.1.1.1", 8333); addrman.Add(CAddress(addr1, NODE_NONE), source); - BOOST_CHECK_EQUAL(addrman.size(), 1); + BOOST_CHECK_EQUAL(addrman.size(), 1U); CService addr1_port = ResolveService("250.1.1.1", 8334); addrman.Add(CAddress(addr1_port, NODE_NONE), source); - BOOST_CHECK_EQUAL(addrman.size(), 1); + BOOST_CHECK_EQUAL(addrman.size(), 1U); CAddrInfo addr_ret2 = addrman.Select(); BOOST_CHECK_EQUAL(addr_ret2.ToString(), "250.1.1.1:8333"); // Test: Add same IP but diff port to tried table, it doesn't get added. // Perhaps this is not ideal behavior but it is the current behavior. addrman.Good(CAddress(addr1_port, NODE_NONE)); - BOOST_CHECK_EQUAL(addrman.size(), 1); + BOOST_CHECK_EQUAL(addrman.size(), 1U); bool newOnly = true; CAddrInfo addr_ret3 = addrman.Select(newOnly); BOOST_CHECK_EQUAL(addr_ret3.ToString(), "250.1.1.1:8333"); @@ -178,7 +178,7 @@ BOOST_AUTO_TEST_CASE(addrman_select) // Test: Select from new with 1 addr in new. CService addr1 = ResolveService("250.1.1.1", 8333); addrman.Add(CAddress(addr1, NODE_NONE), source); - BOOST_CHECK_EQUAL(addrman.size(), 1); + BOOST_CHECK_EQUAL(addrman.size(), 1U); bool newOnly = true; CAddrInfo addr_ret1 = addrman.Select(newOnly); @@ -186,14 +186,14 @@ BOOST_AUTO_TEST_CASE(addrman_select) // Test: move addr to tried, select from new expected nothing returned. addrman.Good(CAddress(addr1, NODE_NONE)); - BOOST_CHECK_EQUAL(addrman.size(), 1); + BOOST_CHECK_EQUAL(addrman.size(), 1U); CAddrInfo addr_ret2 = addrman.Select(newOnly); BOOST_CHECK_EQUAL(addr_ret2.ToString(), "[::]:0"); CAddrInfo addr_ret3 = addrman.Select(); BOOST_CHECK_EQUAL(addr_ret3.ToString(), "250.1.1.1:8333"); - BOOST_CHECK_EQUAL(addrman.size(), 1); + BOOST_CHECK_EQUAL(addrman.size(), 1U); // Add three addresses to new table. @@ -218,14 +218,14 @@ BOOST_AUTO_TEST_CASE(addrman_select) addrman.Good(CAddress(addr7, NODE_NONE)); // Test: 6 addrs + 1 addr from last test = 7. - BOOST_CHECK_EQUAL(addrman.size(), 7); + BOOST_CHECK_EQUAL(addrman.size(), 7U); // Test: Select pulls from new and tried regardless of port number. std::set<uint16_t> ports; for (int i = 0; i < 20; ++i) { ports.insert(addrman.Select().GetPort()); } - BOOST_CHECK_EQUAL(ports.size(), 3); + BOOST_CHECK_EQUAL(ports.size(), 3U); } BOOST_AUTO_TEST_CASE(addrman_new_collisions) @@ -234,7 +234,7 @@ BOOST_AUTO_TEST_CASE(addrman_new_collisions) CNetAddr source = ResolveIP("252.2.2.2"); - BOOST_CHECK_EQUAL(addrman.size(), 0); + BOOST_CHECK_EQUAL(addrman.size(), 0U); for (unsigned int i = 1; i < 18; i++) { CService addr = ResolveService("250.1.1." + std::to_string(i)); @@ -247,11 +247,11 @@ BOOST_AUTO_TEST_CASE(addrman_new_collisions) //Test: new table collision! CService addr1 = ResolveService("250.1.1.18"); addrman.Add(CAddress(addr1, NODE_NONE), source); - BOOST_CHECK_EQUAL(addrman.size(), 17); + BOOST_CHECK_EQUAL(addrman.size(), 17U); CService addr2 = ResolveService("250.1.1.19"); addrman.Add(CAddress(addr2, NODE_NONE), source); - BOOST_CHECK_EQUAL(addrman.size(), 18); + BOOST_CHECK_EQUAL(addrman.size(), 18U); } BOOST_AUTO_TEST_CASE(addrman_tried_collisions) @@ -260,7 +260,7 @@ BOOST_AUTO_TEST_CASE(addrman_tried_collisions) CNetAddr source = ResolveIP("252.2.2.2"); - BOOST_CHECK_EQUAL(addrman.size(), 0); + BOOST_CHECK_EQUAL(addrman.size(), 0U); for (unsigned int i = 1; i < 80; i++) { CService addr = ResolveService("250.1.1." + std::to_string(i)); @@ -274,18 +274,18 @@ BOOST_AUTO_TEST_CASE(addrman_tried_collisions) //Test: tried table collision! CService addr1 = ResolveService("250.1.1.80"); addrman.Add(CAddress(addr1, NODE_NONE), source); - BOOST_CHECK_EQUAL(addrman.size(), 79); + BOOST_CHECK_EQUAL(addrman.size(), 79U); CService addr2 = ResolveService("250.1.1.81"); addrman.Add(CAddress(addr2, NODE_NONE), source); - BOOST_CHECK_EQUAL(addrman.size(), 80); + BOOST_CHECK_EQUAL(addrman.size(), 80U); } BOOST_AUTO_TEST_CASE(addrman_find) { CAddrManTest addrman; - BOOST_CHECK_EQUAL(addrman.size(), 0); + BOOST_CHECK_EQUAL(addrman.size(), 0U); CAddress addr1 = CAddress(ResolveService("250.1.2.1", 8333), NODE_NONE); CAddress addr2 = CAddress(ResolveService("250.1.2.1", 9999), NODE_NONE); @@ -318,7 +318,7 @@ BOOST_AUTO_TEST_CASE(addrman_create) { CAddrManTest addrman; - BOOST_CHECK_EQUAL(addrman.size(), 0); + BOOST_CHECK_EQUAL(addrman.size(), 0U); CAddress addr1 = CAddress(ResolveService("250.1.2.1", 8333), NODE_NONE); CNetAddr source1 = ResolveIP("250.1.2.1"); @@ -338,7 +338,7 @@ BOOST_AUTO_TEST_CASE(addrman_delete) { CAddrManTest addrman; - BOOST_CHECK_EQUAL(addrman.size(), 0); + BOOST_CHECK_EQUAL(addrman.size(), 0U); CAddress addr1 = CAddress(ResolveService("250.1.2.1", 8333), NODE_NONE); CNetAddr source1 = ResolveIP("250.1.2.1"); @@ -347,9 +347,9 @@ BOOST_AUTO_TEST_CASE(addrman_delete) addrman.Create(addr1, source1, &nId); // Test: Delete should actually delete the addr. - BOOST_CHECK_EQUAL(addrman.size(), 1); + BOOST_CHECK_EQUAL(addrman.size(), 1U); addrman.Delete(nId); - BOOST_CHECK_EQUAL(addrman.size(), 0); + BOOST_CHECK_EQUAL(addrman.size(), 0U); CAddrInfo* info2 = addrman.Find(addr1); BOOST_CHECK(info2 == nullptr); } @@ -360,9 +360,9 @@ BOOST_AUTO_TEST_CASE(addrman_getaddr) // Test: Sanity check, GetAddr should never return anything if addrman // is empty. - BOOST_CHECK_EQUAL(addrman.size(), 0); + BOOST_CHECK_EQUAL(addrman.size(), 0U); std::vector<CAddress> vAddr1 = addrman.GetAddr(); - BOOST_CHECK_EQUAL(vAddr1.size(), 0); + BOOST_CHECK_EQUAL(vAddr1.size(), 0U); CAddress addr1 = CAddress(ResolveService("250.250.2.1", 8333), NODE_NONE); addr1.nTime = GetAdjustedTime(); // Set time so isTerrible = false @@ -385,12 +385,12 @@ BOOST_AUTO_TEST_CASE(addrman_getaddr) addrman.Add(addr5, source1); // GetAddr returns 23% of addresses, 23% of 5 is 1 rounded down. - BOOST_CHECK_EQUAL(addrman.GetAddr().size(), 1); + BOOST_CHECK_EQUAL(addrman.GetAddr().size(), 1U); // Test: Ensure GetAddr works with new and tried addresses. addrman.Good(CAddress(addr1, NODE_NONE)); addrman.Good(CAddress(addr2, NODE_NONE)); - BOOST_CHECK_EQUAL(addrman.GetAddr().size(), 1); + BOOST_CHECK_EQUAL(addrman.GetAddr().size(), 1U); // Test: Ensure GetAddr still returns 23% when addrman has many addrs. for (unsigned int i = 1; i < (8 * 256); i++) { @@ -409,9 +409,9 @@ BOOST_AUTO_TEST_CASE(addrman_getaddr) size_t percent23 = (addrman.size() * 23) / 100; BOOST_CHECK_EQUAL(vAddr.size(), percent23); - BOOST_CHECK_EQUAL(vAddr.size(), 461); + BOOST_CHECK_EQUAL(vAddr.size(), 461U); // (Addrman.size() < number of addresses added) due to address collisions. - BOOST_CHECK_EQUAL(addrman.size(), 2006); + BOOST_CHECK_EQUAL(addrman.size(), 2006U); } @@ -454,7 +454,7 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket) } // Test: IP addresses in the same group (\16 prefix for IPv4) should // never get more than 8 buckets - BOOST_CHECK_EQUAL(buckets.size(), 8); + BOOST_CHECK_EQUAL(buckets.size(), 8U); buckets.clear(); for (int j = 0; j < 255; j++) { @@ -466,7 +466,7 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket) } // Test: IP addresses in the different groups should map to more than // 8 buckets. - BOOST_CHECK_EQUAL(buckets.size(), 160); + BOOST_CHECK_EQUAL(buckets.size(), 160U); } BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket) @@ -506,7 +506,7 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket) } // Test: IP addresses in the same group (\16 prefix for IPv4) should // always map to the same bucket. - BOOST_CHECK_EQUAL(buckets.size(), 1); + BOOST_CHECK_EQUAL(buckets.size(), 1U); buckets.clear(); for (int j = 0; j < 4 * 255; j++) { diff --git a/src/test/allocator_tests.cpp b/src/test/allocator_tests.cpp index 24cd88c7a7..67d1229c70 100644 --- a/src/test/allocator_tests.cpp +++ b/src/test/allocator_tests.cpp @@ -64,10 +64,10 @@ BOOST_AUTO_TEST_CASE(arena_tests) BOOST_CHECK(b.stats().used == 128); b.free(a3); BOOST_CHECK(b.stats().used == 0); - BOOST_CHECK_EQUAL(b.stats().chunks_used, 0); + BOOST_CHECK_EQUAL(b.stats().chunks_used, 0U); BOOST_CHECK(b.stats().total == synth_size); BOOST_CHECK(b.stats().free == synth_size); - BOOST_CHECK_EQUAL(b.stats().chunks_free, 1); + BOOST_CHECK_EQUAL(b.stats().chunks_free, 1U); std::vector<void*> addr; BOOST_CHECK(b.alloc(0) == nullptr); // allocating 0 always returns nullptr diff --git a/src/test/blockencodings_tests.cpp b/src/test/blockencodings_tests.cpp index 32330e0548..8cffacbffe 100644 --- a/src/test/blockencodings_tests.cpp +++ b/src/test/blockencodings_tests.cpp @@ -52,8 +52,8 @@ static CBlock BuildBlockTestCase() { } // Number of shared use_counts we expect for a tx we haven't touched -// == 2 (mempool + our copy from the GetSharedTx call) -#define SHARED_TX_OFFSET 2 +// (block + mempool + our copy from the GetSharedTx call) +constexpr long SHARED_TX_OFFSET{3}; BOOST_AUTO_TEST_CASE(SimpleRoundTripTest) { @@ -61,7 +61,7 @@ BOOST_AUTO_TEST_CASE(SimpleRoundTripTest) TestMemPoolEntryHelper entry; CBlock block(BuildBlockTestCase()); - pool.addUnchecked(block.vtx[2]->GetHash(), entry.FromTx(*block.vtx[2])); + pool.addUnchecked(block.vtx[2]->GetHash(), entry.FromTx(block.vtx[2])); LOCK(pool.cs); BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0); @@ -161,7 +161,7 @@ BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest) TestMemPoolEntryHelper entry; CBlock block(BuildBlockTestCase()); - pool.addUnchecked(block.vtx[2]->GetHash(), entry.FromTx(*block.vtx[2])); + pool.addUnchecked(block.vtx[2]->GetHash(), entry.FromTx(block.vtx[2])); LOCK(pool.cs); BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0); @@ -188,7 +188,7 @@ BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest) BOOST_CHECK( partialBlock.IsTxAvailable(1)); BOOST_CHECK( partialBlock.IsTxAvailable(2)); - BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1); + BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1); // +1 because of partialBlock CBlock block2; { @@ -203,6 +203,7 @@ BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest) partialBlock.FillBlock(block2, {block.vtx[1]}); // Current implementation doesn't check txn here, but don't require that partialBlock = tmp; } + BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 2); // +2 because of partialBlock and block2 bool mutated; BOOST_CHECK(block.hashMerkleRoot != BlockMerkleRoot(block2, &mutated)); @@ -213,13 +214,15 @@ BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest) BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block3, &mutated).ToString()); BOOST_CHECK(!mutated); + BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 3); // +2 because of partialBlock and block2 and block3 + txhash = block.vtx[2]->GetHash(); block.vtx.clear(); block2.vtx.clear(); block3.vtx.clear(); - BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1); // + 1 because of partialBlockCopy. + BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1 - 1); // + 1 because of partialBlock; -1 because of block. } - BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0); + BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET - 1); // -1 because of block } BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest) @@ -228,7 +231,7 @@ BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest) TestMemPoolEntryHelper entry; CBlock block(BuildBlockTestCase()); - pool.addUnchecked(block.vtx[1]->GetHash(), entry.FromTx(*block.vtx[1])); + pool.addUnchecked(block.vtx[1]->GetHash(), entry.FromTx(block.vtx[1])); LOCK(pool.cs); BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[1]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0); @@ -268,9 +271,9 @@ BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest) txhash = block.vtx[1]->GetHash(); block.vtx.clear(); block2.vtx.clear(); - BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1); // + 1 because of partialBlockCopy. + BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1 - 1); // + 1 because of partialBlock; -1 because of block. } - BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0); + BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET - 1); // -1 because of block } BOOST_AUTO_TEST_CASE(EmptyBlockRoundTripTest) diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp index 73c8eb5168..17f3004ef3 100644 --- a/src/test/bloom_tests.cpp +++ b/src/test/bloom_tests.cpp @@ -185,7 +185,7 @@ BOOST_AUTO_TEST_CASE(merkle_block_1) CMerkleBlock merkleBlock(block, filter); BOOST_CHECK_EQUAL(merkleBlock.header.GetHash().GetHex(), block.GetHash().GetHex()); - BOOST_CHECK_EQUAL(merkleBlock.vMatchedTxn.size(), 1); + BOOST_CHECK_EQUAL(merkleBlock.vMatchedTxn.size(), 1U); std::pair<unsigned int, uint256> pair = merkleBlock.vMatchedTxn[0]; BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256S("0x74d681e0e03bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20")); diff --git a/src/test/checkqueue_tests.cpp b/src/test/checkqueue_tests.cpp index 8e0ec5243b..de47216449 100644 --- a/src/test/checkqueue_tests.cpp +++ b/src/test/checkqueue_tests.cpp @@ -331,7 +331,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Memory) control.Add(vChecks); } } - BOOST_REQUIRE_EQUAL(MemoryCheck::fake_allocated_memory, 0); + BOOST_REQUIRE_EQUAL(MemoryCheck::fake_allocated_memory, 0U); } tg.interrupt_all(); tg.join_all(); diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp index de7d8f7b90..a146c69fd2 100644 --- a/src/test/coins_tests.cpp +++ b/src/test/coins_tests.cpp @@ -480,8 +480,8 @@ BOOST_AUTO_TEST_CASE(ccoins_serialization) Coin cc1; ss1 >> cc1; BOOST_CHECK_EQUAL(cc1.fCoinBase, false); - BOOST_CHECK_EQUAL(cc1.nHeight, 203998); - BOOST_CHECK_EQUAL(cc1.out.nValue, 60000000000ULL); + BOOST_CHECK_EQUAL(cc1.nHeight, 203998U); + BOOST_CHECK_EQUAL(cc1.out.nValue, CAmount{60000000000}); BOOST_CHECK_EQUAL(HexStr(cc1.out.scriptPubKey), HexStr(GetScriptForDestination(CKeyID(uint160(ParseHex("816115944e077fe7c803cfa57f29b36bf87c1d35")))))); // Good example @@ -489,7 +489,7 @@ BOOST_AUTO_TEST_CASE(ccoins_serialization) Coin cc2; ss2 >> cc2; BOOST_CHECK_EQUAL(cc2.fCoinBase, true); - BOOST_CHECK_EQUAL(cc2.nHeight, 120891); + BOOST_CHECK_EQUAL(cc2.nHeight, 120891U); BOOST_CHECK_EQUAL(cc2.out.nValue, 110397); BOOST_CHECK_EQUAL(HexStr(cc2.out.scriptPubKey), HexStr(GetScriptForDestination(CKeyID(uint160(ParseHex("8c988f1a4a4de2161e0f50aac7f17e7f9555caa4")))))); @@ -498,9 +498,9 @@ BOOST_AUTO_TEST_CASE(ccoins_serialization) Coin cc3; ss3 >> cc3; BOOST_CHECK_EQUAL(cc3.fCoinBase, false); - BOOST_CHECK_EQUAL(cc3.nHeight, 0); + BOOST_CHECK_EQUAL(cc3.nHeight, 0U); BOOST_CHECK_EQUAL(cc3.out.nValue, 0); - BOOST_CHECK_EQUAL(cc3.out.scriptPubKey.size(), 0); + BOOST_CHECK_EQUAL(cc3.out.scriptPubKey.size(), 0U); // scriptPubKey that ends beyond the end of the stream CDataStream ss4(ParseHex("000007"), SER_DISK, CLIENT_VERSION); diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp index cdfc664d56..de0d72614b 100644 --- a/src/test/crypto_tests.cpp +++ b/src/test/crypto_tests.cpp @@ -527,10 +527,10 @@ BOOST_AUTO_TEST_CASE(chacha20_testvector) BOOST_AUTO_TEST_CASE(countbits_tests) { FastRandomContext ctx; - for (int i = 0; i <= 64; ++i) { + for (unsigned int i = 0; i <= 64; ++i) { if (i == 0) { // Check handling of zero. - BOOST_CHECK_EQUAL(CountBits(0), 0); + BOOST_CHECK_EQUAL(CountBits(0), 0U); } else if (i < 10) { for (uint64_t j = 1 << (i - 1); (j >> i) == 0; ++j) { // Exhaustively test up to 10 bits diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp index 35f0463e3e..edc41ec42c 100644 --- a/src/test/dbwrapper_tests.cpp +++ b/src/test/dbwrapper_tests.cpp @@ -210,7 +210,7 @@ BOOST_AUTO_TEST_CASE(iterator_ordering) // Check that creating an iterator creates a snapshot std::unique_ptr<CDBIterator> it(const_cast<CDBWrapper&>(dbw).NewIterator()); - for (int x=0x00; x<256; ++x) { + for (unsigned int x=0x00; x<256; ++x) { uint8_t key = x; uint32_t value = x*x; if (x & 1) BOOST_CHECK(dbw.Write(key, value)); @@ -218,7 +218,7 @@ BOOST_AUTO_TEST_CASE(iterator_ordering) for (int seek_start : {0x00, 0x80}) { it->Seek((uint8_t)seek_start); - for (int x=seek_start; x<255; ++x) { + for (unsigned int x=seek_start; x<255; ++x) { uint8_t key; uint32_t value; BOOST_CHECK(it->Valid()); @@ -295,7 +295,7 @@ BOOST_AUTO_TEST_CASE(iterator_string_ordering) snprintf(buf, sizeof(buf), "%d", seek_start); StringContentsSerializer seek_key(buf); it->Seek(seek_key); - for (int x=seek_start; x<10; ++x) { + for (unsigned int x=seek_start; x<10; ++x) { for (int y = 0; y < 10; y++) { snprintf(buf, sizeof(buf), "%d", x); std::string exp_key(buf); diff --git a/src/test/hash_tests.cpp b/src/test/hash_tests.cpp index 0de0a17904..d7d6c9b5a3 100644 --- a/src/test/hash_tests.cpp +++ b/src/test/hash_tests.cpp @@ -25,22 +25,22 @@ BOOST_AUTO_TEST_CASE(murmurhash3) // // The magic number 0xFBA4C795 comes from CBloomFilter::Hash() - T(0x00000000, 0x00000000, ""); - T(0x6a396f08, 0xFBA4C795, ""); - T(0x81f16f39, 0xffffffff, ""); - - T(0x514e28b7, 0x00000000, "00"); - T(0xea3f0b17, 0xFBA4C795, "00"); - T(0xfd6cf10d, 0x00000000, "ff"); - - T(0x16c6b7ab, 0x00000000, "0011"); - T(0x8eb51c3d, 0x00000000, "001122"); - T(0xb4471bf8, 0x00000000, "00112233"); - T(0xe2301fa8, 0x00000000, "0011223344"); - T(0xfc2e4a15, 0x00000000, "001122334455"); - T(0xb074502c, 0x00000000, "00112233445566"); - T(0x8034d2a0, 0x00000000, "0011223344556677"); - T(0xb4698def, 0x00000000, "001122334455667788"); + T(0x00000000U, 0x00000000, ""); + T(0x6a396f08U, 0xFBA4C795, ""); + T(0x81f16f39U, 0xffffffff, ""); + + T(0x514e28b7U, 0x00000000, "00"); + T(0xea3f0b17U, 0xFBA4C795, "00"); + T(0xfd6cf10dU, 0x00000000, "ff"); + + T(0x16c6b7abU, 0x00000000, "0011"); + T(0x8eb51c3dU, 0x00000000, "001122"); + T(0xb4471bf8U, 0x00000000, "00112233"); + T(0xe2301fa8U, 0x00000000, "0011223344"); + T(0xfc2e4a15U, 0x00000000, "001122334455"); + T(0xb074502cU, 0x00000000, "00112233445566"); + T(0x8034d2a0U, 0x00000000, "0011223344556677"); + T(0xb4698defU, 0x00000000, "001122334455667788"); #undef T } diff --git a/src/test/main_tests.cpp b/src/test/main_tests.cpp index a833a5cb1e..570c205731 100644 --- a/src/test/main_tests.cpp +++ b/src/test/main_tests.cpp @@ -55,7 +55,7 @@ BOOST_AUTO_TEST_CASE(subsidy_limit_test) nSum += nSubsidy * 1000; BOOST_CHECK(MoneyRange(nSum)); } - BOOST_CHECK_EQUAL(nSum, 2099999997690000ULL); + BOOST_CHECK_EQUAL(nSum, CAmount{2099999997690000}); } bool ReturnFalse() { return false; } diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp index 19cd3b0963..37615d08b3 100644 --- a/src/test/mempool_tests.cpp +++ b/src/test/mempool_tests.cpp @@ -89,7 +89,7 @@ BOOST_AUTO_TEST_CASE(MempoolRemoveTest) poolSize = testPool.size(); testPool.removeRecursive(txParent); BOOST_CHECK_EQUAL(testPool.size(), poolSize - 5); - BOOST_CHECK_EQUAL(testPool.size(), 0); + BOOST_CHECK_EQUAL(testPool.size(), 0U); // Add children and grandchildren, but NOT the parent (simulate the parent being in a block) for (int i = 0; i < 3; i++) @@ -102,7 +102,7 @@ BOOST_AUTO_TEST_CASE(MempoolRemoveTest) poolSize = testPool.size(); testPool.removeRecursive(txParent); BOOST_CHECK_EQUAL(testPool.size(), poolSize - 6); - BOOST_CHECK_EQUAL(testPool.size(), 0); + BOOST_CHECK_EQUAL(testPool.size(), 0U); } template<typename name> @@ -156,7 +156,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) tx5.vout[0].nValue = 11 * COIN; entry.nTime = 1; pool.addUnchecked(tx5.GetHash(), entry.Fee(10000LL).FromTx(tx5)); - BOOST_CHECK_EQUAL(pool.size(), 5); + BOOST_CHECK_EQUAL(pool.size(), 5U); std::vector<std::string> sortedOrder; sortedOrder.resize(5); @@ -175,7 +175,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) tx6.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; tx6.vout[0].nValue = 20 * COIN; pool.addUnchecked(tx6.GetHash(), entry.Fee(0LL).FromTx(tx6)); - BOOST_CHECK_EQUAL(pool.size(), 6); + BOOST_CHECK_EQUAL(pool.size(), 6U); // Check that at this point, tx6 is sorted low sortedOrder.insert(sortedOrder.begin(), tx6.GetHash().ToString()); CheckSort<descendant_score>(pool, sortedOrder); @@ -198,7 +198,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) BOOST_CHECK(setAncestorsCalculated == setAncestors); pool.addUnchecked(tx7.GetHash(), entry.FromTx(tx7), setAncestors); - BOOST_CHECK_EQUAL(pool.size(), 7); + BOOST_CHECK_EQUAL(pool.size(), 7U); // Now tx6 should be sorted higher (high fee child): tx7, tx6, tx2, ... sortedOrder.erase(sortedOrder.begin()); @@ -232,7 +232,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) pool.addUnchecked(tx9.GetHash(), entry.Fee(0LL).Time(3).FromTx(tx9), setAncestors); // tx9 should be sorted low - BOOST_CHECK_EQUAL(pool.size(), 9); + BOOST_CHECK_EQUAL(pool.size(), 9U); sortedOrder.insert(sortedOrder.begin(), tx9.GetHash().ToString()); CheckSort<descendant_score>(pool, sortedOrder); @@ -279,7 +279,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) CheckSort<descendant_score>(pool, sortedOrder); // there should be 10 transactions in the mempool - BOOST_CHECK_EQUAL(pool.size(), 10); + BOOST_CHECK_EQUAL(pool.size(), 10U); // Now try removing tx10 and verify the sort order returns to normal pool.removeRecursive(pool.mapTx.find(tx10.GetHash())->GetTx()); @@ -329,7 +329,7 @@ BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest) tx5.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; tx5.vout[0].nValue = 11 * COIN; pool.addUnchecked(tx5.GetHash(), entry.Fee(10000LL).FromTx(tx5)); - BOOST_CHECK_EQUAL(pool.size(), 5); + BOOST_CHECK_EQUAL(pool.size(), 5U); std::vector<std::string> sortedOrder; sortedOrder.resize(5); @@ -359,7 +359,7 @@ BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest) uint64_t tx6Size = GetVirtualTransactionSize(tx6); pool.addUnchecked(tx6.GetHash(), entry.Fee(0LL).FromTx(tx6)); - BOOST_CHECK_EQUAL(pool.size(), 6); + BOOST_CHECK_EQUAL(pool.size(), 6U); // Ties are broken by hash if (tx3.GetHash() < tx6.GetHash()) sortedOrder.push_back(tx6.GetHash().ToString()); @@ -381,7 +381,7 @@ BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest) CAmount fee = (20000/tx2Size)*(tx7Size + tx6Size) - 1; pool.addUnchecked(tx7.GetHash(), entry.Fee(fee).FromTx(tx7)); - BOOST_CHECK_EQUAL(pool.size(), 7); + BOOST_CHECK_EQUAL(pool.size(), 7U); sortedOrder.insert(sortedOrder.begin()+1, tx7.GetHash().ToString()); CheckSort<ancestor_score>(pool, sortedOrder); diff --git a/src/test/merkleblock_tests.cpp b/src/test/merkleblock_tests.cpp index 37a1a84136..2472ea9950 100644 --- a/src/test/merkleblock_tests.cpp +++ b/src/test/merkleblock_tests.cpp @@ -35,20 +35,20 @@ BOOST_AUTO_TEST_CASE(merkleblock_construct_from_txids_found) BOOST_CHECK_EQUAL(merkleBlock.header.GetHash().GetHex(), block.GetHash().GetHex()); // vMatchedTxn is only used when bloom filter is specified. - BOOST_CHECK_EQUAL(merkleBlock.vMatchedTxn.size(), 0); + BOOST_CHECK_EQUAL(merkleBlock.vMatchedTxn.size(), 0U); std::vector<uint256> vMatched; std::vector<unsigned int> vIndex; BOOST_CHECK_EQUAL(merkleBlock.txn.ExtractMatches(vMatched, vIndex).GetHex(), block.hashMerkleRoot.GetHex()); - BOOST_CHECK_EQUAL(vMatched.size(), 2); + BOOST_CHECK_EQUAL(vMatched.size(), 2U); // Ordered by occurrence in depth-first tree traversal. BOOST_CHECK_EQUAL(vMatched[0].ToString(), txhash2.ToString()); - BOOST_CHECK_EQUAL(vIndex[0], 1); + BOOST_CHECK_EQUAL(vIndex[0], 1U); BOOST_CHECK_EQUAL(vMatched[1].ToString(), txhash1.ToString()); - BOOST_CHECK_EQUAL(vIndex[1], 8); + BOOST_CHECK_EQUAL(vIndex[1], 8U); } @@ -65,14 +65,14 @@ BOOST_AUTO_TEST_CASE(merkleblock_construct_from_txids_not_found) CMerkleBlock merkleBlock(block, txids2); BOOST_CHECK_EQUAL(merkleBlock.header.GetHash().GetHex(), block.GetHash().GetHex()); - BOOST_CHECK_EQUAL(merkleBlock.vMatchedTxn.size(), 0); + BOOST_CHECK_EQUAL(merkleBlock.vMatchedTxn.size(), 0U); std::vector<uint256> vMatched; std::vector<unsigned int> vIndex; BOOST_CHECK_EQUAL(merkleBlock.txn.ExtractMatches(vMatched, vIndex).GetHex(), block.hashMerkleRoot.GetHex()); - BOOST_CHECK_EQUAL(vMatched.size(), 0); - BOOST_CHECK_EQUAL(vIndex.size(), 0); + BOOST_CHECK_EQUAL(vMatched.size(), 0U); + BOOST_CHECK_EQUAL(vIndex.size(), 0U); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index d9f6772c2d..c98566f9ca 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -501,7 +501,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) // it into the template because we still check IsFinalTx in CreateNewBlock, // but relative locked txs will if inconsistently added to mempool. // For now these will still generate a valid template until BIP68 soft fork - BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 3); + BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 3U); // However if we advance height by 1 and time by 512, all of them should be mined for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++) chainActive.Tip()->GetAncestor(chainActive.Tip()->nHeight - i)->nTime += 512; //Trick the MedianTimePast @@ -509,7 +509,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) SetMockTime(chainActive.Tip()->GetMedianTimePast() + 1); BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); - BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 5); + BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 5U); chainActive.Tip()->nHeight--; SetMockTime(0); diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp index b593f9633c..066f6328a6 100644 --- a/src/test/multisig_tests.cpp +++ b/src/test/multisig_tests.cpp @@ -19,7 +19,7 @@ BOOST_FIXTURE_TEST_SUITE(multisig_tests, BasicTestingSetup) CScript -sign_multisig(CScript scriptPubKey, std::vector<CKey> keys, CTransaction transaction, int whichIn) +sign_multisig(const CScript& scriptPubKey, const std::vector<CKey>& keys, const CTransaction& transaction, int whichIn) { uint256 hash = SignatureHash(scriptPubKey, transaction, whichIn, SIGHASH_ALL, 0, SigVersion::BASE); diff --git a/src/test/pow_tests.cpp b/src/test/pow_tests.cpp index 26b2f5d0d7..9abfd5ebd8 100644 --- a/src/test/pow_tests.cpp +++ b/src/test/pow_tests.cpp @@ -22,7 +22,7 @@ BOOST_AUTO_TEST_CASE(get_next_work) pindexLast.nHeight = 32255; pindexLast.nTime = 1262152739; // Block #32255 pindexLast.nBits = 0x1d00ffff; - BOOST_CHECK_EQUAL(CalculateNextWorkRequired(&pindexLast, nLastRetargetTime, chainParams->GetConsensus()), 0x1d00d86a); + BOOST_CHECK_EQUAL(CalculateNextWorkRequired(&pindexLast, nLastRetargetTime, chainParams->GetConsensus()), 0x1d00d86aU); } /* Test the constraint on the upper bound for next work */ @@ -34,7 +34,7 @@ BOOST_AUTO_TEST_CASE(get_next_work_pow_limit) pindexLast.nHeight = 2015; pindexLast.nTime = 1233061996; // Block #2015 pindexLast.nBits = 0x1d00ffff; - BOOST_CHECK_EQUAL(CalculateNextWorkRequired(&pindexLast, nLastRetargetTime, chainParams->GetConsensus()), 0x1d00ffff); + BOOST_CHECK_EQUAL(CalculateNextWorkRequired(&pindexLast, nLastRetargetTime, chainParams->GetConsensus()), 0x1d00ffffU); } /* Test the constraint on the lower bound for actual time taken */ @@ -46,7 +46,7 @@ BOOST_AUTO_TEST_CASE(get_next_work_lower_limit_actual) pindexLast.nHeight = 68543; pindexLast.nTime = 1279297671; // Block #68543 pindexLast.nBits = 0x1c05a3f4; - BOOST_CHECK_EQUAL(CalculateNextWorkRequired(&pindexLast, nLastRetargetTime, chainParams->GetConsensus()), 0x1c0168fd); + BOOST_CHECK_EQUAL(CalculateNextWorkRequired(&pindexLast, nLastRetargetTime, chainParams->GetConsensus()), 0x1c0168fdU); } /* Test the constraint on the upper bound for actual time taken */ @@ -58,7 +58,7 @@ BOOST_AUTO_TEST_CASE(get_next_work_upper_limit_actual) pindexLast.nHeight = 46367; pindexLast.nTime = 1269211443; // Block #46367 pindexLast.nBits = 0x1c387f6f; - BOOST_CHECK_EQUAL(CalculateNextWorkRequired(&pindexLast, nLastRetargetTime, chainParams->GetConsensus()), 0x1d00e1fd); + BOOST_CHECK_EQUAL(CalculateNextWorkRequired(&pindexLast, nLastRetargetTime, chainParams->GetConsensus()), 0x1d00e1fdU); } BOOST_AUTO_TEST_CASE(GetBlockProofEquivalentTime_test) diff --git a/src/test/random_tests.cpp b/src/test/random_tests.cpp index 623ed239f0..80a294d129 100644 --- a/src/test/random_tests.cpp +++ b/src/test/random_tests.cpp @@ -52,7 +52,7 @@ BOOST_AUTO_TEST_CASE(fastrandom_randbits) for (int bits = 0; bits < 63; ++bits) { for (int j = 0; j < 1000; ++j) { uint64_t rangebits = ctx1.randbits(bits); - BOOST_CHECK_EQUAL(rangebits >> bits, 0); + BOOST_CHECK_EQUAL(rangebits >> bits, 0U); uint64_t range = ((uint64_t)1) << bits | rangebits; uint64_t rand = ctx2.randrange(range); BOOST_CHECK(rand < range); diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp index 8d9f80ada0..da591547d7 100644 --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -245,16 +245,16 @@ BOOST_AUTO_TEST_CASE(rpc_ban) BOOST_CHECK_NO_THROW(CallRPC(std::string("setban 127.0.0.0 remove"))); BOOST_CHECK_NO_THROW(r = CallRPC(std::string("listbanned"))); ar = r.get_array(); - BOOST_CHECK_EQUAL(ar.size(), 0); + BOOST_CHECK_EQUAL(ar.size(), 0U); - BOOST_CHECK_NO_THROW(r = CallRPC(std::string("setban 127.0.0.0/24 add 1607731200 true"))); + BOOST_CHECK_NO_THROW(r = CallRPC(std::string("setban 127.0.0.0/24 add 9907731200 true"))); BOOST_CHECK_NO_THROW(r = CallRPC(std::string("listbanned"))); ar = r.get_array(); o1 = ar[0].get_obj(); adr = find_value(o1, "address"); UniValue banned_until = find_value(o1, "banned_until"); BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/24"); - BOOST_CHECK_EQUAL(banned_until.get_int64(), 1607731200); // absolute time check + BOOST_CHECK_EQUAL(banned_until.get_int64(), 9907731200); // absolute time check BOOST_CHECK_NO_THROW(CallRPC(std::string("clearbanned"))); @@ -275,7 +275,7 @@ BOOST_AUTO_TEST_CASE(rpc_ban) BOOST_CHECK_NO_THROW(CallRPC(std::string("setban 127.0.0.0/24 remove"))); BOOST_CHECK_NO_THROW(r = CallRPC(std::string("listbanned"))); ar = r.get_array(); - BOOST_CHECK_EQUAL(ar.size(), 0); + BOOST_CHECK_EQUAL(ar.size(), 0U); BOOST_CHECK_NO_THROW(r = CallRPC(std::string("setban 127.0.0.0/255.255.0.0 add"))); BOOST_CHECK_THROW(r = CallRPC(std::string("setban 127.0.1.1 add")), std::runtime_error); @@ -283,7 +283,7 @@ BOOST_AUTO_TEST_CASE(rpc_ban) BOOST_CHECK_NO_THROW(CallRPC(std::string("clearbanned"))); BOOST_CHECK_NO_THROW(r = CallRPC(std::string("listbanned"))); ar = r.get_array(); - BOOST_CHECK_EQUAL(ar.size(), 0); + BOOST_CHECK_EQUAL(ar.size(), 0U); BOOST_CHECK_THROW(r = CallRPC(std::string("setban test add")), std::runtime_error); //invalid IP diff --git a/src/test/script_standard_tests.cpp b/src/test/script_standard_tests.cpp index cd30fbeda7..ff0bf6c66d 100644 --- a/src/test/script_standard_tests.cpp +++ b/src/test/script_standard_tests.cpp @@ -33,7 +33,7 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_success) s << ToByteVector(pubkeys[0]) << OP_CHECKSIG; BOOST_CHECK(Solver(s, whichType, solutions)); BOOST_CHECK_EQUAL(whichType, TX_PUBKEY); - BOOST_CHECK_EQUAL(solutions.size(), 1); + BOOST_CHECK_EQUAL(solutions.size(), 1U); BOOST_CHECK(solutions[0] == ToByteVector(pubkeys[0])); // TX_PUBKEYHASH @@ -41,7 +41,7 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_success) s << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; BOOST_CHECK(Solver(s, whichType, solutions)); BOOST_CHECK_EQUAL(whichType, TX_PUBKEYHASH); - BOOST_CHECK_EQUAL(solutions.size(), 1); + BOOST_CHECK_EQUAL(solutions.size(), 1U); BOOST_CHECK(solutions[0] == ToByteVector(pubkeys[0].GetID())); // TX_SCRIPTHASH @@ -50,7 +50,7 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_success) s << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL; BOOST_CHECK(Solver(s, whichType, solutions)); BOOST_CHECK_EQUAL(whichType, TX_SCRIPTHASH); - BOOST_CHECK_EQUAL(solutions.size(), 1); + BOOST_CHECK_EQUAL(solutions.size(), 1U); BOOST_CHECK(solutions[0] == ToByteVector(CScriptID(redeemScript))); // TX_MULTISIG @@ -61,7 +61,7 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_success) OP_2 << OP_CHECKMULTISIG; BOOST_CHECK(Solver(s, whichType, solutions)); BOOST_CHECK_EQUAL(whichType, TX_MULTISIG); - BOOST_CHECK_EQUAL(solutions.size(), 4); + BOOST_CHECK_EQUAL(solutions.size(), 4U); BOOST_CHECK(solutions[0] == std::vector<unsigned char>({1})); BOOST_CHECK(solutions[1] == ToByteVector(pubkeys[0])); BOOST_CHECK(solutions[2] == ToByteVector(pubkeys[1])); @@ -75,7 +75,7 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_success) OP_3 << OP_CHECKMULTISIG; BOOST_CHECK(Solver(s, whichType, solutions)); BOOST_CHECK_EQUAL(whichType, TX_MULTISIG); - BOOST_CHECK_EQUAL(solutions.size(), 5); + BOOST_CHECK_EQUAL(solutions.size(), 5U); BOOST_CHECK(solutions[0] == std::vector<unsigned char>({2})); BOOST_CHECK(solutions[1] == ToByteVector(pubkeys[0])); BOOST_CHECK(solutions[2] == ToByteVector(pubkeys[1])); @@ -90,14 +90,14 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_success) std::vector<unsigned char>({255}); BOOST_CHECK(Solver(s, whichType, solutions)); BOOST_CHECK_EQUAL(whichType, TX_NULL_DATA); - BOOST_CHECK_EQUAL(solutions.size(), 0); + BOOST_CHECK_EQUAL(solutions.size(), 0U); // TX_WITNESS_V0_KEYHASH s.clear(); s << OP_0 << ToByteVector(pubkeys[0].GetID()); BOOST_CHECK(Solver(s, whichType, solutions)); BOOST_CHECK_EQUAL(whichType, TX_WITNESS_V0_KEYHASH); - BOOST_CHECK_EQUAL(solutions.size(), 1); + BOOST_CHECK_EQUAL(solutions.size(), 1U); BOOST_CHECK(solutions[0] == ToByteVector(pubkeys[0].GetID())); // TX_WITNESS_V0_SCRIPTHASH @@ -109,7 +109,7 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_success) s << OP_0 << ToByteVector(scriptHash); BOOST_CHECK(Solver(s, whichType, solutions)); BOOST_CHECK_EQUAL(whichType, TX_WITNESS_V0_SCRIPTHASH); - BOOST_CHECK_EQUAL(solutions.size(), 1); + BOOST_CHECK_EQUAL(solutions.size(), 1U); BOOST_CHECK(solutions[0] == ToByteVector(scriptHash)); // TX_NONSTANDARD @@ -264,7 +264,7 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations) s << ToByteVector(pubkeys[0]) << OP_CHECKSIG; BOOST_CHECK(ExtractDestinations(s, whichType, addresses, nRequired)); BOOST_CHECK_EQUAL(whichType, TX_PUBKEY); - BOOST_CHECK_EQUAL(addresses.size(), 1); + BOOST_CHECK_EQUAL(addresses.size(), 1U); BOOST_CHECK_EQUAL(nRequired, 1); BOOST_CHECK(boost::get<CKeyID>(&addresses[0]) && *boost::get<CKeyID>(&addresses[0]) == pubkeys[0].GetID()); @@ -274,7 +274,7 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations) s << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; BOOST_CHECK(ExtractDestinations(s, whichType, addresses, nRequired)); BOOST_CHECK_EQUAL(whichType, TX_PUBKEYHASH); - BOOST_CHECK_EQUAL(addresses.size(), 1); + BOOST_CHECK_EQUAL(addresses.size(), 1U); BOOST_CHECK_EQUAL(nRequired, 1); BOOST_CHECK(boost::get<CKeyID>(&addresses[0]) && *boost::get<CKeyID>(&addresses[0]) == pubkeys[0].GetID()); @@ -285,7 +285,7 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations) s << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL; BOOST_CHECK(ExtractDestinations(s, whichType, addresses, nRequired)); BOOST_CHECK_EQUAL(whichType, TX_SCRIPTHASH); - BOOST_CHECK_EQUAL(addresses.size(), 1); + BOOST_CHECK_EQUAL(addresses.size(), 1U); BOOST_CHECK_EQUAL(nRequired, 1); BOOST_CHECK(boost::get<CScriptID>(&addresses[0]) && *boost::get<CScriptID>(&addresses[0]) == CScriptID(redeemScript)); @@ -298,7 +298,7 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations) OP_2 << OP_CHECKMULTISIG; BOOST_CHECK(ExtractDestinations(s, whichType, addresses, nRequired)); BOOST_CHECK_EQUAL(whichType, TX_MULTISIG); - BOOST_CHECK_EQUAL(addresses.size(), 2); + BOOST_CHECK_EQUAL(addresses.size(), 2U); BOOST_CHECK_EQUAL(nRequired, 2); BOOST_CHECK(boost::get<CKeyID>(&addresses[0]) && *boost::get<CKeyID>(&addresses[0]) == pubkeys[0].GetID()); @@ -561,7 +561,14 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine) keystore.AddKey(keys[1]); result = IsMine(keystore, scriptPubKey, isInvalid); - BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); + 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); + BOOST_CHECK_EQUAL(result, ISMINE_NO); BOOST_CHECK(!isInvalid); } diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 46a2d13745..068f1e66f4 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -1029,7 +1029,7 @@ BOOST_AUTO_TEST_CASE(script_PushData) } CScript -sign_multisig(CScript scriptPubKey, std::vector<CKey> keys, CTransaction transaction) +sign_multisig(const CScript& scriptPubKey, const std::vector<CKey>& keys, const CTransaction& transaction) { uint256 hash = SignatureHash(scriptPubKey, transaction, 0, SIGHASH_ALL, 0, SigVersion::BASE); @@ -1053,7 +1053,7 @@ sign_multisig(CScript scriptPubKey, std::vector<CKey> keys, CTransaction transac return result; } CScript -sign_multisig(CScript scriptPubKey, const CKey &key, CTransaction transaction) +sign_multisig(const CScript& scriptPubKey, const CKey& key, const CTransaction& transaction) { std::vector<CKey> keys; keys.push_back(key); @@ -1349,43 +1349,43 @@ BOOST_AUTO_TEST_CASE(script_FindAndDelete) s = CScript() << OP_1 << OP_2; d = CScript(); // delete nothing should be a no-op expect = s; - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 0); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 0); BOOST_CHECK(s == expect); s = CScript() << OP_1 << OP_2 << OP_3; d = CScript() << OP_2; expect = CScript() << OP_1 << OP_3; - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 1); BOOST_CHECK(s == expect); s = CScript() << OP_3 << OP_1 << OP_3 << OP_3 << OP_4 << OP_3; d = CScript() << OP_3; expect = CScript() << OP_1 << OP_4; - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 4); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 4); BOOST_CHECK(s == expect); s = ScriptFromHex("0302ff03"); // PUSH 0x02ff03 onto stack d = ScriptFromHex("0302ff03"); expect = CScript(); - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 1); BOOST_CHECK(s == expect); s = ScriptFromHex("0302ff030302ff03"); // PUSH 0x2ff03 PUSH 0x2ff03 d = ScriptFromHex("0302ff03"); expect = CScript(); - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 2); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 2); BOOST_CHECK(s == expect); s = ScriptFromHex("0302ff030302ff03"); d = ScriptFromHex("02"); expect = s; // FindAndDelete matches entire opcodes - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 0); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 0); BOOST_CHECK(s == expect); s = ScriptFromHex("0302ff030302ff03"); d = ScriptFromHex("ff"); expect = s; - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 0); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 0); BOOST_CHECK(s == expect); // This is an odd edge case: strip of the push-three-bytes @@ -1393,44 +1393,44 @@ BOOST_AUTO_TEST_CASE(script_FindAndDelete) s = ScriptFromHex("0302ff030302ff03"); d = ScriptFromHex("03"); expect = CScript() << ParseHex("ff03") << ParseHex("ff03"); - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 2); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 2); BOOST_CHECK(s == expect); // Byte sequence that spans multiple opcodes: s = ScriptFromHex("02feed5169"); // PUSH(0xfeed) OP_1 OP_VERIFY d = ScriptFromHex("feed51"); expect = s; - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 0); // doesn't match 'inside' opcodes + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 0); // doesn't match 'inside' opcodes BOOST_CHECK(s == expect); s = ScriptFromHex("02feed5169"); // PUSH(0xfeed) OP_1 OP_VERIFY d = ScriptFromHex("02feed51"); expect = ScriptFromHex("69"); - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 1); BOOST_CHECK(s == expect); s = ScriptFromHex("516902feed5169"); d = ScriptFromHex("feed51"); expect = s; - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 0); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 0); BOOST_CHECK(s == expect); s = ScriptFromHex("516902feed5169"); d = ScriptFromHex("02feed51"); expect = ScriptFromHex("516969"); - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 1); BOOST_CHECK(s == expect); s = CScript() << OP_0 << OP_0 << OP_1 << OP_1; d = CScript() << OP_0 << OP_1; expect = CScript() << OP_0 << OP_1; // FindAndDelete is single-pass - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 1); BOOST_CHECK(s == expect); s = CScript() << OP_0 << OP_0 << OP_1 << OP_0 << OP_1 << OP_1; d = CScript() << OP_0 << OP_1; expect = CScript() << OP_0 << OP_1; // FindAndDelete is single-pass - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 2); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 2); BOOST_CHECK(s == expect); // Another weird edge case: @@ -1438,13 +1438,13 @@ BOOST_AUTO_TEST_CASE(script_FindAndDelete) s = ScriptFromHex("0003feed"); d = ScriptFromHex("03feed"); // ... can remove the invalid push expect = ScriptFromHex("00"); - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 1); BOOST_CHECK(s == expect); s = ScriptFromHex("0003feed"); d = ScriptFromHex("00"); expect = ScriptFromHex("03feed"); - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 1); BOOST_CHECK(s == expect); } diff --git a/src/test/serialize_tests.cpp b/src/test/serialize_tests.cpp index 9b8b7bdc56..eba58e0042 100644 --- a/src/test/serialize_tests.cpp +++ b/src/test/serialize_tests.cpp @@ -23,7 +23,7 @@ protected: CTransactionRef txval; public: CSerializeMethodsTestSingle() = default; - CSerializeMethodsTestSingle(int intvalin, bool boolvalin, std::string stringvalin, const char* charstrvalin, CTransaction txvalin) : intval(intvalin), boolval(boolvalin), stringval(std::move(stringvalin)), txval(MakeTransactionRef(txvalin)) + CSerializeMethodsTestSingle(int intvalin, bool boolvalin, std::string stringvalin, const char* charstrvalin, const CTransactionRef& txvalin) : intval(intvalin), boolval(boolvalin), stringval(std::move(stringvalin)), txval(txvalin) { memcpy(charstrval, charstrvalin, sizeof(charstrval)); } @@ -78,18 +78,18 @@ BOOST_AUTO_TEST_CASE(sizes) BOOST_CHECK_EQUAL(sizeof(char), GetSerializeSize(bool(0), 0)); // Sanity-check GetSerializeSize and c++ type matching - BOOST_CHECK_EQUAL(GetSerializeSize(char(0), 0), 1); - BOOST_CHECK_EQUAL(GetSerializeSize(int8_t(0), 0), 1); - BOOST_CHECK_EQUAL(GetSerializeSize(uint8_t(0), 0), 1); - BOOST_CHECK_EQUAL(GetSerializeSize(int16_t(0), 0), 2); - BOOST_CHECK_EQUAL(GetSerializeSize(uint16_t(0), 0), 2); - BOOST_CHECK_EQUAL(GetSerializeSize(int32_t(0), 0), 4); - BOOST_CHECK_EQUAL(GetSerializeSize(uint32_t(0), 0), 4); - BOOST_CHECK_EQUAL(GetSerializeSize(int64_t(0), 0), 8); - BOOST_CHECK_EQUAL(GetSerializeSize(uint64_t(0), 0), 8); - BOOST_CHECK_EQUAL(GetSerializeSize(float(0), 0), 4); - BOOST_CHECK_EQUAL(GetSerializeSize(double(0), 0), 8); - BOOST_CHECK_EQUAL(GetSerializeSize(bool(0), 0), 1); + BOOST_CHECK_EQUAL(GetSerializeSize(char(0), 0), 1U); + BOOST_CHECK_EQUAL(GetSerializeSize(int8_t(0), 0), 1U); + BOOST_CHECK_EQUAL(GetSerializeSize(uint8_t(0), 0), 1U); + BOOST_CHECK_EQUAL(GetSerializeSize(int16_t(0), 0), 2U); + BOOST_CHECK_EQUAL(GetSerializeSize(uint16_t(0), 0), 2U); + BOOST_CHECK_EQUAL(GetSerializeSize(int32_t(0), 0), 4U); + BOOST_CHECK_EQUAL(GetSerializeSize(uint32_t(0), 0), 4U); + BOOST_CHECK_EQUAL(GetSerializeSize(int64_t(0), 0), 8U); + BOOST_CHECK_EQUAL(GetSerializeSize(uint64_t(0), 0), 8U); + BOOST_CHECK_EQUAL(GetSerializeSize(float(0), 0), 4U); + BOOST_CHECK_EQUAL(GetSerializeSize(double(0), 0), 8U); + BOOST_CHECK_EQUAL(GetSerializeSize(bool(0), 0), 1U); } BOOST_AUTO_TEST_CASE(floats_conversion) @@ -103,12 +103,12 @@ BOOST_AUTO_TEST_CASE(floats_conversion) BOOST_CHECK_EQUAL(ser_uint32_to_float(0x40800000), 4.0F); BOOST_CHECK_EQUAL(ser_uint32_to_float(0x44444444), 785.066650390625F); - BOOST_CHECK_EQUAL(ser_float_to_uint32(0.0F), 0x00000000); - BOOST_CHECK_EQUAL(ser_float_to_uint32(0.5F), 0x3f000000); - BOOST_CHECK_EQUAL(ser_float_to_uint32(1.0F), 0x3f800000); - BOOST_CHECK_EQUAL(ser_float_to_uint32(2.0F), 0x40000000); - BOOST_CHECK_EQUAL(ser_float_to_uint32(4.0F), 0x40800000); - BOOST_CHECK_EQUAL(ser_float_to_uint32(785.066650390625F), 0x44444444); + BOOST_CHECK_EQUAL(ser_float_to_uint32(0.0F), 0x00000000U); + BOOST_CHECK_EQUAL(ser_float_to_uint32(0.5F), 0x3f000000U); + BOOST_CHECK_EQUAL(ser_float_to_uint32(1.0F), 0x3f800000U); + BOOST_CHECK_EQUAL(ser_float_to_uint32(2.0F), 0x40000000U); + BOOST_CHECK_EQUAL(ser_float_to_uint32(4.0F), 0x40800000U); + BOOST_CHECK_EQUAL(ser_float_to_uint32(785.066650390625F), 0x44444444U); } BOOST_AUTO_TEST_CASE(doubles_conversion) @@ -299,39 +299,39 @@ BOOST_AUTO_TEST_CASE(insert_delete) { // Test inserting/deleting bytes. CDataStream ss(SER_DISK, 0); - BOOST_CHECK_EQUAL(ss.size(), 0); + BOOST_CHECK_EQUAL(ss.size(), 0U); ss.write("\x00\x01\x02\xff", 4); - BOOST_CHECK_EQUAL(ss.size(), 4); + BOOST_CHECK_EQUAL(ss.size(), 4U); char c = (char)11; // Inserting at beginning/end/middle: ss.insert(ss.begin(), c); - BOOST_CHECK_EQUAL(ss.size(), 5); + BOOST_CHECK_EQUAL(ss.size(), 5U); BOOST_CHECK_EQUAL(ss[0], c); BOOST_CHECK_EQUAL(ss[1], 0); ss.insert(ss.end(), c); - BOOST_CHECK_EQUAL(ss.size(), 6); + BOOST_CHECK_EQUAL(ss.size(), 6U); BOOST_CHECK_EQUAL(ss[4], (char)0xff); BOOST_CHECK_EQUAL(ss[5], c); ss.insert(ss.begin()+2, c); - BOOST_CHECK_EQUAL(ss.size(), 7); + BOOST_CHECK_EQUAL(ss.size(), 7U); BOOST_CHECK_EQUAL(ss[2], c); // Delete at beginning/end/middle ss.erase(ss.begin()); - BOOST_CHECK_EQUAL(ss.size(), 6); + BOOST_CHECK_EQUAL(ss.size(), 6U); BOOST_CHECK_EQUAL(ss[0], 0); ss.erase(ss.begin()+ss.size()-1); - BOOST_CHECK_EQUAL(ss.size(), 5); + BOOST_CHECK_EQUAL(ss.size(), 5U); BOOST_CHECK_EQUAL(ss[4], (char)0xff); ss.erase(ss.begin()+1); - BOOST_CHECK_EQUAL(ss.size(), 4); + BOOST_CHECK_EQUAL(ss.size(), 4U); BOOST_CHECK_EQUAL(ss[0], 0); BOOST_CHECK_EQUAL(ss[1], 1); BOOST_CHECK_EQUAL(ss[2], 2); @@ -340,7 +340,7 @@ BOOST_AUTO_TEST_CASE(insert_delete) // Make sure GetAndClear does the right thing: CSerializeData d; ss.GetAndClear(d); - BOOST_CHECK_EQUAL(ss.size(), 0); + BOOST_CHECK_EQUAL(ss.size(), 0U); } BOOST_AUTO_TEST_CASE(class_methods) @@ -350,8 +350,9 @@ BOOST_AUTO_TEST_CASE(class_methods) std::string stringval("testing"); const char charstrval[16] = "testing charstr"; CMutableTransaction txval; - CSerializeMethodsTestSingle methodtest1(intval, boolval, stringval, charstrval, txval); - CSerializeMethodsTestMany methodtest2(intval, boolval, stringval, charstrval, txval); + CTransactionRef tx_ref{MakeTransactionRef(txval)}; + CSerializeMethodsTestSingle methodtest1(intval, boolval, stringval, charstrval, tx_ref); + CSerializeMethodsTestMany methodtest2(intval, boolval, stringval, charstrval, tx_ref); CSerializeMethodsTestSingle methodtest3; CSerializeMethodsTestMany methodtest4; CDataStream ss(SER_DISK, PROTOCOL_VERSION); diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp index a2bd8998b1..6b8856ef47 100644 --- a/src/test/sighash_tests.cpp +++ b/src/test/sighash_tests.cpp @@ -35,7 +35,7 @@ uint256 static SignatureHashOld(CScript scriptCode, const CTransaction& txTo, un // In case concatenating two scripts ends up with two codeseparators, // or an extra one at the end, this prevents all those possible incompatibilities. - scriptCode.FindAndDelete(CScript(OP_CODESEPARATOR)); + FindAndDelete(scriptCode, CScript(OP_CODESEPARATOR)); // Blank out other inputs' signatures for (unsigned int i = 0; i < txTmp.vin.size(); i++) diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index 9390a93b99..fe816a6f79 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -17,8 +17,6 @@ #include <rpc/register.h> #include <script/sigcache.h> -#include <memory> - void CConnmanTest::AddNode(CNode& node) { LOCK(g_connman->cs_vNodes); @@ -49,7 +47,6 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName) SetupNetworking(); InitSignatureCache(); InitScriptExecutionCache(); - fPrintToDebugLog = false; // don't want to write to debug.log file fCheckBlockIndex = true; SelectParams(chainName); noui_connect(); @@ -68,7 +65,7 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha RegisterAllCoreRPCCommands(tableRPC); ClearDatadirCache(); - pathTemp = fs::temp_directory_path() / strprintf("test_bitcoin_%lu_%i", (unsigned long)GetTime(), (int)(InsecureRandRange(100000))); + 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()); @@ -87,7 +84,7 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha { CValidationState state; if (!ActivateBestChain(state, chainparams)) { - throw std::runtime_error("ActivateBestChain failed."); + throw std::runtime_error(strprintf("ActivateBestChain failed. (%s)", FormatStateMessage(state))); } } nScriptCheckThreads = 3; @@ -125,7 +122,7 @@ TestChain100Setup::TestChain100Setup() : TestingSetup(CBaseChainParams::REGTEST) { std::vector<CMutableTransaction> noTxns; CBlock b = CreateAndProcessBlock(noTxns, scriptPubKey); - coinbaseTxns.push_back(*b.vtx[0]); + m_coinbase_txns.push_back(b.vtx[0]); } } @@ -166,12 +163,12 @@ TestChain100Setup::~TestChain100Setup() CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CMutableTransaction &tx) { - CTransaction txn(tx); - return FromTx(txn); + return FromTx(MakeTransactionRef(tx)); } -CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CTransaction &txn) { - return CTxMemPoolEntry(MakeTransactionRef(txn), nFee, nTime, nHeight, +CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CTransactionRef& tx) +{ + return CTxMemPoolEntry(tx, nFee, nTime, nHeight, spendsCoinbase, sigOpCost, lp); } diff --git a/src/test/test_bitcoin.h b/src/test/test_bitcoin.h index 8136da3aa9..1f91eb622c 100644 --- a/src/test/test_bitcoin.h +++ b/src/test/test_bitcoin.h @@ -87,7 +87,7 @@ struct TestChain100Setup : public TestingSetup { ~TestChain100Setup(); - std::vector<CTransaction> coinbaseTxns; // For convenience, coinbase transactions + std::vector<CTransactionRef> m_coinbase_txns; // For convenience, coinbase transactions CKey coinbaseKey; // private/public key needed to spend coinbase transactions }; @@ -107,8 +107,8 @@ struct TestMemPoolEntryHelper nFee(0), nTime(0), nHeight(1), spendsCoinbase(false), sigOpCost(4) { } - CTxMemPoolEntry FromTx(const CMutableTransaction &tx); - CTxMemPoolEntry FromTx(const CTransaction &tx); + CTxMemPoolEntry FromTx(const CMutableTransaction& tx); + CTxMemPoolEntry FromTx(const CTransactionRef& tx); // Change the default value TestMemPoolEntryHelper &Fee(CAmount _fee) { nFee = _fee; return *this; } diff --git a/src/test/torcontrol_tests.cpp b/src/test/torcontrol_tests.cpp index d0aa8659c2..9ece9e70c2 100644 --- a/src/test/torcontrol_tests.cpp +++ b/src/test/torcontrol_tests.cpp @@ -167,10 +167,10 @@ BOOST_AUTO_TEST_CASE(util_ParseTorReplyMapping) // (needed because string comparison reads the null as end-of-string) BOOST_TEST_MESSAGE(std::string("CheckParseTorReplyMapping(Null=\"\\0\")")); auto ret = ParseTorReplyMapping("Null=\"\\0\""); - BOOST_CHECK_EQUAL(ret.size(), 1); + BOOST_CHECK_EQUAL(ret.size(), 1U); auto r_it = ret.begin(); BOOST_CHECK_EQUAL(r_it->first, "Null"); - BOOST_CHECK_EQUAL(r_it->second.size(), 1); + BOOST_CHECK_EQUAL(r_it->second.size(), 1U); BOOST_CHECK_EQUAL(r_it->second[0], '\0'); // A more complex valid grammar. PROTOCOLINFO accepts a VersionLine that diff --git a/src/test/txindex_tests.cpp b/src/test/txindex_tests.cpp new file mode 100644 index 0000000000..14158f2875 --- /dev/null +++ b/src/test/txindex_tests.cpp @@ -0,0 +1,66 @@ +// Copyright (c) 2017-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 <index/txindex.h> +#include <script/standard.h> +#include <test/test_bitcoin.h> +#include <util.h> +#include <utiltime.h> +#include <validation.h> + +#include <boost/test/unit_test.hpp> + +BOOST_AUTO_TEST_SUITE(txindex_tests) + +BOOST_FIXTURE_TEST_CASE(txindex_initial_sync, TestChain100Setup) +{ + TxIndex txindex(MakeUnique<TxIndexDB>(1 << 20, true)); + + CTransactionRef tx_disk; + uint256 block_hash; + + // Transaction should not be found in the index before it is started. + for (const auto& txn : m_coinbase_txns) { + BOOST_CHECK(!txindex.FindTx(txn->GetHash(), block_hash, tx_disk)); + } + + // BlockUntilSyncedToCurrentChain should return false before txindex is started. + BOOST_CHECK(!txindex.BlockUntilSyncedToCurrentChain()); + + txindex.Start(); + + // Allow tx index to catch up with the block index. + constexpr int64_t timeout_ms = 10 * 1000; + int64_t time_start = GetTimeMillis(); + while (!txindex.BlockUntilSyncedToCurrentChain()) { + BOOST_REQUIRE(time_start + timeout_ms > GetTimeMillis()); + MilliSleep(100); + } + + // Check that txindex has all txs that were in the chain before it started. + for (const auto& txn : m_coinbase_txns) { + if (!txindex.FindTx(txn->GetHash(), block_hash, tx_disk)) { + BOOST_ERROR("FindTx failed"); + } else if (tx_disk->GetHash() != txn->GetHash()) { + BOOST_ERROR("Read incorrect tx"); + } + } + + // Check that new transactions in new blocks make it into the index. + for (int i = 0; i < 10; i++) { + CScript coinbase_script_pub_key = GetScriptForDestination(coinbaseKey.GetPubKey().GetID()); + std::vector<CMutableTransaction> no_txns; + const CBlock& block = CreateAndProcessBlock(no_txns, coinbase_script_pub_key); + const CTransaction& txn = *block.vtx[0]; + + BOOST_CHECK(txindex.BlockUntilSyncedToCurrentChain()); + if (!txindex.FindTx(txn.GetHash(), block_hash, tx_disk)) { + BOOST_ERROR("FindTx failed"); + } else if (tx_disk->GetHash() != txn.GetHash()) { + BOOST_ERROR("Read incorrect tx"); + } + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp index 7087c26774..7a52697859 100644 --- a/src/test/txvalidationcache_tests.cpp +++ b/src/test/txvalidationcache_tests.cpp @@ -48,7 +48,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup) { spends[i].nVersion = 1; spends[i].vin.resize(1); - spends[i].vin[0].prevout.hash = coinbaseTxns[0].GetHash(); + spends[i].vin[0].prevout.hash = m_coinbase_txns[0]->GetHash(); spends[i].vin[0].prevout.n = 0; spends[i].vout.resize(1); spends[i].vout[0].nValue = 11*CENT; @@ -88,7 +88,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup) BOOST_CHECK(chainActive.Tip()->GetBlockHash() == block.GetHash()); // spends[1] should have been removed from the mempool when the // block with spends[0] is accepted: - BOOST_CHECK_EQUAL(mempool.size(), 0); + BOOST_CHECK_EQUAL(mempool.size(), 0U); } // Run CheckInputs (using pcoinsTip) on the given transaction, for all script @@ -167,7 +167,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup) spend_tx.nVersion = 1; spend_tx.vin.resize(1); - spend_tx.vin[0].prevout.hash = coinbaseTxns[0].GetHash(); + spend_tx.vin[0].prevout.hash = m_coinbase_txns[0]->GetHash(); spend_tx.vin[0].prevout.n = 0; spend_tx.vout.resize(4); spend_tx.vout[0].nValue = 11*CENT; @@ -205,7 +205,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup) // not caching invalidity (if that changes, delete this test case). std::vector<CScriptCheck> scriptchecks; BOOST_CHECK(CheckInputs(spend_tx, state, pcoinsTip.get(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, &scriptchecks)); - BOOST_CHECK_EQUAL(scriptchecks.size(), 1); + BOOST_CHECK_EQUAL(scriptchecks.size(), 1U); // Test that CheckInputs returns true iff DERSIG-enforcing flags are // not present. Don't add these checks to the cache, so that we can @@ -314,7 +314,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup) // Sign SignatureData sigdata; - ProduceSignature(MutableTransactionSignatureCreator(&keystore, &valid_with_witness_tx, 0, 11*CENT, SIGHASH_ALL), spend_tx.vout[1].scriptPubKey, sigdata); + ProduceSignature(keystore, MutableTransactionSignatureCreator(&valid_with_witness_tx, 0, 11*CENT, SIGHASH_ALL), spend_tx.vout[1].scriptPubKey, sigdata); UpdateTransaction(valid_with_witness_tx, 0, sigdata); // This should be valid under all script flags. @@ -342,7 +342,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup) // Sign for (int i=0; i<2; ++i) { SignatureData sigdata; - ProduceSignature(MutableTransactionSignatureCreator(&keystore, &tx, i, 11*CENT, SIGHASH_ALL), spend_tx.vout[i].scriptPubKey, sigdata); + ProduceSignature(keystore, MutableTransactionSignatureCreator(&tx, i, 11*CENT, SIGHASH_ALL), spend_tx.vout[i].scriptPubKey, sigdata); UpdateTransaction(tx, i, sigdata); } @@ -364,7 +364,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup) // input was valid) BOOST_CHECK(CheckInputs(tx, state, pcoinsTip.get(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, &scriptchecks)); // Should get 2 script checks back -- caching is on a whole-transaction basis. - BOOST_CHECK_EQUAL(scriptchecks.size(), 2); + BOOST_CHECK_EQUAL(scriptchecks.size(), 2U); } } diff --git a/src/test/uint256_tests.cpp b/src/test/uint256_tests.cpp index ad5478e829..20ed29f59b 100644 --- a/src/test/uint256_tests.cpp +++ b/src/test/uint256_tests.cpp @@ -266,4 +266,17 @@ BOOST_AUTO_TEST_CASE( conversion ) BOOST_CHECK(R2L.GetHex() == UintToArith256(R2L).GetHex()); } +BOOST_AUTO_TEST_CASE( operator_with_self ) +{ + arith_uint256 v = UintToArith256(uint256S("02")); + v *= v; + BOOST_CHECK(v == UintToArith256(uint256S("04"))); + v /= v; + BOOST_CHECK(v == UintToArith256(uint256S("01"))); + v += v; + BOOST_CHECK(v == UintToArith256(uint256S("02"))); + v -= v; + BOOST_CHECK(v == UintToArith256(uint256S("0"))); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 4b44bbadac..1c3acfb1a5 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -62,7 +62,7 @@ BOOST_AUTO_TEST_CASE(util_ParseHex) result = ParseHex("12 34 56 78"); BOOST_CHECK(result.size() == 4 && result[0] == 0x12 && result[1] == 0x34 && result[2] == 0x56 && result[3] == 0x78); - // Leading space must be supported (used in CDBEnv::Salvage) + // Leading space must be supported (used in BerkeleyEnvironment::Salvage) result = ParseHex(" 89 34 56 78"); BOOST_CHECK(result.size() == 4 && result[0] == 0x89 && result[1] == 0x34 && result[2] == 0x56 && result[3] == 0x78); @@ -159,17 +159,6 @@ BOOST_AUTO_TEST_CASE(util_HexStr) } -BOOST_AUTO_TEST_CASE(util_DateTimeStrFormat) -{ - BOOST_CHECK_EQUAL(DateTimeStrFormat("%Y-%m-%d %H:%M:%S", 0), "1970-01-01 00:00:00"); - BOOST_CHECK_EQUAL(DateTimeStrFormat("%Y-%m-%d %H:%M:%S", 0x7FFFFFFF), "2038-01-19 03:14:07"); - BOOST_CHECK_EQUAL(DateTimeStrFormat("%Y-%m-%d %H:%M:%S", 1317425777), "2011-09-30 23:36:17"); - BOOST_CHECK_EQUAL(DateTimeStrFormat("%Y-%m-%dT%H:%M:%SZ", 1317425777), "2011-09-30T23:36:17Z"); - BOOST_CHECK_EQUAL(DateTimeStrFormat("%H:%M:%SZ", 1317425777), "23:36:17Z"); - BOOST_CHECK_EQUAL(DateTimeStrFormat("%Y-%m-%d %H:%M", 1317425777), "2011-09-30 23:36"); - BOOST_CHECK_EQUAL(DateTimeStrFormat("%a, %d %b %Y %H:%M:%S +0000", 1317425777), "Fri, 30 Sep 2011 23:36:17 +0000"); -} - BOOST_AUTO_TEST_CASE(util_FormatISO8601DateTime) { BOOST_CHECK_EQUAL(FormatISO8601DateTime(1317425777), "2011-09-30T23:36:17Z"); @@ -187,9 +176,23 @@ BOOST_AUTO_TEST_CASE(util_FormatISO8601Time) struct TestArgsManager : public ArgsManager { - std::map<std::string, std::string>& GetMapArgs() { return mapArgs; } - const std::map<std::string, std::vector<std::string> >& GetMapMultiArgs() { return mapMultiArgs; } - const std::unordered_set<std::string>& GetNegatedArgs() { return m_negated_args; } + TestArgsManager() { m_network_only_args.clear(); } + std::map<std::string, std::vector<std::string> >& GetOverrideArgs() { return m_override_args; } + std::map<std::string, std::vector<std::string> >& GetConfigArgs() { return m_config_args; } + void ReadConfigString(const std::string str_config) + { + std::istringstream streamConfig(str_config); + { + LOCK(cs_args); + m_config_args.clear(); + } + ReadConfigStream(streamConfig); + } + void SetNetworkOnlyArg(const std::string arg) + { + LOCK(cs_args); + m_network_only_args.insert(arg); + } }; BOOST_AUTO_TEST_CASE(util_ParseParameters) @@ -198,22 +201,26 @@ BOOST_AUTO_TEST_CASE(util_ParseParameters) const char *argv_test[] = {"-ignored", "-a", "-b", "-ccc=argument", "-ccc=multiple", "f", "-d=e"}; testArgs.ParseParameters(0, (char**)argv_test); - BOOST_CHECK(testArgs.GetMapArgs().empty() && testArgs.GetMapMultiArgs().empty()); + BOOST_CHECK(testArgs.GetOverrideArgs().empty() && testArgs.GetConfigArgs().empty()); testArgs.ParseParameters(1, (char**)argv_test); - BOOST_CHECK(testArgs.GetMapArgs().empty() && testArgs.GetMapMultiArgs().empty()); + BOOST_CHECK(testArgs.GetOverrideArgs().empty() && testArgs.GetConfigArgs().empty()); testArgs.ParseParameters(7, (char**)argv_test); // expectation: -ignored is ignored (program name argument), // -a, -b and -ccc end up in map, -d ignored because it is after // a non-option argument (non-GNU option parsing) - BOOST_CHECK(testArgs.GetMapArgs().size() == 3 && testArgs.GetMapMultiArgs().size() == 3); + BOOST_CHECK(testArgs.GetOverrideArgs().size() == 3 && testArgs.GetConfigArgs().empty()); BOOST_CHECK(testArgs.IsArgSet("-a") && testArgs.IsArgSet("-b") && testArgs.IsArgSet("-ccc") && !testArgs.IsArgSet("f") && !testArgs.IsArgSet("-d")); - BOOST_CHECK(testArgs.GetMapMultiArgs().count("-a") && testArgs.GetMapMultiArgs().count("-b") && testArgs.GetMapMultiArgs().count("-ccc") - && !testArgs.GetMapMultiArgs().count("f") && !testArgs.GetMapMultiArgs().count("-d")); - - BOOST_CHECK(testArgs.GetMapArgs()["-a"] == "" && testArgs.GetMapArgs()["-ccc"] == "multiple"); + BOOST_CHECK(testArgs.GetOverrideArgs().count("-a") && testArgs.GetOverrideArgs().count("-b") && testArgs.GetOverrideArgs().count("-ccc") + && !testArgs.GetOverrideArgs().count("f") && !testArgs.GetOverrideArgs().count("-d")); + + BOOST_CHECK(testArgs.GetOverrideArgs()["-a"].size() == 1); + BOOST_CHECK(testArgs.GetOverrideArgs()["-a"].front() == ""); + BOOST_CHECK(testArgs.GetOverrideArgs()["-ccc"].size() == 2); + BOOST_CHECK(testArgs.GetOverrideArgs()["-ccc"].front() == "argument"); + BOOST_CHECK(testArgs.GetOverrideArgs()["-ccc"].back() == "multiple"); BOOST_CHECK(testArgs.GetArgs("-ccc").size() == 2); } @@ -229,15 +236,14 @@ BOOST_AUTO_TEST_CASE(util_GetBoolArg) BOOST_CHECK(testArgs.IsArgSet({'-', opt}) || !opt); // Nothing else should be in the map - BOOST_CHECK(testArgs.GetMapArgs().size() == 6 && - testArgs.GetMapMultiArgs().size() == 6); + BOOST_CHECK(testArgs.GetOverrideArgs().size() == 6 && + testArgs.GetConfigArgs().empty()); // The -no prefix should get stripped on the way in. BOOST_CHECK(!testArgs.IsArgSet("-nob")); // The -b option is flagged as negated, and nothing else is BOOST_CHECK(testArgs.IsArgNegated("-b")); - BOOST_CHECK(testArgs.GetNegatedArgs().size() == 1); BOOST_CHECK(!testArgs.IsArgNegated("-a")); // Check expected values. @@ -253,31 +259,253 @@ BOOST_AUTO_TEST_CASE(util_GetBoolArgEdgeCases) { // Test some awful edge cases that hopefully no user will ever exercise. TestArgsManager testArgs; + + // Params test const char *argv_test[] = {"ignored", "-nofoo", "-foo", "-nobar=0"}; testArgs.ParseParameters(4, (char**)argv_test); // This was passed twice, second one overrides the negative setting. BOOST_CHECK(!testArgs.IsArgNegated("-foo")); - BOOST_CHECK(testArgs.GetBoolArg("-foo", false) == true); + BOOST_CHECK(testArgs.GetArg("-foo", "xxx") == ""); + + // A double negative is a positive, and not marked as negated. + BOOST_CHECK(!testArgs.IsArgNegated("-bar")); + BOOST_CHECK(testArgs.GetArg("-bar", "xxx") == "1"); - // A double negative is a positive. - BOOST_CHECK(testArgs.IsArgNegated("-bar")); - BOOST_CHECK(testArgs.GetBoolArg("-bar", false) == true); + // Config test + const char *conf_test = "nofoo=1\nfoo=1\nnobar=0\n"; + testArgs.ParseParameters(1, (char**)argv_test); + testArgs.ReadConfigString(conf_test); + + // This was passed twice, second one overrides the negative setting, + // and the value. + BOOST_CHECK(!testArgs.IsArgNegated("-foo")); + BOOST_CHECK(testArgs.GetArg("-foo", "xxx") == "1"); + + // A double negative is a positive, and does not count as negated. + BOOST_CHECK(!testArgs.IsArgNegated("-bar")); + BOOST_CHECK(testArgs.GetArg("-bar", "xxx") == "1"); + + // Combined test + const char *combo_test_args[] = {"ignored", "-nofoo", "-bar"}; + const char *combo_test_conf = "foo=1\nnobar=1\n"; + testArgs.ParseParameters(3, (char**)combo_test_args); + testArgs.ReadConfigString(combo_test_conf); + + // Command line overrides, but doesn't erase old setting + BOOST_CHECK(testArgs.IsArgNegated("-foo")); + BOOST_CHECK(testArgs.GetArg("-foo", "xxx") == "0"); + BOOST_CHECK(testArgs.GetArgs("-foo").size() == 0); + + // Command line overrides, but doesn't erase old setting + BOOST_CHECK(!testArgs.IsArgNegated("-bar")); + BOOST_CHECK(testArgs.GetArg("-bar", "xxx") == ""); + BOOST_CHECK(testArgs.GetArgs("-bar").size() == 1 + && testArgs.GetArgs("-bar").front() == ""); +} + +BOOST_AUTO_TEST_CASE(util_ReadConfigStream) +{ + const char *str_config = + "a=\n" + "b=1\n" + "ccc=argument\n" + "ccc=multiple\n" + "d=e\n" + "nofff=1\n" + "noggg=0\n" + "h=1\n" + "noh=1\n" + "noi=1\n" + "i=1\n" + "sec1.ccc=extend1\n" + "\n" + "[sec1]\n" + "ccc=extend2\n" + "d=eee\n" + "h=1\n" + "[sec2]\n" + "ccc=extend3\n" + "iii=2\n"; + + TestArgsManager test_args; + + test_args.ReadConfigString(str_config); + // expectation: a, b, ccc, d, fff, ggg, h, i end up in map + // so do sec1.ccc, sec1.d, sec1.h, sec2.ccc, sec2.iii + + BOOST_CHECK(test_args.GetOverrideArgs().empty()); + BOOST_CHECK(test_args.GetConfigArgs().size() == 13); + + BOOST_CHECK(test_args.GetConfigArgs().count("-a") + && test_args.GetConfigArgs().count("-b") + && test_args.GetConfigArgs().count("-ccc") + && test_args.GetConfigArgs().count("-d") + && test_args.GetConfigArgs().count("-fff") + && test_args.GetConfigArgs().count("-ggg") + && test_args.GetConfigArgs().count("-h") + && test_args.GetConfigArgs().count("-i") + ); + BOOST_CHECK(test_args.GetConfigArgs().count("-sec1.ccc") + && test_args.GetConfigArgs().count("-sec1.h") + && test_args.GetConfigArgs().count("-sec2.ccc") + && test_args.GetConfigArgs().count("-sec2.iii") + ); + + BOOST_CHECK(test_args.IsArgSet("-a") + && test_args.IsArgSet("-b") + && test_args.IsArgSet("-ccc") + && test_args.IsArgSet("-d") + && test_args.IsArgSet("-fff") + && test_args.IsArgSet("-ggg") + && test_args.IsArgSet("-h") + && test_args.IsArgSet("-i") + && !test_args.IsArgSet("-zzz") + && !test_args.IsArgSet("-iii") + ); + + BOOST_CHECK(test_args.GetArg("-a", "xxx") == "" + && test_args.GetArg("-b", "xxx") == "1" + && test_args.GetArg("-ccc", "xxx") == "argument" + && test_args.GetArg("-d", "xxx") == "e" + && test_args.GetArg("-fff", "xxx") == "0" + && test_args.GetArg("-ggg", "xxx") == "1" + && test_args.GetArg("-h", "xxx") == "0" + && test_args.GetArg("-i", "xxx") == "1" + && test_args.GetArg("-zzz", "xxx") == "xxx" + && test_args.GetArg("-iii", "xxx") == "xxx" + ); + + for (bool def : {false, true}) { + BOOST_CHECK(test_args.GetBoolArg("-a", def) + && test_args.GetBoolArg("-b", def) + && !test_args.GetBoolArg("-ccc", def) + && !test_args.GetBoolArg("-d", def) + && !test_args.GetBoolArg("-fff", def) + && test_args.GetBoolArg("-ggg", def) + && !test_args.GetBoolArg("-h", def) + && test_args.GetBoolArg("-i", def) + && test_args.GetBoolArg("-zzz", def) == def + && test_args.GetBoolArg("-iii", def) == def + ); + } + + BOOST_CHECK(test_args.GetArgs("-a").size() == 1 + && test_args.GetArgs("-a").front() == ""); + BOOST_CHECK(test_args.GetArgs("-b").size() == 1 + && test_args.GetArgs("-b").front() == "1"); + BOOST_CHECK(test_args.GetArgs("-ccc").size() == 2 + && test_args.GetArgs("-ccc").front() == "argument" + && test_args.GetArgs("-ccc").back() == "multiple"); + BOOST_CHECK(test_args.GetArgs("-fff").size() == 0); + BOOST_CHECK(test_args.GetArgs("-nofff").size() == 0); + BOOST_CHECK(test_args.GetArgs("-ggg").size() == 1 + && test_args.GetArgs("-ggg").front() == "1"); + BOOST_CHECK(test_args.GetArgs("-noggg").size() == 0); + BOOST_CHECK(test_args.GetArgs("-h").size() == 0); + BOOST_CHECK(test_args.GetArgs("-noh").size() == 0); + BOOST_CHECK(test_args.GetArgs("-i").size() == 1 + && test_args.GetArgs("-i").front() == "1"); + BOOST_CHECK(test_args.GetArgs("-noi").size() == 0); + BOOST_CHECK(test_args.GetArgs("-zzz").size() == 0); + + BOOST_CHECK(!test_args.IsArgNegated("-a")); + BOOST_CHECK(!test_args.IsArgNegated("-b")); + BOOST_CHECK(!test_args.IsArgNegated("-ccc")); + BOOST_CHECK(!test_args.IsArgNegated("-d")); + BOOST_CHECK(test_args.IsArgNegated("-fff")); + BOOST_CHECK(!test_args.IsArgNegated("-ggg")); + BOOST_CHECK(test_args.IsArgNegated("-h")); // last setting takes precedence + BOOST_CHECK(!test_args.IsArgNegated("-i")); // last setting takes precedence + BOOST_CHECK(!test_args.IsArgNegated("-zzz")); + + // Test sections work + test_args.SelectConfigNetwork("sec1"); + + // same as original + BOOST_CHECK(test_args.GetArg("-a", "xxx") == "" + && test_args.GetArg("-b", "xxx") == "1" + && test_args.GetArg("-fff", "xxx") == "0" + && test_args.GetArg("-ggg", "xxx") == "1" + && test_args.GetArg("-zzz", "xxx") == "xxx" + && test_args.GetArg("-iii", "xxx") == "xxx" + ); + // d is overridden + BOOST_CHECK(test_args.GetArg("-d", "xxx") == "eee"); + // section-specific setting + BOOST_CHECK(test_args.GetArg("-h", "xxx") == "1"); + // section takes priority for multiple values + BOOST_CHECK(test_args.GetArg("-ccc", "xxx") == "extend1"); + // check multiple values works + const std::vector<std::string> sec1_ccc_expected = {"extend1","extend2","argument","multiple"}; + const auto& sec1_ccc_res = test_args.GetArgs("-ccc"); + BOOST_CHECK_EQUAL_COLLECTIONS(sec1_ccc_res.begin(), sec1_ccc_res.end(), sec1_ccc_expected.begin(), sec1_ccc_expected.end()); + + test_args.SelectConfigNetwork("sec2"); + + // same as original + BOOST_CHECK(test_args.GetArg("-a", "xxx") == "" + && test_args.GetArg("-b", "xxx") == "1" + && test_args.GetArg("-d", "xxx") == "e" + && test_args.GetArg("-fff", "xxx") == "0" + && test_args.GetArg("-ggg", "xxx") == "1" + && test_args.GetArg("-zzz", "xxx") == "xxx" + && test_args.GetArg("-h", "xxx") == "0" + ); + // section-specific setting + BOOST_CHECK(test_args.GetArg("-iii", "xxx") == "2"); + // section takes priority for multiple values + BOOST_CHECK(test_args.GetArg("-ccc", "xxx") == "extend3"); + // check multiple values works + const std::vector<std::string> sec2_ccc_expected = {"extend3","argument","multiple"}; + const auto& sec2_ccc_res = test_args.GetArgs("-ccc"); + BOOST_CHECK_EQUAL_COLLECTIONS(sec2_ccc_res.begin(), sec2_ccc_res.end(), sec2_ccc_expected.begin(), sec2_ccc_expected.end()); + + // Test section only options + + test_args.SetNetworkOnlyArg("-d"); + test_args.SetNetworkOnlyArg("-ccc"); + test_args.SetNetworkOnlyArg("-h"); + + test_args.SelectConfigNetwork(CBaseChainParams::MAIN); + BOOST_CHECK(test_args.GetArg("-d", "xxx") == "e"); + BOOST_CHECK(test_args.GetArgs("-ccc").size() == 2); + BOOST_CHECK(test_args.GetArg("-h", "xxx") == "0"); + + test_args.SelectConfigNetwork("sec1"); + BOOST_CHECK(test_args.GetArg("-d", "xxx") == "eee"); + BOOST_CHECK(test_args.GetArgs("-d").size() == 1); + BOOST_CHECK(test_args.GetArgs("-ccc").size() == 2); + BOOST_CHECK(test_args.GetArg("-h", "xxx") == "1"); + + test_args.SelectConfigNetwork("sec2"); + BOOST_CHECK(test_args.GetArg("-d", "xxx") == "xxx"); + BOOST_CHECK(test_args.GetArgs("-d").size() == 0); + BOOST_CHECK(test_args.GetArgs("-ccc").size() == 1); + BOOST_CHECK(test_args.GetArg("-h", "xxx") == "0"); } BOOST_AUTO_TEST_CASE(util_GetArg) { TestArgsManager testArgs; - testArgs.GetMapArgs().clear(); - testArgs.GetMapArgs()["strtest1"] = "string..."; + testArgs.GetOverrideArgs().clear(); + testArgs.GetOverrideArgs()["strtest1"] = {"string..."}; // strtest2 undefined on purpose - testArgs.GetMapArgs()["inttest1"] = "12345"; - testArgs.GetMapArgs()["inttest2"] = "81985529216486895"; + testArgs.GetOverrideArgs()["inttest1"] = {"12345"}; + testArgs.GetOverrideArgs()["inttest2"] = {"81985529216486895"}; // inttest3 undefined on purpose - testArgs.GetMapArgs()["booltest1"] = ""; + testArgs.GetOverrideArgs()["booltest1"] = {""}; // booltest2 undefined on purpose - testArgs.GetMapArgs()["booltest3"] = "0"; - testArgs.GetMapArgs()["booltest4"] = "1"; + testArgs.GetOverrideArgs()["booltest3"] = {"0"}; + testArgs.GetOverrideArgs()["booltest4"] = {"1"}; + + // priorities + testArgs.GetOverrideArgs()["pritest1"] = {"a", "b"}; + testArgs.GetConfigArgs()["pritest2"] = {"a", "b"}; + testArgs.GetOverrideArgs()["pritest3"] = {"a"}; + testArgs.GetConfigArgs()["pritest3"] = {"b"}; + testArgs.GetOverrideArgs()["pritest4"] = {"a","b"}; + testArgs.GetConfigArgs()["pritest4"] = {"c","d"}; BOOST_CHECK_EQUAL(testArgs.GetArg("strtest1", "default"), "string..."); BOOST_CHECK_EQUAL(testArgs.GetArg("strtest2", "default"), "default"); @@ -288,6 +516,84 @@ BOOST_AUTO_TEST_CASE(util_GetArg) BOOST_CHECK_EQUAL(testArgs.GetBoolArg("booltest2", false), false); BOOST_CHECK_EQUAL(testArgs.GetBoolArg("booltest3", false), false); BOOST_CHECK_EQUAL(testArgs.GetBoolArg("booltest4", false), true); + + BOOST_CHECK_EQUAL(testArgs.GetArg("pritest1", "default"), "b"); + BOOST_CHECK_EQUAL(testArgs.GetArg("pritest2", "default"), "a"); + BOOST_CHECK_EQUAL(testArgs.GetArg("pritest3", "default"), "a"); + BOOST_CHECK_EQUAL(testArgs.GetArg("pritest4", "default"), "b"); +} + +BOOST_AUTO_TEST_CASE(util_GetChainName) +{ + TestArgsManager test_args; + + const char* argv_testnet[] = {"cmd", "-testnet"}; + const char* argv_regtest[] = {"cmd", "-regtest"}; + const char* argv_test_no_reg[] = {"cmd", "-testnet", "-noregtest"}; + const char* argv_both[] = {"cmd", "-testnet", "-regtest"}; + + // equivalent to "-testnet" + // regtest in testnet section is ignored + const char* testnetconf = "testnet=1\nregtest=0\n[test]\nregtest=1"; + + test_args.ParseParameters(0, (char**)argv_testnet); + BOOST_CHECK_EQUAL(test_args.GetChainName(), "main"); + + test_args.ParseParameters(2, (char**)argv_testnet); + BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); + + test_args.ParseParameters(2, (char**)argv_regtest); + BOOST_CHECK_EQUAL(test_args.GetChainName(), "regtest"); + + test_args.ParseParameters(3, (char**)argv_test_no_reg); + BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); + + test_args.ParseParameters(3, (char**)argv_both); + BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error); + + test_args.ParseParameters(0, (char**)argv_testnet); + test_args.ReadConfigString(testnetconf); + BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); + + test_args.ParseParameters(2, (char**)argv_testnet); + test_args.ReadConfigString(testnetconf); + BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); + + test_args.ParseParameters(2, (char**)argv_regtest); + test_args.ReadConfigString(testnetconf); + BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error); + + test_args.ParseParameters(3, (char**)argv_test_no_reg); + test_args.ReadConfigString(testnetconf); + BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); + + test_args.ParseParameters(3, (char**)argv_both); + test_args.ReadConfigString(testnetconf); + BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error); + + // check setting the network to test (and thus making + // [test] regtest=1 potentially relevant) doesn't break things + test_args.SelectConfigNetwork("test"); + + test_args.ParseParameters(0, (char**)argv_testnet); + test_args.ReadConfigString(testnetconf); + BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); + + test_args.ParseParameters(2, (char**)argv_testnet); + test_args.ReadConfigString(testnetconf); + BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); + + test_args.ParseParameters(2, (char**)argv_regtest); + test_args.ReadConfigString(testnetconf); + BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error); + + test_args.ParseParameters(2, (char**)argv_test_no_reg); + test_args.ReadConfigString(testnetconf); + BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); + + test_args.ParseParameters(3, (char**)argv_both); + test_args.ReadConfigString(testnetconf); + BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error); } BOOST_AUTO_TEST_CASE(util_FormatMoney) diff --git a/src/threadinterrupt.cpp b/src/threadinterrupt.cpp index 5d932091cb..7da4e136ef 100644 --- a/src/threadinterrupt.cpp +++ b/src/threadinterrupt.cpp @@ -5,6 +5,8 @@ #include <threadinterrupt.h> +CThreadInterrupt::CThreadInterrupt() : flag(false) {} + CThreadInterrupt::operator bool() const { return flag.load(std::memory_order_acquire); diff --git a/src/threadinterrupt.h b/src/threadinterrupt.h index 54e3102808..d373e3c371 100644 --- a/src/threadinterrupt.h +++ b/src/threadinterrupt.h @@ -18,6 +18,7 @@ class CThreadInterrupt { public: + CThreadInterrupt(); explicit operator bool() const; void operator()(); void reset(); diff --git a/src/timedata.cpp b/src/timedata.cpp index a803b2fc87..27d08172f5 100644 --- a/src/timedata.cpp +++ b/src/timedata.cpp @@ -110,9 +110,9 @@ void AddTimeData(const CNetAddr& ip, int64_t nOffsetSample) if (LogAcceptCategory(BCLog::NET)) { for (int64_t n : vSorted) { - LogPrint(BCLog::NET, "%+d ", n); + LogPrint(BCLog::NET, "%+d ", n); /* Continued */ } - LogPrint(BCLog::NET, "| "); + LogPrint(BCLog::NET, "| "); /* Continued */ LogPrint(BCLog::NET, "nTimeOffset = %+d (%+d minutes)\n", nTimeOffset, nTimeOffset/60); } diff --git a/src/txdb.cpp b/src/txdb.cpp index 8550a7e889..333d3596c1 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -22,6 +22,7 @@ static const char DB_COIN = 'C'; static const char DB_COINS = 'c'; static const char DB_BLOCK_FILES = 'f'; static const char DB_TXINDEX = 't'; +static const char DB_TXINDEX_BLOCK = 'T'; static const char DB_BLOCK_INDEX = 'b'; static const char DB_BEST_BLOCK = 'B'; @@ -370,7 +371,7 @@ bool CCoinsViewDB::Upgrade() { int64_t count = 0; LogPrintf("Upgrading utxo-set database...\n"); - LogPrintf("[0%%]..."); + LogPrintf("[0%%]..."); /* Continued */ uiInterface.ShowProgress(_("Upgrading UTXO database"), 0, true); size_t batch_size = 1 << 24; CDBBatch batch(db); @@ -389,7 +390,7 @@ bool CCoinsViewDB::Upgrade() { uiInterface.ShowProgress(_("Upgrading UTXO database"), percentageDone, true); if (reportDone < percentageDone/10) { // report max. every 10% step - LogPrintf("[%d%%]...", percentageDone); + LogPrintf("[%d%%]...", percentageDone); /* Continued */ reportDone = percentageDone/10; } } @@ -424,3 +425,173 @@ bool CCoinsViewDB::Upgrade() { LogPrintf("[%s].\n", ShutdownRequested() ? "CANCELLED" : "DONE"); return !ShutdownRequested(); } + +TxIndexDB::TxIndexDB(size_t n_cache_size, bool f_memory, bool f_wipe) : + CDBWrapper(GetDataDir() / "indexes" / "txindex", n_cache_size, f_memory, f_wipe) +{} + +bool TxIndexDB::ReadTxPos(const uint256 &txid, CDiskTxPos& pos) const +{ + return Read(std::make_pair(DB_TXINDEX, txid), pos); +} + +bool TxIndexDB::WriteTxs(const std::vector<std::pair<uint256, CDiskTxPos>>& v_pos) +{ + CDBBatch batch(*this); + for (const auto& tuple : v_pos) { + batch.Write(std::make_pair(DB_TXINDEX, tuple.first), tuple.second); + } + return WriteBatch(batch); +} + +bool TxIndexDB::ReadBestBlock(CBlockLocator& locator) const +{ + bool success = Read(DB_BEST_BLOCK, locator); + if (!success) { + locator.SetNull(); + } + return success; +} + +bool TxIndexDB::WriteBestBlock(const CBlockLocator& locator) +{ + return Write(DB_BEST_BLOCK, locator); +} + +/* + * Safely persist a transfer of data from the old txindex database to the new one, and compact the + * range of keys updated. This is used internally by MigrateData. + */ +static void WriteTxIndexMigrationBatches(TxIndexDB& newdb, CBlockTreeDB& olddb, + CDBBatch& batch_newdb, CDBBatch& batch_olddb, + const std::pair<unsigned char, uint256>& begin_key, + const std::pair<unsigned char, uint256>& end_key) +{ + // Sync new DB changes to disk before deleting from old DB. + newdb.WriteBatch(batch_newdb, /*fSync=*/ true); + olddb.WriteBatch(batch_olddb); + olddb.CompactRange(begin_key, end_key); + + batch_newdb.Clear(); + batch_olddb.Clear(); +} + +bool TxIndexDB::MigrateData(CBlockTreeDB& block_tree_db, const CBlockLocator& best_locator) +{ + // The prior implementation of txindex was always in sync with block index + // and presence was indicated with a boolean DB flag. If the flag is set, + // this means the txindex from a previous version is valid and in sync with + // the chain tip. The first step of the migration is to unset the flag and + // write the chain hash to a separate key, DB_TXINDEX_BLOCK. After that, the + // index entries are copied over in batches to the new database. Finally, + // DB_TXINDEX_BLOCK is erased from the old database and the block hash is + // written to the new database. + // + // Unsetting the boolean flag ensures that if the node is downgraded to a + // previous version, it will not see a corrupted, partially migrated index + // -- it will see that the txindex is disabled. When the node is upgraded + // again, the migration will pick up where it left off and sync to the block + // with hash DB_TXINDEX_BLOCK. + bool f_legacy_flag = false; + block_tree_db.ReadFlag("txindex", f_legacy_flag); + if (f_legacy_flag) { + if (!block_tree_db.Write(DB_TXINDEX_BLOCK, best_locator)) { + return error("%s: cannot write block indicator", __func__); + } + if (!block_tree_db.WriteFlag("txindex", false)) { + return error("%s: cannot write block index db flag", __func__); + } + } + + CBlockLocator locator; + if (!block_tree_db.Read(DB_TXINDEX_BLOCK, locator)) { + return true; + } + + int64_t count = 0; + LogPrintf("Upgrading txindex database... [0%%]\n"); + uiInterface.ShowProgress(_("Upgrading txindex database"), 0, true); + int report_done = 0; + const size_t batch_size = 1 << 24; // 16 MiB + + CDBBatch batch_newdb(*this); + CDBBatch batch_olddb(block_tree_db); + + std::pair<unsigned char, uint256> key; + std::pair<unsigned char, uint256> begin_key{DB_TXINDEX, uint256()}; + std::pair<unsigned char, uint256> prev_key = begin_key; + + bool interrupted = false; + std::unique_ptr<CDBIterator> cursor(block_tree_db.NewIterator()); + for (cursor->Seek(begin_key); cursor->Valid(); cursor->Next()) { + boost::this_thread::interruption_point(); + if (ShutdownRequested()) { + interrupted = true; + break; + } + + if (!cursor->GetKey(key)) { + return error("%s: cannot get key from valid cursor", __func__); + } + if (key.first != DB_TXINDEX) { + break; + } + + // Log progress every 10%. + if (++count % 256 == 0) { + // Since txids are uniformly random and traversed in increasing order, the high 16 bits + // of the hash can be used to estimate the current progress. + const uint256& txid = key.second; + uint32_t high_nibble = + (static_cast<uint32_t>(*(txid.begin() + 0)) << 8) + + (static_cast<uint32_t>(*(txid.begin() + 1)) << 0); + int percentage_done = (int)(high_nibble * 100.0 / 65536.0 + 0.5); + + uiInterface.ShowProgress(_("Upgrading txindex database"), percentage_done, true); + if (report_done < percentage_done/10) { + LogPrintf("Upgrading txindex database... [%d%%]\n", percentage_done); + report_done = percentage_done/10; + } + } + + CDiskTxPos value; + if (!cursor->GetValue(value)) { + return error("%s: cannot parse txindex record", __func__); + } + batch_newdb.Write(key, value); + batch_olddb.Erase(key); + + if (batch_newdb.SizeEstimate() > batch_size || batch_olddb.SizeEstimate() > batch_size) { + // NOTE: it's OK to delete the key pointed at by the current DB cursor while iterating + // because LevelDB iterators are guaranteed to provide a consistent view of the + // underlying data, like a lightweight snapshot. + WriteTxIndexMigrationBatches(*this, block_tree_db, + batch_newdb, batch_olddb, + prev_key, key); + prev_key = key; + } + } + + // If these final DB batches complete the migration, write the best block + // hash marker to the new database and delete from the old one. This signals + // that the former is fully caught up to that point in the blockchain and + // that all txindex entries have been removed from the latter. + if (!interrupted) { + batch_olddb.Erase(DB_TXINDEX_BLOCK); + batch_newdb.Write(DB_BEST_BLOCK, locator); + } + + WriteTxIndexMigrationBatches(*this, block_tree_db, + batch_newdb, batch_olddb, + begin_key, key); + + if (interrupted) { + LogPrintf("[CANCELLED].\n"); + return false; + } + + uiInterface.ShowProgress("", 100, false); + + LogPrintf("[DONE].\n"); + return true; +} diff --git a/src/txdb.h b/src/txdb.h index ad76b3257d..4193f98de1 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -9,6 +9,7 @@ #include <coins.h> #include <dbwrapper.h> #include <chain.h> +#include <primitives/block.h> #include <map> #include <memory> @@ -35,7 +36,7 @@ static const int64_t nMaxBlockDBCache = 2; //! Max memory allocated to block tree DB specific cache, if -txindex (MiB) // Unlike for the UTXO database, for the txindex scenario the leveldb cache make // a meaningful difference: https://github.com/bitcoin/bitcoin/pull/8273#issuecomment-229601991 -static const int64_t nMaxBlockDBAndTxIndexCache = 1024; +static const int64_t nMaxTxIndexCache = 1024; //! Max memory allocated to coin DB specific cache (MiB) static const int64_t nMaxCoinsDBCache = 8; @@ -47,7 +48,7 @@ struct CDiskTxPos : public CDiskBlockPos template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(*static_cast<CDiskBlockPos*>(this)); + READWRITEAS(CDiskBlockPos, *this); READWRITE(VARINT(nTxOffset)); } @@ -112,9 +113,6 @@ class CBlockTreeDB : public CDBWrapper public: explicit CBlockTreeDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false); - CBlockTreeDB(const CBlockTreeDB&) = delete; - CBlockTreeDB& operator=(const CBlockTreeDB&) = delete; - bool WriteBatchSync(const std::vector<std::pair<int, const CBlockFileInfo*> >& fileInfo, int nLastFile, const std::vector<const CBlockIndex*>& blockinfo); bool ReadBlockFileInfo(int nFile, CBlockFileInfo &info); bool ReadLastBlockFile(int &nFile); @@ -127,4 +125,36 @@ public: bool LoadBlockIndexGuts(const Consensus::Params& consensusParams, std::function<CBlockIndex*(const uint256&)> insertBlockIndex); }; +/** + * Access to the txindex database (indexes/txindex/) + * + * The database stores a block locator of the chain the database is synced to + * so that the TxIndex can efficiently determine the point it last stopped at. + * A locator is used instead of a simple hash of the chain tip because blocks + * and block index entries may not be flushed to disk until after this database + * is updated. + */ +class TxIndexDB : public CDBWrapper +{ +public: + explicit TxIndexDB(size_t n_cache_size, bool f_memory = false, bool f_wipe = false); + + /// Read the disk location of the transaction data with the given hash. Returns false if the + /// transaction hash is not indexed. + bool ReadTxPos(const uint256& txid, CDiskTxPos& pos) const; + + /// Write a batch of transaction positions to the DB. + bool WriteTxs(const std::vector<std::pair<uint256, CDiskTxPos>>& v_pos); + + /// Read block locator of the chain that the txindex is in sync with. + bool ReadBestBlock(CBlockLocator& locator) const; + + /// Write block locator of the chain that the txindex is in sync with. + bool WriteBestBlock(const CBlockLocator& locator); + + /// Migrate txindex data from the block tree DB, where it may be for older nodes that have not + /// been upgraded yet to the new database. + bool MigrateData(CBlockTreeDB& block_tree_db, const CBlockLocator& best_locator); +}; + #endif // BITCOIN_TXDB_H diff --git a/src/util.cpp b/src/util.cpp index 46054f5025..9a3067259f 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -31,6 +31,7 @@ #include <algorithm> #include <fcntl.h> +#include <sched.h> #include <sys/resource.h> #include <sys/stat.h> @@ -83,21 +84,11 @@ const int64_t nStartupTime = GetTime(); const char * const BITCOIN_CONF_FILENAME = "bitcoin.conf"; const char * const BITCOIN_PID_FILENAME = "bitcoind.pid"; -const char * const DEFAULT_DEBUGLOGFILE = "debug.log"; ArgsManager gArgs; -bool fPrintToConsole = false; -bool fPrintToDebugLog = true; -bool fLogTimestamps = DEFAULT_LOGTIMESTAMPS; -bool fLogTimeMicros = DEFAULT_LOGTIMEMICROS; -bool fLogIPs = DEFAULT_LOGIPS; -std::atomic<bool> fReopenDebugLog(false); CTranslationInterface translationInterface; -/** Log categories bitfield. */ -std::atomic<uint32_t> logCategories(0); - /** Init OpenSSL library multithreading support */ static std::unique_ptr<CCriticalSection[]> ppmutexOpenSSL; void locking_callback(int mode, int i, const char* file, int line) NO_THREAD_SAFETY_ANALYSIS @@ -146,233 +137,6 @@ public: } instance_of_cinit; -/** - * LogPrintf() has been broken a couple of times now - * by well-meaning people adding mutexes in the most straightforward way. - * It breaks because it may be called by global destructors during shutdown. - * Since the order of destruction of static/global objects is undefined, - * defining a mutex as a global object doesn't work (the mutex gets - * destroyed, and then some later destructor calls OutputDebugStringF, - * maybe indirectly, and you get a core dump at shutdown trying to lock - * the mutex). - */ - -static std::once_flag debugPrintInitFlag; - -/** - * We use std::call_once() to make sure mutexDebugLog and - * vMsgsBeforeOpenLog are initialized in a thread-safe manner. - * - * NOTE: fileout, mutexDebugLog and sometimes vMsgsBeforeOpenLog - * are leaked on exit. This is ugly, but will be cleaned up by - * the OS/libc. When the shutdown sequence is fully audited and - * tested, explicit destruction of these objects can be implemented. - */ -static FILE* fileout = nullptr; -static std::mutex* mutexDebugLog = nullptr; -static std::list<std::string>* vMsgsBeforeOpenLog; - -static int FileWriteStr(const std::string &str, FILE *fp) -{ - return fwrite(str.data(), 1, str.size(), fp); -} - -static void DebugPrintInit() -{ - assert(mutexDebugLog == nullptr); - mutexDebugLog = new std::mutex(); - vMsgsBeforeOpenLog = new std::list<std::string>; -} - -fs::path GetDebugLogPath() -{ - fs::path logfile(gArgs.GetArg("-debuglogfile", DEFAULT_DEBUGLOGFILE)); - return AbsPathForConfigVal(logfile); -} - -bool OpenDebugLog() -{ - std::call_once(debugPrintInitFlag, &DebugPrintInit); - std::lock_guard<std::mutex> scoped_lock(*mutexDebugLog); - - assert(fileout == nullptr); - assert(vMsgsBeforeOpenLog); - fs::path pathDebug = GetDebugLogPath(); - - fileout = fsbridge::fopen(pathDebug, "a"); - if (!fileout) { - return false; - } - - setbuf(fileout, nullptr); // unbuffered - // dump buffered messages from before we opened the log - while (!vMsgsBeforeOpenLog->empty()) { - FileWriteStr(vMsgsBeforeOpenLog->front(), fileout); - vMsgsBeforeOpenLog->pop_front(); - } - - delete vMsgsBeforeOpenLog; - vMsgsBeforeOpenLog = nullptr; - return true; -} - -struct CLogCategoryDesc -{ - uint32_t flag; - std::string category; -}; - -const CLogCategoryDesc LogCategories[] = -{ - {BCLog::NONE, "0"}, - {BCLog::NONE, "none"}, - {BCLog::NET, "net"}, - {BCLog::TOR, "tor"}, - {BCLog::MEMPOOL, "mempool"}, - {BCLog::HTTP, "http"}, - {BCLog::BENCH, "bench"}, - {BCLog::ZMQ, "zmq"}, - {BCLog::DB, "db"}, - {BCLog::RPC, "rpc"}, - {BCLog::ESTIMATEFEE, "estimatefee"}, - {BCLog::ADDRMAN, "addrman"}, - {BCLog::SELECTCOINS, "selectcoins"}, - {BCLog::REINDEX, "reindex"}, - {BCLog::CMPCTBLOCK, "cmpctblock"}, - {BCLog::RAND, "rand"}, - {BCLog::PRUNE, "prune"}, - {BCLog::PROXY, "proxy"}, - {BCLog::MEMPOOLREJ, "mempoolrej"}, - {BCLog::LIBEVENT, "libevent"}, - {BCLog::COINDB, "coindb"}, - {BCLog::QT, "qt"}, - {BCLog::LEVELDB, "leveldb"}, - {BCLog::ALL, "1"}, - {BCLog::ALL, "all"}, -}; - -bool GetLogCategory(uint32_t *f, const std::string *str) -{ - if (f && str) { - if (*str == "") { - *f = BCLog::ALL; - return true; - } - for (unsigned int i = 0; i < ARRAYLEN(LogCategories); i++) { - if (LogCategories[i].category == *str) { - *f = LogCategories[i].flag; - return true; - } - } - } - return false; -} - -std::string ListLogCategories() -{ - std::string ret; - int outcount = 0; - for (unsigned int i = 0; i < ARRAYLEN(LogCategories); i++) { - // Omit the special cases. - if (LogCategories[i].flag != BCLog::NONE && LogCategories[i].flag != BCLog::ALL) { - if (outcount != 0) ret += ", "; - ret += LogCategories[i].category; - outcount++; - } - } - return ret; -} - -std::vector<CLogCategoryActive> ListActiveLogCategories() -{ - std::vector<CLogCategoryActive> ret; - for (unsigned int i = 0; i < ARRAYLEN(LogCategories); i++) { - // Omit the special cases. - if (LogCategories[i].flag != BCLog::NONE && LogCategories[i].flag != BCLog::ALL) { - CLogCategoryActive catActive; - catActive.category = LogCategories[i].category; - catActive.active = LogAcceptCategory(LogCategories[i].flag); - ret.push_back(catActive); - } - } - return ret; -} - -/** - * fStartedNewLine is a state variable held by the calling context that will - * suppress printing of the timestamp when multiple calls are made that don't - * end in a newline. Initialize it to true, and hold it, in the calling context. - */ -static std::string LogTimestampStr(const std::string &str, std::atomic_bool *fStartedNewLine) -{ - std::string strStamped; - - if (!fLogTimestamps) - return str; - - if (*fStartedNewLine) { - int64_t nTimeMicros = GetTimeMicros(); - strStamped = FormatISO8601DateTime(nTimeMicros/1000000); - if (fLogTimeMicros) { - strStamped.pop_back(); - strStamped += strprintf(".%06dZ", nTimeMicros%1000000); - } - int64_t mocktime = GetMockTime(); - if (mocktime) { - strStamped += " (mocktime: " + FormatISO8601DateTime(mocktime) + ")"; - } - strStamped += ' ' + str; - } else - strStamped = str; - - if (!str.empty() && str[str.size()-1] == '\n') - *fStartedNewLine = true; - else - *fStartedNewLine = false; - - return strStamped; -} - -int LogPrintStr(const std::string &str) -{ - int ret = 0; // Returns total number of characters written - static std::atomic_bool fStartedNewLine(true); - - std::string strTimestamped = LogTimestampStr(str, &fStartedNewLine); - - if (fPrintToConsole) - { - // print to console - ret = fwrite(strTimestamped.data(), 1, strTimestamped.size(), stdout); - fflush(stdout); - } - else if (fPrintToDebugLog) - { - std::call_once(debugPrintInitFlag, &DebugPrintInit); - std::lock_guard<std::mutex> scoped_lock(*mutexDebugLog); - - // buffer if we haven't opened the log yet - if (fileout == nullptr) { - assert(vMsgsBeforeOpenLog); - ret = strTimestamped.length(); - vMsgsBeforeOpenLog->push_back(strTimestamped); - } - else - { - // reopen the log file, if requested - if (fReopenDebugLog) { - fReopenDebugLog = false; - fs::path pathDebug = GetDebugLogPath(); - if (fsbridge::freopen(pathDebug,"a",fileout) != nullptr) - setbuf(fileout, nullptr); // unbuffered - } - - ret = FileWriteStr(strTimestamped, fileout); - } - } - return ret; -} - /** A map that contains all the currently held directory locks. After * successful locking, these will be held here until the global destructor * cleans them up and thus automatically unlocks them, or ReleaseDirectoryLocks @@ -454,39 +218,204 @@ static bool InterpretBool(const std::string& strValue) return (atoi(strValue) != 0); } +/** Internal helper functions for ArgsManager */ +class ArgsManagerHelper { +public: + typedef std::map<std::string, std::vector<std::string>> MapArgs; + + /** Determine whether to use config settings in the default section, + * See also comments around ArgsManager::ArgsManager() below. */ + static inline bool UseDefaultSection(const ArgsManager& am, const std::string& arg) + { + return (am.m_network == CBaseChainParams::MAIN || am.m_network_only_args.count(arg) == 0); + } + + /** Convert regular argument into the network-specific setting */ + static inline std::string NetworkArg(const ArgsManager& am, const std::string& arg) + { + assert(arg.length() > 1 && arg[0] == '-'); + return "-" + am.m_network + "." + arg.substr(1); + } + + /** Find arguments in a map and add them to a vector */ + static inline void AddArgs(std::vector<std::string>& res, const MapArgs& map_args, const std::string& arg) + { + auto it = map_args.find(arg); + if (it != map_args.end()) { + res.insert(res.end(), it->second.begin(), it->second.end()); + } + } + + /** Return true/false if an argument is set in a map, and also + * return the first (or last) of the possibly multiple values it has + */ + static inline std::pair<bool,std::string> GetArgHelper(const MapArgs& map_args, const std::string& arg, bool getLast = false) + { + auto it = map_args.find(arg); + + if (it == map_args.end() || it->second.empty()) { + return std::make_pair(false, std::string()); + } + + if (getLast) { + return std::make_pair(true, it->second.back()); + } else { + return std::make_pair(true, it->second.front()); + } + } + + /* Get the string value of an argument, returning a pair of a boolean + * indicating the argument was found, and the value for the argument + * if it was found (or the empty string if not found). + */ + static inline std::pair<bool,std::string> GetArg(const ArgsManager &am, const std::string& arg) + { + LOCK(am.cs_args); + std::pair<bool,std::string> found_result(false, std::string()); + + // We pass "true" to GetArgHelper in order to return the last + // argument value seen from the command line (so "bitcoind -foo=bar + // -foo=baz" gives GetArg(am,"foo")=={true,"baz"} + found_result = GetArgHelper(am.m_override_args, arg, true); + if (found_result.first) { + return found_result; + } + + // But in contrast we return the first argument seen in a config file, + // so "foo=bar \n foo=baz" in the config file gives + // GetArg(am,"foo")={true,"bar"} + if (!am.m_network.empty()) { + found_result = GetArgHelper(am.m_config_args, NetworkArg(am, arg)); + if (found_result.first) { + return found_result; + } + } + + if (UseDefaultSection(am, arg)) { + found_result = GetArgHelper(am.m_config_args, arg); + if (found_result.first) { + return found_result; + } + } + + return found_result; + } + + /* Special test for -testnet and -regtest args, because we + * don't want to be confused by craziness like "[regtest] testnet=1" + */ + static inline bool GetNetBoolArg(const ArgsManager &am, const std::string& net_arg) + { + std::pair<bool,std::string> found_result(false,std::string()); + found_result = GetArgHelper(am.m_override_args, net_arg, true); + if (!found_result.first) { + found_result = GetArgHelper(am.m_config_args, net_arg, true); + if (!found_result.first) { + return false; // not set + } + } + return InterpretBool(found_result.second); // is set, so evaluate + } +}; + /** * Interpret -nofoo as if the user supplied -foo=0. * - * This method also tracks when the -no form was supplied, and treats "-foo" as - * a negated option when this happens. This can be later checked using the + * This method also tracks when the -no form was supplied, and if so, + * checks whether there was a double-negative (-nofoo=0 -> -foo=1). + * + * If there was not a double negative, it removes the "no" from the key, + * and returns true, indicating the caller should clear the args vector + * to indicate a negated option. + * + * If there was a double negative, it removes "no" from the key, sets the + * value to "1" and returns false. + * + * If there was no "no", it leaves key and value untouched and returns + * false. + * + * Where an option was negated can be later checked using the * IsArgNegated() method. One use case for this is to have a way to disable * options that are not normally boolean (e.g. using -nodebuglogfile to request * that debug log output is not sent to any file at all). */ -void ArgsManager::InterpretNegatedOption(std::string& key, std::string& val) +static bool InterpretNegatedOption(std::string& key, std::string& val) { - if (key.substr(0, 3) == "-no") { + assert(key[0] == '-'); + + size_t option_index = key.find('.'); + if (option_index == std::string::npos) { + option_index = 1; + } else { + ++option_index; + } + if (key.substr(option_index, 2) == "no") { bool bool_val = InterpretBool(val); + key.erase(option_index, 2); if (!bool_val ) { // Double negatives like -nofoo=0 are supported (but discouraged) LogPrintf("Warning: parsed potentially confusing double-negative %s=%s\n", key, val); + val = "1"; + } else { + return true; } - key.erase(1, 2); - m_negated_args.insert(key); - val = bool_val ? "0" : "1"; - } else { - // In an invocation like "bitcoind -nofoo -foo" we want to unmark -foo - // as negated when we see the second option. - m_negated_args.erase(key); } + return false; +} + +ArgsManager::ArgsManager() : + /* These options would cause cross-contamination if values for + * mainnet were used while running on regtest/testnet (or vice-versa). + * Setting them as section_only_args ensures that sharing a config file + * between mainnet and regtest/testnet won't cause problems due to these + * parameters by accident. */ + m_network_only_args{ + "-addnode", "-connect", + "-port", "-bind", + "-rpcport", "-rpcbind", + "-wallet", + } +{ + // nothing to do +} + +void ArgsManager::WarnForSectionOnlyArgs() +{ + // if there's no section selected, don't worry + if (m_network.empty()) return; + + // if it's okay to use the default section for this network, don't worry + if (m_network == CBaseChainParams::MAIN) return; + + for (const auto& arg : m_network_only_args) { + std::pair<bool, std::string> found_result; + + // if this option is overridden it's fine + found_result = ArgsManagerHelper::GetArgHelper(m_override_args, arg); + if (found_result.first) continue; + + // if there's a network-specific value for this option, it's fine + found_result = ArgsManagerHelper::GetArgHelper(m_config_args, ArgsManagerHelper::NetworkArg(*this, arg)); + if (found_result.first) continue; + + // if there isn't a default value for this option, it's fine + found_result = ArgsManagerHelper::GetArgHelper(m_config_args, arg); + if (!found_result.first) continue; + + // otherwise, issue a warning + LogPrintf("Warning: Config setting for %s only applied on %s network when in [%s] section.\n", arg, m_network, m_network); + } +} + +void ArgsManager::SelectConfigNetwork(const std::string& network) +{ + m_network = network; } void ArgsManager::ParseParameters(int argc, const char* const argv[]) { LOCK(cs_args); - mapArgs.clear(); - mapMultiArgs.clear(); - m_negated_args.clear(); + m_override_args.clear(); for (int i = 1; i < argc; i++) { std::string key(argv[i]); @@ -509,55 +438,79 @@ void ArgsManager::ParseParameters(int argc, const char* const argv[]) if (key.length() > 1 && key[1] == '-') key.erase(0, 1); - // Transform -nofoo to -foo=0 - InterpretNegatedOption(key, val); - - mapArgs[key] = val; - mapMultiArgs[key].push_back(val); + // Check for -nofoo + if (InterpretNegatedOption(key, val)) { + m_override_args[key].clear(); + } else { + m_override_args[key].push_back(val); + } } } std::vector<std::string> ArgsManager::GetArgs(const std::string& strArg) const { + std::vector<std::string> result = {}; + if (IsArgNegated(strArg)) return result; // special case + LOCK(cs_args); - auto it = mapMultiArgs.find(strArg); - if (it != mapMultiArgs.end()) return it->second; - return {}; + + ArgsManagerHelper::AddArgs(result, m_override_args, strArg); + if (!m_network.empty()) { + ArgsManagerHelper::AddArgs(result, m_config_args, ArgsManagerHelper::NetworkArg(*this, strArg)); + } + + if (ArgsManagerHelper::UseDefaultSection(*this, strArg)) { + ArgsManagerHelper::AddArgs(result, m_config_args, strArg); + } + + return result; } bool ArgsManager::IsArgSet(const std::string& strArg) const { - LOCK(cs_args); - return mapArgs.count(strArg); + if (IsArgNegated(strArg)) return true; // special case + return ArgsManagerHelper::GetArg(*this, strArg).first; } bool ArgsManager::IsArgNegated(const std::string& strArg) const { LOCK(cs_args); - return m_negated_args.find(strArg) != m_negated_args.end(); + + const auto& ov = m_override_args.find(strArg); + if (ov != m_override_args.end()) return ov->second.empty(); + + if (!m_network.empty()) { + const auto& cfs = m_config_args.find(ArgsManagerHelper::NetworkArg(*this, strArg)); + if (cfs != m_config_args.end()) return cfs->second.empty(); + } + + const auto& cf = m_config_args.find(strArg); + if (cf != m_config_args.end()) return cf->second.empty(); + + return false; } std::string ArgsManager::GetArg(const std::string& strArg, const std::string& strDefault) const { - LOCK(cs_args); - auto it = mapArgs.find(strArg); - if (it != mapArgs.end()) return it->second; + if (IsArgNegated(strArg)) return "0"; + std::pair<bool,std::string> found_res = ArgsManagerHelper::GetArg(*this, strArg); + if (found_res.first) return found_res.second; return strDefault; } int64_t ArgsManager::GetArg(const std::string& strArg, int64_t nDefault) const { - LOCK(cs_args); - auto it = mapArgs.find(strArg); - if (it != mapArgs.end()) return atoi64(it->second); + if (IsArgNegated(strArg)) return 0; + std::pair<bool,std::string> found_res = ArgsManagerHelper::GetArg(*this, strArg); + if (found_res.first) return atoi64(found_res.second); return nDefault; } bool ArgsManager::GetBoolArg(const std::string& strArg, bool fDefault) const { - LOCK(cs_args); - auto it = mapArgs.find(strArg); - if (it != mapArgs.end()) return InterpretBool(it->second); + if (IsArgNegated(strArg)) return false; + std::pair<bool,std::string> found_res = ArgsManagerHelper::GetArg(*this, strArg); + if (found_res.first) return InterpretBool(found_res.second); return fDefault; } @@ -580,8 +533,7 @@ bool ArgsManager::SoftSetBoolArg(const std::string& strArg, bool fValue) void ArgsManager::ForceSetArg(const std::string& strArg, const std::string& strValue) { LOCK(cs_args); - mapArgs[strArg] = strValue; - mapMultiArgs[strArg] = {strValue}; + m_override_args[strArg] = {strValue}; } bool HelpRequested(const ArgsManager& args) @@ -735,28 +687,39 @@ fs::path GetConfigFile(const std::string& confPath) return AbsPathForConfigVal(fs::path(confPath), false); } -void ArgsManager::ReadConfigFile(const std::string& confPath) +void ArgsManager::ReadConfigStream(std::istream& stream) { - fs::ifstream streamConfig(GetConfigFile(confPath)); - if (!streamConfig.good()) - return; // No bitcoin.conf file is OK + LOCK(cs_args); + + std::set<std::string> setOptions; + setOptions.insert("*"); + for (boost::program_options::detail::config_file_iterator it(stream, setOptions), end; it != end; ++it) { - LOCK(cs_args); - std::set<std::string> setOptions; - setOptions.insert("*"); - - for (boost::program_options::detail::config_file_iterator it(streamConfig, setOptions), end; it != end; ++it) - { - // Don't overwrite existing settings so command line settings override bitcoin.conf - std::string strKey = std::string("-") + it->string_key; - std::string strValue = it->value[0]; - InterpretNegatedOption(strKey, strValue); - if (mapArgs.count(strKey) == 0) - mapArgs[strKey] = strValue; - mapMultiArgs[strKey].push_back(strValue); + std::string strKey = std::string("-") + it->string_key; + std::string strValue = it->value[0]; + if (InterpretNegatedOption(strKey, strValue)) { + m_config_args[strKey].clear(); + } else { + m_config_args[strKey].push_back(strValue); } } +} + +void ArgsManager::ReadConfigFile(const std::string& confPath) +{ + { + LOCK(cs_args); + m_config_args.clear(); + } + + fs::ifstream stream(GetConfigFile(confPath)); + + // ok to not have a config file + if (stream.good()) { + ReadConfigStream(stream); + } + // If datadir is changed in .conf file: ClearDatadirCache(); if (!fs::is_directory(GetDataDir(false))) { @@ -764,6 +727,20 @@ void ArgsManager::ReadConfigFile(const std::string& confPath) } } +std::string ArgsManager::GetChainName() const +{ + bool fRegTest = ArgsManagerHelper::GetNetBoolArg(*this, "-regtest"); + bool fTestNet = ArgsManagerHelper::GetNetBoolArg(*this, "-testnet"); + + if (fTestNet && fRegTest) + throw std::runtime_error("Invalid combination of -regtest and -testnet."); + if (fRegTest) + return CBaseChainParams::REGTEST; + if (fTestNet) + return CBaseChainParams::TESTNET; + return CBaseChainParams::MAIN; +} + #ifndef WIN32 fs::path GetPidFile() { @@ -811,21 +788,37 @@ bool TryCreateDirectories(const fs::path& p) return false; } -void FileCommit(FILE *file) +bool FileCommit(FILE *file) { - fflush(file); // harmless if redundantly called + if (fflush(file) != 0) { // harmless if redundantly called + LogPrintf("%s: fflush failed: %d\n", __func__, errno); + return false; + } #ifdef WIN32 HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file)); - FlushFileBuffers(hFile); + if (FlushFileBuffers(hFile) == 0) { + LogPrintf("%s: FlushFileBuffers failed: %d\n", __func__, GetLastError()); + return false; + } #else #if defined(__linux__) || defined(__NetBSD__) - fdatasync(fileno(file)); + if (fdatasync(fileno(file)) != 0 && errno != EINVAL) { // Ignore EINVAL for filesystems that don't support sync + LogPrintf("%s: fdatasync failed: %d\n", __func__, errno); + return false; + } #elif defined(__APPLE__) && defined(F_FULLFSYNC) - fcntl(fileno(file), F_FULLFSYNC, 0); + if (fcntl(fileno(file), F_FULLFSYNC, 0) == -1) { // Manpage says "value other than -1" is returned on success + LogPrintf("%s: fcntl F_FULLFSYNC failed: %d\n", __func__, errno); + return false; + } #else - fsync(fileno(file)); + if (fsync(fileno(file)) != 0 && errno != EINVAL) { + LogPrintf("%s: fsync failed: %d\n", __func__, errno); + return false; + } #endif #endif + return true; } bool TruncateFile(FILE *file, unsigned int length) { @@ -905,34 +898,6 @@ void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length) { #endif } -void ShrinkDebugFile() -{ - // Amount of debug.log to save at end when shrinking (must fit in memory) - constexpr size_t RECENT_DEBUG_HISTORY_SIZE = 10 * 1000000; - // Scroll debug.log if it's getting too big - fs::path pathLog = GetDebugLogPath(); - FILE* file = fsbridge::fopen(pathLog, "r"); - // If debug.log file is more than 10% bigger the RECENT_DEBUG_HISTORY_SIZE - // trim it down by saving only the last RECENT_DEBUG_HISTORY_SIZE bytes - if (file && fs::file_size(pathLog) > 11 * (RECENT_DEBUG_HISTORY_SIZE / 10)) - { - // Restart the file with some of the end - std::vector<char> vch(RECENT_DEBUG_HISTORY_SIZE, 0); - fseek(file, -((long)vch.size()), SEEK_END); - int nBytes = fread(vch.data(), 1, vch.size(), file); - fclose(file); - - file = fsbridge::fopen(pathLog, "w"); - if (file) - { - fwrite(vch.data(), 1, nBytes, file); - fclose(file); - } - } - else if (file != nullptr) - fclose(file); -} - #ifdef WIN32 fs::path GetSpecialFolderPath(int nFolder, bool fCreate) { @@ -1039,3 +1004,17 @@ fs::path AbsPathForConfigVal(const fs::path& path, bool net_specific) { return fs::absolute(path, GetDataDir(net_specific)); } + +int ScheduleBatchPriority(void) +{ +#ifdef SCHED_BATCH + const static sched_param param{0}; + if (int ret = pthread_setschedparam(pthread_self(), SCHED_BATCH, ¶m)) { + LogPrintf("Failed to pthread_setschedparam: %s\n", strerror(errno)); + return ret; + } + return 0; +#else + return 1; +#endif +} diff --git a/src/util.h b/src/util.h index 7114c0accd..2da8023285 100644 --- a/src/util.h +++ b/src/util.h @@ -5,7 +5,7 @@ /** * Server/client environment: argument handling, config file parsing, - * logging, thread wrappers, startup time + * thread wrappers, startup time */ #ifndef BITCOIN_UTIL_H #define BITCOIN_UTIL_H @@ -16,6 +16,7 @@ #include <compat.h> #include <fs.h> +#include <logging.h> #include <sync.h> #include <tinyformat.h> #include <utiltime.h> @@ -24,6 +25,7 @@ #include <exception> #include <map> #include <memory> +#include <set> #include <stdint.h> #include <string> #include <unordered_set> @@ -35,11 +37,6 @@ // Application startup time (used for uptime calculation) int64_t GetStartupTime(); -static const bool DEFAULT_LOGTIMEMICROS = false; -static const bool DEFAULT_LOGIPS = false; -static const bool DEFAULT_LOGTIMESTAMPS = true; -extern const char * const DEFAULT_DEBUGLOGFILE; - /** Signals for translation. */ class CTranslationInterface { @@ -48,20 +45,11 @@ public: boost::signals2::signal<std::string (const char* psz)> Translate; }; -extern bool fPrintToConsole; -extern bool fPrintToDebugLog; - -extern bool fLogTimestamps; -extern bool fLogTimeMicros; -extern bool fLogIPs; -extern std::atomic<bool> fReopenDebugLog; extern CTranslationInterface translationInterface; extern const char * const BITCOIN_CONF_FILENAME; extern const char * const BITCOIN_PID_FILENAME; -extern std::atomic<uint32_t> logCategories; - /** * Translation function: Call Translate signal on UI interface, which returns a boost::optional result. * If no translation slot is registered, nothing is returned, and simply return the input. @@ -75,102 +63,15 @@ inline std::string _(const char* psz) void SetupEnvironment(); bool SetupNetworking(); -struct CLogCategoryActive -{ - std::string category; - bool active; -}; - -namespace BCLog { - enum LogFlags : uint32_t { - NONE = 0, - NET = (1 << 0), - TOR = (1 << 1), - MEMPOOL = (1 << 2), - HTTP = (1 << 3), - BENCH = (1 << 4), - ZMQ = (1 << 5), - DB = (1 << 6), - RPC = (1 << 7), - ESTIMATEFEE = (1 << 8), - ADDRMAN = (1 << 9), - SELECTCOINS = (1 << 10), - REINDEX = (1 << 11), - CMPCTBLOCK = (1 << 12), - RAND = (1 << 13), - PRUNE = (1 << 14), - PROXY = (1 << 15), - MEMPOOLREJ = (1 << 16), - LIBEVENT = (1 << 17), - COINDB = (1 << 18), - QT = (1 << 19), - LEVELDB = (1 << 20), - ALL = ~(uint32_t)0, - }; -} -/** Return true if log accepts specified category */ -static inline bool LogAcceptCategory(uint32_t category) -{ - return (logCategories.load(std::memory_order_relaxed) & category) != 0; -} - -/** Returns a string with the log categories. */ -std::string ListLogCategories(); - -/** Returns a vector of the active log categories. */ -std::vector<CLogCategoryActive> ListActiveLogCategories(); - -/** Return true if str parses as a log category and set the flags in f */ -bool GetLogCategory(uint32_t *f, const std::string *str); - -/** Send a string to the log output */ -int LogPrintStr(const std::string &str); - -/** Get format string from VA_ARGS for error reporting */ -template<typename... Args> std::string FormatStringFromLogArgs(const char *fmt, const Args&... args) { return fmt; } - -static inline void MarkUsed() {} -template<typename T, typename... Args> static inline void MarkUsed(const T& t, const Args&... args) -{ - (void)t; - MarkUsed(args...); -} - -// Be conservative when using LogPrintf/error or other things which -// unconditionally log to debug.log! It should not be the case that an inbound -// peer can fill up a user's disk with debug.log entries. - -#ifdef USE_COVERAGE -#define LogPrintf(...) do { MarkUsed(__VA_ARGS__); } while(0) -#define LogPrint(category, ...) do { MarkUsed(__VA_ARGS__); } while(0) -#else -#define LogPrintf(...) do { \ - std::string _log_msg_; /* Unlikely name to avoid shadowing variables */ \ - try { \ - _log_msg_ = tfm::format(__VA_ARGS__); \ - } catch (tinyformat::format_error &fmterr) { \ - /* Original format string will have newline so don't add one here */ \ - _log_msg_ = "Error \"" + std::string(fmterr.what()) + "\" while formatting log message: " + FormatStringFromLogArgs(__VA_ARGS__); \ - } \ - LogPrintStr(_log_msg_); \ -} while(0) - -#define LogPrint(category, ...) do { \ - if (LogAcceptCategory((category))) { \ - LogPrintf(__VA_ARGS__); \ - } \ -} while(0) -#endif - template<typename... Args> bool error(const char* fmt, const Args&... args) { - LogPrintStr("ERROR: " + tfm::format(fmt, args...) + "\n"); + LogPrintf("ERROR: %s\n", tfm::format(fmt, args...)); return false; } void PrintExceptionContinue(const std::exception *pex, const char* pszThread); -void FileCommit(FILE *file); +bool FileCommit(FILE *file); bool TruncateFile(FILE *file, unsigned int length); int RaiseFileDescriptorLimit(int nMinFD); void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length); @@ -196,9 +97,6 @@ void CreatePidFile(const fs::path &path, pid_t pid); #ifdef WIN32 fs::path GetSpecialFolderPath(int nFolder, bool fCreate = true); #endif -fs::path GetDebugLogPath(); -bool OpenDebugLog(); -void ShrinkDebugFile(); void runCommand(const std::string& strCommand); /** @@ -223,16 +121,36 @@ inline bool IsSwitchChar(char c) class ArgsManager { protected: + friend class ArgsManagerHelper; + mutable CCriticalSection cs_args; - std::map<std::string, std::string> mapArgs; - std::map<std::string, std::vector<std::string>> mapMultiArgs; - std::unordered_set<std::string> m_negated_args; + std::map<std::string, std::vector<std::string>> m_override_args; + std::map<std::string, std::vector<std::string>> m_config_args; + std::string m_network; + std::set<std::string> m_network_only_args; + + void ReadConfigStream(std::istream& stream); public: + ArgsManager(); + + /** + * Select the network in use + */ + void SelectConfigNetwork(const std::string& network); + void ParseParameters(int argc, const char*const argv[]); void ReadConfigFile(const std::string& confPath); /** + * Log warnings for options in m_section_only_args when + * they are specified in the default section but not overridden + * on the command line or in a network-specific section in the + * config file. + */ + void WarnForSectionOnlyArgs(); + + /** * Return a vector of strings of the given argument * * @param strArg Argument to get (e.g. "-foo") @@ -306,10 +224,11 @@ public: // been set. Also called directly in testing. void ForceSetArg(const std::string& strArg, const std::string& strValue); -private: - - // Munge -nofoo into -foo=0 and track the value as negated. - void InterpretNegatedOption(std::string &key, std::string &val); + /** + * Looks for -regtest, -testnet and returns the appropriate BIP70 chain name. + * @return CBaseChainParams::MAIN by default; raises runtime error if an invalid combination is given. + */ + std::string GetChainName() const; }; extern ArgsManager gArgs; @@ -381,4 +300,13 @@ 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. + * + * @return The return value of sched_setschedule(), or 1 on systems without + * sched_setchedule(). + */ +int ScheduleBatchPriority(void); + #endif // BITCOIN_UTIL_H diff --git a/src/utiltime.cpp b/src/utiltime.cpp index 8a861039b3..e60996efe1 100644 --- a/src/utiltime.cpp +++ b/src/utiltime.cpp @@ -10,9 +10,10 @@ #include <utiltime.h> #include <atomic> - #include <boost/date_time/posix_time/posix_time.hpp> #include <boost/thread.hpp> +#include <ctime> +#include <tinyformat.h> static std::atomic<int64_t> nMockTime(0); //!< For unit testing @@ -75,25 +76,35 @@ void MilliSleep(int64_t n) #endif } -std::string DateTimeStrFormat(const char* pszFormat, int64_t nTime) -{ - static std::locale classic(std::locale::classic()); - // std::locale takes ownership of the pointer - std::locale loc(classic, new boost::posix_time::time_facet(pszFormat)); - std::stringstream ss; - ss.imbue(loc); - ss << boost::posix_time::from_time_t(nTime); - return ss.str(); -} - std::string FormatISO8601DateTime(int64_t nTime) { - return DateTimeStrFormat("%Y-%m-%dT%H:%M:%SZ", nTime); + struct tm ts; + time_t time_val = nTime; +#ifdef _MSC_VER + gmtime_s(&ts, &time_val); +#else + gmtime_r(&time_val, &ts); +#endif + return strprintf("%04i-%02i-%02iT%02i:%02i:%02iZ", ts.tm_year + 1900, ts.tm_mon + 1, ts.tm_mday, ts.tm_hour, ts.tm_min, ts.tm_sec); } std::string FormatISO8601Date(int64_t nTime) { - return DateTimeStrFormat("%Y-%m-%d", nTime); + struct tm ts; + time_t time_val = nTime; +#ifdef _MSC_VER + gmtime_s(&ts, &time_val); +#else + gmtime_r(&time_val, &ts); +#endif + return strprintf("%04i-%02i-%02i", ts.tm_year + 1900, ts.tm_mon + 1, ts.tm_mday); } std::string FormatISO8601Time(int64_t nTime) { - return DateTimeStrFormat("%H:%M:%SZ", nTime); + struct tm ts; + time_t time_val = nTime; +#ifdef _MSC_VER + gmtime_s(&ts, &time_val); +#else + gmtime_r(&time_val, &ts); +#endif + return strprintf("%02i:%02i:%02iZ", ts.tm_hour, ts.tm_min, ts.tm_sec); } diff --git a/src/utiltime.h b/src/utiltime.h index 807c52ffaf..3bcbb00c16 100644 --- a/src/utiltime.h +++ b/src/utiltime.h @@ -31,8 +31,6 @@ void MilliSleep(int64_t n); * ISO 8601 formatting is preferred. Use the FormatISO8601{DateTime,Date,Time} * helper functions if possible. */ -std::string DateTimeStrFormat(const char* pszFormat, int64_t nTime); - std::string FormatISO8601DateTime(int64_t nTime); std::string FormatISO8601Date(int64_t nTime); std::string FormatISO8601Time(int64_t nTime); diff --git a/src/validation.cpp b/src/validation.cpp index df8729e382..1b9e982753 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -16,6 +16,7 @@ #include <consensus/validation.h> #include <cuckoocache.h> #include <hash.h> +#include <index/txindex.h> #include <init.h> #include <policy/fees.h> #include <policy/policy.h> @@ -142,7 +143,7 @@ private: * ahead and mark descendants of invalid blocks as FAILED_CHILD at that time, * instead of putting things in this set. */ - std::set<CBlockIndex*> g_failed_blocks; + std::set<CBlockIndex*> m_failed_blocks; public: CChain chainActive; @@ -154,6 +155,10 @@ public: bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock); + /** + * 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); @@ -185,6 +190,11 @@ private: CBlockIndex* AddToBlockIndex(const CBlockHeader& block); /** Create a new block index entry for a given block hash */ CBlockIndex * InsertBlockIndex(const uint256& hash); + /** + * Make various assertions about the state of the block index. + * + * By default this only executes fully when using the Regtest chain; see: fCheckBlockIndex. + */ void CheckBlockIndex(const Consensus::Params& consensusParams); void InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state); @@ -202,12 +212,12 @@ CCriticalSection cs_main; BlockMap& mapBlockIndex = g_chainstate.mapBlockIndex; CChain& chainActive = g_chainstate.chainActive; CBlockIndex *pindexBestHeader = nullptr; -CWaitableCriticalSection csBestBlock; -CConditionVariable cvBlockChange; +CWaitableCriticalSection g_best_block_mutex; +CConditionVariable g_best_block_cv; +uint256 g_best_block; int nScriptCheckThreads = 0; std::atomic_bool fImporting(false); std::atomic_bool fReindex(false); -bool fTxIndex = false; bool fHavePruned = false; bool fPruneMode = false; bool fIsBareMultisigStd = DEFAULT_PERMIT_BAREMULTISIG; @@ -263,7 +273,8 @@ CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& loc { AssertLockHeld(cs_main); - // Find the first block the caller has in the main chain + // Find the latest block common to locator and chain - we expect that + // locator.vHave is sorted descending by height. for (const uint256& hash : locator.vHave) { CBlockIndex* pindex = LookupBlockIndex(hash); if (pindex) { @@ -352,7 +363,7 @@ bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp, bool CBlockIndex* tip = chainActive.Tip(); assert(tip != nullptr); - + CBlockIndex index; index.pprev = tip; // CheckSequenceLocks() uses chainActive.Height()+1 to evaluate @@ -507,7 +518,7 @@ void UpdateMempoolForReorg(DisconnectedBlockTransactions &disconnectpool, bool f // Used to avoid mempool polluting consensus critical paths if CCoinsViewMempool // were somehow broken and returning the wrong scriptPubKeys -static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &view, CTxMemPool& pool, +static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& view, const CTxMemPool& pool, unsigned int flags, bool cacheSigStore, PrecomputedTransactionData& txdata) { AssertLockHeld(cs_main); @@ -718,7 +729,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool // No transactions are allowed below minRelayTxFee except from disconnected blocks if (!bypass_limits && nModifiedFees < ::minRelayTxFee.GetFee(nSize)) { - return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "min relay fee not met"); + return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "min relay fee not met", false, strprintf("%d < %d", nModifiedFees, ::minRelayTxFee.GetFee(nSize))); } if (nAbsurdFee && nFees > nAbsurdFee) @@ -917,8 +928,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool // invalid blocks (using TestBlockValidity), however allowing such // transactions into the mempool can be exploited as a DoS attack. unsigned int currentBlockScriptVerifyFlags = GetBlockScriptFlags(chainActive.Tip(), Params().GetConsensus()); - if (!CheckInputsFromMempoolAndCache(tx, state, view, pool, currentBlockScriptVerifyFlags, true, txdata)) - { + if (!CheckInputsFromMempoolAndCache(tx, state, view, pool, currentBlockScriptVerifyFlags, true, txdata)) { // If we're using promiscuousmempoolflags, we may hit this normally // Check if current block has some flags that scriptVerifyFlags // does not before printing an ominous warning @@ -1018,28 +1028,8 @@ bool GetTransaction(const uint256& hash, CTransactionRef& txOut, const Consensus return true; } - if (fTxIndex) { - CDiskTxPos postx; - if (pblocktree->ReadTxIndex(hash, postx)) { - CAutoFile file(OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION); - if (file.IsNull()) - return error("%s: OpenBlockFile failed", __func__); - CBlockHeader header; - try { - file >> header; - fseek(file.Get(), postx.nTxOffset, SEEK_CUR); - file >> txOut; - } catch (const std::exception& e) { - return error("%s: Deserialize or I/O error - %s", __func__, e.what()); - } - hashBlock = header.GetHash(); - if (txOut->GetHash() != hash) - return error("%s: txid mismatch", __func__); - return true; - } - - // transaction not found in index, nothing more can be done - return false; + if (g_txindex) { + return g_txindex->FindTx(hash, hashBlock, txOut); } if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it @@ -1285,7 +1275,7 @@ void static InvalidChainFound(CBlockIndex* pindexNew) void CChainState::InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) { if (!state.CorruptionPossible()) { pindex->nStatus |= BLOCK_FAILED_VALID; - g_failed_blocks.insert(pindex); + m_failed_blocks.insert(pindex); setDirtyBlockIndex.insert(pindex); setBlockIndexCandidates.erase(pindex); InvalidChainFound(pindex); @@ -1614,22 +1604,27 @@ void static FlushBlockFile(bool fFinalize = false) LOCK(cs_LastBlockFile); CDiskBlockPos posOld(nLastBlockFile, 0); + bool status = true; FILE *fileOld = OpenBlockFile(posOld); if (fileOld) { if (fFinalize) - TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nSize); - FileCommit(fileOld); + status &= TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nSize); + status &= FileCommit(fileOld); fclose(fileOld); } fileOld = OpenUndoFile(posOld); if (fileOld) { if (fFinalize) - TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nUndoSize); - FileCommit(fileOld); + status &= TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nUndoSize); + status &= FileCommit(fileOld); fclose(fileOld); } + + if (!status) { + AbortNode("Flushing block file to disk failed. This is likely the result of an I/O error."); + } } static bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize); @@ -1653,26 +1648,6 @@ static bool WriteUndoDataForBlock(const CBlockUndo& blockundo, CValidationState& return true; } -static bool WriteTxIndexDataForBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex) -{ - if (!fTxIndex) return true; - - CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size())); - std::vector<std::pair<uint256, CDiskTxPos> > vPos; - vPos.reserve(block.vtx.size()); - for (const CTransactionRef& tx : block.vtx) - { - vPos.push_back(std::make_pair(tx->GetHash(), pos)); - pos.nTxOffset += ::GetSerializeSize(*tx, SER_DISK, CLIENT_VERSION); - } - - if (!pblocktree->WriteTxIndex(vPos)) { - return AbortNode(state, "Failed to write transaction index"); - } - - return true; -} - static CCheckQueue<CScriptCheck> scriptcheckqueue(128); void ThreadScriptCheck() { @@ -1725,16 +1700,38 @@ public: // Protected by cs_main static ThresholdConditionCache warningcache[VERSIONBITS_NUM_BITS]; +// 0.13.0 was shipped with a segwit deployment defined for testnet, but not for +// mainnet. We no longer need to support disabling the segwit deployment +// except for testing purposes, due to limitations of the functional test +// environment. See test/functional/p2p-segwit.py. +static bool IsScriptWitnessEnabled(const Consensus::Params& params) +{ + return params.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout != 0; +} + static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consensus::Params& consensusparams) { AssertLockHeld(cs_main); unsigned int flags = SCRIPT_VERIFY_NONE; - // Start enforcing P2SH (BIP16) - if (pindex->nHeight >= consensusparams.BIP16Height) { + // BIP16 didn't become active until Apr 1 2012 (on mainnet, and + // retroactively applied to testnet) + // However, only one historical block violated the P2SH rules (on both + // mainnet and testnet), so for simplicity, always leave P2SH + // on except for the one violating block. + if (consensusparams.BIP16Exception.IsNull() || // no bip16 exception on this chain + pindex->phashBlock == nullptr || // this is a new candidate block, eg from TestBlockValidity() + *pindex->phashBlock != consensusparams.BIP16Exception) // this block isn't the historical exception + { flags |= SCRIPT_VERIFY_P2SH; } + // Enforce WITNESS rules whenever P2SH is in effect (and the segwit + // deployment is defined). + if (flags & SCRIPT_VERIFY_P2SH && IsScriptWitnessEnabled(consensusparams)) { + flags |= SCRIPT_VERIFY_WITNESS; + } + // Start enforcing the DERSIG (BIP66) rule if (pindex->nHeight >= consensusparams.BIP66Height) { flags |= SCRIPT_VERIFY_DERSIG; @@ -1750,9 +1747,7 @@ static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consens flags |= SCRIPT_VERIFY_CHECKSEQUENCEVERIFY; } - // Start enforcing WITNESS rules using versionbits logic. - if (IsWitnessEnabled(pindex->pprev, consensusparams)) { - flags |= SCRIPT_VERIFY_WITNESS; + if (IsNullDummyEnabled(pindex->pprev, consensusparams)) { flags |= SCRIPT_VERIFY_NULLDUMMY; } @@ -1794,8 +1789,15 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl // is enforced in ContextualCheckBlockHeader(); we wouldn't want to // re-enforce that rule here (at least until we make it impossible for // GetAdjustedTime() to go backward). - if (!CheckBlock(block, state, chainparams.GetConsensus(), !fJustCheck, !fJustCheck)) + if (!CheckBlock(block, state, chainparams.GetConsensus(), !fJustCheck, !fJustCheck)) { + if (state.CorruptionPossible()) { + // We don't write down blocks to disk if they may have been + // corrupted, so this should be impossible unless we're having hardware + // problems. + return AbortNode(state, "Corrupt block found indicating potential hardware failure; shutting down"); + } return error("%s: Consensus::CheckBlock: %s", __func__, FormatStateMessage(state)); + } // verify that the view's current state corresponds to the previous block uint256 hashPrevBlock = pindex->pprev == nullptr ? uint256() : pindex->pprev->GetBlockHash(); @@ -2037,9 +2039,6 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl setDirtyBlockIndex.insert(pindex); } - if (!WriteTxIndexDataForBlock(block, state, pindex)) - return false; - assert(pindex->phashBlock); // add this block to the view's block chain view.SetBestBlock(pindex->GetBlockHash()); @@ -2058,19 +2057,21 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl * The caches and indexes are flushed depending on the mode we're called with * if they're too large, if it's been a while since the last write, * or always and in all cases if we're in prune mode and are deleting files. + * + * If FlushStateMode::NONE is used, then FlushStateToDisk(...) won't do anything + * besides checking if we need to prune. */ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &state, FlushStateMode mode, int nManualPruneHeight) { int64_t nMempoolUsage = mempool.DynamicMemoryUsage(); LOCK(cs_main); static int64_t nLastWrite = 0; static int64_t nLastFlush = 0; - static int64_t nLastSetChain = 0; std::set<int> setFilesToPrune; - bool fFlushForPrune = false; - bool fDoFullFlush = false; - int64_t nNow = 0; + bool full_flush_completed = false; try { { + bool fFlushForPrune = false; + bool fDoFullFlush = false; LOCK(cs_LastBlockFile); if (fPruneMode && (fCheckForPruning || nManualPruneHeight > 0) && !fReindex) { if (nManualPruneHeight > 0) { @@ -2087,7 +2088,7 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState & } } } - nNow = GetTimeMicros(); + int64_t nNow = GetTimeMicros(); // Avoid writing/flushing immediately after startup. if (nLastWrite == 0) { nLastWrite = nNow; @@ -2095,9 +2096,6 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState & if (nLastFlush == 0) { nLastFlush = nNow; } - if (nLastSetChain == 0) { - nLastSetChain = nNow; - } int64_t nMempoolSizeMax = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000; int64_t cacheSize = pcoinsTip->DynamicMemoryUsage(); int64_t nTotalSpace = nCoinCacheUsage + std::max<int64_t>(nMempoolSizeMax - nMempoolUsage, 0); @@ -2154,12 +2152,12 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState & if (!pcoinsTip->Flush()) return AbortNode(state, "Failed to write to coin database"); nLastFlush = nNow; + full_flush_completed = true; } } - if (fDoFullFlush || ((mode == FlushStateMode::ALWAYS || mode == FlushStateMode::PERIODIC) && nNow > nLastSetChain + (int64_t)DATABASE_WRITE_INTERVAL * 1000000)) { + if (full_flush_completed) { // Update best block in wallet (so we can detect restored wallets). - GetMainSignals().SetBestChain(chainActive.GetLocator()); - nLastSetChain = nNow; + GetMainSignals().ChainStateFlushed(chainActive.GetLocator()); } } catch (const std::runtime_error& e) { return AbortNode(state, std::string("System error while flushing: ") + e.what()); @@ -2170,14 +2168,18 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState & void FlushStateToDisk() { CValidationState state; const CChainParams& chainparams = Params(); - FlushStateToDisk(chainparams, state, FlushStateMode::ALWAYS); + if (!FlushStateToDisk(chainparams, state, FlushStateMode::ALWAYS)) { + LogPrintf("%s: failed to flush state (%s)\n", __func__, FormatStateMessage(state)); + } } void PruneAndFlush() { CValidationState state; fCheckForPruning = true; const CChainParams& chainparams = Params(); - FlushStateToDisk(chainparams, state, FlushStateMode::NONE); + if (!FlushStateToDisk(chainparams, state, FlushStateMode::NONE)) { + LogPrintf("%s: failed to flush state (%s)\n", __func__, FormatStateMessage(state)); + } } static void DoWarning(const std::string& strWarning) @@ -2195,7 +2197,11 @@ void static UpdateTip(const CBlockIndex *pindexNew, const CChainParams& chainPar // New best block mempool.AddTransactionsUpdated(1); - cvBlockChange.notify_all(); + { + WaitableLock lock(g_best_block_mutex); + g_best_block = pindexNew->GetBlockHash(); + g_best_block_cv.notify_all(); + } std::vector<std::string> warningMessages; if (!IsInitialBlockDownload()) @@ -2231,13 +2237,13 @@ void static UpdateTip(const CBlockIndex *pindexNew, const CChainParams& chainPar DoWarning(strWarning); } } - LogPrintf("%s: new best=%s height=%d version=0x%08x log2_work=%.8g tx=%lu date='%s' progress=%f cache=%.1fMiB(%utxo)", __func__, + LogPrintf("%s: new best=%s height=%d version=0x%08x log2_work=%.8g tx=%lu date='%s' progress=%f cache=%.1fMiB(%utxo)", __func__, /* Continued */ pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, pindexNew->nVersion, log(pindexNew->nChainWork.getdouble())/log(2.0), (unsigned long)pindexNew->nChainTx, FormatISO8601DateTime(pindexNew->GetBlockTime()), GuessVerificationProgress(chainParams.TxData(), pindexNew), pcoinsTip->DynamicMemoryUsage() * (1.0 / (1<<20)), pcoinsTip->GetCacheSize()); if (!warningMessages.empty()) - LogPrintf(" warning='%s'", boost::algorithm::join(warningMessages, ", ")); + LogPrintf(" warning='%s'", boost::algorithm::join(warningMessages, ", ")); /* Continued */ LogPrintf("\n"); } @@ -2610,6 +2616,10 @@ static void NotifyHeaderTip() { * Make the best chain active, in multiple steps. The result is either failure * or an activated best chain. pblock is either nullptr or a pointer to a block * that is already loaded (to avoid loading it again from disk). + * + * ActivateBestChain is split into steps (see ActivateBestChainStep) so that + * we avoid holding cs_main for an extended period of time; the length of this + * call may be quite long during reindexing or a substantial reorg. */ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock) { // Note that while we're often called here from ProcessNewBlock, this is @@ -2663,18 +2673,17 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams& assert(trace.pblock && trace.pindex); GetMainSignals().BlockConnected(trace.pblock, trace.pindex, trace.conflictedTxs); } - } - // When we reach this point, we switched to a new tip (stored in pindexNewTip). - // Notifications/callbacks that can run without cs_main + // Notify external listeners about the new tip. + // Enqueue while holding cs_main to ensure that UpdatedBlockTip is called in the order in which blocks are connected + GetMainSignals().UpdatedBlockTip(pindexNewTip, pindexFork, fInitialDownload); - // Notify external listeners about the new tip. - GetMainSignals().UpdatedBlockTip(pindexNewTip, pindexFork, fInitialDownload); - - // Always notify the UI if a new block tip was connected - if (pindexFork != pindexNewTip) { - uiInterface.NotifyBlockTip(fInitialDownload, pindexNewTip); + // Always notify the UI if a new block tip was connected + if (pindexFork != pindexNewTip) { + uiInterface.NotifyBlockTip(fInitialDownload, pindexNewTip); + } } + // When we reach this point, we switched to a new tip (stored in pindexNewTip). if (nStopAtHeight && pindexNewTip && pindexNewTip->nHeight >= nStopAtHeight) StartShutdown(); @@ -2769,7 +2778,7 @@ bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& c pindex->nStatus |= BLOCK_FAILED_VALID; setDirtyBlockIndex.insert(pindex); setBlockIndexCandidates.erase(pindex); - g_failed_blocks.insert(pindex); + m_failed_blocks.insert(pindex); // DisconnectTip will add transactions to disconnectpool; try to add these // back to the mempool. @@ -2815,7 +2824,7 @@ bool CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) { // Reset invalid block marker if it was pointing to one of those. pindexBestInvalid = nullptr; } - g_failed_blocks.erase(it->second); + m_failed_blocks.erase(it->second); } it++; } @@ -3085,6 +3094,12 @@ bool IsWitnessEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& pa return (VersionBitsState(pindexPrev, params, Consensus::DEPLOYMENT_SEGWIT, versionbitscache) == ThresholdState::ACTIVE); } +bool IsNullDummyEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& params) +{ + LOCK(cs_main); + return (VersionBitsState(pindexPrev, params, Consensus::DEPLOYMENT_SEGWIT, versionbitscache) == ThresholdState::ACTIVE); +} + // Compute at which vout of the block's coinbase transaction the witness // commitment occurs, or -1 if not found. static int GetWitnessCommitmentIndex(const CBlock& block) @@ -3308,8 +3323,11 @@ bool CChainState::AcceptBlockHeader(const CBlockHeader& block, CValidationState& if (!ContextualCheckBlockHeader(block, state, chainparams, pindexPrev, GetAdjustedTime())) 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 + // between them as failed. if (!pindexPrev->IsValid(BLOCK_VALID_SCRIPTS)) { - for (const CBlockIndex* failedit : g_failed_blocks) { + for (const CBlockIndex* failedit : m_failed_blocks) { if (pindexPrev->GetAncestor(failedit->nHeight) == failedit) { assert(failedit->nStatus & BLOCK_FAILED_VALID); CBlockIndex* invalid_walk = pindexPrev; @@ -3448,8 +3466,7 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CVali return AbortNode(state, std::string("System error: ") + e.what()); } - if (fCheckForPruning) - FlushStateToDisk(chainparams, state, FlushStateMode::NONE); // we just allocated more disk space for block files + FlushStateToDisk(chainparams, state, FlushStateMode::NONE); CheckBlockIndex(chainparams.GetConsensus()); @@ -3476,7 +3493,7 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<cons } if (!ret) { GetMainSignals().BlockChecked(*pblock, state); - return error("%s: AcceptBlock FAILED (%s)", __func__, state.GetDebugMessage()); + return error("%s: AcceptBlock FAILED (%s)", __func__, FormatStateMessage(state)); } } @@ -3484,7 +3501,7 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<cons CValidationState state; // Only used to report errors, not invalidity - ignore it if (!g_chainstate.ActivateBestChain(state, chainparams, pblock)) - return error("%s: ActivateBestChain failed", __func__); + return error("%s: ActivateBestChain failed (%s)", __func__, FormatStateMessage(state)); return true; } @@ -3602,7 +3619,9 @@ void PruneBlockFilesManual(int nManualPruneHeight) { CValidationState state; const CChainParams& chainparams = Params(); - FlushStateToDisk(chainparams, state, FlushStateMode::NONE, nManualPruneHeight); + if (!FlushStateToDisk(chainparams, state, FlushStateMode::NONE, nManualPruneHeight)) { + LogPrintf("%s: failed to flush state (%s)\n", __func__, FormatStateMessage(state)); + } } /** @@ -3837,10 +3856,6 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams) pblocktree->ReadReindexing(fReindexing); if(fReindexing) fReindex = true; - // Check whether we have a transaction index - pblocktree->ReadFlag("txindex", fTxIndex); - LogPrintf("%s: transaction index %s\n", __func__, fTxIndex ? "enabled" : "disabled"); - return true; } @@ -3856,6 +3871,7 @@ bool LoadChainTip(const CChainParams& chainparams) LogPrintf("%s: Connecting genesis block...\n", __func__); CValidationState state; if (!ActivateBestChain(state, chainparams)) { + LogPrintf("%s: failed to activate chain (%s)\n", __func__, FormatStateMessage(state)); return false; } } @@ -3903,14 +3919,14 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, int nGoodTransactions = 0; CValidationState state; int reportDone = 0; - LogPrintf("[0%%]..."); + LogPrintf("[0%%]..."); /* Continued */ for (CBlockIndex* 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) { // report every 10% step - LogPrintf("[%d%%]...", percentageDone); + LogPrintf("[%d%%]...", percentageDone); /* Continued */ reportDone = percentageDone/10; } uiInterface.ShowProgress(_("Verifying blocks..."), percentageDone, false); @@ -3970,7 +3986,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, if (!ReadBlockFromDisk(block, pindex, chainparams.GetConsensus())) return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); if (!g_chainstate.ConnectBlock(block, state, pindex, coins, chainparams)) - return error("VerifyDB(): *** found unconnectable block at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); + return error("VerifyDB(): *** found unconnectable block at %d, hash=%s (%s)", pindex->nHeight, pindex->GetBlockHash().ToString(), FormatStateMessage(state)); } } @@ -4078,6 +4094,9 @@ bool CChainState::RewindBlockIndex(const CChainParams& params) int nHeight = 1; while (nHeight <= chainActive.Height()) { + // Although SCRIPT_VERIFY_WITNESS is now generally enforced on all + // blocks in ConnectBlock, we don't need to go back and + // re-download/re-verify blocks from before segwit actually activated. if (IsWitnessEnabled(chainActive[nHeight - 1], params.GetConsensus()) && !(chainActive[nHeight]->nStatus & BLOCK_OPT_WITNESS)) { break; } @@ -4097,11 +4116,13 @@ bool CChainState::RewindBlockIndex(const CChainParams& params) break; } if (!DisconnectTip(state, params, nullptr)) { - return error("RewindBlockIndex: unable to disconnect block at height %i", pindex->nHeight); + return error("RewindBlockIndex: unable to disconnect block at height %i (%s)", pindex->nHeight, FormatStateMessage(state)); } // Occasionally flush state to disk. - if (!FlushStateToDisk(params, state, FlushStateMode::PERIODIC)) + if (!FlushStateToDisk(params, state, FlushStateMode::PERIODIC)) { + LogPrintf("RewindBlockIndex: unable to flush state to disk (%s)\n", FormatStateMessage(state)); return false; + } } // Reduce validity flag and have-data flags. @@ -4167,6 +4188,7 @@ bool RewindBlockIndex(const CChainParams& params) { // it'll get called a bunch real soon. CValidationState state; if (!FlushStateToDisk(params, state, FlushStateMode::ALWAYS)) { + LogPrintf("RewindBlockIndex: unable to flush state to disk (%s)\n", FormatStateMessage(state)); return false; } } @@ -4176,7 +4198,7 @@ bool RewindBlockIndex(const CChainParams& params) { void CChainState::UnloadBlockIndex() { nBlockSequenceId = 1; - g_failed_blocks.clear(); + m_failed_blocks.clear(); setBlockIndexCandidates.clear(); } @@ -4227,9 +4249,6 @@ bool LoadBlockIndex(const CChainParams& chainparams) // needs_init. LogPrintf("Initializing databases...\n"); - // Use the provided setting for -txindex in the new database - fTxIndex = gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX); - pblocktree->WriteFlag("txindex", fTxIndex); } return true; } @@ -4253,7 +4272,7 @@ bool CChainState::LoadGenesisBlock(const CChainParams& chainparams) CBlockIndex *pindex = AddToBlockIndex(block); CValidationState state; if (!ReceivedBlockTransactions(block, state, pindex, blockPos, chainparams.GetConsensus())) - return error("%s: genesis block not accepted", __func__); + return error("%s: genesis block not accepted (%s)", __func__, FormatStateMessage(state)); } catch (const std::runtime_error& e) { return error("%s: failed to write genesis block: %s", __func__, e.what()); } @@ -4718,7 +4737,8 @@ bool DumpMempool(void) } file << mapDeltas; - FileCommit(file.Get()); + if (!FileCommit(file.Get())) + throw std::runtime_error("FileCommit failed"); file.fclose(); RenameOver(GetDataDir() / "mempool.dat.new", GetDataDir() / "mempool.dat"); int64_t last = GetTimeMicros(); diff --git a/src/validation.h b/src/validation.h index dad6858b1e..9b40100765 100644 --- a/src/validation.h +++ b/src/validation.h @@ -165,12 +165,12 @@ extern BlockMap& mapBlockIndex; extern uint64_t nLastBlockTx; extern uint64_t nLastBlockWeight; extern const std::string strMessageMagic; -extern CWaitableCriticalSection csBestBlock; -extern CConditionVariable cvBlockChange; +extern CWaitableCriticalSection g_best_block_mutex; +extern CConditionVariable g_best_block_cv; +extern uint256 g_best_block; extern std::atomic_bool fImporting; extern std::atomic_bool fReindex; extern int nScriptCheckThreads; -extern bool fTxIndex; extern bool fIsBareMultisigStd; extern bool fRequireStandard; extern bool fCheckBlockIndex; @@ -410,6 +410,9 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, /** Check whether witness commitments are required for block. */ bool IsWitnessEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& params); +/** Check whether NULLDUMMY (BIP 147) has activated. */ +bool IsNullDummyEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& params); + /** When there are blocks in the active chain with missing data, rewind the chainstate and remove them from the block index */ bool RewindBlockIndex(const CChainParams& params); diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp index 928df4fa65..f328d2d14b 100644 --- a/src/validationinterface.cpp +++ b/src/validationinterface.cpp @@ -25,7 +25,7 @@ struct MainSignalsInstance { boost::signals2::signal<void (const std::shared_ptr<const CBlock> &, const CBlockIndex *pindex, const std::vector<CTransactionRef>&)> BlockConnected; boost::signals2::signal<void (const std::shared_ptr<const CBlock> &)> BlockDisconnected; boost::signals2::signal<void (const CTransactionRef &)> TransactionRemovedFromMempool; - boost::signals2::signal<void (const CBlockLocator &)> SetBestChain; + 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; @@ -80,7 +80,7 @@ void RegisterValidationInterface(CValidationInterface* pwalletIn) { g_signals.m_internals->BlockConnected.connect(boost::bind(&CValidationInterface::BlockConnected, pwalletIn, _1, _2, _3)); 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->SetBestChain.connect(boost::bind(&CValidationInterface::SetBestChain, 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)); @@ -91,7 +91,7 @@ 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->SetBestChain.disconnect(boost::bind(&CValidationInterface::SetBestChain, 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)); g_signals.m_internals->BlockDisconnected.disconnect(boost::bind(&CValidationInterface::BlockDisconnected, pwalletIn, _1)); @@ -107,7 +107,7 @@ 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->SetBestChain.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(); g_signals.m_internals->BlockDisconnected.disconnect_all_slots(); @@ -139,6 +139,10 @@ void CMainSignals::MempoolEntryRemoved(CTransactionRef ptx, MemPoolRemovalReason } void CMainSignals::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) { + // Dependencies exist that require UpdatedBlockTip events to be delivered in the order in which + // the chain actually updates. One way to ensure this is for the caller to invoke this signal + // in the same critical section where the chain is updated + m_internals->m_schedulerClient.AddToProcessQueue([pindexNew, pindexFork, fInitialDownload, this] { m_internals->UpdatedBlockTip(pindexNew, pindexFork, fInitialDownload); }); @@ -162,9 +166,9 @@ void CMainSignals::BlockDisconnected(const std::shared_ptr<const CBlock> &pblock }); } -void CMainSignals::SetBestChain(const CBlockLocator &locator) { +void CMainSignals::ChainStateFlushed(const CBlockLocator &locator) { m_internals->m_schedulerClient.AddToProcessQueue([locator, this] { - m_internals->SetBestChain(locator); + m_internals->ChainStateFlushed(locator); }); } diff --git a/src/validationinterface.h b/src/validationinterface.h index 63097166af..3a5fed0106 100644 --- a/src/validationinterface.h +++ b/src/validationinterface.h @@ -99,9 +99,20 @@ protected: /** * Notifies listeners of the new active block chain on-disk. * + * Prior to this callback, any updates are not guaranteed to persist on disk + * (ie clients need to handle shutdown/restart safety by being able to + * understand when some updates were lost due to unclean shutdown). + * + * When this callback is invoked, the validation changes done by any prior + * callback are guaranteed to exist on disk and survive a restart, including + * an unclean shutdown. + * + * Provides a locator describing the best chain, which is likely useful for + * storing current state on disk in client DBs. + * * Called on a background thread. */ - virtual void SetBestChain(const CBlockLocator &locator) {} + virtual void ChainStateFlushed(const CBlockLocator &locator) {} /** * Notifies listeners about an inventory item being seen on the network. * @@ -157,7 +168,7 @@ public: void TransactionAddedToMempool(const CTransactionRef &); 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 SetBestChain(const CBlockLocator &); + void ChainStateFlushed(const CBlockLocator &); void Inventory(const uint256 &); void Broadcast(int64_t nBestBlockTime, CConnman* connman); void BlockChecked(const CBlock&, const CValidationState&); diff --git a/src/wallet/coincontrol.h b/src/wallet/coincontrol.h index 52d6a291c9..2f08162ee4 100644 --- a/src/wallet/coincontrol.h +++ b/src/wallet/coincontrol.h @@ -26,12 +26,12 @@ public: bool fAllowWatchOnly; //! Override automatic min/max checks on fee, m_feerate must be set if true bool fOverrideFeeRate; - //! Override the default payTxFee if set + //! Override the wallet's m_pay_tx_fee if set boost::optional<CFeeRate> m_feerate; //! Override the default confirmation target if set boost::optional<unsigned int> m_confirm_target; - //! Signal BIP-125 replace by fee. - bool signalRbf; + //! Override the wallet's m_signal_rbf if set + boost::optional<bool> m_signal_bip125_rbf; //! Fee estimation mode to control arguments to estimateSmartFee FeeEstimateMode m_fee_mode; @@ -50,7 +50,7 @@ public: m_feerate.reset(); fOverrideFeeRate = false; m_confirm_target.reset(); - signalRbf = fWalletRbf; + m_signal_bip125_rbf.reset(); m_fee_mode = FeeEstimateMode::UNSET; } diff --git a/src/wallet/coinselection.cpp b/src/wallet/coinselection.cpp index 8596ad2adc..a403411e5b 100644 --- a/src/wallet/coinselection.cpp +++ b/src/wallet/coinselection.cpp @@ -286,10 +286,10 @@ bool KnapsackSolver(const CAmount& nTargetValue, std::vector<CInputCoin>& vCoins } if (LogAcceptCategory(BCLog::SELECTCOINS)) { - LogPrint(BCLog::SELECTCOINS, "SelectCoins() best subset: "); + LogPrint(BCLog::SELECTCOINS, "SelectCoins() best subset: "); /* Continued */ for (unsigned int i = 0; i < vValue.size(); i++) { if (vfBest[i]) { - LogPrint(BCLog::SELECTCOINS, "%s ", FormatMoney(vValue[i].txout.nValue)); + LogPrint(BCLog::SELECTCOINS, "%s ", FormatMoney(vValue[i].txout.nValue)); /* Continued */ } } LogPrint(BCLog::SELECTCOINS, "total %s\n", FormatMoney(nBest)); diff --git a/src/wallet/crypter.h b/src/wallet/crypter.h index fdeb4cfee0..4c0c8ff5ec 100644 --- a/src/wallet/crypter.h +++ b/src/wallet/crypter.h @@ -67,7 +67,7 @@ public: typedef std::vector<unsigned char, secure_allocator<unsigned char> > CKeyingMaterial; -namespace crypto_tests +namespace wallet_crypto_tests { class TestCrypter; } @@ -75,7 +75,7 @@ namespace crypto_tests /** Encryption/decryption context with key information */ class CCrypter { -friend class crypto_tests::TestCrypter; // for test access to chKey/chIV +friend class wallet_crypto_tests::TestCrypter; // for test access to chKey/chIV private: std::vector<unsigned char, secure_allocator<unsigned char>> vchKey; std::vector<unsigned char, secure_allocator<unsigned char>> vchIV; diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index 553cae4d02..10a06e4b9a 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -8,7 +8,6 @@ #include <addrman.h> #include <hash.h> #include <protocol.h> -#include <util.h> #include <utilstrencodings.h> #include <wallet/walletutil.h> @@ -30,14 +29,14 @@ namespace { //! (https://docs.oracle.com/cd/E17275_01/html/programmer_reference/program_copy.html), //! so bitcoin should never create different databases with the same fileid, but //! this error can be triggered if users manually copy database files. -void CheckUniqueFileid(const CDBEnv& env, const std::string& filename, Db& db) +void CheckUniqueFileid(const BerkeleyEnvironment& env, const std::string& filename, Db& db) { if (env.IsMock()) return; u_int8_t fileid[DB_FILE_ID_LEN]; int ret = db.get_mpf()->get_fileid(fileid); if (ret != 0) { - throw std::runtime_error(strprintf("CDB: Can't open database %s (get_fileid failed with %d)", filename, ret)); + throw std::runtime_error(strprintf("BerkeleyBatch: Can't open database %s (get_fileid failed with %d)", filename, ret)); } for (const auto& item : env.mapDb) { @@ -46,7 +45,7 @@ void CheckUniqueFileid(const CDBEnv& env, const std::string& filename, Db& db) memcmp(fileid, item_fileid, sizeof(fileid)) == 0) { const char* item_filename = nullptr; item.second->get_dbname(&item_filename, nullptr); - throw std::runtime_error(strprintf("CDB: Can't open database %s (duplicates fileid %s from %s)", filename, + throw std::runtime_error(strprintf("BerkeleyBatch: Can't open database %s (duplicates fileid %s from %s)", filename, HexStr(std::begin(item_fileid), std::end(item_fileid)), item_filename ? item_filename : "(unknown database)")); } @@ -54,10 +53,10 @@ void CheckUniqueFileid(const CDBEnv& env, const std::string& filename, Db& db) } CCriticalSection cs_db; -std::map<std::string, CDBEnv> g_dbenvs; //!< Map from directory name to open db environment. +std::map<std::string, BerkeleyEnvironment> g_dbenvs; //!< Map from directory name to open db environment. } // namespace -CDBEnv* GetWalletEnv(const fs::path& wallet_path, std::string& database_filename) +BerkeleyEnvironment* GetWalletEnv(const fs::path& wallet_path, std::string& database_filename) { fs::path env_directory; if (fs::is_regular_file(wallet_path)) { @@ -73,7 +72,7 @@ CDBEnv* GetWalletEnv(const fs::path& wallet_path, std::string& database_filename database_filename = "wallet.dat"; } LOCK(cs_db); - // Note: An ununsed temporary CDBEnv object may be created inside the + // Note: An ununsed temporary BerkeleyEnvironment object may be created inside the // emplace function if the key already exists. This is a little inefficient, // but not a big concern since the map will be changed in the future to hold // pointers instead of objects, anyway. @@ -81,10 +80,10 @@ CDBEnv* GetWalletEnv(const fs::path& wallet_path, std::string& database_filename } // -// CDB +// BerkeleyBatch // -void CDBEnv::Close() +void BerkeleyEnvironment::Close() { if (!fDbEnvInit) return; @@ -103,29 +102,29 @@ void CDBEnv::Close() int ret = dbenv->close(0); if (ret != 0) - LogPrintf("CDBEnv::EnvShutdown: Error %d shutting down database environment: %s\n", ret, DbEnv::strerror(ret)); + LogPrintf("BerkeleyEnvironment::EnvShutdown: Error %d shutting down database environment: %s\n", ret, DbEnv::strerror(ret)); if (!fMockDb) DbEnv((u_int32_t)0).remove(strPath.c_str(), 0); } -void CDBEnv::Reset() +void BerkeleyEnvironment::Reset() { dbenv.reset(new DbEnv(DB_CXX_NO_EXCEPTIONS)); fDbEnvInit = false; fMockDb = false; } -CDBEnv::CDBEnv(const fs::path& dir_path) : strPath(dir_path.string()) +BerkeleyEnvironment::BerkeleyEnvironment(const fs::path& dir_path) : strPath(dir_path.string()) { Reset(); } -CDBEnv::~CDBEnv() +BerkeleyEnvironment::~BerkeleyEnvironment() { Close(); } -bool CDBEnv::Open(bool retry) +bool BerkeleyEnvironment::Open(bool retry) { if (fDbEnvInit) return true; @@ -142,7 +141,7 @@ bool CDBEnv::Open(bool retry) fs::path pathLogDir = pathIn / "database"; TryCreateDirectories(pathLogDir); fs::path pathErrorFile = pathIn / "db.log"; - LogPrintf("CDBEnv::Open: LogDir=%s ErrorFile=%s\n", pathLogDir.string(), pathErrorFile.string()); + LogPrintf("BerkeleyEnvironment::Open: LogDir=%s ErrorFile=%s\n", pathLogDir.string(), pathErrorFile.string()); unsigned int nEnvFlags = 0; if (gArgs.GetBoolArg("-privdb", DEFAULT_WALLET_PRIVDB)) @@ -170,7 +169,7 @@ bool CDBEnv::Open(bool retry) S_IRUSR | S_IWUSR); if (ret != 0) { dbenv->close(0); - LogPrintf("CDBEnv::Open: Error %d opening database environment: %s\n", ret, DbEnv::strerror(ret)); + LogPrintf("BerkeleyEnvironment::Open: Error %d opening database environment: %s\n", ret, DbEnv::strerror(ret)); if (retry) { // try moving the database env out of the way fs::path pathDatabaseBak = pathIn / strprintf("database.%d.bak", GetTime()); @@ -195,14 +194,14 @@ bool CDBEnv::Open(bool retry) return true; } -void CDBEnv::MakeMock() +void BerkeleyEnvironment::MakeMock() { if (fDbEnvInit) - throw std::runtime_error("CDBEnv::MakeMock: Already initialized"); + throw std::runtime_error("BerkeleyEnvironment::MakeMock: Already initialized"); boost::this_thread::interruption_point(); - LogPrint(BCLog::DB, "CDBEnv::MakeMock\n"); + LogPrint(BCLog::DB, "BerkeleyEnvironment::MakeMock\n"); dbenv->set_cachesize(1, 0, 1); dbenv->set_lg_bsize(10485760 * 4); @@ -221,13 +220,13 @@ void CDBEnv::MakeMock() DB_PRIVATE, S_IRUSR | S_IWUSR); if (ret > 0) - throw std::runtime_error(strprintf("CDBEnv::MakeMock: Error %d opening database environment.", ret)); + throw std::runtime_error(strprintf("BerkeleyEnvironment::MakeMock: Error %d opening database environment.", ret)); fDbEnvInit = true; fMockDb = true; } -CDBEnv::VerifyResult CDBEnv::Verify(const std::string& strFile, recoverFunc_type recoverFunc, std::string& out_backup_filename) +BerkeleyEnvironment::VerifyResult BerkeleyEnvironment::Verify(const std::string& strFile, recoverFunc_type recoverFunc, std::string& out_backup_filename) { LOCK(cs_db); assert(mapFileUseCount.count(strFile) == 0); @@ -244,10 +243,10 @@ CDBEnv::VerifyResult CDBEnv::Verify(const std::string& strFile, recoverFunc_type return (fRecovered ? VerifyResult::RECOVER_OK : VerifyResult::RECOVER_FAIL); } -bool CDB::Recover(const fs::path& file_path, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& newFilename) +bool BerkeleyBatch::Recover(const fs::path& file_path, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& newFilename) { std::string filename; - CDBEnv* env = GetWalletEnv(file_path, filename); + BerkeleyEnvironment* env = GetWalletEnv(file_path, filename); // Recovery procedure: // move wallet file to walletfilename.timestamp.bak @@ -269,7 +268,7 @@ bool CDB::Recover(const fs::path& file_path, void *callbackDataIn, bool (*recove return false; } - std::vector<CDBEnv::KeyValPair> salvagedData; + std::vector<BerkeleyEnvironment::KeyValPair> salvagedData; bool fSuccess = env->Salvage(newFilename, true, salvagedData); if (salvagedData.empty()) { @@ -292,7 +291,7 @@ bool CDB::Recover(const fs::path& file_path, void *callbackDataIn, bool (*recove } DbTxn* ptxn = env->TxnBegin(); - for (CDBEnv::KeyValPair& row : salvagedData) + for (BerkeleyEnvironment::KeyValPair& row : salvagedData) { if (recoverKVcallback) { @@ -313,10 +312,10 @@ bool CDB::Recover(const fs::path& file_path, void *callbackDataIn, bool (*recove return fSuccess; } -bool CDB::VerifyEnvironment(const fs::path& file_path, std::string& errorStr) +bool BerkeleyBatch::VerifyEnvironment(const fs::path& file_path, std::string& errorStr) { std::string walletFile; - CDBEnv* env = GetWalletEnv(file_path, walletFile); + BerkeleyEnvironment* env = GetWalletEnv(file_path, walletFile); fs::path walletDir = env->Directory(); LogPrintf("Using BerkeleyDB version %s\n", DbEnv::version(0, 0, 0)); @@ -337,17 +336,17 @@ bool CDB::VerifyEnvironment(const fs::path& file_path, std::string& errorStr) return true; } -bool CDB::VerifyDatabaseFile(const fs::path& file_path, std::string& warningStr, std::string& errorStr, CDBEnv::recoverFunc_type recoverFunc) +bool BerkeleyBatch::VerifyDatabaseFile(const fs::path& file_path, std::string& warningStr, std::string& errorStr, BerkeleyEnvironment::recoverFunc_type recoverFunc) { std::string walletFile; - CDBEnv* env = GetWalletEnv(file_path, walletFile); + BerkeleyEnvironment* env = GetWalletEnv(file_path, walletFile); fs::path walletDir = env->Directory(); if (fs::exists(walletDir / walletFile)) { std::string backup_filename; - CDBEnv::VerifyResult r = env->Verify(walletFile, recoverFunc, backup_filename); - if (r == CDBEnv::VerifyResult::RECOVER_OK) + BerkeleyEnvironment::VerifyResult r = env->Verify(walletFile, recoverFunc, backup_filename); + if (r == BerkeleyEnvironment::VerifyResult::RECOVER_OK) { warningStr = strprintf(_("Warning: Wallet file corrupt, data salvaged!" " Original %s saved as %s in %s; if" @@ -355,7 +354,7 @@ bool CDB::VerifyDatabaseFile(const fs::path& file_path, std::string& warningStr, " restore from a backup."), walletFile, backup_filename, walletDir); } - if (r == CDBEnv::VerifyResult::RECOVER_FAIL) + if (r == BerkeleyEnvironment::VerifyResult::RECOVER_FAIL) { errorStr = strprintf(_("%s corrupt, salvage failed"), walletFile); return false; @@ -370,7 +369,7 @@ static const char *HEADER_END = "HEADER=END"; /* End of key/value data */ static const char *DATA_END = "DATA=END"; -bool CDBEnv::Salvage(const std::string& strFile, bool fAggressive, std::vector<CDBEnv::KeyValPair>& vResult) +bool BerkeleyEnvironment::Salvage(const std::string& strFile, bool fAggressive, std::vector<BerkeleyEnvironment::KeyValPair>& vResult) { LOCK(cs_db); assert(mapFileUseCount.count(strFile) == 0); @@ -384,14 +383,14 @@ bool CDBEnv::Salvage(const std::string& strFile, bool fAggressive, std::vector<C Db db(dbenv.get(), 0); int result = db.verify(strFile.c_str(), nullptr, &strDump, flags); if (result == DB_VERIFY_BAD) { - LogPrintf("CDBEnv::Salvage: Database salvage found errors, all data may not be recoverable.\n"); + LogPrintf("BerkeleyEnvironment::Salvage: Database salvage found errors, all data may not be recoverable.\n"); if (!fAggressive) { - LogPrintf("CDBEnv::Salvage: Rerun with aggressive mode to ignore errors and continue.\n"); + LogPrintf("BerkeleyEnvironment::Salvage: Rerun with aggressive mode to ignore errors and continue.\n"); return false; } } if (result != 0 && result != DB_VERIFY_BAD) { - LogPrintf("CDBEnv::Salvage: Database salvage failed with result %d.\n", result); + LogPrintf("BerkeleyEnvironment::Salvage: Database salvage failed with result %d.\n", result); return false; } @@ -415,7 +414,7 @@ bool CDBEnv::Salvage(const std::string& strFile, bool fAggressive, std::vector<C break; getline(strDump, valueHex); if (valueHex == DATA_END) { - LogPrintf("CDBEnv::Salvage: WARNING: Number of keys in data does not match number of values.\n"); + LogPrintf("BerkeleyEnvironment::Salvage: WARNING: Number of keys in data does not match number of values.\n"); break; } vResult.push_back(make_pair(ParseHex(keyHex), ParseHex(valueHex))); @@ -423,7 +422,7 @@ bool CDBEnv::Salvage(const std::string& strFile, bool fAggressive, std::vector<C } if (keyHex != DATA_END) { - LogPrintf("CDBEnv::Salvage: WARNING: Unexpected end of file while reading salvage output.\n"); + LogPrintf("BerkeleyEnvironment::Salvage: WARNING: Unexpected end of file while reading salvage output.\n"); return false; } @@ -431,7 +430,7 @@ bool CDBEnv::Salvage(const std::string& strFile, bool fAggressive, std::vector<C } -void CDBEnv::CheckpointLSN(const std::string& strFile) +void BerkeleyEnvironment::CheckpointLSN(const std::string& strFile) { dbenv->txn_checkpoint(0, 0, 0); if (fMockDb) @@ -440,15 +439,15 @@ void CDBEnv::CheckpointLSN(const std::string& strFile) } -CDB::CDB(CWalletDBWrapper& dbw, const char* pszMode, bool fFlushOnCloseIn) : pdb(nullptr), activeTxn(nullptr) +BerkeleyBatch::BerkeleyBatch(BerkeleyDatabase& database, const char* pszMode, bool fFlushOnCloseIn) : pdb(nullptr), activeTxn(nullptr) { fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w')); fFlushOnClose = fFlushOnCloseIn; - env = dbw.env; - if (dbw.IsDummy()) { + env = database.env; + if (database.IsDummy()) { return; } - const std::string &strFilename = dbw.strFile; + const std::string &strFilename = database.strFile; bool fCreate = strchr(pszMode, 'c') != nullptr; unsigned int nFlags = DB_THREAD; @@ -458,7 +457,7 @@ CDB::CDB(CWalletDBWrapper& dbw, const char* pszMode, bool fFlushOnCloseIn) : pdb { LOCK(cs_db); if (!env->Open(false /* retry */)) - throw std::runtime_error("CDB: Failed to open database environment."); + throw std::runtime_error("BerkeleyBatch: Failed to open database environment."); pdb = env->mapDb[strFilename]; if (pdb == nullptr) { @@ -470,7 +469,7 @@ CDB::CDB(CWalletDBWrapper& dbw, const char* pszMode, bool fFlushOnCloseIn) : pdb DbMpoolFile* mpf = pdb_temp->get_mpf(); ret = mpf->set_flags(DB_MPOOL_NOFILE, 1); if (ret != 0) { - throw std::runtime_error(strprintf("CDB: Failed to configure for no temp file backing for database %s", strFilename)); + throw std::runtime_error(strprintf("BerkeleyBatch: Failed to configure for no temp file backing for database %s", strFilename)); } } @@ -482,7 +481,7 @@ CDB::CDB(CWalletDBWrapper& dbw, const char* pszMode, bool fFlushOnCloseIn) : pdb 0); if (ret != 0) { - throw std::runtime_error(strprintf("CDB: Error %d, can't open database %s", ret, strFilename)); + throw std::runtime_error(strprintf("BerkeleyBatch: Error %d, can't open database %s", ret, strFilename)); } // Call CheckUniqueFileid on the containing BDB environment to @@ -519,7 +518,7 @@ CDB::CDB(CWalletDBWrapper& dbw, const char* pszMode, bool fFlushOnCloseIn) : pdb } } -void CDB::Flush() +void BerkeleyBatch::Flush() { if (activeTxn) return; @@ -532,12 +531,12 @@ void CDB::Flush() env->dbenv->txn_checkpoint(nMinutes ? gArgs.GetArg("-dblogsize", DEFAULT_WALLET_DBLOGSIZE) * 1024 : 0, nMinutes, 0); } -void CWalletDBWrapper::IncrementUpdateCounter() +void BerkeleyDatabase::IncrementUpdateCounter() { ++nUpdateCounter; } -void CDB::Close() +void BerkeleyBatch::Close() { if (!pdb) return; @@ -555,7 +554,7 @@ void CDB::Close() } } -void CDBEnv::CloseDb(const std::string& strFile) +void BerkeleyEnvironment::CloseDb(const std::string& strFile) { { LOCK(cs_db); @@ -569,13 +568,13 @@ void CDBEnv::CloseDb(const std::string& strFile) } } -bool CDB::Rewrite(CWalletDBWrapper& dbw, const char* pszSkip) +bool BerkeleyBatch::Rewrite(BerkeleyDatabase& database, const char* pszSkip) { - if (dbw.IsDummy()) { + if (database.IsDummy()) { return true; } - CDBEnv *env = dbw.env; - const std::string& strFile = dbw.strFile; + BerkeleyEnvironment *env = database.env; + const std::string& strFile = database.strFile; while (true) { { LOCK(cs_db); @@ -586,10 +585,10 @@ bool CDB::Rewrite(CWalletDBWrapper& dbw, const char* pszSkip) env->mapFileUseCount.erase(strFile); bool fSuccess = true; - LogPrintf("CDB::Rewrite: Rewriting %s...\n", strFile); + LogPrintf("BerkeleyBatch::Rewrite: Rewriting %s...\n", strFile); std::string strFileRes = strFile + ".rewrite"; { // surround usage of db with extra {} - CDB db(dbw, "r"); + BerkeleyBatch db(database, "r"); std::unique_ptr<Db> pdbCopy = MakeUnique<Db>(env->dbenv.get(), 0); int ret = pdbCopy->open(nullptr, // Txn pointer @@ -599,7 +598,7 @@ bool CDB::Rewrite(CWalletDBWrapper& dbw, const char* pszSkip) DB_CREATE, // Flags 0); if (ret > 0) { - LogPrintf("CDB::Rewrite: Can't create database file %s\n", strFileRes); + LogPrintf("BerkeleyBatch::Rewrite: Can't create database file %s\n", strFileRes); fSuccess = false; } @@ -649,7 +648,7 @@ bool CDB::Rewrite(CWalletDBWrapper& dbw, const char* pszSkip) fSuccess = false; } if (!fSuccess) - LogPrintf("CDB::Rewrite: Failed to rewrite database file %s\n", strFileRes); + LogPrintf("BerkeleyBatch::Rewrite: Failed to rewrite database file %s\n", strFileRes); return fSuccess; } } @@ -658,11 +657,11 @@ bool CDB::Rewrite(CWalletDBWrapper& dbw, const char* pszSkip) } -void CDBEnv::Flush(bool fShutdown) +void BerkeleyEnvironment::Flush(bool fShutdown) { int64_t nStart = GetTimeMillis(); // Flush log data to the actual data file on all files that are not in use - LogPrint(BCLog::DB, "CDBEnv::Flush: Flush(%s)%s\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started"); + LogPrint(BCLog::DB, "BerkeleyEnvironment::Flush: Flush(%s)%s\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started"); if (!fDbEnvInit) return; { @@ -671,21 +670,21 @@ void CDBEnv::Flush(bool fShutdown) while (mi != mapFileUseCount.end()) { std::string strFile = (*mi).first; int nRefCount = (*mi).second; - LogPrint(BCLog::DB, "CDBEnv::Flush: Flushing %s (refcount = %d)...\n", strFile, nRefCount); + LogPrint(BCLog::DB, "BerkeleyEnvironment::Flush: Flushing %s (refcount = %d)...\n", strFile, nRefCount); if (nRefCount == 0) { // Move log data to the dat file CloseDb(strFile); - LogPrint(BCLog::DB, "CDBEnv::Flush: %s checkpoint\n", strFile); + LogPrint(BCLog::DB, "BerkeleyEnvironment::Flush: %s checkpoint\n", strFile); dbenv->txn_checkpoint(0, 0, 0); - LogPrint(BCLog::DB, "CDBEnv::Flush: %s detach\n", strFile); + LogPrint(BCLog::DB, "BerkeleyEnvironment::Flush: %s detach\n", strFile); if (!fMockDb) dbenv->lsn_reset(strFile.c_str(), 0); - LogPrint(BCLog::DB, "CDBEnv::Flush: %s closed\n", strFile); + LogPrint(BCLog::DB, "BerkeleyEnvironment::Flush: %s closed\n", strFile); mapFileUseCount.erase(mi++); } else mi++; } - LogPrint(BCLog::DB, "CDBEnv::Flush: Flush(%s)%s took %15dms\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started", GetTimeMillis() - nStart); + LogPrint(BCLog::DB, "BerkeleyEnvironment::Flush: Flush(%s)%s took %15dms\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started", GetTimeMillis() - nStart); if (fShutdown) { char** listp; if (mapFileUseCount.empty()) { @@ -698,14 +697,14 @@ void CDBEnv::Flush(bool fShutdown) } } -bool CDB::PeriodicFlush(CWalletDBWrapper& dbw) +bool BerkeleyBatch::PeriodicFlush(BerkeleyDatabase& database) { - if (dbw.IsDummy()) { + if (database.IsDummy()) { return true; } bool ret = false; - CDBEnv *env = dbw.env; - const std::string& strFile = dbw.strFile; + BerkeleyEnvironment *env = database.env; + const std::string& strFile = database.strFile; TRY_LOCK(cs_db, lockDb); if (lockDb) { @@ -741,12 +740,12 @@ bool CDB::PeriodicFlush(CWalletDBWrapper& dbw) return ret; } -bool CWalletDBWrapper::Rewrite(const char* pszSkip) +bool BerkeleyDatabase::Rewrite(const char* pszSkip) { - return CDB::Rewrite(*this, pszSkip); + return BerkeleyBatch::Rewrite(*this, pszSkip); } -bool CWalletDBWrapper::Backup(const std::string& strDest) +bool BerkeleyDatabase::Backup(const std::string& strDest) { if (IsDummy()) { return false; @@ -787,7 +786,7 @@ bool CWalletDBWrapper::Backup(const std::string& strDest) } } -void CWalletDBWrapper::Flush(bool shutdown) +void BerkeleyDatabase::Flush(bool shutdown) { if (!IsDummy()) { env->Flush(shutdown); diff --git a/src/wallet/db.h b/src/wallet/db.h index 49a9f3f082..5e61280f7a 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -25,7 +25,7 @@ static const unsigned int DEFAULT_WALLET_DBLOGSIZE = 100; static const bool DEFAULT_WALLET_PRIVDB = true; -class CDBEnv +class BerkeleyEnvironment { private: bool fDbEnvInit; @@ -39,8 +39,8 @@ public: std::map<std::string, int> mapFileUseCount; std::map<std::string, Db*> mapDb; - CDBEnv(const fs::path& env_directory); - ~CDBEnv(); + BerkeleyEnvironment(const fs::path& env_directory); + ~BerkeleyEnvironment(); void Reset(); void MakeMock(); @@ -86,23 +86,23 @@ public: } }; -/** Get CDBEnv and database filename given a wallet path. */ -CDBEnv* GetWalletEnv(const fs::path& wallet_path, std::string& database_filename); +/** Get BerkeleyEnvironment and database filename given a wallet path. */ +BerkeleyEnvironment* GetWalletEnv(const fs::path& wallet_path, std::string& database_filename); /** An instance of this class represents one database. * For BerkeleyDB this is just a (env, strFile) tuple. **/ -class CWalletDBWrapper +class BerkeleyDatabase { - friend class CDB; + friend class BerkeleyBatch; public: /** Create dummy DB handle */ - CWalletDBWrapper() : nUpdateCounter(0), nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0), env(nullptr) + BerkeleyDatabase() : nUpdateCounter(0), nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0), env(nullptr) { } /** Create DB handle to real database */ - CWalletDBWrapper(const fs::path& wallet_path, bool mock = false) : + BerkeleyDatabase(const fs::path& wallet_path, bool mock = false) : nUpdateCounter(0), nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0) { env = GetWalletEnv(wallet_path, strFile); @@ -114,21 +114,21 @@ public: } /** Return object for accessing database at specified path. */ - static std::unique_ptr<CWalletDBWrapper> Create(const fs::path& path) + static std::unique_ptr<BerkeleyDatabase> Create(const fs::path& path) { - return MakeUnique<CWalletDBWrapper>(path); + return MakeUnique<BerkeleyDatabase>(path); } /** Return object for accessing dummy database with no read/write capabilities. */ - static std::unique_ptr<CWalletDBWrapper> CreateDummy() + static std::unique_ptr<BerkeleyDatabase> CreateDummy() { - return MakeUnique<CWalletDBWrapper>(); + return MakeUnique<BerkeleyDatabase>(); } /** Return object for accessing temporary in-memory database. */ - static std::unique_ptr<CWalletDBWrapper> CreateMock() + static std::unique_ptr<BerkeleyDatabase> CreateMock() { - return MakeUnique<CWalletDBWrapper>("", true /* mock */); + return MakeUnique<BerkeleyDatabase>("", true /* mock */); } /** Rewrite the entire database on disk, with the exception of key pszSkip if non-zero @@ -152,7 +152,7 @@ public: private: /** BerkeleyDB specific */ - CDBEnv *env; + BerkeleyEnvironment *env; std::string strFile; /** Return whether this database handle is a dummy for testing. @@ -164,7 +164,7 @@ private: /** RAII class that provides access to a Berkeley database */ -class CDB +class BerkeleyBatch { protected: Db* pdb; @@ -172,14 +172,14 @@ protected: DbTxn* activeTxn; bool fReadOnly; bool fFlushOnClose; - CDBEnv *env; + BerkeleyEnvironment *env; public: - explicit CDB(CWalletDBWrapper& dbw, const char* pszMode = "r+", bool fFlushOnCloseIn=true); - ~CDB() { Close(); } + explicit BerkeleyBatch(BerkeleyDatabase& database, const char* pszMode = "r+", bool fFlushOnCloseIn=true); + ~BerkeleyBatch() { Close(); } - CDB(const CDB&) = delete; - CDB& operator=(const CDB&) = delete; + BerkeleyBatch(const BerkeleyBatch&) = delete; + BerkeleyBatch& operator=(const BerkeleyBatch&) = delete; void Flush(); void Close(); @@ -187,11 +187,11 @@ public: /* flush the wallet passively (TRY_LOCK) ideal to be called periodically */ - static bool PeriodicFlush(CWalletDBWrapper& dbw); + static bool PeriodicFlush(BerkeleyDatabase& database); /* verifies the database environment */ static bool VerifyEnvironment(const fs::path& file_path, std::string& errorStr); /* verifies the database file */ - static bool VerifyDatabaseFile(const fs::path& file_path, std::string& warningStr, std::string& errorStr, CDBEnv::recoverFunc_type recoverFunc); + static bool VerifyDatabaseFile(const fs::path& file_path, std::string& warningStr, std::string& errorStr, BerkeleyEnvironment::recoverFunc_type recoverFunc); public: template <typename K, typename T> @@ -387,7 +387,7 @@ public: return Write(std::string("version"), nVersion); } - bool static Rewrite(CWalletDBWrapper& dbw, const char* pszSkip = nullptr); + bool static Rewrite(BerkeleyDatabase& database, const char* pszSkip = nullptr); }; #endif // BITCOIN_WALLET_DB_H diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp index 82a5017de0..4d70dde72a 100644 --- a/src/wallet/feebumper.cpp +++ b/src/wallet/feebumper.cpp @@ -134,7 +134,7 @@ Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoin FormatMoney(minTotalFee), FormatMoney(nOldFeeRate.GetFee(maxNewTxSize)), FormatMoney(::incrementalRelayFee.GetFee(maxNewTxSize)))); return Result::INVALID_PARAMETER; } - CAmount requiredFee = GetRequiredFee(maxNewTxSize); + CAmount requiredFee = GetRequiredFee(*wallet, maxNewTxSize); if (total_fee < requiredFee) { errors.push_back(strprintf("Insufficient totalFee (cannot be less than required fee %s)", FormatMoney(requiredFee))); @@ -143,7 +143,7 @@ Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoin new_fee = total_fee; nNewFeeRate = CFeeRate(total_fee, maxNewTxSize); } else { - new_fee = GetMinimumFee(maxNewTxSize, coin_control, mempool, ::feeEstimator, nullptr /* FeeCalculation */); + new_fee = GetMinimumFee(*wallet, maxNewTxSize, coin_control, mempool, ::feeEstimator, nullptr /* FeeCalculation */); nNewFeeRate = CFeeRate(new_fee, maxNewTxSize); // New fee rate must be at least old rate + minimum incremental relay rate @@ -194,14 +194,14 @@ Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoin // If the output would become dust, discard it (converting the dust to fee) poutput->nValue -= nDelta; - if (poutput->nValue <= GetDustThreshold(*poutput, ::dustRelayFee)) { + if (poutput->nValue <= GetDustThreshold(*poutput, GetDiscardRate(*wallet, ::feeEstimator))) { LogPrint(BCLog::RPC, "Bumping fee and discarding dust output\n"); new_fee += poutput->nValue; mtx.vout.erase(mtx.vout.begin() + nOutput); } // Mark new tx not replaceable, if requested. - if (!coin_control.signalRbf) { + if (!coin_control.m_signal_bip125_rbf.get_value_or(wallet->m_signal_rbf)) { for (auto& input : mtx.vin) { if (input.nSequence < 0xfffffffe) input.nSequence = 0xfffffffe; } diff --git a/src/wallet/fees.cpp b/src/wallet/fees.cpp index 03c32d3b97..8d576a6689 100644 --- a/src/wallet/fees.cpp +++ b/src/wallet/fees.cpp @@ -13,15 +13,15 @@ #include <wallet/wallet.h> -CAmount GetRequiredFee(unsigned int nTxBytes) +CAmount GetRequiredFee(const CWallet& wallet, unsigned int nTxBytes) { - return std::max(CWallet::minTxFee.GetFee(nTxBytes), ::minRelayTxFee.GetFee(nTxBytes)); + return GetRequiredFeeRate(wallet).GetFee(nTxBytes); } -CAmount GetMinimumFee(unsigned int nTxBytes, const CCoinControl& coin_control, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation *feeCalc) +CAmount GetMinimumFee(const CWallet& wallet, unsigned int nTxBytes, const CCoinControl& coin_control, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation* feeCalc) { - CAmount fee_needed = GetMinimumFeeRate(coin_control, pool, estimator, feeCalc).GetFee(nTxBytes); + CAmount fee_needed = GetMinimumFeeRate(wallet, coin_control, pool, estimator, feeCalc).GetFee(nTxBytes); // Always obey the maximum if (fee_needed > maxTxFee) { fee_needed = maxTxFee; @@ -30,48 +30,48 @@ CAmount GetMinimumFee(unsigned int nTxBytes, const CCoinControl& coin_control, c return fee_needed; } -CFeeRate GetRequiredFeeRate() +CFeeRate GetRequiredFeeRate(const CWallet& wallet) { - return std::max(CWallet::minTxFee, ::minRelayTxFee); + return std::max(wallet.m_min_fee, ::minRelayTxFee); } -CFeeRate GetMinimumFeeRate(const CCoinControl& coin_control, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation *feeCalc) +CFeeRate GetMinimumFeeRate(const CWallet& wallet, const CCoinControl& coin_control, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation* feeCalc) { /* User control of how to calculate fee uses the following parameter precedence: 1. coin_control.m_feerate 2. coin_control.m_confirm_target - 3. payTxFee (user-set global variable) - 4. nTxConfirmTarget (user-set global variable) + 3. m_pay_tx_fee (user-set member variable of wallet) + 4. m_confirm_target (user-set member variable of wallet) The first parameter that is set is used. */ - CFeeRate feerate_needed ; + CFeeRate feerate_needed; if (coin_control.m_feerate) { // 1. feerate_needed = *(coin_control.m_feerate); if (feeCalc) feeCalc->reason = FeeReason::PAYTXFEE; // Allow to override automatic min/max check over coin control instance if (coin_control.fOverrideFeeRate) return feerate_needed; } - else if (!coin_control.m_confirm_target && ::payTxFee != CFeeRate(0)) { // 3. TODO: remove magic value of 0 for global payTxFee - feerate_needed = ::payTxFee; + else if (!coin_control.m_confirm_target && wallet.m_pay_tx_fee != CFeeRate(0)) { // 3. TODO: remove magic value of 0 for wallet member m_pay_tx_fee + feerate_needed = wallet.m_pay_tx_fee; if (feeCalc) feeCalc->reason = FeeReason::PAYTXFEE; } else { // 2. or 4. // We will use smart fee estimation - unsigned int target = coin_control.m_confirm_target ? *coin_control.m_confirm_target : ::nTxConfirmTarget; + unsigned int target = coin_control.m_confirm_target ? *coin_control.m_confirm_target : wallet.m_confirm_target; // By default estimates are economical iff we are signaling opt-in-RBF - bool conservative_estimate = !coin_control.signalRbf; + bool conservative_estimate = !coin_control.m_signal_bip125_rbf.get_value_or(wallet.m_signal_rbf); // Allow to override the default fee estimate mode over the CoinControl instance if (coin_control.m_fee_mode == FeeEstimateMode::CONSERVATIVE) conservative_estimate = true; else if (coin_control.m_fee_mode == FeeEstimateMode::ECONOMICAL) conservative_estimate = false; feerate_needed = estimator.estimateSmartFee(target, feeCalc, conservative_estimate); if (feerate_needed == CFeeRate(0)) { - // if we don't have enough data for estimateSmartFee, then use fallbackFee - feerate_needed = CWallet::fallbackFee; + // if we don't have enough data for estimateSmartFee, then use fallback fee + feerate_needed = wallet.m_fallback_fee; if (feeCalc) feeCalc->reason = FeeReason::FALLBACK; // directly return if fallback fee is disabled (feerate 0 == disabled) - if (CWallet::fallbackFee == CFeeRate(0)) return feerate_needed; + if (wallet.m_fallback_fee == CFeeRate(0)) return feerate_needed; } // Obey mempool min fee when using smart fee estimation CFeeRate min_mempool_feerate = pool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000); @@ -81,8 +81,8 @@ CFeeRate GetMinimumFeeRate(const CCoinControl& coin_control, const CTxMemPool& p } } - // prevent user from paying a fee below minRelayTxFee or minTxFee - CFeeRate required_feerate = GetRequiredFeeRate(); + // prevent user from paying a fee below the required fee rate + CFeeRate required_feerate = GetRequiredFeeRate(wallet); if (required_feerate > feerate_needed) { feerate_needed = required_feerate; if (feeCalc) feeCalc->reason = FeeReason::REQUIRED; @@ -90,12 +90,12 @@ CFeeRate GetMinimumFeeRate(const CCoinControl& coin_control, const CTxMemPool& p return feerate_needed; } -CFeeRate GetDiscardRate(const CBlockPolicyEstimator& estimator) +CFeeRate GetDiscardRate(const CWallet& wallet, const CBlockPolicyEstimator& estimator) { unsigned int highest_target = estimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE); CFeeRate discard_rate = estimator.estimateSmartFee(highest_target, nullptr /* FeeCalculation */, false /* conservative */); // Don't let discard_rate be greater than longest possible fee estimate if we get a valid fee estimate - discard_rate = (discard_rate == CFeeRate(0)) ? CWallet::m_discard_rate : std::min(discard_rate, CWallet::m_discard_rate); + discard_rate = (discard_rate == CFeeRate(0)) ? wallet.m_discard_rate : std::min(discard_rate, wallet.m_discard_rate); // Discard rate must be at least dustRelayFee discard_rate = std::max(discard_rate, ::dustRelayFee); return discard_rate; diff --git a/src/wallet/fees.h b/src/wallet/fees.h index a627af70b0..b3cd064abc 100644 --- a/src/wallet/fees.h +++ b/src/wallet/fees.h @@ -12,35 +12,36 @@ class CBlockPolicyEstimator; class CCoinControl; class CFeeRate; class CTxMemPool; +class CWallet; struct FeeCalculation; /** - * Return the minimum required fee taking into account the - * floating relay fee and user set minimum transaction fee + * Return the minimum required absolute fee for this size + * based on the required fee rate */ -CAmount GetRequiredFee(unsigned int nTxBytes); +CAmount GetRequiredFee(const CWallet& wallet, unsigned int nTxBytes); /** * Estimate the minimum fee considering user set parameters * and the required fee */ -CAmount GetMinimumFee(unsigned int nTxBytes, const CCoinControl& coin_control, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation *feeCalc); +CAmount GetMinimumFee(const CWallet& wallet, unsigned int nTxBytes, const CCoinControl& coin_control, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation* feeCalc); /** * Return the minimum required feerate taking into account the - * floating relay feerate and user set minimum transaction feerate + * minimum relay feerate and user set minimum transaction feerate */ -CFeeRate GetRequiredFeeRate(); +CFeeRate GetRequiredFeeRate(const CWallet& wallet); /** * Estimate the minimum fee rate considering user set parameters * and the required fee */ -CFeeRate GetMinimumFeeRate(const CCoinControl& coin_control, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation *feeCalc); +CFeeRate GetMinimumFeeRate(const CWallet& wallet, const CCoinControl& coin_control, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation* feeCalc); /** * Return the maximum feerate for discarding change. */ -CFeeRate GetDiscardRate(const CBlockPolicyEstimator& estimator); +CFeeRate GetDiscardRate(const CWallet& wallet, const CBlockPolicyEstimator& estimator); #endif // BITCOIN_WALLET_FEES_H diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp index 3d7bb674f0..06f9c730c0 100644 --- a/src/wallet/init.cpp +++ b/src/wallet/init.cpp @@ -3,18 +3,53 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include <wallet/init.h> - #include <chainparams.h> +#include <init.h> #include <net.h> #include <util.h> #include <utilmoneystr.h> #include <validation.h> +#include <walletinitinterface.h> #include <wallet/rpcwallet.h> #include <wallet/wallet.h> #include <wallet/walletutil.h> -std::string WalletInit::GetHelpString(bool showDebug) +class WalletInit : public WalletInitInterface { +public: + + //! Return the wallets help message. + std::string GetHelpString(bool showDebug) const override; + + //! Wallets parameter interaction + bool ParameterInteraction() const override; + + //! Register wallet RPCs. + void RegisterRPC(CRPCTable &tableRPC) const override; + + //! Responsible for reading and validating the -wallet arguments and verifying the wallet database. + // This function will perform salvage on the wallet if requested, as long as only one wallet is + // being loaded (WalletParameterInteraction forbids -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet). + bool Verify() const override; + + //! Load wallet databases. + bool Open() const override; + + //! Complete startup of wallets. + void Start(CScheduler& scheduler) const override; + + //! Flush all wallets in preparation for shutdown. + void Flush() const override; + + //! Stop all wallets. Wallets will be flushed first. + void Stop() const override; + + //! Close all wallets. + void Close() const override; +}; + +const WalletInitInterface& g_wallet_init_interface = WalletInit(); + +std::string WalletInit::GetHelpString(bool showDebug) const { std::string strUsage = HelpMessageGroup(_("Wallet options:")); strUsage += HelpMessageOpt("-addresstype", strprintf("What type of addresses to use (\"legacy\", \"p2sh-segwit\", or \"bech32\", default: \"%s\")", FormatOutputType(DEFAULT_ADDRESS_TYPE))); @@ -29,7 +64,7 @@ std::string WalletInit::GetHelpString(bool showDebug) strUsage += HelpMessageOpt("-mintxfee=<amt>", strprintf(_("Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)"), CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MINFEE))); strUsage += HelpMessageOpt("-paytxfee=<amt>", strprintf(_("Fee (in %s/kB) to add to transactions you send (default: %s)"), - CURRENCY_UNIT, FormatMoney(payTxFee.GetFeePerK()))); + CURRENCY_UNIT, FormatMoney(CFeeRate{DEFAULT_PAY_TX_FEE}.GetFeePerK()))); strUsage += HelpMessageOpt("-rescan", _("Rescan the block chain for missing wallet transactions on startup")); strUsage += HelpMessageOpt("-salvagewallet", _("Attempt to recover private keys from a corrupt wallet on startup")); strUsage += HelpMessageOpt("-spendzeroconfchange", strprintf(_("Spend unconfirmed change when sending transactions (default: %u)"), DEFAULT_SPEND_ZEROCONF_CHANGE)); @@ -56,7 +91,7 @@ std::string WalletInit::GetHelpString(bool showDebug) return strUsage; } -bool WalletInit::ParameterInteraction() +bool WalletInit::ParameterInteraction() const { if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { for (const std::string& wallet : gArgs.GetArgs("-wallet")) { @@ -83,19 +118,19 @@ bool WalletInit::ParameterInteraction() } } - int zapwallettxes = gArgs.GetArg("-zapwallettxes", 0); + bool zapwallettxes = gArgs.GetBoolArg("-zapwallettxes", false); // -zapwallettxes implies dropping the mempool on startup - if (zapwallettxes != 0 && gArgs.SoftSetBoolArg("-persistmempool", false)) { - LogPrintf("%s: parameter interaction: -zapwallettxes=%s -> setting -persistmempool=0\n", __func__, zapwallettxes); + if (zapwallettxes && gArgs.SoftSetBoolArg("-persistmempool", false)) { + LogPrintf("%s: parameter interaction: -zapwallettxes enabled -> setting -persistmempool=0\n", __func__); } // -zapwallettxes implies a rescan - if (zapwallettxes != 0) { + if (zapwallettxes) { if (is_multiwallet) { return InitError(strprintf("%s is only allowed with a single wallet file", "-zapwallettxes")); } if (gArgs.SoftSetBoolArg("-rescan", true)) { - LogPrintf("%s: parameter interaction: -zapwallettxes=%s -> setting -rescan=1\n", __func__, zapwallettxes); + LogPrintf("%s: parameter interaction: -zapwallettxes enabled -> setting -rescan=1\n", __func__); } } @@ -114,55 +149,6 @@ bool WalletInit::ParameterInteraction() InitWarning(AmountHighWarn("-minrelaytxfee") + " " + _("The wallet will avoid paying less than the minimum relay fee.")); - if (gArgs.IsArgSet("-mintxfee")) - { - CAmount n = 0; - if (!ParseMoney(gArgs.GetArg("-mintxfee", ""), n) || 0 == n) - return InitError(AmountErrMsg("mintxfee", gArgs.GetArg("-mintxfee", ""))); - if (n > HIGH_TX_FEE_PER_KB) - InitWarning(AmountHighWarn("-mintxfee") + " " + - _("This is the minimum transaction fee you pay on every transaction.")); - CWallet::minTxFee = CFeeRate(n); - } - - g_wallet_allow_fallback_fee = Params().IsFallbackFeeEnabled(); - if (gArgs.IsArgSet("-fallbackfee")) - { - CAmount nFeePerK = 0; - if (!ParseMoney(gArgs.GetArg("-fallbackfee", ""), nFeePerK)) - return InitError(strprintf(_("Invalid amount for -fallbackfee=<amount>: '%s'"), gArgs.GetArg("-fallbackfee", ""))); - if (nFeePerK > HIGH_TX_FEE_PER_KB) - InitWarning(AmountHighWarn("-fallbackfee") + " " + - _("This is the transaction fee you may pay when fee estimates are not available.")); - CWallet::fallbackFee = CFeeRate(nFeePerK); - g_wallet_allow_fallback_fee = nFeePerK != 0; //disable fallback fee in case value was set to 0, enable if non-null value - } - if (gArgs.IsArgSet("-discardfee")) - { - CAmount nFeePerK = 0; - if (!ParseMoney(gArgs.GetArg("-discardfee", ""), nFeePerK)) - return InitError(strprintf(_("Invalid amount for -discardfee=<amount>: '%s'"), gArgs.GetArg("-discardfee", ""))); - if (nFeePerK > HIGH_TX_FEE_PER_KB) - InitWarning(AmountHighWarn("-discardfee") + " " + - _("This is the transaction fee you may discard if change is smaller than dust at this level")); - CWallet::m_discard_rate = CFeeRate(nFeePerK); - } - if (gArgs.IsArgSet("-paytxfee")) - { - CAmount nFeePerK = 0; - if (!ParseMoney(gArgs.GetArg("-paytxfee", ""), nFeePerK)) - return InitError(AmountErrMsg("paytxfee", gArgs.GetArg("-paytxfee", ""))); - if (nFeePerK > HIGH_TX_FEE_PER_KB) - InitWarning(AmountHighWarn("-paytxfee") + " " + - _("This is the transaction fee you will pay if you send a transaction.")); - - payTxFee = CFeeRate(nFeePerK, 1000); - if (payTxFee < ::minRelayTxFee) - { - return InitError(strprintf(_("Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s)"), - gArgs.GetArg("-paytxfee", ""), ::minRelayTxFee.ToString())); - } - } if (gArgs.IsArgSet("-maxtxfee")) { CAmount nMaxFee = 0; @@ -177,14 +163,11 @@ bool WalletInit::ParameterInteraction() gArgs.GetArg("-maxtxfee", ""), ::minRelayTxFee.ToString())); } } - nTxConfirmTarget = gArgs.GetArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET); - bSpendZeroConfChange = gArgs.GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE); - fWalletRbf = gArgs.GetBoolArg("-walletrbf", DEFAULT_WALLET_RBF); return true; } -void WalletInit::RegisterRPC(CRPCTable &t) +void WalletInit::RegisterRPC(CRPCTable &t) const { if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { return; @@ -193,7 +176,7 @@ void WalletInit::RegisterRPC(CRPCTable &t) RegisterWalletRPCCommands(t); } -bool WalletInit::Verify() +bool WalletInit::Verify() const { if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { return true; @@ -241,21 +224,21 @@ bool WalletInit::Verify() } std::string strError; - if (!CWalletDB::VerifyEnvironment(wallet_path, strError)) { + if (!WalletBatch::VerifyEnvironment(wallet_path, strError)) { return InitError(strError); } if (gArgs.GetBoolArg("-salvagewallet", false)) { // Recover readable keypairs: - CWallet dummyWallet("dummy", CWalletDBWrapper::CreateDummy()); + CWallet dummyWallet("dummy", WalletDatabase::CreateDummy()); std::string backup_filename; - if (!CWalletDB::Recover(wallet_path, (void *)&dummyWallet, CWalletDB::RecoverKeysOnlyFilter, backup_filename)) { + if (!WalletBatch::Recover(wallet_path, (void *)&dummyWallet, WalletBatch::RecoverKeysOnlyFilter, backup_filename)) { return false; } } std::string strWarning; - bool dbV = CWalletDB::VerifyDatabaseFile(wallet_path, strWarning, strError); + bool dbV = WalletBatch::VerifyDatabaseFile(wallet_path, strWarning, strError); if (!strWarning.empty()) { InitWarning(strWarning); } @@ -268,7 +251,7 @@ bool WalletInit::Verify() return true; } -bool WalletInit::Open() +bool WalletInit::Open() const { if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { LogPrintf("Wallet disabled!\n"); @@ -280,37 +263,37 @@ bool WalletInit::Open() if (!pwallet) { return false; } - vpwallets.push_back(pwallet); + AddWallet(pwallet); } return true; } -void WalletInit::Start(CScheduler& scheduler) +void WalletInit::Start(CScheduler& scheduler) const { - for (CWalletRef pwallet : vpwallets) { + for (CWallet* pwallet : GetWallets()) { pwallet->postInitProcess(scheduler); } } -void WalletInit::Flush() +void WalletInit::Flush() const { - for (CWalletRef pwallet : vpwallets) { + for (CWallet* pwallet : GetWallets()) { pwallet->Flush(false); } } -void WalletInit::Stop() +void WalletInit::Stop() const { - for (CWalletRef pwallet : vpwallets) { + for (CWallet* pwallet : GetWallets()) { pwallet->Flush(true); } } -void WalletInit::Close() +void WalletInit::Close() const { - for (CWalletRef pwallet : vpwallets) { + for (CWallet* pwallet : GetWallets()) { + RemoveWallet(pwallet); delete pwallet; } - vpwallets.clear(); } diff --git a/src/wallet/init.h b/src/wallet/init.h deleted file mode 100644 index f8be90d3e3..0000000000 --- a/src/wallet/init.h +++ /dev/null @@ -1,48 +0,0 @@ -// 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_WALLET_INIT_H -#define BITCOIN_WALLET_INIT_H - -#include <walletinitinterface.h> -#include <string> - -class CRPCTable; -class CScheduler; - -class WalletInit : public WalletInitInterface { -public: - - //! Return the wallets help message. - std::string GetHelpString(bool showDebug) override; - - //! Wallets parameter interaction - bool ParameterInteraction() override; - - //! Register wallet RPCs. - void RegisterRPC(CRPCTable &tableRPC) override; - - //! Responsible for reading and validating the -wallet arguments and verifying the wallet database. - // This function will perform salvage on the wallet if requested, as long as only one wallet is - // being loaded (WalletParameterInteraction forbids -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet). - bool Verify() override; - - //! Load wallet databases. - bool Open() override; - - //! Complete startup of wallets. - void Start(CScheduler& scheduler) override; - - //! Flush all wallets in preparation for shutdown. - void Flush() override; - - //! Stop all wallets. Wallets will be flushed first. - void Stop() override; - - //! Close all wallets. - void Close() override; -}; - -#endif // BITCOIN_WALLET_INIT_H diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 3f88c62c61..0b8a628c21 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -4,9 +4,7 @@ #include <chain.h> #include <key_io.h> -#include <rpc/safemode.h> #include <rpc/server.h> -#include <wallet/init.h> #include <validation.h> #include <script/script.h> #include <script/standard.h> @@ -173,7 +171,13 @@ UniValue importprivkey(const JSONRPCRequest& request) } } if (fRescan) { - pwallet->RescanFromTime(TIMESTAMP_MIN, reserver, true /* update */); + int64_t scanned_time = pwallet->RescanFromTime(TIMESTAMP_MIN, reserver, true /* update */); + if (pwallet->IsAbortingRescan()) { + throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted by user."); + } + if (scanned_time > TIMESTAMP_MIN) { + throw JSONRPCError(RPC_WALLET_ERROR, "Rescan was unable to fully rescan the blockchain. Some transactions may be missing."); + } } return NullUniValue; @@ -199,7 +203,6 @@ UniValue abortrescan(const JSONRPCRequest& request) + HelpExampleRpc("abortrescan", "") ); - ObserveSafeMode(); if (!pwallet->IsScanning() || pwallet->IsAbortingRescan()) return false; pwallet->AbortRescan(); return true; @@ -219,10 +222,11 @@ void ImportScript(CWallet* const pwallet, const CScript& script, const std::stri } if (isRedeemScript) { - if (!pwallet->HaveCScript(script) && !pwallet->AddCScript(script)) { + const CScriptID id(script); + if (!pwallet->HaveCScript(id) && !pwallet->AddCScript(script)) { throw JSONRPCError(RPC_WALLET_ERROR, "Error adding p2sh redeemScript to wallet"); } - ImportAddress(pwallet, CScriptID(script), strLabel); + ImportAddress(pwallet, id, strLabel); } else { CTxDestination destination; if (ExtractDestination(script, destination)) { @@ -311,7 +315,13 @@ UniValue importaddress(const JSONRPCRequest& request) } if (fRescan) { - pwallet->RescanFromTime(TIMESTAMP_MIN, reserver, true /* update */); + int64_t scanned_time = pwallet->RescanFromTime(TIMESTAMP_MIN, reserver, true /* update */); + if (pwallet->IsAbortingRescan()) { + throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted by user."); + } + if (scanned_time > TIMESTAMP_MIN) { + throw JSONRPCError(RPC_WALLET_ERROR, "Rescan was unable to fully rescan the blockchain. Some transactions may be missing."); + } pwallet->ReacceptWalletTransactions(); } @@ -480,7 +490,13 @@ UniValue importpubkey(const JSONRPCRequest& request) } if (fRescan) { - pwallet->RescanFromTime(TIMESTAMP_MIN, reserver, true /* update */); + int64_t scanned_time = pwallet->RescanFromTime(TIMESTAMP_MIN, reserver, true /* update */); + if (pwallet->IsAbortingRescan()) { + throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted by user."); + } + if (scanned_time > TIMESTAMP_MIN) { + throw JSONRPCError(RPC_WALLET_ERROR, "Rescan was unable to fully rescan the blockchain. Some transactions may be missing."); + } pwallet->ReacceptWalletTransactions(); } @@ -535,9 +551,11 @@ UniValue importwallet(const JSONRPCRequest& request) int64_t nFilesize = std::max((int64_t)1, (int64_t)file.tellg()); file.seekg(0, file.beg); - pwallet->ShowProgress(_("Importing..."), 0); // show progress dialog in GUI + // Use uiInterface.ShowProgress instead of pwallet.ShowProgress because pwallet.ShowProgress has a cancel button tied to AbortRescan which + // we don't want for this progress bar shoing the import progress. uiInterface.ShowProgress does not have a cancel button. + uiInterface.ShowProgress(_("Importing..."), 0, false); // show progress dialog in GUI while (file.good()) { - pwallet->ShowProgress("", std::max(1, std::min(99, (int)(((double)file.tellg() / (double)nFilesize) * 100)))); + uiInterface.ShowProgress("", std::max(1, std::min(99, (int)(((double)file.tellg() / (double)nFilesize) * 100))), false); std::string line; std::getline(file, line); if (line.empty() || line[0] == '#') @@ -583,7 +601,8 @@ UniValue importwallet(const JSONRPCRequest& request) } else if(IsHex(vstr[0])) { std::vector<unsigned char> vData(ParseHex(vstr[0])); CScript script = CScript(vData.begin(), vData.end()); - if (pwallet->HaveCScript(script)) { + CScriptID id(script); + if (pwallet->HaveCScript(id)) { LogPrintf("Skipping import of %s (script already present)\n", vstr[0]); continue; } @@ -594,16 +613,23 @@ UniValue importwallet(const JSONRPCRequest& request) } int64_t birth_time = DecodeDumpTime(vstr[1]); if (birth_time > 0) { - pwallet->m_script_metadata[CScriptID(script)].nCreateTime = birth_time; + pwallet->m_script_metadata[id].nCreateTime = birth_time; nTimeBegin = std::min(nTimeBegin, birth_time); } } } file.close(); - pwallet->ShowProgress("", 100); // hide progress dialog in GUI + uiInterface.ShowProgress("", 100, false); // hide progress dialog in GUI pwallet->UpdateTimeFirstKey(nTimeBegin); } - pwallet->RescanFromTime(nTimeBegin, reserver, false /* update */); + uiInterface.ShowProgress("", 100, false); // hide progress dialog in GUI + int64_t scanned_time = pwallet->RescanFromTime(nTimeBegin, reserver, false /* update */); + if (pwallet->IsAbortingRescan()) { + throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted by user."); + } + if (scanned_time > nTimeBegin) { + throw JSONRPCError(RPC_WALLET_ERROR, "Rescan was unable to fully rescan the blockchain. Some transactions may be missing."); + } pwallet->MarkDirty(); if (!fGood) @@ -873,12 +899,12 @@ UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int6 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet"); } - if (!pwallet->HaveCScript(redeemScript) && !pwallet->AddCScript(redeemScript)) { + CScriptID redeem_id(redeemScript); + if (!pwallet->HaveCScript(redeem_id) && !pwallet->AddCScript(redeemScript)) { throw JSONRPCError(RPC_WALLET_ERROR, "Error adding p2sh redeemScript to wallet"); } - CTxDestination redeem_dest = CScriptID(redeemScript); - CScript redeemDestination = GetScriptForDestination(redeem_dest); + CScript redeemDestination = GetScriptForDestination(redeem_id); if (::IsMine(*pwallet, redeemDestination) == ISMINE_SPENDABLE) { throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script"); @@ -1213,6 +1239,9 @@ UniValue importmulti(const JSONRPCRequest& mainRequest) int64_t scannedTime = pwallet->RescanFromTime(nLowestTimestamp, reserver, true /* update */); pwallet->ReacceptWalletTransactions(); + if (pwallet->IsAbortingRescan()) { + throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted by user."); + } if (scannedTime > nLowestTimestamp) { std::vector<UniValue> results = response.getValues(); response.clear(); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index f669921750..bfd9840dfc 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -17,7 +17,6 @@ #include <policy/rbf.h> #include <rpc/mining.h> #include <rpc/rawtransaction.h> -#include <rpc/safemode.h> #include <rpc/server.h> #include <rpc/util.h> #include <script/sign.h> @@ -37,6 +36,8 @@ #include <univalue.h> +#include <functional> + static const std::string WALLET_ENDPOINT_BASE = "/wallet/"; CWallet *GetWalletForJSONRPCRequest(const JSONRPCRequest& request) @@ -44,14 +45,13 @@ CWallet *GetWalletForJSONRPCRequest(const JSONRPCRequest& request) 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())); - for (CWalletRef pwallet : ::vpwallets) { - if (pwallet->GetName() == requestedWallet) { - return pwallet; - } - } - throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Requested wallet does not exist or is not loaded"); + CWallet* pwallet = GetWallet(requestedWallet); + if (!pwallet) throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Requested wallet does not exist or is not loaded"); + return pwallet; } - return ::vpwallets.size() == 1 || (request.fHelp && ::vpwallets.size() > 0) ? ::vpwallets[0] : nullptr; + + std::vector<CWallet*> wallets = GetWallets(); + return wallets.size() == 1 || (request.fHelp && wallets.size() > 0) ? wallets[0] : nullptr; } std::string HelpRequiringPassphrase(CWallet * const pwallet) @@ -65,7 +65,7 @@ bool EnsureWalletIsAvailable(CWallet * const pwallet, bool avoidException) { if (pwallet) return true; if (avoidException) return false; - if (::vpwallets.empty()) { + 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 @@ -186,7 +186,6 @@ UniValue getnewaddress(const JSONRPCRequest& request) return EncodeDestination(dest); } - CTxDestination GetLabelDestination(CWallet* const pwallet, const std::string& label, bool bForceNew=false) { CTxDestination dest; @@ -204,14 +203,23 @@ UniValue getlabeladdress(const JSONRPCRequest& request) return NullUniValue; } - if (request.fHelp || request.params.size() != 1) + if (!IsDeprecatedRPCEnabled("accounts") && request.strMethod == "getaccountaddress") { + if (request.fHelp) { + throw std::runtime_error("getaccountaddress (Deprecated, will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts)"); + } + throw JSONRPCError(RPC_METHOD_DEPRECATED, "getaccountaddress is deprecated and will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts."); + } + + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) throw std::runtime_error( - "getlabeladdress \"label\"\n" - "\nReturns the current Bitcoin address for receiving payments to this label.\n" + "getlabeladdress \"label\" ( force ) \n" + "\nReturns the default receiving address for this label. This will reset to a fresh address once there's a transaction that spends to it.\n" "\nArguments:\n" - "1. \"label\" (string, required) The label name for the address. It can also be set to the empty string \"\" to represent the default label. The label does not need to exist, it will be created and a new address created if there is no label by the given name.\n" + "1. \"label\" (string, required) The label for the address. It can also be set to the empty string \"\" to represent the default label.\n" + "2. \"force\" (bool, optional) Whether the label should be created if it does not yet exist. If False, the RPC will return an error if called with a label that doesn't exist.\n" + " Defaults to false (unless the getaccountaddress method alias is being called, in which case defaults to true for backwards compatibility).\n" "\nResult:\n" - "\"address\" (string) The label bitcoin address\n" + "\"address\" (string) The current receiving address for the label.\n" "\nExamples:\n" + HelpExampleCli("getlabeladdress", "") + HelpExampleCli("getlabeladdress", "\"\"") @@ -223,6 +231,21 @@ UniValue getlabeladdress(const JSONRPCRequest& request) // Parse the label first so we don't generate a key if there's an error std::string label = LabelFromValue(request.params[0]); + bool force = request.strMethod == "getaccountaddress"; + if (!request.params[1].isNull()) { + force = request.params[1].get_bool(); + } + + bool label_found = false; + for (const std::pair<CTxDestination, CAddressBookData>& item : pwallet->mapAddressBook) { + if (item.second.name == label) { + label_found = true; + break; + } + } + if (!force && !label_found) { + throw JSONRPCError(RPC_WALLET_INVALID_LABEL_NAME, std::string("No addresses with label " + label)); + } UniValue ret(UniValue::VSTR); @@ -286,13 +309,20 @@ UniValue setlabel(const JSONRPCRequest& request) return NullUniValue; } - if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) + if (!IsDeprecatedRPCEnabled("accounts") && request.strMethod == "setaccount") { + if (request.fHelp) { + throw std::runtime_error("setaccount (Deprecated, will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts)"); + } + throw JSONRPCError(RPC_METHOD_DEPRECATED, "setaccount is deprecated and will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts."); + } + + if (request.fHelp || request.params.size() != 2) throw std::runtime_error( "setlabel \"address\" \"label\"\n" "\nSets the label associated with the given address.\n" "\nArguments:\n" "1. \"address\" (string, required) The bitcoin address to be associated with a label.\n" - "2. \"label\" (string, required) The label to assign the address to.\n" + "2. \"label\" (string, required) The label to assign to the address.\n" "\nExamples:\n" + HelpExampleCli("setlabel", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"tabby\"") + HelpExampleRpc("setlabel", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"tabby\"") @@ -305,23 +335,22 @@ UniValue setlabel(const JSONRPCRequest& request) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); } - std::string label; - if (!request.params[1].isNull()) - label = LabelFromValue(request.params[1]); + std::string label = LabelFromValue(request.params[1]); - // Only add the label if the address is yours. if (IsMine(*pwallet, dest)) { - // Detect when changing the label of an address that is the 'unused current key' of another label: + // Detect when changing the label of an address that is the receiving address of another label: + // If so, delete the account record for it. Labels, unlike addresses, can be deleted, + // and if we wouldn't do this, the record would stick around forever. if (pwallet->mapAddressBook.count(dest)) { std::string old_label = pwallet->mapAddressBook[dest].name; - if (dest == GetLabelDestination(pwallet, old_label)) { - GetLabelDestination(pwallet, old_label, true); + if (old_label != label && dest == GetLabelDestination(pwallet, old_label)) { + pwallet->DeleteLabel(old_label); } } pwallet->SetAddressBook(dest, label, "receive"); + } else { + pwallet->SetAddressBook(dest, label, "send"); } - else - throw JSONRPCError(RPC_MISC_ERROR, "setlabel can only be used with own address"); return NullUniValue; } @@ -334,6 +363,13 @@ UniValue getaccount(const JSONRPCRequest& request) return NullUniValue; } + if (!IsDeprecatedRPCEnabled("accounts")) { + if (request.fHelp) { + throw std::runtime_error("getaccount (Deprecated, will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts)"); + } + throw JSONRPCError(RPC_METHOD_DEPRECATED, "getaccount is deprecated and will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts."); + } + if (request.fHelp || request.params.size() != 1) throw std::runtime_error( "getaccount \"address\"\n" @@ -370,6 +406,13 @@ UniValue getaddressesbyaccount(const JSONRPCRequest& request) return NullUniValue; } + if (!IsDeprecatedRPCEnabled("accounts")) { + if (request.fHelp) { + throw std::runtime_error("getaddressbyaccount (Deprecated, will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts)"); + } + throw JSONRPCError(RPC_METHOD_DEPRECATED, "getaddressesbyaccount is deprecated and will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts."); + } + if (request.fHelp || request.params.size() != 1) throw std::runtime_error( "getaddressesbyaccount \"account\"\n" @@ -479,8 +522,6 @@ UniValue sendtoaddress(const JSONRPCRequest& request) + HelpExampleRpc("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", 0.1, \"donation\", \"seans outpost\"") ); - ObserveSafeMode(); - // 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(); @@ -511,7 +552,7 @@ UniValue sendtoaddress(const JSONRPCRequest& request) CCoinControl coin_control; if (!request.params[5].isNull()) { - coin_control.signalRbf = request.params[5].get_bool(); + coin_control.m_signal_bip125_rbf = request.params[5].get_bool(); } if (!request.params[6].isNull()) { @@ -561,8 +602,6 @@ UniValue listaddressgroupings(const JSONRPCRequest& request) + HelpExampleRpc("listaddressgroupings", "") ); - ObserveSafeMode(); - // 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(); @@ -678,8 +717,6 @@ UniValue getreceivedbyaddress(const JSONRPCRequest& request) + HelpExampleRpc("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", 6") ); - ObserveSafeMode(); - // 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(); @@ -725,6 +762,13 @@ UniValue getreceivedbylabel(const JSONRPCRequest& request) return NullUniValue; } + if (!IsDeprecatedRPCEnabled("accounts") && request.strMethod == "getreceivedbyaccount") { + if (request.fHelp) { + throw std::runtime_error("getreceivedbyaccount (Deprecated, will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts)"); + } + throw JSONRPCError(RPC_METHOD_DEPRECATED, "getreceivedbyaccount is deprecated and will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts."); + } + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) throw std::runtime_error( "getreceivedbylabel \"label\" ( minconf )\n" @@ -745,8 +789,6 @@ UniValue getreceivedbylabel(const JSONRPCRequest& request) + HelpExampleRpc("getreceivedbylabel", "\"tabby\", 6") ); - ObserveSafeMode(); - // 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(); @@ -790,7 +832,7 @@ UniValue getbalance(const JSONRPCRequest& request) return NullUniValue; } - if (request.fHelp || request.params.size() > 3) + if (request.fHelp || (request.params.size() > 3 && IsDeprecatedRPCEnabled("accounts")) || (request.params.size() != 0 && !IsDeprecatedRPCEnabled("accounts"))) throw std::runtime_error( "getbalance ( \"account\" minconf include_watchonly )\n" "\nIf account is not specified, returns the server's total available balance.\n" @@ -800,7 +842,8 @@ UniValue getbalance(const JSONRPCRequest& request) "Note that the account \"\" is not the same as leaving the parameter out.\n" "The server total may be different to the balance in the default \"\" account.\n" "\nArguments:\n" - "1. \"account\" (string, optional) DEPRECATED. The account string may be given as a\n" + "1. \"account\" (string, optional) DEPRECATED. This argument will be removed in V0.18. \n" + " To use this deprecated argument, start bitcoind with -deprecatedrpc=accounts. The account string may be given as a\n" " specific account name to find the balance associated with wallet keys in\n" " a named account, or as the empty string (\"\") to find the balance\n" " associated with wallet keys not in any named account, or as \"*\" to find\n" @@ -812,8 +855,8 @@ 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) 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" + "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" "\nResult:\n" "amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this account.\n" "\nExamples:\n" @@ -825,42 +868,44 @@ UniValue getbalance(const JSONRPCRequest& request) + HelpExampleRpc("getbalance", "\"*\", 6") ); - ObserveSafeMode(); - // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now pwallet->BlockUntilSyncedToCurrentChain(); LOCK2(cs_main, pwallet->cs_wallet); - const UniValue& account_value = request.params[0]; - const UniValue& minconf = request.params[1]; - const UniValue& include_watchonly = request.params[2]; + if (IsDeprecatedRPCEnabled("accounts")) { + const UniValue& account_value = request.params[0]; + const UniValue& minconf = request.params[1]; + const UniValue& include_watchonly = request.params[2]; - if (account_value.isNull()) { - if (!minconf.isNull()) { - throw JSONRPCError(RPC_INVALID_PARAMETER, - "getbalance minconf option is only currently supported if an account is specified"); - } - if (!include_watchonly.isNull()) { - throw JSONRPCError(RPC_INVALID_PARAMETER, - "getbalance include_watchonly option is only currently supported if an account is specified"); + 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()); } - return ValueFromAmount(pwallet->GetBalance()); - } - const std::string& account_param = account_value.get_str(); - const std::string* account = account_param != "*" ? &account_param : nullptr; + 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; + 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)); + return ValueFromAmount(pwallet->GetLegacyBalance(filter, nMinDepth, account)); + } + + return ValueFromAmount(pwallet->GetBalance()); } UniValue getunconfirmedbalance(const JSONRPCRequest &request) @@ -875,8 +920,6 @@ UniValue getunconfirmedbalance(const JSONRPCRequest &request) "getunconfirmedbalance\n" "Returns the server's total unconfirmed balance\n"); - ObserveSafeMode(); - // 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(); @@ -894,6 +937,13 @@ UniValue movecmd(const JSONRPCRequest& request) return NullUniValue; } + if (!IsDeprecatedRPCEnabled("accounts")) { + if (request.fHelp) { + throw std::runtime_error("move (Deprecated, will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts)"); + } + throw JSONRPCError(RPC_METHOD_DEPRECATED, "move 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() > 5) throw std::runtime_error( "move \"fromaccount\" \"toaccount\" amount ( minconf \"comment\" )\n" @@ -915,7 +965,6 @@ UniValue movecmd(const JSONRPCRequest& request) + HelpExampleRpc("move", "\"timotei\", \"akiko\", 0.01, 6, \"happy birthday!\"") ); - ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); std::string strFrom = LabelFromValue(request.params[0]); @@ -974,8 +1023,6 @@ UniValue sendfrom(const JSONRPCRequest& request) + HelpExampleRpc("sendfrom", "\"tabby\", \"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", 0.01, 6, \"donation\", \"seans outpost\"") ); - ObserveSafeMode(); - // 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(); @@ -1020,9 +1067,50 @@ UniValue sendmany(const JSONRPCRequest& request) return NullUniValue; } - if (request.fHelp || request.params.size() < 2 || request.params.size() > 8) - throw std::runtime_error( - "sendmany \"fromaccount\" {\"address\":amount,...} ( minconf \"comment\" [\"address\",...] replaceable conf_target \"estimate_mode\")\n" + std::string help_text; + if (!IsDeprecatedRPCEnabled("accounts")) { + help_text = "sendmany \"\" {\"address\":amount,...} ( minconf \"comment\" [\"address\",...] replaceable conf_target \"estimate_mode\")\n" + "\nSend multiple times. Amounts are double-precision floating point numbers.\n" + "Note that the \"fromaccount\" argument has been removed in V0.17. To use this RPC with a \"fromaccount\" argument, restart\n" + "bitcoind with -deprecatedrpc=accounts\n" + + HelpRequiringPassphrase(pwallet) + "\n" + "\nArguments:\n" + "1. \"dummy\" (string, required) Must be set to \"\" for backwards compatibility.\n" + "2. \"amounts\" (string, required) A json object with addresses and amounts\n" + " {\n" + " \"address\":amount (numeric or string) The bitcoin address is the key, the numeric amount (can be string) in " + CURRENCY_UNIT + " is the value\n" + " ,...\n" + " }\n" + "3. minconf (numeric, optional, default=1) Only use the balance confirmed at least this many times.\n" + "4. \"comment\" (string, optional) A comment\n" + "5. subtractfeefrom (array, optional) A json array with addresses.\n" + " The fee will be equally deducted from the amount of each selected address.\n" + " Those recipients will receive less bitcoins than you enter in their corresponding amount field.\n" + " If no addresses are specified here, the sender pays the fee.\n" + " [\n" + " \"address\" (string) Subtract fee from this address\n" + " ,...\n" + " ]\n" + "6. replaceable (boolean, optional) Allow this transaction to be replaced by a transaction with higher fees via BIP 125\n" + "7. conf_target (numeric, optional) Confirmation target (in blocks)\n" + "8. \"estimate_mode\" (string, optional, default=UNSET) The fee estimate mode, must be one of:\n" + " \"UNSET\"\n" + " \"ECONOMICAL\"\n" + " \"CONSERVATIVE\"\n" + "\nResult:\n" + "\"txid\" (string) The transaction id for the send. Only 1 transaction is created regardless of \n" + " the number of addresses.\n" + "\nExamples:\n" + "\nSend two amounts to two different addresses:\n" + + HelpExampleCli("sendmany", "\"\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\"") + + "\nSend two amounts to two different addresses setting the confirmation and comment:\n" + + HelpExampleCli("sendmany", "\"\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" 6 \"testing\"") + + "\nSend two amounts to two different addresses, subtract fee from amount:\n" + + HelpExampleCli("sendmany", "\"\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" 1 \"\" \"[\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\",\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\"]\"") + + "\nAs a json rpc call\n" + + HelpExampleRpc("sendmany", "\"\", {\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\":0.01,\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\":0.02}, 6, \"testing\""); + } else { + help_text = "sendmany \"\" \"fromaccount\" {\"address\":amount,...} ( minconf \"comment\" [\"address\",...] replaceable conf_target \"estimate_mode\")\n" "\nSend multiple times. Amounts are double-precision floating point numbers." + HelpRequiringPassphrase(pwallet) + "\n" "\nArguments:\n" @@ -1059,10 +1147,10 @@ UniValue sendmany(const JSONRPCRequest& request) "\nSend two amounts to two different addresses, subtract fee from amount:\n" + HelpExampleCli("sendmany", "\"\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" 1 \"\" \"[\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\",\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\"]\"") + "\nAs a json rpc call\n" - + HelpExampleRpc("sendmany", "\"\", {\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\":0.01,\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\":0.02}, 6, \"testing\"") - ); + + HelpExampleRpc("sendmany", "\"\", {\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\":0.01,\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\":0.02}, 6, \"testing\""); + } - ObserveSafeMode(); + if (request.fHelp || request.params.size() < 2 || request.params.size() > 8) throw std::runtime_error(help_text); // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now @@ -1074,6 +1162,9 @@ UniValue sendmany(const JSONRPCRequest& request) throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); } + if (!IsDeprecatedRPCEnabled("accounts") && !request.params[0].get_str().empty()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Dummy value must be set to \"\""); + } std::string strAccount = LabelFromValue(request.params[0]); UniValue sendTo = request.params[1].get_obj(); int nMinDepth = 1; @@ -1090,7 +1181,7 @@ UniValue sendmany(const JSONRPCRequest& request) CCoinControl coin_control; if (!request.params[5].isNull()) { - coin_control.signalRbf = request.params[5].get_bool(); + coin_control.m_signal_bip125_rbf = request.params[5].get_bool(); } if (!request.params[6].isNull()) { @@ -1557,7 +1648,7 @@ UniValue listreceivedbyaddress(const JSONRPCRequest& request) " \"confirmations\" : n, (numeric) The number of confirmations of the most recent transaction included\n" " \"label\" : \"label\", (string) The label of the receiving address. The default label is \"\".\n" " \"txids\": [\n" - " n, (numeric) The ids of transactions received with the address \n" + " \"txid\", (string) The ids of transactions received with the address \n" " ...\n" " ]\n" " }\n" @@ -1571,8 +1662,6 @@ UniValue listreceivedbyaddress(const JSONRPCRequest& request) + HelpExampleRpc("listreceivedbyaddress", "6, true, true, \"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\"") ); - ObserveSafeMode(); - // 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(); @@ -1589,6 +1678,13 @@ UniValue listreceivedbylabel(const JSONRPCRequest& request) return NullUniValue; } + if (!IsDeprecatedRPCEnabled("accounts") && request.strMethod == "listreceivedbyaccount") { + if (request.fHelp) { + throw std::runtime_error("listreceivedbyaccount (Deprecated, will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts)"); + } + throw JSONRPCError(RPC_METHOD_DEPRECATED, "listreceivedbyaccount 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) throw std::runtime_error( "listreceivedbylabel ( minconf include_empty include_watchonly)\n" @@ -1616,8 +1712,6 @@ UniValue listreceivedbylabel(const JSONRPCRequest& request) + HelpExampleRpc("listreceivedbylabel", "6, true, true") ); - ObserveSafeMode(); - // 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(); @@ -1666,7 +1760,7 @@ void ListTransactions(CWallet* const pwallet, const CWalletTx& wtx, const std::s if (involvesWatchonly || (::IsMine(*pwallet, s.destination) & ISMINE_WATCH_ONLY)) { entry.pushKV("involvesWatchonly", true); } - entry.pushKV("account", strSentAccount); + if (IsDeprecatedRPCEnabled("accounts")) entry.pushKV("account", strSentAccount); MaybePushAddress(entry, s.destination); entry.pushKV("category", "send"); entry.pushKV("amount", ValueFromAmount(-s.amount)); @@ -1697,7 +1791,7 @@ void ListTransactions(CWallet* const pwallet, const CWalletTx& wtx, const std::s if (involvesWatchonly || (::IsMine(*pwallet, r.destination) & ISMINE_WATCH_ONLY)) { entry.pushKV("involvesWatchonly", true); } - entry.pushKV("account", account); + if (IsDeprecatedRPCEnabled("accounts")) entry.pushKV("account", account); MaybePushAddress(entry, r.destination); if (wtx.IsCoinBase()) { @@ -1736,7 +1830,7 @@ void AcentryToJSON(const CAccountingEntry& acentry, const std::string& strAccoun entry.pushKV("category", "move"); entry.pushKV("time", acentry.nTime); entry.pushKV("amount", ValueFromAmount(acentry.nCreditDebit)); - entry.pushKV("otheraccount", acentry.strOtherAccount); + if (IsDeprecatedRPCEnabled("accounts")) entry.pushKV("otheraccount", acentry.strOtherAccount); entry.pushKV("comment", acentry.strComment); ret.push_back(entry); } @@ -1749,19 +1843,64 @@ UniValue listtransactions(const JSONRPCRequest& request) return NullUniValue; } - if (request.fHelp || request.params.size() > 4) - throw std::runtime_error( - "listtransactions ( \"account\" count skip include_watchonly)\n" + std::string help_text {}; + if (!IsDeprecatedRPCEnabled("accounts")) { + help_text = "listtransactions (dummy count skip include_watchonly)\n" + "\nReturns up to 'count' most recent transactions skipping the first 'from' transactions for account 'account'.\n" + "Note that the \"account\" argument and \"otheraccount\" return value have been removed in V0.17. To use this RPC with an \"account\" argument, restart\n" + "bitcoind with -deprecatedrpc=accounts\n" + "\nArguments:\n" + "1. \"dummy\" (string, optional) If set, should be \"*\" for backwards compatibility.\n" + "2. count (numeric, optional, default=10) The number of transactions to return\n" + "3. skip (numeric, optional, default=0) The number of transactions to skip\n" + "4. include_watchonly (bool, optional, default=false) Include transactions to watch-only addresses (see 'importaddress')\n" + "\nResult:\n" + "[\n" + " {\n" + " \"address\":\"address\", (string) The bitcoin address of the transaction.\n" + " \"category\":\"send|receive\", (string) The transaction category.\n" + " \"amount\": x.xxx, (numeric) The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and is positive\n" + " for the 'receive' category,\n" + " \"label\": \"label\", (string) A comment for the address/transaction, if any\n" + " \"vout\": n, (numeric) the vout value\n" + " \"fee\": x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n" + " 'send' category of transactions.\n" + " \"confirmations\": n, (numeric) The number of confirmations for the transaction. Negative confirmations indicate the\n" + " transaction conflicts with the block chain\n" + " \"trusted\": xxx, (bool) Whether we consider the outputs of this unconfirmed transaction safe to spend.\n" + " \"blockhash\": \"hashvalue\", (string) The block hash containing the transaction.\n" + " \"blockindex\": n, (numeric) The index of the transaction in the block that includes it.\n" + " \"blocktime\": xxx, (numeric) The block time in seconds since epoch (1 Jan 1970 GMT).\n" + " \"txid\": \"transactionid\", (string) The transaction id.\n" + " \"time\": xxx, (numeric) The transaction time in seconds since epoch (midnight Jan 1 1970 GMT).\n" + " \"timereceived\": xxx, (numeric) The time received in seconds since epoch (midnight Jan 1 1970 GMT).\n" + " \"comment\": \"...\", (string) If a comment is associated with the transaction.\n" + " \"bip125-replaceable\": \"yes|no|unknown\", (string) Whether this transaction could be replaced due to BIP125 (replace-by-fee);\n" + " may be unknown for unconfirmed transactions not in the mempool\n" + " \"abandoned\": xxx (bool) 'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n" + " 'send' category of transactions.\n" + " }\n" + "]\n" + + "\nExamples:\n" + "\nList the most recent 10 transactions in the systems\n" + + HelpExampleCli("listtransactions", "") + + "\nList transactions 100 to 120\n" + + HelpExampleCli("listtransactions", "\"*\" 20 100") + + "\nAs a json rpc call\n" + + HelpExampleRpc("listtransactions", "\"*\", 20, 100"); + } else { + help_text = "listtransactions ( \"account\" count skip include_watchonly)\n" "\nReturns up to 'count' most recent transactions skipping the first 'from' transactions for account 'account'.\n" "\nArguments:\n" - "1. \"account\" (string, optional) DEPRECATED. The account name. Should be \"*\".\n" + "1. \"account\" (string, optional) DEPRECATED. This argument will be removed in V0.18. The account name. Should be \"*\".\n" "2. count (numeric, optional, default=10) The number of transactions to return\n" "3. skip (numeric, optional, default=0) The number of transactions to skip\n" "4. include_watchonly (bool, optional, default=false) Include transactions to watch-only addresses (see 'importaddress')\n" "\nResult:\n" "[\n" " {\n" - " \"account\":\"accountname\", (string) DEPRECATED. The account name associated with the transaction. \n" + " \"account\":\"accountname\", (string) DEPRECATED. This field will be removed in V0.18. The account name associated with the transaction. \n" " It will be \"\" for the default account.\n" " \"address\":\"address\", (string) The bitcoin address of the transaction. Not present for \n" " move transactions (category = move).\n" @@ -1790,7 +1929,7 @@ UniValue listtransactions(const JSONRPCRequest& request) " \"timereceived\": xxx, (numeric) The time received in seconds since epoch (midnight Jan 1 1970 GMT). Available \n" " for 'send' and 'receive' category of transactions.\n" " \"comment\": \"...\", (string) If a comment is associated with the transaction.\n" - " \"otheraccount\": \"accountname\", (string) DEPRECATED. For the 'move' category of transactions, the account the funds came \n" + " \"otheraccount\": \"accountname\", (string) DEPRECATED. This field will be removed in V0.18. For the 'move' category of transactions, the account the funds came \n" " from (for receiving funds, positive amounts), or went to (for sending funds,\n" " negative amounts).\n" " \"bip125-replaceable\": \"yes|no|unknown\", (string) Whether this transaction could be replaced due to BIP125 (replace-by-fee);\n" @@ -1806,18 +1945,21 @@ UniValue listtransactions(const JSONRPCRequest& request) "\nList transactions 100 to 120\n" + HelpExampleCli("listtransactions", "\"*\" 20 100") + "\nAs a json rpc call\n" - + HelpExampleRpc("listtransactions", "\"*\", 20, 100") - ); - - ObserveSafeMode(); + + HelpExampleRpc("listtransactions", "\"*\", 20, 100"); + } + if (request.fHelp || request.params.size() > 4) throw std::runtime_error(help_text); // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now pwallet->BlockUntilSyncedToCurrentChain(); std::string strAccount = "*"; - if (!request.params[0].isNull()) + if (!request.params[0].isNull()) { strAccount = request.params[0].get_str(); + if (!IsDeprecatedRPCEnabled("accounts") && strAccount != "*") { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Dummy value must be set to \"*\""); + } + } int nCount = 10; if (!request.params[1].isNull()) nCount = request.params[1].get_int(); @@ -1847,9 +1989,10 @@ UniValue listtransactions(const JSONRPCRequest& request) CWalletTx *const pwtx = (*it).second.first; if (pwtx != nullptr) ListTransactions(pwallet, *pwtx, strAccount, 0, true, ret, filter); - CAccountingEntry *const pacentry = (*it).second.second; - if (pacentry != nullptr) - AcentryToJSON(*pacentry, strAccount, ret); + if (IsDeprecatedRPCEnabled("accounts")) { + CAccountingEntry *const pacentry = (*it).second.second; + if (pacentry != nullptr) AcentryToJSON(*pacentry, strAccount, ret); + } if ((int)ret.size() >= (nCount+nFrom)) break; } @@ -1888,6 +2031,13 @@ UniValue listaccounts(const JSONRPCRequest& request) return NullUniValue; } + if (!IsDeprecatedRPCEnabled("accounts")) { + if (request.fHelp) { + throw std::runtime_error("listaccounts (Deprecated, will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts)"); + } + throw JSONRPCError(RPC_METHOD_DEPRECATED, "listaccounts is deprecated and will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts."); + } + if (request.fHelp || request.params.size() > 2) throw std::runtime_error( "listaccounts ( minconf include_watchonly)\n" @@ -1911,8 +2061,6 @@ UniValue listaccounts(const JSONRPCRequest& request) + HelpExampleRpc("listaccounts", "6") ); - ObserveSafeMode(); - // 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(); @@ -1991,7 +2139,7 @@ UniValue listsinceblock(const JSONRPCRequest& request) "\nResult:\n" "{\n" " \"transactions\": [\n" - " \"account\":\"accountname\", (string) DEPRECATED. The account name associated with the transaction. Will be \"\" for the default account.\n" + " \"account\":\"accountname\", (string) DEPRECATED. This field will be removed in V0.18. To see this deprecated field, start bitcoind with -deprecatedrpc=accounts. The account name associated with the transaction. Will be \"\" for the default account.\n" " \"address\":\"address\", (string) The bitcoin address of the transaction. Not present for move transactions (category = move).\n" " \"category\":\"send|receive\", (string) The transaction category. 'send' has negative amounts, 'receive' has positive amounts.\n" " \"amount\": x.xxx, (numeric) The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and for the 'move' category for moves \n" @@ -2025,8 +2173,6 @@ UniValue listsinceblock(const JSONRPCRequest& request) + HelpExampleRpc("listsinceblock", "\"000000000000000bacf66f7497b7dc45ef753ee9a7d38571037cdb1a57f663ad\", 6") ); - ObserveSafeMode(); - // 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(); @@ -2140,7 +2286,7 @@ UniValue gettransaction(const JSONRPCRequest& request) " may be unknown for unconfirmed transactions not in the mempool\n" " \"details\" : [\n" " {\n" - " \"account\" : \"accountname\", (string) DEPRECATED. The account name involved in the transaction, can be \"\" for the default account.\n" + " \"account\" : \"accountname\", (string) DEPRECATED. This field will be removed in a V0.18. To see this deprecated field, start bitcoind with -deprecatedrpc=accounts. The account name involved in the transaction, can be \"\" for the default account.\n" " \"address\" : \"address\", (string) The bitcoin address involved in the transaction\n" " \"category\" : \"send|receive\", (string) The category, either 'send' or 'receive'\n" " \"amount\" : x.xxx, (numeric) The amount in " + CURRENCY_UNIT + "\n" @@ -2162,8 +2308,6 @@ UniValue gettransaction(const JSONRPCRequest& request) + HelpExampleRpc("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") ); - ObserveSafeMode(); - // 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(); @@ -2230,8 +2374,6 @@ UniValue abandontransaction(const JSONRPCRequest& request) ); } - ObserveSafeMode(); - // 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(); @@ -2346,8 +2488,7 @@ UniValue walletpassphrase(const JSONRPCRequest& request) "This is needed prior to performing transactions related to private keys such as sending bitcoins\n" "\nArguments:\n" "1. \"passphrase\" (string, required) The wallet passphrase\n" - "2. timeout (numeric, required) The time to keep the decryption key in seconds. Limited to at most 1073741824 (2^30) seconds.\n" - " Any value greater than 1073741824 seconds will be set to 1073741824 seconds.\n" + "2. timeout (numeric, required) The time to keep the decryption key in seconds; capped at 100000000 (~3 years).\n" "\nNote:\n" "Issuing the walletpassphrase command while the wallet is already unlocked will set a new unlock\n" "time that overrides the old one.\n" @@ -2380,9 +2521,10 @@ UniValue walletpassphrase(const JSONRPCRequest& request) if (nSleepTime < 0) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Timeout cannot be negative."); } - // Clamp timeout to 2^30 seconds - if (nSleepTime > (int64_t)1 << 30) { - nSleepTime = (int64_t)1 << 30; + // Clamp timeout + constexpr int64_t MAX_SLEEP_TIME = 100000000; // larger values trigger a macos/libevent bug? + if (nSleepTime > MAX_SLEEP_TIME) { + nSleepTime = MAX_SLEEP_TIME; } if (strWalletPass.length() > 0) @@ -2399,7 +2541,7 @@ UniValue walletpassphrase(const JSONRPCRequest& request) pwallet->TopUpKeyPool(); pwallet->nRelockTime = GetTime() + nSleepTime; - RPCRunLater(strprintf("lockwallet(%s)", pwallet->GetName()), boost::bind(LockWallet, pwallet), nSleepTime); + RPCRunLater(strprintf("lockwallet(%s)", pwallet->GetName()), std::bind(LockWallet, pwallet), nSleepTime); return NullUniValue; } @@ -2712,7 +2854,6 @@ UniValue listlockunspent(const JSONRPCRequest& request) + HelpExampleRpc("listlockunspent", "") ); - ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); std::vector<COutPoint> vOutpts; @@ -2738,10 +2879,10 @@ UniValue settxfee(const JSONRPCRequest& request) return NullUniValue; } - if (request.fHelp || request.params.size() < 1 || request.params.size() > 1) + if (request.fHelp || request.params.size() < 1 || request.params.size() > 1) { throw std::runtime_error( "settxfee amount\n" - "\nSet the transaction fee per kB. Overwrites the paytxfee parameter.\n" + "\nSet the transaction fee per kB for this wallet. Overrides the global -paytxfee command line parameter.\n" "\nArguments:\n" "1. amount (numeric or string, required) The transaction fee in " + CURRENCY_UNIT + "/kB\n" "\nResult\n" @@ -2750,13 +2891,13 @@ UniValue settxfee(const JSONRPCRequest& request) + HelpExampleCli("settxfee", "0.00001") + HelpExampleRpc("settxfee", "0.00001") ); + } LOCK2(cs_main, pwallet->cs_wallet); - // Amount CAmount nAmount = AmountFromValue(request.params[0]); - payTxFee = CFeeRate(nAmount, 1000); + pwallet->m_pay_tx_fee = CFeeRate(nAmount, 1000); return true; } @@ -2791,8 +2932,6 @@ UniValue getwalletinfo(const JSONRPCRequest& request) + HelpExampleRpc("getwalletinfo", "") ); - ObserveSafeMode(); - // 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(); @@ -2817,9 +2956,9 @@ UniValue getwalletinfo(const JSONRPCRequest& request) if (pwallet->IsCrypted()) { obj.pushKV("unlocked_until", pwallet->nRelockTime); } - obj.pushKV("paytxfee", ValueFromAmount(payTxFee.GetFeePerK())); + obj.pushKV("paytxfee", ValueFromAmount(pwallet->m_pay_tx_fee.GetFeePerK())); if (!masterKeyID.IsNull()) - obj.pushKV("hdmasterkeyid", masterKeyID.GetHex()); + obj.pushKV("hdmasterkeyid", masterKeyID.GetHex()); return obj; } @@ -2842,8 +2981,7 @@ UniValue listwallets(const JSONRPCRequest& request) UniValue obj(UniValue::VARR); - for (CWalletRef pwallet : vpwallets) { - + for (CWallet* pwallet : GetWallets()) { if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -2928,7 +3066,7 @@ UniValue listunspent(const JSONRPCRequest& request) " \"vout\" : n, (numeric) the vout value\n" " \"address\" : \"address\", (string) the bitcoin address\n" " \"label\" : \"label\", (string) The associated label, or \"\" for the default label\n" - " \"account\" : \"account\", (string) DEPRECATED. Backwards compatible alias for label.\n" + " \"account\" : \"account\", (string) DEPRECATED. This field will be removed in V0.18. To see this deprecated field, start bitcoind with -deprecatedrpc=accounts. The associated account, or \"\" for the default account\n" " \"scriptPubKey\" : \"key\", (string) the script key\n" " \"amount\" : x.xxx, (numeric) the transaction output amount in " + CURRENCY_UNIT + "\n" " \"confirmations\" : n, (numeric) The number of confirmations\n" @@ -2950,8 +3088,6 @@ UniValue listunspent(const JSONRPCRequest& request) + HelpExampleRpc("listunspent", "6, 9999999, [] , true, { \"minimumAmount\": 0.005 } ") ); - ObserveSafeMode(); - int nMinDepth = 1; if (!request.params[0].isNull()) { RPCTypeCheckArgument(request.params[0], UniValue::VNUM); @@ -3033,7 +3169,9 @@ UniValue listunspent(const JSONRPCRequest& request) if (pwallet->mapAddressBook.count(address)) { entry.pushKV("label", pwallet->mapAddressBook[address].name); - entry.pushKV("account", pwallet->mapAddressBook[address].name); + if (IsDeprecatedRPCEnabled("accounts")) { + entry.pushKV("account", pwallet->mapAddressBook[address].name); + } } if (scriptPubKey.IsPayToScriptHash()) { @@ -3122,7 +3260,6 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) + HelpExampleCli("sendrawtransaction", "\"signedtransactionhex\"") ); - ObserveSafeMode(); RPCTypeCheck(request.params, {UniValue::VSTR}); // Make sure the results are valid at least up to the most recent block @@ -3199,7 +3336,7 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) subtractFeeFromOutputs = options["subtractFeeFromOutputs"].get_array(); if (options.exists("replaceable")) { - coinControl.signalRbf = options["replaceable"].get_bool(); + coinControl.m_signal_bip125_rbf = options["replaceable"].get_bool(); } if (options.exists("conf_target")) { if (options.exists("feeRate")) { @@ -3388,7 +3525,7 @@ UniValue bumpfee(const JSONRPCRequest& request) // optional parameters CAmount totalFee = 0; CCoinControl coin_control; - coin_control.signalRbf = true; + coin_control.m_signal_bip125_rbf = true; if (!request.params[1].isNull()) { UniValue options = request.params[1]; RPCTypeCheckObj(options, @@ -3412,7 +3549,7 @@ UniValue bumpfee(const JSONRPCRequest& request) } if (options.exists("replaceable")) { - coin_control.signalRbf = options["replaceable"].get_bool(); + coin_control.m_signal_bip125_rbf = options["replaceable"].get_bool(); } if (options.exists("estimate_mode")) { if (!FeeModeFromString(options["estimate_mode"].get_str(), coin_control.m_fee_mode)) { @@ -3715,6 +3852,17 @@ UniValue DescribeWalletAddress(CWallet* pwallet, const CTxDestination& dest) return ret; } +/** Convert CAddressBookData to JSON record. */ +static UniValue AddressBookDataToJSON(const CAddressBookData& data, const bool verbose) +{ + UniValue ret(UniValue::VOBJ); + if (verbose) { + ret.pushKV("name", data.name); + } + ret.pushKV("purpose", data.purpose); + return ret; +} + UniValue getaddressinfo(const JSONRPCRequest& request) { CWallet * const pwallet = GetWalletForJSONRPCRequest(request); @@ -3750,10 +3898,18 @@ UniValue getaddressinfo(const JSONRPCRequest& request) " \"pubkey\" : \"publickeyhex\", (string, optional) The hex value of the raw public key, for single-key addresses (possibly embedded in P2SH or P2WSH)\n" " \"embedded\" : {...}, (object, optional) Information about the address embedded in P2SH or P2WSH, if relevant and known. It includes all getaddressinfo output fields for the embedded address, excluding metadata (\"timestamp\", \"hdkeypath\", \"hdmasterkeyid\") and relation to the wallet (\"ismine\", \"iswatchonly\", \"account\").\n" " \"iscompressed\" : true|false, (boolean) If the address is compressed\n" - " \"account\" : \"account\" (string) The account associated with the address, \"\" is the default account\n" + " \"label\" : \"label\" (string) The label associated with the address, \"\" is the default account\n" + " \"account\" : \"account\" (string) DEPRECATED. This field will be removed in V0.18. To see this deprecated field, start bitcoind with -deprecatedrpc=accounts. The account associated with the address, \"\" is the default account\n" " \"timestamp\" : timestamp, (number, optional) The creation time of the key if available in seconds since epoch (Jan 1 1970 GMT)\n" " \"hdkeypath\" : \"keypath\" (string, optional) The HD keypath if the key is HD and available\n" " \"hdmasterkeyid\" : \"<hash160>\" (string, optional) The Hash160 of the HD master pubkey\n" + " \"labels\" (object) Array of labels associated with the address.\n" + " [\n" + " { (json object of label data)\n" + " \"name\": \"labelname\" (string) The label\n" + " \"purpose\": \"string\" (string) Purpose of address (\"send\" for sending address, \"receive\" for receiving address)\n" + " },...\n" + " ]\n" "}\n" "\nExamples:\n" + HelpExampleCli("getaddressinfo", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"") @@ -3783,7 +3939,10 @@ UniValue getaddressinfo(const JSONRPCRequest& request) UniValue detail = DescribeWalletAddress(pwallet, dest); ret.pushKVs(detail); if (pwallet->mapAddressBook.count(dest)) { - ret.pushKV("account", pwallet->mapAddressBook[dest].name); + ret.pushKV("label", pwallet->mapAddressBook[dest].name); + if (IsDeprecatedRPCEnabled("accounts")) { + ret.pushKV("account", pwallet->mapAddressBook[dest].name); + } } const CKeyMetadata* meta = nullptr; CKeyID key_id = GetKeyForDestination(*pwallet, dest); @@ -3806,6 +3965,112 @@ UniValue getaddressinfo(const JSONRPCRequest& request) ret.pushKV("hdmasterkeyid", meta->hdMasterKeyID.GetHex()); } } + + // Currently only one label can be associated with an address, return an array + // so the API remains stable if we allow multiple labels to be associated with + // an address. + UniValue labels(UniValue::VARR); + std::map<CTxDestination, CAddressBookData>::iterator mi = pwallet->mapAddressBook.find(dest); + if (mi != pwallet->mapAddressBook.end()) { + labels.push_back(AddressBookDataToJSON(mi->second, true)); + } + ret.pushKV("labels", std::move(labels)); + + return ret; +} + +UniValue getaddressesbylabel(const JSONRPCRequest& request) +{ + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { + return NullUniValue; + } + + if (request.fHelp || request.params.size() != 1) + throw std::runtime_error( + "getaddressesbylabel \"label\"\n" + "\nReturns the list of addresses assigned the specified label.\n" + "\nArguments:\n" + "1. \"label\" (string, required) The label.\n" + "\nResult:\n" + "{ (json object with addresses as keys)\n" + " \"address\": { (json object with information about address)\n" + " \"purpose\": \"string\" (string) Purpose of address (\"send\" for sending address, \"receive\" for receiving address)\n" + " },...\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("getaddressesbylabel", "\"tabby\"") + + HelpExampleRpc("getaddressesbylabel", "\"tabby\"") + ); + + LOCK(pwallet->cs_wallet); + + std::string label = LabelFromValue(request.params[0]); + + // Find all addresses that have the given label + UniValue ret(UniValue::VOBJ); + for (const std::pair<CTxDestination, CAddressBookData>& item : pwallet->mapAddressBook) { + if (item.second.name == label) { + ret.pushKV(EncodeDestination(item.first), AddressBookDataToJSON(item.second, false)); + } + } + + if (ret.empty()) { + throw JSONRPCError(RPC_WALLET_INVALID_LABEL_NAME, std::string("No addresses with label " + label)); + } + + return ret; +} + +UniValue listlabels(const JSONRPCRequest& request) +{ + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { + return NullUniValue; + } + + if (request.fHelp || request.params.size() > 1) + throw std::runtime_error( + "listlabels ( \"purpose\" )\n" + "\nReturns the list of all labels, or labels that are assigned to addresses with a specific purpose.\n" + "\nArguments:\n" + "1. \"purpose\" (string, optional) Address purpose to list labels for ('send','receive'). An empty string is the same as not providing this argument.\n" + "\nResult:\n" + "[ (json array of string)\n" + " \"label\", (string) Label name\n" + " ...\n" + "]\n" + "\nExamples:\n" + "\nList all labels\n" + + HelpExampleCli("listlabels", "") + + "\nList labels that have receiving addresses\n" + + HelpExampleCli("listlabels", "receive") + + "\nList labels that have sending addresses\n" + + HelpExampleCli("listlabels", "send") + + "\nAs json rpc call\n" + + HelpExampleRpc("listlabels", "receive") + ); + + LOCK(pwallet->cs_wallet); + + std::string purpose; + if (!request.params[0].isNull()) { + purpose = request.params[0].get_str(); + } + + // Add to a set to sort by label name, then insert into Univalue array + std::set<std::string> label_set; + for (const std::pair<CTxDestination, CAddressBookData>& entry : pwallet->mapAddressBook) { + if (purpose.empty() || entry.second.purpose == purpose) { + label_set.insert(entry.second.name); + } + } + + UniValue ret(UniValue::VARR); + for (const std::string& name : label_set) { + ret.push_back(name); + } + return ret; } @@ -3835,16 +4100,10 @@ static const CRPCCommand commands[] = { "wallet", "dumpprivkey", &dumpprivkey, {"address"} }, { "wallet", "dumpwallet", &dumpwallet, {"filename"} }, { "wallet", "encryptwallet", &encryptwallet, {"passphrase"} }, - { "wallet", "getlabeladdress", &getlabeladdress, {"label"} }, - { "wallet", "getaccountaddress", &getlabeladdress, {"account"} }, - { "wallet", "getaccount", &getaccount, {"address"} }, - { "wallet", "getaddressesbyaccount", &getaddressesbyaccount, {"account"} }, { "wallet", "getaddressinfo", &getaddressinfo, {"address"} }, { "wallet", "getbalance", &getbalance, {"account","minconf","include_watchonly"} }, { "wallet", "getnewaddress", &getnewaddress, {"label|account","address_type"} }, { "wallet", "getrawchangeaddress", &getrawchangeaddress, {"address_type"} }, - { "wallet", "getreceivedbylabel", &getreceivedbylabel, {"label","minconf"} }, - { "wallet", "getreceivedbyaccount", &getreceivedbylabel, {"account","minconf"} }, { "wallet", "getreceivedbyaddress", &getreceivedbyaddress, {"address","minconf"} }, { "wallet", "gettransaction", &gettransaction, {"txid","include_watchonly"} }, { "wallet", "getunconfirmedbalance", &getunconfirmedbalance, {} }, @@ -3856,23 +4115,17 @@ static const CRPCCommand commands[] = { "wallet", "importprunedfunds", &importprunedfunds, {"rawtransaction","txoutproof"} }, { "wallet", "importpubkey", &importpubkey, {"pubkey","label","rescan"} }, { "wallet", "keypoolrefill", &keypoolrefill, {"newsize"} }, - { "wallet", "listaccounts", &listaccounts, {"minconf","include_watchonly"} }, { "wallet", "listaddressgroupings", &listaddressgroupings, {} }, { "wallet", "listlockunspent", &listlockunspent, {} }, - { "wallet", "listreceivedbylabel", &listreceivedbylabel, {"minconf","include_empty","include_watchonly"} }, - { "wallet", "listreceivedbyaccount", &listreceivedbylabel, {"minconf","include_empty","include_watchonly"} }, { "wallet", "listreceivedbyaddress", &listreceivedbyaddress, {"minconf","include_empty","include_watchonly","address_filter"} }, { "wallet", "listsinceblock", &listsinceblock, {"blockhash","target_confirmations","include_watchonly","include_removed"} }, - { "wallet", "listtransactions", &listtransactions, {"account","count","skip","include_watchonly"} }, + { "wallet", "listtransactions", &listtransactions, {"account|dummy","count","skip","include_watchonly"} }, { "wallet", "listunspent", &listunspent, {"minconf","maxconf","addresses","include_unsafe","query_options"} }, { "wallet", "listwallets", &listwallets, {} }, { "wallet", "lockunspent", &lockunspent, {"unlock","transactions"} }, - { "wallet", "move", &movecmd, {"fromaccount","toaccount","amount","minconf","comment"} }, { "wallet", "sendfrom", &sendfrom, {"fromaccount","toaddress","amount","minconf","comment","comment_to"} }, - { "wallet", "sendmany", &sendmany, {"fromaccount","amounts","minconf","comment","subtractfeefrom","replaceable","conf_target","estimate_mode"} }, + { "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", "setlabel", &setlabel, {"address","label"} }, - { "wallet", "setaccount", &setlabel, {"address","account"} }, { "wallet", "settxfee", &settxfee, {"amount"} }, { "wallet", "signmessage", &signmessage, {"address","message"} }, { "wallet", "signrawtransactionwithwallet", &signrawtransactionwithwallet, {"hexstring","prevtxs","sighashtype"} }, @@ -3882,6 +4135,24 @@ static const CRPCCommand commands[] = { "wallet", "removeprunedfunds", &removeprunedfunds, {"txid"} }, { "wallet", "rescanblockchain", &rescanblockchain, {"start_height", "stop_height"} }, + /** Account functions (deprecated) */ + { "wallet", "getaccountaddress", &getlabeladdress, {"account"} }, + { "wallet", "getaccount", &getaccount, {"address"} }, + { "wallet", "getaddressesbyaccount", &getaddressesbyaccount, {"account"} }, + { "wallet", "getreceivedbyaccount", &getreceivedbylabel, {"account","minconf"} }, + { "wallet", "listaccounts", &listaccounts, {"minconf","include_watchonly"} }, + { "wallet", "listreceivedbyaccount", &listreceivedbylabel, {"minconf","include_empty","include_watchonly"} }, + { "wallet", "setaccount", &setlabel, {"address","account"} }, + { "wallet", "move", &movecmd, {"fromaccount","toaccount","amount","minconf","comment"} }, + + /** Label functions (to replace non-balance account functions) */ + { "wallet", "getlabeladdress", &getlabeladdress, {"label","force"} }, + { "wallet", "getaddressesbylabel", &getaddressesbylabel, {"label"} }, + { "wallet", "getreceivedbylabel", &getreceivedbylabel, {"label","minconf"} }, + { "wallet", "listlabels", &listlabels, {"purpose"} }, + { "wallet", "listreceivedbylabel", &listreceivedbylabel, {"minconf","include_empty","include_watchonly"} }, + { "wallet", "setlabel", &setlabel, {"address","label"} }, + { "generating", "generate", &generate, {"nblocks","maxtries"} }, }; diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp index 184a8a3f1f..ac47d4448a 100644 --- a/src/wallet/test/coinselector_tests.cpp +++ b/src/wallet/test/coinselector_tests.cpp @@ -28,7 +28,7 @@ std::vector<std::unique_ptr<CWalletTx>> wtxn; typedef std::set<CInputCoin> CoinSet; static std::vector<COutput> vCoins; -static CWallet testWallet("dummy", CWalletDBWrapper::CreateDummy()); +static CWallet testWallet("dummy", WalletDatabase::CreateDummy()); static CAmount balance = 0; CoinEligibilityFilter filter_standard(1, 6, 0); diff --git a/src/wallet/test/crypto_tests.cpp b/src/wallet/test/wallet_crypto_tests.cpp index d8c0cdf0f9..e04c0af1dd 100644 --- a/src/wallet/test/crypto_tests.cpp +++ b/src/wallet/test/wallet_crypto_tests.cpp @@ -10,7 +10,7 @@ #include <boost/test/unit_test.hpp> -BOOST_FIXTURE_TEST_SUITE(crypto_tests, BasicTestingSetup) +BOOST_FIXTURE_TEST_SUITE(wallet_crypto_tests, BasicTestingSetup) class TestCrypter { diff --git a/src/wallet/test/wallet_test_fixture.cpp b/src/wallet/test/wallet_test_fixture.cpp index 5c550742c8..6129e337ce 100644 --- a/src/wallet/test/wallet_test_fixture.cpp +++ b/src/wallet/test/wallet_test_fixture.cpp @@ -6,10 +6,9 @@ #include <rpc/server.h> #include <wallet/db.h> -#include <wallet/wallet.h> WalletTestingSetup::WalletTestingSetup(const std::string& chainName): - TestingSetup(chainName), m_wallet("mock", CWalletDBWrapper::CreateMock()) + TestingSetup(chainName), m_wallet("mock", WalletDatabase::CreateMock()) { bool fFirstRun; m_wallet.LoadWallet(fFirstRun); diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index e93e0f1966..99c963a348 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -46,7 +46,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) // Verify ScanForWalletTransactions picks up transactions in both the old // and new block files. { - CWallet wallet("dummy", CWalletDBWrapper::CreateDummy()); + CWallet wallet("dummy", WalletDatabase::CreateDummy()); AddKey(wallet, coinbaseKey); WalletRescanReserver reserver(&wallet); reserver.reserve(); @@ -61,7 +61,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) // Verify ScanForWalletTransactions only picks transactions in the new block // file. { - CWallet wallet("dummy", CWalletDBWrapper::CreateDummy()); + CWallet wallet("dummy", WalletDatabase::CreateDummy()); AddKey(wallet, coinbaseKey); WalletRescanReserver reserver(&wallet); reserver.reserve(); @@ -73,8 +73,8 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) // before the missing block, and success for a key whose creation time is // after. { - CWallet wallet("dummy", CWalletDBWrapper::CreateDummy()); - vpwallets.insert(vpwallets.begin(), &wallet); + CWallet wallet("dummy", WalletDatabase::CreateDummy()); + AddWallet(&wallet); UniValue keys; keys.setArray(); UniValue key; @@ -105,7 +105,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) "downloading and rescanning the relevant blocks (see -reindex and -rescan " "options).\"}},{\"success\":true}]", 0, oldTip->GetBlockTimeMax(), TIMESTAMP_WINDOW)); - vpwallets.erase(vpwallets.begin()); + RemoveWallet(&wallet); } } @@ -119,20 +119,20 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup) // will pick up both blocks, not just the first. const int64_t BLOCK_TIME = chainActive.Tip()->GetBlockTimeMax() + 5; SetMockTime(BLOCK_TIME); - coinbaseTxns.emplace_back(*CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]); - coinbaseTxns.emplace_back(*CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]); + m_coinbase_txns.emplace_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]); + m_coinbase_txns.emplace_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]); // Set key birthday to block time increased by the timestamp window, so // rescan will start at the block time. const int64_t KEY_TIME = BLOCK_TIME + TIMESTAMP_WINDOW; SetMockTime(KEY_TIME); - coinbaseTxns.emplace_back(*CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]); + m_coinbase_txns.emplace_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]); LOCK(cs_main); // Import key into wallet and call dumpwallet to create backup file. { - CWallet wallet("dummy", CWalletDBWrapper::CreateDummy()); + CWallet wallet("dummy", WalletDatabase::CreateDummy()); LOCK(wallet.cs_wallet); wallet.mapKeyMetadata[coinbaseKey.GetPubKey().GetID()].nCreateTime = KEY_TIME; wallet.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()); @@ -140,33 +140,34 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup) JSONRPCRequest request; request.params.setArray(); request.params.push_back((pathTemp / "wallet.backup").string()); - vpwallets.insert(vpwallets.begin(), &wallet); + AddWallet(&wallet); ::dumpwallet(request); + RemoveWallet(&wallet); } // Call importwallet RPC and verify all blocks with timestamps >= BLOCK_TIME // were scanned, and no prior blocks were scanned. { - CWallet wallet("dummy", CWalletDBWrapper::CreateDummy()); + CWallet wallet("dummy", WalletDatabase::CreateDummy()); JSONRPCRequest request; request.params.setArray(); request.params.push_back((pathTemp / "wallet.backup").string()); - vpwallets[0] = &wallet; + AddWallet(&wallet); ::importwallet(request); + RemoveWallet(&wallet); LOCK(wallet.cs_wallet); - BOOST_CHECK_EQUAL(wallet.mapWallet.size(), 3); - BOOST_CHECK_EQUAL(coinbaseTxns.size(), 103); - for (size_t i = 0; i < coinbaseTxns.size(); ++i) { - bool found = wallet.GetWalletTx(coinbaseTxns[i].GetHash()); + BOOST_CHECK_EQUAL(wallet.mapWallet.size(), 3U); + BOOST_CHECK_EQUAL(m_coinbase_txns.size(), 103U); + for (size_t i = 0; i < m_coinbase_txns.size(); ++i) { + bool found = wallet.GetWalletTx(m_coinbase_txns[i]->GetHash()); bool expected = i >= 100; BOOST_CHECK_EQUAL(found, expected); } } SetMockTime(0); - vpwallets.erase(vpwallets.begin()); } // Check that GetImmatureCredit() returns a newly calculated value instead of @@ -177,8 +178,8 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup) // debit functions. BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup) { - CWallet wallet("dummy", CWalletDBWrapper::CreateDummy()); - CWalletTx wtx(&wallet, MakeTransactionRef(coinbaseTxns.back())); + CWallet wallet("dummy", WalletDatabase::CreateDummy()); + CWalletTx wtx(&wallet, m_coinbase_txns.back()); LOCK2(cs_main, wallet.cs_wallet); wtx.hashBlock = chainActive.Tip()->GetBlockHash(); wtx.nIndex = 0; @@ -259,7 +260,7 @@ BOOST_AUTO_TEST_CASE(LoadReceiveRequests) m_wallet.AddDestData(dest, "rr1", "val_rr1"); auto values = m_wallet.GetDestValues("rr"); - BOOST_CHECK_EQUAL(values.size(), 2); + BOOST_CHECK_EQUAL(values.size(), 2U); BOOST_CHECK_EQUAL(values[0], "val_rr0"); BOOST_CHECK_EQUAL(values[1], "val_rr1"); } @@ -270,7 +271,7 @@ public: ListCoinsTestingSetup() { CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())); - wallet = MakeUnique<CWallet>("mock", CWalletDBWrapper::CreateMock()); + wallet = MakeUnique<CWallet>("mock", WalletDatabase::CreateMock()); bool firstRun; wallet->LoadWallet(firstRun); AddKey(*wallet, coinbaseKey); @@ -318,9 +319,9 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup) // Confirm ListCoins initially returns 1 coin grouped under coinbaseKey // address. auto list = wallet->ListCoins(); - BOOST_CHECK_EQUAL(list.size(), 1); + BOOST_CHECK_EQUAL(list.size(), 1U); BOOST_CHECK_EQUAL(boost::get<CKeyID>(list.begin()->first).ToString(), coinbaseAddress); - BOOST_CHECK_EQUAL(list.begin()->second.size(), 1); + BOOST_CHECK_EQUAL(list.begin()->second.size(), 1U); // Check initial balance from one mature coinbase transaction. BOOST_CHECK_EQUAL(50 * COIN, wallet->GetAvailableBalance()); @@ -331,16 +332,16 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup) // pubkey. AddTx(CRecipient{GetScriptForRawPubKey({}), 1 * COIN, false /* subtract fee */}); list = wallet->ListCoins(); - BOOST_CHECK_EQUAL(list.size(), 1); + BOOST_CHECK_EQUAL(list.size(), 1U); BOOST_CHECK_EQUAL(boost::get<CKeyID>(list.begin()->first).ToString(), coinbaseAddress); - BOOST_CHECK_EQUAL(list.begin()->second.size(), 2); + BOOST_CHECK_EQUAL(list.begin()->second.size(), 2U); // Lock both coins. Confirm number of available coins drops to 0. { LOCK2(cs_main, wallet->cs_wallet); std::vector<COutput> available; wallet->AvailableCoins(available); - BOOST_CHECK_EQUAL(available.size(), 2); + BOOST_CHECK_EQUAL(available.size(), 2U); } for (const auto& group : list) { for (const auto& coin : group.second) { @@ -352,14 +353,14 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup) LOCK2(cs_main, wallet->cs_wallet); std::vector<COutput> available; wallet->AvailableCoins(available); - BOOST_CHECK_EQUAL(available.size(), 0); + BOOST_CHECK_EQUAL(available.size(), 0U); } // Confirm ListCoins still returns same result as before, despite coins // being locked. list = wallet->ListCoins(); - BOOST_CHECK_EQUAL(list.size(), 1); + BOOST_CHECK_EQUAL(list.size(), 1U); BOOST_CHECK_EQUAL(boost::get<CKeyID>(list.begin()->first).ToString(), coinbaseAddress); - BOOST_CHECK_EQUAL(list.begin()->second.size(), 2); + BOOST_CHECK_EQUAL(list.begin()->second.size(), 2U); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 965eb067cc..9533e6ff56 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -8,11 +8,10 @@ #include <checkpoints.h> #include <chain.h> #include <wallet/coincontrol.h> -#include <wallet/coinselection.h> #include <consensus/consensus.h> #include <consensus/validation.h> #include <fs.h> -#include <wallet/init.h> +#include <init.h> #include <key.h> #include <key_io.h> #include <keystore.h> @@ -27,38 +26,60 @@ #include <scheduler.h> #include <timedata.h> #include <txmempool.h> -#include <util.h> #include <utilmoneystr.h> #include <wallet/fees.h> +#include <algorithm> #include <assert.h> #include <future> #include <boost/algorithm/string/replace.hpp> -std::vector<CWalletRef> vpwallets; -/** Transaction fee set by the user */ -CFeeRate payTxFee(DEFAULT_TRANSACTION_FEE); -unsigned int nTxConfirmTarget = DEFAULT_TX_CONFIRM_TARGET; -bool bSpendZeroConfChange = DEFAULT_SPEND_ZEROCONF_CHANGE; -bool fWalletRbf = DEFAULT_WALLET_RBF; -bool g_wallet_allow_fallback_fee = true; //<! will be defined via chainparams +static CCriticalSection cs_wallets; +static std::vector<CWallet*> vpwallets GUARDED_BY(cs_wallets); -const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000; +bool AddWallet(CWallet* wallet) +{ + LOCK(cs_wallets); + assert(wallet); + std::vector<CWallet*>::const_iterator i = std::find(vpwallets.begin(), vpwallets.end(), wallet); + if (i != vpwallets.end()) return false; + vpwallets.push_back(wallet); + return true; +} -/** - * Fees smaller than this (in satoshi) are considered zero fee (for transaction creation) - * Override with -mintxfee - */ -CFeeRate CWallet::minTxFee = CFeeRate(DEFAULT_TRANSACTION_MINFEE); -/** - * If fee estimation does not have enough data to provide estimates, use this fee instead. - * Has no effect if not using fee estimation - * Override with -fallbackfee - */ -CFeeRate CWallet::fallbackFee = CFeeRate(DEFAULT_FALLBACK_FEE); +bool RemoveWallet(CWallet* wallet) +{ + LOCK(cs_wallets); + assert(wallet); + std::vector<CWallet*>::iterator i = std::find(vpwallets.begin(), vpwallets.end(), wallet); + if (i == vpwallets.end()) return false; + vpwallets.erase(i); + return true; +} + +bool HasWallets() +{ + LOCK(cs_wallets); + return !vpwallets.empty(); +} + +std::vector<CWallet*> GetWallets() +{ + LOCK(cs_wallets); + return vpwallets; +} + +CWallet* GetWallet(const std::string& name) +{ + LOCK(cs_wallets); + for (CWallet* wallet : vpwallets) { + if (wallet->GetName() == name) return wallet; + } + return nullptr; +} -CFeeRate CWallet::m_discard_rate = CFeeRate(DEFAULT_DISCARD_FEE); +const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000; const uint256 CMerkleTx::ABANDON_HASH(uint256S("0000000000000000000000000000000000000000000000000000000000000001")); @@ -132,7 +153,7 @@ const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const return &(it->second); } -CPubKey CWallet::GenerateNewKey(CWalletDB &walletdb, bool internal) +CPubKey CWallet::GenerateNewKey(WalletBatch &batch, bool internal) { AssertLockHeld(cs_wallet); // mapKeyMetadata bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets @@ -145,7 +166,7 @@ CPubKey CWallet::GenerateNewKey(CWalletDB &walletdb, bool internal) // use HD key derivation if HD was enabled during wallet creation if (IsHDEnabled()) { - DeriveNewChildKey(walletdb, metadata, secret, (CanSupportFeature(FEATURE_HD_SPLIT) ? internal : false)); + DeriveNewChildKey(batch, metadata, secret, (CanSupportFeature(FEATURE_HD_SPLIT) ? internal : false)); } else { secret.MakeNewKey(fCompressed); } @@ -161,13 +182,13 @@ CPubKey CWallet::GenerateNewKey(CWalletDB &walletdb, bool internal) mapKeyMetadata[pubkey.GetID()] = metadata; UpdateTimeFirstKey(nCreationTime); - if (!AddKeyPubKeyWithDB(walletdb, secret, pubkey)) { + if (!AddKeyPubKeyWithDB(batch, secret, pubkey)) { throw std::runtime_error(std::string(__func__) + ": AddKey failed"); } return pubkey; } -void CWallet::DeriveNewChildKey(CWalletDB &walletdb, CKeyMetadata& metadata, CKey& secret, bool internal) +void CWallet::DeriveNewChildKey(WalletBatch &batch, CKeyMetadata& metadata, CKey& secret, bool internal) { // for now we use a fixed keypath scheme of m/0'/0'/k CKey key; //master key seed (256bit) @@ -209,26 +230,26 @@ void CWallet::DeriveNewChildKey(CWalletDB &walletdb, CKeyMetadata& metadata, CKe secret = childKey.key; metadata.hdMasterKeyID = hdChain.masterKeyID; // update the chain model in the database - if (!walletdb.WriteHDChain(hdChain)) + if (!batch.WriteHDChain(hdChain)) throw std::runtime_error(std::string(__func__) + ": Writing HD chain model failed"); } -bool CWallet::AddKeyPubKeyWithDB(CWalletDB &walletdb, const CKey& secret, const CPubKey &pubkey) +bool CWallet::AddKeyPubKeyWithDB(WalletBatch &batch, const CKey& secret, const CPubKey &pubkey) { AssertLockHeld(cs_wallet); // mapKeyMetadata // CCryptoKeyStore has no concept of wallet databases, but calls AddCryptedKey // which is overridden below. To avoid flushes, the database handle is // tunneled through to it. - bool needsDB = !pwalletdbEncryption; + bool needsDB = !encrypted_batch; if (needsDB) { - pwalletdbEncryption = &walletdb; + encrypted_batch = &batch; } if (!CCryptoKeyStore::AddKeyPubKey(secret, pubkey)) { - if (needsDB) pwalletdbEncryption = nullptr; + if (needsDB) encrypted_batch = nullptr; return false; } - if (needsDB) pwalletdbEncryption = nullptr; + if (needsDB) encrypted_batch = nullptr; // check if we need to remove from watch-only CScript script; @@ -242,7 +263,7 @@ bool CWallet::AddKeyPubKeyWithDB(CWalletDB &walletdb, const CKey& secret, const } if (!IsCrypted()) { - return walletdb.WriteKey(pubkey, + return batch.WriteKey(pubkey, secret.GetPrivKey(), mapKeyMetadata[pubkey.GetID()]); } @@ -251,8 +272,8 @@ bool CWallet::AddKeyPubKeyWithDB(CWalletDB &walletdb, const CKey& secret, const bool CWallet::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey) { - CWalletDB walletdb(*dbw); - return CWallet::AddKeyPubKeyWithDB(walletdb, secret, pubkey); + WalletBatch batch(*database); + return CWallet::AddKeyPubKeyWithDB(batch, secret, pubkey); } bool CWallet::AddCryptedKey(const CPubKey &vchPubKey, @@ -262,12 +283,12 @@ bool CWallet::AddCryptedKey(const CPubKey &vchPubKey, return false; { LOCK(cs_wallet); - if (pwalletdbEncryption) - return pwalletdbEncryption->WriteCryptedKey(vchPubKey, + if (encrypted_batch) + return encrypted_batch->WriteCryptedKey(vchPubKey, vchCryptedSecret, mapKeyMetadata[vchPubKey.GetID()]); else - return CWalletDB(*dbw).WriteCryptedKey(vchPubKey, + return WalletBatch(*database).WriteCryptedKey(vchPubKey, vchCryptedSecret, mapKeyMetadata[vchPubKey.GetID()]); } @@ -314,7 +335,7 @@ bool CWallet::AddCScript(const CScript& redeemScript) { if (!CCryptoKeyStore::AddCScript(redeemScript)) return false; - return CWalletDB(*dbw).WriteCScript(Hash160(redeemScript), redeemScript); + return WalletBatch(*database).WriteCScript(Hash160(redeemScript), redeemScript); } bool CWallet::LoadCScript(const CScript& redeemScript) @@ -340,7 +361,7 @@ bool CWallet::AddWatchOnly(const CScript& dest) const CKeyMetadata& meta = m_script_metadata[CScriptID(dest)]; UpdateTimeFirstKey(meta.nCreateTime); NotifyWatchonlyChanged(true); - return CWalletDB(*dbw).WriteWatchOnly(dest, meta); + return WalletBatch(*database).WriteWatchOnly(dest, meta); } bool CWallet::AddWatchOnly(const CScript& dest, int64_t nCreateTime) @@ -356,7 +377,7 @@ bool CWallet::RemoveWatchOnly(const CScript &dest) return false; if (!HaveWatchOnly()) NotifyWatchonlyChanged(false); - if (!CWalletDB(*dbw).EraseWatchOnly(dest)) + if (!WalletBatch(*database).EraseWatchOnly(dest)) return false; return true; @@ -422,7 +443,7 @@ bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, return false; if (!crypter.Encrypt(_vMasterKey, pMasterKey.second.vchCryptedKey)) return false; - CWalletDB(*dbw).WriteMasterKey(pMasterKey.first, pMasterKey.second); + WalletBatch(*database).WriteMasterKey(pMasterKey.first, pMasterKey.second); if (fWasLocked) Lock(); return true; @@ -433,13 +454,13 @@ bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, return false; } -void CWallet::SetBestChain(const CBlockLocator& loc) +void CWallet::ChainStateFlushed(const CBlockLocator& loc) { - CWalletDB walletdb(*dbw); - walletdb.WriteBestBlock(loc); + WalletBatch batch(*database); + batch.WriteBestBlock(loc); } -bool CWallet::SetMinVersion(enum WalletFeature nVersion, CWalletDB* pwalletdbIn, bool fExplicit) +bool CWallet::SetMinVersion(enum WalletFeature nVersion, WalletBatch* batch_in, bool fExplicit) { LOCK(cs_wallet); // nWalletVersion if (nWalletVersion >= nVersion) @@ -455,11 +476,11 @@ bool CWallet::SetMinVersion(enum WalletFeature nVersion, CWalletDB* pwalletdbIn, nWalletMaxVersion = nVersion; { - CWalletDB* pwalletdb = pwalletdbIn ? pwalletdbIn : new CWalletDB(*dbw); + WalletBatch* batch = batch_in ? batch_in : new WalletBatch(*database); if (nWalletVersion > 40000) - pwalletdb->WriteMinVersion(nWalletVersion); - if (!pwalletdbIn) - delete pwalletdb; + batch->WriteMinVersion(nWalletVersion); + if (!batch_in) + delete batch; } return true; @@ -509,7 +530,7 @@ bool CWallet::HasWalletSpend(const uint256& txid) const void CWallet::Flush(bool shutdown) { - dbw->Flush(shutdown); + database->Flush(shutdown); } void CWallet::SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator> range) @@ -632,36 +653,36 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) { LOCK(cs_wallet); mapMasterKeys[++nMasterKeyMaxID] = kMasterKey; - assert(!pwalletdbEncryption); - pwalletdbEncryption = new CWalletDB(*dbw); - if (!pwalletdbEncryption->TxnBegin()) { - delete pwalletdbEncryption; - pwalletdbEncryption = nullptr; + assert(!encrypted_batch); + encrypted_batch = new WalletBatch(*database); + if (!encrypted_batch->TxnBegin()) { + delete encrypted_batch; + encrypted_batch = nullptr; return false; } - pwalletdbEncryption->WriteMasterKey(nMasterKeyMaxID, kMasterKey); + encrypted_batch->WriteMasterKey(nMasterKeyMaxID, kMasterKey); if (!EncryptKeys(_vMasterKey)) { - pwalletdbEncryption->TxnAbort(); - delete pwalletdbEncryption; + encrypted_batch->TxnAbort(); + delete encrypted_batch; // We now probably have half of our keys encrypted in memory, and half not... // die and let the user reload the unencrypted wallet. assert(false); } // Encryption was introduced in version 0.4.0 - SetMinVersion(FEATURE_WALLETCRYPT, pwalletdbEncryption, true); + SetMinVersion(FEATURE_WALLETCRYPT, encrypted_batch, true); - if (!pwalletdbEncryption->TxnCommit()) { - delete pwalletdbEncryption; + if (!encrypted_batch->TxnCommit()) { + delete encrypted_batch; // We now have keys encrypted in memory, but not on disk... // die to avoid confusion and let the user reload the unencrypted wallet. assert(false); } - delete pwalletdbEncryption; - pwalletdbEncryption = nullptr; + delete encrypted_batch; + encrypted_batch = nullptr; Lock(); Unlock(strWalletPassphrase); @@ -678,7 +699,7 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) // Need to completely rewrite the wallet file; if we don't, bdb might keep // bits of the unencrypted private key in slack space in the database file. - dbw->Rewrite(); + database->Rewrite(); } NotifyStatusChanged(this); @@ -689,7 +710,7 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) DBErrors CWallet::ReorderTransactions() { LOCK(cs_wallet); - CWalletDB walletdb(*dbw); + WalletBatch batch(*database); // Old wallets didn't have any defined order for transactions // Probably a bad idea to change the output of this @@ -705,7 +726,7 @@ DBErrors CWallet::ReorderTransactions() txByTime.insert(std::make_pair(wtx->nTimeReceived, TxPair(wtx, nullptr))); } std::list<CAccountingEntry> acentries; - walletdb.ListAccountCreditDebit("", acentries); + batch.ListAccountCreditDebit("", acentries); for (CAccountingEntry& entry : acentries) { txByTime.insert(std::make_pair(entry.nTime, TxPair(nullptr, &entry))); @@ -726,11 +747,11 @@ DBErrors CWallet::ReorderTransactions() if (pwtx) { - if (!walletdb.WriteTx(*pwtx)) + if (!batch.WriteTx(*pwtx)) return DBErrors::LOAD_FAIL; } else - if (!walletdb.WriteAccountingEntry(pacentry->nEntryNo, *pacentry)) + if (!batch.WriteAccountingEntry(pacentry->nEntryNo, *pacentry)) return DBErrors::LOAD_FAIL; } else @@ -750,60 +771,60 @@ DBErrors CWallet::ReorderTransactions() // Since we're changing the order, write it back if (pwtx) { - if (!walletdb.WriteTx(*pwtx)) + if (!batch.WriteTx(*pwtx)) return DBErrors::LOAD_FAIL; } else - if (!walletdb.WriteAccountingEntry(pacentry->nEntryNo, *pacentry)) + if (!batch.WriteAccountingEntry(pacentry->nEntryNo, *pacentry)) return DBErrors::LOAD_FAIL; } } - walletdb.WriteOrderPosNext(nOrderPosNext); + batch.WriteOrderPosNext(nOrderPosNext); return DBErrors::LOAD_OK; } -int64_t CWallet::IncOrderPosNext(CWalletDB *pwalletdb) +int64_t CWallet::IncOrderPosNext(WalletBatch *batch) { AssertLockHeld(cs_wallet); // nOrderPosNext int64_t nRet = nOrderPosNext++; - if (pwalletdb) { - pwalletdb->WriteOrderPosNext(nOrderPosNext); + if (batch) { + batch->WriteOrderPosNext(nOrderPosNext); } else { - CWalletDB(*dbw).WriteOrderPosNext(nOrderPosNext); + WalletBatch(*database).WriteOrderPosNext(nOrderPosNext); } return nRet; } bool CWallet::AccountMove(std::string strFrom, std::string strTo, CAmount nAmount, std::string strComment) { - CWalletDB walletdb(*dbw); - if (!walletdb.TxnBegin()) + WalletBatch batch(*database); + if (!batch.TxnBegin()) return false; int64_t nNow = GetAdjustedTime(); // Debit CAccountingEntry debit; - debit.nOrderPos = IncOrderPosNext(&walletdb); + debit.nOrderPos = IncOrderPosNext(&batch); debit.strAccount = strFrom; debit.nCreditDebit = -nAmount; debit.nTime = nNow; debit.strOtherAccount = strTo; debit.strComment = strComment; - AddAccountingEntry(debit, &walletdb); + AddAccountingEntry(debit, &batch); // Credit CAccountingEntry credit; - credit.nOrderPos = IncOrderPosNext(&walletdb); + credit.nOrderPos = IncOrderPosNext(&batch); credit.strAccount = strTo; credit.nCreditDebit = nAmount; credit.nTime = nNow; credit.strOtherAccount = strFrom; credit.strComment = strComment; - AddAccountingEntry(credit, &walletdb); + AddAccountingEntry(credit, &batch); - if (!walletdb.TxnCommit()) + if (!batch.TxnCommit()) return false; return true; @@ -811,10 +832,10 @@ bool CWallet::AccountMove(std::string strFrom, std::string strTo, CAmount nAmoun bool CWallet::GetLabelDestination(CTxDestination &dest, const std::string& label, bool bForceNew) { - CWalletDB walletdb(*dbw); + WalletBatch batch(*database); CAccount account; - walletdb.ReadAccount(label, account); + batch.ReadAccount(label, account); if (!bForceNew) { if (!account.vchPubKey.IsValid()) @@ -841,7 +862,7 @@ bool CWallet::GetLabelDestination(CTxDestination &dest, const std::string& label LearnRelatedScripts(account.vchPubKey, m_default_address_type); dest = GetDestinationForKey(account.vchPubKey, m_default_address_type); SetAddressBook(dest, label, "receive"); - walletdb.WriteAccount(label, account); + batch.WriteAccount(label, account); } else { dest = GetDestinationForKey(account.vchPubKey, m_default_address_type); } @@ -874,11 +895,11 @@ bool CWallet::MarkReplaced(const uint256& originalHash, const uint256& newHash) wtx.mapValue["replaced_by_txid"] = newHash.ToString(); - CWalletDB walletdb(*dbw, "r+"); + WalletBatch batch(*database, "r+"); bool success = true; - if (!walletdb.WriteTx(wtx)) { - LogPrintf("%s: Updating walletdb tx %s failed\n", __func__, wtx.GetHash().ToString()); + if (!batch.WriteTx(wtx)) { + LogPrintf("%s: Updating batch tx %s failed\n", __func__, wtx.GetHash().ToString()); success = false; } @@ -891,7 +912,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose) { LOCK(cs_wallet); - CWalletDB walletdb(*dbw, "r+", fFlushOnClose); + WalletBatch batch(*database, "r+", fFlushOnClose); uint256 hash = wtxIn.GetHash(); @@ -903,7 +924,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose) if (fInsertedNew) { wtx.nTimeReceived = GetAdjustedTime(); - wtx.nOrderPos = IncOrderPosNext(&walletdb); + wtx.nOrderPos = IncOrderPosNext(&batch); wtxOrdered.insert(std::make_pair(wtx.nOrderPos, TxPair(&wtx, nullptr))); wtx.nTimeSmart = ComputeTimeSmart(wtx); AddToSpends(hash); @@ -950,7 +971,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose) // Write to disk if (fInsertedNew || fUpdated) - if (!walletdb.WriteTx(wtx)) + if (!batch.WriteTx(wtx)) return false; // Break debit/credit balance caches: @@ -1075,7 +1096,7 @@ bool CWallet::AbandonTransaction(const uint256& hashTx) { LOCK2(cs_main, cs_wallet); - CWalletDB walletdb(*dbw, "r+"); + WalletBatch batch(*database, "r+"); std::set<uint256> todo; std::set<uint256> done; @@ -1107,7 +1128,7 @@ bool CWallet::AbandonTransaction(const uint256& hashTx) wtx.nIndex = -1; wtx.setAbandoned(); wtx.MarkDirty(); - walletdb.WriteTx(wtx); + 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)); @@ -1149,7 +1170,7 @@ void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx) return; // Do not flush the wallet here for performance reasons - CWalletDB walletdb(*dbw, "r+", false); + WalletBatch batch(*database, "r+", false); std::set<uint256> todo; std::set<uint256> done; @@ -1170,7 +1191,7 @@ void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx) wtx.nIndex = -1; wtx.hashBlock = hashBlock; wtx.MarkDirty(); - walletdb.WriteTx(wtx); + batch.WriteTx(wtx); // Iterate over all its outputs, and mark transactions in the wallet that spend them conflicted too TxSpends::const_iterator iter = mapTxSpends.lower_bound(COutPoint(now, 0)); while (iter != mapTxSpends.end() && iter->first.hash == now) { @@ -1474,7 +1495,7 @@ bool CWallet::SetHDMasterKey(const CPubKey& pubkey) bool CWallet::SetHDChain(const CHDChain& chain, bool memonly) { LOCK(cs_wallet); - if (!memonly && !CWalletDB(*dbw).WriteHDChain(chain)) + if (!memonly && !WalletBatch(*database).WriteHDChain(chain)) throw std::runtime_error(std::string(__func__) + ": writing chain failed"); hdChain = chain; @@ -1538,7 +1559,7 @@ bool CWallet::DummySignInput(CTxIn &tx_in, const CTxOut &txout) const const CScript& scriptPubKey = txout.scriptPubKey; SignatureData sigdata; - if (!ProduceSignature(DummySignatureCreator(this), scriptPubKey, sigdata)) + if (!ProduceSignature(*this, DUMMY_SIGNATURE_CREATOR, scriptPubKey, sigdata)) { return false; } else { @@ -1717,6 +1738,9 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlock CBlockIndex* pindex = pindexStart; CBlockIndex* ret = nullptr; + + if (pindex) LogPrintf("Rescan started from block %d...\n", pindex->nHeight); + { fAbortRescan = false; ShowProgress(_("Rescanning..."), 0); // show rescan progress in GUI as dialog or on splashscreen, if -rescan on startup @@ -1730,7 +1754,7 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlock dProgressTip = GuessVerificationProgress(chainParams.TxData(), tip); } double gvp = dProgressStart; - while (pindex && !fAbortRescan) + while (pindex && !fAbortRescan && !ShutdownRequested()) { if (pindex->nHeight % 100 == 0 && dProgressTip - dProgressStart > 0.0) { ShowProgress(_("Rescanning..."), std::max(1, std::min(99, (int)((gvp - dProgressStart) / (dProgressTip - dProgressStart) * 100)))); @@ -1771,6 +1795,8 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlock } if (pindex && fAbortRescan) { LogPrintf("Rescan aborted at block %d. Progress=%f\n", pindex->nHeight, gvp); + } else if (pindex && ShutdownRequested()) { + LogPrintf("Rescan interrupted by shutdown request at block %d. Progress=%f\n", pindex->nHeight, gvp); } ShowProgress(_("Rescanning..."), 100); // hide progress dialog in GUI } @@ -2016,7 +2042,7 @@ bool CWalletTx::IsTrusted() const return true; if (nDepth < 0) return false; - if (!bSpendZeroConfChange || !IsFromMe(ISMINE_ALL)) // using wtx's cached debit + if (!pwallet->m_spend_zero_conf_change || !IsFromMe(ISMINE_ALL)) // using wtx's cached debit return false; // Don't trust unconfirmed transactions from us unless they are in the mempool. @@ -2232,7 +2258,7 @@ CAmount CWallet::GetLegacyBalance(const isminefilter& filter, int minDepth, cons } if (account) { - balance += CWalletDB(*dbw).GetAccountCreditDebit(*account); + balance += WalletBatch(*database).GetAccountCreditDebit(*account); } return balance; @@ -2452,10 +2478,10 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibil FeeCalculation feeCalc; CCoinControl temp; temp.m_confirm_target = 1008; - CFeeRate long_term_feerate = GetMinimumFeeRate(temp, ::mempool, ::feeEstimator, &feeCalc); + CFeeRate long_term_feerate = GetMinimumFeeRate(*this, temp, ::mempool, ::feeEstimator, &feeCalc); // Calculate cost of change - CAmount cost_of_change = GetDiscardRate(::feeEstimator).GetFee(coin_selection_params.change_spend_size) + coin_selection_params.effective_fee.GetFee(coin_selection_params.change_output_size); + CAmount cost_of_change = GetDiscardRate(*this, ::feeEstimator).GetFee(coin_selection_params.change_spend_size) + coin_selection_params.effective_fee.GetFee(coin_selection_params.change_output_size); // Filter by the min conf specs and add to utxo_pool and calculate effective value for (const COutput &output : vCoins) @@ -2552,11 +2578,11 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm bool res = nTargetValue <= nValueFromPresetInputs || SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(1, 6, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used) || SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(1, 1, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used) || - (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, 2), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) || - (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, std::min((size_t)4, nMaxChainLength/3)), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) || - (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, nMaxChainLength/2), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) || - (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, nMaxChainLength), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) || - (bSpendZeroConfChange && !fRejectLongChains && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, std::numeric_limits<uint64_t>::max()), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + (m_spend_zero_conf_change && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, 2), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) || + (m_spend_zero_conf_change && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, std::min((size_t)4, nMaxChainLength/3)), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) || + (m_spend_zero_conf_change && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, nMaxChainLength/2), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) || + (m_spend_zero_conf_change && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, nMaxChainLength), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) || + (m_spend_zero_conf_change && !fRejectLongChains && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, std::numeric_limits<uint64_t>::max()), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); // because SelectCoinsMinConf clears the setCoinsRet, we now add the possible inputs to the coinset setCoinsRet.insert(setPresetCoins.begin(), setPresetCoins.end()); @@ -2582,7 +2608,7 @@ bool CWallet::SignTransaction(CMutableTransaction &tx) const CScript& scriptPubKey = mi->second.tx->vout[input.prevout.n].scriptPubKey; const CAmount& amount = mi->second.tx->vout[input.prevout.n].nValue; SignatureData sigdata; - if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, amount, SIGHASH_ALL), scriptPubKey, sigdata)) { + if (!ProduceSignature(*this, TransactionSignatureCreator(&txNewConst, nIn, amount, SIGHASH_ALL), scriptPubKey, sigdata)) { return false; } UpdateTransaction(tx, nIn, sigdata); @@ -2775,10 +2801,10 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransac CTxOut change_prototype_txout(0, scriptChange); coin_selection_params.change_output_size = GetSerializeSize(change_prototype_txout, SER_DISK, 0); - CFeeRate discard_rate = GetDiscardRate(::feeEstimator); + CFeeRate discard_rate = GetDiscardRate(*this, ::feeEstimator); // Get the fee rate to use effective values in coin selection - CFeeRate nFeeRateNeeded = GetMinimumFeeRate(coin_control, ::mempool, ::feeEstimator, &feeCalc); + CFeeRate nFeeRateNeeded = GetMinimumFeeRate(*this, coin_control, ::mempool, ::feeEstimator, &feeCalc); nFeeRet = 0; bool pick_new_inputs = true; @@ -2902,8 +2928,8 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransac return false; } - nFeeNeeded = GetMinimumFee(nBytes, coin_control, ::mempool, ::feeEstimator, &feeCalc); - if (feeCalc.reason == FeeReason::FALLBACK && !g_wallet_allow_fallback_fee) { + nFeeNeeded = GetMinimumFee(*this, nBytes, coin_control, ::mempool, ::feeEstimator, &feeCalc); + if (feeCalc.reason == FeeReason::FALLBACK && !m_allow_fallback_fee) { // eventually allow a fallback fee strFailReason = _("Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee."); return false; @@ -2930,7 +2956,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransac // change output. Only try this once. if (nChangePosInOut == -1 && nSubtractFeeFromAmount == 0 && pick_new_inputs) { unsigned int tx_size_with_change = nBytes + coin_selection_params.change_output_size + 2; // Add 2 as a buffer in case increasing # of outputs changes compact size - CAmount fee_needed_with_change = GetMinimumFee(tx_size_with_change, coin_control, ::mempool, ::feeEstimator, nullptr); + CAmount fee_needed_with_change = GetMinimumFee(*this, tx_size_with_change, coin_control, ::mempool, ::feeEstimator, nullptr); CAmount minimum_value_for_change = GetDustThreshold(change_prototype_txout, discard_rate); if (nFeeRet >= fee_needed_with_change + minimum_value_for_change) { pick_new_inputs = false; @@ -2997,7 +3023,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransac // to avoid conflicting with other possible uses of nSequence, // and in the spirit of "smallest possible change from prior // behavior." - const uint32_t nSequence = coin_control.signalRbf ? MAX_BIP125_RBF_SEQUENCE : (CTxIn::SEQUENCE_FINAL - 1); + const uint32_t nSequence = coin_control.m_signal_bip125_rbf.get_value_or(m_signal_rbf) ? MAX_BIP125_RBF_SEQUENCE : (CTxIn::SEQUENCE_FINAL - 1); for (const auto& coin : selected_coins) { txNew.vin.push_back(CTxIn(coin.outpoint, CScript(), nSequence)); } @@ -3011,7 +3037,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransac const CScript& scriptPubKey = coin.txout.scriptPubKey; SignatureData sigdata; - if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.txout.nValue, SIGHASH_ALL), scriptPubKey, sigdata)) + if (!ProduceSignature(*this, TransactionSignatureCreator(&txNewConst, nIn, coin.txout.nValue, SIGHASH_ALL), scriptPubKey, sigdata)) { strFailReason = _("Signing transaction failed"); return false; @@ -3076,7 +3102,7 @@ bool CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve wtxNew.fTimeReceivedIsTxTime = true; wtxNew.fFromMe = true; - LogPrintf("CommitTransaction:\n%s", wtxNew.tx->ToString()); + LogPrintf("CommitTransaction:\n%s", wtxNew.tx->ToString()); /* Continued */ { // Take key pair from key pool so it won't be used again reservekey.KeepKey(); @@ -3116,20 +3142,20 @@ bool CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve } void CWallet::ListAccountCreditDebit(const std::string& strAccount, std::list<CAccountingEntry>& entries) { - CWalletDB walletdb(*dbw); - return walletdb.ListAccountCreditDebit(strAccount, entries); + WalletBatch batch(*database); + return batch.ListAccountCreditDebit(strAccount, entries); } bool CWallet::AddAccountingEntry(const CAccountingEntry& acentry) { - CWalletDB walletdb(*dbw); + WalletBatch batch(*database); - return AddAccountingEntry(acentry, &walletdb); + return AddAccountingEntry(acentry, &batch); } -bool CWallet::AddAccountingEntry(const CAccountingEntry& acentry, CWalletDB *pwalletdb) +bool CWallet::AddAccountingEntry(const CAccountingEntry& acentry, WalletBatch *batch) { - if (!pwalletdb->WriteAccountingEntry(++nAccountingEntryNumber, acentry)) { + if (!batch->WriteAccountingEntry(++nAccountingEntryNumber, acentry)) { return false; } @@ -3145,10 +3171,10 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet) LOCK2(cs_main, cs_wallet); fFirstRunRet = false; - DBErrors nLoadWalletRet = CWalletDB(*dbw,"cr+").LoadWallet(this); + DBErrors nLoadWalletRet = WalletBatch(*database,"cr+").LoadWallet(this); if (nLoadWalletRet == DBErrors::NEED_REWRITE) { - if (dbw->Rewrite("\x04pool")) + if (database->Rewrite("\x04pool")) { setInternalKeyPool.clear(); setExternalKeyPool.clear(); @@ -3173,13 +3199,13 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet) DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut) { AssertLockHeld(cs_wallet); // mapWallet - DBErrors nZapSelectTxRet = CWalletDB(*dbw,"cr+").ZapSelectTx(vHashIn, vHashOut); + DBErrors nZapSelectTxRet = WalletBatch(*database,"cr+").ZapSelectTx(vHashIn, vHashOut); for (uint256 hash : vHashOut) mapWallet.erase(hash); if (nZapSelectTxRet == DBErrors::NEED_REWRITE) { - if (dbw->Rewrite("\x04pool")) + if (database->Rewrite("\x04pool")) { setInternalKeyPool.clear(); setExternalKeyPool.clear(); @@ -3201,10 +3227,10 @@ DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256 DBErrors CWallet::ZapWalletTx(std::vector<CWalletTx>& vWtx) { - DBErrors nZapWalletTxRet = CWalletDB(*dbw,"cr+").ZapWalletTx(vWtx); + DBErrors nZapWalletTxRet = WalletBatch(*database,"cr+").ZapWalletTx(vWtx); if (nZapWalletTxRet == DBErrors::NEED_REWRITE) { - if (dbw->Rewrite("\x04pool")) + if (database->Rewrite("\x04pool")) { LOCK(cs_wallet); setInternalKeyPool.clear(); @@ -3236,9 +3262,9 @@ bool CWallet::SetAddressBook(const CTxDestination& address, const std::string& s } NotifyAddressBookChanged(this, address, strName, ::IsMine(*this, address) != ISMINE_NO, strPurpose, (fUpdated ? CT_UPDATED : CT_NEW) ); - if (!strPurpose.empty() && !CWalletDB(*dbw).WritePurpose(EncodeDestination(address), strPurpose)) + if (!strPurpose.empty() && !WalletBatch(*database).WritePurpose(EncodeDestination(address), strPurpose)) return false; - return CWalletDB(*dbw).WriteName(EncodeDestination(address), strName); + return WalletBatch(*database).WriteName(EncodeDestination(address), strName); } bool CWallet::DelAddressBook(const CTxDestination& address) @@ -3250,15 +3276,15 @@ bool CWallet::DelAddressBook(const CTxDestination& address) std::string strAddress = EncodeDestination(address); for (const std::pair<std::string, std::string> &item : mapAddressBook[address].destdata) { - CWalletDB(*dbw).EraseDestData(strAddress, item.first); + WalletBatch(*database).EraseDestData(strAddress, item.first); } mapAddressBook.erase(address); } NotifyAddressBookChanged(this, address, "", ::IsMine(*this, address) != ISMINE_NO, "", CT_DELETED); - CWalletDB(*dbw).ErasePurpose(EncodeDestination(address)); - return CWalletDB(*dbw).EraseName(EncodeDestination(address)); + WalletBatch(*database).ErasePurpose(EncodeDestination(address)); + return WalletBatch(*database).EraseName(EncodeDestination(address)); } const std::string& CWallet::GetLabelName(const CScript& scriptPubKey) const @@ -3284,15 +3310,15 @@ bool CWallet::NewKeyPool() { { LOCK(cs_wallet); - CWalletDB walletdb(*dbw); + WalletBatch batch(*database); for (int64_t nIndex : setInternalKeyPool) { - walletdb.ErasePool(nIndex); + batch.ErasePool(nIndex); } setInternalKeyPool.clear(); for (int64_t nIndex : setExternalKeyPool) { - walletdb.ErasePool(nIndex); + batch.ErasePool(nIndex); } setExternalKeyPool.clear(); @@ -3357,7 +3383,7 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize) missingInternal = 0; } bool internal = false; - CWalletDB walletdb(*dbw); + WalletBatch batch(*database); for (int64_t i = missingInternal + missingExternal; i--;) { if (i < missingInternal) { @@ -3367,8 +3393,8 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize) assert(m_max_keypool_index < std::numeric_limits<int64_t>::max()); // How in the hell did you use so many keys? int64_t index = ++m_max_keypool_index; - CPubKey pubkey(GenerateNewKey(walletdb, internal)); - if (!walletdb.WritePool(index, CKeyPool(pubkey, internal))) { + CPubKey pubkey(GenerateNewKey(batch, internal)); + if (!batch.WritePool(index, CKeyPool(pubkey, internal))) { throw std::runtime_error(std::string(__func__) + ": writing generated key failed"); } @@ -3403,12 +3429,12 @@ void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRe if(setKeyPool.empty()) return; - CWalletDB walletdb(*dbw); + WalletBatch batch(*database); auto it = setKeyPool.begin(); nIndex = *it; setKeyPool.erase(it); - if (!walletdb.ReadPool(nIndex, keypool)) { + if (!batch.ReadPool(nIndex, keypool)) { throw std::runtime_error(std::string(__func__) + ": read failed"); } if (!HaveKey(keypool.vchPubKey.GetID())) { @@ -3427,8 +3453,8 @@ void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRe void CWallet::KeepKey(int64_t nIndex) { // Remove from key pool - CWalletDB walletdb(*dbw); - walletdb.ErasePool(nIndex); + WalletBatch batch(*database); + batch.ErasePool(nIndex); LogPrintf("keypool keep %d\n", nIndex); } @@ -3457,8 +3483,8 @@ bool CWallet::GetKeyFromPool(CPubKey& result, bool internal) if (nIndex == -1) { if (IsLocked()) return false; - CWalletDB walletdb(*dbw); - result = GenerateNewKey(walletdb, internal); + WalletBatch batch(*database); + result = GenerateNewKey(batch, internal); return true; } KeepKey(nIndex); @@ -3467,14 +3493,14 @@ bool CWallet::GetKeyFromPool(CPubKey& result, bool internal) return true; } -static int64_t GetOldestKeyTimeInPool(const std::set<int64_t>& setKeyPool, CWalletDB& walletdb) { +static int64_t GetOldestKeyTimeInPool(const std::set<int64_t>& setKeyPool, WalletBatch& batch) { if (setKeyPool.empty()) { return GetTime(); } CKeyPool keypool; int64_t nIndex = *(setKeyPool.begin()); - if (!walletdb.ReadPool(nIndex, keypool)) { + if (!batch.ReadPool(nIndex, keypool)) { throw std::runtime_error(std::string(__func__) + ": read oldest key in keypool failed"); } assert(keypool.vchPubKey.IsValid()); @@ -3485,12 +3511,12 @@ int64_t CWallet::GetOldestKeyPoolTime() { LOCK(cs_wallet); - CWalletDB walletdb(*dbw); + WalletBatch batch(*database); // load oldest key from keypool, get time and return - int64_t oldestKey = GetOldestKeyTimeInPool(setExternalKeyPool, walletdb); + int64_t oldestKey = GetOldestKeyTimeInPool(setExternalKeyPool, batch); if (IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT)) { - oldestKey = std::max(GetOldestKeyTimeInPool(setInternalKeyPool, walletdb), oldestKey); + oldestKey = std::max(GetOldestKeyTimeInPool(setInternalKeyPool, batch), oldestKey); } return oldestKey; @@ -3643,6 +3669,12 @@ std::set<CTxDestination> CWallet::GetLabelAddresses(const std::string& label) co return result; } +void CWallet::DeleteLabel(const std::string& label) +{ + WalletBatch batch(*database); + batch.EraseAccount(label); +} + bool CReserveKey::GetReservedKey(CPubKey& pubkey, bool internal) { if (nIndex == -1) @@ -3686,17 +3718,17 @@ void CWallet::MarkReserveKeysAsUsed(int64_t keypool_id) std::set<int64_t> *setKeyPool = internal ? &setInternalKeyPool : &setExternalKeyPool; auto it = setKeyPool->begin(); - CWalletDB walletdb(*dbw); + WalletBatch batch(*database); while (it != std::end(*setKeyPool)) { const int64_t& index = *(it); if (index > keypool_id) break; // set*KeyPool is ordered CKeyPool keypool; - if (walletdb.ReadPool(index, keypool)) { //TODO: This should be unnecessary + if (batch.ReadPool(index, keypool)) { //TODO: This should be unnecessary m_pool_key_to_index.erase(keypool.vchPubKey.GetID()); } LearnAllRelatedScripts(keypool.vchPubKey); - walletdb.ErasePool(index); + batch.ErasePool(index); LogPrintf("keypool index %d removed\n", index); it = setKeyPool->erase(it); } @@ -3873,14 +3905,14 @@ bool CWallet::AddDestData(const CTxDestination &dest, const std::string &key, co return false; mapAddressBook[dest].destdata.insert(std::make_pair(key, value)); - return CWalletDB(*dbw).WriteDestData(EncodeDestination(dest), key, value); + return WalletBatch(*database).WriteDestData(EncodeDestination(dest), key, value); } bool CWallet::EraseDestData(const CTxDestination &dest, const std::string &key) { if (!mapAddressBook[dest].destdata.erase(key)) return false; - return CWalletDB(*dbw).EraseDestData(EncodeDestination(dest), key); + return WalletBatch(*database).EraseDestData(EncodeDestination(dest), key); } bool CWallet::LoadDestData(const CTxDestination &dest, const std::string &key, const std::string &value) @@ -3929,7 +3961,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path& if (gArgs.GetBoolArg("-zapwallettxes", false)) { uiInterface.InitMessage(_("Zapping all transactions from wallet...")); - std::unique_ptr<CWallet> tempWallet = MakeUnique<CWallet>(name, CWalletDBWrapper::Create(path)); + std::unique_ptr<CWallet> tempWallet = MakeUnique<CWallet>(name, WalletDatabase::Create(path)); DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx); if (nZapWalletRet != DBErrors::LOAD_OK) { InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile)); @@ -3941,7 +3973,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path& int64_t nStart = GetTimeMillis(); bool fFirstRun = true; - CWallet *walletInstance = new CWallet(name, CWalletDBWrapper::Create(path)); + CWallet *walletInstance = new CWallet(name, WalletDatabase::Create(path)); DBErrors nLoadWalletRet = walletInstance->LoadWallet(fFirstRun); if (nLoadWalletRet != DBErrors::LOAD_OK) { @@ -4009,7 +4041,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path& return nullptr; } - walletInstance->SetBestChain(chainActive.GetLocator()); + walletInstance->ChainStateFlushed(chainActive.GetLocator()); } else if (gArgs.IsArgSet("-usehd")) { bool useHD = gArgs.GetBoolArg("-usehd", true); if (walletInstance->IsHDEnabled() && !useHD) { @@ -4032,6 +4064,66 @@ CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path& return nullptr; } + if (gArgs.IsArgSet("-mintxfee")) { + CAmount n = 0; + if (!ParseMoney(gArgs.GetArg("-mintxfee", ""), n) || 0 == n) { + InitError(AmountErrMsg("mintxfee", gArgs.GetArg("-mintxfee", ""))); + return nullptr; + } + if (n > HIGH_TX_FEE_PER_KB) { + InitWarning(AmountHighWarn("-mintxfee") + " " + + _("This is the minimum transaction fee you pay on every transaction.")); + } + walletInstance->m_min_fee = CFeeRate(n); + } + + walletInstance->m_allow_fallback_fee = Params().IsFallbackFeeEnabled(); + if (gArgs.IsArgSet("-fallbackfee")) { + CAmount nFeePerK = 0; + if (!ParseMoney(gArgs.GetArg("-fallbackfee", ""), nFeePerK)) { + InitError(strprintf(_("Invalid amount for -fallbackfee=<amount>: '%s'"), gArgs.GetArg("-fallbackfee", ""))); + return nullptr; + } + if (nFeePerK > HIGH_TX_FEE_PER_KB) { + InitWarning(AmountHighWarn("-fallbackfee") + " " + + _("This is the transaction fee you may pay when fee estimates are not available.")); + } + walletInstance->m_fallback_fee = CFeeRate(nFeePerK); + walletInstance->m_allow_fallback_fee = nFeePerK != 0; //disable fallback fee in case value was set to 0, enable if non-null value + } + if (gArgs.IsArgSet("-discardfee")) { + CAmount nFeePerK = 0; + if (!ParseMoney(gArgs.GetArg("-discardfee", ""), nFeePerK)) { + InitError(strprintf(_("Invalid amount for -discardfee=<amount>: '%s'"), gArgs.GetArg("-discardfee", ""))); + return nullptr; + } + if (nFeePerK > HIGH_TX_FEE_PER_KB) { + InitWarning(AmountHighWarn("-discardfee") + " " + + _("This is the transaction fee you may discard if change is smaller than dust at this level")); + } + walletInstance->m_discard_rate = CFeeRate(nFeePerK); + } + if (gArgs.IsArgSet("-paytxfee")) { + CAmount nFeePerK = 0; + if (!ParseMoney(gArgs.GetArg("-paytxfee", ""), nFeePerK)) { + InitError(AmountErrMsg("paytxfee", gArgs.GetArg("-paytxfee", ""))); + return nullptr; + } + if (nFeePerK > HIGH_TX_FEE_PER_KB) { + InitWarning(AmountHighWarn("-paytxfee") + " " + + _("This is the transaction fee you will pay if you send a transaction.")); + } + walletInstance->m_pay_tx_fee = CFeeRate(nFeePerK, 1000); + if (walletInstance->m_pay_tx_fee < ::minRelayTxFee) { + InitError(strprintf(_("Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s)"), + gArgs.GetArg("-paytxfee", ""), ::minRelayTxFee.ToString())); + return nullptr; + } + } + walletInstance->m_confirm_target = gArgs.GetArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET); + walletInstance->m_spend_zero_conf_change = gArgs.GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE); + walletInstance->m_signal_rbf = gArgs.GetBoolArg("-walletrbf", DEFAULT_WALLET_RBF); + LogPrintf(" wallet %15dms\n", GetTimeMillis() - nStart); // Try to top up keypool. No-op if the wallet is locked. @@ -4042,9 +4134,9 @@ CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path& CBlockIndex *pindexRescan = chainActive.Genesis(); if (!gArgs.GetBoolArg("-rescan", false)) { - CWalletDB walletdb(*walletInstance->dbw); + WalletBatch batch(*walletInstance->database); CBlockLocator locator; - if (walletdb.ReadBestBlock(locator)) + if (batch.ReadBestBlock(locator)) pindexRescan = FindForkInGlobalIndex(chainActive, locator); } @@ -4087,13 +4179,13 @@ CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path& walletInstance->ScanForWalletTransactions(pindexRescan, nullptr, reserver, true); } LogPrintf(" rescan %15dms\n", GetTimeMillis() - nStart); - walletInstance->SetBestChain(chainActive.GetLocator()); - walletInstance->dbw->IncrementUpdateCounter(); + walletInstance->ChainStateFlushed(chainActive.GetLocator()); + walletInstance->database->IncrementUpdateCounter(); // Restore wallet transaction metadata after -zapwallettxes=1 if (gArgs.GetBoolArg("-zapwallettxes", false) && gArgs.GetArg("-zapwallettxes", "1") != "2") { - CWalletDB walletdb(*walletInstance->dbw); + WalletBatch batch(*walletInstance->database); for (const CWalletTx& wtxOld : vWtx) { @@ -4110,7 +4202,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path& copyTo->fFromMe = copyFrom->fFromMe; copyTo->strFromAccount = copyFrom->strFromAccount; copyTo->nOrderPos = copyFrom->nOrderPos; - walletdb.WriteTx(*copyTo); + batch.WriteTx(*copyTo); } } } @@ -4143,7 +4235,7 @@ void CWallet::postInitProcess(CScheduler& scheduler) bool CWallet::BackupWallet(const std::string& strDest) { - return dbw->Backup(strDest); + return database->Backup(strDest); } CKeyPool::CKeyPool() diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 1d85fbcea1..3208a20f0f 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -32,25 +32,19 @@ #include <utility> #include <vector> -typedef CWallet* CWalletRef; -extern std::vector<CWalletRef> vpwallets; - -/** - * Settings - */ -extern CFeeRate payTxFee; -extern unsigned int nTxConfirmTarget; -extern bool bSpendZeroConfChange; -extern bool fWalletRbf; -extern bool g_wallet_allow_fallback_fee; +bool AddWallet(CWallet* wallet); +bool RemoveWallet(CWallet* wallet); +bool HasWallets(); +std::vector<CWallet*> GetWallets(); +CWallet* GetWallet(const std::string& name); //! Default for -keypool static const unsigned int DEFAULT_KEYPOOL_SIZE = 1000; //! -paytxfee default -static const CAmount DEFAULT_TRANSACTION_FEE = 0; +constexpr CAmount DEFAULT_PAY_TX_FEE = 0; //! -fallbackfee default static const CAmount DEFAULT_FALLBACK_FEE = 20000; -//! -m_discard_rate default +//! -discardfee default static const CAmount DEFAULT_DISCARD_FEE = 10000; //! -mintxfee default static const CAmount DEFAULT_TRANSACTION_MINFEE = 1000; @@ -277,7 +271,7 @@ public: //Get the marginal bytes of spending the specified output int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* pwallet); -/** +/** * A transaction with a bunch of additional info that only the owner cares about. * It includes any unrecorded transactions needed to link it back to the block chain. */ @@ -405,7 +399,7 @@ public: mapValueCopy["timesmart"] = strprintf("%u", nTimeSmart); } - s << *static_cast<const CMerkleTx*>(this); + s << static_cast<const CMerkleTx&>(*this); std::vector<CMerkleTx> vUnused; //!< Used to be vtxPrev s << vUnused << mapValueCopy << vOrderForm << fTimeReceivedIsTxTime << nTimeReceived << fFromMe << fSpent; } @@ -416,7 +410,7 @@ public: Init(nullptr); char fSpent; - s >> *static_cast<CMerkleTx*>(this); + s >> static_cast<CMerkleTx&>(*this); std::vector<CMerkleTx> vUnused; //!< Used to be vtxPrev s >> vUnused >> mapValue >> vOrderForm >> fTimeReceivedIsTxTime >> nTimeReceived >> fFromMe >> fSpent; @@ -558,7 +552,7 @@ public: }; /** - * Internal transfers. + * DEPRECATED Internal transfers. * Database key is acentry<account><counter>. */ class CAccountingEntry @@ -662,7 +656,7 @@ struct CoinEligibilityFilter }; class WalletRescanReserver; //forward declarations for ScanForWalletTransactions/RescanFromTime -/** +/** * A CWallet is an extension of a keystore, which also maintains a set of transactions and balances, * and provides the ability to create new transactions. */ @@ -670,22 +664,22 @@ class CWallet final : public CCryptoKeyStore, public CValidationInterface { private: static std::atomic<bool> fFlushScheduled; - std::atomic<bool> fAbortRescan; - std::atomic<bool> fScanningWallet; //controlled by WalletRescanReserver + std::atomic<bool> fAbortRescan{false}; + std::atomic<bool> fScanningWallet{false}; // controlled by WalletRescanReserver std::mutex mutexScanning; friend class WalletRescanReserver; - CWalletDB *pwalletdbEncryption; + WalletBatch *encrypted_batch = nullptr; //! the current wallet version: clients below this version are not able to load the wallet - int nWalletVersion; + int nWalletVersion = FEATURE_BASE; //! the maximum wallet format version: memory-only variable that specifies to what version this wallet may be upgraded - int nWalletMaxVersion; + int nWalletMaxVersion = FEATURE_BASE; - int64_t nNextResend; - int64_t nLastResend; - bool fBroadcastTransactions; + int64_t nNextResend = 0; + int64_t nLastResend = 0; + bool fBroadcastTransactions = false; /** * Used to keep track of spent outpoints, and @@ -710,14 +704,14 @@ private: CHDChain hdChain; /* HD derive new child key (on internal or external chain) */ - void DeriveNewChildKey(CWalletDB &walletdb, CKeyMetadata& metadata, CKey& secret, bool internal = false); + void DeriveNewChildKey(WalletBatch &batch, CKeyMetadata& metadata, CKey& secret, bool internal = false); std::set<int64_t> setInternalKeyPool; std::set<int64_t> setExternalKeyPool; - int64_t m_max_keypool_index; + int64_t m_max_keypool_index = 0; std::map<CKeyID, int64_t> m_pool_key_to_index; - int64_t nTimeFirstKey; + int64_t nTimeFirstKey = 0; /** * Private version of AddWatchOnly method which does not accept a @@ -738,7 +732,7 @@ private: std::string m_name; /** Internal database handle. */ - std::unique_ptr<CWalletDBWrapper> dbw; + std::unique_ptr<WalletDatabase> database; /** * The following is used to keep track of how far behind the wallet is @@ -750,7 +744,7 @@ private: * * Protected by cs_main (see BlockUntilSyncedToCurrentChain) */ - const CBlockIndex* m_last_block_processed; + const CBlockIndex* m_last_block_processed = nullptr; public: /* @@ -762,9 +756,9 @@ public: /** Get database handle used by this wallet. Ideally this function would * not be necessary. */ - CWalletDBWrapper& GetDBHandle() + WalletDatabase& GetDBHandle() { - return *dbw; + return *database; } /** @@ -789,36 +783,17 @@ public: typedef std::map<unsigned int, CMasterKey> MasterKeyMap; MasterKeyMap mapMasterKeys; - unsigned int nMasterKeyMaxID; + unsigned int nMasterKeyMaxID = 0; /** Construct wallet with specified name and database implementation. */ - CWallet(std::string name, std::unique_ptr<CWalletDBWrapper> dbw) : m_name(std::move(name)), dbw(std::move(dbw)) + CWallet(std::string name, std::unique_ptr<WalletDatabase> database) : m_name(std::move(name)), database(std::move(database)) { - SetNull(); } ~CWallet() { - delete pwalletdbEncryption; - pwalletdbEncryption = nullptr; - } - - void SetNull() - { - nWalletVersion = FEATURE_BASE; - nWalletMaxVersion = FEATURE_BASE; - nMasterKeyMaxID = 0; - pwalletdbEncryption = nullptr; - nOrderPosNext = 0; - nAccountingEntryNumber = 0; - nNextResend = 0; - nLastResend = 0; - m_max_keypool_index = 0; - nTimeFirstKey = 0; - fBroadcastTransactions = false; - nRelockTime = 0; - fAbortRescan = false; - fScanningWallet = false; + delete encrypted_batch; + encrypted_batch = nullptr; } std::map<uint256, CWalletTx> mapWallet; @@ -828,8 +803,8 @@ public: typedef std::multimap<int64_t, TxPair > TxItems; TxItems wtxOrdered; - int64_t nOrderPosNext; - uint64_t nAccountingEntryNumber; + int64_t nOrderPosNext = 0; + uint64_t nAccountingEntryNumber = 0; std::map<uint256, int> mapRequestCount; std::map<CTxDestination, CAddressBookData> mapAddressBook; @@ -884,10 +859,10 @@ public: * keystore implementation * Generate a new key */ - CPubKey GenerateNewKey(CWalletDB& walletdb, bool internal = false); + CPubKey GenerateNewKey(WalletBatch& batch, bool internal = false); //! Adds a key to the store, and saves it to disk. bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override; - bool AddKeyPubKeyWithDB(CWalletDB &walletdb,const CKey& key, const CPubKey &pubkey); + bool AddKeyPubKeyWithDB(WalletBatch &batch,const CKey& key, const CPubKey &pubkey); //! Adds a key to the store, without saving it to disk (used by LoadWallet) bool LoadKey(const CKey& key, const CPubKey &pubkey) { return CCryptoKeyStore::AddKeyPubKey(key, pubkey); } //! Load metadata (used by LoadWallet) @@ -922,7 +897,7 @@ public: bool LoadWatchOnly(const CScript &dest); //! Holds a timestamp at which point the wallet is scheduled (externally) to be relocked. Caller must arrange for actual relocking to occur via Lock(). - int64_t nRelockTime; + int64_t nRelockTime = 0; bool Unlock(const SecureString& strWalletPassphrase); bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase); @@ -931,11 +906,11 @@ public: void GetKeyBirthTimes(std::map<CTxDestination, int64_t> &mapKeyBirth) const; unsigned int ComputeTimeSmart(const CWalletTx& wtx) const; - /** + /** * Increment the next transaction order id * @return next transaction order id */ - int64_t IncOrderPosNext(CWalletDB *pwalletdb = nullptr); + int64_t IncOrderPosNext(WalletBatch *batch = nullptr); DBErrors ReorderTransactions(); bool AccountMove(std::string strFrom, std::string strTo, CAmount nAmount, std::string strComment = ""); bool GetLabelDestination(CTxDestination &dest, const std::string& label, bool bForceNew = false); @@ -983,7 +958,7 @@ public: void ListAccountCreditDebit(const std::string& strAccount, std::list<CAccountingEntry>& entries); bool AddAccountingEntry(const CAccountingEntry&); - bool AddAccountingEntry(const CAccountingEntry&, CWalletDB *pwalletdb); + bool AddAccountingEntry(const CAccountingEntry&, WalletBatch *batch); bool DummySignTx(CMutableTransaction &txNew, const std::set<CTxOut> &txouts) const { std::vector<CTxOut> v_txouts(txouts.size()); @@ -993,9 +968,19 @@ public: bool DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut> &txouts) const; bool DummySignInput(CTxIn &tx_in, const CTxOut &txout) const; - static CFeeRate minTxFee; - static CFeeRate fallbackFee; - static CFeeRate m_discard_rate; + CFeeRate m_pay_tx_fee{DEFAULT_PAY_TX_FEE}; + unsigned int m_confirm_target{DEFAULT_TX_CONFIRM_TARGET}; + bool m_spend_zero_conf_change{DEFAULT_SPEND_ZEROCONF_CHANGE}; + bool m_signal_rbf{DEFAULT_WALLET_RBF}; + bool m_allow_fallback_fee{true}; //<! will be defined via chainparams + CFeeRate m_min_fee{DEFAULT_TRANSACTION_MINFEE}; //!< Override with -mintxfee + /** + * If fee estimation does not have enough data to provide estimates, use this fee instead. + * Has no effect if not using fee estimation + * Override with -fallbackfee + */ + CFeeRate m_fallback_fee{DEFAULT_FALLBACK_FEE}; + CFeeRate m_discard_rate{DEFAULT_DISCARD_FEE}; OutputType m_default_address_type{DEFAULT_ADDRESS_TYPE}; OutputType m_default_change_type{DEFAULT_CHANGE_TYPE}; @@ -1017,6 +1002,7 @@ public: std::map<CTxDestination, CAmount> GetAddressBalances(); std::set<CTxDestination> GetLabelAddresses(const std::string& label) const; + void DeleteLabel(const std::string& label); isminetype IsMine(const CTxIn& txin) const; /** @@ -1036,7 +1022,7 @@ public: bool IsAllFromMe(const CTransaction& tx, const isminefilter& filter) const; CAmount GetCredit(const CTransaction& tx, const isminefilter& filter) const; CAmount GetChange(const CTransaction& tx) const; - void SetBestChain(const CBlockLocator& loc) override; + void ChainStateFlushed(const CBlockLocator& loc) override; DBErrors LoadWallet(bool& fFirstRunRet); DBErrors ZapWalletTx(std::vector<CWalletTx>& vWtx); @@ -1059,7 +1045,7 @@ public: } void GetScriptForMining(std::shared_ptr<CReserveScript> &script); - + unsigned int GetKeyPoolSize() { AssertLockHeld(cs_wallet); // set{Ex,In}ternalKeyPool @@ -1067,7 +1053,7 @@ public: } //! signify that a particular wallet feature is now used. this may change nWalletVersion and nWalletMaxVersion if those are lower - bool SetMinVersion(enum WalletFeature, CWalletDB* pwalletdbIn = nullptr, bool fExplicit = false); + bool SetMinVersion(enum WalletFeature, WalletBatch* batch_in = nullptr, bool fExplicit = false); //! change which version we're allowed to upgrade to (note that this does not immediately imply upgrading to that format) bool SetMaxVersion(int nVersion); @@ -1084,7 +1070,7 @@ public: //! Flush wallet (bitdb flush) void Flush(bool shutdown=false); - /** + /** * Address book entry changed. * @note called with lock cs_wallet held. */ @@ -1093,7 +1079,7 @@ public: const std::string &purpose, ChangeType status)> NotifyAddressBookChanged; - /** + /** * Wallet transaction added, removed or updated. * @note called with lock cs_wallet held. */ @@ -1140,7 +1126,7 @@ public: /* Generates a new HD master key (will not be activated) */ CPubKey GenerateNewHDMasterKey(); - + /* Set the current HD master key (will reset the chain child index counters) Sets the master key's version based on the current wallet version (so the caller must ensure the current wallet version is correct before calling @@ -1211,8 +1197,8 @@ public: }; -/** - * Account information. +/** + * DEPRECATED Account information. * Stored in wallet with key "acc"+string account name. */ class CAccount @@ -1257,10 +1243,10 @@ std::vector<CTxDestination> GetAllDestinationsForKey(const CPubKey& key); class WalletRescanReserver { private: - CWalletRef m_wallet; + CWallet* m_wallet; bool m_could_reserve; public: - explicit WalletRescanReserver(CWalletRef w) : m_wallet(w), m_could_reserve(false) {} + explicit WalletRescanReserver(CWallet* w) : m_wallet(w), m_could_reserve(false) {} bool reserve() { diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 77cdfe7dd0..5b275131af 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -21,42 +21,42 @@ #include <boost/thread.hpp> // -// CWalletDB +// WalletBatch // -bool CWalletDB::WriteName(const std::string& strAddress, const std::string& strName) +bool WalletBatch::WriteName(const std::string& strAddress, const std::string& strName) { return WriteIC(std::make_pair(std::string("name"), strAddress), strName); } -bool CWalletDB::EraseName(const std::string& strAddress) +bool WalletBatch::EraseName(const std::string& strAddress) { // This should only be used for sending addresses, never for receiving addresses, // receiving addresses must always have an address book entry if they're not change return. return EraseIC(std::make_pair(std::string("name"), strAddress)); } -bool CWalletDB::WritePurpose(const std::string& strAddress, const std::string& strPurpose) +bool WalletBatch::WritePurpose(const std::string& strAddress, const std::string& strPurpose) { return WriteIC(std::make_pair(std::string("purpose"), strAddress), strPurpose); } -bool CWalletDB::ErasePurpose(const std::string& strAddress) +bool WalletBatch::ErasePurpose(const std::string& strAddress) { return EraseIC(std::make_pair(std::string("purpose"), strAddress)); } -bool CWalletDB::WriteTx(const CWalletTx& wtx) +bool WalletBatch::WriteTx(const CWalletTx& wtx) { return WriteIC(std::make_pair(std::string("tx"), wtx.GetHash()), wtx); } -bool CWalletDB::EraseTx(uint256 hash) +bool WalletBatch::EraseTx(uint256 hash) { return EraseIC(std::make_pair(std::string("tx"), hash)); } -bool CWalletDB::WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, const CKeyMetadata& keyMeta) +bool WalletBatch::WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, const CKeyMetadata& keyMeta) { if (!WriteIC(std::make_pair(std::string("keymeta"), vchPubKey), keyMeta, false)) { return false; @@ -71,7 +71,7 @@ bool CWalletDB::WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, c return WriteIC(std::make_pair(std::string("key"), vchPubKey), std::make_pair(vchPrivKey, Hash(vchKey.begin(), vchKey.end())), false); } -bool CWalletDB::WriteCryptedKey(const CPubKey& vchPubKey, +bool WalletBatch::WriteCryptedKey(const CPubKey& vchPubKey, const std::vector<unsigned char>& vchCryptedSecret, const CKeyMetadata &keyMeta) { @@ -87,17 +87,17 @@ bool CWalletDB::WriteCryptedKey(const CPubKey& vchPubKey, return true; } -bool CWalletDB::WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey) +bool WalletBatch::WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey) { return WriteIC(std::make_pair(std::string("mkey"), nID), kMasterKey, true); } -bool CWalletDB::WriteCScript(const uint160& hash, const CScript& redeemScript) +bool WalletBatch::WriteCScript(const uint160& hash, const CScript& redeemScript) { return WriteIC(std::make_pair(std::string("cscript"), hash), redeemScript, false); } -bool CWalletDB::WriteWatchOnly(const CScript &dest, const CKeyMetadata& keyMeta) +bool WalletBatch::WriteWatchOnly(const CScript &dest, const CKeyMetadata& keyMeta) { if (!WriteIC(std::make_pair(std::string("watchmeta"), dest), keyMeta)) { return false; @@ -105,7 +105,7 @@ bool CWalletDB::WriteWatchOnly(const CScript &dest, const CKeyMetadata& keyMeta) return WriteIC(std::make_pair(std::string("watchs"), dest), '1'); } -bool CWalletDB::EraseWatchOnly(const CScript &dest) +bool WalletBatch::EraseWatchOnly(const CScript &dest) { if (!EraseIC(std::make_pair(std::string("watchmeta"), dest))) { return false; @@ -113,60 +113,65 @@ bool CWalletDB::EraseWatchOnly(const CScript &dest) return EraseIC(std::make_pair(std::string("watchs"), dest)); } -bool CWalletDB::WriteBestBlock(const CBlockLocator& locator) +bool WalletBatch::WriteBestBlock(const CBlockLocator& locator) { WriteIC(std::string("bestblock"), CBlockLocator()); // Write empty block locator so versions that require a merkle branch automatically rescan return WriteIC(std::string("bestblock_nomerkle"), locator); } -bool CWalletDB::ReadBestBlock(CBlockLocator& locator) +bool WalletBatch::ReadBestBlock(CBlockLocator& locator) { - if (batch.Read(std::string("bestblock"), locator) && !locator.vHave.empty()) return true; - return batch.Read(std::string("bestblock_nomerkle"), locator); + if (m_batch.Read(std::string("bestblock"), locator) && !locator.vHave.empty()) return true; + return m_batch.Read(std::string("bestblock_nomerkle"), locator); } -bool CWalletDB::WriteOrderPosNext(int64_t nOrderPosNext) +bool WalletBatch::WriteOrderPosNext(int64_t nOrderPosNext) { return WriteIC(std::string("orderposnext"), nOrderPosNext); } -bool CWalletDB::ReadPool(int64_t nPool, CKeyPool& keypool) +bool WalletBatch::ReadPool(int64_t nPool, CKeyPool& keypool) { - return batch.Read(std::make_pair(std::string("pool"), nPool), keypool); + return m_batch.Read(std::make_pair(std::string("pool"), nPool), keypool); } -bool CWalletDB::WritePool(int64_t nPool, const CKeyPool& keypool) +bool WalletBatch::WritePool(int64_t nPool, const CKeyPool& keypool) { return WriteIC(std::make_pair(std::string("pool"), nPool), keypool); } -bool CWalletDB::ErasePool(int64_t nPool) +bool WalletBatch::ErasePool(int64_t nPool) { return EraseIC(std::make_pair(std::string("pool"), nPool)); } -bool CWalletDB::WriteMinVersion(int nVersion) +bool WalletBatch::WriteMinVersion(int nVersion) { return WriteIC(std::string("minversion"), nVersion); } -bool CWalletDB::ReadAccount(const std::string& strAccount, CAccount& account) +bool WalletBatch::ReadAccount(const std::string& strAccount, CAccount& account) { account.SetNull(); - return batch.Read(std::make_pair(std::string("acc"), strAccount), account); + return m_batch.Read(std::make_pair(std::string("acc"), strAccount), account); } -bool CWalletDB::WriteAccount(const std::string& strAccount, const CAccount& account) +bool WalletBatch::WriteAccount(const std::string& strAccount, const CAccount& account) { return WriteIC(std::make_pair(std::string("acc"), strAccount), account); } -bool CWalletDB::WriteAccountingEntry(const uint64_t nAccEntryNum, const CAccountingEntry& acentry) +bool WalletBatch::EraseAccount(const std::string& strAccount) +{ + return EraseIC(std::make_pair(std::string("acc"), strAccount)); +} + +bool WalletBatch::WriteAccountingEntry(const uint64_t nAccEntryNum, const CAccountingEntry& acentry) { return WriteIC(std::make_pair(std::string("acentry"), std::make_pair(acentry.strAccount, nAccEntryNum)), acentry); } -CAmount CWalletDB::GetAccountCreditDebit(const std::string& strAccount) +CAmount WalletBatch::GetAccountCreditDebit(const std::string& strAccount) { std::list<CAccountingEntry> entries; ListAccountCreditDebit(strAccount, entries); @@ -178,11 +183,11 @@ CAmount CWalletDB::GetAccountCreditDebit(const std::string& strAccount) return nCreditDebit; } -void CWalletDB::ListAccountCreditDebit(const std::string& strAccount, std::list<CAccountingEntry>& entries) +void WalletBatch::ListAccountCreditDebit(const std::string& strAccount, std::list<CAccountingEntry>& entries) { bool fAllAccounts = (strAccount == "*"); - Dbc* pcursor = batch.GetCursor(); + Dbc* pcursor = m_batch.GetCursor(); if (!pcursor) throw std::runtime_error(std::string(__func__) + ": cannot create DB cursor"); bool setRange = true; @@ -193,7 +198,7 @@ void CWalletDB::ListAccountCreditDebit(const std::string& strAccount, std::list< if (setRange) ssKey << std::make_pair(std::string("acentry"), std::make_pair((fAllAccounts ? std::string("") : strAccount), uint64_t(0))); CDataStream ssValue(SER_DISK, CLIENT_VERSION); - int ret = batch.ReadAtCursor(pcursor, ssKey, ssValue, setRange); + int ret = m_batch.ReadAtCursor(pcursor, ssKey, ssValue, setRange); setRange = false; if (ret == DB_NOTFOUND) break; @@ -227,13 +232,14 @@ public: unsigned int nCKeys; unsigned int nWatchKeys; unsigned int nKeyMeta; + unsigned int m_unknown_records; bool fIsEncrypted; bool fAnyUnordered; int nFileVersion; std::vector<uint256> vWalletUpgrade; CWalletScanState() { - nKeys = nCKeys = nWatchKeys = nKeyMeta = 0; + nKeys = nCKeys = nWatchKeys = nKeyMeta = m_unknown_records = 0; fIsEncrypted = false; fAnyUnordered = false; nFileVersion = 0; @@ -504,6 +510,8 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, strErr = "Error reading wallet database: SetHDChain failed"; return false; } + } else if (strType != "bestblock" && strType != "bestblock_nomerkle"){ + wss.m_unknown_records++; } } catch (...) { @@ -512,13 +520,13 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, return true; } -bool CWalletDB::IsKeyType(const std::string& strType) +bool WalletBatch::IsKeyType(const std::string& strType) { return (strType== "key" || strType == "wkey" || strType == "mkey" || strType == "ckey"); } -DBErrors CWalletDB::LoadWallet(CWallet* pwallet) +DBErrors WalletBatch::LoadWallet(CWallet* pwallet) { CWalletScanState wss; bool fNoncriticalErrors = false; @@ -527,7 +535,7 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet) LOCK(pwallet->cs_wallet); try { int nMinVersion = 0; - if (batch.Read((std::string)"minversion", nMinVersion)) + if (m_batch.Read((std::string)"minversion", nMinVersion)) { if (nMinVersion > CLIENT_VERSION) return DBErrors::TOO_NEW; @@ -535,7 +543,7 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet) } // Get cursor - Dbc* pcursor = batch.GetCursor(); + Dbc* pcursor = m_batch.GetCursor(); if (!pcursor) { LogPrintf("Error getting wallet database cursor\n"); @@ -547,7 +555,7 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet) // Read next record CDataStream ssKey(SER_DISK, CLIENT_VERSION); CDataStream ssValue(SER_DISK, CLIENT_VERSION); - int ret = batch.ReadAtCursor(pcursor, ssKey, ssValue); + int ret = m_batch.ReadAtCursor(pcursor, ssKey, ssValue); if (ret == DB_NOTFOUND) break; else if (ret != 0) @@ -595,8 +603,8 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet) LogPrintf("nFileVersion = %d\n", wss.nFileVersion); - LogPrintf("Keys: %u plaintext, %u encrypted, %u w/ metadata, %u total\n", - wss.nKeys, wss.nCKeys, wss.nKeyMeta, wss.nKeys + wss.nCKeys); + LogPrintf("Keys: %u plaintext, %u encrypted, %u w/ metadata, %u total. Unknown wallet records: %u\n", + wss.nKeys, wss.nCKeys, wss.nKeyMeta, wss.nKeys + wss.nCKeys, wss.m_unknown_records); // nTimeFirstKey is only reliable if all keys have metadata if ((wss.nKeys + wss.nCKeys + wss.nWatchKeys) != wss.nKeyMeta) @@ -624,20 +632,20 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet) return result; } -DBErrors CWalletDB::FindWalletTx(std::vector<uint256>& vTxHash, std::vector<CWalletTx>& vWtx) +DBErrors WalletBatch::FindWalletTx(std::vector<uint256>& vTxHash, std::vector<CWalletTx>& vWtx) { DBErrors result = DBErrors::LOAD_OK; try { int nMinVersion = 0; - if (batch.Read((std::string)"minversion", nMinVersion)) + if (m_batch.Read((std::string)"minversion", nMinVersion)) { if (nMinVersion > CLIENT_VERSION) return DBErrors::TOO_NEW; } // Get cursor - Dbc* pcursor = batch.GetCursor(); + Dbc* pcursor = m_batch.GetCursor(); if (!pcursor) { LogPrintf("Error getting wallet database cursor\n"); @@ -649,7 +657,7 @@ DBErrors CWalletDB::FindWalletTx(std::vector<uint256>& vTxHash, std::vector<CWal // Read next record CDataStream ssKey(SER_DISK, CLIENT_VERSION); CDataStream ssValue(SER_DISK, CLIENT_VERSION); - int ret = batch.ReadAtCursor(pcursor, ssKey, ssValue); + int ret = m_batch.ReadAtCursor(pcursor, ssKey, ssValue); if (ret == DB_NOTFOUND) break; else if (ret != 0) @@ -683,7 +691,7 @@ DBErrors CWalletDB::FindWalletTx(std::vector<uint256>& vTxHash, std::vector<CWal return result; } -DBErrors CWalletDB::ZapSelectTx(std::vector<uint256>& vTxHashIn, std::vector<uint256>& vTxHashOut) +DBErrors WalletBatch::ZapSelectTx(std::vector<uint256>& vTxHashIn, std::vector<uint256>& vTxHashOut) { // build list of wallet TXs and hashes std::vector<uint256> vTxHash; @@ -721,7 +729,7 @@ DBErrors CWalletDB::ZapSelectTx(std::vector<uint256>& vTxHashIn, std::vector<uin return DBErrors::LOAD_OK; } -DBErrors CWalletDB::ZapWalletTx(std::vector<CWalletTx>& vWtx) +DBErrors WalletBatch::ZapWalletTx(std::vector<CWalletTx>& vWtx) { // build list of wallet TXs std::vector<uint256> vTxHash; @@ -748,8 +756,8 @@ void MaybeCompactWalletDB() return; } - for (CWalletRef pwallet : vpwallets) { - CWalletDBWrapper& dbh = pwallet->GetDBHandle(); + for (CWallet* pwallet : GetWallets()) { + WalletDatabase& dbh = pwallet->GetDBHandle(); unsigned int nUpdateCounter = dbh.nUpdateCounter; @@ -759,7 +767,7 @@ void MaybeCompactWalletDB() } if (dbh.nLastFlushed != nUpdateCounter && GetTime() - dbh.nLastWalletUpdate >= 2) { - if (CDB::PeriodicFlush(dbh)) { + if (BerkeleyBatch::PeriodicFlush(dbh)) { dbh.nLastFlushed = nUpdateCounter; } } @@ -771,19 +779,19 @@ void MaybeCompactWalletDB() // // Try to (very carefully!) recover wallet file if there is a problem. // -bool CWalletDB::Recover(const fs::path& wallet_path, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename) +bool WalletBatch::Recover(const fs::path& wallet_path, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename) { - return CDB::Recover(wallet_path, callbackDataIn, recoverKVcallback, out_backup_filename); + return BerkeleyBatch::Recover(wallet_path, callbackDataIn, recoverKVcallback, out_backup_filename); } -bool CWalletDB::Recover(const fs::path& wallet_path, std::string& out_backup_filename) +bool WalletBatch::Recover(const fs::path& wallet_path, std::string& out_backup_filename) { // recover without a key filter callback // results in recovering all record types - return CWalletDB::Recover(wallet_path, nullptr, nullptr, out_backup_filename); + return WalletBatch::Recover(wallet_path, nullptr, nullptr, out_backup_filename); } -bool CWalletDB::RecoverKeysOnlyFilter(void *callbackData, CDataStream ssKey, CDataStream ssValue) +bool WalletBatch::RecoverKeysOnlyFilter(void *callbackData, CDataStream ssKey, CDataStream ssValue) { CWallet *dummyWallet = reinterpret_cast<CWallet*>(callbackData); CWalletScanState dummyWss; @@ -799,60 +807,60 @@ bool CWalletDB::RecoverKeysOnlyFilter(void *callbackData, CDataStream ssKey, CDa return false; if (!fReadOK) { - LogPrintf("WARNING: CWalletDB::Recover skipping %s: %s\n", strType, strErr); + LogPrintf("WARNING: WalletBatch::Recover skipping %s: %s\n", strType, strErr); return false; } return true; } -bool CWalletDB::VerifyEnvironment(const fs::path& wallet_path, std::string& errorStr) +bool WalletBatch::VerifyEnvironment(const fs::path& wallet_path, std::string& errorStr) { - return CDB::VerifyEnvironment(wallet_path, errorStr); + return BerkeleyBatch::VerifyEnvironment(wallet_path, errorStr); } -bool CWalletDB::VerifyDatabaseFile(const fs::path& wallet_path, std::string& warningStr, std::string& errorStr) +bool WalletBatch::VerifyDatabaseFile(const fs::path& wallet_path, std::string& warningStr, std::string& errorStr) { - return CDB::VerifyDatabaseFile(wallet_path, warningStr, errorStr, CWalletDB::Recover); + return BerkeleyBatch::VerifyDatabaseFile(wallet_path, warningStr, errorStr, WalletBatch::Recover); } -bool CWalletDB::WriteDestData(const std::string &address, const std::string &key, const std::string &value) +bool WalletBatch::WriteDestData(const std::string &address, const std::string &key, const std::string &value) { return WriteIC(std::make_pair(std::string("destdata"), std::make_pair(address, key)), value); } -bool CWalletDB::EraseDestData(const std::string &address, const std::string &key) +bool WalletBatch::EraseDestData(const std::string &address, const std::string &key) { return EraseIC(std::make_pair(std::string("destdata"), std::make_pair(address, key))); } -bool CWalletDB::WriteHDChain(const CHDChain& chain) +bool WalletBatch::WriteHDChain(const CHDChain& chain) { return WriteIC(std::string("hdchain"), chain); } -bool CWalletDB::TxnBegin() +bool WalletBatch::TxnBegin() { - return batch.TxnBegin(); + return m_batch.TxnBegin(); } -bool CWalletDB::TxnCommit() +bool WalletBatch::TxnCommit() { - return batch.TxnCommit(); + return m_batch.TxnCommit(); } -bool CWalletDB::TxnAbort() +bool WalletBatch::TxnAbort() { - return batch.TxnAbort(); + return m_batch.TxnAbort(); } -bool CWalletDB::ReadVersion(int& nVersion) +bool WalletBatch::ReadVersion(int& nVersion) { - return batch.ReadVersion(nVersion); + return m_batch.ReadVersion(nVersion); } -bool CWalletDB::WriteVersion(int nVersion) +bool WalletBatch::WriteVersion(int nVersion) { - return batch.WriteVersion(nVersion); + return m_batch.WriteVersion(nVersion); } diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index 606b7dace7..a73d727c0c 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -20,16 +20,13 @@ /** * Overview of wallet database classes: * - * - CDBEnv is an environment in which the database exists (has no analog in dbwrapper.h) - * - CWalletDBWrapper represents a wallet database (similar to CDBWrapper in dbwrapper.h) - * - CDB is a low-level database transaction (similar to CDBBatch in dbwrapper.h) - * - CWalletDB is a modifier object for the wallet, and encapsulates a database - * transaction as well as methods to act on the database (no analog in - * dbwrapper.h) + * - WalletBatch is an abstract modifier object for the wallet database, and encapsulates a database + * batch update as well as methods to act on the database. It should be agnostic to the database implementation. * - * The latter two are named confusingly, in contrast to what the names CDB - * and CWalletDB suggest they are transient transaction objects and don't - * represent the database itself. + * The following classes are implementation specific: + * - BerkeleyEnvironment is an environment in which the database exists. + * - BerkeleyDatabase represents a wallet database. + * - BerkeleyBatch is a low-level database batch update. */ static const bool DEFAULT_FLUSHWALLET = true; @@ -45,6 +42,9 @@ class CWalletTx; class uint160; class uint256; +/** Backend-agnostic database type. */ +using WalletDatabase = BerkeleyDatabase; + /** Error statuses for the wallet database */ enum class DBErrors { @@ -134,41 +134,41 @@ public: }; /** Access to the wallet database. - * This should really be named CWalletDBBatch, as it represents a single transaction at the + * This represents a single transaction at the * database. It will be committed when the object goes out of scope. * Optionally (on by default) it will flush to disk as well. */ -class CWalletDB +class WalletBatch { private: template <typename K, typename T> bool WriteIC(const K& key, const T& value, bool fOverwrite = true) { - if (!batch.Write(key, value, fOverwrite)) { + if (!m_batch.Write(key, value, fOverwrite)) { return false; } - m_dbw.IncrementUpdateCounter(); + m_database.IncrementUpdateCounter(); return true; } template <typename K> bool EraseIC(const K& key) { - if (!batch.Erase(key)) { + if (!m_batch.Erase(key)) { return false; } - m_dbw.IncrementUpdateCounter(); + m_database.IncrementUpdateCounter(); return true; } public: - explicit CWalletDB(CWalletDBWrapper& dbw, const char* pszMode = "r+", bool _fFlushOnClose = true) : - batch(dbw, pszMode, _fFlushOnClose), - m_dbw(dbw) + explicit WalletBatch(WalletDatabase& database, const char* pszMode = "r+", bool _fFlushOnClose = true) : + m_batch(database, pszMode, _fFlushOnClose), + m_database(database) { } - CWalletDB(const CWalletDB&) = delete; - CWalletDB& operator=(const CWalletDB&) = delete; + WalletBatch(const WalletBatch&) = delete; + WalletBatch& operator=(const WalletBatch&) = delete; bool WriteName(const std::string& strAddress, const std::string& strName); bool EraseName(const std::string& strAddress); @@ -204,6 +204,7 @@ public: bool WriteAccountingEntry(const uint64_t nAccEntryNum, const CAccountingEntry& acentry); bool ReadAccount(const std::string& strAccount, CAccount& account); bool WriteAccount(const std::string& strAccount, const CAccount& account); + bool EraseAccount(const std::string& strAccount); /// Write destination data key,value tuple to database bool WriteDestData(const std::string &address, const std::string &key, const std::string &value); @@ -244,8 +245,8 @@ public: //! Write wallet version bool WriteVersion(int nVersion); private: - CDB batch; - CWalletDBWrapper& m_dbw; + BerkeleyBatch m_batch; + WalletDatabase& m_database; }; //! Compacts BDB state so that wallet.dat is self-contained (if there are changes) diff --git a/src/walletinitinterface.h b/src/walletinitinterface.h index a7b52685dd..5bfde6faaf 100644 --- a/src/walletinitinterface.h +++ b/src/walletinitinterface.h @@ -13,39 +13,25 @@ class CRPCTable; class WalletInitInterface { public: /** Get wallet help string */ - virtual std::string GetHelpString(bool showDebug) = 0; + virtual std::string GetHelpString(bool showDebug) const = 0; /** Check wallet parameter interaction */ - virtual bool ParameterInteraction() = 0; + virtual bool ParameterInteraction() const = 0; /** Register wallet RPC*/ - virtual void RegisterRPC(CRPCTable &) = 0; + virtual void RegisterRPC(CRPCTable &) const = 0; /** Verify wallets */ - virtual bool Verify() = 0; + virtual bool Verify() const = 0; /** Open wallets*/ - virtual bool Open() = 0; + virtual bool Open() const = 0; /** Start wallets*/ - virtual void Start(CScheduler& scheduler) = 0; + virtual void Start(CScheduler& scheduler) const = 0; /** Flush Wallets*/ - virtual void Flush() = 0; + virtual void Flush() const = 0; /** Stop Wallets*/ - virtual void Stop() = 0; + virtual void Stop() const = 0; /** Close wallets */ - virtual void Close() = 0; + virtual void Close() const = 0; virtual ~WalletInitInterface() {} }; -class DummyWalletInit : public WalletInitInterface { -public: - - std::string GetHelpString(bool showDebug) override {return std::string{};} - bool ParameterInteraction() override {return true;} - void RegisterRPC(CRPCTable &) override {} - bool Verify() override {return true;} - bool Open() override {return true;} - void Start(CScheduler& scheduler) override {} - void Flush() override {} - void Stop() override {} - void Close() override {} -}; - #endif // BITCOIN_WALLETINITINTERFACE_H diff --git a/src/warnings.cpp b/src/warnings.cpp index 572c766600..dc4e6e4842 100644 --- a/src/warnings.cpp +++ b/src/warnings.cpp @@ -40,7 +40,6 @@ void SetfLargeWorkInvalidChainFound(bool flag) std::string GetWarnings(const std::string& strFor) { std::string strStatusBar; - std::string strRPC; std::string strGUI; const std::string uiAlertSeperator = "<hr />"; @@ -51,9 +50,6 @@ std::string GetWarnings(const std::string& strFor) strGUI = _("This is a pre-release test build - use at your own risk - do not use for mining or merchant applications"); } - if (gArgs.GetBoolArg("-testsafemode", DEFAULT_TESTSAFEMODE)) - strStatusBar = strRPC = strGUI = "testsafemode enabled"; - // Misc warnings like out of disk space and clock is wrong if (strMiscWarning != "") { @@ -63,12 +59,12 @@ std::string GetWarnings(const std::string& strFor) if (fLargeWorkForkFound) { - strStatusBar = strRPC = "Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues."; + strStatusBar = "Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues."; strGUI += (strGUI.empty() ? "" : uiAlertSeperator) + _("Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues."); } else if (fLargeWorkInvalidChainFound) { - strStatusBar = strRPC = "Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade."; + strStatusBar = "Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade."; strGUI += (strGUI.empty() ? "" : uiAlertSeperator) + _("Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade."); } @@ -76,8 +72,6 @@ std::string GetWarnings(const std::string& strFor) return strGUI; else if (strFor == "statusbar") return strStatusBar; - else if (strFor == "rpc") - return strRPC; assert(!"GetWarnings(): invalid parameter"); return "error"; } diff --git a/src/warnings.h b/src/warnings.h index 3d7ac5aab4..904e8c0440 100644 --- a/src/warnings.h +++ b/src/warnings.h @@ -15,13 +15,10 @@ bool GetfLargeWorkForkFound(); void SetfLargeWorkInvalidChainFound(bool flag); /** Format a string that describes several potential problems detected by the core. * strFor can have three values: - * - "rpc": get critical warnings, which should put the client in safe mode if non-empty * - "statusbar": get all warnings * - "gui": get all warnings, translated (where possible) for GUI * This function only returns the highest priority warning of the set selected by strFor. */ std::string GetWarnings(const std::string& strFor); -static const bool DEFAULT_TESTSAFEMODE = false; - #endif // BITCOIN_WARNINGS_H |