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.include2
-rw-r--r--src/chainparamsbase.cpp44
-rw-r--r--src/chainparamsbase.h5
-rw-r--r--src/chainparamsseeds.h2
-rw-r--r--src/clientversion.cpp2
-rw-r--r--src/init.cpp21
-rw-r--r--src/key.cpp6
-rw-r--r--src/net.cpp86
-rw-r--r--src/net.h10
-rw-r--r--src/net_processing.cpp4
-rw-r--r--src/qt/addresstablemodel.cpp12
-rw-r--r--src/qt/bitcoin.cpp2
-rw-r--r--src/qt/guiutil.cpp4
-rw-r--r--src/qt/optionsmodel.cpp7
-rw-r--r--src/qt/rpcconsole.cpp9
-rw-r--r--src/qt/test/rpcnestedtests.cpp4
-rw-r--r--src/qt/walletmodel.cpp1
-rw-r--r--src/qt/winshutdownmonitor.cpp2
-rw-r--r--src/rest.cpp14
-rw-r--r--src/rpc/blockchain.cpp252
-rw-r--r--src/rpc/client.cpp3
-rw-r--r--src/rpc/mining.cpp130
-rw-r--r--src/rpc/misc.cpp227
-rw-r--r--src/rpc/net.cpp138
-rw-r--r--src/rpc/protocol.cpp18
-rw-r--r--src/rpc/rawtransaction.cpp387
-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/script/interpreter.h2
-rw-r--r--src/script/ismine.h2
-rw-r--r--src/test/data/script_tests.json12
-rw-r--r--src/test/data/tx_invalid.json4
-rw-r--r--src/test/data/tx_valid.json4
-rw-r--r--src/test/rpc_tests.cpp12
-rw-r--r--src/test/util_tests.cpp131
-rw-r--r--src/torcontrol.cpp6
-rw-r--r--src/torcontrol.h2
-rw-r--r--src/univalue/include/univalue.h4
-rw-r--r--src/univalue/test/object.cpp14
-rw-r--r--src/util.cpp35
-rw-r--r--src/util.h6
-rw-r--r--src/validation.cpp7
-rw-r--r--src/wallet/feebumper.cpp48
-rw-r--r--src/wallet/feebumper.h2
-rw-r--r--src/wallet/rpcdump.cpp2
-rw-r--r--src/wallet/rpcwallet.cpp565
-rw-r--r--src/wallet/rpcwallet.h3
-rw-r--r--src/wallet/test/wallet_tests.cpp3
-rw-r--r--src/wallet/wallet.cpp13
51 files changed, 1406 insertions, 955 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..13c27299f8 100644
--- a/src/Makefile.bench.include
+++ b/src/Makefile.bench.include
@@ -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/chainparamsbase.cpp b/src/chainparamsbase.cpp
index 89dd8549b9..a04258fd40 100644
--- a/src/chainparamsbase.cpp
+++ b/src/chainparamsbase.cpp
@@ -24,44 +24,6 @@ void AppendParamsHelpMessages(std::string& strUsage, bool debugHelp)
strUsage += HelpMessageOpt("-testnet", _("Use the test chain"));
}
-/**
- * Main network
- */
-class CBaseMainParams : public CBaseChainParams
-{
-public:
- CBaseMainParams()
- {
- nRPCPort = 8332;
- }
-};
-
-/**
- * Testnet (v3)
- */
-class CBaseTestNetParams : public CBaseChainParams
-{
-public:
- CBaseTestNetParams()
- {
- nRPCPort = 18332;
- strDataDir = "testnet3";
- }
-};
-
-/*
- * Regression test
- */
-class CBaseRegTestParams : public CBaseChainParams
-{
-public:
- CBaseRegTestParams()
- {
- nRPCPort = 18443;
- strDataDir = "regtest";
- }
-};
-
static std::unique_ptr<CBaseChainParams> globalChainBaseParams;
const CBaseChainParams& BaseParams()
@@ -73,11 +35,11 @@ const CBaseChainParams& BaseParams()
std::unique_ptr<CBaseChainParams> CreateBaseChainParams(const std::string& chain)
{
if (chain == CBaseChainParams::MAIN)
- return std::unique_ptr<CBaseChainParams>(new CBaseMainParams());
+ return MakeUnique<CBaseChainParams>("", 8332);
else if (chain == CBaseChainParams::TESTNET)
- return std::unique_ptr<CBaseChainParams>(new CBaseTestNetParams());
+ return MakeUnique<CBaseChainParams>("testnet3", 18332);
else if (chain == CBaseChainParams::REGTEST)
- return std::unique_ptr<CBaseChainParams>(new CBaseRegTestParams());
+ return MakeUnique<CBaseChainParams>("regtest", 18443);
else
throw std::runtime_error(strprintf("%s: Unknown chain %s.", __func__, chain));
}
diff --git a/src/chainparamsbase.h b/src/chainparamsbase.h
index b4d2bb4f08..2cb860380e 100644
--- a/src/chainparamsbase.h
+++ b/src/chainparamsbase.h
@@ -24,9 +24,10 @@ public:
const std::string& DataDir() const { return strDataDir; }
int RPCPort() const { return nRPCPort; }
-protected:
- CBaseChainParams() {}
+ CBaseChainParams() = delete;
+ CBaseChainParams(const std::string& data_dir, int rpc_port) : nRPCPort(rpc_port), strDataDir(data_dir) {}
+private:
int nRPCPort;
std::string strDataDir;
};
diff --git a/src/chainparamsseeds.h b/src/chainparamsseeds.h
index 2b102c464f..6e2b3c34a2 100644
--- a/src/chainparamsseeds.h
+++ b/src/chainparamsseeds.h
@@ -5,7 +5,7 @@
* AUTOGENERATED by contrib/seeds/generate-seeds.py
*
* Each line contains a 16-byte IPv6 address and a port.
- * IPv4 as well as onion addresses are wrapped inside a IPv6 address accordingly.
+ * IPv4 as well as onion addresses are wrapped inside an IPv6 address accordingly.
*/
static SeedSpec6 pnSeed6_main[] = {
{{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x05,0x13,0x05,0x7f}, 8333},
diff --git a/src/clientversion.cpp b/src/clientversion.cpp
index 7aa0a8d660..662fbb6e77 100644
--- a/src/clientversion.cpp
+++ b/src/clientversion.cpp
@@ -43,7 +43,7 @@ const std::string CLIENT_NAME("Satoshi");
//! git will put "#define GIT_ARCHIVE 1" on the next line inside archives. $Format:%n#define GIT_ARCHIVE 1$
#ifdef GIT_ARCHIVE
-#define GIT_COMMIT_ID "$Format:%h$"
+#define GIT_COMMIT_ID "$Format:%H$"
#define GIT_COMMIT_DATE "$Format:%cD$"
#endif
diff --git a/src/init.cpp b/src/init.cpp
index 84398d978c..5c5d1ee792 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -165,6 +165,7 @@ void Interrupt()
InterruptRPC();
InterruptREST();
InterruptTorControl();
+ InterruptMapPort();
if (g_connman)
g_connman->Interrupt();
}
@@ -191,7 +192,7 @@ void Shutdown()
#ifdef ENABLE_WALLET
FlushWallets();
#endif
- MapPort(false);
+ StopMapPort();
// Because these depend on each-other, we make sure that neither can be
// using the other before destroying them.
@@ -545,7 +546,8 @@ static void BlockNotifyCallback(bool initialSync, const CBlockIndex *pBlockIndex
std::string strCmd = gArgs.GetArg("-blocknotify", "");
if (!strCmd.empty()) {
boost::replace_all(strCmd, "%s", pBlockIndex->GetBlockHash().GetHex());
- boost::thread t(runCommand, strCmd); // thread runs free
+ std::thread t(runCommand, strCmd);
+ t.detach(); // thread runs free
}
}
@@ -678,11 +680,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)) {
@@ -1425,6 +1429,9 @@ bool AppInitMain()
pcoinsTip.reset();
pcoinsdbview.reset();
pcoinscatcher.reset();
+ // new CBlockTreeDB tries to delete the existing file, which
+ // fails if it's still open from the previous loop. Close it first:
+ pblocktree.reset();
pblocktree.reset(new CBlockTreeDB(nBlockTreeDBCache, false, fReset));
if (fReset) {
@@ -1519,7 +1526,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);
}
@@ -1671,12 +1678,14 @@ bool AppInitMain()
LogPrintf("nBestHeight = %d\n", chain_active_height);
if (gArgs.GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION))
- StartTorControl(threadGroup, scheduler);
+ StartTorControl();
- Discover(threadGroup);
+ Discover();
// Map ports with UPnP
- MapPort(gArgs.GetBoolArg("-upnp", DEFAULT_UPNP));
+ if (gArgs.GetBoolArg("-upnp", DEFAULT_UPNP)) {
+ StartMapPort();
+ }
CConnman::Options connOptions;
connOptions.nLocalServices = nLocalServices;
diff --git a/src/key.cpp b/src/key.cpp
index e998e3db6e..ffed989be1 100644
--- a/src/key.cpp
+++ b/src/key.cpp
@@ -44,7 +44,7 @@ static int ec_privkey_import_der(const secp256k1_context* ctx, unsigned char *ou
if (end - privkey < 1 || !(*privkey & 0x80u)) {
return 0;
}
- size_t lenb = *privkey & ~0x80u; privkey++;
+ ptrdiff_t lenb = *privkey & ~0x80u; privkey++;
if (lenb < 1 || lenb > 2) {
return 0;
}
@@ -52,7 +52,7 @@ static int ec_privkey_import_der(const secp256k1_context* ctx, unsigned char *ou
return 0;
}
/* sequence length */
- size_t len = privkey[lenb-1] | (lenb > 1 ? privkey[lenb-2] << 8 : 0u);
+ ptrdiff_t len = privkey[lenb-1] | (lenb > 1 ? privkey[lenb-2] << 8 : 0u);
privkey += lenb;
if (end - privkey < len) {
return 0;
@@ -66,7 +66,7 @@ static int ec_privkey_import_der(const secp256k1_context* ctx, unsigned char *ou
if (end - privkey < 2 || privkey[0] != 0x04u) {
return 0;
}
- size_t oslen = privkey[1];
+ ptrdiff_t oslen = privkey[1];
privkey += 2;
if (oslen > 32 || end - privkey < oslen) {
return 0;
diff --git a/src/net.cpp b/src/net.cpp
index 03ed7e7fc1..201914685c 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -1459,6 +1459,8 @@ void CConnman::WakeMessageHandler()
#ifdef USE_UPNP
+static CThreadInterrupt g_upnp_interrupt;
+static std::thread g_upnp_thread;
void ThreadMapPort()
{
std::string port = strprintf("%u", GetListenPort());
@@ -1509,35 +1511,29 @@ void ThreadMapPort()
std::string strDesc = "Bitcoin " + FormatFullVersion();
- try {
- while (true) {
+ do {
#ifndef UPNPDISCOVER_SUCCESS
- /* miniupnpc 1.5 */
- r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype,
- port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0);
+ /* miniupnpc 1.5 */
+ r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype,
+ port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0);
#else
- /* miniupnpc 1.6 */
- r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype,
- port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0, "0");
+ /* miniupnpc 1.6 */
+ r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype,
+ port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0, "0");
#endif
- if(r!=UPNPCOMMAND_SUCCESS)
- LogPrintf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n",
- port, port, lanaddr, r, strupnperror(r));
- else
- LogPrintf("UPnP Port Mapping successful.\n");
-
- MilliSleep(20*60*1000); // Refresh every 20 minutes
- }
- }
- catch (const boost::thread_interrupted&)
- {
- r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", 0);
- LogPrintf("UPNP_DeletePortMapping() returned: %d\n", r);
- freeUPNPDevlist(devlist); devlist = nullptr;
- FreeUPNPUrls(&urls);
- throw;
+ if(r!=UPNPCOMMAND_SUCCESS)
+ LogPrintf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n",
+ port, port, lanaddr, r, strupnperror(r));
+ else
+ LogPrintf("UPnP Port Mapping successful.\n");
}
+ while(g_upnp_interrupt.sleep_for(std::chrono::minutes(20)));
+
+ r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", 0);
+ LogPrintf("UPNP_DeletePortMapping() returned: %d\n", r);
+ freeUPNPDevlist(devlist); devlist = nullptr;
+ FreeUPNPUrls(&urls);
} else {
LogPrintf("No valid UPnP IGDs found\n");
freeUPNPDevlist(devlist); devlist = nullptr;
@@ -1546,27 +1542,39 @@ void ThreadMapPort()
}
}
-void MapPort(bool fUseUPnP)
+void StartMapPort()
{
- static std::unique_ptr<boost::thread> upnp_thread;
+ if (!g_upnp_thread.joinable()) {
+ assert(!g_upnp_interrupt);
+ g_upnp_thread = std::thread((std::bind(&TraceThread<void (*)()>, "upnp", &ThreadMapPort)));
+ }
+}
- if (fUseUPnP)
- {
- if (upnp_thread) {
- upnp_thread->interrupt();
- upnp_thread->join();
- }
- upnp_thread.reset(new boost::thread(boost::bind(&TraceThread<void (*)()>, "upnp", &ThreadMapPort)));
+void InterruptMapPort()
+{
+ if(g_upnp_thread.joinable()) {
+ g_upnp_interrupt();
}
- else if (upnp_thread) {
- upnp_thread->interrupt();
- upnp_thread->join();
- upnp_thread.reset();
+}
+
+void StopMapPort()
+{
+ if(g_upnp_thread.joinable()) {
+ g_upnp_thread.join();
+ g_upnp_interrupt.reset();
}
}
#else
-void MapPort(bool)
+void StartMapPort()
+{
+ // Intentionally left blank.
+}
+void InterruptMapPort()
+{
+ // Intentionally left blank.
+}
+void StopMapPort()
{
// Intentionally left blank.
}
@@ -2121,7 +2129,7 @@ bool CConnman::BindListenPort(const CService &addrBind, std::string& strError, b
return true;
}
-void Discover(boost::thread_group& threadGroup)
+void Discover()
{
if (!fDiscover)
return;
diff --git a/src/net.h b/src/net.h
index 0542ec1aaa..8378a303b8 100644
--- a/src/net.h
+++ b/src/net.h
@@ -37,10 +37,6 @@
class CScheduler;
class CNode;
-namespace boost {
- class thread_group;
-} // namespace boost
-
/** Time between pings automatically sent out for latency probing and keepalive (in seconds). */
static const int PING_INTERVAL = 2 * 60;
/** Time after which to disconnect, after waiting for a ping response (or inactivity). */
@@ -441,8 +437,10 @@ private:
friend struct CConnmanTest;
};
extern std::unique_ptr<CConnman> g_connman;
-void Discover(boost::thread_group& threadGroup);
-void MapPort(bool fUseUPnP);
+void Discover();
+void StartMapPort();
+void InterruptMapPort();
+void StopMapPort();
unsigned short GetListenPort();
bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false);
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index fc0ba82d8b..bf9307727a 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -1226,10 +1226,10 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
}
} // release cs_main
- if (it != pfrom->vRecvGetData.end()) {
+ if (it != pfrom->vRecvGetData.end() && !pfrom->fPauseSend) {
const CInv &inv = *it;
- it++;
if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_CMPCT_BLOCK || inv.type == MSG_WITNESS_BLOCK) {
+ it++;
ProcessGetBlockData(pfrom, consensusParams, inv, connman, interruptMsgProc);
}
}
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 e4c088d379..06e1f1a37c 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -706,7 +706,7 @@ int main(int argc, char *argv[])
if (BitcoinCore::baseInitialize()) {
app.requestInitialize();
#if defined(Q_OS_WIN) && QT_VERSION >= 0x050000
- WinShutdownMonitor::registerShutdownBlockReason(QObject::tr("%1 didn't yet exit safely...").arg(QObject::tr(PACKAGE_NAME)), static_cast<HWND>(app.getMainWinId()));
+ WinShutdownMonitor::registerShutdownBlockReason(QObject::tr("%1 didn't yet exit safely...").arg(QObject::tr(PACKAGE_NAME)), (HWND)app.getMainWinId());
#endif
app.exec();
app.requestShutdown();
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index 558d4f108c..a46e0561b9 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -101,7 +101,7 @@ QFont fixedPitchFont()
#endif
}
-// Just some dummy data to generate an convincing random-looking (but consistent) address
+// Just some dummy data to generate a convincing random-looking (but consistent) address
static const uint8_t dummydata[] = {0xeb,0x15,0x23,0x1d,0xfc,0xeb,0x60,0x92,0x58,0x86,0xb6,0x7d,0x06,0x52,0x99,0x92,0x59,0x15,0xae,0xb1,0x72,0xc0,0x66,0x47};
// Generate a dummy address with invalid CRC, starting with the network prefix.
@@ -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/optionsmodel.cpp b/src/qt/optionsmodel.cpp
index 4ade88d843..909be1c264 100644
--- a/src/qt/optionsmodel.cpp
+++ b/src/qt/optionsmodel.cpp
@@ -315,7 +315,12 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in
break;
case MapPortUPnP: // core option - can be changed on-the-fly
settings.setValue("fUseUPnP", value.toBool());
- MapPort(value.toBool());
+ if (value.toBool()) {
+ StartMapPort();
+ } else {
+ InterruptMapPort();
+ StopMapPort();
+ }
break;
case MinimizeOnClose:
fMinimizeOnClose = value.toBool();
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/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/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/winshutdownmonitor.cpp b/src/qt/winshutdownmonitor.cpp
index e11a72899d..1e7a76efc0 100644
--- a/src/qt/winshutdownmonitor.cpp
+++ b/src/qt/winshutdownmonitor.cpp
@@ -56,7 +56,7 @@ bool WinShutdownMonitor::nativeEventFilter(const QByteArray &eventType, void *pM
void WinShutdownMonitor::registerShutdownBlockReason(const QString& strReason, const HWND& mainWinId)
{
typedef BOOL (WINAPI *PSHUTDOWNBRCREATE)(HWND, LPCWSTR);
- PSHUTDOWNBRCREATE shutdownBRCreate = static_cast<PSHUTDOWNBRCREATE>(GetProcAddress(GetModuleHandleA("User32.dll"), "ShutdownBlockReasonCreate"));
+ PSHUTDOWNBRCREATE shutdownBRCreate = (PSHUTDOWNBRCREATE)GetProcAddress(GetModuleHandleA("User32.dll"), "ShutdownBlockReasonCreate");
if (shutdownBRCreate == nullptr) {
qWarning() << "registerShutdownBlockReason: GetProcAddress for ShutdownBlockReasonCreate failed";
return;
diff --git a/src/rest.cpp b/src/rest.cpp
index 30e481171f..eeeb3f5141 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -540,23 +540,23 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart)
// pack in some essentials
// use more or less the same output as mentioned in Bip64
- objGetUTXOResponse.push_back(Pair("chainHeight", chainActive.Height()));
- objGetUTXOResponse.push_back(Pair("chaintipHash", chainActive.Tip()->GetBlockHash().GetHex()));
- objGetUTXOResponse.push_back(Pair("bitmap", bitmapStringRepresentation));
+ objGetUTXOResponse.pushKV("chainHeight", chainActive.Height());
+ objGetUTXOResponse.pushKV("chaintipHash", chainActive.Tip()->GetBlockHash().GetHex());
+ objGetUTXOResponse.pushKV("bitmap", bitmapStringRepresentation);
UniValue utxos(UniValue::VARR);
for (const CCoin& coin : outs) {
UniValue utxo(UniValue::VOBJ);
- utxo.push_back(Pair("height", (int32_t)coin.nHeight));
- utxo.push_back(Pair("value", ValueFromAmount(coin.out.nValue)));
+ utxo.pushKV("height", (int32_t)coin.nHeight);
+ utxo.pushKV("value", ValueFromAmount(coin.out.nValue));
// include the script in a json output
UniValue o(UniValue::VOBJ);
ScriptPubKeyToUniv(coin.out.scriptPubKey, o, true);
- utxo.push_back(Pair("scriptPubKey", o));
+ utxo.pushKV("scriptPubKey", o);
utxos.push_back(utxo);
}
- objGetUTXOResponse.push_back(Pair("utxos", utxos));
+ objGetUTXOResponse.pushKV("utxos", utxos);
// return json string
std::string strJSON = objGetUTXOResponse.write() + "\n";
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 4276ad9eeb..f1352a13cf 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -88,28 +88,28 @@ UniValue blockheaderToJSON(const CBlockIndex* blockindex)
{
AssertLockHeld(cs_main);
UniValue result(UniValue::VOBJ);
- result.push_back(Pair("hash", blockindex->GetBlockHash().GetHex()));
+ result.pushKV("hash", blockindex->GetBlockHash().GetHex());
int confirmations = -1;
// Only report confirmations if the block is on the main chain
if (chainActive.Contains(blockindex))
confirmations = chainActive.Height() - blockindex->nHeight + 1;
- result.push_back(Pair("confirmations", confirmations));
- result.push_back(Pair("height", blockindex->nHeight));
- result.push_back(Pair("version", blockindex->nVersion));
- result.push_back(Pair("versionHex", strprintf("%08x", blockindex->nVersion)));
- result.push_back(Pair("merkleroot", blockindex->hashMerkleRoot.GetHex()));
- result.push_back(Pair("time", (int64_t)blockindex->nTime));
- result.push_back(Pair("mediantime", (int64_t)blockindex->GetMedianTimePast()));
- result.push_back(Pair("nonce", (uint64_t)blockindex->nNonce));
- result.push_back(Pair("bits", strprintf("%08x", blockindex->nBits)));
- result.push_back(Pair("difficulty", GetDifficulty(blockindex)));
- result.push_back(Pair("chainwork", blockindex->nChainWork.GetHex()));
+ result.pushKV("confirmations", confirmations);
+ result.pushKV("height", blockindex->nHeight);
+ result.pushKV("version", blockindex->nVersion);
+ result.pushKV("versionHex", strprintf("%08x", blockindex->nVersion));
+ result.pushKV("merkleroot", blockindex->hashMerkleRoot.GetHex());
+ result.pushKV("time", (int64_t)blockindex->nTime);
+ result.pushKV("mediantime", (int64_t)blockindex->GetMedianTimePast());
+ result.pushKV("nonce", (uint64_t)blockindex->nNonce);
+ result.pushKV("bits", strprintf("%08x", blockindex->nBits));
+ result.pushKV("difficulty", GetDifficulty(blockindex));
+ result.pushKV("chainwork", blockindex->nChainWork.GetHex());
if (blockindex->pprev)
- result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex()));
+ result.pushKV("previousblockhash", blockindex->pprev->GetBlockHash().GetHex());
CBlockIndex *pnext = chainActive.Next(blockindex);
if (pnext)
- result.push_back(Pair("nextblockhash", pnext->GetBlockHash().GetHex()));
+ result.pushKV("nextblockhash", pnext->GetBlockHash().GetHex());
return result;
}
@@ -117,19 +117,19 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx
{
AssertLockHeld(cs_main);
UniValue result(UniValue::VOBJ);
- result.push_back(Pair("hash", blockindex->GetBlockHash().GetHex()));
+ result.pushKV("hash", blockindex->GetBlockHash().GetHex());
int confirmations = -1;
// Only report confirmations if the block is on the main chain
if (chainActive.Contains(blockindex))
confirmations = chainActive.Height() - blockindex->nHeight + 1;
- result.push_back(Pair("confirmations", confirmations));
- result.push_back(Pair("strippedsize", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS)));
- result.push_back(Pair("size", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION)));
- result.push_back(Pair("weight", (int)::GetBlockWeight(block)));
- result.push_back(Pair("height", blockindex->nHeight));
- result.push_back(Pair("version", block.nVersion));
- result.push_back(Pair("versionHex", strprintf("%08x", block.nVersion)));
- result.push_back(Pair("merkleroot", block.hashMerkleRoot.GetHex()));
+ result.pushKV("confirmations", confirmations);
+ result.pushKV("strippedsize", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS));
+ result.pushKV("size", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION));
+ result.pushKV("weight", (int)::GetBlockWeight(block));
+ result.pushKV("height", blockindex->nHeight);
+ result.pushKV("version", block.nVersion);
+ result.pushKV("versionHex", strprintf("%08x", block.nVersion));
+ result.pushKV("merkleroot", block.hashMerkleRoot.GetHex());
UniValue txs(UniValue::VARR);
for(const auto& tx : block.vtx)
{
@@ -142,19 +142,19 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx
else
txs.push_back(tx->GetHash().GetHex());
}
- result.push_back(Pair("tx", txs));
- result.push_back(Pair("time", block.GetBlockTime()));
- result.push_back(Pair("mediantime", (int64_t)blockindex->GetMedianTimePast()));
- result.push_back(Pair("nonce", (uint64_t)block.nNonce));
- result.push_back(Pair("bits", strprintf("%08x", block.nBits)));
- result.push_back(Pair("difficulty", GetDifficulty(blockindex)));
- result.push_back(Pair("chainwork", blockindex->nChainWork.GetHex()));
+ result.pushKV("tx", txs);
+ result.pushKV("time", block.GetBlockTime());
+ result.pushKV("mediantime", (int64_t)blockindex->GetMedianTimePast());
+ result.pushKV("nonce", (uint64_t)block.nNonce);
+ result.pushKV("bits", strprintf("%08x", block.nBits));
+ result.pushKV("difficulty", GetDifficulty(blockindex));
+ result.pushKV("chainwork", blockindex->nChainWork.GetHex());
if (blockindex->pprev)
- result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex()));
+ result.pushKV("previousblockhash", blockindex->pprev->GetBlockHash().GetHex());
CBlockIndex *pnext = chainActive.Next(blockindex);
if (pnext)
- result.push_back(Pair("nextblockhash", pnext->GetBlockHash().GetHex()));
+ result.pushKV("nextblockhash", pnext->GetBlockHash().GetHex());
return result;
}
@@ -235,8 +235,8 @@ UniValue waitfornewblock(const JSONRPCRequest& request)
block = latestblock;
}
UniValue ret(UniValue::VOBJ);
- ret.push_back(Pair("hash", block.hash.GetHex()));
- ret.push_back(Pair("height", block.height));
+ ret.pushKV("hash", block.hash.GetHex());
+ ret.pushKV("height", block.height);
return ret;
}
@@ -277,8 +277,8 @@ UniValue waitforblock(const JSONRPCRequest& request)
}
UniValue ret(UniValue::VOBJ);
- ret.push_back(Pair("hash", block.hash.GetHex()));
- ret.push_back(Pair("height", block.height));
+ ret.pushKV("hash", block.hash.GetHex());
+ ret.pushKV("height", block.height);
return ret;
}
@@ -319,8 +319,8 @@ UniValue waitforblockheight(const JSONRPCRequest& request)
block = latestblock;
}
UniValue ret(UniValue::VOBJ);
- ret.push_back(Pair("hash", block.hash.GetHex()));
- ret.push_back(Pair("height", block.height));
+ ret.pushKV("hash", block.hash.GetHex());
+ ret.pushKV("height", block.height);
return ret;
}
@@ -379,18 +379,18 @@ void entryToJSON(UniValue &info, const CTxMemPoolEntry &e)
{
AssertLockHeld(mempool.cs);
- info.push_back(Pair("size", (int)e.GetTxSize()));
- info.push_back(Pair("fee", ValueFromAmount(e.GetFee())));
- info.push_back(Pair("modifiedfee", ValueFromAmount(e.GetModifiedFee())));
- info.push_back(Pair("time", e.GetTime()));
- info.push_back(Pair("height", (int)e.GetHeight()));
- info.push_back(Pair("descendantcount", e.GetCountWithDescendants()));
- info.push_back(Pair("descendantsize", e.GetSizeWithDescendants()));
- info.push_back(Pair("descendantfees", e.GetModFeesWithDescendants()));
- info.push_back(Pair("ancestorcount", e.GetCountWithAncestors()));
- info.push_back(Pair("ancestorsize", e.GetSizeWithAncestors()));
- info.push_back(Pair("ancestorfees", e.GetModFeesWithAncestors()));
- info.push_back(Pair("wtxid", mempool.vTxHashes[e.vTxHashesIdx].first.ToString()));
+ info.pushKV("size", (int)e.GetTxSize());
+ info.pushKV("fee", ValueFromAmount(e.GetFee()));
+ info.pushKV("modifiedfee", ValueFromAmount(e.GetModifiedFee()));
+ info.pushKV("time", e.GetTime());
+ info.pushKV("height", (int)e.GetHeight());
+ info.pushKV("descendantcount", e.GetCountWithDescendants());
+ info.pushKV("descendantsize", e.GetSizeWithDescendants());
+ info.pushKV("descendantfees", e.GetModFeesWithDescendants());
+ info.pushKV("ancestorcount", e.GetCountWithAncestors());
+ info.pushKV("ancestorsize", e.GetSizeWithAncestors());
+ info.pushKV("ancestorfees", e.GetModFeesWithAncestors());
+ info.pushKV("wtxid", mempool.vTxHashes[e.vTxHashesIdx].first.ToString());
const CTransaction& tx = e.GetTx();
std::set<std::string> setDepends;
for (const CTxIn& txin : tx.vin)
@@ -405,7 +405,7 @@ void entryToJSON(UniValue &info, const CTxMemPoolEntry &e)
depends.push_back(dep);
}
- info.push_back(Pair("depends", depends));
+ info.pushKV("depends", depends);
}
UniValue mempoolToJSON(bool fVerbose)
@@ -419,7 +419,7 @@ UniValue mempoolToJSON(bool fVerbose)
const uint256& hash = e.GetTx().GetHash();
UniValue info(UniValue::VOBJ);
entryToJSON(info, e);
- o.push_back(Pair(hash.ToString(), info));
+ o.pushKV(hash.ToString(), info);
}
return o;
}
@@ -526,7 +526,7 @@ UniValue getmempoolancestors(const JSONRPCRequest& request)
const uint256& _hash = e.GetTx().GetHash();
UniValue info(UniValue::VOBJ);
entryToJSON(info, e);
- o.push_back(Pair(_hash.ToString(), info));
+ o.pushKV(_hash.ToString(), info);
}
return o;
}
@@ -590,7 +590,7 @@ UniValue getmempooldescendants(const JSONRPCRequest& request)
const uint256& _hash = e.GetTx().GetHash();
UniValue info(UniValue::VOBJ);
entryToJSON(info, e);
- o.push_back(Pair(_hash.ToString(), info));
+ o.pushKV(_hash.ToString(), info);
}
return o;
}
@@ -952,14 +952,14 @@ UniValue gettxoutsetinfo(const JSONRPCRequest& request)
CCoinsStats stats;
FlushStateToDisk();
if (GetUTXOStats(pcoinsdbview.get(), stats)) {
- ret.push_back(Pair("height", (int64_t)stats.nHeight));
- ret.push_back(Pair("bestblock", stats.hashBlock.GetHex()));
- ret.push_back(Pair("transactions", (int64_t)stats.nTransactions));
- ret.push_back(Pair("txouts", (int64_t)stats.nTransactionOutputs));
- ret.push_back(Pair("bogosize", (int64_t)stats.nBogoSize));
- ret.push_back(Pair("hash_serialized_2", stats.hashSerialized.GetHex()));
- ret.push_back(Pair("disk_size", stats.nDiskSize));
- ret.push_back(Pair("total_amount", ValueFromAmount(stats.nTotalAmount)));
+ ret.pushKV("height", (int64_t)stats.nHeight);
+ ret.pushKV("bestblock", stats.hashBlock.GetHex());
+ ret.pushKV("transactions", (int64_t)stats.nTransactions);
+ ret.pushKV("txouts", (int64_t)stats.nTransactionOutputs);
+ ret.pushKV("bogosize", (int64_t)stats.nBogoSize);
+ ret.pushKV("hash_serialized_2", stats.hashSerialized.GetHex());
+ ret.pushKV("disk_size", stats.nDiskSize);
+ ret.pushKV("total_amount", ValueFromAmount(stats.nTotalAmount));
} else {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
}
@@ -1031,17 +1031,17 @@ UniValue gettxout(const JSONRPCRequest& request)
BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock());
CBlockIndex *pindex = it->second;
- ret.push_back(Pair("bestblock", pindex->GetBlockHash().GetHex()));
+ ret.pushKV("bestblock", pindex->GetBlockHash().GetHex());
if (coin.nHeight == MEMPOOL_HEIGHT) {
- ret.push_back(Pair("confirmations", 0));
+ ret.pushKV("confirmations", 0);
} else {
- ret.push_back(Pair("confirmations", (int64_t)(pindex->nHeight - coin.nHeight + 1)));
+ ret.pushKV("confirmations", (int64_t)(pindex->nHeight - coin.nHeight + 1));
}
- ret.push_back(Pair("value", ValueFromAmount(coin.out.nValue)));
+ ret.pushKV("value", ValueFromAmount(coin.out.nValue));
UniValue o(UniValue::VOBJ);
ScriptPubKeyToUniv(coin.out.scriptPubKey, o, true);
- ret.push_back(Pair("scriptPubKey", o));
- ret.push_back(Pair("coinbase", (bool)coin.fCoinBase));
+ ret.pushKV("scriptPubKey", o);
+ ret.pushKV("coinbase", (bool)coin.fCoinBase);
return ret;
}
@@ -1091,16 +1091,16 @@ static UniValue SoftForkMajorityDesc(int version, CBlockIndex* pindex, const Con
activated = pindex->nHeight >= consensusParams.BIP65Height;
break;
}
- rv.push_back(Pair("status", activated));
+ rv.pushKV("status", activated);
return rv;
}
static UniValue SoftForkDesc(const std::string &name, int version, CBlockIndex* pindex, const Consensus::Params& consensusParams)
{
UniValue rv(UniValue::VOBJ);
- rv.push_back(Pair("id", name));
- rv.push_back(Pair("version", version));
- rv.push_back(Pair("reject", SoftForkMajorityDesc(version, pindex, consensusParams)));
+ rv.pushKV("id", name);
+ rv.pushKV("version", version);
+ rv.pushKV("reject", SoftForkMajorityDesc(version, pindex, consensusParams));
return rv;
}
@@ -1109,29 +1109,29 @@ static UniValue BIP9SoftForkDesc(const Consensus::Params& consensusParams, Conse
UniValue rv(UniValue::VOBJ);
const ThresholdState thresholdState = VersionBitsTipState(consensusParams, id);
switch (thresholdState) {
- case THRESHOLD_DEFINED: rv.push_back(Pair("status", "defined")); break;
- case THRESHOLD_STARTED: rv.push_back(Pair("status", "started")); break;
- case THRESHOLD_LOCKED_IN: rv.push_back(Pair("status", "locked_in")); break;
- case THRESHOLD_ACTIVE: rv.push_back(Pair("status", "active")); break;
- case THRESHOLD_FAILED: rv.push_back(Pair("status", "failed")); break;
+ case THRESHOLD_DEFINED: rv.pushKV("status", "defined"); break;
+ case THRESHOLD_STARTED: rv.pushKV("status", "started"); break;
+ case THRESHOLD_LOCKED_IN: rv.pushKV("status", "locked_in"); break;
+ case THRESHOLD_ACTIVE: rv.pushKV("status", "active"); break;
+ case THRESHOLD_FAILED: rv.pushKV("status", "failed"); break;
}
if (THRESHOLD_STARTED == thresholdState)
{
- rv.push_back(Pair("bit", consensusParams.vDeployments[id].bit));
+ rv.pushKV("bit", consensusParams.vDeployments[id].bit);
}
- rv.push_back(Pair("startTime", consensusParams.vDeployments[id].nStartTime));
- rv.push_back(Pair("timeout", consensusParams.vDeployments[id].nTimeout));
- rv.push_back(Pair("since", VersionBitsTipStateSinceHeight(consensusParams, id)));
+ rv.pushKV("startTime", consensusParams.vDeployments[id].nStartTime);
+ rv.pushKV("timeout", consensusParams.vDeployments[id].nTimeout);
+ rv.pushKV("since", VersionBitsTipStateSinceHeight(consensusParams, id));
if (THRESHOLD_STARTED == thresholdState)
{
UniValue statsUV(UniValue::VOBJ);
BIP9Stats statsStruct = VersionBitsTipStatistics(consensusParams, id);
- statsUV.push_back(Pair("period", statsStruct.period));
- statsUV.push_back(Pair("threshold", statsStruct.threshold));
- statsUV.push_back(Pair("elapsed", statsStruct.elapsed));
- statsUV.push_back(Pair("count", statsStruct.count));
- statsUV.push_back(Pair("possible", statsStruct.possible));
- rv.push_back(Pair("statistics", statsUV));
+ statsUV.pushKV("period", statsStruct.period);
+ statsUV.pushKV("threshold", statsStruct.threshold);
+ statsUV.pushKV("elapsed", statsStruct.elapsed);
+ statsUV.pushKV("count", statsStruct.count);
+ statsUV.pushKV("possible", statsStruct.possible);
+ rv.pushKV("statistics", statsUV);
}
return rv;
}
@@ -1142,7 +1142,7 @@ void BIP9SoftForkDescPushBack(UniValue& bip9_softforks, const Consensus::Params&
// A timeout value of 0 guarantees a softfork will never be activated.
// This is used when softfork codes are merged without specifying the deployment schedule.
if (consensusParams.vDeployments[id].nTimeout > 0)
- bip9_softforks.push_back(Pair(VersionBitsDeploymentInfo[id].name, BIP9SoftForkDesc(consensusParams, id)));
+ bip9_softforks.pushKV(VersionBitsDeploymentInfo[id].name, BIP9SoftForkDesc(consensusParams, id));
}
UniValue getblockchaininfo(const JSONRPCRequest& request)
@@ -1202,17 +1202,17 @@ UniValue getblockchaininfo(const JSONRPCRequest& request)
LOCK(cs_main);
UniValue obj(UniValue::VOBJ);
- obj.push_back(Pair("chain", Params().NetworkIDString()));
- obj.push_back(Pair("blocks", (int)chainActive.Height()));
- obj.push_back(Pair("headers", pindexBestHeader ? pindexBestHeader->nHeight : -1));
- obj.push_back(Pair("bestblockhash", chainActive.Tip()->GetBlockHash().GetHex()));
- obj.push_back(Pair("difficulty", (double)GetDifficulty()));
- obj.push_back(Pair("mediantime", (int64_t)chainActive.Tip()->GetMedianTimePast()));
- obj.push_back(Pair("verificationprogress", GuessVerificationProgress(Params().TxData(), chainActive.Tip())));
- obj.push_back(Pair("initialblockdownload", IsInitialBlockDownload()));
- obj.push_back(Pair("chainwork", chainActive.Tip()->nChainWork.GetHex()));
- obj.push_back(Pair("size_on_disk", CalculateCurrentUsage()));
- obj.push_back(Pair("pruned", fPruneMode));
+ obj.pushKV("chain", Params().NetworkIDString());
+ obj.pushKV("blocks", (int)chainActive.Height());
+ obj.pushKV("headers", pindexBestHeader ? pindexBestHeader->nHeight : -1);
+ obj.pushKV("bestblockhash", chainActive.Tip()->GetBlockHash().GetHex());
+ obj.pushKV("difficulty", (double)GetDifficulty());
+ obj.pushKV("mediantime", (int64_t)chainActive.Tip()->GetMedianTimePast());
+ obj.pushKV("verificationprogress", GuessVerificationProgress(Params().TxData(), chainActive.Tip()));
+ obj.pushKV("initialblockdownload", IsInitialBlockDownload());
+ obj.pushKV("chainwork", chainActive.Tip()->nChainWork.GetHex());
+ obj.pushKV("size_on_disk", CalculateCurrentUsage());
+ obj.pushKV("pruned", fPruneMode);
if (fPruneMode) {
CBlockIndex* block = chainActive.Tip();
assert(block);
@@ -1220,13 +1220,13 @@ UniValue getblockchaininfo(const JSONRPCRequest& request)
block = block->pprev;
}
- obj.push_back(Pair("pruneheight", block->nHeight));
+ obj.pushKV("pruneheight", block->nHeight);
// if 0, execution bypasses the whole if block.
bool automatic_pruning = (gArgs.GetArg("-prune", 0) != 1);
- obj.push_back(Pair("automatic_pruning", automatic_pruning));
+ obj.pushKV("automatic_pruning", automatic_pruning);
if (automatic_pruning) {
- obj.push_back(Pair("prune_target_size", nPruneTarget));
+ obj.pushKV("prune_target_size", nPruneTarget);
}
}
@@ -1240,10 +1240,10 @@ UniValue getblockchaininfo(const JSONRPCRequest& request)
for (int pos = Consensus::DEPLOYMENT_CSV; pos != Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++pos) {
BIP9SoftForkDescPushBack(bip9_softforks, consensusParams, static_cast<Consensus::DeploymentPos>(pos));
}
- obj.push_back(Pair("softforks", softforks));
- obj.push_back(Pair("bip9_softforks", bip9_softforks));
+ obj.pushKV("softforks", softforks);
+ obj.pushKV("bip9_softforks", bip9_softforks);
- obj.push_back(Pair("warnings", GetWarnings("statusbar")));
+ obj.pushKV("warnings", GetWarnings("statusbar"));
return obj;
}
@@ -1331,11 +1331,11 @@ UniValue getchaintips(const JSONRPCRequest& request)
for (const CBlockIndex* block : setTips)
{
UniValue obj(UniValue::VOBJ);
- obj.push_back(Pair("height", block->nHeight));
- obj.push_back(Pair("hash", block->phashBlock->GetHex()));
+ obj.pushKV("height", block->nHeight);
+ obj.pushKV("hash", block->phashBlock->GetHex());
const int branchLen = block->nHeight - chainActive.FindFork(block)->nHeight;
- obj.push_back(Pair("branchlen", branchLen));
+ obj.pushKV("branchlen", branchLen);
std::string status;
if (chainActive.Contains(block)) {
@@ -1357,7 +1357,7 @@ UniValue getchaintips(const JSONRPCRequest& request)
// No clue.
status = "unknown";
}
- obj.push_back(Pair("status", status));
+ obj.pushKV("status", status);
res.push_back(obj);
}
@@ -1368,13 +1368,13 @@ UniValue getchaintips(const JSONRPCRequest& request)
UniValue mempoolInfoToJSON()
{
UniValue ret(UniValue::VOBJ);
- ret.push_back(Pair("size", (int64_t) mempool.size()));
- ret.push_back(Pair("bytes", (int64_t) mempool.GetTotalTxSize()));
- ret.push_back(Pair("usage", (int64_t) mempool.DynamicMemoryUsage()));
+ ret.pushKV("size", (int64_t) mempool.size());
+ ret.pushKV("bytes", (int64_t) mempool.GetTotalTxSize());
+ ret.pushKV("usage", (int64_t) mempool.DynamicMemoryUsage());
size_t maxmempool = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
- ret.push_back(Pair("maxmempool", (int64_t) maxmempool));
- ret.push_back(Pair("mempoolminfee", ValueFromAmount(std::max(mempool.GetMinFee(maxmempool), ::minRelayTxFee).GetFeePerK())));
- ret.push_back(Pair("minrelaytxfee", ValueFromAmount(::minRelayTxFee.GetFeePerK())));
+ ret.pushKV("maxmempool", (int64_t) maxmempool);
+ ret.pushKV("mempoolminfee", ValueFromAmount(std::max(mempool.GetMinFee(maxmempool), ::minRelayTxFee).GetFeePerK()));
+ ret.pushKV("minrelaytxfee", ValueFromAmount(::minRelayTxFee.GetFeePerK()));
return ret;
}
@@ -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;
@@ -1563,7 +1563,7 @@ UniValue getchaintxstats(const JSONRPCRequest& request)
pindex = chainActive.Tip();
}
}
-
+
assert(pindex != nullptr);
if (request.params[0].isNull()) {
@@ -1581,15 +1581,15 @@ UniValue getchaintxstats(const JSONRPCRequest& request)
int nTxDiff = pindex->nChainTx - pindexPast->nChainTx;
UniValue ret(UniValue::VOBJ);
- ret.push_back(Pair("time", (int64_t)pindex->nTime));
- ret.push_back(Pair("txcount", (int64_t)pindex->nChainTx));
- ret.push_back(Pair("window_final_block_hash", pindex->GetBlockHash().GetHex()));
- ret.push_back(Pair("window_block_count", blockcount));
+ ret.pushKV("time", (int64_t)pindex->nTime);
+ ret.pushKV("txcount", (int64_t)pindex->nChainTx);
+ ret.pushKV("window_final_block_hash", pindex->GetBlockHash().GetHex());
+ ret.pushKV("window_block_count", blockcount);
if (blockcount > 0) {
- ret.push_back(Pair("window_tx_count", nTxDiff));
- ret.push_back(Pair("window_interval", nTimeDiff));
+ ret.pushKV("window_tx_count", nTxDiff);
+ ret.pushKV("window_interval", nTimeDiff);
if (nTimeDiff > 0) {
- ret.push_back(Pair("txrate", ((double)nTxDiff) / nTimeDiff));
+ ret.pushKV("txrate", ((double)nTxDiff) / nTimeDiff);
}
}
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 69d0d12e86..3f3bfa0cfd 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -211,14 +211,14 @@ UniValue getmininginfo(const JSONRPCRequest& request)
LOCK(cs_main);
UniValue obj(UniValue::VOBJ);
- obj.push_back(Pair("blocks", (int)chainActive.Height()));
- obj.push_back(Pair("currentblockweight", (uint64_t)nLastBlockWeight));
- obj.push_back(Pair("currentblocktx", (uint64_t)nLastBlockTx));
- obj.push_back(Pair("difficulty", (double)GetDifficulty()));
- obj.push_back(Pair("networkhashps", getnetworkhashps(request)));
- obj.push_back(Pair("pooledtx", (uint64_t)mempool.size()));
- obj.push_back(Pair("chain", Params().NetworkIDString()));
- obj.push_back(Pair("warnings", GetWarnings("statusbar")));
+ obj.pushKV("blocks", (int)chainActive.Height());
+ obj.pushKV("currentblockweight", (uint64_t)nLastBlockWeight);
+ obj.pushKV("currentblocktx", (uint64_t)nLastBlockTx);
+ obj.pushKV("difficulty", (double)GetDifficulty());
+ obj.pushKV("networkhashps", getnetworkhashps(request));
+ obj.pushKV("pooledtx", (uint64_t)mempool.size());
+ obj.pushKV("chain", Params().NetworkIDString());
+ obj.pushKV("warnings", GetWarnings("statusbar"));
return obj;
}
@@ -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;
@@ -550,9 +550,9 @@ UniValue getblocktemplate(const JSONRPCRequest& request)
UniValue entry(UniValue::VOBJ);
- entry.push_back(Pair("data", EncodeHexTx(tx)));
- entry.push_back(Pair("txid", txHash.GetHex()));
- entry.push_back(Pair("hash", tx.GetWitnessHash().GetHex()));
+ entry.pushKV("data", EncodeHexTx(tx));
+ entry.pushKV("txid", txHash.GetHex());
+ entry.pushKV("hash", tx.GetWitnessHash().GetHex());
UniValue deps(UniValue::VARR);
for (const CTxIn &in : tx.vin)
@@ -560,23 +560,23 @@ UniValue getblocktemplate(const JSONRPCRequest& request)
if (setTxIndex.count(in.prevout.hash))
deps.push_back(setTxIndex[in.prevout.hash]);
}
- entry.push_back(Pair("depends", deps));
+ entry.pushKV("depends", deps);
int index_in_template = i - 1;
- entry.push_back(Pair("fee", pblocktemplate->vTxFees[index_in_template]));
+ entry.pushKV("fee", pblocktemplate->vTxFees[index_in_template]);
int64_t nTxSigOps = pblocktemplate->vTxSigOpsCost[index_in_template];
if (fPreSegWit) {
assert(nTxSigOps % WITNESS_SCALE_FACTOR == 0);
nTxSigOps /= WITNESS_SCALE_FACTOR;
}
- entry.push_back(Pair("sigops", nTxSigOps));
- entry.push_back(Pair("weight", GetTransactionWeight(tx)));
+ entry.pushKV("sigops", nTxSigOps);
+ entry.pushKV("weight", GetTransactionWeight(tx));
transactions.push_back(entry);
}
UniValue aux(UniValue::VOBJ);
- aux.push_back(Pair("flags", HexStr(COINBASE_FLAGS.begin(), COINBASE_FLAGS.end())));
+ aux.pushKV("flags", HexStr(COINBASE_FLAGS.begin(), COINBASE_FLAGS.end()));
arith_uint256 hashTarget = arith_uint256().SetCompact(pblock->nBits);
@@ -586,7 +586,7 @@ UniValue getblocktemplate(const JSONRPCRequest& request)
aMutable.push_back("prevblock");
UniValue result(UniValue::VOBJ);
- result.push_back(Pair("capabilities", aCaps));
+ result.pushKV("capabilities", aCaps);
UniValue aRules(UniValue::VARR);
UniValue vbavailable(UniValue::VOBJ);
@@ -605,7 +605,7 @@ UniValue getblocktemplate(const JSONRPCRequest& request)
case THRESHOLD_STARTED:
{
const struct VBDeploymentInfo& vbinfo = VersionBitsDeploymentInfo[pos];
- vbavailable.push_back(Pair(gbt_vb_name(pos), consensusParams.vDeployments[pos].bit));
+ vbavailable.pushKV(gbt_vb_name(pos), consensusParams.vDeployments[pos].bit);
if (setClientRules.find(vbinfo.name) == setClientRules.end()) {
if (!vbinfo.gbt_force) {
// If the client doesn't support this, don't indicate it in the [default] version
@@ -630,10 +630,10 @@ UniValue getblocktemplate(const JSONRPCRequest& request)
}
}
}
- result.push_back(Pair("version", pblock->nVersion));
- result.push_back(Pair("rules", aRules));
- result.push_back(Pair("vbavailable", vbavailable));
- result.push_back(Pair("vbrequired", int(0)));
+ result.pushKV("version", pblock->nVersion);
+ result.pushKV("rules", aRules);
+ result.pushKV("vbavailable", vbavailable);
+ result.pushKV("vbrequired", int(0));
if (nMaxVersionPreVB >= 2) {
// If VB is supported by the client, nMaxVersionPreVB is -1, so we won't get here
@@ -643,15 +643,15 @@ UniValue getblocktemplate(const JSONRPCRequest& request)
aMutable.push_back("version/force");
}
- result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex()));
- result.push_back(Pair("transactions", transactions));
- result.push_back(Pair("coinbaseaux", aux));
- result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0]->vout[0].nValue));
- result.push_back(Pair("longpollid", chainActive.Tip()->GetBlockHash().GetHex() + i64tostr(nTransactionsUpdatedLast)));
- result.push_back(Pair("target", hashTarget.GetHex()));
- result.push_back(Pair("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1));
- result.push_back(Pair("mutable", aMutable));
- result.push_back(Pair("noncerange", "00000000ffffffff"));
+ result.pushKV("previousblockhash", pblock->hashPrevBlock.GetHex());
+ result.pushKV("transactions", transactions);
+ result.pushKV("coinbaseaux", aux);
+ result.pushKV("coinbasevalue", (int64_t)pblock->vtx[0]->vout[0].nValue);
+ result.pushKV("longpollid", chainActive.Tip()->GetBlockHash().GetHex() + i64tostr(nTransactionsUpdatedLast));
+ result.pushKV("target", hashTarget.GetHex());
+ result.pushKV("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1);
+ result.pushKV("mutable", aMutable);
+ result.pushKV("noncerange", "00000000ffffffff");
int64_t nSigOpLimit = MAX_BLOCK_SIGOPS_COST;
int64_t nSizeLimit = MAX_BLOCK_SERIALIZED_SIZE;
if (fPreSegWit) {
@@ -660,17 +660,17 @@ UniValue getblocktemplate(const JSONRPCRequest& request)
assert(nSizeLimit % WITNESS_SCALE_FACTOR == 0);
nSizeLimit /= WITNESS_SCALE_FACTOR;
}
- result.push_back(Pair("sigoplimit", nSigOpLimit));
- result.push_back(Pair("sizelimit", nSizeLimit));
+ result.pushKV("sigoplimit", nSigOpLimit);
+ result.pushKV("sizelimit", nSizeLimit);
if (!fPreSegWit) {
- result.push_back(Pair("weightlimit", (int64_t)MAX_BLOCK_WEIGHT));
+ result.pushKV("weightlimit", (int64_t)MAX_BLOCK_WEIGHT);
}
- result.push_back(Pair("curtime", pblock->GetBlockTime()));
- result.push_back(Pair("bits", strprintf("%08x", pblock->nBits)));
- result.push_back(Pair("height", (int64_t)(pindexPrev->nHeight+1)));
+ result.pushKV("curtime", pblock->GetBlockTime());
+ result.pushKV("bits", strprintf("%08x", pblock->nBits));
+ result.pushKV("height", (int64_t)(pindexPrev->nHeight+1));
if (!pblocktemplate->vchCoinbaseCommitment.empty() && fSupportsSegwit) {
- result.push_back(Pair("default_witness_commitment", HexStr(pblocktemplate->vchCoinbaseCommitment.begin(), pblocktemplate->vchCoinbaseCommitment.end())));
+ result.pushKV("default_witness_commitment", HexStr(pblocktemplate->vchCoinbaseCommitment.begin(), pblocktemplate->vchCoinbaseCommitment.end()));
}
return result;
@@ -823,12 +823,12 @@ UniValue estimatesmartfee(const JSONRPCRequest& request)
FeeCalculation feeCalc;
CFeeRate feeRate = ::feeEstimator.estimateSmartFee(conf_target, &feeCalc, conservative);
if (feeRate != CFeeRate(0)) {
- result.push_back(Pair("feerate", ValueFromAmount(feeRate.GetFeePerK())));
+ result.pushKV("feerate", ValueFromAmount(feeRate.GetFeePerK()));
} else {
errors.push_back("Insufficient data or no feerate found");
- result.push_back(Pair("errors", errors));
+ result.pushKV("errors", errors);
}
- result.push_back(Pair("blocks", feeCalc.returnedTarget));
+ result.pushKV("blocks", feeCalc.returnedTarget);
return result;
}
@@ -899,37 +899,37 @@ UniValue estimaterawfee(const JSONRPCRequest& request)
UniValue horizon_result(UniValue::VOBJ);
UniValue errors(UniValue::VARR);
UniValue passbucket(UniValue::VOBJ);
- passbucket.push_back(Pair("startrange", round(buckets.pass.start)));
- passbucket.push_back(Pair("endrange", round(buckets.pass.end)));
- passbucket.push_back(Pair("withintarget", round(buckets.pass.withinTarget * 100.0) / 100.0));
- passbucket.push_back(Pair("totalconfirmed", round(buckets.pass.totalConfirmed * 100.0) / 100.0));
- passbucket.push_back(Pair("inmempool", round(buckets.pass.inMempool * 100.0) / 100.0));
- passbucket.push_back(Pair("leftmempool", round(buckets.pass.leftMempool * 100.0) / 100.0));
+ passbucket.pushKV("startrange", round(buckets.pass.start));
+ passbucket.pushKV("endrange", round(buckets.pass.end));
+ passbucket.pushKV("withintarget", round(buckets.pass.withinTarget * 100.0) / 100.0);
+ passbucket.pushKV("totalconfirmed", round(buckets.pass.totalConfirmed * 100.0) / 100.0);
+ passbucket.pushKV("inmempool", round(buckets.pass.inMempool * 100.0) / 100.0);
+ passbucket.pushKV("leftmempool", round(buckets.pass.leftMempool * 100.0) / 100.0);
UniValue failbucket(UniValue::VOBJ);
- failbucket.push_back(Pair("startrange", round(buckets.fail.start)));
- failbucket.push_back(Pair("endrange", round(buckets.fail.end)));
- failbucket.push_back(Pair("withintarget", round(buckets.fail.withinTarget * 100.0) / 100.0));
- failbucket.push_back(Pair("totalconfirmed", round(buckets.fail.totalConfirmed * 100.0) / 100.0));
- failbucket.push_back(Pair("inmempool", round(buckets.fail.inMempool * 100.0) / 100.0));
- failbucket.push_back(Pair("leftmempool", round(buckets.fail.leftMempool * 100.0) / 100.0));
+ failbucket.pushKV("startrange", round(buckets.fail.start));
+ failbucket.pushKV("endrange", round(buckets.fail.end));
+ failbucket.pushKV("withintarget", round(buckets.fail.withinTarget * 100.0) / 100.0);
+ failbucket.pushKV("totalconfirmed", round(buckets.fail.totalConfirmed * 100.0) / 100.0);
+ failbucket.pushKV("inmempool", round(buckets.fail.inMempool * 100.0) / 100.0);
+ failbucket.pushKV("leftmempool", round(buckets.fail.leftMempool * 100.0) / 100.0);
// CFeeRate(0) is used to indicate error as a return value from estimateRawFee
if (feeRate != CFeeRate(0)) {
- horizon_result.push_back(Pair("feerate", ValueFromAmount(feeRate.GetFeePerK())));
- horizon_result.push_back(Pair("decay", buckets.decay));
- horizon_result.push_back(Pair("scale", (int)buckets.scale));
- horizon_result.push_back(Pair("pass", passbucket));
+ horizon_result.pushKV("feerate", ValueFromAmount(feeRate.GetFeePerK()));
+ horizon_result.pushKV("decay", buckets.decay);
+ horizon_result.pushKV("scale", (int)buckets.scale);
+ horizon_result.pushKV("pass", passbucket);
// buckets.fail.start == -1 indicates that all buckets passed, there is no fail bucket to output
- if (buckets.fail.start != -1) horizon_result.push_back(Pair("fail", failbucket));
+ if (buckets.fail.start != -1) horizon_result.pushKV("fail", failbucket);
} else {
// Output only information that is still meaningful in the event of error
- horizon_result.push_back(Pair("decay", buckets.decay));
- horizon_result.push_back(Pair("scale", (int)buckets.scale));
- horizon_result.push_back(Pair("fail", failbucket));
+ horizon_result.pushKV("decay", buckets.decay);
+ horizon_result.pushKV("scale", (int)buckets.scale);
+ horizon_result.pushKV("fail", failbucket);
errors.push_back("Insufficient data or no feerate found which meets threshold");
- horizon_result.push_back(Pair("errors",errors));
+ horizon_result.pushKV("errors",errors);
}
- result.push_back(Pair(StringForFeeEstimateHorizon(horizon), horizon_result));
+ result.pushKV(StringForFeeEstimateHorizon(horizon), horizon_result);
}
return result;
}
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index aba004a639..f573c7dbeb 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -33,221 +33,56 @@
#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.push_back(Pair("isscript", false));
- obj.push_back(Pair("iswitness", false));
- if (pwallet && pwallet->GetPubKey(keyID, vchPubKey)) {
- obj.push_back(Pair("pubkey", HexStr(vchPubKey)));
- obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
- }
- return obj;
- }
-
- UniValue operator()(const CScriptID &scriptID) const {
- UniValue obj(UniValue::VOBJ);
- CScript subscript;
- obj.push_back(Pair("isscript", true));
- obj.push_back(Pair("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.push_back(Pair("isscript", false));
- obj.push_back(Pair("iswitness", true));
- obj.push_back(Pair("witness_version", 0));
- obj.push_back(Pair("witness_program", HexStr(id.begin(), id.end())));
- if (pwallet && pwallet->GetPubKey(CKeyID(id), pubkey)) {
- obj.push_back(Pair("pubkey", HexStr(pubkey)));
- }
- return obj;
- }
-
- UniValue operator()(const WitnessV0ScriptHash& id) const
- {
- UniValue obj(UniValue::VOBJ);
- CScript subscript;
- obj.push_back(Pair("isscript", true));
- obj.push_back(Pair("iswitness", true));
- obj.push_back(Pair("witness_version", 0));
- obj.push_back(Pair("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.push_back(Pair("iswitness", true));
- obj.push_back(Pair("witness_version", (int)id.version));
- obj.push_back(Pair("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);
UniValue ret(UniValue::VOBJ);
- ret.push_back(Pair("isvalid", isValid));
+ ret.pushKV("isvalid", isValid);
if (isValid)
{
- std::string currentAddress = EncodeDestination(dest);
- ret.push_back(Pair("address", currentAddress));
-
- CScript scriptPubKey = GetScriptForDestination(dest);
- ret.push_back(Pair("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end())));
#ifdef ENABLE_WALLET
- isminetype mine = pwallet ? IsMine(*pwallet, dest) : ISMINE_NO;
- ret.push_back(Pair("ismine", bool(mine & ISMINE_SPENDABLE)));
- ret.push_back(Pair("iswatchonly", bool(mine & ISMINE_WATCH_ONLY)));
- UniValue detail = boost::apply_visitor(DescribeAddressVisitor(pwallet), dest);
- ret.pushKVs(detail);
- if (pwallet && pwallet->mapAddressBook.count(dest)) {
- ret.push_back(Pair("account", pwallet->mapAddressBook[dest].name));
- }
- if (pwallet) {
- const 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.push_back(Pair("timestamp", meta->nCreateTime));
- if (!meta->hdKeypath.empty()) {
- ret.push_back(Pair("hdkeypath", meta->hdKeypath));
- ret.push_back(Pair("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;
}
@@ -304,8 +139,8 @@ UniValue createmultisig(const JSONRPCRequest& request)
CScriptID innerID(inner);
UniValue result(UniValue::VOBJ);
- result.push_back(Pair("address", EncodeDestination(innerID)));
- result.push_back(Pair("redeemScript", HexStr(inner.begin(), inner.end())));
+ result.pushKV("address", EncodeDestination(innerID));
+ result.pushKV("redeemScript", HexStr(inner.begin(), inner.end()));
return result;
}
@@ -439,12 +274,12 @@ static UniValue RPCLockedMemoryInfo()
{
LockedPool::Stats stats = LockedPoolManager::Instance().stats();
UniValue obj(UniValue::VOBJ);
- obj.push_back(Pair("used", uint64_t(stats.used)));
- obj.push_back(Pair("free", uint64_t(stats.free)));
- obj.push_back(Pair("total", uint64_t(stats.total)));
- obj.push_back(Pair("locked", uint64_t(stats.locked)));
- obj.push_back(Pair("chunks_used", uint64_t(stats.chunks_used)));
- obj.push_back(Pair("chunks_free", uint64_t(stats.chunks_free)));
+ obj.pushKV("used", uint64_t(stats.used));
+ obj.pushKV("free", uint64_t(stats.free));
+ obj.pushKV("total", uint64_t(stats.total));
+ obj.pushKV("locked", uint64_t(stats.locked));
+ obj.pushKV("chunks_used", uint64_t(stats.chunks_used));
+ obj.pushKV("chunks_free", uint64_t(stats.chunks_free));
return obj;
}
@@ -501,7 +336,7 @@ UniValue getmemoryinfo(const JSONRPCRequest& request)
std::string mode = request.params[0].isNull() ? "stats" : request.params[0].get_str();
if (mode == "stats") {
UniValue obj(UniValue::VOBJ);
- obj.push_back(Pair("locked", RPCLockedMemoryInfo()));
+ obj.pushKV("locked", RPCLockedMemoryInfo());
return obj;
} else if (mode == "mallocinfo") {
#ifdef HAVE_MALLOC_INFO
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index 59b376c59a..7a0225ff0d 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -130,59 +130,59 @@ UniValue getpeerinfo(const JSONRPCRequest& request)
UniValue obj(UniValue::VOBJ);
CNodeStateStats statestats;
bool fStateStats = GetNodeStateStats(stats.nodeid, statestats);
- obj.push_back(Pair("id", stats.nodeid));
- obj.push_back(Pair("addr", stats.addrName));
+ obj.pushKV("id", stats.nodeid);
+ obj.pushKV("addr", stats.addrName);
if (!(stats.addrLocal.empty()))
- obj.push_back(Pair("addrlocal", stats.addrLocal));
+ obj.pushKV("addrlocal", stats.addrLocal);
if (stats.addrBind.IsValid())
- obj.push_back(Pair("addrbind", stats.addrBind.ToString()));
- obj.push_back(Pair("services", strprintf("%016x", stats.nServices)));
- obj.push_back(Pair("relaytxes", stats.fRelayTxes));
- obj.push_back(Pair("lastsend", stats.nLastSend));
- obj.push_back(Pair("lastrecv", stats.nLastRecv));
- obj.push_back(Pair("bytessent", stats.nSendBytes));
- obj.push_back(Pair("bytesrecv", stats.nRecvBytes));
- obj.push_back(Pair("conntime", stats.nTimeConnected));
- obj.push_back(Pair("timeoffset", stats.nTimeOffset));
+ obj.pushKV("addrbind", stats.addrBind.ToString());
+ obj.pushKV("services", strprintf("%016x", stats.nServices));
+ obj.pushKV("relaytxes", stats.fRelayTxes);
+ obj.pushKV("lastsend", stats.nLastSend);
+ obj.pushKV("lastrecv", stats.nLastRecv);
+ obj.pushKV("bytessent", stats.nSendBytes);
+ obj.pushKV("bytesrecv", stats.nRecvBytes);
+ obj.pushKV("conntime", stats.nTimeConnected);
+ obj.pushKV("timeoffset", stats.nTimeOffset);
if (stats.dPingTime > 0.0)
- obj.push_back(Pair("pingtime", stats.dPingTime));
+ obj.pushKV("pingtime", stats.dPingTime);
if (stats.dMinPing < static_cast<double>(std::numeric_limits<int64_t>::max())/1e6)
- obj.push_back(Pair("minping", stats.dMinPing));
+ obj.pushKV("minping", stats.dMinPing);
if (stats.dPingWait > 0.0)
- obj.push_back(Pair("pingwait", stats.dPingWait));
- obj.push_back(Pair("version", stats.nVersion));
+ obj.pushKV("pingwait", stats.dPingWait);
+ obj.pushKV("version", stats.nVersion);
// Use the sanitized form of subver here, to avoid tricksy remote peers from
// corrupting or modifying the JSON output by putting special characters in
// their ver message.
- obj.push_back(Pair("subver", stats.cleanSubVer));
- obj.push_back(Pair("inbound", stats.fInbound));
- obj.push_back(Pair("addnode", stats.m_manual_connection));
- obj.push_back(Pair("startingheight", stats.nStartingHeight));
+ obj.pushKV("subver", stats.cleanSubVer);
+ obj.pushKV("inbound", stats.fInbound);
+ obj.pushKV("addnode", stats.m_manual_connection);
+ obj.pushKV("startingheight", stats.nStartingHeight);
if (fStateStats) {
- obj.push_back(Pair("banscore", statestats.nMisbehavior));
- obj.push_back(Pair("synced_headers", statestats.nSyncHeight));
- obj.push_back(Pair("synced_blocks", statestats.nCommonHeight));
+ obj.pushKV("banscore", statestats.nMisbehavior);
+ obj.pushKV("synced_headers", statestats.nSyncHeight);
+ obj.pushKV("synced_blocks", statestats.nCommonHeight);
UniValue heights(UniValue::VARR);
for (int height : statestats.vHeightInFlight) {
heights.push_back(height);
}
- obj.push_back(Pair("inflight", heights));
+ obj.pushKV("inflight", heights);
}
- obj.push_back(Pair("whitelisted", stats.fWhitelisted));
+ obj.pushKV("whitelisted", stats.fWhitelisted);
UniValue sendPerMsgCmd(UniValue::VOBJ);
for (const mapMsgCmdSize::value_type &i : stats.mapSendBytesPerMsgCmd) {
if (i.second > 0)
- sendPerMsgCmd.push_back(Pair(i.first, i.second));
+ sendPerMsgCmd.pushKV(i.first, i.second);
}
- obj.push_back(Pair("bytessent_per_msg", sendPerMsgCmd));
+ obj.pushKV("bytessent_per_msg", sendPerMsgCmd);
UniValue recvPerMsgCmd(UniValue::VOBJ);
for (const mapMsgCmdSize::value_type &i : stats.mapRecvBytesPerMsgCmd) {
if (i.second > 0)
- recvPerMsgCmd.push_back(Pair(i.first, i.second));
+ recvPerMsgCmd.pushKV(i.first, i.second);
}
- obj.push_back(Pair("bytesrecv_per_msg", recvPerMsgCmd));
+ obj.pushKV("bytesrecv_per_msg", recvPerMsgCmd);
ret.push_back(obj);
}
@@ -331,16 +331,16 @@ UniValue getaddednodeinfo(const JSONRPCRequest& request)
for (const AddedNodeInfo& info : vInfo) {
UniValue obj(UniValue::VOBJ);
- obj.push_back(Pair("addednode", info.strAddedNode));
- obj.push_back(Pair("connected", info.fConnected));
+ obj.pushKV("addednode", info.strAddedNode);
+ obj.pushKV("connected", info.fConnected);
UniValue addresses(UniValue::VARR);
if (info.fConnected) {
UniValue address(UniValue::VOBJ);
- address.push_back(Pair("address", info.resolvedAddress.ToString()));
- address.push_back(Pair("connected", info.fInbound ? "inbound" : "outbound"));
+ address.pushKV("address", info.resolvedAddress.ToString());
+ address.pushKV("connected", info.fInbound ? "inbound" : "outbound");
addresses.push_back(address);
}
- obj.push_back(Pair("addresses", addresses));
+ obj.pushKV("addresses", addresses);
ret.push_back(obj);
}
@@ -377,18 +377,18 @@ UniValue getnettotals(const JSONRPCRequest& request)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
UniValue obj(UniValue::VOBJ);
- obj.push_back(Pair("totalbytesrecv", g_connman->GetTotalBytesRecv()));
- obj.push_back(Pair("totalbytessent", g_connman->GetTotalBytesSent()));
- obj.push_back(Pair("timemillis", GetTimeMillis()));
+ obj.pushKV("totalbytesrecv", g_connman->GetTotalBytesRecv());
+ obj.pushKV("totalbytessent", g_connman->GetTotalBytesSent());
+ obj.pushKV("timemillis", GetTimeMillis());
UniValue outboundLimit(UniValue::VOBJ);
- outboundLimit.push_back(Pair("timeframe", g_connman->GetMaxOutboundTimeframe()));
- outboundLimit.push_back(Pair("target", g_connman->GetMaxOutboundTarget()));
- outboundLimit.push_back(Pair("target_reached", g_connman->OutboundTargetReached(false)));
- outboundLimit.push_back(Pair("serve_historical_blocks", !g_connman->OutboundTargetReached(true)));
- outboundLimit.push_back(Pair("bytes_left_in_cycle", g_connman->GetOutboundTargetBytesLeft()));
- outboundLimit.push_back(Pair("time_left_in_cycle", g_connman->GetMaxOutboundTimeLeftInCycle()));
- obj.push_back(Pair("uploadtarget", outboundLimit));
+ outboundLimit.pushKV("timeframe", g_connman->GetMaxOutboundTimeframe());
+ outboundLimit.pushKV("target", g_connman->GetMaxOutboundTarget());
+ outboundLimit.pushKV("target_reached", g_connman->OutboundTargetReached(false));
+ outboundLimit.pushKV("serve_historical_blocks", !g_connman->OutboundTargetReached(true));
+ outboundLimit.pushKV("bytes_left_in_cycle", g_connman->GetOutboundTargetBytesLeft());
+ outboundLimit.pushKV("time_left_in_cycle", g_connman->GetMaxOutboundTimeLeftInCycle());
+ obj.pushKV("uploadtarget", outboundLimit);
return obj;
}
@@ -403,11 +403,11 @@ static UniValue GetNetworksInfo()
proxyType proxy;
UniValue obj(UniValue::VOBJ);
GetProxy(network, proxy);
- obj.push_back(Pair("name", GetNetworkName(network)));
- obj.push_back(Pair("limited", IsLimited(network)));
- obj.push_back(Pair("reachable", IsReachable(network)));
- obj.push_back(Pair("proxy", proxy.IsValid() ? proxy.proxy.ToStringIPPort() : std::string()));
- obj.push_back(Pair("proxy_randomize_credentials", proxy.randomize_credentials));
+ obj.pushKV("name", GetNetworkName(network));
+ obj.pushKV("limited", IsLimited(network));
+ obj.pushKV("reachable", IsReachable(network));
+ obj.pushKV("proxy", proxy.IsValid() ? proxy.proxy.ToStringIPPort() : std::string());
+ obj.pushKV("proxy_randomize_credentials", proxy.randomize_credentials);
networks.push_back(obj);
}
return networks;
@@ -458,34 +458,34 @@ UniValue getnetworkinfo(const JSONRPCRequest& request)
LOCK(cs_main);
UniValue obj(UniValue::VOBJ);
- obj.push_back(Pair("version", CLIENT_VERSION));
- obj.push_back(Pair("subversion", strSubVersion));
- obj.push_back(Pair("protocolversion",PROTOCOL_VERSION));
+ obj.pushKV("version", CLIENT_VERSION);
+ obj.pushKV("subversion", strSubVersion);
+ obj.pushKV("protocolversion",PROTOCOL_VERSION);
if(g_connman)
- obj.push_back(Pair("localservices", strprintf("%016x", g_connman->GetLocalServices())));
- obj.push_back(Pair("localrelay", fRelayTxes));
- obj.push_back(Pair("timeoffset", GetTimeOffset()));
+ obj.pushKV("localservices", strprintf("%016x", g_connman->GetLocalServices()));
+ obj.pushKV("localrelay", fRelayTxes);
+ obj.pushKV("timeoffset", GetTimeOffset());
if (g_connman) {
- obj.push_back(Pair("networkactive", g_connman->GetNetworkActive()));
- obj.push_back(Pair("connections", (int)g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL)));
+ obj.pushKV("networkactive", g_connman->GetNetworkActive());
+ obj.pushKV("connections", (int)g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL));
}
- obj.push_back(Pair("networks", GetNetworksInfo()));
- obj.push_back(Pair("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK())));
- obj.push_back(Pair("incrementalfee", ValueFromAmount(::incrementalRelayFee.GetFeePerK())));
+ obj.pushKV("networks", GetNetworksInfo());
+ obj.pushKV("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK()));
+ obj.pushKV("incrementalfee", ValueFromAmount(::incrementalRelayFee.GetFeePerK()));
UniValue localAddresses(UniValue::VARR);
{
LOCK(cs_mapLocalHost);
for (const std::pair<CNetAddr, LocalServiceInfo> &item : mapLocalHost)
{
UniValue rec(UniValue::VOBJ);
- rec.push_back(Pair("address", item.first.ToString()));
- rec.push_back(Pair("port", item.second.nPort));
- rec.push_back(Pair("score", item.second.nScore));
+ rec.pushKV("address", item.first.ToString());
+ rec.pushKV("port", item.second.nPort);
+ rec.pushKV("score", item.second.nScore);
localAddresses.push_back(rec);
}
}
- obj.push_back(Pair("localaddresses", localAddresses));
- obj.push_back(Pair("warnings", GetWarnings("statusbar")));
+ obj.pushKV("localaddresses", localAddresses);
+ obj.pushKV("warnings", GetWarnings("statusbar"));
return obj;
}
@@ -575,10 +575,10 @@ UniValue listbanned(const JSONRPCRequest& request)
{
const CBanEntry& banEntry = entry.second;
UniValue rec(UniValue::VOBJ);
- rec.push_back(Pair("address", entry.first.ToString()));
- rec.push_back(Pair("banned_until", banEntry.nBanUntil));
- rec.push_back(Pair("ban_created", banEntry.nCreateTime));
- rec.push_back(Pair("ban_reason", banEntry.banReasonToString()));
+ rec.pushKV("address", entry.first.ToString());
+ rec.pushKV("banned_until", banEntry.nBanUntil);
+ rec.pushKV("ban_created", banEntry.nCreateTime);
+ rec.pushKV("ban_reason", banEntry.banReasonToString());
bannedAddresses.push_back(rec);
}
diff --git a/src/rpc/protocol.cpp b/src/rpc/protocol.cpp
index cdd2e67a69..0635b757c6 100644
--- a/src/rpc/protocol.cpp
+++ b/src/rpc/protocol.cpp
@@ -26,9 +26,9 @@
UniValue JSONRPCRequestObj(const std::string& strMethod, const UniValue& params, const UniValue& id)
{
UniValue request(UniValue::VOBJ);
- request.push_back(Pair("method", strMethod));
- request.push_back(Pair("params", params));
- request.push_back(Pair("id", id));
+ request.pushKV("method", strMethod);
+ request.pushKV("params", params);
+ request.pushKV("id", id);
return request;
}
@@ -36,11 +36,11 @@ UniValue JSONRPCReplyObj(const UniValue& result, const UniValue& error, const Un
{
UniValue reply(UniValue::VOBJ);
if (!error.isNull())
- reply.push_back(Pair("result", NullUniValue));
+ reply.pushKV("result", NullUniValue);
else
- reply.push_back(Pair("result", result));
- reply.push_back(Pair("error", error));
- reply.push_back(Pair("id", id));
+ reply.pushKV("result", result);
+ reply.pushKV("error", error);
+ reply.pushKV("id", id);
return reply;
}
@@ -53,8 +53,8 @@ std::string JSONRPCReply(const UniValue& result, const UniValue& error, const Un
UniValue JSONRPCError(int code, const std::string& message)
{
UniValue error(UniValue::VOBJ);
- error.push_back(Pair("code", code));
- error.push_back(Pair("message", message));
+ error.pushKV("code", code);
+ error.pushKV("message", message);
return error;
}
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 00e73675b4..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>
@@ -47,17 +47,17 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
TxToUniv(tx, uint256(), entry, true, RPCSerializationFlags());
if (!hashBlock.IsNull()) {
- entry.push_back(Pair("blockhash", hashBlock.GetHex()));
+ entry.pushKV("blockhash", hashBlock.GetHex());
BlockMap::iterator mi = mapBlockIndex.find(hashBlock);
if (mi != mapBlockIndex.end() && (*mi).second) {
CBlockIndex* pindex = (*mi).second;
if (chainActive.Contains(pindex)) {
- entry.push_back(Pair("confirmations", 1 + chainActive.Height() - pindex->nHeight));
- entry.push_back(Pair("time", pindex->GetBlockTime()));
- entry.push_back(Pair("blocktime", pindex->GetBlockTime()));
+ entry.pushKV("confirmations", 1 + chainActive.Height() - pindex->nHeight);
+ entry.pushKV("time", pindex->GetBlockTime());
+ entry.pushKV("blocktime", pindex->GetBlockTime());
}
else
- entry.push_back(Pair("confirmations", 0));
+ entry.pushKV("confirmations", 0);
}
}
}
@@ -190,7 +190,7 @@ UniValue getrawtransaction(const JSONRPCRequest& request)
}
UniValue result(UniValue::VOBJ);
- if (blockindex) result.push_back(Pair("in_active_chain", in_active_chain));
+ if (blockindex) result.pushKV("in_active_chain", in_active_chain);
TxToJSON(*tx, hash_block, result);
return result;
}
@@ -562,7 +562,7 @@ UniValue decodescript(const JSONRPCRequest& request)
if (type.isStr() && type.get_str() != "scripthash") {
// P2SH cannot be wrapped in a P2SH. If this script is already a P2SH,
// don't return the address for a P2SH of the P2SH.
- r.push_back(Pair("p2sh", EncodeDestination(CScriptID(script))));
+ r.pushKV("p2sh", EncodeDestination(CScriptID(script)));
}
return r;
@@ -572,16 +572,16 @@ UniValue decodescript(const JSONRPCRequest& request)
static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::string& strMessage)
{
UniValue entry(UniValue::VOBJ);
- entry.push_back(Pair("txid", txin.prevout.hash.ToString()));
- entry.push_back(Pair("vout", (uint64_t)txin.prevout.n));
+ entry.pushKV("txid", txin.prevout.hash.ToString());
+ entry.pushKV("vout", (uint64_t)txin.prevout.n);
UniValue witness(UniValue::VARR);
for (unsigned int i = 0; i < txin.scriptWitness.stack.size(); i++) {
witness.push_back(HexStr(txin.scriptWitness.stack[i].begin(), txin.scriptWitness.stack[i].end()));
}
- entry.push_back(Pair("witness", witness));
- entry.push_back(Pair("scriptSig", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())));
- entry.push_back(Pair("sequence", (uint64_t)txin.nSequence));
- entry.push_back(Pair("error", strMessage));
+ entry.pushKV("witness", witness);
+ entry.pushKV("scriptSig", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()));
+ entry.pushKV("sequence", (uint64_t)txin.nSequence);
+ entry.pushKV("error", strMessage);
vErrorsRet.push_back(entry);
}
@@ -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) 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\"")
- );
-
- 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);
@@ -916,15 +818,197 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
bool fComplete = vErrors.empty();
UniValue result(UniValue::VOBJ);
- result.push_back(Pair("hex", EncodeHexTx(mtx)));
- result.push_back(Pair("complete", fComplete));
+ result.pushKV("hex", EncodeHexTx(mtx));
+ result.pushKV("complete", fComplete);
if (!vErrors.empty()) {
- result.push_back(Pair("errors", vErrors));
+ result.pushKV("errors", vErrors);
}
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/script/interpreter.h b/src/script/interpreter.h
index e12329be76..4dad6b44c5 100644
--- a/src/script/interpreter.h
+++ b/src/script/interpreter.h
@@ -104,7 +104,7 @@ enum
//
SCRIPT_VERIFY_MINIMALIF = (1U << 13),
- // Signature(s) must be empty vector if an CHECK(MULTI)SIG operation failed
+ // Signature(s) must be empty vector if a CHECK(MULTI)SIG operation failed
//
SCRIPT_VERIFY_NULLFAIL = (1U << 14),
diff --git a/src/script/ismine.h b/src/script/ismine.h
index b54879cc15..c1338c3a8e 100644
--- a/src/script/ismine.h
+++ b/src/script/ismine.h
@@ -29,7 +29,7 @@ enum isminetype
typedef uint8_t isminefilter;
/* isInvalid becomes true when the script is found invalid by consensus or policy. This will terminate the recursion
- * and return a ISMINE_NO immediately, as an invalid script should never be considered as "mine". This is needed as
+ * and return ISMINE_NO immediately, as an invalid script should never be considered as "mine". This is needed as
* different SIGVERSION may have different network rules. Currently the only use of isInvalid is indicate uncompressed
* keys in SIGVERSION_WITNESS_V0 script, but could also be used in similar cases in the future
*/
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/data/tx_invalid.json b/src/test/data/tx_invalid.json
index 09442b7f9f..f8a1347c31 100644
--- a/src/test/data/tx_invalid.json
+++ b/src/test/data/tx_invalid.json
@@ -92,11 +92,11 @@
[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]],
"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004a010047304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH,NULLDUMMY"],
-["As above, but using a OP_1"],
+["As above, but using an OP_1"],
[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]],
"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000495147304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH,NULLDUMMY"],
-["As above, but using a OP_1NEGATE"],
+["As above, but using an OP_1NEGATE"],
[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]],
"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000494f47304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH,NULLDUMMY"],
diff --git a/src/test/data/tx_valid.json b/src/test/data/tx_valid.json
index a845083636..7e39ec7599 100644
--- a/src/test/data/tx_valid.json
+++ b/src/test/data/tx_valid.json
@@ -23,11 +23,11 @@
[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]],
"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004a01ff47304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH"],
-["As above, but using a OP_1"],
+["As above, but using an OP_1"],
[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]],
"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000495147304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH"],
-["As above, but using a OP_1NEGATE"],
+["As above, but using an OP_1NEGATE"],
[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]],
"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000494f47304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH"],
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/util_tests.cpp b/src/test/util_tests.cpp
index 55d60d95e9..463bed5957 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>
@@ -603,4 +608,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/torcontrol.cpp b/src/torcontrol.cpp
index d875008ef2..717d1cf7e5 100644
--- a/src/torcontrol.cpp
+++ b/src/torcontrol.cpp
@@ -731,7 +731,7 @@ void TorController::reconnect_cb(evutil_socket_t fd, short what, void *arg)
/****** Thread ********/
static struct event_base *gBase;
-static boost::thread torControlThread;
+static std::thread torControlThread;
static void TorControlThread()
{
@@ -740,7 +740,7 @@ static void TorControlThread()
event_base_dispatch(gBase);
}
-void StartTorControl(boost::thread_group& threadGroup, CScheduler& scheduler)
+void StartTorControl()
{
assert(!gBase);
#ifdef WIN32
@@ -754,7 +754,7 @@ void StartTorControl(boost::thread_group& threadGroup, CScheduler& scheduler)
return;
}
- torControlThread = boost::thread(boost::bind(&TraceThread<void (*)()>, "torcontrol", &TorControlThread));
+ torControlThread = std::thread(std::bind(&TraceThread<void (*)()>, "torcontrol", &TorControlThread));
}
void InterruptTorControl()
diff --git a/src/torcontrol.h b/src/torcontrol.h
index 20514f7bbf..2be6701fa5 100644
--- a/src/torcontrol.h
+++ b/src/torcontrol.h
@@ -13,7 +13,7 @@
extern const std::string DEFAULT_TOR_CONTROL;
static const bool DEFAULT_LISTEN_ONION = true;
-void StartTorControl(boost::thread_group& threadGroup, CScheduler& scheduler);
+void StartTorControl();
void InterruptTorControl();
void StopTorControl();
diff --git a/src/univalue/include/univalue.h b/src/univalue/include/univalue.h
index 4fd2223b30..c15b2f051e 100644
--- a/src/univalue/include/univalue.h
+++ b/src/univalue/include/univalue.h
@@ -130,6 +130,10 @@ public:
UniValue tmpVal(val_);
return pushKV(key, tmpVal);
}
+ bool pushKV(const std::string& key, bool val_) {
+ UniValue tmpVal((bool)val_);
+ return pushKV(key, tmpVal);
+ }
bool pushKV(const std::string& key, int val_) {
UniValue tmpVal((int64_t)val_);
return pushKV(key, tmpVal);
diff --git a/src/univalue/test/object.cpp b/src/univalue/test/object.cpp
index 679cc9f143..70ccc0d08a 100644
--- a/src/univalue/test/object.cpp
+++ b/src/univalue/test/object.cpp
@@ -261,6 +261,12 @@ BOOST_AUTO_TEST_CASE(univalue_object)
strKey = "temperature";
BOOST_CHECK(obj.pushKV(strKey, (double) 90.012));
+ strKey = "moon";
+ BOOST_CHECK(obj.pushKV(strKey, true));
+
+ strKey = "spoon";
+ BOOST_CHECK(obj.pushKV(strKey, false));
+
UniValue obj2(UniValue::VOBJ);
BOOST_CHECK(obj2.pushKV("cat1", 9000));
BOOST_CHECK(obj2.pushKV("cat2", 12345));
@@ -268,7 +274,7 @@ BOOST_AUTO_TEST_CASE(univalue_object)
BOOST_CHECK(obj.pushKVs(obj2));
BOOST_CHECK_EQUAL(obj.empty(), false);
- BOOST_CHECK_EQUAL(obj.size(), 9);
+ BOOST_CHECK_EQUAL(obj.size(), 11);
BOOST_CHECK_EQUAL(obj["age"].getValStr(), "100");
BOOST_CHECK_EQUAL(obj["first"].getValStr(), "John");
@@ -277,6 +283,8 @@ BOOST_AUTO_TEST_CASE(univalue_object)
BOOST_CHECK_EQUAL(obj["time"].getValStr(), "3600");
BOOST_CHECK_EQUAL(obj["calories"].getValStr(), "12");
BOOST_CHECK_EQUAL(obj["temperature"].getValStr(), "90.012");
+ BOOST_CHECK_EQUAL(obj["moon"].getValStr(), "1");
+ BOOST_CHECK_EQUAL(obj["spoon"].getValStr(), "");
BOOST_CHECK_EQUAL(obj["cat1"].getValStr(), "9000");
BOOST_CHECK_EQUAL(obj["cat2"].getValStr(), "12345");
@@ -289,6 +297,8 @@ BOOST_AUTO_TEST_CASE(univalue_object)
BOOST_CHECK(obj.exists("time"));
BOOST_CHECK(obj.exists("calories"));
BOOST_CHECK(obj.exists("temperature"));
+ BOOST_CHECK(obj.exists("moon"));
+ BOOST_CHECK(obj.exists("spoon"));
BOOST_CHECK(obj.exists("cat1"));
BOOST_CHECK(obj.exists("cat2"));
@@ -302,6 +312,8 @@ BOOST_AUTO_TEST_CASE(univalue_object)
objTypes["time"] = UniValue::VNUM;
objTypes["calories"] = UniValue::VNUM;
objTypes["temperature"] = UniValue::VNUM;
+ objTypes["moon"] = UniValue::VBOOL;
+ objTypes["spoon"] = UniValue::VBOOL;
objTypes["cat1"] = UniValue::VNUM;
objTypes["cat2"] = UniValue::VNUM;
BOOST_CHECK(obj.checkObject(objTypes));
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 978aaf7d06..e809f66e25 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
@@ -1182,7 +1182,8 @@ static void AlertNotify(const std::string& strMessage)
safeStatus = singleQuote+safeStatus+singleQuote;
boost::replace_all(strCmd, "%s", safeStatus);
- boost::thread t(runCommand, strCmd); // thread runs free
+ std::thread t(runCommand, strCmd);
+ t.detach(); // thread runs free
}
static void CheckForkWarningConditions()
@@ -2086,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
diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp
index 9bfcab54a5..9cae660c60 100644
--- a/src/wallet/feebumper.cpp
+++ b/src/wallet/feebumper.cpp
@@ -65,16 +65,39 @@ static feebumper::Result PreconditionChecks(const CWallet* wallet, const CWallet
errors.push_back("Transaction has been mined, or is conflicted with a mined transaction");
return feebumper::Result::WALLET_ERROR;
}
+
+ if (!SignalsOptInRBF(*wtx.tx)) {
+ errors.push_back("Transaction is not BIP 125 replaceable");
+ return feebumper::Result::WALLET_ERROR;
+ }
+
+ if (wtx.mapValue.count("replaced_by_txid")) {
+ errors.push_back(strprintf("Cannot bump transaction %s which was already bumped by transaction %s", wtx.GetHash().ToString(), wtx.mapValue.at("replaced_by_txid")));
+ return feebumper::Result::WALLET_ERROR;
+ }
+
+ // check that original tx consists entirely of our inputs
+ // if not, we can't bump the fee, because the wallet has no way of knowing the value of the other inputs (thus the fee)
+ if (!wallet->IsAllFromMe(*wtx.tx, ISMINE_SPENDABLE)) {
+ errors.push_back("Transaction contains inputs that don't belong to this wallet");
+ return feebumper::Result::WALLET_ERROR;
+ }
+
+
return feebumper::Result::OK;
}
namespace feebumper {
-bool TransactionCanBeBumped(CWallet* wallet, const uint256& txid)
+bool TransactionCanBeBumped(const CWallet* wallet, const uint256& txid)
{
LOCK2(cs_main, wallet->cs_wallet);
const CWalletTx* wtx = wallet->GetWalletTx(txid);
- return wtx && SignalsOptInRBF(*wtx->tx) && !wtx->mapValue.count("replaced_by_txid");
+ if (wtx == nullptr) return false;
+
+ std::vector<std::string> errors_dummy;
+ feebumper::Result res = PreconditionChecks(wallet, *wtx, errors_dummy);
+ return res == feebumper::Result::OK;
}
Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoinControl& coin_control, CAmount total_fee, std::vector<std::string>& errors,
@@ -94,23 +117,6 @@ Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoin
return result;
}
- if (!SignalsOptInRBF(*wtx.tx)) {
- errors.push_back("Transaction is not BIP 125 replaceable");
- return Result::WALLET_ERROR;
- }
-
- if (wtx.mapValue.count("replaced_by_txid")) {
- errors.push_back(strprintf("Cannot bump transaction %s which was already bumped by transaction %s", txid.ToString(), wtx.mapValue.at("replaced_by_txid")));
- return Result::WALLET_ERROR;
- }
-
- // check that original tx consists entirely of our inputs
- // if not, we can't bump the fee, because the wallet has no way of knowing the value of the other inputs (thus the fee)
- if (!wallet->IsAllFromMe(*wtx.tx, ISMINE_SPENDABLE)) {
- errors.push_back("Transaction contains inputs that don't belong to this wallet");
- return Result::WALLET_ERROR;
- }
-
// figure out which output was change
// if there was no change output or multiple change outputs, fail
int nOutput = -1;
@@ -228,6 +234,7 @@ Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoin
}
}
+
return Result::OK;
}
@@ -267,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;
}
@@ -290,4 +297,3 @@ Result CommitTransaction(CWallet* wallet, const uint256& txid, CMutableTransacti
}
} // namespace feebumper
-
diff --git a/src/wallet/feebumper.h b/src/wallet/feebumper.h
index 8eec30440c..7e36a9766b 100644
--- a/src/wallet/feebumper.h
+++ b/src/wallet/feebumper.h
@@ -26,7 +26,7 @@ enum class Result
};
//! Return whether transaction can be bumped.
-bool TransactionCanBeBumped(CWallet* wallet, const uint256& txid);
+bool TransactionCanBeBumped(const CWallet* wallet, const uint256& txid);
//! Create bumpfee transaction.
Result CreateTransaction(const CWallet* wallet,
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index 03fb824e7a..741ea25340 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -790,7 +790,7 @@ UniValue dumpwallet(const JSONRPCRequest& request)
file.close();
UniValue reply(UniValue::VOBJ);
- reply.push_back(Pair("filename", filepath.string()));
+ reply.pushKV("filename", filepath.string());
return reply;
}
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 261d9b37f5..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>
@@ -86,25 +88,25 @@ void EnsureWalletIsUnlocked(CWallet * const pwallet)
void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry)
{
int confirms = wtx.GetDepthInMainChain();
- entry.push_back(Pair("confirmations", confirms));
+ entry.pushKV("confirmations", confirms);
if (wtx.IsCoinBase())
- entry.push_back(Pair("generated", true));
+ entry.pushKV("generated", true);
if (confirms > 0)
{
- entry.push_back(Pair("blockhash", wtx.hashBlock.GetHex()));
- entry.push_back(Pair("blockindex", wtx.nIndex));
- entry.push_back(Pair("blocktime", mapBlockIndex[wtx.hashBlock]->GetBlockTime()));
+ entry.pushKV("blockhash", wtx.hashBlock.GetHex());
+ entry.pushKV("blockindex", wtx.nIndex);
+ entry.pushKV("blocktime", mapBlockIndex[wtx.hashBlock]->GetBlockTime());
} else {
- entry.push_back(Pair("trusted", wtx.IsTrusted()));
+ entry.pushKV("trusted", wtx.IsTrusted());
}
uint256 hash = wtx.GetHash();
- entry.push_back(Pair("txid", hash.GetHex()));
+ entry.pushKV("txid", hash.GetHex());
UniValue conflicts(UniValue::VARR);
for (const uint256& conflict : wtx.GetConflicts())
conflicts.push_back(conflict.GetHex());
- entry.push_back(Pair("walletconflicts", conflicts));
- entry.push_back(Pair("time", wtx.GetTxTime()));
- entry.push_back(Pair("timereceived", (int64_t)wtx.nTimeReceived));
+ entry.pushKV("walletconflicts", conflicts);
+ entry.pushKV("time", wtx.GetTxTime());
+ entry.pushKV("timereceived", (int64_t)wtx.nTimeReceived);
// Add opt-in RBF status
std::string rbfStatus = "no";
@@ -116,10 +118,10 @@ void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry)
else if (rbfState == RBF_TRANSACTIONSTATE_REPLACEABLE_BIP125)
rbfStatus = "yes";
}
- entry.push_back(Pair("bip125-replaceable", rbfStatus));
+ entry.pushKV("bip125-replaceable", rbfStatus);
for (const std::pair<std::string, std::string>& item : wtx.mapValue)
- entry.push_back(Pair(item.first, item.second));
+ entry.pushKV(item.first, item.second);
}
std::string AccountFromValue(const UniValue& value)
@@ -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);
}
@@ -1463,13 +1465,13 @@ UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool fByA
{
UniValue obj(UniValue::VOBJ);
if(fIsWatchonly)
- obj.push_back(Pair("involvesWatchonly", true));
- obj.push_back(Pair("address", EncodeDestination(dest)));
- obj.push_back(Pair("account", strAccount));
- obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
- obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)));
+ obj.pushKV("involvesWatchonly", true);
+ obj.pushKV("address", EncodeDestination(dest));
+ obj.pushKV("account", strAccount);
+ obj.pushKV("amount", ValueFromAmount(nAmount));
+ obj.pushKV("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf));
if (!fByAccounts)
- obj.push_back(Pair("label", strAccount));
+ obj.pushKV("label", strAccount);
UniValue transactions(UniValue::VARR);
if (it != mapTally.end())
{
@@ -1478,7 +1480,7 @@ UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool fByA
transactions.push_back(_item.GetHex());
}
}
- obj.push_back(Pair("txids", transactions));
+ obj.pushKV("txids", transactions);
ret.push_back(obj);
}
}
@@ -1491,10 +1493,10 @@ UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool fByA
int nConf = entry.second.nConf;
UniValue obj(UniValue::VOBJ);
if (entry.second.fIsWatchonly)
- obj.push_back(Pair("involvesWatchonly", true));
- obj.push_back(Pair("account", entry.first));
- obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
- obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)));
+ obj.pushKV("involvesWatchonly", true);
+ obj.pushKV("account", entry.first);
+ obj.pushKV("amount", ValueFromAmount(nAmount));
+ obj.pushKV("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf));
ret.push_back(obj);
}
}
@@ -1600,7 +1602,7 @@ UniValue listreceivedbyaccount(const JSONRPCRequest& request)
static void MaybePushAddress(UniValue & entry, const CTxDestination &dest)
{
if (IsValidDestination(dest)) {
- entry.push_back(Pair("address", EncodeDestination(dest)));
+ entry.pushKV("address", EncodeDestination(dest));
}
}
@@ -1634,20 +1636,20 @@ void ListTransactions(CWallet* const pwallet, const CWalletTx& wtx, const std::s
{
UniValue entry(UniValue::VOBJ);
if (involvesWatchonly || (::IsMine(*pwallet, s.destination) & ISMINE_WATCH_ONLY)) {
- entry.push_back(Pair("involvesWatchonly", true));
+ entry.pushKV("involvesWatchonly", true);
}
- entry.push_back(Pair("account", strSentAccount));
+ entry.pushKV("account", strSentAccount);
MaybePushAddress(entry, s.destination);
- entry.push_back(Pair("category", "send"));
- entry.push_back(Pair("amount", ValueFromAmount(-s.amount)));
+ entry.pushKV("category", "send");
+ entry.pushKV("amount", ValueFromAmount(-s.amount));
if (pwallet->mapAddressBook.count(s.destination)) {
- entry.push_back(Pair("label", pwallet->mapAddressBook[s.destination].name));
+ entry.pushKV("label", pwallet->mapAddressBook[s.destination].name);
}
- entry.push_back(Pair("vout", s.vout));
- entry.push_back(Pair("fee", ValueFromAmount(-nFee)));
+ entry.pushKV("vout", s.vout);
+ entry.pushKV("fee", ValueFromAmount(-nFee));
if (fLong)
WalletTxToJSON(wtx, entry);
- entry.push_back(Pair("abandoned", wtx.isAbandoned()));
+ entry.pushKV("abandoned", wtx.isAbandoned());
ret.push_back(entry);
}
}
@@ -1665,28 +1667,28 @@ void ListTransactions(CWallet* const pwallet, const CWalletTx& wtx, const std::s
{
UniValue entry(UniValue::VOBJ);
if (involvesWatchonly || (::IsMine(*pwallet, r.destination) & ISMINE_WATCH_ONLY)) {
- entry.push_back(Pair("involvesWatchonly", true));
+ entry.pushKV("involvesWatchonly", true);
}
- entry.push_back(Pair("account", account));
+ entry.pushKV("account", account);
MaybePushAddress(entry, r.destination);
if (wtx.IsCoinBase())
{
if (wtx.GetDepthInMainChain() < 1)
- entry.push_back(Pair("category", "orphan"));
+ entry.pushKV("category", "orphan");
else if (wtx.GetBlocksToMaturity() > 0)
- entry.push_back(Pair("category", "immature"));
+ entry.pushKV("category", "immature");
else
- entry.push_back(Pair("category", "generate"));
+ entry.pushKV("category", "generate");
}
else
{
- entry.push_back(Pair("category", "receive"));
+ entry.pushKV("category", "receive");
}
- entry.push_back(Pair("amount", ValueFromAmount(r.amount)));
+ entry.pushKV("amount", ValueFromAmount(r.amount));
if (pwallet->mapAddressBook.count(r.destination)) {
- entry.push_back(Pair("label", account));
+ entry.pushKV("label", account);
}
- entry.push_back(Pair("vout", r.vout));
+ entry.pushKV("vout", r.vout);
if (fLong)
WalletTxToJSON(wtx, entry);
ret.push_back(entry);
@@ -1702,12 +1704,12 @@ void AcentryToJSON(const CAccountingEntry& acentry, const std::string& strAccoun
if (fAllAccounts || acentry.strAccount == strAccount)
{
UniValue entry(UniValue::VOBJ);
- entry.push_back(Pair("account", acentry.strAccount));
- entry.push_back(Pair("category", "move"));
- entry.push_back(Pair("time", acentry.nTime));
- entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit)));
- entry.push_back(Pair("otheraccount", acentry.strOtherAccount));
- entry.push_back(Pair("comment", acentry.strComment));
+ entry.pushKV("account", acentry.strAccount);
+ entry.pushKV("category", "move");
+ entry.pushKV("time", acentry.nTime);
+ entry.pushKV("amount", ValueFromAmount(acentry.nCreditDebit));
+ entry.pushKV("otheraccount", acentry.strOtherAccount);
+ entry.pushKV("comment", acentry.strComment);
ret.push_back(entry);
}
}
@@ -1934,7 +1936,7 @@ UniValue listaccounts(const JSONRPCRequest& request)
UniValue ret(UniValue::VOBJ);
for (const std::pair<std::string, CAmount>& accountBalance : mapAccountBalances) {
- ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second)));
+ ret.pushKV(accountBalance.first, ValueFromAmount(accountBalance.second));
}
return ret;
}
@@ -2074,9 +2076,9 @@ UniValue listsinceblock(const JSONRPCRequest& request)
uint256 lastblock = pblockLast ? pblockLast->GetBlockHash() : uint256();
UniValue ret(UniValue::VOBJ);
- ret.push_back(Pair("transactions", transactions));
- if (include_removed) ret.push_back(Pair("removed", removed));
- ret.push_back(Pair("lastblock", lastblock.GetHex()));
+ ret.pushKV("transactions", transactions);
+ if (include_removed) ret.pushKV("removed", removed);
+ ret.pushKV("lastblock", lastblock.GetHex());
return ret;
}
@@ -2161,18 +2163,18 @@ UniValue gettransaction(const JSONRPCRequest& request)
CAmount nNet = nCredit - nDebit;
CAmount nFee = (wtx.IsFromMe(filter) ? wtx.tx->GetValueOut() - nDebit : 0);
- entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
+ entry.pushKV("amount", ValueFromAmount(nNet - nFee));
if (wtx.IsFromMe(filter))
- entry.push_back(Pair("fee", ValueFromAmount(nFee)));
+ entry.pushKV("fee", ValueFromAmount(nFee));
WalletTxToJSON(wtx, entry);
UniValue details(UniValue::VARR);
ListTransactions(pwallet, wtx, "*", 0, false, details, filter);
- entry.push_back(Pair("details", details));
+ entry.pushKV("details", details);
std::string strHex = EncodeHexTx(*wtx.tx, RPCSerializationFlags());
- entry.push_back(Pair("hex", strHex));
+ entry.pushKV("hex", strHex);
return entry;
}
@@ -2702,8 +2704,8 @@ UniValue listlockunspent(const JSONRPCRequest& request)
for (COutPoint &outpt : vOutpts) {
UniValue o(UniValue::VOBJ);
- o.push_back(Pair("txid", outpt.hash.GetHex()));
- o.push_back(Pair("vout", (int)outpt.n));
+ o.pushKV("txid", outpt.hash.GetHex());
+ o.pushKV("vout", (int)outpt.n);
ret.push_back(o);
}
@@ -2781,24 +2783,24 @@ UniValue getwalletinfo(const JSONRPCRequest& request)
UniValue obj(UniValue::VOBJ);
size_t kpExternalSize = pwallet->KeypoolCountExternalKeys();
- obj.push_back(Pair("walletname", pwallet->GetName()));
- obj.push_back(Pair("walletversion", pwallet->GetVersion()));
- obj.push_back(Pair("balance", ValueFromAmount(pwallet->GetBalance())));
- obj.push_back(Pair("unconfirmed_balance", ValueFromAmount(pwallet->GetUnconfirmedBalance())));
- obj.push_back(Pair("immature_balance", ValueFromAmount(pwallet->GetImmatureBalance())));
- obj.push_back(Pair("txcount", (int)pwallet->mapWallet.size()));
- obj.push_back(Pair("keypoololdest", pwallet->GetOldestKeyPoolTime()));
- obj.push_back(Pair("keypoolsize", (int64_t)kpExternalSize));
+ obj.pushKV("walletname", pwallet->GetName());
+ obj.pushKV("walletversion", pwallet->GetVersion());
+ obj.pushKV("balance", ValueFromAmount(pwallet->GetBalance()));
+ obj.pushKV("unconfirmed_balance", ValueFromAmount(pwallet->GetUnconfirmedBalance()));
+ obj.pushKV("immature_balance", ValueFromAmount(pwallet->GetImmatureBalance()));
+ obj.pushKV("txcount", (int)pwallet->mapWallet.size());
+ obj.pushKV("keypoololdest", pwallet->GetOldestKeyPoolTime());
+ obj.pushKV("keypoolsize", (int64_t)kpExternalSize);
CKeyID masterKeyID = pwallet->GetHDChain().masterKeyID;
if (!masterKeyID.IsNull() && pwallet->CanSupportFeature(FEATURE_HD_SPLIT)) {
- obj.push_back(Pair("keypoolsize_hd_internal", (int64_t)(pwallet->GetKeyPoolSize() - kpExternalSize)));
+ obj.pushKV("keypoolsize_hd_internal", (int64_t)(pwallet->GetKeyPoolSize() - kpExternalSize));
}
if (pwallet->IsCrypted()) {
- obj.push_back(Pair("unlocked_until", pwallet->nRelockTime));
+ obj.pushKV("unlocked_until", pwallet->nRelockTime);
}
- obj.push_back(Pair("paytxfee", ValueFromAmount(payTxFee.GetFeePerK())));
+ obj.pushKV("paytxfee", ValueFromAmount(payTxFee.GetFeePerK()));
if (!masterKeyID.IsNull())
- obj.push_back(Pair("hdmasterkeyid", masterKeyID.GetHex()));
+ obj.pushKV("hdmasterkeyid", masterKeyID.GetHex());
return obj;
}
@@ -3003,31 +3005,31 @@ UniValue listunspent(const JSONRPCRequest& request)
continue;
UniValue entry(UniValue::VOBJ);
- entry.push_back(Pair("txid", out.tx->GetHash().GetHex()));
- entry.push_back(Pair("vout", out.i));
+ entry.pushKV("txid", out.tx->GetHash().GetHex());
+ entry.pushKV("vout", out.i);
if (fValidAddress) {
- entry.push_back(Pair("address", EncodeDestination(address)));
+ entry.pushKV("address", EncodeDestination(address));
if (pwallet->mapAddressBook.count(address)) {
- entry.push_back(Pair("account", pwallet->mapAddressBook[address].name));
+ entry.pushKV("account", pwallet->mapAddressBook[address].name);
}
if (scriptPubKey.IsPayToScriptHash()) {
const CScriptID& hash = boost::get<CScriptID>(address);
CScript redeemScript;
if (pwallet->GetCScript(hash, redeemScript)) {
- entry.push_back(Pair("redeemScript", HexStr(redeemScript.begin(), redeemScript.end())));
+ entry.pushKV("redeemScript", HexStr(redeemScript.begin(), redeemScript.end()));
}
}
}
- entry.push_back(Pair("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end())));
- entry.push_back(Pair("amount", ValueFromAmount(out.tx->tx->vout[out.i].nValue)));
- entry.push_back(Pair("confirmations", out.nDepth));
- entry.push_back(Pair("spendable", out.fSpendable));
- entry.push_back(Pair("solvable", out.fSolvable));
- entry.push_back(Pair("safe", out.fSafe));
+ entry.pushKV("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()));
+ entry.pushKV("amount", ValueFromAmount(out.tx->tx->vout[out.i].nValue));
+ entry.pushKV("confirmations", out.nDepth);
+ entry.pushKV("spendable", out.fSpendable);
+ entry.pushKV("solvable", out.fSolvable);
+ entry.pushKV("safe", out.fSafe);
results.push_back(entry);
}
@@ -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)},
@@ -3229,13 +3230,82 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
}
UniValue result(UniValue::VOBJ);
- result.push_back(Pair("hex", EncodeHexTx(tx)));
- result.push_back(Pair("changepos", changePosition));
- result.push_back(Pair("fee", ValueFromAmount(nFeeOut)));
+ result.pushKV("hex", EncodeHexTx(tx));
+ result.pushKV("changepos", changePosition);
+ result.pushKV("fee", ValueFromAmount(nFeeOut));
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);
@@ -3373,14 +3443,14 @@ UniValue bumpfee(const JSONRPCRequest& request)
throw JSONRPCError(RPC_WALLET_ERROR, errors[0]);
}
UniValue result(UniValue::VOBJ);
- result.push_back(Pair("txid", txid.GetHex()));
- result.push_back(Pair("origfee", ValueFromAmount(old_fee)));
- result.push_back(Pair("fee", ValueFromAmount(new_fee)));
+ result.pushKV("txid", txid.GetHex());
+ result.pushKV("origfee", ValueFromAmount(old_fee));
+ result.pushKV("fee", ValueFromAmount(new_fee));
UniValue result_errors(UniValue::VARR);
for (const std::string& error : errors) {
result_errors.push_back(error);
}
- result.push_back(Pair("errors", result_errors));
+ result.pushKV("errors", result_errors);
return result;
}
@@ -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_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 7f36aefeaf..408a01c50b 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -34,7 +34,6 @@
#include <future>
#include <boost/algorithm/string/replace.hpp>
-#include <boost/thread.hpp>
std::vector<CWalletRef> vpwallets;
/** Transaction fee set by the user */
@@ -976,7 +975,8 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
if (!strCmd.empty())
{
boost::replace_all(strCmd, "%s", wtxIn.GetHash().GetHex());
- boost::thread t(runCommand, strCmd); // thread runs free
+ std::thread t(runCommand, strCmd);
+ t.detach(); // thread runs free
}
return true;
@@ -3092,7 +3092,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 +4179,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 +4186,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;
}