aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am7
-rw-r--r--src/Makefile.bench.include4
-rw-r--r--src/bench/coin_selection.cpp11
-rw-r--r--src/bench/prevector.cpp77
-rw-r--r--src/bench/prevector_destructor.cpp36
-rw-r--r--src/chainparams.cpp8
-rw-r--r--src/chainparams.h3
-rw-r--r--src/compat.h10
-rw-r--r--src/init.cpp8
-rw-r--r--src/leveldb/db/db_impl.cc2
-rw-r--r--src/leveldb/db/leveldbutil.cc1
-rw-r--r--src/leveldb/db/log_reader.cc2
-rw-r--r--src/leveldb/db/repair.cc2
-rw-r--r--src/leveldb/helpers/memenv/memenv.cc3
-rw-r--r--src/leveldb/include/leveldb/env.h9
-rw-r--r--src/leveldb/table/format.cc10
-rw-r--r--src/leveldb/util/env_posix.cc8
-rw-r--r--src/leveldb/util/env_win.cc3
-rw-r--r--src/net.cpp7
-rw-r--r--src/net.h1
-rw-r--r--src/net_processing.cpp13
-rw-r--r--src/prevector.h114
-rw-r--r--src/protocol.cpp13
-rw-r--r--src/protocol.h12
-rw-r--r--src/qt/addresstablemodel.cpp12
-rw-r--r--src/qt/bitcoin.cpp2
-rw-r--r--src/qt/clientmodel.cpp2
-rw-r--r--src/qt/guiutil.cpp2
-rw-r--r--src/qt/rpcconsole.cpp9
-rw-r--r--src/qt/sendcoinsdialog.cpp1
-rw-r--r--src/qt/sendcoinsdialog.h3
-rw-r--r--src/qt/test/rpcnestedtests.cpp4
-rw-r--r--src/qt/test/wallettests.cpp2
-rw-r--r--src/qt/transactionview.cpp29
-rw-r--r--src/qt/transactionview.h4
-rw-r--r--src/qt/walletmodel.cpp1
-rw-r--r--src/qt/walletview.cpp6
-rw-r--r--src/rpc/blockchain.cpp38
-rw-r--r--src/rpc/client.cpp3
-rw-r--r--src/rpc/mining.cpp4
-rw-r--r--src/rpc/misc.cpp209
-rw-r--r--src/rpc/net.cpp2
-rw-r--r--src/rpc/rawtransaction.cpp355
-rw-r--r--src/rpc/rawtransaction.h15
-rw-r--r--src/rpc/util.cpp61
-rw-r--r--src/rpc/util.h9
-rw-r--r--src/test/checkqueue_tests.cpp8
-rw-r--r--src/test/data/script_tests.json12
-rw-r--r--src/test/rpc_tests.cpp12
-rw-r--r--src/test/test_bitcoin.cpp3
-rw-r--r--src/test/util_tests.cpp197
-rw-r--r--src/util.cpp35
-rw-r--r--src/util.h6
-rw-r--r--src/validation.cpp5
-rw-r--r--src/wallet/feebumper.cpp3
-rw-r--r--src/wallet/fees.cpp3
-rw-r--r--src/wallet/init.cpp4
-rw-r--r--src/wallet/rpcwallet.cpp391
-rw-r--r--src/wallet/rpcwallet.h3
-rw-r--r--src/wallet/test/wallet_test_fixture.cpp1
-rw-r--r--src/wallet/test/wallet_tests.cpp3
-rw-r--r--src/wallet/wallet.cpp27
-rw-r--r--src/wallet/wallet.h2
63 files changed, 1272 insertions, 580 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 4fbd605d9e..ac822d6c5e 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -132,13 +132,14 @@ BITCOIN_CORE_H = \
rpc/protocol.h \
rpc/safemode.h \
rpc/server.h \
+ rpc/rawtransaction.h \
rpc/register.h \
rpc/util.h \
scheduler.h \
+ script/ismine.h \
script/sigcache.h \
script/sign.h \
script/standard.h \
- script/ismine.h \
streams.h \
support/allocators/secure.h \
support/allocators/zeroafterfree.h \
@@ -216,7 +217,6 @@ libbitcoin_server_a_SOURCES = \
rpc/safemode.cpp \
rpc/server.cpp \
script/sigcache.cpp \
- script/ismine.cpp \
timedata.cpp \
torcontrol.cpp \
txdb.cpp \
@@ -333,6 +333,7 @@ libbitcoin_common_a_SOURCES = \
policy/feerate.cpp \
protocol.cpp \
scheduler.cpp \
+ script/ismine.cpp \
script/sign.cpp \
script/standard.cpp \
warnings.cpp \
@@ -389,10 +390,10 @@ endif
bitcoind_LDADD = \
$(LIBBITCOIN_SERVER) \
+ $(LIBBITCOIN_WALLET) \
$(LIBBITCOIN_COMMON) \
$(LIBUNIVALUE) \
$(LIBBITCOIN_UTIL) \
- $(LIBBITCOIN_WALLET) \
$(LIBBITCOIN_ZMQ) \
$(LIBBITCOIN_CONSENSUS) \
$(LIBBITCOIN_CRYPTO) \
diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include
index 8e2e587d32..748c5b7887 100644
--- a/src/Makefile.bench.include
+++ b/src/Makefile.bench.include
@@ -27,7 +27,7 @@ bench_bench_bitcoin_SOURCES = \
bench/lockedpool.cpp \
bench/perf.cpp \
bench/perf.h \
- bench/prevector_destructor.cpp
+ bench/prevector.cpp
nodist_bench_bench_bitcoin_SOURCES = $(GENERATED_BENCH_FILES)
@@ -35,6 +35,7 @@ bench_bench_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(EVENT_CLFAGS
bench_bench_bitcoin_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
bench_bench_bitcoin_LDADD = \
$(LIBBITCOIN_SERVER) \
+ $(LIBBITCOIN_WALLET) \
$(LIBBITCOIN_COMMON) \
$(LIBBITCOIN_UTIL) \
$(LIBBITCOIN_CONSENSUS) \
@@ -51,7 +52,6 @@ endif
if ENABLE_WALLET
bench_bench_bitcoin_SOURCES += bench/coin_selection.cpp
-bench_bench_bitcoin_LDADD += $(LIBBITCOIN_WALLET) $(LIBBITCOIN_CRYPTO)
endif
bench_bench_bitcoin_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS)
diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp
index 06d2abeac6..6f438b60e9 100644
--- a/src/bench/coin_selection.cpp
+++ b/src/bench/coin_selection.cpp
@@ -37,11 +37,6 @@ static void CoinSelection(benchmark::State& state)
LOCK(wallet.cs_wallet);
while (state.KeepRunning()) {
- // Empty wallet.
- for (COutput output : vCoins)
- delete output.tx;
- vCoins.clear();
-
// Add coins.
for (int i = 0; i < 1000; i++)
addCoin(1000 * COIN, wallet, vCoins);
@@ -53,6 +48,12 @@ static void CoinSelection(benchmark::State& state)
assert(success);
assert(nValueRet == 1003 * COIN);
assert(setCoinsRet.size() == 2);
+
+ // Empty wallet.
+ for (COutput& output : vCoins) {
+ delete output.tx;
+ }
+ vCoins.clear();
}
}
diff --git a/src/bench/prevector.cpp b/src/bench/prevector.cpp
new file mode 100644
index 0000000000..d0f28d1a3e
--- /dev/null
+++ b/src/bench/prevector.cpp
@@ -0,0 +1,77 @@
+// Copyright (c) 2015-2017 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <compat.h>
+#include <prevector.h>
+
+#include <bench/bench.h>
+
+struct nontrivial_t {
+ int x;
+ nontrivial_t() :x(-1) {}
+};
+static_assert(!IS_TRIVIALLY_CONSTRUCTIBLE<nontrivial_t>::value,
+ "expected nontrivial_t to not be trivially constructible");
+
+typedef unsigned char trivial_t;
+static_assert(IS_TRIVIALLY_CONSTRUCTIBLE<trivial_t>::value,
+ "expected trivial_t to be trivially constructible");
+
+template <typename T>
+static void PrevectorDestructor(benchmark::State& state)
+{
+ while (state.KeepRunning()) {
+ for (auto x = 0; x < 1000; ++x) {
+ prevector<28, T> t0;
+ prevector<28, T> t1;
+ t0.resize(28);
+ t1.resize(29);
+ }
+ }
+}
+
+template <typename T>
+static void PrevectorClear(benchmark::State& state)
+{
+
+ while (state.KeepRunning()) {
+ for (auto x = 0; x < 1000; ++x) {
+ prevector<28, T> t0;
+ prevector<28, T> t1;
+ t0.resize(28);
+ t0.clear();
+ t1.resize(29);
+ t0.clear();
+ }
+ }
+}
+
+template <typename T>
+void PrevectorResize(benchmark::State& state)
+{
+ while (state.KeepRunning()) {
+ prevector<28, T> t0;
+ prevector<28, T> t1;
+ for (auto x = 0; x < 1000; ++x) {
+ t0.resize(28);
+ t0.resize(0);
+ t1.resize(29);
+ t1.resize(0);
+ }
+ }
+}
+
+#define PREVECTOR_TEST(name, nontrivops, trivops) \
+ static void Prevector ## name ## Nontrivial(benchmark::State& state) { \
+ PrevectorResize<nontrivial_t>(state); \
+ } \
+ BENCHMARK(Prevector ## name ## Nontrivial, nontrivops); \
+ static void Prevector ## name ## Trivial(benchmark::State& state) { \
+ PrevectorResize<trivial_t>(state); \
+ } \
+ BENCHMARK(Prevector ## name ## Trivial, trivops);
+
+PREVECTOR_TEST(Clear, 28300, 88600)
+PREVECTOR_TEST(Destructor, 28800, 88900)
+PREVECTOR_TEST(Resize, 28900, 90300)
diff --git a/src/bench/prevector_destructor.cpp b/src/bench/prevector_destructor.cpp
deleted file mode 100644
index 39d0ee5eb1..0000000000
--- a/src/bench/prevector_destructor.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (c) 2015-2017 The Bitcoin Core developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-#include <bench/bench.h>
-#include <prevector.h>
-
-static void PrevectorDestructor(benchmark::State& state)
-{
- while (state.KeepRunning()) {
- for (auto x = 0; x < 1000; ++x) {
- prevector<28, unsigned char> t0;
- prevector<28, unsigned char> t1;
- t0.resize(28);
- t1.resize(29);
- }
- }
-}
-
-static void PrevectorClear(benchmark::State& state)
-{
-
- while (state.KeepRunning()) {
- for (auto x = 0; x < 1000; ++x) {
- prevector<28, unsigned char> t0;
- prevector<28, unsigned char> t1;
- t0.resize(28);
- t0.clear();
- t1.resize(29);
- t0.clear();
- }
- }
-}
-
-BENCHMARK(PrevectorDestructor, 5700);
-BENCHMARK(PrevectorClear, 5600);
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index 6eb223171f..c2b3480f9d 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -175,6 +175,9 @@ public:
// (the tx=... number in the SetBestChain debug.log lines)
3.5 // * estimated number of transactions per second after that timestamp
};
+
+ /* disable fallback fee on mainnet */
+ m_fallback_fee_enabled = false;
}
};
@@ -266,6 +269,8 @@ public:
0.09
};
+ /* enable fallback fee on testnet */
+ m_fallback_fee_enabled = true;
}
};
@@ -343,6 +348,9 @@ public:
base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94};
bech32_hrp = "bcrt";
+
+ /* enable fallback fee on regtest */
+ m_fallback_fee_enabled = true;
}
};
diff --git a/src/chainparams.h b/src/chainparams.h
index d478da9891..6b1f813afb 100644
--- a/src/chainparams.h
+++ b/src/chainparams.h
@@ -65,6 +65,8 @@ public:
bool MineBlocksOnDemand() const { return fMineBlocksOnDemand; }
/** Return the BIP70 network string (main, test or regtest) */
std::string NetworkIDString() const { return strNetworkID; }
+ /** Return true if the fallback fee is by default enabled for this network */
+ bool IsFallbackFeeEnabled() const { return m_fallback_fee_enabled; }
/** Return the list of hostnames to look up for DNS seeds */
const std::vector<std::string>& DNSSeeds() const { return vSeeds; }
const std::vector<unsigned char>& Base58Prefix(Base58Type type) const { return base58Prefixes[type]; }
@@ -91,6 +93,7 @@ protected:
bool fMineBlocksOnDemand;
CCheckpointData checkpointData;
ChainTxData chainTxData;
+ bool m_fallback_fee_enabled;
};
/**
diff --git a/src/compat.h b/src/compat.h
index aae84b1181..8a0f901304 100644
--- a/src/compat.h
+++ b/src/compat.h
@@ -10,6 +10,16 @@
#include <config/bitcoin-config.h>
#endif
+#include <type_traits>
+
+// GCC 4.8 is missing some C++11 type_traits,
+// https://www.gnu.org/software/gcc/gcc-5/changes.html
+#if defined(__GNUC__) && __GNUC__ < 5
+#define IS_TRIVIALLY_CONSTRUCTIBLE std::is_trivial
+#else
+#define IS_TRIVIALLY_CONSTRUCTIBLE std::is_trivially_constructible
+#endif
+
#ifdef WIN32
#ifdef _WIN32_WINNT
#undef _WIN32_WINNT
diff --git a/src/init.cpp b/src/init.cpp
index b28baba779..47bcdf2294 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -448,6 +448,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-limitdescendantcount=<n>", strprintf("Do not accept transactions if any ancestor would have <n> or more in-mempool descendants (default: %u)", DEFAULT_DESCENDANT_LIMIT));
strUsage += HelpMessageOpt("-limitdescendantsize=<n>", strprintf("Do not accept transactions if any ancestor would have more than <n> kilobytes of in-mempool descendants (default: %u).", DEFAULT_DESCENDANT_SIZE_LIMIT));
strUsage += HelpMessageOpt("-vbparams=deployment:start:end", "Use given start/end times for specified version bits deployment (regtest-only)");
+ strUsage += HelpMessageOpt("-addrmantest", "Allows to test address relay on localhost");
}
strUsage += HelpMessageOpt("-debug=<category>", strprintf(_("Output debugging information (default: %u, supplying <category> is optional)"), 0) + ". " +
_("If <category> is not supplied or if <category> = 1, output all debugging information.") + " " + _("<category> can be:") + " " + ListLogCategories() + ".");
@@ -490,7 +491,8 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-whitelistrelay", strprintf(_("Accept relayed transactions received from whitelisted peers even when not relaying transactions (default: %d)"), DEFAULT_WHITELISTRELAY));
strUsage += HelpMessageGroup(_("Block creation options:"));
- strUsage += HelpMessageOpt("-blockmaxsize=<n>", _("Set maximum BIP141 block weight to this * 4. Deprecated, use blockmaxweight"));
+ if (showDebug)
+ strUsage += HelpMessageOpt("-blockmaxsize=<n>", "Set maximum BIP141 block weight to this * 4. Deprecated, use blockmaxweight");
strUsage += HelpMessageOpt("-blockmaxweight=<n>", strprintf(_("Set maximum BIP141 block weight (default: %d)"), DEFAULT_BLOCK_MAX_WEIGHT));
strUsage += HelpMessageOpt("-blockmintxfee=<amt>", strprintf(_("Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s)"), CURRENCY_UNIT, FormatMoney(DEFAULT_BLOCK_MIN_TX_FEE)));
if (showDebug)
@@ -680,11 +682,13 @@ void ThreadImport(std::vector<fs::path> vImportFiles)
if (!ActivateBestChain(state, chainparams)) {
LogPrintf("Failed to connect best block");
StartShutdown();
+ return;
}
if (gArgs.GetBoolArg("-stopafterblockimport", DEFAULT_STOPAFTERBLOCKIMPORT)) {
LogPrintf("Stopping after block import\n");
StartShutdown();
+ return;
}
} // End scope of CImportingNow
if (gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
@@ -1524,7 +1528,7 @@ bool AppInitMain()
if (!is_coinsview_empty) {
uiInterface.InitMessage(_("Verifying blocks..."));
if (fHavePruned && gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS) > MIN_BLOCKS_TO_KEEP) {
- LogPrintf("Prune: pruned datadir may not have more than %d blocks; only checking available blocks",
+ LogPrintf("Prune: pruned datadir may not have more than %d blocks; only checking available blocks\n",
MIN_BLOCKS_TO_KEEP);
}
diff --git a/src/leveldb/db/db_impl.cc b/src/leveldb/db/db_impl.cc
index f43ad76794..3bb58e560a 100644
--- a/src/leveldb/db/db_impl.cc
+++ b/src/leveldb/db/db_impl.cc
@@ -414,7 +414,7 @@ Status DBImpl::RecoverLogFile(uint64_t log_number, bool last_log,
status.ok()) {
if (record.size() < 12) {
reporter.Corruption(
- record.size(), Status::Corruption("log record too small"));
+ record.size(), Status::Corruption("log record too small", fname));
continue;
}
WriteBatchInternal::SetContents(&batch, record);
diff --git a/src/leveldb/db/leveldbutil.cc b/src/leveldb/db/leveldbutil.cc
index 9f4b7dd70c..d06d64d640 100644
--- a/src/leveldb/db/leveldbutil.cc
+++ b/src/leveldb/db/leveldbutil.cc
@@ -19,6 +19,7 @@ class StdoutPrinter : public WritableFile {
virtual Status Close() { return Status::OK(); }
virtual Status Flush() { return Status::OK(); }
virtual Status Sync() { return Status::OK(); }
+ virtual std::string GetName() const { return "[stdout]"; }
};
bool HandleDumpCommand(Env* env, char** files, int num) {
diff --git a/src/leveldb/db/log_reader.cc b/src/leveldb/db/log_reader.cc
index a6d304545d..8b6ad136d7 100644
--- a/src/leveldb/db/log_reader.cc
+++ b/src/leveldb/db/log_reader.cc
@@ -186,7 +186,7 @@ uint64_t Reader::LastRecordOffset() {
}
void Reader::ReportCorruption(uint64_t bytes, const char* reason) {
- ReportDrop(bytes, Status::Corruption(reason));
+ ReportDrop(bytes, Status::Corruption(reason, file_->GetName()));
}
void Reader::ReportDrop(uint64_t bytes, const Status& reason) {
diff --git a/src/leveldb/db/repair.cc b/src/leveldb/db/repair.cc
index 4cd4bb047f..7281e3d345 100644
--- a/src/leveldb/db/repair.cc
+++ b/src/leveldb/db/repair.cc
@@ -203,7 +203,7 @@ class Repairer {
while (reader.ReadRecord(&record, &scratch)) {
if (record.size() < 12) {
reporter.Corruption(
- record.size(), Status::Corruption("log record too small"));
+ record.size(), Status::Corruption("log record too small", logname));
continue;
}
WriteBatchInternal::SetContents(&batch, record);
diff --git a/src/leveldb/helpers/memenv/memenv.cc b/src/leveldb/helpers/memenv/memenv.cc
index 9a98884daf..68c0614a59 100644
--- a/src/leveldb/helpers/memenv/memenv.cc
+++ b/src/leveldb/helpers/memenv/memenv.cc
@@ -176,6 +176,7 @@ class SequentialFileImpl : public SequentialFile {
return Status::OK();
}
+ virtual std::string GetName() const { return "[memenv]"; }
private:
FileState* file_;
uint64_t pos_;
@@ -196,6 +197,7 @@ class RandomAccessFileImpl : public RandomAccessFile {
return file_->Read(offset, n, result, scratch);
}
+ virtual std::string GetName() const { return "[memenv]"; }
private:
FileState* file_;
};
@@ -218,6 +220,7 @@ class WritableFileImpl : public WritableFile {
virtual Status Flush() { return Status::OK(); }
virtual Status Sync() { return Status::OK(); }
+ virtual std::string GetName() const { return "[memenv]"; }
private:
FileState* file_;
};
diff --git a/src/leveldb/include/leveldb/env.h b/src/leveldb/include/leveldb/env.h
index 99b6c21414..275d441eae 100644
--- a/src/leveldb/include/leveldb/env.h
+++ b/src/leveldb/include/leveldb/env.h
@@ -191,6 +191,9 @@ class SequentialFile {
// REQUIRES: External synchronization
virtual Status Skip(uint64_t n) = 0;
+ // Get a name for the file, only for error reporting
+ virtual std::string GetName() const = 0;
+
private:
// No copying allowed
SequentialFile(const SequentialFile&);
@@ -215,6 +218,9 @@ class RandomAccessFile {
virtual Status Read(uint64_t offset, size_t n, Slice* result,
char* scratch) const = 0;
+ // Get a name for the file, only for error reporting
+ virtual std::string GetName() const = 0;
+
private:
// No copying allowed
RandomAccessFile(const RandomAccessFile&);
@@ -234,6 +240,9 @@ class WritableFile {
virtual Status Flush() = 0;
virtual Status Sync() = 0;
+ // Get a name for the file, only for error reporting
+ virtual std::string GetName() const = 0;
+
private:
// No copying allowed
WritableFile(const WritableFile&);
diff --git a/src/leveldb/table/format.cc b/src/leveldb/table/format.cc
index 24e4e02445..285e1c0de3 100644
--- a/src/leveldb/table/format.cc
+++ b/src/leveldb/table/format.cc
@@ -82,7 +82,7 @@ Status ReadBlock(RandomAccessFile* file,
}
if (contents.size() != n + kBlockTrailerSize) {
delete[] buf;
- return Status::Corruption("truncated block read");
+ return Status::Corruption("truncated block read", file->GetName());
}
// Check the crc of the type and the block contents
@@ -92,7 +92,7 @@ Status ReadBlock(RandomAccessFile* file,
const uint32_t actual = crc32c::Value(data, n + 1);
if (actual != crc) {
delete[] buf;
- s = Status::Corruption("block checksum mismatch");
+ s = Status::Corruption("block checksum mismatch", file->GetName());
return s;
}
}
@@ -119,13 +119,13 @@ Status ReadBlock(RandomAccessFile* file,
size_t ulength = 0;
if (!port::Snappy_GetUncompressedLength(data, n, &ulength)) {
delete[] buf;
- return Status::Corruption("corrupted compressed block contents");
+ return Status::Corruption("corrupted compressed block contents", file->GetName());
}
char* ubuf = new char[ulength];
if (!port::Snappy_Uncompress(data, n, ubuf)) {
delete[] buf;
delete[] ubuf;
- return Status::Corruption("corrupted compressed block contents");
+ return Status::Corruption("corrupted compressed block contents", file->GetName());
}
delete[] buf;
result->data = Slice(ubuf, ulength);
@@ -135,7 +135,7 @@ Status ReadBlock(RandomAccessFile* file,
}
default:
delete[] buf;
- return Status::Corruption("bad block type");
+ return Status::Corruption("bad block type", file->GetName());
}
return Status::OK();
diff --git a/src/leveldb/util/env_posix.cc b/src/leveldb/util/env_posix.cc
index dd852af354..4676bc2240 100644
--- a/src/leveldb/util/env_posix.cc
+++ b/src/leveldb/util/env_posix.cc
@@ -121,6 +121,8 @@ class PosixSequentialFile: public SequentialFile {
}
return Status::OK();
}
+
+ virtual std::string GetName() const { return filename_; }
};
// pread() based random-access
@@ -172,6 +174,8 @@ class PosixRandomAccessFile: public RandomAccessFile {
}
return s;
}
+
+ virtual std::string GetName() const { return filename_; }
};
// mmap() based random-access
@@ -206,6 +210,8 @@ class PosixMmapReadableFile: public RandomAccessFile {
}
return s;
}
+
+ virtual std::string GetName() const { return filename_; }
};
class PosixWritableFile : public WritableFile {
@@ -287,6 +293,8 @@ class PosixWritableFile : public WritableFile {
}
return s;
}
+
+ virtual std::string GetName() const { return filename_; }
};
static int LockOrUnlock(int fd, bool lock) {
diff --git a/src/leveldb/util/env_win.cc b/src/leveldb/util/env_win.cc
index d32c4e676c..81380216bb 100644
--- a/src/leveldb/util/env_win.cc
+++ b/src/leveldb/util/env_win.cc
@@ -78,6 +78,7 @@ public:
virtual Status Read(size_t n, Slice* result, char* scratch);
virtual Status Skip(uint64_t n);
BOOL isEnable();
+ virtual std::string GetName() const { return _filename; }
private:
BOOL _Init();
void _CleanUp();
@@ -94,6 +95,7 @@ public:
virtual ~Win32RandomAccessFile();
virtual Status Read(uint64_t offset, size_t n, Slice* result,char* scratch) const;
BOOL isEnable();
+ virtual std::string GetName() const { return _filename; }
private:
BOOL _Init(LPCWSTR path);
void _CleanUp();
@@ -114,6 +116,7 @@ public:
virtual Status Flush();
virtual Status Sync();
BOOL isEnable();
+ virtual std::string GetName() const { return filename_; }
private:
std::string filename_;
::HANDLE _hFile;
diff --git a/src/net.cpp b/src/net.cpp
index 201914685c..c7c19f83a1 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -181,6 +181,10 @@ void AdvertiseLocal(CNode *pnode)
if (fListen && pnode->fSuccessfullyConnected)
{
CAddress addrLocal = GetLocalAddress(&pnode->addr, pnode->GetLocalServices());
+ if (gArgs.GetBoolArg("-addrmantest", false)) {
+ // use IPv4 loopback during addrmantest
+ addrLocal = CAddress(CService(LookupNumeric("127.0.0.1", GetListenPort())), pnode->GetLocalServices());
+ }
// If discovery is enabled, sometimes give our peer the address it
// tells us that it sees us as in case it has a better idea of our
// address than we do.
@@ -189,7 +193,7 @@ void AdvertiseLocal(CNode *pnode)
{
addrLocal.SetIP(pnode->GetAddrLocal());
}
- if (addrLocal.IsRoutable())
+ if (addrLocal.IsRoutable() || gArgs.GetBoolArg("-addrmantest", false))
{
LogPrint(BCLog::NET, "AdvertiseLocal: advertising address %s\n", addrLocal.ToString());
FastRandomContext insecure_rand;
@@ -2718,6 +2722,7 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn
fOneShot = false;
m_manual_connection = false;
fClient = false; // set by version message
+ m_limited_node = false; // set by version message
fFeeler = false;
fSuccessfullyConnected = false;
fDisconnect = false;
diff --git a/src/net.h b/src/net.h
index 8378a303b8..96f04d83e0 100644
--- a/src/net.h
+++ b/src/net.h
@@ -641,6 +641,7 @@ public:
bool fOneShot;
bool m_manual_connection;
bool fClient;
+ bool m_limited_node; //after BIP159
const bool fInbound;
std::atomic_bool fSuccessfullyConnected;
std::atomic_bool fDisconnect;
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index bf9307727a..ddf0dbbbea 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -892,6 +892,7 @@ void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CB
const int nNewHeight = pindexNew->nHeight;
connman->SetBestHeight(nNewHeight);
+ SetServiceFlagsIBDCache(!fInitialDownload);
if (!fInitialDownload) {
// Find the hashes of all blocks that weren't previously in the best chain.
std::vector<uint256> vHashes;
@@ -1642,7 +1643,13 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
pfrom->cleanSubVer = cleanSubVer;
}
pfrom->nStartingHeight = nStartingHeight;
- pfrom->fClient = !(nServices & NODE_NETWORK);
+
+ // set nodes not relaying blocks and tx and not serving (parts) of the historical blockchain as "clients"
+ pfrom->fClient = (!(nServices & NODE_NETWORK) && !(nServices & NODE_NETWORK_LIMITED));
+
+ // set nodes not capable of serving the complete blockchain history as "limited nodes"
+ pfrom->m_limited_node = (!(nServices & NODE_NETWORK) && (nServices & NODE_NETWORK_LIMITED));
+
{
LOCK(pfrom->cs_filter);
pfrom->fRelayTxes = fRelay; // set to true after we get the first filter* message
@@ -1801,7 +1808,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// We only bother storing full nodes, though this may include
// things which we would not make an outbound connection to, in
// part because we may make feeler connections to them.
- if (!MayHaveUsefulAddressDB(addr.nServices))
+ if (!MayHaveUsefulAddressDB(addr.nServices) && !HasAllDesirableServiceFlags(addr.nServices))
continue;
if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60)
@@ -3611,7 +3618,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto, std::atomic<bool>& interruptM
// Message: getdata (blocks)
//
std::vector<CInv> vGetData;
- if (!pto->fClient && (fFetch || !IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
+ if (!pto->fClient && ((fFetch && !pto->m_limited_node) || !IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
std::vector<const CBlockIndex*> vToDownload;
NodeId staller = -1;
FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller, consensusParams);
diff --git a/src/prevector.h b/src/prevector.h
index f8d6a09145..103ead82cc 100644
--- a/src/prevector.h
+++ b/src/prevector.h
@@ -10,9 +10,12 @@
#include <stdint.h>
#include <string.h>
+#include <cstddef>
#include <iterator>
#include <type_traits>
+#include <compat.h>
+
#pragma pack(push, 1)
/** Implements a drop-in replacement for std::vector<T> which stores up to N
* elements directly (without heap allocation). The types Size and Diff are
@@ -194,16 +197,42 @@ private:
T* item_ptr(difference_type pos) { return is_direct() ? direct_ptr(pos) : indirect_ptr(pos); }
const T* item_ptr(difference_type pos) const { return is_direct() ? direct_ptr(pos) : indirect_ptr(pos); }
+ void fill(T* dst, ptrdiff_t count) {
+ if (IS_TRIVIALLY_CONSTRUCTIBLE<T>::value) {
+ // The most common use of prevector is where T=unsigned char. For
+ // trivially constructible types, we can use memset() to avoid
+ // looping.
+ ::memset(dst, 0, count * sizeof(T));
+ } else {
+ for (auto i = 0; i < count; ++i) {
+ new(static_cast<void*>(dst + i)) T();
+ }
+ }
+ }
+
+ void fill(T* dst, ptrdiff_t count, const T& value) {
+ for (auto i = 0; i < count; ++i) {
+ new(static_cast<void*>(dst + i)) T(value);
+ }
+ }
+
+ template<typename InputIterator>
+ void fill(T* dst, InputIterator first, InputIterator last) {
+ while (first != last) {
+ new(static_cast<void*>(dst)) T(*first);
+ ++dst;
+ ++first;
+ }
+ }
+
public:
void assign(size_type n, const T& val) {
clear();
if (capacity() < n) {
change_capacity(n);
}
- while (size() < n) {
- _size++;
- new(static_cast<void*>(item_ptr(size() - 1))) T(val);
- }
+ _size += n;
+ fill(item_ptr(0), n, val);
}
template<typename InputIterator>
@@ -213,11 +242,8 @@ public:
if (capacity() < n) {
change_capacity(n);
}
- while (first != last) {
- _size++;
- new(static_cast<void*>(item_ptr(size() - 1))) T(*first);
- ++first;
- }
+ _size += n;
+ fill(item_ptr(0), first, last);
}
prevector() : _size(0), _union{{}} {}
@@ -228,31 +254,23 @@ public:
explicit prevector(size_type n, const T& val = T()) : _size(0) {
change_capacity(n);
- while (size() < n) {
- _size++;
- new(static_cast<void*>(item_ptr(size() - 1))) T(val);
- }
+ _size += n;
+ fill(item_ptr(0), n, val);
}
template<typename InputIterator>
prevector(InputIterator first, InputIterator last) : _size(0) {
size_type n = last - first;
change_capacity(n);
- while (first != last) {
- _size++;
- new(static_cast<void*>(item_ptr(size() - 1))) T(*first);
- ++first;
- }
+ _size += n;
+ fill(item_ptr(0), first, last);
}
prevector(const prevector<N, T, Size, Diff>& other) : _size(0) {
- change_capacity(other.size());
- const_iterator it = other.begin();
- while (it != other.end()) {
- _size++;
- new(static_cast<void*>(item_ptr(size() - 1))) T(*it);
- ++it;
- }
+ size_type n = other.size();
+ change_capacity(n);
+ _size += n;
+ fill(item_ptr(0), other.begin(), other.end());
}
prevector(prevector<N, T, Size, Diff>&& other) : _size(0) {
@@ -263,14 +281,7 @@ public:
if (&other == this) {
return *this;
}
- resize(0);
- change_capacity(other.size());
- const_iterator it = other.begin();
- while (it != other.end()) {
- _size++;
- new(static_cast<void*>(item_ptr(size() - 1))) T(*it);
- ++it;
- }
+ assign(other.begin(), other.end());
return *this;
}
@@ -314,16 +325,20 @@ public:
}
void resize(size_type new_size) {
- if (size() > new_size) {
+ size_type cur_size = size();
+ if (cur_size == new_size) {
+ return;
+ }
+ if (cur_size > new_size) {
erase(item_ptr(new_size), end());
+ return;
}
if (new_size > capacity()) {
change_capacity(new_size);
}
- while (size() < new_size) {
- _size++;
- new(static_cast<void*>(item_ptr(size() - 1))) T();
- }
+ ptrdiff_t increase = new_size - cur_size;
+ fill(item_ptr(cur_size), increase);
+ _size += increase;
}
void reserve(size_type new_capacity) {
@@ -346,10 +361,11 @@ public:
if (capacity() < new_size) {
change_capacity(new_size + (new_size >> 1));
}
- memmove(item_ptr(p + 1), item_ptr(p), (size() - p) * sizeof(T));
+ T* ptr = item_ptr(p);
+ memmove(ptr + 1, ptr, (size() - p) * sizeof(T));
_size++;
- new(static_cast<void*>(item_ptr(p))) T(value);
- return iterator(item_ptr(p));
+ new(static_cast<void*>(ptr)) T(value);
+ return iterator(ptr);
}
void insert(iterator pos, size_type count, const T& value) {
@@ -358,11 +374,10 @@ public:
if (capacity() < new_size) {
change_capacity(new_size + (new_size >> 1));
}
- memmove(item_ptr(p + count), item_ptr(p), (size() - p) * sizeof(T));
+ T* ptr = item_ptr(p);
+ memmove(ptr + count, ptr, (size() - p) * sizeof(T));
_size += count;
- for (size_type i = 0; i < count; i++) {
- new(static_cast<void*>(item_ptr(p + i))) T(value);
- }
+ fill(item_ptr(p), count, value);
}
template<typename InputIterator>
@@ -373,13 +388,10 @@ public:
if (capacity() < new_size) {
change_capacity(new_size + (new_size >> 1));
}
- memmove(item_ptr(p + count), item_ptr(p), (size() - p) * sizeof(T));
+ T* ptr = item_ptr(p);
+ memmove(ptr + count, ptr, (size() - p) * sizeof(T));
_size += count;
- while (first != last) {
- new(static_cast<void*>(item_ptr(p))) T(*first);
- ++p;
- ++first;
- }
+ fill(ptr, first, last);
}
iterator erase(iterator pos) {
diff --git a/src/protocol.cpp b/src/protocol.cpp
index c412ad9ffe..2ec26fbd3e 100644
--- a/src/protocol.cpp
+++ b/src/protocol.cpp
@@ -12,6 +12,8 @@
# include <arpa/inet.h>
#endif
+static std::atomic<bool> g_initial_block_download_completed(false);
+
namespace NetMsgType {
const char *VERSION="version";
const char *VERACK="verack";
@@ -127,6 +129,17 @@ bool CMessageHeader::IsValid(const MessageStartChars& pchMessageStartIn) const
}
+ServiceFlags GetDesirableServiceFlags(ServiceFlags services) {
+ if ((services & NODE_NETWORK_LIMITED) && g_initial_block_download_completed) {
+ return ServiceFlags(NODE_NETWORK_LIMITED | NODE_WITNESS);
+ }
+ return ServiceFlags(NODE_NETWORK | NODE_WITNESS);
+}
+
+void SetServiceFlagsIBDCache(bool state) {
+ g_initial_block_download_completed = state;
+}
+
CAddress::CAddress() : CService()
{
diff --git a/src/protocol.h b/src/protocol.h
index 42eb57e4f0..e518d11944 100644
--- a/src/protocol.h
+++ b/src/protocol.h
@@ -15,6 +15,7 @@
#include <uint256.h>
#include <version.h>
+#include <atomic>
#include <stdint.h>
#include <string>
@@ -301,9 +302,10 @@ enum ServiceFlags : uint64_t {
* If the NODE_NONE return value is changed, contrib/seeds/makeseeds.py
* should be updated appropriately to filter for the same nodes.
*/
-static ServiceFlags GetDesirableServiceFlags(ServiceFlags services) {
- return ServiceFlags(NODE_NETWORK | NODE_WITNESS);
-}
+ServiceFlags GetDesirableServiceFlags(ServiceFlags services);
+
+/** Set the current IBD status in order to figure out the desirable service flags */
+void SetServiceFlagsIBDCache(bool status);
/**
* A shortcut for (services & GetDesirableServiceFlags(services))
@@ -316,10 +318,10 @@ static inline bool HasAllDesirableServiceFlags(ServiceFlags services) {
/**
* Checks if a peer with the given service flags may be capable of having a
- * robust address-storage DB. Currently an alias for checking NODE_NETWORK.
+ * robust address-storage DB.
*/
static inline bool MayHaveUsefulAddressDB(ServiceFlags services) {
- return services & NODE_NETWORK;
+ return (services & NODE_NETWORK) || (services & NODE_NETWORK_LIMITED);
}
/** A CService with information about it as peer */
diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp
index 74f0db3520..ffb5bff4de 100644
--- a/src/qt/addresstablemodel.cpp
+++ b/src/qt/addresstablemodel.cpp
@@ -393,11 +393,8 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con
}
// Add entry
- {
- LOCK(wallet->cs_wallet);
- wallet->SetAddressBook(DecodeDestination(strAddress), strLabel,
- (type == Send ? "send" : "receive"));
- }
+ wallet->SetAddressBook(DecodeDestination(strAddress), strLabel,
+ (type == Send ? "send" : "receive"));
return QString::fromStdString(strAddress);
}
@@ -411,10 +408,7 @@ bool AddressTableModel::removeRows(int row, int count, const QModelIndex &parent
// Also refuse to remove receiving addresses.
return false;
}
- {
- LOCK(wallet->cs_wallet);
- wallet->DelAddressBook(DecodeDestination(rec->address.toStdString()));
- }
+ wallet->DelAddressBook(DecodeDestination(rec->address.toStdString()));
return true;
}
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 06e1f1a37c..ab381bfb5d 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -29,6 +29,7 @@
#include <init.h>
#include <rpc/server.h>
#include <ui_interface.h>
+#include <uint256.h>
#include <util.h>
#include <warnings.h>
@@ -80,6 +81,7 @@ Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin);
// Declare meta types used for QMetaObject::invokeMethod
Q_DECLARE_METATYPE(bool*)
Q_DECLARE_METATYPE(CAmount)
+Q_DECLARE_METATYPE(uint256)
static void InitMessage(const std::string &message)
{
diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp
index 3642e5d4d4..eaf2896bc3 100644
--- a/src/qt/clientmodel.cpp
+++ b/src/qt/clientmodel.cpp
@@ -138,9 +138,9 @@ size_t ClientModel::getMempoolDynamicUsage() const
double ClientModel::getVerificationProgress(const CBlockIndex *tipIn) const
{
CBlockIndex *tip = const_cast<CBlockIndex *>(tipIn);
+ LOCK(cs_main);
if (!tip)
{
- LOCK(cs_main);
tip = chainActive.Tip();
}
return GuessVerificationProgress(Params().TxData(), tip);
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index edf1c29ea1..a46e0561b9 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -417,7 +417,7 @@ void openDebugLogfile()
bool openBitcoinConf()
{
- boost::filesystem::path pathConfig = GetConfigFile(BITCOIN_CONF_FILENAME);
+ boost::filesystem::path pathConfig = GetConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME));
/* Create the file */
boost::filesystem::ofstream configFile(pathConfig, std::ios_base::app);
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index c4b209a880..1aa4de03ca 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -70,6 +70,7 @@ const QStringList historyFilter = QStringList()
<< "importmulti"
<< "signmessagewithprivkey"
<< "signrawtransaction"
+ << "signrawtransactionwithkey"
<< "walletpassphrase"
<< "walletpassphrasechange"
<< "encryptwallet";
@@ -624,7 +625,7 @@ void RPCConsole::setClientModel(ClientModel *model)
connect(model->getPeerTableModel(), SIGNAL(layoutChanged()), this, SLOT(peerLayoutChanged()));
// peer table signal handling - cache selected node ids
connect(model->getPeerTableModel(), SIGNAL(layoutAboutToBeChanged()), this, SLOT(peerLayoutAboutToChange()));
-
+
// set up ban table
ui->banlistWidget->setModel(model->getBanTableModel());
ui->banlistWidget->verticalHeader()->hide();
@@ -772,7 +773,7 @@ void RPCConsole::clear(bool clearHistory)
#else
QString clsKey = "Ctrl-L";
#endif
-
+
message(CMD_REPLY, (tr("Welcome to the %1 RPC console.").arg(tr(PACKAGE_NAME)) + "<br>" +
tr("Use up and down arrows to navigate history, and %1 to clear screen.").arg("<b>"+clsKey+"</b>") + "<br>" +
tr("Type %1 for an overview of available commands.").arg("<b>help</b>") + "<br>" +
@@ -1144,7 +1145,7 @@ void RPCConsole::disconnectSelectedNode()
{
if(!g_connman)
return;
-
+
// Get selected peer addresses
QList<QModelIndex> nodes = GUIUtil::getEntryData(ui->peerWidget, PeerTableModel::NetNodeId);
for(int i = 0; i < nodes.count(); i++)
@@ -1161,7 +1162,7 @@ void RPCConsole::banSelectedNode(int bantime)
{
if (!clientModel || !g_connman)
return;
-
+
// Get selected peer addresses
QList<QModelIndex> nodes = GUIUtil::getEntryData(ui->peerWidget, PeerTableModel::NetNodeId);
for(int i = 0; i < nodes.count(); i++)
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index 871822ccb4..ef36aab1a4 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -369,6 +369,7 @@ void SendCoinsDialog::on_sendButton_clicked()
accept();
CoinControlDialog::coinControl()->UnSelectAll();
coinControlUpdateLabels();
+ Q_EMIT coinsSent(currentTransaction.getTransaction()->GetHash());
}
fNewRecipientAllowed = true;
}
diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h
index 7c27785d12..48885bbcad 100644
--- a/src/qt/sendcoinsdialog.h
+++ b/src/qt/sendcoinsdialog.h
@@ -54,6 +54,9 @@ public Q_SLOTS:
void setBalance(const CAmount& balance, const CAmount& unconfirmedBalance, const CAmount& immatureBalance,
const CAmount& watchOnlyBalance, const CAmount& watchUnconfBalance, const CAmount& watchImmatureBalance);
+Q_SIGNALS:
+ void coinsSent(const uint256& txid);
+
private:
Ui::SendCoinsDialog *ui;
ClientModel *clientModel;
diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp
index aaec15cc13..9d0e0b97d1 100644
--- a/src/qt/test/rpcnestedtests.cpp
+++ b/src/qt/test/rpcnestedtests.cpp
@@ -82,8 +82,8 @@ void RPCNestedTests::rpcNestedTests()
QVERIFY(filtered == "signmessagewithprivkey(…)");
RPCConsole::RPCParseCommandLine(result, "signmessagewithprivkey abc,def", false, &filtered);
QVERIFY(filtered == "signmessagewithprivkey(…)");
- RPCConsole::RPCParseCommandLine(result, "signrawtransaction(abc)", false, &filtered);
- QVERIFY(filtered == "signrawtransaction(…)");
+ RPCConsole::RPCParseCommandLine(result, "signrawtransactionwithkey(abc)", false, &filtered);
+ QVERIFY(filtered == "signrawtransactionwithkey(…)");
RPCConsole::RPCParseCommandLine(result, "walletpassphrase(help())", false, &filtered);
QVERIFY(filtered == "walletpassphrase(…)");
RPCConsole::RPCParseCommandLine(result, "walletpassphrasechange(help(walletpassphrasechange(abc)))", false, &filtered);
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
index cd49292138..0dc557e3a9 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -158,6 +158,8 @@ void TestGUI()
test.CreateAndProcessBlock({}, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey()));
}
bitdb.MakeMock();
+ g_wallet_allow_fallback_fee = true;
+
std::unique_ptr<CWalletDBWrapper> dbw(new CWalletDBWrapper(&bitdb, "wallet_test.dat"));
CWallet wallet(std::move(dbw));
bool firstRun;
diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp
index 88f8f463bc..006f9fe443 100644
--- a/src/qt/transactionview.cpp
+++ b/src/qt/transactionview.cpp
@@ -263,8 +263,7 @@ void TransactionView::setModel(WalletModel *_model)
void TransactionView::chooseDate(int idx)
{
- if(!transactionProxyModel)
- return;
+ if (!transactionProxyModel) return;
QDate current = QDate::currentDate();
dateRangeWidget->setVisible(false);
switch(dateWidget->itemData(idx).toInt())
@@ -592,6 +591,32 @@ void TransactionView::focusTransaction(const QModelIndex &idx)
transactionView->setFocus();
}
+void TransactionView::focusTransaction(const uint256& txid)
+{
+ if (!transactionProxyModel)
+ return;
+
+ const QModelIndexList results = this->model->getTransactionTableModel()->match(
+ this->model->getTransactionTableModel()->index(0,0),
+ TransactionTableModel::TxHashRole,
+ QString::fromStdString(txid.ToString()), -1);
+
+ transactionView->setFocus();
+ transactionView->selectionModel()->clearSelection();
+ for (const QModelIndex& index : results) {
+ const QModelIndex targetIndex = transactionProxyModel->mapFromSource(index);
+ transactionView->selectionModel()->select(
+ targetIndex,
+ QItemSelectionModel::Rows | QItemSelectionModel::Select);
+ // Called once per destination to ensure all results are in view, unless
+ // transactions are not ordered by (ascending or descending) date.
+ transactionView->scrollTo(targetIndex);
+ // scrollTo() does not scroll far enough the first time when transactions
+ // are ordered by ascending date.
+ if (index == results[0]) transactionView->scrollTo(targetIndex);
+ }
+}
+
// We override the virtual resizeEvent of the QWidget to adjust tables column
// sizes as the tables width is proportional to the dialogs width.
void TransactionView::resizeEvent(QResizeEvent* event)
diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h
index 82e929b53f..66dc5bc86b 100644
--- a/src/qt/transactionview.h
+++ b/src/qt/transactionview.h
@@ -7,6 +7,8 @@
#include <qt/guiutil.h>
+#include <uint256.h>
+
#include <QWidget>
#include <QKeyEvent>
@@ -116,7 +118,7 @@ public Q_SLOTS:
void changedSearch();
void exportClicked();
void focusTransaction(const QModelIndex&);
-
+ void focusTransaction(const uint256& txid);
};
#endif // BITCOIN_QT_TRANSACTIONVIEW_H
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index 541114e5fe..34954a6bfa 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -42,6 +42,7 @@ WalletModel::WalletModel(const PlatformStyle *platformStyle, CWallet *_wallet, O
transactionTableModel(0),
recentRequestsTableModel(0),
cachedBalance(0), cachedUnconfirmedBalance(0), cachedImmatureBalance(0),
+ cachedWatchOnlyBalance{0}, cachedWatchUnconfBalance{0}, cachedWatchImmatureBalance{0},
cachedEncryptionStatus(Unencrypted),
cachedNumBlocks(0)
{
diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp
index 7eced9289d..64497a3431 100644
--- a/src/qt/walletview.cpp
+++ b/src/qt/walletview.cpp
@@ -68,6 +68,9 @@ WalletView::WalletView(const PlatformStyle *_platformStyle, QWidget *parent):
connect(overviewPage, SIGNAL(transactionClicked(QModelIndex)), transactionView, SLOT(focusTransaction(QModelIndex)));
connect(overviewPage, SIGNAL(outOfSyncWarningClicked()), this, SLOT(requestedSyncWarningInfo()));
+ // Highlight transaction after send
+ connect(sendCoinsPage, SIGNAL(coinsSent(uint256)), transactionView, SLOT(focusTransaction(uint256)));
+
// Double-clicking on a transaction on the transaction history page shows details
connect(transactionView, SIGNAL(doubleClicked(QModelIndex)), transactionView, SLOT(showDetails()));
@@ -91,6 +94,9 @@ void WalletView::setBitcoinGUI(BitcoinGUI *gui)
// Clicking on a transaction on the overview page simply sends you to transaction history page
connect(overviewPage, SIGNAL(transactionClicked(QModelIndex)), gui, SLOT(gotoHistoryPage()));
+ // Navigate to transaction history page after send
+ connect(sendCoinsPage, SIGNAL(coinsSent(uint256)), gui, SLOT(gotoHistoryPage()));
+
// Receive and report messages
connect(this, SIGNAL(message(QString,QString,unsigned int)), gui, SLOT(message(QString,QString,unsigned int)));
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 189da6ae48..8007cebc37 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -1434,7 +1434,7 @@ UniValue preciousblock(const JSONRPCRequest& request)
PreciousBlock(state, Params(), pblockindex);
if (!state.IsValid()) {
- throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason());
+ throw JSONRPCError(RPC_DATABASE_ERROR, FormatStateMessage(state));
}
return NullUniValue;
@@ -1472,7 +1472,7 @@ UniValue invalidateblock(const JSONRPCRequest& request)
}
if (!state.IsValid()) {
- throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason());
+ throw JSONRPCError(RPC_DATABASE_ERROR, FormatStateMessage(state));
}
return NullUniValue;
@@ -1509,7 +1509,7 @@ UniValue reconsiderblock(const JSONRPCRequest& request)
ActivateBestChain(state, Params());
if (!state.IsValid()) {
- throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason());
+ throw JSONRPCError(RPC_DATABASE_ERROR, FormatStateMessage(state));
}
return NullUniValue;
@@ -1542,28 +1542,22 @@ UniValue getchaintxstats(const JSONRPCRequest& request)
const CBlockIndex* pindex;
int blockcount = 30 * 24 * 60 * 60 / Params().GetConsensus().nPowTargetSpacing; // By default: 1 month
- bool havehash = !request.params[1].isNull();
- uint256 hash;
- if (havehash) {
- hash = uint256S(request.params[1].get_str());
- }
-
- {
+ if (request.params[1].isNull()) {
LOCK(cs_main);
- if (havehash) {
- auto it = mapBlockIndex.find(hash);
- if (it == mapBlockIndex.end()) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
- }
- pindex = it->second;
- if (!chainActive.Contains(pindex)) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Block is not in main chain");
- }
- } else {
- pindex = chainActive.Tip();
+ pindex = chainActive.Tip();
+ } else {
+ uint256 hash = uint256S(request.params[1].get_str());
+ LOCK(cs_main);
+ auto it = mapBlockIndex.find(hash);
+ if (it == mapBlockIndex.end()) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
+ }
+ pindex = it->second;
+ if (!chainActive.Contains(pindex)) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Block is not in main chain");
}
}
-
+
assert(pindex != nullptr);
if (request.params[0].isNull()) {
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index 99c1242d8a..a95ea0cf92 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -94,6 +94,9 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "decoderawtransaction", 1, "iswitness" },
{ "signrawtransaction", 1, "prevtxs" },
{ "signrawtransaction", 2, "privkeys" },
+ { "signrawtransactionwithkey", 1, "privkeys" },
+ { "signrawtransactionwithkey", 2, "prevtxs" },
+ { "signrawtransactionwithwallet", 1, "prevtxs" },
{ "sendrawtransaction", 1, "allowhighfees" },
{ "combinerawtransaction", 0, "txs" },
{ "fundrawtransaction", 1, "options" },
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index dd74095b62..3f3bfa0cfd 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -264,11 +264,11 @@ static UniValue BIP22ValidationResult(const CValidationState& state)
if (state.IsValid())
return NullUniValue;
- std::string strRejectReason = state.GetRejectReason();
if (state.IsError())
- throw JSONRPCError(RPC_VERIFY_ERROR, strRejectReason);
+ throw JSONRPCError(RPC_VERIFY_ERROR, FormatStateMessage(state));
if (state.IsInvalid())
{
+ std::string strRejectReason = state.GetRejectReason();
if (strRejectReason.empty())
return "rejected";
return strRejectReason;
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index 3f89996e61..f573c7dbeb 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -33,175 +33,33 @@
#include <univalue.h>
-#ifdef ENABLE_WALLET
-class DescribeAddressVisitor : public boost::static_visitor<UniValue>
-{
-public:
- CWallet * const pwallet;
-
- explicit DescribeAddressVisitor(CWallet *_pwallet) : pwallet(_pwallet) {}
-
- void ProcessSubScript(const CScript& subscript, UniValue& obj, bool include_addresses = false) const
- {
- // Always present: script type and redeemscript
- txnouttype which_type;
- std::vector<std::vector<unsigned char>> solutions_data;
- Solver(subscript, which_type, solutions_data);
- obj.pushKV("script", GetTxnOutputType(which_type));
- obj.pushKV("hex", HexStr(subscript.begin(), subscript.end()));
-
- CTxDestination embedded;
- UniValue a(UniValue::VARR);
- if (ExtractDestination(subscript, embedded)) {
- // Only when the script corresponds to an address.
- UniValue subobj = boost::apply_visitor(*this, embedded);
- subobj.pushKV("address", EncodeDestination(embedded));
- subobj.pushKV("scriptPubKey", HexStr(subscript.begin(), subscript.end()));
- // Always report the pubkey at the top level, so that `getnewaddress()['pubkey']` always works.
- if (subobj.exists("pubkey")) obj.pushKV("pubkey", subobj["pubkey"]);
- obj.pushKV("embedded", std::move(subobj));
- if (include_addresses) a.push_back(EncodeDestination(embedded));
- } else if (which_type == TX_MULTISIG) {
- // Also report some information on multisig scripts (which do not have a corresponding address).
- // TODO: abstract out the common functionality between this logic and ExtractDestinations.
- obj.pushKV("sigsrequired", solutions_data[0][0]);
- UniValue pubkeys(UniValue::VARR);
- for (size_t i = 1; i < solutions_data.size() - 1; ++i) {
- CPubKey key(solutions_data[i].begin(), solutions_data[i].end());
- if (include_addresses) a.push_back(EncodeDestination(key.GetID()));
- pubkeys.push_back(HexStr(key.begin(), key.end()));
- }
- obj.pushKV("pubkeys", std::move(pubkeys));
- }
-
- // The "addresses" field is confusing because it refers to public keys using their P2PKH address.
- // For that reason, only add the 'addresses' field when needed for backward compatibility. New applications
- // can use the 'embedded'->'address' field for P2SH or P2WSH wrapped addresses, and 'pubkeys' for
- // inspecting multisig participants.
- if (include_addresses) obj.pushKV("addresses", std::move(a));
- }
-
- UniValue operator()(const CNoDestination &dest) const { return UniValue(UniValue::VOBJ); }
-
- UniValue operator()(const CKeyID &keyID) const {
- UniValue obj(UniValue::VOBJ);
- CPubKey vchPubKey;
- obj.pushKV("isscript", false);
- obj.pushKV("iswitness", false);
- if (pwallet && pwallet->GetPubKey(keyID, vchPubKey)) {
- obj.pushKV("pubkey", HexStr(vchPubKey));
- obj.pushKV("iscompressed", vchPubKey.IsCompressed());
- }
- return obj;
- }
-
- UniValue operator()(const CScriptID &scriptID) const {
- UniValue obj(UniValue::VOBJ);
- CScript subscript;
- obj.pushKV("isscript", true);
- obj.pushKV("iswitness", false);
- if (pwallet && pwallet->GetCScript(scriptID, subscript)) {
- ProcessSubScript(subscript, obj, true);
- }
- return obj;
- }
-
- UniValue operator()(const WitnessV0KeyHash& id) const
- {
- UniValue obj(UniValue::VOBJ);
- CPubKey pubkey;
- obj.pushKV("isscript", false);
- obj.pushKV("iswitness", true);
- obj.pushKV("witness_version", 0);
- obj.pushKV("witness_program", HexStr(id.begin(), id.end()));
- if (pwallet && pwallet->GetPubKey(CKeyID(id), pubkey)) {
- obj.pushKV("pubkey", HexStr(pubkey));
- }
- return obj;
- }
-
- UniValue operator()(const WitnessV0ScriptHash& id) const
- {
- UniValue obj(UniValue::VOBJ);
- CScript subscript;
- obj.pushKV("isscript", true);
- obj.pushKV("iswitness", true);
- obj.pushKV("witness_version", 0);
- obj.pushKV("witness_program", HexStr(id.begin(), id.end()));
- CRIPEMD160 hasher;
- uint160 hash;
- hasher.Write(id.begin(), 32).Finalize(hash.begin());
- if (pwallet && pwallet->GetCScript(CScriptID(hash), subscript)) {
- ProcessSubScript(subscript, obj);
- }
- return obj;
- }
-
- UniValue operator()(const WitnessUnknown& id) const
- {
- UniValue obj(UniValue::VOBJ);
- CScript subscript;
- obj.pushKV("iswitness", true);
- obj.pushKV("witness_version", (int)id.version);
- obj.pushKV("witness_program", HexStr(id.program, id.program + id.length));
- return obj;
- }
-};
-#endif
-
UniValue validateaddress(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
"validateaddress \"address\"\n"
"\nReturn information about the given bitcoin address.\n"
+ "DEPRECATION WARNING: Parts of this command have been deprecated and moved to getaddressinfo. Clients must\n"
+ "transition to using getaddressinfo to access this information before upgrading to v0.18. The following deprecated\n"
+ "fields have moved to getaddressinfo and will only be shown here with -deprecatedrpc=validateaddress: ismine, iswatchonly,\n"
+ "script, hex, pubkeys, sigsrequired, pubkey, addresses, embedded, iscompressed, account, timestamp, hdkeypath, kdmasterkeyid.\n"
"\nArguments:\n"
- "1. \"address\" (string, required) The bitcoin address to validate\n"
+ "1. \"address\" (string, required) The bitcoin address to validate\n"
"\nResult:\n"
"{\n"
" \"isvalid\" : true|false, (boolean) If the address is valid or not. If not, this is the only property returned.\n"
" \"address\" : \"address\", (string) The bitcoin address validated\n"
" \"scriptPubKey\" : \"hex\", (string) The hex encoded scriptPubKey generated by the address\n"
- " \"ismine\" : true|false, (boolean) If the address is yours or not\n"
- " \"iswatchonly\" : true|false, (boolean) If the address is watchonly\n"
- " \"isscript\" : true|false, (boolean, optional) If the address is P2SH or P2WSH. Not included for unknown witness types.\n"
- " \"iswitness\" : true|false, (boolean) If the address is P2WPKH, P2WSH, or an unknown witness version\n"
- " \"witness_version\" : version (number, optional) For all witness output types, gives the version number.\n"
- " \"witness_program\" : \"hex\" (string, optional) For all witness output types, gives the script or key hash present in the address.\n"
- " \"script\" : \"type\" (string, optional) The output script type. Only if \"isscript\" is true and the redeemscript is known. Possible types: nonstandard, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_keyhash, witness_v0_scripthash, witness_unknown\n"
- " \"hex\" : \"hex\", (string, optional) The redeemscript for the P2SH or P2WSH address\n"
- " \"addresses\" (string, optional) Array of addresses associated with the known redeemscript (only if \"iswitness\" is false). This field is superseded by the \"pubkeys\" field and the address inside \"embedded\".\n"
- " [\n"
- " \"address\"\n"
- " ,...\n"
- " ]\n"
- " \"pubkeys\" (string, optional) Array of pubkeys associated with the known redeemscript (only if \"script\" is \"multisig\")\n"
- " [\n"
- " \"pubkey\"\n"
- " ,...\n"
- " ]\n"
- " \"sigsrequired\" : xxxxx (numeric, optional) Number of signatures required to spend multisig output (only if \"script\" is \"multisig\")\n"
- " \"pubkey\" : \"publickeyhex\", (string, optional) The hex value of the raw public key, for single-key addresses (possibly embedded in P2SH or P2WSH)\n"
- " \"embedded\" : {...}, (object, optional) information about the address embedded in P2SH or P2WSH, if relevant and known. It includes all validateaddress output fields for the embedded address, excluding \"isvalid\", 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) DEPRECATED. The account associated with the address, \"\" is the default account\n"
- " \"timestamp\" : timestamp, (number, optional) The creation time of the key if available in seconds since epoch (Jan 1 1970 GMT)\n"
- " \"hdkeypath\" : \"keypath\" (string, optional) The HD keypath if the key is HD and available\n"
- " \"hdmasterkeyid\" : \"<hash160>\" (string, optional) The Hash160 of the HD master pubkey\n"
+ " \"isscript\" : true|false, (boolean) If the key is a script\n"
+ " \"iswitness\" : true|false, (boolean) If the address is a witness address\n"
+ " \"witness_version\" : version (numeric, optional) The version number of the witness program\n"
+ " \"witness_program\" : \"hex\" (string, optional) The hex value of the witness program\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"")
+ HelpExampleRpc("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"")
);
-#ifdef ENABLE_WALLET
- CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
-
- LOCK2(cs_main, pwallet ? &pwallet->cs_wallet : nullptr);
-#else
- LOCK(cs_main);
-#endif
-
CTxDestination dest = DecodeDestination(request.params[0].get_str());
bool isValid = IsValidDestination(dest);
@@ -209,45 +67,22 @@ UniValue validateaddress(const JSONRPCRequest& request)
ret.pushKV("isvalid", isValid);
if (isValid)
{
- std::string currentAddress = EncodeDestination(dest);
- ret.pushKV("address", currentAddress);
-
- CScript scriptPubKey = GetScriptForDestination(dest);
- ret.pushKV("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()));
#ifdef ENABLE_WALLET
- isminetype mine = pwallet ? IsMine(*pwallet, dest) : ISMINE_NO;
- ret.pushKV("ismine", bool(mine & ISMINE_SPENDABLE));
- ret.pushKV("iswatchonly", bool(mine & ISMINE_WATCH_ONLY));
- UniValue detail = boost::apply_visitor(DescribeAddressVisitor(pwallet), dest);
- ret.pushKVs(detail);
- if (pwallet && pwallet->mapAddressBook.count(dest)) {
- ret.pushKV("account", pwallet->mapAddressBook[dest].name);
- }
- if (pwallet) {
- const CKeyMetadata* meta = nullptr;
- CKeyID key_id = GetKeyForDestination(*pwallet, dest);
- if (!key_id.IsNull()) {
- auto it = pwallet->mapKeyMetadata.find(key_id);
- if (it != pwallet->mapKeyMetadata.end()) {
- meta = &it->second;
- }
- }
- if (!meta) {
- auto it = pwallet->m_script_metadata.find(CScriptID(scriptPubKey));
- if (it != pwallet->m_script_metadata.end()) {
- meta = &it->second;
- }
- }
- if (meta) {
- ret.pushKV("timestamp", meta->nCreateTime);
- if (!meta->hdKeypath.empty()) {
- ret.pushKV("hdkeypath", meta->hdKeypath);
- ret.pushKV("hdmasterkeyid", meta->hdMasterKeyID.GetHex());
- }
- }
+ if (!::vpwallets.empty() && IsDeprecatedRPCEnabled("validateaddress")) {
+ ret.pushKVs(getaddressinfo(request));
}
#endif
+ if (ret["address"].isNull()) {
+ std::string currentAddress = EncodeDestination(dest);
+ ret.pushKV("address", currentAddress);
+
+ CScript scriptPubKey = GetScriptForDestination(dest);
+ ret.pushKV("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()));;
+
+ UniValue detail = DescribeAddress(dest);
+ ret.pushKVs(detail);
+ }
}
return ret;
}
@@ -263,7 +98,7 @@ UniValue createmultisig(const JSONRPCRequest& request)
"\nCreates a multi-signature address with n signature of m keys required.\n"
"It returns a json object with the address and redeemScript.\n"
"\nArguments:\n"
- "1. nrequired (numeric, required) The number of required signatures out of the n keys or addresses.\n"
+ "1. nrequired (numeric, required) The number of required signatures out of the n keys.\n"
"2. \"keys\" (string, required) A json array of hex-encoded public keys\n"
" [\n"
" \"key\" (string) The hex-encoded public key\n"
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index 7a0225ff0d..fee2b765ba 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -89,7 +89,7 @@ UniValue getpeerinfo(const JSONRPCRequest& request)
" \"pingtime\": n, (numeric) ping time (if available)\n"
" \"minping\": n, (numeric) minimum observed ping time (if any at all)\n"
" \"pingwait\": n, (numeric) ping wait (if non-zero)\n"
- " \"version\": v, (numeric) The peer version, such as 7001\n"
+ " \"version\": v, (numeric) The peer version, such as 70001\n"
" \"subver\": \"/Satoshi:0.8.5/\", (string) The string version\n"
" \"inbound\": true|false, (boolean) Inbound (true) or Outbound (false)\n"
" \"addnode\": true|false, (boolean) Whether connection was due to addnode/-connect or if it was an automatic/inbound connection\n"
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 24f2431efc..813afde4db 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -17,6 +17,7 @@
#include <policy/policy.h>
#include <policy/rbf.h>
#include <primitives/transaction.h>
+#include <rpc/rawtransaction.h>
#include <rpc/safemode.h>
#include <rpc/server.h>
#include <script/script.h>
@@ -28,7 +29,6 @@
#include <utilstrencodings.h>
#ifdef ENABLE_WALLET
#include <wallet/rpcwallet.h>
-#include <wallet/wallet.h>
#endif
#include <future>
@@ -672,88 +672,13 @@ UniValue combinerawtransaction(const JSONRPCRequest& request)
return EncodeHexTx(mergedTx);
}
-UniValue signrawtransaction(const JSONRPCRequest& request)
+UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival, CBasicKeyStore *keystore, bool is_temp_keystore, const UniValue& hashType)
{
-#ifdef ENABLE_WALLET
- CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
-#endif
-
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 4)
- throw std::runtime_error(
- "signrawtransaction \"hexstring\" ( [{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\",\"redeemScript\":\"hex\"},...] [\"privatekey1\",...] sighashtype )\n"
- "\nSign inputs for raw transaction (serialized, hex-encoded).\n"
- "The second optional argument (may be null) is an array of previous transaction outputs that\n"
- "this transaction depends on but may not yet be in the block chain.\n"
- "The third optional argument (may be null) is an array of base58-encoded private\n"
- "keys that, if given, will be the only keys used to sign the transaction.\n"
-#ifdef ENABLE_WALLET
- + HelpRequiringPassphrase(pwallet) + "\n"
-#endif
-
- "\nArguments:\n"
- "1. \"hexstring\" (string, required) The transaction hex string\n"
- "2. \"prevtxs\" (string, optional) A json array of previous dependent transaction outputs\n"
- " [ (json array of json objects, or 'null' if none provided)\n"
- " {\n"
- " \"txid\":\"id\", (string, required) The transaction id\n"
- " \"vout\":n, (numeric, required) The output number\n"
- " \"scriptPubKey\": \"hex\", (string, required) script key\n"
- " \"redeemScript\": \"hex\", (string, required for P2SH or P2WSH) redeem script\n"
- " \"amount\": value (numeric, required) The amount spent\n"
- " }\n"
- " ,...\n"
- " ]\n"
- "3. \"privkeys\" (string, optional) A json array of base58-encoded private keys for signing\n"
- " [ (json array of strings, or 'null' if none provided)\n"
- " \"privatekey\" (string) private key in base58-encoding\n"
- " ,...\n"
- " ]\n"
- "4. \"sighashtype\" (string, optional, default=ALL) The signature hash type. Must be one of\n"
- " \"ALL\"\n"
- " \"NONE\"\n"
- " \"SINGLE\"\n"
- " \"ALL|ANYONECANPAY\"\n"
- " \"NONE|ANYONECANPAY\"\n"
- " \"SINGLE|ANYONECANPAY\"\n"
-
- "\nResult:\n"
- "{\n"
- " \"hex\" : \"value\", (string) The hex-encoded raw transaction with signature(s)\n"
- " \"complete\" : true|false, (boolean) If the transaction has a complete set of signatures\n"
- " \"errors\" : [ (json array of objects) Script verification errors (if there are any)\n"
- " {\n"
- " \"txid\" : \"hash\", (string) The hash of the referenced, previous transaction\n"
- " \"vout\" : n, (numeric) The index of the output to spent and used as input\n"
- " \"scriptSig\" : \"hex\", (string) The hex-encoded signature script\n"
- " \"sequence\" : n, (numeric) Script sequence number\n"
- " \"error\" : \"text\" (string) Verification or signing error related to the input\n"
- " }\n"
- " ,...\n"
- " ]\n"
- "}\n"
-
- "\nExamples:\n"
- + HelpExampleCli("signrawtransaction", "\"myhex\"")
- + HelpExampleRpc("signrawtransaction", "\"myhex\"")
- );
-
- ObserveSafeMode();
-#ifdef ENABLE_WALLET
- LOCK2(cs_main, pwallet ? &pwallet->cs_wallet : nullptr);
-#else
- LOCK(cs_main);
-#endif
- RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR, UniValue::VARR, UniValue::VSTR}, true);
-
- CMutableTransaction mtx;
- if (!DecodeHexTx(mtx, request.params[0].get_str(), true))
- throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
-
// Fetch previous transactions (inputs):
CCoinsView viewDummy;
CCoinsViewCache view(&viewDummy);
{
- LOCK(mempool.cs);
+ LOCK2(cs_main, mempool.cs);
CCoinsViewCache &viewChain = *pcoinsTip;
CCoinsViewMemPool viewMempool(&viewChain, mempool);
view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view
@@ -765,36 +690,14 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long
}
- bool fGivenKeys = false;
- CBasicKeyStore tempKeystore;
- if (!request.params[2].isNull()) {
- fGivenKeys = true;
- UniValue keys = request.params[2].get_array();
- for (unsigned int idx = 0; idx < keys.size(); idx++) {
- UniValue k = keys[idx];
- CBitcoinSecret vchSecret;
- bool fGood = vchSecret.SetString(k.get_str());
- if (!fGood)
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key");
- CKey key = vchSecret.GetKey();
- if (!key.IsValid())
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range");
- tempKeystore.AddKey(key);
- }
- }
-#ifdef ENABLE_WALLET
- else if (pwallet) {
- EnsureWalletIsUnlocked(pwallet);
- }
-#endif
-
// Add previous txouts given in the RPC call:
- if (!request.params[1].isNull()) {
- UniValue prevTxs = request.params[1].get_array();
- for (unsigned int idx = 0; idx < prevTxs.size(); idx++) {
+ if (!prevTxsUnival.isNull()) {
+ UniValue prevTxs = prevTxsUnival.get_array();
+ for (unsigned int idx = 0; idx < prevTxs.size(); ++idx) {
const UniValue& p = prevTxs[idx];
- if (!p.isObject())
+ if (!p.isObject()) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with {\"txid'\",\"vout\",\"scriptPubKey\"}");
+ }
UniValue prevOut = p.get_obj();
@@ -808,8 +711,9 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
uint256 txid = ParseHashO(prevOut, "txid");
int nOut = find_value(prevOut, "vout").get_int();
- if (nOut < 0)
+ if (nOut < 0) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive");
+ }
COutPoint out(txid, nOut);
std::vector<unsigned char> pkData(ParseHexO(prevOut, "scriptPubKey"));
@@ -834,8 +738,8 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
}
// if redeemScript given and not using the local wallet (private keys
- // given), add redeemScript to the tempKeystore so it can be signed:
- if (fGivenKeys && (scriptPubKey.IsPayToScriptHash() || scriptPubKey.IsPayToWitnessScriptHash())) {
+ // given), add redeemScript to the keystore so it can be signed:
+ if (is_temp_keystore && (scriptPubKey.IsPayToScriptHash() || scriptPubKey.IsPayToWitnessScriptHash())) {
RPCTypeCheckObj(prevOut,
{
{"txid", UniValueType(UniValue::VSTR)},
@@ -847,20 +751,16 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
if (!v.isNull()) {
std::vector<unsigned char> rsData(ParseHexV(v, "redeemScript"));
CScript redeemScript(rsData.begin(), rsData.end());
- tempKeystore.AddCScript(redeemScript);
+ keystore->AddCScript(redeemScript);
+ // Automatically also add the P2WSH wrapped version of the script (to deal with P2SH-P2WSH).
+ keystore->AddCScript(GetScriptForWitness(redeemScript));
}
}
}
}
-#ifdef ENABLE_WALLET
- const CKeyStore& keystore = ((fGivenKeys || !pwallet) ? tempKeystore : *pwallet);
-#else
- const CKeyStore& keystore = tempKeystore;
-#endif
-
int nHashType = SIGHASH_ALL;
- if (!request.params[3].isNull()) {
+ if (!hashType.isNull()) {
static std::map<std::string, int> mapSigHashValues = {
{std::string("ALL"), int(SIGHASH_ALL)},
{std::string("ALL|ANYONECANPAY"), int(SIGHASH_ALL|SIGHASH_ANYONECANPAY)},
@@ -869,11 +769,12 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
{std::string("SINGLE"), int(SIGHASH_SINGLE)},
{std::string("SINGLE|ANYONECANPAY"), int(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY)},
};
- std::string strHashType = request.params[3].get_str();
- if (mapSigHashValues.count(strHashType))
+ std::string strHashType = hashType.get_str();
+ if (mapSigHashValues.count(strHashType)) {
nHashType = mapSigHashValues[strHashType];
- else
+ } else {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid sighash param");
+ }
}
bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
@@ -897,8 +798,9 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
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);
+ if (!fHashSingle || (i < mtx.vout.size())) {
+ ProduceSignature(MutableTransactionSignatureCreator(keystore, &mtx, i, amount, nHashType), prevPubKey, sigdata);
+ }
sigdata = CombineSignatures(prevPubKey, TransactionSignatureChecker(&txConst, i, amount), sigdata, DataFromTransaction(mtx, i));
UpdateTransaction(mtx, i, sigdata);
@@ -925,6 +827,188 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
return result;
}
+UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() < 2 || request.params.size() > 4)
+ throw std::runtime_error(
+ "signrawtransactionwithkey \"hexstring\" [\"privatekey1\",...] ( [{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\",\"redeemScript\":\"hex\"},...] sighashtype )\n"
+ "\nSign inputs for raw transaction (serialized, hex-encoded).\n"
+ "The second argument is an array of base58-encoded private\n"
+ "keys that will be the only keys used to sign the transaction.\n"
+ "The third optional argument (may be null) is an array of previous transaction outputs that\n"
+ "this transaction depends on but may not yet be in the block chain.\n"
+
+ "\nArguments:\n"
+ "1. \"hexstring\" (string, required) The transaction hex string\n"
+ "2. \"privkeys\" (string, required) A json array of base58-encoded private keys for signing\n"
+ " [ (json array of strings)\n"
+ " \"privatekey\" (string) private key in base58-encoding\n"
+ " ,...\n"
+ " ]\n"
+ "3. \"prevtxs\" (string, optional) An json array of previous dependent transaction outputs\n"
+ " [ (json array of json objects, or 'null' if none provided)\n"
+ " {\n"
+ " \"txid\":\"id\", (string, required) The transaction id\n"
+ " \"vout\":n, (numeric, required) The output number\n"
+ " \"scriptPubKey\": \"hex\", (string, required) script key\n"
+ " \"redeemScript\": \"hex\", (string, required for P2SH or P2WSH) redeem script\n"
+ " \"amount\": value (numeric, required) The amount spent\n"
+ " }\n"
+ " ,...\n"
+ " ]\n"
+ "4. \"sighashtype\" (string, optional, default=ALL) The signature hash type. Must be one of\n"
+ " \"ALL\"\n"
+ " \"NONE\"\n"
+ " \"SINGLE\"\n"
+ " \"ALL|ANYONECANPAY\"\n"
+ " \"NONE|ANYONECANPAY\"\n"
+ " \"SINGLE|ANYONECANPAY\"\n"
+
+ "\nResult:\n"
+ "{\n"
+ " \"hex\" : \"value\", (string) The hex-encoded raw transaction with signature(s)\n"
+ " \"complete\" : true|false, (boolean) If the transaction has a complete set of signatures\n"
+ " \"errors\" : [ (json array of objects) Script verification errors (if there are any)\n"
+ " {\n"
+ " \"txid\" : \"hash\", (string) The hash of the referenced, previous transaction\n"
+ " \"vout\" : n, (numeric) The index of the output to spent and used as input\n"
+ " \"scriptSig\" : \"hex\", (string) The hex-encoded signature script\n"
+ " \"sequence\" : n, (numeric) Script sequence number\n"
+ " \"error\" : \"text\" (string) Verification or signing error related to the input\n"
+ " }\n"
+ " ,...\n"
+ " ]\n"
+ "}\n"
+
+ "\nExamples:\n"
+ + HelpExampleCli("signrawtransactionwithkey", "\"myhex\"")
+ + HelpExampleRpc("signrawtransactionwithkey", "\"myhex\"")
+ );
+
+ RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR, UniValue::VARR, UniValue::VSTR}, true);
+
+ CMutableTransaction mtx;
+ if (!DecodeHexTx(mtx, request.params[0].get_str(), true)) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
+ }
+
+ CBasicKeyStore keystore;
+ const UniValue& keys = request.params[1].get_array();
+ for (unsigned int idx = 0; idx < keys.size(); ++idx) {
+ UniValue k = keys[idx];
+ CBitcoinSecret vchSecret;
+ if (!vchSecret.SetString(k.get_str())) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key");
+ }
+ CKey key = vchSecret.GetKey();
+ if (!key.IsValid()) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range");
+ }
+ keystore.AddKey(key);
+ }
+
+ return SignTransaction(mtx, request.params[2], &keystore, true, request.params[3]);
+}
+
+UniValue signrawtransaction(const JSONRPCRequest& request)
+{
+#ifdef ENABLE_WALLET
+ CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
+#endif
+
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 4)
+ throw std::runtime_error(
+ "signrawtransaction \"hexstring\" ( [{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\",\"redeemScript\":\"hex\"},...] [\"privatekey1\",...] sighashtype )\n"
+ "\nDEPRECATED. Sign inputs for raw transaction (serialized, hex-encoded).\n"
+ "The second optional argument (may be null) is an array of previous transaction outputs that\n"
+ "this transaction depends on but may not yet be in the block chain.\n"
+ "The third optional argument (may be null) is an array of base58-encoded private\n"
+ "keys that, if given, will be the only keys used to sign the transaction.\n"
+#ifdef ENABLE_WALLET
+ + HelpRequiringPassphrase(pwallet) + "\n"
+#endif
+ "\nArguments:\n"
+ "1. \"hexstring\" (string, required) The transaction hex string\n"
+ "2. \"prevtxs\" (string, optional) An json array of previous dependent transaction outputs\n"
+ " [ (json array of json objects, or 'null' if none provided)\n"
+ " {\n"
+ " \"txid\":\"id\", (string, required) The transaction id\n"
+ " \"vout\":n, (numeric, required) The output number\n"
+ " \"scriptPubKey\": \"hex\", (string, required) script key\n"
+ " \"redeemScript\": \"hex\", (string, required for P2SH or P2WSH) redeem script\n"
+ " \"amount\": value (numeric, required) The amount spent\n"
+ " }\n"
+ " ,...\n"
+ " ]\n"
+ "3. \"privkeys\" (string, optional) A json array of base58-encoded private keys for signing\n"
+ " [ (json array of strings, or 'null' if none provided)\n"
+ " \"privatekey\" (string) private key in base58-encoding\n"
+ " ,...\n"
+ " ]\n"
+ "4. \"sighashtype\" (string, optional, default=ALL) The signature hash type. Must be one of\n"
+ " \"ALL\"\n"
+ " \"NONE\"\n"
+ " \"SINGLE\"\n"
+ " \"ALL|ANYONECANPAY\"\n"
+ " \"NONE|ANYONECANPAY\"\n"
+ " \"SINGLE|ANYONECANPAY\"\n"
+
+ "\nResult:\n"
+ "{\n"
+ " \"hex\" : \"value\", (string) The hex-encoded raw transaction with signature(s)\n"
+ " \"complete\" : true|false, (boolean) If the transaction has a complete set of signatures\n"
+ " \"errors\" : [ (json array of objects) Script verification errors (if there are any)\n"
+ " {\n"
+ " \"txid\" : \"hash\", (string) The hash of the referenced, previous transaction\n"
+ " \"vout\" : n, (numeric) The index of the output to spent and used as input\n"
+ " \"scriptSig\" : \"hex\", (string) The hex-encoded signature script\n"
+ " \"sequence\" : n, (numeric) Script sequence number\n"
+ " \"error\" : \"text\" (string) Verification or signing error related to the input\n"
+ " }\n"
+ " ,...\n"
+ " ]\n"
+ "}\n"
+
+ "\nExamples:\n"
+ + HelpExampleCli("signrawtransaction", "\"myhex\"")
+ + HelpExampleRpc("signrawtransaction", "\"myhex\"")
+ );
+
+ if (!IsDeprecatedRPCEnabled("signrawtransaction")) {
+ throw JSONRPCError(RPC_METHOD_DEPRECATED, "signrawtransaction is deprecated and will be fully removed in v0.18. "
+ "To use signrawtransaction in v0.17, restart bitcoind with -deprecatedrpc=signrawtransaction.\n"
+ "Projects should transition to using signrawtransactionwithkey and signrawtransactionwithwallet before upgrading to v0.18");
+ }
+
+ RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR, UniValue::VARR, UniValue::VSTR}, true);
+
+ // Make a JSONRPCRequest to pass on to the right signrawtransaction* command
+ JSONRPCRequest new_request;
+ new_request.id = request.id;
+ new_request.params.setArray();
+
+ // For signing with private keys
+ if (!request.params[2].isNull()) {
+ new_request.params.push_back(request.params[0]);
+ // Note: the prevtxs and privkeys are reversed for signrawtransactionwithkey
+ new_request.params.push_back(request.params[2]);
+ new_request.params.push_back(request.params[1]);
+ new_request.params.push_back(request.params[3]);
+ return signrawtransactionwithkey(new_request);
+ }
+ // Otherwise sign with the wallet which does not take a privkeys parameter
+#ifdef ENABLE_WALLET
+ else {
+ new_request.params.push_back(request.params[0]);
+ new_request.params.push_back(request.params[1]);
+ new_request.params.push_back(request.params[3]);
+ return signrawtransactionwithwallet(new_request);
+ }
+#endif
+ // If we have made it this far, then wallet is disabled and no private keys were given, so fail here.
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "No private keys available.");
+}
+
UniValue sendrawtransaction(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
@@ -981,12 +1065,12 @@ UniValue sendrawtransaction(const JSONRPCRequest& request)
if (!AcceptToMemoryPool(mempool, state, std::move(tx), &fMissingInputs,
nullptr /* plTxnReplaced */, false /* bypass_limits */, nMaxRawTxFee)) {
if (state.IsInvalid()) {
- throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason()));
+ throw JSONRPCError(RPC_TRANSACTION_REJECTED, FormatStateMessage(state));
} else {
if (fMissingInputs) {
throw JSONRPCError(RPC_TRANSACTION_ERROR, "Missing inputs");
}
- throw JSONRPCError(RPC_TRANSACTION_ERROR, state.GetRejectReason());
+ throw JSONRPCError(RPC_TRANSACTION_ERROR, FormatStateMessage(state));
}
} else {
// If wallet is enabled, ensure that the wallet has been made aware
@@ -1023,18 +1107,19 @@ UniValue sendrawtransaction(const JSONRPCRequest& request)
}
static const CRPCCommand commands[] =
-{ // category name actor (function) argNames
- // --------------------- ------------------------ ----------------------- ----------
- { "rawtransactions", "getrawtransaction", &getrawtransaction, {"txid","verbose","blockhash"} },
- { "rawtransactions", "createrawtransaction", &createrawtransaction, {"inputs","outputs","locktime","replaceable"} },
- { "rawtransactions", "decoderawtransaction", &decoderawtransaction, {"hexstring","iswitness"} },
- { "rawtransactions", "decodescript", &decodescript, {"hexstring"} },
- { "rawtransactions", "sendrawtransaction", &sendrawtransaction, {"hexstring","allowhighfees"} },
- { "rawtransactions", "combinerawtransaction", &combinerawtransaction, {"txs"} },
- { "rawtransactions", "signrawtransaction", &signrawtransaction, {"hexstring","prevtxs","privkeys","sighashtype"} }, /* uses wallet if enabled */
-
- { "blockchain", "gettxoutproof", &gettxoutproof, {"txids", "blockhash"} },
- { "blockchain", "verifytxoutproof", &verifytxoutproof, {"proof"} },
+{ // category name actor (function) argNames
+ // --------------------- ------------------------ ----------------------- ----------
+ { "rawtransactions", "getrawtransaction", &getrawtransaction, {"txid","verbose","blockhash"} },
+ { "rawtransactions", "createrawtransaction", &createrawtransaction, {"inputs","outputs","locktime","replaceable"} },
+ { "rawtransactions", "decoderawtransaction", &decoderawtransaction, {"hexstring","iswitness"} },
+ { "rawtransactions", "decodescript", &decodescript, {"hexstring"} },
+ { "rawtransactions", "sendrawtransaction", &sendrawtransaction, {"hexstring","allowhighfees"} },
+ { "rawtransactions", "combinerawtransaction", &combinerawtransaction, {"txs"} },
+ { "rawtransactions", "signrawtransaction", &signrawtransaction, {"hexstring","prevtxs","privkeys","sighashtype"} }, /* uses wallet if enabled */
+ { "rawtransactions", "signrawtransactionwithkey", &signrawtransactionwithkey, {"hexstring","privkeys","prevtxs","sighashtype"} },
+
+ { "blockchain", "gettxoutproof", &gettxoutproof, {"txids", "blockhash"} },
+ { "blockchain", "verifytxoutproof", &verifytxoutproof, {"proof"} },
};
void RegisterRawTransactionRPCCommands(CRPCTable &t)
diff --git a/src/rpc/rawtransaction.h b/src/rpc/rawtransaction.h
new file mode 100644
index 0000000000..ec9d1f2cf0
--- /dev/null
+++ b/src/rpc/rawtransaction.h
@@ -0,0 +1,15 @@
+// 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_RAWTRANSACTION_H
+#define BITCOIN_RPC_RAWTRANSACTION_H
+
+class CBasicKeyStore;
+struct CMutableTransaction;
+class UniValue;
+
+/** Sign a transaction with the given keystore and previous transactions */
+UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxs, CBasicKeyStore *keystore, bool tempKeystore, const UniValue& hashType);
+
+#endif // BITCOIN_RPC_RAWTRANSACTION_H
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
index 09ded4e46e..cdcb68d15f 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -66,3 +66,64 @@ CScript CreateMultisigRedeemscript(const int required, const std::vector<CPubKey
return result;
}
+
+class DescribeAddressVisitor : public boost::static_visitor<UniValue>
+{
+public:
+ explicit DescribeAddressVisitor() {}
+
+ UniValue operator()(const CNoDestination& dest) const
+ {
+ return UniValue(UniValue::VOBJ);
+ }
+
+ UniValue operator()(const CKeyID& keyID) const
+ {
+ UniValue obj(UniValue::VOBJ);
+ obj.pushKV("isscript", false);
+ obj.pushKV("iswitness", false);
+ return obj;
+ }
+
+ UniValue operator()(const CScriptID& scriptID) const
+ {
+ UniValue obj(UniValue::VOBJ);
+ obj.pushKV("isscript", true);
+ obj.pushKV("iswitness", false);
+ return obj;
+ }
+
+ UniValue operator()(const WitnessV0KeyHash& id) const
+ {
+ UniValue obj(UniValue::VOBJ);
+ obj.pushKV("isscript", false);
+ obj.pushKV("iswitness", true);
+ obj.pushKV("witness_version", 0);
+ obj.pushKV("witness_program", HexStr(id.begin(), id.end()));
+ return obj;
+ }
+
+ UniValue operator()(const WitnessV0ScriptHash& id) const
+ {
+ UniValue obj(UniValue::VOBJ);
+ obj.pushKV("isscript", true);
+ obj.pushKV("iswitness", true);
+ obj.pushKV("witness_version", 0);
+ obj.pushKV("witness_program", HexStr(id.begin(), id.end()));
+ return obj;
+ }
+
+ UniValue operator()(const WitnessUnknown& id) const
+ {
+ UniValue obj(UniValue::VOBJ);
+ obj.pushKV("iswitness", true);
+ obj.pushKV("witness_version", (int)id.version);
+ obj.pushKV("witness_program", HexStr(id.program, id.program + id.length));
+ return obj;
+ }
+};
+
+UniValue DescribeAddress(const CTxDestination& dest)
+{
+ return boost::apply_visitor(DescribeAddressVisitor(), dest);
+}
diff --git a/src/rpc/util.h b/src/rpc/util.h
index 568a4260ba..5380d45a83 100644
--- a/src/rpc/util.h
+++ b/src/rpc/util.h
@@ -5,6 +5,13 @@
#ifndef BITCOIN_RPC_UTIL_H
#define BITCOIN_RPC_UTIL_H
+#include <pubkey.h>
+#include <script/standard.h>
+#include <univalue.h>
+#include <utilstrencodings.h>
+
+#include <boost/variant/static_visitor.hpp>
+
#include <string>
#include <vector>
@@ -16,4 +23,6 @@ CPubKey HexToPubKey(const std::string& hex_in);
CPubKey AddrToPubKey(CKeyStore* const keystore, const std::string& addr_in);
CScript CreateMultisigRedeemscript(const int required, const std::vector<CPubKey>& pubkeys);
+UniValue DescribeAddress(const CTxDestination& dest);
+
#endif // BITCOIN_RPC_UTIL_H
diff --git a/src/test/checkqueue_tests.cpp b/src/test/checkqueue_tests.cpp
index 42f9dd0600..8e0ec5243b 100644
--- a/src/test/checkqueue_tests.cpp
+++ b/src/test/checkqueue_tests.cpp
@@ -406,11 +406,11 @@ BOOST_AUTO_TEST_CASE(test_CheckQueueControl_Locks)
boost::thread_group tg;
std::mutex m;
std::condition_variable cv;
+ bool has_lock{false};
+ bool has_tried{false};
+ bool done{false};
+ bool done_ack{false};
{
- bool has_lock {false};
- bool has_tried {false};
- bool done {false};
- bool done_ack {false};
std::unique_lock<std::mutex> l(m);
tg.create_thread([&]{
CCheckQueueControl<FakeCheck> control(queue.get());
diff --git a/src/test/data/script_tests.json b/src/test/data/script_tests.json
index 63f43c0fc6..ccefe52246 100644
--- a/src/test/data/script_tests.json
+++ b/src/test/data/script_tests.json
@@ -168,6 +168,18 @@
["1 0 BOOLOR", "NOP", "P2SH,STRICTENC", "OK"],
["0 1 BOOLOR", "NOP", "P2SH,STRICTENC", "OK"],
["0 0 BOOLOR", "NOT", "P2SH,STRICTENC", "OK"],
+["0x01 0x80", "DUP BOOLOR", "P2SH,STRICTENC", "EVAL_FALSE", "negative-0 negative-0 BOOLOR"],
+["0x01 0x00", "DUP BOOLOR", "P2SH,STRICTENC", "EVAL_FALSE", " non-minimal-0 non-minimal-0 BOOLOR"],
+["0x01 0x81", "DUP BOOLOR", "P2SH,STRICTENC", "OK", "-1 -1 BOOLOR"],
+["0x01 0x80", "DUP BOOLAND", "P2SH,STRICTENC", "EVAL_FALSE", "negative-0 negative-0 BOOLAND"],
+["0x01 0x00", "DUP BOOLAND", "P2SH,STRICTENC", "EVAL_FALSE", " non-minimal-0 non-minimal-0 BOOLAND"],
+["0x01 0x81", "DUP BOOLAND", "P2SH,STRICTENC", "OK", "-1 -1 BOOLAND"],
+["0x01 0x00", "NOT", "P2SH,STRICTENC", "OK", "non-minimal-0 NOT"],
+["0x01 0x80", "NOT", "P2SH,STRICTENC", "OK", "negative-0 NOT"],
+["0x01 0x81", "NOT", "P2SH,STRICTENC", "EVAL_FALSE", "negative 1 NOT"],
+["0x01 0x80 0", "NUMEQUAL", "P2SH", "OK", "-0 0 NUMEQUAL"],
+["0x01 0x00 0", "NUMEQUAL", "P2SH", "OK", "non-minimal-0 0 NUMEQUAL"],
+["0x02 0x00 0x00 0", "NUMEQUAL", "P2SH", "OK", "non-minimal-0 0 NUMEQUAL"],
["16 17 BOOLOR", "NOP", "P2SH,STRICTENC", "OK"],
["11 10 1 ADD", "NUMEQUAL", "P2SH,STRICTENC", "OK"],
["11 10 1 ADD", "NUMEQUALVERIFY 1", "P2SH,STRICTENC", "OK"],
diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp
index ed86413b2f..108c1a063e 100644
--- a/src/test/rpc_tests.cpp
+++ b/src/test/rpc_tests.cpp
@@ -69,14 +69,6 @@ BOOST_AUTO_TEST_CASE(rpc_rawparams)
BOOST_CHECK_NO_THROW(r = CallRPC(std::string("decoderawtransaction ")+rawtx+" false"));
BOOST_CHECK_THROW(r = CallRPC(std::string("decoderawtransaction ")+rawtx+" false extra"), std::runtime_error);
- BOOST_CHECK_THROW(CallRPC("signrawtransaction"), std::runtime_error);
- BOOST_CHECK_THROW(CallRPC("signrawtransaction null"), std::runtime_error);
- BOOST_CHECK_THROW(CallRPC("signrawtransaction ff00"), std::runtime_error);
- BOOST_CHECK_NO_THROW(CallRPC(std::string("signrawtransaction ")+rawtx));
- BOOST_CHECK_NO_THROW(CallRPC(std::string("signrawtransaction ")+rawtx+" null null NONE|ANYONECANPAY"));
- BOOST_CHECK_NO_THROW(CallRPC(std::string("signrawtransaction ")+rawtx+" [] [] NONE|ANYONECANPAY"));
- BOOST_CHECK_THROW(CallRPC(std::string("signrawtransaction ")+rawtx+" null null badenum"), std::runtime_error);
-
// Only check failure cases for sendrawtransaction, there's no network to send to...
BOOST_CHECK_THROW(CallRPC("sendrawtransaction"), std::runtime_error);
BOOST_CHECK_THROW(CallRPC("sendrawtransaction null"), std::runtime_error);
@@ -119,9 +111,9 @@ BOOST_AUTO_TEST_CASE(rpc_rawsign)
std::string notsigned = r.get_str();
std::string privkey1 = "\"KzsXybp9jX64P5ekX1KUxRQ79Jht9uzW7LorgwE65i5rWACL6LQe\"";
std::string privkey2 = "\"Kyhdf5LuKTRx4ge69ybABsiUAWjVRK4XGxAKk2FQLp2HjGMy87Z4\"";
- r = CallRPC(std::string("signrawtransaction ")+notsigned+" "+prevout+" "+"[]");
+ r = CallRPC(std::string("signrawtransactionwithkey ")+notsigned+" [] "+prevout);
BOOST_CHECK(find_value(r.get_obj(), "complete").get_bool() == false);
- r = CallRPC(std::string("signrawtransaction ")+notsigned+" "+prevout+" "+"["+privkey1+","+privkey2+"]");
+ r = CallRPC(std::string("signrawtransactionwithkey ")+notsigned+" ["+privkey1+","+privkey2+"] "+prevout);
BOOST_CHECK(find_value(r.get_obj(), "complete").get_bool() == true);
}
diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp
index bdd44489f4..95c4825b84 100644
--- a/src/test/test_bitcoin.cpp
+++ b/src/test/test_bitcoin.cpp
@@ -28,6 +28,9 @@ void CConnmanTest::AddNode(CNode& node)
void CConnmanTest::ClearNodes()
{
LOCK(g_connman->cs_vNodes);
+ for (CNode* node : g_connman->vNodes) {
+ delete node;
+ }
g_connman->vNodes.clear();
}
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index 55d60d95e9..35d3a60825 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -13,6 +13,11 @@
#include <stdint.h>
#include <vector>
+#ifndef WIN32
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#endif
#include <boost/test/unit_test.hpp>
@@ -77,6 +82,20 @@ BOOST_AUTO_TEST_CASE(util_HexStr)
"04 67 8a fd b0");
BOOST_CHECK_EQUAL(
+ HexStr(ParseHex_expected + sizeof(ParseHex_expected),
+ ParseHex_expected + sizeof(ParseHex_expected)),
+ "");
+
+ BOOST_CHECK_EQUAL(
+ HexStr(ParseHex_expected + sizeof(ParseHex_expected),
+ ParseHex_expected + sizeof(ParseHex_expected), true),
+ "");
+
+ BOOST_CHECK_EQUAL(
+ HexStr(ParseHex_expected, ParseHex_expected),
+ "");
+
+ BOOST_CHECK_EQUAL(
HexStr(ParseHex_expected, ParseHex_expected, true),
"");
@@ -85,6 +104,58 @@ BOOST_AUTO_TEST_CASE(util_HexStr)
BOOST_CHECK_EQUAL(
HexStr(ParseHex_vec, true),
"04 67 8a fd b0");
+
+ BOOST_CHECK_EQUAL(
+ HexStr(ParseHex_vec.rbegin(), ParseHex_vec.rend()),
+ "b0fd8a6704"
+ );
+
+ BOOST_CHECK_EQUAL(
+ HexStr(ParseHex_vec.rbegin(), ParseHex_vec.rend(), true),
+ "b0 fd 8a 67 04"
+ );
+
+ BOOST_CHECK_EQUAL(
+ HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected),
+ std::reverse_iterator<const uint8_t *>(ParseHex_expected)),
+ ""
+ );
+
+ BOOST_CHECK_EQUAL(
+ HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected),
+ std::reverse_iterator<const uint8_t *>(ParseHex_expected), true),
+ ""
+ );
+
+ BOOST_CHECK_EQUAL(
+ HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected + 1),
+ std::reverse_iterator<const uint8_t *>(ParseHex_expected)),
+ "04"
+ );
+
+ BOOST_CHECK_EQUAL(
+ HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected + 1),
+ std::reverse_iterator<const uint8_t *>(ParseHex_expected), true),
+ "04"
+ );
+
+ BOOST_CHECK_EQUAL(
+ HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected + 5),
+ std::reverse_iterator<const uint8_t *>(ParseHex_expected)),
+ "b0fd8a6704"
+ );
+
+ BOOST_CHECK_EQUAL(
+ HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected + 5),
+ std::reverse_iterator<const uint8_t *>(ParseHex_expected), true),
+ "b0 fd 8a 67 04"
+ );
+
+ BOOST_CHECK_EQUAL(
+ HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected + 65),
+ std::reverse_iterator<const uint8_t *>(ParseHex_expected)),
+ "5f1df16b2b704c8a578d0bbaf74d385cde12c11ee50455f3c438ef4c3fbcf649b6de611feae06279a60939e028a8d65c10b73071a6f16719274855feb0fd8a6704"
+ );
}
@@ -603,4 +674,130 @@ BOOST_AUTO_TEST_CASE(test_ParseFixedPoint)
BOOST_CHECK(!ParseFixedPoint("1.", 8, &amount));
}
+static void TestOtherThread(fs::path dirname, std::string lockname, bool *result)
+{
+ *result = LockDirectory(dirname, lockname);
+}
+
+#ifndef WIN32 // Cannot do this test on WIN32 due to lack of fork()
+static constexpr char LockCommand = 'L';
+static constexpr char UnlockCommand = 'U';
+static constexpr char ExitCommand = 'X';
+
+static void TestOtherProcess(fs::path dirname, std::string lockname, int fd)
+{
+ char ch;
+ int rv;
+ while (true) {
+ rv = read(fd, &ch, 1); // Wait for command
+ assert(rv == 1);
+ switch(ch) {
+ case LockCommand:
+ ch = LockDirectory(dirname, lockname);
+ rv = write(fd, &ch, 1);
+ assert(rv == 1);
+ break;
+ case UnlockCommand:
+ ReleaseDirectoryLocks();
+ ch = true; // Always succeeds
+ rv = write(fd, &ch, 1);
+ break;
+ case ExitCommand:
+ close(fd);
+ exit(0);
+ default:
+ assert(0);
+ }
+ }
+}
+#endif
+
+BOOST_AUTO_TEST_CASE(test_LockDirectory)
+{
+ fs::path dirname = fs::temp_directory_path() / fs::unique_path();
+ const std::string lockname = ".lock";
+#ifndef WIN32
+ // Revert SIGCHLD to default, otherwise boost.test will catch and fail on
+ // it: there is BOOST_TEST_IGNORE_SIGCHLD but that only works when defined
+ // at build-time of the boost library
+ void (*old_handler)(int) = signal(SIGCHLD, SIG_DFL);
+
+ // Fork another process for testing before creating the lock, so that we
+ // won't fork while holding the lock (which might be undefined, and is not
+ // relevant as test case as that is avoided with -daemonize).
+ int fd[2];
+ BOOST_CHECK_EQUAL(socketpair(AF_UNIX, SOCK_STREAM, 0, fd), 0);
+ pid_t pid = fork();
+ if (!pid) {
+ BOOST_CHECK_EQUAL(close(fd[1]), 0); // Child: close parent end
+ TestOtherProcess(dirname, lockname, fd[0]);
+ }
+ BOOST_CHECK_EQUAL(close(fd[0]), 0); // Parent: close child end
+#endif
+ // Lock on non-existent directory should fail
+ BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname), false);
+
+ fs::create_directories(dirname);
+
+ // Probing lock on new directory should succeed
+ BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname, true), true);
+
+ // Persistent lock on new directory should succeed
+ BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname), true);
+
+ // Another lock on the directory from the same thread should succeed
+ BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname), true);
+
+ // Another lock on the directory from a different thread within the same process should succeed
+ bool threadresult;
+ std::thread thr(TestOtherThread, dirname, lockname, &threadresult);
+ thr.join();
+ BOOST_CHECK_EQUAL(threadresult, true);
+#ifndef WIN32
+ // Try to aquire lock in child process while we're holding it, this should fail.
+ char ch;
+ BOOST_CHECK_EQUAL(write(fd[1], &LockCommand, 1), 1);
+ BOOST_CHECK_EQUAL(read(fd[1], &ch, 1), 1);
+ BOOST_CHECK_EQUAL((bool)ch, false);
+
+ // Give up our lock
+ ReleaseDirectoryLocks();
+ // Probing lock from our side now should succeed, but not hold on to the lock.
+ BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname, true), true);
+
+ // Try to acquire the lock in the child process, this should be succesful.
+ BOOST_CHECK_EQUAL(write(fd[1], &LockCommand, 1), 1);
+ BOOST_CHECK_EQUAL(read(fd[1], &ch, 1), 1);
+ BOOST_CHECK_EQUAL((bool)ch, true);
+
+ // When we try to probe the lock now, it should fail.
+ BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname, true), false);
+
+ // Unlock the lock in the child process
+ BOOST_CHECK_EQUAL(write(fd[1], &UnlockCommand, 1), 1);
+ BOOST_CHECK_EQUAL(read(fd[1], &ch, 1), 1);
+ BOOST_CHECK_EQUAL((bool)ch, true);
+
+ // When we try to probe the lock now, it should succeed.
+ BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname, true), true);
+
+ // Re-lock the lock in the child process, then wait for it to exit, check
+ // successful return. After that, we check that exiting the process
+ // has released the lock as we would expect by probing it.
+ int processstatus;
+ BOOST_CHECK_EQUAL(write(fd[1], &LockCommand, 1), 1);
+ BOOST_CHECK_EQUAL(write(fd[1], &ExitCommand, 1), 1);
+ BOOST_CHECK_EQUAL(waitpid(pid, &processstatus, 0), pid);
+ BOOST_CHECK_EQUAL(processstatus, 0);
+ BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname, true), true);
+
+ // Restore SIGCHLD
+ signal(SIGCHLD, old_handler);
+ BOOST_CHECK_EQUAL(close(fd[1]), 0); // Close our side of the socketpair
+#endif
+ // Clean up
+ ReleaseDirectoryLocks();
+ fs::remove_all(dirname);
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/util.cpp b/src/util.cpp
index 6738bbc6e4..dcf7ed38b1 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -373,20 +373,37 @@ int LogPrintStr(const std::string &str)
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
+ * is called.
+ */
+static std::map<std::string, std::unique_ptr<boost::interprocess::file_lock>> dir_locks;
+/** Mutex to protect dir_locks. */
+static std::mutex cs_dir_locks;
+
bool LockDirectory(const fs::path& directory, const std::string lockfile_name, bool probe_only)
{
+ std::lock_guard<std::mutex> ulock(cs_dir_locks);
fs::path pathLockFile = directory / lockfile_name;
- FILE* file = fsbridge::fopen(pathLockFile, "a"); // empty lock file; created if it doesn't exist.
+
+ // If a lock for this directory already exists in the map, don't try to re-lock it
+ if (dir_locks.count(pathLockFile.string())) {
+ return true;
+ }
+
+ // Create empty lock file if it doesn't exist.
+ FILE* file = fsbridge::fopen(pathLockFile, "a");
if (file) fclose(file);
try {
- static std::map<std::string, boost::interprocess::file_lock> locks;
- boost::interprocess::file_lock& lock = locks.emplace(pathLockFile.string(), pathLockFile.string().c_str()).first->second;
- if (!lock.try_lock()) {
+ auto lock = MakeUnique<boost::interprocess::file_lock>(pathLockFile.string().c_str());
+ if (!lock->try_lock()) {
return false;
}
- if (probe_only) {
- lock.unlock();
+ if (!probe_only) {
+ // Lock successful and we're not just probing, put it into the map
+ dir_locks.emplace(pathLockFile.string(), std::move(lock));
}
} catch (const boost::interprocess::interprocess_exception& e) {
return error("Error while attempting to lock directory %s: %s", directory.string(), e.what());
@@ -394,6 +411,12 @@ bool LockDirectory(const fs::path& directory, const std::string lockfile_name, b
return true;
}
+void ReleaseDirectoryLocks()
+{
+ std::lock_guard<std::mutex> ulock(cs_dir_locks);
+ dir_locks.clear();
+}
+
/** Interpret string as boolean, for argument parsing */
static bool InterpretBool(const std::string& strValue)
{
diff --git a/src/util.h b/src/util.h
index 05138a9bfe..9490a5678f 100644
--- a/src/util.h
+++ b/src/util.h
@@ -174,6 +174,12 @@ int RaiseFileDescriptorLimit(int nMinFD);
void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length);
bool RenameOver(fs::path src, fs::path dest);
bool LockDirectory(const fs::path& directory, const std::string lockfile_name, bool probe_only=false);
+
+/** Release all directory locks. This is used for unit testing only, at runtime
+ * the global destructor will take care of the locks.
+ */
+void ReleaseDirectoryLocks();
+
bool TryCreateDirectories(const fs::path& p);
fs::path GetDefaultDataDir();
const fs::path &GetDataDir(bool fNetSpecific = true);
diff --git a/src/validation.cpp b/src/validation.cpp
index 371460a6f0..d2438b0609 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -712,7 +712,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
CAmount mempoolRejectFee = pool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nSize);
if (!bypass_limits && mempoolRejectFee > 0 && nModifiedFees < mempoolRejectFee) {
- return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", false, strprintf("%d < %d", nFees, mempoolRejectFee));
+ return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", false, strprintf("%d < %d", nModifiedFees, mempoolRejectFee));
}
// No transactions are allowed below minRelayTxFee except from disconnected blocks
@@ -2087,7 +2087,7 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &
nLastWrite = nNow;
}
// Flush best chain related state. This can only be done if the blocks / block index write was also done.
- if (fDoFullFlush) {
+ if (fDoFullFlush && !pcoinsTip->GetBestBlock().IsNull()) {
// Typical Coin structures on disk are around 48 bytes in size.
// Pushing a new one to the database can cause it to be written
// twice (once in the log, and once in the tables). This is already
@@ -4658,6 +4658,7 @@ bool DumpMempool(void)
}
//! Guess how far we are in the verification process at the given block index
+//! require cs_main if pindex has not been validated yet (because nChainTx might be unset)
double GuessVerificationProgress(const ChainTxData& data, const CBlockIndex *pindex) {
if (pindex == nullptr)
return 0.0;
diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp
index 5234a69710..9cae660c60 100644
--- a/src/wallet/feebumper.cpp
+++ b/src/wallet/feebumper.cpp
@@ -274,7 +274,7 @@ Result CommitTransaction(CWallet* wallet, const uint256& txid, CMutableTransacti
CValidationState state;
if (!wallet->CommitTransaction(wtxBumped, reservekey, g_connman.get(), state)) {
// NOTE: CommitTransaction never returns false, so this should never happen.
- errors.push_back(strprintf("The transaction was rejected: %s", state.GetRejectReason()));
+ errors.push_back(strprintf("The transaction was rejected: %s", FormatStateMessage(state)));
return Result::WALLET_ERROR;
}
@@ -297,4 +297,3 @@ Result CommitTransaction(CWallet* wallet, const uint256& txid, CMutableTransacti
}
} // namespace feebumper
-
diff --git a/src/wallet/fees.cpp b/src/wallet/fees.cpp
index 73985dcf25..385fdc963a 100644
--- a/src/wallet/fees.cpp
+++ b/src/wallet/fees.cpp
@@ -53,6 +53,9 @@ CAmount GetMinimumFee(unsigned int nTxBytes, const CCoinControl& coin_control, c
// if we don't have enough data for estimateSmartFee, then use fallbackFee
fee_needed = CWallet::fallbackFee.GetFee(nTxBytes);
if (feeCalc) feeCalc->reason = FeeReason::FALLBACK;
+
+ // directly return if fallback fee is disabled (feerate 0 == disabled)
+ if (CWallet::fallbackFee.GetFee(1000) == 0) return fee_needed;
}
// Obey mempool min fee when using smart fee estimation
CAmount min_mempool_fee = pool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nTxBytes);
diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp
index 74036f4f0f..9ac48bff77 100644
--- a/src/wallet/init.cpp
+++ b/src/wallet/init.cpp
@@ -5,6 +5,7 @@
#include <wallet/init.h>
+#include <chainparams.h>
#include <net.h>
#include <util.h>
#include <utilmoneystr.h>
@@ -123,6 +124,8 @@ bool WalletParameterInteraction()
_("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;
@@ -132,6 +135,7 @@ bool WalletParameterInteraction()
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"))
{
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index b466cf1a81..8b95c56a5f 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -16,6 +16,7 @@
#include <policy/policy.h>
#include <policy/rbf.h>
#include <rpc/mining.h>
+#include <rpc/rawtransaction.h>
#include <rpc/safemode.h>
#include <rpc/server.h>
#include <rpc/util.h>
@@ -25,6 +26,7 @@
#include <utilmoneystr.h>
#include <wallet/coincontrol.h>
#include <wallet/feebumper.h>
+#include <wallet/rpcwallet.h>
#include <wallet/wallet.h>
#include <wallet/walletdb.h>
#include <wallet/walletutil.h>
@@ -435,7 +437,7 @@ static void SendMoney(CWallet * const pwallet, const CTxDestination &address, CA
}
CValidationState state;
if (!pwallet->CommitTransaction(wtxNew, reservekey, g_connman.get(), state)) {
- strError = strprintf("Error: The transaction was rejected! Reason given: %s", state.GetRejectReason());
+ strError = strprintf("Error: The transaction was rejected! Reason given: %s", FormatStateMessage(state));
throw JSONRPCError(RPC_WALLET_ERROR, strError);
}
}
@@ -1155,7 +1157,7 @@ UniValue sendmany(const JSONRPCRequest& request)
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason);
CValidationState state;
if (!pwallet->CommitTransaction(wtx, keyChange, g_connman.get(), state)) {
- strFailReason = strprintf("Transaction commit failed:: %s", state.GetRejectReason());
+ strFailReason = strprintf("Transaction commit failed:: %s", FormatStateMessage(state));
throw JSONRPCError(RPC_WALLET_ERROR, strFailReason);
}
@@ -3129,7 +3131,6 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
{"change_type", UniValueType(UniValue::VSTR)},
{"includeWatching", UniValueType(UniValue::VBOOL)},
{"lockUnspents", UniValueType(UniValue::VBOOL)},
- {"reserveChangeKey", UniValueType(UniValue::VBOOL)}, // DEPRECATED (and ignored), should be removed in 0.16 or so.
{"feeRate", UniValueType()}, // will be checked below
{"subtractFeeFromOutputs", UniValueType(UniValue::VARR)},
{"replaceable", UniValueType(UniValue::VBOOL)},
@@ -3236,6 +3237,75 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
return result;
}
+UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
+{
+ CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
+ return NullUniValue;
+ }
+
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
+ throw std::runtime_error(
+ "signrawtransactionwithwallet \"hexstring\" ( [{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\",\"redeemScript\":\"hex\"},...] sighashtype )\n"
+ "\nSign inputs for raw transaction (serialized, hex-encoded).\n"
+ "The second optional argument (may be null) is an array of previous transaction outputs that\n"
+ "this transaction depends on but may not yet be in the block chain.\n"
+ + HelpRequiringPassphrase(pwallet) + "\n"
+
+ "\nArguments:\n"
+ "1. \"hexstring\" (string, required) The transaction hex string\n"
+ "2. \"prevtxs\" (string, optional) An json array of previous dependent transaction outputs\n"
+ " [ (json array of json objects, or 'null' if none provided)\n"
+ " {\n"
+ " \"txid\":\"id\", (string, required) The transaction id\n"
+ " \"vout\":n, (numeric, required) The output number\n"
+ " \"scriptPubKey\": \"hex\", (string, required) script key\n"
+ " \"redeemScript\": \"hex\", (string, required for P2SH or P2WSH) redeem script\n"
+ " \"amount\": value (numeric, required) The amount spent\n"
+ " }\n"
+ " ,...\n"
+ " ]\n"
+ "3. \"sighashtype\" (string, optional, default=ALL) The signature hash type. Must be one of\n"
+ " \"ALL\"\n"
+ " \"NONE\"\n"
+ " \"SINGLE\"\n"
+ " \"ALL|ANYONECANPAY\"\n"
+ " \"NONE|ANYONECANPAY\"\n"
+ " \"SINGLE|ANYONECANPAY\"\n"
+
+ "\nResult:\n"
+ "{\n"
+ " \"hex\" : \"value\", (string) The hex-encoded raw transaction with signature(s)\n"
+ " \"complete\" : true|false, (boolean) If the transaction has a complete set of signatures\n"
+ " \"errors\" : [ (json array of objects) Script verification errors (if there are any)\n"
+ " {\n"
+ " \"txid\" : \"hash\", (string) The hash of the referenced, previous transaction\n"
+ " \"vout\" : n, (numeric) The index of the output to spent and used as input\n"
+ " \"scriptSig\" : \"hex\", (string) The hex-encoded signature script\n"
+ " \"sequence\" : n, (numeric) Script sequence number\n"
+ " \"error\" : \"text\" (string) Verification or signing error related to the input\n"
+ " }\n"
+ " ,...\n"
+ " ]\n"
+ "}\n"
+
+ "\nExamples:\n"
+ + HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"")
+ + HelpExampleRpc("signrawtransactionwithwallet", "\"myhex\"")
+ );
+
+ RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR, UniValue::VSTR}, true);
+
+ CMutableTransaction mtx;
+ if (!DecodeHexTx(mtx, request.params[0].get_str(), true)) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
+ }
+
+ // Sign the transaction
+ LOCK2(cs_main, pwallet->cs_wallet);
+ return SignTransaction(mtx, request.params[1], pwallet, false, request.params[2]);
+}
+
UniValue bumpfee(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
@@ -3515,6 +3585,209 @@ UniValue rescanblockchain(const JSONRPCRequest& request)
return response;
}
+class DescribeWalletAddressVisitor : public boost::static_visitor<UniValue>
+{
+public:
+ CWallet * const pwallet;
+
+ void ProcessSubScript(const CScript& subscript, UniValue& obj, bool include_addresses = false) const
+ {
+ // Always present: script type and redeemscript
+ txnouttype which_type;
+ std::vector<std::vector<unsigned char>> solutions_data;
+ Solver(subscript, which_type, solutions_data);
+ obj.pushKV("script", GetTxnOutputType(which_type));
+ obj.pushKV("hex", HexStr(subscript.begin(), subscript.end()));
+
+ CTxDestination embedded;
+ UniValue a(UniValue::VARR);
+ if (ExtractDestination(subscript, embedded)) {
+ // Only when the script corresponds to an address.
+ UniValue subobj(UniValue::VOBJ);
+ UniValue detail = DescribeAddress(embedded);
+ subobj.pushKVs(detail);
+ UniValue wallet_detail = boost::apply_visitor(*this, embedded);
+ subobj.pushKVs(wallet_detail);
+ subobj.pushKV("address", EncodeDestination(embedded));
+ subobj.pushKV("scriptPubKey", HexStr(subscript.begin(), subscript.end()));
+ // Always report the pubkey at the top level, so that `getnewaddress()['pubkey']` always works.
+ if (subobj.exists("pubkey")) obj.pushKV("pubkey", subobj["pubkey"]);
+ obj.pushKV("embedded", std::move(subobj));
+ if (include_addresses) a.push_back(EncodeDestination(embedded));
+ } else if (which_type == TX_MULTISIG) {
+ // Also report some information on multisig scripts (which do not have a corresponding address).
+ // TODO: abstract out the common functionality between this logic and ExtractDestinations.
+ obj.pushKV("sigsrequired", solutions_data[0][0]);
+ UniValue pubkeys(UniValue::VARR);
+ for (size_t i = 1; i < solutions_data.size() - 1; ++i) {
+ CPubKey key(solutions_data[i].begin(), solutions_data[i].end());
+ if (include_addresses) a.push_back(EncodeDestination(key.GetID()));
+ pubkeys.push_back(HexStr(key.begin(), key.end()));
+ }
+ obj.pushKV("pubkeys", std::move(pubkeys));
+ }
+
+ // The "addresses" field is confusing because it refers to public keys using their P2PKH address.
+ // For that reason, only add the 'addresses' field when needed for backward compatibility. New applications
+ // can use the 'embedded'->'address' field for P2SH or P2WSH wrapped addresses, and 'pubkeys' for
+ // inspecting multisig participants.
+ if (include_addresses) obj.pushKV("addresses", std::move(a));
+ }
+
+ explicit DescribeWalletAddressVisitor(CWallet* _pwallet) : pwallet(_pwallet) {}
+
+ UniValue operator()(const CNoDestination& dest) const { return UniValue(UniValue::VOBJ); }
+
+ UniValue operator()(const CKeyID& keyID) const
+ {
+ UniValue obj(UniValue::VOBJ);
+ CPubKey vchPubKey;
+ if (pwallet && pwallet->GetPubKey(keyID, vchPubKey)) {
+ obj.pushKV("pubkey", HexStr(vchPubKey));
+ obj.pushKV("iscompressed", vchPubKey.IsCompressed());
+ }
+ return obj;
+ }
+
+ UniValue operator()(const CScriptID& scriptID) const
+ {
+ UniValue obj(UniValue::VOBJ);
+ CScript subscript;
+ if (pwallet && pwallet->GetCScript(scriptID, subscript)) {
+ ProcessSubScript(subscript, obj, IsDeprecatedRPCEnabled("validateaddress"));
+ }
+ return obj;
+ }
+
+ UniValue operator()(const WitnessV0KeyHash& id) const
+ {
+ UniValue obj(UniValue::VOBJ);
+ CPubKey pubkey;
+ if (pwallet && pwallet->GetPubKey(CKeyID(id), pubkey)) {
+ obj.pushKV("pubkey", HexStr(pubkey));
+ }
+ return obj;
+ }
+
+ UniValue operator()(const WitnessV0ScriptHash& id) const
+ {
+ UniValue obj(UniValue::VOBJ);
+ CScript subscript;
+ CRIPEMD160 hasher;
+ uint160 hash;
+ hasher.Write(id.begin(), 32).Finalize(hash.begin());
+ if (pwallet && pwallet->GetCScript(CScriptID(hash), subscript)) {
+ ProcessSubScript(subscript, obj);
+ }
+ return obj;
+ }
+
+ UniValue operator()(const WitnessUnknown& id) const { return UniValue(UniValue::VOBJ); }
+};
+
+UniValue DescribeWalletAddress(CWallet* pwallet, const CTxDestination& dest)
+{
+ UniValue ret(UniValue::VOBJ);
+ UniValue detail = DescribeAddress(dest);
+ ret.pushKVs(detail);
+ ret.pushKVs(boost::apply_visitor(DescribeWalletAddressVisitor(pwallet), dest));
+ return ret;
+}
+
+UniValue getaddressinfo(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(
+ "getaddressinfo \"address\"\n"
+ "\nReturn information about the given bitcoin address. Some information requires the address\n"
+ "to be in the wallet.\n"
+ "\nArguments:\n"
+ "1. \"address\" (string, required) The bitcoin address to get the information of.\n"
+ "\nResult:\n"
+ "{\n"
+ " \"address\" : \"address\", (string) The bitcoin address validated\n"
+ " \"scriptPubKey\" : \"hex\", (string) The hex encoded scriptPubKey generated by the address\n"
+ " \"ismine\" : true|false, (boolean) If the address is yours or not\n"
+ " \"iswatchonly\" : true|false, (boolean) If the address is watchonly\n"
+ " \"isscript\" : true|false, (boolean) If the key is a script\n"
+ " \"iswitness\" : true|false, (boolean) If the address is a witness address\n"
+ " \"witness_version\" : version (numeric, optional) The version number of the witness program\n"
+ " \"witness_program\" : \"hex\" (string, optional) The hex value of the witness program\n"
+ " \"script\" : \"type\" (string, optional) The output script type. Only if \"isscript\" is true and the redeemscript is known. Possible types: nonstandard, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_keyhash, witness_v0_scripthash, witness_unknown\n"
+ " \"hex\" : \"hex\", (string, optional) The redeemscript for the p2sh address\n"
+ " \"pubkeys\" (string, optional) Array of pubkeys associated with the known redeemscript (only if \"script\" is \"multisig\")\n"
+ " [\n"
+ " \"pubkey\"\n"
+ " ,...\n"
+ " ]\n"
+ " \"sigsrequired\" : xxxxx (numeric, optional) Number of signatures required to spend multisig output (only if \"script\" is \"multisig\")\n"
+ " \"pubkey\" : \"publickeyhex\", (string, optional) The hex value of the raw public key, for single-key addresses (possibly embedded in P2SH or P2WSH)\n"
+ " \"embedded\" : {...}, (object, optional) Information about the address embedded in P2SH or P2WSH, if relevant and known. It includes all getaddressinfo output fields for the embedded address, excluding metadata (\"timestamp\", \"hdkeypath\", \"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"
+ " \"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"
+ "}\n"
+ "\nExamples:\n"
+ + HelpExampleCli("getaddressinfo", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"")
+ + HelpExampleRpc("getaddressinfo", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"")
+ );
+ }
+
+ LOCK(pwallet->cs_wallet);
+
+ UniValue ret(UniValue::VOBJ);
+ CTxDestination dest = DecodeDestination(request.params[0].get_str());
+
+ // Make sure the destination is valid
+ if (!IsValidDestination(dest)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
+ }
+
+ std::string currentAddress = EncodeDestination(dest);
+ ret.pushKV("address", currentAddress);
+
+ CScript scriptPubKey = GetScriptForDestination(dest);
+ ret.pushKV("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()));
+
+ isminetype mine = IsMine(*pwallet, dest);
+ ret.pushKV("ismine", bool(mine & ISMINE_SPENDABLE));
+ ret.pushKV("iswatchonly", bool(mine & ISMINE_WATCH_ONLY));
+ UniValue detail = DescribeWalletAddress(pwallet, dest);
+ ret.pushKVs(detail);
+ if (pwallet->mapAddressBook.count(dest)) {
+ ret.pushKV("account", pwallet->mapAddressBook[dest].name);
+ }
+ const CKeyMetadata* meta = nullptr;
+ CKeyID key_id = GetKeyForDestination(*pwallet, dest);
+ if (!key_id.IsNull()) {
+ auto it = pwallet->mapKeyMetadata.find(key_id);
+ if (it != pwallet->mapKeyMetadata.end()) {
+ meta = &it->second;
+ }
+ }
+ if (!meta) {
+ auto it = pwallet->m_script_metadata.find(CScriptID(scriptPubKey));
+ if (it != pwallet->m_script_metadata.end()) {
+ meta = &it->second;
+ }
+ }
+ if (meta) {
+ ret.pushKV("timestamp", meta->nCreateTime);
+ if (!meta->hdKeypath.empty()) {
+ ret.pushKV("hdkeypath", meta->hdKeypath);
+ ret.pushKV("hdmasterkeyid", meta->hdMasterKeyID.GetHex());
+ }
+ }
+ return ret;
+}
+
extern UniValue abortrescan(const JSONRPCRequest& request); // in rpcdump.cpp
extern UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp
extern UniValue importprivkey(const JSONRPCRequest& request);
@@ -3528,61 +3801,63 @@ extern UniValue importmulti(const JSONRPCRequest& request);
extern UniValue rescanblockchain(const JSONRPCRequest& request);
static const CRPCCommand commands[] =
-{ // category name actor (function) argNames
- // --------------------- ------------------------ ----------------------- ----------
- { "rawtransactions", "fundrawtransaction", &fundrawtransaction, {"hexstring","options","iswitness"} },
- { "hidden", "resendwallettransactions", &resendwallettransactions, {} },
- { "wallet", "abandontransaction", &abandontransaction, {"txid"} },
- { "wallet", "abortrescan", &abortrescan, {} },
- { "wallet", "addmultisigaddress", &addmultisigaddress, {"nrequired","keys","account","address_type"} },
- { "hidden", "addwitnessaddress", &addwitnessaddress, {"address","p2sh"} },
- { "wallet", "backupwallet", &backupwallet, {"destination"} },
- { "wallet", "bumpfee", &bumpfee, {"txid", "options"} },
- { "wallet", "dumpprivkey", &dumpprivkey, {"address"} },
- { "wallet", "dumpwallet", &dumpwallet, {"filename"} },
- { "wallet", "encryptwallet", &encryptwallet, {"passphrase"} },
- { "wallet", "getaccountaddress", &getaccountaddress, {"account"} },
- { "wallet", "getaccount", &getaccount, {"address"} },
- { "wallet", "getaddressesbyaccount", &getaddressesbyaccount, {"account"} },
- { "wallet", "getbalance", &getbalance, {"account","minconf","include_watchonly"} },
- { "wallet", "getnewaddress", &getnewaddress, {"account","address_type"} },
- { "wallet", "getrawchangeaddress", &getrawchangeaddress, {"address_type"} },
- { "wallet", "getreceivedbyaccount", &getreceivedbyaccount, {"account","minconf"} },
- { "wallet", "getreceivedbyaddress", &getreceivedbyaddress, {"address","minconf"} },
- { "wallet", "gettransaction", &gettransaction, {"txid","include_watchonly"} },
- { "wallet", "getunconfirmedbalance", &getunconfirmedbalance, {} },
- { "wallet", "getwalletinfo", &getwalletinfo, {} },
- { "wallet", "importmulti", &importmulti, {"requests","options"} },
- { "wallet", "importprivkey", &importprivkey, {"privkey","label","rescan"} },
- { "wallet", "importwallet", &importwallet, {"filename"} },
- { "wallet", "importaddress", &importaddress, {"address","label","rescan","p2sh"} },
- { "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", "listreceivedbyaccount", &listreceivedbyaccount, {"minconf","include_empty","include_watchonly"} },
- { "wallet", "listreceivedbyaddress", &listreceivedbyaddress, {"minconf","include_empty","include_watchonly"} },
- { "wallet", "listsinceblock", &listsinceblock, {"blockhash","target_confirmations","include_watchonly","include_removed"} },
- { "wallet", "listtransactions", &listtransactions, {"account","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", "sendtoaddress", &sendtoaddress, {"address","amount","comment","comment_to","subtractfeefromamount","replaceable","conf_target","estimate_mode"} },
- { "wallet", "setaccount", &setaccount, {"address","account"} },
- { "wallet", "settxfee", &settxfee, {"amount"} },
- { "wallet", "signmessage", &signmessage, {"address","message"} },
- { "wallet", "walletlock", &walletlock, {} },
- { "wallet", "walletpassphrasechange", &walletpassphrasechange, {"oldpassphrase","newpassphrase"} },
- { "wallet", "walletpassphrase", &walletpassphrase, {"passphrase","timeout"} },
- { "wallet", "removeprunedfunds", &removeprunedfunds, {"txid"} },
- { "wallet", "rescanblockchain", &rescanblockchain, {"start_height", "stop_height"} },
-
- { "generating", "generate", &generate, {"nblocks","maxtries"} },
+{ // category name actor (function) argNames
+ // --------------------- ------------------------ ----------------------- ----------
+ { "rawtransactions", "fundrawtransaction", &fundrawtransaction, {"hexstring","options","iswitness"} },
+ { "hidden", "resendwallettransactions", &resendwallettransactions, {} },
+ { "wallet", "abandontransaction", &abandontransaction, {"txid"} },
+ { "wallet", "abortrescan", &abortrescan, {} },
+ { "wallet", "addmultisigaddress", &addmultisigaddress, {"nrequired","keys","account","address_type"} },
+ { "hidden", "addwitnessaddress", &addwitnessaddress, {"address","p2sh"} },
+ { "wallet", "backupwallet", &backupwallet, {"destination"} },
+ { "wallet", "bumpfee", &bumpfee, {"txid", "options"} },
+ { "wallet", "dumpprivkey", &dumpprivkey, {"address"} },
+ { "wallet", "dumpwallet", &dumpwallet, {"filename"} },
+ { "wallet", "encryptwallet", &encryptwallet, {"passphrase"} },
+ { "wallet", "getaccountaddress", &getaccountaddress, {"account"} },
+ { "wallet", "getaccount", &getaccount, {"address"} },
+ { "wallet", "getaddressesbyaccount", &getaddressesbyaccount, {"account"} },
+ { "wallet", "getaddressinfo", &getaddressinfo, {"address"} },
+ { "wallet", "getbalance", &getbalance, {"account","minconf","include_watchonly"} },
+ { "wallet", "getnewaddress", &getnewaddress, {"account","address_type"} },
+ { "wallet", "getrawchangeaddress", &getrawchangeaddress, {"address_type"} },
+ { "wallet", "getreceivedbyaccount", &getreceivedbyaccount, {"account","minconf"} },
+ { "wallet", "getreceivedbyaddress", &getreceivedbyaddress, {"address","minconf"} },
+ { "wallet", "gettransaction", &gettransaction, {"txid","include_watchonly"} },
+ { "wallet", "getunconfirmedbalance", &getunconfirmedbalance, {} },
+ { "wallet", "getwalletinfo", &getwalletinfo, {} },
+ { "wallet", "importmulti", &importmulti, {"requests","options"} },
+ { "wallet", "importprivkey", &importprivkey, {"privkey","label","rescan"} },
+ { "wallet", "importwallet", &importwallet, {"filename"} },
+ { "wallet", "importaddress", &importaddress, {"address","label","rescan","p2sh"} },
+ { "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", "listreceivedbyaccount", &listreceivedbyaccount, {"minconf","include_empty","include_watchonly"} },
+ { "wallet", "listreceivedbyaddress", &listreceivedbyaddress, {"minconf","include_empty","include_watchonly"} },
+ { "wallet", "listsinceblock", &listsinceblock, {"blockhash","target_confirmations","include_watchonly","include_removed"} },
+ { "wallet", "listtransactions", &listtransactions, {"account","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", "sendtoaddress", &sendtoaddress, {"address","amount","comment","comment_to","subtractfeefromamount","replaceable","conf_target","estimate_mode"} },
+ { "wallet", "setaccount", &setaccount, {"address","account"} },
+ { "wallet", "settxfee", &settxfee, {"amount"} },
+ { "wallet", "signmessage", &signmessage, {"address","message"} },
+ { "wallet", "signrawtransactionwithwallet", &signrawtransactionwithwallet, {"hexstring","prevtxs","sighashtype"} },
+ { "wallet", "walletlock", &walletlock, {} },
+ { "wallet", "walletpassphrasechange", &walletpassphrasechange, {"oldpassphrase","newpassphrase"} },
+ { "wallet", "walletpassphrase", &walletpassphrase, {"passphrase","timeout"} },
+ { "wallet", "removeprunedfunds", &removeprunedfunds, {"txid"} },
+ { "wallet", "rescanblockchain", &rescanblockchain, {"start_height", "stop_height"} },
+
+ { "generating", "generate", &generate, {"nblocks","maxtries"} },
};
void RegisterWalletRPCCommands(CRPCTable &t)
diff --git a/src/wallet/rpcwallet.h b/src/wallet/rpcwallet.h
index 77f7b42b23..84f161abb5 100644
--- a/src/wallet/rpcwallet.h
+++ b/src/wallet/rpcwallet.h
@@ -10,6 +10,7 @@
class CRPCTable;
class CWallet;
class JSONRPCRequest;
+class UniValue;
void RegisterWalletRPCCommands(CRPCTable &t);
@@ -25,4 +26,6 @@ std::string HelpRequiringPassphrase(CWallet *);
void EnsureWalletIsUnlocked(CWallet *);
bool EnsureWalletIsAvailable(CWallet *, bool avoidException);
+UniValue getaddressinfo(const JSONRPCRequest& request);
+UniValue signrawtransactionwithwallet(const JSONRPCRequest& request);
#endif //BITCOIN_WALLET_RPCWALLET_H
diff --git a/src/wallet/test/wallet_test_fixture.cpp b/src/wallet/test/wallet_test_fixture.cpp
index 7797f85f07..6ec5ca29ad 100644
--- a/src/wallet/test/wallet_test_fixture.cpp
+++ b/src/wallet/test/wallet_test_fixture.cpp
@@ -11,6 +11,7 @@ WalletTestingSetup::WalletTestingSetup(const std::string& chainName):
TestingSetup(chainName)
{
bitdb.MakeMock();
+ g_wallet_allow_fallback_fee = true;
bool fFirstRun;
g_address_type = OUTPUT_TYPE_DEFAULT;
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 161372784b..9db5d63922 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -451,6 +451,9 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
// than or equal to key birthday.
BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
{
+ g_address_type = OUTPUT_TYPE_DEFAULT;
+ g_change_type = OUTPUT_TYPE_DEFAULT;
+
// Create two blocks with same timestamp to verify that importwallet rescan
// will pick up both blocks, not just the first.
const int64_t BLOCK_TIME = chainActive.Tip()->GetBlockTimeMax() + 5;
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 513819606b..af07be311e 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -43,6 +43,7 @@ bool bSpendZeroConfChange = DEFAULT_SPEND_ZEROCONF_CHANGE;
bool fWalletRbf = DEFAULT_WALLET_RBF;
OutputType g_address_type = OUTPUT_TYPE_NONE;
OutputType g_change_type = OUTPUT_TYPE_NONE;
+bool g_wallet_allow_fallback_fee = false; //<! will be defined via chainparams
const char * DEFAULT_WALLET_DAT = "wallet.dat";
const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000;
@@ -1668,20 +1669,15 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlock
dProgressStart = GuessVerificationProgress(chainParams.TxData(), pindex);
dProgressTip = GuessVerificationProgress(chainParams.TxData(), tip);
}
+ double gvp = dProgressStart;
while (pindex && !fAbortRescan)
{
if (pindex->nHeight % 100 == 0 && dProgressTip - dProgressStart > 0.0) {
- double gvp = 0;
- {
- LOCK(cs_main);
- gvp = GuessVerificationProgress(chainParams.TxData(), pindex);
- }
ShowProgress(_("Rescanning..."), std::max(1, std::min(99, (int)((gvp - dProgressStart) / (dProgressTip - dProgressStart) * 100))));
}
if (GetTime() >= nNow + 60) {
nNow = GetTime();
- LOCK(cs_main);
- LogPrintf("Still rescanning. At block %d. Progress=%f\n", pindex->nHeight, GuessVerificationProgress(chainParams.TxData(), pindex));
+ LogPrintf("Still rescanning. At block %d. Progress=%f\n", pindex->nHeight, gvp);
}
CBlock block;
@@ -1705,6 +1701,7 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlock
{
LOCK(cs_main);
pindex = chainActive.Next(pindex);
+ gvp = GuessVerificationProgress(chainParams.TxData(), pindex);
if (tip != chainActive.Tip()) {
tip = chainActive.Tip();
// in case the tip has changed, update progress max
@@ -1713,7 +1710,7 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlock
}
}
if (pindex && fAbortRescan) {
- LogPrintf("Rescan aborted at block %d. Progress=%f\n", pindex->nHeight, GuessVerificationProgress(chainParams.TxData(), pindex));
+ LogPrintf("Rescan aborted at block %d. Progress=%f\n", pindex->nHeight, gvp);
}
ShowProgress(_("Rescanning..."), 100); // hide progress dialog in GUI
}
@@ -2922,6 +2919,11 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
}
nFeeNeeded = GetMinimumFee(nBytes, coin_control, ::mempool, ::feeEstimator, &feeCalc);
+ if (feeCalc.reason == FeeReason::FALLBACK && !g_wallet_allow_fallback_fee) {
+ // eventually allow a fallback fee
+ strFailReason = _("Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.");
+ return false;
+ }
// If we made it here and we aren't even able to meet the relay fee on the next pass, give up
// because we must be at the maximum allowed fee.
@@ -3092,7 +3094,7 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CCon
{
// Broadcast
if (!wtx.AcceptToMemoryPool(maxTxFee, state)) {
- LogPrintf("CommitTransaction(): Transaction cannot be broadcast immediately, %s\n", state.GetRejectReason());
+ LogPrintf("CommitTransaction(): Transaction cannot be broadcast immediately, %s\n", FormatStateMessage(state));
// TODO: if we expect the failure to be long term or permanent, instead delete wtx from the wallet and return failure.
} else {
wtx.RelayWalletTransaction(connman);
@@ -4179,11 +4181,6 @@ int CMerkleTx::GetBlocksToMaturity() const
bool CWalletTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state)
{
- // Quick check to avoid re-setting fInMempool to false
- if (mempool.exists(tx->GetHash())) {
- return false;
- }
-
// We must set fInMempool here - while it will be re-set to true by the
// entered-mempool callback, if we did not there would be a race where a
// user could call sendmoney in a loop and hit spurious out of funds errors
@@ -4191,7 +4188,7 @@ bool CWalletTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState&
// unavailable as we're not yet aware its in mempool.
bool ret = ::AcceptToMemoryPool(mempool, state, tx, nullptr /* pfMissingInputs */,
nullptr /* plTxnReplaced */, false /* bypass_limits */, nAbsurdFee);
- fInMempool = ret;
+ fInMempool |= ret;
return ret;
}
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index fefe415bb1..8d4b701872 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -39,6 +39,7 @@ extern CFeeRate payTxFee;
extern unsigned int nTxConfirmTarget;
extern bool bSpendZeroConfChange;
extern bool fWalletRbf;
+extern bool g_wallet_allow_fallback_fee;
static const unsigned int DEFAULT_KEYPOOL_SIZE = 1000;
//! -paytxfee default
@@ -65,6 +66,7 @@ static const unsigned int DEFAULT_TX_CONFIRM_TARGET = 6;
static const bool DEFAULT_WALLET_RBF = false;
static const bool DEFAULT_WALLETBROADCAST = true;
static const bool DEFAULT_DISABLE_WALLET = false;
+static const bool DEFAULT_WALLET_ALLOW_FALLBACKFEE = true;
extern const char * DEFAULT_WALLET_DAT;